Cast and Spoil Ballots
Each ballot that is completed by a voter must be either cast or spoiled. A cast ballot is a ballot that the voter accepts as valid and wishes to include in the official election tally. A spoiled ballot, also referred to as a challenged ballot, is a ballot that the voter does not accept as valid and wishes to exclude from the official election tally.
ElectionGuard includes a mechanism to mark a specific ballot as either cast or spoiled. Cast ballots are included in the tally record, while spoiled ballots are not. Spoiled ballots are decrypted into plaintext and published along with the rest of the election record.
Depending on the jurisdiction conducting an election the process of casting and spoiling ballots may be handled differently. For this reason, there are multiple ways to interact with the
- By calling accept_ballot - Ballots can be marked cast or spoiled manually.
- By using the Ballot Box - Ballots can be marked cast or spoiled using a stateful class.
In some jurisdictions, there is a limit on the number of ballots that may be marked as spoiled. If this is the case, you may use the
BallotBoxState.UNKNOWN state, or extend the enumeration to support your specific use case.
Once all of the ballots are marked as cast or spoiled, all of the encryptions of each option are homomorphically combined to form an encryption of the total number of times that each option was selected in the election.
This process is completed only for cast ballot.
The spoiled ballots are simply marked for inclusion in the election results.
- Ciphertext Ballot An encrypted representation of a voter's filled-in ballot.
- Ciphertext Accepted Ballot A wrapper around the
CiphertextBallotthat represents a ballot that is accepted for inclusion in election results and is either: cast or spoiled.
- Ballot Box A stateful collection of ballots that are either cast or spoiled.
- Ballot Store A repository for retaining cast and spoiled ballots.
- Cast Ballot A ballot which a voter has accepted as valid to be included in the official election tally.
- Spoiled Ballot A ballot which a voter did not accept as valid and is not included in the tally.
- Unknown Ballot A ballot which may not yet be determined as cast or spoiled, or that may have been spoiled but is otherwise not published in the election results.
- Homomorphic Tally An encrypted representation of every selection on every ballot that was cast. This representation is stored in a
- Each ballot is loaded into memory (if it is not already).
- Each ballot is verified to be correct according to the specific election metadata and encryption context.
- Each ballot is
acceptedand identified as either being
- The collection of cast and spoiled ballots is cached in the
- All ballots are tallied. The
castballots are combined to create a
CiphertextTallyThe spoiled ballots are cached for decryption later.
The ballot box can be interacted with via a stateful class that caches the election context, or via stateless functions. The following examples demonstrate some ways to interact with the ballot box.
Depending on the specific election workflow, the
BallotBoxclass may not be used for a given election. For instance, in one case a ballot can be accepted directly on an electronic device, in which case there is no
BallotBox. In a different workflow, a ballot may be explicitly cast or spoiled in a later step, such as after printing for voter review.
In all cases, a ballot must be marked as either
spoiled to be included in a tally result.
from electionguard.ballot_box import BallotBox metadata: InternalElectionDescription encryption: CiphertextElection store: BallotStore ballots_to_cast: List[CiphertextBallot] ballots_to_spoil: List[CiphertextBallot] # The Ballot Box is a thin wrapper around the `accept_ballot` function method ballot_box = BallotBox(metadata, encryption, store) # Cast the ballots for ballot in ballots_to_cast: accepted_ballot = ballot_box.cast(ballot) # The ballot is both returned, and placed into the ballot store assert(store.get(accepted_ballot.object_id) == accepted_ballot) # Spoil the ballots for ballot in ballots_to_spoil: assert(ballot_box.spoil(ballot) is not None)
from electionguard.ballot_box import accept_ballot metadata: InternalElectionDescription encryption: CiphertextElection store: BallotStore ballots_to_cast: List[CiphertextBallot] ballots_to_spoil: List[CiphertextBallot] for ballot in ballots_to_cast: accepted_ballot = accept_ballot( ballot, BallotBoxState.CAST, metadata, encryption, store ) for ballot in ballots_to_spoil: accepted_ballot = accept_ballot( ballot, BallotBoxState.SPOILED, metadata, encryption, store )
Generating the encrypted
CiphertextTally can be completed by creating a
CiphertextTally stateful class and manually marshalling each cast and spoiled ballot. Using this method is preferable when the collection of ballots is very large
For convenience, stateless functions are also provided to automatically generate the
CiphertextTally from a
BallotStore. This method is preferred when the collection of ballots is arbitrarily small, or when the
BallotStore is overloaded with a custom implementation.
Using the Stateful Class
metadata: InternalElectionDescription context: CiphertextElectionContext ballots: List[CiphertextAcceptedBallot] tally = CiphertextTally(metadata, context) for ballot in ballots: assert(tally.append(ballot))
metadata: InternalElectionDescription context: CiphertextElectionContext store: BallotStore tally = tally_ballots(store, metadata, context) assert(tally is not None)