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
24 changes: 10 additions & 14 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 @@ -432,7 +428,7 @@ where
}

let next_ts = chain_index
.tipset_by_height(
.load_required_tipset_by_height(
lbr + 1,
heaviest_tipset.clone(),
ResolveNullTipset::TakeNewer,
Expand Down
62 changes: 48 additions & 14 deletions src/chain/store/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,10 @@ 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 `Ok(Some(tipset))` when epoch `to` resolves. Returns `Ok(None)` if the ancestor
/// walk completes without resolving `to` (for example missing parent tipsets). Returns `Err`
/// if `to` is greater than `from.epoch()` or genesis lookup fails when `to` is zero.
///
/// # Why pass in the `from` argument?
///
Expand Down Expand Up @@ -162,7 +164,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 +192,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 +217,28 @@ 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)
}

/// Same as [`Self::tipset_by_height`], but errors if that would return `None`.
pub fn load_required_tipset_by_height(
&self,
to: ChainEpoch,
from: Tipset,
resolve: ResolveNullTipset,
) -> Result<Tipset, Error> {
self.tipset_by_height(to, from, resolve)?
.ok_or_else(|| Error::NotFound(format!("tipset at epoch {to}").into()))
}

/// Finds the latest beacon entry given a tipset up to 20 tipsets behind
Expand Down Expand Up @@ -304,14 +315,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 +353,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_broken_ancestor_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, ResolveNullTipset::TakeOlder)
.unwrap()
.is_none()
);
}
}
2 changes: 1 addition & 1 deletion src/dev/subcommands/export_state_tree_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ impl ExportStateTreeCommand {
genesis_header,
)?);

let start_ts = chain_store.chain_index().tipset_by_height(
let start_ts = chain_store.chain_index().load_required_tipset_by_height(
from,
chain_store.heaviest_tipset(),
ResolveNullTipset::TakeNewer,
Expand Down
12 changes: 9 additions & 3 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,17 @@ 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(
let ts = chain_index.load_required_tipset_by_height(
epoch,
chain_store.heaviest_tipset(),
ResolveNullTipset::TakeOlder,
)?;
let ts_next = chain_store.load_child_tipset(&ts)?;
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,7 +223,7 @@ 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(
let ts = chain_index.load_required_tipset_by_height(
epoch,
chain_store.heaviest_tipset(),
ResolveNullTipset::TakeOlder,
Expand Down
2 changes: 1 addition & 1 deletion src/interpreter/fvm3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ impl<DB: Blockstore> Chain for ForestExterns<DB> {
fn get_tipset_cid(&self, epoch: ChainEpoch) -> anyhow::Result<Cid> {
let ts = self
.chain_index
.tipset_by_height(
.load_required_tipset_by_height(
epoch,
self.heaviest_tipset.clone(),
ResolveNullTipset::TakeOlder,
Expand Down
2 changes: 1 addition & 1 deletion src/interpreter/fvm4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ impl<DB: Blockstore> Chain for ForestExterns<DB> {
fn get_tipset_cid(&self, epoch: ChainEpoch) -> anyhow::Result<Cid> {
let ts = self
.chain_index
.tipset_by_height(
.load_required_tipset_by_height(
epoch,
self.heaviest_tipset.clone(),
ResolveNullTipset::TakeOlder,
Expand Down
2 changes: 1 addition & 1 deletion src/message_pool/msgpool/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ impl<DB: Blockstore> Provider for ChainStore<DB> {
_ => {
let lookback_ts = if ts.epoch() > self.chain_config().policy.chain_finality {
self.chain_index()
.tipset_by_height(
.load_required_tipset_by_height(
ts.epoch() - self.chain_config().policy.chain_finality,
ts.clone(),
ResolveNullTipset::TakeOlder,
Expand Down
Loading
Loading