Skip to content

MCP Proposal Creation - #3239

Draft
larskuhtz wants to merge 3 commits into
lars/proposer-electionfrom
lars/proposal-creation
Draft

larskuhtz wants to merge 3 commits into
lars/proposer-electionfrom
lars/proposal-creation

Conversation

@larskuhtz

@larskuhtz larskuhtz commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Stacked on #3225 (lars/proposer-election). Adds the proposal-creation component and wires proposals end to end through the sim: slots finalize with Positive entries carrying the scheduled proposers' payload roots, across rotations, vacancies, and a window rollover.

Design

Proposal creation is its own component, outside the consensus core. Proposing is driven by the slot deadline, not by eligibility: a proposal only counts if it is disseminated and decoded by a supermajority before the slot deadline D_s, and a proposer benefits from sealing as late as viable. The planner seals at deadline − lead, with lead a fixed conservative bound for now; the component boundary — facts in, wake and seal commands out — is the seam where adaptive or payload-economic strategies plug in later without touching consensus.

Everything else is a constraint from the MCP spec (main.tex, v1.2.0): a party that does not yet have B_{r−y} and state^{r−x} does not propose in round r, with x the execution delay and y ≤ x the observation cutoff.

  • Chaining gate (B_{r−y}): slot r is sealed only once slot r − y is part of the contiguous finalized prefix reported by the conductor — the stand-in for the translated block until sequencing exists. A lagging chain holds seals back; a seal skipped past its deadline leaves the index empty, a throughput cost and never a consensus hazard.
  • Blind-window knowledge: the in-flight proposals a proposer must know at its own index it has structurally — mid-phase they are its own, and at a handoff the schedule's rotation vacancy of y slots guarantees there are none. The planner sees vacancy simply as "not scheduled".
  • Execution prerequisite (state^{r−x}): not modeled; it slots in beside the chaining gate once execution progress exists as an input.

Changes

  • monad-mcp-chorus/src/proposing.rs: ProposalPlanner — requests a wake at deadline − lead for each slot where the schedule assigns this node an index; on wake it seals, or gates on chaining and releases or skips at the next chain advance.
  • monad-mcp-chorus/src/da.rs: the proposer-side seam towards the DA layer — DataAvailability::submit_proposal, by which the planner hands a sealed payload over for dissemination, plus NullDa for proposal-less tests. The consensus-side direction is the existing ChorusDAEvent / ChorusDACommand seam.
  • Fact surface: MonadConductor emits the (previously never-emitted) CloseSlots output whenever the completion cap advances; the runtime forwards OpenSlots/CloseSlots to the observer as handle_slots_opened/handle_chain_advance, and observers compose as tuples.
  • Runtime gains the DA-event entry point — an associated DAEvent and handle_da_event — so a node's DA layer can report availability to the slot instance it drives. Runtimes without a DA layer default it away.
  • monad-mcp-chorus-sim: MockDa, an in-memory DA mock. A submission becomes available locally at once and is announced as one message over the simulated network, under the same latency model as consensus traffic; availability reaches consensus as ChorusDAEvents. The mock validates announcements with the HeaderAuth consensus itself votes with, so the two cannot disagree about who proposes where. SimMessage wraps the wire format, and add_proposer_node wires planner, mock DA, and a per-node observer.
  • Entry, FastCommitVote and FastCommitQc become pub, so finalization data is inspectable by consumers — as ledger sequencing eventually will.

Testing

  • Planner unit tests: wake timing, lead underflow, gating, release and skip, genesis exemption, non-proposer slots. MockDa unit tests: submit, announce, availability reporting, idempotent delivery, first root wins, and an announcement for an index its signer does not hold.
  • tests/proposals.rs end to end: K = 1 rotating every slot — every entry finalizes Positive with the scheduled proposer's payload root, on the same finalization schedule as proposal-less runs; K = 2 staggered phases with y = 2 across a window rollover — genesis exemption, rotation vacancies (Negative exactly where the schedule says), and the chaining gate under load.

@larskuhtz
larskuhtz force-pushed the lars/proposal-creation branch from 5c8c39d to d19b935 Compare September 3, 2026 20:45
larskuhtz and others added 3 commits September 14, 2026 11:13
Add the proposal-creation component and wire proposals end-to-end
through the sim:

* proposing.rs: the ProposalPlanner decides when a node seals and
  submits its proposals. Deadline-driven (seal at deadline - lead, a
  fixed configurable lead for now; the component boundary is the seam
  for future payload-economic strategies). The spec's proposing
  precondition is enforced as a seal prerequisite -- the chaining gate:
  slot r is sealed only once slot r - y is part of the contiguous
  finalized prefix (the stand-in for the translated block B_{r-y}),
  with y the observation cutoff. The blind window's in-flight-knowledge
  requirement is met structurally by the schedule's rotation vacancy.

* da.rs: turn the DAHandle stub into the DataAvailability trait --
  consensus-side reads plus the proposer-side submit_proposal -- with
  NullDa replacing the old always-absent stub. The sim crate adds
  MockDa, an in-memory mock that treats a submission as locally decoded
  and disseminates one announcement message per proposal over the
  simulated network (same latency model as consensus messages).

* runtime/conductor: surface the facts the planner consumes. The
  conductor emits CloseSlots whenever the completion cap advances, and
  the runtime forwards OpenSlots/CloseSlots to the (now composable)
  FinalizationObserver as handle_slots_opened/handle_chain_advance.

* sim: SimMessage wraps the wire format (consensus + DA announcements);
  add_proposer_node wires planner, mock DA, and an extra observer per
  node. Entry/FastCommitVote/FastCommitQc become pub so finalization
  data is inspectable by consumers.

* tests/proposals.rs: end-to-end runs where the finalized entries carry
  the scheduled proposers' payload roots -- K = 1 rotating every slot,
  and K = 2 staggered phases with observation cutoff 2 across a window
  rollover, exercising genesis exemption, rotation vacancies, and the
  chaining gate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The spec (papers/specification/main.tex) defines y as the observation
cutoff (y <= x, the execution delay window): proposals of round r build
against B_{r-y} and state^{r-x}, and a party lacking either does not
propose. The blind window is the sliding window of rounds r-y+1..r whose
proposals may not be known in round r -- not the rotation vacancy, and
not the gate parameter.

Rename PlannerConfig::blind_window to observation_cutoff and rewrite the
module docs in spec terms: the chaining gate is the B_{r-y} prerequisite
(the chained prefix standing in for translation), x/y are protocol
constants enforced as back-pressure, and the in-flight-knowledge half of
the blind window is guaranteed by the schedule's rotation vacancy. No
logic changes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
MockDa re-implemented the proposer-index check privately while the
proposals test built a second authenticator closure for ChorusContext.
Two copies of one policy, free to drift with nothing to catch it.

Hand MockDa the HeaderAuth consensus votes with and let it validate
announcements through that, so an announcement the mock accepts is by
construction one consensus would authenticate. The test builds exactly
one, from the schedule, via proposers::header_auth.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@larskuhtz
larskuhtz force-pushed the lars/proposal-creation branch from d19b935 to 00a4153 Compare September 14, 2026 21:06
@larskuhtz
larskuhtz removed this pull request from stack #3240 September 14, 2026 21:26
@larskuhtz
larskuhtz added this pull request to stack #3258 September 14, 2026 21:27
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