@arkade-os/sdk 0.3.0-alpha.8 → 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 +48 -14
- 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 +13 -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 +15 -170
- package/dist/cjs/providers/expoIndexer.js +22 -111
- package/dist/cjs/providers/expoUtils.js +124 -0
- 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 +105 -15
- package/dist/cjs/utils/transaction.js +28 -0
- package/dist/cjs/utils/unknownFields.js +7 -7
- 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 -0
- package/dist/cjs/wallet/serviceWorker/wallet.js +7 -8
- package/dist/cjs/wallet/serviceWorker/worker.js +46 -27
- package/dist/cjs/wallet/unroll.js +7 -9
- package/dist/cjs/wallet/utils.js +9 -0
- package/dist/cjs/wallet/vtxo-manager.js +323 -0
- package/dist/cjs/wallet/wallet.js +98 -125
- 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 +15 -137
- package/dist/esm/providers/expoIndexer.js +22 -78
- package/dist/esm/providers/expoUtils.js +87 -0
- 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 +97 -8
- package/dist/esm/utils/transaction.js +24 -0
- package/dist/esm/utils/unknownFields.js +3 -3
- 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 -0
- package/dist/esm/wallet/serviceWorker/wallet.js +8 -9
- package/dist/esm/wallet/serviceWorker/worker.js +48 -29
- package/dist/esm/wallet/unroll.js +5 -7
- package/dist/esm/wallet/utils.js +8 -0
- package/dist/esm/wallet/vtxo-manager.js +317 -0
- package/dist/esm/wallet/wallet.js +92 -119
- 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 +9 -7
- 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 +62 -26
- package/dist/types/providers/errors.d.ts +13 -0
- package/dist/types/providers/expoIndexer.d.ts +2 -10
- package/dist/types/providers/expoUtils.d.ts +18 -0
- package/dist/types/providers/indexer.d.ts +1 -9
- 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 +12 -4
- 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 +6 -4
- 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 -0
- 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 +2 -1
- package/dist/types/wallet/vtxo-manager.d.ts +179 -0
- package/dist/types/wallet/wallet.d.ts +8 -4
- package/package.json +1 -2
- 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
|
@@ -38,8 +38,8 @@ exports.waitForIncomingFunds = waitForIncomingFunds;
|
|
|
38
38
|
const base_1 = require("@scure/base");
|
|
39
39
|
const bip68 = __importStar(require("bip68"));
|
|
40
40
|
const payment_js_1 = require("@scure/btc-signer/payment.js");
|
|
41
|
-
const
|
|
42
|
-
const
|
|
41
|
+
const btc_signer_1 = require("@scure/btc-signer");
|
|
42
|
+
const utils_js_1 = require("@scure/btc-signer/utils.js");
|
|
43
43
|
const transactionHistory_1 = require("../utils/transactionHistory");
|
|
44
44
|
const address_1 = require("../script/address");
|
|
45
45
|
const default_1 = require("../script/default");
|
|
@@ -49,14 +49,15 @@ const ark_1 = require("../providers/ark");
|
|
|
49
49
|
const forfeit_1 = require("../forfeit");
|
|
50
50
|
const validation_1 = require("../tree/validation");
|
|
51
51
|
const _1 = require(".");
|
|
52
|
-
const utils_js_1 = require("@scure/btc-signer/utils.js");
|
|
53
52
|
const base_2 = require("../script/base");
|
|
54
53
|
const tapscript_1 = require("../script/tapscript");
|
|
55
54
|
const arkTransaction_1 = require("../utils/arkTransaction");
|
|
55
|
+
const vtxo_manager_1 = require("./vtxo-manager");
|
|
56
56
|
const arknote_1 = require("../arknote");
|
|
57
|
-
const
|
|
57
|
+
const intent_1 = require("../intent");
|
|
58
58
|
const indexer_1 = require("../providers/indexer");
|
|
59
59
|
const txTree_1 = require("../tree/txTree");
|
|
60
|
+
const unknownFields_1 = require("../utils/unknownFields");
|
|
60
61
|
const inMemory_1 = require("../storage/inMemory");
|
|
61
62
|
const walletRepository_1 = require("../repositories/walletRepository");
|
|
62
63
|
const contractRepository_1 = require("../repositories/contractRepository");
|
|
@@ -95,7 +96,7 @@ const utils_1 = require("./utils");
|
|
|
95
96
|
* ```
|
|
96
97
|
*/
|
|
97
98
|
class Wallet {
|
|
98
|
-
constructor(identity, network, networkName, onchainProvider, arkProvider, indexerProvider, arkServerPublicKey, offchainTapscript, boardingTapscript, serverUnrollScript, forfeitOutputScript, dustAmount, walletRepository, contractRepository) {
|
|
99
|
+
constructor(identity, network, networkName, onchainProvider, arkProvider, indexerProvider, arkServerPublicKey, offchainTapscript, boardingTapscript, serverUnrollScript, forfeitOutputScript, forfeitPubkey, dustAmount, walletRepository, contractRepository, renewalConfig) {
|
|
99
100
|
this.identity = identity;
|
|
100
101
|
this.network = network;
|
|
101
102
|
this.networkName = networkName;
|
|
@@ -107,9 +108,15 @@ class Wallet {
|
|
|
107
108
|
this.boardingTapscript = boardingTapscript;
|
|
108
109
|
this.serverUnrollScript = serverUnrollScript;
|
|
109
110
|
this.forfeitOutputScript = forfeitOutputScript;
|
|
111
|
+
this.forfeitPubkey = forfeitPubkey;
|
|
110
112
|
this.dustAmount = dustAmount;
|
|
111
113
|
this.walletRepository = walletRepository;
|
|
112
114
|
this.contractRepository = contractRepository;
|
|
115
|
+
this.renewalConfig = {
|
|
116
|
+
enabled: renewalConfig?.enabled ?? false,
|
|
117
|
+
...vtxo_manager_1.DEFAULT_RENEWAL_CONFIG,
|
|
118
|
+
...renewalConfig,
|
|
119
|
+
};
|
|
113
120
|
}
|
|
114
121
|
static async create(config) {
|
|
115
122
|
const pubkey = await config.identity.xOnlyPublicKey();
|
|
@@ -139,6 +146,7 @@ class Wallet {
|
|
|
139
146
|
const esploraUrl = config.esploraUrl || onchain_1.ESPLORA_URL[info.network];
|
|
140
147
|
// Use provided onchainProvider instance or create a new one
|
|
141
148
|
const onchainProvider = config.onchainProvider || new onchain_1.EsploraProvider(esploraUrl);
|
|
149
|
+
// Generate timelocks
|
|
142
150
|
const exitTimelock = {
|
|
143
151
|
value: info.unilateralExitDelay,
|
|
144
152
|
type: info.unilateralExitDelay < 512n ? "blocks" : "seconds",
|
|
@@ -164,21 +172,22 @@ class Wallet {
|
|
|
164
172
|
// the serverUnrollScript is the one used to create output scripts of the checkpoint transactions
|
|
165
173
|
let serverUnrollScript;
|
|
166
174
|
try {
|
|
167
|
-
const raw = base_1.hex.decode(info.
|
|
175
|
+
const raw = base_1.hex.decode(info.checkpointTapscript);
|
|
168
176
|
serverUnrollScript = tapscript_1.CSVMultisigTapscript.decode(raw);
|
|
169
177
|
}
|
|
170
178
|
catch (e) {
|
|
171
|
-
throw new Error("Invalid
|
|
179
|
+
throw new Error("Invalid checkpointTapscript from server");
|
|
172
180
|
}
|
|
173
181
|
// parse the server forfeit address
|
|
174
182
|
// server is expecting funds to be sent to this address
|
|
175
|
-
const
|
|
176
|
-
const
|
|
183
|
+
const forfeitPubkey = base_1.hex.decode(info.forfeitPubkey).slice(1);
|
|
184
|
+
const forfeitAddress = (0, btc_signer_1.Address)(network).decode(info.forfeitAddress);
|
|
185
|
+
const forfeitOutputScript = btc_signer_1.OutScript.encode(forfeitAddress);
|
|
177
186
|
// Set up storage and repositories
|
|
178
187
|
const storage = config.storage || new inMemory_1.InMemoryStorageAdapter();
|
|
179
188
|
const walletRepository = new walletRepository_1.WalletRepositoryImpl(storage);
|
|
180
189
|
const contractRepository = new contractRepository_1.ContractRepositoryImpl(storage);
|
|
181
|
-
return new Wallet(config.identity, network, info.network, onchainProvider, arkProvider, indexerProvider, serverPubKey, offchainTapscript, boardingTapscript, serverUnrollScript, forfeitOutputScript, info.dust, walletRepository, contractRepository);
|
|
190
|
+
return new Wallet(config.identity, network, info.network, onchainProvider, arkProvider, indexerProvider, serverPubKey, offchainTapscript, boardingTapscript, serverUnrollScript, forfeitOutputScript, forfeitPubkey, info.dust, walletRepository, contractRepository, config.renewalConfig);
|
|
182
191
|
}
|
|
183
192
|
get arkAddress() {
|
|
184
193
|
return this.offchainTapscript.address(this.network.hrp, this.arkServerPublicKey);
|
|
@@ -361,15 +370,12 @@ class Wallet {
|
|
|
361
370
|
async getBoardingUtxos() {
|
|
362
371
|
const boardingAddress = await this.getBoardingAddress();
|
|
363
372
|
const boardingUtxos = await this.onchainProvider.getCoins(boardingAddress);
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
intentTapLeafScript: exit,
|
|
371
|
-
tapTree: encodedBoardingTapscript,
|
|
372
|
-
}));
|
|
373
|
+
const utxos = boardingUtxos.map((utxo) => {
|
|
374
|
+
return (0, utils_1.extendCoin)(this, utxo);
|
|
375
|
+
});
|
|
376
|
+
// Save boardingUtxos using unified repository
|
|
377
|
+
await this.walletRepository.saveUtxos(boardingAddress, utxos);
|
|
378
|
+
return utxos;
|
|
373
379
|
}
|
|
374
380
|
async sendBitcoin(params) {
|
|
375
381
|
if (params.amount <= 0) {
|
|
@@ -418,7 +424,7 @@ class Wallet {
|
|
|
418
424
|
// TODO persist final virtual tx and checkpoints to repository
|
|
419
425
|
// sign the checkpoints
|
|
420
426
|
const finalCheckpoints = await Promise.all(signedCheckpointTxs.map(async (c) => {
|
|
421
|
-
const tx =
|
|
427
|
+
const tx = btc_signer_1.Transaction.fromPSBT(base_1.base64.decode(c));
|
|
422
428
|
const signedCheckpoint = await this.identity.sign(tx);
|
|
423
429
|
return base_1.base64.encode(signedCheckpoint.toPSBT());
|
|
424
430
|
}));
|
|
@@ -476,8 +482,8 @@ class Wallet {
|
|
|
476
482
|
}
|
|
477
483
|
catch {
|
|
478
484
|
// onchain
|
|
479
|
-
const addr = (0,
|
|
480
|
-
script =
|
|
485
|
+
const addr = (0, btc_signer_1.Address)(this.network).decode(output.address);
|
|
486
|
+
script = btc_signer_1.OutScript.encode(addr);
|
|
481
487
|
onchainOutputIndexes.push(index);
|
|
482
488
|
}
|
|
483
489
|
outputs.push({
|
|
@@ -490,7 +496,7 @@ class Wallet {
|
|
|
490
496
|
const signingPublicKeys = [];
|
|
491
497
|
if (hasOffchainOutputs) {
|
|
492
498
|
session = this.identity.signerSession();
|
|
493
|
-
signingPublicKeys.push(base_1.hex.encode(session.getPublicKey()));
|
|
499
|
+
signingPublicKeys.push(base_1.hex.encode(await session.getPublicKey()));
|
|
494
500
|
}
|
|
495
501
|
const [intent, deleteIntent] = await Promise.all([
|
|
496
502
|
this.makeRegisterIntentSignature(params.inputs, outputs, onchainOutputIndexes, signingPublicKeys),
|
|
@@ -506,8 +512,8 @@ class Wallet {
|
|
|
506
512
|
...params.inputs.map((input) => `${input.txid}:${input.vout}`),
|
|
507
513
|
];
|
|
508
514
|
const settlementStream = this.arkProvider.getEventStream(abortController.signal, topics);
|
|
509
|
-
//
|
|
510
|
-
let
|
|
515
|
+
// batchId, sweepTapTreeRoot and forfeitOutputScript are set once the BatchStarted event is received
|
|
516
|
+
let batchId;
|
|
511
517
|
let sweepTapTreeRoot;
|
|
512
518
|
const vtxoChunks = [];
|
|
513
519
|
const connectorsChunks = [];
|
|
@@ -520,30 +526,26 @@ class Wallet {
|
|
|
520
526
|
switch (event.type) {
|
|
521
527
|
// the settlement failed
|
|
522
528
|
case ark_1.SettlementEventType.BatchFailed:
|
|
523
|
-
|
|
524
|
-
if (event.id === roundId) {
|
|
525
|
-
throw new Error(event.reason);
|
|
526
|
-
}
|
|
527
|
-
break;
|
|
529
|
+
throw new Error(event.reason);
|
|
528
530
|
case ark_1.SettlementEventType.BatchStarted:
|
|
529
531
|
if (step !== undefined) {
|
|
530
532
|
continue;
|
|
531
533
|
}
|
|
532
|
-
const res = await this.handleBatchStartedEvent(event, intentId, this.
|
|
534
|
+
const res = await this.handleBatchStartedEvent(event, intentId, this.forfeitPubkey, this.forfeitOutputScript);
|
|
533
535
|
if (!res.skip) {
|
|
534
536
|
step = event.type;
|
|
535
537
|
sweepTapTreeRoot = res.sweepTapTreeRoot;
|
|
536
|
-
|
|
538
|
+
batchId = res.roundId;
|
|
537
539
|
if (!hasOffchainOutputs) {
|
|
538
540
|
// if there are no offchain outputs, we don't have to handle musig2 tree signatures
|
|
539
541
|
// we can directly advance to the finalization step
|
|
540
|
-
step = ark_1.SettlementEventType.
|
|
542
|
+
step = ark_1.SettlementEventType.TreeNonces;
|
|
541
543
|
}
|
|
542
544
|
}
|
|
543
545
|
break;
|
|
544
546
|
case ark_1.SettlementEventType.TreeTx:
|
|
545
547
|
if (step !== ark_1.SettlementEventType.BatchStarted &&
|
|
546
|
-
step !== ark_1.SettlementEventType.
|
|
548
|
+
step !== ark_1.SettlementEventType.TreeNonces) {
|
|
547
549
|
continue;
|
|
548
550
|
}
|
|
549
551
|
// index 0 = vtxo tree
|
|
@@ -559,7 +561,7 @@ class Wallet {
|
|
|
559
561
|
}
|
|
560
562
|
break;
|
|
561
563
|
case ark_1.SettlementEventType.TreeSignature:
|
|
562
|
-
if (step !== ark_1.SettlementEventType.
|
|
564
|
+
if (step !== ark_1.SettlementEventType.TreeNonces) {
|
|
563
565
|
continue;
|
|
564
566
|
}
|
|
565
567
|
if (!hasOffchainOutputs) {
|
|
@@ -601,7 +603,7 @@ class Wallet {
|
|
|
601
603
|
break;
|
|
602
604
|
// the musig2 nonces of the vtxo tree transactions are generated
|
|
603
605
|
// the server expects now the partial musig2 signatures
|
|
604
|
-
case ark_1.SettlementEventType.
|
|
606
|
+
case ark_1.SettlementEventType.TreeNonces:
|
|
605
607
|
if (step !== ark_1.SettlementEventType.TreeSigningStarted) {
|
|
606
608
|
continue;
|
|
607
609
|
}
|
|
@@ -609,14 +611,18 @@ class Wallet {
|
|
|
609
611
|
if (!session) {
|
|
610
612
|
throw new Error("Signing session not set");
|
|
611
613
|
}
|
|
612
|
-
await this.
|
|
614
|
+
const signed = await this.handleSettlementTreeNoncesEvent(event, session);
|
|
615
|
+
if (signed) {
|
|
616
|
+
step = event.type;
|
|
617
|
+
}
|
|
618
|
+
break;
|
|
613
619
|
}
|
|
614
620
|
step = event.type;
|
|
615
621
|
break;
|
|
616
622
|
// the vtxo tree is signed, craft, sign and submit forfeit transactions
|
|
617
623
|
// if any boarding utxos are involved, the settlement tx is also signed
|
|
618
624
|
case ark_1.SettlementEventType.BatchFinalization:
|
|
619
|
-
if (step !== ark_1.SettlementEventType.
|
|
625
|
+
if (step !== ark_1.SettlementEventType.TreeNonces) {
|
|
620
626
|
continue;
|
|
621
627
|
}
|
|
622
628
|
if (!this.forfeitOutputScript) {
|
|
@@ -634,8 +640,10 @@ class Wallet {
|
|
|
634
640
|
if (step !== ark_1.SettlementEventType.BatchFinalization) {
|
|
635
641
|
continue;
|
|
636
642
|
}
|
|
637
|
-
|
|
638
|
-
|
|
643
|
+
if (event.id === batchId) {
|
|
644
|
+
abortController.abort();
|
|
645
|
+
return event.commitmentTxid;
|
|
646
|
+
}
|
|
639
647
|
}
|
|
640
648
|
}
|
|
641
649
|
}
|
|
@@ -714,10 +722,10 @@ class Wallet {
|
|
|
714
722
|
};
|
|
715
723
|
return stopFunc;
|
|
716
724
|
}
|
|
717
|
-
async handleBatchStartedEvent(event, intentId,
|
|
725
|
+
async handleBatchStartedEvent(event, intentId, forfeitPubKey, forfeitOutputScript) {
|
|
718
726
|
const utf8IntentId = new TextEncoder().encode(intentId);
|
|
719
727
|
const intentIdHash = (0, utils_js_1.sha256)(utf8IntentId);
|
|
720
|
-
const intentIdHashStr = base_1.hex.encode(
|
|
728
|
+
const intentIdHashStr = base_1.hex.encode(intentIdHash);
|
|
721
729
|
let skip = true;
|
|
722
730
|
// check if our intent ID hash matches any in the event
|
|
723
731
|
for (const idHash of event.intentIdHashes) {
|
|
@@ -737,7 +745,7 @@ class Wallet {
|
|
|
737
745
|
value: event.batchExpiry,
|
|
738
746
|
type: event.batchExpiry >= 512n ? "seconds" : "blocks",
|
|
739
747
|
},
|
|
740
|
-
pubkeys: [
|
|
748
|
+
pubkeys: [forfeitPubKey],
|
|
741
749
|
}).script;
|
|
742
750
|
const sweepTapTreeRoot = (0, payment_js_1.tapLeafHash)(sweepTapscript);
|
|
743
751
|
return {
|
|
@@ -750,7 +758,7 @@ class Wallet {
|
|
|
750
758
|
// validates the vtxo tree, creates a signing session and generates the musig2 nonces
|
|
751
759
|
async handleSettlementSigningEvent(event, sweepTapTreeRoot, session, vtxoGraph) {
|
|
752
760
|
// validate the unsigned vtxo tree
|
|
753
|
-
const commitmentTx =
|
|
761
|
+
const commitmentTx = btc_signer_1.Transaction.fromPSBT(base_1.base64.decode(event.unsignedCommitmentTx));
|
|
754
762
|
(0, validation_1.validateVtxoTxGraph)(vtxoGraph, commitmentTx, sweepTapTreeRoot);
|
|
755
763
|
// TODO check if our registered outputs are in the vtxo tree
|
|
756
764
|
const sharedOutput = commitmentTx.getOutput(0);
|
|
@@ -758,18 +766,25 @@ class Wallet {
|
|
|
758
766
|
throw new Error("Shared output not found");
|
|
759
767
|
}
|
|
760
768
|
session.init(vtxoGraph, sweepTapTreeRoot, sharedOutput.amount);
|
|
761
|
-
|
|
769
|
+
const pubkey = base_1.hex.encode(await session.getPublicKey());
|
|
770
|
+
const nonces = await session.getNonces();
|
|
771
|
+
await this.arkProvider.submitTreeNonces(event.id, pubkey, nonces);
|
|
762
772
|
}
|
|
763
|
-
async
|
|
764
|
-
session.
|
|
765
|
-
|
|
766
|
-
|
|
773
|
+
async handleSettlementTreeNoncesEvent(event, session) {
|
|
774
|
+
const { hasAllNonces } = await session.aggregatedNonces(event.txid, event.nonces);
|
|
775
|
+
// wait to receive and aggregate all nonces before sending signatures
|
|
776
|
+
if (!hasAllNonces)
|
|
777
|
+
return false;
|
|
778
|
+
const signatures = await session.sign();
|
|
779
|
+
const pubkey = base_1.hex.encode(await session.getPublicKey());
|
|
780
|
+
await this.arkProvider.submitTreeSignatures(event.id, pubkey, signatures);
|
|
781
|
+
return true;
|
|
767
782
|
}
|
|
768
783
|
async handleSettlementFinalizationEvent(event, inputs, forfeitOutputScript, connectorsGraph) {
|
|
769
784
|
// the signed forfeits transactions to submit
|
|
770
785
|
const signedForfeits = [];
|
|
771
786
|
const vtxos = await this.getVirtualCoins();
|
|
772
|
-
let settlementPsbt =
|
|
787
|
+
let settlementPsbt = btc_signer_1.Transaction.fromPSBT(base_1.base64.decode(event.commitmentTx));
|
|
773
788
|
let hasBoardingUtxos = false;
|
|
774
789
|
let connectorIndex = 0;
|
|
775
790
|
const connectorsLeaves = connectorsGraph?.leaves() || [];
|
|
@@ -778,8 +793,6 @@ class Wallet {
|
|
|
778
793
|
const vtxo = vtxos.find((vtxo) => vtxo.txid === input.txid && vtxo.vout === input.vout);
|
|
779
794
|
// boarding utxo, we need to sign the settlement tx
|
|
780
795
|
if (!vtxo) {
|
|
781
|
-
hasBoardingUtxos = true;
|
|
782
|
-
const inputIndexes = [];
|
|
783
796
|
for (let i = 0; i < settlementPsbt.inputsLength; i++) {
|
|
784
797
|
const settlementInput = settlementPsbt.getInput(i);
|
|
785
798
|
if (!settlementInput.txid ||
|
|
@@ -795,9 +808,12 @@ class Wallet {
|
|
|
795
808
|
settlementPsbt.updateInput(i, {
|
|
796
809
|
tapLeafScript: [input.forfeitTapLeafScript],
|
|
797
810
|
});
|
|
798
|
-
|
|
811
|
+
settlementPsbt = await this.identity.sign(settlementPsbt, [
|
|
812
|
+
i,
|
|
813
|
+
]);
|
|
814
|
+
hasBoardingUtxos = true;
|
|
815
|
+
break;
|
|
799
816
|
}
|
|
800
|
-
settlementPsbt = await this.identity.sign(settlementPsbt, inputIndexes);
|
|
801
817
|
continue;
|
|
802
818
|
}
|
|
803
819
|
if ((0, _1.isRecoverable)(vtxo) || (0, _1.isSubdust)(vtxo, this.dustAmount)) {
|
|
@@ -811,7 +827,7 @@ class Wallet {
|
|
|
811
827
|
throw new Error("not enough connectors received");
|
|
812
828
|
}
|
|
813
829
|
const connectorLeaf = connectorsLeaves[connectorIndex];
|
|
814
|
-
const connectorTxId =
|
|
830
|
+
const connectorTxId = connectorLeaf.id;
|
|
815
831
|
const connectorOutput = connectorLeaf.getOutput(0);
|
|
816
832
|
if (!connectorOutput) {
|
|
817
833
|
throw new Error("connector output not found");
|
|
@@ -830,7 +846,7 @@ class Wallet {
|
|
|
830
846
|
amount: BigInt(vtxo.value),
|
|
831
847
|
script: base_2.VtxoScript.decode(input.tapTree).pkScript,
|
|
832
848
|
},
|
|
833
|
-
sighashType:
|
|
849
|
+
sighashType: btc_signer_1.SigHash.DEFAULT,
|
|
834
850
|
tapLeafScript: [input.forfeitTapLeafScript],
|
|
835
851
|
},
|
|
836
852
|
{
|
|
@@ -852,112 +868,69 @@ class Wallet {
|
|
|
852
868
|
: undefined);
|
|
853
869
|
}
|
|
854
870
|
}
|
|
855
|
-
async makeRegisterIntentSignature(
|
|
871
|
+
async makeRegisterIntentSignature(coins, outputs, onchainOutputsIndexes, cosignerPubKeys) {
|
|
856
872
|
const nowSeconds = Math.floor(Date.now() / 1000);
|
|
857
|
-
const
|
|
873
|
+
const inputs = this.prepareIntentProofInputs(coins);
|
|
858
874
|
const message = {
|
|
859
875
|
type: "register",
|
|
860
|
-
input_tap_trees: inputTapTrees,
|
|
861
876
|
onchain_output_indexes: onchainOutputsIndexes,
|
|
862
877
|
valid_at: nowSeconds,
|
|
863
878
|
expire_at: nowSeconds + 2 * 60, // valid for 2 minutes
|
|
864
879
|
cosigners_public_keys: cosignerPubKeys,
|
|
865
880
|
};
|
|
866
881
|
const encodedMessage = JSON.stringify(message, null, 0);
|
|
867
|
-
const
|
|
882
|
+
const proof = intent_1.Intent.create(encodedMessage, inputs, outputs);
|
|
883
|
+
const signedProof = await this.identity.sign(proof);
|
|
868
884
|
return {
|
|
869
|
-
|
|
885
|
+
proof: base_1.base64.encode(signedProof.toPSBT()),
|
|
870
886
|
message: encodedMessage,
|
|
871
887
|
};
|
|
872
888
|
}
|
|
873
|
-
async makeDeleteIntentSignature(
|
|
889
|
+
async makeDeleteIntentSignature(coins) {
|
|
874
890
|
const nowSeconds = Math.floor(Date.now() / 1000);
|
|
875
|
-
const
|
|
891
|
+
const inputs = this.prepareIntentProofInputs(coins);
|
|
876
892
|
const message = {
|
|
877
893
|
type: "delete",
|
|
878
894
|
expire_at: nowSeconds + 2 * 60, // valid for 2 minutes
|
|
879
895
|
};
|
|
880
896
|
const encodedMessage = JSON.stringify(message, null, 0);
|
|
881
|
-
const
|
|
897
|
+
const proof = intent_1.Intent.create(encodedMessage, inputs, []);
|
|
898
|
+
const signedProof = await this.identity.sign(proof);
|
|
882
899
|
return {
|
|
883
|
-
|
|
900
|
+
proof: base_1.base64.encode(signedProof.toPSBT()),
|
|
884
901
|
message: encodedMessage,
|
|
885
902
|
};
|
|
886
903
|
}
|
|
887
|
-
|
|
904
|
+
prepareIntentProofInputs(coins) {
|
|
888
905
|
const inputs = [];
|
|
889
|
-
const
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
const
|
|
893
|
-
|
|
906
|
+
for (const input of coins) {
|
|
907
|
+
const vtxoScript = base_2.VtxoScript.decode(input.tapTree);
|
|
908
|
+
const sequence = getSequence(input);
|
|
909
|
+
const unknown = [unknownFields_1.VtxoTaprootTree.encode(input.tapTree)];
|
|
910
|
+
if (input.extraWitness) {
|
|
911
|
+
unknown.push(unknownFields_1.ConditionWitness.encode(input.extraWitness));
|
|
912
|
+
}
|
|
894
913
|
inputs.push({
|
|
895
|
-
txid: base_1.hex.decode(
|
|
896
|
-
index:
|
|
914
|
+
txid: base_1.hex.decode(input.txid),
|
|
915
|
+
index: input.vout,
|
|
897
916
|
witnessUtxo: {
|
|
898
|
-
amount: BigInt(
|
|
917
|
+
amount: BigInt(input.value),
|
|
899
918
|
script: vtxoScript.pkScript,
|
|
900
919
|
},
|
|
901
920
|
sequence,
|
|
902
|
-
tapLeafScript: [
|
|
921
|
+
tapLeafScript: [input.intentTapLeafScript],
|
|
922
|
+
unknown,
|
|
903
923
|
});
|
|
904
|
-
inputTapTrees.push(base_1.hex.encode(bip322Input.tapTree));
|
|
905
|
-
inputExtraWitnesses.push(bip322Input.extraWitness || []);
|
|
906
924
|
}
|
|
907
|
-
return
|
|
908
|
-
inputs,
|
|
909
|
-
inputTapTrees,
|
|
910
|
-
finalizer: finalizeWithExtraWitnesses(inputExtraWitnesses),
|
|
911
|
-
};
|
|
912
|
-
}
|
|
913
|
-
async makeBIP322Signature(message, inputs, finalizer, outputs) {
|
|
914
|
-
const proof = bip322_1.BIP322.create(message, inputs, outputs);
|
|
915
|
-
const signedProof = await this.identity.sign(proof);
|
|
916
|
-
return bip322_1.BIP322.signature(signedProof, finalizer);
|
|
925
|
+
return inputs;
|
|
917
926
|
}
|
|
918
927
|
}
|
|
919
928
|
exports.Wallet = Wallet;
|
|
920
929
|
Wallet.MIN_FEE_RATE = 1; // sats/vbyte
|
|
921
|
-
function
|
|
922
|
-
return function (tx) {
|
|
923
|
-
for (let i = 0; i < tx.inputsLength; i++) {
|
|
924
|
-
try {
|
|
925
|
-
tx.finalizeIdx(i);
|
|
926
|
-
}
|
|
927
|
-
catch (e) {
|
|
928
|
-
// handle empty witness error
|
|
929
|
-
if (e instanceof Error &&
|
|
930
|
-
e.message.includes("finalize/taproot: empty witness")) {
|
|
931
|
-
const tapLeaves = tx.getInput(i).tapLeafScript;
|
|
932
|
-
if (!tapLeaves || tapLeaves.length <= 0)
|
|
933
|
-
throw e;
|
|
934
|
-
const [cb, s] = tapLeaves[0];
|
|
935
|
-
const script = s.slice(0, -1);
|
|
936
|
-
tx.updateInput(i, {
|
|
937
|
-
finalScriptWitness: [
|
|
938
|
-
script,
|
|
939
|
-
psbt_js_1.TaprootControlBlock.encode(cb),
|
|
940
|
-
],
|
|
941
|
-
});
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
const finalScriptWitness = tx.getInput(i).finalScriptWitness;
|
|
945
|
-
if (!finalScriptWitness)
|
|
946
|
-
throw new Error("input not finalized");
|
|
947
|
-
// input 0 and 1 spend the same pkscript
|
|
948
|
-
const extra = inputExtraWitnesses[i === 0 ? 0 : i - 1];
|
|
949
|
-
if (extra && extra.length > 0) {
|
|
950
|
-
tx.updateInput(i, {
|
|
951
|
-
finalScriptWitness: [...extra, ...finalScriptWitness],
|
|
952
|
-
});
|
|
953
|
-
}
|
|
954
|
-
}
|
|
955
|
-
};
|
|
956
|
-
}
|
|
957
|
-
function getSequence(bip322Input) {
|
|
930
|
+
function getSequence(coin) {
|
|
958
931
|
let sequence = undefined;
|
|
959
932
|
try {
|
|
960
|
-
const scriptWithLeafVersion =
|
|
933
|
+
const scriptWithLeafVersion = coin.intentTapLeafScript[1];
|
|
961
934
|
const script = scriptWithLeafVersion.subarray(0, scriptWithLeafVersion.length - 1);
|
|
962
935
|
const params = tapscript_1.CSVMultisigTapscript.decode(script).params;
|
|
963
936
|
sequence = bip68.encode(params.timelock.type === "blocks"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { base58, hex } from "@scure/base";
|
|
2
|
-
import { VtxoScript } from '../script/base.js';
|
|
3
2
|
import { sha256 } from "@scure/btc-signer/utils.js";
|
|
4
|
-
import { Script } from "@scure/btc-signer
|
|
3
|
+
import { Script } from "@scure/btc-signer";
|
|
4
|
+
import { VtxoScript } from '../script/base.js';
|
|
5
5
|
/**
|
|
6
6
|
* ArkNotes are special virtual coins in the Ark protocol that can be created
|
|
7
7
|
* and spent without requiring any transactions. The server mints them, and they
|
package/dist/esm/forfeit.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { pubECDSA, pubSchnorr, randomPrivateKeyBytes,
|
|
1
|
+
import { pubECDSA, pubSchnorr, randomPrivateKeyBytes, } from "@scure/btc-signer/utils.js";
|
|
2
|
+
import { SigHash } from "@scure/btc-signer";
|
|
2
3
|
import { hex } from "@scure/base";
|
|
3
|
-
import { SigHash } from "@scure/btc-signer/transaction.js";
|
|
4
4
|
import { TreeSignerSession } from '../tree/signingSession.js';
|
|
5
|
-
import { schnorr } from "@noble/secp256k1";
|
|
6
|
-
const ZERO_32 = new Uint8Array(32).fill(0);
|
|
5
|
+
import { schnorr, sign } from "@noble/secp256k1";
|
|
7
6
|
const ALL_SIGHASH = Object.values(SigHash).filter((x) => typeof x === "number");
|
|
8
7
|
/**
|
|
9
8
|
* In-memory single key implementation for Bitcoin transaction signing.
|
|
@@ -48,7 +47,7 @@ export class SingleKey {
|
|
|
48
47
|
const txCpy = tx.clone();
|
|
49
48
|
if (!inputIndexes) {
|
|
50
49
|
try {
|
|
51
|
-
if (!txCpy.sign(this.key, ALL_SIGHASH
|
|
50
|
+
if (!txCpy.sign(this.key, ALL_SIGHASH)) {
|
|
52
51
|
throw new Error("Failed to sign transaction");
|
|
53
52
|
}
|
|
54
53
|
}
|
|
@@ -64,7 +63,7 @@ export class SingleKey {
|
|
|
64
63
|
return txCpy;
|
|
65
64
|
}
|
|
66
65
|
for (const inputIndex of inputIndexes) {
|
|
67
|
-
if (!txCpy.signIdx(this.key, inputIndex, ALL_SIGHASH
|
|
66
|
+
if (!txCpy.signIdx(this.key, inputIndex, ALL_SIGHASH)) {
|
|
68
67
|
throw new Error(`Failed to sign input #${inputIndex}`);
|
|
69
68
|
}
|
|
70
69
|
}
|
|
@@ -79,8 +78,9 @@ export class SingleKey {
|
|
|
79
78
|
signerSession() {
|
|
80
79
|
return TreeSignerSession.random();
|
|
81
80
|
}
|
|
82
|
-
async signMessage(message) {
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
async signMessage(message, signatureType = "schnorr") {
|
|
82
|
+
if (signatureType === "ecdsa")
|
|
83
|
+
return sign(message, this.key, { prehash: false });
|
|
84
|
+
return schnorr.sign(message, this.key);
|
|
85
85
|
}
|
|
86
86
|
}
|
package/dist/esm/index.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { Transaction } from
|
|
1
|
+
import { Transaction } from './utils/transaction.js';
|
|
2
2
|
import { SingleKey } from './identity/singleKey.js';
|
|
3
3
|
import { ArkAddress } from './script/address.js';
|
|
4
4
|
import { VHTLC } from './script/vhtlc.js';
|
|
5
5
|
import { DefaultVtxo } from './script/default.js';
|
|
6
|
-
import { VtxoScript } from './script/base.js';
|
|
6
|
+
import { VtxoScript, TapTreeCoder, } from './script/base.js';
|
|
7
7
|
import { TxType, } from './wallet/index.js';
|
|
8
8
|
import { Wallet, waitForIncomingFunds } from './wallet/wallet.js';
|
|
9
9
|
import { TxTree } from './tree/txTree.js';
|
|
10
10
|
import { Ramps } from './wallet/ramps.js';
|
|
11
|
+
import { VtxoManager } from './wallet/vtxo-manager.js';
|
|
11
12
|
import { ServiceWorkerWallet } from './wallet/serviceWorker/wallet.js';
|
|
12
13
|
import { OnchainWallet } from './wallet/onchain.js';
|
|
13
14
|
import { setupServiceWorker } from './wallet/serviceWorker/utils.js';
|
|
@@ -17,9 +18,9 @@ import { Response } from './wallet/serviceWorker/response.js';
|
|
|
17
18
|
import { ESPLORA_URL, EsploraProvider, } from './providers/onchain.js';
|
|
18
19
|
import { RestArkProvider, SettlementEventType, } from './providers/ark.js';
|
|
19
20
|
import { CLTVMultisigTapscript, ConditionCSVMultisigTapscript, ConditionMultisigTapscript, CSVMultisigTapscript, decodeTapscript, MultisigTapscript, } from './script/tapscript.js';
|
|
20
|
-
import { hasBoardingTxExpired, buildOffchainTx, } from './utils/arkTransaction.js';
|
|
21
|
+
import { hasBoardingTxExpired, buildOffchainTx, verifyTapscriptSignatures, } from './utils/arkTransaction.js';
|
|
21
22
|
import { VtxoTaprootTree, ConditionWitness, getArkPsbtFields, setArkPsbtField, ArkPsbtFieldKey, ArkPsbtFieldKeyType, CosignerPublicKey, VtxoTreeExpiry, } from './utils/unknownFields.js';
|
|
22
|
-
import {
|
|
23
|
+
import { Intent } from './intent/index.js';
|
|
23
24
|
import { ArkNote } from './arknote/index.js';
|
|
24
25
|
import { networks } from './networks.js';
|
|
25
26
|
import { RestIndexerProvider, IndexerTxType, ChainTxType, } from './providers/indexer.js';
|
|
@@ -27,9 +28,10 @@ import { P2A } from './utils/anchor.js';
|
|
|
27
28
|
import { Unroll } from './wallet/unroll.js';
|
|
28
29
|
import { WalletRepositoryImpl } from './repositories/walletRepository.js';
|
|
29
30
|
import { ContractRepositoryImpl } from './repositories/contractRepository.js';
|
|
31
|
+
import { ArkError, maybeArkError } from './providers/errors.js';
|
|
30
32
|
export {
|
|
31
33
|
// Wallets
|
|
32
|
-
Wallet, SingleKey, OnchainWallet, Ramps,
|
|
34
|
+
Wallet, SingleKey, OnchainWallet, Ramps, VtxoManager,
|
|
33
35
|
// Providers
|
|
34
36
|
ESPLORA_URL, EsploraProvider, RestArkProvider, RestIndexerProvider,
|
|
35
37
|
// Script-related
|
|
@@ -39,20 +41,22 @@ TxType, IndexerTxType, ChainTxType, SettlementEventType,
|
|
|
39
41
|
// Service Worker
|
|
40
42
|
setupServiceWorker, Worker, ServiceWorkerWallet, Request, Response,
|
|
41
43
|
// Tapscript
|
|
42
|
-
decodeTapscript, MultisigTapscript, CSVMultisigTapscript, ConditionCSVMultisigTapscript, ConditionMultisigTapscript, CLTVMultisigTapscript,
|
|
44
|
+
decodeTapscript, MultisigTapscript, CSVMultisigTapscript, ConditionCSVMultisigTapscript, ConditionMultisigTapscript, CLTVMultisigTapscript, TapTreeCoder,
|
|
43
45
|
// Ark PSBT fields
|
|
44
46
|
ArkPsbtFieldKey, ArkPsbtFieldKeyType, setArkPsbtField, getArkPsbtFields, CosignerPublicKey, VtxoTreeExpiry, VtxoTaprootTree, ConditionWitness,
|
|
45
47
|
// Utils
|
|
46
|
-
buildOffchainTx, waitForIncomingFunds, hasBoardingTxExpired,
|
|
48
|
+
buildOffchainTx, verifyTapscriptSignatures, waitForIncomingFunds, hasBoardingTxExpired,
|
|
47
49
|
// Arknote
|
|
48
50
|
ArkNote,
|
|
49
51
|
// Network
|
|
50
52
|
networks,
|
|
51
53
|
// Repositories
|
|
52
54
|
WalletRepositoryImpl, ContractRepositoryImpl,
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
+
// Intent proof
|
|
56
|
+
Intent,
|
|
55
57
|
// TxTree
|
|
56
58
|
TxTree,
|
|
57
59
|
// Anchor
|
|
58
|
-
P2A, Unroll, Transaction,
|
|
60
|
+
P2A, Unroll, Transaction,
|
|
61
|
+
// Errors
|
|
62
|
+
ArkError, maybeArkError, };
|