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