Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/scripts/hive/build_simulators.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ echo "Building images"
# TODO: test code has been moved from https://github.com/ethereum/execution-spec-tests to https://github.com/ethereum/execution-specs we need to pin eels branch with `--sim.buildarg branch=<release-branch-name>` once we have the fusaka release tagged on the new repo
./hive -client reth --sim "ethereum/eels" \
--sim.buildarg fixtures=https://github.com/ethereum/execution-spec-tests/releases/download/bal@v5.1.0/fixtures_bal.tar.gz \
--sim.buildarg branch=devnets/bal/2 \
--sim.buildarg branch=err-map-3 \
--sim.timelimit 1s || true &
./hive -client reth --sim "ethereum/engine" -sim.timelimit 1s || true &
./hive -client reth --sim "devp2p" -sim.timelimit 1s || true &
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/hive.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:
- name: Checkout hive tests
uses: actions/checkout@v6
with:
repository: ethereum/hive
repository: Soubhik-10/hive
ref: master
path: hivetests

Expand Down Expand Up @@ -231,7 +231,7 @@ jobs:
- name: Checkout hive tests
uses: actions/checkout@v6
with:
repository: ethereum/hive
repository: Soubhik-10/hive
ref: master
path: hivetests

Expand Down
3 changes: 1 addition & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion crates/rpc/rpc-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ reth-trie-common.workspace = true
reth-chain-state.workspace = true

# ethereum
alloy-eip7928 = { workspace = true, features = ["serde"] }
alloy-eips.workspace = true
alloy-json-rpc.workspace = true
alloy-primitives.workspace = true
Expand Down
6 changes: 3 additions & 3 deletions crates/rpc/rpc-api/src/debug.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use alloy_eip7928::BlockAccessList;
use alloy_eips::{BlockId, BlockNumberOrTag};
use alloy_genesis::ChainConfig;
use alloy_json_rpc::RpcObject;
Expand Down Expand Up @@ -157,9 +156,10 @@ pub trait DebugApi<TxReq: RpcObject> {
hash: B256,
) -> RpcResult<ExecutionWitness>;

/// Re-executes a block and returns the Block Access List (BAL) as defined in EIP-7928.
/// Re-executes a block and returns the RLP-encoded Block Access List (BAL) as defined in
/// EIP-7928.
#[method(name = "getBlockAccessList")]
async fn debug_get_block_access_list(&self, block_id: BlockId) -> RpcResult<BlockAccessList>;
async fn debug_get_block_access_list(&self, block_id: BlockId) -> RpcResult<Bytes>;

/// Sets the logging backtrace location. When a backtrace location is set and a log message is
/// emitted at that location, the stack of the goroutine executing the log statement will
Expand Down
1 change: 1 addition & 0 deletions crates/rpc/rpc-eth-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ tokio.workspace = true
auto_impl.workspace = true
dyn-clone.workspace = true
tracing.workspace = true
serde_json.workspace = true

[features]
js-tracer = ["revm-inspectors/js-tracer", "reth-rpc-eth-types/js-tracer"]
Expand Down
33 changes: 25 additions & 8 deletions crates/rpc/rpc-eth-api/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ use alloy_serde::JsonStorageKey;
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
use reth_primitives_traits::TxTy;
use reth_rpc_convert::RpcTxReq;
use reth_rpc_eth_types::FillTransaction;
use reth_rpc_eth_types::{error::FromEthApiError, EthApiError, FillTransaction};
use reth_rpc_server_types::{result::internal_rpc_err, ToRpcResult};
use reth_storage_api::BlockIdReader;
use serde_json::Value;
use std::collections::HashMap;
use tracing::trace;

Expand Down Expand Up @@ -406,14 +408,14 @@ pub trait EthApi<

/// Returns the EIP-7928 block access list for a block by hash.
#[method(name = "getBlockAccessListByBlockHash")]
async fn block_access_list_by_block_hash(&self, hash: B256) -> RpcResult<Option<Bytes>>;
async fn block_access_list_by_block_hash(&self, hash: B256) -> RpcResult<Option<Value>>;

/// Returns the EIP-7928 block access list for a block by number.
#[method(name = "getBlockAccessListByBlockNumber")]
async fn block_access_list_by_block_number(
&self,
number: BlockNumberOrTag,
) -> RpcResult<Option<Bytes>>;
) -> RpcResult<Option<Value>>;
}

#[async_trait::async_trait]
Expand Down Expand Up @@ -913,17 +915,32 @@ where
}

