@arkade-os/sdk 0.4.59 → 0.4.60
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 +34 -6
- package/dist/adapters/expo.cjs +5 -5
- package/dist/adapters/expo.d.cts +2 -2
- package/dist/adapters/expo.d.ts +2 -2
- package/dist/adapters/expo.js +3 -3
- package/dist/adapters/indexedDB.cjs +5 -5
- package/dist/adapters/indexedDB.js +4 -4
- package/dist/{ark-C9DsPSm8.d.cts → ark-D2_yNhfz.d.cts} +1009 -523
- package/dist/{ark-C9DsPSm8.d.ts → ark-D2_yNhfz.d.ts} +1009 -523
- package/dist/{asyncStorageTaskQueue-CYHXFfvn.d.cts → asyncStorageTaskQueue-CqijAAPG.d.cts} +1 -1
- package/dist/{asyncStorageTaskQueue-BwuWQ1Qc.d.ts → asyncStorageTaskQueue-Vsp6uk8e.d.ts} +1 -1
- package/dist/{chunk-PN5RD7ZI.js → chunk-2DINXHGQ.js} +1665 -1149
- package/dist/chunk-2DINXHGQ.js.map +1 -0
- package/dist/{chunk-IPFGYVF3.cjs → chunk-2XLNQHG4.cjs} +10 -10
- package/dist/chunk-2XLNQHG4.cjs.map +1 -0
- package/dist/{chunk-VSHY5TBY.js → chunk-3Q7BBUWO.js} +4 -4
- package/dist/{chunk-VSHY5TBY.js.map → chunk-3Q7BBUWO.js.map} +1 -1
- package/dist/{chunk-DSTYXT2D.cjs → chunk-4A4GRSTB.cjs} +44 -6
- package/dist/chunk-4A4GRSTB.cjs.map +1 -0
- package/dist/{chunk-H342JPPZ.js → chunk-B66RYHTB.js} +5 -5
- package/dist/chunk-B66RYHTB.js.map +1 -0
- package/dist/{chunk-YEHGCHIL.js → chunk-FSKANUV4.js} +12 -5
- package/dist/chunk-FSKANUV4.js.map +1 -0
- package/dist/{chunk-7FA36NOA.cjs → chunk-H7JCHFM6.cjs} +17 -17
- package/dist/{chunk-7FA36NOA.cjs.map → chunk-H7JCHFM6.cjs.map} +1 -1
- package/dist/{chunk-FIMOVH25.js → chunk-JBULXTCC.js} +40 -7
- package/dist/chunk-JBULXTCC.js.map +1 -0
- package/dist/{chunk-DYNB6POO.cjs → chunk-KNS5MTTI.cjs} +3820 -2915
- package/dist/chunk-KNS5MTTI.cjs.map +1 -0
- package/dist/{chunk-2XO43UHI.cjs → chunk-MZNLCMUP.cjs} +1686 -1169
- package/dist/chunk-MZNLCMUP.cjs.map +1 -0
- package/dist/{chunk-V2KENVN6.cjs → chunk-PRFL62ID.cjs} +22 -15
- package/dist/chunk-PRFL62ID.cjs.map +1 -0
- package/dist/{chunk-OVSNY3HC.js → chunk-Y54MUBT7.js} +3476 -2584
- package/dist/chunk-Y54MUBT7.js.map +1 -0
- package/dist/contracts/handlers/index.cjs +12 -8
- package/dist/contracts/handlers/index.d.cts +3 -3
- package/dist/contracts/handlers/index.d.ts +3 -3
- package/dist/contracts/handlers/index.js +2 -2
- package/dist/{delegate-D0eGaz3J.d.cts → delegate-DNEuM_nK.d.cts} +1 -1
- package/dist/{delegate-B_oKWm1k.d.ts → delegate-DidNTsKm.d.ts} +1 -1
- package/dist/{index-BmK6tEqf.d.ts → index-BP8-zniX.d.cts} +182 -26
- package/dist/{index-BG2AL2Ie.d.cts → index-DzQbNQUG.d.ts} +182 -26
- package/dist/index.cjs +302 -230
- package/dist/index.d.cts +286 -48
- package/dist/index.d.ts +286 -48
- package/dist/index.js +4 -4
- package/dist/repositories/realm/index.cjs +43 -23
- package/dist/repositories/realm/index.cjs.map +1 -1
- package/dist/repositories/realm/index.d.cts +21 -9
- package/dist/repositories/realm/index.d.ts +21 -9
- package/dist/repositories/realm/index.js +29 -9
- package/dist/repositories/realm/index.js.map +1 -1
- package/dist/repositories/sqlite/index.cjs +48 -23
- package/dist/repositories/sqlite/index.cjs.map +1 -1
- package/dist/repositories/sqlite/index.d.cts +9 -3
- package/dist/repositories/sqlite/index.d.ts +9 -3
- package/dist/repositories/sqlite/index.js +35 -10
- package/dist/repositories/sqlite/index.js.map +1 -1
- package/dist/{taskRunner-Cy4Gh8n7.d.ts → taskRunner-9gnlYEpb.d.ts} +1 -1
- package/dist/{taskRunner-CBvKIttE.d.cts → taskRunner-BEQNFEa4.d.cts} +1 -1
- package/dist/wallet/expo/background.cjs +14 -14
- package/dist/wallet/expo/background.d.cts +3 -3
- package/dist/wallet/expo/background.d.ts +3 -3
- package/dist/wallet/expo/background.js +6 -6
- package/dist/wallet/expo/index.cjs +17 -14
- package/dist/wallet/expo/index.cjs.map +1 -1
- package/dist/wallet/expo/index.d.cts +5 -4
- package/dist/wallet/expo/index.d.ts +5 -4
- package/dist/wallet/expo/index.js +8 -5
- package/dist/wallet/expo/index.js.map +1 -1
- package/dist/{wallet-EyVDhqzP.d.ts → wallet-C_MYHyOj.d.ts} +155 -17
- package/dist/{wallet-DlXemAzP.d.cts → wallet-ipUA1XCp.d.cts} +155 -17
- package/dist/worker/expo/index.cjs +9 -9
- package/dist/worker/expo/index.d.cts +5 -5
- package/dist/worker/expo/index.d.ts +5 -5
- package/dist/worker/expo/index.js +5 -5
- package/package.json +4 -2
- package/dist/chunk-2XO43UHI.cjs.map +0 -1
- package/dist/chunk-DSTYXT2D.cjs.map +0 -1
- package/dist/chunk-DYNB6POO.cjs.map +0 -1
- package/dist/chunk-FIMOVH25.js.map +0 -1
- package/dist/chunk-H342JPPZ.js.map +0 -1
- package/dist/chunk-IPFGYVF3.cjs.map +0 -1
- package/dist/chunk-OVSNY3HC.js.map +0 -1
- package/dist/chunk-PN5RD7ZI.js.map +0 -1
- package/dist/chunk-V2KENVN6.cjs.map +0 -1
- package/dist/chunk-YEHGCHIL.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunk4A4GRSTB_cjs = require('./chunk-4A4GRSTB.cjs');
|
|
4
4
|
var chunk5BLDMQED_cjs = require('./chunk-5BLDMQED.cjs');
|
|
5
5
|
var base = require('@scure/base');
|
|
6
6
|
var descriptorsScure = require('@bitcoinerlab/descriptors-scure');
|
|
@@ -76,14 +76,14 @@ var ContractHandlerRegistry = class {
|
|
|
76
76
|
var contractHandlers = new ContractHandlerRegistry();
|
|
77
77
|
exports.DefaultVtxo = void 0;
|
|
78
78
|
((DefaultVtxo2) => {
|
|
79
|
-
class Script3 extends
|
|
79
|
+
class Script3 extends chunk4A4GRSTB_cjs.VtxoScript {
|
|
80
80
|
/** Create the default virtual output script with one forfeit path and one exit path. */
|
|
81
81
|
constructor(options) {
|
|
82
82
|
const { pubKey, serverPubKey, csvTimelock } = options;
|
|
83
|
-
const forfeitScript =
|
|
83
|
+
const forfeitScript = chunk4A4GRSTB_cjs.MultisigTapscript.encode({
|
|
84
84
|
pubkeys: [pubKey, serverPubKey]
|
|
85
85
|
}).script;
|
|
86
|
-
const exitScript =
|
|
86
|
+
const exitScript = chunk4A4GRSTB_cjs.CSVMultisigTapscript.encode({
|
|
87
87
|
timelock: csvTimelock,
|
|
88
88
|
pubkeys: [pubKey]
|
|
89
89
|
}).script;
|
|
@@ -263,21 +263,49 @@ function extractPubKeyBytes(value) {
|
|
|
263
263
|
return base.hex.decode(extractPubKey(normalizeToDescriptor(value)));
|
|
264
264
|
}
|
|
265
265
|
function deserializeCsvTimelock(value) {
|
|
266
|
-
return value !== void 0 && value !== "" ?
|
|
266
|
+
return value !== void 0 && value !== "" ? chunk4A4GRSTB_cjs.sequenceToTimelock(Number(value)) : exports.DefaultVtxo.Script.DEFAULT_TIMELOCK;
|
|
267
267
|
}
|
|
268
268
|
var CLTV_HEIGHT_THRESHOLD = 500000000n;
|
|
269
269
|
function isCltvSatisfied(context, locktime) {
|
|
270
|
+
return cltvMaturity(context, locktime) === "satisfied";
|
|
271
|
+
}
|
|
272
|
+
function cltvMaturity(context, locktime) {
|
|
270
273
|
if (locktime < CLTV_HEIGHT_THRESHOLD) {
|
|
271
|
-
if (context.blockHeight === void 0) return
|
|
272
|
-
return BigInt(context.blockHeight) >= locktime;
|
|
274
|
+
if (context.blockHeight === void 0) return "unknown";
|
|
275
|
+
return BigInt(context.blockHeight) >= locktime ? "satisfied" : "pending";
|
|
276
|
+
}
|
|
277
|
+
return nowSeconds(context) >= locktime ? "satisfied" : "pending";
|
|
278
|
+
}
|
|
279
|
+
function nowSeconds(context) {
|
|
280
|
+
return BigInt(Math.floor(context.chainTime ?? context.currentTime / 1e3));
|
|
281
|
+
}
|
|
282
|
+
function isHeightLocktime(locktime) {
|
|
283
|
+
return locktime < CLTV_HEIGHT_THRESHOLD;
|
|
284
|
+
}
|
|
285
|
+
function assertVhtlcSpendableNow(contract, context) {
|
|
286
|
+
if (!context.collaborative) return;
|
|
287
|
+
if (resolveRole(contract, context) !== "sender") return;
|
|
288
|
+
const raw = contract.params?.refundLocktime;
|
|
289
|
+
if (raw === void 0) return;
|
|
290
|
+
let refundLocktime;
|
|
291
|
+
try {
|
|
292
|
+
refundLocktime = BigInt(raw);
|
|
293
|
+
} catch {
|
|
294
|
+
return;
|
|
273
295
|
}
|
|
274
|
-
|
|
275
|
-
|
|
296
|
+
if (cltvMaturity(context, refundLocktime) !== "pending") return;
|
|
297
|
+
const [matures, now] = isHeightLocktime(refundLocktime) ? [`block ${refundLocktime}`, `${context.blockHeight}`] : [
|
|
298
|
+
`${new Date(Number(refundLocktime) * 1e3).toISOString()}`,
|
|
299
|
+
new Date(context.currentTime).toISOString()
|
|
300
|
+
];
|
|
301
|
+
throw new Error(
|
|
302
|
+
`vhtlc ${contract.script} cannot be spent yet: its refund path opens at ${matures}, now ${now}. Until then the lockup is the receiver's to claim, and the server rejects a spend of it. Retry after ${matures}, or wait for the receiver to claim it with the preimage.`
|
|
303
|
+
);
|
|
276
304
|
}
|
|
277
305
|
function isCsvSpendable(context, sequence) {
|
|
278
306
|
if (sequence === void 0) return true;
|
|
279
307
|
if (!context.vtxo) return false;
|
|
280
|
-
const timelock =
|
|
308
|
+
const timelock = chunk4A4GRSTB_cjs.sequenceToTimelock(sequence);
|
|
281
309
|
if (timelock.type === "blocks") {
|
|
282
310
|
if (context.blockHeight === void 0 || context.vtxo.status.block_height === void 0) {
|
|
283
311
|
return false;
|
|
@@ -287,7 +315,7 @@ function isCsvSpendable(context, sequence) {
|
|
|
287
315
|
if (timelock.type === "seconds") {
|
|
288
316
|
const blockTime = context.vtxo.status.block_time;
|
|
289
317
|
if (blockTime === void 0) return false;
|
|
290
|
-
return context
|
|
318
|
+
return Number(nowSeconds(context)) - blockTime >= Number(timelock.value);
|
|
291
319
|
}
|
|
292
320
|
return false;
|
|
293
321
|
}
|
|
@@ -335,21 +363,21 @@ function forfeitExitSpendablePaths(script, contract, context) {
|
|
|
335
363
|
async function detectUsedScripts(indexerProvider, scriptHexes) {
|
|
336
364
|
const used = /* @__PURE__ */ new Set();
|
|
337
365
|
const unique = [...new Set(scriptHexes)];
|
|
338
|
-
for (let offset = 0; offset < unique.length; offset +=
|
|
339
|
-
const scripts = unique.slice(offset, offset +
|
|
366
|
+
for (let offset = 0; offset < unique.length; offset += chunk4A4GRSTB_cjs.SCRIPT_QUERY_CHUNK_SIZE) {
|
|
367
|
+
const scripts = unique.slice(offset, offset + chunk4A4GRSTB_cjs.SCRIPT_QUERY_CHUNK_SIZE);
|
|
340
368
|
const remaining = new Set(scripts);
|
|
341
369
|
let pageIndex = 0;
|
|
342
370
|
let hasMore = true;
|
|
343
371
|
while (hasMore && remaining.size > 0) {
|
|
344
|
-
const { vtxos, page } = await
|
|
372
|
+
const { vtxos, page } = await chunk4A4GRSTB_cjs.getNormalizedVtxos(indexerProvider, {
|
|
345
373
|
scripts,
|
|
346
374
|
pageIndex,
|
|
347
|
-
pageSize:
|
|
375
|
+
pageSize: chunk4A4GRSTB_cjs.DEFAULT_PAGE_SIZE
|
|
348
376
|
});
|
|
349
377
|
for (const vtxo of vtxos) {
|
|
350
378
|
if (remaining.delete(vtxo.script)) used.add(vtxo.script);
|
|
351
379
|
}
|
|
352
|
-
hasMore = page ? vtxos.length ===
|
|
380
|
+
hasMore = page ? vtxos.length === chunk4A4GRSTB_cjs.DEFAULT_PAGE_SIZE : false;
|
|
353
381
|
pageIndex++;
|
|
354
382
|
}
|
|
355
383
|
}
|
|
@@ -410,7 +438,7 @@ var DefaultContractHandler = {
|
|
|
410
438
|
return {
|
|
411
439
|
pubKey: base.hex.encode(params.pubKey),
|
|
412
440
|
serverPubKey: base.hex.encode(params.serverPubKey),
|
|
413
|
-
csvTimelock:
|
|
441
|
+
csvTimelock: chunk4A4GRSTB_cjs.timelockToSequence(params.csvTimelock).toString()
|
|
414
442
|
};
|
|
415
443
|
},
|
|
416
444
|
deserializeParams(params) {
|
|
@@ -429,6 +457,8 @@ var DefaultContractHandler = {
|
|
|
429
457
|
getSpendablePaths(script, contract, context) {
|
|
430
458
|
return forfeitExitSpendablePaths(script, contract, context);
|
|
431
459
|
},
|
|
460
|
+
/** The wallet's own money. */
|
|
461
|
+
isGenericallySpendable: () => true,
|
|
432
462
|
discoverAt: discoverAtViaRange(discoverDefaultRange),
|
|
433
463
|
discoverRange: discoverDefaultRange,
|
|
434
464
|
candidatesAt: defaultCandidatesAt
|
|
@@ -443,7 +473,7 @@ function defaultCandidatesAt(_index, descriptor, deps) {
|
|
|
443
473
|
params: {
|
|
444
474
|
pubKey: base.hex.encode(c.pubKey),
|
|
445
475
|
serverPubKey: base.hex.encode(c.serverPubKey),
|
|
446
|
-
csvTimelock:
|
|
476
|
+
csvTimelock: chunk4A4GRSTB_cjs.timelockToSequence(c.csvTimelock).toString()
|
|
447
477
|
},
|
|
448
478
|
script: c.scriptHex,
|
|
449
479
|
address: c.script.address(deps.network.hrp, c.serverPubKey).encode()
|
|
@@ -459,12 +489,12 @@ function discoverDefaultRange(entries, deps) {
|
|
|
459
489
|
}
|
|
460
490
|
exports.DelegateVtxo = void 0;
|
|
461
491
|
((DelegateVtxo2) => {
|
|
462
|
-
class Script3 extends
|
|
492
|
+
class Script3 extends chunk4A4GRSTB_cjs.VtxoScript {
|
|
463
493
|
/** Create a delegated virtual output script with forfeit, exit, and delegate paths. */
|
|
464
494
|
constructor(options) {
|
|
465
495
|
const defaultVtxo = new exports.DefaultVtxo.Script(options);
|
|
466
496
|
const { delegatePubKey, pubKey, serverPubKey } = options;
|
|
467
|
-
const delegateScript =
|
|
497
|
+
const delegateScript = chunk4A4GRSTB_cjs.MultisigTapscript.encode({
|
|
468
498
|
pubkeys: [pubKey, delegatePubKey, serverPubKey]
|
|
469
499
|
}).script;
|
|
470
500
|
super([...defaultVtxo.scripts, delegateScript]);
|
|
@@ -503,7 +533,7 @@ var DelegateContractHandler = {
|
|
|
503
533
|
pubKey: base.hex.encode(params.pubKey),
|
|
504
534
|
serverPubKey: base.hex.encode(params.serverPubKey),
|
|
505
535
|
delegatePubKey: base.hex.encode(params.delegatePubKey),
|
|
506
|
-
csvTimelock:
|
|
536
|
+
csvTimelock: chunk4A4GRSTB_cjs.timelockToSequence(params.csvTimelock).toString()
|
|
507
537
|
};
|
|
508
538
|
},
|
|
509
539
|
deserializeParams(params) {
|
|
@@ -528,6 +558,8 @@ var DelegateContractHandler = {
|
|
|
528
558
|
getSpendablePaths(script, contract, context) {
|
|
529
559
|
return forfeitExitSpendablePaths(script, contract, context);
|
|
530
560
|
},
|
|
561
|
+
/** The wallet's own money, delegated signing. */
|
|
562
|
+
isGenericallySpendable: () => true,
|
|
531
563
|
discoverAt: discoverAtViaRange(discoverDelegateRange),
|
|
532
564
|
discoverRange: discoverDelegateRange,
|
|
533
565
|
candidatesAt: delegateCandidatesAt
|
|
@@ -545,7 +577,7 @@ function delegateCandidatesAt(_index, descriptor, deps) {
|
|
|
545
577
|
pubKey: base.hex.encode(c.pubKey),
|
|
546
578
|
serverPubKey: base.hex.encode(c.serverPubKey),
|
|
547
579
|
delegatePubKey: base.hex.encode(delegatePubKey),
|
|
548
|
-
csvTimelock:
|
|
580
|
+
csvTimelock: chunk4A4GRSTB_cjs.timelockToSequence(c.csvTimelock).toString()
|
|
549
581
|
},
|
|
550
582
|
script: c.scriptHex,
|
|
551
583
|
address: c.script.address(deps.network.hrp, c.serverPubKey).encode()
|
|
@@ -562,1225 +594,1696 @@ function discoverDelegateRange(entries, deps) {
|
|
|
562
594
|
(c, index, descriptor) => ({ ...c, ...rotatedReceiveMetadata(index, descriptor) })
|
|
563
595
|
);
|
|
564
596
|
}
|
|
565
|
-
|
|
566
|
-
(
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
)
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
/** Return the collaborative claim tapleaf script. */
|
|
632
|
-
claim() {
|
|
633
|
-
return this.findLeaf(this.claimScript);
|
|
634
|
-
}
|
|
635
|
-
/** Return the collaborative refund tapleaf script. */
|
|
636
|
-
refund() {
|
|
637
|
-
return this.findLeaf(this.refundScript);
|
|
638
|
-
}
|
|
639
|
-
/** Return the refund-without-receiver tapleaf script. */
|
|
640
|
-
refundWithoutReceiver() {
|
|
641
|
-
return this.findLeaf(this.refundWithoutReceiverScript);
|
|
597
|
+
function equalBytes(a, b) {
|
|
598
|
+
if (a.length !== b.length)
|
|
599
|
+
return false;
|
|
600
|
+
for (let i = 0; i < a.length; i++)
|
|
601
|
+
if (a[i] !== b[i])
|
|
602
|
+
return false;
|
|
603
|
+
return true;
|
|
604
|
+
}
|
|
605
|
+
function isBytes(a) {
|
|
606
|
+
return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
|
|
607
|
+
}
|
|
608
|
+
var createView = (arr) => new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
609
|
+
function isPlainObject(obj) {
|
|
610
|
+
return Object.prototype.toString.call(obj) === "[object Object]";
|
|
611
|
+
}
|
|
612
|
+
function isNum(num) {
|
|
613
|
+
return Number.isSafeInteger(num);
|
|
614
|
+
}
|
|
615
|
+
var Bitset = {
|
|
616
|
+
BITS: 32,
|
|
617
|
+
FULL_MASK: -1 >>> 0,
|
|
618
|
+
// 1<<32 will overflow
|
|
619
|
+
len: (len) => Math.ceil(len / 32),
|
|
620
|
+
create: (len) => new Uint32Array(Bitset.len(len)),
|
|
621
|
+
clean: (bs) => bs.fill(0),
|
|
622
|
+
debug: (bs) => Array.from(bs).map((i) => (i >>> 0).toString(2).padStart(32, "0")),
|
|
623
|
+
checkLen: (bs, len) => {
|
|
624
|
+
if (Bitset.len(len) === bs.length)
|
|
625
|
+
return;
|
|
626
|
+
throw new Error(`wrong length=${bs.length}. Expected: ${Bitset.len(len)}`);
|
|
627
|
+
},
|
|
628
|
+
chunkLen: (bsLen, pos, len) => {
|
|
629
|
+
if (pos < 0)
|
|
630
|
+
throw new Error(`wrong pos=${pos}`);
|
|
631
|
+
if (pos + len > bsLen)
|
|
632
|
+
throw new Error(`wrong range=${pos}/${len} of ${bsLen}`);
|
|
633
|
+
},
|
|
634
|
+
set: (bs, chunk, value, allowRewrite = true) => {
|
|
635
|
+
if (!allowRewrite && (bs[chunk] & value) !== 0)
|
|
636
|
+
return false;
|
|
637
|
+
bs[chunk] |= value;
|
|
638
|
+
return true;
|
|
639
|
+
},
|
|
640
|
+
pos: (pos, i) => ({
|
|
641
|
+
chunk: Math.floor((pos + i) / 32),
|
|
642
|
+
mask: 1 << 32 - (pos + i) % 32 - 1
|
|
643
|
+
}),
|
|
644
|
+
indices: (bs, len, invert = false) => {
|
|
645
|
+
Bitset.checkLen(bs, len);
|
|
646
|
+
const { FULL_MASK, BITS } = Bitset;
|
|
647
|
+
const left = BITS - len % BITS;
|
|
648
|
+
const lastMask = left ? FULL_MASK >>> left << left : FULL_MASK;
|
|
649
|
+
const res = [];
|
|
650
|
+
for (let i = 0; i < bs.length; i++) {
|
|
651
|
+
let c = bs[i];
|
|
652
|
+
if (invert)
|
|
653
|
+
c = ~c;
|
|
654
|
+
if (i === bs.length - 1)
|
|
655
|
+
c &= lastMask;
|
|
656
|
+
if (c === 0)
|
|
657
|
+
continue;
|
|
658
|
+
for (let j = 0; j < BITS; j++) {
|
|
659
|
+
const m = 1 << BITS - j - 1;
|
|
660
|
+
if (c & m)
|
|
661
|
+
res.push(i * BITS + j);
|
|
662
|
+
}
|
|
642
663
|
}
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
664
|
+
return res;
|
|
665
|
+
},
|
|
666
|
+
range: (arr) => {
|
|
667
|
+
const res = [];
|
|
668
|
+
let cur;
|
|
669
|
+
for (const i of arr) {
|
|
670
|
+
if (cur === void 0 || i !== cur.pos + cur.length)
|
|
671
|
+
res.push(cur = { pos: i, length: 1 });
|
|
672
|
+
else
|
|
673
|
+
cur.length += 1;
|
|
646
674
|
}
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
675
|
+
return res;
|
|
676
|
+
},
|
|
677
|
+
rangeDebug: (bs, len, invert = false) => `[${Bitset.range(Bitset.indices(bs, len, invert)).map((i) => `(${i.pos}/${i.length})`).join(", ")}]`,
|
|
678
|
+
setRange: (bs, bsLen, pos, len, allowRewrite = true) => {
|
|
679
|
+
Bitset.chunkLen(bsLen, pos, len);
|
|
680
|
+
const { FULL_MASK, BITS } = Bitset;
|
|
681
|
+
const first = pos % BITS ? Math.floor(pos / BITS) : void 0;
|
|
682
|
+
const lastPos = pos + len;
|
|
683
|
+
const last = lastPos % BITS ? Math.floor(lastPos / BITS) : void 0;
|
|
684
|
+
if (first !== void 0 && first === last)
|
|
685
|
+
return Bitset.set(bs, first, FULL_MASK >>> BITS - len << BITS - len - pos, allowRewrite);
|
|
686
|
+
if (first !== void 0) {
|
|
687
|
+
if (!Bitset.set(bs, first, FULL_MASK >>> pos % BITS, allowRewrite))
|
|
688
|
+
return false;
|
|
650
689
|
}
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
690
|
+
const start = first !== void 0 ? first + 1 : pos / BITS;
|
|
691
|
+
const end = last !== void 0 ? last : lastPos / BITS;
|
|
692
|
+
for (let i = start; i < end; i++)
|
|
693
|
+
if (!Bitset.set(bs, i, FULL_MASK, allowRewrite))
|
|
694
|
+
return false;
|
|
695
|
+
if (last !== void 0 && first !== last) {
|
|
696
|
+
if (!Bitset.set(bs, last, FULL_MASK << BITS - lastPos % BITS, allowRewrite))
|
|
697
|
+
return false;
|
|
654
698
|
}
|
|
699
|
+
return true;
|
|
655
700
|
}
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
701
|
+
};
|
|
702
|
+
var Path = {
|
|
703
|
+
/**
|
|
704
|
+
* Internal method for handling stack of paths (debug, errors, dynamic fields via path)
|
|
705
|
+
* This is looks ugly (callback), but allows us to force stack cleaning by construction (.pop always after function).
|
|
706
|
+
* Also, this makes impossible:
|
|
707
|
+
* - pushing field when stack is empty
|
|
708
|
+
* - pushing field inside of field (real bug)
|
|
709
|
+
* NOTE: we don't want to do '.pop' on error!
|
|
710
|
+
*/
|
|
711
|
+
pushObj: (stack, obj, objFn) => {
|
|
712
|
+
const last = { obj };
|
|
713
|
+
stack.push(last);
|
|
714
|
+
objFn((field, fieldFn) => {
|
|
715
|
+
last.field = field;
|
|
716
|
+
fieldFn();
|
|
717
|
+
last.field = void 0;
|
|
718
|
+
});
|
|
719
|
+
stack.pop();
|
|
720
|
+
},
|
|
721
|
+
path: (stack) => {
|
|
722
|
+
const res = [];
|
|
723
|
+
for (const i of stack)
|
|
724
|
+
if (i.field !== void 0)
|
|
725
|
+
res.push(i.field);
|
|
726
|
+
return res.join("/");
|
|
727
|
+
},
|
|
728
|
+
err: (name, stack, msg) => {
|
|
729
|
+
const err = new Error(`${name}(${Path.path(stack)}): ${typeof msg === "string" ? msg : msg.message}`);
|
|
730
|
+
if (msg instanceof Error && msg.stack)
|
|
731
|
+
err.stack = msg.stack;
|
|
732
|
+
return err;
|
|
733
|
+
},
|
|
734
|
+
resolve: (stack, path) => {
|
|
735
|
+
const parts = path.split("/");
|
|
736
|
+
const objPath = stack.map((i2) => i2.obj);
|
|
737
|
+
let i = 0;
|
|
738
|
+
for (; i < parts.length; i++) {
|
|
739
|
+
if (parts[i] === "..")
|
|
740
|
+
objPath.pop();
|
|
741
|
+
else
|
|
742
|
+
break;
|
|
679
743
|
}
|
|
680
|
-
|
|
681
|
-
|
|
744
|
+
let cur = objPath.pop();
|
|
745
|
+
for (; i < parts.length; i++) {
|
|
746
|
+
if (!cur || cur[parts[i]] === void 0)
|
|
747
|
+
return void 0;
|
|
748
|
+
cur = cur[parts[i]];
|
|
682
749
|
}
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
750
|
+
return cur;
|
|
751
|
+
}
|
|
752
|
+
};
|
|
753
|
+
var _Reader = class __Reader {
|
|
754
|
+
pos = 0;
|
|
755
|
+
data;
|
|
756
|
+
opts;
|
|
757
|
+
stack;
|
|
758
|
+
parent;
|
|
759
|
+
parentOffset;
|
|
760
|
+
bitBuf = 0;
|
|
761
|
+
bitPos = 0;
|
|
762
|
+
bs;
|
|
763
|
+
// bitset
|
|
764
|
+
view;
|
|
765
|
+
constructor(data, opts = {}, stack = [], parent = void 0, parentOffset = 0) {
|
|
766
|
+
this.data = data;
|
|
767
|
+
this.opts = opts;
|
|
768
|
+
this.stack = stack;
|
|
769
|
+
this.parent = parent;
|
|
770
|
+
this.parentOffset = parentOffset;
|
|
771
|
+
this.view = createView(data);
|
|
772
|
+
}
|
|
773
|
+
/** Internal method for pointers. */
|
|
774
|
+
_enablePointers() {
|
|
775
|
+
if (this.parent)
|
|
776
|
+
return this.parent._enablePointers();
|
|
777
|
+
if (this.bs)
|
|
778
|
+
return;
|
|
779
|
+
this.bs = Bitset.create(this.data.length);
|
|
780
|
+
Bitset.setRange(this.bs, this.data.length, 0, this.pos, this.opts.allowMultipleReads);
|
|
781
|
+
}
|
|
782
|
+
markBytesBS(pos, len) {
|
|
783
|
+
if (this.parent)
|
|
784
|
+
return this.parent.markBytesBS(this.parentOffset + pos, len);
|
|
785
|
+
if (!len)
|
|
786
|
+
return true;
|
|
787
|
+
if (!this.bs)
|
|
788
|
+
return true;
|
|
789
|
+
return Bitset.setRange(this.bs, this.data.length, pos, len, false);
|
|
790
|
+
}
|
|
791
|
+
markBytes(len) {
|
|
792
|
+
const pos = this.pos;
|
|
793
|
+
this.pos += len;
|
|
794
|
+
const res = this.markBytesBS(pos, len);
|
|
795
|
+
if (!this.opts.allowMultipleReads && !res)
|
|
796
|
+
throw this.err(`multiple read pos=${this.pos} len=${len}`);
|
|
797
|
+
return res;
|
|
798
|
+
}
|
|
799
|
+
pushObj(obj, objFn) {
|
|
800
|
+
return Path.pushObj(this.stack, obj, objFn);
|
|
801
|
+
}
|
|
802
|
+
readView(n, fn) {
|
|
803
|
+
if (!Number.isFinite(n))
|
|
804
|
+
throw this.err(`readView: wrong length=${n}`);
|
|
805
|
+
if (this.pos + n > this.data.length)
|
|
806
|
+
throw this.err("readView: Unexpected end of buffer");
|
|
807
|
+
const res = fn(this.view, this.pos);
|
|
808
|
+
this.markBytes(n);
|
|
809
|
+
return res;
|
|
810
|
+
}
|
|
811
|
+
// read bytes by absolute offset
|
|
812
|
+
absBytes(n) {
|
|
813
|
+
if (n > this.data.length)
|
|
814
|
+
throw new Error("Unexpected end of buffer");
|
|
815
|
+
return this.data.subarray(n);
|
|
816
|
+
}
|
|
817
|
+
finish() {
|
|
818
|
+
if (this.opts.allowUnreadBytes)
|
|
819
|
+
return;
|
|
820
|
+
if (this.bitPos) {
|
|
821
|
+
throw this.err(`${this.bitPos} bits left after unpack: ${base.hex.encode(this.data.slice(this.pos))}`);
|
|
688
822
|
}
|
|
689
|
-
if (
|
|
690
|
-
|
|
823
|
+
if (this.bs && !this.parent) {
|
|
824
|
+
const notRead = Bitset.indices(this.bs, this.data.length, true);
|
|
825
|
+
if (notRead.length) {
|
|
826
|
+
const formatted = Bitset.range(notRead).map(({ pos, length }) => `(${pos}/${length})[${base.hex.encode(this.data.subarray(pos, pos + length))}]`).join(", ");
|
|
827
|
+
throw this.err(`unread byte ranges: ${formatted} (total=${this.data.length})`);
|
|
828
|
+
} else
|
|
829
|
+
return;
|
|
691
830
|
}
|
|
692
|
-
if (!
|
|
693
|
-
throw
|
|
831
|
+
if (!this.isEnd()) {
|
|
832
|
+
throw this.err(`${this.leftBytes} bytes ${this.bitPos} bits left after unpack: ${base.hex.encode(this.data.slice(this.pos))}`);
|
|
694
833
|
}
|
|
695
|
-
|
|
696
|
-
|
|
834
|
+
}
|
|
835
|
+
// User methods
|
|
836
|
+
err(msg) {
|
|
837
|
+
return Path.err("Reader", this.stack, msg);
|
|
838
|
+
}
|
|
839
|
+
offsetReader(n) {
|
|
840
|
+
if (n > this.data.length)
|
|
841
|
+
throw this.err("offsetReader: Unexpected end of buffer");
|
|
842
|
+
return new __Reader(this.absBytes(n), this.opts, this.stack, this, n);
|
|
843
|
+
}
|
|
844
|
+
bytes(n, peek = false) {
|
|
845
|
+
if (this.bitPos)
|
|
846
|
+
throw this.err("readBytes: bitPos not empty");
|
|
847
|
+
if (!Number.isFinite(n))
|
|
848
|
+
throw this.err(`readBytes: wrong length=${n}`);
|
|
849
|
+
if (this.pos + n > this.data.length)
|
|
850
|
+
throw this.err("readBytes: Unexpected end of buffer");
|
|
851
|
+
const slice = this.data.subarray(this.pos, this.pos + n);
|
|
852
|
+
if (!peek)
|
|
853
|
+
this.markBytes(n);
|
|
854
|
+
return slice;
|
|
855
|
+
}
|
|
856
|
+
byte(peek = false) {
|
|
857
|
+
if (this.bitPos)
|
|
858
|
+
throw this.err("readByte: bitPos not empty");
|
|
859
|
+
if (this.pos + 1 > this.data.length)
|
|
860
|
+
throw this.err("readBytes: Unexpected end of buffer");
|
|
861
|
+
const data = this.data[this.pos];
|
|
862
|
+
if (!peek)
|
|
863
|
+
this.markBytes(1);
|
|
864
|
+
return data;
|
|
865
|
+
}
|
|
866
|
+
get leftBytes() {
|
|
867
|
+
return this.data.length - this.pos;
|
|
868
|
+
}
|
|
869
|
+
get totalBytes() {
|
|
870
|
+
return this.data.length;
|
|
871
|
+
}
|
|
872
|
+
isEnd() {
|
|
873
|
+
return this.pos >= this.data.length && !this.bitPos;
|
|
874
|
+
}
|
|
875
|
+
// bits are read in BE mode (left to right): (0b1000_0000).readBits(1) == 1
|
|
876
|
+
bits(bits) {
|
|
877
|
+
if (bits > 32)
|
|
878
|
+
throw this.err("BitReader: cannot read more than 32 bits in single call");
|
|
879
|
+
let out = 0;
|
|
880
|
+
while (bits) {
|
|
881
|
+
if (!this.bitPos) {
|
|
882
|
+
this.bitBuf = this.byte();
|
|
883
|
+
this.bitPos = 8;
|
|
884
|
+
}
|
|
885
|
+
const take = Math.min(bits, this.bitPos);
|
|
886
|
+
this.bitPos -= take;
|
|
887
|
+
out = out << take | this.bitBuf >> this.bitPos & 2 ** take - 1;
|
|
888
|
+
this.bitBuf &= 2 ** this.bitPos - 1;
|
|
889
|
+
bits -= take;
|
|
697
890
|
}
|
|
698
|
-
|
|
699
|
-
|
|
891
|
+
return out >>> 0;
|
|
892
|
+
}
|
|
893
|
+
find(needle, pos = this.pos) {
|
|
894
|
+
if (!isBytes(needle))
|
|
895
|
+
throw this.err(`find: needle is not bytes! ${needle}`);
|
|
896
|
+
if (this.bitPos)
|
|
897
|
+
throw this.err("findByte: bitPos not empty");
|
|
898
|
+
if (!needle.length)
|
|
899
|
+
throw this.err(`find: needle is empty`);
|
|
900
|
+
for (let idx = pos; (idx = this.data.indexOf(needle[0], idx)) !== -1; idx++) {
|
|
901
|
+
if (idx === -1)
|
|
902
|
+
return;
|
|
903
|
+
const leftBytes = this.data.length - idx;
|
|
904
|
+
if (leftBytes < needle.length)
|
|
905
|
+
return;
|
|
906
|
+
if (equalBytes(needle, this.data.subarray(idx, idx + needle.length)))
|
|
907
|
+
return idx;
|
|
700
908
|
}
|
|
701
|
-
|
|
702
|
-
|
|
909
|
+
return;
|
|
910
|
+
}
|
|
911
|
+
};
|
|
912
|
+
var _Writer = class {
|
|
913
|
+
pos = 0;
|
|
914
|
+
stack;
|
|
915
|
+
// We could have a single buffer here and re-alloc it with
|
|
916
|
+
// x1.5-2 size each time it full, but it will be slower:
|
|
917
|
+
// basic/encode bench: 395ns -> 560ns
|
|
918
|
+
buffers = [];
|
|
919
|
+
ptrs = [];
|
|
920
|
+
bitBuf = 0;
|
|
921
|
+
bitPos = 0;
|
|
922
|
+
viewBuf = new Uint8Array(8);
|
|
923
|
+
view;
|
|
924
|
+
finished = false;
|
|
925
|
+
constructor(stack = []) {
|
|
926
|
+
this.stack = stack;
|
|
927
|
+
this.view = createView(this.viewBuf);
|
|
928
|
+
}
|
|
929
|
+
pushObj(obj, objFn) {
|
|
930
|
+
return Path.pushObj(this.stack, obj, objFn);
|
|
931
|
+
}
|
|
932
|
+
writeView(len, fn) {
|
|
933
|
+
if (this.finished)
|
|
934
|
+
throw this.err("buffer: finished");
|
|
935
|
+
if (!isNum(len) || len > 8)
|
|
936
|
+
throw new Error(`wrong writeView length=${len}`);
|
|
937
|
+
fn(this.view);
|
|
938
|
+
this.bytes(this.viewBuf.slice(0, len));
|
|
939
|
+
this.viewBuf.fill(0);
|
|
940
|
+
}
|
|
941
|
+
// User methods
|
|
942
|
+
err(msg) {
|
|
943
|
+
if (this.finished)
|
|
944
|
+
throw this.err("buffer: finished");
|
|
945
|
+
return Path.err("Reader", this.stack, msg);
|
|
946
|
+
}
|
|
947
|
+
bytes(b) {
|
|
948
|
+
if (this.finished)
|
|
949
|
+
throw this.err("buffer: finished");
|
|
950
|
+
if (this.bitPos)
|
|
951
|
+
throw this.err("writeBytes: ends with non-empty bit buffer");
|
|
952
|
+
this.buffers.push(b);
|
|
953
|
+
this.pos += b.length;
|
|
954
|
+
}
|
|
955
|
+
byte(b) {
|
|
956
|
+
if (this.finished)
|
|
957
|
+
throw this.err("buffer: finished");
|
|
958
|
+
if (this.bitPos)
|
|
959
|
+
throw this.err("writeByte: ends with non-empty bit buffer");
|
|
960
|
+
this.buffers.push(new Uint8Array([b]));
|
|
961
|
+
this.pos++;
|
|
962
|
+
}
|
|
963
|
+
finish(clean = true) {
|
|
964
|
+
if (this.finished)
|
|
965
|
+
throw this.err("buffer: finished");
|
|
966
|
+
if (this.bitPos)
|
|
967
|
+
throw this.err("buffer: ends with non-empty bit buffer");
|
|
968
|
+
const buffers = this.buffers.concat(this.ptrs.map((i) => i.buffer));
|
|
969
|
+
const sum = buffers.map((b) => b.length).reduce((a, b) => a + b, 0);
|
|
970
|
+
const buf = new Uint8Array(sum);
|
|
971
|
+
for (let i = 0, pad = 0; i < buffers.length; i++) {
|
|
972
|
+
const a = buffers[i];
|
|
973
|
+
buf.set(a, pad);
|
|
974
|
+
pad += a.length;
|
|
703
975
|
}
|
|
704
|
-
|
|
705
|
-
|
|
976
|
+
for (let pos = this.pos, i = 0; i < this.ptrs.length; i++) {
|
|
977
|
+
const ptr = this.ptrs[i];
|
|
978
|
+
buf.set(ptr.ptr.encode(pos), ptr.pos);
|
|
979
|
+
pos += ptr.buffer.length;
|
|
706
980
|
}
|
|
707
|
-
if (
|
|
708
|
-
|
|
981
|
+
if (clean) {
|
|
982
|
+
this.buffers = [];
|
|
983
|
+
for (const p of this.ptrs)
|
|
984
|
+
p.buffer.fill(0);
|
|
985
|
+
this.ptrs = [];
|
|
986
|
+
this.finished = true;
|
|
987
|
+
this.bitBuf = 0;
|
|
709
988
|
}
|
|
989
|
+
return buf;
|
|
710
990
|
}
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
receiver: base.hex.encode(params.receiver),
|
|
727
|
-
server: base.hex.encode(params.server),
|
|
728
|
-
hash: base.hex.encode(params.preimageHash),
|
|
729
|
-
refundLocktime: params.refundLocktime.toString(),
|
|
730
|
-
claimDelay: chunkDSTYXT2D_cjs.timelockToSequence(params.unilateralClaimDelay).toString(),
|
|
731
|
-
refundDelay: chunkDSTYXT2D_cjs.timelockToSequence(params.unilateralRefundDelay).toString(),
|
|
732
|
-
refundNoReceiverDelay: chunkDSTYXT2D_cjs.timelockToSequence(
|
|
733
|
-
params.unilateralRefundWithoutReceiverDelay
|
|
734
|
-
).toString()
|
|
735
|
-
};
|
|
736
|
-
},
|
|
737
|
-
deserializeParams(params) {
|
|
738
|
-
return {
|
|
739
|
-
sender: base.hex.decode(params.sender),
|
|
740
|
-
receiver: base.hex.decode(params.receiver),
|
|
741
|
-
server: base.hex.decode(params.server),
|
|
742
|
-
preimageHash: base.hex.decode(params.hash),
|
|
743
|
-
refundLocktime: BigInt(params.refundLocktime),
|
|
744
|
-
unilateralClaimDelay: chunkDSTYXT2D_cjs.sequenceToTimelock(Number(params.claimDelay)),
|
|
745
|
-
unilateralRefundDelay: chunkDSTYXT2D_cjs.sequenceToTimelock(Number(params.refundDelay)),
|
|
746
|
-
unilateralRefundWithoutReceiverDelay: chunkDSTYXT2D_cjs.sequenceToTimelock(
|
|
747
|
-
Number(params.refundNoReceiverDelay)
|
|
748
|
-
)
|
|
749
|
-
};
|
|
750
|
-
},
|
|
751
|
-
/**
|
|
752
|
-
* Select spending path based on context.
|
|
753
|
-
*
|
|
754
|
-
* Role is determined from `context.role` or by matching
|
|
755
|
-
* `context.walletDescriptor` (preferred) / `context.walletPubKey`
|
|
756
|
-
* against sender/receiver in contract params.
|
|
757
|
-
*/
|
|
758
|
-
selectPath(script, contract, context) {
|
|
759
|
-
const role = resolveRole(contract, context);
|
|
760
|
-
const preimage = contract.params?.preimage;
|
|
761
|
-
const refundLocktime = BigInt(contract.params.refundLocktime);
|
|
762
|
-
if (!role) {
|
|
763
|
-
return null;
|
|
764
|
-
}
|
|
765
|
-
if (context.collaborative) {
|
|
766
|
-
if (role === "receiver" && preimage) {
|
|
767
|
-
return {
|
|
768
|
-
leaf: script.claim(),
|
|
769
|
-
extraWitness: [base.hex.decode(preimage)]
|
|
770
|
-
};
|
|
771
|
-
}
|
|
772
|
-
if (role === "sender" && isCltvSatisfied(context, refundLocktime)) {
|
|
773
|
-
return {
|
|
774
|
-
leaf: script.refundWithoutReceiver()
|
|
775
|
-
};
|
|
776
|
-
}
|
|
777
|
-
return null;
|
|
778
|
-
}
|
|
779
|
-
if (role === "receiver" && preimage) {
|
|
780
|
-
const sequence = Number(contract.params.claimDelay);
|
|
781
|
-
if (!isCsvSpendable(context, sequence)) return null;
|
|
782
|
-
return {
|
|
783
|
-
leaf: script.unilateralClaim(),
|
|
784
|
-
extraWitness: [base.hex.decode(preimage)],
|
|
785
|
-
sequence
|
|
786
|
-
};
|
|
787
|
-
}
|
|
788
|
-
if (role === "sender") {
|
|
789
|
-
const sequence = Number(contract.params.refundNoReceiverDelay);
|
|
790
|
-
if (!isCsvSpendable(context, sequence)) return null;
|
|
791
|
-
return {
|
|
792
|
-
leaf: script.unilateralRefundWithoutReceiver(),
|
|
793
|
-
sequence
|
|
794
|
-
};
|
|
795
|
-
}
|
|
796
|
-
return null;
|
|
797
|
-
},
|
|
798
|
-
/**
|
|
799
|
-
* Get all possible spending paths (no timelock checks).
|
|
800
|
-
*
|
|
801
|
-
* Role is determined from `context.role` or by matching
|
|
802
|
-
* `context.walletDescriptor` (preferred) / `context.walletPubKey`
|
|
803
|
-
* against sender/receiver in contract params.
|
|
804
|
-
*/
|
|
805
|
-
getAllSpendingPaths(script, contract, context) {
|
|
806
|
-
const role = resolveRole(contract, context);
|
|
807
|
-
const paths = [];
|
|
808
|
-
if (!role) {
|
|
809
|
-
return paths;
|
|
810
|
-
}
|
|
811
|
-
const preimage = contract.params?.preimage;
|
|
812
|
-
if (context.collaborative) {
|
|
813
|
-
if (role === "receiver" && preimage) {
|
|
814
|
-
paths.push({
|
|
815
|
-
leaf: script.claim(),
|
|
816
|
-
extraWitness: [base.hex.decode(preimage)]
|
|
817
|
-
});
|
|
818
|
-
}
|
|
819
|
-
if (role === "sender") {
|
|
820
|
-
paths.push({
|
|
821
|
-
leaf: script.refundWithoutReceiver()
|
|
822
|
-
});
|
|
823
|
-
}
|
|
824
|
-
} else {
|
|
825
|
-
if (role === "receiver" && preimage) {
|
|
826
|
-
const sequence = Number(contract.params.claimDelay);
|
|
827
|
-
paths.push({
|
|
828
|
-
leaf: script.unilateralClaim(),
|
|
829
|
-
extraWitness: [base.hex.decode(preimage)],
|
|
830
|
-
sequence
|
|
831
|
-
});
|
|
832
|
-
}
|
|
833
|
-
if (role === "sender") {
|
|
834
|
-
const sequence = Number(contract.params.refundNoReceiverDelay);
|
|
835
|
-
paths.push({
|
|
836
|
-
leaf: script.unilateralRefundWithoutReceiver(),
|
|
837
|
-
sequence
|
|
838
|
-
});
|
|
991
|
+
bits(value, bits) {
|
|
992
|
+
if (bits > 32)
|
|
993
|
+
throw this.err("writeBits: cannot write more than 32 bits in single call");
|
|
994
|
+
if (value >= 2 ** bits)
|
|
995
|
+
throw this.err(`writeBits: value (${value}) >= 2**bits (${bits})`);
|
|
996
|
+
while (bits) {
|
|
997
|
+
const take = Math.min(bits, 8 - this.bitPos);
|
|
998
|
+
this.bitBuf = this.bitBuf << take | value >> bits - take;
|
|
999
|
+
this.bitPos += take;
|
|
1000
|
+
bits -= take;
|
|
1001
|
+
value &= 2 ** bits - 1;
|
|
1002
|
+
if (this.bitPos === 8) {
|
|
1003
|
+
this.bitPos = 0;
|
|
1004
|
+
this.buffers.push(new Uint8Array([this.bitBuf]));
|
|
1005
|
+
this.pos++;
|
|
839
1006
|
}
|
|
840
1007
|
}
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
1008
|
+
}
|
|
1009
|
+
};
|
|
1010
|
+
function _wrap(inner) {
|
|
1011
|
+
return {
|
|
1012
|
+
// NOTE: we cannot export validate here, since it is likely mistake.
|
|
1013
|
+
encodeStream: inner.encodeStream,
|
|
1014
|
+
decodeStream: inner.decodeStream,
|
|
1015
|
+
size: inner.size,
|
|
1016
|
+
encode: (value) => {
|
|
1017
|
+
const w = new _Writer();
|
|
1018
|
+
inner.encodeStream(w, value);
|
|
1019
|
+
return w.finish();
|
|
1020
|
+
},
|
|
1021
|
+
decode: (data, opts = {}) => {
|
|
1022
|
+
const r = new _Reader(data, opts);
|
|
1023
|
+
const res = inner.decodeStream(r);
|
|
1024
|
+
r.finish();
|
|
1025
|
+
return res;
|
|
848
1026
|
}
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
1027
|
+
};
|
|
1028
|
+
}
|
|
1029
|
+
function validate(inner, fn) {
|
|
1030
|
+
if (!isCoder(inner))
|
|
1031
|
+
throw new Error(`validate: invalid inner value ${inner}`);
|
|
1032
|
+
if (typeof fn !== "function")
|
|
1033
|
+
throw new Error("validate: fn should be function");
|
|
1034
|
+
return _wrap({
|
|
1035
|
+
size: inner.size,
|
|
1036
|
+
encodeStream: (w, value) => {
|
|
1037
|
+
let res;
|
|
1038
|
+
try {
|
|
1039
|
+
res = fn(value);
|
|
1040
|
+
} catch (e) {
|
|
1041
|
+
throw w.err(e);
|
|
857
1042
|
}
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
1043
|
+
inner.encodeStream(w, res);
|
|
1044
|
+
},
|
|
1045
|
+
decodeStream: (r) => {
|
|
1046
|
+
const res = inner.decodeStream(r);
|
|
1047
|
+
try {
|
|
1048
|
+
return fn(res);
|
|
1049
|
+
} catch (e) {
|
|
1050
|
+
throw r.err(e);
|
|
862
1051
|
}
|
|
863
|
-
return paths;
|
|
864
1052
|
}
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
1053
|
+
});
|
|
1054
|
+
}
|
|
1055
|
+
var wrap = (inner) => {
|
|
1056
|
+
const res = _wrap(inner);
|
|
1057
|
+
return inner.validate ? validate(res, inner.validate) : res;
|
|
1058
|
+
};
|
|
1059
|
+
var isBaseCoder = (elm) => isPlainObject(elm) && typeof elm.decode === "function" && typeof elm.encode === "function";
|
|
1060
|
+
function isCoder(elm) {
|
|
1061
|
+
return isPlainObject(elm) && isBaseCoder(elm) && typeof elm.encodeStream === "function" && typeof elm.decodeStream === "function" && (elm.size === void 0 || isNum(elm.size));
|
|
1062
|
+
}
|
|
1063
|
+
var view = (len, opts) => wrap({
|
|
1064
|
+
size: len,
|
|
1065
|
+
encodeStream: (w, value) => w.writeView(len, (view2) => opts.write(view2, value)),
|
|
1066
|
+
decodeStream: (r) => r.readView(len, opts.read),
|
|
1067
|
+
validate: (value) => {
|
|
1068
|
+
if (typeof value !== "number")
|
|
1069
|
+
throw new Error(`viewCoder: expected number, got ${typeof value}`);
|
|
1070
|
+
if (opts.validate)
|
|
1071
|
+
opts.validate(value);
|
|
1072
|
+
return value;
|
|
1073
|
+
}
|
|
1074
|
+
});
|
|
1075
|
+
var intView = (len, signed, opts) => {
|
|
1076
|
+
const bits = len * 8;
|
|
1077
|
+
const maxVal = 2 ** bits;
|
|
1078
|
+
const validateUnsigned = (value) => {
|
|
1079
|
+
if (!isNum(value))
|
|
1080
|
+
throw new Error(`uintView: value is not safe integer: ${value}`);
|
|
1081
|
+
if (0 > value || value >= maxVal) {
|
|
1082
|
+
throw new Error(`uintView: value out of bounds. Expected 0 <= ${value} < ${maxVal}`);
|
|
874
1083
|
}
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
1084
|
+
};
|
|
1085
|
+
return view(len, {
|
|
1086
|
+
write: opts.write,
|
|
1087
|
+
read: opts.read,
|
|
1088
|
+
validate: validateUnsigned
|
|
1089
|
+
});
|
|
1090
|
+
};
|
|
1091
|
+
var U32LE = /* @__PURE__ */ intView(4, false, {
|
|
1092
|
+
read: (view2, pos) => view2.getUint32(pos, true),
|
|
1093
|
+
write: (view2, value) => view2.setUint32(0, value, true)
|
|
1094
|
+
});
|
|
1095
|
+
var U16LE = /* @__PURE__ */ intView(2, false, {
|
|
1096
|
+
read: (view2, pos) => view2.getUint16(pos, true),
|
|
1097
|
+
write: (view2, value) => view2.setUint16(0, value, true)
|
|
1098
|
+
});
|
|
1099
|
+
var U8 = /* @__PURE__ */ intView(1, false, {
|
|
1100
|
+
read: (view2, pos) => view2.getUint8(pos),
|
|
1101
|
+
write: (view2, value) => view2.setUint8(0, value)
|
|
1102
|
+
});
|
|
1103
|
+
var ARKADE_OP = {
|
|
1104
|
+
// Merkle Branch Verification (0xb3 — repurposed NOP4 slot)
|
|
1105
|
+
MERKLEBRANCHVERIFY: 179,
|
|
1106
|
+
// Digest (0xc3)
|
|
1107
|
+
DIGEST: 195,
|
|
1108
|
+
// SHA256 Streaming (0xc4-0xc6)
|
|
1109
|
+
SHA256INITIALIZE: 196,
|
|
1110
|
+
SHA256UPDATE: 197,
|
|
1111
|
+
SHA256FINALIZE: 198,
|
|
1112
|
+
// Input Introspection (0xc7-0xcb)
|
|
1113
|
+
INSPECTINPUTOUTPOINT: 199,
|
|
1114
|
+
INSPECTINPUTARKADESCRIPTHASH: 200,
|
|
1115
|
+
INSPECTINPUTVALUE: 201,
|
|
1116
|
+
INSPECTINPUTSCRIPTPUBKEY: 202,
|
|
1117
|
+
INSPECTINPUTSEQUENCE: 203,
|
|
1118
|
+
// Signatures (0xcc-0xcd)
|
|
1119
|
+
CHECKSIGFROMSTACK: 204,
|
|
1120
|
+
PUSHCURRENTINPUTINDEX: 205,
|
|
1121
|
+
// Input Arkade Witness Introspection (0xce)
|
|
1122
|
+
INSPECTINPUTARKADEWITNESSHASH: 206,
|
|
1123
|
+
// Output Introspection (0xcf, 0xd1)
|
|
1124
|
+
INSPECTOUTPUTVALUE: 207,
|
|
1125
|
+
INSPECTOUTPUTSCRIPTPUBKEY: 209,
|
|
1126
|
+
// Transaction Introspection (0xd2-0xd6)
|
|
1127
|
+
INSPECTVERSION: 210,
|
|
1128
|
+
INSPECTLOCKTIME: 211,
|
|
1129
|
+
INSPECTNUMINPUTS: 212,
|
|
1130
|
+
INSPECTNUMOUTPUTS: 213,
|
|
1131
|
+
TXWEIGHT: 214,
|
|
1132
|
+
// Byte/Number Conversion & Arithmetic (0xd7-0xda)
|
|
1133
|
+
NUM2BIN: 215,
|
|
1134
|
+
BIN2NUM: 216,
|
|
1135
|
+
REVERSEBYTES: 217,
|
|
1136
|
+
MODEXP: 218,
|
|
1137
|
+
// 0xdb-0xdf are unassigned (OP_UNKNOWN219-223)
|
|
1138
|
+
// EC Operations (0xe0-0xe2)
|
|
1139
|
+
ECADD: 224,
|
|
1140
|
+
ECMUL: 225,
|
|
1141
|
+
ECPAIRING: 226,
|
|
1142
|
+
// EC Operations (0xe3-0xe4)
|
|
1143
|
+
ECMULSCALARVERIFY: 227,
|
|
1144
|
+
TWEAKVERIFY: 228,
|
|
1145
|
+
// Asset Groups (0xe5-0xf2)
|
|
1146
|
+
INSPECTNUMASSETGROUPS: 229,
|
|
1147
|
+
INSPECTASSETGROUPASSETID: 230,
|
|
1148
|
+
INSPECTASSETGROUPCTRL: 231,
|
|
1149
|
+
FINDASSETGROUPBYASSETID: 232,
|
|
1150
|
+
INSPECTASSETGROUPMETADATAHASH: 233,
|
|
1151
|
+
INSPECTASSETGROUPNUM: 234,
|
|
1152
|
+
INSPECTASSETGROUP: 235,
|
|
1153
|
+
INSPECTASSETGROUPSUM: 236,
|
|
1154
|
+
INSPECTOUTASSETCOUNT: 237,
|
|
1155
|
+
INSPECTOUTASSETAT: 238,
|
|
1156
|
+
INSPECTOUTASSETLOOKUP: 239,
|
|
1157
|
+
INSPECTINASSETCOUNT: 240,
|
|
1158
|
+
INSPECTINASSETAT: 241,
|
|
1159
|
+
INSPECTINASSETLOOKUP: 242,
|
|
1160
|
+
// Transaction ID (0xf3)
|
|
1161
|
+
TXID: 243,
|
|
1162
|
+
// Packet Introspection (0xf4-0xf5) — added in emulator v0.0.1
|
|
1163
|
+
INSPECTPACKET: 244,
|
|
1164
|
+
INSPECTINPUTPACKET: 245,
|
|
1165
|
+
// Signature Hash (0xf6)
|
|
1166
|
+
SIGHASH: 246
|
|
1167
|
+
};
|
|
1168
|
+
var ARKADE_OPCODES = Object.values(ARKADE_OP);
|
|
1169
|
+
var ARKADE_OPCODE_NAMES = Object.fromEntries(
|
|
1170
|
+
Object.entries(ARKADE_OP).map(([name, value]) => [value, name])
|
|
1171
|
+
);
|
|
1172
|
+
var ARKADE_OPCODE_VALUES = Object.fromEntries(
|
|
1173
|
+
Object.entries(ARKADE_OPCODE_NAMES).map(([value, name]) => [name, Number(value)])
|
|
1174
|
+
);
|
|
1175
|
+
function buildBitcoinOpcodeNames() {
|
|
1176
|
+
const names = {};
|
|
1177
|
+
for (const [key, value] of Object.entries(btcSigner.OP)) {
|
|
1178
|
+
if (typeof value === "number") {
|
|
1179
|
+
const name = key.startsWith("OP_") ? key : `OP_${key}`;
|
|
1180
|
+
names[value] = name;
|
|
883
1181
|
}
|
|
884
|
-
return paths;
|
|
885
1182
|
}
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
},
|
|
898
|
-
selectPath(script, contract, context) {
|
|
899
|
-
return DefaultContractHandler.selectPath(script, contract, context);
|
|
900
|
-
},
|
|
901
|
-
getAllSpendingPaths(script, contract, context) {
|
|
902
|
-
return DefaultContractHandler.getAllSpendingPaths(script, contract, context);
|
|
903
|
-
},
|
|
904
|
-
getSpendablePaths(script, contract, context) {
|
|
905
|
-
return DefaultContractHandler.getSpendablePaths(script, contract, context);
|
|
906
|
-
},
|
|
907
|
-
/**
|
|
908
|
-
* Probe the on-chain UTXO set for a boarding output at this HD index.
|
|
909
|
-
*
|
|
910
|
-
* Boarding's source of truth is the **current** on-chain coin set
|
|
911
|
-
* (`OnchainProvider.getCoins`), not the Ark indexer: a boarded (spent)
|
|
912
|
-
* boarding output becomes an L2 VTXO at the receive index, so the
|
|
913
|
-
* indexer probe already keeps the gap window open for it; only an
|
|
914
|
-
* *unspent* boarding output needs the on-chain probe (see plan §2).
|
|
915
|
-
*
|
|
916
|
-
* No-ops (returns `[]`) when boarding discovery is not plumbed — i.e.
|
|
917
|
-
* `deps.boardingTimelock` or `deps.onchainNetwork` is absent — so the
|
|
918
|
-
* scanner unit harness (which sets neither) is unaffected.
|
|
919
|
-
*
|
|
920
|
-
* Always emits `type: "boarding"`, even in the degenerate equal-delay case
|
|
921
|
-
* where the boarding script is byte-identical to a `default` script at this
|
|
922
|
-
* index (`boardingExitDelay === unilateralExitDelay`). The collision is no
|
|
923
|
-
* longer pre-judged in discovery; it is tolerated FIRST-WINS at the
|
|
924
|
-
* persistence layer ({@link ContractManager.upsertContract}). `deps.csvTimelocks`
|
|
925
|
-
* is intentionally not read here.
|
|
926
|
-
*/
|
|
927
|
-
async discoverAt(index, descriptor, deps) {
|
|
928
|
-
if (!deps.boardingTimelock || !deps.onchainNetwork) return [];
|
|
929
|
-
const pubKey = deriveDescriptorLeafPubKey(descriptor);
|
|
930
|
-
const script = new exports.DefaultVtxo.Script({
|
|
931
|
-
pubKey,
|
|
932
|
-
serverPubKey: deps.serverPubKey,
|
|
933
|
-
csvTimelock: deps.boardingTimelock
|
|
934
|
-
});
|
|
935
|
-
const onchainAddress = script.onchainAddress(deps.onchainNetwork);
|
|
936
|
-
const coins = await deps.onchainProvider.getCoins(onchainAddress);
|
|
937
|
-
if (coins.length === 0) return [];
|
|
938
|
-
const scriptHex = base.hex.encode(script.pkScript);
|
|
939
|
-
const boardingSeq = chunkDSTYXT2D_cjs.timelockToSequence(deps.boardingTimelock);
|
|
940
|
-
return [
|
|
941
|
-
{
|
|
942
|
-
type: "boarding",
|
|
943
|
-
params: {
|
|
944
|
-
pubKey: base.hex.encode(pubKey),
|
|
945
|
-
serverPubKey: base.hex.encode(deps.serverPubKey),
|
|
946
|
-
csvTimelock: boardingSeq.toString()
|
|
947
|
-
},
|
|
948
|
-
script: scriptHex,
|
|
949
|
-
// The persisted row's `address` is the *Ark* address (not the
|
|
950
|
-
// on-chain P2TR), matching the row registered at init so the
|
|
951
|
-
// ContractWatcher keeps monitoring the same L2 script and the
|
|
952
|
-
// VTXO repository bucket lines up (plan §6-I.4). The P2TR is
|
|
953
|
-
// recomputed from params only for the `getCoins` probe.
|
|
954
|
-
address: script.address(deps.network.hrp, deps.serverPubKey).encode(),
|
|
955
|
-
// Tag rotated rows (index > 0) so boot resolution can find the
|
|
956
|
-
// newest boarding address and descriptor-aware signing can
|
|
957
|
-
// recover the per-index key (plan §6-II/§6-III.3). The index-0
|
|
958
|
-
// baseline stays untagged, matching `default`/`delegate`.
|
|
959
|
-
...rotatedReceiveMetadata(index, descriptor)
|
|
960
|
-
}
|
|
961
|
-
];
|
|
1183
|
+
names[0] = "OP_0";
|
|
1184
|
+
return names;
|
|
1185
|
+
}
|
|
1186
|
+
function buildBitcoinOpcodeValues() {
|
|
1187
|
+
const values = {};
|
|
1188
|
+
for (const [key, value] of Object.entries(btcSigner.OP)) {
|
|
1189
|
+
if (typeof value === "number") {
|
|
1190
|
+
const name = key.startsWith("OP_") ? key : `OP_${key}`;
|
|
1191
|
+
values[name] = value;
|
|
1192
|
+
values[key] = value;
|
|
1193
|
+
}
|
|
962
1194
|
}
|
|
1195
|
+
return values;
|
|
1196
|
+
}
|
|
1197
|
+
var BITCOIN_OPCODE_NAMES = buildBitcoinOpcodeNames();
|
|
1198
|
+
var BITCOIN_OPCODE_VALUES = buildBitcoinOpcodeValues();
|
|
1199
|
+
var OPCODE_NAMES = {
|
|
1200
|
+
...BITCOIN_OPCODE_NAMES,
|
|
1201
|
+
// Add Arkade opcodes with OP_ prefix
|
|
1202
|
+
...Object.fromEntries(
|
|
1203
|
+
Object.entries(ARKADE_OPCODE_NAMES).map(([value, name]) => [Number(value), `OP_${name}`])
|
|
1204
|
+
)
|
|
963
1205
|
};
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
1206
|
+
var OPCODE_VALUES = {
|
|
1207
|
+
...BITCOIN_OPCODE_VALUES,
|
|
1208
|
+
// Add Arkade opcodes with and without OP_ prefix
|
|
1209
|
+
...ARKADE_OPCODE_VALUES,
|
|
1210
|
+
...Object.fromEntries(
|
|
1211
|
+
Object.entries(ARKADE_OPCODE_VALUES).map(([name, value]) => [`OP_${name}`, value])
|
|
1212
|
+
)
|
|
1213
|
+
};
|
|
1214
|
+
function getOpcodeName(value) {
|
|
1215
|
+
if (value >= 1 && value <= 75) {
|
|
1216
|
+
return `OP_DATA_${value}`;
|
|
1217
|
+
}
|
|
1218
|
+
return OPCODE_NAMES[value];
|
|
971
1219
|
}
|
|
972
|
-
function
|
|
973
|
-
|
|
1220
|
+
function getOpcodeValue(name) {
|
|
1221
|
+
const dataMatch = name.match(/^OP_DATA_(\d+)$/);
|
|
1222
|
+
if (dataMatch) {
|
|
1223
|
+
const n = parseInt(dataMatch[1], 10);
|
|
1224
|
+
if (n >= 1 && n <= 75) {
|
|
1225
|
+
return n;
|
|
1226
|
+
}
|
|
1227
|
+
return void 0;
|
|
1228
|
+
}
|
|
1229
|
+
return OPCODE_VALUES[name];
|
|
974
1230
|
}
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1231
|
+
|
|
1232
|
+
// src/arkade/bignum.ts
|
|
1233
|
+
var bignum_exports = {};
|
|
1234
|
+
chunk5BLDMQED_cjs.__export(bignum_exports, {
|
|
1235
|
+
BIGNUM_MAX_BYTES: () => BIGNUM_MAX_BYTES,
|
|
1236
|
+
decode: () => decode,
|
|
1237
|
+
encode: () => encode
|
|
1238
|
+
});
|
|
1239
|
+
var BIGNUM_MAX_BYTES = 520;
|
|
1240
|
+
var codec = btcSigner.ScriptNum(
|
|
1241
|
+
BIGNUM_MAX_BYTES,
|
|
1242
|
+
/* forceMinimal */
|
|
1243
|
+
true
|
|
1244
|
+
);
|
|
1245
|
+
function encode(value) {
|
|
1246
|
+
const result = codec.encode(value);
|
|
1247
|
+
if (result.length > BIGNUM_MAX_BYTES) {
|
|
1248
|
+
throw new Error(`BigNum value exceeds 520 bytes (encoded to ${result.length} bytes)`);
|
|
1249
|
+
}
|
|
1250
|
+
return result;
|
|
978
1251
|
}
|
|
979
|
-
function
|
|
980
|
-
return
|
|
1252
|
+
function decode(value) {
|
|
1253
|
+
return codec.decode(value);
|
|
981
1254
|
}
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
if (pos < 0)
|
|
997
|
-
throw new Error(`wrong pos=${pos}`);
|
|
998
|
-
if (pos + len > bsLen)
|
|
999
|
-
throw new Error(`wrong range=${pos}/${len} of ${bsLen}`);
|
|
1000
|
-
},
|
|
1001
|
-
set: (bs, chunk, value, allowRewrite = true) => {
|
|
1002
|
-
if (!allowRewrite && (bs[chunk] & value) !== 0)
|
|
1003
|
-
return false;
|
|
1004
|
-
bs[chunk] |= value;
|
|
1005
|
-
return true;
|
|
1006
|
-
},
|
|
1007
|
-
pos: (pos, i) => ({
|
|
1008
|
-
chunk: Math.floor((pos + i) / 32),
|
|
1009
|
-
mask: 1 << 32 - (pos + i) % 32 - 1
|
|
1010
|
-
}),
|
|
1011
|
-
indices: (bs, len, invert = false) => {
|
|
1012
|
-
Bitset.checkLen(bs, len);
|
|
1013
|
-
const { FULL_MASK, BITS } = Bitset;
|
|
1014
|
-
const left = BITS - len % BITS;
|
|
1015
|
-
const lastMask = left ? FULL_MASK >>> left << left : FULL_MASK;
|
|
1016
|
-
const res = [];
|
|
1017
|
-
for (let i = 0; i < bs.length; i++) {
|
|
1018
|
-
let c = bs[i];
|
|
1019
|
-
if (invert)
|
|
1020
|
-
c = ~c;
|
|
1021
|
-
if (i === bs.length - 1)
|
|
1022
|
-
c &= lastMask;
|
|
1023
|
-
if (c === 0)
|
|
1255
|
+
|
|
1256
|
+
// src/arkade/script.ts
|
|
1257
|
+
var ARKADE_OPS = { ...btcSigner.OP, ...ARKADE_OP };
|
|
1258
|
+
var ArkadeOPNames = {};
|
|
1259
|
+
for (const [k, v] of Object.entries(ARKADE_OPS)) {
|
|
1260
|
+
if (typeof v === "number") ArkadeOPNames[v] = k;
|
|
1261
|
+
}
|
|
1262
|
+
var ArkadeScript = wrap({
|
|
1263
|
+
encodeStream: (w, value) => {
|
|
1264
|
+
for (let o of value) {
|
|
1265
|
+
if (typeof o === "string") {
|
|
1266
|
+
const v = ARKADE_OPS[o];
|
|
1267
|
+
if (v === void 0) throw new Error(`Unknown opcode=${o}`);
|
|
1268
|
+
w.byte(v);
|
|
1024
1269
|
continue;
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1270
|
+
} else if (typeof o === "number" || typeof o === "bigint") {
|
|
1271
|
+
if (o === 0 || o === 0n) {
|
|
1272
|
+
w.byte(0);
|
|
1273
|
+
continue;
|
|
1274
|
+
} else if (o >= 1 && o <= 16) {
|
|
1275
|
+
w.byte(btcSigner.OP.OP_1 - 1 + Number(o));
|
|
1276
|
+
continue;
|
|
1277
|
+
} else if (o === -1 || o === -1n) {
|
|
1278
|
+
w.byte(btcSigner.OP["1NEGATE"]);
|
|
1279
|
+
continue;
|
|
1280
|
+
}
|
|
1281
|
+
const big = typeof o === "number" ? BigInt(o) : o;
|
|
1282
|
+
o = encode(big);
|
|
1029
1283
|
}
|
|
1284
|
+
if (!(o instanceof Uint8Array)) throw new Error(`Wrong Script OP=${o} (${typeof o})`);
|
|
1285
|
+
const len = o.length;
|
|
1286
|
+
if (len < btcSigner.OP.PUSHDATA1) w.byte(len);
|
|
1287
|
+
else if (len <= 255) {
|
|
1288
|
+
w.byte(btcSigner.OP.PUSHDATA1);
|
|
1289
|
+
w.byte(len);
|
|
1290
|
+
} else if (len <= 65535) {
|
|
1291
|
+
w.byte(btcSigner.OP.PUSHDATA2);
|
|
1292
|
+
w.bytes(U16LE.encode(len));
|
|
1293
|
+
} else {
|
|
1294
|
+
w.byte(btcSigner.OP.PUSHDATA4);
|
|
1295
|
+
w.bytes(U32LE.encode(len));
|
|
1296
|
+
}
|
|
1297
|
+
w.bytes(o);
|
|
1030
1298
|
}
|
|
1031
|
-
return res;
|
|
1032
|
-
},
|
|
1033
|
-
range: (arr) => {
|
|
1034
|
-
const res = [];
|
|
1035
|
-
let cur;
|
|
1036
|
-
for (const i of arr) {
|
|
1037
|
-
if (cur === void 0 || i !== cur.pos + cur.length)
|
|
1038
|
-
res.push(cur = { pos: i, length: 1 });
|
|
1039
|
-
else
|
|
1040
|
-
cur.length += 1;
|
|
1041
|
-
}
|
|
1042
|
-
return res;
|
|
1043
|
-
},
|
|
1044
|
-
rangeDebug: (bs, len, invert = false) => `[${Bitset.range(Bitset.indices(bs, len, invert)).map((i) => `(${i.pos}/${i.length})`).join(", ")}]`,
|
|
1045
|
-
setRange: (bs, bsLen, pos, len, allowRewrite = true) => {
|
|
1046
|
-
Bitset.chunkLen(bsLen, pos, len);
|
|
1047
|
-
const { FULL_MASK, BITS } = Bitset;
|
|
1048
|
-
const first = pos % BITS ? Math.floor(pos / BITS) : void 0;
|
|
1049
|
-
const lastPos = pos + len;
|
|
1050
|
-
const last = lastPos % BITS ? Math.floor(lastPos / BITS) : void 0;
|
|
1051
|
-
if (first !== void 0 && first === last)
|
|
1052
|
-
return Bitset.set(bs, first, FULL_MASK >>> BITS - len << BITS - len - pos, allowRewrite);
|
|
1053
|
-
if (first !== void 0) {
|
|
1054
|
-
if (!Bitset.set(bs, first, FULL_MASK >>> pos % BITS, allowRewrite))
|
|
1055
|
-
return false;
|
|
1056
|
-
}
|
|
1057
|
-
const start = first !== void 0 ? first + 1 : pos / BITS;
|
|
1058
|
-
const end = last !== void 0 ? last : lastPos / BITS;
|
|
1059
|
-
for (let i = start; i < end; i++)
|
|
1060
|
-
if (!Bitset.set(bs, i, FULL_MASK, allowRewrite))
|
|
1061
|
-
return false;
|
|
1062
|
-
if (last !== void 0 && first !== last) {
|
|
1063
|
-
if (!Bitset.set(bs, last, FULL_MASK << BITS - lastPos % BITS, allowRewrite))
|
|
1064
|
-
return false;
|
|
1065
|
-
}
|
|
1066
|
-
return true;
|
|
1067
|
-
}
|
|
1068
|
-
};
|
|
1069
|
-
var Path = {
|
|
1070
|
-
/**
|
|
1071
|
-
* Internal method for handling stack of paths (debug, errors, dynamic fields via path)
|
|
1072
|
-
* This is looks ugly (callback), but allows us to force stack cleaning by construction (.pop always after function).
|
|
1073
|
-
* Also, this makes impossible:
|
|
1074
|
-
* - pushing field when stack is empty
|
|
1075
|
-
* - pushing field inside of field (real bug)
|
|
1076
|
-
* NOTE: we don't want to do '.pop' on error!
|
|
1077
|
-
*/
|
|
1078
|
-
pushObj: (stack, obj, objFn) => {
|
|
1079
|
-
const last = { obj };
|
|
1080
|
-
stack.push(last);
|
|
1081
|
-
objFn((field, fieldFn) => {
|
|
1082
|
-
last.field = field;
|
|
1083
|
-
fieldFn();
|
|
1084
|
-
last.field = void 0;
|
|
1085
|
-
});
|
|
1086
|
-
stack.pop();
|
|
1087
|
-
},
|
|
1088
|
-
path: (stack) => {
|
|
1089
|
-
const res = [];
|
|
1090
|
-
for (const i of stack)
|
|
1091
|
-
if (i.field !== void 0)
|
|
1092
|
-
res.push(i.field);
|
|
1093
|
-
return res.join("/");
|
|
1094
|
-
},
|
|
1095
|
-
err: (name, stack, msg) => {
|
|
1096
|
-
const err = new Error(`${name}(${Path.path(stack)}): ${typeof msg === "string" ? msg : msg.message}`);
|
|
1097
|
-
if (msg instanceof Error && msg.stack)
|
|
1098
|
-
err.stack = msg.stack;
|
|
1099
|
-
return err;
|
|
1100
1299
|
},
|
|
1101
|
-
|
|
1102
|
-
const
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
if (
|
|
1114
|
-
|
|
1115
|
-
cur
|
|
1300
|
+
decodeStream: (r) => {
|
|
1301
|
+
const out = [];
|
|
1302
|
+
while (!r.isEnd()) {
|
|
1303
|
+
const cur = r.byte();
|
|
1304
|
+
if (btcSigner.OP.OP_0 < cur && cur <= btcSigner.OP.PUSHDATA4) {
|
|
1305
|
+
let len;
|
|
1306
|
+
if (cur < btcSigner.OP.PUSHDATA1) len = cur;
|
|
1307
|
+
else if (cur === btcSigner.OP.PUSHDATA1) len = U8.decodeStream(r);
|
|
1308
|
+
else if (cur === btcSigner.OP.PUSHDATA2) len = U16LE.decodeStream(r);
|
|
1309
|
+
else if (cur === btcSigner.OP.PUSHDATA4) len = U32LE.decodeStream(r);
|
|
1310
|
+
else throw new Error("Should be not possible");
|
|
1311
|
+
out.push(r.bytes(len));
|
|
1312
|
+
} else if (cur === 0) {
|
|
1313
|
+
out.push(0);
|
|
1314
|
+
} else if (btcSigner.OP.OP_1 <= cur && cur <= btcSigner.OP.OP_16) {
|
|
1315
|
+
out.push(cur - (btcSigner.OP.OP_1 - 1));
|
|
1316
|
+
} else if (cur === btcSigner.OP["1NEGATE"]) {
|
|
1317
|
+
out.push(-1);
|
|
1318
|
+
} else {
|
|
1319
|
+
const op = ArkadeOPNames[cur];
|
|
1320
|
+
if (op === void 0) throw new Error(`Unknown opcode=${cur.toString(16)}`);
|
|
1321
|
+
out.push(op);
|
|
1322
|
+
}
|
|
1116
1323
|
}
|
|
1117
|
-
return
|
|
1118
|
-
}
|
|
1119
|
-
};
|
|
1120
|
-
var _Reader = class __Reader {
|
|
1121
|
-
pos = 0;
|
|
1122
|
-
data;
|
|
1123
|
-
opts;
|
|
1124
|
-
stack;
|
|
1125
|
-
parent;
|
|
1126
|
-
parentOffset;
|
|
1127
|
-
bitBuf = 0;
|
|
1128
|
-
bitPos = 0;
|
|
1129
|
-
bs;
|
|
1130
|
-
// bitset
|
|
1131
|
-
view;
|
|
1132
|
-
constructor(data, opts = {}, stack = [], parent = void 0, parentOffset = 0) {
|
|
1133
|
-
this.data = data;
|
|
1134
|
-
this.opts = opts;
|
|
1135
|
-
this.stack = stack;
|
|
1136
|
-
this.parent = parent;
|
|
1137
|
-
this.parentOffset = parentOffset;
|
|
1138
|
-
this.view = createView(data);
|
|
1139
|
-
}
|
|
1140
|
-
/** Internal method for pointers. */
|
|
1141
|
-
_enablePointers() {
|
|
1142
|
-
if (this.parent)
|
|
1143
|
-
return this.parent._enablePointers();
|
|
1144
|
-
if (this.bs)
|
|
1145
|
-
return;
|
|
1146
|
-
this.bs = Bitset.create(this.data.length);
|
|
1147
|
-
Bitset.setRange(this.bs, this.data.length, 0, this.pos, this.opts.allowMultipleReads);
|
|
1148
|
-
}
|
|
1149
|
-
markBytesBS(pos, len) {
|
|
1150
|
-
if (this.parent)
|
|
1151
|
-
return this.parent.markBytesBS(this.parentOffset + pos, len);
|
|
1152
|
-
if (!len)
|
|
1153
|
-
return true;
|
|
1154
|
-
if (!this.bs)
|
|
1155
|
-
return true;
|
|
1156
|
-
return Bitset.setRange(this.bs, this.data.length, pos, len, false);
|
|
1157
|
-
}
|
|
1158
|
-
markBytes(len) {
|
|
1159
|
-
const pos = this.pos;
|
|
1160
|
-
this.pos += len;
|
|
1161
|
-
const res = this.markBytesBS(pos, len);
|
|
1162
|
-
if (!this.opts.allowMultipleReads && !res)
|
|
1163
|
-
throw this.err(`multiple read pos=${this.pos} len=${len}`);
|
|
1164
|
-
return res;
|
|
1165
|
-
}
|
|
1166
|
-
pushObj(obj, objFn) {
|
|
1167
|
-
return Path.pushObj(this.stack, obj, objFn);
|
|
1168
|
-
}
|
|
1169
|
-
readView(n, fn) {
|
|
1170
|
-
if (!Number.isFinite(n))
|
|
1171
|
-
throw this.err(`readView: wrong length=${n}`);
|
|
1172
|
-
if (this.pos + n > this.data.length)
|
|
1173
|
-
throw this.err("readView: Unexpected end of buffer");
|
|
1174
|
-
const res = fn(this.view, this.pos);
|
|
1175
|
-
this.markBytes(n);
|
|
1176
|
-
return res;
|
|
1324
|
+
return out;
|
|
1177
1325
|
}
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1326
|
+
});
|
|
1327
|
+
function toASM(script) {
|
|
1328
|
+
const parts = [];
|
|
1329
|
+
for (const op of script) {
|
|
1330
|
+
if (typeof op === "string") {
|
|
1331
|
+
const name = op.startsWith("OP_") ? op : `OP_${op}`;
|
|
1332
|
+
parts.push(name);
|
|
1333
|
+
} else if (typeof op === "number" || typeof op === "bigint") {
|
|
1334
|
+
if (op === 0 || op === 0n) {
|
|
1335
|
+
parts.push("OP_0");
|
|
1336
|
+
} else if (op >= 1 && op <= 16) {
|
|
1337
|
+
parts.push(`OP_${op}`);
|
|
1338
|
+
} else if (op === -1 || op === -1n) {
|
|
1339
|
+
parts.push("OP_1NEGATE");
|
|
1340
|
+
} else {
|
|
1341
|
+
parts.push(base.hex.encode(encode(BigInt(op))));
|
|
1342
|
+
}
|
|
1343
|
+
} else {
|
|
1344
|
+
parts.push(base.hex.encode(op));
|
|
1345
|
+
}
|
|
1183
1346
|
}
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1347
|
+
return parts.join(" ");
|
|
1348
|
+
}
|
|
1349
|
+
function fromASM(asm) {
|
|
1350
|
+
const tokens = asm.trim().split(/\s+/).filter(Boolean);
|
|
1351
|
+
const out = [];
|
|
1352
|
+
for (const token of tokens) {
|
|
1353
|
+
if (token === "OP_0" || token === "OP_FALSE") {
|
|
1354
|
+
out.push(0);
|
|
1355
|
+
continue;
|
|
1189
1356
|
}
|
|
1190
|
-
if (
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
const formatted = Bitset.range(notRead).map(({ pos, length }) => `(${pos}/${length})[${base.hex.encode(this.data.subarray(pos, pos + length))}]`).join(", ");
|
|
1194
|
-
throw this.err(`unread byte ranges: ${formatted} (total=${this.data.length})`);
|
|
1195
|
-
} else
|
|
1196
|
-
return;
|
|
1357
|
+
if (token === "OP_TRUE") {
|
|
1358
|
+
out.push(1);
|
|
1359
|
+
continue;
|
|
1197
1360
|
}
|
|
1198
|
-
|
|
1199
|
-
|
|
1361
|
+
const numMatch = token.match(/^OP_(\d+)$/);
|
|
1362
|
+
if (numMatch) {
|
|
1363
|
+
const n = parseInt(numMatch[1], 10);
|
|
1364
|
+
if (n >= 1 && n <= 16) {
|
|
1365
|
+
out.push(n);
|
|
1366
|
+
continue;
|
|
1367
|
+
}
|
|
1200
1368
|
}
|
|
1369
|
+
let key;
|
|
1370
|
+
if (token.startsWith("OP_")) {
|
|
1371
|
+
key = token.slice(3);
|
|
1372
|
+
} else {
|
|
1373
|
+
key = token;
|
|
1374
|
+
}
|
|
1375
|
+
if (key in ARKADE_OPS) {
|
|
1376
|
+
out.push(key);
|
|
1377
|
+
continue;
|
|
1378
|
+
}
|
|
1379
|
+
if (/^[0-9a-fA-F]+$/.test(token) && token.length % 2 === 0) {
|
|
1380
|
+
try {
|
|
1381
|
+
out.push(base.hex.decode(token));
|
|
1382
|
+
continue;
|
|
1383
|
+
} catch {
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
throw new Error(`Invalid ASM token: ${token}`);
|
|
1201
1387
|
}
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
const slice = this.data.subarray(this.pos, this.pos + n);
|
|
1219
|
-
if (!peek)
|
|
1220
|
-
this.markBytes(n);
|
|
1221
|
-
return slice;
|
|
1222
|
-
}
|
|
1223
|
-
byte(peek = false) {
|
|
1224
|
-
if (this.bitPos)
|
|
1225
|
-
throw this.err("readByte: bitPos not empty");
|
|
1226
|
-
if (this.pos + 1 > this.data.length)
|
|
1227
|
-
throw this.err("readBytes: Unexpected end of buffer");
|
|
1228
|
-
const data = this.data[this.pos];
|
|
1229
|
-
if (!peek)
|
|
1230
|
-
this.markBytes(1);
|
|
1231
|
-
return data;
|
|
1232
|
-
}
|
|
1233
|
-
get leftBytes() {
|
|
1234
|
-
return this.data.length - this.pos;
|
|
1235
|
-
}
|
|
1236
|
-
get totalBytes() {
|
|
1237
|
-
return this.data.length;
|
|
1238
|
-
}
|
|
1239
|
-
isEnd() {
|
|
1240
|
-
return this.pos >= this.data.length && !this.bitPos;
|
|
1388
|
+
return out;
|
|
1389
|
+
}
|
|
1390
|
+
function asmToBytes(asm) {
|
|
1391
|
+
return ArkadeScript.encode(fromASM(asm));
|
|
1392
|
+
}
|
|
1393
|
+
function bytesToASM(script) {
|
|
1394
|
+
return toASM(ArkadeScript.decode(script));
|
|
1395
|
+
}
|
|
1396
|
+
var TAG_SCRIPT = "ArkScriptHash";
|
|
1397
|
+
var TAG_WITNESS = "ArkWitnessHash";
|
|
1398
|
+
function arkadeScriptHash(script) {
|
|
1399
|
+
return secp256k1_js.schnorr.utils.taggedHash(TAG_SCRIPT, script);
|
|
1400
|
+
}
|
|
1401
|
+
function arkadeWitnessHash(witness) {
|
|
1402
|
+
if (witness.length === 0) {
|
|
1403
|
+
return new Uint8Array(32);
|
|
1241
1404
|
}
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1405
|
+
return secp256k1_js.schnorr.utils.taggedHash(TAG_WITNESS, witness);
|
|
1406
|
+
}
|
|
1407
|
+
function computeArkadeScriptPublicKey(pubKey, script) {
|
|
1408
|
+
const hash = arkadeScriptHash(script);
|
|
1409
|
+
const xOnly = pubKey.length === 33 ? pubKey.subarray(1) : pubKey;
|
|
1410
|
+
const point = secp256k1_js.schnorr.utils.lift_x(utils_js.bytesToNumberBE(xOnly));
|
|
1411
|
+
const scalar = utils_js.bytesToNumberBE(hash) % secp256k1_js.secp256k1.Point.CURVE().n;
|
|
1412
|
+
const tweak = secp256k1_js.secp256k1.Point.BASE.multiply(scalar);
|
|
1413
|
+
return secp256k1_js.schnorr.utils.pointToBytes(point.add(tweak));
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
// src/script/vhtlc.ts
|
|
1417
|
+
exports.VHTLC = void 0;
|
|
1418
|
+
((VHTLC2) => {
|
|
1419
|
+
class BaseScript extends chunk4A4GRSTB_cjs.VtxoScript {
|
|
1420
|
+
options;
|
|
1421
|
+
claimScript;
|
|
1422
|
+
refundScript;
|
|
1423
|
+
refundWithoutReceiverScript;
|
|
1424
|
+
unilateralClaimScript;
|
|
1425
|
+
unilateralRefundScript;
|
|
1426
|
+
unilateralRefundWithoutReceiverScript;
|
|
1427
|
+
nonInteractiveClaimScript;
|
|
1428
|
+
nonInteractiveClaimArkadeScript;
|
|
1429
|
+
nonInteractiveRefundScript;
|
|
1430
|
+
nonInteractiveRefundArkadeScript;
|
|
1431
|
+
constructor(options, preimageCondition) {
|
|
1432
|
+
validateOptions(options);
|
|
1433
|
+
const {
|
|
1434
|
+
sender,
|
|
1435
|
+
receiver,
|
|
1436
|
+
server,
|
|
1437
|
+
preimageHash,
|
|
1438
|
+
refundLocktime,
|
|
1439
|
+
unilateralClaimDelay,
|
|
1440
|
+
unilateralRefundDelay,
|
|
1441
|
+
unilateralRefundWithoutReceiverDelay
|
|
1442
|
+
} = options;
|
|
1443
|
+
const conditionScript = preimageCondition(preimageHash);
|
|
1444
|
+
const claimScript = chunk4A4GRSTB_cjs.ConditionMultisigTapscript.encode({
|
|
1445
|
+
conditionScript,
|
|
1446
|
+
pubkeys: [receiver, server]
|
|
1447
|
+
}).script;
|
|
1448
|
+
const refundScript = chunk4A4GRSTB_cjs.MultisigTapscript.encode({
|
|
1449
|
+
pubkeys: [sender, receiver, server]
|
|
1450
|
+
}).script;
|
|
1451
|
+
const refundWithoutReceiverScript = chunk4A4GRSTB_cjs.CLTVMultisigTapscript.encode({
|
|
1452
|
+
absoluteTimelock: refundLocktime,
|
|
1453
|
+
pubkeys: [sender, server]
|
|
1454
|
+
}).script;
|
|
1455
|
+
const unilateralClaimScript = chunk4A4GRSTB_cjs.ConditionCSVMultisigTapscript.encode({
|
|
1456
|
+
conditionScript,
|
|
1457
|
+
timelock: unilateralClaimDelay,
|
|
1458
|
+
pubkeys: [receiver]
|
|
1459
|
+
}).script;
|
|
1460
|
+
const unilateralRefundScript = chunk4A4GRSTB_cjs.CSVMultisigTapscript.encode({
|
|
1461
|
+
timelock: unilateralRefundDelay,
|
|
1462
|
+
pubkeys: [sender, receiver]
|
|
1463
|
+
}).script;
|
|
1464
|
+
const unilateralRefundWithoutReceiverScript = chunk4A4GRSTB_cjs.CSVMultisigTapscript.encode({
|
|
1465
|
+
timelock: unilateralRefundWithoutReceiverDelay,
|
|
1466
|
+
pubkeys: [sender]
|
|
1467
|
+
}).script;
|
|
1468
|
+
const scripts = [
|
|
1469
|
+
claimScript,
|
|
1470
|
+
refundScript,
|
|
1471
|
+
refundWithoutReceiverScript,
|
|
1472
|
+
unilateralClaimScript,
|
|
1473
|
+
unilateralRefundScript,
|
|
1474
|
+
unilateralRefundWithoutReceiverScript
|
|
1475
|
+
];
|
|
1476
|
+
let arkadeScriptNic;
|
|
1477
|
+
let nonInteractiveClaimScript;
|
|
1478
|
+
if (options.nonInteractiveClaim) {
|
|
1479
|
+
arkadeScriptNic = enforcePayTo(options.nonInteractiveClaim.receiverPkScript);
|
|
1480
|
+
nonInteractiveClaimScript = chunk4A4GRSTB_cjs.ConditionMultisigTapscript.encode({
|
|
1481
|
+
conditionScript,
|
|
1482
|
+
pubkeys: [
|
|
1483
|
+
server,
|
|
1484
|
+
computeArkadeScriptPublicKey(
|
|
1485
|
+
options.nonInteractiveClaim.emulatorPubkey,
|
|
1486
|
+
arkadeScriptNic
|
|
1487
|
+
)
|
|
1488
|
+
]
|
|
1489
|
+
}).script;
|
|
1490
|
+
scripts.push(nonInteractiveClaimScript);
|
|
1491
|
+
}
|
|
1492
|
+
let arkadeScriptNir;
|
|
1493
|
+
let nonInteractiveRefundScript;
|
|
1494
|
+
if (options.nonInteractiveRefund) {
|
|
1495
|
+
arkadeScriptNir = enforcePayTo(options.nonInteractiveRefund.senderPkScript);
|
|
1496
|
+
nonInteractiveRefundScript = chunk4A4GRSTB_cjs.MultisigTapscript.encode({
|
|
1497
|
+
pubkeys: [
|
|
1498
|
+
server,
|
|
1499
|
+
receiver,
|
|
1500
|
+
computeArkadeScriptPublicKey(
|
|
1501
|
+
options.nonInteractiveRefund.emulatorPubkey,
|
|
1502
|
+
arkadeScriptNir
|
|
1503
|
+
)
|
|
1504
|
+
]
|
|
1505
|
+
}).script;
|
|
1506
|
+
scripts.push(nonInteractiveRefundScript);
|
|
1507
|
+
}
|
|
1508
|
+
super(scripts);
|
|
1509
|
+
this.options = options;
|
|
1510
|
+
this.claimScript = base.hex.encode(claimScript);
|
|
1511
|
+
this.refundScript = base.hex.encode(refundScript);
|
|
1512
|
+
this.refundWithoutReceiverScript = base.hex.encode(refundWithoutReceiverScript);
|
|
1513
|
+
this.unilateralClaimScript = base.hex.encode(unilateralClaimScript);
|
|
1514
|
+
this.unilateralRefundScript = base.hex.encode(unilateralRefundScript);
|
|
1515
|
+
this.unilateralRefundWithoutReceiverScript = base.hex.encode(
|
|
1516
|
+
unilateralRefundWithoutReceiverScript
|
|
1517
|
+
);
|
|
1518
|
+
if (nonInteractiveClaimScript) {
|
|
1519
|
+
this.nonInteractiveClaimScript = base.hex.encode(nonInteractiveClaimScript);
|
|
1520
|
+
this.nonInteractiveClaimArkadeScript = arkadeScriptNic;
|
|
1521
|
+
}
|
|
1522
|
+
if (nonInteractiveRefundScript) {
|
|
1523
|
+
this.nonInteractiveRefundScript = base.hex.encode(nonInteractiveRefundScript);
|
|
1524
|
+
this.nonInteractiveRefundArkadeScript = arkadeScriptNir;
|
|
1251
1525
|
}
|
|
1252
|
-
const take = Math.min(bits, this.bitPos);
|
|
1253
|
-
this.bitPos -= take;
|
|
1254
|
-
out = out << take | this.bitBuf >> this.bitPos & 2 ** take - 1;
|
|
1255
|
-
this.bitBuf &= 2 ** this.bitPos - 1;
|
|
1256
|
-
bits -= take;
|
|
1257
1526
|
}
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1527
|
+
/** Return the collaborative claim tapleaf script. */
|
|
1528
|
+
claim() {
|
|
1529
|
+
return this.findLeaf(this.claimScript);
|
|
1530
|
+
}
|
|
1531
|
+
/** Return the collaborative refund tapleaf script. */
|
|
1532
|
+
refund() {
|
|
1533
|
+
return this.findLeaf(this.refundScript);
|
|
1534
|
+
}
|
|
1535
|
+
/** Return the refund-without-receiver tapleaf script. */
|
|
1536
|
+
refundWithoutReceiver() {
|
|
1537
|
+
return this.findLeaf(this.refundWithoutReceiverScript);
|
|
1538
|
+
}
|
|
1539
|
+
/** Return the unilateral claim tapleaf script. */
|
|
1540
|
+
unilateralClaim() {
|
|
1541
|
+
return this.findLeaf(this.unilateralClaimScript);
|
|
1542
|
+
}
|
|
1543
|
+
/** Return the unilateral refund tapleaf script. */
|
|
1544
|
+
unilateralRefund() {
|
|
1545
|
+
return this.findLeaf(this.unilateralRefundScript);
|
|
1546
|
+
}
|
|
1547
|
+
/** Return the unilateral refund-without-receiver tapleaf script. */
|
|
1548
|
+
unilateralRefundWithoutReceiver() {
|
|
1549
|
+
return this.findLeaf(this.unilateralRefundWithoutReceiverScript);
|
|
1550
|
+
}
|
|
1551
|
+
/** Return the non-interactive claim tapleaf script as well as the ArkadeScript. */
|
|
1552
|
+
nonInteractiveClaim() {
|
|
1553
|
+
if (!this.nonInteractiveClaimScript || !this.nonInteractiveClaimArkadeScript) {
|
|
1554
|
+
throw new Error("VHTLC has no non-interactive claim leaf");
|
|
1555
|
+
}
|
|
1556
|
+
return [
|
|
1557
|
+
this.findLeaf(this.nonInteractiveClaimScript),
|
|
1558
|
+
this.nonInteractiveClaimArkadeScript
|
|
1559
|
+
];
|
|
1560
|
+
}
|
|
1561
|
+
/** Return the non-interactive refund tapleaf script as well as the ArkadeScript. */
|
|
1562
|
+
nonInteractiveRefund() {
|
|
1563
|
+
if (!this.nonInteractiveRefundScript || !this.nonInteractiveRefundArkadeScript) {
|
|
1564
|
+
throw new Error("VHTLC has no non-interactive refund leaf");
|
|
1565
|
+
}
|
|
1566
|
+
return [
|
|
1567
|
+
this.findLeaf(this.nonInteractiveRefundScript),
|
|
1568
|
+
this.nonInteractiveRefundArkadeScript
|
|
1569
|
+
];
|
|
1275
1570
|
}
|
|
1276
|
-
return;
|
|
1277
|
-
}
|
|
1278
|
-
};
|
|
1279
|
-
var _Writer = class {
|
|
1280
|
-
pos = 0;
|
|
1281
|
-
stack;
|
|
1282
|
-
// We could have a single buffer here and re-alloc it with
|
|
1283
|
-
// x1.5-2 size each time it full, but it will be slower:
|
|
1284
|
-
// basic/encode bench: 395ns -> 560ns
|
|
1285
|
-
buffers = [];
|
|
1286
|
-
ptrs = [];
|
|
1287
|
-
bitBuf = 0;
|
|
1288
|
-
bitPos = 0;
|
|
1289
|
-
viewBuf = new Uint8Array(8);
|
|
1290
|
-
view;
|
|
1291
|
-
finished = false;
|
|
1292
|
-
constructor(stack = []) {
|
|
1293
|
-
this.stack = stack;
|
|
1294
|
-
this.view = createView(this.viewBuf);
|
|
1295
|
-
}
|
|
1296
|
-
pushObj(obj, objFn) {
|
|
1297
|
-
return Path.pushObj(this.stack, obj, objFn);
|
|
1298
|
-
}
|
|
1299
|
-
writeView(len, fn) {
|
|
1300
|
-
if (this.finished)
|
|
1301
|
-
throw this.err("buffer: finished");
|
|
1302
|
-
if (!isNum(len) || len > 8)
|
|
1303
|
-
throw new Error(`wrong writeView length=${len}`);
|
|
1304
|
-
fn(this.view);
|
|
1305
|
-
this.bytes(this.viewBuf.slice(0, len));
|
|
1306
|
-
this.viewBuf.fill(0);
|
|
1307
1571
|
}
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
return Path.err("Reader", this.stack, msg);
|
|
1572
|
+
class Script3 extends BaseScript {
|
|
1573
|
+
constructor(options) {
|
|
1574
|
+
super(options, preimageConditionScript);
|
|
1575
|
+
}
|
|
1313
1576
|
}
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
this.buffers.push(b);
|
|
1320
|
-
this.pos += b.length;
|
|
1577
|
+
VHTLC2.Script = Script3;
|
|
1578
|
+
class ScriptV2 extends BaseScript {
|
|
1579
|
+
constructor(options) {
|
|
1580
|
+
super(options, preimageConditionScriptV2);
|
|
1581
|
+
}
|
|
1321
1582
|
}
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1583
|
+
VHTLC2.ScriptV2 = ScriptV2;
|
|
1584
|
+
function validateOptions(options) {
|
|
1585
|
+
const {
|
|
1586
|
+
sender,
|
|
1587
|
+
receiver,
|
|
1588
|
+
server,
|
|
1589
|
+
preimageHash,
|
|
1590
|
+
refundLocktime,
|
|
1591
|
+
unilateralClaimDelay,
|
|
1592
|
+
unilateralRefundDelay,
|
|
1593
|
+
unilateralRefundWithoutReceiverDelay
|
|
1594
|
+
} = options;
|
|
1595
|
+
if (!preimageHash || preimageHash.length !== 20) {
|
|
1596
|
+
throw new Error("preimage hash must be 20 bytes");
|
|
1597
|
+
}
|
|
1598
|
+
if (options.nonInteractiveClaim) {
|
|
1599
|
+
const { emulatorPubkey, receiverPkScript } = options.nonInteractiveClaim;
|
|
1600
|
+
if (!emulatorPubkey || emulatorPubkey.length !== 32 && emulatorPubkey.length !== 33) {
|
|
1601
|
+
throw new Error("Invalid public key length (emulator)");
|
|
1602
|
+
}
|
|
1603
|
+
if (!receiverPkScript || !isP2trPkScript(receiverPkScript)) {
|
|
1604
|
+
throw new Error("Invalid P2TR script");
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
if (options.nonInteractiveRefund) {
|
|
1608
|
+
const { emulatorPubkey, senderPkScript } = options.nonInteractiveRefund;
|
|
1609
|
+
if (!emulatorPubkey || emulatorPubkey.length !== 32 && emulatorPubkey.length !== 33) {
|
|
1610
|
+
throw new Error("Invalid public key length (emulator)");
|
|
1611
|
+
}
|
|
1612
|
+
if (!senderPkScript || !isP2trPkScript(senderPkScript)) {
|
|
1613
|
+
throw new Error("Invalid P2TR script");
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
if (!receiver || receiver.length !== 32) {
|
|
1617
|
+
throw new Error("Invalid public key length (receiver)");
|
|
1618
|
+
}
|
|
1619
|
+
if (!sender || sender.length !== 32) {
|
|
1620
|
+
throw new Error("Invalid public key length (sender)");
|
|
1621
|
+
}
|
|
1622
|
+
if (!server || server.length !== 32) {
|
|
1623
|
+
throw new Error("Invalid public key length (server)");
|
|
1624
|
+
}
|
|
1625
|
+
if (typeof refundLocktime !== "bigint" || refundLocktime <= 0n) {
|
|
1626
|
+
throw new Error("refund locktime must be greater than 0");
|
|
1627
|
+
}
|
|
1628
|
+
if (!unilateralClaimDelay || typeof unilateralClaimDelay.value !== "bigint" || unilateralClaimDelay.value <= 0n) {
|
|
1629
|
+
throw new Error("unilateral claim delay must greater than 0");
|
|
1630
|
+
}
|
|
1631
|
+
if (unilateralClaimDelay.type === "seconds" && unilateralClaimDelay.value % 512n !== 0n) {
|
|
1632
|
+
throw new Error("seconds timelock must be multiple of 512");
|
|
1633
|
+
}
|
|
1634
|
+
if (unilateralClaimDelay.type === "seconds" && unilateralClaimDelay.value < 512n) {
|
|
1635
|
+
throw new Error("seconds timelock must be greater or equal to 512");
|
|
1636
|
+
}
|
|
1637
|
+
if (!unilateralRefundDelay || typeof unilateralRefundDelay.value !== "bigint" || unilateralRefundDelay.value <= 0n) {
|
|
1638
|
+
throw new Error("unilateral refund delay must greater than 0");
|
|
1639
|
+
}
|
|
1640
|
+
if (unilateralRefundDelay.type === "seconds" && unilateralRefundDelay.value % 512n !== 0n) {
|
|
1641
|
+
throw new Error("seconds timelock must be multiple of 512");
|
|
1642
|
+
}
|
|
1643
|
+
if (unilateralRefundDelay.type === "seconds" && unilateralRefundDelay.value < 512n) {
|
|
1644
|
+
throw new Error("seconds timelock must be greater or equal to 512");
|
|
1645
|
+
}
|
|
1646
|
+
if (!unilateralRefundWithoutReceiverDelay || typeof unilateralRefundWithoutReceiverDelay.value !== "bigint" || unilateralRefundWithoutReceiverDelay.value <= 0n) {
|
|
1647
|
+
throw new Error("unilateral refund without receiver delay must greater than 0");
|
|
1648
|
+
}
|
|
1649
|
+
if (unilateralRefundWithoutReceiverDelay.type === "seconds" && unilateralRefundWithoutReceiverDelay.value % 512n !== 0n) {
|
|
1650
|
+
throw new Error("seconds timelock must be multiple of 512");
|
|
1651
|
+
}
|
|
1652
|
+
if (unilateralRefundWithoutReceiverDelay.type === "seconds" && unilateralRefundWithoutReceiverDelay.value < 512n) {
|
|
1653
|
+
throw new Error("seconds timelock must be greater or equal to 512");
|
|
1654
|
+
}
|
|
1329
1655
|
}
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1656
|
+
})(exports.VHTLC || (exports.VHTLC = {}));
|
|
1657
|
+
function preimageConditionScript(preimageHash) {
|
|
1658
|
+
return btcSigner.Script.encode(["HASH160", preimageHash, "EQUAL"]);
|
|
1659
|
+
}
|
|
1660
|
+
function preimageConditionScriptV2(preimageHash) {
|
|
1661
|
+
return btcSigner.Script.encode(["SIZE", 32, "EQUALVERIFY", "HASH160", preimageHash, "EQUAL"]);
|
|
1662
|
+
}
|
|
1663
|
+
function isP2trPkScript(pkScript) {
|
|
1664
|
+
return pkScript.length === 34 && pkScript[0] === 81 && pkScript[1] === 32;
|
|
1665
|
+
}
|
|
1666
|
+
function enforcePayTo(destinationPkScript) {
|
|
1667
|
+
if (!isP2trPkScript(destinationPkScript)) {
|
|
1668
|
+
throw new Error("invalid P2TR script");
|
|
1669
|
+
}
|
|
1670
|
+
return ArkadeScript.encode([
|
|
1671
|
+
"PUSHCURRENTINPUTINDEX",
|
|
1672
|
+
"DUP",
|
|
1673
|
+
"INSPECTOUTPUTSCRIPTPUBKEY",
|
|
1674
|
+
1,
|
|
1675
|
+
"EQUALVERIFY",
|
|
1676
|
+
destinationPkScript.subarray(2),
|
|
1677
|
+
"EQUALVERIFY",
|
|
1678
|
+
"INSPECTOUTPUTVALUE",
|
|
1679
|
+
"PUSHCURRENTINPUTINDEX",
|
|
1680
|
+
"INSPECTINPUTVALUE",
|
|
1681
|
+
"GREATERTHANOREQUAL"
|
|
1682
|
+
]);
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
// src/contracts/handlers/vhtlc.ts
|
|
1686
|
+
var VHTLCContractHandler = {
|
|
1687
|
+
type: "vhtlc",
|
|
1688
|
+
createScript(params) {
|
|
1689
|
+
const typed = this.deserializeParams(params);
|
|
1690
|
+
return new exports.VHTLC.Script(typed);
|
|
1691
|
+
},
|
|
1692
|
+
serializeParams(params) {
|
|
1693
|
+
return {
|
|
1694
|
+
sender: base.hex.encode(params.sender),
|
|
1695
|
+
receiver: base.hex.encode(params.receiver),
|
|
1696
|
+
server: base.hex.encode(params.server),
|
|
1697
|
+
hash: base.hex.encode(params.preimageHash),
|
|
1698
|
+
refundLocktime: params.refundLocktime.toString(),
|
|
1699
|
+
claimDelay: chunk4A4GRSTB_cjs.timelockToSequence(params.unilateralClaimDelay).toString(),
|
|
1700
|
+
refundDelay: chunk4A4GRSTB_cjs.timelockToSequence(params.unilateralRefundDelay).toString(),
|
|
1701
|
+
refundNoReceiverDelay: chunk4A4GRSTB_cjs.timelockToSequence(
|
|
1702
|
+
params.unilateralRefundWithoutReceiverDelay
|
|
1703
|
+
).toString()
|
|
1704
|
+
};
|
|
1705
|
+
},
|
|
1706
|
+
deserializeParams(params) {
|
|
1707
|
+
return {
|
|
1708
|
+
sender: base.hex.decode(params.sender),
|
|
1709
|
+
receiver: base.hex.decode(params.receiver),
|
|
1710
|
+
server: base.hex.decode(params.server),
|
|
1711
|
+
preimageHash: base.hex.decode(params.hash),
|
|
1712
|
+
refundLocktime: BigInt(params.refundLocktime),
|
|
1713
|
+
unilateralClaimDelay: chunk4A4GRSTB_cjs.sequenceToTimelock(Number(params.claimDelay)),
|
|
1714
|
+
unilateralRefundDelay: chunk4A4GRSTB_cjs.sequenceToTimelock(Number(params.refundDelay)),
|
|
1715
|
+
unilateralRefundWithoutReceiverDelay: chunk4A4GRSTB_cjs.sequenceToTimelock(
|
|
1716
|
+
Number(params.refundNoReceiverDelay)
|
|
1717
|
+
)
|
|
1718
|
+
};
|
|
1719
|
+
},
|
|
1720
|
+
/**
|
|
1721
|
+
* Select spending path based on context.
|
|
1722
|
+
*
|
|
1723
|
+
* Role is determined from `context.role` or by matching
|
|
1724
|
+
* `context.walletDescriptor` (preferred) / `context.walletPubKey`
|
|
1725
|
+
* against sender/receiver in contract params.
|
|
1726
|
+
*/
|
|
1727
|
+
selectPath(script, contract, context) {
|
|
1728
|
+
const role = resolveRole(contract, context);
|
|
1729
|
+
const preimage = contract.params?.preimage;
|
|
1730
|
+
const refundLocktime = BigInt(contract.params.refundLocktime);
|
|
1731
|
+
if (!role) {
|
|
1732
|
+
return null;
|
|
1342
1733
|
}
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1734
|
+
if (context.collaborative) {
|
|
1735
|
+
if (role === "receiver" && preimage) {
|
|
1736
|
+
return {
|
|
1737
|
+
leaf: script.claim(),
|
|
1738
|
+
extraWitness: [base.hex.decode(preimage)]
|
|
1739
|
+
};
|
|
1740
|
+
}
|
|
1741
|
+
if (role === "sender" && isCltvSatisfied(context, refundLocktime)) {
|
|
1742
|
+
return {
|
|
1743
|
+
leaf: script.refundWithoutReceiver()
|
|
1744
|
+
};
|
|
1745
|
+
}
|
|
1746
|
+
return null;
|
|
1347
1747
|
}
|
|
1348
|
-
if (
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1748
|
+
if (role === "receiver" && preimage) {
|
|
1749
|
+
const sequence = Number(contract.params.claimDelay);
|
|
1750
|
+
if (!isCsvSpendable(context, sequence)) return null;
|
|
1751
|
+
return {
|
|
1752
|
+
leaf: script.unilateralClaim(),
|
|
1753
|
+
extraWitness: [base.hex.decode(preimage)],
|
|
1754
|
+
sequence
|
|
1755
|
+
};
|
|
1355
1756
|
}
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
while (bits) {
|
|
1364
|
-
const take = Math.min(bits, 8 - this.bitPos);
|
|
1365
|
-
this.bitBuf = this.bitBuf << take | value >> bits - take;
|
|
1366
|
-
this.bitPos += take;
|
|
1367
|
-
bits -= take;
|
|
1368
|
-
value &= 2 ** bits - 1;
|
|
1369
|
-
if (this.bitPos === 8) {
|
|
1370
|
-
this.bitPos = 0;
|
|
1371
|
-
this.buffers.push(new Uint8Array([this.bitBuf]));
|
|
1372
|
-
this.pos++;
|
|
1373
|
-
}
|
|
1757
|
+
if (role === "sender") {
|
|
1758
|
+
const sequence = Number(contract.params.refundNoReceiverDelay);
|
|
1759
|
+
if (!isCsvSpendable(context, sequence)) return null;
|
|
1760
|
+
return {
|
|
1761
|
+
leaf: script.unilateralRefundWithoutReceiver(),
|
|
1762
|
+
sequence
|
|
1763
|
+
};
|
|
1374
1764
|
}
|
|
1375
|
-
|
|
1376
|
-
}
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
const r = new _Reader(data, opts);
|
|
1390
|
-
const res = inner.decodeStream(r);
|
|
1391
|
-
r.finish();
|
|
1392
|
-
return res;
|
|
1765
|
+
return null;
|
|
1766
|
+
},
|
|
1767
|
+
/**
|
|
1768
|
+
* Get all possible spending paths (no timelock checks).
|
|
1769
|
+
*
|
|
1770
|
+
* Role is determined from `context.role` or by matching
|
|
1771
|
+
* `context.walletDescriptor` (preferred) / `context.walletPubKey`
|
|
1772
|
+
* against sender/receiver in contract params.
|
|
1773
|
+
*/
|
|
1774
|
+
getAllSpendingPaths(script, contract, context) {
|
|
1775
|
+
const role = resolveRole(contract, context);
|
|
1776
|
+
const paths = [];
|
|
1777
|
+
if (!role) {
|
|
1778
|
+
return paths;
|
|
1393
1779
|
}
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
return _wrap({
|
|
1402
|
-
size: inner.size,
|
|
1403
|
-
encodeStream: (w, value) => {
|
|
1404
|
-
let res;
|
|
1405
|
-
try {
|
|
1406
|
-
res = fn(value);
|
|
1407
|
-
} catch (e) {
|
|
1408
|
-
throw w.err(e);
|
|
1780
|
+
const preimage = contract.params?.preimage;
|
|
1781
|
+
if (context.collaborative) {
|
|
1782
|
+
if (role === "receiver" && preimage) {
|
|
1783
|
+
paths.push({
|
|
1784
|
+
leaf: script.claim(),
|
|
1785
|
+
extraWitness: [base.hex.decode(preimage)]
|
|
1786
|
+
});
|
|
1409
1787
|
}
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1788
|
+
if (role === "sender") {
|
|
1789
|
+
paths.push({
|
|
1790
|
+
leaf: script.refundWithoutReceiver()
|
|
1791
|
+
});
|
|
1792
|
+
}
|
|
1793
|
+
} else {
|
|
1794
|
+
if (role === "receiver" && preimage) {
|
|
1795
|
+
const sequence = Number(contract.params.claimDelay);
|
|
1796
|
+
paths.push({
|
|
1797
|
+
leaf: script.unilateralClaim(),
|
|
1798
|
+
extraWitness: [base.hex.decode(preimage)],
|
|
1799
|
+
sequence
|
|
1800
|
+
});
|
|
1801
|
+
}
|
|
1802
|
+
if (role === "sender") {
|
|
1803
|
+
const sequence = Number(contract.params.refundNoReceiverDelay);
|
|
1804
|
+
paths.push({
|
|
1805
|
+
leaf: script.unilateralRefundWithoutReceiver(),
|
|
1806
|
+
sequence
|
|
1807
|
+
});
|
|
1418
1808
|
}
|
|
1419
1809
|
}
|
|
1420
|
-
|
|
1421
|
-
}
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
function isCoder(elm) {
|
|
1428
|
-
return isPlainObject(elm) && isBaseCoder(elm) && typeof elm.encodeStream === "function" && typeof elm.decodeStream === "function" && (elm.size === void 0 || isNum(elm.size));
|
|
1429
|
-
}
|
|
1430
|
-
var view = (len, opts) => wrap({
|
|
1431
|
-
size: len,
|
|
1432
|
-
encodeStream: (w, value) => w.writeView(len, (view2) => opts.write(view2, value)),
|
|
1433
|
-
decodeStream: (r) => r.readView(len, opts.read),
|
|
1434
|
-
validate: (value) => {
|
|
1435
|
-
if (typeof value !== "number")
|
|
1436
|
-
throw new Error(`viewCoder: expected number, got ${typeof value}`);
|
|
1437
|
-
if (opts.validate)
|
|
1438
|
-
opts.validate(value);
|
|
1439
|
-
return value;
|
|
1440
|
-
}
|
|
1441
|
-
});
|
|
1442
|
-
var intView = (len, signed, opts) => {
|
|
1443
|
-
const bits = len * 8;
|
|
1444
|
-
const maxVal = 2 ** bits;
|
|
1445
|
-
const validateUnsigned = (value) => {
|
|
1446
|
-
if (!isNum(value))
|
|
1447
|
-
throw new Error(`uintView: value is not safe integer: ${value}`);
|
|
1448
|
-
if (0 > value || value >= maxVal) {
|
|
1449
|
-
throw new Error(`uintView: value out of bounds. Expected 0 <= ${value} < ${maxVal}`);
|
|
1810
|
+
return paths;
|
|
1811
|
+
},
|
|
1812
|
+
getSpendablePaths(script, contract, context) {
|
|
1813
|
+
const role = resolveRole(contract, context);
|
|
1814
|
+
const paths = [];
|
|
1815
|
+
if (!role) {
|
|
1816
|
+
return paths;
|
|
1450
1817
|
}
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
var U8 = /* @__PURE__ */ intView(1, false, {
|
|
1467
|
-
read: (view2, pos) => view2.getUint8(pos),
|
|
1468
|
-
write: (view2, value) => view2.setUint8(0, value)
|
|
1469
|
-
});
|
|
1470
|
-
var ARKADE_OP = {
|
|
1471
|
-
// Merkle Branch Verification (0xb3 — repurposed NOP4 slot)
|
|
1472
|
-
MERKLEBRANCHVERIFY: 179,
|
|
1473
|
-
// Digest (0xc3)
|
|
1474
|
-
DIGEST: 195,
|
|
1475
|
-
// SHA256 Streaming (0xc4-0xc6)
|
|
1476
|
-
SHA256INITIALIZE: 196,
|
|
1477
|
-
SHA256UPDATE: 197,
|
|
1478
|
-
SHA256FINALIZE: 198,
|
|
1479
|
-
// Input Introspection (0xc7-0xcb)
|
|
1480
|
-
INSPECTINPUTOUTPOINT: 199,
|
|
1481
|
-
INSPECTINPUTARKADESCRIPTHASH: 200,
|
|
1482
|
-
INSPECTINPUTVALUE: 201,
|
|
1483
|
-
INSPECTINPUTSCRIPTPUBKEY: 202,
|
|
1484
|
-
INSPECTINPUTSEQUENCE: 203,
|
|
1485
|
-
// Signatures (0xcc-0xcd)
|
|
1486
|
-
CHECKSIGFROMSTACK: 204,
|
|
1487
|
-
PUSHCURRENTINPUTINDEX: 205,
|
|
1488
|
-
// Input Arkade Witness Introspection (0xce)
|
|
1489
|
-
INSPECTINPUTARKADEWITNESSHASH: 206,
|
|
1490
|
-
// Output Introspection (0xcf, 0xd1)
|
|
1491
|
-
INSPECTOUTPUTVALUE: 207,
|
|
1492
|
-
INSPECTOUTPUTSCRIPTPUBKEY: 209,
|
|
1493
|
-
// Transaction Introspection (0xd2-0xd6)
|
|
1494
|
-
INSPECTVERSION: 210,
|
|
1495
|
-
INSPECTLOCKTIME: 211,
|
|
1496
|
-
INSPECTNUMINPUTS: 212,
|
|
1497
|
-
INSPECTNUMOUTPUTS: 213,
|
|
1498
|
-
TXWEIGHT: 214,
|
|
1499
|
-
// Byte/Number Conversion & Arithmetic (0xd7-0xda)
|
|
1500
|
-
NUM2BIN: 215,
|
|
1501
|
-
BIN2NUM: 216,
|
|
1502
|
-
REVERSEBYTES: 217,
|
|
1503
|
-
MODEXP: 218,
|
|
1504
|
-
// 0xdb-0xdf are unassigned (OP_UNKNOWN219-223)
|
|
1505
|
-
// EC Operations (0xe0-0xe2)
|
|
1506
|
-
ECADD: 224,
|
|
1507
|
-
ECMUL: 225,
|
|
1508
|
-
ECPAIRING: 226,
|
|
1509
|
-
// EC Operations (0xe3-0xe4)
|
|
1510
|
-
ECMULSCALARVERIFY: 227,
|
|
1511
|
-
TWEAKVERIFY: 228,
|
|
1512
|
-
// Asset Groups (0xe5-0xf2)
|
|
1513
|
-
INSPECTNUMASSETGROUPS: 229,
|
|
1514
|
-
INSPECTASSETGROUPASSETID: 230,
|
|
1515
|
-
INSPECTASSETGROUPCTRL: 231,
|
|
1516
|
-
FINDASSETGROUPBYASSETID: 232,
|
|
1517
|
-
INSPECTASSETGROUPMETADATAHASH: 233,
|
|
1518
|
-
INSPECTASSETGROUPNUM: 234,
|
|
1519
|
-
INSPECTASSETGROUP: 235,
|
|
1520
|
-
INSPECTASSETGROUPSUM: 236,
|
|
1521
|
-
INSPECTOUTASSETCOUNT: 237,
|
|
1522
|
-
INSPECTOUTASSETAT: 238,
|
|
1523
|
-
INSPECTOUTASSETLOOKUP: 239,
|
|
1524
|
-
INSPECTINASSETCOUNT: 240,
|
|
1525
|
-
INSPECTINASSETAT: 241,
|
|
1526
|
-
INSPECTINASSETLOOKUP: 242,
|
|
1527
|
-
// Transaction ID (0xf3)
|
|
1528
|
-
TXID: 243,
|
|
1529
|
-
// Packet Introspection (0xf4-0xf5) — added in emulator v0.0.1
|
|
1530
|
-
INSPECTPACKET: 244,
|
|
1531
|
-
INSPECTINPUTPACKET: 245,
|
|
1532
|
-
// Signature Hash (0xf6)
|
|
1533
|
-
SIGHASH: 246
|
|
1534
|
-
};
|
|
1535
|
-
var ARKADE_OPCODES = Object.values(ARKADE_OP);
|
|
1536
|
-
var ARKADE_OPCODE_NAMES = Object.fromEntries(
|
|
1537
|
-
Object.entries(ARKADE_OP).map(([name, value]) => [value, name])
|
|
1538
|
-
);
|
|
1539
|
-
var ARKADE_OPCODE_VALUES = Object.fromEntries(
|
|
1540
|
-
Object.entries(ARKADE_OPCODE_NAMES).map(([value, name]) => [name, Number(value)])
|
|
1541
|
-
);
|
|
1542
|
-
function buildBitcoinOpcodeNames() {
|
|
1543
|
-
const names = {};
|
|
1544
|
-
for (const [key, value] of Object.entries(btcSigner.OP)) {
|
|
1545
|
-
if (typeof value === "number") {
|
|
1546
|
-
const name = key.startsWith("OP_") ? key : `OP_${key}`;
|
|
1547
|
-
names[value] = name;
|
|
1818
|
+
const preimage = contract.params?.preimage;
|
|
1819
|
+
const refundLocktime = BigInt(contract.params.refundLocktime);
|
|
1820
|
+
if (context.collaborative) {
|
|
1821
|
+
if (role === "receiver" && preimage) {
|
|
1822
|
+
paths.push({
|
|
1823
|
+
leaf: script.claim(),
|
|
1824
|
+
extraWitness: [base.hex.decode(preimage)]
|
|
1825
|
+
});
|
|
1826
|
+
}
|
|
1827
|
+
if (role === "sender" && isCltvSatisfied(context, refundLocktime)) {
|
|
1828
|
+
paths.push({
|
|
1829
|
+
leaf: script.refundWithoutReceiver()
|
|
1830
|
+
});
|
|
1831
|
+
}
|
|
1832
|
+
return paths;
|
|
1548
1833
|
}
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
values[name] = value;
|
|
1559
|
-
values[key] = value;
|
|
1834
|
+
if (role === "receiver" && preimage) {
|
|
1835
|
+
const sequence = Number(contract.params.claimDelay);
|
|
1836
|
+
if (isCsvSpendable(context, sequence)) {
|
|
1837
|
+
paths.push({
|
|
1838
|
+
leaf: script.unilateralClaim(),
|
|
1839
|
+
extraWitness: [base.hex.decode(preimage)],
|
|
1840
|
+
sequence
|
|
1841
|
+
});
|
|
1842
|
+
}
|
|
1560
1843
|
}
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
...Object.fromEntries(
|
|
1570
|
-
Object.entries(ARKADE_OPCODE_NAMES).map(([value, name]) => [Number(value), `OP_${name}`])
|
|
1571
|
-
)
|
|
1572
|
-
};
|
|
1573
|
-
var OPCODE_VALUES = {
|
|
1574
|
-
...BITCOIN_OPCODE_VALUES,
|
|
1575
|
-
// Add Arkade opcodes with and without OP_ prefix
|
|
1576
|
-
...ARKADE_OPCODE_VALUES,
|
|
1577
|
-
...Object.fromEntries(
|
|
1578
|
-
Object.entries(ARKADE_OPCODE_VALUES).map(([name, value]) => [`OP_${name}`, value])
|
|
1579
|
-
)
|
|
1580
|
-
};
|
|
1581
|
-
function getOpcodeName(value) {
|
|
1582
|
-
if (value >= 1 && value <= 75) {
|
|
1583
|
-
return `OP_DATA_${value}`;
|
|
1584
|
-
}
|
|
1585
|
-
return OPCODE_NAMES[value];
|
|
1586
|
-
}
|
|
1587
|
-
function getOpcodeValue(name) {
|
|
1588
|
-
const dataMatch = name.match(/^OP_DATA_(\d+)$/);
|
|
1589
|
-
if (dataMatch) {
|
|
1590
|
-
const n = parseInt(dataMatch[1], 10);
|
|
1591
|
-
if (n >= 1 && n <= 75) {
|
|
1592
|
-
return n;
|
|
1844
|
+
if (role === "sender") {
|
|
1845
|
+
const sequence = Number(contract.params.refundNoReceiverDelay);
|
|
1846
|
+
if (isCsvSpendable(context, sequence)) {
|
|
1847
|
+
paths.push({
|
|
1848
|
+
leaf: script.unilateralRefundWithoutReceiver(),
|
|
1849
|
+
sequence
|
|
1850
|
+
});
|
|
1851
|
+
}
|
|
1593
1852
|
}
|
|
1594
|
-
return
|
|
1853
|
+
return paths;
|
|
1854
|
+
},
|
|
1855
|
+
/**
|
|
1856
|
+
* Refuse an explicit spend of a lockup whose refund path has not opened.
|
|
1857
|
+
* Shared with the v2 handler so the two cannot drift.
|
|
1858
|
+
* @see assertVhtlcSpendableNow for why this is the one certain case.
|
|
1859
|
+
*/
|
|
1860
|
+
assertSpendableNow(_script, contract, context) {
|
|
1861
|
+
assertVhtlcSpendableNow(contract, context);
|
|
1862
|
+
},
|
|
1863
|
+
/**
|
|
1864
|
+
* Never. A live VHTLC is escrow: the counterparty's claim leaf is armed,
|
|
1865
|
+
* and generic selection — send, settle, renewal, offboard — would move the
|
|
1866
|
+
* lockup out from under a swap the counterparty is still entitled to
|
|
1867
|
+
* complete, or race a claim already in flight.
|
|
1868
|
+
*
|
|
1869
|
+
* Two narrow reasons, and neither is "spending this would destroy the
|
|
1870
|
+
* VTXO". It would not: the leaf {@link deriveTapscripts} stamps is
|
|
1871
|
+
* `refundWithoutReceiver` — `CLTV[sender, server]`, the sender's OWN
|
|
1872
|
+
* refund, which the server rejects until `refundLocktime` matures. The
|
|
1873
|
+
* reasons are that an escrowed lockup must not count toward the
|
|
1874
|
+
* `available` balance bucket (`computeOffchainBalance` credits `available`
|
|
1875
|
+
* only where this gate is open), and that generic RENEWAL must not
|
|
1876
|
+
* silently execute a refund the caller never asked for —
|
|
1877
|
+
* `runPeriodicSettle` selects through `getSpendableVtxos`, which this gate
|
|
1878
|
+
* filters, and it runs unprompted whenever a wallet is built without an
|
|
1879
|
+
* explicit `settlementConfig`.
|
|
1880
|
+
*
|
|
1881
|
+
* **Inseparable from {@link deriveTapscripts}.** Until that method existed
|
|
1882
|
+
* a `vhtlc` row's VTXOs could not be annotated at all, so they never
|
|
1883
|
+
* reached this gate and the answer here was unobservable — the previous
|
|
1884
|
+
* `true` and the missing derivation were safe only by cancelling out.
|
|
1885
|
+
* Deriving the leaf without closing the gate would hand generic renewal an
|
|
1886
|
+
* escrow it had never been offered before. (The earlier `true` was
|
|
1887
|
+
* justified as preserving selection of in-flight swaps; no such selection
|
|
1888
|
+
* was ever reachable, for that same annotation reason.)
|
|
1889
|
+
*/
|
|
1890
|
+
isGenericallySpendable: () => false,
|
|
1891
|
+
/**
|
|
1892
|
+
* The annotation leaf stamped onto every VTXO locked to this contract.
|
|
1893
|
+
*
|
|
1894
|
+
* **Without this the contract is unusable, not merely unoptimized.**
|
|
1895
|
+
* `deriveContractTapscripts` falls back to `script.forfeit()` for any
|
|
1896
|
+
* handler that does not implement {@link TapscriptDeriving}, and no VHTLC
|
|
1897
|
+
* script version has a `forfeit()` — `VtxoScript` does not define one and
|
|
1898
|
+
* `VHTLC.BaseScript` does not add one. The fallback therefore threw
|
|
1899
|
+
* `legacy.forfeit is not a function`, `annotatableIn` caught it, and
|
|
1900
|
+
* `fetchContractVtxosBulk` dropped this contract's VTXOs before they were
|
|
1901
|
+
* persisted. The row existed and was watched while its balance stayed
|
|
1902
|
+
* permanently invisible, and `getSyncState()` reported `degraded` forever.
|
|
1903
|
+
*
|
|
1904
|
+
* `refundWithoutReceiver` is the leaf, for both roles the annotation
|
|
1905
|
+
* serves, because it is the only collaborative path this wallet can
|
|
1906
|
+
* actually satisfy as the sender — the same conclusion `selectPath`
|
|
1907
|
+
* reaches, kept in step with it deliberately. The claim leaves belong to
|
|
1908
|
+
* the receiver and need a preimage the sender does not have; `refund` and
|
|
1909
|
+
* `unilateralRefund` both need the counterparty's signature, which this
|
|
1910
|
+
* protocol has no message to ask for. V1 has no covenant leaves at all.
|
|
1911
|
+
*
|
|
1912
|
+
* **It carries a CLTV, and callers must respect it.** A settlement built
|
|
1913
|
+
* on this leaf is only valid once `refundLocktime` has matured, so a
|
|
1914
|
+
* recovery round that sweeps this VTXO early is rejected by the server.
|
|
1915
|
+
* Nothing in this handler can enforce that, because the annotation is
|
|
1916
|
+
* derived per contract and knows no clock.
|
|
1917
|
+
*/
|
|
1918
|
+
deriveTapscripts(script) {
|
|
1919
|
+
const leaf = script.refundWithoutReceiver();
|
|
1920
|
+
return {
|
|
1921
|
+
forfeitTapLeafScript: leaf,
|
|
1922
|
+
intentTapLeafScript: leaf,
|
|
1923
|
+
tapTree: script.encode()
|
|
1924
|
+
};
|
|
1595
1925
|
}
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
decode: () => decode,
|
|
1604
|
-
encode: () => encode
|
|
1605
|
-
});
|
|
1606
|
-
var BIGNUM_MAX_BYTES = 520;
|
|
1607
|
-
var codec = btcSigner.ScriptNum(
|
|
1608
|
-
BIGNUM_MAX_BYTES,
|
|
1609
|
-
/* forceMinimal */
|
|
1610
|
-
true
|
|
1611
|
-
);
|
|
1612
|
-
function encode(value) {
|
|
1613
|
-
const result = codec.encode(value);
|
|
1614
|
-
if (result.length > BIGNUM_MAX_BYTES) {
|
|
1615
|
-
throw new Error(`BigNum value exceeds 520 bytes (encoded to ${result.length} bytes)`);
|
|
1926
|
+
};
|
|
1927
|
+
function decodeCovenantLeaf(params, destinationKey, emulatorKey, label) {
|
|
1928
|
+
const destination = params[destinationKey];
|
|
1929
|
+
const emulator = params[emulatorKey];
|
|
1930
|
+
if (!destination && !emulator) return void 0;
|
|
1931
|
+
if (!destination || !emulator) {
|
|
1932
|
+
throw new Error(`${label} needs both '${destinationKey}' and '${emulatorKey}', or neither`);
|
|
1616
1933
|
}
|
|
1617
|
-
return
|
|
1618
|
-
}
|
|
1619
|
-
function decode(value) {
|
|
1620
|
-
return codec.decode(value);
|
|
1934
|
+
return { destination: base.hex.decode(destination), emulatorPubkey: base.hex.decode(emulator) };
|
|
1621
1935
|
}
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1936
|
+
var VHTLCV2ContractHandler = {
|
|
1937
|
+
type: "vhtlc-v2",
|
|
1938
|
+
createScript(params) {
|
|
1939
|
+
const typed = this.deserializeParams(params);
|
|
1940
|
+
return new exports.VHTLC.ScriptV2(typed);
|
|
1941
|
+
},
|
|
1942
|
+
serializeParams(params) {
|
|
1943
|
+
return {
|
|
1944
|
+
sender: base.hex.encode(params.sender),
|
|
1945
|
+
receiver: base.hex.encode(params.receiver),
|
|
1946
|
+
server: base.hex.encode(params.server),
|
|
1947
|
+
hash: base.hex.encode(params.preimageHash),
|
|
1948
|
+
refundLocktime: params.refundLocktime.toString(),
|
|
1949
|
+
claimDelay: chunk4A4GRSTB_cjs.timelockToSequence(params.unilateralClaimDelay).toString(),
|
|
1950
|
+
refundDelay: chunk4A4GRSTB_cjs.timelockToSequence(params.unilateralRefundDelay).toString(),
|
|
1951
|
+
refundNoReceiverDelay: chunk4A4GRSTB_cjs.timelockToSequence(
|
|
1952
|
+
params.unilateralRefundWithoutReceiverDelay
|
|
1953
|
+
).toString(),
|
|
1954
|
+
// Spread rather than written as `undefined`: a `Record<string,
|
|
1955
|
+
// string>` round-tripped through a repository must not gain keys
|
|
1956
|
+
// whose value is the string "undefined".
|
|
1957
|
+
...params.nonInteractiveClaim && {
|
|
1958
|
+
nonInteractiveClaimReceiverPkScript: base.hex.encode(
|
|
1959
|
+
params.nonInteractiveClaim.receiverPkScript
|
|
1960
|
+
),
|
|
1961
|
+
nonInteractiveClaimEmulatorPubkey: base.hex.encode(
|
|
1962
|
+
params.nonInteractiveClaim.emulatorPubkey
|
|
1963
|
+
)
|
|
1964
|
+
},
|
|
1965
|
+
...params.nonInteractiveRefund && {
|
|
1966
|
+
nonInteractiveRefundSenderPkScript: base.hex.encode(
|
|
1967
|
+
params.nonInteractiveRefund.senderPkScript
|
|
1968
|
+
),
|
|
1969
|
+
nonInteractiveRefundEmulatorPubkey: base.hex.encode(
|
|
1970
|
+
params.nonInteractiveRefund.emulatorPubkey
|
|
1971
|
+
)
|
|
1650
1972
|
}
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1973
|
+
};
|
|
1974
|
+
},
|
|
1975
|
+
deserializeParams(params) {
|
|
1976
|
+
const claim = decodeCovenantLeaf(
|
|
1977
|
+
params,
|
|
1978
|
+
"nonInteractiveClaimReceiverPkScript",
|
|
1979
|
+
"nonInteractiveClaimEmulatorPubkey",
|
|
1980
|
+
"nonInteractiveClaim"
|
|
1981
|
+
);
|
|
1982
|
+
const refund = decodeCovenantLeaf(
|
|
1983
|
+
params,
|
|
1984
|
+
"nonInteractiveRefundSenderPkScript",
|
|
1985
|
+
"nonInteractiveRefundEmulatorPubkey",
|
|
1986
|
+
"nonInteractiveRefund"
|
|
1987
|
+
);
|
|
1988
|
+
return {
|
|
1989
|
+
sender: base.hex.decode(params.sender),
|
|
1990
|
+
receiver: base.hex.decode(params.receiver),
|
|
1991
|
+
server: base.hex.decode(params.server),
|
|
1992
|
+
preimageHash: base.hex.decode(params.hash),
|
|
1993
|
+
refundLocktime: BigInt(params.refundLocktime),
|
|
1994
|
+
unilateralClaimDelay: chunk4A4GRSTB_cjs.sequenceToTimelock(Number(params.claimDelay)),
|
|
1995
|
+
unilateralRefundDelay: chunk4A4GRSTB_cjs.sequenceToTimelock(Number(params.refundDelay)),
|
|
1996
|
+
unilateralRefundWithoutReceiverDelay: chunk4A4GRSTB_cjs.sequenceToTimelock(
|
|
1997
|
+
Number(params.refundNoReceiverDelay)
|
|
1998
|
+
),
|
|
1999
|
+
...claim && {
|
|
2000
|
+
nonInteractiveClaim: {
|
|
2001
|
+
receiverPkScript: claim.destination,
|
|
2002
|
+
emulatorPubkey: claim.emulatorPubkey
|
|
2003
|
+
}
|
|
2004
|
+
},
|
|
2005
|
+
...refund && {
|
|
2006
|
+
nonInteractiveRefund: {
|
|
2007
|
+
senderPkScript: refund.destination,
|
|
2008
|
+
emulatorPubkey: refund.emulatorPubkey
|
|
2009
|
+
}
|
|
1663
2010
|
}
|
|
1664
|
-
|
|
1665
|
-
}
|
|
2011
|
+
};
|
|
1666
2012
|
},
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
if (op === void 0) throw new Error(`Unknown opcode=${cur.toString(16)}`);
|
|
1688
|
-
out.push(op);
|
|
2013
|
+
/**
|
|
2014
|
+
* Select spending path based on context.
|
|
2015
|
+
*
|
|
2016
|
+
* Role is determined from `context.role` or by matching
|
|
2017
|
+
* `context.walletDescriptor` (preferred) / `context.walletPubKey`
|
|
2018
|
+
* against sender/receiver in contract params.
|
|
2019
|
+
*/
|
|
2020
|
+
selectPath(script, contract, context) {
|
|
2021
|
+
const role = resolveRole(contract, context);
|
|
2022
|
+
const preimage = contract.params?.preimage;
|
|
2023
|
+
const refundLocktime = BigInt(contract.params.refundLocktime);
|
|
2024
|
+
if (!role) {
|
|
2025
|
+
return null;
|
|
2026
|
+
}
|
|
2027
|
+
if (context.collaborative) {
|
|
2028
|
+
if (role === "receiver" && preimage) {
|
|
2029
|
+
return {
|
|
2030
|
+
leaf: script.claim(),
|
|
2031
|
+
extraWitness: [base.hex.decode(preimage)]
|
|
2032
|
+
};
|
|
1689
2033
|
}
|
|
2034
|
+
if (role === "sender" && isCltvSatisfied(context, refundLocktime)) {
|
|
2035
|
+
return {
|
|
2036
|
+
leaf: script.refundWithoutReceiver()
|
|
2037
|
+
};
|
|
2038
|
+
}
|
|
2039
|
+
return null;
|
|
2040
|
+
}
|
|
2041
|
+
if (role === "receiver" && preimage) {
|
|
2042
|
+
const sequence = Number(contract.params.claimDelay);
|
|
2043
|
+
if (!isCsvSpendable(context, sequence)) return null;
|
|
2044
|
+
return {
|
|
2045
|
+
leaf: script.unilateralClaim(),
|
|
2046
|
+
extraWitness: [base.hex.decode(preimage)],
|
|
2047
|
+
sequence
|
|
2048
|
+
};
|
|
2049
|
+
}
|
|
2050
|
+
if (role === "sender") {
|
|
2051
|
+
const sequence = Number(contract.params.refundNoReceiverDelay);
|
|
2052
|
+
if (!isCsvSpendable(context, sequence)) return null;
|
|
2053
|
+
return {
|
|
2054
|
+
leaf: script.unilateralRefundWithoutReceiver(),
|
|
2055
|
+
sequence
|
|
2056
|
+
};
|
|
2057
|
+
}
|
|
2058
|
+
return null;
|
|
2059
|
+
},
|
|
2060
|
+
/**
|
|
2061
|
+
* Get all possible spending paths (no timelock checks).
|
|
2062
|
+
*
|
|
2063
|
+
* Role is determined from `context.role` or by matching
|
|
2064
|
+
* `context.walletDescriptor` (preferred) / `context.walletPubKey`
|
|
2065
|
+
* against sender/receiver in contract params.
|
|
2066
|
+
*/
|
|
2067
|
+
getAllSpendingPaths(script, contract, context) {
|
|
2068
|
+
const role = resolveRole(contract, context);
|
|
2069
|
+
const paths = [];
|
|
2070
|
+
if (!role) {
|
|
2071
|
+
return paths;
|
|
1690
2072
|
}
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
} else if (op >= 1 && op <= 16) {
|
|
1704
|
-
parts.push(`OP_${op}`);
|
|
1705
|
-
} else if (op === -1 || op === -1n) {
|
|
1706
|
-
parts.push("OP_1NEGATE");
|
|
1707
|
-
} else {
|
|
1708
|
-
parts.push(base.hex.encode(encode(BigInt(op))));
|
|
2073
|
+
const preimage = contract.params?.preimage;
|
|
2074
|
+
if (context.collaborative) {
|
|
2075
|
+
if (role === "receiver" && preimage) {
|
|
2076
|
+
paths.push({
|
|
2077
|
+
leaf: script.claim(),
|
|
2078
|
+
extraWitness: [base.hex.decode(preimage)]
|
|
2079
|
+
});
|
|
2080
|
+
}
|
|
2081
|
+
if (role === "sender") {
|
|
2082
|
+
paths.push({
|
|
2083
|
+
leaf: script.refundWithoutReceiver()
|
|
2084
|
+
});
|
|
1709
2085
|
}
|
|
1710
2086
|
} else {
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
2087
|
+
if (role === "receiver" && preimage) {
|
|
2088
|
+
const sequence = Number(contract.params.claimDelay);
|
|
2089
|
+
paths.push({
|
|
2090
|
+
leaf: script.unilateralClaim(),
|
|
2091
|
+
extraWitness: [base.hex.decode(preimage)],
|
|
2092
|
+
sequence
|
|
2093
|
+
});
|
|
2094
|
+
}
|
|
2095
|
+
if (role === "sender") {
|
|
2096
|
+
const sequence = Number(contract.params.refundNoReceiverDelay);
|
|
2097
|
+
paths.push({
|
|
2098
|
+
leaf: script.unilateralRefundWithoutReceiver(),
|
|
2099
|
+
sequence
|
|
2100
|
+
});
|
|
2101
|
+
}
|
|
1723
2102
|
}
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
2103
|
+
return paths;
|
|
2104
|
+
},
|
|
2105
|
+
getSpendablePaths(script, contract, context) {
|
|
2106
|
+
const role = resolveRole(contract, context);
|
|
2107
|
+
const paths = [];
|
|
2108
|
+
if (!role) {
|
|
2109
|
+
return paths;
|
|
1727
2110
|
}
|
|
1728
|
-
const
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
if (
|
|
1732
|
-
|
|
1733
|
-
|
|
2111
|
+
const preimage = contract.params?.preimage;
|
|
2112
|
+
const refundLocktime = BigInt(contract.params.refundLocktime);
|
|
2113
|
+
if (context.collaborative) {
|
|
2114
|
+
if (role === "receiver" && preimage) {
|
|
2115
|
+
paths.push({
|
|
2116
|
+
leaf: script.claim(),
|
|
2117
|
+
extraWitness: [base.hex.decode(preimage)]
|
|
2118
|
+
});
|
|
1734
2119
|
}
|
|
2120
|
+
if (role === "sender" && isCltvSatisfied(context, refundLocktime)) {
|
|
2121
|
+
paths.push({
|
|
2122
|
+
leaf: script.refundWithoutReceiver()
|
|
2123
|
+
});
|
|
2124
|
+
}
|
|
2125
|
+
return paths;
|
|
1735
2126
|
}
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
2127
|
+
if (role === "receiver" && preimage) {
|
|
2128
|
+
const sequence = Number(contract.params.claimDelay);
|
|
2129
|
+
if (isCsvSpendable(context, sequence)) {
|
|
2130
|
+
paths.push({
|
|
2131
|
+
leaf: script.unilateralClaim(),
|
|
2132
|
+
extraWitness: [base.hex.decode(preimage)],
|
|
2133
|
+
sequence
|
|
2134
|
+
});
|
|
2135
|
+
}
|
|
1745
2136
|
}
|
|
1746
|
-
if (
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
2137
|
+
if (role === "sender") {
|
|
2138
|
+
const sequence = Number(contract.params.refundNoReceiverDelay);
|
|
2139
|
+
if (isCsvSpendable(context, sequence)) {
|
|
2140
|
+
paths.push({
|
|
2141
|
+
leaf: script.unilateralRefundWithoutReceiver(),
|
|
2142
|
+
sequence
|
|
2143
|
+
});
|
|
1751
2144
|
}
|
|
1752
2145
|
}
|
|
1753
|
-
|
|
2146
|
+
return paths;
|
|
2147
|
+
},
|
|
2148
|
+
/**
|
|
2149
|
+
* Refuse an explicit spend of a lockup whose refund path has not opened.
|
|
2150
|
+
* @see assertVhtlcSpendableNow for why this is the one certain case.
|
|
2151
|
+
*/
|
|
2152
|
+
assertSpendableNow(_script, contract, context) {
|
|
2153
|
+
assertVhtlcSpendableNow(contract, context);
|
|
2154
|
+
},
|
|
2155
|
+
/**
|
|
2156
|
+
* Never. A live VHTLC is escrow: the counterparty's claim leaf is armed,
|
|
2157
|
+
* and generic selection — send, settle, RENEWAL, offboard — would move the
|
|
2158
|
+
* lockup out from under a swap the counterparty is still entitled to
|
|
2159
|
+
* complete, or race a claim already in flight. The `vhtlc` handler now
|
|
2160
|
+
* answers the same, for the same reason.
|
|
2161
|
+
*
|
|
2162
|
+
* Recovery is unaffected: the gate guards only GENERIC selection, and every
|
|
2163
|
+
* way out of a lockup names its outpoints explicitly — `settle({ inputs })`,
|
|
2164
|
+
* or `@arkade-os/swap`'s `pushRefundWithoutReceiver`, which builds the
|
|
2165
|
+
* refund itself from `findLockupVtxos`' outpoints.
|
|
2166
|
+
*/
|
|
2167
|
+
isGenericallySpendable: () => false,
|
|
2168
|
+
/**
|
|
2169
|
+
* The annotation leaf stamped onto every VTXO locked to this contract.
|
|
2170
|
+
*
|
|
2171
|
+
* **Without this the contract is unusable, not merely unoptimized.**
|
|
2172
|
+
* `deriveContractTapscripts` falls back to `script.forfeit()` for any
|
|
2173
|
+
* handler that does not implement {@link TapscriptDeriving}, and no VHTLC
|
|
2174
|
+
* script version has a `forfeit()` — `VtxoScript` does not define one and
|
|
2175
|
+
* `VHTLC.BaseScript` does not add one. The fallback therefore throws
|
|
2176
|
+
* `legacy.forfeit is not a function`, `annotatableIn` catches it, and
|
|
2177
|
+
* `fetchContractVtxosBulk` drops this contract's VTXOs before they are
|
|
2178
|
+
* persisted. The row would exist and be watched while its balance stayed
|
|
2179
|
+
* permanently invisible, and `getSyncState()` would report `degraded`
|
|
2180
|
+
* forever. (The `vhtlc` handler had the same gap and now closes it the same
|
|
2181
|
+
* way; nothing registers V1 rows through `ContractManager` in this repo, so
|
|
2182
|
+
* it was never hit here.)
|
|
2183
|
+
*
|
|
2184
|
+
* `refundWithoutReceiver` is the leaf, for both roles the annotation
|
|
2185
|
+
* serves, because it is the only collaborative path this wallet can
|
|
2186
|
+
* actually satisfy as the sender — the same conclusion `selectPath`
|
|
2187
|
+
* reaches, kept in step with it deliberately. The claim leaves belong to
|
|
2188
|
+
* the receiver and need a preimage the sender does not have; `refund` and
|
|
2189
|
+
* `unilateralRefund` need the counterparty; the covenant leaves are the
|
|
2190
|
+
* emulator's.
|
|
2191
|
+
*
|
|
2192
|
+
* **It carries a CLTV, and callers must respect it.** A settlement built on
|
|
2193
|
+
* this leaf is only valid once `refundLocktime` has matured, so a recovery
|
|
2194
|
+
* round that sweeps this VTXO early is rejected by the server. That is the
|
|
2195
|
+
* same constraint `packages/boltz-swap` encodes as "pre-CLTV recoverable →
|
|
2196
|
+
* skipped"; nothing in this handler can enforce it, because the annotation
|
|
2197
|
+
* is derived per contract and knows no clock.
|
|
2198
|
+
*/
|
|
2199
|
+
deriveTapscripts(script) {
|
|
2200
|
+
const leaf = script.refundWithoutReceiver();
|
|
2201
|
+
return {
|
|
2202
|
+
forfeitTapLeafScript: leaf,
|
|
2203
|
+
intentTapLeafScript: leaf,
|
|
2204
|
+
tapTree: script.encode()
|
|
2205
|
+
};
|
|
1754
2206
|
}
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
2207
|
+
};
|
|
2208
|
+
var BoardingContractHandler = {
|
|
2209
|
+
type: "boarding",
|
|
2210
|
+
createScript(params) {
|
|
2211
|
+
return DefaultContractHandler.createScript(params);
|
|
2212
|
+
},
|
|
2213
|
+
serializeParams(params) {
|
|
2214
|
+
return DefaultContractHandler.serializeParams(params);
|
|
2215
|
+
},
|
|
2216
|
+
deserializeParams(params) {
|
|
2217
|
+
return DefaultContractHandler.deserializeParams(params);
|
|
2218
|
+
},
|
|
2219
|
+
selectPath(script, contract, context) {
|
|
2220
|
+
return DefaultContractHandler.selectPath(script, contract, context);
|
|
2221
|
+
},
|
|
2222
|
+
getAllSpendingPaths(script, contract, context) {
|
|
2223
|
+
return DefaultContractHandler.getAllSpendingPaths(script, contract, context);
|
|
2224
|
+
},
|
|
2225
|
+
getSpendablePaths(script, contract, context) {
|
|
2226
|
+
return DefaultContractHandler.getSpendablePaths(script, contract, context);
|
|
2227
|
+
},
|
|
2228
|
+
/** Preserves today's settle/board behaviour. */
|
|
2229
|
+
isGenericallySpendable: () => true,
|
|
2230
|
+
/**
|
|
2231
|
+
* Probe the on-chain UTXO set for a boarding output at this HD index.
|
|
2232
|
+
*
|
|
2233
|
+
* Boarding's source of truth is the **current** on-chain coin set
|
|
2234
|
+
* (`OnchainProvider.getCoins`), not the Ark indexer: a boarded (spent)
|
|
2235
|
+
* boarding output becomes an L2 VTXO at the receive index, so the
|
|
2236
|
+
* indexer probe already keeps the gap window open for it; only an
|
|
2237
|
+
* *unspent* boarding output needs the on-chain probe (see plan §2).
|
|
2238
|
+
*
|
|
2239
|
+
* No-ops (returns `[]`) when boarding discovery is not plumbed — i.e.
|
|
2240
|
+
* `deps.boardingTimelock` or `deps.onchainNetwork` is absent — so the
|
|
2241
|
+
* scanner unit harness (which sets neither) is unaffected.
|
|
2242
|
+
*
|
|
2243
|
+
* Always emits `type: "boarding"`, even in the degenerate equal-delay case
|
|
2244
|
+
* where the boarding script is byte-identical to a `default` script at this
|
|
2245
|
+
* index (`boardingExitDelay === unilateralExitDelay`). The collision is no
|
|
2246
|
+
* longer pre-judged in discovery; it is tolerated FIRST-WINS at the
|
|
2247
|
+
* persistence layer ({@link ContractManager.upsertContract}). `deps.csvTimelocks`
|
|
2248
|
+
* is intentionally not read here.
|
|
2249
|
+
*/
|
|
2250
|
+
async discoverAt(index, descriptor, deps) {
|
|
2251
|
+
if (!deps.boardingTimelock || !deps.onchainNetwork) return [];
|
|
2252
|
+
const pubKey = deriveDescriptorLeafPubKey(descriptor);
|
|
2253
|
+
const script = new exports.DefaultVtxo.Script({
|
|
2254
|
+
pubKey,
|
|
2255
|
+
serverPubKey: deps.serverPubKey,
|
|
2256
|
+
csvTimelock: deps.boardingTimelock
|
|
2257
|
+
});
|
|
2258
|
+
const onchainAddress = script.onchainAddress(deps.onchainNetwork);
|
|
2259
|
+
const coins = await deps.onchainProvider.getCoins(onchainAddress);
|
|
2260
|
+
if (coins.length === 0) return [];
|
|
2261
|
+
const scriptHex = base.hex.encode(script.pkScript);
|
|
2262
|
+
const boardingSeq = chunk4A4GRSTB_cjs.timelockToSequence(deps.boardingTimelock);
|
|
2263
|
+
return [
|
|
2264
|
+
{
|
|
2265
|
+
type: "boarding",
|
|
2266
|
+
params: {
|
|
2267
|
+
pubKey: base.hex.encode(pubKey),
|
|
2268
|
+
serverPubKey: base.hex.encode(deps.serverPubKey),
|
|
2269
|
+
csvTimelock: boardingSeq.toString()
|
|
2270
|
+
},
|
|
2271
|
+
script: scriptHex,
|
|
2272
|
+
// The persisted row's `address` is the *Ark* address (not the
|
|
2273
|
+
// on-chain P2TR), matching the row registered at init so the
|
|
2274
|
+
// ContractWatcher keeps monitoring the same L2 script and the
|
|
2275
|
+
// VTXO repository bucket lines up (plan §6-I.4). The P2TR is
|
|
2276
|
+
// recomputed from params only for the `getCoins` probe.
|
|
2277
|
+
address: script.address(deps.network.hrp, deps.serverPubKey).encode(),
|
|
2278
|
+
// Tag rotated rows (index > 0) so boot resolution can find the
|
|
2279
|
+
// newest boarding address and descriptor-aware signing can
|
|
2280
|
+
// recover the per-index key (plan §6-II/§6-III.3). The index-0
|
|
2281
|
+
// baseline stays untagged, matching `default`/`delegate`.
|
|
2282
|
+
...rotatedReceiveMetadata(index, descriptor)
|
|
2283
|
+
}
|
|
2284
|
+
];
|
|
1771
2285
|
}
|
|
1772
|
-
|
|
1773
|
-
}
|
|
1774
|
-
function computeArkadeScriptPublicKey(pubKey, script) {
|
|
1775
|
-
const hash = arkadeScriptHash(script);
|
|
1776
|
-
const xOnly = pubKey.length === 33 ? pubKey.subarray(1) : pubKey;
|
|
1777
|
-
const point = secp256k1_js.schnorr.utils.lift_x(utils_js.bytesToNumberBE(xOnly));
|
|
1778
|
-
const scalar = utils_js.bytesToNumberBE(hash) % secp256k1_js.secp256k1.Point.CURVE().n;
|
|
1779
|
-
const tweak = secp256k1_js.secp256k1.Point.BASE.multiply(scalar);
|
|
1780
|
-
return secp256k1_js.schnorr.utils.pointToBytes(point.add(tweak));
|
|
1781
|
-
}
|
|
1782
|
-
|
|
1783
|
-
// src/arkade/program.ts
|
|
2286
|
+
};
|
|
1784
2287
|
var MinimalScriptNum = btcSigner.ScriptNum(void 0, true);
|
|
1785
2288
|
var SUPPORTED_PROGRAM_VERSION = 0;
|
|
1786
2289
|
function inputName(ref) {
|
|
@@ -1917,27 +2420,27 @@ function encodeTapscriptSegment(seg, pubkeys, args) {
|
|
|
1917
2420
|
if (seg.csv) {
|
|
1918
2421
|
const timelock = { type: seg.csv.type, value: resolveTimelockValue(seg.csv.value, args) };
|
|
1919
2422
|
if (seg.asm) {
|
|
1920
|
-
return
|
|
2423
|
+
return chunk4A4GRSTB_cjs.ConditionCSVMultisigTapscript.encode({
|
|
1921
2424
|
conditionScript: resolveAsm(seg.asm, args),
|
|
1922
2425
|
timelock,
|
|
1923
2426
|
pubkeys
|
|
1924
2427
|
});
|
|
1925
2428
|
}
|
|
1926
|
-
return
|
|
2429
|
+
return chunk4A4GRSTB_cjs.CSVMultisigTapscript.encode({ timelock, pubkeys });
|
|
1927
2430
|
}
|
|
1928
2431
|
if (seg.cltv !== void 0) {
|
|
1929
|
-
return
|
|
2432
|
+
return chunk4A4GRSTB_cjs.CLTVMultisigTapscript.encode({
|
|
1930
2433
|
absoluteTimelock: resolveTimelockValue(seg.cltv, args),
|
|
1931
2434
|
pubkeys
|
|
1932
2435
|
});
|
|
1933
2436
|
}
|
|
1934
2437
|
if (seg.asm) {
|
|
1935
|
-
return
|
|
2438
|
+
return chunk4A4GRSTB_cjs.ConditionMultisigTapscript.encode({
|
|
1936
2439
|
conditionScript: resolveAsm(seg.asm, args),
|
|
1937
2440
|
pubkeys
|
|
1938
2441
|
});
|
|
1939
2442
|
}
|
|
1940
|
-
return
|
|
2443
|
+
return chunk4A4GRSTB_cjs.MultisigTapscript.encode({ pubkeys });
|
|
1941
2444
|
}
|
|
1942
2445
|
function compileFunctions(program, args, keys) {
|
|
1943
2446
|
if (program.version !== SUPPORTED_PROGRAM_VERSION) {
|
|
@@ -1967,7 +2470,7 @@ function compileFunctions(program, args, keys) {
|
|
|
1967
2470
|
return { name: names[i], def, leafScript, arkadeScript, signerKeys: pubkeys };
|
|
1968
2471
|
});
|
|
1969
2472
|
}
|
|
1970
|
-
var ArkadeProgramScript = class extends
|
|
2473
|
+
var ArkadeProgramScript = class extends chunk4A4GRSTB_cjs.VtxoScript {
|
|
1971
2474
|
constructor(program, args, keys) {
|
|
1972
2475
|
const partial = compileFunctions(program, args, keys);
|
|
1973
2476
|
super(partial.map((p) => p.leafScript));
|
|
@@ -2180,7 +2683,7 @@ function pathsFor(script, context, opts) {
|
|
|
2180
2683
|
continue;
|
|
2181
2684
|
}
|
|
2182
2685
|
if (requiresServer) continue;
|
|
2183
|
-
const sequence = csv ? Number(
|
|
2686
|
+
const sequence = csv ? Number(chunk4A4GRSTB_cjs.timelockToSequence(csv)) : void 0;
|
|
2184
2687
|
if (opts.checkTimelocks && csv && !isCsvSpendable(context, sequence)) continue;
|
|
2185
2688
|
if (opts.checkTimelocks && cltv !== void 0 && !isCltvSatisfied(context, cltv)) {
|
|
2186
2689
|
continue;
|
|
@@ -2219,6 +2722,18 @@ var ArkadeContractHandler = {
|
|
|
2219
2722
|
getSpendablePaths(script, _contract, context) {
|
|
2220
2723
|
return pathsFor(script, context, { checkTimelocks: true });
|
|
2221
2724
|
},
|
|
2725
|
+
/**
|
|
2726
|
+
* Opt-in, never opt-out. A program artifact cannot say whether its funds are
|
|
2727
|
+
* escrowed — an escrow's collaborative cancel leaf is indistinguishable from
|
|
2728
|
+
* an ordinary spend — so the answer is read from the row the registering code
|
|
2729
|
+
* wrote. Anything but an explicit `true` (absent, malformed, or a row written
|
|
2730
|
+
* before this gate existed) is not generically spendable: `createContract` is
|
|
2731
|
+
* first-writer-wins, so a fail-open marker would let one unmarked
|
|
2732
|
+
* registration permanently open the gate for every contract sharing the script.
|
|
2733
|
+
*/
|
|
2734
|
+
isGenericallySpendable(contract) {
|
|
2735
|
+
return contract.metadata?.genericallySpendable === true;
|
|
2736
|
+
},
|
|
2222
2737
|
/**
|
|
2223
2738
|
* Annotation tapscripts for VTXOs locked to this contract: prefer the
|
|
2224
2739
|
* collaborative tapscript path (the multisig the Arkade Service co-signs,
|
|
@@ -2246,6 +2761,7 @@ var ArkadeContractHandler = {
|
|
|
2246
2761
|
contractHandlers.register(DefaultContractHandler);
|
|
2247
2762
|
contractHandlers.register(DelegateContractHandler);
|
|
2248
2763
|
contractHandlers.register(VHTLCContractHandler);
|
|
2764
|
+
contractHandlers.register(VHTLCV2ContractHandler);
|
|
2249
2765
|
contractHandlers.register(BoardingContractHandler);
|
|
2250
2766
|
contractHandlers.register(ArkadeContractHandler);
|
|
2251
2767
|
|
|
@@ -2264,6 +2780,7 @@ exports.OPCODE_NAMES = OPCODE_NAMES;
|
|
|
2264
2780
|
exports.OPCODE_VALUES = OPCODE_VALUES;
|
|
2265
2781
|
exports.SUPPORTED_PROGRAM_VERSION = SUPPORTED_PROGRAM_VERSION;
|
|
2266
2782
|
exports.VHTLCContractHandler = VHTLCContractHandler;
|
|
2783
|
+
exports.VHTLCV2ContractHandler = VHTLCV2ContractHandler;
|
|
2267
2784
|
exports.WALLET_RECEIVE_SOURCE = WALLET_RECEIVE_SOURCE;
|
|
2268
2785
|
exports.arkadeScriptHash = arkadeScriptHash;
|
|
2269
2786
|
exports.arkadeWitnessHash = arkadeWitnessHash;
|
|
@@ -2291,5 +2808,5 @@ exports.stringifyArtifact = stringifyArtifact;
|
|
|
2291
2808
|
exports.toASM = toASM;
|
|
2292
2809
|
exports.validateProgram = validateProgram;
|
|
2293
2810
|
exports.witnessRefToBytes = witnessRefToBytes;
|
|
2294
|
-
//# sourceMappingURL=chunk-
|
|
2295
|
-
//# sourceMappingURL=chunk-
|
|
2811
|
+
//# sourceMappingURL=chunk-MZNLCMUP.cjs.map
|
|
2812
|
+
//# sourceMappingURL=chunk-MZNLCMUP.cjs.map
|