@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.
- package/README.md +157 -174
- package/dist/cjs/arknote/index.js +61 -58
- package/dist/cjs/bip322/errors.js +13 -0
- package/dist/cjs/bip322/index.js +178 -0
- package/dist/cjs/forfeit.js +14 -25
- package/dist/cjs/identity/singleKey.js +68 -0
- package/dist/cjs/index.js +43 -17
- package/dist/cjs/providers/ark.js +261 -321
- package/dist/cjs/providers/indexer.js +525 -0
- package/dist/cjs/providers/onchain.js +193 -15
- package/dist/cjs/script/address.js +48 -17
- package/dist/cjs/script/base.js +120 -3
- package/dist/cjs/script/default.js +18 -4
- package/dist/cjs/script/tapscript.js +61 -20
- package/dist/cjs/script/vhtlc.js +85 -7
- package/dist/cjs/tree/signingSession.js +63 -106
- package/dist/cjs/tree/txTree.js +193 -0
- package/dist/cjs/tree/validation.js +79 -155
- package/dist/cjs/utils/anchor.js +35 -0
- package/dist/cjs/utils/arkTransaction.js +108 -0
- package/dist/cjs/utils/transactionHistory.js +84 -72
- package/dist/cjs/utils/txSizeEstimator.js +12 -0
- package/dist/cjs/utils/unknownFields.js +211 -0
- package/dist/cjs/wallet/index.js +12 -0
- package/dist/cjs/wallet/onchain.js +201 -0
- package/dist/cjs/wallet/ramps.js +95 -0
- package/dist/cjs/wallet/serviceWorker/db/vtxo/idb.js +32 -0
- package/dist/cjs/wallet/serviceWorker/request.js +15 -12
- package/dist/cjs/wallet/serviceWorker/response.js +22 -27
- package/dist/cjs/wallet/serviceWorker/utils.js +8 -0
- package/dist/cjs/wallet/serviceWorker/wallet.js +61 -34
- package/dist/cjs/wallet/serviceWorker/worker.js +120 -108
- package/dist/cjs/wallet/unroll.js +270 -0
- package/dist/cjs/wallet/wallet.js +701 -454
- package/dist/esm/arknote/index.js +61 -57
- package/dist/esm/bip322/errors.js +9 -0
- package/dist/esm/bip322/index.js +174 -0
- package/dist/esm/forfeit.js +15 -26
- package/dist/esm/identity/singleKey.js +64 -0
- package/dist/esm/index.js +31 -12
- package/dist/esm/providers/ark.js +259 -320
- package/dist/esm/providers/indexer.js +521 -0
- package/dist/esm/providers/onchain.js +193 -15
- package/dist/esm/script/address.js +48 -17
- package/dist/esm/script/base.js +120 -3
- package/dist/esm/script/default.js +18 -4
- package/dist/esm/script/tapscript.js +61 -20
- package/dist/esm/script/vhtlc.js +85 -7
- package/dist/esm/tree/signingSession.js +65 -108
- package/dist/esm/tree/txTree.js +189 -0
- package/dist/esm/tree/validation.js +75 -152
- package/dist/esm/utils/anchor.js +31 -0
- package/dist/esm/utils/arkTransaction.js +105 -0
- package/dist/esm/utils/transactionHistory.js +84 -72
- package/dist/esm/utils/txSizeEstimator.js +12 -0
- package/dist/esm/utils/unknownFields.js +173 -0
- package/dist/esm/wallet/index.js +9 -0
- package/dist/esm/wallet/onchain.js +196 -0
- package/dist/esm/wallet/ramps.js +91 -0
- package/dist/esm/wallet/serviceWorker/db/vtxo/idb.js +32 -0
- package/dist/esm/wallet/serviceWorker/request.js +15 -12
- package/dist/esm/wallet/serviceWorker/response.js +22 -27
- package/dist/esm/wallet/serviceWorker/utils.js +8 -0
- package/dist/esm/wallet/serviceWorker/wallet.js +62 -35
- package/dist/esm/wallet/serviceWorker/worker.js +120 -108
- package/dist/esm/wallet/unroll.js +267 -0
- package/dist/esm/wallet/wallet.js +674 -461
- package/dist/types/arknote/index.d.ts +40 -13
- package/dist/types/bip322/errors.d.ts +6 -0
- package/dist/types/bip322/index.d.ts +57 -0
- package/dist/types/forfeit.d.ts +2 -14
- package/dist/types/identity/singleKey.d.ts +27 -0
- package/dist/types/index.d.ts +24 -12
- package/dist/types/providers/ark.d.ts +114 -95
- package/dist/types/providers/indexer.d.ts +186 -0
- package/dist/types/providers/onchain.d.ts +41 -11
- package/dist/types/script/address.d.ts +26 -2
- package/dist/types/script/base.d.ts +13 -3
- package/dist/types/script/default.d.ts +22 -0
- package/dist/types/script/tapscript.d.ts +61 -5
- package/dist/types/script/vhtlc.d.ts +27 -0
- package/dist/types/tree/signingSession.d.ts +5 -5
- package/dist/types/tree/txTree.d.ts +28 -0
- package/dist/types/tree/validation.d.ts +15 -22
- package/dist/types/utils/anchor.d.ts +19 -0
- package/dist/types/utils/arkTransaction.d.ts +27 -0
- package/dist/types/utils/transactionHistory.d.ts +7 -1
- package/dist/types/utils/txSizeEstimator.d.ts +3 -0
- package/dist/types/utils/unknownFields.d.ts +83 -0
- package/dist/types/wallet/index.d.ts +51 -50
- package/dist/types/wallet/onchain.d.ts +49 -0
- package/dist/types/wallet/ramps.d.ts +32 -0
- package/dist/types/wallet/serviceWorker/db/vtxo/idb.d.ts +2 -0
- package/dist/types/wallet/serviceWorker/db/vtxo/index.d.ts +2 -0
- package/dist/types/wallet/serviceWorker/request.d.ts +14 -16
- package/dist/types/wallet/serviceWorker/response.d.ts +17 -19
- package/dist/types/wallet/serviceWorker/utils.d.ts +8 -0
- package/dist/types/wallet/serviceWorker/wallet.d.ts +36 -8
- package/dist/types/wallet/serviceWorker/worker.d.ts +7 -3
- package/dist/types/wallet/unroll.d.ts +102 -0
- package/dist/types/wallet/wallet.d.ts +71 -25
- package/package.json +37 -35
- package/dist/cjs/identity/inMemoryKey.js +0 -40
- package/dist/cjs/tree/vtxoTree.js +0 -231
- package/dist/cjs/utils/coinselect.js +0 -73
- package/dist/cjs/utils/psbt.js +0 -137
- package/dist/esm/identity/inMemoryKey.js +0 -36
- package/dist/esm/tree/vtxoTree.js +0 -191
- package/dist/esm/utils/coinselect.js +0 -69
- package/dist/esm/utils/psbt.js +0 -131
- package/dist/types/identity/inMemoryKey.d.ts +0 -12
- package/dist/types/tree/vtxoTree.d.ts +0 -33
- package/dist/types/utils/coinselect.d.ts +0 -21
- 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
|
|
70
|
-
*
|
|
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 =
|
|
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 = [
|
|
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(
|
|
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 =
|
|
468
|
-
const asm = [
|
|
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 =
|
|
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;
|
package/dist/cjs/script/vhtlc.js
CHANGED
|
@@ -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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
69
|
-
throw exports.
|
|
68
|
+
if (!this.graph)
|
|
69
|
+
throw exports.ErrMissingVtxoGraph;
|
|
70
70
|
if (!this.myNonces) {
|
|
71
71
|
this.myNonces = this.generateNonces();
|
|
72
72
|
}
|
|
73
|
-
const
|
|
74
|
-
for (const
|
|
75
|
-
|
|
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
|
|
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.
|
|
94
|
-
throw exports.
|
|
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 (
|
|
101
|
-
const
|
|
102
|
-
|
|
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.
|
|
120
|
-
throw exports.
|
|
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
|
|
124
|
-
const
|
|
125
|
-
|
|
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(
|
|
134
|
-
if (!this.
|
|
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
|
|
116
|
+
const myNonce = this.myNonces.get(g.txid);
|
|
141
117
|
if (!myNonce)
|
|
142
|
-
|
|
143
|
-
const aggNonce = this.aggregateNonces
|
|
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,
|
|
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 <
|
|
153
|
-
const prevout = getPrevOutput(finalKey, this.
|
|
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 =
|
|
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
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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,
|
|
193
|
-
//
|
|
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
|
-
|
|
196
|
-
|
|
197
|
-
if (
|
|
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
|
-
//
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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 =
|
|
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)
|