/// Handler for: `eth_getBlockAccessListByBlockHash`
async fn block_access_list_by_block_hash(&self, hash: B256) -> RpcResult<Option<Bytes>> {
trace!(target: "rpc::eth", ?hash, "Serving eth_getBlockAccessListByBlockHash");
Err(internal_rpc_err("unimplemented"))
async fn block_access_list_by_block_hash(&self, block_hash: B256) -> RpcResult<Option<Value>> {
trace!(target: "rpc::eth", ?block_hash, "Serving eth_getBlockAccessListByBlockHash");

let bal = self.get_block_access_list(block_hash).await?;
let json = serde_json::to_value(&bal)
.map_err(|e| EthApiError::Internal(reth_errors::RethError::msg(e.to_string())))?;

Ok(Some(json))
}

/// Handler for: `eth_getBlockAccessListByBlockNumber`
async fn block_access_list_by_block_number(
&self,
number: BlockNumberOrTag,
) -> RpcResult<Option<Bytes>> {
) -> RpcResult<Option<Value>> {
trace!(target: "rpc::eth", ?number, "Serving eth_getBlockAccessListByBlockNumber");
Err(internal_rpc_err("unimplemented"))
let block_hash = self
.provider()
.block_hash_for_id(number.into())
.map_err(T::Error::from_eth_err)?
.ok_or(EthApiError::HeaderNotFound(number.into()))?;

let bal = self.get_block_access_list(block_hash).await?;
let json = serde_json::to_value(&bal)
.map_err(|e| EthApiError::Internal(reth_errors::RethError::msg(e.to_string())))?;

Ok(Some(json))
}
}
79 changes: 79 additions & 0 deletions crates/rpc/rpc-eth-api/src/helpers/block_access_list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//! Helpers for `eth_blockAccessList` RPC method.
use alloy_consensus::BlockHeader;
use alloy_eips::eip7928::BlockAccessList;
use alloy_primitives::B256;
use reth_chainspec::{ChainSpecProvider, EthereumHardforks};
use reth_errors::RethError;
use reth_evm::{block::BlockExecutor, ConfigureEvm, Evm};
use reth_revm::{database::StateProviderDatabase, State};
use reth_rpc_eth_types::{
cache::db::StateProviderTraitObjWrapper, error::FromEthApiError, EthApiError,
};
use reth_storage_api::{BlockNumReader, StateProviderFactory};

use crate::{
helpers::{Call, LoadBlock, Trace},
RpcNodeCore,
};

/// Helper trait for `eth_blockAccessList` RPC method.
pub trait GetBlockAccessList: Trace + Call + LoadBlock {
/// Retrieves the block access list for a block identified by its hash.
fn get_block_access_list(
&self,
block_hash: B256,
) -> impl Future<Output = Result<Option<BlockAccessList>, Self::Error>> + Send {
async move {
let block = self
.recovered_block(block_hash.into())
.await?
.ok_or_else(|| EthApiError::HeaderNotFound(block_hash.into()))?;

// Check if the block has been pruned (EIP-4444)
let earliest_block = self.provider().earliest_block_number()?;
if block.header().number() < earliest_block {
return Err(EthApiError::PrunedHistoryUnavailable.into());
}
// Check if the block is pre-Amsterdam, as access lists are not available for those
// blocks
if !self.provider().chain_spec().is_amsterdam_active_at_timestamp(block.timestamp()) {
return Err(EthApiError::BlockAccessListNotAvailablePreAmsterdam.into());
}

self.spawn_blocking_io(move |eth_api| {
let state = eth_api
.provider()
.state_by_block_id(block.parent_hash().into())
.map_err(Self::Error::from_eth_err)?;

let mut db = State::builder()
.with_database(StateProviderDatabase::new(StateProviderTraitObjWrapper(state)))
.with_bal_builder()
.build();

let block_txs = block.transactions_recovered();
let mut executor = RpcNodeCore::evm_config(&eth_api)
.executor_for_block(&mut db, block.sealed_block())
.map_err(RethError::other)
.map_err(Self::Error::from_eth_err)?;

executor.apply_pre_execution_changes().map_err(Self::Error::from_eth_err)?;
executor.evm_mut().db_mut().bump_bal_index();

// replay all transactions prior to the targeted transaction
for block_tx in block_txs {
executor.execute_transaction(block_tx).map_err(Self::Error::from_eth_err)?;
executor.evm_mut().db_mut().bump_bal_index();
}

executor
.apply_post_execution_changes()
.map_err(|err| EthApiError::Internal(err.into()))?;

let bal = db.take_built_alloy_bal();
Ok(bal)
})
.await
}
}
}
12 changes: 9 additions & 3 deletions crates/rpc/rpc-eth-api/src/helpers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
//! all the `Eth` traits, e.g. `reth_rpc::EthApi`.

pub mod block;
pub mod block_access_list;
pub mod blocking_task;
pub mod call;
pub mod config;
Expand All @@ -40,12 +41,15 @@ pub use state::{EthState, LoadState};
pub use trace::Trace;
pub use transaction::{EthTransactions, LoadTransaction};

use crate::FullEthApiTypes;
use crate::{helpers::block_access_list::GetBlockAccessList, FullEthApiTypes};

