@ledgerhq/hw-app-btc 6.11.0 → 6.12.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.
Files changed (69) hide show
  1. package/README.md +110 -47
  2. package/lib/Btc.d.ts.map +1 -1
  3. package/lib/Btc.js +5 -3
  4. package/lib/Btc.js.map +1 -1
  5. package/lib/BtcNew.d.ts +1 -1
  6. package/lib/BtcNew.d.ts.map +1 -1
  7. package/lib/BtcNew.js +71 -174
  8. package/lib/BtcNew.js.map +1 -1
  9. package/lib/newops/accounttype.d.ts +110 -0
  10. package/lib/newops/accounttype.d.ts.map +1 -0
  11. package/lib/newops/accounttype.js +236 -0
  12. package/lib/newops/accounttype.js.map +1 -0
  13. package/lib/newops/appClient.d.ts +1 -1
  14. package/lib/newops/appClient.d.ts.map +1 -1
  15. package/lib/newops/appClient.js +7 -7
  16. package/lib/newops/appClient.js.map +1 -1
  17. package/lib/newops/clientCommands.d.ts +3 -2
  18. package/lib/newops/clientCommands.d.ts.map +1 -1
  19. package/lib/newops/clientCommands.js +19 -12
  20. package/lib/newops/clientCommands.js.map +1 -1
  21. package/lib/newops/merkle.js +2 -2
  22. package/lib/newops/merkle.js.map +1 -1
  23. package/lib/newops/psbtExtractor.js +2 -2
  24. package/lib/newops/psbtExtractor.js.map +1 -1
  25. package/lib/newops/psbtv2.d.ts +3 -0
  26. package/lib/newops/psbtv2.d.ts.map +1 -1
  27. package/lib/newops/psbtv2.js +14 -4
  28. package/lib/newops/psbtv2.js.map +1 -1
  29. package/lib-es/Btc.d.ts.map +1 -1
  30. package/lib-es/Btc.js +5 -3
  31. package/lib-es/Btc.js.map +1 -1
  32. package/lib-es/BtcNew.d.ts +1 -1
  33. package/lib-es/BtcNew.d.ts.map +1 -1
  34. package/lib-es/BtcNew.js +73 -176
  35. package/lib-es/BtcNew.js.map +1 -1
  36. package/lib-es/newops/accounttype.d.ts +110 -0
  37. package/lib-es/newops/accounttype.d.ts.map +1 -0
  38. package/lib-es/newops/accounttype.js +233 -0
  39. package/lib-es/newops/accounttype.js.map +1 -0
  40. package/lib-es/newops/appClient.d.ts +1 -1
  41. package/lib-es/newops/appClient.d.ts.map +1 -1
  42. package/lib-es/newops/appClient.js +7 -7
  43. package/lib-es/newops/appClient.js.map +1 -1
  44. package/lib-es/newops/clientCommands.d.ts +3 -2
  45. package/lib-es/newops/clientCommands.d.ts.map +1 -1
  46. package/lib-es/newops/clientCommands.js +19 -12
  47. package/lib-es/newops/clientCommands.js.map +1 -1
  48. package/lib-es/newops/merkle.js +2 -2
  49. package/lib-es/newops/merkle.js.map +1 -1
  50. package/lib-es/newops/psbtExtractor.js +2 -2
  51. package/lib-es/newops/psbtExtractor.js.map +1 -1
  52. package/lib-es/newops/psbtv2.d.ts +3 -0
  53. package/lib-es/newops/psbtv2.d.ts.map +1 -1
  54. package/lib-es/newops/psbtv2.js +14 -4
  55. package/lib-es/newops/psbtv2.js.map +1 -1
  56. package/package.json +3 -3
  57. package/src/Btc.ts +34 -3
  58. package/src/BtcNew.ts +105 -174
  59. package/src/newops/accounttype.ts +373 -0
  60. package/src/newops/appClient.ts +8 -7
  61. package/src/newops/clientCommands.ts +19 -12
  62. package/src/newops/merkle.ts +2 -2
  63. package/src/newops/psbtExtractor.ts +2 -2
  64. package/src/newops/psbtv2.ts +13 -4
  65. package/tests/Btc.test.ts +68 -39
  66. package/tests/newops/BtcNew.test.ts +47 -20
  67. package/tests/newops/integrationtools.ts +91 -50
  68. package/tests/newops/merkle.test.ts +1 -1
  69. package/tests/newops/testtx.ts +0 -55
package/tests/Btc.test.ts CHANGED
@@ -106,14 +106,24 @@ ascii(1NjiCsVBuKDT62LmaUd7WZZZBK2gPAkisb)
106
106
  8bd937d416de7020952cc8e2c99ce9ac7e01265e31ceb8e47bf9c37f46f8abbd
107
107
  */
108
108
  /*eslint-disable */
