Skip to content

Conversation

@karlem
Copy link
Contributor

@karlem karlem commented Oct 28, 2025

Closes #1441 and #1442


Note

High Risk
Touches consensus-critical top-down finality (new F3 mode, proof cache/service, startup gating) and changes the on-chain F3 light-client state shape/serialization (HAMT power table + new fields), which can affect chain compatibility and finality behavior.

Overview
Adds an end-to-end F3 proof-based top-down finality path, including new ipc.topdown.f3 settings (proof-service + execution retry/backoff), default config examples, and startup wiring that selects legacy vote-based vs F3 proof-based topdown with fail-fast checks against committed/genesis state.

Updates the F3 light-client actor to store the validator power table as an on-chain HAMT (CID root + materialized get_state), switch power values to big-endian bytes, and enforce monotonic processed_instance_id updates (no rewinds, idempotent allowed); genesis-from-parent now fetches the relevant F3 certificate to derive base_epoch/ETH block hash and parses parent power via BigInt.

Introduces shared EVM log decoding helpers (fendermint_vm_evm_event_utils) and new interpreter event extraction from F3 proof bundles (topdown messages + power-change requests), and refactors node startup by moving topdown/resolver/voting/polling-syncer orchestration into a dedicated service/topdown.rs module; also updates golden genesis fixtures and lockfile deps accordingly.

Written by Cursor Bugbot for commit fbeabf4. This will update automatically on new commits. Configure here.

@karlem karlem changed the title feat: init lifecycle feat: F3 e2e lifecycle Oct 29, 2025
@karlem karlem force-pushed the f3-lifecycle branch 2 times, most recently from 91db005 to cbce51c Compare November 4, 2025 17:20
Base automatically changed from f3-proofs-cache to main December 18, 2025 16:15
@karlem karlem marked this pull request as ready for review January 16, 2026 19:52
@karlem karlem requested a review from a team as a code owner January 16, 2026 19:52
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

Comment on lines +303 to +304
//
// Note: the first epoch proven in a certificate does not have a previous cursor.

Choose a reason for hiding this comment

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

We should be able to initialize the cursor from the base tipset's storage commitment (used only as parent here), which is the same as the last tipset in the previous cert (used as child there). WDYT?

Choose a reason for hiding this comment

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

As a workaround, maybe we could fetch parent tipsets (which are not very useful anyway) right in generate_proof_for_epoch, call it for the base tipset, get the nonces from the resulting proof bundle and discard the proof. Perhaps, we can do this only once and then maintain the cursor in ProofGeneratorService struct.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think the best way here is to store it on L2 ledger...

Copy link

@sergefdrv sergefdrv left a comment

Choose a reason for hiding this comment

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

I think it should work, so we can test it against calibration and finally get merged 🚀 Good job! 💪

Comment on lines 200 to 211
verify_sequence_against_storage_next(
"top-down message nonces",
next_topdown,
cursor.as_ref().map(|c| c.next_topdown_message_nonce),
Some(cursor.next_parent_topdown_nonce),
&nums.topdown_nonces,
)?;
verify_sequence_against_storage_next(
"power-change configuration numbers",
next_cfg,
cursor.as_ref().map(|c| c.next_power_change_config_number),
Some(cursor.next_parent_power_change_config_number),
&nums.config_numbers,
)?;

Choose a reason for hiding this comment

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

Maybe verify_sequence_against_storage_next could also require the nonce rather than taking it wrapped in Option.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

F3 topdown: Proof Verification & Completeness Enforcement

3 participants