@buildonspark/spark-sdk 0.3.7 → 0.3.9

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 (87) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/bare/index.cjs +8088 -7723
  3. package/dist/bare/index.d.cts +350 -262
  4. package/dist/bare/index.d.ts +350 -262
  5. package/dist/bare/index.js +7968 -7608
  6. package/dist/{chunk-J2P3KTQP.js → chunk-4YFT7DAE.js} +1 -1
  7. package/dist/{chunk-XWLR6G5C.js → chunk-JLF6WJ7K.js} +1 -1
  8. package/dist/{chunk-UYTT3C6H.js → chunk-MFCM6GUD.js} +40 -213
  9. package/dist/{chunk-KDEVNW7C.js → chunk-O4C4HGQL.js} +3391 -3292
  10. package/dist/{chunk-SRPKOCG4.js → chunk-S55NZT4P.js} +8 -10
  11. package/dist/{chunk-P4HYYSMU.js → chunk-WRE2T22S.js} +1 -1
  12. package/dist/{chunk-IC4IUEOS.js → chunk-YEBEN7XD.js} +309 -38
  13. package/dist/{client-Bcb7TUIp.d.cts → client-BIqiUNy4.d.cts} +2 -2
  14. package/dist/{client-D9T58OY8.d.ts → client-BaQf-5gD.d.ts} +2 -2
  15. package/dist/debug.cjs +8068 -7704
  16. package/dist/debug.d.cts +25 -18
  17. package/dist/debug.d.ts +25 -18
  18. package/dist/debug.js +5 -5
  19. package/dist/graphql/objects/index.d.cts +3 -3
  20. package/dist/graphql/objects/index.d.ts +3 -3
  21. package/dist/index.cjs +6871 -6501
  22. package/dist/index.d.cts +9 -8
  23. package/dist/index.d.ts +9 -8
  24. package/dist/index.js +36 -24
  25. package/dist/index.node.cjs +7102 -6903
  26. package/dist/index.node.d.cts +9 -8
  27. package/dist/index.node.d.ts +9 -8
  28. package/dist/index.node.js +35 -23
  29. package/dist/{logging-zkr4UlOi.d.cts → logging-CXhvuqJJ.d.cts} +45 -35
  30. package/dist/{logging-JIaZZIbR.d.ts → logging-DDeMLsVN.d.ts} +45 -35
  31. package/dist/native/{chunk-X2QXUON7.js → chunk-AFP5QR4O.js} +11 -8
  32. package/dist/native/index.react-native.cjs +7054 -6677
  33. package/dist/native/index.react-native.d.cts +180 -92
  34. package/dist/native/index.react-native.d.ts +180 -92
  35. package/dist/native/index.react-native.js +6760 -6393
  36. package/dist/native/{wasm-GKEDPGTM.js → wasm-D4TI35NF.js} +1 -1
  37. package/dist/proto/spark.cjs +309 -38
  38. package/dist/proto/spark.d.cts +1 -1
  39. package/dist/proto/spark.d.ts +1 -1
  40. package/dist/proto/spark.js +5 -1
  41. package/dist/proto/spark_token.d.cts +1 -1
  42. package/dist/proto/spark_token.d.ts +1 -1
  43. package/dist/proto/spark_token.js +2 -2
  44. package/dist/{spark-WA_4wcBr.d.cts → spark-DOpheE8_.d.cts} +69 -7
  45. package/dist/{spark-WA_4wcBr.d.ts → spark-DOpheE8_.d.ts} +69 -7
  46. package/dist/{spark-wallet.browser-DC3jdQPW.d.cts → spark-wallet.browser-CbYo8A_U.d.cts} +8 -8
  47. package/dist/{spark-wallet.browser-BwYkkOBU.d.ts → spark-wallet.browser-Cz8c4kOW.d.ts} +8 -8
  48. package/dist/{spark-wallet.node-CR_zNxmy.d.cts → spark-wallet.node-4WQgWwB2.d.cts} +9 -31
  49. package/dist/{spark-wallet.node-C9d2W-Nb.d.ts → spark-wallet.node-CmIvxtcC.d.ts} +9 -31
  50. package/dist/tests/test-utils.cjs +7341 -7065
  51. package/dist/tests/test-utils.d.cts +7 -5
  52. package/dist/tests/test-utils.d.ts +7 -5
  53. package/dist/tests/test-utils.js +7 -7
  54. package/dist/{token-transactions-BZoJuvuE.d.ts → token-transactions-Bu023ztN.d.ts} +2 -2
  55. package/dist/{token-transactions-I_OFIoNH.d.cts → token-transactions-CV8QD3I7.d.cts} +2 -2
  56. package/dist/types/index.cjs +307 -38
  57. package/dist/types/index.d.cts +2 -2
  58. package/dist/types/index.d.ts +2 -2
  59. package/dist/types/index.js +2 -2
  60. package/dist/{spark-wallet-CE5PYiIb.d.ts → wallet-config-Bmk2eAn8.d.ts} +310 -287
  61. package/dist/{spark-wallet-BuFrUWeE.d.cts → wallet-config-DQw5llqA.d.cts} +310 -287
  62. package/package.json +3 -3
  63. package/src/proto/mock.ts +0 -264
  64. package/src/proto/spark.ts +433 -46
  65. package/src/services/config.ts +5 -0
  66. package/src/services/connection/connection.browser.ts +27 -19
  67. package/src/services/connection/connection.node.ts +79 -24
  68. package/src/services/connection/connection.ts +395 -233
  69. package/src/services/coop-exit.ts +26 -107
  70. package/src/services/deposit.ts +12 -48
  71. package/src/services/lightning.ts +30 -4
  72. package/src/services/signing.ts +187 -37
  73. package/src/services/transfer.ts +553 -723
  74. package/src/services/wallet-config.ts +6 -0
  75. package/src/spark-wallet/proto-descriptors.ts +1 -1
  76. package/src/spark-wallet/spark-wallet.ts +132 -313
  77. package/src/spark-wallet/types.ts +2 -2
  78. package/src/spark_descriptors.pb +0 -0
  79. package/src/tests/connection.test.ts +537 -0
  80. package/src/tests/integration/connection.test.ts +39 -0
  81. package/src/tests/integration/lightning.test.ts +32 -16
  82. package/src/tests/integration/static_deposit.test.ts +13 -11
  83. package/src/tests/integration/transfer.test.ts +13 -1
  84. package/src/tests/isHermeticTest.ts +1 -1
  85. package/src/tests/utils/test-faucet.ts +53 -20
  86. package/src/utils/htlc-transactions.ts +224 -0
  87. package/src/utils/transaction.ts +285 -248
