vote-sdk

module
v0.5.59 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 2, 2026 License: MIT

README

Shielded Vote Chain

A Cosmos SDK application chain for private on-chain voting using Zcash-derived cryptography (Halo2 ZKP circuits, RedPallas signatures, ElGamal encryption, Poseidon Merkle trees).

Specification: Shielded Voting Protocol ZIP

Quick Start

Prerequisites
  • mise — task runner + toolchain installer (recommended)
  • Alternatively, install Go 1.24+ and Rust stable directly
Run a local chain with mise
# 1. Install the toolchain listed in mise.toml (Go, Rust, Node, buf, jq).
mise install

# 2. Build + install svoted and create-val-tx into $GOBIN (on PATH).
mise run install

# 3. Create .env with the vote-manager private key(s) (one-time).
cp .env.example .env
# Edit .env and set VM_PRIVKEYS (comma-separated 64-char hex keys; generate
# each with: openssl rand -hex 32). For a single-vote-manager chain, provide
# exactly one key — any-of-N means any vote manager in the list can authorize
# vote-manager-gated operations.

# 4. Wipe + init a single-validator devnet, then start the daemon.
mise run chain:init
mise run chain:start

Other useful tasks: mise run chain:init-multi + mise run chain:start-multi for a 3-validator local chain, mise run test:go for the Go test suite, mise tasks to list everything.

Contributing from a clone (local binaries, join script with SVOTE_LOCAL_BINARIES, task reference): see CONTRIBUTING.md.

Without mise (direct make)
# Build and install
make circuits      # Rust ZKP circuit static library
make build-ffi     # svoted with ZKP + signature verification
# (or) make build  # no FFI — stubs Halo2/RedPallas verification; dev only

# Init + start (same .env step as above applies)
make init
make start
Consensus timing defaults

svoted overrides CometBFT defaults at startup to reduce block time (~1.2s with 30 validators vs ~4-6s with defaults). See docs/blocktimes.md for the full parameter table, Osmosis comparison, and benchmark results confirming safety.

Test
# Go unit tests (no Rust dependency)
make test

# Halo2 ZKP verification tests (requires: make circuits)
make test-halo2

# All FFI-backed tests (Halo2 + RedPallas)
make test-all-ffi

# Rust circuit unit tests
make circuits-test

Project Structure

app/            Cosmos app, ABCI handlers, PrepareProposal/ProcessProposal
api/            REST API handlers, codec, tx wrapper
circuits/       Rust crate: Halo2 ZKP + RedPallas FFI (staticlib for CGo)
cmd/svoted/     Chain binary and CLI commands
crypto/         Cryptographic primitives (ECIES, ElGamal, Shamir, ZKP, etc.)
deploy/         Caddyfile for HTTPS reverse proxy
docs/           Deployment and protocol documentation
internal/       Helper server (share submission, SQLite, random delays)
proto/          Protobuf definitions (buf-managed)
scripts/        Chain init scripts, validator tooling
x/vote/         Vote module: ceremony, voting rounds, tally, keeper

License

This project is licensed under the MIT License.


Protocol Documentation

Technical Assumptions
  1. The chain launches with a single genesis validator. Additional validators join post-genesis via MsgCreateValidatorWithPallasKey, which atomically creates the validator and registers their Pallas key for the ceremony. Raw MsgCreateValidator is blocked in the ante handler for live transactions. Validator set changes beyond that are handled via major upgrades or a PoA module (future).
  2. Client interaction avoids Cosmos SDK protobuf encoding:
    • Tx submission: Client sends a plain JSON POST; server handler parses JSON and encodes as needed.
    • Query: gRPC gateway supports JSON out-of-the-box.
  3. No native x/gov module. The vote module implements custom private voting instead of reusing standard Cosmos governance.

Architecture

Module: x/vote

The vote module has two major subsystems: the EA Key Ceremony (automatic per voting round) and Voting Rounds (transition from PENDING through ceremony to ACTIVE).

EA Key Ceremony (Per-Round)

