diff --git a/src/chain/store/chain_store.rs b/src/chain/store/chain_store.rs
index 714c1f596ae6..b8a3beca6fb1 100644
--- a/src/chain/store/chain_store.rs
+++ b/src/chain/store/chain_store.rs
@@ -319,16 +319,16 @@ impl ChainStore {
/// 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, Error> {
+ pub async fn load_child_tipset(&self, ts: &Tipset) -> Result , Error> {
let head = self.heaviest_tipset();
if head.parents() == ts.key() {
Ok(Some(head))
} else if head.epoch() > ts.epoch() {
- match self.chain_index().tipset_by_height(
- ts.epoch() + 1,
- head,
- ResolveNullTipset::TakeNewer,
- )? {
+ match self
+ .chain_index()
+ .tipset_by_height_async(ts.epoch() + 1, head, ResolveNullTipset::TakeNewer)
+ .await?
+ {
Some(maybe_child) if maybe_child.parents() == ts.key() => Ok(Some(maybe_child)),
_ => Ok(None),
}
@@ -431,7 +431,7 @@ impl ChainStore {
}
let next_ts = chain_index
- .load_required_tipset_by_height(
+ .load_required_tipset_by_height_blocking(
lbr + 1,
heaviest_tipset.clone(),
ResolveNullTipset::TakeNewer,
diff --git a/src/chain/store/index.rs b/src/chain/store/index.rs
index 0c839cc2e56e..f533facca875 100644
--- a/src/chain/store/index.rs
+++ b/src/chain/store/index.rs
@@ -240,6 +240,7 @@ impl ChainIndex {
Ok(None)
}
+ /// Non-blocking version of [`Self::tipset_by_height`]
pub async fn tipset_by_height_async(
&self,
to: ChainEpoch,
@@ -247,13 +248,13 @@ impl ChainIndex {
resolve: ResolveNullTipset,
) -> Result , Error> {
let this = self.shallow_clone();
- tokio::task::spawn_blocking(move || this.tipset_by_height(to, from, resolve))
- .await
- .map_err(|e| Error::Other(e.to_string()))?
+ tokio::task::spawn_blocking(move || this.tipset_by_height(to, from, resolve)).await?
}
/// Same as [`Self::tipset_by_height`], but errors if that would return `None`.
- pub fn load_required_tipset_by_height(
+ /// This call can be expensive and blocking, use [`Self::load_required_tipset_by_height`]
+ /// in async contexts to avoid exhausting Tokio worker threads.
+ pub fn load_required_tipset_by_height_blocking(
&self,
to: ChainEpoch,
from: Tipset,
@@ -263,6 +264,18 @@ impl ChainIndex {
.ok_or_else(|| Error::NotFound(format!("tipset at epoch {to}").into()))
}
+ /// Same as [`Self::tipset_by_height_async`], but errors if that would return `None`.
+ pub async fn load_required_tipset_by_height(
+ &self,
+ to: ChainEpoch,
+ from: Tipset,
+ resolve: ResolveNullTipset,
+ ) -> Result {
+ self.tipset_by_height_async(to, from, resolve)
+ .await?
+ .ok_or_else(|| Error::NotFound(format!("tipset at epoch {to}").into()))
+ }
+
/// Finds the latest beacon entry given a tipset up to 20 tipsets behind
pub fn latest_beacon_entry(&self, tipset: Tipset) -> Result {
for ts in tipset.chain(&self.db).take(20) {
diff --git a/src/chain_sync/sync_status.rs b/src/chain_sync/sync_status.rs
index a93f9ccea871..d946b7e49968 100644
--- a/src/chain_sync/sync_status.rs
+++ b/src/chain_sync/sync_status.rs
@@ -13,7 +13,7 @@ use std::sync::Arc;
use tracing::log;
// Node considered synced if the head is within this threshold.
-const SYNCED_EPOCH_THRESHOLD: u64 = 10;
+const SYNCED_EPOCH_THRESHOLD: u64 = 2;
/// Represents the overall synchronization status of the Forest node.
#[derive(
diff --git a/src/daemon/mod.rs b/src/daemon/mod.rs
index 5063d78ea926..1f04bcb3c508 100644
--- a/src/daemon/mod.rs
+++ b/src/daemon/mod.rs
@@ -825,7 +825,7 @@ pub(super) async fn start_services(
}
if !opts.stateless
&& !opts.skip_load_actors
- && let Err(e) = ctx.state_manager.maybe_rewind_heaviest_tipset()
+ && let Err(e) = ctx.state_manager.maybe_rewind_heaviest_tipset().await
{
tracing::warn!("error in maybe_rewind_heaviest_tipset: {e:#}");
}
diff --git a/src/dev/subcommands/export_state_tree_cmd.rs b/src/dev/subcommands/export_state_tree_cmd.rs
index b133ba78495a..df7b2670aabb 100644
--- a/src/dev/subcommands/export_state_tree_cmd.rs
+++ b/src/dev/subcommands/export_state_tree_cmd.rs
@@ -77,11 +77,14 @@ impl ExportStateTreeCommand {
.await?;
let chain_store = ChainStore::new(db.clone(), chain_config, genesis_header)?;
- let start_ts = chain_store.chain_index().load_required_tipset_by_height(
- from,
- chain_store.heaviest_tipset(),
- ResolveNullTipset::TakeNewer,
- )?;
+ let start_ts = chain_store
+ .chain_index()
+ .load_required_tipset_by_height(
+ from,
+ chain_store.heaviest_tipset(),
+ ResolveNullTipset::TakeNewer,
+ )
+ .await?;
let mut ipld_roots = vec![];
for (child, ts) in start_ts
diff --git a/src/dev/subcommands/state_cmd.rs b/src/dev/subcommands/state_cmd.rs
index 932f1d5a7dd6..5b1c07569ef5 100644
--- a/src/dev/subcommands/state_cmd.rs
+++ b/src/dev/subcommands/state_cmd.rs
@@ -80,12 +80,14 @@ 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.load_required_tipset_by_height(
- epoch,
- chain_store.heaviest_tipset(),
- ResolveNullTipset::TakeOlder,
- )?;
- let ts_next = chain_store.load_child_tipset(&ts)?.with_context(|| {
+ let ts = chain_index
+ .load_required_tipset_by_height(
+ epoch,
+ chain_store.heaviest_tipset(),
+ ResolveNullTipset::TakeOlder,
+ )
+ .await?;
+ let ts_next = chain_store.load_child_tipset(&ts).await?.with_context(|| {
format!(
"no child tipset for epoch {} (may be chain head)",
ts.epoch()
@@ -210,11 +212,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.load_required_tipset_by_height(
- epoch,
- chain_store.heaviest_tipset(),
- ResolveNullTipset::TakeOlder,
- )?;
+ let ts = chain_index
+ .load_required_tipset_by_height(
+ epoch,
+ chain_store.heaviest_tipset(),
+ ResolveNullTipset::TakeOlder,
+ )
+ .await?;
db.resume_tracking();
SettingsStoreExt::write_obj(&db.tracker, crate::db::setting_keys::HEAD_KEY, ts.key())?;
// Only track the desired tipset
diff --git a/src/interpreter/externs.rs b/src/interpreter/externs.rs
index d8ed23655d3c..d2607950678f 100644
--- a/src/interpreter/externs.rs
+++ b/src/interpreter/externs.rs
@@ -261,7 +261,7 @@ impl ForestExterns {
fn get_tipset_cid_impl(&self, epoch: ChainEpoch) -> anyhow::Result {
let ts = self
.chain_index
- .load_required_tipset_by_height(
+ .load_required_tipset_by_height_blocking(
epoch,
self.heaviest_tipset.clone(),
ResolveNullTipset::TakeOlder,
diff --git a/src/message_pool/msgpool/provider.rs b/src/message_pool/msgpool/provider.rs
index 71c860bd9440..8f90a45272bd 100644
--- a/src/message_pool/msgpool/provider.rs
+++ b/src/message_pool/msgpool/provider.rs
@@ -10,6 +10,7 @@ use crate::message_pool::msg_pool::{
MAX_ACTOR_PENDING_MESSAGES, MAX_UNTRUSTED_ACTOR_PENDING_MESSAGES,
};
use crate::networks::Height;
+use crate::prelude::*;
use crate::shim::{
address::{Address, Protocol::*},
econ::TokenAmount,
@@ -18,8 +19,6 @@ use crate::shim::{
};
use crate::utils::db::CborStoreExt;
use auto_impl::auto_impl;
-use cid::Cid;
-use std::sync::Arc;
use tokio::sync::broadcast;
/// Provider Trait. This trait will be used by the message pool to interact with
@@ -120,7 +119,7 @@ impl Provider for ChainStore {
_ => {
let lookback_ts = if ts.epoch() > self.chain_config().policy.chain_finality {
self.chain_index()
- .load_required_tipset_by_height(
+ .load_required_tipset_by_height_blocking(
ts.epoch() - self.chain_config().policy.chain_finality,
ts.clone(),
ResolveNullTipset::TakeOlder,
@@ -144,3 +143,31 @@ impl Provider for ChainStore {
ChainStore::messages_for_tipset(self, ts).map_err(Into::into)
}
}
+
+#[allow(dead_code)]
+pub trait ProviderExt {
+ /// Non-blocking version of [`Provider::resolve_to_deterministic_address_at_finality`]
+ async fn resolve_to_deterministic_address_at_finality_async(
+ &self,
+ addr: Address,
+ ts: Tipset,
+ ) -> Result;
+}
+
+impl ProviderExt for T
+where
+ T: Provider + ShallowClone + Send + Sync + 'static,
+{
+ async fn resolve_to_deterministic_address_at_finality_async(
+ &self,
+ addr: Address,
+ ts: Tipset,
+ ) -> Result {
+ let this = self.shallow_clone();
+ tokio::task::spawn_blocking(move || {
+ this.resolve_to_deterministic_address_at_finality(&addr, &ts)
+ })
+ .await
+ .context("tokio join error")?
+ }
+}
diff --git a/src/rpc/methods/chain.rs b/src/rpc/methods/chain.rs
index f4087231a512..5a47b1a12849 100644
--- a/src/rpc/methods/chain.rs
+++ b/src/rpc/methods/chain.rs
@@ -296,11 +296,10 @@ impl RpcMethod<1> for ForestChainExport {
let chain_export_guard = ChainExportGuard::try_start_export()?;
let head = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
- let start_ts = ctx.chain_index().load_required_tipset_by_height(
- epoch,
- head,
- ResolveNullTipset::TakeOlder,
- )?;
+ let start_ts = ctx
+ .chain_index()
+ .load_required_tipset_by_height(epoch, head, ResolveNullTipset::TakeOlder)
+ .await?;
let options = ExportOptions {
skip_checksum,
@@ -498,11 +497,10 @@ impl RpcMethod<1> for ForestChainExportDiff {
}
let head = ctx.chain_store().heaviest_tipset();
- let start_ts = ctx.chain_index().load_required_tipset_by_height(
- from,
- head,
- ResolveNullTipset::TakeOlder,
- )?;
+ let start_ts = ctx
+ .chain_index()
+ .load_required_tipset_by_height(from, head, ResolveNullTipset::TakeOlder)
+ .await?;
crate::tool::subcommands::archive_cmd::do_export(
ctx.chain_index().db(),
@@ -798,11 +796,10 @@ impl RpcMethod<2> for ChainGetTipSetByHeight {
let ts = ctx
.chain_store()
.load_required_tipset_or_heaviest(&tipset_key)?;
- let tss = ctx.chain_index().load_required_tipset_by_height(
- height,
- ts,
- ResolveNullTipset::TakeOlder,
- )?;
+ let tss = ctx
+ .chain_index()
+ .load_required_tipset_by_height(height, ts, ResolveNullTipset::TakeOlder)
+ .await?;
Ok(tss)
}
}
@@ -828,11 +825,10 @@ impl RpcMethod<2> for ChainGetTipSetAfterHeight {
let ts = ctx
.chain_store()
.load_required_tipset_or_heaviest(&tipset_key)?;
- let tss = ctx.chain_index().load_required_tipset_by_height(
- height,
- ts,
- ResolveNullTipset::TakeNewer,
- )?;
+ let tss = ctx
+ .chain_index()
+ .load_required_tipset_by_height(height, ts, ResolveNullTipset::TakeNewer)
+ .await?;
Ok(tss)
}
}
@@ -969,16 +965,16 @@ impl ChainGetTipSetV2 {
if finalized.epoch() >= safe_height {
Ok(finalized)
} else {
- Ok(ctx.chain_index().load_required_tipset_by_height(
- safe_height,
- head,
- ResolveNullTipset::TakeOlder,
- )?)
+ Ok(ctx
+ .chain_index()
+ .load_required_tipset_by_height(safe_height, head, ResolveNullTipset::TakeOlder)
+ .await?)
}
}
pub async fn get_latest_finalized_tipset(ctx: &Ctx) -> anyhow::Result {
- ChainGetTipSetFinalityStatus::get_finality_status(ctx)?
+ ChainGetTipSetFinalityStatus::get_finality_status(ctx)
+ .await?
.finalized_tip_set
.context("failed to resolve finalized tipset")
}
@@ -993,11 +989,14 @@ impl ChainGetTipSetV2 {
// Get tipset by height.
if let Some(height) = &selector.height {
let anchor = Self::get_tipset_by_anchor(ctx, height.anchor.as_ref()).await?;
- let ts = ctx.chain_index().load_required_tipset_by_height(
- height.at,
- anchor,
- height.resolve_null_tipset_policy(),
- )?;
+ let ts = ctx
+ .chain_index()
+ .load_required_tipset_by_height(
+ height.at,
+ anchor,
+ height.resolve_null_tipset_policy(),
+ )
+ .await?;
return Ok(ts);
}
// Get tipset by tag, either latest or finalized.
@@ -1032,10 +1031,11 @@ pub enum ChainGetTipSetFinalityStatus {}
const EC_CALCULATOR_FINALITY_CACHE_SIZE: usize = 4;
impl ChainGetTipSetFinalityStatus {
- pub fn get_finality_status(ctx: &Ctx) -> anyhow::Result {
+ pub async fn get_finality_status(ctx: &Ctx) -> anyhow::Result {
let head = ctx.chain_store().heaviest_tipset();
let (ec_finality_threshold_depth, ec_finalized_tip_set) =
- Self::get_ec_finality_threshold_depth_and_tipset_with_cache(ctx, head.shallow_clone())?;
+ Self::get_ec_finality_threshold_depth_and_tipset_with_cache(ctx, head.shallow_clone())
+ .await?;
let f3_finalized_tip_set = ctx.chain_store().f3_finalized_tipset();
let finalized_tip_set = match (&ec_finalized_tip_set, &f3_finalized_tip_set) {
(Some(ec), Some(f3)) => {
@@ -1058,15 +1058,18 @@ impl ChainGetTipSetFinalityStatus {
})
}
- pub fn get_ec_finality_threshold_depth_and_tipset_with_cache(
+ pub async fn get_ec_finality_threshold_depth_and_tipset_with_cache(
ctx: &Ctx,
head: Tipset,
) -> anyhow::Result<(i64, Option)> {
static CACHE: LazyLock)>> =
LazyLock::new(|| quick_cache::sync::Cache::new(EC_CALCULATOR_FINALITY_CACHE_SIZE));
- CACHE.get_or_insert_with(head.shallow_clone().key(), move || {
- Self::get_ec_finality_threshold_depth_and_tipset(ctx, head)
- })
+ CACHE
+ .get_or_insert_async(
+ head.shallow_clone().key(),
+ Self::get_ec_finality_threshold_depth_and_tipset(ctx, head),
+ )
+ .await
}
pub fn get_ec_finality_epoch(
@@ -1166,7 +1169,7 @@ impl ChainGetTipSetFinalityStatus {
}
}
- fn get_ec_finality_threshold_depth_and_tipset(
+ async fn get_ec_finality_threshold_depth_and_tipset(
ctx: &Ctx,
head: Tipset,
) -> anyhow::Result<(i64, Option)> {
@@ -1177,11 +1180,10 @@ impl ChainGetTipSetFinalityStatus {
);
let ec_finality_epoch =
Self::get_ec_finality_epoch_by_depth(ctx.chain_config(), &head, depth);
- let finalized = ctx.chain_index().tipset_by_height(
- ec_finality_epoch,
- head,
- ResolveNullTipset::TakeOlder,
- )?;
+ let finalized = ctx
+ .chain_index()
+ .tipset_by_height_async(ec_finality_epoch, head, ResolveNullTipset::TakeOlder)
+ .await?;
Ok((depth, finalized))
}
}
@@ -1202,7 +1204,7 @@ impl RpcMethod<0> for ChainGetTipSetFinalityStatus {
(): Self::Params,
_: &http::Extensions,
) -> Result {
- Ok(Self::get_finality_status(&ctx)?)
+ Ok(Self::get_finality_status(&ctx).await?)
}
}
diff --git a/src/rpc/methods/eth.rs b/src/rpc/methods/eth.rs
index 1697ae57b879..b1a094aa73c9 100644
--- a/src/rpc/methods/eth.rs
+++ b/src/rpc/methods/eth.rs
@@ -924,11 +924,14 @@ impl RpcMethod<1> for BaseFeeByHeight {
(height,): Self::Params,
_: &http::Extensions,
) -> Result {
- let ts = ctx.chain_index().load_required_tipset_by_height(
- height,
- ctx.chain_store().heaviest_tipset(),
- ResolveNullTipset::TakeOlder,
- )?;
+ let ts = ctx
+ .chain_index()
+ .load_required_tipset_by_height(
+ height,
+ ctx.chain_store().heaviest_tipset(),
+ ResolveNullTipset::TakeOlder,
+ )
+ .await?;
let base_fee = EthBaseFee::get_base_fee(&ctx, &ts)?;
Ok(base_fee.atto().into())
}
@@ -1068,7 +1071,7 @@ fn get_tipset_from_hash(chain_store: &ChainStore, block_hash: &EthHash) -> anyho
Ok(chain_store.chain_index().load_required_tipset(&tsk)?)
}
-fn resolve_block_number_tipset(
+async fn resolve_block_number_tipset(
chain: &ChainStore,
block_number: EthInt64,
resolve: ResolveNullTipset,
@@ -1081,13 +1084,14 @@ fn resolve_block_number_tipset(
chain
.chain_index()
.load_required_tipset_by_height(height, head, resolve)
+ .await
.map_err(|e| match e {
crate::chain::store::Error::NullRound(epoch) => EthErrors::null_round(epoch).into(),
e => e.into(),
})
}
-fn resolve_block_hash_tipset(
+async fn resolve_block_hash_tipset(
chain: &ChainStore,
block_hash: &EthHash,
require_canonical: bool,
@@ -1097,11 +1101,10 @@ fn resolve_block_hash_tipset(
// verify that the tipset is in the canonical chain
if require_canonical {
// walk up the current chain (our head) until we reach ts.epoch()
- let walk_ts = chain.chain_index().load_required_tipset_by_height(
- ts.epoch(),
- chain.heaviest_tipset(),
- resolve,
- )?;
+ let walk_ts = chain
+ .chain_index()
+ .load_required_tipset_by_height(ts.epoch(), chain.heaviest_tipset(), resolve)
+ .await?;
// verify that it equals the expected tipset
if walk_ts != ts {
bail!("tipset is not canonical");
diff --git a/src/rpc/methods/eth/filter/mod.rs b/src/rpc/methods/eth/filter/mod.rs
index dc4d69072d43..ee3489a7d498 100644
--- a/src/rpc/methods/eth/filter/mod.rs
+++ b/src/rpc/methods/eth/filter/mod.rs
@@ -526,11 +526,14 @@ impl EthEventHandler {
} else {
*range.end()
};
- let max_tipset = ctx.chain_index().load_required_tipset_by_height(
- max_height,
- ctx.chain_store().heaviest_tipset(),
- ResolveNullTipset::TakeOlder,
- )?;
+ let max_tipset = ctx
+ .chain_index()
+ .load_required_tipset_by_height(
+ max_height,
+ ctx.chain_store().heaviest_tipset(),
+ ResolveNullTipset::TakeOlder,
+ )
+ .await?;
let tipsets = max_tipset
.chain(ctx.db())
.take_while(|ts| ts.epoch() >= *range.start());
diff --git a/src/rpc/methods/eth/tipset_resolver.rs b/src/rpc/methods/eth/tipset_resolver.rs
index 4fc6b64c1bfd..54c1c391d08d 100644
--- a/src/rpc/methods/eth/tipset_resolver.rs
+++ b/src/rpc/methods/eth/tipset_resolver.rs
@@ -40,20 +40,23 @@ impl<'a> TipsetResolver<'a> {
BlockNumberOrHash::PredefinedBlock(tag) => self.resolve_predefined_tipset(tag).await,
BlockNumberOrHash::BlockNumber(block_number)
| BlockNumberOrHash::BlockNumberObject(BlockNumber { block_number }) => {
- resolve_block_number_tipset(self.ctx.chain_store(), block_number, resolve)
+ resolve_block_number_tipset(self.ctx.chain_store(), block_number, resolve).await
}
BlockNumberOrHash::BlockHash(block_hash) => {
- resolve_block_hash_tipset(self.ctx.chain_store(), &block_hash, false, resolve)
+ resolve_block_hash_tipset(self.ctx.chain_store(), &block_hash, false, resolve).await
}
BlockNumberOrHash::BlockHashObject(BlockHash {
block_hash,
require_canonical,
- }) => resolve_block_hash_tipset(
- self.ctx.chain_store(),
- &block_hash,
- require_canonical,
- resolve,
- ),
+ }) => {
+ resolve_block_hash_tipset(
+ self.ctx.chain_store(),
+ &block_hash,
+ require_canonical,
+ resolve,
+ )
+ .await
+ }
}
}
@@ -95,8 +98,8 @@ impl<'a> TipsetResolver<'a> {
Ok(ts)
} else {
match tag {
- Predefined::Safe => self.get_ec_safe_tipset(),
- Predefined::Finalized => self.get_ec_finalized_tipset(),
+ Predefined::Safe => self.get_ec_safe_tipset().await,
+ Predefined::Finalized => self.get_ec_finalized_tipset().await,
tag => anyhow::bail!("unknown block tag: {tag}"),
}
}
@@ -152,24 +155,25 @@ impl<'a> TipsetResolver<'a> {
/// Returns the tipset considered "safe" relative to the current heaviest tipset.
///
/// The safe tipset is the tipset at height `max(head.epoch() - SAFE_HEIGHT_DISTANCE, 0)`.
- pub fn get_ec_safe_tipset(&self) -> anyhow::Result {
+ pub async fn get_ec_safe_tipset(&self) -> anyhow::Result {
let head = self.ctx.chain_store().heaviest_tipset();
let safe_height = (head.epoch() - SAFE_HEIGHT_DISTANCE).max(0);
- Ok(self.ctx.chain_index().load_required_tipset_by_height(
- safe_height,
- head,
- ResolveNullTipset::TakeOlder,
- )?)
+ Ok(self
+ .ctx
+ .chain_index()
+ .load_required_tipset_by_height(safe_height, head, ResolveNullTipset::TakeOlder)
+ .await?)
}
/// Returns the tipset considered finalized by the expected-consensus finality calculator(`FRC-0089`).
- pub fn get_ec_finalized_tipset(&self) -> anyhow::Result {
+ pub async fn get_ec_finalized_tipset(&self) -> anyhow::Result {
let head = self.ctx.chain_store().heaviest_tipset();
let (_, ec_finalized_tipset) =
ChainGetTipSetFinalityStatus::get_ec_finality_threshold_depth_and_tipset_with_cache(
self.ctx,
head.clone(),
- )?;
+ )
+ .await?;
ec_finalized_tipset.context("failed to resolve EC finalized tipset")
}
}
diff --git a/src/rpc/methods/f3.rs b/src/rpc/methods/f3.rs
index 2c3413f06d5b..899e9af0c066 100644
--- a/src/rpc/methods/f3.rs
+++ b/src/rpc/methods/f3.rs
@@ -96,11 +96,14 @@ impl RpcMethod<1> for GetTipsetByEpoch {
(epoch,): Self::Params,
_: &http::Extensions,
) -> Result {
- let ts = ctx.chain_index().load_required_tipset_by_height(
- epoch,
- ctx.chain_store().heaviest_tipset(),
- ResolveNullTipset::TakeOlder,
- )?;
+ let ts = ctx
+ .chain_index()
+ .load_required_tipset_by_height(
+ epoch,
+ ctx.chain_store().heaviest_tipset(),
+ ResolveNullTipset::TakeOlder,
+ )
+ .await?;
Ok(ts.into())
}
}
diff --git a/src/rpc/methods/state.rs b/src/rpc/methods/state.rs
index d4920f746336..53121815c185 100644
--- a/src/rpc/methods/state.rs
+++ b/src/rpc/methods/state.rs
@@ -1600,21 +1600,26 @@ impl RpcMethod<3> for ForestStateCompute {
let force_recompute = force_recompute.unwrap_or_default();
let n_epochs = n_epochs.map(|n| n.get()).unwrap_or(1) as ChainEpoch;
let to_epoch = from_epoch + n_epochs - 1;
- let to_ts = ctx.chain_index().load_required_tipset_by_height(
- to_epoch,
- ctx.chain_store().heaviest_tipset(),
- ResolveNullTipset::TakeOlder,
- )?;
+ let to_ts = ctx
+ .chain_index()
+ .load_required_tipset_by_height(
+ to_epoch,
+ ctx.chain_store().heaviest_tipset(),
+ ResolveNullTipset::TakeOlder,
+ )
+ .await?;
let from_ts = if from_epoch >= to_ts.epoch() {
// When `from_epoch` is a null epoch or `n_epochs` is 1,
// `to_ts.epoch()` could be less than or equal to `from_epoch`
to_ts.shallow_clone()
} else {
- ctx.chain_index().load_required_tipset_by_height(
- from_epoch,
- to_ts.shallow_clone(),
- ResolveNullTipset::TakeOlder,
- )?
+ ctx.chain_index()
+ .load_required_tipset_by_height(
+ from_epoch,
+ to_ts.shallow_clone(),
+ ResolveNullTipset::TakeOlder,
+ )
+ .await?
};
let mut futures = FuturesOrdered::new();
@@ -1753,7 +1758,7 @@ impl RpcMethod<4> for StateGetRandomnessFromTickets {
) -> Result {
let tipset = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
let chain_rand = ctx.state_manager.chain_rand(tipset);
- let digest = chain_rand.get_chain_randomness(rand_epoch, false)?;
+ let digest = chain_rand.get_chain_randomness(rand_epoch, false).await?;
let value = crate::state_manager::chain_rand::draw_randomness_from_digest(
&digest,
personalization,
@@ -1783,7 +1788,7 @@ impl RpcMethod<2> for StateGetRandomnessDigestFromTickets {
) -> Result {
let tipset = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
let chain_rand = ctx.state_manager.chain_rand(tipset);
- let digest = chain_rand.get_chain_randomness(rand_epoch, false)?;
+ let digest = chain_rand.get_chain_randomness(rand_epoch, false).await?;
Ok(digest.to_vec())
}
}
@@ -1808,7 +1813,7 @@ impl RpcMethod<4> for StateGetRandomnessFromBeacon {
) -> Result {
let tipset = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
let chain_rand = ctx.state_manager.chain_rand(tipset);
- let digest = chain_rand.get_beacon_randomness_v3(rand_epoch)?;
+ let digest = chain_rand.get_beacon_randomness_v3(rand_epoch).await?;
let value = crate::state_manager::chain_rand::draw_randomness_from_digest(
&digest,
personalization,
@@ -1838,7 +1843,7 @@ impl RpcMethod<2> for StateGetRandomnessDigestFromBeacon {
) -> Result {
let tipset = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
let chain_rand = ctx.state_manager.chain_rand(tipset);
- let digest = chain_rand.get_beacon_randomness_v3(rand_epoch)?;
+ let digest = chain_rand.get_beacon_randomness_v3(rand_epoch).await?;
Ok(digest.to_vec())
}
}
diff --git a/src/state_manager/chain_rand.rs b/src/state_manager/chain_rand.rs
index 9bad37d489a2..277e0f04b770 100644
--- a/src/state_manager/chain_rand.rs
+++ b/src/state_manager/chain_rand.rs
@@ -38,7 +38,21 @@ impl ShallowClone for ChainRand {
impl ChainRand {
/// Gets 32 bytes of randomness for `ChainRand` parameterized by the
/// `DomainSeparationTag`, `ChainEpoch`, Entropy from the ticket chain.
- pub fn get_chain_randomness(
+ pub async fn get_chain_randomness(
+ &self,
+ round: ChainEpoch,
+ lookback: bool,
+ ) -> anyhow::Result<[u8; 32]> {
+ let this = self.shallow_clone();
+ tokio::task::spawn_blocking(move || this.get_chain_randomness_blocking(round, lookback))
+ .await?
+ }
+
+ /// Gets 32 bytes of randomness for `ChainRand` parameterized by the
+ /// `DomainSeparationTag`, `ChainEpoch`, Entropy from the ticket chain.
+ /// This call can be expensive and blocking, use [`Self::get_chain_randomness`]
+ /// in async contexts to avoid exhausting Tokio worker threads.
+ pub fn get_chain_randomness_blocking(
&self,
round: ChainEpoch,
lookback: bool,
@@ -58,7 +72,7 @@ impl ChainRand {
};
let rand_ts =
self.chain_index
- .load_required_tipset_by_height(search_height, ts, resolve)?;
+ .load_required_tipset_by_height_blocking(search_height, ts, resolve)?;
Ok(digest(
rand_ts
@@ -70,40 +84,46 @@ impl ChainRand {
}
/// network version 13 onward
- pub fn get_chain_randomness_v2(&self, round: ChainEpoch) -> anyhow::Result<[u8; 32]> {
- self.get_chain_randomness(round, false)
+ pub fn get_chain_randomness_v2_blocking(&self, round: ChainEpoch) -> anyhow::Result<[u8; 32]> {
+ self.get_chain_randomness_blocking(round, false)
}
/// network version 13; without look-back
- pub fn get_beacon_randomness_v2(&self, round: ChainEpoch) -> anyhow::Result<[u8; 32]> {
- self.get_beacon_randomness(round, false)
+ pub fn get_beacon_randomness_v2_blocking(&self, round: ChainEpoch) -> anyhow::Result<[u8; 32]> {
+ self.get_beacon_randomness_blocking(round, false)
}
/// network version 14 onward
- pub fn get_beacon_randomness_v3(&self, round: ChainEpoch) -> anyhow::Result<[u8; 32]> {
+ pub fn get_beacon_randomness_v3_blocking(&self, round: ChainEpoch) -> anyhow::Result<[u8; 32]> {
if round < 0 {
- return self.get_beacon_randomness_v2(round);
+ return self.get_beacon_randomness_v2_blocking(round);
}
let beacon_entry = self.extract_beacon_entry_for_epoch(round)?;
Ok(digest(beacon_entry.signature()))
}
+ /// Non-blocking version of [`Self::get_beacon_randomness_v3_blocking`]
+ pub async fn get_beacon_randomness_v3(&self, round: ChainEpoch) -> anyhow::Result<[u8; 32]> {
+ let this = self.shallow_clone();
+ tokio::task::spawn_blocking(move || this.get_beacon_randomness_v3_blocking(round)).await?
+ }
+
/// Gets 32 bytes of randomness for `ChainRand` parameterized by the
/// `DomainSeparationTag`, `ChainEpoch`, Entropy from the latest beacon
/// entry.
- pub fn get_beacon_randomness(
+ pub fn get_beacon_randomness_blocking(
&self,
round: ChainEpoch,
lookback: bool,
) -> anyhow::Result<[u8; 32]> {
- let rand_ts: Tipset = self.get_beacon_randomness_tipset(round, lookback)?;
+ let rand_ts: Tipset = self.get_beacon_randomness_tipset_blocking(round, lookback)?;
let be = self.chain_index.latest_beacon_entry(rand_ts)?;
Ok(digest(be.signature()))
}
pub fn extract_beacon_entry_for_epoch(&self, epoch: ChainEpoch) -> anyhow::Result {
- let mut rand_ts: Tipset = self.get_beacon_randomness_tipset(epoch, false)?;
+ let mut rand_ts: Tipset = self.get_beacon_randomness_tipset_blocking(epoch, false)?;
let (_, beacon) = self.beacon.beacon_for_epoch(epoch)?;
let round =
beacon.max_beacon_round_for_epoch(self.chain_config.network_version(epoch), epoch);
@@ -126,7 +146,7 @@ impl ChainRand {
)
}
- pub fn get_beacon_randomness_tipset(
+ pub fn get_beacon_randomness_tipset_blocking(
&self,
round: ChainEpoch,
lookback: bool,
@@ -146,7 +166,7 @@ impl ChainRand {
};
self.chain_index
- .load_required_tipset_by_height(search_height, ts, resolve)
+ .load_required_tipset_by_height_blocking(search_height, ts, resolve)
.map_err(|e| e.into())
}
}
@@ -154,24 +174,26 @@ impl ChainRand {
impl Rand for ChainRand {
fn get_chain_randomness(&self, round: ChainEpoch) -> anyhow::Result<[u8; 32]> {
// Inspect and log errors as this is only called in `FVM` and errors are not propagated to the caller
- self.get_chain_randomness_v2(round).inspect_err(|e| {
- tracing::warn!(
- "get_chain_randomness failed, round: {round}, ts@{}: {}, error: {e:#?}",
- self.tipset.epoch(),
- self.tipset.key()
- );
- })
+ self.get_chain_randomness_v2_blocking(round)
+ .inspect_err(|e| {
+ tracing::warn!(
+ "get_chain_randomness failed, round: {round}, ts@{}: {}, error: {e:#?}",
+ self.tipset.epoch(),
+ self.tipset.key()
+ );
+ })
}
fn get_beacon_randomness(&self, round: ChainEpoch) -> anyhow::Result<[u8; 32]> {
// Inspect and log errors as this is only called in `FVM` and errors are not propagated to the caller
- self.get_beacon_randomness_v3(round).inspect_err(|e| {
- tracing::warn!(
- "get_beacon_randomness failed, round: {round}, ts@{}: {}, error: {e:#?}",
- self.tipset.epoch(),
- self.tipset.key()
- );
- })
+ self.get_beacon_randomness_v3_blocking(round)
+ .inspect_err(|e| {
+ tracing::warn!(
+ "get_beacon_randomness failed, round: {round}, ts@{}: {}, error: {e:#?}",
+ self.tipset.epoch(),
+ self.tipset.key()
+ );
+ })
}
}
diff --git a/src/state_manager/execution.rs b/src/state_manager/execution.rs
index 50b4f27ffdb5..6986e3f43edc 100644
--- a/src/state_manager/execution.rs
+++ b/src/state_manager/execution.rs
@@ -189,7 +189,7 @@ impl StateManager {
pub fn validate_range_blocking(&self, epochs: RangeInclusive) -> anyhow::Result<()> {
let heaviest = self.heaviest_tipset();
let heaviest_epoch = heaviest.epoch();
- let end = self.chain_index().load_required_tipset_by_height(
+ let end = self.chain_index().load_required_tipset_by_height_blocking(
*epochs.end(),
heaviest,
ResolveNullTipset::TakeOlder,
diff --git a/src/state_manager/mod.rs b/src/state_manager/mod.rs
index 1b45d25a85ad..0a6dcb6b4e59 100644
--- a/src/state_manager/mod.rs
+++ b/src/state_manager/mod.rs
@@ -235,12 +235,12 @@ impl StateManager {
/// A valid head has
/// - state tree in the blockstore
/// - actor bundle version in the state tree that matches chain configuration
- pub fn maybe_rewind_heaviest_tipset(&self) -> anyhow::Result<()> {
- while self.maybe_rewind_heaviest_tipset_once()? {}
+ pub async fn maybe_rewind_heaviest_tipset(&self) -> anyhow::Result<()> {
+ while self.maybe_rewind_heaviest_tipset_once().await? {}
Ok(())
}
- fn maybe_rewind_heaviest_tipset_once(&self) -> anyhow::Result {
+ async fn maybe_rewind_heaviest_tipset_once(&self) -> anyhow::Result {
let head = self.heaviest_tipset();
if let Some(info) = self
.chain_config()
@@ -253,11 +253,14 @@ impl StateManager {
let bundle_metadata = state.get_actor_bundle_metadata()?;
if expected_bundle_metadata != bundle_metadata {
let current_epoch = head.epoch();
- let target_head = self.chain_index().load_required_tipset_by_height(
- (expected_height_info.epoch - 1).max(0),
- head,
- ResolveNullTipset::TakeOlder,
- )?;
+ let target_head = self
+ .chain_index()
+ .load_required_tipset_by_height(
+ (expected_height_info.epoch - 1).max(0),
+ head,
+ ResolveNullTipset::TakeOlder,
+ )
+ .await?;
let target_epoch = target_head.epoch();
let bundle_version = &bundle_metadata.version;
let expected_bundle_version = &expected_bundle_metadata.version;
diff --git a/src/state_manager/state_computation.rs b/src/state_manager/state_computation.rs
index bb8decd3ab2d..bdf72ed7f653 100644
--- a/src/state_manager/state_computation.rs
+++ b/src/state_manager/state_computation.rs
@@ -22,7 +22,7 @@ impl StateManager {
if let Some(state) = self.cache.get_map(ts.key(), |et| et.into()) {
Ok(state)
} else {
- match self.chain_store().load_child_tipset(ts)? {
+ match self.chain_store().load_child_tipset(ts).await? {
Some(receipt_ts) => Ok(TipsetState {
state_root: *receipt_ts.parent_state(),
receipt_root: *receipt_ts.parent_message_receipts(),
@@ -99,7 +99,7 @@ impl StateManager {
}
self.cache
.get_or_insert_async(ts.key(), async move {
- let receipt_ts = self.chain_store().load_child_tipset(ts)?;
+ let receipt_ts = self.chain_store().load_child_tipset(ts).await?;
self.load_executed_tipset_inner(ts, receipt_ts.as_ref(), policy)
.await
})
diff --git a/src/tool/subcommands/archive_cmd.rs b/src/tool/subcommands/archive_cmd.rs
index 1da1596914a7..6c56f5062be9 100644
--- a/src/tool/subcommands/archive_cmd.rs
+++ b/src/tool/subcommands/archive_cmd.rs
@@ -591,11 +591,13 @@ where
let ts = index
.load_required_tipset_by_height(epoch, ts, ResolveNullTipset::TakeOlder)
+ .await
.context("unable to get a tipset at given height")?;
let seen = if let Some(diff) = diff {
let diff_ts: Tipset = index
.load_required_tipset_by_height(diff, ts.shallow_clone(), ResolveNullTipset::TakeOlder)
+ .await
.context("diff epoch must be smaller than target epoch")?;
let diff_ts: &Tipset = &diff_ts;
let diff_limit = diff_depth.map(|depth| diff_ts.epoch() - depth).unwrap_or(0);
@@ -855,17 +857,21 @@ async fn show_tipset_diff(
CurrentNetwork::set_global(Network::Testnet);
}
let beacon = Arc::new(chain_config.get_beacon_schedule(genesis_timestamp));
- let tipset = chain_index.load_required_tipset_by_height(
- epoch,
- heaviest_tipset.clone(),
- ResolveNullTipset::TakeOlder,
- )?;
+ let tipset = chain_index
+ .load_required_tipset_by_height(
+ epoch,
+ heaviest_tipset.clone(),
+ ResolveNullTipset::TakeOlder,
+ )
+ .await?;
- let child_tipset = chain_index.load_required_tipset_by_height(
- epoch + 1,
- heaviest_tipset.clone(),
- ResolveNullTipset::TakeNewer,
- )?;
+ let child_tipset = chain_index
+ .load_required_tipset_by_height(
+ epoch + 1,
+ heaviest_tipset.clone(),
+ ResolveNullTipset::TakeNewer,
+ )
+ .await?;
let ExecutedTipset { state_root, .. } = apply_block_messages_blocking(
chain_index,
diff --git a/src/tool/subcommands/benchmark_cmd.rs b/src/tool/subcommands/benchmark_cmd.rs
index d7e9ad33ae8f..5c29c404a8bc 100644
--- a/src/tool/subcommands/benchmark_cmd.rs
+++ b/src/tool/subcommands/benchmark_cmd.rs
@@ -223,11 +223,13 @@ async fn benchmark_exporting(
store.shallow_clone(),
heaviest.genesis(store.shallow_clone()).await?,
);
- let ts = idx.load_required_tipset_by_height(
- epoch.unwrap_or(heaviest.epoch()),
- heaviest,
- ResolveNullTipset::TakeOlder,
- )?;
+ let ts = idx
+ .load_required_tipset_by_height(
+ epoch.unwrap_or(heaviest.epoch()),
+ heaviest,
+ ResolveNullTipset::TakeOlder,
+ )
+ .await?;
// We don't do any sanity checking for 'depth'. The output is discarded so
// there's no need.
let stateroot_lookup_limit = ts.epoch() - depth;
diff --git a/src/tool/subcommands/index_cmd.rs b/src/tool/subcommands/index_cmd.rs
index 1d6d2c4cde3c..8ba0ef5d2f4a 100644
--- a/src/tool/subcommands/index_cmd.rs
+++ b/src/tool/subcommands/index_cmd.rs
@@ -96,11 +96,10 @@ impl IndexCommands {
// ensure from epoch is not greater than head epoch. This can happen if the
// assumed head is actually a null tipset.
let from = std::cmp::min(*from, head_ts.epoch());
- chain_store.chain_index().load_required_tipset_by_height(
- from,
- head_ts,
- ResolveNullTipset::TakeOlder,
- )?
+ chain_store
+ .chain_index()
+ .load_required_tipset_by_height(from, head_ts, ResolveNullTipset::TakeOlder)
+ .await?
} else {
head_ts
};
diff --git a/src/tool/subcommands/snapshot_cmd.rs b/src/tool/subcommands/snapshot_cmd.rs
index ab0044df0934..4c2b5093eb28 100644
--- a/src/tool/subcommands/snapshot_cmd.rs
+++ b/src/tool/subcommands/snapshot_cmd.rs
@@ -266,7 +266,7 @@ impl SnapshotCommands {
snapshot,
epoch,
json,
- } => print_computed_state(snapshot, epoch, json),
+ } => print_computed_state(snapshot, epoch, json).await,
}
}
}
@@ -463,7 +463,11 @@ fn validation_spinner(prefix: &'static str) -> indicatif::ProgressBar {
pb
}
-fn print_computed_state(snapshot: PathBuf, epoch: ChainEpoch, json: bool) -> anyhow::Result<()> {
+async fn print_computed_state(
+ snapshot: PathBuf,
+ epoch: ChainEpoch,
+ json: bool,
+) -> anyhow::Result<()> {
// Initialize Blockstore
let store: Arc = Arc::new(AnyCar::try_from(snapshot.as_path())?.try_into()?);
@@ -483,6 +487,7 @@ fn print_computed_state(snapshot: PathBuf, epoch: ChainEpoch, json: bool) -> any
let beacon = Arc::new(chain_config.get_beacon_schedule(genesis_timestamp));
let tipset = chain_index
.load_required_tipset_by_height(epoch, ts, ResolveNullTipset::TakeOlder)
+ .await
.with_context(|| format!("couldn't get a tipset at height {epoch}"))?;
let mut message_calls = vec![];