Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
31 changes: 16 additions & 15 deletions src/chain/store/chain_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,27 +313,23 @@ where
}
}

pub fn load_child_tipset(&self, ts: &Tipset) -> Result<Tipset, Error> {
/// Returns [`None`] when `ts` has no known child on the current heaviest chain
/// (e.g. `ts` is the chain head). Blockstore errors are returned as [`Err`].
pub fn load_child_tipset(&self, ts: &Tipset) -> Result<Option<Tipset>, Error> {
let head = self.heaviest_tipset();
if head.parents() == ts.key() {
Ok(head)
Ok(Some(head))
} else if head.epoch() > ts.epoch() {
let maybe_child = self.chain_index().tipset_by_height(
match self.chain_index().tipset_by_height(
ts.epoch() + 1,
head,
ResolveNullTipset::TakeNewer,
)?;
if maybe_child.parents() == ts.key() {
Ok(maybe_child)
} else {
Err(Error::NotFound(
format!("child of tipset@{}", ts.epoch()).into(),
))
)? {
Some(maybe_child) if maybe_child.parents() == ts.key() => Ok(Some(maybe_child)),
_ => Ok(None),
}
} else {
Err(Error::NotFound(
format!("child of tipset@{}", ts.epoch()).into(),
))
Ok(None)
}
}

Expand Down Expand Up @@ -436,8 +432,13 @@ where
lbr + 1,
heaviest_tipset.clone(),
ResolveNullTipset::TakeNewer,
)
.map_err(|e| Error::Other(format!("Could not get tipset by height {e:?}")))?;
)?
.ok_or_else(|| {
Error::Other(format!(
"Could not get tipset at height {} (lookback round {round}, lbr {lbr})",
lbr + 1
))
})?;
if lbr > next_ts.epoch() {
return Err(Error::Other(format!(
"failed to find non-null tipset {:?} {} which is known to exist, found {:?} {}",
Expand Down
52 changes: 38 additions & 14 deletions src/chain/store/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,11 @@ impl<DB: Blockstore> ChainIndex<DB> {
}

/// Find tipset at epoch `to` in the chain of ancestors starting at `from`.
/// If the tipset is _not_ in the chain of ancestors (i.e., if the `to`
/// epoch is higher than `from.epoch()`), an error will be returned.
///
/// Returns [`None`] when the chain walk from `from` does not contain a
/// matching epoch (including when parent links are missing in the store).
/// Returns an error if `to` is greater than `from.epoch()`, or for
/// blockstore/genesis failures.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
///
/// # Why pass in the `from` argument?
///
Expand Down Expand Up @@ -162,7 +165,7 @@ impl<DB: Blockstore> ChainIndex<DB> {
to: ChainEpoch,
mut from: Tipset,
resolve: ResolveNullTipset,
) -> Result<Tipset, Error> {
) -> Result<Option<Tipset>, Error> {
use crate::shim::policy::policy_constants::CHAIN_FINALITY;

// use `20` as checkpoint interval to match Lotus:
Expand Down Expand Up @@ -190,7 +193,7 @@ impl<DB: Blockstore> ChainIndex<DB> {
}

if to == 0 {
return Ok(Tipset::from(from.genesis(&self.db)?));
return Ok(Some(Tipset::from(from.genesis(&self.db)?)));
}
if to > from.epoch() {
return Err(Error::Other(format!(
Expand All @@ -215,19 +218,17 @@ impl<DB: Blockstore> ChainIndex<DB> {
}

if to == child.epoch() {
return Ok(child);
return Ok(Some(child));
}
if to > parent.epoch() {
// We're at a point where child.epoch() > x > parent.epoch().
match resolve {
ResolveNullTipset::TakeOlder => return Ok(parent),
ResolveNullTipset::TakeNewer => return Ok(child),
ResolveNullTipset::TakeOlder => return Ok(Some(parent)),
ResolveNullTipset::TakeNewer => return Ok(Some(child)),
}
}
}
Comment thread
sudo-shashank marked this conversation as resolved.
Err(Error::Other(format!(
"Tipset with epoch={to} does not exist"
)))
Ok(None)
}

/// Finds the latest beacon entry given a tipset up to 20 tipsets behind
Expand Down Expand Up @@ -304,14 +305,16 @@ mod tests {
assert_eq!(
index
.tipset_by_height(2, epoch4.clone(), ResolveNullTipset::TakeOlder)
.unwrap(),
.unwrap()
.expect("epoch 2 resolved"),
epoch1
);

assert_eq!(
index
.tipset_by_height(2, epoch4, ResolveNullTipset::TakeNewer)
.unwrap(),
.unwrap()
.expect("epoch 2 resolved"),
epoch3
);
}
Expand Down Expand Up @@ -340,15 +343,36 @@ mod tests {
assert_eq!(
index
.tipset_by_height(2, epoch3a, ResolveNullTipset::TakeOlder)
.unwrap(),
.unwrap()
.expect("epoch 2 on branch a"),
epoch2a
);

assert_eq!(
index
.tipset_by_height(2, epoch3b, ResolveNullTipset::TakeOlder)
.unwrap(),
.unwrap()
.expect("epoch 2 on branch b"),
epoch2b
);
}

#[test]
fn tipset_by_height_not_on_chain_returns_none() {
let db = Arc::new(MemoryDB::default());
let genesis = genesis_tipset();
// Epoch 3 header points at a parent key we never persist — `Tipset::chain` stops
// after this tipset, so `tipset_by_height` finds no `(child, parent)` window.
let epoch3 = tipset_child(&tipset_child(&genesis, 2), 3);
persist_tipset(&genesis, &db);
persist_tipset(&epoch3, &db);

let index = ChainIndex::new(db);
assert!(
index
.tipset_by_height(2, epoch3.clone(), ResolveNullTipset::TakeOlder)
.unwrap()
.is_none()
);
}
}
13 changes: 8 additions & 5 deletions src/dev/subcommands/export_state_tree_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,14 @@ impl ExportStateTreeCommand {
genesis_header,
)?);

let start_ts = chain_store.chain_index().tipset_by_height(
from,
chain_store.heaviest_tipset(),
ResolveNullTipset::TakeNewer,
)?;
let start_ts = chain_store
.chain_index()
.tipset_by_height(
from,
chain_store.heaviest_tipset(),
ResolveNullTipset::TakeNewer,
)?
.with_context(|| format!("tipset not found at epoch {from}"))?;

let mut ipld_roots = vec![];
for (child, ts) in start_ts
Expand Down
32 changes: 21 additions & 11 deletions src/dev/subcommands/state_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use crate::{
tool::subcommands::api_cmd::generate_test_snapshot,
utils::ShallowClone as _,
};
use anyhow::Context as _;
use human_repr::HumanCount as _;
use nonzero_ext::nonzero;
use std::{num::NonZeroUsize, path::PathBuf, sync::Arc, time::Instant};
Expand Down Expand Up @@ -86,12 +87,19 @@ impl ComputeCommand {
let (ts, ts_next) = {
// We don't want to track all entries that are visited by `tipset_by_height`
db.pause_tracking();
let ts = chain_index.tipset_by_height(
epoch,
chain_store.heaviest_tipset(),
ResolveNullTipset::TakeOlder,
)?;
let ts_next = chain_store.load_child_tipset(&ts)?;
let ts = chain_index
.tipset_by_height(
epoch,
chain_store.heaviest_tipset(),
ResolveNullTipset::TakeOlder,
)?
.with_context(|| format!("tipset not found at epoch {epoch}"))?;
let ts_next = chain_store.load_child_tipset(&ts)?.with_context(|| {
format!(
"no child tipset for epoch {} (may be chain head)",
ts.epoch()
)
})?;
db.resume_tracking();
SettingsStoreExt::write_obj(
&db.tracker,
Expand Down Expand Up @@ -217,11 +225,13 @@ impl ValidateCommand {
let ts = {
// We don't want to track all entries that are visited by `tipset_by_height`
db.pause_tracking();
let ts = chain_index.tipset_by_height(
epoch,
chain_store.heaviest_tipset(),
ResolveNullTipset::TakeOlder,
)?;
let ts = chain_index
.tipset_by_height(
epoch,
chain_store.heaviest_tipset(),
ResolveNullTipset::TakeOlder,
)?
.with_context(|| format!("tipset not found at epoch {epoch}"))?;
db.resume_tracking();
SettingsStoreExt::write_obj(&db.tracker, crate::db::setting_keys::HEAD_KEY, ts.key())?;
// Only track the desired tipset
Expand Down
4 changes: 2 additions & 2 deletions src/interpreter/fvm3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,8 @@ impl<DB: Blockstore> Chain for ForestExterns<DB> {
epoch,
self.heaviest_tipset.clone(),
ResolveNullTipset::TakeOlder,
)
.context("Failed to get tipset cid")?;
)?
.with_context(|| format!("tipset not found at epoch {epoch}"))?;
Comment thread
sudo-shashank marked this conversation as resolved.
Outdated
ts.key().cid()
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/interpreter/fvm4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,8 @@ impl<DB: Blockstore> Chain for ForestExterns<DB> {
epoch,
self.heaviest_tipset.clone(),
ResolveNullTipset::TakeOlder,
)
.context("Failed to get tipset cid")?;
)?
.with_context(|| format!("tipset not found at epoch {epoch}"))?;
ts.key().cid()
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/message_pool/msgpool/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,12 @@ impl<DB: Blockstore> Provider for ChainStore<DB> {
ResolveNullTipset::TakeOlder,
)
.map_err(|e| Error::Other(e.to_string()))?
.ok_or_else(|| {
Error::Other(format!(
"tipset not found at finality lookback height {}",
ts.epoch() - self.chain_config().policy.chain_finality
))
})?
} else {
// Matches the logic at <https://github.com/filecoin-project/lotus/blob/v1.35.1/chain/stmgr/stmgr.go#L361>
ts.clone()
Expand Down
Loading
Loading