The EA key ceremony runs automatically per voting round. Each MsgCreateVotingSession creates a round in PENDING status and snapshots all eligible validators (bonded + registered Pallas key) into the round's ceremony fields. The ceremony proceeds automatically via PrepareProposal — no manual intervention is needed after initial Pallas key registration.

Per-Round Ceremony State Machine

Ceremony state is stored on the VoteRound itself (fields ceremony_status, ceremony_validators, etc.). There is no global singleton ceremony state.

  PENDING (REGISTERING) ──> PENDING (DEALT) ──> ACTIVE (CONFIRMED)
             │                    │                (all acked)
             │ timeout            │ timeout (≥ 1/2)
             v                    v
  CEREMONY_FAILED     ACTIVE (CONFIRMED)
                         + strip non-ackers
                                  │ timeout (< 1/2)
                                  v
                          CEREMONY_FAILED
From To Trigger Condition
REGISTERING DEALT Auto-deal via PrepareProposal Block proposer is a ceremony validator
DEALT CONFIRMED + ACTIVE MsgAckExecutiveAuthorityKey All validators acked (fast path)
DEALT CONFIRMED + ACTIVE EndBlocker timeout >= 1/2 acked at timeout; non-ackers stripped
REGISTERING CEREMONY_FAILED EndBlocker timeout DKG contributions incomplete at timeout; non-contributors jailed
DEALT CEREMONY_FAILED EndBlocker timeout < 1/2 acked or below published threshold

Key behaviors:

  • Fast path vs timeout — the fast path confirms when ALL validators ack (no stripping needed). The timeout path confirms with >= 1/2 acks (integer arithmetic: acks * 2 >= validators) and strips non-ackers.
  • Auto-deal — the block proposer automatically deals when it detects a PENDING round in REGISTERING state. No manual ceremony.sh deal step.
  • Auto-ack — each block proposer auto-acks via PrepareProposal when it detects a DEALT round.
  • Ceremony timeout jailing — validators who miss REGISTERING contributions are jailed through x/slashing until the chain's downtime jail duration elapses. DEALT non-ackers are stripped from successful timeout confirmations but are not jailed because a missing ack is not reliable blame evidence.
  • Ceremony log — each state transition appends a timestamped entry to ceremony_log on the round, visible in queries and the admin UI.
Pallas Key Registration and Rotation

Validators register their Pallas key via MsgRegisterPallasKey or MsgCreateValidatorWithPallasKey. Keys are stored in a global registry (prefix 0x0C) and persist across rounds.

A registered key can be replaced via MsgRotatePallasKey. Rotation is rejected while the validator is participating in any PENDING round ceremony (the snapshotted key would become stale and ECIES payloads would be undecryptable). The old key's reverse-lookup index is cleaned up so it can be reused.

Auto-Deal and Auto-Ack via PrepareProposal

PrepareProposal composes two ceremony injectors:

  1. Auto-deal — if a PENDING round is in REGISTERING state and the proposer is a ceremony validator, generate ea_sk, Shamir-split it into (t, n) shares, ECIES-encrypt share_i to each validator, publish VK_i = share_i * G and threshold = ceil(n/2), and inject MsgDealExecutiveAuthorityKey.
  2. Auto-ack — if a PENDING round is in DEALT state and the proposer hasn't acked, decrypt the payload to recover their share, verify share_i * G == VK_i (threshold mode) or ea_sk * G == ea_pk (legacy), inject MsgAckExecutiveAuthorityKey, and write the share/key to disk.
Timeout (EndBlocker)

REGISTERING and DEALT phases have timeouts (default: 10 minutes):

  • REGISTERING timeout: Jail validators that did not contribute DKG material and mark the pending round CEREMONY_FAILED.
  • DEALT timeout, >= 1/2 acked: Strip non-ackers, confirm ceremony, activate round.
  • DEALT timeout, < 1/2 acked: Mark the pending round CEREMONY_FAILED without jailing non-ackers, because missing acks are not reliable blame evidence. A later create transaction can retry the same vote metadata because vote_round_id includes the round creation height.
