feat(cardano): capture governance votes and full proposal state#1129
Conversation
Extends ProposalState with backward-compatible additions (new fields at CBOR indexes 9..=15, existing indexes frozen): full seven-variant governance action, lineage parent + purpose, submission epoch, metadata anchor, and slot-stamped append-only vote histories for the three voter classes (CC hot credential, DRep credential, SPO key hash). Old rows decode with the new fields empty; no data migration. Emits a new VoteCast delta for every vote in a tx's voting procedures (all three voter classes; phase-2-invalid txs excluded), buffered until block flush so votes on same-block proposals apply after the proposal exists. NewProposalV2 supersedes NewProposal at emission time, carrying the lineage capture; the legacy variant stays for WAL replay since its bincode field layout is frozen. Outcome stamping still consults the hacks::proposals table until the real ratification engine replaces it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughGovernance proposal modeling now supports additional actions, lineage metadata, anchors, and voter histories. New proposal and vote deltas are wired through Cardano delta handling, emitted by the roll visitor with ordered vote flushing, tested for compatibility, and rendered in state dumps. ChangesGovernance phase-2 support
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant ProposalVisitor
participant GovAction
participant CardanoDelta
GovAction->>ProposalVisitor: Parse action and lineage
ProposalVisitor->>CardanoDelta: Emit NewProposalV2
ProposalVisitor->>ProposalVisitor: Buffer VoteCast
ProposalVisitor->>CardanoDelta: Flush votes after proposals
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
crates/cardano/src/model/proposals.rs (1)
1020-1021: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueTest name mentions "replaces" but nothing is replaced.
The assertions only cover append-only history and exact undo. Consider
vote_cast_appends_and_undo_popsfor accuracy.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/cardano/src/model/proposals.rs` around lines 1020 - 1021, Rename the test function vote_cast_appends_and_replaces to vote_cast_appends_and_undo_pops so its name accurately reflects the append-only behavior and undo assertions it covers.crates/cardano/src/roll/proposals.rs (1)
374-389: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider a params struct for
NewProposalV2::new.Thirteen positional args at two call sites (with three bare
Nones here) is hard to read and will get worse as phases land. ANewProposalV2 { .. }struct literal or small params struct would document each field at the call site.Also applies to: 411-425
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/cardano/src/roll/proposals.rs` around lines 374 - 389, Refactor NewProposalV2::new and both call sites to accept a named fields/params struct instead of thirteen positional arguments. Update the proposal construction in the shown ParamChange path and the additional call site near the other reported location, explicitly naming the three lineage/anchor None values so each field’s purpose is clear while preserving existing behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@crates/cardano/src/model/proposals.rs`:
- Around line 1020-1021: Rename the test function vote_cast_appends_and_replaces
to vote_cast_appends_and_undo_pops so its name accurately reflects the
append-only behavior and undo assertions it covers.
In `@crates/cardano/src/roll/proposals.rs`:
- Around line 374-389: Refactor NewProposalV2::new and both call sites to accept
a named fields/params struct instead of thirteen positional arguments. Update
the proposal construction in the shown ParamChange path and the additional call
site near the other reported location, explicitly naming the three
lineage/anchor None values so each field’s purpose is clear while preserving
existing behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: f3c53d2d-d752-4f50-8deb-7dfa94f3d728
📒 Files selected for processing (5)
crates/cardano/src/model/mod.rscrates/cardano/src/model/proposals.rscrates/cardano/src/model/testing.rscrates/cardano/src/roll/proposals.rssrc/bin/dolos/data/dump_state.rs
Plan
Executes phase 2 (Vote & proposal capture) of
Brain/txpipe plans/dolos-governance-gaps.md— requirement 2 of the Cardano ledger governance-gaps initiative. Semantics source: the domain'shaskell-governance-research.md; shapes perdolos-implementation-design.md§3.1, §4, §5.1 (phase 1 landed in #1128).Done criterion:
ProposalStateextended with the full 7-variant governance action, lineage parent,proposed_in, and slot-stamped per-voter vote histories;VoteCastdeltas emitted for all three voter classes (CC members, DReps, SPOs). Met.What changed
crates/cardano/src/model/proposals.rsProposalAction: appendedNoConfidence,UpdateCommittee,NewConstitution,Infovariants (CBOR indexes 4..=7;Otherkept at 3 for old rows). All 7 Conway actions now map to a concrete variant.ProposalState: backward-compatible additions at frozen-index-safe positions 9..=15 —proposed_in,parent: Option<GovActionId>,purpose: Option<GovPurpose>(new 4-purpose enum),anchor, andcc_votes/drep_votes/spo_votesmaps ofvoter → Vec<(BlockSlot, Vote)>(append-only history; last entry is the effective vote, enabling as-of reads at epoch boundaries). Old rows decode with the new fields empty (#[cbor(default)]/Option); no data migration.VoteCastdelta: appends(slot, vote)to the voter's history; undo pops exactly the appended entry (tracks entry creation for exact rollback).NewProposalV2delta: records action + parent + purpose + anchor +proposed_in. The legacyNewProposalstays for WAL replay only — its bincode field layout is frozen and can't grow (same discipline as fix(cardano): stop dropping StakeVoteDeleg drep leg and drep anchors #1128'sDRepAnchorUpdate). Legacy replay now also populatesproposed_in, derived from thecurrent_epochthe old delta already carries.crates/cardano/src/model/mod.rs:NewProposalV2andVoteCastappended at the end ofCardanoDelta(bincode variant order frozen, append-only).crates/cardano/src/roll/proposals.rs:ProposalVisitormaps all 7GovActions with lineage/anchor capture and now walksvoting_proceduresinvisit_tx, emittingVoteCastfor CC/DRep/SPO voters. Votes are buffered and emitted at blockflushso a vote on a proposal submitted in the same block (legal in Conway, even same-tx) lands after that proposal's creation delta in the per-entity ordering. Phase-2-invalid txs are skipped (research §8). The existingDRepActivitybump inDRepStateVisitoris untouched.src/bin/dolos/data/dump_state.rs: display arms for the new action variants.Interpretation notes (for review)
hacks::proposals", but that describes the end-state: outcome stamping still feeds the boundary enact/drop classification, and the plan keeps the real RATIFY engine for phase 5 (shadow mode) and hack deletion for phase 6. Dropping the lookup here would break ratification tracking for three phases.NewProposalV2documents this.DormancyFold(design §5.1 voting bullet) are deferred to phase 3, which owns epoch-based DRep expiry + dormancy bookkeeping.VoteCastcarries the pallasVoterenum, which is the voter class and credential in one value (the design listsvoter_class+voterseparately; the enum encodes the same information without invalid combinations).Verification
cargo +nightly fmt --all -- --check— cleancargo clippy --all-targets --all-features -- -D warnings— cleancargo test --workspace --all-targets— all suites pass (19 suites, 0 failures; includes new prop tests: apply/undo and WAL serde roundtrips forNewProposalV2/VoteCast, vote append/replace unit test)ProposalStateshape (indexes 0..=8, 4-variant action) and decodes it as the new entity, asserting the new fields come back empty — the design §6 guarantee, now checked mechanically.Finding (not addressed here)
Existing visitors don't gate on
tx.is_valid(): an invalid tx's certs/proposals currently mutate entity state (e.g.DRepActivitybumps,NewProposalemission), contrary to research §8. The new vote path gates explicitly; fixing the pre-existing paths is left as a follow-up for the plan owner.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes