@arkade-os/sdk 0.3.0-alpha.8 → 0.3.1-alpha.1
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 +64 -14
- package/dist/cjs/arknote/index.js +3 -3
- package/dist/cjs/forfeit.js +5 -2
- package/dist/cjs/identity/singleKey.js +5 -4
- package/dist/cjs/index.js +6 -3
- package/dist/cjs/{bip322 → intent}/index.js +37 -55
- package/dist/cjs/providers/ark.js +62 -23
- 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/script/base.js +1 -2
- package/dist/cjs/script/tapscript.js +20 -21
- package/dist/cjs/script/vhtlc.js +2 -2
- package/dist/cjs/tree/signingSession.js +7 -8
- package/dist/cjs/tree/txTree.js +3 -4
- package/dist/cjs/tree/validation.js +2 -3
- package/dist/cjs/utils/arkTransaction.js +104 -12
- package/dist/cjs/utils/unknownFields.js +5 -5
- package/dist/cjs/wallet/onchain.js +4 -5
- package/dist/cjs/wallet/serviceWorker/utils.js +2 -0
- package/dist/cjs/wallet/serviceWorker/wallet.js +4 -8
- package/dist/cjs/wallet/serviceWorker/worker.js +23 -18
- package/dist/cjs/wallet/unroll.js +6 -7
- package/dist/cjs/wallet/vtxo-manager.js +381 -0
- package/dist/cjs/wallet/wallet.js +63 -94
- package/dist/esm/arknote/index.js +2 -2
- package/dist/esm/forfeit.js +4 -1
- package/dist/esm/identity/singleKey.js +7 -6
- package/dist/esm/index.js +7 -6
- package/dist/esm/{bip322 → intent}/index.js +31 -48
- package/dist/esm/providers/ark.js +62 -23
- 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/script/base.js +1 -2
- package/dist/esm/script/tapscript.js +1 -2
- package/dist/esm/script/vhtlc.js +1 -1
- package/dist/esm/tree/signingSession.js +8 -9
- package/dist/esm/tree/txTree.js +3 -4
- package/dist/esm/tree/validation.js +2 -3
- package/dist/esm/utils/arkTransaction.js +95 -4
- package/dist/esm/utils/unknownFields.js +1 -1
- package/dist/esm/wallet/onchain.js +1 -2
- package/dist/esm/wallet/serviceWorker/utils.js +1 -0
- package/dist/esm/wallet/serviceWorker/wallet.js +5 -9
- package/dist/esm/wallet/serviceWorker/worker.js +23 -18
- package/dist/esm/wallet/unroll.js +2 -3
- package/dist/esm/wallet/vtxo-manager.js +372 -0
- package/dist/esm/wallet/wallet.js +56 -87
- package/dist/types/arknote/index.d.ts +1 -1
- package/dist/types/forfeit.d.ts +2 -2
- package/dist/types/identity/index.d.ts +1 -1
- package/dist/types/identity/singleKey.d.ts +1 -1
- package/dist/types/index.d.ts +6 -5
- package/dist/types/intent/index.d.ts +41 -0
- package/dist/types/providers/ark.d.ts +55 -21
- 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/script/base.d.ts +3 -2
- package/dist/types/tree/signingSession.d.ts +10 -10
- package/dist/types/utils/anchor.d.ts +2 -2
- package/dist/types/utils/arkTransaction.d.ts +13 -3
- package/dist/types/utils/unknownFields.d.ts +2 -2
- package/dist/types/wallet/index.d.ts +6 -4
- package/dist/types/wallet/onchain.d.ts +1 -1
- 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 +3 -1
- package/dist/types/wallet/unroll.d.ts +1 -1
- package/dist/types/wallet/vtxo-manager.d.ts +207 -0
- package/dist/types/wallet/wallet.d.ts +7 -3
- 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);
|
|
@@ -418,7 +427,7 @@ class Wallet {
|
|
|
418
427
|
// TODO persist final virtual tx and checkpoints to repository
|
|
419
428
|
// sign the checkpoints
|
|
420
429
|
const finalCheckpoints = await Promise.all(signedCheckpointTxs.map(async (c) => {
|
|
421
|
-
const tx =
|
|
430
|
+
const tx = btc_signer_1.Transaction.fromPSBT(base_1.base64.decode(c));
|
|
422
431
|
const signedCheckpoint = await this.identity.sign(tx);
|
|
423
432
|
return base_1.base64.encode(signedCheckpoint.toPSBT());
|
|
424
433
|
}));
|
|
@@ -476,8 +485,8 @@ class Wallet {
|
|
|
476
485
|
}
|
|
477
486
|
catch {
|
|
478
487
|
// onchain
|
|
479
|
-
const addr = (0,
|
|
480
|
-
script =
|
|
488
|
+
const addr = (0, btc_signer_1.Address)(this.network).decode(output.address);
|
|
489
|
+
script = btc_signer_1.OutScript.encode(addr);
|
|
481
490
|
onchainOutputIndexes.push(index);
|
|
482
491
|
}
|
|
483
492
|
outputs.push({
|
|
@@ -490,7 +499,7 @@ class Wallet {
|
|
|
490
499
|
const signingPublicKeys = [];
|
|
491
500
|
if (hasOffchainOutputs) {
|
|
492
501
|
session = this.identity.signerSession();
|
|
493
|
-
signingPublicKeys.push(base_1.hex.encode(session.getPublicKey()));
|
|
502
|
+
signingPublicKeys.push(base_1.hex.encode(await session.getPublicKey()));
|
|
494
503
|
}
|
|
495
504
|
const [intent, deleteIntent] = await Promise.all([
|
|
496
505
|
this.makeRegisterIntentSignature(params.inputs, outputs, onchainOutputIndexes, signingPublicKeys),
|
|
@@ -529,7 +538,7 @@ class Wallet {
|
|
|
529
538
|
if (step !== undefined) {
|
|
530
539
|
continue;
|
|
531
540
|
}
|
|
532
|
-
const res = await this.handleBatchStartedEvent(event, intentId, this.
|
|
541
|
+
const res = await this.handleBatchStartedEvent(event, intentId, this.forfeitPubkey, this.forfeitOutputScript);
|
|
533
542
|
if (!res.skip) {
|
|
534
543
|
step = event.type;
|
|
535
544
|
sweepTapTreeRoot = res.sweepTapTreeRoot;
|
|
@@ -714,10 +723,10 @@ class Wallet {
|
|
|
714
723
|
};
|
|
715
724
|
return stopFunc;
|
|
716
725
|
}
|
|
717
|
-
async handleBatchStartedEvent(event, intentId,
|
|
726
|
+
async handleBatchStartedEvent(event, intentId, forfeitPubKey, forfeitOutputScript) {
|
|
718
727
|
const utf8IntentId = new TextEncoder().encode(intentId);
|
|
719
728
|
const intentIdHash = (0, utils_js_1.sha256)(utf8IntentId);
|
|
720
|
-
const intentIdHashStr = base_1.hex.encode(
|
|
729
|
+
const intentIdHashStr = base_1.hex.encode(intentIdHash);
|
|
721
730
|
let skip = true;
|
|
722
731
|
// check if our intent ID hash matches any in the event
|
|
723
732
|
for (const idHash of event.intentIdHashes) {
|
|
@@ -737,7 +746,7 @@ class Wallet {
|
|
|
737
746
|
value: event.batchExpiry,
|
|
738
747
|
type: event.batchExpiry >= 512n ? "seconds" : "blocks",
|
|
739
748
|
},
|
|
740
|
-
pubkeys: [
|
|
749
|
+
pubkeys: [forfeitPubKey],
|
|
741
750
|
}).script;
|
|
742
751
|
const sweepTapTreeRoot = (0, payment_js_1.tapLeafHash)(sweepTapscript);
|
|
743
752
|
return {
|
|
@@ -750,7 +759,7 @@ class Wallet {
|
|
|
750
759
|
// validates the vtxo tree, creates a signing session and generates the musig2 nonces
|
|
751
760
|
async handleSettlementSigningEvent(event, sweepTapTreeRoot, session, vtxoGraph) {
|
|
752
761
|
// validate the unsigned vtxo tree
|
|
753
|
-
const commitmentTx =
|
|
762
|
+
const commitmentTx = btc_signer_1.Transaction.fromPSBT(base_1.base64.decode(event.unsignedCommitmentTx));
|
|
754
763
|
(0, validation_1.validateVtxoTxGraph)(vtxoGraph, commitmentTx, sweepTapTreeRoot);
|
|
755
764
|
// TODO check if our registered outputs are in the vtxo tree
|
|
756
765
|
const sharedOutput = commitmentTx.getOutput(0);
|
|
@@ -758,18 +767,21 @@ class Wallet {
|
|
|
758
767
|
throw new Error("Shared output not found");
|
|
759
768
|
}
|
|
760
769
|
session.init(vtxoGraph, sweepTapTreeRoot, sharedOutput.amount);
|
|
761
|
-
|
|
770
|
+
const pubkey = base_1.hex.encode(await session.getPublicKey());
|
|
771
|
+
const nonces = await session.getNonces();
|
|
772
|
+
await this.arkProvider.submitTreeNonces(event.id, pubkey, nonces);
|
|
762
773
|
}
|
|
763
774
|
async handleSettlementSigningNoncesGeneratedEvent(event, session) {
|
|
764
775
|
session.setAggregatedNonces(event.treeNonces);
|
|
765
|
-
const signatures = session.sign();
|
|
766
|
-
|
|
776
|
+
const signatures = await session.sign();
|
|
777
|
+
const pubkey = base_1.hex.encode(await session.getPublicKey());
|
|
778
|
+
await this.arkProvider.submitTreeSignatures(event.id, pubkey, signatures);
|
|
767
779
|
}
|
|
768
780
|
async handleSettlementFinalizationEvent(event, inputs, forfeitOutputScript, connectorsGraph) {
|
|
769
781
|
// the signed forfeits transactions to submit
|
|
770
782
|
const signedForfeits = [];
|
|
771
783
|
const vtxos = await this.getVirtualCoins();
|
|
772
|
-
let settlementPsbt =
|
|
784
|
+
let settlementPsbt = btc_signer_1.Transaction.fromPSBT(base_1.base64.decode(event.commitmentTx));
|
|
773
785
|
let hasBoardingUtxos = false;
|
|
774
786
|
let connectorIndex = 0;
|
|
775
787
|
const connectorsLeaves = connectorsGraph?.leaves() || [];
|
|
@@ -811,7 +823,7 @@ class Wallet {
|
|
|
811
823
|
throw new Error("not enough connectors received");
|
|
812
824
|
}
|
|
813
825
|
const connectorLeaf = connectorsLeaves[connectorIndex];
|
|
814
|
-
const connectorTxId =
|
|
826
|
+
const connectorTxId = connectorLeaf.id;
|
|
815
827
|
const connectorOutput = connectorLeaf.getOutput(0);
|
|
816
828
|
if (!connectorOutput) {
|
|
817
829
|
throw new Error("connector output not found");
|
|
@@ -830,7 +842,7 @@ class Wallet {
|
|
|
830
842
|
amount: BigInt(vtxo.value),
|
|
831
843
|
script: base_2.VtxoScript.decode(input.tapTree).pkScript,
|
|
832
844
|
},
|
|
833
|
-
sighashType:
|
|
845
|
+
sighashType: btc_signer_1.SigHash.DEFAULT,
|
|
834
846
|
tapLeafScript: [input.forfeitTapLeafScript],
|
|
835
847
|
},
|
|
836
848
|
{
|
|
@@ -852,112 +864,69 @@ class Wallet {
|
|
|
852
864
|
: undefined);
|
|
853
865
|
}
|
|
854
866
|
}
|
|
855
|
-
async makeRegisterIntentSignature(
|
|
867
|
+
async makeRegisterIntentSignature(coins, outputs, onchainOutputsIndexes, cosignerPubKeys) {
|
|
856
868
|
const nowSeconds = Math.floor(Date.now() / 1000);
|
|
857
|
-
const
|
|
869
|
+
const inputs = this.prepareIntentProofInputs(coins);
|
|
858
870
|
const message = {
|
|
859
871
|
type: "register",
|
|
860
|
-
input_tap_trees: inputTapTrees,
|
|
861
872
|
onchain_output_indexes: onchainOutputsIndexes,
|
|
862
873
|
valid_at: nowSeconds,
|
|
863
874
|
expire_at: nowSeconds + 2 * 60, // valid for 2 minutes
|
|
864
875
|
cosigners_public_keys: cosignerPubKeys,
|
|
865
876
|
};
|
|
866
877
|
const encodedMessage = JSON.stringify(message, null, 0);
|
|
867
|
-
const
|
|
878
|
+
const proof = intent_1.Intent.create(encodedMessage, inputs, outputs);
|
|
879
|
+
const signedProof = await this.identity.sign(proof);
|
|
868
880
|
return {
|
|
869
|
-
|
|
881
|
+
proof: base_1.base64.encode(signedProof.toPSBT()),
|
|
870
882
|
message: encodedMessage,
|
|
871
883
|
};
|
|
872
884
|
}
|
|
873
|
-
async makeDeleteIntentSignature(
|
|
885
|
+
async makeDeleteIntentSignature(coins) {
|
|
874
886
|
const nowSeconds = Math.floor(Date.now() / 1000);
|
|
875
|
-
const
|
|
887
|
+
const inputs = this.prepareIntentProofInputs(coins);
|
|
876
888
|
const message = {
|
|
877
889
|
type: "delete",
|
|
878
890
|
expire_at: nowSeconds + 2 * 60, // valid for 2 minutes
|
|
879
891
|
};
|
|
880
892
|
const encodedMessage = JSON.stringify(message, null, 0);
|
|
881
|
-
const
|
|
893
|
+
const proof = intent_1.Intent.create(encodedMessage, inputs, []);
|
|
894
|
+
const signedProof = await this.identity.sign(proof);
|
|
882
895
|
return {
|
|
883
|
-
|
|
896
|
+
proof: base_1.base64.encode(signedProof.toPSBT()),
|
|
884
897
|
message: encodedMessage,
|
|
885
898
|
};
|
|
886
899
|
}
|
|
887
|
-
|
|
900
|
+
prepareIntentProofInputs(coins) {
|
|
888
901
|
const inputs = [];
|
|
889
|
-
const
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
const
|
|
893
|
-
|
|
902
|
+
for (const input of coins) {
|
|
903
|
+
const vtxoScript = base_2.VtxoScript.decode(input.tapTree);
|
|
904
|
+
const sequence = getSequence(input);
|
|
905
|
+
const unknown = [unknownFields_1.VtxoTaprootTree.encode(input.tapTree)];
|
|
906
|
+
if (input.extraWitness) {
|
|
907
|
+
unknown.push(unknownFields_1.ConditionWitness.encode(input.extraWitness));
|
|
908
|
+
}
|
|
894
909
|
inputs.push({
|
|
895
|
-
txid: base_1.hex.decode(
|
|
896
|
-
index:
|
|
910
|
+
txid: base_1.hex.decode(input.txid),
|
|
911
|
+
index: input.vout,
|
|
897
912
|
witnessUtxo: {
|
|
898
|
-
amount: BigInt(
|
|
913
|
+
amount: BigInt(input.value),
|
|
899
914
|
script: vtxoScript.pkScript,
|
|
900
915
|
},
|
|
901
916
|
sequence,
|
|
902
|
-
tapLeafScript: [
|
|
917
|
+
tapLeafScript: [input.intentTapLeafScript],
|
|
918
|
+
unknown,
|
|
903
919
|
});
|
|
904
|
-
inputTapTrees.push(base_1.hex.encode(bip322Input.tapTree));
|
|
905
|
-
inputExtraWitnesses.push(bip322Input.extraWitness || []);
|
|
906
920
|
}
|
|
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);
|
|
921
|
+
return inputs;
|
|
917
922
|
}
|
|
918
923
|
}
|
|
919
924
|
exports.Wallet = Wallet;
|
|
920
925
|
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) {
|
|
926
|
+
function getSequence(coin) {
|
|
958
927
|
let sequence = undefined;
|
|
959
928
|
try {
|
|
960
|
-
const scriptWithLeafVersion =
|
|
929
|
+
const scriptWithLeafVersion = coin.intentTapLeafScript[1];
|
|
961
930
|
const script = scriptWithLeafVersion.subarray(0, scriptWithLeafVersion.length - 1);
|
|
962
931
|
const params = tapscript_1.CSVMultisigTapscript.decode(script).params;
|
|
963
932
|
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,12 @@
|
|
|
1
|
-
import { Transaction } from "@scure/btc-signer
|
|
1
|
+
import { Transaction } from "@scure/btc-signer";
|
|
2
2
|
import { P2A } from './utils/anchor.js';
|
|
3
3
|
export function buildForfeitTx(inputs, forfeitPkScript, txLocktime) {
|
|
4
4
|
const tx = new Transaction({
|
|
5
5
|
version: 3,
|
|
6
6
|
lockTime: txLocktime,
|
|
7
|
+
allowUnknownOutputs: true,
|
|
8
|
+
allowUnknown: true,
|
|
9
|
+
allowUnknownInputs: true,
|
|
7
10
|
});
|
|
8
11
|
let amount = 0n;
|
|
9
12
|
for (const input of inputs) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { pubECDSA, pubSchnorr, randomPrivateKeyBytes,
|
|
2
|
-
import { hex } from "@scure/base";
|
|
1
|
+
import { pubECDSA, pubSchnorr, randomPrivateKeyBytes, } from "@scure/btc-signer/utils.js";
|
|
3
2
|
import { SigHash } from "@scure/btc-signer/transaction.js";
|
|
3
|
+
import { hex } from "@scure/base";
|
|
4
4
|
import { TreeSignerSession } from '../tree/signingSession.js';
|
|
5
|
-
import { schnorr } from "@noble/secp256k1";
|
|
5
|
+
import { schnorr, sign } from "@noble/secp256k1";
|
|
6
6
|
const ZERO_32 = new Uint8Array(32).fill(0);
|
|
7
7
|
const ALL_SIGHASH = Object.values(SigHash).filter((x) => typeof x === "number");
|
|
8
8
|
/**
|
|
@@ -79,8 +79,9 @@ export class SingleKey {
|
|
|
79
79
|
signerSession() {
|
|
80
80
|
return TreeSignerSession.random();
|
|
81
81
|
}
|
|
82
|
-
async signMessage(message) {
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
async signMessage(message, signatureType = "schnorr") {
|
|
83
|
+
if (signatureType === "ecdsa")
|
|
84
|
+
return sign(message, this.key, { prehash: false });
|
|
85
|
+
return schnorr.sign(message, this.key);
|
|
85
86
|
}
|
|
86
87
|
}
|
package/dist/esm/index.js
CHANGED
|
@@ -8,6 +8,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';
|
|
@@ -29,7 +30,7 @@ import { WalletRepositoryImpl } from './repositories/walletRepository.js';
|
|
|
29
30
|
import { ContractRepositoryImpl } from './repositories/contractRepository.js';
|
|
30
31
|
export {
|
|
31
32
|
// Wallets
|
|
32
|
-
Wallet, SingleKey, OnchainWallet, Ramps,
|
|
33
|
+
Wallet, SingleKey, OnchainWallet, Ramps, VtxoManager,
|
|
33
34
|
// Providers
|
|
34
35
|
ESPLORA_URL, EsploraProvider, RestArkProvider, RestIndexerProvider,
|
|
35
36
|
// Script-related
|
|
@@ -43,15 +44,15 @@ decodeTapscript, MultisigTapscript, CSVMultisigTapscript, ConditionCSVMultisigTa
|
|
|
43
44
|
// Ark PSBT fields
|
|
44
45
|
ArkPsbtFieldKey, ArkPsbtFieldKeyType, setArkPsbtField, getArkPsbtFields, CosignerPublicKey, VtxoTreeExpiry, VtxoTaprootTree, ConditionWitness,
|
|
45
46
|
// Utils
|
|
46
|
-
buildOffchainTx, waitForIncomingFunds, hasBoardingTxExpired,
|
|
47
|
+
buildOffchainTx, verifyTapscriptSignatures, waitForIncomingFunds, hasBoardingTxExpired,
|
|
47
48
|
// Arknote
|
|
48
49
|
ArkNote,
|
|
49
50
|
// Network
|
|
50
51
|
networks,
|
|
51
52
|
// Repositories
|
|
52
53
|
WalletRepositoryImpl, ContractRepositoryImpl,
|
|
53
|
-
//
|
|
54
|
-
|
|
54
|
+
// Intent proof
|
|
55
|
+
Intent,
|
|
55
56
|
// TxTree
|
|
56
57
|
TxTree,
|
|
57
58
|
// Anchor
|
|
@@ -1,22 +1,19 @@
|
|
|
1
|
-
import { OP } from "@scure/btc-signer
|
|
2
|
-
import { Transaction, SigHash } from "@scure/btc-signer/transaction.js";
|
|
3
|
-
import { Script } from "@scure/btc-signer/script.js";
|
|
4
|
-
import { ErrMissingData, ErrMissingInputs, ErrMissingWitnessUtxo, } from './errors.js';
|
|
1
|
+
import { OP, Transaction, Script, SigHash } from "@scure/btc-signer";
|
|
5
2
|
import { schnorr } from "@noble/curves/secp256k1.js";
|
|
6
|
-
import { base64 } from "@scure/base";
|
|
7
3
|
/**
|
|
8
|
-
*
|
|
4
|
+
* Intent proof implementation for Bitcoin message signing.
|
|
9
5
|
*
|
|
10
|
-
*
|
|
6
|
+
* Intent proof defines a standard for signing Bitcoin messages as well as proving
|
|
11
7
|
* ownership of coins. This namespace provides utilities for creating and
|
|
12
|
-
* validating
|
|
8
|
+
* validating Intent proof.
|
|
13
9
|
*
|
|
10
|
+
* it is greatly inspired by BIP322.
|
|
14
11
|
* @see https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki
|
|
15
12
|
*
|
|
16
13
|
* @example
|
|
17
14
|
* ```typescript
|
|
18
|
-
* // Create a
|
|
19
|
-
* const proof =
|
|
15
|
+
* // Create a Intent proof
|
|
16
|
+
* const proof = Intent.create(
|
|
20
17
|
* "Hello Bitcoin!",
|
|
21
18
|
* [input],
|
|
22
19
|
* [output]
|
|
@@ -25,65 +22,46 @@ import { base64 } from "@scure/base";
|
|
|
25
22
|
* // Sign the proof
|
|
26
23
|
* const signedProof = await identity.sign(proof);
|
|
27
24
|
*
|
|
28
|
-
* // Extract the signature
|
|
29
|
-
* const signature = BIP322.signature(signedProof);
|
|
30
|
-
* ```
|
|
31
25
|
*/
|
|
32
|
-
export var
|
|
33
|
-
(function (
|
|
26
|
+
export var Intent;
|
|
27
|
+
(function (Intent) {
|
|
34
28
|
/**
|
|
35
|
-
* Creates a new
|
|
29
|
+
* Creates a new Intent proof unsigned transaction.
|
|
36
30
|
*
|
|
37
31
|
* This function constructs a special transaction that can be signed to prove
|
|
38
32
|
* ownership of VTXOs and UTXOs. The proof includes the message to be
|
|
39
33
|
* signed and the inputs/outputs that demonstrate ownership.
|
|
40
34
|
*
|
|
41
|
-
* @param message - The
|
|
35
|
+
* @param message - The Intent message to be signed
|
|
42
36
|
* @param inputs - Array of transaction inputs to prove ownership of
|
|
43
37
|
* @param outputs - Optional array of transaction outputs
|
|
44
|
-
* @returns An unsigned
|
|
38
|
+
* @returns An unsigned Intent proof transaction
|
|
45
39
|
*/
|
|
46
40
|
function create(message, inputs, outputs = []) {
|
|
47
41
|
if (inputs.length == 0)
|
|
48
|
-
throw
|
|
42
|
+
throw new Error("intent proof requires at least one input");
|
|
49
43
|
if (!validateInputs(inputs))
|
|
50
|
-
throw
|
|
44
|
+
throw new Error("invalid inputs");
|
|
51
45
|
if (!validateOutputs(outputs))
|
|
52
|
-
throw
|
|
46
|
+
throw new Error("invalid outputs");
|
|
53
47
|
// create the initial transaction to spend
|
|
54
48
|
const toSpend = craftToSpendTx(message, inputs[0].witnessUtxo.script);
|
|
55
49
|
// create the transaction to sign
|
|
56
50
|
return craftToSignTx(toSpend, inputs, outputs);
|
|
57
51
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
* Finalizes and extracts the FullProof transaction into a BIP-322 signature.
|
|
61
|
-
*
|
|
62
|
-
* This function takes a signed proof transaction and converts it into a
|
|
63
|
-
* base64-encoded signature string. If the proof's inputs have special
|
|
64
|
-
* spending conditions, a custom finalizer can be provided.
|
|
65
|
-
*
|
|
66
|
-
* @param signedProof - The signed BIP-322 proof transaction
|
|
67
|
-
* @param finalizer - Optional custom finalizer function
|
|
68
|
-
* @returns Base64-encoded BIP-322 signature
|
|
69
|
-
*/
|
|
70
|
-
function signature(signedProof, finalizer = (tx) => tx.finalize()) {
|
|
71
|
-
finalizer(signedProof);
|
|
72
|
-
return base64.encode(signedProof.extract());
|
|
73
|
-
}
|
|
74
|
-
BIP322.signature = signature;
|
|
75
|
-
})(BIP322 || (BIP322 = {}));
|
|
52
|
+
Intent.create = create;
|
|
53
|
+
})(Intent || (Intent = {}));
|
|
76
54
|
const OP_RETURN_EMPTY_PKSCRIPT = new Uint8Array([OP.RETURN]);
|
|
77
55
|
const ZERO_32 = new Uint8Array(32).fill(0);
|
|
78
56
|
const MAX_INDEX = 0xffffffff;
|
|
79
|
-
const
|
|
57
|
+
const TAG_INTENT_PROOF = "ark-intent-proof-message";
|
|
80
58
|
function validateInput(input) {
|
|
81
59
|
if (input.index === undefined)
|
|
82
|
-
throw
|
|
60
|
+
throw new Error("intent proof input requires index");
|
|
83
61
|
if (input.txid === undefined)
|
|
84
|
-
throw
|
|
62
|
+
throw new Error("intent proof input requires txid");
|
|
85
63
|
if (input.witnessUtxo === undefined)
|
|
86
|
-
throw
|
|
64
|
+
throw new Error("intent proof input requires witness utxo");
|
|
87
65
|
return true;
|
|
88
66
|
}
|
|
89
67
|
function validateInputs(inputs) {
|
|
@@ -92,9 +70,9 @@ function validateInputs(inputs) {
|
|
|
92
70
|
}
|
|
93
71
|
function validateOutput(output) {
|
|
94
72
|
if (output.amount === undefined)
|
|
95
|
-
throw
|
|
73
|
+
throw new Error("intent proof output requires amount");
|
|
96
74
|
if (output.script === undefined)
|
|
97
|
-
throw
|
|
75
|
+
throw new Error("intent proof output requires script");
|
|
98
76
|
return true;
|
|
99
77
|
}
|
|
100
78
|
function validateOutputs(outputs) {
|
|
@@ -102,7 +80,7 @@ function validateOutputs(outputs) {
|
|
|
102
80
|
return true;
|
|
103
81
|
}
|
|
104
82
|
// craftToSpendTx creates the initial transaction that will be spent in the proof
|
|
105
|
-
|
|
83
|
+
function craftToSpendTx(message, pkScript) {
|
|
106
84
|
const messageHash = hashMessage(message);
|
|
107
85
|
const tx = new Transaction({
|
|
108
86
|
version: 0,
|
|
@@ -148,11 +126,16 @@ function craftToSignTx(toSpend, inputs, outputs) {
|
|
|
148
126
|
sighashType: SigHash.ALL,
|
|
149
127
|
});
|
|
150
128
|
// add other inputs
|
|
151
|
-
for (const input of inputs) {
|
|
129
|
+
for (const [i, input] of inputs.entries()) {
|
|
152
130
|
tx.addInput({
|
|
153
131
|
...input,
|
|
154
132
|
sighashType: SigHash.ALL,
|
|
155
133
|
});
|
|
134
|
+
if (input.unknown?.length) {
|
|
135
|
+
tx.updateInput(i + 1, {
|
|
136
|
+
unknown: input.unknown,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
156
139
|
}
|
|
157
140
|
// add the special OP_RETURN output if no outputs are provided
|
|
158
141
|
if (outputs.length === 0) {
|
|
@@ -172,5 +155,5 @@ function craftToSignTx(toSpend, inputs, outputs) {
|
|
|
172
155
|
return tx;
|
|
173
156
|
}
|
|
174
157
|
function hashMessage(message) {
|
|
175
|
-
return schnorr.utils.taggedHash(
|
|
158
|
+
return schnorr.utils.taggedHash(TAG_INTENT_PROOF, new TextEncoder().encode(message));
|
|
176
159
|
}
|