@buildonspark/spark-sdk 0.3.8 → 0.4.0

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 +18 -0
  2. package/android/build.gradle +1 -1
  3. package/android/src/main/java/uniffi/uniffi/spark_frost/spark_frost.kt +1361 -1367
  4. package/android/src/main/jniLibs/arm64-v8a/libuniffi_spark_frost.so +0 -0
  5. package/android/src/main/jniLibs/armeabi-v7a/libuniffi_spark_frost.so +0 -0
  6. package/android/src/main/jniLibs/x86/libuniffi_spark_frost.so +0 -0
  7. package/android/src/main/jniLibs/x86_64/libuniffi_spark_frost.so +0 -0
  8. package/dist/bare/index.cjs +1342 -1346
  9. package/dist/bare/index.d.cts +114 -68
  10. package/dist/bare/index.d.ts +114 -68
  11. package/dist/bare/index.js +1266 -1279
  12. package/dist/{chunk-FXIESWE6.js → chunk-27ILUWDJ.js} +1 -1
  13. package/dist/{chunk-5ASXVNTM.js → chunk-4YFT7DAE.js} +1 -1
  14. package/dist/{chunk-FP2CRVQH.js → chunk-G3LHXHF3.js} +1045 -1308
  15. package/dist/{chunk-XI6FCNYG.js → chunk-JLF6WJ7K.js} +1 -1
  16. package/dist/{chunk-3LPEQGVJ.js → chunk-LOXWCMZL.js} +1 -1
  17. package/dist/{chunk-5HU5W56H.js → chunk-WICAF6BS.js} +2 -2
  18. package/dist/{chunk-VFN34EOX.js → chunk-YEBEN7XD.js} +258 -0
  19. package/dist/{client-pNpGP15j.d.cts → client-BIqiUNy4.d.cts} +1 -1
  20. package/dist/{client-By-N7oJS.d.ts → client-BaQf-5gD.d.ts} +1 -1
  21. package/dist/debug.cjs +1330 -1336
  22. package/dist/debug.d.cts +20 -16
  23. package/dist/debug.d.ts +20 -16
  24. package/dist/debug.js +5 -5
  25. package/dist/graphql/objects/index.d.cts +3 -3
  26. package/dist/graphql/objects/index.d.ts +3 -3
  27. package/dist/index.cjs +1363 -1365
  28. package/dist/index.d.cts +7 -7
  29. package/dist/index.d.ts +7 -7
  30. package/dist/index.js +32 -24
  31. package/dist/index.node.cjs +1363 -1365
  32. package/dist/index.node.d.cts +7 -7
  33. package/dist/index.node.d.ts +7 -7
  34. package/dist/index.node.js +31 -23
  35. package/dist/{logging-DMFVY384.d.ts → logging-BNGm6dBp.d.ts} +42 -37
  36. package/dist/{logging-DxLp34Xm.d.cts → logging-D3IfXfHG.d.cts} +42 -37
  37. package/dist/native/index.react-native.cjs +1505 -1366
  38. package/dist/native/index.react-native.d.cts +112 -58
  39. package/dist/native/index.react-native.d.ts +112 -58
  40. package/dist/native/index.react-native.js +1415 -1289
  41. package/dist/proto/spark.cjs +258 -0
  42. package/dist/proto/spark.d.cts +1 -1
  43. package/dist/proto/spark.d.ts +1 -1
  44. package/dist/proto/spark.js +5 -1
  45. package/dist/proto/spark_token.d.cts +1 -1
  46. package/dist/proto/spark_token.d.ts +1 -1
  47. package/dist/proto/spark_token.js +2 -2
  48. package/dist/{spark-By6yHsrk.d.cts → spark-DOpheE8_.d.cts} +39 -2
  49. package/dist/{spark-By6yHsrk.d.ts → spark-DOpheE8_.d.ts} +39 -2
  50. package/dist/{spark-wallet.browser-C1dQknVj.d.ts → spark-wallet.browser-B2rGwjuM.d.ts} +2 -2
  51. package/dist/{spark-wallet.browser-CNMo3IvO.d.cts → spark-wallet.browser-Ck9No4Ks.d.cts} +2 -2
  52. package/dist/{spark-wallet.node-Og6__NMh.d.ts → spark-wallet.node-BqmKsGPs.d.ts} +2 -2
  53. package/dist/{spark-wallet.node-BZJhJZKq.d.cts → spark-wallet.node-C2TIkyt4.d.cts} +2 -2
  54. package/dist/tests/test-utils.cjs +1304 -1236
  55. package/dist/tests/test-utils.d.cts +18 -4
  56. package/dist/tests/test-utils.d.ts +18 -4
  57. package/dist/tests/test-utils.js +7 -7
  58. package/dist/{token-transactions-CLR3rnYi.d.cts → token-transactions-Db8mkjnU.d.cts} +2 -2
  59. package/dist/{token-transactions-C7yefB2S.d.ts → token-transactions-DoMcrxXQ.d.ts} +2 -2
  60. package/dist/types/index.cjs +256 -0
  61. package/dist/types/index.d.cts +2 -2
  62. package/dist/types/index.d.ts +2 -2
  63. package/dist/types/index.js +2 -2
  64. package/dist/{wallet-config-BoyMVa6n.d.ts → wallet-config-Bg3kWltL.d.ts} +42 -35
  65. package/dist/{wallet-config-xom-9UFF.d.cts → wallet-config-CuZKNo9S.d.cts} +42 -35
  66. package/ios/spark_frostFFI.xcframework/ios-arm64/SparkFrost +0 -0
  67. package/ios/spark_frostFFI.xcframework/ios-arm64/spark_frostFFI.framework/spark_frostFFI +0 -0
  68. package/ios/spark_frostFFI.xcframework/ios-arm64_x86_64-simulator/SparkFrost +0 -0
  69. package/ios/spark_frostFFI.xcframework/ios-arm64_x86_64-simulator/spark_frostFFI.framework/spark_frostFFI +0 -0
  70. package/ios/spark_frostFFI.xcframework/macos-arm64_x86_64/spark_frostFFI.framework/spark_frostFFI +0 -0
  71. package/package.json +1 -1
  72. package/src/index.react-native.ts +8 -2
  73. package/src/proto/spark.ts +348 -4
  74. package/src/services/config.ts +5 -0
  75. package/src/services/coop-exit.ts +26 -107
  76. package/src/services/deposit.ts +12 -48
  77. package/src/services/signing.ts +62 -49
  78. package/src/services/transfer.ts +437 -722
  79. package/src/services/wallet-config.ts +10 -0
  80. package/src/services/xhr-transport.ts +13 -3
  81. package/src/signer/signer.react-native.ts +73 -1
  82. package/src/spark-wallet/proto-descriptors.ts +1 -1
  83. package/src/spark-wallet/spark-wallet.ts +162 -315
  84. package/src/spark-wallet/types.ts +2 -2
  85. package/src/spark_descriptors.pb +0 -0
  86. package/src/tests/integration/lightning.test.ts +1 -27
  87. package/src/tests/integration/static_deposit.test.ts +6 -9
  88. package/src/tests/integration/unilateral-exit.test.ts +117 -0
  89. package/src/tests/optimize.test.ts +31 -1
  90. package/src/tests/utils/signing.ts +33 -0
  91. package/src/tests/utils/test-faucet.ts +61 -0
  92. package/src/utils/optimize.ts +42 -0
  93. package/src/utils/transaction.ts +250 -249
  94. package/src/utils/unilateral-exit.ts +1 -40
