@buildonspark/spark-sdk 0.2.2 → 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 (94) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/{chunk-TM6CHQXC.js → chunk-3SEOTO43.js} +1 -1
  3. package/dist/{chunk-2ENZX6LT.js → chunk-AAZWSPUK.js} +84 -8
  4. package/dist/{chunk-4JD4HIAN.js → chunk-G4MSZ6DE.js} +299 -1
  5. package/dist/{chunk-S2AL73MZ.js → chunk-TVUMSHWA.js} +1 -1
  6. package/dist/{chunk-2TUM3R6C.js → chunk-W4ZRBSWM.js} +2351 -797
  7. package/dist/{chunk-CDLETEDT.js → chunk-WAQKYSDI.js} +13 -1
  8. package/dist/{client-CGTRS23n.d.ts → client-BF4cn8F4.d.ts} +15 -3
  9. package/dist/{client-CcYzmpmj.d.cts → client-KhNkrXz4.d.cts} +15 -3
  10. package/dist/debug.cjs +2948 -1023
  11. package/dist/debug.d.cts +19 -6
  12. package/dist/debug.d.ts +19 -6
  13. package/dist/debug.js +5 -5
  14. package/dist/graphql/objects/index.cjs +13 -1
  15. package/dist/graphql/objects/index.d.cts +2 -2
  16. package/dist/graphql/objects/index.d.ts +2 -2
  17. package/dist/graphql/objects/index.js +1 -1
  18. package/dist/index.cjs +2794 -858
  19. package/dist/index.d.cts +190 -9
  20. package/dist/index.d.ts +190 -9
  21. package/dist/index.js +32 -6
  22. package/dist/index.node.cjs +2931 -892
  23. package/dist/index.node.d.cts +10 -188
  24. package/dist/index.node.d.ts +10 -188
  25. package/dist/index.node.js +134 -6
  26. package/dist/native/index.cjs +2794 -858
  27. package/dist/native/index.d.cts +148 -40
  28. package/dist/native/index.d.ts +148 -40
  29. package/dist/native/index.js +2799 -877
  30. package/dist/proto/lrc20.d.cts +1 -1
  31. package/dist/proto/lrc20.d.ts +1 -1
  32. package/dist/proto/lrc20.js +1 -1
  33. package/dist/proto/spark.cjs +84 -8
  34. package/dist/proto/spark.d.cts +1 -1
  35. package/dist/proto/spark.d.ts +1 -1
  36. package/dist/proto/spark.js +1 -1
  37. package/dist/proto/spark_token.cjs +301 -0
  38. package/dist/proto/spark_token.d.cts +35 -2
  39. package/dist/proto/spark_token.d.ts +35 -2
  40. package/dist/proto/spark_token.js +8 -2
  41. package/dist/{sdk-types-DJ2ve9YY.d.cts → sdk-types-CB9HrW5O.d.cts} +1 -1
  42. package/dist/{sdk-types-DCIVdKUT.d.ts → sdk-types-CkRNraXT.d.ts} +1 -1
  43. package/dist/{spark-BUOx3U7Q.d.cts → spark-B_7nZx6T.d.cts} +112 -10
  44. package/dist/{spark-BUOx3U7Q.d.ts → spark-B_7nZx6T.d.ts} +112 -10
  45. package/dist/{spark-wallet-B_96y9BS.d.ts → spark-wallet-C1Tr_VKI.d.ts} +38 -28
  46. package/dist/{spark-wallet-CHwKQYJu.d.cts → spark-wallet-DG3x2obf.d.cts} +38 -28
  47. package/dist/spark-wallet.node-CGxoeCpH.d.ts +13 -0
  48. package/dist/spark-wallet.node-CN9LoB_O.d.cts +13 -0
  49. package/dist/tests/test-utils.cjs +1086 -218
  50. package/dist/tests/test-utils.d.cts +13 -13
  51. package/dist/tests/test-utils.d.ts +13 -13
  52. package/dist/tests/test-utils.js +56 -19
  53. package/dist/types/index.cjs +97 -9
  54. package/dist/types/index.d.cts +3 -3
  55. package/dist/types/index.d.ts +3 -3
  56. package/dist/types/index.js +3 -3
  57. package/dist/{xchain-address-D5MIHCDL.d.cts → xchain-address-BHu6CpZC.d.ts} +55 -8
  58. package/dist/{xchain-address-DLbW1iDh.d.ts → xchain-address-HBr6isnc.d.cts} +55 -8
  59. package/package.json +1 -1
  60. package/src/graphql/client.ts +8 -0
  61. package/src/graphql/mutations/CompleteLeavesSwap.ts +9 -1
  62. package/src/graphql/mutations/RequestSwapLeaves.ts +4 -0
  63. package/src/graphql/objects/CompleteLeavesSwapInput.ts +34 -34
  64. package/src/graphql/objects/LeavesSwapRequest.ts +4 -0
  65. package/src/graphql/objects/RequestLeavesSwapInput.ts +48 -47
  66. package/src/graphql/objects/SwapLeaf.ts +40 -32
  67. package/src/graphql/objects/UserLeafInput.ts +24 -0
  68. package/src/graphql/objects/UserRequest.ts +4 -0
  69. package/src/index.node.ts +1 -1
  70. package/src/native/index.ts +4 -5
  71. package/src/proto/spark.ts +172 -16
  72. package/src/proto/spark_token.ts +369 -0
  73. package/src/services/coop-exit.ts +171 -36
  74. package/src/services/deposit.ts +471 -74
  75. package/src/services/lightning.ts +18 -5
  76. package/src/services/signing.ts +162 -50
  77. package/src/services/token-transactions.ts +6 -2
  78. package/src/services/transfer.ts +950 -384
  79. package/src/services/tree-creation.ts +342 -121
  80. package/src/spark-wallet/spark-wallet.node.ts +71 -66
  81. package/src/spark-wallet/spark-wallet.ts +459 -166
  82. package/src/tests/integration/coop-exit.test.ts +3 -8
  83. package/src/tests/integration/deposit.test.ts +3 -3
  84. package/src/tests/integration/lightning.test.ts +521 -466
  85. package/src/tests/integration/swap.test.ts +559 -307
  86. package/src/tests/integration/transfer.test.ts +625 -623
  87. package/src/tests/integration/wallet.test.ts +2 -2
  88. package/src/tests/integration/watchtower.test.ts +211 -0
  89. package/src/tests/test-utils.ts +63 -14
  90. package/src/tests/utils/test-faucet.ts +4 -2
  91. package/src/utils/adaptor-signature.ts +15 -5
  92. package/src/utils/fetch.ts +75 -0
  93. package/src/utils/mempool.ts +9 -4
  94. package/src/utils/transaction.ts +388 -26
