@atomiqlabs/chain-starknet 4.0.0-dev.12 → 4.0.0-dev.13
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/LICENSE +201 -201
- package/dist/index.d.ts +38 -38
- package/dist/index.js +54 -54
- package/dist/starknet/StarknetChainType.d.ts +13 -13
- package/dist/starknet/StarknetChainType.js +2 -2
- package/dist/starknet/StarknetInitializer.d.ts +27 -27
- package/dist/starknet/StarknetInitializer.js +69 -69
- package/dist/starknet/btcrelay/BtcRelayAbi.d.ts +250 -250
- package/dist/starknet/btcrelay/BtcRelayAbi.js +341 -341
- package/dist/starknet/btcrelay/StarknetBtcRelay.d.ts +186 -186
- package/dist/starknet/btcrelay/StarknetBtcRelay.js +379 -379
- package/dist/starknet/btcrelay/headers/StarknetBtcHeader.d.ts +31 -31
- package/dist/starknet/btcrelay/headers/StarknetBtcHeader.js +74 -74
- package/dist/starknet/btcrelay/headers/StarknetBtcStoredHeader.d.ts +51 -51
- package/dist/starknet/btcrelay/headers/StarknetBtcStoredHeader.js +113 -113
- package/dist/starknet/chain/StarknetAction.d.ts +19 -19
- package/dist/starknet/chain/StarknetAction.js +73 -73
- package/dist/starknet/chain/StarknetChainInterface.d.ts +52 -52
- package/dist/starknet/chain/StarknetChainInterface.js +91 -91
- package/dist/starknet/chain/StarknetModule.d.ts +14 -14
- package/dist/starknet/chain/StarknetModule.js +13 -13
- package/dist/starknet/chain/modules/ERC20Abi.d.ts +755 -755
- package/dist/starknet/chain/modules/ERC20Abi.js +1032 -1032
- package/dist/starknet/chain/modules/StarknetAccounts.d.ts +6 -6
- package/dist/starknet/chain/modules/StarknetAccounts.js +24 -24
- package/dist/starknet/chain/modules/StarknetAddresses.d.ts +9 -9
- package/dist/starknet/chain/modules/StarknetAddresses.js +26 -26
- package/dist/starknet/chain/modules/StarknetBlocks.d.ts +20 -20
- package/dist/starknet/chain/modules/StarknetBlocks.js +64 -64
- package/dist/starknet/chain/modules/StarknetEvents.d.ts +44 -44
- package/dist/starknet/chain/modules/StarknetEvents.js +88 -88
- package/dist/starknet/chain/modules/StarknetFees.d.ts +77 -77
- package/dist/starknet/chain/modules/StarknetFees.js +114 -114
- package/dist/starknet/chain/modules/StarknetSignatures.d.ts +29 -29
- package/dist/starknet/chain/modules/StarknetSignatures.js +72 -72
- package/dist/starknet/chain/modules/StarknetTokens.d.ts +69 -69
- package/dist/starknet/chain/modules/StarknetTokens.js +102 -98
- package/dist/starknet/chain/modules/StarknetTransactions.d.ts +93 -93
- package/dist/starknet/chain/modules/StarknetTransactions.js +261 -260
- package/dist/starknet/contract/StarknetContractBase.d.ts +13 -13
- package/dist/starknet/contract/StarknetContractBase.js +20 -16
- package/dist/starknet/contract/StarknetContractModule.d.ts +8 -8
- package/dist/starknet/contract/StarknetContractModule.js +11 -11
- package/dist/starknet/contract/modules/StarknetContractEvents.d.ts +51 -51
- package/dist/starknet/contract/modules/StarknetContractEvents.js +97 -97
- package/dist/starknet/events/StarknetChainEvents.d.ts +21 -21
- package/dist/starknet/events/StarknetChainEvents.js +52 -52
- package/dist/starknet/events/StarknetChainEventsBrowser.d.ts +89 -90
- package/dist/starknet/events/StarknetChainEventsBrowser.js +296 -294
- package/dist/starknet/provider/RpcProviderWithRetries.d.ts +21 -21
- package/dist/starknet/provider/RpcProviderWithRetries.js +32 -32
- package/dist/starknet/spv_swap/SpvVaultContractAbi.d.ts +488 -488
- package/dist/starknet/spv_swap/SpvVaultContractAbi.js +656 -656
- package/dist/starknet/spv_swap/StarknetSpvVaultContract.d.ts +66 -66
- package/dist/starknet/spv_swap/StarknetSpvVaultContract.js +382 -382
- package/dist/starknet/spv_swap/StarknetSpvVaultData.d.ts +49 -49
- package/dist/starknet/spv_swap/StarknetSpvVaultData.js +145 -145
- package/dist/starknet/spv_swap/StarknetSpvWithdrawalData.d.ts +25 -25
- package/dist/starknet/spv_swap/StarknetSpvWithdrawalData.js +72 -72
- package/dist/starknet/swaps/EscrowManagerAbi.d.ts +431 -431
- package/dist/starknet/swaps/EscrowManagerAbi.js +583 -583
- package/dist/starknet/swaps/StarknetSwapContract.d.ts +191 -191
- package/dist/starknet/swaps/StarknetSwapContract.js +424 -424
- package/dist/starknet/swaps/StarknetSwapData.d.ts +74 -74
- package/dist/starknet/swaps/StarknetSwapData.js +325 -325
- package/dist/starknet/swaps/StarknetSwapModule.d.ts +10 -10
- package/dist/starknet/swaps/StarknetSwapModule.js +11 -11
- package/dist/starknet/swaps/handlers/IHandler.d.ts +13 -13
- package/dist/starknet/swaps/handlers/IHandler.js +2 -2
- package/dist/starknet/swaps/handlers/claim/ClaimHandlers.d.ts +13 -13
- package/dist/starknet/swaps/handlers/claim/ClaimHandlers.js +13 -13
- package/dist/starknet/swaps/handlers/claim/HashlockClaimHandler.d.ts +21 -21
- package/dist/starknet/swaps/handlers/claim/HashlockClaimHandler.js +44 -44
- package/dist/starknet/swaps/handlers/claim/btc/BitcoinNoncedOutputClaimHandler.d.ts +24 -24
- package/dist/starknet/swaps/handlers/claim/btc/BitcoinNoncedOutputClaimHandler.js +48 -48
- package/dist/starknet/swaps/handlers/claim/btc/BitcoinOutputClaimHandler.d.ts +25 -25
- package/dist/starknet/swaps/handlers/claim/btc/BitcoinOutputClaimHandler.js +40 -40
- package/dist/starknet/swaps/handlers/claim/btc/BitcoinTxIdClaimHandler.d.ts +20 -20
- package/dist/starknet/swaps/handlers/claim/btc/BitcoinTxIdClaimHandler.js +30 -30
- package/dist/starknet/swaps/handlers/claim/btc/IBitcoinClaimHandler.d.ts +45 -45
- package/dist/starknet/swaps/handlers/claim/btc/IBitcoinClaimHandler.js +52 -52
- package/dist/starknet/swaps/handlers/refund/TimelockRefundHandler.d.ts +17 -17
- package/dist/starknet/swaps/handlers/refund/TimelockRefundHandler.js +27 -27
- package/dist/starknet/swaps/modules/StarknetLpVault.d.ts +69 -69
- package/dist/starknet/swaps/modules/StarknetLpVault.js +122 -122
- package/dist/starknet/swaps/modules/StarknetSwapClaim.d.ts +53 -53
- package/dist/starknet/swaps/modules/StarknetSwapClaim.js +100 -100
- package/dist/starknet/swaps/modules/StarknetSwapInit.d.ts +94 -87
- package/dist/starknet/swaps/modules/StarknetSwapInit.js +235 -225
- package/dist/starknet/swaps/modules/StarknetSwapRefund.d.ts +62 -62
- package/dist/starknet/swaps/modules/StarknetSwapRefund.js +128 -128
- package/dist/starknet/wallet/StarknetKeypairWallet.d.ts +7 -7
- package/dist/starknet/wallet/StarknetKeypairWallet.js +35 -30
- package/dist/starknet/wallet/StarknetSigner.d.ts +12 -12
- package/dist/starknet/wallet/StarknetSigner.js +47 -46
- package/dist/utils/Utils.d.ts +37 -37
- package/dist/utils/Utils.js +260 -261
- package/package.json +43 -37
- package/src/index.ts +47 -47
- package/src/starknet/StarknetChainType.ts +28 -28
- package/src/starknet/StarknetInitializer.ts +108 -108
- package/src/starknet/btcrelay/BtcRelayAbi.ts +338 -338
- package/src/starknet/btcrelay/StarknetBtcRelay.ts +494 -494
- package/src/starknet/btcrelay/headers/StarknetBtcHeader.ts +100 -100
- package/src/starknet/btcrelay/headers/StarknetBtcStoredHeader.ts +141 -141
- package/src/starknet/chain/StarknetAction.ts +85 -85
- package/src/starknet/chain/StarknetChainInterface.ts +149 -149
- package/src/starknet/chain/StarknetModule.ts +19 -19
- package/src/starknet/chain/modules/ERC20Abi.ts +1029 -1029
- package/src/starknet/chain/modules/StarknetAccounts.ts +25 -25
- package/src/starknet/chain/modules/StarknetAddresses.ts +22 -22
- package/src/starknet/chain/modules/StarknetBlocks.ts +75 -74
- package/src/starknet/chain/modules/StarknetEvents.ts +104 -104
- package/src/starknet/chain/modules/StarknetFees.ts +154 -154
- package/src/starknet/chain/modules/StarknetSignatures.ts +91 -91
- package/src/starknet/chain/modules/StarknetTokens.ts +120 -116
- package/src/starknet/chain/modules/StarknetTransactions.ts +285 -283
- package/src/starknet/contract/StarknetContractBase.ts +30 -26
- package/src/starknet/contract/StarknetContractModule.ts +16 -16
- package/src/starknet/contract/modules/StarknetContractEvents.ts +134 -134
- package/src/starknet/events/StarknetChainEvents.ts +67 -67
- package/src/starknet/events/StarknetChainEventsBrowser.ts +411 -411
- package/src/starknet/provider/RpcProviderWithRetries.ts +43 -43
- package/src/starknet/spv_swap/SpvVaultContractAbi.ts +656 -656
- package/src/starknet/spv_swap/StarknetSpvVaultContract.ts +483 -483
- package/src/starknet/spv_swap/StarknetSpvVaultData.ts +195 -195
- package/src/starknet/spv_swap/StarknetSpvWithdrawalData.ts +79 -79
- package/src/starknet/swaps/EscrowManagerAbi.ts +582 -582
- package/src/starknet/swaps/StarknetSwapContract.ts +647 -647
- package/src/starknet/swaps/StarknetSwapData.ts +455 -455
- package/src/starknet/swaps/StarknetSwapModule.ts +17 -17
- package/src/starknet/swaps/handlers/IHandler.ts +20 -20
- package/src/starknet/swaps/handlers/claim/ClaimHandlers.ts +23 -23
- package/src/starknet/swaps/handlers/claim/HashlockClaimHandler.ts +53 -53
- package/src/starknet/swaps/handlers/claim/btc/BitcoinNoncedOutputClaimHandler.ts +73 -73
- package/src/starknet/swaps/handlers/claim/btc/BitcoinOutputClaimHandler.ts +67 -67
- package/src/starknet/swaps/handlers/claim/btc/BitcoinTxIdClaimHandler.ts +50 -50
- package/src/starknet/swaps/handlers/claim/btc/IBitcoinClaimHandler.ts +102 -102
- package/src/starknet/swaps/handlers/refund/TimelockRefundHandler.ts +38 -38
- package/src/starknet/swaps/modules/StarknetLpVault.ts +147 -147
- package/src/starknet/swaps/modules/StarknetSwapClaim.ts +141 -141
- package/src/starknet/swaps/modules/StarknetSwapInit.ts +300 -287
- package/src/starknet/swaps/modules/StarknetSwapRefund.ts +196 -196
- package/src/starknet/wallet/StarknetKeypairWallet.ts +44 -39
- package/src/starknet/wallet/StarknetSigner.ts +55 -55
- package/src/utils/Utils.ts +251 -252
|
@@ -1,379 +1,379 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.StarknetBtcRelay = void 0;
|
|
4
|
-
const buffer_1 = require("buffer");
|
|
5
|
-
const StarknetBtcHeader_1 = require("./headers/StarknetBtcHeader");
|
|
6
|
-
const base_1 = require("@atomiqlabs/base");
|
|
7
|
-
const Utils_1 = require("../../utils/Utils");
|
|
8
|
-
const StarknetContractBase_1 = require("../contract/StarknetContractBase");
|
|
9
|
-
const StarknetBtcStoredHeader_1 = require("./headers/StarknetBtcStoredHeader");
|
|
10
|
-
const BtcRelayAbi_1 = require("./BtcRelayAbi");
|
|
11
|
-
const starknet_1 = require("starknet");
|
|
12
|
-
const StarknetFees_1 = require("../chain/modules/StarknetFees");
|
|
13
|
-
const StarknetAction_1 = require("../chain/StarknetAction");
|
|
14
|
-
function serializeBlockHeader(e) {
|
|
15
|
-
return new StarknetBtcHeader_1.StarknetBtcHeader({
|
|
16
|
-
reversed_version: (0, Utils_1.u32ReverseEndianness)(e.getVersion()),
|
|
17
|
-
previous_blockhash: (0, Utils_1.bufferToU32Array)(buffer_1.Buffer.from(e.getPrevBlockhash(), "hex").reverse()),
|
|
18
|
-
merkle_root: (0, Utils_1.bufferToU32Array)(buffer_1.Buffer.from(e.getMerkleRoot(), "hex").reverse()),
|
|
19
|
-
reversed_timestamp: (0, Utils_1.u32ReverseEndianness)(e.getTimestamp()),
|
|
20
|
-
nbits: (0, Utils_1.u32ReverseEndianness)(e.getNbits()),
|
|
21
|
-
nonce: (0, Utils_1.u32ReverseEndianness)(e.getNonce()),
|
|
22
|
-
hash: buffer_1.Buffer.from(e.getHash(), "hex").reverse()
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
const GAS_PER_BLOCKHEADER = { l1DataGas: 600, l2Gas: 40000000, l1Gas: 0 };
|
|
26
|
-
const GAS_PER_BLOCKHEADER_FORK = { l1DataGas: 1000, l2Gas: 60000000, l1Gas: 0 };
|
|
27
|
-
const btcRelayAddreses = {
|
|
28
|
-
[base_1.BitcoinNetwork.TESTNET4]: "0x0099b63f39f0cabb767361de3d8d3e97212351a51540e2687c2571f4da490dbe",
|
|
29
|
-
[base_1.BitcoinNetwork.TESTNET]: "0x068601c79da2231d21e015ccfd59c243861156fa523a12c9f987ec28eb8dbc8c",
|
|
30
|
-
[base_1.BitcoinNetwork.MAINNET]: "0x057b14a4231b82f1e525ff35a722d893ca3dd2bde0baa6cee97937c5be861dbc"
|
|
31
|
-
};
|
|
32
|
-
function serializeCalldata(headers, storedHeader, span) {
|
|
33
|
-
span.push((0, Utils_1.toHex)(headers.length));
|
|
34
|
-
headers.forEach(header => {
|
|
35
|
-
span.push(...header.serialize());
|
|
36
|
-
});
|
|
37
|
-
span.push(...storedHeader.serialize());
|
|
38
|
-
return span;
|
|
39
|
-
}
|
|
40
|
-
const logger = (0, Utils_1.getLogger)("StarknetBtcRelay: ");
|
|
41
|
-
class StarknetBtcRelay extends StarknetContractBase_1.StarknetContractBase {
|
|
42
|
-
SaveMainHeaders(signer, mainHeaders, storedHeader) {
|
|
43
|
-
return new StarknetAction_1.StarknetAction(signer, this.Chain, {
|
|
44
|
-
contractAddress: this.contract.address,
|
|
45
|
-
entrypoint: "submit_main_blockheaders",
|
|
46
|
-
calldata: serializeCalldata(mainHeaders, storedHeader, [])
|
|
47
|
-
}, (0, StarknetFees_1.starknetGasMul)(GAS_PER_BLOCKHEADER, mainHeaders.length));
|
|
48
|
-
}
|
|
49
|
-
SaveShortForkHeaders(signer, forkHeaders, storedHeader) {
|
|
50
|
-
return new StarknetAction_1.StarknetAction(signer, this.Chain, {
|
|
51
|
-
contractAddress: this.contract.address,
|
|
52
|
-
entrypoint: "submit_short_fork_blockheaders",
|
|
53
|
-
calldata: serializeCalldata(forkHeaders, storedHeader, [])
|
|
54
|
-
}, (0, StarknetFees_1.starknetGasMul)(GAS_PER_BLOCKHEADER, forkHeaders.length));
|
|
55
|
-
}
|
|
56
|
-
SaveLongForkHeaders(signer, forkId, forkHeaders, storedHeader, totalForkHeaders = 100) {
|
|
57
|
-
return new StarknetAction_1.StarknetAction(signer, this.Chain, {
|
|
58
|
-
contractAddress: this.contract.address,
|
|
59
|
-
entrypoint: "submit_fork_blockheaders",
|
|
60
|
-
calldata: serializeCalldata(forkHeaders, storedHeader, [(0, Utils_1.toHex)(forkId)])
|
|
61
|
-
}, (0, StarknetFees_1.starknetGasAdd)((0, StarknetFees_1.starknetGasMul)(GAS_PER_BLOCKHEADER, forkHeaders.length), (0, StarknetFees_1.starknetGasMul)(GAS_PER_BLOCKHEADER_FORK, totalForkHeaders)));
|
|
62
|
-
}
|
|
63
|
-
constructor(chainInterface, bitcoinRpc, bitcoinNetwork, contractAddress = btcRelayAddreses[bitcoinNetwork]) {
|
|
64
|
-
super(chainInterface, contractAddress, BtcRelayAbi_1.BtcRelayAbi);
|
|
65
|
-
this.maxHeadersPerTx = 100;
|
|
66
|
-
this.maxForkHeadersPerTx = 100;
|
|
67
|
-
this.maxShortForkHeadersPerTx = 100;
|
|
68
|
-
this.bitcoinRpc = bitcoinRpc;
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Computes subsequent commited headers as they will appear on the blockchain when transactions
|
|
72
|
-
* are submitted & confirmed
|
|
73
|
-
*
|
|
74
|
-
* @param initialStoredHeader
|
|
75
|
-
* @param syncedHeaders
|
|
76
|
-
* @private
|
|
77
|
-
*/
|
|
78
|
-
computeCommitedHeaders(initialStoredHeader, syncedHeaders) {
|
|
79
|
-
const computedCommitedHeaders = [initialStoredHeader];
|
|
80
|
-
for (let blockHeader of syncedHeaders) {
|
|
81
|
-
computedCommitedHeaders.push(computedCommitedHeaders[computedCommitedHeaders.length - 1].computeNext(blockHeader));
|
|
82
|
-
}
|
|
83
|
-
return computedCommitedHeaders;
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* A common logic for submitting blockheaders in a transaction
|
|
87
|
-
*
|
|
88
|
-
* @param signer
|
|
89
|
-
* @param headers headers to sync to the btc relay
|
|
90
|
-
* @param storedHeader current latest stored block header for a given fork
|
|
91
|
-
* @param tipWork work of the current tip in a given fork
|
|
92
|
-
* @param forkId forkId to submit to, forkId=0 means main chain, forkId=-1 means short fork
|
|
93
|
-
* @param feeRate feeRate for the transaction
|
|
94
|
-
* @private
|
|
95
|
-
*/
|
|
96
|
-
async _saveHeaders(signer, headers, storedHeader, tipWork, forkId, feeRate) {
|
|
97
|
-
const blockHeaderObj = headers.map(serializeBlockHeader);
|
|
98
|
-
let starknetAction;
|
|
99
|
-
switch (forkId) {
|
|
100
|
-
case -1:
|
|
101
|
-
starknetAction = this.SaveShortForkHeaders(signer, blockHeaderObj, storedHeader);
|
|
102
|
-
break;
|
|
103
|
-
case 0:
|
|
104
|
-
starknetAction = this.SaveMainHeaders(signer, blockHeaderObj, storedHeader);
|
|
105
|
-
break;
|
|
106
|
-
default:
|
|
107
|
-
starknetAction = this.SaveLongForkHeaders(signer, forkId, blockHeaderObj, storedHeader);
|
|
108
|
-
break;
|
|
109
|
-
}
|
|
110
|
-
const tx = await starknetAction.tx(feeRate);
|
|
111
|
-
const computedCommitedHeaders = this.computeCommitedHeaders(storedHeader, blockHeaderObj);
|
|
112
|
-
const lastStoredHeader = computedCommitedHeaders[computedCommitedHeaders.length - 1];
|
|
113
|
-
if (forkId !== 0 && base_1.StatePredictorUtils.gtBuffer(lastStoredHeader.getBlockHash(), tipWork)) {
|
|
114
|
-
//Fork's work is higher than main chain's work, this fork will become a main chain
|
|
115
|
-
forkId = 0;
|
|
116
|
-
}
|
|
117
|
-
return {
|
|
118
|
-
forkId: forkId,
|
|
119
|
-
lastStoredHeader,
|
|
120
|
-
tx,
|
|
121
|
-
computedCommitedHeaders
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
getBlock(commitHash, blockHash) {
|
|
125
|
-
const keys = [commitHash == null ? null : (0, Utils_1.toHex)(commitHash)];
|
|
126
|
-
if (blockHash != null) {
|
|
127
|
-
const starknetBlockHash = starknet_1.hash.computePoseidonHashOnElements((0, Utils_1.bufferToU32Array)(buffer_1.Buffer.from([...blockHash]).reverse()));
|
|
128
|
-
keys.push(starknetBlockHash);
|
|
129
|
-
}
|
|
130
|
-
return this.Events.findInContractEvents(["btc_relay::events::StoreHeader", "btc_relay::events::StoreForkHeader"], keys, (event) => {
|
|
131
|
-
return Promise.resolve([StarknetBtcStoredHeader_1.StarknetBtcStoredHeader.fromSerializedFeltArray(event.data), BigInt(event.params.commit_hash)]);
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
async getBlockHeight() {
|
|
135
|
-
return Number(await this.contract.get_blockheight());
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* Returns data about current main chain tip stored in the btc relay
|
|
139
|
-
*/
|
|
140
|
-
async getTipData() {
|
|
141
|
-
const commitHash = await this.contract.get_tip_commit_hash();
|
|
142
|
-
if (commitHash == null || BigInt(commitHash) === BigInt(0))
|
|
143
|
-
return null;
|
|
144
|
-
const result = await this.getBlock(commitHash);
|
|
145
|
-
if (result == null)
|
|
146
|
-
return null;
|
|
147
|
-
const [storedBlockHeader] = result;
|
|
148
|
-
return {
|
|
149
|
-
blockheight: storedBlockHeader.getBlockheight(),
|
|
150
|
-
commitHash: (0, Utils_1.bigNumberishToBuffer)(commitHash, 32).toString("hex"),
|
|
151
|
-
blockhash: storedBlockHeader.getBlockHash().toString("hex"),
|
|
152
|
-
chainWork: storedBlockHeader.getChainWork()
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
/**
|
|
156
|
-
* Retrieves blockheader with a specific blockhash, returns null if requiredBlockheight is provided and
|
|
157
|
-
* btc relay contract is not synced up to the desired blockheight
|
|
158
|
-
*
|
|
159
|
-
* @param blockData
|
|
160
|
-
* @param requiredBlockheight
|
|
161
|
-
*/
|
|
162
|
-
async retrieveLogAndBlockheight(blockData, requiredBlockheight) {
|
|
163
|
-
//TODO: we can fetch the blockheight and events in parallel
|
|
164
|
-
const blockHeight = await this.getBlockHeight();
|
|
165
|
-
if (requiredBlockheight != null && blockHeight < requiredBlockheight) {
|
|
166
|
-
return null;
|
|
167
|
-
}
|
|
168
|
-
const result = await this.getBlock(null, buffer_1.Buffer.from(blockData.blockhash, "hex"));
|
|
169
|
-
if (result == null)
|
|
170
|
-
return null;
|
|
171
|
-
const [storedBlockHeader, commitHash] = result;
|
|
172
|
-
//Check if block is part of the main chain
|
|
173
|
-
const chainCommitment = await this.contract.get_commit_hash(storedBlockHeader.block_height);
|
|
174
|
-
if (BigInt(chainCommitment) !== BigInt(commitHash))
|
|
175
|
-
return null;
|
|
176
|
-
logger.debug("retrieveLogAndBlockheight(): block found," +
|
|
177
|
-
" commit hash: " + (0, Utils_1.toHex)(commitHash) + " blockhash: " + blockData.blockhash + " current btc relay height: " + blockHeight);
|
|
178
|
-
return { header: storedBlockHeader, height: blockHeight };
|
|
179
|
-
}
|
|
180
|
-
/**
|
|
181
|
-
* Retrieves blockheader data by blockheader's commit hash,
|
|
182
|
-
*
|
|
183
|
-
* @param commitmentHashStr
|
|
184
|
-
* @param blockData
|
|
185
|
-
*/
|
|
186
|
-
async retrieveLogByCommitHash(commitmentHashStr, blockData) {
|
|
187
|
-
const result = await this.getBlock(commitmentHashStr, buffer_1.Buffer.from(blockData.blockhash, "hex"));
|
|
188
|
-
if (result == null)
|
|
189
|
-
return null;
|
|
190
|
-
const [storedBlockHeader, commitHash] = result;
|
|
191
|
-
//Check if block is part of the main chain
|
|
192
|
-
const chainCommitment = await this.contract.get_commit_hash(storedBlockHeader.block_height);
|
|
193
|
-
if (BigInt(chainCommitment) !== BigInt(commitHash))
|
|
194
|
-
return null;
|
|
195
|
-
logger.debug("retrieveLogByCommitHash(): block found," +
|
|
196
|
-
" commit hash: " + commitmentHashStr + " blockhash: " + blockData.blockhash + " height: " + storedBlockHeader.block_height);
|
|
197
|
-
return storedBlockHeader;
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* Retrieves latest known stored blockheader & blockheader from bitcoin RPC that is in the main chain
|
|
201
|
-
*/
|
|
202
|
-
async retrieveLatestKnownBlockLog() {
|
|
203
|
-
const data = await this.Events.findInContractEvents(["btc_relay::events::StoreHeader", "btc_relay::events::StoreForkHeader"], null, async (event) => {
|
|
204
|
-
const storedHeader = StarknetBtcStoredHeader_1.StarknetBtcStoredHeader.fromSerializedFeltArray(event.data);
|
|
205
|
-
const blockHashHex = storedHeader.getBlockHash().toString("hex");
|
|
206
|
-
const commitHash = event.params.commit_hash;
|
|
207
|
-
const [isInBtcMainChain, btcRelayCommitHash] = await Promise.all([
|
|
208
|
-
this.bitcoinRpc.isInMainChain(blockHashHex).catch(() => false),
|
|
209
|
-
this.contract.get_commit_hash(storedHeader.block_height)
|
|
210
|
-
]);
|
|
211
|
-
if (!isInBtcMainChain)
|
|
212
|
-
return null;
|
|
213
|
-
if (BigInt(commitHash) !== BigInt(btcRelayCommitHash))
|
|
214
|
-
return null;
|
|
215
|
-
return {
|
|
216
|
-
resultStoredHeader: storedHeader,
|
|
217
|
-
resultBitcoinHeader: await this.bitcoinRpc.getBlockHeader(blockHashHex),
|
|
218
|
-
commitHash: commitHash
|
|
219
|
-
};
|
|
220
|
-
});
|
|
221
|
-
if (data != null)
|
|
222
|
-
logger.debug("retrieveLatestKnownBlockLog(): block found," +
|
|
223
|
-
" commit hash: " + (0, Utils_1.toHex)(data.commitHash) + " blockhash: " + data.resultBitcoinHeader.getHash() +
|
|
224
|
-
" height: " + data.resultStoredHeader.getBlockheight());
|
|
225
|
-
return data;
|
|
226
|
-
}
|
|
227
|
-
/**
|
|
228
|
-
* Saves blockheaders as a bitcoin main chain to the btc relay
|
|
229
|
-
*
|
|
230
|
-
* @param signer
|
|
231
|
-
* @param mainHeaders
|
|
232
|
-
* @param storedHeader
|
|
233
|
-
* @param feeRate
|
|
234
|
-
*/
|
|
235
|
-
saveMainHeaders(signer, mainHeaders, storedHeader, feeRate) {
|
|
236
|
-
logger.debug("saveMainHeaders(): submitting main blockheaders, count: " + mainHeaders.length);
|
|
237
|
-
return this._saveHeaders(signer, mainHeaders, storedHeader, null, 0, feeRate);
|
|
238
|
-
}
|
|
239
|
-
/**
|
|
240
|
-
* Creates a new long fork and submits the headers to it
|
|
241
|
-
*
|
|
242
|
-
* @param signer
|
|
243
|
-
* @param forkHeaders
|
|
244
|
-
* @param storedHeader
|
|
245
|
-
* @param tipWork
|
|
246
|
-
* @param feeRate
|
|
247
|
-
*/
|
|
248
|
-
async saveNewForkHeaders(signer, forkHeaders, storedHeader, tipWork, feeRate) {
|
|
249
|
-
let forkId = Math.floor(Math.random() * 0xFFFFFFFFFFFF);
|
|
250
|
-
logger.debug("saveNewForkHeaders(): submitting new fork & blockheaders," +
|
|
251
|
-
" count: " + forkHeaders.length + " forkId: 0x" + forkId.toString(16));
|
|
252
|
-
return await this._saveHeaders(signer, forkHeaders, storedHeader, tipWork, forkId, feeRate);
|
|
253
|
-
}
|
|
254
|
-
/**
|
|
255
|
-
* Continues submitting blockheaders to a given fork
|
|
256
|
-
*
|
|
257
|
-
* @param signer
|
|
258
|
-
* @param forkHeaders
|
|
259
|
-
* @param storedHeader
|
|
260
|
-
* @param forkId
|
|
261
|
-
* @param tipWork
|
|
262
|
-
* @param feeRate
|
|
263
|
-
*/
|
|
264
|
-
saveForkHeaders(signer, forkHeaders, storedHeader, forkId, tipWork, feeRate) {
|
|
265
|
-
logger.debug("saveForkHeaders(): submitting blockheaders to existing fork," +
|
|
266
|
-
" count: " + forkHeaders.length + " forkId: 0x" + forkId.toString(16));
|
|
267
|
-
return this._saveHeaders(signer, forkHeaders, storedHeader, tipWork, forkId, feeRate);
|
|
268
|
-
}
|
|
269
|
-
/**
|
|
270
|
-
* Submits short fork with given blockheaders
|
|
271
|
-
*
|
|
272
|
-
* @param signer
|
|
273
|
-
* @param forkHeaders
|
|
274
|
-
* @param storedHeader
|
|
275
|
-
* @param tipWork
|
|
276
|
-
* @param feeRate
|
|
277
|
-
*/
|
|
278
|
-
saveShortForkHeaders(signer, forkHeaders, storedHeader, tipWork, feeRate) {
|
|
279
|
-
logger.debug("saveShortForkHeaders(): submitting short fork blockheaders," +
|
|
280
|
-
" count: " + forkHeaders.length);
|
|
281
|
-
return this._saveHeaders(signer, forkHeaders, storedHeader, tipWork, -1, feeRate);
|
|
282
|
-
}
|
|
283
|
-
/**
|
|
284
|
-
* Estimate required synchronization fee (worst case) to synchronize btc relay to the required blockheight
|
|
285
|
-
*
|
|
286
|
-
* @param requiredBlockheight
|
|
287
|
-
* @param feeRate
|
|
288
|
-
*/
|
|
289
|
-
async estimateSynchronizeFee(requiredBlockheight, feeRate) {
|
|
290
|
-
const tipData = await this.getTipData();
|
|
291
|
-
const currBlockheight = tipData.blockheight;
|
|
292
|
-
const blockheightDelta = requiredBlockheight - currBlockheight;
|
|
293
|
-
if (blockheightDelta <= 0)
|
|
294
|
-
return 0n;
|
|
295
|
-
const synchronizationFee = BigInt(blockheightDelta) * await this.getFeePerBlock(feeRate);
|
|
296
|
-
logger.debug("estimateSynchronizeFee(): required blockheight: " + requiredBlockheight +
|
|
297
|
-
" blockheight delta: " + blockheightDelta + " fee: " + synchronizationFee.toString(10));
|
|
298
|
-
return synchronizationFee;
|
|
299
|
-
}
|
|
300
|
-
/**
|
|
301
|
-
* Returns fee required (in SOL) to synchronize a single block to btc relay
|
|
302
|
-
*
|
|
303
|
-
* @param feeRate
|
|
304
|
-
*/
|
|
305
|
-
async getFeePerBlock(feeRate) {
|
|
306
|
-
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
307
|
-
return StarknetFees_1.StarknetFees.getGasFee(GAS_PER_BLOCKHEADER, feeRate);
|
|
308
|
-
}
|
|
309
|
-
/**
|
|
310
|
-
* Gets fee rate required for submitting blockheaders to the main chain
|
|
311
|
-
*/
|
|
312
|
-
getMainFeeRate(signer) {
|
|
313
|
-
return this.Chain.Fees.getFeeRate();
|
|
314
|
-
}
|
|
315
|
-
/**
|
|
316
|
-
* Gets fee rate required for submitting blockheaders to the specific fork
|
|
317
|
-
*/
|
|
318
|
-
getForkFeeRate(signer, forkId) {
|
|
319
|
-
return this.Chain.Fees.getFeeRate();
|
|
320
|
-
}
|
|
321
|
-
saveInitialHeader(signer, header, epochStart, pastBlocksTimestamps, feeRate) {
|
|
322
|
-
throw new Error("Not supported, starknet contract is initialized with constructor!");
|
|
323
|
-
}
|
|
324
|
-
/**
|
|
325
|
-
* Gets committed header, identified by blockhash & blockheight, determines required BTC relay blockheight based on
|
|
326
|
-
* requiredConfirmations
|
|
327
|
-
* If synchronizer is passed & blockhash is not found, it produces transactions to sync up the btc relay to the
|
|
328
|
-
* current chain tip & adds them to the txs array
|
|
329
|
-
*
|
|
330
|
-
* @param signer
|
|
331
|
-
* @param btcRelay
|
|
332
|
-
* @param btcTxs
|
|
333
|
-
* @param txs solana transaction array, in case we need to synchronize the btc relay ourselves the synchronization
|
|
334
|
-
* txns are added here
|
|
335
|
-
* @param synchronizer optional synchronizer to use to synchronize the btc relay in case it is not yet synchronized
|
|
336
|
-
* to the required blockheight
|
|
337
|
-
* @param feeRate Fee rate to use for synchronization transactions
|
|
338
|
-
* @private
|
|
339
|
-
*/
|
|
340
|
-
static async getCommitedHeadersAndSynchronize(signer, btcRelay, btcTxs, txs, synchronizer, feeRate) {
|
|
341
|
-
const leavesTxs = [];
|
|
342
|
-
const blockheaders = {};
|
|
343
|
-
for (let btcTx of btcTxs) {
|
|
344
|
-
const requiredBlockheight = btcTx.blockheight + btcTx.requiredConfirmations - 1;
|
|
345
|
-
const result = await (0, Utils_1.tryWithRetries)(() => btcRelay.retrieveLogAndBlockheight({
|
|
346
|
-
blockhash: btcTx.blockhash
|
|
347
|
-
}, requiredBlockheight));
|
|
348
|
-
if (result != null) {
|
|
349
|
-
blockheaders[result.header.getBlockHash().toString("hex")] = result.header;
|
|
350
|
-
}
|
|
351
|
-
else {
|
|
352
|
-
leavesTxs.push(btcTx);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
if (leavesTxs.length === 0)
|
|
356
|
-
return blockheaders;
|
|
357
|
-
//Need to synchronize
|
|
358
|
-
if (synchronizer == null)
|
|
359
|
-
return null;
|
|
360
|
-
//TODO: We don't have to synchronize to tip, only to our required blockheight
|
|
361
|
-
const resp = await synchronizer.syncToLatestTxs(signer.toString(), feeRate);
|
|
362
|
-
logger.debug("getCommitedHeaderAndSynchronize(): BTC Relay not synchronized to required blockheight, " +
|
|
363
|
-
"synchronizing ourselves in " + resp.txs.length + " txs");
|
|
364
|
-
logger.debug("getCommitedHeaderAndSynchronize(): BTC Relay computed header map: ", resp.computedHeaderMap);
|
|
365
|
-
txs.push(...resp.txs);
|
|
366
|
-
for (let key in resp.computedHeaderMap) {
|
|
367
|
-
const header = resp.computedHeaderMap[key];
|
|
368
|
-
blockheaders[header.getBlockHash().toString("hex")] = header;
|
|
369
|
-
}
|
|
370
|
-
//Check that blockhashes of all the rest txs are included
|
|
371
|
-
for (let btcTx of leavesTxs) {
|
|
372
|
-
if (blockheaders[btcTx.blockhash] == null)
|
|
373
|
-
return null;
|
|
374
|
-
}
|
|
375
|
-
//Retrieve computed headers
|
|
376
|
-
return blockheaders;
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
exports.StarknetBtcRelay = StarknetBtcRelay;
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StarknetBtcRelay = void 0;
|
|
4
|
+
const buffer_1 = require("buffer");
|
|
5
|
+
const StarknetBtcHeader_1 = require("./headers/StarknetBtcHeader");
|
|
6
|
+
const base_1 = require("@atomiqlabs/base");
|
|
7
|
+
const Utils_1 = require("../../utils/Utils");
|
|
8
|
+
const StarknetContractBase_1 = require("../contract/StarknetContractBase");
|
|
9
|
+
const StarknetBtcStoredHeader_1 = require("./headers/StarknetBtcStoredHeader");
|
|
10
|
+
const BtcRelayAbi_1 = require("./BtcRelayAbi");
|
|
11
|
+
const starknet_1 = require("starknet");
|
|
12
|
+
const StarknetFees_1 = require("../chain/modules/StarknetFees");
|
|
13
|
+
const StarknetAction_1 = require("../chain/StarknetAction");
|
|
14
|
+
function serializeBlockHeader(e) {
|
|
15
|
+
return new StarknetBtcHeader_1.StarknetBtcHeader({
|
|
16
|
+
reversed_version: (0, Utils_1.u32ReverseEndianness)(e.getVersion()),
|
|
17
|
+
previous_blockhash: (0, Utils_1.bufferToU32Array)(buffer_1.Buffer.from(e.getPrevBlockhash(), "hex").reverse()),
|
|
18
|
+
merkle_root: (0, Utils_1.bufferToU32Array)(buffer_1.Buffer.from(e.getMerkleRoot(), "hex").reverse()),
|
|
19
|
+
reversed_timestamp: (0, Utils_1.u32ReverseEndianness)(e.getTimestamp()),
|
|
20
|
+
nbits: (0, Utils_1.u32ReverseEndianness)(e.getNbits()),
|
|
21
|
+
nonce: (0, Utils_1.u32ReverseEndianness)(e.getNonce()),
|
|
22
|
+
hash: buffer_1.Buffer.from(e.getHash(), "hex").reverse()
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
const GAS_PER_BLOCKHEADER = { l1DataGas: 600, l2Gas: 40000000, l1Gas: 0 };
|
|
26
|
+
const GAS_PER_BLOCKHEADER_FORK = { l1DataGas: 1000, l2Gas: 60000000, l1Gas: 0 };
|
|
27
|
+
const btcRelayAddreses = {
|
|
28
|
+
[base_1.BitcoinNetwork.TESTNET4]: "0x0099b63f39f0cabb767361de3d8d3e97212351a51540e2687c2571f4da490dbe",
|
|
29
|
+
[base_1.BitcoinNetwork.TESTNET]: "0x068601c79da2231d21e015ccfd59c243861156fa523a12c9f987ec28eb8dbc8c",
|
|
30
|
+
[base_1.BitcoinNetwork.MAINNET]: "0x057b14a4231b82f1e525ff35a722d893ca3dd2bde0baa6cee97937c5be861dbc"
|
|
31
|
+
};
|
|
32
|
+
function serializeCalldata(headers, storedHeader, span) {
|
|
33
|
+
span.push((0, Utils_1.toHex)(headers.length));
|
|
34
|
+
headers.forEach(header => {
|
|
35
|
+
span.push(...header.serialize());
|
|
36
|
+
});
|
|
37
|
+
span.push(...storedHeader.serialize());
|
|
38
|
+
return span;
|
|
39
|
+
}
|
|
40
|
+
const logger = (0, Utils_1.getLogger)("StarknetBtcRelay: ");
|
|
41
|
+
class StarknetBtcRelay extends StarknetContractBase_1.StarknetContractBase {
|
|
42
|
+
SaveMainHeaders(signer, mainHeaders, storedHeader) {
|
|
43
|
+
return new StarknetAction_1.StarknetAction(signer, this.Chain, {
|
|
44
|
+
contractAddress: this.contract.address,
|
|
45
|
+
entrypoint: "submit_main_blockheaders",
|
|
46
|
+
calldata: serializeCalldata(mainHeaders, storedHeader, [])
|
|
47
|
+
}, (0, StarknetFees_1.starknetGasMul)(GAS_PER_BLOCKHEADER, mainHeaders.length));
|
|
48
|
+
}
|
|
49
|
+
SaveShortForkHeaders(signer, forkHeaders, storedHeader) {
|
|
50
|
+
return new StarknetAction_1.StarknetAction(signer, this.Chain, {
|
|
51
|
+
contractAddress: this.contract.address,
|
|
52
|
+
entrypoint: "submit_short_fork_blockheaders",
|
|
53
|
+
calldata: serializeCalldata(forkHeaders, storedHeader, [])
|
|
54
|
+
}, (0, StarknetFees_1.starknetGasMul)(GAS_PER_BLOCKHEADER, forkHeaders.length));
|
|
55
|
+
}
|
|
56
|
+
SaveLongForkHeaders(signer, forkId, forkHeaders, storedHeader, totalForkHeaders = 100) {
|
|
57
|
+
return new StarknetAction_1.StarknetAction(signer, this.Chain, {
|
|
58
|
+
contractAddress: this.contract.address,
|
|
59
|
+
entrypoint: "submit_fork_blockheaders",
|
|
60
|
+
calldata: serializeCalldata(forkHeaders, storedHeader, [(0, Utils_1.toHex)(forkId)])
|
|
61
|
+
}, (0, StarknetFees_1.starknetGasAdd)((0, StarknetFees_1.starknetGasMul)(GAS_PER_BLOCKHEADER, forkHeaders.length), (0, StarknetFees_1.starknetGasMul)(GAS_PER_BLOCKHEADER_FORK, totalForkHeaders)));
|
|
62
|
+
}
|
|
63
|
+
constructor(chainInterface, bitcoinRpc, bitcoinNetwork, contractAddress = btcRelayAddreses[bitcoinNetwork]) {
|
|
64
|
+
super(chainInterface, contractAddress, BtcRelayAbi_1.BtcRelayAbi);
|
|
65
|
+
this.maxHeadersPerTx = 100;
|
|
66
|
+
this.maxForkHeadersPerTx = 100;
|
|
67
|
+
this.maxShortForkHeadersPerTx = 100;
|
|
68
|
+
this.bitcoinRpc = bitcoinRpc;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Computes subsequent commited headers as they will appear on the blockchain when transactions
|
|
72
|
+
* are submitted & confirmed
|
|
73
|
+
*
|
|
74
|
+
* @param initialStoredHeader
|
|
75
|
+
* @param syncedHeaders
|
|
76
|
+
* @private
|
|
77
|
+
*/
|
|
78
|
+
computeCommitedHeaders(initialStoredHeader, syncedHeaders) {
|
|
79
|
+
const computedCommitedHeaders = [initialStoredHeader];
|
|
80
|
+
for (let blockHeader of syncedHeaders) {
|
|
81
|
+
computedCommitedHeaders.push(computedCommitedHeaders[computedCommitedHeaders.length - 1].computeNext(blockHeader));
|
|
82
|
+
}
|
|
83
|
+
return computedCommitedHeaders;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* A common logic for submitting blockheaders in a transaction
|
|
87
|
+
*
|
|
88
|
+
* @param signer
|
|
89
|
+
* @param headers headers to sync to the btc relay
|
|
90
|
+
* @param storedHeader current latest stored block header for a given fork
|
|
91
|
+
* @param tipWork work of the current tip in a given fork
|
|
92
|
+
* @param forkId forkId to submit to, forkId=0 means main chain, forkId=-1 means short fork
|
|
93
|
+
* @param feeRate feeRate for the transaction
|
|
94
|
+
* @private
|
|
95
|
+
*/
|
|
96
|
+
async _saveHeaders(signer, headers, storedHeader, tipWork, forkId, feeRate) {
|
|
97
|
+
const blockHeaderObj = headers.map(serializeBlockHeader);
|
|
98
|
+
let starknetAction;
|
|
99
|
+
switch (forkId) {
|
|
100
|
+
case -1:
|
|
101
|
+
starknetAction = this.SaveShortForkHeaders(signer, blockHeaderObj, storedHeader);
|
|
102
|
+
break;
|
|
103
|
+
case 0:
|
|
104
|
+
starknetAction = this.SaveMainHeaders(signer, blockHeaderObj, storedHeader);
|
|
105
|
+
break;
|
|
106
|
+
default:
|
|
107
|
+
starknetAction = this.SaveLongForkHeaders(signer, forkId, blockHeaderObj, storedHeader);
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
const tx = await starknetAction.tx(feeRate);
|
|
111
|
+
const computedCommitedHeaders = this.computeCommitedHeaders(storedHeader, blockHeaderObj);
|
|
112
|
+
const lastStoredHeader = computedCommitedHeaders[computedCommitedHeaders.length - 1];
|
|
113
|
+
if (forkId !== 0 && base_1.StatePredictorUtils.gtBuffer(lastStoredHeader.getBlockHash(), tipWork)) {
|
|
114
|
+
//Fork's work is higher than main chain's work, this fork will become a main chain
|
|
115
|
+
forkId = 0;
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
forkId: forkId,
|
|
119
|
+
lastStoredHeader,
|
|
120
|
+
tx,
|
|
121
|
+
computedCommitedHeaders
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
getBlock(commitHash, blockHash) {
|
|
125
|
+
const keys = [commitHash == null ? null : (0, Utils_1.toHex)(commitHash)];
|
|
126
|
+
if (blockHash != null) {
|
|
127
|
+
const starknetBlockHash = starknet_1.hash.computePoseidonHashOnElements((0, Utils_1.bufferToU32Array)(buffer_1.Buffer.from([...blockHash]).reverse()));
|
|
128
|
+
keys.push(starknetBlockHash);
|
|
129
|
+
}
|
|
130
|
+
return this.Events.findInContractEvents(["btc_relay::events::StoreHeader", "btc_relay::events::StoreForkHeader"], keys, (event) => {
|
|
131
|
+
return Promise.resolve([StarknetBtcStoredHeader_1.StarknetBtcStoredHeader.fromSerializedFeltArray(event.data), BigInt(event.params.commit_hash)]);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
async getBlockHeight() {
|
|
135
|
+
return Number(await this.contract.get_blockheight());
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Returns data about current main chain tip stored in the btc relay
|
|
139
|
+
*/
|
|
140
|
+
async getTipData() {
|
|
141
|
+
const commitHash = await this.contract.get_tip_commit_hash();
|
|
142
|
+
if (commitHash == null || BigInt(commitHash) === BigInt(0))
|
|
143
|
+
return null;
|
|
144
|
+
const result = await this.getBlock(commitHash);
|
|
145
|
+
if (result == null)
|
|
146
|
+
return null;
|
|
147
|
+
const [storedBlockHeader] = result;
|
|
148
|
+
return {
|
|
149
|
+
blockheight: storedBlockHeader.getBlockheight(),
|
|
150
|
+
commitHash: (0, Utils_1.bigNumberishToBuffer)(commitHash, 32).toString("hex"),
|
|
151
|
+
blockhash: storedBlockHeader.getBlockHash().toString("hex"),
|
|
152
|
+
chainWork: storedBlockHeader.getChainWork()
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Retrieves blockheader with a specific blockhash, returns null if requiredBlockheight is provided and
|
|
157
|
+
* btc relay contract is not synced up to the desired blockheight
|
|
158
|
+
*
|
|
159
|
+
* @param blockData
|
|
160
|
+
* @param requiredBlockheight
|
|
161
|
+
*/
|
|
162
|
+
async retrieveLogAndBlockheight(blockData, requiredBlockheight) {
|
|
163
|
+
//TODO: we can fetch the blockheight and events in parallel
|
|
164
|
+
const blockHeight = await this.getBlockHeight();
|
|
165
|
+
if (requiredBlockheight != null && blockHeight < requiredBlockheight) {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
const result = await this.getBlock(null, buffer_1.Buffer.from(blockData.blockhash, "hex"));
|
|
169
|
+
if (result == null)
|
|
170
|
+
return null;
|
|
171
|
+
const [storedBlockHeader, commitHash] = result;
|
|
172
|
+
//Check if block is part of the main chain
|
|
173
|
+
const chainCommitment = await this.contract.get_commit_hash(storedBlockHeader.block_height);
|
|
174
|
+
if (BigInt(chainCommitment) !== BigInt(commitHash))
|
|
175
|
+
return null;
|
|
176
|
+
logger.debug("retrieveLogAndBlockheight(): block found," +
|
|
177
|
+
" commit hash: " + (0, Utils_1.toHex)(commitHash) + " blockhash: " + blockData.blockhash + " current btc relay height: " + blockHeight);
|
|
178
|
+
return { header: storedBlockHeader, height: blockHeight };
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Retrieves blockheader data by blockheader's commit hash,
|
|
182
|
+
*
|
|
183
|
+
* @param commitmentHashStr
|
|
184
|
+
* @param blockData
|
|
185
|
+
*/
|
|
186
|
+
async retrieveLogByCommitHash(commitmentHashStr, blockData) {
|
|
187
|
+
const result = await this.getBlock(commitmentHashStr, buffer_1.Buffer.from(blockData.blockhash, "hex"));
|
|
188
|
+
if (result == null)
|
|
189
|
+
return null;
|
|
190
|
+
const [storedBlockHeader, commitHash] = result;
|
|
191
|
+
//Check if block is part of the main chain
|
|
192
|
+
const chainCommitment = await this.contract.get_commit_hash(storedBlockHeader.block_height);
|
|
193
|
+
if (BigInt(chainCommitment) !== BigInt(commitHash))
|
|
194
|
+
return null;
|
|
195
|
+
logger.debug("retrieveLogByCommitHash(): block found," +
|
|
196
|
+
" commit hash: " + commitmentHashStr + " blockhash: " + blockData.blockhash + " height: " + storedBlockHeader.block_height);
|
|
197
|
+
return storedBlockHeader;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Retrieves latest known stored blockheader & blockheader from bitcoin RPC that is in the main chain
|
|
201
|
+
*/
|
|
202
|
+
async retrieveLatestKnownBlockLog() {
|
|
203
|
+
const data = await this.Events.findInContractEvents(["btc_relay::events::StoreHeader", "btc_relay::events::StoreForkHeader"], null, async (event) => {
|
|
204
|
+
const storedHeader = StarknetBtcStoredHeader_1.StarknetBtcStoredHeader.fromSerializedFeltArray(event.data);
|
|
205
|
+
const blockHashHex = storedHeader.getBlockHash().toString("hex");
|
|
206
|
+
const commitHash = event.params.commit_hash;
|
|
207
|
+
const [isInBtcMainChain, btcRelayCommitHash] = await Promise.all([
|
|
208
|
+
this.bitcoinRpc.isInMainChain(blockHashHex).catch(() => false),
|
|
209
|
+
this.contract.get_commit_hash(storedHeader.block_height)
|
|
210
|
+
]);
|
|
211
|
+
if (!isInBtcMainChain)
|
|
212
|
+
return null;
|
|
213
|
+
if (BigInt(commitHash) !== BigInt(btcRelayCommitHash))
|
|
214
|
+
return null;
|
|
215
|
+
return {
|
|
216
|
+
resultStoredHeader: storedHeader,
|
|
217
|
+
resultBitcoinHeader: await this.bitcoinRpc.getBlockHeader(blockHashHex),
|
|
218
|
+
commitHash: commitHash
|
|
219
|
+
};
|
|
220
|
+
});
|
|
221
|
+
if (data != null)
|
|
222
|
+
logger.debug("retrieveLatestKnownBlockLog(): block found," +
|
|
223
|
+
" commit hash: " + (0, Utils_1.toHex)(data.commitHash) + " blockhash: " + data.resultBitcoinHeader.getHash() +
|
|
224
|
+
" height: " + data.resultStoredHeader.getBlockheight());
|
|
225
|
+
return data;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Saves blockheaders as a bitcoin main chain to the btc relay
|
|
229
|
+
*
|
|
230
|
+
* @param signer
|
|
231
|
+
* @param mainHeaders
|
|
232
|
+
* @param storedHeader
|
|
233
|
+
* @param feeRate
|
|
234
|
+
*/
|
|
235
|
+
saveMainHeaders(signer, mainHeaders, storedHeader, feeRate) {
|
|
236
|
+
logger.debug("saveMainHeaders(): submitting main blockheaders, count: " + mainHeaders.length);
|
|
237
|
+
return this._saveHeaders(signer, mainHeaders, storedHeader, null, 0, feeRate);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Creates a new long fork and submits the headers to it
|
|
241
|
+
*
|
|
242
|
+
* @param signer
|
|
243
|
+
* @param forkHeaders
|
|
244
|
+
* @param storedHeader
|
|
245
|
+
* @param tipWork
|
|
246
|
+
* @param feeRate
|
|
247
|
+
*/
|
|
248
|
+
async saveNewForkHeaders(signer, forkHeaders, storedHeader, tipWork, feeRate) {
|
|
249
|
+
let forkId = Math.floor(Math.random() * 0xFFFFFFFFFFFF);
|
|
250
|
+
logger.debug("saveNewForkHeaders(): submitting new fork & blockheaders," +
|
|
251
|
+
" count: " + forkHeaders.length + " forkId: 0x" + forkId.toString(16));
|
|
252
|
+
return await this._saveHeaders(signer, forkHeaders, storedHeader, tipWork, forkId, feeRate);
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Continues submitting blockheaders to a given fork
|
|
256
|
+
*
|
|
257
|
+
* @param signer
|
|
258
|
+
* @param forkHeaders
|
|
259
|
+
* @param storedHeader
|
|
260
|
+
* @param forkId
|
|
261
|
+
* @param tipWork
|
|
262
|
+
* @param feeRate
|
|
263
|
+
*/
|
|
264
|
+
saveForkHeaders(signer, forkHeaders, storedHeader, forkId, tipWork, feeRate) {
|
|
265
|
+
logger.debug("saveForkHeaders(): submitting blockheaders to existing fork," +
|
|
266
|
+
" count: " + forkHeaders.length + " forkId: 0x" + forkId.toString(16));
|
|
267
|
+
return this._saveHeaders(signer, forkHeaders, storedHeader, tipWork, forkId, feeRate);
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Submits short fork with given blockheaders
|
|
271
|
+
*
|
|
272
|
+
* @param signer
|
|
273
|
+
* @param forkHeaders
|
|
274
|
+
* @param storedHeader
|
|
275
|
+
* @param tipWork
|
|
276
|
+
* @param feeRate
|
|
277
|
+
*/
|
|
278
|
+
saveShortForkHeaders(signer, forkHeaders, storedHeader, tipWork, feeRate) {
|
|
279
|
+
logger.debug("saveShortForkHeaders(): submitting short fork blockheaders," +
|
|
280
|
+
" count: " + forkHeaders.length);
|
|
281
|
+
return this._saveHeaders(signer, forkHeaders, storedHeader, tipWork, -1, feeRate);
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Estimate required synchronization fee (worst case) to synchronize btc relay to the required blockheight
|
|
285
|
+
*
|
|
286
|
+
* @param requiredBlockheight
|
|
287
|
+
* @param feeRate
|
|
288
|
+
*/
|
|
289
|
+
async estimateSynchronizeFee(requiredBlockheight, feeRate) {
|
|
290
|
+
const tipData = await this.getTipData();
|
|
291
|
+
const currBlockheight = tipData.blockheight;
|
|
292
|
+
const blockheightDelta = requiredBlockheight - currBlockheight;
|
|
293
|
+
if (blockheightDelta <= 0)
|
|
294
|
+
return 0n;
|
|
295
|
+
const synchronizationFee = BigInt(blockheightDelta) * await this.getFeePerBlock(feeRate);
|
|
296
|
+
logger.debug("estimateSynchronizeFee(): required blockheight: " + requiredBlockheight +
|
|
297
|
+
" blockheight delta: " + blockheightDelta + " fee: " + synchronizationFee.toString(10));
|
|
298
|
+
return synchronizationFee;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Returns fee required (in SOL) to synchronize a single block to btc relay
|
|
302
|
+
*
|
|
303
|
+
* @param feeRate
|
|
304
|
+
*/
|
|
305
|
+
async getFeePerBlock(feeRate) {
|
|
306
|
+
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
307
|
+
return StarknetFees_1.StarknetFees.getGasFee(GAS_PER_BLOCKHEADER, feeRate);
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Gets fee rate required for submitting blockheaders to the main chain
|
|
311
|
+
*/
|
|
312
|
+
getMainFeeRate(signer) {
|
|
313
|
+
return this.Chain.Fees.getFeeRate();
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Gets fee rate required for submitting blockheaders to the specific fork
|
|
317
|
+
*/
|
|
318
|
+
getForkFeeRate(signer, forkId) {
|
|
319
|
+
return this.Chain.Fees.getFeeRate();
|
|
320
|
+
}
|
|
321
|
+
saveInitialHeader(signer, header, epochStart, pastBlocksTimestamps, feeRate) {
|
|
322
|
+
throw new Error("Not supported, starknet contract is initialized with constructor!");
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Gets committed header, identified by blockhash & blockheight, determines required BTC relay blockheight based on
|
|
326
|
+
* requiredConfirmations
|
|
327
|
+
* If synchronizer is passed & blockhash is not found, it produces transactions to sync up the btc relay to the
|
|
328
|
+
* current chain tip & adds them to the txs array
|
|
329
|
+
*
|
|
330
|
+
* @param signer
|
|
331
|
+
* @param btcRelay
|
|
332
|
+
* @param btcTxs
|
|
333
|
+
* @param txs solana transaction array, in case we need to synchronize the btc relay ourselves the synchronization
|
|
334
|
+
* txns are added here
|
|
335
|
+
* @param synchronizer optional synchronizer to use to synchronize the btc relay in case it is not yet synchronized
|
|
336
|
+
* to the required blockheight
|
|
337
|
+
* @param feeRate Fee rate to use for synchronization transactions
|
|
338
|
+
* @private
|
|
339
|
+
*/
|
|
340
|
+
static async getCommitedHeadersAndSynchronize(signer, btcRelay, btcTxs, txs, synchronizer, feeRate) {
|
|
341
|
+
const leavesTxs = [];
|
|
342
|
+
const blockheaders = {};
|
|
343
|
+
for (let btcTx of btcTxs) {
|
|
344
|
+
const requiredBlockheight = btcTx.blockheight + btcTx.requiredConfirmations - 1;
|
|
345
|
+
const result = await (0, Utils_1.tryWithRetries)(() => btcRelay.retrieveLogAndBlockheight({
|
|
346
|
+
blockhash: btcTx.blockhash
|
|
347
|
+
}, requiredBlockheight));
|
|
348
|
+
if (result != null) {
|
|
349
|
+
blockheaders[result.header.getBlockHash().toString("hex")] = result.header;
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
leavesTxs.push(btcTx);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
if (leavesTxs.length === 0)
|
|
356
|
+
return blockheaders;
|
|
357
|
+
//Need to synchronize
|
|
358
|
+
if (synchronizer == null)
|
|
359
|
+
return null;
|
|
360
|
+
//TODO: We don't have to synchronize to tip, only to our required blockheight
|
|
361
|
+
const resp = await synchronizer.syncToLatestTxs(signer.toString(), feeRate);
|
|
362
|
+
logger.debug("getCommitedHeaderAndSynchronize(): BTC Relay not synchronized to required blockheight, " +
|
|
363
|
+
"synchronizing ourselves in " + resp.txs.length + " txs");
|
|
364
|
+
logger.debug("getCommitedHeaderAndSynchronize(): BTC Relay computed header map: ", resp.computedHeaderMap);
|
|
365
|
+
txs.push(...resp.txs);
|
|
366
|
+
for (let key in resp.computedHeaderMap) {
|
|
367
|
+
const header = resp.computedHeaderMap[key];
|
|
368
|
+
blockheaders[header.getBlockHash().toString("hex")] = header;
|
|
369
|
+
}
|
|
370
|
+
//Check that blockhashes of all the rest txs are included
|
|
371
|
+
for (let btcTx of leavesTxs) {
|
|
372
|
+
if (blockheaders[btcTx.blockhash] == null)
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
//Retrieve computed headers
|
|
376
|
+
return blockheaders;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
exports.StarknetBtcRelay = StarknetBtcRelay;
|