Skip to content

Contest Ballot Checkpoint

Description

Periodically, as ballots are collected, a summary of all newly collected ballots is published in a Contest Ballot Checkpoint document. Each checkpoint accumulates state over time, committing to the current set of accepted ballots via an SMT root and entry count, and optionally listing any ballots rejected in the same interval.

Checkpoint documents are chained together. The final document in the sequence is indicated by the chain metadata.

Typically each Contest Ballot Checkpoint is made immutable by referencing it on the blockchain most applicable to the Contest.

Different blockchains will have different mechanisms for referencing checkpoint documents. For example, Cardano can encode a document_ref in on‑chain metadata, signed by the ballot‑box (bulletin board) operator.

The blockchain record should be as close in time as practically possible to the creation of the Contest Ballot Checkpoint document to provide a reliable anchor for proofs of inclusion and auditability.

Contest Ballot CheckpointContest Ballot Checkpoint    Document RelationshipsContest Ballot                         Contest Ballot                    Contest Parameters                         Contest Parameters                    Contest Ballot Checkpoint                         Contest Ballot Checkpoint                    content typeapplication/cbortype58608925-bda3-47df-b39a-ae0d0a1dd6edidDocument IdverDocument VerrefContest BallotparametersContest ParameterschainChain LinkContest Ballot Checkpoint:e->Contest Ballot:w1*Contest Ballot Checkpoint:e->Contest Parameters:w1*

Validation

  • parameters metadata MUST reference the Contest this checkpoint pertains to.
  • ref metadata MUST reference the accepted Contest Ballots collected in the preceding interval by the bulletin board. Entries MUST be sorted by ascending document_id:document_ver, regardless of the arrival time at the bulletin board.
  • Ballot boxes MUST reject ballots whose document_id:document_ver fall outside the contest’s allowed time window, or that are not close in time to when the ballot box received the ballot.
  • When present, rejections MUST only contain recognized reasons and valid document_ref values of Contest Ballot documents; rejected ballots MUST NOT appear in ref for the same interval.
  • smt-root MUST be the Blake3 root hash of the canonical SMT containing all accepted ballots up to and including this checkpoint;
  • smt-entries MUST equal the total count of leaves in that SMT.
  • chain MUST be intact and consistent: the previous checkpoint referenced by chain MUST exist, match type, id, and parameters, and have a lower ver and height exactly one less than this checkpoint.

Business Logic

Front End

  • Not produced by the Front End.
  • May be read to verify that a proof of inclusion validates against the published smt-root and smt-entries.

Back End

  • Validate that all referenced ballots exist and are valid for the contest.
  • Ensure the document is signed by an authoritative bulletin‑board operator.
  • Ensure all referenced ballots are for the same contest as parameters.
  • Compute and verify smt-root and smt-entries against the current SMT state.
  • If present, validate rejections reasons and that rejected document_refs are Contest Ballot documents.
  • Ensure the chain is intact and consistent with the previous checkpoint.
  • Ensure no previous checkpoint already chains to the same target (no forks within a single authoritative sequence).

COSE Header Parameters

Metadata

type

Parameter Value
Required yes
Format Document Type
Type 58608925-bda3-47df-b39a-ae0d0a1dd6ed

The document TYPE.

type Validation

MUST be a known document type.

id

Parameter Value
Required yes
Format Document Id

Document ID, created the first time the document is created. This must be a properly created UUIDv7 which contains the timestamp of when the document was created.

id Validation

IF ver does not == id then a document with id and ver being equal MUST exist.

ver

Parameter Value
Required yes
Format Document Ver

The unique version of the document. The first version of the document must set ver == id

ver represents new versions of the same document as it changes over time.

ver Validation

The document version must always be >= the document ID.

ref

Parameter Value
Required yes
Format Document Reference
Multiple References True
Valid References Contest Ballot

Reference to a Linked Document or Documents. This is the primary hierarchical reference to a related document.

If a reference is defined as required, there must be at least 1 reference specified. Some documents allow multiple references, and they are documented as required.

The document reference serves two purposes:

  1. It ensures that the document referenced by an ID/Version is not substituted. In other words, that the document intended to be referenced, is actually referenced.
  2. It Allows the document to be unambiguously located in decentralized storage systems.