109
- const pubkeyParent = "045d4a72237572a91e13818fa38cedabe6174569cc9a319012f75150d5c0a0639d54eafd13a68d079b7a67764800c6a981825ef52384f08c3925109188ab21bc09";
110
- const addrParent = Buffer.from("1NjiCsVBuKDT62LmaUd7WZZZBK2gPAkisb", "ascii").toString("hex");
111
- const ccParent = "8bd937d416de7020952cc8e2c99ce9ac7e01265e31ceb8e47bf9c37f46f8abbd";
109
+ const pubkeyParent =
110
+ "045d4a72237572a91e13818fa38cedabe6174569cc9a319012f75150d5c0a0639d54eafd13a68d079b7a67764800c6a981825ef52384f08c3925109188ab21bc09";
111
+ const addrParent = Buffer.from(
112
+ "1NjiCsVBuKDT62LmaUd7WZZZBK2gPAkisb",
113
+ "ascii"
114
+ ).toString("hex");
115
+ const ccParent =
116
+ "8bd937d416de7020952cc8e2c99ce9ac7e01265e31ceb8e47bf9c37f46f8abbd";
112
117
  const responseParent = `41${pubkeyParent}22${addrParent}${ccParent}`;
113
118
 
114
- const pubkeyAcc = "04250dfdfb84c1efd160ed0e10ebac845d0e4b04277174630ba56de96bbd3afb21fc6c04ce0d5a0cbd784fdabc99d16269c27cf3842fe8440f1f21b8af900f0eaa";
115
- const addrAcc = Buffer.from("16Y97ByhyboePhTYMMmFj1tq5Cy1bDq8jT", "ascii").toString("hex");
116
- const ccAcc = "c071c6f2d05cbc9ea9a04951b238086ce1608cf00020c3cab85b36aac5fdd591";
119
+ const pubkeyAcc =
120
+ "04250dfdfb84c1efd160ed0e10ebac845d0e4b04277174630ba56de96bbd3afb21fc6c04ce0d5a0cbd784fdabc99d16269c27cf3842fe8440f1f21b8af900f0eaa";
121
+ const addrAcc = Buffer.from(
122
+ "16Y97ByhyboePhTYMMmFj1tq5Cy1bDq8jT",
123
+ "ascii"
124
+ ).toString("hex");
125
+ const ccAcc =
126
+ "c071c6f2d05cbc9ea9a04951b238086ce1608cf00020c3cab85b36aac5fdd591";
117
127
  /*eslint-enable */
118
128
  const responseAcc = `41${pubkeyAcc}22${addrAcc}${ccAcc}`;
