mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-02-19 03:04:27 -05:00
fix(provider): check executed block before returning historical state (#21571)
Co-authored-by: Amp <amp@ampcode.com> Co-authored-by: joshieDo <93316087+joshieDo@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
e9fe0283a9
commit
70bfdafd26
@@ -104,6 +104,14 @@ pub enum ProviderError {
|
||||
/// State is not available for the given block number because it is pruned.
|
||||
#[error("state at block #{_0} is pruned")]
|
||||
StateAtBlockPruned(BlockNumber),
|
||||
/// State is not available because the block has not been executed yet.
|
||||
#[error("state at block #{requested} is not available, block has not been executed yet (latest executed: #{executed})")]
|
||||
BlockNotExecuted {
|
||||
/// The block number that was requested.
|
||||
requested: BlockNumber,
|
||||
/// The latest executed block number.
|
||||
executed: BlockNumber,
|
||||
},
|
||||
/// Block data is not available because history has expired.
|
||||
///
|
||||
/// The requested block number is below the earliest available block.
|
||||
|
||||
@@ -825,10 +825,19 @@ impl<TX: DbTx + 'static, N: NodeTypes> TryIntoHistoricalStateProvider for Databa
|
||||
self,
|
||||
mut block_number: BlockNumber,
|
||||
) -> ProviderResult<StateProviderBox> {
|
||||
// if the block number is the same as the currently best block number on disk we can use the
|
||||
// latest state provider here
|
||||
if block_number == self.best_block_number().unwrap_or_default() {
|
||||
return Ok(Box::new(LatestStateProvider::new(self)))
|
||||
let best_block = self.best_block_number().unwrap_or_default();
|
||||
|
||||
// Reject requests for blocks beyond the best block
|
||||
if block_number > best_block {
|
||||
return Err(ProviderError::BlockNotExecuted {
|
||||
requested: block_number,
|
||||
executed: best_block,
|
||||
});
|
||||
}
|
||||
|
||||
// If requesting state at the best block, use the latest state provider
|
||||
if block_number == best_block {
|
||||
return Ok(Box::new(LatestStateProvider::new(self)));
|
||||
}
|
||||
|
||||
// +1 as the changeset that we want is the one that was applied after this block.
|
||||
@@ -3605,7 +3614,7 @@ mod tests {
|
||||
let data = BlockchainTestData::default();
|
||||
|
||||
let provider_rw = factory.provider_rw().unwrap();
|
||||
provider_rw.insert_block(&data.genesis.clone().try_recover().unwrap()).unwrap();
|
||||
provider_rw.insert_block(&data.genesis.try_recover().unwrap()).unwrap();
|
||||
provider_rw
|
||||
.write_state(
|
||||
&ExecutionOutcome { first_block: 0, receipts: vec![vec![]], ..Default::default() },
|
||||
@@ -3638,7 +3647,7 @@ mod tests {
|
||||
let data = BlockchainTestData::default();
|
||||
|
||||
let provider_rw = factory.provider_rw().unwrap();
|
||||
provider_rw.insert_block(&data.genesis.clone().try_recover().unwrap()).unwrap();
|
||||
provider_rw.insert_block(&data.genesis.try_recover().unwrap()).unwrap();
|
||||
provider_rw
|
||||
.write_state(
|
||||
&ExecutionOutcome { first_block: 0, receipts: vec![vec![]], ..Default::default() },
|
||||
@@ -3675,7 +3684,7 @@ mod tests {
|
||||
let data = BlockchainTestData::default();
|
||||
|
||||
let provider_rw = factory.provider_rw().unwrap();
|
||||
provider_rw.insert_block(&data.genesis.clone().try_recover().unwrap()).unwrap();
|
||||
provider_rw.insert_block(&data.genesis.try_recover().unwrap()).unwrap();
|
||||
provider_rw
|
||||
.write_state(
|
||||
&ExecutionOutcome { first_block: 0, receipts: vec![vec![]], ..Default::default() },
|
||||
@@ -3713,7 +3722,7 @@ mod tests {
|
||||
let data = BlockchainTestData::default();
|
||||
|
||||
let provider_rw = factory.provider_rw().unwrap();
|
||||
provider_rw.insert_block(&data.genesis.clone().try_recover().unwrap()).unwrap();
|
||||
provider_rw.insert_block(&data.genesis.try_recover().unwrap()).unwrap();
|
||||
provider_rw
|
||||
.write_state(
|
||||
&ExecutionOutcome { first_block: 0, receipts: vec![vec![]], ..Default::default() },
|
||||
@@ -3783,7 +3792,7 @@ mod tests {
|
||||
let data = BlockchainTestData::default();
|
||||
|
||||
let provider_rw = factory.provider_rw().unwrap();
|
||||
provider_rw.insert_block(&data.genesis.clone().try_recover().unwrap()).unwrap();
|
||||
provider_rw.insert_block(&data.genesis.try_recover().unwrap()).unwrap();
|
||||
provider_rw
|
||||
.write_state(
|
||||
&ExecutionOutcome { first_block: 0, receipts: vec![vec![]], ..Default::default() },
|
||||
@@ -4178,4 +4187,42 @@ mod tests {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_into_history_rejects_unexecuted_blocks() {
|
||||
use reth_storage_api::TryIntoHistoricalStateProvider;
|
||||
|
||||
let factory = create_test_provider_factory();
|
||||
|
||||
// Insert genesis block to have some data
|
||||
let data = BlockchainTestData::default();
|
||||
let provider_rw = factory.provider_rw().unwrap();
|
||||
provider_rw.insert_block(&data.genesis.try_recover().unwrap()).unwrap();
|
||||
provider_rw
|
||||
.write_state(
|
||||
&ExecutionOutcome { first_block: 0, receipts: vec![vec![]], ..Default::default() },
|
||||
crate::OriginalValuesKnown::No,
|
||||
StateWriteConfig::default(),
|
||||
)
|
||||
.unwrap();
|
||||
provider_rw.commit().unwrap();
|
||||
|
||||
// Get a fresh provider - Execution checkpoint is 0, no receipts written beyond genesis
|
||||
let provider = factory.provider().unwrap();
|
||||
|
||||
// Requesting historical state for block 0 (executed) should succeed
|
||||
let result = provider.try_into_history_at_block(0);
|
||||
assert!(result.is_ok(), "Block 0 should be available");
|
||||
|
||||
// Get another provider and request state for block 100 (not executed)
|
||||
let provider = factory.provider().unwrap();
|
||||
let result = provider.try_into_history_at_block(100);
|
||||
|
||||
// Should fail with BlockNotExecuted error
|
||||
match result {
|
||||
Err(ProviderError::BlockNotExecuted { requested: 100, .. }) => {}
|
||||
Err(e) => panic!("Expected BlockNotExecuted error, got: {e:?}"),
|
||||
Ok(_) => panic!("Expected error, got Ok"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user