@@ -7,15 +7,9 @@ import {
7
7
  LeafRefundTxSigningJob,
8
8
  Transfer,
9
9
  } from "../proto/spark.js";
10
- import {
11
- getP2TRScriptFromPublicKey,
12
- getTxFromRawTxBytes,
13
- } from "../utils/bitcoin.js";
10
+ import { getTxFromRawTxBytes } from "../utils/bitcoin.js";
14
11
  import { Network } from "../utils/network.js";
15
- import {
16
- getNextTransactionSequence,
17
- maybeApplyFee,
18
- } from "../utils/transaction.js";
12
+ import { createConnectorRefundTxs } from "../utils/transaction.js";
19
13
  import { WalletConfigService } from "./config.js";
20
14
  import { ConnectionManager } from "./connection/connection.js";
21
15
  import { SigningService } from "./signing.js";
@@ -80,93 +74,6 @@ export class CoopExitService extends BaseTransferService {
80
74
  };
81
75
  }
82
76
 
83
- private createConnectorRefundTransactions(
84
- sequence: number,
85
- directSequence: number,
86
- cpfpNodeOutPoint: TransactionInput,
87
- directNodeOutPoint: TransactionInput | undefined,
88
- connectorOutput: TransactionInput,
89
- amountSats: bigint,
90
- receiverPubKey: Uint8Array,
91
- ): {
92
- cpfpRefundTx: Transaction;
93
- directRefundTx?: Transaction;
94
- directFromCpfpRefundTx?: Transaction;
95
- } {
96
- // Create CPFP refund transaction
97
- const cpfpRefundTx = new Transaction();
98
- if (!cpfpNodeOutPoint.txid || cpfpNodeOutPoint.index === undefined) {
99
- throw new ValidationError("Invalid CPFP node outpoint", {
100
- field: "cpfpNodeOutPoint",
101
- value: { txid: cpfpNodeOutPoint.txid, index: cpfpNodeOutPoint.index },
102
- expected: "Both txid and index must be defined",
103
- });
104
- }
105
- cpfpRefundTx.addInput({
106
- txid: cpfpNodeOutPoint.txid,
107
- index: cpfpNodeOutPoint.index,
108
- sequence,
109
- });
110
-
111
- cpfpRefundTx.addInput(connectorOutput);
112
- const receiverScript = getP2TRScriptFromPublicKey(
113
- receiverPubKey,
114
- this.config.getNetwork(),
115
- );
116
-
117
- cpfpRefundTx.addOutput({
118
- script: receiverScript,
119
- amount: amountSats,
120
- });
121
-
122
- // Create direct refund transaction
123
- let directRefundTx: Transaction | undefined;
124
- let directFromCpfpRefundTx: Transaction | undefined;
125
- if (directNodeOutPoint) {
126
- if (!directNodeOutPoint.txid || directNodeOutPoint.index === undefined) {
127
- throw new ValidationError("Invalid direct node outpoint", {
128
- field: "directNodeOutPoint",
129
- value: {
130
- txid: directNodeOutPoint.txid,
131
- index: directNodeOutPoint.index,
132
- },
133
- expected: "Both txid and index must be defined",
134
- });
135
- }
136
- directRefundTx = new Transaction();
137
- directRefundTx.addInput({
138
- txid: directNodeOutPoint.txid,
139
- index: directNodeOutPoint.index,
140
- sequence: directSequence,
141
- });
142
-
143
- directRefundTx.addInput(connectorOutput);
144
- directRefundTx.addOutput({
145
- script: receiverScript,
146
- amount: maybeApplyFee(amountSats),
147
- });
148
-
149
- directFromCpfpRefundTx = new Transaction();
150
- directFromCpfpRefundTx.addInput({
151
- txid: cpfpNodeOutPoint.txid,
152
- index: cpfpNodeOutPoint.index,
153
- sequence: directSequence,
154
- });
155
-
156
- directFromCpfpRefundTx.addInput(connectorOutput);
157
- directFromCpfpRefundTx.addOutput({
158
- script: receiverScript,
159
- amount: maybeApplyFee(amountSats),
160
- });
161
- }
162
-
163
- return {
164
- cpfpRefundTx,
165
- directRefundTx,
166
- directFromCpfpRefundTx,
167
- };
168
- }
169
-
170
77
  private async signCoopExitRefunds(
171
78
  leaves: LeafKeyTweak[],
172
79
  exitTxId: Uint8Array,
@@ -213,18 +120,31 @@ export class CoopExitService extends BaseTransferService {
213
120
  expected: "Valid connector output",
214
121
  });
