@bitgo/wasm-utxo 1.5.0 → 1.7.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 (68) hide show
  1. package/dist/cjs/js/ast/formatNode.js +2 -2
  2. package/dist/cjs/js/ast/fromWasmNode.js +2 -1
  3. package/dist/cjs/js/bip32.d.ts +140 -0
  4. package/dist/cjs/js/bip32.js +177 -0
  5. package/dist/cjs/js/ecpair.d.ts +96 -0
  6. package/dist/cjs/js/ecpair.js +134 -0
  7. package/dist/cjs/js/{fixedScriptWallet.d.ts → fixedScriptWallet/BitGoPsbt.d.ts} +40 -41
  8. package/dist/cjs/js/{fixedScriptWallet.js → fixedScriptWallet/BitGoPsbt.js} +50 -36
  9. package/dist/cjs/js/fixedScriptWallet/ReplayProtection.d.ts +58 -0
  10. package/dist/cjs/js/fixedScriptWallet/ReplayProtection.js +89 -0
  11. package/dist/cjs/js/fixedScriptWallet/RootWalletKeys.d.ts +66 -0
  12. package/dist/cjs/js/fixedScriptWallet/RootWalletKeys.js +108 -0
  13. package/dist/cjs/js/fixedScriptWallet/address.d.ts +20 -0
  14. package/dist/cjs/js/fixedScriptWallet/address.js +29 -0
  15. package/dist/cjs/js/fixedScriptWallet/index.d.ts +4 -0
  16. package/dist/cjs/js/fixedScriptWallet/index.js +12 -0
  17. package/dist/cjs/js/index.d.ts +5 -1
  18. package/dist/cjs/js/index.js +11 -2
  19. package/dist/cjs/js/utxolibCompat.d.ts +0 -18
  20. package/dist/cjs/js/wasm/wasm_utxo.d.ts +254 -22
  21. package/dist/cjs/js/wasm/wasm_utxo.js +1081 -223
  22. package/dist/cjs/js/wasm/wasm_utxo_bg.wasm +0 -0
  23. package/dist/cjs/js/wasm/wasm_utxo_bg.wasm.d.ts +53 -8
  24. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  25. package/dist/esm/js/ast/formatNode.js +2 -2
  26. package/dist/esm/js/ast/fromWasmNode.js +2 -1
  27. package/dist/esm/js/bip32.d.ts +140 -0
  28. package/dist/esm/js/bip32.js +173 -0
  29. package/dist/esm/js/ecpair.d.ts +96 -0
  30. package/dist/esm/js/ecpair.js +130 -0
  31. package/dist/esm/js/{fixedScriptWallet.d.ts → fixedScriptWallet/BitGoPsbt.d.ts} +40 -41
  32. package/dist/esm/js/{fixedScriptWallet.js → fixedScriptWallet/BitGoPsbt.js} +49 -33
  33. package/dist/esm/js/fixedScriptWallet/ReplayProtection.d.ts +58 -0
  34. package/dist/esm/js/fixedScriptWallet/ReplayProtection.js +85 -0
  35. package/dist/esm/js/fixedScriptWallet/RootWalletKeys.d.ts +66 -0
  36. package/dist/esm/js/fixedScriptWallet/RootWalletKeys.js +104 -0
  37. package/dist/esm/js/fixedScriptWallet/address.d.ts +20 -0
  38. package/dist/esm/js/fixedScriptWallet/address.js +25 -0
  39. package/dist/esm/js/fixedScriptWallet/index.d.ts +4 -0
  40. package/dist/esm/js/fixedScriptWallet/index.js +4 -0
  41. package/dist/esm/js/index.d.ts +5 -1
  42. package/dist/esm/js/index.js +8 -1
  43. package/dist/esm/js/utxolibCompat.d.ts +0 -18
  44. package/dist/esm/js/wasm/wasm_utxo.d.ts +254 -22
  45. package/dist/esm/js/wasm/wasm_utxo_bg.js +1070 -220
  46. package/dist/esm/js/wasm/wasm_utxo_bg.wasm +0 -0
  47. package/dist/esm/js/wasm/wasm_utxo_bg.wasm.d.ts +53 -8
  48. package/dist/esm/test/address/utxolibCompat.js +12 -10
  49. package/dist/esm/test/bip32.d.ts +1 -0
  50. package/dist/esm/test/bip32.js +242 -0
  51. package/dist/esm/test/descriptorUtil.js +1 -1
  52. package/dist/esm/test/ecpair.d.ts +1 -0
  53. package/dist/esm/test/ecpair.js +137 -0
  54. package/dist/esm/test/fixedScript/address.js +9 -7
  55. package/dist/esm/test/fixedScript/fixtureUtil.d.ts +5 -3
  56. package/dist/esm/test/fixedScript/fixtureUtil.js +18 -7
  57. package/dist/esm/test/fixedScript/parseTransactionWithWalletKeys.js +39 -11
  58. package/dist/esm/test/fixedScript/verifySignature.js +73 -27
  59. package/dist/esm/test/fixedScriptToDescriptor.js +6 -4
  60. package/dist/esm/test/fixtures.js +5 -1
  61. package/dist/esm/test/psbt.util.js +1 -4
  62. package/dist/esm/test/psbtFixedScriptCompat.js +19 -17
  63. package/dist/esm/test/psbtFixedScriptCompatFixtures.js +1 -1
  64. package/dist/esm/test/psbtFromDescriptor.js +5 -8
  65. package/dist/esm/test/psbtFromDescriptor.util.js +3 -3
  66. package/dist/esm/test/test.js +5 -4
  67. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  68. package/package.json +8 -3
