@buildonspark/spark-sdk 0.2.3 → 0.2.4
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/CHANGELOG.md +8 -0
- package/dist/{chunk-PTRXJS7Q.js → chunk-TVUMSHWA.js} +1 -1
- package/dist/{chunk-PLLJIZC3.js → chunk-W4ZRBSWM.js} +2298 -778
- package/dist/{chunk-CDLETEDT.js → chunk-WAQKYSDI.js} +13 -1
- package/dist/{client-CGTRS23n.d.ts → client-BF4cn8F4.d.ts} +15 -3
- package/dist/{client-CcYzmpmj.d.cts → client-KhNkrXz4.d.cts} +15 -3
- package/dist/debug.cjs +2282 -762
- package/dist/debug.d.cts +17 -4
- package/dist/debug.d.ts +17 -4
- package/dist/debug.js +2 -2
- package/dist/graphql/objects/index.cjs +13 -1
- package/dist/graphql/objects/index.d.cts +2 -2
- package/dist/graphql/objects/index.d.ts +2 -2
- package/dist/graphql/objects/index.js +1 -1
- package/dist/index.cjs +2283 -752
- package/dist/index.d.cts +189 -8
- package/dist/index.d.ts +189 -8
- package/dist/index.js +29 -3
- package/dist/index.node.cjs +2387 -753
- package/dist/index.node.d.cts +9 -189
- package/dist/index.node.d.ts +9 -189
- package/dist/index.node.js +131 -3
- package/dist/native/index.cjs +2283 -752
- package/dist/native/index.d.cts +95 -30
- package/dist/native/index.d.ts +95 -30
- package/dist/native/index.js +2284 -767
- package/dist/{spark-wallet-CxcGPXRB.d.ts → spark-wallet-C1Tr_VKI.d.ts} +31 -25
- package/dist/{spark-wallet-DJJm19BP.d.cts → spark-wallet-DG3x2obf.d.cts} +31 -25
- package/dist/spark-wallet.node-CGxoeCpH.d.ts +13 -0
- package/dist/spark-wallet.node-CN9LoB_O.d.cts +13 -0
- package/dist/tests/test-utils.cjs +570 -73
- package/dist/tests/test-utils.d.cts +11 -11
- package/dist/tests/test-utils.d.ts +11 -11
- package/dist/tests/test-utils.js +53 -16
- package/dist/types/index.cjs +13 -1
- package/dist/types/index.d.cts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.js +1 -1
- package/dist/{xchain-address-Bh9w1SeC.d.ts → xchain-address-BHu6CpZC.d.ts} +54 -7
- package/dist/{xchain-address-SZ7dkVUE.d.cts → xchain-address-HBr6isnc.d.cts} +54 -7
- package/package.json +1 -1
- package/src/graphql/client.ts +8 -0
- package/src/graphql/mutations/CompleteLeavesSwap.ts +9 -1
- package/src/graphql/mutations/RequestSwapLeaves.ts +4 -0
- package/src/graphql/objects/CompleteLeavesSwapInput.ts +34 -34
- package/src/graphql/objects/LeavesSwapRequest.ts +4 -0
- package/src/graphql/objects/RequestLeavesSwapInput.ts +48 -47
- package/src/graphql/objects/SwapLeaf.ts +40 -32
- package/src/graphql/objects/UserLeafInput.ts +24 -0
- package/src/graphql/objects/UserRequest.ts +4 -0
- package/src/index.node.ts +1 -1
- package/src/native/index.ts +4 -5
- package/src/services/coop-exit.ts +171 -36
- package/src/services/deposit.ts +471 -74
- package/src/services/lightning.ts +18 -5
- package/src/services/signing.ts +162 -50
- package/src/services/transfer.ts +950 -384
- package/src/services/tree-creation.ts +342 -121
- package/src/spark-wallet/spark-wallet.node.ts +71 -66
- package/src/spark-wallet/spark-wallet.ts +405 -153
- package/src/tests/integration/coop-exit.test.ts +3 -8
- package/src/tests/integration/deposit.test.ts +3 -3
- package/src/tests/integration/lightning.test.ts +521 -466
- package/src/tests/integration/swap.test.ts +559 -307
- package/src/tests/integration/transfer.test.ts +625 -623
- package/src/tests/integration/wallet.test.ts +2 -2
- package/src/tests/integration/watchtower.test.ts +211 -0
- package/src/tests/test-utils.ts +63 -14
- package/src/tests/utils/test-faucet.ts +4 -2
- package/src/utils/adaptor-signature.ts +15 -5
- package/src/utils/fetch.ts +75 -0
- package/src/utils/mempool.ts +9 -4
- package/src/utils/transaction.ts +388 -26
package/src/services/transfer.ts
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
import { secp256k1 } from "@noble/curves/secp256k1";
|
|
8
8
|
import { sha256 } from "@noble/hashes/sha2";
|
|
9
9
|
import { Transaction } from "@scure/btc-signer";
|
|
10
|
-
import { TransactionInput } from "@scure/btc-signer/psbt";
|
|
10
|
+
import { TransactionInput, TransactionOutput } from "@scure/btc-signer/psbt";
|
|
11
11
|
import * as ecies from "eciesjs";
|
|
12
12
|
import { uuidv7 } from "uuidv7";
|
|
13
13
|
import {
|
|
@@ -49,24 +49,26 @@ import {
|
|
|
49
49
|
import { NetworkToProto } from "../utils/network.js";
|
|
50
50
|
import { VerifiableSecretShare } from "../utils/secret-sharing.js";
|
|
51
51
|
import {
|
|
52
|
-
|
|
52
|
+
createNodeTxs,
|
|
53
|
+
createRefundTxs,
|
|
54
|
+
DIRECT_TIMELOCK_OFFSET,
|
|
55
|
+
getCurrentTimelock,
|
|
53
56
|
getEphemeralAnchorOutput,
|
|
54
57
|
getNextTransactionSequence,
|
|
55
58
|
getTransactionSequence,
|
|
59
|
+
INITIAL_DIRECT_SEQUENCE,
|
|
60
|
+
INITIAL_SEQUENCE,
|
|
61
|
+
maybeApplyFee,
|
|
62
|
+
TEST_UNILATERAL_DIRECT_SEQUENCE,
|
|
63
|
+
TEST_UNILATERAL_SEQUENCE,
|
|
56
64
|
} from "../utils/transaction.js";
|
|
57
65
|
import { getTransferPackageSigningPayload } from "../utils/transfer_package.js";
|
|
58
66
|
import { WalletConfigService } from "./config.js";
|
|
59
67
|
import { ConnectionManager } from "./connection.js";
|
|
60
68
|
import { SigningService } from "./signing.js";
|
|
61
69
|
import { SigningOperator } from "./wallet-config.js";
|
|
62
|
-
const INITIAL_TIME_LOCK = 2000;
|
|
63
|
-
|
|
64
70
|
const DEFAULT_EXPIRY_TIME = 10 * 60 * 1000;
|
|
65
71
|
|
|
66
|
-
function initialSequence() {
|
|
67
|
-
return (1 << 30) | INITIAL_TIME_LOCK;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
72
|
export type LeafKeyTweak = {
|
|
71
73
|
leaf: TreeNode;
|
|
72
74
|
keyDerivation: KeyDerivation;
|
|
@@ -84,9 +86,14 @@ export type ClaimLeafData = {
|
|
|
84
86
|
export type LeafRefundSigningData = {
|
|
85
87
|
keyDerivation: KeyDerivation;
|
|
86
88
|
receivingPubkey: Uint8Array;
|
|
89
|
+
signingNonceCommitment: SigningCommitmentWithOptionalNonce;
|
|
90
|
+
directSigningNonceCommitment: SigningCommitmentWithOptionalNonce;
|
|
87
91
|
tx: Transaction;
|
|
92
|
+
directTx?: Transaction;
|
|
88
93
|
refundTx?: Transaction;
|
|
89
|
-
|
|
94
|
+
directRefundTx?: Transaction;
|
|
95
|
+
directFromCpfpRefundTx?: Transaction;
|
|
96
|
+
directFromCpfpRefundSigningNonceCommitment: SigningCommitmentWithOptionalNonce;
|
|
90
97
|
vout: number;
|
|
91
98
|
};
|
|
92
99
|
|
|
@@ -124,13 +131,17 @@ export class BaseTransferService {
|
|
|
124
131
|
async sendTransferTweakKey(
|
|
125
132
|
transfer: Transfer,
|
|
126
133
|
leaves: LeafKeyTweak[],
|
|
127
|
-
|
|
134
|
+
cpfpRefundSignatureMap: Map<string, Uint8Array>,
|
|
135
|
+
directRefundSignatureMap: Map<string, Uint8Array>,
|
|
136
|
+
directFromCpfpRefundSignatureMap: Map<string, Uint8Array>,
|
|
128
137
|
): Promise<Transfer> {
|
|
129
138
|
const keyTweakInputMap = await this.prepareSendTransferKeyTweaks(
|
|
130
139
|
transfer.id,
|
|
131
140
|
transfer.receiverIdentityPublicKey,
|
|
132
141
|
leaves,
|
|
133
|
-
|
|
142
|
+
cpfpRefundSignatureMap,
|
|
143
|
+
directRefundSignatureMap,
|
|
144
|
+
directFromCpfpRefundSignatureMap,
|
|
134
145
|
);
|
|
135
146
|
|
|
136
147
|
let updatedTransfer: Transfer | undefined;
|
|
@@ -179,15 +190,31 @@ export class BaseTransferService {
|
|
|
179
190
|
async deliverTransferPackage(
|
|
180
191
|
transfer: Transfer,
|
|
181
192
|
leaves: LeafKeyTweak[],
|
|
182
|
-
|
|
193
|
+
cpfpRefundSignatureMap: Map<string, Uint8Array>,
|
|
194
|
+
directRefundSignatureMap: Map<string, Uint8Array>,
|
|
195
|
+
directFromCpfpRefundSignatureMap: Map<string, Uint8Array>,
|
|
183
196
|
): Promise<Transfer> {
|
|
184
197
|
const keyTweakInputMap = await this.prepareSendTransferKeyTweaks(
|
|
185
198
|
transfer.id,
|
|
186
199
|
transfer.receiverIdentityPublicKey,
|
|
187
200
|
leaves,
|
|
188
|
-
|
|
201
|
+
cpfpRefundSignatureMap,
|
|
202
|
+
directRefundSignatureMap,
|
|
203
|
+
directFromCpfpRefundSignatureMap,
|
|
189
204
|
);
|
|
190
205
|
|
|
206
|
+
for (const [key, operator] of Object.entries(
|
|
207
|
+
this.config.getSigningOperators(),
|
|
208
|
+
)) {
|
|
209
|
+
const tweaks = keyTweakInputMap.get(key);
|
|
210
|
+
if (!tweaks) {
|
|
211
|
+
throw new ValidationError("No tweaks for operator", {
|
|
212
|
+
field: "operator",
|
|
213
|
+
value: key,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
191
218
|
const transferPackage = await this.prepareTransferPackage(
|
|
192
219
|
transfer.id,
|
|
193
220
|
keyTweakInputMap,
|
|
@@ -223,6 +250,8 @@ export class BaseTransferService {
|
|
|
223
250
|
receiverIdentityPubkey,
|
|
224
251
|
leaves,
|
|
225
252
|
new Map<string, Uint8Array>(),
|
|
253
|
+
new Map<string, Uint8Array>(),
|
|
254
|
+
new Map<string, Uint8Array>(),
|
|
226
255
|
);
|
|
227
256
|
|
|
228
257
|
const transferPackage = await this.prepareTransferPackage(
|
|
@@ -239,7 +268,7 @@ export class BaseTransferService {
|
|
|
239
268
|
let response: StartTransferResponse;
|
|
240
269
|
|
|
241
270
|
try {
|
|
242
|
-
response = await sparkClient.
|
|
271
|
+
response = await sparkClient.start_transfer_v2({
|
|
243
272
|
transferId: transferID,
|
|
244
273
|
ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
|
|
245
274
|
receiverIdentityPublicKey: receiverIdentityPubkey,
|
|
@@ -277,15 +306,24 @@ export class BaseTransferService {
|
|
|
277
306
|
for (const leaf of leaves) {
|
|
278
307
|
nodes.push(leaf.leaf.id);
|
|
279
308
|
}
|
|
280
|
-
|
|
281
309
|
const signingCommitments = await sparkClient.get_signing_commitments({
|
|
282
310
|
nodeIds: nodes,
|
|
311
|
+
count: 3,
|
|
283
312
|
});
|
|
284
313
|
|
|
285
|
-
const
|
|
314
|
+
const {
|
|
315
|
+
cpfpLeafSigningJobs,
|
|
316
|
+
directLeafSigningJobs,
|
|
317
|
+
directFromCpfpLeafSigningJobs,
|
|
318
|
+
} = await this.signingService.signRefunds(
|
|
286
319
|
leaves,
|
|
287
|
-
signingCommitments.signingCommitments,
|
|
288
320
|
receiverIdentityPubkey,
|
|
321
|
+
signingCommitments.signingCommitments.slice(0, leaves.length),
|
|
322
|
+
signingCommitments.signingCommitments.slice(
|
|
323
|
+
leaves.length,
|
|
324
|
+
2 * leaves.length,
|
|
325
|
+
),
|
|
326
|
+
signingCommitments.signingCommitments.slice(2 * leaves.length),
|
|
289
327
|
);
|
|
290
328
|
|
|
291
329
|
const encryptedKeyTweaks: { [key: string]: Uint8Array } = {};
|
|
@@ -312,12 +350,11 @@ export class BaseTransferService {
|
|
|
312
350
|
}
|
|
313
351
|
|
|
314
352
|
const transferPackage: TransferPackage = {
|
|
315
|
-
leavesToSend:
|
|
353
|
+
leavesToSend: cpfpLeafSigningJobs,
|
|
316
354
|
keyTweakPackage: encryptedKeyTweaks,
|
|
317
355
|
userSignature: new Uint8Array(),
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
directFromCpfpLeavesToSend: [],
|
|
356
|
+
directLeavesToSend: directLeafSigningJobs,
|
|
357
|
+
directFromCpfpLeavesToSend: directFromCpfpLeafSigningJobs,
|
|
321
358
|
};
|
|
322
359
|
|
|
323
360
|
const transferPackageSigningPayload = getTransferPackageSigningPayload(
|
|
@@ -351,6 +388,7 @@ export class BaseTransferService {
|
|
|
351
388
|
});
|
|
352
389
|
}
|
|
353
390
|
let transferResp: FinalizeTransferResponse;
|
|
391
|
+
|
|
354
392
|
try {
|
|
355
393
|
transferResp = await sparkClient.finalize_transfer({
|
|
356
394
|
transferId: transfer.id,
|
|
@@ -392,9 +430,11 @@ export class BaseTransferService {
|
|
|
392
430
|
}
|
|
393
431
|
|
|
394
432
|
async signRefunds(
|
|
395
|
-
leafDataMap: Map<string,
|
|
433
|
+
leafDataMap: Map<string, LeafRefundSigningData>,
|
|
396
434
|
operatorSigningResults: LeafRefundTxSigningResult[],
|
|
397
|
-
|
|
435
|
+
cpfpAdaptorPubKey?: Uint8Array,
|
|
436
|
+
directAdaptorPubKey?: Uint8Array,
|
|
437
|
+
directFromCpfpAdaptorPubKey?: Uint8Array,
|
|
398
438
|
): Promise<NodeSignatures[]> {
|
|
399
439
|
const nodeSignatures: NodeSignatures[] = [];
|
|
400
440
|
for (const operatorSigningResult of operatorSigningResults) {
|
|
@@ -417,24 +457,28 @@ export class BaseTransferService {
|
|
|
417
457
|
);
|
|
418
458
|
}
|
|
419
459
|
|
|
420
|
-
|
|
421
|
-
|
|
460
|
+
// Sign CPFP refund transaction
|
|
461
|
+
const cpfpRefundTxSighash = getSigHashFromTx(
|
|
462
|
+
leafData.refundTx,
|
|
463
|
+
0,
|
|
464
|
+
txOutput,
|
|
465
|
+
);
|
|
422
466
|
const publicKey = await this.config.signer.getPublicKeyFromDerivation(
|
|
423
467
|
leafData.keyDerivation,
|
|
424
468
|
);
|
|
425
|
-
const
|
|
426
|
-
message:
|
|
469
|
+
const cpfpUserSignature = await this.config.signer.signFrost({
|
|
470
|
+
message: cpfpRefundTxSighash,
|
|
427
471
|
publicKey,
|
|
428
472
|
keyDerivation: leafData.keyDerivation,
|
|
429
473
|
selfCommitment: leafData.signingNonceCommitment,
|
|
430
474
|
statechainCommitments:
|
|
431
475
|
operatorSigningResult.refundTxSigningResult?.signingNonceCommitments,
|
|
432
|
-
adaptorPubKey:
|
|
476
|
+
adaptorPubKey: cpfpAdaptorPubKey,
|
|
433
477
|
verifyingKey: operatorSigningResult.verifyingKey,
|
|
434
478
|
});
|
|
435
479
|
|
|
436
|
-
const
|
|
437
|
-
message:
|
|
480
|
+
const cpfpRefundAggregate = await this.config.signer.aggregateFrost({
|
|
481
|
+
message: cpfpRefundTxSighash,
|
|
438
482
|
statechainSignatures:
|
|
439
483
|
operatorSigningResult.refundTxSigningResult?.signatureShares,
|
|
440
484
|
statechainPublicKeys:
|
|
@@ -444,18 +488,105 @@ export class BaseTransferService {
|
|
|
444
488
|
operatorSigningResult.refundTxSigningResult?.signingNonceCommitments,
|
|
445
489
|
selfCommitment: leafData.signingNonceCommitment,
|
|
446
490
|
publicKey,
|
|
447
|
-
selfSignature:
|
|
448
|
-
adaptorPubKey:
|
|
491
|
+
selfSignature: cpfpUserSignature,
|
|
492
|
+
adaptorPubKey: cpfpAdaptorPubKey,
|
|
449
493
|
});
|
|
450
494
|
|
|
495
|
+
// Sign direct refund transaction
|
|
496
|
+
|
|
497
|
+
let directRefundAggregate: Uint8Array | undefined;
|
|
498
|
+
let directFromCpfpRefundAggregate: Uint8Array | undefined;
|
|
499
|
+
if (leafData.directTx) {
|
|
500
|
+
const directTxOutput = leafData.directTx.getOutput(0);
|
|
501
|
+
|
|
502
|
+
if (leafData.directRefundTx) {
|
|
503
|
+
const directRefundTxSighash = getSigHashFromTx(
|
|
504
|
+
leafData.directRefundTx,
|
|
505
|
+
0,
|
|
506
|
+
directTxOutput,
|
|
507
|
+
);
|
|
508
|
+
|
|
509
|
+
const directUserSignature = await this.config.signer.signFrost({
|
|
510
|
+
message: directRefundTxSighash,
|
|
511
|
+
publicKey,
|
|
512
|
+
keyDerivation: leafData.keyDerivation,
|
|
513
|
+
selfCommitment: leafData.directSigningNonceCommitment,
|
|
514
|
+
statechainCommitments:
|
|
515
|
+
operatorSigningResult.directRefundTxSigningResult
|
|
516
|
+
?.signingNonceCommitments,
|
|
517
|
+
adaptorPubKey: directAdaptorPubKey,
|
|
518
|
+
verifyingKey: operatorSigningResult.verifyingKey,
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
directRefundAggregate = await this.config.signer.aggregateFrost({
|
|
522
|
+
message: directRefundTxSighash,
|
|
523
|
+
statechainSignatures:
|
|
524
|
+
operatorSigningResult.directRefundTxSigningResult
|
|
525
|
+
?.signatureShares,
|
|
526
|
+
statechainPublicKeys:
|
|
527
|
+
operatorSigningResult.directRefundTxSigningResult?.publicKeys,
|
|
528
|
+
verifyingKey: operatorSigningResult.verifyingKey,
|
|
529
|
+
statechainCommitments:
|
|
530
|
+
operatorSigningResult.directRefundTxSigningResult
|
|
531
|
+
?.signingNonceCommitments,
|
|
532
|
+
selfCommitment: leafData.directSigningNonceCommitment,
|
|
533
|
+
publicKey,
|
|
534
|
+
selfSignature: directUserSignature,
|
|
535
|
+
adaptorPubKey: directAdaptorPubKey,
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
if (leafData.directFromCpfpRefundTx) {
|
|
540
|
+
const directFromCpfpRefundTxSighash = getSigHashFromTx(
|
|
541
|
+
leafData.directFromCpfpRefundTx,
|
|
542
|
+
0,
|
|
543
|
+
txOutput,
|
|
544
|
+
);
|
|
545
|
+
|
|
546
|
+
const directFromCpfpUserSignature =
|
|
547
|
+
await this.config.signer.signFrost({
|
|
548
|
+
message: directFromCpfpRefundTxSighash,
|
|
549
|
+
publicKey,
|
|
550
|
+
keyDerivation: leafData.keyDerivation,
|
|
551
|
+
selfCommitment:
|
|
552
|
+
leafData.directFromCpfpRefundSigningNonceCommitment,
|
|
553
|
+
statechainCommitments:
|
|
554
|
+
operatorSigningResult.directFromCpfpRefundTxSigningResult
|
|
555
|
+
?.signingNonceCommitments,
|
|
556
|
+
adaptorPubKey: directFromCpfpAdaptorPubKey,
|
|
557
|
+
verifyingKey: operatorSigningResult.verifyingKey,
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
directFromCpfpRefundAggregate =
|
|
561
|
+
await this.config.signer.aggregateFrost({
|
|
562
|
+
message: directFromCpfpRefundTxSighash,
|
|
563
|
+
statechainSignatures:
|
|
564
|
+
operatorSigningResult.directFromCpfpRefundTxSigningResult
|
|
565
|
+
?.signatureShares,
|
|
566
|
+
statechainPublicKeys:
|
|
567
|
+
operatorSigningResult.directFromCpfpRefundTxSigningResult
|
|
568
|
+
?.publicKeys,
|
|
569
|
+
verifyingKey: operatorSigningResult.verifyingKey,
|
|
570
|
+
statechainCommitments:
|
|
571
|
+
operatorSigningResult.directFromCpfpRefundTxSigningResult
|
|
572
|
+
?.signingNonceCommitments,
|
|
573
|
+
selfCommitment:
|
|
574
|
+
leafData.directFromCpfpRefundSigningNonceCommitment,
|
|
575
|
+
publicKey,
|
|
576
|
+
selfSignature: directFromCpfpUserSignature,
|
|
577
|
+
adaptorPubKey: directFromCpfpAdaptorPubKey,
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
451
582
|
nodeSignatures.push({
|
|
452
583
|
nodeId: operatorSigningResult.leafId,
|
|
453
|
-
refundTxSignature: refundAggregate,
|
|
454
584
|
nodeTxSignature: new Uint8Array(),
|
|
455
|
-
// TODO: Add direct refund signature
|
|
456
585
|
directNodeTxSignature: new Uint8Array(),
|
|
457
|
-
|
|
458
|
-
|
|
586
|
+
refundTxSignature: cpfpRefundAggregate,
|
|
587
|
+
directRefundTxSignature: directRefundAggregate ?? new Uint8Array(),
|
|
588
|
+
directFromCpfpRefundTxSignature:
|
|
589
|
+
directFromCpfpRefundAggregate ?? new Uint8Array(),
|
|
459
590
|
});
|
|
460
591
|
}
|
|
461
592
|
return nodeSignatures;
|
|
@@ -465,7 +596,9 @@ export class BaseTransferService {
|
|
|
465
596
|
transferID: string,
|
|
466
597
|
receiverIdentityPubkey: Uint8Array,
|
|
467
598
|
leaves: LeafKeyTweak[],
|
|
468
|
-
|
|
599
|
+
cpfpRefundSignatureMap: Map<string, Uint8Array>,
|
|
600
|
+
directRefundSignatureMap: Map<string, Uint8Array>,
|
|
601
|
+
directFromCpfpRefundSignatureMap: Map<string, Uint8Array>,
|
|
469
602
|
): Promise<Map<string, SendLeafKeyTweak[]>> {
|
|
470
603
|
const receiverEciesPubKey = ecies.PublicKey.fromHex(
|
|
471
604
|
bytesToHex(receiverIdentityPubkey),
|
|
@@ -474,12 +607,18 @@ export class BaseTransferService {
|
|
|
474
607
|
const leavesTweaksMap = new Map<string, SendLeafKeyTweak[]>();
|
|
475
608
|
|
|
476
609
|
for (const leaf of leaves) {
|
|
477
|
-
const
|
|
610
|
+
const cpfpRefundSignature = cpfpRefundSignatureMap.get(leaf.leaf.id);
|
|
611
|
+
const directRefundSignature = directRefundSignatureMap.get(leaf.leaf.id);
|
|
612
|
+
const directFromCpfpRefundSignature =
|
|
613
|
+
directFromCpfpRefundSignatureMap.get(leaf.leaf.id);
|
|
614
|
+
|
|
478
615
|
const leafTweaksMap = await this.prepareSingleSendTransferKeyTweak(
|
|
479
616
|
transferID,
|
|
480
617
|
leaf,
|
|
481
618
|
receiverEciesPubKey,
|
|
482
|
-
|
|
619
|
+
cpfpRefundSignature,
|
|
620
|
+
directRefundSignature,
|
|
621
|
+
directFromCpfpRefundSignature,
|
|
483
622
|
);
|
|
484
623
|
for (const [identifier, leafTweak] of leafTweaksMap) {
|
|
485
624
|
leavesTweaksMap.set(identifier, [
|
|
@@ -496,7 +635,9 @@ export class BaseTransferService {
|
|
|
496
635
|
transferID: string,
|
|
497
636
|
leaf: LeafKeyTweak,
|
|
498
637
|
receiverEciesPubKey: ecies.PublicKey,
|
|
499
|
-
|
|
638
|
+
cpfpRefundSignature?: Uint8Array,
|
|
639
|
+
directRefundSignature?: Uint8Array,
|
|
640
|
+
directFromCpfpRefundSignature?: Uint8Array,
|
|
500
641
|
): Promise<Map<string, SendLeafKeyTweak>> {
|
|
501
642
|
const signingOperators = this.config.getSigningOperators();
|
|
502
643
|
|
|
@@ -553,10 +694,10 @@ export class BaseTransferService {
|
|
|
553
694
|
pubkeySharesTweak: Object.fromEntries(pubkeySharesTweak),
|
|
554
695
|
secretCipher,
|
|
555
696
|
signature,
|
|
556
|
-
refundSignature:
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
697
|
+
refundSignature: cpfpRefundSignature ?? new Uint8Array(),
|
|
698
|
+
directRefundSignature: directRefundSignature ?? new Uint8Array(),
|
|
699
|
+
directFromCpfpRefundSignature:
|
|
700
|
+
directFromCpfpRefundSignature ?? new Uint8Array(),
|
|
560
701
|
});
|
|
561
702
|
}
|
|
562
703
|
|
|
@@ -605,7 +746,12 @@ export class TransferService extends BaseTransferService {
|
|
|
605
746
|
leaves: LeafKeyTweak[],
|
|
606
747
|
receiverIdentityPubkey: Uint8Array,
|
|
607
748
|
): Promise<Transfer> {
|
|
608
|
-
const {
|
|
749
|
+
const {
|
|
750
|
+
transfer,
|
|
751
|
+
signatureMap,
|
|
752
|
+
directSignatureMap,
|
|
753
|
+
directFromCpfpSignatureMap,
|
|
754
|
+
} = await this.sendTransferSignRefund(
|
|
609
755
|
leaves,
|
|
610
756
|
receiverIdentityPubkey,
|
|
611
757
|
new Date(Date.now() + DEFAULT_EXPIRY_TIME),
|
|
@@ -615,6 +761,8 @@ export class TransferService extends BaseTransferService {
|
|
|
615
761
|
transfer,
|
|
616
762
|
leaves,
|
|
617
763
|
signatureMap,
|
|
764
|
+
directSignatureMap,
|
|
765
|
+
directFromCpfpSignatureMap,
|
|
618
766
|
);
|
|
619
767
|
|
|
620
768
|
return transferWithTweakedKeys;
|
|
@@ -748,19 +896,28 @@ export class TransferService extends BaseTransferService {
|
|
|
748
896
|
): Promise<{
|
|
749
897
|
transfer: Transfer;
|
|
750
898
|
signatureMap: Map<string, Uint8Array>;
|
|
899
|
+
directSignatureMap: Map<string, Uint8Array>;
|
|
900
|
+
directFromCpfpSignatureMap: Map<string, Uint8Array>;
|
|
751
901
|
leafDataMap: Map<string, LeafRefundSigningData>;
|
|
752
902
|
}> {
|
|
753
|
-
const {
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
903
|
+
const {
|
|
904
|
+
transfer,
|
|
905
|
+
signatureMap,
|
|
906
|
+
directSignatureMap,
|
|
907
|
+
directFromCpfpSignatureMap,
|
|
908
|
+
leafDataMap,
|
|
909
|
+
} = await this.sendTransferSignRefundInternal(
|
|
910
|
+
leaves,
|
|
911
|
+
receiverIdentityPubkey,
|
|
912
|
+
expiryTime,
|
|
913
|
+
false,
|
|
914
|
+
);
|
|
760
915
|
|
|
761
916
|
return {
|
|
762
917
|
transfer,
|
|
763
918
|
signatureMap,
|
|
919
|
+
directSignatureMap,
|
|
920
|
+
directFromCpfpSignatureMap,
|
|
764
921
|
leafDataMap,
|
|
765
922
|
};
|
|
766
923
|
}
|
|
@@ -772,19 +929,28 @@ export class TransferService extends BaseTransferService {
|
|
|
772
929
|
): Promise<{
|
|
773
930
|
transfer: Transfer;
|
|
774
931
|
signatureMap: Map<string, Uint8Array>;
|
|
932
|
+
directSignatureMap: Map<string, Uint8Array>;
|
|
933
|
+
directFromCpfpSignatureMap: Map<string, Uint8Array>;
|
|
775
934
|
leafDataMap: Map<string, LeafRefundSigningData>;
|
|
776
935
|
}> {
|
|
777
|
-
const {
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
936
|
+
const {
|
|
937
|
+
transfer,
|
|
938
|
+
signatureMap,
|
|
939
|
+
directSignatureMap,
|
|
940
|
+
directFromCpfpSignatureMap,
|
|
941
|
+
leafDataMap,
|
|
942
|
+
} = await this.sendTransferSignRefundInternal(
|
|
943
|
+
leaves,
|
|
944
|
+
receiverIdentityPubkey,
|
|
945
|
+
expiryTime,
|
|
946
|
+
true,
|
|
947
|
+
);
|
|
784
948
|
|
|
785
949
|
return {
|
|
786
950
|
transfer,
|
|
787
951
|
signatureMap,
|
|
952
|
+
directSignatureMap,
|
|
953
|
+
directFromCpfpSignatureMap,
|
|
788
954
|
leafDataMap,
|
|
789
955
|
};
|
|
790
956
|
}
|
|
@@ -793,10 +959,14 @@ export class TransferService extends BaseTransferService {
|
|
|
793
959
|
leaves: LeafKeyTweak[],
|
|
794
960
|
receiverIdentityPubkey: Uint8Array,
|
|
795
961
|
expiryTime: Date,
|
|
796
|
-
|
|
962
|
+
cpfpAdaptorPubKey?: Uint8Array,
|
|
963
|
+
directAdaptorPubKey?: Uint8Array,
|
|
964
|
+
directFromCpfpAdaptorPubKey?: Uint8Array,
|
|
797
965
|
): Promise<{
|
|
798
966
|
transfer: Transfer;
|
|
799
967
|
signatureMap: Map<string, Uint8Array>;
|
|
968
|
+
directSignatureMap: Map<string, Uint8Array>;
|
|
969
|
+
directFromCpfpSignatureMap: Map<string, Uint8Array>;
|
|
800
970
|
leafDataMap: Map<string, LeafRefundSigningData>;
|
|
801
971
|
signingResults: LeafRefundTxSigningResult[];
|
|
802
972
|
}> {
|
|
@@ -805,7 +975,9 @@ export class TransferService extends BaseTransferService {
|
|
|
805
975
|
receiverIdentityPubkey,
|
|
806
976
|
expiryTime,
|
|
807
977
|
true,
|
|
808
|
-
|
|
978
|
+
cpfpAdaptorPubKey,
|
|
979
|
+
directAdaptorPubKey,
|
|
980
|
+
directFromCpfpAdaptorPubKey,
|
|
809
981
|
);
|
|
810
982
|
}
|
|
811
983
|
|
|
@@ -814,10 +986,14 @@ export class TransferService extends BaseTransferService {
|
|
|
814
986
|
receiverIdentityPubkey: Uint8Array,
|
|
815
987
|
expiryTime: Date,
|
|
816
988
|
forSwap: boolean,
|
|
817
|
-
|
|
989
|
+
cpfpAdaptorPubKey?: Uint8Array,
|
|
990
|
+
directAdaptorPubKey?: Uint8Array,
|
|
991
|
+
directFromCpfpAdaptorPubKey?: Uint8Array,
|
|
818
992
|
): Promise<{
|
|
819
993
|
transfer: Transfer;
|
|
820
994
|
signatureMap: Map<string, Uint8Array>;
|
|
995
|
+
directSignatureMap: Map<string, Uint8Array>;
|
|
996
|
+
directFromCpfpSignatureMap: Map<string, Uint8Array>;
|
|
821
997
|
leafDataMap: Map<string, LeafRefundSigningData>;
|
|
822
998
|
signingResults: LeafRefundTxSigningResult[];
|
|
823
999
|
}> {
|
|
@@ -826,16 +1002,39 @@ export class TransferService extends BaseTransferService {
|
|
|
826
1002
|
for (const leaf of leaves) {
|
|
827
1003
|
const signingNonceCommitment =
|
|
828
1004
|
await this.config.signer.getRandomSigningCommitment();
|
|
1005
|
+
const directSigningNonceCommitment =
|
|
1006
|
+
await this.config.signer.getRandomSigningCommitment();
|
|
1007
|
+
const directFromCpfpRefundSigningNonceCommitment =
|
|
1008
|
+
await this.config.signer.getRandomSigningCommitment();
|
|
829
1009
|
|
|
830
1010
|
const tx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
|
|
831
1011
|
const refundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
|
|
832
1012
|
|
|
1013
|
+
const directTx =
|
|
1014
|
+
leaf.leaf.directTx.length > 0
|
|
1015
|
+
? getTxFromRawTxBytes(leaf.leaf.directTx)
|
|
1016
|
+
: undefined;
|
|
1017
|
+
|
|
1018
|
+
const directRefundTx =
|
|
1019
|
+
leaf.leaf.directRefundTx.length > 0
|
|
1020
|
+
? getTxFromRawTxBytes(leaf.leaf.directRefundTx)
|
|
1021
|
+
: undefined;
|
|
1022
|
+
const directFromCpfpRefundTx =
|
|
1023
|
+
leaf.leaf.directFromCpfpRefundTx.length > 0
|
|
1024
|
+
? getTxFromRawTxBytes(leaf.leaf.directFromCpfpRefundTx)
|
|
1025
|
+
: undefined;
|
|
1026
|
+
|
|
833
1027
|
leafDataMap.set(leaf.leaf.id, {
|
|
834
1028
|
keyDerivation: leaf.keyDerivation,
|
|
835
1029
|
receivingPubkey: receiverIdentityPubkey,
|
|
836
1030
|
signingNonceCommitment,
|
|
1031
|
+
directSigningNonceCommitment,
|
|
837
1032
|
tx,
|
|
1033
|
+
directTx,
|
|
838
1034
|
refundTx,
|
|
1035
|
+
directRefundTx,
|
|
1036
|
+
directFromCpfpRefundTx,
|
|
1037
|
+
directFromCpfpRefundSigningNonceCommitment,
|
|
839
1038
|
vout: leaf.leaf.vout,
|
|
840
1039
|
});
|
|
841
1040
|
}
|
|
@@ -844,15 +1043,19 @@ export class TransferService extends BaseTransferService {
|
|
|
844
1043
|
leaves,
|
|
845
1044
|
leafDataMap,
|
|
846
1045
|
);
|
|
847
|
-
|
|
1046
|
+
// 0197d19f-7419-7d51-9d2b-247127b5ab0a
|
|
848
1047
|
const sparkClient = await this.connectionManager.createSparkClient(
|
|
849
1048
|
this.config.getCoordinatorAddress(),
|
|
850
1049
|
);
|
|
851
1050
|
|
|
852
1051
|
let response: CounterLeafSwapResponse;
|
|
853
1052
|
try {
|
|
854
|
-
if (
|
|
855
|
-
|
|
1053
|
+
if (
|
|
1054
|
+
cpfpAdaptorPubKey !== undefined ||
|
|
1055
|
+
directAdaptorPubKey !== undefined ||
|
|
1056
|
+
directFromCpfpAdaptorPubKey !== undefined
|
|
1057
|
+
) {
|
|
1058
|
+
response = await sparkClient.counter_leaf_swap_v2({
|
|
856
1059
|
transfer: {
|
|
857
1060
|
transferId,
|
|
858
1061
|
leavesToSend: signingJobs,
|
|
@@ -862,10 +1065,12 @@ export class TransferService extends BaseTransferService {
|
|
|
862
1065
|
expiryTime: expiryTime,
|
|
863
1066
|
},
|
|
864
1067
|
swapId: uuidv7(),
|
|
865
|
-
adaptorPublicKey:
|
|
1068
|
+
adaptorPublicKey: cpfpAdaptorPubKey,
|
|
1069
|
+
directAdaptorPublicKey: directAdaptorPubKey,
|
|
1070
|
+
directFromCpfpAdaptorPublicKey: directFromCpfpAdaptorPubKey,
|
|
866
1071
|
});
|
|
867
1072
|
} else if (forSwap) {
|
|
868
|
-
response = await sparkClient.
|
|
1073
|
+
response = await sparkClient.start_leaf_swap_v2({
|
|
869
1074
|
transferId,
|
|
870
1075
|
leavesToSend: signingJobs,
|
|
871
1076
|
ownerIdentityPublicKey:
|
|
@@ -874,7 +1079,7 @@ export class TransferService extends BaseTransferService {
|
|
|
874
1079
|
expiryTime: expiryTime,
|
|
875
1080
|
});
|
|
876
1081
|
} else {
|
|
877
|
-
response = await sparkClient.
|
|
1082
|
+
response = await sparkClient.start_transfer_v2({
|
|
878
1083
|
transferId,
|
|
879
1084
|
leavesToSend: signingJobs,
|
|
880
1085
|
ownerIdentityPublicKey:
|
|
@@ -894,17 +1099,31 @@ export class TransferService extends BaseTransferService {
|
|
|
894
1099
|
const signatures = await this.signRefunds(
|
|
895
1100
|
leafDataMap,
|
|
896
1101
|
response.signingResults,
|
|
897
|
-
|
|
1102
|
+
cpfpAdaptorPubKey,
|
|
1103
|
+
directAdaptorPubKey,
|
|
1104
|
+
directFromCpfpAdaptorPubKey,
|
|
898
1105
|
);
|
|
899
1106
|
|
|
900
|
-
const
|
|
1107
|
+
const cpfpSignatureMap = new Map<string, Uint8Array>();
|
|
1108
|
+
const directSignatureMap = new Map<string, Uint8Array>();
|
|
1109
|
+
const directFromCpfpSignatureMap = new Map<string, Uint8Array>();
|
|
901
1110
|
for (const signature of signatures) {
|
|
902
|
-
|
|
1111
|
+
cpfpSignatureMap.set(signature.nodeId, signature.refundTxSignature);
|
|
1112
|
+
directSignatureMap.set(
|
|
1113
|
+
signature.nodeId,
|
|
1114
|
+
signature.directRefundTxSignature,
|
|
1115
|
+
);
|
|
1116
|
+
directFromCpfpSignatureMap.set(
|
|
1117
|
+
signature.nodeId,
|
|
1118
|
+
signature.directFromCpfpRefundTxSignature,
|
|
1119
|
+
);
|
|
903
1120
|
}
|
|
904
1121
|
|
|
905
1122
|
return {
|
|
906
1123
|
transfer: response.transfer,
|
|
907
|
-
signatureMap,
|
|
1124
|
+
signatureMap: cpfpSignatureMap,
|
|
1125
|
+
directSignatureMap: directSignatureMap,
|
|
1126
|
+
directFromCpfpSignatureMap: directFromCpfpSignatureMap,
|
|
908
1127
|
leafDataMap,
|
|
909
1128
|
signingResults: response.signingResults,
|
|
910
1129
|
};
|
|
@@ -923,48 +1142,89 @@ export class TransferService extends BaseTransferService {
|
|
|
923
1142
|
}
|
|
924
1143
|
|
|
925
1144
|
const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
|
|
926
|
-
const
|
|
1145
|
+
const cpfpNodeOutPoint: TransactionInput = {
|
|
927
1146
|
txid: hexToBytes(getTxId(nodeTx)),
|
|
928
1147
|
index: 0,
|
|
929
1148
|
};
|
|
930
1149
|
|
|
1150
|
+
let directNodeTx: Transaction | undefined;
|
|
1151
|
+
let directNodeOutPoint: TransactionInput | undefined;
|
|
1152
|
+
if (leaf.leaf.directTx.length > 0) {
|
|
1153
|
+
directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
|
|
1154
|
+
directNodeOutPoint = {
|
|
1155
|
+
txid: hexToBytes(getTxId(directNodeTx)),
|
|
1156
|
+
index: 0,
|
|
1157
|
+
};
|
|
1158
|
+
}
|
|
1159
|
+
|
|
931
1160
|
const currRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
1161
|
+
|
|
1162
|
+
const sequence = currRefundTx.getInput(0).sequence;
|
|
1163
|
+
if (!sequence) {
|
|
1164
|
+
throw new ValidationError("Invalid refund transaction", {
|
|
1165
|
+
field: "sequence",
|
|
1166
|
+
value: currRefundTx.getInput(0),
|
|
1167
|
+
expected: "Non-null sequence",
|
|
1168
|
+
});
|
|
1169
|
+
}
|
|
1170
|
+
const { nextSequence, nextDirectSequence } = isForClaim
|
|
1171
|
+
? getTransactionSequence(sequence)
|
|
1172
|
+
: getNextTransactionSequence(sequence);
|
|
936
1173
|
|
|
937
1174
|
const amountSats = currRefundTx.getOutput(0).amount;
|
|
938
1175
|
if (amountSats === undefined) {
|
|
939
1176
|
throw new Error("Amount not found in signRefunds");
|
|
940
1177
|
}
|
|
941
1178
|
|
|
942
|
-
const
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
1179
|
+
const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } =
|
|
1180
|
+
createRefundTxs({
|
|
1181
|
+
sequence: nextSequence,
|
|
1182
|
+
directSequence: nextDirectSequence,
|
|
1183
|
+
input: cpfpNodeOutPoint,
|
|
1184
|
+
directInput: directNodeOutPoint,
|
|
1185
|
+
amountSats,
|
|
1186
|
+
receivingPubkey: refundSigningData.receivingPubkey,
|
|
1187
|
+
network: this.config.getNetwork(),
|
|
1188
|
+
});
|
|
949
1189
|
|
|
950
|
-
refundSigningData.refundTx =
|
|
1190
|
+
refundSigningData.refundTx = cpfpRefundTx;
|
|
1191
|
+
refundSigningData.directRefundTx = directRefundTx;
|
|
1192
|
+
refundSigningData.directFromCpfpRefundTx = directFromCpfpRefundTx;
|
|
951
1193
|
|
|
952
|
-
const
|
|
1194
|
+
const cpfpRefundNonceCommitmentProto =
|
|
953
1195
|
refundSigningData.signingNonceCommitment;
|
|
954
|
-
|
|
1196
|
+
const directRefundNonceCommitmentProto =
|
|
1197
|
+
refundSigningData.directSigningNonceCommitment;
|
|
1198
|
+
const directFromCpfpRefundNonceCommitmentProto =
|
|
1199
|
+
refundSigningData.directFromCpfpRefundSigningNonceCommitment;
|
|
1200
|
+
|
|
1201
|
+
const signingPublicKey =
|
|
1202
|
+
await this.config.signer.getPublicKeyFromDerivation(
|
|
1203
|
+
refundSigningData.keyDerivation,
|
|
1204
|
+
);
|
|
955
1205
|
signingJobs.push({
|
|
956
1206
|
leafId: leaf.leaf.id,
|
|
957
1207
|
refundTxSigningJob: {
|
|
958
|
-
signingPublicKey
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
rawTx: refundTx.toBytes(),
|
|
962
|
-
signingNonceCommitment: refundNonceCommitmentProto.commitment,
|
|
1208
|
+
signingPublicKey,
|
|
1209
|
+
rawTx: cpfpRefundTx.toBytes(),
|
|
1210
|
+
signingNonceCommitment: cpfpRefundNonceCommitmentProto.commitment,
|
|
963
1211
|
},
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
1212
|
+
directRefundTxSigningJob: directRefundTx
|
|
1213
|
+
? {
|
|
1214
|
+
signingPublicKey,
|
|
1215
|
+
rawTx: directRefundTx.toBytes(),
|
|
1216
|
+
signingNonceCommitment:
|
|
1217
|
+
directRefundNonceCommitmentProto.commitment,
|
|
1218
|
+
}
|
|
1219
|
+
: undefined,
|
|
1220
|
+
directFromCpfpRefundTxSigningJob: directFromCpfpRefundTx
|
|
1221
|
+
? {
|
|
1222
|
+
signingPublicKey,
|
|
1223
|
+
rawTx: directFromCpfpRefundTx.toBytes(),
|
|
1224
|
+
signingNonceCommitment:
|
|
1225
|
+
directFromCpfpRefundNonceCommitmentProto.commitment,
|
|
1226
|
+
}
|
|
1227
|
+
: undefined,
|
|
968
1228
|
});
|
|
969
1229
|
}
|
|
970
1230
|
|
|
@@ -1122,6 +1382,11 @@ export class TransferService extends BaseTransferService {
|
|
|
1122
1382
|
const leafDataMap: Map<string, LeafRefundSigningData> = new Map();
|
|
1123
1383
|
for (const leafKey of leafKeys) {
|
|
1124
1384
|
const tx = getTxFromRawTxBytes(leafKey.leaf.nodeTx);
|
|
1385
|
+
const directTx =
|
|
1386
|
+
leafKey.leaf.directTx.length > 0
|
|
1387
|
+
? getTxFromRawTxBytes(leafKey.leaf.directTx)
|
|
1388
|
+
: undefined;
|
|
1389
|
+
|
|
1125
1390
|
leafDataMap.set(leafKey.leaf.id, {
|
|
1126
1391
|
keyDerivation: leafKey.newKeyDerivation,
|
|
1127
1392
|
receivingPubkey: await this.config.signer.getPublicKeyFromDerivation(
|
|
@@ -1129,7 +1394,12 @@ export class TransferService extends BaseTransferService {
|
|
|
1129
1394
|
),
|
|
1130
1395
|
signingNonceCommitment:
|
|
1131
1396
|
await this.config.signer.getRandomSigningCommitment(),
|
|
1397
|
+
directSigningNonceCommitment:
|
|
1398
|
+
await this.config.signer.getRandomSigningCommitment(),
|
|
1399
|
+
directFromCpfpRefundSigningNonceCommitment:
|
|
1400
|
+
await this.config.signer.getRandomSigningCommitment(),
|
|
1132
1401
|
tx,
|
|
1402
|
+
directTx,
|
|
1133
1403
|
vout: leafKey.leaf.vout,
|
|
1134
1404
|
});
|
|
1135
1405
|
}
|
|
@@ -1154,7 +1424,7 @@ export class TransferService extends BaseTransferService {
|
|
|
1154
1424
|
}
|
|
1155
1425
|
}
|
|
1156
1426
|
try {
|
|
1157
|
-
resp = await sparkClient.
|
|
1427
|
+
resp = await sparkClient.claim_transfer_sign_refunds_v2({
|
|
1158
1428
|
transferId: transfer.id,
|
|
1159
1429
|
ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
|
|
1160
1430
|
signingJobs,
|
|
@@ -1170,7 +1440,7 @@ export class TransferService extends BaseTransferService {
|
|
|
1170
1440
|
this.config.getCoordinatorAddress(),
|
|
1171
1441
|
);
|
|
1172
1442
|
try {
|
|
1173
|
-
return await sparkClient.
|
|
1443
|
+
return await sparkClient.finalize_node_signatures_v2({
|
|
1174
1444
|
intent: SignatureIntent.TRANSFER,
|
|
1175
1445
|
nodeSignatures,
|
|
1176
1446
|
});
|
|
@@ -1223,135 +1493,180 @@ export class TransferService extends BaseTransferService {
|
|
|
1223
1493
|
}
|
|
1224
1494
|
}
|
|
1225
1495
|
|
|
1226
|
-
async
|
|
1227
|
-
|
|
1228
|
-
|
|
1496
|
+
private async refreshTimelockNodesInternal(
|
|
1497
|
+
node: TreeNode,
|
|
1498
|
+
parentNode: TreeNode,
|
|
1499
|
+
useTestUnilateralSequence?: boolean,
|
|
1500
|
+
) {
|
|
1501
|
+
const signingJobs: (SigningJobWithOptionalNonce & {
|
|
1502
|
+
type: "node" | "directNode" | "cpfp" | "direct" | "directFromCpfp";
|
|
1503
|
+
parentTxOut: TransactionOutput;
|
|
1504
|
+
})[] = [];
|
|
1505
|
+
|
|
1506
|
+
const parentNodeTx = getTxFromRawTxBytes(parentNode.nodeTx);
|
|
1507
|
+
const parentNodeOutput: TransactionOutput = parentNodeTx.getOutput(0);
|
|
1508
|
+
if (!parentNodeOutput) {
|
|
1509
|
+
throw Error("Could not get parent node output");
|
|
1229
1510
|
}
|
|
1230
1511
|
|
|
1231
|
-
const
|
|
1232
|
-
const
|
|
1233
|
-
|
|
1234
|
-
for (let i = 0; i < nodes.length; i++) {
|
|
1235
|
-
const node = nodes[i];
|
|
1236
|
-
if (!node) {
|
|
1237
|
-
throw Error("could not get node");
|
|
1238
|
-
}
|
|
1239
|
-
const nodeTx = getTxFromRawTxBytes(node?.nodeTx);
|
|
1240
|
-
const input = nodeTx.getInput(0);
|
|
1512
|
+
const nodeTx = getTxFromRawTxBytes(node.nodeTx);
|
|
1513
|
+
const nodeInput = nodeTx.getInput(0);
|
|
1241
1514
|
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1515
|
+
const nodeOutput = nodeTx.getOutput(0);
|
|
1516
|
+
if (!nodeOutput) {
|
|
1517
|
+
throw Error("Could not get node output");
|
|
1518
|
+
}
|
|
1245
1519
|
|
|
1246
|
-
|
|
1520
|
+
let directNodeTx: Transaction | undefined;
|
|
1521
|
+
let directNodeInput: TransactionInput | undefined;
|
|
1522
|
+
if (node.directTx.length > 0) {
|
|
1523
|
+
directNodeTx = getTxFromRawTxBytes(node.directTx);
|
|
1524
|
+
directNodeInput = directNodeTx.getInput(0);
|
|
1525
|
+
}
|
|
1247
1526
|
|
|
1248
|
-
|
|
1249
|
-
const originalOutput = nodeTx.getOutput(0);
|
|
1250
|
-
if (!originalOutput) {
|
|
1251
|
-
throw Error("Could not get original output");
|
|
1252
|
-
}
|
|
1527
|
+
const currSequence = nodeInput.sequence;
|
|
1253
1528
|
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1529
|
+
if (!currSequence) {
|
|
1530
|
+
throw new ValidationError("Invalid node transaction", {
|
|
1531
|
+
field: "sequence",
|
|
1532
|
+
value: nodeInput,
|
|
1533
|
+
expected: "Non-null sequence",
|
|
1257
1534
|
});
|
|
1535
|
+
}
|
|
1258
1536
|
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
newTx.addOutput(additionalOutput);
|
|
1264
|
-
}
|
|
1265
|
-
}
|
|
1537
|
+
let { nextSequence, nextDirectSequence } = getNextTransactionSequence(
|
|
1538
|
+
currSequence,
|
|
1539
|
+
true,
|
|
1540
|
+
);
|
|
1266
1541
|
|
|
1267
|
-
|
|
1268
|
-
|
|
1542
|
+
const output = {
|
|
1543
|
+
script: parentNodeOutput.script!,
|
|
1544
|
+
amount: parentNodeOutput.amount!,
|
|
1545
|
+
};
|
|
1269
1546
|
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
sequence: initialSequence(),
|
|
1278
|
-
txid: newNodeTxs[i - 1]?.id,
|
|
1279
|
-
});
|
|
1280
|
-
}
|
|
1547
|
+
const newNodeInput: TransactionInput = {
|
|
1548
|
+
txid: nodeInput.txid,
|
|
1549
|
+
index: nodeInput.index,
|
|
1550
|
+
sequence: useTestUnilateralSequence
|
|
1551
|
+
? TEST_UNILATERAL_SEQUENCE
|
|
1552
|
+
: nextSequence,
|
|
1553
|
+
};
|
|
1281
1554
|
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1555
|
+
const newDirectInput: TransactionInput | undefined =
|
|
1556
|
+
directNodeTx && directNodeInput
|
|
1557
|
+
? {
|
|
1558
|
+
txid: directNodeInput.txid,
|
|
1559
|
+
index: directNodeInput.index,
|
|
1560
|
+
sequence: useTestUnilateralSequence
|
|
1561
|
+
? TEST_UNILATERAL_DIRECT_SEQUENCE
|
|
1562
|
+
: nextDirectSequence,
|
|
1563
|
+
}
|
|
1564
|
+
: undefined;
|
|
1565
|
+
|
|
1566
|
+
const { cpfpNodeTx, directNodeTx: newDirectNodeTx } = createNodeTxs(
|
|
1567
|
+
output,
|
|
1568
|
+
newNodeInput,
|
|
1569
|
+
newDirectInput,
|
|
1570
|
+
);
|
|
1293
1571
|
|
|
1294
|
-
const
|
|
1295
|
-
if (!
|
|
1296
|
-
throw Error("
|
|
1572
|
+
const newCpfpNodeOutput: TransactionOutput = cpfpNodeTx.getOutput(0);
|
|
1573
|
+
if (!newCpfpNodeOutput) {
|
|
1574
|
+
throw Error("Could not get new cpfp node output");
|
|
1297
1575
|
}
|
|
1298
|
-
const refundTx = getTxFromRawTxBytes(leaf?.refundTx);
|
|
1299
|
-
const newRefundTx = new Transaction({
|
|
1300
|
-
version: 3,
|
|
1301
|
-
allowUnknownOutputs: true,
|
|
1302
|
-
});
|
|
1303
1576
|
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
if (!originalRefundOutput) {
|
|
1307
|
-
throw Error("Could not get original refund output");
|
|
1308
|
-
}
|
|
1577
|
+
const newDirectNodeOutput: TransactionOutput | undefined =
|
|
1578
|
+
newDirectNodeTx?.getOutput(0);
|
|
1309
1579
|
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1580
|
+
const signingPublicKey =
|
|
1581
|
+
await this.config.signer.getPublicKeyFromDerivation({
|
|
1582
|
+
type: KeyDerivationType.LEAF,
|
|
1583
|
+
path: node.id,
|
|
1584
|
+
});
|
|
1585
|
+
|
|
1586
|
+
signingJobs.push({
|
|
1587
|
+
signingPublicKey,
|
|
1588
|
+
rawTx: cpfpNodeTx.toBytes(),
|
|
1589
|
+
signingNonceCommitment:
|
|
1590
|
+
await this.config.signer.getRandomSigningCommitment(),
|
|
1591
|
+
type: "node",
|
|
1592
|
+
parentTxOut: parentNodeOutput,
|
|
1313
1593
|
});
|
|
1314
1594
|
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1595
|
+
if (newDirectNodeTx) {
|
|
1596
|
+
signingJobs.push({
|
|
1597
|
+
signingPublicKey,
|
|
1598
|
+
rawTx: newDirectNodeTx.toBytes(),
|
|
1599
|
+
signingNonceCommitment:
|
|
1600
|
+
await this.config.signer.getRandomSigningCommitment(),
|
|
1601
|
+
type: "directNode",
|
|
1602
|
+
parentTxOut: parentNodeOutput,
|
|
1603
|
+
});
|
|
1321
1604
|
}
|
|
1322
1605
|
|
|
1323
|
-
const
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
}
|
|
1606
|
+
const newCpfpRefundOutPoint: TransactionInput = {
|
|
1607
|
+
txid: hexToBytes(getTxId(cpfpNodeTx)!),
|
|
1608
|
+
index: 0,
|
|
1609
|
+
};
|
|
1327
1610
|
|
|
1328
|
-
|
|
1329
|
-
|
|
1611
|
+
let newDirectRefundOutPoint: TransactionInput | undefined;
|
|
1612
|
+
if (newDirectNodeTx) {
|
|
1613
|
+
newDirectRefundOutPoint = {
|
|
1614
|
+
txid: hexToBytes(getTxId(newDirectNodeTx)!),
|
|
1615
|
+
index: 0,
|
|
1616
|
+
};
|
|
1330
1617
|
}
|
|
1331
|
-
newRefundTx.addInput({
|
|
1332
|
-
...refundTxInput,
|
|
1333
|
-
sequence: initialSequence(),
|
|
1334
|
-
txid: getTxId(newNodeTxs[newNodeTxs.length - 1]!),
|
|
1335
|
-
});
|
|
1336
1618
|
|
|
1337
|
-
const
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1619
|
+
const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } =
|
|
1620
|
+
createRefundTxs({
|
|
1621
|
+
sequence: INITIAL_SEQUENCE,
|
|
1622
|
+
directSequence: INITIAL_DIRECT_SEQUENCE,
|
|
1623
|
+
input: newCpfpRefundOutPoint,
|
|
1624
|
+
directInput: newDirectRefundOutPoint,
|
|
1625
|
+
amountSats: nodeOutput.amount!,
|
|
1626
|
+
receivingPubkey: await this.config.signer.getPublicKeyFromDerivation({
|
|
1627
|
+
type: KeyDerivationType.LEAF,
|
|
1628
|
+
path: node.id,
|
|
1629
|
+
}),
|
|
1630
|
+
network: this.config.getNetwork(),
|
|
1631
|
+
});
|
|
1632
|
+
|
|
1633
|
+
signingJobs.push({
|
|
1634
|
+
signingPublicKey,
|
|
1635
|
+
rawTx: cpfpRefundTx.toBytes(),
|
|
1343
1636
|
signingNonceCommitment:
|
|
1344
1637
|
await this.config.signer.getRandomSigningCommitment(),
|
|
1345
|
-
|
|
1638
|
+
type: "cpfp",
|
|
1639
|
+
parentTxOut: newCpfpNodeOutput,
|
|
1640
|
+
});
|
|
1641
|
+
|
|
1642
|
+
if (directRefundTx && newDirectNodeOutput) {
|
|
1643
|
+
signingJobs.push({
|
|
1644
|
+
signingPublicKey,
|
|
1645
|
+
rawTx: directRefundTx.toBytes(),
|
|
1646
|
+
signingNonceCommitment:
|
|
1647
|
+
await this.config.signer.getRandomSigningCommitment(),
|
|
1648
|
+
type: "direct",
|
|
1649
|
+
parentTxOut: newDirectNodeOutput,
|
|
1650
|
+
});
|
|
1651
|
+
}
|
|
1346
1652
|
|
|
1347
|
-
|
|
1653
|
+
if (directFromCpfpRefundTx && newCpfpNodeOutput) {
|
|
1654
|
+
signingJobs.push({
|
|
1655
|
+
signingPublicKey,
|
|
1656
|
+
rawTx: directFromCpfpRefundTx.toBytes(),
|
|
1657
|
+
signingNonceCommitment:
|
|
1658
|
+
await this.config.signer.getRandomSigningCommitment(),
|
|
1659
|
+
type: "directFromCpfp",
|
|
1660
|
+
parentTxOut: newCpfpNodeOutput,
|
|
1661
|
+
});
|
|
1662
|
+
}
|
|
1348
1663
|
|
|
1349
1664
|
const sparkClient = await this.connectionManager.createSparkClient(
|
|
1350
1665
|
this.config.getCoordinatorAddress(),
|
|
1351
1666
|
);
|
|
1352
1667
|
|
|
1353
|
-
const response = await sparkClient.
|
|
1354
|
-
leafId:
|
|
1668
|
+
const response = await sparkClient.refresh_timelock_v2({
|
|
1669
|
+
leafId: node.id,
|
|
1355
1670
|
ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
|
|
1356
1671
|
signingJobs: signingJobs.map(getSigningJobProto),
|
|
1357
1672
|
});
|
|
@@ -1363,52 +1678,31 @@ export class TransferService extends BaseTransferService {
|
|
|
1363
1678
|
}
|
|
1364
1679
|
|
|
1365
1680
|
let nodeSignatures: NodeSignatures[] = [];
|
|
1366
|
-
let
|
|
1367
|
-
let
|
|
1368
|
-
let
|
|
1369
|
-
|
|
1370
|
-
|
|
1681
|
+
let leafCpfpSignature: Uint8Array | undefined;
|
|
1682
|
+
let leafDirectSignature: Uint8Array | undefined;
|
|
1683
|
+
let cpfpRefundSignature: Uint8Array | undefined;
|
|
1684
|
+
let directRefundSignature: Uint8Array | undefined;
|
|
1685
|
+
let directFromCpfpRefundSignature: Uint8Array | undefined;
|
|
1686
|
+
|
|
1687
|
+
for (const [i, signingResult] of response.signingResults.entries()) {
|
|
1371
1688
|
const signingJob = signingJobs[i];
|
|
1372
1689
|
if (!signingJob || !signingResult) {
|
|
1373
1690
|
throw Error("Signing job does not exist");
|
|
1374
1691
|
}
|
|
1375
1692
|
|
|
1376
|
-
if (!signingJob.signingNonceCommitment) {
|
|
1377
|
-
throw Error("nonce commitment does not exist");
|
|
1378
|
-
}
|
|
1379
1693
|
const rawTx = getTxFromRawTxBytes(signingJob.rawTx);
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
let vout: number | undefined;
|
|
1384
|
-
|
|
1385
|
-
if (i === nodes.length) {
|
|
1386
|
-
nodeId = nodes[i - 1]?.id;
|
|
1387
|
-
parentTx = newNodeTxs[i - 1];
|
|
1388
|
-
vout = 0;
|
|
1389
|
-
} else if (i === 0) {
|
|
1390
|
-
nodeId = nodes[i]?.id;
|
|
1391
|
-
parentTx = getTxFromRawTxBytes(parentNode.nodeTx);
|
|
1392
|
-
vout = nodes[i]?.vout;
|
|
1393
|
-
} else {
|
|
1394
|
-
nodeId = nodes[i]?.id;
|
|
1395
|
-
parentTx = newNodeTxs[i - 1];
|
|
1396
|
-
vout = nodes[i]?.vout;
|
|
1694
|
+
const txOut = signingJob.parentTxOut;
|
|
1695
|
+
if (!txOut) {
|
|
1696
|
+
throw Error("Could not get tx out");
|
|
1397
1697
|
}
|
|
1398
1698
|
|
|
1399
|
-
if (!parentTx || !nodeId || vout === undefined) {
|
|
1400
|
-
throw Error("Could not parse signing results");
|
|
1401
|
-
}
|
|
1402
|
-
|
|
1403
|
-
const txOut = parentTx.getOutput(vout);
|
|
1404
|
-
|
|
1405
1699
|
const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
|
|
1406
1700
|
|
|
1407
1701
|
const userSignature = await this.config.signer.signFrost({
|
|
1408
1702
|
message: rawTxSighash,
|
|
1409
1703
|
keyDerivation: {
|
|
1410
1704
|
type: KeyDerivationType.LEAF,
|
|
1411
|
-
path:
|
|
1705
|
+
path: node.id,
|
|
1412
1706
|
},
|
|
1413
1707
|
publicKey: signingJob.signingPublicKey,
|
|
1414
1708
|
verifyingKey: signingResult.verifyingKey,
|
|
@@ -1431,39 +1725,30 @@ export class TransferService extends BaseTransferService {
|
|
|
1431
1725
|
adaptorPubKey: new Uint8Array(),
|
|
1432
1726
|
});
|
|
1433
1727
|
|
|
1434
|
-
if (
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
} else if (i === nodes.length) {
|
|
1445
|
-
refundSignature = signature;
|
|
1446
|
-
} else if (i === nodes.length - 1) {
|
|
1447
|
-
leafNodeId = nodeId;
|
|
1448
|
-
leafSignature = signature;
|
|
1728
|
+
if (signingJob.type === "node") {
|
|
1729
|
+
leafCpfpSignature = signature;
|
|
1730
|
+
} else if (signingJob.type === "directNode") {
|
|
1731
|
+
leafDirectSignature = signature;
|
|
1732
|
+
} else if (signingJob.type === "cpfp") {
|
|
1733
|
+
cpfpRefundSignature = signature;
|
|
1734
|
+
} else if (signingJob.type === "direct") {
|
|
1735
|
+
directRefundSignature = signature;
|
|
1736
|
+
} else if (signingJob.type === "directFromCpfp") {
|
|
1737
|
+
directFromCpfpRefundSignature = signature;
|
|
1449
1738
|
}
|
|
1450
1739
|
}
|
|
1451
1740
|
|
|
1452
|
-
if (!leafSignature || !refundSignature || !leafNodeId) {
|
|
1453
|
-
throw Error("leaf or refund signature does not exist");
|
|
1454
|
-
}
|
|
1455
|
-
|
|
1456
1741
|
nodeSignatures.push({
|
|
1457
|
-
nodeId:
|
|
1458
|
-
nodeTxSignature:
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1742
|
+
nodeId: node.id,
|
|
1743
|
+
nodeTxSignature: leafCpfpSignature || new Uint8Array(),
|
|
1744
|
+
directNodeTxSignature: leafDirectSignature || new Uint8Array(),
|
|
1745
|
+
refundTxSignature: cpfpRefundSignature || new Uint8Array(),
|
|
1746
|
+
directRefundTxSignature: directRefundSignature || new Uint8Array(),
|
|
1747
|
+
directFromCpfpRefundTxSignature:
|
|
1748
|
+
directFromCpfpRefundSignature || new Uint8Array(),
|
|
1464
1749
|
});
|
|
1465
1750
|
|
|
1466
|
-
const result = await sparkClient.
|
|
1751
|
+
const result = await sparkClient.finalize_node_signatures_v2({
|
|
1467
1752
|
intent: SignatureIntent.REFRESH,
|
|
1468
1753
|
nodeSignatures,
|
|
1469
1754
|
});
|
|
@@ -1471,6 +1756,10 @@ export class TransferService extends BaseTransferService {
|
|
|
1471
1756
|
return result;
|
|
1472
1757
|
}
|
|
1473
1758
|
|
|
1759
|
+
async refreshTimelockNodes(node: TreeNode, parentNode: TreeNode) {
|
|
1760
|
+
return await this.refreshTimelockNodesInternal(node, parentNode);
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1474
1763
|
async extendTimelock(node: TreeNode) {
|
|
1475
1764
|
const nodeTx = getTxFromRawTxBytes(node.nodeTx);
|
|
1476
1765
|
const refundTx = getTxFromRawTxBytes(node.refundTx);
|
|
@@ -1481,8 +1770,12 @@ export class TransferService extends BaseTransferService {
|
|
|
1481
1770
|
index: 0,
|
|
1482
1771
|
};
|
|
1483
1772
|
|
|
1484
|
-
const {
|
|
1485
|
-
|
|
1773
|
+
const {
|
|
1774
|
+
nextSequence: newNodeSequence,
|
|
1775
|
+
nextDirectSequence: newDirectNodeSequence,
|
|
1776
|
+
} = getNextTransactionSequence(refundSequence);
|
|
1777
|
+
|
|
1778
|
+
// Create new CPFP node tx
|
|
1486
1779
|
const newNodeTx = new Transaction({
|
|
1487
1780
|
version: 3,
|
|
1488
1781
|
allowUnknownOutputs: true,
|
|
@@ -1495,82 +1788,177 @@ export class TransferService extends BaseTransferService {
|
|
|
1495
1788
|
throw Error("Could not get original node output");
|
|
1496
1789
|
}
|
|
1497
1790
|
|
|
1498
|
-
// const feeReducedAmount = maybeApplyFee(originalOutput.amount!);
|
|
1499
1791
|
newNodeTx.addOutput({
|
|
1500
1792
|
script: originalOutput.script!,
|
|
1501
|
-
amount: originalOutput.amount!,
|
|
1793
|
+
amount: originalOutput.amount!,
|
|
1502
1794
|
});
|
|
1503
1795
|
|
|
1504
1796
|
newNodeTx.addOutput(getEphemeralAnchorOutput());
|
|
1505
1797
|
|
|
1506
|
-
|
|
1798
|
+
// Create new direct node tx
|
|
1799
|
+
|
|
1800
|
+
let newDirectNodeTx: Transaction | undefined;
|
|
1801
|
+
if (node.directTx.length > 0) {
|
|
1802
|
+
newDirectNodeTx = new Transaction({
|
|
1803
|
+
version: 3,
|
|
1804
|
+
allowUnknownOutputs: true,
|
|
1805
|
+
});
|
|
1806
|
+
|
|
1807
|
+
newDirectNodeTx.addInput({
|
|
1808
|
+
...newNodeOutPoint,
|
|
1809
|
+
sequence: newDirectNodeSequence,
|
|
1810
|
+
});
|
|
1811
|
+
|
|
1812
|
+
newDirectNodeTx.addOutput({
|
|
1813
|
+
script: originalOutput.script!,
|
|
1814
|
+
amount: maybeApplyFee(originalOutput.amount!),
|
|
1815
|
+
});
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
const newCpfpRefundOutPoint: TransactionInput = {
|
|
1507
1819
|
txid: hexToBytes(getTxId(newNodeTx)!),
|
|
1508
1820
|
index: 0,
|
|
1509
1821
|
};
|
|
1510
1822
|
|
|
1823
|
+
let newDirectRefundOutPoint: TransactionInput | undefined;
|
|
1824
|
+
if (newDirectNodeTx) {
|
|
1825
|
+
newDirectRefundOutPoint = {
|
|
1826
|
+
txid: hexToBytes(getTxId(newDirectNodeTx)!),
|
|
1827
|
+
index: 0,
|
|
1828
|
+
};
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1511
1831
|
const amountSats = refundTx.getOutput(0).amount;
|
|
1512
1832
|
if (amountSats === undefined) {
|
|
1513
1833
|
throw new Error("Amount not found in extendTimelock");
|
|
1514
1834
|
}
|
|
1515
1835
|
|
|
1516
|
-
const
|
|
1517
|
-
|
|
1518
|
-
|
|
1836
|
+
const signingPublicKey =
|
|
1837
|
+
await this.config.signer.getPublicKeyFromDerivation({
|
|
1838
|
+
type: KeyDerivationType.LEAF,
|
|
1839
|
+
path: node.id,
|
|
1840
|
+
});
|
|
1841
|
+
// Create both CPFP and direct refund transactions
|
|
1842
|
+
|
|
1843
|
+
const {
|
|
1844
|
+
cpfpRefundTx: newCpfpRefundTx,
|
|
1845
|
+
directRefundTx: newDirectRefundTx,
|
|
1846
|
+
directFromCpfpRefundTx: newDirectFromCpfpRefundTx,
|
|
1847
|
+
} = createRefundTxs({
|
|
1848
|
+
sequence: INITIAL_SEQUENCE,
|
|
1849
|
+
directSequence: INITIAL_DIRECT_SEQUENCE,
|
|
1850
|
+
input: newCpfpRefundOutPoint,
|
|
1851
|
+
directInput: newDirectRefundOutPoint,
|
|
1852
|
+
amountSats,
|
|
1853
|
+
receivingPubkey: signingPublicKey,
|
|
1854
|
+
network: this.config.getNetwork(),
|
|
1519
1855
|
});
|
|
1520
1856
|
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
amountSats, // feeReducedRefundAmount,
|
|
1527
|
-
signingPubKey,
|
|
1528
|
-
this.config.getNetwork(),
|
|
1529
|
-
);
|
|
1857
|
+
if (!newCpfpRefundTx) {
|
|
1858
|
+
throw new ValidationError(
|
|
1859
|
+
"Failed to create refund transactions in extendTimelock",
|
|
1860
|
+
);
|
|
1861
|
+
}
|
|
1530
1862
|
|
|
1531
1863
|
const nodeSighash = getSigHashFromTx(newNodeTx, 0, nodeTx.getOutput(0));
|
|
1532
|
-
|
|
1533
|
-
|
|
1864
|
+
|
|
1865
|
+
const directNodeSighash = newDirectNodeTx
|
|
1866
|
+
? getSigHashFromTx(newDirectNodeTx, 0, nodeTx.getOutput(0))
|
|
1867
|
+
: undefined;
|
|
1868
|
+
|
|
1869
|
+
const cpfpRefundSighash = getSigHashFromTx(
|
|
1870
|
+
newCpfpRefundTx,
|
|
1534
1871
|
0,
|
|
1535
1872
|
newNodeTx.getOutput(0),
|
|
1536
1873
|
);
|
|
1874
|
+
const directRefundSighash =
|
|
1875
|
+
newDirectNodeTx && newDirectRefundTx
|
|
1876
|
+
? getSigHashFromTx(newDirectRefundTx, 0, newDirectNodeTx.getOutput(0))
|
|
1877
|
+
: undefined;
|
|
1878
|
+
|
|
1879
|
+
const directFromCpfpRefundSighash = newDirectFromCpfpRefundTx
|
|
1880
|
+
? getSigHashFromTx(newDirectFromCpfpRefundTx, 0, newNodeTx.getOutput(0))
|
|
1881
|
+
: undefined;
|
|
1537
1882
|
|
|
1538
1883
|
const newNodeSigningJob = {
|
|
1539
|
-
signingPublicKey
|
|
1884
|
+
signingPublicKey,
|
|
1540
1885
|
rawTx: newNodeTx.toBytes(),
|
|
1541
1886
|
signingNonceCommitment:
|
|
1542
1887
|
await this.config.signer.getRandomSigningCommitment(),
|
|
1543
1888
|
};
|
|
1544
1889
|
|
|
1545
|
-
const
|
|
1546
|
-
|
|
1547
|
-
|
|
1890
|
+
const newDirectNodeSigningJob: SigningJobWithOptionalNonce | undefined =
|
|
1891
|
+
newDirectNodeTx
|
|
1892
|
+
? {
|
|
1893
|
+
signingPublicKey,
|
|
1894
|
+
rawTx: newDirectNodeTx.toBytes(),
|
|
1895
|
+
signingNonceCommitment:
|
|
1896
|
+
await this.config.signer.getRandomSigningCommitment(),
|
|
1897
|
+
}
|
|
1898
|
+
: undefined;
|
|
1899
|
+
|
|
1900
|
+
const newCpfpRefundSigningJob = {
|
|
1901
|
+
signingPublicKey,
|
|
1902
|
+
rawTx: newCpfpRefundTx.toBytes(),
|
|
1548
1903
|
signingNonceCommitment:
|
|
1549
1904
|
await this.config.signer.getRandomSigningCommitment(),
|
|
1550
1905
|
};
|
|
1551
1906
|
|
|
1907
|
+
const newDirectRefundSigningJob: SigningJobWithOptionalNonce | undefined =
|
|
1908
|
+
newDirectRefundTx
|
|
1909
|
+
? {
|
|
1910
|
+
signingPublicKey,
|
|
1911
|
+
rawTx: newDirectRefundTx.toBytes(),
|
|
1912
|
+
signingNonceCommitment:
|
|
1913
|
+
await this.config.signer.getRandomSigningCommitment(),
|
|
1914
|
+
}
|
|
1915
|
+
: undefined;
|
|
1916
|
+
|
|
1917
|
+
const newDirectFromCpfpRefundSigningJob:
|
|
1918
|
+
| SigningJobWithOptionalNonce
|
|
1919
|
+
| undefined = newDirectFromCpfpRefundTx
|
|
1920
|
+
? {
|
|
1921
|
+
signingPublicKey,
|
|
1922
|
+
rawTx: newDirectFromCpfpRefundTx.toBytes(),
|
|
1923
|
+
signingNonceCommitment:
|
|
1924
|
+
await this.config.signer.getRandomSigningCommitment(),
|
|
1925
|
+
}
|
|
1926
|
+
: undefined;
|
|
1927
|
+
|
|
1552
1928
|
const sparkClient = await this.connectionManager.createSparkClient(
|
|
1553
1929
|
this.config.getCoordinatorAddress(),
|
|
1554
1930
|
);
|
|
1555
1931
|
|
|
1556
|
-
const response = await sparkClient.
|
|
1932
|
+
const response = await sparkClient.extend_leaf_v2({
|
|
1557
1933
|
leafId: node.id,
|
|
1558
1934
|
ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
|
|
1559
1935
|
nodeTxSigningJob: getSigningJobProto(newNodeSigningJob),
|
|
1560
|
-
|
|
1936
|
+
directNodeTxSigningJob: newDirectNodeSigningJob
|
|
1937
|
+
? getSigningJobProto(newDirectNodeSigningJob)
|
|
1938
|
+
: undefined,
|
|
1939
|
+
refundTxSigningJob: getSigningJobProto(newCpfpRefundSigningJob),
|
|
1940
|
+
directRefundTxSigningJob: newDirectRefundSigningJob
|
|
1941
|
+
? getSigningJobProto(newDirectRefundSigningJob)
|
|
1942
|
+
: undefined,
|
|
1943
|
+
directFromCpfpRefundTxSigningJob: newDirectFromCpfpRefundSigningJob
|
|
1944
|
+
? getSigningJobProto(newDirectFromCpfpRefundSigningJob)
|
|
1945
|
+
: undefined,
|
|
1561
1946
|
});
|
|
1562
1947
|
|
|
1563
1948
|
if (!response.nodeTxSigningResult || !response.refundTxSigningResult) {
|
|
1564
1949
|
throw new Error("Signing result does not exist");
|
|
1565
1950
|
}
|
|
1566
1951
|
|
|
1952
|
+
const keyDerivation: KeyDerivation = {
|
|
1953
|
+
type: KeyDerivationType.LEAF,
|
|
1954
|
+
path: node.id,
|
|
1955
|
+
};
|
|
1956
|
+
|
|
1957
|
+
// Sign CPFP node transaction
|
|
1567
1958
|
const nodeUserSig = await this.config.signer.signFrost({
|
|
1568
1959
|
message: nodeSighash,
|
|
1569
|
-
keyDerivation
|
|
1570
|
-
|
|
1571
|
-
path: node.id,
|
|
1572
|
-
},
|
|
1573
|
-
publicKey: signingPubKey,
|
|
1960
|
+
keyDerivation,
|
|
1961
|
+
publicKey: signingPublicKey,
|
|
1574
1962
|
verifyingKey: response.nodeTxSigningResult.verifyingKey,
|
|
1575
1963
|
selfCommitment: newNodeSigningJob.signingNonceCommitment,
|
|
1576
1964
|
statechainCommitments:
|
|
@@ -1578,20 +1966,7 @@ export class TransferService extends BaseTransferService {
|
|
|
1578
1966
|
adaptorPubKey: new Uint8Array(),
|
|
1579
1967
|
});
|
|
1580
1968
|
|
|
1581
|
-
|
|
1582
|
-
message: refundSighash,
|
|
1583
|
-
keyDerivation: {
|
|
1584
|
-
type: KeyDerivationType.LEAF,
|
|
1585
|
-
path: node.id,
|
|
1586
|
-
},
|
|
1587
|
-
publicKey: signingPubKey,
|
|
1588
|
-
verifyingKey: response.refundTxSigningResult.verifyingKey,
|
|
1589
|
-
selfCommitment: newRefundSigningJob.signingNonceCommitment,
|
|
1590
|
-
statechainCommitments:
|
|
1591
|
-
response.refundTxSigningResult.signingResult?.signingNonceCommitments,
|
|
1592
|
-
adaptorPubKey: new Uint8Array(),
|
|
1593
|
-
});
|
|
1594
|
-
|
|
1969
|
+
// Aggregate CPFP node signature
|
|
1595
1970
|
const nodeSig = await this.config.signer.aggregateFrost({
|
|
1596
1971
|
message: nodeSighash,
|
|
1597
1972
|
statechainSignatures:
|
|
@@ -1602,13 +1977,63 @@ export class TransferService extends BaseTransferService {
|
|
|
1602
1977
|
statechainCommitments:
|
|
1603
1978
|
response.nodeTxSigningResult.signingResult?.signingNonceCommitments,
|
|
1604
1979
|
selfCommitment: newNodeSigningJob.signingNonceCommitment,
|
|
1605
|
-
publicKey:
|
|
1980
|
+
publicKey: signingPublicKey,
|
|
1606
1981
|
selfSignature: nodeUserSig,
|
|
1607
1982
|
adaptorPubKey: new Uint8Array(),
|
|
1608
1983
|
});
|
|
1609
1984
|
|
|
1610
|
-
|
|
1611
|
-
|
|
1985
|
+
let directNodeSig: Uint8Array | undefined;
|
|
1986
|
+
if (
|
|
1987
|
+
directNodeSighash &&
|
|
1988
|
+
newDirectNodeSigningJob &&
|
|
1989
|
+
response.directNodeTxSigningResult
|
|
1990
|
+
) {
|
|
1991
|
+
// Sign direct node transaction
|
|
1992
|
+
const directNodeUserSig = await this.config.signer.signFrost({
|
|
1993
|
+
message: directNodeSighash,
|
|
1994
|
+
keyDerivation,
|
|
1995
|
+
publicKey: signingPublicKey,
|
|
1996
|
+
verifyingKey: response.directNodeTxSigningResult.verifyingKey,
|
|
1997
|
+
selfCommitment: newDirectNodeSigningJob.signingNonceCommitment,
|
|
1998
|
+
statechainCommitments:
|
|
1999
|
+
response.directNodeTxSigningResult.signingResult
|
|
2000
|
+
?.signingNonceCommitments,
|
|
2001
|
+
adaptorPubKey: new Uint8Array(),
|
|
2002
|
+
});
|
|
2003
|
+
|
|
2004
|
+
// Aggregate direct node signature
|
|
2005
|
+
directNodeSig = await this.config.signer.aggregateFrost({
|
|
2006
|
+
message: directNodeSighash,
|
|
2007
|
+
statechainSignatures:
|
|
2008
|
+
response.directNodeTxSigningResult.signingResult?.signatureShares,
|
|
2009
|
+
statechainPublicKeys:
|
|
2010
|
+
response.directNodeTxSigningResult.signingResult?.publicKeys,
|
|
2011
|
+
verifyingKey: response.directNodeTxSigningResult.verifyingKey,
|
|
2012
|
+
statechainCommitments:
|
|
2013
|
+
response.directNodeTxSigningResult.signingResult
|
|
2014
|
+
?.signingNonceCommitments,
|
|
2015
|
+
selfCommitment: newDirectNodeSigningJob.signingNonceCommitment,
|
|
2016
|
+
publicKey: signingPublicKey,
|
|
2017
|
+
selfSignature: directNodeUserSig,
|
|
2018
|
+
adaptorPubKey: new Uint8Array(),
|
|
2019
|
+
});
|
|
2020
|
+
}
|
|
2021
|
+
|
|
2022
|
+
// Sign CPFP refund transaction
|
|
2023
|
+
const cpfpRefundUserSig = await this.config.signer.signFrost({
|
|
2024
|
+
message: cpfpRefundSighash,
|
|
2025
|
+
keyDerivation,
|
|
2026
|
+
publicKey: signingPublicKey,
|
|
2027
|
+
verifyingKey: response.refundTxSigningResult.verifyingKey,
|
|
2028
|
+
selfCommitment: newCpfpRefundSigningJob.signingNonceCommitment,
|
|
2029
|
+
statechainCommitments:
|
|
2030
|
+
response.refundTxSigningResult.signingResult?.signingNonceCommitments,
|
|
2031
|
+
adaptorPubKey: new Uint8Array(),
|
|
2032
|
+
});
|
|
2033
|
+
|
|
2034
|
+
// Aggregate CPFP refund signature
|
|
2035
|
+
const cpfpRefundSig = await this.config.signer.aggregateFrost({
|
|
2036
|
+
message: cpfpRefundSighash,
|
|
1612
2037
|
statechainSignatures:
|
|
1613
2038
|
response.refundTxSigningResult.signingResult?.signatureShares,
|
|
1614
2039
|
statechainPublicKeys:
|
|
@@ -1616,137 +2041,278 @@ export class TransferService extends BaseTransferService {
|
|
|
1616
2041
|
verifyingKey: response.refundTxSigningResult.verifyingKey,
|
|
1617
2042
|
statechainCommitments:
|
|
1618
2043
|
response.refundTxSigningResult.signingResult?.signingNonceCommitments,
|
|
1619
|
-
selfCommitment:
|
|
1620
|
-
publicKey:
|
|
1621
|
-
selfSignature:
|
|
2044
|
+
selfCommitment: newCpfpRefundSigningJob.signingNonceCommitment,
|
|
2045
|
+
publicKey: signingPublicKey,
|
|
2046
|
+
selfSignature: cpfpRefundUserSig,
|
|
1622
2047
|
adaptorPubKey: new Uint8Array(),
|
|
1623
2048
|
});
|
|
1624
2049
|
|
|
1625
|
-
|
|
2050
|
+
let directRefundSig: Uint8Array | undefined;
|
|
2051
|
+
if (
|
|
2052
|
+
directRefundSighash &&
|
|
2053
|
+
newDirectRefundSigningJob &&
|
|
2054
|
+
response.directRefundTxSigningResult
|
|
2055
|
+
) {
|
|
2056
|
+
// Sign direct refund transaction
|
|
2057
|
+
const directRefundUserSig = await this.config.signer.signFrost({
|
|
2058
|
+
message: directRefundSighash,
|
|
2059
|
+
keyDerivation,
|
|
2060
|
+
publicKey: signingPublicKey,
|
|
2061
|
+
verifyingKey: response.directRefundTxSigningResult.verifyingKey,
|
|
2062
|
+
selfCommitment: newDirectRefundSigningJob.signingNonceCommitment,
|
|
2063
|
+
statechainCommitments:
|
|
2064
|
+
response.directRefundTxSigningResult.signingResult
|
|
2065
|
+
?.signingNonceCommitments,
|
|
2066
|
+
adaptorPubKey: new Uint8Array(),
|
|
2067
|
+
});
|
|
2068
|
+
|
|
2069
|
+
// Aggregate direct refund signature
|
|
2070
|
+
directRefundSig = await this.config.signer.aggregateFrost({
|
|
2071
|
+
message: directRefundSighash,
|
|
2072
|
+
statechainSignatures:
|
|
2073
|
+
response.directRefundTxSigningResult.signingResult?.signatureShares,
|
|
2074
|
+
statechainPublicKeys:
|
|
2075
|
+
response.directRefundTxSigningResult.signingResult?.publicKeys,
|
|
2076
|
+
verifyingKey: response.directRefundTxSigningResult.verifyingKey,
|
|
2077
|
+
statechainCommitments:
|
|
2078
|
+
response.directRefundTxSigningResult.signingResult
|
|
2079
|
+
?.signingNonceCommitments,
|
|
2080
|
+
selfCommitment: newDirectRefundSigningJob.signingNonceCommitment,
|
|
2081
|
+
publicKey: signingPublicKey,
|
|
2082
|
+
selfSignature: directRefundUserSig,
|
|
2083
|
+
adaptorPubKey: new Uint8Array(),
|
|
2084
|
+
});
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
let directFromCpfpRefundSig: Uint8Array | undefined;
|
|
2088
|
+
if (
|
|
2089
|
+
directFromCpfpRefundSighash &&
|
|
2090
|
+
newDirectFromCpfpRefundSigningJob &&
|
|
2091
|
+
response.directFromCpfpRefundTxSigningResult
|
|
2092
|
+
) {
|
|
2093
|
+
// Sign direct from CPFP refund transaction
|
|
2094
|
+
const directFromCpfpRefundUserSig = await this.config.signer.signFrost({
|
|
2095
|
+
message: directFromCpfpRefundSighash,
|
|
2096
|
+
keyDerivation,
|
|
2097
|
+
publicKey: signingPublicKey,
|
|
2098
|
+
verifyingKey: response.directFromCpfpRefundTxSigningResult.verifyingKey,
|
|
2099
|
+
selfCommitment:
|
|
2100
|
+
newDirectFromCpfpRefundSigningJob.signingNonceCommitment,
|
|
2101
|
+
statechainCommitments:
|
|
2102
|
+
response.directFromCpfpRefundTxSigningResult.signingResult
|
|
2103
|
+
?.signingNonceCommitments,
|
|
2104
|
+
adaptorPubKey: new Uint8Array(),
|
|
2105
|
+
});
|
|
2106
|
+
|
|
2107
|
+
// Aggregate direct from CPFP refund signature
|
|
2108
|
+
directFromCpfpRefundSig = await this.config.signer.aggregateFrost({
|
|
2109
|
+
message: directFromCpfpRefundSighash,
|
|
2110
|
+
statechainSignatures:
|
|
2111
|
+
response.directFromCpfpRefundTxSigningResult.signingResult
|
|
2112
|
+
?.signatureShares,
|
|
2113
|
+
statechainPublicKeys:
|
|
2114
|
+
response.directFromCpfpRefundTxSigningResult.signingResult
|
|
2115
|
+
?.publicKeys,
|
|
2116
|
+
verifyingKey: response.directFromCpfpRefundTxSigningResult.verifyingKey,
|
|
2117
|
+
statechainCommitments:
|
|
2118
|
+
response.directFromCpfpRefundTxSigningResult.signingResult
|
|
2119
|
+
?.signingNonceCommitments,
|
|
2120
|
+
selfCommitment:
|
|
2121
|
+
newDirectFromCpfpRefundSigningJob.signingNonceCommitment,
|
|
2122
|
+
publicKey: signingPublicKey,
|
|
2123
|
+
selfSignature: directFromCpfpRefundUserSig,
|
|
2124
|
+
adaptorPubKey: new Uint8Array(),
|
|
2125
|
+
});
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2128
|
+
return await sparkClient.finalize_node_signatures_v2({
|
|
1626
2129
|
intent: SignatureIntent.EXTEND,
|
|
1627
2130
|
nodeSignatures: [
|
|
1628
2131
|
{
|
|
1629
2132
|
nodeId: response.leafId,
|
|
1630
2133
|
nodeTxSignature: nodeSig,
|
|
1631
|
-
|
|
2134
|
+
directNodeTxSignature: directNodeSig,
|
|
2135
|
+
refundTxSignature: cpfpRefundSig,
|
|
2136
|
+
directRefundTxSignature: directRefundSig,
|
|
2137
|
+
directFromCpfpRefundTxSignature: directFromCpfpRefundSig,
|
|
1632
2138
|
},
|
|
1633
2139
|
],
|
|
1634
2140
|
});
|
|
1635
2141
|
}
|
|
1636
2142
|
|
|
1637
|
-
async
|
|
2143
|
+
async testonly_expireTimeLockNodeTx(node: TreeNode, parentNode: TreeNode) {
|
|
2144
|
+
return await this.refreshTimelockNodesInternal(node, parentNode, true);
|
|
2145
|
+
}
|
|
2146
|
+
|
|
2147
|
+
async testonly_expireTimeLockRefundtx(node: TreeNode) {
|
|
1638
2148
|
const nodeTx = getTxFromRawTxBytes(node.nodeTx);
|
|
1639
|
-
const
|
|
2149
|
+
const directNodeTx =
|
|
2150
|
+
node.directTx.length > 0 ? getTxFromRawTxBytes(node.directTx) : undefined;
|
|
2151
|
+
|
|
2152
|
+
const cpfpRefundTx = getTxFromRawTxBytes(node.refundTx);
|
|
2153
|
+
|
|
2154
|
+
const currSequence = cpfpRefundTx.getInput(0).sequence || 0;
|
|
2155
|
+
const currTimelock = getCurrentTimelock(currSequence);
|
|
2156
|
+
if (currTimelock <= 100) {
|
|
2157
|
+
throw new ValidationError("Cannot expire timelock below 100", {
|
|
2158
|
+
field: "currTimelock",
|
|
2159
|
+
value: currTimelock,
|
|
2160
|
+
expected: "Timelock greater than 100",
|
|
2161
|
+
});
|
|
2162
|
+
}
|
|
2163
|
+
const nextSequence = TEST_UNILATERAL_SEQUENCE;
|
|
2164
|
+
const nextDirectSequence =
|
|
2165
|
+
TEST_UNILATERAL_SEQUENCE + DIRECT_TIMELOCK_OFFSET;
|
|
1640
2166
|
|
|
1641
|
-
const
|
|
1642
|
-
|
|
2167
|
+
const nodeOutput = nodeTx.getOutput(0);
|
|
2168
|
+
if (!nodeOutput) {
|
|
2169
|
+
throw Error("Could not get node output");
|
|
2170
|
+
}
|
|
1643
2171
|
|
|
1644
|
-
const
|
|
2172
|
+
const keyDerivation: KeyDerivation = {
|
|
1645
2173
|
type: KeyDerivationType.LEAF,
|
|
1646
2174
|
path: node.id,
|
|
1647
|
-
}
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
version: 3,
|
|
1651
|
-
allowUnknownOutputs: true,
|
|
1652
|
-
});
|
|
1653
|
-
|
|
1654
|
-
// Apply fee to the refund output
|
|
1655
|
-
const originalRefundOutput = refundTx.getOutput(0);
|
|
1656
|
-
if (!originalRefundOutput) {
|
|
1657
|
-
throw Error("Could not get original refund output");
|
|
1658
|
-
}
|
|
1659
|
-
|
|
1660
|
-
newRefundTx.addOutput({
|
|
1661
|
-
script: originalRefundOutput.script!,
|
|
1662
|
-
amount: originalRefundOutput.amount!,
|
|
1663
|
-
});
|
|
2175
|
+
};
|
|
2176
|
+
const signingPublicKey =
|
|
2177
|
+
await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
|
|
1664
2178
|
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
newRefundTx.addOutput(additionalOutput);
|
|
1670
|
-
}
|
|
1671
|
-
}
|
|
2179
|
+
const cpfpRefundOutPoint: TransactionInput = {
|
|
2180
|
+
txid: hexToBytes(getTxId(nodeTx)!),
|
|
2181
|
+
index: 0,
|
|
2182
|
+
};
|
|
1672
2183
|
|
|
1673
|
-
|
|
1674
|
-
if (
|
|
1675
|
-
|
|
2184
|
+
let directRefundOutPoint: TransactionInput | undefined;
|
|
2185
|
+
if (directNodeTx) {
|
|
2186
|
+
directRefundOutPoint = {
|
|
2187
|
+
txid: hexToBytes(getTxId(directNodeTx)!),
|
|
2188
|
+
index: 0,
|
|
2189
|
+
};
|
|
1676
2190
|
}
|
|
1677
2191
|
|
|
1678
|
-
|
|
1679
|
-
|
|
2192
|
+
const {
|
|
2193
|
+
cpfpRefundTx: newCpfpRefundTx,
|
|
2194
|
+
directRefundTx: newDirectRefundTx,
|
|
2195
|
+
directFromCpfpRefundTx: newDirectFromCpfpRefundTx,
|
|
2196
|
+
} = createRefundTxs({
|
|
1680
2197
|
sequence: nextSequence,
|
|
2198
|
+
directSequence: nextDirectSequence,
|
|
2199
|
+
input: cpfpRefundOutPoint,
|
|
2200
|
+
directInput: directRefundOutPoint,
|
|
2201
|
+
amountSats: nodeOutput.amount!,
|
|
2202
|
+
receivingPubkey: signingPublicKey,
|
|
2203
|
+
network: this.config.getNetwork(),
|
|
1681
2204
|
});
|
|
1682
2205
|
|
|
1683
|
-
const
|
|
1684
|
-
|
|
1685
|
-
|
|
2206
|
+
const signingJobs: (SigningJobWithOptionalNonce & {
|
|
2207
|
+
type: "cpfp" | "direct" | "directFromCpfp";
|
|
2208
|
+
parentTxOut: TransactionOutput;
|
|
2209
|
+
})[] = [];
|
|
2210
|
+
|
|
2211
|
+
signingJobs.push({
|
|
2212
|
+
signingPublicKey,
|
|
2213
|
+
rawTx: newCpfpRefundTx.toBytes(),
|
|
1686
2214
|
signingNonceCommitment:
|
|
1687
2215
|
await this.config.signer.getRandomSigningCommitment(),
|
|
1688
|
-
|
|
2216
|
+
type: "cpfp",
|
|
2217
|
+
parentTxOut: nodeOutput,
|
|
2218
|
+
});
|
|
2219
|
+
|
|
2220
|
+
const directNodeTxOut = directNodeTx?.getOutput(0);
|
|
2221
|
+
if (newDirectRefundTx && directNodeTxOut) {
|
|
2222
|
+
signingJobs.push({
|
|
2223
|
+
signingPublicKey,
|
|
2224
|
+
rawTx: newDirectRefundTx.toBytes(),
|
|
2225
|
+
signingNonceCommitment:
|
|
2226
|
+
await this.config.signer.getRandomSigningCommitment(),
|
|
2227
|
+
type: "direct",
|
|
2228
|
+
parentTxOut: directNodeTxOut,
|
|
2229
|
+
});
|
|
2230
|
+
}
|
|
2231
|
+
|
|
2232
|
+
if (newDirectFromCpfpRefundTx) {
|
|
2233
|
+
signingJobs.push({
|
|
2234
|
+
signingPublicKey,
|
|
2235
|
+
rawTx: newDirectFromCpfpRefundTx.toBytes(),
|
|
2236
|
+
signingNonceCommitment:
|
|
2237
|
+
await this.config.signer.getRandomSigningCommitment(),
|
|
2238
|
+
type: "directFromCpfp",
|
|
2239
|
+
parentTxOut: nodeOutput,
|
|
2240
|
+
});
|
|
2241
|
+
}
|
|
1689
2242
|
|
|
1690
2243
|
const sparkClient = await this.connectionManager.createSparkClient(
|
|
1691
2244
|
this.config.getCoordinatorAddress(),
|
|
1692
2245
|
);
|
|
1693
2246
|
|
|
1694
|
-
const response = await sparkClient.
|
|
2247
|
+
const response = await sparkClient.refresh_timelock_v2({
|
|
1695
2248
|
leafId: node.id,
|
|
1696
2249
|
ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
|
|
1697
|
-
signingJobs:
|
|
2250
|
+
signingJobs: signingJobs.map(getSigningJobProto),
|
|
1698
2251
|
});
|
|
1699
2252
|
|
|
1700
|
-
if (response.signingResults.length !==
|
|
2253
|
+
if (response.signingResults.length !== signingJobs.length) {
|
|
1701
2254
|
throw Error(
|
|
1702
|
-
`Expected
|
|
2255
|
+
`Expected ${signingJobs.length} signing results, got ${response.signingResults.length}`,
|
|
1703
2256
|
);
|
|
1704
2257
|
}
|
|
1705
2258
|
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
}
|
|
2259
|
+
let cpfpRefundSignature: Uint8Array | undefined;
|
|
2260
|
+
let directRefundSignature: Uint8Array | undefined;
|
|
2261
|
+
let directFromCpfpRefundSignature: Uint8Array | undefined;
|
|
1710
2262
|
|
|
1711
|
-
const
|
|
1712
|
-
|
|
2263
|
+
for (const [i, signingJob] of signingJobs.entries()) {
|
|
2264
|
+
const signingResult = response.signingResults[i];
|
|
2265
|
+
if (!signingResult) {
|
|
2266
|
+
throw Error("Signing result does not exist");
|
|
2267
|
+
}
|
|
1713
2268
|
|
|
1714
|
-
|
|
2269
|
+
const rawTx = getTxFromRawTxBytes(signingJob.rawTx);
|
|
2270
|
+
const txOut = signingJob.parentTxOut;
|
|
2271
|
+
const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
|
|
1715
2272
|
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
signingResult.signingResult?.signingNonceCommitments,
|
|
1727
|
-
adaptorPubKey: new Uint8Array(),
|
|
1728
|
-
});
|
|
2273
|
+
const userSignature = await this.config.signer.signFrost({
|
|
2274
|
+
message: rawTxSighash,
|
|
2275
|
+
keyDerivation,
|
|
2276
|
+
publicKey: signingPublicKey,
|
|
2277
|
+
verifyingKey: signingResult.verifyingKey,
|
|
2278
|
+
selfCommitment: signingJob.signingNonceCommitment,
|
|
2279
|
+
statechainCommitments:
|
|
2280
|
+
signingResult.signingResult?.signingNonceCommitments,
|
|
2281
|
+
adaptorPubKey: new Uint8Array(),
|
|
2282
|
+
});
|
|
1729
2283
|
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
2284
|
+
const signature = await this.config.signer.aggregateFrost({
|
|
2285
|
+
message: rawTxSighash,
|
|
2286
|
+
statechainSignatures: signingResult.signingResult?.signatureShares,
|
|
2287
|
+
statechainPublicKeys: signingResult.signingResult?.publicKeys,
|
|
2288
|
+
verifyingKey: signingResult.verifyingKey,
|
|
2289
|
+
statechainCommitments:
|
|
2290
|
+
signingResult.signingResult?.signingNonceCommitments,
|
|
2291
|
+
selfCommitment: signingJob.signingNonceCommitment,
|
|
2292
|
+
publicKey: signingPublicKey,
|
|
2293
|
+
selfSignature: userSignature,
|
|
2294
|
+
adaptorPubKey: new Uint8Array(),
|
|
2295
|
+
});
|
|
1742
2296
|
|
|
1743
|
-
|
|
2297
|
+
if (signingJob.type === "cpfp") {
|
|
2298
|
+
cpfpRefundSignature = signature;
|
|
2299
|
+
} else if (signingJob.type === "direct") {
|
|
2300
|
+
directRefundSignature = signature;
|
|
2301
|
+
} else if (signingJob.type === "directFromCpfp") {
|
|
2302
|
+
directFromCpfpRefundSignature = signature;
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
|
|
2306
|
+
const result = await sparkClient.finalize_node_signatures_v2({
|
|
1744
2307
|
intent: SignatureIntent.REFRESH,
|
|
1745
2308
|
nodeSignatures: [
|
|
1746
2309
|
{
|
|
1747
2310
|
nodeId: node.id,
|
|
1748
2311
|
nodeTxSignature: new Uint8Array(),
|
|
1749
|
-
|
|
2312
|
+
directNodeTxSignature: new Uint8Array(),
|
|
2313
|
+
refundTxSignature: cpfpRefundSignature,
|
|
2314
|
+
directRefundTxSignature: directRefundSignature,
|
|
2315
|
+
directFromCpfpRefundTxSignature: directFromCpfpRefundSignature,
|
|
1750
2316
|
},
|
|
1751
2317
|
],
|
|
1752
2318
|
});
|