├── .DS_Store ├── .github └── CODEOWNERS ├── .gitignore ├── .rusty-hook.toml ├── Cargo.toml ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── arkworks_adapter ├── Cargo.toml ├── README.md └── src │ ├── fq5.rs │ ├── integration_tests.rs │ ├── lib.rs │ └── test_utils.rs ├── default.nix ├── src ├── circuits │ ├── mod.rs │ ├── qap.rs │ ├── r1cs.rs │ └── test_utils.rs ├── config.rs ├── lib.rs ├── math │ ├── cyclic_group.rs │ ├── elliptic_curve.rs │ ├── field_element.rs │ ├── field_extension_element.rs │ ├── mod.rs │ ├── msm.rs │ └── polynomial.rs └── pinocchio │ ├── mod.rs │ ├── prover.rs │ ├── setup.rs │ └── verifier.rs └── tests └── pinocchio.rs /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdaclass/pinocchio_lambda_vm/14ab91e1dd8e76e8769075d2542c7c4bf1988afd/.DS_Store -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @lambdaclass/zk_research_and_development 2 | * @schouhy 3 | * @ajgara 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | arkworks_adapter/target/ 13 | arkworks_adapter/Cargo.lock 14 | arkworks_adapter/**/*.rs.bk 15 | -------------------------------------------------------------------------------- /.rusty-hook.toml: -------------------------------------------------------------------------------- 1 | [hooks] 2 | pre-commit = "cargo test && cargo clippy --all-targets --all-features -- -D warnings && cargo fmt --all -- --check && cd arkworks_adapter && cargo test && cargo clippy --all-targets --all-features -- -D warnings && cargo fmt --all -- --check" 3 | 4 | [logging] 5 | verbose = true 6 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pinocchio_lambda_vm" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | rand = "0.8.5" 8 | 9 | [dev-dependencies] 10 | rusty-hook = "^0.11.2" 11 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:1.66 2 | 3 | WORKDIR /usr/src/elliptic-curves 4 | COPY . . 5 | 6 | CMD cargo test 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Entropy1729 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | cargo test 3 | 4 | test_arkworks_adapter: 5 | cd arkworks_adapter && cargo test 6 | 7 | docker-shell: 8 | docker build -t rust-curves . 9 | docker run -it rust-curves bash 10 | 11 | nix-shell: 12 | nix-shell 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # README 2 | This is a self-contained Rust implementation of [Pinocchio](https://eprint.iacr.org/2013/279.pdf). It is intented for those who want to learn more about SNARKs. Pinocchio is a proving system for efficiently verifying general computations. This source code is the companion of this [blog post](https://blog.lambdaclass.com/pinocchio-virtual-machine-nearly-practical-verifiable-computation/ 3 | ) were a more friendly and high level explanation of Pinocchio can be found. 4 | 5 | It also contains an arkworks_adapter project to use circuits made with Arkworks R1CS with this implementation of Pinocchio 6 | # Usage 7 | 8 | To run tests: 9 | `make test` 10 | `make test_arkworks_adapter` 11 | 12 | To use a docker shell: 13 | `make docker-shell` 14 | 15 | To use a nix shell: 16 | `make nix-shell` 17 | 18 | # Pinocchio 19 | Pinocchio is a proving system. It is composed of an initial setup, a prover and a verifier. The idea is the following. For a computer program P written in some language, the prover runs the program P and computes its output and a proof of execution. This proof convinces the verifier that he has actually run the program and the output is correct. This is useful for example when the prover is an untrusted agent. 20 | 21 | To achieve this, the system needs to work with mathematical expressions instead of normal traces of program executions. For this reason, correct execution instances are expressed in terms of polynomials in a process called arithmetization. 22 | 23 | `pinocchio_lambda_vm` uses elliptic curve cryptography, polynomials and other mathematical tools common to many other SNARKs. 24 | 25 | The module structure is the following: 26 | - ```pinocchio/```: setup, prover, verifier. 27 | - ```math/```: elliptic curve, polynomials and other math primitives. 28 | - ```circuits/```: code relevant to the arithmetization process. 29 | 30 | These concepts are not simple, but they're probably simpler than you think. For further reading we suggest: 31 | - [Pinocchio](https://eprint.iacr.org/2013/279.pdf): original paper with formal explanation of the system. 32 | - [Moonmath manual](https://leastauthority.com/community-matters/moonmath-manual/): friendly resource to understand the mathematical background of zk-SNARKs in general. 33 | - [Pairing for beginners](https://static1.squarespace.com/static/5fdbb09f31d71c1227082339/t/5ff394720493bd28278889c6/1609798774687/PairingsForBeginners.pdf): a good introduction to elliptic curves in general and pairings in particular. 34 | - [An Introduction to Mathematical Cryptography](https://link.springer.com/book/10.1007/978-0-387-77993-5): general cryptography and useful for pairings also. 35 | - [Master thesis from David Møller Hansen](https://www.sagemath.org/files/thesis/hansen-thesis-2009.pdf): other helpful resource to understand pairings and their implementations. 36 | 37 | Some of the algorithms in `pinocchio_lambda_vm` can be found in these books. You will find references to this resources throughout the code. 38 | 39 | # Understanding the code 40 | We encourage to start by reading the [blog post](https://www.notamonadtutorial.com/pinocchio-virtual-machine-nearly-practical-verifiable-computation/ 41 | ). 42 | 43 | Then, in the ```tests/``` module you will find integration tests that will guide you through the happy path: the setup, the prover and the verifier. Each of these components can be located in their own files: 44 | 45 | - `pinocchio/setup.rs`: generates the `VerificationKey` and the `EvaluationKey` with the relevant data to construct and verify proofs. This data comes from the structure of the program P encoded as `Polynomials` in a `QAP` (Quadratic arithmetic program). To hide this data, random `FieldElement`s are sampled as `ToxicWaste` and then mapped to `EllipticCurveElement`s via repeated addition of a `generator()` of the curve. 46 | 47 | - `pinocchio/prover.rs`: takes the circuit encoded as a `QAP` and the trace of the program as `FieldElement`s. Then, it applies the `msm(...)` operation in order to generate the proof elements, in this case, `EllipticCurveElement`s hidings. 48 | 49 | - `pinocchio/verifier.rs`: verifies the proof by checking the conditions mentioned in the paper. This involves computing `pairing(...)` operations between `EllipticCurveElement`'s. 50 | 51 | The rest of the files implement mathematical primitives and other auxiliary tools to complete the system. 52 | -------------------------------------------------------------------------------- /arkworks_adapter/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "arkworks_adapter" 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 | pinocchio_lambda_vm = { path = "../" } 10 | ark-r1cs-std = { version = "^0.3.1"} 11 | ark-ff = { version = "^0.3.0" } 12 | ark-relations = { version = "^0.3.0" } 13 | num-bigint = { version = "0.4", default-features = false } 14 | -------------------------------------------------------------------------------- /arkworks_adapter/README.md: -------------------------------------------------------------------------------- 1 | # Arkworks Adapter for Lambda's Pinocchio 2 | 3 | To generate a proof from a constraint system made in arkworks in Lambda's Pinocchio, call the function to import the r1cs, and the function to import the IO and witness 4 | 5 | ``` 6 | let r1cs = pinocchio_r1cs_from_arkworks_cs(&cs); 7 | let (io, witness) = pinocchio_io_and_witness_from_arkworks_cs(&cs); 8 | ``` 9 | -------------------------------------------------------------------------------- /arkworks_adapter/src/fq5.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::{biginteger::BigInteger64 as BigInteger, fields::*}; 2 | 3 | pub type Fq = Fp64; 4 | 5 | pub struct FqParameters; 6 | 7 | impl Fp64Parameters for FqParameters {} 8 | 9 | impl FftParameters for FqParameters { 10 | type BigInt = BigInteger; 11 | 12 | // N = 2^s * t, t = 3 (odd integer), N = 4 13 | // -> s = 3 14 | const TWO_ADICITY: u32 = 3; 15 | 16 | #[rustfmt::skip] 17 | const TWO_ADIC_ROOT_OF_UNITY: BigInteger = BigInteger([ 18 | 3 19 | ]); 20 | } 21 | 22 | impl FpParameters for FqParameters { 23 | const MODULUS: BigInteger = BigInteger([5]); 24 | 25 | const MODULUS_BITS: u32 = 3; 26 | 27 | const CAPACITY: u32 = Self::MODULUS_BITS - 1; 28 | 29 | const REPR_SHAVE_BITS: u32 = 0; 30 | 31 | const R: BigInteger = BigInteger([1]); 32 | 33 | const R2: BigInteger = BigInteger([1]); 34 | 35 | const INV: u64 = 3689348814741910323; 36 | 37 | /// GENERATOR = 3 38 | /// Encoded in Montgomery form, so the value here is 3 39 | /// since R = 1 40 | #[rustfmt::skip] 41 | const GENERATOR: BigInteger = BigInteger([3]); 42 | 43 | #[rustfmt::skip] 44 | const MODULUS_MINUS_ONE_DIV_TWO: BigInteger = BigInteger([1]); 45 | 46 | // This is used for Tonelly Shanks, an algorithm to solve a square root. 47 | // 2^s * t=5-1 -> 2^s * 3 = 4 48 | #[rustfmt::skip] 49 | const T: BigInteger = BigInteger([3]); 50 | 51 | #[rustfmt::skip] 52 | const T_MINUS_ONE_DIV_TWO: BigInteger = BigInteger([2]); 53 | } 54 | 55 | #[allow(dead_code)] 56 | pub const FQ_ONE: Fq = Fq::new(FqParameters::R); 57 | #[allow(dead_code)] 58 | pub const FQ_ZERO: Fq = Fq::new(BigInteger([0])); 59 | -------------------------------------------------------------------------------- /arkworks_adapter/src/integration_tests.rs: -------------------------------------------------------------------------------- 1 | use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem}; 2 | 3 | use pinocchio_lambda_vm::{ 4 | circuits::qap::QuadraticArithmeticProgram as Qap, 5 | math::elliptic_curve::EllipticCurveElement, 6 | pinocchio::{ 7 | prover, 8 | setup::{setup, EvaluationKey, ToxicWaste, VerificationKey}, 9 | verifier, 10 | }, 11 | }; 12 | 13 | use crate::{ 14 | fq5::Fq, pinocchio_io_and_witness_from_arkworks_cs, pinocchio_r1cs_from_arkworks_cs, 15 | test_utils::PinocchioPaperExampleCircuit, 16 | }; 17 | 18 | #[test] 19 | fn create_proof_from_arkworks_and_verify_it() { 20 | let toxic_waste = ToxicWaste::sample(); 21 | 22 | let a = Fq::from(1); 23 | let b = Fq::from(2); 24 | let c = Fq::from(3); 25 | let d = Fq::from(4); 26 | 27 | let circuit = PinocchioPaperExampleCircuit { a, b, c, d }; 28 | 29 | let cs = ConstraintSystem::new_ref(); 30 | circuit.generate_constraints(cs.clone()).unwrap(); 31 | 32 | let r1cs = pinocchio_r1cs_from_arkworks_cs(&cs); 33 | let (io, witness) = pinocchio_io_and_witness_from_arkworks_cs(&cs); 34 | 35 | let qap: Qap = r1cs.into(); 36 | 37 | let (ek, vk): ( 38 | EvaluationKey, 39 | VerificationKey, 40 | ) = setup(&qap, &toxic_waste); 41 | 42 | let mut c_vector = io.clone(); 43 | c_vector.extend(witness); 44 | 45 | //Reminder: While we differentiate inptus and outputs in Pinocchio 46 | //All the IO can be placed in the input part, since there is only 47 | //IO for the proving system 48 | let proof = prover::generate_proof(&ek, &qap, &c_vector); 49 | 50 | let accepted = verifier::verify(&vk, &proof, &io); 51 | 52 | assert!(accepted); 53 | } 54 | -------------------------------------------------------------------------------- /arkworks_adapter/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod fq5; 2 | #[cfg(test)] 3 | mod integration_tests; 4 | mod test_utils; 5 | 6 | use std::ops::Deref; 7 | 8 | use ark_ff::PrimeField; 9 | use ark_relations::r1cs::ConstraintSystemRef; 10 | use num_bigint::BigUint; 11 | use pinocchio_lambda_vm::circuits::r1cs::R1CS; 12 | use pinocchio_lambda_vm::config::ORDER_R; 13 | 14 | use pinocchio_lambda_vm::math::field_element::FieldElement; 15 | type FE = FieldElement; 16 | 17 | /// Generates an `R1CS` compatible with Lambda Pinocchio from an Arkworks `ConstraintSystemRef` 18 | pub fn pinocchio_r1cs_from_arkworks_cs(cs: &ConstraintSystemRef) -> R1CS { 19 | cs.inline_all_lcs(); 20 | 21 | let r1cs_matrices = cs.to_matrices().unwrap(); 22 | 23 | let a = arkworks_r1cs_matrix_to_pinocchio_r1cs_matrix( 24 | &r1cs_matrices.a, 25 | cs.num_witness_variables() + cs.num_instance_variables() - 1, 26 | ); 27 | 28 | let b = arkworks_r1cs_matrix_to_pinocchio_r1cs_matrix( 29 | &r1cs_matrices.b, 30 | cs.num_witness_variables() + cs.num_instance_variables() - 1, 31 | ); 32 | 33 | let c = arkworks_r1cs_matrix_to_pinocchio_r1cs_matrix( 34 | &r1cs_matrices.c, 35 | cs.num_witness_variables() + cs.num_instance_variables() - 1, 36 | ); 37 | 38 | /* 39 | Notice we can't differentiate outputs and inputs from Arkworks CS, but for the proving system everything that matters is that it's public data (IO), 40 | or private data (witness/c_mid) 41 | */ 42 | 43 | R1CS::new_with_matrixes(a, b, c, cs.num_instance_variables() - 1, 0).unwrap() 44 | } 45 | 46 | /// Generates pinocchio IO and Witness from an Arkworks `ConstraintSystemRef` 47 | pub fn pinocchio_io_and_witness_from_arkworks_cs( 48 | cs: &ConstraintSystemRef, 49 | ) -> (Vec, Vec) { 50 | let binding = cs.borrow().unwrap(); 51 | let borrowed_cs_ref = binding.deref(); 52 | 53 | let ark_witness = &borrowed_cs_ref.witness_assignment; 54 | let ark_io = &borrowed_cs_ref.instance_assignment[1..].to_vec(); 55 | 56 | let io: Vec = ark_io.iter().map(ark_fq_to_pinocchio_fe).collect(); 57 | 58 | let witness: Vec = ark_witness.iter().map(ark_fq_to_pinocchio_fe).collect(); 59 | 60 | (io, witness) 61 | } 62 | 63 | fn arkworks_r1cs_matrix_to_pinocchio_r1cs_matrix( 64 | m: &[Vec<(F, usize)>], 65 | total_variables: usize, 66 | ) -> Vec> { 67 | sparse_matrix_to_dense(&arkworks_matrix_fps_to_pinocchio_fes(m), total_variables) 68 | } 69 | 70 | fn arkworks_matrix_fps_to_pinocchio_fes( 71 | m: &[Vec<(F, usize)>], 72 | ) -> Vec> { 73 | m.iter() 74 | .map(|x| { 75 | x.iter() 76 | .map(|(x, y)| (ark_fq_to_pinocchio_fe(x), *y)) 77 | .collect() 78 | }) 79 | .collect() 80 | } 81 | 82 | fn sparse_matrix_to_dense(m: &[Vec<(FE, usize)>], total_variables: usize) -> Vec> { 83 | m.iter() 84 | .map(|row| sparse_row_to_dense(row, total_variables)) 85 | .collect() 86 | } 87 | 88 | fn sparse_row_to_dense(row: &Vec<(FE, usize)>, total_variables: usize) -> Vec { 89 | //The first column of the r1cs is used for constants 90 | 91 | let mut dense_row = vec![FE::new(0); total_variables + 1]; 92 | 93 | for element in row { 94 | dense_row[element.1] = element.0; 95 | } 96 | dense_row 97 | } 98 | 99 | /// Converts an Arkworks fq to a pinocchio FE 100 | fn ark_fq_to_pinocchio_fe(ark_fq: &F) -> FE { 101 | // into_repr changes back the FQ from the Montgomery to 102 | // the underlaying representation 103 | let ark_fq = ark_fq.into_repr(); 104 | let ark_fq_big_int: BigUint = ark_fq.into(); 105 | let fq_as_128 = biguint_to_u128(ark_fq_big_int); 106 | FE::new(fq_as_128) 107 | } 108 | /// Converts biguint to u128 109 | /// If the biguint is bigger than u128 it takes the first 128 bytes 110 | fn biguint_to_u128(big: BigUint) -> u128 { 111 | match big.to_u64_digits().len() { 112 | 0 => 0, 113 | 1 => big.to_u64_digits()[0].into(), 114 | _ => big.to_u64_digits()[0] as u128 & ((big.to_u64_digits()[1] as u128) << 64), 115 | } 116 | } 117 | 118 | #[cfg(test)] 119 | mod tests { 120 | use super::*; 121 | use crate::test_utils::PinocchioPaperExampleCircuit; 122 | use fq5::Fq; 123 | 124 | use ark_relations::{ 125 | lc, 126 | r1cs::{ 127 | ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef, SynthesisError, Variable, 128 | }, 129 | }; 130 | use pinocchio_lambda_vm::circuits::{r1cs::Constraint, test_utils}; 131 | 132 | // These are tests circuits 133 | pub struct FullyPrivateMulCircuit { 134 | pub a: Fq, 135 | pub b: Fq, 136 | } 137 | 138 | impl ConstraintSynthesizer for FullyPrivateMulCircuit { 139 | fn generate_constraints(self, cs: ConstraintSystemRef) -> Result<(), SynthesisError> { 140 | let a = cs.new_witness_variable(|| Ok(self.a))?; 141 | 142 | let b = cs.new_witness_variable(|| Ok(self.b))?; 143 | 144 | let c = cs.new_witness_variable(|| Ok(self.a * self.b))?; 145 | 146 | cs.enforce_constraint(lc!() + a, lc!() + b, lc!() + c)?; 147 | 148 | Ok(()) 149 | } 150 | } 151 | pub struct PrivInputPubResultMulCircuit { 152 | pub a: Fq, 153 | pub b: Fq, 154 | } 155 | 156 | impl ConstraintSynthesizer for PrivInputPubResultMulCircuit { 157 | fn generate_constraints(self, cs: ConstraintSystemRef) -> Result<(), SynthesisError> { 158 | let a = cs.new_witness_variable(|| Ok(self.a))?; 159 | 160 | let b = cs.new_witness_variable(|| Ok(self.b))?; 161 | 162 | let c = cs.new_input_variable(|| Ok(self.a * self.b))?; 163 | 164 | cs.enforce_constraint(lc!() + a, lc!() + b, lc!() + c)?; 165 | 166 | Ok(()) 167 | } 168 | } 169 | 170 | pub struct MulPlusThree { 171 | pub a: Fq, 172 | pub b: Fq, 173 | } 174 | 175 | impl ConstraintSynthesizer for MulPlusThree { 176 | fn generate_constraints(self, cs: ConstraintSystemRef) -> Result<(), SynthesisError> { 177 | let a = cs.new_witness_variable(|| Ok(self.a))?; 178 | 179 | let b = cs.new_witness_variable(|| Ok(self.b))?; 180 | 181 | let c = cs.new_input_variable(|| Ok(self.a * self.b + Fq::from(3)))?; 182 | 183 | cs.enforce_constraint( 184 | lc!() + a, 185 | lc!() + b, 186 | lc!() + c - (Fq::from(3), Variable::One), 187 | )?; 188 | Ok(()) 189 | } 190 | } 191 | 192 | // Tests start here 193 | 194 | #[test] 195 | fn r1cs_from_arkworks_mul_plus_three_first_constraint_c0_is_minus_three() { 196 | let a = Fq::from(2); 197 | let b = Fq::from(3); 198 | 199 | let circuit = MulPlusThree { a, b }; 200 | 201 | let cs = ConstraintSystem::new_ref(); 202 | circuit.generate_constraints(cs.clone()).unwrap(); 203 | 204 | let is_satisfied = cs.is_satisfied().unwrap(); 205 | if !is_satisfied { 206 | panic!() 207 | } 208 | 209 | let r1cs = pinocchio_r1cs_from_arkworks_cs(&cs); 210 | assert_eq!(r1cs.constraints[0].c[0], -FE::new(3)); 211 | } 212 | 213 | #[test] 214 | fn io_from_arkworks_mul_plus_three_with_0_3_is_3() { 215 | let a = Fq::from(0); 216 | let b = Fq::from(3); 217 | 218 | let circuit = MulPlusThree { a, b }; 219 | 220 | let cs = ConstraintSystem::new_ref(); 221 | circuit.generate_constraints(cs.clone()).unwrap(); 222 | 223 | let (io, _) = pinocchio_io_and_witness_from_arkworks_cs(&cs); 224 | 225 | assert_eq!(io[0], FE::new(3)) 226 | } 227 | 228 | #[test] 229 | fn io_from_arkworks_mul_plus_three_with_1_2_is_5() { 230 | let a = Fq::from(1); 231 | let b = Fq::from(2); 232 | 233 | let circuit = MulPlusThree { a, b }; 234 | 235 | let cs = ConstraintSystem::new_ref(); 236 | circuit.generate_constraints(cs.clone()).unwrap(); 237 | 238 | let is_satisfied = cs.is_satisfied().unwrap(); 239 | if !is_satisfied { 240 | panic!() 241 | } 242 | 243 | let (io, _) = pinocchio_io_and_witness_from_arkworks_cs(&cs); 244 | 245 | assert_eq!(io[0], FE::new(5)) 246 | } 247 | 248 | #[test] 249 | fn pinocchio_paper_r1cs_from_arkworks_eq_r1cs_from_pinocchio_vm() { 250 | let a = Fq::from(4); 251 | let b = Fq::from(3); 252 | let c = Fq::from(2); 253 | let d = Fq::from(1); 254 | 255 | let circuit = PinocchioPaperExampleCircuit { a, b, c, d }; 256 | 257 | let cs = ConstraintSystem::new_ref(); 258 | circuit.generate_constraints(cs.clone()).unwrap(); 259 | 260 | let is_satisfied = cs.is_satisfied().unwrap(); 261 | if !is_satisfied { 262 | panic!() 263 | } 264 | 265 | let converted_r1cs = pinocchio_r1cs_from_arkworks_cs(&cs); 266 | 267 | assert_eq!( 268 | converted_r1cs.constraints, 269 | swap_last_variables_test_circuit(&test_utils::new_test_r1cs()).constraints 270 | ); 271 | 272 | assert_eq!( 273 | converted_r1cs.number_of_inputs + converted_r1cs.number_of_outputs, 274 | test_utils::new_test_r1cs().number_of_inputs 275 | + test_utils::new_test_r1cs().number_of_outputs 276 | ); 277 | } 278 | 279 | #[test] 280 | fn pinocchio_paper_ark_witness_and_ark_io_are_correct() { 281 | let a = Fq::from(1); 282 | let b = Fq::from(2); 283 | let c = Fq::from(3); 284 | let d = Fq::from(4); 285 | 286 | let inputs_as_fe = [FE::new(1), FE::new(2), FE::new(3), FE::new(4)]; 287 | let circuit = PinocchioPaperExampleCircuit { a, b, c, d }; 288 | 289 | let cs = ConstraintSystem::new_ref(); 290 | circuit.generate_constraints(cs.clone()).unwrap(); 291 | 292 | let is_satisfied = cs.is_satisfied().unwrap(); 293 | 294 | if !is_satisfied { 295 | panic!() 296 | } 297 | 298 | let (ark_io, ark_witness) = pinocchio_io_and_witness_from_arkworks_cs(&cs); 299 | 300 | //Since the Arcworks version of the circuit has the last variables 301 | //switched, c6 and c5 needs to be switched 302 | let (c5, c6) = test_utils::test_qap_solver(inputs_as_fe); 303 | 304 | let solver_witness = vec![c5]; 305 | let mut io = inputs_as_fe.to_vec(); 306 | io.push(c6); 307 | 308 | assert_eq!(ark_witness, solver_witness); 309 | assert_eq!(ark_io, io); 310 | } 311 | 312 | /// This function changes variable 5 for 6 313 | /// our current implementation of the paper r1cs and the arkworks 314 | /// version utilizes a different index, but it is the same r1cs 315 | fn swap_last_variables_test_circuit(r1cs: &R1CS) -> R1CS { 316 | let mut updated_constraints: Vec = Vec::new(); 317 | for constraint in &r1cs.constraints { 318 | let mut updated_constraint = constraint.clone(); 319 | updated_constraint.a.swap(5, 6); 320 | updated_constraint.b.swap(5, 6); 321 | updated_constraint.c.swap(5, 6); 322 | updated_constraints.push(updated_constraint); 323 | } 324 | 325 | R1CS::new( 326 | updated_constraints, 327 | r1cs.number_of_inputs, 328 | r1cs.number_of_outputs, 329 | ) 330 | .unwrap() 331 | } 332 | 333 | #[test] 334 | fn fully_private_mul_circuit_has_1_constraint_in_pinocchio_r1cs() { 335 | let a = Fq::from(2); 336 | let b = Fq::from(3); 337 | 338 | let circuit = FullyPrivateMulCircuit { a, b }; 339 | 340 | let cs = ConstraintSystem::new_ref(); 341 | circuit.generate_constraints(cs.clone()).unwrap(); 342 | 343 | let is_satisfied = cs.is_satisfied().unwrap(); 344 | if !is_satisfied { 345 | panic!() 346 | } 347 | let r1cs = pinocchio_r1cs_from_arkworks_cs(&cs); 348 | assert_eq!(r1cs.number_of_constraints(), 1) 349 | } 350 | 351 | #[test] 352 | fn fully_private_mul_circuit_has_3_elements_in_witness_and_0_io_for_pinocchio() { 353 | let a = Fq::from(2); 354 | let b = Fq::from(3); 355 | 356 | let circuit = FullyPrivateMulCircuit { a, b }; 357 | 358 | let cs = ConstraintSystem::new_ref(); 359 | circuit.generate_constraints(cs.clone()).unwrap(); 360 | 361 | let is_satisfied = cs.is_satisfied().unwrap(); 362 | if !is_satisfied { 363 | panic!() 364 | } 365 | let (io, witness) = pinocchio_io_and_witness_from_arkworks_cs(&cs); 366 | assert_eq!(io.len(), 0); 367 | assert_eq!(witness.len(), 3); 368 | } 369 | 370 | #[test] 371 | fn priv_input_pub_output_mul_has_1_io_and_2_witness() { 372 | let a = Fq::from(2); 373 | let b = Fq::from(3); 374 | 375 | let circuit = PrivInputPubResultMulCircuit { a, b }; 376 | 377 | let cs = ConstraintSystem::new_ref(); 378 | circuit.generate_constraints(cs.clone()).unwrap(); 379 | 380 | let is_satisfied = cs.is_satisfied().unwrap(); 381 | if !is_satisfied { 382 | panic!() 383 | } 384 | 385 | let (io, witness) = pinocchio_io_and_witness_from_arkworks_cs(&cs); 386 | 387 | assert_eq!(io.len(), 1); 388 | assert_eq!(witness.len(), 2); 389 | } 390 | 391 | #[test] 392 | fn priv_input_pub_output_mul_2_3_in_pinocchio_has_io_eq_to_6() { 393 | let a = Fq::from(2); 394 | let b = Fq::from(3); 395 | 396 | let circuit = PrivInputPubResultMulCircuit { a, b }; 397 | 398 | let cs = ConstraintSystem::new_ref(); 399 | circuit.generate_constraints(cs.clone()).unwrap(); 400 | 401 | let is_satisfied = cs.is_satisfied().unwrap(); 402 | if !is_satisfied { 403 | panic!() 404 | } 405 | 406 | let (io, _) = pinocchio_io_and_witness_from_arkworks_cs(&cs); 407 | 408 | assert_eq!(io, [FE::new(6)]); 409 | } 410 | } 411 | -------------------------------------------------------------------------------- /arkworks_adapter/src/test_utils.rs: -------------------------------------------------------------------------------- 1 | use ark_relations::{ 2 | lc, 3 | r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError}, 4 | }; 5 | 6 | use crate::fq5::Fq; 7 | 8 | pub struct PinocchioPaperExampleCircuit { 9 | /// Public input 10 | pub a: Fq, 11 | /// Private input 12 | pub b: Fq, 13 | pub c: Fq, 14 | pub d: Fq, 15 | } 16 | 17 | impl ConstraintSynthesizer for PinocchioPaperExampleCircuit { 18 | fn generate_constraints(self, cs: ConstraintSystemRef) -> Result<(), SynthesisError> { 19 | let a = cs.new_input_variable(|| Ok(self.a))?; 20 | let b = cs.new_input_variable(|| Ok(self.b))?; 21 | let c = cs.new_input_variable(|| Ok(self.c))?; 22 | let d = cs.new_input_variable(|| Ok(self.d))?; 23 | 24 | let e = cs.new_witness_variable(|| Ok(self.c * self.d))?; 25 | cs.enforce_constraint(lc!() + c, lc!() + d, lc!() + e)?; 26 | 27 | let calculated_result = self.c * self.d * (self.a + self.b); 28 | 29 | let result = cs.new_input_variable(|| Ok(calculated_result))?; 30 | 31 | cs.enforce_constraint(lc!() + a + b, lc!() + e, lc!() + result)?; 32 | 33 | Ok(()) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import {} }: 2 | pkgs.mkShell { 3 | name = "cuda-env-shell"; 4 | buildInputs = with pkgs; [ 5 | git curl cargo rustc 6 | ]; 7 | } 8 | -------------------------------------------------------------------------------- /src/circuits/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod qap; 2 | pub mod r1cs; 3 | /// Shared utils for integration and unit tests 4 | pub mod test_utils; 5 | -------------------------------------------------------------------------------- /src/circuits/qap.rs: -------------------------------------------------------------------------------- 1 | use super::super::config::ORDER_R; 2 | use super::r1cs::R1CS; 3 | use crate::math::{field_element::FieldElement, polynomial::Polynomial as Poly}; 4 | use std::convert::From; 5 | 6 | type FE = FieldElement; 7 | type Polynomial = Poly; 8 | 9 | #[derive(Clone, Debug, PartialEq, Eq)] 10 | /// QAP Representation of the circuits 11 | pub struct QuadraticArithmeticProgram { 12 | pub vs: Vec, 13 | pub ws: Vec, 14 | pub ys: Vec, 15 | pub target: Polynomial, 16 | pub number_of_inputs: usize, 17 | pub number_of_outputs: usize, 18 | } 19 | 20 | #[derive(Debug, PartialEq, Eq)] 21 | pub enum CreationError { 22 | PolynomialVectorsSizeMismatch, 23 | } 24 | 25 | impl QuadraticArithmeticProgram { 26 | /// Creates a new QAP 27 | /// This expects vectors to be organized like: 28 | /// v0,w0,y0 29 | /// inputs associated v,w,y polynomials 30 | /// mid associated polynomials 31 | /// outputs associated v,w,y polynomials 32 | pub fn new( 33 | vs: Vec, 34 | ws: Vec, 35 | ys: Vec, 36 | target: Polynomial, 37 | number_of_inputs: usize, 38 | number_of_outputs: usize, 39 | ) -> Result { 40 | // TO DO: Check if the amount of inputs and outputs matches the polynomials 41 | if vs.len() != ws.len() || vs.len() != ys.len() || ws.len() != ys.len() { 42 | Err(CreationError::PolynomialVectorsSizeMismatch) 43 | } else { 44 | Ok(Self { 45 | vs, 46 | ws, 47 | ys, 48 | target, 49 | number_of_inputs, 50 | number_of_outputs, 51 | }) 52 | } 53 | } 54 | 55 | pub fn h_polynomial(&self, c: &[FE]) -> Polynomial { 56 | self.p_polynomial(c).div_with_ref(&self.target) 57 | } 58 | /// Receives C elements of a solution of the circuit 59 | /// Returns p polynomial 60 | // This along the polynomial execution should be migrated with a better 61 | // representation of the circuit 62 | pub fn p_polynomial(&self, cs: &[FE]) -> Polynomial { 63 | let v: Polynomial = self.vs[0].clone() 64 | + self.vs[1..] 65 | .iter() 66 | .zip(cs) 67 | .map(|(v, c)| v.mul_with_ref(&Polynomial::new_monomial(*c, 0))) 68 | .reduce(|x, y| x + y) 69 | .unwrap(); 70 | 71 | let w: Polynomial = self.ws[0].clone() 72 | + self.ws[1..] 73 | .iter() 74 | .zip(cs) 75 | .map(|(w, c)| w.mul_with_ref(&Polynomial::new_monomial(*c, 0))) 76 | .reduce(|x, y| x + y) 77 | .unwrap(); 78 | 79 | let y: Polynomial = self.ys[0].clone() 80 | + self.ys[1..] 81 | .iter() 82 | .zip(cs) 83 | .map(|(y, c)| y.mul_with_ref(&Polynomial::new_monomial(*c, 0))) 84 | .reduce(|x, y| x + y) 85 | .unwrap(); 86 | 87 | v * w - y 88 | } 89 | 90 | pub fn v_mid(&'_ self) -> &[Polynomial] { 91 | &self.vs[self.number_of_inputs + 1..(self.vs.len() - self.number_of_outputs)] 92 | } 93 | 94 | pub fn w_mid(&'_ self) -> &[Polynomial] { 95 | &self.ws[self.number_of_inputs + 1..(self.ws.len() - self.number_of_outputs)] 96 | } 97 | 98 | pub fn y_mid(&'_ self) -> &[Polynomial] { 99 | &self.ys[self.number_of_inputs + 1..(self.ys.len() - self.number_of_outputs)] 100 | } 101 | 102 | pub fn v_input(&'_ self) -> &[Polynomial] { 103 | &self.vs[1..self.number_of_inputs + 1] 104 | } 105 | 106 | pub fn w_input(&'_ self) -> &[Polynomial] { 107 | &self.ws[1..self.number_of_inputs + 1] 108 | } 109 | 110 | pub fn y_input(&'_ self) -> &[Polynomial] { 111 | &self.ys[1..self.number_of_inputs + 1] 112 | } 113 | 114 | pub fn v0(&'_ self) -> &Polynomial { 115 | &self.vs[0] 116 | } 117 | 118 | pub fn w0(&'_ self) -> &Polynomial { 119 | &self.ws[0] 120 | } 121 | 122 | pub fn y0(&'_ self) -> &Polynomial { 123 | &self.ys[0] 124 | } 125 | 126 | pub fn v_output(&'_ self) -> &[Polynomial] { 127 | &self.vs[(self.vs.len() - self.number_of_outputs)..] 128 | } 129 | pub fn w_output(&'_ self) -> &[Polynomial] { 130 | &self.ws[(self.ws.len() - self.number_of_outputs)..] 131 | } 132 | 133 | pub fn y_output(&'_ self) -> &[Polynomial] { 134 | &self.ys[(self.ys.len() - self.number_of_outputs)..] 135 | } 136 | } 137 | 138 | impl From for QuadraticArithmeticProgram { 139 | /// Transforms a R1CS to a QAP 140 | fn from(r1cs: R1CS) -> Self { 141 | // The r values for the qap polynomial can each be any number, 142 | // as long as there are the right amount of rs 143 | // In this case, it's set them to be 0,1,2..number_of_constraints(), 144 | // number_of_constraints non inclusive 145 | let rs: Vec = (0..r1cs.number_of_constraints() as u128) 146 | .map(FE::new) 147 | .collect(); 148 | 149 | let mut vs: Vec = Vec::with_capacity(r1cs.witness_size()); 150 | let mut ws: Vec = Vec::with_capacity(r1cs.witness_size()); 151 | let mut ys: Vec = Vec::with_capacity(r1cs.witness_size()); 152 | let mut t: Polynomial = Polynomial::new_monomial(FE::new(1), 0); 153 | 154 | for r in &rs { 155 | t = t * Polynomial::new(vec![-*r, FE::new(1)]); 156 | } 157 | 158 | for i in 0..r1cs.witness_size() { 159 | let v_ys: Vec = r1cs.constraints.iter().map(|c| c.a[i]).collect(); 160 | let w_ys: Vec = r1cs.constraints.iter().map(|c| c.b[i]).collect(); 161 | let y_ys: Vec = r1cs.constraints.iter().map(|c| c.c[i]).collect(); 162 | 163 | vs.push(Polynomial::interpolate(&rs, &v_ys)); 164 | ws.push(Polynomial::interpolate(&rs, &w_ys)); 165 | ys.push(Polynomial::interpolate(&rs, &y_ys)); 166 | } 167 | 168 | QuadraticArithmeticProgram { 169 | vs, 170 | ws, 171 | ys, 172 | target: t, 173 | number_of_inputs: r1cs.number_of_inputs, 174 | number_of_outputs: r1cs.number_of_outputs, 175 | } 176 | } 177 | } 178 | 179 | #[cfg(test)] 180 | mod tests { 181 | use crate::circuits::test_utils::{ 182 | new_test_qap, new_test_r1cs, test_qap_r5, test_qap_r6, test_qap_solver, 183 | }; 184 | 185 | use super::*; 186 | 187 | #[test] 188 | fn qap_with_different_amount_of_polynomials_should_error() { 189 | let v = vec![ 190 | Polynomial::new(vec![FE::new(1), FE::new(2)]), 191 | Polynomial::new(vec![FE::new(2), FE::new(3)]), 192 | ]; 193 | 194 | let u = v.clone(); 195 | let w = vec![Polynomial::new(vec![FE::new(1), FE::new(2)])]; 196 | let t = Polynomial::new(vec![FE::new(3)]); 197 | assert_eq!( 198 | Err(CreationError::PolynomialVectorsSizeMismatch), 199 | QuadraticArithmeticProgram::new(v, u, w, t, 2, 1) 200 | ); 201 | } 202 | 203 | #[test] 204 | fn test_circuit_v_w_y_have_7_elements() { 205 | let test_circuit = new_test_qap(); 206 | assert_eq!(test_circuit.vs.len(), 7); 207 | assert_eq!(test_circuit.ws.len(), 7); 208 | assert_eq!(test_circuit.ys.len(), 7); 209 | } 210 | 211 | //_mid polynomials of test circuit contains only one polynomial 212 | #[test] 213 | fn v_mid_test_circuit_on_r6_is_0() { 214 | let test_circuit = new_test_qap(); 215 | let r6 = test_qap_r6(); 216 | assert_eq!(test_circuit.y_mid()[0].evaluate(r6), FE::new(0)); 217 | } 218 | 219 | #[test] 220 | fn w_mid_test_circuit_has_one_element() { 221 | let test_circuit = new_test_qap(); 222 | assert_eq!(test_circuit.v_mid().len(), 1); 223 | } 224 | 225 | #[test] 226 | fn w_mid_test_circuit_on_r5_is_0() { 227 | let test_circuit = new_test_qap(); 228 | let r5 = test_qap_r5(); 229 | assert_eq!(test_circuit.w_mid()[0].evaluate(r5), FE::new(0)); 230 | } 231 | 232 | #[test] 233 | fn w_mid_test_circuit_on_r6_is_1() { 234 | let test_circuit = new_test_qap(); 235 | let r6 = test_qap_r6(); 236 | assert_eq!(test_circuit.w_mid()[0].evaluate(r6), FE::new(1)); 237 | } 238 | 239 | #[test] 240 | fn y_mid_test_circuit_on_r5_is_1() { 241 | let test_circuit = new_test_qap(); 242 | let r5 = test_qap_r5(); 243 | assert_eq!(test_circuit.y_mid()[0].evaluate(r5), FE::new(1)); 244 | } 245 | 246 | #[test] 247 | fn y_mid_test_circuit_on_r6_is_0() { 248 | let test_circuit = new_test_qap(); 249 | let r6 = test_qap_r6(); 250 | assert_eq!(test_circuit.y_mid()[0].evaluate(r6), FE::new(0)); 251 | } 252 | 253 | #[test] 254 | fn v_input_test_circuit_has_length_4() { 255 | let test_circuit = new_test_qap(); 256 | assert_eq!(test_circuit.v_input().len(), 4); 257 | } 258 | 259 | #[test] 260 | fn w_input_test_circuit_has_length_4() { 261 | let test_circuit = new_test_qap(); 262 | assert_eq!(test_circuit.w_input().len(), 4); 263 | } 264 | #[test] 265 | fn y_input_test_circuit_has_length_4() { 266 | let test_circuit = new_test_qap(); 267 | assert_eq!(test_circuit.y_input().len(), 4); 268 | } 269 | 270 | #[test] 271 | fn v_output_test_circuit_has_length_1() { 272 | let test_circuit = new_test_qap(); 273 | assert_eq!(test_circuit.v_output().len(), 1); 274 | } 275 | 276 | #[test] 277 | fn w_output_test_circuit_has_length_1() { 278 | let test_circuit = new_test_qap(); 279 | assert_eq!(test_circuit.w_output().len(), 1); 280 | } 281 | 282 | #[test] 283 | fn y_output_test_circuit_has_length_1() { 284 | let test_circuit = new_test_qap(); 285 | assert_eq!(test_circuit.y_output().len(), 1); 286 | } 287 | 288 | #[test] 289 | /// This test runs multiple cases calculated in paper 290 | /// t polynomial is tested implicitly by calculating h = p / t 291 | fn test_polynomial_h_cases() { 292 | let test_circuit = new_test_qap(); 293 | 294 | let inputs = [FE::new(1), FE::new(2), FE::new(3), FE::new(4)]; 295 | 296 | let (c5, c6) = test_qap_solver(inputs); 297 | 298 | let mut c_vector = inputs.to_vec(); 299 | c_vector.append(&mut vec![c5, c6]); 300 | 301 | assert_eq!( 302 | test_circuit.h_polynomial(&c_vector), 303 | Polynomial::new_monomial(FE::new(0), 0) 304 | ); 305 | 306 | let inputs = [FE::new(2), FE::new(2), FE::new(2), FE::new(2)]; 307 | 308 | let (c5, c6) = test_qap_solver(inputs); 309 | 310 | let mut c_vector = inputs.to_vec(); 311 | c_vector.append(&mut vec![c5, c6]); 312 | 313 | assert_eq!( 314 | test_circuit.h_polynomial(&c_vector), 315 | Polynomial::new_monomial(FE::new(4), 0) 316 | ); 317 | 318 | let inputs = [FE::new(3), FE::new(3), FE::new(3), FE::new(3)]; 319 | 320 | let (c5, c6) = test_qap_solver(inputs); 321 | 322 | let mut c_vector = inputs.to_vec(); 323 | c_vector.append(&mut vec![c5, c6]); 324 | 325 | assert_eq!( 326 | test_circuit.h_polynomial(&c_vector), 327 | Polynomial::new_monomial(FE::new(18), 0) 328 | ); 329 | 330 | let inputs = [FE::new(4), FE::new(3), FE::new(2), FE::new(1)]; 331 | 332 | let (c5, c6) = test_qap_solver(inputs); 333 | 334 | let mut c_vector = inputs.to_vec(); 335 | c_vector.append(&mut vec![c5, c6]); 336 | 337 | assert_eq!( 338 | test_circuit.h_polynomial(&c_vector), 339 | Polynomial::new_monomial(FE::new(5), 0) 340 | ); 341 | } 342 | 343 | #[test] 344 | fn test_circuit_solver_on_2_2_2_2_outputs_4_and_16() { 345 | let inputs = [FE::new(2), FE::new(2), FE::new(2), FE::new(2)]; 346 | 347 | let (c5, c6) = test_qap_solver(inputs); 348 | assert_eq!(c5, FE::new(4)); 349 | assert_eq!(c6, FE::new(16)); 350 | } 351 | 352 | #[test] 353 | fn test_circuit_solver_on_1_2_3_4_outputs_12_and_36() { 354 | let inputs = [FE::new(1), FE::new(2), FE::new(3), FE::new(4)]; 355 | 356 | let (c5, c6) = test_qap_solver(inputs); 357 | assert_eq!(c5, FE::new(12)); 358 | assert_eq!(c6, FE::new(36)); 359 | } 360 | #[test] 361 | fn test_r1cs_into_qap_is_test_qap() { 362 | let qap = new_test_qap(); 363 | let r1cs = new_test_r1cs(); 364 | let r1cs_as_qap: QuadraticArithmeticProgram = r1cs.into(); 365 | assert_eq!(qap, r1cs_as_qap); 366 | } 367 | } 368 | -------------------------------------------------------------------------------- /src/circuits/r1cs.rs: -------------------------------------------------------------------------------- 1 | use super::super::config::ORDER_R; 2 | use crate::math::field_element::FieldElement; 3 | 4 | type FE = FieldElement; 5 | 6 | #[derive(Debug, PartialEq, Eq)] 7 | pub enum CreationError { 8 | VectorsSizeMismatch, 9 | MatrixesSizeMismatch, 10 | /// Number of IOs should be less than witness size - 1 11 | InputOutputTooBig, 12 | } 13 | 14 | /// R1CS representation of an Arithmetic Program 15 | #[derive(Clone, Debug, PartialEq, Eq)] 16 | pub struct Constraint { 17 | pub a: Vec, 18 | pub b: Vec, 19 | pub c: Vec, 20 | } 21 | #[derive(Clone, Debug, PartialEq, Eq)] 22 | /// R1CS represented as a vector of constraints/gates 23 | /// Noticing joining all the first vectors of the constraints results 24 | /// in the first Matrix of the R1CS 25 | /// joining the second vectors results on the second matrix, and so on 26 | /// 27 | pub struct R1CS { 28 | pub constraints: Vec, 29 | // These are not strictly part of a R1CS 30 | // But they are data of the constraint system 31 | // that is needed to generate the proof 32 | pub number_of_inputs: usize, 33 | pub number_of_outputs: usize, 34 | } 35 | 36 | impl R1CS { 37 | #[allow(dead_code)] 38 | pub fn new( 39 | constraints: Vec, 40 | number_of_inputs: usize, 41 | number_of_outputs: usize, 42 | ) -> Result { 43 | let witness_size = constraints[0].a.len(); 44 | // Each constraints already has the correct dimensionality 45 | // a check is needed before creatin the r1cs 46 | // to be sure all constraints have the same dimension 47 | let all_same_length = constraints 48 | .iter() 49 | .all(|v| v.a.len() == constraints[0].a.len()); 50 | 51 | if !all_same_length { 52 | Err(CreationError::VectorsSizeMismatch) 53 | } else if number_of_inputs + number_of_outputs > witness_size - 1 { 54 | Err(CreationError::InputOutputTooBig) 55 | } else { 56 | Ok(Self { 57 | constraints, 58 | number_of_inputs, 59 | number_of_outputs, 60 | }) 61 | } 62 | } 63 | 64 | pub fn new_with_matrixes( 65 | a: Vec>, 66 | b: Vec>, 67 | c: Vec>, 68 | num_inputs: usize, 69 | num_outputs: usize, 70 | ) -> Result { 71 | let mut constraints: Vec = Vec::with_capacity(a.len()); 72 | // TO DO: 73 | // - Check if sizes match 74 | // - Remove clones 75 | for i in 0..a.len() { 76 | constraints.push(Constraint::new(a[i].clone(), b[i].clone(), c[i].clone()).unwrap()) 77 | } 78 | R1CS::new(constraints, num_inputs, num_outputs) 79 | } 80 | 81 | #[allow(dead_code)] 82 | pub fn verify_solution(self, s: &[FE]) -> bool { 83 | for constraint in self.constraints { 84 | if !constraint.verify_solution(s) { 85 | return false; 86 | } 87 | } 88 | true 89 | } 90 | 91 | pub fn number_of_constraints(&self) -> usize { 92 | self.constraints.len() 93 | } 94 | 95 | /// Returns the size of the witness 96 | /// This is the constant part, plus the of inputs + intermediate values + 97 | /// outputs 98 | pub fn witness_size(&self) -> usize { 99 | // all constraints have the same size 100 | // this is enforced in the creation 101 | self.constraints[0].a.len() 102 | } 103 | } 104 | 105 | impl Constraint { 106 | /// Creates a new constraint for a,b,c vectors 107 | #[allow(dead_code)] 108 | pub fn new(a: Vec, b: Vec, c: Vec) -> Result { 109 | if a.len() != b.len() || a.len() != c.len() || b.len() != c.len() { 110 | Err(CreationError::VectorsSizeMismatch) 111 | } else { 112 | Ok(Self { a, b, c }) 113 | } 114 | } 115 | 116 | #[allow(dead_code)] 117 | pub fn verify_solution(self, s: &[FE]) -> bool { 118 | inner_product(&self.a, s) * inner_product(&self.b, s) == inner_product(&self.c, s) 119 | } 120 | } 121 | 122 | pub fn inner_product(v1: &[FE], v2: &[FE]) -> FE { 123 | v1.iter() 124 | .zip(v2) 125 | .map(|(x, y)| *x * *y) 126 | .fold(FE::new(0), |x, y| x + y) 127 | } 128 | 129 | // v1.iter().zip(v2).map(|(x, y)| *x * *y).fold(FE::new(0), |x,y| x + y) 130 | 131 | #[cfg(test)] 132 | pub mod tests { 133 | use crate::circuits::test_utils::{ 134 | new_test_first_constraint, new_test_r1cs, new_test_second_constraint, 135 | }; 136 | 137 | use super::*; 138 | 139 | #[test] 140 | fn mul_vectors_2_2_3_3_equals_12() { 141 | let v1 = &[FE::new(2), FE::new(2)]; 142 | let v2 = &[FE::new(3), FE::new(3)]; 143 | 144 | assert_eq!(inner_product(v1, v2), FE::new(12)); 145 | } 146 | 147 | #[test] 148 | fn mul_vectors_3_5_equals_15() { 149 | let v1 = &[FE::new(3)]; 150 | let v2 = &[FE::new(5)]; 151 | 152 | assert_eq!(inner_product(v1, v2), FE::new(15)); 153 | } 154 | 155 | #[test] 156 | fn verify_solution_with_test_circuit_c5_constraints() { 157 | assert!(new_test_second_constraint().verify_solution(&test_solution())); 158 | } 159 | 160 | #[test] 161 | fn verify_solution_with_test_circuit_c6_constraints() { 162 | assert!(new_test_second_constraint().verify_solution(&test_solution())); 163 | } 164 | 165 | #[test] 166 | fn verify_bad_solution_with_test_circuit_c5_constraints() { 167 | let solution = vec![ 168 | FE::new(0), 169 | FE::new(0), 170 | FE::new(0), 171 | FE::new(4), 172 | FE::new(1), 173 | FE::new(0), 174 | FE::new(0), 175 | ]; 176 | assert!(!new_test_first_constraint().verify_solution(&solution)); 177 | } 178 | 179 | #[test] 180 | fn verify_bad_solution_with_test_circuit_c6_constraints() { 181 | let solution = vec![ 182 | FE::new(0), 183 | FE::new(2), 184 | FE::new(1), 185 | FE::new(4), 186 | FE::new(5), 187 | FE::new(2), 188 | FE::new(2), 189 | ]; 190 | assert!(!new_test_second_constraint().verify_solution(&solution)); 191 | } 192 | 193 | #[test] 194 | fn verify_solution_with_new_test_r1cs() { 195 | assert!(new_test_r1cs().verify_solution(&test_solution())) 196 | } 197 | 198 | #[test] 199 | fn verify_bad_solution_with_new_test_r1cs() { 200 | let solution = vec![ 201 | FE::new(0), 202 | FE::new(2), 203 | FE::new(1), 204 | FE::new(4), 205 | FE::new(5), 206 | FE::new(2), 207 | FE::new(2), 208 | ]; 209 | 210 | assert!(!new_test_r1cs().verify_solution(&solution)) 211 | } 212 | 213 | #[test] 214 | fn verify_bad_solution_because_of_second_constraint_with_new_test_r1cs() { 215 | let solution = vec![ 216 | FE::new(0), // c0 217 | FE::new(2), // c1 218 | FE::new(1), // c2 219 | FE::new(5), // c3 220 | FE::new(10), // c4 221 | FE::new(50), // c5 = c4 * c3 222 | FE::new(2), // c6 != c5 * (c1+c2), so this should fail 223 | ]; 224 | assert!(!new_test_r1cs().verify_solution(&solution)) 225 | } 226 | 227 | #[test] 228 | fn verify_bad_solution_because_of_first_constraint_with_new_test_r1cs() { 229 | let solution = vec![ 230 | FE::new(0), // c0 231 | FE::new(1), // c1 232 | FE::new(1), // c2 233 | FE::new(5), // c3 234 | FE::new(10), // c4 235 | FE::new(10), // c5 != c4 * c3 236 | FE::new(19), // c6 = c5 * (c1+c2), so this should fail 237 | ]; 238 | assert!(!new_test_r1cs().verify_solution(&solution)) 239 | } 240 | 241 | fn test_solution() -> Vec { 242 | vec![ 243 | FE::new(0), 244 | FE::new(1), 245 | FE::new(2), 246 | FE::new(3), 247 | FE::new(4), 248 | FE::new(12), 249 | FE::new(36), 250 | ] 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /src/circuits/test_utils.rs: -------------------------------------------------------------------------------- 1 | use super::super::config::ORDER_R; 2 | use super::{ 3 | qap::QuadraticArithmeticProgram as QAP, 4 | r1cs::{Constraint, R1CS}, 5 | }; 6 | use crate::math::{field_element::FieldElement, polynomial::Polynomial}; 7 | 8 | type FE = FieldElement; 9 | 10 | // r5 and r6 are exposed to help testing 11 | pub fn test_qap_r5() -> FE { 12 | FE::new(0) 13 | } 14 | 15 | pub fn test_qap_r6() -> FE { 16 | FE::new(1) 17 | } 18 | 19 | /// This is a solver for the test qap 20 | /// Inputs: c1,c2,c3,c4 circuit inputs 21 | /// Outputs: c5 intermediate result, c6 result 22 | pub fn test_qap_solver(inputs: [FE; 4]) -> (FE, FE) { 23 | let c5 = inputs[2] * inputs[3]; 24 | let c6 = (inputs[0] + inputs[1]) * c5; 25 | (c5, c6) 26 | } 27 | 28 | /// Test qap based on pinocchios paper example 29 | pub fn new_test_qap() -> QAP { 30 | let r5: FE = test_qap_r5(); 31 | let r6: FE = test_qap_r6(); 32 | 33 | let t: Polynomial = 34 | Polynomial::new(vec![-r5, FE::new(1)]) * Polynomial::new(vec![-r6, FE::new(1)]); 35 | 36 | let vs = &[ 37 | // v0 is 0 for everypoint for the circuit, since it has no constants 38 | // in the paper they don't write it 39 | Polynomial::interpolate(&[r5, r6], &[FE::new(0), FE::new(0)]), 40 | // v1..v6 are the ones explicitly written in the paper 41 | Polynomial::interpolate(&[r5, r6], &[FE::new(0), FE::new(1)]), 42 | Polynomial::interpolate(&[r5, r6], &[FE::new(0), FE::new(1)]), 43 | Polynomial::interpolate(&[r5, r6], &[FE::new(1), FE::new(0)]), 44 | Polynomial::interpolate(&[r5, r6], &[FE::new(0), FE::new(0)]), 45 | Polynomial::interpolate(&[r5, r6], &[FE::new(0), FE::new(0)]), 46 | Polynomial::interpolate(&[r5, r6], &[FE::new(0), FE::new(0)]), 47 | ]; 48 | 49 | let ws = &[ 50 | //w0 51 | Polynomial::interpolate(&[r5, r6], &[FE::new(0), FE::new(0)]), 52 | //w1 53 | Polynomial::interpolate(&[r5, r6], &[FE::new(0), FE::new(0)]), 54 | Polynomial::interpolate(&[r5, r6], &[FE::new(0), FE::new(0)]), 55 | Polynomial::interpolate(&[r5, r6], &[FE::new(0), FE::new(0)]), 56 | Polynomial::interpolate(&[r5, r6], &[FE::new(1), FE::new(0)]), 57 | Polynomial::interpolate(&[r5, r6], &[FE::new(0), FE::new(1)]), 58 | Polynomial::interpolate(&[r5, r6], &[FE::new(0), FE::new(0)]), 59 | ]; 60 | 61 | let ys = &[ 62 | //y0 63 | Polynomial::interpolate(&[r5, r6], &[FE::new(0), FE::new(0)]), 64 | //y1 65 | Polynomial::interpolate(&[r5, r6], &[FE::new(0), FE::new(0)]), 66 | Polynomial::interpolate(&[r5, r6], &[FE::new(0), FE::new(0)]), 67 | Polynomial::interpolate(&[r5, r6], &[FE::new(0), FE::new(0)]), 68 | Polynomial::interpolate(&[r5, r6], &[FE::new(0), FE::new(0)]), 69 | Polynomial::interpolate(&[r5, r6], &[FE::new(1), FE::new(0)]), 70 | Polynomial::interpolate(&[r5, r6], &[FE::new(0), FE::new(1)]), 71 | ]; 72 | 73 | QAP::new(vs.to_vec(), ws.to_vec(), ys.to_vec(), t, 4, 1).unwrap() 74 | } 75 | 76 | pub fn new_test_r1cs() -> R1CS { 77 | let constraints = vec![new_test_first_constraint(), new_test_second_constraint()]; 78 | R1CS::new(constraints, 4, 1).unwrap() 79 | } 80 | 81 | pub fn new_test_first_constraint() -> Constraint { 82 | Constraint { 83 | a: vec![ 84 | FE::new(0), 85 | FE::new(0), 86 | FE::new(0), 87 | FE::new(1), 88 | FE::new(0), 89 | FE::new(0), 90 | FE::new(0), 91 | ], 92 | b: vec![ 93 | FE::new(0), 94 | FE::new(0), 95 | FE::new(0), 96 | FE::new(0), 97 | FE::new(1), 98 | FE::new(0), 99 | FE::new(0), 100 | ], 101 | c: vec![ 102 | FE::new(0), 103 | FE::new(0), 104 | FE::new(0), 105 | FE::new(0), 106 | FE::new(0), 107 | FE::new(1), 108 | FE::new(0), 109 | ], 110 | } 111 | } 112 | 113 | pub fn new_test_second_constraint() -> Constraint { 114 | Constraint { 115 | a: vec![ 116 | FE::new(0), 117 | FE::new(1), 118 | FE::new(1), 119 | FE::new(0), 120 | FE::new(0), 121 | FE::new(0), 122 | FE::new(0), 123 | ], 124 | b: vec![ 125 | FE::new(0), 126 | FE::new(0), 127 | FE::new(0), 128 | FE::new(0), 129 | FE::new(0), 130 | FE::new(1), 131 | FE::new(0), 132 | ], 133 | c: vec![ 134 | FE::new(0), 135 | FE::new(0), 136 | FE::new(0), 137 | FE::new(0), 138 | FE::new(0), 139 | FE::new(0), 140 | FE::new(1), 141 | ], 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/config.rs: -------------------------------------------------------------------------------- 1 | // The elliptic curve is taken from the book "Pairing for beginners", page 57. 2 | // We use the short Weierstrass form equation: y^2 = x^3 + A * x + B 3 | 4 | pub const ORDER_R: u128 = 5; // Base coefficients for polynomials that encode the circuit structure. 5 | pub const ORDER_P: u128 = 59; // Base coefficients for coordinates of points in elliptic curve. 6 | pub const EMBEDDING_DEGREE: u32 = 2; // Degree to ensure that torsion group is contained in the elliptic curve over field extensions. 7 | pub const ORDER_FIELD_EXTENSION: u128 = ORDER_P.pow(EMBEDDING_DEGREE); 8 | pub const TARGET_NORMALIZATION_POWER: u128 = (ORDER_FIELD_EXTENSION - 1) / ORDER_R; 9 | pub const ELLIPTIC_CURVE_A: u128 = 1; 10 | pub const ELLIPTIC_CURVE_B: u128 = 0; 11 | pub const GENERATOR_AFFINE_X: u128 = 35; 12 | pub const GENERATOR_AFFINE_Y: u128 = 31; 13 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod circuits; 2 | pub mod config; 3 | pub mod math; 4 | pub mod pinocchio; 5 | -------------------------------------------------------------------------------- /src/math/cyclic_group.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Mul; 2 | 3 | pub trait CyclicBilinearGroup: Clone { 4 | type PairingOutput: Mul + PartialEq + Eq; 5 | /// Returns a generator of the group. Every element of the group 6 | /// has to be of the form `operate_with_self(generator(), k)` for some `k`. 7 | fn generator() -> Self; 8 | /// Returns the neutral element of the group. The equality 9 | /// `neutral_element().operate_with(g) == g` must hold 10 | /// for every group element `g`. 11 | fn neutral_element() -> Self; 12 | /// Applies the group operation `times` times with itself 13 | /// The operation can be addition or multiplication depending on 14 | /// the notation of the particular group. 15 | fn operate_with_self(&self, times: u128) -> Self; 16 | /// Applies the group operation between `self` and `other`. 17 | /// Thperation can be addition or multiplication depending on 18 | /// the notation of the particular group. 19 | fn operate_with(&self, other: &Self) -> Self; 20 | /// A bilinear map. 21 | fn pairing(&self, other: &Self) -> Self::PairingOutput; 22 | } 23 | -------------------------------------------------------------------------------- /src/math/elliptic_curve.rs: -------------------------------------------------------------------------------- 1 | use super::super::config::{ 2 | ELLIPTIC_CURVE_A, ELLIPTIC_CURVE_B, GENERATOR_AFFINE_X, GENERATOR_AFFINE_Y, ORDER_P, ORDER_R, 3 | TARGET_NORMALIZATION_POWER, 4 | }; 5 | use super::{ 6 | cyclic_group::CyclicBilinearGroup, field_element::FieldElement, 7 | field_extension_element::FieldExtensionElement, polynomial::Polynomial, 8 | }; 9 | use std::ops; 10 | 11 | type FE = FieldElement; 12 | #[allow(clippy::upper_case_acronyms)] 13 | type FEE = FieldExtensionElement; 14 | 15 | /// Represents an elliptic curve point using the projective short Weierstrass form: 16 | /// y^2 * z = x^3 + a * x * z^2 + b * z^3 17 | /// x, y and z variables are field extension elements. 18 | #[derive(Debug, Clone)] 19 | pub struct EllipticCurveElement { 20 | x: FEE, 21 | y: FEE, 22 | z: FEE, 23 | } 24 | 25 | impl EllipticCurveElement { 26 | /// Creates an elliptic curve point giving the (x, y, z) coordinates. 27 | fn new(x: FEE, y: FEE, z: FEE) -> Self { 28 | assert_eq!( 29 | Self::defining_equation(&x, &y, &z), 30 | FEE::new_base(0), 31 | "Point ({:?}, {:?}, {:?}) does not belong to the elliptic curve.", 32 | x, 33 | y, 34 | z 35 | ); 36 | Self { x, y, z } 37 | } 38 | 39 | /// Evaluates the short Weierstrass equation at (x, y z). 40 | /// Useful for checking if (x, y, z) belongs to the elliptic curve. 41 | fn defining_equation(x: &FEE, y: &FEE, z: &FEE) -> FEE { 42 | y.pow(2) * z 43 | - x.pow(3) 44 | - FEE::new_base(ELLIPTIC_CURVE_A) * x * z.pow(2) 45 | - FEE::new_base(ELLIPTIC_CURVE_B) * z.pow(3) 46 | } 47 | 48 | /// Normalize the projective coordinates to obtain affine coordinates 49 | /// of the form (x, y, 1) 50 | /// Panics if `self` is the point at infinity 51 | fn affine(&self) -> Self { 52 | assert!( 53 | self != &EllipticCurveElement::neutral_element(), 54 | "The point at infinity is not affine." 55 | ); 56 | Self::new(&self.x / &self.z, &self.y / &self.z, FEE::new_base(1)) 57 | } 58 | 59 | /// Evaluates the line between points `self` and `r` at point `q` 60 | fn line(&self, r: &Self, q: &Self) -> FEE { 61 | assert_ne!( 62 | *q, 63 | Self::neutral_element(), 64 | "q cannot be the point at infinity." 65 | ); 66 | if *self == Self::neutral_element() || *r == Self::neutral_element() { 67 | if self == r { 68 | return FEE::new_base(1); 69 | } 70 | if *self == Self::neutral_element() { 71 | &q.x - &r.x 72 | } else { 73 | &q.x - &self.x 74 | } 75 | } else if self != r { 76 | if self.x == r.x { 77 | return &q.x - &self.x; 78 | } else { 79 | let l = (&r.y - &self.y) / (&r.x - &self.x); 80 | return &q.y - &self.y - l * (&q.x - &self.x); 81 | } 82 | } else { 83 | let numerator = FEE::new_base(3) * &self.x.pow(2) + FEE::new_base(ELLIPTIC_CURVE_A); 84 | let denominator = FEE::new_base(2) * &self.y; 85 | if denominator == FEE::new_base(0) { 86 | return &q.x - &self.x; 87 | } else { 88 | let l = numerator / denominator; 89 | return &q.y - &self.y - l * (&q.x - &self.x); 90 | } 91 | } 92 | } 93 | 94 | /// Computes Miller's algorithm between points `p` and `q`. 95 | /// The implementaiton is based on Sagemath's sourcecode: 96 | /// See `_miller_` method on page 114 97 | /// https://www.sagemath.org/files/thesis/hansen-thesis-2009.pdf 98 | /// Other resources can be found at "Pairings for beginners" from Craig Costello, Algorithm 5.1, page 79. 99 | fn miller(p: &Self, q: &Self) -> FEE { 100 | let p = p.affine(); 101 | let q = q.affine(); 102 | let mut order_r = ORDER_R; 103 | let mut bs = vec![]; 104 | while order_r > 0 { 105 | bs.insert(0, order_r & 1); 106 | order_r >>= 1; 107 | } 108 | 109 | let mut f = FEE::new_base(1); 110 | let mut r = p.clone(); 111 | 112 | for b in bs[1..].iter() { 113 | let s = r.operate_with(&r).affine(); 114 | f = f.pow(2) * (r.line(&r, &q) / s.line(&-(&s), &q)); 115 | r = s; 116 | 117 | if *b == 1 { 118 | let mut s = r.operate_with(&p); 119 | if s != Self::neutral_element() { 120 | s = s.affine(); 121 | } 122 | f = f * (r.line(&p, &q) / s.line(&-(&s), &q)); 123 | r = s; 124 | } 125 | } 126 | f 127 | } 128 | 129 | /// Computes the Weil pairing between points `p` and `q`. 130 | /// See "Pairing for beginners" from Craig Costello, page 79. 131 | #[allow(unused)] 132 | fn weil_pairing(p: &Self, q: &Self) -> FEE { 133 | if *p == Self::neutral_element() || *q == Self::neutral_element() || p == q { 134 | FEE::new_base(1) 135 | } else { 136 | let numerator = Self::miller(p, q); 137 | let denominator = Self::miller(q, p); 138 | let result = numerator / denominator; 139 | -result 140 | } 141 | } 142 | 143 | /// Computes the Tate pairing between points `p` and `q`. 144 | /// See "Pairing for beginners" from Craig Costello, page 79. 145 | fn tate_pairing(p: &Self, q: &Self) -> FEE { 146 | if *p == Self::neutral_element() || *q == Self::neutral_element() || p == q { 147 | FEE::new_base(1) 148 | } else { 149 | Self::miller(p, q).pow(TARGET_NORMALIZATION_POWER) 150 | } 151 | } 152 | 153 | /// Apply a distorsion map to point `p`. 154 | /// This is useful for converting points living in the base field 155 | /// to points living in the extension field. 156 | /// The current implementation only works for the elliptic curve with A=1 and B=0 157 | /// ORDER_P=59. This curve was chosen because it is supersingular. 158 | fn distorsion_map(p: &Self) -> Self { 159 | let t = FEE::new(Polynomial::new_monomial(FE::new(1), 1)); 160 | Self::new(-&p.x, &p.y * t, p.z.clone()) 161 | } 162 | } 163 | 164 | impl PartialEq for EllipticCurveElement { 165 | fn eq(&self, other: &Self) -> bool { 166 | // Projective equality relation: first point has to be a multiple of the other 167 | (&self.x * &other.z == &self.z * &other.x) && (&self.x * &other.y == &self.y * &other.x) 168 | } 169 | } 170 | impl Eq for EllipticCurveElement {} 171 | 172 | impl ops::Neg for &EllipticCurveElement { 173 | type Output = EllipticCurveElement; 174 | 175 | fn neg(self) -> Self::Output { 176 | Self::Output::new(self.x.clone(), -self.y.clone(), self.z.clone()) 177 | } 178 | } 179 | 180 | impl ops::Neg for EllipticCurveElement { 181 | type Output = EllipticCurveElement; 182 | 183 | fn neg(self) -> Self::Output { 184 | -&self 185 | } 186 | } 187 | 188 | impl CyclicBilinearGroup for EllipticCurveElement { 189 | type PairingOutput = FEE; 190 | 191 | fn generator() -> Self { 192 | Self::new( 193 | FEE::new_base(GENERATOR_AFFINE_X), 194 | FEE::new_base(GENERATOR_AFFINE_Y), 195 | FEE::new_base(1), 196 | ) 197 | } 198 | 199 | fn neutral_element() -> Self { 200 | Self::new(FEE::new_base(0), FEE::new_base(1), FEE::new_base(0)) 201 | } 202 | 203 | fn operate_with_self(&self, times: u128) -> Self { 204 | let mut times = times; 205 | let mut result = Self::neutral_element(); 206 | let mut base = self.clone(); 207 | 208 | while times > 0 { 209 | // times % 2 == 1 210 | if times & 1 == 1 { 211 | result = result.operate_with(&base); 212 | } 213 | // times = times / 2 214 | times >>= 1; 215 | base = base.operate_with(&base); 216 | } 217 | result 218 | } 219 | 220 | /// Computes the addition of `self` and `other`. 221 | /// Taken from Moonmath (Algorithm 7, page 89) 222 | fn operate_with(&self, other: &Self) -> Self { 223 | if *other == Self::neutral_element() { 224 | self.clone() 225 | } else if *self == Self::neutral_element() { 226 | other.clone() 227 | } else { 228 | let u1 = &other.y * &self.z; 229 | let u2 = &self.y * &other.z; 230 | let v1 = &other.x * &self.z; 231 | let v2 = &self.x * &other.z; 232 | if v1 == v2 { 233 | if u1 != u2 || self.y == FEE::new_base(0) { 234 | Self::neutral_element() 235 | } else { 236 | let w = FEE::new_base(ELLIPTIC_CURVE_A) * self.z.pow(2) 237 | + FEE::new_base(3) * self.x.pow(2); 238 | let s = &self.y * &self.z; 239 | let b = &self.x * &self.y * &s; 240 | let h = w.pow(2) - FEE::new_base(8) * &b; 241 | let xp = FEE::new_base(2) * &h * &s; 242 | let yp = w * (FEE::new_base(4) * &b - &h) 243 | - FEE::new_base(8) * self.y.pow(2) * s.pow(2); 244 | let zp = FEE::new_base(8) * s.pow(3); 245 | Self::new(xp, yp, zp) 246 | } 247 | } else { 248 | let u = u1 - &u2; 249 | let v = v1 - &v2; 250 | let w = &self.z * &other.z; 251 | let a = u.pow(2) * &w - v.pow(3) - FEE::new_base(2) * v.pow(2) * &v2; 252 | let xp = &v * &a; 253 | let yp = u * (v.pow(2) * v2 - a) - v.pow(3) * u2; 254 | let zp = v.pow(3) * w; 255 | Self::new(xp, yp, zp) 256 | } 257 | } 258 | } 259 | 260 | /// Computes a Type 1 Tate pairing between `self` and `other. 261 | /// See "Pairing for beginners" from Craig Costello, section 4.2 Pairing types, page 58. 262 | /// Note that a distorsion map is applied to `other` before using the Tate pairing. 263 | /// So this method can be called with two field extension elements from the base field. 264 | fn pairing(&self, other: &Self) -> Self::PairingOutput { 265 | Self::tate_pairing(self, &Self::distorsion_map(other)) 266 | } 267 | } 268 | 269 | #[cfg(test)] 270 | mod tests { 271 | use super::*; 272 | 273 | // This tests only apply for the specific curve found in the configuration file. 274 | #[test] 275 | fn create_valid_point_works() { 276 | let point = 277 | EllipticCurveElement::new(FEE::new_base(35), FEE::new_base(31), FEE::new_base(1)); 278 | assert_eq!(point.x, FEE::new_base(35)); 279 | assert_eq!(point.y, FEE::new_base(31)); 280 | assert_eq!(point.z, FEE::new_base(1)); 281 | } 282 | 283 | #[test] 284 | #[should_panic] 285 | fn create_invalid_points_panicks() { 286 | EllipticCurveElement::new(FEE::new_base(0), FEE::new_base(1), FEE::new_base(1)); 287 | } 288 | 289 | #[test] 290 | fn operate_with_self_works() { 291 | let mut point_1 = EllipticCurveElement::generator(); 292 | point_1 = point_1.operate_with_self(ORDER_R); 293 | assert_eq!(point_1, EllipticCurveElement::neutral_element()); 294 | } 295 | 296 | #[test] 297 | fn doubling_a_point_works() { 298 | let point = 299 | EllipticCurveElement::new(FEE::new_base(35), FEE::new_base(31), FEE::new_base(1)); 300 | let expected_result = 301 | EllipticCurveElement::new(FEE::new_base(25), FEE::new_base(29), FEE::new_base(1)); 302 | assert_eq!(point.operate_with_self(2).affine(), expected_result); 303 | } 304 | 305 | #[test] 306 | fn test_weil_pairing() { 307 | let pa = EllipticCurveElement::new(FEE::new_base(35), FEE::new_base(31), FEE::new_base(1)); 308 | let pb = EllipticCurveElement::new( 309 | FEE::new(Polynomial::new(vec![FE::new(24)])), 310 | FEE::new(Polynomial::new(vec![FE::new(0), FE::new(31)])), 311 | FEE::new_base(1), 312 | ); 313 | let expected_result = FEE::new(Polynomial::new(vec![FE::new(46), FE::new(3)])); 314 | 315 | let result_weil = EllipticCurveElement::weil_pairing(&pa, &pb); 316 | assert_eq!(result_weil, expected_result); 317 | } 318 | 319 | #[test] 320 | fn test_tate_pairing() { 321 | let pa = EllipticCurveElement::new(FEE::new_base(35), FEE::new_base(31), FEE::new_base(1)); 322 | let pb = EllipticCurveElement::new( 323 | FEE::new(Polynomial::new(vec![FE::new(24)])), 324 | FEE::new(Polynomial::new(vec![FE::new(0), FE::new(31)])), 325 | FEE::new_base(1), 326 | ); 327 | let expected_result = FEE::new(Polynomial::new(vec![FE::new(42), FE::new(19)])); 328 | 329 | let result_weil = EllipticCurveElement::tate_pairing(&pa, &pb); 330 | assert_eq!(result_weil, expected_result); 331 | } 332 | } 333 | -------------------------------------------------------------------------------- /src/math/field_element.rs: -------------------------------------------------------------------------------- 1 | use super::cyclic_group::CyclicBilinearGroup; 2 | use rand::prelude::*; 3 | use std::ops; 4 | 5 | #[derive(Debug, PartialEq, Eq)] 6 | pub enum FieldElementError { 7 | OutOfRangeValue, 8 | DivisionByZero, 9 | } 10 | 11 | /// Represents an element in Fp. (E.g: 0, 1, 2 are the elements of F3) 12 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 13 | pub struct FieldElement { 14 | value: u128, 15 | } 16 | 17 | impl FieldElement { 18 | /// Creates a new field element with `value` modulo order of the field 19 | pub fn new(value: u128) -> Self { 20 | Self { 21 | value: value % ORDER, 22 | } 23 | } 24 | 25 | /// Returns a representative for a field element. 26 | /// E.g.: 8, 5 and 29 in F3 are all represented by 2. 27 | pub fn representative(&self) -> u128 { 28 | self.value 29 | } 30 | 31 | /// Returns a random element from the field. 32 | pub fn random() -> Self { 33 | let value: u128 = rand::thread_rng().gen_range(1..ORDER); 34 | FieldElement { value } 35 | } 36 | 37 | /// Returns `self` to the power of `exponent` using 38 | /// right-to-left binary method for modular exponentiation. 39 | pub fn pow(self, mut exponent: u128) -> Self { 40 | let mut result = Self::new(1); 41 | let mut base = self; 42 | 43 | while exponent > 0 { 44 | // exponent % 2 == 1 45 | if exponent & 1 == 1 { 46 | result = result * base; 47 | } 48 | // exponent = exponent / 2 49 | exponent >>= 1; 50 | base = base * base; 51 | } 52 | result 53 | } 54 | 55 | /// Computes the inverse of the element `self`. 56 | /// Based on Fermat's little theorem. 57 | pub fn inv(self) -> Result { 58 | if self.value != 0 { 59 | Ok(self.pow(ORDER - 2)) 60 | } else { 61 | Err(FieldElementError::DivisionByZero) 62 | } 63 | } 64 | } 65 | 66 | impl ops::Add> for FieldElement { 67 | type Output = FieldElement; 68 | 69 | fn add(self, a_field_element: FieldElement) -> FieldElement { 70 | FieldElement::new(self.value + a_field_element.value) 71 | } 72 | } 73 | 74 | impl ops::AddAssign for FieldElement { 75 | fn add_assign(&mut self, other: Self) { 76 | *self = *self + other; 77 | } 78 | } 79 | 80 | impl ops::Neg for FieldElement { 81 | type Output = FieldElement; 82 | 83 | fn neg(self) -> FieldElement { 84 | FieldElement::new(ORDER - self.value) 85 | } 86 | } 87 | 88 | impl ops::Sub> for FieldElement { 89 | type Output = FieldElement; 90 | 91 | fn sub(self, substrahend: FieldElement) -> FieldElement { 92 | let neg_substrahend = -substrahend; 93 | self + neg_substrahend 94 | } 95 | } 96 | 97 | impl ops::Mul for FieldElement { 98 | type Output = FieldElement; 99 | 100 | fn mul(self, a_field_element: Self) -> Self { 101 | FieldElement::new(self.value * a_field_element.value) 102 | } 103 | } 104 | 105 | impl ops::Div for FieldElement { 106 | type Output = FieldElement; 107 | 108 | #[allow(clippy::suspicious_arithmetic_impl)] 109 | fn div(self, dividend: Self) -> Self { 110 | self * dividend.inv().unwrap() 111 | } 112 | } 113 | 114 | impl CyclicBilinearGroup for FieldElement { 115 | type PairingOutput = Self; 116 | 117 | fn generator() -> FieldElement { 118 | FieldElement::new(1) 119 | } 120 | 121 | fn neutral_element() -> FieldElement { 122 | FieldElement::new(0) 123 | } 124 | 125 | fn operate_with_self(&self, times: u128) -> Self { 126 | FieldElement::new(times) * *self 127 | } 128 | 129 | fn pairing(&self, other: &Self) -> Self { 130 | *self * *other 131 | } 132 | 133 | fn operate_with(&self, other: &Self) -> Self { 134 | *self + *other 135 | } 136 | } 137 | 138 | #[cfg(test)] 139 | mod tests { 140 | use super::*; 141 | const ORDER: u128 = 13; 142 | type FE = FieldElement; 143 | 144 | #[test] 145 | fn order_must_small_as_to_not_allow_overflows() { 146 | // ORDER*ORDER < u128::MAX 147 | assert!(ORDER <= u64::MAX.into()); 148 | } 149 | 150 | #[test] 151 | fn two_plus_one_is_three() { 152 | assert_eq!(FE::new(2) + FE::new(1), FE::new(3)); 153 | } 154 | 155 | #[test] 156 | fn max_order_plus_1_is_0() { 157 | assert_eq!(FE::new(ORDER - 1) + FE::new(1), FE::new(0)); 158 | } 159 | 160 | #[test] 161 | fn when_comparing_13_and_13_they_are_equal() { 162 | let a: FE = FE::new(13); 163 | let b: FE = FE::new(13); 164 | assert_eq!(a, b); 165 | } 166 | 167 | #[test] 168 | fn when_comparing_13_and_8_they_are_different() { 169 | let a: FE = FE::new(13); 170 | let b: FE = FE::new(8); 171 | assert_ne!(a, b); 172 | } 173 | 174 | #[test] 175 | fn mul_neutral_element() { 176 | let a: FE = FE::new(1); 177 | let b: FE = FE::new(2); 178 | assert_eq!(a * b, FE::new(2)); 179 | } 180 | 181 | #[test] 182 | fn mul_2_3_is_6() { 183 | let a: FE = FE::new(2); 184 | let b: FE = FE::new(3); 185 | assert_eq!(a * b, FE::new(6)); 186 | } 187 | 188 | #[test] 189 | fn mul_order_minus_1() { 190 | let a: FE = FE::new(ORDER - 1); 191 | let b: FE = FE::new(ORDER - 1); 192 | assert_eq!(a * b, FE::new(1)); 193 | } 194 | 195 | #[test] 196 | fn inv_0_error() { 197 | let a: FE = FE::new(0); 198 | assert_eq!(a.inv().unwrap_err(), FieldElementError::DivisionByZero); 199 | } 200 | 201 | #[test] 202 | fn inv_2() { 203 | let a: FE = FE::new(2); 204 | assert_eq!(a * a.inv().unwrap(), FE::new(1)); 205 | } 206 | 207 | #[test] 208 | fn pow_2_3() { 209 | assert_eq!(FE::new(2).pow(3), FE::new(8)) 210 | } 211 | 212 | #[test] 213 | fn pow_p_minus_1() { 214 | assert_eq!(FE::new(2).pow(ORDER - 1), FE::new(1)) 215 | } 216 | 217 | #[test] 218 | fn div_1() { 219 | assert_eq!(FE::new(2) / FE::new(1), FE::new(2)) 220 | } 221 | 222 | #[test] 223 | fn div_4_2() { 224 | assert_eq!(FE::new(4) / FE::new(2), FE::new(2)) 225 | } 226 | 227 | #[test] 228 | fn div_4_3() { 229 | assert_eq!(FE::new(4) / FE::new(3) * FE::new(3), FE::new(4)) 230 | } 231 | 232 | #[test] 233 | fn two_plus_its_additive_inv_is_0() { 234 | let two = FE::new(2); 235 | 236 | assert_eq!(two + (-two), FE::new(0)) 237 | } 238 | 239 | #[test] 240 | fn four_minus_three_is_1() { 241 | let four = FE::new(4); 242 | let three = FE::new(3); 243 | 244 | assert_eq!(four - three, FE::new(1)) 245 | } 246 | 247 | #[test] 248 | fn zero_minus_1_is_order_minus_1() { 249 | let zero = FE::new(0); 250 | let one = FE::new(1); 251 | 252 | assert_eq!(zero - one, FE::new(ORDER - 1)) 253 | } 254 | 255 | #[test] 256 | fn neg_zero_is_zero() { 257 | let zero = FE::new(0); 258 | 259 | assert_eq!(-zero, zero); 260 | } 261 | 262 | #[test] 263 | fn zero_constructor_returns_zero() { 264 | assert_eq!(FE::new(0), FE::new(0)); 265 | } 266 | 267 | #[test] 268 | fn field_element_as_group_element_generator_returns_one() { 269 | assert_eq!(FE::generator(), FE::new(1)); 270 | } 271 | 272 | #[test] 273 | fn field_element_as_group_element_multiplication_by_scalar_works_as_multiplication_in_finite_fields( 274 | ) { 275 | let a = FE::new(3); 276 | let b = FE::new(12); 277 | assert_eq!(a * b, a.operate_with_self(12)); 278 | } 279 | 280 | #[test] 281 | fn field_element_as_group_element_pairing_works_as_multiplication_in_finite_fields() { 282 | let a = FE::new(3); 283 | let b = FE::new(12); 284 | assert_eq!(a * b, a.pairing(&b)); 285 | } 286 | } 287 | -------------------------------------------------------------------------------- /src/math/field_extension_element.rs: -------------------------------------------------------------------------------- 1 | use super::super::config::{ORDER_FIELD_EXTENSION, ORDER_P}; 2 | use super::{field_element::FieldElement, polynomial::Polynomial}; 3 | use std::ops; 4 | 5 | type FE = FieldElement; 6 | 7 | /// Represents an element in an extension of a prime field 8 | /// as polynomials modulo a defining polynomial. 9 | #[derive(Debug, Clone)] 10 | pub struct FieldExtensionElement { 11 | value: Polynomial, 12 | } 13 | 14 | impl FieldExtensionElement { 15 | /// Creates a `FieldExtensionElement` from a polynomial `p`. 16 | /// It keeps the remainder of dividing `p` by the defining polynomial. 17 | pub fn new(p: Polynomial) -> Self { 18 | let (_quotient, remainder) = p.long_division_with_remainder(&Self::defining_polynomial()); 19 | Self { value: remainder } 20 | } 21 | 22 | /// Creates a `FieldExtensionElement` belonging to the base prime field. 23 | pub fn new_base(value: u128) -> Self { 24 | Self::new(Polynomial::new_monomial(FE::new(value), 0)) 25 | } 26 | 27 | /// Returns the defining polynomial of the field. In this case: 28 | /// 1 + X^2 29 | /// 30 | /// This polynomial is chosen this way because the resulting field extension 31 | /// is of degree 2. With this property a type I pairing compatible elliptic curve 32 | /// is then defined. 33 | pub fn defining_polynomial() -> Polynomial { 34 | Polynomial::new(vec![FE::new(1), FE::new(0), FE::new(1)]) 35 | } 36 | 37 | /// Returns `self` to the power of `exponent` using 38 | /// right-to-left binary method for modular exponentiation. 39 | pub fn pow(&self, mut exponent: u128) -> Self { 40 | let mut result = Self::new(Polynomial::new_monomial(FE::new(1), 0)); 41 | let mut base = self.clone(); 42 | 43 | while exponent > 0 { 44 | // exponent % 2 == 1 45 | if exponent & 1 == 1 { 46 | result = &result * &base; 47 | } 48 | // exponent = exponent / 2 49 | exponent >>= 1; 50 | base = &base * &base; 51 | } 52 | result 53 | } 54 | 55 | pub fn inv(self) -> Self { 56 | assert_ne!( 57 | self.value, 58 | Polynomial::zero(), 59 | "Cannot invert the zero element." 60 | ); 61 | self.pow(ORDER_FIELD_EXTENSION - 2) 62 | } 63 | } 64 | 65 | impl PartialEq for FieldExtensionElement { 66 | fn eq(&self, other: &Self) -> bool { 67 | self.value == other.value 68 | } 69 | } 70 | impl Eq for FieldExtensionElement {} 71 | 72 | impl ops::Add<&FieldExtensionElement> for &FieldExtensionElement { 73 | type Output = FieldExtensionElement; 74 | 75 | fn add(self, a_field_element: &FieldExtensionElement) -> Self::Output { 76 | Self::Output { 77 | value: &a_field_element.value + &self.value, 78 | } 79 | } 80 | } 81 | 82 | impl ops::Add for FieldExtensionElement { 83 | type Output = FieldExtensionElement; 84 | 85 | fn add(self, a_field_element: FieldExtensionElement) -> Self::Output { 86 | &self + &a_field_element 87 | } 88 | } 89 | 90 | impl ops::Add<&FieldExtensionElement> for FieldExtensionElement { 91 | type Output = FieldExtensionElement; 92 | 93 | fn add(self, a_field_element: &FieldExtensionElement) -> Self::Output { 94 | &self + a_field_element 95 | } 96 | } 97 | 98 | impl ops::Add for &FieldExtensionElement { 99 | type Output = FieldExtensionElement; 100 | 101 | fn add(self, a_field_element: FieldExtensionElement) -> Self::Output { 102 | self + &a_field_element 103 | } 104 | } 105 | 106 | impl ops::Neg for &FieldExtensionElement { 107 | type Output = FieldExtensionElement; 108 | 109 | fn neg(self) -> Self::Output { 110 | Self::Output { 111 | value: -self.value.clone(), 112 | } 113 | } 114 | } 115 | 116 | impl ops::Neg for FieldExtensionElement { 117 | type Output = FieldExtensionElement; 118 | 119 | fn neg(self) -> Self::Output { 120 | -&self 121 | } 122 | } 123 | 124 | impl ops::Sub<&FieldExtensionElement> for &FieldExtensionElement { 125 | type Output = FieldExtensionElement; 126 | 127 | fn sub(self, substrahend: &FieldExtensionElement) -> Self::Output { 128 | self + &(-substrahend) 129 | } 130 | } 131 | 132 | impl ops::Sub for FieldExtensionElement { 133 | type Output = FieldExtensionElement; 134 | 135 | fn sub(self, substrahend: FieldExtensionElement) -> Self::Output { 136 | &self - &substrahend 137 | } 138 | } 139 | 140 | impl ops::Sub<&FieldExtensionElement> for FieldExtensionElement { 141 | type Output = FieldExtensionElement; 142 | 143 | fn sub(self, substrahend: &FieldExtensionElement) -> Self::Output { 144 | &self - substrahend 145 | } 146 | } 147 | 148 | impl ops::Sub for &FieldExtensionElement { 149 | type Output = FieldExtensionElement; 150 | 151 | fn sub(self, substrahend: FieldExtensionElement) -> Self::Output { 152 | self - &substrahend 153 | } 154 | } 155 | 156 | impl ops::Mul<&FieldExtensionElement> for &FieldExtensionElement { 157 | type Output = FieldExtensionElement; 158 | 159 | fn mul(self, a_field_extension_element: &FieldExtensionElement) -> Self::Output { 160 | let p = self.value.mul_with_ref(&a_field_extension_element.value); 161 | Self::Output::new(p) 162 | } 163 | } 164 | 165 | impl ops::Mul for FieldExtensionElement { 166 | type Output = FieldExtensionElement; 167 | 168 | fn mul(self, a_field_extension_element: FieldExtensionElement) -> Self::Output { 169 | &self * &a_field_extension_element 170 | } 171 | } 172 | 173 | impl ops::Mul<&FieldExtensionElement> for FieldExtensionElement { 174 | type Output = FieldExtensionElement; 175 | 176 | fn mul(self, a_field_extension_element: &FieldExtensionElement) -> Self::Output { 177 | &self * a_field_extension_element 178 | } 179 | } 180 | 181 | impl ops::Mul for &FieldExtensionElement { 182 | type Output = FieldExtensionElement; 183 | 184 | fn mul(self, a_field_extension_element: FieldExtensionElement) -> Self::Output { 185 | self * &a_field_extension_element 186 | } 187 | } 188 | 189 | impl ops::Div<&FieldExtensionElement> for &FieldExtensionElement { 190 | type Output = FieldExtensionElement; 191 | 192 | #[allow(clippy::suspicious_arithmetic_impl)] 193 | fn div(self, dividend: &FieldExtensionElement) -> Self::Output { 194 | self * ÷nd.clone().inv() 195 | } 196 | } 197 | 198 | impl ops::Div for FieldExtensionElement { 199 | type Output = FieldExtensionElement; 200 | 201 | fn div(self, dividend: FieldExtensionElement) -> Self::Output { 202 | &self / ÷nd 203 | } 204 | } 205 | 206 | impl ops::Div for &FieldExtensionElement { 207 | type Output = FieldExtensionElement; 208 | 209 | fn div(self, dividend: FieldExtensionElement) -> Self::Output { 210 | self / ÷nd 211 | } 212 | } 213 | 214 | impl ops::Div<&FieldExtensionElement> for FieldExtensionElement { 215 | type Output = FieldExtensionElement; 216 | 217 | fn div(self, dividend: &FieldExtensionElement) -> Self::Output { 218 | &self / dividend 219 | } 220 | } 221 | 222 | #[cfg(test)] 223 | mod tests { 224 | use super::*; 225 | 226 | #[allow(clippy::upper_case_acronyms)] 227 | type FEE = FieldExtensionElement; 228 | 229 | #[test] 230 | fn test_creating_a_field_element_extension_gets_the_remainder_of_the_polynomial() { 231 | let a = FEE::new(Polynomial::new(vec![ 232 | FE::new(2), 233 | FE::new(3), 234 | FE::new(0), 235 | FE::new(1), 236 | ])); 237 | let expected_result = FEE::new(Polynomial::new(vec![FE::new(2), FE::new(2)])); 238 | assert_eq!(a, expected_result); 239 | } 240 | 241 | #[test] 242 | fn test_add_1() { 243 | let a = FEE::new(Polynomial::new(vec![FE::new(0), FE::new(3)])); 244 | let b = FEE::new(Polynomial::new(vec![-FE::new(2), FE::new(8)])); 245 | let expected_result = FEE::new(Polynomial::new(vec![FE::new(57), FE::new(11)])); 246 | assert_eq!(a + b, expected_result); 247 | } 248 | 249 | #[test] 250 | fn test_add_2() { 251 | let a = FEE::new(Polynomial::new(vec![FE::new(12), FE::new(5)])); 252 | let b = FEE::new(Polynomial::new(vec![-FE::new(4), FE::new(2)])); 253 | let expected_result = FEE::new(Polynomial::new(vec![FE::new(8), FE::new(7)])); 254 | assert_eq!(a + b, expected_result); 255 | } 256 | 257 | #[test] 258 | fn test_sub_1() { 259 | let a = FEE::new(Polynomial::new(vec![FE::new(0), FE::new(3)])); 260 | let b = FEE::new(Polynomial::new(vec![-FE::new(2), FE::new(8)])); 261 | let expected_result = FEE::new(Polynomial::new(vec![FE::new(2), FE::new(54)])); 262 | assert_eq!(a - b, expected_result); 263 | } 264 | 265 | #[test] 266 | fn test_sub_2() { 267 | let a = FEE::new(Polynomial::new(vec![FE::new(12), FE::new(5)])); 268 | let b = FEE::new(Polynomial::new(vec![-FE::new(4), FE::new(2)])); 269 | let expected_result = FEE::new(Polynomial::new(vec![FE::new(16), FE::new(3)])); 270 | assert_eq!(a - b, expected_result); 271 | } 272 | 273 | #[test] 274 | fn test_mul_1() { 275 | let a = FEE::new(Polynomial::new(vec![FE::new(0), FE::new(3)])); 276 | let b = FEE::new(Polynomial::new(vec![-FE::new(2), FE::new(8)])); 277 | let expected_result = FEE::new(Polynomial::new(vec![FE::new(35), FE::new(53)])); 278 | assert_eq!(a * b, expected_result); 279 | } 280 | 281 | #[test] 282 | fn test_mul_2() { 283 | let a = FEE::new(Polynomial::new(vec![FE::new(12), FE::new(5)])); 284 | let b = FEE::new(Polynomial::new(vec![-FE::new(4), FE::new(2)])); 285 | let expected_result = FEE::new(Polynomial::new(vec![FE::new(1), FE::new(4)])); 286 | assert_eq!(a * b, expected_result); 287 | } 288 | 289 | #[test] 290 | fn test_div_1() { 291 | let a = FEE::new(Polynomial::new(vec![FE::new(0), FE::new(3)])); 292 | let b = FEE::new(Polynomial::new(vec![-FE::new(2), FE::new(8)])); 293 | let expected_result = FEE::new(Polynomial::new(vec![FE::new(42), FE::new(19)])); 294 | assert_eq!(a / b, expected_result); 295 | } 296 | 297 | #[test] 298 | fn test_div_2() { 299 | let a = FEE::new(Polynomial::new(vec![FE::new(12), FE::new(5)])); 300 | let b = FEE::new(Polynomial::new(vec![-FE::new(4), FE::new(2)])); 301 | let expected_result = FEE::new(Polynomial::new(vec![FE::new(4), FE::new(45)])); 302 | assert_eq!(a / b, expected_result); 303 | } 304 | 305 | #[test] 306 | fn test_pow_1() { 307 | let a = FEE::new(Polynomial::new(vec![FE::new(0), FE::new(3)])); 308 | let b = 5; 309 | let expected_result = FEE::new(Polynomial::new(vec![FE::new(0), FE::new(7)])); 310 | assert_eq!(a.pow(b), expected_result); 311 | } 312 | 313 | #[test] 314 | fn test_pow_2() { 315 | let a = FEE::new(Polynomial::new(vec![FE::new(12), FE::new(5)])); 316 | let b = 8; 317 | let expected_result = FEE::new(Polynomial::new(vec![FE::new(52), FE::new(35)])); 318 | assert_eq!(a.pow(b), expected_result); 319 | } 320 | 321 | #[test] 322 | fn test_inv_1() { 323 | let a = FEE::new(Polynomial::new(vec![FE::new(0), FE::new(3)])); 324 | let expected_result = FEE::new(Polynomial::new(vec![FE::new(0), FE::new(39)])); 325 | assert_eq!(a.inv(), expected_result); 326 | } 327 | 328 | #[test] 329 | fn test_inv() { 330 | let a = FEE::new(Polynomial::new(vec![FE::new(12), FE::new(5)])); 331 | let expected_result = FEE::new(Polynomial::new(vec![FE::new(28), FE::new(8)])); 332 | assert_eq!(a.inv(), expected_result); 333 | } 334 | } 335 | -------------------------------------------------------------------------------- /src/math/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod cyclic_group; 2 | pub mod elliptic_curve; 3 | pub mod field_element; 4 | pub mod field_extension_element; 5 | pub mod msm; 6 | pub mod polynomial; 7 | -------------------------------------------------------------------------------- /src/math/msm.rs: -------------------------------------------------------------------------------- 1 | use crate::config::ORDER_R; 2 | use crate::math::cyclic_group::CyclicBilinearGroup; 3 | use crate::math::field_element::FieldElement; 4 | 5 | type FE = FieldElement; 6 | 7 | /// This function computes the multiscalar multiplication (MSM). 8 | /// 9 | /// Assume a group G of order r is given. 10 | /// Let `hidings = [g_1, ..., g_n]` be a tuple of group points in G and 11 | /// let `cs = [k_1, ..., k_n]` be a tuple of scalars in the Galois field GF(r). 12 | /// 13 | /// Then, with additive notation, `msm(cs, hidings)` computes k_1 * g_1 + .... + k_n * g_n. 14 | /// 15 | /// If `hidings` and `cs` are empty, then `msm` returns the zero element of the group. 16 | /// 17 | /// Panics if `cs` and `hidings` have different lengths. 18 | pub fn msm(cs: &[FE], hidings: &[T]) -> T 19 | where 20 | T: CyclicBilinearGroup, 21 | { 22 | assert_eq!( 23 | cs.len(), 24 | hidings.len(), 25 | "Slices `cs` and `hidings` must be of the same length to compute `msm`." 26 | ); 27 | cs.iter() 28 | .zip(hidings.iter()) 29 | .map(|(&c, h)| h.operate_with_self(c.representative())) 30 | .reduce(|acc, x| acc.operate_with(&x)) 31 | .unwrap_or_else(T::neutral_element) 32 | } 33 | 34 | #[cfg(test)] 35 | mod tests { 36 | use super::*; 37 | use crate::math::elliptic_curve::EllipticCurveElement; 38 | 39 | #[test] 40 | fn msm_11_is_1_over_elliptic_curves() { 41 | let c = [FE::new(1)]; 42 | let hiding = [EllipticCurveElement::generator()]; 43 | assert_eq!(msm(&c, &hiding), EllipticCurveElement::generator()); 44 | } 45 | 46 | #[test] 47 | fn msm_23_is_6_over_field_elements() { 48 | let c = [FE::new(3)]; 49 | let hiding = [FE::new(2)]; 50 | assert_eq!(msm(&c, &hiding), FE::new(6)); 51 | } 52 | 53 | #[test] 54 | fn msm_23_is_6_over_elliptic_curves() { 55 | let c = [FE::new(3)]; 56 | let g = EllipticCurveElement::generator(); 57 | let hiding = [g.operate_with_self(2)]; 58 | assert_eq!(msm(&c, &hiding), g.operate_with_self(6)); 59 | } 60 | 61 | #[test] 62 | fn msm_with_c_2_3_hiding_3_4_is_18_over_field_elements() { 63 | let c = [FE::new(2), FE::new(3)]; 64 | let hiding = [FE::new(3), FE::new(4)]; 65 | assert_eq!(msm(&c, &hiding), FE::new(18)); 66 | } 67 | 68 | #[test] 69 | fn msm_with_c_2_3_hiding_3_4_is_18_over_elliptic_curves() { 70 | let c = [FE::new(2), FE::new(3)]; 71 | let g = EllipticCurveElement::generator(); 72 | let hiding = [g.operate_with_self(3), g.operate_with_self(4)]; 73 | assert_eq!(msm(&c, &hiding), g.operate_with_self(18)); 74 | } 75 | 76 | #[test] 77 | fn msm_with_empty_input_over_field_elements() { 78 | let c = []; 79 | let hiding: [FE; 0] = []; 80 | assert_eq!(msm(&c, &hiding), FE::new(0)); 81 | } 82 | 83 | #[test] 84 | fn msm_with_empty_c_is_none_over_elliptic_curves() { 85 | let c = []; 86 | let hiding: [EllipticCurveElement; 0] = []; 87 | assert_eq!(msm(&c, &hiding), EllipticCurveElement::neutral_element()); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/math/polynomial.rs: -------------------------------------------------------------------------------- 1 | use super::field_element::FieldElement; 2 | use std::ops; 3 | 4 | /// Represents the polynomial c_0 + c_1 * X + c_2 * X^2 + ... + c_n * X^n 5 | /// as a vector of coefficients `[c_0, c_1, ... , c_n]` 6 | #[derive(Debug, Clone, PartialEq, Eq)] 7 | pub struct Polynomial { 8 | coefficients: Vec>, 9 | } 10 | 11 | impl Polynomial { 12 | /// Creates a new polynomial with the given coefficients 13 | pub fn new(coefficients: Vec>) -> Self { 14 | // Removes trailing zero coefficients at the end 15 | let mut unpadded_coefficients = coefficients 16 | .into_iter() 17 | .rev() 18 | .skip_while(|x| *x == FieldElement::new(0)) 19 | .collect::>>(); 20 | unpadded_coefficients.reverse(); 21 | Polynomial { 22 | coefficients: unpadded_coefficients, 23 | } 24 | } 25 | 26 | pub fn new_monomial(coefficient: FieldElement, degree: usize) -> Self { 27 | let mut coefficients = vec![FieldElement::new(0); degree]; 28 | coefficients.push(coefficient); 29 | Self::new(coefficients) 30 | } 31 | 32 | pub fn zero() -> Self { 33 | Self::new(Vec::>::new()) 34 | } 35 | 36 | pub fn interpolate( 37 | xs: &[FieldElement], 38 | ys: &[FieldElement], 39 | ) -> Polynomial { 40 | let mut result = Polynomial::zero(); 41 | 42 | for (i, y) in ys.iter().enumerate() { 43 | let mut y_term = Polynomial::new(vec![*y]); 44 | for (j, x) in xs.iter().enumerate() { 45 | if i != j { 46 | let denominator = Polynomial::new(vec![FieldElement::new(1) / (xs[i] - *x)]); 47 | let numerator = Polynomial::new(vec![-*x, FieldElement::new(1)]); 48 | y_term = y_term.mul_with_ref(&(numerator * denominator)); 49 | } 50 | } 51 | result = result + y_term; 52 | } 53 | result 54 | } 55 | 56 | pub fn evaluate(&self, x: FieldElement) -> FieldElement { 57 | self.coefficients 58 | .iter() 59 | .enumerate() 60 | .fold(FieldElement::new(0), |acc, (i, &c)| { 61 | acc + c * x.pow(i as u128) 62 | }) 63 | } 64 | 65 | pub fn degree(&self) -> usize { 66 | if self.coefficients.is_empty() { 67 | 0 68 | } else { 69 | self.coefficients.len() - 1 70 | } 71 | } 72 | 73 | pub fn leading_coefficient(&self) -> FieldElement { 74 | if let Some(coefficient) = self.coefficients.last() { 75 | *coefficient 76 | } else { 77 | FieldElement::new(0) 78 | } 79 | } 80 | 81 | /// Returns coefficients of the polynomial as an array 82 | /// \[c_0, c_1, c_2, ..., c_n\] 83 | /// that represents the polynomial 84 | /// c_0 + c_1 * X + c_2 * X^2 + ... + c_n * X^n 85 | pub fn coefficients(&self) -> &[FieldElement] { 86 | &self.coefficients 87 | } 88 | 89 | /// Pads polynomial representations with minimum number of zeros to match lengths. 90 | fn pad_with_zero_coefficients( 91 | pa: &Polynomial, 92 | pb: &Polynomial, 93 | ) -> (Polynomial, Polynomial) { 94 | let mut pa = pa.clone(); 95 | let mut pb = pb.clone(); 96 | 97 | if pa.coefficients.len() > pb.coefficients.len() { 98 | pb.coefficients 99 | .resize(pa.coefficients.len(), FieldElement::new(0)); 100 | } else { 101 | pa.coefficients 102 | .resize(pb.coefficients.len(), FieldElement::new(0)); 103 | } 104 | (pa, pb) 105 | } 106 | 107 | /// Computes quotient and remainder of polynomial division. 108 | /// 109 | /// Output: (quotient, remainder) 110 | pub fn long_division_with_remainder(self, dividend: &Self) -> (Self, Self) { 111 | if dividend.degree() > self.degree() { 112 | (Polynomial::zero(), self) 113 | } else { 114 | let mut n = self; 115 | let mut q: Vec> = vec![FieldElement::new(0); n.degree() + 1]; 116 | while n != Polynomial::zero() && n.degree() >= dividend.degree() { 117 | let new_coefficient = n.leading_coefficient() / dividend.leading_coefficient(); 118 | q[n.degree() - dividend.degree()] = new_coefficient; 119 | let d = dividend.mul_with_ref(&Polynomial::new_monomial( 120 | new_coefficient, 121 | n.degree() - dividend.degree(), 122 | )); 123 | n = n - d; 124 | } 125 | (Polynomial::new(q), n) 126 | } 127 | } 128 | 129 | pub fn div_with_ref(self, dividend: &Self) -> Self { 130 | let (quotient, _remainder) = self.long_division_with_remainder(dividend); 131 | quotient 132 | } 133 | 134 | pub fn mul_with_ref(&self, factor: &Self) -> Self { 135 | let degree = self.degree() + factor.degree(); 136 | let mut coefficients = vec![FieldElement::new(0); degree + 1]; 137 | 138 | if self.coefficients.is_empty() || factor.coefficients.is_empty() { 139 | Polynomial::new(vec![FieldElement::new(0)]) 140 | } else { 141 | for i in 0..=factor.degree() { 142 | for j in 0..=self.degree() { 143 | coefficients[i + j] += factor.coefficients[i] * self.coefficients[j]; 144 | } 145 | } 146 | Polynomial::new(coefficients) 147 | } 148 | } 149 | } 150 | 151 | impl ops::Add<&Polynomial> for &Polynomial { 152 | type Output = Polynomial; 153 | 154 | fn add(self, a_polynomial: &Polynomial) -> Self::Output { 155 | let (pa, pb) = Polynomial::pad_with_zero_coefficients(self, a_polynomial); 156 | let iter_coeff_pa = pa.coefficients.iter(); 157 | let iter_coeff_pb = pb.coefficients.iter(); 158 | let new_coefficients = iter_coeff_pa.zip(iter_coeff_pb).map(|(&x, &y)| x + y); 159 | 160 | Polynomial::new(new_coefficients.collect()) 161 | } 162 | } 163 | 164 | impl ops::Add> for Polynomial { 165 | type Output = Polynomial; 166 | 167 | fn add(self, a_polynomial: Polynomial) -> Polynomial { 168 | &self + &a_polynomial 169 | } 170 | } 171 | 172 | impl ops::Add<&Polynomial> for Polynomial { 173 | type Output = Polynomial; 174 | 175 | fn add(self, a_polynomial: &Polynomial) -> Polynomial { 176 | &self + a_polynomial 177 | } 178 | } 179 | 180 | impl ops::Add> for &Polynomial { 181 | type Output = Polynomial; 182 | 183 | fn add(self, a_polynomial: Polynomial) -> Polynomial { 184 | self + &a_polynomial 185 | } 186 | } 187 | impl ops::Neg for Polynomial { 188 | type Output = Polynomial; 189 | 190 | fn neg(self) -> Polynomial { 191 | Polynomial::new(self.coefficients.iter().map(|&x| -x).collect()) 192 | } 193 | } 194 | 195 | impl ops::Sub> for Polynomial { 196 | type Output = Polynomial; 197 | 198 | fn sub(self, substrahend: Polynomial) -> Polynomial { 199 | self + (-substrahend) 200 | } 201 | } 202 | 203 | impl ops::Div> for Polynomial { 204 | type Output = Polynomial; 205 | 206 | fn div(self, dividend: Polynomial) -> Polynomial { 207 | self.div_with_ref(÷nd) 208 | } 209 | } 210 | 211 | impl ops::Mul> for Polynomial { 212 | type Output = Polynomial; 213 | fn mul(self, dividend: Polynomial) -> Polynomial { 214 | self.mul_with_ref(÷nd) 215 | } 216 | } 217 | 218 | #[cfg(test)] 219 | mod tests { 220 | // Some of these tests work when the finite field has order greater than 2. 221 | use super::*; 222 | const ORDER: u128 = 23; 223 | type FE = FieldElement; 224 | 225 | fn polynomial_a() -> Polynomial { 226 | Polynomial::new(vec![FE::new(1), FE::new(2), FE::new(3)]) 227 | } 228 | 229 | fn polynomial_minus_a() -> Polynomial { 230 | Polynomial::new(vec![ 231 | FE::new(ORDER - 1), 232 | FE::new(ORDER - 2), 233 | FE::new(ORDER - 3), 234 | ]) 235 | } 236 | 237 | fn polynomial_b() -> Polynomial { 238 | Polynomial::new(vec![FE::new(3), FE::new(4), FE::new(5)]) 239 | } 240 | 241 | fn polynomial_a_plus_b() -> Polynomial { 242 | Polynomial::new(vec![FE::new(4), FE::new(6), FE::new(8)]) 243 | } 244 | 245 | fn polynomial_b_minus_a() -> Polynomial { 246 | Polynomial::new(vec![FE::new(2), FE::new(2), FE::new(2)]) 247 | } 248 | 249 | #[test] 250 | fn adding_a_and_b_equals_a_plus_b() { 251 | assert_eq!(polynomial_a() + polynomial_b(), polynomial_a_plus_b()); 252 | } 253 | 254 | #[test] 255 | fn adding_a_and_a_plus_b_does_not_equal_b() { 256 | assert_ne!(polynomial_a() + polynomial_a_plus_b(), polynomial_b()); 257 | } 258 | 259 | #[test] 260 | fn add_5_to_0_is_5() { 261 | let p1 = Polynomial::new(vec![FE::new(5)]); 262 | let p2 = Polynomial::new(vec![FE::new(0)]); 263 | assert_eq!(p1 + p2, Polynomial::new(vec![FE::new(5)])); 264 | } 265 | 266 | #[test] 267 | fn add_0_to_5_is_5() { 268 | let p1 = Polynomial::new(vec![FE::new(5)]); 269 | let p2 = Polynomial::new(vec![FE::new(0)]); 270 | assert_eq!(p2 + p1, Polynomial::new(vec![FE::new(5)])); 271 | } 272 | 273 | #[test] 274 | fn negating_0_returns_0() { 275 | let p1 = Polynomial::new(vec![FE::new(0)]); 276 | assert_eq!(-p1, Polynomial::new(vec![FE::new(0)])); 277 | } 278 | 279 | #[test] 280 | fn negating_a_is_equal_to_minus_a() { 281 | assert_eq!(-polynomial_a(), polynomial_minus_a()); 282 | } 283 | 284 | #[test] 285 | fn negating_a_is_not_equal_to_a() { 286 | assert_ne!(-polynomial_a(), polynomial_a()); 287 | } 288 | 289 | #[test] 290 | fn substracting_5_5_gives_0() { 291 | let p1 = Polynomial::new(vec![FE::new(5)]); 292 | let p2 = Polynomial::new(vec![FE::new(5)]); 293 | let p3 = Polynomial::new(vec![FE::new(0)]); 294 | assert_eq!(p1 - p2, p3); 295 | } 296 | 297 | #[test] 298 | fn substracting_b_and_a_equals_b_minus_a() { 299 | assert_eq!(polynomial_b() - polynomial_a(), polynomial_b_minus_a()); 300 | } 301 | 302 | #[test] 303 | fn constructor_removes_zeros_at_the_end_of_polynomial() { 304 | let p1 = Polynomial::new(vec![FE::new(3), FE::new(4), FE::new(0)]); 305 | assert_eq!(p1.coefficients, vec![FE::new(3), FE::new(4)]); 306 | } 307 | 308 | #[test] 309 | fn pad_with_zero_coefficients_returns_polynomials_with_zeros_until_matching_size() { 310 | let p1 = Polynomial::new(vec![FE::new(3), FE::new(4)]); 311 | let p2 = Polynomial::new(vec![FE::new(3)]); 312 | 313 | assert_eq!(p2.coefficients, vec![FE::new(3)]); 314 | let (pp1, pp2) = Polynomial::pad_with_zero_coefficients(&p1, &p2); 315 | assert_eq!(pp1, p1); 316 | assert_eq!(pp2.coefficients, vec![FE::new(3), FE::new(0)]); 317 | } 318 | 319 | #[test] 320 | fn multiply_5_and_0_is_0() { 321 | let p1 = Polynomial::new(vec![FE::new(5)]); 322 | let p2 = Polynomial::new(vec![FE::new(0)]); 323 | assert_eq!(p1 * p2, Polynomial::new(vec![FE::new(0)])); 324 | } 325 | 326 | #[test] 327 | fn multiply_0_and_x_is_0() { 328 | let p1 = Polynomial::new(vec![FE::new(0)]); 329 | let p2 = Polynomial::new(vec![FE::new(0), FE::new(1)]); 330 | assert_eq!(p1 * p2, Polynomial::new(vec![FE::new(0)])); 331 | } 332 | 333 | #[test] 334 | fn multiply_2_by_3_is_6() { 335 | let p1 = Polynomial::new(vec![FE::new(2)]); 336 | let p2 = Polynomial::new(vec![FE::new(3)]); 337 | assert_eq!(p1 * p2, Polynomial::new(vec![FE::new(6)])); 338 | } 339 | 340 | #[test] 341 | fn multiply_2xx_3x_3_times_x_4() { 342 | let p1 = Polynomial::new(vec![FE::new(3), FE::new(3), FE::new(2)]); 343 | let p2 = Polynomial::new(vec![FE::new(4), FE::new(1)]); 344 | assert_eq!( 345 | p1 * p2, 346 | Polynomial::new(vec![FE::new(12), FE::new(15), FE::new(11), FE::new(2)]) 347 | ); 348 | } 349 | 350 | #[test] 351 | fn multiply_x_4_times_2xx_3x_3() { 352 | let p1 = Polynomial::new(vec![FE::new(3), FE::new(3), FE::new(2)]); 353 | let p2 = Polynomial::new(vec![FE::new(4), FE::new(1)]); 354 | assert_eq!( 355 | p2 * p1, 356 | Polynomial::new(vec![FE::new(12), FE::new(15), FE::new(11), FE::new(2)]) 357 | ); 358 | } 359 | 360 | #[test] 361 | fn division_works() { 362 | let p1 = Polynomial::new(vec![FE::new(1), FE::new(3)]); 363 | let p2 = Polynomial::new(vec![FE::new(1), FE::new(3)]); 364 | let p3 = p1.mul_with_ref(&p2); 365 | assert_eq!(p3 / p2, p1); 366 | } 367 | 368 | #[test] 369 | fn division_by_zero_degree_polynomial_works() { 370 | let four = FE::new(4); 371 | let two = FE::new(2); 372 | let p1 = Polynomial::new(vec![four, four]); 373 | let p2 = Polynomial::new(vec![two]); 374 | assert_eq!(Polynomial::new(vec![two, two]), p1 / p2); 375 | } 376 | 377 | #[test] 378 | fn evaluate_constant_polynomial_returns_constant() { 379 | let three = FE::new(3); 380 | let p = Polynomial::new(vec![three]); 381 | assert_eq!(p.evaluate(FE::new(10)), three); 382 | } 383 | 384 | #[test] 385 | fn create_degree_0_new_monomial() { 386 | assert_eq!( 387 | Polynomial::new_monomial(FE::new(3), 0), 388 | Polynomial::new(vec![FE::new(3)]) 389 | ); 390 | } 391 | 392 | #[test] 393 | fn zero_poly_evals_0_in_3() { 394 | assert_eq!( 395 | Polynomial::new_monomial(FE::new(0), 0).evaluate(FE::new(3)), 396 | FE::new(0) 397 | ); 398 | } 399 | 400 | #[test] 401 | fn evaluate_degree_1_new_monomial() { 402 | let two = FE::new(2); 403 | let four = FE::new(4); 404 | let p = Polynomial::new_monomial(two, 1); 405 | assert_eq!(p.evaluate(two), four); 406 | } 407 | 408 | #[test] 409 | fn evaluate_degree_2_monomyal() { 410 | let two = FE::new(2); 411 | let eight = FE::new(8); 412 | let p = Polynomial::new_monomial(two, 2); 413 | assert_eq!(p.evaluate(two), eight); 414 | } 415 | 416 | #[test] 417 | fn evaluate_3_term_polynomial() { 418 | let p = Polynomial::new(vec![FE::new(3), -FE::new(2), FE::new(4)]); 419 | assert_eq!(p.evaluate(FE::new(2)), FE::new(15)); 420 | } 421 | 422 | #[test] 423 | fn simple_interpolating_polynomial_by_hand_works() { 424 | let denominator = Polynomial::new(vec![FE::new(1) / (FE::new(2) - FE::new(4))]); 425 | let numerator = Polynomial::new(vec![-FE::new(4), FE::new(1)]); 426 | let interpolating = numerator * denominator; 427 | assert_eq!( 428 | (FE::new(2) - FE::new(4)) * (FE::new(1) / (FE::new(2) - FE::new(4))), 429 | FE::new(1) 430 | ); 431 | assert_eq!(interpolating.evaluate(FE::new(2)), FE::new(1)); 432 | assert_eq!(interpolating.evaluate(FE::new(4)), FE::new(0)); 433 | } 434 | 435 | #[test] 436 | fn interpolate_x_2_y_3() { 437 | let p = Polynomial::interpolate(&[FE::new(2)], &[FE::new(3)]); 438 | assert_eq!(FE::new(3), p.evaluate(FE::new(2))); 439 | } 440 | 441 | #[test] 442 | fn interpolate_x_0_2_y_3_4() { 443 | let p = Polynomial::interpolate(&[FE::new(0), FE::new(2)], &[FE::new(3), FE::new(4)]); 444 | assert_eq!(FE::new(3), p.evaluate(FE::new(0))); 445 | assert_eq!(FE::new(4), p.evaluate(FE::new(2))); 446 | } 447 | 448 | #[test] 449 | fn interpolate_x_2_5_7_y_10_19_43() { 450 | let p = Polynomial::interpolate( 451 | &[FE::new(2), FE::new(5), FE::new(7)], 452 | &[FE::new(10), FE::new(19), FE::new(43)], 453 | ); 454 | 455 | assert_eq!(FE::new(10), p.evaluate(FE::new(2))); 456 | assert_eq!(FE::new(19), p.evaluate(FE::new(5))); 457 | assert_eq!(FE::new(43), p.evaluate(FE::new(7))); 458 | } 459 | 460 | #[test] 461 | fn interpolate_x_0_0_y_1_1() { 462 | let p = Polynomial::interpolate(&[FE::new(0), FE::new(1)], &[FE::new(0), FE::new(1)]); 463 | 464 | assert_eq!(FE::new(0), p.evaluate(FE::new(0))); 465 | assert_eq!(FE::new(1), p.evaluate(FE::new(1))); 466 | } 467 | 468 | #[test] 469 | fn interpolate_x_0_y_0() { 470 | let p = Polynomial::interpolate(&[FE::new(0)], &[FE::new(0)]); 471 | assert_eq!(FE::new(0), p.evaluate(FE::new(0))); 472 | } 473 | } 474 | -------------------------------------------------------------------------------- /src/pinocchio/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod prover; 2 | pub mod setup; 3 | pub mod verifier; 4 | -------------------------------------------------------------------------------- /src/pinocchio/prover.rs: -------------------------------------------------------------------------------- 1 | use super::super::config::ORDER_R; 2 | use crate::circuits::qap::QuadraticArithmeticProgram as QAP; 3 | use crate::math::cyclic_group::CyclicBilinearGroup; 4 | use crate::math::field_element::FieldElement; 5 | use crate::math::msm::msm; 6 | 7 | use super::setup::EvaluationKey; 8 | 9 | type FE = FieldElement; 10 | 11 | /// Pinocchio's proof 12 | /// Using the notation of Pinocchio's paper, these are 13 | /// the hidings of v_{mid}(s), w_{mid}(s), y_{mid}(s), h(s) 14 | /// and the "redundant" hidings for the consistency checks 15 | /// of the verifier. 16 | /// The polynomials v, w and y are the polynomials from the QAP 17 | /// The polynomial h is equal to (vw - y) / t, where t is the 18 | /// target polynomial of the QAP. 19 | #[derive(Clone, Debug, PartialEq, Eq)] 20 | pub struct Proof { 21 | pub g_vs: T, 22 | pub g_ws: T, 23 | pub g_ys: T, 24 | pub g_hs: T, 25 | pub g_alpha_vs: T, 26 | pub g_alpha_ws: T, 27 | pub g_alpha_ys: T, 28 | pub g_beta_vwy: T, 29 | } 30 | 31 | /// Generates a proof. 32 | /// Takes as input the evaluation_key, 33 | /// the QAP representation of the circuit 34 | /// and the values of the circuit wires corresponding 35 | /// to the particular execution instance. These values are 36 | /// the ones denoted `c_i` in the paper. They include all 37 | /// inputs and outputs. 38 | pub fn generate_proof( 39 | evaluation_key: &EvaluationKey, 40 | qap: &QAP, 41 | qap_c_coefficients: &[FE], 42 | ) -> Proof { 43 | let c_mid = &qap_c_coefficients 44 | [qap.number_of_inputs..(qap_c_coefficients.len() - qap.number_of_outputs)]; 45 | 46 | let h_polynomial = qap.h_polynomial(qap_c_coefficients); 47 | 48 | Proof { 49 | g_vs: msm(c_mid, &evaluation_key.gv_ks), 50 | g_ws: msm(c_mid, &evaluation_key.gw_ks), 51 | g_ys: msm(c_mid, &evaluation_key.gy_ks), 52 | g_alpha_vs: msm(c_mid, &evaluation_key.gv_alphaks), 53 | g_alpha_ws: msm(c_mid, &evaluation_key.gw_alphaks), 54 | g_alpha_ys: msm(c_mid, &evaluation_key.gy_alphaks), 55 | g_beta_vwy: msm(c_mid, &evaluation_key.g_beta), 56 | g_hs: msm( 57 | h_polynomial.coefficients(), 58 | &evaluation_key.g_s_i[..h_polynomial.coefficients().len()], 59 | ), 60 | } 61 | } 62 | 63 | #[cfg(test)] 64 | mod tests { 65 | use crate::math::elliptic_curve::EllipticCurveElement; 66 | use crate::math::{cyclic_group::CyclicBilinearGroup, polynomial::Polynomial}; 67 | 68 | use super::*; 69 | 70 | // This test runs the proof algorithms with some easy inputs 71 | // to check operations are correct 72 | // The evaluation key and polynomials have dummy values and do 73 | // not correspond to any circuit. 74 | #[test] 75 | fn test_proof_is_generated_correctly_when_using_raw_values_instead_of_hidings() { 76 | // In this test we do not hide elements. We work with the raw values instead. 77 | // These are easier to handle and computations can be done with pen and paper. 78 | 79 | // We choose a dummy `target_qap` to be t = X + 1 80 | let target_qap = Polynomial::new(vec![FE::new(1), FE::new(1)]); 81 | 82 | // We choose vs = [t, t, t, t, t]. The same for ws, and ys. 83 | let vs = vec![target_qap.clone(); 5]; 84 | let ws = vec![target_qap.clone(); 5]; 85 | let ys = vec![target_qap.clone(); 5]; 86 | 87 | // There is 1 input and 1 output. So there are 2 middle values. 88 | let easy_qap = QAP { 89 | vs, 90 | ws, 91 | ys, 92 | target: target_qap, 93 | number_of_inputs: 1, 94 | number_of_outputs: 1, 95 | }; 96 | // Dummy evaluation key assuming 97 | // (s, r_v, r_w, alpha_v, alpha_w, alpha_y, beta, gamma) = (1, 1, 1, 2, 2, 2, 3, 1) 98 | let evaluation_key = EvaluationKey { 99 | gv_ks: vec![FE::new(2), FE::new(2)], 100 | gw_ks: vec![FE::new(2), FE::new(2)], 101 | gy_ks: vec![FE::new(2), FE::new(2)], 102 | gv_alphaks: vec![FE::new(4), FE::new(4)], 103 | gw_alphaks: vec![FE::new(4), FE::new(4)], 104 | gy_alphaks: vec![FE::new(4), FE::new(4)], 105 | g_s_i: vec![FE::new(1), FE::new(1)], 106 | g_beta: vec![FE::new(18), FE::new(18)], 107 | }; 108 | 109 | let c_coefficients = vec![ 110 | // c1 111 | FE::new(3), 112 | // c_mids: 113 | // c_2 114 | FE::new(2), 115 | // c_3 116 | FE::new(1), 117 | // output: 118 | // c4 119 | FE::new(1), 120 | ]; 121 | 122 | let proof = generate_proof(&evaluation_key, &easy_qap, &c_coefficients); 123 | 124 | // For this choice of dummy polynomials and circuit wire values, we obtain 125 | // v = t + c_1 * t + c_2 * t + c_3 * t + c_4 * t, and v_{mid} = c_2 * t + c_3 * t. 126 | // And similarly for v and w. 127 | // Since in our evaluation key we are assuming that the component `s` 128 | // of the toxic waste is 1, we have v_{mid}(s) = 2 * t(1) + 1 * t(1) = 6. 129 | assert_eq!(proof.g_vs, FE::new(6)); 130 | assert_eq!(proof.g_ws, FE::new(6)); 131 | assert_eq!(proof.g_ys, FE::new(6)); 132 | assert_eq!(proof.g_alpha_vs, FE::new(2 * 6)); 133 | assert_eq!(proof.g_alpha_ws, FE::new(2 * 6)); 134 | assert_eq!(proof.g_alpha_ws, FE::new(2 * 6)); 135 | 136 | // On the other hand p = vw - y = r^2 * t^2 - r * t, 137 | // where r = 1 + c_1 + c_2 + c_3 + c_4 = 8. 138 | // Therefore h = p / t = r^2 * t - r = 64 * t - 8. 139 | // we have h(s) = h(1) = 64 * t(1) - 8 = 64 * 2 - 8 = 120. 140 | assert_eq!(proof.g_hs, FE::new(120)); 141 | 142 | assert_eq!(proof.g_beta_vwy, FE::new(3 * 6 + 3 * 6 + 3 * 6)); 143 | } 144 | 145 | #[test] 146 | fn test_proof_is_generated_correctly_when_hiding_in_elliptic_curves() { 147 | // Same test as before, but this time hiding points in elliptic curves 148 | let target_qap = Polynomial::new(vec![FE::new(1), FE::new(1)]); 149 | let vs = vec![target_qap.clone(); 5]; 150 | let ws = vec![target_qap.clone(); 5]; 151 | let ys = vec![target_qap.clone(); 5]; 152 | 153 | let easy_qap = QAP { 154 | vs, 155 | ws, 156 | ys, 157 | target: target_qap, 158 | number_of_inputs: 1, 159 | number_of_outputs: 1, 160 | }; 161 | 162 | let g = EllipticCurveElement::generator(); 163 | 164 | let evaluation_key = EvaluationKey { 165 | gv_ks: vec![g.operate_with_self(2), g.operate_with_self(2)], 166 | gw_ks: vec![g.operate_with_self(2), g.operate_with_self(2)], 167 | gy_ks: vec![g.operate_with_self(2), g.operate_with_self(2)], 168 | gv_alphaks: vec![g.operate_with_self(4), g.operate_with_self(4)], 169 | gw_alphaks: vec![g.operate_with_self(4), g.operate_with_self(4)], 170 | gy_alphaks: vec![g.operate_with_self(4), g.operate_with_self(4)], 171 | g_s_i: vec![g.clone(), g.clone()], 172 | g_beta: vec![g.operate_with_self(18), g.operate_with_self(18)], 173 | }; 174 | 175 | let c_coefficients = vec![FE::new(3), FE::new(2), FE::new(1), FE::new(1)]; 176 | 177 | let proof = generate_proof(&evaluation_key, &easy_qap, &c_coefficients); 178 | 179 | assert_eq!(proof.g_vs, g.operate_with_self(6)); 180 | assert_eq!(proof.g_ws, g.operate_with_self(6)); 181 | assert_eq!(proof.g_ys, g.operate_with_self(6)); 182 | assert_eq!(proof.g_alpha_vs, g.operate_with_self(2 * 6)); 183 | assert_eq!(proof.g_alpha_ws, g.operate_with_self(2 * 6)); 184 | assert_eq!(proof.g_alpha_ws, g.operate_with_self(2 * 6)); 185 | 186 | assert_eq!(proof.g_hs, g.operate_with_self(120)); 187 | 188 | assert_eq!(proof.g_beta_vwy, g.operate_with_self(3 * 6 + 3 * 6 + 3 * 6)); 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/pinocchio/setup.rs: -------------------------------------------------------------------------------- 1 | use super::super::config::ORDER_R; 2 | use crate::circuits::qap::QuadraticArithmeticProgram as QAP; 3 | use crate::math; 4 | use math::cyclic_group::CyclicBilinearGroup; 5 | use math::field_element::FieldElement; 6 | 7 | pub type FE = FieldElement; 8 | 9 | /// Evaluation key for Pinocchio 10 | #[derive(Clone, Debug, PartialEq, Eq)] 11 | pub struct EvaluationKey { 12 | pub gv_ks: Vec, 13 | pub gw_ks: Vec, 14 | pub gy_ks: Vec, 15 | pub gv_alphaks: Vec, 16 | pub gw_alphaks: Vec, 17 | pub gy_alphaks: Vec, 18 | pub g_s_i: Vec, 19 | pub g_beta: Vec, 20 | } 21 | /// Verifying key for Pinocchio 22 | #[derive(Clone, Debug, PartialEq, Eq)] 23 | pub struct VerificationKey { 24 | pub g_1: T, 25 | pub g_alpha_v: T, 26 | pub g_alpha_w: T, 27 | pub g_alpha_y: T, 28 | pub g_gamma: T, 29 | pub g_beta_gamma: T, 30 | pub gy_target_on_s: T, 31 | pub gv_ks: Vec, 32 | pub gw_ks: Vec, 33 | pub gy_ks: Vec, 34 | } 35 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 36 | pub struct ToxicWaste { 37 | s: FE, 38 | alpha_v: FE, 39 | alpha_w: FE, 40 | alpha_y: FE, 41 | beta: FE, 42 | rv: FE, 43 | rw: FE, 44 | gamma: FE, 45 | } 46 | 47 | impl ToxicWaste { 48 | pub fn ry(self) -> FE { 49 | self.rv * self.rw 50 | } 51 | 52 | #[allow(clippy::too_many_arguments)] 53 | pub fn new( 54 | s: FE, 55 | alpha_v: FE, 56 | alpha_w: FE, 57 | alpha_y: FE, 58 | beta: FE, 59 | rv: FE, 60 | rw: FE, 61 | gamma: FE, 62 | ) -> Self { 63 | Self { 64 | s, 65 | alpha_v, 66 | alpha_w, 67 | alpha_y, 68 | beta, 69 | rv, 70 | rw, 71 | gamma, 72 | } 73 | } 74 | 75 | pub fn sample() -> Self { 76 | Self { 77 | s: FE::random(), 78 | alpha_v: FE::random(), 79 | alpha_w: FE::random(), 80 | alpha_y: FE::random(), 81 | beta: FE::random(), 82 | rv: FE::random(), 83 | rw: FE::random(), 84 | gamma: FE::random(), 85 | } 86 | } 87 | } 88 | 89 | fn generate_verification_key( 90 | qap: &QAP, 91 | toxic_waste: &ToxicWaste, 92 | generator: &T, 93 | ) -> VerificationKey { 94 | let s = toxic_waste.s; 95 | let alpha_v = toxic_waste.alpha_v; 96 | let alpha_w = toxic_waste.alpha_w; 97 | let alpha_y = toxic_waste.alpha_y; 98 | let beta = toxic_waste.beta; 99 | let rv = toxic_waste.rv; 100 | let rw = toxic_waste.rw; 101 | let gamma = toxic_waste.gamma; 102 | let ry = toxic_waste.ry(); 103 | 104 | let g = generator; 105 | 106 | let vector_capacity = qap.number_of_inputs + qap.number_of_inputs + 1; 107 | let mut gv_ks_io: Vec = Vec::with_capacity(vector_capacity); 108 | let mut gw_ks_io: Vec = Vec::with_capacity(vector_capacity); 109 | let mut gy_ks_io: Vec = Vec::with_capacity(vector_capacity); 110 | 111 | gv_ks_io.push(g.operate_with_self((rv * qap.v0().evaluate(s)).representative())); 112 | gw_ks_io.push(g.operate_with_self((rw * qap.w0().evaluate(s)).representative())); 113 | gy_ks_io.push(g.operate_with_self((ry * qap.y0().evaluate(s)).representative())); 114 | 115 | for k in 0..qap.v_input().len() { 116 | gv_ks_io.push(g.operate_with_self((rv * qap.v_input()[k].evaluate(s)).representative())); 117 | gw_ks_io.push(g.operate_with_self((rw * qap.w_input()[k].evaluate(s)).representative())); 118 | gy_ks_io.push(g.operate_with_self((ry * qap.y_input()[k].evaluate(s)).representative())); 119 | } 120 | 121 | for k in 0..qap.v_output().len() { 122 | gv_ks_io.push(g.operate_with_self((rv * qap.v_output()[k].evaluate(s)).representative())); 123 | gw_ks_io.push(g.operate_with_self((rw * qap.w_output()[k].evaluate(s)).representative())); 124 | gy_ks_io.push(g.operate_with_self((ry * qap.y_output()[k].evaluate(s)).representative())); 125 | } 126 | 127 | VerificationKey { 128 | g_1: g.clone(), 129 | g_alpha_v: g.operate_with_self(alpha_v.representative()), 130 | g_alpha_w: g.operate_with_self(alpha_w.representative()), 131 | g_alpha_y: g.operate_with_self(alpha_y.representative()), 132 | g_gamma: g.operate_with_self(gamma.representative()), 133 | g_beta_gamma: g.operate_with_self((beta * gamma).representative()), 134 | gy_target_on_s: g.operate_with_self((ry * qap.target.evaluate(s)).representative()), 135 | gv_ks: gv_ks_io, 136 | gw_ks: gw_ks_io, 137 | gy_ks: gy_ks_io, 138 | } 139 | } 140 | 141 | fn generate_evaluation_key( 142 | qap: &QAP, 143 | toxic_waste: &ToxicWaste, 144 | generator: &T, 145 | ) -> EvaluationKey { 146 | let (vs_mid, ws_mid, ys_mid) = (qap.v_mid(), qap.w_mid(), qap.y_mid()); 147 | 148 | let s = toxic_waste.s; 149 | let alpha_v = toxic_waste.alpha_v; 150 | let alpha_w = toxic_waste.alpha_w; 151 | let alpha_y = toxic_waste.alpha_y; 152 | let beta = toxic_waste.beta; 153 | let rv = toxic_waste.rv; 154 | let rw = toxic_waste.rw; 155 | let ry = toxic_waste.ry(); 156 | 157 | let g = generator; 158 | 159 | let degree = qap.target.degree(); 160 | 161 | let mut gv_ks_mid: Vec = Vec::with_capacity(vs_mid.len()); 162 | let mut gw_ks_mid: Vec = Vec::with_capacity(vs_mid.len()); 163 | let mut gy_ks_mid: Vec = Vec::with_capacity(vs_mid.len()); 164 | let mut gv_alphaks_mid: Vec = Vec::with_capacity(vs_mid.len()); 165 | let mut gw_alphaks_mid: Vec = Vec::with_capacity(vs_mid.len()); 166 | let mut gy_alphaks_mid: Vec = Vec::with_capacity(vs_mid.len()); 167 | let mut g_beta_mid: Vec = Vec::with_capacity(vs_mid.len()); 168 | // g_s_i is the only paramater to depend on the degree of the qap 169 | // This is an upper bound, it could be smaller 170 | let mut g_s_i: Vec = Vec::with_capacity(degree); 171 | 172 | // Set evaluation keys for each of their respective k mid element 173 | for k in 0..vs_mid.len() { 174 | gv_ks_mid.push(g.operate_with_self((rv * vs_mid[k].evaluate(s)).representative())); 175 | gw_ks_mid.push(g.operate_with_self((rw * ws_mid[k].evaluate(s)).representative())); 176 | gy_ks_mid.push(g.operate_with_self((ry * ys_mid[k].evaluate(s)).representative())); 177 | gv_alphaks_mid 178 | .push(g.operate_with_self((ry * alpha_v * vs_mid[k].evaluate(s)).representative())); 179 | gw_alphaks_mid 180 | .push(g.operate_with_self((rw * alpha_w * ws_mid[k].evaluate(s)).representative())); 181 | gy_alphaks_mid 182 | .push(g.operate_with_self((ry * alpha_y * ys_mid[k].evaluate(s)).representative())); 183 | g_beta_mid.push( 184 | g.operate_with_self( 185 | (rv * beta * vs_mid[k].evaluate(s) 186 | + rw * beta * ws_mid[k].evaluate(s) 187 | + ry * beta * ys_mid[k].evaluate(s)) 188 | .representative(), 189 | ), 190 | ) 191 | } 192 | 193 | for i in 0..qap.target.degree() { 194 | // This unwrap would only fail in an OS 195 | // with 256 bits pointer, which doesn't exist 196 | g_s_i.push(g.operate_with_self(s.pow(i.try_into().unwrap()).representative())); 197 | } 198 | 199 | EvaluationKey { 200 | gv_ks: gv_ks_mid, 201 | gw_ks: gw_ks_mid, 202 | gy_ks: gy_ks_mid, 203 | gv_alphaks: gv_alphaks_mid, 204 | gw_alphaks: gw_alphaks_mid, 205 | gy_alphaks: gy_alphaks_mid, 206 | g_s_i, 207 | g_beta: g_beta_mid, 208 | } 209 | } 210 | 211 | pub fn setup( 212 | qap: &QAP, 213 | toxic_waste: &ToxicWaste, 214 | ) -> (EvaluationKey, VerificationKey) { 215 | let generator = T::generator(); 216 | ( 217 | generate_evaluation_key(qap, toxic_waste, &generator), 218 | generate_verification_key(qap, toxic_waste, &generator), 219 | ) 220 | } 221 | 222 | #[cfg(test)] 223 | mod tests { 224 | use super::*; 225 | use super::{setup, ToxicWaste}; 226 | use crate::circuits::test_utils::new_test_qap; 227 | 228 | fn identity_toxic_waste() -> ToxicWaste { 229 | ToxicWaste { 230 | s: FE::new(1), 231 | alpha_v: FE::new(1), 232 | alpha_w: FE::new(1), 233 | alpha_y: FE::new(1), 234 | beta: FE::new(1), 235 | rv: FE::new(1), 236 | rw: FE::new(1), 237 | gamma: FE::new(1), 238 | } 239 | } 240 | 241 | #[test] 242 | fn evaluation_keys_size_for_test_circuit_is_1_for_each_key() { 243 | let (eval_key, _): (EvaluationKey, VerificationKey) = 244 | setup(&new_test_qap(), &identity_toxic_waste()); 245 | assert_eq!(eval_key.gv_ks.len(), 1); 246 | assert_eq!(eval_key.gw_ks.len(), 1); 247 | assert_eq!(eval_key.gy_ks.len(), 1); 248 | assert_eq!(eval_key.gv_alphaks.len(), 1); 249 | assert_eq!(eval_key.gw_alphaks.len(), 1); 250 | assert_eq!(eval_key.gy_alphaks.len(), 1); 251 | assert_eq!(eval_key.g_beta.len(), 1); 252 | } 253 | 254 | // This test makes a manual computation of each field and compares it 255 | // with the given values of the eval key 256 | #[test] 257 | fn eval_key_returns_appropiate_values() { 258 | let r5 = FE::new(2); 259 | let tw = ToxicWaste { 260 | s: FE::new(2), 261 | alpha_v: FE::new(2), 262 | alpha_w: FE::new(2), 263 | alpha_y: FE::new(2), 264 | beta: FE::new(2), 265 | rv: FE::new(2), 266 | rw: FE::new(2), 267 | gamma: FE::new(1), 268 | }; 269 | 270 | let g = FE::generator(); 271 | let test_circuit = new_test_qap(); 272 | 273 | let (eval_key, _): (EvaluationKey, VerificationKey) = setup(&test_circuit, &tw); 274 | 275 | // These keys should be the same evaluation * rv, which is two 276 | assert_eq!( 277 | eval_key.gv_ks[0], 278 | g.operate_with_self( 279 | (test_circuit.v_mid()[0].evaluate(r5) * FE::new(2)).representative() 280 | ) 281 | ); 282 | assert_eq!( 283 | eval_key.gw_ks[0], 284 | g.operate_with_self( 285 | (test_circuit.w_mid()[0].evaluate(r5) * FE::new(2)).representative() 286 | ) 287 | ); 288 | // These keys should be the same evaluation * ys, which is two 289 | // Since the whole thing is 0 290 | assert_eq!( 291 | eval_key.gy_ks[0], 292 | g.operate_with_self( 293 | (test_circuit.y_mid()[0].evaluate(r5) * FE::new(4)).representative() 294 | ) 295 | ); 296 | 297 | // alpha * rv and alpha * rw is 4 298 | assert_eq!( 299 | eval_key.gv_alphaks[0], 300 | g.operate_with_self( 301 | (test_circuit.v_mid()[0].evaluate(r5) * FE::new(4)).representative() 302 | ) 303 | ); 304 | assert_eq!( 305 | eval_key.gv_alphaks[0], 306 | g.operate_with_self( 307 | (test_circuit.v_mid()[0].evaluate(r5) * FE::new(4)).representative() 308 | ) 309 | ); 310 | // alpha * ry and alpha * rw is 8 311 | assert_eq!( 312 | eval_key.gv_alphaks[0], 313 | g.operate_with_self( 314 | (test_circuit.v_mid()[0].evaluate(r5) * FE::new(8)).representative() 315 | ) 316 | ); 317 | 318 | assert_eq!( 319 | eval_key.g_beta[0], 320 | // beta * rv is 4 321 | g.operate_with_self( 322 | (test_circuit.v_mid()[0].evaluate(r5) * FE::new(4) + 323 | test_circuit.w_mid()[0].evaluate(r5) * FE::new(4) + 324 | // beta * ry is 8 325 | test_circuit.y_mid()[0].evaluate(r5) * FE::new(8)) 326 | .representative() 327 | ) 328 | ) 329 | } 330 | 331 | #[test] 332 | fn verification_key_gvks_has_length_6_for_test_circuit() { 333 | let (_, vk): (EvaluationKey, VerificationKey) = 334 | setup(&new_test_qap(), &identity_toxic_waste()); 335 | assert_eq!(vk.gv_ks.len(), 6); 336 | assert_eq!(vk.gw_ks.len(), 6); 337 | assert_eq!(vk.gy_ks.len(), 6); 338 | } 339 | } 340 | -------------------------------------------------------------------------------- /src/pinocchio/verifier.rs: -------------------------------------------------------------------------------- 1 | use super::super::config::ORDER_R; 2 | use super::prover::Proof; 3 | use super::setup::VerificationKey; 4 | use crate::math::{self, cyclic_group::CyclicBilinearGroup}; 5 | use math::field_element::FieldElement; 6 | use math::msm::msm; 7 | 8 | type FE = FieldElement; 9 | 10 | /// Pinocchio's verification algorithm. 11 | pub fn verify( 12 | verification_key: &VerificationKey, 13 | proof: &Proof, 14 | c_input_output: &[FE], 15 | ) -> bool { 16 | let b1 = check_divisibility(verification_key, proof, c_input_output); 17 | let b2 = check_appropiate_spans(verification_key, proof); 18 | let b3 = check_same_linear_combinations(verification_key, proof); 19 | b1 && b2 && b3 20 | } 21 | 22 | pub fn check_divisibility( 23 | verification_key: &VerificationKey, 24 | proof: &Proof, 25 | input_output: &[FE], 26 | ) -> bool { 27 | let vk = verification_key; 28 | 29 | let hiding_v = vk.gv_ks[0] 30 | .operate_with(&msm(input_output, &vk.gv_ks[1..])) 31 | .operate_with(&proof.g_vs); 32 | let hiding_w = vk.gw_ks[0] 33 | .operate_with(&msm(input_output, &vk.gw_ks[1..])) 34 | .operate_with(&proof.g_ws); 35 | let hiding_y = vk.gy_ks[0] 36 | .operate_with(&msm(input_output, &vk.gy_ks[1..])) 37 | .operate_with(&proof.g_ys); 38 | 39 | let lhs = hiding_v.pairing(&hiding_w); 40 | let rhs_1 = verification_key.gy_target_on_s.pairing(&proof.g_hs); 41 | let rhs_2 = hiding_y.pairing(&vk.g_1); 42 | 43 | lhs == rhs_1 * rhs_2 44 | } 45 | 46 | pub fn check_appropiate_spans( 47 | verification_key: &VerificationKey, 48 | proof: &Proof, 49 | ) -> bool { 50 | let vk = verification_key; 51 | 52 | let b1 = proof.g_alpha_vs.pairing(&vk.g_1) == proof.g_vs.pairing(&vk.g_alpha_v); 53 | let b2 = proof.g_alpha_ws.pairing(&vk.g_1) == proof.g_ws.pairing(&vk.g_alpha_w); 54 | let b3 = proof.g_alpha_ys.pairing(&vk.g_1) == proof.g_ys.pairing(&vk.g_alpha_y); 55 | b1 && b2 && b3 56 | } 57 | 58 | pub fn check_same_linear_combinations( 59 | verification_key: &VerificationKey, 60 | proof: &Proof, 61 | ) -> bool { 62 | let vk = verification_key; 63 | 64 | proof.g_beta_vwy.pairing(&vk.g_gamma) 65 | == (proof 66 | .g_vs 67 | .operate_with(&proof.g_ws) 68 | .operate_with(&proof.g_ys)) 69 | .pairing(&vk.g_beta_gamma) 70 | } 71 | 72 | #[cfg(test)] 73 | mod tests { 74 | use super::*; 75 | use crate::math::elliptic_curve::EllipticCurveElement; 76 | 77 | // In this tests we do not hide elements. We work with the raw values instead. 78 | // These are easier to handle and computations can be done with pen and paper. 79 | // Check out the prover tests to see where most of these values come from. 80 | fn dummy_verification_data_without_hidings() -> (VerificationKey, Proof, Vec) { 81 | // Dummy verification key assuming 82 | // (s, r_v, r_w, alpha_v, alpha_w, alpha_y, beta, gamma) = (1, 1, 1, 2, 2, 2, 3, 1) 83 | let verification_key = VerificationKey { 84 | g_1: FE::new(1), 85 | g_alpha_v: FE::new(2), 86 | g_alpha_w: FE::new(2), 87 | g_alpha_y: FE::new(2), 88 | g_gamma: FE::new(3), 89 | g_beta_gamma: FE::new(3), 90 | gy_target_on_s: FE::new(2), 91 | gv_ks: vec![FE::new(0), FE::new(6), FE::new(6)], 92 | gw_ks: vec![FE::new(0), FE::new(6), FE::new(6)], 93 | gy_ks: vec![FE::new(0), FE::new(6), FE::new(6)], 94 | }; 95 | 96 | let input_output = vec![ 97 | // One input value 98 | FE::new(3), 99 | // One output value 100 | FE::new(1), 101 | ]; 102 | 103 | // This is a valid proof for the above input and output values. 104 | // See the prover tests to see where this comes from. 105 | let proof = Proof { 106 | g_vs: FE::new(6), 107 | g_ws: FE::new(6), 108 | g_ys: FE::new(6), 109 | g_hs: FE::new(120), 110 | g_alpha_vs: FE::new(12), 111 | g_alpha_ws: FE::new(12), 112 | g_alpha_ys: FE::new(12), 113 | g_beta_vwy: FE::new(18), 114 | }; 115 | 116 | (verification_key, proof, input_output) 117 | } 118 | 119 | #[test] 120 | fn test_divisibility_check_on_correct_proof_without_hidings() { 121 | let (verification_key, proof, input_output) = dummy_verification_data_without_hidings(); 122 | assert!(check_divisibility(&verification_key, &proof, &input_output)); 123 | } 124 | 125 | #[test] 126 | fn test_appropiate_spans_on_correct_proof_without_hidings() { 127 | let (verification_key, proof, _) = dummy_verification_data_without_hidings(); 128 | assert!(check_appropiate_spans(&verification_key, &proof)); 129 | } 130 | 131 | #[test] 132 | fn test_same_linear_combinations_on_correct_proof_without_hidings() { 133 | let (verification_key, proof, _) = dummy_verification_data_without_hidings(); 134 | assert!(check_same_linear_combinations(&verification_key, &proof)); 135 | } 136 | 137 | #[test] 138 | fn test_divisibility_check_correct_on_incorrect_input_output_without_hidings() { 139 | let (verification_key, proof, _) = dummy_verification_data_without_hidings(); 140 | let input_output = vec![FE::new(0), FE::new(0)]; 141 | assert!(!check_divisibility( 142 | &verification_key, 143 | &proof, 144 | &input_output 145 | )); 146 | } 147 | 148 | #[test] 149 | fn test_appropiate_spans_correct_on_incorrect_proof_without_hidings() { 150 | let (verification_key, mut proof, _) = dummy_verification_data_without_hidings(); 151 | proof.g_alpha_vs = FE::new(0); 152 | assert!(!check_appropiate_spans(&verification_key, &proof)); 153 | } 154 | 155 | #[test] 156 | fn test_same_linear_combinations_on_incorrect_proof_without_hidings() { 157 | let (verification_key, mut proof, _) = dummy_verification_data_without_hidings(); 158 | proof.g_beta_vwy = FE::new(0); 159 | assert!(!check_same_linear_combinations(&verification_key, &proof)); 160 | } 161 | 162 | // The following is the same as before but with hidings on elliptic curves. 163 | fn dummy_verification_data_with_elliptic_curve_hidings() -> ( 164 | VerificationKey, 165 | Proof, 166 | Vec, 167 | ) { 168 | let g = EllipticCurveElement::generator(); 169 | // Dummy verification key assuming 170 | // (s, r_v, r_w, alpha_v, alpha_w, alpha_y, beta, gamma) = (1, 1, 1, 2, 2, 2, 3, 1) 171 | let verification_key = VerificationKey { 172 | g_1: g.operate_with_self(1), 173 | g_alpha_v: g.operate_with_self(2), 174 | g_alpha_w: g.operate_with_self(2), 175 | g_alpha_y: g.operate_with_self(2), 176 | g_gamma: g.operate_with_self(3), 177 | g_beta_gamma: g.operate_with_self(3), 178 | gy_target_on_s: g.operate_with_self(2), 179 | gv_ks: vec![ 180 | g.operate_with_self(0), 181 | g.operate_with_self(6), 182 | g.operate_with_self(6), 183 | ], 184 | gw_ks: vec![ 185 | g.operate_with_self(0), 186 | g.operate_with_self(6), 187 | g.operate_with_self(6), 188 | ], 189 | gy_ks: vec![ 190 | g.operate_with_self(0), 191 | g.operate_with_self(6), 192 | g.operate_with_self(6), 193 | ], 194 | }; 195 | 196 | let input_output = vec![ 197 | // One input value 198 | FE::new(3), 199 | // One output value 200 | FE::new(1), 201 | ]; 202 | 203 | // This is a valid proof for the above input and output values. 204 | // See the prover tests to see where this comes from. 205 | let proof = Proof { 206 | g_vs: g.operate_with_self(6), 207 | g_ws: g.operate_with_self(6), 208 | g_ys: g.operate_with_self(6), 209 | g_hs: g.operate_with_self(120), 210 | g_alpha_vs: g.operate_with_self(12), 211 | g_alpha_ws: g.operate_with_self(12), 212 | g_alpha_ys: g.operate_with_self(12), 213 | g_beta_vwy: g.operate_with_self(18), 214 | }; 215 | 216 | (verification_key, proof, input_output) 217 | } 218 | 219 | #[test] 220 | fn test_divisibility_check_correct_on_correct_proof_with_elliptic_curve_hidings() { 221 | let (verification_key, proof, input_output) = 222 | dummy_verification_data_with_elliptic_curve_hidings(); 223 | assert!(check_divisibility(&verification_key, &proof, &input_output)); 224 | } 225 | 226 | #[test] 227 | fn test_appropiate_spans_correct_on_correct_proof_with_elliptic_curve_hidings() { 228 | let (verification_key, proof, _) = dummy_verification_data_with_elliptic_curve_hidings(); 229 | assert!(check_appropiate_spans(&verification_key, &proof)); 230 | } 231 | 232 | #[test] 233 | fn test_same_linear_combinations_on_correct_proof_with_elliptic_curve_hidings() { 234 | let (verification_key, proof, _) = dummy_verification_data_with_elliptic_curve_hidings(); 235 | assert!(check_same_linear_combinations(&verification_key, &proof)); 236 | } 237 | 238 | #[test] 239 | fn test_divisibility_check_correct_on_incorrect_input_output_with_elliptic_curve_hidings() { 240 | let (verification_key, proof, _) = dummy_verification_data_with_elliptic_curve_hidings(); 241 | let input_output = vec![FE::new(0), FE::new(1)]; 242 | assert!(!check_divisibility( 243 | &verification_key, 244 | &proof, 245 | &input_output 246 | )); 247 | } 248 | 249 | #[test] 250 | fn test_appropiate_spans_correct_on_incorrect_proof_with_elliptic_curve_hidings() { 251 | let (verification_key, mut proof, _) = 252 | dummy_verification_data_with_elliptic_curve_hidings(); 253 | proof.g_alpha_vs = EllipticCurveElement::neutral_element(); 254 | assert!(!check_appropiate_spans(&verification_key, &proof)); 255 | } 256 | 257 | #[test] 258 | fn test_same_linear_combinations_on_incorrect_proof_with_elliptic_curve_hidings() { 259 | let (verification_key, mut proof, _) = 260 | dummy_verification_data_with_elliptic_curve_hidings(); 261 | proof.g_beta_vwy = EllipticCurveElement::neutral_element(); 262 | assert!(!check_same_linear_combinations(&verification_key, &proof)); 263 | } 264 | } 265 | -------------------------------------------------------------------------------- /tests/pinocchio.rs: -------------------------------------------------------------------------------- 1 | /// Integration test with the happy path: Setup -> Proof generation -> Proof verification. 2 | use pinocchio_lambda_vm::circuits::test_utils::{new_test_r1cs, test_qap_solver}; 3 | use pinocchio_lambda_vm::math::elliptic_curve::EllipticCurveElement; 4 | use pinocchio_lambda_vm::math::field_element::FieldElement as FE; 5 | use pinocchio_lambda_vm::pinocchio::prover; 6 | use pinocchio_lambda_vm::pinocchio::setup::{setup, EvaluationKey, ToxicWaste, VerificationKey}; 7 | use pinocchio_lambda_vm::pinocchio::verifier; 8 | 9 | fn test_pinocchio(toxic_waste: ToxicWaste) { 10 | // Get example circuit. 11 | let test_qap = new_test_r1cs().into(); 12 | 13 | // Construct the evaluation and veryfing key. 14 | let (evaluation_key, verification_key): ( 15 | EvaluationKey, 16 | VerificationKey, 17 | ) = setup(&test_qap, &toxic_waste); 18 | 19 | // Declare inputs to the circuit. Here we choose the ones from the example 20 | // of the paper. 21 | let inputs = [FE::new(1), FE::new(2), FE::new(3), FE::new(4)]; 22 | 23 | // Execute the circuit with the above inputs. 24 | // Get the output and all the intermediate values, 25 | // that is, the values of the circuit wires. 26 | let (c_mid, c_output) = test_qap_solver(inputs); 27 | 28 | // Construct the witness. In the context of Pinocchio 29 | // this means the tuple of all the values corresponding 30 | // to the inputs, the outputs and the values of the 31 | // circuit wires. The prover needs all this to construct 32 | // the proof. 33 | let mut c_vector = inputs.to_vec(); 34 | c_vector.push(c_mid); 35 | c_vector.push(c_output); 36 | 37 | // Generate the proof of execution. 38 | let proof = prover::generate_proof(&evaluation_key, &test_qap, &c_vector); 39 | 40 | // Collect all inputs and ouputs of the circuit. 41 | let mut c_io_vector = inputs.to_vec(); 42 | c_io_vector.push(c_output); 43 | 44 | // Verify the proof. 45 | let accepted = verifier::verify(&verification_key, &proof, &c_io_vector); 46 | 47 | // Accept or reject the proof. 48 | assert!(accepted); 49 | } 50 | 51 | #[test] 52 | fn test_pinocchio_random_toxic_wate() { 53 | let toxic_waste = ToxicWaste::sample(); 54 | test_pinocchio(toxic_waste); 55 | } 56 | 57 | #[test] 58 | fn test_pinocchio_2() { 59 | let toxic_waste = ToxicWaste::new( 60 | FE::new(2), 61 | FE::new(3), 62 | FE::new(3), 63 | FE::new(4), 64 | FE::new(2), 65 | FE::new(3), 66 | FE::new(2), 67 | FE::new(3), 68 | ); 69 | test_pinocchio(toxic_waste); 70 | } 71 | --------------------------------------------------------------------------------