Skip to content

Commit 56fbb0b

Browse files
authored
rlp: v3 updates from integration (#1648)
* rlp updates * util: add arrToBufArr and bufArrToArr and tests * util updates, add deprecation notice for re-exports * rlp tsconfig: include rootDir for composite option * remove util exports deprecation notices * rlp: add readme note for buffer compatibility * undo capitalization
1 parent eb5bb56 commit 56fbb0b

File tree

9 files changed

+133
-19
lines changed

9 files changed

+133
-19
lines changed

packages/rlp/CHANGELOG.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,26 @@ RLP.encode(1)
2727

2828
### Uint8Array
2929

30-
Buffers were replaced in favor of using Uint8Arrays for greater compatibility with browsers.
30+
Buffers were replaced in favor of using Uint8Arrays for improved performance and greater compatibility with browsers.
31+
32+
When upgrading from rlp v2 to v3, you must convert your Buffers to Uint8Arrays before passing in. To help, two new utility methods were added to `ethereumjs-util v7.1.4`: `arrToBufArr` and `bufArrToArr`. These will recursively step through your arrays to replace Buffers with Uint8Arrays, or vise versa.
33+
34+
Example:
35+
36+
```typescript
37+
// Old, rlp v2
38+
import * as rlp from 'rlp'
39+
const bufArr = [Buffer.from('123', 'hex'), Buffer.from('456', 'hex')]
40+
const encoded = rlp.encode(bufArr)
41+
const decoded = rlp.decode(encoded)
42+
43+
// New, rlp v3
44+
import RLP from 'rlp'
45+
const encoded: Uint8Array = RLP.encode(bufArrToArr(bufArr))
46+
const encodedAsBuffer = Buffer.from(encoded)
47+
const decoded: Uint8Array[] = RLP.decode(encoded)
48+
const decodedAsBuffers = arrToBufArr(decoded)
49+
```
3150

3251
### Invalid RLPs
3352

packages/rlp/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,23 @@ assert.deepEqual(nestedList, decoded)
3232

3333
`RLP.decode(encoded, [stream=false])` - Decodes an RLP encoded `Uint8Array`, `Array` or `String` and returns a `Uint8Array` or `NestedUint8Array`. If `stream` is enabled, it will just decode the first rlp sequence in the Uint8Array. By default, it would throw an error if there are more bytes in Uint8Array than used by the rlp sequence.
3434

35+
### Buffer compatibility
36+
37+
If you would like to continue using Buffers like in rlp v2, you can use:
38+
39+
```typescript
40+
import assert from 'assert'
41+
import { arrToBufArr, bufArrToArr } from 'ethereumjs-util'
42+
import RLP from 'rlp'
43+
44+
const bufferList = [Buffer.from('123', 'hex'), Buffer.from('456', 'hex')]
45+
const encoded = RLP.encode(bufArrToArr(bufferList))
46+
const encodedAsBuffer = Buffer.from(encoded)
47+
const decoded = RLP.decode(Uint8Array.from(encodedAsBuffer)) // or RLP.decode(encoded)
48+
const decodedAsBuffers = arrToBufArr(decoded)
49+
assert.deepEqual(bufferList, decodedAsBuffers)
50+
```
51+
3552
## CLI
3653

3754
`rlp encode <JSON string>`\

packages/rlp/src/index.ts

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
1-
export type Input = string | number | bigint | Uint8Array | List | null | undefined
1+
export type Input = string | number | bigint | Uint8Array | Array<Input> | null | undefined
22

3-
// Use interface extension instead of type alias to
4-
// make circular declaration possible.
5-
export interface List extends Array<Input> {}
3+
export type NestedUint8Array = Array<Uint8Array | NestedUint8Array>
64

75
export interface Decoded {
86
data: Uint8Array | NestedUint8Array
97
remainder: Uint8Array
108
}
119

12-
export interface NestedUint8Array extends Array<Uint8Array | NestedUint8Array> {}
13-
1410
/**
1511
* RLP Encoding based on https://eth.wiki/en/fundamentals/rlp
1612
* This function takes in data, converts it to Uint8Array if not,
@@ -51,13 +47,11 @@ function safeSlice(input: Uint8Array, start: number, end: number) {
5147
/**
5248
* Parse integers. Check if there is no leading zeros
5349
* @param v The value to parse
54-
* @param base The base to parse the integer into
5550
*/
5651
function decodeLength(v: Uint8Array): number {
57-
if (v[0] === 0 && v[1] === 0) {
52+
if (v[0] === 0) {
5853
throw new Error('invalid RLP: extra zeros')
5954
}
60-
6155
return parseHexByte(bytesToHex(v))
6256
}
6357

@@ -147,12 +141,12 @@ function _decode(input: Uint8Array): Decoded {
147141
remainder: input.slice(length + llength),
148142
}
149143
} else if (firstByte <= 0xf7) {
150-
// a list between 0-55 bytes long
144+
// a list between 0-55 bytes long
151145
length = firstByte - 0xbf
152146
innerRemainder = safeSlice(input, 1, length)
153147
while (innerRemainder.length) {
154148
d = _decode(innerRemainder)
155-
decoded.push(d.data as Uint8Array)
149+
decoded.push(d.data)
156150
innerRemainder = d.remainder
157151
}
158152

@@ -161,7 +155,7 @@ function _decode(input: Uint8Array): Decoded {
161155
remainder: input.slice(length),
162156
}
163157
} else {
164-
// a list over 55 bytes long
158+
// a list over 55 bytes long
165159
llength = firstByte - 0xf6
166160
length = decodeLength(safeSlice(input, 1, llength))
167161
if (length < 56) {
@@ -176,7 +170,7 @@ function _decode(input: Uint8Array): Decoded {
176170

177171
while (innerRemainder.length) {
178172
d = _decode(innerRemainder)
179-
decoded.push(d.data as Uint8Array)
173+
decoded.push(d.data)
180174
innerRemainder = d.remainder
181175
}
182176

@@ -198,7 +192,6 @@ function bytesToHex(uint8a: Uint8Array): string {
198192
}
199193

200194
function parseHexByte(hexByte: string): number {
201-
if (hexByte.length !== 2) throw new Error('Invalid byte sequence')
202195
const byte = Number.parseInt(hexByte, 16)
203196
if (Number.isNaN(byte)) throw new Error('Invalid byte sequence')
204197
return byte

packages/rlp/tsconfig.prod.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
{
22
"extends": "../../config/tsconfig.prod.json",
33
"compilerOptions": {
4+
"target": "es2020",
5+
"rootDir": "./src",
46
"outDir": "./dist",
7+
"composite": true
58
},
69
"include": ["src/*.ts"]
7-
}
10+
}

packages/util/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
"author": "mjbecze <mjbecze@gmail.com>",
77
"keywords": [
88
"ethereum",
9-
"utilties"
9+
"utilities",
10+
"utils"
1011
],
1112
"engines": {
1213
"node": ">=10.0.0"

packages/util/src/bytes.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { BN } from './externals'
22
import { stripHexPrefix, padToEven, isHexString, isHexPrefixed } from './internal'
3-
import { PrefixedHexString, TransformableToArray, TransformableToBuffer } from './types'
3+
import {
4+
PrefixedHexString,
5+
TransformableToArray,
6+
TransformableToBuffer,
7+
NestedBufferArray,
8+
NestedUint8Array,
9+
} from './types'
410
import { assertIsBuffer, assertIsArray, assertIsHexString } from './helpers'
511

612
/**
@@ -300,3 +306,29 @@ export const validateNoLeadingZeroes = function (values: { [key: string]: Buffer
300306
}
301307
}
302308
}
309+
310+
/**
311+
* Converts a {@link Uint8Array} or {@link NestedUint8Array} to {@link Buffer} or {@link NestedBufferArray}
312+
*/
313+
export function arrToBufArr(arr: Uint8Array): Buffer
314+
export function arrToBufArr(arr: NestedUint8Array): NestedBufferArray
315+
export function arrToBufArr(arr: Uint8Array | NestedUint8Array): Buffer | NestedBufferArray
316+
export function arrToBufArr(arr: Uint8Array | NestedUint8Array): Buffer | NestedBufferArray {
317+
if (!Array.isArray(arr)) {
318+
return Buffer.from(arr)
319+
}
320+
return arr.map((a) => arrToBufArr(a))
321+
}
322+
323+
/**
324+
* Converts a {@link Buffer} or {@link NestedBufferArray} to {@link Uint8Array} or {@link NestedUint8Array}
325+
*/
326+
export function bufArrToArr(arr: Buffer): Uint8Array
327+
export function bufArrToArr(arr: NestedBufferArray): NestedUint8Array
328+
export function bufArrToArr(arr: Buffer | NestedBufferArray): Uint8Array | NestedUint8Array
329+
export function bufArrToArr(arr: Buffer | NestedBufferArray): Uint8Array | NestedUint8Array {
330+
if (!Array.isArray(arr)) {
331+
return Uint8Array.from(arr ?? [])
332+
}
333+
return arr.map((a) => bufArrToArr(a))
334+
}

packages/util/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export * from './bytes'
3434
export * from './object'
3535

3636
/**
37-
* External exports (BN, rlp, secp256k1)
37+
* External exports (BN, rlp)
3838
*/
3939
export * from './externals'
4040

packages/util/src/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ export interface TransformableToBuffer {
4747
toArray?(): Uint8Array
4848
}
4949

50+
export type NestedUint8Array = Array<Uint8Array | NestedUint8Array>
51+
export type NestedBufferArray = Array<Buffer | NestedBufferArray>
52+
5053
/**
5154
* Convert BN to 0x-prefixed hex string.
5255
*/

packages/util/test/bytes.spec.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import tape from 'tape'
22
import {
3+
arrToBufArr,
34
Address,
45
BN,
6+
bufArrToArr,
57
zeros,
68
zeroAddress,
79
isZeroAddress,
@@ -397,3 +399,47 @@ tape('validateNoLeadingZeroes', function (st) {
397399
st.throws(() => validateNoLeadingZeroes(onlyZeroes), 'throws when value has only zeroes')
398400
st.end()
399401
})
402+
403+
tape('arrToBufArr', function (st) {
404+
const uint8 = Uint8Array.from([0, 1, 2])
405+
const uint8Arr = [
406+
Uint8Array.from([1, 2, 3]),
407+
Uint8Array.from([4, 5, 6]),
408+
[Uint8Array.from([7, 8, 9]), Uint8Array.from([1, 0, 0]), [Uint8Array.from([1, 1, 1])]],
409+
]
410+
const buf = Buffer.from(uint8)
411+
const bufArr = [
412+
Buffer.from(Uint8Array.from([1, 2, 3])),
413+
Buffer.from(Uint8Array.from([4, 5, 6])),
414+
[
415+
Buffer.from(Uint8Array.from([7, 8, 9])),
416+
Buffer.from(Uint8Array.from([1, 0, 0])),
417+
[Buffer.from(Uint8Array.from([1, 1, 1]))],
418+
],
419+
]
420+
st.deepEqual(arrToBufArr(uint8), buf)
421+
st.deepEqual(arrToBufArr(uint8Arr), bufArr)
422+
st.end()
423+
})
424+
425+
tape('bufArrToArr', function (st) {
426+
const buf = Buffer.from('123', 'hex')
427+
const bufArr = [
428+
Buffer.from('123', 'hex'),
429+
Buffer.from('456', 'hex'),
430+
[Buffer.from('789', 'hex'), Buffer.from('100', 'hex'), [Buffer.from('111', 'hex')]],
431+
]
432+
const uint8 = Uint8Array.from(buf)
433+
const uint8Arr = [
434+
Uint8Array.from(Buffer.from('123', 'hex')),
435+
Uint8Array.from(Buffer.from('456', 'hex')),
436+
[
437+
Uint8Array.from(Buffer.from('789', 'hex')),
438+
Uint8Array.from(Buffer.from('100', 'hex')),
439+
[Uint8Array.from(Buffer.from('111', 'hex'))],
440+
],
441+
]
442+
st.deepEqual(bufArrToArr(buf), uint8)
443+
st.deepEqual(bufArrToArr(bufArr), uint8Arr)
444+
st.end()
445+
})

0 commit comments

Comments
 (0)