@ledgerhq/hw-app-btc 6.11.1 → 6.15.0

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 (56) hide show
  1. package/README.md +106 -48
  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.map +1 -1
  6. package/lib/BtcNew.js +38 -167
  7. package/lib/BtcNew.js.map +1 -1
  8. package/lib/newops/accounttype.d.ts +110 -0
  9. package/lib/newops/accounttype.d.ts.map +1 -0
  10. package/lib/newops/accounttype.js +236 -0
  11. package/lib/newops/accounttype.js.map +1 -0
  12. package/lib/newops/appClient.js +4 -4
  13. package/lib/newops/appClient.js.map +1 -1
  14. package/lib/newops/clientCommands.d.ts.map +1 -1
  15. package/lib/newops/clientCommands.js +14 -9
  16. package/lib/newops/clientCommands.js.map +1 -1
  17. package/lib/newops/merkle.js +2 -2
  18. package/lib/newops/merkle.js.map +1 -1
  19. package/lib/newops/psbtExtractor.js +2 -2
  20. package/lib/newops/psbtExtractor.js.map +1 -1
  21. package/lib/newops/psbtv2.js +4 -4
  22. package/lib/newops/psbtv2.js.map +1 -1
  23. package/lib-es/Btc.d.ts.map +1 -1
  24. package/lib-es/Btc.js +5 -3
  25. package/lib-es/Btc.js.map +1 -1
  26. package/lib-es/BtcNew.d.ts.map +1 -1
  27. package/lib-es/BtcNew.js +41 -170
  28. package/lib-es/BtcNew.js.map +1 -1
  29. package/lib-es/newops/accounttype.d.ts +110 -0
  30. package/lib-es/newops/accounttype.d.ts.map +1 -0
  31. package/lib-es/newops/accounttype.js +233 -0
  32. package/lib-es/newops/accounttype.js.map +1 -0
  33. package/lib-es/newops/appClient.js +4 -4
  34. package/lib-es/newops/appClient.js.map +1 -1
  35. package/lib-es/newops/clientCommands.d.ts.map +1 -1
  36. package/lib-es/newops/clientCommands.js +14 -9
  37. package/lib-es/newops/clientCommands.js.map +1 -1
  38. package/lib-es/newops/merkle.js +2 -2
  39. package/lib-es/newops/merkle.js.map +1 -1
  40. package/lib-es/newops/psbtExtractor.js +2 -2
  41. package/lib-es/newops/psbtExtractor.js.map +1 -1
  42. package/lib-es/newops/psbtv2.js +4 -4
  43. package/lib-es/newops/psbtv2.js.map +1 -1
  44. package/package.json +3 -3
  45. package/src/Btc.ts +34 -3
  46. package/src/BtcNew.ts +64 -166
  47. package/src/newops/accounttype.ts +373 -0
  48. package/src/newops/appClient.ts +4 -4
  49. package/src/newops/clientCommands.ts +15 -9
  50. package/src/newops/merkle.ts +2 -2
  51. package/src/newops/psbtExtractor.ts +2 -2
  52. package/src/newops/psbtv2.ts +4 -4
  53. package/tests/Btc.test.ts +68 -39
  54. package/tests/newops/BtcNew.test.ts +23 -10
  55. package/tests/newops/integrationtools.ts +71 -41
  56. package/tests/newops/merkle.test.ts +1 -1