119
129
  const transport = await openTransportReplayer(
@@ -123,7 +133,7 @@ ascii(1NjiCsVBuKDT62LmaUd7WZZZBK2gPAkisb)
123
133
  => e040000009028000002c80000000
124
134
  <= ${responseParent}9000
125
135
  => e04000000d038000002c8000000080000011
126
- <= ${responseAcc}9000
136
+ <= ${responseAcc}9000
127
137
  `)
128
138
  );
129
139
  const btc = new Btc(transport);
@@ -430,7 +440,7 @@ test("signMessage", async () => {
430
440
  function testBackend(s: string): any {
431
441
  return async () => {
432
442
  return { publicKey: s, bitcoinAddress: "", chainCode: "" };
433
- }
443
+ };
434
444
  }
435
445
 
436
446
  class TestBtc extends Btc {
@@ -461,37 +471,51 @@ class TestBtc extends Btc {
461
471
  // });
462
472
 
463
473
  test.each`
464
- app | ver | path | format | display | exp
465
- ${"Bitcoin"} | ${"1.99.99"} | ${"m/44'/0'/1'"} | ${"bech32m"} | ${false} | ${""}
466
- ${"Bitcoin"} | ${"1.99.99"} | ${"m/44'/0'"} | ${"bech32m"} | ${false} | ${""}
467
- ${"Bitcoin"} | ${"2.0.0-alpha1"} | ${"m/44'/0'/1'"} | ${"bech32m"} | ${false} | ${"new"}
468
- ${"Bitcoin"} | ${"2.0.0-alpha1"} | ${"m/44'/0'"} | ${"bech32m"} | ${false} | ${"new"}
469
- ${"Bitcoin"} | ${"2.0.0-alpha1"} | ${"m/44'/0'/1'"} | ${"bech32"} | ${false} | ${"new"}
470
- ${"Bitcoin"} | ${"2.0.0-alpha1"} | ${"m/44'/0'"} | ${"bech32"} | ${undefined} | ${"old"}
471
- ${"Bitcoin"} | ${"2.0.0-alpha1"} | ${"m/44'/0'"} | ${"bech32"} | ${true} | ${"new"}
472
- `("dispatch $app $ver $path $format $display to $exp", async ({ app, ver, path, format, display, exp }) => {
473
- const appName = Buffer.of(app.length)
474
- .toString("hex")
475
- .concat(Buffer.from(app, "ascii").toString("hex"));
476
- const appVersion = Buffer.of(ver.length)
477
- .toString("hex")
478
- .concat(Buffer.from(ver, "ascii").toString("hex"));
479
- const resp = `01${appName}${appVersion}01029000`;
480
- const tr = await openTransportReplayer(RecordStore.fromString(`=> b001000000\n <= ${resp}`));
481
- const btc = new TestBtc(tr);
482
- try {
483
- const key = await btc.getWalletPublicKey(path, { format: format, verify: display });
484
- if (exp === "") {
485
- expect(1).toEqual(0); // Allways fail. Don't know how to do that properly
486
- }
487
- expect(key.publicKey).toEqual(exp);
488
- } catch (e: any) {
489
- if (exp != "") {
490
- throw e;
474
+ app | ver | path | format | display | exp
475
+ ${"Bitcoin"} | ${"1.99.99"} | ${"m/44'/0'/1'"} | ${"bech32m"} | ${false} | ${""}
476
+ ${"Bitcoin"} | ${"1.99.99"} | ${"m/44'/0'"} | ${"bech32m"} | ${false} | ${""}
477
+ ${"Bitcoin"} | ${"2.0.0-alpha1"} | ${"m/44'/0'/1'"} | ${"bech32m"} | ${false} | ${"new"}
478
+ ${"Bitcoin"} | ${"2.0.0-alpha1"} | ${"m/44'/0'"} | ${"bech32m"} | ${false} | ${"new"}
479
+ ${"Bitcoin"} | ${"2.0.0-beta"} | ${"m/84'/1'/0'"} | ${"bech32"} | ${false} | ${"new"}
480
+ ${"Bitcoin"} | ${"2.0.0-alpha1"} | ${"m/44'/0'/1'"} | ${"bech32"} | ${false} | ${"new"}
481
+ ${"Bitcoin"} | ${"2.0.0-alpha1"} | ${"m/44'/0'"} | ${"bech32"} | ${undefined} | ${"old"}
482
+ ${"Bitcoin"} | ${"2.0.0-alpha1"} | ${"m/44'/0'"} | ${"bech32"} | ${true} | ${"new"}
483
+ ${"Bitcoin"} | ${"2.0.0-alpha1"} | ${"m/44'/0'/1'/0/0"} | ${"bech32"} | ${false} | ${"new"}
484
+ ${"Bitcoin"} | ${"2.0.0-alpha1"} | ${"m/44'/0'/1'/1/0"} | ${"bech32"} | ${false} | ${"new"}
485
+ ${"Bitcoin"} | ${"2.0.0-alpha1"} | ${"m/44'/0'/1'/1/0"} | ${"legacy"} | ${false} | ${"new"}
486
+ ${"Bitcoin"} | ${"2.0.0-alpha1"} | ${"m/44'/0'/1'/1/0"} | ${"p2sh"} | ${false} | ${"new"}
487
+ ${"Bitcoin"} | ${"2.0.0-alpha1"} | ${"m/44'/0'/1'/2/0"} | ${"bech32"} | ${false} | ${"old"}
488
+ `(
489
+ "dispatch $app $ver $path $format $display to $exp",
490
+ async ({ app, ver, path, format, display, exp }) => {
491
+ const appName = Buffer.from([app.length])
492
+ .toString("hex")
493
+ .concat(Buffer.from(app, "ascii").toString("hex"));
494
+ const appVersion = Buffer.from([ver.length])
495
+ .toString("hex")
496
+ .concat(Buffer.from(ver, "ascii").toString("hex"));
497
+ const resp = `01${appName}${appVersion}01029000`;
498
+ const tr = await openTransportReplayer(
499
+ RecordStore.fromString(`=> b001000000\n <= ${resp}`)
500
+ );
501
+ const btc = new TestBtc(tr);
502
+ try {
503
+ const key = await btc.getWalletPublicKey(path, {
504
+ format: format,
505
+ verify: display,
506
+ });
507
+ if (exp === "") {
508
+ expect(1).toEqual(0); // Allways fail. Don't know how to do that properly
509
+ }
510
+ expect(key.publicKey).toEqual(exp);
511
+ } catch (e: any) {
512
+ if (exp != "") {
513
+ throw e;
514
+ }
515
+ expect(exp).toEqual("");
491
516
  }
492
- expect(exp).toEqual("");
493
517
  }
494
- })
518
+ );
495
519
 
496
520
  // test("getWalletPublicKey compatibility for internal hardened keys", async () => {
497
521
  // await testDispatch("Bitcoin", "1.99.99", "m/44'/0'/1'", "bech32m", "");
