@atomiqlabs/chain-evm 1.0.0-dev.75 → 1.0.0-dev.77
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/chains/botanix/BotanixChainType.d.ts +13 -13
- package/dist/chains/botanix/BotanixChainType.js +2 -2
- package/dist/chains/botanix/BotanixInitializer.d.ts +30 -30
- package/dist/chains/botanix/BotanixInitializer.js +122 -122
- package/dist/chains/citrea/CitreaBtcRelay.d.ts +21 -21
- package/dist/chains/citrea/CitreaBtcRelay.js +43 -43
- package/dist/chains/citrea/CitreaChainType.d.ts +13 -13
- package/dist/chains/citrea/CitreaChainType.js +2 -2
- package/dist/chains/citrea/CitreaFees.d.ts +29 -29
- package/dist/chains/citrea/CitreaFees.js +67 -67
- package/dist/chains/citrea/CitreaInitializer.d.ts +30 -30
- package/dist/chains/citrea/CitreaInitializer.js +129 -129
- package/dist/chains/citrea/CitreaSpvVaultContract.d.ts +15 -15
- package/dist/chains/citrea/CitreaSpvVaultContract.js +74 -74
- package/dist/chains/citrea/CitreaSwapContract.d.ts +22 -22
- package/dist/chains/citrea/CitreaSwapContract.js +96 -96
- package/dist/chains/citrea/CitreaTokens.d.ts +9 -9
- package/dist/chains/citrea/CitreaTokens.js +20 -20
- package/dist/evm/btcrelay/BtcRelayAbi.d.ts +198 -198
- package/dist/evm/btcrelay/BtcRelayAbi.js +261 -261
- package/dist/evm/btcrelay/BtcRelayTypechain.d.ts +172 -172
- package/dist/evm/btcrelay/BtcRelayTypechain.js +2 -2
- package/dist/evm/btcrelay/EVMBtcRelay.d.ts +197 -197
- package/dist/evm/btcrelay/EVMBtcRelay.js +435 -435
- package/dist/evm/btcrelay/headers/EVMBtcHeader.d.ts +33 -33
- package/dist/evm/btcrelay/headers/EVMBtcHeader.js +84 -84
- package/dist/evm/btcrelay/headers/EVMBtcStoredHeader.d.ts +56 -56
- package/dist/evm/btcrelay/headers/EVMBtcStoredHeader.js +123 -123
- package/dist/evm/chain/EVMChainInterface.d.ts +51 -51
- package/dist/evm/chain/EVMChainInterface.js +89 -89
- package/dist/evm/chain/EVMModule.d.ts +9 -9
- package/dist/evm/chain/EVMModule.js +13 -13
- package/dist/evm/chain/modules/ERC20Abi.d.ts +168 -168
- package/dist/evm/chain/modules/ERC20Abi.js +225 -225
- package/dist/evm/chain/modules/EVMAddresses.d.ts +10 -10
- package/dist/evm/chain/modules/EVMAddresses.js +30 -30
- package/dist/evm/chain/modules/EVMBlocks.d.ts +20 -20
- package/dist/evm/chain/modules/EVMBlocks.js +64 -64
- package/dist/evm/chain/modules/EVMEvents.d.ts +36 -36
- package/dist/evm/chain/modules/EVMEvents.js +122 -122
- package/dist/evm/chain/modules/EVMFees.d.ts +36 -36
- package/dist/evm/chain/modules/EVMFees.js +74 -74
- package/dist/evm/chain/modules/EVMSignatures.d.ts +29 -29
- package/dist/evm/chain/modules/EVMSignatures.js +68 -68
- package/dist/evm/chain/modules/EVMTokens.d.ts +70 -70
- package/dist/evm/chain/modules/EVMTokens.js +142 -142
- package/dist/evm/chain/modules/EVMTransactions.d.ts +94 -94
- package/dist/evm/chain/modules/EVMTransactions.js +286 -286
- package/dist/evm/contract/EVMContractBase.d.ts +22 -22
- package/dist/evm/contract/EVMContractBase.js +34 -34
- package/dist/evm/contract/EVMContractModule.d.ts +8 -8
- package/dist/evm/contract/EVMContractModule.js +11 -11
- package/dist/evm/contract/modules/EVMContractEvents.d.ts +42 -42
- package/dist/evm/contract/modules/EVMContractEvents.js +75 -75
- package/dist/evm/events/EVMChainEvents.d.ts +22 -22
- package/dist/evm/events/EVMChainEvents.js +69 -69
- package/dist/evm/events/EVMChainEventsBrowser.d.ts +102 -102
- package/dist/evm/events/EVMChainEventsBrowser.js +413 -404
- package/dist/evm/providers/JsonRpcProviderWithRetries.d.ts +15 -15
- package/dist/evm/providers/JsonRpcProviderWithRetries.js +19 -19
- package/dist/evm/providers/ReconnectingWebSocketProvider.d.ts +22 -22
- package/dist/evm/providers/ReconnectingWebSocketProvider.js +87 -87
- package/dist/evm/providers/SocketProvider.d.ts +111 -111
- package/dist/evm/providers/SocketProvider.js +334 -334
- package/dist/evm/providers/WebSocketProviderWithRetries.d.ts +17 -17
- package/dist/evm/providers/WebSocketProviderWithRetries.js +19 -19
- package/dist/evm/spv_swap/EVMSpvVaultContract.d.ts +79 -79
- package/dist/evm/spv_swap/EVMSpvVaultContract.js +482 -482
- package/dist/evm/spv_swap/EVMSpvVaultData.d.ts +39 -39
- package/dist/evm/spv_swap/EVMSpvVaultData.js +0 -180
- package/dist/evm/spv_swap/EVMSpvWithdrawalData.d.ts +19 -19
- package/dist/evm/spv_swap/EVMSpvWithdrawalData.js +55 -55
- package/dist/evm/spv_swap/SpvVaultContractAbi.d.ts +91 -91
- package/dist/evm/spv_swap/SpvVaultContractAbi.js +849 -849
- package/dist/evm/spv_swap/SpvVaultContractTypechain.d.ts +450 -450
- package/dist/evm/spv_swap/SpvVaultContractTypechain.js +2 -2
- package/dist/evm/swaps/EVMSwapContract.d.ts +193 -193
- package/dist/evm/swaps/EVMSwapContract.js +378 -378
- package/dist/evm/swaps/EVMSwapData.d.ts +66 -66
- package/dist/evm/swaps/EVMSwapData.js +260 -260
- package/dist/evm/swaps/EVMSwapModule.d.ts +9 -9
- package/dist/evm/swaps/EVMSwapModule.js +11 -11
- package/dist/evm/swaps/EscrowManagerAbi.d.ts +120 -120
- package/dist/evm/swaps/EscrowManagerAbi.js +985 -985
- package/dist/evm/swaps/EscrowManagerTypechain.d.ts +475 -475
- package/dist/evm/swaps/EscrowManagerTypechain.js +2 -2
- package/dist/evm/swaps/handlers/IHandler.d.ts +13 -13
- package/dist/evm/swaps/handlers/IHandler.js +2 -2
- package/dist/evm/swaps/handlers/claim/ClaimHandlers.d.ts +10 -10
- package/dist/evm/swaps/handlers/claim/ClaimHandlers.js +13 -13
- package/dist/evm/swaps/handlers/claim/HashlockClaimHandler.d.ts +20 -20
- package/dist/evm/swaps/handlers/claim/HashlockClaimHandler.js +39 -39
- package/dist/evm/swaps/handlers/claim/btc/BitcoinNoncedOutputClaimHandler.d.ts +24 -24
- package/dist/evm/swaps/handlers/claim/btc/BitcoinNoncedOutputClaimHandler.js +59 -59
- package/dist/evm/swaps/handlers/claim/btc/BitcoinOutputClaimHandler.d.ts +25 -25
- package/dist/evm/swaps/handlers/claim/btc/BitcoinOutputClaimHandler.js +51 -51
- package/dist/evm/swaps/handlers/claim/btc/BitcoinTxIdClaimHandler.d.ts +21 -21
- package/dist/evm/swaps/handlers/claim/btc/BitcoinTxIdClaimHandler.js +28 -28
- package/dist/evm/swaps/handlers/claim/btc/IBitcoinClaimHandler.d.ts +48 -48
- package/dist/evm/swaps/handlers/claim/btc/IBitcoinClaimHandler.js +63 -63
- package/dist/evm/swaps/handlers/refund/TimelockRefundHandler.d.ts +17 -17
- package/dist/evm/swaps/handlers/refund/TimelockRefundHandler.js +28 -28
- package/dist/evm/swaps/modules/EVMLpVault.d.ts +69 -69
- package/dist/evm/swaps/modules/EVMLpVault.js +134 -134
- package/dist/evm/swaps/modules/EVMSwapClaim.d.ts +54 -54
- package/dist/evm/swaps/modules/EVMSwapClaim.js +137 -137
- package/dist/evm/swaps/modules/EVMSwapInit.d.ts +88 -88
- package/dist/evm/swaps/modules/EVMSwapInit.js +274 -274
- package/dist/evm/swaps/modules/EVMSwapRefund.d.ts +62 -62
- package/dist/evm/swaps/modules/EVMSwapRefund.js +167 -167
- package/dist/evm/typechain/common.d.ts +50 -50
- package/dist/evm/typechain/common.js +2 -2
- package/dist/evm/wallet/EVMBrowserSigner.d.ts +5 -5
- package/dist/evm/wallet/EVMBrowserSigner.js +11 -11
- package/dist/evm/wallet/EVMPersistentSigner.d.ts +29 -29
- package/dist/evm/wallet/EVMPersistentSigner.js +222 -222
- package/dist/evm/wallet/EVMSigner.d.ts +11 -11
- package/dist/evm/wallet/EVMSigner.js +24 -24
- package/dist/index.d.ts +44 -44
- package/dist/index.js +60 -60
- package/dist/utils/Utils.d.ts +17 -17
- package/dist/utils/Utils.js +81 -81
- package/package.json +39 -39
- package/src/chains/botanix/BotanixChainType.ts +28 -28
- package/src/chains/botanix/BotanixInitializer.ts +171 -171
- package/src/chains/citrea/CitreaBtcRelay.ts +57 -57
- package/src/chains/citrea/CitreaChainType.ts +28 -28
- package/src/chains/citrea/CitreaFees.ts +77 -77
- package/src/chains/citrea/CitreaInitializer.ts +178 -178
- package/src/chains/citrea/CitreaSpvVaultContract.ts +75 -75
- package/src/chains/citrea/CitreaSwapContract.ts +102 -102
- package/src/chains/citrea/CitreaTokens.ts +21 -21
- package/src/evm/btcrelay/BtcRelayAbi.ts +258 -258
- package/src/evm/btcrelay/BtcRelayTypechain.ts +371 -371
- package/src/evm/btcrelay/EVMBtcRelay.ts +537 -537
- package/src/evm/btcrelay/headers/EVMBtcHeader.ts +109 -109
- package/src/evm/btcrelay/headers/EVMBtcStoredHeader.ts +152 -152
- package/src/evm/chain/EVMChainInterface.ts +155 -155
- package/src/evm/chain/EVMModule.ts +21 -21
- package/src/evm/chain/modules/ERC20Abi.ts +222 -222
- package/src/evm/chain/modules/EVMAddresses.ts +28 -28
- package/src/evm/chain/modules/EVMBlocks.ts +75 -75
- package/src/evm/chain/modules/EVMEvents.ts +139 -139
- package/src/evm/chain/modules/EVMFees.ts +104 -104
- package/src/evm/chain/modules/EVMSignatures.ts +76 -76
- package/src/evm/chain/modules/EVMTokens.ts +155 -155
- package/src/evm/chain/modules/EVMTransactions.ts +325 -325
- package/src/evm/contract/EVMContractBase.ts +63 -63
- package/src/evm/contract/EVMContractModule.ts +16 -16
- package/src/evm/contract/modules/EVMContractEvents.ts +102 -102
- package/src/evm/events/EVMChainEvents.ts +82 -82
- package/src/evm/events/EVMChainEventsBrowser.ts +534 -525
- package/src/evm/providers/JsonRpcProviderWithRetries.ts +24 -24
- package/src/evm/providers/ReconnectingWebSocketProvider.ts +101 -101
- package/src/evm/providers/SocketProvider.ts +368 -368
- package/src/evm/providers/WebSocketProviderWithRetries.ts +27 -27
- package/src/evm/spv_swap/EVMSpvVaultContract.ts +615 -615
- package/src/evm/spv_swap/EVMSpvVaultData.ts +224 -224
- package/src/evm/spv_swap/EVMSpvWithdrawalData.ts +70 -70
- package/src/evm/spv_swap/SpvVaultContractAbi.ts +846 -846
- package/src/evm/spv_swap/SpvVaultContractTypechain.ts +685 -685
- package/src/evm/swaps/EVMSwapContract.ts +600 -600
- package/src/evm/swaps/EVMSwapData.ts +378 -378
- package/src/evm/swaps/EVMSwapModule.ts +16 -16
- package/src/evm/swaps/EscrowManagerAbi.ts +982 -982
- package/src/evm/swaps/EscrowManagerTypechain.ts +723 -723
- package/src/evm/swaps/handlers/IHandler.ts +17 -17
- package/src/evm/swaps/handlers/claim/ClaimHandlers.ts +20 -20
- package/src/evm/swaps/handlers/claim/HashlockClaimHandler.ts +46 -46
- package/src/evm/swaps/handlers/claim/btc/BitcoinNoncedOutputClaimHandler.ts +82 -82
- package/src/evm/swaps/handlers/claim/btc/BitcoinOutputClaimHandler.ts +76 -76
- package/src/evm/swaps/handlers/claim/btc/BitcoinTxIdClaimHandler.ts +46 -46
- package/src/evm/swaps/handlers/claim/btc/IBitcoinClaimHandler.ts +115 -115
- package/src/evm/swaps/handlers/refund/TimelockRefundHandler.ts +37 -37
- package/src/evm/swaps/modules/EVMLpVault.ts +154 -154
- package/src/evm/swaps/modules/EVMSwapClaim.ts +172 -172
- package/src/evm/swaps/modules/EVMSwapInit.ts +328 -328
- package/src/evm/swaps/modules/EVMSwapRefund.ts +229 -229
- package/src/evm/typechain/common.ts +131 -131
- package/src/evm/wallet/EVMBrowserSigner.ts +11 -11
- package/src/evm/wallet/EVMPersistentSigner.ts +298 -298
- package/src/evm/wallet/EVMSigner.ts +31 -31
- package/src/index.ts +53 -53
- package/src/utils/Utils.ts +92 -92
|
@@ -1,435 +1,435 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.EVMBtcRelay = void 0;
|
|
4
|
-
const base_1 = require("@atomiqlabs/base");
|
|
5
|
-
const EVMBtcHeader_1 = require("./headers/EVMBtcHeader");
|
|
6
|
-
const Utils_1 = require("../../utils/Utils");
|
|
7
|
-
const EVMContractBase_1 = require("../contract/EVMContractBase");
|
|
8
|
-
const EVMBtcStoredHeader_1 = require("./headers/EVMBtcStoredHeader");
|
|
9
|
-
const EVMFees_1 = require("../chain/modules/EVMFees");
|
|
10
|
-
const BtcRelayAbi_1 = require("./BtcRelayAbi");
|
|
11
|
-
const ethers_1 = require("ethers");
|
|
12
|
-
const promise_cache_ts_1 = require("promise-cache-ts");
|
|
13
|
-
function serializeBlockHeader(e) {
|
|
14
|
-
return new EVMBtcHeader_1.EVMBtcHeader({
|
|
15
|
-
version: e.getVersion(),
|
|
16
|
-
previousBlockhash: Buffer.from(e.getPrevBlockhash(), "hex").reverse(),
|
|
17
|
-
merkleRoot: Buffer.from(e.getMerkleRoot(), "hex").reverse(),
|
|
18
|
-
timestamp: e.getTimestamp(),
|
|
19
|
-
nbits: e.getNbits(),
|
|
20
|
-
nonce: e.getNonce(),
|
|
21
|
-
hash: Buffer.from(e.getHash(), "hex").reverse()
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
const logger = (0, Utils_1.getLogger)("EVMBtcRelay: ");
|
|
25
|
-
class EVMBtcRelay extends EVMContractBase_1.EVMContractBase {
|
|
26
|
-
async SaveMainHeaders(signer, mainHeaders, storedHeader, feeRate) {
|
|
27
|
-
const tx = await this.contract.submitMainBlockheaders.populateTransaction(Buffer.concat([
|
|
28
|
-
storedHeader.serialize(),
|
|
29
|
-
Buffer.concat(mainHeaders.map(header => header.serializeCompact()))
|
|
30
|
-
]));
|
|
31
|
-
tx.from = signer;
|
|
32
|
-
EVMFees_1.EVMFees.applyFeeRate(tx, EVMBtcRelay.GasCosts.GAS_BASE_MAIN + (EVMBtcRelay.GasCosts.GAS_PER_BLOCKHEADER * mainHeaders.length), feeRate);
|
|
33
|
-
return tx;
|
|
34
|
-
}
|
|
35
|
-
async SaveShortForkHeaders(signer, forkHeaders, storedHeader, feeRate) {
|
|
36
|
-
const tx = await this.contract.submitShortForkBlockheaders.populateTransaction(Buffer.concat([
|
|
37
|
-
storedHeader.serialize(),
|
|
38
|
-
Buffer.concat(forkHeaders.map(header => header.serializeCompact()))
|
|
39
|
-
]));
|
|
40
|
-
tx.from = signer;
|
|
41
|
-
EVMFees_1.EVMFees.applyFeeRate(tx, EVMBtcRelay.GasCosts.GAS_BASE_MAIN + (EVMBtcRelay.GasCosts.GAS_PER_BLOCKHEADER * forkHeaders.length), feeRate);
|
|
42
|
-
return tx;
|
|
43
|
-
}
|
|
44
|
-
async SaveLongForkHeaders(signer, forkId, forkHeaders, storedHeader, feeRate, totalForkHeaders = 100) {
|
|
45
|
-
const tx = await this.contract.submitForkBlockheaders.populateTransaction(forkId, Buffer.concat([
|
|
46
|
-
storedHeader.serialize(),
|
|
47
|
-
Buffer.concat(forkHeaders.map(header => header.serializeCompact()))
|
|
48
|
-
]));
|
|
49
|
-
tx.from = signer;
|
|
50
|
-
EVMFees_1.EVMFees.applyFeeRate(tx, EVMBtcRelay.GasCosts.GAS_BASE_FORK + (EVMBtcRelay.GasCosts.GAS_PER_BLOCKHEADER_FORK * forkHeaders.length) + (EVMBtcRelay.GasCosts.GAS_PER_BLOCKHEADER_FORKED * totalForkHeaders), feeRate);
|
|
51
|
-
return tx;
|
|
52
|
-
}
|
|
53
|
-
constructor(chainInterface, bitcoinRpc, bitcoinNetwork, contractAddress, contractDeploymentHeight) {
|
|
54
|
-
super(chainInterface, contractAddress, BtcRelayAbi_1.BtcRelayAbi, contractDeploymentHeight);
|
|
55
|
-
this.maxHeadersPerTx = 100;
|
|
56
|
-
this.maxForkHeadersPerTx = 50;
|
|
57
|
-
this.maxShortForkHeadersPerTx = 100;
|
|
58
|
-
this.commitHashCache = new promise_cache_ts_1.PromiseLruCache(1000);
|
|
59
|
-
this.blockHashCache = new promise_cache_ts_1.PromiseLruCache(1000);
|
|
60
|
-
this.bitcoinRpc = bitcoinRpc;
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Computes subsequent commited headers as they will appear on the blockchain when transactions
|
|
64
|
-
* are submitted & confirmed
|
|
65
|
-
*
|
|
66
|
-
* @param initialStoredHeader
|
|
67
|
-
* @param syncedHeaders
|
|
68
|
-
* @private
|
|
69
|
-
*/
|
|
70
|
-
computeCommitedHeaders(initialStoredHeader, syncedHeaders) {
|
|
71
|
-
const computedCommitedHeaders = [initialStoredHeader];
|
|
72
|
-
for (let blockHeader of syncedHeaders) {
|
|
73
|
-
computedCommitedHeaders.push(computedCommitedHeaders[computedCommitedHeaders.length - 1].computeNext(blockHeader));
|
|
74
|
-
}
|
|
75
|
-
return computedCommitedHeaders;
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* A common logic for submitting blockheaders in a transaction
|
|
79
|
-
*
|
|
80
|
-
* @param signer
|
|
81
|
-
* @param headers headers to sync to the btc relay
|
|
82
|
-
* @param storedHeader current latest stored block header for a given fork
|
|
83
|
-
* @param tipWork work of the current tip in a given fork
|
|
84
|
-
* @param forkId forkId to submit to, forkId=0 means main chain, forkId=-1 means short fork
|
|
85
|
-
* @param feeRate feeRate for the transaction
|
|
86
|
-
* @param totalForkHeaders Total number of headers in a fork
|
|
87
|
-
* @private
|
|
88
|
-
*/
|
|
89
|
-
async _saveHeaders(signer, headers, storedHeader, tipWork, forkId, feeRate, totalForkHeaders) {
|
|
90
|
-
const blockHeaderObj = headers.map(serializeBlockHeader);
|
|
91
|
-
let tx;
|
|
92
|
-
switch (forkId) {
|
|
93
|
-
case -1:
|
|
94
|
-
tx = await this.SaveShortForkHeaders(signer, blockHeaderObj, storedHeader, feeRate);
|
|
95
|
-
break;
|
|
96
|
-
case 0:
|
|
97
|
-
tx = await this.SaveMainHeaders(signer, blockHeaderObj, storedHeader, feeRate);
|
|
98
|
-
break;
|
|
99
|
-
default:
|
|
100
|
-
tx = await this.SaveLongForkHeaders(signer, forkId, blockHeaderObj, storedHeader, feeRate, totalForkHeaders);
|
|
101
|
-
break;
|
|
102
|
-
}
|
|
103
|
-
const computedCommitedHeaders = this.computeCommitedHeaders(storedHeader, blockHeaderObj);
|
|
104
|
-
const lastStoredHeader = computedCommitedHeaders[computedCommitedHeaders.length - 1];
|
|
105
|
-
if (forkId !== 0 && base_1.StatePredictorUtils.gtBuffer(lastStoredHeader.getBlockHash(), tipWork)) {
|
|
106
|
-
//Fork's work is higher than main chain's work, this fork will become a main chain
|
|
107
|
-
forkId = 0;
|
|
108
|
-
}
|
|
109
|
-
return {
|
|
110
|
-
forkId: forkId,
|
|
111
|
-
lastStoredHeader,
|
|
112
|
-
tx,
|
|
113
|
-
computedCommitedHeaders
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
async findStoredBlockheaderInTraces(txTrace, commitHash) {
|
|
117
|
-
if (txTrace.to.toLowerCase() === (await this.contract.getAddress()).toLowerCase()) {
|
|
118
|
-
let dataBuffer;
|
|
119
|
-
if (txTrace.type === "CREATE") {
|
|
120
|
-
dataBuffer = Buffer.from(txTrace.input.substring(txTrace.input.length - 384, txTrace.input.length - 64), "hex");
|
|
121
|
-
}
|
|
122
|
-
else {
|
|
123
|
-
const result = this.parseCalldata(txTrace.input);
|
|
124
|
-
if (result != null) {
|
|
125
|
-
if (result.name === "submitMainBlockheaders" || result.name === "submitShortForkBlockheaders") {
|
|
126
|
-
const functionCall = result;
|
|
127
|
-
dataBuffer = Buffer.from((0, ethers_1.hexlify)(functionCall.args[0]).substring(2), "hex");
|
|
128
|
-
}
|
|
129
|
-
else if (result.name === "submitForkBlockheaders") {
|
|
130
|
-
const functionCall = result;
|
|
131
|
-
dataBuffer = Buffer.from((0, ethers_1.hexlify)(functionCall.args[1]).substring(2), "hex");
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
if (dataBuffer != null) {
|
|
136
|
-
let storedHeader = EVMBtcStoredHeader_1.EVMBtcStoredHeader.deserialize(dataBuffer.subarray(0, 160));
|
|
137
|
-
if (storedHeader.getCommitHash() === commitHash)
|
|
138
|
-
return storedHeader;
|
|
139
|
-
for (let i = 160; i < dataBuffer.length; i += 48) {
|
|
140
|
-
const blockHeader = EVMBtcHeader_1.EVMBtcHeader.deserialize(dataBuffer.subarray(i, i + 48));
|
|
141
|
-
storedHeader = storedHeader.computeNext(blockHeader);
|
|
142
|
-
if (storedHeader.getCommitHash() === commitHash)
|
|
143
|
-
return storedHeader;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
if (txTrace.calls != null) {
|
|
148
|
-
for (let call of txTrace.calls) {
|
|
149
|
-
const result = await this.findStoredBlockheaderInTraces(call, commitHash);
|
|
150
|
-
if (result != null)
|
|
151
|
-
return result;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
return null;
|
|
155
|
-
}
|
|
156
|
-
getBlock(commitHash, blockHash) {
|
|
157
|
-
const blockHashString = blockHash == null ? null : "0x" + Buffer.from([...blockHash]).reverse().toString("hex");
|
|
158
|
-
const generator = () => this.Events.findInContractEvents(["StoreHeader", "StoreForkHeader"], [
|
|
159
|
-
commitHash,
|
|
160
|
-
blockHashString
|
|
161
|
-
], async (event) => {
|
|
162
|
-
const txTrace = await this.Chain.Transactions.traceTransaction(event.transactionHash);
|
|
163
|
-
const storedBlockheader = await this.findStoredBlockheaderInTraces(txTrace, event.args.commitHash);
|
|
164
|
-
if (storedBlockheader == null)
|
|
165
|
-
return null;
|
|
166
|
-
this.commitHashCache.set(event.args.commitHash, Promise.resolve([storedBlockheader, event.args.commitHash]));
|
|
167
|
-
this.blockHashCache.set(event.args.blockHash, Promise.resolve([storedBlockheader, event.args.commitHash]));
|
|
168
|
-
return [storedBlockheader, event.args.commitHash];
|
|
169
|
-
});
|
|
170
|
-
if (commitHash != null)
|
|
171
|
-
return this.commitHashCache.getOrComputeAsync(commitHash, generator);
|
|
172
|
-
if (blockHashString != null)
|
|
173
|
-
return this.blockHashCache.getOrComputeAsync(blockHashString, generator);
|
|
174
|
-
return null;
|
|
175
|
-
}
|
|
176
|
-
async getBlockHeight() {
|
|
177
|
-
return Number(await this.contract.getBlockheight());
|
|
178
|
-
}
|
|
179
|
-
/**
|
|
180
|
-
* Returns data about current main chain tip stored in the btc relay
|
|
181
|
-
*/
|
|
182
|
-
async getTipData() {
|
|
183
|
-
const commitHash = await this.contract.getTipCommitHash();
|
|
184
|
-
if (commitHash == null || BigInt(commitHash) === BigInt(0))
|
|
185
|
-
return null;
|
|
186
|
-
const result = await this.getBlock(commitHash);
|
|
187
|
-
if (result == null)
|
|
188
|
-
return null;
|
|
189
|
-
const storedBlockHeader = result[0];
|
|
190
|
-
return {
|
|
191
|
-
blockheight: storedBlockHeader.getBlockheight(),
|
|
192
|
-
commitHash: commitHash,
|
|
193
|
-
blockhash: storedBlockHeader.getBlockHash().toString("hex"),
|
|
194
|
-
chainWork: storedBlockHeader.getChainWork()
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* Retrieves blockheader with a specific blockhash, returns null if requiredBlockheight is provided and
|
|
199
|
-
* btc relay contract is not synced up to the desired blockheight
|
|
200
|
-
*
|
|
201
|
-
* @param blockData
|
|
202
|
-
* @param requiredBlockheight
|
|
203
|
-
*/
|
|
204
|
-
async retrieveLogAndBlockheight(blockData, requiredBlockheight) {
|
|
205
|
-
//TODO: we can fetch the blockheight and events in parallel
|
|
206
|
-
const blockHeight = await this.getBlockHeight();
|
|
207
|
-
if (requiredBlockheight != null && blockHeight < requiredBlockheight) {
|
|
208
|
-
return null;
|
|
209
|
-
}
|
|
210
|
-
const result = await this.getBlock(null, Buffer.from(blockData.blockhash, "hex"));
|
|
211
|
-
if (result == null)
|
|
212
|
-
return null;
|
|
213
|
-
const [storedBlockHeader, commitHash] = result;
|
|
214
|
-
//Check if block is part of the main chain
|
|
215
|
-
const chainCommitment = await this.contract.getCommitHash(storedBlockHeader.blockHeight);
|
|
216
|
-
if (chainCommitment !== commitHash)
|
|
217
|
-
return null;
|
|
218
|
-
logger.debug("retrieveLogAndBlockheight(): block found," +
|
|
219
|
-
" commit hash: " + commitHash + " blockhash: " + blockData.blockhash + " current btc relay height: " + blockHeight);
|
|
220
|
-
return { header: storedBlockHeader, height: blockHeight };
|
|
221
|
-
}
|
|
222
|
-
/**
|
|
223
|
-
* Retrieves blockheader data by blockheader's commit hash,
|
|
224
|
-
*
|
|
225
|
-
* @param commitmentHashStr
|
|
226
|
-
* @param blockData
|
|
227
|
-
*/
|
|
228
|
-
async retrieveLogByCommitHash(commitmentHashStr, blockData) {
|
|
229
|
-
const result = await this.getBlock(commitmentHashStr, Buffer.from(blockData.blockhash, "hex"));
|
|
230
|
-
if (result == null)
|
|
231
|
-
return null;
|
|
232
|
-
const [storedBlockHeader, commitHash] = result;
|
|
233
|
-
//Check if block is part of the main chain
|
|
234
|
-
const chainCommitment = await this.contract.getCommitHash(storedBlockHeader.blockHeight);
|
|
235
|
-
if (chainCommitment !== commitHash)
|
|
236
|
-
return null;
|
|
237
|
-
logger.debug("retrieveLogByCommitHash(): block found," +
|
|
238
|
-
" commit hash: " + commitmentHashStr + " blockhash: " + blockData.blockhash + " height: " + storedBlockHeader.blockHeight);
|
|
239
|
-
return storedBlockHeader;
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* Retrieves latest known stored blockheader & blockheader from bitcoin RPC that is in the main chain
|
|
243
|
-
*/
|
|
244
|
-
async retrieveLatestKnownBlockLog() {
|
|
245
|
-
const data = await this.Events.findInContractEvents(["StoreHeader", "StoreForkHeader"], null, async (event) => {
|
|
246
|
-
const blockHashHex = Buffer.from(event.args.blockHash.substring(2), "hex").reverse().toString("hex");
|
|
247
|
-
const commitHash = event.args.commitHash;
|
|
248
|
-
const isInBtcMainChain = await this.bitcoinRpc.isInMainChain(blockHashHex).catch(() => false);
|
|
249
|
-
if (!isInBtcMainChain)
|
|
250
|
-
return null;
|
|
251
|
-
const blockHeader = await this.bitcoinRpc.getBlockHeader(blockHashHex);
|
|
252
|
-
if (commitHash !== await this.contract.getCommitHash(blockHeader.getHeight()))
|
|
253
|
-
return null;
|
|
254
|
-
const txTrace = await this.Chain.Transactions.traceTransaction(event.transactionHash);
|
|
255
|
-
const storedHeader = await this.findStoredBlockheaderInTraces(txTrace, commitHash);
|
|
256
|
-
if (storedHeader == null)
|
|
257
|
-
return null;
|
|
258
|
-
return {
|
|
259
|
-
resultStoredHeader: storedHeader,
|
|
260
|
-
resultBitcoinHeader: blockHeader,
|
|
261
|
-
commitHash: commitHash
|
|
262
|
-
};
|
|
263
|
-
});
|
|
264
|
-
if (data != null)
|
|
265
|
-
logger.debug("retrieveLatestKnownBlockLog(): block found," +
|
|
266
|
-
" commit hash: " + data.commitHash + " blockhash: " + data.resultBitcoinHeader.getHash() +
|
|
267
|
-
" height: " + data.resultStoredHeader.getBlockheight());
|
|
268
|
-
return data;
|
|
269
|
-
}
|
|
270
|
-
/**
|
|
271
|
-
* Saves blockheaders as a bitcoin main chain to the btc relay
|
|
272
|
-
*
|
|
273
|
-
* @param signer
|
|
274
|
-
* @param mainHeaders
|
|
275
|
-
* @param storedHeader
|
|
276
|
-
* @param feeRate
|
|
277
|
-
*/
|
|
278
|
-
async saveMainHeaders(signer, mainHeaders, storedHeader, feeRate) {
|
|
279
|
-
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
280
|
-
logger.debug("saveMainHeaders(): submitting main blockheaders, count: " + mainHeaders.length);
|
|
281
|
-
return this._saveHeaders(signer, mainHeaders, storedHeader, null, 0, feeRate, 0);
|
|
282
|
-
}
|
|
283
|
-
/**
|
|
284
|
-
* Creates a new long fork and submits the headers to it
|
|
285
|
-
*
|
|
286
|
-
* @param signer
|
|
287
|
-
* @param forkHeaders
|
|
288
|
-
* @param storedHeader
|
|
289
|
-
* @param tipWork
|
|
290
|
-
* @param feeRate
|
|
291
|
-
*/
|
|
292
|
-
async saveNewForkHeaders(signer, forkHeaders, storedHeader, tipWork, feeRate) {
|
|
293
|
-
let forkId = Math.floor(Math.random() * 0xFFFFFFFFFFFF);
|
|
294
|
-
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
295
|
-
logger.debug("saveNewForkHeaders(): submitting new fork & blockheaders," +
|
|
296
|
-
" count: " + forkHeaders.length + " forkId: 0x" + forkId.toString(16));
|
|
297
|
-
return await this._saveHeaders(signer, forkHeaders, storedHeader, tipWork, forkId, feeRate, forkHeaders.length);
|
|
298
|
-
}
|
|
299
|
-
/**
|
|
300
|
-
* Continues submitting blockheaders to a given fork
|
|
301
|
-
*
|
|
302
|
-
* @param signer
|
|
303
|
-
* @param forkHeaders
|
|
304
|
-
* @param storedHeader
|
|
305
|
-
* @param forkId
|
|
306
|
-
* @param tipWork
|
|
307
|
-
* @param feeRate
|
|
308
|
-
*/
|
|
309
|
-
async saveForkHeaders(signer, forkHeaders, storedHeader, forkId, tipWork, feeRate) {
|
|
310
|
-
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
311
|
-
logger.debug("saveForkHeaders(): submitting blockheaders to existing fork," +
|
|
312
|
-
" count: " + forkHeaders.length + " forkId: 0x" + forkId.toString(16));
|
|
313
|
-
return this._saveHeaders(signer, forkHeaders, storedHeader, tipWork, forkId, feeRate, 100);
|
|
314
|
-
}
|
|
315
|
-
/**
|
|
316
|
-
* Submits short fork with given blockheaders
|
|
317
|
-
*
|
|
318
|
-
* @param signer
|
|
319
|
-
* @param forkHeaders
|
|
320
|
-
* @param storedHeader
|
|
321
|
-
* @param tipWork
|
|
322
|
-
* @param feeRate
|
|
323
|
-
*/
|
|
324
|
-
async saveShortForkHeaders(signer, forkHeaders, storedHeader, tipWork, feeRate) {
|
|
325
|
-
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
326
|
-
logger.debug("saveShortForkHeaders(): submitting short fork blockheaders," +
|
|
327
|
-
" count: " + forkHeaders.length);
|
|
328
|
-
return this._saveHeaders(signer, forkHeaders, storedHeader, tipWork, -1, feeRate, 0);
|
|
329
|
-
}
|
|
330
|
-
/**
|
|
331
|
-
* Estimate required synchronization fee (worst case) to synchronize btc relay to the required blockheight
|
|
332
|
-
*
|
|
333
|
-
* @param requiredBlockheight
|
|
334
|
-
* @param feeRate
|
|
335
|
-
*/
|
|
336
|
-
async estimateSynchronizeFee(requiredBlockheight, feeRate) {
|
|
337
|
-
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
338
|
-
const tipData = await this.getTipData();
|
|
339
|
-
const currBlockheight = tipData.blockheight;
|
|
340
|
-
const blockheightDelta = requiredBlockheight - currBlockheight;
|
|
341
|
-
if (blockheightDelta <= 0)
|
|
342
|
-
return 0n;
|
|
343
|
-
const synchronizationFee = (BigInt(blockheightDelta) * await this.getFeePerBlock(feeRate))
|
|
344
|
-
+ EVMFees_1.EVMFees.getGasFee(EVMBtcRelay.GasCosts.GAS_BASE_MAIN * Math.ceil(blockheightDelta / this.maxHeadersPerTx), feeRate);
|
|
345
|
-
logger.debug("estimateSynchronizeFee(): required blockheight: " + requiredBlockheight +
|
|
346
|
-
" blockheight delta: " + blockheightDelta + " fee: " + synchronizationFee.toString(10));
|
|
347
|
-
return synchronizationFee;
|
|
348
|
-
}
|
|
349
|
-
/**
|
|
350
|
-
* Returns fee required (in native token) to synchronize a single block to btc relay
|
|
351
|
-
*
|
|
352
|
-
* @param feeRate
|
|
353
|
-
*/
|
|
354
|
-
async getFeePerBlock(feeRate) {
|
|
355
|
-
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
356
|
-
return EVMFees_1.EVMFees.getGasFee(EVMBtcRelay.GasCosts.GAS_PER_BLOCKHEADER, feeRate);
|
|
357
|
-
}
|
|
358
|
-
/**
|
|
359
|
-
* Gets fee rate required for submitting blockheaders to the main chain
|
|
360
|
-
*/
|
|
361
|
-
getMainFeeRate(signer) {
|
|
362
|
-
return this.Chain.Fees.getFeeRate();
|
|
363
|
-
}
|
|
364
|
-
/**
|
|
365
|
-
* Gets fee rate required for submitting blockheaders to the specific fork
|
|
366
|
-
*/
|
|
367
|
-
getForkFeeRate(signer, forkId) {
|
|
368
|
-
return this.Chain.Fees.getFeeRate();
|
|
369
|
-
}
|
|
370
|
-
saveInitialHeader(signer, header, epochStart, pastBlocksTimestamps, feeRate) {
|
|
371
|
-
throw new Error("Not supported, EVM contract is initialized with constructor!");
|
|
372
|
-
}
|
|
373
|
-
/**
|
|
374
|
-
* Gets committed header, identified by blockhash & blockheight, determines required BTC relay blockheight based on
|
|
375
|
-
* requiredConfirmations
|
|
376
|
-
* If synchronizer is passed & blockhash is not found, it produces transactions to sync up the btc relay to the
|
|
377
|
-
* current chain tip & adds them to the txs array
|
|
378
|
-
*
|
|
379
|
-
* @param signer
|
|
380
|
-
* @param btcRelay
|
|
381
|
-
* @param btcTxs
|
|
382
|
-
* @param txs solana transaction array, in case we need to synchronize the btc relay ourselves the synchronization
|
|
383
|
-
* txns are added here
|
|
384
|
-
* @param synchronizer optional synchronizer to use to synchronize the btc relay in case it is not yet synchronized
|
|
385
|
-
* to the required blockheight
|
|
386
|
-
* @param feeRate Fee rate to use for synchronization transactions
|
|
387
|
-
* @private
|
|
388
|
-
*/
|
|
389
|
-
static async getCommitedHeadersAndSynchronize(signer, btcRelay, btcTxs, txs, synchronizer, feeRate) {
|
|
390
|
-
const leavesTxs = [];
|
|
391
|
-
const blockheaders = {};
|
|
392
|
-
for (let btcTx of btcTxs) {
|
|
393
|
-
const requiredBlockheight = btcTx.blockheight + btcTx.requiredConfirmations - 1;
|
|
394
|
-
const result = await (0, Utils_1.tryWithRetries)(() => btcRelay.retrieveLogAndBlockheight({
|
|
395
|
-
blockhash: btcTx.blockhash
|
|
396
|
-
}, requiredBlockheight));
|
|
397
|
-
if (result != null) {
|
|
398
|
-
blockheaders[result.header.getBlockHash().toString("hex")] = result.header;
|
|
399
|
-
}
|
|
400
|
-
else {
|
|
401
|
-
leavesTxs.push(btcTx);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
if (leavesTxs.length === 0)
|
|
405
|
-
return blockheaders;
|
|
406
|
-
//Need to synchronize
|
|
407
|
-
if (synchronizer == null)
|
|
408
|
-
return null;
|
|
409
|
-
//TODO: We don't have to synchronize to tip, only to our required blockheight
|
|
410
|
-
const resp = await synchronizer.syncToLatestTxs(signer.toString(), feeRate);
|
|
411
|
-
logger.debug("getCommitedHeaderAndSynchronize(): BTC Relay not synchronized to required blockheight, " +
|
|
412
|
-
"synchronizing ourselves in " + resp.txs.length + " txs");
|
|
413
|
-
logger.debug("getCommitedHeaderAndSynchronize(): BTC Relay computed header map: ", resp.computedHeaderMap);
|
|
414
|
-
txs.push(...resp.txs);
|
|
415
|
-
for (let key in resp.computedHeaderMap) {
|
|
416
|
-
const header = resp.computedHeaderMap[key];
|
|
417
|
-
blockheaders[header.getBlockHash().toString("hex")] = header;
|
|
418
|
-
}
|
|
419
|
-
//Check that blockhashes of all the rest txs are included
|
|
420
|
-
for (let btcTx of leavesTxs) {
|
|
421
|
-
if (blockheaders[btcTx.blockhash] == null)
|
|
422
|
-
return null;
|
|
423
|
-
}
|
|
424
|
-
//Retrieve computed headers
|
|
425
|
-
return blockheaders;
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
exports.EVMBtcRelay = EVMBtcRelay;
|
|
429
|
-
EVMBtcRelay.GasCosts = {
|
|
430
|
-
GAS_PER_BLOCKHEADER: 30000,
|
|
431
|
-
GAS_BASE_MAIN: 15000 + 21000,
|
|
432
|
-
GAS_PER_BLOCKHEADER_FORK: 65000,
|
|
433
|
-
GAS_PER_BLOCKHEADER_FORKED: 10000,
|
|
434
|
-
GAS_BASE_FORK: 25000 + 21000
|
|
435
|
-
};
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EVMBtcRelay = void 0;
|
|
4
|
+
const base_1 = require("@atomiqlabs/base");
|
|
5
|
+
const EVMBtcHeader_1 = require("./headers/EVMBtcHeader");
|
|
6
|
+
const Utils_1 = require("../../utils/Utils");
|
|
7
|
+
const EVMContractBase_1 = require("../contract/EVMContractBase");
|
|
8
|
+
const EVMBtcStoredHeader_1 = require("./headers/EVMBtcStoredHeader");
|
|
9
|
+
const EVMFees_1 = require("../chain/modules/EVMFees");
|
|
10
|
+
const BtcRelayAbi_1 = require("./BtcRelayAbi");
|
|
11
|
+
const ethers_1 = require("ethers");
|
|
12
|
+
const promise_cache_ts_1 = require("promise-cache-ts");
|
|
13
|
+
function serializeBlockHeader(e) {
|
|
14
|
+
return new EVMBtcHeader_1.EVMBtcHeader({
|
|
15
|
+
version: e.getVersion(),
|
|
16
|
+
previousBlockhash: Buffer.from(e.getPrevBlockhash(), "hex").reverse(),
|
|
17
|
+
merkleRoot: Buffer.from(e.getMerkleRoot(), "hex").reverse(),
|
|
18
|
+
timestamp: e.getTimestamp(),
|
|
19
|
+
nbits: e.getNbits(),
|
|
20
|
+
nonce: e.getNonce(),
|
|
21
|
+
hash: Buffer.from(e.getHash(), "hex").reverse()
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
const logger = (0, Utils_1.getLogger)("EVMBtcRelay: ");
|
|
25
|
+
class EVMBtcRelay extends EVMContractBase_1.EVMContractBase {
|
|
26
|
+
async SaveMainHeaders(signer, mainHeaders, storedHeader, feeRate) {
|
|
27
|
+
const tx = await this.contract.submitMainBlockheaders.populateTransaction(Buffer.concat([
|
|
28
|
+
storedHeader.serialize(),
|
|
29
|
+
Buffer.concat(mainHeaders.map(header => header.serializeCompact()))
|
|
30
|
+
]));
|
|
31
|
+
tx.from = signer;
|
|
32
|
+
EVMFees_1.EVMFees.applyFeeRate(tx, EVMBtcRelay.GasCosts.GAS_BASE_MAIN + (EVMBtcRelay.GasCosts.GAS_PER_BLOCKHEADER * mainHeaders.length), feeRate);
|
|
33
|
+
return tx;
|
|
34
|
+
}
|
|
35
|
+
async SaveShortForkHeaders(signer, forkHeaders, storedHeader, feeRate) {
|
|
36
|
+
const tx = await this.contract.submitShortForkBlockheaders.populateTransaction(Buffer.concat([
|
|
37
|
+
storedHeader.serialize(),
|
|
38
|
+
Buffer.concat(forkHeaders.map(header => header.serializeCompact()))
|
|
39
|
+
]));
|
|
40
|
+
tx.from = signer;
|
|
41
|
+
EVMFees_1.EVMFees.applyFeeRate(tx, EVMBtcRelay.GasCosts.GAS_BASE_MAIN + (EVMBtcRelay.GasCosts.GAS_PER_BLOCKHEADER * forkHeaders.length), feeRate);
|
|
42
|
+
return tx;
|
|
43
|
+
}
|
|
44
|
+
async SaveLongForkHeaders(signer, forkId, forkHeaders, storedHeader, feeRate, totalForkHeaders = 100) {
|
|
45
|
+
const tx = await this.contract.submitForkBlockheaders.populateTransaction(forkId, Buffer.concat([
|
|
46
|
+
storedHeader.serialize(),
|
|
47
|
+
Buffer.concat(forkHeaders.map(header => header.serializeCompact()))
|
|
48
|
+
]));
|
|
49
|
+
tx.from = signer;
|
|
50
|
+
EVMFees_1.EVMFees.applyFeeRate(tx, EVMBtcRelay.GasCosts.GAS_BASE_FORK + (EVMBtcRelay.GasCosts.GAS_PER_BLOCKHEADER_FORK * forkHeaders.length) + (EVMBtcRelay.GasCosts.GAS_PER_BLOCKHEADER_FORKED * totalForkHeaders), feeRate);
|
|
51
|
+
return tx;
|
|
52
|
+
}
|
|
53
|
+
constructor(chainInterface, bitcoinRpc, bitcoinNetwork, contractAddress, contractDeploymentHeight) {
|
|
54
|
+
super(chainInterface, contractAddress, BtcRelayAbi_1.BtcRelayAbi, contractDeploymentHeight);
|
|
55
|
+
this.maxHeadersPerTx = 100;
|
|
56
|
+
this.maxForkHeadersPerTx = 50;
|
|
57
|
+
this.maxShortForkHeadersPerTx = 100;
|
|
58
|
+
this.commitHashCache = new promise_cache_ts_1.PromiseLruCache(1000);
|
|
59
|
+
this.blockHashCache = new promise_cache_ts_1.PromiseLruCache(1000);
|
|
60
|
+
this.bitcoinRpc = bitcoinRpc;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Computes subsequent commited headers as they will appear on the blockchain when transactions
|
|
64
|
+
* are submitted & confirmed
|
|
65
|
+
*
|
|
66
|
+
* @param initialStoredHeader
|
|
67
|
+
* @param syncedHeaders
|
|
68
|
+
* @private
|
|
69
|
+
*/
|
|
70
|
+
computeCommitedHeaders(initialStoredHeader, syncedHeaders) {
|
|
71
|
+
const computedCommitedHeaders = [initialStoredHeader];
|
|
72
|
+
for (let blockHeader of syncedHeaders) {
|
|
73
|
+
computedCommitedHeaders.push(computedCommitedHeaders[computedCommitedHeaders.length - 1].computeNext(blockHeader));
|
|
74
|
+
}
|
|
75
|
+
return computedCommitedHeaders;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* A common logic for submitting blockheaders in a transaction
|
|
79
|
+
*
|
|
80
|
+
* @param signer
|
|
81
|
+
* @param headers headers to sync to the btc relay
|
|
82
|
+
* @param storedHeader current latest stored block header for a given fork
|
|
83
|
+
* @param tipWork work of the current tip in a given fork
|
|
84
|
+
* @param forkId forkId to submit to, forkId=0 means main chain, forkId=-1 means short fork
|
|
85
|
+
* @param feeRate feeRate for the transaction
|
|
86
|
+
* @param totalForkHeaders Total number of headers in a fork
|
|
87
|
+
* @private
|
|
88
|
+
*/
|
|
89
|
+
async _saveHeaders(signer, headers, storedHeader, tipWork, forkId, feeRate, totalForkHeaders) {
|
|
90
|
+
const blockHeaderObj = headers.map(serializeBlockHeader);
|
|
91
|
+
let tx;
|
|
92
|
+
switch (forkId) {
|
|
93
|
+
case -1:
|
|
94
|
+
tx = await this.SaveShortForkHeaders(signer, blockHeaderObj, storedHeader, feeRate);
|
|
95
|
+
break;
|
|
96
|
+
case 0:
|
|
97
|
+
tx = await this.SaveMainHeaders(signer, blockHeaderObj, storedHeader, feeRate);
|
|
98
|
+
break;
|
|
99
|
+
default:
|
|
100
|
+
tx = await this.SaveLongForkHeaders(signer, forkId, blockHeaderObj, storedHeader, feeRate, totalForkHeaders);
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
const computedCommitedHeaders = this.computeCommitedHeaders(storedHeader, blockHeaderObj);
|
|
104
|
+
const lastStoredHeader = computedCommitedHeaders[computedCommitedHeaders.length - 1];
|
|
105
|
+
if (forkId !== 0 && base_1.StatePredictorUtils.gtBuffer(lastStoredHeader.getBlockHash(), tipWork)) {
|
|
106
|
+
//Fork's work is higher than main chain's work, this fork will become a main chain
|
|
107
|
+
forkId = 0;
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
forkId: forkId,
|
|
111
|
+
lastStoredHeader,
|
|
112
|
+
tx,
|
|
113
|
+
computedCommitedHeaders
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
async findStoredBlockheaderInTraces(txTrace, commitHash) {
|
|
117
|
+
if (txTrace.to.toLowerCase() === (await this.contract.getAddress()).toLowerCase()) {
|
|
118
|
+
let dataBuffer;
|
|
119
|
+
if (txTrace.type === "CREATE") {
|
|
120
|
+
dataBuffer = Buffer.from(txTrace.input.substring(txTrace.input.length - 384, txTrace.input.length - 64), "hex");
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
const result = this.parseCalldata(txTrace.input);
|
|
124
|
+
if (result != null) {
|
|
125
|
+
if (result.name === "submitMainBlockheaders" || result.name === "submitShortForkBlockheaders") {
|
|
126
|
+
const functionCall = result;
|
|
127
|
+
dataBuffer = Buffer.from((0, ethers_1.hexlify)(functionCall.args[0]).substring(2), "hex");
|
|
128
|
+
}
|
|
129
|
+
else if (result.name === "submitForkBlockheaders") {
|
|
130
|
+
const functionCall = result;
|
|
131
|
+
dataBuffer = Buffer.from((0, ethers_1.hexlify)(functionCall.args[1]).substring(2), "hex");
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (dataBuffer != null) {
|
|
136
|
+
let storedHeader = EVMBtcStoredHeader_1.EVMBtcStoredHeader.deserialize(dataBuffer.subarray(0, 160));
|
|
137
|
+
if (storedHeader.getCommitHash() === commitHash)
|
|
138
|
+
return storedHeader;
|
|
139
|
+
for (let i = 160; i < dataBuffer.length; i += 48) {
|
|
140
|
+
const blockHeader = EVMBtcHeader_1.EVMBtcHeader.deserialize(dataBuffer.subarray(i, i + 48));
|
|
141
|
+
storedHeader = storedHeader.computeNext(blockHeader);
|
|
142
|
+
if (storedHeader.getCommitHash() === commitHash)
|
|
143
|
+
return storedHeader;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (txTrace.calls != null) {
|
|
148
|
+
for (let call of txTrace.calls) {
|
|
149
|
+
const result = await this.findStoredBlockheaderInTraces(call, commitHash);
|
|
150
|
+
if (result != null)
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
getBlock(commitHash, blockHash) {
|
|
157
|
+
const blockHashString = blockHash == null ? null : "0x" + Buffer.from([...blockHash]).reverse().toString("hex");
|
|
158
|
+
const generator = () => this.Events.findInContractEvents(["StoreHeader", "StoreForkHeader"], [
|
|
159
|
+
commitHash,
|
|
160
|
+
blockHashString
|
|
161
|
+
], async (event) => {
|
|
162
|
+
const txTrace = await this.Chain.Transactions.traceTransaction(event.transactionHash);
|
|
163
|
+
const storedBlockheader = await this.findStoredBlockheaderInTraces(txTrace, event.args.commitHash);
|
|
164
|
+
if (storedBlockheader == null)
|
|
165
|
+
return null;
|
|
166
|
+
this.commitHashCache.set(event.args.commitHash, Promise.resolve([storedBlockheader, event.args.commitHash]));
|
|
167
|
+
this.blockHashCache.set(event.args.blockHash, Promise.resolve([storedBlockheader, event.args.commitHash]));
|
|
168
|
+
return [storedBlockheader, event.args.commitHash];
|
|
169
|
+
});
|
|
170
|
+
if (commitHash != null)
|
|
171
|
+
return this.commitHashCache.getOrComputeAsync(commitHash, generator);
|
|
172
|
+
if (blockHashString != null)
|
|
173
|
+
return this.blockHashCache.getOrComputeAsync(blockHashString, generator);
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
async getBlockHeight() {
|
|
177
|
+
return Number(await this.contract.getBlockheight());
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Returns data about current main chain tip stored in the btc relay
|
|
181
|
+
*/
|
|
182
|
+
async getTipData() {
|
|
183
|
+
const commitHash = await this.contract.getTipCommitHash();
|
|
184
|
+
if (commitHash == null || BigInt(commitHash) === BigInt(0))
|
|
185
|
+
return null;
|
|
186
|
+
const result = await this.getBlock(commitHash);
|
|
187
|
+
if (result == null)
|
|
188
|
+
return null;
|
|
189
|
+
const storedBlockHeader = result[0];
|
|
190
|
+
return {
|
|
191
|
+
blockheight: storedBlockHeader.getBlockheight(),
|
|
192
|
+
commitHash: commitHash,
|
|
193
|
+
blockhash: storedBlockHeader.getBlockHash().toString("hex"),
|
|
194
|
+
chainWork: storedBlockHeader.getChainWork()
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Retrieves blockheader with a specific blockhash, returns null if requiredBlockheight is provided and
|
|
199
|
+
* btc relay contract is not synced up to the desired blockheight
|
|
200
|
+
*
|
|
201
|
+
* @param blockData
|
|
202
|
+
* @param requiredBlockheight
|
|
203
|
+
*/
|
|
204
|
+
async retrieveLogAndBlockheight(blockData, requiredBlockheight) {
|
|
205
|
+
//TODO: we can fetch the blockheight and events in parallel
|
|
206
|
+
const blockHeight = await this.getBlockHeight();
|
|
207
|
+
if (requiredBlockheight != null && blockHeight < requiredBlockheight) {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
const result = await this.getBlock(null, Buffer.from(blockData.blockhash, "hex"));
|
|
211
|
+
if (result == null)
|
|
212
|
+
return null;
|
|
213
|
+
const [storedBlockHeader, commitHash] = result;
|
|
214
|
+
//Check if block is part of the main chain
|
|
215
|
+
const chainCommitment = await this.contract.getCommitHash(storedBlockHeader.blockHeight);
|
|
216
|
+
if (chainCommitment !== commitHash)
|
|
217
|
+
return null;
|
|
218
|
+
logger.debug("retrieveLogAndBlockheight(): block found," +
|
|
219
|
+
" commit hash: " + commitHash + " blockhash: " + blockData.blockhash + " current btc relay height: " + blockHeight);
|
|
220
|
+
return { header: storedBlockHeader, height: blockHeight };
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Retrieves blockheader data by blockheader's commit hash,
|
|
224
|
+
*
|
|
225
|
+
* @param commitmentHashStr
|
|
226
|
+
* @param blockData
|
|
227
|
+
*/
|
|
228
|
+
async retrieveLogByCommitHash(commitmentHashStr, blockData) {
|
|
229
|
+
const result = await this.getBlock(commitmentHashStr, Buffer.from(blockData.blockhash, "hex"));
|
|
230
|
+
if (result == null)
|
|
231
|
+
return null;
|
|
232
|
+
const [storedBlockHeader, commitHash] = result;
|
|
233
|
+
//Check if block is part of the main chain
|
|
234
|
+
const chainCommitment = await this.contract.getCommitHash(storedBlockHeader.blockHeight);
|
|
235
|
+
if (chainCommitment !== commitHash)
|
|
236
|
+
return null;
|
|
237
|
+
logger.debug("retrieveLogByCommitHash(): block found," +
|
|
238
|
+
" commit hash: " + commitmentHashStr + " blockhash: " + blockData.blockhash + " height: " + storedBlockHeader.blockHeight);
|
|
239
|
+
return storedBlockHeader;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Retrieves latest known stored blockheader & blockheader from bitcoin RPC that is in the main chain
|
|
243
|
+
*/
|
|
244
|
+
async retrieveLatestKnownBlockLog() {
|
|
245
|
+
const data = await this.Events.findInContractEvents(["StoreHeader", "StoreForkHeader"], null, async (event) => {
|
|
246
|
+
const blockHashHex = Buffer.from(event.args.blockHash.substring(2), "hex").reverse().toString("hex");
|
|
247
|
+
const commitHash = event.args.commitHash;
|
|
248
|
+
const isInBtcMainChain = await this.bitcoinRpc.isInMainChain(blockHashHex).catch(() => false);
|
|
249
|
+
if (!isInBtcMainChain)
|
|
250
|
+
return null;
|
|
251
|
+
const blockHeader = await this.bitcoinRpc.getBlockHeader(blockHashHex);
|
|
252
|
+
if (commitHash !== await this.contract.getCommitHash(blockHeader.getHeight()))
|
|
253
|
+
return null;
|
|
254
|
+
const txTrace = await this.Chain.Transactions.traceTransaction(event.transactionHash);
|
|
255
|
+
const storedHeader = await this.findStoredBlockheaderInTraces(txTrace, commitHash);
|
|
256
|
+
if (storedHeader == null)
|
|
257
|
+
return null;
|
|
258
|
+
return {
|
|
259
|
+
resultStoredHeader: storedHeader,
|
|
260
|
+
resultBitcoinHeader: blockHeader,
|
|
261
|
+
commitHash: commitHash
|
|
262
|
+
};
|
|
263
|
+
});
|
|
264
|
+
if (data != null)
|
|
265
|
+
logger.debug("retrieveLatestKnownBlockLog(): block found," +
|
|
266
|
+
" commit hash: " + data.commitHash + " blockhash: " + data.resultBitcoinHeader.getHash() +
|
|
267
|
+
" height: " + data.resultStoredHeader.getBlockheight());
|
|
268
|
+
return data;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Saves blockheaders as a bitcoin main chain to the btc relay
|
|
272
|
+
*
|
|
273
|
+
* @param signer
|
|
274
|
+
* @param mainHeaders
|
|
275
|
+
* @param storedHeader
|
|
276
|
+
* @param feeRate
|
|
277
|
+
*/
|
|
278
|
+
async saveMainHeaders(signer, mainHeaders, storedHeader, feeRate) {
|
|
279
|
+
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
280
|
+
logger.debug("saveMainHeaders(): submitting main blockheaders, count: " + mainHeaders.length);
|
|
281
|
+
return this._saveHeaders(signer, mainHeaders, storedHeader, null, 0, feeRate, 0);
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Creates a new long fork and submits the headers to it
|
|
285
|
+
*
|
|
286
|
+
* @param signer
|
|
287
|
+
* @param forkHeaders
|
|
288
|
+
* @param storedHeader
|
|
289
|
+
* @param tipWork
|
|
290
|
+
* @param feeRate
|
|
291
|
+
*/
|
|
292
|
+
async saveNewForkHeaders(signer, forkHeaders, storedHeader, tipWork, feeRate) {
|
|
293
|
+
let forkId = Math.floor(Math.random() * 0xFFFFFFFFFFFF);
|
|
294
|
+
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
295
|
+
logger.debug("saveNewForkHeaders(): submitting new fork & blockheaders," +
|
|
296
|
+
" count: " + forkHeaders.length + " forkId: 0x" + forkId.toString(16));
|
|
297
|
+
return await this._saveHeaders(signer, forkHeaders, storedHeader, tipWork, forkId, feeRate, forkHeaders.length);
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Continues submitting blockheaders to a given fork
|
|
301
|
+
*
|
|
302
|
+
* @param signer
|
|
303
|
+
* @param forkHeaders
|
|
304
|
+
* @param storedHeader
|
|
305
|
+
* @param forkId
|
|
306
|
+
* @param tipWork
|
|
307
|
+
* @param feeRate
|
|
308
|
+
*/
|
|
309
|
+
async saveForkHeaders(signer, forkHeaders, storedHeader, forkId, tipWork, feeRate) {
|
|
310
|
+
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
311
|
+
logger.debug("saveForkHeaders(): submitting blockheaders to existing fork," +
|
|
312
|
+
" count: " + forkHeaders.length + " forkId: 0x" + forkId.toString(16));
|
|
313
|
+
return this._saveHeaders(signer, forkHeaders, storedHeader, tipWork, forkId, feeRate, 100);
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Submits short fork with given blockheaders
|
|
317
|
+
*
|
|
318
|
+
* @param signer
|
|
319
|
+
* @param forkHeaders
|
|
320
|
+
* @param storedHeader
|
|
321
|
+
* @param tipWork
|
|
322
|
+
* @param feeRate
|
|
323
|
+
*/
|
|
324
|
+
async saveShortForkHeaders(signer, forkHeaders, storedHeader, tipWork, feeRate) {
|
|
325
|
+
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
326
|
+
logger.debug("saveShortForkHeaders(): submitting short fork blockheaders," +
|
|
327
|
+
" count: " + forkHeaders.length);
|
|
328
|
+
return this._saveHeaders(signer, forkHeaders, storedHeader, tipWork, -1, feeRate, 0);
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Estimate required synchronization fee (worst case) to synchronize btc relay to the required blockheight
|
|
332
|
+
*
|
|
333
|
+
* @param requiredBlockheight
|
|
334
|
+
* @param feeRate
|
|
335
|
+
*/
|
|
336
|
+
async estimateSynchronizeFee(requiredBlockheight, feeRate) {
|
|
337
|
+
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
338
|
+
const tipData = await this.getTipData();
|
|
339
|
+
const currBlockheight = tipData.blockheight;
|
|
340
|
+
const blockheightDelta = requiredBlockheight - currBlockheight;
|
|
341
|
+
if (blockheightDelta <= 0)
|
|
342
|
+
return 0n;
|
|
343
|
+
const synchronizationFee = (BigInt(blockheightDelta) * await this.getFeePerBlock(feeRate))
|
|
344
|
+
+ EVMFees_1.EVMFees.getGasFee(EVMBtcRelay.GasCosts.GAS_BASE_MAIN * Math.ceil(blockheightDelta / this.maxHeadersPerTx), feeRate);
|
|
345
|
+
logger.debug("estimateSynchronizeFee(): required blockheight: " + requiredBlockheight +
|
|
346
|
+
" blockheight delta: " + blockheightDelta + " fee: " + synchronizationFee.toString(10));
|
|
347
|
+
return synchronizationFee;
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Returns fee required (in native token) to synchronize a single block to btc relay
|
|
351
|
+
*
|
|
352
|
+
* @param feeRate
|
|
353
|
+
*/
|
|
354
|
+
async getFeePerBlock(feeRate) {
|
|
355
|
+
feeRate ?? (feeRate = await this.Chain.Fees.getFeeRate());
|
|
356
|
+
return EVMFees_1.EVMFees.getGasFee(EVMBtcRelay.GasCosts.GAS_PER_BLOCKHEADER, feeRate);
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Gets fee rate required for submitting blockheaders to the main chain
|
|
360
|
+
*/
|
|
361
|
+
getMainFeeRate(signer) {
|
|
362
|
+
return this.Chain.Fees.getFeeRate();
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Gets fee rate required for submitting blockheaders to the specific fork
|
|
366
|
+
*/
|
|
367
|
+
getForkFeeRate(signer, forkId) {
|
|
368
|
+
return this.Chain.Fees.getFeeRate();
|
|
369
|
+
}
|
|
370
|
+
saveInitialHeader(signer, header, epochStart, pastBlocksTimestamps, feeRate) {
|
|
371
|
+
throw new Error("Not supported, EVM contract is initialized with constructor!");
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Gets committed header, identified by blockhash & blockheight, determines required BTC relay blockheight based on
|
|
375
|
+
* requiredConfirmations
|
|
376
|
+
* If synchronizer is passed & blockhash is not found, it produces transactions to sync up the btc relay to the
|
|
377
|
+
* current chain tip & adds them to the txs array
|
|
378
|
+
*
|
|
379
|
+
* @param signer
|
|
380
|
+
* @param btcRelay
|
|
381
|
+
* @param btcTxs
|
|
382
|
+
* @param txs solana transaction array, in case we need to synchronize the btc relay ourselves the synchronization
|
|
383
|
+
* txns are added here
|
|
384
|
+
* @param synchronizer optional synchronizer to use to synchronize the btc relay in case it is not yet synchronized
|
|
385
|
+
* to the required blockheight
|
|
386
|
+
* @param feeRate Fee rate to use for synchronization transactions
|
|
387
|
+
* @private
|
|
388
|
+
*/
|
|
389
|
+
static async getCommitedHeadersAndSynchronize(signer, btcRelay, btcTxs, txs, synchronizer, feeRate) {
|
|
390
|
+
const leavesTxs = [];
|
|
391
|
+
const blockheaders = {};
|
|
392
|
+
for (let btcTx of btcTxs) {
|
|
393
|
+
const requiredBlockheight = btcTx.blockheight + btcTx.requiredConfirmations - 1;
|
|
394
|
+
const result = await (0, Utils_1.tryWithRetries)(() => btcRelay.retrieveLogAndBlockheight({
|
|
395
|
+
blockhash: btcTx.blockhash
|
|
396
|
+
}, requiredBlockheight));
|
|
397
|
+
if (result != null) {
|
|
398
|
+
blockheaders[result.header.getBlockHash().toString("hex")] = result.header;
|
|
399
|
+
}
|
|
400
|
+
else {
|
|
401
|
+
leavesTxs.push(btcTx);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
if (leavesTxs.length === 0)
|
|
405
|
+
return blockheaders;
|
|
406
|
+
//Need to synchronize
|
|
407
|
+
if (synchronizer == null)
|
|
408
|
+
return null;
|
|
409
|
+
//TODO: We don't have to synchronize to tip, only to our required blockheight
|
|
410
|
+
const resp = await synchronizer.syncToLatestTxs(signer.toString(), feeRate);
|
|
411
|
+
logger.debug("getCommitedHeaderAndSynchronize(): BTC Relay not synchronized to required blockheight, " +
|
|
412
|
+
"synchronizing ourselves in " + resp.txs.length + " txs");
|
|
413
|
+
logger.debug("getCommitedHeaderAndSynchronize(): BTC Relay computed header map: ", resp.computedHeaderMap);
|
|
414
|
+
txs.push(...resp.txs);
|
|
415
|
+
for (let key in resp.computedHeaderMap) {
|
|
416
|
+
const header = resp.computedHeaderMap[key];
|
|
417
|
+
blockheaders[header.getBlockHash().toString("hex")] = header;
|
|
418
|
+
}
|
|
419
|
+
//Check that blockhashes of all the rest txs are included
|
|
420
|
+
for (let btcTx of leavesTxs) {
|
|
421
|
+
if (blockheaders[btcTx.blockhash] == null)
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
424
|
+
//Retrieve computed headers
|
|
425
|
+
return blockheaders;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
exports.EVMBtcRelay = EVMBtcRelay;
|
|
429
|
+
EVMBtcRelay.GasCosts = {
|
|
430
|
+
GAS_PER_BLOCKHEADER: 30000,
|
|
431
|
+
GAS_BASE_MAIN: 15000 + 21000,
|
|
432
|
+
GAS_PER_BLOCKHEADER_FORK: 65000,
|
|
433
|
+
GAS_PER_BLOCKHEADER_FORKED: 10000,
|
|
434
|
+
GAS_BASE_FORK: 25000 + 21000
|
|
435
|
+
};
|