Introduce @polkadot/util-rlp (#76)
* Add toU8a conversions * Add u8a utilities * @polkadot/rlp with encoding * Decoding test runthrough * Update util locations * Convert util-triehash to use util-rlp * Allow keccak from Uint8Array * Pass Uint8Array to keccak input * Update documentation * Skip some doc generation * Update @polkadot/dev * Update doc typo * Update allocations * Retrieve tests from ethereumjs-testing * Simplify test retrieval * Exclude etereumjs-testing from flow
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { DecodeOutput } from './types';
|
||||
|
||||
const decodeListLong = require('./listLong');
|
||||
const decodeListShort = require('./listShort');
|
||||
const decodeNumber = require('./number');
|
||||
const decodeSingle = require('./single');
|
||||
const decodeString = require('./string');
|
||||
|
||||
function decode (input: Uint8Array): DecodeOutput {
|
||||
const firstByte = input[0];
|
||||
|
||||
// a single byte whose value is in the [0x00, 0x7f] range, that byte is its own RLP encoding
|
||||
if (firstByte <= 0x7f) {
|
||||
return decodeSingle(decode, input);
|
||||
}
|
||||
|
||||
// string is 0-55 bytes long. A single byte with value 0x80 plus the length of the string followed by the string
|
||||
// The range of the first byte is [0x80, 0xb7]
|
||||
if (firstByte <= 0xb7) {
|
||||
return decodeString(decode, input);
|
||||
}
|
||||
|
||||
// a number
|
||||
if (firstByte <= 0xbf) {
|
||||
return decodeNumber(decode, input);
|
||||
}
|
||||
|
||||
// a list between 0-55 bytes long
|
||||
if (firstByte <= 0xf7) {
|
||||
return decodeListShort(decode, input);
|
||||
}
|
||||
|
||||
// a list over 55 bytes long
|
||||
return decodeListLong(decode, input);
|
||||
}
|
||||
|
||||
module.exports = decode;
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
const assert = require('@polkadot/util/assert');
|
||||
|
||||
const internalDecode = require('./decode');
|
||||
|
||||
module.exports = function decoder (input?: Uint8Array): Uint8Array | Array<*> {
|
||||
if (!input || input.length === 0) {
|
||||
return new Uint8Array([]);
|
||||
}
|
||||
|
||||
const { decoded, remainder } = internalDecode(input);
|
||||
|
||||
assert(remainder.length === 0, 'invalid remainder');
|
||||
|
||||
return decoded;
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { DecodeFunc, DecodeOutput } from './types';
|
||||
|
||||
const assert = require('@polkadot/util/assert');
|
||||
|
||||
const safeParseInt = require('./safeParseInt');
|
||||
|
||||
module.exports = function decodeListLong (decode: DecodeFunc, input: Uint8Array): DecodeOutput {
|
||||
const llength = input[0] - 0xf6;
|
||||
const length = safeParseInt(input.slice(1, llength));
|
||||
const totalLength = llength + length;
|
||||
const decoded = [];
|
||||
|
||||
assert(totalLength <= input.length, 'invalid rlp: total length is larger than the data');
|
||||
|
||||
let innerRemainder = input.slice(llength, totalLength);
|
||||
|
||||
assert(innerRemainder.length > 0, 'invalid rlp, List has a invalid length');
|
||||
|
||||
while (innerRemainder.length) {
|
||||
const d = decode(innerRemainder);
|
||||
|
||||
decoded.push(d.decoded);
|
||||
innerRemainder = d.remainder;
|
||||
}
|
||||
|
||||
return {
|
||||
decoded,
|
||||
remainder: input.slice(totalLength)
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { DecodeFunc, DecodeOutput } from './types';
|
||||
|
||||
module.exports = function decodeListShort (decode: DecodeFunc, input: Uint8Array): DecodeOutput {
|
||||
const length = input[0] - 0xbf;
|
||||
let innerRemainder = input.slice(1, length);
|
||||
const decoded = [];
|
||||
|
||||
while (innerRemainder.length) {
|
||||
const d = decode(innerRemainder);
|
||||
|
||||
decoded.push(d.decoded);
|
||||
innerRemainder = d.remainder;
|
||||
}
|
||||
|
||||
return {
|
||||
decoded,
|
||||
remainder: input.slice(length)
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { DecodeFunc, DecodeOutput } from './types';
|
||||
|
||||
const assert = require('@polkadot/util/assert');
|
||||
|
||||
const safeParseInt = require('./safeParseInt');
|
||||
|
||||
module.exports = function decodeNumber (decode: DecodeFunc, input: Uint8Array): DecodeOutput {
|
||||
const llength = input[0] - 0xb6;
|
||||
const length = safeParseInt(input.slice(1, llength));
|
||||
const decoded = input.slice(llength, length + llength);
|
||||
|
||||
assert(decoded.length === length, 'invalid RLP');
|
||||
|
||||
return {
|
||||
decoded,
|
||||
remainder: input.slice(length + llength)
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
const assert = require('@polkadot/util/assert');
|
||||
|
||||
module.exports = function safeParseInt (input: Uint8Array): number {
|
||||
assert(input[0] > 0, 'invalid RLP: extra zeros');
|
||||
|
||||
return input.reduce((result, value) => (result * 256) + value, 0);
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
const safeParseInt = require('./safeParseInt');
|
||||
|
||||
describe('safeParseInt', () => {
|
||||
it('does not allow 0 in first position', () => {
|
||||
expect(
|
||||
() => safeParseInt(
|
||||
new Uint8Array([0])
|
||||
)
|
||||
).toThrow(/invalid RLP: extra zeros/);
|
||||
});
|
||||
|
||||
it('converts a value to the correct number', () => {
|
||||
expect(
|
||||
safeParseInt(
|
||||
new Uint8Array([0x12, 0x34, 0x56])
|
||||
)
|
||||
).toEqual(0x123456);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { DecodeFunc, DecodeOutput } from './types';
|
||||
|
||||
module.exports = function decodeSingle (decode: DecodeFunc, input: Uint8Array): DecodeOutput {
|
||||
return {
|
||||
decoded: input.slice(0, 1),
|
||||
remainder: input.slice(1)
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { DecodeFunc, DecodeOutput } from './types';
|
||||
|
||||
const assert = require('@polkadot/util/assert');
|
||||
|
||||
module.exports = function decodeString (decode: DecodeFunc, input: Uint8Array): DecodeOutput {
|
||||
const firstByte = input[0];
|
||||
const length = firstByte - 0x7f;
|
||||
let decoded;
|
||||
|
||||
// set 0x80 null to 0
|
||||
if (firstByte === 0x80) {
|
||||
decoded = new Uint8Array([]);
|
||||
} else {
|
||||
decoded = input.slice(1, length);
|
||||
}
|
||||
|
||||
assert(!(length === 2 && decoded[0] < 0x80), 'invalid rlp encoding: byte must be less 0x80');
|
||||
|
||||
return {
|
||||
decoded,
|
||||
remainder: input.slice(length)
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
export type DecodeOutput = {
|
||||
decoded: Uint8Array | Array<*>,
|
||||
remainder: Uint8Array
|
||||
}
|
||||
|
||||
export type DecodeFunc = (input: Uint8Array) => DecodeOutput;
|
||||
Reference in New Issue
Block a user