Skip to content
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### 2025-10-28

- Batch BlobsBundle::validate [#4993](https://github.com/lambdaclass/ethrex/pull/4993)
- Remove latest_block_header lock [#5050](https://github.com/lambdaclass/ethrex/pull/5050)

### 2025-10-27

Expand Down
7 changes: 7 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ ethrex-threadpool = { path = "./crates/concurrency" }
tracing = { version = "0.1", features = ["log"] }
tracing-subscriber = { version = "0.3.0", features = ["env-filter"] }

arc-swap = "1.7.1"
async-trait = "0.1.88"
ethereum-types = { version = "0.15.1", features = ["serialize"] }
serde = { version = "1.0.203", features = ["derive"] }
Expand Down
1 change: 1 addition & 0 deletions crates/l2/networking/rpc/l2/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ impl RpcHandler for SponsoredTx {
.await
.map_err(RpcErr::from)?;
let chain_config = context.l1_ctx.storage.get_chain_config();

let chain_id = chain_config.chain_id;
let nonce = context
.l1_ctx
Expand Down
7 changes: 7 additions & 0 deletions crates/l2/prover/src/guest_program/src/risc0/Cargo.lock

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

7 changes: 7 additions & 0 deletions crates/l2/prover/src/guest_program/src/sp1/Cargo.lock

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

7 changes: 7 additions & 0 deletions crates/l2/tee/quote-gen/Cargo.lock

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

1 change: 1 addition & 0 deletions crates/storage/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ ethrex-rlp.workspace = true
ethrex-common.workspace = true
ethrex-trie.workspace = true

arc-swap.workspace = true
async-trait.workspace = true
ethereum-types.workspace = true
anyhow = "1.0.86"
Expand Down
93 changes: 33 additions & 60 deletions crates/storage/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::store_db::in_memory::Store as InMemoryStore;
use crate::store_db::rocksdb::Store as RocksDBStore;
use crate::{api::StoreEngine, apply_prefix};

use arc_swap::ArcSwap;
use ethereum_types::{Address, H256, U256};
use ethrex_common::{
constants::EMPTY_TRIE_HASH,
Expand All @@ -17,11 +18,8 @@ use ethrex_rlp::decode::RLPDecode;
use ethrex_rlp::encode::RLPEncode;
use ethrex_trie::{Nibbles, NodeRLP, Trie, TrieLogger, TrieNode, TrieWitness};
use sha3::{Digest as _, Keccak256};
use std::collections::{BTreeMap, HashMap};
use std::{collections::hash_map::Entry, sync::Arc};
use std::{
collections::{BTreeMap, HashMap},
sync::RwLock,
};
use std::{fmt::Debug, path::Path};
use tracing::{debug, error, info, instrument};
/// Number of state trie segments to fetch concurrently during state sync
Expand All @@ -34,7 +32,13 @@ pub const MAX_SNAPSHOT_READS: usize = 100;
pub struct Store {
pub engine: Arc<dyn StoreEngine>,
pub chain_config: ChainConfig,
pub latest_block_header: Arc<RwLock<BlockHeader>>,
/// Keeps the latest canonical block hash
/// It's wrapped in an ArcSwap to allow for cheap lock-free reads with infrequent writes
/// Reading an out-of-date value is acceptable, since it's only used as:
/// - a cache of the (frequently requested) header
/// - a Latest tag for RPC, where a small extra delay before the newest block is expected
/// - sync-related operations, which must be idempotent in order to handle reorgs
pub latest_block_header: Arc<ArcSwap<BlockHeader>>,
}

pub type StorageTrieNodes = Vec<(H256, Vec<(Nibbles, Vec<u8>)>)>;
Expand Down Expand Up @@ -81,12 +85,12 @@ impl Store {
EngineType::RocksDB => Self {
engine: Arc::new(RocksDBStore::new(path)?),
chain_config: Default::default(),
latest_block_header: Arc::new(RwLock::new(BlockHeader::default())),
latest_block_header: Arc::new(ArcSwap::from_pointee(BlockHeader::default())),
},
EngineType::InMemory => Self {
engine: Arc::new(InMemoryStore::new()),
chain_config: Default::default(),
latest_block_header: Arc::new(RwLock::new(BlockHeader::default())),
latest_block_header: Arc::new(ArcSwap::from_pointee(BlockHeader::default())),
},
};

Expand Down Expand Up @@ -175,13 +179,9 @@ impl Store {
&self,
block_number: BlockNumber,
) -> Result<Option<BlockHeader>, StoreError> {
let latest = self
.latest_block_header
.read()
.map_err(|_| StoreError::LockError)?
.clone();
let latest = self.latest_block_header.load_full();
if block_number == latest.number {
return Ok(Some(latest));
return Ok(Some((*latest).clone()));
}
self.engine.get_block_header(block_number)
}
Expand All @@ -191,12 +191,9 @@ impl Store {
block_hash: BlockHash,
) -> Result<Option<BlockHeader>, StoreError> {
{
let latest = self
.latest_block_header
.read()
.map_err(|_| StoreError::LockError)?;
let latest = self.latest_block_header.load_full();
if block_hash == latest.hash() {
return Ok(Some(latest.clone()));
return Ok(Some((*latest).clone()));
}
}

Expand All @@ -223,11 +220,7 @@ impl Store {
block_number: BlockNumber,
) -> Result<Option<BlockBody>, StoreError> {
// FIXME (#4353)
let latest = self
.latest_block_header
.read()
.map_err(|_| StoreError::LockError)?
.clone();
let latest = self.latest_block_header.load_full();
if block_number == latest.number {
// The latest may not be marked as canonical yet
return self.engine.get_block_body_by_hash(latest.hash()).await;
Expand Down Expand Up @@ -634,14 +627,13 @@ impl Store {
// Set chain config
self.set_chain_config(&genesis.config).await?;

// The cache can't be empty
if let Some(number) = self.engine.get_latest_block_number().await? {
*self
.latest_block_header
.write()
.map_err(|_| StoreError::LockError)? = self
.engine
.get_block_header(number)?
.ok_or_else(|| StoreError::MissingLatestBlockNumber)?;
self.latest_block_header.store(Arc::new(
self.engine
.get_block_header(number)?
.ok_or_else(|| StoreError::MissingLatestBlockNumber)?,
));
}

match self.engine.get_block_header(genesis_block_number)? {
Expand Down Expand Up @@ -686,10 +678,8 @@ impl Store {
.engine
.get_block_header(number)?
.ok_or_else(|| StoreError::Custom("latest block header is missing".to_string()))?;
*self
.latest_block_header
.write()
.map_err(|_| StoreError::LockError)? = latest_block_header;
self.latest_block_header
.store(Arc::new(latest_block_header));
Ok(())
}

Expand Down Expand Up @@ -781,11 +771,7 @@ impl Store {
}

pub async fn get_latest_block_number(&self) -> Result<BlockNumber, StoreError> {
Ok(self
.latest_block_header
.read()
.map_err(|_| StoreError::LockError)?
.number)
Ok(self.latest_block_header.load().number)
}

pub async fn update_pending_block_number(
Expand All @@ -804,10 +790,7 @@ impl Store {
block_number: BlockNumber,
) -> Result<Option<BlockHash>, StoreError> {
{
let last = self
.latest_block_header
.read()
.map_err(|_| StoreError::LockError)?;
let last = self.latest_block_header.load();
if last.number == block_number {
return Ok(Some(last.hash()));
}
Expand All @@ -816,12 +799,7 @@ impl Store {
}

pub async fn get_latest_canonical_block_hash(&self) -> Result<Option<BlockHash>, StoreError> {
Ok(Some(
self.latest_block_header
.read()
.map_err(|_| StoreError::LockError)?
.hash(),
))
Ok(Some(self.latest_block_header.load().hash()))
}

/// Updates the canonical chain.
Expand All @@ -838,13 +816,11 @@ impl Store {
) -> Result<(), StoreError> {
// Updates first the latest_block_header
// to avoid nonce inconsistencies #3927.
*self
.latest_block_header
.write()
.map_err(|_| StoreError::LockError)? = self
.engine
.get_block_header_by_hash(head_hash)?
.ok_or_else(|| StoreError::MissingLatestBlockNumber)?;
self.latest_block_header.store(Arc::new(
self.engine
.get_block_header_by_hash(head_hash)?
.ok_or_else(|| StoreError::MissingLatestBlockNumber)?,
));
self.engine
.forkchoice_update(
new_canonical_blocks,
Expand Down Expand Up @@ -1324,10 +1300,7 @@ impl Store {
block_number: BlockNumber,
) -> Result<Option<BlockHash>, StoreError> {
{
let last = self
.latest_block_header
.read()
.map_err(|_| StoreError::LockError)?;
let last = self.latest_block_header.load();
if last.number == block_number {
return Ok(Some(last.hash()));
}
Expand Down
Loading