ECIES Encryption Scheme

Each validator's ea_sk share is encrypted using ECIES over the Pallas curve with SpendAuthG as the generator:

  1. E = e * SpendAuthG (ephemeral public key)
  2. S = e * pk_i (ECDH shared secret)
  3. k = SHA256(E_compressed || S.x) (symmetric key)
  4. ct = ChaCha20-Poly1305(k, nonce=0, ea_sk) (authenticated encryption)
Vote-Manager Set (any-of-N)

The vote-manager set is a list of on-chain account addresses that gate who can create voting sessions and authorize unrestricted sends. Any member of the set can act alone (any-of-N semantics — strict membership, no threshold, no multi-sig aggregation). The vote-manager set must be non-empty at genesis (no bootstrap path).

MsgUpdateVoteManagers -- Atomically replaces the vote-manager set.

  • Callable by any current vote manager
  • Validation: the new set must be non-empty, each address must be valid bech32, and no duplicates
  • Does not move balances — each vote manager holds their own funds (the bank-module per-account balance). Removed vote managers keep whatever usvote they had, but their sends become rejected because they are no longer vote managers and are not bonded validators (see "Drain before removal" below)
  • Stored as a singleton VoteManagerSet { repeated string addresses } in the KV store (key 0x0A)

QueryVoteManagers returns the current vote-manager set.

Drain before removal. When a vote manager is removed via MsgUpdateVoteManagers, their remaining usvote balance stays in their account but becomes one-way frozen: they cannot MsgAuthorizedSend (no longer a vote manager, not a validator), but can still receive from active vote managers. To avoid stranded balance, an active vote manager should MsgAuthorizedSend to drain a departing vote manager's balance before calling MsgUpdateVoteManagers to remove them.

Voting Rounds

After the ceremony reaches CONFIRMED and at least one vote manager is set, voting sessions can be created.

ACTIVE ──> TALLYING ──> FINALIZED
  ^
  │ (gated: requires CONFIRMED ceremony + non-empty vote-manager set)

MsgCreateVotingSession reads ea_pk from the confirmed ceremony state (not from the message). The round stores its own copy of ea_pk for future key rotation support. Any vote manager can create voting sessions. An optional description field provides human-readable context for the round.

MsgSubmitPartialDecryption is auto-injected via PrepareProposal when a round is in TALLYING state and threshold mode is active. Each validator submits D_i = share_i * C1 per accumulator. Cannot be submitted through the mempool.

MsgSubmitTally is auto-injected via PrepareProposal once t partial decryptions exist on-chain. The proposer Lagrange-combines them to recover ea_sk * C1, runs BSGS, and submits plaintext totals. Cannot be submitted through the mempool.

PrepareProposal / ProcessProposal Pipeline

PrepareProposal composes four injectors that run sequentially on each proposed block:

  1. Ceremony deal injection — if a PENDING round is in REGISTERING and the proposer is a ceremony validator, auto-deal via MsgDealExecutiveAuthorityKey
  2. Ceremony ack injection — if a PENDING round is in DEALT and the proposer hasn't acked, auto-ack via MsgAckExecutiveAuthorityKey
  3. Partial decryption injection (threshold mode) — if a TALLYING round has threshold > 0 and the proposer hasn't yet submitted, compute D_i = share_i * C1 per accumulator and inject MsgSubmitPartialDecryption
  4. Tally injection — when t partials are on-chain (threshold mode) or ea_sk is on disk (legacy), Lagrange-combine and BSGS-solve, then inject MsgSubmitTally

ProcessProposal validates all injected txs on non-proposer validators before accepting a block. MsgAckExecutiveAuthorityKey, MsgSubmitPartialDecryption, and MsgSubmitTally are all blocked from the mempool (CheckTx rejects them).

Custom Wire Format
Rationale

