@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
|
@@ -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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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();
|
|
@@ -1218,8 +1042,40 @@ class BitcoinDdkProvider extends provider_1.default {
|
|
|
1218
1042
|
// Add the funding signatures to the PSBT as partial signatures
|
|
1219
1043
|
let witnessIndex = 0;
|
|
1220
1044
|
for (const fundingInput of signingPartyInputs) {
|
|
1221
|
-
//
|
|
1045
|
+
// Handle DLC inputs with multisig verification
|
|
1222
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
|
+
}
|
|
1223
1079
|
continue;
|
|
1224
1080
|
}
|
|
1225
1081
|
if (witnessIndex >= dlcSign.fundingSignatures.witnessElements.length) {
|
|
@@ -1239,7 +1095,7 @@ class BitcoinDdkProvider extends provider_1.default {
|
|
|
1239
1095
|
partialSig: [
|
|
1240
1096
|
{
|
|
1241
1097
|
pubkey: publicKey,
|
|
1242
|
-
signature:
|
|
1098
|
+
signature: (0, Utils_1.ensureDerSignature)(signature),
|
|
1243
1099
|
},
|
|
1244
1100
|
],
|
|
1245
1101
|
});
|
|
@@ -1281,12 +1137,10 @@ class BitcoinDdkProvider extends provider_1.default {
|
|
|
1281
1137
|
*/
|
|
1282
1138
|
async getFundingTransactionSighashDetails(dlcOffer, dlcAccept, dlcTxs) {
|
|
1283
1139
|
const transaction = bitcoinjs_lib_1.Transaction.fromBuffer(Buffer.from(dlcTxs.fundTx.serialize()));
|
|
1284
|
-
|
|
1285
|
-
const allFundingInputs = [
|
|
1140
|
+
const allFundingInputs = (0, Utils_1.sortFundingInputsBySerialId)([
|
|
1286
1141
|
...dlcOffer.fundingInputs,
|
|
1287
1142
|
...dlcAccept.fundingInputs,
|
|
1288
|
-
];
|
|
1289
|
-
allFundingInputs.sort((a, b) => Number(a.inputSerialId - b.inputSerialId));
|
|
1143
|
+
]);
|
|
1290
1144
|
// Calculate detailed sighash information
|
|
1291
1145
|
const details = [];
|
|
1292
1146
|
for (let i = 0; i < allFundingInputs.length; i++) {
|
|
@@ -1321,7 +1175,7 @@ class BitcoinDdkProvider extends provider_1.default {
|
|
|
1321
1175
|
? dlcAccept.refundSignature
|
|
1322
1176
|
: dlcSign.refundSignature;
|
|
1323
1177
|
// Ensure signature is in DER format (convert from compact if needed)
|
|
1324
|
-
const refundSignature =
|
|
1178
|
+
const refundSignature = (0, Utils_1.ensureDerSignature)(rawRefundSignature);
|
|
1325
1179
|
const signingPubkey = isOfferer
|
|
1326
1180
|
? dlcAccept.fundingPubkey
|
|
1327
1181
|
: dlcOffer.fundingPubkey;
|
|
@@ -1331,19 +1185,7 @@ class BitcoinDdkProvider extends provider_1.default {
|
|
|
1331
1185
|
}
|
|
1332
1186
|
// Create a PSBT for the refund transaction verification using same approach as createDlcClose
|
|
1333
1187
|
const psbt = new bitcoinjs_lib_1.Psbt({ network });
|
|
1334
|
-
|
|
1335
|
-
const fundingPubKeys = Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
|
|
1336
|
-
? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
|
|
1337
|
-
: [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
|
|
1338
|
-
const p2ms = bitcoinjs_lib_1.payments.p2ms({
|
|
1339
|
-
m: 2,
|
|
1340
|
-
pubkeys: fundingPubKeys,
|
|
1341
|
-
network,
|
|
1342
|
-
});
|
|
1343
|
-
const paymentVariant = bitcoinjs_lib_1.payments.p2wsh({
|
|
1344
|
-
redeem: p2ms,
|
|
1345
|
-
network,
|
|
1346
|
-
});
|
|
1188
|
+
const paymentVariant = (0, Utils_1.createP2WSHMultisig)(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey, network);
|
|
1347
1189
|
// Add the funding input with sequence from refund transaction
|
|
1348
1190
|
psbt.addInput({
|
|
1349
1191
|
hash: dlcTxs.fundTx.txId.serialize(),
|
|
@@ -1380,55 +1222,32 @@ class BitcoinDdkProvider extends provider_1.default {
|
|
|
1380
1222
|
}
|
|
1381
1223
|
CreateFundingScript(dlcOffer, dlcAccept) {
|
|
1382
1224
|
const network = this.getBitcoinJsNetwork();
|
|
1383
|
-
|
|
1384
|
-
const fundingPubKeys = Buffer.compare(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey) === -1
|
|
1385
|
-
? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
|
|
1386
|
-
: [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
|
|
1387
|
-
// Create 2-of-2 multisig script
|
|
1388
|
-
const p2ms = bitcoinjs_lib_1.payments.p2ms({
|
|
1389
|
-
m: 2,
|
|
1390
|
-
pubkeys: fundingPubKeys,
|
|
1391
|
-
network,
|
|
1392
|
-
});
|
|
1393
|
-
const paymentVariant = bitcoinjs_lib_1.payments.p2wsh({
|
|
1394
|
-
redeem: p2ms,
|
|
1395
|
-
network,
|
|
1396
|
-
});
|
|
1225
|
+
const paymentVariant = (0, Utils_1.createP2WSHMultisig)(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey, network);
|
|
1397
1226
|
return paymentVariant.redeem.output;
|
|
1398
1227
|
}
|
|
1399
1228
|
async CreateFundingTx(dlcOffer, dlcAccept, dlcSign, dlcTxs, fundingSignatures) {
|
|
1400
1229
|
const network = await this.getConnectedNetwork();
|
|
1401
1230
|
const psbt = new bitcoinjs_lib_1.Psbt({ network });
|
|
1402
|
-
|
|
1403
|
-
const allFundingInputs = [
|
|
1231
|
+
const allFundingInputs = (0, Utils_1.sortFundingInputsBySerialId)([
|
|
1404
1232
|
...dlcOffer.fundingInputs,
|
|
1405
1233
|
...dlcAccept.fundingInputs,
|
|
1406
|
-
];
|
|
1407
|
-
allFundingInputs.sort((a, b) => Number(a.inputSerialId - b.inputSerialId));
|
|
1234
|
+
]);
|
|
1408
1235
|
// Create a map of input txid:vout to witness elements
|
|
1409
1236
|
const witnessMap = new Map();
|
|
1410
|
-
// Map witness elements correctly -
|
|
1237
|
+
// Map witness elements correctly - CreateFundingSigs only creates witness elements
|
|
1411
1238
|
// for the party's own inputs, so we need to map them correctly
|
|
1412
|
-
// For dlcSign (offerer's signatures), map to offerer's inputs
|
|
1239
|
+
// For dlcSign (offerer's signatures), map to offerer's inputs (including DLC inputs)
|
|
1413
1240
|
let offererWitnessIndex = 0;
|
|
1414
1241
|
dlcOffer.fundingInputs.forEach((fundingInput) => {
|
|
1415
|
-
// Skip DLC inputs for now as in CreateFundingSigsAlt
|
|
1416
|
-
if (fundingInput.dlcInput) {
|
|
1417
|
-
return;
|
|
1418
|
-
}
|
|
1419
1242
|
if (offererWitnessIndex < dlcSign.fundingSignatures.witnessElements.length) {
|
|
1420
1243
|
const key = `${fundingInput.prevTx.txId.toString()}:${fundingInput.prevTxVout}`;
|
|
1421
1244
|
witnessMap.set(key, dlcSign.fundingSignatures.witnessElements[offererWitnessIndex]);
|
|
1422
1245
|
offererWitnessIndex++;
|
|
1423
1246
|
}
|
|
1424
1247
|
});
|
|
1425
|
-
// For fundingSignatures (accepter's signatures), map to accepter's inputs
|
|
1248
|
+
// For fundingSignatures (accepter's signatures), map to accepter's inputs (including DLC inputs)
|
|
1426
1249
|
let accepterWitnessIndex = 0;
|
|
1427
1250
|
dlcAccept.fundingInputs.forEach((fundingInput) => {
|
|
1428
|
-
// Skip DLC inputs for now as in CreateFundingSigsAlt
|
|
1429
|
-
if (fundingInput.dlcInput) {
|
|
1430
|
-
return;
|
|
1431
|
-
}
|
|
1432
1251
|
if (accepterWitnessIndex < fundingSignatures.witnessElements.length) {
|
|
1433
1252
|
const key = `${fundingInput.prevTx.txId.toString()}:${fundingInput.prevTxVout}`;
|
|
1434
1253
|
witnessMap.set(key, fundingSignatures.witnessElements[accepterWitnessIndex]);
|
|
@@ -1439,12 +1258,12 @@ class BitcoinDdkProvider extends provider_1.default {
|
|
|
1439
1258
|
const originalTransaction = bitcoinjs_lib_1.Transaction.fromBuffer(Buffer.from(dlcTxs.fundTx.serialize()));
|
|
1440
1259
|
for (const fundingInput of allFundingInputs) {
|
|
1441
1260
|
const prevOut = fundingInput.prevTx.outputs[fundingInput.prevTxVout];
|
|
1442
|
-
// Use same script handling as
|
|
1261
|
+
// Use same script handling as CreateFundingSigs
|
|
1443
1262
|
const witnessUtxo = {
|
|
1444
1263
|
script: Buffer.from(prevOut.scriptPubKey.serialize().subarray(1)),
|
|
1445
1264
|
value: Number(prevOut.value.sats),
|
|
1446
1265
|
};
|
|
1447
|
-
// Use sequence from the original transaction (same as
|
|
1266
|
+
// Use sequence from the original transaction (same as CreateFundingSigs)
|
|
1448
1267
|
const originalInput = originalTransaction.ins.find((input) => input.hash.reverse().toString('hex') ===
|
|
1449
1268
|
fundingInput.prevTx.txId.toString() &&
|
|
1450
1269
|
input.index === fundingInput.prevTxVout);
|
|
@@ -1471,12 +1290,57 @@ class BitcoinDdkProvider extends provider_1.default {
|
|
|
1471
1290
|
const inputKey = `${fundingInput.prevTx.txId.toString()}:${fundingInput.prevTxVout}`;
|
|
1472
1291
|
const witnessElements = witnessMap.get(inputKey);
|
|
1473
1292
|
if (witnessElements && witnessElements.length === 2) {
|
|
1474
|
-
//
|
|
1293
|
+
// Handle DLC inputs with multisig finalization
|
|
1475
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
|
+
}));
|
|
1476
1340
|
continue;
|
|
1477
1341
|
}
|
|
1478
1342
|
// For P2WPKH inputs, finalize the input with witness data
|
|
1479
|
-
const signature =
|
|
1343
|
+
const signature = (0, Utils_1.ensureDerSignature)(witnessElements[0].witness);
|
|
1480
1344
|
const publicKey = witnessElements[1].witness;
|
|
1481
1345
|
// Try a simpler approach - let bitcoinjs-lib handle witness construction
|
|
1482
1346
|
psbt.finalizeInput(inputIndex, () => ({
|
|
@@ -1537,8 +1401,7 @@ Payout Group found but incorrect group index');
|
|
|
1537
1401
|
Payout Group not found');
|
|
1538
1402
|
return { index: payoutIndexOffset + index, groupLength };
|
|
1539
1403
|
}
|
|
1540
|
-
async FindOutcomeIndexFromHyperbolaPayoutCurvePiece(
|
|
1541
|
-
const { dlcOffer } = (0, Utils_1.checkTypes)({ _dlcOffer });
|
|
1404
|
+
async FindOutcomeIndexFromHyperbolaPayoutCurvePiece(dlcOffer, contractDescriptor, contractOraclePairIndex, hyperbolaPayoutCurvePiece, oracleAttestation, outcome) {
|
|
1542
1405
|
const hyperbolaCurve = core_2.HyperbolaPayoutCurve.fromPayoutCurvePiece(hyperbolaPayoutCurvePiece);
|
|
1543
1406
|
const clampBN = (val) => bignumber_js_1.default.max(0, bignumber_js_1.default.min(val, dlcOffer.contractInfo.totalCollateral.toString()));
|
|
1544
1407
|
const payout = clampBN(hyperbolaCurve.getPayout(outcome));
|
|
@@ -1836,18 +1699,8 @@ Payout Group not found even with brute force search');
|
|
|
1836
1699
|
_dlcTxs,
|
|
1837
1700
|
});
|
|
1838
1701
|
const network = await this.getConnectedNetwork();
|
|
1839
|
-
const fundingPubKeys =
|
|
1840
|
-
|
|
1841
|
-
: [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
|
|
1842
|
-
const p2ms = bitcoinjs_lib_1.payments.p2ms({
|
|
1843
|
-
m: 2,
|
|
1844
|
-
pubkeys: fundingPubKeys,
|
|
1845
|
-
network,
|
|
1846
|
-
});
|
|
1847
|
-
const paymentVariant = bitcoinjs_lib_1.payments.p2wsh({
|
|
1848
|
-
redeem: p2ms,
|
|
1849
|
-
network,
|
|
1850
|
-
});
|
|
1702
|
+
const fundingPubKeys = (0, Utils_1.orderPubkeysLexicographically)(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey);
|
|
1703
|
+
const paymentVariant = (0, Utils_1.createP2WSHMultisigFromOrdered)(fundingPubKeys, network);
|
|
1851
1704
|
const sigHashRequestPromises = [];
|
|
1852
1705
|
const sigHashes = [];
|
|
1853
1706
|
for (let i = 0; i < rawCloseTxs.length; i++) {
|
|
@@ -1906,18 +1759,8 @@ Payout Group not found even with brute force search');
|
|
|
1906
1759
|
});
|
|
1907
1760
|
const dlcCloses = _dlcCloses.map((_dlcClose) => (0, Utils_1.checkTypes)({ _dlcClose }).dlcClose);
|
|
1908
1761
|
const network = await this.getConnectedNetwork();
|
|
1909
|
-
const fundingPubKeys =
|
|
1910
|
-
|
|
1911
|
-
: [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
|
|
1912
|
-
const p2ms = bitcoinjs_lib_1.payments.p2ms({
|
|
1913
|
-
m: 2,
|
|
1914
|
-
pubkeys: fundingPubKeys,
|
|
1915
|
-
network,
|
|
1916
|
-
});
|
|
1917
|
-
const paymentVariant = bitcoinjs_lib_1.payments.p2wsh({
|
|
1918
|
-
redeem: p2ms,
|
|
1919
|
-
network,
|
|
1920
|
-
});
|
|
1762
|
+
const fundingPubKeys = (0, Utils_1.orderPubkeysLexicographically)(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey);
|
|
1763
|
+
const paymentVariant = (0, Utils_1.createP2WSHMultisigFromOrdered)(fundingPubKeys, network);
|
|
1921
1764
|
const pubkey = isOfferer ? dlcAccept.fundingPubkey : dlcOffer.fundingPubkey;
|
|
1922
1765
|
const sigsValidity = [];
|
|
1923
1766
|
for (let i = 0; i < rawCloseTxs.length; i++) {
|
|
@@ -2028,8 +1871,7 @@ Payout Group not found even with brute force search');
|
|
|
2028
1871
|
changeSerialId = initResult.changeSerialId;
|
|
2029
1872
|
}
|
|
2030
1873
|
_fundingInputs.forEach((input) => (0, assert_1.default)(input.type === messaging_1.MessageType.FundingInput, 'FundingInput must be V0'));
|
|
2031
|
-
const fundingInputs =
|
|
2032
|
-
fundingInputs.sort((a, b) => Number(a.inputSerialId) - Number(b.inputSerialId));
|
|
1874
|
+
const fundingInputs = (0, Utils_1.sortFundingInputsBySerialId)(_fundingInputs);
|
|
2033
1875
|
const fundOutputSerialId = (0, Utils_1.generateSerialId)();
|
|
2034
1876
|
(0, assert_1.default)(changeSerialId !== fundOutputSerialId, 'changeSerialId cannot equal the fundOutputSerialId');
|
|
2035
1877
|
dlcOffer.contractFlags = Buffer.from('00', 'hex');
|
|
@@ -2143,8 +1985,7 @@ Payout Group not found even with brute force search');
|
|
|
2143
1985
|
changeSerialId = initResult.changeSerialId;
|
|
2144
1986
|
const _fundingInputs = initResult.fundingInputs;
|
|
2145
1987
|
_fundingInputs.forEach((input) => (0, assert_1.default)(input.type === messaging_1.MessageType.FundingInput, 'FundingInput must be V0'));
|
|
2146
|
-
fundingInputs = _fundingInputs.map((input) => input);
|
|
2147
|
-
fundingInputs.sort((a, b) => Number(a.inputSerialId) - Number(b.inputSerialId));
|
|
1988
|
+
fundingInputs = (0, Utils_1.sortFundingInputsBySerialId)(_fundingInputs.map((input) => input));
|
|
2148
1989
|
}
|
|
2149
1990
|
(0, assert_1.default)(Buffer.compare(dlcOffer.fundingPubkey, fundingPubKey) !== 0, 'DlcOffer and DlcAccept FundingPubKey cannot be the same');
|
|
2150
1991
|
const dlcAccept = new messaging_1.DlcAccept();
|
|
@@ -2182,7 +2023,7 @@ Payout Group not found even with brute force search');
|
|
|
2182
2023
|
const { dlcTransactions, messagesList } = await this.createDlcTxs(dlcOffer, dlcAccept);
|
|
2183
2024
|
const { cetSignatures, refundSignature } = await this.CreateCetAdaptorAndRefundSigs(dlcOffer, dlcAccept, dlcTransactions, messagesList, false);
|
|
2184
2025
|
const _dlcTransactions = dlcTransactions;
|
|
2185
|
-
const contractId =
|
|
2026
|
+
const contractId = (0, Utils_1.computeContractId)(_dlcTransactions.fundTx.txId.serialize(), _dlcTransactions.fundTxVout, dlcOffer.temporaryContractId);
|
|
2186
2027
|
_dlcTransactions.contractId = contractId;
|
|
2187
2028
|
dlcAccept.cetAdaptorSignatures = cetSignatures;
|
|
2188
2029
|
dlcAccept.refundSignature = refundSignature;
|
|
@@ -2202,10 +2043,9 @@ Payout Group not found even with brute force search');
|
|
|
2202
2043
|
const { dlcTransactions, messagesList } = await this.createDlcTxs(dlcOffer, dlcAccept);
|
|
2203
2044
|
await this.VerifyCetAdaptorAndRefundSigs(dlcOffer, dlcAccept, dlcSign, dlcTransactions, messagesList, true);
|
|
2204
2045
|
const { cetSignatures, refundSignature } = await this.CreateCetAdaptorAndRefundSigs(dlcOffer, dlcAccept, dlcTransactions, messagesList, true);
|
|
2205
|
-
const fundingSignatures = await this.
|
|
2046
|
+
const fundingSignatures = await this.CreateFundingSigs(dlcOffer, dlcAccept, dlcTransactions, true);
|
|
2206
2047
|
const dlcTxs = dlcTransactions;
|
|
2207
|
-
const contractId =
|
|
2208
|
-
(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);
|
|
2209
2049
|
dlcTxs.contractId = contractId;
|
|
2210
2050
|
dlcSign.contractId = contractId;
|
|
2211
2051
|
dlcSign.cetAdaptorSignatures = cetSignatures;
|
|
@@ -2223,6 +2063,8 @@ Payout Group not found even with brute force search');
|
|
|
2223
2063
|
*/
|
|
2224
2064
|
async finalizeDlcSign(dlcOffer, dlcAccept, dlcSign, dlcTxs) {
|
|
2225
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')}`);
|
|
2226
2068
|
if (dlcOffer.contractInfo.type === messaging_1.MessageType.SingleContractInfo &&
|
|
2227
2069
|
dlcOffer.contractInfo.contractDescriptor.type ===
|
|
2228
2070
|
messaging_1.MessageType.SingleContractInfo) {
|
|
@@ -2237,8 +2079,8 @@ Payout Group not found even with brute force search');
|
|
|
2237
2079
|
messagesList = oracleEventMessagesList;
|
|
2238
2080
|
}
|
|
2239
2081
|
await this.VerifyCetAdaptorAndRefundSigs(dlcOffer, dlcAccept, dlcSign, dlcTxs, messagesList, false);
|
|
2240
|
-
await this.
|
|
2241
|
-
const fundingSignatures = await this.
|
|
2082
|
+
await this.VerifyFundingSigs(dlcOffer, dlcAccept, dlcSign, dlcTxs, false);
|
|
2083
|
+
const fundingSignatures = await this.CreateFundingSigs(dlcOffer, dlcAccept, dlcTxs, false);
|
|
2242
2084
|
const fundTx = await this.CreateFundingTx(dlcOffer, dlcAccept, dlcSign, dlcTxs, fundingSignatures);
|
|
2243
2085
|
return fundTx;
|
|
2244
2086
|
}
|
|
@@ -2273,19 +2115,8 @@ Payout Group not found even with brute force search');
|
|
|
2273
2115
|
if (Number(dlcTxs.refundTx.locktime) !== dlcOffer.refundLocktime) {
|
|
2274
2116
|
throw new Error(`Refund transaction locktime ${dlcTxs.refundTx.locktime} does not match expected ${dlcOffer.refundLocktime}`);
|
|
2275
2117
|
}
|
|
2276
|
-
|
|
2277
|
-
const
|
|
2278
|
-
? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
|
|
2279
|
-
: [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
|
|
2280
|
-
const p2ms = bitcoinjs_lib_1.payments.p2ms({
|
|
2281
|
-
m: 2,
|
|
2282
|
-
pubkeys: fundingPubKeys,
|
|
2283
|
-
network,
|
|
2284
|
-
});
|
|
2285
|
-
const paymentVariant = bitcoinjs_lib_1.payments.p2wsh({
|
|
2286
|
-
redeem: p2ms,
|
|
2287
|
-
network,
|
|
2288
|
-
});
|
|
2118
|
+
const fundingPubKeys = (0, Utils_1.orderPubkeysLexicographically)(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey);
|
|
2119
|
+
const paymentVariant = (0, Utils_1.createP2WSHMultisigFromOrdered)(fundingPubKeys, network);
|
|
2289
2120
|
// Add the funding input with sequence from refund transaction
|
|
2290
2121
|
psbt.addInput({
|
|
2291
2122
|
hash: dlcTxs.fundTx.txId.serialize(),
|
|
@@ -2315,14 +2146,14 @@ Payout Group not found even with brute force search');
|
|
|
2315
2146
|
// This is the offerer's pubkey, use dlcSign.refundSignature
|
|
2316
2147
|
partialSigs.push({
|
|
2317
2148
|
pubkey: pubkey,
|
|
2318
|
-
signature:
|
|
2149
|
+
signature: (0, Utils_1.ensureDerSignature)(dlcSign.refundSignature),
|
|
2319
2150
|
});
|
|
2320
2151
|
}
|
|
2321
2152
|
else if (Buffer.compare(pubkey, dlcAccept.fundingPubkey) === 0) {
|
|
2322
2153
|
// This is the accepter's pubkey, use dlcAccept.refundSignature
|
|
2323
2154
|
partialSigs.push({
|
|
2324
2155
|
pubkey: pubkey,
|
|
2325
|
-
signature:
|
|
2156
|
+
signature: (0, Utils_1.ensureDerSignature)(dlcAccept.refundSignature),
|
|
2326
2157
|
});
|
|
2327
2158
|
}
|
|
2328
2159
|
}
|
|
@@ -2372,18 +2203,7 @@ Payout Group not found even with brute force search');
|
|
|
2372
2203
|
isOfferer = await this.isOfferer(dlcOffer, dlcAccept);
|
|
2373
2204
|
const network = await this.getConnectedNetwork();
|
|
2374
2205
|
const psbt = new bitcoinjs_lib_1.Psbt({ network });
|
|
2375
|
-
const
|
|
2376
|
-
? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
|
|
2377
|
-
: [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
|
|
2378
|
-
const p2ms = bitcoinjs_lib_1.payments.p2ms({
|
|
2379
|
-
m: 2,
|
|
2380
|
-
pubkeys: fundingPubKeys,
|
|
2381
|
-
network,
|
|
2382
|
-
});
|
|
2383
|
-
const paymentVariant = bitcoinjs_lib_1.payments.p2wsh({
|
|
2384
|
-
redeem: p2ms,
|
|
2385
|
-
network,
|
|
2386
|
-
});
|
|
2206
|
+
const paymentVariant = (0, Utils_1.createP2WSHMultisig)(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey, network);
|
|
2387
2207
|
// Initiate and build PSBT
|
|
2388
2208
|
let inputs = _inputs;
|
|
2389
2209
|
if (!_inputs) {
|
|
@@ -2483,7 +2303,7 @@ Payout Group not found even with brute force search');
|
|
|
2483
2303
|
return ecc.verify(msghash, pubkey, signature);
|
|
2484
2304
|
});
|
|
2485
2305
|
// Extract close signature from psbt and decode it to only extract r and s values
|
|
2486
|
-
const closeSignature = await bitcoinjs_lib_1.script.signature.decode(
|
|
2306
|
+
const closeSignature = await bitcoinjs_lib_1.script.signature.decode((0, Utils_1.ensureBuffer)(psbt.data.inputs[fundingInputIndex].partialSig[0].signature)).signature;
|
|
2487
2307
|
// Extract funding signatures from psbt
|
|
2488
2308
|
const inputSigs = psbt.data.inputs
|
|
2489
2309
|
.filter((input) => input !== fundingInputIndex)
|
|
@@ -2492,9 +2312,9 @@ Payout Group not found even with brute force search');
|
|
|
2492
2312
|
const witnessElements = [];
|
|
2493
2313
|
for (let i = 0; i < inputSigs.length; i++) {
|
|
2494
2314
|
const sigWitness = new messaging_1.ScriptWitnessV0();
|
|
2495
|
-
sigWitness.witness =
|
|
2315
|
+
sigWitness.witness = (0, Utils_1.ensureBuffer)(inputSigs[i].signature);
|
|
2496
2316
|
const pubKeyWitness = new messaging_1.ScriptWitnessV0();
|
|
2497
|
-
pubKeyWitness.witness =
|
|
2317
|
+
pubKeyWitness.witness = (0, Utils_1.ensureBuffer)(inputSigs[i].pubkey);
|
|
2498
2318
|
witnessElements.push([sigWitness, pubKeyWitness]);
|
|
2499
2319
|
}
|
|
2500
2320
|
const fundingSignatures = new messaging_1.FundingSignatures();
|
|
@@ -2624,18 +2444,7 @@ Payout Group not found even with brute force search');
|
|
|
2624
2444
|
dlcClose.validate();
|
|
2625
2445
|
const network = await this.getConnectedNetwork();
|
|
2626
2446
|
const psbt = new bitcoinjs_lib_1.Psbt({ network });
|
|
2627
|
-
const
|
|
2628
|
-
? [dlcOffer.fundingPubkey, dlcAccept.fundingPubkey]
|
|
2629
|
-
: [dlcAccept.fundingPubkey, dlcOffer.fundingPubkey];
|
|
2630
|
-
const p2ms = bitcoinjs_lib_1.payments.p2ms({
|
|
2631
|
-
m: 2,
|
|
2632
|
-
pubkeys: fundingPubKeys,
|
|
2633
|
-
network,
|
|
2634
|
-
});
|
|
2635
|
-
const paymentVariant = bitcoinjs_lib_1.payments.p2wsh({
|
|
2636
|
-
redeem: p2ms,
|
|
2637
|
-
network,
|
|
2638
|
-
});
|
|
2447
|
+
const paymentVariant = (0, Utils_1.createP2WSHMultisig)(dlcOffer.fundingPubkey, dlcAccept.fundingPubkey, network);
|
|
2639
2448
|
// Make temporary array to hold all inputs and then sort them
|
|
2640
2449
|
// this method can be improved later
|
|
2641
2450
|
const psbtInputs = [];
|
|
@@ -2857,20 +2666,10 @@ Payout Group not found even with brute force search');
|
|
|
2857
2666
|
const remotePubkey = Buffer.from(dlcInputInfo.remoteFundPubkey, 'hex');
|
|
2858
2667
|
// Use the same deterministic ordering as cfd-dlc-js: lexicographic by hex
|
|
2859
2668
|
// This matches GetOrderedPubkeys() in cfddlc_transactions.cpp
|
|
2860
|
-
const orderedPubkeys =
|
|
2861
|
-
? [localPubkey, remotePubkey]
|
|
2862
|
-
: [remotePubkey, localPubkey];
|
|
2669
|
+
const orderedPubkeys = (0, Utils_1.orderPubkeysLexicographically)(localPubkey, remotePubkey);
|
|
2863
2670
|
const network = await this.getConnectedNetwork();
|
|
2864
2671
|
// Create 2-of-2 multisig payment using deterministic ordering
|
|
2865
|
-
const
|
|
2866
|
-
m: 2,
|
|
2867
|
-
pubkeys: orderedPubkeys,
|
|
2868
|
-
network,
|
|
2869
|
-
});
|
|
2870
|
-
const paymentVariant = bitcoinjs_lib_1.payments.p2wsh({
|
|
2871
|
-
redeem: p2ms,
|
|
2872
|
-
network,
|
|
2873
|
-
});
|
|
2672
|
+
const paymentVariant = (0, Utils_1.createP2WSHMultisigFromOrdered)(orderedPubkeys, network);
|
|
2874
2673
|
const multisigAddress = paymentVariant.address;
|
|
2875
2674
|
// Verify this matches the actual funding output address
|
|
2876
2675
|
const actualFundingOutput = tx.outputs[dlcInputInfo.fundVout];
|