@atomicfinance/bitcoin-dlc-provider 3.3.0 → 3.4.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.
- package/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-test.log +1 -1
- package/CHANGELOG.md +25 -0
- package/dist/BitcoinDlcProvider.d.ts +35 -2
- package/dist/BitcoinDlcProvider.js +307 -1
- package/dist/BitcoinDlcProvider.js.map +1 -1
- package/dist/utils/Utils.d.ts +1 -0
- package/dist/utils/Utils.js +5 -1
- package/dist/utils/Utils.js.map +1 -1
- package/lib/BitcoinDlcProvider.ts +681 -2
- package/lib/utils/Utils.ts +4 -0
- package/package.json +5 -5
|
@@ -8,6 +8,10 @@ import {
|
|
|
8
8
|
AddSignatureToFundTransactionResponse,
|
|
9
9
|
bitcoin,
|
|
10
10
|
CalculateEcSignatureRequest,
|
|
11
|
+
CreateBatchDlcTransactionsRequest,
|
|
12
|
+
CreateBatchDlcTransactionsResponse,
|
|
13
|
+
CreateBatchFundTransactionRequest,
|
|
14
|
+
CreateBatchFundTransactionResponse,
|
|
11
15
|
CreateCetAdaptorSignatureRequest,
|
|
12
16
|
CreateCetAdaptorSignatureResponse,
|
|
13
17
|
CreateCetAdaptorSignaturesRequest,
|
|
@@ -104,6 +108,7 @@ import {
|
|
|
104
108
|
asyncForEach,
|
|
105
109
|
checkTypes,
|
|
106
110
|
generateSerialId,
|
|
111
|
+
generateSerialIds,
|
|
107
112
|
outputsToPayouts,
|
|
108
113
|
} from './utils/Utils';
|
|
109
114
|
|
|
@@ -244,6 +249,71 @@ export default class BitcoinDlcProvider
|
|
|
244
249
|
};
|
|
245
250
|
}
|
|
246
251
|
|
|
252
|
+
private async BatchInitialize(
|
|
253
|
+
collaterals: bigint[],
|
|
254
|
+
feeRatePerVb: bigint,
|
|
255
|
+
fixedInputs: Input[],
|
|
256
|
+
): Promise<BatchInitializeResponse> {
|
|
257
|
+
const network = await this.getConnectedNetwork();
|
|
258
|
+
|
|
259
|
+
const collateral = collaterals.reduce((a, b) => a + b, BigInt(0));
|
|
260
|
+
|
|
261
|
+
const inputs: Input[] = await this.GetInputsForAmount(
|
|
262
|
+
collateral,
|
|
263
|
+
feeRatePerVb,
|
|
264
|
+
fixedInputs,
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
const fundingInputs: FundingInput[] = await Promise.all(
|
|
268
|
+
inputs.map(async (input) => {
|
|
269
|
+
return this.inputToFundingInput(input);
|
|
270
|
+
}),
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
const initializeResponses: BatchBaseInitializeResponse[] = [];
|
|
274
|
+
|
|
275
|
+
const changeSerialId: bigint = generateSerialId();
|
|
276
|
+
|
|
277
|
+
const changeAddress: Address = await this.client.wallet.getUnusedAddress(
|
|
278
|
+
true,
|
|
279
|
+
);
|
|
280
|
+
const changeSPK: Buffer = address.toOutputScript(
|
|
281
|
+
changeAddress.address,
|
|
282
|
+
network,
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
for (let i = 0; i < collaterals.length; i++) {
|
|
286
|
+
const payoutAddress: Address = await this.client.wallet.getUnusedAddress(
|
|
287
|
+
false,
|
|
288
|
+
);
|
|
289
|
+
const payoutSPK: Buffer = address.toOutputScript(
|
|
290
|
+
payoutAddress.address,
|
|
291
|
+
network,
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
const fundingAddress: Address = await this.client.wallet.getUnusedAddress(
|
|
295
|
+
false,
|
|
296
|
+
);
|
|
297
|
+
const fundingPubKey: Buffer = Buffer.from(
|
|
298
|
+
fundingAddress.publicKey,
|
|
299
|
+
'hex',
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
if (fundingAddress.address === payoutAddress.address)
|
|
303
|
+
throw Error('Address reuse');
|
|
304
|
+
|
|
305
|
+
const payoutSerialId: bigint = generateSerialId();
|
|
306
|
+
|
|
307
|
+
initializeResponses.push({
|
|
308
|
+
fundingPubKey,
|
|
309
|
+
payoutSPK,
|
|
310
|
+
payoutSerialId,
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return { fundingInputs, initializeResponses, changeSerialId, changeSPK };
|
|
315
|
+
}
|
|
316
|
+
|
|
247
317
|
/**
|
|
248
318
|
* TODO: Add GetPayoutFromOutcomes
|
|
249
319
|
*
|
|
@@ -563,6 +633,150 @@ export default class BitcoinDlcProvider
|
|
|
563
633
|
return { dlcTransactions, messagesList };
|
|
564
634
|
}
|
|
565
635
|
|
|
636
|
+
public async createBatchDlcTxs(
|
|
637
|
+
_dlcOffers: DlcOffer[],
|
|
638
|
+
_dlcAccepts: DlcAccept[],
|
|
639
|
+
): Promise<CreateBatchDlcTxsResponse> {
|
|
640
|
+
const dlcOffers = _dlcOffers.map((dlcOffer) => {
|
|
641
|
+
return checkTypes({ _dlcOffer: dlcOffer }).dlcOffer;
|
|
642
|
+
});
|
|
643
|
+
const dlcAccepts = _dlcAccepts.map((dlcAccept) => {
|
|
644
|
+
return checkTypes({ _dlcAccept: dlcAccept }).dlcAccept;
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
const localFundPubkeys = dlcOffers.map((dlcOffer) =>
|
|
648
|
+
dlcOffer.fundingPubKey.toString('hex'),
|
|
649
|
+
);
|
|
650
|
+
const remoteFundPubkeys = dlcAccepts.map((dlcAccept) =>
|
|
651
|
+
dlcAccept.fundingPubKey.toString('hex'),
|
|
652
|
+
);
|
|
653
|
+
const localFinalScriptPubkeys = dlcOffers.map((dlcOffer) =>
|
|
654
|
+
dlcOffer.payoutSPK.toString('hex'),
|
|
655
|
+
);
|
|
656
|
+
const remoteFinalScriptPubkeys = dlcAccepts.map((dlcAccept) =>
|
|
657
|
+
dlcAccept.payoutSPK.toString('hex'),
|
|
658
|
+
);
|
|
659
|
+
const localChangeScriptPubkey = dlcOffers[0].changeSPK.toString('hex');
|
|
660
|
+
const remoteChangeScriptPubkey = dlcAccepts[0].changeSPK.toString('hex');
|
|
661
|
+
|
|
662
|
+
const localInputs: Utxo[] = await Promise.all(
|
|
663
|
+
dlcOffers[0].fundingInputs.map(async (fundingInput) => {
|
|
664
|
+
const input = await this.fundingInputToInput(fundingInput, false);
|
|
665
|
+
return input.toUtxo();
|
|
666
|
+
}),
|
|
667
|
+
);
|
|
668
|
+
|
|
669
|
+
const remoteInputs: Utxo[] = await Promise.all(
|
|
670
|
+
dlcAccepts[0].fundingInputs.map(async (fundingInput) => {
|
|
671
|
+
const input = await this.fundingInputToInput(fundingInput, false);
|
|
672
|
+
return input.toUtxo();
|
|
673
|
+
}),
|
|
674
|
+
);
|
|
675
|
+
|
|
676
|
+
const localInputAmount = localInputs.reduce<number>(
|
|
677
|
+
(prev, cur) => prev + cur.amount.GetSatoshiAmount(),
|
|
678
|
+
0,
|
|
679
|
+
);
|
|
680
|
+
|
|
681
|
+
const remoteInputAmount = remoteInputs.reduce<number>(
|
|
682
|
+
(prev, cur) => prev + cur.amount.GetSatoshiAmount(),
|
|
683
|
+
0,
|
|
684
|
+
);
|
|
685
|
+
|
|
686
|
+
const localPayouts: (bigint | number)[] = [];
|
|
687
|
+
const remotePayouts: (bigint | number)[] = [];
|
|
688
|
+
const numPayouts: (bigint | number)[] = [];
|
|
689
|
+
|
|
690
|
+
const nestedMessagesList: Messages[][] = [];
|
|
691
|
+
|
|
692
|
+
// loop through all dlc offers, get payouts, and add to localPayouts and remotePayouts
|
|
693
|
+
for (const dlcOffer of dlcOffers) {
|
|
694
|
+
const payoutResponses = this.GetPayouts(dlcOffer);
|
|
695
|
+
const { payouts, messagesList } = this.FlattenPayouts(payoutResponses);
|
|
696
|
+
const tempLocalPayouts = payouts.map((payout) => payout.local);
|
|
697
|
+
const tempRemotePayouts = payouts.map((payout) => payout.remote);
|
|
698
|
+
localPayouts.push(...tempLocalPayouts);
|
|
699
|
+
remotePayouts.push(...tempRemotePayouts);
|
|
700
|
+
numPayouts.push(tempLocalPayouts.length);
|
|
701
|
+
nestedMessagesList.push(messagesList);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
const batchDlcTxRequest: CreateBatchDlcTransactionsRequest = {
|
|
705
|
+
localPayouts,
|
|
706
|
+
remotePayouts,
|
|
707
|
+
numPayouts,
|
|
708
|
+
localFundPubkeys,
|
|
709
|
+
localFinalScriptPubkeys,
|
|
710
|
+
remoteFundPubkeys,
|
|
711
|
+
remoteFinalScriptPubkeys,
|
|
712
|
+
localInputAmount,
|
|
713
|
+
localCollateralAmounts: dlcOffers.map(
|
|
714
|
+
(dlcOffer) => dlcOffer.offerCollateralSatoshis,
|
|
715
|
+
),
|
|
716
|
+
localPayoutSerialIds: dlcOffers.map(
|
|
717
|
+
(dlcOffer) => dlcOffer.payoutSerialId,
|
|
718
|
+
),
|
|
719
|
+
localChangeSerialId: dlcOffers[0].changeSerialId,
|
|
720
|
+
remoteInputAmount,
|
|
721
|
+
remoteCollateralAmounts: dlcAccepts.map(
|
|
722
|
+
(dlcAccept) => dlcAccept.acceptCollateralSatoshis,
|
|
723
|
+
),
|
|
724
|
+
remotePayoutSerialIds: dlcAccepts.map(
|
|
725
|
+
(dlcAccept) => dlcAccept.payoutSerialId,
|
|
726
|
+
),
|
|
727
|
+
remoteChangeSerialId: dlcAccepts[0].changeSerialId,
|
|
728
|
+
refundLocktimes: dlcOffers.map((dlcOffer) => dlcOffer.refundLocktime),
|
|
729
|
+
localInputs,
|
|
730
|
+
remoteInputs,
|
|
731
|
+
localChangeScriptPubkey,
|
|
732
|
+
remoteChangeScriptPubkey,
|
|
733
|
+
feeRate: Number(dlcOffers[0].feeRatePerVb),
|
|
734
|
+
cetLockTime: dlcOffers[0].cetLocktime,
|
|
735
|
+
fundOutputSerialIds: dlcOffers.map(
|
|
736
|
+
(dlcOffer) => dlcOffer.fundOutputSerialId,
|
|
737
|
+
),
|
|
738
|
+
};
|
|
739
|
+
|
|
740
|
+
const dlcTxs = await this.CreateBatchDlcTransactions(batchDlcTxRequest);
|
|
741
|
+
|
|
742
|
+
const dlcTransactionsList: DlcTransactionsV0[] = [];
|
|
743
|
+
|
|
744
|
+
let start = 0;
|
|
745
|
+
for (let i = 0; i < dlcTxs.refundTxHexList.length; i++) {
|
|
746
|
+
const dlcTransactions = new DlcTransactionsV0();
|
|
747
|
+
|
|
748
|
+
dlcTransactions.fundTx = Tx.decode(
|
|
749
|
+
StreamReader.fromHex(dlcTxs.fundTxHex),
|
|
750
|
+
);
|
|
751
|
+
|
|
752
|
+
dlcTransactions.fundTxVout = [
|
|
753
|
+
BigInt(dlcOffers[i].changeSerialId),
|
|
754
|
+
BigInt(dlcAccepts[i].changeSerialId),
|
|
755
|
+
...dlcOffers.map((dlcOffer) => dlcOffer.fundOutputSerialId),
|
|
756
|
+
]
|
|
757
|
+
.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0))
|
|
758
|
+
.findIndex(
|
|
759
|
+
(j) => BigInt(j) === BigInt(dlcOffers[i].fundOutputSerialId),
|
|
760
|
+
);
|
|
761
|
+
|
|
762
|
+
dlcTransactions.refundTx = Tx.decode(
|
|
763
|
+
StreamReader.fromHex(dlcTxs.refundTxHexList[i]),
|
|
764
|
+
);
|
|
765
|
+
|
|
766
|
+
// slice cetsHexList based on numPayouts
|
|
767
|
+
const end = start + Number(numPayouts[i]);
|
|
768
|
+
const cetsHexList = dlcTxs.cetsHexList.slice(start, end);
|
|
769
|
+
start = end;
|
|
770
|
+
dlcTransactions.cets = cetsHexList.map((cetHex) => {
|
|
771
|
+
return Tx.decode(StreamReader.fromHex(cetHex));
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
dlcTransactionsList.push(dlcTransactions);
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
return { dlcTransactionsList, nestedMessagesList };
|
|
778
|
+
}
|
|
779
|
+
|
|
566
780
|
private GenerateEnumMessages(oracleEvent: OracleEventV0): Messages[] {
|
|
567
781
|
throw Error('Only DigitDecomposition Oracle Events supported');
|
|
568
782
|
}
|
|
@@ -1154,7 +1368,9 @@ Payout Group not found',
|
|
|
1154
1368
|
groupLength = payoutGroup.groups[groupIndex].length;
|
|
1155
1369
|
break;
|
|
1156
1370
|
}
|
|
1157
|
-
} else if (
|
|
1371
|
+
} else if (
|
|
1372
|
+
payoutGroup.payout === BigInt(Math.round(Number(payout.toString())))
|
|
1373
|
+
) {
|
|
1158
1374
|
// Edge case to account for case where payout is maximum payout for DLC
|
|
1159
1375
|
// But rounded payout does not round down
|
|
1160
1376
|
if (payoutGroups[i - 1].payout === roundedPayout) {
|
|
@@ -1871,6 +2087,109 @@ Payout Group found but incorrect group index',
|
|
|
1871
2087
|
return dlcOffer;
|
|
1872
2088
|
}
|
|
1873
2089
|
|
|
2090
|
+
async batchCreateDlcOffer(
|
|
2091
|
+
contractInfos: ContractInfo[],
|
|
2092
|
+
offerCollaterals: bigint[],
|
|
2093
|
+
feeRatePerVb: bigint,
|
|
2094
|
+
cetLocktime: number,
|
|
2095
|
+
refundLocktimes: number[],
|
|
2096
|
+
fixedInputs?: Input[],
|
|
2097
|
+
): Promise<DlcOffer[]> {
|
|
2098
|
+
if (
|
|
2099
|
+
contractInfos.length !== offerCollaterals.length ||
|
|
2100
|
+
contractInfos.length !== refundLocktimes.length
|
|
2101
|
+
) {
|
|
2102
|
+
throw new Error(
|
|
2103
|
+
'The number of contractInfos, offerCollateralSatoshis, and refundLocktimes must be the same',
|
|
2104
|
+
);
|
|
2105
|
+
}
|
|
2106
|
+
|
|
2107
|
+
const dlcOffers: DlcOfferV0[] = [];
|
|
2108
|
+
|
|
2109
|
+
for (let i = 0; i < contractInfos.length; i++) {
|
|
2110
|
+
contractInfos[i].validate();
|
|
2111
|
+
}
|
|
2112
|
+
|
|
2113
|
+
const network = await this.getConnectedNetwork();
|
|
2114
|
+
|
|
2115
|
+
const {
|
|
2116
|
+
fundingInputs: _fundingInputs,
|
|
2117
|
+
changeSPK,
|
|
2118
|
+
changeSerialId,
|
|
2119
|
+
initializeResponses,
|
|
2120
|
+
} = await this.BatchInitialize(offerCollaterals, feeRatePerVb, fixedInputs);
|
|
2121
|
+
|
|
2122
|
+
_fundingInputs.forEach((input) =>
|
|
2123
|
+
assert(
|
|
2124
|
+
input.type === MessageType.FundingInputV0,
|
|
2125
|
+
'FundingInput must be V0',
|
|
2126
|
+
),
|
|
2127
|
+
);
|
|
2128
|
+
|
|
2129
|
+
const fundingInputs: FundingInputV0[] = _fundingInputs.map(
|
|
2130
|
+
(input) => input as FundingInputV0,
|
|
2131
|
+
);
|
|
2132
|
+
|
|
2133
|
+
fundingInputs.sort(
|
|
2134
|
+
(a, b) => Number(a.inputSerialId) - Number(b.inputSerialId),
|
|
2135
|
+
);
|
|
2136
|
+
|
|
2137
|
+
const fundOutputsSerialIds = generateSerialIds(contractInfos.length);
|
|
2138
|
+
|
|
2139
|
+
for (let i = 0; i < contractInfos.length; i++) {
|
|
2140
|
+
const contractInfo = contractInfos[i];
|
|
2141
|
+
const offerCollateralSatoshis = offerCollaterals[i];
|
|
2142
|
+
const fundOutputSerialId = fundOutputsSerialIds[i];
|
|
2143
|
+
const { fundingPubKey, payoutSPK, payoutSerialId } = initializeResponses[
|
|
2144
|
+
i
|
|
2145
|
+
];
|
|
2146
|
+
const refundLocktime = refundLocktimes[i];
|
|
2147
|
+
|
|
2148
|
+
const dlcOffer = new DlcOfferV0();
|
|
2149
|
+
|
|
2150
|
+
dlcOffer.contractFlags = Buffer.from('00', 'hex');
|
|
2151
|
+
dlcOffer.chainHash = chainHashFromNetwork(network);
|
|
2152
|
+
dlcOffer.contractInfo = contractInfo;
|
|
2153
|
+
dlcOffer.fundingPubKey = fundingPubKey;
|
|
2154
|
+
dlcOffer.payoutSPK = payoutSPK;
|
|
2155
|
+
dlcOffer.payoutSerialId = payoutSerialId;
|
|
2156
|
+
dlcOffer.offerCollateralSatoshis = offerCollateralSatoshis;
|
|
2157
|
+
dlcOffer.fundingInputs = fundingInputs;
|
|
2158
|
+
dlcOffer.changeSPK = changeSPK;
|
|
2159
|
+
dlcOffer.changeSerialId = changeSerialId;
|
|
2160
|
+
dlcOffer.fundOutputSerialId = fundOutputSerialId;
|
|
2161
|
+
dlcOffer.feeRatePerVb = feeRatePerVb;
|
|
2162
|
+
dlcOffer.cetLocktime = cetLocktime;
|
|
2163
|
+
dlcOffer.refundLocktime = refundLocktime;
|
|
2164
|
+
|
|
2165
|
+
assert(
|
|
2166
|
+
(() => {
|
|
2167
|
+
const finalizer = new DualFundingTxFinalizer(
|
|
2168
|
+
dlcOffer.fundingInputs,
|
|
2169
|
+
dlcOffer.payoutSPK,
|
|
2170
|
+
dlcOffer.changeSPK,
|
|
2171
|
+
null,
|
|
2172
|
+
null,
|
|
2173
|
+
null,
|
|
2174
|
+
dlcOffer.feeRatePerVb,
|
|
2175
|
+
);
|
|
2176
|
+
const funding = fundingInputs.reduce((total, input) => {
|
|
2177
|
+
return total + input.prevTx.outputs[input.prevTxVout].value.sats;
|
|
2178
|
+
}, BigInt(0));
|
|
2179
|
+
|
|
2180
|
+
return funding >= offerCollateralSatoshis + finalizer.offerFees;
|
|
2181
|
+
})(),
|
|
2182
|
+
'fundingInputs for dlcOffer must be greater than offerCollateralSatoshis plus offerFees',
|
|
2183
|
+
);
|
|
2184
|
+
|
|
2185
|
+
dlcOffer.validate();
|
|
2186
|
+
|
|
2187
|
+
dlcOffers.push(dlcOffer);
|
|
2188
|
+
}
|
|
2189
|
+
|
|
2190
|
+
return dlcOffers;
|
|
2191
|
+
}
|
|
2192
|
+
|
|
1874
2193
|
/**
|
|
1875
2194
|
* Accept DLC Offer
|
|
1876
2195
|
* @param _dlcOffer Dlc Offer Message
|
|
@@ -2017,6 +2336,168 @@ Payout Group found but incorrect group index',
|
|
|
2017
2336
|
return { dlcAccept, dlcTransactions: _dlcTransactions };
|
|
2018
2337
|
}
|
|
2019
2338
|
|
|
2339
|
+
async batchAcceptDlcOffer(
|
|
2340
|
+
_dlcOffers: DlcOffer[],
|
|
2341
|
+
fixedInputs?: Input[],
|
|
2342
|
+
): Promise<BatchAcceptDlcOfferResponse> {
|
|
2343
|
+
const dlcOffers = _dlcOffers.map((_dlcOffer) => {
|
|
2344
|
+
const { dlcOffer } = checkTypes({ _dlcOffer });
|
|
2345
|
+
dlcOffer.validate();
|
|
2346
|
+
return dlcOffer;
|
|
2347
|
+
});
|
|
2348
|
+
|
|
2349
|
+
const acceptCollaterals = dlcOffers.map(
|
|
2350
|
+
(dlcOffer) =>
|
|
2351
|
+
dlcOffer.contractInfo.totalCollateral -
|
|
2352
|
+
dlcOffer.offerCollateralSatoshis,
|
|
2353
|
+
);
|
|
2354
|
+
|
|
2355
|
+
const {
|
|
2356
|
+
fundingInputs: _fundingInputs,
|
|
2357
|
+
changeSPK,
|
|
2358
|
+
changeSerialId,
|
|
2359
|
+
initializeResponses,
|
|
2360
|
+
} = await this.BatchInitialize(
|
|
2361
|
+
acceptCollaterals,
|
|
2362
|
+
dlcOffers[0].feeRatePerVb,
|
|
2363
|
+
fixedInputs,
|
|
2364
|
+
);
|
|
2365
|
+
|
|
2366
|
+
// Check that none of the funding pubkeys are the same between the
|
|
2367
|
+
// dlcOffers and the dlcAccepts (from initializeResponses)
|
|
2368
|
+
dlcOffers.forEach((dlcOffer) => {
|
|
2369
|
+
initializeResponses.forEach((initializeResponse) => {
|
|
2370
|
+
assert(
|
|
2371
|
+
Buffer.compare(
|
|
2372
|
+
dlcOffer.fundingPubKey,
|
|
2373
|
+
initializeResponse.fundingPubKey,
|
|
2374
|
+
) !== 0,
|
|
2375
|
+
'DlcOffer and DlcAccept FundingPubKey cannot be the same',
|
|
2376
|
+
);
|
|
2377
|
+
});
|
|
2378
|
+
});
|
|
2379
|
+
|
|
2380
|
+
_fundingInputs.forEach((input) =>
|
|
2381
|
+
assert(
|
|
2382
|
+
input.type === MessageType.FundingInputV0,
|
|
2383
|
+
'FundingInput must be V0',
|
|
2384
|
+
),
|
|
2385
|
+
);
|
|
2386
|
+
|
|
2387
|
+
const fundingInputs: FundingInputV0[] = _fundingInputs.map(
|
|
2388
|
+
(input) => input as FundingInputV0,
|
|
2389
|
+
);
|
|
2390
|
+
|
|
2391
|
+
fundingInputs.sort(
|
|
2392
|
+
(a, b) => Number(a.inputSerialId) - Number(b.inputSerialId),
|
|
2393
|
+
);
|
|
2394
|
+
|
|
2395
|
+
const dlcAccepts: DlcAcceptV0[] = [];
|
|
2396
|
+
|
|
2397
|
+
initializeResponses.forEach((initializeResponse, i) => {
|
|
2398
|
+
const dlcOffer = dlcOffers[i];
|
|
2399
|
+
const dlcAccept = new DlcAcceptV0();
|
|
2400
|
+
|
|
2401
|
+
const { fundingPubKey, payoutSPK, payoutSerialId } = initializeResponse;
|
|
2402
|
+
|
|
2403
|
+
dlcAccept.tempContractId = sha256(dlcOffers[i].serialize());
|
|
2404
|
+
dlcAccept.acceptCollateralSatoshis = acceptCollaterals[i];
|
|
2405
|
+
dlcAccept.fundingPubKey = fundingPubKey;
|
|
2406
|
+
dlcAccept.payoutSPK = payoutSPK;
|
|
2407
|
+
dlcAccept.payoutSerialId = payoutSerialId;
|
|
2408
|
+
dlcAccept.fundingInputs = fundingInputs;
|
|
2409
|
+
dlcAccept.changeSPK = changeSPK;
|
|
2410
|
+
dlcAccept.changeSerialId = changeSerialId;
|
|
2411
|
+
|
|
2412
|
+
assert(
|
|
2413
|
+
dlcAccept.changeSerialId !== dlcOffer.fundOutputSerialId,
|
|
2414
|
+
'changeSerialId cannot equal the fundOutputSerialId',
|
|
2415
|
+
);
|
|
2416
|
+
|
|
2417
|
+
assert(
|
|
2418
|
+
dlcOffer.payoutSerialId !== dlcAccept.payoutSerialId,
|
|
2419
|
+
'offer.payoutSerialId cannot equal accept.payoutSerialId',
|
|
2420
|
+
);
|
|
2421
|
+
|
|
2422
|
+
assert(
|
|
2423
|
+
(() => {
|
|
2424
|
+
const ids = [
|
|
2425
|
+
dlcOffer.changeSerialId,
|
|
2426
|
+
dlcAccept.changeSerialId,
|
|
2427
|
+
dlcOffer.fundOutputSerialId,
|
|
2428
|
+
];
|
|
2429
|
+
return new Set(ids).size === ids.length;
|
|
2430
|
+
})(),
|
|
2431
|
+
'offer.changeSerialID, accept.changeSerialId and fundOutputSerialId must be unique',
|
|
2432
|
+
);
|
|
2433
|
+
|
|
2434
|
+
dlcAccept.validate();
|
|
2435
|
+
|
|
2436
|
+
assert(
|
|
2437
|
+
(() => {
|
|
2438
|
+
const finalizer = new DualFundingTxFinalizer(
|
|
2439
|
+
dlcOffer.fundingInputs,
|
|
2440
|
+
dlcOffer.payoutSPK,
|
|
2441
|
+
dlcOffer.changeSPK,
|
|
2442
|
+
dlcAccept.fundingInputs,
|
|
2443
|
+
dlcAccept.payoutSPK,
|
|
2444
|
+
dlcAccept.changeSPK,
|
|
2445
|
+
dlcOffer.feeRatePerVb,
|
|
2446
|
+
);
|
|
2447
|
+
const funding = fundingInputs.reduce((total, input) => {
|
|
2448
|
+
return total + input.prevTx.outputs[input.prevTxVout].value.sats;
|
|
2449
|
+
}, BigInt(0));
|
|
2450
|
+
|
|
2451
|
+
return funding >= acceptCollaterals[i] + finalizer.acceptFees;
|
|
2452
|
+
})(),
|
|
2453
|
+
'fundingInputs for dlcAccept must be greater than acceptCollateralSatoshis plus acceptFees',
|
|
2454
|
+
);
|
|
2455
|
+
|
|
2456
|
+
dlcAccepts.push(dlcAccept);
|
|
2457
|
+
});
|
|
2458
|
+
|
|
2459
|
+
const {
|
|
2460
|
+
dlcTransactionsList,
|
|
2461
|
+
nestedMessagesList,
|
|
2462
|
+
} = await this.createBatchDlcTxs(dlcOffers, dlcAccepts);
|
|
2463
|
+
|
|
2464
|
+
for (let i = 0; i < dlcAccepts.length; i++) {
|
|
2465
|
+
const dlcOffer = dlcOffers[i];
|
|
2466
|
+
const dlcAccept = dlcAccepts[i];
|
|
2467
|
+
const dlcTransactions = dlcTransactionsList[i];
|
|
2468
|
+
const messagesList = nestedMessagesList[i];
|
|
2469
|
+
|
|
2470
|
+
const {
|
|
2471
|
+
cetSignatures,
|
|
2472
|
+
refundSignature,
|
|
2473
|
+
} = await this.CreateCetAdaptorAndRefundSigs(
|
|
2474
|
+
dlcOffer,
|
|
2475
|
+
dlcAccept,
|
|
2476
|
+
dlcTransactions,
|
|
2477
|
+
messagesList,
|
|
2478
|
+
false,
|
|
2479
|
+
);
|
|
2480
|
+
|
|
2481
|
+
assert(
|
|
2482
|
+
dlcTransactions.type === MessageType.DlcTransactionsV0,
|
|
2483
|
+
'DlcTransactions must be V0',
|
|
2484
|
+
);
|
|
2485
|
+
const _dlcTransactions = dlcTransactions as DlcTransactionsV0;
|
|
2486
|
+
|
|
2487
|
+
const contractId = xor(
|
|
2488
|
+
_dlcTransactions.fundTx.txId.serialize(),
|
|
2489
|
+
dlcAccept.tempContractId,
|
|
2490
|
+
);
|
|
2491
|
+
_dlcTransactions.contractId = contractId;
|
|
2492
|
+
|
|
2493
|
+
dlcAccepts[i].cetSignatures = cetSignatures;
|
|
2494
|
+
dlcAccepts[i].refundSignature = refundSignature;
|
|
2495
|
+
dlcAccepts[i].negotiationFields = new NegotiationFieldsV0();
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2498
|
+
return { dlcAccepts, dlcTransactionsList };
|
|
2499
|
+
}
|
|
2500
|
+
|
|
2020
2501
|
/**
|
|
2021
2502
|
* Sign Dlc Accept Message
|
|
2022
2503
|
* @param _dlcOffer Dlc Offer Message
|
|
@@ -2098,6 +2579,84 @@ Payout Group found but incorrect group index',
|
|
|
2098
2579
|
return { dlcSign, dlcTransactions: dlcTxs };
|
|
2099
2580
|
}
|
|
2100
2581
|
|
|
2582
|
+
async batchSignDlcAccept(
|
|
2583
|
+
_dlcOffers: DlcOffer[],
|
|
2584
|
+
_dlcAccepts: DlcAccept[],
|
|
2585
|
+
): Promise<BatchSignDlcAcceptResponse> {
|
|
2586
|
+
const dlcOffers = _dlcOffers.map((_dlcOffer) => {
|
|
2587
|
+
const { dlcOffer } = checkTypes({ _dlcOffer });
|
|
2588
|
+
dlcOffer.validate();
|
|
2589
|
+
return dlcOffer;
|
|
2590
|
+
});
|
|
2591
|
+
|
|
2592
|
+
const dlcAccepts = _dlcAccepts.map((_dlcAccept) => {
|
|
2593
|
+
const { dlcAccept } = checkTypes({ _dlcAccept });
|
|
2594
|
+
dlcAccept.validate();
|
|
2595
|
+
return dlcAccept;
|
|
2596
|
+
});
|
|
2597
|
+
|
|
2598
|
+
const {
|
|
2599
|
+
dlcTransactionsList,
|
|
2600
|
+
nestedMessagesList,
|
|
2601
|
+
} = await this.createBatchDlcTxs(dlcOffers, dlcAccepts);
|
|
2602
|
+
|
|
2603
|
+
const dlcSigns: DlcSignV0[] = [];
|
|
2604
|
+
|
|
2605
|
+
const fundingSignatures = await this.CreateFundingSigs(
|
|
2606
|
+
dlcOffers[0],
|
|
2607
|
+
dlcAccepts[0],
|
|
2608
|
+
dlcTransactionsList[0],
|
|
2609
|
+
true,
|
|
2610
|
+
);
|
|
2611
|
+
|
|
2612
|
+
for (let i = 0; i < dlcAccepts.length; i++) {
|
|
2613
|
+
const dlcOffer = dlcOffers[i];
|
|
2614
|
+
const dlcAccept = dlcAccepts[i];
|
|
2615
|
+
const dlcTransactions = dlcTransactionsList[i];
|
|
2616
|
+
const messagesList = nestedMessagesList[i];
|
|
2617
|
+
|
|
2618
|
+
const dlcSign = new DlcSignV0();
|
|
2619
|
+
|
|
2620
|
+
await this.VerifyCetAdaptorAndRefundSigs(
|
|
2621
|
+
dlcOffer,
|
|
2622
|
+
dlcAccept,
|
|
2623
|
+
dlcSign,
|
|
2624
|
+
dlcTransactions,
|
|
2625
|
+
messagesList,
|
|
2626
|
+
true,
|
|
2627
|
+
);
|
|
2628
|
+
|
|
2629
|
+
const {
|
|
2630
|
+
cetSignatures,
|
|
2631
|
+
refundSignature,
|
|
2632
|
+
} = await this.CreateCetAdaptorAndRefundSigs(
|
|
2633
|
+
dlcOffer,
|
|
2634
|
+
dlcAccept,
|
|
2635
|
+
dlcTransactions,
|
|
2636
|
+
messagesList,
|
|
2637
|
+
true,
|
|
2638
|
+
);
|
|
2639
|
+
|
|
2640
|
+
const dlcTxs = dlcTransactions as DlcTransactionsV0;
|
|
2641
|
+
|
|
2642
|
+
const contractId = xor(
|
|
2643
|
+
dlcTxs.fundTx.txId.serialize(),
|
|
2644
|
+
dlcAccept.tempContractId,
|
|
2645
|
+
);
|
|
2646
|
+
|
|
2647
|
+
dlcTxs.contractId = contractId;
|
|
2648
|
+
|
|
2649
|
+
dlcSign.contractId = contractId;
|
|
2650
|
+
dlcSign.cetSignatures = cetSignatures;
|
|
2651
|
+
dlcSign.refundSignature = refundSignature;
|
|
2652
|
+
dlcSign.fundingSignatures = fundingSignatures;
|
|
2653
|
+
|
|
2654
|
+
dlcSigns.push(dlcSign);
|
|
2655
|
+
}
|
|
2656
|
+
|
|
2657
|
+
return { dlcSigns, dlcTransactionsList };
|
|
2658
|
+
}
|
|
2659
|
+
|
|
2101
2660
|
/**
|
|
2102
2661
|
* Finalize Dlc Sign
|
|
2103
2662
|
* @param _dlcOffer Dlc Offer Message
|
|
@@ -2151,6 +2710,79 @@ Payout Group found but incorrect group index',
|
|
|
2151
2710
|
return fundTx;
|
|
2152
2711
|
}
|
|
2153
2712
|
|
|
2713
|
+
async batchFinalizeDlcSign(
|
|
2714
|
+
_dlcOffers: DlcOffer[],
|
|
2715
|
+
_dlcAccepts: DlcAccept[],
|
|
2716
|
+
_dlcSigns: DlcSign[],
|
|
2717
|
+
_dlcTxsList: DlcTransactions[],
|
|
2718
|
+
): Promise<Tx> {
|
|
2719
|
+
const dlcOffers = _dlcOffers.map((_dlcOffer) => {
|
|
2720
|
+
const { dlcOffer } = checkTypes({ _dlcOffer });
|
|
2721
|
+
dlcOffer.validate();
|
|
2722
|
+
return dlcOffer;
|
|
2723
|
+
});
|
|
2724
|
+
|
|
2725
|
+
const dlcAccepts = _dlcAccepts.map((_dlcAccept) => {
|
|
2726
|
+
const { dlcAccept } = checkTypes({ _dlcAccept });
|
|
2727
|
+
dlcAccept.validate();
|
|
2728
|
+
return dlcAccept;
|
|
2729
|
+
});
|
|
2730
|
+
|
|
2731
|
+
const dlcSigns = _dlcSigns.map((_dlcSign) => {
|
|
2732
|
+
const { dlcSign } = checkTypes({ _dlcSign });
|
|
2733
|
+
return dlcSign;
|
|
2734
|
+
});
|
|
2735
|
+
|
|
2736
|
+
const dlcTxsList = _dlcTxsList.map((_dlcTxs) => {
|
|
2737
|
+
const { dlcTxs } = checkTypes({ _dlcTxs });
|
|
2738
|
+
return dlcTxs;
|
|
2739
|
+
});
|
|
2740
|
+
|
|
2741
|
+
await this.VerifyFundingSigs(
|
|
2742
|
+
dlcOffers[0],
|
|
2743
|
+
dlcAccepts[0],
|
|
2744
|
+
dlcSigns[0],
|
|
2745
|
+
dlcTxsList[0],
|
|
2746
|
+
false,
|
|
2747
|
+
);
|
|
2748
|
+
|
|
2749
|
+
for (let i = 0; i < dlcOffers.length; i++) {
|
|
2750
|
+
const dlcOffer = dlcOffers[i];
|
|
2751
|
+
const dlcAccept = dlcAccepts[i];
|
|
2752
|
+
const dlcSign = dlcSigns[i];
|
|
2753
|
+
const dlcTxs = dlcTxsList[i];
|
|
2754
|
+
|
|
2755
|
+
const payoutResponses = this.GetPayouts(dlcOffer);
|
|
2756
|
+
const { messagesList } = this.FlattenPayouts(payoutResponses);
|
|
2757
|
+
|
|
2758
|
+
await this.VerifyCetAdaptorAndRefundSigs(
|
|
2759
|
+
dlcOffer,
|
|
2760
|
+
dlcAccept,
|
|
2761
|
+
dlcSign,
|
|
2762
|
+
dlcTxs,
|
|
2763
|
+
messagesList,
|
|
2764
|
+
false,
|
|
2765
|
+
);
|
|
2766
|
+
}
|
|
2767
|
+
|
|
2768
|
+
const fundingSignatures = await this.CreateFundingSigs(
|
|
2769
|
+
dlcOffers[0],
|
|
2770
|
+
dlcAccepts[0],
|
|
2771
|
+
dlcTxsList[0],
|
|
2772
|
+
false,
|
|
2773
|
+
);
|
|
2774
|
+
|
|
2775
|
+
const fundTx = await this.CreateFundingTx(
|
|
2776
|
+
dlcOffers[0],
|
|
2777
|
+
dlcAccepts[0],
|
|
2778
|
+
dlcSigns[0],
|
|
2779
|
+
dlcTxsList[0],
|
|
2780
|
+
fundingSignatures,
|
|
2781
|
+
);
|
|
2782
|
+
|
|
2783
|
+
return fundTx;
|
|
2784
|
+
}
|
|
2785
|
+
|
|
2154
2786
|
/**
|
|
2155
2787
|
* Execute DLC
|
|
2156
2788
|
* @param _dlcOffer Dlc Offer Message
|
|
@@ -2888,6 +3520,14 @@ Payout Group found but incorrect group index',
|
|
|
2888
3520
|
return this._cfdDlcJs.CreateDlcTransactions(jsonObject);
|
|
2889
3521
|
}
|
|
2890
3522
|
|
|
3523
|
+
async CreateBatchDlcTransactions(
|
|
3524
|
+
jsonObject: CreateBatchDlcTransactionsRequest,
|
|
3525
|
+
): Promise<CreateBatchDlcTransactionsResponse> {
|
|
3526
|
+
await this.CfdLoaded();
|
|
3527
|
+
|
|
3528
|
+
return this._cfdDlcJs.CreateBatchDlcTransactions(jsonObject);
|
|
3529
|
+
}
|
|
3530
|
+
|
|
2891
3531
|
async CreateFundTransaction(
|
|
2892
3532
|
jsonObject: CreateFundTransactionRequest,
|
|
2893
3533
|
): Promise<CreateFundTransactionResponse> {
|
|
@@ -2896,6 +3536,14 @@ Payout Group found but incorrect group index',
|
|
|
2896
3536
|
return this._cfdDlcJs.CreateFundTransaction(jsonObject);
|
|
2897
3537
|
}
|
|
2898
3538
|
|
|
3539
|
+
async CreateBatchFundTransaction(
|
|
3540
|
+
jsonObject: CreateBatchFundTransactionRequest,
|
|
3541
|
+
): Promise<CreateBatchFundTransactionResponse> {
|
|
3542
|
+
await this.CfdLoaded();
|
|
3543
|
+
|
|
3544
|
+
return this._cfdDlcJs.CreateBatchFundTransaction(jsonObject);
|
|
3545
|
+
}
|
|
3546
|
+
|
|
2899
3547
|
async CreateRefundTransaction(
|
|
2900
3548
|
jsonObject: CreateRefundTransactionRequest,
|
|
2901
3549
|
): Promise<CreateRefundTransactionResponse> {
|
|
@@ -3048,10 +3696,26 @@ Payout Group found but incorrect group index',
|
|
|
3048
3696
|
}
|
|
3049
3697
|
}
|
|
3050
3698
|
|
|
3051
|
-
export interface
|
|
3699
|
+
export interface BasicInitializeResponse {
|
|
3052
3700
|
fundingPubKey: Buffer;
|
|
3053
3701
|
payoutSPK: Buffer;
|
|
3054
3702
|
payoutSerialId: bigint;
|
|
3703
|
+
changeSPK: Buffer;
|
|
3704
|
+
changeSerialId: bigint;
|
|
3705
|
+
}
|
|
3706
|
+
|
|
3707
|
+
export interface InitializeResponse extends BasicInitializeResponse {
|
|
3708
|
+
fundingInputs: FundingInput[];
|
|
3709
|
+
}
|
|
3710
|
+
|
|
3711
|
+
export interface BatchBaseInitializeResponse {
|
|
3712
|
+
fundingPubKey: Buffer;
|
|
3713
|
+
payoutSPK: Buffer;
|
|
3714
|
+
payoutSerialId: bigint;
|
|
3715
|
+
}
|
|
3716
|
+
|
|
3717
|
+
export interface BatchInitializeResponse {
|
|
3718
|
+
initializeResponses: BatchBaseInitializeResponse[];
|
|
3055
3719
|
fundingInputs: FundingInput[];
|
|
3056
3720
|
changeSPK: Buffer;
|
|
3057
3721
|
changeSerialId: bigint;
|
|
@@ -3062,11 +3726,21 @@ export interface AcceptDlcOfferResponse {
|
|
|
3062
3726
|
dlcTransactions: DlcTransactions;
|
|
3063
3727
|
}
|
|
3064
3728
|
|
|
3729
|
+
export interface BatchAcceptDlcOfferResponse {
|
|
3730
|
+
dlcAccepts: DlcAccept[];
|
|
3731
|
+
dlcTransactionsList: DlcTransactions[];
|
|
3732
|
+
}
|
|
3733
|
+
|
|
3065
3734
|
export interface SignDlcAcceptResponse {
|
|
3066
3735
|
dlcSign: DlcSign;
|
|
3067
3736
|
dlcTransactions: DlcTransactions;
|
|
3068
3737
|
}
|
|
3069
3738
|
|
|
3739
|
+
export interface BatchSignDlcAcceptResponse {
|
|
3740
|
+
dlcSigns: DlcSign[];
|
|
3741
|
+
dlcTransactionsList: DlcTransactions[];
|
|
3742
|
+
}
|
|
3743
|
+
|
|
3070
3744
|
export interface GetPayoutsResponse {
|
|
3071
3745
|
payouts: PayoutRequest[];
|
|
3072
3746
|
payoutGroups: PayoutGroup[];
|
|
@@ -3078,6 +3752,11 @@ export interface CreateDlcTxsResponse {
|
|
|
3078
3752
|
messagesList: Messages[];
|
|
3079
3753
|
}
|
|
3080
3754
|
|
|
3755
|
+
export interface CreateBatchDlcTxsResponse {
|
|
3756
|
+
dlcTransactionsList: DlcTransactions[];
|
|
3757
|
+
nestedMessagesList: Messages[][];
|
|
3758
|
+
}
|
|
3759
|
+
|
|
3081
3760
|
interface ISig {
|
|
3082
3761
|
encryptedSig: Buffer;
|
|
3083
3762
|
dleqProof: Buffer;
|