Uh oh!
There was an error while loading. Please reload this page.
fix: update TipsetKey save and load logic to match Lotus - #6712
Conversation
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (4)
📒 Files selected for processing (1)
WalkthroughThis PR migrates TipsetKey persistence from a chain-store-backed approach to blockstore-backed storage. It introduces new TipsetKey APIs (car_block, from_bytes, save, load) for blockstore operations, updates ChainStore to remove put_tipset_key and load keys from blockstore, and adds an include_tipset_keys flag to the export pipeline to optionally emit tipset-key blocks in CAR streams. Changes
Sequence DiagramsequenceDiagram
participant Client
participant ChainStore
participant Blockstore
participant TipsetKey
rect rgba(100, 150, 200, 0.5)
Note over Client,TipsetKey: New Blockstore-Based Persistence
Client->>ChainStore: set_heaviest_tipset(ts)
ChainStore->>TipsetKey: ts.key().save(blockstore)?
TipsetKey->>TipsetKey: car_block() → CarBlock
TipsetKey->>Blockstore: store CarBlock
Blockstore-->>TipsetKey: Cid
TipsetKey-->>ChainStore: Ok(Cid)
end
rect rgba(150, 100, 200, 0.5)
Note over Client,TipsetKey: Loading from Blockstore
Client->>ChainStore: get_required_tipset_key(hash)
ChainStore->>TipsetKey: TipsetKey::load(blockstore, cid)?
TipsetKey->>Blockstore: fetch by Cid
Blockstore-->>TipsetKey: RawBytes
TipsetKey->>TipsetKey: from_bytes(RawBytes)?
TipsetKey-->>ChainStore: Self
ChainStore-->>Client: TipsetKey
end
rect rgba(200, 150, 100, 0.5)
Note over Client,TipsetKey: Export with Optional Tipset Keys
Client->>ChainStore: export_to_forest_car(options)
ChainStore->>ChainStore: with_tipset_keys(include_tipset_keys)
ChainStore->>Blockstore: emit tipset-key CarBlocks if enabled
Blockstore-->>Client: CAR stream with/without tipset keys
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
src/tool/subcommands/archive_cmd.rs (1)
629-633:⚠️ Potential issue | 🟠 MajorEnable tipset-key blocks in this export path.
With
include_tipset_keys: false, snapshots produced here still omit the blocks thatChainStore::get_required_tipset_key()now loads directly from the blockstore. Importing one of these exports will leave historical hash-based tipset lookups broken until the data is rebuilt elsewhere. Default this totruehere, or make the user-facing default opt-out instead of opt-in.Suggested change
Some(ExportOptions { skip_checksum: true, include_receipts: false, include_events: false, - include_tipset_keys: false,+ include_tipset_keys: true, seen, }),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/tool/subcommands/archive_cmd.rs` around lines 629 - 633, The export currently constructs ExportOptions with include_tipset_keys: false which causes produced snapshots to omit tipset-key blocks and breaks historical tipset hash lookups on import; update the ExportOptions construction (the ExportOptions struct usage that sets skip_checksum, include_receipts, include_events, include_tipset_keys) to set include_tipset_keys: true (or change the user-facing default to opt-out) so tipset-key blocks are included in exports; locate the ExportOptions instantiation in archive_cmd.rs and change the include_tipset_keys flag accordingly and ensure any related documentation or CLI defaults reflect the opt-out behavior.src/cli/subcommands/snapshot_cmd.rs (1)
140-149:⚠️ Potential issue | 🟠 MajorDon't disable tipset-key emission in CLI snapshot exports.
This CLI now hardcodes
include_tipset_keys: false, so a snapshot exported here will not contain the tipset-key blocks that the new load path depends on. That makes the exported snapshot incomplete for historical hash-based tipset lookup after import.Suggested change
let params = ForestChainExportParams { version: format, epoch: tipset.epoch(), recent_roots: depth, output_path: temp_path.to_path_buf(), tipset_keys: tipset.key().clone().into(), include_receipts: false, include_events: false, - include_tipset_keys: false,+ include_tipset_keys: true, skip_checksum, dry_run, };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/cli/subcommands/snapshot_cmd.rs` around lines 140 - 149, The export currently sets ForestChainExportParams with include_tipset_keys: false which omits tipset-key blocks needed by the load path; update the snapshot command to enable tipset-key emission by setting include_tipset_keys: true (or wire through an explicit CLI flag) when constructing ForestChainExportParams in snapshot_cmd.rs so exported snapshots include tipset keys for historical hash-based tipset lookup.src/chain/store/chain_store.rs (1)
145-150:⚠️ Potential issue | 🟠 MajorPersist the tipset key before mutating head state.
If
save()fails here, the heaviest-tipset key provider and cache have already been updated, but the tipset-key block is still missing and no head-change event is published. That leaves the node in a partially applied state.Suggested change
pub fn set_heaviest_tipset(&self, ts: Tipset) -> Result<(), Error> { + ts.key().save(self.blockstore())?; self.heaviest_tipset_key_provider .set_heaviest_tipset_key(ts.key())?; *self.heaviest_tipset_cache.write() = Some(ts.clone()); - ts.key().save(self.blockstore())?; if self.publisher.send(HeadChange::Apply(ts)).is_err() { debug!("did not publish head change, no active receivers"); } Ok(()) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/chain/store/chain_store.rs` around lines 145 - 150, The function set_heaviest_tipset is updating heaviest_tipset_key_provider and heaviest_tipset_cache before persisting the tipset key, which can leave state partially applied if Tipset.key().save(self.blockstore()) fails; change the order so you call ts.key().save(self.blockstore()) first and return the error if it fails, then update heaviest_tipset_key_provider.set_heaviest_tipset_key(...), update *self.heaviest_tipset_cache.write(), and finally call self.publisher.send(HeadChange::Apply(ts)); ensure errors from save() and the key provider set are propagated and that the publish only happens after successful persistence and in-memory updates.src/rpc/methods/chain.rs (1)
685-694:⚠️ Potential issue | 🟠 MajorKeep
Filecoin.ChainExportcompatible with the new tipset-key storage model.This path still forces
include_tipset_keys: false, so RPC exports omit the blocks thatTipsetKey::load()now expects to find in the blockstore. That leavesFilecoin.ChainExportproducing snapshots that regress historical hash-based tipset resolution after import.Suggested change
(ForestChainExportParams { version: FilecoinSnapshotVersion::V1, epoch, recent_roots, output_path, tipset_keys, include_receipts: false, include_events: false, - include_tipset_keys: false,+ include_tipset_keys: true, skip_checksum, dry_run, },),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/rpc/methods/chain.rs` around lines 685 - 694, The export currently sets ForestChainExportParams.include_tipset_keys = false which causes Filecoin.ChainExport to omit block records needed by TipsetKey::load; change the export to include tipset keys by setting include_tipset_keys = true (or make it configurable) so the exported snapshot contains the blocks used by TipsetKey resolution; update the ForestChainExportParams construction in the Filecoin.ChainExport path to set include_tipset_keys to true (or wire through a flag) to restore historical hash-based tipset resolution after import.
🧹 Nitpick comments (2)
src/blocks/tipset.rs (2)
55-63: Document the new public TipsetKey helpers with rustdoc.
TipsetKey::cidandTipsetKey::car_blockare part of the new public API surface, but they still use plain comments instead of///docs. Please promote the Lotus-compat contract into rustdoc so it shows up in generated API docs.As per coding guidelines, "Document public functions and structs with doc comments".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/blocks/tipset.rs` around lines 55 - 63, The two new public helpers cid and car_block lack rustdoc; replace the plain comment with /// doc comments above pub fn cid and pub fn car_block that explain the Lotus-compat encoding contract (e.g., “Special encoding to match Lotus”), describe the return types (Cid and CarBlock) and possible errors, and note implementation details used for car_block (it serializes via fvm_ipld_encoding::to_vec and computes the CID with Cid::from_cbor_encoded_raw_bytes_blake2b256) so these details appear in generated API docs for cid, car_block, and the CarBlock helper.
60-63: Add operation-specific context to the new TipsetKey persistence helpers.These helpers sit on the new Lotus-compat persistence path, but encode/store/load failures currently bubble up without telling you which step failed. A small
.context(...)on each boundary will make snapshot and RPC regressions much easier to diagnose.As per coding guidelines, "Use
anyhow::Result<T>for most operations and add context with.context()when errors occur".Also applies to: 146-152
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/blocks/tipset.rs` around lines 60 - 63, The persistence helpers (e.g., the car_block method) currently propagate errors without context; update the error boundaries to add operation-specific context by chaining .context(...) to fallible calls: for car_block, add .context("serializing TipsetKey to CAR bytes") to the fvm_ipld_encoding::to_vec(...) call and .context("computing CID from CAR bytes") to the Cid::from_cbor_encoded_raw_bytes_blake2b256(...) call; apply the same pattern to the other TipsetKey persistence helpers (the encode/store/load helpers referenced around lines 146-152) so that each serialize, compute-cid, write, and read operation records a clear, descriptive context string.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/blocks/tipset.rs`:
- Around line 125-138: The decoder incorrectly assumes a fixed encoded CID
length by chunking on BLOCK_HEADER_CID_LEN; instead iterate through the raw
bytes and repeatedly parse CIDs until exhausted. In TipsetKey::from_bytes (the
pub fn from_bytes in src/blocks/tipset.rs) remove the static
BLOCK_HEADER_CID_LEN usage, convert the RawBytes into a byte slice/Vec, then
loop with a cursor: call Cid::read_bytes(&buf[cursor..]) (or the crate's
equivalent that returns the parsed Cid and number of bytes consumed), push each
Cid into the Vec<Cid>, advance cursor by the consumed length, and stop when
cursor == buf.len(); return an error if a CID parse fails or leftover bytes
remain. This ensures correct decoding regardless of each CID's encoded length.
In `@src/chain/store/chain_store.rs`:
- Around line 172-174: get_required_tipset_key() can fail for pre-upgrade DBs
because historical tipset keys were never backfilled; call the existing
backfill_db() (from src/daemon/db_util.rs) during normal startup and after
snapshot import so historical tipset keys get saved (tsk.save()) into the
blockstore. Specifically, invoke backfill_db() from start_services() (before
serving EthAPI requests) and from maybe_import_snapshot() immediately after
importing a snapshot, and guard the call so it only runs when the DB is missing
required tipsets or when a migration flag indicates pre-upgrade data, to ensure
get_required_tipset_key() can load older tipset keys.
In `@src/ipld/util.rs`:
- Around line 338-343: The code currently ignores failures from
tipset.borrow().key().car_block() by using if ... let Ok(...) = ..., causing
missing tipset-key data when export_tipset_keys is true; change that to attempt
car_block() explicitly and propagate the error instead of skipping (e.g., match
or use the ? operator) so that when export_tipset_keys is true and car_block()
fails the function returns the error rather than continuing; update the branch
that currently pushes Emit(cid, Some(data)) to only run after a successful
car_block() and ensure any Err from car_block() is returned to the caller.
---
Outside diff comments:
In `@src/chain/store/chain_store.rs`:
- Around line 145-150: The function set_heaviest_tipset is updating
heaviest_tipset_key_provider and heaviest_tipset_cache before persisting the
tipset key, which can leave state partially applied if
Tipset.key().save(self.blockstore()) fails; change the order so you call
ts.key().save(self.blockstore()) first and return the error if it fails, then
update heaviest_tipset_key_provider.set_heaviest_tipset_key(...), update
*self.heaviest_tipset_cache.write(), and finally call
self.publisher.send(HeadChange::Apply(ts)); ensure errors from save() and the
key provider set are propagated and that the publish only happens after
successful persistence and in-memory updates.
In `@src/cli/subcommands/snapshot_cmd.rs`:
- Around line 140-149: The export currently sets ForestChainExportParams with
include_tipset_keys: false which omits tipset-key blocks needed by the load
path; update the snapshot command to enable tipset-key emission by setting
include_tipset_keys: true (or wire through an explicit CLI flag) when
constructing ForestChainExportParams in snapshot_cmd.rs so exported snapshots
include tipset keys for historical hash-based tipset lookup.
In `@src/rpc/methods/chain.rs`:
- Around line 685-694: The export currently sets
ForestChainExportParams.include_tipset_keys = false which causes
Filecoin.ChainExport to omit block records needed by TipsetKey::load; change the
export to include tipset keys by setting include_tipset_keys = true (or make it
configurable) so the exported snapshot contains the blocks used by TipsetKey
resolution; update the ForestChainExportParams construction in the
Filecoin.ChainExport path to set include_tipset_keys to true (or wire through a
flag) to restore historical hash-based tipset resolution after import.
In `@src/tool/subcommands/archive_cmd.rs`:
- Around line 629-633: The export currently constructs ExportOptions with
include_tipset_keys: false which causes produced snapshots to omit tipset-key
blocks and breaks historical tipset hash lookups on import; update the
ExportOptions construction (the ExportOptions struct usage that sets
skip_checksum, include_receipts, include_events, include_tipset_keys) to set
include_tipset_keys: true (or change the user-facing default to opt-out) so
tipset-key blocks are included in exports; locate the ExportOptions
instantiation in archive_cmd.rs and change the include_tipset_keys flag
accordingly and ensure any related documentation or CLI defaults reflect the
opt-out behavior.
---
Nitpick comments:
In `@src/blocks/tipset.rs`:
- Around line 55-63: The two new public helpers cid and car_block lack rustdoc;
replace the plain comment with /// doc comments above pub fn cid and pub fn
car_block that explain the Lotus-compat encoding contract (e.g., “Special
encoding to match Lotus”), describe the return types (Cid and CarBlock) and
possible errors, and note implementation details used for car_block (it
serializes via fvm_ipld_encoding::to_vec and computes the CID with
Cid::from_cbor_encoded_raw_bytes_blake2b256) so these details appear in
generated API docs for cid, car_block, and the CarBlock helper.
- Around line 60-63: The persistence helpers (e.g., the car_block method)
currently propagate errors without context; update the error boundaries to add
operation-specific context by chaining .context(...) to fallible calls: for
car_block, add .context("serializing TipsetKey to CAR bytes") to the
fvm_ipld_encoding::to_vec(...) call and .context("computing CID from CAR bytes")
to the Cid::from_cbor_encoded_raw_bytes_blake2b256(...) call; apply the same
pattern to the other TipsetKey persistence helpers (the encode/store/load
helpers referenced around lines 146-152) so that each serialize, compute-cid,
write, and read operation records a clear, descriptive context string.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: ac00327b-c9ce-4851-a83f-d5e6065620e5
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (15)
Cargo.tomlmise.tomlsrc/blocks/tipset.rssrc/chain/mod.rssrc/chain/store/chain_store.rssrc/cli/subcommands/snapshot_cmd.rssrc/daemon/db_util.rssrc/daemon/mod.rssrc/db/gc/snapshot.rssrc/ipld/util.rssrc/rpc/methods/chain.rssrc/rpc/methods/eth.rssrc/rpc/methods/f3/types.rssrc/tool/subcommands/archive_cmd.rssrc/utils/cid/mod.rs
💤 Files with no reviewable changes (1)
- mise.toml
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
LesnyRumcajs
commented
Mar 10, 2026
No ✔️ no review! |
Uh oh!
There was an error while loading. Please reload this page.
Summary of changes
Changes introduced in this pull request:
Reference issue to close (if applicable)
Closes
Other information and links
Change checklist
Outside contributions
Summary by CodeRabbit
New Features
Tests