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