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:
Jaco Greeff
2018-02-08 14:50:54 +01:00
committed by GitHub
parent a18e7f8474
commit e832aebb07
64 changed files with 1073 additions and 188 deletions
+42
View File
@@ -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;
+20
View File
@@ -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;
};
+35
View File
@@ -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)
};
};
+23
View File
@@ -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);
});
});
+13
View File
@@ -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)
};
};
+28
View File
@@ -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)
};
};
+11
View File
@@ -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;