There can be any number of Document Locations in any reference. The currently defined locations are:

The document location does not guarantee that the document is actually stored. It only defines that if it were stored, this is the identifier that is required to retrieve it. Therefore it is required that Document References are unique and reproducible, given a documents contents.

ref Validation

The following must be true for a valid reference:

  • The Referenced Document MUST Exist
  • Every value in the document_locator must consistently reference the exact same document.
  • The document_id and document_ver MUST match the values in the referenced document.

parameters

Parameter Value
Required yes
Format Document Reference
Valid References Contest Parameters
Linked Reference Metadata ref

A reference to the Parameters Document this document lies under.

parameters Validation

In addition to the validation performed for Document Reference type fields:

  • Any linked referenced document that includes a parameters metadata must match the parameters of the referencing document, or a parent of those parameters.

For example, a linked reference to Contest Parameters is transitively a reference to the Parameters document it references, and each parameters document they reference until the Brand parameters document is reached.

The use case here is for Templates. The profile template, or proposal templates could be defined at any of these levels, and as long as they all refer to the same chain of parameters in the hierarchy they are all valid.

  • The Document referenced by ref

chain

Parameter Value
Required yes
Format Chain Link

An immutable link to the previous document in a chained sequence of documents. Because ID/Ver only defines values for the current document, and is not intended by itself to prevent insertion of documents in a sequence, the chain metadata allows for the latest document to directly point to its previous iteration.

It also aids in discoverability, where the latest document may be pinned but prior documents can be discovered automatically by following the chain.

chain Validation

Chained Documents do not support collaborators. Any document which is attempted to be published in the sequence which is NOT published by the author of the first document in the sequence is fraudulent, and to be discarded.

In addition, the chained document MUST:

  • Not have collaborators;
  • Have the same id as the document being chained to;
  • Have a ver that is greater than the ver being chained to;
  • Have the same type as the chained document;
  • Have parameters match;
  • Have not be chaining to a document already chained to by another document;
  • Have its absolute height exactly one more than the height of the document being chained to.

IF any of these validations fail, then the entire sequence of documents is INVALID. Not just the current document.

Payload

The Payload is a CBOR document that MUST conform to the contest-ballot-checkpoint CDDL schema.

Contents

  • stage (required)

    • Processing stage represented by this checkpoint.
    • One of: "bulletin-board" | "tally" | "audit".
  • smt-root (required)

    • Blake3 256‑bit digest of the root of the Sparse Merkle Tree (SMT) containing all accepted ballot document_refs up to and including this checkpoint.
  • smt-entries (required)

    • The total number of documents (leaves) in the SMT at this checkpoint.
  • rejections (optional)

    • Map of rejection-reason => [ document_ref, ... ] listing ballots rejected during this checkpoint interval.
    • Reasons are limited to: "already-voted", "obsolete-vote".
  • encrypted-tally (optional)

    • Placeholder map of document_ref => encrypted-tally-proposal-result.
    • May appear at later stages to commit to encrypted tally snapshots.
  • tally (optional)

    • Placeholder map of document_ref => tally-proposal-result for clear tally snapshots.
  • drep-encryption-key (optional)

    • Placeholder for a DRep encryption key to allow decryption where required for audit or published results.

Notes

  • The document ref metadata lists the accepted Contest Ballots collected during the interval covered by this checkpoint; rejected ballots are listed under rejections and are not included in ref for that interval.
  • The SMT is cumulative across the chain; each checkpoint’s smt-root and smt-entries commit to all accepted ballots up to that point.

Schema

Payload CDDL Schema
; Catalyst Ballot Checkpoint data object.
;
; This serves as a checkpoint that collects new `contest-ballot-payload` documents
; that have been observed by a bulletin board.
;
; It will be created periodically during the voting period to allow proofs of inclusion
; to be firmly anchored and repeatably verifiable, and to allow voters or auditors to confirm
; a bulletin board acted honestly and included all valid ballots it detected.
;
; At another interval (which may be the same or different), a roll-up of the latest
; checkpoint is submitted to a blockchain to provide an immutable anchor of the
; ballots collected by a bulletin board up to that point in time.


