Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(pruning): Check pruning in metadata calculator #2286

Merged
merged 1 commit into from
Jun 20, 2024
Merged
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
4 changes: 3 additions & 1 deletion core/node/metadata_calculator/src/recovery/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,9 @@ impl AsyncTreeRecovery {
let actual_root_hash = tree.root_hash().await;
anyhow::ensure!(
actual_root_hash == snapshot.expected_root_hash,
"Root hash of recovered tree {actual_root_hash:?} differs from expected root hash {:?}",
"Root hash of recovered tree {actual_root_hash:?} differs from expected root hash {:?}. \
If pruning is enabled and the tree is initialized some time after node recovery, \
this is caused by snapshot storage logs getting pruned; this setup is currently not supported",
snapshot.expected_root_hash
);
let tree = tree.finalize().await?;
Expand Down
40 changes: 40 additions & 0 deletions core/node/metadata_calculator/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,46 @@ async fn multi_l1_batch_workflow() {
}
}

#[tokio::test]
async fn error_on_pruned_next_l1_batch() {
let pool = ConnectionPool::<Core>::test_pool().await;
let temp_dir = TempDir::new().expect("failed get temporary directory for RocksDB");
let (calculator, _) = setup_calculator(temp_dir.path(), pool.clone()).await;
reset_db_state(&pool, 1).await;
run_calculator(calculator).await;

// Add some new blocks to the storage and mock their partial pruning.
let mut storage = pool.connection().await.unwrap();
let new_logs = gen_storage_logs(100..200, 10);
extend_db_state(&mut storage, new_logs).await;
storage
.pruning_dal()
.soft_prune_batches_range(L1BatchNumber(5), L2BlockNumber(5))
.await
.unwrap();
storage
.pruning_dal()
.hard_prune_batches_range(L1BatchNumber(5), L2BlockNumber(5))
.await
.unwrap();
// Sanity check: there should be no pruned batch headers.
let next_l1_batch_header = storage
.blocks_dal()
.get_l1_batch_header(L1BatchNumber(2))
.await
.unwrap();
assert!(next_l1_batch_header.is_none());

let (calculator, _) = setup_calculator(temp_dir.path(), pool.clone()).await;
let (_stop_sender, stop_receiver) = watch::channel(false);
let err = calculator.run(stop_receiver).await.unwrap_err();
let err = format!("{err:#}");
assert!(
err.contains("L1 batch #2, next to be processed by the tree, is pruned"),
"{err}"
);
}

#[tokio::test]
async fn running_metadata_calculator_with_additional_blocks() {
let pool = ConnectionPool::<Core>::test_pool().await;
Expand Down
15 changes: 15 additions & 0 deletions core/node/metadata_calculator/src/updater.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ impl TreeUpdater {
for l1_batch_number in l1_batch_numbers {
let l1_batch_number = L1BatchNumber(l1_batch_number);
let Some(current_l1_batch_data) = l1_batch_data else {
Self::ensure_not_pruned(storage, l1_batch_number).await?;
return Ok(l1_batch_number);
};
total_logs += current_l1_batch_data.storage_logs.len();
Expand Down Expand Up @@ -167,6 +168,20 @@ impl TreeUpdater {
Ok(last_l1_batch_number + 1)
}

/// Checks whether the requested L1 batch was pruned. Right now, the tree cannot recover from this situation,
/// so we exit with an error if this happens.
async fn ensure_not_pruned(
storage: &mut Connection<'_, Core>,
l1_batch_number: L1BatchNumber,
) -> anyhow::Result<()> {
let pruning_info = storage.pruning_dal().get_pruning_info().await?;
anyhow::ensure!(
Some(l1_batch_number) > pruning_info.last_soft_pruned_l1_batch,
"L1 batch #{l1_batch_number}, next to be processed by the tree, is pruned; the tree cannot continue operating"
);
Ok(())
}

async fn step(
&mut self,
mut storage: Connection<'_, Core>,
Expand Down
Loading