208 lines
5.9 KiB
Plaintext

//test
pragma circom 2.1.9;
include "../hash_bn/poseidon2_hash.circom";
include "../ledger/notes.circom";
include "../hash_bn/merkle.circom";
include "../misc/comparator.circom";
include "../circomlib/circuits/bitify.circom";
include "../misc/constants.circom";
template ticket_calculator(){
signal input epoch_nonce;
signal input slot;
signal input note_id;
signal input secret_key;
signal output out;
component hash = Poseidon2_hash(5);
component dst = LEAD_V1();
hash.inp[0] <== dst.out;
hash.inp[1] <== epoch_nonce;
hash.inp[2] <== slot;
hash.inp[3] <== note_id;
hash.inp[4] <== secret_key;
out <== hash.out;
}
template derive_entropy(){
signal input slot;
signal input note_id;
signal input secret_key;
signal output out;
component hash = Poseidon2_hash(4);
component dst = NONCE_CONTRIB_V1();
hash.inp[0] <== dst.out;
hash.inp[1] <== slot;
hash.inp[2] <== note_id;
hash.inp[3] <== secret_key;
out <== hash.out;
}
template would_win_leadership(secret_depth){
signal input slot;
signal input epoch_nonce;
signal input t0;
signal input t1;
//Part of the note id proof of membership to prove aged
signal input aged_nodes[32];
signal input aged_selectors[32]; // must be bits
signal input aged_root;
//Used to derive the note identifier
signal input transaction_hash;
signal input output_number;
signal input secret_key;
// The winning note value
signal input value;
signal output out;
signal output note_identifier;
// Derive the public key from the secret key
component pk = derive_public_key();
pk.secret_key <== secret_key;
// Derive the note id
component note_id = Poseidon2_hash(5);
component dst_note_id = NOTE_ID_V1();
note_id.inp[0] <== dst_note_id.out;
note_id.inp[1] <== transaction_hash;
note_id.inp[2] <== output_number;
note_id.inp[3] <== value;
note_id.inp[4] <== pk.out;
// Check the note ID is aged enough
//First check selectors are indeed bits
for(var i = 0; i < 32; i++){
aged_selectors[i] * (1 - aged_selectors[i]) === 0;
}
//Then check the proof of membership
component aged_membership = proof_of_membership(32);
for(var i = 0; i < 32; i++){
aged_membership.nodes[i] <== aged_nodes[i];
aged_membership.selector[i] <== aged_selectors[i];
}
aged_membership.root <== aged_root;
aged_membership.leaf <== note_id.out;
// Compute the lottery ticket
component ticket = ticket_calculator();
ticket.epoch_nonce <== epoch_nonce;
ticket.slot <== slot;
ticket.note_id <== note_id.out;
ticket.secret_key <== secret_key;
// Compute the lottery threshold
signal intermediate;
signal threshold;
intermediate <== t1 * value;
threshold <== value * (t0 + intermediate);
// Check that the ticket is winning
component winning = SafeFullLessThan();
winning.a <== ticket.out;
winning.b <== threshold;
// Check that every constraint holds
out <== aged_membership.out * winning.out;
note_identifier <== note_id.out;
}
template proof_of_leadership(secret_depth){
signal input sl;
signal input epoch_nonce; // the epoch nonce eta
signal input t0;
signal input t1;
//Part of the note id proof of membership to prove aged
signal input noteid_aged_path[32];
signal input noteid_aged_selectors[32]; // must be bits
signal input ledger_aged;
//Used to derive the note identifier
signal input secret_key;
signal input note_tx_hash;
signal input note_output_number;
//Part of the note id proof of membership to prove it's unspent
signal input noteid_latest_path[32];
signal input noteid_latest_selectors[32]; // must be bits
signal input ledger_latest;
// The winning note.
signal input v; // value of the note
// Verify the note is winning the lottery
component lottery_checker = would_win_leadership(secret_depth);
lottery_checker.slot <== sl;
lottery_checker.epoch_nonce <== epoch_nonce;
lottery_checker.t0 <== t0;
lottery_checker.t1 <== t1;
for(var i = 0; i < 32; i++){
lottery_checker.aged_nodes[i] <== noteid_aged_path[i];
lottery_checker.aged_selectors[i] <== noteid_aged_selectors[i];
}
lottery_checker.aged_root <== ledger_aged;
lottery_checker.transaction_hash <== note_tx_hash;
lottery_checker.output_number <== note_output_number;
lottery_checker.secret_key <== secret_key;
lottery_checker.value <== v;
// One time signing key used to sign the block proposal and the block
signal input P_lead_part_one;
signal input P_lead_part_two;
//Avoid the circom optimisation that removes unused public input
signal dummy_one;
signal dummy_two;
dummy_one <== P_lead_part_one * P_lead_part_one;
dummy_two <== P_lead_part_two * P_lead_part_two;
signal output entropy_contribution; // This is rho_lead
// Check that the note is unspent
//First check selectors are indeed bits
for(var i = 0; i < 32; i++){
noteid_latest_selectors[i] * (1 - noteid_latest_selectors[i]) === 0;
}
//Then check the note id is in the latest ledger state
component unspent_membership = proof_of_membership(32);
for(var i = 0; i < 32; i++){
unspent_membership.nodes[i] <== noteid_latest_path[i];
unspent_membership.selector[i] <== noteid_latest_selectors[i];
}
unspent_membership.root <== ledger_latest;
unspent_membership.leaf <== lottery_checker.note_identifier;
lottery_checker.out * unspent_membership.out === 1;
// Compute the entropy contribution
component entropy = derive_entropy();
entropy.slot <== sl;
entropy.note_id <== lottery_checker.note_identifier;
entropy.secret_key <== secret_key;
entropy_contribution <== entropy.out;
}