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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@

- [#6405](https://github.com/ChainSafe/forest/pull/6405) Enabled `Filecoin.EthGetLogs` for API v2.

- [#6421](https://github.com/ChainSafe/forest/pull/6421) Add an environment variable `FOREST_RPC_BACKFILL_FULL_TIPSET_FROM_NETWORK` to enable backfilling full tipsets from network in a few RPC methods.

### Changed

- [#6368](https://github.com/ChainSafe/forest/pull/6368): Migrated build and development tooling from Makefile to `mise`. Contributors should install `mise` and use `mise run` commands instead of `make` commands.
Expand Down
1 change: 1 addition & 0 deletions docs/dictionary.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
2k
APIs
backend
backfill
backport
benchmarking
blockstore
Expand Down
1 change: 1 addition & 0 deletions docs/docs/users/reference/env_variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ process.
| `FOREST_ZSTD_FRAME_CACHE_DEFAULT_MAX_SIZE` | positive integer | 268435456 | 536870912 | The default zstd frame cache max size in bytes |
| `FOREST_JWT_DISABLE_EXP_VALIDATION` | 1 or true | empty | 1 | Whether or not to disable JWT expiration validation |
| `FOREST_ETH_BLOCK_CACHE_SIZE` | positive integer | 500 | 1 | The size of Eth block cache |
| `FOREST_RPC_BACKFILL_FULL_TIPSET_FROM_NETWORK` | 1 or true | false | 1 | Whether or not to backfill full tipsets from the p2p network |
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, let's add some warning that enabling this might lead to excessive disk and bandwidth usage.

Also, let's add a CHANGELOG entry.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.


### `FOREST_F3_SIDECAR_FFI_BUILD_OPT_OUT`

Expand Down
25 changes: 0 additions & 25 deletions src/blocks/tipset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,31 +281,6 @@ impl Tipset {
Tipset::load(store, tsk)?.context("Required tipset missing from database")
}

/// Constructs and returns a full tipset if messages from storage exists
pub fn fill_from_blockstore(&self, store: &impl Blockstore) -> Option<FullTipset> {
// Find tipset messages. If any are missing, return `None`.
let blocks = self
.block_headers()
.iter()
.cloned()
.map(|header| {
let (bls_messages, secp_messages) =
crate::chain::store::block_messages(store, &header).ok()?;
Some(Block {
header,
bls_messages,
secp_messages,
})
})
.collect::<Option<Vec<_>>>()?;

// the given tipset has already been verified, so this cannot fail
Some(
FullTipset::new(blocks)
.expect("block headers have already been verified so this check cannot fail"),
)
}

/// Returns epoch of the tipset.
pub fn epoch(&self) -> ChainEpoch {
self.min_ticket_block().epoch
Expand Down
2 changes: 1 addition & 1 deletion src/chain_sync/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ mod validation;

pub use self::{
bad_block_cache::BadBlockCache,
chain_follower::{ChainFollower, load_full_tipset},
chain_follower::{ChainFollower, get_full_tipset, load_full_tipset},
chain_muxer::SyncConfig,
consensus::collect_errs,
sync_status::{ForkSyncInfo, ForkSyncStage, NodeSyncStatus, SyncStatus, SyncStatusReport},
Expand Down
39 changes: 29 additions & 10 deletions src/rpc/methods/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::blocks::RawBlockHeader;
use crate::blocks::{Block, CachingBlockHeader, Tipset, TipsetKey};
use crate::chain::index::ResolveNullTipset;
use crate::chain::{ChainStore, ExportOptions, FilecoinSnapshotVersion, HeadChange};
use crate::chain_sync::{get_full_tipset, load_full_tipset};
use crate::cid_collections::CidHashSet;
use crate::ipld::DfsIter;
use crate::ipld::{CHAIN_EXPORT_STATUS, cancel_export, end_export, start_export};
Expand All @@ -30,6 +31,7 @@ use crate::shim::executor::Receipt;
use crate::shim::message::Message;
use crate::utils::db::CborStoreExt as _;
use crate::utils::io::VoidAsyncWriter;
use crate::utils::misc::env::is_env_truthy;
use anyhow::{Context as _, Result};
use cid::Cid;
use fvm_ipld_blockstore::Blockstore;
Expand Down Expand Up @@ -294,7 +296,7 @@ impl RpcMethod<1> for ChainGetParentMessages {
type Ok = Vec<ApiMessage>;

async fn handle(
ctx: Ctx<impl Blockstore>,
ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
(block_cid,): Self::Params,
) -> Result<Self::Ok, ServerError> {
let store = ctx.store();
Expand All @@ -305,7 +307,7 @@ impl RpcMethod<1> for ChainGetParentMessages {
Ok(vec![])
} else {
let parent_tipset = Tipset::load_required(store, &block_header.parents)?;
load_api_messages_from_tipset(store, &parent_tipset)
load_api_messages_from_tipset(&ctx, parent_tipset.key()).await
}
}
}
Expand Down Expand Up @@ -368,13 +370,13 @@ impl RpcMethod<1> for ChainGetMessagesInTipset {
type Ok = Vec<ApiMessage>;

async fn handle(
ctx: Ctx<impl Blockstore>,
ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
(ApiTipsetKey(tipset_key),): Self::Params,
) -> Result<Self::Ok, ServerError> {
let tipset = ctx
.chain_store()
.load_required_tipset_or_heaviest(&tipset_key)?;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small doubt: Is it possible here that tipset_key is available in the store but the messages (full-tipset) is not available?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Messages are periodically garbage collected.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the tipset is not garbage collected 👍🏼.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Block headers are the spine of the chain that are never garbage collected

load_api_messages_from_tipset(ctx.store(), &tipset)
load_api_messages_from_tipset(&ctx, tipset.key()).await
}
}

Expand Down Expand Up @@ -1314,13 +1316,30 @@ pub(crate) fn chain_notify<DB: Blockstore>(
receiver
}

fn load_api_messages_from_tipset(
store: &impl Blockstore,
tipset: &Tipset,
async fn load_api_messages_from_tipset<DB: Blockstore + Send + Sync + 'static>(
ctx: &crate::rpc::RPCState<DB>,
tipset_keys: &TipsetKey,
) -> Result<Vec<ApiMessage>, ServerError> {
let full_tipset = tipset
.fill_from_blockstore(store)
.context("Failed to load full tipset")?;
static SHOULD_BACKFILL: LazyLock<bool> = LazyLock::new(|| {
let enabled = is_env_truthy("FOREST_RPC_BACKFILL_FULL_TIPSET_FROM_NETWORK");
if enabled {
tracing::warn!(
"Full tipset backfilling from network is enabled via FOREST_RPC_BACKFILL_FULL_TIPSET_FROM_NETWORK, excessive disk and bandwidth usage is expected."
);
}
enabled
});
let full_tipset = if *SHOULD_BACKFILL {
Comment thread
LesnyRumcajs marked this conversation as resolved.
get_full_tipset(
&ctx.sync_network_context,
ctx.chain_store(),
None,
tipset_keys,
)
.await?
} else {
load_full_tipset(ctx.chain_store(), tipset_keys)?
};
let blocks = full_tipset.into_blocks();
let mut messages = vec![];
let mut seen = CidHashSet::default();
Expand Down
Loading