Skip to content
This repository has been archived by the owner on Nov 23, 2023. It is now read-only.

Commit

Permalink
feat: threshold circuit integration
Browse files Browse the repository at this point in the history
  • Loading branch information
brech1 committed Sep 28, 2023
1 parent 13932bd commit 31d1a0c
Show file tree
Hide file tree
Showing 10 changed files with 710 additions and 227 deletions.
124 changes: 109 additions & 15 deletions eigentrust-cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{
use clap::{Args, Parser, Subcommand};
use eigentrust::{
attestation::{AttestationRaw, SignedAttestationRaw},
circuit::ET_PARAMS_K,
circuit::{Circuit, ET_PARAMS_K, TH_PARAMS_K},
error::EigenError,
eth::deploy_as,
storage::{
Expand Down Expand Up @@ -53,6 +53,12 @@ pub enum Mode {
LocalScores,
/// Retrieves and saves all attestations and calculates the global scores.
Scores,
/// Generates a Threshold circuit proof for the selected participant.
ThProof(ThProofData),
/// Generates EigenTrust circuit proving key
ThProvingKey,
/// Verifies the stored threshold circuit proof.
ThVerify,
/// Displays the current configuration.
Show,
/// Updates the configuration. Requires 'UpdateData'.
Expand Down Expand Up @@ -113,14 +119,22 @@ pub struct UpdateData {
node_url: Option<String>,
}

/// KZGParams subcommand inputs
/// KZGParams subcommand input.
#[derive(Args, Debug)]
pub struct KZGParamsData {
/// Polynomial degree.
#[clap(long = "k")]
k: Option<String>,
}

/// ThresholdProof subcommand input.
#[derive(Args, Debug)]
pub struct ThProofData {
/// Peer.
#[clap(long = "peer")]
peer: Option<String>,
}

/// Bandada API action.
pub enum Action {
Add,
Expand Down Expand Up @@ -298,12 +312,12 @@ pub async fn handle_deploy(config: ClientConfig) -> Result<(), EigenError> {
Ok(())
}

/// Handles proving key generation.
/// Handles eigentrust circuit proving key generation.
pub fn handle_et_pk() -> Result<(), EigenError> {
let et_kzg_params = EigenFile::KzgParams(ET_PARAMS_K).load()?;
let proving_key = Client::generate_et_pk(et_kzg_params)?;

EigenFile::ProvingKey.save(proving_key)
EigenFile::ProvingKey(Circuit::EigenTrust).save(proving_key)
}

/// Handles the eigentrust proof generation command.
Expand All @@ -318,14 +332,14 @@ pub async fn handle_et_proof(config: ClientConfig) -> Result<(), EigenError> {
let attestations: Result<Vec<SignedAttestationRaw>, EigenError> =
att_storage.load()?.into_iter().map(|record| record.try_into()).collect();

let proving_key = EigenFile::ProvingKey.load()?;
let proving_key = EigenFile::ProvingKey(Circuit::EigenTrust).load()?;
let kzg_params = EigenFile::KzgParams(ET_PARAMS_K).load()?;

// Generate proof
let report = client.calculate_scores(attestations?, kzg_params, proving_key).await?;
let report = client.calculate_scores(attestations?, kzg_params, proving_key)?;

EigenFile::EtProof.save(report.proof)?;
EigenFile::ETPublicInputs.save(report.pub_inputs.to_bytes())?;
EigenFile::Proof(Circuit::EigenTrust).save(report.proof)?;
EigenFile::PublicInputs(Circuit::EigenTrust).save(report.pub_inputs.to_bytes())?;

Ok(())
}
Expand All @@ -337,12 +351,12 @@ pub async fn handle_et_verify(config: ClientConfig) -> Result<(), EigenError> {

// Load data
let kzg_params = EigenFile::KzgParams(ET_PARAMS_K).load()?;
let public_inputs = EigenFile::ETPublicInputs.load()?;
let proving_key = EigenFile::ProvingKey.load()?;
let proof = EigenFile::EtProof.load()?;
let public_inputs = EigenFile::PublicInputs(Circuit::EigenTrust).load()?;
let proving_key = EigenFile::ProvingKey(Circuit::EigenTrust).load()?;
let proof = EigenFile::Proof(Circuit::EigenTrust).load()?;

// Verify proof
client.verify(kzg_params, public_inputs, proving_key, proof).await?;
client.verify(kzg_params, public_inputs, proving_key, proof)?;

info!("EigenTrust proof has been verified.");
Ok(())
Expand Down Expand Up @@ -402,13 +416,12 @@ pub async fn handle_scores(
},
};

let proving_key = EigenFile::ProvingKey.load()?;
let proving_key = EigenFile::ProvingKey(Circuit::EigenTrust).load()?;
let kzg_params = EigenFile::KzgParams(ET_PARAMS_K).load()?;

// Calculate scores
let score_records: Vec<ScoreRecord> = client
.calculate_scores(attestations, kzg_params, proving_key)
.await?
.calculate_scores(attestations, kzg_params, proving_key)?
.scores
.into_iter()
.map(ScoreRecord::from_score)
Expand All @@ -429,6 +442,87 @@ pub async fn handle_scores(
Ok(())
}

/// Handles threshold circuit proving key generation.
pub async fn handle_th_pk(config: ClientConfig) -> Result<(), EigenError> {
let mnemonic = load_mnemonic();
let client = Client::new(config, mnemonic);

// Load KZG params
let et_kzg_params = EigenFile::KzgParams(ET_PARAMS_K).load()?;
let th_kzg_params = EigenFile::KzgParams(TH_PARAMS_K).load()?;

// Get attestations
let att_fp = get_file_path("attestations", FileType::Csv)?;
handle_attestations(client.get_config().clone()).await?;
let att_storage = CSVFileStorage::<AttestationRecord>::new(att_fp);
let attestations: Result<Vec<SignedAttestationRaw>, EigenError> =
att_storage.load()?.into_iter().map(|record| record.try_into()).collect();

let proving_key = client.generate_th_pk(attestations?, et_kzg_params, th_kzg_params)?;

EigenFile::ProvingKey(Circuit::Threshold).save(proving_key)
}

/// Handles threshold circuit proof generation.
pub async fn handle_th_proof(config: ClientConfig, data: ThProofData) -> Result<(), EigenError> {
let mnemonic = load_mnemonic();
let client = Client::new(config.clone(), mnemonic);

// Load KZG params and proving key
let et_kzg_params = EigenFile::KzgParams(ET_PARAMS_K).load()?;
let th_kzg_params = EigenFile::KzgParams(TH_PARAMS_K).load()?;
let proving_key = EigenFile::ProvingKey(Circuit::Threshold).load()?;

// Get attestations
let att_fp = get_file_path("attestations", FileType::Csv)?;
handle_attestations(client.get_config().clone()).await?;
let att_storage = CSVFileStorage::<AttestationRecord>::new(att_fp);
let attestations: Result<Vec<SignedAttestationRaw>, EigenError> =
att_storage.load()?.into_iter().map(|record| record.try_into()).collect();

// Parse peer id
let peer_id = match data.peer {
Some(peer) => peer.parse::<u32>().map_err(|e| EigenError::ParsingError(e.to_string()))?,
None => {
return Err(EigenError::ValidationError(
"Missing parameter 'peer': participant address.".to_string(),
))
},
};

let report = client.generate_th_proof(
attestations?,
et_kzg_params,
th_kzg_params,
proving_key,
config.band_th.parse().unwrap(),
peer_id,
)?;

EigenFile::Proof(Circuit::Threshold).save(report.proof)?;
EigenFile::PublicInputs(Circuit::Threshold).save(report.pub_inputs.to_bytes())?;

Ok(())
}

/// Handles the eigentrust proof verification command.
pub async fn handle_th_verify(config: ClientConfig) -> Result<(), EigenError> {
let mnemonic = load_mnemonic();
let client = Client::new(config, mnemonic);

// Load data
let kzg_params = EigenFile::KzgParams(TH_PARAMS_K).load()?;
let public_inputs = EigenFile::PublicInputs(Circuit::Threshold).load()?;
let proving_key = EigenFile::ProvingKey(Circuit::Threshold).load()?;
let proof = EigenFile::Proof(Circuit::Threshold).load()?;

// Verify proof
client.verify(kzg_params, public_inputs, proving_key, proof)?;

info!("Threshold proof has been verified.");
Ok(())
}

/// Handles the CLI project configuration update.
pub fn handle_update(config: &mut ClientConfig, data: UpdateData) -> Result<(), EigenError> {
if let Some(as_address) = data.as_address {
Expand Down
25 changes: 13 additions & 12 deletions eigentrust-cli/src/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use dotenv::{dotenv, var};
use eigentrust::{
circuit::Circuit,
error::EigenError,
storage::{BinFileStorage, JSONFileStorage, Storage},
ClientConfig,
Expand All @@ -15,12 +16,12 @@ use std::{env::current_dir, path::PathBuf};
const DEFAULT_MNEMONIC: &str = "test test test test test test test test test test test junk";
/// Library configuration file name.
pub const CONFIG_FILE: &str = "config";
/// EigenTrust generated proof file name.
pub const ET_PROOF_FILE: &str = "et-proof";
/// EigenTrust proving key file name.
pub const ET_PROVING_KEY_FILE: &str = "et-proving-key";
/// EigenTrust proof public inputs file name.
pub const ET_PUB_INP_FILE: &str = "et-public-inputs";
/// Proof file name.
pub const PROOF_FILE: &str = "proof";
/// Proving key file name.
pub const PROVING_KEY_FILE: &str = "proving-key";
/// Public inputs file name.
pub const PUB_INP_FILE: &str = "public-inputs";
/// KZG parameters file name.
pub const PARAMS_FILE: &str = "kzg-params";

Expand Down Expand Up @@ -48,9 +49,9 @@ impl FileType {
// Enum for different EigenTrust binary files
pub enum EigenFile {
KzgParams(u32),
ProvingKey,
EtProof,
ETPublicInputs,
ProvingKey(Circuit),
Proof(Circuit),
PublicInputs(Circuit),
}

impl EigenFile {
Expand All @@ -75,9 +76,9 @@ impl EigenFile {
fn filename(&self) -> String {
match self {
EigenFile::KzgParams(pol_degree) => format!("{}-{}", PARAMS_FILE, pol_degree),
EigenFile::ProvingKey => ET_PROVING_KEY_FILE.to_string(),
EigenFile::EtProof => ET_PROOF_FILE.to_string(),
EigenFile::ETPublicInputs => ET_PUB_INP_FILE.to_string(),
EigenFile::ProvingKey(circuit) => format!("{}-{}", circuit.as_str(), PROVING_KEY_FILE),
EigenFile::Proof(circuit) => format!("{}-{}", circuit.as_str(), PROOF_FILE),
EigenFile::PublicInputs(circuit) => format!("{}-{}", circuit.as_str(), PUB_INP_FILE),
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions eigentrust-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ async fn main() -> Result<(), EigenError> {
Mode::LocalScores => handle_scores(config, AttestationsOrigin::Local).await?,
Mode::Scores => handle_scores(config, AttestationsOrigin::Fetch).await?,
Mode::Show => info!("Client config:\n{:#?}", config),
Mode::ThProof(data) => handle_th_proof(config, data).await?,
Mode::ThProvingKey => handle_th_pk(config).await?,
Mode::ThVerify => handle_th_verify(config).await?,
Mode::Update(update_data) => handle_update(&mut config, update_data)?,
};

Expand Down
12 changes: 12 additions & 0 deletions eigentrust-zk/src/circuits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use crate::{
sponge::StatefulSpongeChipset,
FullRoundChip, PartialRoundChip, PoseidonChipset,
},
verifier::aggregator::native::NativeAggregator,
};
use halo2::halo2curves::{
bn256::{Bn256, Fr as Scalar},
Expand Down Expand Up @@ -54,6 +55,8 @@ pub const NUM_DECIMAL_LIMBS: usize = 2;
pub const POWER_OF_TEN: usize = 72;
/// Default polynomial degree for KZG parameters for EigenTrust circuit.
pub const ET_PARAMS_K: u32 = 20;
/// Default polynomial degree for KZG parameters for Threshold circuit.
pub const TH_PARAMS_K: u32 = 21;

/// Rational score
pub type RationalScore = BigRational;
Expand Down Expand Up @@ -94,6 +97,15 @@ pub type Opinion4 = Opinion<
PoseidonNativeHasher,
PoseidonNativeSponge,
>;
/// Native Aggregator for set with 4 participants
pub type NativeAggregator4 = NativeAggregator<
Bn256,
NUM_NEIGHBOURS,
NUM_BITS,
Bn256_4_68,
PoseidonNativeSponge,
Bn254Params,
>;

/// Native EigenTrust set with 4 participants
pub type NativeEigenTrust4 = NativeEigenTrustSet<
Expand Down
38 changes: 4 additions & 34 deletions eigentrust-zk/src/circuits/threshold/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -651,11 +651,10 @@ mod tests {
params::{
ecc::{bn254::Bn254Params, secp256k1::Secp256k1Params},
hasher::poseidon_bn254_5x5::Params,
rns::{bn256::Bn256_4_68, decompose_big_decimal, secp256k1::Secp256k1_4_68},
rns::{bn256::Bn256_4_68, secp256k1::Secp256k1_4_68},
},
utils::{big_to_fe, fe_to_big, generate_params, prove_and_verify},
utils::{big_to_fe, big_to_fe_rat, fe_to_big, generate_params, prove_and_verify},
verifier::aggregator::native::NativeAggregator,
FieldExt,
};
use halo2::{
arithmetic::Field,
Expand All @@ -666,7 +665,6 @@ mod tests {
secp256k1::Secp256k1Affine,
},
};
use num_bigint::BigInt;
use num_rational::BigRational;
use rand::thread_rng;

Expand Down Expand Up @@ -849,34 +847,6 @@ mod tests {
((pks_fr, s, s_ratios), (opt_att, opt_pks, public_inputs))
}

fn ratio_to_decomposed_helper<
F: FieldExt,
const NUM_DECIMAL_LIMBS: usize,
const POWER_OF_TEN: usize,
>(
ratio: BigRational,
) -> ([F; NUM_DECIMAL_LIMBS], [F; NUM_DECIMAL_LIMBS]) {
let num = ratio.numer();
let den = ratio.denom();
let max_len = NUM_DECIMAL_LIMBS * POWER_OF_TEN;
let bigger = num.max(den);
let dig_len = bigger.to_string().len();
let diff = max_len - dig_len;

let scale = BigInt::from(10_u32).pow(diff as u32);
let num_scaled = num * scale.clone();
let den_scaled = den * scale;
let num_scaled_uint = num_scaled.to_biguint().unwrap();
let den_scaled_uint = den_scaled.to_biguint().unwrap();

let num_decomposed =
decompose_big_decimal::<F, NUM_DECIMAL_LIMBS, POWER_OF_TEN>(num_scaled_uint);
let den_decomposed =
decompose_big_decimal::<F, NUM_DECIMAL_LIMBS, POWER_OF_TEN>(den_scaled_uint);

(num_decomposed, den_decomposed)
}

#[ignore = "threshold circuit test takes too long to run"]
#[test]
fn test_threshold_circuit() {
Expand Down Expand Up @@ -907,7 +877,7 @@ mod tests {
let score = scores[target_idx].clone();
let score_ratio = score_ratios[target_idx].clone();
let (num_decomposed, den_decomposed) =
ratio_to_decomposed_helper::<N, NUM_DECIMAL_LIMBS, POWER_OF_TEN>(score_ratio.clone());
big_to_fe_rat::<N, NUM_DECIMAL_LIMBS, POWER_OF_TEN>(score_ratio.clone());
let threshold = N::from_u128(1000_u128);

let native_threshold: Threshold<
Expand Down Expand Up @@ -1008,7 +978,7 @@ mod tests {
let score = scores[target_idx].clone();
let score_ratio = score_ratios[target_idx].clone();
let (num_decomposed, den_decomposed) =
ratio_to_decomposed_helper::<N, NUM_DECIMAL_LIMBS, POWER_OF_TEN>(score_ratio.clone());
big_to_fe_rat::<N, NUM_DECIMAL_LIMBS, POWER_OF_TEN>(score_ratio.clone());
let threshold = N::from_u128(1000_u128);

let native_threshold: Threshold<
Expand Down
Loading

0 comments on commit 31d1a0c

Please sign in to comment.