@@ -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
  });
@@ -1,26 +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,
12
+ createDecrementedTimelockRefundTxs,
13
+ getCurrentTimelock,
17
14
  getNextHTLCTransactionSequence,
18
- getNextTransactionSequence,
19
15
  } from "../utils/transaction.js";
20
- import { createRefundTxsForLightning } from "../utils/htlc-transactions.js";
21
- import { getNetwork } from "../utils/network.js";
22
16
  import { WalletConfigService } from "./config.js";
23
- import type { LeafKeyTweak } from "./transfer.js";
17
+ import type {
18
+ LeafKeyTweak,
19
+ SigningJobType,
20
+ SigningJobWithOptionalNonce,
21
+ } from "./transfer.js";
24
22
 
25
23
  export class SigningService {
26
24
  private readonly config: WalletConfigService;
@@ -105,24 +103,9 @@ export class SigningService {
105
103
  }
106
104
 
107
105
  const nodeTx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
108
- const cpfpNodeOutPoint: TransactionInput = {
109
- txid: hexToBytes(getTxId(nodeTx)),
110
- index: 0,
111
- };
112
106
 
113
107
  const currRefundTx = getTxFromRawTxBytes(leaf.leaf.refundTx);
114
108
 
115
- const sequence = currRefundTx.getInput(0).sequence;
116
- if (!sequence) {
117
- throw new ValidationError("Invalid refund transaction", {
118
- field: "sequence",
119
- value: currRefundTx.getInput(0),
120
- expected: "Non-null sequence",
121
- });
122
- }
123
- const { nextSequence, nextDirectSequence } =
124
- getNextTransactionSequence(sequence);
125
-
126
109
  const amountSats = currRefundTx.getOutput(0).amount;
127
110
  if (amountSats === undefined) {
128
111
  throw new ValidationError("Invalid refund transaction", {
@@ -133,22 +116,24 @@ export class SigningService {
133
116
  }
134
117
 
135
118
  let directNodeTx: Transaction | undefined;
136
- let directNodeOutPoint: TransactionInput | undefined;
137
119
  if (leaf.leaf.directTx.length > 0) {
138
120
  directNodeTx = getTxFromRawTxBytes(leaf.leaf.directTx);
139
- directNodeOutPoint = {
140
- txid: hexToBytes(getTxId(directNodeTx)),
141
- index: 0,
142
- };
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
+ });
143
130
  }
144
131
 
145
132
  const { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx } =
146
- createRefundTxs({
147
- sequence: nextSequence,
148
- directSequence: nextDirectSequence,
149
- input: cpfpNodeOutPoint,
150
- directInput: directNodeOutPoint,
151
- amountSats,
133
+ createDecrementedTimelockRefundTxs({
134
+ nodeTx: nodeTx,
135
+ directNodeTx: directNodeTx,
136
+ sequence: currentSequence,
152
137
  receivingPubkey: receiverIdentityPubkey,
153
138
  network: this.config.getNetwork(),
154
139
  });
@@ -167,7 +152,8 @@ export class SigningService {
167
152
 
168
153
  cpfpLeafSigningJobs.push(...signingJobs);
169
154
 
170
- if (directRefundTx) {
155
+ const isZeroNode = getCurrentTimelock(nodeTx.getInput(0).sequence);
156
+ if (directRefundTx && !isZeroNode) {
171
157
  if (!directNodeTx) {
172
158
  throw new ValidationError(
173
159
  "Direct node transaction undefined while direct refund transaction is defined",
@@ -193,16 +179,6 @@ export class SigningService {
193
179
  }
194
180
 
195
181
  if (directFromCpfpRefundTx) {
196
- if (!directNodeTx) {
197
- throw new ValidationError(
198
- "Direct node transaction undefined while direct from CPFP refund transaction is defined",
199
- {
200
- field: "directNodeTx",
201
- value: directNodeTx,
202
- expected: "Non-null direct node transaction",
203
- },
204
- );
205
- }
206
182
  const refundSighash = getSigHashFromTx(
207
183
  directFromCpfpRefundTx,
208
184
  0,
@@ -358,4 +334,41 @@ export class SigningService {
358
334
  directFromCpfpLeafSigningJobs,
359
335
  };
360
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
+ }
361
374
  }