Skip to content
Open
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
26 changes: 15 additions & 11 deletions crates/networking/rpc/eth/gas_tip_estimator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,19 @@ impl GasTipEstimator {
return Err(RpcErr::Internal("Error calculating gas price".to_string()));
}
let mut results = vec![];
// TODO: Estimating gas price involves querying multiple blocks
// and doing some calculations with each of them, let's consider
// caching this result, also we can have a specific DB method
// that returns a block range to not query them one-by-one.
for block_num in block_range {
let Some(block_body) = storage.get_block_body(block_num).await? else {

// Bulk fetch all block bodies and headers for the range
let bodies = storage
.get_block_bodies(block_range_lower_bound, latest_block_number)
.await?;
let headers = storage
.get_block_headers(block_range_lower_bound, latest_block_number)
.await?;

for (idx, (block_body, header)) in bodies.into_iter().zip(headers.into_iter()).enumerate() {
let block_num = block_range_lower_bound + idx as u64;

let Some(block_body) = block_body else {
error!(
"Block body for block number {block_num} is missing but is below the latest known block!"
);
Expand All @@ -85,11 +92,8 @@ impl GasTipEstimator {
));
};

let base_fee = storage
.get_block_header(block_num)
.ok()
.flatten()
.and_then(|header| header.base_fee_per_gas);
// Get the base fee for the block
let base_fee = header.and_then(|h| h.base_fee_per_gas);

// Previously we took the gas_price, now we take the effective_gas_tip and add the base_fee in the RPC
// call if needed.
Expand Down
7 changes: 7 additions & 0 deletions crates/storage/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ pub trait StoreEngine: Debug + Send + Sync + RefUnwindSafe {
block_number: BlockNumber,
) -> Result<Option<BlockHeader>, StoreError>;

/// Obtain canonical block headers in from..=to
async fn get_block_headers(
&self,
from: BlockNumber,
to: BlockNumber,
) -> Result<Vec<Option<BlockHeader>>, StoreError>;

/// Add block body
async fn add_block_body(
&self,
Expand Down
71 changes: 71 additions & 0 deletions crates/storage/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,14 @@ impl Store {
self.engine.get_block_header(block_number)
}

pub async fn get_block_headers(
&self,
from: BlockNumber,
to: BlockNumber,
) -> Result<Vec<Option<BlockHeader>>, StoreError> {
self.engine.get_block_headers(from, to).await
}

pub fn get_block_header_by_hash(
&self,
block_hash: BlockHash,
Expand Down Expand Up @@ -1502,6 +1510,7 @@ mod tests {
run_test(test_genesis_block, engine_type).await;
run_test(test_iter_accounts, engine_type).await;
run_test(test_iter_storage, engine_type).await;
run_test(test_get_block_headers_and_bodies_bulk, engine_type).await;
}

async fn test_iter_accounts(store: Store) {
Expand Down Expand Up @@ -1626,6 +1635,68 @@ mod tests {
assert_eq!(stored_body, block_body);
}

async fn test_get_block_headers_and_bodies_bulk(store: Store) {
// Create and store multiple blocks
let mut headers = Vec::new();
let mut bodies = Vec::new();

for i in 0..5u64 {
let (mut header, body) = create_block_for_testing();
header.number = i;
// Make them unique by modifying the timestamp
header.timestamp = 1000 + i;
let hash = header.hash();

store.add_block_header(hash, header.clone()).await.unwrap();
store.add_block_body(hash, body.clone()).await.unwrap();
store
.forkchoice_update(None, i, hash, None, None)
.await
.unwrap();

headers.push(header);
bodies.push(body);
}

// Test bulk fetch of headers
let fetched_headers = store.get_block_headers(0, 4).await.unwrap();
assert_eq!(fetched_headers.len(), 5);
for (i, fetched) in fetched_headers.into_iter().enumerate() {
let fetched = fetched.expect("header should exist");
// Ensure hashes are computed for comparison
let _ = fetched.hash();
let _ = headers[i].hash();
assert_eq!(fetched, headers[i]);
}

// Test bulk fetch of bodies
let fetched_bodies = store.get_block_bodies(0, 4).await.unwrap();
assert_eq!(fetched_bodies.len(), 5);
for (i, fetched) in fetched_bodies.into_iter().enumerate() {
let fetched = fetched.expect("body should exist");
assert_eq!(fetched, bodies[i]);
}

// Test partial range
let partial_headers = store.get_block_headers(2, 4).await.unwrap();
assert_eq!(partial_headers.len(), 3);
for (i, fetched) in partial_headers.into_iter().enumerate() {
let fetched = fetched.expect("header should exist");
let _ = fetched.hash();
let _ = headers[i + 2].hash();
assert_eq!(fetched, headers[i + 2]);
}

// Test range with missing blocks (beyond stored range)
let extended_headers = store.get_block_headers(3, 7).await.unwrap();
assert_eq!(extended_headers.len(), 5);
assert!(extended_headers[0].is_some());
assert!(extended_headers[1].is_some());
assert!(extended_headers[2].is_none());
assert!(extended_headers[3].is_none());
assert!(extended_headers[4].is_none());
}

fn create_block_for_testing() -> (BlockHeader, BlockBody) {
let block_header = BlockHeader {
parent_hash: H256::from_str(
Expand Down
17 changes: 17 additions & 0 deletions crates/storage/store_db/in_memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,23 @@ impl StoreEngine for Store {
}
}

async fn get_block_headers(
&self,
from: BlockNumber,
to: BlockNumber,
) -> Result<Vec<Option<BlockHeader>>, StoreError> {
let store = self.inner()?;
let mut res = Vec::new();
for block_number in from..=to {
let header_opt = store
.canonical_hashes
.get(&block_number)
.and_then(|hash| store.headers.get(hash));
res.push(header_opt.cloned());
}
Ok(res)
}

async fn get_block_body(&self, block_number: u64) -> Result<Option<BlockBody>, StoreError> {
let store = self.inner()?;
if let Some(hash) = store.canonical_hashes.get(&block_number) {
Expand Down
49 changes: 49 additions & 0 deletions crates/storage/store_db/rocksdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1102,6 +1102,55 @@ impl StoreEngine for Store {
self.get_block_header_by_hash(block_hash)
}

async fn get_block_headers(
&self,
from: BlockNumber,
to: BlockNumber,
) -> Result<Vec<Option<BlockHeader>>, StoreError> {
let numbers: Vec<BlockNumber> = (from..=to).collect();
let number_keys: Vec<Vec<u8>> = numbers.iter().map(|n| n.to_le_bytes().to_vec()).collect();

let hashes = self
.read_bulk_async(CF_CANONICAL_BLOCK_HASHES, number_keys, |bytes| {
BlockHashRLP::from_bytes(bytes)
.to()
.map_err(StoreError::from)
})
.await?;

let hash_keys: Vec<Vec<u8>> = hashes
.iter()
.flat_map(|hash_opt| hash_opt.map(|h| BlockHashRLP::from(h).bytes().clone()))
.collect();

let mut headers = self
.read_bulk_async(CF_HEADERS, hash_keys, |bytes| {
BlockHeaderRLP::from_bytes(bytes)
.to()
.map_err(StoreError::from)
})
.await?;

let mut i = 0;

// Fill in with None for missing headers
let headers = hashes
.into_iter()
.map(|opt| {
opt.and_then(|_| {
let header_ref = headers
.get_mut(i)
.expect("headers length is equal to number of Somes in hashes");
let header = std::mem::take(header_ref);
i += 1;
header
})
})
.collect();

Ok(headers)
}

async fn add_block_body(
&self,
block_hash: BlockHash,
Expand Down