@atomicfinance/bitcoin-ddk-provider 4.1.12 → 4.2.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.
@@ -65,169 +65,6 @@ class BitcoinDdkProvider extends provider_1.default {
65
65
  await (0, utils_1.sleep)(10);
66
66
  }
67
67
  }
68
- /**
69
- * Helper function to ensure we have a Buffer object
70
- * Handles cases where Buffer objects have been serialized/deserialized
71
- */
72
- ensureBuffer(bufferLike) {
73
- if (Buffer.isBuffer(bufferLike)) {
74
- return bufferLike;
75
- }
76
- if (bufferLike && bufferLike.type === 'Buffer' && bufferLike.data) {
77
- return Buffer.from(bufferLike.data);
78
- }
79
- return bufferLike;
80
- }
81
- /**
82
- * Detect if signature is in compact format (64 bytes) or DER format
83
- * and convert compact to DER if needed, adding SIGHASH_ALL flag
84
- */
85
- ensureDerSignature(signature) {
86
- // If signature is 64 bytes, it's likely compact format (32-byte r + 32-byte s)
87
- if (signature.length === 64) {
88
- // Convert compact signature to DER format
89
- const r = signature.slice(0, 32);
90
- const s = signature.slice(32, 64);
91
- // Create DER encoding manually
92
- // DER format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S]
93
- // Remove leading zeros from r and s, but keep at least one byte
94
- let rBytes = r;
95
- while (rBytes.length > 1 &&
96
- rBytes[0] === 0x00 &&
97
- (rBytes[1] & 0x80) === 0) {
98
- rBytes = rBytes.slice(1);
99
- }
100
- let sBytes = s;
101
- while (sBytes.length > 1 &&
102
- sBytes[0] === 0x00 &&
103
- (sBytes[1] & 0x80) === 0) {
104
- sBytes = sBytes.slice(1);
105
- }
106
- // Add padding byte if high bit is set (to keep numbers positive)
107
- if ((rBytes[0] & 0x80) !== 0) {
108
- rBytes = Buffer.concat([Buffer.from([0x00]), rBytes]);
109
- }
110
- if ((sBytes[0] & 0x80) !== 0) {
111
- sBytes = Buffer.concat([Buffer.from([0x00]), sBytes]);
112
- }
113
- const totalLength = 2 + rBytes.length + 2 + sBytes.length;
114
- const derSignature = Buffer.concat([
115
- Buffer.from([0x30, totalLength]), // SEQUENCE tag and total length
116
- Buffer.from([0x02, rBytes.length]), // INTEGER tag and R length
117
- rBytes,
118
- Buffer.from([0x02, sBytes.length]), // INTEGER tag and S length
119
- sBytes,
120
- Buffer.from([0x01]), // SIGHASH_ALL flag
121
- ]);
122
- return derSignature;
123
- }
124
- // If it's already DER format, check if it has SIGHASH flag
125
- if (signature.length > 0 && signature[0] === 0x30) {
126
- // Check if it already has a SIGHASH flag (last byte should be 0x01 for SIGHASH_ALL)
127
- if (signature[signature.length - 1] !== 0x01) {
128
- // Add SIGHASH_ALL flag
129
- return Buffer.concat([signature, Buffer.from([0x01])]);
130
- }
131
- return signature;
132
- }
133
- // For other formats, return as-is
134
- return signature;
135
- }
136
- /**
137
- * Detect if signature is in DER format and convert to compact format (64 bytes)
138
- * by extracting r and s values, removing SIGHASH flag if present
139
- */
140
- ensureCompactSignature(signature) {
141
- // If signature is already 64 bytes, it's likely already compact format
142
- if (signature.length === 64) {
143
- return signature;
144
- }
145
- // Check if it's DER format (starts with 0x30)
146
- if (signature.length > 6 && signature[0] === 0x30) {
147
- let derSig = signature;
148
- // Remove SIGHASH flag if present (last byte is typically 0x01 for SIGHASH_ALL)
149
- if (signature[signature.length - 1] === 0x01) {
150
- derSig = signature.slice(0, -1);
151
- }
152
- // Parse DER format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S]
153
- if (derSig[0] !== 0x30) {
154
- throw new Error('Invalid DER signature: missing SEQUENCE tag');
155
- }
156
- const totalLength = derSig[1];
157
- if (derSig.length < totalLength + 2) {
158
- throw new Error('Invalid DER signature: length mismatch');
159
- }
160
- let offset = 2;
161
- // Parse R value
162
- if (derSig[offset] !== 0x02) {
163
- throw new Error('Invalid DER signature: missing INTEGER tag for R');
164
- }
165
- offset++;
166
- const rLength = derSig[offset];
167
- offset++;
168
- if (offset + rLength > derSig.length) {
169
- throw new Error('Invalid DER signature: R length exceeds signature length');
170
- }
171
- let rBytes = derSig.slice(offset, offset + rLength);
172
- offset += rLength;
173
- // Parse S value
174
- if (derSig[offset] !== 0x02) {
175
- throw new Error('Invalid DER signature: missing INTEGER tag for S');
176
- }
177
- offset++;
178
- const sLength = derSig[offset];
179
- offset++;
180
- if (offset + sLength > derSig.length) {
181
- throw new Error('Invalid DER signature: S length exceeds signature length');
182
- }
183
- let sBytes = derSig.slice(offset, offset + sLength);
184
- // Remove leading zero padding from r and s (DER may pad to prevent negative interpretation)
185
- while (rBytes.length > 1 && rBytes[0] === 0x00) {
186
- rBytes = rBytes.slice(1);
187
- }
188
- while (sBytes.length > 1 && sBytes[0] === 0x00) {
189
- sBytes = sBytes.slice(1);
190
- }
191
- // Pad to 32 bytes each (compact format requires exactly 32 bytes for r and s)
192
- while (rBytes.length < 32) {
193
- rBytes = Buffer.concat([Buffer.from([0x00]), rBytes]);
194
- }
195
- while (sBytes.length < 32) {
196
- sBytes = Buffer.concat([Buffer.from([0x00]), sBytes]);
197
- }
198
- if (rBytes.length !== 32 || sBytes.length !== 32) {
199
- throw new Error('Invalid signature values: r or s exceeds 32 bytes');
200
- }
201
- // Combine r and s into 64-byte compact format
202
- return Buffer.concat([rBytes, sBytes]);
203
- }
204
- // For other formats, throw error as we can't convert
205
- throw new Error('Unable to convert signature to compact format: unknown format');
206
- }
207
- /**
208
- * Compute contract ID from fund transaction ID, output index, and temporary contract ID
209
- * Matches the Rust implementation in rust-dlc
210
- */
211
- computeContractId(fundTxId, fundOutputIndex, temporaryContractId) {
212
- if (fundTxId.length !== 32) {
213
- throw new Error('Fund transaction ID must be 32 bytes');
214
- }
215
- if (temporaryContractId.length !== 32) {
216
- throw new Error('Temporary contract ID must be 32 bytes');
217
- }
218
- if (fundOutputIndex > 0xffff) {
219
- throw new Error('Fund output index must fit in 16 bits');
220
- }
221
- const result = Buffer.alloc(32);
222
- // XOR fund_tx_id with temporary_id, with byte order reversal for fund_tx_id
223
- for (let i = 0; i < 32; i++) {
224
- result[i] = fundTxId[31 - i] ^ temporaryContractId[i];
225
- }
226
- // XOR the fund output index into the last two bytes
227
- result[30] ^= (fundOutputIndex >> 8) & 0xff; // High byte
228
- result[31] ^= fundOutputIndex & 0xff; // Low byte
229
- return result;
230
- }
231
68
  /**
232
69
  * Create refund signature using PSBT method instead of DDK
233
70
  */
