Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0480bfcbc5
|
||
|
|
a5157ef592
|
||
|
|
e1d88c6f63
|
||
|
|
7f23d0a609
|
||
|
|
49f3442e5c
|
||
|
|
b39241c840
|
||
|
|
74e1e0cc6a
|
||
|
|
e55352aa4e
|
||
|
|
f7dc72e32c
|
||
|
|
506b77351c
|
||
|
|
2bffbc62bc
|
||
|
|
c616a0d949
|
||
|
|
6ed8b1ea6d
|
||
|
|
9281daa14c | ||
|
|
c7ed69fe55 | ||
|
|
22a1246672 | ||
|
|
4a5b6acc78 | ||
|
|
152d4609a4 | ||
|
|
4ab957e568 | ||
|
|
610c8bb15f | ||
|
|
372d460f1a | ||
|
|
a8732a99aa | ||
|
|
8d3cb0eb6b | ||
|
|
fe0886be23 | ||
|
|
bac357ae10 | ||
|
|
4ab7a5bfe0 | ||
|
|
440b146a24 | ||
|
|
ae0735ccda | ||
|
|
57551b38a0 | ||
|
|
bf63a0ebf6 | ||
|
|
d78344d000 | ||
|
|
169a702c88 | ||
|
|
b4d73ef49a | ||
|
|
21ef853fbd | ||
|
|
a4d940cb69 | ||
|
|
6423537dbe | ||
|
|
61125b3582 | ||
|
|
7d77f89f34 | ||
|
|
bee7204f95 | ||
|
|
bb0715ec2f | ||
|
|
2212f31f9a | ||
|
|
047840319e | ||
|
|
3fbf0b98c2 | ||
|
|
e6b0411e9c | ||
|
|
6e8199bab7 | ||
|
|
8ca4525570 | ||
|
|
0c3a71145a | ||
|
|
9c1be4f19b | ||
|
|
e5e6bdd814 | ||
|
|
55a3201bd5 | ||
|
|
de2538c18d | ||
|
|
e8dced9fed | ||
|
|
0afe1144d1 | ||
|
|
5a04d67bb2 | ||
|
|
41481c1e3a | ||
|
|
8a8b583f81 | ||
|
|
03c8169cd9 | ||
|
|
f9ee358442 | ||
|
|
ef5cfc728c | ||
|
|
88b8a54018 | ||
|
|
8c666725b2 | ||
|
|
daabdfac45 | ||
|
|
538359fd49 | ||
|
|
df044669e2 | ||
|
|
60374f417a | ||
|
|
5235009f4c | ||
|
|
21bfff5a51 | ||
|
|
963025daab | ||
|
|
ec7ef17b64 | ||
|
|
ef94fed1fe | ||
|
|
43ac361806 | ||
|
|
2bc18cfdbb | ||
|
|
be7df03e7c | ||
|
|
0ae44ed34e | ||
|
|
09d54ea200 | ||
|
|
457ea1a0b7 | ||
|
|
972363a5bb | ||
|
|
e59fe6fd22 | ||
|
|
35f9f4468b | ||
|
|
802174d6ad | ||
|
|
d690abea02 | ||
|
|
26a762ae2e | ||
|
|
9d49bcde75 | ||
|
|
8b0f5bf46e | ||
|
|
a75d8761da | ||
|
|
a58fb0bbff | ||
|
|
0467c410c8 | ||
|
|
095095074e | ||
|
|
18a6764841 | ||
|
|
0e0de687c3 | ||
|
|
6197a897aa | ||
|
|
0f7954a89b | ||
|
|
e056618d2e | ||
|
|
82a485cc9c | ||
|
|
2cbea7bf8d | ||
|
|
748f670351 | ||
|
|
5b2749073f | ||
|
|
a328ab4409 | ||
|
|
e25bb48ba9 | ||
|
|
8456870148 | ||
|
|
6f8caad80f | ||
|
|
25fe5f4e27 | ||
|
|
da2edf0aa0 | ||
|
|
eb7b24f9aa | ||
|
|
f4e93cf2c2 | ||
|
|
85b2da734c | ||
|
|
5d03be1084 | ||
|
|
2261862097 | ||
|
|
20d3af56ec | ||
|
|
367633a612 | ||
|
|
b127f678e9 | ||
|
|
3404ad2945 | ||
|
|
4059615d66 | ||
|
|
be9739ee9f | ||
|
|
6ec09907a2 | ||
|
|
677a778833 | ||
|
|
fa90f0a747 | ||
|
|
e721c4f508 | ||
|
|
7f5bd09322 | ||
|
|
5d6244fc0b | ||
|
|
58f12c4257 | ||
|
|
038d20b5e7 | ||
|
|
c9d8c52567 | ||
|
|
3b85773636 | ||
|
|
269740b775 | ||
|
|
fc732b8c44 | ||
|
|
2c4deb3f9d | ||
|
|
ebcb6857ad | ||
|
|
0ff31d7980 | ||
|
|
d897f25546 | ||
|
|
3c431d5a01 | ||
|
|
55ae71125d | ||
|
|
fcd8cee7f6 | ||
|
|
8aa4e60c4c | ||
|
|
85665dfa20 | ||
|
|
5f8f3e6309 | ||
|
|
166845e367 | ||
|
|
7c7233efd1 | ||
|
|
cb7f0da916 | ||
|
|
7d654c236f | ||
|
|
6b4d9aac29 | ||
|
|
4f863ddc29 | ||
|
|
a4a85a3a5d | ||
|
|
2a6669c25d | ||
|
|
cb4c9fd90a | ||
|
|
28beb44374 | ||
|
|
4a4847b032 | ||
|
|
506c20a900 | ||
|
|
736656f34f | ||
|
|
31274916f9 | ||
|
|
6e1afdc2f8 | ||
|
|
fd26d223be | ||
|
|
86d8c81c27 | ||
|
|
2de22cbb31 | ||
|
|
c1b8098b9e | ||
|
|
ee0ed25a83 | ||
|
|
7ef452bdf0 | ||
|
|
eb41ab5bed | ||
|
|
6947b1e4d9 | ||
|
|
527b4f0eeb | ||
|
|
44f7443d13 | ||
|
|
2b800577a5 | ||
|
|
c9ca01e96b | ||
|
|
ca93f3337f |
@@ -18,9 +18,11 @@ jobs:
|
||||
node-version: 'lts/*'
|
||||
- uses: denoland/setup-deno@v1
|
||||
with:
|
||||
deno-version: vx.x.x
|
||||
# Deno v2 throws errors with specific types therefore we set it to the last version before v2
|
||||
deno-version: v1.46.3
|
||||
- name: ${{ matrix.step }}
|
||||
if: always()
|
||||
run: |
|
||||
deno --version
|
||||
yarn install --immutable
|
||||
yarn ${{ matrix.step }}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/util authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/util authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
module.exports = require('@polkadot/dev/config/prettier.cjs');
|
||||
|
||||
Vendored
-894
File diff suppressed because one or more lines are too long
+948
File diff suppressed because one or more lines are too long
+10
-1
@@ -12,4 +12,13 @@ logFilters:
|
||||
|
||||
nodeLinker: node-modules
|
||||
|
||||
yarnPath: .yarn/releases/yarn-4.3.1.cjs
|
||||
yarnPath: .yarn/releases/yarn-4.9.1.cjs
|
||||
|
||||
# @quantus packages come from the Gitea registry. Safe as a whole-scope route
|
||||
# because we own every name in it — unlike @polkadot, where routing the scope
|
||||
# would send yarn looking for dozens of unforked packages that are not there.
|
||||
#
|
||||
# Reads are anonymous; publishing needs `pass gitea/package-publisher-token`.
|
||||
npmScopes:
|
||||
quantus:
|
||||
npmRegistryServer: "https://git.lair.cafe/api/packages/quantus/npm/"
|
||||
|
||||
+202
@@ -1,5 +1,207 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 14.0.3 Mar 23, 2026
|
||||
|
||||
Changes:
|
||||
|
||||
- Fix(hw-ledger): reset transport on operation errors and add explicit disconnect API ([#2024](https://github.com/polkadot-js/common/pull/2024))
|
||||
|
||||
|
||||
## 14.0.2 Mar 16, 2026
|
||||
|
||||
Changes:
|
||||
|
||||
- Set headers to 2026 ([#2021](https://github.com/polkadot-js/common/pull/2021))
|
||||
|
||||
Contributed:
|
||||
|
||||
- fix(util-crypto): use correct x/y getters in secp256k1Expand ([#2018](https://github.com/polkadot-js/common/pull/2018)) (Thanks to https://github.com/AndreasGassmann)
|
||||
|
||||
|
||||
## 14.0.1 Dec 9, 2025
|
||||
|
||||
Changes:
|
||||
|
||||
- fix(x-randomvalues): prioritize native RN modules over polyfilled crypto ([#2013](https://github.com/polkadot-js/common/pull/2013))
|
||||
- sr25519: switch from wasm to micro-sr25519 ([#1971](https://github.com/polkadot-js/common/pull/1971))
|
||||
|
||||
|
||||
## 13.5.9 Nov 25, 2025
|
||||
|
||||
Changes:
|
||||
|
||||
- Bump polkadot dependencies ([#2011](https://github.com/polkadot-js/common/pull/2011))
|
||||
|
||||
|
||||
## 13.5.8 Nov 11, 2025
|
||||
|
||||
Changes:
|
||||
|
||||
- Bump @polkadot dependencies ([#2009](https://github.com/polkadot-js/common/pull/2009))
|
||||
|
||||
|
||||
## 13.5.7 Oct 13, 2025
|
||||
|
||||
Changes:
|
||||
|
||||
- Revert "Set pbkdf2Encode rounds to default to 210,000" ([#2007](https://github.com/polkadot-js/common/pull/2007))
|
||||
|
||||
|
||||
## 13.5.6 Aug 26, 2025
|
||||
|
||||
Changes:
|
||||
|
||||
- Set pbkdf2Encode rounds to default to 210,000 ([#1983](https://github.com/polkadot-js/common/pull/1983))
|
||||
- Bump @polkadot/wasm deps ([#2002](https://github.com/polkadot-js/common/pull/2002))
|
||||
|
||||
Contributed:
|
||||
|
||||
- Add DENTNet to generic Polkadot app supported chains ([#1942](https://github.com/polkadot-js/common/pull/1942))
|
||||
|
||||
## 13.5.5 Aug 11, 2025
|
||||
|
||||
Changes:
|
||||
|
||||
- Add ledger support for Mythos ([#1969](https://github.com/polkadot-js/common/pull/1969))
|
||||
|
||||
## 13.5.4 July 28, 2025
|
||||
|
||||
Changes:
|
||||
|
||||
- Add Creditcoin3 support to Generic Ledger App ([#1985](https://github.com/polkadot-js/common/pull/1985))
|
||||
|
||||
## 13.5.3 July 1, 2025
|
||||
|
||||
Changes:
|
||||
|
||||
- Update polkadot dependencies ([#1995](https://github.com/polkadot-js/common/pull/1995))
|
||||
- Add Optional Strict Input Checking to u8aToU8a ([#1994](https://github.com/polkadot-js/common/pull/1994))
|
||||
- Update scrypt parameter validation ([#1993](https://github.com/polkadot-js/common/pull/1993))
|
||||
|
||||
## 13.5.2 Jun 17, 2025
|
||||
|
||||
Changes:
|
||||
|
||||
- fix: enhance error handling for secure random number generation in React Native ([#1991](https://github.com/polkadot-js/common/pull/1991))
|
||||
- Add input length validation in scryptFromU8a function ([#1990](https://github.com/polkadot-js/common/pull/1990))
|
||||
|
||||
Contributed:
|
||||
|
||||
- Add Liberland support to Generic Ledger App ([#1989](https://github.com/polkadot-js/common/pull/1989))
|
||||
|
||||
## 13.5.1 May 19, 2025
|
||||
|
||||
Changes:
|
||||
|
||||
- Ledger ECDSA signing support ([#1986](https://github.com/polkadot-js/common/pull/1986))
|
||||
- Bump yarn to 4.9.1 ([#1987](https://github.com/polkadot-js/common/pull/1987))
|
||||
|
||||
|
||||
## 13.4.4 Apr 14, 2025
|
||||
|
||||
Changes:
|
||||
|
||||
- Improve objectSpread function property handling ([#1982](https://github.com/polkadot-js/common/pull/1982))
|
||||
|
||||
|
||||
## 13.4.3 Feb 17, 2025
|
||||
|
||||
Changes:
|
||||
|
||||
- Rollback ws change in x-ws ([#1980](https://github.com/polkadot-js/common/pull/1980))
|
||||
NOTE: this rollbacks changes made in ([#1976](https://github.com/polkadot-js/common/pull/1976)) to ensure websocket usage doesnt break in any downstream libs.
|
||||
|
||||
|
||||
## 13.4.2 Feb 17, 2025
|
||||
|
||||
Changes:
|
||||
|
||||
- Cast ws as global.WebSocket ([#1978](https://github.com/polkadot-js/common/pull/1978))
|
||||
|
||||
|
||||
## 13.4.1 Feb 16, 2025
|
||||
|
||||
Changes:
|
||||
|
||||
- Enable KeyringPair Creation with Custom Mnemonic Wordlists ([#1974](https://github.com/polkadot-js/common/pull/1974))
|
||||
- Use ws library in Node.js 22+ for Better WebSocket Error Handling ([#1976](https://github.com/polkadot-js/common/pull/1976))
|
||||
- Fix Misclassification of ECDSA Signatures in `verifyMultisig` ([#1973](https://github.com/polkadot-js/common/pull/1973))
|
||||
- Bump dev to 0.83.2 ([#1972](https://github.com/polkadot-js/common/pull/1972))
|
||||
- Bump yarn to 4.6.0 ([#1967](https://github.com/polkadot-js/common/pull/1967))
|
||||
|
||||
|
||||
## 13.3.1 Jan 6, 2025
|
||||
|
||||
Changes:
|
||||
|
||||
- Give compatibility for nodejs v22
|
||||
- Fix `getSeparator` which fixes toLocaleString ([#1965](https://github.com/polkadot-js/common/pull/1965))
|
||||
- 2025 Headers ([#1963](https://github.com/polkadot-js/common/pull/1963))
|
||||
|
||||
|
||||
## 13.2.3 Nov 11, 2024
|
||||
|
||||
Contributed:
|
||||
|
||||
- Add Vara support to the Generic Ledger App ([#1958](https://github.com/polkadot-js/common/pull/1958)) (Thanks to https://github.com/breathx)
|
||||
|
||||
Changes:
|
||||
|
||||
- Bump Dev to 0.82.1 ([#1959](https://github.com/polkadot-js/common/pull/1959))
|
||||
|
||||
|
||||
## 13.2.2 Oct 28, 2024
|
||||
|
||||
Contributed:
|
||||
|
||||
- Add Polimec support to the Generic Ledger App ([#1945](https://github.com/polkadot-js/common/pull/1945)) (Thanks to https://github.com/lrazovic)
|
||||
- Add Frequency to the Generic Ledger App ([#1955](https://github.com/polkadot-js/common/pull/1955)) (Thanks to https://github.com/wilwade)
|
||||
|
||||
Changes:
|
||||
|
||||
- Bump yarn to 4.5.1 ([#1953](https://github.com/polkadot-js/common/pull/1953))
|
||||
|
||||
|
||||
## 13.2.1 Oct 21, 2024
|
||||
|
||||
Contributed:
|
||||
|
||||
- Add bittensor to genericLedgerApps (Thanks to https://github.com/camfairchild) ([#1941](https://github.com/polkadot-js/common/pull/1941))
|
||||
|
||||
Changes:
|
||||
|
||||
- Fix Deno CI Build ([#1947](https://github.com/polkadot-js/common/pull/1947))
|
||||
- Bump yarn to 4.5.0 ([#1946](https://github.com/polkadot-js/common/pull/1946))
|
||||
- Update Ledger Deps ([#1948](https://github.com/polkadot-js/common/pull/1948))
|
||||
- Update WS and deps ([#1949](https://github.com/polkadot-js/common/pull/1949))
|
||||
- Bump polkadot/wasm to 7.4.1 ([#1950](https://github.com/polkadot-js/common/pull/1950))
|
||||
- Bump dev to 0.81.2 ([#1951](https://github.com/polkadot-js/common/pull/1951))
|
||||
- Allows for CJS target in build
|
||||
|
||||
|
||||
## 13.1.1 Sep 16, 2024
|
||||
|
||||
Contributed:
|
||||
|
||||
HydraDx - Hydration Rebrand Thanks to (https://github.com/jak-pan) ([#1923](https://github.com/polkadot-js/common/pull/1923))
|
||||
|
||||
Changes:
|
||||
|
||||
- Update all deps that have patch releases ([#1934](https://github.com/polkadot-js/common/pull/1934))
|
||||
- Bump yarn to 4.4.1 ([#1940](https://github.com/polkadot-js/common/pull/1940))
|
||||
- Update ss58-registry to 1.50.0 ([#1937](https://github.com/polkadot-js/common/pull/1937))
|
||||
- Update dev and typescript ([#1936](https://github.com/polkadot-js/common/pull/1936))
|
||||
- Add Encointer and Integritee to Ledger, and adjust tests ([#1939](https://github.com/polkadot-js/common/pull/1939))
|
||||
- This PR added the addition of `genericLedgerApps` and `prevLedgerRecord` objects. They both combine into `ledgerApps` in `hw-ledger/defaults`.
|
||||
|
||||
|
||||
## 13.0.2 July 12, 2024
|
||||
|
||||
Contributed:
|
||||
|
||||
- Add peaq support for ledger (Thanks to https://github.com/lavish0000)
|
||||
|
||||
|
||||
## 13.0.1 July 12, 2024
|
||||
|
||||
Breaking Changes:
|
||||
|
||||
+17
-4
@@ -1,14 +1,18 @@
|
||||
1674 Jaco Bump deps (#1912)
|
||||
47 Tarik Gul 14.0.3 (#2025)
|
||||
13 Carlo Sala feat(networks): add zeitgeist Ledger support (#1840)
|
||||
11 Valentin Fernandez 13.5.7 (#2008)
|
||||
9 Antoine Estienne Add eth uri keyring test (#1291)
|
||||
9 Tarik Gul 13.0.1 (#1927)
|
||||
9 rajk93 14.0.1 (#2014)
|
||||
6 Amaury Martiny Add tests from Rust (#283)
|
||||
5 Jakub Pánik Add HydraDX parachain to genesis (#1583)
|
||||
6 Jakub Pánik HydraDX -> Hydration rebrand (#1923)
|
||||
5 Arjun Porwal 13.5.6 (#2004)
|
||||
5 kwingram25 Add BN consts and timeToString (#610)
|
||||
4 Adam Dossa Update Polymesh Genesis Hash (#1193)
|
||||
4 pan add bifrost kusama (#1626)
|
||||
3 AndreasGassmann fix(util-crypto): use correct x/y getters in secp256k1Expand (#2018)
|
||||
3 Luke Schoen docs: Fix example in readme since we use default imports (#243)
|
||||
2 AndreasGassmann feat(dependencies): remove moment.js (#530)
|
||||
2 Cameron Fairchild Add bittensor details (#1783)
|
||||
2 Gav Wood Fix stake:unstake (#182)
|
||||
2 Hyungsuk Kang Add cross client Encryption/Decryption (#496)
|
||||
2 Jared Norris Add Sora network (#1306)
|
||||
@@ -24,40 +28,49 @@
|
||||
1 amphineko chore: fix typo in versionDetect messages (#941)
|
||||
1 Axel Chalon Simplify @polkadot/keyring KeyringPair (#419)
|
||||
1 Brad Larson [FIX] Adjust error message (#646)
|
||||
1 Cameron Fairchild Add bittensor details (#1783)
|
||||
1 Carlos Medeiros add hydradx to knownLedger (#1647)
|
||||
1 Chris Get rid of warnings for dual esm and cjs packages of the same version (#1890)
|
||||
1 Dmitrii Novikov Add `Vara` support to Generic Ledger App (#1958)
|
||||
1 Eliott Teissonniere add nodle ledger specs (#1089)
|
||||
1 Francis O'Brien Fix(hw-ledger): reset transport on operation errors and add explicit disconnect API (#2024)
|
||||
1 Guilane DENGUIR feat(VTB): Added support for VTB blockchain (#1651)
|
||||
1 h4x3rotab Throws an error when signing with a locked keypair (#578)
|
||||
1 Harry Liu Update README.md (#446)
|
||||
1 henrikdent Add DENTNet ledger support (via generic Polkadot app) (#1942)
|
||||
1 Hoon Kim replace plasm genesis with astar (#1397)
|
||||
1 Hussein Ait-Lahcen Add Picasso Genesis Hash (#1261)
|
||||
1 Igor Support genshiro network (#1148)
|
||||
1 Jake Naviasky Add evmToAddress and addressToEvm crypto utils. (#705)
|
||||
1 JesseAbram added check if valid address to docs (#440)
|
||||
1 joe petrowski Add example for multisig address creation (#590)
|
||||
1 José Molina Colmenero Add ledger support for Mythos (#1969)
|
||||
1 kaelnew add ledger support for stafi (#1569)
|
||||
1 Leonardo Custodio Ledger support (#1879)
|
||||
1 Leonardo Razovic feat: add Polimec (#1945)
|
||||
1 Lezek123 logger - wildcard include/exclude with DEBUG env (#932)
|
||||
1 Lovish Arora Add peaq support for ledger (#1928)
|
||||
1 Mario Neises remove redudant export (#257)
|
||||
1 Miguel Hervas Add CFG Ledger Support (#1057)
|
||||
1 Mikhail Fedosov feat: add 3dpass network (#1769)
|
||||
1 Mohsan Riaz Add Ternoa for ledger hardware support (#1711)
|
||||
1 nahuseyoum Reserving 65 for Aventus based on the new extended range (#871)
|
||||
1 Nantian add acala ledger config (#1485)
|
||||
1 Paul Miller sr25519: switch from wasm to micro-sr25519 (#1971)
|
||||
1 peetzweg/ adds `isRiscV` guard function similar to isWasm (#1878)
|
||||
1 qwer951123 Add acala genesis (#1161)
|
||||
1 Radek Bochenek feat: add Liberland support to Generic Ledger App (#1989)
|
||||
1 Raoul Millais Fix yarn add instructions in x-global README (#873)
|
||||
1 Raymond Zhong Add Edgeware ledger app (#1006)
|
||||
1 Reto Trinkler Adding 4 as address type of ss58 for Katal Chain (#639)
|
||||
1 Rocco Musolino remove doc broken link (#697)
|
||||
1 Salina Add Creditcoin3 support to Generic Ledger App (#1985)
|
||||
1 Shamilkhan Add genesis hash for Cere Network (#1806)
|
||||
1 Song Zhou Add in ability to encrypt/decrypt msg with keyring (#1070)
|
||||
1 Thibaut Sardan add hasLedgerSupport prop to networks (#835)
|
||||
1 Tomokazu Kozuma fix comment (#1119)
|
||||
1 Tore19 Add stafi genesis hash (#1096)
|
||||
1 Wei Tang ss58: add Kulupu prefix to the list (#595)
|
||||
1 Wil Wade Add Frequency to the Generic Ledger App (#1955)
|
||||
1 WoeOm Handing high precision scenes (#450)
|
||||
1 xx network Add xxnetwork genesis hash and ledger app (#1463)
|
||||
1 Yaroslav Bolyukin Add quartz support for ledger, update unique slip-0044 identifier (#1882)
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/util authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/util authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import baseConfig from '@polkadot/dev/config/eslint';
|
||||
|
||||
+8
-9
@@ -6,7 +6,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/polkadot-js/common#readme",
|
||||
"license": "Apache-2.0",
|
||||
"packageManager": "yarn@4.3.1",
|
||||
"packageManager": "yarn@4.9.1",
|
||||
"private": true,
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -14,10 +14,10 @@
|
||||
},
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"version": "13.0.1",
|
||||
"version": "14.0.3",
|
||||
"versions": {
|
||||
"git": "13.0.1",
|
||||
"npm": "13.0.1"
|
||||
"git": "14.0.3",
|
||||
"npm": "14.0.3"
|
||||
},
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
@@ -31,16 +31,15 @@
|
||||
"deno": "yarn polkadot-dev-deno-map && yarn build && deno check --import-map=import_map.json mod.ts",
|
||||
"lint": "polkadot-dev-run-lint",
|
||||
"postinstall": "polkadot-dev-yarn-only",
|
||||
"test": "polkadot-dev-run-test --env browser ^mnemonic/toMiniSecretCmp",
|
||||
"test:mnemonicCmp": "polkadot-dev-run-test --env browser mnemonic/toMiniSecretCmp",
|
||||
"test": "polkadot-dev-run-test --env browser",
|
||||
"test:node": "polkadot-dev-run-test --env browser ^mnemonic/toMiniSecretCmp",
|
||||
"test:one": "polkadot-dev-run-test --env browser"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@polkadot/dev": "^0.79.1",
|
||||
"@types/node": "^20.11.5"
|
||||
"@polkadot/dev": "^0.83.3",
|
||||
"@types/node": "^22.7.5"
|
||||
},
|
||||
"resolutions": {
|
||||
"typescript": "^5.3.3"
|
||||
"typescript": "^5.5.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,18 +14,18 @@
|
||||
},
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"version": "13.0.1",
|
||||
"version": "14.0.3",
|
||||
"browser": "browser.js",
|
||||
"main": "node.js",
|
||||
"react-native": "react-native.js",
|
||||
"dependencies": {
|
||||
"@ledgerhq/hw-transport": "^6.31.0",
|
||||
"@ledgerhq/hw-transport-webhid": "^6.29.0",
|
||||
"@ledgerhq/hw-transport-webusb": "^6.29.0",
|
||||
"@polkadot/util": "13.0.1",
|
||||
"tslib": "^2.6.2"
|
||||
"@ledgerhq/hw-transport": "^6.31.4",
|
||||
"@ledgerhq/hw-transport-webhid": "^6.29.4",
|
||||
"@ledgerhq/hw-transport-webusb": "^6.29.4",
|
||||
"@polkadot/util": "14.0.3",
|
||||
"tslib": "^2.8.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@ledgerhq/hw-transport-node-hid-singleton": "^6.31.1"
|
||||
"@ledgerhq/hw-transport-node-hid-singleton": "^6.31.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/hw-ledger authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/hw-ledger authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import LedgerHid from '@ledgerhq/hw-transport-webhid';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/hw-ledger authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/hw-ledger authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { createDefs } from './util.js';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/hw-ledger authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/hw-ledger authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export * from './empty.js';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/hw-ledger authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/hw-ledger authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import LedgerHid from '@ledgerhq/hw-transport-node-hid-singleton';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/hw-ledger-transports authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/hw-ledger-transports authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Do not edit, auto-generated by @polkadot/dev
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright 2017-2024 @polkadot/hw-ledger-transports authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/hw-ledger-transports authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Do not edit, auto-generated by @polkadot/dev
|
||||
|
||||
export const packageInfo = { name: '@polkadot/hw-ledger-transports', path: 'auto', type: 'auto', version: '13.0.1' };
|
||||
export const packageInfo = { name: '@polkadot/hw-ledger-transports', path: 'auto', type: 'auto', version: '14.0.3' };
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/hw-ledger authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/hw-ledger authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export * from './empty.js';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/hw-ledger authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/hw-ledger authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// CJS, so we use import * syntax
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/hw-ledger authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/hw-ledger authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Transport, TransportDef, TransportType } from './types.js';
|
||||
|
||||
@@ -17,12 +17,12 @@
|
||||
"./packageDetect.cjs"
|
||||
],
|
||||
"type": "module",
|
||||
"version": "13.0.1",
|
||||
"version": "14.0.3",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@polkadot/hw-ledger-transports": "13.0.1",
|
||||
"@polkadot/util": "13.0.1",
|
||||
"@zondax/ledger-substrate": "0.44.4",
|
||||
"tslib": "^2.6.2"
|
||||
"@polkadot/hw-ledger-transports": "14.0.3",
|
||||
"@polkadot/util": "14.0.3",
|
||||
"@zondax/ledger-substrate": "1.1.1",
|
||||
"tslib": "^2.8.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2017-2024 @polkadot/hw-ledger authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/hw-ledger authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { SubstrateApp } from '@zondax/ledger-substrate';
|
||||
import type { TransportDef, TransportType } from '@polkadot/hw-ledger-transports/types';
|
||||
import type { Transport, TransportDef, TransportType } from '@polkadot/hw-ledger-transports/types';
|
||||
import type { AccountOptions, LedgerAddress, LedgerSignature, LedgerVersion } from './types.js';
|
||||
|
||||
import { newSubstrateApp } from '@zondax/ledger-substrate';
|
||||
@@ -30,6 +30,17 @@ async function wrapError <T extends WrappedResult> (promise: Promise<T>): Promis
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @internal Best-effort transport cleanup after failed operations */
|
||||
async function closeTransport (transport: Transport | null): Promise<void> {
|
||||
if (transport) {
|
||||
try {
|
||||
await transport.close();
|
||||
} catch {
|
||||
// Ignore cleanup errors so the original failure is preserved.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal Wraps a sign/signRaw call and returns the associated signature */
|
||||
function sign (method: 'sign' | 'signRaw', message: Uint8Array, accountOffset = 0, addressOffset = 0, { account = LEDGER_DEFAULT_ACCOUNT, addressIndex = LEDGER_DEFAULT_INDEX, change = LEDGER_DEFAULT_CHANGE }: Partial<AccountOptions> = {}): (app: SubstrateApp) => Promise<LedgerSignature> {
|
||||
return async (app: SubstrateApp): Promise<LedgerSignature> => {
|
||||
@@ -114,16 +125,29 @@ export class Ledger {
|
||||
return this.withApp(sign('signRaw', u8aWrapBytes(message), accountOffset, addressOffset, options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes any active transport connection
|
||||
*/
|
||||
public async disconnect (): Promise<void> {
|
||||
const app = this.#app;
|
||||
|
||||
this.#app = null;
|
||||
|
||||
await closeTransport(app?.transport || null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* Returns a created SubstrateApp to perform operations against. Generally
|
||||
* this is only used internally, to ensure consistent bahavior.
|
||||
* this is only used internally, to ensure consistent behavior.
|
||||
*/
|
||||
async withApp <T> (fn: (app: SubstrateApp) => Promise<T>): Promise<T> {
|
||||
let transport: Transport | null = null;
|
||||
|
||||
try {
|
||||
if (!this.#app) {
|
||||
const transport = await this.#transportDef.create();
|
||||
transport = await this.#transportDef.create();
|
||||
|
||||
// We need this override for the actual type passing - the Deno environment
|
||||
// is quite a bit stricter and it yields invalids between the two (specifically
|
||||
@@ -136,6 +160,7 @@ export class Ledger {
|
||||
|
||||
return await fn(this.#app);
|
||||
} catch (error) {
|
||||
await closeTransport(this.#app?.transport || transport);
|
||||
this.#app = null;
|
||||
|
||||
throw error;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2017-2024 @polkadot/hw-ledger authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/hw-ledger authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { TransportDef, TransportType } from '@polkadot/hw-ledger-transports/types';
|
||||
import type { Transport, TransportDef, TransportType } from '@polkadot/hw-ledger-transports/types';
|
||||
import type { AccountOptionsGeneric, LedgerAddress, LedgerSignature, LedgerVersion } from './types.js';
|
||||
|
||||
import { PolkadotGenericApp } from '@zondax/ledger-substrate';
|
||||
@@ -33,7 +33,7 @@ async function wrapError <T extends WrappedResult> (promise: Promise<T>): Promis
|
||||
try {
|
||||
result = await promise;
|
||||
} catch (e: unknown) {
|
||||
// We check to see if the propogated error is the newer ResponseError type.
|
||||
// We check to see if the propagated error is the newer ResponseError type.
|
||||
// The response code use to be part of the result, but with the latest breaking changes from 0.42.x
|
||||
// the interface and it's types have completely changed.
|
||||
if ((e as ResponseError).returnCode) {
|
||||
@@ -46,8 +46,19 @@ async function wrapError <T extends WrappedResult> (promise: Promise<T>): Promis
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @internal Wraps a sign/signRaw call and returns the associated signature */
|
||||
function sign (method: 'sign' | 'signRaw', message: Uint8Array, slip44: number, accountIndex = 0, addressOffset = 0): (app: PolkadotGenericApp) => Promise<LedgerSignature> {
|
||||
/** @internal Best-effort transport cleanup after failed operations */
|
||||
async function closeTransport (transport: Transport | null): Promise<void> {
|
||||
if (transport) {
|
||||
try {
|
||||
await transport.close();
|
||||
} catch {
|
||||
// Ignore cleanup errors so the original failure is preserved.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal Wraps a signEd25519/signRawEd25519 call and returns the associated signature */
|
||||
function sign (method: 'signEd25519' | 'signRawEd25519', message: Uint8Array, slip44: number, accountIndex = 0, addressOffset = 0): (app: PolkadotGenericApp) => Promise<LedgerSignature> {
|
||||
const bip42Path = `m/44'/${slip44}'/${accountIndex}'/${0}'/${addressOffset}'`;
|
||||
|
||||
return async (app: PolkadotGenericApp): Promise<LedgerSignature> => {
|
||||
@@ -59,7 +70,22 @@ function sign (method: 'sign' | 'signRaw', message: Uint8Array, slip44: number,
|
||||
};
|
||||
}
|
||||
|
||||
/** @internal Wraps a signWithMetadata call and returns the associated signature */
|
||||
/** @internal Wraps a signEcdsa/signRawEcdsa call and returns the associated signature */
|
||||
function signEcdsa (method: 'signEcdsa' | 'signRawEcdsa', message: Uint8Array, slip44: number, accountIndex = 0, addressOffset = 0): (app: PolkadotGenericApp) => Promise<LedgerSignature> {
|
||||
const bip42Path = `m/44'/${slip44}'/${accountIndex}'/${0}'/${addressOffset}'`;
|
||||
|
||||
return async (app: PolkadotGenericApp): Promise<LedgerSignature> => {
|
||||
const { r, s, v } = await wrapError(app[method](bip42Path, u8aToBuffer(message)));
|
||||
|
||||
const signature = Buffer.concat([r, s, v]);
|
||||
|
||||
return {
|
||||
signature: hexAddPrefix(signature.toString('hex'))
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/** @internal Wraps a signWithMetadataEd25519 call and returns the associated signature */
|
||||
function signWithMetadata (message: Uint8Array, slip44: number, accountIndex = 0, addressOffset = 0, { metadata }: Partial<AccountOptionsGeneric> = {}): (app: PolkadotGenericApp) => Promise<LedgerSignature> {
|
||||
const bip42Path = `m/44'/${slip44}'/${accountIndex}'/${0}'/${addressOffset}'`;
|
||||
|
||||
@@ -70,7 +96,28 @@ function signWithMetadata (message: Uint8Array, slip44: number, accountIndex = 0
|
||||
|
||||
const bufferMsg = Buffer.from(message);
|
||||
|
||||
const { signature } = await wrapError(app.signWithMetadata(bip42Path, bufferMsg, metadata));
|
||||
const { signature } = await wrapError(app.signWithMetadataEd25519(bip42Path, bufferMsg, metadata));
|
||||
|
||||
return {
|
||||
signature: hexAddPrefix(signature.toString('hex'))
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/** @internal Wraps a signWithMetadataEcdsa call and returns the associated signature */
|
||||
function signWithMetadataEcdsa (message: Uint8Array, slip44: number, accountIndex = 0, addressOffset = 0, { metadata }: Partial<AccountOptionsGeneric> = {}): (app: PolkadotGenericApp) => Promise<LedgerSignature> {
|
||||
const bip42Path = `m/44'/${slip44}'/${accountIndex}'/${0}'/${addressOffset}'`;
|
||||
|
||||
return async (app: PolkadotGenericApp): Promise<LedgerSignature> => {
|
||||
if (!metadata) {
|
||||
throw new Error('The metadata option must be present when using signWithMetadata');
|
||||
}
|
||||
|
||||
const bufferMsg = Buffer.from(message);
|
||||
|
||||
const { r, s, v } = await wrapError(app.signWithMetadataEcdsa(bip42Path, bufferMsg, metadata));
|
||||
|
||||
const signature = Buffer.concat([r, s, v]);
|
||||
|
||||
return {
|
||||
signature: hexAddPrefix(signature.toString('hex'))
|
||||
@@ -119,14 +166,31 @@ export class LedgerGeneric {
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns the address associated with a specific account & address offset. Optionally
|
||||
* @description Returns the address associated with a specific Ed25519 account & address offset. Optionally
|
||||
* asks for on-device confirmation
|
||||
*/
|
||||
public async getAddress (ss58Prefix: number, confirm = false, accountIndex = 0, addressOffset = 0): Promise<LedgerAddress> {
|
||||
const bip42Path = `m/44'/${this.#slip44}'/${accountIndex}'/${0}'/${addressOffset}'`;
|
||||
|
||||
return this.withApp(async (app: PolkadotGenericApp): Promise<LedgerAddress> => {
|
||||
const { address, pubKey } = await wrapError(app.getAddress(bip42Path, ss58Prefix, confirm));
|
||||
const { address, pubKey } = await wrapError(app.getAddressEd25519(bip42Path, ss58Prefix, confirm));
|
||||
|
||||
return {
|
||||
address,
|
||||
publicKey: hexAddPrefix(pubKey)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns the address associated with a specific ecdsa account & address offset. Optionally
|
||||
* asks for on-device confirmation
|
||||
*/
|
||||
public async getAddressEcdsa (confirm = false, accountIndex = 0, addressOffset = 0) {
|
||||
const bip42Path = `m/44'/${this.#slip44}'/${accountIndex}'/${0}'/${addressOffset}'`;
|
||||
|
||||
return this.withApp(async (app: PolkadotGenericApp): Promise<LedgerAddress> => {
|
||||
const { address, pubKey } = await wrapError(app.getAddressEcdsa(bip42Path, confirm));
|
||||
|
||||
return {
|
||||
address,
|
||||
@@ -154,14 +218,28 @@ export class LedgerGeneric {
|
||||
* @description Signs a transaction on the Ledger device. This requires the LedgerGeneric class to be instantiated with `chainId`, and `metaUrl`
|
||||
*/
|
||||
public async sign (message: Uint8Array, accountIndex?: number, addressOffset?: number): Promise<LedgerSignature> {
|
||||
return this.withApp(sign('sign', message, this.#slip44, accountIndex, addressOffset));
|
||||
return this.withApp(sign('signEd25519', message, this.#slip44, accountIndex, addressOffset));
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Signs a message (non-transactional) on the Ledger device
|
||||
*/
|
||||
public async signRaw (message: Uint8Array, accountIndex?: number, addressOffset?: number): Promise<LedgerSignature> {
|
||||
return this.withApp(sign('signRaw', u8aWrapBytes(message), this.#slip44, accountIndex, addressOffset));
|
||||
return this.withApp(sign('signRawEd25519', u8aWrapBytes(message), this.#slip44, accountIndex, addressOffset));
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Signs a transaction on the Ledger device with Ecdsa. This requires the LedgerGeneric class to be instantiated with `chainId`, and `metaUrl`
|
||||
*/
|
||||
public async signEcdsa (message: Uint8Array, accountIndex?: number, addressOffset?: number): Promise<LedgerSignature> {
|
||||
return this.withApp(signEcdsa('signEcdsa', u8aWrapBytes(message), this.#slip44, accountIndex, addressOffset));
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Signs a message with Ecdsa (non-transactional) on the Ledger device
|
||||
*/
|
||||
public async signRawEcdsa (message: Uint8Array, accountIndex?: number, addressOffset?: number): Promise<LedgerSignature> {
|
||||
return this.withApp(signEcdsa('signRawEcdsa', u8aWrapBytes(message), this.#slip44, accountIndex, addressOffset));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,6 +249,24 @@ export class LedgerGeneric {
|
||||
return this.withApp(signWithMetadata(message, this.#slip44, accountIndex, addressOffset, options));
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Signs a transaction on the ledger device for an ecdsa signature provided some metadata.
|
||||
*/
|
||||
public async signWithMetadataEcdsa (message: Uint8Array, accountIndex?: number, addressOffset?: number, options?: Partial<AccountOptionsGeneric>) {
|
||||
return this.withApp(signWithMetadataEcdsa(message, this.#slip44, accountIndex, addressOffset, options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes any active transport connection
|
||||
*/
|
||||
public async disconnect (): Promise<void> {
|
||||
const app = this.#app;
|
||||
|
||||
this.#app = null;
|
||||
|
||||
await closeTransport(app?.transport || null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
@@ -178,9 +274,11 @@ export class LedgerGeneric {
|
||||
* this is only used internally, to ensure consistent bahavior.
|
||||
*/
|
||||
async withApp <T> (fn: (app: PolkadotGenericApp) => Promise<T>): Promise<T> {
|
||||
let transport: Transport | null = null;
|
||||
|
||||
try {
|
||||
if (!this.#app) {
|
||||
const transport = await this.#transportDef.create();
|
||||
transport = await this.#transportDef.create();
|
||||
|
||||
// We need this override for the actual type passing - the Deno environment
|
||||
// is quite a bit stricter and it yields invalids between the two (specifically
|
||||
@@ -193,6 +291,7 @@ export class LedgerGeneric {
|
||||
|
||||
return await fn(this.#app);
|
||||
} catch (error) {
|
||||
await closeTransport(this.#app?.transport || transport);
|
||||
this.#app = null;
|
||||
|
||||
throw error;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/hw-ledger authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/hw-ledger authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// This is necessary to ensure users still have access to class Ledger even though its deprecated.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/hw-ledger authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/hw-ledger authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export const LEDGER_DEFAULT_ACCOUNT = 0x80000000;
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// Copyright 2017-2024 @polkadot/hw-ledger authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/hw-ledger authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
import { supportedApps } from '@zondax/ledger-substrate';
|
||||
|
||||
import { ledgerApps } from './defaults.js';
|
||||
import { prevLedgerRecord } from './defaults.js';
|
||||
|
||||
describe('ledgerApps', (): void => {
|
||||
for (const k of Object.keys(ledgerApps)) {
|
||||
for (const k of Object.keys(prevLedgerRecord)) {
|
||||
it(`${k} is available in @zondax/ledger-substrate`, (): void => {
|
||||
expect(
|
||||
supportedApps.find(({ name }) =>
|
||||
name === ledgerApps[k]
|
||||
name === prevLedgerRecord[k]
|
||||
)
|
||||
).toBeDefined();
|
||||
});
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
// Copyright 2017-2024 @polkadot/hw-ledger authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/hw-ledger authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// These match up with the keys of the knownLedger object in the @polkadot/networks/defaults/ledger.ts
|
||||
// and maps to the known name in the @zondax/ledger-substrate/supported_apps package
|
||||
export const ledgerApps: Record<string, string> = {
|
||||
// These map to the known name in the @zondax/ledger-substrate/supported_apps package
|
||||
// but they do not reflect all ledger apps that are supported. Since ledger now has support for all
|
||||
// substrate chains via the PolkadotGenericApp, any new chains that need ledger support can be added to
|
||||
// `genericLedgerApps` below.
|
||||
export const prevLedgerRecord: Record<string, string> = {
|
||||
acala: 'Acala',
|
||||
ajuna: 'Ajuna',
|
||||
'aleph-node': 'AlephZero',
|
||||
@@ -27,6 +29,7 @@ export const ledgerApps: Record<string, string> = {
|
||||
nodle: 'Nodle',
|
||||
origintrail: 'OriginTrail',
|
||||
parallel: 'Parallel',
|
||||
peaq: 'Peaq',
|
||||
pendulum: 'Pendulum',
|
||||
phala: 'Phala',
|
||||
picasso: 'Picasso',
|
||||
@@ -44,3 +47,23 @@ export const ledgerApps: Record<string, string> = {
|
||||
xxnetwork: 'XXNetwork',
|
||||
zeitgeist: 'Zeitgeist'
|
||||
};
|
||||
|
||||
// Any chains moving forward that are supported by the PolkadotGenericApp from ledger will input their names below.
|
||||
export const genericLedgerApps = {
|
||||
bittensor: 'Bittensor',
|
||||
creditcoin3: 'Creditcoin3',
|
||||
dentnet: 'DENTNet',
|
||||
encointer: 'Encointer',
|
||||
frequency: 'Frequency',
|
||||
integritee: 'Integritee',
|
||||
liberland: 'Liberland',
|
||||
mythos: 'Mythos',
|
||||
polimec: 'Polimec',
|
||||
vara: 'Vara'
|
||||
};
|
||||
|
||||
// These match up with the keys of the knownLedger object in the @polkadot/networks/defaults/ledger.ts
|
||||
export const ledgerApps: Record<string, string> = {
|
||||
...prevLedgerRecord,
|
||||
...genericLedgerApps
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/hw-ledger authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/hw-ledger authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import './packageDetect.js';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/hw-ledger authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/hw-ledger authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export * from './index.js';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/hw-ledger authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/hw-ledger authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Do not edit, auto-generated by @polkadot/dev
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright 2017-2024 @polkadot/hw-ledger authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/hw-ledger authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Do not edit, auto-generated by @polkadot/dev
|
||||
|
||||
export const packageInfo = { name: '@polkadot/hw-ledger', path: 'auto', type: 'auto', version: '13.0.1' };
|
||||
export const packageInfo = { name: '@polkadot/hw-ledger', path: 'auto', type: 'auto', version: '14.0.3' };
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/hw-ledger authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/hw-ledger authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { HexString } from '@polkadot/util/types';
|
||||
|
||||
@@ -18,15 +18,16 @@
|
||||
"./packageDetect.cjs"
|
||||
],
|
||||
"type": "module",
|
||||
"version": "13.0.1",
|
||||
"version": "14.0.3-quantus.3",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@polkadot/util": "13.0.1",
|
||||
"@polkadot/util-crypto": "13.0.1",
|
||||
"tslib": "^2.6.2"
|
||||
"@polkadot/util": "14.0.3",
|
||||
"@polkadot/util-crypto": "14.0.3-quantus.3",
|
||||
"@quantus/crypto": "^0.3.0",
|
||||
"tslib": "^2.8.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@polkadot/util": "13.0.1",
|
||||
"@polkadot/util-crypto": "13.0.1"
|
||||
"@polkadot/util": "14.0.3",
|
||||
"@polkadot/util-crypto": "14.0.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/keyring authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// all external
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/keyring authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// default substrate dev phrase
|
||||
|
||||
@@ -1,577 +0,0 @@
|
||||
// Copyright 2017-2024 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
import type { KeyringPair$Json } from './types.js';
|
||||
|
||||
import { hexToU8a, stringToU8a } from '@polkadot/util';
|
||||
import { base64Decode, cryptoWaitReady, encodeAddress, randomAsU8a, setSS58Format } from '@polkadot/util-crypto';
|
||||
|
||||
import { decodePair } from './pair/decode.js';
|
||||
import Keyring from './index.js';
|
||||
|
||||
await cryptoWaitReady();
|
||||
|
||||
describe('keypair', (): void => {
|
||||
describe('ed25519', (): void => {
|
||||
const publicKeyOne = new Uint8Array([47, 140, 97, 41, 216, 22, 207, 81, 195, 116, 188, 127, 8, 195, 230, 62, 209, 86, 207, 120, 174, 251, 74, 101, 80, 217, 123, 135, 153, 121, 119, 238]);
|
||||
const publicKeyTwo = new Uint8Array([215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58, 14, 225, 114, 243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26]);
|
||||
const seedOne = stringToU8a('12345678901234567890123456789012');
|
||||
const seedTwo = hexToU8a('0x9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60');
|
||||
let keyring: Keyring;
|
||||
|
||||
beforeEach((): void => {
|
||||
keyring = new Keyring({ ss58Format: 42, type: 'ed25519' });
|
||||
|
||||
keyring.addFromSeed(seedOne, {});
|
||||
});
|
||||
|
||||
it('adds the pair', (): void => {
|
||||
expect(
|
||||
keyring.addFromSeed(seedTwo, {}).publicKey
|
||||
).toEqual(publicKeyTwo);
|
||||
});
|
||||
|
||||
it('creates via a dev seed', (): void => {
|
||||
expect(
|
||||
keyring.addFromUri('//Alice').address
|
||||
).toEqual('5FA9nQDVg267DEd8m1ZypXLBnvN7SFxYwV7ndqSYGiN9TTpu');
|
||||
});
|
||||
|
||||
it('creates a ed25519 pair via mnemonicToSeed', (): void => {
|
||||
expect(
|
||||
keyring.addFromUri(
|
||||
'seed sock milk update focus rotate barely fade car face mechanic mercy'
|
||||
).address
|
||||
).toEqual('5DkQP32jP4DVJLWWBRBoZF2tpWjqFrcrTBo6H5NcSk7MxKCC');
|
||||
});
|
||||
|
||||
it('adds from a mnemonic, with correct ss58', (): void => {
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
setSS58Format(20); // this would not 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('HSLu2eci2GCfWkRimjjdTXKoFSDL3rBv5Ey2JWCBj68cVZj');
|
||||
expect(encodeAddress(pair.publicKey)).toEqual('35cDYtPsdG1HUa2n2MaARgJyRz1WKMBZK1DL6c5cX7nugQh1');
|
||||
});
|
||||
|
||||
it('allows publicKeys retrieval', (): void => {
|
||||
keyring.addFromSeed(seedTwo, {});
|
||||
|
||||
expect(
|
||||
keyring.getPublicKeys()
|
||||
).toEqual([publicKeyOne, publicKeyTwo]);
|
||||
});
|
||||
|
||||
it('allows retrieval of a specific item', (): void => {
|
||||
expect(
|
||||
keyring.getPair(publicKeyOne).publicKey
|
||||
).toEqual(publicKeyOne);
|
||||
});
|
||||
|
||||
it('allows adding from JSON', (): void => {
|
||||
expect(
|
||||
keyring.addFromJson(
|
||||
JSON.parse('{"address":"5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQua","encoded":"0xb4a14995d25ab609f3686e9fa45f1fb237cd833f33f00d4b12c51858ca070d96972e47d73aae5eeb0fc06f923826cf0943fdb02c2c2ee30ef52a7912663053940d1da4da66b3a3f520ae07422c1c94b2d95690fca9d1f4a997623bb2923a8833280e19e7f72c3c5cfa343974e60e2b3dc53b404fdaf330756daad5e4e3","encoding":{"content":"pkcs8","type":"xsalsa20-poly1305","version":"0"},"meta":{"isTesting":true,"name":"alice"}}') as KeyringPair$Json
|
||||
).publicKey
|
||||
).toEqual(
|
||||
new Uint8Array([209, 114, 167, 76, 218, 76, 134, 89, 18, 195, 43, 160, 168, 10, 87, 174, 105, 171, 174, 65, 14, 92, 203, 89, 222, 232, 78, 47, 68, 50, 219, 79])
|
||||
);
|
||||
});
|
||||
|
||||
it('signs and verifies', (): void => {
|
||||
const MESSAGE = stringToU8a('this is a message');
|
||||
const pair = keyring.getPair(publicKeyOne);
|
||||
const signature = pair.sign(MESSAGE);
|
||||
|
||||
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 => {
|
||||
const MESSAGE = stringToU8a('this is a message');
|
||||
const pair = keyring.getPair(publicKeyOne);
|
||||
const signature = pair.sign(MESSAGE, { withType: true });
|
||||
|
||||
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('sr25519', (): void => {
|
||||
const publicKeyOne = new Uint8Array([116, 28, 8, 160, 111, 65, 197, 150, 96, 143, 103, 116, 37, 155, 217, 4, 51, 4, 173, 250, 93, 62, 234, 98, 118, 11, 217, 190, 151, 99, 77, 99]);
|
||||
const publicKeyTwo = hexToU8a('0x44a996beb1eef7bdcab976ab6d2ca26104834164ecf28fb375600576fcc6eb0f');
|
||||
const seedOne = stringToU8a('12345678901234567890123456789012');
|
||||
const seedTwo = hexToU8a('0x9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60');
|
||||
let keyring: Keyring;
|
||||
|
||||
beforeEach((): void => {
|
||||
keyring = new Keyring({ ss58Format: 42, type: 'sr25519' });
|
||||
|
||||
keyring.addFromSeed(seedOne, {});
|
||||
});
|
||||
|
||||
it('creates with dev phrase when only path specified', (): void => {
|
||||
expect(
|
||||
keyring.createFromUri('//Alice').address
|
||||
).toEqual('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY');
|
||||
});
|
||||
|
||||
it('creates with integer derivations', (): void => {
|
||||
// MAX_SAFE_INTEGER
|
||||
expect(
|
||||
keyring.createFromUri('//9007199254740991').address
|
||||
).toEqual('5CDsyNZyqxLpHnTvknr68anUcYoBFjZbFKiEJJf4prB75Uog');
|
||||
|
||||
// MAX_SAFE_INTEGER + extra digits
|
||||
expect(
|
||||
keyring.createFromUri('//900719925474099999').address
|
||||
).toEqual('5GHj2D7RG2m2DXYwGSDpXwuuxn53G987i7p2EQVDqP4NYu4q');
|
||||
});
|
||||
|
||||
it('creates via dev seed (2-byte encoding)', (): void => {
|
||||
keyring.setSS58Format(252);
|
||||
|
||||
expect(
|
||||
keyring.addFromUri('//Alice').address
|
||||
).toEqual('xw8P6urbSAronL3zZFB7dg8p7LLSgKCUFDUgjohnf1iP434ic');
|
||||
});
|
||||
|
||||
it('adds the pair', (): void => {
|
||||
expect(
|
||||
keyring.addFromSeed(seedTwo, {}).publicKey
|
||||
).toEqual(publicKeyTwo);
|
||||
});
|
||||
|
||||
it('adds from a mnemonic', (): void => {
|
||||
keyring.setSS58Format(2);
|
||||
|
||||
expect(
|
||||
keyring.addFromMnemonic('moral movie very draw assault whisper awful rebuild speed purity repeat card', {}).address
|
||||
).toEqual('FSjXNRT2K1R5caeHLPD6WMrqYUpfGZB7ua8W89JFctZ1YqV');
|
||||
});
|
||||
|
||||
it('allows publicKeys retrieval', (): void => {
|
||||
keyring.addFromSeed(seedTwo, {});
|
||||
|
||||
expect(
|
||||
keyring.getPublicKeys()
|
||||
).toEqual([publicKeyOne, publicKeyTwo]);
|
||||
});
|
||||
|
||||
it('allows retrieval of a specific item', (): void => {
|
||||
expect(
|
||||
keyring.getPair(publicKeyOne).publicKey
|
||||
).toEqual(publicKeyOne);
|
||||
});
|
||||
|
||||
it('allows adding from JSON', (): void => {
|
||||
expect(
|
||||
keyring.addFromJson(
|
||||
JSON.parse('{"address":"5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQua","encoded":"0xb4a14995d25ab609f3686e9fa45f1fb237cd833f33f00d4b12c51858ca070d96972e47d73aae5eeb0fc06f923826cf0943fdb02c2c2ee30ef52a7912663053940d1da4da66b3a3f520ae07422c1c94b2d95690fca9d1f4a997623bb2923a8833280e19e7f72c3c5cfa343974e60e2b3dc53b404fdaf330756daad5e4e3","encoding":{"content":"pkcs8","type":"xsalsa20-poly1305","version":"0"},"meta":{"isTesting":true,"name":"alice"}}') as KeyringPair$Json
|
||||
).publicKey
|
||||
).toEqual(
|
||||
new Uint8Array([209, 114, 167, 76, 218, 76, 134, 89, 18, 195, 43, 160, 168, 10, 87, 174, 105, 171, 174, 65, 14, 92, 203, 89, 222, 232, 78, 47, 68, 50, 219, 79])
|
||||
);
|
||||
});
|
||||
|
||||
it('signs and verifies', (): void => {
|
||||
const MESSAGE = stringToU8a('this is a message');
|
||||
const pair = keyring.getPair(publicKeyOne);
|
||||
const signature = pair.sign(MESSAGE);
|
||||
|
||||
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 => {
|
||||
const MESSAGE = stringToU8a('this is a message');
|
||||
const pair = keyring.getPair(publicKeyOne);
|
||||
const signature = pair.sign(MESSAGE, { withType: true });
|
||||
|
||||
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('ecdsa', (): void => {
|
||||
const seedOne = 'potato act energy ahead stone taxi receive fame gossip equip chest round';
|
||||
const seedTwo = hexToU8a('0x3c74be003bd9a876be439949ccf2b292bd966c94959a689173b295b326cd6da7');
|
||||
const publicKeyOne = hexToU8a('0x02c6b6c664db5ef505477bba1cf2f1789c98796b9bb5fa21abd0ac4589bed980e7');
|
||||
const publicKeyTwo = hexToU8a('0x021da683b913fb28c979ba3e5f1881415cef4b1f58a5d05ed3610a2995e7b4943c');
|
||||
const addressKeyOne = hexToU8a('0x0cfd0dd2c59a9987b9848919163931b6a42283ffd3d91e92c98b522525a7038f');
|
||||
let keyring: Keyring;
|
||||
|
||||
beforeEach((): void => {
|
||||
keyring = new Keyring({ ss58Format: 42, type: 'ecdsa' });
|
||||
|
||||
keyring.addFromMnemonic(seedOne, {});
|
||||
});
|
||||
|
||||
it('creates with dev phrase when only path specified', (): void => {
|
||||
expect(
|
||||
keyring.createFromUri('//Alice').address
|
||||
).toEqual('5C7C2Z5sWbytvHpuLTvzKunnnRwQxft1jiqrLD5rhucQ5S9X');
|
||||
});
|
||||
|
||||
it('adds the pair', (): void => {
|
||||
expect(
|
||||
keyring.addFromSeed(seedTwo, {}).publicKey
|
||||
).toEqual(publicKeyTwo);
|
||||
});
|
||||
|
||||
it('adds from a mnemonic', (): void => {
|
||||
keyring.setSS58Format(2);
|
||||
|
||||
expect(
|
||||
keyring.addFromMnemonic('moral movie very draw assault whisper awful rebuild speed purity repeat card').address
|
||||
).toEqual('DrRE1KAcs4pCicX8yJPh7YxkLPQ2vXnCFSVRPQfx38KjEFe');
|
||||
});
|
||||
|
||||
it('allows publicKeys retrieval', (): void => {
|
||||
keyring.addFromSeed(seedTwo, {});
|
||||
|
||||
expect(
|
||||
keyring.getPublicKeys()
|
||||
).toEqual([publicKeyOne, publicKeyTwo]);
|
||||
});
|
||||
|
||||
it('allows retrieval of a specific item', (): void => {
|
||||
expect(
|
||||
keyring.getPair(addressKeyOne).publicKey
|
||||
).toEqual(publicKeyOne);
|
||||
});
|
||||
|
||||
it('allows adding from JSON', (): void => {
|
||||
expect(
|
||||
keyring.addFromJson(
|
||||
JSON.parse('{"address":"5DzMsaYFhmpRdErWrP6K6PD7UXzYoeETToSBUrZSvxasqWRz","encoded":"0xa192d39b42bc1601bf61df31039a554228593fadf870bc837b658a5114627aca199fff596260c95fe8994c66a47636cf0270aa08f402ba5541038753960d00e6c3af5e239ec58fb1eef3db7d6bc266f4853bdfe4ed17122d9092d879014d53980d2ee57f6f55a88c38836447d8645008e8815379626addc8f81f80cd49a2","encoding":{"content":"pkcs8","type":"xsalsa20-poly1305","version":"2"},"meta":{}}') as KeyringPair$Json
|
||||
).address
|
||||
).toEqual('5DzMsaYFhmpRdErWrP6K6PD7UXzYoeETToSBUrZSvxasqWRz');
|
||||
});
|
||||
|
||||
it('allows creation from JSON', (): void => {
|
||||
keyring.setSS58Format(2);
|
||||
const pair = keyring.createFromJson(
|
||||
JSON.parse('{"address":"0x02fde629668eb2bcc7d748f40a7e597f7c7b363498ff3db31f03ce4854937883ad","encoded":"qIhAhKqtf2iyEoWEr8nmBdksSI8EHHCpgJHToqd6Pl8AgAAAAQAAAAgAAADDZ//fj/BRRj+0+bl1KAlYgoPJp6nEUwiw0fVqO2BW4mjEgQ+iWwJEgDf1JUtecbzOlfhTXBzqX/dIYzLgUADrF4EFEPpboCWiU1iN7W/3DM1cOTRVvTGcbdIqW//z3axhz961qzeJVUIFgllwGe/euLUPIlKbIkiN/CsRYdQ=","encoding":{"content":["pkcs8","ecdsa"],"type":["scrypt","xsalsa20-poly1305"],"version":"3"},"meta":{"genesisHash":"0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe","name":"ecdsa","tags":[],"whenCreated":1600925898271}}') as KeyringPair$Json
|
||||
);
|
||||
|
||||
expect(pair.address).toEqual('DHL8HKFuTTR55JzzLmkJRCAfPBbuevKaT9cXikxbEV97Ko8');
|
||||
expect(pair.publicKey).toEqual(hexToU8a('0x02fde629668eb2bcc7d748f40a7e597f7c7b363498ff3db31f03ce4854937883ad'));
|
||||
});
|
||||
|
||||
it('fails toJson() when password is incorrect', (): void => {
|
||||
const pair = keyring.createFromJson(
|
||||
JSON.parse('{"address":"0x02fde629668eb2bcc7d748f40a7e597f7c7b363498ff3db31f03ce4854937883ad","encoded":"qIhAhKqtf2iyEoWEr8nmBdksSI8EHHCpgJHToqd6Pl8AgAAAAQAAAAgAAADDZ//fj/BRRj+0+bl1KAlYgoPJp6nEUwiw0fVqO2BW4mjEgQ+iWwJEgDf1JUtecbzOlfhTXBzqX/dIYzLgUADrF4EFEPpboCWiU1iN7W/3DM1cOTRVvTGcbdIqW//z3axhz961qzeJVUIFgllwGe/euLUPIlKbIkiN/CsRYdQ=","encoding":{"content":["pkcs8","ecdsa"],"type":["scrypt","xsalsa20-poly1305"],"version":"3"},"meta":{"genesisHash":"0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe","name":"ecdsa","tags":[],"whenCreated":1600925898271}}') as KeyringPair$Json
|
||||
);
|
||||
|
||||
expect(
|
||||
() => pair.toJson('invalid')
|
||||
).toThrow(/Unable to decode using the supplied passphrase/);
|
||||
});
|
||||
|
||||
it('pass toJson() when password is correct', (): void => {
|
||||
const pair = keyring.createFromJson(
|
||||
JSON.parse('{"address":"0x02fde629668eb2bcc7d748f40a7e597f7c7b363498ff3db31f03ce4854937883ad","encoded":"qIhAhKqtf2iyEoWEr8nmBdksSI8EHHCpgJHToqd6Pl8AgAAAAQAAAAgAAADDZ//fj/BRRj+0+bl1KAlYgoPJp6nEUwiw0fVqO2BW4mjEgQ+iWwJEgDf1JUtecbzOlfhTXBzqX/dIYzLgUADrF4EFEPpboCWiU1iN7W/3DM1cOTRVvTGcbdIqW//z3axhz961qzeJVUIFgllwGe/euLUPIlKbIkiN/CsRYdQ=","encoding":{"content":["pkcs8","ecdsa"],"type":["scrypt","xsalsa20-poly1305"],"version":"3"},"meta":{"genesisHash":"0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe","name":"ecdsa","tags":[],"whenCreated":1600925898271}}') as KeyringPair$Json
|
||||
);
|
||||
|
||||
expect(
|
||||
() => pair.toJson('testing')
|
||||
).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, 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 => {
|
||||
const MESSAGE = stringToU8a('this is a message');
|
||||
const pair = keyring.getPair(addressKeyOne);
|
||||
const signature = pair.sign(MESSAGE, { withType: true });
|
||||
|
||||
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 => {
|
||||
// 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';
|
||||
const ETH_ADDRESS_TWO = '0x4119b2e6c3Cb618F4f0B93ac77f9BeeC7FF02887';
|
||||
|
||||
let keyring: Keyring;
|
||||
|
||||
beforeEach((): void => {
|
||||
keyring = new Keyring({ type: 'ethereum' });
|
||||
});
|
||||
|
||||
it('creates with dev phrase from the private key', (): void => {
|
||||
const pair = keyring.addFromSeed(hexToU8a(PRIV_KEY_ONE));
|
||||
|
||||
expect(
|
||||
pair.address
|
||||
).toEqual(ETH_ADDRESS_ONE);
|
||||
});
|
||||
|
||||
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('creates with dev phrase with derivation path specified - addFromUri with type', (): void => {
|
||||
const keyringUntyped = new Keyring();
|
||||
|
||||
expect(
|
||||
keyringUntyped.addFromUri(PHRASE, {}, 'ethereum').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('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 => {
|
||||
const pair = keyring.addFromJson(
|
||||
JSON.parse('{"address":"KWCv1L3QX9LDPwY4VzvLmarEmXjVJidUzZcinvVnmxAJJCBou","encoded":"U8qFEaghhmNV2PgFhjqzmhyUy37Ok7abfFU2MNsBd0sAgAAAAQAAAAgAAAA3+NniKogzNphiMNueB1X0sGA07B6CaXWfpXPx45iSXoTTprwzU5mOoSqUWO0GKHROI72LN+uJ8Yfv6Ll6JOOV3VPKfoVoFmYm+zDrrMPa0gk5E5kUuSijxADcE6zUrliPVr0Ix/qaghu5SJ7RtWDQLBf4Hp86SJ8Gg6gTSSk=","encoding":{"content":["pkcs8","ethereum"],"type":["scrypt","xsalsa20-poly1305"],"version":"3"},"meta":{}}') as KeyringPair$Json
|
||||
);
|
||||
|
||||
expect(pair.publicKey).toEqual(hexToU8a('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077'));
|
||||
expect(pair.address).toEqual(ETH_ADDRESS_TWO);
|
||||
|
||||
pair.decodePkcs8('password');
|
||||
|
||||
expect(pair.isLocked).toBe(false);
|
||||
expect(pair.publicKey).toEqual(hexToU8a('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077'));
|
||||
expect(pair.address).toBe(ETH_ADDRESS_TWO);
|
||||
});
|
||||
|
||||
it('allows for signing/verification', (): void => {
|
||||
const MESSAGE = stringToU8a('just some test message');
|
||||
const signer = keyring.createFromUri(PHRASE);
|
||||
const verifier = keyring.addFromJson(
|
||||
JSON.parse('{"address":"KWCv1L3QX9LDPwY4VzvLmarEmXjVJidUzZcinvVnmxAJJCBou","encoded":"U8qFEaghhmNV2PgFhjqzmhyUy37Ok7abfFU2MNsBd0sAgAAAAQAAAAgAAAA3+NniKogzNphiMNueB1X0sGA07B6CaXWfpXPx45iSXoTTprwzU5mOoSqUWO0GKHROI72LN+uJ8Yfv6Ll6JOOV3VPKfoVoFmYm+zDrrMPa0gk5E5kUuSijxADcE6zUrliPVr0Ix/qaghu5SJ7RtWDQLBf4Hp86SJ8Gg6gTSSk=","encoding":{"content":["pkcs8","ethereum"],"type":["scrypt","xsalsa20-poly1305"],"version":"3"},"meta":{}}') as KeyringPair$Json
|
||||
);
|
||||
|
||||
const signature = signer.sign(MESSAGE);
|
||||
const dummyPublic = verifier.publicKey.slice();
|
||||
|
||||
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 => {
|
||||
const MESSAGE = stringToU8a('just some test message');
|
||||
const signer = keyring.createFromUri(PHRASE);
|
||||
const verifier = keyring.addFromJson(
|
||||
JSON.parse('{"address":"KWCv1L3QX9LDPwY4VzvLmarEmXjVJidUzZcinvVnmxAJJCBou","encoded":"U8qFEaghhmNV2PgFhjqzmhyUy37Ok7abfFU2MNsBd0sAgAAAAQAAAAgAAAA3+NniKogzNphiMNueB1X0sGA07B6CaXWfpXPx45iSXoTTprwzU5mOoSqUWO0GKHROI72LN+uJ8Yfv6Ll6JOOV3VPKfoVoFmYm+zDrrMPa0gk5E5kUuSijxADcE6zUrliPVr0Ix/qaghu5SJ7RtWDQLBf4Hp86SJ8Gg6gTSSk=","encoding":{"content":["pkcs8","ethereum"],"type":["scrypt","xsalsa20-poly1305"],"version":"3"},"meta":{}}') as KeyringPair$Json
|
||||
);
|
||||
|
||||
const signature = signer.sign(MESSAGE, { withType: true });
|
||||
const dummyPublic = verifier.publicKey.slice();
|
||||
|
||||
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');
|
||||
});
|
||||
});
|
||||
|
||||
describe('util', (): void => {
|
||||
let keyring: Keyring;
|
||||
|
||||
beforeEach((): void => {
|
||||
keyring = new Keyring({ ss58Format: 42 });
|
||||
});
|
||||
|
||||
it('can re-encode an address to Polkadot live', (): void => {
|
||||
expect(
|
||||
keyring.encodeAddress('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', 0)
|
||||
).toEqual('15oF4uVJwmo4TdGW7VfQxNLavjCXviqxT9S1MgbjMNHr6Sp5');
|
||||
});
|
||||
|
||||
it('can re-encode an address to keyring default', (): void => {
|
||||
expect(
|
||||
keyring.encodeAddress('15oF4uVJwmo4TdGW7VfQxNLavjCXviqxT9S1MgbjMNHr6Sp5')
|
||||
).toEqual('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY');
|
||||
});
|
||||
});
|
||||
|
||||
describe('version 2 JSON', (): void => {
|
||||
const PAIR = '{"address":"5CczAE5AmGrZ93MeVhha3Ywam7j9dKB7cArnH7gtrXcMFJvu","encoded":"0xee8f236e2ac3217ce689692a4afc612220dc77fddaed0482f8f95136a7c3e034cccfbc495410a6e9b2439904974ed1d207abeca536ff6985ceb78edeeb3dc343e561c184c488101af8811d1331430b4ccf0e96ef507132e5132964e8564232e7100d973c5bee7b231dd0c8ad5273f3501515a422c8d7ed9d20a73c0ed17c98ee4588e54844bb73052dcad81f7a1094613d63c162fec7446c88b1fae70e","encoding":{"content":["pkcs8","sr25519"],"type":"xsalsa20-poly1305","version":"2"},"meta":{"genesisHash":"0xe143f23803ac50e8f6f8e62695d1ce9e4e1d68aa36c1cd2cfd15340213f3423e","name":"json v2","tags":[],"whenCreated":1595243159596}}';
|
||||
const PASS2 = 'versionTwo';
|
||||
const PASS3 = 'versionThree';
|
||||
let keyring: Keyring;
|
||||
|
||||
beforeEach((): void => {
|
||||
keyring = new Keyring({ ss58Format: 42 });
|
||||
});
|
||||
|
||||
it('can decode from a version 2 JSON file', (): void => {
|
||||
const pair = keyring.addFromJson(JSON.parse(PAIR) as KeyringPair$Json);
|
||||
|
||||
pair.decodePkcs8(PASS2);
|
||||
|
||||
const json = pair.toJson(PASS3);
|
||||
|
||||
expect(pair.isLocked).toBe(false);
|
||||
expect(pair.address).toBe('5CczAE5AmGrZ93MeVhha3Ywam7j9dKB7cArnH7gtrXcMFJvu');
|
||||
expect(json.encoding).toEqual({
|
||||
content: ['pkcs8', 'sr25519'],
|
||||
type: ['scrypt', 'xsalsa20-poly1305'],
|
||||
version: '3'
|
||||
});
|
||||
|
||||
pair.decodePkcs8(PASS3);
|
||||
|
||||
expect(pair.address).toEqual('5CczAE5AmGrZ93MeVhha3Ywam7j9dKB7cArnH7gtrXcMFJvu');
|
||||
});
|
||||
});
|
||||
|
||||
describe('version 3 JSON (hex)', (): void => {
|
||||
const PAIR = '{"address":"FLiSDPCcJ6auZUGXALLj6jpahcP6adVFDBUQznPXUQ7yoqH","encoded":"0xcd238963070cc4d6806053ee1ac500c7add9c28732bb5d434a332f84a91d9be0008000000100000008000000cf630a1113941b350ddd06697e50399183162e5e9a0e893eafc7f5f4893a223dca5055706b9925b56fdb4304192143843da718e11717daf89cf4f4781f94fb443f61432f782d54280af9eec90bd3069c3cc2d957a42b7c18dc2e9497f623735518e0e49b58f8e4db2c09da3a45dbb935659d015fc94b946cba75b606a6ff7f4e823f6b049e2e6892026b49de02d6dbbd64646fe0933f537d9ea53a70be","encoding":{"content":["pkcs8","sr25519"],"type":["scrypt","xsalsa20-poly1305"],"version":"3"},"meta":{"genesisHash":"0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe","name":"version3","tags":[],"whenCreated":1595277797639}}';
|
||||
const PASS3 = 'version3';
|
||||
let keyring: Keyring;
|
||||
|
||||
beforeEach((): void => {
|
||||
keyring = new Keyring({ ss58Format: 2 });
|
||||
});
|
||||
|
||||
it('can decode from a version 3 JSON file', (): void => {
|
||||
const pair = keyring.addFromJson(JSON.parse(PAIR) as KeyringPair$Json);
|
||||
|
||||
pair.decodePkcs8(PASS3);
|
||||
|
||||
expect(pair.isLocked).toBe(false);
|
||||
expect(pair.address).toBe('FLiSDPCcJ6auZUGXALLj6jpahcP6adVFDBUQznPXUQ7yoqH');
|
||||
});
|
||||
});
|
||||
|
||||
describe('version 3 JSON (base64)', (): void => {
|
||||
const PAIR = '{"address":"FLiSDPCcJ6auZUGXALLj6jpahcP6adVFDBUQznPXUQ7yoqH","encoded":"ILjSgYaGvq1zaCz/kx+aqfLaHBjLXz0Qsmr6RnkOVU4AgAAAAQAAAAgAAAB5R2hm5kgXyc0NQYFxvMU4zCdjB+ugs/ibEooqCvuudbaeKn3Ee47NkCqU1ecOJV+eeaVn4W4dRvIpj5kGmQOGsewR+MiQ/B0G9NFh7JXV0qcPlk2QMNW1/mbJrTO4miqL448BSkP7ZOhUV6HFUpMt3B9HwjiRLN8RORcFp0ID/Azs4Jl/xOpXNzbgQGIffWgCIKTxN9N1ku6tdlG4","encoding":{"content":["pkcs8","sr25519"],"type":["scrypt","xsalsa20-poly1305"],"version":"3"},"meta":{"genesisHash":"0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe","name":"version3","tags":[],"whenCreated":1595277797639,"whenEdited":1595278378596}}';
|
||||
const PASS3 = 'version3';
|
||||
let keyring: Keyring;
|
||||
|
||||
beforeEach((): void => {
|
||||
keyring = new Keyring({ ss58Format: 2 });
|
||||
});
|
||||
|
||||
it('can decode from a version 3 JSON file', (): void => {
|
||||
const pair = keyring.addFromJson(JSON.parse(PAIR) as KeyringPair$Json);
|
||||
|
||||
pair.decodePkcs8(PASS3);
|
||||
|
||||
expect(pair.isLocked).toBe(false);
|
||||
expect(pair.address).toBe('FLiSDPCcJ6auZUGXALLj6jpahcP6adVFDBUQznPXUQ7yoqH');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/keyring authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import './packageDetect.js';
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
// Copyright 2017-2026 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
import type { KeypairType } from '@polkadot/util-crypto/types';
|
||||
|
||||
import { contextForSpec } from '@quantus/crypto';
|
||||
|
||||
import { u8aConcat } from '@polkadot/util';
|
||||
import { cryptoWaitReady, decodeAddress, dilithiumSizes } from '@polkadot/util-crypto';
|
||||
|
||||
import { Keyring } from './index.js';
|
||||
import { createTestPairs } from './testingPairs.js';
|
||||
|
||||
await cryptoWaitReady();
|
||||
|
||||
// Addresses `quantus developer create-test-wallets` prints for the dev accounts.
|
||||
const ALICE = 'qzk1Nxai3dZD9Cn5kwGcgL6mKxsfxwqdis7kDQJ52aJS2vSn7';
|
||||
const BOB = 'qzkYEQv8tQsmniZYdame3Cku18RL5g9bGK9Pdydq5TMPdpE3y';
|
||||
const CHARLIE = 'qzntBpmqHZF1jxC8KJKpuxcYuHST892jyXBqRctpAxd1WQ9BL';
|
||||
const MESSAGE = new Uint8Array([1, 2, 3, 4]);
|
||||
const PASSWORD = 'correct horse';
|
||||
|
||||
// Upstream's keyring specs exercised ed25519, sr25519, ecdsa and ethereum pairs.
|
||||
// Those types, and their primitives, are gone (quantus/common#6). What replaces
|
||||
// them is what a post-quantum-only keyring has to guarantee instead.
|
||||
describe('Keyring (post-quantum only)', (): void => {
|
||||
describe('refuses quantum-unsafe keys, and says why', (): void => {
|
||||
const unsafe = ['ed25519', 'sr25519', 'ecdsa', 'ethereum'] as unknown as KeypairType[];
|
||||
|
||||
for (const type of unsafe) {
|
||||
it(`at construction: ${type}`, (): void => {
|
||||
expect(() => new Keyring({ type })).toThrow(/not quantum-safe and cannot be held here/);
|
||||
});
|
||||
|
||||
it(`from a uri: ${type}`, (): void => {
|
||||
expect(() => new Keyring().createFromUri('//Alice', {}, type)).toThrow(/not quantum-safe/);
|
||||
});
|
||||
}
|
||||
|
||||
// The entry point that matters most: a backup file the user chose. This is
|
||||
// the shape of a real polkadot{.js} export of an ed25519 account.
|
||||
it('from a polkadot{.js} JSON backup', (): void => {
|
||||
const json = {
|
||||
address: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
|
||||
encoded: '0x00',
|
||||
encoding: { content: ['pkcs8', 'ed25519'], type: ['scrypt', 'xsalsa20-poly1305'], version: '3' },
|
||||
meta: { name: 'polkadot account' }
|
||||
};
|
||||
|
||||
expect(() => new Keyring().createFromJson(json as never)).toThrow(/ed25519 keys are not quantum-safe and cannot be held here/);
|
||||
});
|
||||
|
||||
it('and still refuses an unknown type as unknown', (): void => {
|
||||
expect(() => new Keyring({ type: 'dilithium44' as KeypairType })).toThrow(/found 'dilithium44'/);
|
||||
});
|
||||
});
|
||||
|
||||
it('defaults to ML-DSA-65', (): void => {
|
||||
expect(new Keyring().type).toEqual('dilithium65');
|
||||
});
|
||||
|
||||
describe('test pairs are the Quantus dev accounts', (): void => {
|
||||
const pairs = createTestPairs({ ss58Format: 189 });
|
||||
|
||||
it('with the addresses the chain endows', (): void => {
|
||||
expect(pairs.crystal_alice.address).toEqual(ALICE);
|
||||
expect(pairs.dilithium_bob.address).toEqual(BOB);
|
||||
expect(pairs.crystal_charlie.address).toEqual(CHARLIE);
|
||||
expect(pairs.crystal_alice.type).toEqual('dilithium87');
|
||||
});
|
||||
|
||||
it('and a nobody pair of a type the keyring can hold', (): void => {
|
||||
expect(pairs.nobody.type).toEqual('dilithium65');
|
||||
});
|
||||
});
|
||||
|
||||
// Upstream decoded the address and passed it as the public key, which for
|
||||
// Substrate is the same bytes. Here the address is a hash of the key, so every
|
||||
// watch-only account reported the hash of its own address.
|
||||
it('adds a watch-only account at the address it was given', (): void => {
|
||||
const keyring = new Keyring({ ss58Format: 189 });
|
||||
const watched = keyring.addFromAddress(ALICE, { name: 'watched' });
|
||||
|
||||
expect(watched.address).toEqual(ALICE);
|
||||
expect(watched.addressRaw).toEqual(decodeAddress(ALICE));
|
||||
expect(keyring.getPair(ALICE).meta.name).toEqual('watched');
|
||||
});
|
||||
|
||||
describe('JSON round trip', (): void => {
|
||||
for (const type of ['dilithium65', 'dilithium87'] as const) {
|
||||
it(`${type}: encrypts, restores locked at the right address, and unlocks`, (): void => {
|
||||
const keyring = new Keyring({ ss58Format: 189 });
|
||||
const pair = keyring.addFromSeed(new Uint8Array(32).fill(7), { name: 'roundtrip' }, type);
|
||||
const restored = keyring.createFromJson(pair.toJson(PASSWORD));
|
||||
|
||||
expect(restored.isLocked).toBe(true);
|
||||
expect(restored.address).toEqual(pair.address);
|
||||
expect(() => restored.decodePkcs8('wrong')).toThrow();
|
||||
|
||||
restored.decodePkcs8(PASSWORD);
|
||||
|
||||
expect(restored.publicKey).toEqual(pair.publicKey);
|
||||
expect(restored.sign(MESSAGE, { context: contextForSpec(148) })).toEqual(pair.sign(MESSAGE, { context: contextForSpec(148) }));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('verify', (): void => {
|
||||
const keyring = new Keyring({ ss58Format: 189 });
|
||||
const alice = keyring.addFromSeed(new Uint8Array(32), {}, 'dilithium87');
|
||||
const bob = keyring.addFromSeed(new Uint8Array(32).fill(1), {}, 'dilithium87');
|
||||
const context = contextForSpec(148);
|
||||
// what pair.sign returns: the chain's wire form, signature ‖ publicKey
|
||||
const signature = alice.sign(MESSAGE, { context });
|
||||
const bare = signature.slice(0, dilithiumSizes('dilithium87').signature);
|
||||
|
||||
it('accepts the signature ‖ publicKey form and a bare signature', (): void => {
|
||||
expect(signature).toEqual(u8aConcat(bare, alice.publicKey));
|
||||
expect(alice.verify(MESSAGE, signature, alice.publicKey, context)).toBe(true);
|
||||
expect(alice.verify(MESSAGE, bare, alice.publicKey, context)).toBe(true);
|
||||
});
|
||||
|
||||
it('fails under the wrong context, for the wrong signer, or for another message', (): void => {
|
||||
expect(alice.verify(MESSAGE, signature, alice.publicKey)).toBe(false);
|
||||
expect(alice.verify(MESSAGE, signature, bob.publicKey, context)).toBe(false);
|
||||
expect(alice.verify(new Uint8Array([9]), signature, alice.publicKey, context)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('has no derivation and no VRF', (): void => {
|
||||
const pair = new Keyring().addFromSeed(new Uint8Array(32));
|
||||
|
||||
expect(() => pair.derive('//1')).toThrow(/derive from the mnemonic/);
|
||||
expect(() => pair.vrfSign(MESSAGE)).toThrow(/VRF signing is not available/);
|
||||
expect(() => pair.vrfVerify(MESSAGE, new Uint8Array(96), pair.publicKey)).toThrow(/VRF verification is not available/);
|
||||
});
|
||||
});
|
||||
@@ -1,22 +1,43 @@
|
||||
// Copyright 2017-2024 @polkadot/keyring authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { EncryptedJsonEncoding, Keypair, KeypairType } from '@polkadot/util-crypto/types';
|
||||
import type { KeyringInstance, KeyringOptions, KeyringPair, KeyringPair$Json, KeyringPair$Meta } from './types.js';
|
||||
|
||||
import { hexToU8a, isHex, stringToU8a } from '@polkadot/util';
|
||||
import { base64Decode, decodeAddress, ed25519PairFromSeed as ed25519FromSeed, encodeAddress, ethereumEncode, hdEthereum, keyExtractSuri, keyFromPath, mnemonicToLegacySeed, mnemonicToMiniSecret, secp256k1PairFromSeed as secp256k1FromSeed, sr25519PairFromSeed as sr25519FromSeed } from '@polkadot/util-crypto';
|
||||
import { hexToU8a, isHex } from '@polkadot/util';
|
||||
import { base64Decode, decodeAddress, dilithiumPairFromMnemonic, dilithiumPairFromSeed, dilithiumPathFromSuri, encodeAddress, keyExtractSuri } from '@polkadot/util-crypto';
|
||||
|
||||
import { createPair } from './pair/index.js';
|
||||
import { DEV_PHRASE } from './defaults.js';
|
||||
import { Pairs } from './pairs.js';
|
||||
|
||||
const PairFromSeed = {
|
||||
ecdsa: (seed: Uint8Array): Keypair => secp256k1FromSeed(seed),
|
||||
ed25519: (seed: Uint8Array): Keypair => ed25519FromSeed(seed),
|
||||
ethereum: (seed: Uint8Array): Keypair => secp256k1FromSeed(seed),
|
||||
sr25519: (seed: Uint8Array): Keypair => sr25519FromSeed(seed)
|
||||
};
|
||||
/**
|
||||
* Every keypair type this keyring will construct.
|
||||
*
|
||||
* Was two separate inline arrays, both of which a widened `KeypairType` silently
|
||||
* walked past — the union is compile-time and these checks are not. Named once so
|
||||
* the next change to the union has one place to look, and so the error message
|
||||
* cannot drift from the check.
|
||||
*/
|
||||
const KEYPAIR_TYPES: KeypairType[] = ['dilithium65', 'dilithium87'];
|
||||
|
||||
/** Upstream's types, refused by name so the refusal can say why. */
|
||||
const QUANTUM_UNSAFE = ['ecdsa', 'ed25519', 'ethereum', 'sr25519'];
|
||||
|
||||
/**
|
||||
* Refuse anything that is not a post-quantum keypair type.
|
||||
*
|
||||
* A key of upstream's types arriving here (a JSON backup from polkadot{.js}, a
|
||||
* caller asking for sr25519) is not a bug in this software, and "unknown crypto
|
||||
* type" would read as one. Say what it is instead. quantus/common#6
|
||||
*/
|
||||
function assertKeypairType (type: string | undefined): asserts type is KeypairType {
|
||||
if (type && QUANTUM_UNSAFE.includes(type)) {
|
||||
throw new Error(`${type} keys are not quantum-safe and cannot be held here; only ML-DSA (dilithium65, dilithium87) keys are supported`);
|
||||
} else if (!KEYPAIR_TYPES.includes(type as KeypairType)) {
|
||||
throw new Error(`Expected a keypair type of one of ${KEYPAIR_TYPES.map((t) => `'${t}'`).join(', ')}, found '${type || 'unknown'}'`);
|
||||
}
|
||||
}
|
||||
|
||||
function pairToPublic ({ publicKey }: KeyringPair): Uint8Array {
|
||||
return publicKey;
|
||||
@@ -48,11 +69,12 @@ export class Keyring implements KeyringInstance {
|
||||
public decodeAddress = decodeAddress;
|
||||
|
||||
constructor (options: KeyringOptions = {}) {
|
||||
options.type = options.type || 'ed25519';
|
||||
// ML-DSA-65 is the scheme new Quantus accounts use.
|
||||
options.type = options.type || 'dilithium65';
|
||||
|
||||
if (!['ecdsa', 'ethereum', 'ed25519', 'sr25519'].includes(options.type || 'undefined')) {
|
||||
throw new Error(`Expected a keyring type of either 'ed25519', 'sr25519', 'ethereum' or 'ecdsa', found '${options.type || 'unknown'}`);
|
||||
}
|
||||
// Behind the union type: a JavaScript caller, or one casting, can still pass
|
||||
// anything, and this fails at construction rather than at use.
|
||||
assertKeypairType(options.type);
|
||||
|
||||
this.#pairs = new Pairs();
|
||||
this.#ss58 = options.ss58Format;
|
||||
@@ -74,7 +96,7 @@ export class Keyring implements KeyringInstance {
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns the type of the keyring, ed25519, sr25519 or ecdsa
|
||||
* @description Returns the type of the keyring: dilithium65 or dilithium87
|
||||
*/
|
||||
public get type (): KeypairType {
|
||||
return this.#type;
|
||||
@@ -97,9 +119,14 @@ export class Keyring implements KeyringInstance {
|
||||
* `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?: EncryptedJsonEncoding[]): KeyringPair {
|
||||
const publicKey = this.decodeAddress(address, ignoreChecksum);
|
||||
assertKeypairType(type);
|
||||
|
||||
return this.addPair(createPair({ toSS58: this.encodeAddress, type }, { publicKey, secretKey: new Uint8Array() }, meta, encoded, encType));
|
||||
// An address is a Poseidon2 hash of an ML-DSA public key, not the key, so it
|
||||
// is carried as an account id. Upstream passed it as `publicKey`, which for
|
||||
// these types made every watch-only account report the hash of its address.
|
||||
const accountId = this.decodeAddress(address, ignoreChecksum);
|
||||
|
||||
return this.addPair(createPair({ toSS58: this.encodeAddress, type }, { accountId, publicKey: new Uint8Array(), secretKey: new Uint8Array() }, meta, encoded, encType));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -121,8 +148,8 @@ export class Keyring implements KeyringInstance {
|
||||
* of an account backup), and then generates a keyring pair from it that it passes to
|
||||
* `addPair` to stores in a keyring pair dictionary the public key of the generated pair as a key and the pair as the associated value.
|
||||
*/
|
||||
public addFromMnemonic (mnemonic: string, meta: KeyringPair$Meta = {}, type: KeypairType = this.type): KeyringPair {
|
||||
return this.addFromUri(mnemonic, meta, type);
|
||||
public addFromMnemonic (mnemonic: string, meta: KeyringPair$Meta = {}, type: KeypairType = this.type, wordlist?: string[]): KeyringPair {
|
||||
return this.addFromUri(mnemonic, meta, type, wordlist);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,7 +171,7 @@ export class Keyring implements KeyringInstance {
|
||||
*/
|
||||
public addFromSeed (seed: Uint8Array, meta: KeyringPair$Meta = {}, type: KeypairType = this.type): KeyringPair {
|
||||
return this.addPair(
|
||||
createPair({ toSS58: this.encodeAddress, type }, PairFromSeed[type](seed), meta, null)
|
||||
this.createFromPair(dilithiumPairFromSeed(seed, type), meta, type)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -153,9 +180,9 @@ export class Keyring implements KeyringInstance {
|
||||
* @summary Creates an account via an suri
|
||||
* @description Extracts the phrase, path and password from a SURI format for specifying secret keys `<secret>/<soft-key>//<hard-key>///<password>` (the `///password` may be omitted, and `/<soft-key>` and `//<hard-key>` maybe repeated and mixed). The secret can be a hex string, mnemonic phrase or a string (to be padded)
|
||||
*/
|
||||
public addFromUri (suri: string, meta: KeyringPair$Meta = {}, type: KeypairType = this.type): KeyringPair {
|
||||
public addFromUri (suri: string, meta: KeyringPair$Meta = {}, type: KeypairType = this.type, wordlist?: string[]): KeyringPair {
|
||||
return this.addPair(
|
||||
this.createFromUri(suri, meta, type)
|
||||
this.createFromUri(suri, meta, type, wordlist)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -175,19 +202,24 @@ export class Keyring implements KeyringInstance {
|
||||
? [type]
|
||||
: type;
|
||||
|
||||
if (!['ed25519', 'sr25519', 'ecdsa', 'ethereum'].includes(cryptoType)) {
|
||||
throw new Error(`Unknown crypto type ${cryptoType}`);
|
||||
}
|
||||
// The guard that matters most: JSON arrives from a file the user chose, and a
|
||||
// polkadot{.js} backup of an sr25519 account must be refused with a reason,
|
||||
// not constructed and not reported as an unknown type.
|
||||
assertKeypairType(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)
|
||||
const raw = isHex(address)
|
||||
? hexToU8a(address)
|
||||
: this.decodeAddress(address, ignoreChecksum);
|
||||
const decoded = isHex(encoded)
|
||||
? hexToU8a(encoded)
|
||||
: base64Decode(encoded);
|
||||
|
||||
return createPair({ toSS58: this.encodeAddress, type: cryptoType as KeypairType }, { publicKey, secretKey: new Uint8Array() }, meta, decoded, encType);
|
||||
// The account id is a one-way Poseidon2 hash, and the 1952/2592-byte public
|
||||
// key is inside `decoded`, encrypted until someone supplies a password. So
|
||||
// carry the account id as an account id: createPair uses it for the address
|
||||
// while locked, and checks it against the real public key the moment
|
||||
// `decodePkcs8` produces one.
|
||||
return createPair({ toSS58: this.encodeAddress, type: cryptoType }, { accountId: raw, publicKey: new Uint8Array(), secretKey: new Uint8Array() }, meta, decoded, encType);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -195,6 +227,8 @@ export class Keyring implements KeyringInstance {
|
||||
* @summary Creates a pair from an explicit publicKey/secreteKey combination
|
||||
*/
|
||||
public createFromPair (pair: Keypair, meta: KeyringPair$Meta = {}, type: KeypairType = this.type): KeyringPair {
|
||||
assertKeypairType(type);
|
||||
|
||||
return createPair({ toSS58: this.encodeAddress, type }, pair, meta, null);
|
||||
}
|
||||
|
||||
@@ -203,50 +237,40 @@ export class Keyring implements KeyringInstance {
|
||||
* @summary Creates a Keypair from an suri
|
||||
* @description This creates a pair from the suri, but does not add it to the keyring
|
||||
*/
|
||||
public createFromUri (_suri: string, meta: KeyringPair$Meta = {}, type: KeypairType = this.type): KeyringPair {
|
||||
// here we only aut-add the dev phrase if we have a hard-derived path
|
||||
public createFromUri (_suri: string, meta: KeyringPair$Meta = {}, type: KeypairType = this.type, _wordlist?: string[]): KeyringPair {
|
||||
assertKeypairType(type);
|
||||
|
||||
// here we only auto-add the dev phrase if we have a hard-derived path
|
||||
const suri = _suri.startsWith('//')
|
||||
? `${DEV_PHRASE}${_suri}`
|
||||
: _suri;
|
||||
const { derivePath, password, path, phrase } = keyExtractSuri(suri);
|
||||
let seed: Uint8Array;
|
||||
const isPhraseHex = isHex(phrase, 256);
|
||||
const { derivePath, password, phrase } = keyExtractSuri(suri);
|
||||
|
||||
if (isPhraseHex) {
|
||||
seed = hexToU8a(phrase);
|
||||
} else {
|
||||
const parts = phrase.split(' ');
|
||||
|
||||
if ([12, 15, 18, 21, 24].includes(parts.length)) {
|
||||
seed = type === 'ethereum'
|
||||
? mnemonicToLegacySeed(phrase, '', false, 64)
|
||||
: mnemonicToMiniSecret(phrase, password);
|
||||
} else {
|
||||
if (phrase.length > 32) {
|
||||
throw new Error('specified phrase is not a valid mnemonic and is invalid as a raw seed at > 32 bytes');
|
||||
}
|
||||
|
||||
seed = stringToU8a(phrase.padEnd(32));
|
||||
// ML-DSA derives from the mnemonic itself along a hardened BIP44 path, not
|
||||
// from a seed along a junction chain: lattice keys have no public
|
||||
// derivability, so there is no soft junction to emulate, and the chain's own
|
||||
// Pair::derive refuses for the same reason.
|
||||
if (isHex(phrase, 256)) {
|
||||
// A raw 32-byte seed goes straight into keygen, which is how the
|
||||
// dev-genesis accounts are defined. Combining one with a derivation path
|
||||
// is ambiguous (is the seed the master, or already derived?), so refuse
|
||||
// rather than pick.
|
||||
if (derivePath) {
|
||||
throw new Error('A derivation path cannot be combined with a raw seed for post-quantum pairs');
|
||||
}
|
||||
|
||||
return this.createFromPair(dilithiumPairFromSeed(hexToU8a(phrase), type), meta, type);
|
||||
}
|
||||
|
||||
const derived = type === 'ethereum'
|
||||
? isPhraseHex
|
||||
? PairFromSeed[type](seed) // for eth, if the private key is provided as suri, it must be derived only once
|
||||
: hdEthereum(seed, derivePath.substring(1))
|
||||
: keyFromPath(PairFromSeed[type](seed), path, type);
|
||||
|
||||
return createPair({ toSS58: this.encodeAddress, type }, derived, meta, null);
|
||||
return this.createFromPair(
|
||||
dilithiumPairFromMnemonic(phrase, password || '', dilithiumPathFromSuri(type, derivePath), type),
|
||||
meta,
|
||||
type
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @name encodeAddress
|
||||
* @description Encodes the input into an ss58 representation
|
||||
*/
|
||||
public encodeAddress = (address: Uint8Array | string, ss58Format?: number): string => {
|
||||
return this.type === 'ethereum'
|
||||
? ethereumEncode(address)
|
||||
: encodeAddress(address, ss58Format ?? this.#ss58);
|
||||
return encodeAddress(address, ss58Format ?? this.#ss58);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/keyring authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export * from './index.js';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/keyring authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Do not edit, auto-generated by @polkadot/dev
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright 2017-2024 @polkadot/keyring authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Do not edit, auto-generated by @polkadot/dev
|
||||
|
||||
export const packageInfo = { name: '@polkadot/keyring', path: 'auto', type: 'auto', version: '13.0.1' };
|
||||
export const packageInfo = { name: '@polkadot/keyring', path: 'auto', type: 'auto', version: '14.0.3-quantus.3' };
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
// Copyright 2017-2024 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
import { createTestPairs } from '../testingPairs.js';
|
||||
|
||||
const keyring = createTestPairs({ type: 'ed25519' }, false);
|
||||
|
||||
describe('decode', (): void => {
|
||||
it('fails when no data provided', (): void => {
|
||||
expect(
|
||||
(): void => keyring.alice.decodePkcs8()
|
||||
).toThrow(/No encrypted data available/);
|
||||
});
|
||||
|
||||
it('returns correct publicKey from encoded', (): void => {
|
||||
const PASS = 'testing';
|
||||
|
||||
expect(
|
||||
(): void => keyring.alice.decodePkcs8(
|
||||
PASS, keyring.alice.encodePkcs8(PASS)
|
||||
)
|
||||
).not.toThrow();
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/keyring authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { EncryptedJsonEncoding } from '@polkadot/util-crypto/types';
|
||||
@@ -16,8 +16,20 @@ const SEED_OFFSET = PAIR_HDR.length;
|
||||
* For divisor/headers, don't rely on the magic being static. These will
|
||||
* change between generations, aka with the long-awaited 4th generation
|
||||
* of the format. The external decode interface is the only way to use and decode these.
|
||||
*
|
||||
* `secretLength` exists for keys that are not 32 or 64 bytes. The body is
|
||||
* `HDR ‖ secretKey ‖ DIV ‖ publicKey`, and upstream located the divider by
|
||||
* trying the two lengths every curve scheme uses. An ML-DSA secret is 4032 or
|
||||
* 4896 bytes, so neither offset matches and the decode throws "Invalid encoding
|
||||
* divider found in body" — a stored account that cannot be read back.
|
||||
*
|
||||
* The caller passes the length rather than this function searching for the
|
||||
* divider. Searching would work almost always and fail catastrophically when it
|
||||
* did not: PAIR_DIV is five bytes, so a 4032-byte secret contains a false match
|
||||
* about once in 270 million keys, and the result would be a silently wrong key
|
||||
* rather than an error. The caller knows the type, so it knows the length.
|
||||
**/
|
||||
export function decodePair (passphrase?: string, encrypted?: Uint8Array | null, _encType?: EncryptedJsonEncoding | EncryptedJsonEncoding[]): { publicKey: Uint8Array; secretKey: Uint8Array } {
|
||||
export function decodePair (passphrase?: string, encrypted?: Uint8Array | null, _encType?: EncryptedJsonEncoding | EncryptedJsonEncoding[], secretLength?: number): { publicKey: Uint8Array; secretKey: Uint8Array } {
|
||||
const encType = Array.isArray(_encType) || _encType === undefined
|
||||
? _encType
|
||||
: [_encType];
|
||||
@@ -29,6 +41,23 @@ export function decodePair (passphrase?: string, encrypted?: Uint8Array | null,
|
||||
throw new Error('Invalid encoding header found in body');
|
||||
}
|
||||
|
||||
// an explicitly-sized secret (ML-DSA); there is one offset and it either
|
||||
// matches or the blob is not what the caller said it was
|
||||
if (secretLength) {
|
||||
const divOffset = SEED_OFFSET + secretLength;
|
||||
|
||||
if (!u8aEq(decrypted.subarray(divOffset, divOffset + PAIR_DIV.length), PAIR_DIV)) {
|
||||
throw new Error('Invalid encoding divider found in body');
|
||||
}
|
||||
|
||||
return {
|
||||
// the public key is the remainder: its length varies by scheme and the
|
||||
// body ends here, so there is nothing to read past it
|
||||
publicKey: decrypted.subarray(divOffset + PAIR_DIV.length),
|
||||
secretKey: decrypted.subarray(SEED_OFFSET, divOffset)
|
||||
};
|
||||
}
|
||||
|
||||
// setup for generation 3 format
|
||||
let secretKey = decrypted.subarray(SEED_OFFSET, SEED_OFFSET + SEC_LENGTH);
|
||||
let divOffset = SEED_OFFSET + SEC_LENGTH;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/keyring authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/** public/secret section divider (generation 1-3, will change in 4, don't rely on value) */
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
// Copyright 2017-2026 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
import { contextForSpec, Scheme, sizes } from '@quantus/crypto';
|
||||
|
||||
import { dilithiumVerify } from '@polkadot/util-crypto';
|
||||
|
||||
import { Keyring } from '../keyring.js';
|
||||
|
||||
// crystal_alice, from the chain's dev genesis: a raw 32-byte seed straight into
|
||||
// ML-DSA-87 keygen, no HD derivation. The expected address is what
|
||||
// `quantus developer create-test-wallets` prints — an independent implementation,
|
||||
// not this one.
|
||||
const ALICE_ADDRESS = 'qzk1Nxai3dZD9Cn5kwGcgL6mKxsfxwqdis7kDQJ52aJS2vSn7';
|
||||
|
||||
const MESSAGE = new Uint8Array([0x74, 0x68, 0x65, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64]);
|
||||
|
||||
describe('dilithium pairs', (): void => {
|
||||
const keyring = new Keyring({ ss58Format: 189, type: 'dilithium87' });
|
||||
const alice = keyring.addFromSeed(new Uint8Array(32), { name: 'crystal_alice' }, 'dilithium87');
|
||||
const context = contextForSpec(148);
|
||||
const s87 = sizes(Scheme.MlDsa87);
|
||||
const s65 = sizes(Scheme.MlDsa65);
|
||||
|
||||
it('derives the address quantus-cli derives', (): void => {
|
||||
expect(alice.address).toEqual(ALICE_ADDRESS);
|
||||
});
|
||||
|
||||
// The assumption this whole fork exists to break. Everywhere else in this
|
||||
// package `addressRaw` and `publicKey` are the same 32 bytes.
|
||||
it('has an addressRaw that is not the publicKey', (): void => {
|
||||
expect(alice.addressRaw.length).toEqual(32);
|
||||
expect(alice.publicKey.length).toEqual(s87.publicKey);
|
||||
});
|
||||
|
||||
it('signs to the wire form the runtime reads', (): void => {
|
||||
const signature = alice.sign(MESSAGE, { context });
|
||||
|
||||
expect(signature.length).toEqual(s87.signatureWithPublicKey);
|
||||
});
|
||||
|
||||
// `withType` prepends the signature enum's variant index. For the curve types
|
||||
// that enum is Substrate's MultiSignature; here it is the runtime's
|
||||
// DilithiumSignatureScheme, reached by the same mechanism.
|
||||
it('prepends the chain variant byte with withType', (): void => {
|
||||
const signed87 = alice.sign(MESSAGE, { context, withType: true });
|
||||
const k65 = keyring.addFromSeed(new Uint8Array(32).fill(9), {}, 'dilithium65');
|
||||
const signed65 = k65.sign(MESSAGE, { context, withType: true });
|
||||
|
||||
expect(signed87[0]).toEqual(0);
|
||||
expect(signed87.length).toEqual(s87.signatureWithPublicKey + 1);
|
||||
expect(signed65[0]).toEqual(1);
|
||||
expect(signed65.length).toEqual(s65.signatureWithPublicKey + 1);
|
||||
});
|
||||
|
||||
it('verifies against the account id', (): void => {
|
||||
const signature = alice.sign(MESSAGE, { context });
|
||||
|
||||
expect(dilithiumVerify(MESSAGE, signature, alice.addressRaw, 'dilithium87', context)).toEqual(true);
|
||||
});
|
||||
|
||||
// The spec-148 boundary. A signature made under the wrong context is
|
||||
// cryptographically valid and rejected by the chain, so this is the only place
|
||||
// it can be caught locally.
|
||||
it('does not verify under the wrong context', (): void => {
|
||||
const signature = alice.sign(MESSAGE, { context });
|
||||
|
||||
expect(dilithiumVerify(MESSAGE, signature, alice.addressRaw, 'dilithium87', contextForSpec(147))).toEqual(false);
|
||||
});
|
||||
|
||||
// Verification checks the embedded public key hashes to the account being
|
||||
// verified against, not merely that the signature is valid for *some* key.
|
||||
it('does not verify another account signature', (): void => {
|
||||
const bob = keyring.addFromSeed(new Uint8Array(32).fill(1), {}, 'dilithium87');
|
||||
const signature = bob.sign(MESSAGE, { context });
|
||||
|
||||
expect(dilithiumVerify(MESSAGE, signature, alice.addressRaw, 'dilithium87', context)).toEqual(false);
|
||||
});
|
||||
|
||||
it('refuses to sign without a context', (): void => {
|
||||
expect(() => alice.sign(MESSAGE)).toThrow(/signing context is required/);
|
||||
});
|
||||
|
||||
// There is no ML-DSA VRF, and faking one from an ordinary signature would
|
||||
// produce output that looks like a VRF proof and cannot be verified as one.
|
||||
it('refuses to VRF sign', (): void => {
|
||||
expect(() => alice.vrfSign(MESSAGE)).toThrow(/not available for dilithium87/);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,97 @@
|
||||
// Copyright 2017-2026 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
import { dilithiumPath } from '@polkadot/util-crypto';
|
||||
|
||||
import { Keyring } from '../keyring.js';
|
||||
|
||||
// The well-known Substrate development phrase — public by design, so pinning it
|
||||
// commits no secret. Any account it derives is assumed compromised.
|
||||
const DEV_PHRASE = 'bottom drive obey lake curtain smoke basket hold race lonely fit walk';
|
||||
|
||||
// What `quantus wallet import --mnemonic-file <DEV_PHRASE> --scheme <s>` prints.
|
||||
// An independent implementation, not this one.
|
||||
const EXPECT_65 = 'qzq29m9WvneDAeXbtgueKCREtNe1rVVs6bXSMLmjr6shqvwq6';
|
||||
const EXPECT_87 = 'qzjrYTUnnE5NduTZKxe9dESCMTZg7nTueKM3bwhnkRdD1iYV4';
|
||||
|
||||
// crystal_alice — a raw 32-byte seed straight into keygen, no derivation.
|
||||
const ALICE = 'qzk1Nxai3dZD9Cn5kwGcgL6mKxsfxwqdis7kDQJ52aJS2vSn7';
|
||||
|
||||
describe('dilithium derivation', (): void => {
|
||||
const keyring = new Keyring({ ss58Format: 189, type: 'dilithium65' });
|
||||
|
||||
it('builds the paths quantus-cli uses', (): void => {
|
||||
expect(dilithiumPath('dilithium65')).toEqual("m/44'/189189'/0'/0'/1'");
|
||||
expect(dilithiumPath('dilithium87')).toEqual("m/44'/189189'/0'/0'/0'");
|
||||
expect(dilithiumPath('dilithium65', 3)).toEqual("m/44'/189189'/3'/0'/1'");
|
||||
});
|
||||
|
||||
// The whole derivation chain at once: BIP39 to a 64-byte seed (not Substrate's
|
||||
// mnemonicToMiniSecret), the HMAC-SHA512 walk keyed with "Dilithium seed", the
|
||||
// trailing hardened index carrying the scheme, and the Poseidon2 account hash.
|
||||
it('derives what quantus-cli derives, both schemes', (): void => {
|
||||
expect(keyring.createFromUri(DEV_PHRASE, {}, 'dilithium65').address).toEqual(EXPECT_65);
|
||||
expect(keyring.createFromUri(DEV_PHRASE, {}, 'dilithium87').address).toEqual(EXPECT_87);
|
||||
});
|
||||
|
||||
it('defaults to account index 0', (): void => {
|
||||
expect(keyring.createFromUri(`${DEV_PHRASE}//0`, {}, 'dilithium65').address).toEqual(EXPECT_65);
|
||||
});
|
||||
|
||||
it('derives distinct accounts per index', (): void => {
|
||||
const zero = keyring.createFromUri(`${DEV_PHRASE}//0`, {}, 'dilithium65').address;
|
||||
const one = keyring.createFromUri(`${DEV_PHRASE}//1`, {}, 'dilithium65').address;
|
||||
|
||||
expect(zero).not.toEqual(one);
|
||||
});
|
||||
|
||||
it('accepts an explicit hardened path', (): void => {
|
||||
const byIndex = keyring.createFromUri(`${DEV_PHRASE}//2`, {}, 'dilithium65').address;
|
||||
const byPath = keyring.createFromUri(`${DEV_PHRASE}//m/44'/189189'/2'/0'/1'`, {}, 'dilithium65').address;
|
||||
|
||||
expect(byPath).toEqual(byIndex);
|
||||
});
|
||||
|
||||
// The suri syntax was built for curve junctions, where `/foo` is a soft
|
||||
// derivation over arbitrary bytes. Neither form exists for ML-DSA, and quietly
|
||||
// reinterpreting one would hand back an address no other tool derives.
|
||||
it('refuses soft derivation', (): void => {
|
||||
expect(() => keyring.createFromUri(`${DEV_PHRASE}/0`, {}, 'dilithium65')).toThrow(/Soft derivation is not possible/);
|
||||
});
|
||||
|
||||
it('refuses a named junction', (): void => {
|
||||
expect(() => keyring.createFromUri(`${DEV_PHRASE}//Alice`, {}, 'dilithium65')).toThrow(/Unsupported derivation path/);
|
||||
});
|
||||
|
||||
it('refuses an unhardened level in an explicit path', (): void => {
|
||||
expect(() => keyring.createFromUri(`${DEV_PHRASE}//m/44'/189189'/0'/0'/1`, {}, 'dilithium65')).toThrow(/Unhardened derivation/);
|
||||
});
|
||||
|
||||
// A raw seed is already key material. Whether it is the master or something
|
||||
// already derived is unanswerable, so combining it with a path is refused
|
||||
// rather than guessed.
|
||||
it('takes a raw hex seed underived, and refuses to derive from one', (): void => {
|
||||
const seed = `0x${'00'.repeat(32)}`;
|
||||
|
||||
expect(keyring.createFromUri(seed, {}, 'dilithium87').address).toEqual(ALICE);
|
||||
expect(() => keyring.createFromUri(`${seed}//1`, {}, 'dilithium87')).toThrow(/cannot be combined with a raw seed/);
|
||||
});
|
||||
|
||||
it('honours a BIP39 passphrase', (): void => {
|
||||
const plain = keyring.createFromUri(DEV_PHRASE, {}, 'dilithium65').address;
|
||||
const withPass = keyring.createFromUri(`${DEV_PHRASE}///hunter2`, {}, 'dilithium65').address;
|
||||
|
||||
expect(plain).not.toEqual(withPass);
|
||||
});
|
||||
|
||||
// Deriving a child *from a pair* is not merely unimplemented here, it is
|
||||
// impossible: ML-DSA keys are not derivable from one another, and the Quantus
|
||||
// tree derives each account from the mnemonic independently.
|
||||
it('refuses to derive from an existing pair', (): void => {
|
||||
const pair = keyring.createFromUri(DEV_PHRASE, {}, 'dilithium65');
|
||||
|
||||
expect(() => pair.derive('//1')).toThrow(/derive from the mnemonic with createFromUri/);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,103 @@
|
||||
// Copyright 2017-2026 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
import { contextForSpec } from '@quantus/crypto';
|
||||
|
||||
import { dilithiumVerify } from '@polkadot/util-crypto';
|
||||
|
||||
import { Keyring } from '../keyring.js';
|
||||
|
||||
// The address `quantus developer create-test-wallets` prints for crystal_alice.
|
||||
const ALICE_ADDRESS = 'qzk1Nxai3dZD9Cn5kwGcgL6mKxsfxwqdis7kDQJ52aJS2vSn7';
|
||||
const PASSWORD = 'not a good password';
|
||||
const MESSAGE = new Uint8Array([0x6a, 0x73, 0x6f, 0x6e]);
|
||||
|
||||
describe('dilithium account JSON', (): void => {
|
||||
const keyring = new Keyring({ ss58Format: 189, type: 'dilithium87' });
|
||||
const context = contextForSpec(148);
|
||||
|
||||
const backup = (type: 'dilithium65' | 'dilithium87', fill = 0) => {
|
||||
const pair = keyring.addFromSeed(new Uint8Array(32).fill(fill), { name: 'backed up' }, type);
|
||||
|
||||
return { json: pair.toJson(PASSWORD), original: pair };
|
||||
};
|
||||
|
||||
// The whole point. Before this worked, an exported Quantus account could not be
|
||||
// imported by anything — decodePair looked for its divider at one of two fixed
|
||||
// offsets and an ML-DSA secret is at neither, so restore threw "Invalid
|
||||
// encoding divider found in body". That is a data-durability bug, not a
|
||||
// convenience one: it had to land before any build let a user create an account.
|
||||
it('round-trips an ML-DSA-87 account', (): void => {
|
||||
const { json, original } = backup('dilithium87');
|
||||
const restored = keyring.createFromJson(json);
|
||||
|
||||
restored.decodePkcs8(PASSWORD);
|
||||
|
||||
expect(restored.address).toEqual(original.address);
|
||||
expect(restored.publicKey).toEqual(original.publicKey);
|
||||
expect(restored.isLocked).toEqual(false);
|
||||
});
|
||||
|
||||
it('round-trips an ML-DSA-65 account', (): void => {
|
||||
const { json, original } = backup('dilithium65', 5);
|
||||
const restored = keyring.createFromJson(json);
|
||||
|
||||
restored.decodePkcs8(PASSWORD);
|
||||
|
||||
expect(restored.address).toEqual(original.address);
|
||||
expect(restored.publicKey).toEqual(original.publicKey);
|
||||
});
|
||||
|
||||
// A restored pair is locked, and callers read `pair.address` off it long before
|
||||
// any password appears. For every other scheme that works because the address
|
||||
// *is* the public key; here the key is still encrypted, so the account id has
|
||||
// to be carried as data until decodePkcs8 supplies the real one.
|
||||
it('reports the same address before and after unlock', (): void => {
|
||||
const { json } = backup('dilithium87');
|
||||
const restored = keyring.createFromJson(json);
|
||||
|
||||
expect(restored.isLocked).toEqual(true);
|
||||
expect(restored.address).toEqual(ALICE_ADDRESS);
|
||||
|
||||
restored.decodePkcs8(PASSWORD);
|
||||
|
||||
expect(restored.address).toEqual(ALICE_ADDRESS);
|
||||
});
|
||||
|
||||
it('restores a pair that can still sign', (): void => {
|
||||
const { json, original } = backup('dilithium87');
|
||||
const restored = keyring.createFromJson(json);
|
||||
|
||||
restored.decodePkcs8(PASSWORD);
|
||||
|
||||
const signature = restored.sign(MESSAGE, { context });
|
||||
|
||||
expect(dilithiumVerify(MESSAGE, signature, original.addressRaw, 'dilithium87', context)).toEqual(true);
|
||||
});
|
||||
|
||||
it('fails cleanly on a wrong password', (): void => {
|
||||
const { json } = backup('dilithium87');
|
||||
const restored = keyring.createFromJson(json);
|
||||
|
||||
expect(() => restored.decodePkcs8('wrong')).toThrow();
|
||||
// and must not have half-applied anything
|
||||
expect(restored.isLocked).toEqual(true);
|
||||
expect(restored.address).toEqual(ALICE_ADDRESS);
|
||||
});
|
||||
|
||||
// For every other scheme a tampered `address` field cannot decode at all,
|
||||
// because the address is the public key. Here it decodes perfectly and yields a
|
||||
// pair reporting an address its key does not control — a user would see someone
|
||||
// else's address in their own wallet and believe they held it.
|
||||
it('rejects JSON whose address does not match its key', (): void => {
|
||||
const { json } = backup('dilithium87');
|
||||
const other = keyring.addFromSeed(new Uint8Array(32).fill(2), {}, 'dilithium87');
|
||||
const tampered = { ...json, address: other.address };
|
||||
const restored = keyring.createFromJson(tampered);
|
||||
|
||||
expect(restored.address).toEqual(other.address);
|
||||
expect(() => restored.decodePkcs8(PASSWORD)).toThrow(/does not match the address/);
|
||||
});
|
||||
});
|
||||
@@ -1,28 +0,0 @@
|
||||
// Copyright 2017-2024 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
import { NONCE_LENGTH, SCRYPT_LENGTH } from '@polkadot/util-crypto/json/constants';
|
||||
|
||||
import { createTestPairs } from '../testingPairs.js';
|
||||
import { PAIR_DIV, PAIR_HDR, PUB_LENGTH, SEC_LENGTH } from './defaults.js';
|
||||
|
||||
const DECODED_LENGTH = PAIR_DIV.length + PAIR_HDR.length + PUB_LENGTH + SEC_LENGTH;
|
||||
const ENCODED_LENGTH = 16 + DECODED_LENGTH + NONCE_LENGTH + SCRYPT_LENGTH;
|
||||
|
||||
const keyring = createTestPairs({ type: 'ed25519' }, false);
|
||||
|
||||
describe('encode', (): void => {
|
||||
it('returns PKCS8 when no passphrase supplied', (): void => {
|
||||
expect(
|
||||
keyring.alice.encodePkcs8()
|
||||
).toHaveLength(DECODED_LENGTH);
|
||||
});
|
||||
|
||||
it('returns encoded PKCS8 when passphrase supplied', (): void => {
|
||||
expect(
|
||||
keyring.alice.encodePkcs8('testing')
|
||||
).toHaveLength(ENCODED_LENGTH);
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/keyring authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { PairInfo } from './types.js';
|
||||
|
||||
@@ -1,189 +0,0 @@
|
||||
// Copyright 2017-2024 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
import { hexToU8a, u8aToHex } from '@polkadot/util';
|
||||
import { cryptoWaitReady, encodeAddress as toSS58, setSS58Format } from '@polkadot/util-crypto';
|
||||
|
||||
import { PAIRSSR25519 } from '../testing.js';
|
||||
import { createTestPairs } from '../testingPairs.js';
|
||||
import { createPair } from './index.js';
|
||||
|
||||
const keyring = createTestPairs({ type: 'ed25519' }, false);
|
||||
|
||||
const TEST_ADDRESS = '0x4119b2e6c3Cb618F4f0B93ac77f9BeeC7FF02887';
|
||||
|
||||
await cryptoWaitReady();
|
||||
|
||||
describe('pair', (): void => {
|
||||
const SIGNATURE = new Uint8Array([80, 191, 198, 147, 225, 207, 75, 88, 126, 39, 129, 109, 191, 38, 72, 181, 75, 254, 81, 143, 244, 79, 237, 38, 236, 141, 28, 252, 134, 26, 169, 234, 79, 33, 153, 158, 151, 34, 175, 188, 235, 20, 35, 135, 83, 120, 139, 211, 233, 130, 1, 208, 201, 215, 73, 80, 56, 98, 185, 196, 11, 8, 193, 14]);
|
||||
|
||||
it('has a publicKey', (): void => {
|
||||
expect(
|
||||
keyring.alice.publicKey
|
||||
).toEqual(
|
||||
new Uint8Array([209, 114, 167, 76, 218, 76, 134, 89, 18, 195, 43, 160, 168, 10, 87, 174, 105, 171, 174, 65, 14, 92, 203, 89, 222, 232, 78, 47, 68, 50, 219, 79])
|
||||
);
|
||||
expect(
|
||||
keyring.alice.addressRaw
|
||||
).toEqual(
|
||||
new Uint8Array([209, 114, 167, 76, 218, 76, 134, 89, 18, 195, 43, 160, 168, 10, 87, 174, 105, 171, 174, 65, 14, 92, 203, 89, 222, 232, 78, 47, 68, 50, 219, 79])
|
||||
);
|
||||
});
|
||||
|
||||
it('allows signing', (): void => {
|
||||
expect(
|
||||
keyring.alice.sign(
|
||||
new Uint8Array([0x61, 0x62, 0x63, 0x64])
|
||||
)
|
||||
).toEqual(SIGNATURE);
|
||||
});
|
||||
|
||||
it('validates a correctly signed message', (): void => {
|
||||
expect(
|
||||
keyring.alice.verify(
|
||||
new Uint8Array([0x61, 0x62, 0x63, 0x64]),
|
||||
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]),
|
||||
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,
|
||||
keyring.alice.publicKey
|
||||
)
|
||||
).toEqual(false);
|
||||
});
|
||||
|
||||
it('allows vrf sign and verify', (): void => {
|
||||
const message = new Uint8Array([0x61, 0x62, 0x63, 0x64, 0x65]);
|
||||
|
||||
expect(
|
||||
keyring.alice.vrfVerify(
|
||||
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' });
|
||||
|
||||
expect(keyring.bob.meta).toMatchObject({ foo: 'bar', something: 'else' });
|
||||
|
||||
keyring.bob.setMeta({ something: 'thing' });
|
||||
|
||||
expect(keyring.bob.meta).toMatchObject({ foo: 'bar', something: 'thing' });
|
||||
});
|
||||
|
||||
it('allows encoding of address with different prefixes', (): void => {
|
||||
expect(keyring.alice.address).toEqual('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQua');
|
||||
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
setSS58Format(255);
|
||||
|
||||
expect(keyring.alice.address).toEqual('yGHU8YKprxHbHdEv7oUK4rzMZXtsdhcXVG2CAMyC9WhzhjH2k');
|
||||
|
||||
// eslint-disable-next-line deprecation/deprecation
|
||||
setSS58Format(42);
|
||||
});
|
||||
|
||||
it('allows getting public key after decoding', (): void => {
|
||||
const PASS = 'testing';
|
||||
const encoded = keyring.alice.encodePkcs8(PASS);
|
||||
|
||||
const pair = createPair({ toSS58, type: 'sr25519' }, { publicKey: keyring.alice.publicKey });
|
||||
|
||||
pair.decodePkcs8(PASS, encoded);
|
||||
|
||||
expect(pair.isLocked).toEqual(false);
|
||||
});
|
||||
|
||||
it('allows derivation on the pair', (): void => {
|
||||
const alice = createPair({ toSS58, type: 'sr25519' }, { publicKey: hexToU8a(PAIRSSR25519[0].p), secretKey: hexToU8a(PAIRSSR25519[0].s) }, {});
|
||||
const stash = alice.derive('//stash');
|
||||
const soft = alice.derive('//funding/0');
|
||||
|
||||
expect(stash.publicKey).toEqual(hexToU8a(PAIRSSR25519[1].p));
|
||||
expect(soft.address).toEqual('5ECQNn7UueWHPFda5qUi4fTmTtyCnPvGnuoyVVSj5CboJh9J');
|
||||
});
|
||||
|
||||
it('fails to sign when locked', (): void => {
|
||||
const pair = createPair({ toSS58, type: 'sr25519' }, { publicKey: keyring.alice.publicKey });
|
||||
|
||||
expect(pair.isLocked).toEqual(true);
|
||||
expect((): Uint8Array =>
|
||||
pair.sign(new Uint8Array([0]))
|
||||
).toThrow('Cannot sign with a locked key pair');
|
||||
});
|
||||
|
||||
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(TEST_ADDRESS);
|
||||
expect(pair.addressRaw).toEqual(hexToU8a(TEST_ADDRESS));
|
||||
});
|
||||
|
||||
it('has a valid address from a known ethereum address (20 length)', (): void => {
|
||||
const pair = createPair({ toSS58, type: 'ethereum' }, { publicKey: new Uint8Array([75, 32, 205, 127, 248, 119, 52, 31, 46, 171, 170, 23, 158, 23, 46, 108, 95, 180, 186, 168]), secretKey: new Uint8Array([]) });
|
||||
|
||||
expect(pair.address.toLowerCase()).toEqual('0x4b20cd7ff877341f2eabaa179e172e6c5fb4baa8');
|
||||
expect(pair.addressRaw).toEqual(hexToU8a('0x4b20cd7ff877341f2eabaa179e172e6c5fb4baa8'));
|
||||
});
|
||||
|
||||
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));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,12 +1,12 @@
|
||||
// Copyright 2017-2024 @polkadot/keyring authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { EncryptedJsonEncoding, Keypair, KeypairType } from '@polkadot/util-crypto/types';
|
||||
import type { EncryptedJsonEncoding, KeypairType } from '@polkadot/util-crypto/types';
|
||||
import type { KeyringPair, KeyringPair$Json, KeyringPair$Meta, SignOptions } from '../types.js';
|
||||
import type { PairInfo } from './types.js';
|
||||
|
||||
import { objectSpread, u8aConcat, u8aEmpty, u8aEq, u8aToHex, u8aToU8a } from '@polkadot/util';
|
||||
import { blake2AsU8a, ed25519PairFromSeed as ed25519FromSeed, ed25519Sign, ethereumEncode, keccakAsU8a, keyExtractPath, keyFromPath, secp256k1Compress, secp256k1Expand, secp256k1PairFromSeed as secp256k1FromSeed, secp256k1Sign, signatureVerify, sr25519PairFromSeed as sr25519FromSeed, sr25519Sign, sr25519VrfSign, sr25519VrfVerify } from '@polkadot/util-crypto';
|
||||
import { objectSpread, u8aConcat, u8aEmpty, u8aEq, u8aToU8a } from '@polkadot/util';
|
||||
import { dilithiumAccountFromPublic, dilithiumSign, dilithiumSizes, dilithiumVerify } from '@polkadot/util-crypto';
|
||||
|
||||
import { decodePair } from './decode.js';
|
||||
import { encodePair } from './encode.js';
|
||||
@@ -19,42 +19,38 @@ interface Setup {
|
||||
|
||||
const SIG_TYPE_NONE = new Uint8Array();
|
||||
|
||||
const TYPE_FROM_SEED = {
|
||||
ecdsa: secp256k1FromSeed,
|
||||
ed25519: ed25519FromSeed,
|
||||
ethereum: secp256k1FromSeed,
|
||||
sr25519: sr25519FromSeed
|
||||
// The Quantus runtime's `DilithiumSignatureScheme`: `Dilithium87` is variant 0
|
||||
// and `Dilithium65` variant 1. Reached through the same `withType` mechanism
|
||||
// upstream used for Substrate's `MultiSignature`, so nothing above this file
|
||||
// needs to know which enum it is.
|
||||
//
|
||||
// There are no other arms. Upstream's ed25519, sr25519, ecdsa and ethereum were
|
||||
// removed with their primitives: each falls to Shor's algorithm, and this keyring
|
||||
// exists to hold keys that do not. quantus/common#6
|
||||
const TYPE_PREFIX: Record<KeypairType, Uint8Array> = {
|
||||
dilithium65: new Uint8Array([1]),
|
||||
dilithium87: new Uint8Array([0])
|
||||
};
|
||||
|
||||
const TYPE_PREFIX = {
|
||||
ecdsa: new Uint8Array([2]),
|
||||
ed25519: new Uint8Array([0]),
|
||||
ethereum: new Uint8Array([2]),
|
||||
sr25519: new Uint8Array([1])
|
||||
};
|
||||
/**
|
||||
* A post-quantum pair will not sign without being told which context to use.
|
||||
*
|
||||
* See `SignOptions.context`. The alternative — defaulting to the extrinsic
|
||||
* context — produces a valid signature that a spec-147 chain rejects, with
|
||||
* nothing local able to explain why.
|
||||
*/
|
||||
function requireContext (context?: Uint8Array): Uint8Array {
|
||||
if (!context) {
|
||||
throw new Error('A signing context is required for post-quantum keypairs; pass options.context (see contextForSpec)');
|
||||
}
|
||||
|
||||
const TYPE_SIGNATURE = {
|
||||
ecdsa: (m: Uint8Array, p: Partial<Keypair>) => secp256k1Sign(m, p, 'blake2'),
|
||||
ed25519: ed25519Sign,
|
||||
ethereum: (m: Uint8Array, p: Partial<Keypair>) => secp256k1Sign(m, p, 'keccak'),
|
||||
sr25519: sr25519Sign
|
||||
};
|
||||
|
||||
const TYPE_ADDRESS = {
|
||||
ecdsa: (p: Uint8Array) => p.length > 32 ? blake2AsU8a(p) : p,
|
||||
ed25519: (p: Uint8Array) => p,
|
||||
ethereum: (p: Uint8Array) => p.length === 20 ? p : keccakAsU8a(secp256k1Expand(p)),
|
||||
sr25519: (p: Uint8Array) => p
|
||||
};
|
||||
return context;
|
||||
}
|
||||
|
||||
function isLocked (secretKey?: Uint8Array): secretKey is undefined {
|
||||
return !secretKey || u8aEmpty(secretKey);
|
||||
}
|
||||
|
||||
function vrfHash (proof: Uint8Array, context?: string | Uint8Array, extra?: string | Uint8Array): Uint8Array {
|
||||
return blake2AsU8a(u8aConcat(context || '', extra || '', proof));
|
||||
}
|
||||
|
||||
/**
|
||||
* @name createPair
|
||||
* @summary Creates a keyring pair object
|
||||
@@ -86,18 +82,29 @@ function vrfHash (proof: Uint8Array, context?: string | Uint8Array, extra?: stri
|
||||
* 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?: EncryptedJsonEncoding[]): KeyringPair {
|
||||
export function createPair ({ toSS58, type }: Setup, { accountId, 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);
|
||||
// ML-DSA secrets are 4032/4896 bytes, which is neither of the two lengths
|
||||
// decodePair would otherwise try, so it has to be told.
|
||||
const decoded = decodePair(passphrase, userEncoded || encoded, encTypes, dilithiumSizes(type).secretKey);
|
||||
|
||||
if (decoded.secretKey.length === 64) {
|
||||
publicKey = decoded.publicKey;
|
||||
secretKey = decoded.secretKey;
|
||||
} else {
|
||||
const pair = TYPE_FROM_SEED[type](decoded.secretKey);
|
||||
publicKey = decoded.publicKey;
|
||||
secretKey = decoded.secretKey;
|
||||
|
||||
publicKey = pair.publicKey;
|
||||
secretKey = pair.secretKey;
|
||||
// The public key has only now arrived. If the pair was constructed from JSON
|
||||
// it has been reporting an address carried as data since then, so check the
|
||||
// two agree.
|
||||
//
|
||||
// This is not a paranoid check. A JSON file whose `address` field has been
|
||||
// edited decodes perfectly and would yield a pair reporting an address its
|
||||
// key does not control: a user would see an attacker's address in their own
|
||||
// wallet and believe they held it.
|
||||
if (accountId) {
|
||||
if (!u8aEq(dilithiumAccountFromPublic(publicKey), accountId)) {
|
||||
throw new Error('Decoded public key does not match the address in the account JSON');
|
||||
}
|
||||
|
||||
accountId = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -110,24 +117,19 @@ export function createPair ({ toSS58, type }: Setup, { publicKey, secretKey }: P
|
||||
return encoded;
|
||||
};
|
||||
|
||||
const encodeAddress = (): string => {
|
||||
const raw = TYPE_ADDRESS[type](publicKey);
|
||||
|
||||
return type === 'ethereum'
|
||||
? ethereumEncode(raw)
|
||||
: toSS58(raw);
|
||||
};
|
||||
// While a pair restored from JSON, or added by address, has no public key,
|
||||
// its account id cannot be computed (it is a one-way Poseidon2 hash of that
|
||||
// key), so use the one carried alongside. `decodePkcs8` clears it once the
|
||||
// real key arrives and has been checked against it.
|
||||
const addressRawOf = (): Uint8Array =>
|
||||
accountId ?? dilithiumAccountFromPublic(publicKey);
|
||||
|
||||
return {
|
||||
get address (): string {
|
||||
return encodeAddress();
|
||||
return toSS58(addressRawOf());
|
||||
},
|
||||
get addressRaw (): Uint8Array {
|
||||
const raw = TYPE_ADDRESS[type](publicKey);
|
||||
|
||||
return type === 'ethereum'
|
||||
? raw.slice(-20)
|
||||
: raw;
|
||||
return addressRawOf();
|
||||
},
|
||||
get isLocked (): boolean {
|
||||
return isLocked(secretKey);
|
||||
@@ -143,17 +145,12 @@ export function createPair ({ toSS58, type }: Setup, { publicKey, secretKey }: P
|
||||
},
|
||||
// eslint-disable-next-line sort-keys
|
||||
decodePkcs8,
|
||||
derive: (suri: string, meta?: KeyringPair$Meta): KeyringPair => {
|
||||
if (type === 'ethereum') {
|
||||
throw new Error('Unable to derive on this keypair');
|
||||
} else if (isLocked(secretKey)) {
|
||||
throw new Error('Cannot derive on a locked keypair');
|
||||
}
|
||||
|
||||
const { path } = keyExtractPath(suri);
|
||||
const derived = keyFromPath({ publicKey, secretKey }, path, type);
|
||||
|
||||
return createPair({ toSS58, type }, derived, meta, null);
|
||||
derive: (_suri: string, _meta?: KeyringPair$Meta): KeyringPair => {
|
||||
// Not "not implemented" — not possible. A child here would have to come
|
||||
// from this pair's key material, and ML-DSA keys are not derivable from
|
||||
// one another at all; the Quantus tree derives every account from the
|
||||
// mnemonic independently. So the caller needs the mnemonic, not this pair.
|
||||
throw new Error(`Unable to derive from an existing ${type} pair; derive from the mnemonic with createFromUri instead`);
|
||||
},
|
||||
encodePkcs8: (passphrase?: string): Uint8Array => {
|
||||
return recode(passphrase);
|
||||
@@ -173,48 +170,37 @@ export function createPair ({ toSS58, type }: Setup, { publicKey, secretKey }: P
|
||||
options.withType
|
||||
? TYPE_PREFIX[type]
|
||||
: SIG_TYPE_NONE,
|
||||
TYPE_SIGNATURE[type](u8aToU8a(message), { publicKey, secretKey })
|
||||
dilithiumSign(u8aToU8a(message), { publicKey, secretKey }, type, requireContext(options.context))
|
||||
);
|
||||
},
|
||||
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)
|
||||
// unless the publicKey is already an address
|
||||
const address = ['ecdsa', 'ethereum'].includes(type)
|
||||
? publicKey.length === 20
|
||||
? u8aToHex(publicKey)
|
||||
: u8aToHex(secp256k1Compress(publicKey))
|
||||
: encodeAddress();
|
||||
|
||||
return pairToJson(type, { address, meta }, recode(passphrase), !!passphrase);
|
||||
return pairToJson(type, { address: toSS58(addressRawOf()), meta }, recode(passphrase), !!passphrase);
|
||||
},
|
||||
unlock: (passphrase?: string): void => {
|
||||
return decodePkcs8(passphrase);
|
||||
},
|
||||
verify: (message: string | Uint8Array, signature: string | Uint8Array, signerPublic: string | Uint8Array): boolean => {
|
||||
return signatureVerify(message, signature, TYPE_ADDRESS[type](u8aToU8a(signerPublic))).isValid;
|
||||
verify: (message: string | Uint8Array, signature: string | Uint8Array, signerPublic: string | Uint8Array, context: Uint8Array = new Uint8Array()): boolean => {
|
||||
const pk = u8aToU8a(signerPublic);
|
||||
const sig = u8aToU8a(signature);
|
||||
const { signature: sigLength } = dilithiumSizes(type);
|
||||
|
||||
// Accept a bare signature or the chain's `signature ‖ publicKey` form; the
|
||||
// verifier needs the latter, and the public key is at hand either way.
|
||||
const sigWithPublic = sig.length === sigLength
|
||||
? u8aConcat(sig, pk)
|
||||
: sig;
|
||||
|
||||
// Unlike signing, a wrong context here cannot produce anything harmful —
|
||||
// only `false` — so the empty context used for raw bytes is the default.
|
||||
return dilithiumVerify(u8aToU8a(message), sigWithPublic, dilithiumAccountFromPublic(pk), type, context);
|
||||
},
|
||||
vrfSign: (message: string | Uint8Array, context?: string | Uint8Array, extra?: string | Uint8Array): Uint8Array => {
|
||||
if (isLocked(secretKey)) {
|
||||
throw new Error('Cannot sign with a locked key pair');
|
||||
}
|
||||
|
||||
if (type === 'sr25519') {
|
||||
return sr25519VrfSign(message, { secretKey }, context, extra);
|
||||
}
|
||||
|
||||
const proof = TYPE_SIGNATURE[type](u8aToU8a(message), { publicKey, secretKey });
|
||||
|
||||
return u8aConcat(vrfHash(proof, context, extra), proof);
|
||||
vrfSign: (): Uint8Array => {
|
||||
// There is no ML-DSA VRF, and faking one out of an ordinary signature needs
|
||||
// uniqueness ML-DSA does not have in the form required.
|
||||
throw new Error(`VRF signing is not available for ${type}`);
|
||||
},
|
||||
vrfVerify: (message: string | Uint8Array, vrfResult: Uint8Array, signerPublic: Uint8Array | string, context?: string | Uint8Array, extra?: string | Uint8Array): boolean => {
|
||||
if (type === 'sr25519') {
|
||||
return sr25519VrfVerify(message, vrfResult, publicKey, context, extra);
|
||||
}
|
||||
|
||||
const result = signatureVerify(message, u8aConcat(TYPE_PREFIX[type], vrfResult.subarray(32)), TYPE_ADDRESS[type](u8aToU8a(signerPublic)));
|
||||
|
||||
return result.isValid && u8aEq(vrfResult.subarray(0, 32), vrfHash(vrfResult.subarray(32), context, extra));
|
||||
vrfVerify: (): boolean => {
|
||||
throw new Error(`VRF verification is not available for ${type}`);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/keyring authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { KeyringPair, KeyringPair$Json, KeyringPair$Meta } from '../types.js';
|
||||
@@ -18,7 +18,7 @@ const json: KeyringPair$Json = {
|
||||
address,
|
||||
encoded: '',
|
||||
encoding: {
|
||||
content: ['pkcs8', 'ed25519'],
|
||||
content: ['pkcs8', 'dilithium65'],
|
||||
type: 'none',
|
||||
version: '0'
|
||||
},
|
||||
@@ -46,7 +46,7 @@ const pair: KeyringPair = {
|
||||
new Uint8Array(64),
|
||||
toJson: (_passphrase?: string): KeyringPair$Json =>
|
||||
json,
|
||||
type: 'ed25519',
|
||||
type: 'dilithium65',
|
||||
unlock: (_passphrase?: string): void =>
|
||||
undefined,
|
||||
verify: (_message: Uint8Array, _signature: Uint8Array): boolean =>
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
// Copyright 2017-2024 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
import { createTestPairs } from '../testingPairs.js';
|
||||
|
||||
const keyring = createTestPairs({ type: 'ed25519' }, false);
|
||||
|
||||
describe('toJson', (): void => {
|
||||
it('creates an unencoded output with no passphrase', (): void => {
|
||||
expect(
|
||||
keyring.alice.toJson()
|
||||
).toMatchObject({
|
||||
address: '5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQua',
|
||||
encoded: 'MFMCAQEwBQYDK2VwBCIEIEFsaWNlICAgICAgICAgICAgICAgICAgICAgICAgICAg0XKnTNpMhlkSwyugqApXrmmrrkEOXMtZ3uhOL0Qy20+hIwMhANFyp0zaTIZZEsMroKgKV65pq65BDlzLWd7oTi9EMttP',
|
||||
encoding: {
|
||||
content: ['pkcs8', 'ed25519'],
|
||||
type: ['none'],
|
||||
version: '3'
|
||||
},
|
||||
meta: {
|
||||
isTesting: true,
|
||||
name: 'alice'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('creates an encoded output with passphrase', (): void => {
|
||||
const json = keyring.alice.toJson('testing');
|
||||
|
||||
expect(json.encoded).toHaveLength(268);
|
||||
expect(json).toMatchObject({
|
||||
address: '5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQua',
|
||||
encoding: {
|
||||
content: ['pkcs8', 'ed25519'],
|
||||
type: ['scrypt', 'xsalsa20-poly1305'],
|
||||
version: '3'
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/keyring authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { KeypairType } from '@polkadot/util-crypto/types';
|
||||
|
||||
@@ -1,7 +1,22 @@
|
||||
// Copyright 2017-2024 @polkadot/keyring authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export interface PairInfo {
|
||||
/**
|
||||
* The 32-byte account id, when it cannot be derived from `publicKey`.
|
||||
*
|
||||
* Only ML-DSA pairs need this, and only while locked. Everywhere else the
|
||||
* address is a cheap function of the public key, so a pair restored from JSON
|
||||
* can report its address before anyone supplies a password. A Quantus account
|
||||
* id is a one-way Poseidon2 hash, and the public key lives inside the
|
||||
* encrypted blob — so between `createFromJson` and `decodePkcs8` there is
|
||||
* nothing to compute it from, and callers read `pair.address` in exactly that
|
||||
* window.
|
||||
*
|
||||
* Carried as data rather than derived, and checked against the real public key
|
||||
* once that arrives.
|
||||
*/
|
||||
accountId?: Uint8Array | undefined;
|
||||
publicKey: Uint8Array;
|
||||
secretKey?: Uint8Array | undefined;
|
||||
seed?: Uint8Array | null;
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
// Copyright 2017-2024 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
import { u8aToHex } from '@polkadot/util';
|
||||
import { cryptoWaitReady, ed25519PairFromSeed, encodeAddress as toSS58, randomAsU8a, secp256k1PairFromSeed, sr25519PairFromSeed } from '@polkadot/util-crypto';
|
||||
|
||||
import { createPair } from './index.js';
|
||||
|
||||
const MESSAGE = 'this is a test message';
|
||||
const CONTEXT = 'some context';
|
||||
|
||||
await cryptoWaitReady();
|
||||
|
||||
const ecdsa = createPair({ toSS58, type: 'ecdsa' }, secp256k1PairFromSeed(randomAsU8a()));
|
||||
const ed25519 = createPair({ toSS58, type: 'ed25519' }, ed25519PairFromSeed(randomAsU8a()));
|
||||
const sr25519 = createPair({ toSS58, type: 'sr25519' }, sr25519PairFromSeed(randomAsU8a()));
|
||||
|
||||
describe('vrf', (): void => {
|
||||
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);
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/keyring authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { KeyringPair, KeyringPairs } from './types.js';
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
// Copyright 2017-2024 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
// From https://github.com/paritytech/substrate/wiki/Secret-URI-Test-Vectors
|
||||
|
||||
import type { KeypairType } from '@polkadot/util-crypto/types';
|
||||
|
||||
import { u8aToHex } from '@polkadot/util';
|
||||
import { cryptoWaitReady } from '@polkadot/util-crypto';
|
||||
|
||||
import Keyring from './index.js';
|
||||
|
||||
const PHRASE = 'bottom drive obey lake curtain smoke basket hold race lonely fit walk';
|
||||
const ETHEREUM_PHRASE = 'seed sock milk update focus rotate barely fade car face mechanic mercy';
|
||||
|
||||
const TESTS = {
|
||||
ecdsa: [
|
||||
{
|
||||
pk: '0x020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a1',
|
||||
ss: '5C7C2Z5sWbytvHpuLTvzKunnnRwQxft1jiqrLD5rhucQ5S9X',
|
||||
uri: `${PHRASE}//Alice`
|
||||
}
|
||||
],
|
||||
ethereum: [
|
||||
{
|
||||
pk: '0x0381351b1b46d2602b0992bb5d5531f9c1696b0812feb2534b6884adc47e2e1d8b',
|
||||
ss: '0x31ea8795EE32D782C8ff41a5C68Dcbf0F5B27f6d',
|
||||
uri: `${ETHEREUM_PHRASE}/m/44'/60'/0'/0/0`
|
||||
},
|
||||
{
|
||||
pk: '0x02509540919faacf9ab52146c9aa40db68172d83777250b28e4679176e49ccdd9f',
|
||||
ss: '0xf24FF3a9CF04c71Dbc94D0b566f7A27B94566cac',
|
||||
uri: `${PHRASE}/m/44'/60'/0'/0/0`
|
||||
},
|
||||
{
|
||||
pk: '0x033bc19e36ff1673910575b6727a974a9abd80c9a875d41ab3e2648dbfb9e4b518',
|
||||
ss: '0x3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0',
|
||||
uri: `${PHRASE}/m/44'/60'/0'/0/1`
|
||||
}
|
||||
],
|
||||
sr25519: [
|
||||
{
|
||||
pk: '0x46ebddef8cd9bb167dc30878d7113b7e168e6f0646beffd77d69d39bad76b47a',
|
||||
ss: '5DfhGyQdFobKM8NsWvEeAKk5EQQgYe9AydgJ7rMB6E1EqRzV',
|
||||
uri: PHRASE
|
||||
},
|
||||
{
|
||||
pk: '0xb69355deefa7a8f33e9297f5af22e680f03597a99d4f4b1c44be47e7a2275802',
|
||||
ss: '5GC6LfpV352HtJPySfAecb5JdePtf4R9Vq49NUU8RhzgBqgq',
|
||||
uri: `${PHRASE}///password`
|
||||
},
|
||||
{
|
||||
pk: '0x40b9675df90efa6069ff623b0fdfcf706cd47ca7452a5056c7ad58194d23440a',
|
||||
ss: '5DXZzrDxHbkQov4QBAY4TjpwnHCMrKXkomTnKSw8UArBEY5v',
|
||||
uri: `${PHRASE}/foo`
|
||||
},
|
||||
{
|
||||
pk: '0x547d4a55642ec7ebadc0bd29b6e570b8c926059b3c0655d4948075e9a7e6f31e',
|
||||
ss: '5DyV6fZuvPemWrUqBgWwTSgoV86w6xms3KhkFU6cQcWxU8eP',
|
||||
uri: `${PHRASE}//foo`
|
||||
},
|
||||
{
|
||||
pk: '0x3841947ffcde6f5fef26fb68b59bb8665637e30e32ec2051f99cf6b9c674fe09',
|
||||
ss: '5DLU27is5iViNopQb2KxsTyPx6j4vCu8X3sk3j3NNLkPCqKM',
|
||||
uri: `${PHRASE}//foo/bar`
|
||||
},
|
||||
{
|
||||
pk: '0xdc142f7476a7b0aa262aeccf207f1d18daa90762db393006741e8a31f39dbc53',
|
||||
ss: '5H3GPTqDSpjkfDwbHy12PD6BWm8jvGSX4xYC8UMprHpTPcRg',
|
||||
uri: `${PHRASE}/foo//bar`
|
||||
},
|
||||
{
|
||||
pk: '0xa2e56b06407a6d1e819d2fc33fa0ec604b29c2e868b70b3696bb049b8725934b',
|
||||
ss: '5FkHmNgbg64MwStgCyDi2Uw3ufFu11mqQgmWT9uwK4Lghvpv',
|
||||
uri: `${PHRASE}//foo/bar//42/69`
|
||||
},
|
||||
{
|
||||
pk: '0x0e0d24e3e1ff2c07f269c99e2e0df8681fda1851ac42fc846ca2daaa90cd8f14',
|
||||
ss: '5CP8S23JBNXYNpJsL7ESPJBNnUZE6itcfM4EnDxEhaVEU6dT',
|
||||
uri: `${PHRASE}//foo/bar//42/69///password`
|
||||
},
|
||||
{
|
||||
pk: '0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d',
|
||||
ss: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
|
||||
uri: `${PHRASE}//Alice`
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
await cryptoWaitReady();
|
||||
|
||||
describe('keyring.addFromUri', (): void => {
|
||||
for (const [type, tests] of Object.entries(TESTS)) {
|
||||
const keyring = new Keyring({ type: type as KeypairType });
|
||||
|
||||
describe(`${type}`, (): void => {
|
||||
tests.forEach(({ pk, ss, uri }): void => {
|
||||
it(`creates ${uri}`, (): void => {
|
||||
const pair = keyring.addFromUri(uri, {}, type as KeypairType);
|
||||
|
||||
expect(u8aToHex(pair.publicKey)).toEqual(pk);
|
||||
expect(pair.address).toEqual(ss);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
+34
-137
@@ -1,151 +1,48 @@
|
||||
// Copyright 2017-2024 @polkadot/keyring authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { HexString } from '@polkadot/util/types';
|
||||
import type { KeypairType } from '@polkadot/util-crypto/types';
|
||||
import type { KeyringInstance, KeyringOptions } from './types.js';
|
||||
|
||||
import { hexToU8a } from '@polkadot/util';
|
||||
|
||||
import { createPair } from './pair/index.js';
|
||||
import { Keyring } from './keyring.js';
|
||||
|
||||
interface PairDef {
|
||||
name?: string;
|
||||
p: HexString;
|
||||
s: HexString;
|
||||
seed?: string;
|
||||
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 PAIRSSR25519: PairDef[] = [
|
||||
{
|
||||
p: '0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d',
|
||||
s: '0x98319d4ff8a9508c4bb0cf0b5a78d760a0b2082c02775e6e82370816fedfff48925a225d97aa00682d6a59b95b18780c10d7032336e88f3442b42361f4a66011', // nosemgrep
|
||||
seed: 'Alice',
|
||||
type: 'sr25519'
|
||||
},
|
||||
{
|
||||
p: '0xbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f',
|
||||
s: '0xe8da6c9d810e020f5e3c7f5af2dea314cbeaa0d72bc6421e92c0808a0c584a6046ab28e97c3ffc77fe12b5a4d37e8cd4afbfebbf2391ffc7cb07c0f38c023efd', // nosemgrep
|
||||
seed: 'Alice//stash',
|
||||
type: 'sr25519'
|
||||
},
|
||||
{
|
||||
p: '0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48',
|
||||
s: '0x081ff694633e255136bdb456c20a5fc8fed21f8b964c11bb17ff534ce80ebd5941ae88f85d0c1bfc37be41c904e1dfc01de8c8067b0d6d5df25dd1ac0894a325', // nosemgrep
|
||||
seed: 'Bob',
|
||||
type: 'sr25519'
|
||||
},
|
||||
{
|
||||
p: '0xfe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e',
|
||||
s: '0xc006507cdfc267a21532394c49ca9b754ca71de21e15a1cdf807c7ceab6d0b6c3ed408d9d35311540dcd54931933e67cf1ea10d46f75408f82b789d9bd212fde', // nosemgrep
|
||||
seed: 'Bob//stash',
|
||||
type: 'sr25519'
|
||||
},
|
||||
{
|
||||
p: '0x90b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22',
|
||||
s: '0xa8f2d83016052e5d6d77b2f6fd5d59418922a09024cda701b3c34369ec43a7668faf12ff39cd4e5d92bb773972f41a7a5279ebc2ed92264bed8f47d344f8f18c', // nosemgrep
|
||||
seed: 'Charlie',
|
||||
type: 'sr25519'
|
||||
},
|
||||
{
|
||||
p: '0x306721211d5404bd9da88e0204360a1a9ab8b87c66c1bc2fcdd37f3c2222cc20',
|
||||
s: '0x20e05482ca4677e0edbc58ae9a3a59f6ed3b1a9484ba17e64d6fe8688b2b7b5d108c4487b9323b98b11fe36cb301b084e920f7b7895536809a6d62a451b25568', // nosemgrep
|
||||
seed: 'Dave',
|
||||
type: 'sr25519'
|
||||
},
|
||||
{
|
||||
p: '0xe659a7a1628cdd93febc04a4e0646ea20e9f5f0ce097d9a05290d4a9e054df4e',
|
||||
s: '0x683576abfd5dc35273e4264c23095a1bf21c14517bece57c7f0cc5c0ed4ce06a3dbf386b7828f348abe15d76973a72009e6ef86a5c91db2990cb36bb657c6587', // nosemgrep
|
||||
seed: 'Eve',
|
||||
type: 'sr25519'
|
||||
},
|
||||
{
|
||||
p: '0x1cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c',
|
||||
s: '0xb835c20f450079cf4f513900ae9faf8df06ad86c681884122c752a4b2bf74d4303e4f21bc6cc62bb4eeed5a9cce642c25e2d2ac1464093b50f6196d78e3a7426', // nosemgrep
|
||||
seed: 'Ferdie',
|
||||
type: 'sr25519'
|
||||
}
|
||||
];
|
||||
|
||||
export const PAIRSETHEREUM: PairDef[] = [
|
||||
{
|
||||
name: 'Alith',
|
||||
p: '0x02509540919faacf9ab52146c9aa40db68172d83777250b28e4679176e49ccdd9f',
|
||||
s: '0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133', // nosemgrep
|
||||
type: 'ethereum'
|
||||
},
|
||||
{
|
||||
name: 'Baltathar',
|
||||
p: '0x033bc19e36ff1673910575b6727a974a9abd80c9a875d41ab3e2648dbfb9e4b518',
|
||||
s: '0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b', // nosemgrep
|
||||
type: 'ethereum'
|
||||
},
|
||||
{
|
||||
name: 'Charleth',
|
||||
p: '0x0234637bdc0e89b5d46543bcbf8edff329d2702bc995e27e9af4b1ba009a3c2a5e',
|
||||
s: '0x0b6e18cafb6ed99687ec547bd28139cafdd2bffe70e6b688025de6b445aa5c5b', // nosemgrep
|
||||
type: 'ethereum'
|
||||
},
|
||||
{
|
||||
name: 'Dorothy',
|
||||
p: '0x02a00d60b2b408c2a14c5d70cdd2c205db8985ef737a7e55ad20ea32cc9e7c417c',
|
||||
s: '0x39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68', // nosemgrep
|
||||
type: 'ethereum'
|
||||
},
|
||||
{
|
||||
name: 'Ethan',
|
||||
p: '0x025cdc005b752651cd3f728fb9192182acb3a9c89e19072cbd5b03f3ee1f1b3ffa',
|
||||
s: '0x7dce9bc8babb68fec1409be38c8e1a52650206a7ed90ff956ae8a6d15eeaaef4', // nosemgrep
|
||||
type: 'ethereum'
|
||||
},
|
||||
{
|
||||
name: 'Faith',
|
||||
p: '0x037964b6c9d546da4646ada28a99e34acaa1d14e7aba861a9055f9bd200c8abf74',
|
||||
s: '0xb9d2ea9a615f3165812e8d44de0d24da9bbd164b65c4f0573e1ce2c8dbd9c8df', // nosemgrep
|
||||
type: 'ethereum'
|
||||
}
|
||||
];
|
||||
|
||||
function createMeta (name?: string, seed?: string) {
|
||||
if (!name && !seed) {
|
||||
throw new Error('Testing pair should have either a name or a seed');
|
||||
}
|
||||
|
||||
return {
|
||||
isTesting: true,
|
||||
name: name || seed?.replace('//', '_').toLowerCase()
|
||||
};
|
||||
interface DevAccount {
|
||||
name: string;
|
||||
/** The byte the 32-byte seed is filled with. */
|
||||
seed: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name testKeyring
|
||||
* @summary Create an instance of Keyring pre-populated with locked test accounts
|
||||
* @description The test accounts (i.e. alice, bob, dave, eve, ferdie)
|
||||
* are available on the dev chain and each test account is initialized with DOT funds.
|
||||
* The accounts a Quantus dev chain endows at genesis.
|
||||
*
|
||||
* From `chain:primitives/dilithium-crypto/src/pair.rs`: ML-DSA-87 keys generated
|
||||
* directly from a 32-byte seed filled with one byte, with no HD derivation. Their
|
||||
* account ids are pinned against `quantus-cli` in `@quantus/crypto`'s tests
|
||||
* (`dev_account_ids_match_the_cli`), e.g. crystal_alice is
|
||||
* `qzk1Nxai3dZD9Cn5kwGcgL6mKxsfxwqdis7kDQJ52aJS2vSn7`.
|
||||
*
|
||||
* They replace upstream's sr25519 Alice…Ferdie and Ethereum Alith…Faith, which
|
||||
* this keyring can no longer hold. quantus/common#6
|
||||
*/
|
||||
export function createTestKeyring (options: KeyringOptions = {}, isDerived = true): KeyringInstance {
|
||||
const keyring = new Keyring(options);
|
||||
const pairs = options.type === 'ethereum'
|
||||
? PAIRSETHEREUM
|
||||
: PAIRSSR25519;
|
||||
export const DEV_ACCOUNTS: DevAccount[] = [
|
||||
{ name: 'crystal_alice', seed: 0 },
|
||||
{ name: 'dilithium_bob', seed: 1 },
|
||||
{ name: 'crystal_charlie', seed: 2 }
|
||||
];
|
||||
|
||||
for (const { name, p, s, seed, type } of pairs) {
|
||||
const meta = createMeta(name, seed);
|
||||
const pair = !isDerived && !name && seed
|
||||
? keyring.addFromUri(seed, meta, options.type)
|
||||
: keyring.addPair(
|
||||
createPair(
|
||||
{ toSS58: keyring.encodeAddress, type },
|
||||
{ publicKey: hexToU8a(p), secretKey: hexToU8a(s) },
|
||||
meta
|
||||
)
|
||||
);
|
||||
/**
|
||||
* @name testKeyring
|
||||
* @summary Create an instance of Keyring pre-populated with the dev accounts
|
||||
* @description crystal_alice, dilithium_bob and crystal_charlie, as a Quantus
|
||||
* dev chain endows them. They are always ML-DSA-87, whatever `options.type` says,
|
||||
* because that is what the chain made them; `options` sets the keyring's default
|
||||
* type and SS58 format for anything added afterwards. `_isDerived` is accepted for
|
||||
* upstream's signature and ignored: these accounts have no derivation.
|
||||
*/
|
||||
export function createTestKeyring (options: KeyringOptions = {}, _isDerived = true): KeyringInstance {
|
||||
const keyring = new Keyring(options);
|
||||
|
||||
for (const { name, seed } of DEV_ACCOUNTS) {
|
||||
const pair = keyring.addFromSeed(new Uint8Array(32).fill(seed), { isTesting: true, name }, 'dilithium87');
|
||||
|
||||
pair.lock = (): void => {
|
||||
// we don't have lock/unlock functionality here
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
// Copyright 2017-2024 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
import { u8aToHex } from '@polkadot/util';
|
||||
import { cryptoWaitReady } from '@polkadot/util-crypto';
|
||||
|
||||
import Keyring from './index.js';
|
||||
import { createTestPairs } from './testingPairs.js';
|
||||
|
||||
const TEST_ADD = '0xf24FF3a9CF04c71Dbc94D0b566f7A27B94566cac';
|
||||
|
||||
await cryptoWaitReady();
|
||||
|
||||
describe('testingPairs', (): void => {
|
||||
it('creates without failing', (): void => {
|
||||
expect(
|
||||
Object.keys(createTestPairs())
|
||||
).toHaveLength(2 + 0 + 7); // stash, session, pairs
|
||||
});
|
||||
|
||||
it('has the correct address for Alice (non-HDKD)', (): void => {
|
||||
expect(
|
||||
createTestPairs({ type: 'ed25519' }, false).alice.address
|
||||
).toEqual('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQua');
|
||||
});
|
||||
|
||||
it('has the correct address for Alice (HDKD)', (): void => {
|
||||
expect(
|
||||
createTestPairs({ type: 'ed25519' }).alice.address
|
||||
).toEqual('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY');
|
||||
});
|
||||
|
||||
it('has the correct address for Alith (Eth)', (): void => {
|
||||
expect(
|
||||
createTestPairs({ type: 'ethereum' }).Alith.address
|
||||
).toEqual(TEST_ADD);
|
||||
});
|
||||
|
||||
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?.address).toEqual(TEST_ADD);
|
||||
});
|
||||
|
||||
describe('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'];
|
||||
|
||||
// @ts-expect-error We should not delete from the maps, however this is a test
|
||||
delete ring.nobody;
|
||||
|
||||
Object
|
||||
.keys(ring)
|
||||
.filter((_, i) => i < 6)
|
||||
.forEach((testKeyring, i) => {
|
||||
it(`checks #${i}`, (): void => {
|
||||
expect(
|
||||
u8aToHex(ring[testKeyring].publicKey)
|
||||
).toEqual(
|
||||
u8aToHex(keyring.createFromUri(privKeys[i]).publicKey)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2017-2024 @polkadot/keyring authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { KeypairType } from '@polkadot/util-crypto/types';
|
||||
import type { KeyringOptions, KeyringPair } from './types.js';
|
||||
|
||||
import { nobody } from './pair/nobody.js';
|
||||
@@ -9,39 +8,17 @@ import { createTestKeyring } from './testing.js';
|
||||
|
||||
export interface TestKeyringMap {
|
||||
nobody: KeyringPair;
|
||||
|
||||
[index: string]: KeyringPair;
|
||||
}
|
||||
|
||||
export interface TestKeyringMapSubstrate extends TestKeyringMap {
|
||||
alice: KeyringPair;
|
||||
bob: KeyringPair;
|
||||
charlie: KeyringPair;
|
||||
dave: KeyringPair;
|
||||
eve: KeyringPair;
|
||||
ferdie: KeyringPair;
|
||||
/** The Quantus dev accounts, by name. See `DEV_ACCOUNTS`. */
|
||||
export interface TestKeyringMapQuantus extends TestKeyringMap {
|
||||
crystal_alice: KeyringPair;
|
||||
crystal_charlie: KeyringPair;
|
||||
dilithium_bob: KeyringPair;
|
||||
}
|
||||
|
||||
export interface TestKeyringMapEthereum extends TestKeyringMap {
|
||||
Alith: KeyringPair;
|
||||
Baltathar: KeyringPair;
|
||||
Charleth: KeyringPair;
|
||||
Dorothy: KeyringPair;
|
||||
Ethan: KeyringPair;
|
||||
Faith: KeyringPair;
|
||||
}
|
||||
|
||||
export type DetectMap<O extends KeyringOptions | undefined> = DetectPairType<O> extends 'ethereum'
|
||||
? TestKeyringMapEthereum
|
||||
: TestKeyringMapSubstrate;
|
||||
|
||||
export type DetectPairType<O extends KeyringOptions | undefined> = O extends KeyringOptions
|
||||
? O['type'] extends KeypairType
|
||||
? O['type']
|
||||
: 'sr25519'
|
||||
: 'sr25519';
|
||||
|
||||
export function createTestPairs <O extends KeyringOptions, M = DetectMap<O>> (options?: O, isDerived = true): M {
|
||||
export function createTestPairs <O extends KeyringOptions, M = TestKeyringMapQuantus> (options?: O, isDerived = true): M {
|
||||
const keyring = createTestKeyring(options, isDerived);
|
||||
const pairs = keyring.getPairs();
|
||||
const map: TestKeyringMap = { nobody: nobody() };
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/keyring authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { HexString } from '@polkadot/util/types';
|
||||
@@ -7,7 +7,7 @@ import type { EncryptedJson, Keypair, KeypairType, Prefix } from '@polkadot/util
|
||||
export interface KeyringOptions {
|
||||
/** The ss58Format to use for address encoding (defaults to 42) */
|
||||
ss58Format?: Prefix;
|
||||
/** The type of keyring to create (defaults to ed25519) */
|
||||
/** The type of keyring to create (defaults to dilithium65, ML-DSA-65) */
|
||||
type?: KeypairType;
|
||||
}
|
||||
|
||||
@@ -72,6 +72,17 @@ export interface KeyringPair$Json extends EncryptedJson {
|
||||
}
|
||||
|
||||
export interface SignOptions {
|
||||
/**
|
||||
* The FIPS 204 domain-separation context, for post-quantum pairs.
|
||||
*
|
||||
* Mandatory for `dilithium65`/`dilithium87` and ignored by every other type.
|
||||
* There is deliberately no default: Quantus extrinsics on spec >= 148 are
|
||||
* verified under `QUANTUS_EXTRINSIC` and earlier ones under the empty context,
|
||||
* and a signature made under the wrong one is cryptographically valid, rejected
|
||||
* by the chain, and locally indistinguishable from a correct one. Only the
|
||||
* caller knows the spec version, so only the caller can answer.
|
||||
*/
|
||||
context?: Uint8Array;
|
||||
/** Create a MultiSignature-compatible output with an indicator type */
|
||||
withType?: boolean;
|
||||
}
|
||||
@@ -92,7 +103,11 @@ export interface KeyringPair {
|
||||
sign (message: string | Uint8Array, options?: SignOptions): Uint8Array;
|
||||
toJson (passphrase?: string): KeyringPair$Json;
|
||||
unlock (passphrase?: string): void;
|
||||
verify (message: string | Uint8Array, signature: Uint8Array, signerPublic: string | Uint8Array): boolean;
|
||||
/**
|
||||
* Verify an ML-DSA signature, bare or as the chain's `signature ‖ publicKey`,
|
||||
* under `context` (default: the empty context used for raw bytes).
|
||||
*/
|
||||
verify (message: string | Uint8Array, signature: Uint8Array, signerPublic: string | Uint8Array, context?: Uint8Array): boolean;
|
||||
vrfSign (message: string | Uint8Array, context?: string | Uint8Array, extra?: string | Uint8Array): Uint8Array;
|
||||
vrfVerify (message: string | Uint8Array, vrfResult: Uint8Array, signerPublic: string | Uint8Array, context?: string | Uint8Array, extra?: string | Uint8Array): boolean;
|
||||
}
|
||||
@@ -116,13 +131,13 @@ export interface KeyringInstance {
|
||||
addPair (pair: KeyringPair): KeyringPair;
|
||||
addFromAddress (address: string | Uint8Array, meta?: KeyringPair$Meta, encoded?: Uint8Array | null, type?: KeypairType, ignoreChecksum?: boolean): KeyringPair;
|
||||
addFromJson (pair: KeyringPair$Json, ignoreChecksum?: boolean): KeyringPair;
|
||||
addFromMnemonic (mnemonic: string, meta?: KeyringPair$Meta, type?: KeypairType): KeyringPair;
|
||||
addFromMnemonic (mnemonic: string, meta?: KeyringPair$Meta, type?: KeypairType, wordlist?: string[]): 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;
|
||||
addFromUri (suri: string, meta?: KeyringPair$Meta, type?: KeypairType, wordlist?: string[]): 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;
|
||||
createFromUri (suri: string, meta?: KeyringPair$Meta, type?: KeypairType, wordlist?: string[]): KeyringPair;
|
||||
getPair (address: string | Uint8Array): KeyringPair;
|
||||
getPairs (): KeyringPair[];
|
||||
getPublicKeys (): Uint8Array[];
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
},
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"version": "13.0.1",
|
||||
"version": "14.0.3-quantus.3",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@polkadot/util": "13.0.1",
|
||||
"@substrate/ss58-registry": "^1.46.0",
|
||||
"tslib": "^2.6.2"
|
||||
"@polkadot/util": "14.0.3",
|
||||
"@substrate/ss58-registry": "^1.51.0",
|
||||
"tslib": "^2.8.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@polkadot/hw-ledger": "13.0.1"
|
||||
"@polkadot/hw-ledger": "14.0.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/networks authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/networks authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
@@ -22,10 +22,15 @@ describe('defaults', (): void => {
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it('has no entries for testnets', (): void => {
|
||||
// Upstream asserted the opposite: that no testnet appears in knownGenesis.
|
||||
// This fork carries Quantus testnets deliberately — a wallet for a chain
|
||||
// under development has to reach its testnet — so the assertion becomes that
|
||||
// any testnet listed here is one we meant to list. See defaults/genesis.ts.
|
||||
it('has genesis entries only for testnets we deliberately support', (): void => {
|
||||
expect(
|
||||
genesisKeys.filter((network) =>
|
||||
knownTestnet[network]
|
||||
knownTestnet[network] &&
|
||||
!network.startsWith('quantus')
|
||||
)
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/networks authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/networks authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { KnownGenesis } from '../types.js';
|
||||
@@ -8,9 +8,20 @@ import type { KnownGenesis } from '../types.js';
|
||||
// oldest goes last. This make lookups for the current a simple genesisHash[0]
|
||||
// where the latest ios always the first entry (See Kusama as an example)
|
||||
//
|
||||
// IMPORTANT: Apart from the test relays, this list is limited to live parachains
|
||||
// and live production networks. It does not and should not contain any testnets,
|
||||
// either stand-alone or connected to test relays such as Westend/Rococo
|
||||
// UPSTREAM SAID: "Apart from the test relays, this list is limited to live
|
||||
// parachains and live production networks. It does not and should not contain
|
||||
// any testnets, either stand-alone or connected to test relays such as
|
||||
// Westend/Rococo"
|
||||
//
|
||||
// THIS FORK DOES NOT. That rule keeps testnets out of polkadot-js/apps' chain
|
||||
// picker, which is a sensible product decision for a production explorer and the
|
||||
// wrong one for us. Quantus is a chain under active development, and a wallet
|
||||
// that cannot talk to its testnet is a wallet that cannot be tested against
|
||||
// anything but real funds. Testnets are first-class here.
|
||||
//
|
||||
// `isTestnet` is still set honestly, so a consumer that wants to hide them can;
|
||||
// what changed is that we no longer make that choice on the consumer's behalf.
|
||||
// See `toExpanded` in ../interfaces.ts.
|
||||
export const knownGenesis: KnownGenesis = {
|
||||
acala: [
|
||||
'0xfc41b9bd8ef8fe53d58c7ea67c794c7ec9a73daf05e6d54b14ff6342c99ba64c'
|
||||
@@ -46,9 +57,17 @@ export const knownGenesis: KnownGenesis = {
|
||||
composable: [
|
||||
'0xdaab8df776eb52ec604a5df5d388bb62a050a0aaec4556a64265b9d42755552d'
|
||||
],
|
||||
creditcoin3: [
|
||||
'0x4436a7d64e363df85e065a894721002a86643283f9707338bf195d360ba2ee71', // cc3 mainnet
|
||||
'0xfc4ec97a1c1f119c4353aecb4a17c7c0cf7b40d5d660143d8bad9117e9866572', // cc3 testnet/drynet
|
||||
'0xfc9df99a665f964aed6649f275055e54df5e3420489538ed31d7788f53d11ef6' // cc3 devnet
|
||||
],
|
||||
darwinia: [
|
||||
'0xe71578b37a7c799b0ab4ee87ffa6f059a6b98f71f06fb8c84a8d88013a548ad6'
|
||||
],
|
||||
dentnet: [
|
||||
'0x0313f6a011d128d22f996703cbab05162e2fdc9e031493314fe6db79979c5ca7'
|
||||
],
|
||||
'dock-mainnet': [
|
||||
'0x6bfe24dca2a3be10f22212678ac13a6446ec764103c0f3471c71609eac384aae',
|
||||
'0xf73467c6544aa68df2ee546b135f955c46b90fa627e9b5d7935f41061bb8a5a9'
|
||||
@@ -56,22 +75,32 @@ export const knownGenesis: KnownGenesis = {
|
||||
edgeware: [
|
||||
'0x742a2ca70c2fda6cee4f8df98d64c4c670a052d9568058982dad9d5a7a135c5b'
|
||||
],
|
||||
encointer: [
|
||||
'0x7dd99936c1e9e6d1ce7d90eb6f33bea8393b4bf87677d675aa63c9cb3e8c5b5b'
|
||||
],
|
||||
enjin: [
|
||||
'0xd8761d3c88f26dc12875c00d3165f7d67243d56fc85b4cf19937601a7916e5a9'
|
||||
],
|
||||
equilibrium: [
|
||||
'0x6f1a800de3daff7f5e037ddf66ab22ce03ab91874debeddb1086f5f7dbd48925'
|
||||
],
|
||||
frequency: [
|
||||
'0x4a587bf17a404e3572747add7aab7bbe56e805a5479c6c436f07f36fcc8d3ae1'
|
||||
],
|
||||
genshiro: [
|
||||
'0x9b8cefc0eb5c568b527998bdd76c184e2b76ae561be76e4667072230217ea243'
|
||||
],
|
||||
hydradx: [
|
||||
'0xafdc188f45c71dacbaa0b62e16a91f726c7b8699a9748cdf715459de6b7f366d', // HydraDX Parachain
|
||||
'0xafdc188f45c71dacbaa0b62e16a91f726c7b8699a9748cdf715459de6b7f366d', // Hydration | HydraDX Parachain
|
||||
'0xd2a620c27ec5cbc5621ff9a522689895074f7cca0d08e7134a7804e1a3ba86fc', // Snakenet Gen3-1
|
||||
'0x10af6e84234477d84dc572bac0789813b254aa490767ed06fb9591191d1073f9', // Snakenet Gen3
|
||||
'0x3d75507dd46301767e601265791da1d9cb47b6ebc94e87347b635e5bf58bd047', // Snakenet Gen2
|
||||
'0x0ed32bfcab4a83517fac88f2aa7cbc2f88d3ab93be9a12b6188a036bf8a943c2' // Snakenet Gen1
|
||||
],
|
||||
integritee: [
|
||||
'0xcdedc8eadbfa209d3f207bba541e57c3c58a667b05a2e1d1e86353c9000758da', // on Kusama
|
||||
'0xe13e7af377c64e83f95e0d70d5e5c3c01d697a84538776c5b9bbe0e7d7b6034c' // on Polkadot
|
||||
],
|
||||
'interlay-parachain': [
|
||||
'0xbf88efe70e9e0e916416e8bed61f2b45717f517d7f3523e33c7b001e5ffcbc72'
|
||||
],
|
||||
@@ -89,9 +118,15 @@ export const knownGenesis: KnownGenesis = {
|
||||
'0xe3777fa922cafbff200cadeaea1a76bd7898ad5b89f7848999058b50e715f636', // Kusama CC2
|
||||
'0x3fd7b9eb6a00376e5be61f01abb429ffb0b104be05eaff4d458da48fcd425baf' // Kusama CC1
|
||||
],
|
||||
liberland: [
|
||||
'0x6bd89e052d67a45bb60a9a23e8581053d5e0d619f15cb9865946937e690c42d6'
|
||||
],
|
||||
matrixchain: [
|
||||
'0x3af4ff48ec76d2efc8476730f423ac07e25ad48f5f4c9dc39c778b164d808615'
|
||||
],
|
||||
mythos: [
|
||||
'0xf6ee56e9c5277df5b4ce6ae9983ee88f3cbed27d31beeb98f9f84f997a1ab0b9'
|
||||
],
|
||||
nodle: [
|
||||
'0x97da7ede98d7bad4e36b4d734b6055425a3be036da2a332ea5a7037656427a21'
|
||||
],
|
||||
@@ -104,6 +139,9 @@ export const knownGenesis: KnownGenesis = {
|
||||
parallel: [
|
||||
'0xe61a41c53f5dcd0beb09df93b34402aada44cb05117b71059cce40a2723a4e97'
|
||||
],
|
||||
peaq: [
|
||||
'0xd2a5d385932d1f650dae03ef8e2748983779ee342c614f80854d32b8cd8fa48c'
|
||||
],
|
||||
pendulum: [
|
||||
'0x5d3c298622d5634ed019bf61ea4b71655030015bde9beb0d6a24743714462c86'
|
||||
],
|
||||
@@ -114,6 +152,9 @@ export const knownGenesis: KnownGenesis = {
|
||||
'0x6811a339673c9daa897944dcdac99c6e2939cc88245ed21951a0a3c9a2be75bc',
|
||||
'0xe8e7f0f4c4f5a00720b4821dbfddefea7490bcf0b19009961cc46957984e2c1c'
|
||||
],
|
||||
polimec: [
|
||||
'0x7eb9354488318e7549c722669dcbdcdc526f1fef1420e7944667212f3601fdbd'
|
||||
],
|
||||
polkadex: [
|
||||
'0x3920bcb4960a1eef5580cd5367ff3f430eef052774f78468852f7b9cb39f8a3c'
|
||||
],
|
||||
@@ -123,6 +164,18 @@ export const knownGenesis: KnownGenesis = {
|
||||
polymesh: [
|
||||
'0x6fbd74e5e1d0a61d52ccfe9d4adaed16dd3a7caa37c6bc4d0c2fa12e8b2f4063'
|
||||
],
|
||||
// Quantus mainnet, read from the node with `chain_getBlockHash(0)` on
|
||||
// 2026-09-09, the day it launched. Not the staging chain, which is
|
||||
// `0xb0e90ed9f291a9194f79…` and which telemetry confusingly calls
|
||||
// "Quantus Staging Mainnet".
|
||||
quantus: [
|
||||
'0xfb5487c0be6ae4ade2d41d16e50465129861636c2b8d61fa94d7a19631626fba'
|
||||
],
|
||||
// Heisenberg, the public Quantus testnet. A testnet carrying a genesis hash is
|
||||
// a deliberate departure from upstream — see the note above.
|
||||
'quantus-heisenberg': [
|
||||
'0xa5aa9e5c84d4a3722c152295e7973c9af522f2fb1ef7db5afaa3d5f4dc8d3b4f'
|
||||
],
|
||||
quartz: [
|
||||
'0xcd4d732201ebe5d6b014edda071c4203e16867305332301dc8d092044b28e554'
|
||||
],
|
||||
@@ -161,6 +214,9 @@ export const knownGenesis: KnownGenesis = {
|
||||
unique: [
|
||||
'0x84322d9cddbf35088f1e54e9a85c967a41a56a4f43445768125e61af166c7d31'
|
||||
],
|
||||
vara: [
|
||||
'0xfe1b4c55fd4d668101126434206571a7838a8b6b93a6d1b95d607e78e6c53763'
|
||||
],
|
||||
vtb: [
|
||||
'0x286bc8414c7000ce1d6ee6a834e29a54c1784814b76243eb77ed0b2c5573c60f',
|
||||
'0x7483b89572fb2bd687c7b9a93b242d0b237f9aba463aba07ec24503931038aaa'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/networks authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/networks authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { KnownIcon } from '../types.js';
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// Copyright 2017-2024 @polkadot/networks authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/networks authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export { knownGenesis } from './genesis.js';
|
||||
export { knownIcon } from './icons.js';
|
||||
export { knownLedger } from './ledger.js';
|
||||
export { knownTestnet } from './testnets.js';
|
||||
export { knownQuantus } from './quantus.js';
|
||||
export { exposedTestnet, knownTestnet } from './testnets.js';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/networks authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/networks authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { KnownLedger } from '../types.js';
|
||||
@@ -14,26 +14,36 @@ export const knownLedger: KnownLedger = {
|
||||
astar: 0x0000032a,
|
||||
bifrost: 0x00000314,
|
||||
'bifrost-kusama': 0x00000314,
|
||||
bittensor: 0x00000162,
|
||||
centrifuge: 0x000002eb,
|
||||
composable: 0x00000162,
|
||||
creditcoin3: 0x00000162,
|
||||
darwinia: 0x00000162,
|
||||
dentnet: 0x000002de,
|
||||
'dock-mainnet': 0x00000252,
|
||||
edgeware: 0x0000020b,
|
||||
encointer: 0x000001b2,
|
||||
enjin: 0x00000483,
|
||||
equilibrium: 0x05f5e0fd,
|
||||
frequency: 0x0000082b,
|
||||
genshiro: 0x05f5e0fc,
|
||||
hydradx: 0x00000162,
|
||||
integritee: 0x000007df,
|
||||
'interlay-parachain': 0x00000162,
|
||||
karura: 0x000002ae,
|
||||
khala: 0x000001b2,
|
||||
kusama: 0x000001b2,
|
||||
liberland: 0x000002ff,
|
||||
matrixchain: 0x00000483,
|
||||
mythos: 0x0000003c,
|
||||
nodle: 0x000003eb,
|
||||
origintrail: 0x00000162,
|
||||
parallel: 0x00000162,
|
||||
peaq: 0x00000d0a,
|
||||
pendulum: 0x00000162,
|
||||
phala: 0x00000162,
|
||||
picasso: 0x000001b2,
|
||||
polimec: 0x00000d10,
|
||||
polkadex: 0x0000031f,
|
||||
polkadot: 0x00000162,
|
||||
polymesh: 0x00000253,
|
||||
@@ -44,6 +54,7 @@ export const knownLedger: KnownLedger = {
|
||||
statemint: 0x00000162, // common-good on Polkadot, shares derivation
|
||||
ternoa: 0x00003e3,
|
||||
unique: 0x00000295,
|
||||
vara: 0x00001370,
|
||||
vtb: 0x000002b6,
|
||||
xxnetwork: 0x000007a3,
|
||||
zeitgeist: 0x00000162
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright 2017-2026 @polkadot/networks authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { KnownSubstrate } from '../types.js';
|
||||
|
||||
// Quantus networks, which `@substrate/ss58-registry` does not carry.
|
||||
//
|
||||
// Prefix 189 is unclaimed in that registry — nothing occupies 185..195 at all —
|
||||
// so this is an addition rather than an override. It lives in this fork because
|
||||
// the registry is Parity-curated and post-quantum support has not landed
|
||||
// upstream; see the standing convention in quantus/extension#1.
|
||||
//
|
||||
// `standardAccount: 'ml-dsa'` is a value upstream's type does not allow, which
|
||||
// is why `KnownSubstrate` is widened in ../types.ts. It is load-bearing: a null
|
||||
// or missing `standardAccount` makes `toExpanded` mark the network `isIgnored`
|
||||
// and it disappears from `availableNetworks`.
|
||||
export const knownQuantus: KnownSubstrate[] = [
|
||||
{
|
||||
decimals: [12],
|
||||
displayName: 'Quantus',
|
||||
network: 'quantus',
|
||||
prefix: 189,
|
||||
standardAccount: 'ml-dsa',
|
||||
symbols: ['QTC'],
|
||||
website: 'https://quantus.com'
|
||||
},
|
||||
{
|
||||
decimals: [12],
|
||||
displayName: 'Quantus Heisenberg',
|
||||
network: 'quantus-heisenberg',
|
||||
prefix: 189,
|
||||
standardAccount: 'ml-dsa',
|
||||
// HEI, not QTC — confirmed from the chain itself via system_properties on
|
||||
// a1-heisenberg.quantus.cat. A testnet token that displayed as the mainnet
|
||||
// symbol would be a good way to confuse play money for real.
|
||||
symbols: ['HEI'],
|
||||
website: 'https://quantus.com'
|
||||
}
|
||||
];
|
||||
@@ -1,9 +1,10 @@
|
||||
// Copyright 2017-2024 @polkadot/networks authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/networks authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { KnownTestnet } from '../types.js';
|
||||
|
||||
// testnets should not allow selection
|
||||
// Marks a network as a testnet. Purely descriptive — see `exposedTestnet` below
|
||||
// for which of them this fork actually offers.
|
||||
export const knownTestnet: KnownTestnet = {
|
||||
'': true, // this is the default non-network entry
|
||||
'cess-testnet': true,
|
||||
@@ -11,6 +12,24 @@ export const knownTestnet: KnownTestnet = {
|
||||
jupiter: true,
|
||||
'mathchain-testnet': true,
|
||||
p3dt: true,
|
||||
'quantus-heisenberg': true,
|
||||
subspace_testnet: true,
|
||||
'zero-alphaville': true
|
||||
};
|
||||
|
||||
// Testnets this fork exposes anyway, despite upstream's rule that a testnet is
|
||||
// never offered.
|
||||
//
|
||||
// That rule keeps testnets out of polkadot-js/apps' chain picker, which is right
|
||||
// for a production explorer and wrong for us: Quantus is under active
|
||||
// development, and a wallet that cannot reach its testnet can only be tested
|
||||
// against real funds.
|
||||
//
|
||||
// An explicit allowlist rather than dropping the rule wholesale. Dropping it
|
||||
// exposed all fifteen of upstream's testnets too, which is a change we have no
|
||||
// reason to make and no way to have thought about — it moved the tail of the
|
||||
// sorted list from 'ZERO' to 'ZERO Alphaville' and would have surprised anyone
|
||||
// reading `availableNetworks`. Opting in one network at a time says what we mean.
|
||||
export const exposedTestnet: KnownTestnet = {
|
||||
'quantus-heisenberg': true
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/networks authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/networks authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
@@ -6,7 +6,7 @@
|
||||
import type { SubstrateNetwork } from './types.js';
|
||||
|
||||
import { knownGenesis, knownIcon, knownLedger, knownTestnet } from './defaults/index.js';
|
||||
import { allNetworks, availableNetworks, selectableNetworks } from './index.js';
|
||||
import { allNetworks, availableNetworks, isQuantumSafe, selectableNetworks } from './index.js';
|
||||
|
||||
describe('availableNetworks', (): void => {
|
||||
it('has the correct starting order', (): void => {
|
||||
@@ -51,16 +51,40 @@ describe('availableNetworks', (): void => {
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it('has no testnets exposed', (): void => {
|
||||
// Upstream asserted that no testnet reaches availableNetworks at all. This
|
||||
// fork exposes the Quantus ones deliberately — see exposedTestnet — so what is
|
||||
// worth pinning is that an exposed testnet is still *labelled* one. Losing the
|
||||
// label is the failure that matters: a user cannot tell play money from real
|
||||
// money by looking at an address.
|
||||
it('labels every exposed testnet as a testnet', (): void => {
|
||||
expect(
|
||||
Object.keys(knownTestnet).filter((network) =>
|
||||
availableNetworks.some((a) =>
|
||||
a.network === network
|
||||
)
|
||||
availableNetworks.some((a) => a.network === network && !a.isTestnet)
|
||||
)
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it('exposes no testnets except the ones we opted into', (): void => {
|
||||
expect(
|
||||
availableNetworks
|
||||
.filter((a) => a.isTestnet && !a.network.startsWith('quantus'))
|
||||
.map((a) => a.network)
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
// These lists stay complete on purpose. An earlier version of this fork
|
||||
// filtered them to post-quantum chains, which broke @polkadot/types-known — it
|
||||
// looks up every chain it holds upgrade history for and throws when one is
|
||||
// missing, so @polkadot/api failed at import. Gating what a wallet offers is
|
||||
// the wallet's job; isQuantumSafe is exported for it and the extension applies
|
||||
// it in its own chain list.
|
||||
it('identifies which chains are post-quantum', (): void => {
|
||||
expect(isQuantumSafe('ml-dsa')).toEqual(true);
|
||||
expect(isQuantumSafe('Sr25519')).toEqual(false);
|
||||
expect(isQuantumSafe(null)).toEqual(false);
|
||||
expect(availableNetworks.filter((n) => isQuantumSafe(n.standardAccount)).map((n) => n.network)).toEqual(['quantus', 'quantus-heisenberg']);
|
||||
});
|
||||
|
||||
it('has allNetworks icons, except for overrides', (): void => {
|
||||
expect(
|
||||
availableNetworks.filter(({ icon, network }) =>
|
||||
@@ -92,14 +116,20 @@ describe('availableNetworks', (): void => {
|
||||
});
|
||||
|
||||
describe('allNetworks', (): void => {
|
||||
it('has no ss58 duplicates', (): void => {
|
||||
// Upstream asserted one network per SS58 prefix. That stops holding the moment
|
||||
// a chain has a testnet: Quantus mainnet and Heisenberg are both 189, because
|
||||
// they are the same chain at two deployments and an address is valid on
|
||||
// either. Consumers key on genesisHash, so sharing a prefix costs nothing.
|
||||
it('has no ss58 duplicates, except deployments of the same chain', (): void => {
|
||||
const dupes: SubstrateNetwork[] = [];
|
||||
const uniques: SubstrateNetwork[] = [];
|
||||
|
||||
allNetworks.forEach((a): void => {
|
||||
if (uniques.some((u) => u.prefix === a.prefix)) {
|
||||
const clash = uniques.find((u) => u.prefix === a.prefix);
|
||||
|
||||
if (clash && clash.network.split('-')[0] !== a.network.split('-')[0]) {
|
||||
dupes.push(a);
|
||||
} else {
|
||||
} else if (!clash) {
|
||||
uniques.push(a);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/networks authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/networks authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// TODO: This was removed, really cannot recall the reason...
|
||||
@@ -7,3 +7,4 @@
|
||||
|
||||
export * from './interfaces.js';
|
||||
export { packageInfo } from './packageInfo.js';
|
||||
export { isQuantumSafe } from './quantumSafe.js';
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// Copyright 2017-2024 @polkadot/networks authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/networks authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { KnownSubstrate, Network, SubstrateNetwork } from './types.js';
|
||||
|
||||
import knownSubstrate from '@substrate/ss58-registry';
|
||||
|
||||
import { knownGenesis, knownIcon, knownLedger, knownTestnet } from './defaults/index.js';
|
||||
import { exposedTestnet, knownGenesis, knownIcon, knownLedger, knownQuantus, knownTestnet } from './defaults/index.js';
|
||||
|
||||
// These are known prefixes that are not sorted
|
||||
const UNSORTED = [0, 2, 42];
|
||||
@@ -26,7 +26,16 @@ function toExpanded (o: KnownSubstrate): SubstrateNetwork {
|
||||
|
||||
// filtering
|
||||
n.isTestnet = !!knownTestnet[network] || TESTNETS.includes(nameParts[nameParts.length - 1]);
|
||||
n.isIgnored = n.isTestnet || (
|
||||
|
||||
// Upstream had a bare `isTestnet ||` here, so a testnet was dropped no matter
|
||||
// how complete its entry was. That suits a production chain picker and does
|
||||
// not suit us — Quantus development happens on Heisenberg, and tools that
|
||||
// cannot reach a testnet can only be tested against real funds.
|
||||
//
|
||||
// The exception is an allowlist rather than a blanket removal: see
|
||||
// `exposedTestnet`. `isTestnet` is still reported honestly either way, so a
|
||||
// consumer can label or filter — we simply no longer make that call for them.
|
||||
n.isIgnored = (n.isTestnet && !exposedTestnet[network]) || (
|
||||
!(
|
||||
o.standardAccount &&
|
||||
o.decimals?.length &&
|
||||
@@ -59,11 +68,31 @@ function sortNetworks (a: Network, b: Network): number {
|
||||
: 1;
|
||||
}
|
||||
|
||||
// This is all the Substrate networks with our additional information
|
||||
export const allNetworks = knownSubstrate.map(toExpanded);
|
||||
// This is all the Substrate networks with our additional information.
|
||||
//
|
||||
// None of these three lists is filtered to post-quantum chains, and an earlier
|
||||
// version of this fork filtering `availableNetworks` was a mistake worth
|
||||
// recording. These are *library* lists, not a product's offer: other packages
|
||||
// consume them expecting the chains Substrate knows about to be present.
|
||||
// `@polkadot/types-known` looks up every chain it has upgrade history for and
|
||||
// **throws** when one is missing, so removing Polkadot and Kusama here made
|
||||
// `@polkadot/api` fail at import — nothing to do with Quantus, and impossible to
|
||||
// diagnose from the error.
|
||||
//
|
||||
// Gating what a wallet *offers* is the wallet's decision. `isQuantumSafe` is
|
||||
// exported for exactly that, and the extension applies it in its own chain list.
|
||||
// Keeping these complete also means a pasted Polkadot address still decodes and
|
||||
// renders as what it is — recognisable, and therefore refusable.
|
||||
//
|
||||
// Quantus is appended rather than merged: `@substrate/ss58-registry` does not
|
||||
// carry it and prefix 189 is unclaimed there, so there is nothing to override.
|
||||
// Appending also means a registry bump cannot silently drop it.
|
||||
export const allNetworks = knownSubstrate
|
||||
.map((o) => toExpanded(o as KnownSubstrate))
|
||||
.concat(knownQuantus.map(toExpanded));
|
||||
|
||||
// The list of available/claimed prefixes
|
||||
// - no testnets
|
||||
// - testnets only where explicitly exposed (see exposedTestnet)
|
||||
// - we only include those where we have a standardAccount
|
||||
// - sort by name, however we keep 0, 2, 42 first in the list
|
||||
export const availableNetworks = allNetworks.filter(filterAvailable).sort(sortNetworks);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/networks authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/networks authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export * from './index.js';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/networks authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/networks authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Do not edit, auto-generated by @polkadot/dev
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright 2017-2024 @polkadot/networks authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/networks authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Do not edit, auto-generated by @polkadot/dev
|
||||
|
||||
export const packageInfo = { name: '@polkadot/networks', path: 'auto', type: 'auto', version: '13.0.1' };
|
||||
export const packageInfo = { name: '@polkadot/networks', path: 'auto', type: 'auto', version: '14.0.3-quantus.3' };
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright 2017-2026 @polkadot/networks authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { KnownSubstrate } from './types.js';
|
||||
|
||||
/**
|
||||
* The `standardAccount` values that survive a cryptographically relevant quantum
|
||||
* computer.
|
||||
*
|
||||
* Everything else in the registry — `*25519`, `Ed25519`, `Sr25519`, `secp256k1` —
|
||||
* is a discrete-log scheme, and Shor's algorithm breaks all of them at once. A
|
||||
* chain using one of those has no quantum-safe account type to offer, whatever
|
||||
* else is true of it.
|
||||
*/
|
||||
const QUANTUM_SAFE_ACCOUNTS: KnownSubstrate['standardAccount'][] = ['ml-dsa'];
|
||||
|
||||
/**
|
||||
* Whether a network's accounts are post-quantum.
|
||||
*
|
||||
* This gates what the tooling built on this fork will *offer*. It deliberately
|
||||
* does not gate address encoding or decoding: pasting a Polkadot address into a
|
||||
* Quantus wallet should render as a Polkadot address so it can be recognised and
|
||||
* refused, not fail to decode and look like a typo.
|
||||
*
|
||||
* A `null` standardAccount — which the registry uses for chains that never said —
|
||||
* is treated as unsafe. Unknown is not the same as safe, and the cost of being
|
||||
* wrong in that direction is a user holding funds under a key Shor's algorithm
|
||||
* recovers.
|
||||
*/
|
||||
export function isQuantumSafe (standardAccount: KnownSubstrate['standardAccount']): boolean {
|
||||
return QUANTUM_SAFE_ACCOUNTS.includes(standardAccount);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/networks authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/networks authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/keyring authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/keyring authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { RegistryEntry } from '@substrate/ss58-registry';
|
||||
@@ -12,7 +12,19 @@ export type KnownLedger = Record<string, number>;
|
||||
|
||||
export type KnownGenesis = Record<string, HexString[]>;
|
||||
|
||||
export type KnownSubstrate = RegistryEntry;
|
||||
/**
|
||||
* A registry entry, widened for schemes the upstream registry does not know about.
|
||||
*
|
||||
* `@substrate/ss58-registry` types `standardAccount` as a closed union of the
|
||||
* curves Substrate ships with. Quantus signs with ML-DSA, which is not one of
|
||||
* them and — given the registry is a Parity-curated list and post-quantum
|
||||
* support has not landed upstream — is not going to become one. Widening here
|
||||
* keeps the Quantus entry in `defaults/quantus.ts` type-checked rather than
|
||||
* cast, and leaves every upstream entry exactly as the registry declares it.
|
||||
*/
|
||||
export type KnownSubstrate = Omit<RegistryEntry, 'standardAccount'> & {
|
||||
standardAccount: RegistryEntry['standardAccount'] | 'ml-dsa';
|
||||
};
|
||||
|
||||
export type KnownTestnet = Record<string, true>;
|
||||
|
||||
|
||||
@@ -731,7 +731,7 @@
|
||||
{
|
||||
"prefix": 63,
|
||||
"network": "hydradx",
|
||||
"displayName": "HydraDX",
|
||||
"displayName": "Hydration",
|
||||
"symbols": [
|
||||
"HDX"
|
||||
],
|
||||
@@ -739,7 +739,7 @@
|
||||
12
|
||||
],
|
||||
"standardAccount": "*25519",
|
||||
"website": "https://hydradx.io"
|
||||
"website": "https://hydration.net"
|
||||
},
|
||||
{
|
||||
"prefix": 65,
|
||||
@@ -1417,6 +1417,19 @@
|
||||
"standardAccount": "*25519",
|
||||
"website": "https://logion.network"
|
||||
},
|
||||
{
|
||||
"prefix": 2024,
|
||||
"network": "vow-chain",
|
||||
"displayName": "Enigmatic Smile",
|
||||
"symbols": [
|
||||
"VOW"
|
||||
],
|
||||
"decimals": [
|
||||
18
|
||||
],
|
||||
"standardAccount": "*25519",
|
||||
"website": "https://www.vow.foundation/"
|
||||
},
|
||||
{
|
||||
"prefix": 2032,
|
||||
"network": "interlay",
|
||||
@@ -1575,16 +1588,16 @@
|
||||
},
|
||||
{
|
||||
"prefix": 6094,
|
||||
"network": "subspace",
|
||||
"displayName": "Subspace",
|
||||
"network": "autonomys",
|
||||
"displayName": "Autonomys",
|
||||
"symbols": [
|
||||
"SSC"
|
||||
"AI3"
|
||||
],
|
||||
"decimals": [
|
||||
18
|
||||
],
|
||||
"standardAccount": "*25519",
|
||||
"website": "https://subspace.network"
|
||||
"website": "https://autonomys.xyz"
|
||||
},
|
||||
{
|
||||
"prefix": 7007,
|
||||
@@ -1741,7 +1754,7 @@
|
||||
"TCESS"
|
||||
],
|
||||
"decimals": [
|
||||
12
|
||||
18
|
||||
],
|
||||
"standardAccount": "*25519",
|
||||
"website": "https://cess.cloud"
|
||||
@@ -1754,7 +1767,7 @@
|
||||
"CESS"
|
||||
],
|
||||
"decimals": [
|
||||
12
|
||||
18
|
||||
],
|
||||
"standardAccount": "*25519",
|
||||
"website": "https://cess.cloud"
|
||||
@@ -1811,6 +1824,19 @@
|
||||
"standardAccount": "*25519",
|
||||
"website": "https://nftmart.io"
|
||||
},
|
||||
{
|
||||
"prefix": 12850,
|
||||
"network": "analog-timechain",
|
||||
"displayName": "Analog Timechain",
|
||||
"symbols": [
|
||||
"ANLOG"
|
||||
],
|
||||
"decimals": [
|
||||
12
|
||||
],
|
||||
"standardAccount": "*25519",
|
||||
"website": "https://analog.one"
|
||||
},
|
||||
{
|
||||
"prefix": 13116,
|
||||
"network": "bittensor",
|
||||
@@ -1836,5 +1862,44 @@
|
||||
],
|
||||
"standardAccount": "*25519",
|
||||
"website": "https://goro.network"
|
||||
},
|
||||
{
|
||||
"prefix": 14998,
|
||||
"network": "mosaic-chain",
|
||||
"displayName": "Mosaic Chain",
|
||||
"symbols": [
|
||||
"MOS"
|
||||
],
|
||||
"decimals": [
|
||||
18
|
||||
],
|
||||
"standardAccount": "*25519",
|
||||
"website": "https://mosaicchain.io"
|
||||
},
|
||||
{
|
||||
"prefix": 29972,
|
||||
"network": "mythos",
|
||||
"displayName": "Mythos",
|
||||
"symbols": [
|
||||
"MYTH"
|
||||
],
|
||||
"decimals": [
|
||||
18
|
||||
],
|
||||
"standardAccount": "secp256k1",
|
||||
"website": "https://mythos.foundation"
|
||||
},
|
||||
{
|
||||
"prefix": 8888,
|
||||
"network": "xcavate",
|
||||
"displayName": "Xcavate Protocol",
|
||||
"symbols": [
|
||||
"XCAV"
|
||||
],
|
||||
"decimals": [
|
||||
12
|
||||
],
|
||||
"standardAccount": "*25519",
|
||||
"website": "https://xcavate.io/"
|
||||
}
|
||||
]
|
||||
@@ -20,7 +20,7 @@
|
||||
"./packageDetect.cjs"
|
||||
],
|
||||
"type": "module",
|
||||
"version": "13.0.1",
|
||||
"version": "14.0.3-quantus.3",
|
||||
"browser": {
|
||||
"crypto": false,
|
||||
"stream": false
|
||||
@@ -29,16 +29,18 @@
|
||||
"dependencies": {
|
||||
"@noble/curves": "^1.3.0",
|
||||
"@noble/hashes": "^1.3.3",
|
||||
"@polkadot/networks": "13.0.1",
|
||||
"@polkadot/util": "13.0.1",
|
||||
"@polkadot/wasm-crypto": "^7.3.2",
|
||||
"@polkadot/wasm-util": "^7.3.2",
|
||||
"@polkadot/x-bigint": "13.0.1",
|
||||
"@polkadot/x-randomvalues": "13.0.1",
|
||||
"@scure/base": "^1.1.5",
|
||||
"tslib": "^2.6.2"
|
||||
"@polkadot/networks": "14.0.3-quantus.3",
|
||||
"@polkadot/util": "14.0.3",
|
||||
"@polkadot/wasm-crypto": "^7.5.3",
|
||||
"@polkadot/wasm-util": "^7.5.3",
|
||||
"@polkadot/x-bigint": "14.0.3",
|
||||
"@polkadot/x-randomvalues": "14.0.3",
|
||||
"@quantus/crypto": "^0.3.0",
|
||||
"@scure/base": "^1.1.7",
|
||||
"@scure/sr25519": "^0.2.0",
|
||||
"tslib": "^2.8.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@polkadot/util": "13.0.1"
|
||||
"@polkadot/util": "14.0.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/util-crypto authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/util-crypto authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { decodeAddress } from './decode.js';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/util-crypto authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/util-crypto authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Prefix } from './types.js';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/util-crypto authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/util-crypto authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { sshash } from './sshash.js';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/util-crypto authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/util-crypto authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Prefix } from './types.js';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/util-crypto authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { availableNetworks } from '../networks.js';
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
// Copyright 2017-2024 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
import { waitReady } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { deriveAddress } from './index.js';
|
||||
|
||||
describe('deriveAddress', (): void => {
|
||||
beforeEach(async (): Promise<void> => {
|
||||
await waitReady();
|
||||
});
|
||||
|
||||
it('derives a known path', (): void => {
|
||||
expect(
|
||||
deriveAddress('5CZtJLXtVzrBJq1fMWfywDa6XuRwXekGdShPR4b8i9GWSbzB', '/joe/polkadot/0')
|
||||
).toEqual('5GZ4srnepXvdsuNVoxCGyVZd8ScDm4gkGLTKuaGARy9akjTa');
|
||||
});
|
||||
|
||||
it('fails on hard paths', (): void => {
|
||||
expect(
|
||||
() => deriveAddress('5CZtJLXtVzrBJq1fMWfywDa6XuRwXekGdShPR4b8i9GWSbzB', '//bob')
|
||||
).toThrow(/Expected suri to contain a combination of non-hard paths/);
|
||||
});
|
||||
});
|
||||
@@ -1,36 +0,0 @@
|
||||
// Copyright 2017-2024 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { DeriveJunction } from '../key/DeriveJunction.js';
|
||||
import type { Prefix } from './types.js';
|
||||
|
||||
import { keyExtractPath } from '../key/index.js';
|
||||
import { sr25519DerivePublic } from '../sr25519/index.js';
|
||||
import { decodeAddress } from './decode.js';
|
||||
import { encodeAddress } from './encode.js';
|
||||
|
||||
function filterHard ({ isHard }: DeriveJunction): boolean {
|
||||
return isHard;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name deriveAddress
|
||||
* @summary Creates a sr25519 derived address from the supplied and path.
|
||||
* @description
|
||||
* Creates a sr25519 derived address based on the input address/publicKey and the uri supplied.
|
||||
*/
|
||||
export function deriveAddress (who: string | Uint8Array, suri: string, ss58Format?: Prefix): string {
|
||||
const { path } = keyExtractPath(suri);
|
||||
|
||||
if (!path.length || path.every(filterHard)) {
|
||||
throw new Error('Expected suri to contain a combination of non-hard paths');
|
||||
}
|
||||
|
||||
let publicKey = decodeAddress(who);
|
||||
|
||||
for (const { chainCode } of path) {
|
||||
publicKey = sr25519DerivePublic(publicKey, chainCode);
|
||||
}
|
||||
|
||||
return encodeAddress(publicKey, ss58Format);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/util-crypto authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/util-crypto authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Prefix } from './types.js';
|
||||
@@ -15,7 +15,7 @@ export function encodeAddress (key: string | Uint8Array, ss58Format: Prefix = de
|
||||
// decode it, this means we can re-encode an address
|
||||
const u8a = decodeAddress(key);
|
||||
|
||||
if ((ss58Format < 0) || (ss58Format > 16383) || [46, 47].includes(ss58Format)) {
|
||||
if ((ss58Format < 0) || (ss58Format > 16383 && !ss58Exceptions.includes(ss58Format)) || [46, 47].includes(ss58Format)) {
|
||||
throw new Error('Out of range ss58Format specified');
|
||||
} else if (!defaults.allowedDecodedLengths.includes(u8a.length)) {
|
||||
throw new Error(`Expected a valid key to convert, with length ${defaults.allowedDecodedLengths.join(', ')}`);
|
||||
@@ -38,3 +38,6 @@ export function encodeAddress (key: string | Uint8Array, ss58Format: Prefix = de
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Exceptions like 29972 (Mythos chain) which uses Ethereum style account (not ss58 Encoded)
|
||||
const ss58Exceptions = [29972];
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/util-crypto authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/util-crypto authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { BN } from '@polkadot/util';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/util-crypto authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/util-crypto authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { BN } from '@polkadot/util';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/util-crypto authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2024 @polkadot/util-crypto authors & contributors
|
||||
// Copyright 2017-2026 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { u8aEq } from '@polkadot/util';
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user