Merge pull request #7 from logos-blockchain/tl/PoL_update

Tl/po l update
This commit is contained in:
thomaslavaur 2026-01-15 13:15:36 +01:00 committed by GitHub
commit 0b0054a57b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 34 additions and 131 deletions

View File

@ -221,7 +221,7 @@ if not core_or_leader in [0,1]:
# 1) Corenode registry Merkleproof
# pick a random core_sk and derive its public key
core_sk = F(randrange(0,p,1))
pk_core = Compression([ F(1296193216988918402894), core_sk ])
pk_core = Compression([ F(4605003), core_sk ])
core_selectors = randrange(0,2**20,1)
core_selectors = format(int(core_selectors),'020b')
core_nodes = [F(randrange(0,p,1)) for i in range(20)]
@ -247,30 +247,18 @@ t1 = F(p- (int(t1_constant) // total_stake**2))
value = F(total_stake / 100)
threshold = (t0 + t1 * value) * value
starting_slot = randrange(max(0,slot_number-2**25+1),slot_number,1)
slot_secret = F(randrange(0,p,1))
slot_secret_indexes = format(int(slot_number - starting_slot),'025b')
sk = F(randrange(0,p,1))
tx_hash = F(randrange(0,p,1))
output_number = F(randrange(0,50,1))
pk = Compression([F(4605003),sk])
slot_secret_path = [F(randrange(0,p,1)) for i in range(25)]
secret_root = slot_secret
for i in range(25):
if int(slot_secret_indexes[24-i]) == 0:
secret_root = Compression([secret_root,slot_secret_path[i]])
else:
secret_root = Compression([slot_secret_path[i],secret_root])
sk = poseidon2_hash([F(256174383281726064679014503048630094),starting_slot,secret_root])
pk = Compression([F(1296193216988918402894),sk])
note_id = poseidon2_hash([F(65580641562429851895355409762135920462),tx_hash,output_number,value,pk])
note_id = poseidon2_hash([F(232989242343357190262606),tx_hash,output_number,value,pk])
ticket = poseidon2_hash([F(13887241025832268),F(epoch_nonce),F(slot_number),note_id,sk])
while(ticket > threshold):
output_number += 1
note_id = poseidon2_hash([F(65580641562429851895355409762135920462),tx_hash,output_number,value,pk])
note_id = poseidon2_hash([F(232989242343357190262606),tx_hash,output_number,value,pk])
ticket = poseidon2_hash([F(13887241025832268),F(epoch_nonce),F(slot_number),note_id,sk])
aged_nodes = [F(randrange(0,p,1)) for i in range(32)]
@ -308,13 +296,11 @@ inp = {
"pol_epoch_nonce": str(epoch_nonce),
"pol_t0": str(t0),
"pol_t1": str(t1),
"pol_slot_secret": str(slot_secret),
"pol_slot_secret_path": [str(x) for x in slot_secret_path],
"pol_secret_key": str(sk),
"pol_noteid_path": [str(x) for x in aged_nodes],
"pol_noteid_path_selectors": [str(x) for x in aged_selectors],
"pol_note_tx_hash": str(tx_hash),
"pol_note_output_number": str(output_number),
"pol_sk_starting_slot": str(starting_slot),
"pol_note_value": str(value)
}

View File

@ -2,7 +2,7 @@
pragma circom 2.1.9;
include "../hash_bn/poseidon2_hash.circom";
include "../misc/constants.circom"; // defines NOMOS_KDF, SELECTION_RANDOMNESS, PROOF_NULLIFIER
include "../misc/constants.circom"; // defines KDF, SELECTION_RANDOMNESS, PROOF_NULLIFIER
include "../misc/comparator.circom";
include "../circomlib/circuits/bitify.circom";
include "../mantle/pol_lib.circom"; // defines proof_of_leadership
@ -48,15 +48,13 @@ template ProofOfQuota(nLevelsPK, nLevelsPol, bitsQuota) {
signal input pol_epoch_nonce;
signal input pol_t0;
signal input pol_t1;
signal input pol_slot_secret;
signal input pol_slot_secret_path[nLevelsPol];
signal input pol_noteid_path[32];
signal input pol_noteid_path_selectors[32];
signal input pol_secret_key;
signal input pol_note_tx_hash;
signal input pol_note_output_number;
signal input pol_sk_starting_slot;
signal input pol_note_value;
@ -96,10 +94,6 @@ template ProofOfQuota(nLevelsPK, nLevelsPol, bitsQuota) {
would_win.epoch_nonce <== pol_epoch_nonce;
would_win.t0 <== pol_t0;
would_win.t1 <== pol_t1;
would_win.slot_secret <== pol_slot_secret;
for (var i = 0; i < nLevelsPol; i++) {
would_win.slot_secret_path[i] <== pol_slot_secret_path[i];
}
for (var i = 0; i < 32; i++) {
would_win.aged_nodes[i] <== pol_noteid_path[i];
would_win.aged_selectors[i] <== pol_noteid_path_selectors[i];
@ -107,7 +101,7 @@ template ProofOfQuota(nLevelsPK, nLevelsPol, bitsQuota) {
would_win.aged_root <== pol_ledger_aged;
would_win.transaction_hash <== pol_note_tx_hash;
would_win.output_number <== pol_note_output_number;
would_win.starting_slot <== pol_sk_starting_slot;
would_win.secret_key <== pol_secret_key;
would_win.value <== pol_note_value;
// Enforce the selected role is correct

View File

@ -5,26 +5,12 @@ include "../hash_bn/poseidon2_hash.circom";
include "../hash_bn/poseidon2_perm.circom";
include "../misc/constants.circom";
template derive_secret_key(){
signal input starting_slot;
signal input secrets_root;
signal output out;
component hash = Poseidon2_hash(3);
component dst = NOMOS_POL_SK_V1();
hash.inp[0] <== dst.out;
hash.inp[1] <== starting_slot;
hash.inp[2] <== secrets_root;
out <== hash.out;
}
template derive_public_key(){
signal input secret_key;
signal output out;
component hash = Compression();
component dst = NOMOS_KDF();
component dst = KDF();
hash.inp[0] <== dst.out;
hash.inp[1] <== secret_key;
out <== hash.out;

View File

@ -228,30 +228,17 @@ t1 = F(p- (int(t1_constant) // total_stake**2))
value = F(total_stake / 100)
threshold = (t0 + t1 * value) * value
starting_slot = randrange(max(0,slot_number-2**25+1),slot_number,1)
slot_secret = F(randrange(0,p,1))
slot_secret_indexes = format(int(slot_number - starting_slot),'025b')
sk = F(randrange(0,p,1))
tx_hash = F(randrange(0,p,1))
output_number = F(randrange(0,50,1))
pk = Compression([F(4605003),sk])
slot_secret_path = [F(randrange(0,p,1)) for i in range(25)]
secret_root = slot_secret
for i in range(25):
if int(slot_secret_indexes[24-i]) == 0:
secret_root = Compression([secret_root,slot_secret_path[i]])
else:
secret_root = Compression([slot_secret_path[i],secret_root])
sk = poseidon2_hash([F(256174383281726064679014503048630094),starting_slot,secret_root])
pk = Compression([F(1296193216988918402894),sk])
note_id = poseidon2_hash([F(65580641562429851895355409762135920462),tx_hash,output_number,value,pk])
note_id = poseidon2_hash([F(232989242343357190262606),tx_hash,output_number,value,pk])
ticket = poseidon2_hash([F(13887241025832268),F(epoch_nonce),F(slot_number),note_id,sk])
while(ticket > threshold):
output_number += 1
note_id = poseidon2_hash([F(65580641562429851895355409762135920462),tx_hash,output_number,value,pk])
note_id = poseidon2_hash([F(232989242343357190262606),tx_hash,output_number,value,pk])
ticket = poseidon2_hash([F(13887241025832268),F(epoch_nonce),F(slot_number),note_id,sk])
aged_nodes = [F(randrange(0,p,1)) for i in range(32)]
@ -284,10 +271,9 @@ inp = {
"epoch_nonce": str(epoch_nonce),
"t0": str(t0),
"t1": str(t1),
"slot_secret": str(slot_secret),
"secret_key": str(sk),
"P_lead_part_one": str(F(123456)),
"P_lead_part_two": str(F(654321)),
"slot_secret_path": [str(x) for x in slot_secret_path],
"noteid_aged_path": [str(x) for x in aged_nodes],
"noteid_aged_selectors": [str(x) for x in aged_selectors],
"ledger_aged": str(aged_root),
@ -296,7 +282,6 @@ inp = {
"noteid_latest_path": [str(x) for x in unspent_nodes],
"noteid_latest_selectors": [str(x) for x in unspent_selectors],
"ledger_latest": str(latest_root),
"starting_slot": str(starting_slot),
"v": str(value)
}

View File

@ -34,7 +34,7 @@ template derive_entropy(){
signal output out;
component hash = Poseidon2_hash(4);
component dst = NOMOS_NONCE_CONTRIB_V1();
component dst = NONCE_CONTRIB_V1();
hash.inp[0] <== dst.out;
hash.inp[1] <== slot;
hash.inp[2] <== note_id;
@ -48,8 +48,6 @@ template would_win_leadership(secret_depth){
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];
@ -59,51 +57,23 @@ template would_win_leadership(secret_depth){
//Used to derive the note identifier
signal input transaction_hash;
signal input output_number;
//Part of the secret key
signal input starting_slot;
signal input secret_key;
// The winning note value
signal input value;
signal output out;
signal output note_identifier;
signal output secret_key;
// Derivation of the secrets root from the slot secret at position slot - starting_slot
// Verify that the substraction wont underflow (starting_slot < slot)
component checker = SafeFullLessThan();
checker.a <== starting_slot;
checker.b <== slot;
// Compute the positions related to slot - starting_slot and make sure slot - starting_slot is a 25 bits number
component bits = Num2Bits(secret_depth);
bits.in <== slot - starting_slot;
// Derive the secrets root
component secrets_root = compute_merkle_root(secret_depth);
for(var i=0; i<secret_depth; i++){
secrets_root.nodes[i] <== slot_secret_path[i];
secrets_root.selector[i] <== bits.out[secret_depth-1-i];
}
secrets_root.leaf <== slot_secret;
// Derive the secret key
component sk = derive_secret_key();
sk.starting_slot <== starting_slot;
sk.secrets_root <== secrets_root.root;
// Derive the public key from the secret key
component pk = derive_public_key();
pk.secret_key <== sk.out;
pk.secret_key <== secret_key;
// Derive the note id
component note_id = Poseidon2_hash(5);
component dst_note_id = NOMOS_NOTE_ID_V1();
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;
@ -131,7 +101,7 @@ template would_win_leadership(secret_depth){
ticket.epoch_nonce <== epoch_nonce;
ticket.slot <== slot;
ticket.note_id <== note_id.out;
ticket.secret_key <== sk.out;
ticket.secret_key <== secret_key;
// Compute the lottery threshold
@ -147,12 +117,9 @@ template would_win_leadership(secret_depth){
winning.b <== threshold;
// Check that every constraint holds
signal intermediate_out;
intermediate_out <== aged_membership.out * winning.out;
out <== intermediate_out * checker.out;
out <== aged_membership.out * winning.out;
note_identifier <== note_id.out;
secret_key <== sk.out;
}
@ -161,8 +128,6 @@ template proof_of_leadership(secret_depth){
signal input epoch_nonce; // the epoch nonce eta
signal input t0;
signal input t1;
signal input slot_secret; // This is r_sl
signal input slot_secret_path[secret_depth];
//Part of the note id proof of membership to prove aged
signal input noteid_aged_path[32];
@ -170,6 +135,7 @@ template proof_of_leadership(secret_depth){
signal input ledger_aged;
//Used to derive the note identifier
signal input secret_key;
signal input note_tx_hash;
signal input note_output_number;
@ -178,10 +144,7 @@ template proof_of_leadership(secret_depth){
signal input noteid_latest_selectors[32]; // must be bits
signal input ledger_latest;
//Part of the secret key
signal input starting_slot;
// The winning note. The unit is supposed to be NMO and the ZoneID is MANTLE
// The winning note.
signal input v; // value of the note
@ -191,10 +154,6 @@ template proof_of_leadership(secret_depth){
lottery_checker.epoch_nonce <== epoch_nonce;
lottery_checker.t0 <== t0;
lottery_checker.t1 <== t1;
lottery_checker.slot_secret <== slot_secret;
for(var i = 0; i < secret_depth; i++){
lottery_checker.slot_secret_path[i] <== slot_secret_path[i];
}
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];
@ -202,7 +161,7 @@ template proof_of_leadership(secret_depth){
lottery_checker.aged_root <== ledger_aged;
lottery_checker.transaction_hash <== note_tx_hash;
lottery_checker.output_number <== note_output_number;
lottery_checker.starting_slot <== starting_slot;
lottery_checker.secret_key <== secret_key;
lottery_checker.value <== v;
@ -241,7 +200,7 @@ template proof_of_leadership(secret_depth){
component entropy = derive_entropy();
entropy.slot <== sl;
entropy.note_id <== lottery_checker.note_identifier;
entropy.secret_key <== lottery_checker.secret_key;
entropy.secret_key <== secret_key;
entropy_contribution <== entropy.out;
}

View File

@ -11,31 +11,24 @@ template LEAD_V1(){
}
// int.from_bytes(b"NOMOS_POL_SK_V1", byteorder="little") = 256174383281726064679014503048630094
template NOMOS_POL_SK_V1(){
// int.from_bytes(b"NONCE_CONTRIB_V1", byteorder="little") = 65580641403957881555985426713123114830
template NONCE_CONTRIB_V1(){
signal output out;
out <== 256174383281726064679014503048630094;
out <== 65580641403957881555985426713123114830;
}
// int.from_bytes(b"NOMOS_NONCE_CONTRIB_V1", byteorder="little") = 18459309511848927313552932915476467038165525790019406
template NOMOS_NONCE_CONTRIB_V1(){
// int.from_bytes(b"KDF", byteorder="little") = 4605003
template KDF(){
signal output out;
out <== 18459309511848927313552932915476467038165525790019406;
out <== 4605003;
}
// int.from_bytes(b"NOMOS_KDF", byteorder="little") = 1296193216988918402894
template NOMOS_KDF(){
// int.from_bytes(b"NOTE_ID_V1", byteorder="little") = 232989242343357190262606
template NOTE_ID_V1(){
signal output out;
out <== 1296193216988918402894;
}
// int.from_bytes(b"NOMOS_NOTE_ID_V1", byteorder="little") = 65580641562429851895355409762135920462
template NOMOS_NOTE_ID_V1(){
signal output out;
out <== 65580641562429851895355409762135920462;
out <== 232989242343357190262606;
}