@arkade-os/sdk 0.1.4 → 0.2.1

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 (114) hide show
  1. package/README.md +157 -174
  2. package/dist/cjs/arknote/index.js +61 -58
  3. package/dist/cjs/bip322/errors.js +13 -0
  4. package/dist/cjs/bip322/index.js +178 -0
  5. package/dist/cjs/forfeit.js +14 -25
  6. package/dist/cjs/identity/singleKey.js +68 -0
  7. package/dist/cjs/index.js +43 -17
  8. package/dist/cjs/providers/ark.js +261 -321
  9. package/dist/cjs/providers/indexer.js +525 -0
  10. package/dist/cjs/providers/onchain.js +193 -15
  11. package/dist/cjs/script/address.js +48 -17
  12. package/dist/cjs/script/base.js +120 -3
  13. package/dist/cjs/script/default.js +18 -4
  14. package/dist/cjs/script/tapscript.js +61 -20
  15. package/dist/cjs/script/vhtlc.js +85 -7
  16. package/dist/cjs/tree/signingSession.js +63 -106
  17. package/dist/cjs/tree/txTree.js +193 -0
  18. package/dist/cjs/tree/validation.js +79 -155
  19. package/dist/cjs/utils/anchor.js +35 -0
  20. package/dist/cjs/utils/arkTransaction.js +108 -0
  21. package/dist/cjs/utils/transactionHistory.js +84 -72
  22. package/dist/cjs/utils/txSizeEstimator.js +12 -0
  23. package/dist/cjs/utils/unknownFields.js +211 -0
  24. package/dist/cjs/wallet/index.js +12 -0
  25. package/dist/cjs/wallet/onchain.js +201 -0
  26. package/dist/cjs/wallet/ramps.js +95 -0
  27. package/dist/cjs/wallet/serviceWorker/db/vtxo/idb.js +32 -0
  28. package/dist/cjs/wallet/serviceWorker/request.js +15 -12
  29. package/dist/cjs/wallet/serviceWorker/response.js +22 -27
  30. package/dist/cjs/wallet/serviceWorker/utils.js +8 -0
  31. package/dist/cjs/wallet/serviceWorker/wallet.js +61 -34
  32. package/dist/cjs/wallet/serviceWorker/worker.js +120 -108
  33. package/dist/cjs/wallet/unroll.js +270 -0
  34. package/dist/cjs/wallet/wallet.js +701 -454
  35. package/dist/esm/arknote/index.js +61 -57
  36. package/dist/esm/bip322/errors.js +9 -0
  37. package/dist/esm/bip322/index.js +174 -0
  38. package/dist/esm/forfeit.js +15 -26
  39. package/dist/esm/identity/singleKey.js +64 -0
  40. package/dist/esm/index.js +31 -12
  41. package/dist/esm/providers/ark.js +259 -320
  42. package/dist/esm/providers/indexer.js +521 -0
  43. package/dist/esm/providers/onchain.js +193 -15
  44. package/dist/esm/script/address.js +48 -17
  45. package/dist/esm/script/base.js +120 -3
  46. package/dist/esm/script/default.js +18 -4
  47. package/dist/esm/script/tapscript.js +61 -20
  48. package/dist/esm/script/vhtlc.js +85 -7
  49. package/dist/esm/tree/signingSession.js +65 -108
  50. package/dist/esm/tree/txTree.js +189 -0
  51. package/dist/esm/tree/validation.js +75 -152
  52. package/dist/esm/utils/anchor.js +31 -0
  53. package/dist/esm/utils/arkTransaction.js +105 -0
  54. package/dist/esm/utils/transactionHistory.js +84 -72
  55. package/dist/esm/utils/txSizeEstimator.js +12 -0
  56. package/dist/esm/utils/unknownFields.js +173 -0
  57. package/dist/esm/wallet/index.js +9 -0
  58. package/dist/esm/wallet/onchain.js +196 -0
  59. package/dist/esm/wallet/ramps.js +91 -0
  60. package/dist/esm/wallet/serviceWorker/db/vtxo/idb.js +32 -0
  61. package/dist/esm/wallet/serviceWorker/request.js +15 -12
  62. package/dist/esm/wallet/serviceWorker/response.js +22 -27
  63. package/dist/esm/wallet/serviceWorker/utils.js +8 -0
  64. package/dist/esm/wallet/serviceWorker/wallet.js +62 -35
  65. package/dist/esm/wallet/serviceWorker/worker.js +120 -108
  66. package/dist/esm/wallet/unroll.js +267 -0
  67. package/dist/esm/wallet/wallet.js +674 -461
  68. package/dist/types/arknote/index.d.ts +40 -13
  69. package/dist/types/bip322/errors.d.ts +6 -0
  70. package/dist/types/bip322/index.d.ts +57 -0
  71. package/dist/types/forfeit.d.ts +2 -14
  72. package/dist/types/identity/singleKey.d.ts +27 -0
  73. package/dist/types/index.d.ts +24 -12
  74. package/dist/types/providers/ark.d.ts +114 -95
  75. package/dist/types/providers/indexer.d.ts +186 -0
  76. package/dist/types/providers/onchain.d.ts +41 -11
  77. package/dist/types/script/address.d.ts +26 -2
  78. package/dist/types/script/base.d.ts +13 -3
  79. package/dist/types/script/default.d.ts +22 -0
  80. package/dist/types/script/tapscript.d.ts +61 -5
  81. package/dist/types/script/vhtlc.d.ts +27 -0
  82. package/dist/types/tree/signingSession.d.ts +5 -5
  83. package/dist/types/tree/txTree.d.ts +28 -0
  84. package/dist/types/tree/validation.d.ts +15 -22
  85. package/dist/types/utils/anchor.d.ts +19 -0
  86. package/dist/types/utils/arkTransaction.d.ts +27 -0
  87. package/dist/types/utils/transactionHistory.d.ts +7 -1
  88. package/dist/types/utils/txSizeEstimator.d.ts +3 -0
  89. package/dist/types/utils/unknownFields.d.ts +83 -0
  90. package/dist/types/wallet/index.d.ts +51 -50
  91. package/dist/types/wallet/onchain.d.ts +49 -0
  92. package/dist/types/wallet/ramps.d.ts +32 -0
  93. package/dist/types/wallet/serviceWorker/db/vtxo/idb.d.ts +2 -0
  94. package/dist/types/wallet/serviceWorker/db/vtxo/index.d.ts +2 -0
  95. package/dist/types/wallet/serviceWorker/request.d.ts +14 -16
  96. package/dist/types/wallet/serviceWorker/response.d.ts +17 -19
  97. package/dist/types/wallet/serviceWorker/utils.d.ts +8 -0
  98. package/dist/types/wallet/serviceWorker/wallet.d.ts +36 -8
  99. package/dist/types/wallet/serviceWorker/worker.d.ts +7 -3
  100. package/dist/types/wallet/unroll.d.ts +102 -0
  101. package/dist/types/wallet/wallet.d.ts +71 -25
  102. package/package.json +37 -35
  103. package/dist/cjs/identity/inMemoryKey.js +0 -40
  104. package/dist/cjs/tree/vtxoTree.js +0 -231
  105. package/dist/cjs/utils/coinselect.js +0 -73
  106. package/dist/cjs/utils/psbt.js +0 -137
  107. package/dist/esm/identity/inMemoryKey.js +0 -36
  108. package/dist/esm/tree/vtxoTree.js +0 -191
  109. package/dist/esm/utils/coinselect.js +0 -69
  110. package/dist/esm/utils/psbt.js +0 -131
  111. package/dist/types/identity/inMemoryKey.d.ts +0 -12
  112. package/dist/types/tree/vtxoTree.d.ts +0 -33
  113. package/dist/types/utils/coinselect.d.ts +0 -21
  114. package/dist/types/utils/psbt.d.ts +0 -11