@@ -16,19 +16,24 @@ import {
16
16
  TreeNode,
17
17
  } from "../proto/spark.js";
18
18
  import {
19
+ KeyDerivation,
19
20
  KeyDerivationType,
20
21
  SigningCommitmentWithOptionalNonce,
21
22
  } from "../signer/types.js";
22
23
  import {
23
- getP2TRAddressFromPublicKey,
24
24
  getSigHashFromTx,
25
25
  getTxFromRawTxBytes,
26
26
  getTxId,
27
27
  } from "../utils/bitcoin.js";
28
28
  import { getNetwork, Network } from "../utils/network.js";
29
29
  import {
30
+ createLeafNodeTx,
31
+ createNodeTxs,
32
+ createRefundTxs,
33
+ createSplitTx,
30
34
  DEFAULT_FEE_SATS,
31
- getEphemeralAnchorOutput,
35
+ INITIAL_DIRECT_SEQUENCE,
36
+ INITIAL_SEQUENCE,
32
37
  } from "../utils/transaction.js";
33
38
  import { WalletConfigService } from "./config.js";
34
39
  import { ConnectionManager } from "./connection.js";
@@ -41,8 +46,20 @@ export type DepositAddressTree = {
41
46
  };
42
47
 
43
48
  export type CreationNodeWithNonces = CreationNode & {
49
+ directNodeTxSigningCommitment?:
50
+ | SigningCommitmentWithOptionalNonce
51
+ | undefined;
44
52
  nodeTxSigningCommitment?: SigningCommitmentWithOptionalNonce | undefined;
45
53
  refundTxSigningCommitment?: SigningCommitmentWithOptionalNonce | undefined;
54
+ directRefundTxSigningCommitment?:
55
+ | SigningCommitmentWithOptionalNonce
56
+ | undefined;
57
+ directFromCpfpRefundTxSigningCommitment?:
58
+ | SigningCommitmentWithOptionalNonce
59
+ | undefined;
60
+ directNodeTxSigningJob?: SigningJob | undefined;
61
+ directRefundTxSigningJob?: SigningJob | undefined;
62
+ directFromCpfpRefundTxSigningJob?: SigningJob | undefined;
46
63
  };
47
64
 
48
65
  const INITIAL_TIME_LOCK = 2000;
