feat: add canonical_headers_range (#1795)

Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
This commit is contained in:
Matthias Seitz
2023-03-17 01:06:32 +01:00
committed by GitHub
parent 57894f7b98
commit 241ec32abf
16 changed files with 114 additions and 49 deletions

1
Cargo.lock generated
View File

@ -5109,7 +5109,6 @@ dependencies = [
"auto_impl",
"bytes",
"criterion",
"enr 0.8.0",
"ethereum-types",
"ethnum",
"hex-literal",

View File

@ -516,8 +516,8 @@ pub fn verify_receipt<'a>(
mod tests {
use super::*;
use reth_primitives::{
hex_literal::hex, keccak256, Account, Address, Bytecode, Bytes, ChainSpecBuilder,
ForkCondition, StorageKey, H256, MAINNET, U256,
hex_literal::hex, keccak256, Account, Address, BlockNumber, Bytecode, Bytes,
ChainSpecBuilder, ForkCondition, StorageKey, H256, MAINNET, U256,
};
use reth_provider::{
post_state::{Change, Storage},
@ -531,7 +531,7 @@ mod tests {
struct StateProviderTest {
accounts: HashMap<Address, (HashMap<StorageKey, U256>, Account)>,
contracts: HashMap<H256, Bytecode>,
block_hash: HashMap<U256, H256>,
block_hash: HashMap<u64, H256>,
}
impl StateProviderTest {
@ -560,9 +560,22 @@ mod tests {
}
impl BlockHashProvider for StateProviderTest {
fn block_hash(&self, number: U256) -> reth_interfaces::Result<Option<H256>> {
fn block_hash(&self, number: u64) -> reth_interfaces::Result<Option<H256>> {
Ok(self.block_hash.get(&number).cloned())
}
fn canonical_hashes_range(
&self,
start: BlockNumber,
end: BlockNumber,
) -> reth_interfaces::Result<Vec<H256>> {
let range = start..end;
Ok(self
.block_hash
.iter()
.filter_map(|(block, hash)| if range.contains(block) { Some(*hash) } else { None })
.collect())
}
}
impl StateProvider for StateProviderTest {

View File

@ -30,10 +30,10 @@ impl<'a, SP: StateProvider> PostStateProvider<'a, SP> {
}
}
/* Implement StateProvider traits */
impl<'a, SP: StateProvider> BlockHashProvider for PostStateProvider<'a, SP> {
fn block_hash(&self, number: U256) -> Result<Option<H256>> {
// All block numbers fit inside u64 and revm checks if it is last 256 block numbers.
let block_number = number.as_limbs()[0];
fn block_hash(&self, block_number: BlockNumber) -> Result<Option<H256>> {
if let Some(sidechain_block_hash) = self.sidechain_block_hashes.get(&block_number).cloned()
{
return Ok(Some(sidechain_block_hash))
@ -46,6 +46,10 @@ impl<'a, SP: StateProvider> BlockHashProvider for PostStateProvider<'a, SP> {
.ok_or(ProviderError::BlockchainTreeBlockHash { block_number })?,
))
}
fn canonical_hashes_range(&self, _start: BlockNumber, _end: BlockNumber) -> Result<Vec<H256>> {
unimplemented!()
}
}
impl<'a, SP: StateProvider> AccountProvider for PostStateProvider<'a, SP> {

View File

@ -7,7 +7,7 @@ use reth_eth_wire::{
GetReceipts, NodeData, Receipts,
};
use reth_interfaces::p2p::error::RequestResult;
use reth_primitives::{BlockHashOrNumber, Header, HeadersDirection, PeerId, U256};
use reth_primitives::{BlockHashOrNumber, Header, HeadersDirection, PeerId};
use reth_provider::{BlockProvider, HeaderProvider};
use std::{
borrow::Borrow,
@ -82,7 +82,7 @@ where
let mut block: BlockHashOrNumber = match start_block {
BlockHashOrNumber::Hash(start) => start.into(),
BlockHashOrNumber::Number(num) => {
if let Some(hash) = self.client.block_hash(U256::from(num)).unwrap_or_default() {
if let Some(hash) = self.client.block_hash(num).unwrap_or_default() {
hash.into()
} else {
return headers

View File

@ -63,6 +63,7 @@ impl<DB: StateProvider> DatabaseRef for State<DB> {
}
fn block_hash(&self, number: U256) -> Result<H256, Self::Error> {
Ok(self.0.block_hash(number)?.unwrap_or_default())
// Note: this unwrap is potentially unsafe
Ok(self.0.block_hash(number.try_into().unwrap())?.unwrap_or_default())
}
}

View File

@ -462,7 +462,7 @@ impl<Client: HeaderProvider + BlockProvider + StateProviderFactory + EvmEnvProvi
}
// Attempt to look up terminal block hash
let local_hash = self.client.block_hash(U256::from(terminal_block_number.as_u64()))?;
let local_hash = self.client.block_hash(terminal_block_number.as_u64())?;
// Transition configuration exchange is successful if block hashes match
match local_hash {

View File

@ -310,8 +310,7 @@ where
}
}
let oldest_block_hash =
self.inner.client.block_hash(start_block.try_into().unwrap()).to_rpc_result()?.unwrap();
let oldest_block_hash = self.inner.client.block_hash(start_block).to_rpc_result()?.unwrap();
fee_history_cache_items.get_mut(&start_block).unwrap().hash = Some(oldest_block_hash);
fee_history_cache.get_mut(&start_block).unwrap().hash = Some(oldest_block_hash);

View File

@ -5,10 +5,7 @@ use crate::{
};
use async_trait::async_trait;
use jsonrpsee::{core::RpcResult, server::IdProvider};
use reth_primitives::{
filter::{Filter, FilterBlockOption, FilteredParams},
U256,
};
use reth_primitives::filter::{Filter, FilterBlockOption, FilteredParams};
use reth_provider::{BlockProvider, EvmEnvProvider};
use reth_rpc_api::EthFilterApiServer;
use reth_rpc_types::{FilterChanges, FilterId, Log};
@ -96,7 +93,7 @@ where
let block_hash = self
.inner
.client
.block_hash(U256::from(block_num))
.block_hash(block_num)
.to_rpc_result()?
.ok_or(EthApiError::UnknownBlockNumber)?;
block_hashes.push(block_hash);

View File

@ -94,10 +94,20 @@ impl<DB: Database> HeaderProvider for ShareableDatabase<DB> {
}
impl<DB: Database> BlockHashProvider for ShareableDatabase<DB> {
fn block_hash(&self, number: U256) -> Result<Option<H256>> {
// TODO: This unwrap is potentially unsafe
fn block_hash(&self, number: u64) -> Result<Option<H256>> {
self.db.view(|tx| tx.get::<tables::CanonicalHeaders>(number))?.map_err(Into::into)
}
fn canonical_hashes_range(&self, start: BlockNumber, end: BlockNumber) -> Result<Vec<H256>> {
let range = start..end;
self.db
.view(|tx| tx.get::<tables::CanonicalHeaders>(number.try_into().unwrap()))?
.view(|tx| {
let mut cursor = tx.cursor_read::<tables::CanonicalHeaders>()?;
cursor
.walk_range(range)?
.map(|result| result.map(|(_, hash)| hash).map_err(Into::into))
.collect::<Result<Vec<_>>>()
})?
.map_err(Into::into)
}
}
@ -109,7 +119,7 @@ impl<DB: Database> BlockIdProvider for ShareableDatabase<DB> {
.view(|tx| tx.get::<tables::SyncStage>("Finish".to_string()))?
.map_err(Into::<reth_interfaces::db::Error>::into)?
.unwrap_or_default();
let best_hash = self.block_hash(U256::from(best_number))?.unwrap_or_default();
let best_hash = self.block_hash(best_number)?.unwrap_or_default();
Ok(ChainInfo { best_hash, best_number, last_finalized: None, safe_finalized: None })
}

View File

@ -10,7 +10,7 @@ use reth_db::{
};
use reth_interfaces::Result;
use reth_primitives::{
Account, Address, Bytecode, Bytes, StorageKey, StorageValue, TransitionId, H256, U256,
Account, Address, BlockNumber, Bytecode, Bytes, StorageKey, StorageValue, TransitionId, H256,
};
use std::marker::PhantomData;
@ -72,8 +72,21 @@ impl<'a, 'b, TX: DbTx<'a>> AccountProvider for HistoricalStateProviderRef<'a, 'b
impl<'a, 'b, TX: DbTx<'a>> BlockHashProvider for HistoricalStateProviderRef<'a, 'b, TX> {
/// Get block hash by number.
fn block_hash(&self, number: U256) -> Result<Option<H256>> {
self.tx.get::<tables::CanonicalHeaders>(number.to::<u64>()).map_err(Into::into)
fn block_hash(&self, number: u64) -> Result<Option<H256>> {
self.tx.get::<tables::CanonicalHeaders>(number).map_err(Into::into)
}
fn canonical_hashes_range(&self, start: BlockNumber, end: BlockNumber) -> Result<Vec<H256>> {
let range = start..end;
self.tx
.cursor_read::<tables::CanonicalHeaders>()
.map(|mut cursor| {
cursor
.walk_range(range)?
.map(|result| result.map(|(_, hash)| hash).map_err(Into::into))
.collect::<Result<Vec<_>>>()
})?
.map_err(Into::into)
}
}

View File

@ -9,8 +9,8 @@ use reth_db::{
};
use reth_interfaces::{provider::ProviderError, Result};
use reth_primitives::{
keccak256, Account, Address, Bytecode, Bytes, StorageKey, StorageValue, H256, KECCAK_EMPTY,
U256,
keccak256, Account, Address, BlockNumber, Bytecode, Bytes, StorageKey, StorageValue, H256,
KECCAK_EMPTY,
};
use std::marker::PhantomData;
@ -38,8 +38,21 @@ impl<'a, 'b, TX: DbTx<'a>> AccountProvider for LatestStateProviderRef<'a, 'b, TX
impl<'a, 'b, TX: DbTx<'a>> BlockHashProvider for LatestStateProviderRef<'a, 'b, TX> {
/// Get block hash by number.
fn block_hash(&self, number: U256) -> Result<Option<H256>> {
self.db.get::<tables::CanonicalHeaders>(number.to::<u64>()).map_err(Into::into)
fn block_hash(&self, number: u64) -> Result<Option<H256>> {
self.db.get::<tables::CanonicalHeaders>(number).map_err(Into::into)
}
fn canonical_hashes_range(&self, start: BlockNumber, end: BlockNumber) -> Result<Vec<H256>> {
let range = start..end;
self.db
.cursor_read::<tables::CanonicalHeaders>()
.map(|mut cursor| {
cursor
.walk_range(range)?
.map(|result| result.map(|(_, hash)| hash).map_err(Into::into))
.collect::<Result<Vec<_>>>()
})?
.map_err(Into::into)
}
}

View File

@ -5,12 +5,12 @@
///
/// Used to implement provider traits.
macro_rules! delegate_impls_to_as_ref {
(for $target:ty => $($trait:ident $(where [$($generics:tt)*])? { $(fn $func:ident(&self, $($arg:ident: $argty:ty),*) -> $ret:path;)* })* ) => {
(for $target:ty => $($trait:ident $(where [$($generics:tt)*])? { $(fn $func:ident$(<$($generic_arg:ident: $generic_arg_ty:path),*>)?(&self, $($arg:ident: $argty:ty),*) -> $ret:path;)* })* ) => {
$(
impl<'a, $($($generics)*)?> $trait for $target {
$(
fn $func(&self, $($arg: $argty),*) -> $ret {
fn $func$(<$($generic_arg: $generic_arg_ty),*>)?(&self, $($arg: $argty),*) -> $ret {
self.as_ref().$func($($arg),*)
}
)*
@ -34,7 +34,8 @@ macro_rules! delegate_provider_impls {
fn basic_account(&self, address: reth_primitives::Address) -> reth_interfaces::Result<Option<reth_primitives::Account>>;
}
BlockHashProvider $(where [$($generics)*])? {
fn block_hash(&self, number: reth_primitives::U256) -> reth_interfaces::Result<Option<reth_primitives::H256>>;
fn block_hash(&self, number: u64) -> reth_interfaces::Result<Option<reth_primitives::H256>>;
fn canonical_hashes_range(&self, start: reth_primitives::BlockNumber, end: reth_primitives::BlockNumber) -> reth_interfaces::Result<Vec<reth_primitives::H256>>;
}
StateProvider $(where [$($generics)*])?{
fn storage(&self, account: reth_primitives::Address, storage_key: reth_primitives::StorageKey) -> reth_interfaces::Result<Option<reth_primitives::StorageValue>>;

View File

@ -124,7 +124,12 @@ impl HeaderProvider for MockEthProvider {
range: impl RangeBounds<reth_primitives::BlockNumber>,
) -> Result<Vec<Header>> {
let lock = self.headers.lock();
Ok(lock.values().filter(|header| range.contains(&header.number)).cloned().collect())
let mut headers: Vec<_> =
lock.values().filter(|header| range.contains(&header.number)).cloned().collect();
headers.sort_by_key(|header| header.number);
Ok(headers)
}
}
@ -171,21 +176,24 @@ impl ReceiptProvider for MockEthProvider {
}
impl BlockHashProvider for MockEthProvider {
fn block_hash(&self, number: U256) -> Result<Option<H256>> {
fn block_hash(&self, number: u64) -> Result<Option<H256>> {
let lock = self.blocks.lock();
let hash =
lock.iter().find_map(
|(hash, b)| {
if b.number == number.to::<u64>() {
Some(*hash)
} else {
None
}
},
);
lock.iter().find_map(|(hash, b)| if b.number == number { Some(*hash) } else { None });
Ok(hash)
}
fn canonical_hashes_range(&self, start: BlockNumber, end: BlockNumber) -> Result<Vec<H256>> {
let range = start..end;
let lock = self.blocks.lock();
let mut hashes: Vec<_> =
lock.iter().filter(|(_, block)| range.contains(&block.number)).collect();
hashes.sort_by_key(|(_, block)| block.number);
Ok(hashes.into_iter().map(|(hash, _)| *hash).collect())
}
}
impl BlockIdProvider for MockEthProvider {

View File

@ -18,9 +18,13 @@ pub struct NoopProvider;
/// Noop implementation for testing purposes
impl BlockHashProvider for NoopProvider {
fn block_hash(&self, _number: U256) -> Result<Option<H256>> {
fn block_hash(&self, _number: u64) -> Result<Option<H256>> {
Ok(None)
}
fn canonical_hashes_range(&self, _start: BlockNumber, _end: BlockNumber) -> Result<Vec<H256>> {
Ok(vec![])
}
}
impl BlockIdProvider for NoopProvider {

View File

@ -1,11 +1,14 @@
use auto_impl::auto_impl;
use reth_interfaces::Result;
use reth_primitives::{H256, U256};
use reth_primitives::{BlockNumber, H256};
/// Client trait for fetching block hashes by number.
#[auto_impl(&, Arc, Box)]
pub trait BlockHashProvider: Send + Sync {
/// Get the hash of the block with the given number. Returns `None` if no block with this number
/// exists.
fn block_hash(&self, number: U256) -> Result<Option<H256>>;
fn block_hash(&self, number: BlockNumber) -> Result<Option<H256>>;
/// Get headers in range of block hashes or numbers
fn canonical_hashes_range(&self, start: BlockNumber, end: BlockNumber) -> Result<Vec<H256>>;
}

View File

@ -1,6 +1,6 @@
use super::BlockHashProvider;
use reth_interfaces::Result;
use reth_primitives::{BlockId, BlockNumberOrTag, ChainInfo, H256, U256};
use reth_primitives::{BlockId, BlockNumberOrTag, ChainInfo, H256};
/// Client trait for transforming [BlockId].
#[auto_impl::auto_impl(&, Arc)]
@ -33,7 +33,7 @@ pub trait BlockIdProvider: BlockHashProvider + Send + Sync {
return Ok(Some(self.chain_info()?.best_hash))
}
self.convert_block_number(num)?
.map(|num| self.block_hash(U256::from(num)))
.map(|num| self.block_hash(num))
.transpose()
.map(|maybe_hash| maybe_hash.flatten())
}