@@ -1,14 +1,31 @@
1
1
  import assert from "node:assert";
2
2
  import * as utxolib from "@bitgo/utxo-lib";
3
3
  import { fixedScriptWallet } from "../../js/index.js";
4
- import { loadPsbtFixture, loadWalletKeysFromFixture, getPsbtBuffer } from "./fixtureUtil.js";
4
+ import { loadPsbtFixture, loadWalletKeysFromFixture, getPsbtBuffer, loadReplayProtectionKeyFromFixture, } from "./fixtureUtil.js";
5
+ function getExpectedInputScriptType(fixtureScriptType) {
6
+ // Map fixture types to InputScriptType values
7
+ // Based on the Rust mapping in src/fixed_script_wallet/test_utils/fixtures.rs
8
+ switch (fixtureScriptType) {
9
+ case "p2shP2pk":
10
+ case "p2sh":
11
+ case "p2shP2wsh":
12
+ case "p2wsh":
13
+ return fixtureScriptType;
14
+ case "p2tr":
15
+ return "p2trLegacy";
16
+ case "p2trMusig2":
17
+ return "p2trMusig2ScriptPath";
18
+ case "taprootKeyPathSpend":
19
+ return "p2trMusig2KeyPath";
20
+ default:
21
+ throw new Error(`Unknown fixture script type: ${fixtureScriptType}`);
22
+ }
23
+ }
5
24
  function getOtherWalletKeys() {
6
25
  const otherWalletKeys = utxolib.testutil.getKeyTriple("too many secrets");
7
26
  return new utxolib.bitgo.RootWalletKeys(otherWalletKeys);
8
27
  }
