@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.
Files changed (73) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/{chunk-PTRXJS7Q.js → chunk-TVUMSHWA.js} +1 -1
  3. package/dist/{chunk-PLLJIZC3.js → chunk-W4ZRBSWM.js} +2298 -778
  4. package/dist/{chunk-CDLETEDT.js → chunk-WAQKYSDI.js} +13 -1
  5. package/dist/{client-CGTRS23n.d.ts → client-BF4cn8F4.d.ts} +15 -3
  6. package/dist/{client-CcYzmpmj.d.cts → client-KhNkrXz4.d.cts} +15 -3
  7. package/dist/debug.cjs +2282 -762
  8. package/dist/debug.d.cts +17 -4
  9. package/dist/debug.d.ts +17 -4
  10. package/dist/debug.js +2 -2
  11. package/dist/graphql/objects/index.cjs +13 -1
  12. package/dist/graphql/objects/index.d.cts +2 -2
  13. package/dist/graphql/objects/index.d.ts +2 -2
  14. package/dist/graphql/objects/index.js +1 -1
  15. package/dist/index.cjs +2283 -752
  16. package/dist/index.d.cts +189 -8
  17. package/dist/index.d.ts +189 -8
  18. package/dist/index.js +29 -3
  19. package/dist/index.node.cjs +2387 -753
  20. package/dist/index.node.d.cts +9 -189
  21. package/dist/index.node.d.ts +9 -189
  22. package/dist/index.node.js +131 -3
  23. package/dist/native/index.cjs +2283 -752
  24. package/dist/native/index.d.cts +95 -30
  25. package/dist/native/index.d.ts +95 -30
  26. package/dist/native/index.js +2284 -767
  27. package/dist/{spark-wallet-CxcGPXRB.d.ts → spark-wallet-C1Tr_VKI.d.ts} +31 -25
  28. package/dist/{spark-wallet-DJJm19BP.d.cts → spark-wallet-DG3x2obf.d.cts} +31 -25
  29. package/dist/spark-wallet.node-CGxoeCpH.d.ts +13 -0
  30. package/dist/spark-wallet.node-CN9LoB_O.d.cts +13 -0
  31. package/dist/tests/test-utils.cjs +570 -73
  32. package/dist/tests/test-utils.d.cts +11 -11
  33. package/dist/tests/test-utils.d.ts +11 -11
  34. package/dist/tests/test-utils.js +53 -16
  35. package/dist/types/index.cjs +13 -1
  36. package/dist/types/index.d.cts +1 -1
  37. package/dist/types/index.d.ts +1 -1
  38. package/dist/types/index.js +1 -1
  39. package/dist/{xchain-address-Bh9w1SeC.d.ts → xchain-address-BHu6CpZC.d.ts} +54 -7
  40. package/dist/{xchain-address-SZ7dkVUE.d.cts → xchain-address-HBr6isnc.d.cts} +54 -7
  41. package/package.json +1 -1
  42. package/src/graphql/client.ts +8 -0
  43. package/src/graphql/mutations/CompleteLeavesSwap.ts +9 -1
  44. package/src/graphql/mutations/RequestSwapLeaves.ts +4 -0
  45. package/src/graphql/objects/CompleteLeavesSwapInput.ts +34 -34
  46. package/src/graphql/objects/LeavesSwapRequest.ts +4 -0
  47. package/src/graphql/objects/RequestLeavesSwapInput.ts +48 -47
  48. package/src/graphql/objects/SwapLeaf.ts +40 -32
  49. package/src/graphql/objects/UserLeafInput.ts +24 -0
  50. package/src/graphql/objects/UserRequest.ts +4 -0
  51. package/src/index.node.ts +1 -1
  52. package/src/native/index.ts +4 -5
  53. package/src/services/coop-exit.ts +171 -36
  54. package/src/services/deposit.ts +471 -74
  55. package/src/services/lightning.ts +18 -5
  56. package/src/services/signing.ts +162 -50
  57. package/src/services/transfer.ts +950 -384
  58. package/src/services/tree-creation.ts +342 -121
  59. package/src/spark-wallet/spark-wallet.node.ts +71 -66
  60. package/src/spark-wallet/spark-wallet.ts +405 -153
  61. package/src/tests/integration/coop-exit.test.ts +3 -8
  62. package/src/tests/integration/deposit.test.ts +3 -3
  63. package/src/tests/integration/lightning.test.ts +521 -466
  64. package/src/tests/integration/swap.test.ts +559 -307
  65. package/src/tests/integration/transfer.test.ts +625 -623
  66. package/src/tests/integration/wallet.test.ts +2 -2
  67. package/src/tests/integration/watchtower.test.ts +211 -0
  68. package/src/tests/test-utils.ts +63 -14
  69. package/src/tests/utils/test-faucet.ts +4 -2
  70. package/src/utils/adaptor-signature.ts +15 -5
  71. package/src/utils/fetch.ts +75 -0
  72. package/src/utils/mempool.ts +9 -4
  73. package/src/utils/transaction.ts +388 -26
