@atomiqlabs/chain-solana 12.0.14 → 12.0.15
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 +29 -29
- package/dist/index.js +45 -45
- package/dist/solana/SolanaChainType.d.ts +11 -11
- package/dist/solana/SolanaChainType.js +2 -2
- package/dist/solana/SolanaChains.d.ts +20 -20
- package/dist/solana/SolanaChains.js +25 -25
- package/dist/solana/SolanaInitializer.d.ts +18 -18
- package/dist/solana/SolanaInitializer.js +63 -63
- package/dist/solana/btcrelay/SolanaBtcRelay.d.ts +228 -228
- package/dist/solana/btcrelay/SolanaBtcRelay.js +441 -441
- package/dist/solana/btcrelay/headers/SolanaBtcHeader.d.ts +29 -29
- package/dist/solana/btcrelay/headers/SolanaBtcHeader.js +34 -34
- package/dist/solana/btcrelay/headers/SolanaBtcStoredHeader.d.ts +46 -46
- package/dist/solana/btcrelay/headers/SolanaBtcStoredHeader.js +78 -78
- package/dist/solana/btcrelay/program/programIdl.json +671 -671
- package/dist/solana/chain/SolanaAction.d.ts +26 -26
- package/dist/solana/chain/SolanaAction.js +86 -86
- package/dist/solana/chain/SolanaChainInterface.d.ts +65 -65
- package/dist/solana/chain/SolanaChainInterface.js +125 -125
- package/dist/solana/chain/SolanaModule.d.ts +14 -14
- package/dist/solana/chain/SolanaModule.js +13 -13
- package/dist/solana/chain/modules/SolanaAddresses.d.ts +8 -8
- package/dist/solana/chain/modules/SolanaAddresses.js +22 -22
- package/dist/solana/chain/modules/SolanaBlocks.d.ts +28 -28
- package/dist/solana/chain/modules/SolanaBlocks.js +72 -72
- package/dist/solana/chain/modules/SolanaEvents.d.ts +68 -68
- package/dist/solana/chain/modules/SolanaEvents.js +238 -238
- package/dist/solana/chain/modules/SolanaFees.d.ts +121 -121
- package/dist/solana/chain/modules/SolanaFees.js +379 -379
- package/dist/solana/chain/modules/SolanaSignatures.d.ts +23 -23
- package/dist/solana/chain/modules/SolanaSignatures.js +39 -39
- package/dist/solana/chain/modules/SolanaSlots.d.ts +31 -31
- package/dist/solana/chain/modules/SolanaSlots.js +68 -68
- package/dist/solana/chain/modules/SolanaTokens.d.ts +136 -136
- package/dist/solana/chain/modules/SolanaTokens.js +248 -248
- package/dist/solana/chain/modules/SolanaTransactions.d.ts +124 -124
- package/dist/solana/chain/modules/SolanaTransactions.js +323 -323
- package/dist/solana/events/SolanaChainEvents.d.ts +88 -88
- package/dist/solana/events/SolanaChainEvents.js +256 -256
- package/dist/solana/events/SolanaChainEventsBrowser.d.ts +75 -75
- package/dist/solana/events/SolanaChainEventsBrowser.js +172 -172
- package/dist/solana/program/SolanaProgramBase.d.ts +40 -40
- package/dist/solana/program/SolanaProgramBase.js +43 -43
- package/dist/solana/program/SolanaProgramModule.d.ts +8 -8
- package/dist/solana/program/SolanaProgramModule.js +11 -11
- package/dist/solana/program/modules/SolanaProgramEvents.d.ts +53 -53
- package/dist/solana/program/modules/SolanaProgramEvents.js +114 -114
- package/dist/solana/swaps/SolanaSwapData.d.ts +71 -71
- package/dist/solana/swaps/SolanaSwapData.js +292 -292
- package/dist/solana/swaps/SolanaSwapModule.d.ts +10 -10
- package/dist/solana/swaps/SolanaSwapModule.js +11 -11
- package/dist/solana/swaps/SolanaSwapProgram.d.ts +224 -224
- package/dist/solana/swaps/SolanaSwapProgram.js +570 -570
- package/dist/solana/swaps/SwapTypeEnum.d.ts +11 -11
- package/dist/solana/swaps/SwapTypeEnum.js +42 -42
- package/dist/solana/swaps/modules/SolanaDataAccount.d.ts +94 -94
- package/dist/solana/swaps/modules/SolanaDataAccount.js +231 -231
- package/dist/solana/swaps/modules/SolanaLpVault.d.ts +71 -71
- package/dist/solana/swaps/modules/SolanaLpVault.js +173 -173
- package/dist/solana/swaps/modules/SwapClaim.d.ts +129 -129
- package/dist/solana/swaps/modules/SwapClaim.js +291 -291
- package/dist/solana/swaps/modules/SwapInit.d.ts +217 -217
- package/dist/solana/swaps/modules/SwapInit.js +519 -519
- package/dist/solana/swaps/modules/SwapRefund.d.ts +82 -82
- package/dist/solana/swaps/modules/SwapRefund.js +262 -262
- package/dist/solana/swaps/programIdl.json +945 -945
- package/dist/solana/swaps/programTypes.d.ts +943 -943
- package/dist/solana/swaps/programTypes.js +945 -945
- package/dist/solana/wallet/SolanaKeypairWallet.d.ts +9 -9
- package/dist/solana/wallet/SolanaKeypairWallet.js +33 -33
- package/dist/solana/wallet/SolanaSigner.d.ts +11 -11
- package/dist/solana/wallet/SolanaSigner.js +17 -17
- package/dist/utils/Utils.d.ts +53 -53
- package/dist/utils/Utils.js +170 -170
- package/package.json +41 -41
- package/src/index.ts +36 -36
- package/src/solana/SolanaChainType.ts +27 -27
- package/src/solana/SolanaChains.ts +23 -23
- package/src/solana/SolanaInitializer.ts +102 -102
- package/src/solana/btcrelay/SolanaBtcRelay.ts +589 -589
- package/src/solana/btcrelay/headers/SolanaBtcHeader.ts +57 -57
- package/src/solana/btcrelay/headers/SolanaBtcStoredHeader.ts +102 -102
- package/src/solana/btcrelay/program/programIdl.json +670 -670
- package/src/solana/chain/SolanaAction.ts +108 -108
- package/src/solana/chain/SolanaChainInterface.ts +192 -192
- package/src/solana/chain/SolanaModule.ts +20 -20
- package/src/solana/chain/modules/SolanaAddresses.ts +20 -20
- package/src/solana/chain/modules/SolanaBlocks.ts +78 -78
- package/src/solana/chain/modules/SolanaEvents.ts +270 -270
- package/src/solana/chain/modules/SolanaFees.ts +450 -450
- package/src/solana/chain/modules/SolanaSignatures.ts +39 -39
- package/src/solana/chain/modules/SolanaSlots.ts +82 -82
- package/src/solana/chain/modules/SolanaTokens.ts +307 -307
- package/src/solana/chain/modules/SolanaTransactions.ts +365 -365
- package/src/solana/events/SolanaChainEvents.ts +299 -299
- package/src/solana/events/SolanaChainEventsBrowser.ts +209 -209
- package/src/solana/program/SolanaProgramBase.ts +79 -79
- package/src/solana/program/SolanaProgramModule.ts +15 -15
- package/src/solana/program/modules/SolanaProgramEvents.ts +155 -155
- package/src/solana/swaps/SolanaSwapData.ts +430 -430
- package/src/solana/swaps/SolanaSwapModule.ts +16 -16
- package/src/solana/swaps/SolanaSwapProgram.ts +854 -854
- package/src/solana/swaps/SwapTypeEnum.ts +29 -29
- package/src/solana/swaps/modules/SolanaDataAccount.ts +307 -307
- package/src/solana/swaps/modules/SolanaLpVault.ts +215 -215
- package/src/solana/swaps/modules/SwapClaim.ts +389 -389
- package/src/solana/swaps/modules/SwapInit.ts +663 -663
- package/src/solana/swaps/modules/SwapRefund.ts +323 -323
- package/src/solana/swaps/programIdl.json +944 -944
- package/src/solana/swaps/programTypes.ts +1885 -1885
- package/src/solana/wallet/SolanaKeypairWallet.ts +36 -36
- package/src/solana/wallet/SolanaSigner.ts +24 -24
- package/src/utils/Utils.ts +180 -180
|
@@ -1,589 +1,589 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Connection,
|
|
3
|
-
PublicKey,
|
|
4
|
-
Signer,
|
|
5
|
-
SystemProgram,
|
|
6
|
-
Transaction
|
|
7
|
-
} from "@solana/web3.js";
|
|
8
|
-
import {SolanaBtcStoredHeader, SolanaBtcStoredHeaderType} from "./headers/SolanaBtcStoredHeader";
|
|
9
|
-
import {SolanaBtcHeader} from "./headers/SolanaBtcHeader";
|
|
10
|
-
import * as programIdl from "./program/programIdl.json";
|
|
11
|
-
import {BitcoinRpc, BtcBlock, BtcRelay, StatePredictorUtils} from "@atomiqlabs/base";
|
|
12
|
-
import {MethodsBuilder} from "@coral-xyz/anchor/dist/cjs/program/namespace/methods";
|
|
13
|
-
import {SolanaProgramBase} from "../program/SolanaProgramBase";
|
|
14
|
-
import {SolanaAction} from "../chain/SolanaAction";
|
|
15
|
-
import {Buffer} from "buffer";
|
|
16
|
-
import {SolanaTx} from "../chain/modules/SolanaTransactions";
|
|
17
|
-
import {SolanaSigner} from "../wallet/SolanaSigner";
|
|
18
|
-
import * as BN from "bn.js";
|
|
19
|
-
import {SolanaChainInterface} from "../chain/SolanaChainInterface";
|
|
20
|
-
import {getLogger} from "../../utils/Utils";
|
|
21
|
-
|
|
22
|
-
const MAX_CLOSE_IX_PER_TX = 10;
|
|
23
|
-
|
|
24
|
-
function serializeBlockHeader(e: BtcBlock): SolanaBtcHeader {
|
|
25
|
-
return new SolanaBtcHeader({
|
|
26
|
-
version: e.getVersion(),
|
|
27
|
-
reversedPrevBlockhash: [...Buffer.from(e.getPrevBlockhash(), "hex").reverse()],
|
|
28
|
-
merkleRoot: [...Buffer.from(e.getMerkleRoot(), "hex").reverse()],
|
|
29
|
-
timestamp: e.getTimestamp(),
|
|
30
|
-
nbits: e.getNbits(),
|
|
31
|
-
nonce: e.getNonce(),
|
|
32
|
-
hash: Buffer.from(e.getHash(), "hex").reverse()
|
|
33
|
-
});
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export class SolanaBtcRelay<B extends BtcBlock> extends SolanaProgramBase<any> implements BtcRelay<SolanaBtcStoredHeader, {tx: Transaction, signers: Signer[]}, B, SolanaSigner> {
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Creates initialization action for initializing the btc relay
|
|
40
|
-
*
|
|
41
|
-
* @param signer
|
|
42
|
-
* @param header
|
|
43
|
-
* @param epochStart
|
|
44
|
-
* @param pastBlocksTimestamps
|
|
45
|
-
* @constructor
|
|
46
|
-
* @private
|
|
47
|
-
*/
|
|
48
|
-
private async Initialize(signer: PublicKey, header: B, epochStart: number, pastBlocksTimestamps: number[]): Promise<SolanaAction> {
|
|
49
|
-
const serializedBlock = serializeBlockHeader(header);
|
|
50
|
-
return new SolanaAction(signer, this.Chain,
|
|
51
|
-
await this.program.methods
|
|
52
|
-
.initialize(
|
|
53
|
-
serializedBlock,
|
|
54
|
-
header.getHeight(),
|
|
55
|
-
header.getChainWork(),
|
|
56
|
-
epochStart,
|
|
57
|
-
pastBlocksTimestamps
|
|
58
|
-
)
|
|
59
|
-
.accounts({
|
|
60
|
-
signer,
|
|
61
|
-
mainState: this.BtcRelayMainState,
|
|
62
|
-
headerTopic: this.BtcRelayHeader(serializedBlock.hash),
|
|
63
|
-
systemProgram: SystemProgram.programId
|
|
64
|
-
})
|
|
65
|
-
.instruction(),
|
|
66
|
-
100_000
|
|
67
|
-
)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Creates verify action to be used with the swap program, specifies the action to be firstIxBeforeComputeBudget,
|
|
72
|
-
* such that the verify instruction will always be the 0th in the transaction, this is required because
|
|
73
|
-
* swap program expects the verify instruction to be at the 0th position
|
|
74
|
-
*
|
|
75
|
-
* @param signer
|
|
76
|
-
* @param reversedTxId
|
|
77
|
-
* @param confirmations
|
|
78
|
-
* @param position
|
|
79
|
-
* @param reversedMerkleProof
|
|
80
|
-
* @param committedHeader
|
|
81
|
-
*/
|
|
82
|
-
public async Verify(
|
|
83
|
-
signer: PublicKey,
|
|
84
|
-
reversedTxId: Buffer,
|
|
85
|
-
confirmations: number,
|
|
86
|
-
position: number,
|
|
87
|
-
reversedMerkleProof: Buffer[],
|
|
88
|
-
committedHeader: SolanaBtcStoredHeader
|
|
89
|
-
): Promise<SolanaAction> {
|
|
90
|
-
return new SolanaAction(signer, this.Chain,
|
|
91
|
-
await this.program.methods
|
|
92
|
-
.verifyTransaction(
|
|
93
|
-
reversedTxId,
|
|
94
|
-
confirmations,
|
|
95
|
-
position,
|
|
96
|
-
reversedMerkleProof,
|
|
97
|
-
committedHeader
|
|
98
|
-
)
|
|
99
|
-
.accounts({
|
|
100
|
-
signer,
|
|
101
|
-
mainState: this.BtcRelayMainState
|
|
102
|
-
})
|
|
103
|
-
.instruction(),
|
|
104
|
-
null,
|
|
105
|
-
null,
|
|
106
|
-
null,
|
|
107
|
-
true
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
public async CloseForkAccount(signer: PublicKey, forkId: number): Promise<SolanaAction> {
|
|
112
|
-
return new SolanaAction(signer, this.Chain,
|
|
113
|
-
await this.program.methods
|
|
114
|
-
.closeForkAccount(
|
|
115
|
-
new BN(forkId)
|
|
116
|
-
)
|
|
117
|
-
.accounts({
|
|
118
|
-
signer,
|
|
119
|
-
forkState: this.BtcRelayFork(forkId, signer),
|
|
120
|
-
systemProgram: SystemProgram.programId,
|
|
121
|
-
})
|
|
122
|
-
.instruction(),
|
|
123
|
-
20000
|
|
124
|
-
)
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
BtcRelayMainState = this.pda("state");
|
|
128
|
-
BtcRelayHeader = this.pda("header", (hash: Buffer) => [hash]);
|
|
129
|
-
BtcRelayFork = this.pda("fork",
|
|
130
|
-
(forkId: number, pubkey: PublicKey) => [new BN(forkId).toArrayLike(Buffer, "le", 8), pubkey.toBuffer()]
|
|
131
|
-
);
|
|
132
|
-
|
|
133
|
-
bitcoinRpc: BitcoinRpc<B>;
|
|
134
|
-
|
|
135
|
-
readonly maxHeadersPerTx: number = 5;
|
|
136
|
-
readonly maxForkHeadersPerTx: number = 4;
|
|
137
|
-
readonly maxShortForkHeadersPerTx: number = 4;
|
|
138
|
-
|
|
139
|
-
constructor(
|
|
140
|
-
chainInterface: SolanaChainInterface,
|
|
141
|
-
bitcoinRpc: BitcoinRpc<B>,
|
|
142
|
-
programAddress?: string
|
|
143
|
-
) {
|
|
144
|
-
super(chainInterface, programIdl, programAddress);
|
|
145
|
-
this.bitcoinRpc = bitcoinRpc;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Gets set of block commitments representing current main chain from the mainState
|
|
150
|
-
*
|
|
151
|
-
* @param mainState
|
|
152
|
-
* @private
|
|
153
|
-
*/
|
|
154
|
-
private getBlockCommitmentsSet(mainState: any): Set<string> {
|
|
155
|
-
const storedCommitments = new Set<string>();
|
|
156
|
-
mainState.blockCommitments.forEach(e => {
|
|
157
|
-
storedCommitments.add(Buffer.from(e).toString("hex"));
|
|
158
|
-
});
|
|
159
|
-
return storedCommitments;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Computes subsequent commited headers as they will appear on the blockchain when transactions
|
|
164
|
-
* are submitted & confirmed
|
|
165
|
-
*
|
|
166
|
-
* @param initialStoredHeader
|
|
167
|
-
* @param syncedHeaders
|
|
168
|
-
* @private
|
|
169
|
-
*/
|
|
170
|
-
private computeCommitedHeaders(initialStoredHeader: SolanaBtcStoredHeader, syncedHeaders: SolanaBtcHeader[]) {
|
|
171
|
-
const computedCommitedHeaders = [initialStoredHeader];
|
|
172
|
-
for(let blockHeader of syncedHeaders) {
|
|
173
|
-
computedCommitedHeaders.push(computedCommitedHeaders[computedCommitedHeaders.length-1].computeNext(blockHeader));
|
|
174
|
-
}
|
|
175
|
-
return computedCommitedHeaders;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* A common logic for submitting blockheaders in a transaction
|
|
180
|
-
*
|
|
181
|
-
* @param signer
|
|
182
|
-
* @param headers headers to sync to the btc relay
|
|
183
|
-
* @param storedHeader current latest stored block header for a given fork
|
|
184
|
-
* @param tipWork work of the current tip in a given fork
|
|
185
|
-
* @param forkId forkId to submit to, forkId=0 means main chain
|
|
186
|
-
* @param feeRate feeRate for the transaction
|
|
187
|
-
* @param createTx transaction generator function
|
|
188
|
-
* @private
|
|
189
|
-
*/
|
|
190
|
-
private async _saveHeaders(
|
|
191
|
-
signer: PublicKey,
|
|
192
|
-
headers: BtcBlock[],
|
|
193
|
-
storedHeader: SolanaBtcStoredHeader,
|
|
194
|
-
tipWork: Buffer,
|
|
195
|
-
forkId: number,
|
|
196
|
-
feeRate: string,
|
|
197
|
-
createTx: (blockHeaders: SolanaBtcHeader[]) => MethodsBuilder<any, any>
|
|
198
|
-
) {
|
|
199
|
-
const blockHeaderObj = headers.map(serializeBlockHeader);
|
|
200
|
-
|
|
201
|
-
const tx = await createTx(blockHeaderObj)
|
|
202
|
-
.remainingAccounts(blockHeaderObj.map(e => {
|
|
203
|
-
return {
|
|
204
|
-
pubkey: this.BtcRelayHeader(e.hash),
|
|
205
|
-
isSigner: false,
|
|
206
|
-
isWritable: false
|
|
207
|
-
}
|
|
208
|
-
}))
|
|
209
|
-
.transaction();
|
|
210
|
-
tx.feePayer = signer;
|
|
211
|
-
|
|
212
|
-
this.Chain.Fees.applyFeeRateBegin(tx, null, feeRate);
|
|
213
|
-
this.Chain.Fees.applyFeeRateEnd(tx, null, feeRate);
|
|
214
|
-
|
|
215
|
-
const computedCommitedHeaders = this.computeCommitedHeaders(storedHeader, blockHeaderObj);
|
|
216
|
-
const lastStoredHeader = computedCommitedHeaders[computedCommitedHeaders.length-1];
|
|
217
|
-
if(forkId!==0 && StatePredictorUtils.gtBuffer(Buffer.from(lastStoredHeader.chainWork), tipWork)) {
|
|
218
|
-
//Fork's work is higher than main chain's work, this fork will become a main chain
|
|
219
|
-
forkId = 0;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
return {
|
|
223
|
-
forkId: forkId,
|
|
224
|
-
lastStoredHeader,
|
|
225
|
-
tx: {
|
|
226
|
-
tx,
|
|
227
|
-
signers: []
|
|
228
|
-
},
|
|
229
|
-
computedCommitedHeaders
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Returns data about current main chain tip stored in the btc relay
|
|
235
|
-
*/
|
|
236
|
-
public async getTipData(): Promise<{ commitHash: string; blockhash: string, chainWork: Buffer, blockheight: number }> {
|
|
237
|
-
const data: any = await this.program.account.mainState.fetchNullable(this.BtcRelayMainState);
|
|
238
|
-
if(data==null) return null;
|
|
239
|
-
|
|
240
|
-
return {
|
|
241
|
-
blockheight: data.blockHeight,
|
|
242
|
-
commitHash: Buffer.from(data.tipCommitHash).toString("hex"),
|
|
243
|
-
blockhash: Buffer.from(data.tipBlockHash).reverse().toString("hex"),
|
|
244
|
-
chainWork: Buffer.from(data.chainWork)
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Retrieves blockheader with a specific blockhash, returns null if requiredBlockheight is provided and
|
|
250
|
-
* btc relay contract is not synced up to the desired blockheight
|
|
251
|
-
*
|
|
252
|
-
* @param blockData
|
|
253
|
-
* @param requiredBlockheight
|
|
254
|
-
*/
|
|
255
|
-
public async retrieveLogAndBlockheight(blockData: {blockhash: string}, requiredBlockheight?: number): Promise<{
|
|
256
|
-
header: SolanaBtcStoredHeader,
|
|
257
|
-
height: number
|
|
258
|
-
} | null> {
|
|
259
|
-
const mainState: any = await this.program.account.mainState.fetch(this.BtcRelayMainState);
|
|
260
|
-
|
|
261
|
-
if(requiredBlockheight!=null && mainState.blockHeight < requiredBlockheight) {
|
|
262
|
-
return null;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
const storedCommitments = this.getBlockCommitmentsSet(mainState);
|
|
266
|
-
const blockHashBuffer = Buffer.from(blockData.blockhash, 'hex').reverse();
|
|
267
|
-
const topicKey = this.BtcRelayHeader(blockHashBuffer);
|
|
268
|
-
|
|
269
|
-
const data = await this.Events.findInEvents(topicKey, async (event) => {
|
|
270
|
-
if(event.name==="StoreFork" || event.name==="StoreHeader") {
|
|
271
|
-
const eventData: any = event.data;
|
|
272
|
-
const commitHash = Buffer.from(eventData.commitHash).toString("hex");
|
|
273
|
-
if(blockHashBuffer.equals(Buffer.from(eventData.blockHash)) && storedCommitments.has(commitHash))
|
|
274
|
-
return {
|
|
275
|
-
header: new SolanaBtcStoredHeader(eventData.header as SolanaBtcStoredHeaderType),
|
|
276
|
-
height: mainState.blockHeight as number,
|
|
277
|
-
commitHash
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
});
|
|
281
|
-
if(data!=null) this.logger.debug("retrieveLogAndBlockheight(): block found," +
|
|
282
|
-
" commit hash: "+data.commitHash+" blockhash: "+blockData.blockhash+" height: "+data.height);
|
|
283
|
-
|
|
284
|
-
return data;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* Retrieves blockheader data by blockheader's commit hash,
|
|
289
|
-
*
|
|
290
|
-
* @param commitmentHashStr
|
|
291
|
-
* @param blockData
|
|
292
|
-
*/
|
|
293
|
-
public async retrieveLogByCommitHash(commitmentHashStr: string, blockData: {blockhash: string}): Promise<SolanaBtcStoredHeader> {
|
|
294
|
-
const blockHashBuffer = Buffer.from(blockData.blockhash, "hex").reverse();
|
|
295
|
-
const topicKey = this.BtcRelayHeader(blockHashBuffer);
|
|
296
|
-
|
|
297
|
-
const data = await this.Events.findInEvents(topicKey, async (event) => {
|
|
298
|
-
if(event.name==="StoreFork" || event.name==="StoreHeader") {
|
|
299
|
-
const eventData: any = event.data;
|
|
300
|
-
const commitHash = Buffer.from(eventData.commitHash).toString("hex");
|
|
301
|
-
if(commitmentHashStr===commitHash)
|
|
302
|
-
return new SolanaBtcStoredHeader(eventData.header as SolanaBtcStoredHeaderType);
|
|
303
|
-
}
|
|
304
|
-
});
|
|
305
|
-
if(data!=null) this.logger.debug("retrieveLogByCommitHash(): block found," +
|
|
306
|
-
" commit hash: "+commitmentHashStr+" blockhash: "+blockData.blockhash+" height: "+data.blockheight);
|
|
307
|
-
|
|
308
|
-
return data;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
/**
|
|
312
|
-
* Retrieves latest known stored blockheader & blockheader from bitcoin RPC that is in the main chain
|
|
313
|
-
*/
|
|
314
|
-
public async retrieveLatestKnownBlockLog(): Promise<{
|
|
315
|
-
resultStoredHeader: SolanaBtcStoredHeader,
|
|
316
|
-
resultBitcoinHeader: B
|
|
317
|
-
}> {
|
|
318
|
-
const mainState: any = await this.program.account.mainState.fetch(this.BtcRelayMainState);
|
|
319
|
-
const storedCommitments = this.getBlockCommitmentsSet(mainState);
|
|
320
|
-
|
|
321
|
-
const data = await this.Events.findInEvents(this.program.programId, async (event) => {
|
|
322
|
-
if(event.name==="StoreFork" || event.name==="StoreHeader") {
|
|
323
|
-
const eventData: any = event.data;
|
|
324
|
-
const blockHashHex = Buffer.from(eventData.blockHash).reverse().toString("hex");
|
|
325
|
-
const isInMainChain = await this.bitcoinRpc.isInMainChain(blockHashHex).catch(() => false);
|
|
326
|
-
const commitHash = Buffer.from(eventData.commitHash).toString("hex");
|
|
327
|
-
//Check if this fork is part of main chain
|
|
328
|
-
if(isInMainChain && storedCommitments.has(commitHash))
|
|
329
|
-
return {
|
|
330
|
-
resultStoredHeader: new SolanaBtcStoredHeader(eventData.header),
|
|
331
|
-
resultBitcoinHeader: await this.bitcoinRpc.getBlockHeader(blockHashHex),
|
|
332
|
-
commitHash: commitHash
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
|
-
}, null, 10);
|
|
336
|
-
if(data!=null) this.logger.debug("retrieveLatestKnownBlockLog(): block found," +
|
|
337
|
-
" commit hash: "+data.commitHash+" blockhash: "+data.resultBitcoinHeader.getHash()+
|
|
338
|
-
" height: "+data.resultStoredHeader.blockheight);
|
|
339
|
-
|
|
340
|
-
return data;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
/**
|
|
344
|
-
* Saves initial block header when the btc relay is in uninitialized state
|
|
345
|
-
*
|
|
346
|
-
* @param signer
|
|
347
|
-
* @param header a bitcoin blockheader to submit
|
|
348
|
-
* @param epochStart timestamp of the start of the epoch (block timestamp at blockheight-(blockheight%2016))
|
|
349
|
-
* @param pastBlocksTimestamps timestamp of the past 10 blocks
|
|
350
|
-
* @param feeRate fee rate to use for the transaction
|
|
351
|
-
*/
|
|
352
|
-
async saveInitialHeader(
|
|
353
|
-
signer: string,
|
|
354
|
-
header: B,
|
|
355
|
-
epochStart: number,
|
|
356
|
-
pastBlocksTimestamps: number[],
|
|
357
|
-
feeRate?: string
|
|
358
|
-
): Promise<{ tx: Transaction; signers: Signer[]; }> {
|
|
359
|
-
if(pastBlocksTimestamps.length!==10) throw new Error("Invalid prevBlocksTimestamps");
|
|
360
|
-
|
|
361
|
-
const action = await this.Initialize(new PublicKey(signer), header, epochStart, pastBlocksTimestamps);
|
|
362
|
-
|
|
363
|
-
this.logger.debug("saveInitialHeader(): saving initial header, blockhash: "+header.getHash()+
|
|
364
|
-
" blockheight: "+header.getHeight()+" epochStart: "+epochStart+" past block timestamps: "+pastBlocksTimestamps.join());
|
|
365
|
-
|
|
366
|
-
return await action.tx(feeRate);
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
/**
|
|
370
|
-
* Saves blockheaders as a bitcoin main chain to the btc relay
|
|
371
|
-
*
|
|
372
|
-
* @param signer
|
|
373
|
-
* @param mainHeaders
|
|
374
|
-
* @param storedHeader
|
|
375
|
-
* @param feeRate
|
|
376
|
-
*/
|
|
377
|
-
public saveMainHeaders(signer: string, mainHeaders: BtcBlock[], storedHeader: SolanaBtcStoredHeader, feeRate?: string) {
|
|
378
|
-
this.logger.debug("saveMainHeaders(): submitting main blockheaders, count: "+mainHeaders.length);
|
|
379
|
-
const _signer = new PublicKey(signer);
|
|
380
|
-
return this._saveHeaders(_signer, mainHeaders, storedHeader, null, 0, feeRate,
|
|
381
|
-
(blockHeaders) => this.program.methods
|
|
382
|
-
.submitBlockHeaders(
|
|
383
|
-
blockHeaders,
|
|
384
|
-
storedHeader
|
|
385
|
-
)
|
|
386
|
-
.accounts({
|
|
387
|
-
signer: _signer,
|
|
388
|
-
mainState: this.BtcRelayMainState,
|
|
389
|
-
})
|
|
390
|
-
);
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
/**
|
|
394
|
-
* Creates a new long fork and submits the headers to it
|
|
395
|
-
*
|
|
396
|
-
* @param signer
|
|
397
|
-
* @param forkHeaders
|
|
398
|
-
* @param storedHeader
|
|
399
|
-
* @param tipWork
|
|
400
|
-
* @param feeRate
|
|
401
|
-
*/
|
|
402
|
-
public async saveNewForkHeaders(signer: string, forkHeaders: BtcBlock[], storedHeader: SolanaBtcStoredHeader, tipWork: Buffer, feeRate?: string) {
|
|
403
|
-
const mainState: any = await this.program.account.mainState.fetch(this.BtcRelayMainState);
|
|
404
|
-
let forkId: BN = mainState.forkCounter;
|
|
405
|
-
|
|
406
|
-
const _signer = new PublicKey(signer);
|
|
407
|
-
|
|
408
|
-
this.logger.debug("saveNewForkHeaders(): submitting new fork & blockheaders," +
|
|
409
|
-
" count: "+forkHeaders.length+" forkId: "+forkId.toString(10));
|
|
410
|
-
return await this._saveHeaders(_signer, forkHeaders, storedHeader, tipWork, forkId.toNumber(), feeRate,
|
|
411
|
-
(blockHeaders) => this.program.methods
|
|
412
|
-
.submitForkHeaders(
|
|
413
|
-
blockHeaders,
|
|
414
|
-
storedHeader,
|
|
415
|
-
forkId,
|
|
416
|
-
true
|
|
417
|
-
)
|
|
418
|
-
.accounts({
|
|
419
|
-
signer: _signer,
|
|
420
|
-
mainState: this.BtcRelayMainState,
|
|
421
|
-
forkState: this.BtcRelayFork(forkId.toNumber(), _signer),
|
|
422
|
-
systemProgram: SystemProgram.programId,
|
|
423
|
-
})
|
|
424
|
-
);
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
/**
|
|
428
|
-
* Continues submitting blockheaders to a given fork
|
|
429
|
-
*
|
|
430
|
-
* @param signer
|
|
431
|
-
* @param forkHeaders
|
|
432
|
-
* @param storedHeader
|
|
433
|
-
* @param forkId
|
|
434
|
-
* @param tipWork
|
|
435
|
-
* @param feeRate
|
|
436
|
-
*/
|
|
437
|
-
public saveForkHeaders(signer: string, forkHeaders: BtcBlock[], storedHeader: SolanaBtcStoredHeader, forkId: number, tipWork: Buffer, feeRate?: string) {
|
|
438
|
-
this.logger.debug("saveForkHeaders(): submitting blockheaders to existing fork," +
|
|
439
|
-
" count: "+forkHeaders.length+" forkId: "+forkId.toString(10));
|
|
440
|
-
|
|
441
|
-
const _signer = new PublicKey(signer);
|
|
442
|
-
|
|
443
|
-
return this._saveHeaders(_signer, forkHeaders, storedHeader, tipWork, forkId, feeRate,
|
|
444
|
-
(blockHeaders) => this.program.methods
|
|
445
|
-
.submitForkHeaders(
|
|
446
|
-
blockHeaders,
|
|
447
|
-
storedHeader,
|
|
448
|
-
new BN(forkId),
|
|
449
|
-
false
|
|
450
|
-
)
|
|
451
|
-
.accounts({
|
|
452
|
-
signer: _signer,
|
|
453
|
-
mainState: this.BtcRelayMainState,
|
|
454
|
-
forkState: this.BtcRelayFork(forkId, _signer),
|
|
455
|
-
systemProgram: SystemProgram.programId,
|
|
456
|
-
})
|
|
457
|
-
)
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
/**
|
|
461
|
-
* Submits short fork with given blockheaders
|
|
462
|
-
*
|
|
463
|
-
* @param signer
|
|
464
|
-
* @param forkHeaders
|
|
465
|
-
* @param storedHeader
|
|
466
|
-
* @param tipWork
|
|
467
|
-
* @param feeRate
|
|
468
|
-
*/
|
|
469
|
-
public saveShortForkHeaders(signer: string, forkHeaders: BtcBlock[], storedHeader: SolanaBtcStoredHeader, tipWork: Buffer, feeRate?: string) {
|
|
470
|
-
this.logger.debug("saveShortForkHeaders(): submitting short fork blockheaders," +
|
|
471
|
-
" count: "+forkHeaders.length);
|
|
472
|
-
|
|
473
|
-
const _signer = new PublicKey(signer);
|
|
474
|
-
|
|
475
|
-
return this._saveHeaders(_signer, forkHeaders, storedHeader, tipWork, -1, feeRate,
|
|
476
|
-
(blockHeaders) => this.program.methods
|
|
477
|
-
.submitShortForkHeaders(
|
|
478
|
-
blockHeaders,
|
|
479
|
-
storedHeader
|
|
480
|
-
)
|
|
481
|
-
.accounts({
|
|
482
|
-
signer: _signer,
|
|
483
|
-
mainState: this.BtcRelayMainState
|
|
484
|
-
})
|
|
485
|
-
);
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
/**
|
|
489
|
-
* Sweeps fork data PDAs back to self
|
|
490
|
-
*
|
|
491
|
-
* @param signer
|
|
492
|
-
* @param lastSweepId lastCheckedId returned from the previous sweepForkData() call
|
|
493
|
-
* @returns {number} lastCheckedId that should be passed to the next call of sweepForkData()
|
|
494
|
-
*/
|
|
495
|
-
public async sweepForkData(signer: SolanaSigner, lastSweepId?: number): Promise<number | null> {
|
|
496
|
-
const mainState: any = await this.program.account.mainState.fetch(this.BtcRelayMainState);
|
|
497
|
-
let forkId: number = mainState.forkCounter.toNumber();
|
|
498
|
-
|
|
499
|
-
const txs: SolanaTx[] = [];
|
|
500
|
-
let action = new SolanaAction(signer.getPublicKey(), this.Chain);
|
|
501
|
-
|
|
502
|
-
let lastCheckedId = lastSweepId;
|
|
503
|
-
for(
|
|
504
|
-
let i = lastSweepId==null ? 0 : lastSweepId+1;
|
|
505
|
-
i<=forkId; i++
|
|
506
|
-
) {
|
|
507
|
-
lastCheckedId = i;
|
|
508
|
-
|
|
509
|
-
const accountAddr = this.BtcRelayFork(i, signer.getPublicKey());
|
|
510
|
-
let forkState: any = await this.program.account.forkState.fetchNullable(accountAddr);
|
|
511
|
-
if(forkState==null) continue;
|
|
512
|
-
|
|
513
|
-
this.logger.info("sweepForkData(): sweeping forkId: "+i);
|
|
514
|
-
action.add(await this.CloseForkAccount(signer.getPublicKey(), i));
|
|
515
|
-
|
|
516
|
-
if(action.ixsLength()>=MAX_CLOSE_IX_PER_TX) {
|
|
517
|
-
await action.addToTxs(txs);
|
|
518
|
-
action = new SolanaAction(signer.getPublicKey(), this.Chain);
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
if(action.ixsLength()>=MAX_CLOSE_IX_PER_TX) {
|
|
523
|
-
await action.addToTxs(txs);
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
if(txs.length>0) {
|
|
527
|
-
const signatures = await this.Chain.sendAndConfirm(signer, txs, true);
|
|
528
|
-
this.logger.info("sweepForkData(): forks swept, signatures: "+signatures.join());
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
return lastCheckedId;
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
/**
|
|
535
|
-
* Estimate required synchronization fee (worst case) to synchronize btc relay to the required blockheight
|
|
536
|
-
*
|
|
537
|
-
* @param requiredBlockheight
|
|
538
|
-
* @param feeRate
|
|
539
|
-
*/
|
|
540
|
-
public async estimateSynchronizeFee(requiredBlockheight: number, feeRate?: string): Promise<bigint> {
|
|
541
|
-
const tipData = await this.getTipData();
|
|
542
|
-
const currBlockheight = tipData.blockheight;
|
|
543
|
-
|
|
544
|
-
const blockheightDelta = requiredBlockheight-currBlockheight;
|
|
545
|
-
|
|
546
|
-
if(blockheightDelta<=0) return 0n;
|
|
547
|
-
|
|
548
|
-
const synchronizationFee = BigInt(blockheightDelta) * await this.getFeePerBlock(feeRate);
|
|
549
|
-
this.logger.debug("estimateSynchronizeFee(): required blockheight: "+requiredBlockheight+
|
|
550
|
-
" blockheight delta: "+blockheightDelta+" fee: "+synchronizationFee.toString(10));
|
|
551
|
-
|
|
552
|
-
return synchronizationFee;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
/**
|
|
556
|
-
* Returns fee required (in SOL) to synchronize a single block to btc relay
|
|
557
|
-
*
|
|
558
|
-
* @param feeRate
|
|
559
|
-
*/
|
|
560
|
-
public async getFeePerBlock(feeRate?: string): Promise<bigint> {
|
|
561
|
-
// feeRate = feeRate || await this.getMainFeeRate(null);
|
|
562
|
-
// return BASE_FEE_SOL_PER_BLOCKHEADER.add(this.Fees.getPriorityFee(200000, feeRate, false));
|
|
563
|
-
return 50000n;
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
/**
|
|
567
|
-
* Gets fee rate required for submitting blockheaders to the main chain
|
|
568
|
-
*/
|
|
569
|
-
public getMainFeeRate(signer: string | null): Promise<string> {
|
|
570
|
-
const _signer = signer==null ? null : new PublicKey(signer);
|
|
571
|
-
return this.Chain.Fees.getFeeRate(_signer==null ? [this.BtcRelayMainState] : [
|
|
572
|
-
_signer,
|
|
573
|
-
this.BtcRelayMainState
|
|
574
|
-
]);
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
/**
|
|
578
|
-
* Gets fee rate required for submitting blockheaders to the specific fork
|
|
579
|
-
*/
|
|
580
|
-
public getForkFeeRate(signer: string, forkId: number): Promise<string> {
|
|
581
|
-
const _signer = new PublicKey(signer);
|
|
582
|
-
return this.Chain.Fees.getFeeRate([
|
|
583
|
-
_signer,
|
|
584
|
-
this.BtcRelayMainState,
|
|
585
|
-
this.BtcRelayFork(forkId, _signer)
|
|
586
|
-
]);
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
Connection,
|
|
3
|
+
PublicKey,
|
|
4
|
+
Signer,
|
|
5
|
+
SystemProgram,
|
|
6
|
+
Transaction
|
|
7
|
+
} from "@solana/web3.js";
|
|
8
|
+
import {SolanaBtcStoredHeader, SolanaBtcStoredHeaderType} from "./headers/SolanaBtcStoredHeader";
|
|
9
|
+
import {SolanaBtcHeader} from "./headers/SolanaBtcHeader";
|
|
10
|
+
import * as programIdl from "./program/programIdl.json";
|
|
11
|
+
import {BitcoinRpc, BtcBlock, BtcRelay, StatePredictorUtils} from "@atomiqlabs/base";
|
|
12
|
+
import {MethodsBuilder} from "@coral-xyz/anchor/dist/cjs/program/namespace/methods";
|
|
13
|
+
import {SolanaProgramBase} from "../program/SolanaProgramBase";
|
|
14
|
+
import {SolanaAction} from "../chain/SolanaAction";
|
|
15
|
+
import {Buffer} from "buffer";
|
|
16
|
+
import {SolanaTx} from "../chain/modules/SolanaTransactions";
|
|
17
|
+
import {SolanaSigner} from "../wallet/SolanaSigner";
|
|
18
|
+
import * as BN from "bn.js";
|
|
19
|
+
import {SolanaChainInterface} from "../chain/SolanaChainInterface";
|
|
20
|
+
import {getLogger} from "../../utils/Utils";
|
|
21
|
+
|
|
22
|
+
const MAX_CLOSE_IX_PER_TX = 10;
|
|
23
|
+
|
|
24
|
+
function serializeBlockHeader(e: BtcBlock): SolanaBtcHeader {
|
|
25
|
+
return new SolanaBtcHeader({
|
|
26
|
+
version: e.getVersion(),
|
|
27
|
+
reversedPrevBlockhash: [...Buffer.from(e.getPrevBlockhash(), "hex").reverse()],
|
|
28
|
+
merkleRoot: [...Buffer.from(e.getMerkleRoot(), "hex").reverse()],
|
|
29
|
+
timestamp: e.getTimestamp(),
|
|
30
|
+
nbits: e.getNbits(),
|
|
31
|
+
nonce: e.getNonce(),
|
|
32
|
+
hash: Buffer.from(e.getHash(), "hex").reverse()
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export class SolanaBtcRelay<B extends BtcBlock> extends SolanaProgramBase<any> implements BtcRelay<SolanaBtcStoredHeader, {tx: Transaction, signers: Signer[]}, B, SolanaSigner> {
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Creates initialization action for initializing the btc relay
|
|
40
|
+
*
|
|
41
|
+
* @param signer
|
|
42
|
+
* @param header
|
|
43
|
+
* @param epochStart
|
|
44
|
+
* @param pastBlocksTimestamps
|
|
45
|
+
* @constructor
|
|
46
|
+
* @private
|
|
47
|
+
*/
|
|
48
|
+
private async Initialize(signer: PublicKey, header: B, epochStart: number, pastBlocksTimestamps: number[]): Promise<SolanaAction> {
|
|
49
|
+
const serializedBlock = serializeBlockHeader(header);
|
|
50
|
+
return new SolanaAction(signer, this.Chain,
|
|
51
|
+
await this.program.methods
|
|
52
|
+
.initialize(
|
|
53
|
+
serializedBlock,
|
|
54
|
+
header.getHeight(),
|
|
55
|
+
header.getChainWork(),
|
|
56
|
+
epochStart,
|
|
57
|
+
pastBlocksTimestamps
|
|
58
|
+
)
|
|
59
|
+
.accounts({
|
|
60
|
+
signer,
|
|
61
|
+
mainState: this.BtcRelayMainState,
|
|
62
|
+
headerTopic: this.BtcRelayHeader(serializedBlock.hash),
|
|
63
|
+
systemProgram: SystemProgram.programId
|
|
64
|
+
})
|
|
65
|
+
.instruction(),
|
|
66
|
+
100_000
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Creates verify action to be used with the swap program, specifies the action to be firstIxBeforeComputeBudget,
|
|
72
|
+
* such that the verify instruction will always be the 0th in the transaction, this is required because
|
|
73
|
+
* swap program expects the verify instruction to be at the 0th position
|
|
74
|
+
*
|
|
75
|
+
* @param signer
|
|
76
|
+
* @param reversedTxId
|
|
77
|
+
* @param confirmations
|
|
78
|
+
* @param position
|
|
79
|
+
* @param reversedMerkleProof
|
|
80
|
+
* @param committedHeader
|
|
81
|
+
*/
|
|
82
|
+
public async Verify(
|
|
83
|
+
signer: PublicKey,
|
|
84
|
+
reversedTxId: Buffer,
|
|
85
|
+
confirmations: number,
|
|
86
|
+
position: number,
|
|
87
|
+
reversedMerkleProof: Buffer[],
|
|
88
|
+
committedHeader: SolanaBtcStoredHeader
|
|
89
|
+
): Promise<SolanaAction> {
|
|
90
|
+
return new SolanaAction(signer, this.Chain,
|
|
91
|
+
await this.program.methods
|
|
92
|
+
.verifyTransaction(
|
|
93
|
+
reversedTxId,
|
|
94
|
+
confirmations,
|
|
95
|
+
position,
|
|
96
|
+
reversedMerkleProof,
|
|
97
|
+
committedHeader
|
|
98
|
+
)
|
|
99
|
+
.accounts({
|
|
100
|
+
signer,
|
|
101
|
+
mainState: this.BtcRelayMainState
|
|
102
|
+
})
|
|
103
|
+
.instruction(),
|
|
104
|
+
null,
|
|
105
|
+
null,
|
|
106
|
+
null,
|
|
107
|
+
true
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
public async CloseForkAccount(signer: PublicKey, forkId: number): Promise<SolanaAction> {
|
|
112
|
+
return new SolanaAction(signer, this.Chain,
|
|
113
|
+
await this.program.methods
|
|
114
|
+
.closeForkAccount(
|
|
115
|
+
new BN(forkId)
|
|
116
|
+
)
|
|
117
|
+
.accounts({
|
|
118
|
+
signer,
|
|
119
|
+
forkState: this.BtcRelayFork(forkId, signer),
|
|
120
|
+
systemProgram: SystemProgram.programId,
|
|
121
|
+
})
|
|
122
|
+
.instruction(),
|
|
123
|
+
20000
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
BtcRelayMainState = this.pda("state");
|
|
128
|
+
BtcRelayHeader = this.pda("header", (hash: Buffer) => [hash]);
|
|
129
|
+
BtcRelayFork = this.pda("fork",
|
|
130
|
+
(forkId: number, pubkey: PublicKey) => [new BN(forkId).toArrayLike(Buffer, "le", 8), pubkey.toBuffer()]
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
bitcoinRpc: BitcoinRpc<B>;
|
|
134
|
+
|
|
135
|
+
readonly maxHeadersPerTx: number = 5;
|
|
136
|
+
readonly maxForkHeadersPerTx: number = 4;
|
|
137
|
+
readonly maxShortForkHeadersPerTx: number = 4;
|
|
138
|
+
|
|
139
|
+
constructor(
|
|
140
|
+
chainInterface: SolanaChainInterface,
|
|
141
|
+
bitcoinRpc: BitcoinRpc<B>,
|
|
142
|
+
programAddress?: string
|
|
143
|
+
) {
|
|
144
|
+
super(chainInterface, programIdl, programAddress);
|
|
145
|
+
this.bitcoinRpc = bitcoinRpc;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Gets set of block commitments representing current main chain from the mainState
|
|
150
|
+
*
|
|
151
|
+
* @param mainState
|
|
152
|
+
* @private
|
|
153
|
+
*/
|
|
154
|
+
private getBlockCommitmentsSet(mainState: any): Set<string> {
|
|
155
|
+
const storedCommitments = new Set<string>();
|
|
156
|
+
mainState.blockCommitments.forEach(e => {
|
|
157
|
+
storedCommitments.add(Buffer.from(e).toString("hex"));
|
|
158
|
+
});
|
|
159
|
+
return storedCommitments;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Computes subsequent commited headers as they will appear on the blockchain when transactions
|
|
164
|
+
* are submitted & confirmed
|
|
165
|
+
*
|
|
166
|
+
* @param initialStoredHeader
|
|
167
|
+
* @param syncedHeaders
|
|
168
|
+
* @private
|
|
169
|
+
*/
|
|
170
|
+
private computeCommitedHeaders(initialStoredHeader: SolanaBtcStoredHeader, syncedHeaders: SolanaBtcHeader[]) {
|
|
171
|
+
const computedCommitedHeaders = [initialStoredHeader];
|
|
172
|
+
for(let blockHeader of syncedHeaders) {
|
|
173
|
+
computedCommitedHeaders.push(computedCommitedHeaders[computedCommitedHeaders.length-1].computeNext(blockHeader));
|
|
174
|
+
}
|
|
175
|
+
return computedCommitedHeaders;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* A common logic for submitting blockheaders in a transaction
|
|
180
|
+
*
|
|
181
|
+
* @param signer
|
|
182
|
+
* @param headers headers to sync to the btc relay
|
|
183
|
+
* @param storedHeader current latest stored block header for a given fork
|
|
184
|
+
* @param tipWork work of the current tip in a given fork
|
|
185
|
+
* @param forkId forkId to submit to, forkId=0 means main chain
|
|
186
|
+
* @param feeRate feeRate for the transaction
|
|
187
|
+
* @param createTx transaction generator function
|
|
188
|
+
* @private
|
|
189
|
+
*/
|
|
190
|
+
private async _saveHeaders(
|
|
191
|
+
signer: PublicKey,
|
|
192
|
+
headers: BtcBlock[],
|
|
193
|
+
storedHeader: SolanaBtcStoredHeader,
|
|
194
|
+
tipWork: Buffer,
|
|
195
|
+
forkId: number,
|
|
196
|
+
feeRate: string,
|
|
197
|
+
createTx: (blockHeaders: SolanaBtcHeader[]) => MethodsBuilder<any, any>
|
|
198
|
+
) {
|
|
199
|
+
const blockHeaderObj = headers.map(serializeBlockHeader);
|
|
200
|
+
|
|
201
|
+
const tx = await createTx(blockHeaderObj)
|
|
202
|
+
.remainingAccounts(blockHeaderObj.map(e => {
|
|
203
|
+
return {
|
|
204
|
+
pubkey: this.BtcRelayHeader(e.hash),
|
|
205
|
+
isSigner: false,
|
|
206
|
+
isWritable: false
|
|
207
|
+
}
|
|
208
|
+
}))
|
|
209
|
+
.transaction();
|
|
210
|
+
tx.feePayer = signer;
|
|
211
|
+
|
|
212
|
+
this.Chain.Fees.applyFeeRateBegin(tx, null, feeRate);
|
|
213
|
+
this.Chain.Fees.applyFeeRateEnd(tx, null, feeRate);
|
|
214
|
+
|
|
215
|
+
const computedCommitedHeaders = this.computeCommitedHeaders(storedHeader, blockHeaderObj);
|
|
216
|
+
const lastStoredHeader = computedCommitedHeaders[computedCommitedHeaders.length-1];
|
|
217
|
+
if(forkId!==0 && StatePredictorUtils.gtBuffer(Buffer.from(lastStoredHeader.chainWork), tipWork)) {
|
|
218
|
+
//Fork's work is higher than main chain's work, this fork will become a main chain
|
|
219
|
+
forkId = 0;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return {
|
|
223
|
+
forkId: forkId,
|
|
224
|
+
lastStoredHeader,
|
|
225
|
+
tx: {
|
|
226
|
+
tx,
|
|
227
|
+
signers: []
|
|
228
|
+
},
|
|
229
|
+
computedCommitedHeaders
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Returns data about current main chain tip stored in the btc relay
|
|
235
|
+
*/
|
|
236
|
+
public async getTipData(): Promise<{ commitHash: string; blockhash: string, chainWork: Buffer, blockheight: number }> {
|
|
237
|
+
const data: any = await this.program.account.mainState.fetchNullable(this.BtcRelayMainState);
|
|
238
|
+
if(data==null) return null;
|
|
239
|
+
|
|
240
|
+
return {
|
|
241
|
+
blockheight: data.blockHeight,
|
|
242
|
+
commitHash: Buffer.from(data.tipCommitHash).toString("hex"),
|
|
243
|
+
blockhash: Buffer.from(data.tipBlockHash).reverse().toString("hex"),
|
|
244
|
+
chainWork: Buffer.from(data.chainWork)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Retrieves blockheader with a specific blockhash, returns null if requiredBlockheight is provided and
|
|
250
|
+
* btc relay contract is not synced up to the desired blockheight
|
|
251
|
+
*
|
|
252
|
+
* @param blockData
|
|
253
|
+
* @param requiredBlockheight
|
|
254
|
+
*/
|
|
255
|
+
public async retrieveLogAndBlockheight(blockData: {blockhash: string}, requiredBlockheight?: number): Promise<{
|
|
256
|
+
header: SolanaBtcStoredHeader,
|
|
257
|
+
height: number
|
|
258
|
+
} | null> {
|
|
259
|
+
const mainState: any = await this.program.account.mainState.fetch(this.BtcRelayMainState);
|
|
260
|
+
|
|
261
|
+
if(requiredBlockheight!=null && mainState.blockHeight < requiredBlockheight) {
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const storedCommitments = this.getBlockCommitmentsSet(mainState);
|
|
266
|
+
const blockHashBuffer = Buffer.from(blockData.blockhash, 'hex').reverse();
|
|
267
|
+
const topicKey = this.BtcRelayHeader(blockHashBuffer);
|
|
268
|
+
|
|
269
|
+
const data = await this.Events.findInEvents(topicKey, async (event) => {
|
|
270
|
+
if(event.name==="StoreFork" || event.name==="StoreHeader") {
|
|
271
|
+
const eventData: any = event.data;
|
|
272
|
+
const commitHash = Buffer.from(eventData.commitHash).toString("hex");
|
|
273
|
+
if(blockHashBuffer.equals(Buffer.from(eventData.blockHash)) && storedCommitments.has(commitHash))
|
|
274
|
+
return {
|
|
275
|
+
header: new SolanaBtcStoredHeader(eventData.header as SolanaBtcStoredHeaderType),
|
|
276
|
+
height: mainState.blockHeight as number,
|
|
277
|
+
commitHash
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
if(data!=null) this.logger.debug("retrieveLogAndBlockheight(): block found," +
|
|
282
|
+
" commit hash: "+data.commitHash+" blockhash: "+blockData.blockhash+" height: "+data.height);
|
|
283
|
+
|
|
284
|
+
return data;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Retrieves blockheader data by blockheader's commit hash,
|
|
289
|
+
*
|
|
290
|
+
* @param commitmentHashStr
|
|
291
|
+
* @param blockData
|
|
292
|
+
*/
|
|
293
|
+
public async retrieveLogByCommitHash(commitmentHashStr: string, blockData: {blockhash: string}): Promise<SolanaBtcStoredHeader> {
|
|
294
|
+
const blockHashBuffer = Buffer.from(blockData.blockhash, "hex").reverse();
|
|
295
|
+
const topicKey = this.BtcRelayHeader(blockHashBuffer);
|
|
296
|
+
|
|
297
|
+
const data = await this.Events.findInEvents(topicKey, async (event) => {
|
|
298
|
+
if(event.name==="StoreFork" || event.name==="StoreHeader") {
|
|
299
|
+
const eventData: any = event.data;
|
|
300
|
+
const commitHash = Buffer.from(eventData.commitHash).toString("hex");
|
|
301
|
+
if(commitmentHashStr===commitHash)
|
|
302
|
+
return new SolanaBtcStoredHeader(eventData.header as SolanaBtcStoredHeaderType);
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
if(data!=null) this.logger.debug("retrieveLogByCommitHash(): block found," +
|
|
306
|
+
" commit hash: "+commitmentHashStr+" blockhash: "+blockData.blockhash+" height: "+data.blockheight);
|
|
307
|
+
|
|
308
|
+
return data;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Retrieves latest known stored blockheader & blockheader from bitcoin RPC that is in the main chain
|
|
313
|
+
*/
|
|
314
|
+
public async retrieveLatestKnownBlockLog(): Promise<{
|
|
315
|
+
resultStoredHeader: SolanaBtcStoredHeader,
|
|
316
|
+
resultBitcoinHeader: B
|
|
317
|
+
}> {
|
|
318
|
+
const mainState: any = await this.program.account.mainState.fetch(this.BtcRelayMainState);
|
|
319
|
+
const storedCommitments = this.getBlockCommitmentsSet(mainState);
|
|
320
|
+
|
|
321
|
+
const data = await this.Events.findInEvents(this.program.programId, async (event) => {
|
|
322
|
+
if(event.name==="StoreFork" || event.name==="StoreHeader") {
|
|
323
|
+
const eventData: any = event.data;
|
|
324
|
+
const blockHashHex = Buffer.from(eventData.blockHash).reverse().toString("hex");
|
|
325
|
+
const isInMainChain = await this.bitcoinRpc.isInMainChain(blockHashHex).catch(() => false);
|
|
326
|
+
const commitHash = Buffer.from(eventData.commitHash).toString("hex");
|
|
327
|
+
//Check if this fork is part of main chain
|
|
328
|
+
if(isInMainChain && storedCommitments.has(commitHash))
|
|
329
|
+
return {
|
|
330
|
+
resultStoredHeader: new SolanaBtcStoredHeader(eventData.header),
|
|
331
|
+
resultBitcoinHeader: await this.bitcoinRpc.getBlockHeader(blockHashHex),
|
|
332
|
+
commitHash: commitHash
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
}, null, 10);
|
|
336
|
+
if(data!=null) this.logger.debug("retrieveLatestKnownBlockLog(): block found," +
|
|
337
|
+
" commit hash: "+data.commitHash+" blockhash: "+data.resultBitcoinHeader.getHash()+
|
|
338
|
+
" height: "+data.resultStoredHeader.blockheight);
|
|
339
|
+
|
|
340
|
+
return data;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Saves initial block header when the btc relay is in uninitialized state
|
|
345
|
+
*
|
|
346
|
+
* @param signer
|
|
347
|
+
* @param header a bitcoin blockheader to submit
|
|
348
|
+
* @param epochStart timestamp of the start of the epoch (block timestamp at blockheight-(blockheight%2016))
|
|
349
|
+
* @param pastBlocksTimestamps timestamp of the past 10 blocks
|
|
350
|
+
* @param feeRate fee rate to use for the transaction
|
|
351
|
+
*/
|
|
352
|
+
async saveInitialHeader(
|
|
353
|
+
signer: string,
|
|
354
|
+
header: B,
|
|
355
|
+
epochStart: number,
|
|
356
|
+
pastBlocksTimestamps: number[],
|
|
357
|
+
feeRate?: string
|
|
358
|
+
): Promise<{ tx: Transaction; signers: Signer[]; }> {
|
|
359
|
+
if(pastBlocksTimestamps.length!==10) throw new Error("Invalid prevBlocksTimestamps");
|
|
360
|
+
|
|
361
|
+
const action = await this.Initialize(new PublicKey(signer), header, epochStart, pastBlocksTimestamps);
|
|
362
|
+
|
|
363
|
+
this.logger.debug("saveInitialHeader(): saving initial header, blockhash: "+header.getHash()+
|
|
364
|
+
" blockheight: "+header.getHeight()+" epochStart: "+epochStart+" past block timestamps: "+pastBlocksTimestamps.join());
|
|
365
|
+
|
|
366
|
+
return await action.tx(feeRate);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Saves blockheaders as a bitcoin main chain to the btc relay
|
|
371
|
+
*
|
|
372
|
+
* @param signer
|
|
373
|
+
* @param mainHeaders
|
|
374
|
+
* @param storedHeader
|
|
375
|
+
* @param feeRate
|
|
376
|
+
*/
|
|
377
|
+
public saveMainHeaders(signer: string, mainHeaders: BtcBlock[], storedHeader: SolanaBtcStoredHeader, feeRate?: string) {
|
|
378
|
+
this.logger.debug("saveMainHeaders(): submitting main blockheaders, count: "+mainHeaders.length);
|
|
379
|
+
const _signer = new PublicKey(signer);
|
|
380
|
+
return this._saveHeaders(_signer, mainHeaders, storedHeader, null, 0, feeRate,
|
|
381
|
+
(blockHeaders) => this.program.methods
|
|
382
|
+
.submitBlockHeaders(
|
|
383
|
+
blockHeaders,
|
|
384
|
+
storedHeader
|
|
385
|
+
)
|
|
386
|
+
.accounts({
|
|
387
|
+
signer: _signer,
|
|
388
|
+
mainState: this.BtcRelayMainState,
|
|
389
|
+
})
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Creates a new long fork and submits the headers to it
|
|
395
|
+
*
|
|
396
|
+
* @param signer
|
|
397
|
+
* @param forkHeaders
|
|
398
|
+
* @param storedHeader
|
|
399
|
+
* @param tipWork
|
|
400
|
+
* @param feeRate
|
|
401
|
+
*/
|
|
402
|
+
public async saveNewForkHeaders(signer: string, forkHeaders: BtcBlock[], storedHeader: SolanaBtcStoredHeader, tipWork: Buffer, feeRate?: string) {
|
|
403
|
+
const mainState: any = await this.program.account.mainState.fetch(this.BtcRelayMainState);
|
|
404
|
+
let forkId: BN = mainState.forkCounter;
|
|
405
|
+
|
|
406
|
+
const _signer = new PublicKey(signer);
|
|
407
|
+
|
|
408
|
+
this.logger.debug("saveNewForkHeaders(): submitting new fork & blockheaders," +
|
|
409
|
+
" count: "+forkHeaders.length+" forkId: "+forkId.toString(10));
|
|
410
|
+
return await this._saveHeaders(_signer, forkHeaders, storedHeader, tipWork, forkId.toNumber(), feeRate,
|
|
411
|
+
(blockHeaders) => this.program.methods
|
|
412
|
+
.submitForkHeaders(
|
|
413
|
+
blockHeaders,
|
|
414
|
+
storedHeader,
|
|
415
|
+
forkId,
|
|
416
|
+
true
|
|
417
|
+
)
|
|
418
|
+
.accounts({
|
|
419
|
+
signer: _signer,
|
|
420
|
+
mainState: this.BtcRelayMainState,
|
|
421
|
+
forkState: this.BtcRelayFork(forkId.toNumber(), _signer),
|
|
422
|
+
systemProgram: SystemProgram.programId,
|
|
423
|
+
})
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Continues submitting blockheaders to a given fork
|
|
429
|
+
*
|
|
430
|
+
* @param signer
|
|
431
|
+
* @param forkHeaders
|
|
432
|
+
* @param storedHeader
|
|
433
|
+
* @param forkId
|
|
434
|
+
* @param tipWork
|
|
435
|
+
* @param feeRate
|
|
436
|
+
*/
|
|
437
|
+
public saveForkHeaders(signer: string, forkHeaders: BtcBlock[], storedHeader: SolanaBtcStoredHeader, forkId: number, tipWork: Buffer, feeRate?: string) {
|
|
438
|
+
this.logger.debug("saveForkHeaders(): submitting blockheaders to existing fork," +
|
|
439
|
+
" count: "+forkHeaders.length+" forkId: "+forkId.toString(10));
|
|
440
|
+
|
|
441
|
+
const _signer = new PublicKey(signer);
|
|
442
|
+
|
|
443
|
+
return this._saveHeaders(_signer, forkHeaders, storedHeader, tipWork, forkId, feeRate,
|
|
444
|
+
(blockHeaders) => this.program.methods
|
|
445
|
+
.submitForkHeaders(
|
|
446
|
+
blockHeaders,
|
|
447
|
+
storedHeader,
|
|
448
|
+
new BN(forkId),
|
|
449
|
+
false
|
|
450
|
+
)
|
|
451
|
+
.accounts({
|
|
452
|
+
signer: _signer,
|
|
453
|
+
mainState: this.BtcRelayMainState,
|
|
454
|
+
forkState: this.BtcRelayFork(forkId, _signer),
|
|
455
|
+
systemProgram: SystemProgram.programId,
|
|
456
|
+
})
|
|
457
|
+
)
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Submits short fork with given blockheaders
|
|
462
|
+
*
|
|
463
|
+
* @param signer
|
|
464
|
+
* @param forkHeaders
|
|
465
|
+
* @param storedHeader
|
|
466
|
+
* @param tipWork
|
|
467
|
+
* @param feeRate
|
|
468
|
+
*/
|
|
469
|
+
public saveShortForkHeaders(signer: string, forkHeaders: BtcBlock[], storedHeader: SolanaBtcStoredHeader, tipWork: Buffer, feeRate?: string) {
|
|
470
|
+
this.logger.debug("saveShortForkHeaders(): submitting short fork blockheaders," +
|
|
471
|
+
" count: "+forkHeaders.length);
|
|
472
|
+
|
|
473
|
+
const _signer = new PublicKey(signer);
|
|
474
|
+
|
|
475
|
+
return this._saveHeaders(_signer, forkHeaders, storedHeader, tipWork, -1, feeRate,
|
|
476
|
+
(blockHeaders) => this.program.methods
|
|
477
|
+
.submitShortForkHeaders(
|
|
478
|
+
blockHeaders,
|
|
479
|
+
storedHeader
|
|
480
|
+
)
|
|
481
|
+
.accounts({
|
|
482
|
+
signer: _signer,
|
|
483
|
+
mainState: this.BtcRelayMainState
|
|
484
|
+
})
|
|
485
|
+
);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Sweeps fork data PDAs back to self
|
|
490
|
+
*
|
|
491
|
+
* @param signer
|
|
492
|
+
* @param lastSweepId lastCheckedId returned from the previous sweepForkData() call
|
|
493
|
+
* @returns {number} lastCheckedId that should be passed to the next call of sweepForkData()
|
|
494
|
+
*/
|
|
495
|
+
public async sweepForkData(signer: SolanaSigner, lastSweepId?: number): Promise<number | null> {
|
|
496
|
+
const mainState: any = await this.program.account.mainState.fetch(this.BtcRelayMainState);
|
|
497
|
+
let forkId: number = mainState.forkCounter.toNumber();
|
|
498
|
+
|
|
499
|
+
const txs: SolanaTx[] = [];
|
|
500
|
+
let action = new SolanaAction(signer.getPublicKey(), this.Chain);
|
|
501
|
+
|
|
502
|
+
let lastCheckedId = lastSweepId;
|
|
503
|
+
for(
|
|
504
|
+
let i = lastSweepId==null ? 0 : lastSweepId+1;
|
|
505
|
+
i<=forkId; i++
|
|
506
|
+
) {
|
|
507
|
+
lastCheckedId = i;
|
|
508
|
+
|
|
509
|
+
const accountAddr = this.BtcRelayFork(i, signer.getPublicKey());
|
|
510
|
+
let forkState: any = await this.program.account.forkState.fetchNullable(accountAddr);
|
|
511
|
+
if(forkState==null) continue;
|
|
512
|
+
|
|
513
|
+
this.logger.info("sweepForkData(): sweeping forkId: "+i);
|
|
514
|
+
action.add(await this.CloseForkAccount(signer.getPublicKey(), i));
|
|
515
|
+
|
|
516
|
+
if(action.ixsLength()>=MAX_CLOSE_IX_PER_TX) {
|
|
517
|
+
await action.addToTxs(txs);
|
|
518
|
+
action = new SolanaAction(signer.getPublicKey(), this.Chain);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
if(action.ixsLength()>=MAX_CLOSE_IX_PER_TX) {
|
|
523
|
+
await action.addToTxs(txs);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
if(txs.length>0) {
|
|
527
|
+
const signatures = await this.Chain.sendAndConfirm(signer, txs, true);
|
|
528
|
+
this.logger.info("sweepForkData(): forks swept, signatures: "+signatures.join());
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
return lastCheckedId;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Estimate required synchronization fee (worst case) to synchronize btc relay to the required blockheight
|
|
536
|
+
*
|
|
537
|
+
* @param requiredBlockheight
|
|
538
|
+
* @param feeRate
|
|
539
|
+
*/
|
|
540
|
+
public async estimateSynchronizeFee(requiredBlockheight: number, feeRate?: string): Promise<bigint> {
|
|
541
|
+
const tipData = await this.getTipData();
|
|
542
|
+
const currBlockheight = tipData.blockheight;
|
|
543
|
+
|
|
544
|
+
const blockheightDelta = requiredBlockheight-currBlockheight;
|
|
545
|
+
|
|
546
|
+
if(blockheightDelta<=0) return 0n;
|
|
547
|
+
|
|
548
|
+
const synchronizationFee = BigInt(blockheightDelta) * await this.getFeePerBlock(feeRate);
|
|
549
|
+
this.logger.debug("estimateSynchronizeFee(): required blockheight: "+requiredBlockheight+
|
|
550
|
+
" blockheight delta: "+blockheightDelta+" fee: "+synchronizationFee.toString(10));
|
|
551
|
+
|
|
552
|
+
return synchronizationFee;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Returns fee required (in SOL) to synchronize a single block to btc relay
|
|
557
|
+
*
|
|
558
|
+
* @param feeRate
|
|
559
|
+
*/
|
|
560
|
+
public async getFeePerBlock(feeRate?: string): Promise<bigint> {
|
|
561
|
+
// feeRate = feeRate || await this.getMainFeeRate(null);
|
|
562
|
+
// return BASE_FEE_SOL_PER_BLOCKHEADER.add(this.Fees.getPriorityFee(200000, feeRate, false));
|
|
563
|
+
return 50000n;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Gets fee rate required for submitting blockheaders to the main chain
|
|
568
|
+
*/
|
|
569
|
+
public getMainFeeRate(signer: string | null): Promise<string> {
|
|
570
|
+
const _signer = signer==null ? null : new PublicKey(signer);
|
|
571
|
+
return this.Chain.Fees.getFeeRate(_signer==null ? [this.BtcRelayMainState] : [
|
|
572
|
+
_signer,
|
|
573
|
+
this.BtcRelayMainState
|
|
574
|
+
]);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Gets fee rate required for submitting blockheaders to the specific fork
|
|
579
|
+
*/
|
|
580
|
+
public getForkFeeRate(signer: string, forkId: number): Promise<string> {
|
|
581
|
+
const _signer = new PublicKey(signer);
|
|
582
|
+
return this.Chain.Fees.getFeeRate([
|
|
583
|
+
_signer,
|
|
584
|
+
this.BtcRelayMainState,
|
|
585
|
+
this.BtcRelayFork(forkId, _signer)
|
|
586
|
+
]);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
}
|