-
Notifications
You must be signed in to change notification settings - Fork 4
Rust accepts empty Simplex candidates whose referenced block is not the FSM tip, while C++ rejects them #43
Description
Rust and C++ do not enforce the same validity rule for empty Simplex candidates.
An empty candidate carries a referenced block id. C++ rejects the candidate unless that referenced block exactly matches the current normal FSM state for the slot. Rust does not perform that check and auto-approves empty candidates once signature and parent-chain checks pass.
This is consensus-critical because it allows Rust validators to approve and vote for an empty candidate that C++ validators reject.
Affected code
- Rust:
rust_implementation/ton-rust-node/src/node/simplex/src/block.rs:908-1018 - Rust:
rust_implementation/ton-rust-node/src/node/simplex/src/session_processor.rs:5812-5820 - Rust:
rust_implementation/ton-rust-node/src/node/simplex/src/session_processor.rs:5886-5895 - C++:
validator/consensus/block-validator.cpp:68-75
Rust/C++ discrepancy
C++ empty-candidate validation does:
- reject unless
block == event->state->as_normal()
Rust does not have an equivalent state-tip comparison.
In Rust:
RawCandidate::from_tl()extracts the referenced block forConsensus_Empty- it checks signature validity and parent presence
check_validation()explicitly skips further validation for empty blockstry_approve_block()auto-approves them immediately
So Rust accepts an empty candidate without confirming that its referenced block matches the active FSM tip.
Trigger
The discrepancy is reachable when a leader emits an empty candidate such that:
- the candidate is correctly signed
- the parent chain is resolvable
- the
blockfield refers to some block other than the FSM's current normal state for that slot
This is not a malformed-wire-format issue. The candidate can be well-formed and signed by the expected leader.