9
28
  describe("parseTransactionWithWalletKeys", function () {
10
- // Replay protection script that matches Rust tests
11
- const replayProtectionScript = Buffer.from("a91420b37094d82a513451ff0ccd9db23aba05bc5ef387", "hex");
12
29
  const supportedNetworks = utxolib.getNetworkList().filter((network) => {
13
30
  return (utxolib.isMainnet(network) &&
14
31
  network !== utxolib.networks.bitcoincash &&
@@ -17,20 +34,20 @@ describe("parseTransactionWithWalletKeys", function () {
17
34
  network !== utxolib.networks.ecash &&
18
35
  network !== utxolib.networks.zcash);
19
36
  });
20
- function hasReplayProtection(network) {
21
- const mainnet = utxolib.getMainnet(network);
22
- return mainnet === utxolib.networks.bitcoincash;
23
- }
24
37
  supportedNetworks.forEach((network) => {
25
38
  const networkName = utxolib.getNetworkName(network);
26
39
  describe(`network: ${networkName}`, function () {
27
40
  let fullsignedPsbtBytes;
28
41
  let bitgoPsbt;
29
42
  let rootWalletKeys;
43
+ let replayProtectionKey;
44
+ let fixture;
30
45
  before(function () {
31
- fullsignedPsbtBytes = getPsbtBuffer(loadPsbtFixture(networkName, "fullsigned"));
46
+ fixture = loadPsbtFixture(networkName, "fullsigned");
47
+ fullsignedPsbtBytes = getPsbtBuffer(fixture);
32
48
  bitgoPsbt = fixedScriptWallet.BitGoPsbt.fromBytes(fullsignedPsbtBytes, networkName);
33
- rootWalletKeys = loadWalletKeysFromFixture(networkName);
49
+ rootWalletKeys = loadWalletKeysFromFixture(fixture);
50
+ replayProtectionKey = loadReplayProtectionKeyFromFixture(fixture);
34
51
  });
35
52
  it("should have matching unsigned transaction ID", function () {
36
53
  const unsignedTxid = bitgoPsbt.unsignedTxid();
@@ -42,7 +59,7 @@ describe("parseTransactionWithWalletKeys", function () {
42
59
  });
43
60
  it("should parse transaction and identify internal/external outputs", function () {
44
61
  const parsed = bitgoPsbt.parseTransactionWithWalletKeys(rootWalletKeys, {
45
- outputScripts: [replayProtectionScript],
62
+ publicKeys: [replayProtectionKey],
46
63
  });
47
64
  // Verify all inputs have addresses and values
48
65
  parsed.inputs.forEach((input, i) => {
@@ -85,10 +102,21 @@ describe("parseTransactionWithWalletKeys", function () {
85
102
  assert.ok(typeof parsed.virtualSize === "number", "Virtual size should be a number");
86
103
  assert.ok(parsed.virtualSize > 0, "Virtual size should be > 0");
87
104
  });
105
+ it("should parse inputs with correct scriptType", function () {
106
+ const parsed = bitgoPsbt.parseTransactionWithWalletKeys(rootWalletKeys, {
107
+ publicKeys: [replayProtectionKey],
108
+ });
109
+ // Verify all inputs have scriptType matching fixture
110
+ parsed.inputs.forEach((input, i) => {
111
+ const fixtureInput = fixture.psbtInputs[i];
112
+ const expectedScriptType = getExpectedInputScriptType(fixtureInput.type);
113
+ assert.strictEqual(input.scriptType, expectedScriptType, `Input ${i} scriptType should be ${expectedScriptType}, got ${input.scriptType}`);
114
+ });
115
+ });
88
116
  it("should fail to parse with other wallet keys", function () {
89
117
  assert.throws(() => {
90
118
  bitgoPsbt.parseTransactionWithWalletKeys(getOtherWalletKeys(), {
91
- outputScripts: [replayProtectionScript],
119
+ publicKeys: [replayProtectionKey],
92
120
  });
93
121
  }, (error) => {
94
122
  return error.message.includes("Failed to parse transaction: Input 0: wallet validation failed");
@@ -1,7 +1,7 @@
1
1
  import assert from "node:assert";
2
2
  import * as utxolib from "@bitgo/utxo-lib";
3
- import { fixedScriptWallet } from "../../js/index.js";
4
- import { loadPsbtFixture, loadWalletKeysFromFixture, getPsbtBuffer, } from "./fixtureUtil.js";
3
+ import { fixedScriptWallet, BIP32 } from "../../js/index.js";
4
+ import { loadPsbtFixture, loadWalletKeysFromFixture, getPsbtBuffer, loadReplayProtectionKeyFromFixture, } from "./fixtureUtil.js";
5
5
  /**
6
6
  * Get expected signature state for an input based on type and signing stage
7
7
  * @param inputType - The type of input (e.g., "p2shP2pk", "p2trMusig2")
@@ -29,7 +29,7 @@ function getExpectedSignatures(inputType, signatureStage) {
29
29
  // Regular multisig uses user + bitgo
30
30
  return { user: true, backup: false, bitgo: true };
31
31
  default:
32
- throw new Error(`Unknown signature stage: ${signatureStage}`);
32
+ throw new Error(`Unknown signature stage: ${String(signatureStage)}`);
33
33
  }
34
34
  }
35
35
  /**
@@ -40,25 +40,45 @@ function getExpectedSignatures(inputType, signatureStage) {
40
40
  * @param inputType - The type of input (for replay protection handling)
41
41
  * @param expectedSignatures - Expected signature state for each key or replay protection
42
42
  */
43
- function verifyInputSignatures(bitgoPsbt, rootWalletKeys, inputIndex, expectedSignatures) {
43
+ function verifyInputSignatures(bitgoPsbt, parsed, rootWalletKeys, replayProtectionKey, inputIndex, expectedSignatures) {
44
44
  // Handle replay protection inputs (P2shP2pk)
45
45
  if ("hasReplayProtectionSignature" in expectedSignatures) {
46
- const replayProtectionScript = Buffer.from("a91420b37094d82a513451ff0ccd9db23aba05bc5ef387", "hex");
47
46
  const hasReplaySig = bitgoPsbt.verifyReplayProtectionSignature(inputIndex, {
48
- outputScripts: [replayProtectionScript],
47
+ publicKeys: [replayProtectionKey],
49
48
  });
50
49
  assert.strictEqual(hasReplaySig, expectedSignatures.hasReplayProtectionSignature, `Input ${inputIndex} replay protection signature mismatch`);
51
50
  return;
52
51
  }
52
+ if (parsed.inputs[inputIndex].scriptType === "p2shP2pk") {
53
+ const hasReplaySig = bitgoPsbt.verifySignature(inputIndex, replayProtectionKey);
54
+ assert.ok("hasReplayProtectionSignature" in expectedSignatures, "Expected hasReplayProtectionSignature to be present");
55
+ assert.strictEqual(hasReplaySig, expectedSignatures.hasReplayProtectionSignature, `Input ${inputIndex} replay protection signature mismatch`);
56
+ return;
57
+ }
53
58
  // Handle standard multisig inputs
54
- const xpubs = rootWalletKeys.triple;
55
- const hasUserSig = bitgoPsbt.verifySignature(inputIndex, xpubs[0].toBase58());
56
- const hasBackupSig = bitgoPsbt.verifySignature(inputIndex, xpubs[1].toBase58());
57
- const hasBitGoSig = bitgoPsbt.verifySignature(inputIndex, xpubs[2].toBase58());
59
+ const hasUserSig = bitgoPsbt.verifySignature(inputIndex, rootWalletKeys.userKey());
60
+ const hasBackupSig = bitgoPsbt.verifySignature(inputIndex, rootWalletKeys.backupKey());
61
+ const hasBitGoSig = bitgoPsbt.verifySignature(inputIndex, rootWalletKeys.bitgoKey());
58
62
  assert.strictEqual(hasUserSig, expectedSignatures.user, `Input ${inputIndex} user key signature mismatch`);
59
63
  assert.strictEqual(hasBackupSig, expectedSignatures.backup, `Input ${inputIndex} backup key signature mismatch`);
60
64
  assert.strictEqual(hasBitGoSig, expectedSignatures.bitgo, `Input ${inputIndex} BitGo key signature mismatch`);
61
65
  }
66
+ /**
67
+ * Helper to verify signatures for all inputs in a PSBT
68
+ * @param bitgoPsbt - The PSBT to verify
69
+ * @param fixture - The test fixture containing input metadata
70
+ * @param rootWalletKeys - Wallet keys for verification
71
+ * @param replayProtectionKey - Key for replay protection inputs
72
+ * @param signatureStage - The signing stage (unsigned, halfsigned, fullsigned)
73
+ */
74
+ function verifyAllInputSignatures(bitgoPsbt, fixture, rootWalletKeys, replayProtectionKey, signatureStage) {
75
+ const parsed = bitgoPsbt.parseTransactionWithWalletKeys(rootWalletKeys, {
76
+ publicKeys: [replayProtectionKey],
77
+ });
78
+ fixture.psbtInputs.forEach((input, index) => {
79
+ verifyInputSignatures(bitgoPsbt, parsed, rootWalletKeys, replayProtectionKey, index, getExpectedSignatures(input.type, signatureStage));
80
+ });
81
+ }
62
82
  describe("verifySignature", function () {
63
83
  const supportedNetworks = utxolib.getNetworkList().filter((network) => {
64
84
  return (utxolib.isMainnet(network) &&
@@ -72,6 +92,7 @@ describe("verifySignature", function () {
72
92
  const networkName = utxolib.getNetworkName(network);
73
93
  describe(`network: ${networkName}`, function () {
74
94
  let rootWalletKeys;
95
+ let replayProtectionKey;
75
96
  let unsignedFixture;
76
97
  let halfsignedFixture;
77
98
  let fullsignedFixture;
@@ -79,42 +100,34 @@ describe("verifySignature", function () {
79
100
  let halfsignedBitgoPsbt;
80
101
  let fullsignedBitgoPsbt;
81
102
  before(function () {
82
- rootWalletKeys = loadWalletKeysFromFixture(networkName);
83
103
  unsignedFixture = loadPsbtFixture(networkName, "unsigned");
84
104
  halfsignedFixture = loadPsbtFixture(networkName, "halfsigned");
85
105
  fullsignedFixture = loadPsbtFixture(networkName, "fullsigned");
106
+ rootWalletKeys = loadWalletKeysFromFixture(fullsignedFixture);
107
+ replayProtectionKey = loadReplayProtectionKeyFromFixture(fullsignedFixture);
86
108
  unsignedBitgoPsbt = fixedScriptWallet.BitGoPsbt.fromBytes(getPsbtBuffer(unsignedFixture), networkName);
87
109
  halfsignedBitgoPsbt = fixedScriptWallet.BitGoPsbt.fromBytes(getPsbtBuffer(halfsignedFixture), networkName);
88
110
  fullsignedBitgoPsbt = fixedScriptWallet.BitGoPsbt.fromBytes(getPsbtBuffer(fullsignedFixture), networkName);
89
111
  });
90
112
  describe("unsigned PSBT", function () {
91
113
  it("should return false for unsigned inputs", function () {
92
- // Verify all xpubs return false for all inputs
93
- unsignedFixture.psbtInputs.forEach((input, index) => {
94
- verifyInputSignatures(unsignedBitgoPsbt, rootWalletKeys, index, getExpectedSignatures(input.type, "unsigned"));
95
- });
114
+ verifyAllInputSignatures(unsignedBitgoPsbt, unsignedFixture, rootWalletKeys, replayProtectionKey, "unsigned");
96
115
  });
97
116
  });
98
117
  describe("half-signed PSBT", function () {
99
118
  it("should return true for signed xpubs and false for unsigned", function () {
100
- halfsignedFixture.psbtInputs.forEach((input, index) => {
101
- verifyInputSignatures(halfsignedBitgoPsbt, rootWalletKeys, index, getExpectedSignatures(input.type, "halfsigned"));
102
- });
119
+ verifyAllInputSignatures(halfsignedBitgoPsbt, halfsignedFixture, rootWalletKeys, replayProtectionKey, "halfsigned");
103
120
  });
104
121
  });
105
122
  describe("fully signed PSBT", function () {
106
123
  it("should have 2 signatures (2-of-3 multisig)", function () {
107
- // In fullsigned fixtures, verify 2 signatures exist per multisig input
108
- fullsignedFixture.psbtInputs.forEach((input, index) => {
109
- verifyInputSignatures(fullsignedBitgoPsbt, rootWalletKeys, index, getExpectedSignatures(input.type, "fullsigned"));
110
- });
124
+ verifyAllInputSignatures(fullsignedBitgoPsbt, fullsignedFixture, rootWalletKeys, replayProtectionKey, "fullsigned");
111
125
  });
112
126
  });
113
127
  describe("error handling", function () {
114
128
  it("should throw error for out of bounds input index", function () {
115
- const xpubs = rootWalletKeys.triple;
116
129
  assert.throws(() => {
117
- fullsignedBitgoPsbt.verifySignature(999, xpubs[0].toBase58());
130
+ fullsignedBitgoPsbt.verifySignature(999, rootWalletKeys.userKey());
118
131
  }, (error) => {
119
132
  return error.message.includes("Input index 999 out of bounds");
120
133
  }, "Should throw error for out of bounds input index");
@@ -123,18 +136,51 @@ describe("verifySignature", function () {
123
136
  assert.throws(() => {
124
137
  fullsignedBitgoPsbt.verifySignature(0, "invalid-xpub");
125
138
  }, (error) => {
126
- return error.message.includes("Invalid xpub");
139
+ return error.message.includes("Invalid");
127
140
  }, "Should throw error for invalid xpub");
128
141
  });
129
142
  it("should return false for xpub not in derivation path", function () {
130
143
  // Create a different xpub that's not in the wallet
131
144
  // Use a proper 32-byte seed (256 bits)
132
145
  const differentSeed = Buffer.alloc(32, 0xaa); // 32 bytes filled with 0xaa
133
- const differentKey = utxolib.bip32.fromSeed(differentSeed, network);
146
+ const differentKey = BIP32.fromSeed(differentSeed);
134
147
  const differentXpub = differentKey.neutered();
135
- const result = fullsignedBitgoPsbt.verifySignature(0, differentXpub.toBase58());
148
+ const result = fullsignedBitgoPsbt.verifySignature(0, differentXpub);
136
149
  assert.strictEqual(result, false, "Should return false for xpub not in PSBT derivation paths");
137
150
  });
151
+ it("should verify signature with raw public key (Uint8Array)", function () {
152
+ // Verify that xpub-based verification works
153
+ const userKey = rootWalletKeys.userKey();
154
+ const hasXpubSig = fullsignedBitgoPsbt.verifySignature(0, userKey);
155
+ // This test specifically checks that raw public key verification works
156
+ // We test the underlying WASM API by ensuring both xpub and raw pubkey
157
+ // calls reach the correct methods
158
+ // Use a random public key that's not in the PSBT to test the API works
159
+ const randomSeed = Buffer.alloc(32, 0xcc);
160
+ const randomKey = BIP32.fromSeed(randomSeed);
161
+ const randomPubkey = randomKey.publicKey;
162
+ // This should return false (no signature for this key)
163
+ const result = fullsignedBitgoPsbt.verifySignature(0, randomPubkey);
164
+ assert.strictEqual(result, false, "Should return false for public key not in PSBT");
165
+ // Verify the xpub check still works (regression test)
166
+ assert.strictEqual(hasXpubSig, true, "Should still verify with xpub");
167
+ });
168
+ it("should return false for raw public key with no signature", function () {
169
+ // Create a random public key that's not in the PSBT
170
+ const randomSeed = Buffer.alloc(32, 0xbb);
171
+ const randomKey = BIP32.fromSeed(randomSeed);
172
+ const randomPubkey = randomKey.publicKey;
173
+ const result = fullsignedBitgoPsbt.verifySignature(0, randomPubkey);
174
+ assert.strictEqual(result, false, "Should return false for public key not in PSBT signatures");
175
+ });
176
+ it("should throw error for invalid key length", function () {
177
+ const invalidKey = Buffer.alloc(31); // Invalid length (should be 32 for private key or 33 for public key)
178
+ assert.throws(() => {
179
+ fullsignedBitgoPsbt.verifySignature(0, invalidKey);
180
+ }, (error) => {
181
+ return error.message.includes("Invalid key length");
182
+ }, "Should throw error for invalid key length");
183
+ });
138
184
  });
139
185
  });
140
186
  });
@@ -82,10 +82,12 @@ function runTest(scriptType, index, scope) {
82
82
  });
83
83
  });
84
84
  }
85
- scriptTypes.forEach((scriptType) => {
86
- index.forEach((index) => {
87
- scope.forEach((scope) => {
88
- runTest(scriptType, index, scope);
85
+ describe("fixedScript to descriptor", function () {
86
+ scriptTypes.forEach((scriptType) => {
87
+ index.forEach((index) => {
88
+ scope.forEach((scope) => {
89
+ runTest(scriptType, index, scope);
90
+ });
89
91
  });
90
92
  });
91
93
  });
@@ -4,9 +4,13 @@ export async function getFixture(path, defaultValue) {
4
4
  return JSON.parse(await fs.readFile(path, "utf8"));
5
5
  }
6
6
  catch (e) {
7
- if (e.code === "ENOENT") {
7
+ if (typeof e === "object" &&
8
+ e !== null &&
9
+ "code" in e &&
10
+ e.code === "ENOENT") {
8
11
  await fs.writeFile(path, JSON.stringify(defaultValue, null, 2));
9
12
  throw new Error(`Fixture not found at ${path}, created a new one`);
10
13
  }
14
+ throw e;
11
15
  }
12
16
  }
@@ -1,9 +1,6 @@
1
1
  import * as assert from "node:assert";
2
2
  import * as utxolib from "@bitgo/utxo-lib";
3
3
  import { Psbt } from "../js/index.js";
4
- function toAddress(descriptor, network) {
5
- utxolib.address.fromOutputScript(Buffer.from(descriptor.scriptPubkey()), network);
6
- }
7
4
  export function toWrappedPsbt(psbt) {
8
5
  if (psbt instanceof utxolib.bitgo.UtxoPsbt || psbt instanceof utxolib.Psbt) {
9
6
  psbt = psbt.toBuffer();
@@ -69,7 +66,7 @@ function normalizeBip32Derivation(v) {
69
66
  if (!Array.isArray(v)) {
70
67
  throw new Error("Expected bip32Derivation to be an array");
71
68
  }
72
- return [...v]
69
+ return v
73
70
  .map((e) => {
74
71
  let { path } = e;
75
72
  if (path.startsWith("m/")) {
@@ -65,7 +65,7 @@ function describeUpdateInputWithDescriptor(psbt, scriptType) {
65
65
  });
66
66
  describe("psbt signWithXprv", function () {
67
67
  function signWithKey(keys, { checkFinalized = false } = {}) {
68
- it(`signs the input with keys ${keys}`, function () {
68
+ it(`signs the input with keys ${keys.join(", ")}`, function () {
69
69
  const psbt = getWrappedPsbtWithDescriptorInfo();
70
70
  keys.forEach((keyName) => {
71
71
  const key = keyName === "unrelated" ? getKey(keyName) : rootWalletKeys[keyName];
@@ -93,22 +93,24 @@ function describeUpdateInputWithDescriptor(psbt, scriptType) {
93
93
  signWithKey(["user", "bitgo"], { checkFinalized: true });
94
94
  });
95
95
  }
96
- fixtures.forEach(({ psbt, scriptType, stage }) => {
97
- describe(`PSBT fixture ${scriptType} ${stage}`, function () {
98
- let buf;
99
- let wrappedPsbt;
100
- before(function () {
101
- buf = psbt.toBuffer();
102
- wrappedPsbt = toWrappedPsbt(buf);
103
- });
104
- it("should map to same hex", function () {
105
- assertEqualBuffer(buf, wrappedPsbt.serialize());
106
- });
107
- it("should round-trip utxolib -> ms -> utxolib", function () {
108
- assertEqualBuffer(buf, toUtxoPsbt(wrappedPsbt).toBuffer());
96
+ describe("PSBT fixture", function () {
97
+ fixtures.forEach(({ psbt, scriptType, stage }) => {
98
+ describe(`PSBT fixture ${scriptType} ${stage}`, function () {
99
+ let buf;
100
+ let wrappedPsbt;
101
+ before(function () {
102
+ buf = psbt.toBuffer();
103
+ wrappedPsbt = toWrappedPsbt(buf);
104
+ });
105
+ it("should map to same hex", function () {
106
+ assertEqualBuffer(buf, wrappedPsbt.serialize());
107
+ });
108
+ it("should round-trip utxolib -> ms -> utxolib", function () {
109
+ assertEqualBuffer(buf, toUtxoPsbt(wrappedPsbt).toBuffer());
110
+ });
111
+ if (stage === "bare") {
112
+ describeUpdateInputWithDescriptor(psbt, scriptType);
113
+ }
109
114
  });
110
- if (stage === "bare") {
111
- describeUpdateInputWithDescriptor(psbt, scriptType);
112
- }
113
115
  });
114
116
  });
@@ -14,7 +14,7 @@ export function toPsbtWithPrevOutOnly(psbt) {
14
14
  ...(witnessUtxo ? { witnessUtxo } : { nonWitnessUtxo }),
15
15
  });
16
16
  });
17
- psbt.txOutputs.forEach((output, vout) => {
17
+ psbt.txOutputs.forEach((output) => {
18
18
  psbtCopy.addOutput(output);
19
19
  });
20
20
  return psbtCopy;
@@ -8,9 +8,6 @@ import { toWrappedPsbt } from "./psbt.util.js";
8
8
  function toKeyWithPath(k, path = "*") {
9
9
  return k.neutered().toBase58() + "/" + path;
10
10
  }
11
- function toKeyPlain(k) {
12
- return k.toString("hex");
13
- }
14
11
  function toECPair(k) {
15
12
  assert(k.privateKey);
16
13
  return ECPair.fromPrivateKey(k.privateKey);
@@ -39,8 +36,8 @@ function describeSignDescriptor(name, descriptor, { signBip32 = [], signECPair =
39
36
  [isTaproot ? "Schnorr" : "Ecdsa"]: keys.map((key) => key.publicKey.subarray(isTaproot ? 1 : 0).toString("hex")),
40
37
  };
41
38
  }
42
- signBip32.forEach((signSeq, i) => {
43
- it(`should sign ${signSeq.map((k) => getKeyName(k))} xprv`, function () {
39
+ signBip32.forEach((signSeq) => {
40
+ it(`should sign ${signSeq.map((k) => getKeyName(k)).join(", ")} xprv`, function () {
44
41
  const wrappedPsbt = toWrappedPsbt(psbt);
45
42
  signSeq.forEach((key) => {
46
43
  assert.deepStrictEqual(wrappedPsbt.signWithXprv(key.toBase58()), {
@@ -50,7 +47,7 @@ function describeSignDescriptor(name, descriptor, { signBip32 = [], signECPair =
50
47
  });
51
48
  wrappedPsbt.finalize();
52
49
  });
53
- it(`should sign ${signSeq.map((k) => getKeyName(k))} prv buffer`, function () {
50
+ it(`should sign ${signSeq.map((k) => getKeyName(k)).join(", ")} prv buffer`, function () {
54
51
  const wrappedPsbt = toWrappedPsbt(psbt);
55
52
  signSeq.forEach((key) => {
56
53
  assert.deepStrictEqual(wrappedPsbt.signWithPrv(key.derive(0).privateKey), {
@@ -62,8 +59,8 @@ function describeSignDescriptor(name, descriptor, { signBip32 = [], signECPair =
62
59
  });
63
60
  });
64
61
  });
65
- signECPair.forEach((signSeq, i) => {
66
- it(`should sign ${signSeq.map((k) => getKeyName(k))} ec pair`, function () {
62
+ signECPair.forEach((signSeq) => {
63
+ it(`should sign ${signSeq.map((k) => getKeyName(k)).join(", ")} ec pair`, function () {
67
64
  const wrappedPsbt = toWrappedPsbt(psbt);
68
65
  signSeq.forEach((key) => {
69
66
  assert(key.privateKey);
@@ -10,7 +10,7 @@ export function toDerivedDescriptorWalletOutput(output, descriptor) {
10
10
  const derivedDescriptor = descriptor.atDerivationIndex(output.descriptorIndex);
11
11
  const script = createScriptPubKeyFromDescriptor(derivedDescriptor);
12
12
  if (!script.equals(output.witnessUtxo.script)) {
13
- throw new Error(`Script mismatch: descriptor ${output.descriptorName} ${descriptor.toString()} script=${script}`);
13
+ throw new Error(`Script mismatch: descriptor ${output.descriptorName} ${descriptor.toString()} script=${script.toString("hex")}`);
14
14
  }
15
15
  return {
16
16
  hash: output.hash,
@@ -33,7 +33,7 @@ function updateInputsWithDescriptors(psbt, descriptors) {
33
33
  wrappedPsbt.updateInputWithDescriptor(inputIndex, descriptor);
34
34
  }
35
35
  const unwrappedPsbt = toUtxoPsbt(wrappedPsbt);
36
- for (const inputIndex in psbt.txInputs) {
36
+ for (let inputIndex = 0; inputIndex < psbt.txInputs.length; inputIndex++) {
37
37
  psbt.data.inputs[inputIndex] = unwrappedPsbt.data.inputs[inputIndex];
38
38
  }
39
39
  }
@@ -45,7 +45,7 @@ function updateOutputsWithDescriptors(psbt, descriptors) {
45
45
  }
46
46
  }
47
47
  const unwrappedPsbt = toUtxoPsbt(wrappedPsbt);
48
- for (const outputIndex in psbt.txOutputs) {
48
+ for (let outputIndex = 0; outputIndex < psbt.txOutputs.length; outputIndex++) {
49
49
  psbt.data.outputs[outputIndex] = unwrappedPsbt.data.outputs[outputIndex];
50
50
  }
51
51
  }
@@ -86,7 +86,7 @@ describe("Descriptor fixtures", function () {
86
86
  }
87
87
  assert.strictEqual(descriptorString, fixture.descriptor);
88
88
  });
89
- it("should parse (pkType derivable)", async function () {
89
+ it("should parse (pkType derivable)", function () {
90
90
  const descriptor = Descriptor.fromString(fixture.descriptor, "derivable");
91
91
  if (isDerivable(i)) {
92
92
  assert.doesNotThrow(() => Descriptor.fromString(fixture.descriptor, "derivable").atDerivationIndex(0));
@@ -95,8 +95,9 @@ describe("Descriptor fixtures", function () {
95
95
  }
96
96
  const scriptPubKey = Buffer.from(descriptor.atDerivationIndex(fixture.index ?? 0).scriptPubkey());
97
97
  assert.strictEqual(scriptPubKey.toString("hex"), fixture.script);
98
- if (descriptor.descType() !== "Bare") {
99
- assert.strictEqual(scriptPubKey.length, getScriptPubKeyLength(descriptor.descType()), `Unexpected scriptPubKey length for descriptor ${descriptor.descType()}: ${scriptPubKey.length}`);
98
+ const descType = descriptor.descType();
99
+ if (descType !== "Bare") {
100
+ assert.strictEqual(scriptPubKey.length, getScriptPubKeyLength(descType), `Unexpected scriptPubKey length for descriptor ${descType}: ${scriptPubKey.length}`);
100
101
  }
101
102
  }
102
103
  else {
@@ -106,7 +107,7 @@ describe("Descriptor fixtures", function () {
106
107
  assert.ok(Number.isInteger(descriptor.maxWeightToSatisfy()));
107
108
  assertKnownDescriptorType(descriptor);
108
109
  });
109
- it("can round-trip with formatNode(toWasmNode(.))", async function () {
110
+ it("can round-trip with formatNode(toWasmNode(.))", function () {
110
111
  const ast = fromDescriptor(descriptor);
111
112
  assert.strictEqual(formatNode(ast), removeChecksum(descriptor.toString()));
112
113
  });