Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2fa0ad11f | ||
|
|
654e44aa99 | ||
|
|
476689f615 | ||
|
|
fc1582aa70 | ||
|
|
a616b84045 | ||
|
|
73708b72c7 | ||
|
|
8f0807ddbe | ||
|
|
5547f885be | ||
|
|
5f8d521c6a | ||
|
|
51a169a88a | ||
|
|
ad89ed5f3d | ||
|
|
ca1362c595 |
@@ -1,5 +1,20 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 5.6.1 Feb 7, 2021
|
||||
|
||||
Contributed:
|
||||
|
||||
- Fix installation instructions in `@polkadot/x-global` (Thanks to https://github.com/raoulmillais)
|
||||
|
||||
Changes:
|
||||
|
||||
- Conform to new ss58 encoding with ids up to 16,383 (>= 64 as 2 bytes)
|
||||
- Use JS sqrt inside `bnSqrt` when value <= MAX_SAFE_INTEGER
|
||||
- Adjust all `Buffer.from` usages, prefer `Uint8Array` where possible
|
||||
- Expand tests for keyring with `vrf{Sign, Verify}`
|
||||
- Align `@polkadot/networks` with latest ss58 registry
|
||||
|
||||
|
||||
## 5.5.2 Feb 2, 2021
|
||||
|
||||
Changes:
|
||||
|
||||
+3
-3
@@ -20,10 +20,10 @@
|
||||
"test:one": "polkadot-dev-run-test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.10",
|
||||
"@polkadot/dev": "^0.61.24",
|
||||
"@babel/core": "^7.12.13",
|
||||
"@polkadot/dev": "^0.61.25",
|
||||
"@polkadot/ts": "^0.3.59",
|
||||
"@types/jest": "^26.0.20"
|
||||
},
|
||||
"version": "5.5.2"
|
||||
"version": "5.6.1"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@polkadot/hw-ledger-transports",
|
||||
"version": "5.5.2",
|
||||
"version": "5.6.1",
|
||||
"repository": "github:polkadot-js/common",
|
||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||
"maintainers": [],
|
||||
@@ -11,7 +11,7 @@
|
||||
"react-native": "react-native.js",
|
||||
"sideEffects": false,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@babel/runtime": "^7.12.13",
|
||||
"@ledgerhq/hw-transport-webusb": "^5.41.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@polkadot/hw-ledger",
|
||||
"version": "5.5.2",
|
||||
"version": "5.6.1",
|
||||
"repository": "github:polkadot-js/common",
|
||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||
"maintainers": [],
|
||||
@@ -9,9 +9,9 @@
|
||||
"main": "index.js",
|
||||
"sideEffects": false,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@polkadot/hw-ledger-transports": "5.5.2",
|
||||
"@polkadot/util": "^5.5.2",
|
||||
"@babel/runtime": "^7.12.13",
|
||||
"@polkadot/hw-ledger-transports": "5.6.1",
|
||||
"@polkadot/util": "^5.6.1",
|
||||
"@zondax/ledger-polkadot": "^0.13.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@polkadot/keyring",
|
||||
"version": "5.5.2",
|
||||
"version": "5.6.1",
|
||||
"description": "Keyring management",
|
||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||
"maintainers": [],
|
||||
@@ -15,12 +15,12 @@
|
||||
"bugs": "https://github.com/polkadot-js/common/issues",
|
||||
"homepage": "https://github.com/polkadot-js/common/tree/master/packages/keyring#readme",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@polkadot/util": "5.5.2",
|
||||
"@polkadot/util-crypto": "5.5.2"
|
||||
"@babel/runtime": "^7.12.13",
|
||||
"@polkadot/util": "5.6.1",
|
||||
"@polkadot/util-crypto": "5.6.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@polkadot/util": "5.5.2",
|
||||
"@polkadot/util-crypto": "5.5.2"
|
||||
"@polkadot/util": "5.6.1",
|
||||
"@polkadot/util-crypto": "5.6.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,11 +46,11 @@ describe('keypair', (): void => {
|
||||
|
||||
it('adds from a mnemonic, with correct ss58', (): void => {
|
||||
setSS58Format(20); // this would not be used
|
||||
keyring.setSS58Format(68); // this would be used
|
||||
keyring.setSS58Format(2); // this would be used
|
||||
|
||||
const pair = keyring.addFromMnemonic('moral movie very draw assault whisper awful rebuild speed purity repeat card', {});
|
||||
|
||||
expect(pair.address).toEqual('7sPsxWPE5DzAyPT3VuoJYw5NTGscx9QYN9oddQx4kALKC3hH');
|
||||
expect(pair.address).toEqual('HSLu2eci2GCfWkRimjjdTXKoFSDL3rBv5Ey2JWCBj68cVZj');
|
||||
expect(encodeAddress(pair.publicKey)).toEqual('35cDYtPsdG1HUa2n2MaARgJyRz1WKMBZK1DL6c5cX7nugQh1');
|
||||
});
|
||||
|
||||
@@ -123,11 +123,11 @@ describe('keypair', (): void => {
|
||||
});
|
||||
|
||||
it('adds from a mnemonic', (): void => {
|
||||
keyring.setSS58Format(68);
|
||||
keyring.setSS58Format(2);
|
||||
|
||||
expect(
|
||||
keyring.addFromMnemonic('moral movie very draw assault whisper awful rebuild speed purity repeat card', {}).address
|
||||
).toEqual('7qQGarA4PWjPPVHG4USn1yuuVZvEHN7XZz8o7EbAp48jayZQ');
|
||||
).toEqual('FSjXNRT2K1R5caeHLPD6WMrqYUpfGZB7ua8W89JFctZ1YqV');
|
||||
});
|
||||
|
||||
it('allows publicKeys retrieval', (): void => {
|
||||
@@ -200,11 +200,11 @@ describe('keypair', (): void => {
|
||||
});
|
||||
|
||||
it('adds from a mnemonic', (): void => {
|
||||
keyring.setSS58Format(68);
|
||||
keyring.setSS58Format(2);
|
||||
|
||||
expect(
|
||||
keyring.addFromMnemonic('moral movie very draw assault whisper awful rebuild speed purity repeat card').address
|
||||
).toEqual('7ooxHV3mz4nnWbK8v7Mxcb71QMpof268eL1A2VrYWUNWJk8P');
|
||||
).toEqual('DrRE1KAcs4pCicX8yJPh7YxkLPQ2vXnCFSVRPQfx38KjEFe');
|
||||
});
|
||||
|
||||
it('allows publicKeys retrieval', (): void => {
|
||||
|
||||
@@ -78,15 +78,11 @@ describe('pair', (): void => {
|
||||
});
|
||||
|
||||
it('allows encoding of address with different prefixes', (): void => {
|
||||
expect(keyring.alice.address).toEqual(
|
||||
'5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQua'
|
||||
);
|
||||
expect(keyring.alice.address).toEqual('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQua');
|
||||
|
||||
setSS58Format(68);
|
||||
setSS58Format(255);
|
||||
|
||||
expect(keyring.alice.address).toEqual(
|
||||
'7sGUeMak588SPY2YMmmuKUuLz7u2WQpf74F9dCFtSLB2td9d'
|
||||
);
|
||||
expect(keyring.alice.address).toEqual('yGHU8YKprxHbHdEv7oUK4rzMZXtsdhcXVG2CAMyC9WhzhjH2k');
|
||||
|
||||
setSS58Format(42);
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { Keypair, KeypairType } from '@polkadot/util-crypto/types';
|
||||
import type { KeyringPair, KeyringPair$Json, KeyringPair$JsonEncodingTypes, KeyringPair$Meta, SignOptions } from '../types';
|
||||
import type { PairInfo } from './types';
|
||||
|
||||
import { assert, u8aConcat, u8aEq, u8aToHex } from '@polkadot/util';
|
||||
import { assert, u8aConcat, u8aEq, u8aToHex, u8aToU8a } from '@polkadot/util';
|
||||
import { blake2AsU8a, ethereumEncode, keccakAsU8a, keyExtractPath, keyFromPath, naclKeypairFromSeed as naclFromSeed, naclSign, schnorrkelKeypairFromSeed as schnorrkelFromSeed, schnorrkelSign, schnorrkelVrfSign, schnorrkelVrfVerify, secp256k1Compress, secp256k1Expand, secp256k1KeypairFromSeed as secp256k1FromSeed, secp256k1Sign, signatureVerify } from '@polkadot/util-crypto';
|
||||
|
||||
import { decodePair } from './decode';
|
||||
@@ -47,13 +47,13 @@ const TYPE_ADDRESS = {
|
||||
sr25519: (p: Uint8Array) => p
|
||||
};
|
||||
|
||||
function isEmpty (u8a: Uint8Array): boolean {
|
||||
return u8a.reduce((count, u8) => count + u8, 0) === 0;
|
||||
}
|
||||
|
||||
// Not 100% correct, since it can be a Uint8Array, but an invalid one - just say "undefined" is anything non-valid
|
||||
function isLocked (secretKey?: Uint8Array): secretKey is undefined {
|
||||
return !secretKey || secretKey.length === 0 || isEmpty(secretKey);
|
||||
return !secretKey || secretKey.length === 0 || secretKey.every((b) => b === 0);
|
||||
}
|
||||
|
||||
function vrfHash (proof: Uint8Array, context?: string | Uint8Array, extra?: string | Uint8Array): Uint8Array {
|
||||
return blake2AsU8a(u8aConcat(context || '', extra || '', proof));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,22 +152,23 @@ export function createPair ({ toSS58, type }: Setup, { publicKey, secretKey }: P
|
||||
|
||||
return createPair({ toSS58, type }, derived, meta, null);
|
||||
},
|
||||
encodePkcs8: (passphrase?: string): Uint8Array =>
|
||||
recode(passphrase),
|
||||
encodePkcs8: (passphrase?: string): Uint8Array => {
|
||||
return recode(passphrase);
|
||||
},
|
||||
lock: (): void => {
|
||||
secretKey = new Uint8Array();
|
||||
},
|
||||
setMeta: (additional: KeyringPair$Meta): void => {
|
||||
meta = { ...meta, ...additional };
|
||||
},
|
||||
sign: (message: Uint8Array, options: SignOptions = {}): Uint8Array => {
|
||||
sign: (message: string | Uint8Array, options: SignOptions = {}): Uint8Array => {
|
||||
assert(!isLocked(secretKey), 'Cannot sign with a locked key pair');
|
||||
|
||||
return u8aConcat(
|
||||
options.withType
|
||||
? TYPE_PREFIX[type]
|
||||
: SIG_TYPE_NONE,
|
||||
TYPE_SIGNATURE[type](message, { publicKey, secretKey })
|
||||
TYPE_SIGNATURE[type](u8aToU8a(message), { publicKey, secretKey })
|
||||
);
|
||||
},
|
||||
toJson: (passphrase?: string): KeyringPair$Json => {
|
||||
@@ -177,28 +178,27 @@ export function createPair ({ toSS58, type }: Setup, { publicKey, secretKey }: P
|
||||
|
||||
return pairToJson(type, { address, meta }, recode(passphrase), !!passphrase);
|
||||
},
|
||||
verify: (message: Uint8Array, signature: Uint8Array): boolean =>
|
||||
signatureVerify(message, signature, TYPE_ADDRESS[type](publicKey)).isValid,
|
||||
vrfSign: (message: Uint8Array, context?: string | Uint8Array, extra?: string | Uint8Array): Uint8Array => {
|
||||
verify: (message: string | Uint8Array, signature: Uint8Array): boolean => {
|
||||
return signatureVerify(message, signature, TYPE_ADDRESS[type](publicKey)).isValid;
|
||||
},
|
||||
vrfSign: (message: string | Uint8Array, context?: string | Uint8Array, extra?: string | Uint8Array): Uint8Array => {
|
||||
assert(!isLocked(secretKey), 'Cannot sign with a locked key pair');
|
||||
|
||||
if (type === 'sr25519') {
|
||||
return schnorrkelVrfSign(message, { secretKey }, context, extra);
|
||||
}
|
||||
|
||||
const proof = TYPE_SIGNATURE[type](message, { publicKey, secretKey });
|
||||
const proof = TYPE_SIGNATURE[type](u8aToU8a(message), { publicKey, secretKey });
|
||||
|
||||
return u8aConcat(
|
||||
blake2AsU8a(u8aConcat(context || '', extra || '', proof)),
|
||||
proof
|
||||
);
|
||||
return u8aConcat(vrfHash(proof, context, extra), proof);
|
||||
},
|
||||
vrfVerify: (message: Uint8Array, vrfResult: Uint8Array, context?: string | Uint8Array, extra?: string | Uint8Array): boolean =>
|
||||
type === 'sr25519'
|
||||
vrfVerify: (message: string | Uint8Array, vrfResult: Uint8Array, context?: string | Uint8Array, extra?: string | Uint8Array): boolean => {
|
||||
return type === 'sr25519'
|
||||
? schnorrkelVrfVerify(message, vrfResult, publicKey, context, extra)
|
||||
: (
|
||||
signatureVerify(message, vrfResult.subarray(32), TYPE_ADDRESS[type](publicKey)).isValid &&
|
||||
u8aEq(vrfResult.subarray(0, 32), blake2AsU8a(u8aConcat(context || '', extra || '', vrfResult.subarray(32))))
|
||||
)
|
||||
signatureVerify(message, u8aConcat(TYPE_PREFIX[type], vrfResult.subarray(32)), TYPE_ADDRESS[type](publicKey)).isValid &&
|
||||
u8aEq(vrfResult.subarray(0, 32), vrfHash(vrfResult.subarray(32), context, extra))
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright 2017-2021 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { KeyringPair } from '../types';
|
||||
|
||||
import { u8aToHex } from '@polkadot/util';
|
||||
import { cryptoWaitReady, encodeAddress as toSS58, naclKeypairFromSeed, randomAsU8a, schnorrkelKeypairFromSeed, secp256k1KeypairFromSeed } from '@polkadot/util-crypto';
|
||||
|
||||
import { createPair } from '.';
|
||||
|
||||
const MESSAGE = 'this is a test message';
|
||||
const CONTEXT = 'some context';
|
||||
|
||||
describe('vrf', (): void => {
|
||||
let ecdsa: KeyringPair;
|
||||
let ed25519: KeyringPair;
|
||||
let sr25519: KeyringPair;
|
||||
|
||||
beforeAll(async (): Promise<void> => {
|
||||
await cryptoWaitReady();
|
||||
|
||||
ecdsa = createPair({ toSS58, type: 'ecdsa' }, secp256k1KeypairFromSeed(randomAsU8a()));
|
||||
ed25519 = createPair({ toSS58, type: 'ed25519' }, naclKeypairFromSeed(randomAsU8a()));
|
||||
sr25519 = createPair({ toSS58, type: 'sr25519' }, schnorrkelKeypairFromSeed(randomAsU8a()));
|
||||
});
|
||||
|
||||
it('has deterministic signature values for ecdsa', (): void => {
|
||||
const sig1 = ecdsa.vrfSign(MESSAGE, CONTEXT);
|
||||
const sig2 = ecdsa.vrfSign(MESSAGE, CONTEXT);
|
||||
|
||||
expect(u8aToHex(sig1)).toEqual(u8aToHex(sig2));
|
||||
expect(ecdsa.vrfVerify(MESSAGE, sig1, CONTEXT)).toEqual(true);
|
||||
expect(ecdsa.vrfVerify(MESSAGE, sig2, CONTEXT)).toEqual(true);
|
||||
});
|
||||
|
||||
it('has deterministic signature values for ed25519', (): void => {
|
||||
const sig1 = ed25519.vrfSign(MESSAGE, CONTEXT);
|
||||
const sig2 = ed25519.vrfSign(MESSAGE, CONTEXT);
|
||||
|
||||
expect(u8aToHex(sig1)).toEqual(u8aToHex(sig2));
|
||||
expect(ed25519.vrfVerify(MESSAGE, sig1, CONTEXT)).toEqual(true);
|
||||
expect(ed25519.vrfVerify(MESSAGE, sig2, CONTEXT)).toEqual(true);
|
||||
});
|
||||
|
||||
it('has deterministic signature values for sr25519', (): void => {
|
||||
const sig1 = sr25519.vrfSign(MESSAGE, CONTEXT);
|
||||
const sig2 = sr25519.vrfSign(MESSAGE, CONTEXT);
|
||||
|
||||
expect(u8aToHex(sig1.slice(0, 32))).toEqual(u8aToHex(sig2.slice(0, 32)));
|
||||
expect(sr25519.vrfVerify(MESSAGE, sig1, CONTEXT)).toEqual(true);
|
||||
expect(sr25519.vrfVerify(MESSAGE, sig2, CONTEXT)).toEqual(true);
|
||||
});
|
||||
});
|
||||
@@ -45,11 +45,11 @@ export interface KeyringPair {
|
||||
encodePkcs8: (passphrase?: string) => Uint8Array;
|
||||
lock: () => void;
|
||||
setMeta: (meta: KeyringPair$Meta) => void;
|
||||
sign (message: Uint8Array, options?: SignOptions): Uint8Array;
|
||||
sign (message: string | Uint8Array, options?: SignOptions): Uint8Array;
|
||||
toJson (passphrase?: string): KeyringPair$Json;
|
||||
verify (message: Uint8Array, signature: Uint8Array): boolean;
|
||||
vrfSign (message: Uint8Array, context?: string | Uint8Array, extra?: string | Uint8Array): Uint8Array;
|
||||
vrfVerify (message: Uint8Array, vrfResult: Uint8Array, context?: string | Uint8Array, extra?: string | Uint8Array): boolean;
|
||||
verify (message: string | Uint8Array, signature: Uint8Array): boolean;
|
||||
vrfSign (message: string | Uint8Array, context?: string | Uint8Array, extra?: string | Uint8Array): Uint8Array;
|
||||
vrfVerify (message: string | Uint8Array, vrfResult: Uint8Array, context?: string | Uint8Array, extra?: string | Uint8Array): boolean;
|
||||
}
|
||||
|
||||
export interface KeyringPairs {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@polkadot/networks",
|
||||
"version": "5.5.2",
|
||||
"version": "5.6.1",
|
||||
"description": "A list of all available Substrate networks and their applicable prefixes",
|
||||
"main": "index.js",
|
||||
"sideEffects": false,
|
||||
@@ -12,10 +12,10 @@
|
||||
"bugs": "https://github.com/polkadot-js/common/issues",
|
||||
"homepage": "https://github.com/polkadot-js/common/tree/master/packages/networks#readme",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5"
|
||||
"@babel/runtime": "^7.12.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@polkadot/util": "5.5.2",
|
||||
"@polkadot/x-fetch": "5.5.2"
|
||||
"@polkadot/util": "5.6.1",
|
||||
"@polkadot/x-fetch": "5.6.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,11 +16,11 @@ const UNSORTED = [0, 2, 42];
|
||||
// last. This make lookups for the current a simple genesisHash[0]
|
||||
// (See Kusama as an example)
|
||||
|
||||
const createReserved = (prefix: number, displayName = 'This prefix is reserved.'): NetworkFromSubstrate => ({
|
||||
const createReserved = (prefix: number, displayName: string, network: string | null = null): NetworkFromSubstrate => ({
|
||||
decimals: null,
|
||||
displayName,
|
||||
isIgnored: true,
|
||||
network: `reserved${prefix}`,
|
||||
network,
|
||||
prefix,
|
||||
standardAccount: null,
|
||||
symbols: null,
|
||||
@@ -41,7 +41,7 @@ const all: NetworkFromSubstrate[] = [
|
||||
symbols: ['DOT'],
|
||||
website: 'https://polkadot.network'
|
||||
},
|
||||
createReserved(1),
|
||||
createReserved(1, 'Bare 32-bit Schnorr/Ristretto (S/R 25519) public key.'),
|
||||
{
|
||||
decimals: [12],
|
||||
displayName: 'Kusama Relay Chain',
|
||||
@@ -59,7 +59,7 @@ const all: NetworkFromSubstrate[] = [
|
||||
symbols: ['KSM'],
|
||||
website: 'https://kusama.network'
|
||||
},
|
||||
createReserved(3),
|
||||
createReserved(3, 'Bare 32-bit Ed25519 public key.'),
|
||||
{
|
||||
decimals: null,
|
||||
displayName: 'Katal Chain',
|
||||
@@ -417,7 +417,7 @@ const all: NetworkFromSubstrate[] = [
|
||||
symbols: null,
|
||||
website: 'https://substrate.dev/'
|
||||
},
|
||||
createReserved(43),
|
||||
createReserved(43, 'Bare 32-bit ECDSA SECP-256k1 public key.'),
|
||||
{
|
||||
decimals: [8],
|
||||
displayName: 'ChainX',
|
||||
@@ -436,9 +436,8 @@ const all: NetworkFromSubstrate[] = [
|
||||
symbols: ['UART', 'UINK'],
|
||||
website: 'https://uniarts.me'
|
||||
},
|
||||
createReserved(46),
|
||||
createReserved(47),
|
||||
createReserved(48, 'All prefixes 48 and higher are reserved and cannot be allocated.')
|
||||
createReserved(46, 'This prefix is reserved.', 'reserved46'),
|
||||
createReserved(47, 'This prefix is reserved.', 'reserved47')
|
||||
];
|
||||
|
||||
// The list of available/claimed prefixes
|
||||
|
||||
@@ -6,7 +6,7 @@ export type Icon = 'beachball' | 'empty' | 'jdenticon' | 'polkadot' | 'substrate
|
||||
export interface NetworkFromSubstrate {
|
||||
decimals: number[] | null,
|
||||
displayName: string;
|
||||
network: string;
|
||||
network: string | null;
|
||||
prefix: number;
|
||||
genesisHash?: string[] | null;
|
||||
hasLedgerSupport?: boolean;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@polkadot/util-crypto",
|
||||
"version": "5.5.2",
|
||||
"version": "5.6.1",
|
||||
"description": "A collection of useful crypto utilities for @polkadot",
|
||||
"main": "index.js",
|
||||
"sideEffects": [
|
||||
@@ -15,16 +15,16 @@
|
||||
"bugs": "https://github.com/polkadot-js/common/issues",
|
||||
"homepage": "https://github.com/polkadot-js/common/tree/master/packages/util-crypto#readme",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@polkadot/networks": "5.5.2",
|
||||
"@polkadot/util": "5.5.2",
|
||||
"@babel/runtime": "^7.12.13",
|
||||
"@polkadot/networks": "5.6.1",
|
||||
"@polkadot/util": "5.6.1",
|
||||
"@polkadot/wasm-crypto": "^3.2.2",
|
||||
"@polkadot/x-randomvalues": "5.5.2",
|
||||
"@polkadot/x-randomvalues": "5.6.1",
|
||||
"base-x": "^3.0.8",
|
||||
"blakejs": "^1.1.0",
|
||||
"bn.js": "^4.11.9",
|
||||
"create-hash": "^1.2.0",
|
||||
"elliptic": "^6.5.3",
|
||||
"elliptic": "^6.5.4",
|
||||
"hash.js": "^1.1.7",
|
||||
"js-sha3": "^0.8.0",
|
||||
"scryptsy": "^2.1.0",
|
||||
@@ -39,6 +39,6 @@
|
||||
"@types/xxhashjs": "^0.2.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@polkadot/util": "5.5.2"
|
||||
"@polkadot/util": "5.6.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,13 +22,13 @@ export function checkAddress (address: string, prefix: Prefix): [boolean, string
|
||||
return [false, (error as Error).message];
|
||||
}
|
||||
|
||||
if (decoded[0] !== prefix) {
|
||||
return [false, `Prefix mismatch, expected ${prefix}, found ${decoded[0]}`];
|
||||
const [isValid,,, ss58Decoded] = checkAddressChecksum(decoded);
|
||||
|
||||
if (ss58Decoded !== prefix) {
|
||||
return [false, `Prefix mismatch, expected ${prefix}, found ${ss58Decoded}`];
|
||||
} else if (!defaults.allowedEncodedLengths.includes(decoded.length)) {
|
||||
return [false, 'Invalid decoded address length'];
|
||||
}
|
||||
|
||||
const [isValid] = checkAddressChecksum(decoded);
|
||||
|
||||
return [isValid, isValid ? null : 'Invalid decoded address checksum'];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { base58Decode } from '../base58/decode';
|
||||
import { checkAddressChecksum } from './checksum';
|
||||
|
||||
describe('checkAddressChecksum', (): void => {
|
||||
it('correctly extracts the info from a 1-byte-prefix address', (): void => {
|
||||
expect(
|
||||
checkAddressChecksum(base58Decode('F3opxRbN5ZbjJNU511Kj2TLuzFcDq9BGduA9TgiECafpg29'))
|
||||
).toEqual([true, 33, 1, 2]);
|
||||
});
|
||||
|
||||
it('correctly extracts the info from a 2-byte-prefix address', (): void => {
|
||||
expect(
|
||||
checkAddressChecksum(base58Decode('yGHU8YKprxHbHdEv7oUK4rzMZXtsdhcXVG2CAMyC9WhzhjH2k'))
|
||||
).toEqual([true, 34, 2, 255]);
|
||||
});
|
||||
|
||||
it('correctly extracts the info from a 2-byte-prefix address (ecdsa, from Substrate)', (): void => {
|
||||
expect(
|
||||
checkAddressChecksum(base58Decode('4pbsSkWcBaYoFHrKJZp5fDVUKbqSYD9dhZZGvpp3vQ5ysVs5ybV'))
|
||||
).toEqual([true, 35, 2, 200]);
|
||||
});
|
||||
});
|
||||
@@ -3,19 +3,23 @@
|
||||
|
||||
import { sshash } from './sshash';
|
||||
|
||||
export function checkAddressChecksum (decoded: Uint8Array): [boolean, number] {
|
||||
const isPublicKey = [35, 36].includes(decoded.length);
|
||||
export function checkAddressChecksum (decoded: Uint8Array): [boolean, number, number, number] {
|
||||
const ss58Length = (decoded[0] & 0b0100_0000) ? 2 : 1;
|
||||
const ss58Decoded = ss58Length === 1
|
||||
? decoded[0]
|
||||
: ((decoded[0] & 0b0011_1111) << 2) | (decoded[1] >> 6) | ((decoded[1] & 0b0011_1111) << 8);
|
||||
|
||||
// non-publicKeys has 1 byte checksums, else default to 2
|
||||
// 32/33 bytes public + 2 bytes checksum + prefix
|
||||
const isPublicKey = [34 + ss58Length, 35 + ss58Length].includes(decoded.length);
|
||||
const length = decoded.length - (isPublicKey ? 2 : 1);
|
||||
|
||||
// calculate the hash and do the checksum byte checks
|
||||
const hash = sshash(decoded.subarray(0, length));
|
||||
const isValid = (decoded[0] & 0b1000_0000) === 0 && ![46, 47].includes(decoded[0]) && (
|
||||
isPublicKey
|
||||
? decoded[decoded.length - 2] === hash[0] && decoded[decoded.length - 1] === hash[1]
|
||||
: decoded[decoded.length - 1] === hash[0]
|
||||
);
|
||||
|
||||
// see if the hash actually matches
|
||||
const isValid = isPublicKey
|
||||
? decoded[decoded.length - 2] === hash[0] && decoded[decoded.length - 1] === hash[1]
|
||||
: decoded[decoded.length - 1] === hash[0];
|
||||
|
||||
return [isValid, length];
|
||||
return [isValid, length, ss58Length, ss58Decoded];
|
||||
}
|
||||
|
||||
@@ -51,25 +51,25 @@ describe('decodeAddress', (): void => {
|
||||
|
||||
it('decodes a 1-byte accountId (with prefix)', (): void => {
|
||||
expect(
|
||||
decodeAddress('PqtB', false, 68)
|
||||
decodeAddress('g4b', false, 2)
|
||||
).toEqual(new Uint8Array([1]));
|
||||
});
|
||||
|
||||
it('decodes a 2-byte accountId', (): void => {
|
||||
expect(
|
||||
decodeAddress('2jpAFn', false, 68)
|
||||
decodeAddress('3xygo', false, 2)
|
||||
).toEqual(new Uint8Array([0, 1]));
|
||||
});
|
||||
|
||||
it('encodes a 4-byte address', (): void => {
|
||||
expect(
|
||||
decodeAddress('as7QnGMf', false, 68)
|
||||
decodeAddress('zswfoZa', false, 2)
|
||||
).toEqual(new Uint8Array([1, 2, 3, 4]));
|
||||
});
|
||||
|
||||
it('decodes a 8-byte address', (): void => {
|
||||
expect(
|
||||
decodeAddress('4q7qY5RBG7Z4wv', false, 68)
|
||||
decodeAddress('848Gh2GcGaZia', false, 2)
|
||||
).toEqual(new Uint8Array([42, 44, 10, 0, 0, 0, 0, 0]));
|
||||
});
|
||||
|
||||
@@ -81,6 +81,20 @@ describe('decodeAddress', (): void => {
|
||||
);
|
||||
});
|
||||
|
||||
it('decodes a 2-byte prefix', (): void => {
|
||||
expect(
|
||||
decodeAddress('yGHU8YKprxHbHdEv7oUK4rzMZXtsdhcXVG2CAMyC9WhzhjH2k')
|
||||
).toEqual(
|
||||
decodeAddress('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQua')
|
||||
);
|
||||
});
|
||||
|
||||
it('decodes a 2-byte prefix (ecdsa, from Substrate)', (): void => {
|
||||
expect(
|
||||
u8aToHex(decodeAddress('4pbsSkWcBaYoFHrKJZp5fDVUKbqSYD9dhZZGvpp3vQ5ysVs5ybV'))
|
||||
).toEqual('0x035676109c54b9a16d271abeb4954316a40a32bcce023ac14c8e26e958aa68fba9');
|
||||
});
|
||||
|
||||
it.skip('allows invalid prefix (in list)', (): void => {
|
||||
expect(
|
||||
(): Uint8Array => decodeAddress('6GfvWUvHvU8otbZ7sFhXH4eYeMcKdUkL61P3nFy52efEPVUx')
|
||||
|
||||
@@ -10,32 +10,23 @@ import { base58Decode } from '../base58/decode';
|
||||
import { checkAddressChecksum } from './checksum';
|
||||
import { defaults } from './defaults';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export function decodeAddress (encoded: string | Uint8Array, ignoreChecksum?: boolean, ss58Format: Prefix = -1): Uint8Array {
|
||||
if (isU8a(encoded) || isHex(encoded)) {
|
||||
return u8aToU8a(encoded);
|
||||
}
|
||||
|
||||
const wrapError = (message: string) => `Decoding ${encoded as string}: ${message}`;
|
||||
let decoded;
|
||||
|
||||
try {
|
||||
decoded = base58Decode(encoded);
|
||||
const decoded = base58Decode(encoded);
|
||||
|
||||
assert(defaults.allowedEncodedLengths.includes(decoded.length), 'Invalid decoded address length');
|
||||
|
||||
const [isValid, endPos, ss58Length, ss58Decoded] = checkAddressChecksum(decoded);
|
||||
|
||||
assert(ignoreChecksum || isValid, 'Invalid decoded address checksum');
|
||||
assert([-1, ss58Decoded].includes(ss58Format), `Expected ss58Format ${ss58Format}, received ${ss58Decoded}`);
|
||||
|
||||
return decoded.slice(ss58Length, endPos);
|
||||
} catch (error) {
|
||||
throw new Error(wrapError((error as Error).message));
|
||||
throw new Error(`Decoding ${encoded as string}: ${(error as Error).message}`);
|
||||
}
|
||||
|
||||
// assert(defaults.allowedPrefix.includes(decoded[0] as Prefix), error('Invalid decoded address prefix'));
|
||||
assert(defaults.allowedEncodedLengths.includes(decoded.length), wrapError('Invalid decoded address length'));
|
||||
|
||||
// TODO Unless it is an "use everywhere" prefix, throw an error
|
||||
// if (ss58Format !== -1 && (decoded[0] !== ss58Format)) {
|
||||
// console.log(`WARN: Expected ${ss58Format}, found ${decoded[0]}`);
|
||||
// }
|
||||
|
||||
const [isValid, endPos] = checkAddressChecksum(decoded);
|
||||
|
||||
assert(ignoreChecksum || isValid, wrapError('Invalid decoded address checksum'));
|
||||
|
||||
return decoded.slice(1, endPos);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { available } from '@polkadot/networks';
|
||||
export const defaults = {
|
||||
allowedDecodedLengths: [1, 2, 4, 8, 32, 33],
|
||||
// publicKey has prefix + 2 checksum bytes, short only prefix + 1 checksum byte
|
||||
allowedEncodedLengths: [3, 4, 6, 10, 35, 36],
|
||||
allowedEncodedLengths: [3, 4, 6, 10, 35, 36, 37, 38],
|
||||
allowedPrefix: available.map(({ prefix }) => prefix),
|
||||
prefix: 42
|
||||
};
|
||||
|
||||
@@ -7,6 +7,40 @@ import { encodeAddress } from '.';
|
||||
|
||||
const keyring = createTestPairs({ type: 'ed25519' }, false);
|
||||
|
||||
const SUBKEY = [
|
||||
{
|
||||
address: '5DA4D4GL5iakrn22h5uKoevgvo18Pqj5BcdEUv8etEDPdijA',
|
||||
publicKey: '0x3050f8456519829fe03302da802d22d3233a5f4037b9a3e2bcc403ccfcb2d735',
|
||||
ss58Format: 42
|
||||
},
|
||||
{
|
||||
// ecdsa
|
||||
address: '4pbsSkWcBaYoFHrKJZp5fDVUKbqSYD9dhZZGvpp3vQ5ysVs5ybV',
|
||||
publicKey: '0x035676109c54b9a16d271abeb4954316a40a32bcce023ac14c8e26e958aa68fba9',
|
||||
ss58Format: 200
|
||||
},
|
||||
{
|
||||
address: 'yGF4JP7q5AK46d1FPCEm9sYQ4KooSjHMpyVAjLnsCSWVafPnf',
|
||||
publicKey: '0x66cd6cf085627d6c85af1aaf2bd10cf843033e929b4e3b1c2ba8e4aa46fe111b',
|
||||
ss58Format: 255
|
||||
},
|
||||
{
|
||||
address: 'yGDYxQatQwuxqT39Zs4LtcTnpzE12vXb7ZJ6xpdiHv6gTu1hF',
|
||||
publicKey: '0x242fd5a078ac6b7c3c2531e9bcf1314343782aeb58e7bc6880794589e701db55',
|
||||
ss58Format: 255
|
||||
},
|
||||
{
|
||||
address: 'mHm8k9Emsvyfp3piCauSH684iA6NakctF8dySQcX94GDdrJrE',
|
||||
publicKey: '0x44d5a3ac156335ea99d33a83c57c7146c40c8e2260a8a4adf4e7a86256454651',
|
||||
ss58Format: 4242
|
||||
},
|
||||
{
|
||||
address: 'r6Gr4gaMP8TsjhFbqvZhv3YvnasugLiRJpzpRHifsqqG18UXa',
|
||||
publicKey: '0x88f01441682a17b52d6ae12d1a5670cf675fd254897efabaa5069eb3a701ab73',
|
||||
ss58Format: 14269
|
||||
}
|
||||
];
|
||||
|
||||
describe('encode', (): void => {
|
||||
it('encodes an address to a valid value', (): void => {
|
||||
expect(
|
||||
@@ -16,8 +50,8 @@ describe('encode', (): void => {
|
||||
|
||||
it('can re-encode an address', (): void => {
|
||||
expect(
|
||||
encodeAddress('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', 68)
|
||||
).toEqual('7sL6eNJj5ZGV5cn3hhV2deRUsivXfBfMH76wCALCqWj1EKzv');
|
||||
encodeAddress('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', 2)
|
||||
).toEqual('HNZata7iMYWmk5RvZRTiAsSDhV8366zq2YGb3tLH5Upf74F');
|
||||
});
|
||||
|
||||
it('can re-encode an address to Polkadot live', (): void => {
|
||||
@@ -45,33 +79,33 @@ describe('encode', (): void => {
|
||||
it('encodes a 1-byte address (with prefix)', (): void => {
|
||||
expect(
|
||||
encodeAddress(
|
||||
new Uint8Array([1]), 68
|
||||
new Uint8Array([1]), 2
|
||||
)
|
||||
).toEqual('PqtB');
|
||||
).toEqual('g4b');
|
||||
});
|
||||
|
||||
it('encodes a 2-byte address', (): void => {
|
||||
expect(
|
||||
encodeAddress(
|
||||
new Uint8Array([0, 1]), 68
|
||||
new Uint8Array([0, 1]), 2
|
||||
)
|
||||
).toEqual('2jpAFn');
|
||||
).toEqual('3xygo');
|
||||
});
|
||||
|
||||
it('encodes a 4-byte address', (): void => {
|
||||
expect(
|
||||
encodeAddress(
|
||||
new Uint8Array([1, 2, 3, 4]), 68
|
||||
new Uint8Array([1, 2, 3, 4]), 2
|
||||
)
|
||||
).toEqual('as7QnGMf');
|
||||
).toEqual('zswfoZa');
|
||||
});
|
||||
|
||||
it('encodes a 8-byte address', (): void => {
|
||||
expect(
|
||||
encodeAddress(
|
||||
new Uint8Array([42, 44, 10, 0, 0, 0, 0, 0]), 68
|
||||
new Uint8Array([42, 44, 10, 0, 0, 0, 0, 0]), 2
|
||||
)
|
||||
).toEqual('4q7qY5RBG7Z4wv');
|
||||
).toEqual('848Gh2GcGaZia');
|
||||
});
|
||||
|
||||
it('encodes an 33-byte address', (): void => {
|
||||
@@ -79,4 +113,36 @@ describe('encode', (): void => {
|
||||
encodeAddress('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077')
|
||||
).toEqual('KWCv1L3QX9LDPwY4VzvLmarEmXjVJidUzZcinvVnmxAJJCBou');
|
||||
});
|
||||
|
||||
it('encodes with 2 byte prefix', (): void => {
|
||||
expect(
|
||||
encodeAddress(keyring.alice.publicKey, 255)
|
||||
).toEqual('yGHU8YKprxHbHdEv7oUK4rzMZXtsdhcXVG2CAMyC9WhzhjH2k');
|
||||
});
|
||||
|
||||
SUBKEY.forEach(({ address, publicKey, ss58Format }, index): void => {
|
||||
it(`encodes with Subkey equality (${index})`, (): void => {
|
||||
expect(
|
||||
encodeAddress(publicKey, ss58Format)
|
||||
).toEqual(address);
|
||||
});
|
||||
});
|
||||
|
||||
it('does not encode for > 16,383, < 0', (): void => {
|
||||
expect(
|
||||
() => encodeAddress(keyring.alice.publicKey, -1)
|
||||
).toThrow(/range ss58Format specified/);
|
||||
expect(
|
||||
() => encodeAddress(keyring.alice.publicKey, 16384)
|
||||
).toThrow(/range ss58Format specified/);
|
||||
});
|
||||
|
||||
it('does not encode reserved', (): void => {
|
||||
expect(
|
||||
() => encodeAddress(keyring.alice.publicKey, 46)
|
||||
).toThrow(/range ss58Format specified/);
|
||||
expect(
|
||||
() => encodeAddress(keyring.alice.publicKey, 47)
|
||||
).toThrow(/range ss58Format specified/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,13 +15,23 @@ export function encodeAddress (_key: Uint8Array | string, ss58Format: Prefix = d
|
||||
// decode it, this means we can re-encode an address
|
||||
const key = decodeAddress(_key);
|
||||
|
||||
assert(ss58Format >= 0 && ss58Format <= 16383 && ![46, 47].includes(ss58Format), 'Out of range ss58Format specified');
|
||||
assert(defaults.allowedDecodedLengths.includes(key.length), `Expected a valid key to convert, with length ${defaults.allowedDecodedLengths.join(', ')}`);
|
||||
|
||||
const isPublicKey = [32, 33].includes(key.length);
|
||||
const input = u8aConcat(new Uint8Array([ss58Format]), key);
|
||||
const hash = sshash(input);
|
||||
const input = u8aConcat(
|
||||
new Uint8Array(
|
||||
ss58Format < 64
|
||||
? [ss58Format]
|
||||
: [
|
||||
((ss58Format & 0b0000_0000_1111_1100) >> 2) | 0b0100_0000,
|
||||
(ss58Format >> 8) | ((ss58Format & 0b0000_0000_0000_0011) << 6)
|
||||
]
|
||||
),
|
||||
key
|
||||
);
|
||||
|
||||
return base58Encode(
|
||||
u8aConcat(input, hash.subarray(0, isPublicKey ? 2 : 1))
|
||||
u8aConcat(input, sshash(input).subarray(0, isPublicKey ? 2 : 1))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ describe('addressEq', (): void => {
|
||||
expect(
|
||||
addressEq(
|
||||
'5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
|
||||
'7sL6eNJj5ZGV5cn3hhV2deRUsivXfBfMH76wCALCqWj1EKzv'
|
||||
'15oF4uVJwmo4TdGW7VfQxNLavjCXviqxT9S1MgbjMNHr6Sp5'
|
||||
)
|
||||
).toEqual(true);
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@ import { encodeAddress, setSS58Format } from '.';
|
||||
|
||||
describe('setSS58Format', (): void => {
|
||||
beforeEach((): void => {
|
||||
setSS58Format(68);
|
||||
setSS58Format(2);
|
||||
});
|
||||
|
||||
it('sets and allows encoding using', (): void => {
|
||||
@@ -13,6 +13,6 @@ describe('setSS58Format', (): void => {
|
||||
encodeAddress(
|
||||
new Uint8Array([1])
|
||||
)
|
||||
).toEqual('PqtB');
|
||||
).toEqual('g4b');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,15 +11,10 @@ const ED25519_CRYPTO = 'ed25519 seed';
|
||||
// gets an xprv from a mnemonic
|
||||
export function ledgerMaster (mnemonic: string): Uint8Array {
|
||||
const seed = mnemonicToSeedSync(mnemonic);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
const chainCode = hmacSha256(ED25519_CRYPTO, new Uint8Array([1, ...seed]));
|
||||
let priv;
|
||||
|
||||
while (!priv || (priv[31] & 0b0010_0000)) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
priv = hmacSha512(ED25519_CRYPTO, priv || seed);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
import hash from 'hash.js';
|
||||
|
||||
import { assert, u8aToU8a } from '@polkadot/util';
|
||||
import { assert, stringToU8a, u8aToU8a } from '@polkadot/util';
|
||||
|
||||
import { pbkdf2Encode } from '../pbkdf2';
|
||||
import { randomAsU8a } from '../random/asU8a';
|
||||
@@ -45,13 +45,9 @@ function deriveChecksumBits (entropyBuffer: Uint8Array): string {
|
||||
return bytesToBinary(Array.from(result)).slice(0, CS);
|
||||
}
|
||||
|
||||
function salt (password?: string): string {
|
||||
return 'mnemonic' + (password || '');
|
||||
}
|
||||
|
||||
export function mnemonicToSeedSync (mnemonic: string, password?: string): Uint8Array {
|
||||
const mnemonicBuffer = Buffer.from(normalize(mnemonic), 'utf8');
|
||||
const saltBuffer = Buffer.from(salt(normalize(password)), 'utf8');
|
||||
const mnemonicBuffer = stringToU8a(normalize(mnemonic));
|
||||
const saltBuffer = stringToU8a(`mnemonic${normalize(password)}`);
|
||||
|
||||
return pbkdf2Encode(mnemonicBuffer, saltBuffer).password;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { u8aToHex } from '@polkadot/util';
|
||||
import { u8aEq, u8aToHex } from '@polkadot/util';
|
||||
import { waitReady } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { mnemonicToLegacySeed } from './';
|
||||
|
||||
const MENMONIC = 'seed sock milk update focus rotate barely fade car face mechanic mercy';
|
||||
const MNEMONIC = 'seed sock milk update focus rotate barely fade car face mechanic mercy';
|
||||
const SEED = '0x3c121e20de068083b49c2315697fb59a2d9e8643c24e5ea7628132c58969a027';
|
||||
|
||||
describe('mnemonicToLegacySeed', (): void => {
|
||||
@@ -14,11 +14,30 @@ describe('mnemonicToLegacySeed', (): void => {
|
||||
await waitReady();
|
||||
});
|
||||
|
||||
[undefined, 'foo', 'bar'].forEach((password): void => {
|
||||
it(`generates Wasm & Js equivalents (password = ${password || 'undefined'})`, (): void => {
|
||||
expect(
|
||||
u8aEq(
|
||||
mnemonicToLegacySeed(MNEMONIC, password, true),
|
||||
mnemonicToLegacySeed(MNEMONIC, password, false)
|
||||
)
|
||||
).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
[false, true].forEach((onlyJs): void => {
|
||||
it(`generates a valid seed (onlyJs = ${onlyJs.toString()})`, (): void => {
|
||||
expect(
|
||||
u8aToHex(mnemonicToLegacySeed(MENMONIC, undefined, onlyJs))
|
||||
u8aToHex(mnemonicToLegacySeed(MNEMONIC, undefined, onlyJs))
|
||||
).toEqual(SEED);
|
||||
});
|
||||
});
|
||||
|
||||
[false, true].forEach((onlyJs): void => {
|
||||
it(`fails with non-mnemonics (onlyJs = ${onlyJs.toString()})`, (): void => {
|
||||
expect(
|
||||
() => mnemonicToLegacySeed('foo bar baz', undefined, onlyJs)
|
||||
).toThrow(/mnemonic specified/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { assert } from '@polkadot/util';
|
||||
import { bip39ToSeed, isReady } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { mnemonicToSeedSync } from './bip39';
|
||||
import { mnemonicValidate } from './validate';
|
||||
|
||||
/**
|
||||
* @name toSeed
|
||||
@@ -23,6 +25,8 @@ import { mnemonicToSeedSync } from './bip39';
|
||||
* ```
|
||||
*/
|
||||
export function mnemonicToLegacySeed (mnemonic: string, password = '', onlyJs = false): Uint8Array {
|
||||
assert(mnemonicValidate(mnemonic), 'Invalid bip39 mnemonic specified');
|
||||
|
||||
return isReady() && !onlyJs
|
||||
? bip39ToSeed(mnemonic, password)
|
||||
: mnemonicToSeedSync(mnemonic, password).subarray(0, 32);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { u8aToHex } from '@polkadot/util';
|
||||
import { u8aEq, u8aToHex } from '@polkadot/util';
|
||||
|
||||
import tests from '../schnorrkel/keypair/testing';
|
||||
import { cryptoWaitReady } from '..';
|
||||
@@ -15,6 +15,17 @@ describe('mnemonicToMiniSecret', (): void => {
|
||||
await cryptoWaitReady();
|
||||
});
|
||||
|
||||
[undefined, 'foo', 'bar'].forEach((password): void => {
|
||||
it(`generates Wasm & Js equivalents (password = ${password || 'undefined'})`, (): void => {
|
||||
expect(
|
||||
u8aEq(
|
||||
mnemonicToMiniSecret(MNEMONIC, password, true),
|
||||
mnemonicToMiniSecret(MNEMONIC, password, false)
|
||||
)
|
||||
).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
[false, true].forEach((onlyJs): void => {
|
||||
it(`generates a valid seed (onlyJs = ${onlyJs.toString()})`, (): void => {
|
||||
expect(
|
||||
@@ -35,4 +46,12 @@ describe('mnemonicToMiniSecret', (): void => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
[false, true].forEach((onlyJs): void => {
|
||||
it(`fails with non-mnemonics (onlyJs = ${onlyJs.toString()})`, (): void => {
|
||||
expect(
|
||||
() => mnemonicToMiniSecret('foo bar baz', undefined, onlyJs)
|
||||
).toThrow(/mnemonic specified/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { stringToU8a, u8aToBuffer } from '@polkadot/util';
|
||||
import { assert, stringToU8a } from '@polkadot/util';
|
||||
import { bip39ToMiniSecret, isReady } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { pbkdf2Encode } from '../pbkdf2';
|
||||
import { mnemonicToEntropy } from './toEntropy';
|
||||
import { mnemonicValidate } from './validate';
|
||||
|
||||
export function mnemonicToMiniSecret (mnemonic: string, password = '', onlyJs = false): Uint8Array {
|
||||
assert(mnemonicValidate(mnemonic), 'Invalid bip39 mnemonic specified');
|
||||
|
||||
if (isReady() && !onlyJs) {
|
||||
return bip39ToMiniSecret(mnemonic, password);
|
||||
}
|
||||
|
||||
const entropy = u8aToBuffer(mnemonicToEntropy(mnemonic));
|
||||
const salt = u8aToBuffer(stringToU8a(`mnemonic${password}`));
|
||||
const entropy = mnemonicToEntropy(mnemonic);
|
||||
const salt = stringToU8a(`mnemonic${password}`);
|
||||
|
||||
// return the first 32 bytes as the seed
|
||||
return pbkdf2Encode(entropy, salt).password.slice(0, 32);
|
||||
|
||||
@@ -15,15 +15,17 @@ describe('pbkdf2Encode', (): void => {
|
||||
await waitReady();
|
||||
});
|
||||
|
||||
it('has equivalent Wasm & JS results', (): void => {
|
||||
const salt = randomAsU8a();
|
||||
[256, 1024, 2048].forEach((rounds): void => {
|
||||
it(`has equivalent Wasm & JS results (${rounds} rounds)`, (): void => {
|
||||
const salt = randomAsU8a();
|
||||
|
||||
expect(
|
||||
u8aEq(
|
||||
pbkdf2Encode(TEST_PASSWORD, salt, 2048, false).password,
|
||||
pbkdf2Encode(TEST_PASSWORD, salt, 2048, true).password
|
||||
)
|
||||
).toBe(true);
|
||||
expect(
|
||||
u8aEq(
|
||||
pbkdf2Encode(TEST_PASSWORD, salt, rounds, false).password,
|
||||
pbkdf2Encode(TEST_PASSWORD, salt, rounds, true).password
|
||||
)
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
[false, true].forEach((onlyJs): void => {
|
||||
|
||||
@@ -3,18 +3,18 @@
|
||||
|
||||
// Adapted from https://gist.github.com/calvinmetcalf/91e8e84dc63c75f2aa53
|
||||
|
||||
import { u8aConcat } from '@polkadot/util';
|
||||
import { bnToU8a, u8aConcat } from '@polkadot/util';
|
||||
|
||||
import { hmacSha512 } from '../hmac';
|
||||
|
||||
export function pbkdf2Sync (password: Uint8Array, salt: Uint8Array, rounds: number, len = 64): Uint8Array {
|
||||
let out = new Uint8Array();
|
||||
let num = 0;
|
||||
const block = Buffer.concat([salt, Buffer.from([0, 0, 0, 0])]);
|
||||
const block = u8aConcat(salt, new Uint8Array(4));
|
||||
|
||||
while (out.length < len) {
|
||||
num++;
|
||||
block.writeUInt32BE(num, salt.length);
|
||||
block.set(bnToU8a(num, { bitLength: 32, isLe: false }), salt.length);
|
||||
|
||||
let prev = hmacSha512(password, block);
|
||||
const md = prev;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@polkadot/util",
|
||||
"version": "5.5.2",
|
||||
"version": "5.6.1",
|
||||
"description": "A collection of useful utilities for @polkadot",
|
||||
"main": "index.js",
|
||||
"sideEffects": false,
|
||||
@@ -12,9 +12,9 @@
|
||||
"bugs": "https://github.com/polkadot-js/common/issues",
|
||||
"homepage": "https://github.com/polkadot-js/common/tree/master/packages/util#readme",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@polkadot/x-textdecoder": "5.5.2",
|
||||
"@polkadot/x-textencoder": "5.5.2",
|
||||
"@babel/runtime": "^7.12.13",
|
||||
"@polkadot/x-textdecoder": "5.6.1",
|
||||
"@polkadot/x-textencoder": "5.6.1",
|
||||
"@types/bn.js": "^4.11.6",
|
||||
"bn.js": "^4.11.9",
|
||||
"camelcase": "^5.3.1",
|
||||
|
||||
@@ -92,3 +92,15 @@ export const BN_MILLION: BN = new BN(1_000_000);
|
||||
* @summary BN constant for 1,000,000,000.
|
||||
*/
|
||||
export const BN_BILLION: BN = new BN(1_000_000_000);
|
||||
|
||||
/**
|
||||
* @name BN_QUINTILL
|
||||
* @summary BN constant for 1,000,000,000,000,000,000.
|
||||
*/
|
||||
export const BN_QUINTILL: BN = BN_BILLION.mul(BN_BILLION);
|
||||
|
||||
/**
|
||||
* @name BN_MAX_INTEGER
|
||||
* @summary BN constant for MAX_SAFE_INTEGER
|
||||
*/
|
||||
export const BN_MAX_INTEGER: BN = new BN(Number.MAX_SAFE_INTEGER);
|
||||
|
||||
@@ -5,6 +5,29 @@ import BN from 'bn.js';
|
||||
|
||||
import { bnSqrt } from '.';
|
||||
|
||||
const TESTS: [string | number | BN | BigInt, string | number][] = [
|
||||
[0, 0],
|
||||
[1, 1],
|
||||
[4, 2],
|
||||
[256 * 256, 256],
|
||||
[Number.MAX_SAFE_INTEGER, 94906265],
|
||||
[new BN(Number.MAX_SAFE_INTEGER).iaddn(256), 94906265],
|
||||
[12345678n * 12345679n, 12345678],
|
||||
// test cases below from https://github.com/Aisse-258/bigint-isqrt/blob/f5254b9750841959022461c1353437a07a08f501/test/sqrt-test.js
|
||||
[
|
||||
'54866395443885995655625',
|
||||
'234235768925'
|
||||
],
|
||||
[
|
||||
'82120471531550314555681345949499512621827274120673745141541602816614526075010755373654280259022317599142038423759320355177481886719814621305828811322920076213800348341464996337890625',
|
||||
'9062034624274524065844376014975805577107171799890766992670739972241112960081909332275390625'
|
||||
],
|
||||
[
|
||||
'2068290204957779940494571454815902632050207752627007664813877111611972735314294907791993008455381265730624848057015476004143868408297250386512042711005577373327434315310235583583151773358353534031397385969275475421659036494173765903706963544628565027679444680637920371210258368358706248446698622704138943704561842220533563976838506992543814273403403362954987589637283890281226038798503294069403370486523697009383229578343444337394508531937972775639593763364402389410142290440075517598007930025487176173228232153685095699851764630884360483051570513804913157899314303269924424908606245851172031453725690942288677622973423417902662711606364188470325930406288190754356367655606430407552275285889968860367977288564364154999338284288438279464648663835830140826171534470717958048887869721791862539529444820484648908879364324577951008050851801647916673254009490596379549192296404841454780884358889449367135382779230574781333008561195244620152128505091883408484094800737142399855337799331037607433934520934135669364954550499058123797911762228642423567268540444910163468496805547450495819213412636594319148417857753712526066272727725420886898680432958409892237191284583904066400736417812258327303739879128916456683685232170600928808798462776174857486589598776587550843543720784829419478239563366491666155716208452087857080883906200829769222868347286071365156305396955420561718593130305703074908891135467193627721682044757187980688062367562451981939375400543212890625',
|
||||
'45478458691536369397924851088318871329614555665525810477032568598360481742967756704524034986984134271202405190906906195123751965012732122548540879928668891416468847696038707003466943811913626146981446753434347552196868717612248915137033581428310504778371173112125038060138252719589122657221314190626986951627417768579641354255445988772645883238873672997347648890137913979299598785628581961589739331483895853398160785485300758458294999124984266041998145956341386549681176624661615465976970855592467853824812945820721933491609237311130118125761849911376495970746282571994312288380577879004620824004760109618074670810797544446334767103935754106103208789391060008291027361311679876439695844637933763721093782805837690830230712890625'
|
||||
]
|
||||
];
|
||||
|
||||
describe('bnSqrt', (): void => {
|
||||
it('fails on < 0 roots', (): void => {
|
||||
expect(
|
||||
@@ -12,21 +35,9 @@ describe('bnSqrt', (): void => {
|
||||
).toThrow(/negative numbers is not supported/);
|
||||
});
|
||||
|
||||
it('finds the sqrt (exact)', (): void => {
|
||||
expect(
|
||||
bnSqrt(new BN(256).mul(new BN(256)))
|
||||
).toEqual(new BN(256));
|
||||
});
|
||||
|
||||
it('finds the sqrt (round)', (): void => {
|
||||
expect(
|
||||
bnSqrt(new BN(266))
|
||||
).toEqual(new BN(16));
|
||||
});
|
||||
|
||||
it('find the sqrt for 1', (): void => {
|
||||
expect(
|
||||
bnSqrt(new BN(1))
|
||||
).toEqual(new BN(1));
|
||||
TESTS.forEach(([value, expected], index): void => {
|
||||
it(`calcs for test #${index}`, (): void => {
|
||||
expect(bnSqrt(value).eq(new BN(expected))).toEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
// Copyright 2017-2021 @polkadot/util authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { ToBn } from '../types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
|
||||
import { assert } from '../assert';
|
||||
import { BN_ONE, BN_TWO, BN_ZERO } from '../bn';
|
||||
|
||||
// https://golb.hplar.ch/2018/09/javascript-bigint.html
|
||||
function newtonIteration (n: BN, x0: BN): BN {
|
||||
const x1 = n.div(x0).iadd(x0).ishrn(1);
|
||||
|
||||
return (x0.eq(x1) || x0.eq(x1.sub(BN_ONE)))
|
||||
? x0
|
||||
: newtonIteration(n, x1);
|
||||
}
|
||||
import { BN_MAX_INTEGER, BN_ONE, BN_ZERO } from '../bn';
|
||||
import { bnToBn } from './toBn';
|
||||
|
||||
/**
|
||||
* @name bnSqrt
|
||||
@@ -28,10 +22,28 @@ function newtonIteration (n: BN, x0: BN): BN {
|
||||
* bnSqrt(new BN(16)).toString(); // => '4'
|
||||
* ```
|
||||
*/
|
||||
export function bnSqrt (value: BN): BN {
|
||||
assert(value.gte(BN_ZERO), 'square root of negative numbers is not supported');
|
||||
export function bnSqrt <ExtToBn extends ToBn> (value: ExtToBn | BN | BigInt | string | number | null): BN {
|
||||
const n = bnToBn(value);
|
||||
|
||||
return value.lt(BN_TWO)
|
||||
? value
|
||||
: newtonIteration(value, new BN(1));
|
||||
assert(n.gte(BN_ZERO), 'square root of negative numbers is not supported');
|
||||
|
||||
// https://stackoverflow.com/questions/53683995/javascript-big-integer-square-root/
|
||||
// shortcut <= 2^53 - 1 to use the JS utils
|
||||
if (n.lte(BN_MAX_INTEGER)) {
|
||||
return new BN(Math.floor(Math.sqrt(n.toNumber())));
|
||||
}
|
||||
|
||||
// Use sqrt(MAX_SAFE_INTEGER) as starting point. since we already know the
|
||||
// output will be larger than this, we expect this to be a safe start
|
||||
let x0 = new BN(94906265);
|
||||
|
||||
while (true) {
|
||||
const x1 = n.div(x0).iadd(x0).ishrn(1);
|
||||
|
||||
if (x0.eq(x1) || x0.eq(x1.sub(BN_ONE))) {
|
||||
return x0;
|
||||
}
|
||||
|
||||
x0 = x1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@polkadot/x-fetch",
|
||||
"version": "5.5.2",
|
||||
"version": "5.6.1",
|
||||
"description": "A cross-environment fetch replacement",
|
||||
"browser": "browser.js",
|
||||
"main": "node.js",
|
||||
@@ -14,8 +14,8 @@
|
||||
"bugs": "https://github.com/polkadot-js/common/issues",
|
||||
"homepage": "https://github.com/polkadot-js/common/tree/master/packages/x-fetch#readme",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@polkadot/x-global": "5.5.2",
|
||||
"@babel/runtime": "^7.12.13",
|
||||
"@polkadot/x-global": "5.6.1",
|
||||
"@types/node-fetch": "^2.5.8",
|
||||
"node-fetch": "^2.6.1"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
A cross-environment global object. checks for global > self > window > this.
|
||||
|
||||
Install it via `yarn add @polkadot/x-fetch`
|
||||
Install it via `yarn add @polkadot/x-global`
|
||||
|
||||
```js
|
||||
import { xglobal } from '@polkadot/x-global';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@polkadot/x-global",
|
||||
"version": "5.5.2",
|
||||
"version": "5.6.1",
|
||||
"description": "A cross-environment global replacement",
|
||||
"main": "index.js",
|
||||
"sideEffects": false,
|
||||
@@ -12,7 +12,7 @@
|
||||
"bugs": "https://github.com/polkadot-js/common/issues",
|
||||
"homepage": "https://github.com/polkadot-js/common/tree/master/packages/x-global#readme",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@babel/runtime": "^7.12.13",
|
||||
"@types/node-fetch": "^2.5.8",
|
||||
"node-fetch": "^2.6.1"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@polkadot/x-randomvalues",
|
||||
"version": "5.5.2",
|
||||
"version": "5.6.1",
|
||||
"description": "A cross-environment window.crypto.getRandomValues replacement",
|
||||
"browser": "browser.js",
|
||||
"main": "node.js",
|
||||
@@ -14,10 +14,10 @@
|
||||
"bugs": "https://github.com/polkadot-js/common/issues",
|
||||
"homepage": "https://github.com/polkadot-js/common/tree/master/packages/x-randomvalues#readme",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@polkadot/x-global": "5.5.2"
|
||||
"@babel/runtime": "^7.12.13",
|
||||
"@polkadot/x-global": "5.6.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react-native": "^0.63.46"
|
||||
"@types/react-native": "^0.63.48"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@polkadot/x-rxjs",
|
||||
"version": "5.5.2",
|
||||
"version": "5.6.1",
|
||||
"description": "A pass-through interface for RxJs (allowing node + mjs usage)",
|
||||
"main": "index.js",
|
||||
"sideEffects": false,
|
||||
@@ -12,7 +12,7 @@
|
||||
"bugs": "https://github.com/polkadot-js/common/issues",
|
||||
"homepage": "https://github.com/polkadot-js/common/tree/master/packages/x-rxjs#readme",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@babel/runtime": "^7.12.13",
|
||||
"rxjs": "^6.6.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@polkadot/x-textdecoder",
|
||||
"version": "5.5.2",
|
||||
"version": "5.6.1",
|
||||
"description": "A TextDecoder replacement",
|
||||
"browser": "browser.js",
|
||||
"main": "node.js",
|
||||
@@ -14,7 +14,7 @@
|
||||
"bugs": "https://github.com/polkadot-js/common/issues",
|
||||
"homepage": "https://github.com/polkadot-js/common/tree/master/packages/x-textdecoder#readme",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@polkadot/x-global": "5.5.2"
|
||||
"@babel/runtime": "^7.12.13",
|
||||
"@polkadot/x-global": "5.6.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@polkadot/x-textencoder",
|
||||
"version": "5.5.2",
|
||||
"version": "5.6.1",
|
||||
"description": "A TextDecoder replacement",
|
||||
"browser": "browser.js",
|
||||
"main": "node.js",
|
||||
@@ -14,7 +14,7 @@
|
||||
"bugs": "https://github.com/polkadot-js/common/issues",
|
||||
"homepage": "https://github.com/polkadot-js/common/tree/master/packages/x-textencoder#readme",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@polkadot/x-global": "5.5.2"
|
||||
"@babel/runtime": "^7.12.13",
|
||||
"@polkadot/x-global": "5.6.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@polkadot/x-ws",
|
||||
"version": "5.5.2",
|
||||
"version": "5.6.1",
|
||||
"description": "A cross-environment WebSocket replacement",
|
||||
"browser": "browser.js",
|
||||
"main": "node.js",
|
||||
@@ -14,8 +14,8 @@
|
||||
"bugs": "https://github.com/polkadot-js/common/issues",
|
||||
"homepage": "https://github.com/polkadot-js/common/tree/master/packages/x-ws#readme",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@polkadot/x-global": "5.5.2",
|
||||
"@babel/runtime": "^7.12.13",
|
||||
"@polkadot/x-global": "5.6.1",
|
||||
"@types/websocket": "^1.0.1",
|
||||
"websocket": "^1.0.33"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user