state package is the canonical two-list apparatus for shyvoting and the
nearest reference for shyshares. It is the place where canonical anonymous
transactions mutate consensus state. Other packages are support infrastructure.
Invariants
The state machine enforces three invariants on every committed block:len(voteDirections) == len(voterRegistry) == TotalVotes— count-match- Voter device signature verifies before any ballot is accepted — oracle prevention
- Managed IDV attestation verifies before any ballot — identity binding
executeBallotCast.
Invariants 2 and 3 are cryptographic: validateBallotCast verifies voter_sig
and the IDV attestation signature before any state mutation.
Identity tier routing — both tiers use a device-held secret; the difference is
receipt store visibility. If a ZK verifier is loaded, the ZK path is taken:
identity_hash = ZKNullifier; IDV attested only a commitment and cannot compute the
nullifier; receipt store is unlinkable even under subpoena. If no ZK verifier is loaded
(preferred non-ZK tier): identity_hash = SHA-256(voter_pub_key ‖ poll_id); IDV attested
voter_pub_key and can compute identity_hash; receipt store is linkable under lawful
process; biometric recovery on any device.
NewState
- If
kmsKeyIDis non-empty, a production signing backend is initialised - If
kmsKeyIDis empty, tally signing falls back to a SHA-256 stub (for local development)
SetDiditPubKey before processing ballots. SetZKVerifier is optional:
if omitted the state machine uses the preferred device-keypair tier. Both are
fail-safe: a missing provider key causes all ballot transactions to be rejected regardless
of tier.
SetZKVerifier / SetDiditPubKey
FinalizeBlock is ever invoked. app.New handles this
automatically when using the app package.
ValidateTx / ExecuteTx
ValidateTx is safe to call concurrently (read-only). ExecuteTx must be called
sequentially within a block (called by FinalizeBlock in order).
Commit
WriteSync batch, increments the
block height, and returns the new app hash.
App hash is SHA-256 over all sorted map entries (polls, vote directions, voter registry,
tallies, validators, confirms). Sort order is lexicographic by key — deterministic across
all validators for the same block.
Query
| Path | Returns |
|---|---|
/poll/{poll_id} | types.Poll JSON |
/tally/{poll_id} | types.Tally JSON |
/vote/{ballot_id} | types.VoteRecord JSON |
/confirms/{poll_id} | {"confirmed_count": N} JSON |
/confirms/{id} returns only the aggregate count, not individual identity hashes.
Embedding in your runtime
Preferred non-ZK tier (device keypair):app.New which handles all of this for you.