The standard Cosmos SDK Tx envelope requires a signer address, fee fields, and a conventional signature (secp256k1 or ed25519). Vote-round messages (MsgDelegateVote, MsgCastVote, MsgRevealShare) cannot use this envelope because they are authenticated via ZKP + RedPallas spend-auth signatures — there is no conventional Cosmos account involved. Similarly, MsgDealExecutiveAuthorityKey, MsgAckExecutiveAuthorityKey, and MsgSubmitPartialDecryption are auto-injected by the block proposer via PrepareProposal and are never client-signed at all.

The custom wire format is the minimal encoding that satisfies both cases: a single-byte type tag lets the TxDecoder unambiguously identify the message type without parsing a full TxBody, and the tag byte acts as the sole discriminator between the custom path and the standard Cosmos SDK path. Messages that do have a conventional signer (ceremony setup messages, MsgCreateVotingSession) still use the standard Tx envelope and flow through normal signature verification.

Wire Format

Each custom transaction is a 1-byte tag followed by a protobuf-encoded message body:

[tag (1 byte)] [proto-encoded message body]
Tag Message Category Auth mechanism
0x01 MsgCreateVotingSession Voting round Standard Cosmos Tx (signed)
0x02 MsgDelegateVote Voting round ZKP #1 + RedPallas
0x03 MsgCastVote Voting round ZKP #2 + RedPallas
0x04 MsgRevealShare Voting round ZKP #3
0x05 MsgSubmitTally Voting round (injected) Proposer identity check
0x06 MsgRegisterPallasKey Ceremony Standard Cosmos Tx (signed)
0x07 MsgDealExecutiveAuthorityKey Ceremony (injected) Proposer identity check
0x08 MsgAckExecutiveAuthorityKey Ceremony (injected) Proposer identity check
0x09 MsgCreateValidatorWithPallasKey Ceremony Standard Cosmos Tx (signed)
0x0C MsgUpdateVoteManagers Management Standard Cosmos Tx (signed)
0x0D MsgSubmitPartialDecryption Tallying (injected) Proposer identity check

Any transaction whose first byte does not match a known tag is decoded as a standard Cosmos SDK Tx. Tag 0x0A is deliberately skipped because it collides with the standard Cosmos Tx protobuf encoding (field 1, wire type 2) — this collision is what makes the two decoders unambiguously distinguishable by a single byte peek. Note that raw MsgCreateValidator is blocked by the ante handler for live transactions -- post-genesis validators must use MsgCreateValidatorWithPallasKey (tag 0x09) instead.

Message Authentication Invariants

Every message has a specific set of auth checks enforced across the ABCI pipeline. The table below lists the complete check set for each message. "PP" = PrepareProposal, "ProcessProp" = ProcessProposal.

Standard Cosmos SDK messages

These flow through the standard ante chain: signature verification (SigVerificationDecorator), then CeremonyValidatorDecorator for validator-gated types.

Message Who can submit Ante checks MsgServer checks
MsgRegisterPallasKey Any bonded validator secp256k1 sig + CeremonyValidatorDecorator (bonded validator gate) Valid Pallas point; no duplicate registration
MsgRotatePallasKey Any bonded validator secp256k1 sig + CeremonyValidatorDecorator (bonded validator gate) Valid Pallas point; existing key required; no in-flight ceremony; new PK globally unique
MsgCreateValidatorWithPallasKey Anyone (becomes a validator) secp256k1 sig; exempt from CeremonyValidatorDecorator Delegates to x/staking CreateValidator; registers Pallas key; rejects duplicates
MsgUpdateVoteManagers Any current vote manager secp256k1 sig; exempt from CeremonyValidatorDecorator (has own auth) ValidateVoteManagerOnly: creator must be in the vote-manager set; atomic replace of the whole set; no balance movement
MsgCreateVotingSession Any current vote manager secp256k1 sig (standard Cosmos Tx) ValidateVoteManagerOnly: creator must be in the vote-manager set
MsgAuthorizedSend Any vote manager or bonded validator secp256k1 sig (standard Cosmos Tx) Any vote manager can send to anyone; validators can send to any vote manager or another bonded validator; all other senders rejected
MsgCreateValidator Blocked post-genesis Ante handler rejects at BlockHeight > 0 N/A — never reaches MsgServer
MsgSend / MsgMultiSend Blocked Not in message whitelist N/A — never reaches MsgServer
Vote-round messages (custom wire format, ZKP/RedPallas auth)