215
122
  }
123
+
124
+ const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
125
+
126
+ let directNodeTx: Transaction | undefined;
127
+ if (leaf.leaf.directTx.length > 0) {
128
+ directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
129
+ }
130
+
216
131
  const currentRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
132
+ if (!currentRefundTx) {
133
+ throw new ValidationError("Invalid refund transaction", {
134
+ field: "currentRefundTx",
135
+ value: currentRefundTx,
136
+ expected: "Non-null refund transaction",
137
+ });
138
+ }
217
139
 
218
- const sequence = currentRefundTx.getInput(0).sequence;
219
- if (!sequence) {
140
+ const currentSequence = currentRefundTx.getInput(0).sequence;
141
+ if (!currentSequence) {
220
142
  throw new ValidationError("Invalid refund transaction", {
221
143
  field: "sequence",
222
144
  value: currentRefundTx.getInput(0),
223
145
  expected: "Non-null sequence",
224
146
  });
225
147
  }
226
- const { nextSequence, nextDirectSequence } =
227
- getNextTransactionSequence(sequence);
228
148
 
229
149
  let currentDirectRefundTx: Transaction | undefined;
230
150
  if (leaf.leaf.directRefundTx.length > 0) {
@@ -232,15 +152,14 @@ export class CoopExitService extends BaseTransferService {
232
152
  }
233
153
 
234
154
  const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } =
235
- this.createConnectorRefundTransactions(
236
- nextSequence,
237
- nextDirectSequence,
238
- currentRefundTx.getInput(0),
239
- currentDirectRefundTx?.getInput(0),
155
+ createConnectorRefundTxs({
156
+ nodeTx,
157
+ directNodeTx,
158
+ sequence: currentSequence,
240
159
  connectorOutput,
241
- BigInt(leaf.leaf.value),
242
- receiverPubKey,
243
- );
160
+ receivingPubkey: receiverPubKey,
161
+ network: this.config.getNetwork(),
162
+ });
244
163
 
245
164
  const signingNonceCommitment =
246
165
  await this.config.signer.getRandomSigningCommitment();
@@ -12,15 +12,13 @@ import {
12
12
  StartDepositTreeCreationResponse,
13
13
  } from "../proto/spark.js";
14
14
  import { KeyDerivation } from "../signer/types.js";
15
- import { getSigHashFromTx, getTxId } from "../utils/bitcoin.js";
15
+ import { getSigHashFromTx } from "../utils/bitcoin.js";
16
16
  import { subtractPublicKeys } from "../utils/keys.js";
17
17
  import { getNetwork } from "../utils/network.js";
18
18
  import { proofOfPossessionMessageHashForDepositAddress } from "../utils/proof.js";
19
19
  import {
20
- createRefundTxs,
21
- createRootTx,
22
- INITIAL_DIRECT_SEQUENCE,
23
- INITIAL_SEQUENCE,
20
+ createInitialTimelockRefundTxs,
21
+ createRootNodeTx,
24
22
  } from "../utils/transaction.js";
25
23
  import { WalletConfigService } from "./config.js";
26
24
  import { ConnectionManager } from "./connection/connection.js";
@@ -250,28 +248,10 @@ export class DepositService {
250
248
  expected: "Valid output index",
251
249
  });