/// Extension trait that bundles traits needed for tracing transactions.
pub trait TraceExt: LoadTransaction + LoadBlock + SpawnBlocking + Trace + Call {}
pub trait TraceExt:
LoadTransaction + LoadBlock + SpawnBlocking + Trace + Call + GetBlockAccessList
{
}

impl<T> TraceExt for T where T: LoadTransaction + LoadBlock + Trace + Call {}
impl<T> TraceExt for T where T: LoadTransaction + LoadBlock + Trace + Call + GetBlockAccessList {}

/// Helper trait to unify all `eth` rpc server building block traits, for simplicity.
///
Expand All @@ -60,6 +64,7 @@ pub trait FullEthApi:
+ EthFees
+ Trace
+ LoadReceipt
+ GetBlockAccessList
{
}

Expand All @@ -73,5 +78,6 @@ impl<T> FullEthApi for T where
+ EthFees
+ Trace
+ LoadReceipt
+ GetBlockAccessList
{
}
6 changes: 6 additions & 0 deletions crates/rpc/rpc-eth-types/src/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,9 @@ pub enum EthApiError {
/// The underlying error object
error: jsonrpsee_types::ErrorObject<'static>,
},
/// Error thrown when trying to access block access list for blocks before Amsterdam
#[error("Block access list not available for pre-Amsterdam blocks")]
BlockAccessListNotAvailablePreAmsterdam,
/// Any other error
#[error("{0}")]
Other(Box<dyn ToRpcError>),
Expand Down Expand Up @@ -345,6 +348,9 @@ impl From<EthApiError> for jsonrpsee_types::error::ErrorObject<'static> {
error.data(),
)
}
EthApiError::BlockAccessListNotAvailablePreAmsterdam => {
rpc_error_with_code(4445, error.to_string())
}
}
}
}
Expand Down
1 change: 0 additions & 1 deletion crates/rpc/rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ reth-node-api.workspace = true
reth-trie-common.workspace = true

# ethereum
alloy-eip7928.workspace = true
alloy-evm = { workspace = true, features = ["overrides"] }
alloy-consensus.workspace = true
alloy-signer.workspace = true
Expand Down
19 changes: 16 additions & 3 deletions crates/rpc/rpc/src/debug.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use alloy_consensus::{transaction::TxHashRef, BlockHeader};
use alloy_eip7928::BlockAccessList;
use alloy_eips::{eip2718::Encodable2718, BlockId, BlockNumberOrTag};
use alloy_evm::{env::BlockEnvironment, Evm};
use alloy_genesis::ChainConfig;
Expand Down Expand Up @@ -160,6 +159,19 @@ where
.await
}

/// Replays a block and generates the RLP-encoded Block Access List (EIP-7928).
pub async fn get_block_access_list(&self, block_id: BlockId) -> Result<Bytes, Eth::Error> {
let block_hash = self
.provider()
.block_hash_for_id(block_id)
.map_err(Eth::Error::from_eth_err)?
.ok_or(EthApiError::HeaderNotFound(block_id))?;

let bal = self.eth_api().get_block_access_list(block_hash).await?;

Ok(alloy_rlp::encode(bal.unwrap_or_default()).into())
}

/// Replays the given block and returns the trace of each transaction.
///
/// This expects a rlp encoded block
Expand Down Expand Up @@ -871,8 +883,9 @@ where
Self::debug_execution_witness_by_block_hash(self, hash).await.map_err(Into::into)
}

async fn debug_get_block_access_list(&self, _block_id: BlockId) -> RpcResult<BlockAccessList> {
Err(internal_rpc_err("unimplemented"))
async fn debug_get_block_access_list(&self, block_id: BlockId) -> RpcResult<Bytes> {
let _permit = self.acquire_trace_permit().await;
self.get_block_access_list(block_id).await.map_err(Into::into)
}

async fn debug_backtrace_at(&self, _location: &str) -> RpcResult<()> {
Expand Down
15 changes: 15 additions & 0 deletions crates/rpc/rpc/src/eth/helpers/block_access_list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//! Contains RPC handler implementations specific to block access lists.

use reth_rpc_convert::RpcConvert;
use reth_rpc_eth_api::{helpers::block_access_list::GetBlockAccessList, FromEvmError, RpcNodeCore};
use reth_rpc_eth_types::EthApiError;

use crate::EthApi;

impl<N, Rpc> GetBlockAccessList for EthApi<N, Rpc>
where
N: RpcNodeCore,
EthApiError: FromEvmError<N::Evm>,
Rpc: RpcConvert<Primitives = N::Primitives, Error = EthApiError, Evm = N::Evm>,
{
}
1 change: 1 addition & 0 deletions crates/rpc/rpc/src/eth/helpers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ mod trace;
mod transaction;

pub use sync_listener::SyncListener;
pub mod block_access_list;
Loading