UNPKG

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