These use the custom wire format and bypass the Cosmos Tx envelope. Auth is handled by the ValidateVoteTx pipeline in x/vote/ante.

Message Who can submit Ante checks MsgServer checks
MsgDelegateVote Any Zcash note holder (anonymous) ValidateBasic (field sizes); round ACTIVE; gov nullifier uniqueness; RedPallas sig over sighash; ZKP #1 (delegation proof: note ownership, VAN encoding, nc_root, nf_imt_root) Record gov nullifiers; append van_cmx to commitment tree
MsgCastVote Any delegation holder (anonymous) ValidateBasic; round ACTIVE; VAN nullifier uniqueness; RedPallas sig over canonical sighash; ZKP #2 (vote commitment: VAN ownership, ea_pk binding, commitment tree anchor) Record VAN nullifier; append vote_authority_note_new + vote_commitment to tree
MsgRevealShare Any vote holder (anonymous) ValidateBasic; round ACTIVE or TALLYING; share nullifier uniqueness; ZKP #3 (vote share: share ownership, commitment tree anchor) Record share nullifier; HomomorphicAdd enc_share into tally accumulator
Proposer-injected messages (custom wire format, proposer identity auth)

These are auto-injected by PrepareProposal and cannot be submitted through the mempool (CheckTx/ReCheckTx reject them). Auth is enforced at three layers: ante handler (per-tag dispatch), ProcessProposal (non-proposer validators verify before accepting a block), and MsgServer (FinalizeBlock execution).

Message Ante check ProcessProposal check MsgServer check
MsgDealExecutiveAuthorityKey ValidateProposerIsCreator (mempool block + creator == proposer) Round PENDING + REGISTERING; payload count matches; creator is ceremony validator; creator == proposer ValidateProposerIsCreator; round PENDING + REGISTERING; creator in ceremony validators; ea_pk valid; payloads 1:1 with validators; threshold + VK validation
MsgAckExecutiveAuthorityKey ValidateProposerIsCreator (mempool block + creator == proposer) Round PENDING + DEALT; creator is ceremony validator; no duplicate ack; creator == proposer ValidateProposerIsCreator; round PENDING + DEALT; creator in ceremony validators; no duplicate ack
MsgSubmitPartialDecryption ValidateProposerIsCreator (mempool block + creator == proposer) Round TALLYING + threshold > 0; creator is ceremony validator; ValidatorIndex == ShamirIndex; no duplicate; creator == proposer ValidateProposerIsCreator; round TALLYING + threshold > 0; creator in ceremony validators; ValidatorIndex == ShamirIndex; no duplicate; entries are valid Pallas points
MsgSubmitTally ValidateVoteTxValidateProposerIsCreator (mempool block + creator == proposer) Round TALLYING; creator == proposer ValidateProposerIsCreator; round TALLYING; verify each entry against on-chain accumulators (DLEQ proof in legacy mode, Lagrange re-derivation in threshold mode); transition to FINALIZED
Key design invariant

ValidateProposerIsCreator is the unified proposer identity check shared by all four injected message types. It enforces two properties:

  1. Mempool exclusion: IsCheckTx() || IsReCheckTx() returns an error, preventing external submission.
  2. Proposer binding: During FinalizeBlock, msg.Creator must equal the operator address of the validator whose consensus key matches BlockHeader.ProposerAddress. This prevents a malicious proposer from injecting messages on behalf of other validators.
Message Whitelist and Ante Handler Design

Standard Cosmos transactions pass through a two-layer gate before reaching the decorator chain. Both layers are in app/ante.go and app/ante_whitelist.go.