@@ -0,0 +1,373 @@
1
+ import { crypto } from "bitcoinjs-lib";
2
+ import { pointAddScalar } from "tiny-secp256k1";
3
+ import { BufferWriter } from "../buffertools";
4
+ import {
5
+ HASH_SIZE,
6
+ OP_CHECKSIG,
7
+ OP_DUP,
8
+ OP_EQUAL,
9
+ OP_EQUALVERIFY,
10
+ OP_HASH160,
11
+ } from "../constants";
12
+ import { hashPublicKey } from "../hashPublicKey";
13
+ import { DefaultDescriptorTemplate } from "./policy";
14
+ import { PsbtV2 } from "./psbtv2";
15
+
16
+ export type SpendingCondition = {
17
+ scriptPubKey: Buffer;
18
+ redeemScript?: Buffer;
19
+ // Possible future extension:
20
+ // witnessScript?: Buffer; // For p2wsh witnessScript
21
+ // tapScript?: {tapPath: Buffer[], script: Buffer} // For taproot
22
+ };
23
+
24
+ export type SpentOutput = { cond: SpendingCondition; amount: Buffer };
25
+
26
+ /**
27
+ * Encapsulates differences between account types, for example p2wpkh,
28
+ * p2wpkhWrapped, p2tr.
29
+ */
30
+ export interface AccountType {
31
+ /**
32
+ * Generates a scriptPubKey (output script) from a list of public keys. If a
33
+ * p2sh redeemScript or a p2wsh witnessScript is needed it will also be set on
34
+ * the returned SpendingCondition.
35
+ *
36
+ * The pubkeys are expected to be 33 byte ecdsa compressed pubkeys.
37
+ */
38
+ spendingCondition(pubkeys: Buffer[]): SpendingCondition;
39
+
40
+ /**
41
+ * Populates the psbt with account type-specific data for an input.
42
+ * @param i The index of the input map to populate
43
+ * @param inputTx The full transaction containing the spent output. This may
44
+ * be omitted for taproot.
45
+ * @param spentOutput The amount and spending condition of the spent output
46
+ * @param pubkeys The 33 byte ecdsa compressed public keys involved in the input
47
+ * @param pathElems The paths corresponding to the pubkeys, in same order.
48
+ */
49
+ setInput(
50
+ i: number,
51
+ inputTx: Buffer | undefined,
52
+ spentOutput: SpentOutput,
53
+ pubkeys: Buffer[],
54
+ pathElems: number[][]
55
+ ): void;
56
+
57
+ /**
58
+ * Populates the psbt with account type-specific data for an output. This is typically
59
+ * done for change outputs and other outputs that goes to the same account as
60
+ * being spent from.
61
+ * @param i The index of the output map to populate
62
+ * @param cond The spending condition for this output
63
+ * @param pubkeys The 33 byte ecdsa compressed public keys involved in this output
64
+ * @param paths The paths corresponding to the pubkeys, in same order.
65
+ */
66
+ setOwnOutput(
67
+ i: number,
68
+ cond: SpendingCondition,
69
+ pubkeys: Buffer[],
70
+ paths: number[][]
71
+ ): void;
72
+
73
+ /**
74
+ * Returns the descriptor template for this account type. Currently only
75
+ * DefaultDescriptorTemplates are allowed, but that might be changed in the
76
+ * future. See class WalletPolicy for more information on descriptor
77
+ * templates.
78
+ */
79
+ getDescriptorTemplate(): DefaultDescriptorTemplate;
80
+ }
81
+
82
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
83
+ interface BaseAccount extends AccountType {}
84
+
85
+ abstract class BaseAccount implements AccountType {
86
+ constructor(protected psbt: PsbtV2, protected masterFp: Buffer) {}
87
+ }
88
+
89
+ /**
90
+ * Superclass for single signature accounts. This will make sure that the pubkey
91
+ * arrays and path arrays in the method arguments contains exactly one element
92
+ * and calls an abstract method to do the actual work.
93
+ */
94
+ abstract class SingleKeyAccount extends BaseAccount {
95
+ spendingCondition(pubkeys: Buffer[]): SpendingCondition {
96
+ if (pubkeys.length != 1) {
97
+ throw new Error("Expected single key, got " + pubkeys.length);
98
+ }
99
+ return this.singleKeyCondition(pubkeys[0]);
100
+ }
101
+ protected abstract singleKeyCondition(pubkey: Buffer): SpendingCondition;
102
+
103
+ setInput(
104
+ i: number,
105
+ inputTx: Buffer | undefined,
106
+ spentOutput: SpentOutput,
107
+ pubkeys: Buffer[],
108
+ pathElems: number[][]
109
+ ) {
110
+ if (pubkeys.length != 1) {
111
+ throw new Error("Expected single key, got " + pubkeys.length);
112
+ }
113
+ if (pathElems.length != 1) {
114
+ throw new Error("Expected single path, got " + pathElems.length);
115
+ }
116
+ this.setSingleKeyInput(i, inputTx, spentOutput, pubkeys[0], pathElems[0]);
117
+ }
118
+ protected abstract setSingleKeyInput(
119
+ i: number,
120
+ inputTx: Buffer | undefined,
121
+ spentOutput: SpentOutput,
122
+ pubkey: Buffer,
123
+ path: number[]
124
+ );
125
+
126
+ setOwnOutput(
127
+ i: number,
128
+ cond: SpendingCondition,
129
+ pubkeys: Buffer[],
130
+ paths: number[][]
131
+ ) {
132
+ if (pubkeys.length != 1) {
133
+ throw new Error("Expected single key, got " + pubkeys.length);
134
+ }
135
+ if (paths.length != 1) {
136
+ throw new Error("Expected single path, got " + paths.length);
137
+ }
138
+ this.setSingleKeyOutput(i, cond, pubkeys[0], paths[0]);
139
+ }
140
+ protected abstract setSingleKeyOutput(
141
+ i: number,
142
+ cond: SpendingCondition,
143
+ pubkey: Buffer,
144
+ path: number[]
145
+ );
146
+ }
147
+
148
+ export class p2pkh extends SingleKeyAccount {
149
+ singleKeyCondition(pubkey: Buffer): SpendingCondition {
150
+ const buf = new BufferWriter();
151
+ const pubkeyHash = hashPublicKey(pubkey);
152
+ buf.writeSlice(Buffer.from([OP_DUP, OP_HASH160, HASH_SIZE]));
153
+ buf.writeSlice(pubkeyHash);
154
+ buf.writeSlice(Buffer.from([OP_EQUALVERIFY, OP_CHECKSIG]));
155
+ return { scriptPubKey: buf.buffer() };
156
+ }
157
+
158
+ setSingleKeyInput(
159
+ i: number,
160
+ inputTx: Buffer | undefined,
161
+ _spentOutput: SpentOutput,
162
+ pubkey: Buffer,
163
+ path: number[]
164
+ ) {
165
+ if (!inputTx) {
166
+ throw new Error("Full input base transaction required");
167
+ }
168
+ this.psbt.setInputNonWitnessUtxo(i, inputTx);
169
+ this.psbt.setInputBip32Derivation(i, pubkey, this.masterFp, path);
170
+ }
171
+
172
+ setSingleKeyOutput(
173
+ i: number,
174
+ cond: SpendingCondition,
175
+ pubkey: Buffer,
176
+ path: number[]
177
+ ) {
178
+ this.psbt.setOutputBip32Derivation(i, pubkey, this.masterFp, path);
179
+ }
180
+
181
+ getDescriptorTemplate(): DefaultDescriptorTemplate {
182
+ return "pkh(@0)";
183
+ }
184
+ }
185
+
186
+ export class p2tr extends SingleKeyAccount {
187
+ singleKeyCondition(pubkey: Buffer): SpendingCondition {
188
+ const xonlyPubkey = pubkey.slice(1); // x-only pubkey
189
+ const buf = new BufferWriter();
190
+ const outputKey = this.getTaprootOutputKey(xonlyPubkey);
191
+ buf.writeSlice(Buffer.from([0x51, 32])); // push1, pubkeylen
192
+ buf.writeSlice(outputKey);
193
+ return { scriptPubKey: buf.buffer() };
194
+ }
195
+
196
+ setSingleKeyInput(
197
+ i: number,
198
+ _inputTx: Buffer | undefined,
199
+ spentOutput: SpentOutput,
200
+ pubkey: Buffer,
201
+ path: number[]
202
+ ) {
203
+ const xonly = pubkey.slice(1);
204
+ this.psbt.setInputTapBip32Derivation(i, xonly, [], this.masterFp, path);
205
+ this.psbt.setInputWitnessUtxo(
206
+ i,
207
+ spentOutput.amount,
208
+ spentOutput.cond.scriptPubKey
209
+ );
210
+ }
211
+
212
+ setSingleKeyOutput(
213
+ i: number,
214
+ cond: SpendingCondition,
215
+ pubkey: Buffer,
216
+ path: number[]
217
+ ) {
218
+ const xonly = pubkey.slice(1);
219
+ this.psbt.setOutputTapBip32Derivation(i, xonly, [], this.masterFp, path);
220
+ }
221
+
222
+ getDescriptorTemplate(): DefaultDescriptorTemplate {
223
+ return "tr(@0)";
224
+ }
225
+
226
+ /*
227
+ The following two functions are copied from wallet-btc and adapted.
228
+ They should be moved to a library to avoid code reuse.
229
+ */
230
+ private hashTapTweak(x: Buffer): Buffer {
231
+ // hash_tag(x) = SHA256(SHA256(tag) || SHA256(tag) || x), see BIP340
232
+ // See https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki#specification
233
+ const h = crypto.sha256(Buffer.from("TapTweak", "utf-8"));
234
+ return crypto.sha256(Buffer.concat([h, h, x]));
235
+ }
236
+
237
+ /**
238
+ * Calculates a taproot output key from an internal key. This output key will be
239
+ * used as witness program in a taproot output. The internal key is tweaked
240
+ * according to recommendation in BIP341:
241
+ * https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_ref-22-0
242
+ *
243
+ * @param internalPubkey A 32 byte x-only taproot internal key
244
+ * @returns The output key
245
+ */
246
+ getTaprootOutputKey(internalPubkey: Buffer): Buffer {
247
+ if (internalPubkey.length != 32) {
248
+ throw new Error("Expected 32 byte pubkey. Got " + internalPubkey.length);
249
+ }
250
+ // A BIP32 derived key can be converted to a schnorr pubkey by dropping
251
+ // the first byte, which represent the oddness/evenness. In schnorr all
252
+ // pubkeys are even.
253
+ // https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki#public-key-conversion
254
+ const evenEcdsaPubkey = Buffer.concat([
255
+ Buffer.from([0x02]),
256
+ internalPubkey,
257
+ ]);
258
+ const tweak = this.hashTapTweak(internalPubkey);
259
+
260
+ // Q = P + int(hash_TapTweak(bytes(P)))G
261
+ const outputEcdsaKey = Buffer.from(pointAddScalar(evenEcdsaPubkey, tweak));
262
+ // Convert to schnorr.
263
+ const outputSchnorrKey = outputEcdsaKey.slice(1);
264
+ // Create address
265
+ return outputSchnorrKey;
266
+ }
267
+ }
268
+
269
+ export class p2wpkhWrapped extends SingleKeyAccount {
270
+ singleKeyCondition(pubkey: Buffer): SpendingCondition {
271
+ const buf = new BufferWriter();
272
+ const redeemScript = this.createRedeemScript(pubkey);
273
+ const scriptHash = hashPublicKey(redeemScript);
274
+ buf.writeSlice(Buffer.from([OP_HASH160, HASH_SIZE]));
275
+ buf.writeSlice(scriptHash);
276
+ buf.writeUInt8(OP_EQUAL);
277
+ return { scriptPubKey: buf.buffer(), redeemScript: redeemScript };
278
+ }
279
+
280
+ setSingleKeyInput(
281
+ i: number,
282
+ inputTx: Buffer | undefined,
283
+ spentOutput: SpentOutput,
284
+ pubkey: Buffer,
285
+ path: number[]
286
+ ) {
287
+ if (!inputTx) {
288
+ throw new Error("Full input base transaction required");
289
+ }
290
+ this.psbt.setInputNonWitnessUtxo(i, inputTx);
291
+ this.psbt.setInputBip32Derivation(i, pubkey, this.masterFp, path);
292
+
293
+ const userSuppliedRedeemScript = spentOutput.cond.redeemScript;
294
+ const expectedRedeemScript = this.createRedeemScript(pubkey);
295
+ if (
296
+ userSuppliedRedeemScript &&
297
+ !expectedRedeemScript.equals(userSuppliedRedeemScript)
298
+ ) {
299
+ // At what point might a user set the redeemScript on its own?
300
+ throw new Error(`User-supplied redeemScript ${userSuppliedRedeemScript.toString(
301
+ "hex"
302
+ )} doesn't
303
+ match expected ${expectedRedeemScript.toString("hex")} for input ${i}`);
304
+ }
305
+ this.psbt.setInputRedeemScript(i, expectedRedeemScript);
306
+ this.psbt.setInputWitnessUtxo(
307
+ i,
308
+ spentOutput.amount,
309
+ spentOutput.cond.scriptPubKey
310
+ );
311
+ }
312
+
313
+ setSingleKeyOutput(
314
+ i: number,
315
+ cond: SpendingCondition,
316
+ pubkey: Buffer,
317
+ path: number[]
318
+ ) {
319
+ this.psbt.setOutputRedeemScript(i, cond.redeemScript!);
320
+ this.psbt.setOutputBip32Derivation(i, pubkey, this.masterFp, path);
321
+ }
322
+
323
+ getDescriptorTemplate(): DefaultDescriptorTemplate {
324
+ return "sh(wpkh(@0))";
325
+ }
326
+
327
+ private createRedeemScript(pubkey: Buffer): Buffer {
328
+ const pubkeyHash = hashPublicKey(pubkey);
329
+ return Buffer.concat([Buffer.from("0014", "hex"), pubkeyHash]);
330
+ }
331
+ }
332
+
333
+ export class p2wpkh extends SingleKeyAccount {
334
+ singleKeyCondition(pubkey: Buffer): SpendingCondition {
335
+ const buf = new BufferWriter();
336
+ const pubkeyHash = hashPublicKey(pubkey);
337
+ buf.writeSlice(Buffer.from([0, HASH_SIZE]));
338
+ buf.writeSlice(pubkeyHash);
339
+ return { scriptPubKey: buf.buffer() };
340
+ }
341
+
342
+ setSingleKeyInput(
343
+ i: number,
344
+ inputTx: Buffer | undefined,
345
+ spentOutput: SpentOutput,
346
+ pubkey: Buffer,
347
+ path: number[]
348
+ ) {
349
+ if (!inputTx) {
350
+ throw new Error("Full input base transaction required");
351
+ }
352
+ this.psbt.setInputNonWitnessUtxo(i, inputTx);
353
+ this.psbt.setInputBip32Derivation(i, pubkey, this.masterFp, path);
354
+ this.psbt.setInputWitnessUtxo(
355
+ i,
356
+ spentOutput.amount,
357
+ spentOutput.cond.scriptPubKey
358
+ );
359
+ }
360
+
361
+ setSingleKeyOutput(
362
+ i: number,
363
+ cond: SpendingCondition,
364
+ pubkey: Buffer,
365
+ path: number[]
366
+ ) {
367
+ this.psbt.setOutputBip32Derivation(i, pubkey, this.masterFp, path);
368
+ }
369
+
370
+ getDescriptorTemplate(): DefaultDescriptorTemplate {
371
+ return "wpkh(@0)";
372
+ }
373
+ }
@@ -73,7 +73,7 @@ export class AppClient {
73
73
  const response = await this.makeRequest(
74
74
  BitcoinIns.GET_PUBKEY,
75
75
  Buffer.concat([
76
- Buffer.of(display ? 1 : 0),
76
+ Buffer.from(display ? [1] : [0]),
77
77
  pathElementsToBuffer(pathElements),
78
78
  ])
79
79
  );
@@ -108,10 +108,10 @@ export class AppClient {
108
108
  const response = await this.makeRequest(
109
109
  BitcoinIns.GET_WALLET_ADDRESS,
110
110
  Buffer.concat([
111
- Buffer.of(display ? 1 : 0),
111
+ Buffer.from(display ? [1] : [0]),
112
112
  walletPolicy.getWalletId(),
113
113
  walletHMAC || Buffer.alloc(32, 0),
114
- Buffer.of(change),
114
+ Buffer.from([change]),
115
115
  addressIndexBuffer,
116
116
  ]),
117
117
  clientInterpreter
@@ -181,6 +181,6 @@ export class AppClient {
181
181
  }
182
182
 
183
183
  async getMasterFingerprint(): Promise<Buffer> {
184
- return this.makeRequest(BitcoinIns.GET_MASTER_FINGERPRINT, Buffer.of());
184
+ return this.makeRequest(BitcoinIns.GET_MASTER_FINGERPRINT, Buffer.from([]));
185
185
  }
186
186
  }
@@ -1,4 +1,5 @@
1
1
  import { crypto } from "bitcoinjs-lib";
2
+ import { BufferReader } from "../buffertools";
2
3
  import { createVarint } from "../varint";
3
4
  import { hashLeaf, Merkle } from "./merkle";
4
5
  import { MerkleMap } from "./merkleMap";
@@ -106,19 +107,24 @@ export class GetMerkleLeafProofCommand extends ClientCommand {
106
107
  execute(request: Buffer): Buffer {
107
108
  const req = request.subarray(1);
108
109
 
109
- if (req.length != 32 + 4 + 4) {
110
- throw new Error("Invalid request, unexpected trailing data");
110
+ if (req.length < 32 + 1 + 1) {
111
+ throw new Error("Invalid request, expected at least 34 bytes");
111
112
  }
112
113
 
113
- // read the hash
114
- const hash = Buffer.alloc(32);
115
- for (let i = 0; i < 32; i++) {
116
- hash[i] = req.readUInt8(i);
117
- }
114
+ const reqBuf = new BufferReader(req);
115
+ const hash = reqBuf.readSlice(32);
118
116
  const hash_hex = hash.toString("hex");
119
117
 
120
- const tree_size = req.readUInt32BE(32);
121
- const leaf_index = req.readUInt32BE(32 + 4);
118
+ let tree_size;
119
+ let leaf_index;
120
+ try {
121
+ tree_size = reqBuf.readVarInt();
122
+ leaf_index = reqBuf.readVarInt();
123
+ } catch (e: any) {
124
+ throw new Error(
125
+ "Invalid request, couldn't parse tree_size or leaf_index"
126
+ );
127
+ }
122
128
 
123
129
  const mt = this.known_trees.get(hash_hex);
124
130
  if (!mt) {
@@ -62,7 +62,7 @@ export class Merkle {
62
62
  }
63
63
 
64
64
  hashNode(left: Buffer, right: Buffer): Buffer {
65
- return this.h(Buffer.concat([Buffer.of(1), left, right]));
65
+ return this.h(Buffer.concat([Buffer.from([1]), left, right]));
66
66
  }
67
67
  }
68
68
 
@@ -70,7 +70,7 @@ export function hashLeaf(
70
70
  buf: Buffer,
71
71
  hashFunction: (buf: Buffer) => Buffer = crypto.sha256
72
72
  ): Buffer {
73
- return hashConcat(Buffer.of(0), buf, hashFunction);
73
+ return hashConcat(Buffer.from([0]), buf, hashFunction);
74
74
  }
75
75
 
76
76
  function hashConcat(
@@ -13,7 +13,7 @@ export function extract(psbt: PsbtV2): Buffer {
13
13
 
14
14
  const isSegwit = !!psbt.getInputWitnessUtxo(0);
15
15
  if (isSegwit) {
16
- tx.writeSlice(Buffer.of(0, 1));
16
+ tx.writeSlice(Buffer.from([0, 1]));
17
17
  }
18
18
  const inputCount = psbt.getGlobalInputCount();
19
19
  tx.writeVarInt(inputCount);
@@ -21,7 +21,7 @@ export function extract(psbt: PsbtV2): Buffer {
21
21
  for (let i = 0; i < inputCount; i++) {
22
22
  tx.writeSlice(psbt.getInputPreviousTxid(i));
23
23
  tx.writeUInt32(psbt.getInputOutputIndex(i));
24
- tx.writeVarSlice(psbt.getInputFinalScriptsig(i) ?? Buffer.of());
24
+ tx.writeVarSlice(psbt.getInputFinalScriptsig(i) ?? Buffer.from([]));
25
25
  tx.writeUInt32(psbt.getInputSequence(i));
26
26
  if (isSegwit) {
27
27
  witnessWriter.writeSlice(psbt.getInputFinalScriptwitness(i));
@@ -33,7 +33,7 @@ export enum psbtOut {
33
33
  TAP_BIP32_DERIVATION = 0x07,
34
34
  }
35
35
 
36
- const PSBT_MAGIC_BYTES = Buffer.of(0x70, 0x73, 0x62, 0x74, 0xff);
36
+ const PSBT_MAGIC_BYTES = Buffer.from([0x70, 0x73, 0x62, 0x74, 0xff]);
37
37
 
38
38
  export class NoSuchEntry extends Error {}
39
39
 
@@ -321,7 +321,7 @@ export class PsbtV2 {
321
321
  }
322
322
  serialize(): Buffer {
323
323
  const buf = new BufferWriter();
324
- buf.writeSlice(Buffer.of(0x70, 0x73, 0x62, 0x74, 0xff));
324
+ buf.writeSlice(Buffer.from([0x70, 0x73, 0x62, 0x74, 0xff]));
325
325
  serializeMap(buf, this.globalMap);
326
326
  this.inputMaps.forEach((map) => {
327
327
  serializeMap(buf, map);
@@ -371,7 +371,7 @@ export class PsbtV2 {
371
371
  return keyTypes.some((k) => k == keyType);
372
372
  }
373
373
  private setGlobal(keyType: KeyType, value: Buffer) {
374
- const key = new Key(keyType, Buffer.of());
374
+ const key = new Key(keyType, Buffer.from([]));
375
375
  this.globalMap.set(key.toString(), value);
376
376
  }
377
377
  private getGlobal(keyType: KeyType): Buffer {
@@ -541,7 +541,7 @@ function serializeMap(buf: BufferWriter, map: Map<string, Buffer>) {
541
541
  }
542
542
 
543
543
  function b(): Buffer {
544
- return Buffer.of();
544
+ return Buffer.from([]);
545
545
  }
546
546
  function set(
547
547
  map: Map<string, Buffer>,
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> {}