Action
Election Domain Hash
Compute the edh from the Election name
Inputs
#![allow(unused)] fn main() { struct VoteInput { sk: Option<SpendingKey>, fvk: FullViewingKey, pos: u32, note: Note, nf_start: Nullifier, nf_pos: u32, nf_path: MerklePath, cmx_path: MerklePath, } }
skis needed if the election requires "Spend Signatures",posis the position of the note in thecmxtree1,nf_startis the \(a_i\) of the interval \([a_i, b_i]\) wherenfbelongs,nf_posis the position ofnf_startin the NF tree,nf_pathandcmx_pathare the Merkle Path obtained in the previous step.
Calculate the domain nullifier dnf.
#![allow(unused)] fn main() { let dnf = spend.note.nullifier_domain(&fvk, &edh); }
Output
The output is a voting note.
#![allow(unused)] fn main() { struct VoteOutput(Address, u64); }
Encrypted Note
The encrypted note corresponds to the CompactAction of Orchard. For reference, it is composed of the following fields.
- zip-212 version byte: 1 byte
- address diversifier: 11 bytes
- address pkd: 32 bytes
- value: 8 bytes For a total of 52 bytes.
It can be built using OrchardNoteEncryption from LRZ.
In pseudo-code
- Set
rhoas the nullifier (or domain diversifier on the Vote Chain) of the spent note - Pick
rseedwith the OsRng - Create an Orchard Note
Note::from_parts(recipient, NoteValue::from_raw(value), rho, rseed) - Encrypt the node
#![allow(unused)] fn main() { let rseed = RandomSeed::random(&mut rng, &rho); let note = Note::from_parts(recipient, NoteValue::from_raw(value), rho, rseed); let encryptor = OrchardNoteEncryption::new(None, output.clone(), candidate, [0u8; 512]); }
Get the ephemeral public key epk, the encrypted note encand the note commitmentcmx`.
#![allow(unused)] fn main() { let epk = encryptor.epk(); let enc = encryptor.encrypt_note_plaintext()[0..52]; let cmx = note.commitment(); }
- Calculate the vote net value as input value - output value.
- Pick a random trapdoor value
rcv - Accumulate
rcvover the transaction - Derive the net value commitment
#![allow(unused)] fn main() { let value_net = spend.note.value() - output.value(); let rcv = ValueCommitTrapdoor::random(&mut rng); total_rcv = total_rcv + &rcv; let cv_net = ValueCommitment::derive(value_net, rcv.clone()); }
Randomized public key
- Pick a random scalar \( \alpha \) in Fq (scalar field of Pallas)
- Derive the spend authorizing key from the spending key
- Add \( \alpha \) and derive the public key
#![allow(unused)] fn main() { let alpha = Fq::random(&mut rng); let spk = SpendAuthorizingKey::from(&spend.sk); let rk = spk.randomize(&alpha); }
Zero Knowledge Proof
Collect the public data (instance data):
cmx_root,nf_rootcv_netdnfrkcmxedh
And the secret data (advice data)
dnfnf_startnf_pathfvkspend_notecmx_pathoutput_notealpharcv
Then call the ZKP builder for the voting circuit as described in Circuit.
Ballot Action
Collect
cvrkdnfcmxepkenc
and form the BallotAction
#![allow(unused)] fn main() { struct BallotAction { cv: Hash, rk: Hash, nf: Hash, cmx: Hash, epk: Hash, enc: [u8; 52], } }
Ballot Data
Collect all the BallotActions into a BallotData
#![allow(unused)] fn main() { struct BallotData { actions: Vec<BallotAction>, } }
Sighash
Compute the sighash of the BallotData as
#![allow(unused)] fn main() { fn sig_hash(&self) -> Hash { let bin_data = serde_cbor::to_vec(&self).unwrap(); let p = Params::new() .hash_length(32) .personal(b"Ballot______Data") .hash(&bin_data); p.as_bytes().try_into().unwrap() } }
It is the Blake2b-256 hash of the CBOR serialization of
BallotData with Personalization Ballot______Data.
Binding Signature
Use the sighash and total_rcv to compute the Binding Signature
#![allow(unused)] fn main() { let bsk: SigningKey<Binding> = rcv.to_bytes(); let binding_signature = bsk.sign(&mut rng, sig_hash); }
Spend Signature
Sign the sighash using the public key rk, sighash
and SpendAuth.
Ballot Envelope
Combine the Ballot Data, Binding Signature, Input Signatures and proofs into the Ballot Envelope.
#![allow(unused)] fn main() { type Signature = [u8; 64]; struct BallotEnvelope { data: BallotData, input_signature: Vec<Signature>, binding_signature: Signature, proofs: Vec<Vec<u8>>, } }
Send to Server
Serialize BallotEnvelope as CBOR and send the bytes to the Voting Server.
Relative to the registration window, not to the activation of Orchard.
Or domain diversifier af