@@ -39,6 +39,7 @@ const bip68 = __importStar(require("bip68"));
39
39
  const script_1 = require("@scure/btc-signer/script");
40
40
  const payment_1 = require("@scure/btc-signer/payment");
41
41
  const base_1 = require("@scure/base");
42
+ const MinimalScriptNum = (0, script_1.ScriptNum)(undefined, true);
42
43
  var TapscriptType;
43
44
  (function (TapscriptType) {
44
45
  TapscriptType["Multisig"] = "multisig";
@@ -47,6 +48,16 @@ var TapscriptType;
47
48
  TapscriptType["ConditionMultisig"] = "condition-multisig";
48
49
  TapscriptType["CLTVMultisig"] = "cltv-multisig";
49
50
  })(TapscriptType || (exports.TapscriptType = TapscriptType = {}));
51
+ /**
52
+ * decodeTapscript is a function that decodes an ark tapsript from a raw script.
53
+ *
54
+ * @throws {Error} if the script is not a valid ark tapscript
55
+ * @example
56
+ * ```typescript
57
+ * const arkTapscript = decodeTapscript(new Uint8Array(32));
58
+ * console.log("type:", arkTapscript.type);
59
+ * ```
60
+ */
50
61
  function decodeTapscript(script) {
51
62
  const types = [
52
63
  MultisigTapscript,
@@ -66,8 +77,14 @@ function decodeTapscript(script) {
66
77
  throw new Error(`Failed to decode: script ${base_1.hex.encode(script)} is not a valid tapscript`);
67
78
  }
68
79
  /**
69
- * Implements a multi-signature script that requires a threshold of signatures
70
- * from the specified pubkeys.
80
+ * Implements a multi-signature tapscript.
81
+ *
82
+ * <pubkey> CHECKSIGVERIFY <pubkey> CHECKSIG
83
+ *
84
+ * @example
85
+ * ```typescript
86
+ * const multisigTapscript = MultisigTapscript.encode({ pubkeys: [new Uint8Array(32), new Uint8Array(32)] });
87
+ * ```
71
88
  */
72
89
  var MultisigTapscript;
73
90
  (function (MultisigTapscript) {
@@ -93,7 +110,6 @@ var MultisigTapscript;
93
110
  type: TapscriptType.Multisig,
94
111
  params,
95
112
  script: (0, payment_1.p2tr_ms)(params.pubkeys.length, params.pubkeys).script,
96
- witnessSize: () => params.pubkeys.length * 64,
97
113
  };
98
114
  }
99
115
  const asm = [];
@@ -111,7 +127,6 @@ var MultisigTapscript;
111
127
  type: TapscriptType.Multisig,
112
128
  params,
113
129
  script: script_1.Script.encode(asm),
114
- witnessSize: () => params.pubkeys.length * 64,
115
130
  };
116
131
  }
