UNPKG

12.5 kBJavaScriptView Raw
1import { hexToU8a, isHex, stringToU8a } from '@polkadot/util';
2import { base64Decode, decodeAddress, ed25519PairFromSeed as ed25519FromSeed, encodeAddress, ethereumEncode, hdEthereum, keyExtractSuri, keyFromPath, mnemonicToLegacySeed, mnemonicToMiniSecret, secp256k1PairFromSeed as secp256k1FromSeed, sr25519PairFromSeed as sr25519FromSeed } from '@polkadot/util-crypto';
3import { createPair } from './pair/index.js';
4import { DEV_PHRASE } from './defaults.js';
5import { Pairs } from './pairs.js';
6const PairFromSeed = {
7 ecdsa: (seed) => secp256k1FromSeed(seed),
8 ed25519: (seed) => ed25519FromSeed(seed),
9 ethereum: (seed) => secp256k1FromSeed(seed),
10 sr25519: (seed) => sr25519FromSeed(seed)
11};
12function pairToPublic({ publicKey }) {
13 return publicKey;
14}
15/**
16 * # @polkadot/keyring
17 *
18 * ## Overview
19 *
20 * @name Keyring
21 * @summary Keyring management of user accounts
22 * @description Allows generation of keyring pairs from a variety of input combinations, such as
23 * json object containing account address or public key, account metadata, and account encoded using
24 * `addFromJson`, or by providing those values as arguments separately to `addFromAddress`,
25 * or by providing the mnemonic (seed phrase) and account metadata as arguments to `addFromMnemonic`.
26 * Stores the keyring pairs in a keyring pair dictionary. Removal of the keyring pairs from the keyring pair
27 * dictionary is achieved using `removePair`. Retrieval of all the stored pairs via `getPairs` or perform
28 * lookup of a pair for a given account address or public key using `getPair`. JSON metadata associated with
29 * an account may be obtained using `toJson` accompanied by the account passphrase.
30 */
31export class Keyring {
32 __internal__pairs;
33 __internal__type;
34 __internal__ss58;
35 decodeAddress = decodeAddress;
36 constructor(options = {}) {
37 options.type = options.type || 'ed25519';
38 if (!['ecdsa', 'ethereum', 'ed25519', 'sr25519'].includes(options.type || 'undefined')) {
39 throw new Error(`Expected a keyring type of either 'ed25519', 'sr25519', 'ethereum' or 'ecdsa', found '${options.type || 'unknown'}`);
40 }
41 this.__internal__pairs = new Pairs();
42 this.__internal__ss58 = options.ss58Format;
43 this.__internal__type = options.type;
44 }
45 /**
46 * @description retrieve the pairs (alias for getPairs)
47 */
48 get pairs() {
49 return this.getPairs();
50 }
51 /**
52 * @description retrieve the publicKeys (alias for getPublicKeys)
53 */
54 get publicKeys() {
55 return this.getPublicKeys();
56 }
57 /**
58 * @description Returns the type of the keyring, ed25519, sr25519 or ecdsa
59 */
60 get type() {
61 return this.__internal__type;
62 }
63 /**
64 * @name addPair
65 * @summary Stores an account, given a keyring pair, as a Key/Value (public key, pair) in Keyring Pair Dictionary
66 */
67 addPair(pair) {
68 return this.__internal__pairs.add(pair);
69 }
70 /**
71 * @name addFromAddress
72 * @summary Stores an account, given an account address, as a Key/Value (public key, pair) in Keyring Pair Dictionary
73 * @description Allows user to explicitly provide separate inputs including account address or public key, and optionally
74 * the associated account metadata, and the default encoded value as arguments (that may be obtained from the json file
75 * of an account backup), and then generates a keyring pair from them that it passes to
76 * `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.
77 */
78 addFromAddress(address, meta = {}, encoded = null, type = this.type, ignoreChecksum, encType) {
79 const publicKey = this.decodeAddress(address, ignoreChecksum);
80 return this.addPair(createPair({ toSS58: this.encodeAddress, type }, { publicKey, secretKey: new Uint8Array() }, meta, encoded, encType));
81 }
82 /**
83 * @name addFromJson
84 * @summary Stores an account, given JSON data, as a Key/Value (public key, pair) in Keyring Pair Dictionary
85 * @description Allows user to provide a json object argument that contains account information (that may be obtained from the json file
86 * of an account backup), and then generates a keyring pair from it that it passes to
87 * `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.
88 */
89 addFromJson(json, ignoreChecksum) {
90 return this.addPair(this.createFromJson(json, ignoreChecksum));
91 }
92 /**
93 * @name addFromMnemonic
94 * @summary Stores an account, given a mnemonic, as a Key/Value (public key, pair) in Keyring Pair Dictionary
95 * @description Allows user to provide a mnemonic (seed phrase that is provided when account is originally created)
96 * argument and a metadata argument that contains account information (that may be obtained from the json file
97 * of an account backup), and then generates a keyring pair from it that it passes to
98 * `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.
99 */
100 addFromMnemonic(mnemonic, meta = {}, type = this.type) {
101 return this.addFromUri(mnemonic, meta, type);
102 }
103 /**
104 * @name addFromPair
105 * @summary Stores an account created from an explicit publicKey/secreteKey combination
106 */
107 addFromPair(pair, meta = {}, type = this.type) {
108 return this.addPair(this.createFromPair(pair, meta, type));
109 }
110 /**
111 * @name addFromSeed
112 * @summary Stores an account, given seed data, as a Key/Value (public key, pair) in Keyring Pair Dictionary
113 * @description Stores in a keyring pair dictionary the public key of the pair as a key and the pair as the associated value.
114 * Allows user to provide the account seed as an argument, and then generates a keyring pair from it that it passes to
115 * `addPair` to store in a keyring pair dictionary the public key of the generated pair as a key and the pair as the associated value.
116 */
117 addFromSeed(seed, meta = {}, type = this.type) {
118 return this.addPair(createPair({ toSS58: this.encodeAddress, type }, PairFromSeed[type](seed), meta, null));
119 }
120 /**
121 * @name addFromUri
122 * @summary Creates an account via an suri
123 * @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)
124 */
125 addFromUri(suri, meta = {}, type = this.type) {
126 return this.addPair(this.createFromUri(suri, meta, type));
127 }
128 /**
129 * @name createFromJson
130 * @description Creates a pair from a JSON keyfile
131 */
132 createFromJson({ address, encoded, encoding: { content, type, version }, meta }, ignoreChecksum) {
133 if (version === '3' && content[0] !== 'pkcs8') {
134 throw new Error(`Unable to decode non-pkcs8 type, [${content.join(',')}] found}`);
135 }
136 const cryptoType = version === '0' || !Array.isArray(content)
137 ? this.type
138 : content[1];
139 const encType = !Array.isArray(type)
140 ? [type]
141 : type;
142 if (!['ed25519', 'sr25519', 'ecdsa', 'ethereum'].includes(cryptoType)) {
143 throw new Error(`Unknown crypto type ${cryptoType}`);
144 }
145 // 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
146 const publicKey = isHex(address)
147 ? hexToU8a(address)
148 : this.decodeAddress(address, ignoreChecksum);
149 const decoded = isHex(encoded)
150 ? hexToU8a(encoded)
151 : base64Decode(encoded);
152 return createPair({ toSS58: this.encodeAddress, type: cryptoType }, { publicKey, secretKey: new Uint8Array() }, meta, decoded, encType);
153 }
154 /**
155 * @name createFromPair
156 * @summary Creates a pair from an explicit publicKey/secreteKey combination
157 */
158 createFromPair(pair, meta = {}, type = this.type) {
159 return createPair({ toSS58: this.encodeAddress, type }, pair, meta, null);
160 }
161 /**
162 * @name createFromUri
163 * @summary Creates a Keypair from an suri
164 * @description This creates a pair from the suri, but does not add it to the keyring
165 */
166 createFromUri(_suri, meta = {}, type = this.type) {
167 // here we only aut-add the dev phrase if we have a hard-derived path
168 const suri = _suri.startsWith('//')
169 ? `${DEV_PHRASE}${_suri}`
170 : _suri;
171 const { derivePath, password, path, phrase } = keyExtractSuri(suri);
172 let seed;
173 const isPhraseHex = isHex(phrase, 256);
174 if (isPhraseHex) {
175 seed = hexToU8a(phrase);
176 }
177 else {
178 const parts = phrase.split(' ');
179 if ([12, 15, 18, 21, 24].includes(parts.length)) {
180 seed = type === 'ethereum'
181 ? mnemonicToLegacySeed(phrase, '', false, 64)
182 : mnemonicToMiniSecret(phrase, password);
183 }
184 else {
185 if (phrase.length > 32) {
186 throw new Error('specified phrase is not a valid mnemonic and is invalid as a raw seed at > 32 bytes');
187 }
188 seed = stringToU8a(phrase.padEnd(32));
189 }
190 }
191 const derived = type === 'ethereum'
192 ? isPhraseHex
193 ? PairFromSeed[type](seed) // for eth, if the private key is provided as suri, it must be derived only once
194 : hdEthereum(seed, derivePath.substring(1))
195 : keyFromPath(PairFromSeed[type](seed), path, type);
196 return createPair({ toSS58: this.encodeAddress, type }, derived, meta, null);
197 }
198 /**
199 * @name encodeAddress
200 * @description Encodes the input into an ss58 representation
201 */
202 encodeAddress = (address, ss58Format) => {
203 return this.type === 'ethereum'
204 ? ethereumEncode(address)
205 : encodeAddress(address, ss58Format ?? this.__internal__ss58);
206 };
207 /**
208 * @name getPair
209 * @summary Retrieves an account keyring pair from the Keyring Pair Dictionary, given an account address
210 * @description Returns a keyring pair value from the keyring pair dictionary by performing
211 * a key lookup using the provided account address or public key (after decoding it).
212 */
213 getPair(address) {
214 return this.__internal__pairs.get(address);
215 }
216 /**
217 * @name getPairs
218 * @summary Retrieves all account keyring pairs from the Keyring Pair Dictionary
219 * @description Returns an array list of all the keyring pair values that are stored in the keyring pair dictionary.
220 */
221 getPairs() {
222 return this.__internal__pairs.all();
223 }
224 /**
225 * @name getPublicKeys
226 * @summary Retrieves Public Keys of all Keyring Pairs stored in the Keyring Pair Dictionary
227 * @description Returns an array list of all the public keys associated with each of the keyring pair values that are stored in the keyring pair dictionary.
228 */
229 getPublicKeys() {
230 return this.__internal__pairs.all().map(pairToPublic);
231 }
232 /**
233 * @name removePair
234 * @description Deletes the provided input address or public key from the stored Keyring Pair Dictionary.
235 */
236 removePair(address) {
237 this.__internal__pairs.remove(address);
238 }
239 /**
240 * @name setSS58Format;
241 * @description Sets the ss58 format for the keyring
242 */
243 setSS58Format(ss58) {
244 this.__internal__ss58 = ss58;
245 }
246 /**
247 * @name toJson
248 * @summary Returns a JSON object associated with the input argument that contains metadata assocated with an account
249 * @description Returns a JSON object containing the metadata associated with an account
250 * when valid address or public key and when the account passphrase is provided if the account secret
251 * is not already unlocked and available in memory. Note that in [Polkadot-JS Apps](https://github.com/polkadot-js/apps) the user
252 * may backup their account to a JSON file that contains this information.
253 */
254 toJson(address, passphrase) {
255 return this.__internal__pairs.get(address).toJson(passphrase);
256 }
257}