; Catalyst Ballot Checkpoint Payload data object.
contest-ballot-checkpoint = {
    "stage" : stage
    "smt-root" : smt-root
    "smt-entries" : smt-entries
    ? "rejections" : rejections
    ? "encrypted-tally" : encrypted-tally
    ? "tally" : tally
    ? "drep-encryption-key" : drep-encryption-key
}

; What stage in the ballot processing does this checkpoint represent.
stage = (
    "bulletin-board" /
    "tally" /
    "audit"
)

; The SMT Root hash is a Blake 3 256bit digest Hash.
smt-root = blake3

; Blake3 Hash (Digest Size is determined by length of bytes)
blake3 = #6.32781(bytes)

; The Count of all Documents held by the SMT.
smt-entries = uint

; List of documents rejected at this checkpoint, grouped by reason.
rejections = {
    + rejection-reason => [ + document_ref ]
}

; The reason a document was rejected at this checkpoint.
rejection-reason = (
    "already-voted" /  ; Used to indicate a voter already voted in another system (ie, Jormungandr)
    "obsolete-vote"    ; Used to indicate a vote that was cast was replaced with a newer vote.
)

; Reference to a single Signed Document
document_ref = [
  document_id
  document_ver
  document_locator
]

; Document ID
document_id = uuid_v7

; UUIDv7
uuid_v7 = #6.37(bytes .size 16)

; Document Version
document_ver = uuid_v7

; Where a document can be located, must be a unique identifier.
document_locator = {
  "cid" : cid
}

; IPLD content identifier.
; Currently limited to SHA2-256 based CIDs.
cid = #6.42(bytes .abnfb ("cid" .det cbor-cid ))

; CIDv1 ABNF Constrained for SHA2-256
cbor-cid = '
    cid = cidv1 codec-cbor sha2-256 digest-32 digest
    cidv1 = %x00 %x01
    codec-cbor = %x51
    sha2-256 = %x12
    digest-32 = %x20
    digest = 32(%x00-FF)
'

; Placeholder of encrypted tally result.
encrypted-tally = {
    + document_ref => encrypted-tally-proposal-result
}

; Placeholder of encrypted tally result.
encrypted-tally-proposal-result = [ 1, undefined ]

; Placeholder of encrypted tally result.
tally = {
    + document_ref => tally-proposal-result
}

; Placeholder of encrypted tally result.
tally-proposal-result = [ 0, { + clear-choice : voting-power } ]

; Universal Unencrypted Choice
clear-choice = int

; Voting Power.
voting-power = int

; Placeholder of drep encryption key.
drep-encryption-key = undefined

Sub-schemas

Required Definition: stage
; What stage in the ballot processing does this checkpoint represent.


; What stage in the ballot processing does this checkpoint represent.
stage = (
    "bulletin-board" /
    "tally" /
    "audit"
)
Required Definition: smt-root
; The SMT Root hash is a Blake 3 256bit digest Hash.


; The SMT Root hash is a Blake 3 256bit digest Hash.
smt-root = blake3

; Blake3 Hash (Digest Size is determined by length of bytes)
blake3 = #6.32781(bytes)
Required Definition: blake3
; Blake3 Hash (Digest Size is determined by length of bytes)


; Blake3 Hash (Digest Size is determined by length of bytes)
blake3 = #6.32781(bytes)
Required Definition: smt-entries
; The Count of all Documents held by the SMT.


; The Count of all Documents held by the SMT.
smt-entries = uint
Required Definition: rejections
; For any documents that were rejected for a defined reason,
; the list of document references which were rejected.


; List of documents rejected at this checkpoint, grouped by reason.
rejections = {
    + rejection-reason => [ + document_ref ]
}

; The reason a document was rejected at this checkpoint.
rejection-reason = (
    "already-voted" /  ; Used to indicate a voter already voted in another system (ie, Jormungandr)
    "obsolete-vote"    ; Used to indicate a vote that was cast was replaced with a newer vote.
)

; Reference to a single Signed Document
document_ref = [
  document_id
  document_ver
  document_locator
]

; Document ID
document_id = uuid_v7

; UUIDv7
uuid_v7 = #6.37(bytes .size 16)

; Document Version
document_ver = uuid_v7