117
132
  MultisigTapscript.encode = encode;
@@ -182,7 +197,6 @@ var MultisigTapscript;
182
197
  type: TapscriptType.Multisig,
183
198
  params: { pubkeys, type: MultisigType.CHECKSIGADD },
184
199
  script,
185
- witnessSize: () => pubkeys.length * 64,
186
200
  };
187
201
  }
188
202
  // <pubkey> CHECKSIGVERIFY <pubkey> CHECKSIG
@@ -226,7 +240,6 @@ var MultisigTapscript;
226
240
  type: TapscriptType.Multisig,
227
241
  params: { pubkeys, type: MultisigType.CHECKSIG },
228
242
  script,
229
- witnessSize: () => pubkeys.length * 64,
230
243
  };
231
244
  }
232
245
  function is(tapscript) {
@@ -239,6 +252,13 @@ var MultisigTapscript;
239
252
  * after the relative timelock has expired. The timelock can be specified in blocks or seconds.
240
253
  *
241
254
  * This is the standard exit closure and it is also used for the sweep closure in vtxo trees.
255
+ *
256
+ * <sequence> CHECKSEQUENCEVERIFY DROP <pubkey> CHECKSIG
257
+ *
258
+ * @example
259
+ * ```typescript
260
+ * const csvMultisigTapscript = CSVMultisigTapscript.encode({ timelock: { type: "blocks", value: 144 }, pubkeys: [new Uint8Array(32), new Uint8Array(32)] });
261
+ * ```
242
262
  */
243
263
  var CSVMultisigTapscript;
244
264
  (function (CSVMultisigTapscript) {
@@ -248,10 +268,14 @@ var CSVMultisigTapscript;
248
268
  throw new Error(`Invalid pubkey length: expected 32, got ${pubkey.length}`);
249
269
  }
250
270
  }
251
- const sequence = (0, script_1.ScriptNum)().encode(BigInt(bip68.encode(params.timelock.type === "blocks"
271
+ const sequence = MinimalScriptNum.encode(BigInt(bip68.encode(params.timelock.type === "blocks"
252
272
  ? { blocks: Number(params.timelock.value) }
253
273
  : { seconds: Number(params.timelock.value) })));
254
- const asm = [sequence, "CHECKSEQUENCEVERIFY", "DROP"];
274
+ const asm = [
275
+ sequence.length === 1 ? sequence[0] : sequence,
276
+ "CHECKSEQUENCEVERIFY",
277
+ "DROP",
278
+ ];
255
279
  const multisigScript = MultisigTapscript.encode(params);
256
280
  const script = new Uint8Array([
257
281
  ...script_1.Script.encode(asm),
@@ -261,7 +285,6 @@ var CSVMultisigTapscript;
261
285
  type: TapscriptType.CSVMultisig,
262
286
  params,
263
287
  script,
264
- witnessSize: () => params.pubkeys.length * 64,
265
288
  };
266
289
  }
267
290
  CSVMultisigTapscript.encode = encode;
@@ -288,7 +311,7 @@ var CSVMultisigTapscript;
288
311
  catch (error) {
289
312
  throw new Error(`Invalid multisig script: ${error instanceof Error ? error.message : String(error)}`);
290
313
  }
291
- const sequenceNum = Number((0, script_1.ScriptNum)().decode(sequence));
314
+ const sequenceNum = Number(MinimalScriptNum.decode(sequence));
292
315
  const decodedTimelock = bip68.decode(sequenceNum);
293
316
  const timelock = decodedTimelock.blocks !== undefined
294
317
  ? { type: "blocks", value: BigInt(decodedTimelock.blocks) }
@@ -307,7 +330,6 @@ var CSVMultisigTapscript;
307
330
  ...multisig.params,
308
331
  },
309
332
  script,
310
- witnessSize: () => multisig.params.pubkeys.length * 64,
311
333
  };
312
334
  }
313
335
  CSVMultisigTapscript.decode = decode;
@@ -320,6 +342,13 @@ var CSVMultisigTapscript;
320
342
  * Combines a condition script with an exit closure. The resulting script requires
321
343
  * the condition to be met, followed by the standard exit closure requirements
322
344
  * (timelock and signatures).
345
+ *
346
+ * <conditionScript> VERIFY <sequence> CHECKSEQUENCEVERIFY DROP <pubkey> CHECKSIGVERIFY <pubkey> CHECKSIG
347
+ *
348
+ * @example
349
+ * ```typescript
350
+ * const conditionCSVMultisigTapscript = ConditionCSVMultisigTapscript.encode({ conditionScript: new Uint8Array(32), pubkeys: [new Uint8Array(32), new Uint8Array(32)] });
351
+ * ```
323
352
  */
324
353
  var ConditionCSVMultisigTapscript;
325
354
  (function (ConditionCSVMultisigTapscript) {
@@ -333,7 +362,6 @@ var ConditionCSVMultisigTapscript;
333
362
  type: TapscriptType.ConditionCSVMultisig,
334
363
  params,
335
364
  script,
336
- witnessSize: (conditionSize) => conditionSize + params.pubkeys.length * 64,
337
365
  };
338
366
  }
339
367
  ConditionCSVMultisigTapscript.encode = encode;
@@ -377,7 +405,6 @@ var ConditionCSVMultisigTapscript;
377
405
  ...csvMultisig.params,
378
406
  },
