@atomicfinance/bitcoin-ddk-provider 4.1.13 → 4.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +27 -0
- package/dist/BitcoinDdkProvider.d.ts +3 -23
- package/dist/BitcoinDdkProvider.js +154 -355
- package/dist/BitcoinDdkProvider.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/Utils.d.ts +66 -1
- package/dist/utils/Utils.js +255 -0
- package/dist/utils/Utils.js.map +1 -1
- package/lib/BitcoinDdkProvider.ts +280 -456
- package/lib/index.ts +1 -0
- package/lib/utils/Utils.ts +332 -0
- package/package.json +5 -5
|
@@ -85,7 +85,20 @@ import {
|
|
|
85
85
|
import crypto from 'crypto';
|
|
86
86
|
import { ECPairFactory, ECPairInterface } from 'ecpair';
|
|
87
87
|
|
|
88
|
-
import {
|
|
88
|
+
import {
|
|
89
|
+
checkTypes,
|
|
90
|
+
computeContractId,
|
|
91
|
+
createP2MSMultisig,
|
|
92
|
+
createP2WSHMultisig,
|
|
93
|
+
createP2WSHMultisigFromOrdered,
|
|
94
|
+
ensureBuffer,
|
|
95
|
+
ensureCompactSignature,
|
|
96
|
+
ensureDerSignature,
|
|
97
|
+
generateSerialId,
|
|
98
|
+
orderPubkeysLexicographically,
|
|
99
|
+
outputsToPayouts,
|
|
100
|
+
sortFundingInputsBySerialId,
|
|
101
|
+
} from './utils/Utils';
|
|
89
102
|
|
|
90
103
|
const ECPair = ECPairFactory(ecc);
|
|
91
104
|
|
|
@@ -105,220 +118,6 @@ export default class BitcoinDdkProvider extends Provider {
|
|
|
105
118
|
}
|
|
106
119
|
}
|
|
107
120
|
|
|
108
|
-
/**
|
|
109
|
-
* Helper function to ensure we have a Buffer object
|
|
110
|
-
* Handles cases where Buffer objects have been serialized/deserialized
|
|
111
|
-
*/
|
|
112
|
-
private ensureBuffer(
|
|
113
|
-
bufferLike: Buffer | { type: string; data: number[] } | any,
|
|
114
|
-
): Buffer {
|
|
115
|
-
if (Buffer.isBuffer(bufferLike)) {
|
|
116
|
-
return bufferLike;
|
|
117
|
-
}
|
|
118
|
-
if (bufferLike && bufferLike.type === 'Buffer' && bufferLike.data) {
|
|
119
|
-
return Buffer.from(bufferLike.data);
|
|
120
|
-
}
|
|
121
|
-
return bufferLike;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Detect if signature is in compact format (64 bytes) or DER format
|
|
126
|
-
* and convert compact to DER if needed, adding SIGHASH_ALL flag
|
|
127
|
-
*/
|
|
128
|
-
private ensureDerSignature(signature: Buffer): Buffer {
|
|
129
|
-
// If signature is 64 bytes, it's likely compact format (32-byte r + 32-byte s)
|
|
130
|
-
if (signature.length === 64) {
|
|
131
|
-
// Convert compact signature to DER format
|
|
132
|
-
const r = signature.slice(0, 32);
|
|
133
|
-
const s = signature.slice(32, 64);
|
|
134
|
-
|
|
135
|
-
// Create DER encoding manually
|
|
136
|
-
// DER format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S]
|
|
137
|
-
|
|
138
|
-
// Remove leading zeros from r and s, but keep at least one byte
|
|
139
|
-
let rBytes = r;
|
|
140
|
-
while (
|
|
141
|
-
rBytes.length > 1 &&
|
|
142
|
-
rBytes[0] === 0x00 &&
|
|
143
|
-
(rBytes[1] & 0x80) === 0
|
|
144
|
-
) {
|
|
145
|
-
rBytes = rBytes.slice(1);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
let sBytes = s;
|
|
149
|
-
while (
|
|
150
|
-
sBytes.length > 1 &&
|
|
151
|
-
sBytes[0] === 0x00 &&
|
|
152
|
-
(sBytes[1] & 0x80) === 0
|
|
153
|
-
) {
|
|
154
|
-
sBytes = sBytes.slice(1);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Add padding byte if high bit is set (to keep numbers positive)
|
|
158
|
-
if ((rBytes[0] & 0x80) !== 0) {
|
|
159
|
-
rBytes = Buffer.concat([Buffer.from([0x00]), rBytes]);
|
|
160
|
-
}
|
|
161
|
-
if ((sBytes[0] & 0x80) !== 0) {
|
|
162
|
-
sBytes = Buffer.concat([Buffer.from([0x00]), sBytes]);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const totalLength = 2 + rBytes.length + 2 + sBytes.length;
|
|
166
|
-
|
|
167
|
-
const derSignature = Buffer.concat([
|
|
168
|
-
Buffer.from([0x30, totalLength]), // SEQUENCE tag and total length
|
|
169
|
-
Buffer.from([0x02, rBytes.length]), // INTEGER tag and R length
|
|
170
|
-
rBytes,
|
|
171
|
-
Buffer.from([0x02, sBytes.length]), // INTEGER tag and S length
|
|
172
|
-
sBytes,
|
|
173
|
-
Buffer.from([0x01]), // SIGHASH_ALL flag
|
|
174
|
-
]);
|
|
175
|
-
|
|
176
|
-
return derSignature;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// If it's already DER format, check if it has SIGHASH flag
|
|
180
|
-
if (signature.length > 0 && signature[0] === 0x30) {
|
|
181
|
-
// Check if it already has a SIGHASH flag (last byte should be 0x01 for SIGHASH_ALL)
|
|
182
|
-
if (signature[signature.length - 1] !== 0x01) {
|
|
183
|
-
// Add SIGHASH_ALL flag
|
|
184
|
-
return Buffer.concat([signature, Buffer.from([0x01])]);
|
|
185
|
-
}
|
|
186
|
-
return signature;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// For other formats, return as-is
|
|
190
|
-
return signature;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Detect if signature is in DER format and convert to compact format (64 bytes)
|
|
195
|
-
* by extracting r and s values, removing SIGHASH flag if present
|
|
196
|
-
*/
|
|
197
|
-
private ensureCompactSignature(signature: Buffer): Buffer {
|
|
198
|
-
// If signature is already 64 bytes, it's likely already compact format
|
|
199
|
-
if (signature.length === 64) {
|
|
200
|
-
return signature;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Check if it's DER format (starts with 0x30)
|
|
204
|
-
if (signature.length > 6 && signature[0] === 0x30) {
|
|
205
|
-
let derSig = signature;
|
|
206
|
-
|
|
207
|
-
// Remove SIGHASH flag if present (last byte is typically 0x01 for SIGHASH_ALL)
|
|
208
|
-
if (signature[signature.length - 1] === 0x01) {
|
|
209
|
-
derSig = signature.slice(0, -1);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// Parse DER format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S]
|
|
213
|
-
if (derSig[0] !== 0x30) {
|
|
214
|
-
throw new Error('Invalid DER signature: missing SEQUENCE tag');
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
const totalLength = derSig[1];
|
|
218
|
-
if (derSig.length < totalLength + 2) {
|
|
219
|
-
throw new Error('Invalid DER signature: length mismatch');
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
let offset = 2;
|
|
223
|
-
|
|
224
|
-
// Parse R value
|
|
225
|
-
if (derSig[offset] !== 0x02) {
|
|
226
|
-
throw new Error('Invalid DER signature: missing INTEGER tag for R');
|
|
227
|
-
}
|
|
228
|
-
offset++;
|
|
229
|
-
|
|
230
|
-
const rLength = derSig[offset];
|
|
231
|
-
offset++;
|
|
232
|
-
|
|
233
|
-
if (offset + rLength > derSig.length) {
|
|
234
|
-
throw new Error(
|
|
235
|
-
'Invalid DER signature: R length exceeds signature length',
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
let rBytes = derSig.slice(offset, offset + rLength);
|
|
240
|
-
offset += rLength;
|
|
241
|
-
|
|
242
|
-
// Parse S value
|
|
243
|
-
if (derSig[offset] !== 0x02) {
|
|
244
|
-
throw new Error('Invalid DER signature: missing INTEGER tag for S');
|
|
245
|
-
}
|
|
246
|
-
offset++;
|
|
247
|
-
|
|
248
|
-
const sLength = derSig[offset];
|
|
249
|
-
offset++;
|
|
250
|
-
|
|
251
|
-
if (offset + sLength > derSig.length) {
|
|
252
|
-
throw new Error(
|
|
253
|
-
'Invalid DER signature: S length exceeds signature length',
|
|
254
|
-
);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
let sBytes = derSig.slice(offset, offset + sLength);
|
|
258
|
-
|
|
259
|
-
// Remove leading zero padding from r and s (DER may pad to prevent negative interpretation)
|
|
260
|
-
while (rBytes.length > 1 && rBytes[0] === 0x00) {
|
|
261
|
-
rBytes = rBytes.slice(1);
|
|
262
|
-
}
|
|
263
|
-
while (sBytes.length > 1 && sBytes[0] === 0x00) {
|
|
264
|
-
sBytes = sBytes.slice(1);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// Pad to 32 bytes each (compact format requires exactly 32 bytes for r and s)
|
|
268
|
-
while (rBytes.length < 32) {
|
|
269
|
-
rBytes = Buffer.concat([Buffer.from([0x00]), rBytes]);
|
|
270
|
-
}
|
|
271
|
-
while (sBytes.length < 32) {
|
|
272
|
-
sBytes = Buffer.concat([Buffer.from([0x00]), sBytes]);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
if (rBytes.length !== 32 || sBytes.length !== 32) {
|
|
276
|
-
throw new Error('Invalid signature values: r or s exceeds 32 bytes');
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// Combine r and s into 64-byte compact format
|
|
280
|
-
return Buffer.concat([rBytes, sBytes]);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// For other formats, throw error as we can't convert
|
|
284
|
-
throw new Error(
|
|
285
|
-
'Unable to convert signature to compact format: unknown format',
|
|
286
|
-
);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* Compute contract ID from fund transaction ID, output index, and temporary contract ID
|
|
291
|
-
* Matches the Rust implementation in rust-dlc
|
|
292
|
-
*/
|
|
293
|
-
private computeContractId(
|
|
294
|
-
fundTxId: Buffer,
|
|
295
|
-
fundOutputIndex: number,
|
|
296
|
-
temporaryContractId: Buffer,
|
|
297
|
-
): Buffer {
|
|
298
|
-
if (fundTxId.length !== 32) {
|
|
299
|
-
throw new Error('Fund transaction ID must be 32 bytes');
|
|
300
|
-
}
|
|
301
|
-
if (temporaryContractId.length !== 32) {
|
|
302
|
-
throw new Error('Temporary contract ID must be 32 bytes');
|
|
303
|
-
}
|
|
304
|
-
if (fundOutputIndex > 0xffff) {
|
|
305
|
-
throw new Error('Fund output index must fit in 16 bits');
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
const result = Buffer.alloc(32);
|
|
309
|
-
|
|
310
|
-
// XOR fund_tx_id with temporary_id, with byte order reversal for fund_tx_id
|
|
311
|
-
for (let i = 0; i < 32; i++) {
|
|
312
|
-
result[i] = fundTxId[31 - i] ^ temporaryContractId[i];
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// XOR the fund output index into the last two bytes
|
|
316
|
-
result[30] ^= (fundOutputIndex >> 8) & 0xff; // High byte
|
|
317
|
-
result[31] ^= fundOutputIndex & 0xff; // Low byte
|
|
318
|
-
|
|
319
|
-
return result;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
121
|
/**
|
|
323
122
|
* Create refund signature using PSBT method instead of DDK
|
|
324
123
|
*/
|
|
@@ -338,22 +137,11 @@ export default class BitcoinDdkProvider extends Provider {
|
|
|
338
137
|
);
|
|
339
138
|
}
|
|
340
139
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
|
|
345
|
-
: [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
|
|
346
|
-
|
|
347
|
-
const p2ms = payments.p2ms({
|
|
348
|
-
m: 2,
|
|
349
|
-
pubkeys: fundingPubKeys,
|
|
140
|
+
const paymentVariant = createP2WSHMultisig(
|
|
141
|
+
dlcOffer.fundingPubkey,
|
|
142
|
+
dlcAccept.fundingPubkey,
|
|
350
143
|
network,
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
const paymentVariant = payments.p2wsh({
|
|
354
|
-
redeem: p2ms,
|
|
355
|
-
network,
|
|
356
|
-
});
|
|
144
|
+
);
|
|
357
145
|
|
|
358
146
|
// Add the funding input with sequence from refund transaction
|
|
359
147
|
psbt.addInput({
|
|
@@ -404,7 +192,7 @@ export default class BitcoinDdkProvider extends Provider {
|
|
|
404
192
|
const derSignature = partialSig.signature;
|
|
405
193
|
|
|
406
194
|
// Convert DER signature to compact format
|
|
407
|
-
return
|
|
195
|
+
return ensureCompactSignature(derSignature);
|
|
408
196
|
}
|
|
409
197
|
|
|
410
198
|
/**
|
|
@@ -962,12 +750,19 @@ export default class BitcoinDdkProvider extends Provider {
|
|
|
962
750
|
}),
|
|
963
751
|
);
|
|
964
752
|
|
|
965
|
-
// Calculate input amounts
|
|
966
|
-
const
|
|
753
|
+
// Calculate input amounts (including both regular inputs and DLC inputs)
|
|
754
|
+
const localRegularInputAmount = localRegularInputs.reduce<number>(
|
|
967
755
|
(prev, cur) => prev + cur.amount.GetSatoshiAmount(),
|
|
968
756
|
0,
|
|
969
757
|
);
|
|
970
758
|
|
|
759
|
+
const localDlcInputAmount = localDlcInputs.reduce<number>(
|
|
760
|
+
(prev, cur) => prev + Number(cur.fundAmount),
|
|
761
|
+
0,
|
|
762
|
+
);
|
|
763
|
+
|
|
764
|
+
const localInputAmount = localRegularInputAmount + localDlcInputAmount;
|
|
765
|
+
|
|
971
766
|
const remoteInputAmount = remoteInputs.reduce<number>(
|
|
972
767
|
(prev, cur) => prev + cur.amount.GetSatoshiAmount(),
|
|
973
768
|
0,
|
|
@@ -1046,7 +841,7 @@ export default class BitcoinDdkProvider extends Provider {
|
|
|
1046
841
|
BigInt(dlcOffer.feeRatePerVb),
|
|
1047
842
|
0,
|
|
1048
843
|
dlcOffer.cetLocktime,
|
|
1049
|
-
dlcOffer.fundOutputSerialId,
|
|
844
|
+
BigInt(dlcOffer.fundOutputSerialId),
|
|
1050
845
|
);
|
|
1051
846
|
} else {
|
|
1052
847
|
// Use regular DLC transactions when no DLC inputs
|
|
@@ -1280,19 +1075,12 @@ export default class BitcoinDdkProvider extends Provider {
|
|
|
1280
1075
|
|
|
1281
1076
|
const cetsHex = dlcTxs.cets.map((cet) => cet.serialize().toString('hex'));
|
|
1282
1077
|
|
|
1283
|
-
// Create the correct P2WSH multisig funding script
|
|
1284
|
-
const fundingPubKeys =
|
|
1285
|
-
Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
|
|
1286
|
-
? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
|
|
1287
|
-
: [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
|
|
1288
|
-
|
|
1289
|
-
const p2ms = payments.p2ms({
|
|
1290
|
-
m: 2,
|
|
1291
|
-
pubkeys: fundingPubKeys,
|
|
1292
|
-
network,
|
|
1293
|
-
});
|
|
1294
|
-
|
|
1295
1078
|
// We need the redeem script (multisig), not the P2WSH output
|
|
1079
|
+
const p2ms = createP2MSMultisig(
|
|
1080
|
+
dlcOffer.fundingPubkey,
|
|
1081
|
+
dlcAccept.fundingPubkey,
|
|
1082
|
+
network,
|
|
1083
|
+
);
|
|
1296
1084
|
const fundingSPK = p2ms.output!;
|
|
1297
1085
|
|
|
1298
1086
|
// For finding the private key, we still need the individual P2WPKH address
|
|
@@ -1499,16 +1287,12 @@ export default class BitcoinDdkProvider extends Provider {
|
|
|
1499
1287
|
|
|
1500
1288
|
// Create the correct P2WSH multisig funding script for verification
|
|
1501
1289
|
const network = await this.getConnectedNetwork();
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
const verifyP2ms = payments.p2ms({
|
|
1508
|
-
m: 2,
|
|
1509
|
-
pubkeys: verifyFundingPubKeys,
|
|
1290
|
+
|
|
1291
|
+
const verifyP2ms = createP2MSMultisig(
|
|
1292
|
+
dlcOffer.fundingPubkey,
|
|
1293
|
+
dlcAccept.fundingPubkey,
|
|
1510
1294
|
network,
|
|
1511
|
-
|
|
1295
|
+
);
|
|
1512
1296
|
|
|
1513
1297
|
// We need the redeem script (multisig), not the P2WSH output
|
|
1514
1298
|
const fundingSPK = verifyP2ms.output!;
|
|
@@ -1658,7 +1442,7 @@ export default class BitcoinDdkProvider extends Provider {
|
|
|
1658
1442
|
}
|
|
1659
1443
|
}
|
|
1660
1444
|
|
|
1661
|
-
private async
|
|
1445
|
+
private async CreateFundingSigs(
|
|
1662
1446
|
dlcOffer: DlcOffer,
|
|
1663
1447
|
dlcAccept: DlcAccept,
|
|
1664
1448
|
dlcTxs: DlcTransactions,
|
|
@@ -1670,14 +1454,10 @@ export default class BitcoinDdkProvider extends Provider {
|
|
|
1670
1454
|
const network = await this.getConnectedNetwork();
|
|
1671
1455
|
const psbt = new Psbt({ network });
|
|
1672
1456
|
|
|
1673
|
-
|
|
1674
|
-
const allFundingInputs = [
|
|
1457
|
+
const allFundingInputs = sortFundingInputsBySerialId([
|
|
1675
1458
|
...dlcOffer.fundingInputs,
|
|
1676
1459
|
...dlcAccept.fundingInputs,
|
|
1677
|
-
];
|
|
1678
|
-
|
|
1679
|
-
// Sort by inputSerialId to reconstruct proper transaction order
|
|
1680
|
-
allFundingInputs.sort((a, b) => Number(a.inputSerialId - b.inputSerialId));
|
|
1460
|
+
]);
|
|
1681
1461
|
|
|
1682
1462
|
// Add all inputs to PSBT with proper witnessUtxo
|
|
1683
1463
|
for (const fundingInput of allFundingInputs) {
|
|
@@ -1756,11 +1536,42 @@ export default class BitcoinDdkProvider extends Provider {
|
|
|
1756
1536
|
|
|
1757
1537
|
// Check if this is a DLC input (2-of-2 multisig from previous DLC)
|
|
1758
1538
|
if (fundingInput.dlcInput) {
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1539
|
+
const paymentVariant = createP2WSHMultisig(
|
|
1540
|
+
fundingInput.dlcInput.localFundPubkey,
|
|
1541
|
+
fundingInput.dlcInput.remoteFundPubkey,
|
|
1542
|
+
network,
|
|
1763
1543
|
);
|
|
1544
|
+
|
|
1545
|
+
const redeemScript = paymentVariant.redeem.output;
|
|
1546
|
+
|
|
1547
|
+
// Find the correct private key for this DLC input
|
|
1548
|
+
const dlcPrivKey = await this.findDlcFundingPrivateKey(
|
|
1549
|
+
fundingInput.dlcInput.localFundPubkey.toString('hex'),
|
|
1550
|
+
fundingInput.dlcInput.remoteFundPubkey.toString('hex'),
|
|
1551
|
+
);
|
|
1552
|
+
const keyPair = ECPair.fromPrivateKey(Buffer.from(dlcPrivKey, 'hex'));
|
|
1553
|
+
|
|
1554
|
+
// Update the input with the witnessScript before signing
|
|
1555
|
+
psbt.updateInput(inputIndex, {
|
|
1556
|
+
witnessScript: redeemScript,
|
|
1557
|
+
});
|
|
1558
|
+
|
|
1559
|
+
psbt.signInput(inputIndex, keyPair);
|
|
1560
|
+
|
|
1561
|
+
const inputData = psbt.data.inputs[inputIndex];
|
|
1562
|
+
const partialSigs = inputData.partialSig;
|
|
1563
|
+
if (!partialSigs || partialSigs.length === 0) {
|
|
1564
|
+
throw new Error(
|
|
1565
|
+
`No signatures found for input ${inputIndex} after signing`,
|
|
1566
|
+
);
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
const sigWitness = new ScriptWitnessV0();
|
|
1570
|
+
sigWitness.witness = ensureBuffer(partialSigs[0].signature);
|
|
1571
|
+
const pubKeyWitness = new ScriptWitnessV0();
|
|
1572
|
+
pubKeyWitness.witness = keyPair.publicKey;
|
|
1573
|
+
|
|
1574
|
+
witnessElements.push([sigWitness, pubKeyWitness]);
|
|
1764
1575
|
} else {
|
|
1765
1576
|
// For P2WPKH inputs, use PSBT signing
|
|
1766
1577
|
psbt.signInput(inputIndex, keyPair);
|
|
@@ -1776,7 +1587,7 @@ export default class BitcoinDdkProvider extends Provider {
|
|
|
1776
1587
|
|
|
1777
1588
|
// For P2WPKH, create witness manually: [signature, publicKey]
|
|
1778
1589
|
const sigWitness = new ScriptWitnessV0();
|
|
1779
|
-
sigWitness.witness =
|
|
1590
|
+
sigWitness.witness = ensureBuffer(partialSigs[0].signature);
|
|
1780
1591
|
const pubKeyWitness = new ScriptWitnessV0();
|
|
1781
1592
|
pubKeyWitness.witness = keyPair.publicKey;
|
|
1782
1593
|
|
|
@@ -1792,7 +1603,7 @@ export default class BitcoinDdkProvider extends Provider {
|
|
|
1792
1603
|
return fundingSignatures;
|
|
1793
1604
|
}
|
|
1794
1605
|
|
|
1795
|
-
private async
|
|
1606
|
+
private async VerifyFundingSigs(
|
|
1796
1607
|
dlcOffer: DlcOffer,
|
|
1797
1608
|
dlcAccept: DlcAccept,
|
|
1798
1609
|
dlcSign: DlcSign,
|
|
@@ -1811,12 +1622,10 @@ export default class BitcoinDdkProvider extends Provider {
|
|
|
1811
1622
|
? dlcAccept.fundingInputs
|
|
1812
1623
|
: dlcOffer.fundingInputs;
|
|
1813
1624
|
|
|
1814
|
-
|
|
1815
|
-
const allFundingInputs = [
|
|
1625
|
+
const allFundingInputs = sortFundingInputsBySerialId([
|
|
1816
1626
|
...dlcOffer.fundingInputs,
|
|
1817
1627
|
...dlcAccept.fundingInputs,
|
|
1818
|
-
];
|
|
1819
|
-
allFundingInputs.sort((a, b) => Number(a.inputSerialId - b.inputSerialId));
|
|
1628
|
+
]);
|
|
1820
1629
|
|
|
1821
1630
|
// Compare transaction IDs
|
|
1822
1631
|
const dlcFundTxId = dlcTxs.fundTx.txId.toString();
|
|
@@ -1870,8 +1679,61 @@ export default class BitcoinDdkProvider extends Provider {
|
|
|
1870
1679
|
// Add the funding signatures to the PSBT as partial signatures
|
|
1871
1680
|
let witnessIndex = 0;
|
|
1872
1681
|
for (const fundingInput of signingPartyInputs) {
|
|
1873
|
-
//
|
|
1682
|
+
// Handle DLC inputs with multisig verification
|
|
1874
1683
|
if (fundingInput.dlcInput) {
|
|
1684
|
+
const dlcInput = fundingInput.dlcInput;
|
|
1685
|
+
|
|
1686
|
+
const paymentVariant = createP2WSHMultisig(
|
|
1687
|
+
dlcInput.localFundPubkey,
|
|
1688
|
+
dlcInput.remoteFundPubkey,
|
|
1689
|
+
network,
|
|
1690
|
+
);
|
|
1691
|
+
|
|
1692
|
+
// Find this input's index in the sorted transaction
|
|
1693
|
+
const inputIndex = allFundingInputs.findIndex(
|
|
1694
|
+
(input) =>
|
|
1695
|
+
input.prevTx.txId.toString() ===
|
|
1696
|
+
fundingInput.prevTx.txId.toString() &&
|
|
1697
|
+
input.prevTxVout === fundingInput.prevTxVout,
|
|
1698
|
+
);
|
|
1699
|
+
|
|
1700
|
+
if (inputIndex === -1) {
|
|
1701
|
+
throw new Error(
|
|
1702
|
+
`DLC input not found in transaction: ${fundingInput.prevTx.txId.toString()}:${fundingInput.prevTxVout}`,
|
|
1703
|
+
);
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
// Update the input with the witnessScript for verification
|
|
1707
|
+
psbt.updateInput(inputIndex, {
|
|
1708
|
+
witnessScript: paymentVariant.redeem.output,
|
|
1709
|
+
});
|
|
1710
|
+
|
|
1711
|
+
// Add the DLC signature from witness elements if available
|
|
1712
|
+
if (witnessIndex < dlcSign.fundingSignatures.witnessElements.length) {
|
|
1713
|
+
const witnessElement =
|
|
1714
|
+
dlcSign.fundingSignatures.witnessElements[witnessIndex];
|
|
1715
|
+
const signature = witnessElement[0].witness;
|
|
1716
|
+
const publicKey = witnessElement[1].witness;
|
|
1717
|
+
|
|
1718
|
+
psbt.updateInput(inputIndex, {
|
|
1719
|
+
partialSig: [
|
|
1720
|
+
{
|
|
1721
|
+
pubkey: publicKey,
|
|
1722
|
+
signature: ensureDerSignature(signature),
|
|
1723
|
+
},
|
|
1724
|
+
],
|
|
1725
|
+
});
|
|
1726
|
+
|
|
1727
|
+
// Verify the signature for this DLC input
|
|
1728
|
+
psbt.validateSignaturesOfInput(
|
|
1729
|
+
inputIndex,
|
|
1730
|
+
(pubkey: Buffer, msghash: Buffer, signature: Buffer) => {
|
|
1731
|
+
return ecc.verify(msghash, pubkey, signature);
|
|
1732
|
+
},
|
|
1733
|
+
);
|
|
1734
|
+
|
|
1735
|
+
witnessIndex++;
|
|
1736
|
+
}
|
|
1875
1737
|
continue;
|
|
1876
1738
|
}
|
|
1877
1739
|
|
|
@@ -1904,7 +1766,7 @@ export default class BitcoinDdkProvider extends Provider {
|
|
|
1904
1766
|
partialSig: [
|
|
1905
1767
|
{
|
|
1906
1768
|
pubkey: publicKey,
|
|
1907
|
-
signature:
|
|
1769
|
+
signature: ensureDerSignature(signature),
|
|
1908
1770
|
},
|
|
1909
1771
|
],
|
|
1910
1772
|
});
|
|
@@ -1981,12 +1843,10 @@ export default class BitcoinDdkProvider extends Provider {
|
|
|
1981
1843
|
Buffer.from(dlcTxs.fundTx.serialize()),
|
|
1982
1844
|
);
|
|
1983
1845
|
|
|
1984
|
-
|
|
1985
|
-
const allFundingInputs = [
|
|
1846
|
+
const allFundingInputs = sortFundingInputsBySerialId([
|
|
1986
1847
|
...dlcOffer.fundingInputs,
|
|
1987
1848
|
...dlcAccept.fundingInputs,
|
|
1988
|
-
];
|
|
1989
|
-
allFundingInputs.sort((a, b) => Number(a.inputSerialId - b.inputSerialId));
|
|
1849
|
+
]);
|
|
1990
1850
|
|
|
1991
1851
|
// Calculate detailed sighash information
|
|
1992
1852
|
const details = [];
|
|
@@ -2047,7 +1907,7 @@ export default class BitcoinDdkProvider extends Provider {
|
|
|
2047
1907
|
: dlcSign.refundSignature;
|
|
2048
1908
|
|
|
2049
1909
|
// Ensure signature is in DER format (convert from compact if needed)
|
|
2050
|
-
const refundSignature =
|
|
1910
|
+
const refundSignature = ensureDerSignature(rawRefundSignature);
|
|
2051
1911
|
|
|
2052
1912
|
const signingPubkey = isOfferer
|
|
2053
1913
|
? dlcAccept.fundingPubkey
|
|
@@ -2063,22 +1923,11 @@ export default class BitcoinDdkProvider extends Provider {
|
|
|
2063
1923
|
// Create a PSBT for the refund transaction verification using same approach as createDlcClose
|
|
2064
1924
|
const psbt = new Psbt({ network });
|
|
2065
1925
|
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
|
|
2070
|
-
: [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
|
|
2071
|
-
|
|
2072
|
-
const p2ms = payments.p2ms({
|
|
2073
|
-
m: 2,
|
|
2074
|
-
pubkeys: fundingPubKeys,
|
|
2075
|
-
network,
|
|
2076
|
-
});
|
|
2077
|
-
|
|
2078
|
-
const paymentVariant = payments.p2wsh({
|
|
2079
|
-
redeem: p2ms,
|
|
1926
|
+
const paymentVariant = createP2WSHMultisig(
|
|
1927
|
+
dlcOffer.fundingPubkey,
|
|
1928
|
+
dlcAccept.fundingPubkey,
|
|
2080
1929
|
network,
|
|
2081
|
-
|
|
1930
|
+
);
|
|
2082
1931
|
|
|
2083
1932
|
// Add the funding input with sequence from refund transaction
|
|
2084
1933
|
psbt.addInput({
|
|
@@ -2132,23 +1981,11 @@ export default class BitcoinDdkProvider extends Provider {
|
|
|
2132
1981
|
): Buffer {
|
|
2133
1982
|
const network = this.getBitcoinJsNetwork();
|
|
2134
1983
|
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
|
|
2139
|
-
: [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
|
|
2140
|
-
|
|
2141
|
-
// Create 2-of-2 multisig script
|
|
2142
|
-
const p2ms = payments.p2ms({
|
|
2143
|
-
m: 2,
|
|
2144
|
-
pubkeys: fundingPubKeys,
|
|
2145
|
-
network,
|
|
2146
|
-
});
|
|
2147
|
-
|
|
2148
|
-
const paymentVariant = payments.p2wsh({
|
|
2149
|
-
redeem: p2ms,
|
|
1984
|
+
const paymentVariant = createP2WSHMultisig(
|
|
1985
|
+
dlcOffer.fundingPubkey,
|
|
1986
|
+
dlcAccept.fundingPubkey,
|
|
2150
1987
|
network,
|
|
2151
|
-
|
|
1988
|
+
);
|
|
2152
1989
|
|
|
2153
1990
|
return paymentVariant.redeem!.output!;
|
|
2154
1991
|
}
|
|
@@ -2163,27 +2000,20 @@ export default class BitcoinDdkProvider extends Provider {
|
|
|
2163
2000
|
const network = await this.getConnectedNetwork();
|
|
2164
2001
|
const psbt = new Psbt({ network });
|
|
2165
2002
|
|
|
2166
|
-
|
|
2167
|
-
const allFundingInputs = [
|
|
2003
|
+
const allFundingInputs = sortFundingInputsBySerialId([
|
|
2168
2004
|
...dlcOffer.fundingInputs,
|
|
2169
2005
|
...dlcAccept.fundingInputs,
|
|
2170
|
-
];
|
|
2171
|
-
allFundingInputs.sort((a, b) => Number(a.inputSerialId - b.inputSerialId));
|
|
2006
|
+
]);
|
|
2172
2007
|
|
|
2173
2008
|
// Create a map of input txid:vout to witness elements
|
|
2174
2009
|
const witnessMap = new Map<string, ScriptWitnessV0[]>();
|
|
2175
2010
|
|
|
2176
|
-
// Map witness elements correctly -
|
|
2011
|
+
// Map witness elements correctly - CreateFundingSigs only creates witness elements
|
|
2177
2012
|
// for the party's own inputs, so we need to map them correctly
|
|
2178
2013
|
|
|
2179
|
-
// For dlcSign (offerer's signatures), map to offerer's inputs
|
|
2014
|
+
// For dlcSign (offerer's signatures), map to offerer's inputs (including DLC inputs)
|
|
2180
2015
|
let offererWitnessIndex = 0;
|
|
2181
2016
|
dlcOffer.fundingInputs.forEach((fundingInput) => {
|
|
2182
|
-
// Skip DLC inputs for now as in CreateFundingSigsAlt
|
|
2183
|
-
if (fundingInput.dlcInput) {
|
|
2184
|
-
return;
|
|
2185
|
-
}
|
|
2186
|
-
|
|
2187
2017
|
if (
|
|
2188
2018
|
offererWitnessIndex < dlcSign.fundingSignatures.witnessElements.length
|
|
2189
2019
|
) {
|
|
@@ -2196,14 +2026,9 @@ export default class BitcoinDdkProvider extends Provider {
|
|
|
2196
2026
|
}
|
|
2197
2027
|
});
|
|
2198
2028
|
|
|
2199
|
-
// For fundingSignatures (accepter's signatures), map to accepter's inputs
|
|
2029
|
+
// For fundingSignatures (accepter's signatures), map to accepter's inputs (including DLC inputs)
|
|
2200
2030
|
let accepterWitnessIndex = 0;
|
|
2201
2031
|
dlcAccept.fundingInputs.forEach((fundingInput) => {
|
|
2202
|
-
// Skip DLC inputs for now as in CreateFundingSigsAlt
|
|
2203
|
-
if (fundingInput.dlcInput) {
|
|
2204
|
-
return;
|
|
2205
|
-
}
|
|
2206
|
-
|
|
2207
2032
|
if (accepterWitnessIndex < fundingSignatures.witnessElements.length) {
|
|
2208
2033
|
const key = `${fundingInput.prevTx.txId.toString()}:${fundingInput.prevTxVout}`;
|
|
2209
2034
|
witnessMap.set(
|
|
@@ -2222,13 +2047,13 @@ export default class BitcoinDdkProvider extends Provider {
|
|
|
2222
2047
|
for (const fundingInput of allFundingInputs) {
|
|
2223
2048
|
const prevOut = fundingInput.prevTx.outputs[fundingInput.prevTxVout];
|
|
2224
2049
|
|
|
2225
|
-
// Use same script handling as
|
|
2050
|
+
// Use same script handling as CreateFundingSigs
|
|
2226
2051
|
const witnessUtxo = {
|
|
2227
2052
|
script: Buffer.from(prevOut.scriptPubKey.serialize().subarray(1)),
|
|
2228
2053
|
value: Number(prevOut.value.sats),
|
|
2229
2054
|
};
|
|
2230
2055
|
|
|
2231
|
-
// Use sequence from the original transaction (same as
|
|
2056
|
+
// Use sequence from the original transaction (same as CreateFundingSigs)
|
|
2232
2057
|
const originalInput = originalTransaction.ins.find(
|
|
2233
2058
|
(input) =>
|
|
2234
2059
|
input.hash.reverse().toString('hex') ===
|
|
@@ -2266,13 +2091,76 @@ export default class BitcoinDdkProvider extends Provider {
|
|
|
2266
2091
|
|
|
2267
2092
|
const witnessElements = witnessMap.get(inputKey);
|
|
2268
2093
|
if (witnessElements && witnessElements.length === 2) {
|
|
2269
|
-
//
|
|
2094
|
+
// Handle DLC inputs with multisig finalization
|
|
2270
2095
|
if (fundingInput.dlcInput) {
|
|
2096
|
+
const dlcInput = fundingInput.dlcInput;
|
|
2097
|
+
|
|
2098
|
+
// Create the multisig script for finalization
|
|
2099
|
+
// Use lexicographic ordering to match DDK library behavior
|
|
2100
|
+
const orderedPubkeys = orderPubkeysLexicographically(
|
|
2101
|
+
dlcInput.localFundPubkey,
|
|
2102
|
+
dlcInput.remoteFundPubkey,
|
|
2103
|
+
);
|
|
2104
|
+
|
|
2105
|
+
const paymentVariant = createP2WSHMultisigFromOrdered(
|
|
2106
|
+
orderedPubkeys,
|
|
2107
|
+
network,
|
|
2108
|
+
);
|
|
2109
|
+
|
|
2110
|
+
// Update the input with the witnessScript
|
|
2111
|
+
psbt.updateInput(inputIndex, {
|
|
2112
|
+
witnessScript: paymentVariant.redeem.output,
|
|
2113
|
+
});
|
|
2114
|
+
|
|
2115
|
+
// Sign this DLC input ourselves
|
|
2116
|
+
const dlcPrivKey = await this.findDlcFundingPrivateKey(
|
|
2117
|
+
dlcInput.localFundPubkey.toString('hex'),
|
|
2118
|
+
dlcInput.remoteFundPubkey.toString('hex'),
|
|
2119
|
+
);
|
|
2120
|
+
const keyPair = ECPair.fromPrivateKey(Buffer.from(dlcPrivKey, 'hex'));
|
|
2121
|
+
|
|
2122
|
+
psbt.signInput(inputIndex, keyPair);
|
|
2123
|
+
|
|
2124
|
+
// Get our signature and determine which pubkey it corresponds to
|
|
2125
|
+
const ourPartialSig = psbt.data.inputs[inputIndex].partialSig[0];
|
|
2126
|
+
const ourSig = ensureDerSignature(ourPartialSig.signature);
|
|
2127
|
+
const ourPubkey = ourPartialSig.pubkey;
|
|
2128
|
+
|
|
2129
|
+
// Get the other party's signature from witnessElements
|
|
2130
|
+
const otherPartySig = ensureDerSignature(witnessElements[0].witness);
|
|
2131
|
+
|
|
2132
|
+
// Order signatures according to the lexicographic pubkey order used in the multisig script
|
|
2133
|
+
let sig1: Buffer, sig2: Buffer;
|
|
2134
|
+
|
|
2135
|
+
if (Buffer.compare(ourPubkey, orderedPubkeys[0]) === 0) {
|
|
2136
|
+
// Our signature corresponds to the first pubkey in lexicographic order
|
|
2137
|
+
sig1 = ourSig;
|
|
2138
|
+
sig2 = otherPartySig;
|
|
2139
|
+
} else {
|
|
2140
|
+
// Our signature corresponds to the second pubkey in lexicographic order
|
|
2141
|
+
sig1 = otherPartySig;
|
|
2142
|
+
sig2 = ourSig;
|
|
2143
|
+
}
|
|
2144
|
+
|
|
2145
|
+
// Finalize with proper multisig witness: [OP_0, sig1, sig2, redeemScript]
|
|
2146
|
+
psbt.finalizeInput(inputIndex, () => ({
|
|
2147
|
+
finalScriptSig: Buffer.alloc(0),
|
|
2148
|
+
finalScriptWitness: Buffer.concat([
|
|
2149
|
+
Buffer.from([0x04]), // witness stack count
|
|
2150
|
+
Buffer.from([0x00]), // OP_0 (Bitcoin multisig bug)
|
|
2151
|
+
Buffer.from([sig1.length]),
|
|
2152
|
+
sig1,
|
|
2153
|
+
Buffer.from([sig2.length]),
|
|
2154
|
+
sig2,
|
|
2155
|
+
Buffer.from([paymentVariant.redeem.output.length]),
|
|
2156
|
+
paymentVariant.redeem.output,
|
|
2157
|
+
]),
|
|
2158
|
+
}));
|
|
2271
2159
|
continue;
|
|
2272
2160
|
}
|
|
2273
2161
|
|
|
2274
2162
|
// For P2WPKH inputs, finalize the input with witness data
|
|
2275
|
-
const signature =
|
|
2163
|
+
const signature = ensureDerSignature(witnessElements[0].witness);
|
|
2276
2164
|
const publicKey = witnessElements[1].witness;
|
|
2277
2165
|
|
|
2278
2166
|
// Try a simpler approach - let bitcoinjs-lib handle witness construction
|
|
@@ -2376,15 +2264,13 @@ Payout Group not found',
|
|
|
2376
2264
|
}
|
|
2377
2265
|
|
|
2378
2266
|
async FindOutcomeIndexFromHyperbolaPayoutCurvePiece(
|
|
2379
|
-
|
|
2267
|
+
dlcOffer: DlcOffer,
|
|
2380
2268
|
contractDescriptor: NumericalDescriptor,
|
|
2381
2269
|
contractOraclePairIndex: number,
|
|
2382
2270
|
hyperbolaPayoutCurvePiece: HyperbolaPayoutCurvePiece,
|
|
2383
2271
|
oracleAttestation: OracleAttestation,
|
|
2384
2272
|
outcome: bigint,
|
|
2385
2273
|
): Promise<FindOutcomeResponse> {
|
|
2386
|
-
const { dlcOffer } = checkTypes({ _dlcOffer });
|
|
2387
|
-
|
|
2388
2274
|
const hyperbolaCurve = HyperbolaPayoutCurve.fromPayoutCurvePiece(
|
|
2389
2275
|
hyperbolaPayoutCurvePiece,
|
|
2390
2276
|
);
|
|
@@ -2914,21 +2800,15 @@ Payout Group not found even with brute force search',
|
|
|
2914
2800
|
|
|
2915
2801
|
const network = await this.getConnectedNetwork();
|
|
2916
2802
|
|
|
2917
|
-
const fundingPubKeys =
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
const p2ms = payments.p2ms({
|
|
2923
|
-
m: 2,
|
|
2924
|
-
pubkeys: fundingPubKeys,
|
|
2925
|
-
network,
|
|
2926
|
-
});
|
|
2803
|
+
const fundingPubKeys = orderPubkeysLexicographically(
|
|
2804
|
+
dlcOffer.fundingPubkey,
|
|
2805
|
+
dlcAccept.fundingPubkey,
|
|
2806
|
+
);
|
|
2927
2807
|
|
|
2928
|
-
const paymentVariant =
|
|
2929
|
-
|
|
2808
|
+
const paymentVariant = createP2WSHMultisigFromOrdered(
|
|
2809
|
+
fundingPubKeys,
|
|
2930
2810
|
network,
|
|
2931
|
-
|
|
2811
|
+
);
|
|
2932
2812
|
|
|
2933
2813
|
const sigHashRequestPromises: Promise<string>[] = [];
|
|
2934
2814
|
const sigHashes = [];
|
|
@@ -3025,21 +2905,15 @@ Payout Group not found even with brute force search',
|
|
|
3025
2905
|
|
|
3026
2906
|
const network = await this.getConnectedNetwork();
|
|
3027
2907
|
|
|
3028
|
-
const fundingPubKeys =
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
const p2ms = payments.p2ms({
|
|
3034
|
-
m: 2,
|
|
3035
|
-
pubkeys: fundingPubKeys,
|
|
3036
|
-
network,
|
|
3037
|
-
});
|
|
2908
|
+
const fundingPubKeys = orderPubkeysLexicographically(
|
|
2909
|
+
dlcOffer.fundingPubkey,
|
|
2910
|
+
dlcAccept.fundingPubkey,
|
|
2911
|
+
);
|
|
3038
2912
|
|
|
3039
|
-
const paymentVariant =
|
|
3040
|
-
|
|
2913
|
+
const paymentVariant = createP2WSHMultisigFromOrdered(
|
|
2914
|
+
fundingPubKeys,
|
|
3041
2915
|
network,
|
|
3042
|
-
|
|
2916
|
+
);
|
|
3043
2917
|
|
|
3044
2918
|
const pubkey = isOfferer ? dlcAccept.fundingPubkey : dlcOffer.fundingPubkey;
|
|
3045
2919
|
|
|
@@ -3208,13 +3082,7 @@ Payout Group not found even with brute force search',
|
|
|
3208
3082
|
),
|
|
3209
3083
|
);
|
|
3210
3084
|
|
|
3211
|
-
const fundingInputs
|
|
3212
|
-
(input) => input as FundingInput,
|
|
3213
|
-
);
|
|
3214
|
-
|
|
3215
|
-
fundingInputs.sort(
|
|
3216
|
-
(a, b) => Number(a.inputSerialId) - Number(b.inputSerialId),
|
|
3217
|
-
);
|
|
3085
|
+
const fundingInputs = sortFundingInputsBySerialId(_fundingInputs);
|
|
3218
3086
|
|
|
3219
3087
|
const fundOutputSerialId = generateSerialId();
|
|
3220
3088
|
|
|
@@ -3404,10 +3272,8 @@ Payout Group not found even with brute force search',
|
|
|
3404
3272
|
),
|
|
3405
3273
|
);
|
|
3406
3274
|
|
|
3407
|
-
fundingInputs =
|
|
3408
|
-
|
|
3409
|
-
fundingInputs.sort(
|
|
3410
|
-
(a, b) => Number(a.inputSerialId) - Number(b.inputSerialId),
|
|
3275
|
+
fundingInputs = sortFundingInputsBySerialId(
|
|
3276
|
+
_fundingInputs.map((input) => input as FundingInput),
|
|
3411
3277
|
);
|
|
3412
3278
|
}
|
|
3413
3279
|
|
|
@@ -3492,7 +3358,7 @@ Payout Group not found even with brute force search',
|
|
|
3492
3358
|
|
|
3493
3359
|
const _dlcTransactions = dlcTransactions;
|
|
3494
3360
|
|
|
3495
|
-
const contractId =
|
|
3361
|
+
const contractId = computeContractId(
|
|
3496
3362
|
_dlcTransactions.fundTx.txId.serialize(),
|
|
3497
3363
|
_dlcTransactions.fundTxVout,
|
|
3498
3364
|
dlcOffer.temporaryContractId,
|
|
@@ -3548,7 +3414,7 @@ Payout Group not found even with brute force search',
|
|
|
3548
3414
|
true,
|
|
3549
3415
|
);
|
|
3550
3416
|
|
|
3551
|
-
const fundingSignatures = await this.
|
|
3417
|
+
const fundingSignatures = await this.CreateFundingSigs(
|
|
3552
3418
|
dlcOffer,
|
|
3553
3419
|
dlcAccept,
|
|
3554
3420
|
dlcTransactions,
|
|
@@ -3557,24 +3423,12 @@ Payout Group not found even with brute force search',
|
|
|
3557
3423
|
|
|
3558
3424
|
const dlcTxs = dlcTransactions;
|
|
3559
3425
|
|
|
3560
|
-
const contractId =
|
|
3426
|
+
const contractId = computeContractId(
|
|
3561
3427
|
dlcTxs.fundTx.txId.serialize(),
|
|
3562
3428
|
dlcTxs.fundTxVout,
|
|
3563
3429
|
dlcOffer.temporaryContractId,
|
|
3564
3430
|
);
|
|
3565
3431
|
|
|
3566
|
-
assert(
|
|
3567
|
-
Buffer.compare(
|
|
3568
|
-
contractId,
|
|
3569
|
-
this.computeContractId(
|
|
3570
|
-
dlcTxs.fundTx.txId.serialize(),
|
|
3571
|
-
dlcTxs.fundTxVout,
|
|
3572
|
-
dlcOffer.temporaryContractId,
|
|
3573
|
-
),
|
|
3574
|
-
) === 0,
|
|
3575
|
-
'contractId must be the xor of funding txid, fundingOutputIndex and the tempContractId',
|
|
3576
|
-
);
|
|
3577
|
-
|
|
3578
3432
|
dlcTxs.contractId = contractId;
|
|
3579
3433
|
|
|
3580
3434
|
dlcSign.contractId = contractId;
|
|
@@ -3601,6 +3455,17 @@ Payout Group not found even with brute force search',
|
|
|
3601
3455
|
): Promise<Tx> {
|
|
3602
3456
|
let messagesList: Messages[] = [];
|
|
3603
3457
|
|
|
3458
|
+
const contractId = computeContractId(
|
|
3459
|
+
dlcTxs.fundTx.txId.serialize(),
|
|
3460
|
+
dlcTxs.fundTxVout,
|
|
3461
|
+
dlcOffer.temporaryContractId,
|
|
3462
|
+
);
|
|
3463
|
+
|
|
3464
|
+
assert(
|
|
3465
|
+
Buffer.compare(contractId, dlcSign.contractId) === 0,
|
|
3466
|
+
`Finalize Dlc Sign Contract ID mismatch: ${contractId.toString('hex')} !== ${dlcSign.contractId.toString('hex')}`,
|
|
3467
|
+
);
|
|
3468
|
+
|
|
3604
3469
|
if (
|
|
3605
3470
|
dlcOffer.contractInfo.type === MessageType.SingleContractInfo &&
|
|
3606
3471
|
(dlcOffer.contractInfo as SingleContractInfo).contractDescriptor.type ===
|
|
@@ -3628,15 +3493,9 @@ Payout Group not found even with brute force search',
|
|
|
3628
3493
|
false,
|
|
3629
3494
|
);
|
|
3630
3495
|
|
|
3631
|
-
await this.
|
|
3632
|
-
dlcOffer,
|
|
3633
|
-
dlcAccept,
|
|
3634
|
-
dlcSign,
|
|
3635
|
-
dlcTxs,
|
|
3636
|
-
false,
|
|
3637
|
-
);
|
|
3496
|
+
await this.VerifyFundingSigs(dlcOffer, dlcAccept, dlcSign, dlcTxs, false);
|
|
3638
3497
|
|
|
3639
|
-
const fundingSignatures = await this.
|
|
3498
|
+
const fundingSignatures = await this.CreateFundingSigs(
|
|
3640
3499
|
dlcOffer,
|
|
3641
3500
|
dlcAccept,
|
|
3642
3501
|
dlcTxs,
|
|
@@ -3711,22 +3570,15 @@ Payout Group not found even with brute force search',
|
|
|
3711
3570
|
);
|
|
3712
3571
|
}
|
|
3713
3572
|
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
: [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
|
|
3719
|
-
|
|
3720
|
-
const p2ms = payments.p2ms({
|
|
3721
|
-
m: 2,
|
|
3722
|
-
pubkeys: fundingPubKeys,
|
|
3723
|
-
network,
|
|
3724
|
-
});
|
|
3573
|
+
const fundingPubKeys = orderPubkeysLexicographically(
|
|
3574
|
+
dlcOffer.fundingPubkey,
|
|
3575
|
+
dlcAccept.fundingPubkey,
|
|
3576
|
+
);
|
|
3725
3577
|
|
|
3726
|
-
const paymentVariant =
|
|
3727
|
-
|
|
3578
|
+
const paymentVariant = createP2WSHMultisigFromOrdered(
|
|
3579
|
+
fundingPubKeys,
|
|
3728
3580
|
network,
|
|
3729
|
-
|
|
3581
|
+
);
|
|
3730
3582
|
|
|
3731
3583
|
// Add the funding input with sequence from refund transaction
|
|
3732
3584
|
psbt.addInput({
|
|
@@ -3764,13 +3616,13 @@ Payout Group not found even with brute force search',
|
|
|
3764
3616
|
// This is the offerer's pubkey, use dlcSign.refundSignature
|
|
3765
3617
|
partialSigs.push({
|
|
3766
3618
|
pubkey: pubkey,
|
|
3767
|
-
signature:
|
|
3619
|
+
signature: ensureDerSignature(dlcSign.refundSignature),
|
|
3768
3620
|
});
|
|
3769
3621
|
} else if (Buffer.compare(pubkey, dlcAccept.fundingPubkey) === 0) {
|
|
3770
3622
|
// This is the accepter's pubkey, use dlcAccept.refundSignature
|
|
3771
3623
|
partialSigs.push({
|
|
3772
3624
|
pubkey: pubkey,
|
|
3773
|
-
signature:
|
|
3625
|
+
signature: ensureDerSignature(dlcAccept.refundSignature),
|
|
3774
3626
|
});
|
|
3775
3627
|
}
|
|
3776
3628
|
}
|
|
@@ -3843,21 +3695,11 @@ Payout Group not found even with brute force search',
|
|
|
3843
3695
|
const network = await this.getConnectedNetwork();
|
|
3844
3696
|
const psbt = new Psbt({ network });
|
|
3845
3697
|
|
|
3846
|
-
const
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
: [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
|
|
3850
|
-
|
|
3851
|
-
const p2ms = payments.p2ms({
|
|
3852
|
-
m: 2,
|
|
3853
|
-
pubkeys: fundingPubKeys,
|
|
3854
|
-
network,
|
|
3855
|
-
});
|
|
3856
|
-
|
|
3857
|
-
const paymentVariant = payments.p2wsh({
|
|
3858
|
-
redeem: p2ms,
|
|
3698
|
+
const paymentVariant = createP2WSHMultisig(
|
|
3699
|
+
dlcOffer.fundingPubkey,
|
|
3700
|
+
dlcAccept.fundingPubkey,
|
|
3859
3701
|
network,
|
|
3860
|
-
|
|
3702
|
+
);
|
|
3861
3703
|
|
|
3862
3704
|
// Initiate and build PSBT
|
|
3863
3705
|
let inputs: Input[] = _inputs;
|
|
@@ -4035,9 +3877,7 @@ Payout Group not found even with brute force search',
|
|
|
4035
3877
|
|
|
4036
3878
|
// Extract close signature from psbt and decode it to only extract r and s values
|
|
4037
3879
|
const closeSignature = await script.signature.decode(
|
|
4038
|
-
|
|
4039
|
-
psbt.data.inputs[fundingInputIndex].partialSig[0].signature,
|
|
4040
|
-
),
|
|
3880
|
+
ensureBuffer(psbt.data.inputs[fundingInputIndex].partialSig[0].signature),
|
|
4041
3881
|
).signature;
|
|
4042
3882
|
|
|
4043
3883
|
// Extract funding signatures from psbt
|
|
@@ -4049,9 +3889,9 @@ Payout Group not found even with brute force search',
|
|
|
4049
3889
|
const witnessElements: ScriptWitnessV0[][] = [];
|
|
4050
3890
|
for (let i = 0; i < inputSigs.length; i++) {
|
|
4051
3891
|
const sigWitness = new ScriptWitnessV0();
|
|
4052
|
-
sigWitness.witness =
|
|
3892
|
+
sigWitness.witness = ensureBuffer(inputSigs[i].signature);
|
|
4053
3893
|
const pubKeyWitness = new ScriptWitnessV0();
|
|
4054
|
-
pubKeyWitness.witness =
|
|
3894
|
+
pubKeyWitness.witness = ensureBuffer(inputSigs[i].pubkey);
|
|
4055
3895
|
witnessElements.push([sigWitness, pubKeyWitness]);
|
|
4056
3896
|
}
|
|
4057
3897
|
const fundingSignatures = new FundingSignatures();
|
|
@@ -4297,21 +4137,11 @@ Payout Group not found even with brute force search',
|
|
|
4297
4137
|
const network = await this.getConnectedNetwork();
|
|
4298
4138
|
const psbt = new Psbt({ network });
|
|
4299
4139
|
|
|
4300
|
-
const
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
: [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
|
|
4304
|
-
|
|
4305
|
-
const p2ms = payments.p2ms({
|
|
4306
|
-
m: 2,
|
|
4307
|
-
pubkeys: fundingPubKeys,
|
|
4140
|
+
const paymentVariant = createP2WSHMultisig(
|
|
4141
|
+
dlcOffer.fundingPubkey,
|
|
4142
|
+
dlcAccept.fundingPubkey,
|
|
4308
4143
|
network,
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
const paymentVariant = payments.p2wsh({
|
|
4312
|
-
redeem: p2ms,
|
|
4313
|
-
network,
|
|
4314
|
-
});
|
|
4144
|
+
);
|
|
4315
4145
|
|
|
4316
4146
|
// Make temporary array to hold all inputs and then sort them
|
|
4317
4147
|
// this method can be improved later
|
|
@@ -4637,24 +4467,18 @@ Payout Group not found even with brute force search',
|
|
|
4637
4467
|
|
|
4638
4468
|
// Use the same deterministic ordering as cfd-dlc-js: lexicographic by hex
|
|
4639
4469
|
// This matches GetOrderedPubkeys() in cfddlc_transactions.cpp
|
|
4640
|
-
const orderedPubkeys =
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
4470
|
+
const orderedPubkeys = orderPubkeysLexicographically(
|
|
4471
|
+
localPubkey,
|
|
4472
|
+
remotePubkey,
|
|
4473
|
+
);
|
|
4644
4474
|
|
|
4645
4475
|
const network = await this.getConnectedNetwork();
|
|
4646
4476
|
|
|
4647
4477
|
// Create 2-of-2 multisig payment using deterministic ordering
|
|
4648
|
-
const
|
|
4649
|
-
|
|
4650
|
-
pubkeys: orderedPubkeys,
|
|
4478
|
+
const paymentVariant = createP2WSHMultisigFromOrdered(
|
|
4479
|
+
orderedPubkeys,
|
|
4651
4480
|
network,
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
const paymentVariant = payments.p2wsh({
|
|
4655
|
-
redeem: p2ms,
|
|
4656
|
-
network,
|
|
4657
|
-
});
|
|
4481
|
+
);
|
|
4658
4482
|
|
|
4659
4483
|
const multisigAddress = paymentVariant.address!;
|
|
4660
4484
|
|