You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When a new validator's bond deploy is included in a block and that block is finalized, the validator's stake should show up in the bonds map returned by /api/last-finalized-block within one epoch boundary (pos_epoch_length blocks) of the deploy landing — matching what post_state_bonds already reflects at inclusion time.
What did you see instead?
The bond deploy executed successfully on every node (("Bond result:", true, "Message:", Nil) — logged independently on validator1-0, validator2-0, validator3-0, validator4-0, bootstrap-0, and observer-0) and was selected into block 73 (Deploy selection for block 73: ... valid=1, selected=1), which was subsequently finalized. Yet the harness's /api/last-finalized-block poll (pkg/monitor/monitor.go:WaitForBond, reads bestLFB.Bonds) never saw the new validator's pubkey appear, and the smoke experiment aborted after the full 5-minute timeout:
bond for 04b461c2cf47d23f… not present in LFB bonds map after 5m0s (deploy likely never landed)
This is despite the chain staying healthy the entire time — finalized_block climbed monotonically from 47 to 94 over the wait window (smoke-chain.csv), with no freeze, no replay errors, no DAG-merge corruption. That rules out this being another instance of the previously-reported bonding failure classes #51, #104 — both of those involve the network stalling or state corrupting. Here the network kept producing and finalizing blocks fine; only the reported bonds set stayed stale.
Steps to reproduce the bug
Start a 3-genesis-validator network with heartbeat enabled, pos_epoch_length=10, pos_quarantine_length=10.
Wait for a pre-bond block milestone (70 finalized blocks in this run).
Submit a bond deploy for a 4th, already-synced validator.
Poll /api/last-finalized-block's bonds field for the new validator's pubkey for at least 5 minutes (well past the next epoch boundary after the deploy lands).
Supporting data (from this run)
Deploy 3045022100df3dae9b6e... submitted at 10:29:37.448 (validAfterBlockNumber: 70).
Selected and included in block 73 (seqNum 69) at 10:29:37.822 (alreadyInScope=0, selected=1) — validator1-0.log:5107.
Bond result: true logged on every node between 10:29:37 and 10:29:54 — validator1-0.log:5116, plus matching lines in bootstrap-0.log, observer-0.log, validator2-0.log, validator3-0.log, validator4-0.log.
Immediately after, at 10:29:38.631:
"block bonds field differs from post-state bonds", floor_number: 70, committee: 3, post_state_bonds: 4
— validator1-0.log:5119 (f1r3fly.casper.bonds_validation, block_creator.rs:3098). This confirms the discrepancy exists from the moment the bond lands: post-state already has 4 bonds, but the block's own bonds field (committee) is still 3, carried over from floor_number 70.
Subsequent attempts to re-include the same deploy in later blocks (75 and beyond) are correctly rejected as duplicates (Deploy ... FILTERED (already in scope), DagMerger rejected 1 user deploys) — validator1-0.log:5177, :5182. This is expected dedup behavior, not the bug; it just confirms the deploy was never re-selected because it was already considered "in scope" from block 73.
By the time the experiment aborted, finalized_block had reached 94 — several epoch boundaries (80, 90) past block 73 — with no sign of the bonds field ever picking up the 4th validator.
validator4's PROPOSED count stayed at 0 for the entire run (smoke.txtVALIDATOR BLOCKS table), consistent with it never being recognized as part of the committee.
Root cause hypothesis
The block header's bonds field (what /api/last-finalized-block exposes, and what determines committee membership) is meant to be refreshed from post_state_bonds at each epoch checkpoint (pos_epoch_length=10 blocks). That refresh appears to not be happening — or is happening against the wrong reference point — so a bond that lands mid-epoch never gets picked up by any later block's bonds field, even multiple epoch boundaries later. This looks like a regression in the epoch/committee-update logic specifically, not in bond-deploy execution (which clearly succeeds — Bond result: true everywhere) or in general consensus health (chain kept finalizing normally throughout).
Affected source locations (candidates)
casper/src/rust/blocks/proposer/block_creator.rs:3098 (f1r3fly.casper.bonds_validation — where the discrepancy is logged)
Whatever code path is supposed to promote post_state_bonds into a block's bonds field at the next epoch boundary.
Suggested fix / next steps
Reproduce with the raw node logs from this run (logs/dev-v/smoke-27-07-26-101109/logs/smoke/{validator1-0,bootstrap-0,observer-0,validator2-0,validator3-0,validator4-0}.log, all around 10:29:37–10:29:55) and step through the epoch-checkpoint code that's supposed to consume post_state_bonds into the block's bonds field.
Confirm whether the bonds field ever updates at all after enough further epochs elapse (this run only waited to block 94 — 2 epochs past the deploy — before the harness's 5-minute timeout forced an abort), or whether it's permanently stuck.
Done: widened the smoke/consensus bond-verification wait from 5 to 20 minutes (bondVerifyTimeout in experiment/consensus/runner.go), matching the existing budget already used for the later "Propose started" activation wait.
Resolved — permanent staleness confirmed, not a timing issue. Re-ran smoke with the 20-minute timeout: still ABORTED — bond verification validator4: bond for 04b461c2cf47d23f… not present in LFB bonds map after 20m0s, with validator4 PROPOSED=0 for the entire 169-block run (chain healthy and finalizing the whole time, same as the first run). 20 minutes at this run's ~12.4s/block cadence is roughly 9 epoch boundaries past the deploy landing — the bonds field never catches up no matter how much epoch-aligned time it's given. This rules out "the test was just impatient" and confirms the root cause is a genuine node-side regression in promoting post_state_bonds into the block/LFB bonds field. This is now ready to file upstream as-is.
Related issues
Different failure mode, same general area (bonding a new validator): Multiple validators bonding issue #51 — DAG-merge/state-root corruption, not observed here.
What did you expect to see?
When a new validator's bond deploy is included in a block and that block is finalized, the validator's stake should show up in the
bondsmap returned by/api/last-finalized-blockwithin one epoch boundary (pos_epoch_lengthblocks) of the deploy landing — matching whatpost_state_bondsalready reflects at inclusion time.What did you see instead?
The bond deploy executed successfully on every node (
("Bond result:", true, "Message:", Nil)— logged independently onvalidator1-0,validator2-0,validator3-0,validator4-0,bootstrap-0, andobserver-0) and was selected into block 73 (Deploy selection for block 73: ... valid=1, selected=1), which was subsequently finalized. Yet the harness's/api/last-finalized-blockpoll (pkg/monitor/monitor.go:WaitForBond, readsbestLFB.Bonds) never saw the new validator's pubkey appear, and thesmokeexperiment aborted after the full 5-minute timeout:This is despite the chain staying healthy the entire time —
finalized_blockclimbed monotonically from 47 to 94 over the wait window (smoke-chain.csv), with no freeze, no replay errors, no DAG-merge corruption. That rules out this being another instance of the previously-reported bonding failure classes #51, #104 — both of those involve the network stalling or state corrupting. Here the network kept producing and finalizing blocks fine; only the reportedbondsset stayed stale.Steps to reproduce the bug
pos_epoch_length=10,pos_quarantine_length=10./api/last-finalized-block'sbondsfield for the new validator's pubkey for at least 5 minutes (well past the next epoch boundary after the deploy lands).Supporting data (from this run)
3045022100df3dae9b6e...submitted at10:29:37.448(validAfterBlockNumber: 70).10:29:37.822(alreadyInScope=0, selected=1) —validator1-0.log:5107.Bond result: truelogged on every node between10:29:37and10:29:54—validator1-0.log:5116, plus matching lines inbootstrap-0.log,observer-0.log,validator2-0.log,validator3-0.log,validator4-0.log.10:29:38.631:validator1-0.log:5119(f1r3fly.casper.bonds_validation,block_creator.rs:3098). This confirms the discrepancy exists from the moment the bond lands: post-state already has 4 bonds, but the block's ownbondsfield (committee) is still 3, carried over fromfloor_number70.Deploy ... FILTERED (already in scope),DagMerger rejected 1 user deploys) —validator1-0.log:5177,:5182. This is expected dedup behavior, not the bug; it just confirms the deploy was never re-selected because it was already considered "in scope" from block 73.finalized_blockhad reached 94 — several epoch boundaries (80, 90) past block 73 — with no sign of thebondsfield ever picking up the 4th validator.validator4'sPROPOSEDcount stayed at0for the entire run (smoke.txtVALIDATOR BLOCKStable), consistent with it never being recognized as part of the committee.Root cause hypothesis
The block header's
bondsfield (what/api/last-finalized-blockexposes, and what determines committee membership) is meant to be refreshed frompost_state_bondsat each epoch checkpoint (pos_epoch_length=10blocks). That refresh appears to not be happening — or is happening against the wrong reference point — so a bond that lands mid-epoch never gets picked up by any later block'sbondsfield, even multiple epoch boundaries later. This looks like a regression in the epoch/committee-update logic specifically, not in bond-deploy execution (which clearly succeeds —Bond result: trueeverywhere) or in general consensus health (chain kept finalizing normally throughout).Affected source locations (candidates)
Whatever code path is supposed to promote
post_state_bondsinto a block'sbondsfield at the next epoch boundary.Suggested fix / next steps
logs/dev-v/smoke-27-07-26-101109/logs/smoke/{validator1-0,bootstrap-0,observer-0,validator2-0,validator3-0,validator4-0}.log, all around10:29:37–10:29:55) and step through the epoch-checkpoint code that's supposed to consumepost_state_bondsinto the block'sbondsfield.bondsfield ever updates at all after enough further epochs elapse (this run only waited to block 94 — 2 epochs past the deploy — before the harness's 5-minute timeout forced an abort), or whether it's permanently stuck.smoke/consensus bond-verification wait from 5 to 20 minutes (bondVerifyTimeoutinexperiment/consensus/runner.go), matching the existing budget already used for the later "Propose started" activation wait.smokewith the 20-minute timeout: stillABORTED — bond verification validator4: bond for 04b461c2cf47d23f… not present in LFB bonds map after 20m0s, withvalidator4 PROPOSED=0for the entire 169-block run (chain healthy and finalizing the whole time, same as the first run). 20 minutes at this run's ~12.4s/block cadence is roughly 9 epoch boundaries past the deploy landing — thebondsfield never catches up no matter how much epoch-aligned time it's given. This rules out "the test was just impatient" and confirms the root cause is a genuine node-side regression in promotingpost_state_bondsinto the block/LFBbondsfield. This is now ready to file upstream as-is.Related issues
Relates tests
smoke-27-07-26-101109
smoke-27-07-26-113545