@@ -502,5 +526,10 @@ ${"Bitcoin"} | ${"2.0.0-alpha1"} | ${"m/44'/0'"} | ${"bech32"} | ${true} | ${"ne
502
526
  // await testDispatch("Bitcoin", "2.0.0-alpha1", "m/44'/0'", "bech32", "old");
503
527
  // });
504
528
 
505
- async function testDispatch(name: string, version: string, path: string, addressFormat: AddressFormat | undefined, exp: string): Promise<void> {
506
- }
529
+ async function testDispatch(
530
+ name: string,
531
+ version: string,
532
+ path: string,
533
+ addressFormat: AddressFormat | undefined,
534
+ exp: string
535
+ ): Promise<void> {}
@@ -1,5 +1,4 @@
1
1
  /* eslint-disable @typescript-eslint/no-non-null-assertion */
2
- /* eslint-disable prettier/prettier */
3
2
  import { openTransportReplayer, RecordStore } from "@ledgerhq/hw-transport-mocker";
4
3
  import { TransportReplayer } from "@ledgerhq/hw-transport-mocker/lib/openTransportReplayer";
5
4
  import ecc from "tiny-secp256k1";
@@ -10,8 +9,8 @@ import {
10
9
  WalletPolicy
11
10
  } from "../../src/newops/policy";
12
11
  import { PsbtV2 } from "../../src/newops/psbtv2";
13
- import { AccountType, addressFormatFromDescriptorTemplate, creatDummyXpub, masterFingerprint, runSignTransaction, TestingClient } from "./integrationtools";
14
- import { CoreTx, p2pkh, p2tr, p2wpkh, p2wpkhTwoInputs, wrappedP2wpkh, wrappedP2wpkhTwoInputs } from "./testtx";
12
+ import { StandardPurpose, addressFormatFromDescriptorTemplate, creatDummyXpub, masterFingerprint, runSignTransaction, TestingClient } from "./integrationtools";
13
+ import { CoreInput, CoreTx, p2pkh, p2tr, p2wpkh, wrappedP2wpkh, wrappedP2wpkhTwoInputs } from "./testtx";
15
14
 
16
15
  test("getWalletPublicKey p2pkh", async () => {
17
16
  await testGetWalletPublicKey("m/44'/1'/0'", "pkh(@0)");
@@ -41,7 +40,7 @@ test("getWalletXpub normal path", async () => {
41
40
  await testGetWalletXpub("m/44'/0'/0'");
42
41
  });
43
42
 
44
- function testPaths(type: AccountType): {ins: string[], out?: string} {
43
+ function testPaths(type: StandardPurpose): { ins: string[], out?: string } {
45
44
  const basePath = `m/${type}/1'/0'/`;
46
45
  const ins = [
47
46
  basePath + "0/0",
@@ -51,48 +50,64 @@ function testPaths(type: AccountType): {ins: string[], out?: string} {
51
50
  basePath + "0/2",
52
51
  basePath + "1/2",
53
52
  ];
54
- return {ins};
53
+ return { ins };
55
54
  }
56
55
 
57
56
  test("Sign p2pkh", async () => {
58
57
  const changePubkey = "037ed58c914720772c59f7a1e7e76fba0ef95d7c5667119798586301519b9ad2cf";
59
- await runSignTransactionTest(p2pkh, AccountType.p2pkh, changePubkey);
58
+ await runSignTransactionTest(p2pkh, StandardPurpose.p2pkh, changePubkey);
60
59
  });
61
60
  test("Sign p2wpkh wrapped", async () => {
62
61
  let changePubkey = "03efc6b990c1626d08bd176aab0e545a4f55c627c7ddee878d12bbbc46a126177a";
63
- await runSignTransactionTest(wrappedP2wpkh, AccountType.p2wpkhInP2sh, changePubkey);
62
+ await runSignTransactionTest(wrappedP2wpkh, StandardPurpose.p2wpkhInP2sh, changePubkey);
64
63
  changePubkey = "031175a985c56e310ce3496a819229b427a2172920fd20b5972dda62758c6def09";
65
- await runSignTransactionTest(wrappedP2wpkhTwoInputs, AccountType.p2wpkhInP2sh, changePubkey);
64
+ await runSignTransactionTest(wrappedP2wpkhTwoInputs, StandardPurpose.p2wpkhInP2sh, changePubkey);
66
65
  });
67
66
  test("Sign p2wpkh", async () => {
68
- await runSignTransactionTest(p2wpkh, AccountType.p2wpkh);
69
- await runSignTransactionTest(p2wpkhTwoInputs, AccountType.p2wpkh);
67
+ await runSignTransactionTest(p2wpkh, StandardPurpose.p2wpkh);
70
68
  });
71
69
  test("Sign p2tr", async () => {
72
- await runSignTransactionTest(p2tr, AccountType.p2tr);
70
+ // This tx uses locktime, so this test verifies that locktime is propagated to/from
71
+ // the psbt correctly.
72
+ await runSignTransactionTest(p2tr, StandardPurpose.p2tr);
73
73
  });
74
74
 
75
- async function runSignTransactionTest(testTx: CoreTx, accountType: AccountType, changePubkey?: string) {
75
+ test("Sign p2tr with sigHashType", async () => {
76
+ const testTx = JSON.parse(JSON.stringify(p2tr));
77
+ testTx.vin.forEach((input: CoreInput, index: number) => {
78
+ // Test SIGHASH_SINGLE | SIGHASH_ANYONECANPAY, 0x83
79
+ const sig = input.txinwitness![0] + "83";
80
+ input.txinwitness = [sig];
81
+ })
82
+ const tx = await runSignTransactionNoVerification(testTx, StandardPurpose.p2tr);
83
+ // The verification of the sighashtype is done in MockClient.signPsbt
84
+ })
85
+
86
+ async function runSignTransactionTest(testTx: CoreTx, accountType: StandardPurpose, changePubkey?: string) {
87
+ const tx = await runSignTransactionNoVerification(testTx, accountType, changePubkey);
88
+ expect(tx).toEqual(testTx.hex);
89
+ }
90
+
91
+ async function runSignTransactionNoVerification(testTx: CoreTx, accountType: StandardPurpose, changePubkey?: string): Promise<string> {
76
92
  const [client, transport] = await createClient();
77
93
  const accountXpub = "tpubDCwYjpDhUdPGP5rS3wgNg13mTrrjBuG8V9VpWbyptX6TRPbNoZVXsoVUSkCjmQ8jJycjuDKBb9eataSymXakTTaGifxR6kmVsfFehH1ZgJT";
78
94
  client.mockGetPubkeyResponse(`m/${accountType}/1'/0'`, accountXpub);
79
95
  const paths = testPaths(accountType);
80
96
  if (changePubkey) {
81
- paths.out = `m/${accountType}/1'/0'` + "/1/3";
97
+ paths.out = `m/${accountType}/1'/0'` + "/1/3";
82
98
  client.mockGetPubkeyResponse(paths.out, creatDummyXpub(Buffer.from(changePubkey, "hex")));
83
99
  }
84
100
  const tx = await runSignTransaction(testTx, paths, client, transport);
85
- expect(tx).toEqual(testTx.hex);
86
101
  await transport.close();
102
+ return tx;
87
103
  }
88
104
 
89
-
90
105
  async function testGetWalletXpub(path: string, version = 0x043587cf) {
91
106
  const [client] = await createClient();
92
107
  const expectedXpub = "tpubDCwYjpDhUdPGP5rS3wgNg13mTrrjBuG8V9VpWbyptX6TRPbNoZVXsoVUSkCjmQ8jJycjuDKBb9eataSymXakTTaGifxR6kmVsfFehH1ZgJT";
93
108
  client.mockGetPubkeyResponse(path, expectedXpub);
94
109
  const btc = new BtcNew(client);
95
- const result = await btc.getWalletXpub({path: path, xpubVersion: version});
110
+ const result = await btc.getWalletXpub({ path: path, xpubVersion: version });
96
111
  expect(result).toEqual(expectedXpub);
97
112
  }
98
113
  async function testGetWalletPublicKey(
@@ -119,7 +134,7 @@ async function testGetWalletPublicKey(
119
134
 
120
135
  const btcNew = new BtcNew(client);
121
136
  const addressFormat = addressFormatFromDescriptorTemplate(expectedDescriptorTemplate);
122
- const result = await btcNew.getWalletPublicKey(path, {format: addressFormat});
137
+ const result = await btcNew.getWalletPublicKey(path, { format: addressFormat });
123
138
  verifyGetWalletPublicKeyResult(result, keyXpub, "testaddress");
124
139
 
125
140
  const resultAccount = await btcNew.getWalletPublicKey(accountPath);
@@ -195,11 +210,23 @@ class MockClient extends TestingClient {
195
210
  return masterFingerprint;
196
211
  }
197
212
  async signPsbt(
198
- _psbt: PsbtV2,
213
+ psbt: PsbtV2,
199
214
  _walletPolicy: WalletPolicy,
200
- _walletHMAC: Buffer | null
215
+ _walletHMAC: Buffer | null,
201
216
  ): Promise<Map<number, Buffer>> {
202
- return this.yieldSigs.splice(0, 1)[0];
217
+ const sigs = this.yieldSigs.splice(0, 1)[0];
218
+ const sig0 = sigs.get(0)!;
219
+ if (sig0.length == 64) {
220
+ // Taproot may leave out sighash type, which defaults to 0x01 SIGHASH_ALL
221
+ return sigs;
222
+ }
223
+ const sigHashType = sig0.readUInt8(sig0.length - 1);
224
+ if (sigHashType != 0x01) {
225
+ for (let i = 0; i < psbt.getGlobalInputCount(); i++) {
226
+ expect(psbt.getInputSighashType(i)).toEqual(sigHashType);
227
+ }
228
+ }
229
+ return sigs;
203
230
  }
204
231
  private getWalletAddressKey(
205
232
  walletPolicy: WalletPolicy,
@@ -1,5 +1,4 @@
1
1
  /* eslint-disable @typescript-eslint/no-non-null-assertion */
2
- /* eslint-disable prettier/prettier */
3
2
  import Transport from "@ledgerhq/hw-transport";
4
3
  import bs58check from "bs58check";
5
4
  import Btc from "../../src/Btc";
@@ -10,26 +9,26 @@ import { AddressFormat } from "../../src/getWalletPublicKey";
10
9
  import { AppClient } from "../../src/newops/appClient";
11
10
  import {
12
11
  DefaultDescriptorTemplate,
13
- WalletPolicy
12
+ WalletPolicy,
14
13
  } from "../../src/newops/policy";
15
14
  import { Transaction } from "../../src/types";
16
15
  import { CoreInput, CoreTx, spentTxs } from "./testtx";
17
16
 
18
-
19
17
  export async function runSignTransaction(
20
- testTx: CoreTx,
21
- testPaths: {ins: string[], out?: string},
22
- client: TestingClient,
23
- transport: Transport): Promise<string> {
18
+ testTx: CoreTx,
19
+ testPaths: { ins: string[]; out?: string },
20
+ client: TestingClient,
21
+ transport: Transport
22
+ ): Promise<string> {
24
23
  const btcNew = new BtcNew(client);
25
24
  // btc is needed to perform some functions like splitTransaction.
26
25
  const btc = new Btc(transport);
27
26
  const accountType = getAccountType(testTx.vin[0], btc);
28
27
  const additionals: string[] = [];
29
- if (accountType == AccountType.p2wpkh) {
28
+ if (accountType == StandardPurpose.p2wpkh) {
30
29
  additionals.push("bech32");
31
30
  }
32
- if (accountType == AccountType.p2tr) {
31
+ if (accountType == StandardPurpose.p2tr) {
33
32
  additionals.push("bech32m");
34
33
  }
35
34
  const associatedKeysets: string[] = [];
@@ -38,35 +37,61 @@ export async function runSignTransaction(
38
37
  const path = testPaths.ins[index];
39
38
  associatedKeysets.push(path);
40
39
  const inputData = createInput(input, btc);
41
- const pubkey = getPubkey(index, accountType, testTx, inputData[0], inputData[1]);
40
+ const pubkey = getPubkey(
41
+ index,
42
+ accountType,
43
+ testTx,
44
+ inputData[0],
45
+ inputData[1]
46
+ );
42
47
  const mockXpub = creatDummyXpub(pubkey);
43
48
  client.mockGetPubkeyResponse(path, mockXpub);
44
49
  yieldSigs.set(index, getSignature(input, accountType));
45
50
  return inputData;
46
51
  });
52
+ const sig0 = yieldSigs.get(0)!;
53
+ let sigHashType: number | undefined = sig0.readUInt8(sig0.length - 1);
54
+ if (sigHashType == 0x01) {
55
+ sigHashType = undefined;
56
+ }
47
57
  client.mockSignPsbt(yieldSigs);
48
58
  const outputWriter = new BufferWriter();
49
59
  outputWriter.writeVarInt(testTx.vout.length);
50
- testTx.vout.forEach(output => {
51
- outputWriter.writeUInt64(BigInt(Number.parseFloat((output.value * 100000000).toFixed(8))));
52
- outputWriter.writeVarSlice(Buffer.from(output.scriptPubKey.hex, "hex"));
60
+ testTx.vout.forEach((output) => {
61
+ outputWriter.writeUInt64(
62
+ BigInt(Number.parseFloat((output.value * 100000000).toFixed(8)))
63
+ );
64
+ outputWriter.writeVarSlice(Buffer.from(output.scriptPubKey.hex, "hex"));
53
65
  });
54
- const outputScriptHex = outputWriter.buffer().toString("hex");
55
-
56
- const arg: CreateTransactionArg = {
66
+ const outputScriptHex = outputWriter.buffer().toString("hex");
67
+ let callbacks = "";
68
+ function logCallback(message: string) {
69
+ callbacks += new Date().toISOString() + " " + message + "\n";
70
+ }
71
+ const arg: CreateTransactionArg = {
57
72
  inputs,
58
73
  additionals,
59
74
  associatedKeysets,
60
75
  changePath: testPaths.out,
61
76
  outputScriptHex,
62
77
  lockTime: testTx.locktime,
63
- segwit: accountType != AccountType.p2pkh,
78
+ sigHashType,
79
+ segwit: accountType != StandardPurpose.p2pkh,
80
+ onDeviceSignatureGranted: () => logCallback("CALLBACK: signature granted"),
81
+ onDeviceSignatureRequested: () =>
82
+ logCallback("CALLBACK: signature requested"),
83
+ onDeviceStreaming: (arg) => logCallback("CALLBACK: " + JSON.stringify(arg)),
64
84
  };
85
+ logCallback("Start createPaymentTransactionNew");
65
86
  const tx = await btcNew.createPaymentTransactionNew(arg);
87
+ logCallback("Done createPaymentTransactionNew");
88
+ // console.log(callbacks);
66
89
  return tx;
67
- };
90
+ }
68
91
 
69
- export function addressFormatFromDescriptorTemplate(descTemp: DefaultDescriptorTemplate): AddressFormat {
92
+ export function addressFormatFromDescriptorTemplate(
93
+ descTemp: DefaultDescriptorTemplate
94
+ ): AddressFormat {
70
95
  if (descTemp == "tr(@0)") return "bech32m";
71
96
  if (descTemp == "pkh(@0)") return "legacy";
72
97
  if (descTemp == "wpkh(@0)") return "bech32";
@@ -74,42 +99,57 @@ export function addressFormatFromDescriptorTemplate(descTemp: DefaultDescriptorT
74
99
  throw new Error();
75
100
  }
76
101
 
77
- export enum AccountType {
102
+ export enum StandardPurpose {
78
103
  p2tr = "86'",
79
104
  p2wpkh = "84'",
80
105
  p2wpkhInP2sh = "49'",
81
- p2pkh = "44'"
106
+ p2pkh = "44'",
82
107
  }
83
108
 
84
- function getPubkey(inputIndex: number, accountType: AccountType, testTx: CoreTx, spentTx: Transaction, spentOutputIndex: number): Buffer {
109
+ function getPubkey(
110
+ inputIndex: number,
111
+ accountType: StandardPurpose,
112
+ testTx: CoreTx,
113
+ spentTx: Transaction,
114
+ spentOutputIndex: number
115
+ ): Buffer {
85
116
  const scriptSig = Buffer.from(testTx.vin[inputIndex].scriptSig.hex, "hex");
86
- if (accountType == AccountType.p2pkh) {
87
- return scriptSig.slice(scriptSig.length-33);
117
+ if (accountType == StandardPurpose.p2pkh) {
118
+ return scriptSig.slice(scriptSig.length - 33);
88
119
  }
89
- if (accountType == AccountType.p2tr) {
120
+ if (accountType == StandardPurpose.p2tr) {
90
121
  return spentTx.outputs![spentOutputIndex].script.slice(2, 34); // 32 bytes x-only pubkey
91
122
  }
92
- if (accountType == AccountType.p2wpkh || accountType == AccountType.p2wpkhInP2sh) {
123
+ if (
124
+ accountType == StandardPurpose.p2wpkh ||
125
+ accountType == StandardPurpose.p2wpkhInP2sh
126
+ ) {
93
127
  return Buffer.from(testTx.vin[inputIndex].txinwitness![1], "hex");
94
128
  }
95
129
  throw new Error();
96
130
  }
97
131
 
98
- function getSignature(testTxInput: CoreInput, accountType: AccountType): Buffer {
132
+ function getSignature(
133
+ testTxInput: CoreInput,
134
+ accountType: StandardPurpose
135
+ ): Buffer {
99
136
  const scriptSig = Buffer.from(testTxInput.scriptSig.hex, "hex");
100
- if (accountType == AccountType.p2pkh) {
101
- return scriptSig.slice(1, scriptSig.length-34);
137
+ if (accountType == StandardPurpose.p2pkh) {
138
+ return scriptSig.slice(1, scriptSig.length - 34);
102
139
  }
103
- if (accountType == AccountType.p2tr) {
140
+ if (accountType == StandardPurpose.p2tr) {
104
141
  return Buffer.from(testTxInput.txinwitness![0], "hex");
105
142
  }
106
- if (accountType == AccountType.p2wpkh || accountType == AccountType.p2wpkhInP2sh) {
143
+ if (
144
+ accountType == StandardPurpose.p2wpkh ||
145
+ accountType == StandardPurpose.p2wpkhInP2sh
146
+ ) {
107
147
  return Buffer.from(testTxInput.txinwitness![0], "hex");
108
148
  }
109
149
  throw new Error();
110
150
  }
111
151
 
112
- function getAccountType(coreInput: CoreInput, btc: Btc): AccountType {
152
+ function getAccountType(coreInput: CoreInput, btc: Btc): StandardPurpose {
113
153
  const spentTx = spentTxs[coreInput.txid];
114
154
  if (!spentTx) {
115
155
  throw new Error("Spent tx " + coreInput.txid + " unavailable.");
@@ -118,46 +158,47 @@ function getAccountType(coreInput: CoreInput, btc: Btc): AccountType {
118
158
  const spentOutput = splitSpentTx.outputs![coreInput.vout];
119
159
  const script = spentOutput.script;
120
160
  if (script.length == 34 && script[0] == 0x51) {
121
- return AccountType.p2tr;
161
+ return StandardPurpose.p2tr;
122
162
  }
123
163
  if (script.length == 22 && script[0] == 0x00) {
124
- return AccountType.p2wpkh;
164
+ return StandardPurpose.p2wpkh;
125
165
  }
126
166
  if (script.length == 23) {
127
- return AccountType.p2wpkhInP2sh;
167
+ return StandardPurpose.p2wpkhInP2sh;
128
168
  }
129
- return AccountType.p2pkh;
169
+ return StandardPurpose.p2pkh;
130
170
  }
131
171
 
132
172
  export function creatDummyXpub(pubkey: Buffer): string {
133
- const xpubDecoded = bs58check.decode("tpubDHcN44A4UHqdHJZwBxgTbu8Cy87ZrZkN8tQnmJGhcijHqe4rztuvGcD4wo36XSviLmiqL5fUbDnekYaQ7LzAnaqauBb9RsyahsTTFHdeJGd");
134
- const pubkey33 = pubkey.length == 33 ? pubkey : Buffer.concat([Buffer.of(2), pubkey]);
135
- xpubDecoded.fill(pubkey33, xpubDecoded.length-33);
173
+ const xpubDecoded = bs58check.decode(
174
+ "tpubDHcN44A4UHqdHJZwBxgTbu8Cy87ZrZkN8tQnmJGhcijHqe4rztuvGcD4wo36XSviLmiqL5fUbDnekYaQ7LzAnaqauBb9RsyahsTTFHdeJGd"
175
+ );
176
+ const pubkey33 =
177
+ pubkey.length == 33 ? pubkey : Buffer.concat([Buffer.from([2]), pubkey]);
178
+ xpubDecoded.fill(pubkey33, xpubDecoded.length - 33);
136
179
  return bs58check.encode(xpubDecoded);
137
180
  }
138
181
 
139
- function createInput(coreInput: CoreInput, btc: Btc): [Transaction, number, string, number] {
182
+ function createInput(
183
+ coreInput: CoreInput,
184
+ btc: Btc
185
+ ): [Transaction, number, string | null, number] {
140
186
  const spentTx = spentTxs[coreInput.txid];
141
187
  if (!spentTx) {
142
188
  throw new Error("Spent tx " + coreInput.txid + " unavailable.");
143
189
  }
144
190
  const splitSpentTx = btc.splitTransaction(spentTx, true);
145
- const scriptSig = coreInput.scriptSig;
146
- let redeemScript;
147
- if (scriptSig?.hex && scriptSig.hex.startsWith("160014")) {
148
- redeemScript = scriptSig.hex.substring(2);
149
- }
150
- return [splitSpentTx, coreInput.vout, redeemScript, coreInput.sequence];
191
+ return [splitSpentTx, coreInput.vout, null, coreInput.sequence];
151
192
  }
152
193
 
153
- export const masterFingerprint = Buffer.of(1, 2, 3, 4);
194
+ export const masterFingerprint = Buffer.from([1, 2, 3, 4]);
154
195
  export class TestingClient extends AppClient {
155
- mockGetPubkeyResponse(_pathElements: string, _response: string): void {};
196
+ mockGetPubkeyResponse(_pathElements: string, _response: string): void {}
156
197
  mockGetWalletAddressResponse(
157
198
  _walletPolicy: WalletPolicy,
158
199
  _change: number,
159
200
  _addressIndex: number,
160
201
  _response: string
161
- ): void {};
162
- mockSignPsbt(_yieldSigs: Map<number, Buffer>): void {};
202
+ ): void {}
203
+ mockSignPsbt(_yieldSigs: Map<number, Buffer>): void {}
163
204
  }
@@ -3,7 +3,7 @@ function testHasher(buf: Buffer): Buffer {
3
3
  return Buffer.from(buf);
4
4
  }
5
5
  function leaf(n: number) {
6
- return Buffer.of(0, n);
6
+ return Buffer.from([0, n]);
7
7
  }
8
8
  function merkleOf(count: number): Merkle {
9
9
  const leaves: Buffer[] = [];
@@ -237,61 +237,6 @@ export const p2pkh: CoreTx = {
237
237
  "blocktime": 1633611385
238
238
  };
239
239
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
240
- export const p2wpkhTwoInputs = {
241
- "txid": "1913b7b5ffdcb5f32b9aca1f5eec2a189e7c66650f82b560eae211265fc995b7",
242
- "hash": "c3439dcd3489373c586c7aed48c32f2b5d9c71aad24acd765a61684d98690a3f",
243
- "version": 2,
244
- "size": 388,
245
- "vsize": 226,
246
- "weight": 904,
247
- "locktime": 0,
248
- "vin": [
249
- {
250
- "txid": "5512d5788d4c26117f093de91223ef384c3fb22799810a92e3304bb6f0819224",
251
- "vout": 1,
252
- "scriptSig": {
253
- "asm": "0014c1ac0d63d0258ea1b6fe90ef72d0c35d8d773dd3",
254
- "hex": "160014c1ac0d63d0258ea1b6fe90ef72d0c35d8d773dd3"
255
- },
256
- "txinwitness": [
257
- "30440220543617c5f4504dc29d34d2d06d0d7733dac4ec418b77c67feefb29f3f82ba3d80220690b784c52c3375f4ba9e64cc5c0aeb6a1b9fc6aadda0062905c06ce3bbba57501",
258
- "02fb255ed920db5c2f507289202eb60a160e5a067ee7e30199a4ed81b74c22e441"
259
- ],
260
- "sequence": 4294967295
261
- },
262
- {
263
- "txid": "28ad5054e029252d72da37f13fce66212d7f7763845b4a8c4aaf78e897b2bf9f",
264
- "vout": 1,
265
- "scriptSig": {
266
- "asm": "0014c1ac0d63d0258ea1b6fe90ef72d0c35d8d773dd3",
267
- "hex": "160014c1ac0d63d0258ea1b6fe90ef72d0c35d8d773dd3"
268
- },
269
- "txinwitness": [
270
- "3044022049e7f3015a33ccdb015fe3891667564fd37111272df57e58447645c7bad8fed0022074d1e93ba946453896d0f0bc500df3a1e0d5bb5ad10cd9906736d5fbaebadd5801",
271
- "02fb255ed920db5c2f507289202eb60a160e5a067ee7e30199a4ed81b74c22e441"
272
- ],
273
- "sequence": 4294967295
274
- }
275
- ],
276
- "vout": [
277
- {
278
- "value": 0.01800000,
279
- "n": 0,
280
- "scriptPubKey": {
281
- "asm": "OP_DUP OP_HASH160 f73384bcc3951ab6a75541ff79a9a51f82056ed8 OP_EQUALVERIFY OP_CHECKSIG",
282
- "hex": "76a914f73384bcc3951ab6a75541ff79a9a51f82056ed888ac",
283
- "address": "n442v1DrXQNim9gjjctKjyGVoe717hNdtG",
284
- "type": "pubkeyhash"
285
- }
286
- }
287
- ],
288
- "hex": "02000000000102249281f0b64b30e3920a819927b23f4c38ef2312e93d097f11264c8d78d512550100000017160014c1ac0d63d0258ea1b6fe90ef72d0c35d8d773dd3ffffffff9fbfb297e878af4a8c4a5b8463777f2d2166ce3ff137da722d2529e05450ad280100000017160014c1ac0d63d0258ea1b6fe90ef72d0c35d8d773dd3ffffffff0140771b00000000001976a914f73384bcc3951ab6a75541ff79a9a51f82056ed888ac024730440220543617c5f4504dc29d34d2d06d0d7733dac4ec418b77c67feefb29f3f82ba3d80220690b784c52c3375f4ba9e64cc5c0aeb6a1b9fc6aadda0062905c06ce3bbba575012102fb255ed920db5c2f507289202eb60a160e5a067ee7e30199a4ed81b74c22e44102473044022049e7f3015a33ccdb015fe3891667564fd37111272df57e58447645c7bad8fed0022074d1e93ba946453896d0f0bc500df3a1e0d5bb5ad10cd9906736d5fbaebadd58012102fb255ed920db5c2f507289202eb60a160e5a067ee7e30199a4ed81b74c22e44100000000",
289
- "blockhash": "00000000025a711e6cd4bce9138dc852232a4494afbf36d8bb80499a786da2a4",
290
- "confirmations": 1,
291
- "time": 1633944124,
292
- "blocktime": 1633944124
293
- };
294
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
295
240
  export const wrappedP2wpkhTwoInputs = {
296
241
  "txid": "c03119b538c78f56c8ce2e6cc5fc6998d447eeef42e34c12692764a3f1a3da7c",
297
242
  "hash": "6b3812304554a6964e43a6971ac533046f4be101e39609f72179856916e20268",