Skip to content

Commit bdaeb41

Browse files
chore(trie): Use trie changesets for engine unwinding
1 parent 7381462 commit bdaeb41

File tree

9 files changed

+355
-167
lines changed

9 files changed

+355
-167
lines changed

crates/cli/commands/src/stage/unwind.rs

Lines changed: 23 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -60,54 +60,35 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> Command<C>
6060

6161
let components = components(provider_factory.chain_spec());
6262

63-
let highest_static_file_block = provider_factory
64-
.static_file_provider()
65-
.get_highest_static_files()
66-
.max_block_num()
67-
.filter(|highest_static_file_block| *highest_static_file_block > target);
68-
69-
// Execute a pipeline unwind if the start of the range overlaps the existing static
70-
// files. If that's the case, then copy all available data from MDBX to static files, and
71-
// only then, proceed with the unwind.
72-
//
73-
// We also execute a pipeline unwind if `offline` is specified, because we need to only
74-
// unwind the data associated with offline stages.
75-
if highest_static_file_block.is_some() || self.offline {
76-
if self.offline {
77-
info!(target: "reth::cli", "Performing an unwind for offline-only data!");
78-
}
79-
80-
if let Some(highest_static_file_block) = highest_static_file_block {
81-
info!(target: "reth::cli", ?target, ?highest_static_file_block, "Executing a pipeline unwind.");
82-
} else {
83-
info!(target: "reth::cli", ?target, "Executing a pipeline unwind.");
84-
}
85-
info!(target: "reth::cli", prune_config=?config.prune, "Using prune settings");
86-
87-
// This will build an offline-only pipeline if the `offline` flag is enabled
88-
let mut pipeline =
89-
self.build_pipeline(config, provider_factory, components.evm_config().clone())?;
63+
// Sanity check that we have static files available to unwind to the target block.
64+
let highest_static_file_block =
65+
provider_factory.static_file_provider().get_highest_static_files().max_block_num();
66+
if highest_static_file_block
67+
.filter(|highest_static_file_block| *highest_static_file_block > target)
68+
.is_none()
69+
{
70+
return Err(eyre::eyre!("static files not available for target block {target}, highest is {highest_static_file_block:?}"));
71+
}
9072

91-
// Move all applicable data from database to static files.
92-
pipeline.move_to_static_files()?;
73+
if self.offline {
74+
info!(target: "reth::cli", "Performing an unwind for offline-only data!");
75+
}
9376

94-
pipeline.unwind(target, None)?;
77+
if let Some(highest_static_file_block) = highest_static_file_block {
78+
info!(target: "reth::cli", ?target, ?highest_static_file_block, "Executing a pipeline unwind.");
9579
} else {
96-
info!(target: "reth::cli", ?target, "Executing a database unwind.");
97-
let provider = provider_factory.provider_rw()?;
80+
info!(target: "reth::cli", ?target, "Executing a pipeline unwind.");
81+
}
82+
info!(target: "reth::cli", prune_config=?config.prune, "Using prune settings");
9883

99-
provider
100-
.remove_block_and_execution_above(target)
101-
.map_err(|err| eyre::eyre!("Transaction error on unwind: {err}"))?;
84+
// This will build an offline-only pipeline if the `offline` flag is enabled
85+
let mut pipeline =
86+
self.build_pipeline(config, provider_factory, components.evm_config().clone())?;
10287

103-
// update finalized block if needed
104-
let last_saved_finalized_block_number = provider.last_finalized_block_number()?;
105-
if last_saved_finalized_block_number.is_none_or(|f| f > target) {
106-
provider.save_finalized_block_number(target)?;
107-
}
88+
// Move all applicable data from database to static files.
89+
pipeline.move_to_static_files()?;
10890

109-
provider.commit()?;
110-
}
91+
pipeline.unwind(target, None)?;
11192

11293
info!(target: "reth::cli", ?target, "Unwound blocks");
11394

crates/ethereum/node/tests/e2e/p2p.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@ async fn e2e_test_send_transactions() -> eyre::Result<()> {
9191
Ok(())
9292
}
9393

94-
#[ignore] // TODO(mediocregopher): re-enable as part of https://github.com/paradigmxyz/reth/issues/18517
9594
#[tokio::test]
9695
async fn test_long_reorg() -> eyre::Result<()> {
9796
reth_tracing::init_test_tracing();

crates/storage/db-api/src/models/accounts.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,44 @@ impl Decode for BlockNumberHashedAddress {
145145
}
146146
}
147147

148+
/// A [`RangeBounds`] over a range of [`BlockNumberHashedAddress`]s. Used to conveniently convert
149+
/// from a range of [`BlockNumber`]s.
150+
#[derive(Debug)]
151+
pub struct BlockNumberHashedAddressRange {
152+
/// Starting bound of the range.
153+
pub start: Bound<BlockNumberHashedAddress>,
154+
/// Ending bound of the range.
155+
pub end: Bound<BlockNumberHashedAddress>,
156+
}
157+
158+
impl RangeBounds<BlockNumberHashedAddress> for BlockNumberHashedAddressRange {
159+
fn start_bound(&self) -> Bound<&BlockNumberHashedAddress> {
160+
self.start.as_ref()
161+
}
162+
163+
fn end_bound(&self) -> Bound<&BlockNumberHashedAddress> {
164+
self.end.as_ref()
165+
}
166+
}
167+
168+
impl<R: RangeBounds<BlockNumber>> From<R> for BlockNumberHashedAddressRange {
169+
fn from(r: R) -> Self {
170+
let start = match r.start_bound() {
171+
Bound::Included(n) => Bound::Included(BlockNumberHashedAddress((*n, B256::ZERO))),
172+
Bound::Excluded(n) => Bound::Included(BlockNumberHashedAddress((n + 1, B256::ZERO))),
173+
Bound::Unbounded => Bound::Unbounded,
174+
};
175+
176+
let end = match r.end_bound() {
177+
Bound::Included(n) => Bound::Excluded(BlockNumberHashedAddress((n + 1, B256::ZERO))),
178+
Bound::Excluded(n) => Bound::Excluded(BlockNumberHashedAddress((*n, B256::ZERO))),
179+
Bound::Unbounded => Bound::Unbounded,
180+
};
181+
182+
Self { start, end }
183+
}
184+
}
185+
148186
/// [`Address`] concatenated with [`StorageKey`]. Used by `reth_etl` and history stages.
149187
///
150188
/// Since it's used as a key, it isn't compressed when encoding it.

0 commit comments

Comments
 (0)