Skip to content

Commit

Permalink
Added a new option --validate-tipsets in forest-cli validate command (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
sudo-shashank authored Jul 14, 2023
1 parent 6fcbd8a commit c00871a
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 20 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@

### Added

- [#3167](https://github.com/ChainSafe/forest/pull/3167): Added a new option
`--validate-tipsets` for `forest-cli snapshot validate`.
- [#3166](https://github.com/ChainSafe/forest/issues/3166): Add
`forest-cli archive info` command for inspecting archives.
- [#3159](https://github.com/ChainSafe/forest/issues/3159): Add
Expand Down
2 changes: 1 addition & 1 deletion scripts/tests/forest_cli_check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ pushd "$(mktemp --directory)"

validate_me=$(find . -type f | head -1)
: : validating under calibnet chain should succeed
"$FOREST_CLI_PATH" --chain calibnet snapshot validate "$validate_me"
"$FOREST_CLI_PATH" --chain calibnet snapshot validate "$validate_me" --validate-tipsets=-1000

: : validating under mainnet chain should fail
if "$FOREST_CLI_PATH" --chain mainnet snapshot validate "$validate_me"; then
Expand Down
9 changes: 9 additions & 0 deletions src/blocks/tipset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,15 @@ impl Tipset {
}
broken
}
/// Returns an iterator of all tipsets
pub fn chain(self, store: impl Blockstore) -> impl Iterator<Item = Tipset> {
itertools::unfold(Some(self), move |tipset| {
tipset.take().map(|child| {
*tipset = Tipset::load(&store, child.parents()).ok().flatten();
child
})
})
}
}

/// `FullTipset` is an expanded version of a tipset that contains all the blocks
Expand Down
60 changes: 48 additions & 12 deletions src/cli/subcommands/snapshot_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@ use crate::car_backed_blockstore::{
use crate::chain::ChainStore;
use crate::cli::subcommands::{cli_error_and_die, handle_rpc_err};
use crate::cli_shared::snapshot::{self, TrustedVendor};
use crate::fil_cns::composition as cns;
use crate::genesis::read_genesis_header;
use crate::ipld::{recurse_links_hash, CidHashSet};
use crate::networks::NetworkChain;
use crate::rpc_api::{chain_api::ChainExportParams, progress_api::GetProgressType};
use crate::rpc_client::{chain_ops::*, progress_ops::get_progress};
use crate::shim::clock::ChainEpoch;
use crate::utils::io::ProgressBar;
use anyhow::bail;
use crate::state_manager::StateManager;
use crate::utils::{io::ProgressBar, proofs_api::paramfetch::ensure_params_downloaded};
use anyhow::{bail, Context};
use chrono::Utc;
use clap::Subcommand;
use fvm_ipld_blockstore::Blockstore;
Expand Down Expand Up @@ -56,6 +58,10 @@ pub enum SnapshotCommands {
/// Number of block headers to validate from the tip
#[arg(long, default_value = "2000")]
recent_stateroots: i64,
/// Validate already computed tipsets at given EPOCH,
/// use a negative value -N to validate the last N EPOCH(s) starting at HEAD.
#[arg(long)]
validate_tipsets: Option<i64>,
/// Path to a snapshot CAR, which may be zstd compressed
snapshot: PathBuf,
},
Expand Down Expand Up @@ -157,6 +163,7 @@ impl SnapshotCommands {
}
Self::Validate {
recent_stateroots,
validate_tipsets,
snapshot,
} => {
// this is all blocking...
Expand All @@ -168,6 +175,7 @@ impl SnapshotCommands {
store.roots(),
Arc::new(store),
recent_stateroots,
*validate_tipsets,
)
.await
}
Expand All @@ -187,6 +195,7 @@ impl SnapshotCommands {
store.roots(),
Arc::new(store),
recent_stateroots,
*validate_tipsets,
)
.await
}
Expand Down Expand Up @@ -240,42 +249,71 @@ async fn validate_with_blockstore<BlockstoreT>(
roots: Vec<Cid>,
store: Arc<BlockstoreT>,
recent_stateroots: &i64,
) -> Result<(), anyhow::Error>
validate_tipsets: Option<i64>,
) -> anyhow::Result<()>
where
BlockstoreT: Blockstore + Send + Sync,
BlockstoreT: Blockstore + Send + Sync + 'static,
{
let genesis = read_genesis_header(
config.client.genesis_file.as_ref(),
config.chain.genesis_bytes(),
&store,
)
.await?;

let chain_data_root = TempDir::new()?;
let chain_store = Arc::new(ChainStore::new(
store,
Arc::clone(&store),
config.chain.clone(),
&genesis,
TempDir::new()?.path(),
chain_data_root.path(),
)?);

let ts = chain_store.tipset_from_keys(&TipsetKeys::new(roots))?;
let ts = Tipset::load(&store, &TipsetKeys::new(roots))?.context("missing root tipset")?;

validate_links_and_genesis_traversal(
&chain_store,
ts,
&ts,
chain_store.blockstore(),
*recent_stateroots,
&Tipset::from(genesis),
&config.chain.network,
)
.await?;

if let Some(validate_from) = validate_tipsets {
let last_epoch = match validate_from.is_negative() {
true => ts.epoch() + validate_from,
false => validate_from,
};
// Set proof parameter data dir
if cns::FETCH_PARAMS {
crate::utils::proofs_api::paramfetch::set_proofs_parameter_cache_dir_env(
&config.client.data_dir,
);
}
// Initialize StateManager
let state_manager = Arc::new(StateManager::new(
chain_store,
Arc::clone(&config.chain),
Arc::new(crate::interpreter::RewardActorMessageCalc),
)?);
ensure_params_downloaded().await?;
// Prepare tipset stream to validate
let tipsets = ts
.chain(&store)
.map(|ts| Arc::clone(&Arc::new(ts)))
.take_while(|tipset| tipset.epoch() >= last_epoch);

state_manager.validate_tipsets(tipsets)?
}

println!("Snapshot is valid");
Ok(())
}

async fn validate_links_and_genesis_traversal<DB>(
chain_store: &ChainStore<DB>,
ts: Arc<Tipset>,
ts: &Tipset,
db: &DB,
recent_stateroots: ChainEpoch,
genesis_tipset: &Tipset,
Expand Down Expand Up @@ -340,7 +378,5 @@ where

drop(pb);

println!("Snapshot is valid");

Ok(())
}
6 changes: 4 additions & 2 deletions src/daemon/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -432,8 +432,10 @@ pub(super) async fn start(
assert!(current_height.is_positive());
match validate_from.is_negative() {
// allow --height=-1000 to scroll back from the current head
true => state_manager.validate((current_height + validate_from)..=current_height)?,
false => state_manager.validate(validate_from..=current_height)?,
true => {
state_manager.validate_range((current_height + validate_from)..=current_height)?
}
false => state_manager.validate_range(validate_from..=current_height)?,
}
}

Expand Down
16 changes: 11 additions & 5 deletions src/state_manager/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1250,9 +1250,7 @@ where
/// This function is blocking, but we do observe threads waiting and synchronizing.
/// This is suspected to be due something in the VM or its `WASM` runtime.
#[tracing::instrument(skip(self))]
pub fn validate(self: &Arc<Self>, epochs: RangeInclusive<i64>) -> anyhow::Result<()> {
use rayon::iter::ParallelIterator as _;

pub fn validate_range(self: &Arc<Self>, epochs: RangeInclusive<i64>) -> anyhow::Result<()> {
let heaviest = self.cs.heaviest_tipset();
let heaviest_epoch = heaviest.epoch();
let end = self
Expand All @@ -1269,10 +1267,18 @@ where
// if this has parents, unfold them in the next iteration
*tipset = self.cs.tipset_from_keys(child.parents()).ok();
Some(child)
});
})
.take_while(|tipset| tipset.epoch() >= *epochs.start());

self.validate_tipsets(tipsets)
}

pub fn validate_tipsets<T>(self: &Arc<Self>, tipsets: T) -> anyhow::Result<()>
where
T: Iterator<Item = Arc<Tipset>> + Send,
{
use rayon::iter::ParallelIterator as _;
tipsets
.take_while(|tipset| tipset.epoch() >= *epochs.start())
.tuple_windows()
.par_bridge()
.try_for_each(|(child, parent)| {
Expand Down

0 comments on commit c00871a

Please sign in to comment.