Skip to main content
import "github.com/populist/protocol/verify"
The verify package is intentionally minimal: it has zero non-stdlib dependencies. Any auditor can run it without installing CometBFT, cloud KMS SDKs, or ZK libraries. It is the independent verification artifact — the thing you hand to a third-party auditor.

BuildSigningPayload

func BuildSigningPayload(
    voteMerkleRoot  []byte,
    voterMerkleRoot []byte,
    totalVotes      int64,
    counts          map[string]int64,
) []byte
Reconstructs the canonical SHA-256 signing payload from tally components. This is the same function called inside state.executePollClose — it produces an identical payload that can be verified against the stored tally signature.

VerifyECDSA

func VerifyECDSA(pubKeyDER, sigDER, payload []byte) (bool, error)
Verifies a DER-encoded ECDSA signature against a payload using the DER-encoded public key. Uses crypto/x509.ParsePKIXPublicKey — no external dependencies.

End-to-end verification

go run ./verify/cmd/verify --api https://your-deployment.example --poll <poll-id>
Or call the functions directly:
// 1. Fetch tally
tally := fetchTally(apiBase, pollID) // GET /polls/{id}/tally

// 2. Fetch voter count (for confirmed_count validation)
count := fetchVoterCount(apiBase, pollID) // GET /polls/{id}/voters

// 3. Reconstruct payload
payload := verify.BuildSigningPayload(
    tally.VoteMerkleRoot,
    tally.VoterMerkleRoot,
    tally.TotalVotes,
    tally.Counts,
)

// 4. Verify
ok, err := verify.VerifyECDSA(tally.PublicKey, tally.Signature, payload)
if !ok || err != nil {
    log.Fatal("tally signature INVALID")
}
fmt.Println("Tally signature: VALID")
fmt.Printf("Sybil gap: %d unconfirmed of %d total\n",
    tally.TotalVotes - tally.ConfirmedCount, tally.TotalVotes)

What verification proves

A valid tally signature proves:
  • The counts, total votes, and Merkle roots were not modified after poll close
  • The signing key was the managed signer registered for this deployment (public key on record)
  • The Merkle roots commit to the exact set of ballot IDs (List 1) and identity hashes (List 2) at the time of close
It does not prove that every voter was a real person — that is what the Sybil-audit ConfirmedCount metric provides.