diff --git a/evm_loader/program/src/evm/database.rs b/evm_loader/program/src/evm/database.rs index 803d0cff6..b2087ce7b 100644 --- a/evm_loader/program/src/evm/database.rs +++ b/evm_loader/program/src/evm/database.rs @@ -21,8 +21,6 @@ pub trait Database { chain_id: u64, value: U256, ) -> Result<()>; - - async fn code_hash(&self, address: Address, chain_id: u64) -> Result<[u8; 32]>; async fn code_size(&self, address: Address) -> Result; async fn code(&self, address: Address) -> Result; fn set_code(&mut self, address: Address, chain_id: u64, code: Vec) -> Result<()>; @@ -51,3 +49,44 @@ pub trait Database { is_static: bool, ) -> Option>>; } + +/// Provides convenience methods that can be implemented in terms of `Database`. +#[maybe_async(?Send)] +pub trait DatabaseExt { + /// Returns whether an account exists, which is also referred to as the account being non-empty, + /// in accordance with https://eips.ethereum.org/EIPS/eip-161. + async fn account_exists(&self, address: Address, chain_id: u64) -> Result; + + /// Returns the code has for an address in accordance with https://eips.ethereum.org/EIPS/eip-1052. + async fn code_hash(&self, address: Address, chain_id: u64) -> Result<[u8; 32]>; +} + +#[maybe_async(?Send)] +impl DatabaseExt for T { + async fn account_exists(&self, address: Address, chain_id: u64) -> Result { + Ok(self.nonce(address, chain_id).await? > 0 || self.balance(address, chain_id).await? > 0) + } + + async fn code_hash(&self, address: Address, chain_id: u64) -> Result<[u8; 32]> { + // `self.code` will return an empty buffer when the account does not exist, and when the + // account exists but does not have any code. For that reason we also have to check if the + // account exists if the buffer is empty since we must return [0u8; 32] if it does not. We + // could check if the account exists first, but that would lead to more computations in the + // common case where the account exists and contains code. + let buffer = self.code(address).await?; + let bytes_to_hash = if !buffer.is_empty() { + // A program account exists at the address. + Some(&*buffer) + } else if self.account_exists(address, chain_id).await? { + // A data account exists at the address. + Some(<&[u8]>::default()) + } else { + // No account exists at the address. + None + }; + + Ok(bytes_to_hash + .map(|bytes| solana_program::keccak::hash(bytes).to_bytes()) + .unwrap_or_default()) + } +} diff --git a/evm_loader/program/src/evm/opcode.rs b/evm_loader/program/src/evm/opcode.rs index acc9ed5dc..165111245 100644 --- a/evm_loader/program/src/evm/opcode.rs +++ b/evm_loader/program/src/evm/opcode.rs @@ -3,7 +3,10 @@ use ethnum::{I256, U256}; use maybe_async::maybe_async; use solana_program::log::sol_log_data; -use super::{database::Database, tracing_event, Context, Machine, Reason}; +use super::{ + database::{Database, DatabaseExt}, + tracing_event, Context, Machine, Reason, +}; use crate::{ error::{Error, Result}, evm::{trace_end_step, Buffer}, diff --git a/evm_loader/program/src/executor/state.rs b/evm_loader/program/src/executor/state.rs index 1b3ad8cd7..5a79b16a8 100644 --- a/evm_loader/program/src/executor/state.rs +++ b/evm_loader/program/src/executor/state.rs @@ -284,32 +284,6 @@ impl<'a, B: AccountStorage> Database for ExecutorState<'a, B> { Ok(self.backend.code_size(from_address).await) } - async fn code_hash(&self, address: Address, chain_id: u64) -> Result<[u8; 32]> { - // https://eips.ethereum.org/EIPS/eip-1052 - // https://eips.ethereum.org/EIPS/eip-161 - - // `self.code` will return an empty buffer when the account does not exist, and when the - // account exists but does not have any code. For that reason we also have to check if the - // account exists if the buffer is empty since we must return [0u8; 32] if it does not. We - // could check if the account exists first, but that would lead to more computations in the - // common case where the account exists and contains code. - let buffer = self.code(address).await?; - let bytes_to_hash = if !buffer.is_empty() { - // A program account exists at the address. - Some(&*buffer) - } else if self.account_exists(address, chain_id).await? { - // A data account exists at the address. - Some(Default::default()) - } else { - // No account exists at the address. - None - }; - - Ok(bytes_to_hash - .map(|bytes| solana_program::keccak::hash(bytes).to_bytes()) - .unwrap_or_default()) - } - async fn code(&self, from_address: Address) -> Result { for action in &self.actions { if let Action::EvmSetCode { address, code, .. } = action { @@ -479,12 +453,3 @@ impl<'a, B: AccountStorage> Database for ExecutorState<'a, B> { self.backend.contract_chain_id(contract).await } } - -impl<'a, B: AccountStorage> ExecutorState<'a, B> { - /// Returns whether an account exists, which is also referred to as the account being non-empty, - /// in accordance with https://eips.ethereum.org/EIPS/eip-161. - #[maybe_async] - async fn account_exists(&self, address: Address, chain_id: u64) -> Result { - Ok(self.nonce(address, chain_id).await? > 0 || self.balance(address, chain_id).await? > 0) - } -}