Skip to content

Commit bf7c644

Browse files
authored
fix(rpc): backfill tipset messages from p2p network in a few RPC methods (#6421)
1 parent b10e85c commit bf7c644

File tree

6 files changed

+34
-36
lines changed

6 files changed

+34
-36
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343

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

46+
- [#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.
47+
4648
### Changed
4749

4850
- [#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.

docs/dictionary.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
2k
22
APIs
33
backend
4+
backfill
45
backport
56
benchmarking
67
blockstore

docs/docs/users/reference/env_variables.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ process.
5353
| `FOREST_ZSTD_FRAME_CACHE_DEFAULT_MAX_SIZE` | positive integer | 268435456 | 536870912 | The default zstd frame cache max size in bytes |
5454
| `FOREST_JWT_DISABLE_EXP_VALIDATION` | 1 or true | empty | 1 | Whether or not to disable JWT expiration validation |
5555
| `FOREST_ETH_BLOCK_CACHE_SIZE` | positive integer | 500 | 1 | The size of Eth block cache |
56+
| `FOREST_RPC_BACKFILL_FULL_TIPSET_FROM_NETWORK` | 1 or true | false | 1 | Whether or not to backfill full tipsets from the p2p network |
5657

5758
### `FOREST_F3_SIDECAR_FFI_BUILD_OPT_OUT`
5859

src/blocks/tipset.rs

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -281,31 +281,6 @@ impl Tipset {
281281
Tipset::load(store, tsk)?.context("Required tipset missing from database")
282282
}
283283

284-
/// Constructs and returns a full tipset if messages from storage exists
285-
pub fn fill_from_blockstore(&self, store: &impl Blockstore) -> Option<FullTipset> {
286-
// Find tipset messages. If any are missing, return `None`.
287-
let blocks = self
288-
.block_headers()
289-
.iter()
290-
.cloned()
291-
.map(|header| {
292-
let (bls_messages, secp_messages) =
293-
crate::chain::store::block_messages(store, &header).ok()?;
294-
Some(Block {
295-
header,
296-
bls_messages,
297-
secp_messages,
298-
})
299-
})
300-
.collect::<Option<Vec<_>>>()?;
301-
302-
// the given tipset has already been verified, so this cannot fail
303-
Some(
304-
FullTipset::new(blocks)
305-
.expect("block headers have already been verified so this check cannot fail"),
306-
)
307-
}
308-
309284
/// Returns epoch of the tipset.
310285
pub fn epoch(&self) -> ChainEpoch {
311286
self.min_ticket_block().epoch

src/chain_sync/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ mod validation;
1313

1414
pub use self::{
1515
bad_block_cache::BadBlockCache,
16-
chain_follower::{ChainFollower, load_full_tipset},
16+
chain_follower::{ChainFollower, get_full_tipset, load_full_tipset},
1717
chain_muxer::SyncConfig,
1818
consensus::collect_errs,
1919
sync_status::{ForkSyncInfo, ForkSyncStage, NodeSyncStatus, SyncStatus, SyncStatusReport},

src/rpc/methods/chain.rs

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::blocks::RawBlockHeader;
1010
use crate::blocks::{Block, CachingBlockHeader, Tipset, TipsetKey};
1111
use crate::chain::index::ResolveNullTipset;
1212
use crate::chain::{ChainStore, ExportOptions, FilecoinSnapshotVersion, HeadChange};
13+
use crate::chain_sync::{get_full_tipset, load_full_tipset};
1314
use crate::cid_collections::CidHashSet;
1415
use crate::ipld::DfsIter;
1516
use crate::ipld::{CHAIN_EXPORT_STATUS, cancel_export, end_export, start_export};
@@ -30,6 +31,7 @@ use crate::shim::executor::Receipt;
3031
use crate::shim::message::Message;
3132
use crate::utils::db::CborStoreExt as _;
3233
use crate::utils::io::VoidAsyncWriter;
34+
use crate::utils::misc::env::is_env_truthy;
3335
use anyhow::{Context as _, Result};
3436
use cid::Cid;
3537
use fvm_ipld_blockstore::Blockstore;
@@ -294,7 +296,7 @@ impl RpcMethod<1> for ChainGetParentMessages {
294296
type Ok = Vec<ApiMessage>;
295297

296298
async fn handle(
297-
ctx: Ctx<impl Blockstore>,
299+
ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
298300
(block_cid,): Self::Params,
299301
) -> Result<Self::Ok, ServerError> {
300302
let store = ctx.store();
@@ -305,7 +307,7 @@ impl RpcMethod<1> for ChainGetParentMessages {
305307
Ok(vec![])
306308
} else {
307309
let parent_tipset = Tipset::load_required(store, &block_header.parents)?;
308-
load_api_messages_from_tipset(store, &parent_tipset)
310+
load_api_messages_from_tipset(&ctx, parent_tipset.key()).await
309311
}
310312
}
311313
}
@@ -368,13 +370,13 @@ impl RpcMethod<1> for ChainGetMessagesInTipset {
368370
type Ok = Vec<ApiMessage>;
369371

370372
async fn handle(
371-
ctx: Ctx<impl Blockstore>,
373+
ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
372374
(ApiTipsetKey(tipset_key),): Self::Params,
373375
) -> Result<Self::Ok, ServerError> {
374376
let tipset = ctx
375377
.chain_store()
376378
.load_required_tipset_or_heaviest(&tipset_key)?;
377-
load_api_messages_from_tipset(ctx.store(), &tipset)
379+
load_api_messages_from_tipset(&ctx, tipset.key()).await
378380
}
379381
}
380382

@@ -1314,13 +1316,30 @@ pub(crate) fn chain_notify<DB: Blockstore>(
13141316
receiver
13151317
}
13161318

1317-
fn load_api_messages_from_tipset(
1318-
store: &impl Blockstore,
1319-
tipset: &Tipset,
1319+
async fn load_api_messages_from_tipset<DB: Blockstore + Send + Sync + 'static>(
1320+
ctx: &crate::rpc::RPCState<DB>,
1321+
tipset_keys: &TipsetKey,
13201322
) -> Result<Vec<ApiMessage>, ServerError> {
1321-
let full_tipset = tipset
1322-
.fill_from_blockstore(store)
1323-
.context("Failed to load full tipset")?;
1323+
static SHOULD_BACKFILL: LazyLock<bool> = LazyLock::new(|| {
1324+
let enabled = is_env_truthy("FOREST_RPC_BACKFILL_FULL_TIPSET_FROM_NETWORK");
1325+
if enabled {
1326+
tracing::warn!(
1327+
"Full tipset backfilling from network is enabled via FOREST_RPC_BACKFILL_FULL_TIPSET_FROM_NETWORK, excessive disk and bandwidth usage is expected."
1328+
);
1329+
}
1330+
enabled
1331+
});
1332+
let full_tipset = if *SHOULD_BACKFILL {
1333+
get_full_tipset(
1334+
&ctx.sync_network_context,
1335+
ctx.chain_store(),
1336+
None,
1337+
tipset_keys,
1338+
)
1339+
.await?
1340+
} else {
1341+
load_full_tipset(ctx.chain_store(), tipset_keys)?
1342+
};
13241343
let blocks = full_tipset.into_blocks();
13251344
let mut messages = vec![];
13261345
let mut seen = CidHashSet::default();

0 commit comments

Comments
 (0)