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

feat(lib): threshold circuit integration #361

Merged
merged 8 commits into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

# Assets
/eigentrust-cli/assets/attestation_station.rs
/eigentrust-cli/assets/et-proving-key.bin
/eigentrust-cli/assets/et-public-inputs.bin
/eigentrust-cli/assets/et-proof.bin
/eigentrust-cli/assets/*-proving-key.bin
/eigentrust-cli/assets/*-public-inputs.bin
/eigentrust-cli/assets/*-proof.bin
/eigentrust-cli/assets/kzg-params-*.bin
158 changes: 125 additions & 33 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 Threshold 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,34 +312,28 @@ 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.
pub async fn handle_et_proof(config: ClientConfig) -> Result<(), EigenError> {
let mnemonic = load_mnemonic();
let client = Client::new(config, mnemonic);
let attestations = load_or_fetch_attestations(client.get_config().clone()).await?;

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 = 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.generate_et_proof(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 +345,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 All @@ -369,7 +377,6 @@ pub async fn handle_scores(
) -> Result<(), EigenError> {
let mnemonic = load_mnemonic();
let client = Client::new(config, mnemonic);

let att_fp = get_file_path("attestations", FileType::Csv)?;

// Get or Fetch attestations
Expand Down Expand Up @@ -402,23 +409,13 @@ pub async fn handle_scores(
},
};

let proving_key = EigenFile::ProvingKey.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?
.scores
.into_iter()
.map(ScoreRecord::from_score)
.collect();

let scores_fp = get_file_path("scores", FileType::Csv)?;
let score_records: Vec<ScoreRecord> =
client.calculate_scores(attestations)?.into_iter().map(ScoreRecord::from_score).collect();

// Save scores
let scores_fp = get_file_path("scores", FileType::Csv)?;
let mut records_storage = CSVFileStorage::<ScoreRecord>::new(scores_fp);

records_storage.save(score_records)?;

info!(
Expand All @@ -429,6 +426,75 @@ 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);
let attestations = load_or_fetch_attestations(client.get_config().clone()).await?;

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

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);
let attestations = load_or_fetch_attestations(client.get_config().clone()).await?;

// 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()?;

// 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 Expand Up @@ -474,6 +540,32 @@ pub fn handle_update(config: &mut ClientConfig, data: UpdateData) -> Result<(),
json_storage.save(config.clone())
}

/// Tries to load attestations from local storage. If no attestations are found,
/// it fetches them from the AS contract.
pub async fn load_or_fetch_attestations(
config: ClientConfig,
) -> Result<Vec<SignedAttestationRaw>, EigenError> {
let att_file_path = get_file_path("attestations", FileType::Csv)?;
let att_storage = CSVFileStorage::<AttestationRecord>::new(att_file_path.clone());

match att_storage.load() {
Ok(local_records) => {
if !local_records.is_empty() {
return local_records.into_iter().map(|record| record.try_into()).collect();
}
debug!("No local attestations found. Fetching from AS contract.");
},
Err(_) => {
debug!("Error loading local attestations. Fetching from AS contract.");
},
}

let client = Client::new(config, load_mnemonic());
handle_attestations(client.get_config().clone()).await?;

att_storage.load()?.into_iter().map(|record| record.try_into()).collect()
}

#[cfg(test)]
mod tests {
use crate::cli::{AttestData, Cli};
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
2 changes: 1 addition & 1 deletion eigentrust-zk/src/circuits/dynamic_sets/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ where
unassigned_msg_hashes.push(msg_hashes_row);
unassigned_s_invs.push(s_inv_row);

let pk = pks[i].clone().unwrap();
let pk = pks[i].clone().unwrap_or(PublicKey::default());
let unassigned_pk = UnassignedPublicKey::new(pk);
unassigned_pks.push(unassigned_pk);
}
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
Loading
Loading