; Where a document can be located, must be a unique identifier.
document_locator = {
  "cid" : cid
}

; IPLD content identifier.
; Currently limited to SHA2-256 based CIDs.
cid = #6.42(bytes .abnfb ("cid" .det cbor-cid ))

; CIDv1 ABNF Constrained for SHA2-256
cbor-cid = '
    cid = cidv1 codec-cbor sha2-256 digest-32 digest
    cidv1 = %x00 %x01
    codec-cbor = %x51
    sha2-256 = %x12
    digest-32 = %x20
    digest = 32(%x00-FF)
'
Required Definition: rejection-reason
; The reason a document was rejected at this checkpoint.


; The reason a document was rejected at this checkpoint.
rejection-reason = (
    "already-voted" /  ; Used to indicate a voter already voted in another system (ie, Jormungandr)
    "obsolete-vote"    ; Used to indicate a vote that was cast was replaced with a newer vote.
)
Required Definition: document_ref
; document_ref


; Reference to a single Signed Document
document_ref = [
  document_id
  document_ver
  document_locator
]

; Document ID
document_id = uuid_v7

; UUIDv7
uuid_v7 = #6.37(bytes .size 16)

; Document Version
document_ver = uuid_v7

; Where a document can be located, must be a unique identifier.
document_locator = {
  "cid" : cid
}

; IPLD content identifier.
; Currently limited to SHA2-256 based CIDs.
cid = #6.42(bytes .abnfb ("cid" .det cbor-cid ))

; CIDv1 ABNF Constrained for SHA2-256
cbor-cid = '
    cid = cidv1 codec-cbor sha2-256 digest-32 digest
    cidv1 = %x00 %x01
    codec-cbor = %x51
    sha2-256 = %x12
    digest-32 = %x20
    digest = 32(%x00-FF)
'
Required Definition: document_id
; Unique Document Identifier


; Document ID
document_id = uuid_v7

; UUIDv7
uuid_v7 = #6.37(bytes .size 16)
Required Definition: uuid_v7
; Version 7 UUID
; See: https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-7
;      https://github.com/lucas-clemente/cbor-specs/blob/master/uuid.md


; UUIDv7
uuid_v7 = #6.37(bytes .size 16)
Required Definition: document_ver
; Unique Chronological Document Version Identifier


; Document Version
document_ver = uuid_v7

; UUIDv7
uuid_v7 = #6.37(bytes .size 16)
Required Definition: document_locator
; document_locator


; Where a document can be located, must be a unique identifier.
document_locator = {
  "cid" : cid
}

; IPLD content identifier.
; Currently limited to SHA2-256 based CIDs.
cid = #6.42(bytes .abnfb ("cid" .det cbor-cid ))

; CIDv1 ABNF Constrained for SHA2-256
cbor-cid = '
    cid = cidv1 codec-cbor sha2-256 digest-32 digest
    cidv1 = %x00 %x01
    codec-cbor = %x51
    sha2-256 = %x12
    digest-32 = %x20
    digest = 32(%x00-FF)
'
Required Definition: cid
; IPLD content identifier.
; Also known as an IPFS CID.
; Currently limited to SHA2-256 based CIDs.
; See: https://docs.ipfs.tech/concepts/content-addressing/#what-is-a-cid
;      https://github.com/ipld/cid-cbor/


; IPLD content identifier.
; Currently limited to SHA2-256 based CIDs.
cid = #6.42(bytes .abnfb ("cid" .det cbor-cid ))

; CIDv1 ABNF Constrained for SHA2-256
cbor-cid = '
    cid = cidv1 codec-cbor sha2-256 digest-32 digest
    cidv1 = %x00 %x01
    codec-cbor = %x51
    sha2-256 = %x12
    digest-32 = %x20
    digest = 32(%x00-FF)
'
Required Definition: cbor-cid
; CIDv1 ABNF Constrained for SHA2-256


; CIDv1 ABNF Constrained for SHA2-256
cbor-cid = '
    cid = cidv1 codec-cbor sha2-256 digest-32 digest
    cidv1 = %x00 %x01
    codec-cbor = %x51
    sha2-256 = %x12
    digest-32 = %x20
    digest = 32(%x00-FF)
