Add PoL crate

This commit is contained in:
Giacomo Pasini 2024-09-02 21:18:33 +02:00
parent 0cb039d806
commit 8a17deccaf
No known key found for this signature in database
GPG Key ID: FC08489D2D895D4B
12 changed files with 321 additions and 2 deletions

View File

@ -29,6 +29,8 @@ members = [
"consensus/carnot-engine",
"consensus/cryptarchia-engine",
"ledger/cryptarchia-ledger",
"tests"
"proof_of_leadership/proof_statements",
"tests",
]
resolver = "2"
exclude = ["proof_of_leadership/risc0/risc0_proofs"]
resolver = "2"

View File

@ -0,0 +1,10 @@
[package]
name = "leader_proof_statements"
version = "0.1.0"
edition = "2021"
[dependencies]
cl = { path = "../../cl/cl" }
serde = { version = "1.0", features = ["derive"] }
crypto-bigint = { version = "0.5.5", features = ["serde"] }
sha2 = "0.10"

View File

@ -0,0 +1,94 @@
use crypto_bigint::{CheckedMul, CheckedSub, Encoding, U256};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct LeaderPublic {
pub cm_root: [u8; 32],
pub epoch_nonce: [u8; 32],
pub slot: u64,
pub scaled_phi_approx: (U256, U256),
pub nullifier: cl::Nullifier,
pub evolved_commitment: cl::NoteCommitment,
}
impl LeaderPublic {
pub fn new(
cm_root: [u8; 32],
epoch_nonce: [u8; 32],
slot: u64,
active_slot_coefficient: f64,
total_stake: u64,
nullifier: cl::Nullifier,
evolved_commitment: cl::NoteCommitment,
) -> Self {
let total_stake_big = U256::from_u64(total_stake);
let total_stake_sq_big = total_stake_big.checked_mul(&total_stake_big).unwrap();
let double_total_stake_sq_big = total_stake_sq_big.checked_mul(&U256::from_u64(2)).unwrap();
let precision_u64 = u64::MAX;
let precision_big = U256::from_u64(u64::MAX);
let precision_f64 = precision_u64 as f64;
let order: U256 = U256::MAX;
let order_div_precision = order.checked_div(&precision_big).unwrap();
let order_div_precision_sq = order_div_precision.checked_div(&precision_big).unwrap();
let neg_f_ln: U256 =
U256::from_u64(((-f64::ln(1f64 - active_slot_coefficient)) * precision_f64) as u64);
let neg_f_ln_sq = neg_f_ln.checked_mul(&neg_f_ln).unwrap();
let neg_f_ln_order: U256 = order_div_precision.checked_mul(&neg_f_ln).unwrap();
let t0 = neg_f_ln_order.checked_div(&total_stake_big).unwrap();
let t1 = order_div_precision_sq
.checked_mul(&neg_f_ln_sq)
.unwrap()
.checked_div(&double_total_stake_sq_big)
.unwrap();
Self {
cm_root,
epoch_nonce,
slot,
nullifier,
evolved_commitment,
scaled_phi_approx: (t0, t1),
}
}
pub fn check_winning(&self, input: &cl::InputWitness) -> bool {
let threshold = phi_approx(U256::from_u64(input.note.value), self.scaled_phi_approx);
let ticket = ticket(input, self.epoch_nonce, self.slot);
ticket < threshold
}
}
fn phi_approx(stake: U256, approx: (U256, U256)) -> U256 {
// stake * (t0 - t1 * stake)
stake
.checked_mul(
&approx
.0
.checked_sub(&approx.1.checked_mul(&stake).unwrap())
.unwrap(),
)
.unwrap()
}
fn ticket(input: &cl::InputWitness, epoch_nonce: [u8; 32], slot: u64) -> U256 {
let mut hasher = Sha256::new();
hasher.update(b"NOMOS_LEAD");
hasher.update(epoch_nonce);
hasher.update(slot.to_be_bytes());
hasher.update(input.note_commitment().as_bytes());
hasher.update(input.nf_sk.0);
let ticket_bytes: [u8; 32] = hasher.finalize().into();
U256::from_be_bytes(ticket_bytes)
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct LeaderPrivate {
pub input: cl::InputWitness,
pub input_cm_path: Vec<cl::merkle::PathNode>,
}

2
proof_of_leadership/risc0/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
Cargo.lock
target/

View File

@ -0,0 +1,11 @@
[workspace]
resolver = "2"
members = [ "prover", "risc0_proofs"]
# Always optimize; building and running the risc0_proofs takes much longer without optimization.
[profile.dev]
opt-level = 3
[profile.release]
debug = 1
lto = true

View File

@ -0,0 +1,15 @@
[package]
name = "nomos_pol_prover"
version = "0.1.0"
edition = "2021"
[dependencies]
cl = { path = "../../../cl/cl" }
leader_proof_statements = { path = "../../proof_statements" }
nomos_pol_risc0_proofs = { path = "../risc0_proofs" }
risc0-zkvm = { version = "1.0", features = ["prove", "metal"] }
risc0-groth16 = { version = "1.0" }
tracing = "0.1"
rand = "0.8.5"
rand_core = "0.6.0"
thiserror = "1.0.62"

View File

@ -0,0 +1,111 @@
use leader_proof_statements::{LeaderPrivate, LeaderPublic};
use risc0_zkvm::Receipt;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[error("risc0 failed to serde")]
Risc0Serde(#[from] risc0_zkvm::serde::Error),
}
pub fn prove(leader_public: LeaderPublic, leader_private: LeaderPrivate) -> Result<Receipt, Error> {
let env = risc0_zkvm::ExecutorEnv::builder()
.write(&leader_public)
.unwrap()
.write(&leader_private)
.unwrap()
.build()
.unwrap();
// Obtain the default prover.
let prover = risc0_zkvm::default_prover();
let start_t = std::time::Instant::now();
// Proof information by proving the specified ELF binary.
// This struct contains the receipt along with statistics about execution of the guest
let opts = risc0_zkvm::ProverOpts::succinct();
let prove_info =
prover.prove_with_opts(env, nomos_pol_risc0_proofs::PROOF_OF_LEADERSHIP_ELF, &opts)?;
tracing::debug!(
"STARK prover time: {:.2?}, total_cycles: {}",
start_t.elapsed(),
prove_info.stats.total_cycles
);
// extract the receipt.
Ok(prove_info.receipt)
}
#[cfg(test)]
mod test {
use super::*;
use cl::{note::NoteWitness, nullifier::NullifierSecret};
use rand::thread_rng;
const MAX_NOTE_COMMS: usize = 1 << 8;
// derive-unit(NOMOS_NMO)
pub const NMO_UNIT: [u8; 32] = [
67, 50, 140, 228, 181, 204, 226, 242, 254, 193, 239, 51, 237, 68, 36, 126, 124, 227, 60,
112, 223, 195, 146, 236, 5, 21, 42, 215, 48, 122, 25, 195,
];
fn note_commitment_leaves(
note_commitments: &[cl::NoteCommitment],
) -> [[u8; 32]; MAX_NOTE_COMMS] {
let note_comm_bytes =
Vec::from_iter(note_commitments.iter().map(|c| c.as_bytes().to_vec()));
let cm_leaves = cl::merkle::padded_leaves::<MAX_NOTE_COMMS>(&note_comm_bytes);
cm_leaves
}
#[test]
fn failure() {
assert!(false);
}
#[test]
fn test_leader_prover() {
let mut rng = thread_rng();
let input = cl::InputWitness {
note: NoteWitness::basic(32, NMO_UNIT, &mut rng),
nf_sk: NullifierSecret::random(&mut rng),
};
let notes = vec![input.note_commitment()];
let epoch_nonce = [0u8; 32];
let slot = 0;
let active_slot_coefficient = 0.05;
let total_stake = 1000;
let leaves = note_commitment_leaves(&notes);
let mut expected_public_inputs = LeaderPublic::new(
cl::merkle::root(leaves),
epoch_nonce,
slot,
active_slot_coefficient,
total_stake,
input.nullifier(),
input.evolve_output(b"NOMOS_POL").commit_note(),
);
while !expected_public_inputs.check_winning(&input) {
expected_public_inputs.slot += 1;
}
println!("slot={}", expected_public_inputs.slot);
let private_inputs = LeaderPrivate {
input: input.clone(),
input_cm_path: cl::merkle::path(leaves, 0),
};
let proof = prove(expected_public_inputs, private_inputs);
assert!(proof
.verify(nomos_pol_risc0_proofs::PROOF_OF_LEADERSHIP_ID)
.is_ok());
assert_eq!(expected_public_inputs, proof.journal.decode().unwrap());
}
}

View File

@ -0,0 +1,10 @@
[package]
name = "nomos_pol_risc0_proofs"
version = "0.1.0"
edition = "2021"
[build-dependencies]
risc0-build = { version = "1.0" }
[package.metadata.risc0]
methods = ["proof_of_leadership"]

View File

@ -0,0 +1,3 @@
fn main() {
risc0_build::embed_methods();
}

View File

@ -0,0 +1,19 @@
[package]
name = "proof_of_leadership"
version = "0.1.0"
edition = "2021"
[workspace]
[dependencies]
risc0-zkvm = { version = "1.0", default-features = false, features = ['std'] }
serde = { version = "1.0", features = ["derive"] }
cl = { path = "../../../../cl/cl" }
leader_proof_statements = { path = "../../../proof_statements" }
sha2 = "0.10"
crypto-bigint = "0.5.5"
[patch.crates-io]
# add RISC Zero accelerator support for all downstream usages of the following crates.
sha2 = { git = "https://github.com/risc0/RustCrypto-hashes", tag = "sha2-v0.10.8-risczero.0" }
crypto-bigint = { git = "https://github.com/risc0/RustCrypto-crypto-bigint", tag = "v0.5.5-risczero.0" }

View File

@ -0,0 +1,41 @@
use cl::balance::Unit;
/// Proof of Leadership
use cl::merkle;
use leader_proof_statements::{LeaderPrivate, LeaderPublic};
use risc0_zkvm::guest::env;
// derive-unit(NOMOS_NMO)
pub const NMO_UNIT: Unit = [
67, 50, 140, 228, 181, 204, 226, 242, 254, 193, 239, 51, 237, 68, 36, 126, 124, 227, 60, 112,
223, 195, 146, 236, 5, 21, 42, 215, 48, 122, 25, 195,
];
fn main() {
let public_inputs: LeaderPublic = env::read();
let LeaderPrivate {
input,
input_cm_path,
} = env::read();
// Lottery checks
assert!(public_inputs.check_winning(&input));
// Ensure note is valid
assert_eq!(input.note.unit, NMO_UNIT);
let note_cm = input.note_commitment();
let note_cm_leaf = merkle::leaf(note_cm.as_bytes());
let note_cm_root = merkle::path_root(note_cm_leaf, &input_cm_path);
assert_eq!(note_cm_root, public_inputs.cm_root);
// Public input constraints
assert_eq!(input.nullifier(), public_inputs.nullifier);
let evolved_output = input.evolve_output(b"NOMOS_POL");
assert_eq!(
evolved_output.commit_note(),
public_inputs.evolved_commitment
);
env::commit(&public_inputs);
}

View File

@ -0,0 +1 @@
include!(concat!(env!("OUT_DIR"), "/methods.rs"));