ss58 Checksum updates (#387)

This commit is contained in:
Jaco Greeff
2019-03-29 14:16:59 +01:00
committed by GitHub
parent 78e303a95f
commit 8861269963
12 changed files with 67 additions and 50 deletions
+13 -9
View File
@@ -6,12 +6,16 @@
import testingPairs from '../testingPairs';
import decode from './decode';
const keyring = testingPairs({ type: 'ed25519' }, false);
describe('decode', () => {
let keyring: any;
beforeAll(async () => {
keyring = testingPairs({ type: 'sr25519' });
});
it('decodes an address', () => {
expect(
decode('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaDtZ')
decode('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY')
).toEqual(
keyring.alice.publicKey()
);
@@ -35,31 +39,31 @@ describe('decode', () => {
it('decodes a short address', () => {
expect(
decode('F7L6')
decode('F7NZ')
).toEqual(new Uint8Array([1]));
});
it('decodes a 1-byte accountId (with prefix)', () => {
expect(
decode('Pqt7', 68)
decode('PqtB', false, 68)
).toEqual(new Uint8Array([1]));
});
it('decodes a 2-byte accountId', () => {
expect(
decode('2jpAJz', 68)
decode('2jpAFn', false, 68)
).toEqual(new Uint8Array([0, 1]));
});
it('encodes a 4-byte address', () => {
expect(
decode('as7QnGQ7', 68)
decode('as7QnGMf', false, 68)
).toEqual(new Uint8Array([1, 2, 3, 4]));
});
it('decodes a 8-byte accountId', () => {
it('decodes a 8-byte address', () => {
expect(
decode('4q7qY5RBG7Z4xX', 68)
decode('4q7qY5RBG7Z4wv', false, 68)
).toEqual(new Uint8Array([42, 44, 10, 0, 0, 0, 0, 0]));
});
+4 -4
View File
@@ -8,11 +8,11 @@ import { Prefix } from './types';
import bs58 from 'bs58';
import { assert, bufferToU8a, isHex, isU8a, u8aToU8a } from '@polkadot/util';
import { blake2AsU8a } from '@polkadot/util-crypto';
import defaults from './defaults';
import sshash from './sshash';
export default function decode (encoded: string | Uint8Array, prefix: Prefix = defaults.prefix): Uint8Array {
export default function decode (encoded: string | Uint8Array, ignoreChecksum?: boolean, prefix: Prefix = defaults.prefix): Uint8Array {
if (isU8a(encoded) || isHex(encoded)) {
return u8aToU8a(encoded);
}
@@ -35,12 +35,12 @@ export default function decode (encoded: string | Uint8Array, prefix: Prefix = d
const endPos = decoded.length - (isPublicKey ? 2 : 1);
// calculate the hash and do the checksum byte checks
const hash = blake2AsU8a(decoded.subarray(0, endPos), 512);
const hash = sshash(decoded.subarray(0, endPos));
const checks = isPublicKey
? decoded[decoded.length - 2] === hash[0] && decoded[decoded.length - 1] === hash[1]
: decoded[decoded.length - 1] === hash[0];
assert(checks, error('Invalid decoded address checksum'));
assert(ignoreChecksum || checks, error('Invalid decoded address checksum'));
return decoded.slice(1, endPos);
}
+6 -6
View File
@@ -12,7 +12,7 @@ describe('encode', () => {
it('encodes an address to a valid value', () => {
expect(
keyring.alice.address()
).toEqual('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaDtZ');
).toEqual('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQua');
});
it('fails when non-valid publicKey provided', () => {
@@ -28,7 +28,7 @@ describe('encode', () => {
encode(
new Uint8Array([1])
)
).toEqual('F7L6');
).toEqual('F7NZ');
});
it('encodes a 1-byte address (with prefix)', () => {
@@ -36,7 +36,7 @@ describe('encode', () => {
encode(
new Uint8Array([1]), 68
)
).toEqual('Pqt7');
).toEqual('PqtB');
});
it('encodes a 2-byte address', () => {
@@ -44,7 +44,7 @@ describe('encode', () => {
encode(
new Uint8Array([0, 1]), 68
)
).toEqual('2jpAJz');
).toEqual('2jpAFn');
});
it('encodes a 4-byte address', () => {
@@ -52,7 +52,7 @@ describe('encode', () => {
encode(
new Uint8Array([1, 2, 3, 4]), 68
)
).toEqual('as7QnGQ7');
).toEqual('as7QnGMf');
});
it('enodes a 8-byte address', () => {
@@ -60,6 +60,6 @@ describe('encode', () => {
encode(
new Uint8Array([42, 44, 10, 0, 0, 0, 0, 0]), 68
)
).toEqual('4q7qY5RBG7Z4xX');
).toEqual('4q7qY5RBG7Z4wv');
});
});
+2 -3
View File
@@ -8,9 +8,9 @@ import { Prefix } from './types';
import bs58 from 'bs58';
import { assert, u8aConcat, u8aToBuffer, u8aToU8a } from '@polkadot/util';
import { blake2AsU8a } from '@polkadot/util-crypto';
import defaults from './defaults';
import sshash from './sshash';
export default function encode (_key: Uint8Array | string, prefix: Prefix = defaults.prefix): string {
const key = u8aToU8a(_key);
@@ -19,9 +19,8 @@ export default function encode (_key: Uint8Array | string, prefix: Prefix = defa
const isPublicKey = key.length === 32;
// generate an input with the prefix and calculate hash
const input = u8aConcat(new Uint8Array([prefix]), key);
const hash = blake2AsU8a(input, 512);
const hash = sshash(input);
return bs58.encode(
u8aToBuffer(
@@ -15,6 +15,6 @@ describe('setPrefix', () => {
encode(
new Uint8Array([1])
)
).toEqual('Pqt7');
).toEqual('PqtB');
});
});
+12
View File
@@ -0,0 +1,12 @@
// Copyright 2017-2019 @polkadot/keyring authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { stringToU8a, u8aConcat } from '@polkadot/util';
import { blake2AsU8a } from '@polkadot/util-crypto';
const SS58_PREFIX = stringToU8a('SS58PRE');
export default function hash (key: Uint8Array): Uint8Array {
return blake2AsU8a(u8aConcat(SS58_PREFIX, key), 512);
}
+7 -7
View File
@@ -32,12 +32,12 @@ describe('keypair', () => {
).toEqual(publicKeyTwo);
});
it('creates a ed25519 pair via mneminicToSeed', () => {
it('creates a ed25519 pair via mnemonicToSeed', () => {
expect(
keypair.addFromUri(
'seed sock milk update focus rotate barely fade car face mechanic mercy'
).address()
).toEqual('5DkQP32jP4DVJLWWBRBoZF2tpWjqFrcrTBo6H5NcSk7MxFyP');
).toEqual('5DkQP32jP4DVJLWWBRBoZF2tpWjqFrcrTBo6H5NcSk7MxKCC');
});
it('adds from a mnemonic', () => {
@@ -45,7 +45,7 @@ describe('keypair', () => {
expect(
keypair.addFromMnemonic('moral movie very draw assault whisper awful rebuild speed purity repeat card', {}).address()
).toEqual('7pDZKLEixRnF6Q5jzr7DsCEiNPt3d6Rknc14SyUcnRwTQK14');
).toEqual('7pDZKLEixRnF6Q5jzr7DsCEiNPt3d6Rknc14SyUcnRwTQ8Jw');
});
it('allows publicKeys retrieval', () => {
@@ -65,7 +65,7 @@ describe('keypair', () => {
it('allows adding from JSON', () => {
expect(
keypair.addFromJson(
JSON.parse('{"address":"5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaDtZ","encoded":"0xb4a14995d25ab609f3686e9fa45f1fb237cd833f33f00d4b12c51858ca070d96972e47d73aae5eeb0fc06f923826cf0943fdb02c2c2ee30ef52a7912663053940d1da4da66b3a3f520ae07422c1c94b2d95690fca9d1f4a997623bb2923a8833280e19e7f72c3c5cfa343974e60e2b3dc53b404fdaf330756daad5e4e3","encoding":{"content":"pkcs8","type":"xsalsa20-poly1305","version":"0"},"meta":{"isTesting":true,"name":"alice"}}')
JSON.parse('{"address":"5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQua","encoded":"0xb4a14995d25ab609f3686e9fa45f1fb237cd833f33f00d4b12c51858ca070d96972e47d73aae5eeb0fc06f923826cf0943fdb02c2c2ee30ef52a7912663053940d1da4da66b3a3f520ae07422c1c94b2d95690fca9d1f4a997623bb2923a8833280e19e7f72c3c5cfa343974e60e2b3dc53b404fdaf330756daad5e4e3","encoding":{"content":"pkcs8","type":"xsalsa20-poly1305","version":"0"},"meta":{"isTesting":true,"name":"alice"}}')
).publicKey()
).toEqual(
new Uint8Array([209, 114, 167, 76, 218, 76, 134, 89, 18, 195, 43, 160, 168, 10, 87, 174, 105, 171, 174, 65, 14, 92, 203, 89, 222, 232, 78, 47, 68, 50, 219, 79])
@@ -97,7 +97,7 @@ describe('keypair', () => {
it('creates with dev phrase when only path specified', () => {
expect(
keypair.createFromUri('//Alice').address()
).toEqual('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKv3gB');
).toEqual('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY');
});
it('adds the pair', () => {
@@ -111,7 +111,7 @@ describe('keypair', () => {
expect(
keypair.addFromMnemonic('moral movie very draw assault whisper awful rebuild speed purity repeat card', {}).address()
).toEqual('7nmZ1fkYHpnEz6tqR9cLkmwYRbqda9wPsiDq56JhgmNXqGjt');
).toEqual('7nmZ1fkYHpnEz6tqR9cLkmwYRbqda9wPsiDq56JhgmNXq6uW');
});
it('allows publicKeys retrieval', () => {
@@ -131,7 +131,7 @@ describe('keypair', () => {
it('allows adding from JSON', () => {
expect(
keypair.addFromJson(
JSON.parse('{"address":"5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaDtZ","encoded":"0xb4a14995d25ab609f3686e9fa45f1fb237cd833f33f00d4b12c51858ca070d96972e47d73aae5eeb0fc06f923826cf0943fdb02c2c2ee30ef52a7912663053940d1da4da66b3a3f520ae07422c1c94b2d95690fca9d1f4a997623bb2923a8833280e19e7f72c3c5cfa343974e60e2b3dc53b404fdaf330756daad5e4e3","encoding":{"content":"pkcs8","type":"xsalsa20-poly1305","version":"0"},"meta":{"isTesting":true,"name":"alice"}}')
JSON.parse('{"address":"5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQua","encoded":"0xb4a14995d25ab609f3686e9fa45f1fb237cd833f33f00d4b12c51858ca070d96972e47d73aae5eeb0fc06f923826cf0943fdb02c2c2ee30ef52a7912663053940d1da4da66b3a3f520ae07422c1c94b2d95690fca9d1f4a997623bb2923a8833280e19e7f72c3c5cfa343974e60e2b3dc53b404fdaf330756daad5e4e3","encoding":{"content":"pkcs8","type":"xsalsa20-poly1305","version":"0"},"meta":{"isTesting":true,"name":"alice"}}')
).publicKey()
).toEqual(
new Uint8Array([209, 114, 167, 76, 218, 76, 134, 89, 18, 195, 43, 160, 168, 10, 87, 174, 105, 171, 174, 65, 14, 92, 203, 89, 222, 232, 78, 47, 68, 50, 219, 79])
+6 -4
View File
@@ -87,8 +87,10 @@ export default class Keyring implements KeyringInstance {
* of an account backup), and then generates a keyring pair from them that it passes to
* `addPair` to stores in a keyring pair dictionary the public key of the generated pair as a key and the pair as the associated value.
*/
addFromAddress (address: string | Uint8Array, meta: KeyringPair$Meta = {}, encoded: Uint8Array | null = null, type: KeypairType = this.type): KeyringPair {
return this.addPair(createPair(type, { publicKey: this.decodeAddress(address) }, meta, encoded));
addFromAddress (address: string | Uint8Array, meta: KeyringPair$Meta = {}, encoded: Uint8Array | null = null, type: KeypairType = this.type, ignoreChecksum?: boolean): KeyringPair {
const publicKey = this.decodeAddress(address, ignoreChecksum);
return this.addPair(createPair(type, { publicKey }, meta, encoded));
}
/**
@@ -98,11 +100,11 @@ export default class Keyring implements KeyringInstance {
* of an account backup), and then generates a keyring pair from it that it passes to
* `addPair` to stores in a keyring pair dictionary the public key of the generated pair as a key and the pair as the associated value.
*/
addFromJson ({ address, encoded, encoding: { content, version }, meta }: KeyringPair$Json): KeyringPair {
addFromJson ({ address, encoded, encoding: { content, version }, meta }: KeyringPair$Json, ignoreChecksum?: boolean): KeyringPair {
const type = version === '0' || !Array.isArray(content)
? this.type
: content[1];
return this.addFromAddress(address, meta, hexToU8a(encoded), type);
return this.addFromAddress(address, meta, hexToU8a(encoded), type, ignoreChecksum);
}
/**
+2 -2
View File
@@ -11,7 +11,7 @@ describe('toJson', () => {
expect(
keyring.alice.toJson()
).toMatchObject({
address: '5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaDtZ',
address: '5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQua',
encoded: '0x3053020101300506032b657004220420416c696365202020202020202020202020202020202020202020202020202020d172a74cda4c865912c32ba0a80a57ae69abae410e5ccb59dee84e2f4432db4fa123032100d172a74cda4c865912c32ba0a80a57ae69abae410e5ccb59dee84e2f4432db4f',
encoding: {
content: ['pkcs8', 'ed25519'],
@@ -30,7 +30,7 @@ describe('toJson', () => {
expect(json.encoded).toHaveLength(316);
expect(json).toMatchObject({
address: '5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaDtZ',
address: '5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQua',
encoding: {
content: ['pkcs8', 'ed25519'],
type: 'xsalsa20-poly1305',
+9 -9
View File
@@ -14,47 +14,47 @@ const PHRASE = 'bottom drive obey lake curtain smoke basket hold race lonely fit
const TESTS = [
{
pk: '0x46ebddef8cd9bb167dc30878d7113b7e168e6f0646beffd77d69d39bad76b47a',
ss: '5DfhGyQdFobKM8NsWvEeAKk5EQQgYe9AydgJ7rMB6E1EqAS7',
ss: '5DfhGyQdFobKM8NsWvEeAKk5EQQgYe9AydgJ7rMB6E1EqRzV',
uri: PHRASE
},
{
pk: '0xb69355deefa7a8f33e9297f5af22e680f03597a99d4f4b1c44be47e7a2275802',
ss: '5GC6LfpV352HtJPySfAecb5JdePtf4R9Vq49NUU8RhzgBq1z',
ss: '5GC6LfpV352HtJPySfAecb5JdePtf4R9Vq49NUU8RhzgBqgq',
uri: `${PHRASE}///password`
},
{
pk: '0x40b9675df90efa6069ff623b0fdfcf706cd47ca7452a5056c7ad58194d23440a',
ss: '5DXZzrDxHbkQov4QBAY4TjpwnHCMrKXkomTnKSw8UArBESDT',
ss: '5DXZzrDxHbkQov4QBAY4TjpwnHCMrKXkomTnKSw8UArBEY5v',
uri: `${PHRASE}/foo`
},
{
pk: '0x547d4a55642ec7ebadc0bd29b6e570b8c926059b3c0655d4948075e9a7e6f31e',
ss: '5DyV6fZuvPemWrUqBgWwTSgoV86w6xms3KhkFU6cQcWxTwuv',
ss: '5DyV6fZuvPemWrUqBgWwTSgoV86w6xms3KhkFU6cQcWxU8eP',
uri: `${PHRASE}//foo`
},
{
pk: '0x3841947ffcde6f5fef26fb68b59bb8665637e30e32ec2051f99cf6b9c674fe09',
ss: '5DLU27is5iViNopQb2KxsTyPx6j4vCu8X3sk3j3NNLkPCsLj',
ss: '5DLU27is5iViNopQb2KxsTyPx6j4vCu8X3sk3j3NNLkPCqKM',
uri: `${PHRASE}//foo/bar`
},
{
pk: '0xdc142f7476a7b0aa262aeccf207f1d18daa90762db393006741e8a31f39dbc53',
ss: '5H3GPTqDSpjkfDwbHy12PD6BWm8jvGSX4xYC8UMprHpTPhQg',
ss: '5H3GPTqDSpjkfDwbHy12PD6BWm8jvGSX4xYC8UMprHpTPcRg',
uri: `${PHRASE}/foo//bar`
},
{
pk: '0xa2e56b06407a6d1e819d2fc33fa0ec604b29c2e868b70b3696bb049b8725934b',
ss: '5FkHmNgbg64MwStgCyDi2Uw3ufFu11mqQgmWT9uwK4Lgi3SE',
ss: '5FkHmNgbg64MwStgCyDi2Uw3ufFu11mqQgmWT9uwK4Lghvpv',
uri: `${PHRASE}//foo/bar//42/69`
},
{
pk: '0x0e0d24e3e1ff2c07f269c99e2e0df8681fda1851ac42fc846ca2daaa90cd8f14',
ss: '5CP8S23JBNXYNpJsL7ESPJBNnUZE6itcfM4EnDxEhaVEUAWL',
ss: '5CP8S23JBNXYNpJsL7ESPJBNnUZE6itcfM4EnDxEhaVEU6dT',
uri: `${PHRASE}//foo/bar//42/69///password`
},
{
pk: '0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d',
ss: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKv3gB',
ss: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
uri: `${PHRASE}//Alice`
}
];
+2 -2
View File
@@ -20,12 +20,12 @@ describe('testingPairs', () => {
it('has the correct address for Alice (non-HDKD)', () => {
expect(
testingPairs({ type: 'ed25519' }, false).alice.address()
).toEqual('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaDtZ');
).toEqual('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQua');
});
it('has the correct address for Alice (HDKD)', () => {
expect(
testingPairs({ type: 'ed25519' }, true).alice.address()
).toEqual('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKv3gB');
).toEqual('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY');
});
});
+3 -3
View File
@@ -57,13 +57,13 @@ export interface KeyringInstance {
readonly publicKeys: Array<Uint8Array>;
readonly type: KeypairType;
decodeAddress (encoded: string | Uint8Array): Uint8Array;
decodeAddress (encoded: string | Uint8Array, ignoreChecksum?: boolean): Uint8Array;
encodeAddress (key: Uint8Array | string): string;
setAddressPrefix (prefix: Prefix): void;
addPair (pair: KeyringPair): KeyringPair;
addFromAddress (address: string | Uint8Array, meta?: KeyringPair$Meta, encoded?: Uint8Array | null, type?: KeypairType): KeyringPair;
addFromJson (pair: KeyringPair$Json): KeyringPair;
addFromAddress (address: string | Uint8Array, meta?: KeyringPair$Meta, encoded?: Uint8Array | null, type?: KeypairType, ignoreChecksum?: boolean): KeyringPair;
addFromJson (pair: KeyringPair$Json, ignoreChecksum?: boolean): KeyringPair;
addFromMnemonic (mnemonic: string, meta?: KeyringPair$Meta, type?: KeypairType): KeyringPair;
addFromSeed (seed: Uint8Array, meta?: KeyringPair$Meta, type?: KeypairType): KeyringPair;
addFromUri (suri: string, meta?: KeyringPair$Meta, type?: KeypairType): KeyringPair;