Address type cleanups (#337)

* AccountIndex allows only <= 0xef for 1 byte

* Max 1-byte constructed from prefix

* Expand Address tests, add ts checks
This commit is contained in:
Jaco Greeff
2018-10-22 19:05:05 +02:00
committed by GitHub
parent d2eb6e5e5b
commit 3ac4a3b5be
7 changed files with 220 additions and 177 deletions
-55
View File
@@ -1,55 +0,0 @@
// Copyright 2017-2018 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import AccountId from './AccountId';
import U8a from './codec/U8a';
const testDecode = (type, input, expected) =>
it(`can decode from ${type}`, () => {
const a = new AccountId(input);
expect(a.toString()).toBe(expected);
});
const testEncode = (to, expected) =>
it(`can encode ${to}`, () => {
const a = new AccountId('5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s');
expect(a[to]()).toEqual(expected);
});
describe('AccountId', () => {
testDecode(
'AccountId',
new AccountId('0x0102030405060708010203040506070801020304050607080102030405060708'),
'5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'
);
testDecode('hex', '0x0102030405060708010203040506070801020304050607080102030405060708', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s');
testDecode('string', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s');
testDecode(
'U8a',
new U8a([
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8
]),
'5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'
);
testDecode(
'Uint8Array',
Uint8Array.from([
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8
]),
'5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'
);
testEncode('toHex', '0x0102030405060708010203040506070801020304050607080102030405060708');
testEncode('toJSON', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s');
testEncode('toString', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s');
testEncode('toU8a', Uint8Array.from([
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8
]));
});
+57
View File
@@ -0,0 +1,57 @@
// Copyright 2017-2018 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import AccountId from './AccountId';
import U8a from './codec/U8a';
describe('AccountId', () => {
describe('decoding', () => {
const testDecode = (type: string, input: Uint8Array | string | AccountId | U8a, expected: string) =>
it(`can decode from ${type}`, () => {
const a = new AccountId(input);
expect(a.toString()).toBe(expected);
});
testDecode(
'AccountId',
new AccountId('0x0102030405060708010203040506070801020304050607080102030405060708'),
'5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'
);
testDecode('hex', '0x0102030405060708010203040506070801020304050607080102030405060708', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s');
testDecode('string', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s');
testDecode(
'U8a',
new U8a([
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8
]),
'5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'
);
testDecode(
'Uint8Array',
Uint8Array.from([
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8
]),
'5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'
);
});
describe('encoding', () => {
const testEncode = (to: 'toHex' | 'toJSON' | 'toString' | 'toU8a', expected: Uint8Array | string) =>
it(`can encode ${to}`, () => {
const a = new AccountId('5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s');
expect(a[to]()).toEqual(expected);
});
testEncode('toHex', '0x0102030405060708010203040506070801020304050607080102030405060708');
testEncode('toJSON', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s');
testEncode('toString', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s');
testEncode('toU8a', Uint8Array.from([
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8
]));
});
});
@@ -12,6 +12,12 @@ describe('AccountIndex', () => {
).toEqual('336794129');
});
it('creates a BN representation (from ss-58)', () => {
expect(
new AccountIndex('Mwz15xN8').toBn().toString()
).toEqual('336794129');
});
it('constructs 2-byte from number', () => {
expect(
new AccountIndex(256 * 1).toString()
@@ -21,6 +27,27 @@ describe('AccountIndex', () => {
it('constructs from number', () => {
expect(
new AccountIndex(new BN(336794129)).toString()
).toEqual('3N5RJXxJhgRhxu');
).toEqual('Mwz15xN8');
});
describe('calcLength', () => {
const testLength = (value: number, length: number) =>
expect(AccountIndex.calcLength(value)).toEqual(length);
it('returns 1 for <= 0xef', () => {
testLength(0xef, 1);
});
it('returns 2 for > 0xef', () => {
testLength(0xf0, 2);
});
it('returns 4 bytes for 32-bit inputs', () => {
testLength(0xffeeddcc, 4);
});
it('returns 8 bytes for larger inputs', () => {
testLength(0x122334455, 8);
});
});
});
+31 -20
View File
@@ -11,6 +11,14 @@ import U8a from './codec/U8a';
export const ENUMSET_SIZE = new BN(64);
const PREFIX_1BYTE = 0xef;
const PREFIX_2BYTE = 0xfc;
const PREFIX_4BYTE = 0xfd;
const PREFIX_8BYTE = 0xfe;
const MAX_1BYTE = new BN(PREFIX_1BYTE);
const MAX_2BYTE = new BN(1).shln(16);
const MAX_4BYTE = new BN(1).shln(32);
// A wrapper around an AccountIndex, which is a shortened, variable-length encoding
// for an Account. We extends from U8a which is basically
// just a Uint8Array wrapper.
@@ -35,20 +43,9 @@ export default class AccountIndex extends U8a {
return value.subarray(offset, offset + length);
} else if (isNumber(value) || isBn(value)) {
const index = bnToBn(value);
const bitLength = index.ltn(1 << 8)
? 8
: (
index.ltn(1 << 16)
? 16
: (
index.ltn(1 << 32)
? 32
: 64
)
);
const bitLength = 8 * AccountIndex.calcLength(value);
return bnToU8a(index, bitLength, true);
return bnToU8a(value, bitLength, true);
} else if (isHex(value)) {
return hexToU8a(value);
}
@@ -56,16 +53,30 @@ export default class AccountIndex extends U8a {
return decodeAddress(value);
}
static calcLength (_value: BN | number): number {
const value = bnToBn(_value);
if (value.lte(MAX_1BYTE)) {
return 1;
} else if (value.lt(MAX_2BYTE)) {
return 2;
} else if (value.lt(MAX_4BYTE)) {
return 4;
}
return 8;
}
static readLength (input: Uint8Array): [number, number] {
const first = input[0];
if (first <= 0xef) {
if (first <= PREFIX_1BYTE) {
return [0, 1];
} else if (first === 0xfc) {
} else if (first === PREFIX_2BYTE) {
return [1, 2];
} else if (first === 0xfd) {
} else if (first === PREFIX_4BYTE) {
return [1, 4];
} else if (first === 0xfe) {
} else if (first === PREFIX_8BYTE) {
return [1, 8];
}
@@ -74,9 +85,9 @@ export default class AccountIndex extends U8a {
static writeLength (input: Uint8Array): Uint8Array {
switch (input.length) {
case 2: return new Uint8Array([0xfc]);
case 4: return new Uint8Array([0xfd]);
case 8: return new Uint8Array([0xfe]);
case 2: return new Uint8Array([PREFIX_2BYTE]);
case 4: return new Uint8Array([PREFIX_4BYTE]);
case 8: return new Uint8Array([PREFIX_8BYTE]);
default: return new Uint8Array([]);
}
}
-99
View File
@@ -1,99 +0,0 @@
// Copyright 2017-2018 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import AccountId from './AccountId';
import AccountIndex from './AccountIndex';
import Address from './Address';
const testDecode = (type, input, expected) =>
it(`can decode from ${type}`, () => {
const a = new Address(input);
expect(a.toString()).toBe(expected);
});
const testEncode = (to, expected) =>
it(`can encode ${to}`, () => {
const a = new Address('5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s');
expect(a[to]()).toEqual(expected);
});
describe('Address', () => {
testDecode(
'Address',
new Address('5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'),
'5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'
);
testDecode(
'AccountId',
new AccountId('5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'),
'5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'
);
testDecode(
'AccountIndex',
new Address('PrF3'),
// NOTE Expected adress here is encoded with prefix 42, input above with 68
'F7ih'
);
testDecode(
'AccountIndex',
new AccountIndex('0x0001'),
'25GUyk'
);
testDecode(
'Array',
[
255,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8
],
'5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'
);
testDecode(
'Uint8Array (with prefix 255)',
Uint8Array.from([
255,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8
]),
'5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'
);
testDecode(
'Uint8Array (with prefix 1 byte)',
Uint8Array.from([1]),
'F7L6'
);
testDecode(
'Uint8Array (with prefix 2 bytes)',
Uint8Array.from([0xfc, 0, 1]),
'25GUyk'
);
testDecode(
'Uint8Array (with prefix 4 bytes)',
Uint8Array.from([0xfd, 17, 18, 19, 20]),
'Mwz15xN8'
);
testDecode(
'Uint8Array (with prefix 8 bytes)',
Uint8Array.from([0xfe, 17, 18, 19, 20, 21, 22, 23, 24]),
'3N5RJXxM5fLd4h'
);
// FIXME Add this
// testEncode(
// 'toHex',
// '0x0102030405060708010203040506070801020304050607080102030405060708'
// );
testEncode(
'toString',
'5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'
);
testEncode(
'toU8a',
Uint8Array.from([
255,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8
])
);
});
+102
View File
@@ -0,0 +1,102 @@
// Copyright 2017-2018 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import AccountId from './AccountId';
import AccountIndex from './AccountIndex';
import Address from './Address';
describe('Address', () => {
const testDecode = (type: string, input: Address | AccountId | AccountIndex | Array<number> | Uint8Array, expected: string) =>
it(`can decode from ${type}`, () => {
const a = new Address(input);
expect(a.toString()).toBe(expected);
});
describe('decoding', () => {
testDecode(
'Address',
new Address('5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'),
'5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'
);
testDecode(
'AccountId',
new AccountId('5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'),
'5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'
);
testDecode(
'AccountIndex',
new Address('PrF3'),
// NOTE Expected adress here is encoded with prefix 42, input above with 68
'F7ih'
);
testDecode(
'AccountIndex',
new AccountIndex('0x0001'),
'25GUyk'
);
testDecode(
'Array',
[
255,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8
],
'5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'
);
testDecode(
'Uint8Array (with prefix 255)',
Uint8Array.from([
255,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8
]),
'5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'
);
testDecode(
'Uint8Array (with prefix 1 byte)',
Uint8Array.from([1]),
'F7L6'
);
testDecode(
'Uint8Array (with prefix 2 bytes)',
Uint8Array.from([0xfc, 0, 1]),
'25GUyk'
);
testDecode(
'Uint8Array (with prefix 4 bytes)',
Uint8Array.from([0xfd, 17, 18, 19, 20]),
'Mwz15xN8'
);
testDecode(
'Uint8Array (with prefix 8 bytes)',
Uint8Array.from([0xfe, 17, 18, 19, 20, 21, 22, 23, 24]),
'3N5RJXxM5fLd4h'
);
});
describe('encoding', () => {
const testEncode = (to: 'toHex' | 'toString' | 'toU8a', expected: string | Uint8Array) =>
it(`can encode ${to}`, () => {
const a = new Address('5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s');
expect(a[to]()).toEqual(expected);
});
testEncode(
'toHex',
'0xff0102030405060708010203040506070801020304050607080102030405060708'
);
testEncode(
'toString',
'5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCt72s'
);
testEncode(
'toU8a',
Uint8Array.from([
255,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8
])
);
});
});
+2 -2
View File
@@ -17,13 +17,13 @@ const ACCOUNT_ID_PREFIX = new Uint8Array([0xff]);
// is encoded as
// [ <prefix-byte>, ...publicKey/...bytes ]
export default class Address extends Base<AccountId | AccountIndex> {
constructor (value: Address | AccountId | AccountIndex | Uint8Array | string = new Uint8Array()) {
constructor (value: Address | AccountId | AccountIndex | Array<number> | Uint8Array | string = new Uint8Array()) {
super(
Address.decodeAddress(value)
);
}
static decodeAddress (value: Address | AccountId | AccountIndex | Uint8Array | string): AccountId | AccountIndex {
static decodeAddress (value: Address | AccountId | AccountIndex | Array<number> | Uint8Array | string): AccountId | AccountIndex {
if (value instanceof Address) {
return value.raw;
} else if (value instanceof AccountId || value instanceof AccountIndex) {