mirror of
https://github.com/logos-blockchain/logos-blockchain-pocs.git
synced 2026-01-05 14:43:08 +00:00
add the PoQ and corrected a lot of stuff related to Mantle updates
This commit is contained in:
parent
30ab8f7eda
commit
676acc9feb
98
circom_circuits/Blend/poq.circom
Normal file
98
circom_circuits/Blend/poq.circom
Normal file
@ -0,0 +1,98 @@
|
||||
// PoQ.circom
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "../hash_bn/poseidon2_hash.circom";
|
||||
include "../misc/constants.circom"; // defines NOMOS_KDF, SELECTION_RANDOMNESS, PROOF_NULLIFIER
|
||||
include "../misc/comparator.circom";
|
||||
include "../circomlib/circuits/bitify.circom";
|
||||
include "../ledger/notes.circom"; // defines proof_of_membership
|
||||
include "../Mantle/pol.circom"; // defines proof_of_leadership
|
||||
|
||||
/**
|
||||
* ProofOfQuota(nLevelsPK, nLevelsPol)
|
||||
*
|
||||
* - nLevelsPK : depth of the core-node public-key registry Merkle tree
|
||||
* - nLevelsPol : depth of the slot-secret tree used in PoL (25)
|
||||
*/
|
||||
template ProofOfQuota(nLevelsPK, nLevelsPol, bitsQuota) {
|
||||
// Public Inputs
|
||||
signal input session; // session s
|
||||
signal input Qc; // core quota Q_C
|
||||
signal input Ql; // leadership quota Q_L
|
||||
signal input pk_root; // Merkle root of registered core-node public keys
|
||||
signal input aged_root; // PoL: aged notes root
|
||||
signal input latest_root; // PoL: latest notes root
|
||||
signal input K; // Blend: one-time signature public key
|
||||
|
||||
signal output nullifier;
|
||||
|
||||
// Private Inputs
|
||||
signal input selector; // 0 = core, 1 = leader
|
||||
signal input index; // nullifier index
|
||||
|
||||
// Core-nodes inputs
|
||||
signal input core_sk; // core node secret key
|
||||
signal input core_path[nLevelsPK]; // Merkle path for core PK
|
||||
signal input core_selectors[nLevelsPK]; // path selectors (bits)
|
||||
|
||||
// Leaders inputs (all PoL inputs)
|
||||
component pol = proof_of_leadership();
|
||||
|
||||
|
||||
// Constraints
|
||||
selector * (1 - selector) === 0;
|
||||
|
||||
// derive pk_core = Poseidon(NOMOS_KDF || core_sk)
|
||||
component kdf = Poseidon2_hash(2);
|
||||
component dstKdf = NOMOS_KDF();
|
||||
kdf.inp[0] <== dstKdf.out;
|
||||
kdf.inp[1] <== core_sk;
|
||||
signal pk_core;
|
||||
pk_core <== kdf.out;
|
||||
|
||||
// Merkle‐verify pk_core in pk_root
|
||||
component coreReg = proof_of_membership(nLevelsPK);
|
||||
for (var i = 0; i < nLevelsPK; i++) {
|
||||
core_selectors[i] * (1 - core_selectors[i]) === 0;
|
||||
coreReg.nodes[i] <== core_path[i];
|
||||
coreReg.selector[i] <== core_selectors[i];
|
||||
}
|
||||
coreReg.root <== pk_root;
|
||||
coreReg.leaf <== pk_core;
|
||||
|
||||
// enforce PoL
|
||||
// (All constraints inside pol ensure LeadershipVerify)
|
||||
// /!\ copy the PoL constraints here /!\
|
||||
is_winning <== //0 or 1 for PoL
|
||||
|
||||
// Enforce the selected role is correct
|
||||
selector * (is_winning - coreReg.out) + coreReg.out === 1;
|
||||
|
||||
|
||||
|
||||
// Quota check: index < Qc if core, index < Ql if leader
|
||||
component cmp = SafeLessThan(bitsQuota);
|
||||
cmp.a <== index;
|
||||
cmp.b <== selector * (Ql - Qc) + Qc;
|
||||
cmp.out === 1;
|
||||
|
||||
// Derive selection_randomness
|
||||
component randomness = Poseidon2_hash(4);
|
||||
component dstSel = SELECTION_RANDOMNESS();
|
||||
randomness.inp[0] <== dstSel.out;
|
||||
// choose core_sk or pol.secret_key:
|
||||
randomness.inp[1] <== selector * (pol.secret_key - core_sk ) + core_sk;
|
||||
randomness.inp[2] <== index;
|
||||
randomness.inp[3] <== session;
|
||||
|
||||
// Derive proof_nullifier
|
||||
component nf = Poseidon2_hash(2);
|
||||
component dstNF = PROOF_NULLIFIER();
|
||||
nf.inp[0] <== dstNF.out;
|
||||
nf.inp[1] <== randomness.out;
|
||||
nullifier <== nf.out;
|
||||
}
|
||||
|
||||
// Instantiate with chosen depths: 32 for core PK tree, 25 for PoL slot tree
|
||||
component main { public [ session, Qc, Ql, pk_root, aged_root, latest_root, K ] }
|
||||
= ProofOfQuota(32, 25, 6);
|
||||
@ -11,7 +11,7 @@ include "../misc/constants.circom";
|
||||
template ticket_calculator(){
|
||||
signal input epoch_nonce;
|
||||
signal input slot;
|
||||
signal input commitment;
|
||||
signal input note_id;
|
||||
signal input secret_key;
|
||||
signal output out;
|
||||
|
||||
@ -20,7 +20,7 @@ template ticket_calculator(){
|
||||
hash.inp[0] <== dst.out;
|
||||
hash.inp[1] <== epoch_nonce;
|
||||
hash.inp[2] <== slot;
|
||||
hash.inp[3] <== commitment;
|
||||
hash.inp[3] <== note_id;
|
||||
hash.inp[4] <== secret_key;
|
||||
|
||||
out <== hash.out;
|
||||
@ -42,28 +42,27 @@ template derive_secret_key(){
|
||||
|
||||
template derive_entropy(){
|
||||
signal input slot;
|
||||
signal input commitment;
|
||||
signal input note_id;
|
||||
signal input secret_key;
|
||||
signal output out;
|
||||
|
||||
component hash = Poseidon2_hash(4);
|
||||
component dst = NOMOS_NONCE_CONTRIB();
|
||||
component dst = NOMOS_NONCE_CONTRIB_V1();
|
||||
hash.inp[0] <== dst.out;
|
||||
hash.inp[1] <== slot;
|
||||
hash.inp[2] <== commitment;
|
||||
hash.inp[2] <== note_id;
|
||||
hash.inp[3] <== secret_key;
|
||||
|
||||
out <== hash.out;
|
||||
}
|
||||
|
||||
|
||||
template proof_of_leadership(){
|
||||
template is_winning_leadership(secret_depth){
|
||||
signal input slot;
|
||||
signal input epoch_nonce;
|
||||
signal input t0;
|
||||
signal input t1;
|
||||
signal input slot_secret;
|
||||
signal input slot_secret_path[25];
|
||||
signal input slot_secret_path[secret_depth];
|
||||
|
||||
//Part of the note id proof of membership to prove aged
|
||||
signal input aged_nodes[32];
|
||||
@ -83,19 +82,10 @@ template proof_of_leadership(){
|
||||
signal input starting_slot;
|
||||
signal input secrets_root;
|
||||
|
||||
// The winning note. The unit is supposed to be NMO and the ZoneID is MANTLE
|
||||
signal input state;
|
||||
// The winning note value
|
||||
signal input value;
|
||||
signal input nonce;
|
||||
|
||||
// One time signing key used to sign the block proposal and the block
|
||||
signal input one_time_key;
|
||||
|
||||
//Avoid the circom optimisation that removes unused public input
|
||||
signal dummy;
|
||||
dummy <== one_time_key * one_time_key;
|
||||
|
||||
signal output entropy_contrib;
|
||||
signal output out;
|
||||
|
||||
|
||||
// Derive the secret key
|
||||
@ -109,28 +99,17 @@ template proof_of_leadership(){
|
||||
pk.secret_key <== sk.out;
|
||||
|
||||
|
||||
|
||||
// Derive the commitment from the note and the public key
|
||||
component cm = commitment();
|
||||
cm.state <== state;
|
||||
cm.value <== value;
|
||||
component nmo = NMO();
|
||||
cm.unit <== nmo.out;
|
||||
cm.nonce <== nonce;
|
||||
component staking = STAKING();
|
||||
cm.zoneID <== staking.out;
|
||||
cm.public_key <== pk.out;
|
||||
|
||||
// Derive the note id
|
||||
component note_id = Poseidon2_hash(4);
|
||||
component dst_note_id = NOMOS_NOTE_ID();
|
||||
component note_id = Poseidon2_hash(5);
|
||||
component dst_note_id = NOMOS_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] <== cm.out;
|
||||
note_id.inp[3] <== value;
|
||||
note_id.inp[4] <== pk.out;
|
||||
|
||||
|
||||
// Check the note is aged enough
|
||||
// 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;
|
||||
@ -141,7 +120,7 @@ template proof_of_leadership(){
|
||||
aged_membership.nodes[i] <== aged_nodes[i];
|
||||
aged_membership.selector[i] <== aged_selectors[i];
|
||||
}
|
||||
aged_membership.root <== aged_root;
|
||||
aged_membership.root <== aged_root;
|
||||
aged_membership.leaf <== note_id.out;
|
||||
|
||||
|
||||
@ -149,7 +128,7 @@ template proof_of_leadership(){
|
||||
component ticket = ticket_calculator();
|
||||
ticket.epoch_nonce <== epoch_nonce;
|
||||
ticket.slot <== slot;
|
||||
ticket.commitment <== cm.out;
|
||||
ticket.note_id <== note_id.out;
|
||||
ticket.secret_key <== sk.out;
|
||||
|
||||
|
||||
@ -164,7 +143,6 @@ template proof_of_leadership(){
|
||||
component winning = FullLessThan();
|
||||
winning.a <== ticket.out;
|
||||
winning.b <== threshold;
|
||||
winning.out === 1;
|
||||
|
||||
|
||||
// Check that the note is unspent
|
||||
@ -187,27 +165,101 @@ template proof_of_leadership(){
|
||||
component checker = SafeLessEqThan(252);
|
||||
checker.in[0] <== starting_slot;
|
||||
checker.in[1] <== slot;
|
||||
checker.out === 1;
|
||||
// Compute the positions related to slot - starting_slot
|
||||
component bits = Num2Bits(25);
|
||||
// Compute the positions related to slot - starting_slot (and make sure it's 25 bits)
|
||||
component bits = Num2Bits(secret_depth);
|
||||
bits.in <== slot - starting_slot;
|
||||
// Check the membership of the secret_slot against the secrets_root
|
||||
component secret_membership = proof_of_membership(25);
|
||||
for(var i =0; i<25; i++){
|
||||
component secret_membership = proof_of_membership(secret_depth);
|
||||
for(var i =0; i<secret_depth; i++){
|
||||
secret_membership.nodes[i] <== slot_secret_path[i];
|
||||
secret_membership.selector[i] <== bits.out[24-i];
|
||||
secret_membership.selector[i] <== bits.out[secret_depth-1-i];
|
||||
}
|
||||
secret_membership.root <== secrets_root;
|
||||
secret_membership.leaf <== slot_secret;
|
||||
|
||||
// Check that every constraint holds
|
||||
signal intermediate_out[3];
|
||||
intermediate_out[0] <== aged_membership.out * winning.out;
|
||||
intermediate_out[1] <== unspent_membership.out * secret_membership.out;
|
||||
intermediate_out[2] <== intermediate_out[0] * intermediate_out[1];
|
||||
out <== intermediate_out[2] * checker.out;
|
||||
}
|
||||
|
||||
|
||||
template proof_of_leadership(secret_depth){
|
||||
signal input slot;
|
||||
signal input epoch_nonce;
|
||||
signal input t0;
|
||||
signal input t1;
|
||||
signal input slot_secret;
|
||||
signal input slot_secret_path[secret_depth];
|
||||
|
||||
//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;
|
||||
|
||||
//Part of the note id proof of membership to prove it's unspent
|
||||
signal input latest_nodes[32];
|
||||
signal input latest_selectors[32]; // must be bits
|
||||
signal input latest_root;
|
||||
|
||||
//Part of the secret key
|
||||
signal input starting_slot;
|
||||
signal input secrets_root;
|
||||
|
||||
// The winning note. The unit is supposed to be NMO and the ZoneID is MANTLE
|
||||
signal input value;
|
||||
|
||||
// Verify the note is winning the lottery
|
||||
component lottery_checker = is_winning_leadership(secret_depth);
|
||||
lottery_checker.slot <== slot;
|
||||
lottery_checker.epoch_nonce <== epoch_nonce;
|
||||
lottery_checker.t0 <== t0;
|
||||
lottery_checker.t1 <== t1;
|
||||
lottery_checker.slot_secret <== slot_secret;
|
||||
for(i = 0; i < secret_depth; i++){
|
||||
lottery_checker.slot_secret_path[i] <== slot_secret_path[i];
|
||||
}
|
||||
for(i = 0; i < 32; i++){
|
||||
lottery_checker.aged_nodes[i] <== aged_nodes[i];
|
||||
lottery_checker.aged_selectors[i] <== aged_selectors[i];
|
||||
lottery_checker.latest_nodes[i] <== latest_nodes[i];
|
||||
lottery_checker.latest_selectors[i] <== latest_selectors[i];
|
||||
}
|
||||
lottery_checker.aged_root <== aged_root;
|
||||
lottery_checker.transaction_hash <== transaction_hash;
|
||||
lottery_checker.output_number <== output_number;
|
||||
lottery_checker.latest_root <== latest_root;
|
||||
lottery_checker.starting_slot <== starting_slot;
|
||||
lottery_checker.secrets_root <== secrets_root;
|
||||
lottery_checker.value <== value;
|
||||
|
||||
lottery_checker.out === 1;
|
||||
|
||||
|
||||
// One time signing key used to sign the block proposal and the block
|
||||
signal input one_time_key;
|
||||
|
||||
//Avoid the circom optimisation that removes unused public input
|
||||
signal dummy;
|
||||
dummy <== one_time_key * one_time_key;
|
||||
|
||||
signal output entropy_contrib;
|
||||
|
||||
|
||||
// Compute the entropy contribution
|
||||
component entropy = derive_entropy();
|
||||
entropy.slot <== slot;
|
||||
entropy.commitment <== cm.out;
|
||||
entropy.note_id <== note_id.out;
|
||||
entropy.secret_key <== sk.out;
|
||||
|
||||
entropy_contrib <== entropy.out;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
component main {public [slot,epoch_nonce,t0,t1,aged_root,latest_root,one_time_key]}= proof_of_leadership();
|
||||
@ -2,7 +2,7 @@
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "../hash_bn/poseidon2_hash.circom";
|
||||
include "../misc/comparator.circom";
|
||||
include "../circomlib/circuits/comparators.circom";
|
||||
|
||||
// proof of Merkle membership of depth n
|
||||
// /!\ To call this function, it's important to check that each selector is a bit before!!!
|
||||
@ -11,6 +11,7 @@ template proof_of_membership(n) {
|
||||
signal input selector[n]; // it's the leaf's indice in big endian bits
|
||||
signal input root;
|
||||
signal input leaf;
|
||||
signal output out;
|
||||
|
||||
|
||||
component compression_hash[n];
|
||||
@ -25,45 +26,9 @@ template proof_of_membership(n) {
|
||||
compression_hash[i].inp[1] <== nodes[i] - selector[n-1-i] * (nodes[i] - compression_hash[i-1].out);
|
||||
}
|
||||
|
||||
root === compression_hash[n-1].out;
|
||||
}
|
||||
component eq = IsEqual();
|
||||
eq.in[0] <== root;
|
||||
eq.in[1] <== compression_hash[n-1].out;
|
||||
|
||||
|
||||
// /!\ DEPRECATED /!\
|
||||
// proof of Merkle non-membership using an IMT of depth n
|
||||
// /!\ To call this function, it's important to check that each selector is a bit before!!!
|
||||
template proof_of_non_membership(n) {
|
||||
signal input previous; // We prove that the nullifier isn't in the set because it falls between previous and next
|
||||
signal input nullifier;
|
||||
signal input next;
|
||||
signal input nodes[n];
|
||||
signal input selector[n];
|
||||
signal input root;
|
||||
|
||||
component hash = Poseidon2_hash(2);
|
||||
component comparator[2];
|
||||
component membership = proof_of_membership(n);
|
||||
|
||||
// Recover the leaf representing previous pointing to next in the IMT
|
||||
hash.inp[0] <== previous;
|
||||
hash.inp[1] <== next;
|
||||
|
||||
// Verify that the leaf computed is indeed in the IMT
|
||||
membership.root <== root;
|
||||
membership.leaf <== hash.out;
|
||||
for(var i =0; i < n; i++){
|
||||
membership.nodes[i] <== nodes[i];
|
||||
membership.selector[i] <== selector[i];
|
||||
}
|
||||
|
||||
// Check that nullifier stictly falls between previous and next.
|
||||
comparator[0] = SafeFullLessThan();
|
||||
comparator[0].a <== previous;
|
||||
comparator[0].b <== nullifier;
|
||||
comparator[0].out === 1;
|
||||
|
||||
comparator[1] = SafeFullLessThan();
|
||||
comparator[1].a <== nullifier;
|
||||
comparator[1].b <== next;
|
||||
comparator[1].out === 1;
|
||||
out <== eq.out;
|
||||
}
|
||||
@ -2,47 +2,8 @@
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "../hash_bn/poseidon2_hash.circom";
|
||||
include "merkle.circom";
|
||||
include "../misc/constants.circom";
|
||||
|
||||
// The unit of the note is supposed to be NMO
|
||||
template commitment(){
|
||||
signal input state;
|
||||
signal input value;
|
||||
signal input unit;
|
||||
signal input nonce;
|
||||
signal input zoneID;
|
||||
signal input public_key;
|
||||
signal output out;
|
||||
|
||||
component hash = Poseidon2_hash(7);
|
||||
component dst = NOMOS_NOTE_CM();
|
||||
hash.inp[0] <== dst.out;
|
||||
hash.inp[1] <== state;
|
||||
hash.inp[2] <== value;
|
||||
hash.inp[3] <== unit;
|
||||
hash.inp[4] <== nonce;
|
||||
hash.inp[5] <== public_key;
|
||||
hash.inp[6] <== zoneID;
|
||||
|
||||
out <== hash.out;
|
||||
}
|
||||
|
||||
// We don't use it anymore
|
||||
template nullifier(){
|
||||
signal input commitment;
|
||||
signal input secret_key;
|
||||
signal output out;
|
||||
|
||||
component hash = Poseidon2_hash(3);
|
||||
component dst = NOMOS_NOTE_NF();
|
||||
hash.inp[0] <== dst.out;
|
||||
hash.inp[1] <== commitment;
|
||||
hash.inp[2] <== secret_key;
|
||||
|
||||
out <== hash.out;
|
||||
}
|
||||
|
||||
template derive_public_key(){
|
||||
signal input secret_key;
|
||||
signal output out;
|
||||
@ -52,22 +13,4 @@ template derive_public_key(){
|
||||
hash.inp[0] <== dst.out;
|
||||
hash.inp[1] <== secret_key;
|
||||
out <== hash.out;
|
||||
}
|
||||
|
||||
template derive_unit(){
|
||||
signal input minting_covenant;
|
||||
signal input spending_covenant;
|
||||
signal input burning_covenant;
|
||||
signal input unit_arg_cm;
|
||||
signal output out;
|
||||
|
||||
component hash = Poseidon2_hash(5);
|
||||
component dst = NOMOS_UNIT();
|
||||
hash.inp[0] <== dst.out;
|
||||
hash.inp[1] <== minting_covenant;
|
||||
hash.inp[2] <== spending_covenant;
|
||||
hash.inp[3] <== burning_covenant;
|
||||
hash.inp[4] <== unit_arg_cm;
|
||||
out <== hash.out;
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user