package/dist/index.cjs CHANGED
@@ -1290,7 +1290,10 @@ __export(index_exports, {
1290
1290
  AuthenticationError: () => AuthenticationError,
1291
1291
  ConfigurationError: () => ConfigurationError,
1292
1292
  DEFAULT_FEE_SATS: () => DEFAULT_FEE_SATS,
1293
+ DIRECT_TIMELOCK_OFFSET: () => DIRECT_TIMELOCK_OFFSET,
1293
1294
  DefaultSparkSigner: () => DefaultSparkSigner,
1295
+ INITIAL_DIRECT_SEQUENCE: () => INITIAL_DIRECT_SEQUENCE,
1296
+ INITIAL_SEQUENCE: () => INITIAL_SEQUENCE,
1294
1297
  InternalValidationError: () => InternalValidationError,
1295
1298
  KeyDerivationType: () => KeyDerivationType,
1296
1299
  LRC_WALLET_NETWORK: () => LRC_WALLET_NETWORK,
@@ -1303,6 +1306,8 @@ __export(index_exports, {
1303
1306
  RPCError: () => RPCError,
1304
1307
  SparkSDKError: () => SparkSDKError,
1305
1308
  SparkWallet: () => SparkWallet,
1309
+ TEST_UNILATERAL_DIRECT_SEQUENCE: () => TEST_UNILATERAL_DIRECT_SEQUENCE,
1310
+ TEST_UNILATERAL_SEQUENCE: () => TEST_UNILATERAL_SEQUENCE,
1306
1311
  TaprootOutputKeysGenerator: () => TaprootOutputKeysGenerator,
1307
1312
  TaprootSparkSigner: () => TaprootSparkSigner,
1308
1313
  TokenTransactionService: () => TokenTransactionService,
@@ -1322,13 +1327,21 @@ __export(index_exports, {
1322
1327
  constructFeeBumpTx: () => constructFeeBumpTx,
1323
1328
  constructUnilateralExitFeeBumpPackages: () => constructUnilateralExitFeeBumpPackages,
1324
1329
  constructUnilateralExitTxs: () => constructUnilateralExitTxs,
1330
+ createConnectorRefundTransactions: () => createConnectorRefundTransactions,
1331
+ createLeafNodeTx: () => createLeafNodeTx,
1332
+ createNodeTx: () => createNodeTx,
1333
+ createNodeTxs: () => createNodeTxs,
1325
1334
  createRefundTx: () => createRefundTx,
1335
+ createRefundTxs: () => createRefundTxs,
1336
+ createRootTx: () => createRootTx,
1326
1337
  createSigningCommitment: () => createSigningCommitment,
1327
1338
  createSigningNonce: () => createSigningNonce,
1339
+ createSplitTx: () => createSplitTx,
1328
1340
  decodeBech32mTokenIdentifier: () => decodeBech32mTokenIdentifier,
1329
1341
  decodeBytesToSigningCommitment: () => decodeBytesToSigningCommitment,
1330
1342
  decodeBytesToSigningNonce: () => decodeBytesToSigningNonce,
1331
1343
  decodeSparkAddress: () => decodeSparkAddress,
1344
+ doesLeafNeedRefresh: () => doesLeafNeedRefresh,
1332
1345
  encodeBech32mTokenIdentifier: () => encodeBech32mTokenIdentifier,
1333
1346
  encodeSigningCommitmentToBytes: () => encodeSigningCommitmentToBytes,
1334
1347
  encodeSigningNonceToBytes: () => encodeSigningNonceToBytes,
@@ -16203,13 +16216,24 @@ function applyAdaptorToSignature(pubkey, hash, signature, adaptorPrivateKeyBytes
16203
16216
  const adaptorPrivateKey = (0, import_utils4.bytesToNumberBE)(adaptorPrivateKeyBytes);
16204
16217
  const newS = (0, import_modular.mod)(sBigInt + adaptorPrivateKey, import_secp256k12.secp256k1.CURVE.n);
16205
16218
  const newSig = new Uint8Array([...r, ...(0, import_utils4.numberToBytesBE)(newS, 32)]);
16206
- if (import_secp256k12.schnorr.verify(newSig, hash, pubkey)) {
16207
- return newSig;
16219
+ try {
16220
+ if (import_secp256k12.schnorr.verify(newSig, hash, pubkey)) {
16221
+ return newSig;
16222
+ }
16223
+ } catch (e) {
16224
+ console.error("[applyAdaptorToSignature] Addition verification failed:", e);
16208
16225
  }
16209
16226
  const altS = (0, import_modular.mod)(sBigInt - adaptorPrivateKey, import_secp256k12.secp256k1.CURVE.n);
16210
16227
  const altSig = new Uint8Array([...r, ...(0, import_utils4.numberToBytesBE)(altS, 32)]);
16211
- if (import_secp256k12.schnorr.verify(altSig, hash, pubkey)) {
16212
- return altSig;
16228
+ try {
16229
+ if (import_secp256k12.schnorr.verify(altSig, hash, pubkey)) {
16230
+ return altSig;
16231
+ }
16232
+ } catch (e) {
16233
+ console.error(
16234
+ "[applyAdaptorToSignature] Subtraction verification failed:",
16235
+ e
16236
+ );
16213
16237
  }
16214
16238
  throw new Error("Cannot apply adaptor to signature");
16215
16239
  }
@@ -17103,7 +17127,11 @@ var SwapLeafFromJson = (obj) => {
17103
17127
  return {
17104
17128
  leafId: obj["swap_leaf_leaf_id"],
17105
17129
  rawUnsignedRefundTransaction: obj["swap_leaf_raw_unsigned_refund_transaction"],
17106
- adaptorSignedSignature: obj["swap_leaf_adaptor_signed_signature"]
17130
+ adaptorSignedSignature: obj["swap_leaf_adaptor_signed_signature"],
17131
+ directRawUnsignedRefundTransaction: obj["swap_leaf_direct_raw_unsigned_refund_transaction"],
17132
+ directAdaptorSignedSignature: obj["swap_leaf_direct_adaptor_signed_signature"],
17133
+ directFromCpfpRawUnsignedRefundTransaction: obj["swap_leaf_direct_from_cpfp_raw_unsigned_refund_transaction"],
17134
+ directFromCpfpAdaptorSignedSignature: obj["swap_leaf_direct_from_cpfp_adaptor_signed_signature"]
17107
17135
  };
17108
17136
  };
17109
17137
 
@@ -17358,6 +17386,10 @@ fragment LeavesSwapRequestFragment on LeavesSwapRequest {
17358
17386
  swap_leaf_leaf_id: leaf_id
17359
17387
  swap_leaf_raw_unsigned_refund_transaction: raw_unsigned_refund_transaction
17360
17388
  swap_leaf_adaptor_signed_signature: adaptor_signed_signature
17389
+ swap_leaf_direct_raw_unsigned_refund_transaction: direct_raw_unsigned_refund_transaction
17390
+ swap_leaf_direct_adaptor_signed_signature: direct_adaptor_signed_signature
17391
+ swap_leaf_direct_from_cpfp_raw_unsigned_refund_transaction: direct_from_cpfp_raw_unsigned_refund_transaction
17392
+ swap_leaf_direct_from_cpfp_adaptor_signed_signature: direct_from_cpfp_adaptor_signed_signature
17361
17393
  }
17362
17394
  }`;
17363
17395
 
@@ -17704,6 +17736,10 @@ fragment UserRequestFragment on UserRequest {
17704
17736
  swap_leaf_leaf_id: leaf_id
17705
17737
  swap_leaf_raw_unsigned_refund_transaction: raw_unsigned_refund_transaction
17706
17738
  swap_leaf_adaptor_signed_signature: adaptor_signed_signature
17739
+ swap_leaf_direct_raw_unsigned_refund_transaction: direct_raw_unsigned_refund_transaction
17740
+ swap_leaf_direct_adaptor_signed_signature: direct_adaptor_signed_signature
17741
+ swap_leaf_direct_from_cpfp_raw_unsigned_refund_transaction: direct_from_cpfp_raw_unsigned_refund_transaction
17742
+ swap_leaf_direct_from_cpfp_adaptor_signed_signature: direct_from_cpfp_adaptor_signed_signature
17707
17743
  }
17708
17744
  }
17709
17745
  ... on LightningReceiveRequest {
@@ -17829,18 +17865,40 @@ function mapTransferToWalletTransfer(proto, identityPublicKey) {
17829
17865
  };
17830
17866
  }
17831
17867
 
17868
+ // src/utils/fetch.ts
17869
+ init_buffer();
17870
+ var fetchImpl = typeof window !== "undefined" && window.fetch ? window.fetch.bind(window) : globalThis.fetch ? globalThis.fetch : null;
17871
+ var Headers = globalThis.Headers ?? null;
17872
+ var getFetch = () => {
17873
+ if (!fetchImpl) {
17874
+ throw new Error(
17875
+ "Fetch implementation is not set. Please set it using setFetch()."
17876
+ );
17877
+ }
17878
+ if (!Headers) {
17879
+ throw new Error(
17880
+ "Headers implementation is not set. Please set it using setFetch()."
17881
+ );
17882
+ }
17883
+ return {
17884
+ fetch: fetchImpl,
17885
+ Headers
17886
+ };
17887
+ };
17888
+
17832
17889
  // src/utils/mempool.ts
17833
17890
  async function getLatestDepositTxId(address2) {
17891
+ const { fetch: fetch2, Headers: Headers2 } = getFetch();
17834
17892
  const network = getNetworkFromAddress(address2);
17835
17893
  const baseUrl = network === BitcoinNetwork_default.REGTEST ? getElectrsUrl("REGTEST") : getElectrsUrl("MAINNET");
17836
- const headers = {};
17894
+ const headers = new Headers2();
17837
17895
  if (network === BitcoinNetwork_default.REGTEST) {
17838
17896
  const auth = btoa(
17839
17897
  `${ELECTRS_CREDENTIALS.username}:${ELECTRS_CREDENTIALS.password}`
17840
17898
  );
17841
- headers["Authorization"] = `Basic ${auth}`;
17899
+ headers.set("Authorization", `Basic ${auth}`);
17842
17900
  }
17843
- const response = await fetch(`${baseUrl}/address/${address2}/txs`, {
17901
+ const response = await fetch2(`${baseUrl}/address/${address2}/txs`, {
17844
17902
  headers
17845
17903
  });
17846
17904
  const addressTxs = await response.json();
@@ -17857,14 +17915,15 @@ async function getLatestDepositTxId(address2) {
17857
17915
  return null;
17858
17916
  }
17859
17917
  async function isTxBroadcast(txid, baseUrl, network) {
17860
- const headers = {};
17918
+ const { fetch: fetch2, Headers: Headers2 } = getFetch();
17919
+ const headers = new Headers2();
17861
17920
  if (network === 3 /* REGTEST */) {
17862
17921
  const auth = btoa(
17863
17922
  `${ELECTRS_CREDENTIALS.username}:${ELECTRS_CREDENTIALS.password}`
17864
17923
  );
17865
- headers["Authorization"] = `Basic ${auth}`;
17924
+ headers.set("Authorization", `Basic ${auth}`);
17866
17925
  }
17867
- const response = await fetch(`${baseUrl}/tx/${txid}`, {
17926
+ const response = await fetch2(`${baseUrl}/tx/${txid}`, {
17868
17927
  headers
17869
17928
  });
17870
17929
  const tx = await response.json();
@@ -18335,7 +18394,14 @@ function getTransferPackageSigningPayload(transferID, transferPackage) {
18335
18394
  // src/utils/transaction.ts
18336
18395
  init_buffer();
18337
18396
  var import_btc_signer = require("@scure/btc-signer");
18397
+ var INITIAL_TIMELOCK = 2e3;
18398
+ var TEST_UNILATERAL_TIMELOCK = 100;
18338
18399
  var TIME_LOCK_INTERVAL = 100;
18400
+ var DIRECT_TIMELOCK_OFFSET = 50;
18401
+ var INITIAL_SEQUENCE = 1 << 30 | INITIAL_TIMELOCK;
18402
+ var INITIAL_DIRECT_SEQUENCE = 1 << 30 | INITIAL_TIMELOCK + DIRECT_TIMELOCK_OFFSET;
18403
+ var TEST_UNILATERAL_SEQUENCE = 1 << 30 | TEST_UNILATERAL_TIMELOCK;
18404
+ var TEST_UNILATERAL_DIRECT_SEQUENCE = 1 << 30 | TEST_UNILATERAL_TIMELOCK + DIRECT_TIMELOCK_OFFSET;
18339
18405
  var ESTIMATED_TX_SIZE = 191;
18340
18406
  var DEFAULT_SATS_PER_VBYTE = 5;
18341
18407
  var DEFAULT_FEE_SATS = ESTIMATED_TX_SIZE * DEFAULT_SATS_PER_VBYTE;
@@ -18345,29 +18411,270 @@ function maybeApplyFee(amount) {
18345
18411
  }
18346
18412
  return amount;
18347
18413
  }
18348
- function createRefundTx(sequence, nodeOutPoint, amountSats, receivingPubkey, network) {
18349
- const newRefundTx = new import_btc_signer.Transaction({
18414
+ function createRootTx(depositOutPoint, depositTxOut) {
18415
+ const cpfpRootTx = new import_btc_signer.Transaction({
18416
+ version: 3,
18417
+ allowUnknownOutputs: true
18418
+ });
18419
+ cpfpRootTx.addInput(depositOutPoint);
18420
+ cpfpRootTx.addOutput(depositTxOut);
18421
+ cpfpRootTx.addOutput(getEphemeralAnchorOutput());
18422
+ const directRootTx = new import_btc_signer.Transaction({
18423
+ version: 3,
18424
+ allowUnknownOutputs: true
18425
+ });
18426
+ directRootTx.addInput(depositOutPoint);
18427
+ directRootTx.addOutput({
18428
+ script: depositTxOut.script,
18429
+ amount: maybeApplyFee(depositTxOut.amount ?? 0n)
18430
+ });
18431
+ return [cpfpRootTx, directRootTx];
18432
+ }
18433
+ function createSplitTx(parentOutPoint, childTxOuts) {
18434
+ const cpfpSplitTx = new import_btc_signer.Transaction({
18435
+ version: 3,
18436
+ allowUnknownOutputs: true
18437
+ });
18438
+ cpfpSplitTx.addInput(parentOutPoint);
18439
+ for (const txOut of childTxOuts) {
18440
+ cpfpSplitTx.addOutput(txOut);
18441
+ }
18442
+ cpfpSplitTx.addOutput(getEphemeralAnchorOutput());
18443
+ const directSplitTx = new import_btc_signer.Transaction({
18444
+ version: 3,
18445
+ allowUnknownOutputs: true
18446
+ });
18447
+ directSplitTx.addInput(parentOutPoint);
18448
+ let totalOutputAmount = 0n;
18449
+ for (const txOut of childTxOuts) {
18450
+ totalOutputAmount += txOut.amount ?? 0n;
18451
+ }
18452
+ if (totalOutputAmount > BigInt(DEFAULT_FEE_SATS)) {
18453
+ const feeRatio = Number(DEFAULT_FEE_SATS) / Number(totalOutputAmount);
18454
+ for (const txOut of childTxOuts) {
18455
+ const adjustedAmount = BigInt(
18456
+ Math.floor(Number(txOut.amount ?? 0n) * (1 - feeRatio))
18457
+ );
18458
+ directSplitTx.addOutput({
18459
+ script: txOut.script,
18460
+ amount: adjustedAmount
18461
+ });
18462
+ }
18463
+ } else {
18464
+ for (const txOut of childTxOuts) {
18465
+ directSplitTx.addOutput(txOut);
18466
+ }
18467
+ }
18468
+ return [cpfpSplitTx, directSplitTx];
18469
+ }
18470
+ function createNodeTx({
18471
+ txOut,
18472
+ parentOutPoint,
18473
+ applyFee,
18474
+ includeAnchor
18475
+ }) {
18476
+ const nodeTx = new import_btc_signer.Transaction({
18477
+ version: 3,
18478
+ allowUnknownOutputs: true
18479
+ });
18480
+ nodeTx.addInput(parentOutPoint);
18481
+ if (applyFee) {
18482
+ nodeTx.addOutput({
18483
+ script: txOut.script,
18484
+ amount: maybeApplyFee(txOut.amount ?? 0n)
18485
+ });
18486
+ } else {
18487
+ nodeTx.addOutput(txOut);
18488
+ }
18489
+ if (includeAnchor) {
18490
+ nodeTx.addOutput(getEphemeralAnchorOutput());
18491
+ }
18492
+ return nodeTx;
18493
+ }
18494
+ function createNodeTxs(txOut, txIn, directTxIn) {
18495
+ const cpfpNodeTx = createNodeTx({
18496
+ txOut,
18497
+ parentOutPoint: txIn,
18498
+ includeAnchor: true
18499
+ });
18500
+ let directNodeTx;
18501
+ if (directTxIn) {
18502
+ directNodeTx = createNodeTx({
18503
+ txOut,
18504
+ parentOutPoint: directTxIn,
18505
+ includeAnchor: false,
18506
+ applyFee: true
18507
+ });
18508
+ }
18509
+ return { cpfpNodeTx, directNodeTx };
18510
+ }
18511
+ function createLeafNodeTx(sequence, directSequence, parentOutPoint, txOut, shouldCalculateFee) {
18512
+ const cpfpLeafTx = new import_btc_signer.Transaction({
18513
+ version: 3,
18514
+ allowUnknownOutputs: true
18515
+ });
18516
+ cpfpLeafTx.addInput({
18517
+ ...parentOutPoint,
18518
+ sequence
18519
+ });
18520
+ cpfpLeafTx.addOutput(txOut);
18521
+ cpfpLeafTx.addOutput(getEphemeralAnchorOutput());
18522
+ const directLeafTx = new import_btc_signer.Transaction({
18523
+ version: 3,
18524
+ allowUnknownOutputs: true
18525
+ });
18526
+ directLeafTx.addInput({
18527
+ ...parentOutPoint,
18528
+ sequence: directSequence
18529
+ });
18530
+ const amountSats = txOut.amount ?? 0n;
18531
+ let outputAmount = amountSats;
18532
+ if (shouldCalculateFee) {
18533
+ outputAmount = maybeApplyFee(amountSats);
18534
+ }
18535
+ directLeafTx.addOutput({
18536
+ script: txOut.script,
18537
+ amount: outputAmount
18538
+ });
18539
+ return [cpfpLeafTx, directLeafTx];
18540
+ }
18541
+ function createRefundTx({
18542
+ sequence,
18543
+ input,
18544
+ amountSats,
18545
+ receivingPubkey,
18546
+ network,
18547
+ shouldCalculateFee,
18548
+ includeAnchor
18549
+ }) {
18550
+ const refundTx = new import_btc_signer.Transaction({
18350
18551
  version: 3,
18351
18552
  allowUnknownOutputs: true
18352
18553
  });
18353
- newRefundTx.addInput({
18354
- ...nodeOutPoint,
18554
+ refundTx.addInput({
18555
+ ...input,
18355
18556
  sequence
18356
18557
  });
18357
18558
  const refundPkScript = getP2TRScriptFromPublicKey(receivingPubkey, network);
18358
- newRefundTx.addOutput({
18559
+ let outputAmount = amountSats;
18560
+ if (shouldCalculateFee) {
18561
+ outputAmount = maybeApplyFee(amountSats);
18562
+ }
18563
+ refundTx.addOutput({
18359
18564
  script: refundPkScript,
18565
+ amount: outputAmount
18566
+ });
18567
+ if (includeAnchor) {
18568
+ refundTx.addOutput(getEphemeralAnchorOutput());
18569
+ }
18570
+ return refundTx;
18571
+ }
18572
+ function createRefundTxs({
18573
+ sequence,
18574
+ directSequence,
18575
+ input,
18576
+ directInput,
18577
+ amountSats,
18578
+ receivingPubkey,
18579
+ network
18580
+ }) {
18581
+ const cpfpRefundTx = createRefundTx({
18582
+ sequence,
18583
+ input,
18584
+ amountSats,
18585
+ receivingPubkey,
18586
+ network,
18587
+ shouldCalculateFee: false,
18588
+ includeAnchor: true
18589
+ });
18590
+ let directRefundTx;
18591
+ let directFromCpfpRefundTx;
18592
+ if (directSequence && directInput) {
18593
+ directRefundTx = createRefundTx({
18594
+ sequence: directSequence,
18595
+ input: directInput,
18596
+ amountSats,
18597
+ receivingPubkey,
18598
+ network,
18599
+ shouldCalculateFee: true,
18600
+ includeAnchor: false
18601
+ });
18602
+ directFromCpfpRefundTx = createRefundTx({
18603
+ sequence: directSequence,
18604
+ input,
18605
+ amountSats,
18606
+ receivingPubkey,
18607
+ network,
18608
+ shouldCalculateFee: true,
18609
+ includeAnchor: false
18610
+ });
18611
+ } else if (directInput && !directSequence) {
18612
+ throw new ValidationError(
18613
+ "directSequence must be provided if directInput is",
18614
+ {
18615
+ field: "directSequence",
18616
+ value: directSequence
18617
+ }
18618
+ );
18619
+ }
18620
+ return { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx };
18621
+ }
18622
+ function createConnectorRefundTransactions(sequence, cpfpNodeOutPoint, directNodeOutPoint, connectorOutput, amountSats, receiverPubKey, network, shouldCalculateFee) {
18623
+ const cpfpRefundTx = new import_btc_signer.Transaction({
18624
+ version: 3,
18625
+ allowUnknownOutputs: true
18626
+ });
18627
+ cpfpRefundTx.addInput({
18628
+ ...cpfpNodeOutPoint,
18629
+ sequence
18630
+ });
18631
+ cpfpRefundTx.addInput(connectorOutput);
18632
+ const receiverScript = getP2TRScriptFromPublicKey(receiverPubKey, network);
18633
+ cpfpRefundTx.addOutput({
18634
+ script: receiverScript,
18360
18635
  amount: amountSats
18361
18636
  });
18362
- newRefundTx.addOutput(getEphemeralAnchorOutput());
18363
- return newRefundTx;
18637
+ const directRefundTx = new import_btc_signer.Transaction({
18638
+ version: 3,
18639
+ allowUnknownOutputs: true
18640
+ });
18641
+ directRefundTx.addInput({
18642
+ ...directNodeOutPoint,
18643
+ sequence
18644
+ });
18645
+ directRefundTx.addInput(connectorOutput);
18646
+ let outputAmount = amountSats;
18647
+ if (shouldCalculateFee) {
18648
+ outputAmount = maybeApplyFee(amountSats);
18649
+ }
18650
+ directRefundTx.addOutput({
18651
+ script: receiverScript,
18652
+ amount: outputAmount
18653
+ });
18654
+ const directFromCpfpTx = new import_btc_signer.Transaction({
18655
+ version: 3,
18656
+ allowUnknownOutputs: true
18657
+ });
18658
+ directFromCpfpTx.addInput({
18659
+ ...cpfpNodeOutPoint,
18660
+ sequence
18661
+ });
18662
+ directFromCpfpTx.addInput(connectorOutput);
18663
+ directFromCpfpTx.addOutput({
18664
+ script: receiverScript,
18665
+ amount: outputAmount
18666
+ });
18667
+ return [cpfpRefundTx, directRefundTx, directFromCpfpTx];
18364
18668
  }
18365
18669
  function getCurrentTimelock(currSequence) {
18366
18670
  return (currSequence || 0) & 65535;
18367
18671
  }
18368
18672
  function getTransactionSequence(currSequence) {
18369
18673
  const timelock = getCurrentTimelock(currSequence);
18370
- return 1 << 30 | timelock;
18674
+ return {
18675
+ nextSequence: 1 << 30 | timelock,
18676
+ nextDirectSequence: 1 << 30 | timelock + DIRECT_TIMELOCK_OFFSET
18677
+ };
18371
18678
  }
18372
18679
  function checkIfValidSequence(currSequence) {
18373
18680
  const TIME_LOCK_ACTIVE = (currSequence || 0) & 2147483648;
@@ -18385,24 +18692,32 @@ function checkIfValidSequence(currSequence) {
18385
18692
  });
18386
18693
  }
18387
18694
  }
18388
- function getNextTransactionSequence(currSequence, forRefresh) {
18695
+ function doesLeafNeedRefresh(currSequence, isNodeTx) {
18389
18696
  const currentTimelock = getCurrentTimelock(currSequence);
18390
- const nextTimelock = currentTimelock - TIME_LOCK_INTERVAL;
18391
- if (forRefresh && nextTimelock <= 100 && currentTimelock > 0) {
18392
- return {
18393
- nextSequence: 1 << 30 | nextTimelock,
18394
- needRefresh: true
18395
- };
18697
+ if (isNodeTx) {
18698
+ return currentTimelock === 0;
18396
18699
  }
18397
- if (nextTimelock < 0) {
18700
+ return currentTimelock <= 100;
18701
+ }
18702
+ function getNextTransactionSequence(currSequence, isNodeTx) {
18703
+ const currentTimelock = getCurrentTimelock(currSequence);
18704
+ const nextTimelock = currentTimelock - TIME_LOCK_INTERVAL;
18705
+ if (isNodeTx && nextTimelock < 0) {
18398
18706
  throw new ValidationError("timelock interval is less than 0", {
18399
18707
  field: "nextTimelock",
18400
- value: nextTimelock
18708
+ value: nextTimelock,
18709
+ expected: "Non-negative timelock interval"
18710
+ });
18711
+ } else if (!isNodeTx && nextTimelock <= 0) {
18712
+ throw new ValidationError("timelock interval is less than or equal to 0", {
18713
+ field: "nextTimelock",
18714
+ value: nextTimelock,
18715
+ expected: "Timelock greater than 0"
18401
18716
  });
18402
18717
  }
18403
18718
  return {
18404
18719
  nextSequence: 1 << 30 | nextTimelock,
18405
- needRefresh: nextTimelock <= 100
18720
+ nextDirectSequence: 1 << 30 | nextTimelock + DIRECT_TIMELOCK_OFFSET
18406
18721
  };
18407
18722
  }
18408
18723
  function getEphemeralAnchorOutput() {
@@ -18931,7 +19246,7 @@ init_buffer();
18931
19246
  var import_core8 = require("@lightsparkdev/core");
18932
19247
  var isReactNative = typeof navigator !== "undefined" && navigator.product === "ReactNative";
18933
19248
  var isBun = globalThis.Bun !== void 0;
18934
- var packageVersion = true ? "0.2.3" : "unknown";
19249
+ var packageVersion = true ? "0.2.4" : "unknown";
18935
19250
  var baseEnvStr = "unknown";
18936
19251
  if (isBun) {
18937
19252
  const bunVersion = "version" in globalThis.Bun ? globalThis.Bun.version : "unknown-version";
@@ -19642,10 +19957,18 @@ init_buffer();
19642
19957
  var CompleteLeavesSwap = `
19643
19958
  mutation CompleteLeavesSwap(
19644
19959
  $adaptor_secret_key: String!
19960
+ $direct_adaptor_secret_key: String!
19961
+ $direct_from_cpfp_adaptor_secret_key: String!
19645
19962
  $user_outbound_transfer_external_id: UUID!
19646
19963
  $leaves_swap_request_id: ID!
19647
19964
  ) {
19648
- complete_leaves_swap(input: { adaptor_secret_key: $adaptor_secret_key, user_outbound_transfer_external_id: $user_outbound_transfer_external_id, leaves_swap_request_id: $leaves_swap_request_id }) {
19965
+ complete_leaves_swap(input: {
19966
+ adaptor_secret_key: $adaptor_secret_key,
19967
+ direct_adaptor_secret_key: $direct_adaptor_secret_key,
19968
+ direct_from_cpfp_adaptor_secret_key: $direct_from_cpfp_adaptor_secret_key,
19969
+ user_outbound_transfer_external_id: $user_outbound_transfer_external_id,
19970
+ leaves_swap_request_id: $leaves_swap_request_id
19971
+ }) {
19649
19972
  request {
19650
19973
  ...LeavesSwapRequestFragment
19651
19974
  }
@@ -19776,6 +20099,8 @@ init_buffer();
19776
20099
  var RequestSwapLeaves = `
19777
20100
  mutation RequestSwapLeaves(
19778
20101
  $adaptor_pubkey: PublicKey!
20102
+ $direct_adaptor_pubkey: PublicKey
20103
+ $direct_from_cpfp_adaptor_pubkey: PublicKey
19779
20104
  $total_amount_sats: Long!
19780
20105
  $target_amount_sats: Long!
19781
20106
  $fee_sats: Long!
@@ -19785,6 +20110,8 @@ var RequestSwapLeaves = `
19785
20110
  ) {
19786
20111
  request_leaves_swap(input: {
19787
20112
  adaptor_pubkey: $adaptor_pubkey
20113
+ direct_adaptor_pubkey: $direct_adaptor_pubkey
20114
+ direct_from_cpfp_adaptor_pubkey: $direct_from_cpfp_adaptor_pubkey
19788
20115
  total_amount_sats: $total_amount_sats
19789
20116
  target_amount_sats: $target_amount_sats
19790
20117
  fee_sats: $fee_sats
@@ -20293,6 +20620,8 @@ var SspClient = class {
20293
20620
  }
20294
20621
  async requestLeaveSwap({
20295
20622
  adaptorPubkey,
20623
+ directAdaptorPubkey,
20624
+ directFromCpfpAdaptorPubkey,
20296
20625
  totalAmountSats,
20297
20626
  targetAmountSats,
20298
20627
  feeSats,
@@ -20304,6 +20633,8 @@ var SspClient = class {
20304
20633
  queryPayload: RequestSwapLeaves,
20305
20634
  variables: {
20306
20635
  adaptor_pubkey: adaptorPubkey,
20636
+ direct_adaptor_pubkey: directAdaptorPubkey,
20637
+ direct_from_cpfp_adaptor_pubkey: directFromCpfpAdaptorPubkey,
20307
20638
  total_amount_sats: totalAmountSats,
20308
20639
  target_amount_sats: targetAmountSats,
20309
20640
  fee_sats: feeSats,
@@ -20322,6 +20653,8 @@ var SspClient = class {
20322
20653
  }
20323
20654
  async completeLeaveSwap({
20324
20655
  adaptorSecretKey,
20656
+ directAdaptorSecretKey,
20657
+ directFromCpfpAdaptorSecretKey,
20325
20658
  userOutboundTransferExternalId,
20326
20659
  leavesSwapRequestId
20327
20660
  }) {
@@ -20329,6 +20662,8 @@ var SspClient = class {
20329
20662
  queryPayload: CompleteLeavesSwap,
20330
20663
  variables: {
20331
20664
  adaptor_secret_key: adaptorSecretKey,
20665
+ direct_adaptor_secret_key: directAdaptorSecretKey,
20666
+ direct_from_cpfp_adaptor_secret_key: directFromCpfpAdaptorSecretKey,
20332
20667
  user_outbound_transfer_external_id: userOutboundTransferExternalId,
20333
20668
  leaves_swap_request_id: leavesSwapRequestId
20334
20669
  },
@@ -24493,11 +24828,7 @@ var import_sha28 = require("@noble/hashes/sha2");
24493
24828
  var import_btc_signer2 = require("@scure/btc-signer");
24494
24829
  var ecies2 = __toESM(require("eciesjs"), 1);
24495
24830
  var import_uuidv72 = require("uuidv7");
24496
- var INITIAL_TIME_LOCK = 2e3;
24497
24831
  var DEFAULT_EXPIRY_TIME = 10 * 60 * 1e3;
24498
- function initialSequence() {
24499
- return 1 << 30 | INITIAL_TIME_LOCK;
24500
- }
24501
24832
  function getSigningJobProto(signingJob) {
24502
24833
  return {
24503
24834
  signingPublicKey: signingJob.signingPublicKey,
@@ -24514,12 +24845,14 @@ var BaseTransferService = class {
24514
24845
  this.connectionManager = connectionManager;
24515
24846
  this.signingService = signingService;
24516
24847
  }
24517
- async sendTransferTweakKey(transfer, leaves, refundSignatureMap) {
24848
+ async sendTransferTweakKey(transfer, leaves, cpfpRefundSignatureMap, directRefundSignatureMap, directFromCpfpRefundSignatureMap) {
24518
24849
  const keyTweakInputMap = await this.prepareSendTransferKeyTweaks(
24519
24850
  transfer.id,
24520
24851
  transfer.receiverIdentityPublicKey,
24521
24852
  leaves,
24522
- refundSignatureMap
24853
+ cpfpRefundSignatureMap,
24854
+ directRefundSignatureMap,
24855
+ directFromCpfpRefundSignatureMap
24523
24856
  );
24524
24857
  let updatedTransfer;
24525
24858
  const coordinatorOperator = this.config.getSigningOperators()[this.config.getCoordinatorIdentifier()];
@@ -24557,13 +24890,26 @@ var BaseTransferService = class {
24557
24890
  }
24558
24891
  return updatedTransfer;
24559
24892
  }
24560
- async deliverTransferPackage(transfer, leaves, refundSignatureMap) {
24893
+ async deliverTransferPackage(transfer, leaves, cpfpRefundSignatureMap, directRefundSignatureMap, directFromCpfpRefundSignatureMap) {
24561
24894
  const keyTweakInputMap = await this.prepareSendTransferKeyTweaks(
24562
24895
  transfer.id,
24563
24896
  transfer.receiverIdentityPublicKey,
24564
24897
  leaves,
24565
- refundSignatureMap
24898
+ cpfpRefundSignatureMap,
24899
+ directRefundSignatureMap,
24900
+ directFromCpfpRefundSignatureMap
24566
24901
  );
24902
+ for (const [key, operator] of Object.entries(
24903
+ this.config.getSigningOperators()
24904
+ )) {
24905
+ const tweaks = keyTweakInputMap.get(key);
24906
+ if (!tweaks) {
24907
+ throw new ValidationError("No tweaks for operator", {
24908
+ field: "operator",
24909
+ value: key
24910
+ });
24911
+ }
24912
+ }
24567
24913
  const transferPackage = await this.prepareTransferPackage(
24568
24914
  transfer.id,
24569
24915
  keyTweakInputMap,
@@ -24589,6 +24935,8 @@ var BaseTransferService = class {
24589
24935
  transferID,
24590
24936
  receiverIdentityPubkey,
24591
24937
  leaves,
24938
+ /* @__PURE__ */ new Map(),
24939
+ /* @__PURE__ */ new Map(),
24592
24940
  /* @__PURE__ */ new Map()
24593
24941
  );
24594
24942
  const transferPackage = await this.prepareTransferPackage(
@@ -24602,7 +24950,7 @@ var BaseTransferService = class {
24602
24950
  );
24603
24951
  let response;
24604
24952
  try {
24605
- response = await sparkClient.start_transfer({
24953
+ response = await sparkClient.start_transfer_v2({
24606
24954
  transferId: transferID,
24607
24955
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
24608
24956
  receiverIdentityPublicKey: receiverIdentityPubkey,
@@ -24631,12 +24979,22 @@ var BaseTransferService = class {
24631
24979
  nodes.push(leaf.leaf.id);
24632
24980
  }
24633
24981
  const signingCommitments = await sparkClient.get_signing_commitments({
24634
- nodeIds: nodes
24982
+ nodeIds: nodes,
24983
+ count: 3
24635
24984
  });
24636
- const leafSigningJobs = await this.signingService.signRefunds(
24985
+ const {
24986
+ cpfpLeafSigningJobs,
24987
+ directLeafSigningJobs,
24988
+ directFromCpfpLeafSigningJobs
24989
+ } = await this.signingService.signRefunds(
24637
24990
  leaves,
24638
- signingCommitments.signingCommitments,
24639
- receiverIdentityPubkey
24991
+ receiverIdentityPubkey,
24992
+ signingCommitments.signingCommitments.slice(0, leaves.length),
24993
+ signingCommitments.signingCommitments.slice(
24994
+ leaves.length,
24995
+ 2 * leaves.length
24996
+ ),
24997
+ signingCommitments.signingCommitments.slice(2 * leaves.length)
24640
24998
  );
24641
24999
  const encryptedKeyTweaks = {};
24642
25000
  for (const [key, value] of keyTweakInputMap) {
@@ -24656,12 +25014,11 @@ var BaseTransferService = class {
24656
25014
  encryptedKeyTweaks[key] = Uint8Array.from(encryptedProto);
24657
25015
  }
24658
25016
  const transferPackage = {
24659
- leavesToSend: leafSigningJobs,
25017
+ leavesToSend: cpfpLeafSigningJobs,
24660
25018
  keyTweakPackage: encryptedKeyTweaks,
24661
25019
  userSignature: new Uint8Array(),
24662
- // TODO: Add direct refund signature
24663
- directLeavesToSend: [],
24664
- directFromCpfpLeavesToSend: []
25020
+ directLeavesToSend: directLeafSigningJobs,
25021
+ directFromCpfpLeavesToSend: directFromCpfpLeafSigningJobs
24665
25022
  };
24666
25023
  const transferPackageSigningPayload = getTransferPackageSigningPayload(
24667
25024
  transferID,
@@ -24721,7 +25078,7 @@ var BaseTransferService = class {
24721
25078
  }
24722
25079
  return updatedTransfer;
24723
25080
  }
24724
- async signRefunds(leafDataMap, operatorSigningResults, adaptorPubKey) {
25081
+ async signRefunds(leafDataMap, operatorSigningResults, cpfpAdaptorPubKey, directAdaptorPubKey, directFromCpfpAdaptorPubKey) {
24725
25082
  const nodeSignatures = [];
24726
25083
  for (const operatorSigningResult of operatorSigningResults) {
24727
25084
  const leafData = leafDataMap.get(operatorSigningResult.leafId);
@@ -24736,54 +25093,120 @@ var BaseTransferService = class {
24736
25093
  `Output not found for leaf ${operatorSigningResult.leafId}`
24737
25094
  );
24738
25095
  }
24739
- const refundTxSighash = getSigHashFromTx(leafData.refundTx, 0, txOutput);
25096
+ const cpfpRefundTxSighash = getSigHashFromTx(
25097
+ leafData.refundTx,
25098
+ 0,
25099
+ txOutput
25100
+ );
24740
25101
  const publicKey = await this.config.signer.getPublicKeyFromDerivation(
24741
25102
  leafData.keyDerivation
24742
25103
  );
24743
- const userSignature = await this.config.signer.signFrost({
24744
- message: refundTxSighash,
25104
+ const cpfpUserSignature = await this.config.signer.signFrost({
25105
+ message: cpfpRefundTxSighash,
24745
25106
  publicKey,
24746
25107
  keyDerivation: leafData.keyDerivation,
24747
25108
  selfCommitment: leafData.signingNonceCommitment,
24748
25109
  statechainCommitments: operatorSigningResult.refundTxSigningResult?.signingNonceCommitments,
24749
- adaptorPubKey,
25110
+ adaptorPubKey: cpfpAdaptorPubKey,
24750
25111
  verifyingKey: operatorSigningResult.verifyingKey
24751
25112
  });
24752
- const refundAggregate = await this.config.signer.aggregateFrost({
24753
- message: refundTxSighash,
25113
+ const cpfpRefundAggregate = await this.config.signer.aggregateFrost({
25114
+ message: cpfpRefundTxSighash,
24754
25115
  statechainSignatures: operatorSigningResult.refundTxSigningResult?.signatureShares,
24755
25116
  statechainPublicKeys: operatorSigningResult.refundTxSigningResult?.publicKeys,
24756
25117
  verifyingKey: operatorSigningResult.verifyingKey,
24757
25118
  statechainCommitments: operatorSigningResult.refundTxSigningResult?.signingNonceCommitments,
24758
25119
  selfCommitment: leafData.signingNonceCommitment,
24759
25120
  publicKey,
24760
- selfSignature: userSignature,
24761
- adaptorPubKey
25121
+ selfSignature: cpfpUserSignature,
25122
+ adaptorPubKey: cpfpAdaptorPubKey
24762
25123
  });
25124
+ let directRefundAggregate;
25125
+ let directFromCpfpRefundAggregate;
25126
+ if (leafData.directTx) {
25127
+ const directTxOutput = leafData.directTx.getOutput(0);
25128
+ if (leafData.directRefundTx) {
25129
+ const directRefundTxSighash = getSigHashFromTx(
25130
+ leafData.directRefundTx,
25131
+ 0,
25132
+ directTxOutput
25133
+ );
25134
+ const directUserSignature = await this.config.signer.signFrost({
25135
+ message: directRefundTxSighash,
25136
+ publicKey,
25137
+ keyDerivation: leafData.keyDerivation,
25138
+ selfCommitment: leafData.directSigningNonceCommitment,
25139
+ statechainCommitments: operatorSigningResult.directRefundTxSigningResult?.signingNonceCommitments,
25140
+ adaptorPubKey: directAdaptorPubKey,
25141
+ verifyingKey: operatorSigningResult.verifyingKey
25142
+ });
25143
+ directRefundAggregate = await this.config.signer.aggregateFrost({
25144
+ message: directRefundTxSighash,
25145
+ statechainSignatures: operatorSigningResult.directRefundTxSigningResult?.signatureShares,
25146
+ statechainPublicKeys: operatorSigningResult.directRefundTxSigningResult?.publicKeys,
25147
+ verifyingKey: operatorSigningResult.verifyingKey,
25148
+ statechainCommitments: operatorSigningResult.directRefundTxSigningResult?.signingNonceCommitments,
25149
+ selfCommitment: leafData.directSigningNonceCommitment,
25150
+ publicKey,
25151
+ selfSignature: directUserSignature,
25152
+ adaptorPubKey: directAdaptorPubKey
25153
+ });
25154
+ }
25155
+ if (leafData.directFromCpfpRefundTx) {
25156
+ const directFromCpfpRefundTxSighash = getSigHashFromTx(
25157
+ leafData.directFromCpfpRefundTx,
25158
+ 0,
25159
+ txOutput
25160
+ );
25161
+ const directFromCpfpUserSignature = await this.config.signer.signFrost({
25162
+ message: directFromCpfpRefundTxSighash,
25163
+ publicKey,
25164
+ keyDerivation: leafData.keyDerivation,
25165
+ selfCommitment: leafData.directFromCpfpRefundSigningNonceCommitment,
25166
+ statechainCommitments: operatorSigningResult.directFromCpfpRefundTxSigningResult?.signingNonceCommitments,
25167
+ adaptorPubKey: directFromCpfpAdaptorPubKey,
25168
+ verifyingKey: operatorSigningResult.verifyingKey
25169
+ });
25170
+ directFromCpfpRefundAggregate = await this.config.signer.aggregateFrost({
25171
+ message: directFromCpfpRefundTxSighash,
25172
+ statechainSignatures: operatorSigningResult.directFromCpfpRefundTxSigningResult?.signatureShares,
25173
+ statechainPublicKeys: operatorSigningResult.directFromCpfpRefundTxSigningResult?.publicKeys,
25174
+ verifyingKey: operatorSigningResult.verifyingKey,
25175
+ statechainCommitments: operatorSigningResult.directFromCpfpRefundTxSigningResult?.signingNonceCommitments,
25176
+ selfCommitment: leafData.directFromCpfpRefundSigningNonceCommitment,
25177
+ publicKey,
25178
+ selfSignature: directFromCpfpUserSignature,
25179
+ adaptorPubKey: directFromCpfpAdaptorPubKey
25180
+ });
25181
+ }
25182
+ }
24763
25183
  nodeSignatures.push({
24764
25184
  nodeId: operatorSigningResult.leafId,
24765
- refundTxSignature: refundAggregate,
24766
25185
  nodeTxSignature: new Uint8Array(),
24767
- // TODO: Add direct refund signature
24768
25186
  directNodeTxSignature: new Uint8Array(),
24769
- directRefundTxSignature: new Uint8Array(),
24770
- directFromCpfpRefundTxSignature: new Uint8Array()
25187
+ refundTxSignature: cpfpRefundAggregate,
25188
+ directRefundTxSignature: directRefundAggregate ?? new Uint8Array(),
25189
+ directFromCpfpRefundTxSignature: directFromCpfpRefundAggregate ?? new Uint8Array()
24771
25190
  });
24772
25191
  }
24773
25192
  return nodeSignatures;
24774
25193
  }
24775
- async prepareSendTransferKeyTweaks(transferID, receiverIdentityPubkey, leaves, refundSignatureMap) {
25194
+ async prepareSendTransferKeyTweaks(transferID, receiverIdentityPubkey, leaves, cpfpRefundSignatureMap, directRefundSignatureMap, directFromCpfpRefundSignatureMap) {
24776
25195
  const receiverEciesPubKey = ecies2.PublicKey.fromHex(
24777
25196
  (0, import_utils14.bytesToHex)(receiverIdentityPubkey)
24778
25197
  );
24779
25198
  const leavesTweaksMap = /* @__PURE__ */ new Map();
24780
25199
  for (const leaf of leaves) {
24781
- const refundSignature = refundSignatureMap.get(leaf.leaf.id);
25200
+ const cpfpRefundSignature = cpfpRefundSignatureMap.get(leaf.leaf.id);
25201
+ const directRefundSignature = directRefundSignatureMap.get(leaf.leaf.id);
25202
+ const directFromCpfpRefundSignature = directFromCpfpRefundSignatureMap.get(leaf.leaf.id);
24782
25203
  const leafTweaksMap = await this.prepareSingleSendTransferKeyTweak(
24783
25204
  transferID,
24784
25205
  leaf,
24785
25206
  receiverEciesPubKey,
24786
- refundSignature
25207
+ cpfpRefundSignature,
25208
+ directRefundSignature,
25209
+ directFromCpfpRefundSignature
24787
25210
  );
24788
25211
  for (const [identifier, leafTweak] of leafTweaksMap) {
24789
25212
  leavesTweaksMap.set(identifier, [
@@ -24794,7 +25217,7 @@ var BaseTransferService = class {
24794
25217
  }
24795
25218
  return leavesTweaksMap;
24796
25219
  }
24797
- async prepareSingleSendTransferKeyTweak(transferID, leaf, receiverEciesPubKey, refundSignature) {
25220
+ async prepareSingleSendTransferKeyTweak(transferID, leaf, receiverEciesPubKey, cpfpRefundSignature, directRefundSignature, directFromCpfpRefundSignature) {
24798
25221
  const signingOperators = this.config.getSigningOperators();
24799
25222
  const { shares, secretCipher } = await this.config.signer.subtractSplitAndEncrypt({
24800
25223
  first: leaf.keyDerivation,
@@ -24842,10 +25265,9 @@ var BaseTransferService = class {
24842
25265
  pubkeySharesTweak: Object.fromEntries(pubkeySharesTweak),
24843
25266
  secretCipher,
24844
25267
  signature,
24845
- refundSignature: refundSignature ?? new Uint8Array(),
24846
- // TODO: Add direct refund signature
24847
- directRefundSignature: new Uint8Array(),
24848
- directFromCpfpRefundSignature: new Uint8Array()
25268
+ refundSignature: cpfpRefundSignature ?? new Uint8Array(),
25269
+ directRefundSignature: directRefundSignature ?? new Uint8Array(),
25270
+ directFromCpfpRefundSignature: directFromCpfpRefundSignature ?? new Uint8Array()
24849
25271
  });
24850
25272
  }
24851
25273
  return leafTweaksMap;
@@ -24875,7 +25297,12 @@ var TransferService = class extends BaseTransferService {
24875
25297
  * Deprecated in v0.1.32
24876
25298
  */
24877
25299
  async sendTransfer(leaves, receiverIdentityPubkey) {
24878
- const { transfer, signatureMap } = await this.sendTransferSignRefund(
25300
+ const {
25301
+ transfer,
25302
+ signatureMap,
25303
+ directSignatureMap,
25304
+ directFromCpfpSignatureMap
25305
+ } = await this.sendTransferSignRefund(
24879
25306
  leaves,
24880
25307
  receiverIdentityPubkey,
24881
25308
  new Date(Date.now() + DEFAULT_EXPIRY_TIME)
@@ -24883,7 +25310,9 @@ var TransferService = class extends BaseTransferService {
24883
25310
  const transferWithTweakedKeys = await this.sendTransferTweakKey(
24884
25311
  transfer,
24885
25312
  leaves,
24886
- signatureMap
25313
+ signatureMap,
25314
+ directSignatureMap,
25315
+ directFromCpfpSignatureMap
24887
25316
  );
24888
25317
  return transferWithTweakedKeys;
24889
25318
  }
@@ -24987,7 +25416,13 @@ var TransferService = class extends BaseTransferService {
24987
25416
  return transferResp.transfers[0];
24988
25417
  }
24989
25418
  async sendTransferSignRefund(leaves, receiverIdentityPubkey, expiryTime) {
24990
- const { transfer, signatureMap, leafDataMap } = await this.sendTransferSignRefundInternal(
25419
+ const {
25420
+ transfer,
25421
+ signatureMap,
25422
+ directSignatureMap,
25423
+ directFromCpfpSignatureMap,
25424
+ leafDataMap
25425
+ } = await this.sendTransferSignRefundInternal(
24991
25426
  leaves,
24992
25427
  receiverIdentityPubkey,
24993
25428
  expiryTime,
@@ -24996,11 +25431,19 @@ var TransferService = class extends BaseTransferService {
24996
25431
  return {
24997
25432
  transfer,
24998
25433
  signatureMap,
25434
+ directSignatureMap,
25435
+ directFromCpfpSignatureMap,
24999
25436
  leafDataMap
25000
25437
  };
25001
25438
  }
25002
25439
  async startSwapSignRefund(leaves, receiverIdentityPubkey, expiryTime) {
25003
- const { transfer, signatureMap, leafDataMap } = await this.sendTransferSignRefundInternal(
25440
+ const {
25441
+ transfer,
25442
+ signatureMap,
25443
+ directSignatureMap,
25444
+ directFromCpfpSignatureMap,
25445
+ leafDataMap
25446
+ } = await this.sendTransferSignRefundInternal(
25004
25447
  leaves,
25005
25448
  receiverIdentityPubkey,
25006
25449
  expiryTime,
@@ -25009,31 +25452,45 @@ var TransferService = class extends BaseTransferService {
25009
25452
  return {
25010
25453
  transfer,
25011
25454
  signatureMap,
25455
+ directSignatureMap,
25456
+ directFromCpfpSignatureMap,
25012
25457
  leafDataMap
25013
25458
  };
25014
25459
  }
25015
- async counterSwapSignRefund(leaves, receiverIdentityPubkey, expiryTime, adaptorPubKey) {
25460
+ async counterSwapSignRefund(leaves, receiverIdentityPubkey, expiryTime, cpfpAdaptorPubKey, directAdaptorPubKey, directFromCpfpAdaptorPubKey) {
25016
25461
  return this.sendTransferSignRefundInternal(
25017
25462
  leaves,
25018
25463
  receiverIdentityPubkey,
25019
25464
  expiryTime,
25020
25465
  true,
25021
- adaptorPubKey
25466
+ cpfpAdaptorPubKey,
25467
+ directAdaptorPubKey,
25468
+ directFromCpfpAdaptorPubKey
25022
25469
  );
25023
25470
  }
25024
- async sendTransferSignRefundInternal(leaves, receiverIdentityPubkey, expiryTime, forSwap, adaptorPubKey) {
25471
+ async sendTransferSignRefundInternal(leaves, receiverIdentityPubkey, expiryTime, forSwap, cpfpAdaptorPubKey, directAdaptorPubKey, directFromCpfpAdaptorPubKey) {
25025
25472
  const transferId = (0, import_uuidv72.uuidv7)();
25026
25473
  const leafDataMap = /* @__PURE__ */ new Map();
25027
25474
  for (const leaf of leaves) {
25028
25475
  const signingNonceCommitment = await this.config.signer.getRandomSigningCommitment();
25476
+ const directSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
25477
+ const directFromCpfpRefundSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
25029
25478
  const tx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
25030
25479
  const refundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
25480
+ const directTx = leaf.leaf.directTx.length > 0 ? getTxFromRawTxBytes(leaf.leaf.directTx) : void 0;
25481
+ const directRefundTx = leaf.leaf.directRefundTx.length > 0 ? getTxFromRawTxBytes(leaf.leaf.directRefundTx) : void 0;
25482
+ const directFromCpfpRefundTx = leaf.leaf.directFromCpfpRefundTx.length > 0 ? getTxFromRawTxBytes(leaf.leaf.directFromCpfpRefundTx) : void 0;
25031
25483
  leafDataMap.set(leaf.leaf.id, {
25032
25484
  keyDerivation: leaf.keyDerivation,
25033
25485
  receivingPubkey: receiverIdentityPubkey,
25034
25486
  signingNonceCommitment,
25487
+ directSigningNonceCommitment,
25035
25488
  tx,
25489
+ directTx,
25036
25490
  refundTx,
25491
+ directRefundTx,
25492
+ directFromCpfpRefundTx,
25493
+ directFromCpfpRefundSigningNonceCommitment,
25037
25494
  vout: leaf.leaf.vout
25038
25495
  });
25039
25496
  }
@@ -25046,8 +25503,8 @@ var TransferService = class extends BaseTransferService {
25046
25503
  );
25047
25504
  let response;
25048
25505
  try {
25049
- if (adaptorPubKey !== void 0) {
25050
- response = await sparkClient.counter_leaf_swap({
25506
+ if (cpfpAdaptorPubKey !== void 0 || directAdaptorPubKey !== void 0 || directFromCpfpAdaptorPubKey !== void 0) {
25507
+ response = await sparkClient.counter_leaf_swap_v2({
25051
25508
  transfer: {
25052
25509
  transferId,
25053
25510
  leavesToSend: signingJobs,
@@ -25056,10 +25513,12 @@ var TransferService = class extends BaseTransferService {
25056
25513
  expiryTime
25057
25514
  },
25058
25515
  swapId: (0, import_uuidv72.uuidv7)(),
25059
- adaptorPublicKey: adaptorPubKey || new Uint8Array()
25516
+ adaptorPublicKey: cpfpAdaptorPubKey,
25517
+ directAdaptorPublicKey: directAdaptorPubKey,
25518
+ directFromCpfpAdaptorPublicKey: directFromCpfpAdaptorPubKey
25060
25519
  });
25061
25520
  } else if (forSwap) {
25062
- response = await sparkClient.start_leaf_swap({
25521
+ response = await sparkClient.start_leaf_swap_v2({
25063
25522
  transferId,
25064
25523
  leavesToSend: signingJobs,
25065
25524
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
@@ -25067,7 +25526,7 @@ var TransferService = class extends BaseTransferService {
25067
25526
  expiryTime
25068
25527
  });
25069
25528
  } else {
25070
- response = await sparkClient.start_transfer({
25529
+ response = await sparkClient.start_transfer_v2({
25071
25530
  transferId,
25072
25531
  leavesToSend: signingJobs,
25073
25532
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
@@ -25084,15 +25543,29 @@ var TransferService = class extends BaseTransferService {
25084
25543
  const signatures = await this.signRefunds(
25085
25544
  leafDataMap,
25086
25545
  response.signingResults,
25087
- adaptorPubKey
25546
+ cpfpAdaptorPubKey,
25547
+ directAdaptorPubKey,
25548
+ directFromCpfpAdaptorPubKey
25088
25549
  );
25089
- const signatureMap = /* @__PURE__ */ new Map();
25550
+ const cpfpSignatureMap = /* @__PURE__ */ new Map();
25551
+ const directSignatureMap = /* @__PURE__ */ new Map();
25552
+ const directFromCpfpSignatureMap = /* @__PURE__ */ new Map();
25090
25553
  for (const signature of signatures) {
25091
- signatureMap.set(signature.nodeId, signature.refundTxSignature);
25554
+ cpfpSignatureMap.set(signature.nodeId, signature.refundTxSignature);
25555
+ directSignatureMap.set(
25556
+ signature.nodeId,
25557
+ signature.directRefundTxSignature
25558
+ );
25559
+ directFromCpfpSignatureMap.set(
25560
+ signature.nodeId,
25561
+ signature.directFromCpfpRefundTxSignature
25562
+ );
25092
25563
  }
25093
25564
  return {
25094
25565
  transfer: response.transfer,
25095
- signatureMap,
25566
+ signatureMap: cpfpSignatureMap,
25567
+ directSignatureMap,
25568
+ directFromCpfpSignatureMap,
25096
25569
  leafDataMap,
25097
25570
  signingResults: response.signingResults
25098
25571
  };
@@ -25105,38 +25578,68 @@ var TransferService = class extends BaseTransferService {
25105
25578
  throw new Error(`Leaf data not found for leaf ${leaf.leaf.id}`);
25106
25579
  }
25107
25580
  const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
25108
- const nodeOutPoint = {
25581
+ const cpfpNodeOutPoint = {
25109
25582
  txid: (0, import_utils14.hexToBytes)(getTxId(nodeTx)),
25110
25583
  index: 0
25111
25584
  };
25585
+ let directNodeTx;
25586
+ let directNodeOutPoint;
25587
+ if (leaf.leaf.directTx.length > 0) {
25588
+ directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
25589
+ directNodeOutPoint = {
25590
+ txid: (0, import_utils14.hexToBytes)(getTxId(directNodeTx)),
25591
+ index: 0
25592
+ };
25593
+ }
25112
25594
  const currRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
25113
- const nextSequence = isForClaim ? getTransactionSequence(currRefundTx.getInput(0).sequence) : getNextTransactionSequence(currRefundTx.getInput(0).sequence).nextSequence;
25595
+ const sequence = currRefundTx.getInput(0).sequence;
25596
+ if (!sequence) {
25597
+ throw new ValidationError("Invalid refund transaction", {
25598
+ field: "sequence",
25599
+ value: currRefundTx.getInput(0),
25600
+ expected: "Non-null sequence"
25601
+ });
25602
+ }
25603
+ const { nextSequence, nextDirectSequence } = isForClaim ? getTransactionSequence(sequence) : getNextTransactionSequence(sequence);
25114
25604
  const amountSats = currRefundTx.getOutput(0).amount;
25115
25605
  if (amountSats === void 0) {
25116
25606
  throw new Error("Amount not found in signRefunds");
25117
25607
  }
25118
- const refundTx = createRefundTx(
25119
- nextSequence,
25120
- nodeOutPoint,
25608
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
25609
+ sequence: nextSequence,
25610
+ directSequence: nextDirectSequence,
25611
+ input: cpfpNodeOutPoint,
25612
+ directInput: directNodeOutPoint,
25121
25613
  amountSats,
25122
- refundSigningData.receivingPubkey,
25123
- this.config.getNetwork()
25614
+ receivingPubkey: refundSigningData.receivingPubkey,
25615
+ network: this.config.getNetwork()
25616
+ });
25617
+ refundSigningData.refundTx = cpfpRefundTx;
25618
+ refundSigningData.directRefundTx = directRefundTx;
25619
+ refundSigningData.directFromCpfpRefundTx = directFromCpfpRefundTx;
25620
+ const cpfpRefundNonceCommitmentProto = refundSigningData.signingNonceCommitment;
25621
+ const directRefundNonceCommitmentProto = refundSigningData.directSigningNonceCommitment;
25622
+ const directFromCpfpRefundNonceCommitmentProto = refundSigningData.directFromCpfpRefundSigningNonceCommitment;
25623
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(
25624
+ refundSigningData.keyDerivation
25124
25625
  );
25125
- refundSigningData.refundTx = refundTx;
25126
- const refundNonceCommitmentProto = refundSigningData.signingNonceCommitment;
25127
25626
  signingJobs.push({
25128
25627
  leafId: leaf.leaf.id,
25129
25628
  refundTxSigningJob: {
25130
- signingPublicKey: await this.config.signer.getPublicKeyFromDerivation(
25131
- refundSigningData.keyDerivation
25132
- ),
25133
- rawTx: refundTx.toBytes(),
25134
- signingNonceCommitment: refundNonceCommitmentProto.commitment
25629
+ signingPublicKey,
25630
+ rawTx: cpfpRefundTx.toBytes(),
25631
+ signingNonceCommitment: cpfpRefundNonceCommitmentProto.commitment
25135
25632
  },
25136
- // TODO: Add direct refund signature
25137
- directRefundTxSigningJob: void 0,
25138
- // TODO: Add direct refund signature
25139
- directFromCpfpRefundTxSigningJob: void 0
25633
+ directRefundTxSigningJob: directRefundTx ? {
25634
+ signingPublicKey,
25635
+ rawTx: directRefundTx.toBytes(),
25636
+ signingNonceCommitment: directRefundNonceCommitmentProto.commitment
25637
+ } : void 0,
25638
+ directFromCpfpRefundTxSigningJob: directFromCpfpRefundTx ? {
25639
+ signingPublicKey,
25640
+ rawTx: directFromCpfpRefundTx.toBytes(),
25641
+ signingNonceCommitment: directFromCpfpRefundNonceCommitmentProto.commitment
25642
+ } : void 0
25140
25643
  });
25141
25644
  }
25142
25645
  return signingJobs;
@@ -25257,13 +25760,17 @@ var TransferService = class extends BaseTransferService {
25257
25760
  const leafDataMap = /* @__PURE__ */ new Map();
25258
25761
  for (const leafKey of leafKeys) {
25259
25762
  const tx = getTxFromRawTxBytes(leafKey.leaf.nodeTx);
25763
+ const directTx = leafKey.leaf.directTx.length > 0 ? getTxFromRawTxBytes(leafKey.leaf.directTx) : void 0;
25260
25764
  leafDataMap.set(leafKey.leaf.id, {
25261
25765
  keyDerivation: leafKey.newKeyDerivation,
25262
25766
  receivingPubkey: await this.config.signer.getPublicKeyFromDerivation(
25263
25767
  leafKey.newKeyDerivation
25264
25768
  ),
25265
25769
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
25770
+ directSigningNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
25771
+ directFromCpfpRefundSigningNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
25266
25772
  tx,
25773
+ directTx,
25267
25774
  vout: leafKey.leaf.vout
25268
25775
  });
25269
25776
  }
@@ -25285,7 +25792,7 @@ var TransferService = class extends BaseTransferService {
25285
25792
  }
25286
25793
  }
25287
25794
  try {
25288
- resp = await sparkClient.claim_transfer_sign_refunds({
25795
+ resp = await sparkClient.claim_transfer_sign_refunds_v2({
25289
25796
  transferId: transfer.id,
25290
25797
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
25291
25798
  signingJobs
@@ -25300,7 +25807,7 @@ var TransferService = class extends BaseTransferService {
25300
25807
  this.config.getCoordinatorAddress()
25301
25808
  );
25302
25809
  try {
25303
- return await sparkClient.finalize_node_signatures({
25810
+ return await sparkClient.finalize_node_signatures_v2({
25304
25811
  intent: 1 /* TRANSFER */,
25305
25812
  nodeSignatures
25306
25813
  });
@@ -25339,109 +25846,134 @@ var TransferService = class extends BaseTransferService {
25339
25846
  throw new Error(`Error querying pending transfers by sender: ${error}`);
25340
25847
  }
25341
25848
  }
25342
- async refreshTimelockNodes(nodes, parentNode) {
25343
- if (nodes.length === 0) {
25344
- throw Error("no nodes to refresh");
25345
- }
25849
+ async refreshTimelockNodesInternal(node, parentNode, useTestUnilateralSequence) {
25346
25850
  const signingJobs = [];
25347
- const newNodeTxs = [];
25348
- for (let i = 0; i < nodes.length; i++) {
25349
- const node = nodes[i];
25350
- if (!node) {
25351
- throw Error("could not get node");
25352
- }
25353
- const nodeTx = getTxFromRawTxBytes(node?.nodeTx);
25354
- const input = nodeTx.getInput(0);
25355
- if (!input) {
25356
- throw Error("Could not fetch tx input");
25357
- }
25358
- const newTx = new import_btc_signer2.Transaction({ version: 3, allowUnknownOutputs: true });
25359
- const originalOutput = nodeTx.getOutput(0);
25360
- if (!originalOutput) {
25361
- throw Error("Could not get original output");
25362
- }
25363
- newTx.addOutput({
25364
- script: originalOutput.script,
25365
- amount: originalOutput.amount
25366
- });
25367
- for (let j = 1; j < nodeTx.outputsLength; j++) {
25368
- const additionalOutput = nodeTx.getOutput(j);
25369
- if (additionalOutput) {
25370
- newTx.addOutput(additionalOutput);
25371
- }
25372
- }
25373
- if (i === 0) {
25374
- const currSequence = input.sequence;
25375
- newTx.addInput({
25376
- ...input,
25377
- sequence: getNextTransactionSequence(currSequence).nextSequence
25378
- });
25379
- } else {
25380
- newTx.addInput({
25381
- ...input,
25382
- sequence: initialSequence(),
25383
- txid: newNodeTxs[i - 1]?.id
25384
- });
25385
- }
25386
- signingJobs.push({
25387
- signingPublicKey: await this.config.signer.getPublicKeyFromDerivation({
25388
- type: "leaf" /* LEAF */,
25389
- path: node.id
25390
- }),
25391
- rawTx: newTx.toBytes(),
25392
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
25851
+ const parentNodeTx = getTxFromRawTxBytes(parentNode.nodeTx);
25852
+ const parentNodeOutput = parentNodeTx.getOutput(0);
25853
+ if (!parentNodeOutput) {
25854
+ throw Error("Could not get parent node output");
25855
+ }
25856
+ const nodeTx = getTxFromRawTxBytes(node.nodeTx);
25857
+ const nodeInput = nodeTx.getInput(0);
25858
+ const nodeOutput = nodeTx.getOutput(0);
25859
+ if (!nodeOutput) {
25860
+ throw Error("Could not get node output");
25861
+ }
25862
+ let directNodeTx;
25863
+ let directNodeInput;
25864
+ if (node.directTx.length > 0) {
25865
+ directNodeTx = getTxFromRawTxBytes(node.directTx);
25866
+ directNodeInput = directNodeTx.getInput(0);
25867
+ }
25868
+ const currSequence = nodeInput.sequence;
25869
+ if (!currSequence) {
25870
+ throw new ValidationError("Invalid node transaction", {
25871
+ field: "sequence",
25872
+ value: nodeInput,
25873
+ expected: "Non-null sequence"
25393
25874
  });
25394
- newNodeTxs[i] = newTx;
25395
25875
  }
25396
- const leaf = nodes[nodes.length - 1];
25397
- if (!leaf?.refundTx) {
25398
- throw Error("leaf does not have refund tx");
25876
+ let { nextSequence, nextDirectSequence } = getNextTransactionSequence(
25877
+ currSequence,
25878
+ true
25879
+ );
25880
+ const output = {
25881
+ script: parentNodeOutput.script,
25882
+ amount: parentNodeOutput.amount
25883
+ };
25884
+ const newNodeInput = {
25885
+ txid: nodeInput.txid,
25886
+ index: nodeInput.index,
25887
+ sequence: useTestUnilateralSequence ? TEST_UNILATERAL_SEQUENCE : nextSequence
25888
+ };
25889
+ const newDirectInput = directNodeTx && directNodeInput ? {
25890
+ txid: directNodeInput.txid,
25891
+ index: directNodeInput.index,
25892
+ sequence: useTestUnilateralSequence ? TEST_UNILATERAL_DIRECT_SEQUENCE : nextDirectSequence
25893
+ } : void 0;
25894
+ const { cpfpNodeTx, directNodeTx: newDirectNodeTx } = createNodeTxs(
25895
+ output,
25896
+ newNodeInput,
25897
+ newDirectInput
25898
+ );
25899
+ const newCpfpNodeOutput = cpfpNodeTx.getOutput(0);
25900
+ if (!newCpfpNodeOutput) {
25901
+ throw Error("Could not get new cpfp node output");
25399
25902
  }
25400
- const refundTx = getTxFromRawTxBytes(leaf?.refundTx);
25401
- const newRefundTx = new import_btc_signer2.Transaction({
25402
- version: 3,
25403
- allowUnknownOutputs: true
25903
+ const newDirectNodeOutput = newDirectNodeTx?.getOutput(0);
25904
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation({
25905
+ type: "leaf" /* LEAF */,
25906
+ path: node.id
25404
25907
  });
25405
- const originalRefundOutput = refundTx.getOutput(0);
25406
- if (!originalRefundOutput) {
25407
- throw Error("Could not get original refund output");
25408
- }
25409
- newRefundTx.addOutput({
25410
- script: originalRefundOutput.script,
25411
- amount: originalRefundOutput.amount
25908
+ signingJobs.push({
25909
+ signingPublicKey,
25910
+ rawTx: cpfpNodeTx.toBytes(),
25911
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
25912
+ type: "node",
25913
+ parentTxOut: parentNodeOutput
25412
25914
  });
25413
- for (let j = 1; j < refundTx.outputsLength; j++) {
25414
- const additionalOutput = refundTx.getOutput(j);
25415
- if (additionalOutput) {
25416
- newRefundTx.addOutput(additionalOutput);
25417
- }
25418
- }
25419
- const refundTxInput = refundTx.getInput(0);
25420
- if (!refundTxInput) {
25421
- throw Error("refund tx doesn't have input");
25915
+ if (newDirectNodeTx) {
25916
+ signingJobs.push({
25917
+ signingPublicKey,
25918
+ rawTx: newDirectNodeTx.toBytes(),
25919
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
25920
+ type: "directNode",
25921
+ parentTxOut: parentNodeOutput
25922
+ });
25422
25923
  }
25423
- if (!newNodeTxs[newNodeTxs.length - 1]) {
25424
- throw Error("Could not get last node tx");
25924
+ const newCpfpRefundOutPoint = {
25925
+ txid: (0, import_utils14.hexToBytes)(getTxId(cpfpNodeTx)),
25926
+ index: 0
25927
+ };
25928
+ let newDirectRefundOutPoint;
25929
+ if (newDirectNodeTx) {
25930
+ newDirectRefundOutPoint = {
25931
+ txid: (0, import_utils14.hexToBytes)(getTxId(newDirectNodeTx)),
25932
+ index: 0
25933
+ };
25425
25934
  }
25426
- newRefundTx.addInput({
25427
- ...refundTxInput,
25428
- sequence: initialSequence(),
25429
- txid: getTxId(newNodeTxs[newNodeTxs.length - 1])
25430
- });
25431
- const refundSigningJob = {
25432
- signingPublicKey: await this.config.signer.getPublicKeyFromDerivation({
25935
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
25936
+ sequence: INITIAL_SEQUENCE,
25937
+ directSequence: INITIAL_DIRECT_SEQUENCE,
25938
+ input: newCpfpRefundOutPoint,
25939
+ directInput: newDirectRefundOutPoint,
25940
+ amountSats: nodeOutput.amount,
25941
+ receivingPubkey: await this.config.signer.getPublicKeyFromDerivation({
25433
25942
  type: "leaf" /* LEAF */,
25434
- path: leaf.id
25943
+ path: node.id
25435
25944
  }),
25436
- rawTx: newRefundTx.toBytes(),
25437
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
25438
- };
25439
- signingJobs.push(refundSigningJob);
25945
+ network: this.config.getNetwork()
25946
+ });
25947
+ signingJobs.push({
25948
+ signingPublicKey,
25949
+ rawTx: cpfpRefundTx.toBytes(),
25950
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
25951
+ type: "cpfp",
25952
+ parentTxOut: newCpfpNodeOutput
25953
+ });
25954
+ if (directRefundTx && newDirectNodeOutput) {
25955
+ signingJobs.push({
25956
+ signingPublicKey,
25957
+ rawTx: directRefundTx.toBytes(),
25958
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
25959
+ type: "direct",
25960
+ parentTxOut: newDirectNodeOutput
25961
+ });
25962
+ }
25963
+ if (directFromCpfpRefundTx && newCpfpNodeOutput) {
25964
+ signingJobs.push({
25965
+ signingPublicKey,
25966
+ rawTx: directFromCpfpRefundTx.toBytes(),
25967
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
25968
+ type: "directFromCpfp",
25969
+ parentTxOut: newCpfpNodeOutput
25970
+ });
25971
+ }
25440
25972
  const sparkClient = await this.connectionManager.createSparkClient(
25441
25973
  this.config.getCoordinatorAddress()
25442
25974
  );
25443
- const response = await sparkClient.refresh_timelock({
25444
- leafId: leaf.id,
25975
+ const response = await sparkClient.refresh_timelock_v2({
25976
+ leafId: node.id,
25445
25977
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
25446
25978
  signingJobs: signingJobs.map(getSigningJobProto)
25447
25979
  });
@@ -25451,45 +25983,27 @@ var TransferService = class extends BaseTransferService {
25451
25983
  );
25452
25984
  }
25453
25985
  let nodeSignatures = [];
25454
- let leafSignature;
25455
- let refundSignature;
25456
- let leafNodeId;
25457
- for (let i = 0; i < response.signingResults.length; i++) {
25458
- const signingResult = response.signingResults[i];
25986
+ let leafCpfpSignature;
25987
+ let leafDirectSignature;
25988
+ let cpfpRefundSignature;
25989
+ let directRefundSignature;
25990
+ let directFromCpfpRefundSignature;
25991
+ for (const [i, signingResult] of response.signingResults.entries()) {
25459
25992
  const signingJob = signingJobs[i];
25460
25993
  if (!signingJob || !signingResult) {
25461
25994
  throw Error("Signing job does not exist");
25462
25995
  }
25463
- if (!signingJob.signingNonceCommitment) {
25464
- throw Error("nonce commitment does not exist");
25465
- }
25466
25996
  const rawTx = getTxFromRawTxBytes(signingJob.rawTx);
25467
- let parentTx;
25468
- let nodeId;
25469
- let vout;
25470
- if (i === nodes.length) {
25471
- nodeId = nodes[i - 1]?.id;
25472
- parentTx = newNodeTxs[i - 1];
25473
- vout = 0;
25474
- } else if (i === 0) {
25475
- nodeId = nodes[i]?.id;
25476
- parentTx = getTxFromRawTxBytes(parentNode.nodeTx);
25477
- vout = nodes[i]?.vout;
25478
- } else {
25479
- nodeId = nodes[i]?.id;
25480
- parentTx = newNodeTxs[i - 1];
25481
- vout = nodes[i]?.vout;
25997
+ const txOut = signingJob.parentTxOut;
25998
+ if (!txOut) {
25999
+ throw Error("Could not get tx out");
25482
26000
  }
25483
- if (!parentTx || !nodeId || vout === void 0) {
25484
- throw Error("Could not parse signing results");
25485
- }
25486
- const txOut = parentTx.getOutput(vout);
25487
26001
  const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
25488
26002
  const userSignature = await this.config.signer.signFrost({
25489
26003
  message: rawTxSighash,
25490
26004
  keyDerivation: {
25491
26005
  type: "leaf" /* LEAF */,
25492
- path: nodeId
26006
+ path: node.id
25493
26007
  },
25494
26008
  publicKey: signingJob.signingPublicKey,
25495
26009
  verifyingKey: signingResult.verifyingKey,
@@ -25508,41 +26022,35 @@ var TransferService = class extends BaseTransferService {
25508
26022
  selfSignature: userSignature,
25509
26023
  adaptorPubKey: new Uint8Array()
25510
26024
  });
25511
- if (i !== nodes.length && i !== nodes.length - 1) {
25512
- nodeSignatures.push({
25513
- nodeId,
25514
- nodeTxSignature: signature,
25515
- refundTxSignature: new Uint8Array(),
25516
- // TODO: Add direct refund signature
25517
- directNodeTxSignature: new Uint8Array(),
25518
- directRefundTxSignature: new Uint8Array(),
25519
- directFromCpfpRefundTxSignature: new Uint8Array()
25520
- });
25521
- } else if (i === nodes.length) {
25522
- refundSignature = signature;
25523
- } else if (i === nodes.length - 1) {
25524
- leafNodeId = nodeId;
25525
- leafSignature = signature;
26025
+ if (signingJob.type === "node") {
26026
+ leafCpfpSignature = signature;
26027
+ } else if (signingJob.type === "directNode") {
26028
+ leafDirectSignature = signature;
26029
+ } else if (signingJob.type === "cpfp") {
26030
+ cpfpRefundSignature = signature;
26031
+ } else if (signingJob.type === "direct") {
26032
+ directRefundSignature = signature;
26033
+ } else if (signingJob.type === "directFromCpfp") {
26034
+ directFromCpfpRefundSignature = signature;
25526
26035
  }
25527
26036
  }
25528
- if (!leafSignature || !refundSignature || !leafNodeId) {
25529
- throw Error("leaf or refund signature does not exist");
25530
- }
25531
26037
  nodeSignatures.push({
25532
- nodeId: leafNodeId,
25533
- nodeTxSignature: leafSignature,
25534
- refundTxSignature: refundSignature,
25535
- // TODO: Add direct refund signature
25536
- directNodeTxSignature: new Uint8Array(),
25537
- directRefundTxSignature: new Uint8Array(),
25538
- directFromCpfpRefundTxSignature: new Uint8Array()
26038
+ nodeId: node.id,
26039
+ nodeTxSignature: leafCpfpSignature || new Uint8Array(),
26040
+ directNodeTxSignature: leafDirectSignature || new Uint8Array(),
26041
+ refundTxSignature: cpfpRefundSignature || new Uint8Array(),
26042
+ directRefundTxSignature: directRefundSignature || new Uint8Array(),
26043
+ directFromCpfpRefundTxSignature: directFromCpfpRefundSignature || new Uint8Array()
25539
26044
  });
25540
- const result = await sparkClient.finalize_node_signatures({
26045
+ const result = await sparkClient.finalize_node_signatures_v2({
25541
26046
  intent: 3 /* REFRESH */,
25542
26047
  nodeSignatures
25543
26048
  });
25544
26049
  return result;
25545
26050
  }
26051
+ async refreshTimelockNodes(node, parentNode) {
26052
+ return await this.refreshTimelockNodesInternal(node, parentNode);
26053
+ }
25546
26054
  async extendTimelock(node) {
25547
26055
  const nodeTx = getTxFromRawTxBytes(node.nodeTx);
25548
26056
  const refundTx = getTxFromRawTxBytes(node.refundTx);
@@ -25551,7 +26059,10 @@ var TransferService = class extends BaseTransferService {
25551
26059
  txid: (0, import_utils14.hexToBytes)(getTxId(nodeTx)),
25552
26060
  index: 0
25553
26061
  };
25554
- const { nextSequence: newNodeSequence } = getNextTransactionSequence(refundSequence);
26062
+ const {
26063
+ nextSequence: newNodeSequence,
26064
+ nextDirectSequence: newDirectNodeSequence
26065
+ } = getNextTransactionSequence(refundSequence);
25555
26066
  const newNodeTx = new import_btc_signer2.Transaction({
25556
26067
  version: 3,
25557
26068
  allowUnknownOutputs: true
@@ -25564,81 +26075,122 @@ var TransferService = class extends BaseTransferService {
25564
26075
  newNodeTx.addOutput({
25565
26076
  script: originalOutput.script,
25566
26077
  amount: originalOutput.amount
25567
- // feeReducedAmount,
25568
26078
  });
25569
26079
  newNodeTx.addOutput(getEphemeralAnchorOutput());
25570
- const newRefundOutPoint = {
26080
+ let newDirectNodeTx;
26081
+ if (node.directTx.length > 0) {
26082
+ newDirectNodeTx = new import_btc_signer2.Transaction({
26083
+ version: 3,
26084
+ allowUnknownOutputs: true
26085
+ });
26086
+ newDirectNodeTx.addInput({
26087
+ ...newNodeOutPoint,
26088
+ sequence: newDirectNodeSequence
26089
+ });
26090
+ newDirectNodeTx.addOutput({
26091
+ script: originalOutput.script,
26092
+ amount: maybeApplyFee(originalOutput.amount)
26093
+ });
26094
+ }
26095
+ const newCpfpRefundOutPoint = {
25571
26096
  txid: (0, import_utils14.hexToBytes)(getTxId(newNodeTx)),
25572
26097
  index: 0
25573
26098
  };
26099
+ let newDirectRefundOutPoint;
26100
+ if (newDirectNodeTx) {
26101
+ newDirectRefundOutPoint = {
26102
+ txid: (0, import_utils14.hexToBytes)(getTxId(newDirectNodeTx)),
26103
+ index: 0
26104
+ };
26105
+ }
25574
26106
  const amountSats = refundTx.getOutput(0).amount;
25575
26107
  if (amountSats === void 0) {
25576
26108
  throw new Error("Amount not found in extendTimelock");
25577
26109
  }
25578
- const signingPubKey = await this.config.signer.getPublicKeyFromDerivation({
26110
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation({
25579
26111
  type: "leaf" /* LEAF */,
25580
26112
  path: node.id
25581
26113
  });
25582
- const newRefundTx = createRefundTx(
25583
- initialSequence(),
25584
- newRefundOutPoint,
26114
+ const {
26115
+ cpfpRefundTx: newCpfpRefundTx,
26116
+ directRefundTx: newDirectRefundTx,
26117
+ directFromCpfpRefundTx: newDirectFromCpfpRefundTx
26118
+ } = createRefundTxs({
26119
+ sequence: INITIAL_SEQUENCE,
26120
+ directSequence: INITIAL_DIRECT_SEQUENCE,
26121
+ input: newCpfpRefundOutPoint,
26122
+ directInput: newDirectRefundOutPoint,
25585
26123
  amountSats,
25586
- // feeReducedRefundAmount,
25587
- signingPubKey,
25588
- this.config.getNetwork()
25589
- );
26124
+ receivingPubkey: signingPublicKey,
26125
+ network: this.config.getNetwork()
26126
+ });
26127
+ if (!newCpfpRefundTx) {
26128
+ throw new ValidationError(
26129
+ "Failed to create refund transactions in extendTimelock"
26130
+ );
26131
+ }
25590
26132
  const nodeSighash = getSigHashFromTx(newNodeTx, 0, nodeTx.getOutput(0));
25591
- const refundSighash = getSigHashFromTx(
25592
- newRefundTx,
26133
+ const directNodeSighash = newDirectNodeTx ? getSigHashFromTx(newDirectNodeTx, 0, nodeTx.getOutput(0)) : void 0;
26134
+ const cpfpRefundSighash = getSigHashFromTx(
26135
+ newCpfpRefundTx,
25593
26136
  0,
25594
26137
  newNodeTx.getOutput(0)
25595
26138
  );
26139
+ const directRefundSighash = newDirectNodeTx && newDirectRefundTx ? getSigHashFromTx(newDirectRefundTx, 0, newDirectNodeTx.getOutput(0)) : void 0;
26140
+ const directFromCpfpRefundSighash = newDirectFromCpfpRefundTx ? getSigHashFromTx(newDirectFromCpfpRefundTx, 0, newNodeTx.getOutput(0)) : void 0;
25596
26141
  const newNodeSigningJob = {
25597
- signingPublicKey: signingPubKey,
26142
+ signingPublicKey,
25598
26143
  rawTx: newNodeTx.toBytes(),
25599
26144
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
25600
26145
  };
25601
- const newRefundSigningJob = {
25602
- signingPublicKey: signingPubKey,
25603
- rawTx: newRefundTx.toBytes(),
26146
+ const newDirectNodeSigningJob = newDirectNodeTx ? {
26147
+ signingPublicKey,
26148
+ rawTx: newDirectNodeTx.toBytes(),
26149
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
26150
+ } : void 0;
26151
+ const newCpfpRefundSigningJob = {
26152
+ signingPublicKey,
26153
+ rawTx: newCpfpRefundTx.toBytes(),
25604
26154
  signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
25605
26155
  };
26156
+ const newDirectRefundSigningJob = newDirectRefundTx ? {
26157
+ signingPublicKey,
26158
+ rawTx: newDirectRefundTx.toBytes(),
26159
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
26160
+ } : void 0;
26161
+ const newDirectFromCpfpRefundSigningJob = newDirectFromCpfpRefundTx ? {
26162
+ signingPublicKey,
26163
+ rawTx: newDirectFromCpfpRefundTx.toBytes(),
26164
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
26165
+ } : void 0;
25606
26166
  const sparkClient = await this.connectionManager.createSparkClient(
25607
26167
  this.config.getCoordinatorAddress()
25608
26168
  );
25609
- const response = await sparkClient.extend_leaf({
26169
+ const response = await sparkClient.extend_leaf_v2({
25610
26170
  leafId: node.id,
25611
26171
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
25612
26172
  nodeTxSigningJob: getSigningJobProto(newNodeSigningJob),
25613
- refundTxSigningJob: getSigningJobProto(newRefundSigningJob)
26173
+ directNodeTxSigningJob: newDirectNodeSigningJob ? getSigningJobProto(newDirectNodeSigningJob) : void 0,
26174
+ refundTxSigningJob: getSigningJobProto(newCpfpRefundSigningJob),
26175
+ directRefundTxSigningJob: newDirectRefundSigningJob ? getSigningJobProto(newDirectRefundSigningJob) : void 0,
26176
+ directFromCpfpRefundTxSigningJob: newDirectFromCpfpRefundSigningJob ? getSigningJobProto(newDirectFromCpfpRefundSigningJob) : void 0
25614
26177
  });
25615
26178
  if (!response.nodeTxSigningResult || !response.refundTxSigningResult) {
25616
26179
  throw new Error("Signing result does not exist");
25617
26180
  }
26181
+ const keyDerivation = {
26182
+ type: "leaf" /* LEAF */,
26183
+ path: node.id
26184
+ };
25618
26185
  const nodeUserSig = await this.config.signer.signFrost({
25619
26186
  message: nodeSighash,
25620
- keyDerivation: {
25621
- type: "leaf" /* LEAF */,
25622
- path: node.id
25623
- },
25624
- publicKey: signingPubKey,
26187
+ keyDerivation,
26188
+ publicKey: signingPublicKey,
25625
26189
  verifyingKey: response.nodeTxSigningResult.verifyingKey,
25626
26190
  selfCommitment: newNodeSigningJob.signingNonceCommitment,
25627
26191
  statechainCommitments: response.nodeTxSigningResult.signingResult?.signingNonceCommitments,
25628
26192
  adaptorPubKey: new Uint8Array()
25629
26193
  });
25630
- const refundUserSig = await this.config.signer.signFrost({
25631
- message: refundSighash,
25632
- keyDerivation: {
25633
- type: "leaf" /* LEAF */,
25634
- path: node.id
25635
- },
25636
- publicKey: signingPubKey,
25637
- verifyingKey: response.refundTxSigningResult.verifyingKey,
25638
- selfCommitment: newRefundSigningJob.signingNonceCommitment,
25639
- statechainCommitments: response.refundTxSigningResult.signingResult?.signingNonceCommitments,
25640
- adaptorPubKey: new Uint8Array()
25641
- });
25642
26194
  const nodeSig = await this.config.signer.aggregateFrost({
25643
26195
  message: nodeSighash,
25644
26196
  statechainSignatures: response.nodeTxSigningResult.signingResult?.signatureShares,
@@ -25646,122 +26198,253 @@ var TransferService = class extends BaseTransferService {
25646
26198
  verifyingKey: response.nodeTxSigningResult.verifyingKey,
25647
26199
  statechainCommitments: response.nodeTxSigningResult.signingResult?.signingNonceCommitments,
25648
26200
  selfCommitment: newNodeSigningJob.signingNonceCommitment,
25649
- publicKey: signingPubKey,
26201
+ publicKey: signingPublicKey,
25650
26202
  selfSignature: nodeUserSig,
25651
26203
  adaptorPubKey: new Uint8Array()
25652
26204
  });
25653
- const refundSig = await this.config.signer.aggregateFrost({
25654
- message: refundSighash,
26205
+ let directNodeSig;
26206
+ if (directNodeSighash && newDirectNodeSigningJob && response.directNodeTxSigningResult) {
26207
+ const directNodeUserSig = await this.config.signer.signFrost({
26208
+ message: directNodeSighash,
26209
+ keyDerivation,
26210
+ publicKey: signingPublicKey,
26211
+ verifyingKey: response.directNodeTxSigningResult.verifyingKey,
26212
+ selfCommitment: newDirectNodeSigningJob.signingNonceCommitment,
26213
+ statechainCommitments: response.directNodeTxSigningResult.signingResult?.signingNonceCommitments,
26214
+ adaptorPubKey: new Uint8Array()
26215
+ });
26216
+ directNodeSig = await this.config.signer.aggregateFrost({
26217
+ message: directNodeSighash,
26218
+ statechainSignatures: response.directNodeTxSigningResult.signingResult?.signatureShares,
26219
+ statechainPublicKeys: response.directNodeTxSigningResult.signingResult?.publicKeys,
26220
+ verifyingKey: response.directNodeTxSigningResult.verifyingKey,
26221
+ statechainCommitments: response.directNodeTxSigningResult.signingResult?.signingNonceCommitments,
26222
+ selfCommitment: newDirectNodeSigningJob.signingNonceCommitment,
26223
+ publicKey: signingPublicKey,
26224
+ selfSignature: directNodeUserSig,
26225
+ adaptorPubKey: new Uint8Array()
26226
+ });
26227
+ }
26228
+ const cpfpRefundUserSig = await this.config.signer.signFrost({
26229
+ message: cpfpRefundSighash,
26230
+ keyDerivation,
26231
+ publicKey: signingPublicKey,
26232
+ verifyingKey: response.refundTxSigningResult.verifyingKey,
26233
+ selfCommitment: newCpfpRefundSigningJob.signingNonceCommitment,
26234
+ statechainCommitments: response.refundTxSigningResult.signingResult?.signingNonceCommitments,
26235
+ adaptorPubKey: new Uint8Array()
26236
+ });
26237
+ const cpfpRefundSig = await this.config.signer.aggregateFrost({
26238
+ message: cpfpRefundSighash,
25655
26239
  statechainSignatures: response.refundTxSigningResult.signingResult?.signatureShares,
25656
26240
  statechainPublicKeys: response.refundTxSigningResult.signingResult?.publicKeys,
25657
26241
  verifyingKey: response.refundTxSigningResult.verifyingKey,
25658
26242
  statechainCommitments: response.refundTxSigningResult.signingResult?.signingNonceCommitments,
25659
- selfCommitment: newRefundSigningJob.signingNonceCommitment,
25660
- publicKey: signingPubKey,
25661
- selfSignature: refundUserSig,
26243
+ selfCommitment: newCpfpRefundSigningJob.signingNonceCommitment,
26244
+ publicKey: signingPublicKey,
26245
+ selfSignature: cpfpRefundUserSig,
25662
26246
  adaptorPubKey: new Uint8Array()
25663
26247
  });
25664
- return await sparkClient.finalize_node_signatures({
26248
+ let directRefundSig;
26249
+ if (directRefundSighash && newDirectRefundSigningJob && response.directRefundTxSigningResult) {
26250
+ const directRefundUserSig = await this.config.signer.signFrost({
26251
+ message: directRefundSighash,
26252
+ keyDerivation,
26253
+ publicKey: signingPublicKey,
26254
+ verifyingKey: response.directRefundTxSigningResult.verifyingKey,
26255
+ selfCommitment: newDirectRefundSigningJob.signingNonceCommitment,
26256
+ statechainCommitments: response.directRefundTxSigningResult.signingResult?.signingNonceCommitments,
26257
+ adaptorPubKey: new Uint8Array()
26258
+ });
26259
+ directRefundSig = await this.config.signer.aggregateFrost({
26260
+ message: directRefundSighash,
26261
+ statechainSignatures: response.directRefundTxSigningResult.signingResult?.signatureShares,
26262
+ statechainPublicKeys: response.directRefundTxSigningResult.signingResult?.publicKeys,
26263
+ verifyingKey: response.directRefundTxSigningResult.verifyingKey,
26264
+ statechainCommitments: response.directRefundTxSigningResult.signingResult?.signingNonceCommitments,
26265
+ selfCommitment: newDirectRefundSigningJob.signingNonceCommitment,
26266
+ publicKey: signingPublicKey,
26267
+ selfSignature: directRefundUserSig,
26268
+ adaptorPubKey: new Uint8Array()
26269
+ });
26270
+ }
26271
+ let directFromCpfpRefundSig;
26272
+ if (directFromCpfpRefundSighash && newDirectFromCpfpRefundSigningJob && response.directFromCpfpRefundTxSigningResult) {
26273
+ const directFromCpfpRefundUserSig = await this.config.signer.signFrost({
26274
+ message: directFromCpfpRefundSighash,
26275
+ keyDerivation,
26276
+ publicKey: signingPublicKey,
26277
+ verifyingKey: response.directFromCpfpRefundTxSigningResult.verifyingKey,
26278
+ selfCommitment: newDirectFromCpfpRefundSigningJob.signingNonceCommitment,
26279
+ statechainCommitments: response.directFromCpfpRefundTxSigningResult.signingResult?.signingNonceCommitments,
26280
+ adaptorPubKey: new Uint8Array()
26281
+ });
26282
+ directFromCpfpRefundSig = await this.config.signer.aggregateFrost({
26283
+ message: directFromCpfpRefundSighash,
26284
+ statechainSignatures: response.directFromCpfpRefundTxSigningResult.signingResult?.signatureShares,
26285
+ statechainPublicKeys: response.directFromCpfpRefundTxSigningResult.signingResult?.publicKeys,
26286
+ verifyingKey: response.directFromCpfpRefundTxSigningResult.verifyingKey,
26287
+ statechainCommitments: response.directFromCpfpRefundTxSigningResult.signingResult?.signingNonceCommitments,
26288
+ selfCommitment: newDirectFromCpfpRefundSigningJob.signingNonceCommitment,
26289
+ publicKey: signingPublicKey,
26290
+ selfSignature: directFromCpfpRefundUserSig,
26291
+ adaptorPubKey: new Uint8Array()
26292
+ });
26293
+ }
26294
+ return await sparkClient.finalize_node_signatures_v2({
25665
26295
  intent: 4 /* EXTEND */,
25666
26296
  nodeSignatures: [
25667
26297
  {
25668
26298
  nodeId: response.leafId,
25669
26299
  nodeTxSignature: nodeSig,
25670
- refundTxSignature: refundSig
26300
+ directNodeTxSignature: directNodeSig,
26301
+ refundTxSignature: cpfpRefundSig,
26302
+ directRefundTxSignature: directRefundSig,
26303
+ directFromCpfpRefundTxSignature: directFromCpfpRefundSig
25671
26304
  }
25672
26305
  ]
25673
26306
  });
25674
26307
  }
25675
- async refreshTimelockRefundTx(node) {
26308
+ async testonly_expireTimeLockNodeTx(node, parentNode) {
26309
+ return await this.refreshTimelockNodesInternal(node, parentNode, true);
26310
+ }
26311
+ async testonly_expireTimeLockRefundtx(node) {
25676
26312
  const nodeTx = getTxFromRawTxBytes(node.nodeTx);
25677
- const refundTx = getTxFromRawTxBytes(node.refundTx);
25678
- const currSequence = refundTx.getInput(0).sequence || 0;
25679
- const { nextSequence } = getNextTransactionSequence(currSequence);
25680
- const signingPubKey = await this.config.signer.getPublicKeyFromDerivation({
26313
+ const directNodeTx = node.directTx.length > 0 ? getTxFromRawTxBytes(node.directTx) : void 0;
26314
+ const cpfpRefundTx = getTxFromRawTxBytes(node.refundTx);
26315
+ const currSequence = cpfpRefundTx.getInput(0).sequence || 0;
26316
+ const currTimelock = getCurrentTimelock(currSequence);
26317
+ if (currTimelock <= 100) {
26318
+ throw new ValidationError("Cannot expire timelock below 100", {
26319
+ field: "currTimelock",
26320
+ value: currTimelock,
26321
+ expected: "Timelock greater than 100"
26322
+ });
26323
+ }
26324
+ const nextSequence = TEST_UNILATERAL_SEQUENCE;
26325
+ const nextDirectSequence = TEST_UNILATERAL_SEQUENCE + DIRECT_TIMELOCK_OFFSET;
26326
+ const nodeOutput = nodeTx.getOutput(0);
26327
+ if (!nodeOutput) {
26328
+ throw Error("Could not get node output");
26329
+ }
26330
+ const keyDerivation = {
25681
26331
  type: "leaf" /* LEAF */,
25682
26332
  path: node.id
25683
- });
25684
- const newRefundTx = new import_btc_signer2.Transaction({
25685
- version: 3,
25686
- allowUnknownOutputs: true
25687
- });
25688
- const originalRefundOutput = refundTx.getOutput(0);
25689
- if (!originalRefundOutput) {
25690
- throw Error("Could not get original refund output");
26333
+ };
26334
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
26335
+ const cpfpRefundOutPoint = {
26336
+ txid: (0, import_utils14.hexToBytes)(getTxId(nodeTx)),
26337
+ index: 0
26338
+ };
26339
+ let directRefundOutPoint;
26340
+ if (directNodeTx) {
26341
+ directRefundOutPoint = {
26342
+ txid: (0, import_utils14.hexToBytes)(getTxId(directNodeTx)),
26343
+ index: 0
26344
+ };
25691
26345
  }
25692
- newRefundTx.addOutput({
25693
- script: originalRefundOutput.script,
25694
- amount: originalRefundOutput.amount
26346
+ const {
26347
+ cpfpRefundTx: newCpfpRefundTx,
26348
+ directRefundTx: newDirectRefundTx,
26349
+ directFromCpfpRefundTx: newDirectFromCpfpRefundTx
26350
+ } = createRefundTxs({
26351
+ sequence: nextSequence,
26352
+ directSequence: nextDirectSequence,
26353
+ input: cpfpRefundOutPoint,
26354
+ directInput: directRefundOutPoint,
26355
+ amountSats: nodeOutput.amount,
26356
+ receivingPubkey: signingPublicKey,
26357
+ network: this.config.getNetwork()
25695
26358
  });
25696
- for (let j = 1; j < refundTx.outputsLength; j++) {
25697
- const additionalOutput = refundTx.getOutput(j);
25698
- if (additionalOutput) {
25699
- newRefundTx.addOutput(additionalOutput);
25700
- }
26359
+ const signingJobs = [];
26360
+ signingJobs.push({
26361
+ signingPublicKey,
26362
+ rawTx: newCpfpRefundTx.toBytes(),
26363
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
26364
+ type: "cpfp",
26365
+ parentTxOut: nodeOutput
26366
+ });
26367
+ const directNodeTxOut = directNodeTx?.getOutput(0);
26368
+ if (newDirectRefundTx && directNodeTxOut) {
26369
+ signingJobs.push({
26370
+ signingPublicKey,
26371
+ rawTx: newDirectRefundTx.toBytes(),
26372
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
26373
+ type: "direct",
26374
+ parentTxOut: directNodeTxOut
26375
+ });
25701
26376
  }
25702
- const refundTxInput = refundTx.getInput(0);
25703
- if (!refundTxInput) {
25704
- throw Error("refund tx doesn't have input");
26377
+ if (newDirectFromCpfpRefundTx) {
26378
+ signingJobs.push({
26379
+ signingPublicKey,
26380
+ rawTx: newDirectFromCpfpRefundTx.toBytes(),
26381
+ signingNonceCommitment: await this.config.signer.getRandomSigningCommitment(),
26382
+ type: "directFromCpfp",
26383
+ parentTxOut: nodeOutput
26384
+ });
25705
26385
  }
25706
- newRefundTx.addInput({
25707
- ...refundTxInput,
25708
- sequence: nextSequence
25709
- });
25710
- const refundSigningJob = {
25711
- signingPublicKey: signingPubKey,
25712
- rawTx: newRefundTx.toBytes(),
25713
- signingNonceCommitment: await this.config.signer.getRandomSigningCommitment()
25714
- };
25715
26386
  const sparkClient = await this.connectionManager.createSparkClient(
25716
26387
  this.config.getCoordinatorAddress()
25717
26388
  );
25718
- const response = await sparkClient.refresh_timelock({
26389
+ const response = await sparkClient.refresh_timelock_v2({
25719
26390
  leafId: node.id,
25720
26391
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
25721
- signingJobs: [getSigningJobProto(refundSigningJob)]
26392
+ signingJobs: signingJobs.map(getSigningJobProto)
25722
26393
  });
25723
- if (response.signingResults.length !== 1) {
26394
+ if (response.signingResults.length !== signingJobs.length) {
25724
26395
  throw Error(
25725
- `Expected 1 signing result, got ${response.signingResults.length}`
26396
+ `Expected ${signingJobs.length} signing results, got ${response.signingResults.length}`
25726
26397
  );
25727
26398
  }
25728
- const signingResult = response.signingResults[0];
25729
- if (!signingResult || !refundSigningJob.signingNonceCommitment) {
25730
- throw Error("Signing result or nonce commitment does not exist");
26399
+ let cpfpRefundSignature;
26400
+ let directRefundSignature;
26401
+ let directFromCpfpRefundSignature;
26402
+ for (const [i, signingJob] of signingJobs.entries()) {
26403
+ const signingResult = response.signingResults[i];
26404
+ if (!signingResult) {
26405
+ throw Error("Signing result does not exist");
26406
+ }
26407
+ const rawTx = getTxFromRawTxBytes(signingJob.rawTx);
26408
+ const txOut = signingJob.parentTxOut;
26409
+ const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
26410
+ const userSignature = await this.config.signer.signFrost({
26411
+ message: rawTxSighash,
26412
+ keyDerivation,
26413
+ publicKey: signingPublicKey,
26414
+ verifyingKey: signingResult.verifyingKey,
26415
+ selfCommitment: signingJob.signingNonceCommitment,
26416
+ statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
26417
+ adaptorPubKey: new Uint8Array()
26418
+ });
26419
+ const signature = await this.config.signer.aggregateFrost({
26420
+ message: rawTxSighash,
26421
+ statechainSignatures: signingResult.signingResult?.signatureShares,
26422
+ statechainPublicKeys: signingResult.signingResult?.publicKeys,
26423
+ verifyingKey: signingResult.verifyingKey,
26424
+ statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
26425
+ selfCommitment: signingJob.signingNonceCommitment,
26426
+ publicKey: signingPublicKey,
26427
+ selfSignature: userSignature,
26428
+ adaptorPubKey: new Uint8Array()
26429
+ });
26430
+ if (signingJob.type === "cpfp") {
26431
+ cpfpRefundSignature = signature;
26432
+ } else if (signingJob.type === "direct") {
26433
+ directRefundSignature = signature;
26434
+ } else if (signingJob.type === "directFromCpfp") {
26435
+ directFromCpfpRefundSignature = signature;
26436
+ }
25731
26437
  }
25732
- const rawTx = getTxFromRawTxBytes(refundSigningJob.rawTx);
25733
- const txOut = nodeTx.getOutput(0);
25734
- const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
25735
- const userSignature = await this.config.signer.signFrost({
25736
- message: rawTxSighash,
25737
- keyDerivation: {
25738
- type: "leaf" /* LEAF */,
25739
- path: node.id
25740
- },
25741
- publicKey: signingPubKey,
25742
- verifyingKey: signingResult.verifyingKey,
25743
- selfCommitment: refundSigningJob.signingNonceCommitment,
25744
- statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
25745
- adaptorPubKey: new Uint8Array()
25746
- });
25747
- const signature = await this.config.signer.aggregateFrost({
25748
- message: rawTxSighash,
25749
- statechainSignatures: signingResult.signingResult?.signatureShares,
25750
- statechainPublicKeys: signingResult.signingResult?.publicKeys,
25751
- verifyingKey: signingResult.verifyingKey,
25752
- statechainCommitments: signingResult.signingResult?.signingNonceCommitments,
25753
- selfCommitment: refundSigningJob.signingNonceCommitment,
25754
- publicKey: signingPubKey,
25755
- selfSignature: userSignature,
25756
- adaptorPubKey: new Uint8Array()
25757
- });
25758
- const result = await sparkClient.finalize_node_signatures({
26438
+ const result = await sparkClient.finalize_node_signatures_v2({
25759
26439
  intent: 3 /* REFRESH */,
25760
26440
  nodeSignatures: [
25761
26441
  {
25762
26442
  nodeId: node.id,
25763
26443
  nodeTxSignature: new Uint8Array(),
25764
- refundTxSignature: signature
26444
+ directNodeTxSignature: new Uint8Array(),
26445
+ refundTxSignature: cpfpRefundSignature,
26446
+ directRefundTxSignature: directRefundSignature,
26447
+ directFromCpfpRefundTxSignature: directFromCpfpRefundSignature
25765
26448
  }
25766
26449
  ]
25767
26450
  });
@@ -25780,7 +26463,12 @@ var CoopExitService = class extends BaseTransferService {
25780
26463
  connectorOutputs,
25781
26464
  receiverPubKey
25782
26465
  }) {
25783
- const { transfer, signaturesMap } = await this.signCoopExitRefunds(
26466
+ const {
26467
+ transfer,
26468
+ signaturesMap,
26469
+ directSignaturesMap,
26470
+ directFromCpfpSignaturesMap
26471
+ } = await this.signCoopExitRefunds(
25784
26472
  leaves,
25785
26473
  exitTxId,
25786
26474
  connectorOutputs,
@@ -25789,34 +26477,81 @@ var CoopExitService = class extends BaseTransferService {
25789
26477
  const transferTweak = await this.deliverTransferPackage(
25790
26478
  transfer,
25791
26479
  leaves,
25792
- signaturesMap
26480
+ signaturesMap,
26481
+ directSignaturesMap,
26482
+ directFromCpfpSignaturesMap
25793
26483
  );
25794
- return { transfer: transferTweak, signaturesMap };
25795
- }
25796
- createConnectorRefundTransaction(sequence, nodeOutPoint, connectorOutput, amountSats, receiverPubKey) {
25797
- const refundTx = new import_btc_signer3.Transaction();
25798
- if (!nodeOutPoint.txid || nodeOutPoint.index === void 0) {
25799
- throw new ValidationError("Invalid node outpoint", {
25800
- field: "nodeOutPoint",
25801
- value: { txid: nodeOutPoint.txid, index: nodeOutPoint.index },
26484
+ return {
26485
+ transfer: transferTweak,
26486
+ signaturesMap,
26487
+ directSignaturesMap,
26488
+ directFromCpfpSignaturesMap
26489
+ };
26490
+ }
26491
+ createConnectorRefundTransactions(sequence, directSequence, cpfpNodeOutPoint, directNodeOutPoint, connectorOutput, amountSats, receiverPubKey) {
26492
+ const cpfpRefundTx = new import_btc_signer3.Transaction();
26493
+ if (!cpfpNodeOutPoint.txid || cpfpNodeOutPoint.index === void 0) {
26494
+ throw new ValidationError("Invalid CPFP node outpoint", {
26495
+ field: "cpfpNodeOutPoint",
26496
+ value: { txid: cpfpNodeOutPoint.txid, index: cpfpNodeOutPoint.index },
25802
26497
  expected: "Both txid and index must be defined"
25803
26498
  });
25804
26499
  }
25805
- refundTx.addInput({
25806
- txid: nodeOutPoint.txid,
25807
- index: nodeOutPoint.index,
26500
+ cpfpRefundTx.addInput({
26501
+ txid: cpfpNodeOutPoint.txid,
26502
+ index: cpfpNodeOutPoint.index,
25808
26503
  sequence
25809
26504
  });
25810
- refundTx.addInput(connectorOutput);
26505
+ cpfpRefundTx.addInput(connectorOutput);
25811
26506
  const receiverScript = getP2TRScriptFromPublicKey(
25812
26507
  receiverPubKey,
25813
26508
  this.config.getNetwork()
25814
26509
  );
25815
- refundTx.addOutput({
26510
+ cpfpRefundTx.addOutput({
25816
26511
  script: receiverScript,
25817
26512
  amount: amountSats
25818
26513
  });
25819
- return refundTx;
26514
+ let directRefundTx;
26515
+ let directFromCpfpRefundTx;
26516
+ if (directNodeOutPoint) {
26517
+ if (!directNodeOutPoint.txid || directNodeOutPoint.index === void 0) {
26518
+ throw new ValidationError("Invalid direct node outpoint", {
26519
+ field: "directNodeOutPoint",
26520
+ value: {
26521
+ txid: directNodeOutPoint.txid,
26522
+ index: directNodeOutPoint.index
26523
+ },
26524
+ expected: "Both txid and index must be defined"
26525
+ });
26526
+ }
26527
+ directRefundTx = new import_btc_signer3.Transaction();
26528
+ directRefundTx.addInput({
26529
+ txid: directNodeOutPoint.txid,
26530
+ index: directNodeOutPoint.index,
26531
+ sequence: directSequence
26532
+ });
26533
+ directRefundTx.addInput(connectorOutput);
26534
+ directRefundTx.addOutput({
26535
+ script: receiverScript,
26536
+ amount: maybeApplyFee(amountSats)
26537
+ });
26538
+ directFromCpfpRefundTx = new import_btc_signer3.Transaction();
26539
+ directFromCpfpRefundTx.addInput({
26540
+ txid: cpfpNodeOutPoint.txid,
26541
+ index: cpfpNodeOutPoint.index,
26542
+ sequence: directSequence
26543
+ });
26544
+ directFromCpfpRefundTx.addInput(connectorOutput);
26545
+ directFromCpfpRefundTx.addOutput({
26546
+ script: receiverScript,
26547
+ amount: maybeApplyFee(amountSats)
26548
+ });
26549
+ }
26550
+ return {
26551
+ cpfpRefundTx,
26552
+ directRefundTx,
26553
+ directFromCpfpRefundTx
26554
+ };
25820
26555
  }
25821
26556
  async signCoopExitRefunds(leaves, exitTxId, connectorOutputs, receiverPubKey) {
25822
26557
  if (leaves.length !== connectorOutputs.length) {
@@ -25852,39 +26587,67 @@ var CoopExitService = class extends BaseTransferService {
25852
26587
  });
25853
26588
  }
25854
26589
  const currentRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
25855
- const { nextSequence } = getNextTransactionSequence(
25856
- currentRefundTx.getInput(0).sequence
25857
- );
25858
- const refundTx = this.createConnectorRefundTransaction(
26590
+ const sequence = currentRefundTx.getInput(0).sequence;
26591
+ if (!sequence) {
26592
+ throw new ValidationError("Invalid refund transaction", {
26593
+ field: "sequence",
26594
+ value: currentRefundTx.getInput(0),
26595
+ expected: "Non-null sequence"
26596
+ });
26597
+ }
26598
+ const { nextSequence, nextDirectSequence } = getNextTransactionSequence(sequence);
26599
+ let currentDirectRefundTx;
26600
+ if (leaf.leaf.directRefundTx.length > 0) {
26601
+ currentDirectRefundTx = getTxFromRawTxBytes(leaf.leaf.directRefundTx);
26602
+ }
26603
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = this.createConnectorRefundTransactions(
25859
26604
  nextSequence,
26605
+ nextDirectSequence,
25860
26606
  currentRefundTx.getInput(0),
26607
+ currentDirectRefundTx?.getInput(0),
25861
26608
  connectorOutput,
25862
26609
  BigInt(leaf.leaf.value),
25863
26610
  receiverPubKey
25864
26611
  );
25865
26612
  const signingNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26613
+ const directSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26614
+ const directFromCpfpSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26615
+ const signingPublicKey = await this.config.signer.getPublicKeyFromDerivation(leaf.keyDerivation);
25866
26616
  const signingJob = {
25867
26617
  leafId: leaf.leaf.id,
25868
26618
  refundTxSigningJob: {
25869
26619
  signingPublicKey: await this.config.signer.getPublicKeyFromDerivation(
25870
26620
  leaf.keyDerivation
25871
26621
  ),
25872
- rawTx: refundTx.toBytes(),
26622
+ rawTx: cpfpRefundTx.toBytes(),
25873
26623
  signingNonceCommitment: signingNonceCommitment.commitment
25874
26624
  },
25875
- // TODO: Add direct refund signature
25876
- directRefundTxSigningJob: void 0,
25877
- directFromCpfpRefundTxSigningJob: void 0
26625
+ directRefundTxSigningJob: directRefundTx ? {
26626
+ signingPublicKey,
26627
+ rawTx: directRefundTx.toBytes(),
26628
+ signingNonceCommitment: directSigningNonceCommitment.commitment
26629
+ } : void 0,
26630
+ directFromCpfpRefundTxSigningJob: directFromCpfpRefundTx ? {
26631
+ signingPublicKey,
26632
+ rawTx: directFromCpfpRefundTx.toBytes(),
26633
+ signingNonceCommitment: directFromCpfpSigningNonceCommitment.commitment
26634
+ } : void 0
25878
26635
  };
25879
26636
  signingJobs.push(signingJob);
25880
26637
  const tx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
26638
+ const directTx = leaf.leaf.directTx.length > 0 ? getTxFromRawTxBytes(leaf.leaf.directTx) : void 0;
25881
26639
  leafDataMap.set(leaf.leaf.id, {
25882
26640
  keyDerivation: leaf.keyDerivation,
25883
- refundTx,
26641
+ receivingPubkey: receiverPubKey,
25884
26642
  signingNonceCommitment,
26643
+ directSigningNonceCommitment,
25885
26644
  tx,
25886
- vout: leaf.leaf.vout,
25887
- receivingPubkey: receiverPubKey
26645
+ directTx,
26646
+ refundTx: cpfpRefundTx,
26647
+ directRefundTx,
26648
+ directFromCpfpRefundTx,
26649
+ directFromCpfpRefundSigningNonceCommitment: directFromCpfpSigningNonceCommitment,
26650
+ vout: leaf.leaf.vout
25888
26651
  });
25889
26652
  }
25890
26653
  const sparkClient = await this.connectionManager.createSparkClient(
@@ -25892,7 +26655,7 @@ var CoopExitService = class extends BaseTransferService {
25892
26655
  );
25893
26656
  let response;
25894
26657
  try {
25895
- response = await sparkClient.cooperative_exit({
26658
+ response = await sparkClient.cooperative_exit_v2({
25896
26659
  transfer: {
25897
26660
  transferId: (0, import_uuidv73.uuidv7)(),
25898
26661
  leavesToSend: signingJobs,
@@ -25926,10 +26689,25 @@ var CoopExitService = class extends BaseTransferService {
25926
26689
  response.signingResults
25927
26690
  );
25928
26691
  const signaturesMap = /* @__PURE__ */ new Map();
26692
+ const directSignaturesMap = /* @__PURE__ */ new Map();
26693
+ const directFromCpfpSignaturesMap = /* @__PURE__ */ new Map();
25929
26694
  for (const signature of signatures) {
25930
26695
  signaturesMap.set(signature.nodeId, signature.refundTxSignature);
26696
+ directSignaturesMap.set(
26697
+ signature.nodeId,
26698
+ signature.directRefundTxSignature
26699
+ );
26700
+ directFromCpfpSignaturesMap.set(
26701
+ signature.nodeId,
26702
+ signature.directFromCpfpRefundTxSignature
26703
+ );
25931
26704
  }
25932
- return { transfer: response.transfer, signaturesMap };
26705
+ return {
26706
+ transfer: response.transfer,
26707
+ signaturesMap,
26708
+ directSignaturesMap,
26709
+ directFromCpfpSignaturesMap
26710
+ };
25933
26711
  }
25934
26712
  };
25935
26713
 
@@ -25938,10 +26716,8 @@ init_buffer();
25938
26716
  var import_secp256k110 = require("@noble/curves/secp256k1");
25939
26717
  var import_sha29 = require("@noble/hashes/sha2");
25940
26718
  var import_utils15 = require("@noble/hashes/utils");
25941
- var btc5 = __toESM(require("@scure/btc-signer"), 1);
25942
26719
  var import_btc_signer4 = require("@scure/btc-signer");
25943
26720
  var import_utils16 = require("@scure/btc-signer/utils");
25944
- var INITIAL_TIME_LOCK2 = 2e3;
25945
26721
  var DepositService = class {
25946
26722
  config;
25947
26723
  connectionManager;
@@ -26059,7 +26835,6 @@ var DepositService = class {
26059
26835
  depositTx,
26060
26836
  vout
26061
26837
  }) {
26062
- const rootTx = new import_btc_signer4.Transaction({ version: 3 });
26063
26838
  const output = depositTx.getOutput(vout);
26064
26839
  if (!output) {
26065
26840
  throw new ValidationError("Invalid deposit transaction output", {
@@ -26077,39 +26852,345 @@ var DepositService = class {
26077
26852
  expected: "Output with script and amount"
26078
26853
  });
26079
26854
  }
26080
- let outputAmount = amount;
26081
- rootTx.addInput({
26082
- txid: getTxId(depositTx),
26855
+ const depositOutPoint = {
26856
+ txid: (0, import_utils15.hexToBytes)(getTxId(depositTx)),
26083
26857
  index: vout
26084
- });
26085
- rootTx.addOutput({
26858
+ };
26859
+ const depositTxOut = {
26086
26860
  script,
26087
- amount: outputAmount
26861
+ amount
26862
+ };
26863
+ const [cpfpRootTx, directRootTx] = createRootTx(
26864
+ depositOutPoint,
26865
+ depositTxOut
26866
+ );
26867
+ const cpfpRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26868
+ const directRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26869
+ const cpfpRootTxSighash = getSigHashFromTx(cpfpRootTx, 0, output);
26870
+ const directRootTxSighash = getSigHashFromTx(directRootTx, 0, output);
26871
+ const signingPubKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
26872
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
26873
+ sequence: INITIAL_SEQUENCE,
26874
+ directSequence: INITIAL_DIRECT_SEQUENCE,
26875
+ input: { txid: (0, import_utils15.hexToBytes)(getTxId(cpfpRootTx)), index: 0 },
26876
+ directInput: { txid: (0, import_utils15.hexToBytes)(getTxId(directRootTx)), index: 0 },
26877
+ amountSats: amount,
26878
+ receivingPubkey: signingPubKey,
26879
+ network: this.config.getNetwork()
26088
26880
  });
26089
- rootTx.addOutput(getEphemeralAnchorOutput());
26090
- const rootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26091
- const rootTxSighash = getSigHashFromTx(rootTx, 0, output);
26092
- const refundTx = new import_btc_signer4.Transaction({ version: 3 });
26093
- const sequence = 1 << 30 | INITIAL_TIME_LOCK2;
26094
- refundTx.addInput({
26095
- txid: getTxId(rootTx),
26096
- index: 0,
26097
- sequence
26881
+ const cpfpRefundNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26882
+ const directRefundNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26883
+ const directFromCpfpRefundNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26884
+ const cpfpRefundTxSighash = getSigHashFromTx(
26885
+ cpfpRefundTx,
26886
+ 0,
26887
+ cpfpRootTx.getOutput(0)
26888
+ );
26889
+ if (!directRefundTx || !directFromCpfpRefundTx) {
26890
+ throw new ValidationError(
26891
+ "Expected direct refund transactions for tree creation",
26892
+ {
26893
+ field: "directRefundTx",
26894
+ value: directRefundTx
26895
+ }
26896
+ );
26897
+ }
26898
+ const directRefundTxSighash = getSigHashFromTx(
26899
+ directRefundTx,
26900
+ 0,
26901
+ directRootTx.getOutput(0)
26902
+ );
26903
+ const directFromCpfpRefundTxSighash = getSigHashFromTx(
26904
+ directFromCpfpRefundTx,
26905
+ 0,
26906
+ cpfpRootTx.getOutput(0)
26907
+ );
26908
+ const sparkClient = await this.connectionManager.createSparkClient(
26909
+ this.config.getCoordinatorAddress()
26910
+ );
26911
+ let treeResp;
26912
+ try {
26913
+ treeResp = await sparkClient.start_deposit_tree_creation({
26914
+ identityPublicKey: await this.config.signer.getIdentityPublicKey(),
26915
+ onChainUtxo: {
26916
+ vout,
26917
+ rawTx: depositTx.toBytes(true),
26918
+ network: this.config.getNetworkProto()
26919
+ },
26920
+ rootTxSigningJob: {
26921
+ rawTx: cpfpRootTx.toBytes(),
26922
+ signingPublicKey: signingPubKey,
26923
+ signingNonceCommitment: cpfpRootNonceCommitment.commitment
26924
+ },
26925
+ refundTxSigningJob: {
26926
+ rawTx: cpfpRefundTx.toBytes(),
26927
+ signingPublicKey: signingPubKey,
26928
+ signingNonceCommitment: cpfpRefundNonceCommitment.commitment
26929
+ },
26930
+ directRootTxSigningJob: {
26931
+ rawTx: directRootTx.toBytes(),
26932
+ signingPublicKey: signingPubKey,
26933
+ signingNonceCommitment: directRootNonceCommitment.commitment
26934
+ },
26935
+ directRefundTxSigningJob: {
26936
+ rawTx: directRefundTx.toBytes(),
26937
+ signingPublicKey: signingPubKey,
26938
+ signingNonceCommitment: directRefundNonceCommitment.commitment
26939
+ },
26940
+ directFromCpfpRefundTxSigningJob: {
26941
+ rawTx: directFromCpfpRefundTx.toBytes(),
26942
+ signingPublicKey: signingPubKey,
26943
+ signingNonceCommitment: directFromCpfpRefundNonceCommitment.commitment
26944
+ }
26945
+ });
26946
+ } catch (error) {
26947
+ throw new NetworkError(
26948
+ "Failed to start deposit tree creation",
26949
+ {
26950
+ operation: "start_deposit_tree_creation",
26951
+ errorCount: 1,
26952
+ errors: error instanceof Error ? error.message : String(error)
26953
+ },
26954
+ error
26955
+ );
26956
+ }
26957
+ if (!treeResp.rootNodeSignatureShares?.verifyingKey) {
26958
+ throw new ValidationError("No verifying key found in tree response", {
26959
+ field: "verifyingKey",
26960
+ value: treeResp.rootNodeSignatureShares,
26961
+ expected: "Non-null verifying key"
26962
+ });
26963
+ }
26964
+ if (!treeResp.rootNodeSignatureShares.nodeTxSigningResult?.signingNonceCommitments) {
26965
+ throw new ValidationError(
26966
+ "No signing nonce commitments found in tree response",
26967
+ {
26968
+ field: "nodeTxSigningResult.signingNonceCommitments",
26969
+ value: treeResp.rootNodeSignatureShares.nodeTxSigningResult,
26970
+ expected: "Non-null signing nonce commitments"
26971
+ }
26972
+ );
26973
+ }
26974
+ if (!treeResp.rootNodeSignatureShares.refundTxSigningResult?.signingNonceCommitments) {
26975
+ throw new ValidationError(
26976
+ "No signing nonce commitments found in tree response",
26977
+ {
26978
+ field: "refundTxSigningResult.signingNonceCommitments"
26979
+ }
26980
+ );
26981
+ }
26982
+ if (!treeResp.rootNodeSignatureShares.directNodeTxSigningResult?.signingNonceCommitments) {
26983
+ throw new ValidationError(
26984
+ "No direct node signing nonce commitments found in tree response",
26985
+ {
26986
+ field: "directNodeTxSigningResult.signingNonceCommitments"
26987
+ }
26988
+ );
26989
+ }
26990
+ if (!treeResp.rootNodeSignatureShares.directRefundTxSigningResult?.signingNonceCommitments) {
26991
+ throw new ValidationError(
26992
+ "No direct refund signing nonce commitments found in tree response",
26993
+ {
26994
+ field: "directRefundTxSigningResult.signingNonceCommitments"
26995
+ }
26996
+ );
26997
+ }
26998
+ if (!treeResp.rootNodeSignatureShares.directFromCpfpRefundTxSigningResult?.signingNonceCommitments) {
26999
+ throw new ValidationError(
27000
+ "No direct from CPFP refund signing nonce commitments found in tree response",
27001
+ {
27002
+ field: "directFromCpfpRefundTxSigningResult.signingNonceCommitments"
27003
+ }
27004
+ );
27005
+ }
27006
+ if (!(0, import_utils16.equalBytes)(treeResp.rootNodeSignatureShares.verifyingKey, verifyingKey)) {
27007
+ throw new ValidationError("Verifying key mismatch", {
27008
+ field: "verifyingKey",
27009
+ value: treeResp.rootNodeSignatureShares.verifyingKey,
27010
+ expected: verifyingKey
27011
+ });
27012
+ }
27013
+ const cpfpRootSignature = await this.config.signer.signFrost({
27014
+ message: cpfpRootTxSighash,
27015
+ publicKey: signingPubKey,
27016
+ keyDerivation,
27017
+ verifyingKey,
27018
+ selfCommitment: cpfpRootNonceCommitment,
27019
+ statechainCommitments: treeResp.rootNodeSignatureShares.nodeTxSigningResult.signingNonceCommitments,
27020
+ adaptorPubKey: new Uint8Array()
26098
27021
  });
27022
+ const directRootSignature = await this.config.signer.signFrost({
27023
+ message: directRootTxSighash,
27024
+ publicKey: signingPubKey,
27025
+ keyDerivation,
27026
+ verifyingKey,
27027
+ selfCommitment: directRootNonceCommitment,
27028
+ statechainCommitments: treeResp.rootNodeSignatureShares.directNodeTxSigningResult.signingNonceCommitments,
27029
+ adaptorPubKey: new Uint8Array()
27030
+ });
27031
+ const cpfpRefundSignature = await this.config.signer.signFrost({
27032
+ message: cpfpRefundTxSighash,
27033
+ publicKey: signingPubKey,
27034
+ keyDerivation,
27035
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
27036
+ selfCommitment: cpfpRefundNonceCommitment,
27037
+ statechainCommitments: treeResp.rootNodeSignatureShares.refundTxSigningResult.signingNonceCommitments,
27038
+ adaptorPubKey: new Uint8Array()
27039
+ });
27040
+ const directRefundSignature = await this.config.signer.signFrost({
27041
+ message: directRefundTxSighash,
27042
+ publicKey: signingPubKey,
27043
+ keyDerivation,
27044
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
27045
+ selfCommitment: directRefundNonceCommitment,
27046
+ statechainCommitments: treeResp.rootNodeSignatureShares.directRefundTxSigningResult.signingNonceCommitments,
27047
+ adaptorPubKey: new Uint8Array()
27048
+ });
27049
+ const directFromCpfpRefundSignature = await this.config.signer.signFrost({
27050
+ message: directFromCpfpRefundTxSighash,
27051
+ publicKey: signingPubKey,
27052
+ keyDerivation,
27053
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
27054
+ selfCommitment: directFromCpfpRefundNonceCommitment,
27055
+ statechainCommitments: treeResp.rootNodeSignatureShares.directFromCpfpRefundTxSigningResult.signingNonceCommitments,
27056
+ adaptorPubKey: new Uint8Array()
27057
+ });
27058
+ const cpfpRootAggregate = await this.config.signer.aggregateFrost({
27059
+ message: cpfpRootTxSighash,
27060
+ statechainSignatures: treeResp.rootNodeSignatureShares.nodeTxSigningResult.signatureShares,
27061
+ statechainPublicKeys: treeResp.rootNodeSignatureShares.nodeTxSigningResult.publicKeys,
27062
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
27063
+ statechainCommitments: treeResp.rootNodeSignatureShares.nodeTxSigningResult.signingNonceCommitments,
27064
+ selfCommitment: cpfpRootNonceCommitment,
27065
+ publicKey: signingPubKey,
27066
+ selfSignature: cpfpRootSignature,
27067
+ adaptorPubKey: new Uint8Array()
27068
+ });
27069
+ const directRootAggregate = await this.config.signer.aggregateFrost({
27070
+ message: directRootTxSighash,
27071
+ statechainSignatures: treeResp.rootNodeSignatureShares.directNodeTxSigningResult.signatureShares,
27072
+ statechainPublicKeys: treeResp.rootNodeSignatureShares.directNodeTxSigningResult.publicKeys,
27073
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
27074
+ statechainCommitments: treeResp.rootNodeSignatureShares.directNodeTxSigningResult.signingNonceCommitments,
27075
+ selfCommitment: directRootNonceCommitment,
27076
+ publicKey: signingPubKey,
27077
+ selfSignature: directRootSignature,
27078
+ adaptorPubKey: new Uint8Array()
27079
+ });
27080
+ const cpfpRefundAggregate = await this.config.signer.aggregateFrost({
27081
+ message: cpfpRefundTxSighash,
27082
+ statechainSignatures: treeResp.rootNodeSignatureShares.refundTxSigningResult.signatureShares,
27083
+ statechainPublicKeys: treeResp.rootNodeSignatureShares.refundTxSigningResult.publicKeys,
27084
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
27085
+ statechainCommitments: treeResp.rootNodeSignatureShares.refundTxSigningResult.signingNonceCommitments,
27086
+ selfCommitment: cpfpRefundNonceCommitment,
27087
+ publicKey: signingPubKey,
27088
+ selfSignature: cpfpRefundSignature,
27089
+ adaptorPubKey: new Uint8Array()
27090
+ });
27091
+ const directRefundAggregate = await this.config.signer.aggregateFrost({
27092
+ message: directRefundTxSighash,
27093
+ statechainSignatures: treeResp.rootNodeSignatureShares.directRefundTxSigningResult.signatureShares,
27094
+ statechainPublicKeys: treeResp.rootNodeSignatureShares.directRefundTxSigningResult.publicKeys,
27095
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
27096
+ statechainCommitments: treeResp.rootNodeSignatureShares.directRefundTxSigningResult.signingNonceCommitments,
27097
+ selfCommitment: directRefundNonceCommitment,
27098
+ publicKey: signingPubKey,
27099
+ selfSignature: directRefundSignature,
27100
+ adaptorPubKey: new Uint8Array()
27101
+ });
27102
+ const directFromCpfpRefundAggregate = await this.config.signer.aggregateFrost({
27103
+ message: directFromCpfpRefundTxSighash,
27104
+ statechainSignatures: treeResp.rootNodeSignatureShares.directFromCpfpRefundTxSigningResult.signatureShares,
27105
+ statechainPublicKeys: treeResp.rootNodeSignatureShares.directFromCpfpRefundTxSigningResult.publicKeys,
27106
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
27107
+ statechainCommitments: treeResp.rootNodeSignatureShares.directFromCpfpRefundTxSigningResult.signingNonceCommitments,
27108
+ selfCommitment: directFromCpfpRefundNonceCommitment,
27109
+ publicKey: signingPubKey,
27110
+ selfSignature: directFromCpfpRefundSignature,
27111
+ adaptorPubKey: new Uint8Array()
27112
+ });
27113
+ let finalizeResp;
27114
+ try {
27115
+ finalizeResp = await sparkClient.finalize_node_signatures_v2({
27116
+ intent: 0 /* CREATION */,
27117
+ nodeSignatures: [
27118
+ {
27119
+ nodeId: treeResp.rootNodeSignatureShares.nodeId,
27120
+ nodeTxSignature: cpfpRootAggregate,
27121
+ refundTxSignature: cpfpRefundAggregate,
27122
+ directNodeTxSignature: directRootAggregate,
27123
+ directRefundTxSignature: directRefundAggregate,
27124
+ directFromCpfpRefundTxSignature: directFromCpfpRefundAggregate
27125
+ }
27126
+ ]
27127
+ });
27128
+ } catch (error) {
27129
+ throw new NetworkError(
27130
+ "Failed to finalize node signatures",
27131
+ {
27132
+ operation: "finalize_node_signatures",
27133
+ errorCount: 1,
27134
+ errors: error instanceof Error ? error.message : String(error)
27135
+ },
27136
+ error
27137
+ );
27138
+ }
27139
+ return finalizeResp;
27140
+ }
27141
+ /**
27142
+ * @deprecated
27143
+ * Use createTreeRoot instead.
27144
+ * This is currently only used to test backwards compatibility.
27145
+ */
27146
+ async createTreeWithoutDirectTx({
27147
+ keyDerivation,
27148
+ verifyingKey,
27149
+ depositTx,
27150
+ vout
27151
+ }) {
27152
+ const output = depositTx.getOutput(vout);
27153
+ if (!output) {
27154
+ throw new ValidationError("Invalid deposit transaction output", {
27155
+ field: "vout",
27156
+ value: vout,
27157
+ expected: "Valid output index"
27158
+ });
27159
+ }
27160
+ const script = output.script;
27161
+ const amount = output.amount;
27162
+ if (!script || !amount) {
27163
+ throw new ValidationError("No script or amount found in deposit tx", {
27164
+ field: "output",
27165
+ value: output,
27166
+ expected: "Output with script and amount"
27167
+ });
27168
+ }
27169
+ const depositOutPoint = {
27170
+ txid: (0, import_utils15.hexToBytes)(getTxId(depositTx)),
27171
+ index: vout
27172
+ };
27173
+ const depositTxOut = {
27174
+ script,
27175
+ amount
27176
+ };
27177
+ const [cpfpRootTx, _] = createRootTx(depositOutPoint, depositTxOut);
27178
+ const cpfpRootNonceCommitment = await this.config.signer.getRandomSigningCommitment();
27179
+ const cpfpRootTxSighash = getSigHashFromTx(cpfpRootTx, 0, output);
26099
27180
  const signingPubKey = await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
26100
- const refundP2trAddress = getP2TRAddressFromPublicKey(
26101
- signingPubKey,
26102
- this.config.getNetwork()
26103
- );
26104
- const refundAddress = btc5.Address(getNetwork(this.config.getNetwork())).decode(refundP2trAddress);
26105
- const refundPkScript = btc5.OutScript.encode(refundAddress);
26106
- refundTx.addOutput({
26107
- script: refundPkScript,
26108
- amount: outputAmount
27181
+ const { cpfpRefundTx } = createRefundTxs({
27182
+ sequence: INITIAL_SEQUENCE,
27183
+ input: { txid: (0, import_utils15.hexToBytes)(getTxId(cpfpRootTx)), index: 0 },
27184
+ amountSats: amount,
27185
+ receivingPubkey: signingPubKey,
27186
+ network: this.config.getNetwork()
26109
27187
  });
26110
- refundTx.addOutput(getEphemeralAnchorOutput());
26111
- const refundNonceCommitment = await this.config.signer.getRandomSigningCommitment();
26112
- const refundTxSighash = getSigHashFromTx(refundTx, 0, rootTx.getOutput(0));
27188
+ const cpfpRefundNonceCommitment = await this.config.signer.getRandomSigningCommitment();
27189
+ const cpfpRefundTxSighash = getSigHashFromTx(
27190
+ cpfpRefundTx,
27191
+ 0,
27192
+ cpfpRootTx.getOutput(0)
27193
+ );
26113
27194
  const sparkClient = await this.connectionManager.createSparkClient(
26114
27195
  this.config.getCoordinatorAddress()
26115
27196
  );
@@ -26123,14 +27204,14 @@ var DepositService = class {
26123
27204
  network: this.config.getNetworkProto()
26124
27205
  },
26125
27206
  rootTxSigningJob: {
26126
- rawTx: rootTx.toBytes(),
27207
+ rawTx: cpfpRootTx.toBytes(),
26127
27208
  signingPublicKey: signingPubKey,
26128
- signingNonceCommitment: rootNonceCommitment.commitment
27209
+ signingNonceCommitment: cpfpRootNonceCommitment.commitment
26129
27210
  },
26130
27211
  refundTxSigningJob: {
26131
- rawTx: refundTx.toBytes(),
27212
+ rawTx: cpfpRefundTx.toBytes(),
26132
27213
  signingPublicKey: signingPubKey,
26133
- signingNonceCommitment: refundNonceCommitment.commitment
27214
+ signingNonceCommitment: cpfpRefundNonceCommitment.commitment
26134
27215
  }
26135
27216
  });
26136
27217
  } catch (error) {
@@ -26176,55 +27257,55 @@ var DepositService = class {
26176
27257
  expected: verifyingKey
26177
27258
  });
26178
27259
  }
26179
- const rootSignature = await this.config.signer.signFrost({
26180
- message: rootTxSighash,
27260
+ const cpfpRootSignature = await this.config.signer.signFrost({
27261
+ message: cpfpRootTxSighash,
26181
27262
  publicKey: signingPubKey,
26182
27263
  keyDerivation,
26183
27264
  verifyingKey,
26184
- selfCommitment: rootNonceCommitment,
27265
+ selfCommitment: cpfpRootNonceCommitment,
26185
27266
  statechainCommitments: treeResp.rootNodeSignatureShares.nodeTxSigningResult.signingNonceCommitments,
26186
27267
  adaptorPubKey: new Uint8Array()
26187
27268
  });
26188
- const refundSignature = await this.config.signer.signFrost({
26189
- message: refundTxSighash,
27269
+ const cpfpRefundSignature = await this.config.signer.signFrost({
27270
+ message: cpfpRefundTxSighash,
26190
27271
  publicKey: signingPubKey,
26191
27272
  keyDerivation,
26192
- verifyingKey,
26193
- selfCommitment: refundNonceCommitment,
27273
+ verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
27274
+ selfCommitment: cpfpRefundNonceCommitment,
26194
27275
  statechainCommitments: treeResp.rootNodeSignatureShares.refundTxSigningResult.signingNonceCommitments,
26195
27276
  adaptorPubKey: new Uint8Array()
26196
27277
  });
26197
- const rootAggregate = await this.config.signer.aggregateFrost({
26198
- message: rootTxSighash,
27278
+ const cpfpRootAggregate = await this.config.signer.aggregateFrost({
27279
+ message: cpfpRootTxSighash,
26199
27280
  statechainSignatures: treeResp.rootNodeSignatureShares.nodeTxSigningResult.signatureShares,
26200
27281
  statechainPublicKeys: treeResp.rootNodeSignatureShares.nodeTxSigningResult.publicKeys,
26201
27282
  verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
26202
27283
  statechainCommitments: treeResp.rootNodeSignatureShares.nodeTxSigningResult.signingNonceCommitments,
26203
- selfCommitment: rootNonceCommitment,
27284
+ selfCommitment: cpfpRootNonceCommitment,
26204
27285
  publicKey: signingPubKey,
26205
- selfSignature: rootSignature,
27286
+ selfSignature: cpfpRootSignature,
26206
27287
  adaptorPubKey: new Uint8Array()
26207
27288
  });
26208
- const refundAggregate = await this.config.signer.aggregateFrost({
26209
- message: refundTxSighash,
27289
+ const cpfpRefundAggregate = await this.config.signer.aggregateFrost({
27290
+ message: cpfpRefundTxSighash,
26210
27291
  statechainSignatures: treeResp.rootNodeSignatureShares.refundTxSigningResult.signatureShares,
26211
27292
  statechainPublicKeys: treeResp.rootNodeSignatureShares.refundTxSigningResult.publicKeys,
26212
27293
  verifyingKey: treeResp.rootNodeSignatureShares.verifyingKey,
26213
27294
  statechainCommitments: treeResp.rootNodeSignatureShares.refundTxSigningResult.signingNonceCommitments,
26214
- selfCommitment: refundNonceCommitment,
27295
+ selfCommitment: cpfpRefundNonceCommitment,
26215
27296
  publicKey: signingPubKey,
26216
- selfSignature: refundSignature,
27297
+ selfSignature: cpfpRefundSignature,
26217
27298
  adaptorPubKey: new Uint8Array()
26218
27299
  });
26219
27300
  let finalizeResp;
26220
27301
  try {
26221
- finalizeResp = await sparkClient.finalize_node_signatures({
27302
+ finalizeResp = await sparkClient.finalize_node_signatures_v2({
26222
27303
  intent: 0 /* CREATION */,
26223
27304
  nodeSignatures: [
26224
27305
  {
26225
27306
  nodeId: treeResp.rootNodeSignatureShares.nodeId,
26226
- nodeTxSignature: rootAggregate,
26227
- refundTxSignature: refundAggregate
27307
+ nodeTxSignature: cpfpRootAggregate,
27308
+ refundTxSignature: cpfpRefundAggregate
26228
27309
  }
26229
27310
  ]
26230
27311
  });
@@ -26434,7 +27515,8 @@ var LightningService = class {
26434
27515
  let signingCommitments;
26435
27516
  try {
26436
27517
  signingCommitments = await sparkClient.get_signing_commitments({
26437
- nodeIds: leaves.map((leaf) => leaf.leaf.id)
27518
+ nodeIds: leaves.map((leaf) => leaf.leaf.id),
27519
+ count: 3
26438
27520
  });
26439
27521
  } catch (error) {
26440
27522
  throw new NetworkError(
@@ -26447,10 +27529,19 @@ var LightningService = class {
26447
27529
  error
26448
27530
  );
26449
27531
  }
26450
- const leafSigningJobs = await this.signingService.signRefunds(
27532
+ const {
27533
+ cpfpLeafSigningJobs,
27534
+ directLeafSigningJobs,
27535
+ directFromCpfpLeafSigningJobs
27536
+ } = await this.signingService.signRefunds(
26451
27537
  leaves,
26452
- signingCommitments.signingCommitments,
26453
- receiverIdentityPubkey
27538
+ receiverIdentityPubkey,
27539
+ signingCommitments.signingCommitments.slice(0, leaves.length),
27540
+ signingCommitments.signingCommitments.slice(
27541
+ leaves.length,
27542
+ 2 * leaves.length
27543
+ ),
27544
+ signingCommitments.signingCommitments.slice(2 * leaves.length)
26454
27545
  );
26455
27546
  const transferId = (0, import_uuidv74.uuidv7)();
26456
27547
  let bolt11String = "";
@@ -26487,7 +27578,7 @@ var LightningService = class {
26487
27578
  const reason = isInboundPayment ? 1 /* REASON_RECEIVE */ : 0 /* REASON_SEND */;
26488
27579
  let response;
26489
27580
  try {
26490
- response = await sparkClient.initiate_preimage_swap({
27581
+ response = await sparkClient.initiate_preimage_swap_v2({
26491
27582
  paymentHash,
26492
27583
  invoiceAmount: {
26493
27584
  invoiceAmountProof: {
@@ -26499,7 +27590,9 @@ var LightningService = class {
26499
27590
  transfer: {
26500
27591
  transferId,
26501
27592
  ownerIdentityPublicKey: await this.config.signer.getIdentityPublicKey(),
26502
- leavesToSend: leafSigningJobs,
27593
+ leavesToSend: cpfpLeafSigningJobs,
27594
+ directLeavesToSend: directLeafSigningJobs,
27595
+ directFromCpfpLeavesToSend: directFromCpfpLeafSigningJobs,
26503
27596
  receiverIdentityPublicKey: receiverIdentityPubkey,
26504
27597
  expiryTime: new Date(Date.now() + 2 * 60 * 1e3)
26505
27598
  },
@@ -28976,13 +30069,6 @@ function isTokenTransaction(tokenTransaction) {
28976
30069
  init_buffer();
28977
30070
  var import_utils20 = require("@noble/curves/abstract/utils");
28978
30071
  var import_btc_signer5 = require("@scure/btc-signer");
28979
- var INITIAL_TIME_LOCK3 = 2e3;
28980
- function maybeApplyFee2(amount) {
28981
- if (amount > BigInt(DEFAULT_FEE_SATS)) {
28982
- return amount - BigInt(DEFAULT_FEE_SATS);
28983
- }
28984
- return amount;
28985
- }
28986
30072
  var TreeCreationService = class {
28987
30073
  config;
28988
30074
  connectionManager;
@@ -29097,7 +30183,7 @@ var TreeCreationService = class {
29097
30183
  );
29098
30184
  let response;
29099
30185
  try {
29100
- response = await sparkClient.create_tree(request);
30186
+ response = await sparkClient.create_tree_v2(request);
29101
30187
  } catch (error) {
29102
30188
  throw new Error(`Error creating tree: ${error}`);
29103
30189
  }
@@ -29114,7 +30200,7 @@ var TreeCreationService = class {
29114
30200
  );
29115
30201
  let finalizeResp;
29116
30202
  try {
29117
- finalizeResp = await sparkClient.finalize_node_signatures({
30203
+ finalizeResp = await sparkClient.finalize_node_signatures_v2({
29118
30204
  nodeSignatures
29119
30205
  });
29120
30206
  } catch (error) {
@@ -29179,91 +30265,111 @@ var TreeCreationService = class {
29179
30265
  async buildChildCreationNode(node, parentTx, vout, network) {
29180
30266
  const internalCreationNode = {
29181
30267
  nodeTxSigningJob: void 0,
29182
- refundTxSigningJob: void 0,
29183
- children: [],
29184
30268
  directNodeTxSigningJob: void 0,
30269
+ refundTxSigningJob: void 0,
29185
30270
  directRefundTxSigningJob: void 0,
29186
- directFromCpfpRefundTxSigningJob: void 0
30271
+ directFromCpfpRefundTxSigningJob: void 0,
30272
+ children: []
29187
30273
  };
29188
- const tx = new import_btc_signer5.Transaction({ version: 3 });
29189
- tx.addInput({
29190
- txid: getTxId(parentTx),
29191
- index: vout
29192
- });
29193
30274
  const parentTxOut = parentTx.getOutput(vout);
29194
30275
  if (!parentTxOut?.script || !parentTxOut?.amount) {
29195
30276
  throw new Error("parentTxOut is undefined");
29196
30277
  }
29197
- tx.addOutput({
30278
+ const parentOutPoint = {
30279
+ txid: (0, import_utils20.hexToBytes)(getTxId(parentTx)),
30280
+ index: vout
30281
+ };
30282
+ const parentTxOutObj = {
29198
30283
  script: parentTxOut.script,
29199
30284
  amount: parentTxOut.amount
29200
- // maybeApplyFee(parentTxOut.amount),
29201
- });
29202
- tx.addOutput(getEphemeralAnchorOutput());
29203
- const signingNonceCommitment = await this.config.signer.getRandomSigningCommitment();
29204
- const signingJob = {
30285
+ };
30286
+ const { cpfpNodeTx, directNodeTx } = createNodeTxs(
30287
+ parentTxOutObj,
30288
+ parentOutPoint
30289
+ );
30290
+ const cpfpNodeSigningCommitment = await this.config.signer.getRandomSigningCommitment();
30291
+ const directNodeSigningCommitment = await this.config.signer.getRandomSigningCommitment();
30292
+ const cpfpNodeSigningJob = {
29205
30293
  signingPublicKey: node.signingPublicKey,
29206
- rawTx: tx.toBytes(),
29207
- signingNonceCommitment: signingNonceCommitment.commitment
30294
+ rawTx: cpfpNodeTx.toBytes(),
30295
+ signingNonceCommitment: cpfpNodeSigningCommitment.commitment
29208
30296
  };
29209
- internalCreationNode.nodeTxSigningCommitment = signingNonceCommitment;
29210
- internalCreationNode.nodeTxSigningJob = signingJob;
29211
- const sequence = 1 << 30 | INITIAL_TIME_LOCK3;
30297
+ const directNodeSigningJob = directNodeTx ? {
30298
+ signingPublicKey: node.signingPublicKey,
30299
+ rawTx: directNodeTx.toBytes(),
30300
+ signingNonceCommitment: directNodeSigningCommitment.commitment
30301
+ } : void 0;
30302
+ internalCreationNode.nodeTxSigningCommitment = cpfpNodeSigningCommitment;
30303
+ internalCreationNode.directNodeTxSigningCommitment = directNodeSigningCommitment;
30304
+ internalCreationNode.nodeTxSigningJob = cpfpNodeSigningJob;
30305
+ internalCreationNode.directNodeTxSigningJob = directNodeSigningJob;
30306
+ const sequence = INITIAL_SEQUENCE;
30307
+ const directSequence = INITIAL_DIRECT_SEQUENCE;
29212
30308
  const childCreationNode = {
29213
30309
  nodeTxSigningJob: void 0,
29214
- refundTxSigningJob: void 0,
29215
- children: [],
29216
30310
  directNodeTxSigningJob: void 0,
30311
+ refundTxSigningJob: void 0,
29217
30312
  directRefundTxSigningJob: void 0,
29218
- directFromCpfpRefundTxSigningJob: void 0
30313
+ directFromCpfpRefundTxSigningJob: void 0,
30314
+ children: []
29219
30315
  };
29220
- const childTx = new import_btc_signer5.Transaction({ version: 3 });
29221
- childTx.addInput({
29222
- txid: getTxId(tx),
29223
- index: 0,
29224
- sequence
29225
- });
29226
- childTx.addOutput({
29227
- script: parentTxOut.script,
29228
- amount: parentTxOut.amount
29229
- // maybeApplyFee(parentTxOut.amount),
29230
- });
29231
- childTx.addOutput(getEphemeralAnchorOutput());
29232
- const childSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
29233
- const childSigningJob = {
30316
+ const [cpfpLeafTx, directLeafTx] = createLeafNodeTx(
30317
+ sequence,
30318
+ directSequence,
30319
+ { txid: (0, import_utils20.hexToBytes)(getTxId(cpfpNodeTx)), index: 0 },
30320
+ parentTxOutObj,
30321
+ true
30322
+ // shouldCalculateFee
30323
+ );
30324
+ const cpfpLeafSigningCommitment = await this.config.signer.getRandomSigningCommitment();
30325
+ const directLeafSigningCommitment = await this.config.signer.getRandomSigningCommitment();
30326
+ const cpfpLeafSigningJob = {
29234
30327
  signingPublicKey: node.signingPublicKey,
29235
- rawTx: childTx.toBytes(),
29236
- signingNonceCommitment: childSigningNonceCommitment.commitment
29237
- };
29238
- childCreationNode.nodeTxSigningCommitment = childSigningNonceCommitment;
29239
- childCreationNode.nodeTxSigningJob = childSigningJob;
29240
- const refundTx = new import_btc_signer5.Transaction({ version: 3 });
29241
- refundTx.addInput({
29242
- txid: getTxId(childTx),
29243
- index: 0,
29244
- sequence
29245
- });
29246
- const refundP2trAddress = getP2TRAddressFromPublicKey(
29247
- node.signingPublicKey,
30328
+ rawTx: cpfpLeafTx.toBytes(),
30329
+ signingNonceCommitment: cpfpLeafSigningCommitment.commitment
30330
+ };
30331
+ const directLeafSigningJob = {
30332
+ signingPublicKey: node.signingPublicKey,
30333
+ rawTx: directLeafTx.toBytes(),
30334
+ signingNonceCommitment: directLeafSigningCommitment.commitment
30335
+ };
30336
+ childCreationNode.nodeTxSigningCommitment = cpfpLeafSigningCommitment;
30337
+ childCreationNode.directNodeTxSigningCommitment = directLeafSigningCommitment;
30338
+ childCreationNode.nodeTxSigningJob = cpfpLeafSigningJob;
30339
+ childCreationNode.directNodeTxSigningJob = directLeafSigningJob;
30340
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
30341
+ sequence,
30342
+ directSequence,
30343
+ input: { txid: (0, import_utils20.hexToBytes)(getTxId(cpfpLeafTx)), index: 0 },
30344
+ directInput: { txid: (0, import_utils20.hexToBytes)(getTxId(directLeafTx)), index: 0 },
30345
+ amountSats: parentTxOut.amount,
30346
+ receivingPubkey: node.signingPublicKey,
29248
30347
  network
29249
- );
29250
- const refundAddress = (0, import_btc_signer5.Address)(getNetwork(network)).decode(
29251
- refundP2trAddress
29252
- );
29253
- const refundPkScript = import_btc_signer5.OutScript.encode(refundAddress);
29254
- refundTx.addOutput({
29255
- script: refundPkScript,
29256
- amount: maybeApplyFee2(parentTxOut.amount)
29257
30348
  });
29258
- refundTx.addOutput(getEphemeralAnchorOutput());
29259
- const refundSigningNonceCommitment = await this.config.signer.getRandomSigningCommitment();
29260
- const refundSigningJob = {
30349
+ const cpfpRefundSigningCommitment = await this.config.signer.getRandomSigningCommitment();
30350
+ const directRefundSigningCommitment = await this.config.signer.getRandomSigningCommitment();
30351
+ const directFromCpfpRefundSigningCommitment = await this.config.signer.getRandomSigningCommitment();
30352
+ const cpfpRefundSigningJob = {
29261
30353
  signingPublicKey: node.signingPublicKey,
29262
- rawTx: refundTx.toBytes(),
29263
- signingNonceCommitment: refundSigningNonceCommitment.commitment
30354
+ rawTx: cpfpRefundTx.toBytes(),
30355
+ signingNonceCommitment: cpfpRefundSigningCommitment.commitment
29264
30356
  };
29265
- childCreationNode.refundTxSigningCommitment = refundSigningNonceCommitment;
29266
- childCreationNode.refundTxSigningJob = refundSigningJob;
30357
+ const directRefundSigningJob = directRefundTx ? {
30358
+ signingPublicKey: node.signingPublicKey,
30359
+ rawTx: directRefundTx.toBytes(),
30360
+ signingNonceCommitment: directRefundSigningCommitment.commitment
30361
+ } : void 0;
30362
+ const directFromCpfpRefundSigningJob = directFromCpfpRefundTx ? {
30363
+ signingPublicKey: node.signingPublicKey,
30364
+ rawTx: directFromCpfpRefundTx.toBytes(),
30365
+ signingNonceCommitment: directFromCpfpRefundSigningCommitment.commitment
30366
+ } : void 0;
30367
+ childCreationNode.refundTxSigningCommitment = cpfpRefundSigningCommitment;
30368
+ childCreationNode.directRefundTxSigningCommitment = directRefundSigningCommitment;
30369
+ childCreationNode.directFromCpfpRefundTxSigningCommitment = directFromCpfpRefundSigningCommitment;
30370
+ childCreationNode.refundTxSigningJob = cpfpRefundSigningJob;
30371
+ childCreationNode.directRefundTxSigningJob = directRefundSigningJob;
30372
+ childCreationNode.directFromCpfpRefundTxSigningJob = directFromCpfpRefundSigningJob;
29267
30373
  internalCreationNode.children.push(childCreationNode);
29268
30374
  return internalCreationNode;
29269
30375
  }
@@ -29272,11 +30378,7 @@ var TreeCreationService = class {
29272
30378
  if (!parentTxOutput?.script || !parentTxOutput?.amount) {
29273
30379
  throw new Error("parentTxOutput is undefined");
29274
30380
  }
29275
- const rootNodeTx = new import_btc_signer5.Transaction({ version: 3 });
29276
- rootNodeTx.addInput({
29277
- txid: getTxId(parentTx),
29278
- index: vout
29279
- });
30381
+ const childTxOuts = [];
29280
30382
  for (let i = 0; i < root.children.length; i++) {
29281
30383
  const child = root.children[i];
29282
30384
  if (!child || !child.address) {
@@ -29284,28 +30386,41 @@ var TreeCreationService = class {
29284
30386
  }
29285
30387
  const childAddress = (0, import_btc_signer5.Address)(getNetwork(network)).decode(child.address);
29286
30388
  const childPkScript = import_btc_signer5.OutScript.encode(childAddress);
29287
- rootNodeTx.addOutput({
30389
+ childTxOuts.push({
29288
30390
  script: childPkScript,
29289
30391
  amount: parentTxOutput.amount / 2n
29290
- // feeAdjustedAmount / 2n,
29291
30392
  });
29292
30393
  }
29293
- rootNodeTx.addOutput(getEphemeralAnchorOutput());
29294
- const rootNodeSigningCommitment = await this.config.signer.getRandomSigningCommitment();
29295
- const rootNodeSigningJob = {
30394
+ const parentOutPoint = {
30395
+ txid: (0, import_utils20.hexToBytes)(getTxId(parentTx)),
30396
+ index: vout
30397
+ };
30398
+ const [cpfpSplitTx, directSplitTx] = createSplitTx(
30399
+ parentOutPoint,
30400
+ childTxOuts
30401
+ );
30402
+ const cpfpSplitSigningCommitment = await this.config.signer.getRandomSigningCommitment();
30403
+ const directSplitSigningCommitment = await this.config.signer.getRandomSigningCommitment();
30404
+ const cpfpSplitSigningJob = {
30405
+ signingPublicKey: root.signingPublicKey,
30406
+ rawTx: cpfpSplitTx.toBytes(),
30407
+ signingNonceCommitment: cpfpSplitSigningCommitment.commitment
30408
+ };
30409
+ const directSplitSigningJob = {
29296
30410
  signingPublicKey: root.signingPublicKey,
29297
- rawTx: rootNodeTx.toBytes(),
29298
- signingNonceCommitment: rootNodeSigningCommitment.commitment
30411
+ rawTx: directSplitTx.toBytes(),
30412
+ signingNonceCommitment: directSplitSigningCommitment.commitment
29299
30413
  };
29300
30414
  const rootCreationNode = {
29301
- nodeTxSigningJob: rootNodeSigningJob,
30415
+ nodeTxSigningJob: cpfpSplitSigningJob,
30416
+ directNodeTxSigningJob: directSplitSigningJob,
29302
30417
  refundTxSigningJob: void 0,
29303
- children: [],
29304
- directNodeTxSigningJob: void 0,
29305
30418
  directRefundTxSigningJob: void 0,
29306
- directFromCpfpRefundTxSigningJob: void 0
30419
+ directFromCpfpRefundTxSigningJob: void 0,
30420
+ children: []
29307
30421
  };
29308
- rootCreationNode.nodeTxSigningCommitment = rootNodeSigningCommitment;
30422
+ rootCreationNode.nodeTxSigningCommitment = cpfpSplitSigningCommitment;
30423
+ rootCreationNode.directNodeTxSigningCommitment = directSplitSigningCommitment;
29309
30424
  const leftChild = root.children[0];
29310
30425
  const rightChild = root.children[1];
29311
30426
  if (!leftChild || !rightChild) {
@@ -29313,13 +30428,15 @@ var TreeCreationService = class {
29313
30428
  }
29314
30429
  const leftChildCreationNode = await this.buildChildCreationNode(
29315
30430
  leftChild,
29316
- rootNodeTx,
30431
+ cpfpSplitTx,
30432
+ // Use CPFP version for children
29317
30433
  0,
29318
30434
  network
29319
30435
  );
29320
30436
  const rightChildCreationNode = await this.buildChildCreationNode(
29321
30437
  rightChild,
29322
- rootNodeTx,
30438
+ cpfpSplitTx,
30439
+ // Use CPFP version for children
29323
30440
  1,
29324
30441
  network
29325
30442
  );
@@ -29328,19 +30445,19 @@ var TreeCreationService = class {
29328
30445
  return rootCreationNode;
29329
30446
  }
29330
30447
  async signNodeCreation(parentTx, vout, internalNode, creationNode, creationResponseNode) {
29331
- if (!creationNode.nodeTxSigningJob?.signingPublicKey || !internalNode.verificationKey) {
30448
+ if (!creationNode.nodeTxSigningJob?.signingPublicKey || !creationNode.directNodeTxSigningJob?.signingPublicKey || !internalNode.verificationKey) {
29332
30449
  throw new Error("signingPublicKey or verificationKey is undefined");
29333
30450
  }
29334
30451
  const parentTxOutput = parentTx.getOutput(vout);
29335
30452
  if (!parentTxOutput) {
29336
30453
  throw new Error("parentTxOutput is undefined");
29337
30454
  }
29338
- const tx = getTxFromRawTxBytes(creationNode.nodeTxSigningJob.rawTx);
29339
- const txSighash = getSigHashFromTx(tx, 0, parentTxOutput);
29340
- let nodeTxSignature = new Uint8Array();
30455
+ const cpfpNodeTx = getTxFromRawTxBytes(creationNode.nodeTxSigningJob.rawTx);
30456
+ const cpfpNodeTxSighash = getSigHashFromTx(cpfpNodeTx, 0, parentTxOutput);
30457
+ let cpfpNodeTxSignature = new Uint8Array();
29341
30458
  if (creationNode.nodeTxSigningCommitment) {
29342
- const userSignature = await this.config.signer.signFrost({
29343
- message: txSighash,
30459
+ const cpfpUserSignature = await this.config.signer.signFrost({
30460
+ message: cpfpNodeTxSighash,
29344
30461
  publicKey: creationNode.nodeTxSigningJob.signingPublicKey,
29345
30462
  keyDerivation: {
29346
30463
  type: "leaf" /* LEAF */,
@@ -29350,30 +30467,84 @@ var TreeCreationService = class {
29350
30467
  statechainCommitments: creationResponseNode.nodeTxSigningResult?.signingNonceCommitments,
29351
30468
  verifyingKey: internalNode.verificationKey
29352
30469
  });
29353
- nodeTxSignature = await this.config.signer.aggregateFrost({
29354
- message: txSighash,
30470
+ cpfpNodeTxSignature = await this.config.signer.aggregateFrost({
30471
+ message: cpfpNodeTxSighash,
29355
30472
  statechainSignatures: creationResponseNode.nodeTxSigningResult?.signatureShares,
29356
30473
  statechainPublicKeys: creationResponseNode.nodeTxSigningResult?.publicKeys,
29357
30474
  verifyingKey: internalNode.verificationKey,
29358
30475
  statechainCommitments: creationResponseNode.nodeTxSigningResult?.signingNonceCommitments,
29359
30476
  selfCommitment: creationNode.nodeTxSigningCommitment,
29360
- selfSignature: userSignature,
30477
+ selfSignature: cpfpUserSignature,
29361
30478
  publicKey: internalNode.signingPublicKey
29362
30479
  });
29363
30480
  }
29364
- let refundTxSignature = new Uint8Array();
29365
- if (creationNode.refundTxSigningCommitment) {
29366
- const rawTx = creationNode.refundTxSigningJob?.rawTx;
29367
- if (!rawTx) {
29368
- throw new Error("rawTx is undefined");
29369
- }
29370
- if (!creationNode.refundTxSigningJob?.signingPublicKey) {
29371
- throw new Error("signingPublicKey is undefined");
29372
- }
29373
- const refundTx = getTxFromRawTxBytes(rawTx);
29374
- const refundTxSighash = getSigHashFromTx(refundTx, 0, parentTxOutput);
29375
- const refundSigningResponse = await this.config.signer.signFrost({
29376
- message: refundTxSighash,
30481
+ const directNodeTx = getTxFromRawTxBytes(
30482
+ creationNode.directNodeTxSigningJob.rawTx
30483
+ );
30484
+ const directNodeTxSighash = getSigHashFromTx(
30485
+ directNodeTx,
30486
+ 0,
30487
+ parentTxOutput
30488
+ );
30489
+ let directNodeTxSignature = new Uint8Array();
30490
+ if (creationNode.directNodeTxSigningCommitment) {
30491
+ const directUserSignature = await this.config.signer.signFrost({
30492
+ message: directNodeTxSighash,
30493
+ publicKey: creationNode.directNodeTxSigningJob.signingPublicKey,
30494
+ keyDerivation: {
30495
+ type: "leaf" /* LEAF */,
30496
+ path: creationResponseNode.nodeId
30497
+ },
30498
+ selfCommitment: creationNode.directNodeTxSigningCommitment,
30499
+ statechainCommitments: creationResponseNode.directNodeTxSigningResult?.signingNonceCommitments,
30500
+ verifyingKey: internalNode.verificationKey
30501
+ });
30502
+ directNodeTxSignature = await this.config.signer.aggregateFrost({
30503
+ message: directNodeTxSighash,
30504
+ statechainSignatures: creationResponseNode.directNodeTxSigningResult?.signatureShares,
30505
+ statechainPublicKeys: creationResponseNode.directNodeTxSigningResult?.publicKeys,
30506
+ verifyingKey: internalNode.verificationKey,
30507
+ statechainCommitments: creationResponseNode.directNodeTxSigningResult?.signingNonceCommitments,
30508
+ selfCommitment: creationNode.directNodeTxSigningCommitment,
30509
+ selfSignature: directUserSignature,
30510
+ publicKey: internalNode.signingPublicKey
30511
+ });
30512
+ }
30513
+ let cpfpRefundTxSignature = new Uint8Array();
30514
+ let directRefundTxSignature = new Uint8Array();
30515
+ let directFromCpfpRefundTxSignature = new Uint8Array();
30516
+ if (creationNode.refundTxSigningCommitment && creationNode.directRefundTxSigningCommitment && creationNode.directFromCpfpRefundTxSigningCommitment) {
30517
+ const rawCpfpRefundTx = creationNode.refundTxSigningJob?.rawTx;
30518
+ const rawDirectRefundTx = creationNode.directRefundTxSigningJob?.rawTx;
30519
+ const rawDirectFromCpfpRefundTx = creationNode.directFromCpfpRefundTxSigningJob?.rawTx;
30520
+ if (!rawCpfpRefundTx || !rawDirectRefundTx || !rawDirectFromCpfpRefundTx) {
30521
+ throw new Error("refund transaction rawTx is undefined");
30522
+ }
30523
+ if (!creationNode.refundTxSigningJob?.signingPublicKey || !creationNode.directRefundTxSigningJob?.signingPublicKey || !creationNode.directFromCpfpRefundTxSigningJob?.signingPublicKey) {
30524
+ throw new Error("refund transaction signingPublicKey is undefined");
30525
+ }
30526
+ const cpfpRefundTx = getTxFromRawTxBytes(rawCpfpRefundTx);
30527
+ const directRefundTx = getTxFromRawTxBytes(rawDirectRefundTx);
30528
+ const directFromCpfpRefundTx = getTxFromRawTxBytes(
30529
+ rawDirectFromCpfpRefundTx
30530
+ );
30531
+ const cpfpRefundTxSighash = getSigHashFromTx(
30532
+ cpfpRefundTx,
30533
+ 0,
30534
+ cpfpNodeTx.getOutput(0)
30535
+ );
30536
+ const directRefundTxSighash = getSigHashFromTx(
30537
+ directRefundTx,
30538
+ 0,
30539
+ directNodeTx.getOutput(0)
30540
+ );
30541
+ const directFromCpfpRefundTxSighash = getSigHashFromTx(
30542
+ directFromCpfpRefundTx,
30543
+ 0,
30544
+ cpfpNodeTx.getOutput(0)
30545
+ );
30546
+ const cpfpRefundUserSignature = await this.config.signer.signFrost({
30547
+ message: cpfpRefundTxSighash,
29377
30548
  publicKey: creationNode.refundTxSigningJob.signingPublicKey,
29378
30549
  keyDerivation: {
29379
30550
  type: "leaf" /* LEAF */,
@@ -29383,27 +30554,69 @@ var TreeCreationService = class {
29383
30554
  statechainCommitments: creationResponseNode.refundTxSigningResult?.signingNonceCommitments,
29384
30555
  verifyingKey: internalNode.verificationKey
29385
30556
  });
29386
- refundTxSignature = await this.config.signer.aggregateFrost({
29387
- message: refundTxSighash,
30557
+ cpfpRefundTxSignature = await this.config.signer.aggregateFrost({
30558
+ message: cpfpRefundTxSighash,
29388
30559
  statechainSignatures: creationResponseNode.refundTxSigningResult?.signatureShares,
29389
30560
  statechainPublicKeys: creationResponseNode.refundTxSigningResult?.publicKeys,
29390
30561
  verifyingKey: internalNode.verificationKey,
29391
30562
  statechainCommitments: creationResponseNode.refundTxSigningResult?.signingNonceCommitments,
29392
30563
  selfCommitment: creationNode.refundTxSigningCommitment,
29393
- selfSignature: refundSigningResponse,
30564
+ selfSignature: cpfpRefundUserSignature,
30565
+ publicKey: internalNode.signingPublicKey
30566
+ });
30567
+ const keyDerivation = {
30568
+ type: "leaf" /* LEAF */,
30569
+ path: creationResponseNode.nodeId
30570
+ };
30571
+ const directRefundUserSignature = await this.config.signer.signFrost({
30572
+ message: directRefundTxSighash,
30573
+ publicKey: creationNode.directRefundTxSigningJob.signingPublicKey,
30574
+ keyDerivation,
30575
+ selfCommitment: creationNode.directRefundTxSigningCommitment,
30576
+ statechainCommitments: creationResponseNode.directRefundTxSigningResult?.signingNonceCommitments,
30577
+ verifyingKey: internalNode.verificationKey
30578
+ });
30579
+ directRefundTxSignature = await this.config.signer.aggregateFrost({
30580
+ message: directRefundTxSighash,
30581
+ statechainSignatures: creationResponseNode.directRefundTxSigningResult?.signatureShares,
30582
+ statechainPublicKeys: creationResponseNode.directRefundTxSigningResult?.publicKeys,
30583
+ verifyingKey: internalNode.verificationKey,
30584
+ statechainCommitments: creationResponseNode.directRefundTxSigningResult?.signingNonceCommitments,
30585
+ selfCommitment: creationNode.directRefundTxSigningCommitment,
30586
+ selfSignature: directRefundUserSignature,
29394
30587
  publicKey: internalNode.signingPublicKey
29395
30588
  });
30589
+ const directFromCpfpRefundUserSignature = await this.config.signer.signFrost({
30590
+ message: directFromCpfpRefundTxSighash,
30591
+ publicKey: creationNode.directFromCpfpRefundTxSigningJob.signingPublicKey,
30592
+ keyDerivation,
30593
+ selfCommitment: creationNode.directFromCpfpRefundTxSigningCommitment,
30594
+ statechainCommitments: creationResponseNode.directFromCpfpRefundTxSigningResult?.signingNonceCommitments,
30595
+ verifyingKey: internalNode.verificationKey
30596
+ });
30597
+ directFromCpfpRefundTxSignature = await this.config.signer.aggregateFrost(
30598
+ {
30599
+ message: directFromCpfpRefundTxSighash,
30600
+ statechainSignatures: creationResponseNode.directFromCpfpRefundTxSigningResult?.signatureShares,
30601
+ statechainPublicKeys: creationResponseNode.directFromCpfpRefundTxSigningResult?.publicKeys,
30602
+ verifyingKey: internalNode.verificationKey,
30603
+ statechainCommitments: creationResponseNode.directFromCpfpRefundTxSigningResult?.signingNonceCommitments,
30604
+ selfCommitment: creationNode.directFromCpfpRefundTxSigningCommitment,
30605
+ selfSignature: directFromCpfpRefundUserSignature,
30606
+ publicKey: internalNode.signingPublicKey
30607
+ }
30608
+ );
29396
30609
  }
29397
30610
  return {
29398
- tx,
30611
+ tx: cpfpNodeTx,
30612
+ // Return CPFP version for children
29399
30613
  signature: {
29400
30614
  nodeId: creationResponseNode.nodeId,
29401
- nodeTxSignature,
29402
- refundTxSignature,
29403
- // TODO: Add direct refund signature
29404
- directNodeTxSignature: new Uint8Array(),
29405
- directRefundTxSignature: new Uint8Array(),
29406
- directFromCpfpRefundTxSignature: new Uint8Array()
30615
+ nodeTxSignature: cpfpNodeTxSignature,
30616
+ directNodeTxSignature,
30617
+ refundTxSignature: cpfpRefundTxSignature,
30618
+ directRefundTxSignature,
30619
+ directFromCpfpRefundTxSignature
29407
30620
  }
29408
30621
  };
29409
30622
  }
@@ -29466,8 +30679,45 @@ var SigningService = class {
29466
30679
  constructor(config) {
29467
30680
  this.config = config;
29468
30681
  }
29469
- async signRefunds(leaves, signingCommitments, receiverIdentityPubkey) {
30682
+ async signRefundsInternal(refundTx, sighash, leaf, signingCommitments) {
29470
30683
  const leafSigningJobs = [];
30684
+ const signingCommitment = await this.config.signer.getRandomSigningCommitment();
30685
+ if (!signingCommitments) {
30686
+ throw new ValidationError("Invalid signing commitments", {
30687
+ field: "signingNonceCommitments",
30688
+ value: signingCommitments,
30689
+ expected: "Non-null signing commitments"
30690
+ });
30691
+ }
30692
+ const signingResult = await this.config.signer.signFrost({
30693
+ message: sighash,
30694
+ keyDerivation: leaf.keyDerivation,
30695
+ publicKey: await this.config.signer.getPublicKeyFromDerivation(
30696
+ leaf.keyDerivation
30697
+ ),
30698
+ selfCommitment: signingCommitment,
30699
+ statechainCommitments: signingCommitments,
30700
+ adaptorPubKey: new Uint8Array(),
30701
+ verifyingKey: leaf.leaf.verifyingPublicKey
30702
+ });
30703
+ leafSigningJobs.push({
30704
+ leafId: leaf.leaf.id,
30705
+ signingPublicKey: await this.config.signer.getPublicKeyFromDerivation(
30706
+ leaf.keyDerivation
30707
+ ),
30708
+ rawTx: refundTx.toBytes(),
30709
+ signingNonceCommitment: signingCommitment.commitment,
30710
+ userSignature: signingResult,
30711
+ signingCommitments: {
30712
+ signingCommitments
30713
+ }
30714
+ });
30715
+ return leafSigningJobs;
30716
+ }
30717
+ async signRefunds(leaves, receiverIdentityPubkey, cpfpSigningCommitments, directSigningCommitments, directFromCpfpSigningCommitments) {
30718
+ const cpfpLeafSigningJobs = [];
30719
+ const directLeafSigningJobs = [];
30720
+ const directFromCpfpLeafSigningJobs = [];
29471
30721
  for (let i = 0; i < leaves.length; i++) {
29472
30722
  const leaf = leaves[i];
29473
30723
  if (!leaf?.leaf) {
@@ -29478,14 +30728,20 @@ var SigningService = class {
29478
30728
  });
29479
30729
  }
29480
30730
  const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
29481
- const nodeOutPoint = {
30731
+ const cpfpNodeOutPoint = {
29482
30732
  txid: (0, import_utils21.hexToBytes)(getTxId(nodeTx)),
29483
30733
  index: 0
29484
30734
  };
29485
30735
  const currRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
29486
- const { nextSequence } = getNextTransactionSequence(
29487
- currRefundTx.getInput(0).sequence
29488
- );
30736
+ const sequence = currRefundTx.getInput(0).sequence;
30737
+ if (!sequence) {
30738
+ throw new ValidationError("Invalid refund transaction", {
30739
+ field: "sequence",
30740
+ value: currRefundTx.getInput(0),
30741
+ expected: "Non-null sequence"
30742
+ });
30743
+ }
30744
+ const { nextSequence, nextDirectSequence } = getNextTransactionSequence(sequence);
29489
30745
  const amountSats = currRefundTx.getOutput(0).amount;
29490
30746
  if (amountSats === void 0) {
29491
30747
  throw new ValidationError("Invalid refund transaction", {
@@ -29494,48 +30750,90 @@ var SigningService = class {
29494
30750
  expected: "Non-null amount"
29495
30751
  });
29496
30752
  }
29497
- const refundTx = createRefundTx(
29498
- nextSequence,
29499
- nodeOutPoint,
30753
+ let directNodeTx;
30754
+ let directNodeOutPoint;
30755
+ if (leaf.leaf.directTx.length > 0) {
30756
+ directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
30757
+ directNodeOutPoint = {
30758
+ txid: (0, import_utils21.hexToBytes)(getTxId(directNodeTx)),
30759
+ index: 0
30760
+ };
30761
+ }
30762
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } = createRefundTxs({
30763
+ sequence: nextSequence,
30764
+ directSequence: nextDirectSequence,
30765
+ input: cpfpNodeOutPoint,
30766
+ directInput: directNodeOutPoint,
29500
30767
  amountSats,
29501
- receiverIdentityPubkey,
29502
- this.config.getNetwork()
30768
+ receivingPubkey: receiverIdentityPubkey,
30769
+ network: this.config.getNetwork()
30770
+ });
30771
+ const refundSighash = getSigHashFromTx(
30772
+ cpfpRefundTx,
30773
+ 0,
30774
+ nodeTx.getOutput(0)
29503
30775
  );
29504
- const sighash = getSigHashFromTx(refundTx, 0, nodeTx.getOutput(0));
29505
- const signingCommitment = await this.config.signer.getRandomSigningCommitment();
29506
- const signingNonceCommitments = signingCommitments[i]?.signingNonceCommitments;
29507
- if (!signingNonceCommitments) {
29508
- throw new ValidationError("Invalid signing commitments", {
29509
- field: "signingNonceCommitments",
29510
- value: signingCommitments[i],
29511
- expected: "Non-null signing nonce commitments"
29512
- });
30776
+ const signingJobs = await this.signRefundsInternal(
30777
+ cpfpRefundTx,
30778
+ refundSighash,
30779
+ leaf,
30780
+ cpfpSigningCommitments[i]?.signingNonceCommitments
30781
+ );
30782
+ cpfpLeafSigningJobs.push(...signingJobs);
30783
+ if (directRefundTx) {
30784
+ if (!directNodeTx) {
30785
+ throw new ValidationError(
30786
+ "Direct node transaction undefined while direct refund transaction is defined",
30787
+ {
30788
+ field: "directNodeTx",
30789
+ value: directNodeTx,
30790
+ expected: "Non-null direct node transaction"
30791
+ }
30792
+ );
30793
+ }
30794
+ const refundSighash2 = getSigHashFromTx(
30795
+ directRefundTx,
30796
+ 0,
30797
+ directNodeTx.getOutput(0)
30798
+ );
30799
+ const signingJobs2 = await this.signRefundsInternal(
30800
+ directRefundTx,
30801
+ refundSighash2,
30802
+ leaf,
30803
+ directSigningCommitments[i]?.signingNonceCommitments
30804
+ );
30805
+ directLeafSigningJobs.push(...signingJobs2);
29513
30806
  }
29514
- const signingResult = await this.config.signer.signFrost({
29515
- message: sighash,
29516
- keyDerivation: leaf.keyDerivation,
29517
- publicKey: await this.config.signer.getPublicKeyFromDerivation(
29518
- leaf.keyDerivation
29519
- ),
29520
- selfCommitment: signingCommitment,
29521
- statechainCommitments: signingNonceCommitments,
29522
- adaptorPubKey: new Uint8Array(),
29523
- verifyingKey: leaf.leaf.verifyingPublicKey
29524
- });
29525
- leafSigningJobs.push({
29526
- leafId: leaf.leaf.id,
29527
- signingPublicKey: await this.config.signer.getPublicKeyFromDerivation(
29528
- leaf.keyDerivation
29529
- ),
29530
- rawTx: refundTx.toBytes(),
29531
- signingNonceCommitment: signingCommitment.commitment,
29532
- userSignature: signingResult,
29533
- signingCommitments: {
29534
- signingCommitments: signingNonceCommitments
30807
+ if (directFromCpfpRefundTx) {
30808
+ if (!directNodeTx) {
30809
+ throw new ValidationError(
30810
+ "Direct node transaction undefined while direct from CPFP refund transaction is defined",
30811
+ {
30812
+ field: "directNodeTx",
30813
+ value: directNodeTx,
30814
+ expected: "Non-null direct node transaction"
30815
+ }
30816
+ );
29535
30817
  }
29536
- });
30818
+ const refundSighash2 = getSigHashFromTx(
30819
+ directFromCpfpRefundTx,
30820
+ 0,
30821
+ nodeTx.getOutput(0)
30822
+ );
30823
+ const signingJobs2 = await this.signRefundsInternal(
30824
+ directFromCpfpRefundTx,
30825
+ refundSighash2,
30826
+ leaf,
30827
+ directFromCpfpSigningCommitments[i]?.signingNonceCommitments
30828
+ );
30829
+ directFromCpfpLeafSigningJobs.push(...signingJobs2);
30830
+ }
29537
30831
  }
29538
- return leafSigningJobs;
30832
+ return {
30833
+ cpfpLeafSigningJobs,
30834
+ directLeafSigningJobs,
30835
+ directFromCpfpLeafSigningJobs
30836
+ };
29539
30837
  }
29540
30838
  };
29541
30839
 
@@ -29543,7 +30841,7 @@ var SigningService = class {
29543
30841
  init_buffer();
29544
30842
  var import_utils22 = require("@noble/curves/abstract/utils");
29545
30843
  var import_secp256k114 = require("@noble/curves/secp256k1");
29546
- var btc6 = __toESM(require("@scure/btc-signer"), 1);
30844
+ var btc5 = __toESM(require("@scure/btc-signer"), 1);
29547
30845
  var import_btc_signer6 = require("@scure/btc-signer");
29548
30846
  var import_utils23 = require("@scure/btc-signer/utils");
29549
30847
  var STATIC_FAUCET_KEY = (0, import_utils22.hexToBytes)(
@@ -29689,7 +30987,7 @@ var BitcoinFaucet = class _BitcoinFaucet {
29689
30987
  }
29690
30988
  async sendFaucetCoinToP2WPKHAddress(pubKey) {
29691
30989
  const sendToPubKeyTx = new import_btc_signer6.Transaction();
29692
- const p2wpkhAddress = btc6.p2wpkh(pubKey, getNetwork(4 /* LOCAL */)).address;
30990
+ const p2wpkhAddress = btc5.p2wpkh(pubKey, getNetwork(4 /* LOCAL */)).address;
29693
30991
  if (!p2wpkhAddress) {
29694
30992
  throw new Error("Invalid P2WPKH address");
29695
30993
  }
@@ -29745,12 +31043,13 @@ var BitcoinFaucet = class _BitcoinFaucet {
29745
31043
  }
29746
31044
  async call(method, params) {
29747
31045
  try {
29748
- const response = await fetch(this.url, {
31046
+ const { fetch: fetch2, Headers: Headers2 } = getFetch();
31047
+ const response = await fetch2(this.url, {
29749
31048
  method: "POST",
29750
- headers: {
31049
+ headers: new Headers2({
29751
31050
  "Content-Type": "application/json",
29752
31051
  Authorization: "Basic " + btoa(`${this.username}:${this.password}`)
29753
- },
31052
+ }),
29754
31053
  body: JSON.stringify({
29755
31054
  jsonrpc: "1.0",
29756
31055
  id: "spark-js",
@@ -30080,31 +31379,37 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
30080
31379
  }
30081
31380
  }
30082
31381
  async getLeaves(isBalanceCheck = false) {
30083
- const leaves = await this.queryNodes({
30084
- source: {
30085
- $case: "ownerIdentityPubkey",
30086
- ownerIdentityPubkey: await this.config.signer.getIdentityPublicKey()
30087
- },
30088
- includeParents: false,
30089
- network: NetworkToProto[this.config.getNetwork()]
30090
- });
31382
+ const operatorToLeaves = /* @__PURE__ */ new Map();
31383
+ const ownerIdentityPubkey = await this.config.signer.getIdentityPublicKey();
31384
+ let signingOperators = Object.entries(this.config.getSigningOperators());
31385
+ if (isBalanceCheck) {
31386
+ signingOperators = signingOperators.filter(
31387
+ ([id, _]) => id === this.config.getCoordinatorIdentifier()
31388
+ );
31389
+ }
31390
+ await Promise.all(
31391
+ signingOperators.map(async ([id, operator]) => {
31392
+ const leaves2 = await this.queryNodes(
31393
+ {
31394
+ source: {
31395
+ $case: "ownerIdentityPubkey",
31396
+ ownerIdentityPubkey
31397
+ },
31398
+ includeParents: false,
31399
+ network: NetworkToProto[this.config.getNetwork()]
31400
+ },
31401
+ operator.address
31402
+ );
31403
+ operatorToLeaves.set(id, leaves2);
31404
+ })
31405
+ );
31406
+ const leaves = operatorToLeaves.get(
31407
+ this.config.getCoordinatorIdentifier()
31408
+ );
30091
31409
  const leavesToIgnore = /* @__PURE__ */ new Set();
30092
31410
  if (!isBalanceCheck) {
30093
- for (const [id, operator] of Object.entries(
30094
- this.config.getSigningOperators()
30095
- )) {
31411
+ for (const [id, operatorLeaves] of operatorToLeaves) {
30096
31412
  if (id !== this.config.getCoordinatorIdentifier()) {
30097
- const operatorLeaves = await this.queryNodes(
30098
- {
30099
- source: {
30100
- $case: "ownerIdentityPubkey",
30101
- ownerIdentityPubkey: await this.config.signer.getIdentityPublicKey()
30102
- },
30103
- includeParents: false,
30104
- network: NetworkToProto[this.config.getNetwork()]
30105
- },
30106
- operator.address
30107
- );
30108
31413
  for (const [nodeId, leaf] of Object.entries(leaves.nodes)) {
30109
31414
  const operatorLeaf = operatorLeaves.nodes[nodeId];
30110
31415
  if (!operatorLeaf) {
@@ -30545,21 +31850,68 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
30545
31850
  }
30546
31851
  }))
30547
31852
  );
30548
- const { transfer, signatureMap } = await this.transferService.startSwapSignRefund(
31853
+ const {
31854
+ transfer,
31855
+ signatureMap,
31856
+ directSignatureMap,
31857
+ directFromCpfpSignatureMap
31858
+ } = await this.transferService.startSwapSignRefund(
30549
31859
  leafKeyTweaks,
30550
31860
  (0, import_utils24.hexToBytes)(this.config.getSspIdentityPublicKey()),
30551
31861
  new Date(Date.now() + 2 * 60 * 1e3)
30552
31862
  );
30553
31863
  try {
30554
31864
  if (!transfer.leaves[0]?.leaf) {
31865
+ console.error("[processSwapBatch] First leaf is missing");
30555
31866
  throw new Error("Failed to get leaf");
30556
31867
  }
30557
- const refundSignature = signatureMap.get(transfer.leaves[0].leaf.id);
30558
- if (!refundSignature) {
30559
- throw new Error("Failed to get refund signature");
31868
+ const cpfpRefundSignature = signatureMap.get(transfer.leaves[0].leaf.id);
31869
+ if (!cpfpRefundSignature) {
31870
+ console.error(
31871
+ "[processSwapBatch] Missing CPFP refund signature for first leaf"
31872
+ );
31873
+ throw new Error("Failed to get CPFP refund signature");
31874
+ }
31875
+ const directRefundSignature = directSignatureMap.get(
31876
+ transfer.leaves[0].leaf.id
31877
+ );
31878
+ if (!directRefundSignature) {
31879
+ console.error(
31880
+ "[processSwapBatch] Missing direct refund signature for first leaf"
31881
+ );
31882
+ throw new Error("Failed to get direct refund signature");
31883
+ }
31884
+ const directFromCpfpRefundSignature = directFromCpfpSignatureMap.get(
31885
+ transfer.leaves[0].leaf.id
31886
+ );
31887
+ if (!directFromCpfpRefundSignature) {
31888
+ console.error(
31889
+ "[processSwapBatch] Missing direct from CPFP refund signature for first leaf"
31890
+ );
31891
+ throw new Error("Failed to get direct from CPFP refund signature");
31892
+ }
31893
+ const {
31894
+ adaptorPrivateKey: cpfpAdaptorPrivateKey,
31895
+ adaptorSignature: cpfpAdaptorSignature
31896
+ } = generateAdaptorFromSignature(cpfpRefundSignature);
31897
+ let directAdaptorPrivateKey = new Uint8Array();
31898
+ let directAdaptorSignature = new Uint8Array();
31899
+ let directFromCpfpAdaptorPrivateKey = new Uint8Array();
31900
+ let directFromCpfpAdaptorSignature = new Uint8Array();
31901
+ if (directRefundSignature.length > 0) {
31902
+ const { adaptorPrivateKey, adaptorSignature } = generateAdaptorFromSignature(directRefundSignature);
31903
+ directAdaptorPrivateKey = adaptorPrivateKey;
31904
+ directAdaptorSignature = adaptorSignature;
31905
+ }
31906
+ if (directFromCpfpRefundSignature.length > 0) {
31907
+ const { adaptorPrivateKey, adaptorSignature } = generateAdaptorFromSignature(directFromCpfpRefundSignature);
31908
+ directFromCpfpAdaptorPrivateKey = adaptorPrivateKey;
31909
+ directFromCpfpAdaptorSignature = adaptorSignature;
30560
31910
  }
30561
- const { adaptorPrivateKey, adaptorSignature } = generateAdaptorFromSignature(refundSignature);
30562
31911
  if (!transfer.leaves[0].leaf) {
31912
+ console.error(
31913
+ "[processSwapBatch] First leaf missing when preparing user leaves"
31914
+ );
30563
31915
  throw new Error("Failed to get leaf");
30564
31916
  }
30565
31917
  const userLeaves = [];
@@ -30568,37 +31920,113 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
30568
31920
  raw_unsigned_refund_transaction: (0, import_utils24.bytesToHex)(
30569
31921
  transfer.leaves[0].intermediateRefundTx
30570
31922
  ),
30571
- adaptor_added_signature: (0, import_utils24.bytesToHex)(adaptorSignature)
31923
+ direct_raw_unsigned_refund_transaction: (0, import_utils24.bytesToHex)(
31924
+ transfer.leaves[0].intermediateDirectRefundTx
31925
+ ),
31926
+ direct_from_cpfp_raw_unsigned_refund_transaction: (0, import_utils24.bytesToHex)(
31927
+ transfer.leaves[0].intermediateDirectFromCpfpRefundTx
31928
+ ),
31929
+ adaptor_added_signature: (0, import_utils24.bytesToHex)(cpfpAdaptorSignature),
31930
+ direct_adaptor_added_signature: (0, import_utils24.bytesToHex)(directAdaptorSignature),
31931
+ direct_from_cpfp_adaptor_added_signature: (0, import_utils24.bytesToHex)(
31932
+ directFromCpfpAdaptorSignature
31933
+ )
30572
31934
  });
30573
31935
  for (let i = 1; i < transfer.leaves.length; i++) {
30574
31936
  const leaf = transfer.leaves[i];
30575
31937
  if (!leaf?.leaf) {
31938
+ console.error(`[processSwapBatch] Leaf ${i + 1} is missing`);
30576
31939
  throw new Error("Failed to get leaf");
30577
31940
  }
30578
- const refundSignature2 = signatureMap.get(leaf.leaf.id);
30579
- if (!refundSignature2) {
30580
- throw new Error("Failed to get refund signature");
31941
+ const cpfpRefundSignature2 = signatureMap.get(leaf.leaf.id);
31942
+ if (!cpfpRefundSignature2) {
31943
+ console.error(
31944
+ `[processSwapBatch] Missing CPFP refund signature for leaf ${i + 1}`
31945
+ );
31946
+ throw new Error("Failed to get CPFP refund signature");
31947
+ }
31948
+ const directRefundSignature2 = directSignatureMap.get(leaf.leaf.id);
31949
+ if (!directRefundSignature2) {
31950
+ console.error(
31951
+ `[processSwapBatch] Missing direct refund signature for leaf ${i + 1}`
31952
+ );
31953
+ throw new Error("Failed to get direct refund signature");
31954
+ }
31955
+ const directFromCpfpRefundSignature2 = directFromCpfpSignatureMap.get(
31956
+ leaf.leaf.id
31957
+ );
31958
+ if (!directFromCpfpRefundSignature2) {
31959
+ console.error(
31960
+ `[processSwapBatch] Missing direct from CPFP refund signature for leaf ${i + 1}`
31961
+ );
31962
+ throw new Error("Failed to get direct from CPFP refund signature");
30581
31963
  }
30582
- const signature = generateSignatureFromExistingAdaptor(
30583
- refundSignature2,
30584
- adaptorPrivateKey
31964
+ const cpfpSignature = generateSignatureFromExistingAdaptor(
31965
+ cpfpRefundSignature2,
31966
+ cpfpAdaptorPrivateKey
30585
31967
  );
31968
+ let directSignature = new Uint8Array();
31969
+ if (directRefundSignature2.length > 0) {
31970
+ directSignature = generateSignatureFromExistingAdaptor(
31971
+ directRefundSignature2,
31972
+ directAdaptorPrivateKey
31973
+ );
31974
+ }
31975
+ let directFromCpfpSignature = new Uint8Array();
31976
+ if (directFromCpfpRefundSignature2.length > 0) {
31977
+ directFromCpfpSignature = generateSignatureFromExistingAdaptor(
31978
+ directFromCpfpRefundSignature2,
31979
+ directFromCpfpAdaptorPrivateKey
31980
+ );
31981
+ }
30586
31982
  userLeaves.push({
30587
31983
  leaf_id: leaf.leaf.id,
30588
31984
  raw_unsigned_refund_transaction: (0, import_utils24.bytesToHex)(
30589
31985
  leaf.intermediateRefundTx
30590
31986
  ),
30591
- adaptor_added_signature: (0, import_utils24.bytesToHex)(signature)
31987
+ direct_raw_unsigned_refund_transaction: (0, import_utils24.bytesToHex)(
31988
+ leaf.intermediateDirectRefundTx
31989
+ ),
31990
+ direct_from_cpfp_raw_unsigned_refund_transaction: (0, import_utils24.bytesToHex)(
31991
+ leaf.intermediateDirectFromCpfpRefundTx
31992
+ ),
31993
+ adaptor_added_signature: (0, import_utils24.bytesToHex)(cpfpSignature),
31994
+ direct_adaptor_added_signature: (0, import_utils24.bytesToHex)(directSignature),
31995
+ direct_from_cpfp_adaptor_added_signature: (0, import_utils24.bytesToHex)(
31996
+ directFromCpfpSignature
31997
+ )
30592
31998
  });
30593
31999
  }
30594
32000
  const sspClient = this.getSspClient();
30595
- const adaptorPubkey = (0, import_utils24.bytesToHex)(
30596
- import_secp256k115.secp256k1.getPublicKey(adaptorPrivateKey)
32001
+ const cpfpAdaptorPubkey = (0, import_utils24.bytesToHex)(
32002
+ import_secp256k115.secp256k1.getPublicKey(cpfpAdaptorPrivateKey)
30597
32003
  );
32004
+ if (!cpfpAdaptorPubkey) {
32005
+ throw new Error("Failed to generate CPFP adaptor pubkey");
32006
+ }
32007
+ let directAdaptorPubkey;
32008
+ if (directAdaptorPrivateKey.length > 0) {
32009
+ directAdaptorPubkey = (0, import_utils24.bytesToHex)(
32010
+ import_secp256k115.secp256k1.getPublicKey(directAdaptorPrivateKey)
32011
+ );
32012
+ }
32013
+ let directFromCpfpAdaptorPubkey;
32014
+ if (directFromCpfpAdaptorPrivateKey.length > 0) {
32015
+ directFromCpfpAdaptorPubkey = (0, import_utils24.bytesToHex)(
32016
+ import_secp256k115.secp256k1.getPublicKey(directFromCpfpAdaptorPrivateKey)
32017
+ );
32018
+ }
30598
32019
  let request = null;
32020
+ const targetAmountSats = targetAmounts?.reduce((acc, amount) => acc + amount, 0) || leavesBatch.reduce((acc, leaf) => acc + leaf.value, 0);
32021
+ const totalAmountSats = leavesBatch.reduce(
32022
+ (acc, leaf) => acc + leaf.value,
32023
+ 0
32024
+ );
30599
32025
  request = await sspClient.requestLeaveSwap({
30600
32026
  userLeaves,
30601
- adaptorPubkey,
32027
+ adaptorPubkey: cpfpAdaptorPubkey,
32028
+ directAdaptorPubkey,
32029
+ directFromCpfpAdaptorPubkey,
30602
32030
  targetAmountSats: targetAmounts?.reduce((acc, amount) => acc + amount, 0) || leavesBatch.reduce((acc, leaf) => acc + leaf.value, 0),
30603
32031
  totalAmountSats: leavesBatch.reduce((acc, leaf) => acc + leaf.value, 0),
30604
32032
  targetAmountSatsList: targetAmounts,
@@ -30607,6 +32035,7 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
30607
32035
  idempotencyKey: (0, import_uuidv75.uuidv7)()
30608
32036
  });
30609
32037
  if (!request) {
32038
+ console.error("[processSwapBatch] Leave swap request returned null");
30610
32039
  throw new Error("Failed to request leaves swap. No response returned.");
30611
32040
  }
30612
32041
  const nodes = await this.queryNodes({
@@ -30620,50 +32049,140 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
30620
32049
  network: NetworkToProto[this.config.getNetwork()]
30621
32050
  });
30622
32051
  if (Object.values(nodes.nodes).length !== request.swapLeaves.length) {
32052
+ console.error("[processSwapBatch] Node count mismatch:", {
32053
+ actual: Object.values(nodes.nodes).length,
32054
+ expected: request.swapLeaves.length
32055
+ });
30623
32056
  throw new Error("Expected same number of nodes as swapLeaves");
30624
32057
  }
30625
32058
  for (const [nodeId, node] of Object.entries(nodes.nodes)) {
30626
32059
  if (!node.nodeTx) {
32060
+ console.error(`[processSwapBatch] Node tx missing for ${nodeId}`);
30627
32061
  throw new Error(`Node tx not found for leaf ${nodeId}`);
30628
32062
  }
30629
32063
  if (!node.verifyingPublicKey) {
32064
+ console.error(
32065
+ `[processSwapBatch] Verifying public key missing for ${nodeId}`
32066
+ );
30630
32067
  throw new Error(`Node public key not found for leaf ${nodeId}`);
30631
32068
  }
30632
32069
  const leaf = request.swapLeaves.find((leaf2) => leaf2.leafId === nodeId);
30633
32070
  if (!leaf) {
32071
+ console.error(`[processSwapBatch] Leaf not found for node ${nodeId}`);
30634
32072
  throw new Error(`Leaf not found for node ${nodeId}`);
30635
32073
  }
30636
- const nodeTx = getTxFromRawTxBytes(node.nodeTx);
30637
- const refundTxBytes = (0, import_utils24.hexToBytes)(leaf.rawUnsignedRefundTransaction);
30638
- const refundTx = getTxFromRawTxBytes(refundTxBytes);
30639
- const sighash = getSigHashFromTx(refundTx, 0, nodeTx.getOutput(0));
32074
+ const cpfpNodeTx = getTxFromRawTxBytes(node.nodeTx);
32075
+ const cpfpRefundTxBytes = (0, import_utils24.hexToBytes)(leaf.rawUnsignedRefundTransaction);
32076
+ const cpfpRefundTx = getTxFromRawTxBytes(cpfpRefundTxBytes);
32077
+ const cpfpSighash = getSigHashFromTx(
32078
+ cpfpRefundTx,
32079
+ 0,
32080
+ cpfpNodeTx.getOutput(0)
32081
+ );
30640
32082
  const nodePublicKey = node.verifyingPublicKey;
30641
32083
  const taprootKey = computeTaprootKeyNoScript(nodePublicKey.slice(1));
30642
- const adaptorSignatureBytes = (0, import_utils24.hexToBytes)(leaf.adaptorSignedSignature);
32084
+ const cpfpAdaptorSignatureBytes = (0, import_utils24.hexToBytes)(
32085
+ leaf.adaptorSignedSignature
32086
+ );
32087
+ applyAdaptorToSignature(
32088
+ taprootKey.slice(1),
32089
+ cpfpSighash,
32090
+ cpfpAdaptorSignatureBytes,
32091
+ cpfpAdaptorPrivateKey
32092
+ );
32093
+ if (!leaf.directRawUnsignedRefundTransaction) {
32094
+ throw new Error(
32095
+ `Direct raw unsigned refund transaction missing for node ${nodeId}`
32096
+ );
32097
+ }
32098
+ if (!leaf.directAdaptorSignedSignature) {
32099
+ throw new Error(
32100
+ `Direct adaptor signed signature missing for node ${nodeId}`
32101
+ );
32102
+ }
32103
+ const directNodeTx = getTxFromRawTxBytes(node.directTx);
32104
+ const directRefundTxBytes = (0, import_utils24.hexToBytes)(
32105
+ leaf.directRawUnsignedRefundTransaction
32106
+ );
32107
+ const directRefundTx = getTxFromRawTxBytes(directRefundTxBytes);
32108
+ const directSighash = getSigHashFromTx(
32109
+ directRefundTx,
32110
+ 0,
32111
+ directNodeTx.getOutput(0)
32112
+ );
32113
+ if (!leaf.directFromCpfpAdaptorSignedSignature) {
32114
+ throw new Error(
32115
+ `Direct adaptor signed signature missing for node ${nodeId}`
32116
+ );
32117
+ }
32118
+ const directAdaptorSignatureBytes = (0, import_utils24.hexToBytes)(
32119
+ leaf.directAdaptorSignedSignature
32120
+ );
32121
+ applyAdaptorToSignature(
32122
+ taprootKey.slice(1),
32123
+ directSighash,
32124
+ directAdaptorSignatureBytes,
32125
+ directAdaptorPrivateKey
32126
+ );
32127
+ if (!leaf.directRawUnsignedRefundTransaction) {
32128
+ throw new Error(
32129
+ `Direct raw unsigned refund transaction missing for node ${nodeId}`
32130
+ );
32131
+ }
32132
+ if (!leaf.directFromCpfpRawUnsignedRefundTransaction) {
32133
+ throw new Error(
32134
+ `Direct raw unsigned refund transaction missing for node ${nodeId}`
32135
+ );
32136
+ }
32137
+ const directFromCpfpRefundTxBytes = (0, import_utils24.hexToBytes)(
32138
+ leaf.directFromCpfpRawUnsignedRefundTransaction
32139
+ );
32140
+ const directFromCpfpRefundTx = getTxFromRawTxBytes(
32141
+ directFromCpfpRefundTxBytes
32142
+ );
32143
+ const directFromCpfpSighash = getSigHashFromTx(
32144
+ directFromCpfpRefundTx,
32145
+ 0,
32146
+ cpfpNodeTx.getOutput(0)
32147
+ );
32148
+ const directFromCpfpAdaptorSignatureBytes = (0, import_utils24.hexToBytes)(
32149
+ leaf.directFromCpfpAdaptorSignedSignature
32150
+ );
30643
32151
  applyAdaptorToSignature(
30644
32152
  taprootKey.slice(1),
30645
- sighash,
30646
- adaptorSignatureBytes,
30647
- adaptorPrivateKey
32153
+ directFromCpfpSighash,
32154
+ directFromCpfpAdaptorSignatureBytes,
32155
+ directFromCpfpAdaptorPrivateKey
30648
32156
  );
30649
32157
  }
30650
32158
  await this.transferService.deliverTransferPackage(
30651
32159
  transfer,
30652
32160
  leafKeyTweaks,
30653
- signatureMap
32161
+ signatureMap,
32162
+ directSignatureMap,
32163
+ directFromCpfpSignatureMap
30654
32164
  );
30655
32165
  const completeResponse = await sspClient.completeLeaveSwap({
30656
- adaptorSecretKey: (0, import_utils24.bytesToHex)(adaptorPrivateKey),
32166
+ adaptorSecretKey: (0, import_utils24.bytesToHex)(cpfpAdaptorPrivateKey),
32167
+ directAdaptorSecretKey: (0, import_utils24.bytesToHex)(directAdaptorPrivateKey),
32168
+ directFromCpfpAdaptorSecretKey: (0, import_utils24.bytesToHex)(
32169
+ directFromCpfpAdaptorPrivateKey
32170
+ ),
30657
32171
  userOutboundTransferExternalId: transfer.id,
30658
32172
  leavesSwapRequestId: request.id
30659
32173
  });
30660
32174
  if (!completeResponse || !completeResponse.inboundTransfer?.sparkId) {
32175
+ console.error(
32176
+ "[processSwapBatch] Invalid complete response:",
32177
+ completeResponse
32178
+ );
30661
32179
  throw new Error("Failed to complete leaves swap");
30662
32180
  }
30663
32181
  const incomingTransfer = await this.transferService.queryTransfer(
30664
32182
  completeResponse.inboundTransfer.sparkId
30665
32183
  );
30666
32184
  if (!incomingTransfer) {
32185
+ console.error("[processSwapBatch] No incoming transfer found");
30667
32186
  throw new Error("Failed to get incoming transfer");
30668
32187
  }
30669
32188
  return await this.claimTransfer({
@@ -30673,6 +32192,11 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
30673
32192
  optimize: false
30674
32193
  });
30675
32194
  } catch (e) {
32195
+ console.error("[processSwapBatch] Error details:", {
32196
+ error: e,
32197
+ message: e.message,
32198
+ stack: e.stack
32199
+ });
30676
32200
  await this.cancelAllSenderInitiatedTransfers();
30677
32201
  throw new Error(`Failed to request leaves swap: ${e}`);
30678
32202
  }
@@ -31143,8 +32667,9 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
31143
32667
  field: "txid"
31144
32668
  });
31145
32669
  }
32670
+ const { fetch: fetch2, Headers: Headers2 } = getFetch();
31146
32671
  const baseUrl = this.config.getElectrsUrl();
31147
- const headers = {};
32672
+ const headers = new Headers2();
31148
32673
  let txHex;
31149
32674
  if (this.config.getNetwork() === 4 /* LOCAL */) {
31150
32675
  const localFaucet = BitcoinFaucet.getInstance();
@@ -31155,9 +32680,9 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
31155
32680
  const auth = btoa(
31156
32681
  `${ELECTRS_CREDENTIALS.username}:${ELECTRS_CREDENTIALS.password}`
31157
32682
  );
31158
- headers["Authorization"] = `Basic ${auth}`;
32683
+ headers.set("Authorization", `Basic ${auth}`);
31159
32684
  }
31160
- const response = await fetch(`${baseUrl}/tx/${txid}/hex`, {
32685
+ const response = await fetch2(`${baseUrl}/tx/${txid}/hex`, {
31161
32686
  headers
31162
32687
  });
31163
32688
  txHex = await response.text();
@@ -31285,8 +32810,9 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
31285
32810
  this.mutexes.set(txid, mutex);
31286
32811
  }
31287
32812
  const nodes = await mutex.runExclusive(async () => {
32813
+ const { fetch: fetch2, Headers: Headers2 } = getFetch();
31288
32814
  const baseUrl = this.config.getElectrsUrl();
31289
- const headers = {};
32815
+ const headers = new Headers2();
31290
32816
  let txHex;
31291
32817
  if (this.config.getNetwork() === 4 /* LOCAL */) {
31292
32818
  const localFaucet = BitcoinFaucet.getInstance();
@@ -31297,9 +32823,9 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
31297
32823
  const auth = btoa(
31298
32824
  `${ELECTRS_CREDENTIALS.username}:${ELECTRS_CREDENTIALS.password}`
31299
32825
  );
31300
- headers["Authorization"] = `Basic ${auth}`;
32826
+ headers.set("Authorization", `Basic ${auth}`);
31301
32827
  }
31302
- const response = await fetch(`${baseUrl}/tx/${txid}/hex`, {
32828
+ const response = await fetch2(`${baseUrl}/tx/${txid}/hex`, {
31303
32829
  headers
31304
32830
  });
31305
32831
  txHex = await response.text();
@@ -31541,10 +33067,16 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
31541
33067
  const validNodes = [];
31542
33068
  for (const node of nodes) {
31543
33069
  const nodeTx = getTxFromRawTxBytes(node.nodeTx);
31544
- const { needRefresh } = getNextTransactionSequence(
31545
- nodeTx.getInput(0).sequence
31546
- );
31547
- if (needRefresh) {
33070
+ const sequence = nodeTx.getInput(0).sequence;
33071
+ if (!sequence) {
33072
+ throw new ValidationError("Invalid node transaction", {
33073
+ field: "sequence",
33074
+ value: nodeTx.getInput(0),
33075
+ expected: "Non-null sequence"
33076
+ });
33077
+ }
33078
+ const needsRefresh = doesLeafNeedRefresh(sequence, true);
33079
+ if (needsRefresh) {
31548
33080
  nodesToExtend.push(node);
31549
33081
  nodeIds.push(node.id);
31550
33082
  } else {
@@ -31581,11 +33113,16 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
31581
33113
  const validNodes = [];
31582
33114
  for (const node of nodes) {
31583
33115
  const refundTx = getTxFromRawTxBytes(node.refundTx);
31584
- const { needRefresh } = getNextTransactionSequence(
31585
- refundTx.getInput(0).sequence,
31586
- true
31587
- );
31588
- if (needRefresh) {
33116
+ const sequence = refundTx.getInput(0).sequence;
33117
+ if (!sequence) {
33118
+ throw new ValidationError("Invalid refund transaction", {
33119
+ field: "sequence",
33120
+ value: refundTx.getInput(0),
33121
+ expected: "Non-null sequence"
33122
+ });
33123
+ }
33124
+ const needsRefresh = doesLeafNeedRefresh(sequence);
33125
+ if (needsRefresh) {
31589
33126
  nodesToRefresh.push(node);
31590
33127
  nodeIds.push(node.id);
31591
33128
  } else {
@@ -31619,7 +33156,7 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
31619
33156
  throw new Error(`parent node ${node.parentNodeId} not found`);
31620
33157
  }
31621
33158
  const { nodes: nodes2 } = await this.transferService.refreshTimelockNodes(
31622
- [node],
33159
+ node,
31623
33160
  parentNode
31624
33161
  );
31625
33162
  if (nodes2.length !== 1) {
@@ -31668,7 +33205,9 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
31668
33205
  leavesToClaim.push({
31669
33206
  leaf: {
31670
33207
  ...leaf.leaf,
31671
- refundTx: leaf.intermediateRefundTx
33208
+ refundTx: leaf.intermediateRefundTx,
33209
+ directRefundTx: leaf.intermediateDirectRefundTx,
33210
+ directFromCpfpRefundTx: leaf.intermediateDirectFromCpfpRefundTx
31672
33211
  },
31673
33212
  keyDerivation: {
31674
33213
  type: "ecies" /* ECIES */,
@@ -31744,7 +33283,7 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
31744
33283
  if (type && transfer.type !== type) {
31745
33284
  continue;
31746
33285
  }
31747
- if (transfer.status !== 2 /* TRANSFER_STATUS_SENDER_KEY_TWEAKED */ && transfer.status !== 3 /* TRANSFER_STATUS_RECEIVER_KEY_TWEAKED */ && transfer.status !== 4 /* TRANSFER_STATUS_RECEIVER_REFUND_SIGNED */ && transfer.status !== 10 /* TRANSFER_STATUS_RECEIVER_KEY_TWEAK_APPLIED */) {
33286
+ if (transfer.status !== 2 /* TRANSFER_STATUS_SENDER_KEY_TWEAKED */ && transfer.status !== 3 /* TRANSFER_STATUS_RECEIVER_KEY_TWEAKED */ && transfer.status !== 4 /* TRANSFER_STATUS_RECEIVER_REFUND_SIGNED */ && transfer.status !== 10 /* TRANSFER_STATUS_RECEIVER_KEY_TWEAK_APPLIED */ && transfer.status !== 9 /* TRANSFER_STATUS_RECEIVER_KEY_TWEAK_LOCKED */) {
31748
33287
  continue;
31749
33288
  }
31750
33289
  promises.push(
@@ -32032,6 +33571,8 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
32032
33571
  await this.transferService.deliverTransferPackage(
32033
33572
  swapResponse.transfer,
32034
33573
  leavesToSend,
33574
+ /* @__PURE__ */ new Map(),
33575
+ /* @__PURE__ */ new Map(),
32035
33576
  /* @__PURE__ */ new Map()
32036
33577
  );
32037
33578
  const sspResponse = await sspClient.requestLightningSend({
@@ -32794,87 +34335,64 @@ var SparkWallet = class _SparkWallet extends import_eventemitter3.EventEmitter {
32794
34335
  },
32795
34336
  includeParents: true
32796
34337
  });
32797
- const node = response.nodes[nodeId];
32798
- if (!node) {
34338
+ let leaf = response.nodes[nodeId];
34339
+ if (!leaf) {
32799
34340
  throw new ValidationError("Node not found", {
32800
34341
  field: "nodeId",
32801
34342
  value: nodeId
32802
34343
  });
32803
34344
  }
32804
- if (!node.parentNodeId) {
32805
- throw new ValidationError("Node has no parent", {
32806
- field: "parentNodeId",
32807
- value: node.parentNodeId
32808
- });
32809
- }
32810
- const parentNode = response.nodes[node.parentNodeId];
32811
- if (!parentNode) {
32812
- throw new ValidationError("Parent node not found", {
32813
- field: "parentNodeId",
32814
- value: node.parentNodeId
32815
- });
32816
- }
32817
- const result = await this.transferService.refreshTimelockNodes(
32818
- [node],
32819
- parentNode
32820
- );
32821
- const leafIndex = this.leaves.findIndex((leaf) => leaf.id === node.id);
32822
- if (leafIndex !== -1 && result.nodes.length > 0) {
32823
- const newNode = result.nodes[0];
32824
- if (newNode) {
32825
- this.leaves[leafIndex] = newNode;
34345
+ let parentNode;
34346
+ let hasParentNode = false;
34347
+ if (!leaf.parentNodeId) {
34348
+ } else {
34349
+ hasParentNode = true;
34350
+ parentNode = response.nodes[leaf.parentNodeId];
34351
+ if (!parentNode) {
34352
+ throw new ValidationError("Parent node not found", {
34353
+ field: "parentNodeId",
34354
+ value: leaf.parentNodeId
34355
+ });
32826
34356
  }
32827
34357
  }
32828
- } catch (error) {
32829
- throw new NetworkError(
32830
- "Failed to refresh timelock",
32831
- {
32832
- method: "refresh_timelock"
32833
- },
32834
- error
32835
- );
32836
- }
32837
- }
32838
- /**
32839
- * Refresh the timelock of a specific node's refund transaction only.
32840
- *
32841
- * @param {string} nodeId - The ID of the node whose refund transaction to refresh
32842
- * @returns {Promise<void>} Promise that resolves when the refund timelock is refreshed
32843
- */
32844
- async testOnly_expireTimelockRefundTx(nodeId) {
32845
- const sparkClient = await this.connectionManager.createSparkClient(
32846
- this.config.getCoordinatorAddress()
32847
- );
32848
- try {
32849
- const response = await sparkClient.query_nodes({
32850
- source: {
32851
- $case: "nodeIds",
32852
- nodeIds: {
32853
- nodeIds: [nodeId]
34358
+ const nodeTx = getTxFromRawTxBytes(leaf.nodeTx);
34359
+ const refundTx = getTxFromRawTxBytes(leaf.refundTx);
34360
+ if (hasParentNode) {
34361
+ const nodeTimelock = getCurrentTimelock(nodeTx.getInput(0).sequence);
34362
+ if (nodeTimelock > 100) {
34363
+ const expiredNodeTxLeaf = await this.transferService.testonly_expireTimeLockNodeTx(
34364
+ leaf,
34365
+ parentNode
34366
+ );
34367
+ if (!expiredNodeTxLeaf.nodes[0]) {
34368
+ throw new ValidationError("No expired node tx leaf", {
34369
+ field: "expiredNodeTxLeaf",
34370
+ value: expiredNodeTxLeaf
34371
+ });
32854
34372
  }
32855
- },
32856
- includeParents: false
32857
- });
32858
- const node = response.nodes[nodeId];
32859
- if (!node) {
32860
- throw new ValidationError("Node not found", {
32861
- field: "nodeId",
32862
- value: nodeId
32863
- });
34373
+ leaf = expiredNodeTxLeaf.nodes[0];
34374
+ }
32864
34375
  }
32865
- const result = await this.transferService.refreshTimelockRefundTx(node);
32866
- const leafIndex = this.leaves.findIndex((leaf) => leaf.id === node.id);
32867
- if (leafIndex !== -1 && result.nodes.length > 0) {
32868
- const newNode = result.nodes[0];
32869
- if (newNode) {
32870
- this.leaves[leafIndex] = newNode;
34376
+ const refundTimelock = getCurrentTimelock(refundTx.getInput(0).sequence);
34377
+ if (refundTimelock > 100) {
34378
+ const expiredTxLeaf = await this.transferService.testonly_expireTimeLockRefundtx(leaf);
34379
+ if (!expiredTxLeaf.nodes[0]) {
34380
+ throw new ValidationError("No expired tx leaf", {
34381
+ field: "expiredTxLeaf",
34382
+ value: expiredTxLeaf
34383
+ });
32871
34384
  }
34385
+ leaf = expiredTxLeaf.nodes[0];
34386
+ }
34387
+ const leafIndex = this.leaves.findIndex((leaf2) => leaf2.id === leaf2.id);
34388
+ if (leafIndex !== -1) {
34389
+ this.leaves[leafIndex] = leaf;
32872
34390
  }
32873
34391
  } catch (error) {
32874
34392
  throw new NetworkError(
32875
- "Failed to refresh refund timelock",
34393
+ "Failed to refresh timelock",
32876
34394
  {
32877
- method: "refresh_timelock_refund_tx"
34395
+ method: "refresh_timelock"
32878
34396
  },
32879
34397
  error
32880
34398
  );
@@ -32945,7 +34463,10 @@ setCrypto(cryptoImpl2);
32945
34463
  AuthenticationError,
32946
34464
  ConfigurationError,
32947
34465
  DEFAULT_FEE_SATS,
34466
+ DIRECT_TIMELOCK_OFFSET,
32948
34467
  DefaultSparkSigner,
34468
+ INITIAL_DIRECT_SEQUENCE,
34469
+ INITIAL_SEQUENCE,
32949
34470
  InternalValidationError,
32950
34471
  KeyDerivationType,
32951
34472
  LRC_WALLET_NETWORK,
@@ -32958,6 +34479,8 @@ setCrypto(cryptoImpl2);
32958
34479
  RPCError,
32959
34480
  SparkSDKError,
32960
34481
  SparkWallet,
34482
+ TEST_UNILATERAL_DIRECT_SEQUENCE,
34483
+ TEST_UNILATERAL_SEQUENCE,
32961
34484
  TaprootOutputKeysGenerator,
32962
34485
  TaprootSparkSigner,
32963
34486
  TokenTransactionService,
@@ -32977,13 +34500,21 @@ setCrypto(cryptoImpl2);
32977
34500
  constructFeeBumpTx,
32978
34501
  constructUnilateralExitFeeBumpPackages,
32979
34502
  constructUnilateralExitTxs,
34503
+ createConnectorRefundTransactions,
34504
+ createLeafNodeTx,
34505
+ createNodeTx,
34506
+ createNodeTxs,
32980
34507
  createRefundTx,
34508
+ createRefundTxs,
34509
+ createRootTx,
32981
34510
  createSigningCommitment,
32982
34511
  createSigningNonce,
34512
+ createSplitTx,
32983
34513
  decodeBech32mTokenIdentifier,
32984
34514
  decodeBytesToSigningCommitment,
32985
34515
  decodeBytesToSigningNonce,
32986
34516
  decodeSparkAddress,
34517
+ doesLeafNeedRefresh,
32987
34518
  encodeBech32mTokenIdentifier,
32988
34519
  encodeSigningCommitmentToBytes,
32989
34520
  encodeSigningNonceToBytes,