Socket
Socket
Sign inDemoInstall

rlp

Package Overview
Dependencies
0
Maintainers
3
Versions
33
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 2.2.7 to 3.0.0

55

dist/index.d.ts

@@ -1,25 +0,38 @@

/// <reference types="node" />
import { Decoded, Input, List } from './types';
export { Decoded, Input, List };
export declare type Input = string | number | bigint | Uint8Array | Array<Input> | null | undefined;
export declare type NestedUint8Array = Array<Uint8Array | NestedUint8Array>;
export interface Decoded {
data: Uint8Array | NestedUint8Array;
remainder: Uint8Array;
}
/**
* RLP Encoding based on: https://github.com/ethereum/wiki/wiki/%5BEnglish%5D-RLP
* This function takes in a data, convert it to buffer if not, and a length for recursion
* @param input - will be converted to buffer
* @returns returns buffer of encoded data
* RLP Encoding based on https://eth.wiki/en/fundamentals/rlp
* This function takes in data, converts it to Uint8Array if not,
* and adds a length for recursion.
* @param input Will be converted to Uint8Array
* @returns Uint8Array of encoded data
**/
export declare function encode(input: Input): Buffer;
export declare function encode(input: Input): Uint8Array;
/**
* RLP Decoding based on: {@link https://github.com/ethereum/wiki/wiki/%5BEnglish%5D-RLP|RLP}
* @param input - will be converted to buffer
* @param stream - Is the input a stream (false by default)
* @returns - returns decode Array of Buffers containg the original message
* RLP Decoding based on https://eth.wiki/en/fundamentals/rlp
* @param input Will be converted to Uint8Array
* @param stream Is the input a stream (false by default)
* @returns decoded Array of Uint8Arrays containing the original message
**/
export declare function decode(input: Buffer, stream?: boolean): Buffer;
export declare function decode(input: Buffer[], stream?: boolean): Buffer[];
export declare function decode(input: Input, stream?: boolean): Buffer[] | Buffer | Decoded;
/**
* Get the length of the RLP input
* @param input
* @returns The length of the input or an empty Buffer if no input
*/
export declare function getLength(input: Input): Buffer | number;
export declare function decode(input: Input, stream?: false): Uint8Array | NestedUint8Array;
export declare function decode(input: Input, stream?: true): Decoded;
declare function bytesToHex(uint8a: Uint8Array): string;
declare function hexToBytes(hex: string): Uint8Array;
/** Concatenates two Uint8Arrays into one. */
declare function concatBytes(...arrays: Uint8Array[]): Uint8Array;
declare function utf8ToBytes(utf: string): Uint8Array;
export declare const utils: {
bytesToHex: typeof bytesToHex;
concatBytes: typeof concatBytes;
hexToBytes: typeof hexToBytes;
utf8ToBytes: typeof utf8ToBytes;
};
declare const RLP: {
encode: typeof encode;
decode: typeof decode;
};
export default RLP;
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getLength = exports.decode = exports.encode = void 0;
const bn_js_1 = __importDefault(require("bn.js"));
exports.utils = exports.decode = exports.encode = void 0;
/**
* RLP Encoding based on: https://github.com/ethereum/wiki/wiki/%5BEnglish%5D-RLP
* This function takes in a data, convert it to buffer if not, and a length for recursion
* @param input - will be converted to buffer
* @returns returns buffer of encoded data
* RLP Encoding based on https://eth.wiki/en/fundamentals/rlp
* This function takes in data, converts it to Uint8Array if not,
* and adds a length for recursion.
* @param input Will be converted to Uint8Array
* @returns Uint8Array of encoded data
**/

