diff --git a/lib/src/consts.rs b/lib/src/consts.rs index eb029d594..2903ea6fa 100644 --- a/lib/src/consts.rs +++ b/lib/src/consts.rs @@ -82,7 +82,8 @@ pub static TKO_MAINNET_CHAIN_SPEC: Lazy = Lazy::new(|| { } }); - +#[cfg(feature = "taiko")] +pub use crate::taiko::consts::testnet::*; /// The Optimism mainnet specification. #[cfg(feature = "optimism")] diff --git a/lib/src/host/provider/cached_rpc_provider.rs b/lib/src/host/provider/cached_rpc_provider.rs index 111391d97..b58508d69 100644 --- a/lib/src/host/provider/cached_rpc_provider.rs +++ b/lib/src/host/provider/cached_rpc_provider.rs @@ -141,4 +141,41 @@ impl Provider for CachedRpcProvider { Ok(out) } + + #[cfg(feature = "taiko")] + fn get_logs(&mut self, query: &LogsQuery) -> Result> { + let cache_out = self.cache.get_logs(query); + if cache_out.is_ok() { + return cache_out; + } + + let out = self.rpc.get_logs(query)?; + self.cache.insert_logs(query.clone(), out); + + Ok(out) + } + + #[cfg(feature = "taiko")] + fn get_transaction(&mut self, query: &super::TxQuery) -> Result { + let mut cache_out = self.cache.get_transaction(query); + if cache_out.is_ok() { + return cache_out; + } + + // Search cached block for target Tx + let cache_block_out = self.cache + .get_full_block(&BlockQuery {block_no: query.block_no}) + .map(|b| b.transactions.iter().filter(|tx| tx.hash == query.tx_hash).collect::>()) + .map(|txs| txs.pop()); + if let Ok(tx_op) = cache_block_out { + if let Some(tx) = tx_op { + return Ok(tx) + } + } + + let out = self.rpc.get_transaction(query)?; + self.cache.insert_transaction(query.clone(), out); + + Ok(out) + } } diff --git a/lib/src/host/provider/file_provider.rs b/lib/src/host/provider/file_provider.rs index 285bd2db2..84a79bd24 100644 --- a/lib/src/host/provider/file_provider.rs +++ b/lib/src/host/provider/file_provider.rs @@ -19,14 +19,17 @@ use std::{ path::{Path, PathBuf}, }; +use ethers_core::types::Log; use anyhow::{anyhow, Result}; -use ethers_core::types::{ +use ethers_core::{abi::Hash, types::{ Block, Bytes, EIP1186ProofResponse, Transaction, TransactionReceipt, H256, U256, -}; +}}; use serde::{Deserialize, Serialize}; use serde_with::serde_as; use super::{AccountQuery, BlockQuery, MutProvider, ProofQuery, Provider, StorageQuery}; +#[cfg(feature = "taiko")] +use super::{LogsQuery, TxQuery}; #[serde_as] #[derive(Deserialize, Serialize)] @@ -52,6 +55,13 @@ pub struct FileProvider { code: HashMap, #[serde_as(as = "Vec<(_, _)>")] storage: HashMap, + + #[cfg(feature = "taiko")] + #[serde_as(as = "Vec<(_, _)>")] + logs: HashMap>, + #[cfg(feature = "taiko")] + #[serde_as(as = "Vec<(_, _)>")] + transactions: HashMap, } impl FileProvider { @@ -67,6 +77,10 @@ impl FileProvider { balance: HashMap::new(), code: HashMap::new(), storage: HashMap::new(), + #[cfg(feature = "taiko")] + logs: HashMap::new(), + #[cfg(feature = "taiko")] + transactions: HashMap::new(), } } @@ -156,6 +170,22 @@ impl Provider for FileProvider { None => Err(anyhow!("No data for {:?}", query)), } } + + #[cfg(feature = "taiko")] + fn get_logs(&mut self, query: &LogsQuery) -> Result> { + match self.logs.get(query) { + Some(val) => Ok(val.clone()), + None => Err(anyhow!("No data for {:?}", query)), + } + } + + #[cfg(feature = "taiko")] + fn get_transaction(&mut self, query: &TxQuery) -> Result { + match self.transactions.get(query) { + Some(val) => Ok(val.clone()), + None => Err(anyhow!("No data for {:?}", query)), + } + } } impl MutProvider for FileProvider { @@ -198,4 +228,16 @@ impl MutProvider for FileProvider { self.storage.insert(query, val); self.dirty = true; } + + #[cfg(feature = "taiko")] + fn insert_logs(&mut self, query: LogsQuery, val: Vec) { + self.storage.insert(query, val); + self.dirty = true; + } + + #[cfg(feature = "taiko")] + fn insert_transaction(&mut self, query: super::TxQuery, val: Transaction) { + self.transaction.insert(query, val); + self.dirty = true; + } } diff --git a/lib/src/host/provider/mod.rs b/lib/src/host/provider/mod.rs index d62034170..1a4617ea7 100644 --- a/lib/src/host/provider/mod.rs +++ b/lib/src/host/provider/mod.rs @@ -14,11 +14,16 @@ use std::{collections::BTreeSet, path::PathBuf}; +use alloy_primitives::TxHash; +use ethers_core::types::{Log, H256}; +use alloy_sol_types::{sol_data::Uint, SolEvent}; use anyhow::{anyhow, Context, Result}; use ethers_core::types::{ - Block, Bytes, EIP1186ProofResponse, Transaction, TransactionReceipt, H160, H256, U256, + Block, Bytes, EIP1186ProofResponse, Transaction, TransactionReceipt, H160, U256, }; use serde::{Deserialize, Serialize}; +#[cfg(feature = "taiko")] +use crate::taiko::BlockProposed; pub mod cached_rpc_provider; pub mod file_provider; @@ -49,6 +54,21 @@ pub struct StorageQuery { pub index: H256, } +#[cfg(feature = "taiko")] +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct LogsQuery { + pub address: H160, + pub from_block: u64, + pub to_block: u64, +} + +#[cfg(feature = "taiko")] +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct TxQuery { + pub tx_hash: H256, + pub block_no: Option, +} + pub trait Provider: Send { fn save(&self) -> Result<()>; @@ -60,6 +80,10 @@ pub trait Provider: Send { fn get_balance(&mut self, query: &AccountQuery) -> Result; fn get_code(&mut self, query: &AccountQuery) -> Result; fn get_storage(&mut self, query: &StorageQuery) -> Result; + #[cfg(feature = "taiko")] + fn get_logs(&mut self, query: &LogsQuery) -> Result>; + #[cfg(feature = "taiko")] + fn get_transaction(&mut self, query: &TxQuery) -> Result; } pub trait MutProvider: Provider { @@ -71,6 +95,10 @@ pub trait MutProvider: Provider { fn insert_balance(&mut self, query: AccountQuery, val: U256); fn insert_code(&mut self, query: AccountQuery, val: Bytes); fn insert_storage(&mut self, query: StorageQuery, val: H256); + #[cfg(feature = "taiko")] + fn insert_logs(&mut self, query: LogsQuery, val: Vec); + #[cfg(feature = "taiko")] + fn insert_transaction(&mut self, query: TxQuery, val: Transaction); } pub fn new_file_provider(file_path: PathBuf) -> Result> { @@ -103,3 +131,51 @@ pub fn new_provider( (None, None) => Err(anyhow!("No cache_path or rpc_url given")), } } + + +use alloy_sol_types::{TopicList}; +use zeth_primitives::ethers::from_ethers_h256; + +#[cfg(feature = "taiko")] +impl dyn Provider { + fn filter_block_proposal( + &mut self, + l1_contract: H160, + l1_block_no: u64, + l2_block_no: u64, + ) -> Result<(Transaction, BlockProposed)>{ + let logs = self.get_logs( + &LogsQuery { + address: l1_contract, + from_block: l1_block_no, + to_block: l1_block_no, + } + )?; + let res = logs + .iter() + .filter(|log| log.topics().len() == <::TopicList as TopicList>::COUNT) + .filter(|log| from_ethers_h256(log.topics()[0]) == BlockProposed::SIGNATURE_HASH) + .map(|log| { + let block_proposed = ::decode_log( + alloy_primitives::Log {address: log.address, data: log.data }, + true + ) + .with_context(|| anyhow!("Decode log failed for l1_block_no {}", l1_block_no))?; + (log.block_number, log.transaction_hash, block_proposed) + }) + .filter(|(block_no, tx_hash, event)| event.blockId == revm::primitives::U256::from(l2_block_no)) + .collect::>(); + + let (block_no, tx_hash, event) = res.pop() + .with_context(|| anyhow!("Cannot find BlockProposed event for {}" l2_block_no))?; + + let tx = self + .get_transaction(& TxQuery { + tx_hash: tx_hash.unwrap(), + block_no: block_no.map(|b| b.as_u64()) + }) + .with_context(|| anyhow!("Cannot find BlockProposed Tx {:?}", tx_hash))?; + + Ok((tx, event.data)) + } +} \ No newline at end of file diff --git a/lib/src/host/provider/rpc_provider.rs b/lib/src/host/provider/rpc_provider.rs index 2644e1f2a..68c35281f 100644 --- a/lib/src/host/provider/rpc_provider.rs +++ b/lib/src/host/provider/rpc_provider.rs @@ -14,7 +14,7 @@ use anyhow::{anyhow, Result}; use ethers_core::types::{ - Block, Bytes, EIP1186ProofResponse, Transaction, TransactionReceipt, H256, U256, + Block, Bytes, EIP1186ProofResponse, Filter, Transaction, TransactionReceipt, H256, U256 }; use ethers_providers::{Http, Middleware, RetryClient}; use log::info; @@ -143,4 +143,29 @@ impl Provider for RpcProvider { Ok(out) } + + #[cfg(feature = "taiko")] + fn get_logs(&mut self, query: &LogsQuery) -> Result> { + info!("Querying RPC for logs: {:?}", query); + + let out = self.tokio_handle.block_on(async { + self.http_client + .get_logs(&Filter::new().address(query.l1_contract).from_block(query.l1_block_no).to_block(query.l1_block_no)) + .await + })?; + + Ok(out) + } + + #[cfg(feature = "taiko")] + fn get_transaction(&mut self, query: &super::TxQuery) -> Result { + info!("Querying RPC for tx: {:?}", query); + let out = self.tokio_handle.block_on(async { + self.http_client + .get_transaction(query.tx_hash) + .await + })?; + + Ok(out) + } } diff --git a/lib/src/lib.rs b/lib/src/lib.rs index c5d58b534..470f186e3 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -23,6 +23,8 @@ pub mod consts; pub mod input; pub mod mem_db; pub mod optimism; + +#[cfg(feature = "taiko")] pub mod taiko; mod utils; diff --git a/lib/src/taiko/protocol_instance.rs b/lib/src/taiko/protocol_instance.rs index 33ac7a8dd..ed067f1b3 100644 --- a/lib/src/taiko/protocol_instance.rs +++ b/lib/src/taiko/protocol_instance.rs @@ -2,6 +2,7 @@ use alloy_sol_types::SolValue; use anyhow::{Result, anyhow}; use alloy_primitives::{Address, TxHash, B256}; use revm::primitives::SpecId; +use serde_json::to_string; use zeth_primitives::{block::Header, ethers::{from_ethers_h256, from_ethers_u256}, keccak::keccak, transactions::EthereumTransaction}; use crate::consts::TKO_MAINNET_CHAIN_SPEC; @@ -105,7 +106,7 @@ pub fn assemble_protocol_instance(sys: &TaikoSystemInfo, header: &Header) -> Res }, prover: sys.prover, }; - verify(header, &mut pi, sys)?; + verify(sys, header, &mut pi)?; Ok(pi) } @@ -119,8 +120,7 @@ pub fn verify(sys: &TaikoSystemInfo, header: &Header, pi: &mut ProtocolInstance) pi.block_metadata )); } - // println!("Protocol instance Transition: {:?}", pi.transition); - // check the block hash + // Check the block hash if Some(header.hash()) != sys.l2_fini_block.hash.map(from_ethers_h256) { let txs: Vec = sys .l2_fini_block @@ -129,9 +129,9 @@ pub fn verify(sys: &TaikoSystemInfo, header: &Header, pi: &mut ProtocolInstance) .filter_map(|tx| tx.clone().try_into().ok()) .collect(); return Err(anyhow!( - "block hash mismatch, expected: {}, got: {}", - to_string(&txs).unwrap_or_default(), - to_string(&header.transactions).unwrap_or_default(), + "block hash mismatch, expected: {}, got: {}", + header.hash(), + sys.l2_fini_block.hash.map(from_ethers_h256).unwrap() )); } diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index e0dbb2c95..65d664c57 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -30,5 +30,7 @@ hex-literal = "0.4" serde_json = "1.0" [features] +default = ["taiko"] +taiko = [] ethers = ["dep:ethers-core"] revm = ["dep:revm-primitives"]