379
407
  script,
380
- witnessSize: (conditionSize) => conditionSize + csvMultisig.params.pubkeys.length * 64,
381
408
  };
382
409
  }
383
410
  ConditionCSVMultisigTapscript.decode = decode;
@@ -390,6 +417,13 @@ var ConditionCSVMultisigTapscript;
390
417
  * Combines a condition script with a forfeit closure. The resulting script requires
391
418
  * the condition to be met, followed by the standard forfeit closure requirements
392
419
  * (multi-signature).
420
+ *
421
+ * <conditionScript> VERIFY <pubkey> CHECKSIGVERIFY <pubkey> CHECKSIG
422
+ *
423
+ * @example
424
+ * ```typescript
425
+ * const conditionMultisigTapscript = ConditionMultisigTapscript.encode({ conditionScript: new Uint8Array(32), pubkeys: [new Uint8Array(32), new Uint8Array(32)] });
426
+ * ```
393
427
  */
394
428
  var ConditionMultisigTapscript;
395
429
  (function (ConditionMultisigTapscript) {
@@ -403,7 +437,6 @@ var ConditionMultisigTapscript;
403
437
  type: TapscriptType.ConditionMultisig,
404
438
  params,
405
439
  script,
406
- witnessSize: (conditionSize) => conditionSize + params.pubkeys.length * 64,
407
440
  };
408
441
  }
409
442
  ConditionMultisigTapscript.encode = encode;
@@ -447,7 +480,6 @@ var ConditionMultisigTapscript;
447
480
  ...multisig.params,
448
481
  },
449
482
  script,
450
- witnessSize: (conditionSize) => conditionSize + multisig.params.pubkeys.length * 64,
451
483
  };
452
484
  }
453
485
  ConditionMultisigTapscript.decode = decode;
@@ -460,12 +492,23 @@ var ConditionMultisigTapscript;
460
492
  * Implements an absolute timelock (CLTV) script combined with a forfeit closure.
461
493
  * The script requires waiting until a specific block height/timestamp before the
462
494
  * forfeit closure conditions can be met.
495
+ *
496
+ * <locktime> CHECKLOCKTIMEVERIFY DROP <pubkey> CHECKSIGVERIFY <pubkey> CHECKSIG
497
+ *
498
+ * @example
499
+ * ```typescript
500
+ * const cltvMultisigTapscript = CLTVMultisigTapscript.encode({ absoluteTimelock: 144, pubkeys: [new Uint8Array(32), new Uint8Array(32)] });
501
+ * ```
463
502
  */
464
503
  var CLTVMultisigTapscript;
