Skip to content

Commit

Permalink
load access list into db
Browse files Browse the repository at this point in the history
  • Loading branch information
Wollac committed Aug 24, 2024
1 parent 270e57e commit 4d77cdd
Show file tree
Hide file tree
Showing 8 changed files with 364 additions and 179 deletions.
80 changes: 9 additions & 71 deletions steel/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,15 @@ pub mod host {
use super::BlockInput;
use crate::{
host::{db::AlloyDb, HostEvmEnv},
state::StateAccount,
EvmBlockHeader, MerkleTrie,
EvmBlockHeader,
};
use alloy::{
network::Network, providers::Provider, rpc::types::Header as RpcHeader,
transports::Transport,
};
use alloy_primitives::{keccak256, StorageKey};
use anyhow::{anyhow, ensure, Context};

use anyhow::{anyhow, ensure};
use log::debug;
use revm::primitives::HashMap;

impl<H: EvmBlockHeader> BlockInput<H> {
/// Derives the verifiable input from a [HostEvmEnv].
Expand All @@ -100,82 +98,22 @@ pub mod host {
{
let db = &env.db.unwrap();

// use the same provider as the database
let provider = db.inner().provider();
let block_number = db.inner().block_number();

// retrieve EIP-1186 proofs for all accounts
let mut proofs = Vec::new();
for (address, storage_keys) in db.accounts() {
let proof = provider
.get_proof(
*address,
storage_keys.iter().map(|v| StorageKey::from(*v)).collect(),
)
.number(block_number)
.await
.context("eth_getProof failed")?;
proofs.push(proof);
}

// build the sparse MPT for the state and verify it against the header
let state_nodes = proofs.iter().flat_map(|p| p.account_proof.iter());
let state_trie =
MerkleTrie::from_rlp_nodes(state_nodes).context("accountProof invalid")?;
let (state_trie, storage_tries) = db.state_proof().await?;
ensure!(
env.header.state_root() == &state_trie.hash_slow(),
"accountProof root does not match header's stateRoot"
);

// build the sparse MPT for account storages and filter duplicates
let mut storage_tries = HashMap::new();
for proof in proofs {
// skip non-existing accounts or accounts where no storage slots were requested
if proof.storage_proof.is_empty() || proof.storage_hash.is_zero() {
continue;
}

// build the sparse MPT for that account's storage by iterating over all storage
// proofs
let storage_nodes = proof.storage_proof.iter().flat_map(|p| p.proof.iter());
let storage_trie =
MerkleTrie::from_rlp_nodes(storage_nodes).context("storageProof invalid")?;
let storage_root_hash = storage_trie.hash_slow();
// verify it against the state trie
let account: StateAccount = state_trie
.get_rlp(keccak256(proof.address))
.with_context(|| {
format!("invalid RLP value in state trie for {}", proof.address)
})?
.unwrap_or_default();
ensure!(
account.storage_root == storage_root_hash,
"storageProof of {} does not match storageRoot in the state",
proof.address
);

storage_tries.insert(storage_root_hash, storage_trie);
}
let storage_tries: Vec<_> = storage_tries.into_values().collect();

// collect the bytecode of all referenced contracts
let contracts: Vec<_> = db.contracts().values().cloned().collect();

// retrieve ancestor block headers
let mut ancestors = Vec::new();
if let Some(&block_hash_min_number) = db.block_hash_numbers().iter().min() {
for number in (block_hash_min_number..block_number).rev() {
let rpc_block = provider
.get_block_by_number(number.into(), false)
.await
.context("eth_getBlockByNumber failed")?
.with_context(|| format!("block {} not found", number))?;
let header: H = rpc_block
.header
.try_into()
.map_err(|err| anyhow!("header invalid: {}", err))?;
ancestors.push(header);
}
for rlp_header in db.ancestor_proof().await? {
let header: H = rlp_header
.try_into()
.map_err(|err| anyhow!("header invalid: {}", err))?;
ancestors.push(header);
}

debug!("state size: {}", state_trie.size());
Expand Down
20 changes: 19 additions & 1 deletion steel/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use std::{

#[cfg(feature = "host")]
use crate::host::HostEvmEnv;
use crate::{state::WrapStateDb, EvmBlockHeader, GuestEvmEnv};
use crate::{host::db::AlloyDb, state::WrapStateDb, EvmBlockHeader, GuestEvmEnv};
use alloy_primitives::{Address, TxKind, U256};
use alloy_sol_types::{SolCall, SolType};
use revm::{
Expand Down Expand Up @@ -177,6 +177,24 @@ impl<C, E> CallBuilder<C, E> {
}
}

#[cfg(feature = "host")]
impl<'a, C, T, N, P, H> CallBuilder<C, &'a mut HostEvmEnv<AlloyDb<T, N, P>, H>>
where
T: alloy::transports::Transport + Clone,
N: alloy::network::Network,
P: alloy::providers::Provider<T, N>,
{
/// Sets the `access_list` in the transaction to the provided value
pub async fn access_list(
self,
access_list: alloy::eips::eip2930::AccessList,
) -> anyhow::Result<Self> {
let db = self.env.db.as_mut().unwrap();
db.add_access_list(access_list).await?;
Ok(self)
}
}

#[cfg(feature = "host")]
impl<'a, C, D, H> CallBuilder<C, &'a mut HostEvmEnv<D, H>>
where
Expand Down
2 changes: 1 addition & 1 deletion steel/src/host/db/alloy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pub struct AlloyDb<T: Transport + Clone, N: Network, P: Provider<T, N>> {
}

impl<T: Transport + Clone, N: Network, P: Provider<T, N>> AlloyDb<T, N, P> {
/// Create a new AlloyDb instance, with a [Provider] and a block.
/// Creates a new AlloyDb instance, with a [Provider] and a block.
///
/// This will panic if called outside the context of a Tokio runtime.
pub fn new(provider: P, block_number: BlockNumber) -> Self {
Expand Down
4 changes: 2 additions & 2 deletions steel/src/host/db/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
//!
//! [Database]: revm::Database
mod alloy;
mod trace;
mod proof;

pub use alloy::AlloyDb;
pub use trace::TraceDb;
pub use proof::ProofDb;
Loading

0 comments on commit 4d77cdd

Please sign in to comment.