@@ -205,7 +222,7 @@ export class TreeCreationService {
205
222
 
206
223
  let response: CreateTreeResponse;
207
224
  try {
208
- response = await sparkClient.create_tree(request);
225
+ response = await sparkClient.create_tree_v2(request);
209
226
  } catch (error) {
210
227
  throw new Error(`Error creating tree: ${error}`);
211
228
  }
@@ -226,7 +243,7 @@ export class TreeCreationService {
226
243
 
227
244
  let finalizeResp: FinalizeNodeSignaturesResponse;
228
245
  try {
229
- finalizeResp = await sparkClient.finalize_node_signatures({
246
+ finalizeResp = await sparkClient.finalize_node_signatures_v2({
230
247
  nodeSignatures: nodeSignatures,
231
248
  });
232
249
  } catch (error) {
@@ -318,111 +335,154 @@ export class TreeCreationService {
318
335
  // internal node
319
336
  const internalCreationNode: CreationNodeWithNonces = {
320
337
  nodeTxSigningJob: undefined,
321
- refundTxSigningJob: undefined,
322
- children: [],
323
338
  directNodeTxSigningJob: undefined,
339
+ refundTxSigningJob: undefined,
324
340
  directRefundTxSigningJob: undefined,
325
341
  directFromCpfpRefundTxSigningJob: undefined,
342
+ children: [],
326
343
  };
327
344
 
328
- const tx = new Transaction({ version: 3 });
329
- tx.addInput({
330
- txid: getTxId(parentTx),
331
- index: vout,
332
- });
333
-
334
345
  const parentTxOut = parentTx.getOutput(vout);
335
346
  if (!parentTxOut?.script || !parentTxOut?.amount) {
336
347
  throw new Error("parentTxOut is undefined");
337
348
  }
338
349
 
339
- tx.addOutput({
350
+ const parentOutPoint = {
351
+ txid: hexToBytes(getTxId(parentTx)),
352
+ index: vout,
353
+ };
354
+ const parentTxOutObj = {
340
355
  script: parentTxOut.script,
341
- amount: parentTxOut.amount, // maybeApplyFee(parentTxOut.amount),
342
- });
356
+ amount: parentTxOut.amount,
357
+ };
343
358
 
344
- tx.addOutput(getEphemeralAnchorOutput());
359
+ // Create both CPFP and direct node transactions
360
+ const { cpfpNodeTx, directNodeTx } = createNodeTxs(
361
+ parentTxOutObj,
362
+ parentOutPoint,
363
+ );
345
364
 
346
- const signingNonceCommitment =
365
+ // Create nonce commitments for node transactions
366
+ const cpfpNodeSigningCommitment =
367
+ await this.config.signer.getRandomSigningCommitment();
368
+ const directNodeSigningCommitment =
347
369
  await this.config.signer.getRandomSigningCommitment();
348
- const signingJob: SigningJob = {
370
+
371
+ const cpfpNodeSigningJob: SigningJob = {
349
372
  signingPublicKey: node.signingPublicKey,
350
- rawTx: tx.toBytes(),
351
- signingNonceCommitment: signingNonceCommitment.commitment,
373
+ rawTx: cpfpNodeTx.toBytes(),
374
+ signingNonceCommitment: cpfpNodeSigningCommitment.commitment,
352
375
  };
353
-
354
- internalCreationNode.nodeTxSigningCommitment = signingNonceCommitment;
355
- internalCreationNode.nodeTxSigningJob = signingJob;
376
+ const directNodeSigningJob: SigningJob | undefined = directNodeTx
377
+ ? {
378
+ signingPublicKey: node.signingPublicKey,
379
+ rawTx: directNodeTx.toBytes(),
380
+ signingNonceCommitment: directNodeSigningCommitment.commitment,
381
+ }
382
+ : undefined;
383
+
384
+ internalCreationNode.nodeTxSigningCommitment = cpfpNodeSigningCommitment;
385
+ internalCreationNode.directNodeTxSigningCommitment =
386
+ directNodeSigningCommitment;
387
+ internalCreationNode.nodeTxSigningJob = cpfpNodeSigningJob;
388
+ internalCreationNode.directNodeTxSigningJob = directNodeSigningJob;
356
389
 
357
390
  // leaf node
358
- const sequence = (1 << 30) | INITIAL_TIME_LOCK;
391
+ const sequence = INITIAL_SEQUENCE;
392
+ const directSequence = INITIAL_DIRECT_SEQUENCE;
359
393
 
360
394
  const childCreationNode: CreationNodeWithNonces = {
361
395
  nodeTxSigningJob: undefined,
362
- refundTxSigningJob: undefined,
363
- children: [],
364
396
  directNodeTxSigningJob: undefined,
397
+ refundTxSigningJob: undefined,
365
398
  directRefundTxSigningJob: undefined,
366
399
  directFromCpfpRefundTxSigningJob: undefined,
400
+ children: [],
367
401
  };
368
402
 
369
- const childTx = new Transaction({ version: 3 });
370
- childTx.addInput({
371
- txid: getTxId(tx),
372
- index: 0,
403
+ // Create both CPFP and direct leaf node transactions
404
+ const [cpfpLeafTx, directLeafTx] = createLeafNodeTx(
373
405
  sequence,
374
- });
375
-
376
- childTx.addOutput({
377
- script: parentTxOut.script,
378
- amount: parentTxOut.amount, // maybeApplyFee(parentTxOut.amount),
379
- });
380
-
381
- childTx.addOutput(getEphemeralAnchorOutput());
406
+ directSequence,
407
+ { txid: hexToBytes(getTxId(cpfpNodeTx)), index: 0 },
408
+ parentTxOutObj,
409
+ true, // shouldCalculateFee
410
+ );
382
411
 
383
- const childSigningNonceCommitment =
412
+ // Create nonce commitments for leaf node transactions
413
+ const cpfpLeafSigningCommitment =
384
414
  await this.config.signer.getRandomSigningCommitment();
385
- const childSigningJob: SigningJob = {
415
+ const directLeafSigningCommitment =
416
+ await this.config.signer.getRandomSigningCommitment();
417
+
418
+ const cpfpLeafSigningJob: SigningJob = {
386
419
  signingPublicKey: node.signingPublicKey,
387
- rawTx: childTx.toBytes(),
388
- signingNonceCommitment: childSigningNonceCommitment.commitment,
420
+ rawTx: cpfpLeafTx.toBytes(),
421
+ signingNonceCommitment: cpfpLeafSigningCommitment.commitment,
422
+ };
423
+ const directLeafSigningJob: SigningJob = {
424
+ signingPublicKey: node.signingPublicKey,
425
+ rawTx: directLeafTx.toBytes(),
426
+ signingNonceCommitment: directLeafSigningCommitment.commitment,
389
427
  };
390
428
 
391
- childCreationNode.nodeTxSigningCommitment = childSigningNonceCommitment;
392
- childCreationNode.nodeTxSigningJob = childSigningJob;
393
-
394
- const refundTx = new Transaction({ version: 3 });
395
- refundTx.addInput({
396
- txid: getTxId(childTx),
397
- index: 0,
398
- sequence,
399
- });
400
-
401
- const refundP2trAddress = getP2TRAddressFromPublicKey(
402
- node.signingPublicKey,
403
- network,
404
- );
405
- const refundAddress = Address(getNetwork(network)).decode(
406
- refundP2trAddress,
407
- );
408
- const refundPkScript = OutScript.encode(refundAddress);
409
- refundTx.addOutput({
410
- script: refundPkScript,
411
- amount: maybeApplyFee(parentTxOut.amount),
412
- });
413
-
414
- refundTx.addOutput(getEphemeralAnchorOutput());
429
+ childCreationNode.nodeTxSigningCommitment = cpfpLeafSigningCommitment;
430
+ childCreationNode.directNodeTxSigningCommitment =
431
+ directLeafSigningCommitment;
432
+ childCreationNode.nodeTxSigningJob = cpfpLeafSigningJob;
433
+ childCreationNode.directNodeTxSigningJob = directLeafSigningJob;
434
+
435
+ // Create both CPFP and direct refund transactions
436
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } =
437
+ createRefundTxs({
438
+ sequence,
439
+ directSequence,
440
+ input: { txid: hexToBytes(getTxId(cpfpLeafTx)), index: 0 },
441
+ directInput: { txid: hexToBytes(getTxId(directLeafTx)), index: 0 },
442
+ amountSats: parentTxOut.amount,
443
+ receivingPubkey: node.signingPublicKey,
444
+ network,
445
+ });
415
446
 
416
- const refundSigningNonceCommitment =
447
+ // Create nonce commitments for refund transactions
448
+ const cpfpRefundSigningCommitment =
449
+ await this.config.signer.getRandomSigningCommitment();
450
+ const directRefundSigningCommitment =
451
+ await this.config.signer.getRandomSigningCommitment();
452
+ const directFromCpfpRefundSigningCommitment =
417
453
  await this.config.signer.getRandomSigningCommitment();
418
454
 
419
- const refundSigningJob: SigningJob = {
455
+ const cpfpRefundSigningJob: SigningJob = {
420
456
  signingPublicKey: node.signingPublicKey,
421
- rawTx: refundTx.toBytes(),
422
- signingNonceCommitment: refundSigningNonceCommitment.commitment,
457
+ rawTx: cpfpRefundTx.toBytes(),
458
+ signingNonceCommitment: cpfpRefundSigningCommitment.commitment,
423
459
  };
424
- childCreationNode.refundTxSigningCommitment = refundSigningNonceCommitment;
425
- childCreationNode.refundTxSigningJob = refundSigningJob;
460
+ const directRefundSigningJob: SigningJob | undefined = directRefundTx
461
+ ? {
462
+ signingPublicKey: node.signingPublicKey,
463
+ rawTx: directRefundTx.toBytes(),
464
+ signingNonceCommitment: directRefundSigningCommitment.commitment,
465
+ }
466
+ : undefined;
467
+ const directFromCpfpRefundSigningJob: SigningJob | undefined =
468
+ directFromCpfpRefundTx
469
+ ? {
470
+ signingPublicKey: node.signingPublicKey,
471
+ rawTx: directFromCpfpRefundTx.toBytes(),
472
+ signingNonceCommitment:
473
+ directFromCpfpRefundSigningCommitment.commitment,
474
+ }
475
+ : undefined;
476
+
477
+ childCreationNode.refundTxSigningCommitment = cpfpRefundSigningCommitment;
478
+ childCreationNode.directRefundTxSigningCommitment =
479
+ directRefundSigningCommitment;
480
+ childCreationNode.directFromCpfpRefundTxSigningCommitment =
481
+ directFromCpfpRefundSigningCommitment;
482
+ childCreationNode.refundTxSigningJob = cpfpRefundSigningJob;
483
+ childCreationNode.directRefundTxSigningJob = directRefundSigningJob;
484
+ childCreationNode.directFromCpfpRefundTxSigningJob =
485
+ directFromCpfpRefundSigningJob;
426
486
 
427
487
  internalCreationNode.children.push(childCreationNode);
428
488
 
@@ -440,12 +500,9 @@ export class TreeCreationService {
440
500
  if (!parentTxOutput?.script || !parentTxOutput?.amount) {
441
501
  throw new Error("parentTxOutput is undefined");
442
502
  }
443
- const rootNodeTx = new Transaction({ version: 3 });
444
- rootNodeTx.addInput({
445
- txid: getTxId(parentTx),
446
- index: vout,
447
- });
448
503
 
504
+ // Create child transaction outputs
505
+ const childTxOuts: { script: Uint8Array; amount: bigint }[] = [];
449
506
  for (let i = 0; i < root.children.length; i++) {
450
507
  const child = root.children[i];
451
508
  if (!child || !child.address) {
@@ -454,32 +511,51 @@ export class TreeCreationService {
454
511
  const childAddress = Address(getNetwork(network)).decode(child.address);
455
512
  const childPkScript = OutScript.encode(childAddress);
456
513
 
457
- // First subtract fee from total amount, then split between children
458
- // const feeAdjustedAmount = maybeApplyFee(parentTxOutput.amount);
459
- rootNodeTx.addOutput({
514
+ childTxOuts.push({
460
515
  script: childPkScript,
461
- amount: parentTxOutput.amount / 2n, // feeAdjustedAmount / 2n,
516
+ amount: parentTxOutput.amount / 2n,
462
517
  });
463
518
  }
464
519
 
465
- rootNodeTx.addOutput(getEphemeralAnchorOutput());
520
+ const parentOutPoint = {
521
+ txid: hexToBytes(getTxId(parentTx)),
522
+ index: vout,
523
+ };
524
+
525
+ // Create both CPFP and direct split transactions
526
+ const [cpfpSplitTx, directSplitTx] = createSplitTx(
527
+ parentOutPoint,
528
+ childTxOuts,
529
+ );
466
530
 
467
- const rootNodeSigningCommitment =
531
+ // Create nonce commitments for split transactions
532
+ const cpfpSplitSigningCommitment =
533
+ await this.config.signer.getRandomSigningCommitment();
534
+ const directSplitSigningCommitment =
468
535
  await this.config.signer.getRandomSigningCommitment();
469
- const rootNodeSigningJob: SigningJob = {
536
+
537
+ const cpfpSplitSigningJob: SigningJob = {
470
538
  signingPublicKey: root.signingPublicKey,
471
- rawTx: rootNodeTx.toBytes(),
472
- signingNonceCommitment: rootNodeSigningCommitment.commitment,
539
+ rawTx: cpfpSplitTx.toBytes(),
540
+ signingNonceCommitment: cpfpSplitSigningCommitment.commitment,
473
541
  };
542
+ const directSplitSigningJob: SigningJob = {
543
+ signingPublicKey: root.signingPublicKey,
544
+ rawTx: directSplitTx.toBytes(),
545
+ signingNonceCommitment: directSplitSigningCommitment.commitment,
546
+ };
547
+
474
548
  const rootCreationNode: CreationNodeWithNonces = {
475
- nodeTxSigningJob: rootNodeSigningJob,
549
+ nodeTxSigningJob: cpfpSplitSigningJob,
550
+ directNodeTxSigningJob: directSplitSigningJob,
476
551
  refundTxSigningJob: undefined,
477
- children: [],
478
- directNodeTxSigningJob: undefined,
479
552
  directRefundTxSigningJob: undefined,
480
553
  directFromCpfpRefundTxSigningJob: undefined,
554
+ children: [],
481
555
  };
482
- rootCreationNode.nodeTxSigningCommitment = rootNodeSigningCommitment;
556
+ rootCreationNode.nodeTxSigningCommitment = cpfpSplitSigningCommitment;
557
+ rootCreationNode.directNodeTxSigningCommitment =
558
+ directSplitSigningCommitment;
483
559
 
484
560
  const leftChild = root.children[0];
485
561
  const rightChild = root.children[1];
@@ -489,13 +565,13 @@ export class TreeCreationService {
489
565
 
490
566
  const leftChildCreationNode = await this.buildChildCreationNode(
491
567
  leftChild,
492
- rootNodeTx,
568
+ cpfpSplitTx, // Use CPFP version for children
493
569
  0,
494
570
  network,
495
571
  );
496
572
  const rightChildCreationNode = await this.buildChildCreationNode(
497
573
  rightChild,
498
- rootNodeTx,
574
+ cpfpSplitTx, // Use CPFP version for children
499
575
  1,
500
576
  network,
501
577
  );
@@ -515,6 +591,7 @@ export class TreeCreationService {
515
591
  ): Promise<{ tx: Transaction; signature: NodeSignatures }> {
516
592
  if (
517
593
  !creationNode.nodeTxSigningJob?.signingPublicKey ||
594
+ !creationNode.directNodeTxSigningJob?.signingPublicKey ||
518
595
  !internalNode.verificationKey
519
596
  ) {
520
597
  throw new Error("signingPublicKey or verificationKey is undefined");
@@ -525,13 +602,14 @@ export class TreeCreationService {
525
602
  throw new Error("parentTxOutput is undefined");
526
603
  }
527
604
 
528
- const tx = getTxFromRawTxBytes(creationNode.nodeTxSigningJob.rawTx);
529
- const txSighash = getSigHashFromTx(tx, 0, parentTxOutput);
605
+ // Sign CPFP node transaction
606
+ const cpfpNodeTx = getTxFromRawTxBytes(creationNode.nodeTxSigningJob.rawTx);
607
+ const cpfpNodeTxSighash = getSigHashFromTx(cpfpNodeTx, 0, parentTxOutput);
530
608
 
531
- let nodeTxSignature: Uint8Array = new Uint8Array();
609
+ let cpfpNodeTxSignature: Uint8Array = new Uint8Array();
532
610
  if (creationNode.nodeTxSigningCommitment) {
533
- const userSignature = await this.config.signer.signFrost({
534
- message: txSighash,
611
+ const cpfpUserSignature = await this.config.signer.signFrost({
612
+ message: cpfpNodeTxSighash,
535
613
  publicKey: creationNode.nodeTxSigningJob.signingPublicKey,
536
614
  keyDerivation: {
537
615
  type: KeyDerivationType.LEAF,
@@ -543,8 +621,8 @@ export class TreeCreationService {
543
621
  verifyingKey: internalNode.verificationKey,
544
622
  });
545
623
 
546
- nodeTxSignature = await this.config.signer.aggregateFrost({
547
- message: txSighash,
624
+ cpfpNodeTxSignature = await this.config.signer.aggregateFrost({
625
+ message: cpfpNodeTxSighash,
548
626
  statechainSignatures:
549
627
  creationResponseNode.nodeTxSigningResult?.signatureShares,
550
628
  statechainPublicKeys:
@@ -553,25 +631,105 @@ export class TreeCreationService {
553
631
  statechainCommitments:
554
632
  creationResponseNode.nodeTxSigningResult?.signingNonceCommitments,
555
633
  selfCommitment: creationNode.nodeTxSigningCommitment,
556
- selfSignature: userSignature,
634
+ selfSignature: cpfpUserSignature,
557
635
  publicKey: internalNode.signingPublicKey,
558
636
  });
559
637
  }
560
638
 
561
- let refundTxSignature: Uint8Array = new Uint8Array();
562
- if (creationNode.refundTxSigningCommitment) {
563
- const rawTx = creationNode.refundTxSigningJob?.rawTx;
564
- if (!rawTx) {
565
- throw new Error("rawTx is undefined");
639
+ // Sign direct node transaction
640
+ const directNodeTx = getTxFromRawTxBytes(
641
+ creationNode.directNodeTxSigningJob.rawTx,
642
+ );
643
+ const directNodeTxSighash = getSigHashFromTx(
644
+ directNodeTx,
645
+ 0,
646
+ parentTxOutput,
647
+ );
648
+
649
+ let directNodeTxSignature: Uint8Array = new Uint8Array();
650
+ if (creationNode.directNodeTxSigningCommitment) {
651
+ const directUserSignature = await this.config.signer.signFrost({
652
+ message: directNodeTxSighash,
653
+ publicKey: creationNode.directNodeTxSigningJob.signingPublicKey,
654
+ keyDerivation: {
655
+ type: KeyDerivationType.LEAF,
656
+ path: creationResponseNode.nodeId,
657
+ },
658
+ selfCommitment: creationNode.directNodeTxSigningCommitment,
659
+ statechainCommitments:
660
+ creationResponseNode.directNodeTxSigningResult
661
+ ?.signingNonceCommitments,
662
+ verifyingKey: internalNode.verificationKey,
663
+ });
664
+
665
+ directNodeTxSignature = await this.config.signer.aggregateFrost({
666
+ message: directNodeTxSighash,
667
+ statechainSignatures:
668
+ creationResponseNode.directNodeTxSigningResult?.signatureShares,
669
+ statechainPublicKeys:
670
+ creationResponseNode.directNodeTxSigningResult?.publicKeys,
671
+ verifyingKey: internalNode.verificationKey,
672
+ statechainCommitments:
673
+ creationResponseNode.directNodeTxSigningResult
674
+ ?.signingNonceCommitments,
675
+ selfCommitment: creationNode.directNodeTxSigningCommitment,
676
+ selfSignature: directUserSignature,
677
+ publicKey: internalNode.signingPublicKey,
678
+ });
679
+ }
680
+
681
+ // Sign refund transactions if they exist
682
+ let cpfpRefundTxSignature: Uint8Array = new Uint8Array();
683
+ let directRefundTxSignature: Uint8Array = new Uint8Array();
684
+ let directFromCpfpRefundTxSignature: Uint8Array = new Uint8Array();
685
+ if (
686
+ creationNode.refundTxSigningCommitment &&
687
+ creationNode.directRefundTxSigningCommitment &&
688
+ creationNode.directFromCpfpRefundTxSigningCommitment
689
+ ) {
690
+ const rawCpfpRefundTx = creationNode.refundTxSigningJob?.rawTx;
691
+ const rawDirectRefundTx = creationNode.directRefundTxSigningJob?.rawTx;
692
+ const rawDirectFromCpfpRefundTx =
693
+ creationNode.directFromCpfpRefundTxSigningJob?.rawTx;
694
+ if (
695
+ !rawCpfpRefundTx ||
696
+ !rawDirectRefundTx ||
697
+ !rawDirectFromCpfpRefundTx
698
+ ) {
699
+ throw new Error("refund transaction rawTx is undefined");
566
700
  }
567
- if (!creationNode.refundTxSigningJob?.signingPublicKey) {
568
- throw new Error("signingPublicKey is undefined");
701
+ if (
702
+ !creationNode.refundTxSigningJob?.signingPublicKey ||
703
+ !creationNode.directRefundTxSigningJob?.signingPublicKey ||
704
+ !creationNode.directFromCpfpRefundTxSigningJob?.signingPublicKey
705
+ ) {
706
+ throw new Error("refund transaction signingPublicKey is undefined");
569
707
  }
570
- const refundTx = getTxFromRawTxBytes(rawTx);
571
- const refundTxSighash = getSigHashFromTx(refundTx, 0, parentTxOutput);
572
708
 
573
- const refundSigningResponse = await this.config.signer.signFrost({
574
- message: refundTxSighash,
709
+ const cpfpRefundTx = getTxFromRawTxBytes(rawCpfpRefundTx);
710
+ const directRefundTx = getTxFromRawTxBytes(rawDirectRefundTx);
711
+ const directFromCpfpRefundTx = getTxFromRawTxBytes(
712
+ rawDirectFromCpfpRefundTx,
713
+ );
714
+ const cpfpRefundTxSighash = getSigHashFromTx(
715
+ cpfpRefundTx,
716
+ 0,
717
+ cpfpNodeTx.getOutput(0),
718
+ );
719
+ const directRefundTxSighash = getSigHashFromTx(
720
+ directRefundTx,
721
+ 0,
722
+ directNodeTx.getOutput(0),
723
+ );
724
+ const directFromCpfpRefundTxSighash = getSigHashFromTx(
725
+ directFromCpfpRefundTx,
726
+ 0,
727
+ cpfpNodeTx.getOutput(0),
728
+ );
729
+
730
+ // Sign CPFP refund transaction
731
+ const cpfpRefundUserSignature = await this.config.signer.signFrost({
732
+ message: cpfpRefundTxSighash,
575
733
  publicKey: creationNode.refundTxSigningJob.signingPublicKey,
576
734
  keyDerivation: {
577
735
  type: KeyDerivationType.LEAF,
@@ -583,8 +741,8 @@ export class TreeCreationService {
583
741
  verifyingKey: internalNode.verificationKey,
584
742
  });
585
743
 
586
- refundTxSignature = await this.config.signer.aggregateFrost({
587
- message: refundTxSighash,
744
+ cpfpRefundTxSignature = await this.config.signer.aggregateFrost({
745
+ message: cpfpRefundTxSighash,
588
746
  statechainSignatures:
589
747
  creationResponseNode.refundTxSigningResult?.signatureShares,
590
748
  statechainPublicKeys:
@@ -593,21 +751,84 @@ export class TreeCreationService {
593
751
  statechainCommitments:
594
752
  creationResponseNode.refundTxSigningResult?.signingNonceCommitments,
595
753
  selfCommitment: creationNode.refundTxSigningCommitment,
596
- selfSignature: refundSigningResponse,
754
+ selfSignature: cpfpRefundUserSignature,
597
755
  publicKey: internalNode.signingPublicKey,
598
756
  });
757
+
758
+ const keyDerivation: KeyDerivation = {
759
+ type: KeyDerivationType.LEAF,
760
+ path: creationResponseNode.nodeId,
761
+ };
762
+ // Sign direct refund transaction
763
+ const directRefundUserSignature = await this.config.signer.signFrost({
764
+ message: directRefundTxSighash,
765
+ publicKey: creationNode.directRefundTxSigningJob.signingPublicKey,
766
+ keyDerivation,
767
+ selfCommitment: creationNode.directRefundTxSigningCommitment,
768
+ statechainCommitments:
769
+ creationResponseNode.directRefundTxSigningResult
770
+ ?.signingNonceCommitments,
771
+ verifyingKey: internalNode.verificationKey,
772
+ });
773
+
774
+ directRefundTxSignature = await this.config.signer.aggregateFrost({
775
+ message: directRefundTxSighash,
776
+ statechainSignatures:
777
+ creationResponseNode.directRefundTxSigningResult?.signatureShares,
778
+ statechainPublicKeys:
779
+ creationResponseNode.directRefundTxSigningResult?.publicKeys,
780
+ verifyingKey: internalNode.verificationKey,
781
+ statechainCommitments:
782
+ creationResponseNode.directRefundTxSigningResult
783
+ ?.signingNonceCommitments,
784
+ selfCommitment: creationNode.directRefundTxSigningCommitment,
785
+ selfSignature: directRefundUserSignature,
786
+ publicKey: internalNode.signingPublicKey,
787
+ });
788
+
789
+ // Sign direct from CPFP refund transaction
790
+ const directFromCpfpRefundUserSignature =
791
+ await this.config.signer.signFrost({
792
+ message: directFromCpfpRefundTxSighash,
793
+ publicKey:
794
+ creationNode.directFromCpfpRefundTxSigningJob.signingPublicKey,
795
+ keyDerivation,
796
+ selfCommitment: creationNode.directFromCpfpRefundTxSigningCommitment,
797
+ statechainCommitments:
798
+ creationResponseNode.directFromCpfpRefundTxSigningResult
799
+ ?.signingNonceCommitments,
800
+ verifyingKey: internalNode.verificationKey,
801
+ });
802
+
803
+ directFromCpfpRefundTxSignature = await this.config.signer.aggregateFrost(
804
+ {
805
+ message: directFromCpfpRefundTxSighash,
806
+ statechainSignatures:
807
+ creationResponseNode.directFromCpfpRefundTxSigningResult
808
+ ?.signatureShares,
809
+ statechainPublicKeys:
810
+ creationResponseNode.directFromCpfpRefundTxSigningResult
811
+ ?.publicKeys,
812
+ verifyingKey: internalNode.verificationKey,
813
+ statechainCommitments:
814
+ creationResponseNode.directFromCpfpRefundTxSigningResult
815
+ ?.signingNonceCommitments,
816
+ selfCommitment: creationNode.directFromCpfpRefundTxSigningCommitment,
817
+ selfSignature: directFromCpfpRefundUserSignature,
818
+ publicKey: internalNode.signingPublicKey,
819
+ },
820
+ );
599
821
  }
600
822
 
601
823
  return {
602
- tx: tx,
824
+ tx: cpfpNodeTx, // Return CPFP version for children
603
825
  signature: {
604
826
  nodeId: creationResponseNode.nodeId,
605
- nodeTxSignature: nodeTxSignature,
606
- refundTxSignature: refundTxSignature,
607
- // TODO: Add direct refund signature
608
- directNodeTxSignature: new Uint8Array(),
609
- directRefundTxSignature: new Uint8Array(),
610
- directFromCpfpRefundTxSignature: new Uint8Array(),
827
+ nodeTxSignature: cpfpNodeTxSignature,
828
+ directNodeTxSignature: directNodeTxSignature,
829
+ refundTxSignature: cpfpRefundTxSignature,
830
+ directRefundTxSignature: directRefundTxSignature,
831
+ directFromCpfpRefundTxSignature: directFromCpfpRefundTxSignature,
611
832
  },
612
833
  };
613
834
  }