diff --git a/bots/src/scripts/fundAccounts.ts b/bots/src/scripts/fundAccounts.ts index d9dc4b4b..59e41bea 100644 --- a/bots/src/scripts/fundAccounts.ts +++ b/bots/src/scripts/fundAccounts.ts @@ -1,89 +1,125 @@ -import { MnemonicKey, MsgSend, Wallet } from "@initia/initia.js"; -import { getConfig } from "config"; -import { sendTx } from "lib/tx"; +import { Coin, MnemonicKey, MsgSend, Wallet } from '@initia/initia.js'; +import { delay } from 'bluebird'; +import { getConfig } from 'config'; +import { sendTx } from 'lib/tx'; +import { TxBot } from 'test/utils/TxBot'; const config = getConfig(); const L1_FUNDER = new Wallet( - config.l1lcd, - new MnemonicKey({ - mnemonic: '' - }) -) + config.l1lcd, + new MnemonicKey({ + mnemonic: + 'recycle sight world spoon leopard shine dizzy before public use jungle either arctic detail hawk output option august hedgehog menu keen night work become' + }) +); const L2_FUNDER = new Wallet( - config.l2lcd, + config.l2lcd, + new MnemonicKey({ + mnemonic: '' + }) +); + +async function fundL1() { + const executor = new Wallet( + config.l1lcd, + new MnemonicKey({ mnemonic: config.EXECUTOR_MNEMONIC }) + ); + const output = new Wallet( + config.l1lcd, + new MnemonicKey({ mnemonic: config.OUTPUT_SUBMITTER_MNEMONIC }) + ); + const batch = new Wallet( + config.l1lcd, + new MnemonicKey({ mnemonic: config.BATCH_SUBMITTER_MNEMONIC }) + ); + const challenger = new Wallet( + config.l1lcd, + new MnemonicKey({ mnemonic: config.CHALLENGER_MNEMONIC }) + ); + const receiver = new Wallet( + config.l1lcd, new MnemonicKey({ mnemonic: '' }) -) - -async function fundL1(){ - const executor = new Wallet(config.l1lcd, new MnemonicKey({mnemonic: config.EXECUTOR_MNEMONIC})) - const output = new Wallet(config.l1lcd, new MnemonicKey({mnemonic: config.OUTPUT_SUBMITTER_MNEMONIC})) - const batch = new Wallet(config.l1lcd, new MnemonicKey({mnemonic: config.BATCH_SUBMITTER_MNEMONIC})) - const challenger = new Wallet(config.l1lcd, new MnemonicKey({mnemonic: config.CHALLENGER_MNEMONIC})) + ); - const sendMsg = [ - new MsgSend( - L1_FUNDER.key.accAddress, - executor.key.accAddress, - "50000000000uinit" - ), - new MsgSend( - L1_FUNDER.key.accAddress, - output.key.accAddress, - "50000000000uinit" - ), - new MsgSend( - L1_FUNDER.key.accAddress, - batch.key.accAddress, - "50000000000uinit" - ), - new MsgSend( - L1_FUNDER.key.accAddress, - challenger.key.accAddress, - "50000000000uinit" - ) - ] - await sendTx(L1_FUNDER, sendMsg); + const sendMsg = [ + new MsgSend( + L1_FUNDER.key.accAddress, + executor.key.accAddress, + '50000000000uinit' + ), + new MsgSend( + L1_FUNDER.key.accAddress, + output.key.accAddress, + '50000000000uinit' + ), + new MsgSend( + L1_FUNDER.key.accAddress, + batch.key.accAddress, + '50000000000uinit' + ), + new MsgSend( + L1_FUNDER.key.accAddress, + challenger.key.accAddress, + '50000000000uinit' + ), + new MsgSend( + L1_FUNDER.key.accAddress, + receiver.key.accAddress, + '100000000000uinit' + ) + ]; + await sendTx(L1_FUNDER, sendMsg); } -async function fundL2(){ - const executor = new Wallet(config.l2lcd, new MnemonicKey({mnemonic: config.EXECUTOR_MNEMONIC})) - const output = new Wallet(config.l2lcd, new MnemonicKey({mnemonic: config.OUTPUT_SUBMITTER_MNEMONIC})) - const batch = new Wallet(config.l2lcd, new MnemonicKey({mnemonic: config.BATCH_SUBMITTER_MNEMONIC})) - const challenger = new Wallet(config.l2lcd, new MnemonicKey({mnemonic: config.CHALLENGER_MNEMONIC})) +async function fundL2() { + const executor = new Wallet( + config.l2lcd, + new MnemonicKey({ mnemonic: config.EXECUTOR_MNEMONIC }) + ); + const output = new Wallet( + config.l2lcd, + new MnemonicKey({ mnemonic: config.OUTPUT_SUBMITTER_MNEMONIC }) + ); + const batch = new Wallet( + config.l2lcd, + new MnemonicKey({ mnemonic: config.BATCH_SUBMITTER_MNEMONIC }) + ); + const challenger = new Wallet( + config.l2lcd, + new MnemonicKey({ mnemonic: config.CHALLENGER_MNEMONIC }) + ); + + const sendMsg = [ + new MsgSend(L2_FUNDER.key.accAddress, executor.key.accAddress, '1umin'), + new MsgSend(L2_FUNDER.key.accAddress, output.key.accAddress, '1umin'), + new MsgSend(L2_FUNDER.key.accAddress, batch.key.accAddress, '1umin'), + new MsgSend(L2_FUNDER.key.accAddress, challenger.key.accAddress, '1umin') + ]; + await sendTx(L2_FUNDER, sendMsg); +} - const sendMsg = [ - new MsgSend( - L2_FUNDER.key.accAddress, - executor.key.accAddress, - "1umin" - ), - new MsgSend( - L2_FUNDER.key.accAddress, - output.key.accAddress, - "1umin" - ), - new MsgSend( - L2_FUNDER.key.accAddress, - batch.key.accAddress, - "1umin" - ), - new MsgSend( - L2_FUNDER.key.accAddress, - challenger.key.accAddress, - "1umin" - ) - ] - await sendTx(L2_FUNDER, sendMsg); +async function startDepositTxBot() { + const txBot = new TxBot(config.BRIDGE_ID); + for (;;) { + const res = await txBot.deposit( + txBot.l1sender, + txBot.l2sender, + new Coin('uinit', 1_000_000) + ); + console.log(`Deposited height ${res.height} ${res.txhash}`); + await delay(10_000); + } } -async function main(){ - // await fundL1(); - // await fundL2(); - console.log('Funded accounts'); +async function main() { + await startDepositTxBot(); + // await fundL1(); + // await fundL2(); + console.log('Funded accounts'); } if (require.main === module) { - main(); -} \ No newline at end of file + main(); +} diff --git a/bots/src/worker/bridgeExecutor/L1Monitor.ts b/bots/src/worker/bridgeExecutor/L1Monitor.ts index 801ed95f..c0cd5748 100644 --- a/bots/src/worker/bridgeExecutor/L1Monitor.ts +++ b/bots/src/worker/bridgeExecutor/L1Monitor.ts @@ -77,13 +77,13 @@ export class L1Monitor extends Monitor { public async handleEvents(manager: EntityManager): Promise { const msgs: Msg[] = []; - const depositEvents = await this.helper.fetchEvents( + const [isEmpty, depositEvents] = await this.helper.fetchEvents( config.l1lcd, this.syncedHeight, 'initiate_token_deposit' ); - if (depositEvents.length === 0) return false; + if (isEmpty) return false; for (const evt of depositEvents) { const attrMap = this.helper.eventsToAttrMap(evt); diff --git a/bots/src/worker/bridgeExecutor/L2Monitor.ts b/bots/src/worker/bridgeExecutor/L2Monitor.ts index 30c9fc5e..90fbc7b4 100644 --- a/bots/src/worker/bridgeExecutor/L2Monitor.ts +++ b/bots/src/worker/bridgeExecutor/L2Monitor.ts @@ -76,13 +76,13 @@ export class L2Monitor extends Monitor { } public async handleEvents(manager: EntityManager): Promise { - const withdrawalEvents = await this.helper.fetchEvents( + const [isEmpty, withdrawalEvents] = await this.helper.fetchEvents( config.l2lcd, this.syncedHeight, 'initiate_token_withdrawal' ); - if (withdrawalEvents.length === 0) return false; + if (isEmpty) return false; for (const evt of withdrawalEvents) { const attrMap = this.helper.eventsToAttrMap(evt); diff --git a/bots/src/worker/bridgeExecutor/Monitor.ts b/bots/src/worker/bridgeExecutor/Monitor.ts index b58bef79..ea364551 100644 --- a/bots/src/worker/bridgeExecutor/Monitor.ts +++ b/bots/src/worker/bridgeExecutor/Monitor.ts @@ -36,9 +36,9 @@ export abstract class Monitor { if (!state) { await this.db .getRepository(StateEntity) - .save({ name: this.name(), height: 1 }); + .save({ name: this.name(), height: 0 }); } - this.syncedHeight = state?.height || 1; + this.syncedHeight = state?.height || 0; this.socket.initialize(); this.isRunning = true; @@ -50,42 +50,46 @@ export abstract class Monitor { this.isRunning = false; } - async fetchBlockchainData() { - const latestHeight = this.socket.latestHeight; - if (!latestHeight || !(latestHeight > this.syncedHeight)) { - return null; - } - return this.rpcClient.getBlockchain( - this.syncedHeight + 1, - Math.min(latestHeight, this.syncedHeight + MAX_BLOCKS) - ); + async handleBlockWithStateUpdate(manager: EntityManager): Promise { + await this.handleBlock(manager); + await manager + .getRepository(StateEntity) + .update({ name: this.name() }, { height: this.syncedHeight }); } public async monitor(): Promise { while (this.isRunning) { try { - const blockchainData = await this.fetchBlockchainData(); + const latestHeight = this.socket.latestHeight; + if (!latestHeight || !(latestHeight > this.syncedHeight)) continue; + + const blockchainData = await this.rpcClient.getBlockchain( + this.syncedHeight + 1, + // cap the query to fetch 20 blocks at maximum + // DO NOT CHANGE THIS, hard limit is 20 in cometbft. + Math.min(latestHeight, this.syncedHeight + 20) + ); if (blockchainData === null) continue; await this.db.transaction(async (manager: EntityManager) => { for (const metadata of blockchainData?.block_metas.reverse()) { - const nextHeight = this.syncedHeight + 1; - if (nextHeight !== parseInt(metadata.header.height)) { + this.syncedHeight++; + if (this.syncedHeight !== parseInt(metadata.header.height)) { throw new Error( - `expected block meta is the height ${nextHeight}, but got ${metadata.header.height}` + `expected block meta is the height ${this.syncedHeight}, but got ${metadata.header.height}` ); } - if (nextHeight % 10 === 0) { - this.logger.info(`${this.name()} height ${nextHeight}`); + if (this.syncedHeight % 10 === 0) { + this.logger.info(`${this.name()} height ${this.syncedHeight}`); } - - if (parseInt(metadata.num_txs) !== 0) { - await this.handleBlock(manager); - this.syncedHeight++; - await manager - .getRepository(StateEntity) - .update({ name: this.name() }, { height: this.syncedHeight }); + + // every block except the first block has at least one tx (skip blockSDK). + if ( + (this.syncedHeight !== 1 && parseInt(metadata.num_txs) === 1) || + (this.syncedHeight === 1 && parseInt(metadata.num_txs) === 0) + ) { + await this.handleBlockWithStateUpdate(manager); continue; } @@ -101,15 +105,8 @@ export abstract class Monitor { break; } this.retryNum = 0; - await this.handleBlock(manager); - - this.syncedHeight++; - await manager - .getRepository(StateEntity) - .update({ name: this.name() }, { height: this.syncedHeight }); - // add delay to prevent spamming - await Bluebird.Promise.delay(INTERVAL_MONITOR); + await this.handleBlockWithStateUpdate(manager); } }); } catch (err) { diff --git a/bots/src/worker/bridgeExecutor/MonitorHelper.ts b/bots/src/worker/bridgeExecutor/MonitorHelper.ts index ecbbc2ad..bcc8913d 100644 --- a/bots/src/worker/bridgeExecutor/MonitorHelper.ts +++ b/bots/src/worker/bridgeExecutor/MonitorHelper.ts @@ -98,15 +98,23 @@ class MonitorHelper { lcd: LCDClient, height: number, eventType: string - ): Promise { + ): Promise<[boolean, any[]]> { const searchRes = await lcd.tx.search({ events: [{ key: 'tx.height', value: height.toString() }] }); - return searchRes.txs - .flatMap((tx) => tx.logs ?? []) - .flatMap((log) => log.events) - .filter((evt) => evt.type === eventType); + const extractEvents = (txs) => + txs + .flatMap((tx) => tx.logs ?? []) + .flatMap((log) => log.events) + .filter((evt) => evt.type === eventType); + + const txsToProcess = height === 1 ? searchRes.txs : searchRes.txs.slice(1); + + const isEmpty = txsToProcess.length === 0; + const events = extractEvents(txsToProcess); + + return [isEmpty, events]; } public eventsToAttrMap(event: any): { [key: string]: string } { diff --git a/bots/src/worker/bridgeExecutor/index.ts b/bots/src/worker/bridgeExecutor/index.ts index 7e844051..5515bc64 100644 --- a/bots/src/worker/bridgeExecutor/index.ts +++ b/bots/src/worker/bridgeExecutor/index.ts @@ -19,12 +19,12 @@ async function runBot(): Promise { new RPCSocket(config.L1_RPC_URI, 10000, logger), new RPCClient(config.L1_RPC_URI, logger), logger - ), - new L2Monitor( - new RPCSocket(config.L2_RPC_URI, 10000, logger), - new RPCClient(config.L2_RPC_URI, logger), - logger ) + // new L2Monitor( + // new RPCSocket(config.L2_RPC_URI, 10000, logger), + // new RPCClient(config.L2_RPC_URI, logger), + // logger + // ) ]; try { await Promise.all( diff --git a/bots/src/worker/challenger/L1Monitor.ts b/bots/src/worker/challenger/L1Monitor.ts index 681e05c3..1e4aca2b 100644 --- a/bots/src/worker/challenger/L1Monitor.ts +++ b/bots/src/worker/challenger/L1Monitor.ts @@ -62,20 +62,19 @@ export class L1Monitor extends Monitor { } public async handleEvents(manager: EntityManager): Promise { - const depositEvents = await this.helper.fetchEvents( + const [isEmpty, depositEvents] = await this.helper.fetchEvents( config.l1lcd, this.syncedHeight, 'initiate_token_deposit' ); - const withdrawalEvents = await this.helper.fetchEvents( + const [_isEmpty, withdrawalEvents] = await this.helper.fetchEvents( config.l1lcd, this.syncedHeight, 'finalize_token_withdrawal' ); - if (depositEvents.length === 0 && withdrawalEvents.length === 0) - return false; + if (isEmpty) return false; for (const evt of depositEvents) { const attrMap = this.helper.eventsToAttrMap(evt); diff --git a/bots/src/worker/challenger/L2Monitor.ts b/bots/src/worker/challenger/L2Monitor.ts index cb497163..ff17d6da 100644 --- a/bots/src/worker/challenger/L2Monitor.ts +++ b/bots/src/worker/challenger/L2Monitor.ts @@ -130,28 +130,32 @@ export class L2Monitor extends Monitor { await manager.getRepository(ChallengerFinalizeDepositTxEntity).save(entity); } - public async handleEvents(manager: EntityManager): Promise { - const withdrawalEvents = await this.helper.fetchEvents( + public async handleEvents(manager: EntityManager): Promise { + const [isEmpty, withdrawalEvents] = await this.helper.fetchEvents( config.l2lcd, this.syncedHeight, 'initiate_token_withdrawal' ); - for (const evt of withdrawalEvents) { - const attrMap = this.helper.eventsToAttrMap(evt); - await this.handleInitiateTokenWithdrawalEvent(manager, attrMap); - } - - const depositEvents = await this.helper.fetchEvents( + const [_isEmpty, depositEvents] = await this.helper.fetchEvents( config.l2lcd, this.syncedHeight, 'finalize_token_deposit' ); + if (isEmpty) return false; + + for (const evt of withdrawalEvents) { + const attrMap = this.helper.eventsToAttrMap(evt); + await this.handleInitiateTokenWithdrawalEvent(manager, attrMap); + } + for (const evt of depositEvents) { const attrMap = this.helper.eventsToAttrMap(evt); await this.handleFinalizeTokenDepositEvent(manager, attrMap); } + + return true; } private async saveMerkleRootAndProof(