Layer 1: Pre-filter in NewDualAnteHandler (before any decorator runs)
  1. Single-message only. Multi-message transactions are rejected unconditionally. This eliminates the noop-signer attack class where a zero-signer message (e.g. MsgRevealShare) piggybacks on a legitimately-signed carrier message.
  2. Vote/ceremony messages blocked (defense-in-depth). isVoteModuleMsg rejects MsgDelegateVote, MsgCastVote, MsgRevealShare, MsgDealExecutiveAuthorityKey, MsgAckExecutiveAuthorityKey, MsgSubmitPartialDecryption, and MsgSubmitTally. These must enter via the custom VoteTxWrapper path where ZKP/RedPallas verification runs. The whitelist (Layer 2) would also catch these, but this explicit type check fires earlier and produces a more actionable error.
  3. MsgCreateValidator blocked post-genesis. At BlockHeight > 0, raw MsgCreateValidator is rejected — validators must use MsgCreateValidatorWithPallasKey to atomically register a Pallas key. The message is allowed at height 0 for genesis gentx bootstrapping.
Layer 2: MessageWhitelistDecorator (inside the standard ante chain)

A positive-security allowlist: only messages whose proto type URL appears in DefaultAllowedMessages() are accepted. Any new message type must be explicitly added here before it can be processed. The current allowed set is:

Module Allowed messages
Vote MsgCreateVotingSession, MsgRegisterPallasKey, MsgRotatePallasKey, MsgCreateValidatorWithPallasKey, MsgUpdateVoteManagers, MsgAuthorizedSend
Staking MsgCreateValidator (genesis only — blocked post-genesis by Layer 1), MsgEditValidator
Slashing MsgUnjail

Explicitly excluded (and the reasons):

Excluded messages Reason
MsgSend, MsgMultiSend Replaced by MsgAuthorizedSend with role-based restrictions. Unrestricted transfers would let anyone accumulate stake and create a validator, bypassing the controlled validator set.
MsgDelegate, MsgUndelegate, MsgBeginRedelegate Prevents validators from reorganizing stake without a vote manager. Initial self-delegation is handled atomically by MsgCreateValidatorWithPallasKey.
MsgWithdrawDelegatorReward, MsgWithdrawValidatorCommission Prevents extracting staking rewards as liquid tokens that could be transferred outside of vote-manager control.
MsgFundCommunityPool, MsgSetWithdrawAddress, MsgUpdateParams No governance module; these have no legitimate use on this chain.
All vote/ceremony ZKP messages Must use the custom VoteTxWrapper wire format with ZKP/RedPallas authentication. Blocked by both Layer 1 and Layer 2.
MsgAuthorizedSend authorization rules

MsgAuthorizedSend is the only coin-transfer path on this chain. Authorization is enforced in the MsgServer handler (x/vote/keeper/msg_server_send.go):

  • Any vote manager can send to anyone (distributes stake to new validators).
  • Bonded validators can send to any vote manager or other bonded validators (operational redistribution within the trusted set).
  • All other senders are rejected. Note that a former vote manager who has been removed from the set is neither a vote manager nor a validator, so their remaining balance becomes one-way frozen — drain before removal.
Design assumptions
  1. The vote-manager set has full control over stake distribution. Validators receive tokens via MsgAuthorizedSend and bond them during MsgCreateValidatorWithPallasKey. Each vote manager holds their own pre-funded balance; the genesis stake pool is split evenly across the set to preserve total supply.
  2. The whitelist is a compile-time constant. Adding a new message type requires a code change, rebuild, and coordinated chain upgrade.
  3. Custom wire format messages (VoteTxWrapper) are never subject to the whitelist — they are routed to the ZKP/RedPallas validation pipeline before the standard ante chain runs.
  4. If a custom wire format message is somehow removed from isVoteModuleMsg (Layer 1), the whitelist (Layer 2) still blocks it since it is not in DefaultAllowedMessages().
REST API

The chain exposes a JSON REST API alongside CometBFT RPC. Clients POST JSON bodies for transaction submission and GET for queries — no protobuf encoding required on the client side.

Transaction Endpoints (Custom Wire Format)

Vote-round messages use the custom wire format and are submitted as JSON POST requests:

