Skip to content

feat(node): bound the block-proof merge with a publish-budget attestation-group cap#992

Draft
GrapeBaBa wants to merge 1 commit into
mainfrom
feat/merge-budget-part-cap
Draft

feat(node): bound the block-proof merge with a publish-budget attestation-group cap#992
GrapeBaBa wants to merge 1 commit into
mainfrom
feat/merge-budget-part-cap

Conversation

@GrapeBaBa

Copy link
Copy Markdown
Member

Part of fixing delayed block publishing: proposal_deadline_pct bounds only the gathering/compaction phase; the Type-2 merge itself was unbounded, so a busy attestation pool could push the block past interval 2-4 before it reached gossip.

Mechanism

The merge cost scales with its component count (one per attestation group + the proposer part), so the proposer derives a group cap from a publish budget before building the block:

publish budget (proposal_publish_target_intervals × 800ms, default 2 intervals)
  − gathering budget (existing proposal_deadline_pct)
  − 100ms signing margin
  = merge budget
merge budget ÷ per-component cost (EMA of measured merges) − 1 (proposer part) = group cap
  • The cap is applied at selection time (block body and proof components stay 1:1 — no post-hoc rebuild); candidates are already sorted most-valuable-first (fix(forkchoice): prefer fresh high-target proposal attestations #990), so truncation defers the least valuable groups to later proposers.
  • The per-component estimate self-calibrates: seeded at 800ms, updated 3:1 from measured merges, and only from multi-part merges — a proposer-only merge attributes its entire fixed cost (state snapshot, proposer prove, gate wait) to a single part and would otherwise ratchet the cap down permanently.
  • Liveness floor: the cap never goes below one attestation group. Attestations only reach the state through blocks; an all-zero cap across the network would stall justification outright. The thin-vs-late trade applies only above the floor.
  • A budget-capped selection logs the deferral at info level.

Notes

  • No CLI flag in this PR: the CLI arg struct already sits at zigcli's comptime eval-branch-quota cliff — adding any new field fails the build (structargs.zig:140: evaluation exceeded 1000 backwards branches). The knob is plumbed through NodeOptions/ChainOpts for programmatic use; exposing the flag needs a zigcli quota fix first (worth its own chore).
  • This bounds publish latency; it does not make same-slot attestation (interval 1) reachable — at current prover speeds any nonzero-attestation merge overruns interval 1 regardless. Complements fix(node): propose-path layering + publish latency + coverage report #988 (which removed the redundant verify+STF from the publish hop) and the planned precompute work (moving the merge into the previous slot's tail).

Validation

zig build + full zig build test green; new unit tests for the cap policy (computeMergeGroupCap: proposer-part reservation, liveness floor, boundary/degenerate inputs).

…ion-group cap)

The proposal deadline (proposal_deadline_pct) bounded only the gathering/
compaction phase; the Type-2 merge itself ran unbounded, so a busy pool let
the block-proof merge push gossip publication multiple intervals late. The
merge cost scales with its component count (one per attestation group + the
proposer), so the proposer now derives a group cap from a publish budget:

  publish budget (proposal_publish_target_intervals, default 2 intervals)
  - gathering budget - signing margin = merge budget
  merge budget / per-component cost estimate - 1 (proposer part) = group cap

The per-component estimate is an EMA of measured merges, seeded at 800ms and
updated only from multi-part merges (a proposer-only merge attributes its
whole fixed cost to one part and would ratchet the cap down). The cap floors
at one group: attestations only reach the state through blocks, so capping
to zero across the network would stall justification outright; the
thin-vs-late trade applies only above the floor. Deferred groups stay in the
pool for later proposers. Candidates are already sorted most-valuable-first,
so truncation keeps the highest-coverage groups.

No CLI flag yet: the arg struct already sits at zigcli's comptime
eval-branch-quota cliff (any new field fails the build); the knob is
plumbed through NodeOptions/ChainOpts for programmatic configuration.
@GrapeBaBa GrapeBaBa marked this pull request as draft June 11, 2026 07:51
@GrapeBaBa

Copy link
Copy Markdown
Member Author

Converting to draft: at current devnet scale (1-2 attestation groups per block) the cap never binds — this is scale-prep for multi-subnet configurations where merge part counts actually grow. The immediately useful piece is the observability (the per-component merge-cost EMA shows up in proposal logs, which feeds the precompute design). Will mark ready when a multi-subnet target makes it actionable.

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.

1 participant