├── .github └── workflows │ └── test.yaml ├── .gitignore ├── Cargo.toml ├── README.md ├── ac_compiler ├── Cargo.toml └── src │ ├── circuit.rs │ ├── circuit_compiler.rs │ ├── constraint_builder.rs │ ├── error.rs │ ├── example_circuits.rs │ ├── gate.rs │ ├── lib.rs │ ├── tests.rs │ └── variable.rs ├── fiat_shamir_rng ├── Cargo.toml └── src │ └── lib.rs ├── functional_commitment ├── Cargo.toml └── src │ ├── lib.rs │ └── tests.rs ├── homomorphic_poly_commit ├── Cargo.toml └── src │ ├── error.rs │ ├── lib.rs │ ├── marlin_kzg.rs │ └── sonic_kzg.rs ├── index_private_marlin ├── Cargo.toml └── src │ ├── ahp │ ├── constraint_systems.rs │ ├── indexer.rs │ ├── mod.rs │ ├── prover.rs │ └── verifier.rs │ ├── data_structures.rs │ ├── error.rs │ ├── lib.rs │ └── well_formation.rs ├── proof_of_function_relation ├── Cargo.toml └── src │ ├── discrete_log_comparison │ ├── mod.rs │ ├── piop │ │ ├── mod.rs │ │ └── prover.rs │ ├── proof.rs │ └── tests.rs │ ├── error.rs │ ├── geo_seq │ ├── mod.rs │ ├── proof.rs │ └── tests.rs │ ├── lib.rs │ ├── non_zero_over_k │ ├── mod.rs │ ├── piop │ │ ├── mod.rs │ │ └── prover.rs │ ├── proof.rs │ └── tests.rs │ ├── subset_over_k │ ├── mod.rs │ └── proof.rs │ ├── t_diag │ ├── mod.rs │ ├── piop │ │ └── mod.rs │ ├── proof.rs │ └── tests.rs │ ├── t_functional_triple │ ├── mod.rs │ ├── proof.rs │ └── tests.rs │ ├── t_strictly_lower_triangular_test │ ├── mod.rs │ ├── proof.rs │ └── tests.rs │ └── util.rs └── zero_over_k ├── Cargo.toml └── src ├── error.rs ├── lib.rs ├── util.rs ├── virtual_oracle ├── generic_shifting_vo │ ├── mod.rs │ ├── presets.rs │ ├── tests.rs │ └── vo_term.rs └── mod.rs └── zero_over_k ├── mod.rs ├── piop ├── mod.rs ├── prover.rs └── verifier.rs ├── proof.rs └── tests.rs /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - main 7 | env: 8 | RUST_BACKTRACE: 1 9 | CARGO_NET_GIT_FETCH_WITH_CLI: true 10 | 11 | jobs: 12 | style: 13 | name: Check Style 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v1 18 | - name: Install Rust 19 | uses: actions-rs/toolchain@v1 20 | with: 21 | profile: minimal 22 | override: true 23 | toolchain: stable 24 | components: rustfmt 25 | 26 | - name: cargo fmt --check 27 | uses: actions-rs/cargo@v1 28 | with: 29 | command: fmt 30 | args: --all -- --check 31 | 32 | test: 33 | name: Test 34 | runs-on: ubuntu-latest 35 | env: 36 | RUSTFLAGS: -Dwarnings 37 | strategy: 38 | matrix: 39 | rust: 40 | - stable 41 | steps: 42 | - name: Checkout 43 | uses: actions/checkout@v2 44 | - name: Install Rust (${{ matrix.rust }}) 45 | uses: actions-rs/toolchain@v1 46 | with: 47 | profile: minimal 48 | toolchain: ${{ matrix.rust }} 49 | override: true 50 | 51 | - name: Test 52 | uses: actions-rs/cargo@v1 53 | with: 54 | command: test 55 | args: --release --all --no-fail-fast 56 | 57 | build-wasm: 58 | name: Build non-native targets 59 | runs-on: ubuntu-latest 60 | env: 61 | RUSTFLAGS: -Dwarnings 62 | strategy: 63 | matrix: 64 | rust: 65 | - stable 66 | target: 67 | - wasm32-unknown-unknown 68 | steps: 69 | - name: Checkout 70 | uses: actions/checkout@v2 71 | - name: Install Rust (${{ matrix.rust }}) 72 | uses: actions-rs/toolchain@v1 73 | with: 74 | profile: minimal 75 | toolchain: ${{ matrix.rust }} 76 | target: ${{ matrix.target }} 77 | override: true 78 | 79 | - name: Build 80 | uses: actions-rs/cargo@v1 81 | with: 82 | command: build 83 | args: --release --all --target ${{ matrix.target }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | target 5 | .vscode 6 | 7 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 8 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 9 | Cargo.lock 10 | 11 | # These are backup files generated by rustfmt 12 | **/*.rs.bk 13 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "homomorphic_poly_commit", 5 | "zero_over_k", 6 | "fiat_shamir_rng", 7 | "proof_of_function_relation", 8 | "ac_compiler", 9 | "index_private_marlin", 10 | "functional_commitment" 11 | ] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Function-Hiding Functional Commitment 2 | 3 | A proof-of-concept implementation of the Marlin-based efficient function-hiding functional commitments https://eprint.iacr.org/2021/1342 4 | 5 | This repository contains experimental cryptographic code and should not be used in production. 6 | -------------------------------------------------------------------------------- /ac_compiler/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ac_compiler" 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 | ark-bls12-381 = "0.3.0" 10 | ark-ff = "0.3.0" 11 | ark-poly = "0.3.0" 12 | ark-poly-commit = "0.3.0" 13 | ark-relations = "0.3.0" 14 | ark-std = "0.3.0" 15 | rand_core = {version = "0.6", default-features=false, features = ["getrandom"] } 16 | rand = "0.8.4" 17 | 18 | ark-ec = "0.3.0" 19 | ark-serialize = "0.3.0" 20 | digest = { version = "0.9" } 21 | 22 | [dev-dependencies] 23 | proof_of_function_relation = { path = "../proof_of_function_relation" } 24 | # index_private_marlin = { path = "../index_private_marlin" } 25 | ark-marlin = "0.3.0" 26 | ark-bn254 = "0.3.0" 27 | blake2 = { version = "0.9", default-features = false } 28 | homomorphic_poly_commit = { path = "../homomorphic_poly_commit" } 29 | fiat_shamir_rng = { path = "../fiat_shamir_rng"} 30 | rand_chacha = { version = "0.3.0", default-features = false } 31 | -------------------------------------------------------------------------------- /ac_compiler/src/circuit.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::Field; 2 | 3 | use crate::{constraint_builder::ConstraintBuilder, error::Error, gate::Gate}; 4 | 5 | #[derive(Clone, PartialEq, Eq, Debug)] 6 | pub struct Circuit { 7 | pub gates: Vec, 8 | pub number_of_inputs: usize, 9 | pub number_of_outputs: usize, 10 | } 11 | 12 | impl Circuit { 13 | pub fn new(gates: Vec, number_of_inputs: usize, number_of_outputs: usize) -> Self { 14 | Self { 15 | gates, 16 | number_of_inputs, 17 | number_of_outputs, 18 | } 19 | } 20 | 21 | pub fn from_constraint_builder(cb: &ConstraintBuilder) -> Self { 22 | Self { 23 | gates: cb.gates.clone(), 24 | number_of_inputs: cb.number_of_inputs, 25 | number_of_outputs: cb.number_of_outputs, 26 | } 27 | } 28 | 29 | pub fn synthesize(f: Func, cb: &mut ConstraintBuilder) -> Result 30 | where 31 | F: Field, 32 | Func: FnOnce(&mut ConstraintBuilder) -> Result<(), Error>, 33 | { 34 | f(cb)?; 35 | cb.finalize(); 36 | Ok(Self::from_constraint_builder(&cb)) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ac_compiler/src/circuit_compiler.rs: -------------------------------------------------------------------------------- 1 | use crate::{Matrix, R1CSfIndex}; 2 | use ark_ff::PrimeField; 3 | // use ark_marlin::ahp::indexer::Matrix; 4 | use std::{cmp::max, marker::PhantomData}; 5 | 6 | use crate::{circuit::Circuit, empty_matrix, gate::GateType}; 7 | 8 | /// Given: an arithmetic circuit with ng gates, ni inputs, and no <= ng outputs, where gates are triples of (left_input_index, right_input_index, (add/mul)) 9 | /// Produces: An index for R_R1CS-f(ng + ni + 1, ni + 1, no) 10 | pub trait CircuitCompiler { 11 | fn ac2tft(circuit: &Circuit) -> (R1CSfIndex, Matrix, Matrix, Matrix); 12 | } 13 | 14 | pub struct VanillaCompiler { 15 | _f: PhantomData, 16 | } 17 | 18 | impl CircuitCompiler for VanillaCompiler { 19 | fn ac2tft(circuit: &Circuit) -> (R1CSfIndex, Matrix, Matrix, Matrix) { 20 | let number_of_constraints = circuit.gates.len() + circuit.number_of_inputs + 1; 21 | let number_of_input_rows = circuit.number_of_inputs + 1; // this is the `t` value in a t-functional triple 22 | let number_of_outputs = circuit.number_of_outputs; 23 | 24 | // num of non zero entires should be max of nom of non zero in a, b, c 25 | let mut number_of_non_zero_entries_a = 0; 26 | let mut number_of_non_zero_entries_b = 0; 27 | let mut number_of_non_zero_entries_c = 0; 28 | 29 | let mut a_matrix = empty_matrix(number_of_constraints); 30 | let mut b_matrix = empty_matrix(number_of_constraints); 31 | let mut c_matrix = empty_matrix(number_of_constraints); 32 | 33 | // 0 + var_index is intentionaly left to notate that indices from the compiler are already shifted by one (because of the dummy selector) 34 | for (i, gate) in circuit.gates.iter().enumerate() { 35 | c_matrix[1 + circuit.number_of_inputs + i] 36 | .push((F::one(), 1 + circuit.number_of_inputs + i)); 37 | number_of_non_zero_entries_c += 1; 38 | match gate.symbol { 39 | GateType::Add => { 40 | a_matrix[1 + circuit.number_of_inputs + i].push((F::one(), 0)); 41 | b_matrix[1 + circuit.number_of_inputs + i] 42 | .push((F::one(), 0 + gate.left_index)); 43 | b_matrix[1 + circuit.number_of_inputs + i] 44 | .push((F::one(), 0 + gate.right_index)); 45 | number_of_non_zero_entries_a += 1; 46 | number_of_non_zero_entries_b += 2; 47 | } 48 | GateType::Mul => { 49 | a_matrix[1 + circuit.number_of_inputs + i] 50 | .push((F::one(), 0 + gate.left_index)); 51 | b_matrix[1 + circuit.number_of_inputs + i] 52 | .push((F::one(), 0 + gate.right_index)); 53 | number_of_non_zero_entries_a += 1; 54 | number_of_non_zero_entries_b += 1; 55 | } 56 | } 57 | } 58 | 59 | let number_of_non_zero_entries = max( 60 | number_of_non_zero_entries_a, 61 | max(number_of_non_zero_entries_b, number_of_non_zero_entries_c), 62 | ); 63 | 64 | let index_info = R1CSfIndex { 65 | number_of_constraints, 66 | number_of_input_rows, 67 | number_of_outputs, 68 | number_of_non_zero_entries, 69 | }; 70 | 71 | (index_info, a_matrix, b_matrix, c_matrix) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /ac_compiler/src/constraint_builder.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::BTreeMap, marker::PhantomData}; 2 | 3 | use crate::{ 4 | error::Error, 5 | gate::{Gate, GateType}, 6 | variable::{Variable, VariableType}, 7 | }; 8 | use ark_ff::Field; 9 | 10 | pub struct ConstraintBuilder { 11 | pub(crate) gates: Vec, 12 | label_to_var_index: BTreeMap, 13 | curr_index: usize, 14 | pub(crate) number_of_inputs: usize, 15 | pub(crate) number_of_outputs: usize, 16 | pub assignment: Vec, 17 | _f: PhantomData, 18 | } 19 | 20 | impl ConstraintBuilder { 21 | pub fn new() -> Self { 22 | Self { 23 | gates: Vec::new(), 24 | label_to_var_index: BTreeMap::new(), 25 | curr_index: 1, // we reserve first input to be dummy selector constraint for addition 26 | number_of_inputs: 0, 27 | number_of_outputs: 0, 28 | assignment: vec![F::one()], // we add 1 to be the value of dummy selector 29 | _f: PhantomData, 30 | } 31 | } 32 | 33 | pub fn new_input_variable(&mut self, label: &str, value: F) -> Result, Error> { 34 | self.register_new_var(label, value, VariableType::Input) 35 | } 36 | 37 | pub fn enforce_constraint( 38 | &mut self, 39 | lhs: &Variable, 40 | rhs: &Variable, 41 | constraint_type: GateType, 42 | variable_type: VariableType, 43 | ) -> Result, Error> { 44 | let lhs_index = match self.label_to_var_index.get(&lhs.label) { 45 | Some(index) => Ok(*index), 46 | None => Err(Error::VarMissing(format!( 47 | "Var with label {} doesn't exists", 48 | lhs.label 49 | ))), 50 | }?; 51 | 52 | let rhs_index = match self.label_to_var_index.get(&rhs.label) { 53 | Some(index) => Ok(*index), 54 | None => Err(Error::VarMissing(format!( 55 | "Var with label {} doesn't exists", 56 | lhs.label 57 | ))), 58 | }?; 59 | 60 | let new_value = match constraint_type { 61 | GateType::Add => { 62 | self.gates.push(Gate { 63 | left_index: lhs_index, 64 | right_index: rhs_index, 65 | symbol: GateType::Add, 66 | }); 67 | lhs.value + rhs.value 68 | } 69 | GateType::Mul => { 70 | self.gates.push(Gate { 71 | left_index: lhs_index, 72 | right_index: rhs_index, 73 | symbol: GateType::Mul, 74 | }); 75 | lhs.value * rhs.value 76 | } 77 | }; 78 | 79 | // for now automatically assign wtns_prefix to intermidiate valies 80 | let new_label = format!("w_{}", self.curr_index); 81 | self.register_new_var(&new_label, new_value, variable_type) 82 | } 83 | 84 | fn register_new_var( 85 | &mut self, 86 | label: &str, 87 | value: F, 88 | variable_type: VariableType, 89 | ) -> Result, Error> { 90 | // we don't allow vars with same labels 91 | if self.label_to_var_index.contains_key(label.into()) { 92 | return Err(Error::VarAlreadyExists(format!( 93 | "Var with label {} already exists", 94 | label 95 | ))); 96 | } 97 | 98 | let var = Variable { 99 | label: label.into(), 100 | value, 101 | variable_type, 102 | }; 103 | 104 | self.label_to_var_index 105 | .insert(label.into(), self.curr_index); 106 | 107 | // whenever new var is added, global index is incremented by one 108 | // this follows from the fact that each gate introduces another intermidiate var 109 | self.curr_index += 1; 110 | 111 | match variable_type { 112 | VariableType::Input => { 113 | self.number_of_inputs += 1; 114 | } 115 | VariableType::Witness => {} 116 | VariableType::Output => { 117 | self.number_of_outputs += 1; 118 | } 119 | }; 120 | 121 | self.assignment.push(value); 122 | 123 | Ok(var) 124 | } 125 | 126 | // we want assignment to be power of 2 127 | // after all user constraints are added 128 | // additional dummy gates are appended to fullfil this 129 | pub fn finalize(&mut self) { 130 | let next_power_of_2 = |val: usize| -> usize { 131 | let val_f64 = val as f64; 132 | let log2_val = val_f64.log2().floor() as u32; 133 | if 2u32.pow(log2_val) == val_f64 as u32 { 134 | return val; 135 | } else { 136 | return 2u32.pow(1u32 + log2_val).try_into().unwrap(); 137 | } 138 | }; 139 | 140 | let unnormalized_num_of_constraints = self.gates.len() + self.number_of_inputs + 1; 141 | let number_of_constraints = next_power_of_2(unnormalized_num_of_constraints); 142 | let number_of_dummy_constraints = number_of_constraints - unnormalized_num_of_constraints; 143 | 144 | // dummy constraint is represented as 1 * 1 = 1 where we refer to dummy variable at first position 145 | let output_gates = self 146 | .gates 147 | .split_off(self.gates.len() - self.number_of_outputs); 148 | let dummy_gates = vec![Gate::new(0, 0, GateType::Mul); number_of_dummy_constraints]; 149 | self.gates.extend(dummy_gates); 150 | self.gates.extend(output_gates); 151 | 152 | let outputs = self 153 | .assignment 154 | .split_off(unnormalized_num_of_constraints - self.number_of_outputs); 155 | let dummy_assignment = vec![F::one(); number_of_dummy_constraints]; 156 | self.assignment.extend(&dummy_assignment); 157 | self.assignment.extend(&outputs); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /ac_compiler/src/error.rs: -------------------------------------------------------------------------------- 1 | #[derive(PartialEq, Debug)] 2 | pub enum Error { 3 | VarAlreadyExists(String), 4 | VarMissing(String), 5 | } 6 | -------------------------------------------------------------------------------- /ac_compiler/src/example_circuits.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | circuit::Circuit, 3 | gate::{Gate, GateType}, 4 | }; 5 | 6 | /// Encode the circuit x^2 + 5 7 | pub fn sample_circuit_1() -> Circuit { 8 | // index 0: x [public] 9 | // index 1: 5 [public] 10 | // index 2: x^2 [witness] 11 | let gate_0 = Gate::new(0, 0, GateType::Mul); 12 | let gate_1 = Gate::new(2, 1, GateType::Add); 13 | 14 | Circuit::new(vec![gate_0, gate_1], 2, 1) 15 | } 16 | 17 | /// Encode the circuit x^3 + 2x + 5 18 | pub fn sample_circuit_2() -> Circuit { 19 | // index 0: 2 [public] 20 | // index 1: 5 [public] 21 | // index 2: x [public] 22 | // index 3: x^2 [witness] 23 | // index 4: x^3 [witness] 24 | // index 5: 2x [witness] 25 | // index 6: x^3 + 2x [witness] 26 | 27 | let gate_0 = Gate::new(2, 2, GateType::Mul); // produce x^2 28 | let gate_1 = Gate::new(3, 2, GateType::Mul); // produce x^3 29 | let gate_2 = Gate::new(0, 2, GateType::Mul); // produce 2x 30 | let gate_3 = Gate::new(4, 5, GateType::Add); // produce x^3 + 2x 31 | let gate_4 = Gate::new(6, 1, GateType::Add); // produce the output 32 | 33 | Circuit::new(vec![gate_0, gate_1, gate_2, gate_3, gate_4], 3, 1) 34 | } 35 | -------------------------------------------------------------------------------- /ac_compiler/src/gate.rs: -------------------------------------------------------------------------------- 1 | /// Type of an arithmetic gate 2 | #[derive(Clone, PartialEq, Eq, Debug, Ord, PartialOrd)] 3 | pub enum GateType { 4 | Add, 5 | Mul, 6 | } 7 | 8 | #[derive(Clone, PartialEq, Eq, Debug)] 9 | pub struct Gate { 10 | pub left_index: usize, 11 | pub right_index: usize, 12 | pub symbol: GateType, 13 | } 14 | 15 | impl Gate { 16 | pub fn new(left_index: usize, right_index: usize, symbol: GateType) -> Self { 17 | Self { 18 | left_index, 19 | right_index, 20 | symbol, 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ac_compiler/src/lib.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::PrimeField; 2 | use ark_poly::{EvaluationDomain, GeneralEvaluationDomain}; 3 | use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError}; 4 | 5 | pub mod circuit; 6 | pub mod circuit_compiler; 7 | pub mod constraint_builder; 8 | pub mod error; 9 | pub mod example_circuits; 10 | pub mod gate; 11 | pub mod tests; 12 | pub mod variable; 13 | 14 | use ark_std::io::{Read, Write}; 15 | 16 | pub type Matrix = Vec>; 17 | 18 | /// A structure containing the output-final R1CS encoding of an arithmetic circuit. There are `t` input rows, 19 | /// the first is always reserved for the constant 1. All other input rows are for public data, regardless of 20 | /// whether this is a public variable or public constant. 21 | #[derive(PartialEq, Eq, Clone, Debug, CanonicalSerialize, CanonicalDeserialize)] 22 | pub struct R1CSfIndex { 23 | /// Number of constrains (this is also the length of the matrices) 24 | pub number_of_constraints: usize, 25 | 26 | /// Number of rows that are reserved for the inputs. This is the `t` value in a t-functional triple. 27 | /// Note that this is the **number of public inputs to the circuit plus 1** (this is by construction) 28 | pub number_of_input_rows: usize, 29 | 30 | /// Number of outputs 31 | pub number_of_outputs: usize, 32 | 33 | /// The maximum number of non-zero entries 34 | pub number_of_non_zero_entries: usize, 35 | // pub a: Matrix, 36 | // pub b: Matrix, 37 | // pub c: Matrix, 38 | } 39 | 40 | impl ark_ff::ToBytes for R1CSfIndex { 41 | fn write(&self, mut w: W) -> ark_std::io::Result<()> { 42 | (self.number_of_constraints as u64).write(&mut w)?; 43 | (self.number_of_input_rows as u64).write(&mut w)?; 44 | (self.number_of_outputs as u64).write(&mut w)?; 45 | (self.number_of_outputs as u64).write(&mut w)?; 46 | (self.number_of_outputs as u64).write(&mut w) 47 | } 48 | } 49 | 50 | impl R1CSfIndex { 51 | // since there are two domains: interpolation and input 52 | // for discrete log comparison it's required that input <= interpolation 53 | pub fn check_domains_sizes(&self) -> bool { 54 | // we work with circuits where number_of_constraints is always power of 2 55 | let interpolation_domain = 56 | GeneralEvaluationDomain::::new(self.number_of_non_zero_entries).unwrap(); 57 | self.number_of_constraints <= interpolation_domain.size() 58 | } 59 | } 60 | 61 | fn empty_matrix(length: usize) -> Matrix { 62 | let mut matrix = vec![]; 63 | for _ in 0..length { 64 | matrix.push(vec![]); 65 | } 66 | matrix 67 | } 68 | 69 | #[macro_export] 70 | /// Print a Matrix 71 | macro_rules! printmatrix { 72 | ($matrix:expr) => { 73 | for (row_index, row) in $matrix.iter().enumerate() { 74 | for (value, col_index) in row { 75 | println!("row {}, col {}: {}", row_index, col_index, value) 76 | } 77 | } 78 | }; 79 | } 80 | 81 | #[macro_export] 82 | /// Print a Matrix 83 | macro_rules! slt_test { 84 | ($matrix:expr, $num_of_pub_inputs_plus_one:expr) => { 85 | for (row_index, row) in $matrix.iter().enumerate() { 86 | for (_, col_index) in row { 87 | // assert!(row_index > $num_of_pub_inputs_plus_one); 88 | assert!(row_index > *col_index); 89 | } 90 | } 91 | }; 92 | } 93 | 94 | #[macro_export] 95 | /// Print a Matrix 96 | macro_rules! diag_test { 97 | ($matrix:expr) => { 98 | for (row_index, row) in $matrix.iter().enumerate() { 99 | for (_, col_index) in row { 100 | assert_eq!(row_index, *col_index); 101 | } 102 | } 103 | }; 104 | } 105 | -------------------------------------------------------------------------------- /ac_compiler/src/tests.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use crate::variable::VariableType; 4 | use crate::{ 5 | circuit::Circuit, 6 | circuit_compiler::{CircuitCompiler, VanillaCompiler}, 7 | constraint_builder::ConstraintBuilder, 8 | diag_test, 9 | error::Error, 10 | gate::GateType, 11 | slt_test, 12 | }; 13 | use ark_bn254::Fr; 14 | use ark_ff::Zero; 15 | 16 | type F = Fr; 17 | 18 | fn circuit_test_template(constraints: Func) 19 | where 20 | Func: FnOnce(&mut ConstraintBuilder) -> Result<(), Error>, 21 | { 22 | let mut cb = ConstraintBuilder::::new(); 23 | 24 | let synthesized_circuit = Circuit::synthesize(constraints, &mut cb).unwrap(); 25 | let (_r1csf_index_from_synthesized, a, b, c) = 26 | VanillaCompiler::::ac2tft(&synthesized_circuit); 27 | 28 | slt_test!(a, r1csf_index_from_synthesized.number_of_input_rows); 29 | slt_test!(b, r1csf_index_from_synthesized.number_of_input_rows); 30 | diag_test!(c); 31 | 32 | // Perform matrix multiplications 33 | let inner_prod_fn = |row: &[(F, usize)]| { 34 | let mut acc = F::zero(); 35 | for &(_, i) in row { 36 | acc += cb.assignment[i]; 37 | } 38 | acc 39 | }; 40 | 41 | let z_a: Vec = a.iter().map(|row| inner_prod_fn(row)).collect(); 42 | let z_b: Vec = b.iter().map(|row| inner_prod_fn(row)).collect(); 43 | let z_c: Vec = c.iter().map(|row| inner_prod_fn(row)).collect(); 44 | 45 | assert_eq!(z_a.len(), z_b.len()); 46 | assert_eq!(z_b.len(), z_c.len()); 47 | for ((&za_i, &zb_i), &zc_i) in z_a.iter().zip(z_b.iter()).zip(z_c.iter()) { 48 | assert_eq!(za_i * zb_i, zc_i); 49 | } 50 | } 51 | 52 | #[test] 53 | fn test_simple_circuit() { 54 | let constraints = |cb: &mut ConstraintBuilder| -> Result<(), Error> { 55 | let two = cb.new_input_variable("two", F::from(2u64))?; 56 | let five = cb.new_input_variable("five", F::from(5u64))?; 57 | let x = cb.new_input_variable("x", F::from(7u64))?; 58 | 59 | let x_square = cb.enforce_constraint(&x, &x, GateType::Mul, VariableType::Witness)?; 60 | let x_cube = 61 | cb.enforce_constraint(&x_square, &x, GateType::Mul, VariableType::Witness)?; 62 | 63 | let two_x = cb.enforce_constraint(&two, &x, GateType::Mul, VariableType::Witness)?; 64 | let x_qubed_plus_2x = 65 | cb.enforce_constraint(&x_cube, &two_x, GateType::Add, VariableType::Witness)?; 66 | 67 | let _ = cb.enforce_constraint( 68 | &x_qubed_plus_2x, 69 | &five, 70 | GateType::Add, 71 | VariableType::Output, 72 | )?; 73 | 74 | Ok(()) 75 | }; 76 | 77 | circuit_test_template(constraints) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /ac_compiler/src/variable.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::Field; 2 | 3 | #[derive(Clone, Copy)] 4 | pub enum VariableType { 5 | Input, 6 | Witness, 7 | Output, 8 | } 9 | #[derive(Clone)] 10 | pub struct Variable { 11 | pub label: String, 12 | pub variable_type: VariableType, 13 | pub value: F, 14 | } 15 | -------------------------------------------------------------------------------- /fiat_shamir_rng/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fiat_shamir_rng" 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 | ark-ff = { version = "^0.3.0", default-features = false } 10 | ark-std = { version = "^0.3.0", default-features = false } 11 | 12 | digest = { version = "0.9" } -------------------------------------------------------------------------------- /fiat_shamir_rng/src/lib.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::{FromBytes, ToBytes}; 2 | use ark_std::convert::From; 3 | use ark_std::marker::PhantomData; 4 | use ark_std::rand::{RngCore, SeedableRng}; 5 | use digest::Digest; 6 | 7 | use ark_std::vec::Vec; 8 | 9 | /// An RNG suitable for Fiat-Shamir transforms 10 | pub trait FiatShamirRng: RngCore { 11 | /// Create a new `Self` with an initial input 12 | fn initialize<'a, T: 'a + ToBytes>(initial_input: &'a T) -> Self; 13 | /// Absorb new inputs into state 14 | fn absorb<'a, T: 'a + ToBytes>(&mut self, new_input: &'a T); 15 | } 16 | 17 | /// A simple `FiatShamirRng` that refreshes its seed by hashing together the previous seed 18 | /// and the new seed material. 19 | pub struct SimpleHashFiatShamirRng { 20 | r: R, 21 | seed: [u8; 32], 22 | #[doc(hidden)] 23 | digest: PhantomData, 24 | } 25 | 26 | impl RngCore for SimpleHashFiatShamirRng { 27 | #[inline] 28 | fn next_u32(&mut self) -> u32 { 29 | self.r.next_u32() 30 | } 31 | 32 | #[inline] 33 | fn next_u64(&mut self) -> u64 { 34 | self.r.next_u64() 35 | } 36 | 37 | #[inline] 38 | fn fill_bytes(&mut self, dest: &mut [u8]) { 39 | self.r.fill_bytes(dest); 40 | } 41 | 42 | #[inline] 43 | fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), ark_std::rand::Error> { 44 | Ok(self.r.fill_bytes(dest)) 45 | } 46 | } 47 | 48 | impl FiatShamirRng for SimpleHashFiatShamirRng 49 | where 50 | R::Seed: From<[u8; 32]>, 51 | { 52 | /// Create a new `Self` by initializing with a fresh seed. 53 | /// `self.seed = H(initial_input)`. 54 | #[inline] 55 | fn initialize<'a, T: 'a + ToBytes>(initial_input: &'a T) -> Self { 56 | let mut bytes = Vec::new(); 57 | initial_input 58 | .write(&mut bytes) 59 | .expect("failed to convert to bytes"); 60 | let seed = FromBytes::read(D::digest(&bytes).as_ref()).expect("failed to get [u8; 32]"); 61 | let r = R::from_seed(::from(seed)); 62 | Self { 63 | r, 64 | seed: seed, 65 | digest: PhantomData, 66 | } 67 | } 68 | 69 | /// Refresh `self.seed` with new material. Achieved by setting 70 | /// `self.seed = H(new_input || self.seed)`. 71 | #[inline] 72 | fn absorb<'a, T: 'a + ToBytes>(&mut self, new_input: &'a T) { 73 | let mut bytes = Vec::new(); 74 | new_input 75 | .write(&mut bytes) 76 | .expect("failed to convert to bytes"); 77 | bytes.extend_from_slice(&self.seed); 78 | self.seed = FromBytes::read(D::digest(&bytes).as_ref()).expect("failed to get [u8; 32]"); 79 | self.r = R::from_seed(::from(self.seed)); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /functional_commitment/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "functional_commitment" 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 | ark-ff = { version = "^0.3.0", default-features = false } 10 | ark-poly = { version = "^0.3.0", default-features = false } 11 | homomorphic_poly_commit = { path = "../homomorphic_poly_commit"} 12 | index_private_marlin = { path = "../index_private_marlin"} 13 | proof_of_function_relation = { path = "../proof_of_function_relation"} 14 | fiat_shamir_rng = { path = "../fiat_shamir_rng"} 15 | rand_chacha = { version = "0.3.0", default-features = false } 16 | blake2 = { version = "0.9", default-features = false } 17 | ark-relations = { version = "^0.3.0", default-features = false } 18 | ark-poly-commit = { version = "^0.3.0", default-features = false } 19 | ark-std = { version = "^0.3.0", default-features = false } 20 | ac_compiler = { path = "../ac_compiler" } 21 | 22 | [dev-dependencies] 23 | ark-bn254 = { version = "^0.3.0", default-features = false } 24 | ark-bls12-381 = { version = "^0.3.0", default-features = false, features = [ "curve" ] } -------------------------------------------------------------------------------- /functional_commitment/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod tests; 2 | 3 | #[macro_export] 4 | /// Print a Matrix 5 | macro_rules! slt_test { 6 | ($matrix:expr, $num_of_pub_inputs_plus_one:expr) => { 7 | for (row_index, row) in $matrix.iter().enumerate() { 8 | for (_, col_index) in row { 9 | assert!(row_index >= $num_of_pub_inputs_plus_one); 10 | assert!(row_index > *col_index); 11 | } 12 | } 13 | }; 14 | } 15 | 16 | #[macro_export] 17 | /// Print a Matrix 18 | macro_rules! diag_test { 19 | ($matrix:expr) => { 20 | for (row_index, row) in $matrix.iter().enumerate() { 21 | for (_, col_index) in row { 22 | assert_eq!(row_index, *col_index); 23 | } 24 | } 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /homomorphic_poly_commit/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "homomorphic_poly_commit" 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 | ark-poly-commit = "0.3.0" 10 | ark-ff = "0.3.0" 11 | ark-poly = "0.3.0" 12 | ark-ec = "0.3.0" 13 | ark-std = "0.3.0" 14 | ark-marlin = "0.3.0" 15 | ark-serialize = "0.3.0" 16 | rand_core = {version = "0.6", default-features=false, features = ["getrandom"] } 17 | ark-bn254 = "0.3.0" 18 | rand = "0.8.4" -------------------------------------------------------------------------------- /homomorphic_poly_commit/src/error.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, PartialEq)] 2 | pub enum Error { 3 | PCError { 4 | error: String, 5 | }, 6 | 7 | /// A commitment could not be found when evaluating a linear combination 8 | MissingCommitment(String), 9 | InputLengthError(String), 10 | MismatchedDegreeBounds(String), 11 | ConstantTermInAggregation, 12 | } 13 | 14 | /// Convert an ark_poly_commit error 15 | pub fn to_pc_error(error: PC::Error) -> Error 16 | where 17 | F: ark_ff::Field, 18 | PC: ark_poly_commit::PolynomialCommitment>, 19 | { 20 | println!("Polynomial Commitment Error: {:?}", error); 21 | Error::PCError { 22 | error: format!("Polynomial Commitment Error: {:?}", error), 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /homomorphic_poly_commit/src/lib.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::PrimeField; 2 | use ark_poly::univariate::DensePolynomial; 3 | use ark_poly_commit::{LabeledCommitment, LinearCombination, PolynomialCommitment}; 4 | 5 | use crate::error::Error; 6 | 7 | pub mod error; 8 | pub mod marlin_kzg; 9 | pub mod sonic_kzg; 10 | 11 | /// An additively homomorphic polynomial commitment scheme 12 | pub trait AdditivelyHomomorphicPCS: PolynomialCommitment> 13 | where 14 | F: PrimeField, 15 | { 16 | /// Aggregate labeled commitments according to the linear combination. If hiding bounds are enforced, the committer is expected to provide 17 | /// a vector of hiding randomness values, otherwise use `None`. A verifier can always aggregate with `None` for randomness. 18 | fn aggregate_commitments( 19 | commitments: &[LabeledCommitment], 20 | randomness: Option>, 21 | lc: &LinearCombination, 22 | ) -> Result<(LabeledCommitment, Self::Randomness), Error>; 23 | } 24 | -------------------------------------------------------------------------------- /homomorphic_poly_commit/src/marlin_kzg.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | use ark_ec::PairingEngine; 4 | use ark_poly::univariate::DensePolynomial; 5 | use ark_poly_commit::{ 6 | kzg10, marlin_pc::MarlinKZG10, LCTerm, LabeledCommitment, LinearCombination, PCCommitment, 7 | PCRandomness, PolynomialCommitment, 8 | }; 9 | 10 | use crate::{error::Error, AdditivelyHomomorphicPCS}; 11 | 12 | /// The Default KZG-style commitment scheme 13 | pub type KZG10 = MarlinKZG10::Fr>>; 14 | 15 | /// A single KZG10 commitment 16 | // pub type KZG10Commitment = as PolynomialCommitment< 17 | // ::Fr, 18 | // DensePolynomial<::Fr>, 19 | // >>::Commitment; 20 | 21 | pub type KZGRandomness = as PolynomialCommitment< 22 | ::Fr, 23 | DensePolynomial<::Fr>, 24 | >>::Randomness; 25 | 26 | impl AdditivelyHomomorphicPCS for MarlinKZG10> { 27 | fn aggregate_commitments( 28 | commitments: &[LabeledCommitment], 29 | randomness: Option>, 30 | lc: &LinearCombination, 31 | ) -> Result<(LabeledCommitment, Self::Randomness), Error> { 32 | let randomness = randomness.map_or( 33 | vec![ 34 | Self::Randomness { 35 | rand: kzg10::Randomness::empty(), 36 | shifted_rand: Some(kzg10::Randomness::empty()) 37 | }; 38 | commitments.len() 39 | ], 40 | |rands| rands, 41 | ); 42 | 43 | let degree_bound = commitments[0].degree_bound(); 44 | // create mapping of label -> commitment and fail if all degree bounds are not the same 45 | let label_comm_mapping = commitments 46 | .iter() 47 | .zip(randomness.iter()) 48 | .map(|(comm, rand)| { 49 | if comm.degree_bound() != degree_bound { 50 | // Can only accumulate commitments that have the same degree bound 51 | return Err(Error::MismatchedDegreeBounds(format!( 52 | "{} has degree bound {:?}, but {} has degree bound {:?}", 53 | commitments[0].label(), 54 | degree_bound, 55 | comm.label(), 56 | comm.degree_bound() 57 | ))); 58 | } 59 | Ok(( 60 | comm.label().clone(), 61 | (comm.commitment().clone(), rand.clone()), 62 | )) 63 | }) 64 | .collect::, Error>>()?; 65 | 66 | // initial values 67 | let mut aggregate_commitment = kzg10::Commitment::empty(); 68 | let mut aggregate_shifted_commitment = kzg10::Commitment::empty(); 69 | let mut aggregate_randomness = kzg10::Randomness::empty(); 70 | let mut aggregate_shifted_randomness = kzg10::Randomness::empty(); 71 | 72 | for (coef, term) in lc.iter() { 73 | match term { 74 | // No support for constant terms 75 | LCTerm::One => return Err(Error::ConstantTermInAggregation), 76 | 77 | // Find the corresponding commitment and randomness in our map; aggregate. 78 | LCTerm::PolyLabel(label) => match label_comm_mapping.get(label) { 79 | Some((comm, rand)) => { 80 | if degree_bound.is_some() { 81 | aggregate_shifted_commitment += ( 82 | *coef, 83 | &comm.shifted_comm.expect( 84 | "Degree bounded polynomial must have shifted commitment", 85 | ), 86 | ); 87 | aggregate_shifted_randomness += ( 88 | *coef, 89 | &rand.shifted_rand.clone().expect( 90 | "Degree bounded polynomial must have shifted commitment", 91 | ), 92 | ); 93 | } 94 | 95 | aggregate_commitment += (*coef, &comm.comm); 96 | aggregate_randomness += (*coef, &rand.rand); 97 | } 98 | None => { 99 | return Err(Error::MissingCommitment(format!( 100 | "Could not find object with label '{}' when computing '{}'", 101 | label, 102 | lc.label() 103 | ))) 104 | } 105 | }, 106 | } 107 | } 108 | 109 | let (shifted_comm, shifted_rand) = match degree_bound.is_some() { 110 | true => ( 111 | Some(aggregate_shifted_commitment), 112 | Some(aggregate_shifted_randomness), 113 | ), 114 | false => (None, None), 115 | }; 116 | 117 | let commitment = Self::Commitment { 118 | comm: aggregate_commitment, 119 | shifted_comm, 120 | }; 121 | 122 | let randomness = Self::Randomness { 123 | rand: aggregate_randomness, 124 | shifted_rand, 125 | }; 126 | 127 | Ok(( 128 | LabeledCommitment::new(lc.label().clone(), commitment, degree_bound), 129 | randomness, 130 | )) 131 | } 132 | } 133 | 134 | #[cfg(test)] 135 | mod test { 136 | use crate::{marlin_kzg::KZG10, AdditivelyHomomorphicPCS}; 137 | use ark_bn254::{Bn254, Fr}; 138 | use ark_ff::One; 139 | use ark_ff::UniformRand; 140 | use ark_poly::univariate::DensePolynomial; 141 | use ark_poly::UVPolynomial; 142 | use ark_poly_commit::LinearCombination; 143 | use ark_poly_commit::{LabeledPolynomial, PolynomialCommitment}; 144 | use ark_std::rand::thread_rng; 145 | use rand_core::OsRng; 146 | 147 | type F = Fr; 148 | type PC = KZG10; 149 | 150 | #[test] 151 | fn test_aggregate_comm_with_rand() { 152 | // Parameters 153 | let rng = &mut thread_rng(); 154 | let maximum_degree: usize = 16; 155 | let hiding_bound = 1; 156 | let enforced_degree_bounds = [10]; 157 | let degree_bound = 10; 158 | 159 | // Setup the commitment scheme 160 | let pp = PC::setup(maximum_degree, None, &mut OsRng).unwrap(); 161 | let (ck, vk) = PC::trim( 162 | &pp, 163 | maximum_degree, 164 | hiding_bound, 165 | Some(&enforced_degree_bounds), 166 | ) 167 | .unwrap(); 168 | 169 | // Define polynomials and a linear combination 170 | let a_unlabeled: DensePolynomial = DensePolynomial::rand(7, rng); 171 | let a_poly = LabeledPolynomial::new( 172 | String::from("a"), 173 | a_unlabeled, 174 | Some(degree_bound), 175 | Some(hiding_bound), 176 | ); 177 | 178 | let b_unlabeled: DensePolynomial = DensePolynomial::rand(5, rng); 179 | let b_poly = LabeledPolynomial::new( 180 | String::from("b"), 181 | b_unlabeled, 182 | Some(degree_bound), 183 | Some(hiding_bound), 184 | ); 185 | 186 | let a_plus_2b_poly = a_poly.polynomial().clone() + (b_poly.polynomial() * F::from(2u64)); 187 | let a_plus_2b_poly = LabeledPolynomial::new( 188 | String::from("a_plus_2b_poly"), 189 | a_plus_2b_poly, 190 | Some(degree_bound), 191 | Some(hiding_bound), 192 | ); 193 | 194 | let polynomials = vec![a_poly.clone(), b_poly.clone()]; 195 | let linear_combination = 196 | LinearCombination::new("a_plus_2b", vec![(F::one(), "a"), (F::from(2u64), "b")]); 197 | 198 | // Commit Phase 199 | let (commitments, rands) = PC::commit(&ck, &polynomials, Some(rng)).unwrap(); 200 | let (test_commitment, test_rand) = 201 | PC::aggregate_commitments(&commitments, Some(rands.to_vec()), &linear_combination) 202 | .unwrap(); 203 | 204 | for comm in &commitments { 205 | println!("{}: {:?}", comm.label(), comm.commitment().shifted_comm); 206 | } 207 | println!( 208 | "{}: {:?}", 209 | test_commitment.label(), 210 | test_commitment.commitment().shifted_comm 211 | ); 212 | 213 | let mut manual_commitment = commitments[0].commitment().comm; 214 | manual_commitment += (F::from(2u64), &commitments[1].commitment().comm); 215 | 216 | let mut manual_rand = rands[0].clone().rand; 217 | manual_rand += (F::from(2u64), &rands[1].clone().rand); 218 | 219 | assert_eq!(test_commitment.commitment().comm, manual_commitment); 220 | assert_eq!(test_rand.rand, manual_rand); 221 | 222 | println!("PASSED SANITY"); 223 | 224 | // Derive evaluation point and generate a query set 225 | let evaluation_point = Fr::rand(rng); 226 | 227 | // Evaluation Phase, here we only output the evaluation of the linear combination 228 | let manual_eval = a_plus_2b_poly.evaluate(&evaluation_point); 229 | 230 | let opening_challenge = F::rand(rng); 231 | 232 | // Opening phase 233 | let lc_opening_proof = PC::open( 234 | &ck, 235 | &[a_plus_2b_poly], 236 | &[test_commitment.clone()], 237 | &evaluation_point, 238 | opening_challenge, 239 | &[test_rand], 240 | Some(rng), 241 | ) 242 | .unwrap(); 243 | 244 | // Verify 245 | let res = PC::check( 246 | &vk, 247 | &[test_commitment], 248 | &evaluation_point, 249 | vec![manual_eval], 250 | &lc_opening_proof, 251 | opening_challenge, 252 | Some(rng), 253 | ) 254 | .unwrap(); 255 | 256 | assert_eq!(true, res) 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /homomorphic_poly_commit/src/sonic_kzg.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | use ark_ec::PairingEngine; 4 | use ark_poly::univariate::DensePolynomial; 5 | use ark_poly_commit::{ 6 | sonic_pc::SonicKZG10, LCTerm, LabeledCommitment, LinearCombination, PCCommitment, PCRandomness, 7 | }; 8 | 9 | use crate::{error::Error, AdditivelyHomomorphicPCS}; 10 | 11 | /// The Default KZG-style commitment scheme 12 | pub type KZG10 = SonicKZG10::Fr>>; 13 | 14 | /// A single KZG10 commitment 15 | // pub type KZG10Commitment = as PolynomialCommitment< 16 | // ::Fr, 17 | // DensePolynomial<::Fr>, 18 | // >>::Commitment; 19 | 20 | // pub type KZGRandomness = as PolynomialCommitment< 21 | // ::Fr, 22 | // DensePolynomial<::Fr>, 23 | // >>::Randomness; 24 | 25 | impl AdditivelyHomomorphicPCS for SonicKZG10> { 26 | fn aggregate_commitments( 27 | commitments: &[LabeledCommitment], 28 | randomness: Option>, 29 | lc: &LinearCombination, 30 | ) -> Result<(LabeledCommitment, Self::Randomness), Error> { 31 | let degree_bound = commitments[0].degree_bound(); 32 | 33 | let randomness = randomness.map_or( 34 | vec![Self::Randomness::empty(); commitments.len()], 35 | |rands| rands, 36 | ); 37 | 38 | // create mapping of label -> commitment and fail if all degree bounds are not the same 39 | let label_comm_mapping = commitments 40 | .iter() 41 | .zip(randomness.iter()) 42 | .map(|(comm, rand)| { 43 | if comm.degree_bound() != degree_bound { 44 | // Can only accumulate commitments that have the same degree bound 45 | return Err(Error::MismatchedDegreeBounds(format!( 46 | "{} has degree bound {:?}, but {} has degree bound {:?}", 47 | commitments[0].label(), 48 | degree_bound, 49 | comm.label(), 50 | comm.degree_bound() 51 | ))); 52 | } 53 | Ok(( 54 | comm.label().clone(), 55 | (comm.commitment().clone(), rand.clone()), 56 | )) 57 | }) 58 | .collect::, Error>>()?; 59 | 60 | // initial values 61 | let mut aggregate_commitment = Self::Commitment::empty(); 62 | let mut aggregate_randomness = Self::Randomness::empty(); 63 | 64 | for (coef, term) in lc.iter() { 65 | match term { 66 | // No support for constant terms 67 | LCTerm::One => return Err(Error::ConstantTermInAggregation), 68 | 69 | // Find the corresponding commitment and randomness in our map; aggregate. 70 | LCTerm::PolyLabel(label) => match label_comm_mapping.get(label) { 71 | Some((comm, rand)) => { 72 | aggregate_commitment += (*coef, &comm); 73 | aggregate_randomness += (*coef, rand); 74 | } 75 | None => { 76 | return Err(Error::MissingCommitment(format!( 77 | "Could not find object with label '{}' when computing '{}'", 78 | label, 79 | lc.label() 80 | ))) 81 | } 82 | }, 83 | } 84 | } 85 | 86 | Ok(( 87 | LabeledCommitment::new(lc.label().clone(), aggregate_commitment, degree_bound), 88 | aggregate_randomness, 89 | )) 90 | } 91 | } 92 | 93 | #[cfg(test)] 94 | mod test { 95 | use crate::{sonic_kzg::KZG10, AdditivelyHomomorphicPCS}; 96 | use ark_bn254::{Bn254, Fr}; 97 | use ark_ff::One; 98 | use ark_ff::UniformRand; 99 | use ark_poly::univariate::DensePolynomial; 100 | use ark_poly::UVPolynomial; 101 | use ark_poly_commit::LinearCombination; 102 | use ark_poly_commit::{LabeledPolynomial, PolynomialCommitment}; 103 | use ark_std::rand::thread_rng; 104 | use rand_core::OsRng; 105 | 106 | type F = Fr; 107 | type PC = KZG10; 108 | 109 | #[test] 110 | fn test_aggregate_comm_with_rand() { 111 | // Parameters 112 | let rng = &mut thread_rng(); 113 | let maximum_degree: usize = 16; 114 | let hiding_bound = 1; 115 | let enforced_degree_bounds = [10]; 116 | 117 | // Setup the commitment scheme 118 | let pp = PC::setup(maximum_degree, None, &mut OsRng).unwrap(); 119 | let (ck, vk) = PC::trim( 120 | &pp, 121 | maximum_degree, 122 | hiding_bound, 123 | Some(&enforced_degree_bounds), 124 | ) 125 | .unwrap(); 126 | 127 | // Define polynomials and a linear combination 128 | let a_unlabeled: DensePolynomial = DensePolynomial::rand(7, rng); 129 | let a_poly = LabeledPolynomial::new(String::from("a"), a_unlabeled, Some(10), Some(1)); 130 | 131 | let b_unlabeled: DensePolynomial = DensePolynomial::rand(5, rng); 132 | let b_poly = LabeledPolynomial::new(String::from("b"), b_unlabeled, Some(10), Some(1)); 133 | 134 | let a_plus_2b_poly = a_poly.polynomial().clone() + (b_poly.polynomial() * F::from(2u64)); 135 | let a_plus_2b_poly = 136 | LabeledPolynomial::new(String::from("a_plus_2b"), a_plus_2b_poly, Some(10), Some(1)); 137 | let polynomials = vec![a_poly.clone(), b_poly.clone()]; 138 | let linear_combination = 139 | LinearCombination::new("a_plus_2b", vec![(F::one(), "a"), (F::from(2u64), "b")]); 140 | 141 | // Commit Phase 142 | let (commitments, rands) = PC::commit(&ck, &polynomials, Some(rng)).unwrap(); 143 | let (test_commitment, test_rand) = 144 | PC::aggregate_commitments(&commitments, Some(rands.to_vec()), &linear_combination) 145 | .unwrap(); 146 | 147 | // Derive evaluation point and generate a query set 148 | let evaluation_point = Fr::rand(rng); 149 | 150 | // Evaluation Phase, here we only output the evaluation of the linear combination 151 | let manual_eval = a_plus_2b_poly.evaluate(&evaluation_point); 152 | 153 | // Opening phase 154 | let opening_challenge = F::rand(rng); 155 | let lc_opening_proof = PC::open( 156 | &ck, 157 | &[a_plus_2b_poly], 158 | &[test_commitment.clone()], 159 | &evaluation_point, 160 | opening_challenge, 161 | &[test_rand], 162 | Some(rng), 163 | ) 164 | .unwrap(); 165 | 166 | // Verify 167 | let res = PC::check( 168 | &vk, 169 | &[test_commitment], 170 | &evaluation_point, 171 | vec![manual_eval], 172 | &lc_opening_proof, 173 | opening_challenge, 174 | Some(rng), 175 | ) 176 | .unwrap(); 177 | 178 | assert_eq!(true, res) 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /index_private_marlin/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "index_private_marlin" 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 | ark-serialize = { version = "^0.3.0", default-features = false, features = [ "derive" ] } 10 | ark-ff = { version = "^0.3.0", default-features = false } 11 | ark-std = { version = "^0.3.0", default-features = false } 12 | ark-poly = { version = "^0.3.0", default-features = false } 13 | ark-relations = { version = "^0.3.0", default-features = false } 14 | ark-poly-commit = { version = "^0.3.0", default-features = false } 15 | ark-ec = { version = "^0.3.0", default-features = false } 16 | zero_over_k = { path = "../zero_over_k" } 17 | homomorphic_poly_commit = { path = "../homomorphic_poly_commit"} 18 | fiat_shamir_rng = { path = "../fiat_shamir_rng"} 19 | ac_compiler = { path = "../ac_compiler" } 20 | 21 | rayon = { version = "1", optional = true } 22 | digest = { version = "0.9" } 23 | derivative = { version = "2", features = ["use_core"] } 24 | 25 | [dev-dependencies] 26 | rand_chacha = { version = "0.3.0", default-features = false } 27 | blake2 = { version = "0.9", default-features = false } 28 | ark-bls12-381 = { version = "^0.3.0", default-features = false, features = [ "curve" ] } 29 | ark-mnt4-298 = { version = "^0.3.0", default-features = false, features = ["r1cs", "curve"] } 30 | ark-mnt6-298 = { version = "^0.3.0", default-features = false, features = ["r1cs"] } 31 | ark-mnt4-753 = { version = "^0.3.0", default-features = false, features = ["r1cs", "curve"] } 32 | ark-mnt6-753 = { version = "^0.3.0", default-features = false, features = ["r1cs"] } -------------------------------------------------------------------------------- /index_private_marlin/src/ahp/constraint_systems.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | use ark_ff::PrimeField; 4 | use ark_poly::{ 5 | univariate::DensePolynomial, EvaluationDomain, Evaluations as EvaluationsOnDomain, 6 | GeneralEvaluationDomain, 7 | }; 8 | use ark_relations::r1cs::Matrix; 9 | use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError}; 10 | use ark_std::{ 11 | cfg_iter_mut, 12 | io::{Read, Write}, 13 | }; 14 | use derivative::Derivative; 15 | 16 | use super::UnnormalizedBivariateLagrangePoly; 17 | 18 | pub type LabeledPolynomial = ark_poly_commit::LabeledPolynomial>; 19 | 20 | /// Evaluations of various polynomials related to the constraint matrices, 21 | /// over the same domain. 22 | #[derive(Derivative, CanonicalSerialize, CanonicalDeserialize)] 23 | #[derivative(Debug(bound = "F: PrimeField"), Clone(bound = "F: PrimeField"))] 24 | pub struct MatrixEvals { 25 | /// Evaluations of the `row` polynomial. 26 | pub row: EvaluationsOnDomain, 27 | /// Evaluations of the `col` polynomial. 28 | pub col: EvaluationsOnDomain, 29 | /// Evaluations of the `val_a` polynomial. 30 | pub val: EvaluationsOnDomain, 31 | } 32 | 33 | #[derive(Derivative, CanonicalSerialize, CanonicalDeserialize)] 34 | #[derivative(Debug(bound = "F: PrimeField"), Clone(bound = "F: PrimeField"))] 35 | pub struct MatrixArithmetization { 36 | /// LDE of the row indices of M^*. 37 | pub row: LabeledPolynomial, 38 | /// LDE of the column indices of M^*. 39 | pub col: LabeledPolynomial, 40 | /// LDE of the non-zero entries of A^*. 41 | pub val: LabeledPolynomial, 42 | 43 | /// Evaluation of `self.row`, `self.col`, and `self.val` on the domain `K`. 44 | pub evals_on_k: MatrixEvals, 45 | } 46 | 47 | pub fn arithmetize_matrix( 48 | m: &Matrix, 49 | interpolation_domain: GeneralEvaluationDomain, 50 | output_domain: GeneralEvaluationDomain, 51 | // input_domain: GeneralEvaluationDomain, 52 | m_prefix: &str, 53 | is_diag_matrix: bool, 54 | degree_bound: Option, 55 | hiding_bound: Option, 56 | ) -> MatrixArithmetization { 57 | let elems: Vec<_> = output_domain.elements().collect(); 58 | 59 | let eq_poly_vals: BTreeMap = output_domain 60 | .elements() 61 | .zip(output_domain.batch_eval_unnormalized_bivariate_lagrange_poly_with_same_inputs()) 62 | .collect(); 63 | 64 | let mut row_vec = Vec::with_capacity(interpolation_domain.size()); 65 | let mut col_vec = Vec::with_capacity(interpolation_domain.size()); 66 | let mut val_vec = Vec::with_capacity(interpolation_domain.size()); 67 | let mut inverses = Vec::with_capacity(interpolation_domain.size()); 68 | let mut count = 0; 69 | 70 | for (row_index, row) in m.into_iter().enumerate() { 71 | for (val, col_index) in row { 72 | let row_val = elems[row_index]; 73 | let col_val = elems[*col_index]; 74 | 75 | // We are dealing with the transpose of M 76 | row_vec.push(col_val); 77 | col_vec.push(row_val); 78 | 79 | val_vec.push(*val); 80 | inverses.push(eq_poly_vals[&col_val]); 81 | 82 | count += 1; 83 | } 84 | } 85 | ark_ff::batch_inversion::(&mut inverses); 86 | drop(eq_poly_vals); 87 | 88 | cfg_iter_mut!(val_vec).zip(inverses).for_each(|(v, inv)| { 89 | *v *= &inv; 90 | }); 91 | 92 | let last_column = *col_vec.last().unwrap(); 93 | let last_row = *row_vec.last().unwrap(); 94 | 95 | let val_to_append_col = if is_diag_matrix { 96 | F::one() 97 | } else { 98 | last_column 99 | }; 100 | 101 | let val_to_append_row = if is_diag_matrix { F::one() } else { last_row }; 102 | 103 | for _ in count..interpolation_domain.size() { 104 | col_vec.push(val_to_append_col); 105 | row_vec.push(val_to_append_row); 106 | val_vec.push(F::zero()); 107 | } 108 | 109 | let row_evals_on_k = EvaluationsOnDomain::from_vec_and_domain(row_vec, interpolation_domain); 110 | let col_evals_on_k = EvaluationsOnDomain::from_vec_and_domain(col_vec, interpolation_domain); 111 | let val_evals_on_k = EvaluationsOnDomain::from_vec_and_domain(val_vec, interpolation_domain); 112 | 113 | let row = row_evals_on_k.clone().interpolate(); 114 | let col = col_evals_on_k.clone().interpolate(); 115 | let val = val_evals_on_k.clone().interpolate(); 116 | 117 | let evals_on_k = MatrixEvals { 118 | row: row_evals_on_k, 119 | col: col_evals_on_k, 120 | val: val_evals_on_k, 121 | }; 122 | 123 | MatrixArithmetization { 124 | row: LabeledPolynomial::new( 125 | format!("{}_row", m_prefix).into(), 126 | row, 127 | degree_bound, 128 | hiding_bound, 129 | ), 130 | col: LabeledPolynomial::new( 131 | format!("{}_col", m_prefix).into(), 132 | col, 133 | degree_bound, 134 | hiding_bound, 135 | ), 136 | val: LabeledPolynomial::new( 137 | format!("{}_val", m_prefix).into(), 138 | val, 139 | degree_bound, 140 | hiding_bound, 141 | ), 142 | evals_on_k, 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /index_private_marlin/src/ahp/indexer.rs: -------------------------------------------------------------------------------- 1 | use ac_compiler::R1CSfIndex; 2 | use ark_ff::PrimeField; 3 | use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError}; 4 | use derivative::Derivative; 5 | 6 | use ark_std::io::{Read, Write}; 7 | 8 | use super::constraint_systems::MatrixArithmetization; 9 | 10 | /// Represents a matrix. 11 | pub type Matrix = Vec>; 12 | 13 | #[derive(Derivative)] 14 | #[derivative(Clone(bound = "F: PrimeField"))] 15 | /// The indexed version of the constraint system. 16 | #[derive(CanonicalSerialize, CanonicalDeserialize)] 17 | pub struct Index { 18 | /// Information about the index. 19 | pub index_info: R1CSfIndex, 20 | 21 | /// The A matrix arithmetization 22 | pub a_arith: MatrixArithmetization, 23 | 24 | /// The B matrix arithmetization 25 | pub b_arith: MatrixArithmetization, 26 | 27 | /// The C matrix arithmetization 28 | pub c_arith: MatrixArithmetization, 29 | 30 | /// tmp store matrices 31 | pub a: Matrix, 32 | pub b: Matrix, 33 | pub c: Matrix, 34 | } 35 | -------------------------------------------------------------------------------- /index_private_marlin/src/ahp/verifier.rs: -------------------------------------------------------------------------------- 1 | use super::constraint_systems::LabeledPolynomial; 2 | use super::Error; 3 | use crate::well_formation::{construct_lagrange_basis, construct_vanishing}; 4 | use ac_compiler::R1CSfIndex; 5 | use ark_ff::PrimeField; 6 | use ark_ff::Zero; 7 | use ark_poly::univariate::DensePolynomial; 8 | use ark_poly::EvaluationDomain; 9 | use ark_poly::GeneralEvaluationDomain; 10 | use ark_poly_commit::QuerySet; 11 | use ark_std::rand::RngCore; 12 | 13 | use super::AHPForR1CS; 14 | 15 | /// State of the AHP verifier 16 | pub struct VerifierState { 17 | pub(crate) domain_h: GeneralEvaluationDomain, 18 | pub(crate) domain_k: GeneralEvaluationDomain, 19 | 20 | pub(crate) first_round_msg: Option>, 21 | pub(crate) second_round_msg: Option>, 22 | 23 | pub(crate) gamma: Option, 24 | } 25 | 26 | /// First message of the verifier. 27 | #[derive(Copy, Clone)] 28 | pub struct VerifierFirstMsg { 29 | /// Query for the random polynomial. 30 | pub alpha: F, 31 | /// Randomizer for the lincheck for `A`. 32 | pub eta_a: F, 33 | /// Randomizer for the lincheck for `B`. 34 | pub eta_b: F, 35 | /// Randomizer for the lincheck for `C`. 36 | pub eta_c: F, 37 | } 38 | 39 | /// Second verifier message. 40 | #[derive(Copy, Clone)] 41 | pub struct VerifierSecondMsg { 42 | /// Query for the second round of polynomials. 43 | pub beta: F, 44 | } 45 | 46 | /// The first set of prover oracles. 47 | pub struct VerifierWellFormationOracles { 48 | /// The LDE of `pi`. 49 | pub x: LabeledPolynomial, 50 | /// The LDE of `output`. 51 | pub y: LabeledPolynomial, 52 | 53 | pub vh_gt_x: LabeledPolynomial, 54 | pub vh_lt_y: LabeledPolynomial, 55 | } 56 | 57 | impl VerifierWellFormationOracles { 58 | /// Iterate over the polynomials output by the prover in the index private first round. 59 | pub fn iter(&self) -> impl Iterator> { 60 | vec![&self.x, &self.vh_gt_x, &self.y, &self.vh_lt_y].into_iter() 61 | } 62 | } 63 | 64 | impl AHPForR1CS { 65 | /// Output the first message and next round state. 66 | pub fn verifier_first_round( 67 | index_info: &R1CSfIndex, 68 | rng: &mut R, 69 | ) -> Result<(VerifierFirstMsg, VerifierState), Error> { 70 | if !index_info.check_domains_sizes::() { 71 | return Err(Error::DomainHLargerThanDomainK); 72 | } 73 | let domain_k = GeneralEvaluationDomain::::new(index_info.number_of_non_zero_entries) 74 | .ok_or(Error::DomainTooLarge)?; 75 | let domain_h = GeneralEvaluationDomain::::new(index_info.number_of_constraints) 76 | .ok_or(Error::DomainTooLarge)?; 77 | 78 | let alpha = domain_h.sample_element_outside_domain(rng); 79 | let eta_a = F::rand(rng); 80 | let eta_b = F::rand(rng); 81 | let eta_c = F::rand(rng); 82 | 83 | let msg = VerifierFirstMsg { 84 | alpha, 85 | eta_a, 86 | eta_b, 87 | eta_c, 88 | }; 89 | 90 | let new_state = VerifierState { 91 | domain_h, 92 | domain_k, 93 | first_round_msg: Some(msg), 94 | second_round_msg: None, 95 | gamma: None, 96 | }; 97 | 98 | Ok((msg, new_state)) 99 | } 100 | 101 | /// Output the second message and next round state. 102 | pub fn verifier_second_round( 103 | mut state: VerifierState, 104 | rng: &mut R, 105 | ) -> (VerifierSecondMsg, VerifierState) { 106 | let beta = state.domain_h.sample_element_outside_domain(rng); 107 | let msg = VerifierSecondMsg { beta }; 108 | state.second_round_msg = Some(msg); 109 | 110 | (msg, state) 111 | } 112 | 113 | /// Output the third message and next round state. 114 | pub fn verifier_third_round( 115 | mut state: VerifierState, 116 | rng: &mut R, 117 | ) -> VerifierState { 118 | state.gamma = Some(F::rand(rng)); 119 | state 120 | } 121 | 122 | /// Output the query state and next round state. 123 | pub fn verifier_query_set<'a, R: RngCore>( 124 | state: VerifierState, 125 | _: &'a mut R, 126 | ) -> (QuerySet, VerifierState) { 127 | let beta = state.second_round_msg.unwrap().beta; 128 | 129 | let gamma = state.gamma.unwrap(); 130 | 131 | let mut query_set = QuerySet::new(); 132 | 133 | query_set.insert(("g_1".into(), ("beta".into(), beta))); 134 | query_set.insert(("z_b".into(), ("beta".into(), beta))); 135 | query_set.insert(("t".into(), ("beta".into(), beta))); 136 | query_set.insert(("outer_sumcheck".into(), ("beta".into(), beta))); 137 | 138 | query_set.insert(("g_2".into(), ("gamma".into(), gamma))); 139 | query_set.insert(("f_sumcheck".into(), ("gamma".into(), gamma))); 140 | 141 | (query_set, state) 142 | } 143 | 144 | pub fn verifier_well_formation_oracles<'a>( 145 | info: &R1CSfIndex, 146 | public_input: &Vec, 147 | output: &Vec, 148 | state: &VerifierState, 149 | ) -> VerifierWellFormationOracles { 150 | let elems: Vec = state.domain_h.elements().collect(); 151 | 152 | let pi_roots_of_unity = &elems[..info.number_of_input_rows]; 153 | let output_roots_of_unity = &elems[info.number_of_constraints - info.number_of_outputs..]; 154 | 155 | let pi_bases = construct_lagrange_basis(pi_roots_of_unity); 156 | // let pi_evals = state.public_input; 157 | 158 | let output_bases = construct_lagrange_basis(output_roots_of_unity); 159 | // let output_evals = state.output(); 160 | 161 | let vh_gt_x = construct_vanishing(&elems[info.number_of_input_rows..]); 162 | let vh_lt_y = construct_vanishing(&elems[..elems.len() - info.number_of_outputs]); 163 | 164 | let mut x_poly = DensePolynomial::::zero(); 165 | for (l_i, x_i) in pi_bases.iter().zip(public_input.iter()) { 166 | x_poly += &(l_i * *x_i); 167 | } 168 | 169 | let mut y_poly = DensePolynomial::::zero(); 170 | for (l_i, y_i) in output_bases.iter().zip(output.iter()) { 171 | y_poly += &(l_i * *y_i); 172 | } 173 | 174 | // let z_poly = prover_state.z_poly.expect("Z must be calculated"); 175 | 176 | // Sanity checks 177 | // for elem in &elems { 178 | // let z_i = z_poly.evaluate(elem); 179 | // let x_i = x_poly.evaluate(elem); 180 | // let vhx_i = vh_gt_x.evaluate(elem); 181 | // assert_eq!((z_i - x_i) * vhx_i, F::zero()); 182 | 183 | // let y_i = y_poly.evaluate(elem); 184 | // let vhy_i = vh_lt_y.evaluate(elem); 185 | // assert_eq!((z_i - y_i) * vhy_i, F::zero()); 186 | // } 187 | 188 | let x = LabeledPolynomial::new("pi_lde".to_string(), x_poly, None, None); 189 | let y = LabeledPolynomial::new("output_lde".to_string(), y_poly, None, None); 190 | let vh_gt_x = LabeledPolynomial::new("vh_gt_x".to_string(), vh_gt_x, None, None); 191 | let vh_lt_y = LabeledPolynomial::new("vh_lt_y".to_string(), vh_lt_y, None, None); 192 | 193 | VerifierWellFormationOracles { 194 | x, 195 | y, 196 | vh_gt_x, 197 | vh_lt_y, 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /index_private_marlin/src/data_structures.rs: -------------------------------------------------------------------------------- 1 | use crate::ahp::indexer::*; 2 | use crate::ahp::prover::ProverMsg; 3 | use crate::Vec; 4 | use ark_ff::PrimeField; 5 | use ark_poly::univariate::DensePolynomial; 6 | use ark_poly_commit::{BatchLCProof, PolynomialCommitment}; 7 | use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError}; 8 | use ark_std::io::{Read, Write}; 9 | 10 | use ::zero_over_k::zero_over_k::proof::Proof as ZeroOverKProof; 11 | use ac_compiler::R1CSfIndex; 12 | use homomorphic_poly_commit::AdditivelyHomomorphicPCS; 13 | 14 | /* ************************************************************************* */ 15 | /* ************************************************************************* */ 16 | /* ************************************************************************* */ 17 | 18 | /// The universal public parameters for the argument system. 19 | pub type UniversalSRS = >>::UniversalParams; 20 | 21 | /* ************************************************************************* */ 22 | /* ************************************************************************* */ 23 | /* ************************************************************************* */ 24 | 25 | /// Proving key for a specific index (i.e., R1CS matrices). 26 | #[derive(CanonicalSerialize, CanonicalDeserialize)] 27 | pub struct ProverKey> { 28 | /// The index verifier key. 29 | pub vk: VerifierKey, 30 | /// The randomness used for hiding matrix ldes 31 | pub rands: Vec, 32 | /// The index itself. 33 | pub index: Index, 34 | /// The committer key for this index, trimmed from the universal SRS. 35 | pub committer_key: PC::CommitterKey, 36 | } 37 | 38 | impl> Clone for ProverKey 39 | where 40 | PC::Commitment: Clone, 41 | { 42 | fn clone(&self) -> Self { 43 | Self { 44 | vk: self.vk.clone(), 45 | rands: self.rands.clone(), 46 | index: self.index.clone(), 47 | committer_key: self.committer_key.clone(), 48 | } 49 | } 50 | } 51 | 52 | impl> ProverKey { 53 | pub fn get_rands(&self) -> Vec { 54 | self.rands.clone() 55 | } 56 | } 57 | 58 | /// Verifier key that will be used in index private version 59 | /// Prover will commit to matrix arithmetizations and this data will be used for 60 | /// slt and diag testing 61 | #[derive(CanonicalSerialize, CanonicalDeserialize)] 62 | pub struct VerifierKey> { 63 | // /// matrix a row commitment 64 | // pub a_row_commit: PC::Commitment, 65 | 66 | // /// matrix a col commitment 67 | // pub a_col_commit: PC::Commitment, 68 | 69 | // /// matrix a val commitment 70 | // pub a_val_commit: PC::Commitment, 71 | 72 | // /// matrix b row commitment 73 | // pub b_row_commit: PC::Commitment, 74 | 75 | // /// matrix b col commitment 76 | // pub b_col_commit: PC::Commitment, 77 | 78 | // /// matrix b val commitment 79 | // pub b_val_commit: PC::Commitment, 80 | 81 | // /// matrix c row commitment 82 | // pub c_row_commit: PC::Commitment, 83 | 84 | // /// matrix c col commitment 85 | // pub c_col_commit: PC::Commitment, 86 | 87 | // /// matrix c val commitment 88 | // pub c_val_commit: PC::Commitment, 89 | /// a(row, col, val), b(row, col, val), c(row, col, val), 90 | 91 | /// commitments of (row, col, val) for each matrix 92 | pub commits: Vec, 93 | 94 | /// verifier key 95 | pub verifier_key: PC::VerifierKey, 96 | 97 | /// Stores information about the size of the index, as well as its field of 98 | /// definition. 99 | pub index_info: R1CSfIndex, 100 | } 101 | 102 | impl> Clone for VerifierKey { 103 | fn clone(&self) -> Self { 104 | Self { 105 | commits: self.commits.clone(), 106 | index_info: self.index_info.clone(), 107 | verifier_key: self.verifier_key.clone(), 108 | } 109 | } 110 | } 111 | 112 | impl> ark_ff::ToBytes for VerifierKey { 113 | fn write(&self, mut w: W) -> ark_std::io::Result<()> { 114 | self.index_info.write(&mut w)?; 115 | self.commits.write(&mut w) 116 | } 117 | } 118 | 119 | impl> VerifierKey { 120 | /// Iterate over the commitments to indexed polynomials in `self`. 121 | pub fn iter(&self) -> impl Iterator { 122 | self.commits.iter() 123 | } 124 | } 125 | 126 | /* ************************************************************************* */ 127 | /* ************************************************************************* */ 128 | /* ************************************************************************* */ 129 | 130 | /// A zkSNARK index private proof. 131 | #[derive(Clone, CanonicalSerialize, CanonicalDeserialize)] 132 | pub struct Proof> { 133 | /// Commitments to the polynomials produced by the AHP prover. 134 | pub commitments: Vec>, 135 | /// Evaluations of these polynomials. 136 | pub evaluations: Vec, 137 | /// The field elements sent by the prover. 138 | pub prover_messages: Vec>, 139 | /// An evaluation proof from the polynomial commitment. 140 | pub pc_proof: BatchLCProof, PC>, 141 | 142 | pub rational_sumcheck_zero_over_k_proof: ZeroOverKProof, 143 | pub well_formation_proof: ZeroOverKProof, 144 | } 145 | 146 | impl> Proof { 147 | /// Construct a new proof. 148 | pub fn new( 149 | commitments: Vec>, 150 | evaluations: Vec, 151 | prover_messages: Vec>, 152 | pc_proof: BatchLCProof, PC>, 153 | rational_sumcheck_zero_over_k_proof: ZeroOverKProof, 154 | well_formation_proof: ZeroOverKProof, 155 | ) -> Self { 156 | Self { 157 | commitments, 158 | evaluations, 159 | prover_messages, 160 | pc_proof, 161 | rational_sumcheck_zero_over_k_proof, 162 | well_formation_proof, 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /index_private_marlin/src/error.rs: -------------------------------------------------------------------------------- 1 | use crate::ahp::Error as AHPError; 2 | use ::zero_over_k::error::Error as ZeroOverKError; 3 | 4 | /// A `enum` specifying the possible failure modes of the `SNARK`. 5 | #[derive(Debug)] 6 | pub enum Error { 7 | /// The index is too large for the universal public parameters. 8 | IndexTooLarge, 9 | /// There was an error in the underlying holographic IOP. 10 | AHPError(AHPError), 11 | /// There was an error in the underlying polynomial commitment. 12 | PolynomialCommitmentError(E), 13 | 14 | /// ZeroOverKError 15 | ZeroOverKError(ZeroOverKError), 16 | 17 | /// Number of constraints is larger than number of non zero elements (we don't allow this because Discrete-log Comparison in proof of function fails) 18 | DomainHLargerThanDomainK, 19 | DomainTooLarge, 20 | } 21 | 22 | impl From for Error { 23 | fn from(err: AHPError) -> Self { 24 | Error::AHPError(err) 25 | } 26 | } 27 | 28 | impl From for Error { 29 | fn from(err: ZeroOverKError) -> Self { 30 | Error::ZeroOverKError(err) 31 | } 32 | } 33 | 34 | impl Error { 35 | /// Convert an error in the underlying polynomial commitment scheme 36 | /// to a `Error`. 37 | pub fn from_pc_err(err: E) -> Self { 38 | Error::PolynomialCommitmentError(err) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /index_private_marlin/src/well_formation.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::FftField; 2 | use ark_poly::{univariate::DensePolynomial, UVPolynomial}; 3 | 4 | // given the x coords construct Li polynomials 5 | pub fn construct_lagrange_basis(evaulation_domain: &[F]) -> Vec> { 6 | let mut bases = Vec::with_capacity(evaulation_domain.len()); 7 | for i in 0..evaulation_domain.len() { 8 | let mut l_i = DensePolynomial::from_coefficients_slice(&[F::one()]); 9 | let x_i = evaulation_domain[i]; 10 | for j in 0..evaulation_domain.len() { 11 | if j != i { 12 | let nom = 13 | DensePolynomial::from_coefficients_slice(&[-evaulation_domain[j], F::one()]); 14 | let denom = x_i - evaulation_domain[j]; 15 | 16 | l_i = &l_i * &(&nom * denom.inverse().unwrap()); 17 | } 18 | } 19 | 20 | bases.push(l_i); 21 | } 22 | 23 | bases 24 | } 25 | 26 | pub fn construct_vanishing(evaulation_domain: &[F]) -> DensePolynomial { 27 | let mut v_h = DensePolynomial::from_coefficients_slice(&[F::one()]); 28 | for point in evaulation_domain { 29 | v_h = &v_h * &DensePolynomial::from_coefficients_slice(&[-*point, F::one()]); 30 | } 31 | 32 | v_h 33 | } 34 | -------------------------------------------------------------------------------- /proof_of_function_relation/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "proof_of_function_relation" 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 | ark-bn254 = "0.3.0" 10 | ark-ec = "0.3.0" 11 | ark-ff = "0.3.0" 12 | ark-poly = "0.3.0" 13 | ark-poly-commit = "0.3.0" 14 | ark-std = "0.3.0" 15 | ark-marlin = "0.3.0" 16 | ark-serialize = "0.3.0" 17 | rand_core = {version = "0.6", default-features=false, features = ["getrandom"] } 18 | rand = "0.8.4" 19 | blake2 = { version = "0.9", default-features = false } 20 | digest = { version = "0.9" } 21 | zero_over_k = { path = "../zero_over_k" } 22 | homomorphic_poly_commit = { path = "../homomorphic_poly_commit"} 23 | fiat_shamir_rng = { path = "../fiat_shamir_rng"} 24 | rand_chacha = { version = "0.3.0", default-features = false } 25 | -------------------------------------------------------------------------------- /proof_of_function_relation/src/discrete_log_comparison/piop/mod.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::{PrimeField, SquareRootField}; 2 | use ark_poly_commit::LinearCombination; 3 | use ark_std::marker::PhantomData; 4 | 5 | pub mod prover; 6 | 7 | pub struct PIOPforDLComparison { 8 | _field: PhantomData, 9 | } 10 | 11 | impl PIOPforDLComparison { 12 | pub fn s_minus_one_linear_combination() -> LinearCombination { 13 | LinearCombination::new("s_minus_one", vec![(F::one(), "s"), (-F::one(), "one")]) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /proof_of_function_relation/src/discrete_log_comparison/piop/prover.rs: -------------------------------------------------------------------------------- 1 | use crate::discrete_log_comparison::piop::PIOPforDLComparison; 2 | use crate::error::Error; 3 | use crate::util::*; 4 | use ark_ff::{PrimeField, SquareRootField}; 5 | use ark_marlin::ahp::prover::ProverMsg; 6 | use ark_poly::{ 7 | univariate::DensePolynomial, EvaluationDomain, GeneralEvaluationDomain, UVPolynomial, 8 | }; 9 | use ark_poly_commit::LabeledPolynomial; 10 | use ark_std::rand::Rng; 11 | 12 | pub struct ProverState<'a, F: PrimeField + SquareRootField> { 13 | domain_k: &'a GeneralEvaluationDomain, 14 | 15 | domain_h: &'a GeneralEvaluationDomain, 16 | 17 | f: &'a LabeledPolynomial>, 18 | 19 | g: &'a LabeledPolynomial>, 20 | 21 | enforced_degree_bound: Option, 22 | 23 | first_oracles: Option>, 24 | 25 | pub a_s: Option>, 26 | 27 | pub c_s: Option>, 28 | 29 | pub delta: Option, 30 | } 31 | 32 | /// The first set of prover oracles 33 | #[derive(Clone)] 34 | pub struct ProverFirstOracles { 35 | pub s: LabeledPolynomial>, 36 | 37 | pub f_prime: LabeledPolynomial>, 38 | 39 | pub g_prime: LabeledPolynomial>, 40 | 41 | pub s_prime: LabeledPolynomial>, 42 | 43 | pub h: LabeledPolynomial>, 44 | } 45 | 46 | impl ProverFirstOracles { 47 | pub fn iter(&self) -> impl Iterator>> { 48 | vec![ 49 | &self.s, 50 | &self.f_prime, 51 | &self.g_prime, 52 | &self.s_prime, 53 | &self.h, 54 | ] 55 | .into_iter() 56 | } 57 | } 58 | 59 | #[allow(dead_code)] 60 | impl PIOPforDLComparison { 61 | pub fn prover_init<'a>( 62 | domain_k: &'a GeneralEvaluationDomain, 63 | domain_h: &'a GeneralEvaluationDomain, 64 | f: &'a LabeledPolynomial>, 65 | g: &'a LabeledPolynomial>, 66 | enforced_degree_bound: Option, 67 | ) -> Result, Error> { 68 | Ok(ProverState { 69 | domain_k, 70 | domain_h, 71 | f, 72 | g, 73 | enforced_degree_bound, 74 | first_oracles: None, 75 | a_s: None, 76 | c_s: None, 77 | delta: None, 78 | }) 79 | } 80 | 81 | pub fn prover_first_round<'a, R: Rng>( 82 | mut state: ProverState<'a, F>, 83 | _rng: &mut R, 84 | ) -> Result<(ProverMsg, ProverFirstOracles, ProverState<'a, F>), Error> { 85 | let m = state.domain_k.size(); 86 | let n = state.domain_h.size(); 87 | let delta = state.domain_h.element(1).sqrt().unwrap(); 88 | 89 | // Compute s 90 | let f_evals = state.domain_k.fft(state.f.polynomial().coeffs()); 91 | let g_evals = state.domain_k.fft(state.g.polynomial().coeffs()); 92 | 93 | let s_evals: Vec = f_evals 94 | .iter() 95 | .zip(g_evals.iter()) 96 | .map(|(&f_eval, g_eval)| f_eval * g_eval.inverse().unwrap()) 97 | .collect(); 98 | 99 | let s = DensePolynomial::::from_coefficients_slice(&state.domain_k.ifft(&s_evals)); 100 | let s = LabeledPolynomial::new(String::from("s"), s, state.enforced_degree_bound, Some(1)); 101 | 102 | // For b in {f, g, s}, compute b_prime 103 | let omegas = state.domain_h.elements(); 104 | let omega_powers_mapping = omegas 105 | .enumerate() 106 | .map(|(power, omega)| (omega, power)) 107 | .collect::>(); 108 | 109 | let f_prime_evals = f_evals 110 | .iter() 111 | .map(|f| { 112 | let power = omega_powers_mapping.get(f).expect("F evals are wrong"); 113 | delta.pow(&[(*power) as u64]) 114 | }) 115 | .collect::>(); 116 | 117 | let f_prime = 118 | DensePolynomial::::from_coefficients_slice(&state.domain_k.ifft(&f_prime_evals)); 119 | let f_prime = LabeledPolynomial::new( 120 | String::from("f_prime"), 121 | f_prime, 122 | state.enforced_degree_bound, 123 | Some(1), 124 | ); 125 | 126 | let g_prime_evals = g_evals 127 | .iter() 128 | .map(|g| { 129 | let power = omega_powers_mapping.get(g).expect("G evals are wrong"); 130 | delta.pow(&[(*power) as u64]) 131 | }) 132 | .collect::>(); 133 | 134 | let g_prime = 135 | DensePolynomial::::from_coefficients_slice(&state.domain_k.ifft(&g_prime_evals)); 136 | let g_prime = LabeledPolynomial::new( 137 | String::from("g_prime"), 138 | g_prime, 139 | state.enforced_degree_bound, 140 | Some(1), 141 | ); 142 | 143 | let s_prime_evals = s_evals 144 | .iter() 145 | .map(|s| { 146 | let power = omega_powers_mapping.get(s).expect("S evals are wrong"); 147 | delta.pow(&[(*power) as u64]) 148 | }) 149 | .collect::>(); 150 | 151 | let s_prime = 152 | DensePolynomial::::from_coefficients_slice(&state.domain_k.ifft(&s_prime_evals)); 153 | let s_prime = LabeledPolynomial::new( 154 | String::from("s_prime"), 155 | s_prime, 156 | state.enforced_degree_bound, 157 | Some(1), 158 | ); 159 | 160 | // Compute the sequence h 161 | let mut a_s = vec![F::one()]; 162 | let mut c_s = vec![n]; 163 | 164 | let to_pad = m - n; 165 | if to_pad > 0 { 166 | a_s.push(F::zero()); 167 | c_s.push(to_pad); 168 | } 169 | 170 | let seq = generate_sequence(delta, &a_s, &c_s); 171 | let h = DensePolynomial::::from_coefficients_slice(&state.domain_k.ifft(&seq)); 172 | let h = LabeledPolynomial::new(String::from("h"), h, state.enforced_degree_bound, Some(1)); 173 | 174 | // create ProverFirstOracles struct 175 | let prover_oracles = ProverFirstOracles { 176 | s, 177 | f_prime, 178 | g_prime, 179 | s_prime, 180 | h, 181 | }; 182 | 183 | // Prover message 184 | let msg = ProverMsg::EmptyMessage; 185 | 186 | // Update Prover state 187 | state.first_oracles = Some(prover_oracles.clone()); 188 | state.a_s = Some(a_s); 189 | state.c_s = Some(c_s); 190 | state.delta = Some(delta); 191 | 192 | Ok((msg, prover_oracles, state)) 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /proof_of_function_relation/src/discrete_log_comparison/proof.rs: -------------------------------------------------------------------------------- 1 | use crate::geo_seq::proof::Proof as GeoProof; 2 | use crate::non_zero_over_k::proof::Proof as NonZeroProof; 3 | use crate::subset_over_k::proof::Proof as SubsetProof; 4 | use ark_ff::PrimeField; 5 | use homomorphic_poly_commit::AdditivelyHomomorphicPCS; 6 | use zero_over_k::zero_over_k::proof::Proof as ZeroProof; 7 | 8 | use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError}; 9 | use ark_std::io::{Read, Write}; 10 | 11 | #[derive(Clone, CanonicalSerialize, CanonicalDeserialize)] 12 | pub struct Proof> { 13 | // Commitments 14 | pub s_commit: PC::Commitment, 15 | pub f_prime_commit: PC::Commitment, 16 | pub g_prime_commit: PC::Commitment, 17 | pub s_prime_commit: PC::Commitment, 18 | pub h_commit: PC::Commitment, 19 | 20 | // Proofs 21 | pub f_prime_square_proof: ZeroProof, 22 | pub g_prime_square_proof: ZeroProof, 23 | pub s_prime_square_proof: ZeroProof, 24 | pub f_prime_product_proof: ZeroProof, 25 | pub f_prime_subset_proof: SubsetProof, 26 | pub g_prime_subset_proof: SubsetProof, 27 | pub s_prime_subset_proof: SubsetProof, 28 | pub h_proof: GeoProof, 29 | pub nzk_f_prime_proof: NonZeroProof, 30 | pub nzk_g_prime_proof: NonZeroProof, 31 | pub nzk_s_prime_proof: NonZeroProof, 32 | pub nzk_s_minus_one_proof: NonZeroProof, 33 | } 34 | -------------------------------------------------------------------------------- /proof_of_function_relation/src/discrete_log_comparison/tests.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use ark_bn254::{Bn254, Fr}; 4 | use ark_ff::{to_bytes, Field, SquareRootField}; 5 | use ark_poly::{ 6 | univariate::DensePolynomial, EvaluationDomain, GeneralEvaluationDomain, UVPolynomial, 7 | }; 8 | use ark_poly_commit::{LabeledPolynomial, PolynomialCommitment}; 9 | use ark_std::rand::thread_rng; 10 | use blake2::Blake2s; 11 | use homomorphic_poly_commit::marlin_kzg::KZG10; 12 | 13 | use crate::{discrete_log_comparison::DLComparison, error::Error}; 14 | use fiat_shamir_rng::{FiatShamirRng, SimpleHashFiatShamirRng}; 15 | use rand_chacha::ChaChaRng; 16 | 17 | type FS = SimpleHashFiatShamirRng; 18 | type F = Fr; 19 | type PC = KZG10; 20 | 21 | #[test] 22 | fn test_square_root_friendly() { 23 | let m = 6; 24 | let n = 4; 25 | 26 | let domain = GeneralEvaluationDomain::new(m).unwrap(); 27 | let domain2 = GeneralEvaluationDomain::new(n).unwrap(); 28 | 29 | let generator: F = domain.element(1); 30 | let g2: F = domain2.element(1); 31 | 32 | assert_eq!(g2.pow(&[n as u64]), F::from(1u64)); 33 | 34 | // assert_eq!(generator, g2); 35 | 36 | let sq_root = generator.sqrt().unwrap(); 37 | let sq_root2 = g2.sqrt().unwrap(); 38 | 39 | assert_eq!(sq_root2.pow(&[2 * n as u64]), F::from(1u64)); 40 | 41 | // println!("{}", sq_root); 42 | 43 | assert_eq!(generator, sq_root * sq_root); 44 | assert_eq!(g2, sq_root2 * sq_root2); 45 | } 46 | 47 | #[test] 48 | fn test_discrete_log_proof() { 49 | let rng = &mut thread_rng(); 50 | let m = 8; 51 | let n = 4; 52 | 53 | let enforced_degree_bound = m + 1; 54 | let enforced_hiding_bound = Some(1); 55 | 56 | let domain_k = GeneralEvaluationDomain::::new(m).unwrap(); 57 | let domain_h = GeneralEvaluationDomain::::new(n).unwrap(); 58 | 59 | // For the test to pass, each value in f_evals must be less than its corresponding value in 60 | // g_evals, mod n (since we use domain_h) 61 | 62 | let f_evals = vec![ 63 | domain_h.element(1), 64 | domain_h.element(2), 65 | domain_h.element(3), 66 | domain_h.element(3), 67 | domain_h.element(1), 68 | domain_h.element(2), 69 | domain_h.element(3), 70 | domain_h.element(3), 71 | ]; 72 | let g_evals = vec![ 73 | domain_h.element(0), 74 | domain_h.element(1), 75 | domain_h.element(1), 76 | domain_h.element(2), 77 | domain_h.element(0), 78 | domain_h.element(1), 79 | domain_h.element(1), 80 | domain_h.element(2), 81 | ]; 82 | 83 | let f_poly = DensePolynomial::::from_coefficients_vec(domain_k.ifft(&f_evals)); 84 | let f_poly = LabeledPolynomial::new( 85 | String::from("f_poly"), 86 | f_poly, 87 | Some(enforced_degree_bound), 88 | enforced_hiding_bound, 89 | ); 90 | let g_poly = DensePolynomial::::from_coefficients_vec(domain_k.ifft(&g_evals)); 91 | let g_poly = LabeledPolynomial::new( 92 | String::from("g_poly"), 93 | g_poly, 94 | Some(enforced_degree_bound), 95 | enforced_hiding_bound, 96 | ); 97 | 98 | let max_degree = 20; 99 | let pp = PC::setup(max_degree, None, rng).unwrap(); 100 | let (ck, vk) = PC::trim(&pp, max_degree, 1, Some(&[2, enforced_degree_bound])).unwrap(); 101 | 102 | let (commitments, rands) = 103 | PC::commit(&ck, &[f_poly.clone(), g_poly.clone()], Some(rng)).unwrap(); 104 | 105 | let mut fs_rng = FS::initialize(&to_bytes!(b"Testing :)").unwrap()); 106 | 107 | let proof = DLComparison::::prove( 108 | &ck, 109 | &domain_k, 110 | &domain_h, 111 | &f_poly, 112 | &commitments[0], 113 | &rands[0], 114 | &g_poly, 115 | &commitments[1], 116 | &rands[1], 117 | Some(enforced_degree_bound), 118 | &mut fs_rng, 119 | rng, 120 | ) 121 | .unwrap(); 122 | 123 | let res = DLComparison::verify( 124 | &vk, 125 | &ck, 126 | &domain_k, 127 | &domain_h, 128 | &commitments[0], 129 | &commitments[1], 130 | Some(enforced_degree_bound), 131 | proof, 132 | &mut fs_rng, 133 | ) 134 | .unwrap(); 135 | 136 | assert_eq!((), res) 137 | } 138 | 139 | #[test] 140 | fn test_malicious_discrete_log_proof() { 141 | let rng = &mut thread_rng(); 142 | let m = 8; 143 | let n = 4; 144 | 145 | let enforced_degree_bound = m + 1; 146 | let enforced_hiding_bound = Some(1); 147 | 148 | let domain_k = GeneralEvaluationDomain::::new(m).unwrap(); 149 | let domain_h = GeneralEvaluationDomain::::new(n).unwrap(); 150 | 151 | let f_evals = vec![ 152 | domain_h.element(1), 153 | domain_h.element(2), 154 | domain_h.element(3), 155 | domain_h.element(3), 156 | domain_h.element(1), 157 | domain_h.element(2), 158 | domain_h.element(3), 159 | domain_h.element(3), 160 | ]; 161 | let g_evals = vec![ 162 | domain_h.element(3), // here log(g) > log(f) 163 | domain_h.element(1), 164 | domain_h.element(1), 165 | domain_h.element(2), 166 | domain_h.element(0), 167 | domain_h.element(1), 168 | domain_h.element(1), 169 | domain_h.element(2), 170 | ]; 171 | 172 | let f_poly = DensePolynomial::::from_coefficients_vec(domain_k.ifft(&f_evals)); 173 | let f_poly = LabeledPolynomial::new( 174 | String::from("f_poly"), 175 | f_poly, 176 | Some(enforced_degree_bound), 177 | enforced_hiding_bound, 178 | ); 179 | let g_poly = DensePolynomial::::from_coefficients_vec(domain_k.ifft(&g_evals)); 180 | let g_poly = LabeledPolynomial::new( 181 | String::from("g_poly"), 182 | g_poly, 183 | Some(enforced_degree_bound), 184 | enforced_hiding_bound, 185 | ); 186 | 187 | let max_degree = 20; 188 | let pp = PC::setup(max_degree, None, rng).unwrap(); 189 | let (ck, vk) = PC::trim(&pp, max_degree, 1, Some(&[2, enforced_degree_bound])).unwrap(); 190 | 191 | let (commitments, rands) = 192 | PC::commit(&ck, &[f_poly.clone(), g_poly.clone()], Some(rng)).unwrap(); 193 | 194 | let mut fs_rng = FS::initialize(&to_bytes!(b"Testing :)").unwrap()); 195 | 196 | let proof = DLComparison::::prove( 197 | &ck, 198 | &domain_k, 199 | &domain_h, 200 | &f_poly, 201 | &commitments[0], 202 | &rands[0], 203 | &g_poly, 204 | &commitments[1], 205 | &rands[1], 206 | Some(enforced_degree_bound), 207 | &mut fs_rng, 208 | rng, 209 | ) 210 | .unwrap(); 211 | 212 | let res = DLComparison::verify( 213 | &vk, 214 | &ck, 215 | &domain_k, 216 | &domain_h, 217 | &commitments[0], 218 | &commitments[1], 219 | Some(enforced_degree_bound), 220 | proof, 221 | &mut fs_rng, 222 | ); 223 | assert!(res.is_err()); 224 | 225 | // Test for a specific error 226 | assert_eq!( 227 | res.err().unwrap(), 228 | Error::ZeroOverKError(String::from("Check2Failed")) 229 | ); 230 | } 231 | 232 | #[test] 233 | fn test_reject_large_degree() { 234 | let rng = &mut thread_rng(); 235 | let m = 8; 236 | let n = 4; 237 | 238 | let enforced_degree_bound = m + 1; 239 | let other_degree_bound = m + 5; 240 | let enforced_hiding_bound = Some(1); 241 | 242 | let domain_k = GeneralEvaluationDomain::::new(m).unwrap(); 243 | let domain_h = GeneralEvaluationDomain::::new(n).unwrap(); 244 | 245 | // For the test to pass, each value in f_evals must be less than its corresponding value in 246 | // g_evals, mod n (since we use domain_h) 247 | 248 | let f_evals = vec![ 249 | domain_h.element(1), 250 | domain_h.element(2), 251 | domain_h.element(3), 252 | domain_h.element(3), 253 | domain_h.element(1), 254 | domain_h.element(2), 255 | domain_h.element(3), 256 | domain_h.element(3), 257 | ]; 258 | let g_evals = vec![ 259 | domain_h.element(0), 260 | domain_h.element(1), 261 | domain_h.element(1), 262 | domain_h.element(2), 263 | domain_h.element(0), 264 | domain_h.element(1), 265 | domain_h.element(1), 266 | domain_h.element(2), 267 | ]; 268 | 269 | let f_poly = DensePolynomial::::from_coefficients_vec(domain_k.ifft(&f_evals)); 270 | let f_poly = LabeledPolynomial::new( 271 | String::from("f_poly"), 272 | f_poly, 273 | Some(other_degree_bound), 274 | enforced_hiding_bound, 275 | ); 276 | let g_poly = DensePolynomial::::from_coefficients_vec(domain_k.ifft(&g_evals)); 277 | let g_poly = LabeledPolynomial::new( 278 | String::from("g_poly"), 279 | g_poly, 280 | Some(other_degree_bound), 281 | enforced_hiding_bound, 282 | ); 283 | 284 | let max_degree = 20; 285 | let pp = PC::setup(max_degree, None, rng).unwrap(); 286 | let (ck, vk) = PC::trim( 287 | &pp, 288 | max_degree, 289 | 1, 290 | Some(&[2, enforced_degree_bound, other_degree_bound]), 291 | ) 292 | .unwrap(); 293 | 294 | let (commitments, rands) = 295 | PC::commit(&ck, &[f_poly.clone(), g_poly.clone()], Some(rng)).unwrap(); 296 | 297 | let mut fs_rng = FS::initialize(&to_bytes!(b"Testing :)").unwrap()); 298 | 299 | let proof = DLComparison::::prove( 300 | &ck, 301 | &domain_k, 302 | &domain_h, 303 | &f_poly, 304 | &commitments[0], 305 | &rands[0], 306 | &g_poly, 307 | &commitments[1], 308 | &rands[1], 309 | Some(other_degree_bound), 310 | &mut fs_rng, 311 | rng, 312 | ) 313 | .unwrap(); 314 | 315 | let res = DLComparison::verify( 316 | &vk, 317 | &ck, 318 | &domain_k, 319 | &domain_h, 320 | &commitments[0], 321 | &commitments[1], 322 | Some(enforced_degree_bound), 323 | proof, 324 | &mut fs_rng, 325 | ); 326 | 327 | assert!(res.is_err()); 328 | 329 | // Test for a specific error 330 | assert_eq!( 331 | res.err().unwrap(), 332 | Error::ZeroOverKError(String::from("BatchCheckError")) 333 | ); 334 | } 335 | } 336 | -------------------------------------------------------------------------------- /proof_of_function_relation/src/error.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, PartialEq)] 2 | pub enum Error { 3 | // In zero_over_k and geo_seq 4 | BatchCheckError, 5 | 6 | // In non_zero_over_k 7 | FEvalIsZero, 8 | 9 | // In zero_over_k, discrete_log_comparison, 10 | ToBytesError, 11 | 12 | // In discrete_log_comparison 13 | OmegaSqrtError, 14 | 15 | /// Number of user inputs is too large 16 | T2Large, 17 | 18 | ProofSerializationError, 19 | ProofDeserializationError, 20 | 21 | PCError { 22 | error: String, 23 | }, 24 | 25 | ZeroOverKError(String), 26 | } 27 | 28 | /// Convert an ark_poly_commit error 29 | pub fn to_pc_error(error: PC::Error) -> Error 30 | where 31 | F: ark_ff::Field, 32 | PC: ark_poly_commit::PolynomialCommitment>, 33 | { 34 | println!("Polynomial Commitment Error: {:?}", error); 35 | Error::PCError { 36 | error: format!("Polynomial Commitment Error: {:?}", error), 37 | } 38 | } 39 | 40 | impl From for Error { 41 | fn from(err: zero_over_k::error::Error) -> Self { 42 | Self::ZeroOverKError(format!("{:?}", err)) 43 | } 44 | } 45 | 46 | impl From for Error { 47 | fn from(err: homomorphic_poly_commit::error::Error) -> Self { 48 | Self::PCError { 49 | error: format!("{:?}", err), 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /proof_of_function_relation/src/geo_seq/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::error::{to_pc_error, Error}; 2 | use crate::geo_seq::proof::Proof; 3 | use ark_ff::{to_bytes, PrimeField}; 4 | use ark_poly::{univariate::DensePolynomial, EvaluationDomain, GeneralEvaluationDomain}; 5 | use ark_poly_commit::{LabeledCommitment, LabeledPolynomial, QuerySet}; 6 | use ark_std::marker::PhantomData; 7 | use fiat_shamir_rng::FiatShamirRng; 8 | use homomorphic_poly_commit::AdditivelyHomomorphicPCS; 9 | use rand::Rng; 10 | use rand_core::OsRng; 11 | use std::iter; 12 | use zero_over_k::{ 13 | virtual_oracle::generic_shifting_vo::{vo_term::VOTerm, GenericShiftingVO}, 14 | zero_over_k::ZeroOverK, 15 | {geometric_seq_check, vo_constant}, 16 | }; 17 | 18 | pub mod proof; 19 | mod tests; 20 | 21 | pub struct GeoSeqTest, FS: FiatShamirRng> { 22 | _field: PhantomData, 23 | _pc: PhantomData, 24 | _fs: PhantomData, 25 | } 26 | 27 | impl, FS: FiatShamirRng> GeoSeqTest { 28 | pub const PROTOCOL_NAME: &'static [u8] = b"Geometric Sequence Test"; 29 | // TODO: for both prove() and verify: 30 | // TODO: have an assertion that domain is large enough given m 31 | // TODO: move the padding outside and the check that the length is correct 32 | // TODO: verifier should check that the size of the sequence is correct given the domain 33 | pub fn prove( 34 | ck: &PC::CommitterKey, 35 | common_ratio: F, 36 | f: &LabeledPolynomial>, 37 | f_commit: &LabeledCommitment, 38 | f_rand: &PC::Randomness, 39 | sequence_initial_values: &Vec, 40 | sequence_lengths: &Vec, 41 | domain: &GeneralEvaluationDomain, 42 | rng: &mut R, 43 | ) -> Result, Error> { 44 | // Generate the GeoSequenceVO virtual oracle 45 | let alphas = [F::one(), domain.element(1)]; 46 | let geo_seq_vo = GenericShiftingVO::new( 47 | &[0, 0], 48 | &alphas, 49 | geometric_seq_check!(common_ratio, sequence_lengths, domain), 50 | )?; 51 | 52 | let fs_bytes = &to_bytes![ 53 | &Self::PROTOCOL_NAME, 54 | sequence_initial_values, 55 | sequence_lengths 56 | .iter() 57 | .map(|&x| x as u64) 58 | .collect::>(), 59 | common_ratio, 60 | &[f_commit.clone()].to_vec(), 61 | &alphas.to_vec() 62 | ] 63 | .map_err(|_| Error::ToBytesError)?; 64 | let mut fs_rng = FS::initialize(fs_bytes); 65 | 66 | let mut query_set = QuerySet::new(); 67 | let sequence_starting_indices = iter::once(0) 68 | .chain(sequence_lengths.iter().scan(0, |st, elem| { 69 | *st += elem; 70 | Some(*st) 71 | })) 72 | .collect::>(); 73 | for (i, &pi) in sequence_starting_indices.iter().enumerate() { 74 | query_set.insert(( 75 | f.label().clone(), 76 | ( 77 | format!("gamma_pi_{}", i), 78 | domain.element(1).pow([pi as u64]), 79 | ), 80 | )); 81 | } 82 | 83 | let separation_challenge = F::rand(&mut fs_rng); 84 | let opening_proof = PC::batch_open( 85 | ck, 86 | &[f.clone()], 87 | &[f_commit.clone()], 88 | &query_set, 89 | separation_challenge, 90 | &[f_rand.clone()], 91 | Some(rng), 92 | ) 93 | .map_err(to_pc_error::)?; 94 | 95 | let z_proof = ZeroOverK::::prove( 96 | &[f.clone()], 97 | &[f_commit.clone()], 98 | &[f_rand.clone()], 99 | f.degree_bound(), 100 | &geo_seq_vo, 101 | &domain, 102 | &ck, 103 | rng, 104 | )?; 105 | 106 | let proof = Proof:: { 107 | z_proof, 108 | opening_proof, 109 | }; 110 | Ok(proof) 111 | } 112 | 113 | pub fn verify( 114 | common_ratio: F, 115 | sequence_initial_values: &Vec, 116 | sequence_lengths: &Vec, 117 | domain: &GeneralEvaluationDomain, 118 | f_commit: &LabeledCommitment, 119 | enforced_degree_bound: Option, 120 | proof: Proof, 121 | vk: &PC::VerifierKey, 122 | ) -> Result<(), Error> { 123 | let bounded_f_commit = LabeledCommitment::new( 124 | f_commit.label().clone(), 125 | f_commit.commitment().clone(), 126 | enforced_degree_bound, 127 | ); 128 | 129 | let alphas = [F::one(), domain.element(1)]; 130 | let geo_seq_vo = GenericShiftingVO::new( 131 | &[0, 0], 132 | &alphas, 133 | geometric_seq_check!(common_ratio, sequence_lengths, domain), 134 | )?; 135 | 136 | // Test that for all i in n, check that f(gamma^p_i) = a_i 137 | let sequence_starting_indices = iter::once(0) 138 | .chain(sequence_lengths.iter().scan(0, |st, elem| { 139 | *st += elem; 140 | Some(*st) 141 | })) 142 | .collect::>(); 143 | let points = sequence_starting_indices 144 | .iter() 145 | .map(|&pi| domain.element(1).pow([pi as u64])) 146 | .collect::>(); 147 | 148 | let fs_bytes = &to_bytes![ 149 | &Self::PROTOCOL_NAME, 150 | sequence_initial_values, 151 | sequence_lengths 152 | .iter() 153 | .map(|&x| x as u64) 154 | .collect::>(), 155 | common_ratio, 156 | &[f_commit.clone()].to_vec(), 157 | &alphas.to_vec() 158 | ] 159 | .map_err(|_| Error::ToBytesError)?; 160 | let mut fs_rng = FS::initialize(fs_bytes); 161 | 162 | let mut query_set = QuerySet::new(); 163 | for (i, &point_i) in points.iter().enumerate() { 164 | query_set.insert(( 165 | bounded_f_commit.label().clone(), 166 | (format!("gamma_pi_{}", i), point_i), 167 | )); 168 | } 169 | let mut evaluations = ark_poly_commit::Evaluations::new(); 170 | for (&point_i, &a_i) in points.iter().zip(sequence_initial_values.iter()) { 171 | evaluations.insert((bounded_f_commit.label().clone(), point_i), a_i); 172 | } 173 | 174 | let separation_challenge = F::rand(&mut fs_rng); 175 | match PC::batch_check( 176 | vk, 177 | &[bounded_f_commit.clone()], 178 | &query_set, 179 | &evaluations, 180 | &proof.opening_proof, 181 | separation_challenge, 182 | &mut OsRng, 183 | ) { 184 | Ok(true) => Ok(()), 185 | Ok(false) => Err(Error::BatchCheckError), 186 | Err(e) => panic!("{:?}", e), 187 | }?; 188 | 189 | // TODO: is this check done or does the function return before it should if the above batch 190 | // check passes? 191 | 192 | // let seq = generate_sequence::(r, &a_s.as_slice(), &c_s.as_slice()); 193 | // let f = DensePolynomial::::from_coefficients_slice(&domain.ifft(&seq)); 194 | 195 | // TODO: raise a different error? 196 | ZeroOverK::::verify( 197 | proof.z_proof, 198 | &[bounded_f_commit.clone()], 199 | enforced_degree_bound, 200 | &geo_seq_vo, 201 | &domain, 202 | vk, 203 | )?; 204 | 205 | Ok(()) 206 | } 207 | 208 | #[allow(dead_code)] 209 | /// Inefficiently verify that the sequence is valid 210 | pub fn naive_verify(seq: &Vec, r: F, a_s: &[F], c_s: &[usize]) -> bool { 211 | if a_s.len() != c_s.len() { 212 | return false; 213 | } 214 | 215 | if c_s.iter().fold(0, |x, y| x + y) != seq.len() { 216 | return false; 217 | } 218 | 219 | let mut i = 0; 220 | for (a_i, a) in a_s.iter().enumerate() { 221 | let mut r_pow = F::from(1 as u64); 222 | for _ in 0..c_s[a_i] { 223 | let expected = *a * r_pow; 224 | 225 | if expected != seq[i] { 226 | return false; 227 | } 228 | 229 | r_pow = r * r_pow; 230 | i += 1; 231 | } 232 | } 233 | 234 | return true; 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /proof_of_function_relation/src/geo_seq/proof.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::PrimeField; 2 | use homomorphic_poly_commit::AdditivelyHomomorphicPCS; 3 | use zero_over_k::zero_over_k::proof::Proof as ZProof; 4 | 5 | use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError}; 6 | use ark_std::io::{Read, Write}; 7 | 8 | #[derive(Clone, CanonicalSerialize, CanonicalDeserialize)] 9 | pub struct Proof> { 10 | pub z_proof: ZProof, 11 | pub opening_proof: PC::BatchProof, 12 | } 13 | -------------------------------------------------------------------------------- /proof_of_function_relation/src/geo_seq/tests.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use crate::{error::Error, geo_seq::GeoSeqTest, util::generate_sequence}; 4 | use ark_bn254::{Bn254, Fr}; 5 | use ark_poly::{ 6 | univariate::DensePolynomial, EvaluationDomain, GeneralEvaluationDomain, UVPolynomial, 7 | }; 8 | use ark_poly_commit::{LabeledPolynomial, PolynomialCommitment}; 9 | use ark_std::rand::thread_rng; 10 | use blake2::Blake2s; 11 | use fiat_shamir_rng::SimpleHashFiatShamirRng; 12 | use homomorphic_poly_commit::marlin_kzg::KZG10; 13 | use rand_chacha::ChaChaRng; 14 | 15 | type FS = SimpleHashFiatShamirRng; 16 | type F = Fr; 17 | type PC = KZG10; 18 | 19 | /// Test that geometric_sequence() works correctly 20 | #[test] 21 | fn test_generate_sequence_0() { 22 | let common_ratio = F::from(2u64); 23 | let sequence_initial_values = &[F::from(1u64), F::from(2u64)]; 24 | let sequence_lengths = &[3, 3]; 25 | let seq = generate_sequence(common_ratio, sequence_initial_values, sequence_lengths); 26 | 27 | let expected = [1, 2, 4, 2, 4, 8] 28 | .iter() 29 | .map(|x| F::from(*x as u64)) 30 | .collect::>(); 31 | assert_eq!(expected.len(), seq.len()); 32 | for (i, s) in seq.iter().enumerate() { 33 | assert_eq!(&expected[i], s); 34 | } 35 | 36 | assert!(GeoSeqTest::, FS>::naive_verify( 37 | &seq, 38 | common_ratio, 39 | sequence_initial_values, 40 | sequence_lengths 41 | )); 42 | } 43 | 44 | #[test] 45 | fn test_generate_sequence_1() { 46 | let common_ratio = F::from(1u64); 47 | let sequence_initial_values = &[F::from(1u64), F::from(1u64)]; 48 | let sequence_lengths = &[1, 1]; 49 | 50 | let seq = generate_sequence(common_ratio, sequence_initial_values, sequence_lengths); 51 | let expected = [1, 1] 52 | .iter() 53 | .map(|x| F::from(*x as u64)) 54 | .collect::>(); 55 | assert_eq!(expected.len(), seq.len()); 56 | for (i, s) in seq.iter().enumerate() { 57 | assert_eq!(&expected[i], s); 58 | } 59 | assert!(GeoSeqTest::, FS>::naive_verify( 60 | &seq, 61 | common_ratio, 62 | sequence_initial_values, 63 | sequence_lengths 64 | )); 65 | } 66 | 67 | #[test] 68 | fn test_geo_seq_proof() { 69 | let rng = &mut thread_rng(); 70 | 71 | // define a sequence 72 | let common_ratio = Fr::from(9u64); 73 | let mut sequence_initial_values = vec![ 74 | Fr::from(2u64), 75 | Fr::from(5u64), 76 | Fr::from(7u64), 77 | Fr::from(11u64), 78 | ]; 79 | let mut sequence_lengths = vec![5, 3, 10, 30]; 80 | 81 | // choose an appropriate domain for our sequence 82 | let m = sequence_lengths.iter().sum(); 83 | let domain_k = GeneralEvaluationDomain::::new(m).unwrap(); 84 | 85 | // pad sequence to fit the domain 86 | let to_pad = domain_k.size() - m; 87 | if to_pad > 0 { 88 | sequence_initial_values.push(Fr::from(0u64)); 89 | sequence_lengths.push(to_pad); 90 | } 91 | 92 | // generate the sequence 93 | let seq = generate_sequence::( 94 | common_ratio, 95 | &sequence_initial_values.as_slice(), 96 | &sequence_lengths.as_slice(), 97 | ); 98 | 99 | // Setup our polynomial commitment scheme 100 | let max_degree = 80; 101 | let max_hiding = 1; 102 | 103 | let enforced_hiding_bound = Some(1); 104 | let enforced_degree_bound = domain_k.size() + 1; // masking polynomials in zero over k have degree |K|+1 by definition 105 | 106 | let pp = PC::setup(max_degree, None, rng).unwrap(); 107 | let (ck, vk) = PC::trim( 108 | &pp, 109 | max_degree, 110 | max_hiding, 111 | Some(&[2, enforced_degree_bound]), 112 | ) 113 | .unwrap(); 114 | 115 | // Generate a polynomial from the sequence defined above 116 | let f = DensePolynomial::::from_coefficients_slice(&domain_k.ifft(&seq)); 117 | let f = LabeledPolynomial::new( 118 | String::from("f"), 119 | f, 120 | Some(enforced_degree_bound), 121 | enforced_hiding_bound, 122 | ); 123 | 124 | let (commitment, rands) = PC::commit(&ck, &[f.clone()], Some(rng)).unwrap(); 125 | 126 | let proof = GeoSeqTest::, FS>::prove( 127 | &ck, 128 | common_ratio, 129 | &f, 130 | &commitment[0].clone(), 131 | &rands[0].clone(), 132 | &mut sequence_initial_values, 133 | &mut sequence_lengths, 134 | &domain_k, 135 | rng, 136 | ) 137 | .unwrap(); 138 | 139 | let res = GeoSeqTest::, FS>::verify( 140 | common_ratio, 141 | &sequence_initial_values, 142 | &sequence_lengths, 143 | &domain_k, 144 | &commitment[0], 145 | Some(enforced_degree_bound), 146 | proof, 147 | &vk, 148 | ) 149 | .unwrap(); 150 | 151 | assert_eq!((), res) 152 | } 153 | 154 | #[test] 155 | fn test_geo_seq_invalid() { 156 | let rng = &mut thread_rng(); 157 | 158 | // define a sequence 159 | let common_ratio = Fr::from(9u64); 160 | let mut sequence_initial_values = vec![ 161 | Fr::from(2u64), 162 | Fr::from(5u64), 163 | Fr::from(7u64), 164 | Fr::from(11u64), 165 | ]; 166 | let mut sequence_lengths = vec![5, 3, 10, 30]; 167 | 168 | // choose an appropriate domain for our sequence 169 | let m = sequence_lengths.iter().sum(); 170 | let domain_k = GeneralEvaluationDomain::::new(m).unwrap(); 171 | 172 | // pad sequence to fit the domain 173 | let to_pad = domain_k.size() - m; 174 | if to_pad > 0 { 175 | sequence_initial_values.push(Fr::from(0u64)); 176 | sequence_lengths.push(to_pad); 177 | } 178 | 179 | // generate the sequence 180 | let seq = generate_sequence::( 181 | common_ratio, 182 | &sequence_initial_values.as_slice(), 183 | &sequence_lengths.as_slice(), 184 | ); 185 | 186 | // Setup our polynomial commitment scheme 187 | let max_degree = 80; 188 | let max_hiding = 1; 189 | 190 | let enforced_hiding_bound = Some(1); 191 | let enforced_degree_bound = domain_k.size() + 1; // masking polynomials in zero over k have degree |K|+1 by definition 192 | 193 | let pp = PC::setup(max_degree, None, rng).unwrap(); 194 | let (ck, vk) = PC::trim( 195 | &pp, 196 | max_degree, 197 | max_hiding, 198 | Some(&[2, enforced_degree_bound]), 199 | ) 200 | .unwrap(); 201 | 202 | // Generate a polynomial from the sequence defined above 203 | let f = DensePolynomial::::from_coefficients_slice(&domain_k.ifft(&seq)); 204 | let f = LabeledPolynomial::new( 205 | String::from("f"), 206 | f, 207 | Some(enforced_degree_bound), 208 | enforced_hiding_bound, 209 | ); 210 | 211 | let (commitment, rands) = PC::commit(&ck, &[f.clone()], Some(rng)).unwrap(); 212 | 213 | let wrong_common_ratio = Fr::from(2u64); 214 | 215 | let proof = GeoSeqTest::, FS>::prove( 216 | &ck, 217 | wrong_common_ratio, 218 | &f, 219 | &commitment[0].clone(), 220 | &rands[0].clone(), 221 | &mut sequence_initial_values, 222 | &mut sequence_lengths, 223 | &domain_k, 224 | rng, 225 | ) 226 | .unwrap(); 227 | 228 | let res = GeoSeqTest::, FS>::verify( 229 | common_ratio, 230 | &sequence_initial_values, 231 | &sequence_lengths, 232 | &domain_k, 233 | &commitment[0], 234 | Some(enforced_degree_bound), 235 | proof, 236 | &vk, 237 | ); 238 | 239 | assert!(common_ratio != wrong_common_ratio); 240 | 241 | assert!(res.is_err()); 242 | 243 | // Test for a specific error 244 | assert_eq!(res.err().unwrap(), Error::BatchCheckError); 245 | } 246 | 247 | #[test] 248 | fn test_reject_wrong_degree() { 249 | let rng = &mut thread_rng(); 250 | 251 | // define a sequence 252 | let common_ratio = Fr::from(9u64); 253 | let mut sequence_initial_values = vec![ 254 | Fr::from(2u64), 255 | Fr::from(5u64), 256 | Fr::from(7u64), 257 | Fr::from(11u64), 258 | ]; 259 | let mut sequence_lengths = vec![5, 3, 10, 30]; 260 | 261 | // choose an appropriate domain for our sequence 262 | let m = sequence_lengths.iter().sum(); 263 | let domain_k = GeneralEvaluationDomain::::new(m).unwrap(); 264 | 265 | // pad sequence to fit the domain 266 | let to_pad = domain_k.size() - m; 267 | if to_pad > 0 { 268 | sequence_initial_values.push(Fr::from(0u64)); 269 | sequence_lengths.push(to_pad); 270 | } 271 | 272 | // generate the sequence 273 | let seq = generate_sequence::( 274 | common_ratio, 275 | &sequence_initial_values.as_slice(), 276 | &sequence_lengths.as_slice(), 277 | ); 278 | 279 | // Setup our polynomial commitment scheme 280 | let max_degree = 80; 281 | let max_hiding = 1; 282 | 283 | let enforced_hiding_bound = Some(1); 284 | let enforced_degree_bound = domain_k.size() + 1; // masking polynomials in zero over k have degree |K|+1 by definition 285 | let other_degree = max_degree; 286 | 287 | let pp = PC::setup(max_degree, None, rng).unwrap(); 288 | let (ck, vk) = PC::trim( 289 | &pp, 290 | max_degree, 291 | max_hiding, 292 | Some(&[2, enforced_degree_bound, other_degree]), 293 | ) 294 | .unwrap(); 295 | 296 | // Generate a polynomial from the sequence defined above 297 | let f = DensePolynomial::::from_coefficients_slice(&domain_k.ifft(&seq)); 298 | let f = LabeledPolynomial::new( 299 | String::from("f"), 300 | f, 301 | Some(other_degree), 302 | enforced_hiding_bound, 303 | ); 304 | 305 | let (commitment, rands) = PC::commit(&ck, &[f.clone()], Some(rng)).unwrap(); 306 | 307 | let proof = GeoSeqTest::, FS>::prove( 308 | &ck, 309 | common_ratio, 310 | &f, 311 | &commitment[0].clone(), 312 | &rands[0].clone(), 313 | &mut sequence_initial_values, 314 | &mut sequence_lengths, 315 | &domain_k, 316 | rng, 317 | ) 318 | .unwrap(); 319 | 320 | let res = GeoSeqTest::, FS>::verify( 321 | common_ratio, 322 | &sequence_initial_values, 323 | &sequence_lengths, 324 | &domain_k, 325 | &commitment[0], 326 | Some(enforced_degree_bound), 327 | proof, 328 | &vk, 329 | ); 330 | 331 | assert!(res.is_err()); 332 | 333 | assert_eq!(res.err().unwrap(), Error::BatchCheckError); 334 | } 335 | } 336 | -------------------------------------------------------------------------------- /proof_of_function_relation/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod discrete_log_comparison; 2 | pub mod error; 3 | pub mod geo_seq; 4 | pub mod non_zero_over_k; 5 | pub mod subset_over_k; 6 | pub mod t_diag; 7 | pub mod t_functional_triple; 8 | pub mod t_strictly_lower_triangular_test; 9 | pub mod util; 10 | -------------------------------------------------------------------------------- /proof_of_function_relation/src/non_zero_over_k/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::error::{to_pc_error, Error}; 2 | use crate::non_zero_over_k::{piop::PIOPforNonZeroOverK, proof::Proof}; 3 | use ark_ff::PrimeField; 4 | use ark_poly::{univariate::DensePolynomial, GeneralEvaluationDomain}; 5 | use ark_poly_commit::{LabeledCommitment, LabeledPolynomial}; 6 | use fiat_shamir_rng::FiatShamirRng; 7 | use homomorphic_poly_commit::AdditivelyHomomorphicPCS; 8 | use rand::Rng; 9 | use std::marker::PhantomData; 10 | use zero_over_k::{ 11 | virtual_oracle::generic_shifting_vo::{presets, GenericShiftingVO}, 12 | zero_over_k::ZeroOverK, 13 | }; 14 | 15 | pub mod piop; 16 | pub mod proof; 17 | mod tests; 18 | 19 | pub struct NonZeroOverK, FS: FiatShamirRng> { 20 | _field: PhantomData, 21 | _pc: PhantomData, 22 | _fs_rng: PhantomData, 23 | } 24 | 25 | impl, FS: FiatShamirRng> NonZeroOverK { 26 | pub fn prove( 27 | ck: &PC::CommitterKey, 28 | domain: &GeneralEvaluationDomain, 29 | f: &LabeledPolynomial>, 30 | f_commit: &LabeledCommitment, 31 | f_rand: &PC::Randomness, 32 | rng: &mut R, 33 | ) -> Result, Error> { 34 | //----------------------------------------------- 35 | // INIT PROVER 36 | let prover_initial_state = PIOPforNonZeroOverK::prover_init(domain, f)?; 37 | 38 | //----------------------------------------------- 39 | // FIRST ROUND 40 | let (_, prover_first_oracles, _prover_state) = 41 | PIOPforNonZeroOverK::prover_first_round(prover_initial_state, rng)?; 42 | 43 | //----------------------------------------------- 44 | // RUN SUBPROTOCOLS 45 | let (commitments, rands) = PC::commit(ck, &[prover_first_oracles.g.clone()], Some(rng)) 46 | .map_err(to_pc_error::)?; 47 | 48 | let concrete_oracles = [f.clone(), prover_first_oracles.g.clone()]; 49 | 50 | let alphas = vec![F::one(), F::one()]; 51 | let inverse_check_oracle = 52 | GenericShiftingVO::new(&vec![0, 1], &alphas, presets::inverse_check)?; 53 | 54 | let zero_over_k_proof = ZeroOverK::::prove( 55 | &concrete_oracles, 56 | &[f_commit.clone(), commitments[0].clone()], 57 | &[f_rand.clone(), rands[0].clone()], 58 | f.degree_bound(), 59 | &inverse_check_oracle, 60 | &domain, 61 | ck, 62 | rng, 63 | )?; 64 | 65 | let proof = Proof { 66 | g_commit: commitments[0].commitment().clone(), 67 | zero_over_k_proof, 68 | }; 69 | 70 | Ok(proof) 71 | } 72 | 73 | pub fn verify( 74 | vk: &PC::VerifierKey, 75 | domain: &GeneralEvaluationDomain, 76 | f_commit: PC::Commitment, 77 | enforced_degree_bound: Option, 78 | proof: Proof, 79 | ) -> Result<(), Error> { 80 | let bounded_f_commit = 81 | LabeledCommitment::new(String::from("f"), f_commit, enforced_degree_bound); 82 | let g_commit = LabeledCommitment::new( 83 | String::from("g"), 84 | proof.g_commit.clone(), 85 | enforced_degree_bound, 86 | ); 87 | 88 | let concrete_oracles_commitments = [bounded_f_commit.clone(), g_commit]; 89 | let alphas = vec![F::one(), F::one()]; 90 | let inverse_check_oracle = 91 | GenericShiftingVO::new(&vec![0, 1], &alphas, presets::inverse_check)?; 92 | 93 | ZeroOverK::::verify( 94 | proof.zero_over_k_proof, 95 | &concrete_oracles_commitments, 96 | enforced_degree_bound, 97 | &inverse_check_oracle, 98 | &domain, 99 | &vk, 100 | ) 101 | .map_err(Error::from) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /proof_of_function_relation/src/non_zero_over_k/piop/mod.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::{FftField, PrimeField}; 2 | use ark_std::marker::PhantomData; 3 | 4 | pub mod prover; 5 | 6 | #[allow(dead_code)] 7 | pub struct PIOPforNonZeroOverK { 8 | _field: PhantomData, 9 | } 10 | -------------------------------------------------------------------------------- /proof_of_function_relation/src/non_zero_over_k/piop/prover.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Error; 2 | use crate::non_zero_over_k::piop::PIOPforNonZeroOverK; 3 | use ark_ff::{FftField, PrimeField}; 4 | use ark_marlin::ahp::prover::ProverMsg; 5 | use ark_poly::{ 6 | univariate::DensePolynomial, EvaluationDomain, GeneralEvaluationDomain, UVPolynomial, 7 | }; 8 | use ark_poly_commit::LabeledPolynomial; 9 | use ark_std::rand::Rng; 10 | 11 | pub struct ProverState<'a, F: PrimeField + FftField> { 12 | domain_k: &'a GeneralEvaluationDomain, 13 | 14 | f: &'a LabeledPolynomial>, 15 | 16 | first_oracles: Option>, 17 | } 18 | 19 | /// The first set of prover oracles 20 | #[derive(Clone)] 21 | pub struct ProverFirstOracles { 22 | pub g: LabeledPolynomial>, 23 | } 24 | 25 | impl ProverFirstOracles { 26 | pub fn iter(&self) -> impl Iterator>> { 27 | vec![&self.g].into_iter() 28 | } 29 | } 30 | 31 | impl PIOPforNonZeroOverK { 32 | pub fn prover_init<'a>( 33 | domain_k: &'a GeneralEvaluationDomain, 34 | f: &'a LabeledPolynomial>, 35 | ) -> Result, Error> { 36 | Ok(ProverState { 37 | domain_k, 38 | f, 39 | first_oracles: None, 40 | }) 41 | } 42 | 43 | pub fn prover_first_round<'a, R: Rng>( 44 | mut state: ProverState<'a, F>, 45 | _rng: &mut R, 46 | ) -> Result<(ProverMsg, ProverFirstOracles, ProverState<'a, F>), Error> { 47 | let f_evals = state.domain_k.fft(state.f.polynomial()); 48 | 49 | // Check that all the f_evals are nonzero; otherwise, .inverse() will return None and 50 | // .unwrap() will panic 51 | for f in f_evals.iter() { 52 | if f.inverse().is_none() { 53 | return Err(Error::FEvalIsZero); 54 | } 55 | } 56 | 57 | let g_evals = f_evals 58 | .iter() 59 | .map(|x| x.inverse().unwrap()) 60 | .collect::>(); 61 | 62 | let g = DensePolynomial::::from_coefficients_slice(&state.domain_k.ifft(&g_evals)); 63 | let g = LabeledPolynomial::new( 64 | String::from("g"), 65 | g.clone(), 66 | state.f.degree_bound(), 67 | Some(1), 68 | ); 69 | 70 | // create ProverFirstOracles struct 71 | let prover_oracles = ProverFirstOracles { g }; 72 | 73 | // Prover message 74 | let msg = ProverMsg::EmptyMessage; 75 | 76 | // Update Prover state 77 | state.first_oracles = Some(prover_oracles.clone()); 78 | 79 | Ok((msg, prover_oracles, state)) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /proof_of_function_relation/src/non_zero_over_k/proof.rs: -------------------------------------------------------------------------------- 1 | use ::zero_over_k::zero_over_k; 2 | use ark_ff::PrimeField; 3 | use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError}; 4 | use ark_std::io::{Read, Write}; 5 | use homomorphic_poly_commit::AdditivelyHomomorphicPCS; 6 | 7 | #[derive(Clone, CanonicalSerialize, CanonicalDeserialize)] 8 | pub struct Proof> { 9 | pub g_commit: PC::Commitment, 10 | pub zero_over_k_proof: zero_over_k::proof::Proof, 11 | } 12 | -------------------------------------------------------------------------------- /proof_of_function_relation/src/non_zero_over_k/tests.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod test { 3 | use crate::{error::Error, non_zero_over_k::NonZeroOverK}; 4 | use ark_bn254::{Bn254, Fr}; 5 | use ark_ff::Field; 6 | use ark_ff::One; 7 | use ark_ff::Zero; 8 | use ark_poly::{ 9 | univariate::DensePolynomial, EvaluationDomain, GeneralEvaluationDomain, UVPolynomial, 10 | }; 11 | use ark_poly_commit::{LabeledPolynomial, PolynomialCommitment}; 12 | use ark_std::rand::thread_rng; 13 | use blake2::Blake2s; 14 | use fiat_shamir_rng::SimpleHashFiatShamirRng; 15 | use homomorphic_poly_commit::marlin_kzg::KZG10; 16 | use rand_chacha::ChaChaRng; 17 | use zero_over_k::{ 18 | util::sample_vector, 19 | virtual_oracle::generic_shifting_vo::{presets, GenericShiftingVO}, 20 | }; 21 | 22 | type F = Fr; 23 | type PC = KZG10; 24 | type FS = SimpleHashFiatShamirRng; 25 | 26 | // This test should pass because it the randomly generated virtual oracle should 27 | // not evalute to 0 - if it does, just rerun the test 28 | #[test] 29 | fn test_non_zero_over_k() { 30 | let m = 8; 31 | let rng = &mut thread_rng(); 32 | let domain_k = GeneralEvaluationDomain::::new(m).unwrap(); 33 | 34 | let max_degree = 20; 35 | let max_hiding = 1; 36 | 37 | let enforced_hiding_bound = Some(1); 38 | let enforced_degree_bound = 14; 39 | 40 | let pp = PC::setup(max_degree, None, rng).unwrap(); 41 | let (ck, vk) = PC::trim( 42 | &pp, 43 | max_degree, 44 | max_hiding, 45 | Some(&[2, enforced_degree_bound]), 46 | ) 47 | .unwrap(); 48 | 49 | let f_unlabeled: DensePolynomial = DensePolynomial::rand(7, rng); 50 | let f = LabeledPolynomial::new( 51 | String::from("f"), 52 | f_unlabeled, 53 | Some(enforced_degree_bound), 54 | enforced_hiding_bound, 55 | ); 56 | 57 | let (f_commit, f_rand) = PC::commit(&ck, &[f.clone()], Some(rng)).unwrap(); 58 | 59 | let proof = NonZeroOverK::::prove( 60 | &ck, 61 | &domain_k, 62 | &f, 63 | &f_commit[0].clone(), 64 | &f_rand[0].clone(), 65 | rng, 66 | ) 67 | .unwrap(); 68 | 69 | let res = NonZeroOverK::::verify( 70 | &vk, 71 | &domain_k, 72 | f_commit[0].commitment().clone(), 73 | Some(enforced_degree_bound), 74 | proof, 75 | ) 76 | .unwrap(); 77 | 78 | assert_eq!(res, ()); 79 | } 80 | 81 | // This test will fail because one of the f_evals is 0. The prover will fail to run. 82 | #[test] 83 | fn test_f_eval_is_zero() { 84 | let m = 8; 85 | let rng = &mut thread_rng(); 86 | let domain_k = GeneralEvaluationDomain::::new(m).unwrap(); 87 | 88 | let max_degree = 20; 89 | let max_hiding = 1; 90 | 91 | let enforced_hiding_bound = Some(1); 92 | let enforced_degree_bound = 14; 93 | 94 | let pp = PC::setup(max_degree, None, rng).unwrap(); 95 | let (ck, _vk) = PC::trim( 96 | &pp, 97 | max_degree, 98 | max_hiding, 99 | Some(&[2, enforced_degree_bound]), 100 | ) 101 | .unwrap(); 102 | 103 | // choose a random function f and set one of its evaluations in K to 0 104 | let mut f_evals: Vec = sample_vector(rng, m); 105 | f_evals[4] = F::zero(); 106 | let f = DensePolynomial::::from_coefficients_slice(&domain_k.ifft(&f_evals)); 107 | let f = LabeledPolynomial::new( 108 | String::from("f"), 109 | f, 110 | Some(enforced_degree_bound), 111 | enforced_hiding_bound, 112 | ); 113 | 114 | let (f_commit, f_rand) = PC::commit(&ck, &[f.clone()], Some(rng)).unwrap(); 115 | 116 | let proof = NonZeroOverK::::prove( 117 | &ck, 118 | &domain_k, 119 | &f, 120 | &f_commit[0].clone(), 121 | &f_rand[0].clone(), 122 | rng, 123 | ); 124 | 125 | assert!(proof.is_err()); 126 | 127 | // Test for a specific error 128 | assert_eq!(proof.err().unwrap(), Error::FEvalIsZero); 129 | } 130 | 131 | // This test uses a virtual oracle that evalutes to zero over k. 132 | // Therefore, non_zero_over_k should fail. 133 | #[test] 134 | fn test_using_zero_over_k_vo() { 135 | let m = 8; 136 | let rng = &mut thread_rng(); 137 | let domain_k = GeneralEvaluationDomain::::new(m).unwrap(); 138 | 139 | let max_degree = 20; 140 | let max_hiding = 1; 141 | 142 | let enforced_hiding_bound = Some(1); 143 | let enforced_degree_bound = 14; 144 | 145 | let pp = PC::setup(max_degree, None, rng).unwrap(); 146 | let (ck, vk) = PC::trim( 147 | &pp, 148 | max_degree, 149 | max_hiding, 150 | Some(&[2, enforced_degree_bound]), 151 | ) 152 | .unwrap(); 153 | 154 | let a_evals: Vec = vec![ 155 | F::from(1u64), 156 | F::from(2u64), 157 | F::from(3u64), 158 | F::from(4u64), 159 | F::from(5u64), 160 | F::from(6u64), 161 | F::from(7u64), 162 | F::from(8u64), 163 | ]; 164 | 165 | let a = DensePolynomial::::from_coefficients_slice(&domain_k.ifft(&a_evals)); 166 | let a = LabeledPolynomial::new( 167 | String::from("a"), 168 | a, 169 | Some(enforced_degree_bound), 170 | enforced_hiding_bound, 171 | ); 172 | 173 | let a_evals = domain_k.fft(a.coeffs()); 174 | 175 | let b_evals = a_evals 176 | .iter() 177 | .map(|x| x.inverse().unwrap()) 178 | .collect::>(); 179 | 180 | let b = DensePolynomial::::from_coefficients_slice(&domain_k.ifft(&b_evals)); 181 | let b = LabeledPolynomial::new( 182 | String::from("b"), 183 | b.clone(), 184 | Some(enforced_degree_bound), 185 | enforced_hiding_bound, 186 | ); 187 | 188 | let concrete_oracles = [a, b]; 189 | let alphas = vec![F::one(), F::one()]; 190 | 191 | let zero_over_k_vo = 192 | GenericShiftingVO::new(&vec![0, 1], &alphas, presets::inverse_check).unwrap(); 193 | 194 | let f = zero_over_k_vo 195 | .compute_polynomial(&concrete_oracles) 196 | .unwrap(); 197 | let f = LabeledPolynomial::new( 198 | String::from("f"), 199 | f.clone(), 200 | Some(enforced_degree_bound), 201 | enforced_hiding_bound, 202 | ); 203 | 204 | let (f_commit, f_rand) = PC::commit(&ck, &[f.clone()], Some(rng)).unwrap(); 205 | 206 | let proof = NonZeroOverK::::prove( 207 | &ck, 208 | &domain_k, 209 | &f, 210 | &f_commit[0].clone(), 211 | &f_rand[0].clone(), 212 | rng, 213 | ) 214 | .unwrap(); 215 | let res = NonZeroOverK::::verify( 216 | &vk, 217 | &domain_k, 218 | f_commit[0].commitment().clone(), 219 | Some(enforced_degree_bound), 220 | proof, 221 | ); 222 | 223 | assert!(res.is_err()); 224 | // Test for a specific error 225 | assert_eq!( 226 | res.err().unwrap(), 227 | Error::ZeroOverKError(String::from("Check2Failed")) 228 | ); 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /proof_of_function_relation/src/subset_over_k/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::{error::Error, subset_over_k::proof::Proof}; 2 | use ark_ff::PrimeField; 3 | use ark_std::marker::PhantomData; 4 | use fiat_shamir_rng::FiatShamirRng; 5 | use homomorphic_poly_commit::AdditivelyHomomorphicPCS; 6 | 7 | pub mod proof; 8 | 9 | // TODO: implement SubsetOverK, currently just a placeholder 10 | pub struct SubsetOverK, FS: FiatShamirRng> { 11 | _field: PhantomData, 12 | _pc: PhantomData, 13 | _fs: PhantomData, 14 | } 15 | 16 | impl SubsetOverK 17 | where 18 | F: PrimeField, 19 | PC: AdditivelyHomomorphicPCS, 20 | FS: FiatShamirRng, 21 | { 22 | pub fn prove() -> Proof { 23 | Proof {} 24 | } 25 | 26 | pub fn verify(_proof: Proof) -> Result<(), Error> { 27 | Ok(()) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /proof_of_function_relation/src/subset_over_k/proof.rs: -------------------------------------------------------------------------------- 1 | use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError}; 2 | use ark_std::io::{Read, Write}; 3 | 4 | #[derive(Clone, CanonicalSerialize, CanonicalDeserialize)] 5 | pub struct Proof {} 6 | -------------------------------------------------------------------------------- /proof_of_function_relation/src/t_diag/piop/mod.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::PrimeField; 2 | use ark_poly_commit::{LinearCombination, PolynomialLabel}; 3 | use ark_std::marker::PhantomData; 4 | 5 | pub struct PIOPforTDiagTest { 6 | _field: PhantomData, 7 | } 8 | 9 | impl PIOPforTDiagTest { 10 | pub fn generate_h_linear_combination() -> LinearCombination { 11 | LinearCombination::new("h", vec![(F::one(), "h1"), (F::one(), "h2")]) 12 | } 13 | 14 | #[allow(non_snake_case)] 15 | pub fn generate_valM_plus_h2_linear_combination( 16 | val_label: &PolynomialLabel, 17 | ) -> LinearCombination { 18 | LinearCombination::new( 19 | "val_plus_h2", 20 | vec![(F::one(), val_label.clone()), (F::one(), "h2".to_string())], 21 | ) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /proof_of_function_relation/src/t_diag/proof.rs: -------------------------------------------------------------------------------- 1 | use crate::{geo_seq::proof::Proof as GeoSeqProof, non_zero_over_k::proof::Proof as NonZeroProof}; 2 | use ark_ff::PrimeField; 3 | use homomorphic_poly_commit::AdditivelyHomomorphicPCS; 4 | use zero_over_k::zero_over_k::proof::Proof as ZeroProof; 5 | 6 | use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError}; 7 | use ark_std::io::{Read, Write}; 8 | 9 | #[derive(Clone, CanonicalSerialize, CanonicalDeserialize)] 10 | pub struct Proof> { 11 | pub h1_commit: PC::Commitment, 12 | pub h2_commit: PC::Commitment, 13 | pub h1_seq_proof: GeoSeqProof, 14 | pub h2_seq_proof: GeoSeqProof, 15 | pub h_eq_row_m: ZeroProof, 16 | pub row_m_eq_col_m: ZeroProof, 17 | pub val_m_times_h2_proof: ZeroProof, 18 | pub val_plus_h2_proof: NonZeroProof, 19 | } 20 | -------------------------------------------------------------------------------- /proof_of_function_relation/src/t_diag/tests.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod test { 3 | use crate::{error::Error, t_diag::TDiag, util::gen_t_diag_test_polys}; 4 | 5 | use ark_bn254::{Bn254, Fr}; 6 | use ark_poly::{EvaluationDomain, GeneralEvaluationDomain}; 7 | use ark_poly_commit::PolynomialCommitment; 8 | use ark_std::rand::thread_rng; 9 | use blake2::Blake2s; 10 | use fiat_shamir_rng::SimpleHashFiatShamirRng; 11 | use homomorphic_poly_commit::marlin_kzg::KZG10; 12 | use rand_chacha::ChaChaRng; 13 | 14 | type FS = SimpleHashFiatShamirRng; 15 | type F = Fr; 16 | type PC = KZG10; 17 | 18 | #[test] 19 | fn test_diag_matrix() { 20 | let rng = &mut thread_rng(); 21 | let m = 8; 22 | let n = 4; 23 | let t = 2; 24 | 25 | let domain_k = GeneralEvaluationDomain::::new(m).unwrap(); 26 | let domain_h = GeneralEvaluationDomain::::new(n).unwrap(); 27 | 28 | let enforced_degree_bound = domain_k.size() + 1; 29 | let enforced_hiding_bound = 1; 30 | 31 | let polys = gen_t_diag_test_polys( 32 | domain_k, 33 | domain_h, 34 | Some(enforced_degree_bound), 35 | Some(enforced_hiding_bound), 36 | ); 37 | 38 | let row_poly = polys[4].clone(); 39 | let col_poly = polys[5].clone(); 40 | let val_poly = polys[6].clone(); 41 | 42 | let max_degree = 20; 43 | let pp = PC::setup(max_degree, None, rng).unwrap(); 44 | let (ck, vk) = PC::trim(&pp, max_degree, 1, Some(&[2, enforced_degree_bound])).unwrap(); 45 | 46 | let (commitments, rands) = PC::commit( 47 | &ck, 48 | &[row_poly.clone(), col_poly.clone(), val_poly.clone()], 49 | Some(rng), 50 | ) 51 | .unwrap(); 52 | 53 | let proof = TDiag::::prove( 54 | &ck, 55 | t, 56 | &row_poly, 57 | &col_poly, 58 | &val_poly, 59 | &commitments[0], 60 | &commitments[1], 61 | &commitments[2], 62 | &rands[0], 63 | &rands[1], 64 | &rands[2], 65 | Some(enforced_degree_bound), 66 | &domain_k, 67 | &domain_h, 68 | domain_h.size(), 69 | rng, 70 | ) 71 | .unwrap(); 72 | 73 | let is_valid = TDiag::::verify( 74 | &vk, 75 | t, 76 | &commitments[0], 77 | &commitments[1], 78 | &commitments[2], 79 | Some(enforced_degree_bound), 80 | &domain_h, 81 | &domain_k, 82 | domain_h.size(), 83 | proof, 84 | ); 85 | 86 | assert!(is_valid.is_ok()); 87 | } 88 | 89 | #[test] 90 | fn test_diag_matrix_error() { 91 | let rng = &mut thread_rng(); 92 | let m = 8; 93 | let n = 4; 94 | let t = 2; 95 | 96 | let domain_k = GeneralEvaluationDomain::::new(m).unwrap(); 97 | let domain_h = GeneralEvaluationDomain::::new(n).unwrap(); 98 | 99 | let enforced_degree_bound = domain_k.size() + 1; 100 | let enforced_hiding_bound = 1; 101 | 102 | let polys = gen_t_diag_test_polys( 103 | domain_k, 104 | domain_h, 105 | Some(enforced_degree_bound), 106 | Some(enforced_hiding_bound), 107 | ); 108 | 109 | let row_poly = polys[0].clone(); // This will cause an error 110 | let col_poly = polys[0].clone(); 111 | let val_poly = polys[0].clone(); 112 | 113 | let max_degree = 20; 114 | let pp = PC::setup(max_degree, None, rng).unwrap(); 115 | let (ck, _vk) = PC::trim( 116 | &pp, 117 | max_degree, 118 | enforced_hiding_bound, 119 | Some(&[2, enforced_degree_bound]), 120 | ) 121 | .unwrap(); 122 | 123 | let (commitments, rands) = PC::commit( 124 | &ck, 125 | &[row_poly.clone(), col_poly.clone(), val_poly.clone()], 126 | Some(rng), 127 | ) 128 | .unwrap(); 129 | 130 | let proof = TDiag::::prove( 131 | &ck, 132 | t, 133 | &row_poly, 134 | &col_poly, 135 | &val_poly, 136 | &commitments[0], 137 | &commitments[1], 138 | &commitments[2], 139 | &rands[0], 140 | &rands[1], 141 | &rands[2], 142 | Some(enforced_degree_bound), 143 | &domain_k, 144 | &domain_h, 145 | domain_h.size(), 146 | rng, 147 | ); 148 | 149 | assert!(proof.is_err()); 150 | 151 | // Test for a specific error 152 | assert_eq!(proof.err().unwrap(), Error::FEvalIsZero); 153 | } 154 | 155 | #[test] 156 | fn test_reject_wrong_degree() { 157 | let rng = &mut thread_rng(); 158 | let m = 8; 159 | let n = 4; 160 | let t = 2; 161 | 162 | let domain_k = GeneralEvaluationDomain::::new(m).unwrap(); 163 | let domain_h = GeneralEvaluationDomain::::new(n).unwrap(); 164 | 165 | let enforced_degree_bound = domain_k.size() + 1; 166 | let other_degree_bound = domain_k.size() + 5; 167 | let enforced_hiding_bound = 1; 168 | 169 | let polys = gen_t_diag_test_polys( 170 | domain_k, 171 | domain_h, 172 | Some(other_degree_bound), 173 | Some(enforced_hiding_bound), 174 | ); 175 | 176 | let row_poly = polys[4].clone(); 177 | let col_poly = polys[5].clone(); 178 | let val_poly = polys[6].clone(); 179 | 180 | let max_degree = 20; 181 | let pp = PC::setup(max_degree, None, rng).unwrap(); 182 | let (ck, vk) = PC::trim( 183 | &pp, 184 | max_degree, 185 | 1, 186 | Some(&[2, enforced_degree_bound, other_degree_bound]), 187 | ) 188 | .unwrap(); 189 | 190 | let (commitments, rands) = PC::commit( 191 | &ck, 192 | &[row_poly.clone(), col_poly.clone(), val_poly.clone()], 193 | Some(rng), 194 | ) 195 | .unwrap(); 196 | 197 | let proof = TDiag::::prove( 198 | &ck, 199 | t, 200 | &row_poly, 201 | &col_poly, 202 | &val_poly, 203 | &commitments[0], 204 | &commitments[1], 205 | &commitments[2], 206 | &rands[0], 207 | &rands[1], 208 | &rands[2], 209 | Some(other_degree_bound), 210 | &domain_k, 211 | &domain_h, 212 | domain_h.size(), 213 | rng, 214 | ) 215 | .unwrap(); 216 | 217 | let is_valid = TDiag::::verify( 218 | &vk, 219 | t, 220 | &commitments[0], 221 | &commitments[1], 222 | &commitments[2], 223 | Some(enforced_degree_bound), 224 | &domain_h, 225 | &domain_k, 226 | domain_h.size(), 227 | proof, 228 | ); 229 | 230 | assert!(is_valid.is_err()); 231 | 232 | assert_eq!(is_valid.err().unwrap(), Error::BatchCheckError) 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /proof_of_function_relation/src/t_functional_triple/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | error::Error, t_diag::TDiag, t_functional_triple::proof::Proof, 3 | t_strictly_lower_triangular_test::TStrictlyLowerTriangular, 4 | }; 5 | use ark_poly::{univariate::DensePolynomial, EvaluationDomain, GeneralEvaluationDomain}; 6 | use ark_poly_commit::{LabeledCommitment, LabeledPolynomial}; 7 | use rand::Rng; 8 | use std::marker::PhantomData; 9 | 10 | use ark_ff::{PrimeField, SquareRootField}; 11 | use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; 12 | use fiat_shamir_rng::FiatShamirRng; 13 | use homomorphic_poly_commit::AdditivelyHomomorphicPCS; 14 | use std::io::BufReader; 15 | 16 | pub mod proof; 17 | // mod tests; 18 | 19 | pub struct TFT, FS: FiatShamirRng> 20 | { 21 | _field: PhantomData, 22 | _pc: PhantomData, 23 | _digest: PhantomData, 24 | } 25 | 26 | impl TFT 27 | where 28 | F: PrimeField + SquareRootField, 29 | PC: AdditivelyHomomorphicPCS, 30 | FS: FiatShamirRng, 31 | { 32 | pub const PROTOCOL_NAME: &'static [u8] = b"t-FT Test"; 33 | 34 | // TODO: change to use ark-marlin Index. (wait for a new release?) 35 | pub fn prove( 36 | ck: &PC::CommitterKey, 37 | t: usize, 38 | domain_k: &GeneralEvaluationDomain, 39 | domain_h: &GeneralEvaluationDomain, 40 | enforced_degree_bound: Option, 41 | // a 42 | row_a_poly: &LabeledPolynomial>, 43 | col_a_poly: &LabeledPolynomial>, 44 | row_a_commit: &LabeledCommitment, 45 | col_a_commit: &LabeledCommitment, 46 | row_a_random: &PC::Randomness, 47 | col_a_random: &PC::Randomness, 48 | // b 49 | row_b_poly: &LabeledPolynomial>, 50 | col_b_poly: &LabeledPolynomial>, 51 | row_b_commit: &LabeledCommitment, 52 | col_b_commit: &LabeledCommitment, 53 | row_b_random: &PC::Randomness, 54 | col_b_random: &PC::Randomness, 55 | // c 56 | row_c_poly: &LabeledPolynomial>, 57 | col_c_poly: &LabeledPolynomial>, 58 | val_c_poly: &LabeledPolynomial>, 59 | row_c_commit: &LabeledCommitment, 60 | col_c_commit: &LabeledCommitment, 61 | val_c_commit: &LabeledCommitment, 62 | row_c_random: &PC::Randomness, 63 | col_c_random: &PC::Randomness, 64 | val_c_random: &PC::Randomness, 65 | //rands 66 | fs_rng: &mut FS, 67 | rng: &mut R, 68 | ) -> Result, Error> { 69 | // 1. t-SLT test on A 70 | let a_slt_proof = TStrictlyLowerTriangular::::prove( 71 | ck, 72 | t, 73 | domain_k, 74 | domain_h, 75 | row_a_poly, 76 | row_a_commit, 77 | row_a_random, 78 | col_a_poly, 79 | col_a_commit, 80 | col_a_random, 81 | enforced_degree_bound, 82 | fs_rng, 83 | rng, 84 | )?; 85 | 86 | // 2. t-SLT test on B 87 | let b_slt_proof = TStrictlyLowerTriangular::::prove( 88 | ck, 89 | t, 90 | domain_k, 91 | domain_h, 92 | row_b_poly, 93 | row_b_commit, 94 | row_b_random, 95 | col_b_poly, 96 | col_b_commit, 97 | col_b_random, 98 | enforced_degree_bound, 99 | fs_rng, 100 | rng, 101 | )?; 102 | 103 | // 3. t-Diag test on C 104 | let c_diag_proof = TDiag::::prove( 105 | ck, 106 | t, 107 | row_c_poly, 108 | col_c_poly, 109 | val_c_poly, 110 | row_c_commit, 111 | col_c_commit, 112 | val_c_commit, 113 | row_c_random, 114 | col_c_random, 115 | val_c_random, 116 | enforced_degree_bound, 117 | domain_k, 118 | domain_h, 119 | domain_h.size(), 120 | rng, 121 | )?; 122 | 123 | let proof = Proof { 124 | a_slt_proof, 125 | b_slt_proof, 126 | c_diag_proof, 127 | }; 128 | 129 | let mut writer = Vec::::new(); 130 | let _ = proof 131 | .serialize(&mut writer) 132 | .map_err(|_| Error::ProofSerializationError) 133 | .unwrap(); 134 | 135 | Ok(Vec::from(writer.as_slice())) 136 | } 137 | 138 | pub fn verify( 139 | vk: &PC::VerifierKey, 140 | ck: &PC::CommitterKey, 141 | t: usize, 142 | row_a_commitment: &LabeledCommitment, 143 | col_a_commitment: &LabeledCommitment, 144 | row_b_commitment: &LabeledCommitment, 145 | col_b_commitment: &LabeledCommitment, 146 | row_c_commitment: &LabeledCommitment, 147 | col_c_commitment: &LabeledCommitment, 148 | val_c_commitment: &LabeledCommitment, 149 | enforced_degree_bound: Option, 150 | domain_h: &GeneralEvaluationDomain, 151 | domain_k: &GeneralEvaluationDomain, 152 | proof_bytes: Vec, 153 | fs_rng: &mut FS, 154 | ) -> Result<(), Error> { 155 | let reader = BufReader::new(proof_bytes.as_slice()); 156 | let proof: Proof = Proof::::deserialize(reader).unwrap(); 157 | 158 | TStrictlyLowerTriangular::::verify( 159 | vk, 160 | ck, 161 | t, 162 | domain_k, 163 | domain_h, 164 | row_a_commitment, 165 | col_a_commitment, 166 | enforced_degree_bound, 167 | proof.a_slt_proof, 168 | fs_rng, 169 | )?; 170 | 171 | TStrictlyLowerTriangular::::verify( 172 | vk, 173 | ck, 174 | t, 175 | domain_k, 176 | domain_h, 177 | row_b_commitment, 178 | col_b_commitment, 179 | enforced_degree_bound, 180 | proof.b_slt_proof, 181 | fs_rng, 182 | )?; 183 | 184 | TDiag::::verify( 185 | vk, 186 | t, 187 | row_c_commitment, 188 | col_c_commitment, 189 | val_c_commitment, 190 | enforced_degree_bound, 191 | domain_h, 192 | domain_k, 193 | domain_h.size(), 194 | proof.c_diag_proof, 195 | )?; 196 | 197 | Ok(()) 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /proof_of_function_relation/src/t_functional_triple/proof.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | t_diag::proof::Proof as TDiagProof, t_strictly_lower_triangular_test::proof::Proof as TSLTProof, 3 | }; 4 | use ark_ff::{PrimeField, SquareRootField}; 5 | use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError}; 6 | use ark_std::io::{Read, Write}; 7 | use homomorphic_poly_commit::AdditivelyHomomorphicPCS; 8 | 9 | #[derive(Clone, CanonicalSerialize, CanonicalDeserialize)] 10 | pub struct Proof> { 11 | pub a_slt_proof: TSLTProof, 12 | pub b_slt_proof: TSLTProof, 13 | pub c_diag_proof: TDiagProof, 14 | } 15 | -------------------------------------------------------------------------------- /proof_of_function_relation/src/t_strictly_lower_triangular_test/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | discrete_log_comparison::DLComparison, 3 | error::{to_pc_error, Error}, 4 | geo_seq::GeoSeqTest, 5 | subset_over_k::SubsetOverK, 6 | t_strictly_lower_triangular_test::proof::Proof, 7 | util::generate_sequence, 8 | }; 9 | use ark_ff::{to_bytes, PrimeField, SquareRootField}; 10 | use ark_poly::{ 11 | univariate::DensePolynomial, EvaluationDomain, GeneralEvaluationDomain, UVPolynomial, 12 | }; 13 | use ark_poly_commit::{LabeledCommitment, LabeledPolynomial}; 14 | use fiat_shamir_rng::FiatShamirRng; 15 | use homomorphic_poly_commit::AdditivelyHomomorphicPCS; 16 | use rand::Rng; 17 | use std::marker::PhantomData; 18 | 19 | pub mod proof; 20 | mod tests; 21 | 22 | pub struct TStrictlyLowerTriangular< 23 | F: PrimeField + SquareRootField, 24 | PC: AdditivelyHomomorphicPCS, 25 | FS: FiatShamirRng, 26 | > { 27 | _field: PhantomData, 28 | _pc: PhantomData, 29 | _fs: PhantomData, 30 | } 31 | 32 | impl TStrictlyLowerTriangular 33 | where 34 | F: PrimeField + SquareRootField, 35 | PC: AdditivelyHomomorphicPCS, 36 | FS: FiatShamirRng, 37 | { 38 | pub const PROTOCOL_NAME: &'static [u8] = b"t-Strictly Lower Triangular Test"; 39 | 40 | pub fn prove( 41 | ck: &PC::CommitterKey, 42 | t: usize, 43 | domain_k: &GeneralEvaluationDomain, 44 | domain_h: &GeneralEvaluationDomain, 45 | row_poly: &LabeledPolynomial>, 46 | row_commit: &LabeledCommitment, 47 | row_random: &PC::Randomness, 48 | col_poly: &LabeledPolynomial>, 49 | col_commit: &LabeledCommitment, 50 | col_random: &PC::Randomness, 51 | enforced_degree_bound: Option, 52 | fs_rng: &mut FS, 53 | rng: &mut R, 54 | ) -> Result, Error> { 55 | let fs_bytes = &to_bytes![&Self::PROTOCOL_NAME].map_err(|_| Error::ToBytesError)?; 56 | fs_rng.absorb(fs_bytes); 57 | 58 | let r = domain_h.element(1); 59 | 60 | if t > domain_h.size() { 61 | return Err(Error::T2Large); 62 | } 63 | 64 | // Step 1: interpolate h 65 | let mut a_s = vec![domain_h.element(t)]; 66 | let mut c_s = vec![domain_h.size() - t]; 67 | 68 | let to_pad = domain_k.size() - (domain_h.size() - t); 69 | if to_pad > 0 { 70 | a_s.push(F::zero()); 71 | c_s.push(to_pad); 72 | } 73 | 74 | let seq = generate_sequence::(r, &a_s.as_slice(), &c_s.as_slice()); 75 | let h = DensePolynomial::::from_coefficients_slice(&domain_k.ifft(&seq)); 76 | let h = LabeledPolynomial::new(String::from("h"), h, enforced_degree_bound, Some(1)); 77 | 78 | let (commitment, rands) = 79 | PC::commit(&ck, &[h.clone()], Some(rng)).map_err(to_pc_error::)?; 80 | 81 | let h_commit = commitment[0].clone(); 82 | 83 | // Step 2: Geometric sequence test on h 84 | let geo_seq_proof = GeoSeqTest::::prove( 85 | ck, r, &h, &h_commit, &rands[0], &a_s, &c_s, domain_k, rng, 86 | )?; 87 | 88 | // Step 3: Subset over K between row_M and h 89 | let subset_proof = SubsetOverK::::prove(); 90 | 91 | // Step 4: Discrete Log Comparison between row_M and col_M 92 | let dl_proof = DLComparison::::prove( 93 | ck, 94 | domain_k, 95 | domain_h, 96 | row_poly, 97 | row_commit, 98 | row_random, 99 | col_poly, 100 | col_commit, 101 | col_random, 102 | enforced_degree_bound, 103 | fs_rng, 104 | rng, 105 | )?; 106 | 107 | let proof = Proof { 108 | h_commit: h_commit.commitment().clone(), 109 | dl_proof, 110 | geo_seq_proof, 111 | subset_proof, 112 | }; 113 | 114 | Ok(proof) 115 | } 116 | 117 | pub fn verify( 118 | vk: &PC::VerifierKey, 119 | ck: &PC::CommitterKey, 120 | t: usize, 121 | domain_k: &GeneralEvaluationDomain, 122 | domain_h: &GeneralEvaluationDomain, 123 | row_commit: &LabeledCommitment, 124 | col_commit: &LabeledCommitment, 125 | enforced_degree_bound: Option, 126 | proof: Proof, 127 | fs_rng: &mut FS, 128 | ) -> Result<(), Error> { 129 | // re-label the oracle commitments with the enforced degree bound 130 | let row_commit = LabeledCommitment::new( 131 | row_commit.label().clone(), 132 | row_commit.commitment().clone(), 133 | enforced_degree_bound, 134 | ); 135 | let col_commit = LabeledCommitment::new( 136 | col_commit.label().clone(), 137 | col_commit.commitment().clone(), 138 | enforced_degree_bound, 139 | ); 140 | 141 | // Step 2: Geometric sequence test on h 142 | let mut a_s = vec![domain_h.element(t)]; 143 | let mut c_s = vec![domain_h.size() - t]; 144 | 145 | let to_pad = domain_k.size() - (domain_h.size() - t); 146 | if to_pad > 0 { 147 | a_s.push(F::zero()); 148 | c_s.push(to_pad); 149 | } 150 | 151 | let h_commit = 152 | LabeledCommitment::new(String::from("h"), proof.h_commit, enforced_degree_bound); 153 | 154 | GeoSeqTest::::verify( 155 | domain_h.element(1), 156 | &a_s, 157 | &c_s, 158 | domain_k, 159 | &h_commit, 160 | enforced_degree_bound, 161 | proof.geo_seq_proof, 162 | vk, 163 | )?; 164 | 165 | // Step 3: Subset over K between row_M and h 166 | SubsetOverK::::verify(proof.subset_proof)?; 167 | 168 | // Step 4: Discrete Log Comparison between row_M and col_M 169 | DLComparison::::verify( 170 | vk, 171 | ck, 172 | domain_k, 173 | domain_h, 174 | &row_commit, 175 | &col_commit, 176 | enforced_degree_bound, 177 | proof.dl_proof, 178 | fs_rng, 179 | )?; 180 | 181 | Ok(()) 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /proof_of_function_relation/src/t_strictly_lower_triangular_test/proof.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | discrete_log_comparison::proof::Proof as DLProof, geo_seq::proof::Proof as GeoSeqProof, 3 | subset_over_k::proof::Proof as SubsetProof, 4 | }; 5 | use ark_ff::{PrimeField, SquareRootField}; 6 | use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError}; 7 | use ark_std::io::{Read, Write}; 8 | use homomorphic_poly_commit::AdditivelyHomomorphicPCS; 9 | 10 | #[derive(Clone, CanonicalSerialize, CanonicalDeserialize)] 11 | pub struct Proof 12 | where 13 | F: PrimeField + SquareRootField, 14 | PC: AdditivelyHomomorphicPCS, 15 | { 16 | pub h_commit: PC::Commitment, 17 | pub dl_proof: DLProof, 18 | pub geo_seq_proof: GeoSeqProof, 19 | pub subset_proof: SubsetProof, 20 | } 21 | -------------------------------------------------------------------------------- /proof_of_function_relation/src/util.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::{FftField, Field, PrimeField}; 2 | use ark_poly::{ 3 | univariate::DensePolynomial, EvaluationDomain, GeneralEvaluationDomain, UVPolynomial, 4 | }; 5 | use ark_poly_commit::LabeledPolynomial; 6 | 7 | #[inline] 8 | pub fn powers_of(scalar: F) -> impl Iterator 9 | where 10 | F: Field, 11 | { 12 | core::iter::successors(Some(F::one()), move |p| Some(*p * scalar)) 13 | } 14 | 15 | /// Generates the concatenation of geometric sequences that all share a common ratio 16 | pub fn generate_sequence( 17 | common_ratio: F, 18 | initial_terms: &[F], 19 | subsequence_lengths: &[usize], 20 | ) -> Vec { 21 | assert_ne!(initial_terms.len(), 0); 22 | assert_eq!(subsequence_lengths.len(), initial_terms.len()); 23 | 24 | let mut concatenation = Vec::::default(); 25 | 26 | for (i, term) in initial_terms.iter().enumerate() { 27 | for ratio_power in powers_of(common_ratio).take(subsequence_lengths[i]) { 28 | let val = *term * ratio_power; 29 | concatenation.push(val); 30 | } 31 | } 32 | 33 | concatenation 34 | } 35 | 36 | pub fn gen_t_diag_test_polys( 37 | domain_k: GeneralEvaluationDomain, 38 | domain_h: GeneralEvaluationDomain, 39 | degree_bound: Option, 40 | hiding_bound: Option, 41 | ) -> Vec>> { 42 | // M indices 43 | /* 44 | 00, 01, 02, 03 45 | 10, 11, 12, 13 46 | 20, 21, 22, 23 47 | 30, 31, 32, 33 48 | */ 49 | 50 | // A (slt), t = 2 51 | /* 52 | 0, 0, 0, 0 53 | 1, 0, 0, 0 54 | 1, 2, 0, 0 55 | 0, 3, 0, 0 56 | */ 57 | 58 | // rowM and colM are vectors that encode position of each non-zero element 59 | 60 | // domain = 1, gamma, gamma^2 gamma^3 gamma^4 gamma^5 gamma^6 gamma^7 61 | // rowA_evals = w^1 w^2 w^2 w^3 w^3 w^3 w^3 w^3 62 | // colA_evals = w^0 w^0 w^1 w^1 w^1 w^1 w^1 w^1 63 | 64 | // B (stl), t = 1 65 | /* 66 | 0, 0, 0, 0 67 | 7, 0, 0, 0 68 | 0, 0, 0, 0 69 | 2, 2, 0, 0 70 | */ 71 | 72 | // rowM and colM are vectors that encode position of each non-zero element 73 | 74 | // domain = 1, gamma, gamma^2 gamma^3 gamma^4 gamma^5 gamma^6 gamma^7 75 | // rowB_evals = w^1 w^3 w^3 w^3 w^3 w^3 w^3 w^3 76 | // colB_evals = w^0 w^0 w^1 w^1 w^1 w^1 w^1 w^1 77 | 78 | // C (diag) 79 | /* 80 | 0, 0, 0, 0 81 | 0, 0, 0, 0 82 | 0, 0, 2, 0 83 | 0, 0, 0, 2 84 | */ 85 | 86 | // rowM and colM are vectors that encode position of each non-zero element 87 | 88 | // domain = 1, gamma, gamma^2 gamma^3 gamma^4 gamma^5 gamma^6 gamma^7 89 | // rowC_evals = w^2 w^3 w^0 w^0 w^0 w^0 w^0 w^0 90 | // colC_evals = w^2 w^3 w^0 w^0 w^0 w^0 w^0 w^0 91 | // vaLC_evals = 2 2 0 0 0 0 0 0 92 | 93 | let omega_0 = domain_h.element(0); 94 | let omega_1 = domain_h.element(1); 95 | let omega_2 = domain_h.element(2); 96 | let omega_3 = domain_h.element(3); 97 | 98 | let row_a_evals = vec![ 99 | omega_1, omega_2, omega_2, omega_3, omega_3, omega_3, omega_3, omega_3, 100 | ]; 101 | let col_a_evals = vec![ 102 | omega_0, omega_0, omega_1, omega_1, omega_1, omega_1, omega_1, omega_1, 103 | ]; 104 | 105 | let row_b_evals = vec![ 106 | omega_1, omega_3, omega_3, omega_3, omega_3, omega_3, omega_3, omega_3, 107 | ]; 108 | let col_b_evals = vec![ 109 | omega_0, omega_0, omega_1, omega_1, omega_1, omega_1, omega_1, omega_1, 110 | ]; 111 | 112 | let row_c_evals = vec![ 113 | omega_2, omega_3, omega_0, omega_0, omega_0, omega_0, omega_0, omega_0, 114 | ]; 115 | let col_c_evals = vec![ 116 | omega_2, omega_3, omega_0, omega_0, omega_0, omega_0, omega_0, omega_0, 117 | ]; 118 | 119 | let val_c_evals = vec![ 120 | F::from(2u64), 121 | F::from(2u64), 122 | F::from(0u64), 123 | F::from(0u64), 124 | F::from(0u64), 125 | F::from(0u64), 126 | F::from(0u64), 127 | F::from(0u64), 128 | ]; 129 | 130 | let row_a_poly = DensePolynomial::::from_coefficients_slice(&domain_k.ifft(&row_a_evals)); 131 | let col_a_poly = DensePolynomial::::from_coefficients_slice(&domain_k.ifft(&col_a_evals)); 132 | let row_a_poly = LabeledPolynomial::new( 133 | String::from("row_a_poly"), 134 | row_a_poly, 135 | degree_bound, 136 | hiding_bound, 137 | ); 138 | let col_a_poly = LabeledPolynomial::new( 139 | String::from("col_a_poly"), 140 | col_a_poly, 141 | degree_bound, 142 | hiding_bound, 143 | ); 144 | 145 | let row_b_poly = DensePolynomial::::from_coefficients_slice(&domain_k.ifft(&row_b_evals)); 146 | let col_b_poly = DensePolynomial::::from_coefficients_slice(&domain_k.ifft(&col_b_evals)); 147 | let row_b_poly = LabeledPolynomial::new( 148 | String::from("row_b_poly"), 149 | row_b_poly, 150 | degree_bound, 151 | hiding_bound, 152 | ); 153 | let col_b_poly = LabeledPolynomial::new( 154 | String::from("col_b_poly"), 155 | col_b_poly, 156 | degree_bound, 157 | hiding_bound, 158 | ); 159 | 160 | let row_c_poly = DensePolynomial::::from_coefficients_slice(&domain_k.ifft(&row_c_evals)); 161 | let col_c_poly = DensePolynomial::::from_coefficients_slice(&domain_k.ifft(&col_c_evals)); 162 | let val_c_poly = DensePolynomial::::from_coefficients_slice(&domain_k.ifft(&val_c_evals)); 163 | let row_c_poly = LabeledPolynomial::new( 164 | String::from("row_c_poly"), 165 | row_c_poly, 166 | degree_bound, 167 | hiding_bound, 168 | ); 169 | let col_c_poly = LabeledPolynomial::new( 170 | String::from("col_c_poly"), 171 | col_c_poly, 172 | degree_bound, 173 | hiding_bound, 174 | ); 175 | let val_c_poly = LabeledPolynomial::new( 176 | String::from("val_c_poly"), 177 | val_c_poly, 178 | degree_bound, 179 | hiding_bound, 180 | ); 181 | 182 | vec![ 183 | row_a_poly, col_a_poly, row_b_poly, col_b_poly, row_c_poly, col_c_poly, val_c_poly, 184 | ] 185 | } 186 | -------------------------------------------------------------------------------- /zero_over_k/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zero_over_k" 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 | ark-poly-commit = "0.3.0" 10 | ark-ff = "0.3.0" 11 | ark-poly = "0.3.0" 12 | ark-ec = "0.3.0" 13 | ark-std = "0.3.0" 14 | ark-marlin = "0.3.0" 15 | ark-serialize = "0.3.0" 16 | rand_core = {version = "0.6", default-features=false, features = ["getrandom"] } 17 | ark-bn254 = "0.3.0" 18 | rand = "0.8.4" 19 | blake2 = { version = "0.9", default-features = false } 20 | digest = { version = "0.9" } 21 | homomorphic_poly_commit = { path = "../homomorphic_poly_commit" } 22 | fiat_shamir_rng = { path = "../fiat_shamir_rng"} 23 | rand_chacha = { version = "0.3.0", default-features = false } -------------------------------------------------------------------------------- /zero_over_k/src/error.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, PartialEq)] 2 | pub enum Error { 3 | // In zero_over_k 4 | BatchCheckError, 5 | 6 | // In zero_over_k 7 | Check1Failed, 8 | Check2Failed, 9 | 10 | // In non_zero_over_k 11 | FEvalIsZero, 12 | FPrimeEvalError, 13 | 14 | // In zero_over_k, 15 | ToBytesError, 16 | 17 | PCError { 18 | error: String, 19 | }, 20 | 21 | /// A commitment could not be found when evaluating a linear combination 22 | MissingCommitment(String), 23 | InputLengthError(String), 24 | MismatchedDegreeBounds(String), 25 | 26 | UnsupportedDegree(String), 27 | 28 | VOFailedToInstantiate, 29 | VOFailedToCompute, 30 | } 31 | 32 | /// Convert an ark_poly_commit error 33 | pub fn to_pc_error(error: PC::Error) -> Error 34 | where 35 | F: ark_ff::Field, 36 | PC: ark_poly_commit::PolynomialCommitment>, 37 | { 38 | println!("Polynomial Commitment Error: {:?}", error); 39 | Error::PCError { 40 | error: format!("Polynomial Commitment Error: {:?}", error), 41 | } 42 | } 43 | 44 | impl From for Error { 45 | fn from(err: homomorphic_poly_commit::error::Error) -> Self { 46 | Self::PCError { 47 | error: format!("{:?}", err), 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /zero_over_k/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod error; 2 | pub mod util; 3 | pub mod virtual_oracle; 4 | pub mod zero_over_k; 5 | -------------------------------------------------------------------------------- /zero_over_k/src/util.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::Field; 2 | use ark_poly::{univariate::DensePolynomial, UVPolynomial}; 3 | use ark_std::UniformRand; 4 | use rand::Rng; 5 | 6 | pub fn powers_of(scalar: F) -> impl Iterator 7 | where 8 | F: Field, 9 | { 10 | core::iter::successors(Some(F::one()), move |p| Some(*p * scalar)) 11 | } 12 | 13 | /// Extract the labels from a list of labeled elements 14 | #[macro_export] 15 | macro_rules! get_labels { 16 | ($vector:expr) => { 17 | $vector 18 | .iter() 19 | .map(|f| f.label().clone()) 20 | .collect::>() 21 | }; 22 | } 23 | 24 | pub fn shift_dense_poly( 25 | p: &DensePolynomial, 26 | shifting_factor: &F, 27 | ) -> DensePolynomial { 28 | if *shifting_factor == F::one() { 29 | return p.clone(); 30 | } 31 | 32 | let mut coeffs = p.coeffs().to_vec(); 33 | let mut acc = F::one(); 34 | for i in 0..coeffs.len() { 35 | coeffs[i] = coeffs[i] * acc; 36 | acc *= shifting_factor; 37 | } 38 | 39 | DensePolynomial::from_coefficients_vec(coeffs) 40 | } 41 | 42 | /// Sample a vector of random elements of type T 43 | pub fn sample_vector(seed: &mut R, length: usize) -> Vec { 44 | (0..length) 45 | .collect::>() 46 | .iter() 47 | .map(|_| T::rand(seed)) 48 | .collect::>() 49 | } 50 | -------------------------------------------------------------------------------- /zero_over_k/src/virtual_oracle/generic_shifting_vo/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Error; 2 | use crate::util::shift_dense_poly; 3 | use crate::virtual_oracle::generic_shifting_vo::vo_term::VOTerm; 4 | use ark_ff::Field; 5 | use ark_poly::{univariate::DensePolynomial, UVPolynomial}; 6 | use ark_poly_commit::{Evaluations, PolynomialLabel, QuerySet}; 7 | 8 | use super::VirtualOracle; 9 | 10 | pub mod presets; 11 | mod tests; 12 | pub mod vo_term; 13 | 14 | /// A virtual oracle which shifts the concrete oracles and combines them according to a user-defined function. 15 | /// The function can be specified as a function or a closure if one needs to capture environment variables. Presets are 16 | /// available in this crate. See equation (2) in https://eprint.iacr.org/2021/1342 for an explicit definition. 17 | pub struct GenericShiftingVO 18 | where 19 | F: Field, 20 | T: Fn(&[VOTerm]) -> VOTerm, 21 | { 22 | mapping_vector: Vec, 23 | shifting_coefficients: Vec, 24 | combine_function: T, 25 | minimum_oracle_length: usize, 26 | } 27 | 28 | impl GenericShiftingVO 29 | where 30 | F: Field, 31 | T: Fn(&[VOTerm]) -> VOTerm, 32 | { 33 | /// Constructor for an input-shifting virtual oracle 34 | pub fn new( 35 | mapping_vector: &[usize], 36 | shifting_coefficients: &[F], 37 | combine_function: T, 38 | ) -> Result { 39 | let number_of_user_oracles = mapping_vector.len(); 40 | 41 | if shifting_coefficients.len() != number_of_user_oracles { 42 | return Err(Error::InputLengthError(String::from( 43 | "mapping vector and shifting coefficients do not match", 44 | ))); 45 | } 46 | 47 | let max_index = mapping_vector 48 | .iter() 49 | .max() 50 | .expect("mapping vector is empty") 51 | .clone(); 52 | 53 | let minimum_oracle_length = max_index; 54 | 55 | Ok(Self { 56 | mapping_vector: mapping_vector.to_vec(), 57 | shifting_coefficients: shifting_coefficients.to_vec(), 58 | combine_function, 59 | minimum_oracle_length, 60 | }) 61 | } 62 | 63 | /// Returns the polynomial that results from the combination of the given concrete oracles 64 | pub fn compute_polynomial( 65 | &self, 66 | concrete_oracles: &[ark_poly_commit::LabeledPolynomial>], 67 | ) -> Result, Error> { 68 | self.check_conrete_oracle_length(concrete_oracles.len())?; 69 | 70 | let x_poly = DensePolynomial::from_coefficients_slice(&[F::zero(), F::one()]); 71 | 72 | // Put the indeterminate variable X as terms[0] 73 | let mut terms: Vec> = vec![VOTerm::Polynomial(x_poly)]; 74 | 75 | // For each item in the mapping vector, we select the corresponding concrete oracle, apply the desired 76 | // shift and push the resulting polynomial as a term. 77 | self.mapping_vector 78 | .iter() 79 | .enumerate() 80 | .for_each(|(term_index, &mapped_index)| { 81 | let shifted = shift_dense_poly( 82 | &concrete_oracles[mapped_index], 83 | &self.shifting_coefficients[term_index], 84 | ); 85 | terms.push(VOTerm::Polynomial(shifted)) 86 | }); 87 | 88 | let combined = (self.combine_function)(&terms); 89 | match combined { 90 | VOTerm::Evaluation(_) => Err(Error::VOFailedToInstantiate), 91 | VOTerm::Polynomial(poly) => Ok(poly), 92 | } 93 | } 94 | 95 | /// Check that enough oracles were provided. 96 | fn check_conrete_oracle_length(&self, input_length: usize) -> Result<(), Error> { 97 | if input_length < self.minimum_oracle_length { 98 | return Err(Error::InputLengthError(format!( 99 | "Mapping vector requires {} oracles/evaluations but only {} were provided", 100 | self.minimum_oracle_length, input_length 101 | ))); 102 | } 103 | Ok(()) 104 | } 105 | } 106 | 107 | impl VirtualOracle for GenericShiftingVO 108 | where 109 | F: Field, 110 | T: Fn(&[VOTerm]) -> VOTerm, 111 | { 112 | fn generate_query_set( 113 | &self, 114 | concrete_oracle_labels: &[PolynomialLabel], 115 | query_point: &(String, F), 116 | ) -> Result, Error> { 117 | self.check_conrete_oracle_length(concrete_oracle_labels.len())?; 118 | 119 | let mut query_set = QuerySet::new(); 120 | 121 | // for each term, extract the concrete oracle's label, choose the desired eval point (based on the query point and alpha), 122 | // and create a label for this point. Insert the resulting tuple into the query set. 123 | self.mapping_vector 124 | .iter() 125 | .enumerate() 126 | .for_each(|(term_index, &mapped_index)| { 127 | let poly_label = concrete_oracle_labels[mapped_index].clone(); 128 | let eval_point = self.shifting_coefficients[term_index] * query_point.1; 129 | let point_label = format!("{}_times_alpha{}", query_point.0, term_index); 130 | 131 | query_set.insert((poly_label, (point_label, eval_point))); 132 | }); 133 | 134 | Ok(query_set) 135 | } 136 | 137 | fn evaluate_from_concrete_evals( 138 | &self, 139 | concrete_oracle_labels: &[PolynomialLabel], 140 | eval_point: &F, 141 | evaluations: &Evaluations, 142 | ) -> Result { 143 | let mut terms: Vec> = self 144 | .mapping_vector 145 | .iter() 146 | .enumerate() 147 | .map(|(term_index, &mapped_index)| { 148 | let poly_label = concrete_oracle_labels[mapped_index].clone(); 149 | let shifted_eval_point = self.shifting_coefficients[term_index] * eval_point; 150 | let key = (poly_label, shifted_eval_point); 151 | 152 | VOTerm::Evaluation( 153 | evaluations 154 | .get(&key) 155 | .expect("Missing a concrete oracle evaluation for VO computation") 156 | .clone(), 157 | ) 158 | }) 159 | .collect(); 160 | 161 | terms.insert(0, VOTerm::Evaluation(eval_point.clone())); 162 | 163 | let combined = (self.combine_function)(&terms); 164 | match combined { 165 | VOTerm::Evaluation(eval) => Ok(eval), 166 | VOTerm::Polynomial(_) => Err(Error::VOFailedToCompute), 167 | } 168 | } 169 | 170 | fn mapping_vector(&self) -> Vec { 171 | self.mapping_vector.clone() 172 | } 173 | 174 | fn shifting_coefficients(&self) -> Vec { 175 | self.shifting_coefficients.clone() 176 | } 177 | 178 | fn apply_evaluation_function(&self, terms: &[VOTerm]) -> VOTerm { 179 | (self.combine_function)(terms) 180 | } 181 | 182 | fn num_of_variable_terms(&self) -> usize { 183 | self.mapping_vector.len() 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /zero_over_k/src/virtual_oracle/generic_shifting_vo/presets.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::{FftField, Field}; 2 | 3 | use crate::vo_constant; 4 | 5 | use super::vo_term::VOTerm; 6 | 7 | /// A function to be used in a virtual oracle. Should the VO evaluate to 0 for all points in a domain K, 8 | /// we can conclude that terms[1] and terms[2] are equal for all points in the K 9 | pub fn equality_check(terms: &[VOTerm]) -> VOTerm { 10 | terms[1].clone() - terms[2].clone() 11 | } 12 | 13 | /// A function to be used in a virtual oracle. Should the VO evaluate to 0 for all points in a domain K, 14 | /// we can conclude that terms[1] is the inverse of terms[2] for all points in K 15 | pub fn inverse_check(terms: &[VOTerm]) -> VOTerm { 16 | terms[1].clone() * terms[2].clone() - vo_constant!(F::one()) 17 | } 18 | 19 | /// A function to be used in a virtual oracle. Should the VO evaluate to 0 for all points in a domain K, 20 | /// we can conclude that terms[1] is the square-root of terms[2] for all points in K 21 | pub fn square_check(terms: &[VOTerm]) -> VOTerm { 22 | terms[1].clone() - terms[2].clone() * terms[2].clone() 23 | } 24 | 25 | /// A function to be used in a virtual oracle. This function can be used to check that the product of 26 | /// terms[1] and terms[2] is 0 over a domain K 27 | pub fn zero_product_check(terms: &[VOTerm]) -> VOTerm { 28 | terms[1].clone() * terms[2].clone() 29 | } 30 | 31 | /// A function to be used in a virtual oracle. This function can be used to check that terms[1] agrees with 32 | /// the product of terms[2] and terms[3] over a domain K 33 | pub fn abc_product_check(terms: &[VOTerm]) -> VOTerm { 34 | terms[1].clone() - terms[2].clone() * terms[3].clone() 35 | } 36 | 37 | /// A closure to be used in a geometric sequence test virtual oracle. 38 | /// This specific construction expects terms[0] to be X (as enforced by default), terms[1] to be f(X) and 39 | /// terms[2] to be f(gamma*X); where X is an indeterminate variable, f is a polynomial for which we make 40 | /// a geometric sequence claim and gamma is the generator of the subgroup of interest. 41 | #[macro_export] 42 | macro_rules! geometric_seq_check { 43 | ($common_ratio:expr, $sequence_lengths:expr, $domain:expr) => { 44 | |terms: &[VOTerm]| { 45 | // construct (f(gamma * X) - r * f(X)) 46 | let check_next_term = terms[2].clone() - vo_constant!($common_ratio) * terms[1].clone(); 47 | 48 | let mut evaluation_function = check_next_term; 49 | let mut current_sequence_starting_index = 0; 50 | // construct each stitch and multiply to the final polynomial 51 | $sequence_lengths 52 | .iter() 53 | .for_each(|current_sequence_length| { 54 | let stitch = terms[0].clone() 55 | - vo_constant!($domain.element(1).pow([(current_sequence_starting_index 56 | + current_sequence_length 57 | - 1) 58 | as u64])); 59 | current_sequence_starting_index += current_sequence_length; 60 | evaluation_function = evaluation_function.clone() * stitch; 61 | }); 62 | 63 | evaluation_function 64 | } 65 | }; 66 | } 67 | -------------------------------------------------------------------------------- /zero_over_k/src/virtual_oracle/generic_shifting_vo/tests.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod test { 3 | use crate::util::sample_vector; 4 | use crate::virtual_oracle::generic_shifting_vo::presets; 5 | use crate::virtual_oracle::VirtualOracle; 6 | use crate::{error::to_pc_error, zero_over_k::ZeroOverK}; 7 | use crate::{error::Error, util::shift_dense_poly, vo_constant}; 8 | use ark_bn254::{Bn254, Fr}; 9 | use ark_ff::{Field, One, UniformRand}; 10 | use ark_poly::{ 11 | univariate::DensePolynomial, EvaluationDomain, Evaluations, GeneralEvaluationDomain, 12 | Polynomial, UVPolynomial, 13 | }; 14 | use ark_poly_commit::{evaluate_query_set, LabeledPolynomial, PolynomialCommitment}; 15 | use ark_std::{rand::thread_rng, test_rng}; 16 | use blake2::Blake2s; 17 | use fiat_shamir_rng::SimpleHashFiatShamirRng; 18 | use homomorphic_poly_commit::marlin_kzg::KZG10; 19 | use rand_chacha::ChaChaRng; 20 | 21 | use super::super::{GenericShiftingVO, VOTerm}; 22 | 23 | type F = Fr; 24 | type PC = KZG10; 25 | type FS = SimpleHashFiatShamirRng; 26 | 27 | pub fn simple_addition(terms: &[VOTerm]) -> VOTerm { 28 | terms[1].clone() + vo_constant!(F::from(2u64)) * terms[2].clone() 29 | } 30 | 31 | pub fn simple_mul(concrete_terms: &[VOTerm]) -> VOTerm { 32 | concrete_terms[1].clone() * concrete_terms[2].clone() 33 | } 34 | 35 | pub fn harder_addition(concrete_terms: &[VOTerm]) -> VOTerm { 36 | concrete_terms[0].clone() 37 | + vo_constant!(F::from(2u64)) * concrete_terms[2].clone() 38 | + vo_constant!(F::from(3u64)) * concrete_terms[3].clone() 39 | } 40 | 41 | #[test] 42 | fn test_add_oracle() { 43 | let rng = &mut thread_rng(); 44 | let a_poly = LabeledPolynomial::new( 45 | String::from("a"), 46 | DensePolynomial::::rand(4, rng), 47 | None, 48 | None, 49 | ); 50 | let b_poly = LabeledPolynomial::new( 51 | String::from("b"), 52 | DensePolynomial::::rand(4, rng), 53 | None, 54 | None, 55 | ); 56 | let c_poly = LabeledPolynomial::new( 57 | String::from("c"), 58 | DensePolynomial::::rand(4, rng), 59 | None, 60 | None, 61 | ); 62 | let concrete_oracles = &[a_poly.clone(), b_poly.clone(), c_poly.clone()]; 63 | 64 | let shifting_coefficients: Vec = sample_vector(rng, 2); 65 | let mapping_vector = vec![2, 0]; 66 | 67 | // Compute the expected polynomial 68 | let shifted_c = shift_dense_poly(&c_poly, &shifting_coefficients[0]); 69 | let shifted_a = shift_dense_poly(&a_poly, &shifting_coefficients[1]); 70 | let two_constant_poly = DensePolynomial::from_coefficients_vec(vec![F::from(2u64)]); 71 | let expected = &shifted_c + &(&two_constant_poly * &shifted_a); 72 | 73 | let add_oracle = 74 | GenericShiftingVO::new(&mapping_vector, &shifting_coefficients, simple_addition) 75 | .unwrap(); 76 | 77 | // Check that we get the right polynomial 78 | let sum = add_oracle.compute_polynomial(concrete_oracles).unwrap(); 79 | assert_eq!(expected, sum); 80 | } 81 | 82 | #[test] 83 | fn test_mul_oracle() { 84 | let rng = &mut thread_rng(); 85 | let a_poly = LabeledPolynomial::new( 86 | String::from("a"), 87 | DensePolynomial::::rand(4, rng), 88 | None, 89 | None, 90 | ); 91 | let b_poly = LabeledPolynomial::new( 92 | String::from("b"), 93 | DensePolynomial::::rand(4, rng), 94 | None, 95 | None, 96 | ); 97 | let c_poly = LabeledPolynomial::new( 98 | String::from("c"), 99 | DensePolynomial::::rand(4, rng), 100 | None, 101 | None, 102 | ); 103 | 104 | let concrete_oracles = &[a_poly.clone(), b_poly.clone(), c_poly.clone()]; 105 | 106 | let shifting_coefficients: Vec = sample_vector(rng, 2); 107 | let mapping_vector = vec![2, 0]; 108 | 109 | // Compute the expected polynomial 110 | let shifted_c = shift_dense_poly(&c_poly, &shifting_coefficients[0]); 111 | let shifted_a = shift_dense_poly(&a_poly, &shifting_coefficients[1]); 112 | let expected = &shifted_c * &shifted_a; 113 | 114 | let mul_oracle = 115 | GenericShiftingVO::new(&mapping_vector, &shifting_coefficients, simple_mul).unwrap(); 116 | 117 | // Check that we get the right polynomial 118 | let prod = mul_oracle.compute_polynomial(concrete_oracles).unwrap(); 119 | assert_eq!(expected, prod); 120 | } 121 | 122 | #[test] 123 | fn test_short_input_vec() { 124 | // mapping vector expects there to be a concrete oracle with index 1; effectively expected at last 2 concrete oracles 125 | let mapping_vector = vec![2]; 126 | let shift_coefficients = vec![F::one()]; 127 | let add_oracle = 128 | GenericShiftingVO::new(&mapping_vector, &shift_coefficients, simple_addition).unwrap(); 129 | 130 | // We only provide one concrete oracle 131 | let default_labeled = LabeledPolynomial::new( 132 | String::from(""), 133 | DensePolynomial::::default(), 134 | None, 135 | None, 136 | ); 137 | let err_poly = add_oracle.compute_polynomial(&vec![default_labeled]); 138 | assert!(err_poly.is_err()); 139 | assert_eq!( 140 | err_poly.unwrap_err(), 141 | Error::InputLengthError(String::from( 142 | "Mapping vector requires 2 oracles/evaluations but only 1 were provided" 143 | )) 144 | ); 145 | } 146 | 147 | #[test] 148 | fn test_query_set() { 149 | let rng = &mut thread_rng(); 150 | let a_poly = LabeledPolynomial::new( 151 | String::from("a"), 152 | DensePolynomial::::rand(4, rng), 153 | None, 154 | None, 155 | ); 156 | let b_poly = LabeledPolynomial::new( 157 | String::from("b"), 158 | DensePolynomial::::rand(4, rng), 159 | None, 160 | None, 161 | ); 162 | let c_poly = LabeledPolynomial::new( 163 | String::from("c"), 164 | DensePolynomial::::rand(4, rng), 165 | None, 166 | None, 167 | ); 168 | 169 | let concrete_oracles = &[a_poly.clone(), b_poly.clone(), c_poly.clone()]; 170 | let oracle_labels: Vec<_> = concrete_oracles 171 | .iter() 172 | .map(|oracle| oracle.label().clone()) 173 | .collect(); 174 | 175 | let shifting_coefficients: Vec = sample_vector(rng, 3); 176 | let mapping_vector = vec![2, 2, 0]; 177 | 178 | let add_oracle = 179 | GenericShiftingVO::new(&mapping_vector, &shifting_coefficients, harder_addition) 180 | .unwrap(); 181 | 182 | let eval_point = (String::from("beta"), F::rand(rng)); 183 | let query_set = add_oracle 184 | .generate_query_set(&oracle_labels, &eval_point) 185 | .unwrap(); 186 | 187 | let evals = evaluate_query_set(concrete_oracles.iter(), &query_set); 188 | 189 | let evaluated = add_oracle 190 | .evaluate_from_concrete_evals(&oracle_labels, &eval_point.1, &evals) 191 | .unwrap(); 192 | let eval_from_poly = add_oracle 193 | .compute_polynomial(concrete_oracles) 194 | .unwrap() 195 | .evaluate(&eval_point.1); 196 | 197 | assert_eq!(evaluated, eval_from_poly) 198 | } 199 | 200 | #[test] 201 | fn test_zero_over_k_inverse_check_oracle() { 202 | let m = 8; 203 | let rng = &mut test_rng(); 204 | let domain_k = GeneralEvaluationDomain::::new(m).unwrap(); 205 | 206 | let max_degree = 20; 207 | let max_hiding = 1; 208 | 209 | let enforced_hiding_bound = Some(1); 210 | let enforced_degree_bound = 14; 211 | 212 | let pp = PC::setup(max_degree, None, rng).unwrap(); 213 | let (ck, vk) = PC::trim( 214 | &pp, 215 | max_degree, 216 | max_hiding, 217 | Some(&[2, enforced_degree_bound]), 218 | ) 219 | .unwrap(); 220 | 221 | // Step 1: choose a random polynomial 222 | let f_unlabeled: DensePolynomial = DensePolynomial::rand(7, rng); 223 | let f = LabeledPolynomial::new( 224 | String::from("f"), 225 | f_unlabeled, 226 | Some(enforced_degree_bound), 227 | enforced_hiding_bound, 228 | ); 229 | 230 | // Step 2: evaluate it 231 | let f_evals = f.evaluate_over_domain_by_ref(domain_k); 232 | 233 | // Step 3: find the inverse at each of these points 234 | let desired_g_evals = f_evals 235 | .evals 236 | .iter() 237 | .map(|&x| x.inverse().unwrap()) 238 | .collect::>(); 239 | let desired_g_evals = Evaluations::from_vec_and_domain(desired_g_evals, domain_k); 240 | 241 | // Step 4: interpolate a polynomial from the inverses 242 | let g = desired_g_evals.clone().interpolate(); 243 | let g = LabeledPolynomial::new( 244 | String::from("g"), 245 | g.clone(), 246 | Some(enforced_degree_bound), 247 | enforced_hiding_bound, 248 | ); 249 | 250 | // Step 5: commit to the concrete oracles 251 | let concrete_oracles = [f, g]; 252 | let (commitments, rands) = PC::commit(&ck, &concrete_oracles, Some(rng)) 253 | .map_err(to_pc_error::) 254 | .unwrap(); 255 | 256 | // Step 6: Derive the desired virtual oracle 257 | let alphas = vec![F::one(), F::one()]; 258 | let inverse_check_oracle = 259 | GenericShiftingVO::new(&vec![0, 1], &alphas.clone(), presets::inverse_check).unwrap(); 260 | 261 | // Step 7: prove 262 | let zero_over_k_proof = ZeroOverK::::prove( 263 | &concrete_oracles, 264 | &commitments, 265 | &rands, 266 | Some(enforced_degree_bound), 267 | &inverse_check_oracle, 268 | &domain_k, 269 | &ck, 270 | rng, 271 | ); 272 | 273 | // Step 8: verify 274 | let is_valid = ZeroOverK::::verify( 275 | zero_over_k_proof.unwrap(), 276 | &commitments, 277 | Some(enforced_degree_bound), 278 | &inverse_check_oracle, 279 | &domain_k, 280 | &vk, 281 | ); 282 | 283 | assert!(is_valid.is_ok()); 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /zero_over_k/src/virtual_oracle/generic_shifting_vo/vo_term.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Add, Div, Mul, Sub}; 2 | 3 | use ark_ff::{FftField, Field}; 4 | use ark_poly::{univariate::DensePolynomial, UVPolynomial}; 5 | 6 | /// A term to be manipulated by the virtual oracle's function, this is either a (shifted) polynomial or an evaluation thereof. 7 | #[derive(Clone, Debug)] 8 | pub enum VOTerm { 9 | Evaluation(F), 10 | Polynomial(DensePolynomial), 11 | } 12 | 13 | impl Add for VOTerm { 14 | type Output = Self; 15 | 16 | fn add(self, rhs: Self) -> Self::Output { 17 | match self { 18 | Self::Evaluation(eval) => match rhs { 19 | Self::Evaluation(rhs_eval) => Self::Evaluation(eval + rhs_eval), 20 | Self::Polynomial(rhs_poly) => { 21 | Self::Polynomial(DensePolynomial::from_coefficients_slice(&[eval]) + rhs_poly) 22 | } 23 | }, 24 | Self::Polynomial(poly) => match rhs { 25 | Self::Evaluation(rhs_eval) => { 26 | Self::Polynomial(poly + DensePolynomial::from_coefficients_slice(&[rhs_eval])) 27 | } 28 | Self::Polynomial(rhs_poly) => Self::Polynomial(poly + rhs_poly), 29 | }, 30 | } 31 | } 32 | } 33 | 34 | impl Sub for VOTerm { 35 | type Output = Self; 36 | 37 | fn sub(self, rhs: Self) -> Self::Output { 38 | match self { 39 | Self::Evaluation(eval) => match rhs { 40 | Self::Evaluation(rhs_eval) => Self::Evaluation(eval - rhs_eval), 41 | Self::Polynomial(rhs_poly) => { 42 | Self::Polynomial(&DensePolynomial::from_coefficients_slice(&[eval]) - &rhs_poly) 43 | } 44 | }, 45 | Self::Polynomial(poly) => match rhs { 46 | Self::Evaluation(rhs_eval) => { 47 | Self::Polynomial(&poly - &DensePolynomial::from_coefficients_slice(&[rhs_eval])) 48 | } 49 | Self::Polynomial(rhs_poly) => Self::Polynomial(&poly - &rhs_poly), 50 | }, 51 | } 52 | } 53 | } 54 | 55 | impl Mul for VOTerm { 56 | type Output = Self; 57 | 58 | fn mul(self, rhs: Self) -> Self::Output { 59 | match self { 60 | Self::Evaluation(eval) => match rhs { 61 | Self::Evaluation(rhs_eval) => Self::Evaluation(eval * rhs_eval), 62 | Self::Polynomial(rhs_poly) => Self::Polynomial(&rhs_poly * eval), 63 | }, 64 | Self::Polynomial(poly) => match rhs { 65 | Self::Evaluation(rhs_eval) => Self::Polynomial(&poly * rhs_eval), 66 | Self::Polynomial(rhs_poly) => Self::Polynomial(&poly * &rhs_poly), 67 | }, 68 | } 69 | } 70 | } 71 | 72 | impl Div for VOTerm { 73 | type Output = Self; 74 | 75 | fn div(self, rhs: Self) -> Self::Output { 76 | match self { 77 | Self::Evaluation(eval) => match rhs { 78 | Self::Evaluation(rhs_eval) => Self::Evaluation(eval / rhs_eval), 79 | Self::Polynomial(rhs_poly) => { 80 | Self::Polynomial(&DensePolynomial::from_coefficients_slice(&[eval]) / &rhs_poly) 81 | } 82 | }, 83 | Self::Polynomial(poly) => match rhs { 84 | Self::Evaluation(rhs_eval) => { 85 | Self::Polynomial(&poly / &DensePolynomial::from_coefficients_slice(&[rhs_eval])) 86 | } 87 | Self::Polynomial(rhs_poly) => Self::Polynomial(&poly / &rhs_poly), 88 | }, 89 | } 90 | } 91 | } 92 | 93 | #[macro_export] 94 | macro_rules! vo_constant { 95 | ($field_element:expr) => { 96 | VOTerm::Evaluation($field_element) 97 | }; 98 | } 99 | -------------------------------------------------------------------------------- /zero_over_k/src/virtual_oracle/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Error; 2 | use ark_ff::Field; 3 | use ark_poly_commit::{Evaluations, PolynomialLabel, QuerySet}; 4 | 5 | use self::generic_shifting_vo::vo_term::VOTerm; 6 | 7 | pub mod generic_shifting_vo; 8 | 9 | pub trait VirtualOracle { 10 | /// maps input concrete oracles to internal terms, e.g.: 11 | /// mapping_vector = [0, 0, 2] means h_0 = concrete_0, h_1 = concrete_0, h_2 = concrete_2 12 | fn mapping_vector(&self) -> Vec; 13 | 14 | /// returns the shifting coefficients (denoted alpha in the paper) 15 | fn shifting_coefficients(&self) -> Vec; 16 | 17 | fn apply_evaluation_function(&self, terms: &[VOTerm]) -> VOTerm; 18 | 19 | /// Gives a count of all the terms expected by the VO function excluding the X term 20 | fn num_of_variable_terms(&self) -> usize; 21 | 22 | /// Generate a query set that will allow to evaluate the VO at a requested (labeled) point 23 | fn generate_query_set( 24 | &self, 25 | concrete_oracle_labels: &[PolynomialLabel], 26 | query_point: &(String, F), 27 | ) -> Result, Error>; 28 | 29 | /// Given evalutations of each of the concrete oracles, produce the corresponding evaluation for the virtual oracle 30 | fn evaluate_from_concrete_evals( 31 | &self, 32 | concrete_oracle_labels: &[PolynomialLabel], 33 | eval_point: &F, 34 | evaluations: &Evaluations, 35 | ) -> Result; 36 | } 37 | 38 | /// Returns the list of concrete oracle labels ordered according to the mapping vector 39 | pub fn get_term_labels>( 40 | virtual_oracle: &VO, 41 | concrete_oracle_labels: &[PolynomialLabel], 42 | ) -> Vec { 43 | let mut h_labels = Vec::with_capacity(virtual_oracle.num_of_variable_terms()); 44 | for concrete_oracle_index in virtual_oracle.mapping_vector() { 45 | h_labels.push(concrete_oracle_labels[concrete_oracle_index].clone()); 46 | } 47 | 48 | h_labels 49 | } 50 | -------------------------------------------------------------------------------- /zero_over_k/src/zero_over_k/piop/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | util::powers_of, 3 | virtual_oracle::{get_term_labels, VirtualOracle}, 4 | }; 5 | use ark_ff::PrimeField; 6 | use ark_poly::univariate::DensePolynomial; 7 | use ark_poly_commit::{LinearCombination, PolynomialLabel}; 8 | use ark_std::marker::PhantomData; 9 | 10 | mod prover; 11 | mod verifier; 12 | 13 | /// A labeled DensePolynomial with coefficients over `F` 14 | pub type LabeledPolynomial = ark_poly_commit::LabeledPolynomial>; 15 | 16 | #[allow(dead_code)] 17 | pub struct PIOPforZeroOverK> { 18 | _field: PhantomData, 19 | _oracle: PhantomData, 20 | } 21 | 22 | impl> PIOPforZeroOverK { 23 | pub fn get_h_prime_labels(virtual_oracle: &VO) -> impl Iterator { 24 | (0..virtual_oracle.num_of_variable_terms()) 25 | .enumerate() 26 | .map(|(i, _)| format!("h_prime_{}", i)) 27 | } 28 | 29 | pub fn get_r_labels(virtual_oracle: &VO) -> impl Iterator { 30 | (0..virtual_oracle.num_of_variable_terms()) 31 | .enumerate() 32 | .map(|(i, _)| format!("r_{}", i)) 33 | } 34 | 35 | pub fn generate_h_prime_linear_combinations( 36 | virtual_oracle: &VO, 37 | concrete_oracle_labels: &[PolynomialLabel], 38 | ) -> Vec> { 39 | let h_labels = get_term_labels(virtual_oracle, concrete_oracle_labels); 40 | let h_prime_labels = Self::get_h_prime_labels(virtual_oracle); 41 | let mut linear_combinations: Vec> = Vec::new(); 42 | 43 | // Generate all the h_primes 44 | h_prime_labels.enumerate().for_each(|(i, label)| { 45 | let lc = LinearCombination::new( 46 | label, 47 | vec![ 48 | (F::one(), h_labels[i].clone()), 49 | (F::one(), format!("m_{}", i)), 50 | ], 51 | ); 52 | linear_combinations.push(lc) 53 | }); 54 | 55 | linear_combinations 56 | } 57 | 58 | pub fn generate_q2_linear_combination( 59 | virtual_oracle: &VO, 60 | q2_separation_challenge: F, 61 | ) -> LinearCombination { 62 | // Generate q2 63 | let r_labels = Self::get_r_labels(virtual_oracle); 64 | let terms: Vec<_> = r_labels 65 | .zip(powers_of(q2_separation_challenge)) 66 | .map(|(r_label, challenge_power)| (challenge_power, r_label)) 67 | .collect(); 68 | LinearCombination::new("q_2", terms) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /zero_over_k/src/zero_over_k/piop/prover.rs: -------------------------------------------------------------------------------- 1 | use super::PIOPforZeroOverK; 2 | use crate::error::Error; 3 | use crate::util::*; 4 | use crate::virtual_oracle::generic_shifting_vo::vo_term::VOTerm; 5 | use crate::virtual_oracle::VirtualOracle; 6 | use crate::zero_over_k::piop::{verifier::VerifierFirstMsg, LabeledPolynomial}; 7 | use ark_ff::{PrimeField, Zero}; 8 | use ark_marlin::ahp::prover::ProverMsg; 9 | use ark_poly::{ 10 | univariate::DenseOrSparsePolynomial, univariate::DensePolynomial, EvaluationDomain, 11 | GeneralEvaluationDomain, UVPolynomial, 12 | }; 13 | use ark_std::rand::Rng; 14 | use std::iter; 15 | 16 | // TODO: change to use the new VirtualOracle implementation 17 | pub struct ProverState<'a, F: PrimeField, VO: VirtualOracle> { 18 | all_concrete_oracles: &'a [LabeledPolynomial], 19 | 20 | maximum_oracle_degree_bound: Option, 21 | 22 | alphas: &'a Vec, 23 | 24 | virtual_oracle: &'a VO, 25 | 26 | /// domain K over which a virtual oracle should be equal to 0 27 | domain_k: &'a GeneralEvaluationDomain, 28 | 29 | masking_polynomials: Option>>, 30 | 31 | random_polynomials: Option>>, 32 | 33 | // this variable is made public to avoid recomputing the masked oracles at the PIOP-to-SNARK compiler stage 34 | pub masked_oracles: Option>>, 35 | 36 | q_1: Option>, 37 | 38 | q_2: Option>, 39 | 40 | verifier_message: Option>, 41 | } 42 | 43 | /// The first set of prover oracles 44 | pub struct ProverFirstOracles { 45 | pub masking_polynomials: Vec>, 46 | 47 | pub random_polynomials: Vec>, 48 | 49 | pub q_1: LabeledPolynomial, 50 | } 51 | 52 | #[allow(dead_code)] 53 | impl ProverFirstOracles { 54 | pub fn iter(&self) -> impl Iterator> { 55 | self.masking_polynomials 56 | .iter() 57 | .chain(self.random_polynomials.iter()) 58 | .chain(iter::once(&self.q_1)) 59 | } 60 | } 61 | 62 | /// The second set of prover oracles 63 | pub struct ProverSecondOracles { 64 | pub q_2: LabeledPolynomial, 65 | } 66 | 67 | #[allow(dead_code)] 68 | impl ProverSecondOracles { 69 | pub fn iter(&self) -> impl Iterator> { 70 | iter::once(&self.q_2) 71 | } 72 | } 73 | 74 | impl> PIOPforZeroOverK { 75 | /// Return the initial prover state 76 | pub fn prover_init<'a>( 77 | domain: &'a GeneralEvaluationDomain, 78 | all_concrete_oracles: &'a [LabeledPolynomial], 79 | maximum_oracle_degree_bound: Option, 80 | virtual_oracle: &'a VO, 81 | alphas: &'a Vec, 82 | ) -> Result, Error> { 83 | Ok(ProverState { 84 | all_concrete_oracles, 85 | maximum_oracle_degree_bound, 86 | alphas, 87 | virtual_oracle, 88 | domain_k: domain, 89 | masking_polynomials: None, 90 | random_polynomials: None, 91 | masked_oracles: None, 92 | q_1: None, 93 | q_2: None, 94 | verifier_message: None, 95 | }) 96 | } 97 | 98 | pub fn prover_first_round<'a, R: Rng>( 99 | mut state: ProverState<'a, F, VO>, 100 | rng: &mut R, 101 | ) -> Result<(ProverMsg, ProverFirstOracles, ProverState<'a, F, VO>), Error> { 102 | let domain = state.domain_k; 103 | let alphas = state.alphas; 104 | 105 | // generate the masking polynomials and keep a record of the random polynomials that were used 106 | let (random_polynomials, masking_polynomials) = compute_maskings( 107 | state.virtual_oracle, 108 | &domain, 109 | &alphas, 110 | state.maximum_oracle_degree_bound, 111 | rng, 112 | ); 113 | 114 | // assign h polynomials based on the VO's mapping 115 | let h_polynomials: Vec<_> = state 116 | .virtual_oracle 117 | .mapping_vector() 118 | .iter() 119 | .map(|&mapped_index| state.all_concrete_oracles[mapped_index].clone()) 120 | .collect(); 121 | 122 | // compute the masked oracles 123 | let h_primes = h_polynomials 124 | .iter() 125 | .zip(masking_polynomials.iter()) 126 | .enumerate() 127 | .map(|(i, (oracle, masking_poly))| { 128 | LabeledPolynomial::new( 129 | format!("h_prime_{}", i), 130 | oracle.polynomial() + masking_poly.polynomial(), 131 | state.maximum_oracle_degree_bound, 132 | Some(1), 133 | ) 134 | }) 135 | .collect::>(); 136 | 137 | // Compute f_prime using the virtual oracle's function 138 | let f_prime = compute_f_prime(state.virtual_oracle, &h_primes)?; 139 | 140 | // divide by the vanishing polynomial 141 | let (quotient, _r) = DenseOrSparsePolynomial::from(&f_prime) 142 | .divide_with_q_and_r(&DenseOrSparsePolynomial::from( 143 | &domain.vanishing_polynomial(), 144 | )) 145 | .unwrap(); 146 | 147 | // sanity check 148 | // assert_eq!(_r, DensePolynomial::::zero()); 149 | 150 | let msg = ProverMsg::EmptyMessage; 151 | 152 | let q_1 = LabeledPolynomial::new(String::from("q_1"), quotient, None, None); // TODO: enforce degree bound on q1. Requires degree of the VO function 153 | 154 | let oracles = ProverFirstOracles { 155 | masking_polynomials: masking_polynomials.clone(), 156 | random_polynomials: random_polynomials.clone(), 157 | q_1: q_1.clone(), 158 | }; 159 | 160 | state.q_1 = Some(q_1); 161 | state.masking_polynomials = Some(masking_polynomials); 162 | state.random_polynomials = Some(random_polynomials); 163 | state.masked_oracles = Some(h_primes); 164 | 165 | Ok((msg, oracles, state)) 166 | } 167 | 168 | pub fn prover_second_round<'a, R: Rng>( 169 | ver_message: &VerifierFirstMsg, 170 | mut state: ProverState<'a, F, VO>, 171 | _r: &mut R, 172 | ) -> (ProverMsg, ProverSecondOracles, ProverState<'a, F, VO>) { 173 | let random_polynomials = state.random_polynomials.clone() 174 | .expect("ProverState should include the random polynomials that were used to create the maskings in round 1"); 175 | 176 | // q_2 is defined as r1 + c*r2 + c^2r3 + ... 177 | let q_2 = random_polynomials 178 | .iter() 179 | .zip(powers_of(ver_message.c)) 180 | .fold(DensePolynomial::::zero(), |acc_poly, (r, c_power)| { 181 | acc_poly + (r.polynomial() * c_power) 182 | }); 183 | 184 | let q_2 = LabeledPolynomial::new(String::from("q_2"), q_2, Some(2), None); 185 | 186 | let msg = ProverMsg::EmptyMessage; 187 | 188 | let oracles = ProverSecondOracles { q_2: q_2.clone() }; 189 | 190 | state.q_2 = Some(q_2); 191 | state.verifier_message = Some(*ver_message); 192 | 193 | (msg, oracles, state) 194 | } 195 | } 196 | 197 | /// computes array of m_i = ri(alpha_i^-1) * zk(alpha_i^-1) 198 | fn compute_maskings>( 199 | virtual_oracle: &VO, 200 | domain: &GeneralEvaluationDomain, 201 | alphas: &[F], 202 | masking_bound: Option, 203 | rng: &mut R, 204 | ) -> (Vec>, Vec>) { 205 | let num_of_concrete_oracles = virtual_oracle.num_of_variable_terms(); 206 | //r is defined as polynomial degree < 2 207 | let degree = 1; 208 | 209 | let mut random_polynomials = Vec::with_capacity(num_of_concrete_oracles); 210 | let mut masking_polynomials = Vec::with_capacity(num_of_concrete_oracles); 211 | 212 | for i in 0..num_of_concrete_oracles { 213 | let r = DensePolynomial::::rand(degree, rng); 214 | let shifting_factor = alphas[i].inverse().unwrap(); 215 | let r_shifted = shift_dense_poly(&r, &shifting_factor); 216 | let vanishing_shifted = 217 | shift_dense_poly(&domain.vanishing_polynomial().into(), &shifting_factor); 218 | 219 | random_polynomials.push(LabeledPolynomial::new( 220 | format!("r_{}", i), 221 | r, 222 | Some(2), 223 | Some(1), 224 | )); 225 | // random_polynomials.push(LabeledPolynomial::new(format!("r_{}", i), r, None, None)); 226 | masking_polynomials.push(LabeledPolynomial::new( 227 | format!("m_{}", i), 228 | &r_shifted * &vanishing_shifted, 229 | masking_bound, 230 | Some(1), 231 | )); 232 | } 233 | 234 | (random_polynomials, masking_polynomials) 235 | } 236 | 237 | fn compute_f_prime>( 238 | virtual_oracle: &VO, 239 | h_prime_polynomials: &[LabeledPolynomial], 240 | ) -> Result, Error> { 241 | let x_poly = DensePolynomial::from_coefficients_slice(&[F::zero(), F::one()]); 242 | 243 | let shifted_h_primes: Vec> = h_prime_polynomials 244 | .iter() 245 | .enumerate() 246 | .map(|(i, labeled_poly)| { 247 | shift_dense_poly( 248 | labeled_poly.polynomial(), 249 | &virtual_oracle.shifting_coefficients()[i], 250 | ) 251 | }) 252 | .collect(); 253 | 254 | let terms_for_eval_function = iter::once(&x_poly) 255 | .chain(shifted_h_primes.iter()) 256 | .map(|poly| VOTerm::Polynomial(poly.clone())) 257 | .collect::>>(); 258 | 259 | let f_prime = match virtual_oracle.apply_evaluation_function(&terms_for_eval_function) { 260 | VOTerm::Polynomial(f_prime) => Ok(f_prime), 261 | VOTerm::Evaluation(_) => Err(Error::VOFailedToInstantiate), 262 | }; 263 | 264 | f_prime 265 | } 266 | -------------------------------------------------------------------------------- /zero_over_k/src/zero_over_k/piop/verifier.rs: -------------------------------------------------------------------------------- 1 | use super::PIOPforZeroOverK; 2 | use crate::error::Error; 3 | use crate::virtual_oracle::VirtualOracle; 4 | use ark_ff::PrimeField; 5 | use ark_poly::{EvaluationDomain, GeneralEvaluationDomain}; 6 | use ark_poly_commit::QuerySet; 7 | use rand::Rng; 8 | use std::collections::HashMap; 9 | 10 | #[derive(Copy, Clone)] 11 | pub struct VerifierState<'a, F: PrimeField, VO: VirtualOracle> { 12 | virtual_oracle: &'a VO, 13 | 14 | maximum_oracle_degree_bound: Option, 15 | 16 | /// domain K over which a virtual oracle should be equal to 0 17 | domain_k: &'a GeneralEvaluationDomain, 18 | 19 | verifier_first_message: Option>, 20 | 21 | pub beta_1: Option, 22 | 23 | pub beta_2: Option, 24 | } 25 | 26 | /// Verifier message 27 | #[derive(Copy, Clone)] 28 | pub struct VerifierFirstMsg { 29 | /// Linearisation challenge for the maskings 30 | pub c: F, 31 | } 32 | 33 | impl> PIOPforZeroOverK { 34 | /// Return the initial verifier state 35 | pub fn verifier_init<'a>( 36 | virtual_oracle: &'a VO, 37 | maximum_oracle_degree_bound: Option, 38 | domain_k: &'a GeneralEvaluationDomain, 39 | ) -> Result, Error> { 40 | Ok(VerifierState { 41 | virtual_oracle, 42 | maximum_oracle_degree_bound, 43 | domain_k, 44 | verifier_first_message: None, 45 | beta_1: None, 46 | beta_2: None, 47 | }) 48 | } 49 | 50 | pub fn verifier_first_round<'a, R: Rng>( 51 | mut state: VerifierState<'a, F, VO>, 52 | rng: &mut R, 53 | ) -> Result<(VerifierFirstMsg, VerifierState<'a, F, VO>), Error> { 54 | let beta_1 = state.domain_k.sample_element_outside_domain(rng); 55 | let beta_2 = state.domain_k.sample_element_outside_domain(rng); 56 | let c = state.domain_k.sample_element_outside_domain(rng); 57 | 58 | let msg = VerifierFirstMsg { c }; 59 | 60 | state.verifier_first_message = Some(msg); 61 | state.beta_1 = Some(beta_1); 62 | state.beta_2 = Some(beta_2); 63 | 64 | Ok((msg, state)) 65 | } 66 | 67 | pub fn verifier_query_set( 68 | state: &VerifierState, 69 | alphas: &[F], 70 | ) -> Result, Error> { 71 | let beta_1 = state 72 | .beta_1 73 | .expect("Verifier should have computed beta 1 at this stage"); 74 | let beta_2 = state 75 | .beta_2 76 | .expect("Verifier should have computed beta 2 at this stage"); 77 | 78 | // let num_of_oracles = state.virtual_oracle.num_of_oracles(); 79 | 80 | // let mut h_prime_labels = Vec::with_capacity(num_of_oracles); 81 | // let mut m_labels = Vec::with_capacity(num_of_oracles); 82 | 83 | // for i in 0..num_of_oracles { 84 | // h_prime_labels.push(format!("h_prime_{}", i)); 85 | // m_labels.push(format!("m_{}", i)); 86 | // } 87 | 88 | // let alphas_labeled = alphas 89 | // .iter() 90 | // .enumerate() 91 | // .map(|(i, alpha)| (format!("alpha_{}", i), *alpha)) 92 | // .collect::>(); 93 | 94 | // let mut query_set = QuerySet::new(); 95 | // let h_primes_set = state.virtual_oracle.get_query_set( 96 | // &h_prime_labels, 97 | // &alphas_labeled.to_vec(), 98 | // &(String::from("beta_1"), beta_1), 99 | // ); 100 | // let m_query_set = state.virtual_oracle.get_query_set( 101 | // &m_labels, 102 | // &alphas_labeled.to_vec(), 103 | // &(String::from("beta_2"), beta_2), 104 | // ); 105 | 106 | // query_set.extend(h_primes_set); 107 | // query_set.extend(m_query_set); 108 | 109 | let mut query_set = QuerySet::::new(); 110 | let mut point_evaluations: HashMap = HashMap::new(); 111 | 112 | point_evaluations.insert(beta_1, String::from("beta_1")); 113 | point_evaluations.insert(beta_2, String::from("beta_2")); 114 | 115 | for (i, alpha) in alphas.iter().enumerate() { 116 | let test_point = *alpha * beta_1; 117 | let label = match point_evaluations.get(&test_point) { 118 | Some(label) => label.clone(), 119 | None => { 120 | let label = format!("alpha_{}_beta_1", i); 121 | point_evaluations.insert(test_point, label.clone()); 122 | 123 | label 124 | } 125 | }; 126 | 127 | query_set.insert((format!("h_prime_{}", i), (label, *alpha * beta_1))); 128 | 129 | let test_point = *alpha * beta_2; 130 | let label = match point_evaluations.get(&test_point) { 131 | Some(label) => label.clone(), 132 | None => { 133 | let label = format!("alpha_{}_beta_2", i); 134 | point_evaluations.insert(test_point, label.clone()); 135 | 136 | label 137 | } 138 | }; 139 | 140 | query_set.insert((format!("m_{}", i), (label, *alpha * beta_2))); 141 | } 142 | 143 | query_set.insert((String::from("q_1"), (String::from("beta_1"), beta_1))); 144 | query_set.insert((String::from("q_2"), (String::from("beta_2"), beta_2))); 145 | 146 | /* 147 | * What do we get with geo seq virtual oracle 148 | * we have h_primes_0 and m_primes_0 149 | * h_prime_0 in point alpha_0_beta_1 150 | * h_prime_0 in point beta_1 151 | * 152 | * m_0 in point alpha_0_beta_2 153 | * m_0 in point beta_2 154 | * 155 | * q_1, in point: beta_1 156 | * q_2, in point: beta_2 157 | */ 158 | 159 | Ok(query_set) 160 | } 161 | 162 | // evaluate poly: h_prime_0, in point: beta_1 with value Fp256 "(293D6AB4C074E7502C1DBD28D1151523DB175383DDBC82E5392B6EA5B70C6A7A)" 163 | // evaluate poly: h_prime_1, in point: beta_1 with value Fp256 "(293D6AB4C074E7502C1DBD28D1151523DB175383DDBC82E5392B6EA5B70C6A7A)" 164 | // evaluate poly: m_0, in point: beta_2 with value Fp256 "(1A6BFDB9903D732617070351C48D21919D5715900E9204C2E3A0970EC36CE81D)" 165 | // evaluate poly: m_1, in point: beta_2 with value Fp256 "(1A6BFDB9903D732617070351C48D21919D5715900E9204C2E3A0970EC36CE81D)" 166 | // evaluate poly: q_1, in point: beta_1 with value Fp256 "(293D6AB4C074E7502C1DBD28D1151523DB175383DDBC82E5392B6EA5B70C6A7A)" 167 | // evaluate poly: q_2, in point: beta_2 with value Fp256 "(1A6BFDB9903D732617070351C48D21919D5715900E9204C2E3A0970EC36CE81D)" 168 | } 169 | -------------------------------------------------------------------------------- /zero_over_k/src/zero_over_k/proof.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::PrimeField; 2 | use homomorphic_poly_commit::AdditivelyHomomorphicPCS; 3 | 4 | use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError}; 5 | use ark_std::io::{Read, Write}; 6 | 7 | #[derive(Clone, CanonicalSerialize, CanonicalDeserialize)] 8 | pub struct Proof> { 9 | // commitments 10 | pub m_commitments: Vec, 11 | pub r_commitments: Vec, 12 | pub q1_commit: PC::Commitment, 13 | 14 | // evaluations 15 | pub q1_eval: F, 16 | pub q2_eval: F, 17 | pub h_prime_evals: Vec, 18 | pub m_evals: Vec, 19 | 20 | // opening proof 21 | pub opening_proof: PC::BatchProof, 22 | } 23 | -------------------------------------------------------------------------------- /zero_over_k/src/zero_over_k/tests.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod test { 3 | use crate::{ 4 | error::{to_pc_error, Error}, 5 | virtual_oracle::generic_shifting_vo::{presets, GenericShiftingVO}, 6 | zero_over_k::ZeroOverK, 7 | }; 8 | use ark_bn254::{Bn254, Fr}; 9 | use ark_ff::Field; 10 | use ark_ff::One; 11 | use ark_poly::{ 12 | univariate::DensePolynomial, EvaluationDomain, Evaluations, GeneralEvaluationDomain, 13 | UVPolynomial, 14 | }; 15 | use ark_poly_commit::{LabeledCommitment, LabeledPolynomial, PolynomialCommitment}; 16 | use ark_std::{rand::thread_rng, test_rng}; 17 | use blake2::Blake2s; 18 | use fiat_shamir_rng::SimpleHashFiatShamirRng; 19 | use homomorphic_poly_commit::marlin_kzg::KZG10; 20 | use rand_chacha::ChaChaRng; 21 | type FS = SimpleHashFiatShamirRng; 22 | 23 | type F = Fr; 24 | type PC = KZG10; 25 | 26 | #[test] 27 | fn test_zero_over_k_inverse_check_oracle() { 28 | let m = 8; 29 | let rng = &mut test_rng(); 30 | let domain_k = GeneralEvaluationDomain::::new(m).unwrap(); 31 | 32 | let max_degree = 20; 33 | let max_hiding = 1; 34 | 35 | let enforced_hiding_bound = Some(1); 36 | let enforced_degree_bound = 14; 37 | 38 | let pp = PC::setup(max_degree, None, rng).unwrap(); 39 | let (ck, vk) = PC::trim( 40 | &pp, 41 | max_degree, 42 | max_hiding, 43 | Some(&[2, enforced_degree_bound]), 44 | ) 45 | .unwrap(); 46 | 47 | // Step 1: choose a random polynomial 48 | let f_unlabeled: DensePolynomial = DensePolynomial::rand(7, rng); 49 | let f = LabeledPolynomial::new( 50 | String::from("f"), 51 | f_unlabeled, 52 | Some(enforced_degree_bound), 53 | enforced_hiding_bound, 54 | ); 55 | 56 | // Step 2: evaluate it 57 | let f_evals = f.evaluate_over_domain_by_ref(domain_k); 58 | 59 | // Step 3: find the inverse at each of these points 60 | let desired_g_evals = f_evals 61 | .evals 62 | .iter() 63 | .map(|&x| x.inverse().unwrap()) 64 | .collect::>(); 65 | let desired_g_evals = Evaluations::from_vec_and_domain(desired_g_evals, domain_k); 66 | 67 | // Step 4: interpolate a polynomial from the inverses 68 | let g = desired_g_evals.clone().interpolate(); 69 | let g = LabeledPolynomial::new( 70 | String::from("g"), 71 | g.clone(), 72 | Some(enforced_degree_bound), 73 | enforced_hiding_bound, 74 | ); 75 | 76 | // Step 5: commit to the concrete oracles 77 | let concrete_oracles = [f, g]; 78 | let (commitments, rands) = PC::commit(&ck, &concrete_oracles, Some(rng)) 79 | .map_err(to_pc_error::) 80 | .unwrap(); 81 | 82 | // Step 6: Derive the desired virtual oracle 83 | let alphas = vec![F::one(), F::one()]; 84 | let inverse_check_oracle = 85 | GenericShiftingVO::new(&vec![0, 1], &alphas, presets::inverse_check).unwrap(); 86 | 87 | // Step 7: prove 88 | let zero_over_k_proof = ZeroOverK::::prove( 89 | &concrete_oracles, 90 | &commitments, 91 | &rands, 92 | Some(enforced_degree_bound), 93 | &inverse_check_oracle, 94 | &domain_k, 95 | &ck, 96 | rng, 97 | ); 98 | 99 | // Step 8: verify 100 | let is_valid = ZeroOverK::::verify( 101 | zero_over_k_proof.unwrap(), 102 | &commitments, 103 | Some(enforced_degree_bound), 104 | &inverse_check_oracle, 105 | &domain_k, 106 | &vk, 107 | ); 108 | 109 | assert!(is_valid.is_ok()); 110 | } 111 | 112 | #[test] 113 | fn test_error_on_invalid_proof() { 114 | let m = 8; 115 | let rng = &mut thread_rng(); 116 | let domain_k = GeneralEvaluationDomain::::new(m).unwrap(); 117 | 118 | let max_degree = 20; 119 | let max_hiding = 1; 120 | 121 | let enforced_hiding_bound = Some(1); 122 | let enforced_degree_bound = 14; 123 | 124 | let pp = PC::setup(max_degree, None, rng).unwrap(); 125 | let (ck, vk) = PC::trim(&pp, max_degree, max_hiding, Some(&[2, 14])).unwrap(); 126 | 127 | let f_evals: Vec = vec![ 128 | F::from(1u64), 129 | F::from(2u64), 130 | F::from(3u64), 131 | F::from(4u64), 132 | F::from(5u64), 133 | F::from(6u64), 134 | F::from(7u64), 135 | F::from(8u64), 136 | ]; 137 | 138 | let f = DensePolynomial::::from_coefficients_slice(&domain_k.ifft(&f_evals)); 139 | let f = LabeledPolynomial::new( 140 | String::from("f"), 141 | f, 142 | Some(enforced_degree_bound), 143 | enforced_hiding_bound, 144 | ); 145 | 146 | let g_evals: Vec = vec![ 147 | F::from(8u64), 148 | F::from(7u64), 149 | F::from(6u64), 150 | F::from(5u64), 151 | F::from(4u64), 152 | F::from(3u64), 153 | F::from(2u64), 154 | F::from(1u64), 155 | ]; 156 | 157 | let g = DensePolynomial::::from_coefficients_slice(&domain_k.ifft(&g_evals)); 158 | let g = LabeledPolynomial::new( 159 | String::from("g"), 160 | g.clone(), 161 | Some(enforced_degree_bound), 162 | enforced_hiding_bound, 163 | ); 164 | 165 | let concrete_oracles = [f, g]; 166 | let alphas = vec![F::one(), F::one()]; 167 | let (commitments, rands) = PC::commit(&ck, &concrete_oracles, Some(rng)) 168 | .map_err(to_pc_error::) 169 | .unwrap(); 170 | 171 | let zero_over_k_vo = 172 | GenericShiftingVO::new(&[0, 1], &alphas, presets::equality_check).unwrap(); 173 | 174 | let zero_over_k_proof = ZeroOverK::::prove( 175 | &concrete_oracles, 176 | &commitments, 177 | &rands, 178 | Some(enforced_degree_bound), 179 | &zero_over_k_vo, 180 | &domain_k, 181 | &ck, 182 | rng, 183 | ) 184 | .unwrap(); 185 | 186 | let res = ZeroOverK::::verify( 187 | zero_over_k_proof, 188 | &commitments, 189 | Some(enforced_degree_bound), 190 | &zero_over_k_vo, 191 | &domain_k, 192 | &vk, 193 | ); 194 | 195 | assert!(res.is_err()); 196 | 197 | // Test for a specific error 198 | assert_eq!(res.unwrap_err(), Error::Check2Failed); 199 | } 200 | 201 | #[test] 202 | fn test_degree_bound_not_respected() { 203 | let m = 8; 204 | let rng = &mut test_rng(); 205 | let domain_k = GeneralEvaluationDomain::::new(m).unwrap(); 206 | 207 | let max_degree = 20; 208 | let max_hiding = 1; 209 | 210 | let enforced_hiding_bound = Some(1); 211 | let prover_degree_bound = 15; 212 | let verifier_degree_bound = 14; 213 | 214 | let pp = PC::setup(max_degree, None, rng).unwrap(); 215 | let (ck, vk) = PC::trim( 216 | &pp, 217 | max_degree, 218 | max_hiding, 219 | Some(&[2, verifier_degree_bound, prover_degree_bound]), 220 | ) 221 | .unwrap(); 222 | 223 | // Step 1: choose a random polynomial 224 | let f_unlabeled: DensePolynomial = DensePolynomial::rand(7, rng); 225 | let f = LabeledPolynomial::new( 226 | String::from("f"), 227 | f_unlabeled, 228 | Some(prover_degree_bound), 229 | enforced_hiding_bound, 230 | ); 231 | 232 | // Step 2: evaluate it 233 | let f_evals = f.evaluate_over_domain_by_ref(domain_k); 234 | 235 | // Step 3: find the inverse at each of these points 236 | let desired_g_evals = f_evals 237 | .evals 238 | .iter() 239 | .map(|&x| x.inverse().unwrap()) 240 | .collect::>(); 241 | let desired_g_evals = Evaluations::from_vec_and_domain(desired_g_evals, domain_k); 242 | 243 | // Step 4: interpolate a polynomial from the inverses 244 | let g = desired_g_evals.clone().interpolate(); 245 | let g = LabeledPolynomial::new( 246 | String::from("g"), 247 | g.clone(), 248 | Some(prover_degree_bound), 249 | enforced_hiding_bound, 250 | ); 251 | 252 | // Step 5: commit to the concrete oracles 253 | let concrete_oracles = [f, g]; 254 | let (commitments, rands) = PC::commit(&ck, &concrete_oracles, Some(rng)) 255 | .map_err(to_pc_error::) 256 | .unwrap(); 257 | 258 | // Step 6: Derive the desired virtual oracle 259 | let alphas = vec![F::one(), F::one()]; 260 | let inverse_check_oracle = 261 | GenericShiftingVO::new(&vec![0, 1], &alphas, presets::inverse_check).unwrap(); 262 | 263 | // Step 7: prove 264 | let zero_over_k_proof = ZeroOverK::::prove( 265 | &concrete_oracles, 266 | &commitments, 267 | &rands, 268 | Some(prover_degree_bound), 269 | &inverse_check_oracle, 270 | &domain_k, 271 | &ck, 272 | rng, 273 | ) 274 | .unwrap(); 275 | 276 | // Step 8: verify 277 | let verifier_commitments: Vec> = commitments 278 | .iter() 279 | .map(|c| { 280 | let label = c.label().clone(); 281 | let comm = c.commitment().clone(); 282 | LabeledCommitment::new(label, comm, Some(verifier_degree_bound)) 283 | }) 284 | .collect(); 285 | let res = ZeroOverK::::verify( 286 | zero_over_k_proof, 287 | &verifier_commitments, 288 | Some(verifier_degree_bound), 289 | &inverse_check_oracle, 290 | &domain_k, 291 | &vk, 292 | ); 293 | 294 | assert!(res.is_err()); 295 | 296 | assert_eq!(res.unwrap_err(), Error::BatchCheckError); 297 | } 298 | } 299 | --------------------------------------------------------------------------------