fix: avoid deep-cloning Tipset and FullTipset#6274
Conversation
WalkthroughThis PR replaces widespread Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Areas requiring extra attention:
Possibly related PRs
Suggested labels
Suggested reviewers
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🧰 Additional context used🧠 Learnings (1)📓 Common learnings⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
🔇 Additional comments (3)
Comment |
| pub struct Tipset { | ||
| /// Sorted | ||
| #[get_size(size_fn = nunny_vec_heap_size_helper)] | ||
| headers: NonEmpty<CachingBlockHeader>, |
There was a problem hiding this comment.
The major changes are within this file (tipset.rs)
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/benchmark_private/tipset_validation.rs (1)
48-66: Type mismatch:Arc<Tipset>created butTipsetexpected in return type.The function signature on line 48 declares the return type as
Tipset, but line 50 createsArc::new(snap_car.heaviest_tipset()?), which producesArc<Tipset>. ThisArc<Tipset>is then returned on line 66, causing a type mismatch.Apply this diff to remove the
Arcwrapper and align with the new API semantics:- let ts = Arc::new(snap_car.heaviest_tipset()?); + let ts = snap_car.heaviest_tipset()?;Also update line 65 to remove the redundant
.clone()on the warmup call, since the Arc wrapper is no longer needed:- validate(state_manager.clone(), ts.clone()).await; + validate(state_manager.clone(), ts.clone()).await;(Note: The
.clone()ontsis still needed sincevalidatetakes ownership of theTipset, but it should now be a cheap clone due to internal Arc-wrapped collections per the PR's design.)
🧹 Nitpick comments (4)
src/rpc/methods/f3.rs (1)
160-466: GetPowerTable::compute now borrowsTipsetdirectly; consider dropping redundant&tsin v12+ macro callsThe switch to
ts: &Tipsetaligns this helper with the rest of the state-manager APIs and removes the extra Arc layer; the internal uses (get_actor_state*,resolve_to_deterministic_address, logging, etc.) are all consistent and avoid additional cloning, which fits the PR goal.One minor clean‑up: in the v12–v17 branches you still pass
&tsintohandle_miner_state_v12_on!, which makes$tsan&&Tipsetwhile v8–v11 usetsdirectly. It’s harmless but slightly noisy and inconsistent with the earlier arms; you can simplify the call sites to taketsas an&Tipset:- power::State::V12(s) => { - handle_miner_state_v12_on!( - v12, - id_power_worker_mappings, - &ts, - s, - &from_policy_v13_to_v12(&ctx.chain_config().policy) - ); - } + power::State::V12(s) => { + handle_miner_state_v12_on!( + v12, + id_power_worker_mappings, + ts, + s, + &from_policy_v13_to_v12(&ctx.chain_config().policy) + ); + } @@ - power::State::V13(s) => { + power::State::V13(s) => { handle_miner_state_v12_on!( v13, id_power_worker_mappings, - &ts, + ts, s, &ctx.chain_config().policy ); } @@ - power::State::V14(s) => { + power::State::V14(s) => { handle_miner_state_v12_on!( v14, id_power_worker_mappings, - &ts, + ts, s, &from_policy_v13_to_v14(&ctx.chain_config().policy) ); } @@ - power::State::V15(s) => { + power::State::V15(s) => { handle_miner_state_v12_on!( v15, id_power_worker_mappings, - &ts, + ts, s, &from_policy_v13_to_v15(&ctx.chain_config().policy) ); } @@ - power::State::V16(s) => { + power::State::V16(s) => { handle_miner_state_v12_on!( v16, id_power_worker_mappings, - &ts, + ts, s, &from_policy_v13_to_v16(&ctx.chain_config().policy) ); } @@ - power::State::V17(s) => { + power::State::V17(s) => { handle_miner_state_v12_on!( v17, id_power_worker_mappings, - &ts, + ts, s, &from_policy_v13_to_v17(&ctx.chain_config().policy) ); }src/message_pool/msgpool/selection.rs (1)
9-29: Tipset-by-valuerun_head_changerefactor looks sound;BorrowMutuse is slightly overkillPassing
from/toas ownedTipsetand cloning them intoleft_chain/right_chainpreserves the previous behaviour while avoiding deep cloning, assumingTipsetstays internally Arc-backed. The epoch walk and LCA search logic are unchanged.The new
BorrowMutimport and usage (rmsgs.borrow_mut()) work but are a bit unnecessary here:rmsgsis already a&mut HashMap<…>, which is exactly whatremove_from_selected_msgsconceptually needs. If that helper is now generic overimpl BorrowMut<_>, it’s more idiomatic to take theBorrowMutat the callee boundary and keep call sites passing&mutdirectly.Not a blocker, but you could simplify by either:
- Letting
remove_from_selected_msgsitself call.borrow_mut()on its argument, or- Narrowing the parameter type back to
&mut Pendingand passingrmsgswithout the trait call here.Also applies to: 815-869
src/rpc/methods/eth/filter/mod.rs (1)
405-414: Remove unnecessary Arc wrapping.Since
collect_eventsnow accepts&Tipsetinstead of&Arc<Tipset>, theArc::new()wrapping at lines 407 and 412 is no longer necessary. Due to Deref coercion this still compiles, but it allocates an Arc unnecessarily.ParsedFilterTipsets::Hash(block_hash) => { let tipset = get_tipset_from_hash(ctx.chain_store(), block_hash)?; - let tipset = Arc::new(tipset); Self::collect_events(ctx, &tipset, Some(pf), skip_event, &mut collected_events) .await?; } ParsedFilterTipsets::Key(tsk) => { - let tipset = Arc::new(Tipset::load_required(ctx.store(), tsk)?); + let tipset = Tipset::load_required(ctx.store(), tsk)?; Self::collect_events(ctx, &tipset, Some(pf), skip_event, &mut collected_events) .await?; }src/rpc/methods/eth.rs (1)
1505-1547:get_block_receiptsstill wrapsTipsetinto anArcunnecessarilyThe function:
- Resolves
ts: Tipset, then immediately doeslet ts_ref = Arc::new(ts);.- Passes
&ts_refintoexecute_tipsetandnew_eth_tx_receipt, which now both only require&Tipset.This reintroduces an
Arc<Tipset>layer in a hot path without need; a simple refactor to keeptsas a plainTipsetand pass&tseverywhere would reduce allocations and keep the API surface cleaner.Example refactor:
- let ts = tipset_by_block_number_or_hash(...)?; - ... - let ts_ref = Arc::new(ts); - let ts_key = ts_ref.key(); + let ts = tipset_by_block_number_or_hash(...)?; + let ts_key = ts.key(); ... - let (state_root, msgs_and_receipts) = execute_tipset(ctx, &ts_ref).await?; + let (state_root, msgs_and_receipts) = execute_tipset(ctx, &ts).await?; ... - ts_ref.epoch(), - &ts_key.cid()?, + ts.epoch(), + &ts_key.cid()?, ... - let receipt = new_eth_tx_receipt(ctx, &ts_ref, &tx, &receipt).await?; + let receipt = new_eth_tx_receipt(ctx, &ts, &tx, &receipt).await?;This keeps behavior identical but avoids the extra
Arc.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (45)
src/beacon/drand.rs(0 hunks)src/benchmark_private/tipset_validation.rs(2 hunks)src/blocks/header.rs(0 hunks)src/blocks/ticket.rs(1 hunks)src/blocks/tipset.rs(11 hunks)src/blocks/vrf_proof.rs(3 hunks)src/chain/store/chain_store.rs(8 hunks)src/chain/store/index.rs(12 hunks)src/chain_sync/chain_follower.rs(19 hunks)src/chain_sync/network_context.rs(2 hunks)src/chain_sync/tipset_syncer.rs(2 hunks)src/cli/subcommands/chain_cmd.rs(1 hunks)src/cli/subcommands/f3_cmd.rs(2 hunks)src/cli/subcommands/info_cmd.rs(7 hunks)src/daemon/db_util.rs(1 hunks)src/daemon/mod.rs(2 hunks)src/fil_cns/validation.rs(1 hunks)src/interpreter/fvm2.rs(2 hunks)src/interpreter/fvm3.rs(2 hunks)src/interpreter/fvm4.rs(2 hunks)src/interpreter/vm.rs(1 hunks)src/libp2p/chain_exchange/message.rs(1 hunks)src/message_pool/msgpool/mod.rs(5 hunks)src/message_pool/msgpool/msg_pool.rs(4 hunks)src/message_pool/msgpool/provider.rs(4 hunks)src/message_pool/msgpool/selection.rs(2 hunks)src/message_pool/msgpool/test_provider.rs(4 hunks)src/rpc/methods/chain.rs(12 hunks)src/rpc/methods/eth.rs(17 hunks)src/rpc/methods/eth/filter/mod.rs(3 hunks)src/rpc/methods/f3.rs(2 hunks)src/rpc/methods/f3/types.rs(0 hunks)src/rpc/methods/gas.rs(1 hunks)src/rpc/methods/state.rs(4 hunks)src/rpc/methods/sync.rs(2 hunks)src/rpc/mod.rs(1 hunks)src/state_manager/chain_rand.rs(6 hunks)src/state_manager/mod.rs(34 hunks)src/state_manager/tests.rs(6 hunks)src/tool/offline_server/server.rs(2 hunks)src/tool/subcommands/api_cmd/api_compare_tests.rs(1 hunks)src/tool/subcommands/archive_cmd.rs(8 hunks)src/tool/subcommands/benchmark_cmd.rs(2 hunks)src/tool/subcommands/snapshot_cmd.rs(2 hunks)src/tool/subcommands/state_compute_cmd.rs(1 hunks)
💤 Files with no reviewable changes (3)
- src/blocks/header.rs
- src/beacon/drand.rs
- src/rpc/methods/f3/types.rs
🧰 Additional context used
🧠 Learnings (11)
📓 Common learnings
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 5944
File: src/chain/store/index.rs:0-0
Timestamp: 2025-08-18T03:09:47.932Z
Learning: In Forest's tipset_by_height caching implementation, hanabi1224 prefers performance-conscious solutions that leverage finality guarantees rather than expensive chain walking for fork detection. The approach of constraining cache lookups to finalized epochs (using CHECKPOINT_INTERVAL >= CHAIN_FINALITY) provides fork safety without the performance cost of ancestry verification.
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 5930
File: build.rs:64-77
Timestamp: 2025-08-13T09:43:20.301Z
Learning: hanabi1224 prefers hard compile-time errors in build scripts rather than runtime safeguards or collision detection, believing it's better to fail fast and fix root causes of issues like malformed snapshot names.
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 6057
File: src/cli/subcommands/f3_cmd.rs:0-0
Timestamp: 2025-09-09T10:37:17.947Z
Learning: hanabi1224 prefers having default timeouts (like 10m for --no-progress-timeout) to prevent commands from hanging indefinitely, even when the timeout flag isn't explicitly provided by users. This fail-fast approach is preferred over requiring explicit flag usage.
📚 Learning: 2025-09-11T16:03:14.328Z
Learnt from: akaladarshi
Repo: ChainSafe/forest PR: 6000
File: src/tool/subcommands/api_cmd/state_decode_params_tests/miner.rs:4-9
Timestamp: 2025-09-11T16:03:14.328Z
Learning: In the Forest codebase's state_decode_params_tests module, common imports like `to_vec`, `TokenAmount`, `Cid`, etc. are centralized in the mod.rs file and made available to submodules through `use super::*;`, eliminating the need for duplicate imports in individual test files.
Applied to files:
src/state_manager/tests.rssrc/rpc/methods/chain.rssrc/chain/store/index.rs
📚 Learning: 2025-10-17T14:24:47.046Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 6167
File: src/tool/subcommands/state_compute_cmd.rs:89-91
Timestamp: 2025-10-17T14:24:47.046Z
Learning: In `src/tool/subcommands/state_compute_cmd.rs`, when using `ReadOpsTrackingStore` to generate minimal snapshots, `HEAD_KEY` should be written to `db.tracker` (not `db` itself) before calling `export_forest_car()`, because the export reads from the tracker MemoryDB which accumulates only the accessed data during computation.
Applied to files:
src/state_manager/tests.rssrc/daemon/db_util.rssrc/tool/subcommands/state_compute_cmd.rssrc/tool/subcommands/benchmark_cmd.rssrc/interpreter/fvm4.rssrc/tool/subcommands/snapshot_cmd.rssrc/tool/offline_server/server.rssrc/cli/subcommands/f3_cmd.rssrc/rpc/methods/state.rssrc/daemon/mod.rssrc/tool/subcommands/api_cmd/api_compare_tests.rssrc/interpreter/fvm2.rssrc/tool/subcommands/archive_cmd.rs
📚 Learning: 2025-08-08T12:11:55.266Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 5867
File: src/ipld/util.rs:461-487
Timestamp: 2025-08-08T12:11:55.266Z
Learning: Forest (src/ipld/util.rs, Rust): In UnorderedChainStream::poll_next, dropping `extract_sender` (when no more tipsets and the extract queue is empty) is the intended shutdown signal for workers. Any subsequent attempt to enqueue work after this drop is a logic error and should be treated as an error; do not change `send()` to ignore a missing sender.
Applied to files:
src/rpc/mod.rssrc/tool/subcommands/snapshot_cmd.rssrc/tool/offline_server/server.rssrc/rpc/methods/sync.rssrc/chain_sync/network_context.rssrc/message_pool/msgpool/test_provider.rssrc/rpc/methods/state.rssrc/daemon/mod.rssrc/message_pool/msgpool/mod.rssrc/message_pool/msgpool/selection.rssrc/cli/subcommands/chain_cmd.rssrc/message_pool/msgpool/msg_pool.rssrc/chain_sync/chain_follower.rs
📚 Learning: 2025-08-25T13:35:24.230Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 5969
File: src/tool/subcommands/snapshot_cmd.rs:412-412
Timestamp: 2025-08-25T13:35:24.230Z
Learning: In src/tool/subcommands/snapshot_cmd.rs, the +1 in `last_epoch = ts.epoch() - epochs as i64 + 1` fixes an off-by-1 bug where specifying --check-stateroots=N would validate N+1 epochs instead of N epochs, causing out-of-bounds errors when the snapshot contains only N recent state roots.
Applied to files:
src/tool/subcommands/state_compute_cmd.rssrc/tool/subcommands/snapshot_cmd.rssrc/cli/subcommands/chain_cmd.rssrc/chain/store/index.rssrc/tool/subcommands/archive_cmd.rs
📚 Learning: 2025-08-18T03:09:47.932Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 5944
File: src/chain/store/index.rs:0-0
Timestamp: 2025-08-18T03:09:47.932Z
Learning: In Forest's tipset_by_height caching implementation, hanabi1224 prefers performance-conscious solutions that leverage finality guarantees rather than expensive chain walking for fork detection. The approach of constraining cache lookups to finalized epochs (using CHECKPOINT_INTERVAL >= CHAIN_FINALITY) provides fork safety without the performance cost of ancestry verification.
Applied to files:
src/tool/subcommands/benchmark_cmd.rssrc/interpreter/fvm4.rssrc/tool/subcommands/snapshot_cmd.rssrc/interpreter/fvm3.rssrc/interpreter/fvm2.rssrc/chain/store/index.rssrc/tool/subcommands/archive_cmd.rs
📚 Learning: 2025-08-08T12:10:45.218Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 5867
File: src/ipld/util.rs:553-558
Timestamp: 2025-08-08T12:10:45.218Z
Learning: Forest project targets Rust stable >=1.89; features stabilized in 1.88 like let-chains are acceptable in this codebase.
Applied to files:
src/interpreter/fvm4.rssrc/rpc/methods/sync.rssrc/interpreter/fvm3.rssrc/interpreter/fvm2.rs
📚 Learning: 2025-08-19T11:17:35.177Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 5946
File: src/rpc/methods/state.rs:1458-1458
Timestamp: 2025-08-19T11:17:35.177Z
Learning: FuturesOrdered in the futures crate has a push_front method that pushes futures to the front of the queue, making them next to be considered for completion, in addition to the standard push/push_back methods.
Applied to files:
src/rpc/methods/state.rs
📚 Learning: 2025-08-04T13:36:22.993Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 5835
File: src/chain/tests.rs:58-76
Timestamp: 2025-08-04T13:36:22.993Z
Learning: In the Forest codebase, `Vec<u8>` can be used as an `AsyncWrite` implementation in test contexts. The user confirmed that tests using `&mut Vec<u8>` with `export` and `export_v2` functions compile and pass both locally and on CI.
Applied to files:
src/chain/store/index.rs
📚 Learning: 2025-10-16T11:05:13.586Z
Learnt from: elmattic
Repo: ChainSafe/forest PR: 6166
File: src/auth/mod.rs:127-150
Timestamp: 2025-10-16T11:05:13.586Z
Learning: In Rust 2024, `std::env::set_var` and `std::env::remove_var` are unsafe functions. They require `unsafe` blocks because they are only safe in single-threaded programs (Windows is an exception). When these functions are used in tests, ensure proper serialization with `#[serial]` or similar mechanisms.
Applied to files:
src/chain/store/index.rs
📚 Learning: 2025-07-17T15:21:40.753Z
Learnt from: hanabi1224
Repo: ChainSafe/forest PR: 5841
File: src/utils/get_size/mod.rs:10-10
Timestamp: 2025-07-17T15:21:40.753Z
Learning: The get_size2 crate's GetSize trait provides default implementations: get_stack_size() uses std::mem::size_of, get_heap_size() returns 0, and get_size() returns their sum. An empty impl like `impl GetSize for MyType {}` is valid and uses these defaults, making it suitable for simple wrapper types that don't allocate heap memory.
Applied to files:
src/blocks/vrf_proof.rs
🧬 Code graph analysis (19)
src/state_manager/tests.rs (3)
src/rpc/mod.rs (1)
chain_store(474-476)src/state_manager/mod.rs (1)
chain_store(348-350)src/daemon/context.rs (1)
chain_store(69-71)
src/interpreter/fvm4.rs (5)
src/chain/store/chain_store.rs (1)
heaviest_tipset(221-229)src/state_manager/mod.rs (1)
heaviest_tipset(208-210)src/db/car/plain.rs (1)
heaviest_tipset(208-210)src/db/car/forest.rs (1)
heaviest_tipset(186-188)src/db/car/many.rs (1)
heaviest_tipset(141-147)
src/tool/subcommands/snapshot_cmd.rs (4)
src/rpc/types/tsk_impl.rs (1)
ts(31-31)src/rpc/types/tests.rs (1)
ts(36-36)src/blocks/tipset.rs (2)
epoch(303-305)epoch(518-520)src/networks/mod.rs (1)
epoch(503-516)
src/rpc/methods/sync.rs (1)
src/rpc/types/tsk_impl.rs (1)
ts(31-31)
src/cli/subcommands/info_cmd.rs (1)
src/blocks/tipset.rs (2)
new(239-257)new(467-486)
src/interpreter/fvm3.rs (5)
src/chain/store/chain_store.rs (1)
heaviest_tipset(221-229)src/state_manager/mod.rs (1)
heaviest_tipset(208-210)src/db/car/plain.rs (1)
heaviest_tipset(208-210)src/db/car/forest.rs (1)
heaviest_tipset(186-188)src/db/car/many.rs (1)
heaviest_tipset(141-147)
src/chain_sync/network_context.rs (1)
src/blocks/tipset.rs (1)
tsk(262-266)
src/message_pool/msgpool/test_provider.rs (1)
src/message_pool/msgpool/provider.rs (4)
get_heaviest_tipset(36-36)get_heaviest_tipset(88-90)load_tipset(50-50)load_tipset(114-120)
src/fil_cns/validation.rs (2)
src/chain_sync/tipset_syncer.rs (1)
block_timestamp_checks(514-528)src/blocks/block.rs (1)
header(30-32)
src/message_pool/msgpool/mod.rs (1)
src/message_pool/msgpool/msg_pool.rs (2)
new(72-77)new(465-583)
src/message_pool/msgpool/msg_pool.rs (1)
src/blocks/tipset.rs (2)
new(239-257)new(467-486)
src/state_manager/chain_rand.rs (2)
src/blocks/chain4u.rs (1)
tipset(183-185)src/blocks/tipset.rs (2)
epoch(303-305)epoch(518-520)
src/rpc/methods/chain.rs (2)
src/blocks/tipset.rs (2)
into_lotus_json(655-657)from_lotus_json(659-661)src/lotus_json/arc.rs (2)
into_lotus_json(18-20)from_lotus_json(22-24)
src/message_pool/msgpool/provider.rs (2)
src/message_pool/msgpool/test_provider.rs (2)
get_heaviest_tipset(126-128)load_tipset(189-197)src/chain/store/index.rs (1)
load_tipset(54-72)
src/chain/store/index.rs (1)
src/blocks/tipset.rs (11)
tsk(262-266)load(261-269)from(104-109)from(159-161)from(165-167)from(171-176)from(180-185)from(204-211)from(452-457)chain(385-392)blocks(492-494)
src/chain/store/chain_store.rs (6)
src/state_manager/mod.rs (1)
heaviest_tipset(208-210)src/db/car/plain.rs (1)
heaviest_tipset(208-210)src/db/car/forest.rs (1)
heaviest_tipset(186-188)src/fil_cns/weight.rs (1)
fil_cns(23-57)src/blocks/tipset.rs (2)
weight(347-349)weight(522-524)src/fil_cns/mod.rs (1)
weight(91-96)
src/chain_sync/chain_follower.rs (1)
src/blocks/tipset.rs (4)
key(330-333)key(505-508)parents(339-341)parents(514-516)
src/rpc/methods/eth.rs (2)
src/blocks/tipset.rs (1)
chain(385-392)src/chain/store/index.rs (1)
chain(191-198)
src/state_manager/mod.rs (4)
src/chain/store/chain_store.rs (1)
heaviest_tipset(221-229)src/db/car/plain.rs (1)
heaviest_tipset(208-210)src/db/car/forest.rs (1)
heaviest_tipset(186-188)src/blocks/tipset.rs (7)
from(104-109)from(159-161)from(165-167)from(171-176)from(180-185)from(204-211)from(452-457)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: tests-release
- GitHub Check: Build forest binaries on Linux AMD64
- GitHub Check: tests
- GitHub Check: cargo-publish-dry-run
- GitHub Check: All lint checks
- GitHub Check: Build Ubuntu
Summary of changes
Changes introduced in this pull request:
TipsetandFullTipsetArcwrapper ofTipsetandFullTipsetReference issue to close (if applicable)
Closes
Other information and links
Change checklist
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.