@arkade-os/sdk 0.0.16
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 +312 -0
- package/dist/cjs/arknote/index.js +86 -0
- package/dist/cjs/forfeit.js +38 -0
- package/dist/cjs/identity/inMemoryKey.js +40 -0
- package/dist/cjs/identity/index.js +2 -0
- package/dist/cjs/index.js +48 -0
- package/dist/cjs/musig2/index.js +10 -0
- package/dist/cjs/musig2/keys.js +57 -0
- package/dist/cjs/musig2/nonces.js +44 -0
- package/dist/cjs/musig2/sign.js +102 -0
- package/dist/cjs/networks.js +26 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/providers/ark.js +530 -0
- package/dist/cjs/providers/onchain.js +61 -0
- package/dist/cjs/script/address.js +45 -0
- package/dist/cjs/script/base.js +51 -0
- package/dist/cjs/script/default.js +40 -0
- package/dist/cjs/script/tapscript.js +528 -0
- package/dist/cjs/script/vhtlc.js +84 -0
- package/dist/cjs/tree/signingSession.js +238 -0
- package/dist/cjs/tree/validation.js +184 -0
- package/dist/cjs/tree/vtxoTree.js +197 -0
- package/dist/cjs/utils/bip21.js +114 -0
- package/dist/cjs/utils/coinselect.js +73 -0
- package/dist/cjs/utils/psbt.js +124 -0
- package/dist/cjs/utils/transactionHistory.js +148 -0
- package/dist/cjs/utils/txSizeEstimator.js +95 -0
- package/dist/cjs/wallet/index.js +8 -0
- package/dist/cjs/wallet/serviceWorker/db/vtxo/idb.js +153 -0
- package/dist/cjs/wallet/serviceWorker/db/vtxo/index.js +2 -0
- package/dist/cjs/wallet/serviceWorker/request.js +75 -0
- package/dist/cjs/wallet/serviceWorker/response.js +187 -0
- package/dist/cjs/wallet/serviceWorker/wallet.js +332 -0
- package/dist/cjs/wallet/serviceWorker/worker.js +452 -0
- package/dist/cjs/wallet/wallet.js +720 -0
- package/dist/esm/arknote/index.js +81 -0
- package/dist/esm/forfeit.js +35 -0
- package/dist/esm/identity/inMemoryKey.js +36 -0
- package/dist/esm/identity/index.js +1 -0
- package/dist/esm/index.js +39 -0
- package/dist/esm/musig2/index.js +3 -0
- package/dist/esm/musig2/keys.js +21 -0
- package/dist/esm/musig2/nonces.js +8 -0
- package/dist/esm/musig2/sign.js +63 -0
- package/dist/esm/networks.js +22 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/providers/ark.js +526 -0
- package/dist/esm/providers/onchain.js +57 -0
- package/dist/esm/script/address.js +41 -0
- package/dist/esm/script/base.js +46 -0
- package/dist/esm/script/default.js +37 -0
- package/dist/esm/script/tapscript.js +491 -0
- package/dist/esm/script/vhtlc.js +81 -0
- package/dist/esm/tree/signingSession.js +200 -0
- package/dist/esm/tree/validation.js +179 -0
- package/dist/esm/tree/vtxoTree.js +157 -0
- package/dist/esm/utils/bip21.js +110 -0
- package/dist/esm/utils/coinselect.js +69 -0
- package/dist/esm/utils/psbt.js +118 -0
- package/dist/esm/utils/transactionHistory.js +145 -0
- package/dist/esm/utils/txSizeEstimator.js +91 -0
- package/dist/esm/wallet/index.js +5 -0
- package/dist/esm/wallet/serviceWorker/db/vtxo/idb.js +149 -0
- package/dist/esm/wallet/serviceWorker/db/vtxo/index.js +1 -0
- package/dist/esm/wallet/serviceWorker/request.js +72 -0
- package/dist/esm/wallet/serviceWorker/response.js +184 -0
- package/dist/esm/wallet/serviceWorker/wallet.js +328 -0
- package/dist/esm/wallet/serviceWorker/worker.js +448 -0
- package/dist/esm/wallet/wallet.js +716 -0
- package/dist/types/arknote/index.d.ts +17 -0
- package/dist/types/forfeit.d.ts +15 -0
- package/dist/types/identity/inMemoryKey.d.ts +12 -0
- package/dist/types/identity/index.d.ts +7 -0
- package/dist/types/index.d.ts +22 -0
- package/dist/types/musig2/index.d.ts +4 -0
- package/dist/types/musig2/keys.d.ts +9 -0
- package/dist/types/musig2/nonces.d.ts +13 -0
- package/dist/types/musig2/sign.d.ts +27 -0
- package/dist/types/networks.d.ts +16 -0
- package/dist/types/providers/ark.d.ts +126 -0
- package/dist/types/providers/onchain.d.ts +36 -0
- package/dist/types/script/address.d.ts +10 -0
- package/dist/types/script/base.d.ts +26 -0
- package/dist/types/script/default.d.ts +19 -0
- package/dist/types/script/tapscript.d.ts +94 -0
- package/dist/types/script/vhtlc.d.ts +31 -0
- package/dist/types/tree/signingSession.d.ts +32 -0
- package/dist/types/tree/validation.d.ts +22 -0
- package/dist/types/tree/vtxoTree.d.ts +32 -0
- package/dist/types/utils/bip21.d.ts +21 -0
- package/dist/types/utils/coinselect.d.ts +21 -0
- package/dist/types/utils/psbt.d.ts +11 -0
- package/dist/types/utils/transactionHistory.d.ts +2 -0
- package/dist/types/utils/txSizeEstimator.d.ts +27 -0
- package/dist/types/wallet/index.d.ts +122 -0
- package/dist/types/wallet/serviceWorker/db/vtxo/idb.d.ts +18 -0
- package/dist/types/wallet/serviceWorker/db/vtxo/index.d.ts +12 -0
- package/dist/types/wallet/serviceWorker/request.d.ts +68 -0
- package/dist/types/wallet/serviceWorker/response.d.ts +107 -0
- package/dist/types/wallet/serviceWorker/wallet.d.ts +23 -0
- package/dist/types/wallet/serviceWorker/worker.d.ts +26 -0
- package/dist/types/wallet/wallet.d.ts +42 -0
- package/package.json +88 -0
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.CLTVMultisigTapscript = exports.ConditionMultisigTapscript = exports.ConditionCSVMultisigTapscript = exports.CSVMultisigTapscript = exports.MultisigTapscript = exports.TapscriptType = void 0;
|
|
37
|
+
exports.decodeTapscript = decodeTapscript;
|
|
38
|
+
const bip68 = __importStar(require("bip68"));
|
|
39
|
+
const script_1 = require("@scure/btc-signer/script");
|
|
40
|
+
const payment_1 = require("@scure/btc-signer/payment");
|
|
41
|
+
const base_1 = require("@scure/base");
|
|
42
|
+
var TapscriptType;
|
|
43
|
+
(function (TapscriptType) {
|
|
44
|
+
TapscriptType["Multisig"] = "multisig";
|
|
45
|
+
TapscriptType["CSVMultisig"] = "csv-multisig";
|
|
46
|
+
TapscriptType["ConditionCSVMultisig"] = "condition-csv-multisig";
|
|
47
|
+
TapscriptType["ConditionMultisig"] = "condition-multisig";
|
|
48
|
+
TapscriptType["CLTVMultisig"] = "cltv-multisig";
|
|
49
|
+
})(TapscriptType || (exports.TapscriptType = TapscriptType = {}));
|
|
50
|
+
function decodeTapscript(script) {
|
|
51
|
+
const types = [
|
|
52
|
+
MultisigTapscript,
|
|
53
|
+
CSVMultisigTapscript,
|
|
54
|
+
ConditionCSVMultisigTapscript,
|
|
55
|
+
ConditionMultisigTapscript,
|
|
56
|
+
CLTVMultisigTapscript,
|
|
57
|
+
];
|
|
58
|
+
for (const type of types) {
|
|
59
|
+
try {
|
|
60
|
+
return type.decode(script);
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
throw new Error(`Failed to decode: script ${base_1.hex.encode(script)} is not a valid tapscript`);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Implements a multi-signature script that requires a threshold of signatures
|
|
70
|
+
* from the specified pubkeys.
|
|
71
|
+
*/
|
|
72
|
+
var MultisigTapscript;
|
|
73
|
+
(function (MultisigTapscript) {
|
|
74
|
+
let MultisigType;
|
|
75
|
+
(function (MultisigType) {
|
|
76
|
+
MultisigType[MultisigType["CHECKSIG"] = 0] = "CHECKSIG";
|
|
77
|
+
MultisigType[MultisigType["CHECKSIGADD"] = 1] = "CHECKSIGADD";
|
|
78
|
+
})(MultisigType = MultisigTapscript.MultisigType || (MultisigTapscript.MultisigType = {}));
|
|
79
|
+
function encode(params) {
|
|
80
|
+
if (params.pubkeys.length === 0) {
|
|
81
|
+
throw new Error("At least 1 pubkey is required");
|
|
82
|
+
}
|
|
83
|
+
for (const pubkey of params.pubkeys) {
|
|
84
|
+
if (pubkey.length !== 32) {
|
|
85
|
+
throw new Error(`Invalid pubkey length: expected 32, got ${pubkey.length}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (!params.type) {
|
|
89
|
+
params.type = MultisigType.CHECKSIG;
|
|
90
|
+
}
|
|
91
|
+
if (params.type === MultisigType.CHECKSIGADD) {
|
|
92
|
+
return {
|
|
93
|
+
type: TapscriptType.Multisig,
|
|
94
|
+
params,
|
|
95
|
+
script: (0, payment_1.p2tr_ms)(params.pubkeys.length, params.pubkeys).script,
|
|
96
|
+
witnessSize: () => params.pubkeys.length * 64,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
const asm = [];
|
|
100
|
+
for (let i = 0; i < params.pubkeys.length; i++) {
|
|
101
|
+
asm.push(params.pubkeys[i]);
|
|
102
|
+
// CHECKSIGVERIFY except the last pubkey
|
|
103
|
+
if (i < params.pubkeys.length - 1) {
|
|
104
|
+
asm.push("CHECKSIGVERIFY");
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
asm.push("CHECKSIG");
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
type: TapscriptType.Multisig,
|
|
112
|
+
params,
|
|
113
|
+
script: script_1.Script.encode(asm),
|
|
114
|
+
witnessSize: () => params.pubkeys.length * 64,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
MultisigTapscript.encode = encode;
|
|
118
|
+
function decode(script) {
|
|
119
|
+
if (script.length === 0) {
|
|
120
|
+
throw new Error("Failed to decode: script is empty");
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
// Try decoding as checksigAdd first
|
|
124
|
+
return decodeChecksigAdd(script);
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
// If checksigAdd fails, try regular checksig
|
|
128
|
+
try {
|
|
129
|
+
return decodeChecksig(script);
|
|
130
|
+
}
|
|
131
|
+
catch (error2) {
|
|
132
|
+
throw new Error(`Failed to decode script: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
MultisigTapscript.decode = decode;
|
|
137
|
+
// <pubkey> CHECKSIG <pubkey> CHECKSIGADD <len_keys> NUMEQUAL
|
|
138
|
+
function decodeChecksigAdd(script) {
|
|
139
|
+
const asm = script_1.Script.decode(script);
|
|
140
|
+
const pubkeys = [];
|
|
141
|
+
let foundNumEqual = false;
|
|
142
|
+
// Parse through ASM operations
|
|
143
|
+
for (let i = 0; i < asm.length; i++) {
|
|
144
|
+
const op = asm[i];
|
|
145
|
+
// If it's a data push, it should be a 32-byte pubkey
|
|
146
|
+
if (typeof op !== "string" && typeof op !== "number") {
|
|
147
|
+
if (op.length !== 32) {
|
|
148
|
+
throw new Error(`Invalid pubkey length: expected 32, got ${op.length}`);
|
|
149
|
+
}
|
|
150
|
+
pubkeys.push(op);
|
|
151
|
+
// Check next operation is CHECKSIGADD or CHECKSIG
|
|
152
|
+
if (i + 1 >= asm.length ||
|
|
153
|
+
(asm[i + 1] !== "CHECKSIGADD" && asm[i + 1] !== "CHECKSIG")) {
|
|
154
|
+
throw new Error("Expected CHECKSIGADD or CHECKSIG after pubkey");
|
|
155
|
+
}
|
|
156
|
+
i++; // Skip the CHECKSIGADD op
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
// Last operation should be NUMEQUAL
|
|
160
|
+
if (i === asm.length - 1) {
|
|
161
|
+
if (op !== "NUMEQUAL") {
|
|
162
|
+
throw new Error("Expected NUMEQUAL at end of script");
|
|
163
|
+
}
|
|
164
|
+
foundNumEqual = true;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (!foundNumEqual) {
|
|
168
|
+
throw new Error("Missing NUMEQUAL operation");
|
|
169
|
+
}
|
|
170
|
+
if (pubkeys.length === 0) {
|
|
171
|
+
throw new Error("Invalid script: must have at least 1 pubkey");
|
|
172
|
+
}
|
|
173
|
+
// Verify the script by re-encoding and comparing
|
|
174
|
+
const reconstructed = encode({
|
|
175
|
+
pubkeys,
|
|
176
|
+
type: MultisigType.CHECKSIGADD,
|
|
177
|
+
});
|
|
178
|
+
if (base_1.hex.encode(reconstructed.script) !== base_1.hex.encode(script)) {
|
|
179
|
+
throw new Error("Invalid script format: script reconstruction mismatch");
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
type: TapscriptType.Multisig,
|
|
183
|
+
params: { pubkeys, type: MultisigType.CHECKSIGADD },
|
|
184
|
+
script,
|
|
185
|
+
witnessSize: () => pubkeys.length * 64,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
// <pubkey> CHECKSIGVERIFY <pubkey> CHECKSIG
|
|
189
|
+
function decodeChecksig(script) {
|
|
190
|
+
const asm = script_1.Script.decode(script);
|
|
191
|
+
const pubkeys = [];
|
|
192
|
+
// Parse through ASM operations
|
|
193
|
+
for (let i = 0; i < asm.length; i++) {
|
|
194
|
+
const op = asm[i];
|
|
195
|
+
// If it's a data push, it should be a 32-byte pubkey
|
|
196
|
+
if (typeof op !== "string" && typeof op !== "number") {
|
|
197
|
+
if (op.length !== 32) {
|
|
198
|
+
throw new Error(`Invalid pubkey length: expected 32, got ${op.length}`);
|
|
199
|
+
}
|
|
200
|
+
pubkeys.push(op);
|
|
201
|
+
// Check next operation
|
|
202
|
+
if (i + 1 >= asm.length) {
|
|
203
|
+
throw new Error("Unexpected end of script");
|
|
204
|
+
}
|
|
205
|
+
const nextOp = asm[i + 1];
|
|
206
|
+
if (nextOp !== "CHECKSIGVERIFY" && nextOp !== "CHECKSIG") {
|
|
207
|
+
throw new Error("Expected CHECKSIGVERIFY or CHECKSIG after pubkey");
|
|
208
|
+
}
|
|
209
|
+
// Last operation must be CHECKSIG, not CHECKSIGVERIFY
|
|
210
|
+
if (i === asm.length - 2 && nextOp !== "CHECKSIG") {
|
|
211
|
+
throw new Error("Last operation must be CHECKSIG");
|
|
212
|
+
}
|
|
213
|
+
i++; // Skip the CHECKSIG/CHECKSIGVERIFY op
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (pubkeys.length === 0) {
|
|
218
|
+
throw new Error("Invalid script: must have at least 1 pubkey");
|
|
219
|
+
}
|
|
220
|
+
// Verify the script by re-encoding and comparing
|
|
221
|
+
const reconstructed = encode({ pubkeys, type: MultisigType.CHECKSIG });
|
|
222
|
+
if (base_1.hex.encode(reconstructed.script) !== base_1.hex.encode(script)) {
|
|
223
|
+
throw new Error("Invalid script format: script reconstruction mismatch");
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
type: TapscriptType.Multisig,
|
|
227
|
+
params: { pubkeys, type: MultisigType.CHECKSIG },
|
|
228
|
+
script,
|
|
229
|
+
witnessSize: () => pubkeys.length * 64,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
function is(tapscript) {
|
|
233
|
+
return tapscript.type === TapscriptType.Multisig;
|
|
234
|
+
}
|
|
235
|
+
MultisigTapscript.is = is;
|
|
236
|
+
})(MultisigTapscript || (exports.MultisigTapscript = MultisigTapscript = {}));
|
|
237
|
+
/**
|
|
238
|
+
* Implements a relative timelock script that requires all specified pubkeys to sign
|
|
239
|
+
* after the relative timelock has expired. The timelock can be specified in blocks or seconds.
|
|
240
|
+
*
|
|
241
|
+
* This is the standard exit closure and it is also used for the sweep closure in vtxo trees.
|
|
242
|
+
*/
|
|
243
|
+
var CSVMultisigTapscript;
|
|
244
|
+
(function (CSVMultisigTapscript) {
|
|
245
|
+
function encode(params) {
|
|
246
|
+
for (const pubkey of params.pubkeys) {
|
|
247
|
+
if (pubkey.length !== 32) {
|
|
248
|
+
throw new Error(`Invalid pubkey length: expected 32, got ${pubkey.length}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
const sequence = (0, script_1.ScriptNum)().encode(BigInt(bip68.encode(params.timelock.type === "blocks"
|
|
252
|
+
? { blocks: Number(params.timelock.value) }
|
|
253
|
+
: { seconds: Number(params.timelock.value) })));
|
|
254
|
+
const asm = [sequence, "CHECKSEQUENCEVERIFY", "DROP"];
|
|
255
|
+
const multisigScript = MultisigTapscript.encode(params);
|
|
256
|
+
const script = new Uint8Array([
|
|
257
|
+
...script_1.Script.encode(asm),
|
|
258
|
+
...multisigScript.script,
|
|
259
|
+
]);
|
|
260
|
+
return {
|
|
261
|
+
type: TapscriptType.CSVMultisig,
|
|
262
|
+
params,
|
|
263
|
+
script,
|
|
264
|
+
witnessSize: () => params.pubkeys.length * 64,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
CSVMultisigTapscript.encode = encode;
|
|
268
|
+
function decode(script) {
|
|
269
|
+
if (script.length === 0) {
|
|
270
|
+
throw new Error("Failed to decode: script is empty");
|
|
271
|
+
}
|
|
272
|
+
const asm = script_1.Script.decode(script);
|
|
273
|
+
if (asm.length < 3) {
|
|
274
|
+
throw new Error(`Invalid script: too short (expected at least 3)`);
|
|
275
|
+
}
|
|
276
|
+
const sequence = asm[0];
|
|
277
|
+
if (typeof sequence === "string" || typeof sequence === "number") {
|
|
278
|
+
throw new Error("Invalid script: expected sequence number");
|
|
279
|
+
}
|
|
280
|
+
if (asm[1] !== "CHECKSEQUENCEVERIFY" || asm[2] !== "DROP") {
|
|
281
|
+
throw new Error("Invalid script: expected CHECKSEQUENCEVERIFY DROP");
|
|
282
|
+
}
|
|
283
|
+
const multisigScript = new Uint8Array(script_1.Script.encode(asm.slice(3)));
|
|
284
|
+
let multisig;
|
|
285
|
+
try {
|
|
286
|
+
multisig = MultisigTapscript.decode(multisigScript);
|
|
287
|
+
}
|
|
288
|
+
catch (error) {
|
|
289
|
+
throw new Error(`Invalid multisig script: ${error instanceof Error ? error.message : String(error)}`);
|
|
290
|
+
}
|
|
291
|
+
const sequenceNum = Number((0, script_1.ScriptNum)().decode(sequence));
|
|
292
|
+
const decodedTimelock = bip68.decode(sequenceNum);
|
|
293
|
+
const timelock = decodedTimelock.blocks !== undefined
|
|
294
|
+
? { type: "blocks", value: BigInt(decodedTimelock.blocks) }
|
|
295
|
+
: { type: "seconds", value: BigInt(decodedTimelock.seconds) };
|
|
296
|
+
const reconstructed = encode({
|
|
297
|
+
timelock,
|
|
298
|
+
...multisig.params,
|
|
299
|
+
});
|
|
300
|
+
if (base_1.hex.encode(reconstructed.script) !== base_1.hex.encode(script)) {
|
|
301
|
+
throw new Error("Invalid script format: script reconstruction mismatch");
|
|
302
|
+
}
|
|
303
|
+
return {
|
|
304
|
+
type: TapscriptType.CSVMultisig,
|
|
305
|
+
params: {
|
|
306
|
+
timelock,
|
|
307
|
+
...multisig.params,
|
|
308
|
+
},
|
|
309
|
+
script,
|
|
310
|
+
witnessSize: () => multisig.params.pubkeys.length * 64,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
CSVMultisigTapscript.decode = decode;
|
|
314
|
+
function is(tapscript) {
|
|
315
|
+
return tapscript.type === TapscriptType.CSVMultisig;
|
|
316
|
+
}
|
|
317
|
+
CSVMultisigTapscript.is = is;
|
|
318
|
+
})(CSVMultisigTapscript || (exports.CSVMultisigTapscript = CSVMultisigTapscript = {}));
|
|
319
|
+
/**
|
|
320
|
+
* Combines a condition script with an exit closure. The resulting script requires
|
|
321
|
+
* the condition to be met, followed by the standard exit closure requirements
|
|
322
|
+
* (timelock and signatures).
|
|
323
|
+
*/
|
|
324
|
+
var ConditionCSVMultisigTapscript;
|
|
325
|
+
(function (ConditionCSVMultisigTapscript) {
|
|
326
|
+
function encode(params) {
|
|
327
|
+
const script = new Uint8Array([
|
|
328
|
+
...params.conditionScript,
|
|
329
|
+
...script_1.Script.encode(["VERIFY"]),
|
|
330
|
+
...CSVMultisigTapscript.encode(params).script,
|
|
331
|
+
]);
|
|
332
|
+
return {
|
|
333
|
+
type: TapscriptType.ConditionCSVMultisig,
|
|
334
|
+
params,
|
|
335
|
+
script,
|
|
336
|
+
witnessSize: (conditionSize) => conditionSize + params.pubkeys.length * 64,
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
ConditionCSVMultisigTapscript.encode = encode;
|
|
340
|
+
function decode(script) {
|
|
341
|
+
if (script.length === 0) {
|
|
342
|
+
throw new Error("Failed to decode: script is empty");
|
|
343
|
+
}
|
|
344
|
+
const asm = script_1.Script.decode(script);
|
|
345
|
+
if (asm.length < 1) {
|
|
346
|
+
throw new Error(`Invalid script: too short (expected at least 1)`);
|
|
347
|
+
}
|
|
348
|
+
let verifyIndex = -1;
|
|
349
|
+
for (let i = asm.length - 1; i >= 0; i--) {
|
|
350
|
+
if (asm[i] === "VERIFY") {
|
|
351
|
+
verifyIndex = i;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
if (verifyIndex === -1) {
|
|
355
|
+
throw new Error("Invalid script: missing VERIFY operation");
|
|
356
|
+
}
|
|
357
|
+
const conditionScript = new Uint8Array(script_1.Script.encode(asm.slice(0, verifyIndex)));
|
|
358
|
+
const csvMultisigScript = new Uint8Array(script_1.Script.encode(asm.slice(verifyIndex + 1)));
|
|
359
|
+
let csvMultisig;
|
|
360
|
+
try {
|
|
361
|
+
csvMultisig = CSVMultisigTapscript.decode(csvMultisigScript);
|
|
362
|
+
}
|
|
363
|
+
catch (error) {
|
|
364
|
+
throw new Error(`Invalid CSV multisig script: ${error instanceof Error ? error.message : String(error)}`);
|
|
365
|
+
}
|
|
366
|
+
const reconstructed = encode({
|
|
367
|
+
conditionScript,
|
|
368
|
+
...csvMultisig.params,
|
|
369
|
+
});
|
|
370
|
+
if (base_1.hex.encode(reconstructed.script) !== base_1.hex.encode(script)) {
|
|
371
|
+
throw new Error("Invalid script format: script reconstruction mismatch");
|
|
372
|
+
}
|
|
373
|
+
return {
|
|
374
|
+
type: TapscriptType.ConditionCSVMultisig,
|
|
375
|
+
params: {
|
|
376
|
+
conditionScript,
|
|
377
|
+
...csvMultisig.params,
|
|
378
|
+
},
|
|
379
|
+
script,
|
|
380
|
+
witnessSize: (conditionSize) => conditionSize + csvMultisig.params.pubkeys.length * 64,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
ConditionCSVMultisigTapscript.decode = decode;
|
|
384
|
+
function is(tapscript) {
|
|
385
|
+
return tapscript.type === TapscriptType.ConditionCSVMultisig;
|
|
386
|
+
}
|
|
387
|
+
ConditionCSVMultisigTapscript.is = is;
|
|
388
|
+
})(ConditionCSVMultisigTapscript || (exports.ConditionCSVMultisigTapscript = ConditionCSVMultisigTapscript = {}));
|
|
389
|
+
/**
|
|
390
|
+
* Combines a condition script with a forfeit closure. The resulting script requires
|
|
391
|
+
* the condition to be met, followed by the standard forfeit closure requirements
|
|
392
|
+
* (multi-signature).
|
|
393
|
+
*/
|
|
394
|
+
var ConditionMultisigTapscript;
|
|
395
|
+
(function (ConditionMultisigTapscript) {
|
|
396
|
+
function encode(params) {
|
|
397
|
+
const script = new Uint8Array([
|
|
398
|
+
...params.conditionScript,
|
|
399
|
+
...script_1.Script.encode(["VERIFY"]),
|
|
400
|
+
...MultisigTapscript.encode(params).script,
|
|
401
|
+
]);
|
|
402
|
+
return {
|
|
403
|
+
type: TapscriptType.ConditionMultisig,
|
|
404
|
+
params,
|
|
405
|
+
script,
|
|
406
|
+
witnessSize: (conditionSize) => conditionSize + params.pubkeys.length * 64,
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
ConditionMultisigTapscript.encode = encode;
|
|
410
|
+
function decode(script) {
|
|
411
|
+
if (script.length === 0) {
|
|
412
|
+
throw new Error("Failed to decode: script is empty");
|
|
413
|
+
}
|
|
414
|
+
const asm = script_1.Script.decode(script);
|
|
415
|
+
if (asm.length < 1) {
|
|
416
|
+
throw new Error(`Invalid script: too short (expected at least 1)`);
|
|
417
|
+
}
|
|
418
|
+
let verifyIndex = -1;
|
|
419
|
+
for (let i = asm.length - 1; i >= 0; i--) {
|
|
420
|
+
if (asm[i] === "VERIFY") {
|
|
421
|
+
verifyIndex = i;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
if (verifyIndex === -1) {
|
|
425
|
+
throw new Error("Invalid script: missing VERIFY operation");
|
|
426
|
+
}
|
|
427
|
+
const conditionScript = new Uint8Array(script_1.Script.encode(asm.slice(0, verifyIndex)));
|
|
428
|
+
const multisigScript = new Uint8Array(script_1.Script.encode(asm.slice(verifyIndex + 1)));
|
|
429
|
+
let multisig;
|
|
430
|
+
try {
|
|
431
|
+
multisig = MultisigTapscript.decode(multisigScript);
|
|
432
|
+
}
|
|
433
|
+
catch (error) {
|
|
434
|
+
throw new Error(`Invalid multisig script: ${error instanceof Error ? error.message : String(error)}`);
|
|
435
|
+
}
|
|
436
|
+
const reconstructed = encode({
|
|
437
|
+
conditionScript,
|
|
438
|
+
...multisig.params,
|
|
439
|
+
});
|
|
440
|
+
if (base_1.hex.encode(reconstructed.script) !== base_1.hex.encode(script)) {
|
|
441
|
+
throw new Error("Invalid script format: script reconstruction mismatch");
|
|
442
|
+
}
|
|
443
|
+
return {
|
|
444
|
+
type: TapscriptType.ConditionMultisig,
|
|
445
|
+
params: {
|
|
446
|
+
conditionScript,
|
|
447
|
+
...multisig.params,
|
|
448
|
+
},
|
|
449
|
+
script,
|
|
450
|
+
witnessSize: (conditionSize) => conditionSize + multisig.params.pubkeys.length * 64,
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
ConditionMultisigTapscript.decode = decode;
|
|
454
|
+
function is(tapscript) {
|
|
455
|
+
return tapscript.type === TapscriptType.ConditionMultisig;
|
|
456
|
+
}
|
|
457
|
+
ConditionMultisigTapscript.is = is;
|
|
458
|
+
})(ConditionMultisigTapscript || (exports.ConditionMultisigTapscript = ConditionMultisigTapscript = {}));
|
|
459
|
+
/**
|
|
460
|
+
* Implements an absolute timelock (CLTV) script combined with a forfeit closure.
|
|
461
|
+
* The script requires waiting until a specific block height/timestamp before the
|
|
462
|
+
* forfeit closure conditions can be met.
|
|
463
|
+
*/
|
|
464
|
+
var CLTVMultisigTapscript;
|
|
465
|
+
(function (CLTVMultisigTapscript) {
|
|
466
|
+
function encode(params) {
|
|
467
|
+
const locktime = (0, script_1.ScriptNum)().encode(params.absoluteTimelock);
|
|
468
|
+
const asm = [locktime, "CHECKLOCKTIMEVERIFY", "DROP"];
|
|
469
|
+
const timelockedScript = script_1.Script.encode(asm);
|
|
470
|
+
const script = new Uint8Array([
|
|
471
|
+
...timelockedScript,
|
|
472
|
+
...MultisigTapscript.encode(params).script,
|
|
473
|
+
]);
|
|
474
|
+
return {
|
|
475
|
+
type: TapscriptType.CLTVMultisig,
|
|
476
|
+
params,
|
|
477
|
+
script,
|
|
478
|
+
witnessSize: () => params.pubkeys.length * 64,
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
CLTVMultisigTapscript.encode = encode;
|
|
482
|
+
function decode(script) {
|
|
483
|
+
if (script.length === 0) {
|
|
484
|
+
throw new Error("Failed to decode: script is empty");
|
|
485
|
+
}
|
|
486
|
+
const asm = script_1.Script.decode(script);
|
|
487
|
+
if (asm.length < 3) {
|
|
488
|
+
throw new Error(`Invalid script: too short (expected at least 3)`);
|
|
489
|
+
}
|
|
490
|
+
const locktime = asm[0];
|
|
491
|
+
if (typeof locktime === "string" || typeof locktime === "number") {
|
|
492
|
+
throw new Error("Invalid script: expected locktime number");
|
|
493
|
+
}
|
|
494
|
+
if (asm[1] !== "CHECKLOCKTIMEVERIFY" || asm[2] !== "DROP") {
|
|
495
|
+
throw new Error("Invalid script: expected CHECKLOCKTIMEVERIFY DROP");
|
|
496
|
+
}
|
|
497
|
+
const multisigScript = new Uint8Array(script_1.Script.encode(asm.slice(3)));
|
|
498
|
+
let multisig;
|
|
499
|
+
try {
|
|
500
|
+
multisig = MultisigTapscript.decode(multisigScript);
|
|
501
|
+
}
|
|
502
|
+
catch (error) {
|
|
503
|
+
throw new Error(`Invalid multisig script: ${error instanceof Error ? error.message : String(error)}`);
|
|
504
|
+
}
|
|
505
|
+
const absoluteTimelock = (0, script_1.ScriptNum)().decode(locktime);
|
|
506
|
+
const reconstructed = encode({
|
|
507
|
+
absoluteTimelock,
|
|
508
|
+
...multisig.params,
|
|
509
|
+
});
|
|
510
|
+
if (base_1.hex.encode(reconstructed.script) !== base_1.hex.encode(script)) {
|
|
511
|
+
throw new Error("Invalid script format: script reconstruction mismatch");
|
|
512
|
+
}
|
|
513
|
+
return {
|
|
514
|
+
type: TapscriptType.CLTVMultisig,
|
|
515
|
+
params: {
|
|
516
|
+
absoluteTimelock,
|
|
517
|
+
...multisig.params,
|
|
518
|
+
},
|
|
519
|
+
script,
|
|
520
|
+
witnessSize: () => multisig.params.pubkeys.length * 64,
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
CLTVMultisigTapscript.decode = decode;
|
|
524
|
+
function is(tapscript) {
|
|
525
|
+
return tapscript.type === TapscriptType.CLTVMultisig;
|
|
526
|
+
}
|
|
527
|
+
CLTVMultisigTapscript.is = is;
|
|
528
|
+
})(CLTVMultisigTapscript || (exports.CLTVMultisigTapscript = CLTVMultisigTapscript = {}));
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VHTLC = void 0;
|
|
4
|
+
const btc_signer_1 = require("@scure/btc-signer");
|
|
5
|
+
const tapscript_1 = require("./tapscript");
|
|
6
|
+
const base_1 = require("@scure/base");
|
|
7
|
+
const base_2 = require("./base");
|
|
8
|
+
// VHTLC is an Hashed Timelock Contract VtxoScript implementation
|
|
9
|
+
// - claim (preimage + receiver)
|
|
10
|
+
// - refund (sender + receiver + server)
|
|
11
|
+
// - refundWithoutReceiver (at refundLocktime, sender + receiver + server)
|
|
12
|
+
// - unilateralClaim (preimage + receiver after unilateralClaimDelay)
|
|
13
|
+
// - unilateralRefund (sender + receiver after unilateralRefundDelay)
|
|
14
|
+
// - unilateralRefundWithoutReceiver (sender after unilateralRefundWithoutReceiverDelay)
|
|
15
|
+
var VHTLC;
|
|
16
|
+
(function (VHTLC) {
|
|
17
|
+
class Script extends base_2.VtxoScript {
|
|
18
|
+
constructor(options) {
|
|
19
|
+
const { sender, receiver, server, preimageHash, refundLocktime, unilateralClaimDelay, unilateralRefundDelay, unilateralRefundWithoutReceiverDelay, } = options;
|
|
20
|
+
const conditionScript = preimageConditionScript(preimageHash);
|
|
21
|
+
const claimScript = tapscript_1.ConditionMultisigTapscript.encode({
|
|
22
|
+
conditionScript,
|
|
23
|
+
pubkeys: [receiver, server],
|
|
24
|
+
}).script;
|
|
25
|
+
const refundScript = tapscript_1.MultisigTapscript.encode({
|
|
26
|
+
pubkeys: [sender, receiver, server],
|
|
27
|
+
}).script;
|
|
28
|
+
const refundWithoutReceiverScript = tapscript_1.CLTVMultisigTapscript.encode({
|
|
29
|
+
absoluteTimelock: refundLocktime,
|
|
30
|
+
pubkeys: [sender, server],
|
|
31
|
+
}).script;
|
|
32
|
+
const unilateralClaimScript = tapscript_1.ConditionCSVMultisigTapscript.encode({
|
|
33
|
+
conditionScript,
|
|
34
|
+
timelock: unilateralClaimDelay,
|
|
35
|
+
pubkeys: [receiver],
|
|
36
|
+
}).script;
|
|
37
|
+
const unilateralRefundScript = tapscript_1.CSVMultisigTapscript.encode({
|
|
38
|
+
timelock: unilateralRefundDelay,
|
|
39
|
+
pubkeys: [sender, receiver],
|
|
40
|
+
}).script;
|
|
41
|
+
const unilateralRefundWithoutReceiverScript = tapscript_1.CSVMultisigTapscript.encode({
|
|
42
|
+
timelock: unilateralRefundWithoutReceiverDelay,
|
|
43
|
+
pubkeys: [sender],
|
|
44
|
+
}).script;
|
|
45
|
+
super([
|
|
46
|
+
claimScript,
|
|
47
|
+
refundScript,
|
|
48
|
+
refundWithoutReceiverScript,
|
|
49
|
+
unilateralClaimScript,
|
|
50
|
+
unilateralRefundScript,
|
|
51
|
+
unilateralRefundWithoutReceiverScript,
|
|
52
|
+
]);
|
|
53
|
+
this.options = options;
|
|
54
|
+
this.claimScript = base_1.hex.encode(claimScript);
|
|
55
|
+
this.refundScript = base_1.hex.encode(refundScript);
|
|
56
|
+
this.refundWithoutReceiverScript = base_1.hex.encode(refundWithoutReceiverScript);
|
|
57
|
+
this.unilateralClaimScript = base_1.hex.encode(unilateralClaimScript);
|
|
58
|
+
this.unilateralRefundScript = base_1.hex.encode(unilateralRefundScript);
|
|
59
|
+
this.unilateralRefundWithoutReceiverScript = base_1.hex.encode(unilateralRefundWithoutReceiverScript);
|
|
60
|
+
}
|
|
61
|
+
claim() {
|
|
62
|
+
return this.findLeaf(this.claimScript);
|
|
63
|
+
}
|
|
64
|
+
refund() {
|
|
65
|
+
return this.findLeaf(this.refundScript);
|
|
66
|
+
}
|
|
67
|
+
refundWithoutReceiver() {
|
|
68
|
+
return this.findLeaf(this.refundWithoutReceiverScript);
|
|
69
|
+
}
|
|
70
|
+
unilateralClaim() {
|
|
71
|
+
return this.findLeaf(this.unilateralClaimScript);
|
|
72
|
+
}
|
|
73
|
+
unilateralRefund() {
|
|
74
|
+
return this.findLeaf(this.unilateralRefundScript);
|
|
75
|
+
}
|
|
76
|
+
unilateralRefundWithoutReceiver() {
|
|
77
|
+
return this.findLeaf(this.unilateralRefundWithoutReceiverScript);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
VHTLC.Script = Script;
|
|
81
|
+
})(VHTLC || (exports.VHTLC = VHTLC = {}));
|
|
82
|
+
function preimageConditionScript(preimageHash) {
|
|
83
|
+
return btc_signer_1.Script.encode(["HASH160", preimageHash, "EQUAL"]);
|
|
84
|
+
}
|