'
Required Definition: encrypted-tally
; The Result of an encrypted tally.
; Placeholder until formally defined.


; Placeholder of encrypted tally result.
encrypted-tally = {
    + document_ref => encrypted-tally-proposal-result
}

; Reference to a single Signed Document
document_ref = [
  document_id
  document_ver
  document_locator
]

; Document ID
document_id = uuid_v7

; UUIDv7
uuid_v7 = #6.37(bytes .size 16)

; Document Version
document_ver = uuid_v7

; Where a document can be located, must be a unique identifier.
document_locator = {
  "cid" : cid
}

; IPLD content identifier.
; Currently limited to SHA2-256 based CIDs.
cid = #6.42(bytes .abnfb ("cid" .det cbor-cid ))

; CIDv1 ABNF Constrained for SHA2-256
cbor-cid = '
    cid = cidv1 codec-cbor sha2-256 digest-32 digest
    cidv1 = %x00 %x01
    codec-cbor = %x51
    sha2-256 = %x12
    digest-32 = %x20
    digest = 32(%x00-FF)
'

; Placeholder of encrypted tally result.
encrypted-tally-proposal-result = [ 1, undefined ]
Required Definition: encrypted-tally-proposal-result
; The Result of an encrypted tally.
; Placeholder until formally defined.


; Placeholder of encrypted tally result.
encrypted-tally-proposal-result = [ 1, undefined ]
Required Definition: tally
; The Result of an encrypted tally.
; Placeholder until formally defined.


; Placeholder of encrypted tally result.
tally = {
    + document_ref => tally-proposal-result
}

; Reference to a single Signed Document
document_ref = [
  document_id
  document_ver
  document_locator
]

; Document ID
document_id = uuid_v7

; UUIDv7
uuid_v7 = #6.37(bytes .size 16)

; Document Version
document_ver = uuid_v7

; Where a document can be located, must be a unique identifier.
document_locator = {
  "cid" : cid
}

; IPLD content identifier.
; Currently limited to SHA2-256 based CIDs.
cid = #6.42(bytes .abnfb ("cid" .det cbor-cid ))

; CIDv1 ABNF Constrained for SHA2-256
cbor-cid = '
    cid = cidv1 codec-cbor sha2-256 digest-32 digest
    cidv1 = %x00 %x01
    codec-cbor = %x51
    sha2-256 = %x12
    digest-32 = %x20
    digest = 32(%x00-FF)
'

; Placeholder of encrypted tally result.
tally-proposal-result = [ 0, { + clear-choice : voting-power } ]

; Universal Unencrypted Choice
clear-choice = int

; Voting Power.
voting-power = int
Required Definition: tally-proposal-result
; The Result of an encrypted tally.
; Placeholder until formally defined.


; Placeholder of encrypted tally result.
tally-proposal-result = [ 0, { + clear-choice : voting-power } ]

; Universal Unencrypted Choice
clear-choice = int

; Voting Power.
voting-power = int
Required Definition: clear-choice
; An Choice Selection (clear/unencrypted).
;
; This can be a positive or negative integer, and is
; constrained by the parameters of the contest.


; Universal Unencrypted Choice
clear-choice = int
Required Definition: voting-power
; The Voting Power.
; Voting Power is always an integer, any fractional voting power
; is represented as a fixed point, and not defined here.


; Voting Power.
voting-power = int
Required Definition: drep-encryption-key
; Placeholder to store the drep encryption key so drep votes can be decrypted if required.
; Placeholder until formally defined.


; Placeholder of drep encryption key.
drep-encryption-key = undefined

Signers

The following Admin roles may sign documents of this type:

  • Bulletin Board Operator

Only the original author can update and sign a new version of documents.

Copyright © 2024-2025 IOG Singapore, All Rights Reserved
License This document is licensed under CC-BY-4.0
Created 2024-12-27
Modified 2025-11-10
Authors Alex Pozhylenkov alex.pozhylenkov@iohk.io
Nathan Bogale nathan.bogale@iohk.io
Neil McAuliffe neil.mcauliffe@iohk.io
Steven Johnson steven.johnson@iohk.io

Changelog

0.1.5 (2025-11-03)

  • Add Voting Ballots and Ballot Checkpoint Documents