@@ -20,41 +17,50 @@ function encode(input) {

}
const buf = Buffer.concat(output);
return Buffer.concat([encodeLength(buf.length, 192), buf]);
const buf = concatBytes(...output);
return concatBytes(encodeLength(buf.length, 192), buf);
}
else {
const inputBuf = toBuffer(input);
return inputBuf.length === 1 && inputBuf[0] < 128
? inputBuf
: Buffer.concat([encodeLength(inputBuf.length, 128), inputBuf]);
const inputBuf = toBytes(input);
if (inputBuf.length === 1 && inputBuf[0] < 128) {
return inputBuf;
}
return concatBytes(encodeLength(inputBuf.length, 128), inputBuf);
}
exports.encode = encode;
/**
* Slices a Uint8Array, throws if the slice goes out-of-bounds of the Uint8Array.
* E.g. `safeSlice(hexToBytes('aa'), 1, 2)` will throw.
* @param input
* @param start
* @param end
*/
function safeSlice(input, start, end) {
if (end > input.length) {
throw new Error('invalid RLP (safeSlice): end slice of Uint8Array out-of-bounds');
}
return input.slice(start, end);
}
/**
* Parse integers. Check if there is no leading zeros
* @param v The value to parse
* @param base The base to parse the integer into
*/
function safeParseInt(v, base) {
if (v[0] === '0' && v[1] === '0') {
function decodeLength(v) {
if (v[0] === 0) {
throw new Error('invalid RLP: extra zeros');
}
return parseInt(v, base);
return parseHexByte(bytesToHex(v));
}
function encodeLength(len, offset) {
if (len < 56) {
return Buffer.from([len + offset]);
return Uint8Array.from([len + offset]);
}
else {
const hexLength = intToHex(len);
const lLength = hexLength.length / 2;
const firstByte = intToHex(offset + 55 + lLength);
return Buffer.from(firstByte + hexLength, 'hex');
}
const hexLength = numberToHex(len);
const lLength = hexLength.length / 2;
const firstByte = numberToHex(offset + 55 + lLength);
return Uint8Array.from(hexToBytes(firstByte + hexLength));
}
function decode(input, stream = false) {
if (!input || input.length === 0) {
return Buffer.from([]);
return Uint8Array.from([]);
}
const inputBuffer = toBuffer(input);
const decoded = _decode(inputBuffer);
const inputBytes = toBytes(input);
const decoded = _decode(inputBytes);
if (stream) {

@@ -64,3 +70,3 @@ return decoded;

if (decoded.remainder.length !== 0) {
throw new Error('invalid remainder');
throw new Error('invalid RLP: remainder must be zero');
}

@@ -70,34 +76,2 @@ return decoded.data;

exports.decode = decode;
/**
* Get the length of the RLP input
* @param input
* @returns The length of the input or an empty Buffer if no input
*/
function getLength(input) {
if (!input || input.length === 0) {
return Buffer.from([]);
}
const inputBuffer = toBuffer(input);
const firstByte = inputBuffer[0];
if (firstByte <= 0x7f) {
return inputBuffer.length;
}
else if (firstByte <= 0xb7) {
return firstByte - 0x7f;
}
else if (firstByte <= 0xbf) {
return firstByte - 0xb6;
}
else if (firstByte <= 0xf7) {
// a list between 0-55 bytes long
return firstByte - 0xbf;
}
else {
// a list over 55 bytes long
const llength = firstByte - 0xf6;
const length = safeParseInt(inputBuffer.slice(1, llength).toString('hex'), 16);
return llength + length;
}
}
exports.getLength = getLength;
/** Decode an input with RLP */

@@ -121,9 +95,9 @@ function _decode(input) {

if (firstByte === 0x80) {
data = Buffer.from([]);
data = Uint8Array.from([]);
}
else {
data = input.slice(1, length);
data = safeSlice(input, 1, length);
}
if (length === 2 && data[0] < 0x80) {
throw new Error('invalid rlp encoding: byte must be less 0x80');
throw new Error('invalid RLP encoding: invalid prefix, single byte < 0x80 are not prefixed');
}

@@ -142,10 +116,7 @@ return {

}
length = safeParseInt(input.slice(1, llength).toString('hex'), 16);
length = decodeLength(safeSlice(input, 1, llength));
if (length <= 55) {
throw new Error('invalid RLP: expected string length to be greater than 55');
}
data = input.slice(llength, length + llength);
if (data.length < length) {
throw new Error('invalid RLP: not enough bytes for string');
}
data = safeSlice(input, llength, length + llength);
return {

@@ -157,5 +128,5 @@ data: data,

else if (firstByte <= 0xf7) {
// a list between 0-55 bytes long
// a list between 0-55 bytes long
length = firstByte - 0xbf;
innerRemainder = input.slice(1, length);
innerRemainder = safeSlice(input, 1, length);
while (innerRemainder.length) {

@@ -172,13 +143,13 @@ d = _decode(innerRemainder);

else {
// a list over 55 bytes long
// a list over 55 bytes long
llength = firstByte - 0xf6;
length = safeParseInt(input.slice(1, llength).toString('hex'), 16);
length = decodeLength(safeSlice(input, 1, llength));
if (length < 56) {
throw new Error('invalid RLP: encoded list too short');
}
const totalLength = llength + length;
if (totalLength > input.length) {
throw new Error('invalid rlp: total length is larger than the data');
throw new Error('invalid RLP: total length is larger than the data');
}
innerRemainder = input.slice(llength, totalLength);
if (innerRemainder.length === 0) {
throw new Error('invalid rlp, List has a invalid length');
}
innerRemainder = safeSlice(input, llength, totalLength);
while (innerRemainder.length) {

@@ -195,15 +166,49 @@ d = _decode(innerRemainder);

}
/** Check if a string is prefixed by 0x */
function isHexPrefixed(str) {
return str.slice(0, 2) === '0x';
const cachedHexes = Array.from({ length: 256 }, (_v, i) => i.toString(16).padStart(2, '0'));
function bytesToHex(uint8a) {
// Pre-caching chars with `cachedHexes` speeds this up 6x
let hex = '';
for (let i = 0; i < uint8a.length; i++) {
hex += cachedHexes[uint8a[i]];
}
return hex;
}
/** Removes 0x from a given String */
function stripHexPrefix(str) {
if (typeof str !== 'string') {
return str;
function parseHexByte(hexByte) {
const byte = Number.parseInt(hexByte, 16);
if (Number.isNaN(byte))
throw new Error('Invalid byte sequence');
return byte;
}
// Caching slows it down 2-3x
function hexToBytes(hex) {
if (typeof hex !== 'string') {
throw new TypeError('hexToBytes: expected string, got ' + typeof hex);
}
return isHexPrefixed(str) ? str.slice(2) : str;
if (hex.length % 2)
throw new Error('hexToBytes: received invalid unpadded hex');
const array = new Uint8Array(hex.length / 2);
for (let i = 0; i < array.length; i++) {
const j = i * 2;
array[i] = parseHexByte(hex.slice(j, j + 2));
}
return array;
}
/** Concatenates two Uint8Arrays into one. */
function concatBytes(...arrays) {
if (arrays.length === 1)
return arrays[0];
const length = arrays.reduce((a, arr) => a + arr.length, 0);
const result = new Uint8Array(length);
for (let i = 0, pad = 0; i < arrays.length; i++) {
const arr = arrays[i];
result.set(arr, pad);
pad += arr.length;
}
return result;
}
function utf8ToBytes(utf) {
return new TextEncoder().encode(utf);
}
/** Transform an integer into its hexadecimal value */
function intToHex(integer) {
function numberToHex(integer) {
if (integer < 0) {

@@ -219,42 +224,43 @@ throw new Error('Invalid integer as argument, must be unsigned!');

}
/** Transform an integer into a Buffer */
function intToBuffer(integer) {
const hex = intToHex(integer);
return Buffer.from(hex, 'hex');
/** Check if a string is prefixed by 0x */
function isHexPrefixed(str) {
return str.length >= 2 && str[0] === '0' && str[1] === 'x';
}
/** Transform anything into a Buffer */
function toBuffer(v) {
if (!Buffer.isBuffer(v)) {
if (typeof v === 'string') {
if (isHexPrefixed(v)) {
return Buffer.from(padToEven(stripHexPrefix(v)), 'hex');
}
else {
return Buffer.from(v);
}
/** Removes 0x from a given String */
function stripHexPrefix(str) {
if (typeof str !== 'string') {
return str;
}
return isHexPrefixed(str) ? str.slice(2) : str;
}
/** Transform anything into a Uint8Array */
function toBytes(v) {
if (v instanceof Uint8Array) {
return v;
}
if (typeof v === 'string') {
if (isHexPrefixed(v)) {
return hexToBytes(padToEven(stripHexPrefix(v)));
}
else if (typeof v === 'number' || typeof v === 'bigint') {
if (!v) {
return Buffer.from([]);
}
else {
return intToBuffer(v);
}
return utf8ToBytes(v);
}
if (typeof v === 'number' || typeof v === 'bigint') {
if (!v) {
return Uint8Array.from([]);
}
else if (v === null || v === undefined) {
return Buffer.from([]);
}
else if (v instanceof Uint8Array) {
return Buffer.from(v);
}
else if (bn_js_1.default.isBN(v)) {
// converts a BN to a Buffer
return Buffer.from(v.toArray());
}
else {
throw new Error('invalid type');
}
return hexToBytes(numberToHex(v));
}
return v;
if (v === null || v === undefined) {
return Uint8Array.from([]);
}
throw new Error('toBytes: received unsupported type ' + typeof v);
}
exports.utils = {
bytesToHex,
concatBytes,
hexToBytes,
utf8ToBytes,
};
const RLP = { encode, decode };
exports.default = RLP;
//# sourceMappingURL=index.js.map
{
"name": "rlp",
"version": "2.2.7",
"version": "3.0.0",
"description": "Recursive Length Prefix Encoding Module",
"license": "MPL-2.0",
"keywords": [
"rlp",
"ethereum"
],
"files": [
"dist",
"dist.browser",
"bin",
"src"
],
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"browser": "dist.browser/index.js",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"bin": {
"rlp": "./bin/rlp"
"rlp": "bin/rlp"
},
"scripts": {
"build": "tsc -p ./tsconfig.prod.json && tsc -p tsconfig.browser.json",
"prepublishOnly": "npm run test && npm run build",
"coverage": "npm run build && nyc --reporter=lcov npm run test:unit",
"tsc": "tsc -p ./tsconfig.prod.json --noEmit",
"lint": "ethereumjs-config-lint",
"lint:fix": "ethereumjs-config-lint-fix",
"test": "npm run lint && npm run build && npm run test:unit && npm run test:browser",
"test:unit": "mocha --reporter spec --require ts-node/register test/*.spec.ts",
"build": "../../config/cli/ts-build.sh node",
"prepublishOnly": "../../config/cli/prepublish.sh",
"clean": "../../config/cli/clean-package.sh",
"coverage": "../../config/cli/coverage.sh",
"tsc": "../../config/cli/ts-compile.sh",
"lint": "../../config/cli/lint.sh",
"lint:fix": "../../config/cli/lint-fix.sh",
"tape": "tape -r ts-node/register",
"test": "npm run test:node && npm run test:browser",
"test:node": "npm run tape -- test/*.spec.ts",
"test:browser": "karma start karma.conf.js"
},
"husky": {
"hooks": {
"pre-push": "npm run lint"
}
"devDependencies": {
"@types/node": "^16.11.7",
"@types/tape": "^4.13.2",
"karma": "^6.3.4",
"karma-chrome-launcher": "^3.1.0",
"karma-firefox-launcher": "^2.1.1",
"karma-tap": "^4.2.0",
"karma-typescript": "^5.5.3",
"nyc": "^15.1.0",
"prettier": "^2.4.1",
"tape": "^5.3.1",
"ts-node": "^10.2.1",
"typescript": "^4.4.2"
},
"repository": {
"type": "git",
"url": "https://github.com/ethereumjs/rlp.git"
},
"keywords": [
"rlp",
"ethereum"
],
"author": {

@@ -47,30 +53,13 @@ "name": "martin becze",

"Alex Beregszaszi <alex@rtfs.hu>",
"Holger Drewes <Holger.Drewes@gmail.com>"
"Holger Drewes <Holger.Drewes@gmail.com>",
"Paul Miller <pkg@paulmillr.com>"
],
"license": "MPL-2.0",
"repository": {
"type": "git",
"url": "https://github.com/ethereumjs/ethereumjs-monorepo.git"
},
"homepage": "https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/rlp#readme",
"bugs": {
"url": "https://github.com/ethereumjs/rlp/issues"
},
"dependencies": {
"bn.js": "^5.2.0"
},
"devDependencies": {
"@ethereumjs/eslint-config-defaults": "^2.0.0",
"@ethereumjs/config-coverage": "^2.0.0",
"@ethereumjs/config-typescript": "^2.0.0",
"@types/bn.js": "^5.1.0",
"@types/mocha": "^9.0.0",
"@types/node": "^12.13.0",
"husky": "^4.2.5",
"karma": "^6.3.4",
"karma-chrome-launcher": "^3.1.0",
"karma-firefox-launcher": "^2.1.1",
"karma-mocha": "^2.0.1",
"karma-typescript": "^5.5.2",
"mocha": "7.1.2",
"nyc": "^15.1.0",
"prettier": "^2.4.1",
"ts-node": "^10.2.1",
"typescript": "^4.4.3"
"url": "https://github.com/ethereumjs/ethereumjs-monorepo/issues?q=is%3Aissue+label%3A%22package%3A+rlp%22"
}
}

@@ -1,9 +0,10 @@

# SYNOPSIS
# rlp
[![NPM Package](https://img.shields.io/npm/v/rlp.svg)](https://www.npmjs.org/package/rlp)
[![Actions Status](https://github.com/ethereumjs/rlp/workflows/Build/badge.svg)](https://github.com/ethereumjs/rlp/actions)
[![Coverage Status](https://img.shields.io/coveralls/ethereumjs/rlp.svg)](https://coveralls.io/r/ethereumjs/rlp)
[![Discord](https://img.shields.io/static/v1?logo=discord&label=discord&message=Join&color=blue)](https://discord.gg/TNwARpR)
[![NPM Package][rlp-npm-badge]][rlp-npm-link]
[![GitHub Issues][rlp-issues-badge]][rlp-issues-link]
[![Actions Status][rlp-actions-badge]][rlp-actions-link]
[![Code Coverage][rlp-coverage-badge]][rlp-coverage-link]
[![Discord][discord-badge]][discord-link]
[Recursive Length](https://github.com/ethereum/wiki/wiki/RLP) Prefix Encoding for Node.js.
[Recursive Length Prefix](https://eth.wiki/en/fundamentals/rlp) encoding for Node.js and the browser.

@@ -14,3 +15,3 @@ ## INSTALL

install with `-g` if you want to use the cli.
install with `-g` if you want to use the CLI.

@@ -20,8 +21,8 @@ ## USAGE

```typescript
import * as assert from 'assert'
import * as rlp from 'rlp'
import assert from 'assert'
import RLP from 'rlp'
const nestedList = [[], [[]], [[], [[]]]]
const encoded = rlp.encode(nestedList)
const decoded = rlp.decode(encoded)
const encoded = RLP.encode(nestedList)
const decoded = RLP.decode(encoded)
assert.deepEqual(nestedList, decoded)

@@ -32,11 +33,35 @@ ```

`rlp.encode(plain)` - RLP encodes an `Array`, `Buffer` or `String` and returns a `Buffer`.
`RLP.encode(plain)` - RLP encodes an `Array`, `Uint8Array` or `String` and returns a `Uint8Array`.
`rlp.decode(encoded, [skipRemainderCheck=false])` - Decodes an RLP encoded `Buffer`, `Array` or `String` and returns a `Buffer` or an `Array` of `Buffers`. If `skipRemainderCheck` is enabled, `rlp` will just decode the first rlp sequence in the buffer. By default, it would throw an error if there are more bytes in Buffer than used by rlp sequence.
`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.
### Buffer compatibility
If you would like to continue using Buffers like in rlp v2, you can use:
```typescript
import assert from 'assert'
import { arrToBufArr, bufArrToArr } from 'ethereumjs-util'
import RLP from 'rlp'
const bufferList = [Buffer.from('123', 'hex'), Buffer.from('456', 'hex')]
const encoded = RLP.encode(bufArrToArr(bufferList))
const encodedAsBuffer = Buffer.from(encoded)
const decoded = RLP.decode(Uint8Array.from(encodedAsBuffer)) // or RLP.decode(encoded)
const decodedAsBuffers = arrToBufArr(decoded)
assert.deepEqual(bufferList, decodedAsBuffers)
```
## CLI
`rlp decode <hex string>`
`rlp encode <json String>`
`rlp encode <JSON string>`\
`rlp decode <0x-prefixed hex string>`
### Examples
- `rlp encode '5'` -> `0x05`
- `rlp encode '[5]'` -> `0xc105`
- `rlp encode '["cat", "dog"]'` -> `0xc88363617483646f67`
- `rlp decode 0xc88363617483646f67` -> `["cat","dog"]`
## TESTS

@@ -48,14 +73,11 @@

To auto fix linting problems use: `npm run lint:fix`
To auto-fix linting problems run: `npm run lint:fix`
## CODE COVERAGE
Install dev dependencies
`npm install`
Install dev dependencies: `npm install`
Run
`npm run coverage`
Run coverage: `npm run coverage`
The results are at
`coverage/lcov-report/index.html`
The results will be at: `coverage/lcov-report/index.html`

@@ -67,1 +89,12 @@ # EthereumJS

If you want to join for work or do improvements on the libraries have a look at our [contribution guidelines](https://ethereumjs.readthedocs.io/en/latest/contributing.html).
[discord-badge]: https://img.shields.io/static/v1?logo=discord&label=discord&message=Join&color=blue
[discord-link]: https://discord.gg/TNwARpR
[rlp-npm-badge]: https://img.shields.io/npm/v/rlp.svg
[rlp-npm-link]: https://www.npmjs.com/package/rlp
[rlp-issues-badge]: https://img.shields.io/github/issues/ethereumjs/ethereumjs-monorepo/package:%20rlp?label=issues
[rlp-issues-link]: https://github.com/ethereumjs/ethereumjs-monorepo/issues?q=is%3Aopen+is%3Aissue+label%3A"package%3A+rlp"
[rlp-actions-badge]: https://github.com/ethereumjs/ethereumjs-monorepo/workflows/rlp/badge.svg
[rlp-actions-link]: https://github.com/ethereumjs/ethereumjs-monorepo/actions?query=workflow%3A%22rlp%22
[rlp-coverage-badge]: https://codecov.io/gh/ethereumjs/ethereumjs-monorepo/branch/master/graph/badge.svg?flag=rlp
[rlp-coverage-link]: https://codecov.io/gh/ethereumjs/ethereumjs-monorepo/tree/master/packages/rlp

@@ -1,70 +0,83 @@

import BN from 'bn.js'
export type Input = string | number | bigint | Uint8Array | Array<Input> | null | undefined
import { Decoded, Input, List } from './types'
export type NestedUint8Array = Array<Uint8Array | NestedUint8Array>
// Types exported outside of this package
export { Decoded, Input, List }
export interface Decoded {
data: Uint8Array | NestedUint8Array
remainder: Uint8Array
}
/**
* RLP Encoding based on: https://github.com/ethereum/wiki/wiki/%5BEnglish%5D-RLP
* This function takes in a data, convert it to buffer if not, and a length for recursion
* @param input - will be converted to buffer
* @returns returns buffer of encoded data
* RLP Encoding based on https://eth.wiki/en/fundamentals/rlp
* This function takes in data, converts it to Uint8Array if not,
* and adds a length for recursion.
* @param input Will be converted to Uint8Array
* @returns Uint8Array of encoded data
**/
export function encode(input: Input): Buffer {
export function encode(input: Input): Uint8Array {
if (Array.isArray(input)) {
const output: Buffer[] = []
const output: Uint8Array[] = []
for (let i = 0; i < input.length; i++) {
output.push(encode(input[i]))
}
const buf = Buffer.concat(output)
return Buffer.concat([encodeLength(buf.length, 192), buf])
} else {
const inputBuf = toBuffer(input)
return inputBuf.length === 1 && inputBuf[0] < 128
? inputBuf
: Buffer.concat([encodeLength(inputBuf.length, 128), inputBuf])
const buf = concatBytes(...output)
return concatBytes(encodeLength(buf.length, 192), buf)
}
const inputBuf = toBytes(input)
if (inputBuf.length === 1 && inputBuf[0] < 128) {
return inputBuf
}
return concatBytes(encodeLength(inputBuf.length, 128), inputBuf)
}
/**
* Slices a Uint8Array, throws if the slice goes out-of-bounds of the Uint8Array.
* E.g. `safeSlice(hexToBytes('aa'), 1, 2)` will throw.
* @param input
* @param start
* @param end
*/
function safeSlice(input: Uint8Array, start: number, end: number) {
if (end > input.length) {
throw new Error('invalid RLP (safeSlice): end slice of Uint8Array out-of-bounds')
}
return input.slice(start, end)
}
/**
* Parse integers. Check if there is no leading zeros
* @param v The value to parse
* @param base The base to parse the integer into
*/
function safeParseInt(v: string, base: number): number {
if (v[0] === '0' && v[1] === '0') {
function decodeLength(v: Uint8Array): number {
if (v[0] === 0) {
throw new Error('invalid RLP: extra zeros')
}
return parseInt(v, base)
return parseHexByte(bytesToHex(v))
}
function encodeLength(len: number, offset: number): Buffer {
function encodeLength(len: number, offset: number): Uint8Array {
if (len < 56) {
return Buffer.from([len + offset])
} else {
const hexLength = intToHex(len)
const lLength = hexLength.length / 2
const firstByte = intToHex(offset + 55 + lLength)
return Buffer.from(firstByte + hexLength, 'hex')
return Uint8Array.from([len + offset])
}
const hexLength = numberToHex(len)
const lLength = hexLength.length / 2
const firstByte = numberToHex(offset + 55 + lLength)
return Uint8Array.from(hexToBytes(firstByte + hexLength))
}
/**
* RLP Decoding based on: {@link https://github.com/ethereum/wiki/wiki/%5BEnglish%5D-RLP|RLP}
* @param input - will be converted to buffer
* @param stream - Is the input a stream (false by default)
* @returns - returns decode Array of Buffers containg the original message
* RLP Decoding based on https://eth.wiki/en/fundamentals/rlp
* @param input Will be converted to Uint8Array
* @param stream Is the input a stream (false by default)
* @returns decoded Array of Uint8Arrays containing the original message
**/
export function decode(input: Buffer, stream?: boolean): Buffer
export function decode(input: Buffer[], stream?: boolean): Buffer[]
export function decode(input: Input, stream?: boolean): Buffer[] | Buffer | Decoded
export function decode(input: Input, stream: boolean = false): Buffer[] | Buffer | Decoded {
export function decode(input: Input, stream?: false): Uint8Array | NestedUint8Array
export function decode(input: Input, stream?: true): Decoded
export function decode(input: Input, stream = false): Uint8Array | NestedUint8Array | Decoded {
if (!input || (input as any).length === 0) {
return Buffer.from([])
return Uint8Array.from([])
}
const inputBuffer = toBuffer(input)
const decoded = _decode(inputBuffer)
const inputBytes = toBytes(input)
const decoded = _decode(inputBytes)

@@ -75,3 +88,3 @@ if (stream) {

if (decoded.remainder.length !== 0) {
throw new Error('invalid remainder')
throw new Error('invalid RLP: remainder must be zero')
}

@@ -82,35 +95,5 @@

/**
* Get the length of the RLP input
* @param input
* @returns The length of the input or an empty Buffer if no input
*/
export function getLength(input: Input): Buffer | number {
if (!input || (input as any).length === 0) {
return Buffer.from([])
}
const inputBuffer = toBuffer(input)
const firstByte = inputBuffer[0]
if (firstByte <= 0x7f) {
return inputBuffer.length
} else if (firstByte <= 0xb7) {
return firstByte - 0x7f
} else if (firstByte <= 0xbf) {
return firstByte - 0xb6
} else if (firstByte <= 0xf7) {
// a list between 0-55 bytes long
return firstByte - 0xbf
} else {
// a list over 55 bytes long
const llength = firstByte - 0xf6
const length = safeParseInt(inputBuffer.slice(1, llength).toString('hex'), 16)
return llength + length
}
}
/** Decode an input with RLP */
function _decode(input: Buffer): Decoded {
let length, llength, data, innerRemainder, d
function _decode(input: Uint8Array): Decoded {
let length: number, llength: number, data: Uint8Array, innerRemainder: Uint8Array, d: Decoded
const decoded = []

@@ -132,9 +115,9 @@ const firstByte = input[0]

if (firstByte === 0x80) {
data = Buffer.from([])
data = Uint8Array.from([])
} else {
data = input.slice(1, length)
data = safeSlice(input, 1, length)
}
if (length === 2 && data[0] < 0x80) {
throw new Error('invalid rlp encoding: byte must be less 0x80')
throw new Error('invalid RLP encoding: invalid prefix, single byte < 0x80 are not prefixed')
}

@@ -153,10 +136,7 @@

}
length = safeParseInt(input.slice(1, llength).toString('hex'), 16)
length = decodeLength(safeSlice(input, 1, llength))
if (length <= 55) {
throw new Error('invalid RLP: expected string length to be greater than 55')
}
data = input.slice(llength, length + llength)
if (data.length < length) {
throw new Error('invalid RLP: not enough bytes for string')
}
data = safeSlice(input, llength, length + llength)

@@ -168,8 +148,8 @@ return {

} else if (firstByte <= 0xf7) {
// a list between 0-55 bytes long
// a list between 0-55 bytes long
length = firstByte - 0xbf
innerRemainder = input.slice(1, length)
innerRemainder = safeSlice(input, 1, length)
while (innerRemainder.length) {
d = _decode(innerRemainder)
decoded.push(d.data as Buffer)
decoded.push(d.data)
innerRemainder = d.remainder

@@ -183,20 +163,21 @@ }

} else {
// a list over 55 bytes long
// a list over 55 bytes long
llength = firstByte - 0xf6
length = safeParseInt(input.slice(1, llength).toString('hex'), 16)
length = decodeLength(safeSlice(input, 1, llength))
if (length < 56) {
throw new Error('invalid RLP: encoded list too short')
}
const totalLength = llength + length
if (totalLength > input.length) {
throw new Error('invalid rlp: total length is larger than the data')
throw new Error('invalid RLP: total length is larger than the data')
}
innerRemainder = input.slice(llength, totalLength)
if (innerRemainder.length === 0) {
throw new Error('invalid rlp, List has a invalid length')
}
innerRemainder = safeSlice(input, llength, totalLength)
while (innerRemainder.length) {
d = _decode(innerRemainder)
decoded.push(d.data as Buffer)
decoded.push(d.data)
innerRemainder = d.remainder
}
return {

@@ -209,17 +190,56 @@ data: decoded,

/** Check if a string is prefixed by 0x */
function isHexPrefixed(str: string): boolean {
return str.slice(0, 2) === '0x'
const cachedHexes = Array.from({ length: 256 }, (_v, i) => i.toString(16).padStart(2, '0'))
function bytesToHex(uint8a: Uint8Array): string {
// Pre-caching chars with `cachedHexes` speeds this up 6x
let hex = ''
for (let i = 0; i < uint8a.length; i++) {
hex += cachedHexes[uint8a[i]]
}
return hex
}
/** Removes 0x from a given String */
function stripHexPrefix(str: string): string {
if (typeof str !== 'string') {
return str
function parseHexByte(hexByte: string): number {
const byte = Number.parseInt(hexByte, 16)
if (Number.isNaN(byte)) throw new Error('Invalid byte sequence')
return byte
}
// Caching slows it down 2-3x
function hexToBytes(hex: string): Uint8Array {
if (typeof hex !== 'string') {
throw new TypeError('hexToBytes: expected string, got ' + typeof hex)
}
return isHexPrefixed(str) ? str.slice(2) : str
if (hex.length % 2) throw new Error('hexToBytes: received invalid unpadded hex')
const array = new Uint8Array(hex.length / 2)
for (let i = 0; i < array.length; i++) {
const j = i * 2
array[i] = parseHexByte(hex.slice(j, j + 2))
}
return array
}
/** Concatenates two Uint8Arrays into one. */
function concatBytes(...arrays: Uint8Array[]): Uint8Array {
if (arrays.length === 1) return arrays[0]
const length = arrays.reduce((a, arr) => a + arr.length, 0)
const result = new Uint8Array(length)
for (let i = 0, pad = 0; i < arrays.length; i++) {
const arr = arrays[i]
result.set(arr, pad)
pad += arr.length
}
return result
}
// Global symbols in both browsers and Node.js since v11
// See https://github.com/microsoft/TypeScript/issues/31535
declare const TextEncoder: any
declare const TextDecoder: any
function utf8ToBytes(utf: string): Uint8Array {
return new TextEncoder().encode(utf)
}
/** Transform an integer into its hexadecimal value */
function intToHex(integer: number | bigint): string {
function numberToHex(integer: number | bigint): string {
if (integer < 0) {

@@ -237,35 +257,46 @@ throw new Error('Invalid integer as argument, must be unsigned!')

/** Transform an integer into a Buffer */
function intToBuffer(integer: number | bigint): Buffer {
const hex = intToHex(integer)
return Buffer.from(hex, 'hex')
/** Check if a string is prefixed by 0x */
function isHexPrefixed(str: string): boolean {
return str.length >= 2 && str[0] === '0' && str[1] === 'x'
}
/** Transform anything into a Buffer */
function toBuffer(v: Input): Buffer {
if (!Buffer.isBuffer(v)) {
if (typeof v === 'string') {
if (isHexPrefixed(v)) {
return Buffer.from(padToEven(stripHexPrefix(v)), 'hex')
} else {
return Buffer.from(v)
}
} else if (typeof v === 'number' || typeof v === 'bigint') {
if (!v) {
return Buffer.from([])
} else {
return intToBuffer(v)
}
} else if (v === null || v === undefined) {
return Buffer.from([])
} else if (v instanceof Uint8Array) {
return Buffer.from(v as any)
} else if (BN.isBN(v)) {
// converts a BN to a Buffer
return Buffer.from(v.toArray())
} else {
throw new Error('invalid type')
/** Removes 0x from a given String */
function stripHexPrefix(str: string): string {
if (typeof str !== 'string') {
return str
}
return isHexPrefixed(str) ? str.slice(2) : str
}
/** Transform anything into a Uint8Array */
function toBytes(v: Input): Uint8Array {
if (v instanceof Uint8Array) {
return v
}
if (typeof v === 'string') {
if (isHexPrefixed(v)) {
return hexToBytes(padToEven(stripHexPrefix(v)))
}
return utf8ToBytes(v)
}
return v
if (typeof v === 'number' || typeof v === 'bigint') {
if (!v) {
return Uint8Array.from([])
}
return hexToBytes(numberToHex(v))
}
if (v === null || v === undefined) {
return Uint8Array.from([])
}
throw new Error('toBytes: received unsupported type ' + typeof v)
}
export const utils = {
bytesToHex,
concatBytes,
hexToBytes,
utf8ToBytes,
}
const RLP = { encode, decode }
export default RLP

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc