├── scripts ├── clippy.sh ├── build.sh ├── diff.sh ├── test.sh ├── fmt-check.sh ├── build-docs.sh └── init.sh ├── eigentrust-zk ├── .gitignore ├── src │ ├── params │ │ ├── mod.rs │ │ ├── ecc │ │ │ ├── mod.rs │ │ │ ├── bn254.rs │ │ │ └── secp256k1.rs │ │ ├── rns │ │ │ ├── bn256.rs │ │ │ └── secp256k1.rs │ │ └── hasher │ │ │ ├── mod.rs │ │ │ └── rescue_prime_bn254_5x5.rs │ ├── gadgets │ │ ├── mod.rs │ │ ├── absorb.rs │ │ ├── bits2integer.rs │ │ ├── lt_eq.rs │ │ └── bits2num.rs │ ├── rescue_prime │ │ ├── native │ │ │ ├── sponge.rs │ │ │ └── mod.rs │ │ └── sponge.rs │ ├── poseidon │ │ └── native │ │ │ ├── sponge.rs │ │ │ └── mod.rs │ ├── circuits │ │ ├── opinion │ │ │ └── native.rs │ │ └── mod.rs │ ├── ecc │ │ └── mod.rs │ ├── merkle_tree │ │ └── native.rs │ ├── edwards │ │ ├── params.rs │ │ └── native.rs │ ├── eddsa │ │ └── native.rs │ ├── verifier │ │ └── mod.rs │ └── lib.rs ├── Cargo.toml └── README.md ├── .env.origin ├── Cargo.toml ├── rust-toolchain.toml ├── .gitignore ├── docs ├── SUMMARY.md ├── README.md ├── 0_attestation_station.md ├── 2_data_processing.md ├── 5_beyond.md ├── 1_attestations.md ├── 3_dynamic_sets.md └── 4_algorithm.md ├── eigentrust-cli ├── assets │ ├── scores.csv │ ├── config.json │ └── attestations.csv ├── Cargo.toml ├── src │ ├── bandada.rs │ ├── main.rs │ └── fs.rs └── README.md ├── eigentrust ├── Cargo.toml └── src │ ├── error.rs │ └── eth.rs ├── rustfmt.toml ├── LICENSE ├── .github └── workflows │ ├── test.yml │ └── coverage.yml └── README.md /scripts/clippy.sh: -------------------------------------------------------------------------------- 1 | cargo clippy 2 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | cargo build --release 2 | -------------------------------------------------------------------------------- /scripts/diff.sh: -------------------------------------------------------------------------------- 1 | git diff --exit-code 2 | -------------------------------------------------------------------------------- /scripts/test.sh: -------------------------------------------------------------------------------- 1 | cargo test --all --release -------------------------------------------------------------------------------- /eigentrust-zk/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock -------------------------------------------------------------------------------- /scripts/fmt-check.sh: -------------------------------------------------------------------------------- 1 | cargo fmt --all -- --check 2 | -------------------------------------------------------------------------------- /.env.origin: -------------------------------------------------------------------------------- 1 | MNEMONIC= 2 | BANDADA_API_KEY= 3 | LOG_LEVEL= 4 | -------------------------------------------------------------------------------- /scripts/build-docs.sh: -------------------------------------------------------------------------------- 1 | cargo doc -p eigentrust --no-deps 2 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "eigentrust", 4 | "eigentrust-cli", 5 | "eigentrust-zk", 6 | ] 7 | default-members = ["eigentrust-cli"] 8 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "1.70.0" 3 | components = ["rustfmt", "clippy", "rls", "rust-analysis"] 4 | targets = ["aarch64-apple-darwin", "x86_64-unknown-linux-gnu"] 5 | -------------------------------------------------------------------------------- /eigentrust-zk/src/params/mod.rs: -------------------------------------------------------------------------------- 1 | /// Ecc params 2 | pub mod ecc; 3 | /// Params and constants for hashing functions 4 | pub mod hasher; 5 | /// Rns params for integer operations 6 | pub mod rns; 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Local 2 | .DS_Store 3 | .env 4 | 5 | # Build 6 | /target 7 | 8 | # Assets 9 | /eigentrust-cli/assets/attestation_station.rs 10 | /eigentrust-cli/assets/*-proving-key.bin 11 | /eigentrust-cli/assets/*-public-inputs.bin 12 | /eigentrust-cli/assets/*-proof.bin 13 | /eigentrust-cli/assets/kzg-params-*.bin 14 | -------------------------------------------------------------------------------- /docs/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | * [EigenTrust](README.md) 4 | * [Attestation Station](0_attestation_station.md) 5 | * [Attestation](1_attestations.md) 6 | * [Data Processing](2_data_processing.md) 7 | * [Dynamic Sets](3_dynamic_sets.md) 8 | * [The Algorithm](4_algorithm.md) 9 | * [Future Ideas](5_beyond.md) 10 | -------------------------------------------------------------------------------- /eigentrust-cli/assets/scores.csv: -------------------------------------------------------------------------------- 1 | peer_address,score_fr,numerator,denominator,score 2 | 0x70997970c51812dc3a010c7d01b50e0d17dc79c8,0x00000000000000000000000000000000000000000000000000000000000003e8,1000,1,1000 3 | 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266,0x00000000000000000000000000000000000000000000000000000000000003e8,1000,1,1000 4 | -------------------------------------------------------------------------------- /scripts/init.sh: -------------------------------------------------------------------------------- 1 | if [[ $(cargo --version) ]]; then 2 | echo "Found cargo" 3 | else 4 | curl https://sh.rustup.rs -sSf | sh -s -- -y 5 | source $HOME/.cargo/env 6 | export PATH=$HOME/.cargo/bin:$PATH 7 | fi 8 | 9 | sudo add-apt-repository ppa:ethereum/ethereum 10 | sudo apt-get update 11 | cargo install svm-rs && svm install 0.8.17 -------------------------------------------------------------------------------- /eigentrust-cli/assets/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "as_address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", 3 | "band_id": "51629751621128677209874422363557", 4 | "band_th": "500", 5 | "band_url": "http://localhost:3000", 6 | "chain_id": "31337", 7 | "domain": "0x0000000000000000000000000000000000000000", 8 | "node_url": "http://localhost:8545" 9 | } 10 | -------------------------------------------------------------------------------- /eigentrust-cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "eigentrust-cli" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | clap = { version = "4.2.7", features = ["derive"] } 8 | dotenv = "0.15.0" 9 | env_logger = "0.10.0" 10 | ethers = "2.0.8" 11 | log = "0.4.19" 12 | reqwest = "0.11.18" 13 | serde = { version = "1.0", features = ["derive"] } 14 | serde_json = "1.0" 15 | tokio = "1.18" 16 | 17 | # Path dependencies 18 | eigentrust = { path = "../eigentrust" } 19 | -------------------------------------------------------------------------------- /eigentrust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "eigentrust" 3 | version = "0.1.0" 4 | edition = "2021" 5 | description = "A library for managing trust in a distributed network with zero-knowledge features." 6 | repository = "https://github.com/eigen-trust/protocol" 7 | license = "MIT" 8 | 9 | [dependencies] 10 | csv = "1.1" 11 | ethers = "2.0.8" 12 | log = "0.4.19" 13 | rand = "0.8" 14 | num-rational = "0.4.1" 15 | serde = { version = "1.0", features = ["derive"] } 16 | serde_json = "1.0" 17 | thiserror = "1.0.43" 18 | tokio = { version = "1.18", features = ["time", "macros", "rt-multi-thread", "net"] } 19 | 20 | # Path dependencies 21 | eigentrust-zk = { path = "../eigentrust-zk" } 22 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | # Rustfmt configuration file 2 | # https://rust-lang.github.io/rustfmt/ 3 | 4 | # Stable 5 | array_width = 90 6 | attr_fn_like_width = 90 7 | chain_width = 90 8 | fn_params_layout = "Compressed" 9 | hard_tabs = true 10 | match_block_trailing_comma = true 11 | short_array_element_width_threshold = 40 12 | single_line_if_else_max_width = 80 13 | struct_lit_width = 80 14 | struct_variant_width = 80 15 | use_field_init_shorthand = true 16 | 17 | # Unstable 18 | # condense_wildcard_suffixes = true 19 | # format_code_in_doc_comments = true 20 | # format_strings = true 21 | # imports_granularity = "Crate" 22 | # overflow_delimited_expr = true 23 | # reorder_impl_items = true 24 | # wrap_comments = true 25 | -------------------------------------------------------------------------------- /eigentrust-zk/src/gadgets/mod.rs: -------------------------------------------------------------------------------- 1 | /// Absorb the rescue-poseidon hash sponge 2 | pub mod absorb; 3 | /// Convert integer to bits 4 | pub mod bits2integer; 5 | /// Convert number to bits 6 | pub mod bits2num; 7 | /// Check if a number is less than or equal to a value 8 | pub mod lt_eq; 9 | /// Common gadget for the optimization 10 | pub mod main; 11 | /// Set membership gadget 12 | pub mod set; 13 | 14 | /* NOTE: 15 | Following chipsets are not used atm because of column limits. 16 | Currently, the maximum columns for EigenTrust circuit is 8 advice + 5 fixed. 17 | We plan to use them when lookup table can be used in circuit. 18 | */ 19 | /// Check if a number is less than or equal to a value (using lookup table) 20 | pub mod lt_eq_lookup; 21 | /// Range check gadget (using lookup table) 22 | pub mod range; 23 | -------------------------------------------------------------------------------- /eigentrust-zk/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "eigentrust-zk" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | plotters = { version = "0.3.0" } 10 | rand = "0.8" 11 | hex = { version = "0.4", default-features = false, features = ["alloc"] } 12 | halo2 = { package = "halo2_proofs", git = "https://github.com/privacy-scaling-explorations/halo2", tag = "v2023_04_20" } 13 | blake = "2.0.2" 14 | itertools = "0.10.3" 15 | num-bigint = { version = "0.4.0", features = ["rand"] } 16 | num-integer = "0.1.42" 17 | num-traits = "0.2.11" 18 | num-rational = "0.4.1" 19 | serde = { version = "1.0", features = ["derive"] } 20 | serde_json = "1.0" 21 | sha3 = "0.10.8" 22 | 23 | [dependencies.snark-verifier] 24 | git = "https://github.com/privacy-scaling-explorations/snark-verifier" 25 | rev = "e5d5e4a" 26 | default-features = false 27 | features = ["loader_evm", "system_halo2"] 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /eigentrust-zk/src/params/ecc/mod.rs: -------------------------------------------------------------------------------- 1 | /// Params for bn254 2 | pub mod bn254; 3 | /// Params for secp256k1 4 | pub mod secp256k1; 5 | 6 | use crate::utils::big_to_fe; 7 | use crate::FieldExt; 8 | use halo2::halo2curves::ff::PrimeField; 9 | use halo2::halo2curves::group::Curve; 10 | use halo2::halo2curves::CurveAffine; 11 | use num_bigint::BigUint; 12 | use num_traits::One; 13 | use std::fmt::Debug; 14 | 15 | /// Params for Ecc operations 16 | pub trait EccParams: Clone + Default + Debug + PartialEq { 17 | /// Sliding window size 18 | fn window_size() -> u32; 19 | 20 | /// Aux init point 21 | fn aux_init() -> C; 22 | 23 | /// Make aux_fin when sliding window is > 1. 24 | fn make_mul_aux(aux_to_add: C, window_size: u32) -> C 25 | where 26 | C::Scalar: FieldExt, 27 | { 28 | assert!(C::Scalar::NUM_BITS % window_size == 0); 29 | assert!(window_size > 0); 30 | 31 | let n = C::Scalar::NUM_BITS; 32 | let number_of_selectors = n / window_size; 33 | let mut k0 = BigUint::one(); 34 | let one = BigUint::one(); 35 | for i in 0..number_of_selectors { 36 | k0 |= &one << (i * window_size); 37 | } 38 | 39 | (-aux_to_add * big_to_fe::(k0)).to_affine() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /eigentrust-zk/src/params/ecc/bn254.rs: -------------------------------------------------------------------------------- 1 | use halo2::halo2curves::{ 2 | bn256::{Fq, G1Affine}, 3 | CurveAffine, 4 | }; 5 | 6 | use super::EccParams; 7 | 8 | #[derive(Clone, Debug, Default, PartialEq)] 9 | /// Params for Bn254 curve 10 | pub struct Bn254Params; 11 | 12 | impl EccParams for Bn254Params { 13 | fn window_size() -> u32 { 14 | 2 15 | } 16 | 17 | fn aux_init() -> G1Affine { 18 | let to_add_x = Fq::from_raw([ 19 | 0xc31ec539373ca785, 0x9da68395fc2377e1, 0x125da415992c10c3, 0x95a8a5d788e033e, 20 | ]); 21 | let to_add_y = Fq::from_raw([ 22 | 0x48331c1ae1c20e12, 0xd8e08c497c6f41c2, 0xbd3fa8607e7558fb, 0x10d670a5ac441899, 23 | ]); 24 | G1Affine::from_xy(to_add_x, to_add_y).unwrap() 25 | } 26 | } 27 | 28 | #[cfg(test)] 29 | mod test { 30 | use halo2::{ 31 | arithmetic::Field, 32 | halo2curves::{ 33 | bn256::{Fq, Fr, G1Affine}, 34 | group::Curve, 35 | }, 36 | }; 37 | 38 | use crate::{params::rns::decompose_big, utils::fe_to_big}; 39 | 40 | #[test] 41 | fn generate_bn254_aux() { 42 | use rand::rngs::OsRng; 43 | let random_scalar = Fr::random(OsRng); 44 | let g = G1Affine::generator(); 45 | let to_add = (g * random_scalar).to_affine(); 46 | let x_big = fe_to_big(to_add.x); 47 | let y_big = fe_to_big(to_add.y); 48 | let x_limbs = decompose_big::(x_big); 49 | let y_limbs = decompose_big::(y_big); 50 | println!("{:?}", x_limbs); 51 | println!("{:?}", y_limbs); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /eigentrust-zk/src/params/ecc/secp256k1.rs: -------------------------------------------------------------------------------- 1 | use super::EccParams; 2 | use halo2::halo2curves::secp256k1::{Fp, Secp256k1Affine}; 3 | use halo2::halo2curves::CurveAffine; 4 | 5 | #[derive(Clone, Debug, Default, PartialEq)] 6 | /// Params for Secp256k1 curve 7 | pub struct Secp256k1Params; 8 | 9 | impl EccParams for Secp256k1Params { 10 | fn window_size() -> u32 { 11 | 4 12 | } 13 | 14 | fn aux_init() -> Secp256k1Affine { 15 | let to_add_x = Fp::from_raw([ 16 | 0xad467b63916e17d3, 0x12498a1eac60a622, 0x9b68199adf3ffe7b, 0xdd882e3e36427390, 17 | ]); 18 | let to_add_y = Fp::from_raw([ 19 | 0x12aeff734725fdec, 0x45a315ac5e816919, 0x11251eb4ee816550, 0x77783c268dbe2977, 20 | ]); 21 | Secp256k1Affine::from_xy(to_add_x, to_add_y).unwrap() 22 | } 23 | } 24 | 25 | #[cfg(test)] 26 | mod test { 27 | use crate::{params::rns::decompose_big, utils::fe_to_big}; 28 | use halo2::halo2curves::secp256k1::{Fp, Fq, Secp256k1}; 29 | use halo2::{arithmetic::Field, halo2curves::group::Curve}; 30 | 31 | #[test] 32 | fn generate_secp256k1_aux() { 33 | use rand::rngs::OsRng; 34 | let random_scalar = Fq::random(OsRng); 35 | let g = Secp256k1::generator(); 36 | let to_add = (g * random_scalar).to_affine(); 37 | let x_big = fe_to_big(to_add.x); 38 | let y_big = fe_to_big(to_add.y); 39 | let x_limbs = decompose_big::(x_big); 40 | let y_limbs = decompose_big::(y_big); 41 | println!("{:?}", x_limbs); 42 | println!("{:?}", y_limbs); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Build & Test Circuits, Server, Client 2 | 3 | # Controls when the action will run. 4 | on: 5 | pull_request: 6 | branches: 7 | - "**" # Pull request targeting any branch 8 | push: 9 | branches: 10 | - "master" # Push to master branch 11 | 12 | # Allows you to run this workflow manually from the Actions tab 13 | workflow_dispatch: 14 | 15 | env: 16 | CARGO_TERM_COLOR: always 17 | 18 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 19 | jobs: 20 | build: 21 | # The type of runner that the job will run on 22 | runs-on: ubuntu-latest 23 | 24 | # Steps represent a sequence of tasks that will be executed as part of the job 25 | steps: 26 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 27 | - uses: actions/checkout@v2 28 | 29 | - name: Rust Cache 30 | uses: Swatinem/rust-cache@v1.3.0 31 | 32 | - name: Set-Up 33 | run: ./scripts/init.sh 34 | 35 | - name: Install Foundry 36 | uses: foundry-rs/foundry-toolchain@v1 37 | 38 | - name: Fmt Check 39 | run: ./scripts/fmt-check.sh 40 | 41 | - name: Clippy Check 42 | run: ./scripts/clippy.sh 43 | 44 | - name: Build 45 | run: ./scripts/build.sh && ./scripts/diff.sh 46 | 47 | - uses: taiki-e/install-action@nextest 48 | 49 | - name: Test 50 | uses: actions-rs/cargo@v1 51 | with: 52 | command: nextest 53 | args: run --all --release -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: Coverage 2 | 3 | # Controls when the action will run. 4 | on: 5 | pull_request: 6 | branches: 7 | - "**" # Pull request targeting any branch 8 | push: 9 | branches: 10 | - "master" # Push to master branch 11 | 12 | # Allows you to run this workflow manually from the Actions tab 13 | workflow_dispatch: 14 | 15 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 16 | jobs: 17 | coverage: 18 | name: coverage 19 | runs-on: ubuntu-latest 20 | 21 | steps: 22 | - name: Checkout repository 23 | uses: actions/checkout@v2 24 | 25 | - name: Rust Cache 26 | uses: Swatinem/rust-cache@v1.3.0 27 | 28 | - name: Install Rust 29 | run: ./scripts/init.sh 30 | 31 | - name: Install Rustup component 32 | run: rustup component add llvm-tools-preview 33 | 34 | - name: Install grcov 35 | run: cargo install grcov 36 | 37 | - name: Run Tests 38 | run: cargo test -p eigen-trust-server --verbose -- --nocapture 39 | env: 40 | RUSTFLAGS: "-Zinstrument-coverage" 41 | LLVM_PROFILE_FILE: "coverage-%p-%m.profraw" 42 | 43 | - name: Generate Coverage data 44 | run: grcov . -s . --binary-path ./target/debug/ --ignore-not-existing --ignore **/lib.rs -t lcov -o cov.info 45 | 46 | - uses: codecov/codecov-action@v3 47 | with: 48 | files: ./cov.info 49 | name: codecov-umbrella 50 | fail_ci_if_error: true 51 | verbose: true -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: EigenTrust protocol 3 | --- 4 | 5 | # EigenTrust 6 | 7 | EigenTrust is a reputation-based trust management algorithm that evaluates the trustworthiness of peers in peer-to-peer networks. The algorithm calculates a reputation score for each peer based on feedback from other peers in the network. 8 | 9 | The algorithm is based on an opinion matrix that describes the distribution of the reputation owned by the peers in the network. The opinion matrix is used to calculate new scores for each peer, and the process is repeated until the scores converge. 10 | 11 | EigenTrust uses a recursive algorithm that involves updating the scores for each peer based on the scores of the other peers in the network. The algorithm is designed to be resilient against collusion and free-riding, two common problems in peer-to-peer networks. 12 | 13 | The EigenTrust algorithm can be used in a variety of decentralized decision-making applications, such as decentralized autonomous organizations (DAOs), prediction markets, and reputation systems. It can also be used in peer-to-peer networks to facilitate content discovery and recommendation. 14 | 15 | **Main characteristics:** 16 | - Self-policing - the shared ethics of the user groups is defined and enforced by the peers themselves and not by some central authority. 17 | 18 | - Incorruptible - Reputation should be obtained by consistent good behavior through several transactions. It is resistant to sybil attack through mechanisms of bootstrapping or sybil rank filtering algorithm. 19 | 20 | - Permissonless - New peers can join the system without the permisson of central autorithy, given that they satisfy certain critirea. 21 | -------------------------------------------------------------------------------- /eigentrust-cli/src/bandada.rs: -------------------------------------------------------------------------------- 1 | //! # Bandada API module. 2 | //! 3 | //! Bandada API handling module. 4 | 5 | use dotenv::{dotenv, var}; 6 | use eigentrust::error::EigenError; 7 | use reqwest::header::{HeaderMap, HeaderValue, CONTENT_TYPE}; 8 | use reqwest::{Client, Response}; 9 | 10 | /// Bandada API client. 11 | pub struct BandadaApi { 12 | base_url: String, 13 | client: Client, 14 | key: String, 15 | } 16 | 17 | impl BandadaApi { 18 | /// Creates a new `BandadaApi`. 19 | pub fn new(base_url: &str) -> Result { 20 | dotenv().ok(); 21 | let key = 22 | var("BANDADA_API_KEY").map_err(|e| EigenError::ConfigurationError(e.to_string()))?; 23 | 24 | Ok(Self { base_url: base_url.to_string(), client: Client::new(), key }) 25 | } 26 | 27 | /// Adds Member. 28 | pub async fn add_member( 29 | &self, group_id: &str, identity_commitment: &str, 30 | ) -> Result { 31 | let mut headers = HeaderMap::new(); 32 | headers.insert("X-API-KEY", HeaderValue::from_str(&self.key).unwrap()); 33 | headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json")); 34 | 35 | self.client 36 | .post(&format!( 37 | "{}/groups/{}/members/{}", 38 | self.base_url, group_id, identity_commitment 39 | )) 40 | .headers(headers) 41 | .send() 42 | .await 43 | .map_err(|e| EigenError::RequestError(e.to_string())) 44 | } 45 | 46 | /// Removes Member. 47 | pub async fn remove_member( 48 | &self, group_id: &str, member_id: &str, 49 | ) -> Result { 50 | let mut headers = HeaderMap::new(); 51 | headers.insert("X-API-KEY", HeaderValue::from_str(&self.key).unwrap()); 52 | 53 | self.client 54 | .delete(&format!( 55 | "{}/groups/{}/members/{}", 56 | self.base_url, group_id, member_id 57 | )) 58 | .headers(headers) 59 | .send() 60 | .await 61 | .map_err(|e| EigenError::RequestError(e.to_string())) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /docs/0_attestation_station.md: -------------------------------------------------------------------------------- 1 | --- 2 | This page describes the AttestationStation smart contract and how its used within the EigenTrust protocol context. 3 | --- 4 | 5 | # AttestationStation 6 | 7 | The AttestationStation smart contract is a key component of the EigenTrust protocol, which is designed to enable trust among peers in decentralized networks. Attestations, also known as opinions or ratings, are an important part of the protocol as they allow peers to express their trust or distrust of other peers. The AttestationStation contract serves as a repository for attestations submitted by peers in the network. 8 | 9 | Examples (Let's assume we are doing ratings from 0-5): 10 | - Alice attests Bob with a rating of 5 11 | - Alice attests to Carol with a rating of 2 12 | - Bob attests to Alice with a rating of 3 13 | - Carol says Bob with a rating of 4 14 | - Alice attests Bob with a rating of 1 15 | 16 | The mapping attestations is a 3-dimensional mapping that stores attestations submitted by peers. The first key is the address of the peer submitting the attestation. The second key is the address of the peer being attested to. The third key is a bytes32 hash representing the transaction or interaction between the peers. The value stored in the mapping is a bytes array representing the attestation data. 17 | ```solidity 18 | mapping(address => mapping(address => mapping(bytes32 => bytes))) public attestations; 19 | ``` 20 | 21 | The AttestationData struct represents an attestation submitted by a peer. The `about` field is the address of the peer being attested to. The `key` field is a bytes32 hash representing the transaction or interaction between the peers. The `val` field is a bytes array representing the attestation data. The structure of the `val` field is described in more detail in the [Attestations](../1_attestations.md) documentation. 22 | ```solidity 23 | struct AttestationData { 24 | address about; 25 | bytes32 key; 26 | bytes val; 27 | } 28 | ``` 29 | -------------------------------------------------------------------------------- /eigentrust-cli/src/main.rs: -------------------------------------------------------------------------------- 1 | //! # ZK Eigentrust CLI 2 | //! 3 | //! This crate provides a CLI interface to use the `eigentrust` library. 4 | 5 | #![warn(trivial_casts)] 6 | #![deny( 7 | absolute_paths_not_starting_with_crate, deprecated, future_incompatible, missing_docs, 8 | nonstandard_style, unreachable_code, unreachable_patterns 9 | )] 10 | #![forbid(unsafe_code)] 11 | #![deny( 12 | // Complexity 13 | clippy::unnecessary_cast, 14 | clippy::needless_question_mark, 15 | // Pedantic 16 | clippy::cast_lossless, 17 | clippy::cast_possible_wrap, 18 | // Perf 19 | clippy::redundant_clone, 20 | // Restriction 21 | clippy::panic, 22 | // Style 23 | clippy::let_and_return, 24 | clippy::needless_borrow 25 | )] 26 | 27 | mod bandada; 28 | mod cli; 29 | mod fs; 30 | 31 | use clap::Parser; 32 | use cli::*; 33 | use dotenv::dotenv; 34 | use eigentrust::error::EigenError; 35 | use env_logger::{init_from_env, Env}; 36 | use fs::load_config; 37 | use log::info; 38 | 39 | #[tokio::main] 40 | async fn main() -> Result<(), EigenError> { 41 | dotenv().ok(); 42 | init_from_env(Env::default().filter_or("LOG_LEVEL", "info")); 43 | 44 | match Cli::parse().mode { 45 | Mode::Attest(attest_data) => handle_attest(attest_data).await?, 46 | Mode::Attestations => handle_attestations().await?, 47 | Mode::Bandada(bandada_data) => handle_bandada(bandada_data).await?, 48 | Mode::Deploy => handle_deploy().await?, 49 | Mode::ETProof => handle_et_proof().await?, 50 | Mode::ETProvingKey => handle_et_pk().await?, 51 | Mode::ETVerify => handle_et_verify().await?, 52 | Mode::KZGParams(kzg_params_data) => handle_params(kzg_params_data)?, 53 | Mode::LocalScores => handle_scores(AttestationsOrigin::Local).await?, 54 | Mode::Scores => handle_scores(AttestationsOrigin::Fetch).await?, 55 | Mode::Show => info!("Client config:\n{:#?}", load_config()?), 56 | Mode::ThProof(th_proof_data) => handle_th_proof(th_proof_data).await?, 57 | Mode::ThProvingKey => handle_th_pk().await?, 58 | Mode::ThVerify => handle_th_verify().await?, 59 | Mode::Update(update_data) => handle_update(update_data)?, 60 | }; 61 | 62 | Ok(()) 63 | } 64 | -------------------------------------------------------------------------------- /eigentrust/src/error.rs: -------------------------------------------------------------------------------- 1 | //! # Error Module. 2 | //! 3 | //! This module features the `EigenError` enum for error handling throughout the project. 4 | 5 | use thiserror::Error; 6 | 7 | /// The crate-wide error variants. 8 | #[derive(Debug, Error)] 9 | pub enum EigenError { 10 | /// Attestation error 11 | #[error("AttestationError: {0}")] 12 | AttestationError(String), 13 | 14 | /// Configuration error 15 | #[error("ConfigurationError: {0}")] 16 | ConfigurationError(String), 17 | 18 | /// Connection error 19 | #[error("ConnectionError: {0}")] 20 | ConnectionError(String), 21 | 22 | /// Contract compilation error 23 | #[error("ContractError: {0}")] 24 | ContractError(String), 25 | 26 | /// Conversion error 27 | #[error("ConversionError: {0}")] 28 | ConversionError(String), 29 | 30 | /// File read/write error 31 | #[error("FileIOError: {0}")] 32 | FileIOError(String), 33 | 34 | /// Input/output error 35 | #[error("IOError: {0}")] 36 | IOError(std::io::Error), 37 | 38 | /// Keys Error 39 | #[error("KeysError: {0}")] 40 | KeysError(String), 41 | 42 | /// Network error 43 | #[error("NetworkError: {0}")] 44 | NetworkError(String), 45 | 46 | /// Parsing error 47 | #[error("ParsingError: {0}")] 48 | ParsingError(String), 49 | 50 | /// Proving error 51 | #[error("ProvingError: {0}")] 52 | ProvingError(String), 53 | 54 | /// Read/Write error 55 | #[error("ReadWriteError: {0}")] 56 | ReadWriteError(String), 57 | 58 | /// Recovery error 59 | #[error("RecoveryError: {0}")] 60 | RecoveryError(String), 61 | 62 | /// Request error 63 | #[error("RequestError: {0}")] 64 | RequestError(String), 65 | 66 | /// Resource unavailable error 67 | #[error("ResourceUnavailableError: {0}")] 68 | ResourceUnavailableError(String), 69 | 70 | /// Transaction error 71 | #[error("TransactionError: {0}")] 72 | TransactionError(String), 73 | 74 | /// Unknown error 75 | #[error("UnknownError: {0}")] 76 | UnknownError(String), 77 | 78 | /// Validation error 79 | #[error("ValidationError: {0}")] 80 | ValidationError(String), 81 | 82 | /// Verification error 83 | #[error("VerificationError: {0}")] 84 | VerificationError(String), 85 | 86 | /// Failed generating proving/verifying keys 87 | #[error("KeygenError: {0}")] 88 | KeygenError(String), 89 | } 90 | -------------------------------------------------------------------------------- /docs/2_data_processing.md: -------------------------------------------------------------------------------- 1 | --- 2 | This page provides an overview of how data is processed after being pulled from AttestationStation. The data stored in AttestationStation is submitted from one user to another, but the EigenTrust algorithm processes opinions from one user to the whole group. 3 | --- 4 | 5 | To start, a group needs to be defined. This is typically done by assigning a group ID and creating a list of peers, as shown below: 6 | ``` 7 | group_id = 1377 8 | group = [peer1, peer2, peer3, peer4, peer5] 9 | ``` 10 | 11 | 12 | And lets assume we have some attestations in AS already: 13 | ``` 14 | peer1 => peer2 => 1377 => 5 15 | peer2 => peer3 => 1377 => 7 16 | peer4 => peer2 => 1377 => 3 17 | ``` 18 | 19 | Once the group is defined, the next step is to search AttestationStation to construct an opinion map. This is done by iterating through each peer in the group and searching for relevant attestations. The pseudo code snippet below demonstrates this process: 20 | ```rust 21 | for i in 0..group.len() { 22 | let peer_i = group[i]; 23 | if peer_i == null { 24 | continue; 25 | } 26 | for j in 0..group.len() { 27 | let peer_j = group[j]; 28 | 29 | let is_null = peer_j == null; 30 | let is_self = peer_i == peer_j; 31 | if is_null || is_self { 32 | continue; 33 | } 34 | 35 | let att = AS.attestations(peer_i, peer_j, group_id); 36 | op_map[peer_i][j] = (peer_j, att); 37 | } 38 | } 39 | ``` 40 | 41 | In this code, `peer_i` and `peer_j` represent two peers in the group, and `AS.attestations(peer_i, peer_j, group_id)` retrieves the attestation between the two peers for the given group ID. The resulting opinion map, stored in the op_map variable, is a two-dimensional array that maps each peer to a list of their attestations with other peers in the group. 42 | 43 | Here's an example of what the opinion map might look like, based on the attestations shown earlier: 44 | ``` 45 | peer1_op => [(peer1, 0), (peer2, 5), (peer3, 0), (peer4, 0), (peer5, 0)] 46 | peer2_op => [(peer1, 0), (peer2, 0), (peer3, 7), (peer4, 0), (peer5, 0)] 47 | peer4_op => [(peer1, 0), (peer2, 3), (peer3, 0), (peer4, 0), (peer5, 0)] 48 | ``` 49 | 50 | This opinion map is then passed to a filtering algorithm before being passed to the EigenTrust algorithm.\ 51 | The details of the filtering algorithm are discussed in more detail in the [Dynamic Sets](../3_dynamic_sets.md) page. 52 | -------------------------------------------------------------------------------- /eigentrust-zk/src/rescue_prime/native/sponge.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | rescue_prime::{native::RescuePrime, RoundParams}, 3 | FieldExt, SpongeHasher, 4 | }; 5 | use std::marker::PhantomData; 6 | 7 | #[derive(Clone)] 8 | /// Constructs objects. 9 | pub struct RescuePrimeSponge 10 | where 11 | P: RoundParams, 12 | { 13 | /// Constructs a vector for the inputs. 14 | inputs: Vec, 15 | /// Internal state 16 | state: [F; WIDTH], 17 | /// Constructs a phantom data for the parameters. 18 | _params: PhantomData

, 19 | } 20 | 21 | impl RescuePrimeSponge 22 | where 23 | P: RoundParams, 24 | { 25 | /// Create objects. 26 | pub fn new() -> Self { 27 | Self { inputs: Vec::new(), state: [F::ZERO; WIDTH], _params: PhantomData } 28 | } 29 | 30 | /// Clones and appends all elements from a slice to the vec. 31 | pub fn update(&mut self, inputs: &[F]) { 32 | self.inputs.extend_from_slice(inputs); 33 | } 34 | 35 | /// Absorb the data in and split it into 36 | /// chunks of size WIDTH. 37 | pub fn load_state(chunk: &[F]) -> [F; WIDTH] { 38 | assert!(chunk.len() <= WIDTH); 39 | let mut fixed_chunk = [F::ZERO; WIDTH]; 40 | fixed_chunk[..chunk.len()].copy_from_slice(chunk); 41 | fixed_chunk 42 | } 43 | 44 | /// Squeeze the data out by 45 | /// permuting until no more chunks are left 46 | pub fn squeeze(&mut self) -> F { 47 | assert!(!self.inputs.is_empty()); 48 | 49 | for chunk in self.inputs.chunks(WIDTH) { 50 | let loaded_state = Self::load_state(chunk); 51 | let mut input = [F::ZERO; WIDTH]; 52 | for i in 0..WIDTH { 53 | input[i] = loaded_state[i] + self.state[i]; 54 | } 55 | 56 | let rescue_prime = RescuePrime::<_, WIDTH, P>::new(input); 57 | self.state = rescue_prime.permute(); 58 | } 59 | 60 | // Clear the inputs, and return the result 61 | self.inputs.clear(); 62 | self.state[0] 63 | } 64 | } 65 | 66 | impl Default for RescuePrimeSponge 67 | where 68 | P: RoundParams, 69 | { 70 | fn default() -> Self { 71 | Self::new() 72 | } 73 | } 74 | 75 | impl SpongeHasher for RescuePrimeSponge 76 | where 77 | P: RoundParams, 78 | { 79 | fn new() -> Self { 80 | Self::new() 81 | } 82 | 83 | fn update(&mut self, inputs: &[F]) { 84 | Self::update(self, inputs) 85 | } 86 | 87 | fn squeeze(&mut self) -> F { 88 | Self::squeeze(self) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /eigentrust-zk/src/poseidon/native/sponge.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | poseidon::{native::Poseidon, RoundParams}, 3 | FieldExt, SpongeHasher, 4 | }; 5 | use std::marker::PhantomData; 6 | 7 | /// Constructs objects. 8 | #[derive(Clone)] 9 | pub struct PoseidonSponge 10 | where 11 | P: RoundParams, 12 | { 13 | /// Constructs a vector for the inputs. 14 | inputs: Vec, 15 | /// Internal state 16 | state: [F; WIDTH], 17 | /// Constructs a phantom data for the parameters. 18 | _params: PhantomData

, 19 | } 20 | 21 | impl PoseidonSponge 22 | where 23 | P: RoundParams, 24 | { 25 | /// Create objects. 26 | pub fn new() -> Self { 27 | Self { inputs: Vec::new(), state: [F::ZERO; WIDTH], _params: PhantomData } 28 | } 29 | 30 | /// Clones and appends all elements from a slice to the vec. 31 | pub fn update(&mut self, inputs: &[F]) { 32 | self.inputs.extend_from_slice(inputs); 33 | } 34 | 35 | /// Absorb the data in and split it into 36 | /// chunks of size WIDTH. 37 | fn load_state(chunk: &[F]) -> [F; WIDTH] { 38 | assert!(chunk.len() <= WIDTH); 39 | let mut fixed_chunk = [F::ZERO; WIDTH]; 40 | fixed_chunk[..chunk.len()].copy_from_slice(chunk); 41 | fixed_chunk 42 | } 43 | 44 | /// Squeeze the data out by 45 | /// permuting until no more chunks are left. 46 | pub fn squeeze(&mut self) -> F { 47 | if self.inputs.is_empty() { 48 | self.inputs.push(F::ZERO); 49 | } 50 | 51 | for chunk in self.inputs.chunks(WIDTH) { 52 | let mut input = [F::ZERO; WIDTH]; 53 | 54 | // Absorb 55 | let loaded_state = Self::load_state(chunk); 56 | for i in 0..WIDTH { 57 | input[i] = loaded_state[i] + self.state[i]; 58 | } 59 | 60 | // Permute 61 | let pos = Poseidon::<_, WIDTH, P>::new(input); 62 | self.state = pos.permute(); 63 | } 64 | 65 | // Clear the inputs, and return the result 66 | self.inputs.clear(); 67 | self.state[0] 68 | } 69 | } 70 | 71 | impl Default for PoseidonSponge 72 | where 73 | P: RoundParams, 74 | { 75 | fn default() -> Self { 76 | Self::new() 77 | } 78 | } 79 | 80 | impl SpongeHasher for PoseidonSponge 81 | where 82 | P: RoundParams, 83 | { 84 | fn new() -> Self { 85 | Self::new() 86 | } 87 | 88 | fn update(&mut self, inputs: &[F]) { 89 | Self::update(self, inputs) 90 | } 91 | 92 | fn squeeze(&mut self) -> F { 93 | PoseidonSponge::squeeze(self) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /docs/5_beyond.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: This pages explores future directions for EigenTrust. 3 | --- 4 | 5 | Several problems to be solved: 6 | 1) Non-unique peer identifiers:\ 7 | If there are two or more peers with the same identifier in the set, the filtering algorithm will not work as intended. To avoid this, it is important to use unique identifiers for each peer. 8 | We can achieve this by requiring the set to have unique id associated with it. This can be achieved by compressing all the peers ids into a MerkleTree root/Sponge hash outside the circuit and make constraints inside the circuit. 9 | For example, consider a set: 10 | ```rust 11 | s = [(peer1, 1000), (peer2, 2000), (peer3, 500), (peer4, 300), (peer5, 200)] 12 | ``` 13 | 14 | We can extract the peer ids from this set, and construct the unique id for the whole set 15 | ```rust 16 | s_ids = s.map(|x| x.0) 17 | root_hash = construct_merkle_tree(s_ids) 18 | // OR 19 | final_hash = poseidon_sponge_hash(s_ids) 20 | ``` 21 | Then we can pass `root_hash` or `final_hash` into the circuit as a public input. We would re-construct the same tree/sponge hash inside the circuit and compare with the public input. 22 | 23 | 2) Performance considerations:\ 24 | As the size of the set and the opinion map grows, the filtering algorithm can become computationally expensive. To optimize performance, it may be necessary to use more efficient data structures and algorithms. 25 | One way we could fix this is to split the network into smaller ones. So, have predefined groups of maximum 256 peers for which we can make EigenTrust convergence proofs. We can then aggregate 2 or more of these proofs to form larger groups. 26 | 27 | If we have sets with `N` participants, aggregating `M` number of proofs, would result in `N * M` number of participants. 28 | ```rust 29 | s1 = ([peer1_score, peer2_score, peer3_score], proof_1) 30 | s2 = ([peer4_score, peer5_score, peer6_score], proof_2) 31 | s3 = ([peer7_score, peer8_score, peer9_score], proof_3) 32 | 33 | accumulator_limbs = aggregate(s1, s2, s3) 34 | 35 | verify(accumulator_limbs) 36 | ``` 37 | 38 | We can also do multiple levels of aggregation in form of a Merkle Tree until we reach the root, where we have aggregated the whole network. 39 | 40 | Future directions - Integration with smart contract platforms: 41 | 1) Peers can use these proofs to prove their reputation and use them to join communities such as Semaphore groups or similar working groups. We can generalise this to make gate-keeping for any form of action on any protocol. 42 | 2) We can also integrate EigenTrust sets inside the smart contract itself. The converged scores of each participants can be used for reputation-weighted voting inside this smart contract. 43 | -------------------------------------------------------------------------------- /docs/1_attestations.md: -------------------------------------------------------------------------------- 1 | --- 2 | This page describes the smart contract used for attestations 3 | --- 4 | 5 | # Attestations 6 | 7 | Attestations are the ratings or opinions given by one peer about another peer in the EigenTrust protocol. Each attestation is given for a single transaction or interaction between peers. 8 | 9 | The structure of an attestation is defined as follows: 10 | ```rust 11 | struct Attestation { 12 | about: F, 13 | key: F, 14 | value: F, 15 | message: F 16 | } 17 | ``` 18 | 19 | Here's a breakdown of each field in the attestation: 20 | 21 | - `about`: the Ethereum address of the peer being rated. This could be an EOA, a smart contract, a DAO, etc. 22 | - `key`: a unique identifier for the transaction or interaction being rated. This could be a hash of the transaction data or a random number generated by the rater. 23 | - `value`: the score given by the rater for the transaction or interaction. The score can range from 0 to a maximum score defined as a constant in the protocol. 24 | - `message`: an optional field for attaching additional information to the attestation. This could be a message from the rater, a domain in which the transaction took place, or a content hash related to the transaction. 25 | 26 | To ensure the integrity and authenticity of an attestation, it is hashed using the Poseidon hash function and then signed using the ECDSA signing algorithm: 27 | ```rust 28 | let att_hash = Poseidon::hash(attestation); 29 | let sig = ECDSA::sign(att_hash, keys); 30 | ``` 31 | The resulting signature, value and message bytes are stored in the AttestationStation smart contract. The bytes layout would be: 32 | ```rust 33 | r = [u8; 32] 34 | s = [u8; 32] 35 | value = u8 36 | message = [u8; 32] 37 | ``` 38 | This adds up to 97 bytes or 65 if we exclude message bytes. 39 | 40 | In case of fetching the attestation from AS and verifying it - first, we read the event: 41 | ```solidity 42 | event AttestationCreated( 43 | address indexed creator, 44 | address indexed about, 45 | bytes32 indexed key, 46 | bytes val 47 | ); 48 | ``` 49 | 50 | Using this data, we extract the `r` and `s`, we verify the signature: 51 | ```rust 52 | let (r, s, value, message) = extract_r_s_value_message(val); 53 | let att = Attestation::new(about, key, value, message); 54 | let hash = Poseidon::hash(att); 55 | let is_valid = ECDSA::verify(pub_key, r, s, hash); 56 | assert!(is_valid); 57 | ``` 58 | 59 | Then check if the used `pub_key` is actually the pre-image of the `creator`: 60 | ```rust 61 | let pk_hash = keccak256(pub_key); 62 | let creator_address = to_address(pk_hash); 63 | assert!(creator_address == creator); 64 | ``` 65 | 66 | See [AttestationStation](../0_attestation_station.md) for more details on how attestations are stored and managed in the EigenTrust protocol. 67 | 68 | By signing the attestation, the rater can prove that they made the rating and that the rating has not been tampered with. This is important for verifying the validity of the attestation in an off-chain environment, such as when calculating the EigenTrust scores for each peer. 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ZK EigenTrust - Deprecated 2 | 3 | **Notice: This project is no longer being maintained as of November 2023.** 4 | 5 | [![MIT licensed][mit-badge]][mit-url] 6 | [![Build Status][actions-badge]][actions-url] 7 | 8 | [mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg 9 | [mit-url]: https://github.com/eigen-trust/protocol/blob/master/LICENSE 10 | [actions-badge]: https://github.com/eigen-trust/protocol/actions/workflows/test.yml/badge.svg 11 | [actions-url]: https://github.com/eigen-trust/protocol/actions?query=branch%3Amaster 12 | 13 | A Rust and Halo2-based library designed to manage trust in distributed networks with zero-knowledge proofs, guided by the framework established in the original [EigenTrust paper](https://nlp.stanford.edu/pubs/eigentrust.pdf). Its primary characteristics are: 14 | 15 | - **Self-policing**: The shared ethics of the user population is defined and enforced by the peers themselves and not by some central authority. 16 | 17 | - **Minimal**: computation, infrastructure, storage, and message complexity are reduced to a minimum. 18 | 19 | - **Incorruptible**: Reputation should be obtained by consistent good behavior through several transactions. This is enforced for all users, so no one can cheat the system and obtain a higher reputation. It is also resistant to malicious collectives. 20 | 21 | ## Deprecation Notice 22 | 23 | Effective November 2023, this project has been deprecated and is no longer under active development. 24 | 25 | We invite the community to fork and maintain their own versions of this codebase. Should you choose to do so, we remind you to comply with the terms outlined in the [license](LICENSE). 26 | 27 | ## Structure 28 | 29 | The project is organized in three crates: 30 | 31 | - [eigentrust](eigentrust): This is the core library crate. It provides the `Client` struct for interfacing with the EigenTrust algorithm's circuits and includes additional modules to extend its functionality and facilitate integration. 32 | 33 | - [eigentrust-cli](eigentrust-cli): This crate offers a command-line interface application that serves as a practical example of using the library. It supports operations such as deploying smart contracts, submitting attestations, calculating global trust scores, and generating and verifying zero-knowledge proofs. 34 | 35 | - [eigentrust-zk](eigentrust-zk): Dedicated to the zero-knowledge components of the protocol, this crate encompasses the necessary Chips, Chipsets, and Circuits that pertain to the EigenTrust protocol implementation. 36 | 37 | For a more in-depth understanding of the project's architecture and functionality, please refer to the documentation in the [docs](docs) directory. 38 | 39 | There's also a [scripts](scripts) directory containing scripts for building documentation, running tests across the workspace, and compiling the entire project. 40 | 41 | ### License 42 | 43 | Licensed under the MIT License - see the [LICENSE](LICENSE) file for details or visit [opensource.org](http://opensource.org/licenses/MIT). 44 | 45 | ### Acknowledgements 46 | 47 | - Ethereum Foundation and Privacy & Scaling Explorations team. 48 | - All contributors to this repository. 49 | -------------------------------------------------------------------------------- /eigentrust-cli/assets/attestations.csv: -------------------------------------------------------------------------------- 1 | about,domain,value,message,sig_r,sig_s,rec_id 2 | 0x70997970c51812dc3a010c7d01b50e0d17dc79c8,0x0000000000000000000000000000000000000000,10,0x0000000000000000000000000000000000000000000000000000000000000000,0x35d796aa9d85cf941ad74b83bda302eecbb047c5aca245d6d3a793c93b236d29,0x9eba00fad7e20f48374f4ee25aeaf15b16ff8f4a46b484806c09ebb3e6d39c12,1 3 | 0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc,0x0000000000000000000000000000000000000000,10,0x0000000000000000000000000000000000000000000000000000000000000000,0x66eb26cda8066652b58b3a7b1c205b264ec9ac779dcdf201937e6ad605cf21ca,0x8915a3f533ce28ba04e893a84acd06c4afb4ed3122f9dfe4fb708ba0919ca04d,0 4 | 0x90f79bf6eb2c4f870365e785982e1f101e93b906,0x0000000000000000000000000000000000000000,10,0x0000000000000000000000000000000000000000000000000000000000000000,0x295e449af2d3249dfef036132773fa30e42e276136976914b739db7ccd811920,0x7e6c7df89d201782832591222be9121e7acc9d25c066e9d83e6c9e35e8b21a55,0 5 | 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266,0x0000000000000000000000000000000000000000,10,0x0000000000000000000000000000000000000000000000000000000000000000,0xc953465e18b11946d850504e285d9e942e8f70832ad4868b2862316a362a9253,0x72cbab1a73d5f535bb506185ebd98c6698ec06ddcf1030bc55dc6ae97f4d7109,0 6 | 0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc,0x0000000000000000000000000000000000000000,10,0x0000000000000000000000000000000000000000000000000000000000000000,0xbca5ee8a00309bacb4c4e44fa22f7f8195552617be90773ea502b36cdf0ba859,0x1db14610dcc3c4e7a8848f1c9d972a9f7b909872d73f9e14d55e912a2102b607,0 7 | 0x90f79bf6eb2c4f870365e785982e1f101e93b906,0x0000000000000000000000000000000000000000,10,0x0000000000000000000000000000000000000000000000000000000000000000,0x2a9c1ddc1fc3dbe26eaf66975aa00af3f7950c4f668aad354fa3f0ab6ec76b5b,0xc825724b91178b39682c0b81bcf787479631221e62b193452e53bd50240c6223,1 8 | 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266,0x0000000000000000000000000000000000000000,10,0x0000000000000000000000000000000000000000000000000000000000000000,0xc3930963d681f6645573912a05286679d43d82f2f32796037c61fbc9083e44b0,0x19e62c5855a24d735cbafec7ae1bf36f68c0c6b2dfda84e3d735c800338fdb25,0 9 | 0x70997970c51812dc3a010c7d01b50e0d17dc79c8,0x0000000000000000000000000000000000000000,10,0x0000000000000000000000000000000000000000000000000000000000000000,0xf1d68ea2a6126ff932bf7e675fb798cb88c4837d5e6afcdfbe7e4d7b28d3ad47,0xe3c85704eb9a2b95a126949a41e0cba827668005a3aca9c377ca5f1135f0d34c,0 10 | 0x90f79bf6eb2c4f870365e785982e1f101e93b906,0x0000000000000000000000000000000000000000,10,0x0000000000000000000000000000000000000000000000000000000000000000,0x16a137d719ea5c3ab62950d200d1351191a70356cc6ccf3e1b3b8fac9210109f,0xf842bb839d24dabc83f20c8dfb3278ed5b6b49f434fd6b8d8539fccc62073d3f,0 11 | 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266,0x0000000000000000000000000000000000000000,10,0x0000000000000000000000000000000000000000000000000000000000000000,0xabc32b960ec7bc2cb9f38e40f4b3d8aa7a47ca09ca3a2bd2f014ddba121a4b44,0x2b8db4319dbb438f36232d88bd4756cd09248d3aac8314666869d0a0ce63c17b,1 12 | 0x70997970c51812dc3a010c7d01b50e0d17dc79c8,0x0000000000000000000000000000000000000000,10,0x0000000000000000000000000000000000000000000000000000000000000000,0x72656c3000dc2fd34a12ba1d4dc0d8cfd4bb7c64fcfb0a00cd6a23e63b39e09a,0xd7155d0c31194651a763faa4ebfdd82be6314b9a5a79d1e001f28b7191d7020e,0 13 | 0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc,0x0000000000000000000000000000000000000000,10,0x0000000000000000000000000000000000000000000000000000000000000000,0xec144687ce2ad27e5c170ccab61a20cf608f2119616d22b890c0bbac4c3f25cd,0xf845e319b040368f1febf1ef7efb816f9663f7072ebf592229711c480912ef5a,0 14 | -------------------------------------------------------------------------------- /eigentrust-zk/src/circuits/opinion/native.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use halo2::halo2curves::CurveAffine; 4 | 5 | use crate::{ 6 | circuits::dynamic_sets::native::SignedAttestation, 7 | circuits::HASHER_WIDTH, 8 | ecdsa::native::{EcdsaVerifier, PublicKey}, 9 | integer::native::Integer, 10 | params::{ecc::EccParams, rns::RnsParams}, 11 | FieldExt, Hasher, SpongeHasher, 12 | }; 13 | 14 | /// Opinion info of peer 15 | pub struct Opinion< 16 | const NUM_NEIGHBOURS: usize, 17 | C: CurveAffine, 18 | N: FieldExt, 19 | const NUM_LIMBS: usize, 20 | const NUM_BITS: usize, 21 | P, 22 | EC, 23 | H: Hasher, 24 | SH: SpongeHasher, 25 | > where 26 | P: RnsParams + RnsParams, 27 | EC: EccParams, 28 | C::ScalarExt: FieldExt, 29 | C::Base: FieldExt, 30 | { 31 | from: PublicKey, 32 | attestations: Vec>, 33 | domain: N, 34 | _h: PhantomData<(H, SH)>, 35 | } 36 | 37 | impl< 38 | const NUM_NEIGHBOURS: usize, 39 | C: CurveAffine, 40 | N: FieldExt, 41 | const NUM_LIMBS: usize, 42 | const NUM_BITS: usize, 43 | P, 44 | EC, 45 | H: Hasher, 46 | SH: SpongeHasher, 47 | > Opinion 48 | where 49 | P: RnsParams + RnsParams, 50 | EC: EccParams, 51 | C::ScalarExt: FieldExt, 52 | C::Base: FieldExt, 53 | { 54 | /// Construct new instance 55 | pub fn new( 56 | from: PublicKey, 57 | attestations: Vec>, domain: N, 58 | ) -> Self { 59 | Self { from, attestations, domain, _h: PhantomData } 60 | } 61 | 62 | /// Validate attestations & calculate the hash 63 | pub fn validate(&self, set: Vec) -> (N, Vec, N) { 64 | let addr = self.from.to_address(); 65 | 66 | let pos_from = set.iter().position(|&x| x == addr); 67 | assert!(pos_from.is_some()); 68 | 69 | let is_default_pk = self.from == PublicKey::default(); 70 | 71 | let mut scores = Vec::new(); 72 | let mut hashes = Vec::new(); 73 | for i in 0..NUM_NEIGHBOURS { 74 | let att = self.attestations[i].clone(); 75 | assert!(att.attestation.about == set[i]); 76 | assert!(att.attestation.domain == self.domain); 77 | 78 | let att_hasher = H::new([ 79 | att.attestation.about, 80 | att.attestation.domain, 81 | att.attestation.value, 82 | att.attestation.message, 83 | N::ZERO, 84 | ]); 85 | let att_hash = att_hasher.finalize()[0]; 86 | 87 | let sig = self.attestations[i].signature.clone(); 88 | let msg_hash = Integer::::from_n(att_hash); 89 | let ecdsa_verifier = EcdsaVerifier::new(sig, msg_hash, self.from.clone()); 90 | let is_valid = ecdsa_verifier.verify(); 91 | 92 | let is_default_addr = set[i] == N::ZERO; 93 | let invalid_condition = !is_valid || is_default_addr || is_default_pk; 94 | let (final_score, final_hash) = if invalid_condition { 95 | (N::ZERO, N::ZERO) 96 | } else { 97 | (att.attestation.value, att_hash) 98 | }; 99 | 100 | scores.push(final_score); 101 | hashes.push(final_hash); 102 | } 103 | 104 | let mut sponge_hasher = SH::new(); 105 | sponge_hasher.update(&hashes); 106 | let op_hash = sponge_hasher.squeeze(); 107 | 108 | (addr, scores, op_hash) 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /eigentrust-zk/src/rescue_prime/native/mod.rs: -------------------------------------------------------------------------------- 1 | /// Native sponge implementation 2 | pub mod sponge; 3 | 4 | use crate::{params::hasher::RoundParams, FieldExt, Hasher}; 5 | use std::marker::PhantomData; 6 | 7 | /// Constructs objects. 8 | pub struct RescuePrime 9 | where 10 | P: RoundParams, 11 | { 12 | /// Constructs an array for the inputs. 13 | inputs: [F; WIDTH], 14 | /// Constructs a phantom data for the parameters. 15 | _params: PhantomData

, 16 | } 17 | 18 | impl RescuePrime 19 | where 20 | P: RoundParams, 21 | { 22 | /// Create the objects. 23 | pub fn new(inputs: [F; WIDTH]) -> Self { 24 | RescuePrime { inputs, _params: PhantomData } 25 | } 26 | 27 | /// Rescue Prime permutation 28 | pub fn permute(&self) -> [F; WIDTH] { 29 | let full_rounds = P::full_rounds(); 30 | let round_constants = P::round_constants(); 31 | 32 | let mut state = self.inputs; 33 | for i in 0..full_rounds - 1 { 34 | // Apply Sbox 35 | for state in state.iter_mut().take(WIDTH) { 36 | *state = P::sbox_f(*state); 37 | } 38 | // Apply MDS 39 | state = P::apply_mds(&state); 40 | // Apply round constants 41 | let consts = P::load_round_constants(i, &round_constants); 42 | state = P::apply_round_constants(&state, &consts); 43 | // Apply Sbox inverse 44 | for state in state.iter_mut().take(WIDTH) { 45 | *state = P::sbox_inv_f(*state); 46 | } 47 | // Apply MDS for the second time 48 | state = P::apply_mds(&state); 49 | // Apply next round constants 50 | let consts = P::load_round_constants(i + 1, &round_constants); 51 | state = P::apply_round_constants(&state, &consts); 52 | } 53 | 54 | state 55 | } 56 | } 57 | 58 | impl Hasher for RescuePrime 59 | where 60 | P: RoundParams, 61 | { 62 | fn new(inputs: [F; WIDTH]) -> Self { 63 | Self::new(inputs) 64 | } 65 | 66 | fn finalize(&self) -> [F; WIDTH] { 67 | Self::permute(self) 68 | } 69 | } 70 | 71 | #[cfg(test)] 72 | mod test { 73 | use super::*; 74 | use crate::params::hasher::{hex_to_field, rescue_prime_bn254_5x5::Params}; 75 | use halo2::halo2curves::bn256::Fr; 76 | 77 | type TestHasher = RescuePrime; 78 | 79 | #[test] 80 | fn test_native_rescue_prime_5x5() { 81 | // Testing 5x5 input. 82 | let inputs: [Fr; 5] = [ 83 | "0x0000000000000000000000000000000000000000000000000000000000000000", 84 | "0x0000000000000000000000000000000000000000000000000000000000000001", 85 | "0x0000000000000000000000000000000000000000000000000000000000000002", 86 | "0x0000000000000000000000000000000000000000000000000000000000000003", 87 | "0x0000000000000000000000000000000000000000000000000000000000000004", 88 | ] 89 | .map(|n| hex_to_field(n)); 90 | 91 | // Results taken from https://github.com/matter-labs/rescue-poseidon 92 | let outputs: [Fr; 5] = [ 93 | "0x1a06ea09af4d8d61f991846f001ded4056feafcef55f1e9c4fd18100b8c7654f", 94 | "0x2f66d057b2bd9692f51e072013b8f320c5e6d7081070ffe7ca357e18e5faecf4", 95 | "0x177abf3b6a2e903adf4c71f18f744b55b39c487a9a4fd1a1d4aee381b99f357b", 96 | "0x1271bfa104c298efaccc1680be1b6e36cbf2c87ea789f2f79f7742bc16992235", 97 | "0x040f785abfad4da68331f9c884343fa6eecb07060ebcd96117862acebae5c3ac", 98 | ] 99 | .map(|n| hex_to_field(n)); 100 | 101 | let rescue_prime = TestHasher::new(inputs); 102 | 103 | let out = rescue_prime.permute(); 104 | 105 | assert_eq!(out, outputs); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /docs/3_dynamic_sets.md: -------------------------------------------------------------------------------- 1 | --- 2 | This page describes our dynamic sets filtering algorithm. 3 | --- 4 | 5 | Suppose we create a fixed set of peers with a limit of 5.\ 6 | Instead of using Ethereum addresses we will use simplified identifiers 7 | like peer1, peer2, peer3, etc. and we will use `null` for empty slots. 8 | 9 | This set will change over time as we will be able to add and remove members. 10 | 11 | For example, let's say we have the following set of peers: 12 | ``` 13 | set = [peer1, peer2, peer3, null, null] 14 | ``` 15 | 16 | Now, imagine we have a map that represent opinions from one peer to the whole group. Every opinion should match the set to be valid, in the following way: 17 | ``` 18 | peer1 => [(peer1, 0), (peer2, 4), (peer3, 6), (null, 0), (null, 0)] 19 | ``` 20 | The opinion array should equal the original set in length.\ 21 | The items in the array are touples of the id of the peer that we want to give the score to and the actual score. The id at each index should match the id in the set in order to be considered valid. 22 | 23 | The whole map should look like this: 24 | ``` 25 | scores => { 26 | peer1 => [(peer1, 0), (peer2, 4), (peer3, 6), (null, 0), (null, 0)] 27 | peer2 => [(peer1, 4), (peer2, 0), (peer3, 6), (null, 0), (null, 0)] 28 | peer3 => [(peer1, 4), (peer2, 6), (peer3, 0), (null, 0), (null, 0)] 29 | } 30 | ``` 31 | 32 | Now, let's take a look at how we filter out invalid cases from the opinion array. 33 | 34 | **Filtering of invalid cases:** 35 | 36 | 1) Id at the specific index does not match the one in the set:\ 37 | Suppose we want to give a score of 5 to peer13 at the index 3: 38 | ``` 39 | peer1 => [(peer1, 0), (peer2, 4), (peer3, 6), (peer13, 5), (null, 0)] 40 | ``` 41 | Since the id at index 3 is null, the id and the score will be nullified, and the new opinion will look like: 42 | ``` 43 | peer1 => [(peer1, 0), (peer2, 4), (peer3, 6), (null, 0), (null, 0)] 44 | ``` 45 | 46 | 2) Non 0 score was given to itself:\ 47 | Giving score to itself is forbiden since peers would be able to give the score only to themselves, thus introducing reputation leaking during the convergence. 48 | So, this type of opinion: 49 | ``` 50 | peer1 => [(peer1, 4), (peer2, 0), (peer3, 6), (null, 0), (null, 0)] 51 | ``` 52 | will turn into: 53 | ``` 54 | peer1 => [(peer1, 0), (peer2, 0), (peer3, 6), (null, 0), (null, 0)] 55 | ``` 56 | 57 | 3) Total sum of scores is 0:\ 58 | If the initial opinion, or the filtered opinion has a sum of scores of 0, 59 | the equal score (score of 1) is given to each peer, so this: 60 | ``` 61 | peer1 => [(peer1, 0), (peer2, 0), (peer3, 0), (null, 0), (null, 0)] 62 | ``` 63 | will turn into this: 64 | ``` 65 | peer1 => [(peer1, 0), (peer2, 1), (peer3, 1), (null, 0), (null, 0)] 66 | ``` 67 | 68 | 4) Opinion array does not exist/not signed:\ 69 | Will be treated the same way as 3). The equal score will be distributed to all peers 70 | 71 | The pseudo code algorithm: 72 | ```rust 73 | for i in set.len() { 74 | let pk_i = set[i]; 75 | if pk_i == null { 76 | continue; 77 | } 78 | 79 | for j in set.len() { 80 | let pk_j = set[j]; 81 | let op_pk_j = scores[pk_i][j].0; 82 | 83 | let is_diff_pk_j = pk_j != op_pk_j; 84 | let is_pk_j_zero = pk_j == null; 85 | let is_pk_i = pk_j == pk_i; 86 | 87 | if is_diff_pk_j || is_pk_j_zero || is_pk_i { 88 | scores[pk_i][j].1 = 0; 89 | } 90 | 91 | if is_diff_pk_j { 92 | scores[pk_i][j].0 = pk_j; 93 | } 94 | } 95 | 96 | let op_score_sum = sum(scores[pk_i]); 97 | if op_score_sum == 0 { 98 | for j in 0..group.len() { 99 | let pk_j = scores[pk_i][j].0; 100 | 101 | let is_diff_pk = pk_j != pk_i; 102 | let is_not_null = pk_j != null; 103 | 104 | if is_diff_pk && is_not_null { 105 | scores[pk_i][j] = (pk_j, Fr::from(1)); 106 | } 107 | } 108 | } 109 | } 110 | ``` 111 | -------------------------------------------------------------------------------- /eigentrust-zk/src/gadgets/absorb.rs: -------------------------------------------------------------------------------- 1 | use crate::{Chip, CommonConfig, FieldExt, RegionCtx}; 2 | use halo2::{ 3 | circuit::{AssignedCell, Layouter, Region}, 4 | plonk::{ConstraintSystem, Error, Expression, Selector}, 5 | poly::Rotation, 6 | }; 7 | 8 | /// Copy the intermediate hash(poseidon, rescue_prime, ...) state 9 | /// into the region 10 | pub fn copy_state( 11 | ctx: &mut RegionCtx<'_, F>, config: &CommonConfig, prev_state: &[AssignedCell; WIDTH], 12 | ) -> Result<[AssignedCell; WIDTH], Error> { 13 | let mut state: [Option>; WIDTH] = [(); WIDTH].map(|_| None); 14 | for i in 0..WIDTH { 15 | let new_state = ctx.copy_assign(config.advice[i], prev_state[i].clone())?; 16 | state[i] = Some(new_state); 17 | } 18 | Ok(state.map(|item| item.unwrap())) 19 | } 20 | 21 | /// A chip for absorbing the previous hash(poseidon, rescue_prime, ...) state 22 | pub struct AbsorbChip { 23 | prev_state: [AssignedCell; WIDTH], 24 | state: [AssignedCell; WIDTH], 25 | } 26 | 27 | impl AbsorbChip { 28 | /// Constructor for a chip 29 | pub fn new( 30 | prev_state: [AssignedCell; WIDTH], state: [AssignedCell; WIDTH], 31 | ) -> Self { 32 | Self { prev_state, state } 33 | } 34 | } 35 | 36 | impl Chip for AbsorbChip { 37 | type Output = [AssignedCell; WIDTH]; 38 | 39 | fn configure(common: &CommonConfig, meta: &mut ConstraintSystem) -> Selector { 40 | let absorb_selector = meta.selector(); 41 | 42 | meta.create_gate("absorb", |v_cells| { 43 | let mut exprs = [(); WIDTH].map(|_| Expression::Constant(F::ZERO)); 44 | 45 | let s = v_cells.query_selector(absorb_selector); 46 | for i in 0..WIDTH { 47 | let hasher_exp = v_cells.query_advice(common.advice[i], Rotation::cur()); 48 | let sponge_exp = v_cells.query_advice(common.advice[i + WIDTH], Rotation::cur()); 49 | let next_sponge_exp = 50 | v_cells.query_advice(common.advice[i + 2 * WIDTH], Rotation::cur()); 51 | let diff = next_sponge_exp - (sponge_exp + hasher_exp); 52 | exprs[i] = s.clone() * diff; 53 | } 54 | 55 | exprs 56 | }); 57 | 58 | absorb_selector 59 | } 60 | 61 | fn synthesize( 62 | self, common: &CommonConfig, selector: &Selector, mut layouter: impl Layouter, 63 | ) -> Result { 64 | layouter.assign_region( 65 | || "absorb", 66 | |region: Region<'_, F>| { 67 | let mut ctx = RegionCtx::new(region, 0); 68 | ctx.enable(*selector)?; 69 | 70 | // Load previous RescuePrime state 71 | let loaded_state = { 72 | let mut loaded_state: [Option>; WIDTH] = 73 | [(); WIDTH].map(|_| None); 74 | for i in 0..WIDTH { 75 | let new_state = 76 | ctx.copy_assign(common.advice[i], self.prev_state[i].clone())?; 77 | loaded_state[i] = Some(new_state); 78 | } 79 | loaded_state.map(|item| item.unwrap()) 80 | }; 81 | 82 | // Load next chunk 83 | let loaded_chunk = { 84 | let mut loaded_chunk: [Option>; WIDTH] = 85 | [(); WIDTH].map(|_| None); 86 | for i in 0..WIDTH { 87 | let new_state = 88 | ctx.copy_assign(common.advice[i + WIDTH], self.state[i].clone())?; 89 | loaded_chunk[i] = Some(new_state); 90 | } 91 | loaded_chunk.map(|item| item.unwrap()) 92 | }; 93 | 94 | // Calculate the next state to permute 95 | let mut next_state: [Option>; WIDTH] = [(); WIDTH].map(|_| None); 96 | for i in 0..WIDTH { 97 | let chunk_state = &loaded_chunk[i]; 98 | let pos_state = &loaded_state[i]; 99 | let sum = chunk_state.value().and_then(|&s| { 100 | let pos_state_val = pos_state.value(); 101 | pos_state_val.map(|&ps| s + ps) 102 | }); 103 | let assigned_sum = ctx.assign_advice(common.advice[i + 2 * WIDTH], sum)?; 104 | next_state[i] = Some(assigned_sum); 105 | } 106 | 107 | Ok(next_state.map(|x| x.unwrap())) 108 | }, 109 | ) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /eigentrust-cli/src/fs.rs: -------------------------------------------------------------------------------- 1 | //! # Filesystem Actions Module. 2 | //! 3 | //! This module provides functionalities for filesystem actions. 4 | 5 | use crate::CliConfig; 6 | use dotenv::{dotenv, var}; 7 | use eigentrust::{ 8 | circuit::Circuit, 9 | error::EigenError, 10 | storage::{BinFileStorage, JSONFileStorage, Storage}, 11 | }; 12 | use log::warn; 13 | use std::{env::current_dir, path::PathBuf}; 14 | 15 | /// Default mnemonic seed phrase. 16 | const DEFAULT_MNEMONIC: &str = "test test test test test test test test test test test junk"; 17 | /// Library configuration file name. 18 | pub const CONFIG_FILE: &str = "config"; 19 | /// Proof file name. 20 | pub const PROOF_FILE: &str = "proof"; 21 | /// Proving key file name. 22 | pub const PROVING_KEY_FILE: &str = "proving-key"; 23 | /// Public inputs file name. 24 | pub const PUB_INP_FILE: &str = "public-inputs"; 25 | /// KZG parameters file name. 26 | pub const PARAMS_FILE: &str = "kzg-params"; 27 | 28 | /// Enum representing the possible file extensions. 29 | pub enum FileType { 30 | /// CSV file. 31 | Csv, 32 | /// JSON file. 33 | Json, 34 | /// Binary file. 35 | Bin, 36 | } 37 | 38 | impl FileType { 39 | /// Converts the enum variant into its corresponding file extension. 40 | fn as_str(&self) -> &'static str { 41 | match self { 42 | FileType::Csv => "csv", 43 | FileType::Json => "json", 44 | FileType::Bin => "bin", 45 | } 46 | } 47 | } 48 | 49 | // Enum for different EigenTrust binary files 50 | pub enum EigenFile { 51 | KzgParams(u32), 52 | ProvingKey(Circuit), 53 | Proof(Circuit), 54 | PublicInputs(Circuit), 55 | } 56 | 57 | impl EigenFile { 58 | /// Loads the contents of the file. 59 | pub fn load(&self) -> Result, EigenError> { 60 | let filepath = self.path()?; 61 | BinFileStorage::new(filepath).load() 62 | } 63 | 64 | /// Saves the data to the file. 65 | pub fn save(&self, data: Vec) -> Result<(), EigenError> { 66 | let filepath = self.path()?; 67 | BinFileStorage::new(filepath).save(data) 68 | } 69 | 70 | /// Returns the path of the file. 71 | fn path(&self) -> Result { 72 | get_file_path(&self.filename(), FileType::Bin) 73 | } 74 | 75 | /// Returns the filename of the file. 76 | fn filename(&self) -> String { 77 | match self { 78 | EigenFile::KzgParams(pol_degree) => format!("{}-{}", PARAMS_FILE, pol_degree), 79 | EigenFile::ProvingKey(circuit) => format!("{}-{}", circuit.as_str(), PROVING_KEY_FILE), 80 | EigenFile::Proof(circuit) => format!("{}-{}", circuit.as_str(), PROOF_FILE), 81 | EigenFile::PublicInputs(circuit) => format!("{}-{}", circuit.as_str(), PUB_INP_FILE), 82 | } 83 | } 84 | } 85 | 86 | /// Loads the mnemonic from the environment file. 87 | pub fn load_mnemonic() -> String { 88 | dotenv().ok(); 89 | var("MNEMONIC").unwrap_or_else(|_| { 90 | warn!("MNEMONIC environment variable is not set. Using default."); 91 | DEFAULT_MNEMONIC.to_string() 92 | }) 93 | } 94 | 95 | /// Retrieves the path to the `assets` directory. 96 | pub fn get_assets_path() -> Result { 97 | current_dir().map_err(EigenError::IOError).map(|current_dir| { 98 | // Workaround for the tests running in the `client` directory. 99 | #[cfg(test)] 100 | { 101 | current_dir.join("assets") 102 | } 103 | 104 | #[cfg(not(test))] 105 | { 106 | current_dir.join("eigentrust-cli/assets") 107 | } 108 | }) 109 | } 110 | 111 | /// Helper function to get the path of a file in the `assets` directory. 112 | pub fn get_file_path(file_name: &str, file_type: FileType) -> Result { 113 | let assets_path = get_assets_path()?; 114 | Ok(assets_path.join(format!("{}.{}", file_name, file_type.as_str()))) 115 | } 116 | 117 | /// Loads the configuration file. 118 | pub fn load_config() -> Result { 119 | let filepath = get_file_path(CONFIG_FILE, FileType::Json)?; 120 | JSONFileStorage::::new(filepath).load() 121 | } 122 | 123 | #[cfg(test)] 124 | mod tests { 125 | use super::*; 126 | use std::fs; 127 | 128 | #[test] 129 | fn test_eigenfile_save_and_load() { 130 | let test_data = vec![1u8, 2, 3, 4, 5]; 131 | let eigen_file = EigenFile::KzgParams(999); 132 | 133 | eigen_file.save(test_data.clone()).unwrap(); 134 | let loaded_data = eigen_file.load().unwrap(); 135 | assert_eq!(test_data, loaded_data); 136 | 137 | fs::remove_file(eigen_file.path().unwrap()).unwrap(); 138 | } 139 | 140 | #[test] 141 | fn test_eigenfile_path_and_filename() { 142 | let eigen_file = EigenFile::KzgParams(999); 143 | let filename = eigen_file.filename(); 144 | let path = eigen_file.path().unwrap(); 145 | 146 | assert!(path.to_string_lossy().contains(&filename)); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /eigentrust-zk/src/params/rns/bn256.rs: -------------------------------------------------------------------------------- 1 | // Rns 2 | // bit_len_lookup: 17, 3 | // wrong_modulus: 4 | // 21888242871839275222246405745257275088696311157297823662689037894645226208583, 5 | // native_modulus: 6 | // 21888242871839275222246405745257275088548364400416034343698204186575808495617, 7 | // binary_modulus: 8 | // 7588550360256754183279148073529370729071901715047420004889892225542594864082845696, 9 | // crt_modulus: 10 | // 1661000333304832637718917699744950972288079041304113932603045769717696232214372508635029\ 11 | /// 51190734612532350192541290114096968998888229427253981337422670103314432, 12 | // right_shifters: [ 13 | // 0x0000000000000000000000000000000000000000000000000000000000000001, 14 | // 0x0b603a5609b3f6f81dbc9c192fc7933ab42e346981868e480f8e4610fb396ee5, 15 | // 0x1b7c016fe8acfaed1a908db2cea9b991a31a140f219532a9568bea8e0766f9dd, 16 | // 0x0523513296c10199338287b1e0bedd9955a33201cd88df51769b0bf04e2f27cc, 17 | // ], 18 | // left_shifters: [ 19 | // 0x0000000000000000000000000000000000000000000000000000000000000001, 20 | // 0x0000000000000000000000000000000000000000000000100000000000000000, 21 | // 0x0000000000000000000000000000010000000000000000000000000000000000, 22 | // 0x0000000000001000000000000000000000000000000000000000000000000000, 23 | // ], 24 | // base_aux: [ 25 | // 488280579659007654542, 26 | // 510955945554286098768, 27 | // 301160387202582250159, 28 | // 1702635872462387, 29 | // ], 30 | // negative_wrong_modulus_decomposed: [ 31 | // 0x000000000000000000000000000000000000000000000002c3df73e9278302b9, 32 | // 0x00000000000000000000000000000000000000000000000a2687e956e978e357, 33 | // 0x00000000000000000000000000000000000000000000000fd647afba497e7ea7, 34 | // 0x00000000000000000000000000000000000000000000000ffffcf9bb18d1ece5, 35 | // ], 36 | // wrong_modulus_decomposed: [ 37 | // 0x00000000000000000000000000000000000000000000000d3c208c16d87cfd47, 38 | // 0x000000000000000000000000000000000000000000000005d97816a916871ca8, 39 | // 0x00000000000000000000000000000000000000000000000029b85045b6818158, 40 | // 0x00000000000000000000000000000000000000000000000000030644e72e131a, 41 | // ], 42 | // wrong_modulus_minus_one: [ 43 | // 0x00000000000000000000000000000000000000000000000d3c208c16d87cfd46, 44 | // 0x000000000000000000000000000000000000000000000005d97816a916871ca8, 45 | // 0x00000000000000000000000000000000000000000000000029b85045b6818158, 46 | // 0x00000000000000000000000000000000000000000000000000030644e72e131a, 47 | // ], 48 | // wrong_modulus_in_native_modulus: 49 | // 0x000000000000000000000000000000006f4d8248eeb859fbf83e9682e87cfd46, 50 | // max_reduced_limb: 295147905179352825855, 51 | // max_unreduced_limb: 5070602400912917605986812821503, 52 | // max_remainder: 53 | // 28948022309329048855892746252171976963317496166410141009864396001978282409983, 54 | // max_operand: 55 | // 7410693711188236507108543040556026102609279018600996098525285376506440296955903, 56 | // max_mul_quotient: 57 | // 3794275180128377091639574036764685364535950857523710002444946112771297432041422847, 58 | // max_most_significant_reduced_limb: 1125899906842623, 59 | // max_most_significant_operand_limb: 288230376151711743, 60 | // max_most_significant_mul_quotient_limb: 147573952589676412927, 61 | // mul_v_bit_len: 71, 62 | // red_v_bit_len: 69, 63 | use super::*; 64 | use halo2::halo2curves::bn256::{Fq, Fr}; 65 | 66 | /// Structure for the Bn256_4_68 67 | #[derive(Debug, Clone, PartialEq, Default)] 68 | pub struct Bn256_4_68; 69 | 70 | impl RnsParams for Bn256_4_68 { 71 | fn wrong_modulus() -> BigUint { 72 | BigUint::from_str( 73 | "21888242871839275222246405745257275088696311157297823662689037894645226208583", 74 | ) 75 | .unwrap() 76 | } 77 | 78 | fn wrong_modulus_in_native_modulus() -> Fr { 79 | Fr::from_u128(147946756881789318990833708069417712966) 80 | } 81 | 82 | fn negative_wrong_modulus_decomposed() -> [Fr; 4] { 83 | let limb0 = Fr::from_u128(51007615349848998585); 84 | let limb1 = Fr::from_u128(187243884991886189399); 85 | let limb2 = Fr::from_u128(292141664167738113703); 86 | let limb3 = Fr::from_u128(295147053861416594661); 87 | [limb0, limb1, limb2, limb3] 88 | } 89 | 90 | fn right_shifters() -> [Fr; 4] { 91 | let limb0 = Fr::from_u128(1); 92 | let limb1 = Fr::from_raw([ 93 | 0xf8e4610fb396ee5, 0xb42e346981868e48, 0x1dbc9c192fc7933a, 0xb603a5609b3f6f8, 94 | ]); 95 | let limb2 = Fr::from_raw([ 96 | 0x568bea8e0766f9dd, 0xa31a140f219532a9, 0x1a908db2cea9b991, 0x1b7c016fe8acfaed, 97 | ]); 98 | let limb3 = Fr::from_raw([ 99 | 0x769b0bf04e2f27cc, 0x55a33201cd88df51, 0x338287b1e0bedd99, 0x523513296c10199, 100 | ]); 101 | [limb0, limb1, limb2, limb3] 102 | } 103 | 104 | fn left_shifters() -> [Fr; 4] { 105 | let limb0 = Fr::from_u128(1); 106 | let limb1 = Fr::from_raw([0x0, 0x10, 0x0, 0x0]); 107 | let limb2 = Fr::from_raw([0x0, 0x0, 0x100, 0x0]); 108 | let limb3 = Fr::from_raw([0x0, 0x0, 0x0, 0x1000]); 109 | [limb0, limb1, limb2, limb3] 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /eigentrust-zk/src/ecc/mod.rs: -------------------------------------------------------------------------------- 1 | /// Ecc where both base and scalar fields are emulated 2 | pub mod generic; 3 | /// Ecc where base field is the wrong field and scalar is the native 4 | pub mod same_curve; 5 | 6 | use crate::{gadgets::main::MainConfig, integer::IntegerEqualConfig}; 7 | use halo2::plonk::Selector; 8 | 9 | /// Configuration elements for the circuit are defined here. 10 | #[derive(Debug, Clone)] 11 | pub struct EccAddConfig { 12 | /// Constructs selectors from different circuits. 13 | integer_reduce_selector: Selector, 14 | integer_sub_selector: Selector, 15 | integer_mul_selector: Selector, 16 | integer_div_selector: Selector, 17 | } 18 | 19 | impl EccAddConfig { 20 | /// Construct a new config given the selector of child chips 21 | pub fn new( 22 | integer_reduce_selector: Selector, integer_sub_selector: Selector, 23 | integer_mul_selector: Selector, integer_div_selector: Selector, 24 | ) -> Self { 25 | Self { 26 | integer_reduce_selector, 27 | integer_sub_selector, 28 | integer_mul_selector, 29 | integer_div_selector, 30 | } 31 | } 32 | } 33 | 34 | /// Configuration elements for the circuit are defined here. 35 | #[derive(Debug, Clone)] 36 | pub struct EccDoubleConfig { 37 | /// Constructs selectors from different circuits. 38 | integer_reduce_selector: Selector, 39 | integer_add_selector: Selector, 40 | integer_sub_selector: Selector, 41 | integer_mul_selector: Selector, 42 | integer_div_selector: Selector, 43 | } 44 | 45 | impl EccDoubleConfig { 46 | /// Construct a new config given the selector of child chips 47 | pub fn new( 48 | integer_reduce_selector: Selector, integer_add_selector: Selector, 49 | integer_sub_selector: Selector, integer_mul_selector: Selector, 50 | integer_div_selector: Selector, 51 | ) -> Self { 52 | Self { 53 | integer_reduce_selector, 54 | integer_add_selector, 55 | integer_sub_selector, 56 | integer_mul_selector, 57 | integer_div_selector, 58 | } 59 | } 60 | } 61 | 62 | /// Configuration elements for the circuit are defined here. 63 | #[derive(Debug, Clone)] 64 | pub struct EccEqualConfig { 65 | int_eq: IntegerEqualConfig, 66 | main: MainConfig, 67 | } 68 | 69 | impl EccEqualConfig { 70 | /// Constructor for Ecc equality config 71 | pub fn new(main: MainConfig, int_eq: IntegerEqualConfig) -> Self { 72 | Self { int_eq, main } 73 | } 74 | } 75 | 76 | /// Configuration elements for the circuit are defined here. 77 | #[derive(Debug, Clone)] 78 | pub struct EccUnreducedLadderConfig { 79 | /// Constructs selectors from different circuits. 80 | integer_add_selector: Selector, 81 | integer_sub_selector: Selector, 82 | integer_mul_selector: Selector, 83 | integer_div_selector: Selector, 84 | } 85 | 86 | impl EccUnreducedLadderConfig { 87 | /// Construct a new config given the selector of child chips 88 | pub fn new( 89 | integer_add_selector: Selector, integer_sub_selector: Selector, 90 | integer_mul_selector: Selector, integer_div_selector: Selector, 91 | ) -> Self { 92 | Self { 93 | integer_add_selector, 94 | integer_sub_selector, 95 | integer_mul_selector, 96 | integer_div_selector, 97 | } 98 | } 99 | } 100 | 101 | /// Configuration elements for the circuit are defined here. 102 | #[derive(Debug, Clone)] 103 | pub struct EccTableSelectConfig { 104 | /// Constructs config from main circuit. 105 | main: MainConfig, 106 | } 107 | 108 | impl EccTableSelectConfig { 109 | /// Construct a new config given the selector of child chips 110 | pub fn new(main: MainConfig) -> Self { 111 | Self { main } 112 | } 113 | } 114 | 115 | /// Configuration elements for the circuit are defined here. 116 | #[derive(Debug, Clone)] 117 | pub struct EccMulConfig { 118 | /// Constructs configs and selector from different circuits. 119 | ladder: EccUnreducedLadderConfig, 120 | add: EccAddConfig, 121 | double: EccDoubleConfig, 122 | table_select: EccTableSelectConfig, 123 | bits2num: Selector, 124 | } 125 | 126 | impl EccMulConfig { 127 | /// Construct a new config given the selector of child chips 128 | pub fn new( 129 | ladder: EccUnreducedLadderConfig, add: EccAddConfig, double: EccDoubleConfig, 130 | table_select: EccTableSelectConfig, bits2num: Selector, 131 | ) -> Self { 132 | Self { ladder, add, double, table_select, bits2num } 133 | } 134 | } 135 | 136 | /// Configuration elements for the circuit are defined here. 137 | #[derive(Debug, Clone)] 138 | pub struct EccBatchedMulConfig { 139 | /// Constructs configs and selector from different circuits. 140 | pub(crate) add: EccAddConfig, 141 | double: EccDoubleConfig, 142 | bits2num: Selector, 143 | } 144 | 145 | impl EccBatchedMulConfig { 146 | /// Construct a new config 147 | pub fn new(add: EccAddConfig, double: EccDoubleConfig, bits2num: Selector) -> Self { 148 | Self { add, double, bits2num } 149 | } 150 | } 151 | 152 | #[derive(Clone, Debug)] 153 | /// Config for Aux assigner 154 | pub struct AuxConfig { 155 | ecc_double: EccDoubleConfig, 156 | } 157 | 158 | impl AuxConfig { 159 | /// AuxConfig constructor 160 | pub fn new(ecc_double: EccDoubleConfig) -> Self { 161 | Self { ecc_double } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /eigentrust-zk/src/poseidon/native/mod.rs: -------------------------------------------------------------------------------- 1 | /// Native sponge implementation 2 | pub mod sponge; 3 | 4 | use crate::{params::hasher::RoundParams, FieldExt, Hasher}; 5 | use std::marker::PhantomData; 6 | 7 | /// Constructs objects. 8 | #[derive(Clone)] 9 | pub struct Poseidon 10 | where 11 | P: RoundParams, 12 | { 13 | /// Constructs an array for the inputs. 14 | inputs: [F; WIDTH], 15 | /// Constructs a phantom data for the parameters. 16 | _params: PhantomData

, 17 | } 18 | 19 | impl Poseidon 20 | where 21 | P: RoundParams, 22 | { 23 | /// Create the objects. 24 | pub fn new(inputs: [F; WIDTH]) -> Self { 25 | Poseidon { inputs, _params: PhantomData } 26 | } 27 | 28 | /// The Hades Design Strategy for Hashing. 29 | /// Mixing rounds with half-full S-box layers and 30 | /// rounds with partial S-box layers. 31 | /// More detailed explanation for 32 | /// The Round Function (TRF) and Hades: 33 | /// https://eprint.iacr.org/2019/458.pdf#page=5 34 | pub fn permute(&self) -> [F; WIDTH] { 35 | let full_rounds = P::full_rounds(); 36 | let half_full_rounds = full_rounds / 2; 37 | let partial_rounds = P::partial_rounds(); 38 | let round_constants = P::round_constants(); 39 | let total_count = P::round_constants_count(); 40 | 41 | let first_round_end = half_full_rounds * WIDTH; 42 | let first_round_constants = &round_constants[0..first_round_end]; 43 | 44 | let second_round_end = first_round_end + partial_rounds * WIDTH; 45 | let second_round_constants = &round_constants[first_round_end..second_round_end]; 46 | 47 | let third_round_constants = &round_constants[second_round_end..total_count]; 48 | 49 | let mut state = self.inputs; 50 | for round in 0..half_full_rounds { 51 | let round_consts = P::load_round_constants(round, first_round_constants); 52 | // 1. step for the TRF. 53 | // AddRoundConstants step. 54 | state = P::apply_round_constants(&state, &round_consts); 55 | // Applying S-boxes for the full round. 56 | for state in state.iter_mut().take(WIDTH) { 57 | // 2. step for the TRF. 58 | // SubWords step. 59 | *state = P::sbox_f(*state); 60 | } 61 | // 3. step for the TRF. 62 | // MixLayer step. 63 | state = P::apply_mds(&state); 64 | } 65 | 66 | for round in 0..partial_rounds { 67 | let round_consts = P::load_round_constants(round, second_round_constants); 68 | // 1. step for the TRF. 69 | // AddRoundConstants step. 70 | state = P::apply_round_constants(&state, &round_consts); 71 | // Applying single S-box for the partial round. 72 | // 2. step for the TRF. 73 | // SubWords step, denoted by S-box. 74 | state[0] = P::sbox_f(state[0]); 75 | // 3. step for the TRF. 76 | // MixLayer step. 77 | state = P::apply_mds(&state); 78 | } 79 | 80 | for round in 0..half_full_rounds { 81 | let round_consts = P::load_round_constants(round, third_round_constants); 82 | // 1. step for the TRF. 83 | // AddRoundConstants step. 84 | state = P::apply_round_constants(&state, &round_consts); 85 | // Applying S-boxes for the full round. 86 | for state in state.iter_mut().take(WIDTH) { 87 | // 2. step for the TRF. 88 | // SubWords step, denoted by S-box. 89 | *state = P::sbox_f(*state); 90 | } 91 | // 3. step for the TRF. 92 | // MixLayer step. 93 | state = P::apply_mds(&state); 94 | } 95 | 96 | state 97 | } 98 | } 99 | 100 | impl Hasher for Poseidon 101 | where 102 | P: RoundParams, 103 | { 104 | fn new(inputs: [F; WIDTH]) -> Self { 105 | Self::new(inputs) 106 | } 107 | 108 | fn finalize(&self) -> [F; WIDTH] { 109 | Self::permute(self) 110 | } 111 | } 112 | 113 | #[cfg(test)] 114 | mod test { 115 | use super::*; 116 | use crate::params::hasher::{hex_to_field, poseidon_bn254_5x5::Params}; 117 | use halo2::halo2curves::bn256::Fr; 118 | 119 | type TestPoseidon = Poseidon; 120 | 121 | #[test] 122 | fn test_native_poseidon_5x5() { 123 | // Testing 5x5 input. 124 | let inputs: [Fr; 5] = [ 125 | "0x0000000000000000000000000000000000000000000000000000000000000000", 126 | "0x0000000000000000000000000000000000000000000000000000000000000001", 127 | "0x0000000000000000000000000000000000000000000000000000000000000002", 128 | "0x0000000000000000000000000000000000000000000000000000000000000003", 129 | "0x0000000000000000000000000000000000000000000000000000000000000004", 130 | ] 131 | .map(|n| hex_to_field(n)); 132 | 133 | let outputs: [Fr; 5] = [ 134 | "0x299c867db6c1fdd79dcefa40e4510b9837e60ebb1ce0663dbaa525df65250465", 135 | "0x1148aaef609aa338b27dafd89bb98862d8bb2b429aceac47d86206154ffe053d", 136 | "0x24febb87fed7462e23f6665ff9a0111f4044c38ee1672c1ac6b0637d34f24907", 137 | "0x0eb08f6d809668a981c186beaf6110060707059576406b248e5d9cf6e78b3d3e", 138 | "0x07748bc6877c9b82c8b98666ee9d0626ec7f5be4205f79ee8528ef1c4a376fc7", 139 | ] 140 | .map(|n| hex_to_field(n)); 141 | 142 | let poseidon = TestPoseidon::new(inputs); 143 | 144 | let out = poseidon.permute(); 145 | 146 | assert_eq!(out, outputs); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /eigentrust-zk/src/params/rns/secp256k1.rs: -------------------------------------------------------------------------------- 1 | /// We implement two structs one for the Secpk256k1 Base Field as the wrong 2 | /// field and one for the Secp256k1 Scalar Field as the wrong field. The native 3 | /// field is the BN256 scalar field. The reason for implementing both these 4 | /// structs is that ECDSA verification contains operations in both fields. 5 | /// 6 | /// Secp25k1 aux points 7 | /// to_add.x: 0x25968a86095277f8a984c449dc3447d5b2007a27b9eece0db6fb9ae69217bae0 8 | /// to_add.y: 0xdb08482232d4e3cf46c81d9b71d2247b87c83eb80e46c645758b5bfd51a955d8 9 | /// to_sub.x: 0xf6530b63da8d89214d6a0cfe38f7294317ebe6f8cd408c9617a123c2b0a7b025 10 | /// to_sub.y: 0x0903073bbd64df08681cf59bf4689b77e18b198eb3833371e39cf322ff8f1de3 11 | /// 12 | /// Wrong Modulus in Native Modulus for Base Field: https://www.wolframalpha.com/input?\ 13 | /// i=115792089237316195423570985008687907853269984665640564039457584007908834671663+mod+\ 14 | /// 21888242871839275222246405745257275088548364400416034343698204186575808495617 15 | /// https://www.wolframalpha.com/input?i=6350874878119819312338956282401532410528162663560392320966563075029792193578+in+hex 16 | /// 17 | /// Negative Wrong Modulus for Base Field: https://www.wolframalpha.com/input?\ 18 | /// i=-115792089237316195423570985008687907853269984665640564039457584007908834671663+mod+2%5E272 19 | /// 20 | /// Wrong Modulus in Native Modulus for Scalar Field: https://www.wolframalpha.com/input?\ 21 | /// i=115792089237316195423570985008687907852837564279074904382605163141518161494337+mod+\ 22 | /// 21888242871839275222246405745257275088548364400416034343698204186575808495617 23 | /// https://www.wolframalpha.com/input?i=6350874878119819312338956282401532410095742276994732664114142208639119016252+in+hex 24 | /// 25 | /// Negative Wrong Modulus for Scalar Field: https://www.wolframalpha.com/input?\ 26 | /// i=-115792089237316195423570985008687907852837564279074904382605163141518161494337+mod+2%5E272 27 | use super::*; 28 | use halo2::halo2curves::secp256k1::{Fp, Fq}; 29 | 30 | #[derive(Debug, Clone, PartialEq, Default)] 31 | /// Struct for the Secp256k1 Base and Scalar Field as the wrong field. 32 | /// From https://github.com/privacy-scaling-explorations/halo2curves/blob/main/src/secp256k1/fp.rs 33 | /// From https://github.com/privacy-scaling-explorations/halo2curves/blob/main/src/secp256k1/fq.rs 34 | pub struct Secp256k1_4_68; 35 | 36 | impl RnsParams for Secp256k1_4_68 { 37 | fn wrong_modulus() -> BigUint { 38 | BigUint::from_str( 39 | "115792089237316195423570985008687907853269984665640564039457584007908834671663", 40 | ) 41 | .unwrap() 42 | } 43 | 44 | fn wrong_modulus_in_native_modulus() -> Fr { 45 | Fr::from_raw([ 46 | 0xac96341b4ffffc2a, 0x36fc76959f60cd29, 0x666ea36f7879462e, 0x0e0a77c19a07df2f, 47 | ]) 48 | } 49 | 50 | fn negative_wrong_modulus_decomposed() -> [Fr; 4] { 51 | let limb0 = Fr::from_u128(4294968273); 52 | let limb1 = Fr::from_u128(0); 53 | let limb2 = Fr::from_u128(0); 54 | let limb3 = Fr::from_u128(295143401579725455360); 55 | [limb0, limb1, limb2, limb3] 56 | } 57 | 58 | fn right_shifters() -> [Fr; 4] { 59 | let limb0 = Fr::from_u128(1); 60 | let limb1 = Fr::from_raw([ 61 | 0xf8e4610fb396ee5, 0xb42e346981868e48, 0x1dbc9c192fc7933a, 0xb603a5609b3f6f8, 62 | ]); 63 | let limb2 = Fr::from_raw([ 64 | 0x568bea8e0766f9dd, 0xa31a140f219532a9, 0x1a908db2cea9b991, 0x1b7c016fe8acfaed, 65 | ]); 66 | let limb3 = Fr::from_raw([ 67 | 0x769b0bf04e2f27cc, 0x55a33201cd88df51, 0x338287b1e0bedd99, 0x523513296c10199, 68 | ]); 69 | [limb0, limb1, limb2, limb3] 70 | } 71 | 72 | fn left_shifters() -> [Fr; 4] { 73 | let limb0 = Fr::from_u128(1); 74 | let limb1 = Fr::from_raw([0x0, 0x10, 0x0, 0x0]); 75 | let limb2 = Fr::from_raw([0x0, 0x0, 0x100, 0x0]); 76 | let limb3 = Fr::from_raw([0x0, 0x0, 0x0, 0x1000]); 77 | [limb0, limb1, limb2, limb3] 78 | } 79 | } 80 | 81 | impl RnsParams for Secp256k1_4_68 { 82 | fn wrong_modulus() -> BigUint { 83 | BigUint::from_str( 84 | "115792089237316195423570985008687907852837564279074904382605163141518161494337", 85 | ) 86 | .unwrap() 87 | } 88 | 89 | fn wrong_modulus_in_native_modulus() -> Fr { 90 | Fr::from_raw([ 91 | 0x6c6892a92036413c, 0xf1ab537c4ea96d65, 0x666ea36f7879462c, 0x0e0a77c19a07df2f, 92 | ]) 93 | } 94 | 95 | fn negative_wrong_modulus_decomposed() -> [Fr; 4] { 96 | let limb0 = Fr::from_u128(78411506203312635583); 97 | let limb1 = Fr::from_u128(1465097257942218236); 98 | let limb2 = Fr::from_u128(0); 99 | let limb3 = Fr::from_u128(295143401579725455360); 100 | [limb0, limb1, limb2, limb3] 101 | } 102 | 103 | fn right_shifters() -> [Fr; 4] { 104 | let limb0 = Fr::from_u128(1); 105 | let limb1 = Fr::from_raw([ 106 | 0xf8e4610fb396ee5, 0xb42e346981868e48, 0x1dbc9c192fc7933a, 0xb603a5609b3f6f8, 107 | ]); 108 | let limb2 = Fr::from_raw([ 109 | 0x568bea8e0766f9dd, 0xa31a140f219532a9, 0x1a908db2cea9b991, 0x1b7c016fe8acfaed, 110 | ]); 111 | let limb3 = Fr::from_raw([ 112 | 0x769b0bf04e2f27cc, 0x55a33201cd88df51, 0x338287b1e0bedd99, 0x523513296c10199, 113 | ]); 114 | [limb0, limb1, limb2, limb3] 115 | } 116 | 117 | fn left_shifters() -> [Fr; 4] { 118 | let limb0 = Fr::from_u128(1); 119 | let limb1 = Fr::from_raw([0x0, 0x10, 0x0, 0x0]); 120 | let limb2 = Fr::from_raw([0x0, 0x0, 0x100, 0x0]); 121 | let limb3 = Fr::from_raw([0x0, 0x0, 0x0, 0x1000]); 122 | [limb0, limb1, limb2, limb3] 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /eigentrust-zk/src/params/hasher/mod.rs: -------------------------------------------------------------------------------- 1 | /// Poseidon Bn254 with WIDTH = 10 and EXPONENTIATION = 5 2 | pub mod poseidon_bn254_10x5; 3 | /// Poseidon Bn254 with WIDTH = 5 and EXPONENTIATION = 5 4 | pub mod poseidon_bn254_5x5; 5 | /// Rescue Prime Bn254 with WIDTH = 5 and EXPONENTIATION = 5 6 | pub mod rescue_prime_bn254_5x5; 7 | 8 | use std::fmt::Debug; 9 | 10 | use crate::FieldExt; 11 | use halo2::{circuit::Value, plonk::Expression}; 12 | 13 | /// Trait definition of Round parameters of Poseidon 14 | pub trait RoundParams: Sbox + Clone + Debug { 15 | /// Returns a number of full rounds. 16 | fn full_rounds() -> usize; 17 | /// Returns a number of partial rounds. 18 | fn partial_rounds() -> usize; 19 | 20 | /// Returns total count size. 21 | fn round_constants_count() -> usize { 22 | let partial_rounds = Self::partial_rounds(); 23 | let full_rounds = Self::full_rounds(); 24 | (partial_rounds + full_rounds) * WIDTH 25 | } 26 | 27 | /// Returns round constants array to be used in permutation. 28 | fn round_constants() -> Vec { 29 | let round_constants_raw = Self::round_constants_raw(); 30 | let round_constants: Vec = round_constants_raw.iter().map(|x| hex_to_field(x)).collect(); 31 | assert_eq!(round_constants.len(), Self::round_constants_count()); 32 | round_constants 33 | } 34 | 35 | /// Returns relevant constants for the given round. 36 | fn load_round_constants(round: usize, round_consts: &[F]) -> [F; WIDTH] { 37 | let mut result = [F::ZERO; WIDTH]; 38 | for i in 0..WIDTH { 39 | result[i] = round_consts[round * WIDTH + i]; 40 | } 41 | result 42 | } 43 | 44 | /// Returns MDS matrix with a size of WIDTH x WIDTH. 45 | fn mds() -> [[F; WIDTH]; WIDTH] { 46 | let mds_raw = Self::mds_raw(); 47 | mds_raw.map(|row| row.map(|item| hex_to_field(item))) 48 | } 49 | 50 | /// Returns round constants in its hex string form. 51 | fn round_constants_raw() -> Vec<&'static str>; 52 | /// Returns MDS martrix in its hex string form. 53 | fn mds_raw() -> [[&'static str; WIDTH]; WIDTH]; 54 | /// Add round constants to the state values 55 | /// for the AddRoundConstants operation. 56 | fn apply_round_constants(state: &[F; WIDTH], round_consts: &[F; WIDTH]) -> [F; WIDTH] { 57 | let mut next_state = [F::ZERO; WIDTH]; 58 | for i in 0..WIDTH { 59 | let state = state[i]; 60 | let round_const = round_consts[i]; 61 | let sum = state + round_const; 62 | next_state[i] = sum; 63 | } 64 | next_state 65 | } 66 | /// Compute MDS matrix for MixLayer operation. 67 | fn apply_mds(state: &[F; WIDTH]) -> [F; WIDTH] { 68 | let mut new_state = [F::ZERO; WIDTH]; 69 | let mds = Self::mds(); 70 | for i in 0..WIDTH { 71 | for j in 0..WIDTH { 72 | let mds_ij = &mds[i][j]; 73 | let m_product = state[j] * mds_ij; 74 | new_state[i] += m_product; 75 | } 76 | } 77 | new_state 78 | } 79 | 80 | /// Add round constants to the state values 81 | /// for the AddRoundConstants operation. 82 | fn apply_round_constants_val( 83 | state_cells: &[Value; WIDTH], round_const_values: &[Value; WIDTH], 84 | ) -> [Value; WIDTH] { 85 | let mut next_state = [Value::unknown(); WIDTH]; 86 | for i in 0..WIDTH { 87 | let round_const = &round_const_values[i]; 88 | let sum = *round_const + state_cells[i]; 89 | next_state[i] = sum; 90 | } 91 | next_state 92 | } 93 | 94 | /// Compute MDS matrix for MixLayer operation. 95 | fn apply_mds_val(next_state: &[Value; WIDTH]) -> [Value; WIDTH] { 96 | let mut new_state = [Value::known(F::ZERO); WIDTH]; 97 | let mds = Self::mds(); 98 | for i in 0..WIDTH { 99 | for j in 0..WIDTH { 100 | let mds_ij = &Value::known(mds[i][j]); 101 | let m_product = next_state[j] * mds_ij; 102 | new_state[i] = new_state[i] + m_product; 103 | } 104 | } 105 | new_state 106 | } 107 | 108 | /// Add round constants expression to the state values 109 | /// expression for the AddRoundConstants operation in the circuit. 110 | fn apply_round_constants_expr( 111 | curr_state: &[Expression; WIDTH], round_constants: &[Expression; WIDTH], 112 | ) -> [Expression; WIDTH] { 113 | let mut exprs = [(); WIDTH].map(|_| Expression::Constant(F::ZERO)); 114 | for i in 0..WIDTH { 115 | exprs[i] = curr_state[i].clone() + round_constants[i].clone(); 116 | } 117 | exprs 118 | } 119 | 120 | /// Compute MDS matrix for MixLayer operation in the circuit. 121 | fn apply_mds_expr(exprs: &[Expression; WIDTH]) -> [Expression; WIDTH] { 122 | let mut new_exprs = [(); WIDTH].map(|_| Expression::Constant(F::ZERO)); 123 | // Mat mul with MDS 124 | let mds = Self::mds(); 125 | for i in 0..WIDTH { 126 | for j in 0..WIDTH { 127 | new_exprs[i] = new_exprs[i].clone() + (exprs[j].clone() * mds[i][j]); 128 | } 129 | } 130 | new_exprs 131 | } 132 | } 133 | 134 | /// Trait definition for Sbox operation of Poseidon 135 | pub trait Sbox { 136 | /// Returns the S-box exponentiation for the expression. 137 | fn sbox_expr(exp: Expression) -> Expression; 138 | /// Returns the S-box exponentiation for the field element. 139 | fn sbox_f(f: F) -> F; 140 | /// Returns the S-box exponentiation of the inverse for the field element. 141 | fn sbox_inv_f(f: F) -> F; 142 | } 143 | 144 | /// Returns congruent field element for the given hex string. 145 | pub fn hex_to_field(s: &str) -> F { 146 | let s = &s[2..]; 147 | let mut bytes = hex::decode(s).expect("Invalid params"); 148 | bytes.reverse(); 149 | let mut bytes_wide: [u8; 64] = [0; 64]; 150 | bytes_wide[..bytes.len()].copy_from_slice(&bytes[..]); 151 | F::from_uniform_bytes(&bytes_wide) 152 | } 153 | -------------------------------------------------------------------------------- /eigentrust-zk/src/circuits/mod.rs: -------------------------------------------------------------------------------- 1 | use self::{ 2 | dynamic_sets::{ 3 | native::{EigenTrustSet as NativeEigenTrustSet, SignedAttestation}, 4 | EigenTrustSet, 5 | }, 6 | opinion::native::Opinion, 7 | threshold::{native::Threshold, ThresholdCircuit}, 8 | }; 9 | use crate::{ 10 | ecdsa::native::{EcdsaKeypair, PublicKey, Signature}, 11 | eddsa::EddsaChipset, 12 | edwards::params::BabyJubJub, 13 | params::{ 14 | ecc::{bn254::Bn254Params, secp256k1::Secp256k1Params}, 15 | hasher::poseidon_bn254_5x5::Params, 16 | rns::{bn256::Bn256_4_68, secp256k1::Secp256k1_4_68}, 17 | }, 18 | poseidon::{ 19 | native::{sponge::PoseidonSponge, Poseidon}, 20 | sponge::StatefulSpongeChipset, 21 | FullRoundChip, PartialRoundChip, PoseidonChipset, 22 | }, 23 | verifier::aggregator::native::NativeAggregator, 24 | }; 25 | use halo2::{ 26 | halo2curves::{ 27 | bn256::{Bn256, Fr as Scalar}, 28 | secp256k1::Secp256k1Affine, 29 | }, 30 | poly::kzg::commitment::KZGCommitmentScheme, 31 | }; 32 | use num_rational::BigRational; 33 | 34 | /// EigenTrustSet 35 | pub mod dynamic_sets; 36 | /// Opinion gadgets + native version 37 | pub mod opinion; 38 | /// Utility for checking the score threshold 39 | pub mod threshold; 40 | 41 | /// Number of peers in the set 42 | pub const NUM_NEIGHBOURS: usize = 4; 43 | /// Number of iterations to run until convergence 44 | pub const NUM_ITERATIONS: usize = 20; 45 | /// Intial score (pre-trust) 46 | pub const INITIAL_SCORE: u128 = 1000; 47 | /// Minimum peers for scores calculation 48 | pub const MIN_PEER_COUNT: usize = 2; 49 | /// Number of limbs for integers 50 | pub const NUM_LIMBS: usize = 4; 51 | /// Number of bits for integer limbs 52 | pub const NUM_BITS: usize = 68; 53 | /// Default width for the hasher used 54 | pub const HASHER_WIDTH: usize = 5; 55 | /// Number of limbs for representing big numbers in threshold checking. 56 | pub const NUM_DECIMAL_LIMBS: usize = 2; 57 | /// Number of digits of each limbs for threshold checking. 58 | pub const POWER_OF_TEN: usize = 72; 59 | /// Default polynomial degree for KZG parameters for EigenTrust circuit. 60 | pub const ET_PARAMS_K: u32 = 20; 61 | /// Default polynomial degree for KZG parameters for Threshold circuit. 62 | pub const TH_PARAMS_K: u32 = 21; 63 | 64 | /// KZG Commitment Scheme 65 | pub type KZGParams = KZGCommitmentScheme; 66 | /// Rational score 67 | pub type RationalScore = BigRational; 68 | /// Type alias for the native poseidon hasher with a width of 5 and bn254 params 69 | pub type PoseidonNativeHasher = Poseidon; 70 | /// Type alias for native poseidon sponge with a width of 5 and bn254 params 71 | pub type PoseidonNativeSponge = PoseidonSponge; 72 | /// Type alias for the poseidon hasher chip with a width of 5 and bn254 params 73 | pub type PoseidonHasher = PoseidonChipset; 74 | /// Partial rounds of permulation chip 75 | pub type PartialRoundHasher = PartialRoundChip; 76 | /// Full rounds of permuation chip 77 | pub type FullRoundHasher = FullRoundChip; 78 | /// Type alias for the poseidon spong chip with a width of 5 and bn254 params 79 | pub type SpongeHasher = StatefulSpongeChipset; 80 | /// Type alias for Eddsa chip on BabyJubJub elliptic curve 81 | pub type Eddsa = EddsaChipset; 82 | /// ECDSA public key. 83 | pub type ECDSAPublicKey = 84 | PublicKey; 85 | /// ECDSA keypair. 86 | pub type ECDSAKeypair = 87 | EcdsaKeypair; 88 | /// ECDSA signature. 89 | pub type ECDSASignature = Signature; 90 | /// Signed attestation 91 | pub type SignedAttestationSecp = 92 | SignedAttestation; 93 | /// Opinion for 4 neighbours 94 | pub type Opinion4 = Opinion< 95 | NUM_NEIGHBOURS, 96 | Secp256k1Affine, 97 | Scalar, 98 | NUM_LIMBS, 99 | NUM_BITS, 100 | Secp256k1_4_68, 101 | Secp256k1Params, 102 | PoseidonNativeHasher, 103 | PoseidonNativeSponge, 104 | >; 105 | /// Native Aggregator for set with 4 participants 106 | pub type NativeAggregator4 = NativeAggregator< 107 | Bn256, 108 | NUM_NEIGHBOURS, 109 | NUM_BITS, 110 | Bn256_4_68, 111 | PoseidonNativeSponge, 112 | Bn254Params, 113 | >; 114 | /// Native EigenTrust set with 4 participants 115 | pub type NativeEigenTrust4 = NativeEigenTrustSet< 116 | NUM_NEIGHBOURS, 117 | NUM_ITERATIONS, 118 | INITIAL_SCORE, 119 | Secp256k1Affine, 120 | Scalar, 121 | NUM_LIMBS, 122 | NUM_BITS, 123 | Secp256k1_4_68, 124 | Secp256k1Params, 125 | PoseidonNativeHasher, 126 | PoseidonNativeSponge, 127 | >; 128 | /// EigenTrust set with 4 participants 129 | pub type EigenTrust4 = EigenTrustSet< 130 | NUM_NEIGHBOURS, 131 | NUM_ITERATIONS, 132 | INITIAL_SCORE, 133 | Secp256k1Affine, 134 | Scalar, 135 | NUM_LIMBS, 136 | NUM_BITS, 137 | Secp256k1_4_68, 138 | Secp256k1Params, 139 | PoseidonHasher, 140 | PoseidonNativeHasher, 141 | SpongeHasher, 142 | >; 143 | /// Native Threshold for scores computed in EigenTrust4 144 | pub type NativeThreshold4 = 145 | Threshold; 146 | /// Threshold Circuit for scores computed in EigenTrust4 147 | pub type Threshold4 = ThresholdCircuit< 148 | Bn256, 149 | NUM_DECIMAL_LIMBS, 150 | POWER_OF_TEN, 151 | NUM_NEIGHBOURS, 152 | INITIAL_SCORE, 153 | NUM_LIMBS, 154 | NUM_BITS, 155 | Bn256_4_68, 156 | Bn254Params, 157 | SpongeHasher, 158 | PoseidonNativeSponge, 159 | Params, 160 | >; 161 | -------------------------------------------------------------------------------- /eigentrust-zk/src/params/hasher/rescue_prime_bn254_5x5.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use halo2::halo2curves::bn256::Fr; 3 | 4 | #[derive(Clone, Debug)] 5 | /// Configures a structure. 6 | pub struct Params; 7 | 8 | impl Sbox for Params { 9 | fn sbox_expr(exp: Expression) -> Expression { 10 | let exp2 = exp.clone() * exp.clone(); 11 | let exp4 = exp2.clone() * exp2; 12 | exp4 * exp 13 | } 14 | 15 | fn sbox_f(f: F) -> F { 16 | let f2 = f * f; 17 | let f4 = f2 * f2; 18 | f4 * f 19 | } 20 | 21 | fn sbox_inv_f(f: F) -> F { 22 | // Pow by inverse of 5 23 | f.pow([ 24 | 14981214993055009997, 6006880321387387405, 10624953561019755799, 2789598613442376532, 25 | ]) 26 | } 27 | } 28 | 29 | impl RoundParams for Params { 30 | fn partial_rounds() -> usize { 31 | 0 32 | } 33 | 34 | fn full_rounds() -> usize { 35 | 8 36 | } 37 | 38 | fn round_constants_raw() -> Vec<&'static str> { 39 | [ 40 | "0x2ca4a8f397401b89877423695357441aa6f56488841db4b55ffb8d911b947e44", 41 | "0x245a7b16a5b3b10cf19fbec864eb6fc2559d88e25ccc9b27b1c6b7dede3c9190", 42 | "0x19e20da5a280c6b52b228c4cc14bec5ac4e39f3fd464dbe6a0203de8b5d7d795", 43 | "0x0f8263b25f9b8a46cfd68979b111feecf4b734ef44093df98aeed58c36803238", 44 | "0x11d3d70e0ce66186372e7fe386ad3f5ccf2c811231720e3f0192a8b8995eedfe", 45 | "0x01cfc193c968b4c6d20ec2df4f110906a8360bc62150ca0400a53620699932bf", 46 | "0x2d69bbded4695ac9a93ff4a23a17c103376d0ea8c7c5663ad283ec8428b51e25", 47 | "0x071237ec067c09821a6045191b2281cbd82186de8ba1374cbc96b87dc1b61c07", 48 | "0x24161b3910f19b028ffadca01be8fa665dfccab8c044cdc3397cdaac1666a08d", 49 | "0x22e6c619a6ecf9a856dbe0e5306d0b99eb4d7ad1b1a6d33ce3ae4ef635dedfc6", 50 | "0x12fe8da5193f1d8310d56f9666c2818cb773104fb5e16daf89956ac6e0ec4cec", 51 | "0x103dc1169a48ca6394a5f58f327b5ec3b34f35f5be85be08def563c534614fbb", 52 | "0x0b40a5360e10b322fb67174e2df67079dd7abd217d125c0251482a15ae6e14eb", 53 | "0x13f6c28b697c88a387a69d712e32fbea0350e1a6233ef755df28168d434531b4", 54 | "0x10aa4f4aafc4ac840abf70f0efdd37a9e5b103152ff754a45e998243444177d4", 55 | "0x123fdba3acb01a1af93ab0beeb66dc7e3b6c455ede611a5eddd1c63da1306f32", 56 | "0x14b11a1f018a3684f810508727a472808f620d9b9cdd28708735267a3969fd93", 57 | "0x14e6815fc9838e55a9e8681bd8781d63eaa51f494ab1168b38fee42f33451297", 58 | "0x20d3e0623f8878b6ea3cc84c227edb0d7f92602593f0b19bceea9bbdfb044ce8", 59 | "0x2e866c6a7fbbbfddfeb0bda9f375ab1f7c406c21b165d46f1728149544ac7b2e", 60 | "0x0af23ac272eb2c056d6e0f10df465150330663a97e3ce6c90ab1224d32dddda7", 61 | "0x00e5e60f14b9d190aaa3e7944b90c1bbd231692e1dbf8e5869dad2d9f708e9c6", 62 | "0x17b44d163738b1ea585c1ec820ee91fe89cf92f9221bd46dc29c6bdf9542b90b", 63 | "0x28ef1c64cfa2b272bec8850b67f7c340cb146d9c084d59119d4befc9e4a683a4", 64 | "0x1c36102272038b2518169af737850e34d421e5b8e96c4f6d6496ee880adc544b", 65 | "0x19aa5606180777d0437629fb73fa97338875d2332d8d478df1de9061269092fa", 66 | "0x1aec324236183446d97fa67c33bcb6128b8a7dc85f1686f9d5bf2c34ce3663e1", 67 | "0x1411983e589c744567fe19e737b9d2c942923d466406de9642f401f72f1d3ec0", 68 | "0x0900bd14426240fc675ca4a3c84a2c85fe6e958ccf92ffa49a72d182ce67ff30", 69 | "0x0edfdabb93d0650b2d0da4aa90f83dd46a3d6d868534b611f01c6efde225570c", 70 | "0x05e1e0c7ce6bf5e75308fd960c058d0cf47da1bee7cf6c5f730a76aab750fa3e", 71 | "0x27902233323d1871fb83f97c40480264391deedb2fcee12864adf503d442f144", 72 | "0x034927884e7a8042701e54e5c08d97cb80a6875c45390c6c5dd25c642f3cf233", 73 | "0x168bec5a3792b86e6d5d946fe3697653401501b080e03fb4a5d5b643752ab01a", 74 | "0x24f01f489c63d0812decff6cbdebadbb23b3a469fe7b1ba29c82dd5cb15a0642", 75 | "0x0f629e3f29346352ae58ff95d2a2faaafe60abb5d0129c212024e1d299d69670", 76 | "0x0ad63006a8c4dc5183f961e485d1271865f7e217e2165f391f62a30cc0f31013", 77 | "0x0360d6a52b4059c7b6f1a9784f2f60e953cb143e265cd90c9edeb5db3c5f13d9", 78 | "0x2f17067d317d1ccc87726275d791e7c0e613a344b703171443a772cd745f4f68", 79 | "0x0784da44edbf903e6f5aa79f7b4591d39bfdf1f56c2ac7b5f2b0520fcdf4a4cc", 80 | ] 81 | .to_vec() 82 | } 83 | 84 | fn mds_raw() -> [[&'static str; 5]; 5] { 85 | [ 86 | [ 87 | "0x0d5365c702a1d156ecb069c1a5b6f3fefa94d552d01f19e0cea4ba91e4537c0b", 88 | "0x0e0ec9e239372ffcc147499c18127fcd14df23c28afea070d9a5ca5a8e2edad1", 89 | "0x139b19872a35491f40fb3ae9e5d85fc5af24bd86e334c4dd256da7318de3d3b0", 90 | "0x0b9abb8cac6d528f3cd7583d153402fb66d1bdd7fad8bfc993ffbd462c26dcb9", 91 | "0x25ef5785f15274631fcb54ee87fe105c042eaf901054d5cb2ea1f140c7795eda", 92 | ], 93 | [ 94 | "0x116b9e8295f0086e020f035edc4ec2b5e65187597edb3d37de19589968b8d95a", 95 | "0x17f2bb6779aa19aa40b9903101f34f3079b64947538c68eec839700e4d3f4dc6", 96 | "0x2748eafccb493093f5b6b8eed565ed7c5d6a196cfd09afaadcc1e4ef41723ff2", 97 | "0x177e934ef325737ab20d869f5570b6d442938a9ba31a69931fdce3b5681f3dab", 98 | "0x20be0041859b35e0ebfa48cb6b55623cdb72cca5866939f0854e2ec3271c9d28", 99 | ], 100 | [ 101 | "0x1c794b13d4b66d9883849539f556b020117666918dae1210cc4c713c4137c980", 102 | "0x196241af44073291925c16017a812df6b64258f4b0e570fb12381945105fd50f", 103 | "0x0820fbc1ecd8b272543eb1fdcfb2a04a8897b42d545ad050fa4743ba248df73b", 104 | "0x1d630dd3a25b7aaa9684f4f641b9d763ebd202d0dc44245aaac81e37379c822b", 105 | "0x0d79e1f5cf020c1d584cb948c22d55a9d5f98c9b2b6eae028d346fa5cb0ecebc", 106 | ], 107 | [ 108 | "0x1f9743a6cceebff6284adf5b93119ffa18ac050da856193e19f9f0d8b5673ea4", 109 | "0x023d9a2dbf719235ecd411ae6234fb6fe55a1ff2be57568089c198aa54888aef", 110 | "0x11a2ed471ebf4a4fb2471cf834a842b85c09ebadd54cb6decd9d42a1a026b7c4", 111 | "0x2b32d181fb89f8afa92d9139bee4d4eabfb1b2e3831499dcf1b261913c3b978c", 112 | "0x1894483c1c129daae2eae33b791ab6709b2d387453f531f5def0fa4c89d07947", 113 | ], 114 | [ 115 | "0x14de4fd37b770cabbb10d2ca60e89e300d24cc1c04e936d6d3bb66ac2953769b", 116 | "0x2caaef6f5c7fdc4c9b039d78d96c05eacf06e2e72b108d420001fcb9ba1883ae", 117 | "0x144dd8dffcabe999775811f306b50ccb43bcb1d5c6a661e89d095bd2aed983c5", 118 | "0x2b535d04594ad253957b83629c6899a05f0332764aa5ab5708ddb230a9f7e722", 119 | "0x0819a28061b1b476f1c62584e24040eea0c8a6afd5e052b8823eb3bee7ecf144", 120 | ], 121 | ] 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /eigentrust/src/eth.rs: -------------------------------------------------------------------------------- 1 | //! # Ethereum Module. 2 | //! 3 | //! This module provides types and functionalities for general ethereum interactions. 4 | 5 | use crate::{att_station::AttestationStation, error::EigenError, ClientSigner, Scalar}; 6 | use eigentrust_zk::{ 7 | circuits::{ECDSAKeypair, ECDSAPublicKey}, 8 | halo2::halo2curves::{secp256k1::Secp256k1Affine, CurveAffine}, 9 | }; 10 | use ethers::{ 11 | abi::Address, 12 | prelude::k256::ecdsa::SigningKey, 13 | signers::coins_bip39::{English, Mnemonic}, 14 | }; 15 | use std::sync::Arc; 16 | 17 | /// Deploys the AttestationStation contract. 18 | pub async fn deploy_as(signer: Arc) -> Result { 19 | let res = AttestationStation::deploy(signer, ()) 20 | .map_err(|e| EigenError::ContractError(e.to_string()))?; 21 | 22 | let transaction = res.send().await.map_err(|e| EigenError::TransactionError(e.to_string()))?; 23 | 24 | Ok(transaction.address()) 25 | } 26 | 27 | /// Returns a vector of ECDSA key pairs derived from the given mnemonic phrase. 28 | pub fn ecdsa_keypairs_from_mnemonic( 29 | mnemonic: &str, count: u32, 30 | ) -> Result, EigenError> { 31 | let mnemonic = Mnemonic::::new_from_phrase(mnemonic) 32 | .map_err(|e| EigenError::ParsingError(e.to_string()))?; 33 | let mut keys = Vec::new(); 34 | 35 | // The hardened derivation flag. 36 | const BIP32_HARDEN: u32 = 0x8000_0000; 37 | 38 | for i in 0..count { 39 | // Set standard derivation path 44'/60'/0'/0/i 40 | let derivation_path: Vec = 41 | vec![44 + BIP32_HARDEN, 60 + BIP32_HARDEN, BIP32_HARDEN, 0, i]; 42 | 43 | let private_key = mnemonic 44 | .derive_key(&derivation_path, None) 45 | .map_err(|e| EigenError::KeysError(e.to_string()))?; 46 | let signing_key: &SigningKey = private_key.as_ref(); 47 | 48 | let mut pk_bytes: [u8; 32] = [0; 32]; 49 | pk_bytes.copy_from_slice(&signing_key.to_bytes()[0..32]); 50 | pk_bytes.reverse(); 51 | 52 | let scalar_pk_option = ::ScalarExt::from_bytes(&pk_bytes); 53 | 54 | let scalar_pk = match scalar_pk_option.is_some().into() { 55 | true => scalar_pk_option.unwrap(), 56 | false => { 57 | return Err(EigenError::ParsingError( 58 | "Failed to construct scalar private key from bytes".to_string(), 59 | )) 60 | }, 61 | }; 62 | 63 | keys.push(ECDSAKeypair::from_private_key(scalar_pk)); 64 | } 65 | 66 | Ok(keys) 67 | } 68 | 69 | /// Constructs an Ethereum address for the given ECDSA public key. 70 | pub fn address_from_ecdsa_key(pub_key: &ECDSAPublicKey) -> Address { 71 | let mut address_bytes = pub_key.to_address().to_bytes(); 72 | address_bytes[..20].reverse(); 73 | Address::from_slice(&address_bytes[0..20]) 74 | } 75 | 76 | /// Constructs a Scalar from the given Ethereum address. 77 | pub fn scalar_from_address(address: &Address) -> Result { 78 | let mut address_fixed = address.to_fixed_bytes(); 79 | address_fixed.reverse(); 80 | 81 | let mut address_bytes = [0u8; 32]; 82 | address_bytes[..address_fixed.len()].copy_from_slice(&address_fixed); 83 | 84 | let about_opt = Scalar::from_bytes(&address_bytes); 85 | let about = match about_opt.is_some().into() { 86 | true => about_opt.unwrap(), 87 | false => { 88 | return Err(EigenError::ParsingError( 89 | "Failed to convert address to scalar".to_string(), 90 | )) 91 | }, 92 | }; 93 | 94 | Ok(about) 95 | } 96 | 97 | #[cfg(test)] 98 | mod tests { 99 | use crate::{eth::*, Client, SecpScalar}; 100 | use ethers::{ 101 | types::H160, 102 | utils::{hex, Anvil}, 103 | }; 104 | use std::str::FromStr; 105 | 106 | const TEST_MNEMONIC: &'static str = 107 | "test test test test test test test test test test test junk"; 108 | const TEST_AS_ADDRESS: &'static str = "0x5fbdb2315678afecb367f032d93f642f64180aa3"; 109 | const TEST_CHAIN_ID: u32 = 31337; 110 | 111 | #[tokio::test] 112 | async fn test_deploy_as() { 113 | let anvil = Anvil::new().spawn(); 114 | let node_url = anvil.endpoint().to_string(); 115 | let client = Client::new( 116 | TEST_MNEMONIC.to_string(), 117 | TEST_CHAIN_ID, 118 | Address::from_str(TEST_AS_ADDRESS).unwrap().to_fixed_bytes(), 119 | H160::zero().to_fixed_bytes(), 120 | node_url, 121 | ); 122 | 123 | // Deploy 124 | let res = deploy_as(client.signer).await; 125 | assert!(res.is_ok()); 126 | 127 | drop(anvil); 128 | } 129 | 130 | #[test] 131 | fn test_ecdsa_keypairs_from_mnemonic() { 132 | // Expected address 133 | let address_str = "f39Fd6e51aad88F6F4ce6aB8827279cffFb92266"; 134 | let expected_address_bytes: [u8; 20] = 135 | hex::decode(address_str).expect("Decoding failed").try_into().expect("Wrong length"); 136 | 137 | let keypairs = ecdsa_keypairs_from_mnemonic(TEST_MNEMONIC, 1).unwrap(); 138 | 139 | // Get the little-endian address from the public key. 140 | let le_address = keypairs[0].public_key.to_address().to_bytes(); 141 | 142 | // Convert the first 20 bytes of the little-endian address back to the original format. 143 | let mut rec_address_bytes: [u8; 20] = [0; 20]; 144 | rec_address_bytes.copy_from_slice(&le_address[0..20]); 145 | rec_address_bytes.reverse(); 146 | 147 | assert_eq!(rec_address_bytes, expected_address_bytes); 148 | } 149 | 150 | #[test] 151 | fn test_address_from_public_key() { 152 | // Test private key 153 | let private_key_str = "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"; 154 | let mut private_key_bytes: [u8; 32] = hex::decode(private_key_str) 155 | .expect("Decoding failed") 156 | .try_into() 157 | .expect("Wrong length"); 158 | 159 | // Expected address 160 | let address_str = "f39Fd6e51aad88F6F4ce6aB8827279cffFb92266"; 161 | let expected_address_bytes: [u8; 20] = 162 | hex::decode(address_str).expect("Decoding failed").try_into().expect("Wrong length"); 163 | 164 | // Build scalar 165 | private_key_bytes.reverse(); 166 | let private_key_fq = SecpScalar::from_bytes(&private_key_bytes).unwrap(); 167 | 168 | let keypair = ECDSAKeypair::from_private_key(private_key_fq); 169 | 170 | let recovered_address = address_from_ecdsa_key(&keypair.public_key); 171 | 172 | assert_eq!(recovered_address.to_fixed_bytes(), expected_address_bytes); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /docs/4_algorithm.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: A higher level description of Eigen Trust protocol. 3 | --- 4 | 5 | # Algorithm 6 | 7 | The name Eigen Trust originates from the [original paper](https://nlp.stanford.edu/pubs/eigentrust.pdf). 8 | 9 | It relates to the way the reputation score is calculated, which is done the following way: 10 | 11 | Suppose we have a vector `s` that contains the initial scores for 5 peers: 12 | ``` 13 | s = [1000, 2000, 500, 300, 200] 14 | ``` 15 | 16 | We will refer to peers by their index in this set. e.g. Peer 0 is a peer on index `0` (which has reputation of `1000`) 17 | 18 | Now let's suppose we have an opinion matrix: 19 | ``` 20 | op0 = [0.0, 0.2, 0.3, 0.5, 0.0] - Peer 0 opinions 21 | op1 = [0.1, 0.0, 0.1, 0.1, 0.7] - Peer 1 opinions 22 | op2 = [0.4, 0.1, 0.0, 0.2, 0.3] - Peer 2 opinions 23 | op3 = [0.1, 0.1, 0.7, 0.0, 0.1] - Peer 3 opinions 24 | op4 = [0.3, 0.1, 0.4, 0.2, 0.0] - Peer 4 opinions 25 | ``` 26 | 27 | The matrix define above describes the distribution of the reputation owned by the peers. Notice that the sum of the distribution of one peer is equal to `1.0`, and we are also not giving ourselves any amount. 28 | 29 | Now, let's turn that that distribution into scores: 30 | 31 | We take the score of Peer N and multiply it with each element in `op[n]`: 32 | ``` 33 | sop0 = s[0] * op0 = [ 0, 200, 300, 500, 0] 34 | sop1 = s[1] * op1 = [200, 0, 200, 200, 1400] 35 | sop2 = s[2] * op2 = [200, 50, 0, 100, 150] 36 | sop3 = s[3] * op3 = [ 30, 30, 210, 0, 30] 37 | sop4 = s[4] * op4 = [ 60, 20, 80, 40, 0] 38 | ``` 39 | 40 | Now, from this new matrix we can get the new scores for Peer 0: 41 | ``` 42 | s0 = sop0[0] + sop1[0] + sop2[0] + sop3[0] + sop4[0] = 0 + 200 + 200 + 30 + 60 = 490 43 | ``` 44 | 45 | If we apply the same for formula we can get the new scores for all peers: 46 | ``` 47 | s = [490, 300, 790, 840, 1580] 48 | ``` 49 | 50 | Notice that amount of reputation in the system is always the same (compare with initial `s`). The reputation cannot be created or destroyed, it can only be allocated. 51 | 52 | Everything we did was just one iteration of Eigen Trust algorithm. If we apply the same process throughout several iterations, the reputation score of each peer will not change much further after a certain point. When that happens we say that the reputation scores has **converged.** 53 | 54 | Here is an algorithm example written in Rust: 55 | ```rust 56 | let mut s: [f32; 5] = [1000., 2000., 500., 300., 200.]; 57 | 58 | const NUM_ITER: usize = 10; 59 | 60 | let op0 = [0.0, 0.2, 0.3, 0.5, 0.0]; // - Peer 0 opinions 61 | let op1 = [0.1, 0.0, 0.1, 0.1, 0.7]; // - Peer 1 opinions 62 | let op2 = [0.4, 0.1, 0.0, 0.2, 0.3]; // - Peer 2 opinions 63 | let op3 = [0.1, 0.1, 0.7, 0.0, 0.1]; // - Peer 3 opinions 64 | let op4 = [0.3, 0.1, 0.4, 0.2, 0.0]; // = Peer 4 opinions 65 | 66 | for _ in 0..NUM_ITER { 67 | // sop0 = s[0] * op0 68 | let sop0 = op0.map(|v| v * s[0]); 69 | // sop1 = s[1] * op1 70 | let sop1 = op1.map(|v| v * s[1]); 71 | // sop2 = s[2] * op2 72 | let sop2 = op2.map(|v| v * s[2]); 73 | // sop3 = s[3] * op3 74 | let sop3 = op3.map(|v| v * s[3]); 75 | // sop4 = s[4] * op4 76 | let sop4 = op4.map(|v| v * s[4]); 77 | 78 | let s0 = sop0[0] + sop1[0] + sop2[0] + sop3[0] + sop4[0]; 79 | let s1 = sop0[1] + sop1[1] + sop2[1] + sop3[1] + sop4[1]; 80 | let s2 = sop0[2] + sop1[2] + sop2[2] + sop3[2] + sop4[2]; 81 | let s3 = sop0[3] + sop1[3] + sop2[3] + sop3[3] + sop4[3]; 82 | let s4 = sop0[4] + sop1[4] + sop2[4] + sop3[4] + sop4[4]; 83 | 84 | s = [s0, s1, s2, s3, s4]; 85 | 86 | println!("[{}]", s.map(|v| format!("{:>9.4}", v)).join(", ")); 87 | } 88 | ``` 89 | 90 | The logs: 91 | ``` 92 | [490.0000, 300.0000, 790.0000, 840.0000, 1580.0000] - iter 0 93 | [904.0000, 419.0000, 1397.0000, 749.0000, 531.0000] - iter 1 94 | [834.9000, 448.5000, 1049.8000, 879.5000, 787.3000] - iter 2 95 | [788.9100, 438.6400, 1225.8900, 829.7200, 716.8400] - iter 3 96 | [832.2440, 435.0270, 1148.0771, 826.8651, 757.7870] - iter 4 97 | [812.7562, 439.7218, 1175.0963, 840.7976, 731.6286] - iter 5 98 | [817.5791, 437.3035, 1169.0088, 831.6953, 744.4139] - iter 6 99 | [817.8276, 438.0276, 1168.9563, 835.2045, 739.9846] - iter 7 100 | [816.9011, 437.9801, 1169.7881, 834.5048, 740.8266] - iter 8 101 | [817.4117, 437.8922, 1169.3523, 834.3715, 740.9730] - iter 9 102 | ``` 103 | 104 | We can see that only after a few iterations, the scores will converge, depending on how much accuracy is needed. 105 | 106 | In the real world, we are working with finite field, so our data structures and algorithm has to be modified a bit. 107 | 108 | First we define a constant for a number of iterations: 109 | ```rust 110 | const NUM_ITER = 10; 111 | ``` 112 | 113 | Then, the EigenTrust set which includes the inital score for each peer: 114 | ```rust 115 | s = [(peer1, 1000), (peer2, 2000), (peer3, 500), (peer4, 300), (peer5, 200)] 116 | ``` 117 | 118 | Now, the scores map: 119 | ```rust 120 | scores => { 121 | peer1 => [(peer1, 0), (peer2, 2), (peer3, 3), (peer4, 5), (peer5, 0)] 122 | peer2 => [(peer1, 1), (peer2, 0), (peer3, 1), (peer4, 1), (peer5, 7)] 123 | peer3 => [(peer1, 4), (peer2, 1), (peer3, 0), (peer4, 2), (peer5, 3)] 124 | peer4 => [(peer1, 1), (peer2, 1), (peer3, 7), (peer4, 0), (peer5, 1)] 125 | peer5 => [(peer1, 3), (peer2, 1), (peer3, 4), (peer4, 2), (peer5, 0)] 126 | } 127 | ``` 128 | 129 | Minimum number of participants should be 2, if it is less, the matrix would not be able to converge: 130 | ```rust 131 | let valid_peers_count = count(filter(s, |s| s.0 != null)) 132 | assert!(valid_peers_count >= 2) 133 | ``` 134 | 135 | Before we run the algorithm, we have to normalise the scores: 136 | ```rust 137 | // Normalise the scores 138 | for i in 0..s.len() { 139 | let (pk_i, creadits) = s[i]; 140 | if pk == null { 141 | continue; 142 | } 143 | let sum = sum(scores[pk_i]); 144 | for j in 0..s.len() { 145 | scores[pk_i][j] = scores[pk_i][j] / sum; 146 | } 147 | } 148 | ``` 149 | 150 | Now, we have conditions to run the EigenTrust algorithm: 151 | ```rust 152 | for _ in 0..NUM_ITER { 153 | for i in 0..s.len() { 154 | let (pk_i, _) = s[i]; 155 | let new_score = 0; 156 | if pk_i == null { 157 | continue; 158 | } 159 | for j in 0..s.len() { 160 | let (pk_j, neighbour_score) = s[j]; 161 | if pk_j == null { 162 | continue; 163 | } 164 | let score = scores[pk_j][i]; 165 | new_score += score * neighbour_score; 166 | } 167 | s[i].1 = new_score 168 | } 169 | } 170 | ``` 171 | -------------------------------------------------------------------------------- /eigentrust-zk/src/merkle_tree/native.rs: -------------------------------------------------------------------------------- 1 | use crate::{FieldExt, Hasher}; 2 | use num_integer::Integer; 3 | use num_traits::pow; 4 | use std::{collections::HashMap, marker::PhantomData}; 5 | 6 | const WIDTH: usize = 5; 7 | 8 | #[derive(Clone, Debug)] 9 | /// MerkleTree structure 10 | pub struct MerkleTree 11 | where 12 | H: Hasher, 13 | { 14 | /// HashMap to keep the level and index of the nodes 15 | pub(crate) nodes: HashMap>, 16 | /// Height of the tree 17 | pub(crate) height: usize, 18 | /// Root of the tree 19 | pub(crate) root: F, 20 | /// PhantomData for the hasher 21 | _h: PhantomData, 22 | } 23 | 24 | impl MerkleTree 25 | where 26 | H: Hasher, 27 | { 28 | /// Build a MerkleTree from given leaf nodes and height 29 | pub fn build_tree(mut leaves: Vec) -> Self { 30 | assert!(leaves.len() <= pow(ARITY, HEIGHT)); 31 | assert!(ARITY <= WIDTH); 32 | 33 | // 0th level is the leaf level and the max level is the root level 34 | let mut nodes = HashMap::new(); 35 | // Assign zero to the leaf values if they are empty 36 | for _i in leaves.len()..pow(ARITY, HEIGHT) { 37 | leaves.push(F::ZERO); 38 | } 39 | nodes.insert(0, leaves); 40 | 41 | let mut hasher_inputs = [F::ZERO; WIDTH]; 42 | for level in 0..HEIGHT { 43 | let mut hashes = Vec::new(); 44 | for i in 0..nodes[&level].len() { 45 | if i % ARITY != 0 { 46 | continue; 47 | } 48 | hasher_inputs[..ARITY].copy_from_slice(&nodes[&level][i..(ARITY + i)]); 49 | let hasher = H::new(hasher_inputs); 50 | hashes.push(hasher.finalize()[0]); 51 | } 52 | nodes.insert(level + 1, hashes); 53 | } 54 | let root = nodes[&HEIGHT][0]; 55 | MerkleTree { nodes, height: HEIGHT, root, _h: PhantomData } 56 | } 57 | } 58 | 59 | #[derive(Clone)] 60 | /// Path structure 61 | pub struct Path 62 | where 63 | H: Hasher, 64 | { 65 | /// Value that is based on for construction of the path 66 | #[allow(dead_code)] 67 | pub(crate) value: F, 68 | /// Array that keeps the path 69 | pub(crate) path_arr: [[F; ARITY]; LENGTH], 70 | /// PhantomData for the hasher 71 | _h: PhantomData, 72 | } 73 | 74 | impl 75 | Path 76 | where 77 | H: Hasher, 78 | { 79 | /// Find path for the given value to the root 80 | pub fn find_path( 81 | merkle_tree: &MerkleTree, mut value_index: usize, 82 | ) -> Path { 83 | let value = merkle_tree.nodes[&0][value_index]; 84 | let mut path_arr: [[F; ARITY]; LENGTH] = [[F::ZERO; ARITY]; LENGTH]; 85 | 86 | for level in 0..merkle_tree.height { 87 | let wrap = value_index.div_rem(&ARITY); 88 | for i in 0..ARITY { 89 | path_arr[level][i] = merkle_tree.nodes[&level][wrap.0 * ARITY + i]; 90 | } 91 | value_index /= ARITY; 92 | } 93 | 94 | path_arr[merkle_tree.height][0] = merkle_tree.root; 95 | Self { value, path_arr, _h: PhantomData } 96 | } 97 | 98 | /// Sanity check for the path array 99 | pub fn verify(&self) -> bool { 100 | let mut is_satisfied = true; 101 | let mut hasher_inputs = [F::ZERO; WIDTH]; 102 | for i in 0..self.path_arr.len() - 1 { 103 | hasher_inputs[..ARITY].copy_from_slice(&self.path_arr[i][..ARITY]); 104 | let hasher = H::new(hasher_inputs); 105 | is_satisfied &= self.path_arr[i + 1].contains(&(hasher.finalize()[0])) 106 | } 107 | is_satisfied 108 | } 109 | } 110 | 111 | #[cfg(test)] 112 | mod test { 113 | use super::MerkleTree; 114 | use crate::{ 115 | merkle_tree::native::Path, params::hasher::poseidon_bn254_5x5::Params, 116 | poseidon::native::Poseidon, 117 | }; 118 | use halo2::{arithmetic::Field, halo2curves::bn256::Fr}; 119 | use rand::thread_rng; 120 | 121 | #[test] 122 | fn should_build_tree_and_find_path_arity_2() { 123 | // Testing build_tree and find_path functions with arity 2 124 | let rng = &mut thread_rng(); 125 | let value = Fr::random(rng.clone()); 126 | let leaves = vec![ 127 | Fr::random(rng.clone()), 128 | Fr::random(rng.clone()), 129 | Fr::random(rng.clone()), 130 | Fr::random(rng.clone()), 131 | value, 132 | Fr::random(rng.clone()), 133 | Fr::random(rng.clone()), 134 | Fr::random(rng.clone()), 135 | ]; 136 | let merkle = MerkleTree::>::build_tree(leaves); 137 | let path = Path::>::find_path(&merkle, 4); 138 | 139 | assert!(path.verify()); 140 | // Assert last element of the array and the root of the tree 141 | assert_eq!(path.path_arr[merkle.height][0], merkle.root); 142 | } 143 | 144 | #[test] 145 | fn should_build_tree_and_find_path_arity_3() { 146 | // Testing build_tree and find_path functions with arity 3 147 | let rng = &mut thread_rng(); 148 | let value = Fr::random(rng.clone()); 149 | let leaves = vec![ 150 | Fr::random(rng.clone()), 151 | Fr::random(rng.clone()), 152 | Fr::random(rng.clone()), 153 | Fr::random(rng.clone()), 154 | Fr::random(rng.clone()), 155 | Fr::random(rng.clone()), 156 | Fr::random(rng.clone()), 157 | value, 158 | Fr::random(rng.clone()), 159 | Fr::random(rng.clone()), 160 | Fr::random(rng.clone()), 161 | Fr::random(rng.clone()), 162 | Fr::random(rng.clone()), 163 | Fr::random(rng.clone()), 164 | Fr::random(rng.clone()), 165 | Fr::random(rng.clone()), 166 | Fr::random(rng.clone()), 167 | Fr::random(rng.clone()), 168 | Fr::random(rng.clone()), 169 | Fr::random(rng.clone()), 170 | ]; 171 | let merkle = MerkleTree::>::build_tree(leaves); 172 | let path = Path::>::find_path(&merkle, 7); 173 | 174 | assert!(path.verify()); 175 | // Assert last element of the array and the root of the tree 176 | assert_eq!(path.path_arr[merkle.height][0], merkle.root); 177 | } 178 | 179 | #[test] 180 | fn should_build_small_tree() { 181 | // Testing build_tree and find_path functions with a small array 182 | let rng = &mut thread_rng(); 183 | let value = Fr::random(rng.clone()); 184 | let merkle = MerkleTree::>::build_tree(vec![value]); 185 | let path = Path::>::find_path(&merkle, 0); 186 | assert!(path.verify()); 187 | // Assert last element of the array and the root of the tree 188 | assert_eq!(path.path_arr[merkle.height][0], merkle.root); 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /eigentrust-zk/src/gadgets/bits2integer.rs: -------------------------------------------------------------------------------- 1 | use super::bits2num::Bits2NumChip; 2 | use crate::{ 3 | integer::AssignedInteger, params::rns::RnsParams, Chip, Chipset, CommonConfig, FieldExt, 4 | }; 5 | use halo2::{ 6 | circuit::{AssignedCell, Layouter}, 7 | plonk::{Error, Selector}, 8 | }; 9 | 10 | /// Bits2IntegerChipsetConfig structure. 11 | #[derive(Debug, Clone)] 12 | pub struct Bits2IntegerChipsetConfig { 13 | bits2num: Selector, 14 | } 15 | 16 | impl Bits2IntegerChipsetConfig { 17 | /// Construct a new config. 18 | pub fn new(bits2num: Selector) -> Self { 19 | Self { bits2num } 20 | } 21 | } 22 | 23 | /// Bits2IntegerChipset structure. 24 | #[derive(Clone)] 25 | pub struct Bits2IntegerChipset< 26 | W: FieldExt, 27 | N: FieldExt, 28 | const NUM_LIMBS: usize, 29 | const NUM_BITS: usize, 30 | P, 31 | > where 32 | P: RnsParams, 33 | { 34 | /// Assigned Integer variable. 35 | assigned_integer: AssignedInteger, 36 | } 37 | 38 | impl 39 | Bits2IntegerChipset 40 | where 41 | P: RnsParams, 42 | { 43 | /// Creates a new chipset. 44 | pub fn new(assigned_integer: AssignedInteger) -> Self { 45 | Self { assigned_integer } 46 | } 47 | } 48 | 49 | impl Chipset 50 | for Bits2IntegerChipset 51 | where 52 | P: RnsParams, 53 | { 54 | type Config = Bits2IntegerChipsetConfig; 55 | type Output = Vec>; 56 | 57 | /// Synthesize the circuit. 58 | fn synthesize( 59 | self, common: &CommonConfig, config: &Self::Config, mut layouter: impl Layouter, 60 | ) -> Result { 61 | let mut bits = Vec::new(); 62 | for i in 0..NUM_LIMBS { 63 | let limb_bits_chip = 64 | Bits2NumChip::new_exact::(self.assigned_integer.limbs[i].clone()); 65 | let limb_bits = limb_bits_chip.synthesize( 66 | common, 67 | &config.bits2num, 68 | layouter.namespace(|| "limb bits"), 69 | )?; 70 | bits.extend(limb_bits); 71 | } 72 | 73 | Ok(bits) 74 | } 75 | } 76 | 77 | #[cfg(test)] 78 | mod test { 79 | use std::str::FromStr; 80 | 81 | use super::*; 82 | use crate::integer::UnassignedInteger; 83 | use crate::params::rns::bn256::Bn256_4_68; 84 | use crate::RegionCtx; 85 | use crate::{ 86 | integer::native::Integer, 87 | utils::{generate_params, prove_and_verify}, 88 | CommonConfig, UnassignedValue, 89 | }; 90 | use halo2::circuit::Region; 91 | use halo2::{ 92 | circuit::SimpleFloorPlanner, 93 | dev::MockProver, 94 | halo2curves::bn256::{Bn256, Fq, Fr}, 95 | plonk::{Circuit, ConstraintSystem}, 96 | }; 97 | use num_bigint::BigUint; 98 | 99 | type W = Fq; 100 | type N = Fr; 101 | const NUM_LIMBS: usize = 4; 102 | const NUM_BITS: usize = 68; 103 | type P = Bn256_4_68; 104 | 105 | #[derive(Clone)] 106 | struct TestConfig { 107 | common: CommonConfig, 108 | bits2integer: Bits2IntegerChipsetConfig, 109 | } 110 | 111 | #[derive(Clone)] 112 | struct TestCircuit { 113 | unassigned_integer: UnassignedInteger, 114 | } 115 | 116 | impl TestCircuit { 117 | fn new(integer: Integer) -> Self { 118 | Self { unassigned_integer: UnassignedInteger::from(integer) } 119 | } 120 | } 121 | 122 | impl Circuit for TestCircuit { 123 | type Config = TestConfig; 124 | type FloorPlanner = SimpleFloorPlanner; 125 | 126 | fn without_witnesses(&self) -> Self { 127 | Self { 128 | unassigned_integer: UnassignedInteger::without_witnesses(&self.unassigned_integer), 129 | } 130 | } 131 | 132 | fn configure(meta: &mut ConstraintSystem) -> TestConfig { 133 | let common = CommonConfig::new(meta); 134 | let bits2integer_selector = Bits2NumChip::configure(&common, meta); 135 | let bits2integer = Bits2IntegerChipsetConfig::new(bits2integer_selector); 136 | 137 | TestConfig { common, bits2integer } 138 | } 139 | 140 | fn synthesize( 141 | &self, config: TestConfig, mut layouter: impl Layouter, 142 | ) -> Result<(), Error> { 143 | let assigned_limbs = layouter.assign_region( 144 | || "temp", 145 | |region: Region<'_, N>| { 146 | let mut ctx = RegionCtx::new(region, 0); 147 | let mut assigned_limbs: [Option>; NUM_LIMBS] = 148 | [(); NUM_LIMBS].map(|_| None); 149 | for i in 0..NUM_LIMBS { 150 | let x = ctx.assign_advice( 151 | config.common.advice[i], self.unassigned_integer.limbs[i], 152 | )?; 153 | assigned_limbs[i] = Some(x); 154 | } 155 | Ok(assigned_limbs) 156 | }, 157 | )?; 158 | 159 | let assigned_integer = AssignedInteger::::new( 160 | self.unassigned_integer.integer.clone(), 161 | assigned_limbs.map(|x| x.unwrap()), 162 | ); 163 | 164 | let bits2integer = Bits2IntegerChipset::new(assigned_integer); 165 | let _ = bits2integer.synthesize( 166 | &config.common, 167 | &config.bits2integer, 168 | layouter.namespace(|| "bits2integer"), 169 | )?; 170 | 171 | Ok(()) 172 | } 173 | } 174 | 175 | #[test] 176 | fn test_bits_to_integer() { 177 | // Testing field element 0x1. 178 | let numba_big = BigUint::from_str("1").unwrap(); 179 | let numba = Integer::::new(numba_big); 180 | 181 | let circuit = TestCircuit::new(numba); 182 | let k = 8; 183 | let prover = MockProver::run(k, &circuit, vec![vec![]]).unwrap(); 184 | 185 | assert_eq!(prover.verify(), Ok(())); 186 | } 187 | 188 | #[test] 189 | fn test_bits_to_integer_big() { 190 | // Testing biggest value in the field. 191 | let numba_big = BigUint::from_str( 192 | "21888242871839275222246405745257275088548364400416034343698204186575808495616", 193 | ) 194 | .unwrap(); 195 | let numba = Integer::::new(numba_big); 196 | 197 | let circuit = TestCircuit::new(numba); 198 | let k = 8; 199 | let prover = MockProver::run(k, &circuit, vec![vec![]]).unwrap(); 200 | 201 | assert_eq!(prover.verify(), Ok(())); 202 | } 203 | 204 | #[test] 205 | fn test_bits_to_integer_production() { 206 | let numba_big = BigUint::from_str("3823613239503432837285398709123").unwrap(); 207 | let numba = Integer::::new(numba_big); 208 | let circuit = TestCircuit::new(numba); 209 | let k = 8; 210 | let rng = &mut rand::thread_rng(); 211 | let params = generate_params(k); 212 | let res = prove_and_verify::(params, circuit, &[&[]], rng).unwrap(); 213 | 214 | assert!(res); 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /eigentrust-zk/README.md: -------------------------------------------------------------------------------- 1 | # EigenTrust ZK 2 | 3 | This crate contains all the Chips, Chipsets and Circuit related to EigenTrust protocol. 4 | There are 2 main traits that we use to atomically make chips: 5 | 6 | 1) Chip 7 | ```rust 8 | /// Trait for an atomic chip implementation 9 | /// Each chip uses common config columns, but has its own selector 10 | pub trait Chip { 11 | /// Output of the synthesis 12 | type Output: Clone; 13 | /// Gate configuration, using common config columns 14 | fn configure(common: &CommonConfig, meta: &mut ConstraintSystem) -> Selector; 15 | /// Chip synthesis. This function can return an assigned cell to be used 16 | /// elsewhere in the circuit 17 | fn synthesize( 18 | self, common: &CommonConfig, selector: &Selector, layouter: impl Layouter, 19 | ) -> Result; 20 | } 21 | ``` 22 | Supposed to be the lowest-level primitive and the place where gates are defined. 23 | Specifically, the gates are defined in the `configure` function, 24 | which accepts a `CommonConfig`, and returns a single selector, that will be used to activate this gate. 25 | Example usage: 26 | ```rust 27 | /// Structure for the main chip. 28 | pub struct MainChip { 29 | advice: [AssignedCell; NUM_ADVICE], 30 | fixed: [F; NUM_FIXED], 31 | } 32 | 33 | impl MainChip { 34 | /// Assigns a new witness that is equal to boolean AND of `x` and `y` 35 | pub fn new(advice: [AssignedCell; NUM_ADVICE], fixed: [F; NUM_FIXED]) -> Self { 36 | Self { advice, fixed } 37 | } 38 | } 39 | 40 | impl Chip for MainChip { 41 | type Output = (); 42 | 43 | fn configure(common: &CommonConfig, meta: &mut ConstraintSystem) -> Selector { 44 | let selector = meta.selector(); 45 | 46 | meta.create_gate("main gate", |v_cells| { 47 | // MainGate constraints 48 | let a = v_cells.query_advice(common.advice[0], Rotation::cur()); 49 | let b = v_cells.query_advice(common.advice[1], Rotation::cur()); 50 | let c = v_cells.query_advice(common.advice[2], Rotation::cur()); 51 | 52 | ... 53 | }); 54 | selector 55 | } 56 | 57 | fn synthesize( 58 | self, common: &CommonConfig, selector: &Selector, mut layouter: impl Layouter, 59 | ) -> Result { 60 | layouter.assign_region( 61 | || "main gate", 62 | |region| { 63 | let mut ctx = RegionCtx::new(region, 0); 64 | 65 | ctx.enable(*selector)?; 66 | 67 | // e.g.: 68 | ctx.assign_advice(common.advice[0], self.advice[0]); 69 | 70 | ... 71 | }, 72 | ) 73 | } 74 | } 75 | ``` 76 | 77 | 2) Chipset 78 | ```rust 79 | /// Chipset uses a collection of chips as primitives to build more abstract 80 | /// circuits 81 | pub trait Chipset { 82 | /// Config used for synthesis 83 | type Config: Clone; 84 | /// Output of the synthesis 85 | type Output: Clone; 86 | /// Chipset synthesis. This function can have multiple smaller chips 87 | /// synthesised inside. Also can returns an assigned cell. 88 | fn synthesize( 89 | self, common: &CommonConfig, config: &Self::Config, layouter: impl Layouter, 90 | ) -> Result; 91 | } 92 | ``` 93 | Is one level higher that a `Chip` and underneath it should call one or multiple chips. 94 | `Config` should contain the selectors for the underlying chips. 95 | It also accepts `CommonConfig` that will be passed down to chips. 96 | ```rust 97 | /// Main config for common primitives like `add`, `mul` ... 98 | #[derive(Debug, Clone)] 99 | pub struct MainConfig { 100 | selector: Selector, 101 | } 102 | 103 | impl MainConfig { 104 | /// Initialization function for MainConfig 105 | pub fn new(selector: Selector) -> Self { 106 | Self { selector } 107 | } 108 | } 109 | 110 | /// Chip for addition operation 111 | pub struct AddChipset { 112 | x: AssignedCell, 113 | y: AssignedCell, 114 | } 115 | 116 | impl AddChipset { 117 | /// Create new AddChipset 118 | pub fn new(x: AssignedCell, y: AssignedCell) -> Self { 119 | Self { x, y } 120 | } 121 | } 122 | 123 | impl Chipset for AddChipset { 124 | type Config = MainConfig; 125 | type Output = AssignedCell; 126 | 127 | fn synthesize( 128 | self, common: &CommonConfig, config: &Self::Config, mut layouter: impl Layouter, 129 | ) -> Result { 130 | /// e.g.: 131 | let advices = [self.x, self.y, self.x + self.y, zero, zero]; 132 | let fixed = [F::ONE, F::ONE, -F::ONE, F::ZERO, F::ZERO, F::ZERO, F::ZERO, F::ZERO]; 133 | let main_chip = MainChip::new(advices, fixed); 134 | main_chip.synthesize(common, &config.selector, layouter.namespace(|| "main_add"))?; 135 | 136 | Ok(sum) 137 | } 138 | } 139 | ``` 140 | 141 | Finally, `CommonConfig` is a predefined struct containing shared set of advice and fixed columns. 142 | Currently, it is fixed to 20 advice, 10 fixed, 1 table and 1 instance column. 143 | ```rust 144 | /// Number of advice columns in common config 145 | pub const ADVICE: usize = 20; 146 | /// Number of fixed columns in common config 147 | pub const FIXED: usize = 10; 148 | 149 | /// Common config for the whole circuit 150 | #[derive(Clone, Debug)] 151 | pub struct CommonConfig { 152 | /// Advice columns 153 | advice: [Column; ADVICE], 154 | /// Fixed columns 155 | fixed: [Column; FIXED], 156 | /// Table column 157 | table: TableColumn, 158 | /// Instance column 159 | instance: Column, 160 | } 161 | 162 | impl CommonConfig { 163 | /// Create a new `CommonConfig` columns 164 | pub fn new(meta: &mut ConstraintSystem) -> Self { 165 | let advice = [(); ADVICE].map(|_| meta.advice_column()); 166 | let fixed = [(); FIXED].map(|_| meta.fixed_column()); 167 | let table = meta.lookup_table_column(); 168 | let instance = meta.instance_column(); 169 | 170 | advice.map(|c| meta.enable_equality(c)); 171 | fixed.map(|c| meta.enable_constant(c)); 172 | meta.enable_equality(instance); 173 | 174 | Self { advice, fixed, table, instance } 175 | } 176 | } 177 | ``` 178 | 179 | This config is constructed once in the main circuit and passed down to every chip and chipset. 180 | Example: 181 | ```rust 182 | impl SomeCircuit { 183 | type Config = SomeConfig; 184 | 185 | fn configure(meta: &mut ConstraintSystem) -> Self::Config { 186 | let common = CommonConfig::new(meta); 187 | let main = MainChip::configure(&common, meta); 188 | let bits2num_selector = Bits2NumChip::configure(&common, meta); 189 | let set_selector = SetChip::configure(&common, meta); 190 | ... 191 | } 192 | } 193 | ``` 194 | Each chip that accepts `CommonConfig` has the responsability to pick column that it needs for enforcing constraints. 195 | (NOTE: This is a design flaw - the higher-level circuit should be the one picking the columns based on the requirements of the chips/chipsets.) 196 | 197 | Additional utils: `RegionCtx` 198 | 199 | RegionCtx is a wrapper around Halo2's vanilla Region API. Example usage: 200 | ```rust 201 | let mut ctx = RegionCtx::new(region, 0); 202 | // Enabling selectors 203 | ctx.enable(*selector)?; 204 | // Assign advice columm from instance 205 | let assigned_inst = ctx.assign_from_instance(common.advice[0], common.instance, 0)?; 206 | // Assign from constant 207 | let assigned_one = ctx.assign_from_constant(common.advice[1], F::ONE)?; 208 | // Assign to advice column 209 | let assigned_res = ctx.assign_advice(common.advice[2], some_value)?; 210 | // Copy assign to advice column 211 | let assigned_x = ctx.copy_assign(common.advice[3], self.x)?; 212 | // Assigned to fixed column 213 | let assigned_zero = ctx.assign_fixed(common.fixed[0], F::ZERO)?; 214 | // Constrain equality 215 | ctx.constrain_equal(assigned_x, assigned_zero)?; 216 | // Constrain to constant 217 | ctx.constrain_to_constant(assigned_one, F::ONE)?; 218 | // Move to next row 219 | ctx.next(); 220 | // Return back to region 221 | let region = ctx.into_region(); 222 | ``` 223 | -------------------------------------------------------------------------------- /eigentrust-zk/src/edwards/params.rs: -------------------------------------------------------------------------------- 1 | use crate::FieldExt; 2 | use halo2::{circuit::Value, halo2curves::bn256::Fr, plonk::Expression}; 3 | 4 | /// Trait for defining point A and D for Edward curves 5 | pub trait EdwardsParams { 6 | /// Returns A value 7 | fn a() -> F; 8 | /// Returns D value 9 | fn d() -> F; 10 | /// Returns B8 point 11 | fn b8() -> (F, F); 12 | /// Returns G point 13 | fn g() -> (F, F); 14 | /// Returns suborder 15 | fn suborder() -> F; 16 | /// Suborder field size in bits 17 | fn suborder_size() -> usize; 18 | /// Performs Add operation 19 | fn add(r_x: F, r_y: F, r_z: F, e_x: F, e_y: F, e_z: F) -> (F, F, F); 20 | /// Performs Add operation on Expression 21 | fn add_exp( 22 | r_x: Expression, r_y: Expression, r_z: Expression, e_x: Expression, 23 | e_y: Expression, e_z: Expression, 24 | ) -> (Expression, Expression, Expression); 25 | /// Performs Add operation on Value 26 | fn add_value( 27 | r_x: Value, r_y: Value, r_z: Value, e_x: Value, e_y: Value, e_z: Value, 28 | ) -> (Value, Value, Value); 29 | /// Performs Double operation 30 | fn double(e_x: F, e_y: F, e_z: F) -> (F, F, F); 31 | /// Performs Double operation on Expression 32 | fn double_exp( 33 | e_x: Expression, e_y: Expression, e_z: Expression, 34 | ) -> (Expression, Expression, Expression); 35 | /// Performs Double operation on Value 36 | fn double_value(e_x: Value, e_y: Value, e_z: Value) -> (Value, Value, Value); 37 | } 38 | 39 | /// Struct for defining BabyJubJub A and D points 40 | #[derive(Hash, Debug, Clone, Copy, PartialEq, Eq, Default)] 41 | pub struct BabyJubJub; 42 | 43 | impl EdwardsParams for BabyJubJub { 44 | fn a() -> Fr { 45 | Fr::from_raw([0x292FC, 0x00, 0x00, 0x00]) 46 | } 47 | 48 | fn d() -> Fr { 49 | Fr::from_raw([0x292F8, 0x00, 0x00, 0x00]) 50 | } 51 | 52 | fn b8() -> (Fr, Fr) { 53 | ( 54 | Fr::from_raw([ 55 | 0x2893F3F6BB957051, 0x2AB8D8010534E0B6, 0x4EACB2E09D6277C1, 0xBB77A6AD63E739B, 56 | ]), 57 | Fr::from_raw([ 58 | 0x4B3C257A872D7D8B, 0xFCE0051FB9E13377, 0x25572E1CD16BF9ED, 0x25797203F7A0B249, 59 | ]), 60 | ) 61 | } 62 | 63 | fn g() -> (Fr, Fr) { 64 | ( 65 | Fr::from_raw([ 66 | 0x40F41A59F4D4B45E, 0xB494B1255B1162BB, 0x38BCBA38F25645AD, 0x23343E3445B673D, 67 | ]), 68 | Fr::from_raw([ 69 | 0x50F87D64FC000001, 0x4A0CFA121E6E5C24, 0x6E14116DA0605617, 0xC19139CB84C680A, 70 | ]), 71 | ) 72 | } 73 | 74 | fn suborder() -> Fr { 75 | Fr::from_raw([ 76 | 0x677297DC392126F1, 0xAB3EEDB83920EE0A, 0x370A08B6D0302B0B, 0x60C89CE5C263405, 77 | ]) 78 | } 79 | 80 | fn suborder_size() -> usize { 81 | 252 82 | } 83 | 84 | /// ADD operation between points `r` and `e` 85 | // add-2008-bbjlp https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#addition-add-2008-bbjlp 86 | fn add(r_x: Fr, r_y: Fr, r_z: Fr, e_x: Fr, e_y: Fr, e_z: Fr) -> (Fr, Fr, Fr) { 87 | let const_d = Self::d(); 88 | let const_a = Self::a(); 89 | // A = Z1*Z2 90 | let a = r_z.mul(&e_z); 91 | // B = A^2 92 | let b = a.square(); 93 | // C = X1*X2 94 | let c = r_x.mul(&e_x); 95 | // D = Y1*Y2 96 | let d = r_y.mul(&e_y); 97 | // E = d*C*D 98 | let e = const_d.mul(&c).mul(&d); 99 | // F = B-E 100 | let f = b.sub(&e); 101 | // G = B+E 102 | let g = b.add(&e); 103 | // X3 = A*F*((X1+Y1)*(X2+Y2)-C-D) 104 | let x3 = a.mul(&f).mul(&r_x.add(&r_y).mul(&e_x.add(&e_y)).sub(&c).sub(&d)); 105 | // Y3 = A*G*(D-a*C) 106 | let y3 = a.mul(&g).mul(&d.sub(&const_a.mul(&c))); 107 | // Z3 = F*G 108 | let z3 = f.mul(&g); 109 | 110 | (x3, y3, z3) 111 | } 112 | 113 | /// ADD operation between expressions `r` and `e` 114 | fn add_exp( 115 | r_x: Expression, r_y: Expression, r_z: Expression, e_x: Expression, 116 | e_y: Expression, e_z: Expression, 117 | ) -> (Expression, Expression, Expression) { 118 | let const_d = Self::d(); 119 | let const_a = Self::a(); 120 | // A = Z1*Z2 121 | let r_a = r_z * e_z; 122 | // B = A^2 123 | let r_b = r_a.clone().square(); 124 | // C = X1*X2 125 | let r_c = r_x.clone() * e_x.clone(); 126 | // D = Y1*Y2 127 | let r_d = r_y.clone() * e_y.clone(); 128 | // E = d*C*D 129 | let r_e = r_c.clone() * r_d.clone() * const_d; 130 | // F = B-E 131 | let r_f = r_b.clone() - r_e.clone(); 132 | // G = B+E 133 | let r_g = r_b + r_e; 134 | // X3 = A*F*((X1+Y1)*(X2+Y2)-C-D) 135 | let r_x3 = 136 | r_a.clone() * r_f.clone() * ((r_x + r_y) * (e_x + e_y) - r_c.clone() - r_d.clone()); 137 | // Y3 = A*G*(D-a*C) 138 | let r_y3 = r_a * r_g.clone() * (r_d - r_c * const_a); 139 | // Z3 = F*G 140 | let r_z3 = r_f * r_g; 141 | (r_x3, r_y3, r_z3) 142 | } 143 | 144 | /// ADD operation between assigned values `r` and `e` 145 | fn add_value( 146 | r_x: Value, r_y: Value, r_z: Value, e_x: Value, e_y: Value, 147 | e_z: Value, 148 | ) -> (Value, Value, Value) { 149 | let const_d = Value::known(Self::d()); 150 | let const_a = Value::known(Self::a()); 151 | // Add `r` and `e` 152 | // A = Z1*Z2 153 | let r_a = r_z * e_z; 154 | // B = A^2 155 | let r_b = r_a * r_a; 156 | // C = X1*X2 157 | let r_c = r_x * e_x; 158 | // D = Y1*Y2 159 | let r_d = r_y * e_y; 160 | // E = d*C*D 161 | let r_e = const_d * r_c * r_d; 162 | // F = B-E 163 | let r_f = r_b - r_e; 164 | // G = B+E 165 | let r_g = r_b + r_e; 166 | // X3 = A*F*((X1+Y1)*(X2+Y2)-C-D) 167 | let r_x3 = r_a * r_f * ((r_x + r_y) * (e_x + e_y) - r_c - r_d); 168 | // Y3 = A*G*(D-a*C) 169 | let r_y3 = r_a * r_g * (r_d - const_a * r_c); 170 | // Z3 = F*G 171 | let r_z3 = r_f * r_g; 172 | 173 | (r_x3, r_y3, r_z3) 174 | } 175 | 176 | /// DOUBLE operation of point `e` 177 | // dbl-2008-bbjlp https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#doubling-dbl-2008-bbjlp 178 | fn double(e_x: Fr, e_y: Fr, e_z: Fr) -> (Fr, Fr, Fr) { 179 | let const_a = Self::a(); 180 | // B = (X1+Y1)^2 181 | let b = e_x.add(&e_y).square(); 182 | // C = X1^2 183 | let c = e_x.square(); 184 | // D = Y1^2 185 | let d = e_y.square(); 186 | // E = a*C 187 | let e = const_a.mul(&c); 188 | // F = E+D 189 | let f = e.add(&d); 190 | // H = Z1^2 191 | let h = e_z.square(); 192 | // J = F-2*H 193 | let j = f.sub(&h.double()); 194 | // X3 = (B-C-D)*J 195 | let x3 = b.sub(&c).sub(&d).mul(&j); 196 | // Y3 = F*(E-D) 197 | let y3 = f.mul(&e.sub(&d)); 198 | // Z3 = F*J 199 | let z3 = f.mul(&j); 200 | 201 | (x3, y3, z3) 202 | } 203 | 204 | /// DOUBLE operation of expression `e` 205 | fn double_exp( 206 | e_x: Expression, e_y: Expression, e_z: Expression, 207 | ) -> (Expression, Expression, Expression) { 208 | let const_a = Self::a(); 209 | // B = (X1+Y1)^2 210 | let e_b = (e_x.clone() + e_y.clone()).square(); 211 | // C = X1^2 212 | let e_c = e_x.square(); 213 | // D = Y1^2 214 | let e_d = e_y.square(); 215 | // E = a*C 216 | let e_e = e_c.clone() * const_a; 217 | // F = E+D 218 | let e_f = e_e.clone() + e_d.clone(); 219 | // H = Z1^2 220 | let e_h = e_z.square(); 221 | // J = F-2*H 222 | let e_j = e_f.clone() - (e_h.clone() + e_h); 223 | // X3 = (B-C-D)*J 224 | let e_x3 = (e_b - e_c - e_d.clone()) * e_j.clone(); 225 | // Y3 = F*(E-D) 226 | let e_y3 = e_f.clone() * (e_e - e_d); 227 | // Z3 = F*J 228 | let e_z3 = e_f * e_j; 229 | 230 | (e_x3, e_y3, e_z3) 231 | } 232 | 233 | /// DOUBLE operation of assigned value `e` 234 | fn double_value( 235 | e_x: Value, e_y: Value, e_z: Value, 236 | ) -> (Value, Value, Value) { 237 | let const_a = Value::known(Self::a()); 238 | // B = (X1+Y1)^2 239 | let e_b = e_x + e_y; 240 | let e_b = e_b * e_b; 241 | // C = X1^2 242 | let e_c = e_x * e_x; 243 | // D = Y1^2 244 | let e_d = e_y * e_y; 245 | // E = a*C 246 | let e_e = const_a * e_c; 247 | // F = E+D 248 | let e_f = e_e + e_d; 249 | // H = Z1^2 250 | let e_h = e_z * e_z; 251 | // J = F-2*H 252 | let e_j = e_f - (e_h + e_h); 253 | // X3 = (B-C-D)*J 254 | let e_x3 = (e_b - e_c - e_d) * e_j; 255 | // Y3 = F*(E-D) 256 | let e_y3 = e_f * (e_e - e_d); 257 | // Z3 = F*J 258 | let e_z3 = e_f * e_j; 259 | 260 | (e_x3, e_y3, e_z3) 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /eigentrust-zk/src/edwards/native.rs: -------------------------------------------------------------------------------- 1 | use halo2::circuit::Value; 2 | 3 | use crate::{utils::to_bits, FieldExt}; 4 | 5 | use super::params::EdwardsParams; 6 | use std::marker::PhantomData; 7 | 8 | #[derive(Clone, Copy, Debug)] 9 | /// Constructs PointProjective objects. 10 | pub struct PointProjective> { 11 | /// Constructs a field element for the x. 12 | pub x: F, 13 | /// Constructs a field element for the y. 14 | pub y: F, 15 | /// Constructs a field element for the z. 16 | pub z: F, 17 | _p: PhantomData

, 18 | } 19 | 20 | impl> PointProjective { 21 | /// Returns affine representation from the given projective space 22 | /// representation. 23 | pub fn affine(&self) -> Point { 24 | if bool::from(self.z.is_zero()) { 25 | return Point { x: F::ZERO, y: F::ZERO, _p: PhantomData }; 26 | } 27 | 28 | let zinv = self.z.invert().unwrap(); 29 | let x = self.x.mul(&zinv); 30 | let y = self.y.mul(&zinv); 31 | 32 | Point { x, y, _p: PhantomData } 33 | } 34 | 35 | /// DOUBLE operation of point `self` 36 | pub fn double(&self) -> Self { 37 | // dbl-2008-bbjlp https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#doubling-dbl-2008-bbjlp 38 | let (x3, y3, z3) = P::double(self.x, self.y, self.z); 39 | 40 | PointProjective { x: x3, y: y3, z: z3, _p: PhantomData } 41 | } 42 | 43 | /// ADD operation between points `self` and `q` 44 | pub fn add(&self, q: &Self) -> Self { 45 | // add-2008-bbjlp https://hyperelliptic.org/EFD/g1p/auto-twisted-projective.html#addition-add-2008-bbjlp 46 | let (x3, y3, z3) = P::add(self.x, self.y, self.z, q.x, q.y, q.z); 47 | 48 | PointProjective { x: x3, y: y3, z: z3, _p: PhantomData } 49 | } 50 | } 51 | 52 | #[derive(Hash, Clone, Copy, Debug, PartialEq, Eq, Default)] 53 | /// Configures Point objects. 54 | pub struct Point> { 55 | /// Constructs a field element for the x. 56 | pub x: F, 57 | /// Constructs a field element for the y. 58 | pub y: F, 59 | _p: PhantomData

, 60 | } 61 | 62 | #[derive(Clone, Copy, Debug, Default)] 63 | /// Configures unassigned Point objects. 64 | pub struct UnassignedPoint> { 65 | /// Constructs a field element wrapped in value for the x. 66 | pub x: Value, 67 | /// Constructs a field element wrapped in value for the y. 68 | pub y: Value, 69 | pub(crate) _p: PhantomData

