Skip to content

Commit 7b08d9e

Browse files
authored
feat(l1): implement eth-config (#4625)
**Motivation** Achieving full Fusaka implementation before launch. **Description** This PR implements [EIP-7910](https://eips.ethereum.org/EIPS/eip-7910). To summarize, it consists of a new eth method, that returns the fork ID, chain ID, activation time, active precompiles and system contracts for the currently activated fork, and when applicable, the next and latest fork's the client has in its configuration. As part of implementing the precompiles side of things, the logic of how we check an address is a precompile was refactored, introducing a method that returns the precompiles per fork so as to more closely tie the addresses we return in the `eth-config` method and the way we check a precompile is active when executing it. To test this PR, you can follow the instructions in [this page](https://eest.ethereum.org/main/running_tests/execute/eth_config/). Keep in mind the tests require to have whichever chain they're being run on fully synced. Closes #3711
1 parent ad5a496 commit 7b08d9e

9 files changed

Lines changed: 639 additions & 175 deletions

File tree

crates/common/types/genesis.rs

Lines changed: 93 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,9 @@ impl TryFrom<&Path> for Genesis {
111111
)]
112112
#[serde(rename_all = "camelCase")]
113113
pub struct ForkBlobSchedule {
114-
pub target: u32,
115-
pub max: u32,
116114
pub base_fee_update_fraction: u64,
115+
pub max: u32,
116+
pub target: u32,
117117
}
118118

119119
#[allow(unused)]
@@ -460,6 +460,97 @@ impl ChainConfig {
460460
self.get_fork(block_timestamp)
461461
}
462462

463+
pub fn next_fork(&self, block_timestamp: u64) -> Option<Fork> {
464+
let next = if self.is_bpo5_activated(block_timestamp) {
465+
None
466+
} else if self.is_bpo4_activated(block_timestamp) && self.bpo5_time.is_some() {
467+
Some(Fork::BPO5)
468+
} else if self.is_bpo3_activated(block_timestamp) && self.bpo4_time.is_some() {
469+
Some(Fork::BPO4)
470+
} else if self.is_bpo2_activated(block_timestamp) && self.bpo3_time.is_some() {
471+
Some(Fork::BPO3)
472+
} else if self.is_bpo1_activated(block_timestamp) && self.bpo2_time.is_some() {
473+
Some(Fork::BPO2)
474+
} else if self.is_osaka_activated(block_timestamp) && self.bpo1_time.is_some() {
475+
Some(Fork::BPO1)
476+
} else if self.is_prague_activated(block_timestamp) && self.osaka_time.is_some() {
477+
Some(Fork::Osaka)
478+
} else if self.is_cancun_activated(block_timestamp) && self.prague_time.is_some() {
479+
Some(Fork::Prague)
480+
} else if self.is_shanghai_activated(block_timestamp) && self.cancun_time.is_some() {
481+
Some(Fork::Cancun)
482+
} else {
483+
None
484+
};
485+
match next {
486+
Some(fork) if fork > self.fork(block_timestamp) => next,
487+
_ => None,
488+
}
489+
}
490+
491+
pub fn get_last_scheduled_fork(&self) -> Fork {
492+
if self.bpo5_time.is_some() {
493+
Fork::BPO5
494+
} else if self.bpo4_time.is_some() {
495+
Fork::BPO4
496+
} else if self.bpo3_time.is_some() {
497+
Fork::BPO3
498+
} else if self.bpo2_time.is_some() {
499+
Fork::BPO2
500+
} else if self.bpo1_time.is_some() {
501+
Fork::BPO1
502+
} else if self.osaka_time.is_some() {
503+
Fork::Osaka
504+
} else if self.prague_time.is_some() {
505+
Fork::Prague
506+
} else if self.cancun_time.is_some() {
507+
Fork::Cancun
508+
} else {
509+
Fork::Paris
510+
}
511+
}
512+
513+
pub fn get_activation_timestamp_for_fork(&self, fork: Fork) -> Option<u64> {
514+
match fork {
515+
Fork::Cancun => self.cancun_time,
516+
Fork::Prague => self.prague_time,
517+
Fork::Osaka => self.osaka_time,
518+
Fork::BPO1 => self.bpo1_time,
519+
Fork::BPO2 => self.bpo2_time,
520+
Fork::BPO3 => self.bpo3_time,
521+
Fork::BPO4 => self.bpo4_time,
522+
Fork::BPO5 => self.bpo5_time,
523+
Fork::Homestead => self.homestead_block,
524+
Fork::DaoFork => self.dao_fork_block,
525+
Fork::Byzantium => self.byzantium_block,
526+
Fork::Constantinople => self.constantinople_block,
527+
Fork::Petersburg => self.petersburg_block,
528+
Fork::Istanbul => self.istanbul_block,
529+
Fork::MuirGlacier => self.muir_glacier_block,
530+
Fork::Berlin => self.berlin_block,
531+
Fork::London => self.london_block,
532+
Fork::ArrowGlacier => self.arrow_glacier_block,
533+
Fork::GrayGlacier => self.gray_glacier_block,
534+
Fork::Paris => self.merge_netsplit_block,
535+
Fork::Shanghai => self.shanghai_time,
536+
_ => None,
537+
}
538+
}
539+
540+
pub fn get_blob_schedule_for_fork(&self, fork: Fork) -> Option<ForkBlobSchedule> {
541+
match fork {
542+
Fork::Cancun => Some(self.blob_schedule.cancun),
543+
Fork::Prague => Some(self.blob_schedule.prague),
544+
Fork::Osaka => Some(self.blob_schedule.osaka),
545+
Fork::BPO1 => Some(self.blob_schedule.bpo1),
546+
Fork::BPO2 => Some(self.blob_schedule.bpo2),
547+
Fork::BPO3 => self.blob_schedule.bpo3,
548+
Fork::BPO4 => self.blob_schedule.bpo4,
549+
Fork::BPO5 => self.blob_schedule.bpo5,
550+
_ => None,
551+
}
552+
}
553+
463554
pub fn gather_forks(&self, genesis_header: BlockHeader) -> (Vec<u64>, Vec<u64>) {
464555
let mut block_number_based_forks: Vec<u64> = vec![
465556
self.homestead_block,

crates/networking/rpc/engine/blobs.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ impl RpcHandler for BlobsV1Request {
4444

4545
async fn handle(&self, context: RpcApiContext) -> Result<Value, RpcErr> {
4646
info!("Received new engine request: Requested Blobs");
47+
4748
if self.blob_versioned_hashes.len() >= GET_BLOBS_V1_REQUEST_MAX_SIZE {
4849
return Err(RpcErr::TooLargeRequest);
4950
}

crates/networking/rpc/eth/client.rs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1+
use std::collections::BTreeMap;
2+
3+
use ethrex_common::H32;
4+
use ethrex_common::H160;
15
use ethrex_common::serde_utils;
6+
use ethrex_common::types::Fork;
7+
use ethrex_common::types::ForkBlobSchedule;
8+
use ethrex_common::types::ForkId;
9+
use ethrex_vm::{precompiles_for_fork, system_contracts::system_contracts_for_fork};
210
use serde::{Deserialize, Serialize};
311
use serde_json::Value;
412
use tracing::debug;
@@ -62,3 +70,110 @@ impl RpcHandler for Syncing {
6270
}
6371
}
6472
}
73+
74+
pub struct Config;
75+
76+
#[derive(Debug, Serialize, Deserialize)]
77+
#[serde(rename_all = "camelCase")]
78+
struct EthConfigObject {
79+
activation_time: Option<u64>,
80+
blob_schedule: Option<ForkBlobSchedule>,
81+
#[serde(with = "serde_utils::u64::hex_str")]
82+
chain_id: u64,
83+
fork_id: H32,
84+
precompiles: BTreeMap<String, H160>,
85+
system_contracts: BTreeMap<String, H160>,
86+
}
87+
88+
#[derive(Debug, Serialize, Deserialize)]
89+
#[serde(rename_all = "camelCase")]
90+
struct EthConfigResponse {
91+
current: EthConfigObject,
92+
next: Option<EthConfigObject>,
93+
last: Option<EthConfigObject>,
94+
}
95+
96+
impl RpcHandler for Config {
97+
fn parse(_params: &Option<Vec<Value>>) -> Result<Self, RpcErr> {
98+
Ok(Self {})
99+
}
100+
101+
async fn handle(&self, context: RpcApiContext) -> Result<Value, RpcErr> {
102+
let chain_config = context.storage.get_chain_config()?;
103+
let Some(latest_block) = context
104+
.storage
105+
.get_block_by_number(context.storage.get_latest_block_number().await?)
106+
.await?
107+
else {
108+
return Err(RpcErr::Internal("Failed to fetch latest block".to_string()));
109+
};
110+
111+
let latest_block_timestamp = latest_block.header.timestamp;
112+
let current_fork = chain_config.get_fork(latest_block_timestamp);
113+
114+
if current_fork < Fork::Paris {
115+
return Err(RpcErr::UnsuportedFork(
116+
"eth-config is not supported for forks prior to Paris".to_string(),
117+
));
118+
}
119+
120+
let current = get_config_for_fork(current_fork, &context).await?;
121+
let next = if let Some(next_fork) = chain_config.next_fork(latest_block_timestamp) {
122+
Some(get_config_for_fork(next_fork, &context).await?)
123+
} else {
124+
None
125+
};
126+
let last_fork = chain_config.get_last_scheduled_fork();
127+
let last = if last_fork > current_fork {
128+
Some(get_config_for_fork(last_fork, &context).await?)
129+
} else {
130+
None
131+
};
132+
let response = EthConfigResponse {
133+
current,
134+
next,
135+
last,
136+
};
137+
138+
serde_json::to_value(response).map_err(|error| RpcErr::Internal(error.to_string()))
139+
}
140+
}
141+
142+
async fn get_config_for_fork(
143+
fork: Fork,
144+
context: &RpcApiContext,
145+
) -> Result<EthConfigObject, RpcErr> {
146+
let chain_config = context.storage.get_chain_config()?;
147+
let activation_time = chain_config.get_activation_timestamp_for_fork(fork);
148+
let genesis_header = context
149+
.storage
150+
.get_block_by_number(0)
151+
.await?
152+
.expect("Failed to get genesis block. This should not happen.")
153+
.header;
154+
let block_number = context.storage.get_latest_block_number().await?;
155+
let fork_id = if let Some(timestamp) = activation_time {
156+
ForkId::new(chain_config, genesis_header, timestamp, block_number).fork_hash
157+
} else {
158+
H32::zero()
159+
};
160+
let mut system_contracts = BTreeMap::new();
161+
for contract in system_contracts_for_fork(fork) {
162+
system_contracts.insert(contract.name.to_string(), contract.address);
163+
}
164+
165+
let mut precompiles = BTreeMap::new();
166+
167+
for precompile in precompiles_for_fork(fork) {
168+
precompiles.insert(precompile.name.to_string(), precompile.address);
169+
}
170+
171+
Ok(EthConfigObject {
172+
activation_time,
173+
blob_schedule: chain_config.get_blob_schedule_for_fork(fork),
174+
chain_id: chain_config.chain_id,
175+
fork_id,
176+
precompiles,
177+
system_contracts,
178+
})
179+
}

0 commit comments

Comments
 (0)