Add PoL crate
This commit is contained in:
parent
0cb039d806
commit
8a17deccaf
|
@ -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"
|
|
@ -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"
|
|
@ -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>,
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
Cargo.lock
|
||||
target/
|
|
@ -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
|
|
@ -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"
|
|
@ -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>(¬e_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(¬es);
|
||||
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());
|
||||
}
|
||||
}
|
|
@ -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"]
|
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
risc0_build::embed_methods();
|
||||
}
|
|
@ -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" }
|
|
@ -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);
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
include!(concat!(env!("OUT_DIR"), "/methods.rs"));
|
Loading…
Reference in New Issue