, 70 | } 71 | 72 | impl> Point { 73 | /// Returns a new Edwards point in affine repr 74 | pub fn new(x: F, y: F) -> Self { 75 | Self { x, y, _p: PhantomData } 76 | } 77 | 78 | /// Returns projective space representation from the given affine 79 | /// representation. 80 | pub fn projective(&self) -> PointProjective { 81 | PointProjective { x: self.x, y: self.y, z: F::ONE, _p: PhantomData } 82 | } 83 | 84 | /// Returns scalar multiplication of the element. 85 | pub fn mul_scalar(&self, scalar: F) -> PointProjective { 86 | let mut r: PointProjective = 87 | PointProjective { x: F::ZERO, y: F::ONE, z: F::ONE, _p: PhantomData }; 88 | let mut exp: PointProjective = self.projective(); 89 | let scalar_bits = to_bits(scalar.to_repr().as_ref()); 90 | // Double and add operation. 91 | for bits in &scalar_bits { 92 | if *bits { 93 | r = r.add(&exp); 94 | } 95 | exp = exp.double(); 96 | } 97 | r 98 | } 99 | 100 | /// Returns true if the given point is equal to the element. Else, false. 101 | pub fn equals(&self, p: Self) -> bool { 102 | self.x == p.x && self.y == p.y 103 | } 104 | } 105 | 106 | #[cfg(test)] 107 | mod tests { 108 | use super::*; 109 | use crate::edwards::params::BabyJubJub; 110 | use halo2::halo2curves::{bn256::Fr, group::ff::PrimeField}; 111 | 112 | #[test] 113 | fn test_add_same_point() { 114 | // Testing addition operation with identical points. 115 | let p: PointProjective = PointProjective { 116 | x: Fr::from_str_vartime( 117 | "17777552123799933955779906779655732241715742912184938656739573121738514868268", 118 | ) 119 | .unwrap(), 120 | y: Fr::from_str_vartime( 121 | "2626589144620713026669568689430873010625803728049924121243784502389097019475", 122 | ) 123 | .unwrap(), 124 | z: Fr::one(), 125 | _p: PhantomData, 126 | }; 127 | 128 | let q: PointProjective = PointProjective { 129 | x: Fr::from_str_vartime( 130 | "17777552123799933955779906779655732241715742912184938656739573121738514868268", 131 | ) 132 | .unwrap(), 133 | y: Fr::from_str_vartime( 134 | "2626589144620713026669568689430873010625803728049924121243784502389097019475", 135 | ) 136 | .unwrap(), 137 | z: Fr::one(), 138 | _p: PhantomData, 139 | }; 140 | 141 | let res = p.add(&q).affine(); 142 | assert_eq!( 143 | res.x, 144 | Fr::from_str_vartime( 145 | "6890855772600357754907169075114257697580319025794532037257385534741338397365" 146 | ) 147 | .unwrap(), 148 | ); 149 | assert_eq!( 150 | res.y, 151 | Fr::from_str_vartime( 152 | "4338620300185947561074059802482547481416142213883829469920100239455078257889" 153 | ) 154 | .unwrap(), 155 | ); 156 | } 157 | 158 | #[test] 159 | fn test_add_different_points() { 160 | // Testing addition operation with different points. 161 | let p: PointProjective = PointProjective { 162 | x: Fr::from_str_vartime( 163 | "17777552123799933955779906779655732241715742912184938656739573121738514868268", 164 | ) 165 | .unwrap(), 166 | y: Fr::from_str_vartime( 167 | "2626589144620713026669568689430873010625803728049924121243784502389097019475", 168 | ) 169 | .unwrap(), 170 | z: Fr::one(), 171 | _p: PhantomData, 172 | }; 173 | 174 | let q: PointProjective = PointProjective { 175 | x: Fr::from_str_vartime( 176 | "16540640123574156134436876038791482806971768689494387082833631921987005038935", 177 | ) 178 | .unwrap(), 179 | y: Fr::from_str_vartime( 180 | "20819045374670962167435360035096875258406992893633759881276124905556507972311", 181 | ) 182 | .unwrap(), 183 | z: Fr::one(), 184 | _p: PhantomData, 185 | }; 186 | 187 | let res = p.add(&q).affine(); 188 | assert_eq!( 189 | res.x, 190 | Fr::from_str_vartime( 191 | "7916061937171219682591368294088513039687205273691143098332585753343424131937" 192 | ) 193 | .unwrap(), 194 | ); 195 | assert_eq!( 196 | res.y, 197 | Fr::from_str_vartime( 198 | "14035240266687799601661095864649209771790948434046947201833777492504781204499" 199 | ) 200 | .unwrap(), 201 | ); 202 | } 203 | 204 | #[test] 205 | fn test_mul_scalar() { 206 | // Testing scalar multiplication operation. 207 | let p: Point = Point { 208 | x: Fr::from_str_vartime( 209 | "17777552123799933955779906779655732241715742912184938656739573121738514868268", 210 | ) 211 | .unwrap(), 212 | y: Fr::from_str_vartime( 213 | "2626589144620713026669568689430873010625803728049924121243784502389097019475", 214 | ) 215 | .unwrap(), 216 | _p: PhantomData, 217 | }; 218 | let res_m = p.mul_scalar(Fr::from(3)).affine(); 219 | let res_a = p.projective().add(&p.projective()); 220 | let res_a = res_a.add(&p.projective()).affine(); 221 | assert_eq!(res_m.x, res_a.x); 222 | 223 | assert_eq!( 224 | res_m.x, 225 | Fr::from_str_vartime( 226 | "19372461775513343691590086534037741906533799473648040012278229434133483800898" 227 | ) 228 | .unwrap(), 229 | ); 230 | assert_eq!( 231 | res_m.y, 232 | Fr::from_str_vartime( 233 | "9458658722007214007257525444427903161243386465067105737478306991484593958249" 234 | ) 235 | .unwrap(), 236 | ); 237 | 238 | let n = Fr::from_str_vartime( 239 | "14035240266687799601661095864649209771790948434046947201833777492504781204499", 240 | ) 241 | .unwrap(); 242 | let res2 = p.mul_scalar(n).affine(); 243 | assert_eq!( 244 | res2.x, 245 | Fr::from_str_vartime( 246 | "17070357974431721403481313912716834497662307308519659060910483826664480189605" 247 | ) 248 | .unwrap(), 249 | ); 250 | assert_eq!( 251 | res2.y, 252 | Fr::from_str_vartime( 253 | "4014745322800118607127020275658861516666525056516280575712425373174125159339" 254 | ) 255 | .unwrap(), 256 | ); 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /eigentrust-zk/src/rescue_prime/sponge.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | gadgets::absorb::AbsorbChip, params::hasher::RoundParams, rescue_prime::RescuePrimeChip, Chip, 3 | Chipset, CommonConfig, FieldExt, RegionCtx, 4 | }; 5 | use halo2::{ 6 | circuit::{AssignedCell, Layouter, Region}, 7 | plonk::{Error, Selector}, 8 | }; 9 | use std::marker::PhantomData; 10 | 11 | #[derive(Clone, Debug)] 12 | /// Selectors for RescuePrime sponge 13 | pub struct RescuePrimeSpongeConfig { 14 | rescue_prime_selector: Selector, 15 | absorb_selector: Selector, 16 | } 17 | 18 | impl RescuePrimeSpongeConfig { 19 | /// Constructs a new config 20 | pub fn new(rescue_prime_selector: Selector, absorb_selector: Selector) -> Self { 21 | Self { rescue_prime_selector, absorb_selector } 22 | } 23 | } 24 | 25 | #[derive(Clone)] 26 | /// Constructs a chip structure for the circuit. 27 | pub struct RescuePrimeSpongeChipset 28 | where 29 | P: RoundParams, 30 | { 31 | /// Constructs a cell vector for the inputs. 32 | inputs: Vec>, 33 | /// Constructs a phantom data for the parameters. 34 | _params: PhantomData

, 35 | } 36 | 37 | impl RescuePrimeSpongeChipset 38 | where 39 | P: RoundParams, 40 | { 41 | /// Create a new chip. 42 | pub fn new() -> Self { 43 | Self { inputs: Vec::new(), _params: PhantomData } 44 | } 45 | 46 | /// Clones and appends all elements from a slice to the vec. 47 | pub fn update(&mut self, inputs: &[AssignedCell]) { 48 | self.inputs.extend_from_slice(inputs); 49 | } 50 | } 51 | 52 | impl Default for RescuePrimeSpongeChipset 53 | where 54 | P: RoundParams, 55 | { 56 | fn default() -> Self { 57 | Self::new() 58 | } 59 | } 60 | 61 | impl Chipset for RescuePrimeSpongeChipset 62 | where 63 | P: RoundParams, 64 | { 65 | type Config = RescuePrimeSpongeConfig; 66 | type Output = AssignedCell; 67 | 68 | fn synthesize( 69 | self, common: &CommonConfig, config: &Self::Config, mut layouter: impl Layouter, 70 | ) -> Result { 71 | assert!(!self.inputs.is_empty()); 72 | 73 | let zero_state = layouter.assign_region( 74 | || "load_initial_state", 75 | |region: Region<'_, F>| { 76 | let mut ctx = RegionCtx::new(region, 0); 77 | 78 | let mut state: [Option>; WIDTH] = [(); WIDTH].map(|_| None); 79 | for i in 0..WIDTH { 80 | let zero_asgn = ctx.assign_from_constant(common.advice[i], F::ZERO)?; 81 | state[i] = Some(zero_asgn); 82 | } 83 | Ok(state.map(|item| item.unwrap())) 84 | }, 85 | )?; 86 | 87 | let mut state = zero_state.clone(); 88 | for (i, chunk) in self.inputs.chunks(WIDTH).enumerate() { 89 | let mut curr_chunk = zero_state.clone(); 90 | curr_chunk[..chunk.len()].clone_from_slice(chunk); 91 | 92 | let absorb = AbsorbChip::new(state, curr_chunk); 93 | let inputs = absorb.synthesize( 94 | common, 95 | &config.absorb_selector, 96 | layouter.namespace(|| format!("absorb_{}", i)), 97 | )?; 98 | 99 | let rescue_prime = RescuePrimeChip::<_, WIDTH, P>::new(inputs); 100 | state = rescue_prime.synthesize( 101 | common, 102 | &config.rescue_prime_selector, 103 | layouter.namespace(|| format!("rescue_prime_permute_{}", i)), 104 | )?; 105 | } 106 | 107 | Ok(state[0].clone()) 108 | } 109 | } 110 | 111 | #[cfg(test)] 112 | mod test { 113 | use super::{AbsorbChip, RescuePrimeSpongeChipset, RescuePrimeSpongeConfig}; 114 | use crate::{ 115 | rescue_prime::{native::sponge::RescuePrimeSponge, RescuePrimeChip}, 116 | Chip, Chipset, CommonConfig, RegionCtx, 117 | }; 118 | 119 | use crate::params::hasher::{hex_to_field, rescue_prime_bn254_5x5::Params}; 120 | 121 | use halo2::{ 122 | circuit::{AssignedCell, Layouter, Region, SimpleFloorPlanner, Value}, 123 | dev::MockProver, 124 | halo2curves::bn256::Fr, 125 | plonk::{Circuit, ConstraintSystem, Error}, 126 | }; 127 | 128 | type TestRescuePrimeSponge = RescuePrimeSponge; 129 | type TestRescuePrimeSpongeChipset = RescuePrimeSpongeChipset; 130 | 131 | #[derive(Clone)] 132 | struct RescuePrimeTesterConfig { 133 | common: CommonConfig, 134 | sponge: RescuePrimeSpongeConfig, 135 | } 136 | 137 | struct RescuePrimeTester { 138 | inputs1: [Value; 5], 139 | inputs2: [Value; 5], 140 | } 141 | 142 | impl RescuePrimeTester { 143 | fn new(inputs1: [Fr; 5], inputs2: [Fr; 5]) -> Self { 144 | Self { 145 | inputs1: inputs1.map(|item| Value::known(item)), 146 | inputs2: inputs2.map(|item| Value::known(item)), 147 | } 148 | } 149 | } 150 | 151 | impl Circuit for RescuePrimeTester { 152 | type Config = RescuePrimeTesterConfig; 153 | type FloorPlanner = SimpleFloorPlanner; 154 | 155 | fn without_witnesses(&self) -> Self { 156 | Self { inputs1: [Value::unknown(); 5], inputs2: [Value::unknown(); 5] } 157 | } 158 | 159 | fn configure(meta: &mut ConstraintSystem) -> Self::Config { 160 | let common = CommonConfig::new(meta); 161 | let absorb_selector = AbsorbChip::<_, 5>::configure(&common, meta); 162 | let rescue_prime_selector = RescuePrimeChip::<_, 5, Params>::configure(&common, meta); 163 | let sponge = RescuePrimeSpongeConfig::new(rescue_prime_selector, absorb_selector); 164 | Self::Config { common, sponge } 165 | } 166 | 167 | fn synthesize( 168 | &self, config: Self::Config, mut layouter: impl Layouter, 169 | ) -> Result<(), Error> { 170 | let inputs1 = layouter.assign_region( 171 | || "load_state1", 172 | |region: Region<'_, Fr>| { 173 | let mut ctx = RegionCtx::new(region, 0); 174 | let mut state: [Option>; 5] = [(); 5].map(|_| None); 175 | for i in 0..5 { 176 | let val = self.inputs1[i].clone(); 177 | let asgn_val = ctx.assign_advice(config.common.advice[i], val)?; 178 | state[i] = Some(asgn_val); 179 | } 180 | Ok(state.map(|item| item.unwrap())) 181 | }, 182 | )?; 183 | 184 | let inputs2 = layouter.assign_region( 185 | || "load_state2", 186 | |region: Region<'_, Fr>| { 187 | let mut ctx = RegionCtx::new(region, 0); 188 | let mut state: [Option>; 5] = [(); 5].map(|_| None); 189 | for i in 0..5 { 190 | let val = self.inputs2[i].clone(); 191 | let asgn_val = ctx.assign_advice(config.common.advice[i], val)?; 192 | state[i] = Some(asgn_val); 193 | } 194 | Ok(state.map(|item| item.unwrap())) 195 | }, 196 | )?; 197 | 198 | let mut rescue_prime_sponge = TestRescuePrimeSpongeChipset::new(); 199 | rescue_prime_sponge.update(&inputs1); 200 | rescue_prime_sponge.update(&inputs2); 201 | let result_state = rescue_prime_sponge.synthesize( 202 | &config.common, 203 | &config.sponge, 204 | layouter.namespace(|| "rescue_prime_sponge"), 205 | )?; 206 | 207 | layouter.constrain_instance(result_state.cell(), config.common.instance, 0)?; 208 | 209 | Ok(()) 210 | } 211 | } 212 | 213 | #[test] 214 | fn should_match_native_sponge() { 215 | // Testing circuit and native sponge equality. 216 | let inputs1: [Fr; 5] = [ 217 | "0x0000000000000000000000000000000000000000000000000000000000000000", 218 | "0x0000000000000000000000000000000000000000000000000000000000000001", 219 | "0x0000000000000000000000000000000000000000000000000000000000000002", 220 | "0x0000000000000000000000000000000000000000000000000000000000000003", 221 | "0x0000000000000000000000000000000000000000000000000000000000000004", 222 | ] 223 | .map(|n| hex_to_field(n)); 224 | 225 | let inputs2: [Fr; 5] = [ 226 | "0x0000000000000000000000000000000000000000000000000000000000000005", 227 | "0x0000000000000000000000000000000000000000000000000000000000000006", 228 | "0x0000000000000000000000000000000000000000000000000000000000000007", 229 | "0x0000000000000000000000000000000000000000000000000000000000000008", 230 | "0x0000000000000000000000000000000000000000000000000000000000000009", 231 | ] 232 | .map(|n| hex_to_field(n)); 233 | 234 | let mut sponge = TestRescuePrimeSponge::new(); 235 | sponge.update(&inputs1); 236 | sponge.update(&inputs2); 237 | 238 | let native_result = sponge.squeeze(); 239 | 240 | let rescue_prime_sponge = RescuePrimeTester::new(inputs1, inputs2); 241 | 242 | let k = 5; 243 | let prover = MockProver::run(k, &rescue_prime_sponge, vec![vec![native_result]]).unwrap(); 244 | assert_eq!(prover.verify(), Ok(())); 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /eigentrust-cli/README.md: -------------------------------------------------------------------------------- 1 | # ZK EigenTrust CLI 2 | 3 | [![MIT licensed][mit-badge]][mit-url] 4 | [![Build Status][actions-badge]][actions-url] 5 | 6 | [mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg 7 | [mit-url]: https://github.com/eigen-trust/protocol/blob/master/LICENSE 8 | [actions-badge]: https://github.com/eigen-trust/protocol/actions/workflows/test.yml/badge.svg 9 | [actions-url]: https://github.com/eigen-trust/protocol/actions?query=branch%3Amaster 10 | 11 | This crate provides a CLI interface to use the `eigentrust` library. It allows you to deploy the smart contracts, submit attestations, calculate the global scores and generate the zk proofs. 12 | 13 | ## Requirements 14 | 15 | - Rust: To install, follow the instructions found [here](https://www.rust-lang.org/tools/install). 16 | - Solidity Compiler: To install, follow the instructions found [here](https://docs.soliditylang.org/en/v0.8.9/installing-solidity.html), or use the [Solidity Compiler Version Manager](https://github.com/alloy-rs/svm-rs) (recommended): 17 | 18 | ```bash 19 | cargo install svm-rs 20 | svm install 0.8.17 21 | ``` 22 | 23 | - Anvil: CLI tool for running a local EVM blockchain. Follow these steps to install Anvil from source: 24 | 25 | ```bash 26 | git clone https://github.com/foundry-rs/foundry 27 | cd foundry 28 | cargo install --path ./anvil --bins --locked --force 29 | ``` 30 | 31 | ## Getting Started 32 | 33 | If you want to use a local EVM blockchain, the first step is to spin up Anvil by running the `anvil` command: 34 | 35 | ```bash 36 | anvil 37 | ``` 38 | 39 | Otherwise you should configure the `node_url` data field of the `config.json` file in the `assets` folder to point to the correct Ethereum node. There's more about this in the configuration section. 40 | 41 | Open a new terminal to use the CLI. Let's build the release version of the crate so we can run it from the `target` directory: 42 | 43 | ```bash 44 | cargo build --release 45 | ``` 46 | 47 | Once the project is built, we need to deploy the AttestationStation smart contract to the blockchain. This is done by running the `deploy` command: 48 | 49 | ```bash 50 | ./target/release/eigentrust-cli deploy 51 | ``` 52 | 53 | The next step is submitting an attestation to a peer in the network. Attestations allow us to give a score to a peer and store that in the blockchain. This is done by running the `attest` command: 54 | 55 | ```bash 56 | ./target/release/eigentrust-cli attest --to 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 --score 5 57 | ``` 58 | 59 | With two peers and one score we can now run the eigentrust algorithm and calculate the global scores, but first we need to generate some important parameters. We'll start generating the KZG public verifier parameters for the eigentrust circuit, it's polynomial degree is 20. 60 | 61 | **This command could take some time to complete depending on your hardware ⏳** 62 | 63 | ```bash 64 | ./target/release/eigentrust-cli kzg-params --k 20 65 | ``` 66 | 67 | Once we have them, it's possible to create a proving key: 68 | 69 | ```bash 70 | ./target/release/eigentrust-cli et-proving-key 71 | ``` 72 | 73 | Now it's time to calculate the global scores and generate a proof that they have been correctly calculated: 74 | 75 | ```bash 76 | ./target/release/eigentrust-cli et-proof 77 | ``` 78 | 79 | Finally, we can verify the generated proof: 80 | 81 | ```bash 82 | ./target/release/eigentrust-cli et-verify 83 | ``` 84 | 85 | And that's it! Keep in mind that if you'd like to keep attesting and generating proofs, you don't need to generate the KZG parameters and the verifying key again. 86 | 87 | ## CLI 88 | 89 | The command-line interface was built using [clap.rs](http://clap.rs/). There is a command description in the help menu, which can be opened passing `-h`. It also provides the following command options: 90 | 91 | - `attest`: Submits an attestation. Takes the following options: 92 | - `--to`: Specify the attested address. 93 | - `--score`: Specify the given score (between 0 and 255). 94 | - `--message`: Specify an optional 32-byte message in hexadecimal format. 95 | - `attestations`: Retrieves and stores all attestations. 96 | - `bandada`: Used to manage Semaphore groups using the Bandada API. It is designed to either add participants to a group or remove them from it. Before executing this command, you should run the `scores` command to ensure having participants' scores, and to setup the `band-id` and `band-th` in the configuration . Please note that when adding a participant, the command checks if their score is above the defined bandada group threshold, and only then proceeds with the addition. It requires the following options: 97 | - `--action (add | remove)`: Defines the action to perform. You can choose to `add` a new member to a group or `remove` an existing member from it. 98 | - `--ic`: Provides the identity commitment of the participant you intend to add or remove from the group. 99 | - `--addr`: Specifies the participant's Ethereum address. 100 | - `deploy`: Deploys the AttestationStation contract. 101 | - `et-proof`: Runs the EigenTrust algorithm to calculate the global scores and stores the generated proof. 102 | - `et-proving-key`: Generates the EigenTrust circuit proving keys. 103 | - `et-verify`: Verifies the stored generated proof for the EigenTrust algorithm. 104 | - `kzg-params`: Generates the KZG parameters. 105 | - `local-scores`: Uses locally stored attestation to calculate the global scores and stores them in the `scores.csv` file within the `assets` folder. 106 | - `scores`: Retrieve attestations and calculates the global scores and stores them in the `scores.csv` file within the `assets` folder. 107 | - `show`: Displays the `config.json` file. 108 | - `th-proof`: Generates a threshold proof for the given ethereum address. 109 | - `th-proving-key`: Generates the threshold circuit proving keys. 110 | - `th-verify`: Verifies the generated threshold proof. 111 | - `update`: Updates the specified field in `config.json`. Takes the following options: 112 | 113 | - `--as-address`: Updates the address of the AttestationStation contract. 114 | - `--domain`: Updates the domain identifier. 115 | - `--band-id`: Updates the bandada group id. 116 | - `--band-th`: Updates the bandada group score threshold. 117 | - `--band-url`: Updates the bandada API endpoint. 118 | - `--chain-id`: Updates the network chain id. 119 | - `--node`: Updates the Ethereum node URL. 120 | 121 | ### Example of `update` command 122 | 123 | ```bash 124 | ./target/release/eigentrust-cli update --node http://localhost:8545 125 | ``` 126 | 127 | ### Example of `attest` command 128 | 129 | ```bash 130 | ./target/release/eigentrust-cli attest --to 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 --score 5 --message 0x473fe1d0de78c8f334d059013d902c13c8b53eb0f669caa9cad677ce1a601167 131 | ``` 132 | 133 | ### Example of `bandada` command 134 | 135 | ```bash 136 | ./target/release/eigentrust-cli scores # Can be skipped for testing, a scores.csv file is provided. 137 | ./target/release/eigentrust-cli update --band-id 51629751621128677209874422363557 --band-th 500 138 | ./target/release/eigentrust-cli bandada --action add --ic 82918723982 --addr 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266 139 | ``` 140 | 141 | ### Example of threshold proofs 142 | 143 | Threshold proofs are generated for a specific participant of the set, in this case we're assuming that you made an attestation with the examples given in this file. 144 | 145 | ```bash 146 | # Generate necessary files 147 | ./target/release/eigentrust-cli kzg-params --k 21 148 | ./target/release/eigentrust-cli th-proving-key 149 | 150 | # Generate and verify proof 151 | ./target/release/eigentrust-cli th-proof --peer 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 152 | ./target/release/eigentrust-cli th-verify 153 | ``` 154 | 155 | ## Configuration 156 | 157 | The configuration file is stored in `eigentrust-cli/assets/config.json`. You may need to update these parameters if, for example, the smart contracts are redeployed to new addresses or if you want to connect to a different Ethereum node. You can modify the following parameters: 158 | 159 | - `as_address`: AttestationStation smart contract address. This is the contract that will receive the attestations. 160 | - `mnemonic`: Ethereum wallet mnemonic phrase. 161 | - `band_id`: Bandada group id. 162 | - `band_th`: Bandada group score threshold. This is the minimum score required to be added to a bandada group. 163 | - `band_url`: Bandada API endpoint. 164 | - `chain_id`: Network chain id. The default is `31337` to work with a local network. 165 | - `node_url`: URL of the Ethereum node we are connecting to. The default is `http://localhost:8545` to work with a local network. 166 | 167 | These parameters can also be modified using the `update` CLI command. 168 | 169 | ## Environment Configuration 170 | 171 | You can customize some settings through environment variables: 172 | 173 | - `MNEMONIC`: Your Ethereum wallet's mnemonic phrase. 174 | - `BANDADA_API_KEY`: The Bandada group API key. 175 | - `LOG_LEVEL`: The logging level. Available options are `error | warn | info | debug | trace`. Default is `info`. 176 | 177 | We've provided a template for these variables in a file named `.env.origin`. You can create a copy of this file and rename it to `.env`: 178 | 179 | ```bash 180 | cp .env.origin .env 181 | ``` 182 | 183 | Next, edit the `.env` file and replace the placeholder values with your actual ones: 184 | 185 | ```bash 186 | MNEMONIC="your mnemonic phrase" 187 | BANDADA_API_KEY="your bandada group api key" 188 | LOG_LEVEL="info" 189 | ``` 190 | 191 | Feel free to only specify variables you want to change from their defaults. 192 | -------------------------------------------------------------------------------- /eigentrust-zk/src/eddsa/native.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use crate::{ 4 | edwards::{ 5 | native::{Point, UnassignedPoint}, 6 | params::{BabyJubJub, EdwardsParams}, 7 | }, 8 | params::hasher::poseidon_bn254_5x5::Params, 9 | poseidon::native::Poseidon, 10 | utils::to_wide, 11 | UnassignedValue, 12 | }; 13 | use halo2::{ 14 | arithmetic::Field, 15 | circuit::Value, 16 | halo2curves::{bn256::Fr, ff::FromUniformBytes, group::ff::PrimeField}, 17 | }; 18 | use num_bigint::BigUint; 19 | use rand::RngCore; 20 | 21 | type Hasher = Poseidon; 22 | 23 | /// Hashes the input with using the BLAKE hash function. 24 | fn blh(b: &[u8]) -> Vec { 25 | let mut hash = [0; 64]; 26 | blake::hash(512, b, &mut hash).unwrap(); 27 | hash.to_vec() 28 | } 29 | 30 | /// Configures a structure for the secret key. 31 | #[derive(Clone, Debug)] 32 | pub struct SecretKey(Fr, Fr); 33 | 34 | impl SecretKey { 35 | /// Constructs SecretKey from raw values 36 | pub fn from_raw(sk_raw: [[u8; 32]; 2]) -> Self { 37 | let part0 = Fr::from_repr(sk_raw[0]).unwrap(); 38 | let part1 = Fr::from_repr(sk_raw[1]).unwrap(); 39 | Self(part0, part1) 40 | } 41 | 42 | /// Convert to raw bytes 43 | pub fn to_raw(&self) -> [[u8; 32]; 2] { 44 | let part0: [u8; 32] = self.0.to_bytes(); 45 | let part1: [u8; 32] = self.1.to_bytes(); 46 | [part0, part1] 47 | } 48 | 49 | /// Returns a secret key from a byte array. 50 | /// Used to produce deterministic outputs. 51 | pub fn from_byte_array(b: &[u8]) -> Self { 52 | let hash: Vec = blh(b); 53 | let bytes_wide = to_wide(&hash[..32]); 54 | let sk0 = Fr::from_uniform_bytes(&bytes_wide); 55 | 56 | let bytes_wide = to_wide(&hash[32..]); 57 | let sk1 = Fr::from_uniform_bytes(&bytes_wide); 58 | SecretKey(sk0, sk1) 59 | } 60 | 61 | /// Randomly generates a field element and returns 62 | /// two hashed values from it. 63 | pub fn random(rng: &mut R) -> Self { 64 | let a = Fr::random(rng); 65 | SecretKey::from_byte_array(&a.to_bytes()) 66 | } 67 | 68 | /// Returns a public key from the secret key. 69 | pub fn public(&self) -> PublicKey { 70 | let (b8_x, b8_y) = BabyJubJub::b8(); 71 | let b8_point = Point::new(b8_x, b8_y); 72 | let a = b8_point.mul_scalar(self.0); 73 | PublicKey(a.affine()) 74 | } 75 | } 76 | 77 | /// Configures a structure for the public key. 78 | #[derive(Hash, Clone, Copy, PartialEq, Eq, Default, Debug)] 79 | pub struct PublicKey(pub Point); 80 | 81 | impl PublicKey { 82 | /// Construct PublicKey from raw data 83 | pub fn from_raw(pk: [[u8; 32]; 2]) -> Self { 84 | let x = Fr::from_repr(pk[0]).unwrap(); 85 | let y = Fr::from_repr(pk[1]).unwrap(); 86 | let point = Point::new(x, y); 87 | Self(point) 88 | } 89 | 90 | /// Convert to raw bytes 91 | pub fn to_raw(&self) -> [[u8; 32]; 2] { 92 | let x = self.0.x.to_bytes(); 93 | let y = self.0.y.to_bytes(); 94 | [x, y] 95 | } 96 | } 97 | 98 | /// Configures a structure for the unassigned public key. 99 | #[derive(Clone, Copy, Default, Debug)] 100 | pub struct UnassignedPublicKey(pub UnassignedPoint); 101 | 102 | impl From for UnassignedPublicKey { 103 | fn from(pk: PublicKey) -> Self { 104 | Self(UnassignedPoint { x: Value::known(pk.0.x), y: Value::known(pk.0.y), _p: PhantomData }) 105 | } 106 | } 107 | 108 | impl UnassignedValue for UnassignedPublicKey { 109 | fn without_witnesses(&self) -> Self { 110 | Self(UnassignedPoint { x: Value::unknown(), y: Value::unknown(), _p: PhantomData }) 111 | } 112 | } 113 | 114 | #[derive(Clone, Debug, PartialEq, Eq)] 115 | /// Configures signature objects. 116 | pub struct Signature { 117 | /// Constructs a point for the R. 118 | pub big_r: Point, 119 | /// Constructs a field element for the s. 120 | pub s: Fr, 121 | } 122 | 123 | impl Signature { 124 | /// Construct signature from the data 125 | pub fn new(r_x: Fr, r_y: Fr, s: Fr) -> Self { 126 | let big_r = Point::new(r_x, r_y); 127 | Self { big_r, s } 128 | } 129 | } 130 | 131 | impl Default for Signature { 132 | fn default() -> Self { 133 | let r_x = Fr::zero(); 134 | let r_y = Fr::zero(); 135 | let s = Fr::zero(); 136 | 137 | let point = Point::new(r_x, r_y); 138 | Self { big_r: point, s } 139 | } 140 | } 141 | 142 | #[derive(Clone, Debug)] 143 | /// Configures unassigned signature objects. 144 | pub struct UnassignedSignature { 145 | /// Constructs a unassigned point for the R. 146 | pub big_r: UnassignedPoint, 147 | /// Constructs a unassigned field element for the s. 148 | pub s: Value, 149 | } 150 | 151 | impl From for UnassignedSignature { 152 | fn from(sig: Signature) -> Self { 153 | Self { 154 | big_r: UnassignedPoint { 155 | x: Value::known(sig.big_r.x), 156 | y: Value::known(sig.big_r.y), 157 | _p: PhantomData, 158 | }, 159 | s: Value::known(sig.s), 160 | } 161 | } 162 | } 163 | 164 | impl UnassignedValue for UnassignedSignature { 165 | fn without_witnesses(&self) -> Self { 166 | Self { 167 | big_r: UnassignedPoint { x: Value::unknown(), y: Value::unknown(), _p: PhantomData }, 168 | s: Value::unknown(), 169 | } 170 | } 171 | } 172 | 173 | /// Returns a signature from given keys and message. 174 | pub fn sign(sk: &SecretKey, pk: &PublicKey, m: Fr) -> Signature { 175 | let inputs = [Fr::zero(), sk.1, m, Fr::zero(), Fr::zero()]; 176 | let r = Hasher::new(inputs).permute()[0]; 177 | let r_bn = BigUint::from_bytes_le(&r.to_bytes()); 178 | 179 | // R = B8 * r 180 | let (b8_x, b8_y) = BabyJubJub::b8(); 181 | let b8_point = Point::new(b8_x, b8_y); 182 | let big_r = b8_point.mul_scalar(r).affine(); 183 | // H(R || PK || M) 184 | let m_hash_input = [big_r.x, big_r.y, pk.0.x, pk.0.y, m]; 185 | let m_hash = Hasher::new(m_hash_input).permute()[0]; 186 | let m_hash_bn = BigUint::from_bytes_le(&m_hash.to_bytes()); 187 | // S = r + H(R || PK || M) * sk0 (mod n) 188 | let sk0 = BigUint::from_bytes_le(&sk.0.to_bytes()); 189 | let s = r_bn + &sk0 * m_hash_bn; 190 | let suborder = BabyJubJub::suborder(); 191 | let s = s % BigUint::from_bytes_le(&suborder.to_bytes()); 192 | let s = Fr::from_uniform_bytes(&to_wide(&s.to_bytes_le())); 193 | 194 | Signature { big_r, s } 195 | } 196 | 197 | /// Checks if the signature holds with the given PK and message. 198 | pub fn verify(sig: &Signature, pk: &PublicKey, m: Fr) -> bool { 199 | let suborder = BabyJubJub::suborder(); 200 | if sig.s > suborder { 201 | // S can't be higher than SUBORDER 202 | return false; 203 | } 204 | // Cl = s * G 205 | let (b8_x, b8_y) = BabyJubJub::b8(); 206 | let b8_point = Point::new(b8_x, b8_y); 207 | let cl = b8_point.mul_scalar(sig.s); 208 | // H(R || PK || M) 209 | let m_hash_input = [sig.big_r.x, sig.big_r.y, pk.0.x, pk.0.y, m]; 210 | let m_hash = Hasher::new(m_hash_input).permute()[0]; 211 | let pk_h = pk.0.mul_scalar(m_hash); 212 | // Cr = R + H(R || PK || M) * PK 213 | let cr = sig.big_r.projective().add(&pk_h); 214 | cr.affine().equals(cl.affine()) 215 | } 216 | 217 | #[cfg(test)] 218 | mod test { 219 | use super::*; 220 | use halo2::halo2curves::group::ff::PrimeField; 221 | use rand::thread_rng; 222 | 223 | #[test] 224 | fn should_sign_and_verify() { 225 | // Testing a valid case. 226 | let mut rng = thread_rng(); 227 | 228 | let sk = SecretKey::random(&mut rng); 229 | let pk = sk.public(); 230 | 231 | let m = Fr::from_str_vartime("123456789012345678901234567890").unwrap(); 232 | let sig = sign(&sk, &pk, m); 233 | let res = verify(&sig, &pk, m); 234 | 235 | assert!(res); 236 | } 237 | 238 | #[test] 239 | fn test_invalid_big_r() { 240 | // Testing invalid R. 241 | let mut rng = thread_rng(); 242 | 243 | let sk = SecretKey::random(&mut rng); 244 | let pk = sk.public(); 245 | 246 | let inputs = [Fr::zero(), Fr::one(), Fr::one(), Fr::zero(), Fr::zero()]; 247 | let different_r = Hasher::new(inputs).permute()[0]; 248 | 249 | let m = Fr::from_str_vartime("123456789012345678901234567890").unwrap(); 250 | let mut sig = sign(&sk, &pk, m); 251 | 252 | let (b8_x, b8_y) = BabyJubJub::b8(); 253 | let b8_point = Point::new(b8_x, b8_y); 254 | sig.big_r = b8_point.mul_scalar(different_r).affine(); 255 | let res = verify(&sig, &pk, m); 256 | 257 | assert_eq!(res, false); 258 | } 259 | 260 | #[test] 261 | fn test_invalid_s() { 262 | // Testing invalid s. 263 | let mut rng = thread_rng(); 264 | 265 | let sk = SecretKey::random(&mut rng); 266 | let pk = sk.public(); 267 | 268 | let m = Fr::from_str_vartime("123456789012345678901234567890").unwrap(); 269 | let mut sig = sign(&sk, &pk, m); 270 | sig.s = sig.s.add(&Fr::from(1)); 271 | let res = verify(&sig, &pk, m); 272 | 273 | assert_eq!(res, false); 274 | } 275 | 276 | #[test] 277 | fn test_invalid_pk() { 278 | // Testing invalid public key. 279 | let mut rng = thread_rng(); 280 | 281 | let sk1 = SecretKey::random(&mut rng); 282 | let pk1 = sk1.public(); 283 | 284 | let sk2 = SecretKey::random(&mut rng); 285 | let pk2 = sk2.public(); 286 | 287 | let m = Fr::from_str_vartime("123456789012345678901234567890").unwrap(); 288 | let sig = sign(&sk1, &pk1, m); 289 | let res = verify(&sig, &pk2, m); 290 | 291 | assert_eq!(res, false); 292 | } 293 | 294 | #[test] 295 | fn test_invalid_message() { 296 | // Testing invalid message. 297 | let mut rng = thread_rng(); 298 | 299 | let sk = SecretKey::random(&mut rng); 300 | let pk = sk.public(); 301 | 302 | let m1 = Fr::from_str_vartime("123456789012345678901234567890").unwrap(); 303 | let sig = sign(&sk, &pk, m1); 304 | let m2 = Fr::from_str_vartime("123456789012345678901234567890123123").unwrap(); 305 | let res = verify(&sig, &pk, m2); 306 | 307 | assert_eq!(res, false); 308 | } 309 | } 310 | -------------------------------------------------------------------------------- /eigentrust-zk/src/verifier/mod.rs: -------------------------------------------------------------------------------- 1 | use halo2::{ 2 | dev::MockProver, 3 | halo2curves::{ 4 | ff::{FromUniformBytes, WithSmallOrderMulGroup}, 5 | group::ff::PrimeField, 6 | pairing::Engine, 7 | serde::SerdeObject, 8 | CurveAffine, 9 | }, 10 | plonk::{create_proof, keygen_pk, keygen_vk, Circuit, ProvingKey, VerifyingKey}, 11 | poly::{ 12 | commitment::{Params, ParamsProver}, 13 | kzg::{ 14 | commitment::{KZGCommitmentScheme, ParamsKZG}, 15 | multiopen::ProverGWC, 16 | }, 17 | }, 18 | transcript::TranscriptWriterBuffer, 19 | }; 20 | use itertools::Itertools; 21 | use rand::rngs::OsRng; 22 | pub use snark_verifier::loader::evm::compile_yul; 23 | use snark_verifier::{ 24 | loader::evm::{self, Address, EvmLoader, ExecutorBuilder}, 25 | pcs::kzg::{Gwc19, KzgAs}, 26 | system::halo2::{compile, transcript::evm::EvmTranscript, Config}, 27 | util::arithmetic::MultiMillerLoop, 28 | verifier::{self, SnarkVerifier}, 29 | }; 30 | use std::{fmt::Debug, rc::Rc}; 31 | use verifier::plonk::PlonkVerifier; 32 | 33 | /// PLONK proof aggregator 34 | pub mod aggregator; 35 | /// Halo2 loader 36 | pub mod loader; 37 | /// Poseidon transcript 38 | pub mod transcript; 39 | 40 | /// Encode instances and proof into calldata. 41 | pub fn encode_calldata(instances: &[Vec], proof: &[u8]) -> Vec 42 | where 43 | F: PrimeField, 44 | { 45 | let mut calldata = Vec::new(); 46 | for inst_row in instances { 47 | for value in inst_row { 48 | let mut bytes = value.to_repr(); 49 | bytes.reverse(); 50 | calldata.extend(bytes); 51 | } 52 | } 53 | calldata.extend(proof); 54 | 55 | calldata 56 | } 57 | 58 | /// Generate Public Key 59 | pub fn gen_pk>( 60 | params: &ParamsKZG, circuit: &C, 61 | ) -> ProvingKey 62 | where 63 | E::Scalar: FromUniformBytes<64>, 64 | E::G1Affine: SerdeObject, 65 | E::G2Affine: SerdeObject, 66 | { 67 | let vk = keygen_vk(params, circuit).unwrap(); 68 | keygen_pk(params, vk, circuit).unwrap() 69 | } 70 | 71 | /// Generate proof 72 | pub fn gen_proof>( 73 | params: &ParamsKZG, pk: &ProvingKey, circuit: C, instances: Vec>, 74 | ) -> Vec 75 | where 76 | E::Scalar: PrimeField + FromUniformBytes<64> + WithSmallOrderMulGroup<3> + Ord, 77 | E::G1Affine: SerdeObject, 78 | E::G2Affine: SerdeObject, 79 | { 80 | MockProver::run(params.k(), &circuit, instances.clone()).unwrap().assert_satisfied(); 81 | 82 | let instances = instances.iter().map(|instances| instances.as_slice()).collect_vec(); 83 | let proof = { 84 | let mut transcript = TranscriptWriterBuffer::<_, E::G1Affine, _>::init(Vec::new()); 85 | create_proof::, ProverGWC<_>, _, _, EvmTranscript<_, _, _, _>, _>( 86 | params, 87 | pk, 88 | &[circuit], 89 | &[instances.as_slice()], 90 | OsRng, 91 | &mut transcript, 92 | ) 93 | .unwrap(); 94 | transcript.finalize() 95 | }; 96 | 97 | proof 98 | } 99 | 100 | /// Generate solidity verifier 101 | pub fn gen_evm_verifier( 102 | params: &ParamsKZG, vk: &VerifyingKey, num_instance: Vec, 103 | ) -> Vec 104 | where 105 | E: MultiMillerLoop, 106 | E::Scalar: PrimeField + FromUniformBytes<64> + WithSmallOrderMulGroup<3> + Ord, 107 | ::Base: PrimeField, 108 | E::G1Affine: SerdeObject, 109 | E::G2Affine: SerdeObject, 110 | { 111 | let code = gen_evm_verifier_code(params, vk, num_instance); 112 | evm::compile_yul(&code) 113 | } 114 | 115 | /// Generate solidity verifier 116 | pub fn gen_evm_verifier_code( 117 | params: &ParamsKZG, vk: &VerifyingKey, num_instance: Vec, 118 | ) -> String 119 | where 120 | E: MultiMillerLoop, 121 | E::Scalar: PrimeField + FromUniformBytes<64> + WithSmallOrderMulGroup<3> + Ord, 122 | ::Base: PrimeField, 123 | E::G1Affine: SerdeObject, 124 | E::G2Affine: SerdeObject, 125 | { 126 | let protocol = compile( 127 | params, 128 | vk, 129 | Config::kzg().with_num_instance(num_instance.clone()), 130 | ); 131 | 132 | let loader = EvmLoader::new::<::Base, E::Scalar>(); 133 | let protocol = protocol.loaded(&loader); 134 | let mut transcript = EvmTranscript::<_, Rc, _, _>::new(&loader); 135 | 136 | let instances = transcript.load_instances(num_instance); 137 | let vk = (params.get_g()[0], params.g2(), params.s_g2()).into(); 138 | 139 | let proof = 140 | PlonkVerifier::>::read_proof(&vk, &protocol, &instances, &mut transcript) 141 | .unwrap(); 142 | PlonkVerifier::>::verify(&vk, &protocol, &instances, &proof).unwrap(); 143 | 144 | loader.yul_code() 145 | } 146 | 147 | /// Verify proof inside the smart contract 148 | pub fn evm_verify(deployment_code: Vec, instances: Vec>, proof: Vec) 149 | where 150 | F: PrimeField, 151 | { 152 | let calldata = encode_calldata(&instances, &proof); 153 | let mut evm = ExecutorBuilder::default().with_gas_limit(u64::MAX.into()).build(); 154 | 155 | let caller = Address::from_low_u64_be(0xfe); 156 | let deployment_result = evm.deploy(caller, deployment_code.into(), 0.into()); 157 | dbg!(deployment_result.exit_reason); 158 | 159 | let verifier_address = deployment_result.address.unwrap(); 160 | let result = evm.call_raw(caller, verifier_address, calldata.into(), 0.into()); 161 | 162 | dbg!(result.gas_used); 163 | dbg!(result.reverted); 164 | dbg!(result.exit_reason); 165 | 166 | let success = !result.reverted; 167 | assert!(success); 168 | } 169 | 170 | #[cfg(test)] 171 | mod test { 172 | use std::usize; 173 | 174 | use crate::{ 175 | utils::{generate_params, prove_and_verify}, 176 | FieldExt, RegionCtx, 177 | }; 178 | use halo2::{ 179 | circuit::{Layouter, Region, SimpleFloorPlanner, Value}, 180 | dev::MockProver, 181 | halo2curves::bn256::{Bn256, Fr}, 182 | plonk::{ 183 | Advice, Circuit, Column, ConstraintSystem, Error, Expression, Fixed, Instance, Selector, 184 | }, 185 | poly::Rotation, 186 | }; 187 | 188 | const NUM_ADVICE: usize = 2; 189 | const NUM_FIXED: usize = 2; 190 | const VERTICAL_SIZE: usize = 1; 191 | 192 | #[derive(Clone)] 193 | struct TestConfigPi { 194 | advice: [Column; NUM_ADVICE], 195 | fixed: [Column; NUM_FIXED], 196 | pi: Column, 197 | selector: Selector, 198 | } 199 | 200 | #[derive(Clone)] 201 | struct TestCircuitPi { 202 | advice: [Value; NUM_ADVICE], 203 | fixed: [F; NUM_FIXED], 204 | } 205 | 206 | impl TestCircuitPi { 207 | fn new(advice: [F; NUM_ADVICE], fixed: [F; NUM_FIXED]) -> Self { 208 | Self { advice: advice.map(|x| Value::known(x)), fixed } 209 | } 210 | } 211 | 212 | impl Circuit for TestCircuitPi { 213 | type Config = TestConfigPi; 214 | type FloorPlanner = SimpleFloorPlanner; 215 | 216 | fn without_witnesses(&self) -> Self { 217 | Self { advice: [(); NUM_ADVICE].map(|_| Value::unknown()), fixed: self.fixed.clone() } 218 | } 219 | 220 | fn configure(meta: &mut ConstraintSystem) -> TestConfigPi { 221 | let advice = [(); NUM_ADVICE].map(|_| meta.advice_column()); 222 | let fixed = [(); NUM_FIXED].map(|_| meta.fixed_column()); 223 | let pi = meta.instance_column(); 224 | let s = meta.selector(); 225 | 226 | advice.map(|c| meta.enable_equality(c)); 227 | fixed.map(|c| meta.enable_equality(c)); 228 | meta.enable_equality(pi); 229 | 230 | meta.create_gate("add", |v_cells| { 231 | let s_exp = v_cells.query_selector(s); 232 | let advice_set_exp = advice.map(|c| v_cells.query_advice(c, Rotation::cur())); 233 | let fixed_set_exp = fixed.map(|c| v_cells.query_fixed(c, Rotation::cur())); 234 | 235 | let mut sum = Expression::Constant(F::ZERO); 236 | for i in 0..advice_set_exp.len() { 237 | sum = sum + advice_set_exp[i].clone(); 238 | } 239 | for i in 0..fixed_set_exp.len() { 240 | sum = sum + fixed_set_exp[i].clone(); 241 | } 242 | 243 | let c_exp = v_cells.query_advice(advice[0], Rotation::next()); 244 | 245 | let res = c_exp - sum; 246 | 247 | vec![s_exp * res] 248 | }); 249 | 250 | TestConfigPi { advice, fixed, pi, selector: s } 251 | } 252 | 253 | fn synthesize( 254 | &self, config: TestConfigPi, mut layouter: impl Layouter, 255 | ) -> Result<(), Error> { 256 | for s in 0..S { 257 | let res = layouter.assign_region( 258 | || "add", 259 | |region: Region<'_, F>| { 260 | let mut ctx = RegionCtx::new(region, 0); 261 | ctx.enable(config.selector)?; 262 | 263 | for i in 0..self.advice.len() { 264 | ctx.assign_advice(config.advice[i], self.advice[i])?; 265 | } 266 | for i in 0..self.fixed.len() { 267 | ctx.assign_fixed(config.fixed[i], self.fixed[i])?; 268 | } 269 | 270 | let mut sum = Value::known(F::ZERO); 271 | for i in 0..self.advice.len() { 272 | sum = sum + self.advice[i]; 273 | } 274 | for i in 0..self.fixed.len() { 275 | sum = sum + Value::known(self.fixed[i]); 276 | } 277 | 278 | ctx.next(); 279 | 280 | let c_cell = ctx.assign_advice(config.advice[0], sum)?; 281 | Ok(c_cell) 282 | }, 283 | )?; 284 | 285 | layouter.constrain_instance(res.cell(), config.pi, s)?; 286 | } 287 | 288 | Ok(()) 289 | } 290 | } 291 | 292 | #[test] 293 | fn verify_dummy_pi_dev() { 294 | let advice = [Fr::one(); NUM_ADVICE]; 295 | let fixed = [Fr::one(); NUM_FIXED]; 296 | let mut sum = Fr::zero(); 297 | for i in 0..advice.len() { 298 | sum += advice[i]; 299 | } 300 | for i in 0..fixed.len() { 301 | sum += fixed[i]; 302 | } 303 | 304 | let circuit = TestCircuitPi::<_, VERTICAL_SIZE>::new(advice, fixed); 305 | let k = 4; 306 | 307 | let pub_ins = vec![sum; VERTICAL_SIZE]; 308 | let prover = MockProver::run(k, &circuit, vec![pub_ins]).unwrap(); 309 | assert_eq!(prover.verify(), Ok(())); 310 | } 311 | 312 | #[test] 313 | fn verify_dummy_pi_prod() { 314 | let rng = &mut rand::thread_rng(); 315 | let advice = [Fr::one(); NUM_ADVICE]; 316 | let fixed = [Fr::one(); NUM_FIXED]; 317 | let mut sum = Fr::zero(); 318 | for i in 0..advice.len() { 319 | sum += advice[i]; 320 | } 321 | for i in 0..fixed.len() { 322 | sum += fixed[i]; 323 | } 324 | let circuit = TestCircuitPi::<_, VERTICAL_SIZE>::new(advice, fixed); 325 | 326 | let k = 4; 327 | let params = generate_params(k); 328 | 329 | let pub_ins = vec![sum; VERTICAL_SIZE]; 330 | let res = prove_and_verify::(params, circuit, &[&pub_ins], rng).unwrap(); 331 | assert!(res); 332 | } 333 | } 334 | -------------------------------------------------------------------------------- /eigentrust-zk/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! The module for the main EigenTrust circuit. 2 | 3 | // Rustc 4 | #![warn(trivial_casts)] 5 | #![deny( 6 | absolute_paths_not_starting_with_crate, deprecated, future_incompatible, missing_docs, 7 | nonstandard_style, unreachable_code, unreachable_patterns 8 | )] 9 | #![forbid(unsafe_code)] 10 | // Clippy 11 | #![allow(clippy::tabs_in_doc_comments, clippy::needless_range_loop, clippy::new_without_default)] 12 | #![deny( 13 | // Complexity 14 | clippy::unnecessary_cast, 15 | clippy::needless_question_mark, 16 | clippy::clone_on_copy, 17 | // Pedantic 18 | clippy::cast_lossless, 19 | clippy::cast_possible_wrap, 20 | // Perf 21 | clippy::redundant_clone, 22 | // Restriction 23 | clippy::panic, 24 | // Style 25 | clippy::let_and_return, 26 | clippy::needless_borrow 27 | )] 28 | 29 | use std::fmt::Debug; 30 | use std::hash::Hash; 31 | 32 | use halo2::halo2curves::bn256::Fr as Scalar; 33 | use halo2::plonk::TableColumn; 34 | use halo2::{ 35 | circuit::{AssignedCell, Layouter, Region, Value}, 36 | halo2curves::{ 37 | bn256::{Fq as BnBase, Fr as BnScalar}, 38 | ff::{FromUniformBytes, PrimeField}, 39 | secp256k1::{Fp as SecpBase, Fq as SecpScalar}, 40 | }, 41 | plonk::{Advice, Column, ConstraintSystem, Error, Fixed, Instance, Selector}, 42 | }; 43 | 44 | pub use halo2; 45 | use serde::{Deserialize, Serialize}; 46 | 47 | /// EigenTrust-related circuits 48 | pub mod circuits; 49 | /// Ecc arithemtic on wrong field 50 | pub mod ecc; 51 | /// ECDSA signature scheme gadgets + native version 52 | pub mod ecdsa; 53 | /// EDDSA signature scheme gadgets + native version 54 | pub mod eddsa; 55 | /// Edwards curve operations 56 | pub mod edwards; 57 | /// Common gadgets used across circuits 58 | pub mod gadgets; 59 | /// Integer type - Wrong field arithmetic 60 | /// 61 | /// NOTE: `integer`-related chipsets assume `NUM_LIMBS = 4` & `20 advice cols` 62 | pub mod integer; 63 | /// MerkleTree 64 | pub mod merkle_tree; 65 | /// A module for defining round parameters and MDS matrix for hash 66 | /// permutations 67 | pub mod params; 68 | /// Poseidon hash function gadgets + native version 69 | pub mod poseidon; 70 | /// Rescue Prime hash function gadgets + native version 71 | pub mod rescue_prime; 72 | 73 | /// Utilities for proving and verifying 74 | pub mod utils; 75 | /// PLONK verifier 76 | pub mod verifier; 77 | 78 | /// Extention to the traits provided by halo2 79 | pub trait FieldExt: PrimeField + FromUniformBytes<64> + Hash {} 80 | impl FieldExt for BnBase {} 81 | impl FieldExt for BnScalar {} 82 | impl FieldExt for SecpBase {} 83 | impl FieldExt for SecpScalar {} 84 | 85 | /// Hasher trait 86 | pub trait Hasher { 87 | /// Creates a new hasher 88 | fn new(inputs: [F; WIDTH]) -> Self; 89 | /// Finalize the hasher 90 | fn finalize(&self) -> [F; WIDTH]; 91 | } 92 | 93 | /// Sponge Hasher trait 94 | pub trait SpongeHasher: Clone { 95 | /// Creates a new sponge hasher 96 | fn new() -> Self; 97 | /// Update current sponge state 98 | fn update(&mut self, inputs: &[F]); 99 | /// Finalize the sponge hasher 100 | fn squeeze(&mut self) -> F; 101 | } 102 | 103 | /// Hasher chipset trait 104 | pub trait HasherChipset: Chipset + Clone { 105 | /// Creates a new hasher chipset 106 | fn new(inputs: [AssignedCell; WIDTH]) -> Self; 107 | /// Configure 108 | fn configure(common: &CommonConfig, meta: &mut ConstraintSystem) -> Self::Config; 109 | /// Finalize the hasher 110 | fn finalize( 111 | self, common: &CommonConfig, config: &Self::Config, layouter: impl Layouter, 112 | ) -> Result<[AssignedCell; WIDTH], Error>; 113 | } 114 | 115 | /// Sponge Hasher chipset trait 116 | pub trait SpongeHasherChipset: Clone + Debug { 117 | /// Config selectors for the sponge 118 | type Config: Clone + Debug; 119 | /// Creates a new sponge hasher chipset 120 | fn init(common: &CommonConfig, layouter: impl Layouter) -> Result; 121 | /// Configure 122 | fn configure(common: &CommonConfig, meta: &mut ConstraintSystem) -> Self::Config; 123 | /// Update current sponge chipset state 124 | fn update(&mut self, inputs: &[AssignedCell]); 125 | /// Finalize the sponge hasher 126 | fn squeeze( 127 | &mut self, common: &CommonConfig, config: &Self::Config, layouter: impl Layouter, 128 | ) -> Result, Error>; 129 | } 130 | 131 | /// UnassignedValue Trait 132 | pub trait UnassignedValue { 133 | /// Returns unknown value type 134 | fn without_witnesses(&self) -> Self; 135 | } 136 | 137 | #[derive(Debug)] 138 | /// Region Context struct for managing region assignments 139 | pub struct RegionCtx<'a, F: FieldExt> { 140 | /// Region struct 141 | region: Region<'a, F>, 142 | /// Current row offset 143 | offset: usize, 144 | } 145 | 146 | impl<'a, F: FieldExt> RegionCtx<'a, F> { 147 | /// Construct new Region Context 148 | pub fn new(region: Region<'a, F>, offset: usize) -> RegionCtx<'a, F> { 149 | RegionCtx { region, offset } 150 | } 151 | 152 | /// Return current row offset 153 | pub fn offset(&self) -> usize { 154 | self.offset 155 | } 156 | 157 | /// Turn into region struct 158 | pub fn into_region(self) -> Region<'a, F> { 159 | self.region 160 | } 161 | 162 | /// Assign value to a fixed column 163 | pub fn assign_fixed( 164 | &mut self, column: Column, value: F, 165 | ) -> Result, Error> { 166 | self.region.assign_fixed( 167 | || format!("fixed_{}", self.offset), 168 | column, 169 | self.offset, 170 | || Value::known(value), 171 | ) 172 | } 173 | 174 | /// Assign to advice column from an instance column 175 | pub fn assign_from_instance( 176 | &mut self, advice: Column, instance: Column, index: usize, 177 | ) -> Result, Error> { 178 | self.region.assign_advice_from_instance( 179 | || format!("advice_{}", self.offset), 180 | instance, 181 | index, 182 | advice, 183 | self.offset, 184 | ) 185 | } 186 | 187 | /// Assign to advice column from an fixed column 188 | pub fn assign_from_constant( 189 | &mut self, advice: Column, constant: F, 190 | ) -> Result, Error> { 191 | self.region.assign_advice_from_constant( 192 | || format!("advice_{}", self.offset), 193 | advice, 194 | self.offset, 195 | constant, 196 | ) 197 | } 198 | 199 | /// Assign value to an advice column 200 | pub fn assign_advice( 201 | &mut self, column: Column, value: Value, 202 | ) -> Result, Error> { 203 | self.region.assign_advice( 204 | || format!("advice_{}", self.offset), 205 | column, 206 | self.offset, 207 | || value, 208 | ) 209 | } 210 | 211 | /// Copy value from passed assigned cell into an advice column 212 | pub fn copy_assign( 213 | &mut self, column: Column, value: AssignedCell, 214 | ) -> Result, Error> { 215 | value.copy_advice( 216 | || format!("advice_{}", self.offset), 217 | &mut self.region, 218 | column, 219 | self.offset, 220 | ) 221 | } 222 | 223 | /// Constrain two cells to be equal 224 | pub fn constrain_equal( 225 | &mut self, a_cell: AssignedCell, b_cell: AssignedCell, 226 | ) -> Result<(), Error> { 227 | self.region.constrain_equal(a_cell.cell(), b_cell.cell()) 228 | } 229 | 230 | /// Constrain a cell to be equal to a constant 231 | pub fn constrain_to_constant( 232 | &mut self, a_cell: AssignedCell, constant: F, 233 | ) -> Result<(), Error> { 234 | self.region.constrain_constant(a_cell.cell(), constant) 235 | } 236 | 237 | /// Enable selector at the current row offset 238 | pub fn enable(&mut self, selector: Selector) -> Result<(), Error> { 239 | selector.enable(&mut self.region, self.offset) 240 | } 241 | 242 | /// Increment the row offset 243 | pub fn next(&mut self) { 244 | self.offset += 1 245 | } 246 | } 247 | 248 | /// Number of advice columns in common config 249 | pub const ADVICE: usize = 20; 250 | /// Number of fixed columns in common config 251 | pub const FIXED: usize = 10; 252 | 253 | /// Common config for the whole circuit 254 | #[derive(Clone, Debug)] 255 | pub struct CommonConfig { 256 | /// Advice columns 257 | advice: [Column; ADVICE], 258 | /// Fixed columns 259 | fixed: [Column; FIXED], 260 | /// Table column 261 | #[allow(dead_code)] 262 | table: TableColumn, 263 | /// Instance column 264 | instance: Column, 265 | } 266 | 267 | impl CommonConfig { 268 | /// Create a new `CommonConfig` columns 269 | pub fn new(meta: &mut ConstraintSystem) -> Self { 270 | let advice = [(); ADVICE].map(|_| meta.advice_column()); 271 | let fixed = [(); FIXED].map(|_| meta.fixed_column()); 272 | let table = meta.lookup_table_column(); 273 | let instance = meta.instance_column(); 274 | 275 | advice.map(|c| meta.enable_equality(c)); 276 | fixed.map(|c| meta.enable_constant(c)); 277 | meta.enable_equality(instance); 278 | 279 | Self { advice, fixed, table, instance } 280 | } 281 | } 282 | 283 | /// Trait for an atomic chip implementation 284 | /// Each chip uses common config columns, but has its own selector 285 | pub trait Chip { 286 | /// Output of the synthesis 287 | type Output: Clone; 288 | /// Gate configuration, using common config columns 289 | fn configure(common: &CommonConfig, meta: &mut ConstraintSystem) -> Selector; 290 | /// Chip synthesis. This function can return an assigned cell to be used 291 | /// elsewhere in the circuit 292 | fn synthesize( 293 | self, common: &CommonConfig, selector: &Selector, layouter: impl Layouter, 294 | ) -> Result; 295 | } 296 | 297 | /// Chipset uses a collection of chips as primitives to build more abstract 298 | /// circuits 299 | pub trait Chipset { 300 | /// Config used for synthesis 301 | type Config: Clone; 302 | /// Output of the synthesis 303 | type Output: Clone; 304 | /// Chipset synthesis. This function can have multiple smaller chips 305 | /// synthesised inside. Also can returns an assigned cell. 306 | fn synthesize( 307 | self, common: &CommonConfig, config: &Self::Config, layouter: impl Layouter, 308 | ) -> Result; 309 | } 310 | 311 | #[derive(Debug, Clone)] 312 | /// Structure for holding the ZK proof and public inputs needed for verification 313 | pub struct Proof { 314 | /// Public inputs 315 | pub pub_ins: Vec, 316 | /// Proof bytes 317 | pub proof: Vec, 318 | } 319 | 320 | impl From for Proof { 321 | fn from(value: ProofRaw) -> Self { 322 | let pub_ins = value.pub_ins.iter().map(|x| Scalar::from_bytes(x).unwrap()).collect(); 323 | let proof = value.proof; 324 | 325 | Self { pub_ins, proof } 326 | } 327 | } 328 | 329 | #[derive(Debug, Clone, Serialize, Deserialize)] 330 | /// Structure for holding the ZK proof and raw public inputs 331 | pub struct ProofRaw { 332 | /// Public inputs 333 | pub pub_ins: Vec<[u8; 32]>, 334 | /// Proof bytes 335 | pub proof: Vec, 336 | } 337 | 338 | impl From for ProofRaw { 339 | fn from(value: Proof) -> Self { 340 | let pub_ins = value.pub_ins.iter().map(|x| x.to_bytes()).collect(); 341 | let proof = value.proof; 342 | 343 | ProofRaw { pub_ins, proof } 344 | } 345 | } 346 | -------------------------------------------------------------------------------- /eigentrust-zk/src/gadgets/lt_eq.rs: -------------------------------------------------------------------------------- 1 | use super::{bits2num::Bits2NumChip, main::MainConfig}; 2 | use crate::{ 3 | gadgets::main::IsZeroChipset, utils::to_wide, Chip, Chipset, CommonConfig, FieldExt, RegionCtx, 4 | }; 5 | use halo2::{ 6 | circuit::{AssignedCell, Layouter, Region, Value}, 7 | plonk::{ConstraintSystem, Error, Expression, Selector}, 8 | poly::Rotation, 9 | }; 10 | use std::vec; 11 | 12 | /// 1 << 252 13 | pub const N_SHIFTED: [u8; 32] = [ 14 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 15 | ]; 16 | /// Numbers are limited to 252 to avoid overflow 17 | pub const NUM_BITS: usize = 252; 18 | /// Same number of bits as N_SHIFTED, since NUM + N_SHIFTED is the operation. 19 | pub const DIFF_BITS: usize = 253; 20 | 21 | /// Chip for finding the difference between 2 numbers with size of 252 bits 22 | pub struct NShiftedChip { 23 | x: AssignedCell, 24 | y: AssignedCell, 25 | } 26 | 27 | impl NShiftedChip { 28 | /// Constructs a new chip 29 | pub fn new(x: AssignedCell, y: AssignedCell) -> Self { 30 | Self { x, y } 31 | } 32 | } 33 | 34 | impl Chip for NShiftedChip { 35 | type Output = AssignedCell; 36 | 37 | fn configure(common: &CommonConfig, meta: &mut ConstraintSystem) -> Selector { 38 | // 39 | // IMPORTANT: For the maximal usage of CommonConfig columns(20 advice + 10 fixed), 40 | // we use the advice column 16 - 18. (17th ~ 19th) 41 | // 42 | 43 | let selector = meta.selector(); 44 | let n_shifted = F::from_uniform_bytes(&to_wide(&N_SHIFTED)); 45 | 46 | meta.create_gate("x + n_shifted - y", |v_cells| { 47 | let n_shifted_exp = Expression::Constant(n_shifted); 48 | 49 | let s_exp = v_cells.query_selector(selector); 50 | let x_exp = v_cells.query_advice(common.advice[16], Rotation::cur()); 51 | let y_exp = v_cells.query_advice(common.advice[17], Rotation::cur()); 52 | let res_exp = v_cells.query_advice(common.advice[18], Rotation::cur()); 53 | 54 | vec![ 55 | // (x + n_shifted - y) - z == 0 56 | // n_shifted value is equal to smallest 253 bit number. 57 | // Because of that calculations will be done in between the 252 to 254-bit range. 58 | // That range can hold 252-bit number calculations without overflowing. 59 | // Example: 60 | // x = 5; 61 | // y = 3; 62 | // z = (x + n_shifted - y); 63 | // z = (5 - 3) + n_shifted = 2 + n_shifted => 64 | // diff_bits holds (x + n_shifted - y) as bits. 65 | // After that, checking the constraint diff_bits - z = 0. 66 | s_exp * ((x_exp + n_shifted_exp - y_exp) - res_exp), 67 | ] 68 | }); 69 | 70 | selector 71 | } 72 | 73 | fn synthesize( 74 | self, common: &CommonConfig, selector: &Selector, mut layouter: impl Layouter, 75 | ) -> Result { 76 | layouter.assign_region( 77 | || "less_than_equal", 78 | |region: Region<'_, F>| { 79 | let mut ctx = RegionCtx::new(region, 0); 80 | ctx.enable(*selector)?; 81 | 82 | let assigned_x = ctx.copy_assign(common.advice[16], self.x.clone())?; 83 | let assigned_y = ctx.copy_assign(common.advice[17], self.y.clone())?; 84 | 85 | let n_shifted = Value::known(F::from_uniform_bytes(&to_wide(&N_SHIFTED))); 86 | let res = assigned_x.value().cloned() + n_shifted - assigned_y.value(); 87 | 88 | let assigned_res = ctx.assign_advice(common.advice[18], res)?; 89 | 90 | Ok(assigned_res) 91 | }, 92 | ) 93 | } 94 | } 95 | 96 | #[derive(Clone, Debug)] 97 | /// Selectors for LessEqualChipset 98 | pub struct LessEqualConfig { 99 | main: MainConfig, 100 | bits_2_num_selector: Selector, 101 | n_shifted_selector: Selector, 102 | } 103 | 104 | impl LessEqualConfig { 105 | /// Constructs new config 106 | pub fn new( 107 | main: MainConfig, bits_2_num_selector: Selector, n_shifted_selector: Selector, 108 | ) -> Self { 109 | Self { main, bits_2_num_selector, n_shifted_selector } 110 | } 111 | } 112 | 113 | /// A chip for checking if number is in range 114 | pub struct LessEqualChipset { 115 | x: AssignedCell, 116 | y: AssignedCell, 117 | } 118 | 119 | impl LessEqualChipset { 120 | /// Constructs a new chipset 121 | pub fn new(x: AssignedCell, y: AssignedCell) -> Self { 122 | Self { x, y } 123 | } 124 | } 125 | 126 | impl Chipset for LessEqualChipset { 127 | type Config = LessEqualConfig; 128 | type Output = AssignedCell; 129 | 130 | /// Synthesize the circuit. 131 | fn synthesize( 132 | self, common: &CommonConfig, config: &Self::Config, mut layouter: impl Layouter, 133 | ) -> Result { 134 | let x_b2n = Bits2NumChip::new_exact::(self.x.clone()); 135 | let _ = x_b2n.synthesize( 136 | common, 137 | &config.bits_2_num_selector, 138 | layouter.namespace(|| "x_b2n"), 139 | )?; 140 | 141 | let y_b2n = Bits2NumChip::new_exact::(self.y.clone()); 142 | let _ = y_b2n.synthesize( 143 | common, 144 | &config.bits_2_num_selector, 145 | layouter.namespace(|| "y_b2n"), 146 | )?; 147 | 148 | let n_shifted_chip = NShiftedChip::new(self.x, self.y); 149 | let inp = n_shifted_chip.synthesize( 150 | common, 151 | &config.n_shifted_selector, 152 | layouter.namespace(|| "n_shifted_diff"), 153 | )?; 154 | 155 | let diff_b2n = Bits2NumChip::new_exact::(inp); 156 | let bits = diff_b2n.synthesize( 157 | common, 158 | &config.bits_2_num_selector, 159 | layouter.namespace(|| "bits2num"), 160 | )?; 161 | 162 | // Check the last bit. 163 | // If it is 1, that means the result is bigger than 253 bits. 164 | // This means x is bigger than y and is_zero will return 0. 165 | // If it is 0, that means the result is smaller than 253 bits. 166 | // This means y is bigger than x and is_zero will return 1. 167 | // If both are equal last bit still will be 1 and the number will be exactly 253 168 | // bits. In that case, is_zero will return 0 as well. 169 | let is_zero_chip = IsZeroChipset::new(bits[DIFF_BITS - 1].clone()); 170 | let res = 171 | is_zero_chip.synthesize(common, &config.main, layouter.namespace(|| "is_zero"))?; 172 | Ok(res) 173 | } 174 | } 175 | 176 | #[cfg(test)] 177 | mod test { 178 | use super::*; 179 | use crate::{ 180 | gadgets::main::MainChip, 181 | utils::{generate_params, prove_and_verify}, 182 | }; 183 | use halo2::{ 184 | circuit::{SimpleFloorPlanner, Value}, 185 | dev::MockProver, 186 | halo2curves::{ 187 | bn256::{Bn256, Fr}, 188 | ff::FromUniformBytes, 189 | }, 190 | plonk::Circuit, 191 | }; 192 | 193 | #[derive(Clone)] 194 | struct TestConfig { 195 | common: CommonConfig, 196 | lt_eq: LessEqualConfig, 197 | } 198 | 199 | #[derive(Clone)] 200 | struct TestCircuit { 201 | x: Value, 202 | y: Value, 203 | } 204 | 205 | impl TestCircuit { 206 | fn new(x: Fr, y: Fr) -> Self { 207 | Self { x: Value::known(x), y: Value::known(y) } 208 | } 209 | } 210 | 211 | impl Circuit for TestCircuit { 212 | type Config = TestConfig; 213 | type FloorPlanner = SimpleFloorPlanner; 214 | 215 | fn without_witnesses(&self) -> Self { 216 | Self { x: Value::unknown(), y: Value::unknown() } 217 | } 218 | 219 | fn configure(meta: &mut ConstraintSystem) -> TestConfig { 220 | let common = CommonConfig::new(meta); 221 | let main = MainConfig::new(MainChip::configure(&common, meta)); 222 | 223 | let b2n_selector = Bits2NumChip::configure(&common, meta); 224 | let ns_selector = NShiftedChip::configure(&common, meta); 225 | let lt_eq = LessEqualConfig::new(main, b2n_selector, ns_selector); 226 | 227 | TestConfig { common, lt_eq } 228 | } 229 | 230 | fn synthesize( 231 | &self, config: TestConfig, mut layouter: impl Layouter, 232 | ) -> Result<(), Error> { 233 | let (x, y) = layouter.assign_region( 234 | || "temp", 235 | |region: Region<'_, Fr>| { 236 | let mut ctx = RegionCtx::new(region, 0); 237 | let x = ctx.assign_advice(config.common.advice[0], self.x)?; 238 | let y = ctx.assign_advice(config.common.advice[1], self.y)?; 239 | Ok((x, y)) 240 | }, 241 | )?; 242 | let lt_eq_chip = LessEqualChipset::::new(x, y); 243 | let res = lt_eq_chip.synthesize( 244 | &config.common, 245 | &config.lt_eq, 246 | layouter.namespace(|| "less_eq"), 247 | )?; 248 | 249 | layouter.constrain_instance(res.cell(), config.common.instance, 0)?; 250 | Ok(()) 251 | } 252 | } 253 | 254 | #[test] 255 | fn test_less_than_y_x() { 256 | // Testing x > y. 257 | let x = Fr::from(8); 258 | let y = Fr::from(4); 259 | 260 | let test_chip = TestCircuit::new(x, y); 261 | 262 | let k = 9; 263 | let pub_ins = vec![Fr::from(0)]; 264 | let prover = MockProver::run(k, &test_chip, vec![pub_ins]).unwrap(); 265 | assert_eq!(prover.verify(), Ok(())); 266 | } 267 | 268 | #[test] 269 | fn test_less_than_x_y() { 270 | // Testing x < y. 271 | let x = Fr::from(3); 272 | let y = Fr::from(9); 273 | 274 | let test_chip = TestCircuit::new(x, y); 275 | 276 | let k = 9; 277 | let pub_ins = vec![Fr::from(1)]; 278 | let prover = MockProver::run(k, &test_chip, vec![pub_ins]).unwrap(); 279 | assert_eq!(prover.verify(), Ok(())); 280 | } 281 | 282 | #[test] 283 | fn test_less_than_x_y_equal() { 284 | // Testing x = y. 285 | let x = Fr::from(4); 286 | let y = Fr::from(4); 287 | 288 | let test_chip = TestCircuit::new(x, y); 289 | 290 | let k = 9; 291 | let pub_ins = vec![Fr::from(0)]; 292 | let prover = MockProver::run(k, &test_chip, vec![pub_ins]).unwrap(); 293 | assert_eq!(prover.verify(), Ok(())); 294 | } 295 | 296 | #[test] 297 | fn test_less_than_x252_y() { 298 | // Testing x = biggest 252 bit number. 299 | let bit252 = Fr::from_uniform_bytes(&to_wide(&N_SHIFTED)); 300 | let x = bit252.sub(&Fr::one()); 301 | let y = Fr::from(9); 302 | 303 | let test_chip = TestCircuit::new(x, y); 304 | 305 | let k = 9; 306 | let pub_ins = vec![Fr::from(0)]; 307 | let prover = MockProver::run(k, &test_chip, vec![pub_ins]).unwrap(); 308 | assert_eq!(prover.verify(), Ok(())); 309 | } 310 | 311 | #[test] 312 | fn test_less_than_x_y252() { 313 | // Testing y = biggest 252 bit number. 314 | let bit252 = Fr::from_uniform_bytes(&to_wide(&N_SHIFTED)); 315 | let x = Fr::from(2); 316 | let y = bit252.sub(&Fr::from(1)); 317 | 318 | let test_chip = TestCircuit::new(x, y); 319 | 320 | let k = 9; 321 | let pub_ins = vec![Fr::from(1)]; 322 | let prover = MockProver::run(k, &test_chip, vec![pub_ins]).unwrap(); 323 | assert_eq!(prover.verify(), Ok(())); 324 | } 325 | 326 | #[test] 327 | fn test_less_than_x252_y252() { 328 | // Testing x = y = biggest 252 bit number. 329 | let bit252 = Fr::from_uniform_bytes(&to_wide(&N_SHIFTED)); 330 | let x = bit252.sub(&Fr::from(1)); 331 | let y = bit252.sub(&Fr::from(1)); 332 | 333 | let test_chip = TestCircuit::new(x, y); 334 | 335 | let k = 9; 336 | let pub_ins = vec![Fr::from(0)]; 337 | let prover = MockProver::run(k, &test_chip, vec![pub_ins]).unwrap(); 338 | assert_eq!(prover.verify(), Ok(())); 339 | } 340 | 341 | #[test] 342 | fn test_less_than_production() { 343 | let x = Fr::from(8); 344 | let y = Fr::from(4); 345 | let test_chip = TestCircuit::new(x, y); 346 | 347 | let k = 9; 348 | let rng = &mut rand::thread_rng(); 349 | let params = generate_params(k); 350 | let pub_ins = [Fr::from(0)]; 351 | let res = prove_and_verify::(params, test_chip, &[&pub_ins], rng).unwrap(); 352 | 353 | assert!(res); 354 | } 355 | } 356 | -------------------------------------------------------------------------------- /eigentrust-zk/src/gadgets/bits2num.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | utils::{assigned_to_field, field_to_bits, field_to_bits_vec}, 3 | Chip, CommonConfig, FieldExt, RegionCtx, 4 | }; 5 | use halo2::{ 6 | circuit::{AssignedCell, Layouter, Region, Value}, 7 | plonk::{ConstraintSystem, Error, Expression, Selector}, 8 | poly::Rotation, 9 | }; 10 | 11 | /// Constructs a cell and a variable for the circuit. 12 | #[derive(Clone)] 13 | pub struct Bits2NumChip { 14 | /// Assigns a cell for the value. 15 | value: AssignedCell, 16 | /// Constructs bits variable for the circuit. 17 | bits: Vec>, 18 | } 19 | 20 | impl Bits2NumChip { 21 | /// Create a new chip. 22 | pub fn new_exact(value: AssignedCell) -> Self { 23 | let fe = assigned_to_field(value.clone()); 24 | let bit_vals = match fe { 25 | Some(fe) => field_to_bits::<_, B>(fe).map(|x| Value::known(x)).to_vec(), 26 | None => field_to_bits::<_, B>(F::ZERO).map(|_| Value::unknown()).to_vec(), 27 | }; 28 | Self { value, bits: bit_vals } 29 | } 30 | 31 | /// Create a new chip. 32 | pub fn new(value: AssignedCell) -> Self { 33 | let fe = assigned_to_field(value.clone()); 34 | let bit_vals = match fe { 35 | Some(fe) => field_to_bits_vec(fe).iter().map(|&x| Value::known(x)).collect(), 36 | None => field_to_bits_vec(F::ZERO).iter().map(|_| Value::unknown()).collect(), 37 | }; 38 | Self { value, bits: bit_vals } 39 | } 40 | } 41 | 42 | impl Chip for Bits2NumChip { 43 | type Output = Vec>; 44 | 45 | /// Make the circuit config. 46 | fn configure(common: &CommonConfig, meta: &mut ConstraintSystem) -> Selector { 47 | let selector = meta.selector(); 48 | 49 | meta.create_gate("bits2num", |v_cells| { 50 | // 51 | // Gate config 52 | // 53 | // | selector | 0 | 1 | 2 | 3 | 4 | 5 | 54 | // |----------|--------|---------|----------|------------|---------|----------| 55 | // | * | bit[i] | e2 | lc1 | bit[i + 1] | e2 | lc1 | 56 | // | | | e2_next | lc1_next | | | | 57 | // 58 | // NOTE: Current chip config & synthesize is based on assumption that 59 | // the length of `bits` is even number. 60 | // 61 | // bits.len() % 2 == 0 62 | 63 | // 64 | // Example: value = 13 (1101) 65 | // bits = 1011 (little-endian order) 66 | // 67 | // | selector | 0 | 1 | 2 | 3 | 4 | 5 | 68 | // |----------|--------|---------|----------|------------|---------|----------| 69 | // | * | 1 | 1 | 0 | 0 | 2 | 1 | 70 | // | * | 1 | 4 | 1 | 1 | 8 | 5 | 71 | // | | | 16 | 13 | | | | 72 | // 73 | // Original, more intelligible config example 74 | // 75 | // | selector | 0 | 1 | 2 | 76 | // |----------|--------|---------|----------| 77 | // | * | 1 | 1 | 0 | 78 | // | * | 0 | 2 | 1 | 79 | // | * | 1 | 4 | 1 | 80 | // | * | 1 | 8 | 5 | 81 | // | | | 16 | 13 | 82 | // 83 | 84 | // IMPORTANT: For the maximal usage of CommonConfig columns(20 advice + 10 fixed), 85 | // we use the advice column 10 - 15. (11th ~ 16th) 86 | 87 | let one_exp = Expression::Constant(F::ONE); 88 | 89 | let bit_i_exp = v_cells.query_advice(common.advice[10], Rotation::cur()); 90 | let e2_i_exp = v_cells.query_advice(common.advice[11], Rotation::cur()); 91 | let lc1_i_exp = v_cells.query_advice(common.advice[12], Rotation::cur()); 92 | 93 | let bit_i_1_exp = v_cells.query_advice(common.advice[13], Rotation::cur()); 94 | let e2_i_1_exp = v_cells.query_advice(common.advice[14], Rotation::cur()); 95 | let lc1_i_1_exp = v_cells.query_advice(common.advice[15], Rotation::cur()); 96 | 97 | let e2_next_exp = v_cells.query_advice(common.advice[11], Rotation::next()); 98 | let lc1_next_exp = v_cells.query_advice(common.advice[12], Rotation::next()); 99 | 100 | let s_exp = v_cells.query_selector(selector); 101 | 102 | vec![ 103 | // bit * (1 - bit) == 0 104 | // Constraining bit to be a boolean. 105 | s_exp.clone() * (bit_i_exp.clone() * (one_exp.clone() - bit_i_exp.clone())), 106 | s_exp.clone() * (bit_i_1_exp.clone() * (one_exp - bit_i_1_exp.clone())), 107 | // e2 + e2 == e2_next 108 | // Starting from 1, doubling. 109 | s_exp.clone() * ((e2_i_exp.clone() + e2_i_exp.clone()) - e2_i_1_exp.clone()), 110 | s_exp.clone() * ((e2_i_1_exp.clone() + e2_i_1_exp.clone()) - e2_next_exp), 111 | // lc1 + bit * e2 == lc1_next 112 | // If the bit is equal to 1, e2 will be added to the sum. 113 | // Example: 114 | // bit = 1 115 | // e2 = 1 (first rotation) 116 | // lc1 = 0 117 | // If the bit == 1, double the e2. 118 | // This will be used in the next rotation, if bit == 1 again. (e2_next = 1 + 1 = 2) 119 | // 120 | // Check the constraint => (1 * 1 + 0) 121 | // lc1_next = 1 122 | s_exp.clone() * ((bit_i_exp * e2_i_exp + lc1_i_exp) - lc1_i_1_exp.clone()), 123 | s_exp * ((bit_i_1_exp * e2_i_1_exp + lc1_i_1_exp) - lc1_next_exp), 124 | ] 125 | }); 126 | 127 | selector 128 | } 129 | 130 | /// Synthesize the circuit. 131 | fn synthesize( 132 | self, common: &CommonConfig, selector: &Selector, mut layouter: impl Layouter, 133 | ) -> Result { 134 | layouter.assign_region( 135 | || "bits2num", 136 | |region: Region<'_, F>| { 137 | let mut ctx = RegionCtx::new(region, 0); 138 | let mut lc1 = ctx.assign_from_constant(common.advice[12], F::ZERO)?; 139 | let mut e2 = ctx.assign_from_constant(common.advice[11], F::ONE)?; 140 | 141 | let mut res_bits = Vec::new(); 142 | 143 | // If the length of `self.bits` is odd, we add extra zero at the end. 144 | // It does not affect the proval since the most significant bit(last element of bits) becomes zero. 145 | let mut input_bits = self.bits.clone(); 146 | if self.bits.len() % 2 == 1 { 147 | input_bits.push(Value::known(F::ZERO)); 148 | } 149 | for i in (0..input_bits.len()).step_by(2) { 150 | ctx.enable(*selector)?; 151 | 152 | // assign `bits[i]` & compute next values 153 | let bit_i = ctx.assign_advice(common.advice[10], input_bits[i])?; 154 | 155 | let lc1_i_1 = 156 | lc1.value().cloned() + bit_i.value().cloned() * e2.value().cloned(); 157 | let e2_i_1 = e2.value().cloned() + e2.value(); 158 | 159 | // assign `bits[i + 1]` & compute next values 160 | let bit_i_1 = ctx.assign_advice(common.advice[13], input_bits[i + 1])?; 161 | let e2_i_1 = ctx.assign_advice(common.advice[14], e2_i_1)?; 162 | let lc1_1 = ctx.assign_advice(common.advice[15], lc1_i_1)?; 163 | 164 | let lc1_next = 165 | lc1_1.value().cloned() + bit_i_1.value().cloned() * e2_i_1.value().cloned(); 166 | let e2_next = e2_i_1.value().cloned() + e2_i_1.value(); 167 | 168 | res_bits.push(bit_i.clone()); 169 | res_bits.push(bit_i_1.clone()); 170 | 171 | ctx.next(); 172 | e2 = ctx.assign_advice(common.advice[11], e2_next)?; 173 | lc1 = ctx.assign_advice(common.advice[12], lc1_next)?; 174 | } 175 | ctx.constrain_equal(self.value.clone(), lc1)?; 176 | 177 | // If the length of `self.bits` is odd, it means that we have added extra zero at the end of bits. 178 | // Hence, we should pop the last bit element. 179 | if self.bits.len() % 2 == 1 { 180 | res_bits.pop(); 181 | } 182 | 183 | Ok(res_bits) 184 | }, 185 | ) 186 | } 187 | } 188 | 189 | #[cfg(test)] 190 | mod test { 191 | use super::*; 192 | use crate::{ 193 | utils::{generate_params, prove_and_verify}, 194 | CommonConfig, 195 | }; 196 | use halo2::{ 197 | circuit::SimpleFloorPlanner, 198 | dev::MockProver, 199 | halo2curves::bn256::{Bn256, Fr}, 200 | plonk::Circuit, 201 | }; 202 | 203 | #[derive(Clone)] 204 | struct TestConfig { 205 | common: CommonConfig, 206 | bits2num_selector: Selector, 207 | } 208 | 209 | #[derive(Clone)] 210 | struct TestCircuit { 211 | numba: Value, 212 | } 213 | 214 | impl TestCircuit { 215 | fn new(x: Fr) -> Self { 216 | Self { numba: Value::known(x) } 217 | } 218 | } 219 | 220 | impl Circuit for TestCircuit { 221 | type Config = TestConfig; 222 | type FloorPlanner = SimpleFloorPlanner; 223 | 224 | fn without_witnesses(&self) -> Self { 225 | Self { numba: Value::unknown() } 226 | } 227 | 228 | fn configure(meta: &mut ConstraintSystem) -> TestConfig { 229 | let common = CommonConfig::new(meta); 230 | let bits2num_selector = Bits2NumChip::configure(&common, meta); 231 | 232 | TestConfig { common, bits2num_selector } 233 | } 234 | 235 | fn synthesize( 236 | &self, config: TestConfig, mut layouter: impl Layouter, 237 | ) -> Result<(), Error> { 238 | let numba = layouter.assign_region( 239 | || "temp", 240 | |region: Region<'_, Fr>| { 241 | let mut ctx = RegionCtx::new(region, 0); 242 | ctx.assign_advice(config.common.advice[0], self.numba) 243 | }, 244 | )?; 245 | 246 | let bits2num = Bits2NumChip::new_exact::(numba); 247 | let _ = bits2num.synthesize( 248 | &config.common, 249 | &config.bits2num_selector, 250 | layouter.namespace(|| "bits2num"), 251 | )?; 252 | 253 | Ok(()) 254 | } 255 | } 256 | 257 | #[test] 258 | fn test_bits_to_num() { 259 | // Testing field element 0x01234567890abcdef. 260 | let numba = Fr::from(1311768467294899695u64); 261 | 262 | let circuit = TestCircuit::<256>::new(numba); 263 | let k = 8; 264 | let prover = MockProver::run(k, &circuit, vec![vec![]]).unwrap(); 265 | 266 | assert_eq!(prover.verify(), Ok(())); 267 | } 268 | 269 | #[test] 270 | fn test_bits_to_num_big() { 271 | // Testing biggest value in the field. 272 | let numba = Fr::zero().sub(&Fr::one()); 273 | 274 | let circuit = TestCircuit::<256>::new(numba); 275 | let k = 8; 276 | let prover = MockProver::run(k, &circuit, vec![vec![]]).unwrap(); 277 | 278 | assert_eq!(prover.verify(), Ok(())); 279 | } 280 | 281 | #[test] 282 | fn test_bits_to_num_big_plus() { 283 | // Testing biggest value in the field + 1. 284 | let circuit = TestCircuit::<256>::new(Fr::zero()); 285 | let k = 8; 286 | let prover = MockProver::run(k, &circuit, vec![vec![]]).unwrap(); 287 | 288 | assert_eq!(prover.verify(), Ok(())); 289 | } 290 | 291 | #[test] 292 | fn test_bits_to_num_zero_bits() { 293 | // Testing zero as value with 0 bits. 294 | let circuit = TestCircuit::<0>::new(Fr::zero()); 295 | let k = 8; 296 | let prover = MockProver::run(k, &circuit, vec![vec![]]).unwrap(); 297 | 298 | assert_eq!(prover.verify(), Ok(())); 299 | } 300 | 301 | #[test] 302 | fn test_bits_to_num_zero_value() { 303 | // Testing zero as value with 254 bits. 304 | let circuit = TestCircuit::<254>::new(Fr::zero()); 305 | let k = 8; 306 | let prover = MockProver::run(k, &circuit, vec![vec![]]).unwrap(); 307 | 308 | assert_eq!(prover.verify(), Ok(())); 309 | } 310 | 311 | #[test] 312 | fn test_bits_to_num_production() { 313 | let numba = Fr::from(1311768467294899695u64); 314 | let circuit = TestCircuit::<256>::new(numba); 315 | let k = 8; 316 | let rng = &mut rand::thread_rng(); 317 | let params = generate_params(k); 318 | let res = prove_and_verify::(params, circuit, &[&[]], rng).unwrap(); 319 | 320 | assert!(res); 321 | } 322 | } 323 | --------------------------------------------------------------------------------