@@ -238,19 +75,7 @@ class BitcoinDdkProvider extends provider_1.default {
238
75
  if (Number(dlcTxs.refundTx.locktime) !== dlcOffer.refundLocktime) {
239
76
  throw new Error(`Refund transaction locktime ${dlcTxs.refundTx.locktime} does not match expected ${dlcOffer.refundLocktime}`);
240
77
  }
241
- // Create the same funding script as createDlcClose
242
- const fundingPubKeys = Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
243
- ? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
244
- : [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
245
- const p2ms = bitcoinjs_lib_1.payments.p2ms({
246
- m: 2,
247
- pubkeys: fundingPubKeys,
248
- network,
249
- });
250
- const paymentVariant = bitcoinjs_lib_1.payments.p2wsh({
251
- redeem: p2ms,
252
- network,
253
- });
78
+ const paymentVariant = (0, Utils_1.createP2WSHMultisig)(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey, network);
254
79
  // Add the funding input with sequence from refund transaction
255
80
  psbt.addInput({
256
81
  hash: dlcTxs.fundTx.txId.serialize(),
@@ -283,7 +108,7 @@ class BitcoinDdkProvider extends provider_1.default {
283
108
  const partialSig = psbt.data.inputs[0].partialSig[0];
284
109
  const derSignature = partialSig.signature;
285
110
  // Convert DER signature to compact format
286
- return this.ensureCompactSignature(derSignature);
111
+ return (0, Utils_1.ensureCompactSignature)(derSignature);
287
112
  }
288
113
  /**
289
114
  * Find private key for DLC funding pubkey by deriving wallet addresses
@@ -632,8 +457,10 @@ class BitcoinDdkProvider extends provider_1.default {
632
457
  const input = await this.fundingInputToInput(fundingInput, false);
633
458
  return input.toUtxo();
634
459
  }));
635
- // Calculate input amounts
636
- const localInputAmount = localRegularInputs.reduce((prev, cur) => prev + cur.amount.GetSatoshiAmount(), 0);
460
+ // Calculate input amounts (including both regular inputs and DLC inputs)
461
+ const localRegularInputAmount = localRegularInputs.reduce((prev, cur) => prev + cur.amount.GetSatoshiAmount(), 0);
462
+ const localDlcInputAmount = localDlcInputs.reduce((prev, cur) => prev + Number(cur.fundAmount), 0);
463
+ const localInputAmount = localRegularInputAmount + localDlcInputAmount;
637
464
  const remoteInputAmount = remoteInputs.reduce((prev, cur) => prev + cur.amount.GetSatoshiAmount(), 0);
638
465
  let payouts = [];
639
466
  let messagesList = [];
@@ -688,7 +515,7 @@ class BitcoinDdkProvider extends provider_1.default {
688
515
  let dlcTxs;
689
516
  if (hasDlcInputs) {
690
517
  // Use spliced DLC transactions when DLC inputs are present
691
- dlcTxs = await this._ddk.createSplicedDlcTransactions(outcomes, localParams, remoteParams, dlcOffer.refundLocktime, BigInt(dlcOffer.feeRatePerVb), 0, dlcOffer.cetLocktime, dlcOffer.fundOutputSerialId);
518
+ dlcTxs = await this._ddk.createSplicedDlcTransactions(outcomes, localParams, remoteParams, dlcOffer.refundLocktime, BigInt(dlcOffer.feeRatePerVb), 0, dlcOffer.cetLocktime, BigInt(dlcOffer.fundOutputSerialId));
692
519
  }
693
520
  else {
694
521
  // Use regular DLC transactions when no DLC inputs
@@ -860,16 +687,8 @@ class BitcoinDdkProvider extends provider_1.default {
860
687
  async CreateCetAdaptorAndRefundSigs(dlcOffer, dlcAccept, dlcTxs, messagesList, isOfferer) {
861
688
  const network = await this.getConnectedNetwork();
862
689
  const cetsHex = dlcTxs.cets.map((cet) => cet.serialize().toString('hex'));
863
- // Create the correct P2WSH multisig funding script
864
- const fundingPubKeys = Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
865
- ? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
866
- : [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
867
- const p2ms = bitcoinjs_lib_1.payments.p2ms({
868
- m: 2,
869
- pubkeys: fundingPubKeys,
870
- network,
871
- });
872
690
  // We need the redeem script (multisig), not the P2WSH output
691
+ const p2ms = (0, Utils_1.createP2MSMultisig)(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey, network);
873
692
  const fundingSPK = p2ms.output;
874
693
  // For finding the private key, we still need the individual P2WPKH address
875
694
  const individualFundingSPK = bitcoin_1.Script.p2wpkhLock((0, crypto_1.hash160)(isOfferer ? dlcOffer.fundingPubkey : dlcAccept.fundingPubkey))
@@ -980,14 +799,7 @@ class BitcoinDdkProvider extends provider_1.default {
980
799
  });
981
800
  // Create the correct P2WSH multisig funding script for verification
982
801
  const network = await this.getConnectedNetwork();
983
- const verifyFundingPubKeys = Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
984
- ? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
985
- : [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
986
- const verifyP2ms = bitcoinjs_lib_1.payments.p2ms({
987
- m: 2,
988
- pubkeys: verifyFundingPubKeys,
989
- network,
990
- });
802
+ const verifyP2ms = (0, Utils_1.createP2MSMultisig)(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey, network);
991
803
  // We need the redeem script (multisig), not the P2WSH output
992
804
  const fundingSPK = verifyP2ms.output;
993
805
  const ddkOracleInfo = {
@@ -1070,17 +882,14 @@ class BitcoinDdkProvider extends provider_1.default {
1070
882
  }
1071
883
  }
1072
884
  }
1073
- async CreateFundingSigsAlt(dlcOffer, dlcAccept, dlcTxs, isOfferer) {
885
+ async CreateFundingSigs(dlcOffer, dlcAccept, dlcTxs, isOfferer) {
1074
886
  const transaction = bitcoinjs_lib_1.Transaction.fromBuffer(Buffer.from(dlcTxs.fundTx.serialize()));
1075
887
  const network = await this.getConnectedNetwork();
1076
888
  const psbt = new bitcoinjs_lib_1.Psbt({ network });
1077
- // Combine all funding inputs from both parties
1078
- const allFundingInputs = [
889
+ const allFundingInputs = (0, Utils_1.sortFundingInputsBySerialId)([
1079
890
  ...dlcOffer.fundingInputs,
1080
891
  ...dlcAccept.fundingInputs,
1081
- ];
1082
- // Sort by inputSerialId to reconstruct proper transaction order
1083
- allFundingInputs.sort((a, b) => Number(a.inputSerialId - b.inputSerialId));
892
+ ]);
1084
893
  // Add all inputs to PSBT with proper witnessUtxo
1085
894
  for (const fundingInput of allFundingInputs) {
1086
895
  const prevOut = fundingInput.prevTx.outputs[fundingInput.prevTxVout];
@@ -1137,9 +946,26 @@ class BitcoinDdkProvider extends provider_1.default {
1137
946
  const keyPair = ECPair.fromPrivateKey(Buffer.from(privKey, 'hex'));
1138
947
  // Check if this is a DLC input (2-of-2 multisig from previous DLC)
1139
948
  if (fundingInput.dlcInput) {
1140
- // For DLC inputs, we'll need to handle 2-of-2 multisig signing differently
1141
- // For now, throw an error as this requires implementing multisig support
1142
- throw new Error('DLC input signing not yet implemented in CreateFundingSigsAlt');
949
+ const paymentVariant = (0, Utils_1.createP2WSHMultisig)(fundingInput.dlcInput.localFundPubkey, fundingInput.dlcInput.remoteFundPubkey, network);
950
+ const redeemScript = paymentVariant.redeem.output;
951
+ // Find the correct private key for this DLC input
952
+ const dlcPrivKey = await this.findDlcFundingPrivateKey(fundingInput.dlcInput.localFundPubkey.toString('hex'), fundingInput.dlcInput.remoteFundPubkey.toString('hex'));
953
+ const keyPair = ECPair.fromPrivateKey(Buffer.from(dlcPrivKey, 'hex'));
954
+ // Update the input with the witnessScript before signing
955
+ psbt.updateInput(inputIndex, {
956
+ witnessScript: redeemScript,
957
+ });
958
+ psbt.signInput(inputIndex, keyPair);
959
+ const inputData = psbt.data.inputs[inputIndex];
960
+ const partialSigs = inputData.partialSig;
961
+ if (!partialSigs || partialSigs.length === 0) {
962
+ throw new Error(`No signatures found for input ${inputIndex} after signing`);
963
+ }
964
+ const sigWitness = new messaging_1.ScriptWitnessV0();
965
+ sigWitness.witness = (0, Utils_1.ensureBuffer)(partialSigs[0].signature);
966
+ const pubKeyWitness = new messaging_1.ScriptWitnessV0();
967
+ pubKeyWitness.witness = keyPair.publicKey;
968
+ witnessElements.push([sigWitness, pubKeyWitness]);
1143
969
  }
1144
970
  else {
1145
971
  // For P2WPKH inputs, use PSBT signing
@@ -1152,7 +978,7 @@ class BitcoinDdkProvider extends provider_1.default {
1152
978
  }
1153
979
  // For P2WPKH, create witness manually: [signature, publicKey]
1154
980
  const sigWitness = new messaging_1.ScriptWitnessV0();
1155
- sigWitness.witness = this.ensureBuffer(partialSigs[0].signature);
981
+ sigWitness.witness = (0, Utils_1.ensureBuffer)(partialSigs[0].signature);
1156
982
  const pubKeyWitness = new messaging_1.ScriptWitnessV0();
1157
983
  pubKeyWitness.witness = keyPair.publicKey;
1158
984
  witnessElements.push([sigWitness, pubKeyWitness]);
@@ -1164,7 +990,7 @@ class BitcoinDdkProvider extends provider_1.default {
1164
990
  fundingSignatures.witnessElements = witnessElements;
1165
991
  return fundingSignatures;
1166
992
  }
1167
- async VerifyFundingSigsAlt(dlcOffer, dlcAccept, dlcSign, dlcTxs, isOfferer) {
993
+ async VerifyFundingSigs(dlcOffer, dlcAccept, dlcSign, dlcTxs, isOfferer) {
1168
994
  const network = await this.getConnectedNetwork();
1169
995
  const transaction = bitcoinjs_lib_1.Transaction.fromBuffer(Buffer.from(dlcTxs.fundTx.serialize()));
1170
996
  // Get the party whose signatures we're verifying
@@ -1173,12 +999,10 @@ class BitcoinDdkProvider extends provider_1.default {
1173
999
  const signingPartyInputs = isOfferer
1174
1000
  ? dlcAccept.fundingInputs
1175
1001
  : dlcOffer.fundingInputs;
1176
- // Combine all inputs to get the correct ordering
1177
- const allFundingInputs = [
1002
+ const allFundingInputs = (0, Utils_1.sortFundingInputsBySerialId)([
1178
1003
  ...dlcOffer.fundingInputs,
1179
1004
  ...dlcAccept.fundingInputs,
1180
- ];
1181
- allFundingInputs.sort((a, b) => Number(a.inputSerialId - b.inputSerialId));
1005
+ ]);
1182
1006
  // Compare transaction IDs
1183
1007
  const dlcFundTxId = dlcTxs.fundTx.txId.toString();
1184
1008
  const psbtBuilderTxId = transaction.getId();
@@ -1188,10 +1012,16 @@ class BitcoinDdkProvider extends provider_1.default {
1188
1012
  // Add all inputs (needed for proper sighash calculation)
1189
1013
  for (const input of allFundingInputs) {
1190
1014
  const prevOutput = input.prevTx.outputs[input.prevTxVout];
1015
+ // Use sequence from the original transaction to ensure consistency with CreateFundingTx
1016
+ const originalInput = transaction.ins.find((txInput) => txInput.hash.reverse().toString('hex') ===
1017
+ input.prevTx.txId.toString() && txInput.index === input.prevTxVout);
1018
+ const sequenceValue = originalInput
1019
+ ? originalInput.sequence
1020
+ : Number(input.sequence);
1191
1021
  psbt.addInput({
1192
1022
  hash: input.prevTx.txId.toString(),
1193
1023
  index: input.prevTxVout,
1194
- sequence: 0,
1024
+ sequence: sequenceValue,
1195
1025
  witnessUtxo: {
1196
1026
  script: Buffer.from(prevOutput.scriptPubKey.serialize().subarray(1)),
1197
1027
  value: Number(prevOutput.value.sats),
@@ -1212,8 +1042,40 @@ class BitcoinDdkProvider extends provider_1.default {
1212
1042
  // Add the funding signatures to the PSBT as partial signatures
1213
1043
  let witnessIndex = 0;
1214
1044
  for (const fundingInput of signingPartyInputs) {
1215
- // Skip DLC inputs for now (same as CreateFundingSigsAlt)
1045
+ // Handle DLC inputs with multisig verification
1216
1046
  if (fundingInput.dlcInput) {
1047
+ const dlcInput = fundingInput.dlcInput;
1048
+ const paymentVariant = (0, Utils_1.createP2WSHMultisig)(dlcInput.localFundPubkey, dlcInput.remoteFundPubkey, network);
1049
+ // Find this input's index in the sorted transaction
1050
+ const inputIndex = allFundingInputs.findIndex((input) => input.prevTx.txId.toString() ===
1051
+ fundingInput.prevTx.txId.toString() &&
1052
+ input.prevTxVout === fundingInput.prevTxVout);
1053
+ if (inputIndex === -1) {
1054
+ throw new Error(`DLC input not found in transaction: ${fundingInput.prevTx.txId.toString()}:${fundingInput.prevTxVout}`);
1055
+ }
1056
+ // Update the input with the witnessScript for verification
1057
+ psbt.updateInput(inputIndex, {
1058
+ witnessScript: paymentVariant.redeem.output,
1059
+ });
1060
+ // Add the DLC signature from witness elements if available
1061
+ if (witnessIndex < dlcSign.fundingSignatures.witnessElements.length) {
1062
+ const witnessElement = dlcSign.fundingSignatures.witnessElements[witnessIndex];
1063
+ const signature = witnessElement[0].witness;
1064
+ const publicKey = witnessElement[1].witness;
1065
+ psbt.updateInput(inputIndex, {
1066
+ partialSig: [
1067
+ {
1068
+ pubkey: publicKey,
1069
+ signature: (0, Utils_1.ensureDerSignature)(signature),
1070
+ },
1071
+ ],
1072
+ });
1073
+ // Verify the signature for this DLC input
1074
+ psbt.validateSignaturesOfInput(inputIndex, (pubkey, msghash, signature) => {
1075
+ return ecc.verify(msghash, pubkey, signature);
1076
+ });
1077
+ witnessIndex++;
1078
+ }
1217
1079
  continue;
1218
1080
  }
1219
1081
  if (witnessIndex >= dlcSign.fundingSignatures.witnessElements.length) {
@@ -1233,7 +1095,7 @@ class BitcoinDdkProvider extends provider_1.default {
1233
1095
  partialSig: [
1234
1096
  {
1235
1097
  pubkey: publicKey,
1236
- signature: this.ensureDerSignature(signature),
1098
+ signature: (0, Utils_1.ensureDerSignature)(signature),
1237
1099
  },
1238
1100
  ],
1239
1101
  });
@@ -1243,6 +1105,67 @@ class BitcoinDdkProvider extends provider_1.default {
1243
1105
  witnessIndex++;
1244
1106
  }
1245
1107
  }
1108
+ /**
1109
+ * Get sighash for funding transaction inputs
1110
+ * @param dlcOffer DLC Offer
1111
+ * @param dlcAccept DLC Accept
1112
+ * @param dlcTxs DLC Transactions
1113
+ * @param inputIndex Index of the input to get sighash for (optional, if not provided returns all)
1114
+ * @returns Array of sighashes for each input or single sighash if inputIndex specified
1115
+ */
1116
+ async getFundingTransactionSighash(dlcOffer, dlcAccept, dlcTxs, inputIndex) {
1117
+ // Use the detailed function to ensure consistency
1118
+ const details = await this.getFundingTransactionSighashDetails(dlcOffer, dlcAccept, dlcTxs);
1119
+ if (inputIndex !== undefined) {
1120
+ // Return sighash for specific input
1121
+ if (inputIndex >= details.length) {
1122
+ throw new Error(`Input index ${inputIndex} out of range. Total inputs: ${details.length}`);
1123
+ }
1124
+ return details[inputIndex].sighash;
1125
+ }
1126
+ else {
1127
+ // Return sighashes for all inputs
1128
+ return details.map((detail) => detail.sighash);
1129
+ }
1130
+ }
1131
+ /**
1132
+ * Get detailed sighash information for funding transaction (useful for debugging)
1133
+ * @param dlcOffer DLC Offer
1134
+ * @param dlcAccept DLC Accept
1135
+ * @param dlcTxs DLC Transactions
1136
+ * @returns Detailed sighash information for each input
1137
+ */
1138
+ async getFundingTransactionSighashDetails(dlcOffer, dlcAccept, dlcTxs) {
1139
+ const transaction = bitcoinjs_lib_1.Transaction.fromBuffer(Buffer.from(dlcTxs.fundTx.serialize()));
1140
+ const allFundingInputs = (0, Utils_1.sortFundingInputsBySerialId)([
1141
+ ...dlcOffer.fundingInputs,
1142
+ ...dlcAccept.fundingInputs,
1143
+ ]);
1144
+ // Calculate detailed sighash information
1145
+ const details = [];
1146
+ for (let i = 0; i < allFundingInputs.length; i++) {
1147
+ const input = allFundingInputs[i];
1148
+ const prevOutput = input.prevTx.outputs[input.prevTxVout];
1149
+ const originalInput = transaction.ins.find((txInput) => txInput.hash.reverse().toString('hex') ===
1150
+ input.prevTx.txId.toString() && txInput.index === input.prevTxVout);
1151
+ const sequenceValue = originalInput
1152
+ ? originalInput.sequence
1153
+ : Number(input.sequence);
1154
+ const script = Buffer.from(prevOutput.scriptPubKey.serialize().subarray(1));
1155
+ const value = Number(prevOutput.value.sats);
1156
+ const sighash = transaction.hashForWitnessV0(i, script, value, bitcoinjs_lib_1.Transaction.SIGHASH_ALL);
1157
+ details.push({
1158
+ inputIndex: i,
1159
+ txid: input.prevTx.txId.toString(),
1160
+ vout: input.prevTxVout,
1161
+ sequence: sequenceValue,
1162
+ scriptPubKey: script.toString('hex'),
1163
+ value: value,
1164
+ sighash: sighash.toString('hex'),
1165
+ });
1166
+ }
1167
+ return details;
1168
+ }
1246
1169
  async VerifyRefundSignatureAlt(dlcOffer, dlcAccept, dlcSign, dlcTxs, isOfferer) {
1247
1170
  const network = await this.getConnectedNetwork();
1248
1171
  // Get the refund signature we need to verify
@@ -1252,7 +1175,7 @@ class BitcoinDdkProvider extends provider_1.default {
1252
1175
  ? dlcAccept.refundSignature
1253
1176
  : dlcSign.refundSignature;
1254
1177
  // Ensure signature is in DER format (convert from compact if needed)
1255
- const refundSignature = this.ensureDerSignature(rawRefundSignature);
1178
+ const refundSignature = (0, Utils_1.ensureDerSignature)(rawRefundSignature);
1256
1179
  const signingPubkey = isOfferer
1257
1180
  ? dlcAccept.fundingPubkey
1258
1181
  : dlcOffer.fundingPubkey;
@@ -1262,19 +1185,7 @@ class BitcoinDdkProvider extends provider_1.default {
1262
1185
  }
1263
1186
  // Create a PSBT for the refund transaction verification using same approach as createDlcClose
1264
1187
  const psbt = new bitcoinjs_lib_1.Psbt({ network });
1265
- // Create the same funding script as createDlcClose
1266
- const fundingPubKeys = Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
1267
- ? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
1268
- : [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
1269
- const p2ms = bitcoinjs_lib_1.payments.p2ms({
1270
- m: 2,
1271
- pubkeys: fundingPubKeys,
1272
- network,
1273
- });
1274
- const paymentVariant = bitcoinjs_lib_1.payments.p2wsh({
1275
- redeem: p2ms,
1276
- network,
1277
- });
1188
+ const paymentVariant = (0, Utils_1.createP2WSHMultisig)(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey, network);
1278
1189
  // Add the funding input with sequence from refund transaction
1279
1190
  psbt.addInput({
1280
1191
  hash: dlcTxs.fundTx.txId.serialize(),
@@ -1311,55 +1222,32 @@ class BitcoinDdkProvider extends provider_1.default {
1311
1222
  }
1312
1223
  CreateFundingScript(dlcOffer, dlcAccept) {
1313
1224
  const network = this.getBitcoinJsNetwork();
1314
- // Sort funding pubkeys in lexicographical order as per DLC spec
1315
- const fundingPubKeys = Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
1316
- ? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
1317
- : [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
1318
- // Create 2-of-2 multisig script
1319
- const p2ms = bitcoinjs_lib_1.payments.p2ms({
1320
- m: 2,
1321
- pubkeys: fundingPubKeys,
1322
- network,
1323
- });
1324
- const paymentVariant = bitcoinjs_lib_1.payments.p2wsh({
1325
- redeem: p2ms,
1326
- network,
1327
- });
1225
+ const paymentVariant = (0, Utils_1.createP2WSHMultisig)(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey, network);
1328
1226
  return paymentVariant.redeem.output;
1329
1227
  }
1330
1228
  async CreateFundingTx(dlcOffer, dlcAccept, dlcSign, dlcTxs, fundingSignatures) {
1331
1229
  const network = await this.getConnectedNetwork();
1332
1230
  const psbt = new bitcoinjs_lib_1.Psbt({ network });
1333
- // Combine and sort all funding inputs by serial ID (same as CreateFundingSigsAlt)
1334
- const allFundingInputs = [
1231
+ const allFundingInputs = (0, Utils_1.sortFundingInputsBySerialId)([
1335
1232
  ...dlcOffer.fundingInputs,
1336
1233
  ...dlcAccept.fundingInputs,
1337
- ];
1338
- allFundingInputs.sort((a, b) => Number(a.inputSerialId - b.inputSerialId));
1234
+ ]);
1339
1235
  // Create a map of input txid:vout to witness elements
1340
1236
  const witnessMap = new Map();
1341
- // Map witness elements correctly - CreateFundingSigsAlt only creates witness elements
1237
+ // Map witness elements correctly - CreateFundingSigs only creates witness elements
1342
1238
  // for the party's own inputs, so we need to map them correctly
1343
- // For dlcSign (offerer's signatures), map to offerer's inputs only
1239
+ // For dlcSign (offerer's signatures), map to offerer's inputs (including DLC inputs)
1344
1240
  let offererWitnessIndex = 0;
1345
1241
  dlcOffer.fundingInputs.forEach((fundingInput) => {
1346
- // Skip DLC inputs for now as in CreateFundingSigsAlt
1347
- if (fundingInput.dlcInput) {
1348
- return;
1349
- }
1350
1242
  if (offererWitnessIndex < dlcSign.fundingSignatures.witnessElements.length) {
1351
1243
  const key = `${fundingInput.prevTx.txId.toString()}:${fundingInput.prevTxVout}`;
1352
1244
  witnessMap.set(key, dlcSign.fundingSignatures.witnessElements[offererWitnessIndex]);
1353
1245
  offererWitnessIndex++;
1354
1246
  }
1355
1247
  });
1356
- // For fundingSignatures (accepter's signatures), map to accepter's inputs only
1248
+ // For fundingSignatures (accepter's signatures), map to accepter's inputs (including DLC inputs)
1357
1249
  let accepterWitnessIndex = 0;
1358
1250
  dlcAccept.fundingInputs.forEach((fundingInput) => {
1359
- // Skip DLC inputs for now as in CreateFundingSigsAlt
1360
- if (fundingInput.dlcInput) {
1361
- return;
1362
- }
1363
1251
  if (accepterWitnessIndex < fundingSignatures.witnessElements.length) {
1364
1252
  const key = `${fundingInput.prevTx.txId.toString()}:${fundingInput.prevTxVout}`;
1365
1253
  witnessMap.set(key, fundingSignatures.witnessElements[accepterWitnessIndex]);
@@ -1370,12 +1258,12 @@ class BitcoinDdkProvider extends provider_1.default {
1370
1258
  const originalTransaction = bitcoinjs_lib_1.Transaction.fromBuffer(Buffer.from(dlcTxs.fundTx.serialize()));
1371
1259
  for (const fundingInput of allFundingInputs) {
1372
1260
  const prevOut = fundingInput.prevTx.outputs[fundingInput.prevTxVout];
1373
- // Use same script handling as CreateFundingSigsAlt
1261
+ // Use same script handling as CreateFundingSigs
1374
1262
  const witnessUtxo = {
1375
1263
  script: Buffer.from(prevOut.scriptPubKey.serialize().subarray(1)),
1376
1264
  value: Number(prevOut.value.sats),
1377
1265
  };
1378
- // Use sequence from the original transaction (same as CreateFundingSigsAlt)
1266
+ // Use sequence from the original transaction (same as CreateFundingSigs)
1379
1267
  const originalInput = originalTransaction.ins.find((input) => input.hash.reverse().toString('hex') ===
1380
1268
  fundingInput.prevTx.txId.toString() &&
1381
1269
  input.index === fundingInput.prevTxVout);
@@ -1402,12 +1290,57 @@ class BitcoinDdkProvider extends provider_1.default {
1402
1290
  const inputKey = `${fundingInput.prevTx.txId.toString()}:${fundingInput.prevTxVout}`;
1403
1291
  const witnessElements = witnessMap.get(inputKey);
1404
1292
  if (witnessElements && witnessElements.length === 2) {
1405
- // Skip DLC inputs for now as requested
1293
+ // Handle DLC inputs with multisig finalization
1406
1294
  if (fundingInput.dlcInput) {
1295
+ const dlcInput = fundingInput.dlcInput;
1296
+ // Create the multisig script for finalization
1297
+ // Use lexicographic ordering to match DDK library behavior
1298
+ const orderedPubkeys = (0, Utils_1.orderPubkeysLexicographically)(dlcInput.localFundPubkey, dlcInput.remoteFundPubkey);
1299
+ const paymentVariant = (0, Utils_1.createP2WSHMultisigFromOrdered)(orderedPubkeys, network);
1300
+ // Update the input with the witnessScript
1301
+ psbt.updateInput(inputIndex, {
1302
+ witnessScript: paymentVariant.redeem.output,
1303
+ });
1304
+ // Sign this DLC input ourselves
1305
+ const dlcPrivKey = await this.findDlcFundingPrivateKey(dlcInput.localFundPubkey.toString('hex'), dlcInput.remoteFundPubkey.toString('hex'));
1306
+ const keyPair = ECPair.fromPrivateKey(Buffer.from(dlcPrivKey, 'hex'));
1307
+ psbt.signInput(inputIndex, keyPair);
1308
+ // Get our signature and determine which pubkey it corresponds to
1309
+ const ourPartialSig = psbt.data.inputs[inputIndex].partialSig[0];
1310
+ const ourSig = (0, Utils_1.ensureDerSignature)(ourPartialSig.signature);
1311
+ const ourPubkey = ourPartialSig.pubkey;
1312
+ // Get the other party's signature from witnessElements
1313
+ const otherPartySig = (0, Utils_1.ensureDerSignature)(witnessElements[0].witness);
1314
+ // Order signatures according to the lexicographic pubkey order used in the multisig script
1315
+ let sig1, sig2;
1316
+ if (Buffer.compare(ourPubkey, orderedPubkeys[0]) === 0) {
1317
+ // Our signature corresponds to the first pubkey in lexicographic order
1318
+ sig1 = ourSig;
1319
+ sig2 = otherPartySig;
1320
+ }
1321
+ else {
1322
+ // Our signature corresponds to the second pubkey in lexicographic order
1323
+ sig1 = otherPartySig;
1324
+ sig2 = ourSig;
1325
+ }
1326
+ // Finalize with proper multisig witness: [OP_0, sig1, sig2, redeemScript]
1327
+ psbt.finalizeInput(inputIndex, () => ({
1328
+ finalScriptSig: Buffer.alloc(0),
1329
+ finalScriptWitness: Buffer.concat([
1330
+ Buffer.from([0x04]), // witness stack count
1331
+ Buffer.from([0x00]), // OP_0 (Bitcoin multisig bug)
1332
+ Buffer.from([sig1.length]),
1333
+ sig1,
1334
+ Buffer.from([sig2.length]),
1335
+ sig2,
1336
+ Buffer.from([paymentVariant.redeem.output.length]),
1337
+ paymentVariant.redeem.output,
1338
+ ]),
1339
+ }));
1407
1340
  continue;
1408
1341
  }
1409
1342
  // For P2WPKH inputs, finalize the input with witness data
1410
- const signature = this.ensureDerSignature(witnessElements[0].witness);
1343
+ const signature = (0, Utils_1.ensureDerSignature)(witnessElements[0].witness);
1411
1344
  const publicKey = witnessElements[1].witness;
1412
1345
  // Try a simpler approach - let bitcoinjs-lib handle witness construction
1413
1346
  psbt.finalizeInput(inputIndex, () => ({
@@ -1468,8 +1401,7 @@ Payout Group found but incorrect group index');
1468
1401
  Payout Group not found');
1469
1402
  return { index: payoutIndexOffset + index, groupLength };
1470
1403
  }
1471
- async FindOutcomeIndexFromHyperbolaPayoutCurvePiece(_dlcOffer, contractDescriptor, contractOraclePairIndex, hyperbolaPayoutCurvePiece, oracleAttestation, outcome) {
1472
- const { dlcOffer } = (0, Utils_1.checkTypes)({ _dlcOffer });
1404
+ async FindOutcomeIndexFromHyperbolaPayoutCurvePiece(dlcOffer, contractDescriptor, contractOraclePairIndex, hyperbolaPayoutCurvePiece, oracleAttestation, outcome) {
1473
1405
  const hyperbolaCurve = core_2.HyperbolaPayoutCurve.fromPayoutCurvePiece(hyperbolaPayoutCurvePiece);
1474
1406
  const clampBN = (val) => bignumber_js_1.default.max(0, bignumber_js_1.default.min(val, dlcOffer.contractInfo.totalCollateral.toString()));
1475
1407
  const payout = clampBN(hyperbolaCurve.getPayout(outcome));
@@ -1767,18 +1699,8 @@ Payout Group not found even with brute force search');
1767
1699
  _dlcTxs,
1768
1700
  });
1769
1701
  const network = await this.getConnectedNetwork();
1770
- const fundingPubKeys = Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
1771
- ? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
1772
- : [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
1773
- const p2ms = bitcoinjs_lib_1.payments.p2ms({
1774
- m: 2,
1775
- pubkeys: fundingPubKeys,
1776
- network,
1777
- });
1778
- const paymentVariant = bitcoinjs_lib_1.payments.p2wsh({
1779
- redeem: p2ms,
1780
- network,
1781
- });
1702
+ const fundingPubKeys = (0, Utils_1.orderPubkeysLexicographically)(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey);
1703
+ const paymentVariant = (0, Utils_1.createP2WSHMultisigFromOrdered)(fundingPubKeys, network);
1782
1704
  const sigHashRequestPromises = [];
1783
1705
  const sigHashes = [];
1784
1706
  for (let i = 0; i < rawCloseTxs.length; i++) {
@@ -1837,18 +1759,8 @@ Payout Group not found even with brute force search');
1837
1759
  });
1838
1760
  const dlcCloses = _dlcCloses.map((_dlcClose) => (0, Utils_1.checkTypes)({ _dlcClose }).dlcClose);
1839
1761
  const network = await this.getConnectedNetwork();
1840
- const fundingPubKeys = Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
1841
- ? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
1842
- : [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
1843
- const p2ms = bitcoinjs_lib_1.payments.p2ms({
1844
- m: 2,
1845
- pubkeys: fundingPubKeys,
1846
- network,
1847
- });
1848
- const paymentVariant = bitcoinjs_lib_1.payments.p2wsh({
1849
- redeem: p2ms,
1850
- network,
1851
- });
1762
+ const fundingPubKeys = (0, Utils_1.orderPubkeysLexicographically)(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey);
1763
+ const paymentVariant = (0, Utils_1.createP2WSHMultisigFromOrdered)(fundingPubKeys, network);
1852
1764
  const pubkey = isOfferer ? dlcAccept.fundingPubkey : dlcOffer.fundingPubkey;
1853
1765
  const sigsValidity = [];
1854
1766
  for (let i = 0; i < rawCloseTxs.length; i++) {
@@ -1959,8 +1871,7 @@ Payout Group not found even with brute force search');
1959
1871
  changeSerialId = initResult.changeSerialId;
1960
1872
  }
1961
1873
  _fundingInputs.forEach((input) => (0, assert_1.default)(input.type === messaging_1.MessageType.FundingInput, 'FundingInput must be V0'));
1962
- const fundingInputs = _fundingInputs.map((input) => input);
1963
- fundingInputs.sort((a, b) => Number(a.inputSerialId) - Number(b.inputSerialId));
1874
+ const fundingInputs = (0, Utils_1.sortFundingInputsBySerialId)(_fundingInputs);
1964
1875
  const fundOutputSerialId = (0, Utils_1.generateSerialId)();
1965
1876
  (0, assert_1.default)(changeSerialId !== fundOutputSerialId, 'changeSerialId cannot equal the fundOutputSerialId');
1966
1877
  dlcOffer.contractFlags = Buffer.from('00', 'hex');
@@ -2074,8 +1985,7 @@ Payout Group not found even with brute force search');
2074
1985
  changeSerialId = initResult.changeSerialId;
2075
1986
  const _fundingInputs = initResult.fundingInputs;
2076
1987
  _fundingInputs.forEach((input) => (0, assert_1.default)(input.type === messaging_1.MessageType.FundingInput, 'FundingInput must be V0'));
2077
- fundingInputs = _fundingInputs.map((input) => input);
2078
- fundingInputs.sort((a, b) => Number(a.inputSerialId) - Number(b.inputSerialId));
1988
+ fundingInputs = (0, Utils_1.sortFundingInputsBySerialId)(_fundingInputs.map((input) => input));
2079
1989
  }
2080
1990
  (0, assert_1.default)(Buffer.compare(dlcOffer.fundingPubkey, fundingPubKey) !== 0, 'DlcOffer and DlcAccept FundingPubKey cannot be the same');
2081
1991
  const dlcAccept = new messaging_1.DlcAccept();
@@ -2113,7 +2023,7 @@ Payout Group not found even with brute force search');
2113
2023
  const { dlcTransactions, messagesList } = await this.createDlcTxs(dlcOffer, dlcAccept);
2114
2024
  const { cetSignatures, refundSignature } = await this.CreateCetAdaptorAndRefundSigs(dlcOffer, dlcAccept, dlcTransactions, messagesList, false);
2115
2025
  const _dlcTransactions = dlcTransactions;
2116
- const contractId = this.computeContractId(_dlcTransactions.fundTx.txId.serialize(), _dlcTransactions.fundTxVout, dlcOffer.temporaryContractId);
2026
+ const contractId = (0, Utils_1.computeContractId)(_dlcTransactions.fundTx.txId.serialize(), _dlcTransactions.fundTxVout, dlcOffer.temporaryContractId);
2117
2027
  _dlcTransactions.contractId = contractId;
2118
2028
  dlcAccept.cetAdaptorSignatures = cetSignatures;
2119
2029
  dlcAccept.refundSignature = refundSignature;
@@ -2133,10 +2043,9 @@ Payout Group not found even with brute force search');
2133
2043
  const { dlcTransactions, messagesList } = await this.createDlcTxs(dlcOffer, dlcAccept);
2134
2044
  await this.VerifyCetAdaptorAndRefundSigs(dlcOffer, dlcAccept, dlcSign, dlcTransactions, messagesList, true);
2135
2045
  const { cetSignatures, refundSignature } = await this.CreateCetAdaptorAndRefundSigs(dlcOffer, dlcAccept, dlcTransactions, messagesList, true);
2136
- const fundingSignatures = await this.CreateFundingSigsAlt(dlcOffer, dlcAccept, dlcTransactions, true);
2046
+ const fundingSignatures = await this.CreateFundingSigs(dlcOffer, dlcAccept, dlcTransactions, true);
2137
2047
  const dlcTxs = dlcTransactions;
2138
- const contractId = this.computeContractId(dlcTxs.fundTx.txId.serialize(), dlcTxs.fundTxVout, dlcOffer.temporaryContractId);
2139
- (0, assert_1.default)(Buffer.compare(contractId, this.computeContractId(dlcTxs.fundTx.txId.serialize(), dlcTxs.fundTxVout, dlcOffer.temporaryContractId)) === 0, 'contractId must be the xor of funding txid, fundingOutputIndex and the tempContractId');
2048
+ const contractId = (0, Utils_1.computeContractId)(dlcTxs.fundTx.txId.serialize(), dlcTxs.fundTxVout, dlcOffer.temporaryContractId);
2140
2049
  dlcTxs.contractId = contractId;
2141
2050
  dlcSign.contractId = contractId;
2142
2051
  dlcSign.cetAdaptorSignatures = cetSignatures;
@@ -2154,6 +2063,8 @@ Payout Group not found even with brute force search');
2154
2063
  */
2155
2064
  async finalizeDlcSign(dlcOffer, dlcAccept, dlcSign, dlcTxs) {
2156
2065
  let messagesList = [];
2066
+ const contractId = (0, Utils_1.computeContractId)(dlcTxs.fundTx.txId.serialize(), dlcTxs.fundTxVout, dlcOffer.temporaryContractId);
2067
+ (0, assert_1.default)(Buffer.compare(contractId, dlcSign.contractId) === 0, `Finalize Dlc Sign Contract ID mismatch: ${contractId.toString('hex')} !== ${dlcSign.contractId.toString('hex')}`);
2157
2068
  if (dlcOffer.contractInfo.type === messaging_1.MessageType.SingleContractInfo &&
2158
2069
  dlcOffer.contractInfo.contractDescriptor.type ===
2159
2070
  messaging_1.MessageType.SingleContractInfo) {
@@ -2168,8 +2079,8 @@ Payout Group not found even with brute force search');
2168
2079
  messagesList = oracleEventMessagesList;
2169
2080
  }
2170
2081
  await this.VerifyCetAdaptorAndRefundSigs(dlcOffer, dlcAccept, dlcSign, dlcTxs, messagesList, false);
2171
- await this.VerifyFundingSigsAlt(dlcOffer, dlcAccept, dlcSign, dlcTxs, false);
2172
- const fundingSignatures = await this.CreateFundingSigsAlt(dlcOffer, dlcAccept, dlcTxs, false);
2082
+ await this.VerifyFundingSigs(dlcOffer, dlcAccept, dlcSign, dlcTxs, false);
2083
+ const fundingSignatures = await this.CreateFundingSigs(dlcOffer, dlcAccept, dlcTxs, false);
2173
2084
  const fundTx = await this.CreateFundingTx(dlcOffer, dlcAccept, dlcSign, dlcTxs, fundingSignatures);
2174
2085
  return fundTx;
2175
2086
  }
@@ -2204,19 +2115,8 @@ Payout Group not found even with brute force search');
2204
2115
  if (Number(dlcTxs.refundTx.locktime) !== dlcOffer.refundLocktime) {
2205
2116
  throw new Error(`Refund transaction locktime ${dlcTxs.refundTx.locktime} does not match expected ${dlcOffer.refundLocktime}`);
2206
2117
  }
2207
- // Create the same funding script as createDlcClose
2208
- const fundingPubKeys = Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
2209
- ? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
2210
- : [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
2211
- const p2ms = bitcoinjs_lib_1.payments.p2ms({
2212
- m: 2,
2213
- pubkeys: fundingPubKeys,
2214
- network,
2215
- });
2216
- const paymentVariant = bitcoinjs_lib_1.payments.p2wsh({
2217
- redeem: p2ms,
2218
- network,
2219
- });
2118
+ const fundingPubKeys = (0, Utils_1.orderPubkeysLexicographically)(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey);
2119
+ const paymentVariant = (0, Utils_1.createP2WSHMultisigFromOrdered)(fundingPubKeys, network);
2220
2120
  // Add the funding input with sequence from refund transaction
2221
2121
  psbt.addInput({
2222
2122
  hash: dlcTxs.fundTx.txId.serialize(),
@@ -2246,14 +2146,14 @@ Payout Group not found even with brute force search');
2246
2146
  // This is the offerer's pubkey, use dlcSign.refundSignature
2247
2147
  partialSigs.push({
2248
2148
  pubkey: pubkey,
2249
- signature: this.ensureDerSignature(dlcSign.refundSignature),
2149
+ signature: (0, Utils_1.ensureDerSignature)(dlcSign.refundSignature),
2250
2150
  });
2251
2151
  }
2252
2152
  else if (Buffer.compare(pubkey, dlcAccept.fundingPubkey) === 0) {
2253
2153
  // This is the accepter's pubkey, use dlcAccept.refundSignature
2254
2154
  partialSigs.push({
2255
2155
  pubkey: pubkey,
2256
- signature: this.ensureDerSignature(dlcAccept.refundSignature),
2156
+ signature: (0, Utils_1.ensureDerSignature)(dlcAccept.refundSignature),
2257
2157
  });
2258
2158
  }
2259
2159
  }
@@ -2303,18 +2203,7 @@ Payout Group not found even with brute force search');
2303
2203
  isOfferer = await this.isOfferer(dlcOffer, dlcAccept);
2304
2204
  const network = await this.getConnectedNetwork();
2305
2205
  const psbt = new bitcoinjs_lib_1.Psbt({ network });
2306
- const fundingPubKeys = Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
2307
- ? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
2308
- : [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
2309
- const p2ms = bitcoinjs_lib_1.payments.p2ms({
2310
- m: 2,
2311
- pubkeys: fundingPubKeys,
2312
- network,
2313
- });
2314
- const paymentVariant = bitcoinjs_lib_1.payments.p2wsh({
2315
- redeem: p2ms,
2316
- network,
2317
- });
2206
+ const paymentVariant = (0, Utils_1.createP2WSHMultisig)(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey, network);
2318
2207
  // Initiate and build PSBT
2319
2208
  let inputs = _inputs;
2320
2209
  if (!_inputs) {
@@ -2414,7 +2303,7 @@ Payout Group not found even with brute force search');
2414
2303
  return ecc.verify(msghash, pubkey, signature);
2415
2304
  });
2416
2305
  // Extract close signature from psbt and decode it to only extract r and s values
2417
- const closeSignature = await bitcoinjs_lib_1.script.signature.decode(this.ensureBuffer(psbt.data.inputs[fundingInputIndex].partialSig[0].signature)).signature;
2306
+ const closeSignature = await bitcoinjs_lib_1.script.signature.decode((0, Utils_1.ensureBuffer)(psbt.data.inputs[fundingInputIndex].partialSig[0].signature)).signature;
2418
2307
  // Extract funding signatures from psbt
2419
2308
  const inputSigs = psbt.data.inputs
2420
2309
  .filter((input) => input !== fundingInputIndex)
@@ -2423,9 +2312,9 @@ Payout Group not found even with brute force search');
2423
2312
  const witnessElements = [];
2424
2313
  for (let i = 0; i < inputSigs.length; i++) {
2425
2314
  const sigWitness = new messaging_1.ScriptWitnessV0();
2426
- sigWitness.witness = this.ensureBuffer(inputSigs[i].signature);
2315
+ sigWitness.witness = (0, Utils_1.ensureBuffer)(inputSigs[i].signature);
2427
2316
  const pubKeyWitness = new messaging_1.ScriptWitnessV0();
2428
- pubKeyWitness.witness = this.ensureBuffer(inputSigs[i].pubkey);
2317
+ pubKeyWitness.witness = (0, Utils_1.ensureBuffer)(inputSigs[i].pubkey);
2429
2318
  witnessElements.push([sigWitness, pubKeyWitness]);
2430
2319
  }
2431
2320
  const fundingSignatures = new messaging_1.FundingSignatures();
@@ -2555,18 +2444,7 @@ Payout Group not found even with brute force search');
2555
2444
  dlcClose.validate();
2556
2445
  const network = await this.getConnectedNetwork();
2557
2446
  const psbt = new bitcoinjs_lib_1.Psbt({ network });
2558
- const fundingPubKeys = Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
2559
- ? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
2560
- : [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
2561
- const p2ms = bitcoinjs_lib_1.payments.p2ms({
2562
- m: 2,
2563
- pubkeys: fundingPubKeys,
2564
- network,
2565
- });
2566
- const paymentVariant = bitcoinjs_lib_1.payments.p2wsh({
2567
- redeem: p2ms,
2568
- network,
2569
- });
2447
+ const paymentVariant = (0, Utils_1.createP2WSHMultisig)(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey, network);
2570
2448
  // Make temporary array to hold all inputs and then sort them
2571
2449
  // this method can be improved later
2572
2450
  const psbtInputs = [];
@@ -2793,15 +2671,7 @@ Payout Group not found even with brute force search');
2793
2671
  : [remotePubkey, localPubkey];
2794
2672
  const network = await this.getConnectedNetwork();
2795
2673
  // Create 2-of-2 multisig payment using deterministic ordering
2796
- const p2ms = bitcoinjs_lib_1.payments.p2ms({
2797
- m: 2,
2798
- pubkeys: orderedPubkeys,
2799
- network,
2800
- });
2801
- const paymentVariant = bitcoinjs_lib_1.payments.p2wsh({
2802
- redeem: p2ms,
2803
- network,
2804
- });
2674
+ const paymentVariant = (0, Utils_1.createP2WSHMultisigFromOrdered)(orderedPubkeys, network);
2805
2675
  const multisigAddress = paymentVariant.address;
2806
2676
  // Verify this matches the actual funding output address
2807
2677
  const actualFundingOutput = tx.outputs[dlcInputInfo.fundVout];