@arkade-os/sdk 0.3.0-alpha.7 → 0.3.0
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 +99 -14
- package/dist/cjs/adapters/expo.js +8 -0
- package/dist/cjs/arknote/index.js +3 -3
- package/dist/cjs/forfeit.js +2 -2
- package/dist/cjs/identity/singleKey.js +8 -8
- package/dist/cjs/index.js +14 -5
- package/dist/cjs/{bip322 → intent}/index.js +38 -61
- package/dist/cjs/musig2/index.js +2 -1
- package/dist/cjs/musig2/nonces.js +4 -0
- package/dist/cjs/providers/ark.js +76 -45
- package/dist/cjs/providers/errors.js +59 -0
- package/dist/cjs/providers/expoArk.js +82 -0
- package/dist/cjs/providers/expoIndexer.js +105 -0
- package/dist/cjs/providers/expoUtils.js +124 -0
- package/dist/cjs/providers/indexer.js +3 -1
- package/dist/cjs/providers/onchain.js +19 -20
- package/dist/cjs/repositories/walletRepository.js +64 -28
- package/dist/cjs/script/base.js +15 -7
- package/dist/cjs/script/tapscript.js +20 -21
- package/dist/cjs/script/vhtlc.js +2 -2
- package/dist/cjs/tree/signingSession.js +44 -11
- package/dist/cjs/tree/txTree.js +3 -4
- package/dist/cjs/tree/validation.js +2 -3
- package/dist/cjs/utils/arkTransaction.js +118 -15
- package/dist/cjs/utils/transaction.js +28 -0
- package/dist/cjs/utils/unknownFields.js +7 -7
- package/dist/cjs/wallet/index.js +1 -1
- package/dist/cjs/wallet/onchain.js +6 -7
- package/dist/cjs/wallet/serviceWorker/response.js +32 -0
- package/dist/cjs/wallet/serviceWorker/utils.js +2 -9
- package/dist/cjs/wallet/serviceWorker/wallet.js +7 -8
- package/dist/cjs/wallet/serviceWorker/worker.js +48 -32
- package/dist/cjs/wallet/unroll.js +7 -9
- package/dist/cjs/wallet/utils.js +20 -0
- package/dist/cjs/wallet/vtxo-manager.js +323 -0
- package/dist/cjs/wallet/wallet.js +165 -174
- package/dist/esm/adapters/expo.js +3 -0
- package/dist/esm/arknote/index.js +2 -2
- package/dist/esm/forfeit.js +1 -1
- package/dist/esm/identity/singleKey.js +9 -9
- package/dist/esm/index.js +14 -10
- package/dist/esm/{bip322 → intent}/index.js +32 -54
- package/dist/esm/musig2/index.js +1 -1
- package/dist/esm/musig2/nonces.js +3 -0
- package/dist/esm/providers/ark.js +76 -45
- package/dist/esm/providers/errors.js +54 -0
- package/dist/esm/providers/expoArk.js +78 -0
- package/dist/esm/providers/expoIndexer.js +101 -0
- package/dist/esm/providers/expoUtils.js +87 -0
- package/dist/esm/providers/indexer.js +3 -1
- package/dist/esm/providers/onchain.js +19 -20
- package/dist/esm/repositories/walletRepository.js +64 -28
- package/dist/esm/script/base.js +12 -4
- package/dist/esm/script/tapscript.js +1 -2
- package/dist/esm/script/vhtlc.js +1 -1
- package/dist/esm/tree/signingSession.js +45 -12
- package/dist/esm/tree/txTree.js +3 -4
- package/dist/esm/tree/validation.js +2 -3
- package/dist/esm/utils/arkTransaction.js +110 -9
- package/dist/esm/utils/transaction.js +24 -0
- package/dist/esm/utils/unknownFields.js +3 -3
- package/dist/esm/wallet/index.js +1 -1
- package/dist/esm/wallet/onchain.js +3 -4
- package/dist/esm/wallet/serviceWorker/response.js +32 -0
- package/dist/esm/wallet/serviceWorker/utils.js +1 -8
- package/dist/esm/wallet/serviceWorker/wallet.js +8 -9
- package/dist/esm/wallet/serviceWorker/worker.js +49 -33
- package/dist/esm/wallet/unroll.js +5 -7
- package/dist/esm/wallet/utils.js +16 -0
- package/dist/esm/wallet/vtxo-manager.js +317 -0
- package/dist/esm/wallet/wallet.js +159 -168
- package/dist/types/adapters/expo.d.ts +4 -0
- package/dist/types/arknote/index.d.ts +1 -1
- package/dist/types/forfeit.d.ts +2 -2
- package/dist/types/identity/index.d.ts +2 -2
- package/dist/types/identity/singleKey.d.ts +2 -2
- package/dist/types/index.d.ts +11 -9
- package/dist/types/intent/index.d.ts +41 -0
- package/dist/types/musig2/index.d.ts +1 -1
- package/dist/types/musig2/nonces.d.ts +1 -0
- package/dist/types/providers/ark.d.ts +197 -27
- package/dist/types/providers/errors.d.ts +13 -0
- package/dist/types/providers/expoArk.d.ts +22 -0
- package/dist/types/providers/expoIndexer.d.ts +18 -0
- package/dist/types/providers/expoUtils.d.ts +18 -0
- package/dist/types/providers/indexer.d.ts +8 -8
- package/dist/types/providers/onchain.d.ts +6 -2
- package/dist/types/repositories/walletRepository.d.ts +9 -5
- package/dist/types/script/base.d.ts +5 -2
- package/dist/types/tree/signingSession.d.ts +16 -11
- package/dist/types/utils/anchor.d.ts +2 -2
- package/dist/types/utils/arkTransaction.d.ts +15 -5
- package/dist/types/utils/transaction.d.ts +13 -0
- package/dist/types/utils/unknownFields.d.ts +4 -4
- package/dist/types/wallet/index.d.ts +47 -7
- package/dist/types/wallet/onchain.d.ts +1 -1
- package/dist/types/wallet/serviceWorker/response.d.ts +16 -2
- package/dist/types/wallet/serviceWorker/utils.d.ts +1 -2
- package/dist/types/wallet/serviceWorker/wallet.d.ts +2 -2
- package/dist/types/wallet/serviceWorker/worker.d.ts +7 -1
- package/dist/types/wallet/unroll.d.ts +1 -1
- package/dist/types/wallet/utils.d.ts +3 -0
- package/dist/types/wallet/vtxo-manager.d.ts +179 -0
- package/dist/types/wallet/wallet.d.ts +17 -5
- package/package.json +11 -3
- package/dist/cjs/bip322/errors.js +0 -13
- package/dist/esm/bip322/errors.js +0 -9
- package/dist/types/bip322/errors.d.ts +0 -6
- package/dist/types/bip322/index.d.ts +0 -57
|
@@ -21,9 +21,10 @@ exports.ESPLORA_URL = {
|
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
23
|
class EsploraProvider {
|
|
24
|
-
constructor(baseUrl) {
|
|
24
|
+
constructor(baseUrl, opts) {
|
|
25
25
|
this.baseUrl = baseUrl;
|
|
26
|
-
this.
|
|
26
|
+
this.pollingInterval = opts?.pollingInterval ?? 15000;
|
|
27
|
+
this.forcePolling = opts?.forcePolling ?? false;
|
|
27
28
|
}
|
|
28
29
|
async getCoins(address) {
|
|
29
30
|
const response = await fetch(`${this.baseUrl}/address/${address}/utxo`);
|
|
@@ -94,13 +95,9 @@ class EsploraProvider {
|
|
|
94
95
|
let intervalId = null;
|
|
95
96
|
const wsUrl = this.baseUrl.replace(/^http(s)?:/, "ws$1:") + "/v1/ws";
|
|
96
97
|
const poll = async () => {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
// websocket is not reliable, so we will fallback to polling
|
|
101
|
-
const pollingInterval = 5000; // 5 seconds
|
|
102
|
-
const getAllTxs = () => {
|
|
103
|
-
return Promise.all(addresses.map((address) => this.getTransactions(address))).then((txArrays) => txArrays.flat());
|
|
98
|
+
const getAllTxs = async () => {
|
|
99
|
+
const txArrays = await Promise.all(addresses.map((address) => this.getTransactions(address)));
|
|
100
|
+
return txArrays.flat();
|
|
104
101
|
};
|
|
105
102
|
// initial fetch to get existing transactions
|
|
106
103
|
const initialTxs = await getAllTxs();
|
|
@@ -125,9 +122,19 @@ class EsploraProvider {
|
|
|
125
122
|
catch (error) {
|
|
126
123
|
console.error("Error in polling mechanism:", error);
|
|
127
124
|
}
|
|
128
|
-
}, pollingInterval);
|
|
125
|
+
}, this.pollingInterval);
|
|
129
126
|
};
|
|
130
127
|
let ws = null;
|
|
128
|
+
const stopFunc = () => {
|
|
129
|
+
if (ws)
|
|
130
|
+
ws.close();
|
|
131
|
+
if (intervalId)
|
|
132
|
+
clearInterval(intervalId);
|
|
133
|
+
};
|
|
134
|
+
if (this.forcePolling) {
|
|
135
|
+
await poll();
|
|
136
|
+
return stopFunc;
|
|
137
|
+
}
|
|
131
138
|
try {
|
|
132
139
|
ws = new WebSocket(wsUrl);
|
|
133
140
|
ws.addEventListener("open", () => {
|
|
@@ -174,13 +181,6 @@ class EsploraProvider {
|
|
|
174
181
|
// if websocket is not available, fallback to polling
|
|
175
182
|
await poll();
|
|
176
183
|
}
|
|
177
|
-
const stopFunc = () => {
|
|
178
|
-
if (ws && ws.readyState === WebSocket.OPEN)
|
|
179
|
-
ws.close();
|
|
180
|
-
if (intervalId)
|
|
181
|
-
clearInterval(intervalId);
|
|
182
|
-
this.polling = false;
|
|
183
|
-
};
|
|
184
184
|
return stopFunc;
|
|
185
185
|
}
|
|
186
186
|
async getChainTip() {
|
|
@@ -249,8 +249,7 @@ const isExplorerTransaction = (tx) => {
|
|
|
249
249
|
return (typeof tx.txid === "string" &&
|
|
250
250
|
Array.isArray(tx.vout) &&
|
|
251
251
|
tx.vout.every((vout) => typeof vout.scriptpubkey_address === "string" &&
|
|
252
|
-
typeof vout.value === "
|
|
252
|
+
typeof vout.value === "number") &&
|
|
253
253
|
typeof tx.status === "object" &&
|
|
254
|
-
typeof tx.status.confirmed === "boolean"
|
|
255
|
-
typeof tx.status.block_time === "number");
|
|
254
|
+
typeof tx.status.confirmed === "boolean");
|
|
256
255
|
};
|
|
@@ -15,7 +15,14 @@ const serializeVtxo = (v) => ({
|
|
|
15
15
|
tapTree: toHex(v.tapTree),
|
|
16
16
|
forfeitTapLeafScript: serializeTapLeaf(v.forfeitTapLeafScript),
|
|
17
17
|
intentTapLeafScript: serializeTapLeaf(v.intentTapLeafScript),
|
|
18
|
-
extraWitness: v.extraWitness?.map(
|
|
18
|
+
extraWitness: v.extraWitness?.map(toHex),
|
|
19
|
+
});
|
|
20
|
+
const serializeUtxo = (u) => ({
|
|
21
|
+
...u,
|
|
22
|
+
tapTree: toHex(u.tapTree),
|
|
23
|
+
forfeitTapLeafScript: serializeTapLeaf(u.forfeitTapLeafScript),
|
|
24
|
+
intentTapLeafScript: serializeTapLeaf(u.intentTapLeafScript),
|
|
25
|
+
extraWitness: u.extraWitness?.map(toHex),
|
|
19
26
|
});
|
|
20
27
|
const deserializeTapLeaf = (t) => {
|
|
21
28
|
const cb = btc_signer_1.TaprootControlBlock.decode(fromHex(t.cb));
|
|
@@ -27,13 +34,21 @@ const deserializeVtxo = (o) => ({
|
|
|
27
34
|
tapTree: fromHex(o.tapTree),
|
|
28
35
|
forfeitTapLeafScript: deserializeTapLeaf(o.forfeitTapLeafScript),
|
|
29
36
|
intentTapLeafScript: deserializeTapLeaf(o.intentTapLeafScript),
|
|
30
|
-
extraWitness: o.extraWitness?.map(
|
|
37
|
+
extraWitness: o.extraWitness?.map(fromHex),
|
|
38
|
+
});
|
|
39
|
+
const deserializeUtxo = (o) => ({
|
|
40
|
+
...o,
|
|
41
|
+
tapTree: fromHex(o.tapTree),
|
|
42
|
+
forfeitTapLeafScript: deserializeTapLeaf(o.forfeitTapLeafScript),
|
|
43
|
+
intentTapLeafScript: deserializeTapLeaf(o.intentTapLeafScript),
|
|
44
|
+
extraWitness: o.extraWitness?.map(fromHex),
|
|
31
45
|
});
|
|
32
46
|
class WalletRepositoryImpl {
|
|
33
47
|
constructor(storage) {
|
|
34
48
|
this.storage = storage;
|
|
35
49
|
this.cache = {
|
|
36
50
|
vtxos: new Map(),
|
|
51
|
+
utxos: new Map(),
|
|
37
52
|
transactions: new Map(),
|
|
38
53
|
walletState: null,
|
|
39
54
|
initialized: new Set(),
|
|
@@ -61,18 +76,6 @@ class WalletRepositoryImpl {
|
|
|
61
76
|
return [];
|
|
62
77
|
}
|
|
63
78
|
}
|
|
64
|
-
async saveVtxo(address, vtxo) {
|
|
65
|
-
const vtxos = await this.getVtxos(address);
|
|
66
|
-
const existing = vtxos.findIndex((v) => v.txid === vtxo.txid && v.vout === vtxo.vout);
|
|
67
|
-
if (existing !== -1) {
|
|
68
|
-
vtxos[existing] = vtxo;
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
vtxos.push(vtxo);
|
|
72
|
-
}
|
|
73
|
-
this.cache.vtxos.set(address, vtxos.slice());
|
|
74
|
-
await this.storage.setItem(`vtxos:${address}`, JSON.stringify(vtxos.map(serializeVtxo)));
|
|
75
|
-
}
|
|
76
79
|
async saveVtxos(address, vtxos) {
|
|
77
80
|
const storedVtxos = await this.getVtxos(address);
|
|
78
81
|
for (const vtxo of vtxos) {
|
|
@@ -98,6 +101,53 @@ class WalletRepositoryImpl {
|
|
|
98
101
|
this.cache.vtxos.set(address, []);
|
|
99
102
|
await this.storage.removeItem(`vtxos:${address}`);
|
|
100
103
|
}
|
|
104
|
+
async getUtxos(address) {
|
|
105
|
+
const cacheKey = `utxos:${address}`;
|
|
106
|
+
if (this.cache.utxos.has(address)) {
|
|
107
|
+
return this.cache.utxos.get(address);
|
|
108
|
+
}
|
|
109
|
+
const stored = await this.storage.getItem(cacheKey);
|
|
110
|
+
if (!stored) {
|
|
111
|
+
this.cache.utxos.set(address, []);
|
|
112
|
+
return [];
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
const parsed = JSON.parse(stored);
|
|
116
|
+
const utxos = parsed.map(deserializeUtxo);
|
|
117
|
+
this.cache.utxos.set(address, utxos.slice());
|
|
118
|
+
return utxos.slice();
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
console.error(`Failed to parse UTXOs for address ${address}:`, error);
|
|
122
|
+
this.cache.utxos.set(address, []);
|
|
123
|
+
return [];
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async saveUtxos(address, utxos) {
|
|
127
|
+
const storedUtxos = await this.getUtxos(address);
|
|
128
|
+
utxos.forEach((utxo) => {
|
|
129
|
+
const existing = storedUtxos.findIndex((u) => u.txid === utxo.txid && u.vout === utxo.vout);
|
|
130
|
+
if (existing !== -1) {
|
|
131
|
+
storedUtxos[existing] = utxo;
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
storedUtxos.push(utxo);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
this.cache.utxos.set(address, storedUtxos.slice());
|
|
138
|
+
await this.storage.setItem(`utxos:${address}`, JSON.stringify(storedUtxos.map(serializeUtxo)));
|
|
139
|
+
}
|
|
140
|
+
async removeUtxo(address, utxoId) {
|
|
141
|
+
const utxos = await this.getUtxos(address);
|
|
142
|
+
const [txid, vout] = utxoId.split(":");
|
|
143
|
+
const filtered = utxos.filter((v) => !(v.txid === txid && v.vout === parseInt(vout, 10)));
|
|
144
|
+
this.cache.utxos.set(address, filtered.slice());
|
|
145
|
+
await this.storage.setItem(`utxos:${address}`, JSON.stringify(filtered.map(serializeUtxo)));
|
|
146
|
+
}
|
|
147
|
+
async clearUtxos(address) {
|
|
148
|
+
this.cache.utxos.set(address, []);
|
|
149
|
+
await this.storage.removeItem(`utxos:${address}`);
|
|
150
|
+
}
|
|
101
151
|
async getTransactionHistory(address) {
|
|
102
152
|
const cacheKey = `tx:${address}`;
|
|
103
153
|
if (this.cache.transactions.has(address)) {
|
|
@@ -119,20 +169,6 @@ class WalletRepositoryImpl {
|
|
|
119
169
|
return [];
|
|
120
170
|
}
|
|
121
171
|
}
|
|
122
|
-
async saveTransaction(address, tx) {
|
|
123
|
-
const transactions = await this.getTransactionHistory(address);
|
|
124
|
-
const existing = transactions.findIndex((t) => t.key === tx.key);
|
|
125
|
-
if (existing !== -1) {
|
|
126
|
-
transactions[existing] = tx;
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
transactions.push(tx);
|
|
130
|
-
}
|
|
131
|
-
// Sort by createdAt descending
|
|
132
|
-
transactions.sort((a, b) => b.createdAt - a.createdAt);
|
|
133
|
-
this.cache.transactions.set(address, transactions);
|
|
134
|
-
await this.storage.setItem(`tx:${address}`, JSON.stringify(transactions));
|
|
135
|
-
}
|
|
136
172
|
async saveTransactions(address, txs) {
|
|
137
173
|
const storedTransactions = await this.getTransactionHistory(address);
|
|
138
174
|
for (const tx of txs) {
|
package/dist/cjs/script/base.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.VtxoScript = void 0;
|
|
3
|
+
exports.VtxoScript = exports.TapTreeCoder = void 0;
|
|
4
4
|
exports.scriptFromTapLeafScript = scriptFromTapLeafScript;
|
|
5
5
|
const btc_signer_1 = require("@scure/btc-signer");
|
|
6
6
|
const payment_js_1 = require("@scure/btc-signer/payment.js");
|
|
7
7
|
const psbt_js_1 = require("@scure/btc-signer/psbt.js");
|
|
8
|
-
const utils_js_1 = require("@scure/btc-signer/utils.js");
|
|
9
8
|
const base_1 = require("@scure/base");
|
|
10
9
|
const address_1 = require("./address");
|
|
11
10
|
const tapscript_1 = require("./tapscript");
|
|
12
|
-
|
|
11
|
+
exports.TapTreeCoder = psbt_js_1.PSBTOutput.tapTree[2];
|
|
13
12
|
function scriptFromTapLeafScript(leaf) {
|
|
14
13
|
return leaf[1].subarray(0, leaf[1].length - 1); // remove the version byte
|
|
15
14
|
}
|
|
@@ -23,14 +22,23 @@ function scriptFromTapLeafScript(leaf) {
|
|
|
23
22
|
*/
|
|
24
23
|
class VtxoScript {
|
|
25
24
|
static decode(tapTree) {
|
|
26
|
-
const leaves = TapTreeCoder.decode(tapTree);
|
|
25
|
+
const leaves = exports.TapTreeCoder.decode(tapTree);
|
|
27
26
|
const scripts = leaves.map((leaf) => leaf.script);
|
|
28
27
|
return new VtxoScript(scripts);
|
|
29
28
|
}
|
|
30
29
|
constructor(scripts) {
|
|
31
30
|
this.scripts = scripts;
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
// reverse the scripts if the number of scripts is odd
|
|
32
|
+
// this is to be compatible with arkd algorithm computing taproot tree from list of tapscripts
|
|
33
|
+
// the scripts must be reversed only HERE while we compute the tweaked public key
|
|
34
|
+
// but the original order should be preserved while encoding as taptree
|
|
35
|
+
// note: .slice().reverse() is used instead of .reverse() to avoid mutating the original array
|
|
36
|
+
const list = scripts.length % 2 !== 0 ? scripts.slice().reverse() : scripts;
|
|
37
|
+
const tapTree = (0, btc_signer_1.taprootListToTree)(list.map((script) => ({
|
|
38
|
+
script,
|
|
39
|
+
leafVersion: payment_js_1.TAP_LEAF_VERSION,
|
|
40
|
+
})));
|
|
41
|
+
const payment = (0, btc_signer_1.p2tr)(btc_signer_1.TAPROOT_UNSPENDABLE_KEY, tapTree, undefined, true);
|
|
34
42
|
if (!payment.tapLeafScript ||
|
|
35
43
|
payment.tapLeafScript.length !== scripts.length) {
|
|
36
44
|
throw new Error("invalid scripts");
|
|
@@ -39,7 +47,7 @@ class VtxoScript {
|
|
|
39
47
|
this.tweakedPublicKey = payment.tweakedPubkey;
|
|
40
48
|
}
|
|
41
49
|
encode() {
|
|
42
|
-
const tapTree = TapTreeCoder.encode(this.scripts.map((script) => ({
|
|
50
|
+
const tapTree = exports.TapTreeCoder.encode(this.scripts.map((script) => ({
|
|
43
51
|
depth: 1,
|
|
44
52
|
version: payment_js_1.TAP_LEAF_VERSION,
|
|
45
53
|
script,
|
|
@@ -36,10 +36,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.CLTVMultisigTapscript = exports.ConditionMultisigTapscript = exports.ConditionCSVMultisigTapscript = exports.CSVMultisigTapscript = exports.MultisigTapscript = exports.TapscriptType = void 0;
|
|
37
37
|
exports.decodeTapscript = decodeTapscript;
|
|
38
38
|
const bip68 = __importStar(require("bip68"));
|
|
39
|
-
const
|
|
40
|
-
const payment_js_1 = require("@scure/btc-signer/payment.js");
|
|
39
|
+
const btc_signer_1 = require("@scure/btc-signer");
|
|
41
40
|
const base_1 = require("@scure/base");
|
|
42
|
-
const MinimalScriptNum = (0,
|
|
41
|
+
const MinimalScriptNum = (0, btc_signer_1.ScriptNum)(undefined, true);
|
|
43
42
|
var TapscriptType;
|
|
44
43
|
(function (TapscriptType) {
|
|
45
44
|
TapscriptType["Multisig"] = "multisig";
|
|
@@ -109,7 +108,7 @@ var MultisigTapscript;
|
|
|
109
108
|
return {
|
|
110
109
|
type: TapscriptType.Multisig,
|
|
111
110
|
params,
|
|
112
|
-
script: (0,
|
|
111
|
+
script: (0, btc_signer_1.p2tr_ms)(params.pubkeys.length, params.pubkeys).script,
|
|
113
112
|
};
|
|
114
113
|
}
|
|
115
114
|
const asm = [];
|
|
@@ -126,7 +125,7 @@ var MultisigTapscript;
|
|
|
126
125
|
return {
|
|
127
126
|
type: TapscriptType.Multisig,
|
|
128
127
|
params,
|
|
129
|
-
script:
|
|
128
|
+
script: btc_signer_1.Script.encode(asm),
|
|
130
129
|
};
|
|
131
130
|
}
|
|
132
131
|
MultisigTapscript.encode = encode;
|
|
@@ -151,7 +150,7 @@ var MultisigTapscript;
|
|
|
151
150
|
MultisigTapscript.decode = decode;
|
|
152
151
|
// <pubkey> CHECKSIG <pubkey> CHECKSIGADD <len_keys> NUMEQUAL
|
|
153
152
|
function decodeChecksigAdd(script) {
|
|
154
|
-
const asm =
|
|
153
|
+
const asm = btc_signer_1.Script.decode(script);
|
|
155
154
|
const pubkeys = [];
|
|
156
155
|
let foundNumEqual = false;
|
|
157
156
|
// Parse through ASM operations
|
|
@@ -201,7 +200,7 @@ var MultisigTapscript;
|
|
|
201
200
|
}
|
|
202
201
|
// <pubkey> CHECKSIGVERIFY <pubkey> CHECKSIG
|
|
203
202
|
function decodeChecksig(script) {
|
|
204
|
-
const asm =
|
|
203
|
+
const asm = btc_signer_1.Script.decode(script);
|
|
205
204
|
const pubkeys = [];
|
|
206
205
|
// Parse through ASM operations
|
|
207
206
|
for (let i = 0; i < asm.length; i++) {
|
|
@@ -278,7 +277,7 @@ var CSVMultisigTapscript;
|
|
|
278
277
|
];
|
|
279
278
|
const multisigScript = MultisigTapscript.encode(params);
|
|
280
279
|
const script = new Uint8Array([
|
|
281
|
-
...
|
|
280
|
+
...btc_signer_1.Script.encode(asm),
|
|
282
281
|
...multisigScript.script,
|
|
283
282
|
]);
|
|
284
283
|
return {
|
|
@@ -292,7 +291,7 @@ var CSVMultisigTapscript;
|
|
|
292
291
|
if (script.length === 0) {
|
|
293
292
|
throw new Error("Failed to decode: script is empty");
|
|
294
293
|
}
|
|
295
|
-
const asm =
|
|
294
|
+
const asm = btc_signer_1.Script.decode(script);
|
|
296
295
|
if (asm.length < 3) {
|
|
297
296
|
throw new Error(`Invalid script: too short (expected at least 3)`);
|
|
298
297
|
}
|
|
@@ -303,7 +302,7 @@ var CSVMultisigTapscript;
|
|
|
303
302
|
if (asm[1] !== "CHECKSEQUENCEVERIFY" || asm[2] !== "DROP") {
|
|
304
303
|
throw new Error("Invalid script: expected CHECKSEQUENCEVERIFY DROP");
|
|
305
304
|
}
|
|
306
|
-
const multisigScript = new Uint8Array(
|
|
305
|
+
const multisigScript = new Uint8Array(btc_signer_1.Script.encode(asm.slice(3)));
|
|
307
306
|
let multisig;
|
|
308
307
|
try {
|
|
309
308
|
multisig = MultisigTapscript.decode(multisigScript);
|
|
@@ -361,7 +360,7 @@ var ConditionCSVMultisigTapscript;
|
|
|
361
360
|
function encode(params) {
|
|
362
361
|
const script = new Uint8Array([
|
|
363
362
|
...params.conditionScript,
|
|
364
|
-
...
|
|
363
|
+
...btc_signer_1.Script.encode(["VERIFY"]),
|
|
365
364
|
...CSVMultisigTapscript.encode(params).script,
|
|
366
365
|
]);
|
|
367
366
|
return {
|
|
@@ -375,7 +374,7 @@ var ConditionCSVMultisigTapscript;
|
|
|
375
374
|
if (script.length === 0) {
|
|
376
375
|
throw new Error("Failed to decode: script is empty");
|
|
377
376
|
}
|
|
378
|
-
const asm =
|
|
377
|
+
const asm = btc_signer_1.Script.decode(script);
|
|
379
378
|
if (asm.length < 1) {
|
|
380
379
|
throw new Error(`Invalid script: too short (expected at least 1)`);
|
|
381
380
|
}
|
|
@@ -388,8 +387,8 @@ var ConditionCSVMultisigTapscript;
|
|
|
388
387
|
if (verifyIndex === -1) {
|
|
389
388
|
throw new Error("Invalid script: missing VERIFY operation");
|
|
390
389
|
}
|
|
391
|
-
const conditionScript = new Uint8Array(
|
|
392
|
-
const csvMultisigScript = new Uint8Array(
|
|
390
|
+
const conditionScript = new Uint8Array(btc_signer_1.Script.encode(asm.slice(0, verifyIndex)));
|
|
391
|
+
const csvMultisigScript = new Uint8Array(btc_signer_1.Script.encode(asm.slice(verifyIndex + 1)));
|
|
393
392
|
let csvMultisig;
|
|
394
393
|
try {
|
|
395
394
|
csvMultisig = CSVMultisigTapscript.decode(csvMultisigScript);
|
|
@@ -436,7 +435,7 @@ var ConditionMultisigTapscript;
|
|
|
436
435
|
function encode(params) {
|
|
437
436
|
const script = new Uint8Array([
|
|
438
437
|
...params.conditionScript,
|
|
439
|
-
...
|
|
438
|
+
...btc_signer_1.Script.encode(["VERIFY"]),
|
|
440
439
|
...MultisigTapscript.encode(params).script,
|
|
441
440
|
]);
|
|
442
441
|
return {
|
|
@@ -450,7 +449,7 @@ var ConditionMultisigTapscript;
|
|
|
450
449
|
if (script.length === 0) {
|
|
451
450
|
throw new Error("Failed to decode: script is empty");
|
|
452
451
|
}
|
|
453
|
-
const asm =
|
|
452
|
+
const asm = btc_signer_1.Script.decode(script);
|
|
454
453
|
if (asm.length < 1) {
|
|
455
454
|
throw new Error(`Invalid script: too short (expected at least 1)`);
|
|
456
455
|
}
|
|
@@ -463,8 +462,8 @@ var ConditionMultisigTapscript;
|
|
|
463
462
|
if (verifyIndex === -1) {
|
|
464
463
|
throw new Error("Invalid script: missing VERIFY operation");
|
|
465
464
|
}
|
|
466
|
-
const conditionScript = new Uint8Array(
|
|
467
|
-
const multisigScript = new Uint8Array(
|
|
465
|
+
const conditionScript = new Uint8Array(btc_signer_1.Script.encode(asm.slice(0, verifyIndex)));
|
|
466
|
+
const multisigScript = new Uint8Array(btc_signer_1.Script.encode(asm.slice(verifyIndex + 1)));
|
|
468
467
|
let multisig;
|
|
469
468
|
try {
|
|
470
469
|
multisig = MultisigTapscript.decode(multisigScript);
|
|
@@ -515,7 +514,7 @@ var CLTVMultisigTapscript;
|
|
|
515
514
|
"CHECKLOCKTIMEVERIFY",
|
|
516
515
|
"DROP",
|
|
517
516
|
];
|
|
518
|
-
const timelockedScript =
|
|
517
|
+
const timelockedScript = btc_signer_1.Script.encode(asm);
|
|
519
518
|
const script = new Uint8Array([
|
|
520
519
|
...timelockedScript,
|
|
521
520
|
...MultisigTapscript.encode(params).script,
|
|
@@ -531,7 +530,7 @@ var CLTVMultisigTapscript;
|
|
|
531
530
|
if (script.length === 0) {
|
|
532
531
|
throw new Error("Failed to decode: script is empty");
|
|
533
532
|
}
|
|
534
|
-
const asm =
|
|
533
|
+
const asm = btc_signer_1.Script.decode(script);
|
|
535
534
|
if (asm.length < 3) {
|
|
536
535
|
throw new Error(`Invalid script: too short (expected at least 3)`);
|
|
537
536
|
}
|
|
@@ -542,7 +541,7 @@ var CLTVMultisigTapscript;
|
|
|
542
541
|
if (asm[1] !== "CHECKLOCKTIMEVERIFY" || asm[2] !== "DROP") {
|
|
543
542
|
throw new Error("Invalid script: expected CHECKLOCKTIMEVERIFY DROP");
|
|
544
543
|
}
|
|
545
|
-
const multisigScript = new Uint8Array(
|
|
544
|
+
const multisigScript = new Uint8Array(btc_signer_1.Script.encode(asm.slice(3)));
|
|
546
545
|
let multisig;
|
|
547
546
|
try {
|
|
548
547
|
multisig = MultisigTapscript.decode(multisigScript);
|
package/dist/cjs/script/vhtlc.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.VHTLC = void 0;
|
|
4
|
-
const
|
|
4
|
+
const btc_signer_1 = require("@scure/btc-signer");
|
|
5
5
|
const tapscript_1 = require("./tapscript");
|
|
6
6
|
const base_1 = require("@scure/base");
|
|
7
7
|
const base_2 = require("./base");
|
|
@@ -158,5 +158,5 @@ var VHTLC;
|
|
|
158
158
|
}
|
|
159
159
|
})(VHTLC || (exports.VHTLC = VHTLC = {}));
|
|
160
160
|
function preimageConditionScript(preimageHash) {
|
|
161
|
-
return
|
|
161
|
+
return btc_signer_1.Script.encode(["HASH160", preimageHash, "EQUAL"]);
|
|
162
162
|
}
|
|
@@ -57,15 +57,15 @@ class TreeSignerSession {
|
|
|
57
57
|
const secretKey = (0, utils_js_1.randomPrivateKeyBytes)();
|
|
58
58
|
return new TreeSignerSession(secretKey);
|
|
59
59
|
}
|
|
60
|
-
init(tree, scriptRoot, rootInputAmount) {
|
|
60
|
+
async init(tree, scriptRoot, rootInputAmount) {
|
|
61
61
|
this.graph = tree;
|
|
62
62
|
this.scriptRoot = scriptRoot;
|
|
63
63
|
this.rootSharedOutputAmount = rootInputAmount;
|
|
64
64
|
}
|
|
65
|
-
getPublicKey() {
|
|
65
|
+
async getPublicKey() {
|
|
66
66
|
return secp256k1_js_1.secp256k1.getPublicKey(this.secretKey);
|
|
67
67
|
}
|
|
68
|
-
getNonces() {
|
|
68
|
+
async getNonces() {
|
|
69
69
|
if (!this.graph)
|
|
70
70
|
throw exports.ErrMissingVtxoGraph;
|
|
71
71
|
if (!this.myNonces) {
|
|
@@ -77,12 +77,46 @@ class TreeSignerSession {
|
|
|
77
77
|
}
|
|
78
78
|
return publicNonces;
|
|
79
79
|
}
|
|
80
|
-
|
|
81
|
-
if (this.
|
|
82
|
-
throw
|
|
83
|
-
this.aggregateNonces
|
|
80
|
+
async aggregatedNonces(txid, noncesByPubkey) {
|
|
81
|
+
if (!this.graph)
|
|
82
|
+
throw exports.ErrMissingVtxoGraph;
|
|
83
|
+
if (!this.aggregateNonces) {
|
|
84
|
+
this.aggregateNonces = new Map();
|
|
85
|
+
}
|
|
86
|
+
if (!this.myNonces) {
|
|
87
|
+
await this.getNonces(); // generate nonces if not generated yet
|
|
88
|
+
}
|
|
89
|
+
if (this.aggregateNonces.has(txid)) {
|
|
90
|
+
return {
|
|
91
|
+
hasAllNonces: this.aggregateNonces.size === this.myNonces?.size,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
const myNonce = this.myNonces.get(txid);
|
|
95
|
+
if (!myNonce)
|
|
96
|
+
throw new Error(`missing nonce for txid ${txid}`);
|
|
97
|
+
const myPublicKey = await this.getPublicKey();
|
|
98
|
+
// set my nonce to not rely on server
|
|
99
|
+
noncesByPubkey.set(base_1.hex.encode(myPublicKey.subarray(1)), myNonce);
|
|
100
|
+
const tx = this.graph.find(txid);
|
|
101
|
+
if (!tx)
|
|
102
|
+
throw new Error(`missing tx for txid ${txid}`);
|
|
103
|
+
const cosigners = (0, unknownFields_1.getArkPsbtFields)(tx.root, 0, unknownFields_1.CosignerPublicKey).map((c) => base_1.hex.encode(c.key.subarray(1)) // xonly pubkey
|
|
104
|
+
);
|
|
105
|
+
const pubNonces = [];
|
|
106
|
+
for (const cosigner of cosigners) {
|
|
107
|
+
const nonce = noncesByPubkey.get(cosigner);
|
|
108
|
+
if (!nonce) {
|
|
109
|
+
throw new Error(`missing nonce for cosigner ${cosigner}`);
|
|
110
|
+
}
|
|
111
|
+
pubNonces.push(nonce.pubNonce);
|
|
112
|
+
}
|
|
113
|
+
const aggregateNonce = musig2.aggregateNonces(pubNonces);
|
|
114
|
+
this.aggregateNonces.set(txid, { pubNonce: aggregateNonce });
|
|
115
|
+
return {
|
|
116
|
+
hasAllNonces: this.aggregateNonces.size === this.myNonces?.size,
|
|
117
|
+
};
|
|
84
118
|
}
|
|
85
|
-
sign() {
|
|
119
|
+
async sign() {
|
|
86
120
|
if (!this.graph)
|
|
87
121
|
throw exports.ErrMissingVtxoGraph;
|
|
88
122
|
if (!this.aggregateNonces)
|
|
@@ -166,9 +200,8 @@ async function validateTreeSigs(finalAggregatedKey, sharedOutputAmount, vtxoTree
|
|
|
166
200
|
function getPrevOutput(finalKey, graph, sharedOutputAmount, tx) {
|
|
167
201
|
// generate P2TR script from musig2 final key
|
|
168
202
|
const pkScript = script_js_1.Script.encode(["OP_1", finalKey.slice(1)]);
|
|
169
|
-
const txid = base_1.hex.encode((0, utils_js_1.sha256x2)(tx.toBytes(true)).reverse());
|
|
170
203
|
// if the input is the root input, return the shared output amount
|
|
171
|
-
if (
|
|
204
|
+
if (tx.id === graph.txid) {
|
|
172
205
|
return {
|
|
173
206
|
amount: sharedOutputAmount,
|
|
174
207
|
script: pkScript,
|
|
@@ -178,7 +211,7 @@ function getPrevOutput(finalKey, graph, sharedOutputAmount, tx) {
|
|
|
178
211
|
const parentInput = tx.getInput(0);
|
|
179
212
|
if (!parentInput.txid)
|
|
180
213
|
throw new Error("missing parent input txid");
|
|
181
|
-
const parentTxid = base_1.hex.encode(
|
|
214
|
+
const parentTxid = base_1.hex.encode(parentInput.txid);
|
|
182
215
|
const parent = graph.find(parentTxid);
|
|
183
216
|
if (!parent)
|
|
184
217
|
throw new Error("parent tx not found");
|
package/dist/cjs/tree/txTree.js
CHANGED
|
@@ -4,7 +4,6 @@ exports.TxTree = void 0;
|
|
|
4
4
|
const transaction_js_1 = require("@scure/btc-signer/transaction.js");
|
|
5
5
|
const base_1 = require("@scure/base");
|
|
6
6
|
const base_2 = require("@scure/base");
|
|
7
|
-
const utils_js_1 = require("@scure/btc-signer/utils.js");
|
|
8
7
|
/**
|
|
9
8
|
* TxTree is a graph of bitcoin transactions.
|
|
10
9
|
* It is used to represent batch tree created during settlement session
|
|
@@ -22,7 +21,7 @@ class TxTree {
|
|
|
22
21
|
const chunksByTxid = new Map();
|
|
23
22
|
for (const chunk of chunks) {
|
|
24
23
|
const decodedChunk = decodeNode(chunk);
|
|
25
|
-
const txid =
|
|
24
|
+
const txid = decodedChunk.tx.id;
|
|
26
25
|
chunksByTxid.set(txid, decodedChunk);
|
|
27
26
|
}
|
|
28
27
|
// Find the root chunks (the ones that aren't referenced as a child)
|
|
@@ -91,7 +90,7 @@ class TxTree {
|
|
|
91
90
|
}
|
|
92
91
|
child.validate();
|
|
93
92
|
const childInput = child.root.getInput(0);
|
|
94
|
-
const parentTxid =
|
|
93
|
+
const parentTxid = this.root.id;
|
|
95
94
|
// verify the input of the child is the output of the parent
|
|
96
95
|
if (!childInput.txid ||
|
|
97
96
|
base_2.hex.encode(childInput.txid) !== parentTxid ||
|
|
@@ -126,7 +125,7 @@ class TxTree {
|
|
|
126
125
|
return leaves;
|
|
127
126
|
}
|
|
128
127
|
get txid() {
|
|
129
|
-
return
|
|
128
|
+
return this.root.id;
|
|
130
129
|
}
|
|
131
130
|
find(txid) {
|
|
132
131
|
if (txid === this.txid) {
|
|
@@ -6,7 +6,6 @@ exports.validateVtxoTxGraph = validateVtxoTxGraph;
|
|
|
6
6
|
const base_1 = require("@scure/base");
|
|
7
7
|
const transaction_js_1 = require("@scure/btc-signer/transaction.js");
|
|
8
8
|
const base_2 = require("@scure/base");
|
|
9
|
-
const utils_js_1 = require("@scure/btc-signer/utils.js");
|
|
10
9
|
const musig2_1 = require("../musig2");
|
|
11
10
|
const unknownFields_1 = require("../utils/unknownFields");
|
|
12
11
|
const ErrInvalidSettlementTx = (tx) => new Error(`invalid settlement transaction: ${tx}`);
|
|
@@ -31,7 +30,7 @@ function validateConnectorsTxGraph(settlementTxB64, connectorsGraph) {
|
|
|
31
30
|
const settlementTx = transaction_js_1.Transaction.fromPSBT(base_2.base64.decode(settlementTxB64));
|
|
32
31
|
if (settlementTx.outputsLength <= BATCH_OUTPUT_CONNECTORS_INDEX)
|
|
33
32
|
throw exports.ErrInvalidSettlementTxOutputs;
|
|
34
|
-
const expectedRootTxid =
|
|
33
|
+
const expectedRootTxid = settlementTx.id;
|
|
35
34
|
if (!rootInput.txid)
|
|
36
35
|
throw exports.ErrWrongSettlementTxid;
|
|
37
36
|
if (base_1.hex.encode(rootInput.txid) !== expectedRootTxid)
|
|
@@ -58,7 +57,7 @@ function validateVtxoTxGraph(graph, roundTransaction, sweepTapTreeRoot) {
|
|
|
58
57
|
throw exports.ErrEmptyTree;
|
|
59
58
|
}
|
|
60
59
|
const rootInput = graph.root.getInput(0);
|
|
61
|
-
const commitmentTxid =
|
|
60
|
+
const commitmentTxid = roundTransaction.id;
|
|
62
61
|
if (!rootInput.txid ||
|
|
63
62
|
base_1.hex.encode(rootInput.txid) !== commitmentTxid ||
|
|
64
63
|
rootInput.index !== BATCH_OUTPUT_VTXO_INDEX) {
|