From ac911045dae6088ae9d6fe835defa4c054e8322b Mon Sep 17 00:00:00 2001 From: Enrique Ortiz Date: Fri, 17 Nov 2023 13:03:33 -0400 Subject: [PATCH] feat: use TransportError on Anvil --- crates/anvil/src/config.rs | 25 +--- crates/anvil/src/eth/api.rs | 25 ++-- crates/anvil/src/eth/backend/fork.rs | 210 ++++++++------------------- crates/anvil/src/eth/error.rs | 7 + 4 files changed, 89 insertions(+), 178 deletions(-) diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index c4e221db9cf8..b498e56974c4 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -18,6 +18,7 @@ use crate::{ use alloy_primitives::U64; use alloy_providers::provider::TempProvider; use alloy_rpc_types::BlockNumberOrTag; +use alloy_transport::TransportError; use anvil_server::ServerConfig; use ethers::{ core::k256::ecdsa::SigningKey, @@ -929,7 +930,6 @@ impl NodeConfig { provider .get_chain_id() .await - .success() .expect("Failed to fetch network chain id") .to::(), ); @@ -955,13 +955,12 @@ impl NodeConfig { let block = provider .get_block(BlockNumberOrTag::Number(U64::from(fork_block_number)).into(), false) .await - .success() .expect("Failed to get fork block"); let block = if let Some(block) = block { block } else { - if let Some(latest_block) = provider.get_block_number().await.success() { + if let Ok(latest_block) = provider.get_block_number().await { let mut message = format!( "Failed to get block for block number: {fork_block_number}\n\ latest block number: {latest_block}" @@ -1021,7 +1020,7 @@ latest block number: {latest_block}" // use remote gas price if self.gas_price.is_none() { - if let Some(gas_price) = provider.get_gas_price().await.success() { + if let Ok(gas_price) = provider.get_gas_price().await { self.gas_price = Some(gas_price.to_ethers()); fees.set_gas_price(gas_price.to_ethers()); } @@ -1035,7 +1034,7 @@ latest block number: {latest_block}" let chain_id = if let Some(fork_chain_id) = fork_chain_id { fork_chain_id.as_u64() } else { - provider.get_chain_id().await.success().unwrap().to::() + provider.get_chain_id().await.unwrap().to::() }; // need to update the dev signers and env with the chain id @@ -1188,23 +1187,13 @@ pub fn anvil_tmp_dir() -> Option { /// /// This fetches the "latest" block and checks whether the `Block` is fully populated (`hash` field /// is present). This prevents edge cases where anvil forks the "latest" block but `eth_getBlockByNumber` still returns a pending block, -async fn find_latest_fork_block(provider: P) -> Result { - let mut num = provider - .get_block_number() - .await - .success() - .ok_or_else(|| eyre::eyre!("Could not get block number"))? - .to::(); +async fn find_latest_fork_block(provider: P) -> Result { + let mut num = provider.get_block_number().await?.to::(); // walk back from the head of the chain, but at most 2 blocks, which should be more than enough // leeway for _ in 0..2 { - if let Some(block) = provider - .get_block(num.into(), false) - .await - .success() - .ok_or_else(|| eyre::eyre!("Could not get block number"))? - { + if let Some(block) = provider.get_block(num.into(), false).await? { if block.header.hash.is_some() { break } diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 6a152331e9ed..acf8f26dede6 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -1916,18 +1916,19 @@ impl EthApi { if let Some(receipt) = self.backend.mined_transaction_receipt(tx.hash) { if let Some(output) = receipt.out { // insert revert reason if failure - if receipt.inner.status_code.unwrap_or_default().to::() == 0 { - if let Some(reason) = decode_revert_reason(&output) { - tx.other.insert( - "revertReason".to_string(), - serde_json::to_value(reason).expect("Infallible"), - ); - } - } - tx.other.insert( - "output".to_string(), - serde_json::to_value(output).expect("Infallible"), - ); + // TODO: Support for additional fields + // if receipt.inner.status_code.unwrap_or_default().to::() == 0 { + // if let Some(reason) = decode_revert_reason(&output) { + // tx.other.insert( + // "revertReason".to_string(), + // serde_json::to_value(reason).expect("Infallible"), + // ); + // } + // } + // tx.other.insert( + // "output".to_string(), + // serde_json::to_value(output).expect("Infallible"), + // ); } } } diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index 96b2db3505dd..b53a9d28fc9d 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -9,6 +9,7 @@ use alloy_rpc_types::{ CallRequest, EIP1186AccountProofResponse, FeeHistory, Filter, Log, Transaction, TransactionReceipt, }; +use alloy_transport::TransportError; use ethers::{ providers::ProviderError, // types::{GethDebugTracingOptions, GethTrace, Trace}, @@ -69,23 +70,14 @@ impl ClientFork { let chain_id = if let Some(chain_id) = override_chain_id { chain_id.into() } else { - self.provider() - .get_chain_id() - .await - .success() - .ok_or(BlockchainError::ChainIdNotAvailable)? - .to::() + self.provider().get_chain_id().await?.to::() }; self.config.write().chain_id = chain_id; } let provider = self.provider(); - let block = provider - .get_block(block_number, false) - .await - .success() - .ok_or(BlockchainError::BlockNotFound)? - .ok_or(BlockchainError::BlockNotFound)?; + let block = + provider.get_block(block_number, false).await?.ok_or(BlockchainError::BlockNotFound)?; let block_hash = block.header.hash.ok_or(BlockchainError::BlockNotFound)?; let timestamp = block.header.timestamp.to::(); let base_fee = block.header.base_fee_per_gas; @@ -164,12 +156,8 @@ impl ClientFork { block_count: U256, newest_block: BlockNumber, reward_percentiles: &[f64], - ) -> Result { - self.provider() - .get_fee_history(block_count, newest_block, reward_percentiles) - .await - .success() - .ok_or_else(|| eyre::eyre!("Could not fetch fee history for {:?}", newest_block)) + ) -> Result { + self.provider().get_fee_history(block_count, newest_block, reward_percentiles).await } /// Sends `eth_getProof` @@ -178,12 +166,8 @@ impl ClientFork { address: Address, keys: Vec, block_number: Option, - ) -> Result { - self.provider() - .get_proof(address, keys, block_number) - .await - .success() - .ok_or_else(|| eyre::eyre!("Could not fetch proof for {:?}", address)) + ) -> Result { + self.provider().get_proof(address, keys, block_number).await } /// Sends `eth_call` @@ -191,7 +175,7 @@ impl ClientFork { &self, request: &CallRequest, block: Option, - ) -> Result { + ) -> Result { let request = Arc::new(request.clone()); let block = block.unwrap_or(BlockNumber::Latest); @@ -205,12 +189,7 @@ impl ClientFork { let block_id: BlockId = block.into(); - let res: Bytes = self - .provider() - .call((*request).clone(), Some(block_id)) - .await - .success() - .ok_or_else(|| eyre::eyre!("Could not fetch call for {:?}", request))?; + let res: Bytes = self.provider().call((*request).clone(), Some(block_id)).await?; if let BlockNumber::Number(num) = block { // cache result @@ -226,7 +205,7 @@ impl ClientFork { &self, request: &CallRequest, block: Option, - ) -> Result { + ) -> Result { let request = Arc::new(request.clone()); let block = block.unwrap_or(BlockNumber::Latest); @@ -240,12 +219,7 @@ impl ClientFork { let block_id: BlockId = block.into(); - let res = self - .provider() - .estimate_gas((*request).clone(), Some(block_id)) - .await - .success() - .ok_or_else(|| eyre::eyre!("Could not fetch call for {:?}", request))?; + let res = self.provider().estimate_gas((*request).clone(), Some(block_id)).await?; if let BlockNumber::Number(num) = block { // cache result @@ -261,12 +235,8 @@ impl ClientFork { &self, request: &CallRequest, block: Option, - ) -> Result { - self.provider() - .create_access_list(request.clone(), block.map(|b| b.into())) - .await - .success() - .ok_or_else(|| eyre::eyre!("Could not fetch call for {:?}", request)) + ) -> Result { + self.provider().create_access_list(request.clone(), block.map(|b| b.into())).await } pub async fn storage_at( @@ -274,29 +244,19 @@ impl ClientFork { address: Address, index: StorageKey, number: Option, - ) -> Result { + ) -> Result { let index = B256::from(index); - self.provider() - .get_storage_at(address, index.into(), number.map(Into::into)) - .await - .success() - .ok_or_else(|| eyre::eyre!("Could not fetch storage for {:?}", address)) + self.provider().get_storage_at(address, index.into(), number.map(Into::into)).await } - pub async fn logs(&self, filter: &Filter) -> Result, eyre::Report> { + pub async fn logs(&self, filter: &Filter) -> Result, TransportError> { if let Some(logs) = self.storage_read().logs.get(filter).cloned() { return Ok(logs); } - let logs = self - .provider() - .get_logs(filter.clone()) - .await - .success() - .ok_or_else(|| eyre::eyre!("Could not fetch logs for {:?}", filter))?; + let logs = self.provider().get_logs(filter.clone()).await?; let mut storage = self.storage_write(); - // TODO: Intermediate struct to insert filters, or fix the filtering storage.logs.insert(filter.clone(), logs.clone()); Ok(logs) } @@ -305,7 +265,7 @@ impl ClientFork { &self, address: Address, blocknumber: u64, - ) -> Result { + ) -> Result { trace!(target: "backend::fork", "get_code={:?}", address); if let Some(code) = self.storage_read().code_at.get(&(address, blocknumber)).cloned() { return Ok(code); @@ -313,12 +273,8 @@ impl ClientFork { let block_id = BlockId::Number(blocknumber.into()); - let code = self - .provider() - .get_code_at(address, block_id) - .await - .success() - .ok_or_else(|| eyre::eyre!("Could not fetch storage for {:?}", address))?; + let code = self.provider().get_code_at(address, block_id).await?; + let mut storage = self.storage_write(); storage.code_at.insert((address, blocknumber), code.clone().0.into()); @@ -329,33 +285,25 @@ impl ClientFork { &self, address: Address, blocknumber: u64, - ) -> Result { + ) -> Result { trace!(target: "backend::fork", "get_balance={:?}", address); - self.provider() - .get_balance(address, Some(blocknumber.into())) - .await - .success() - .ok_or_else(|| eyre::eyre!("Could not fetch balance for {address}")) + self.provider().get_balance(address, Some(blocknumber.into())).await } pub async fn get_nonce( &self, address: Address, blocknumber: u64, - ) -> Result { + ) -> Result { trace!(target: "backend::fork", "get_nonce={:?}", address); - self.provider() - .get_transaction_count(address, Some(blocknumber.into())) - .await - .success() - .ok_or_else(|| eyre::eyre!("Could not fetch storage for {:?}", address)) + self.provider().get_transaction_count(address, Some(blocknumber.into())).await } pub async fn transaction_by_block_number_and_index( &self, number: u64, index: usize, - ) -> Result, eyre::Report> { + ) -> Result, TransportError> { if let Some(block) = self.block_by_number(number).await? { match block.transactions { BlockTransactions::Full(txs) => { @@ -365,13 +313,11 @@ impl ClientFork { } BlockTransactions::Hashes(hashes) => { if let Some(tx_hash) = hashes.get(index) { - return self - .transaction_by_hash(*tx_hash) - .await - .wrap_err("Could not fetch transaction") + return self.transaction_by_hash(*tx_hash).await } } - BlockTransactions::Uncle => return Err(eyre::eyre!("Uncles not supported")), + // TODO(evalir): Is it possible to reach this case? Should we support it + BlockTransactions::Uncle => panic!("Uncles not supported"), } } Ok(None) @@ -381,7 +327,7 @@ impl ClientFork { &self, hash: B256, index: usize, - ) -> Result, eyre::Report> { + ) -> Result, TransportError> { if let Some(block) = self.block_by_hash(hash).await? { match block.transactions { BlockTransactions::Full(txs) => { @@ -391,13 +337,11 @@ impl ClientFork { } BlockTransactions::Hashes(hashes) => { if let Some(tx_hash) = hashes.get(index) { - return self - .transaction_by_hash(*tx_hash) - .await - .wrap_err("Could not fetch transaction") + return self.transaction_by_hash(*tx_hash).await } } - BlockTransactions::Uncle => return Err(eyre::eyre!("Uncles not supported")), + // TODO(evalir): Is it possible to reach this case? Should we support it + BlockTransactions::Uncle => panic!("Uncles not supported"), } } Ok(None) @@ -406,34 +350,26 @@ impl ClientFork { pub async fn transaction_by_hash( &self, hash: B256, - ) -> Result, eyre::Report> { + ) -> Result, TransportError> { trace!(target: "backend::fork", "transaction_by_hash={:?}", hash); if let tx @ Some(_) = self.storage_read().transactions.get(&hash).cloned() { return Ok(tx); } - let tx = self - .provider() - .get_transaction_by_hash(hash) - .await - .success() - .ok_or_else(|| eyre::eyre!("Could not fetch transaction for {hash}"))?; + let tx = self.provider().get_transaction_by_hash(hash).await?; + let mut storage = self.storage_write(); storage.transactions.insert(hash, tx.clone()); return Ok(Some(tx)); } - pub async fn trace_transaction(&self, hash: B256) -> Result, eyre::Report> { + pub async fn trace_transaction(&self, hash: B256) -> Result, TransportError> { if let Some(traces) = self.storage_read().transaction_traces.get(&hash).cloned() { return Ok(traces); } - let traces = self - .provider() - .trace_transaction(hash) - .await - .success() - .ok_or_else(|| eyre::eyre!("Could not trace transaction for {hash}"))?; + let traces = self.provider().trace_transaction(hash).await?; + let mut storage = self.storage_write(); storage.transaction_traces.insert(hash, traces.clone()); @@ -444,34 +380,26 @@ impl ClientFork { &self, hash: B256, opts: GethDebugTracingOptions, - ) -> Result { + ) -> Result { if let Some(traces) = self.storage_read().geth_transaction_traces.get(&hash).cloned() { return Ok(traces); } - let trace = self - .provider() - .debug_trace_transaction(hash, opts) - .await - .success() - .ok_or_else(|| eyre::eyre!("Could not debug_trace transaction for {hash}"))?; + let trace = self.provider().debug_trace_transaction(hash, opts).await?; + let mut storage = self.storage_write(); storage.geth_transaction_traces.insert(hash, trace.clone()); Ok(trace) } - pub async fn trace_block(&self, number: u64) -> Result, eyre::Report> { + pub async fn trace_block(&self, number: u64) -> Result, TransportError> { if let Some(traces) = self.storage_read().block_traces.get(&number).cloned() { return Ok(traces); } - let traces = self - .provider() - .trace_block(number.into()) - .await - .success() - .ok_or_else(|| eyre::eyre!("Could not trace block {number}"))?; + let traces = self.provider().trace_block(number.into()).await?; + let mut storage = self.storage_write(); storage.block_traces.insert(number, traces.clone()); @@ -481,18 +409,12 @@ impl ClientFork { pub async fn transaction_receipt( &self, hash: B256, - ) -> Result, eyre::Report> { + ) -> Result, TransportError> { if let Some(receipt) = self.storage_read().transaction_receipts.get(&hash).cloned() { return Ok(Some(receipt)); } - if let Some(receipt) = self - .provider() - .get_transaction_receipt(hash) - .await - .success() - .ok_or_else(|| eyre::eyre!("Could not fetch tx receipt for hash {hash}"))? - { + if let Some(receipt) = self.provider().get_transaction_receipt(hash).await? { let mut storage = self.storage_write(); storage.transaction_receipts.insert(hash, receipt.clone()); return Ok(Some(receipt)); @@ -501,7 +423,7 @@ impl ClientFork { Ok(None) } - pub async fn block_by_hash(&self, hash: B256) -> Result, eyre::Report> { + pub async fn block_by_hash(&self, hash: B256) -> Result, TransportError> { if let Some(block) = self.storage_read().blocks.get(&hash).cloned() { return Ok(Some(block)); } @@ -509,14 +431,17 @@ impl ClientFork { Ok(block) } - pub async fn block_by_hash_full(&self, hash: B256) -> Result, eyre::Report> { + pub async fn block_by_hash_full(&self, hash: B256) -> Result, TransportError> { if let Some(block) = self.storage_read().blocks.get(&hash).cloned() { return Ok(Some(self.convert_to_full_block(block))); } self.fetch_full_block(hash).await } - pub async fn block_by_number(&self, block_number: u64) -> Result, eyre::Report> { + pub async fn block_by_number( + &self, + block_number: u64, + ) -> Result, TransportError> { if let Some(block) = self .storage_read() .hashes @@ -534,7 +459,7 @@ impl ClientFork { pub async fn block_by_number_full( &self, block_number: u64, - ) -> Result, eyre::Report> { + ) -> Result, TransportError> { if let Some(block) = self .storage_read() .hashes @@ -551,14 +476,8 @@ impl ClientFork { async fn fetch_full_block( &self, block_id: impl Into, - ) -> Result, eyre::Report> { - if let Some(block) = self - .provider() - .get_block(block_id.into(), true) - .await - .success() - .ok_or_else(|| eyre::eyre!("Could not fetch full block"))? - { + ) -> Result, TransportError> { + if let Some(block) = self.provider().get_block(block_id.into(), true).await? { let hash = block.header.hash.unwrap(); let block_number = block.header.number.unwrap().to::(); let mut storage = self.storage_write(); @@ -580,7 +499,7 @@ impl ClientFork { &self, hash: B256, index: usize, - ) -> Result, eyre::Report> { + ) -> Result, TransportError> { if let Some(block) = self.block_by_hash(hash).await? { return self.uncles_by_block_and_index(block, index).await; } @@ -591,7 +510,7 @@ impl ClientFork { &self, number: u64, index: usize, - ) -> Result, eyre::Report> { + ) -> Result, TransportError> { if let Some(block) = self.block_by_number(number).await? { return self.uncles_by_block_and_index(block, index).await; } @@ -602,24 +521,19 @@ impl ClientFork { &self, block: Block, index: usize, - ) -> Result, eyre::Report> { + ) -> Result, TransportError> { let block_hash = block .header .hash - .ok_or_else(|| ProviderError::CustomError("missing block-hash".to_string()))?; + // TODO: Nicer way to make a custom error from a TransportError + .expect("Missing block hash"); if let Some(uncles) = self.storage_read().uncles.get(&block_hash) { return Ok(uncles.get(index).cloned()); } let mut uncles = Vec::with_capacity(block.uncles.len()); for (uncle_idx, _) in block.uncles.iter().enumerate() { - let uncle = match self - .provider() - .get_uncle(block_hash, U64::from(uncle_idx)) - .await - .success() - .ok_or_else(|| eyre::eyre!("Could not fetch full block"))? - { + let uncle = match self.provider().get_uncle(block_hash, U64::from(uncle_idx)).await? { Some(u) => u, None => return Ok(None), }; diff --git a/crates/anvil/src/eth/error.rs b/crates/anvil/src/eth/error.rs index 971ef9956111..90d80f1c60d8 100644 --- a/crates/anvil/src/eth/error.rs +++ b/crates/anvil/src/eth/error.rs @@ -1,6 +1,7 @@ //! Aggregated error type for this module use crate::eth::pool::transactions::PoolTransaction; +use alloy_transport::TransportError; use anvil_rpc::{ error::{ErrorCode, RpcError}, response::ResponseResult, @@ -58,6 +59,8 @@ pub enum BlockchainError { FeeHistory(#[from] FeeHistoryError), #[error(transparent)] ForkProvider(#[from] ProviderError), + #[error(transparent)] + AlloyForkProvider(#[from] TransportError), #[error("EVM error {0:?}")] EvmError(InstructionResult), #[error("Invalid url {0:?}")] @@ -369,6 +372,10 @@ impl ToRpcResponseResult for Result { error!(%err, "fork provider error"); RpcError::internal_error_with(format!("Fork Error: {err:?}")) } + BlockchainError::AlloyForkProvider(err) => { + error!(%err, "alloy fork provider error"); + RpcError::internal_error_with(format!("Fork Error: {err:?}")) + } err @ BlockchainError::EvmError(_) => { RpcError::internal_error_with(err.to_string()) }