465
504
  (function (CLTVMultisigTapscript) {
466
505
  function encode(params) {
467
- const locktime = (0, script_1.ScriptNum)().encode(params.absoluteTimelock);
468
- const asm = [locktime, "CHECKLOCKTIMEVERIFY", "DROP"];
506
+ const locktime = MinimalScriptNum.encode(params.absoluteTimelock);
507
+ const asm = [
508
+ locktime.length === 1 ? locktime[0] : locktime,
509
+ "CHECKLOCKTIMEVERIFY",
510
+ "DROP",
511
+ ];
469
512
  const timelockedScript = script_1.Script.encode(asm);
470
513
  const script = new Uint8Array([
471
514
  ...timelockedScript,
@@ -475,7 +518,6 @@ var CLTVMultisigTapscript;
475
518
  type: TapscriptType.CLTVMultisig,
476
519
  params,
477
520
  script,
478
- witnessSize: () => params.pubkeys.length * 64,
479
521
  };
480
522
  }
481
523
  CLTVMultisigTapscript.encode = encode;
@@ -502,7 +544,7 @@ var CLTVMultisigTapscript;
502
544
  catch (error) {
503
545
  throw new Error(`Invalid multisig script: ${error instanceof Error ? error.message : String(error)}`);
504
546
  }
505
- const absoluteTimelock = (0, script_1.ScriptNum)().decode(locktime);
547
+ const absoluteTimelock = MinimalScriptNum.decode(locktime);
506
548
  const reconstructed = encode({
507
549
  absoluteTimelock,
508
550
  ...multisig.params,
@@ -517,7 +559,6 @@ var CLTVMultisigTapscript;
517
559
  ...multisig.params,
518
560
  },
519
561
  script,
520
- witnessSize: () => multisig.params.pubkeys.length * 64,
521
562
  };
522
563
  }
523
564
  CLTVMultisigTapscript.decode = decode;
@@ -5,17 +5,38 @@ const btc_signer_1 = require("@scure/btc-signer");
5
5
  const tapscript_1 = require("./tapscript");
6
6
  const base_1 = require("@scure/base");
7
7
  const base_2 = require("./base");
8
- // VHTLC is an Hashed Timelock Contract VtxoScript implementation
9
- // - claim (preimage + receiver)
10
- // - refund (sender + receiver + server)
11
- // - refundWithoutReceiver (at refundLocktime, sender + receiver + server)
12
- // - unilateralClaim (preimage + receiver after unilateralClaimDelay)
13
- // - unilateralRefund (sender + receiver after unilateralRefundDelay)
14
- // - unilateralRefundWithoutReceiver (sender after unilateralRefundWithoutReceiverDelay)
8
+ /**
9
+ * Virtual Hash Time Lock Contract (VHTLC) implementation.
10
+ *
11
+ * VHTLC is a contract that enables atomic swaps and conditional payments
12
+ * in the Ark protocol. It provides multiple spending paths:
13
+ *
14
+ * - **claim**: Receiver can claim funds by revealing the preimage
15
+ * - **refund**: Sender and receiver can collaboratively refund
16
+ * - **refundWithoutReceiver**: Sender can refund after locktime expires
17
+ * - **unilateralClaim**: Receiver can claim unilaterally after delay
18
+ * - **unilateralRefund**: Sender and receiver can refund unilaterally after delay
19
+ * - **unilateralRefundWithoutReceiver**: Sender can refund unilaterally after delay
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * const vhtlc = new VHTLC.Script({
24
+ * sender: alicePubKey,
25
+ * receiver: bobPubKey,
26
+ * server: serverPubKey,
27
+ * preimageHash: hash160(secret),
28
+ * refundLocktime: BigInt(chainTip + 10),
29
+ * unilateralClaimDelay: { type: 'blocks', value: 100n },
30
+ * unilateralRefundDelay: { type: 'blocks', value: 102n },
31
+ * unilateralRefundWithoutReceiverDelay: { type: 'blocks', value: 103n }
32
+ * });
33
+ * ```
34
+ */
15
35
  var VHTLC;
16
36
  (function (VHTLC) {
17
37
  class Script extends base_2.VtxoScript {
18
38
  constructor(options) {
39
+ validateOptions(options);
19
40
  const { sender, receiver, server, preimageHash, refundLocktime, unilateralClaimDelay, unilateralRefundDelay, unilateralRefundWithoutReceiverDelay, } = options;
20
41
  const conditionScript = preimageConditionScript(preimageHash);
21
42
  const claimScript = tapscript_1.ConditionMultisigTapscript.encode({
@@ -78,6 +99,63 @@ var VHTLC;
78
99
  }
79
100
  }
80
101
  VHTLC.Script = Script;
102
+ function validateOptions(options) {
103
+ const { sender, receiver, server, preimageHash, refundLocktime, unilateralClaimDelay, unilateralRefundDelay, unilateralRefundWithoutReceiverDelay, } = options;
104
+ if (!preimageHash || preimageHash.length !== 20) {
105
+ throw new Error("preimage hash must be 20 bytes");
106
+ }
107
+ if (!receiver || receiver.length !== 32) {
108
+ throw new Error("Invalid public key length (receiver)");
109
+ }
110
+ if (!sender || sender.length !== 32) {
111
+ throw new Error("Invalid public key length (sender)");
112
+ }
113
+ if (!server || server.length !== 32) {
114
+ throw new Error("Invalid public key length (server)");
115
+ }
116
+ if (typeof refundLocktime !== "bigint" || refundLocktime <= 0n) {
117
+ throw new Error("refund locktime must be greater than 0");
118
+ }
119
+ if (!unilateralClaimDelay ||
120
+ typeof unilateralClaimDelay.value !== "bigint" ||
121
+ unilateralClaimDelay.value <= 0n) {
122
+ throw new Error("unilateral claim delay must greater than 0");
123
+ }
124
+ if (unilateralClaimDelay.type === "seconds" &&
125
+ unilateralClaimDelay.value % 512n !== 0n) {
126
+ throw new Error("seconds timelock must be multiple of 512");
127
+ }
128
+ if (unilateralClaimDelay.type === "seconds" &&
129
+ unilateralClaimDelay.value < 512n) {
130
+ throw new Error("seconds timelock must be greater or equal to 512");
131
+ }
132
+ if (!unilateralRefundDelay ||
133
+ typeof unilateralRefundDelay.value !== "bigint" ||
134
+ unilateralRefundDelay.value <= 0n) {
135
+ throw new Error("unilateral refund delay must greater than 0");
136
+ }
137
+ if (unilateralRefundDelay.type === "seconds" &&
138
+ unilateralRefundDelay.value % 512n !== 0n) {
139
+ throw new Error("seconds timelock must be multiple of 512");
140
+ }
141
+ if (unilateralRefundDelay.type === "seconds" &&
142
+ unilateralRefundDelay.value < 512n) {
143
+ throw new Error("seconds timelock must be greater or equal to 512");
144
+ }
145
+ if (!unilateralRefundWithoutReceiverDelay ||
146
+ typeof unilateralRefundWithoutReceiverDelay.value !== "bigint" ||
147
+ unilateralRefundWithoutReceiverDelay.value <= 0n) {
148
+ throw new Error("unilateral refund without receiver delay must greater than 0");
149
+ }
150
+ if (unilateralRefundWithoutReceiverDelay.type === "seconds" &&
151
+ unilateralRefundWithoutReceiverDelay.value % 512n !== 0n) {
152
+ throw new Error("seconds timelock must be multiple of 512");
153
+ }
154
+ if (unilateralRefundWithoutReceiverDelay.type === "seconds" &&
155
+ unilateralRefundWithoutReceiverDelay.value < 512n) {
156
+ throw new Error("seconds timelock must be greater or equal to 512");
157
+ }
158
+ }
81
159
  })(VHTLC || (exports.VHTLC = VHTLC = {}));
82
160
  function preimageConditionScript(preimageHash) {
83
161
  return btc_signer_1.Script.encode(["HASH160", preimageHash, "EQUAL"]);
@@ -33,22 +33,22 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.TreeSignerSession = exports.ErrMissingAggregateKey = exports.ErrMissingVtxoTree = void 0;
36
+ exports.TreeSignerSession = exports.ErrMissingAggregateKey = exports.ErrMissingVtxoGraph = void 0;
37
37
  exports.validateTreeSigs = validateTreeSigs;
38
38
  const musig2 = __importStar(require("../musig2"));
39
- const vtxoTree_1 = require("./vtxoTree");
40
39
  const btc_signer_1 = require("@scure/btc-signer");
41
40
  const base_1 = require("@scure/base");
42
41
  const secp256k1_1 = require("@noble/curves/secp256k1");
43
42
  const utils_1 = require("@scure/btc-signer/utils");
44
- exports.ErrMissingVtxoTree = new Error("missing vtxo tree");
43
+ const unknownFields_1 = require("../utils/unknownFields");
44
+ exports.ErrMissingVtxoGraph = new Error("missing vtxo graph");
45
45
  exports.ErrMissingAggregateKey = new Error("missing aggregate key");
46
46
  class TreeSignerSession {
47
47
  constructor(secretKey) {
48
48
  this.secretKey = secretKey;
49
49
  this.myNonces = null;
50
50
  this.aggregateNonces = null;
51
- this.tree = null;
51
+ this.graph = null;
52
52
  this.scriptRoot = null;
53
53
  this.rootSharedOutputAmount = null;
54
54
  }
@@ -57,7 +57,7 @@ class TreeSignerSession {
57
57
  return new TreeSignerSession(secretKey);
58
58
  }
59
59
  init(tree, scriptRoot, rootInputAmount) {
60
- this.tree = tree;
60
+ this.graph = tree;
61
61
  this.scriptRoot = scriptRoot;
62
62
  this.rootSharedOutputAmount = rootInputAmount;
63
63
  }
@@ -65,24 +65,16 @@ class TreeSignerSession {
65
65
  return secp256k1_1.secp256k1.getPublicKey(this.secretKey);
66
66
  }
67
67
  getNonces() {
68
- if (!this.tree)
69
- throw exports.ErrMissingVtxoTree;
68
+ if (!this.graph)
69
+ throw exports.ErrMissingVtxoGraph;
70
70
  if (!this.myNonces) {
71
71
  this.myNonces = this.generateNonces();
72
72
  }
73
- const nonces = [];
74
- for (const levelNonces of this.myNonces) {
75
- const levelPubNonces = [];
76
- for (const nonce of levelNonces) {
77
- if (!nonce) {
78
- levelPubNonces.push(null);
79
- continue;
80
- }
81
- levelPubNonces.push({ pubNonce: nonce.pubNonce });
82
- }
83
- nonces.push(levelPubNonces);
73
+ const publicNonces = new Map();
74
+ for (const [txid, nonces] of this.myNonces) {
75
+ publicNonces.set(txid, { pubNonce: nonces.pubNonce });
84
76
  }
85
- return nonces;
77
+ return publicNonces;
86
78
  }
87
79
  setAggregatedNonces(nonces) {
88
80
  if (this.aggregateNonces)
@@ -90,71 +82,55 @@ class TreeSignerSession {
90
82
  this.aggregateNonces = nonces;
91
83
  }
92
84
  sign() {
93
- if (!this.tree)
94
- throw exports.ErrMissingVtxoTree;
85
+ if (!this.graph)
86
+ throw exports.ErrMissingVtxoGraph;
95
87
  if (!this.aggregateNonces)
96
88
  throw new Error("nonces not set");
97
89
  if (!this.myNonces)
98
90
  throw new Error("nonces not generated");
99
- const sigs = [];
100
- for (let levelIndex = 0; levelIndex < this.tree.levels.length; levelIndex++) {
101
- const levelSigs = [];
102
- const level = this.tree.levels[levelIndex];
103
- for (let nodeIndex = 0; nodeIndex < level.length; nodeIndex++) {
104
- const node = level[nodeIndex];
105
- const tx = btc_signer_1.Transaction.fromPSBT(base_1.base64.decode(node.tx));
106
- const sig = this.signPartial(tx, levelIndex, nodeIndex);
107
- if (sig) {
108
- levelSigs.push(sig);
109
- }
110
- else {
111
- levelSigs.push(null);
112
- }
113
- }
114
- sigs.push(levelSigs);
91
+ const sigs = new Map();
92
+ for (const g of this.graph) {
93
+ const sig = this.signPartial(g);
94
+ sigs.set(g.txid, sig);
115
95
  }
116
96
  return sigs;
117
97
  }
118
98
  generateNonces() {
119
- if (!this.tree)
120
- throw exports.ErrMissingVtxoTree;
121
- const myNonces = [];
99
+ if (!this.graph)
100
+ throw exports.ErrMissingVtxoGraph;
101
+ const myNonces = new Map();
122
102
  const publicKey = secp256k1_1.secp256k1.getPublicKey(this.secretKey);
123
- for (const level of this.tree.levels) {
124
- const levelNonces = [];
125
- for (let i = 0; i < level.length; i++) {
126
- const nonces = musig2.generateNonces(publicKey);
127
- levelNonces.push(nonces);
128
- }
129
- myNonces.push(levelNonces);
103
+ for (const g of this.graph) {
104
+ const nonces = musig2.generateNonces(publicKey);
105
+ myNonces.set(g.txid, nonces);
130
106
  }
131
107
  return myNonces;
132
108
  }
133
- signPartial(tx, levelIndex, nodeIndex) {
134
- if (!this.tree || !this.scriptRoot || !this.rootSharedOutputAmount) {
109
+ signPartial(g) {
110
+ if (!this.graph || !this.scriptRoot || !this.rootSharedOutputAmount) {
135
111
  throw TreeSignerSession.NOT_INITIALIZED;
136
112
  }
137
113
  if (!this.myNonces || !this.aggregateNonces) {
138
114
  throw new Error("session not properly initialized");
139
115
  }
140
- const myNonce = this.myNonces[levelIndex][nodeIndex];
116
+ const myNonce = this.myNonces.get(g.txid);
141
117
  if (!myNonce)
142
- return null;
143
- const aggNonce = this.aggregateNonces[levelIndex][nodeIndex];
118
+ throw new Error("missing private nonce");
119
+ const aggNonce = this.aggregateNonces.get(g.txid);
144
120
  if (!aggNonce)
145
121
  throw new Error("missing aggregate nonce");
146
122
  const prevoutAmounts = [];
147
123
  const prevoutScripts = [];
148
- const cosigners = (0, vtxoTree_1.getCosignerKeys)(tx);
124
+ const cosigners = (0, unknownFields_1.getArkPsbtFields)(g.root, 0, unknownFields_1.CosignerPublicKey).map((c) => c.key);
149
125
  const { finalKey } = musig2.aggregateKeys(cosigners, true, {
150
126
  taprootTweak: this.scriptRoot,
151
127
  });
152
- for (let inputIndex = 0; inputIndex < tx.inputsLength; inputIndex++) {
153
- const prevout = getPrevOutput(finalKey, this.tree, this.rootSharedOutputAmount, tx);
128
+ for (let inputIndex = 0; inputIndex < g.root.inputsLength; inputIndex++) {
129
+ const prevout = getPrevOutput(finalKey, this.graph, this.rootSharedOutputAmount, g.root);
154
130
  prevoutAmounts.push(prevout.amount);
155
131
  prevoutScripts.push(prevout.script);
156
132
  }
157
- const message = tx.preimageWitnessV1(0, // always first input
133
+ const message = g.root.preimageWitnessV1(0, // always first input
158
134
  prevoutScripts, btc_signer_1.SigHash.DEFAULT, prevoutAmounts);
159
135
  return musig2.sign(myNonce.secNonce, this.secretKey, aggNonce.pubNonce, cosigners, message, {
160
136
  taprootTweak: this.scriptRoot,
@@ -167,66 +143,47 @@ TreeSignerSession.NOT_INITIALIZED = new Error("session not initialized, call ini
167
143
  // Helper function to validate tree signatures
168
144
  async function validateTreeSigs(finalAggregatedKey, sharedOutputAmount, vtxoTree) {
169
145
  // Iterate through each level of the tree
170
- for (const level of vtxoTree.levels) {
171
- for (const node of level) {
172
- // Parse the transaction
173
- const tx = btc_signer_1.Transaction.fromPSBT(base_1.base64.decode(node.tx));
174
- const input = tx.getInput(0);
175
- // Check if input has signature
176
- if (!input.tapKeySig) {
177
- throw new Error("unsigned tree input");
178
- }
179
- // Get the previous output information
180
- const prevout = getPrevOutput(finalAggregatedKey, vtxoTree, sharedOutputAmount, tx);
181
- // Calculate the message that was signed
182
- const message = tx.preimageWitnessV1(0, // always first input
183
- [prevout.script], btc_signer_1.SigHash.DEFAULT, [prevout.amount]);
184
- // Verify the signature
185
- const isValid = secp256k1_1.schnorr.verify(input.tapKeySig, message, finalAggregatedKey);
186
- if (!isValid) {
187
- throw new Error("invalid signature");
188
- }
146
+ for (const g of vtxoTree) {
147
+ // Parse the transaction
148
+ const input = g.root.getInput(0);
149
+ // Check if input has signature
150
+ if (!input.tapKeySig) {
151
+ throw new Error("unsigned tree input");
152
+ }
153
+ // Get the previous output information
154
+ const prevout = getPrevOutput(finalAggregatedKey, vtxoTree, sharedOutputAmount, g.root);
155
+ // Calculate the message that was signed
156
+ const message = g.root.preimageWitnessV1(0, // always first input
157
+ [prevout.script], btc_signer_1.SigHash.DEFAULT, [prevout.amount]);
158
+ // Verify the signature
159
+ const isValid = secp256k1_1.schnorr.verify(input.tapKeySig, message, finalAggregatedKey);
160
+ if (!isValid) {
161
+ throw new Error("invalid signature");
189
162
  }
190
163
  }
191
164
  }
192
- function getPrevOutput(finalKey, vtxoTree, sharedOutputAmount, partial) {
193
- // Generate P2TR script
165
+ function getPrevOutput(finalKey, graph, sharedOutputAmount, tx) {
166
+ // generate P2TR script from musig2 final key
194
167
  const pkScript = btc_signer_1.Script.encode(["OP_1", finalKey.slice(1)]);
195
- // Get root node
196
- const rootNode = vtxoTree.levels[0][0];
197
- if (!rootNode)
198
- throw new Error("empty vtxo tree");
199
- const input = partial.getInput(0);
200
- if (!input.txid)
201
- throw new Error("missing input txid");
202
- const parentTxID = base_1.hex.encode(input.txid);
203
- // Check if parent is root
204
- if (rootNode.parentTxid === parentTxID) {
168
+ const txid = base_1.hex.encode((0, utils_1.sha256x2)(tx.toBytes(true)).reverse());
169
+ // if the input is the root input, return the shared output amount
170
+ if (txid === graph.txid) {
205
171
  return {
206
172
  amount: sharedOutputAmount,
207
173
  script: pkScript,
208
174
  };
209
175
  }
210
- // Search for parent in tree
211
- let parent = null;
212
- for (const level of vtxoTree.levels) {
213
- for (const node of level) {
214
- if (node.txid === parentTxID) {
215
- parent = node;
216
- break;
217
- }
218
- }
219
- if (parent)
220
- break;
221
- }
222
- if (!parent) {
223
- throw new Error("parent tx not found");
224
- }
225
- // Parse parent tx
226
- const parentTx = btc_signer_1.Transaction.fromPSBT(base_1.base64.decode(parent.tx));
227
- if (!input.index)
176
+ // find the parent transaction
177
+ const parentInput = tx.getInput(0);
178
+ if (!parentInput.txid)
179
+ throw new Error("missing parent input txid");
180
+ const parentTxid = base_1.hex.encode(new Uint8Array(parentInput.txid));
181
+ const parent = graph.find(parentTxid);
182
+ if (!parent)
183
+ throw new Error("parent tx not found");
184
+ if (parentInput.index === undefined)
228
185
  throw new Error("missing input index");
229
- const parentOutput = parentTx.getOutput(input.index);
186
+ const parentOutput = parent.root.getOutput(parentInput.index);
230
187
  if (!parentOutput)
231
188
  throw new Error("parent output not found");
232
189
  if (!parentOutput.amount)