# Ballot Protocol
The Vocdoni Ballot Protocol aims to be a very simple yet powerful specification for the representation of ballots and results for a voting process.
A voting process is made out of one or more fields, each of which represents a single question or an option for a question, depending on the type of process. When voting, eligible voters choose from a set of predefined answers for each field. The allowed number of answers, type of answer, etc. also depend on the specific type of process. An eligible voter expresses their choices by casting a ballot.
A ballot is represented as an array (or list) of natural numbers. Each position of the array contains an answer to one of the process' fields.
Results are accumulated in a two-dimension array of natural numbers (a matrix). Each row of this matrix corresponds to a ballot field, and each column corresponds to one of the possible values for that field. Any number in the results matrix is simply a count of the votes for the value represented at that index.
A more thorough introduction is available on our blog (opens new window).
The protocol is composed by a set of numeric and boolean variables that restrict the format of the ballot. They are defined in the Process Parameters.
uint8 maxCount
Max number of fields per ballot. 1 <= maxCount <= 100.uint8 maxValue
Determines the acceptable maximum value for all fields.uint8 minValue
Determines the acceptable minimum value for all fields.bool uniqueValues
Choices for a question cannot appear twice or moreuint16 maxTotalCost
Maximum limit on the total sum of all ballot fields' values (if applicable). 0 => No limit / Not applicable.uint16 minTotalCost
Minimum limit on the total sum of all ballot fields' values (if applicable). 0 => No limit / Not applicable.uint16 costExponent
The exponent that will be used to compute the "cost" of the field values.totalCost = Σ (value[i] ^ costExponent) <= maxTotalCost
costExponent
is represented asexp * 10000
so:- 0 => 0.0000
- 10000 => 1.0000
- 65535 => 6.5535
# Results and example
The result is represented as a matrix of natural numbers. Let's take this JSON example:
[
[1,2,3], // in field 1: value 0 got 1 vote, value 1 got 2, value 2 got 3
[1,4,2], // in field 2: value 0 got 1 vote, value 1 got 4, value 2 got 2
[0,1,0] // in field 3: value 0 got 0 vote, value 1 got 1, value 2 got 0
]
Each row of the matrix represents a field, there are three fields:
[ [First field], [Second field], [Third field] ]
The ballot protocol does not cover interpretation. However, a very common interpretation would be to consider each field as the multiplier (weight) equal to its position.
- First question [1,2,3] =
0*1 + 1*2 + 2*3
=0 + 2 + 6
= 8 - Second question [0,4,2] =
0*0 + 1*4 + 2*2
= 8 - Third question [5,1,0] =
0*5 + 1*1 + 2*0
= 1
6 votes have been cast.
# Example
Lets see an example with maxCount=3
and maxValue=5
The human-friendly question would be: Rate these 3 candidates to the best rock music player of the 20th century, from 0 to 5 stars.
- Lennon is the candidate 0, so question 0
- Hendrix is the candidate 1, so question 1
- Joplin is the candidate 2, so question 2
Let's start, currently there are no votes cast:
Results: Lennon:[0,0,0] Hendrix:[0,0,0] Joplin:[0,0,0]
New vote [2,1,2] =>
- give 2 points to candidate 0
- give 1 point to candidate 1
- give 2 points to candidate 2
Results: Lennon:[0,0,1] Hendrix:[0,1,0] Joplin:[0,0,1]
- candidate 0 = 2 points (1·2)
- candidate 1 = 1 point (1·1)
- candidate 2 = 2 points (1·2)
New vote [0,1,2] =>
- give 0 points to candidate 0
- give 1 point to candidate 1
- give 2 points to candidate 2
Results: Lennon:[1,0,1] Hendrix:[0,2,0] Joplin:[0,0,2]
- candidate 0 = 2 points
- candidate 1 = 2 point
- candidate 2 = 4 points
New vote [0,0,0] =>
- give 0 points to candidate 0
- give 0 points to candidate 1
- give 0 points to candidate 2
Results: Lennon:[2,0,1] Hendrix:[1,2,0] Joplin:[1,0,2]
- candidate 0 = 2 points
- candidate 1 = 2 point
- candidate 2 = 4 points
Final Results: [ [2,0,1], [1,2,0], [1,0,2] ]
- Lennon got
2*0 + 1*0 + 2*1
= 2 points - Hendrix got
0*1 + 1*2 + 2*0
= 2 points - Joplin got
0*1 + 1*0 + 2*2
= 4 points - Participation is 3 votes:
[2+0+1] || [1+2+0] || [1+0+2]
# More Examples
# Rate a product
Give a value from 0-5 to a product
- Vote Envelopes:
[2]
[5]
[2]
- Results: [ [0,0,2,0,0,1] ]
- Two users have given 2 stars
- One user have given 5 stars
Parameters
maxCount | minValue | maxValue | minTotalCost | maxTotalCost | costExponent | uniqueValues |
---|---|---|---|---|---|---|
1 | 0 | 5 | - | - | - | No |
# Rate 3 candidates
Lennon, Hendrix, Joplin ⇒ 0, 1, 2
- Vote Envelope:
[2,1,2]
[0,1,2]
[0,0,0]
- Results:
[ [2,0,1], [1,2,0], [1,0,2] ]
- Lennon got
2*0 + 0*1 + 1*2
= 2 points - Hendrix got
1*0 + 2*1 + 0*2
= 2 points - Joplin got
1*0 + 0*1 + 2*2
= 4 points
- Lennon got
maxCount | minValue | maxValue | minTotalCost | maxTotalCost | costExponent | uniqueValues |
---|---|---|---|---|---|---|
3 | 0 | 2 | - | - | - | Yes |
# Single choice
How do you feel today? Choose 1 out of 3 possible options: Bad, Good, Amazing
- Vote Envelopes:
[0,1,0]
[0,1,0]
[0,0,1]
- Results:
[ [3,0,0], [1,2,0], [2,1,0] ]
- Bad:
0*3 + 1*0 + 2*0
= 0 - Good:
0*1 + 1*2 + 2*0
= 2 - Amazing:
0*2 + 1*1 + 2*0
= 1
- Bad:
maxCount | minValue | maxValue | minTotalCost | maxTotalCost | costExponent | uniqueValues |
---|---|---|---|---|---|---|
3 | 0 | 1 | 1 | 1 | - | No |
# Multiple choice
Choose your 3 favorite colours out of 5: green:0, blue:1, pink:2, orange:3, black:4
- Vote Envelope:
[1,1,1,0,0]
[0,1,1,1,0]
[1,1,1,0,0]
- Results:
[ [1, 2], [0, 3], [0, 3], [2, 1], [3, 0] ]
- green:
0*1 + 2*1
= 2 - blue:
0*0 + 1*3
= 3 - pink:
0*0 + 1*3
= 3 - orange:
0*2 + 1*1
= 1 - black:
0*3 + 0*1
= 0
- green:
maxCount | minValue | maxValue | minTotalCost | maxTotalCost | costExponent | uniqueValues |
---|---|---|---|---|---|---|
5 | 0 | 1 | 3 | 3 | - | No |
# Linear Weighted choice
Sort your 5 favorite blockchains: Bitcoin:0, Ethereum:1, Monero:2, Zcash:3, Polkadot:4.
Your first option gets 5 points,... the last 0 points.
- Vote Envelope:
[0,3,1,4,2]
[3,0,1,4,2]
[1,2,3,4,0]
- Results;
[ [1,1,0,1,0], [1,0,1,1,0], [0,2,1,0,0], [0,0,0,0,3], [1,0,2,0,0] ]
- Bitcoin:
0*1 + 1*1 + 2*0 + 3*1 + 0*4
= 4 - Ethereum:
0*1 + 1*0 + 2*1 + 3*1 + 0*4
= 5 - Monero:
0*1 + 1*2 + 2*1 + 3*0 + 4*0
= 6 - Zcash:
0*0 + 1*0 + 2*0 + 3*0 + 4*3
= 12 - Polkadot:
0*1 + 1*0 + 2*2 + 3*0 + 4*0
= 4
- Bitcoin:
maxCount | minValue | maxValue | minTotalCost | maxTotalCost | costExponent | uniqueValues |
---|---|---|---|---|---|---|
5 | 0 | 4 | - | - | - | Yes |
# Quadratic voting
You might distribute 12 credits among 4 NGOs. You can give as many tokens as you wish to a single option, but the cost raises exponentially with each additional token added to an option.
NGOs are: greenpeace:0, redcross:1, msf:2, amnesty:3
- Vote Envelopes:
[2,2,2,0]
[1,1,2,2]
[0,3,1,1]
Cost exponent calculations:
[2, 2, 2, 0]
envelope 1 calculation: 2^2 + 2^2 + 2^2 + 0^2
= 4 + 4 + 4 + 0 = 12 credits
[1, 1, 2, 2]
envelope 2 calculation: 1^2 + 1^2 + 2^2 + 2^2
= 1 + 1 + 4 + 4 = 10 credits
[0, 3, 1, 1]
envelope 3 calculation: 0^2 + 3^2 + 1^2 + 1^2
= 0 + 9 + 1 + 1 = 11 credits
- Results:
[ [1,1,1,0], [0,1,1,1], [0,1,2,0], [1,1,1,0] ]
- greenpeace:
0*1 + 1*1 + 2*1 + 3*0
= 3 - redcross:
0*0 + 1*1 + 2*1 + 3*1
= 6 - msf:
0*0 + 1*1 + 2*2 + 3*0
= 5 - amnesty:
0*1 + 1*1 + 2*1 + 3*0
= 3
- greenpeace:
maxCount | minValue | maxValue | minTotalCost | maxTotalCost | costExponent | uniqueValues |
---|---|---|---|---|---|---|
4 | 0 | - | 0 | 12 | 20000 | No |
# Multiquestion
This mode requires a different way of interpreting the results, since each position of the results array is not multiplied by its index.
3 positions (CEO, COO, CFO), 5 candidates
- Ballots:
[4,3,2]
[4,2,3]
[0,1,4]
- Results:
[ [1,0,0,0,2], [0,1,1,1,0], [0,0,1,1,1] ]
- CEO: candidate0:1 candidate4:2
- COO: candidate1:1 candidate2:1 candidate3:1
- CFO: candidate2:1 candidate3:1 candidate4:1
Vote Envelopes: [4,3,2] [4,2,3] [0,1,4]
CEO COO CFO
Option A results: [ [1, 0, 0, 0, 2], [0, 1, 1, 1, 0], [0, 0, 1, 1, 1] ]
Candidate 5 has won the CEO position, whereas COO is tied between candidates 2, 3, and 4, and CFO is tied between candidates 3, 4, and 5.
maxCount | minValue | maxValue | minTotalCost | maxTotalCost | costExponent | uniqueValues |
---|---|---|---|---|---|---|
3 | 0 | 4 | - | - | - | No |
# Vocdoni results interpretation
Results interpretation is not part of the ballot protocol, but it informs how the protocol should be understood and displayed. The Process Metadata has a couple of flags describing the expected interpretation.
"results": {
"aggregation": "index-weighted", // "index-weighted" | "discrete-counting",
"display": "rating" // "rating" | "simple-question" | "multiple-choice" | "linear-weighted" | "quadratic-voting" | "multiple-question" | "raw"
}
The results provided by the scrutinizer contain a preliminary aggregation in a 2-dimensional matrix, where the final step needs to be completed by UI clients, depending on results.aggregation
.
# Single question
Index-weighted: each option's value is multiplied by its position index, then summed.
[ [1,0,1], [2,0,0], [0,1,1] ] => [0*1+1*0+2*1], [0*2+1*0+2*0], [0*1+1*1+2*1] => [2,0,3]
option1: 2 votes
option2: 0 votes
option3: 3 votes
This interpretation suits any case with extended features, such as multiple choice, quadratic voting, linear-weighted, etc. This is because it is able to weigh multiple possible values for a single field.
# Multi question
Discrete values: field values are just counted (weight = 1)
[ [1,2,0], [0,1,2], [1,1,1] ] => [ [question1], [question2], [question3] ]
field1: candidate0: 1 vote, candidate1: 2 votes, candidate 3: 0 votes
field2: candidate0: 0 votes, candidate1: 1 vote, candidate 2: 2 votes
field2: candidate0: 1 vote, candidate1: 1 vote, candidate 2: 1 vote
As this format can only accept binary (yes/no) input for each option, it is only available for use with a basic set of features (multi-question, single-choice)