feat(sidecar): gov-vote handler (PR-B of #163)#175
Conversation
First user-facing gov sign-tx handler. Composes on the sign-tx foundation (#170) and hardening (#173). Closes #163 for the gov-vote slice; gov-submit-proposal (#163-C) ships separately. The handler builds a Cosmos gov v1beta1 MsgVote and delegates to SignAndBroadcast. Idempotency is intentionally NOT handled in the sidecar — the chain overwrites on duplicate (last-write-wins on the (proposalID, voter) storage key), so the caller's request is authoritative. Engine UUID dedupe covers the task-submission case. Crash-rehydration semantics are documented inline: safe for MsgVote specifically because of chain idempotency. Future non-idempotent sign-tx handlers (MsgSend, MsgWithdraw, etc.) need a pre-broadcast SQLite marker — tracked in #174. Also closes #172 item 3 (deferred from #173): the new TestSignedTxHasNoFeePayerOrGranter decodes the signed proto Tx and asserts AuthInfo.Fee.Payer/Granter are empty strings — directly locking the no-fee-grant invariant rather than relying on the FeePayer() interface method (which falls back to signer when the field is unset, masking explicit WithFeePayer(signer) plumbing). Shared ParseVoteOption is exported so client and server validation stay in lockstep — drift between the two enum lists is otherwise inevitable. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Trio cross-review focused on comment bloat. Synthesis: drop paraphrase, keep WHY. Net 23 lines removed. Dropped: - GovVoteRequest "is the wire-format param shape" + "caller's request is authoritative" — both said by the type name and the kept WHY line. - buildVoteMsg docstring — function name + tests file already say it. - "Tracked in #174" forward-pointer — the issue back-pointers this PR; forward refs from source rot faster. - GovVoteTask client-side idempotency note — duplicates the server-side GovVoteRequest comment; one canonical location. - "(closes #172 item 3)" parenthetical on the fee-payer test — belongs in commit/PR, not source. Tightened: - GovVoter docstring — from 4 lines to 2; kept the value-copy WHY. - Handler() rehydration block — collapsed redundancies, kept the "future Msgs must evaluate per-Msg idempotency" template warning (load-bearing for the next sign-tx contributor). - ParseVoteOption — kept the client/server drift WHY; dropped the mapping description (the body is the mapping). Kept (load-bearing WHY): - SECURITY POSTURE NOTE banner — established pattern in sign_and_broadcast.go and trust-boundary risk during the pre-authn phase. - Stale-proposal no-pre-check + TOCTOU reasoning. - Test docstring + inline FeeTx.FeePayer() fallback warning. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fakeTxClient field block had extra whitespace from the lastTxBytes addition; gofmt -s realigns it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit b1061c3. Configure here.
| p["memo"] = t.Memo | ||
| } | ||
| return TaskRequest{Type: t.TaskType(), Params: &p} | ||
| } |
There was a problem hiding this comment.
Missing SubmitGovVoteTask client convenience method
Low Severity
Every other task type in the client SDK (SnapshotRestoreTask, AwaitConditionTask, ConfigApplyTask, etc.) has a corresponding Submit*Task convenience method on SidecarClient in client.go that calls Validate() before delegating to SubmitTask. The new GovVoteTask defines Validate(), TaskType(), and ToTaskRequest() but has no matching SubmitGovVoteTask method, breaking the established SDK pattern. Callers using the generic SubmitTask path can accidentally skip client-side validation.
Reviewed by Cursor Bugbot for commit b1061c3. Configure here.


First user-facing gov sign-tx handler. Composes on the sign-tx foundation (#170) and hardening (#173).
Summary
Test plan
🤖 Generated with Claude Code
Note
High Risk
Adds a new sign-and-broadcast task that can submit on-chain governance votes using the validator’s keyring, which is security-sensitive (especially given the sidecar API exposure). Also introduces new invariants around tx fee payer/granter enforced by tests, but mistakes could still result in unintended transactions/fees.
Overview
Adds a new
gov-votetask end-to-end: registersengine.TaskGovVoteinserve.go, defines the task type inengine/types.go, and implementssidecar/tasks/gov_vote.goto build a gov v1beta1MsgVoteand delegate signing/broadcasting toSignAndBroadcast.Extends the Go client SDK (
sidecar/client/tasks.go) with a typedGovVoteTask(validation + wire params) and shares vote-option parsing via exportedtasks.ParseVoteOption.Hardens tx signing tests by capturing broadcast tx bytes and asserting, at the protobuf level, that
AuthInfo.Fee.PayerandAuthInfo.Fee.Granterare empty, preventing accidental fee delegation regressions.Reviewed by Cursor Bugbot for commit b1061c3. Bugbot is set up for automated code reviews on this repo. Configure here.