Skip to content
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@

- [#5869](https://github.com/ChainSafe/forest/pull/5869) Updated `forest-cli snapshot export` to print average speed.

- [#5969](https://github.com/ChainSafe/forest/pull/5969) Updated `forest-tool snapshot validate` to print better error message for troubleshooting.

### Removed

### Fixed
Expand Down
4 changes: 4 additions & 0 deletions src/cli_shared/logger/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ fn default_env_filter() -> EnvFilter {
fn default_tool_filter() -> EnvFilter {
let default_directives = [
"info",
"bellperson::groth16::aggregate::verify=warn",
"storage_proofs_core=warn",
"axum=warn",
"filecoin_proofs=warn",
"forest::snapshot=info",
"forest::progress=info",
"libp2p_bitswap=off",
Expand Down
33 changes: 27 additions & 6 deletions src/ipld/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,16 @@ impl Iterator for DfsIter {
}
}

enum IterateType {
Message(Cid),
StateRoot(Cid),
}

enum Task {
// Yield the block, don't visit it.
Emit(Cid, Option<Vec<u8>>),
// Visit all the elements, recursively.
Iterate(VecDeque<Cid>),
Iterate(ChainEpoch, Cid, IterateType, VecDeque<Cid>),
}

pin_project! {
Expand Down Expand Up @@ -195,12 +200,12 @@ impl<DB: Blockstore, T: Borrow<Tipset>, ITER: Iterator<Item = T> + Unpin> Stream
return Poll::Ready(Some(Ok(CarBlock { cid, data })));
} else if fail_on_dead_links {
return Poll::Ready(Some(Err(anyhow::anyhow!(
"missing key: {cid}"
"[Emit] missing key: {cid}"
))));
};
}
}
Iterate(cid_vec) => {
Iterate(epoch, block_cid, _type, cid_vec) => {
while let Some(cid) = cid_vec.pop_front() {
// The link traversal implementation assumes there are three types of encoding:
// 1. DAG_CBOR: needs to be reachable, so we add it to the queue and load.
Expand All @@ -220,8 +225,16 @@ impl<DB: Blockstore, T: Borrow<Tipset>, ITER: Iterator<Item = T> + Unpin> Stream
}
return Poll::Ready(Some(Ok(CarBlock { cid, data })));
} else if fail_on_dead_links {
let type_display = match _type {
IterateType::Message(c) => {
format!("message {c}")
}
IterateType::StateRoot(c) => {
format!("state root {c}")
}
};
return Poll::Ready(Some(Err(anyhow::anyhow!(
"missing key: {cid}"
"[Iterate] missing key: {cid} from {type_display} in block {block_cid} at epoch {epoch}"
))));
}
}
Expand Down Expand Up @@ -251,6 +264,9 @@ impl<DB: Blockstore, T: Borrow<Tipset>, ITER: Iterator<Item = T> + Unpin> Stream
// Process block messages.
if block.epoch > stateroot_limit {
this.dfs.push_back(Iterate(
block.epoch,
*block.cid(),
IterateType::Message(block.messages),
DfsIter::from(block.messages)
.filter_map(ipld_to_cid)
.collect(),
Expand All @@ -263,6 +279,9 @@ impl<DB: Blockstore, T: Borrow<Tipset>, ITER: Iterator<Item = T> + Unpin> Stream
// NOTE: In the original `walk_snapshot` implementation we walk the dag
// immediately. Which is what we do here as well, but using a queue.
this.dfs.push_back(Iterate(
block.epoch,
*block.cid(),
IterateType::StateRoot(block.state_root),
DfsIter::from(block.state_root)
.filter_map(ipld_to_cid)
.collect(),
Expand Down Expand Up @@ -429,7 +448,9 @@ impl<
// If the receiving end has already quit - just ignore it and
// break out of the loop.
let _ = block_sender
.send_async(Err(anyhow::anyhow!("missing key: {cid}")))
.send_async(Err(anyhow::anyhow!(
"[Send] missing key: {cid}"
)))
.await;
break 'main;
}
Expand Down Expand Up @@ -499,7 +520,7 @@ impl<'a, DB: Blockstore + Send + Sync + 'static, T: Iterator<Item = Tipset> + Un
} else if let Some(data) = self.db.get(&cid)? {
return Poll::Ready(Some(Ok(CarBlock { cid, data })));
} else if fail_on_dead_links {
return Poll::Ready(Some(Err(anyhow::anyhow!("missing key: {cid}"))));
return Poll::Ready(Some(Err(anyhow::anyhow!("[Poll] missing key: {cid}"))));
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/state_manager/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1727,7 +1727,7 @@ where
NO_CALLBACK,
VMTrace::NotTraced,
)
.context("couldn't compute tipset state")?;
.map_err(|e| anyhow::anyhow!("couldn't compute tipset state: {e}"))?;
let expected_receipt = child.min_ticket_block().message_receipts;
let expected_state = child.parent_state();
match (expected_state, expected_receipt) == (&actual_state, actual_receipt) {
Expand Down
6 changes: 4 additions & 2 deletions src/tool/subcommands/snapshot_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,10 @@ where
),
);

let last_epoch = ts.epoch() - epochs as i64;
// Fix off-by-1 bug: prevent validating more epochs than available in the snapshot.
// Without +1, specifying --check-stateroots=900 would validate 901 epochs,
// causing out-of-bounds errors when the snapshot contains only 900 recent state roots.
let last_epoch = ts.epoch() - epochs as i64 + 1;
Comment thread
coderabbitai[bot] marked this conversation as resolved.

// Bundles are required when doing state migrations.
load_actor_bundles(&db, &network).await?;
Expand Down Expand Up @@ -445,7 +448,6 @@ where
)?;

pb.finish_with_message("✅ verified!");
drop(pb);
Ok(())
}

Expand Down
Loading