feat: support optional memo on votes (soft fork)#1419
Draft
lucasmenendez wants to merge 4 commits into
Draft
Conversation
Coverage Report for CI Build 28508953749Coverage at 62.835% (no base build to compare)Details
Uncovered Changes
Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
Activate the repo's local-path replace so this PR builds against the unpublished VoteEnvelope.memo field while vocdoni/dvote-protobuf#75 is in review. The final commit of this PR replaces this with a bump to the published tag (go.vocdoni.io/proto v1.16.0) and re-comments the directive. DO NOT MERGE while this replace is present.
Honor the new optional VoteEnvelope.memo free-text field (max 256 bytes): - genesis: add VoteMemoActive(chainID, height), a per-chain soft-fork gate. Heights are placeholders (forkNever) except the local TEST chain; they must be scheduled for dev/stage/lts before release, coordinated with the validator upgrade so every validator runs this binary before the activation height. - transaction: in VoteTxCheck, once the fork is active for the chain, validate the 256-byte cap and copy the memo onto the state Vote. Before activation the memo is ignored (not stored, not hashed) so upgraded nodes produce the same state as pre-fork nodes, keeping the rollout window safe. - state: persist the memo in StateDBVote and include it in Vote.Hash() only when non-empty. Because the proto field is `optional`, an empty memo is stored as nil (absent), not a present empty string, so the marshaled StateDBVote bytes and the state hash stay identical to a pre-fork vote.
- VoteMemoActive: unknown chain inactive, TEST active from genesis, below/at/above a scheduled height, and forkNever never active. - Vote.Hash: empty memo matches the pre-fork baseline, a non-empty memo changes the hash deterministically, and DeepCopy preserves the memo. - AddVote/Vote: a memo survives the StateDBVote round-trip, and an empty memo is stored as an absent field (nil), preserving state-hash determinism.
- api: add `memo` to the Vote response and populate it in GET /votes/{voteId}
from the authoritative state (the memo is not indexed).
- apiclient: add Memo to VoteData and attach it to the VoteEnvelope before the
tx is marshaled and signed, so it is covered by the vote signature.
536a060 to
c82a509
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Consumer side of the
VoteEnvelope.memofeature. Depends on vocdoni/dvote-protobuf#75.What
Adds support for the optional free-text
memofield on votes (max 256 bytes), enabling an open "Other" answer. Because the memo is persisted in consensus state and hashed into the vote, it is gated behind a height-activated soft fork so every validator enables it at the same block.Commits
chore[temporary] activate the local-path replacego.vocdoni.io/proto => ../dvote-protobufso this PR builds against the unpublished proto. Requires a siblingdvote-protobufcheckout on the Clean workspace before compose test #75 branch withmake golangrun locally (bindings are CI-generated, not committed).feat(vochain)consensus core — fork gate (genesis.VoteMemoActive), 256-byte validation + memo copy inVoteTxCheck, persistence + hashing instate.test(vochain)fork gate, hash determinism, StateDBVote persistence + empty-memo determinism.feat(api)exposememoinGET /votes/{voteId}and let clients set it viaapiclient.VoteData.Safety / determinism
Tx) still verify.Vote.Hash()only mixes in the memo when non-empty.optionalfield handling — the proto field isoptional(per Clean workspace before compose test #75 review), soMemois a*string. An empty memo is stored as nil (absent), never a present empty string, so the marshalledStateDBVotebytes — and thus the state hash — stay identical to a pre-fork vote.Tx), set on the envelope before marshalling inapiclient.require go.vocdoni.io/prototo the published tag (v1.16.0) and re-comment the local replace (commit 1).vochain/genesis/forks.gofor dev/stage/lts (currentlyforkNever; only the localvocdoni/TEST/1chain is active from genesis). Coordinate so every validator runs this binary before the chosen height.Notes
GET /votes/{voteId}reads the memo directly from authoritative state (the indexer doesn't store it) — consistent with how it already reaches into state for decryption keys. The memo is intentionally not added to the paginated list endpoint.