Method Path Description
POST /shielded-vote/v1/delegate-vote Submit a delegation proof (ZKP #1)
POST /shielded-vote/v1/cast-vote Cast an encrypted vote (ZKP #2)
POST /shielded-vote/v1/reveal-share Reveal an encrypted share (ZKP #3)

These endpoints accept JSON, encode the message with the custom wire format, and broadcast via CometBFT's broadcast_tx_sync. MsgSubmitTally, MsgDealExecutiveAuthorityKey, MsgAckExecutiveAuthorityKey, and MsgSubmitPartialDecryption have no REST endpoints — they are proposer-only and auto-injected via PrepareProposal.

Ceremony and management messages (MsgRegisterPallasKey, MsgRotatePallasKey, MsgCreateValidatorWithPallasKey, MsgUpdateVoteManagers, MsgCreateVotingSession) are standard Cosmos SDK transactions routed through the MsgServiceRouter. They can be submitted via the Cosmos SDK CLI or gRPC gateway.

Query Endpoints
Method Path Description
GET /shielded-vote/v1/ceremony Current ceremony state and status
GET /shielded-vote/v1/rounds List all stored vote rounds
GET /shielded-vote/v1/rounds/active Currently active voting round
GET /shielded-vote/v1/round/{round_id} Voting round by hex round ID
GET /shielded-vote/v1/vote-summary/{round_id} Denormalized round summary with proposals
GET /shielded-vote/v1/tally/{round_id}/{proposal_id} Tally for a specific proposal
GET /shielded-vote/v1/tally-results/{round_id} All tally results for a round
GET /shielded-vote/v1/commitment-tree/{round_id}/{height} Vote commitment tree at block height
GET /shielded-vote/v1/commitment-tree/{round_id}/latest Latest vote commitment tree
GET /shielded-vote/v1/commitment-tree/{round_id}/leaves Tree leaves (?from_height=X&to_height=Y)
GET /shielded-vote/v1/pallas-keys All registered Pallas keys
GET /shielded-vote/v1/vote-managers Current vote-manager set (any-of-N)
GET /shielded-vote/v1/genesis Chain genesis JSON
GET /shielded-vote/v1/snapshot-data/{height} Nullifier snapshot data at block height
GET /shielded-vote/v1/tx/{hash} Transaction status by hash
Helper Sentry Observability

The helper server uses Sentry when [helper].sentry_dsn is set in app.toml or SENTRY_DSN is present in the environment. Sentry events include a stage tag that identifies the helper code path that emitted the error, such as enqueue, process_share, leaf_read, helper_new, or tree_status.

When a voting round closes, the helper summarizes queued shares before purging expired witness data. If any shares for that round are still pending or failed, it emits a Sentry error with stage=round_closed_unsubmitted_shares and tags for round_id, total_shares, pending_shares, failed_shares, submitted_shares, and unsubmitted_shares. Configure Sentry issue alerts on that stage tag to page when share data was accepted by the helper but not submitted on-chain before the round closed.

On-Chain State (KV Store Keys)
Key Type Description
0x09 CeremonyState (singleton) EA key ceremony lifecycle
0x0A VoteManagerSet (singleton) Vote-manager addresses (any-of-N)
0x01 VoteRound (per round) Voting session state
0x02-0x08 Various Nullifiers, tallies, commitment tree, etc.
CeremonyState Fields
enum CeremonyStatus {
  CEREMONY_STATUS_UNSPECIFIED   = 0;
  CEREMONY_STATUS_REGISTERING   = 1; // Accepting validator pk_i registrations (no timeout)
  CEREMONY_STATUS_DEALT         = 2; // DealerTx landed, awaiting acks
  CEREMONY_STATUS_CONFIRMED     = 3; // All acked (fast path) or >=1/2 acked at timeout, ea_pk ready
}

message CeremonyState {
  CeremonyStatus              status        = 1;
  bytes                       ea_pk         = 2;  // Set when DealerTx lands
  repeated ValidatorPallasKey validators    = 3;  // All registered pk_i
  repeated DealerPayload      payloads      = 4;  // ECIES envelopes from DealerTx
  repeated AckEntry           acks          = 5;  // Per-validator ack status
  string                      dealer        = 6;  // Validator address of the dealer
  uint64                      phase_start   = 7;  // Unix seconds when current phase started
  uint64                      phase_timeout = 8;  // Timeout in seconds for current phase
}

Directories

Path Synopsis
Package api provides the JSON REST API and wire-format codec for vote transactions that bypass the Cosmos SDK Tx envelope.
Package api provides the JSON REST API and wire-format codec for vote transactions that bypass the Cosmos SDK Tx envelope.
cmd
svoted command
voting-config command
crypto
elgamal
Package elgamal implements additively homomorphic El Gamal encryption over the Pallas curve using the mikelodder7/curvey library.
Package elgamal implements additively homomorphic El Gamal encryption over the Pallas curve using the mikelodder7/curvey library.
shamir
Package shamir implements (t, n) Shamir secret sharing over the Pallas scalar field (Fq).
Package shamir implements (t, n) Shamir secret sharing over the Pallas scalar field (Fq).
ffi
ncroot
Package ncroot provides Go bindings to the Rust FFI function that computes the Orchard note commitment tree root from a hex-encoded frontier.
Package ncroot provides Go bindings to the Rust FFI function that computes the Orchard note commitment tree root from a hex-encoded frontier.
redpallas
Package redpallas provides an interface for RedPallas signature verification.
Package redpallas provides an interface for RedPallas signature verification.
roundid
Package roundid provides Go bindings to the Rust FFI function that derives vote_round_id from a creation height and session fields via Poseidon hash.
Package roundid provides Go bindings to the Rust FFI function that derives vote_round_id from a creation height and session fields via Poseidon hash.
votecommitment
Package votecommitment provides Go bindings to the Rust FFI function that computes the vote commitment hash via Poseidon.
Package votecommitment provides Go bindings to the Rust FFI function that computes the vote commitment hash via Poseidon.
votetree
Package votetree provides Go CGO bindings to the Poseidon Merkle tree exported by the shielded-vote-circuits Rust static library (libshielded_vote_circuits.a).
Package votetree provides Go CGO bindings to the Poseidon Merkle tree exported by the shielded-vote-circuits Rust static library (libshielded_vote_circuits.a).
zkp
Package zkp provides interfaces for zero-knowledge proof verification.
Package zkp provides interfaces for zero-knowledge proof verification.
internal
admin
Package admin serves the voting-config endpoint by proxying the GitHub Pages CDN (valargroup/token-holder-voting-config) and exposes HTTP endpoints for validator join registration (pending queue in SQLite).
Package admin serves the voting-config endpoint by proxying the GitHub Pages CDN (valargroup/token-holder-voting-config) and exposes HTTP endpoints for validator join registration (pending queue in SQLite).
helper
Package helper implements the share processing pipeline that receives encrypted voting shares from wallets, waits until wallet-provided submit_at times, generates ZKP 3 proofs, and submits MsgRevealShare to the chain.
Package helper implements the share processing pipeline that receives encrypted voting shares from wallets, waits until wallet-provided submit_at times, generates ZKP 3 proofs, and submits MsgRevealShare to the chain.
ui
Package ui serves the admin SPA as static files from a pre-built Vite dist directory.
Package ui serves the admin SPA as static files from a pre-built Vite dist directory.
scripts
create-val-tx command
Command create-val-tx constructs a MsgCreateValidatorWithPallasKey, signs it via `svoted tx sign`, and broadcasts it via `svoted tx broadcast`.
Command create-val-tx constructs a MsgCreateValidatorWithPallasKey, signs it via `svoted tx sign`, and broadcasts it via `svoted tx broadcast`.
Package testutil provides shared test infrastructure for the Shielded-Vote chain integration tests.
Package testutil provides shared test infrastructure for the Shielded-Vote chain integration tests.
x
vote/ante
Package ante implements the validation pipeline for vote module transactions.
Package ante implements the validation pipeline for vote module transactions.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL