Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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