diff --git a/Cargo.lock b/Cargo.lock index fc76be14d80b..b0af1abad750 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2745,26 +2745,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "exex-optimism" -version = "0.0.0" -dependencies = [ - "alloy-sol-types", - "eyre", - "futures", - "itertools 0.12.1", - "reth", - "reth-exex", - "reth-node-api", - "reth-node-core", - "reth-node-ethereum", - "reth-primitives", - "reth-provider", - "reth-tracing", - "rusqlite", - "tokio", -] - [[package]] name = "eyre" version = "0.6.12" @@ -5085,6 +5065,26 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +[[package]] +name = "op-bridge" +version = "0.0.0" +dependencies = [ + "alloy-sol-types", + "eyre", + "futures", + "itertools 0.12.1", + "reth", + "reth-exex", + "reth-node-api", + "reth-node-core", + "reth-node-ethereum", + "reth-primitives", + "reth-provider", + "reth-tracing", + "rusqlite", + "tokio", +] + [[package]] name = "opaque-debug" version = "0.3.1" diff --git a/Cargo.toml b/Cargo.toml index fede2d290dbd..34c9740a2ffd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,7 +77,7 @@ members = [ "examples/trace-transaction-cli/", "examples/polygon-p2p/", "examples/custom-inspector/", - "examples/exex/optimism/", + "examples/exex/op-bridge/", "testing/ef-tests/", ] default-members = ["bin/reth"] diff --git a/examples/exex/optimism/Cargo.toml b/examples/exex/optimism/Cargo.toml deleted file mode 100644 index e4c1d4a43cdb..000000000000 --- a/examples/exex/optimism/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "exex-optimism" -version = "0.0.0" -publish = false -edition.workspace = true -license.workspace = true - -[dependencies] -reth.workspace = true -reth-exex.workspace = true -reth-node-api.workspace = true -reth-node-core.workspace = true -reth-node-ethereum.workspace = true -reth-primitives.workspace = true -reth-provider.workspace = true -reth-tracing.workspace = true - -eyre.workspace = true -tokio.workspace = true -futures.workspace = true -alloy-sol-types = { workspace = true, features = ["json"] } -itertools.workspace = true -rusqlite = { version = "0.31.0", features = ["bundled"] } diff --git a/examples/exex/optimism/l1_standard_bridge_abi.json b/examples/exex/optimism/l1_standard_bridge_abi.json deleted file mode 100644 index 4ae6406f0793..000000000000 --- a/examples/exex/optimism/l1_standard_bridge_abi.json +++ /dev/null @@ -1,664 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "address payable", - "name": "_messenger", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "localToken", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "remoteToken", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "extraData", - "type": "bytes" - } - ], - "name": "ERC20BridgeFinalized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "localToken", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "remoteToken", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "extraData", - "type": "bytes" - } - ], - "name": "ERC20BridgeInitiated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "l1Token", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "l2Token", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "extraData", - "type": "bytes" - } - ], - "name": "ERC20DepositInitiated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "l1Token", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "l2Token", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "extraData", - "type": "bytes" - } - ], - "name": "ERC20WithdrawalFinalized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "extraData", - "type": "bytes" - } - ], - "name": "ETHBridgeFinalized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "extraData", - "type": "bytes" - } - ], - "name": "ETHBridgeInitiated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "extraData", - "type": "bytes" - } - ], - "name": "ETHDepositInitiated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "extraData", - "type": "bytes" - } - ], - "name": "ETHWithdrawalFinalized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint8", - "name": "version", - "type": "uint8" - } - ], - "name": "Initialized", - "type": "event" - }, - { - "inputs": [], - "name": "MESSENGER", - "outputs": [ - { - "internalType": "contract CrossDomainMessenger", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "OTHER_BRIDGE", - "outputs": [ - { - "internalType": "contract StandardBridge", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_localToken", - "type": "address" - }, - { - "internalType": "address", - "name": "_remoteToken", - "type": "address" - }, - { "internalType": "uint256", "name": "_amount", "type": "uint256" }, - { - "internalType": "uint32", - "name": "_minGasLimit", - "type": "uint32" - }, - { "internalType": "bytes", "name": "_extraData", "type": "bytes" } - ], - "name": "bridgeERC20", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_localToken", - "type": "address" - }, - { - "internalType": "address", - "name": "_remoteToken", - "type": "address" - }, - { "internalType": "address", "name": "_to", "type": "address" }, - { "internalType": "uint256", "name": "_amount", "type": "uint256" }, - { - "internalType": "uint32", - "name": "_minGasLimit", - "type": "uint32" - }, - { "internalType": "bytes", "name": "_extraData", "type": "bytes" } - ], - "name": "bridgeERC20To", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint32", - "name": "_minGasLimit", - "type": "uint32" - }, - { "internalType": "bytes", "name": "_extraData", "type": "bytes" } - ], - "name": "bridgeETH", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "_to", "type": "address" }, - { - "internalType": "uint32", - "name": "_minGasLimit", - "type": "uint32" - }, - { "internalType": "bytes", "name": "_extraData", "type": "bytes" } - ], - "name": "bridgeETHTo", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_l1Token", - "type": "address" - }, - { - "internalType": "address", - "name": "_l2Token", - "type": "address" - }, - { "internalType": "uint256", "name": "_amount", "type": "uint256" }, - { - "internalType": "uint32", - "name": "_minGasLimit", - "type": "uint32" - }, - { "internalType": "bytes", "name": "_extraData", "type": "bytes" } - ], - "name": "depositERC20", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_l1Token", - "type": "address" - }, - { - "internalType": "address", - "name": "_l2Token", - "type": "address" - }, - { "internalType": "address", "name": "_to", "type": "address" }, - { "internalType": "uint256", "name": "_amount", "type": "uint256" }, - { - "internalType": "uint32", - "name": "_minGasLimit", - "type": "uint32" - }, - { "internalType": "bytes", "name": "_extraData", "type": "bytes" } - ], - "name": "depositERC20To", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint32", - "name": "_minGasLimit", - "type": "uint32" - }, - { "internalType": "bytes", "name": "_extraData", "type": "bytes" } - ], - "name": "depositETH", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "_to", "type": "address" }, - { - "internalType": "uint32", - "name": "_minGasLimit", - "type": "uint32" - }, - { "internalType": "bytes", "name": "_extraData", "type": "bytes" } - ], - "name": "depositETHTo", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "", "type": "address" }, - { "internalType": "address", "name": "", "type": "address" } - ], - "name": "deposits", - "outputs": [ - { "internalType": "uint256", "name": "", "type": "uint256" } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_localToken", - "type": "address" - }, - { - "internalType": "address", - "name": "_remoteToken", - "type": "address" - }, - { "internalType": "address", "name": "_from", "type": "address" }, - { "internalType": "address", "name": "_to", "type": "address" }, - { "internalType": "uint256", "name": "_amount", "type": "uint256" }, - { "internalType": "bytes", "name": "_extraData", "type": "bytes" } - ], - "name": "finalizeBridgeERC20", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "_from", "type": "address" }, - { "internalType": "address", "name": "_to", "type": "address" }, - { "internalType": "uint256", "name": "_amount", "type": "uint256" }, - { "internalType": "bytes", "name": "_extraData", "type": "bytes" } - ], - "name": "finalizeBridgeETH", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_l1Token", - "type": "address" - }, - { - "internalType": "address", - "name": "_l2Token", - "type": "address" - }, - { "internalType": "address", "name": "_from", "type": "address" }, - { "internalType": "address", "name": "_to", "type": "address" }, - { "internalType": "uint256", "name": "_amount", "type": "uint256" }, - { "internalType": "bytes", "name": "_extraData", "type": "bytes" } - ], - "name": "finalizeERC20Withdrawal", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "_from", "type": "address" }, - { "internalType": "address", "name": "_to", "type": "address" }, - { "internalType": "uint256", "name": "_amount", "type": "uint256" }, - { "internalType": "bytes", "name": "_extraData", "type": "bytes" } - ], - "name": "finalizeETHWithdrawal", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract SuperchainConfig", - "name": "_superchainConfig", - "type": "address" - } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "l2TokenBridge", - "outputs": [ - { "internalType": "address", "name": "", "type": "address" } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "messenger", - "outputs": [ - { - "internalType": "contract CrossDomainMessenger", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "otherBridge", - "outputs": [ - { - "internalType": "contract StandardBridge", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "paused", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "superchainConfig", - "outputs": [ - { - "internalType": "contract SuperchainConfig", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "version", - "outputs": [{ "internalType": "string", "name": "", "type": "string" }], - "stateMutability": "view", - "type": "function" - }, - { "stateMutability": "payable", "type": "receive" } -] diff --git a/examples/exex/optimism/src/main.rs b/examples/exex/optimism/src/main.rs deleted file mode 100644 index d1a8b9ce7889..000000000000 --- a/examples/exex/optimism/src/main.rs +++ /dev/null @@ -1,230 +0,0 @@ -use std::{ - pin::Pin, - task::{ready, Context, Poll}, -}; - -use alloy_sol_types::{sol, SolEventInterface}; -use futures::Future; -use reth::builder::FullNodeTypes; -use reth_exex::{ExExContext, ExExEvent}; -use reth_node_ethereum::EthereumNode; -use reth_primitives::{Log, SealedBlockWithSenders, TransactionSigned}; -use reth_provider::Chain; -use reth_tracing::tracing::info; -use rusqlite::Connection; - -sol!(L1StandardBridge, "l1_standard_bridge_abi.json"); -use crate::L1StandardBridge::{ETHBridgeFinalized, ETHBridgeInitiated, L1StandardBridgeEvents}; - -struct OptimismExEx { - ctx: ExExContext, - connection: Connection, -} - -impl OptimismExEx { - fn new(ctx: ExExContext, connection: Connection) -> eyre::Result { - // Create deposits and withdrawals tables - connection.execute( - r#" - CREATE TABLE IF NOT EXISTS deposits ( - id INTEGER PRIMARY KEY, - block_number INTEGER NOT NULL, - tx_hash TEXT NOT NULL UNIQUE, - contract_address TEXT NOT NULL, - "from" TEXT NOT NULL, - "to" TEXT NOT NULL, - amount TEXT NOT NULL - ); - "#, - (), - )?; - connection.execute( - r#" - CREATE TABLE IF NOT EXISTS withdrawals ( - id INTEGER PRIMARY KEY, - block_number INTEGER NOT NULL, - tx_hash TEXT NOT NULL UNIQUE, - contract_address TEXT NOT NULL, - "from" TEXT NOT NULL, - "to" TEXT NOT NULL, - amount TEXT NOT NULL - ); - "#, - (), - )?; - - // Create a bridge contract addresses table and insert known ones with their respective - // names - connection.execute( - r#" - CREATE TABLE IF NOT EXISTS contracts ( - id INTEGER PRIMARY KEY, - address TEXT NOT NULL UNIQUE, - name TEXT NOT NULL - ); - "#, - (), - )?; - connection.execute( - r#" - INSERT OR IGNORE INTO contracts (address, name) - VALUES - ('0x3154Cf16ccdb4C6d922629664174b904d80F2C35', 'Base'), - ('0x3a05E5d33d7Ab3864D53aaEc93c8301C1Fa49115', 'Blast'), - ('0x697402166Fbf2F22E970df8a6486Ef171dbfc524', 'Blast'), - ('0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1', 'Optimism'), - ('0x735aDBbE72226BD52e818E7181953f42E3b0FF21', 'Mode'), - ('0x3B95bC951EE0f553ba487327278cAc44f29715E5', 'Manta'); - "#, - (), - )?; - - info!("Initialized database tables"); - - Ok(Self { ctx, connection }) - } -} - -impl Future for OptimismExEx { - type Output = eyre::Result<()>; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.get_mut(); - - // Process all new chain state notifications until there are no more - while let Some(notification) = ready!(this.ctx.notifications.poll_recv(cx)) { - if let Some(reverted_chain) = notification.reverted() { - let events = decode_chain_into_events(&reverted_chain); - - let mut deposits = 0; - let mut withdrawals = 0; - - for (_, tx, _, event) in events { - match event { - L1StandardBridgeEvents::ETHBridgeInitiated(ETHBridgeInitiated { - .. - }) => { - deposits += this.connection.execute( - "DELETE FROM deposits WHERE tx_hash = ?;", - (tx.hash().to_string(),), - )?; - } - L1StandardBridgeEvents::ETHBridgeFinalized(ETHBridgeFinalized { - .. - }) => { - withdrawals += this.connection.execute( - "DELETE FROM withdrawals WHERE tx_hash = ?;", - (tx.hash().to_string(),), - )?; - } - _ => continue, - }; - } - - info!(%deposits, %withdrawals, "Reverted chain events"); - } - - let committed = notification.committed(); - let events = decode_chain_into_events(&committed); - - let mut deposits = 0; - let mut withdrawals = 0; - - for (block, tx, log, event) in events { - match event { - L1StandardBridgeEvents::ETHBridgeInitiated(ETHBridgeInitiated { - amount, - from, - to, - .. - }) => { - deposits += this.connection.execute( - r#" - INSERT INTO deposits (block_number, tx_hash, contract_address, "from", "to", amount) - VALUES (?, ?, ?, ?, ?, ?) - "#, - ( - block.number, - tx.hash().to_string(), - log.address.to_string(), - from.to_string(), - to.to_string(), - amount.to_string(), - ), - )?; - } - L1StandardBridgeEvents::ETHBridgeFinalized(ETHBridgeFinalized { - amount, - from, - to, - .. - }) => { - withdrawals += this.connection.execute( - r#" - INSERT INTO withdrawals (block_number, tx_hash, contract_address, "from", "to", amount) - VALUES (?, ?, ?, ?, ?, ?) - "#, - ( - block.number, - tx.hash().to_string(), - log.address.to_string(), - from.to_string(), - to.to_string(), - amount.to_string(), - ), - )?; - } - _ => continue, - }; - } - - info!(%deposits, %withdrawals, "Committed chain events"); - - // Send a finished height event, signaling the node that we don't need any blocks below - // this height anymore - this.ctx.events.send(ExExEvent::FinishedHeight(notification.tip().number))?; - } - - Poll::Pending - } -} - -fn decode_chain_into_events( - chain: &Chain, -) -> impl Iterator -{ - chain - // Get all blocks and receipts - .blocks_and_receipts() - // Get all receipts - .flat_map(|(block, receipts)| { - block - .body - .iter() - .zip(receipts.iter().flatten()) - .map(move |(tx, receipt)| (block, tx, receipt)) - }) - // Get all logs - .flat_map(|(block, tx, receipt)| receipt.logs.iter().map(move |log| (block, tx, log))) - // Decode and filter bridge events - .filter_map(|(block, tx, log)| { - L1StandardBridgeEvents::decode_raw_log(&log.topics, &log.data, true) - .ok() - .map(|event| (block, tx, log, event)) - }) -} - -fn main() -> eyre::Result<()> { - reth::cli::Cli::parse_args().run(|builder, _| async move { - let handle = builder - .node(EthereumNode::default()) - .install_exex("Optimism", move |ctx| async { - let connection = Connection::open("optimism.db")?; - OptimismExEx::new(ctx, connection) - }) - .launch() - .await?; - - handle.wait_for_node_exit().await - }) -}