Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: pectra support #395

Merged
merged 8 commits into from
Feb 10, 2025
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
33 changes: 10 additions & 23 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ alloy = { version = "0.9.1", features = [
] }
alloy-trie = "0.7.8"
op-alloy-rpc-types = "0.9.0"
revm = { version = "18.0.0", default-features = false, features = [
revm = { version = "19.4.0", default-features = false, features = [
"std",
"serde",
"optional_block_gas_limit",
Expand Down
4 changes: 3 additions & 1 deletion core/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::client::node::Node;
#[cfg(not(target_arch = "wasm32"))]
use crate::client::rpc::Rpc;
use crate::consensus::Consensus;
use crate::fork_schedule::ForkSchedule;
use crate::network_spec::NetworkSpec;
use crate::time::interval;
use crate::types::BlockTag;
Expand All @@ -29,9 +30,10 @@ impl<N: NetworkSpec, C: Consensus<N::BlockResponse>> Client<N, C> {
pub fn new(
execution_rpc: &str,
consensus: C,
fork_schedule: ForkSchedule,
#[cfg(not(target_arch = "wasm32"))] rpc_address: Option<SocketAddr>,
) -> Result<Self> {
let node = Node::new(execution_rpc, consensus)?;
let node = Node::new(execution_rpc, consensus, fork_schedule)?;
let node = Arc::new(node);

#[cfg(not(target_arch = "wasm32"))]
Expand Down
26 changes: 22 additions & 4 deletions core/src/client/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::execution::evm::Evm;
use crate::execution::rpc::http_rpc::HttpRpc;
use crate::execution::state::State;
use crate::execution::ExecutionClient;
use crate::fork_schedule::ForkSchedule;
use crate::network_spec::NetworkSpec;
use crate::time::{SystemTime, UNIX_EPOCH};
use crate::types::BlockTag;
Expand All @@ -20,22 +21,29 @@ pub struct Node<N: NetworkSpec, C: Consensus<N::BlockResponse>> {
pub consensus: C,
pub execution: Arc<ExecutionClient<N, HttpRpc<N>>>,
pub history_size: usize,
fork_schedule: ForkSchedule,
}

impl<N: NetworkSpec, C: Consensus<N::BlockResponse>> Node<N, C> {
pub fn new(execution_rpc: &str, mut consensus: C) -> Result<Self, ClientError> {
pub fn new(
execution_rpc: &str,
mut consensus: C,
fork_schedule: ForkSchedule,
) -> Result<Self, ClientError> {
let block_recv = consensus.block_recv().take().unwrap();
let finalized_block_recv = consensus.finalized_block_recv().take().unwrap();

let state = State::new(block_recv, finalized_block_recv, 256, execution_rpc);
let execution = Arc::new(
ExecutionClient::new(execution_rpc, state).map_err(ClientError::InternalError)?,
ExecutionClient::new(execution_rpc, state, fork_schedule)
.map_err(ClientError::InternalError)?,
);

Ok(Node {
consensus,
execution,
history_size: 64,
fork_schedule,
})
}

Expand All @@ -46,14 +54,24 @@ impl<N: NetworkSpec, C: Consensus<N::BlockResponse>> Node<N, C> {
) -> Result<Bytes, ClientError> {
self.check_blocktag_age(&block).await?;

let mut evm = Evm::new(self.execution.clone(), self.chain_id(), block);
let mut evm = Evm::new(
self.execution.clone(),
self.chain_id(),
self.fork_schedule,
block,
);
evm.call(tx).await.map_err(ClientError::EvmError)
}

pub async fn estimate_gas(&self, tx: &N::TransactionRequest) -> Result<u64, ClientError> {
self.check_head_age().await?;

let mut evm = Evm::new(self.execution.clone(), self.chain_id(), BlockTag::Latest);
let mut evm = Evm::new(
self.execution.clone(),
self.chain_id(),
self.fork_schedule,
BlockTag::Latest,
);

evm.estimate_gas(tx).await.map_err(ClientError::EvmError)
}
Expand Down
2 changes: 0 additions & 2 deletions core/src/execution/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,3 @@ pub const PARALLEL_QUERY_BATCH_SIZE: usize = 20;
// We currently limit the max number of logs to fetch,
// to avoid blocking the client for too long.
pub const MAX_SUPPORTED_LOGS_NUMBER: usize = 5;
pub const BLOB_BASE_FEE_UPDATE_FRACTION: u64 = 3338477;
pub const MIN_BASE_FEE_PER_BLOB_GAS: u64 = 1;
27 changes: 19 additions & 8 deletions core/src/execution/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,37 @@
};
use tracing::trace;

use crate::execution::{
constants::PARALLEL_QUERY_BATCH_SIZE,
errors::{EvmError, ExecutionError},
rpc::ExecutionRpc,
ExecutionClient,
};
use crate::network_spec::NetworkSpec;
use crate::types::BlockTag;
use crate::{
execution::{
constants::PARALLEL_QUERY_BATCH_SIZE,
errors::{EvmError, ExecutionError},
rpc::ExecutionRpc,
ExecutionClient,
},
fork_schedule::ForkSchedule,
};

pub struct Evm<N: NetworkSpec, R: ExecutionRpc<N>> {
execution: Arc<ExecutionClient<N, R>>,
chain_id: u64,
tag: BlockTag,
fork_schedule: ForkSchedule,
}

impl<N: NetworkSpec, R: ExecutionRpc<N>> Evm<N, R> {
pub fn new(execution: Arc<ExecutionClient<N, R>>, chain_id: u64, tag: BlockTag) -> Self {
pub fn new(
execution: Arc<ExecutionClient<N, R>>,
chain_id: u64,
fork_schedule: ForkSchedule,
tag: BlockTag,
) -> Self {
Evm {
execution,
chain_id,
tag,
fork_schedule,
}
}

Expand Down Expand Up @@ -92,7 +102,7 @@

async fn get_env(&self, tx: &N::TransactionRequest, tag: BlockTag) -> Env {
let mut env = Env::default();
env.tx = N::tx_env(tx);

Check failure on line 105 in core/src/execution/evm.rs

View workflow job for this annotation

GitHub Actions / clippy

field assignment outside of initializer for an instance created with Default::default()

let block = self
.execution
Expand All @@ -100,7 +110,8 @@
.await
.ok_or(ExecutionError::BlockNotFound(tag))
.unwrap();
env.block = N::block_env(&block);

env.block = N::block_env(&block, &self.fork_schedule);

env.cfg.chain_id = self.chain_id;
env.cfg.disable_block_gas_limit = true;
Expand Down
50 changes: 17 additions & 33 deletions core/src/execution/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@
use alloy::rlp;
use alloy::rpc::types::{BlockTransactions, Filter, FilterChanges, Log};
use alloy_trie::root::ordered_trie_root_with_encoder;
use constants::{BLOB_BASE_FEE_UPDATE_FRACTION, MIN_BASE_FEE_PER_BLOB_GAS};
use eyre::Result;
use futures::future::try_join_all;
use proof::{verify_account_proof, verify_storage_proof};
use revm::primitives::KECCAK_EMPTY;
use revm::primitives::{BlobExcessGasAndPrice, KECCAK_EMPTY};
use tracing::warn;

use crate::fork_schedule::ForkSchedule;
use crate::network_spec::NetworkSpec;
use crate::types::BlockTag;

use self::constants::MAX_SUPPORTED_LOGS_NUMBER;
use self::errors::ExecutionError;
use self::proof::{verify_account_proof, verify_storage_proof};
use self::rpc::ExecutionRpc;
use self::state::{FilterType, State};
use self::types::Account;
Expand All @@ -35,12 +35,17 @@
pub struct ExecutionClient<N: NetworkSpec, R: ExecutionRpc<N>> {
pub rpc: R,
state: State<N, R>,
fork_schedule: ForkSchedule,
}

impl<N: NetworkSpec, R: ExecutionRpc<N>> ExecutionClient<N, R> {
pub fn new(rpc: &str, state: State<N, R>) -> Result<Self> {
pub fn new(rpc: &str, state: State<N, R>, fork_schedule: ForkSchedule) -> Result<Self> {
let rpc: R = ExecutionRpc::new(rpc)?;
Ok(ExecutionClient::<N, R> { rpc, state })
Ok(ExecutionClient::<N, R> {
rpc,
state,
fork_schedule,
})
}

pub async fn check_rpc(&self, chain_id: u64) -> Result<()> {
Expand Down Expand Up @@ -121,42 +126,21 @@

pub async fn blob_base_fee(&self, block: BlockTag) -> U256 {
let block = self.state.get_block(block).await;
if block.is_none() {
let Some(block) = block else {
warn!(target: "helios::execution", "requested block not found");
return U256::from(0);
}
let parent_hash = block.unwrap().header().parent_hash();
};

let parent_hash = block.header().parent_hash();
let parent_block = self.get_block_by_hash(parent_hash, false).await;
if parent_block.is_none() {
warn!(target: "helios::execution", "requested parent block not foundß");
return U256::from(0);
};

let blob_base_fee = Self::calculate_base_fee_per_blob_gas(
parent_block.unwrap().header().excess_blob_gas().unwrap(),
);

U256::from(blob_base_fee)
}

fn calculate_base_fee_per_blob_gas(parent_excess_blob_gas: u64) -> u64 {
Self::fake_exponential(
MIN_BASE_FEE_PER_BLOB_GAS,
parent_excess_blob_gas,
BLOB_BASE_FEE_UPDATE_FRACTION,
)
}
//https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4844.md#helpers
fn fake_exponential(factor: u64, numerator: u64, denominator: u64) -> u64 {
let mut i = 1;
let mut output = 0;
let mut numerator_accum = factor * denominator;
while numerator_accum > 0 {
output += numerator_accum;
numerator_accum = numerator_accum * numerator / (denominator * i);
i += 1;
}
output / denominator
let excess_blob_gas = parent_block.unwrap().header().excess_blob_gas().unwrap();
let is_prague = block.header().timestamp() >= self.fork_schedule.prague_timestamp;
U256::from(BlobExcessGasAndPrice::new(excess_blob_gas, is_prague).blob_gasprice)
}

pub async fn get_block_by_hash(&self, hash: B256, full_tx: bool) -> Option<N::BlockResponse> {
Expand Down Expand Up @@ -370,7 +354,7 @@
}
_ => {
// only concerned with filters created via helios
return Err(ExecutionError::FilterNotFound(filter_id).into());

Check failure on line 357 in core/src/execution/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

unneeded `return` statement
}
}
}
Expand Down Expand Up @@ -532,7 +516,7 @@
/// Compute a trie root of a collection of encoded items.
/// Ref: https://github.com/alloy-rs/trie/blob/main/src/root.rs.
fn ordered_trie_root(items: &[Vec<u8>]) -> B256 {
fn noop_encoder(item: &Vec<u8>, buffer: &mut Vec<u8>) {

Check failure on line 519 in core/src/execution/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

writing `&Vec` instead of `&[_]` involves a new object where a slice will do
buffer.extend_from_slice(item);
}

Expand Down
6 changes: 6 additions & 0 deletions core/src/fork_schedule.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
use serde::{Deserialize, Serialize};

#[derive(Clone, Copy, Serialize, Deserialize, Default, Debug)]
pub struct ForkSchedule {
pub prague_timestamp: u64,
}
1 change: 1 addition & 0 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod client;
pub mod consensus;
pub mod errors;
pub mod execution;
pub mod fork_schedule;
pub mod network_spec;
pub mod time;
pub mod types;
4 changes: 3 additions & 1 deletion core/src/network_spec.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use alloy::{network::Network, rpc::types::Log};
use revm::primitives::{BlockEnv, TxEnv};

use crate::fork_schedule::ForkSchedule;

pub trait NetworkSpec: Network {
fn encode_receipt(receipt: &Self::ReceiptResponse) -> Vec<u8>;
fn is_hash_valid(block: &Self::BlockResponse) -> bool;
fn receipt_contains(list: &[Self::ReceiptResponse], elem: &Self::ReceiptResponse) -> bool;
fn receipt_logs(receipt: &Self::ReceiptResponse) -> Vec<Log>;
fn tx_env(request: &Self::TransactionRequest) -> TxEnv;
fn block_env(block: &Self::BlockResponse) -> BlockEnv;
fn block_env(block: &Self::BlockResponse, fork_schedule: &ForkSchedule) -> BlockEnv;
}
1 change: 1 addition & 0 deletions ethereum/consensus-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ alloy = { version = "0.9.1", features = [
"ssz",
"rlp",
"k256",
"eips",
] }
alloy-rlp = "0.3.10"
bls12_381.workspace = true
Expand Down
Loading
Loading