Skip to content

Commit

Permalink
Move Database convenience functions to extension trait
Browse files Browse the repository at this point in the history
Added `DatabaseExt` which implements functions that can be implemented
in terms of `Database`.
  • Loading branch information
mickvangelderen committed Jan 12, 2024
1 parent 428e138 commit 43ddf36
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 38 deletions.
43 changes: 41 additions & 2 deletions evm_loader/program/src/evm/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<usize>;
async fn code(&self, address: Address) -> Result<Buffer>;
fn set_code(&mut self, address: Address, chain_id: u64, code: Vec<u8>) -> Result<()>;
Expand Down Expand Up @@ -51,3 +49,44 @@ pub trait Database {
is_static: bool,
) -> Option<Result<Vec<u8>>>;
}

/// 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<bool>;

/// 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<T: Database> DatabaseExt for T {
async fn account_exists(&self, address: Address, chain_id: u64) -> Result<bool> {
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())
}
}
5 changes: 4 additions & 1 deletion evm_loader/program/src/evm/opcode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down
35 changes: 0 additions & 35 deletions evm_loader/program/src/executor/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<crate::evm::Buffer> {
for action in &self.actions {
if let Action::EvmSetCode { address, code, .. } = action {
Expand Down Expand Up @@ -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<bool> {
Ok(self.nonce(address, chain_id).await? > 0 || self.balance(address, chain_id).await? > 0)
}
}

0 comments on commit 43ddf36

Please sign in to comment.