@prosopo/keyring 2.9.99 → 2.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build$colon$cjs.log +3 -6
- package/.turbo/turbo-build$colon$tsc.log +11 -14
- package/.turbo/turbo-build.log +4 -10
- package/CHANGELOG.md +39 -0
- package/LICENSE +201 -0
- package/dist/cjs/pair/index.cjs +2 -2
- package/dist/pair/index.js +2 -2
- package/dist/pair/index.js.map +1 -1
- package/dist/pair/nobody.d.ts.map +1 -1
- package/dist/pair/nobody.js.map +1 -1
- package/package.json +59 -60
- package/src/accounts/getPair.test.ts +0 -142
- package/src/accounts/getPair.ts +0 -63
- package/src/accounts/index.ts +0 -16
- package/src/accounts/mnemonic.test.ts +0 -103
- package/src/accounts/mnemonic.ts +0 -45
- package/src/accounts/testAccounts.test.ts +0 -102
- package/src/accounts/testAccounts.ts +0 -66
- package/src/index.ts +0 -16
- package/src/keyring/index.ts +0 -15
- package/src/keyring/keyring.test.ts +0 -373
- package/src/keyring/keyring.ts +0 -411
- package/src/keyring/pairs.test.ts +0 -133
- package/src/keyring/pairs.ts +0 -44
- package/src/keyring/testing.test.ts +0 -146
- package/src/keyring/testing.ts +0 -119
- package/src/keyring/testingPairs.ts +0 -61
- package/src/keyring.test-d.ts +0 -134
- package/src/pair/decode.spec.ts +0 -25
- package/src/pair/decode.ts +0 -64
- package/src/pair/defaults.ts +0 -22
- package/src/pair/encode.spec.ts +0 -30
- package/src/pair/encode.ts +0 -32
- package/src/pair/index.ts +0 -304
- package/src/pair/nobody.test.ts +0 -127
- package/src/pair/nobody.ts +0 -78
- package/src/pair/toJson.spec.ts +0 -46
- package/src/pair/toJson.ts +0 -28
- package/tsconfig.cjs.json +0 -30
- package/tsconfig.json +0 -33
- package/tsconfig.tsbuildinfo +0 -1
- package/tsconfig.types.json +0 -9
|
@@ -1,373 +0,0 @@
|
|
|
1
|
-
// Copyright 2021-2026 Prosopo (UK) Ltd.
|
|
2
|
-
//
|
|
3
|
-
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
// you may not use this file except in compliance with the License.
|
|
5
|
-
// You may obtain a copy of the License at
|
|
6
|
-
//
|
|
7
|
-
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
//
|
|
9
|
-
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
// See the License for the specific language governing permissions and
|
|
13
|
-
// limitations under the License.
|
|
14
|
-
|
|
15
|
-
import { u8aToHex } from "@polkadot/util";
|
|
16
|
-
import type { KeyringPair, KeyringPair$Json } from "@prosopo/types";
|
|
17
|
-
import type { KeypairType } from "@prosopo/util-crypto";
|
|
18
|
-
import {
|
|
19
|
-
decodeAddress,
|
|
20
|
-
encodeAddress,
|
|
21
|
-
mnemonicToMiniSecret,
|
|
22
|
-
sr25519FromSeed,
|
|
23
|
-
} from "@prosopo/util-crypto";
|
|
24
|
-
import { describe, expect, it } from "vitest";
|
|
25
|
-
import { DEV_PHRASE, Keyring } from "./keyring.js";
|
|
26
|
-
|
|
27
|
-
const ALICE_SURI = `${DEV_PHRASE}//Alice`;
|
|
28
|
-
// Well-known dev key: the public half of `//Alice` derived from the standard
|
|
29
|
-
// substrate dev phrase, published in substrate's own test fixtures.
|
|
30
|
-
const ALICE_PUBLIC =
|
|
31
|
-
"0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d";
|
|
32
|
-
const SEED_32 = new Uint8Array(32).fill(1);
|
|
33
|
-
|
|
34
|
-
// Key derivation runs scrypt/pbkdf2 with production parameters and takes
|
|
35
|
-
// seconds per call, so these suites need more than the 10s default.
|
|
36
|
-
const SLOW = { timeout: 60000 };
|
|
37
|
-
|
|
38
|
-
describe("Keyring construction", SLOW, () => {
|
|
39
|
-
it("defaults to sr25519", () => {
|
|
40
|
-
expect(new Keyring().type).toBe("sr25519");
|
|
41
|
-
expect(new Keyring({}).type).toBe("sr25519");
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it("accepts an explicit sr25519 type", () => {
|
|
45
|
-
expect(new Keyring({ type: "sr25519" }).type).toBe("sr25519");
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it("rejects every other curve, naming what it found", () => {
|
|
49
|
-
// This fork only implements sr25519; silently accepting ed25519 would
|
|
50
|
-
// produce pairs that throw deep inside signing instead.
|
|
51
|
-
for (const type of ["ed25519", "ecdsa", "ethereum"] as KeypairType[]) {
|
|
52
|
-
expect(() => new Keyring({ type })).toThrow(
|
|
53
|
-
`Expected a keyring type of either 'sr25519', found '${type}`,
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it("starts with no pairs", () => {
|
|
59
|
-
const keyring = new Keyring();
|
|
60
|
-
expect(keyring.pairs).toEqual([]);
|
|
61
|
-
expect(keyring.getPairs()).toEqual([]);
|
|
62
|
-
expect(keyring.publicKeys).toEqual([]);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
describe("Keyring address encoding", SLOW, () => {
|
|
67
|
-
it("uses the configured ss58 format", () => {
|
|
68
|
-
const publicKey = new Uint8Array(32).fill(2);
|
|
69
|
-
expect(new Keyring({ ss58Format: 42 }).encodeAddress(publicKey)).toBe(
|
|
70
|
-
encodeAddress(publicKey, 42),
|
|
71
|
-
);
|
|
72
|
-
expect(new Keyring({ ss58Format: 2 }).encodeAddress(publicKey)).toBe(
|
|
73
|
-
encodeAddress(publicKey, 2),
|
|
74
|
-
);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it("lets an explicit format override the configured one", () => {
|
|
78
|
-
const publicKey = new Uint8Array(32).fill(3);
|
|
79
|
-
expect(new Keyring({ ss58Format: 42 }).encodeAddress(publicKey, 2)).toBe(
|
|
80
|
-
encodeAddress(publicKey, 2),
|
|
81
|
-
);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it("applies a format set after construction to new pairs", () => {
|
|
85
|
-
const keyring = new Keyring({ ss58Format: 42 });
|
|
86
|
-
const before = keyring.addFromUri(ALICE_SURI).address;
|
|
87
|
-
keyring.setSS58Format(2);
|
|
88
|
-
const after = keyring.addFromUri(ALICE_SURI).address;
|
|
89
|
-
|
|
90
|
-
expect(after).not.toBe(before);
|
|
91
|
-
expect(decodeAddress(after).toString()).toBe(
|
|
92
|
-
decodeAddress(before).toString(),
|
|
93
|
-
);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it("exposes decodeAddress as the inverse of its own encoding", () => {
|
|
97
|
-
const keyring = new Keyring({ ss58Format: 42 });
|
|
98
|
-
const publicKey = new Uint8Array(32).fill(4);
|
|
99
|
-
expect(keyring.decodeAddress(keyring.encodeAddress(publicKey))).toEqual(
|
|
100
|
-
publicKey,
|
|
101
|
-
);
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
describe("Keyring.createFromUri", SLOW, () => {
|
|
106
|
-
it("derives the well-known Alice key from the dev phrase", () => {
|
|
107
|
-
const pair = new Keyring().createFromUri(ALICE_SURI);
|
|
108
|
-
expect(u8aToHex(pair.publicKey)).toBe(ALICE_PUBLIC);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it("expands a bare hard-derivation path against the dev phrase", () => {
|
|
112
|
-
// `//Alice` on its own is the shorthand every dev script uses.
|
|
113
|
-
expect(u8aToHex(new Keyring().createFromUri("//Alice").publicKey)).toBe(
|
|
114
|
-
ALICE_PUBLIC,
|
|
115
|
-
);
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it("does not create the pair in the keyring", () => {
|
|
119
|
-
const keyring = new Keyring();
|
|
120
|
-
keyring.createFromUri(ALICE_SURI);
|
|
121
|
-
expect(keyring.getPairs()).toEqual([]);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it("treats a 256-bit hex phrase as a raw seed", () => {
|
|
125
|
-
const seedHex = u8aToHex(SEED_32);
|
|
126
|
-
expect(u8aToHex(new Keyring().createFromUri(seedHex).publicKey)).toBe(
|
|
127
|
-
u8aToHex(sr25519FromSeed(SEED_32).publicKey),
|
|
128
|
-
);
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it("pads a short non-mnemonic phrase out to 32 bytes", () => {
|
|
132
|
-
const pair = new Keyring().createFromUri("hello");
|
|
133
|
-
const padded = new Uint8Array(32).fill(32);
|
|
134
|
-
padded.set(new TextEncoder().encode("hello"));
|
|
135
|
-
expect(u8aToHex(pair.publicKey)).toBe(
|
|
136
|
-
u8aToHex(sr25519FromSeed(padded).publicKey),
|
|
137
|
-
);
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it("rejects a phrase that is neither a mnemonic nor short enough to pad", () => {
|
|
141
|
-
// Silently truncating would give a key the caller never intended and
|
|
142
|
-
// could not recover funds from.
|
|
143
|
-
expect(() => new Keyring().createFromUri("x".repeat(33))).toThrow(
|
|
144
|
-
"specified phrase is not a valid mnemonic and is invalid as a raw seed at > 32 bytes",
|
|
145
|
-
);
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it("accepts a 32 character phrase at the padding boundary", () => {
|
|
149
|
-
expect(() => new Keyring().createFromUri("x".repeat(32))).not.toThrow();
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
it("derives a 12 word mnemonic through the mini secret rather than padding", () => {
|
|
153
|
-
expect(u8aToHex(new Keyring().createFromUri(DEV_PHRASE).publicKey)).toBe(
|
|
154
|
-
u8aToHex(sr25519FromSeed(mnemonicToMiniSecret(DEV_PHRASE)).publicKey),
|
|
155
|
-
);
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it("treats a word count outside the mnemonic set as a raw phrase", () => {
|
|
159
|
-
// 13 words is not a valid BIP39 length, and the string is well over 32
|
|
160
|
-
// bytes, so it must be refused rather than quietly padded or hashed.
|
|
161
|
-
expect(() => new Keyring().createFromUri(`${DEV_PHRASE} extra`)).toThrow(
|
|
162
|
-
"is not a valid mnemonic",
|
|
163
|
-
);
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
it("gives a different key for a different password", () => {
|
|
167
|
-
const plain = new Keyring().createFromUri(DEV_PHRASE);
|
|
168
|
-
const withPassword = new Keyring().createFromUri(`${DEV_PHRASE}///pass`);
|
|
169
|
-
expect(u8aToHex(withPassword.publicKey)).not.toBe(
|
|
170
|
-
u8aToHex(plain.publicKey),
|
|
171
|
-
);
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
it("distinguishes soft and hard derivation of the same name", () => {
|
|
175
|
-
const soft = new Keyring().createFromUri(`${DEV_PHRASE}/Alice`);
|
|
176
|
-
const hard = new Keyring().createFromUri(ALICE_SURI);
|
|
177
|
-
expect(u8aToHex(soft.publicKey)).not.toBe(u8aToHex(hard.publicKey));
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
it("carries meta through to the pair", () => {
|
|
181
|
-
const pair = new Keyring().createFromUri(ALICE_SURI, { name: "alice" });
|
|
182
|
-
expect(pair.meta.name).toBe("alice");
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
it("refuses ethereum derivation for a mnemonic", () => {
|
|
186
|
-
expect(() =>
|
|
187
|
-
new Keyring().createFromUri(DEV_PHRASE, {}, "ethereum"),
|
|
188
|
-
).toThrow("Not implemented - Prosopo Keyring supports sr25519 only");
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
describe("Keyring add* methods", SLOW, () => {
|
|
193
|
-
it("stores a pair created from a uri and returns it on lookup", () => {
|
|
194
|
-
const keyring = new Keyring();
|
|
195
|
-
const pair = keyring.addFromUri(ALICE_SURI);
|
|
196
|
-
|
|
197
|
-
expect(keyring.getPairs()).toEqual([pair]);
|
|
198
|
-
expect(keyring.getPair(pair.address)).toBe(pair);
|
|
199
|
-
expect(keyring.getPair(pair.publicKey)).toBe(pair);
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
it("treats addFromMnemonic as addFromUri", () => {
|
|
203
|
-
const fromMnemonic = new Keyring().addFromMnemonic(DEV_PHRASE);
|
|
204
|
-
const fromUri = new Keyring().addFromUri(DEV_PHRASE);
|
|
205
|
-
expect(fromMnemonic.address).toBe(fromUri.address);
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
it("derives a pair from a raw seed", () => {
|
|
209
|
-
const pair = new Keyring().addFromSeed(SEED_32);
|
|
210
|
-
expect(u8aToHex(pair.publicKey)).toBe(
|
|
211
|
-
u8aToHex(sr25519FromSeed(SEED_32).publicKey),
|
|
212
|
-
);
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
it("refuses a seed for an unimplemented curve", () => {
|
|
216
|
-
expect(() => new Keyring().addFromSeed(SEED_32, {}, "ed25519")).toThrow(
|
|
217
|
-
"Not Implemented",
|
|
218
|
-
);
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
it("adds an address-only pair that has no secret and cannot sign", () => {
|
|
222
|
-
// Watch-only accounts are legitimate, but they must not silently
|
|
223
|
-
// produce a signature made from an empty secret.
|
|
224
|
-
const keyring = new Keyring();
|
|
225
|
-
const address = keyring.addFromUri(ALICE_SURI).address;
|
|
226
|
-
const watchOnly = new Keyring().addFromAddress(address);
|
|
227
|
-
|
|
228
|
-
expect(watchOnly.address).toBe(address);
|
|
229
|
-
expect(watchOnly.isLocked).toBe(true);
|
|
230
|
-
expect(() => watchOnly.sign(new Uint8Array([1]))).toThrow();
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
it("adds a pair from an explicit keypair", () => {
|
|
234
|
-
const keypair = sr25519FromSeed(SEED_32);
|
|
235
|
-
const keyring = new Keyring();
|
|
236
|
-
const pair = keyring.addFromPair(keypair, { name: "explicit" });
|
|
237
|
-
|
|
238
|
-
expect(u8aToHex(pair.publicKey)).toBe(u8aToHex(keypair.publicKey));
|
|
239
|
-
expect(pair.meta.name).toBe("explicit");
|
|
240
|
-
expect(keyring.getPairs()).toEqual([pair]);
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
it("exposes public keys for every stored pair", () => {
|
|
244
|
-
const keyring = new Keyring();
|
|
245
|
-
const first = keyring.addFromUri(ALICE_SURI);
|
|
246
|
-
const second = keyring.addFromUri(`${DEV_PHRASE}//Bob`);
|
|
247
|
-
expect(keyring.publicKeys).toEqual([first.publicKey, second.publicKey]);
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
it("removes a pair", () => {
|
|
251
|
-
const keyring = new Keyring();
|
|
252
|
-
const pair = keyring.addFromUri(ALICE_SURI);
|
|
253
|
-
keyring.removePair(pair.address);
|
|
254
|
-
expect(keyring.pairs).toEqual([]);
|
|
255
|
-
expect(() => keyring.getPair(pair.address)).toThrow(
|
|
256
|
-
"Unable to retrieve keypair",
|
|
257
|
-
);
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
it("keeps addPair and the pairs getter in agreement", () => {
|
|
261
|
-
const keyring = new Keyring();
|
|
262
|
-
const pair = keyring.createFromUri(ALICE_SURI);
|
|
263
|
-
expect(keyring.addPair(pair)).toBe(pair);
|
|
264
|
-
expect(keyring.pairs).toEqual(keyring.getPairs());
|
|
265
|
-
});
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
describe("Keyring.createFromJson", SLOW, () => {
|
|
269
|
-
const json = (
|
|
270
|
-
overrides: Partial<KeyringPair$Json> = {},
|
|
271
|
-
): KeyringPair$Json => ({
|
|
272
|
-
address: encodeAddress(new Uint8Array(32).fill(5), 42),
|
|
273
|
-
encoded: "0x00",
|
|
274
|
-
encoding: {
|
|
275
|
-
content: ["pkcs8", "sr25519"],
|
|
276
|
-
type: ["none"],
|
|
277
|
-
version: "3",
|
|
278
|
-
},
|
|
279
|
-
meta: { name: "from-json" },
|
|
280
|
-
...overrides,
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
it("round trips address, meta and type", () => {
|
|
284
|
-
const pair = new Keyring().createFromJson(json());
|
|
285
|
-
expect(pair.meta.name).toBe("from-json");
|
|
286
|
-
expect(pair.type).toBe("sr25519");
|
|
287
|
-
expect(u8aToHex(pair.publicKey)).toBe(u8aToHex(new Uint8Array(32).fill(5)));
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
it("accepts a hex address as the public key directly", () => {
|
|
291
|
-
const pair = new Keyring().createFromJson(json({ address: ALICE_PUBLIC }));
|
|
292
|
-
expect(u8aToHex(pair.publicKey)).toBe(ALICE_PUBLIC);
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
it("rejects a v3 file that is not pkcs8", () => {
|
|
296
|
-
expect(() =>
|
|
297
|
-
new Keyring().createFromJson(
|
|
298
|
-
json({
|
|
299
|
-
encoding: {
|
|
300
|
-
content: ["none", "sr25519"],
|
|
301
|
-
type: ["none"],
|
|
302
|
-
version: "3",
|
|
303
|
-
},
|
|
304
|
-
}),
|
|
305
|
-
),
|
|
306
|
-
).toThrow("Unable to decode non-pkcs8 type");
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
it("falls back to the keyring type for a v0 file", () => {
|
|
310
|
-
// v0 predates the content array, so the crypto type is implied.
|
|
311
|
-
const pair = new Keyring().createFromJson(
|
|
312
|
-
json({
|
|
313
|
-
encoding: {
|
|
314
|
-
content: ["pkcs8", "ed25519"],
|
|
315
|
-
type: ["none"],
|
|
316
|
-
version: "0",
|
|
317
|
-
},
|
|
318
|
-
}),
|
|
319
|
-
);
|
|
320
|
-
expect(pair.type).toBe("sr25519");
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
it("rejects a crypto type this keyring cannot handle", () => {
|
|
324
|
-
expect(() =>
|
|
325
|
-
new Keyring().createFromJson(
|
|
326
|
-
json({
|
|
327
|
-
encoding: {
|
|
328
|
-
content: ["pkcs8", "ed25519"],
|
|
329
|
-
type: ["none"],
|
|
330
|
-
version: "3",
|
|
331
|
-
},
|
|
332
|
-
}),
|
|
333
|
-
),
|
|
334
|
-
).toThrow("Unknown crypto type ed25519");
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
it("normalises a single encoding type into a list", () => {
|
|
338
|
-
expect(() =>
|
|
339
|
-
new Keyring().createFromJson(
|
|
340
|
-
json({
|
|
341
|
-
encoding: {
|
|
342
|
-
content: ["pkcs8", "sr25519"],
|
|
343
|
-
type: "none",
|
|
344
|
-
version: "3",
|
|
345
|
-
},
|
|
346
|
-
}),
|
|
347
|
-
),
|
|
348
|
-
).not.toThrow();
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
it("stores the pair when added rather than created", () => {
|
|
352
|
-
const keyring = new Keyring();
|
|
353
|
-
const pair = keyring.addFromJson(json());
|
|
354
|
-
expect(keyring.getPairs()).toEqual([pair]);
|
|
355
|
-
});
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
describe("Keyring.toJson", SLOW, () => {
|
|
359
|
-
it("delegates to the stored pair", () => {
|
|
360
|
-
const keyring = new Keyring();
|
|
361
|
-
const pair: KeyringPair = keyring.addFromUri(ALICE_SURI);
|
|
362
|
-
const asJson = keyring.toJson(pair.address);
|
|
363
|
-
|
|
364
|
-
expect(asJson.address).toBe(pair.address);
|
|
365
|
-
expect(asJson.encoding.content).toContain("sr25519");
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
it("throws for an address it does not hold", () => {
|
|
369
|
-
expect(() =>
|
|
370
|
-
new Keyring().toJson(encodeAddress(new Uint8Array(32).fill(6), 42)),
|
|
371
|
-
).toThrow("Unable to retrieve keypair");
|
|
372
|
-
});
|
|
373
|
-
});
|