252
250
  }
253
- const script = output.script;
254
- const amount = output.amount;
255
- if (!script || !amount) {
256
- throw new ValidationError("No script or amount found in deposit tx", {
257
- field: "output",
258
- value: output,
259
- expected: "Output with script and amount",
260
- });
261
- }
262
251
 
263
- const depositOutPoint = {
264
- txid: hexToBytes(getTxId(depositTx)),
265
- index: vout,
266
- };
267
- const depositTxOut = {
268
- script,
269
- amount,
270
- };
271
-
272
- const [cpfpRootTx, directRootTx] = createRootTx(
273
- depositOutPoint,
274
- depositTxOut,
252
+ const { nodeTx: cpfpRootTx, directNodeTx: directRootTx } = createRootNodeTx(
253
+ depositTx,
254
+ vout,
275
255
  );
276
256
 
277
257
  // Create nonce commitments for root transactions
@@ -287,14 +267,10 @@ export class DepositService {
287
267
  const signingPubKey =
288
268
  await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
289
269
 
290
- // Create refund transactions (CPFP and direct)
291
270
  const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } =
292
- createRefundTxs({
293
- sequence: INITIAL_SEQUENCE,
294
- directSequence: INITIAL_DIRECT_SEQUENCE,
295
- input: { txid: hexToBytes(getTxId(cpfpRootTx)), index: 0 },
296
- directInput: { txid: hexToBytes(getTxId(directRootTx)), index: 0 },
297
- amountSats: amount,
271
+ createInitialTimelockRefundTxs({
272
+ nodeTx: cpfpRootTx,
273
+ directNodeTx: directRootTx,
298
274
  receivingPubkey: signingPubKey,
299
275
  network: this.config.getNetwork(),
300
276
  });
@@ -675,16 +651,7 @@ export class DepositService {
675
651
  });
676
652
  }
677
653
 
678
- const depositOutPoint = {
679
- txid: hexToBytes(getTxId(depositTx)),
680
- index: vout,
681
- };
682
- const depositTxOut = {
683
- script,
684
- amount,
685
- };
686
-
687
- const [cpfpRootTx, _] = createRootTx(depositOutPoint, depositTxOut);
654
+ const { nodeTx: cpfpRootTx } = createRootNodeTx(depositTx, vout);
688
655
 
689
656
  // Create nonce commitments for root transactions
690
657
  const cpfpRootNonceCommitment =
@@ -696,11 +663,8 @@ export class DepositService {
696
663
  const signingPubKey =
697
664
  await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
698
665
 
699
- // Create refund transactions (CPFP and direct)
700
- const { cpfpRefundTx } = createRefundTxs({
701
- sequence: INITIAL_SEQUENCE,
702
- input: { txid: hexToBytes(getTxId(cpfpRootTx)), index: 0 },
703
- amountSats: amount,
666
+ const { cpfpRefundTx } = createInitialTimelockRefundTxs({
667
+ nodeTx: cpfpRootTx,
704
668
  receivingPubkey: signingPubKey,
705
669
  network: this.config.getNetwork(),
706
670
  });
@@ -15,6 +15,7 @@ import {
15
15
  ProvidePreimageResponse,
16
16
  QueryUserSignedRefundsResponse,
17
17
  Transfer,
18
+ StartTransferRequest,
18
19
  UserSignedRefund,
19
20
  } from "../proto/spark.js";
20
21
  import { getTxFromRawTxBytes } from "../utils/bitcoin.js";
@@ -51,6 +52,9 @@ export type SwapNodesForPreimageParams = {
51
52
  isInboundPayment: boolean;
52
53
  feeSats?: number;
53
54
  amountSatsToSend?: number;
55
+ startTransferRequest?: StartTransferRequest;
56
+ expiryTime?: Date;
57
+ transferID?: string;
54
58
  };
55
59
 
56
60
  export class LightningService {
@@ -175,6 +179,19 @@ export class LightningService {
175
179
  return invoice;
176
180
  }
177
181
 
182
+ /**
183
+ * Swap nodes for preimage
184
+ * @param leaves - The leaves to swap for preimage
185
+ * @param receiverIdentityPubkey - The receiver identity public key
186
+ * @param paymentHash - The payment hash
187
+ * @param invoiceString - The invoice string
188
+ * @param isInboundPayment - Whether the payment is inbound
189
+ * @param feeSats - The fee in sats
190
+ * @param amountSatsToSend - The amount in sats to send
191
+ * @param expiryTime - The expiry time
192
+ * @param startTransferRequest - The start transfer request, do not populate if is inbound payment
193
+ * @param transferID - The transfer ID, do not populate if is inbound payment
194
+ */
178
195
  async swapNodesForPreimage({
179
196
  leaves,
180
197
  receiverIdentityPubkey,
@@ -183,6 +200,9 @@ export class LightningService {
183
200
  isInboundPayment,
184
201
  feeSats = 0,
185
202
  amountSatsToSend,
203
+ expiryTime,
204
+ startTransferRequest,
205
+ transferID,
186
206
  }: SwapNodesForPreimageParams): Promise<InitiatePreimageSwapResponse> {
187
207
  const sparkClient = await this.connectionManager.createSparkClient(
188
208
  this.config.getCoordinatorAddress(),
@@ -222,7 +242,7 @@ export class LightningService {
222
242
  signingCommitments.signingCommitments.slice(2 * leaves.length),
223
243
  );
224
244
 
225
- const transferId = uuidv7();
245
+ const transferId = transferID ? transferID : uuidv7();
226
246
  let bolt11String = "";
227
247
  let amountSats: number = 0;
228
248
  if (invoiceString) {
@@ -267,6 +287,7 @@ export class LightningService {
267
287
  : InitiatePreimageSwapRequest_Reason.REASON_SEND;
268
288
 
269
289
  let response: InitiatePreimageSwapResponse;
290
+ // TODO(LIG-8126): Remove transfer inputs once SDK upgrade is complete
270
291
  try {
271
292
  response = await sparkClient.initiate_preimage_swap_v2({
272
293
  paymentHash,
@@ -282,13 +303,18 @@ export class LightningService {
282
303
  ownerIdentityPublicKey:
283
304
  await this.config.signer.getIdentityPublicKey(),
284
305
  leavesToSend: cpfpLeafSigningJobs,
285
- directLeavesToSend: directLeafSigningJobs,
286
- directFromCpfpLeavesToSend: directFromCpfpLeafSigningJobs,
306
+ directLeavesToSend: startTransferRequest
307
+ ? undefined
308
+ : directLeafSigningJobs,
309
+ directFromCpfpLeavesToSend: startTransferRequest
310
+ ? undefined
311
+ : directFromCpfpLeafSigningJobs,
287
312
  receiverIdentityPublicKey: receiverIdentityPubkey,
288
- expiryTime: new Date(Date.now() + 2 * 60 * 1000),
313
+ expiryTime,
289
314
  },
290
315
  receiverIdentityPublicKey: receiverIdentityPubkey,
291
316
  feeSats,
317
+ transferRequest: startTransferRequest,
292
318
  });
293
319
  } catch (error) {
294
320
  throw new NetworkError(
@@ -1,23 +1,24 @@
1
- import { hexToBytes } from "@noble/curves/utils";
2
1
  import { Transaction } from "@scure/btc-signer";
3
- import { TransactionInput } from "@scure/btc-signer/psbt";
4
2
  import { ValidationError } from "../errors/types.js";
5
3
  import { SigningCommitment } from "../proto/common.js";
6
4
  import {
7
5
  RequestedSigningCommitments,
8
6
  UserSignedTxSigningJob,
9
7
  } from "../proto/spark.js";
8
+ import { getSigHashFromTx, getTxFromRawTxBytes } from "../utils/bitcoin.js";
9
+ import { createRefundTxsForLightning } from "../utils/htlc-transactions.js";
10
+ import { getNetwork } from "../utils/network.js";
10
11
  import {
11
- getSigHashFromTx,
12
- getTxFromRawTxBytes,
13
- getTxId,
14
- } from "../utils/bitcoin.js";
15
- import {
16
- createRefundTxs,
17
- getNextTransactionSequence,
12
+ createDecrementedTimelockRefundTxs,
13
+ getCurrentTimelock,
14
+ getNextHTLCTransactionSequence,
18
15
  } from "../utils/transaction.js";
19
16
  import { WalletConfigService } from "./config.js";
20
- import type { LeafKeyTweak } from "./transfer.js";
17
+ import type {
18
+ LeafKeyTweak,
19
+ SigningJobType,
20
+ SigningJobWithOptionalNonce,
21
+ } from "./transfer.js";
21
22
 
22
23
  export class SigningService {
23
24
  private readonly config: WalletConfigService;
@@ -102,10 +103,132 @@ export class SigningService {
102
103
  }
103
104
 
104
105
  const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
105
- const cpfpNodeOutPoint: TransactionInput = {
106
- txid: hexToBytes(getTxId(nodeTx)),
107
- index: 0,
108
- };
106
+
107
+ const currRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
108
+
109
+ const amountSats = currRefundTx.getOutput(0).amount;
110
+ if (amountSats === undefined) {
111
+ throw new ValidationError("Invalid refund transaction", {
112
+ field: "amount",
113
+ value: currRefundTx.getOutput(0),
114
+ expected: "Non-null amount",
115
+ });
116
+ }
117
+
118
+ let directNodeTx: Transaction | undefined;
119
+ if (leaf.leaf.directTx.length > 0) {
120
+ directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
121
+ }
122
+
123
+ const currentSequence = currRefundTx.getInput(0).sequence;
124
+ if (!currentSequence) {
125
+ throw new ValidationError("Invalid refund transaction", {
126
+ field: "sequence",
127
+ value: currRefundTx.getInput(0),
128
+ expected: "Non-null sequence",
129
+ });
130
+ }
131
+
132
+ const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } =
133
+ createDecrementedTimelockRefundTxs({
134
+ nodeTx: nodeTx,
135
+ directNodeTx: directNodeTx,
136
+ sequence: currentSequence,
137
+ receivingPubkey: receiverIdentityPubkey,
138
+ network: this.config.getNetwork(),
139
+ });
140
+
141
+ const refundSighash = getSigHashFromTx(
142
+ cpfpRefundTx,
143
+ 0,
144
+ nodeTx.getOutput(0),
145
+ );
146
+ const signingJobs = await this.signRefundsInternal(
147
+ cpfpRefundTx,
148
+ refundSighash,
149
+ leaf,
150
+ cpfpSigningCommitments[i]?.signingNonceCommitments,
151
+ );
152
+
153
+ cpfpLeafSigningJobs.push(...signingJobs);
154
+
155
+ const isZeroNode = getCurrentTimelock(nodeTx.getInput(0).sequence);
156
+ if (directRefundTx && !isZeroNode) {
157
+ if (!directNodeTx) {
158
+ throw new ValidationError(
159
+ "Direct node transaction undefined while direct refund transaction is defined",
160
+ {
161
+ field: "directNodeTx",
162
+ value: directNodeTx,
163
+ expected: "Non-null direct node transaction",
164
+ },
165
+ );
166
+ }
167
+ const refundSighash = getSigHashFromTx(
168
+ directRefundTx,
169
+ 0,
170
+ directNodeTx.getOutput(0),
171
+ );
172
+ const signingJobs = await this.signRefundsInternal(
173
+ directRefundTx,
174
+ refundSighash,
175
+ leaf,
176
+ directSigningCommitments[i]?.signingNonceCommitments,
177
+ );
178
+ directLeafSigningJobs.push(...signingJobs);
179
+ }
180
+
181
+ if (directFromCpfpRefundTx) {
182
+ const refundSighash = getSigHashFromTx(
183
+ directFromCpfpRefundTx,
184
+ 0,
185
+ nodeTx.getOutput(0),
186
+ );
187
+ const signingJobs = await this.signRefundsInternal(
188
+ directFromCpfpRefundTx,
189
+ refundSighash,
190
+ leaf,
191
+ directFromCpfpSigningCommitments[i]?.signingNonceCommitments,
192
+ );
193
+ directFromCpfpLeafSigningJobs.push(...signingJobs);
194
+ }
195
+ }
196
+
197
+ return {
198
+ cpfpLeafSigningJobs,
199
+ directLeafSigningJobs,
200
+ directFromCpfpLeafSigningJobs,
201
+ };
202
+ }
203
+
204
+ async signRefundsForLightning(
205
+ leaves: LeafKeyTweak[],
206
+ receiverIdentityPubkey: Uint8Array,
207
+ cpfpSigningCommitments: RequestedSigningCommitments[],
208
+ directSigningCommitments: RequestedSigningCommitments[],
209
+ directFromCpfpSigningCommitments: RequestedSigningCommitments[],
210
+ hash: Uint8Array,
211
+ ): Promise<{
212
+ cpfpLeafSigningJobs: UserSignedTxSigningJob[];
213
+ directLeafSigningJobs: UserSignedTxSigningJob[];
214
+ directFromCpfpLeafSigningJobs: UserSignedTxSigningJob[];
215
+ }> {
216
+ const network = getNetwork(this.config.getNetwork());
217
+ const cpfpLeafSigningJobs: UserSignedTxSigningJob[] = [];
218
+ const directLeafSigningJobs: UserSignedTxSigningJob[] = [];
219
+ const directFromCpfpLeafSigningJobs: UserSignedTxSigningJob[] = [];
220
+
221
+ for (let i = 0; i < leaves.length; i++) {
222
+ const leaf = leaves[i];
223
+ if (!leaf?.leaf) {
224
+ throw new ValidationError("Leaf not found in signRefunds", {
225
+ field: "leaf",
226
+ value: leaf,
227
+ expected: "Non-null leaf",
228
+ });
229
+ }
230
+
231
+ const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
109
232
 
110
233
  const currRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
111
234
 
@@ -117,8 +240,6 @@ export class SigningService {
117
240
  expected: "Non-null sequence",
118
241
  });
119
242
  }
120
- const { nextSequence, nextDirectSequence } =
121
- getNextTransactionSequence(sequence);
122
243
 
123
244
  const amountSats = currRefundTx.getOutput(0).amount;
124
245
  if (amountSats === undefined) {
@@ -129,25 +250,27 @@ export class SigningService {
129
250
  });
130
251
  }
131
252
 
253
+ const { nextSequence, nextDirectSequence } =
254
+ getNextHTLCTransactionSequence(sequence);
255
+
132
256
  let directNodeTx: Transaction | undefined;
133
- let directNodeOutPoint: TransactionInput | undefined;
134
257
  if (leaf.leaf.directTx.length > 0) {
135
258
  directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
136
- directNodeOutPoint = {
137
- txid: hexToBytes(getTxId(directNodeTx)),
138
- index: 0,
139
- };
140
259
  }
141
260
 
261
+ const identityPublicKey = await this.config.signer.getIdentityPublicKey();
262
+
142
263
  const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } =
143
- createRefundTxs({
264
+ createRefundTxsForLightning({
265
+ nodeTx: nodeTx,
266
+ directNodeTx: directNodeTx,
267
+ vout: 0,
268
+ network,
144
269
  sequence: nextSequence,
145
270
  directSequence: nextDirectSequence,
146
- input: cpfpNodeOutPoint,
147
- directInput: directNodeOutPoint,
148
- amountSats,
149
- receivingPubkey: receiverIdentityPubkey,
150
- network: this.config.getNetwork(),
271
+ hash,
272
+ hashLockDestinationPubkey: receiverIdentityPubkey,
273
+ sequenceLockDestinationPubkey: identityPublicKey,
151
274
  });
152
275
 
153
276
  const refundSighash = getSigHashFromTx(
@@ -190,16 +313,6 @@ export class SigningService {
190
313
  }
191
314
 
192
315
  if (directFromCpfpRefundTx) {
193
- if (!directNodeTx) {
194
- throw new ValidationError(
195
- "Direct node transaction undefined while direct from CPFP refund transaction is defined",
196
- {
197
- field: "directNodeTx",
198
- value: directNodeTx,
199
- expected: "Non-null direct node transaction",
200
- },
201
- );
202
- }
203
316
  const refundSighash = getSigHashFromTx(
204
317
  directFromCpfpRefundTx,
205
318
  0,
@@ -221,4 +334,41 @@ export class SigningService {
221
334
  directFromCpfpLeafSigningJobs,
222
335
  };
223
336
  }
337
+
338
+ async signSigningJobs(
339
+ signingJobs: (SigningJobWithOptionalNonce & RequestedSigningCommitments)[],
340
+ ): Promise<Map<SigningJobType, UserSignedTxSigningJob>> {
341
+ const userSignedTxSigningJobs: Map<SigningJobType, UserSignedTxSigningJob> =
342
+ new Map();
343
+
344
+ for (const signingJob of signingJobs) {
345
+ const rawTx = getTxFromRawTxBytes(signingJob.rawTx);
346
+ const txOut = signingJob.parentTxOut;
347
+ const rawTxSighash = getSigHashFromTx(rawTx, 0, txOut);
348
+ const userSignature = await this.config.signer.signFrost({
349
+ message: rawTxSighash,
350
+ keyDerivation: signingJob.keyDerivation,
351
+ publicKey: signingJob.signingPublicKey,
352
+ verifyingKey: signingJob.verifyingKey,
353
+ selfCommitment: signingJob.signingNonceCommitment,
354
+ statechainCommitments: signingJob.signingNonceCommitments,
355
+ adaptorPubKey: new Uint8Array(),
356
+ });
357
+
358
+ const userSignedTxSigningJob: UserSignedTxSigningJob = {
359
+ leafId: signingJob.leafId,
360
+ signingPublicKey: signingJob.signingPublicKey,
361
+ rawTx: rawTx.toBytes(),
362
+ signingNonceCommitment: signingJob.signingNonceCommitment.commitment,
363
+ signingCommitments: {
364
+ signingCommitments: signingJob.signingNonceCommitments,
365
+ },
366
+ userSignature,
367
+ };
368
+
369
+ userSignedTxSigningJobs.set(signingJob.type, userSignedTxSigningJob);
370
+ }
371
+
372
+ return userSignedTxSigningJobs;
373
+ }
224
374
  }