@metamask/eth-hd-keyring 4.0.2 → 5.0.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/.eslintrc.js +3 -0
- package/.yarn/plugins/@yarnpkg/plugin-allow-scripts.cjs +9 -0
- package/CHANGELOG.md +27 -1
- package/README.md +2 -3
- package/index.js +212 -37
- package/jest.config.js +2 -2
- package/package.json +16 -14
- package/test/index.js +480 -100
- package/.github/CODEOWNERS +0 -4
- package/.github/workflows/build-test.yml +0 -48
- package/.github/workflows/create-release-pr.yml +0 -50
- package/.github/workflows/publish-release.yml +0 -29
- package/.github/workflows/require-additional-reviewer.yml +0 -29
package/test/index.js
CHANGED
|
@@ -5,8 +5,24 @@ const {
|
|
|
5
5
|
recoverTypedSignature,
|
|
6
6
|
signTypedData,
|
|
7
7
|
SignTypedDataVersion,
|
|
8
|
+
encrypt,
|
|
8
9
|
} = require('@metamask/eth-sig-util');
|
|
9
|
-
const
|
|
10
|
+
const { wordlist } = require('@metamask/scure-bip39/dist/wordlists/english');
|
|
11
|
+
const oldMMForkBIP39 = require('@metamask/bip39');
|
|
12
|
+
const {
|
|
13
|
+
isValidAddress,
|
|
14
|
+
bufferToHex,
|
|
15
|
+
toBuffer,
|
|
16
|
+
ecrecover,
|
|
17
|
+
pubToAddress,
|
|
18
|
+
} = require('@ethereumjs/util');
|
|
19
|
+
const {
|
|
20
|
+
TransactionFactory,
|
|
21
|
+
Transaction: EthereumTx,
|
|
22
|
+
} = require('@ethereumjs/tx');
|
|
23
|
+
|
|
24
|
+
const OldHdKeyring = require('@metamask/eth-hd-keyring');
|
|
25
|
+
const { keccak256 } = require('ethereum-cryptography/keccak');
|
|
10
26
|
const HdKeyring = require('..');
|
|
11
27
|
|
|
12
28
|
// Sample account:
|
|
@@ -18,12 +34,40 @@ const sampleMnemonic =
|
|
|
18
34
|
const firstAcct = '0x1c96099350f13d558464ec79b9be4445aa0ef579';
|
|
19
35
|
const secondAcct = '0x1b00aed43a693f3a957f9feb5cc08afa031e37a0';
|
|
20
36
|
|
|
37
|
+
const notKeyringAddress = '0xbD20F6F5F1616947a39E11926E78ec94817B3931';
|
|
38
|
+
|
|
21
39
|
describe('hd-keyring', () => {
|
|
22
40
|
let keyring;
|
|
23
41
|
beforeEach(() => {
|
|
24
42
|
keyring = new HdKeyring();
|
|
25
43
|
});
|
|
26
44
|
|
|
45
|
+
describe('compare old bip39 implementation with new', () => {
|
|
46
|
+
it('should derive the same accounts from the same mnemonics', async () => {
|
|
47
|
+
const mnemonics = [];
|
|
48
|
+
for (let i = 0; i < 99; i++) {
|
|
49
|
+
mnemonics.push(oldMMForkBIP39.generateMnemonic());
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
await Promise.all(
|
|
53
|
+
mnemonics.map(async (mnemonic) => {
|
|
54
|
+
const newHDKeyring = new HdKeyring({ mnemonic, numberOfAccounts: 3 });
|
|
55
|
+
const oldHDKeyring = new OldHdKeyring({
|
|
56
|
+
mnemonic,
|
|
57
|
+
numberOfAccounts: 3,
|
|
58
|
+
});
|
|
59
|
+
const newAccounts = await newHDKeyring.getAccounts();
|
|
60
|
+
const oldAccounts = await oldHDKeyring.getAccounts();
|
|
61
|
+
await expect(newAccounts[0]).toStrictEqual(oldAccounts[0]);
|
|
62
|
+
|
|
63
|
+
await expect(newAccounts[1]).toStrictEqual(oldAccounts[1]);
|
|
64
|
+
|
|
65
|
+
await expect(newAccounts[2]).toStrictEqual(oldAccounts[2]);
|
|
66
|
+
}),
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
27
71
|
describe('constructor', () => {
|
|
28
72
|
it('constructs with a typeof string mnemonic', async () => {
|
|
29
73
|
keyring = new HdKeyring({
|
|
@@ -36,9 +80,9 @@ describe('hd-keyring', () => {
|
|
|
36
80
|
expect(accounts[1]).toStrictEqual(secondAcct);
|
|
37
81
|
});
|
|
38
82
|
|
|
39
|
-
it('constructs with a typeof
|
|
83
|
+
it('constructs with a typeof buffer mnemonic', async () => {
|
|
40
84
|
keyring = new HdKeyring({
|
|
41
|
-
mnemonic:
|
|
85
|
+
mnemonic: Buffer.from(sampleMnemonic, 'utf8'),
|
|
42
86
|
numberOfAccounts: 2,
|
|
43
87
|
});
|
|
44
88
|
|
|
@@ -47,9 +91,15 @@ describe('hd-keyring', () => {
|
|
|
47
91
|
expect(accounts[1]).toStrictEqual(secondAcct);
|
|
48
92
|
});
|
|
49
93
|
|
|
50
|
-
it('constructs with a typeof
|
|
94
|
+
it('constructs with a typeof Uint8Array mnemonic', async () => {
|
|
95
|
+
const indices = sampleMnemonic
|
|
96
|
+
.split(' ')
|
|
97
|
+
.map((word) => wordlist.indexOf(word));
|
|
98
|
+
const uInt8ArrayOfMnemonic = new Uint8Array(
|
|
99
|
+
new Uint16Array(indices).buffer,
|
|
100
|
+
);
|
|
51
101
|
keyring = new HdKeyring({
|
|
52
|
-
mnemonic:
|
|
102
|
+
mnemonic: uInt8ArrayOfMnemonic,
|
|
53
103
|
numberOfAccounts: 2,
|
|
54
104
|
});
|
|
55
105
|
|
|
@@ -132,18 +182,34 @@ describe('hd-keyring', () => {
|
|
|
132
182
|
});
|
|
133
183
|
|
|
134
184
|
describe('#serialize mnemonic.', () => {
|
|
135
|
-
it('serializes mnemonic stored as a buffer
|
|
136
|
-
keyring.
|
|
185
|
+
it('serializes mnemonic stored as a buffer to a Uint8Array', async () => {
|
|
186
|
+
keyring.mnemonic = oldMMForkBIP39.generateMnemonic();
|
|
187
|
+
const mnemonicAsUint8Array = keyring._stringToUint8Array(
|
|
188
|
+
keyring.mnemonic.toString(),
|
|
189
|
+
);
|
|
137
190
|
const output = await keyring.serialize();
|
|
138
191
|
expect(output.numberOfAccounts).toBe(0);
|
|
139
|
-
expect(
|
|
192
|
+
expect(output.mnemonic).toStrictEqual(mnemonicAsUint8Array);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('serializes keyring data with mnemonic stored as a Uint8Array', async () => {
|
|
196
|
+
keyring.generateRandomMnemonic();
|
|
197
|
+
const { mnemonic } = keyring;
|
|
198
|
+
const hdpath = keyring.hdPath;
|
|
199
|
+
keyring.addAccounts(1);
|
|
200
|
+
const output = await keyring.serialize();
|
|
201
|
+
expect(output.numberOfAccounts).toBe(1);
|
|
202
|
+
expect(output.hdPath).toStrictEqual(hdpath);
|
|
203
|
+
expect(output.mnemonic).toStrictEqual(mnemonic);
|
|
140
204
|
});
|
|
141
205
|
|
|
142
|
-
it('serializes mnemonic stored as a string
|
|
206
|
+
it('serializes mnemonic stored as a string', async () => {
|
|
143
207
|
keyring.mnemonic = sampleMnemonic;
|
|
144
208
|
const output = await keyring.serialize();
|
|
145
209
|
expect(output.numberOfAccounts).toBe(0);
|
|
146
|
-
expect(
|
|
210
|
+
expect(output.mnemonic).toStrictEqual(
|
|
211
|
+
keyring._stringToUint8Array(sampleMnemonic),
|
|
212
|
+
);
|
|
147
213
|
});
|
|
148
214
|
});
|
|
149
215
|
|
|
@@ -153,14 +219,16 @@ describe('hd-keyring', () => {
|
|
|
153
219
|
mnemonic: sampleMnemonic,
|
|
154
220
|
numberOfAccounts: 1,
|
|
155
221
|
});
|
|
156
|
-
|
|
222
|
+
const accountsFirstCheck = await keyring.getAccounts();
|
|
223
|
+
|
|
224
|
+
expect(accountsFirstCheck).toHaveLength(1);
|
|
157
225
|
await keyring.addAccounts(1);
|
|
158
|
-
const
|
|
159
|
-
expect(
|
|
160
|
-
expect(
|
|
161
|
-
expect(
|
|
226
|
+
const accountsSecondCheck = await keyring.getAccounts();
|
|
227
|
+
expect(accountsSecondCheck[0]).toStrictEqual(firstAcct);
|
|
228
|
+
expect(accountsSecondCheck[1]).toStrictEqual(secondAcct);
|
|
229
|
+
expect(accountsSecondCheck).toHaveLength(2);
|
|
162
230
|
const serialized = await keyring.serialize();
|
|
163
|
-
expect(
|
|
231
|
+
expect(keyring._uint8ArrayToString(serialized.mnemonic)).toStrictEqual(
|
|
164
232
|
sampleMnemonic,
|
|
165
233
|
);
|
|
166
234
|
});
|
|
@@ -171,7 +239,8 @@ describe('hd-keyring', () => {
|
|
|
171
239
|
it('creates a single wallet', async () => {
|
|
172
240
|
keyring.generateRandomMnemonic();
|
|
173
241
|
await keyring.addAccounts();
|
|
174
|
-
|
|
242
|
+
const accounts = await keyring.getAccounts();
|
|
243
|
+
expect(accounts).toHaveLength(1);
|
|
175
244
|
});
|
|
176
245
|
|
|
177
246
|
it('throws an error when no SRP has been generated yet', async () => {
|
|
@@ -185,28 +254,9 @@ describe('hd-keyring', () => {
|
|
|
185
254
|
it('creates that number of wallets', async () => {
|
|
186
255
|
keyring.generateRandomMnemonic();
|
|
187
256
|
await keyring.addAccounts(3);
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
});
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
describe('#getAccounts', () => {
|
|
194
|
-
it('calls getAddress on each wallet', async () => {
|
|
195
|
-
// Push a mock wallet
|
|
196
|
-
const desiredOutput = 'foo';
|
|
197
|
-
keyring.wallets.push({
|
|
198
|
-
getAddress() {
|
|
199
|
-
return {
|
|
200
|
-
toString() {
|
|
201
|
-
return desiredOutput;
|
|
202
|
-
},
|
|
203
|
-
};
|
|
204
|
-
},
|
|
257
|
+
const accounts = await keyring.getAccounts();
|
|
258
|
+
expect(accounts).toHaveLength(3);
|
|
205
259
|
});
|
|
206
|
-
|
|
207
|
-
const output = await keyring.getAccounts();
|
|
208
|
-
expect(output[0]).toBe(`0x${desiredOutput}`);
|
|
209
|
-
expect(output).toHaveLength(1);
|
|
210
260
|
});
|
|
211
261
|
});
|
|
212
262
|
|
|
@@ -270,7 +320,9 @@ describe('hd-keyring', () => {
|
|
|
270
320
|
await keyring.addAccounts(1);
|
|
271
321
|
const addresses = await keyring.getAccounts();
|
|
272
322
|
const address = addresses[0];
|
|
273
|
-
const signature = await keyring.
|
|
323
|
+
const signature = await keyring.signTypedData(address, typedData, {
|
|
324
|
+
version: SignTypedDataVersion.V1,
|
|
325
|
+
});
|
|
274
326
|
const restored = recoverTypedSignature({
|
|
275
327
|
data: typedData,
|
|
276
328
|
signature,
|
|
@@ -297,7 +349,9 @@ describe('hd-keyring', () => {
|
|
|
297
349
|
});
|
|
298
350
|
const addresses = await keyring.getAccounts();
|
|
299
351
|
const address = addresses[0];
|
|
300
|
-
const signature = await keyring.
|
|
352
|
+
const signature = await keyring.signTypedData(address, typedData, {
|
|
353
|
+
version: SignTypedDataVersion.V3,
|
|
354
|
+
});
|
|
301
355
|
const restored = recoverTypedSignature({
|
|
302
356
|
data: typedData,
|
|
303
357
|
signature,
|
|
@@ -351,7 +405,9 @@ describe('hd-keyring', () => {
|
|
|
351
405
|
await keyring.addAccounts(1);
|
|
352
406
|
const addresses = await keyring.getAccounts();
|
|
353
407
|
const address = addresses[0];
|
|
354
|
-
const signature = await keyring.
|
|
408
|
+
const signature = await keyring.signTypedData(address, typedData, {
|
|
409
|
+
version: SignTypedDataVersion.V3,
|
|
410
|
+
});
|
|
355
411
|
const restored = recoverTypedSignature({
|
|
356
412
|
data: typedData,
|
|
357
413
|
signature,
|
|
@@ -423,129 +479,453 @@ describe('hd-keyring', () => {
|
|
|
423
479
|
})
|
|
424
480
|
*/
|
|
425
481
|
|
|
426
|
-
describe('
|
|
427
|
-
it('should
|
|
482
|
+
describe('signing methods withAppKeyOrigin option', () => {
|
|
483
|
+
it('should signPersonalMessage with the expected key when passed a withAppKeyOrigin', async () => {
|
|
428
484
|
const address = firstAcct;
|
|
485
|
+
const message = '0x68656c6c6f20776f726c64';
|
|
429
486
|
|
|
430
|
-
|
|
487
|
+
const privateKey = Buffer.from(
|
|
488
|
+
'8e82d2d74c50e5c8460f771d38a560ebe1151a9134c65a7e92b28ad0cfae7151',
|
|
489
|
+
'hex',
|
|
490
|
+
);
|
|
491
|
+
const expectedSig = personalSign({ privateKey, data: message });
|
|
492
|
+
|
|
493
|
+
await keyring.deserialize({
|
|
431
494
|
mnemonic: sampleMnemonic,
|
|
432
495
|
numberOfAccounts: 1,
|
|
433
496
|
});
|
|
434
|
-
const
|
|
435
|
-
|
|
436
|
-
|
|
497
|
+
const sig = await keyring.signPersonalMessage(address, message, {
|
|
498
|
+
withAppKeyOrigin: 'someapp.origin.io',
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
expect(sig).toStrictEqual(expectedSig);
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
it('should signTypedData with the expected key when passed a withAppKeyOrigin', async () => {
|
|
505
|
+
const address = firstAcct;
|
|
506
|
+
const typedData = {
|
|
507
|
+
types: {
|
|
508
|
+
EIP712Domain: [],
|
|
509
|
+
},
|
|
510
|
+
domain: {},
|
|
511
|
+
primaryType: 'EIP712Domain',
|
|
512
|
+
message: {},
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
const privateKey = Buffer.from(
|
|
516
|
+
'8e82d2d74c50e5c8460f771d38a560ebe1151a9134c65a7e92b28ad0cfae7151',
|
|
517
|
+
'hex',
|
|
437
518
|
);
|
|
519
|
+
const expectedSig = signTypedData({
|
|
520
|
+
privateKey,
|
|
521
|
+
data: typedData,
|
|
522
|
+
version: SignTypedDataVersion.V3,
|
|
523
|
+
});
|
|
438
524
|
|
|
439
|
-
|
|
440
|
-
|
|
525
|
+
await keyring.deserialize({
|
|
526
|
+
mnemonic: sampleMnemonic,
|
|
527
|
+
numberOfAccounts: 1,
|
|
528
|
+
});
|
|
441
529
|
|
|
442
|
-
const
|
|
443
|
-
|
|
530
|
+
const sig = await keyring.signTypedData(address, typedData, {
|
|
531
|
+
withAppKeyOrigin: 'someapp.origin.io',
|
|
532
|
+
version: SignTypedDataVersion.V3,
|
|
533
|
+
});
|
|
534
|
+
expect(sig).toStrictEqual(expectedSig);
|
|
444
535
|
});
|
|
536
|
+
});
|
|
445
537
|
|
|
446
|
-
|
|
447
|
-
|
|
538
|
+
// /
|
|
539
|
+
/* TESTS FOR BASE-KEYRING METHODS */
|
|
540
|
+
// /
|
|
541
|
+
|
|
542
|
+
describe('#signMessage', function () {
|
|
543
|
+
const message =
|
|
544
|
+
'0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0';
|
|
545
|
+
const expectedResult =
|
|
546
|
+
'0xb21867b2221db0172e970b7370825b71c57823ff8714168ce9748f32f450e2c43d0fe396eb5b5f59284b7fd108c8cf61a6180a6756bdd3d4b7b9ccc4ac6d51611b';
|
|
547
|
+
|
|
548
|
+
it('passes the dennis test', async function () {
|
|
549
|
+
await keyring.deserialize({
|
|
448
550
|
mnemonic: sampleMnemonic,
|
|
449
551
|
numberOfAccounts: 1,
|
|
450
552
|
});
|
|
553
|
+
const result = await keyring.signMessage(firstAcct, message);
|
|
554
|
+
expect(result).toBe(expectedResult);
|
|
555
|
+
});
|
|
451
556
|
|
|
452
|
-
|
|
557
|
+
it('reliably can decode messages it signs', async function () {
|
|
558
|
+
await keyring.deserialize({
|
|
559
|
+
mnemonic: sampleMnemonic,
|
|
560
|
+
numberOfAccounts: 1,
|
|
561
|
+
});
|
|
562
|
+
const localMessage = 'hello there!';
|
|
563
|
+
const msgHashHex = bufferToHex(keccak256(Buffer.from(localMessage)));
|
|
564
|
+
await keyring.addAccounts(9);
|
|
565
|
+
const addresses = await keyring.getAccounts();
|
|
566
|
+
const signatures = await Promise.all(
|
|
567
|
+
addresses.map(async (accountAddress) => {
|
|
568
|
+
return await keyring.signMessage(accountAddress, msgHashHex);
|
|
569
|
+
}),
|
|
570
|
+
);
|
|
571
|
+
signatures.forEach((sgn, index) => {
|
|
572
|
+
const accountAddress = addresses[index];
|
|
453
573
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
574
|
+
const r = toBuffer(sgn.slice(0, 66));
|
|
575
|
+
const s = toBuffer(`0x${sgn.slice(66, 130)}`);
|
|
576
|
+
const v = BigInt(`0x${sgn.slice(130, 132)}`);
|
|
577
|
+
const m = toBuffer(msgHashHex);
|
|
578
|
+
const pub = ecrecover(m, v, r, s);
|
|
579
|
+
const adr = `0x${pubToAddress(pub).toString('hex')}`;
|
|
580
|
+
|
|
581
|
+
expect(adr).toBe(accountAddress);
|
|
582
|
+
});
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
it('throw error for invalid message', async function () {
|
|
586
|
+
await keyring.deserialize({
|
|
587
|
+
mnemonic: sampleMnemonic,
|
|
588
|
+
numberOfAccounts: 1,
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
await expect(keyring.signMessage(firstAcct, '')).rejects.toThrow(
|
|
592
|
+
'Cannot convert 0x to a BigInt',
|
|
457
593
|
);
|
|
594
|
+
});
|
|
458
595
|
|
|
459
|
-
|
|
596
|
+
it('throw error if empty address is passed', async function () {
|
|
597
|
+
await keyring.deserialize({
|
|
598
|
+
mnemonic: sampleMnemonic,
|
|
599
|
+
numberOfAccounts: 1,
|
|
600
|
+
});
|
|
460
601
|
|
|
461
|
-
|
|
462
|
-
address,
|
|
463
|
-
'anotherapp.origin.io',
|
|
602
|
+
await expect(keyring.signMessage('', message)).rejects.toThrow(
|
|
603
|
+
'Must specify address.',
|
|
464
604
|
);
|
|
605
|
+
});
|
|
465
606
|
|
|
466
|
-
|
|
607
|
+
it('throw error if address not associated with the current keyring is passed', async function () {
|
|
608
|
+
await keyring.deserialize({
|
|
609
|
+
mnemonic: sampleMnemonic,
|
|
610
|
+
numberOfAccounts: 1,
|
|
611
|
+
});
|
|
467
612
|
|
|
468
|
-
expect(
|
|
613
|
+
await expect(
|
|
614
|
+
keyring.signMessage(notKeyringAddress, message),
|
|
615
|
+
).rejects.toThrow('HD Keyring - Unable to find matching address.');
|
|
469
616
|
});
|
|
617
|
+
});
|
|
470
618
|
|
|
471
|
-
|
|
619
|
+
describe('#removeAccount', function () {
|
|
620
|
+
beforeEach(() => {
|
|
472
621
|
keyring = new HdKeyring({
|
|
473
622
|
mnemonic: sampleMnemonic,
|
|
474
623
|
numberOfAccounts: 1,
|
|
475
624
|
});
|
|
625
|
+
});
|
|
476
626
|
|
|
477
|
-
|
|
627
|
+
describe('if the account exists', function () {
|
|
628
|
+
it('should remove that account', async function () {
|
|
629
|
+
const addresses = await keyring.getAccounts();
|
|
630
|
+
expect(addresses).toHaveLength(1);
|
|
631
|
+
keyring.removeAccount(addresses[0]);
|
|
632
|
+
const addressesAfterRemoval = await keyring.getAccounts();
|
|
633
|
+
expect(addressesAfterRemoval).toHaveLength(0);
|
|
634
|
+
});
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
describe('if the account does not exist', function () {
|
|
638
|
+
it('should throw an error', function () {
|
|
639
|
+
const unexistingAccount = '0x0000000000000000000000000000000000000000';
|
|
640
|
+
expect(() => keyring.removeAccount(unexistingAccount)).toThrow(
|
|
641
|
+
`Address ${unexistingAccount} not found in this keyring`,
|
|
642
|
+
);
|
|
643
|
+
});
|
|
644
|
+
});
|
|
645
|
+
});
|
|
478
646
|
|
|
647
|
+
describe('getAppKeyAddress', function () {
|
|
648
|
+
beforeEach(() => {
|
|
649
|
+
keyring = new HdKeyring({
|
|
650
|
+
mnemonic: sampleMnemonic,
|
|
651
|
+
numberOfAccounts: 1,
|
|
652
|
+
});
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
it('should return a public address custom to the provided app key origin', async function () {
|
|
656
|
+
const appKeyAddress = await keyring.getAppKeyAddress(
|
|
657
|
+
firstAcct,
|
|
658
|
+
'someapp.origin.io',
|
|
659
|
+
);
|
|
660
|
+
|
|
661
|
+
expect(firstAcct).not.toBe(appKeyAddress);
|
|
662
|
+
expect(isValidAddress(appKeyAddress)).toBe(true);
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
it('should return different addresses when provided different app key origins', async function () {
|
|
479
666
|
const appKeyAddress1 = await keyring.getAppKeyAddress(
|
|
480
|
-
|
|
667
|
+
firstAcct,
|
|
481
668
|
'someapp.origin.io',
|
|
482
669
|
);
|
|
483
670
|
|
|
484
|
-
expect(
|
|
671
|
+
expect(isValidAddress(appKeyAddress1)).toBe(true);
|
|
485
672
|
|
|
486
673
|
const appKeyAddress2 = await keyring.getAppKeyAddress(
|
|
487
|
-
|
|
674
|
+
firstAcct,
|
|
675
|
+
'anotherapp.origin.io',
|
|
676
|
+
);
|
|
677
|
+
|
|
678
|
+
expect(isValidAddress(appKeyAddress2)).toBe(true);
|
|
679
|
+
expect(appKeyAddress1).not.toBe(appKeyAddress2);
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
it('should return the same address when called multiple times with the same params', async function () {
|
|
683
|
+
const appKeyAddress1 = await keyring.getAppKeyAddress(
|
|
684
|
+
firstAcct,
|
|
488
685
|
'someapp.origin.io',
|
|
489
686
|
);
|
|
490
687
|
|
|
491
|
-
expect(
|
|
688
|
+
expect(isValidAddress(appKeyAddress1)).toBe(true);
|
|
492
689
|
|
|
493
|
-
|
|
690
|
+
const appKeyAddress2 = await keyring.getAppKeyAddress(
|
|
691
|
+
firstAcct,
|
|
692
|
+
'someapp.origin.io',
|
|
693
|
+
);
|
|
694
|
+
|
|
695
|
+
expect(isValidAddress(appKeyAddress2)).toBe(true);
|
|
696
|
+
expect(appKeyAddress1).toBe(appKeyAddress2);
|
|
494
697
|
});
|
|
495
|
-
});
|
|
496
698
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
699
|
+
it('should throw error if the provided origin is not a string', async function () {
|
|
700
|
+
await expect(keyring.getAppKeyAddress(firstAcct, [])).rejects.toThrow(
|
|
701
|
+
`'origin' must be a non-empty string`,
|
|
702
|
+
);
|
|
703
|
+
});
|
|
501
704
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
'
|
|
705
|
+
it('should throw error if the provided origin is an empty string', async function () {
|
|
706
|
+
await expect(keyring.getAppKeyAddress(firstAcct, '')).rejects.toThrow(
|
|
707
|
+
`'origin' must be a non-empty string`,
|
|
505
708
|
);
|
|
506
|
-
|
|
709
|
+
});
|
|
710
|
+
});
|
|
507
711
|
|
|
508
|
-
|
|
712
|
+
describe('exportAccount', function () {
|
|
713
|
+
beforeEach(() => {
|
|
714
|
+
keyring = new HdKeyring({
|
|
509
715
|
mnemonic: sampleMnemonic,
|
|
510
716
|
numberOfAccounts: 1,
|
|
511
717
|
});
|
|
512
|
-
|
|
513
|
-
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
it('should return a hex-encoded private key', async function () {
|
|
721
|
+
const expectedPrivateKeyResult =
|
|
722
|
+
'0xd3cc16948a02a91b9fcf83735653bf3dfd82c86543fdd1e9a828bd25e8a7b68d';
|
|
723
|
+
const privKeyHexValue = await keyring.exportAccount(firstAcct);
|
|
724
|
+
|
|
725
|
+
expect(expectedPrivateKeyResult).toBe(`0x${privKeyHexValue}`);
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
it('throw error if account is not present', async function () {
|
|
729
|
+
await expect(keyring.exportAccount(notKeyringAddress)).rejects.toThrow(
|
|
730
|
+
'HD Keyring - Unable to find matching address.',
|
|
731
|
+
);
|
|
732
|
+
});
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
describe('#encryptionPublicKey', function () {
|
|
736
|
+
const publicKey = 'LV7lWhd0mUDcvxkMU2o6uKXftu25zq4bMYdmMqppXic=';
|
|
737
|
+
beforeEach(() => {
|
|
738
|
+
keyring = new HdKeyring({
|
|
739
|
+
mnemonic: sampleMnemonic,
|
|
740
|
+
numberOfAccounts: 1,
|
|
514
741
|
});
|
|
742
|
+
});
|
|
515
743
|
|
|
516
|
-
|
|
744
|
+
it('returns the expected value', async function () {
|
|
745
|
+
const encryptionPublicKey = await keyring.getEncryptionPublicKey(
|
|
746
|
+
firstAcct,
|
|
747
|
+
);
|
|
748
|
+
expect(publicKey).toBe(encryptionPublicKey);
|
|
517
749
|
});
|
|
518
750
|
|
|
519
|
-
it('
|
|
520
|
-
|
|
751
|
+
it('throw error if address is blank', async function () {
|
|
752
|
+
await expect(keyring.getEncryptionPublicKey('')).rejects.toThrow(
|
|
753
|
+
'Must specify address.',
|
|
754
|
+
);
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
it('throw error if address is not present in the keyring', async function () {
|
|
758
|
+
await expect(
|
|
759
|
+
keyring.getEncryptionPublicKey(notKeyringAddress),
|
|
760
|
+
).rejects.toThrow('HD Keyring - Unable to find matching address.');
|
|
761
|
+
});
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
describe('#signTypedData V4 signature verification', function () {
|
|
765
|
+
beforeEach(() => {
|
|
766
|
+
keyring = new HdKeyring({
|
|
767
|
+
mnemonic: sampleMnemonic,
|
|
768
|
+
numberOfAccounts: 1,
|
|
769
|
+
});
|
|
770
|
+
});
|
|
771
|
+
|
|
772
|
+
const expectedSignature =
|
|
773
|
+
'0x220917664ef676d592bd709a5bffedaf69c5f6c72f13c6c4547a41d211f0923c3180893b1dec023433f11b664fabda22b74b57d21094f7798fc85b7650f8edbb1b';
|
|
774
|
+
|
|
775
|
+
it('returns the expected value', async function () {
|
|
521
776
|
const typedData = {
|
|
522
777
|
types: {
|
|
523
|
-
EIP712Domain: [
|
|
778
|
+
EIP712Domain: [
|
|
779
|
+
{ name: 'name', type: 'string' },
|
|
780
|
+
{ name: 'version', type: 'string' },
|
|
781
|
+
{ name: 'chainId', type: 'uint256' },
|
|
782
|
+
{ name: 'verifyingContract', type: 'address' },
|
|
783
|
+
],
|
|
784
|
+
Person: [
|
|
785
|
+
{ name: 'name', type: 'string' },
|
|
786
|
+
{ name: 'wallets', type: 'address[]' },
|
|
787
|
+
],
|
|
788
|
+
Mail: [
|
|
789
|
+
{ name: 'from', type: 'Person' },
|
|
790
|
+
{ name: 'to', type: 'Person[]' },
|
|
791
|
+
{ name: 'contents', type: 'string' },
|
|
792
|
+
],
|
|
793
|
+
Group: [
|
|
794
|
+
{ name: 'name', type: 'string' },
|
|
795
|
+
{ name: 'members', type: 'Person[]' },
|
|
796
|
+
],
|
|
797
|
+
},
|
|
798
|
+
domain: {
|
|
799
|
+
name: 'Ether Mail',
|
|
800
|
+
version: '1',
|
|
801
|
+
chainId: 1,
|
|
802
|
+
verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC',
|
|
803
|
+
},
|
|
804
|
+
primaryType: 'Mail',
|
|
805
|
+
message: {
|
|
806
|
+
from: {
|
|
807
|
+
name: 'Cow',
|
|
808
|
+
wallets: [
|
|
809
|
+
'0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826',
|
|
810
|
+
'0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF',
|
|
811
|
+
],
|
|
812
|
+
},
|
|
813
|
+
to: [
|
|
814
|
+
{
|
|
815
|
+
name: 'Bob',
|
|
816
|
+
wallets: [
|
|
817
|
+
'0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB',
|
|
818
|
+
'0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57',
|
|
819
|
+
'0xB0B0b0b0b0b0B000000000000000000000000000',
|
|
820
|
+
],
|
|
821
|
+
},
|
|
822
|
+
],
|
|
823
|
+
contents: 'Hello, Bob!',
|
|
524
824
|
},
|
|
525
|
-
domain: {},
|
|
526
|
-
primaryType: 'EIP712Domain',
|
|
527
|
-
message: {},
|
|
528
825
|
};
|
|
529
826
|
|
|
530
|
-
const
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
827
|
+
const addresses = await keyring.getAccounts();
|
|
828
|
+
const [address] = addresses;
|
|
829
|
+
|
|
830
|
+
const signature = await keyring.signTypedData(address, typedData, {
|
|
831
|
+
version: 'V4',
|
|
832
|
+
});
|
|
833
|
+
expect(signature).toBe(expectedSignature);
|
|
834
|
+
const restored = recoverTypedSignature({
|
|
536
835
|
data: typedData,
|
|
537
|
-
|
|
836
|
+
signature,
|
|
837
|
+
version: SignTypedDataVersion.V4,
|
|
538
838
|
});
|
|
839
|
+
expect(restored).toBe(address);
|
|
840
|
+
});
|
|
841
|
+
});
|
|
539
842
|
|
|
540
|
-
|
|
843
|
+
describe('#decryptMessage', function () {
|
|
844
|
+
const message = 'Hello world!';
|
|
845
|
+
let encryptedMessage;
|
|
846
|
+
|
|
847
|
+
beforeEach(async () => {
|
|
848
|
+
keyring = new HdKeyring({
|
|
541
849
|
mnemonic: sampleMnemonic,
|
|
542
850
|
numberOfAccounts: 1,
|
|
543
851
|
});
|
|
544
852
|
|
|
545
|
-
const
|
|
546
|
-
|
|
853
|
+
const encryptionPublicKey = await keyring.getEncryptionPublicKey(
|
|
854
|
+
firstAcct,
|
|
855
|
+
);
|
|
856
|
+
encryptedMessage = encrypt({
|
|
857
|
+
publicKey: encryptionPublicKey,
|
|
858
|
+
data: message,
|
|
859
|
+
version: 'x25519-xsalsa20-poly1305',
|
|
547
860
|
});
|
|
548
|
-
|
|
861
|
+
});
|
|
862
|
+
|
|
863
|
+
it('returns the expected value', async function () {
|
|
864
|
+
const decryptedMessage = await keyring.decryptMessage(
|
|
865
|
+
firstAcct,
|
|
866
|
+
encryptedMessage,
|
|
867
|
+
);
|
|
868
|
+
expect(message).toBe(decryptedMessage);
|
|
869
|
+
});
|
|
870
|
+
|
|
871
|
+
it('throw error if address passed is not present in the keyring', async function () {
|
|
872
|
+
await expect(
|
|
873
|
+
keyring.decryptMessage(notKeyringAddress, encryptedMessage),
|
|
874
|
+
).rejects.toThrow('HD Keyring - Unable to find matching address.');
|
|
875
|
+
});
|
|
876
|
+
|
|
877
|
+
it('throw error if wrong encrypted data object is passed', async function () {
|
|
878
|
+
await expect(keyring.decryptMessage(firstAcct, {})).rejects.toThrow(
|
|
879
|
+
'Encryption type/version not supported.',
|
|
880
|
+
);
|
|
881
|
+
});
|
|
882
|
+
});
|
|
883
|
+
|
|
884
|
+
describe('#signTransaction', function () {
|
|
885
|
+
beforeEach(() => {
|
|
886
|
+
keyring = new HdKeyring({
|
|
887
|
+
mnemonic: sampleMnemonic,
|
|
888
|
+
numberOfAccounts: 1,
|
|
889
|
+
});
|
|
890
|
+
});
|
|
891
|
+
|
|
892
|
+
const txParams = {
|
|
893
|
+
from: firstAcct,
|
|
894
|
+
nonce: '0x00',
|
|
895
|
+
gasPrice: '0x09184e72a000',
|
|
896
|
+
gasLimit: '0x2710',
|
|
897
|
+
to: firstAcct,
|
|
898
|
+
value: '0x1000',
|
|
899
|
+
};
|
|
900
|
+
|
|
901
|
+
it('returns a signed legacy tx object', async function () {
|
|
902
|
+
const tx = new EthereumTx(txParams);
|
|
903
|
+
expect(tx.isSigned()).toBe(false);
|
|
904
|
+
|
|
905
|
+
const signed = await keyring.signTransaction(firstAcct, tx);
|
|
906
|
+
expect(signed.isSigned()).toBe(true);
|
|
907
|
+
});
|
|
908
|
+
|
|
909
|
+
it('returns a signed tx object', async function () {
|
|
910
|
+
const tx = TransactionFactory.fromTxData(txParams);
|
|
911
|
+
expect(tx.isSigned()).toBe(false);
|
|
912
|
+
|
|
913
|
+
const signed = await keyring.signTransaction(firstAcct, tx);
|
|
914
|
+
expect(signed.isSigned()).toBe(true);
|
|
915
|
+
});
|
|
916
|
+
|
|
917
|
+
it('returns rejected promise if empty address is passed', async function () {
|
|
918
|
+
const tx = TransactionFactory.fromTxData(txParams);
|
|
919
|
+
await expect(keyring.signTransaction('', tx)).rejects.toThrow(
|
|
920
|
+
'Must specify address.',
|
|
921
|
+
);
|
|
922
|
+
});
|
|
923
|
+
|
|
924
|
+
it('throw error if wrong address is passed', async function () {
|
|
925
|
+
const tx = TransactionFactory.fromTxData(txParams);
|
|
926
|
+
await expect(
|
|
927
|
+
keyring.signTransaction(notKeyringAddress, tx),
|
|
928
|
+
).rejects.toThrow('HD Keyring - Unable to find matching address.');
|
|
549
929
|
});
|
|
550
930
|
});
|
|
551
931
|
});
|