Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4fbdbcf8c5 | ||
|
|
ac5c993b27 | ||
|
|
e05dcf7383 | ||
|
|
c9ea3ab1ae | ||
|
|
4b097dcaef | ||
|
|
2a9cca5f1b | ||
|
|
8ce3a40c66 | ||
|
|
958a13384d | ||
|
|
e25545c39f | ||
|
|
bca1e0c5c4 | ||
|
|
bf36024568 | ||
|
|
1292a270e0 | ||
|
|
68fe800f63 | ||
|
|
ceb1575e1a | ||
|
|
4916d63832 | ||
|
|
d2176485b9 | ||
|
|
e9dd421b62 | ||
|
|
802c241820 | ||
|
|
a3b5ced423 | ||
|
|
b975766d35 | ||
|
|
eb460570ac | ||
|
|
a8658d5b60 | ||
|
|
fbe462e2e3 | ||
|
|
04bbf28a57 | ||
|
|
47b79675da | ||
|
|
4ed83e737c | ||
|
|
2ee863becc | ||
|
|
b343c31317 | ||
|
|
04759e563e | ||
|
|
6a5478919c | ||
|
|
ac828b31e6 | ||
|
|
86f17113e3 | ||
|
|
cb0429fae9 | ||
|
|
f23418ad84 | ||
|
|
8386a70bfe | ||
|
|
843cb3d00a | ||
|
|
3143e9df1c | ||
|
|
b76bdb5ed5 | ||
|
|
0e80e88456 | ||
|
|
46bfe643a6 | ||
|
|
2e0b8e4d11 | ||
|
|
a8668c3c44 | ||
|
|
11431f0e68 | ||
|
|
6c664498dd | ||
|
|
7721ac722d | ||
|
|
0070f33089 | ||
|
|
a1267eb891 | ||
|
|
1803ba6a8e | ||
|
|
ae4b166c2f | ||
|
|
38d6136aa8 | ||
|
|
02fea57a48 | ||
|
|
882826a7e3 | ||
|
|
56a7788e28 | ||
|
|
82c4b75a12 | ||
|
|
ca3c932701 | ||
|
|
76b155c269 | ||
|
|
faed35e208 | ||
|
|
c46cd5030e | ||
|
|
c7e474cdc6 | ||
|
|
ac7a678a30 | ||
|
|
f956759865 | ||
|
|
2768b70d43 | ||
|
|
cc4a579905 | ||
|
|
450b7b8e8b | ||
|
|
a248bc9f96 | ||
|
|
35f677f2e9 | ||
|
|
fccba5d93d | ||
|
|
ae172e2c53 | ||
|
|
aa9bf0780b | ||
|
|
11f1d8057b | ||
|
|
e2f1e7c033 | ||
|
|
bb213425f8 | ||
|
|
162596ed93 | ||
|
|
aa3ba246a7 | ||
|
|
baf65438fe | ||
|
|
8b443dfa4b | ||
|
|
12f3f8695f | ||
|
|
964d308491 | ||
|
|
a1bf03e40e | ||
|
|
ff940e5d26 | ||
|
|
e2fa0ad11f | ||
|
|
654e44aa99 | ||
|
|
476689f615 | ||
|
|
fc1582aa70 | ||
|
|
a616b84045 | ||
|
|
73708b72c7 | ||
|
|
8f0807ddbe | ||
|
|
5547f885be | ||
|
|
5f8d521c6a | ||
|
|
51a169a88a | ||
|
|
ad89ed5f3d | ||
|
|
ca1362c595 |
+8
-2
@@ -1,10 +1,16 @@
|
||||
6
|
||||
|
||||
2.6.2
|
||||
2.12.2
|
||||
5.2.2
|
||||
5.2.3
|
||||
|
||||
5.4.2
|
||||
5.4.3
|
||||
5.4.4
|
||||
5.5.2
|
||||
5.5.2
|
||||
5.6.2
|
||||
5.6.3
|
||||
5.9.2
|
||||
|
||||
6.0.2
|
||||
6.0.3
|
||||
@@ -5,7 +5,7 @@ jobs:
|
||||
pr:
|
||||
strategy:
|
||||
matrix:
|
||||
step: ['lint', 'test', 'build', 'networks:crosscheck']
|
||||
step: ['lint', 'test', 'build']
|
||||
name: ${{ matrix.step }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: SS58
|
||||
on:
|
||||
schedule:
|
||||
- cron: '45 3 * * *'
|
||||
- cron: '15 4/6 * * *'
|
||||
|
||||
jobs:
|
||||
ss58:
|
||||
@@ -22,3 +22,4 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GH_PAT_BOT }}
|
||||
with:
|
||||
filename: .github/ss58-check.md
|
||||
update_existing: true
|
||||
|
||||
File diff suppressed because one or more lines are too long
+1
-1
@@ -10,4 +10,4 @@ plugins:
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-version.cjs
|
||||
spec: "@yarnpkg/plugin-version"
|
||||
|
||||
yarnPath: .yarn/releases/yarn-2.4.0.cjs
|
||||
yarnPath: .yarn/releases/yarn-2.4.1.cjs
|
||||
|
||||
+111
@@ -1,5 +1,116 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 6.0.3 Mar 5, 2021
|
||||
|
||||
Changes:
|
||||
|
||||
- Add import indirection for both CJS & ESM (where generated source file is commonjs)
|
||||
|
||||
|
||||
## 6.0.2 Mar 5, 2021
|
||||
|
||||
Changes:
|
||||
|
||||
- Fix `@polkadot/x-rxjs` ESM exports for Node environments (now different due to the ESM conversion)
|
||||
|
||||
|
||||
## 6.0.1 Mar 5, 2021
|
||||
|
||||
**Important** In the 6.0 version the default package type has been changed to ESM modules by default. This should not affect usage, however since the output formats changed, a new major version is required.
|
||||
|
||||
Contributed:
|
||||
|
||||
- Add dev addresses for Frontier-based chains (Thanks to https://github.com/joelamouche)
|
||||
|
||||
Changes:
|
||||
|
||||
- Build to ESM by default (with cjs versions via export map)
|
||||
- Allow addition or raw pairs on the keyring via `{add, create}FromPair`
|
||||
- Extract re-usable json encryption/decryption functions
|
||||
|
||||
|
||||
## 5.9.2 Feb 28, 2021
|
||||
|
||||
Changes:
|
||||
|
||||
- Fix package detection for `@polkadot/networks` (no dependencies)
|
||||
|
||||
|
||||
## 5.9.1 Feb 28, 2021
|
||||
|
||||
Changes:
|
||||
|
||||
- Expand `detectPackage` with optional param allowing checks for monorepo versions
|
||||
- Export logger `format` function directly as `loggerFormat`
|
||||
- Expand tests and checks for `hdValidatePath`
|
||||
- Remove `hdEthereum` class-based approach, simplify
|
||||
- Cleanup formatting on nested conditionals in the `@polkadot/x-` packages
|
||||
- All packages now export `packageInfo` from the root
|
||||
|
||||
|
||||
## 5.8.1 Feb 26, 2021
|
||||
|
||||
Contributed:
|
||||
|
||||
- Add ss58 information for HydraDX (Thanks to https://github.com/jak-pan)
|
||||
- HD derivation for Ethereum (Thanks to https://github.com/joelamouche)
|
||||
|
||||
Changes:
|
||||
|
||||
- Adjust base64 encoding/decoding to not rely on Buffer being available
|
||||
- Rework Ethereum derivation to only use Uint8Array internally
|
||||
- Shared HD path validation for Ledger & Ethereum
|
||||
- Additional derivation, encoding and decoding tests on keyring
|
||||
- Align networks with upstream ss58 registry
|
||||
- `@polkadot/wasm-crypto` 3.2.4
|
||||
|
||||
|
||||
## 5.7.1 Feb 21, 2021
|
||||
|
||||
**Breaking change** The signature of `pair.verify` now requires the signer public key as a parameter. If using `pair.verify` pass the publicKey (hex or Uint8Array) of the signer.
|
||||
|
||||
Changes:
|
||||
|
||||
- The signature on the keyring for `.verify` now explicitly requires the `publicKey` of the signer. Previously it only verified against an own public key.
|
||||
- Add additional ss58 test vectors (against chains with >63 prefixes in the Substrate registry)
|
||||
|
||||
|
||||
## 5.6.3 Feb 16, 2021
|
||||
|
||||
Contributed:
|
||||
|
||||
- Add ss58 information for Aventus (Thanks to https://github.com/nahuseyoum)
|
||||
|
||||
Changes:
|
||||
|
||||
- Add `unlock(password)` to keypair (less confusing than only having `decodePkcs8`, equivalent to `lock()`)
|
||||
- Change use of `detectPackage` to use `packageInfo.ts`
|
||||
- Align with upstream ss58 registry
|
||||
- `@polkadot/wasm-crypto` 3.2.3
|
||||
|
||||
|
||||
## 5.6.2 Feb 8, 2021
|
||||
|
||||
Changes:
|
||||
|
||||
- `@polkadot/networks` now filters the new upstream-introduced `null` networks
|
||||
|
||||
|
||||
## 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:
|
||||
|
||||
@@ -32,5 +32,5 @@ module.exports = Object.assign({}, config, {
|
||||
'<rootDir>/packages/x-textencoder/build',
|
||||
'<rootDir>/packages/x-ws/build'
|
||||
],
|
||||
resolver: '@polkadot/dev/config/jest-resolver.cjs'
|
||||
transformIgnorePatterns: ['/node_modules/(?!@polkadot)']
|
||||
});
|
||||
+5
-5
@@ -7,7 +7,7 @@
|
||||
"packages/*"
|
||||
],
|
||||
"resolutions": {
|
||||
"typescript": "^4.1.3"
|
||||
"typescript": "^4.2.2"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "polkadot-dev-build-ts",
|
||||
@@ -20,10 +20,10 @@
|
||||
"test:one": "polkadot-dev-run-test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.10",
|
||||
"@polkadot/dev": "^0.61.24",
|
||||
"@polkadot/ts": "^0.3.59",
|
||||
"@babel/core": "^7.13.8",
|
||||
"@polkadot/dev": "^0.62.5",
|
||||
"@polkadot/ts": "^0.3.61",
|
||||
"@types/jest": "^26.0.20"
|
||||
},
|
||||
"version": "5.5.2"
|
||||
"version": "6.0.3"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"name": "@polkadot/hw-ledger-transports",
|
||||
"version": "5.5.2",
|
||||
"version": "6.0.3",
|
||||
"type": "module",
|
||||
"repository": "github:polkadot-js/common",
|
||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||
"maintainers": [],
|
||||
@@ -11,14 +12,14 @@
|
||||
"react-native": "react-native.js",
|
||||
"sideEffects": false,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@ledgerhq/hw-transport-webusb": "^5.41.0"
|
||||
"@babel/runtime": "^7.13.9",
|
||||
"@ledgerhq/hw-transport-webusb": "^5.45.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/ledgerhq__hw-transport-node-hid": "^4.22.2",
|
||||
"@types/ledgerhq__hw-transport-webusb": "^4.70.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@ledgerhq/hw-transport-node-hid-singleton": "^5.41.0"
|
||||
"@ledgerhq/hw-transport-node-hid-singleton": "^5.45.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,9 @@ import type { TransportDef } from './types';
|
||||
|
||||
import LedgerWebUSB from '@ledgerhq/hw-transport-webusb';
|
||||
|
||||
const transports: TransportDef[] = [
|
||||
export { packageInfo } from './packageInfo';
|
||||
|
||||
export const transports: TransportDef[] = [
|
||||
// deprecated
|
||||
// import LedgerU2F from '@ledgerhq/hw-transport-u2f';
|
||||
// {
|
||||
@@ -20,5 +22,3 @@ const transports: TransportDef[] = [
|
||||
type: 'webusb'
|
||||
}
|
||||
];
|
||||
|
||||
export { transports };
|
||||
|
||||
@@ -6,12 +6,12 @@ import type { TransportDef } from './types';
|
||||
|
||||
import LedgerHid from '@ledgerhq/hw-transport-node-hid-singleton';
|
||||
|
||||
const transports: TransportDef[] = [
|
||||
export { packageInfo } from './packageInfo';
|
||||
|
||||
export const transports: TransportDef[] = [
|
||||
{
|
||||
create: (): Promise<Transport> =>
|
||||
LedgerHid.create(),
|
||||
type: 'hid'
|
||||
}
|
||||
];
|
||||
|
||||
export { transports };
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright 2017-2021 @polkadot/hw-ledger-transports authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Auto-generated by @polkadot/dev, do not edit
|
||||
|
||||
export const packageInfo = { name: '@polkadot/hw-ledger-transports', version: '6.0.3' };
|
||||
@@ -3,6 +3,6 @@
|
||||
|
||||
import type { TransportDef } from './types';
|
||||
|
||||
const transports: TransportDef[] = [];
|
||||
export { packageInfo } from './packageInfo';
|
||||
|
||||
export { transports };
|
||||
export const transports: TransportDef[] = [];
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
{
|
||||
"name": "@polkadot/hw-ledger",
|
||||
"version": "5.5.2",
|
||||
"version": "6.0.3",
|
||||
"type": "module",
|
||||
"repository": "github:polkadot-js/common",
|
||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||
"maintainers": [],
|
||||
"contributors": [],
|
||||
"license": "Apache-2.0",
|
||||
"main": "index.js",
|
||||
"sideEffects": false,
|
||||
"sideEffects": [
|
||||
"./detectPackage.js",
|
||||
"./detectPackage.cjs"
|
||||
],
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@polkadot/hw-ledger-transports": "5.5.2",
|
||||
"@polkadot/util": "^5.5.2",
|
||||
"@zondax/ledger-polkadot": "^0.13.0"
|
||||
"@babel/runtime": "^7.13.9",
|
||||
"@polkadot/hw-ledger-transports": "6.0.3",
|
||||
"@polkadot/util": "^6.0.3",
|
||||
"@zondax/ledger-polkadot": "^0.13.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
// Copyright 2017-2021 @polkadot/hw-ledger authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { packageInfo as transportInfo } from '@polkadot/hw-ledger-transports/packageInfo';
|
||||
import { detectPackage, packageInfo as utilInfo } from '@polkadot/util';
|
||||
|
||||
import { packageInfo } from './packageInfo';
|
||||
|
||||
detectPackage(packageInfo, typeof __dirname !== 'undefined' && __dirname, [transportInfo, utilInfo]);
|
||||
@@ -1,13 +1,17 @@
|
||||
// Copyright 2017-2021 @polkadot/hw-ledger authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import './detectPackage';
|
||||
|
||||
import type Transport from '@ledgerhq/hw-transport';
|
||||
import type { AccountOptions, LedgerAddress, LedgerSignature, LedgerTypes, LedgerVersion } from './types';
|
||||
|
||||
import { newDockApp, newKusamaApp, newPolkadotApp, newPolymeshApp, ResponseBase, SubstrateApp } from '@zondax/ledger-polkadot';
|
||||
|
||||
import { transports } from '@polkadot/hw-ledger-transports';
|
||||
import { assert, bufferToU8a, u8aToBuffer, u8aToHex } from '@polkadot/util';
|
||||
import { assert, u8aToBuffer } from '@polkadot/util';
|
||||
|
||||
export { packageInfo } from './packageInfo';
|
||||
|
||||
export const LEDGER_DEFAULT_ACCOUNT = 0x80000000;
|
||||
|
||||
@@ -77,7 +81,7 @@ export class Ledger {
|
||||
const { signature } = await this.#wrapError(app.sign(account + accountOffset, change, addressIndex + addressOffset, buffer));
|
||||
|
||||
return {
|
||||
signature: u8aToHex(bufferToU8a(signature))
|
||||
signature: `0x${signature.toString('hex')}`
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright 2017-2021 @polkadot/hw-ledger authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Auto-generated by @polkadot/dev, do not edit
|
||||
|
||||
export const packageInfo = { name: '@polkadot/hw-ledger', version: '6.0.3' };
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"name": "@polkadot/keyring",
|
||||
"version": "5.5.2",
|
||||
"version": "6.0.3",
|
||||
"type": "module",
|
||||
"description": "Keyring management",
|
||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||
"maintainers": [],
|
||||
@@ -9,18 +10,18 @@
|
||||
"main": "index.js",
|
||||
"sideEffects": [
|
||||
"./detectPackage.js",
|
||||
"./detectPackage.mjs"
|
||||
"./detectPackage.cjs"
|
||||
],
|
||||
"repository": "github:polkadot-js/common",
|
||||
"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.13.9",
|
||||
"@polkadot/util": "6.0.3",
|
||||
"@polkadot/util-crypto": "6.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@polkadot/util": "5.5.2",
|
||||
"@polkadot/util-crypto": "5.5.2"
|
||||
"@polkadot/util": "6.0.3",
|
||||
"@polkadot/util-crypto": "6.0.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// default substrate dev phrase
|
||||
const DEV_PHRASE = 'bottom drive obey lake curtain smoke basket hold race lonely fit walk';
|
||||
export const DEV_PHRASE = 'bottom drive obey lake curtain smoke basket hold race lonely fit walk';
|
||||
|
||||
// seed from the above phrase
|
||||
const DEV_SEED = '0xfac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e';
|
||||
|
||||
export {
|
||||
DEV_PHRASE,
|
||||
DEV_SEED
|
||||
};
|
||||
export const DEV_SEED = '0xfac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e';
|
||||
|
||||
@@ -1,25 +1,9 @@
|
||||
// Copyright 2017-2021 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// For esm, this should be import.meta.url or to get the same behavior as __dirname, we can use
|
||||
//
|
||||
// new URL(import.meta.url).pathname)
|
||||
//
|
||||
// The issue is the WP4 has "some" issues with import.meta.url. So because of bundlers, we can't have
|
||||
// nice things... In this case it is even worse since import.meta.url won't even make it compile, so
|
||||
// there is a complete dead end with usage thereof
|
||||
//
|
||||
// When that is fixed, a solution is to have both .js & .mjs files, with the following content -
|
||||
//
|
||||
// cjs: util.detectPackage(packageInfo, () => __dirname);
|
||||
// esm: detectPackage(packageInfo, () => import.meta.url);
|
||||
//
|
||||
// With the above we additionally need a .d.ts to just export the packageInfo
|
||||
import { detectPackage, packageInfo as utilInfo } from '@polkadot/util';
|
||||
import { packageInfo as cryptoInfo } from '@polkadot/util-crypto/packageInfo';
|
||||
|
||||
import { detectPackage } from '@polkadot/util';
|
||||
import { packageInfo } from './packageInfo';
|
||||
|
||||
import packageInfo from './package-info.json';
|
||||
|
||||
detectPackage(packageInfo, typeof __dirname !== 'undefined' && __dirname);
|
||||
|
||||
export { packageInfo };
|
||||
detectPackage(packageInfo, typeof __dirname !== 'undefined' && __dirname, [utilInfo, cryptoInfo]);
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
// Copyright 2017-2021 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { hexToU8a, stringToU8a } from '@polkadot/util';
|
||||
import { cryptoWaitReady, encodeAddress, setSS58Format } from '@polkadot/util-crypto';
|
||||
import type { KeyringPair$Json } from './types';
|
||||
|
||||
import { hexToU8a, stringToU8a } from '@polkadot/util';
|
||||
import { base64Decode, cryptoWaitReady, encodeAddress, randomAsU8a, setSS58Format } from '@polkadot/util-crypto';
|
||||
|
||||
import { decodePair } from './pair/decode';
|
||||
import Keyring from '.';
|
||||
|
||||
describe('keypair', (): void => {
|
||||
@@ -46,11 +49,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');
|
||||
});
|
||||
|
||||
@@ -83,8 +86,9 @@ describe('keypair', (): void => {
|
||||
const pair = keyring.getPair(publicKeyOne);
|
||||
const signature = pair.sign(MESSAGE);
|
||||
|
||||
expect(pair.verify(MESSAGE, signature)).toBe(true);
|
||||
expect(pair.verify(new Uint8Array(), signature)).toBe(false);
|
||||
expect(pair.verify(MESSAGE, signature, pair.publicKey)).toBe(true);
|
||||
expect(pair.verify(MESSAGE, signature, randomAsU8a())).toBe(false);
|
||||
expect(pair.verify(new Uint8Array(), signature, pair.publicKey)).toBe(false);
|
||||
});
|
||||
|
||||
it('signs and verifies (withType)', (): void => {
|
||||
@@ -92,8 +96,9 @@ describe('keypair', (): void => {
|
||||
const pair = keyring.getPair(publicKeyOne);
|
||||
const signature = pair.sign(MESSAGE, { withType: true });
|
||||
|
||||
expect(pair.verify(MESSAGE, signature)).toBe(true);
|
||||
expect(pair.verify(new Uint8Array(), signature)).toBe(false);
|
||||
expect(pair.verify(MESSAGE, signature, pair.publicKey)).toBe(true);
|
||||
expect(pair.verify(MESSAGE, signature, randomAsU8a())).toBe(false);
|
||||
expect(pair.verify(new Uint8Array(), signature, pair.publicKey)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -123,11 +128,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 => {
|
||||
@@ -159,8 +164,9 @@ describe('keypair', (): void => {
|
||||
const pair = keyring.getPair(publicKeyOne);
|
||||
const signature = pair.sign(MESSAGE);
|
||||
|
||||
expect(pair.verify(MESSAGE, signature)).toBe(true);
|
||||
expect(pair.verify(new Uint8Array(), signature)).toBe(false);
|
||||
expect(pair.verify(MESSAGE, signature, pair.publicKey)).toBe(true);
|
||||
expect(pair.verify(MESSAGE, signature, randomAsU8a())).toBe(false);
|
||||
expect(pair.verify(new Uint8Array(), signature, pair.publicKey)).toBe(false);
|
||||
});
|
||||
|
||||
it('signs and verifies (withType)', (): void => {
|
||||
@@ -168,8 +174,9 @@ describe('keypair', (): void => {
|
||||
const pair = keyring.getPair(publicKeyOne);
|
||||
const signature = pair.sign(MESSAGE, { withType: true });
|
||||
|
||||
expect(pair.verify(MESSAGE, signature)).toBe(true);
|
||||
expect(pair.verify(new Uint8Array(), signature)).toBe(false);
|
||||
expect(pair.verify(MESSAGE, signature, pair.publicKey)).toBe(true);
|
||||
expect(pair.verify(MESSAGE, signature, randomAsU8a())).toBe(false);
|
||||
expect(pair.verify(new Uint8Array(), signature, pair.publicKey)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -200,11 +207,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 => {
|
||||
@@ -259,13 +266,33 @@ describe('keypair', (): void => {
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it('encodes a pair toJSON (and decodes)', (): void => {
|
||||
const pair = keyring.createFromUri('moral movie very draw assault whisper awful rebuild speed purity repeat card');
|
||||
const json = pair.toJson('password');
|
||||
|
||||
expect(json.address).toEqual('0x03ddca309bd5fedd01f914d6fb76f23aa848a2a520802159215dba5085d7863619');
|
||||
expect(json.encoding).toEqual({
|
||||
content: ['pkcs8', 'ecdsa'],
|
||||
type: ['scrypt', 'xsalsa20-poly1305'],
|
||||
version: '3'
|
||||
});
|
||||
|
||||
const newPair = keyring.createFromJson(json);
|
||||
|
||||
expect(newPair.publicKey).toEqual(pair.publicKey);
|
||||
expect(
|
||||
() => newPair.unlock('password')
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it('signs and verifies', (): void => {
|
||||
const MESSAGE = stringToU8a('this is a message');
|
||||
const pair = keyring.getPair(addressKeyOne);
|
||||
const signature = pair.sign(MESSAGE);
|
||||
|
||||
expect(pair.verify(MESSAGE, signature)).toBe(true);
|
||||
expect(pair.verify(new Uint8Array(), signature)).toBe(false);
|
||||
expect(pair.verify(MESSAGE, signature, pair.publicKey)).toBe(true);
|
||||
expect(pair.verify(MESSAGE, signature, randomAsU8a())).toBe(false);
|
||||
expect(pair.verify(new Uint8Array(), signature, pair.publicKey)).toBe(false);
|
||||
});
|
||||
|
||||
it('signs and verifies (withType)', (): void => {
|
||||
@@ -273,36 +300,86 @@ describe('keypair', (): void => {
|
||||
const pair = keyring.getPair(addressKeyOne);
|
||||
const signature = pair.sign(MESSAGE, { withType: true });
|
||||
|
||||
expect(pair.verify(MESSAGE, signature)).toBe(true);
|
||||
expect(pair.verify(new Uint8Array(), signature)).toBe(false);
|
||||
expect(pair.verify(MESSAGE, signature, pair.publicKey)).toBe(true);
|
||||
expect(pair.verify(MESSAGE, signature, randomAsU8a())).toBe(false);
|
||||
expect(pair.verify(new Uint8Array(), signature, pair.publicKey)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ethereum', (): void => {
|
||||
const PHRASE = 'seed sock milk update focus rotate barely fade car face mechanic mercy';
|
||||
// combine mnemonic with derivation path
|
||||
const PHRASE = 'seed sock milk update focus rotate barely fade car face mechanic mercy' + '/m/44\'/60\'/0\'/0/0';
|
||||
const PRIV_KEY_ONE = '0x070dc3117300011918e26b02176945cc15c3d548cf49fd8418d97f93af699e46';
|
||||
const ETH_ADDRESS_ONE = '0x31ea8795EE32D782C8ff41a5C68Dcbf0F5B27f6d';
|
||||
|
||||
let keyring: Keyring;
|
||||
|
||||
beforeEach((): void => {
|
||||
keyring = new Keyring({ type: 'ethereum' });
|
||||
});
|
||||
|
||||
it('adds a pair with the correct address', (): void => {
|
||||
const pair = keyring.createFromUri(PHRASE);
|
||||
it('creates with dev phrase from the private key', (): void => {
|
||||
const pair = keyring.addFromSeed(hexToU8a(PRIV_KEY_ONE));
|
||||
|
||||
expect(pair.publicKey).toEqual(hexToU8a('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077'));
|
||||
expect(pair.address).toEqual('0x4119b2e6c3Cb618F4f0B93ac77f9BeeC7FF02887');
|
||||
expect(
|
||||
pair.address
|
||||
).toEqual(ETH_ADDRESS_ONE);
|
||||
});
|
||||
|
||||
it('encodes a pair toJSON', (): void => {
|
||||
it('creates with dev phrase from the private key in createFromUri', (): void => {
|
||||
const pair = keyring.createFromUri(PRIV_KEY_ONE);
|
||||
|
||||
expect(
|
||||
pair.address
|
||||
).toEqual(ETH_ADDRESS_ONE);
|
||||
});
|
||||
|
||||
it('creates with dev phrase with derivation path specified', (): void => {
|
||||
const pair = keyring.createFromUri(PHRASE);
|
||||
|
||||
expect(
|
||||
pair.address
|
||||
).toEqual(ETH_ADDRESS_ONE);
|
||||
});
|
||||
|
||||
it('creates with dev phrase with derivation path specified - addFromUri', (): void => {
|
||||
expect(
|
||||
keyring.addFromUri(PHRASE).address
|
||||
).toEqual(ETH_ADDRESS_ONE);
|
||||
});
|
||||
|
||||
it('encodes a pair toJSON (and decodes)', (): void => {
|
||||
const pair = keyring.createFromUri(PHRASE);
|
||||
const json = pair.toJson('password');
|
||||
|
||||
expect(json.address).toEqual('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077');
|
||||
expect(json.address).toEqual('0x0381351b1b46d2602b0992bb5d5531f9c1696b0812feb2534b6884adc47e2e1d8b'); // this is the public key (different from address for ethereum)
|
||||
expect(json.encoding).toEqual({
|
||||
content: ['pkcs8', 'ethereum'],
|
||||
type: ['scrypt', 'xsalsa20-poly1305'],
|
||||
version: '3'
|
||||
});
|
||||
|
||||
const newPair = keyring.createFromJson(json);
|
||||
|
||||
expect(newPair.publicKey).toEqual(pair.publicKey);
|
||||
expect(
|
||||
() => newPair.unlock('password')
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it('encodes a pair toJSON and back', (): void => {
|
||||
const pairOriginal = keyring.createFromUri(PHRASE);
|
||||
const json = pairOriginal.toJson('password');
|
||||
const pair = keyring.addFromJson(
|
||||
json
|
||||
);
|
||||
|
||||
expect(pair.address).toEqual(ETH_ADDRESS_ONE);
|
||||
|
||||
pair.decodePkcs8('password');
|
||||
|
||||
expect(pair.isLocked).toBe(false);
|
||||
expect(pair.address).toBe(ETH_ADDRESS_ONE);
|
||||
});
|
||||
|
||||
it('allows adding from JSON', (): void => {
|
||||
@@ -328,9 +405,13 @@ describe('keypair', (): void => {
|
||||
);
|
||||
|
||||
const signature = signer.sign(MESSAGE);
|
||||
const dummyPublic = verifier.publicKey.slice();
|
||||
|
||||
expect(verifier.verify(MESSAGE, signature)).toBe(true);
|
||||
expect(verifier.verify(new Uint8Array(), signature)).toBe(false);
|
||||
dummyPublic[dummyPublic.length - 1] = 0;
|
||||
|
||||
expect(verifier.verify(MESSAGE, signature, signer.publicKey)).toBe(true);
|
||||
expect(verifier.verify(MESSAGE, signature, dummyPublic)).toBe(false);
|
||||
expect(verifier.verify(new Uint8Array(), signature, signer.publicKey)).toBe(false);
|
||||
});
|
||||
|
||||
it('allows for signing/verification (withType)', (): void => {
|
||||
@@ -341,9 +422,37 @@ describe('keypair', (): void => {
|
||||
);
|
||||
|
||||
const signature = signer.sign(MESSAGE, { withType: true });
|
||||
const dummyPublic = verifier.publicKey.slice();
|
||||
|
||||
expect(verifier.verify(MESSAGE, signature)).toBe(true);
|
||||
expect(verifier.verify(new Uint8Array(), signature)).toBe(false);
|
||||
dummyPublic[dummyPublic.length - 1] = 0;
|
||||
|
||||
expect(verifier.verify(MESSAGE, signature, signer.publicKey)).toBe(true);
|
||||
expect(verifier.verify(MESSAGE, signature, dummyPublic)).toBe(false);
|
||||
expect(verifier.verify(new Uint8Array(), signature, signer.publicKey)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('raw pair add/create', (): void => {
|
||||
const json = JSON.parse('{"address":"5PjeoaQzCoYbSi42aQRKB3Sx18StCaEAzCbGEEbWbZyfKS3H","encoded":"JQUl8ZpoXv2OMkL9TPylLmcIye2cYhaS9INICbFgZTsAgAAAAQAAAAgAAAAr/0hJOOzokIdBG71TstigLABX9D5xGD7L37ySxtjDrVRg26LL90jLQ47quT9o3bq6ppXMVL6USk7Q4p3WU66bojTFuCDyhpYRhNbUqU6s0rD3S4bhv9lG+pG9vQ4eD5PVQUvxdANmJpYuDg45nrTmsMC5AHGdFGkHW/LHnkmbFid1cvPYkdiBoef5CIEdoly512pxMupVxnJWF1NT","encoding":{"content":["pkcs8","sr25519"],"type":["scrypt","xsalsa20-poly1305"],"version":"3"},"meta":{"name":"hello"}}') as KeyringPair$Json;
|
||||
const decoded = decodePair('1', base64Decode(json.encoded), json.encoding.type);
|
||||
const keyring = new Keyring({ ss58Format: 44 });
|
||||
|
||||
it('creates a pair from a private/public combo', (): void => {
|
||||
const pair = keyring.createFromPair(decoded, json.meta, 'sr25519');
|
||||
|
||||
expect(pair.address).toEqual('5PjeoaQzCoYbSi42aQRKB3Sx18StCaEAzCbGEEbWbZyfKS3H');
|
||||
expect(pair.isLocked).toEqual(false);
|
||||
expect(pair.meta.name).toEqual('hello');
|
||||
});
|
||||
|
||||
it('adds a pair from a private/public combo', (): void => {
|
||||
keyring.addFromPair(decoded, json.meta, 'sr25519');
|
||||
|
||||
const pair = keyring.getPairs()[0];
|
||||
|
||||
expect(pair.address).toEqual('5PjeoaQzCoYbSi42aQRKB3Sx18StCaEAzCbGEEbWbZyfKS3H');
|
||||
expect(pair.isLocked).toEqual(false);
|
||||
expect(pair.meta.name).toEqual('hello');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ import { Keyring } from './keyring';
|
||||
|
||||
export { decodeAddress, encodeAddress, setSS58Format } from '@polkadot/util-crypto';
|
||||
|
||||
export { packageInfo } from './packageInfo';
|
||||
|
||||
export default Keyring;
|
||||
|
||||
export { Keyring };
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// Copyright 2017-2021 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { EncryptedJsonEncoding } from '@polkadot/util-crypto/json/types';
|
||||
import type { Keypair, KeypairType } from '@polkadot/util-crypto/types';
|
||||
import type { KeyringInstance, KeyringOptions, KeyringPair, KeyringPair$Json, KeyringPair$JsonEncodingTypes, KeyringPair$Meta } from './types';
|
||||
import type { KeyringInstance, KeyringOptions, KeyringPair, KeyringPair$Json, KeyringPair$Meta } from './types';
|
||||
|
||||
import { assert, hexToU8a, isHex, isUndefined, stringToU8a } from '@polkadot/util';
|
||||
import { base64Decode, decodeAddress, encodeAddress, keyExtractSuri, keyFromPath, mnemonicToLegacySeed, mnemonicToMiniSecret, naclKeypairFromSeed as naclFromSeed, schnorrkelKeypairFromSeed as schnorrkelFromSeed, secp256k1KeypairFromSeed as secp256k1FromSeed } from '@polkadot/util-crypto';
|
||||
import { base64Decode, decodeAddress, encodeAddress, ethereumEncode, hdEthereum, keyExtractSuri, keyFromPath, mnemonicToLegacySeed, mnemonicToMiniSecret, naclKeypairFromSeed as naclFromSeed, schnorrkelKeypairFromSeed as schnorrkelFromSeed, secp256k1KeypairFromSeed as secp256k1FromSeed } from '@polkadot/util-crypto';
|
||||
|
||||
import { DEV_PHRASE } from './defaults';
|
||||
import { createPair } from './pair';
|
||||
@@ -46,7 +47,7 @@ export class Keyring implements KeyringInstance {
|
||||
constructor (options: KeyringOptions = {}) {
|
||||
options.type = options.type || 'ed25519';
|
||||
|
||||
assert(options && ['ecdsa', 'ethereum', 'ed25519', 'sr25519'].includes(options.type || 'undefined'), `Expected a keyring type of either 'ed25519', 'sr25519' or 'ecdsa', found '${options.type}`);
|
||||
assert(options && ['ecdsa', 'ethereum', 'ed25519', 'sr25519'].includes(options.type || 'undefined'), `Expected a keyring type of either 'ed25519', 'sr25519', 'ethereum' or 'ecdsa', found '${options.type}`);
|
||||
|
||||
this.#pairs = new Pairs();
|
||||
this.#ss58 = options.ss58Format;
|
||||
@@ -90,7 +91,7 @@ export 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.
|
||||
*/
|
||||
public addFromAddress (address: string | Uint8Array, meta: KeyringPair$Meta = {}, encoded: Uint8Array | null = null, type: KeypairType = this.type, ignoreChecksum?: boolean, encType?: KeyringPair$JsonEncodingTypes[]): KeyringPair {
|
||||
public addFromAddress (address: string | Uint8Array, meta: KeyringPair$Meta = {}, encoded: Uint8Array | null = null, type: KeypairType = this.type, ignoreChecksum?: boolean, encType?: EncryptedJsonEncoding[]): KeyringPair {
|
||||
const publicKey = this.decodeAddress(address, ignoreChecksum);
|
||||
|
||||
return this.addPair(createPair({ toSS58: this.encodeAddress, type }, { publicKey, secretKey: new Uint8Array() }, meta, encoded, encType));
|
||||
@@ -119,6 +120,16 @@ export class Keyring implements KeyringInstance {
|
||||
return this.addFromUri(mnemonic, meta, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @name addFromPair
|
||||
* @summary Stores an account created from an explicit publicKey/secreteKey combination
|
||||
*/
|
||||
public addFromPair (pair: Keypair, meta: KeyringPair$Meta = {}, type: KeypairType = this.type): KeyringPair {
|
||||
return this.addPair(
|
||||
this.createFromPair(pair, meta, type)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @name addFromSeed
|
||||
* @summary Stores an account, given seed data, as a Key/Value (public key, pair) in Keyring Pair Dictionary
|
||||
@@ -127,7 +138,9 @@ export class Keyring implements KeyringInstance {
|
||||
* `addPair` to store in a keyring pair dictionary the public key of the generated pair as a key and the pair as the associated value.
|
||||
*/
|
||||
public addFromSeed (seed: Uint8Array, meta: KeyringPair$Meta = {}, type: KeypairType = this.type): KeyringPair {
|
||||
return this.addPair(createPair({ toSS58: this.encodeAddress, type }, keypairFromSeed[type](seed), meta, null));
|
||||
return this.addPair(
|
||||
createPair({ toSS58: this.encodeAddress, type }, keypairFromSeed[type](seed), meta, null)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,12 +159,18 @@ export class Keyring implements KeyringInstance {
|
||||
* @description Creates a pair from a JSON keyfile
|
||||
*/
|
||||
public createFromJson ({ address, encoded, encoding: { content, type, version }, meta }: KeyringPair$Json, ignoreChecksum?: boolean): KeyringPair {
|
||||
assert(version !== '3' || content[0] === 'pkcs8', () => `Unable to decode non-pkcs8 type, [${content.join(',')}] found}`);
|
||||
|
||||
const cryptoType = version === '0' || !Array.isArray(content)
|
||||
? this.type
|
||||
: content[1];
|
||||
const encType = !Array.isArray(type)
|
||||
? [type]
|
||||
: type;
|
||||
|
||||
assert(['ed25519', 'sr25519', 'ecdsa', 'ethereum'].includes(cryptoType), `Unknown crypto type ${cryptoType}`);
|
||||
|
||||
// Here the address and publicKey are 32 bytes and isomorphic. This is why the address field needs to be the public key for ethereum type pairs
|
||||
const publicKey = isHex(address)
|
||||
? hexToU8a(address)
|
||||
: this.decodeAddress(address, ignoreChecksum);
|
||||
@@ -159,7 +178,15 @@ export class Keyring implements KeyringInstance {
|
||||
? hexToU8a(encoded)
|
||||
: base64Decode(encoded);
|
||||
|
||||
return createPair({ toSS58: this.encodeAddress, type: cryptoType }, { publicKey, secretKey: new Uint8Array() }, meta, decoded, encType);
|
||||
return createPair({ toSS58: this.encodeAddress, type: cryptoType as KeypairType }, { publicKey, secretKey: new Uint8Array() }, meta, decoded, encType);
|
||||
}
|
||||
|
||||
/**
|
||||
* @name createFromPair
|
||||
* @summary Creates a pair from an explicit publicKey/secreteKey combination
|
||||
*/
|
||||
public createFromPair (pair: Keypair, meta: KeyringPair$Meta = {}, type: KeypairType = this.type): KeyringPair {
|
||||
return createPair({ toSS58: this.encodeAddress, type }, pair, meta, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -172,18 +199,19 @@ export class Keyring implements KeyringInstance {
|
||||
const suri = _suri.startsWith('//')
|
||||
? `${DEV_PHRASE}${_suri}`
|
||||
: _suri;
|
||||
const { password, path, phrase } = keyExtractSuri(suri);
|
||||
const { derivePath, password, path, phrase } = keyExtractSuri(suri);
|
||||
let seed: Uint8Array;
|
||||
const isPhraseHex = isHex(phrase, 256);
|
||||
|
||||
if (isHex(phrase, 256)) {
|
||||
if (isPhraseHex) {
|
||||
seed = hexToU8a(phrase);
|
||||
} else {
|
||||
const str = phrase as string;
|
||||
const str = phrase;
|
||||
const parts = str.split(' ');
|
||||
|
||||
if ([12, 15, 18, 21, 24].includes(parts.length)) {
|
||||
seed = type === 'ethereum'
|
||||
? mnemonicToLegacySeed(phrase)
|
||||
? mnemonicToLegacySeed(phrase, '', false, 64)
|
||||
: mnemonicToMiniSecret(phrase, password);
|
||||
} else {
|
||||
assert(str.length <= 32, 'specified phrase is not a valid mnemonic and is invalid as a raw seed at > 32 bytes');
|
||||
@@ -192,8 +220,11 @@ export class Keyring implements KeyringInstance {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME Need to support Ethereum-type derivation paths
|
||||
const derived = keyFromPath(keypairFromSeed[type](seed), path, type);
|
||||
const derived = type === 'ethereum'
|
||||
? isPhraseHex
|
||||
? keypairFromSeed[type](seed) // for eth, if the private key is provided as suri, it must be derived only once
|
||||
: hdEthereum(seed, derivePath.substring(1))
|
||||
: keyFromPath(keypairFromSeed[type](seed), path, type);
|
||||
|
||||
return createPair({ toSS58: this.encodeAddress, type }, derived, meta, null);
|
||||
}
|
||||
@@ -203,7 +234,9 @@ export class Keyring implements KeyringInstance {
|
||||
* @description Encodes the input into an ss58 representation
|
||||
*/
|
||||
public encodeAddress = (address: Uint8Array | string, ss58Format?: number): string => {
|
||||
return encodeAddress(address, isUndefined(ss58Format) ? this.#ss58 : ss58Format);
|
||||
return this.type === 'ethereum'
|
||||
? ethereumEncode(address)
|
||||
: encodeAddress(address, isUndefined(ss58Format) ? this.#ss58 : ss58Format);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright 2017-2021 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Auto-generated by @polkadot/dev, do not edit
|
||||
|
||||
export const packageInfo = { name: '@polkadot/keyring', version: '6.0.3' };
|
||||
@@ -1,13 +1,13 @@
|
||||
// Copyright 2017-2021 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { KeyringPair$JsonEncodingTypes } from '../types';
|
||||
import type { EncryptedJsonEncoding } from '@polkadot/util-crypto/json/types';
|
||||
import type { PairInfo } from './types';
|
||||
|
||||
import { assert, stringToU8a, u8aFixLength } from '@polkadot/util';
|
||||
import { naclDecrypt, scryptEncode, scryptFromU8a } from '@polkadot/util-crypto';
|
||||
import { assert, isUndefined, u8aEq } from '@polkadot/util';
|
||||
import { jsonDecryptData } from '@polkadot/util-crypto';
|
||||
|
||||
import { ENCODING, NONCE_LENGTH, PKCS8_DIVIDER, PKCS8_HEADER, PUB_LENGTH, SCRYPT_LENGTH, SEC_LENGTH, SEED_LENGTH } from './defaults';
|
||||
import { PKCS8_DIVIDER, PKCS8_HEADER, PUB_LENGTH, SEC_LENGTH, SEED_LENGTH } from './defaults';
|
||||
|
||||
const SEED_OFFSET = PKCS8_HEADER.length;
|
||||
|
||||
@@ -15,59 +15,33 @@ type DecodeResult = PairInfo & {
|
||||
secretKey: Uint8Array;
|
||||
};
|
||||
|
||||
function decodePkcs8 (encoded: Uint8Array): DecodeResult {
|
||||
const header = encoded.subarray(0, PKCS8_HEADER.length);
|
||||
export function decodePair (passphrase?: string, encrypted?: Uint8Array | null, _encType?: EncryptedJsonEncoding | EncryptedJsonEncoding[]): DecodeResult {
|
||||
const encType = Array.isArray(_encType) || isUndefined(_encType)
|
||||
? _encType
|
||||
: [_encType];
|
||||
const decrypted = jsonDecryptData(encrypted, passphrase, encType);
|
||||
const header = decrypted.subarray(0, PKCS8_HEADER.length);
|
||||
|
||||
assert(header.toString() === PKCS8_HEADER.toString(), 'Invalid Pkcs8 header found in body');
|
||||
assert(u8aEq(header, PKCS8_HEADER), 'Invalid Pkcs8 header found in body');
|
||||
|
||||
let secretKey = encoded.subarray(SEED_OFFSET, SEED_OFFSET + SEC_LENGTH);
|
||||
let secretKey = decrypted.subarray(SEED_OFFSET, SEED_OFFSET + SEC_LENGTH);
|
||||
let divOffset = SEED_OFFSET + SEC_LENGTH;
|
||||
let divider = encoded.subarray(divOffset, divOffset + PKCS8_DIVIDER.length);
|
||||
let divider = decrypted.subarray(divOffset, divOffset + PKCS8_DIVIDER.length);
|
||||
|
||||
// old-style, we have the seed here
|
||||
if (divider.toString() !== PKCS8_DIVIDER.toString()) {
|
||||
if (!u8aEq(divider, PKCS8_DIVIDER)) {
|
||||
divOffset = SEED_OFFSET + SEED_LENGTH;
|
||||
secretKey = encoded.subarray(SEED_OFFSET, divOffset);
|
||||
divider = encoded.subarray(divOffset, divOffset + PKCS8_DIVIDER.length);
|
||||
secretKey = decrypted.subarray(SEED_OFFSET, divOffset);
|
||||
divider = decrypted.subarray(divOffset, divOffset + PKCS8_DIVIDER.length);
|
||||
|
||||
assert(u8aEq(divider, PKCS8_DIVIDER), 'Invalid Pkcs8 divider found in body');
|
||||
}
|
||||
|
||||
assert(divider.toString() === PKCS8_DIVIDER.toString(), 'Invalid Pkcs8 divider found in body');
|
||||
|
||||
const pubOffset = divOffset + PKCS8_DIVIDER.length;
|
||||
const publicKey = encoded.subarray(pubOffset, pubOffset + PUB_LENGTH);
|
||||
const publicKey = decrypted.subarray(pubOffset, pubOffset + PUB_LENGTH);
|
||||
|
||||
return {
|
||||
publicKey,
|
||||
secretKey
|
||||
};
|
||||
}
|
||||
|
||||
export function decodePair (passphrase?: string, encrypted?: Uint8Array | null, encType: KeyringPair$JsonEncodingTypes[] = ENCODING): DecodeResult {
|
||||
assert(encrypted, 'No encrypted data available to decode');
|
||||
assert(passphrase || !encType.includes('xsalsa20-poly1305'), 'Password required to decode encypted data');
|
||||
|
||||
let encoded: Uint8Array | null = encrypted;
|
||||
|
||||
if (passphrase) {
|
||||
let password: Uint8Array;
|
||||
|
||||
if (encType.includes('scrypt')) {
|
||||
const { params, salt } = scryptFromU8a(encrypted);
|
||||
|
||||
password = scryptEncode(passphrase, salt, params).password;
|
||||
encrypted = encrypted.subarray(SCRYPT_LENGTH);
|
||||
} else {
|
||||
password = stringToU8a(passphrase);
|
||||
}
|
||||
|
||||
encoded = naclDecrypt(
|
||||
encrypted.subarray(NONCE_LENGTH),
|
||||
encrypted.subarray(0, NONCE_LENGTH),
|
||||
u8aFixLength(password, 256, true)
|
||||
);
|
||||
}
|
||||
|
||||
assert(encoded, 'Unable to decode using the supplied passphrase');
|
||||
|
||||
return decodePkcs8(encoded);
|
||||
}
|
||||
|
||||
@@ -1,26 +1,9 @@
|
||||
// Copyright 2017-2021 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { KeyringPair$JsonEncodingTypes } from '../types';
|
||||
|
||||
const ENCODING: KeyringPair$JsonEncodingTypes[] = ['scrypt', 'xsalsa20-poly1305'];
|
||||
const NONCE_LENGTH = 24;
|
||||
const PKCS8_DIVIDER = new Uint8Array([161, 35, 3, 33, 0]);
|
||||
const PKCS8_HEADER = new Uint8Array([48, 83, 2, 1, 1, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32]);
|
||||
const PUB_LENGTH = 32;
|
||||
const SALT_LENGTH = 32;
|
||||
const SEC_LENGTH = 64;
|
||||
const SEED_LENGTH = 32;
|
||||
const SCRYPT_LENGTH = SALT_LENGTH + (3 * 4);
|
||||
|
||||
export {
|
||||
ENCODING,
|
||||
NONCE_LENGTH,
|
||||
PKCS8_DIVIDER,
|
||||
PKCS8_HEADER,
|
||||
PUB_LENGTH,
|
||||
SALT_LENGTH,
|
||||
SEC_LENGTH,
|
||||
SEED_LENGTH,
|
||||
SCRYPT_LENGTH
|
||||
};
|
||||
export const PKCS8_DIVIDER = new Uint8Array([161, 35, 3, 33, 0]);
|
||||
export const PKCS8_HEADER = new Uint8Array([48, 83, 2, 1, 1, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32]);
|
||||
export const PUB_LENGTH = 32;
|
||||
export const SALT_LENGTH = 32;
|
||||
export const SEC_LENGTH = 64;
|
||||
export const SEED_LENGTH = 32;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// Copyright 2017-2021 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { NONCE_LENGTH, SCRYPT_LENGTH } from '@polkadot/util-crypto/json/constants';
|
||||
|
||||
import { createTestPairs } from '../testingPairs';
|
||||
import { NONCE_LENGTH, PKCS8_DIVIDER, PKCS8_HEADER, PUB_LENGTH, SCRYPT_LENGTH, SEC_LENGTH } from './defaults';
|
||||
import { PKCS8_DIVIDER, PKCS8_HEADER, PUB_LENGTH, SEC_LENGTH } from './defaults';
|
||||
|
||||
const PKCS8_LENGTH = PKCS8_DIVIDER.length + PKCS8_HEADER.length + PUB_LENGTH + SEC_LENGTH;
|
||||
const ENCODED_LENGTH = 16 + PKCS8_LENGTH + NONCE_LENGTH + SCRYPT_LENGTH;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// Copyright 2017-2021 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { hexToU8a } from '@polkadot/util';
|
||||
import { hexToU8a, u8aToHex } from '@polkadot/util';
|
||||
import { cryptoWaitReady, encodeAddress as toSS58, setSS58Format } from '@polkadot/util-crypto';
|
||||
|
||||
import { PAIRS } from '../testing';
|
||||
import { PAIRSSR25519 } from '../testing';
|
||||
import { createTestPairs } from '../testingPairs';
|
||||
import { createPair } from '.';
|
||||
|
||||
@@ -42,16 +42,28 @@ describe('pair', (): void => {
|
||||
expect(
|
||||
keyring.alice.verify(
|
||||
new Uint8Array([0x61, 0x62, 0x63, 0x64]),
|
||||
SIGNATURE
|
||||
SIGNATURE,
|
||||
keyring.alice.publicKey
|
||||
)
|
||||
).toEqual(true);
|
||||
});
|
||||
|
||||
it('fails a correctly signed message (signer changed)', (): void => {
|
||||
expect(
|
||||
keyring.alice.verify(
|
||||
new Uint8Array([0x61, 0x62, 0x63, 0x64, 0x65]),
|
||||
SIGNATURE,
|
||||
keyring.bob.publicKey
|
||||
)
|
||||
).toEqual(false);
|
||||
});
|
||||
|
||||
it('fails a correctly signed message (message changed)', (): void => {
|
||||
expect(
|
||||
keyring.alice.verify(
|
||||
new Uint8Array([0x61, 0x62, 0x63, 0x64, 0x65]),
|
||||
SIGNATURE
|
||||
SIGNATURE,
|
||||
keyring.alice.publicKey
|
||||
)
|
||||
).toEqual(false);
|
||||
});
|
||||
@@ -62,11 +74,24 @@ describe('pair', (): void => {
|
||||
expect(
|
||||
keyring.alice.vrfVerify(
|
||||
message,
|
||||
keyring.alice.vrfSign(message)
|
||||
keyring.alice.vrfSign(message),
|
||||
keyring.alice.publicKey
|
||||
)
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('fails vrf sign and verify (publicKey changed)', (): void => {
|
||||
const message = new Uint8Array([0x61, 0x62, 0x63, 0x64, 0x65]);
|
||||
|
||||
expect(
|
||||
keyring.alice.vrfVerify(
|
||||
message,
|
||||
keyring.alice.vrfSign(message),
|
||||
keyring.bob.publicKey
|
||||
)
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('allows setting/getting of meta', (): void => {
|
||||
keyring.bob.setMeta({ foo: 'bar', something: 'else' });
|
||||
|
||||
@@ -78,15 +103,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);
|
||||
});
|
||||
@@ -103,11 +124,11 @@ describe('pair', (): void => {
|
||||
});
|
||||
|
||||
it('allows derivation on the pair', (): void => {
|
||||
const alice = createPair({ toSS58, type: 'sr25519' }, { publicKey: PAIRS[0].publicKey, secretKey: PAIRS[0].secretKey }, {});
|
||||
const alice = createPair({ toSS58, type: 'sr25519' }, { publicKey: PAIRSSR25519[0].publicKey, secretKey: PAIRSSR25519[0].secretKey }, {});
|
||||
const stash = alice.derive('//stash');
|
||||
const soft = alice.derive('//funding/0');
|
||||
|
||||
expect(stash.publicKey).toEqual(PAIRS[1].publicKey);
|
||||
expect(stash.publicKey).toEqual(PAIRSSR25519[1].publicKey);
|
||||
expect(soft.address).toEqual('5ECQNn7UueWHPFda5qUi4fTmTtyCnPvGnuoyVVSj5CboJh9J');
|
||||
});
|
||||
|
||||
@@ -121,11 +142,43 @@ describe('pair', (): void => {
|
||||
});
|
||||
|
||||
describe('ethereum', (): void => {
|
||||
const PUBLICDERIVED = new Uint8Array([
|
||||
3, 129, 53, 27, 27, 70, 210, 96,
|
||||
43, 9, 146, 187, 93, 85, 49, 249,
|
||||
193, 105, 107, 8, 18, 254, 178, 83,
|
||||
75, 104, 132, 173, 196, 126, 46, 29,
|
||||
139
|
||||
]);
|
||||
const SECRETDERIVED = new Uint8Array([
|
||||
7, 13, 195, 17, 115, 0, 1, 25,
|
||||
24, 226, 107, 2, 23, 105, 69, 204,
|
||||
21, 195, 213, 72, 207, 73, 253, 132,
|
||||
24, 217, 127, 147, 175, 105, 158, 70
|
||||
]);
|
||||
|
||||
it('has a valid address from a known public', (): void => {
|
||||
const pair = createPair({ toSS58, type: 'ethereum' }, { publicKey: hexToU8a('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077') });
|
||||
|
||||
expect(pair.address).toEqual('0x4119b2e6c3Cb618F4f0B93ac77f9BeeC7FF02887');
|
||||
expect(pair.addressRaw).toEqual(hexToU8a('0x4119b2e6c3Cb618F4f0B93ac77f9BeeC7FF02887'));
|
||||
});
|
||||
|
||||
it('converts to json', (): void => {
|
||||
const pair = createPair({ toSS58, type: 'ethereum' }, { publicKey: PUBLICDERIVED, secretKey: SECRETDERIVED });
|
||||
const json = pair.toJson('password');
|
||||
|
||||
expect(json.encoding).toEqual({
|
||||
content: ['pkcs8', 'ethereum'],
|
||||
type: ['scrypt', 'xsalsa20-poly1305'],
|
||||
version: '3'
|
||||
});
|
||||
expect(json.address).toEqual(u8aToHex(PUBLICDERIVED));
|
||||
});
|
||||
|
||||
it('has Gerald as test address for Ethereum type parachains', (): void => {
|
||||
const keyringEthereum = createTestPairs({ type: 'ethereum' }, false);
|
||||
|
||||
expect(keyringEthereum.Gerald.address).toEqual('0x6Be02d1d3665660d22FF9624b7BE0551ee1Ac91b');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// Copyright 2017-2021 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { EncryptedJsonEncoding } from '@polkadot/util-crypto/json/types';
|
||||
import type { Keypair, KeypairType } from '@polkadot/util-crypto/types';
|
||||
import type { KeyringPair, KeyringPair$Json, KeyringPair$JsonEncodingTypes, KeyringPair$Meta, SignOptions } from '../types';
|
||||
import type { KeyringPair, KeyringPair$Json, 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 +48,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));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,7 +88,7 @@ function isLocked (secretKey?: Uint8Array): secretKey is undefined {
|
||||
* an `encoded` property that is assigned with the encoded public key in hex format, and an `encoding`
|
||||
* property that indicates whether the public key value of the `encoded` property is encoded or not.
|
||||
*/
|
||||
export function createPair ({ toSS58, type }: Setup, { publicKey, secretKey }: PairInfo, meta: KeyringPair$Meta = {}, encoded: Uint8Array | null = null, encTypes?: KeyringPair$JsonEncodingTypes[]): KeyringPair {
|
||||
export function createPair ({ toSS58, type }: Setup, { publicKey, secretKey }: PairInfo, meta: KeyringPair$Meta = {}, encoded: Uint8Array | null = null, encTypes?: EncryptedJsonEncoding[]): KeyringPair {
|
||||
const decodePkcs8 = (passphrase?: string, userEncoded?: Uint8Array | null): void => {
|
||||
const decoded = decodePair(passphrase, userEncoded || encoded, encTypes);
|
||||
|
||||
@@ -145,6 +146,7 @@ export function createPair ({ toSS58, type }: Setup, { publicKey, secretKey }: P
|
||||
// eslint-disable-next-line sort-keys
|
||||
decodePkcs8,
|
||||
derive: (suri: string, meta?: KeyringPair$Meta): KeyringPair => {
|
||||
assert(type !== 'ethereum', 'Unable to derive on this keypair');
|
||||
assert(!isLocked(secretKey), 'Cannot derive on a locked keypair');
|
||||
|
||||
const { path } = keyExtractPath(suri);
|
||||
@@ -152,53 +154,61 @@ 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 => {
|
||||
// NOTE: For ecdsa and ethereum, the publicKey cannot be extracted from the address. For these
|
||||
// pass the hex-encoded publicKey through to the address portion of the JSON (before decoding)
|
||||
const address = ['ecdsa', 'ethereum'].includes(type)
|
||||
? u8aToHex(secp256k1Compress(publicKey))
|
||||
: encodeAddress();
|
||||
|
||||
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 => {
|
||||
unlock: (passphrase?: string): void => {
|
||||
return decodePkcs8(passphrase);
|
||||
},
|
||||
verify: (message: string | Uint8Array, signature: Uint8Array, _signerPublic: string | Uint8Array): boolean => {
|
||||
return signatureVerify(message, signature, TYPE_ADDRESS[type](u8aToU8a(_signerPublic))).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'
|
||||
? 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))))
|
||||
)
|
||||
vrfVerify: (message: string | Uint8Array, vrfResult: Uint8Array, _signerPublic: Uint8Array | string, context?: string | Uint8Array, extra?: string | Uint8Array): boolean => {
|
||||
const signerPublic = TYPE_ADDRESS[type](u8aToU8a(_signerPublic));
|
||||
|
||||
if (type === 'sr25519') {
|
||||
return schnorrkelVrfVerify(message, vrfResult, publicKey, context, extra);
|
||||
}
|
||||
|
||||
const result = signatureVerify(message, u8aConcat(TYPE_PREFIX[type], vrfResult.subarray(32)), signerPublic);
|
||||
|
||||
return result.isValid && u8aEq(vrfResult.subarray(0, 32), vrfHash(vrfResult.subarray(32), context, extra));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -52,6 +52,9 @@ export function nobody (): KeyringPair {
|
||||
json,
|
||||
type: 'ed25519',
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
unlock: (passphrase?: string): void =>
|
||||
undefined,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
verify: (message: Uint8Array, signature: Uint8Array): boolean =>
|
||||
false,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
|
||||
@@ -2,34 +2,19 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { KeypairType } from '@polkadot/util-crypto/types';
|
||||
import type { KeyringPair$Json, KeyringPair$JsonEncodingTypes, KeyringPair$Meta } from '../types';
|
||||
import type { KeyringPair$Json, KeyringPair$Meta } from '../types';
|
||||
|
||||
import { base64Encode } from '@polkadot/util-crypto';
|
||||
|
||||
import { ENCODING } from './defaults';
|
||||
import { jsonEncryptFormat } from '@polkadot/util-crypto';
|
||||
|
||||
interface PairStateJson {
|
||||
address: string;
|
||||
meta: KeyringPair$Meta;
|
||||
}
|
||||
|
||||
// version 2 - nonce, encoded (previous)
|
||||
// version 3 - salt, nonce, encoded
|
||||
const VERSION = '3';
|
||||
|
||||
const ENC_NONE: KeyringPair$JsonEncodingTypes[] = ['none'];
|
||||
|
||||
export function pairToJson (type: KeypairType, { address, meta }: PairStateJson, encoded: Uint8Array, isEncrypted: boolean): KeyringPair$Json {
|
||||
return {
|
||||
...jsonEncryptFormat(encoded, ['pkcs8', type], isEncrypted),
|
||||
address,
|
||||
encoded: base64Encode(encoded),
|
||||
encoding: {
|
||||
content: ['pkcs8', type],
|
||||
type: isEncrypted
|
||||
? ENCODING
|
||||
: ENC_NONE,
|
||||
version: VERSION
|
||||
},
|
||||
meta
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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, ecdsa.publicKey, CONTEXT)).toEqual(true);
|
||||
expect(ecdsa.vrfVerify(MESSAGE, sig2, ecdsa.publicKey, 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, ed25519.publicKey, CONTEXT)).toEqual(true);
|
||||
expect(ed25519.vrfVerify(MESSAGE, sig2, ed25519.publicKey, 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, sr25519.publicKey, CONTEXT)).toEqual(true);
|
||||
expect(sr25519.vrfVerify(MESSAGE, sig2, sr25519.publicKey, CONTEXT)).toEqual(true);
|
||||
});
|
||||
});
|
||||
@@ -18,6 +18,13 @@ const TESTS = {
|
||||
uri: `${PHRASE}//Alice`
|
||||
}
|
||||
],
|
||||
ethereum: [
|
||||
{
|
||||
pk: '0x0381351b1b46d2602b0992bb5d5531f9c1696b0812feb2534b6884adc47e2e1d8b',
|
||||
ss: '0x31ea8795EE32D782C8ff41a5C68Dcbf0F5B27f6d',
|
||||
uri: "seed sock milk update focus rotate barely fade car face mechanic mercy/m/44'/60'/0'/0/0"
|
||||
}
|
||||
],
|
||||
sr25519: [
|
||||
{
|
||||
pk: '0x46ebddef8cd9bb167dc30878d7113b7e168e6f0646beffd77d69d39bad76b47a',
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import type { KeyringInstance, KeyringOptions } from './types';
|
||||
|
||||
import { hexToU8a } from '@polkadot/util';
|
||||
import { KeypairType } from '@polkadot/util-crypto/types';
|
||||
|
||||
import { createPair } from './pair';
|
||||
import Keyring from '.';
|
||||
@@ -13,53 +14,118 @@ interface PairDef {
|
||||
publicKey: Uint8Array;
|
||||
seed: string;
|
||||
secretKey?: Uint8Array;
|
||||
type?: 'ed25519' | 'sr25519';
|
||||
type: KeypairType
|
||||
}
|
||||
|
||||
// NOTE This is not great since we have the secretKey here explicitly, but a testing
|
||||
// keyring is for testing - what happens is that in most cases the keyring is initialises
|
||||
// before anything else. Since the sr25519 crypto is async, this creates problems with
|
||||
// adding the keys when only the keyring is used.
|
||||
export const PAIRS: PairDef[] = [
|
||||
export const PAIRSSR25519: PairDef[] = [
|
||||
{
|
||||
publicKey: hexToU8a('0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d'),
|
||||
secretKey: hexToU8a('0x98319d4ff8a9508c4bb0cf0b5a78d760a0b2082c02775e6e82370816fedfff48925a225d97aa00682d6a59b95b18780c10d7032336e88f3442b42361f4a66011'),
|
||||
seed: 'Alice'
|
||||
seed: 'Alice',
|
||||
type: 'sr25519'
|
||||
},
|
||||
{
|
||||
publicKey: hexToU8a('0xbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f'),
|
||||
secretKey: hexToU8a('0xe8da6c9d810e020f5e3c7f5af2dea314cbeaa0d72bc6421e92c0808a0c584a6046ab28e97c3ffc77fe12b5a4d37e8cd4afbfebbf2391ffc7cb07c0f38c023efd'),
|
||||
seed: 'Alice//stash'
|
||||
seed: 'Alice//stash',
|
||||
type: 'sr25519'
|
||||
},
|
||||
{
|
||||
publicKey: hexToU8a('0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48'),
|
||||
secretKey: hexToU8a('0x081ff694633e255136bdb456c20a5fc8fed21f8b964c11bb17ff534ce80ebd5941ae88f85d0c1bfc37be41c904e1dfc01de8c8067b0d6d5df25dd1ac0894a325'),
|
||||
seed: 'Bob'
|
||||
seed: 'Bob',
|
||||
type: 'sr25519'
|
||||
},
|
||||
{
|
||||
publicKey: hexToU8a('0xfe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e'),
|
||||
secretKey: hexToU8a('0xc006507cdfc267a21532394c49ca9b754ca71de21e15a1cdf807c7ceab6d0b6c3ed408d9d35311540dcd54931933e67cf1ea10d46f75408f82b789d9bd212fde'),
|
||||
seed: 'Bob//stash'
|
||||
seed: 'Bob//stash',
|
||||
type: 'sr25519'
|
||||
},
|
||||
{
|
||||
publicKey: hexToU8a('0x90b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22'),
|
||||
secretKey: hexToU8a('0xa8f2d83016052e5d6d77b2f6fd5d59418922a09024cda701b3c34369ec43a7668faf12ff39cd4e5d92bb773972f41a7a5279ebc2ed92264bed8f47d344f8f18c'),
|
||||
seed: 'Charlie'
|
||||
seed: 'Charlie',
|
||||
type: 'sr25519'
|
||||
},
|
||||
{
|
||||
publicKey: hexToU8a('0x306721211d5404bd9da88e0204360a1a9ab8b87c66c1bc2fcdd37f3c2222cc20'),
|
||||
secretKey: hexToU8a('0x20e05482ca4677e0edbc58ae9a3a59f6ed3b1a9484ba17e64d6fe8688b2b7b5d108c4487b9323b98b11fe36cb301b084e920f7b7895536809a6d62a451b25568'),
|
||||
seed: 'Dave'
|
||||
seed: 'Dave',
|
||||
type: 'sr25519'
|
||||
},
|
||||
{
|
||||
publicKey: hexToU8a('0xe659a7a1628cdd93febc04a4e0646ea20e9f5f0ce097d9a05290d4a9e054df4e'),
|
||||
secretKey: hexToU8a('0x683576abfd5dc35273e4264c23095a1bf21c14517bece57c7f0cc5c0ed4ce06a3dbf386b7828f348abe15d76973a72009e6ef86a5c91db2990cb36bb657c6587'),
|
||||
seed: 'Eve'
|
||||
seed: 'Eve',
|
||||
type: 'sr25519'
|
||||
},
|
||||
{
|
||||
publicKey: hexToU8a('0x1cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c'),
|
||||
secretKey: hexToU8a('0xb835c20f450079cf4f513900ae9faf8df06ad86c681884122c752a4b2bf74d4303e4f21bc6cc62bb4eeed5a9cce642c25e2d2ac1464093b50f6196d78e3a7426'),
|
||||
seed: 'Ferdie'
|
||||
seed: 'Ferdie',
|
||||
type: 'sr25519'
|
||||
}
|
||||
];
|
||||
|
||||
export const PAIRSETHEREUM: PairDef[] = [
|
||||
{
|
||||
name: 'Alith',
|
||||
publicKey: hexToU8a('0x02509540919faacf9ab52146c9aa40db68172d83777250b28e4679176e49ccdd9f'),
|
||||
secretKey: hexToU8a('0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133'),
|
||||
seed: "m/44'/40'/0'/0",
|
||||
type: 'ethereum'
|
||||
},
|
||||
{
|
||||
name: 'Baltathar',
|
||||
publicKey: hexToU8a('0x033bc19e36ff1673910575b6727a974a9abd80c9a875d41ab3e2648dbfb9e4b518'),
|
||||
secretKey: hexToU8a('0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b'),
|
||||
seed: "m/44'/40'/0'/0",
|
||||
type: 'ethereum'
|
||||
},
|
||||
{
|
||||
name: 'Charleth',
|
||||
publicKey: hexToU8a('0x0234637bdc0e89b5d46543bcbf8edff329d2702bc995e27e9af4b1ba009a3c2a5e'),
|
||||
secretKey: hexToU8a('0x0b6e18cafb6ed99687ec547bd28139cafdd2bffe70e6b688025de6b445aa5c5b'),
|
||||
seed: "m/44'/40'/0'/0",
|
||||
type: 'ethereum'
|
||||
},
|
||||
{
|
||||
name: 'Dorothy',
|
||||
publicKey: hexToU8a('0x02a00d60b2b408c2a14c5d70cdd2c205db8985ef737a7e55ad20ea32cc9e7c417c'),
|
||||
secretKey: hexToU8a('0x39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68'),
|
||||
seed: "m/44'/40'/0'/0",
|
||||
type: 'ethereum'
|
||||
},
|
||||
{
|
||||
name: 'Ethan',
|
||||
publicKey: hexToU8a('0x025cdc005b752651cd3f728fb9192182acb3a9c89e19072cbd5b03f3ee1f1b3ffa'),
|
||||
secretKey: hexToU8a('0x7dce9bc8babb68fec1409be38c8e1a52650206a7ed90ff956ae8a6d15eeaaef4'),
|
||||
seed: "m/44'/40'/0'/0",
|
||||
type: 'ethereum'
|
||||
},
|
||||
{
|
||||
name: 'Faith',
|
||||
publicKey: hexToU8a('0x037964b6c9d546da4646ada28a99e34acaa1d14e7aba861a9055f9bd200c8abf74'),
|
||||
secretKey: hexToU8a('0xb9d2ea9a615f3165812e8d44de0d24da9bbd164b65c4f0573e1ce2c8dbd9c8df'),
|
||||
seed: "m/44'/40'/0'/0",
|
||||
type: 'ethereum'
|
||||
},
|
||||
// Gerald will be removed when moonbeam is done changing dev addresses (should def be done by end of march 21)
|
||||
{
|
||||
name: 'Gerald',
|
||||
publicKey: new Uint8Array([3, 98, 79, 114, 14, 174, 103, 106,
|
||||
4, 17, 22, 49, 201, 202, 51, 140,
|
||||
17, 208, 245, 168, 14, 228, 34, 16,
|
||||
198, 190, 114, 152, 60, 235, 98, 15,
|
||||
191]),
|
||||
secretKey: hexToU8a('0x99b3c12287537e38c90a9219d4cb074a89a16e9cdb20bf85728ebd97c343e342'),
|
||||
seed: "m/44'/40'/0'/0",
|
||||
type: 'ethereum'
|
||||
}
|
||||
];
|
||||
|
||||
@@ -71,13 +137,13 @@ export const PAIRS: PairDef[] = [
|
||||
*/
|
||||
export function createTestKeyring (options: KeyringOptions = {}, isDerived = true): KeyringInstance {
|
||||
const keyring = new Keyring(options);
|
||||
const pairs: PairDef[] = (options.type && options.type === 'ethereum') ? PAIRSETHEREUM : PAIRSSR25519;
|
||||
|
||||
PAIRS.forEach(({ name, publicKey, secretKey, seed, type = 'sr25519' }): void => {
|
||||
pairs.forEach(({ name, publicKey, secretKey, seed, type }): void => {
|
||||
const meta = {
|
||||
isTesting: true,
|
||||
name: name || seed.replace('//', '_').toLowerCase()
|
||||
};
|
||||
|
||||
const pair = !isDerived && !name
|
||||
? keyring.addFromUri(seed, meta, options.type)
|
||||
: keyring.addPair(
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
// Copyright 2017-2021 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { u8aToHex } from '@polkadot/util';
|
||||
import { cryptoWaitReady } from '@polkadot/util-crypto';
|
||||
|
||||
import { createTestPairs } from './testingPairs';
|
||||
import Keyring from '.';
|
||||
|
||||
describe('testingPairs', (): void => {
|
||||
beforeEach(async (): Promise<void> => {
|
||||
@@ -27,4 +29,41 @@ describe('testingPairs', (): void => {
|
||||
createTestPairs({ type: 'ed25519' }).alice.address
|
||||
).toEqual('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY');
|
||||
});
|
||||
|
||||
it('has the correct address for Alith (Eth)', (): void => {
|
||||
expect(
|
||||
createTestPairs({ type: 'ethereum' }).Alith.address
|
||||
).toEqual('0xf24FF3a9CF04c71Dbc94D0b566f7A27B94566cac');
|
||||
});
|
||||
|
||||
it('has the correct address for Alith (Eth), same as obtained by createFromUri', (): void => {
|
||||
const keyring = new Keyring({ type: 'ethereum' });
|
||||
const pair = keyring.createFromUri('0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133');
|
||||
|
||||
expect(
|
||||
pair && pair.address
|
||||
).toEqual('0xf24FF3a9CF04c71Dbc94D0b566f7A27B94566cac');
|
||||
});
|
||||
|
||||
it('checks eth test addresses', (): void => {
|
||||
const ring = createTestPairs({ type: 'ethereum' });
|
||||
const keyring = new Keyring({ type: 'ethereum' });
|
||||
// priv keys generated by ganache-cli --mnemonic "bottom drive obey lake curtain smoke basket hold race lonely fit walk"
|
||||
const privKeys: string[] = ['0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133',
|
||||
'0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b',
|
||||
'0x0b6e18cafb6ed99687ec547bd28139cafdd2bffe70e6b688025de6b445aa5c5b',
|
||||
'0x39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68',
|
||||
'0x7dce9bc8babb68fec1409be38c8e1a52650206a7ed90ff956ae8a6d15eeaaef4',
|
||||
'0xb9d2ea9a615f3165812e8d44de0d24da9bbd164b65c4f0573e1ce2c8dbd9c8df',
|
||||
'0x96b8a38e12e1a31dee1eab2fffdf9d9990045f5b37e44d8cc27766ef294acf18',
|
||||
'0x0d6dcaaef49272a5411896be8ad16c01c35d6f8c18873387b71fbc734759b0ab',
|
||||
'0x4c42532034540267bf568198ccec4cb822a025da542861fcb146a5fab6433ff8',
|
||||
'0x94c49300a58d576011096bcb006aa06f5a91b34b4383891e8029c21dc39fbb8b'];
|
||||
|
||||
delete ring.nobody;
|
||||
|
||||
Object.keys(ring).forEach((testKeyring, i) => {
|
||||
if (i < 6) { expect(u8aToHex(ring[testKeyring].publicKey)).toEqual(u8aToHex(keyring.createFromUri(privKeys[i]).publicKey)); }
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Prefix } from '@polkadot/util-crypto/address/types';
|
||||
import type { KeypairType } from '@polkadot/util-crypto/types';
|
||||
import type { EncryptedJson } from '@polkadot/util-crypto/json/types';
|
||||
import type { Keypair, KeypairType } from '@polkadot/util-crypto/types';
|
||||
|
||||
export interface KeyringOptions {
|
||||
ss58Format?: Prefix;
|
||||
@@ -11,20 +12,8 @@ export interface KeyringOptions {
|
||||
|
||||
export type KeyringPair$Meta = Record<string, unknown>;
|
||||
|
||||
export type KeyringPair$JsonVersion = '0' | '1' | '2' | '3';
|
||||
|
||||
export type KeyringPair$JsonEncodingTypes = 'none' | 'scrypt' | 'xsalsa20-poly1305';
|
||||
|
||||
export interface KeyringPair$JsonEncoding {
|
||||
content: ['pkcs8', KeypairType];
|
||||
type: KeyringPair$JsonEncodingTypes | KeyringPair$JsonEncodingTypes[];
|
||||
version: KeyringPair$JsonVersion;
|
||||
}
|
||||
|
||||
export interface KeyringPair$Json {
|
||||
export interface KeyringPair$Json extends EncryptedJson {
|
||||
address: string;
|
||||
encoded: string;
|
||||
encoding: KeyringPair$JsonEncoding;
|
||||
meta: KeyringPair$Meta;
|
||||
}
|
||||
|
||||
@@ -40,16 +29,17 @@ export interface KeyringPair {
|
||||
readonly publicKey: Uint8Array;
|
||||
readonly type: KeypairType;
|
||||
|
||||
decodePkcs8: (passphrase?: string, encoded?: Uint8Array) => void;
|
||||
derive: (suri: string, meta?: KeyringPair$Meta) => KeyringPair;
|
||||
encodePkcs8: (passphrase?: string) => Uint8Array;
|
||||
lock: () => void;
|
||||
setMeta: (meta: KeyringPair$Meta) => void;
|
||||
sign (message: Uint8Array, options?: SignOptions): Uint8Array;
|
||||
decodePkcs8 (passphrase?: string, encoded?: Uint8Array): void;
|
||||
derive (suri: string, meta?: KeyringPair$Meta): KeyringPair;
|
||||
encodePkcs8 (passphrase?: string): Uint8Array;
|
||||
lock (): void;
|
||||
setMeta (meta: KeyringPair$Meta): void;
|
||||
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;
|
||||
unlock (passphrase?: string): void;
|
||||
verify (message: string | Uint8Array, signature: Uint8Array, signerPublic: string | Uint8Array): boolean;
|
||||
vrfSign (message: string | Uint8Array, context?: string | Uint8Array, extra?: string | Uint8Array): Uint8Array;
|
||||
vrfVerify (message: string | Uint8Array, vrfResult: Uint8Array, signerPublic: Uint8Array | string, context?: string | Uint8Array, extra?: string | Uint8Array): boolean;
|
||||
}
|
||||
|
||||
export interface KeyringPairs {
|
||||
@@ -72,9 +62,11 @@ export interface KeyringInstance {
|
||||
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;
|
||||
addFromPair (pair: Keypair, meta?: KeyringPair$Meta, type?: KeypairType): KeyringPair
|
||||
addFromSeed (seed: Uint8Array, meta?: KeyringPair$Meta, type?: KeypairType): KeyringPair;
|
||||
addFromUri (suri: string, meta?: KeyringPair$Meta, type?: KeypairType): KeyringPair;
|
||||
createFromJson (json: KeyringPair$Json, ignoreChecksum?: boolean): KeyringPair;
|
||||
createFromPair (pair: Keypair, meta: KeyringPair$Meta, type: KeypairType): KeyringPair
|
||||
createFromUri (suri: string, meta?: KeyringPair$Meta, type?: KeypairType): KeyringPair;
|
||||
getPair (address: string | Uint8Array): KeyringPair;
|
||||
getPairs (): KeyringPair[];
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"name": "@polkadot/networks",
|
||||
"version": "5.5.2",
|
||||
"version": "6.0.3",
|
||||
"type": "module",
|
||||
"description": "A list of all available Substrate networks and their applicable prefixes",
|
||||
"main": "index.js",
|
||||
"sideEffects": false,
|
||||
@@ -12,10 +13,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.13.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@polkadot/util": "5.5.2",
|
||||
"@polkadot/x-fetch": "5.5.2"
|
||||
"@polkadot/util": "6.0.3",
|
||||
"@polkadot/x-fetch": "6.0.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
//
|
||||
// Once the above is published as a package, the duplication here can be removed
|
||||
|
||||
import type { Network, NetworkFromSubstrate } from './types';
|
||||
import type { Network, NetworkFromSubstrate, NetworkFromSubstrateNamed } from './types';
|
||||
|
||||
export { packageInfo } from './packageInfo';
|
||||
|
||||
// These are known prefixes that are not sorted
|
||||
const UNSORTED = [0, 2, 42];
|
||||
@@ -16,18 +18,18 @@ 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,
|
||||
website: null
|
||||
});
|
||||
|
||||
const all: NetworkFromSubstrate[] = [
|
||||
export const all: NetworkFromSubstrate[] = [
|
||||
{
|
||||
decimals: [10],
|
||||
displayName: 'Polkadot Relay Chain',
|
||||
@@ -41,7 +43,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 +61,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',
|
||||
@@ -344,6 +346,15 @@ const all: NetworkFromSubstrate[] = [
|
||||
symbols: null,
|
||||
website: null
|
||||
},
|
||||
{
|
||||
decimals: [12],
|
||||
displayName: 'Ares Protocol',
|
||||
network: 'ares',
|
||||
prefix: 34,
|
||||
standardAccount: '*25519',
|
||||
symbols: ['ARES'],
|
||||
website: 'https://www.aresprotocol.com/'
|
||||
},
|
||||
{
|
||||
decimals: [15],
|
||||
displayName: 'Valiu Liquidity Network',
|
||||
@@ -417,7 +428,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 +447,44 @@ 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'),
|
||||
{
|
||||
decimals: [12],
|
||||
displayName: 'Neatcoin Mainnet',
|
||||
network: 'neatcoin',
|
||||
prefix: 48,
|
||||
standardAccount: '*25519',
|
||||
symbols: ['NEAT'],
|
||||
website: 'https://neatcoin.org'
|
||||
},
|
||||
{
|
||||
decimals: [12],
|
||||
displayName: 'HydraDX',
|
||||
network: 'hydradx',
|
||||
prefix: 63,
|
||||
standardAccount: '*25519',
|
||||
symbols: ['HDX'],
|
||||
website: 'https://hydradx.io'
|
||||
},
|
||||
{
|
||||
decimals: [18],
|
||||
displayName: 'AvN Mainnet',
|
||||
network: 'aventus',
|
||||
prefix: 65,
|
||||
standardAccount: '*25519',
|
||||
symbols: ['AVT'],
|
||||
website: 'https://aventus.io'
|
||||
},
|
||||
{
|
||||
decimals: [12],
|
||||
displayName: 'Crust Network',
|
||||
network: 'crust',
|
||||
prefix: 66,
|
||||
standardAccount: '*25519',
|
||||
symbols: ['CRU'],
|
||||
website: 'https://crust.network'
|
||||
}
|
||||
];
|
||||
|
||||
// The list of available/claimed prefixes
|
||||
@@ -446,8 +492,8 @@ const all: NetworkFromSubstrate[] = [
|
||||
// - we only include those where we have a standardAccount
|
||||
// - when no icon has been specified, default to substrate
|
||||
// - sort by name, however we keep 0, 2, 42 first in the list
|
||||
const available: Network[] = all
|
||||
.filter((n) => !n.isIgnored)
|
||||
export const available: Network[] = all
|
||||
.filter((n): n is NetworkFromSubstrateNamed => !n.isIgnored && !!n.network)
|
||||
.map((n) => ({ ...n, genesisHash: n.genesisHash || [], icon: n.icon || 'substrate' }))
|
||||
.sort((a, b) =>
|
||||
UNSORTED.includes(a.prefix) && UNSORTED.includes(b.prefix)
|
||||
@@ -460,8 +506,4 @@ const available: Network[] = all
|
||||
);
|
||||
|
||||
// A filtered list of those chains we have details about (genesisHashes)
|
||||
const filtered: Network[] = available.filter((n) => n.genesisHash.length || n.prefix === 42);
|
||||
|
||||
export { all, available };
|
||||
|
||||
export default filtered;
|
||||
export default available.filter((n) => n.genesisHash.length || n.prefix === 42);
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright 2017-2021 @polkadot/networks authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Auto-generated by @polkadot/dev, do not edit
|
||||
|
||||
export const packageInfo = { name: '@polkadot/networks', version: '6.0.3' };
|
||||
@@ -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;
|
||||
@@ -18,7 +18,11 @@ export interface NetworkFromSubstrate {
|
||||
website: string | null;
|
||||
}
|
||||
|
||||
export interface Network extends NetworkFromSubstrate {
|
||||
export interface NetworkFromSubstrateNamed extends NetworkFromSubstrate {
|
||||
network: string;
|
||||
}
|
||||
|
||||
export interface Network extends NetworkFromSubstrateNamed {
|
||||
genesisHash: string[];
|
||||
icon: Icon;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
{
|
||||
"name": "@polkadot/util-crypto",
|
||||
"version": "5.5.2",
|
||||
"version": "6.0.3",
|
||||
"type": "module",
|
||||
"description": "A collection of useful crypto utilities for @polkadot",
|
||||
"main": "index.js",
|
||||
"sideEffects": [
|
||||
"./detectPackage.js",
|
||||
"./detectPackage.mjs"
|
||||
"./detectPackage.cjs"
|
||||
],
|
||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||
"maintainers": [],
|
||||
@@ -15,16 +16,17 @@
|
||||
"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",
|
||||
"@polkadot/wasm-crypto": "^3.2.2",
|
||||
"@polkadot/x-randomvalues": "5.5.2",
|
||||
"@babel/runtime": "^7.13.9",
|
||||
"@polkadot/networks": "6.0.3",
|
||||
"@polkadot/util": "6.0.3",
|
||||
"@polkadot/wasm-crypto": "^4.0.2",
|
||||
"@polkadot/x-randomvalues": "6.0.3",
|
||||
"base-x": "^3.0.8",
|
||||
"base64-js": "^1.5.1",
|
||||
"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 +41,6 @@
|
||||
"@types/xxhashjs": "^0.2.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@polkadot/util": "5.5.2"
|
||||
"@polkadot/util": "6.0.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,31 @@
|
||||
// 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 (66)', (): void => {
|
||||
expect(
|
||||
checkAddressChecksum(base58Decode('cTGShekJ1L1UKFZR9xmv9UTJod7vqjFAPo4sDhXih2c3y1yLS'))
|
||||
).toEqual([true, 34, 2, 66]);
|
||||
});
|
||||
|
||||
it('correctly extracts the info from a 2-byte-prefix address (255)', (): 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,28 @@ describe('decodeAddress', (): void => {
|
||||
);
|
||||
});
|
||||
|
||||
it('decodes a 2-byte prefix (65)', (): void => {
|
||||
expect(
|
||||
decodeAddress('cLtA6nCDyvwKcEHH4QkZDSHMhS9s78BvUJUsKUbUAn1Jc2SCF')
|
||||
).toEqual(
|
||||
hexToU8a('0x08e8969768fc14399930d4b8d693f68a2ff6c6a597325d6946095e5e9d9d1b0e')
|
||||
);
|
||||
});
|
||||
|
||||
it('decodes a 2-byte prefix (255)', (): 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,53 @@ import { encodeAddress } from '.';
|
||||
|
||||
const keyring = createTestPairs({ type: 'ed25519' }, false);
|
||||
|
||||
const SUBKEY = [
|
||||
{
|
||||
// substrate default
|
||||
address: '5DA4D4GL5iakrn22h5uKoevgvo18Pqj5BcdEUv8etEDPdijA',
|
||||
publicKey: '0x3050f8456519829fe03302da802d22d3233a5f4037b9a3e2bcc403ccfcb2d735',
|
||||
ss58Format: 42
|
||||
},
|
||||
{
|
||||
// aventus
|
||||
address: 'cLtA6nCDyvwKcEHH4QkZDSHMhS9s78BvUJUsKUbUAn1Jc2SCF',
|
||||
publicKey: '0x08e8969768fc14399930d4b8d693f68a2ff6c6a597325d6946095e5e9d9d1b0e',
|
||||
ss58Format: 65
|
||||
},
|
||||
{
|
||||
// crust
|
||||
address: 'cTGShekJ1L1UKFZR9xmv9UTJod7vqjFAPo4sDhXih2c3y1yLS',
|
||||
publicKey: '0x04a047d52fe542484c69bc528990cfeaf3a663dded0638ee1b51cf78bacd1072',
|
||||
ss58Format: 66
|
||||
},
|
||||
{
|
||||
// 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 +63,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 +92,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 +126,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} - ${ss58Format})`, (): 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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,10 +2,5 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// https://github.com/multiformats/js-multibase/blob/master/src/constants.js
|
||||
const BASE32_ALPHABET = 'abcdefghijklmnopqrstuvwxyz234567';
|
||||
const BITS_PER_CHAR = 5;
|
||||
|
||||
export {
|
||||
BASE32_ALPHABET,
|
||||
BITS_PER_CHAR
|
||||
};
|
||||
export const BASE32_ALPHABET = 'abcdefghijklmnopqrstuvwxyz234567';
|
||||
export const BITS_PER_CHAR = 5;
|
||||
|
||||
@@ -4,11 +4,6 @@
|
||||
import baseX from 'base-x';
|
||||
|
||||
// https://github.com/cryptocoinjs/base-x#alphabets
|
||||
const BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
|
||||
export const BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
|
||||
|
||||
const bs58 = baseX(BASE58_ALPHABET);
|
||||
|
||||
export {
|
||||
BASE58_ALPHABET,
|
||||
bs58
|
||||
};
|
||||
export const bs58 = baseX(BASE58_ALPHABET);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { u8aToBuffer, u8aToU8a } from '@polkadot/util';
|
||||
import { u8aToU8a } from '@polkadot/util';
|
||||
|
||||
import { bs58 } from './bs58';
|
||||
|
||||
@@ -12,9 +12,7 @@ import { bs58 } from './bs58';
|
||||
* From the provided input, create the base58 and return the result as a string.
|
||||
*/
|
||||
export function base58Encode (value: Uint8Array | string | Buffer | number[], ipfsCompat?: boolean): string {
|
||||
const out = bs58.encode(
|
||||
u8aToBuffer(u8aToU8a(value))
|
||||
);
|
||||
const out = bs58.encode(u8aToU8a(value));
|
||||
|
||||
return ipfsCompat
|
||||
? `z${out}`
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { bufferToU8a } from '@polkadot/util';
|
||||
import base64 from 'base64-js';
|
||||
|
||||
import { base64Validate } from './validate';
|
||||
|
||||
@@ -14,5 +14,5 @@ import { base64Validate } from './validate';
|
||||
export function base64Decode (value: string): Uint8Array {
|
||||
base64Validate(value);
|
||||
|
||||
return bufferToU8a(Buffer.from(value, 'base64'));
|
||||
return base64.toByteArray(value);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { u8aToBuffer, u8aToU8a } from '@polkadot/util';
|
||||
import base64 from 'base64-js';
|
||||
|
||||
import { u8aToU8a } from '@polkadot/util';
|
||||
|
||||
/**
|
||||
* @name base64Encode
|
||||
@@ -10,5 +12,5 @@ import { u8aToBuffer, u8aToU8a } from '@polkadot/util';
|
||||
* From the provided input, create the base64 and return the result as a string.
|
||||
*/
|
||||
export function base64Encode (value: Uint8Array | string | Buffer | number[]): string {
|
||||
return u8aToBuffer(u8aToU8a(value)).toString('base64');
|
||||
return base64.fromByteArray(u8aToU8a(value));
|
||||
}
|
||||
|
||||
@@ -1,25 +1,10 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// For esm, this should be import.meta.url or to get the same behavior as __dirname, we can use
|
||||
//
|
||||
// new URL(import.meta.url).pathname)
|
||||
//
|
||||
// The issue is the WP4 has "some" issues with import.meta.url. So because of bundlers, we can't have
|
||||
// nice things... In this case it is even worse since import.meta.url won't even make it compile, so
|
||||
// there is a complete dead end with usage thereof
|
||||
//
|
||||
// When that is fixed, a solution is to have both .js & .mjs files, with the following content -
|
||||
//
|
||||
// cjs: util.detectPackage(packageInfo, () => __dirname);
|
||||
// esm: detectPackage(packageInfo, () => import.meta.url);
|
||||
//
|
||||
// With the above we additionally need a .d.ts to just export the packageInfo
|
||||
import { packageInfo as netInfo } from '@polkadot/networks/packageInfo';
|
||||
import { detectPackage, packageInfo as utilInfo } from '@polkadot/util';
|
||||
import { packageInfo as randomInfo } from '@polkadot/x-randomvalues';
|
||||
|
||||
import { detectPackage } from '@polkadot/util';
|
||||
import { packageInfo } from './packageInfo';
|
||||
|
||||
import packageInfo from './package-info.json';
|
||||
|
||||
detectPackage(packageInfo, typeof __dirname !== 'undefined' && __dirname);
|
||||
|
||||
export { packageInfo };
|
||||
detectPackage(packageInfo, typeof __dirname !== 'undefined' && __dirname, [netInfo, utilInfo, randomInfo]);
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { mnemonicToLegacySeed } from '@polkadot/util-crypto';
|
||||
|
||||
import { hdEthereum } from '.';
|
||||
|
||||
describe('hdEthereum', (): void => {
|
||||
const PHRASE = 'seed sock milk update focus rotate barely fade car face mechanic mercy';
|
||||
const derivationPath = 'm/44\'/60\'/0\'/0/0';
|
||||
const PUBLIC = new Uint8Array([
|
||||
3, 118, 64, 77, 247, 27, 4, 157,
|
||||
236, 206, 251, 221, 230, 244, 154, 147,
|
||||
189, 131, 249, 169, 102, 78, 3, 185,
|
||||
153, 19, 89, 40, 24, 25, 139, 131,
|
||||
93
|
||||
]);
|
||||
const SECRET = new Uint8Array([
|
||||
166, 162, 203, 17, 2, 206, 110, 176,
|
||||
18, 102, 230, 144, 90, 158, 25, 232,
|
||||
43, 180, 176, 49, 189, 149, 3, 71,
|
||||
243, 228, 223, 104, 125, 132, 58, 228
|
||||
]
|
||||
);
|
||||
const PUBLICDERIVED = new Uint8Array([
|
||||
3, 129, 53, 27, 27, 70, 210, 96,
|
||||
43, 9, 146, 187, 93, 85, 49, 249,
|
||||
193, 105, 107, 8, 18, 254, 178, 83,
|
||||
75, 104, 132, 173, 196, 126, 46, 29,
|
||||
139
|
||||
]);
|
||||
const SECRETDERIVED = new Uint8Array([
|
||||
7, 13, 195, 17, 115, 0, 1, 25,
|
||||
24, 226, 107, 2, 23, 105, 69, 204,
|
||||
21, 195, 213, 72, 207, 73, 253, 132,
|
||||
24, 217, 127, 147, 175, 105, 158, 70
|
||||
]);
|
||||
|
||||
it('derives the right key pair from a mnemonic', (): void => {
|
||||
const key = hdEthereum(mnemonicToLegacySeed(PHRASE, '', false, 64));
|
||||
|
||||
expect(key.publicKey).toEqual(PUBLIC);
|
||||
expect(key.secretKey).toEqual(SECRET);
|
||||
});
|
||||
|
||||
it('derives the right key pair from a mnemonic and a derivation path', (): void => {
|
||||
const key = hdEthereum(mnemonicToLegacySeed(PHRASE, '', false, 64), derivationPath);
|
||||
|
||||
expect(key.publicKey).toEqual(PUBLICDERIVED);
|
||||
expect(key.secretKey).toEqual(SECRETDERIVED);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Keypair } from '../../types';
|
||||
|
||||
import { assert, bnToU8a, stringToU8a, u8aConcat } from '@polkadot/util';
|
||||
|
||||
import { hmacSha512 } from '../../hmac';
|
||||
import { secp256k1KeypairFromSeed, secp256k1PrivateKeyTweakAdd } from '../../secp256k1';
|
||||
import { HARDENED, hdValidatePath } from '../validatePath';
|
||||
|
||||
interface CodedKeypair extends Keypair {
|
||||
chainCode: Uint8Array;
|
||||
}
|
||||
|
||||
const MASTER_SECRET = stringToU8a('Bitcoin seed');
|
||||
|
||||
function createCoded (secretKey: Uint8Array, chainCode: Uint8Array): CodedKeypair {
|
||||
return {
|
||||
chainCode,
|
||||
publicKey: secp256k1KeypairFromSeed(secretKey).publicKey,
|
||||
secretKey
|
||||
};
|
||||
}
|
||||
|
||||
function deriveChild (hd: CodedKeypair, index: number): CodedKeypair {
|
||||
const indexBuffer = bnToU8a(index, { bitLength: 32, isLe: false });
|
||||
const data = index >= HARDENED
|
||||
? u8aConcat(new Uint8Array(1), hd.secretKey, indexBuffer)
|
||||
: u8aConcat(hd.publicKey, indexBuffer);
|
||||
|
||||
try {
|
||||
const I = hmacSha512(hd.chainCode, data);
|
||||
|
||||
return createCoded(
|
||||
secp256k1PrivateKeyTweakAdd(hd.secretKey, I.slice(0, 32)),
|
||||
I.slice(32)
|
||||
);
|
||||
} catch (err) {
|
||||
// In case parse256(IL) >= n or ki == 0, proceed with the next value for i
|
||||
return deriveChild(hd, index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
export function hdEthereum (seed: Uint8Array, path = ''): Keypair {
|
||||
const I = hmacSha512(MASTER_SECRET, seed);
|
||||
const hd = createCoded(I.slice(0, 32), I.slice(32));
|
||||
|
||||
if (!path || path === 'm' || path === 'M' || path === "m'" || path === "M'") {
|
||||
return hd;
|
||||
}
|
||||
|
||||
assert(hdValidatePath(path), 'Invalid derivation path');
|
||||
|
||||
return path
|
||||
.split('/')
|
||||
.slice(1)
|
||||
.reduce((hd: CodedKeypair, c): CodedKeypair =>
|
||||
deriveChild(
|
||||
hd, parseInt(c, 10) + (
|
||||
(c.length > 1) && c.endsWith("'")
|
||||
? HARDENED
|
||||
: 0
|
||||
)
|
||||
), hd
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export { hdEthereum } from './ethereum';
|
||||
export { hdLedger } from './ledger';
|
||||
export { hdValidatePath } from './validatePath';
|
||||
|
||||
@@ -30,7 +30,7 @@ const TESTS = {
|
||||
]
|
||||
},
|
||||
Polkadot: {
|
||||
slip44: 0x80000162,
|
||||
slip44: 0x0162,
|
||||
tests: [
|
||||
{
|
||||
ed25519: '0xe8c68348586d53e4e8d1a864b0e4e17c75e4eb06e0c63c1432bef2ba29e69d41',
|
||||
|
||||
@@ -7,15 +7,13 @@ import { assert } from '@polkadot/util';
|
||||
|
||||
import { mnemonicValidate } from '../../mnemonic';
|
||||
import { naclKeypairFromSeed } from '../../nacl';
|
||||
import { HARDENED, hdValidatePath } from '../validatePath';
|
||||
import { ledgerDerivePrivate } from './derivePrivate';
|
||||
import { ledgerMaster } from './master';
|
||||
import { ledgerValidatePath } from './validatePath';
|
||||
|
||||
const HARDENED = 0x80000000;
|
||||
|
||||
export function hdLedger (mnemonic: string, path: string): Keypair {
|
||||
assert(mnemonicValidate(mnemonic), 'Invalid mnemonic passed to ledger derivation');
|
||||
assert(ledgerValidatePath(path), 'Invalid derivation path');
|
||||
assert(hdValidatePath(path), 'Invalid derivation path');
|
||||
|
||||
return naclKeypairFromSeed(
|
||||
path
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export function ledgerValidatePath (path: string): boolean {
|
||||
if (!path.startsWith('m/')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !path
|
||||
.split('/')
|
||||
.slice(1)
|
||||
.map((n) => parseInt(n.replace("'", ''), 10))
|
||||
.some((n) => isNaN(n));
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { hdValidatePath } from '.';
|
||||
|
||||
const VECTORS: [string, boolean][] = [
|
||||
["m/44'/60'/0'/0/0", true],
|
||||
['m/0', true],
|
||||
["m/123'", true],
|
||||
["n/44'/60'/0'/0/0", false],
|
||||
['m', false],
|
||||
['m/', false],
|
||||
["m/xyz'", false],
|
||||
['m/123x', false],
|
||||
['m/123"', false],
|
||||
["m/123''", false],
|
||||
["m/123'0'", false],
|
||||
[`m/${0x80000000}`, false],
|
||||
['m/-1', false]
|
||||
];
|
||||
|
||||
describe('hdValidatePath', (): void => {
|
||||
VECTORS.forEach(([path, result]): void => {
|
||||
it(`validates ${path} as ${result.toString()}`, (): void => {
|
||||
expect(hdValidatePath(path)).toEqual(result);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export const HARDENED = 0x80000000;
|
||||
|
||||
export function hdValidatePath (path: string): boolean {
|
||||
if (!path.startsWith('m/')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const parts = path.split('/').slice(1);
|
||||
|
||||
return parts.every((n) => /^\d+'?$/.test(n)) && (
|
||||
!parts
|
||||
.map((n) => parseInt(n.replace("'", ''), 10))
|
||||
.some((n) => isNaN(n) || (n >= HARDENED) || (n < 0))
|
||||
);
|
||||
}
|
||||
@@ -4,6 +4,8 @@
|
||||
import './cryptoInit';
|
||||
import './detectPackage';
|
||||
|
||||
export { packageInfo } from './packageInfo';
|
||||
|
||||
// all internal exports
|
||||
export * from './address';
|
||||
export * from './base32';
|
||||
@@ -14,6 +16,7 @@ export * from './crypto';
|
||||
export * from './ethereum';
|
||||
export * from './hd';
|
||||
export * from './hmac';
|
||||
export * from './json';
|
||||
export * from './keccak';
|
||||
export * from './key';
|
||||
export * from './mnemonic';
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { EncryptedJsonEncoding, EncryptedJsonVersion } from './types';
|
||||
|
||||
export const ENCODING: EncryptedJsonEncoding[] = ['scrypt', 'xsalsa20-poly1305'];
|
||||
export const ENCODING_NONE: EncryptedJsonEncoding[] = ['none'];
|
||||
export const ENCODING_VERSION: EncryptedJsonVersion = '3';
|
||||
|
||||
export const NONCE_LENGTH = 24;
|
||||
export const SCRYPT_LENGTH = 32 + (3 * 4);
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { EncryptedJson } from './types';
|
||||
|
||||
import { assert, hexToU8a, isHex } from '@polkadot/util';
|
||||
|
||||
import { base64Decode } from '../base64';
|
||||
import { jsonDecryptData } from './decryptData';
|
||||
|
||||
export function jsonDecrypt ({ encoded, encoding }: EncryptedJson, passphrase?: string | null): Uint8Array {
|
||||
assert(encoded, 'No encrypted data available to decode');
|
||||
|
||||
return jsonDecryptData(
|
||||
isHex(encoded)
|
||||
? hexToU8a(encoded)
|
||||
: base64Decode(encoded),
|
||||
passphrase,
|
||||
Array.isArray(encoding.type)
|
||||
? encoding.type
|
||||
: [encoding.type]
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { EncryptedJsonEncoding } from './types';
|
||||
|
||||
import { assert, stringToU8a, u8aFixLength } from '@polkadot/util';
|
||||
|
||||
import { naclDecrypt } from '../nacl';
|
||||
import { scryptEncode, scryptFromU8a } from '../scrypt';
|
||||
import { ENCODING, NONCE_LENGTH, SCRYPT_LENGTH } from './constants';
|
||||
|
||||
export function jsonDecryptData (encrypted?: Uint8Array | null, passphrase?: string | null, encType: EncryptedJsonEncoding[] = ENCODING): Uint8Array {
|
||||
assert(encrypted, 'No encrypted data available to decode');
|
||||
assert(passphrase || !encType.includes('xsalsa20-poly1305'), 'Password required to decode encypted data');
|
||||
|
||||
let encoded: Uint8Array | null = encrypted;
|
||||
|
||||
if (passphrase) {
|
||||
let password: Uint8Array;
|
||||
|
||||
if (encType.includes('scrypt')) {
|
||||
const { params, salt } = scryptFromU8a(encrypted);
|
||||
|
||||
password = scryptEncode(passphrase, salt, params).password;
|
||||
encrypted = encrypted.subarray(SCRYPT_LENGTH);
|
||||
} else {
|
||||
password = stringToU8a(passphrase);
|
||||
}
|
||||
|
||||
encoded = naclDecrypt(
|
||||
encrypted.subarray(NONCE_LENGTH),
|
||||
encrypted.subarray(0, NONCE_LENGTH),
|
||||
u8aFixLength(password, 256, true)
|
||||
);
|
||||
}
|
||||
|
||||
assert(encoded, 'Unable to decode using the supplied passphrase');
|
||||
|
||||
return encoded;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { EncryptedJson } from './types';
|
||||
|
||||
import { u8aConcat } from '@polkadot/util';
|
||||
|
||||
import { naclEncrypt } from '../nacl';
|
||||
import { scryptEncode, scryptToU8a } from '../scrypt';
|
||||
import { jsonEncryptFormat } from './encryptFormat';
|
||||
|
||||
export function jsonEncrypt (data: Uint8Array, contentType: string[], passphrase?: string | null): EncryptedJson {
|
||||
let isEncrypted = false;
|
||||
let encoded = data;
|
||||
|
||||
if (passphrase) {
|
||||
const { params, password, salt } = scryptEncode(passphrase);
|
||||
const { encrypted, nonce } = naclEncrypt(encoded, password.subarray(0, 32));
|
||||
|
||||
isEncrypted = true;
|
||||
encoded = u8aConcat(scryptToU8a(salt, params), nonce, encrypted);
|
||||
}
|
||||
|
||||
return jsonEncryptFormat(encoded, contentType, isEncrypted);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { EncryptedJson } from './types';
|
||||
|
||||
import { base64Encode } from '../base64';
|
||||
import { ENCODING, ENCODING_NONE, ENCODING_VERSION } from './constants';
|
||||
|
||||
export function jsonEncryptFormat (encoded: Uint8Array, contentType: string[], isEncrypted: boolean): EncryptedJson {
|
||||
return {
|
||||
encoded: base64Encode(encoded),
|
||||
encoding: {
|
||||
content: contentType,
|
||||
type: isEncrypted
|
||||
? ENCODING
|
||||
: ENCODING_NONE,
|
||||
version: ENCODING_VERSION
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export { jsonDecrypt } from './decrypt';
|
||||
export { jsonDecryptData } from './decryptData';
|
||||
export { jsonEncrypt } from './encrypt';
|
||||
export { jsonEncryptFormat } from './encryptFormat';
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export type EncryptedJsonVersion = '0' | '1' | '2' | '3';
|
||||
|
||||
export type EncryptedJsonEncoding = 'none' | 'scrypt' | 'xsalsa20-poly1305';
|
||||
|
||||
export interface EncryptedJsonDescriptor {
|
||||
content: string[];
|
||||
type: EncryptedJsonEncoding | EncryptedJsonEncoding[];
|
||||
version: EncryptedJsonVersion;
|
||||
}
|
||||
|
||||
export interface EncryptedJson {
|
||||
encoded: string;
|
||||
encoding: EncryptedJsonDescriptor;
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import { DeriveJunction } from './DeriveJunction';
|
||||
import { keyExtractPath } from './extractPath';
|
||||
|
||||
export interface ExtractResult {
|
||||
derivePath: string,
|
||||
password?: string;
|
||||
path: DeriveJunction[];
|
||||
phrase: string;
|
||||
@@ -27,6 +28,7 @@ export function keyExtractSuri (suri: string): ExtractResult {
|
||||
const { path } = keyExtractPath(derivePath);
|
||||
|
||||
return {
|
||||
derivePath,
|
||||
password,
|
||||
path,
|
||||
phrase
|
||||
|
||||
@@ -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,24 +1,52 @@
|
||||
// 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 SEED = '0x3c121e20de068083b49c2315697fb59a2d9e8643c24e5ea7628132c58969a027';
|
||||
const MNEMONIC = 'seed sock milk update focus rotate barely fade car face mechanic mercy';
|
||||
const SEED_32 = '0x3c121e20de068083b49c2315697fb59a2d9e8643c24e5ea7628132c58969a027';
|
||||
const SEED_64 = '0x3c121e20de068083b49c2315697fb59a2d9e8643c24e5ea7628132c58969a0275693dd5bd9d4cc9e648475eba9613ed4678f4d62560a9c42f75bac04022ded25';
|
||||
|
||||
describe('mnemonicToLegacySeed', (): void => {
|
||||
beforeEach(async (): Promise<void> => {
|
||||
await waitReady();
|
||||
});
|
||||
|
||||
[false, true].forEach((onlyJs): void => {
|
||||
it(`generates a valid seed (onlyJs = ${onlyJs.toString()})`, (): void => {
|
||||
[undefined, 'foo', 'bar'].forEach((password): void => {
|
||||
it(`generates Wasm & Js equivalents (password = ${password || 'undefined'})`, (): void => {
|
||||
expect(
|
||||
u8aToHex(mnemonicToLegacySeed(MENMONIC, undefined, onlyJs))
|
||||
).toEqual(SEED);
|
||||
u8aEq(
|
||||
mnemonicToLegacySeed(MNEMONIC, password, true),
|
||||
mnemonicToLegacySeed(MNEMONIC, password, false)
|
||||
)
|
||||
).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
[false, true].forEach((onlyJs): void => {
|
||||
it(`generates a valid 64bytes seed (onlyJs = ${onlyJs.toString()})`, (): void => {
|
||||
expect(
|
||||
u8aToHex(mnemonicToLegacySeed(MNEMONIC, undefined, onlyJs, 64))
|
||||
).toEqual(SEED_64);
|
||||
});
|
||||
});
|
||||
|
||||
[false, true].forEach((onlyJs): void => {
|
||||
it(`generates a valid 32bytes seed (onlyJs = ${onlyJs.toString()})`, (): void => {
|
||||
expect(
|
||||
u8aToHex(mnemonicToLegacySeed(MNEMONIC, undefined, onlyJs))
|
||||
).toEqual(SEED_32);
|
||||
});
|
||||
});
|
||||
|
||||
[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
|
||||
@@ -22,8 +24,17 @@ import { mnemonicToSeedSync } from './bip39';
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export function mnemonicToLegacySeed (mnemonic: string, password = '', onlyJs = false): Uint8Array {
|
||||
return isReady() && !onlyJs
|
||||
? bip39ToSeed(mnemonic, password)
|
||||
: mnemonicToSeedSync(mnemonic, password).subarray(0, 32);
|
||||
export function mnemonicToLegacySeed (mnemonic: string, password = '', onlyJs = false, byteLength: 32 | 64 = 32): Uint8Array {
|
||||
assert(mnemonicValidate(mnemonic), 'Invalid bip39 mnemonic specified');
|
||||
assert([32, 64].includes(byteLength), `Invalid seed length ${byteLength}, expected 32 or 64`);
|
||||
|
||||
if (byteLength && byteLength === 32) {
|
||||
return isReady() && !onlyJs
|
||||
? bip39ToSeed(mnemonic, password)
|
||||
: mnemonicToSeedSync(mnemonic, password).subarray(0, 32);
|
||||
} else if (byteLength === 64) {
|
||||
return mnemonicToSeedSync(mnemonic, password);
|
||||
} else {
|
||||
return new Uint8Array();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Auto-generated by @polkadot/dev, do not edit
|
||||
|
||||
export const packageInfo = { name: '@polkadot/util-crypto', version: '6.0.3' };
|
||||
@@ -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,10 +1,8 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
const DEFAULT_PARAMS = {
|
||||
export const DEFAULT_PARAMS = {
|
||||
N: 1 << 15,
|
||||
p: 1,
|
||||
r: 8
|
||||
};
|
||||
|
||||
export { DEFAULT_PARAMS };
|
||||
|
||||
@@ -6,4 +6,5 @@ export { secp256k1Expand } from './expand';
|
||||
export { secp256k1KeypairFromSeed } from './keypair/fromSeed';
|
||||
export { secp256k1Recover } from './recover';
|
||||
export { secp256k1Sign } from './sign';
|
||||
export { secp256k1PrivateKeyTweakAdd } from './tweakAdd';
|
||||
export { secp256k1Verify } from './verify';
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { secp256k1PrivateKeyTweakAdd } from './tweakAdd';
|
||||
|
||||
describe('TweakAdd', (): void => {
|
||||
describe('PrivateKey TweakAdd', (): void => {
|
||||
it('succeeds for a simple case', (): void => {
|
||||
const A = new Uint8Array([0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1]);
|
||||
const B = new Uint8Array([3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]);
|
||||
|
||||
expect(
|
||||
secp256k1PrivateKeyTweakAdd(A, B)
|
||||
).toEqual(new Uint8Array([
|
||||
3, 4, 3, 4, 3, 4, 3, 4, 3,
|
||||
4, 3, 4, 3, 4, 3, 4, 3, 4,
|
||||
3, 4, 3, 4, 3, 4, 3, 4, 3,
|
||||
4, 3, 4, 3, 4
|
||||
]));
|
||||
});
|
||||
|
||||
it('fails for wrong array length', (): void => {
|
||||
const A = new Uint8Array([0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1]);
|
||||
const B = new Uint8Array([3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]);
|
||||
|
||||
expect(
|
||||
() => secp256k1PrivateKeyTweakAdd(A, B)
|
||||
).toThrow(/Expected tweak to be an Uint8Array/);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2017-2021 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import BN from 'bn.js';
|
||||
|
||||
import { assert, bnToU8a, isU8a } from '@polkadot/util';
|
||||
|
||||
import { secp256k1 } from './secp256k1';
|
||||
|
||||
const ecparams = secp256k1.curve as { n: BN };
|
||||
|
||||
export function secp256k1PrivateKeyTweakAdd (seckey: Uint8Array, tweak: Uint8Array): Uint8Array {
|
||||
assert(isU8a(seckey) && seckey.length === 32, 'Expected seckey to be an Uint8Array with length 32');
|
||||
assert(isU8a(tweak) && tweak.length === 32, 'Expected tweak to be an Uint8Array with length 32');
|
||||
|
||||
const bn = new BN(tweak);
|
||||
|
||||
assert(bn.cmp(ecparams.n) < 0, 'Tweak parameter is out of range');
|
||||
|
||||
bn.iadd(new BN(seckey));
|
||||
|
||||
if (bn.cmp(ecparams.n) >= 0) {
|
||||
bn.isub(ecparams.n);
|
||||
}
|
||||
|
||||
assert(!bn.isZero(), 'Invalid resulting private key');
|
||||
|
||||
return bnToU8a(bn, { bitLength: 256, isLe: false });
|
||||
}
|
||||
@@ -36,21 +36,24 @@ describe('signatureVerify', (): void => {
|
||||
it('verifies an ed25519 signature', (): void => {
|
||||
expect(signatureVerify(MESSAGE, SIG_ED, ADDR_ED)).toEqual({
|
||||
crypto: 'ed25519',
|
||||
isValid: true
|
||||
isValid: true,
|
||||
publicKey: decodeAddress(ADDR_ED)
|
||||
});
|
||||
});
|
||||
|
||||
it('verifies an ecdsa signature', (): void => {
|
||||
expect(signatureVerify(MESSAGE, SIG_EC, ADDR_EC)).toEqual({
|
||||
crypto: 'ecdsa',
|
||||
isValid: true
|
||||
isValid: true,
|
||||
publicKey: decodeAddress(ADDR_EC)
|
||||
});
|
||||
});
|
||||
|
||||
it('verifies an ethereum signature', (): void => {
|
||||
expect(signatureVerify(MESSAGE, SIG_ET, ADDR_ET)).toEqual({
|
||||
crypto: 'ethereum',
|
||||
isValid: true
|
||||
isValid: true,
|
||||
publicKey: hexToU8a(ADDR_ET)
|
||||
});
|
||||
});
|
||||
|
||||
@@ -63,35 +66,40 @@ describe('signatureVerify', (): void => {
|
||||
'0x002309df96687e44280bb72c3818358faeeb699c'
|
||||
)).toEqual({
|
||||
crypto: 'ethereum',
|
||||
isValid: true
|
||||
isValid: true,
|
||||
publicKey: hexToU8a('0x002309df96687e44280bb72c3818358faeeb699c')
|
||||
});
|
||||
});
|
||||
|
||||
it('fails on invalid ethereum signature', (): void => {
|
||||
expect(signatureVerify(MESSAGE, SIG_EC, ADDR_ET)).toEqual({
|
||||
crypto: 'none',
|
||||
isValid: false
|
||||
isValid: false,
|
||||
publicKey: hexToU8a(ADDR_ET)
|
||||
});
|
||||
});
|
||||
|
||||
it('verifies an sr25519 signature', (): void => {
|
||||
expect(signatureVerify(MESSAGE, SIG_SR, ADDR_SR)).toEqual({
|
||||
crypto: 'sr25519',
|
||||
isValid: true
|
||||
isValid: true,
|
||||
publicKey: decodeAddress(ADDR_SR)
|
||||
});
|
||||
});
|
||||
|
||||
it('allows various inputs', (): void => {
|
||||
expect(signatureVerify(stringToU8a(MESSAGE), hexToU8a(SIG_ED), decodeAddress(ADDR_ED))).toEqual({
|
||||
crypto: 'ed25519',
|
||||
isValid: true
|
||||
isValid: true,
|
||||
publicKey: decodeAddress(ADDR_ED)
|
||||
});
|
||||
});
|
||||
|
||||
it('fails on an invalid signature', (): void => {
|
||||
expect(signatureVerify(MESSAGE, SIG_SR, ADDR_ED)).toEqual({
|
||||
crypto: 'none',
|
||||
isValid: false
|
||||
isValid: false,
|
||||
publicKey: decodeAddress(ADDR_ED)
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -100,35 +108,40 @@ describe('signatureVerify', (): void => {
|
||||
it('verifies an ed25519 signature', (): void => {
|
||||
expect(signatureVerify(MESSAGE, MUL_ED, ADDR_ED)).toEqual({
|
||||
crypto: 'ed25519',
|
||||
isValid: true
|
||||
isValid: true,
|
||||
publicKey: decodeAddress(ADDR_ED)
|
||||
});
|
||||
});
|
||||
|
||||
it('verifies an ecdsa signature', (): void => {
|
||||
expect(signatureVerify(MESSAGE, MUL_EC, ADDR_EC)).toEqual({
|
||||
crypto: 'ecdsa',
|
||||
isValid: true
|
||||
isValid: true,
|
||||
publicKey: decodeAddress(ADDR_EC)
|
||||
});
|
||||
});
|
||||
|
||||
it('verifies an ethereum signature', (): void => {
|
||||
expect(signatureVerify(MESSAGE, MUL_ET, ADDR_ET)).toEqual({
|
||||
crypto: 'ethereum',
|
||||
isValid: true
|
||||
isValid: true,
|
||||
publicKey: hexToU8a(ADDR_ET)
|
||||
});
|
||||
});
|
||||
|
||||
it('verifies an sr25519 signature', (): void => {
|
||||
expect(signatureVerify(MESSAGE, MUL_SR, ADDR_SR)).toEqual({
|
||||
crypto: 'sr25519',
|
||||
isValid: true
|
||||
isValid: true,
|
||||
publicKey: decodeAddress(ADDR_SR)
|
||||
});
|
||||
});
|
||||
|
||||
it('fails on an invalid signature', (): void => {
|
||||
expect(signatureVerify(MESSAGE, MUL_SR, ADDR_ED)).toEqual({
|
||||
crypto: 'sr25519',
|
||||
isValid: false
|
||||
isValid: false,
|
||||
publicKey: new Uint8Array([61, 12, 55, 211, 0, 211, 97, 199, 4, 37, 17, 213, 81, 175, 166, 23, 251, 199, 144, 210, 19, 83, 186, 1, 196, 231, 14, 156, 171, 46, 141, 146])
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -79,9 +79,9 @@ export function signatureVerify (message: Uint8Array | string, signature: Uint8A
|
||||
|
||||
assert([64, 65, 66].includes(signatureU8a.length), `Invalid signature length, expected [64..66] bytes, found ${signatureU8a.length}`);
|
||||
|
||||
const result: VerifyResult = { crypto: 'none', isValid: false };
|
||||
const publicKey = decodeAddress(addressOrPublicKey);
|
||||
const input = { message, publicKey, signature: signatureU8a };
|
||||
const result: VerifyResult = { crypto: 'none', isValid: false, publicKey };
|
||||
|
||||
return [0, 1, 2].includes(signatureU8a[0]) && [65, 66].includes(signatureU8a.length)
|
||||
? verifyMultisig(result, input)
|
||||
|
||||
@@ -16,4 +16,5 @@ export type KeypairType = 'ed25519' | 'sr25519' | 'ecdsa' | 'ethereum';
|
||||
export interface VerifyResult {
|
||||
crypto: 'none' | KeypairType;
|
||||
isValid: boolean;
|
||||
publicKey: Uint8Array;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
{
|
||||
"name": "@polkadot/util",
|
||||
"version": "5.5.2",
|
||||
"version": "6.0.3",
|
||||
"type": "module",
|
||||
"description": "A collection of useful utilities for @polkadot",
|
||||
"main": "index.js",
|
||||
"sideEffects": false,
|
||||
"sideEffects": [
|
||||
"./detectPackage.js",
|
||||
"./detectPackage.cjs"
|
||||
],
|
||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||
"maintainers": [],
|
||||
"contributors": [],
|
||||
@@ -12,9 +16,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.13.9",
|
||||
"@polkadot/x-textdecoder": "6.0.3",
|
||||
"@polkadot/x-textencoder": "6.0.3",
|
||||
"@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,38 +0,0 @@
|
||||
// Copyright 2017-2021 @polkadot/util authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { detectPackage } from '.';
|
||||
|
||||
describe('assertSingletonPackage', (): void => {
|
||||
const PKG = '@polkadot/util';
|
||||
const VER1 = '9.8.0-beta.45';
|
||||
const VER2 = '9.7.1';
|
||||
const VER3 = '9.6.1';
|
||||
const PATH = '/Users/jaco/Projects/polkadot-js/api/node_modules/@polkadot/util';
|
||||
const RES2 = `Multiple instances of @polkadot/util detected, ensure that there is only one package in your dependency tree.\n\t${VER1}\t${PATH}/01\n\t${VER2} \t${PATH}/02`;
|
||||
const RES3 = `${RES2}\n\t${VER3} \t${PATH}/03`;
|
||||
|
||||
it('should not log the first time', (): void => {
|
||||
const spy = jest.spyOn(console, 'warn');
|
||||
|
||||
detectPackage({ name: PKG, version: VER1 }, `${PATH}/01`);
|
||||
expect(spy).not.toHaveBeenCalled();
|
||||
spy.mockRestore();
|
||||
});
|
||||
|
||||
it('should log the second time', (): void => {
|
||||
const spy = jest.spyOn(console, 'warn');
|
||||
|
||||
detectPackage({ name: PKG, version: VER2 }, `${PATH}/02`);
|
||||
expect(spy).toHaveBeenCalledWith(RES2);
|
||||
spy.mockRestore();
|
||||
});
|
||||
|
||||
it('should allow for function use', (): void => {
|
||||
const spy = jest.spyOn(console, 'warn');
|
||||
|
||||
detectPackage({ name: PKG, version: VER3 }, () => `${PATH}/03`);
|
||||
expect(spy).toHaveBeenCalledWith(RES3);
|
||||
spy.mockRestore();
|
||||
});
|
||||
});
|
||||
@@ -1,91 +1,25 @@
|
||||
// Copyright 2017-2021 @polkadot/util authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { xglobal } from '@polkadot/x-global';
|
||||
// For esm, this should be import.meta.url or to get the same behavior as __dirname, we can use
|
||||
//
|
||||
// new URL(import.meta.url).pathname)
|
||||
//
|
||||
// The issue is the WP4 has "some" issues with import.meta.url. So because of bundlers, we can't have
|
||||
// nice things... In this case it is even worse since import.meta.url won't even make it compile, so
|
||||
// there is a complete dead end with usage thereof
|
||||
//
|
||||
// When that is fixed, a solution is to have both .js & .mjs files, with the following content -
|
||||
//
|
||||
// cjs: util.detectPackage(packageInfo, () => __dirname);
|
||||
// esm: detectPackage(packageInfo, () => import.meta.url);
|
||||
//
|
||||
// With the above we additionally need a .d.ts to just export the packageInfo
|
||||
|
||||
import { isFunction } from './is/function';
|
||||
import { isString } from './is/string';
|
||||
import { assert } from './assert';
|
||||
import { packageInfo as decoderInfo } from '@polkadot/x-textdecoder';
|
||||
import { packageInfo as encoderInfo } from '@polkadot/x-textencoder';
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
type This = typeof globalThis;
|
||||
import { packageInfo } from './packageInfo';
|
||||
import { detectPackage } from './versionDetect';
|
||||
|
||||
interface PackageJson {
|
||||
name: string;
|
||||
version: string;
|
||||
}
|
||||
|
||||
interface VersionPath {
|
||||
path?: string;
|
||||
version: string;
|
||||
}
|
||||
|
||||
interface PjsChecks extends This {
|
||||
__polkadotjs: Record<string, VersionPath[]>;
|
||||
}
|
||||
|
||||
type PjsWindow = (Window & This) & PjsChecks;
|
||||
type FnString = () => string | undefined;
|
||||
|
||||
/** @internal */
|
||||
function expandPath (path?: string): string {
|
||||
return (!path || path.length < 5) ? '<unknown>' : path;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
function getEntry (name: string): VersionPath[] {
|
||||
const _global = xglobal as PjsWindow;
|
||||
|
||||
if (!_global.__polkadotjs) {
|
||||
_global.__polkadotjs = {};
|
||||
}
|
||||
|
||||
if (!_global.__polkadotjs[name]) {
|
||||
_global.__polkadotjs[name] = [];
|
||||
}
|
||||
|
||||
return _global.__polkadotjs[name];
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
function flattenVersions (entry: VersionPath[]): string {
|
||||
const all = entry.map((version: VersionPath | string): VersionPath =>
|
||||
isString(version)
|
||||
? { version }
|
||||
: version
|
||||
);
|
||||
const verLength = all.reduce((max, { version }) => Math.max(max, version.length), 0);
|
||||
|
||||
return all
|
||||
.map(({ path, version }) => `\t${version.padEnd(verLength)}\t${expandPath(path)}`)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
function getPath (pathOrFn?: FnString | string | false): false | string | undefined {
|
||||
if (isFunction(pathOrFn)) {
|
||||
try {
|
||||
return pathOrFn();
|
||||
} catch (error) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
return pathOrFn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name detectPackage
|
||||
* @summary Checks that a specific package is only imported once
|
||||
*/
|
||||
export function detectPackage ({ name, version }: PackageJson, pathOrFn?: FnString | string | false): void {
|
||||
assert(name.startsWith('@polkadot'), `Invalid package descriptor ${name}`);
|
||||
|
||||
const entry = getEntry(name);
|
||||
|
||||
entry.push({ path: getPath(pathOrFn) || '', version });
|
||||
|
||||
if (entry.length !== 1) {
|
||||
console.warn(`Multiple instances of ${name} detected, ensure that there is only one package in your dependency tree.\n${flattenVersions(entry)}`);
|
||||
}
|
||||
}
|
||||
detectPackage(packageInfo, typeof __dirname !== 'undefined' && __dirname, [decoderInfo, encoderInfo]);
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
// Copyright 2017-2021 @polkadot/util authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import './detectPackage';
|
||||
|
||||
/**
|
||||
* @summary Utility methods for this package are split into groups
|
||||
*/
|
||||
|
||||
export { assert, assertReturn } from './assert';
|
||||
export { detectPackage } from './detectPackage';
|
||||
export { extractTime } from './extractTime';
|
||||
export { logger } from './logger';
|
||||
export { memoize } from './memoize';
|
||||
export { promisify } from './promisify';
|
||||
export { packageInfo } from './packageInfo';
|
||||
|
||||
export * from './array';
|
||||
export * from './assert';
|
||||
export * from './bn';
|
||||
export * from './buffer';
|
||||
export * from './compact';
|
||||
export * from './extractTime';
|
||||
export * from './format';
|
||||
export * from './hex';
|
||||
export * from './is';
|
||||
export * from './logger';
|
||||
export * from './memoize';
|
||||
export * from './number';
|
||||
export * from './promisify';
|
||||
export * from './string';
|
||||
export * from './u8a';
|
||||
export * from './versionDetect';
|
||||
|
||||
@@ -5,8 +5,7 @@ import type { Logger } from './types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
|
||||
import { format } from './logger';
|
||||
import { logger } from '.';
|
||||
import { logger, loggerFormat } from '.';
|
||||
|
||||
describe('logger', (): void => {
|
||||
let dateMatch: unknown;
|
||||
@@ -44,13 +43,13 @@ describe('logger', (): void => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
describe('format', (): void => {
|
||||
describe('loggerFormat', (): void => {
|
||||
it('returns null as-is', (): void => {
|
||||
expect(format(null)).toEqual(null);
|
||||
expect(loggerFormat(null)).toEqual(null);
|
||||
});
|
||||
|
||||
it('returns undefined as-is', (): void => {
|
||||
expect(format(undefined)).toEqual(undefined);
|
||||
expect(loggerFormat(undefined)).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user