├── src ├── provider │ ├── keccak.rs │ ├── mod.rs │ ├── bn254.rs │ ├── poseidon.rs │ └── kzg.rs ├── rust-toolchain ├── primary │ ├── bn254_curve.rs │ ├── circuit.rs │ ├── mod.rs │ ├── kzg.rs │ ├── nifs.rs │ ├── bn254_field.rs │ └── plonk.rs ├── poseidon │ ├── mod.rs │ ├── grain_lfsr.rs │ └── poseidon_constants.rs ├── secondary │ ├── grumpkin_curve.rs │ ├── mod.rs │ ├── ipa.rs │ └── grumpkin_field.rs ├── lib.rs ├── Makefile ├── error.rs ├── nifs.rs ├── traits.rs └── plonk.rs ├── rust-toolchain ├── image.png ├── image-1.png ├── .gitignore ├── .vscode └── settings.json ├── Makefile ├── .github └── workflows │ └── rust.yml ├── Cargo.toml └── README.md /src/provider/keccak.rs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly-2023-07-13 -------------------------------------------------------------------------------- /src/rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly-2023-07-13 -------------------------------------------------------------------------------- /image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PayneJoe/PNova/HEAD/image.png -------------------------------------------------------------------------------- /src/primary/bn254_curve.rs: -------------------------------------------------------------------------------- 1 | // bn254 curve implementaion for PCS 2 | -------------------------------------------------------------------------------- /image-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PayneJoe/PNova/HEAD/image-1.png -------------------------------------------------------------------------------- /src/poseidon/mod.rs: -------------------------------------------------------------------------------- 1 | mod grain_lfsr; 2 | pub mod poseidon_constants; 3 | -------------------------------------------------------------------------------- /src/primary/circuit.rs: -------------------------------------------------------------------------------- 1 | // primary circuit implementation based BN254 curve 2 | -------------------------------------------------------------------------------- /src/secondary/grumpkin_curve.rs: -------------------------------------------------------------------------------- 1 | // grumpkin curve implementaion for PCS 2 | -------------------------------------------------------------------------------- /src/secondary/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod grumpkin_curve; 2 | pub mod grumpkin_field; 3 | pub mod ipa; 4 | -------------------------------------------------------------------------------- /src/provider/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod bn254; 2 | pub mod keccak; 3 | pub mod kzg; 4 | pub mod poseidon; 5 | -------------------------------------------------------------------------------- /src/secondary/ipa.rs: -------------------------------------------------------------------------------- 1 | // ipa commitment scheme for grumpkin curve which is not fully pairing-friendly 2 | -------------------------------------------------------------------------------- /src/primary/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod bn254_curve; 2 | pub mod bn254_field; 3 | pub mod circuit; 4 | pub mod kzg; 5 | pub mod nifs; 6 | pub mod plonk; 7 | -------------------------------------------------------------------------------- /src/secondary/grumpkin_field.rs: -------------------------------------------------------------------------------- 1 | pub use crate::primary::bn254_field::{Fq as Fr, FqBackend as FrBackend}; 2 | pub use crate::primary::bn254_field::{Fr as Fq, FrBackend as FqBackend}; 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .*.sw* 2 | .DS_Store 3 | .idea 4 | /target 5 | cargo-system-config.toml 6 | Cargo.lock 7 | /.vscode 8 | /.github 9 | 10 | # Test coverage (grcov) 11 | default.profraw 12 | /.pre-commit-config.yaml -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod error; 2 | pub mod nifs; 3 | pub mod plonk; 4 | pub mod poseidon; 5 | pub mod primary; 6 | pub mod provider; 7 | pub mod secondary; 8 | pub mod traits; 9 | 10 | type Commitment = <::CE as traits::CommitmentEngineTrait>::Commitment; 11 | type CommitmentKey = 12 | <::CE as traits::CommitmentEngineTrait>::CommitmentKey; 13 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.linkedProjects": [ 3 | "./Cargo.toml", 4 | "./Cargo.toml", 5 | "./Cargo.toml", 6 | "./Cargo.toml", 7 | "./Cargo.toml", 8 | "./Cargo.toml", 9 | "./Cargo.toml", 10 | "./Cargo.toml", 11 | "./Cargo.toml", 12 | "./Cargo.toml", 13 | "./Cargo.toml", 14 | "./Cargo.toml", 15 | "./Cargo.toml", 16 | "./Cargo.toml", 17 | "./Cargo.toml" 18 | ] 19 | } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CARGO = cargo 2 | 3 | UNAME_S := $(shell uname -s) 4 | ifeq ($(UNAME_S),Darwin) 5 | CARGO += --config 'build.rustdocflags = ["-C", "link-args=-framework CoreFoundation -framework Security"]' 6 | endif 7 | 8 | help: ## Display this help screen 9 | @grep -h \ 10 | -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \ 11 | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' 12 | 13 | clippy: ## Run clippy checks over all workspace members and formated correctly 14 | @cargo check 15 | @cargo fmt --all -- --check 16 | @cargo clippy --all-targets -- -D warnings 17 | 18 | fix: ## Automatically apply lint suggestions. This flag implies `--no-deps` and `--all-targets` 19 | @cargo clippy --fix 20 | 21 | test: ## Run tests for all the workspace members 22 | @cargo test --release --all 23 | 24 | .PHONY: clippy fmt test -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | CARGO = cargo 2 | 3 | UNAME_S := $(shell uname -s) 4 | ifeq ($(UNAME_S),Darwin) 5 | CARGO += --config 'build.rustdocflags = ["-C", "link-args=-framework CoreFoundation -framework Security"]' 6 | endif 7 | 8 | help: ## Display this help screen 9 | @grep -h \ 10 | -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \ 11 | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' 12 | 13 | clippy: ## Run clippy checks over all workspace members and formated correctly 14 | @cargo check 15 | @cargo fmt --all -- --check 16 | @cargo clippy --all-targets -- -D warnings 17 | 18 | fix: ## Automatically apply lint suggestions. This flag implies `--no-deps` and `--all-targets` 19 | @cargo clippy --fix 20 | 21 | test: ## Run tests for all the workspace members 22 | @cargo test --release --all 23 | 24 | .PHONY: clippy fmt test -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | //! This module defines errors returned by the library. 2 | use core::fmt::Debug; 3 | use thiserror::Error; 4 | 5 | /// Errors returned by Nova 6 | #[derive(Clone, Debug, Eq, PartialEq, Error)] 7 | pub enum MyError { 8 | /// group error 9 | #[error("group error")] 10 | GroupError, 11 | /// keccak transcript error 12 | #[error("keccak error")] 13 | KeccakError, 14 | /// curve error 15 | #[error("curve error")] 16 | CurveError, 17 | /// hash error 18 | #[error("hash error")] 19 | HashError, 20 | /// commitment error 21 | #[error("commitment error")] 22 | CommitmentError, 23 | /// witness error 24 | #[error("witness erro")] 25 | WitnessError, 26 | /// public intput error 27 | #[error("public input error")] 28 | PublicIntputError, 29 | #[error("Selector error")] 30 | SelectorError, 31 | } 32 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Build and Test PNova 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Install 15 | run: rustup default stable 16 | - name: Install rustfmt Components 17 | run: rustup component add rustfmt 18 | - name: Install clippy 19 | run: rustup component add clippy 20 | - name: Build 21 | run: cargo build --verbose 22 | - name: Build examples 23 | run: cargo build --examples --verbose 24 | - name: Build benches 25 | run: cargo build --benches --verbose 26 | - name: Run tests 27 | run: cargo +stable test --release --verbose 28 | - name: Check Rustfmt Code Style 29 | run: cargo fmt --all -- --check 30 | - name: Check clippy warnings 31 | run: cargo clippy --all-targets -- -D warnings 32 | 33 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pnova" 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 | rand = "0.8" 10 | serde = { version = "1.0", features = ["derive"] } 11 | thiserror = "1.0" 12 | sha3 = "0.10" 13 | rayon = "1.7" 14 | num-traits = "0.2" 15 | digest = "0.10" 16 | generic-array = "1.0.0" 17 | ark-bn254 = "0.4.0" 18 | 19 | ark-crypto-primitives = { version = "0.4.0", default-features = false, features = [ 20 | "sponge", 21 | ] } 22 | ark-std = { version = "0.4.0", default-features = false } 23 | ark-ec = {version = "0.4.0", default-features = false} 24 | ark-ff = {version= "0.4.0", default-features = false} 25 | ark-poly = {version = "0.4.0", default-features = false} 26 | jf_primitives = {git = "https://github.com/EspressoSystems/jellyfish", package = "jf-primitives"} 27 | jf_utils = {git = "https://github.com/EspressoSystems/jellyfish", package = "jf-utils"} -------------------------------------------------------------------------------- /src/nifs.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | error::MyError, 3 | plonk::*, 4 | traits::{Group, ROConstantsTrait, ROTrait}, 5 | Commitment, CommitmentKey, 6 | }; 7 | use std::marker::PhantomData; 8 | 9 | pub struct NIFS { 10 | pub(crate) comm_T: Commitment, 11 | _p: PhantomData, 12 | } 13 | 14 | impl NIFS { 15 | pub fn prove( 16 | ck: &CommitmentKey, 17 | pp_digest: &::ScalarField, 18 | S: &PLONKShape, 19 | U1: &RelaxedPLONKInstance, 20 | W1: &RelaxedPLONKWitness, 21 | U2: &PLONKInstance, 22 | W2: &PLONKWitness, 23 | ) -> Result<(NIFS, (RelaxedPLONKInstance, RelaxedPLONKWitness)), MyError> { 24 | let constants = <::RO as ROTrait< 25 | ::BaseField, 26 | ::ScalarField, 27 | >>::Constants::new(3); 28 | let ro = <::RO as ROTrait< 29 | ::BaseField, 30 | ::ScalarField, 31 | >>::new(constants.clone()); 32 | todo!() 33 | } 34 | pub fn verifiy( 35 | &self, 36 | pp_digest: &::ScalarField, 37 | U1: &RelaxedPLONKInstance, 38 | U2: &PLONKInstance, 39 | ) -> Result, MyError> { 40 | todo!() 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/provider/bn254.rs: -------------------------------------------------------------------------------- 1 | use crate::poseidon::poseidon_constants::{PoseidonDefaultConfig, PoseidonDefaultConfigEntry}; 2 | use crate::provider::{kzg::CommitmentEngine, poseidon::PoseidonRO}; 3 | use crate::traits::Group; 4 | use ark_bn254::{Bn254, Fq, FqConfig, Fr, G1Affine}; 5 | use ark_ff::fields::MontBackend; 6 | 7 | impl PoseidonDefaultConfig<4> for MontBackend { 8 | const PARAMS_OPT_FOR_CONSTRAINTS: [PoseidonDefaultConfigEntry; 7] = [ 9 | PoseidonDefaultConfigEntry::new(2, 17, 8, 31, 0), 10 | PoseidonDefaultConfigEntry::new(3, 5, 8, 56, 0), 11 | PoseidonDefaultConfigEntry::new(4, 5, 8, 56, 0), 12 | PoseidonDefaultConfigEntry::new(5, 5, 8, 57, 0), 13 | PoseidonDefaultConfigEntry::new(6, 5, 8, 57, 0), 14 | PoseidonDefaultConfigEntry::new(7, 5, 8, 57, 0), 15 | PoseidonDefaultConfigEntry::new(8, 5, 8, 57, 0), 16 | ]; 17 | const PARAMS_OPT_FOR_WEIGHTS: [PoseidonDefaultConfigEntry; 7] = [ 18 | PoseidonDefaultConfigEntry::new(2, 257, 8, 13, 0), 19 | PoseidonDefaultConfigEntry::new(3, 257, 8, 13, 0), 20 | PoseidonDefaultConfigEntry::new(4, 257, 8, 13, 0), 21 | PoseidonDefaultConfigEntry::new(5, 257, 8, 13, 0), 22 | PoseidonDefaultConfigEntry::new(6, 257, 8, 13, 0), 23 | PoseidonDefaultConfigEntry::new(7, 257, 8, 13, 0), 24 | PoseidonDefaultConfigEntry::new(8, 257, 8, 13, 0), 25 | ]; 26 | } 27 | 28 | impl Group for Bn254 { 29 | type BaseField = Fq; 30 | type ScalarField = Fr; 31 | type PreprocessedGroupElement = G1Affine; 32 | type RO = PoseidonRO; 33 | type CE = CommitmentEngine; 34 | } 35 | -------------------------------------------------------------------------------- /src/traits.rs: -------------------------------------------------------------------------------- 1 | use ark_ec::pairing::Pairing; 2 | use ark_ff::PrimeField; 3 | use core::fmt::Debug; 4 | use rand::rngs::StdRng; 5 | 6 | pub trait Group: Sized + Pairing { 7 | type BaseField: PrimeField; 8 | type ScalarField: PrimeField; 9 | type PreprocessedGroupElement: Clone + Debug; 10 | type RO: ROTrait<::BaseField, ::ScalarField>; 11 | type CE: CommitmentEngineTrait; 12 | } 13 | pub trait ROConstantsTrait { 14 | /// produces constants/parameters associated with the hash function 15 | fn new(rate: usize) -> Self; 16 | } 17 | 18 | pub trait ROTrait { 19 | type Constants: ROConstantsTrait + Clone; 20 | 21 | /// Initializes the hash function 22 | fn new(constants: Self::Constants) -> Self; 23 | 24 | /// Adds a scalar to the internal state 25 | fn absorb(&mut self, e: BaseField); 26 | 27 | /// Returns a challenge of `num_bits` by hashing the internal state 28 | fn squeeze(&mut self) -> ScalarField; 29 | } 30 | 31 | pub trait CommitmentEngineTrait { 32 | /// Holds the type of the commitment key 33 | type CommitmentKey: Clone + Debug; 34 | 35 | /// Holds the type of the commitment 36 | type Commitment: Clone + Debug + PartialEq + Eq; 37 | 38 | /// Samples a new commitment key of a specified size 39 | fn setup(rng: &mut StdRng, degree: usize) -> Self::CommitmentKey; 40 | 41 | /// Commits to the provided vector using the provided generators 42 | fn commit(ck: &Self::CommitmentKey, v: &[::ScalarField]) -> Self::Commitment; 43 | } 44 | 45 | // pub trait CommitmentTrait: Clone + Debug + PartialEq + Eq {} 46 | -------------------------------------------------------------------------------- /src/provider/poseidon.rs: -------------------------------------------------------------------------------- 1 | use ark_crypto_primitives::sponge::poseidon::{PoseidonConfig, PoseidonSponge}; 2 | use ark_crypto_primitives::sponge::{Absorb, CryptographicSponge}; 3 | use ark_ff::PrimeField; 4 | 5 | use std::marker::PhantomData; 6 | 7 | use crate::poseidon::poseidon_constants::PoseidonDefaultConfigField; 8 | use crate::traits::{ROConstantsTrait, ROTrait}; 9 | 10 | /// wrap a EXTERNAL object PoseidonConfig, and implement LOCAL trait 'ROConstantsTrait' 11 | #[derive(Clone)] 12 | pub struct PoseidonConstants(PoseidonConfig); 13 | 14 | impl ROConstantsTrait for PoseidonConstants 15 | where 16 | BaseField: PoseidonDefaultConfigField, 17 | { 18 | fn new(rate: usize) -> Self { 19 | Self( 20 | ::get_default_poseidon_parameters(rate, false) 21 | .unwrap(), 22 | ) 23 | } 24 | } 25 | 26 | /// wrap a EXTERNAL object PoseidonSponge, impl LOCAL trait ROTrait 27 | pub struct PoseidonRO { 28 | ro: PoseidonSponge, 29 | _p: PhantomData, 30 | } 31 | 32 | impl ROTrait for PoseidonRO 33 | where 34 | BaseField: PrimeField + PoseidonDefaultConfigField + Absorb, 35 | ScalarField: PrimeField, 36 | { 37 | type Constants = PoseidonConstants; 38 | 39 | fn new(constants: Self::Constants) -> Self { 40 | Self { 41 | ro: PoseidonSponge::::new(&constants.0), 42 | _p: PhantomData, 43 | } 44 | } 45 | 46 | fn absorb(&mut self, e: BaseField) { 47 | self.ro.absorb(&e); 48 | } 49 | 50 | fn squeeze(&mut self) -> ScalarField { 51 | self.ro.squeeze_field_elements::(1 as usize)[0] 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/primary/kzg.rs: -------------------------------------------------------------------------------- 1 | /// kzg commitment scheme for bn254 curve which is pairing-friendly 2 | /// 3 | use ark_ec::{pairing::Pairing, scalar_mul::fixed_base::FixedBase, CurveGroup}; 4 | use ark_ff::PrimeField; 5 | use ark_std::{ 6 | end_timer, 7 | rand::{CryptoRng, RngCore}, 8 | start_timer, vec, One, UniformRand, 9 | }; 10 | 11 | use jf_primitives::pcs::prelude::{PCSError, UnivariateUniversalParams}; 12 | 13 | pub fn gen_srs_for_testing( 14 | rng: &mut R, 15 | prover_degree: usize, 16 | verifier_degree: usize, 17 | ) -> Result, PCSError> { 18 | let setup_time = start_timer!(|| ark_std::format!( 19 | "KZG10::Setup with prover degree {} and verifier degree {}", 20 | prover_degree, 21 | verifier_degree 22 | )); 23 | let beta = E::ScalarField::rand(rng); 24 | let g = E::G1::rand(rng); 25 | let h = E::G2::rand(rng); 26 | 27 | let mut powers_of_beta = vec![E::ScalarField::one()]; 28 | 29 | let mut cur = beta; 30 | let max_degree = ark_std::cmp::max(prover_degree, verifier_degree); 31 | for _ in 0..max_degree { 32 | powers_of_beta.push(cur); 33 | cur *= β 34 | } 35 | 36 | let window_size = FixedBase::get_mul_window_size(prover_degree + 1); 37 | 38 | let scalar_bits = E::ScalarField::MODULUS_BIT_SIZE as usize; 39 | let g_time = start_timer!(|| "Generating powers of G"); 40 | // TODO: parallelization 41 | let g_table = FixedBase::get_window_table(scalar_bits, window_size, g); 42 | let powers_of_g = FixedBase::msm::(scalar_bits, window_size, &g_table, &powers_of_beta); 43 | end_timer!(g_time); 44 | 45 | let powers_of_g = E::G1::normalize_batch(&powers_of_g); 46 | 47 | let h = h.into_affine(); 48 | let beta_h = (h * beta).into_affine(); 49 | 50 | let powers_of_h = powers_of_beta 51 | .iter() 52 | .take(verifier_degree + 1) 53 | .map(|x| (h * x).into_affine()) 54 | .collect(); 55 | 56 | let pp = UnivariateUniversalParams { 57 | powers_of_g, 58 | h, 59 | beta_h, 60 | powers_of_h, 61 | }; 62 | end_timer!(setup_time); 63 | Ok(pp) 64 | } 65 | -------------------------------------------------------------------------------- /src/primary/nifs.rs: -------------------------------------------------------------------------------- 1 | /// Non-interactive Folding Scheme based Plonkish Nova over BN254 curve 2 | /// 3 | /// 4 | use ark_crypto_primitives::sponge::poseidon::PoseidonSponge; 5 | use ark_crypto_primitives::sponge::CryptographicSponge; 6 | use ark_ec::pairing::Pairing; 7 | use ark_ff::{BigInt, BigInteger}; 8 | use ark_ff::{Field, PrimeField}; 9 | use jf_primitives::pcs::prelude::Commitment; 10 | 11 | use std::marker::PhantomData; 12 | 13 | use super::bn254_field::Fq; 14 | use super::plonk::{ 15 | CommitmentKey, PLONKInstance, PLONKShape, PLONKWitness, RelaxedPLONKInstance, 16 | RelaxedPLONKWitness, 17 | }; 18 | use crate::error::MyError; 19 | use crate::poseidon::poseidon_constants::PoseidonDefaultConfigField; 20 | 21 | pub struct NIFS { 22 | pub(crate) comm_T: Commitment, 23 | _p: PhantomData, 24 | } 25 | 26 | impl NIFS { 27 | pub fn prove( 28 | ck: &CommitmentKey, 29 | pp_digest: &E::ScalarField, 30 | S: &PLONKShape, 31 | U1: &RelaxedPLONKInstance, 32 | W1: &RelaxedPLONKWitness, 33 | U2: &PLONKInstance, 34 | W2: &PLONKWitness, 35 | ) -> Result<(NIFS, (RelaxedPLONKInstance, RelaxedPLONKWitness)), MyError> { 36 | let sponge_constant = Fq::get_default_poseidon_parameters(3, false).unwrap(); 37 | let mut sponge = PoseidonSponge::::new(&sponge_constant); 38 | todo!() 39 | } 40 | pub fn verifiy( 41 | &self, 42 | pp_digest: &E::ScalarField, 43 | U1: &RelaxedPLONKInstance, 44 | U2: &PLONKInstance, 45 | ) -> Result, MyError> { 46 | todo!() 47 | } 48 | } 49 | 50 | #[cfg(test)] 51 | mod tests { 52 | use ark_ec::pairing::Pairing; 53 | use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial}; 54 | use jf_primitives::pcs::{ 55 | prelude::{UnivariateKzgPCS, UnivariateUniversalParams}, 56 | PolynomialCommitmentScheme, StructuredReferenceString, 57 | }; 58 | 59 | use crate::primary::kzg::gen_srs_for_testing; 60 | use ark_bn254::Bn254; 61 | use jf_utils::test_rng; 62 | 63 | fn test_pcs_end_to_end_template() 64 | where 65 | E: Pairing, 66 | { 67 | let degree = 4; 68 | let rng = &mut test_rng(); 69 | let pp: UnivariateUniversalParams = gen_srs_for_testing(rng, degree, 1).unwrap(); 70 | let (ck, _) = pp.trim(degree).unwrap(); 71 | let p = as DenseUVPolynomial>::rand( 72 | degree, rng, 73 | ); 74 | let comm = UnivariateKzgPCS::::commit(&ck, &p).unwrap(); 75 | assert!(comm == comm, ""); 76 | } 77 | #[test] 78 | fn test_pcs() { 79 | test_pcs_end_to_end_template::(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/primary/bn254_field.rs: -------------------------------------------------------------------------------- 1 | //// configurations for bn254 curve/field 2 | use crate::poseidon::poseidon_constants::{PoseidonDefaultConfig, PoseidonDefaultConfigEntry}; 3 | use ark_crypto_primitives::sponge::Absorb; 4 | use ark_ff::{fields::models::*, MontBackend, MontConfig, PrimeField}; 5 | 6 | /// for bn254 base field Fq 7 | #[derive(MontConfig)] 8 | #[modulus = "21888242871839275222246405745257275088696311157297823662689037894645226208583"] 9 | #[generator = "3"] 10 | pub struct FqBackend; 11 | 12 | type FqConfig = MontBackend; 13 | pub type Fq = Fp256; 14 | 15 | impl PoseidonDefaultConfig<4> for FqConfig { 16 | const PARAMS_OPT_FOR_CONSTRAINTS: [PoseidonDefaultConfigEntry; 7] = [ 17 | PoseidonDefaultConfigEntry::new(2, 17, 8, 31, 0), 18 | PoseidonDefaultConfigEntry::new(3, 5, 8, 56, 0), 19 | PoseidonDefaultConfigEntry::new(4, 5, 8, 56, 0), 20 | PoseidonDefaultConfigEntry::new(5, 5, 8, 57, 0), 21 | PoseidonDefaultConfigEntry::new(6, 5, 8, 57, 0), 22 | PoseidonDefaultConfigEntry::new(7, 5, 8, 57, 0), 23 | PoseidonDefaultConfigEntry::new(8, 5, 8, 57, 0), 24 | ]; 25 | const PARAMS_OPT_FOR_WEIGHTS: [PoseidonDefaultConfigEntry; 7] = [ 26 | PoseidonDefaultConfigEntry::new(2, 257, 8, 13, 0), 27 | PoseidonDefaultConfigEntry::new(3, 257, 8, 13, 0), 28 | PoseidonDefaultConfigEntry::new(4, 257, 8, 13, 0), 29 | PoseidonDefaultConfigEntry::new(5, 257, 8, 13, 0), 30 | PoseidonDefaultConfigEntry::new(6, 257, 8, 13, 0), 31 | PoseidonDefaultConfigEntry::new(7, 257, 8, 13, 0), 32 | PoseidonDefaultConfigEntry::new(8, 257, 8, 13, 0), 33 | ]; 34 | } 35 | 36 | /// for bn254 scalar field Fr 37 | #[derive(MontConfig)] 38 | #[modulus = "21888242871839275222246405745257275088548364400416034343698204186575808495617"] 39 | #[generator = "5"] 40 | #[small_subgroup_base = "3"] 41 | #[small_subgroup_power = "2"] 42 | pub struct FrBackend; 43 | 44 | type FrConfig = MontBackend; 45 | pub type Fr = Fp256>; 46 | 47 | impl PoseidonDefaultConfig<4> for FrConfig { 48 | const PARAMS_OPT_FOR_CONSTRAINTS: [PoseidonDefaultConfigEntry; 7] = [ 49 | PoseidonDefaultConfigEntry::new(2, 17, 8, 31, 0), 50 | PoseidonDefaultConfigEntry::new(3, 5, 8, 56, 0), 51 | PoseidonDefaultConfigEntry::new(4, 5, 8, 56, 0), 52 | PoseidonDefaultConfigEntry::new(5, 5, 8, 57, 0), 53 | PoseidonDefaultConfigEntry::new(6, 5, 8, 57, 0), 54 | PoseidonDefaultConfigEntry::new(7, 5, 8, 57, 0), 55 | PoseidonDefaultConfigEntry::new(8, 5, 8, 57, 0), 56 | ]; 57 | const PARAMS_OPT_FOR_WEIGHTS: [PoseidonDefaultConfigEntry; 7] = [ 58 | PoseidonDefaultConfigEntry::new(2, 257, 8, 13, 0), 59 | PoseidonDefaultConfigEntry::new(3, 257, 8, 13, 0), 60 | PoseidonDefaultConfigEntry::new(4, 257, 8, 13, 0), 61 | PoseidonDefaultConfigEntry::new(5, 257, 8, 13, 0), 62 | PoseidonDefaultConfigEntry::new(6, 257, 8, 13, 0), 63 | PoseidonDefaultConfigEntry::new(7, 257, 8, 13, 0), 64 | PoseidonDefaultConfigEntry::new(8, 257, 8, 13, 0), 65 | ]; 66 | } 67 | -------------------------------------------------------------------------------- /src/provider/kzg.rs: -------------------------------------------------------------------------------- 1 | use rand::rngs::StdRng; 2 | use std::marker::PhantomData; 3 | 4 | use crate::traits::CommitmentEngineTrait; 5 | 6 | use ark_ec::{pairing::Pairing, scalar_mul::fixed_base::FixedBase, CurveGroup}; 7 | use ark_ff::{Field, PrimeField}; 8 | use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial}; 9 | use ark_std::{ 10 | end_timer, 11 | rand::{CryptoRng, RngCore}, 12 | start_timer, vec, One, UniformRand, 13 | }; 14 | 15 | use jf_primitives::pcs::prelude::Commitment; 16 | use jf_primitives::pcs::{ 17 | prelude::{PCSError, UnivariateKzgPCS, UnivariateProverParam, UnivariateUniversalParams}, 18 | PolynomialCommitmentScheme, StructuredReferenceString, 19 | }; 20 | 21 | use crate::traits::Group; 22 | 23 | pub fn gen_srs_for_testing( 24 | rng: &mut R, 25 | prover_degree: usize, 26 | verifier_degree: usize, 27 | ) -> Result, PCSError> { 28 | let setup_time = start_timer!(|| ark_std::format!( 29 | "KZG10::Setup with prover degree {} and verifier degree {}", 30 | prover_degree, 31 | verifier_degree 32 | )); 33 | let beta = E::ScalarField::rand(rng); 34 | let g = E::G1::rand(rng); 35 | let h = E::G2::rand(rng); 36 | 37 | let mut powers_of_beta = vec![E::ScalarField::one()]; 38 | 39 | let mut cur = beta; 40 | let max_degree = ark_std::cmp::max(prover_degree, verifier_degree); 41 | for _ in 0..max_degree { 42 | powers_of_beta.push(cur); 43 | cur *= β 44 | } 45 | 46 | let window_size = FixedBase::get_mul_window_size(prover_degree + 1); 47 | 48 | let scalar_bits = E::ScalarField::MODULUS_BIT_SIZE as usize; 49 | let g_time = start_timer!(|| "Generating powers of G"); 50 | // TODO: parallelization 51 | let g_table = FixedBase::get_window_table(scalar_bits, window_size, g); 52 | let powers_of_g = FixedBase::msm::(scalar_bits, window_size, &g_table, &powers_of_beta); 53 | end_timer!(g_time); 54 | 55 | let powers_of_g = E::G1::normalize_batch(&powers_of_g); 56 | 57 | let h = h.into_affine(); 58 | let beta_h = (h * beta).into_affine(); 59 | 60 | let powers_of_h = powers_of_beta 61 | .iter() 62 | .take(verifier_degree + 1) 63 | .map(|x| (h * x).into_affine()) 64 | .collect(); 65 | 66 | let pp = UnivariateUniversalParams { 67 | powers_of_g, 68 | h, 69 | beta_h, 70 | powers_of_h, 71 | }; 72 | end_timer!(setup_time); 73 | Ok(pp) 74 | } 75 | 76 | /// implement LOCAL trait CommitmentEngineTrait for it 77 | /// 78 | pub struct CommitmentEngine { 79 | _p: PhantomData, 80 | } 81 | 82 | #[derive(Clone, Debug, PartialEq, Eq)] 83 | pub struct KZGCommitmentKey(UnivariateProverParam); 84 | 85 | #[derive(Clone, Debug, PartialEq, Eq)] 86 | pub struct KZGCommitment(Commitment); 87 | 88 | // impl CommitmentTrait for KZGCommitment where G: Group {} 89 | 90 | impl CommitmentEngineTrait for CommitmentEngine 91 | where 92 | G: Group, 93 | { 94 | type CommitmentKey = KZGCommitmentKey; 95 | type Commitment = KZGCommitment; 96 | 97 | fn setup(rng: &mut StdRng, degree: usize) -> Self::CommitmentKey { 98 | let pp: UnivariateUniversalParams = gen_srs_for_testing(rng, degree, 1).unwrap(); 99 | let (ck, _) = pp.trim(degree).unwrap(); 100 | KZGCommitmentKey(ck) 101 | } 102 | 103 | fn commit(ck: &Self::CommitmentKey, v: &[::ScalarField]) -> Self::Commitment { 104 | let poly = ::ScalarField> as DenseUVPolynomial< 105 | ::ScalarField, 106 | >>::from_coefficients_vec(v.to_vec()); 107 | KZGCommitment(UnivariateKzgPCS::::commit(&ck.0, &poly).unwrap()) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/poseidon/grain_lfsr.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use ark_ff::{BigInteger, PrimeField}; 4 | use ark_std::vec::Vec; 5 | 6 | pub struct PoseidonGrainLFSR { 7 | pub prime_num_bits: u64, 8 | 9 | pub state: [bool; 80], 10 | pub head: usize, 11 | } 12 | 13 | #[allow(unused_variables)] 14 | impl PoseidonGrainLFSR { 15 | pub fn new( 16 | is_sbox_an_inverse: bool, 17 | prime_num_bits: u64, 18 | state_len: u64, 19 | num_full_rounds: u64, 20 | num_partial_rounds: u64, 21 | ) -> Self { 22 | let mut state = [false; 80]; 23 | 24 | // b0, b1 describes the field 25 | state[1] = true; 26 | 27 | // b2, ..., b5 describes the S-BOX 28 | if is_sbox_an_inverse { 29 | state[5] = true; 30 | } else { 31 | state[5] = false; 32 | } 33 | 34 | // b6, ..., b17 are the binary representation of n (prime_num_bits) 35 | { 36 | let mut cur = prime_num_bits; 37 | for i in (6..=17).rev() { 38 | state[i] = cur & 1 == 1; 39 | cur >>= 1; 40 | } 41 | } 42 | 43 | // b18, ..., b29 are the binary representation of t (state_len, rate + capacity) 44 | { 45 | let mut cur = state_len; 46 | for i in (18..=29).rev() { 47 | state[i] = cur & 1 == 1; 48 | cur >>= 1; 49 | } 50 | } 51 | 52 | // b30, ..., b39 are the binary representation of R_F (the number of full rounds) 53 | { 54 | let mut cur = num_full_rounds; 55 | for i in (30..=39).rev() { 56 | state[i] = cur & 1 == 1; 57 | cur >>= 1; 58 | } 59 | } 60 | 61 | // b40, ..., b49 are the binary representation of R_P (the number of partial rounds) 62 | { 63 | let mut cur = num_partial_rounds; 64 | for i in (40..=49).rev() { 65 | state[i] = cur & 1 == 1; 66 | cur >>= 1; 67 | } 68 | } 69 | 70 | // b50, ..., b79 are set to 1 71 | for i in 50..=79 { 72 | state[i] = true; 73 | } 74 | 75 | let head = 0; 76 | 77 | let mut res = Self { 78 | prime_num_bits, 79 | state, 80 | head, 81 | }; 82 | res.init(); 83 | res 84 | } 85 | 86 | pub fn get_bits(&mut self, num_bits: usize) -> Vec { 87 | let mut res = Vec::new(); 88 | 89 | for _ in 0..num_bits { 90 | // Obtain the first bit 91 | let mut new_bit = self.update(); 92 | 93 | // Loop until the first bit is true 94 | while new_bit == false { 95 | // Discard the second bit 96 | let _ = self.update(); 97 | // Obtain another first bit 98 | new_bit = self.update(); 99 | } 100 | 101 | // Obtain the second bit 102 | res.push(self.update()); 103 | } 104 | 105 | res 106 | } 107 | 108 | pub fn get_field_elements_rejection_sampling( 109 | &mut self, 110 | num_elems: usize, 111 | ) -> Vec { 112 | assert_eq!(F::MODULUS_BIT_SIZE as u64, self.prime_num_bits); 113 | 114 | let mut res = Vec::new(); 115 | for _ in 0..num_elems { 116 | // Perform rejection sampling 117 | loop { 118 | // Obtain n bits and make it most-significant-bit first 119 | let mut bits = self.get_bits(self.prime_num_bits as usize); 120 | bits.reverse(); 121 | 122 | // Construct the number 123 | let bigint = F::BigInt::from_bits_le(&bits); 124 | 125 | if let Some(f) = F::from_bigint(bigint) { 126 | res.push(f); 127 | break; 128 | } 129 | } 130 | } 131 | 132 | res 133 | } 134 | 135 | pub fn get_field_elements_mod_p(&mut self, num_elems: usize) -> Vec { 136 | assert_eq!(F::MODULUS_BIT_SIZE as u64, self.prime_num_bits); 137 | 138 | let mut res = Vec::new(); 139 | for _ in 0..num_elems { 140 | // Obtain n bits and make it most-significant-bit first 141 | let mut bits = self.get_bits(self.prime_num_bits as usize); 142 | bits.reverse(); 143 | 144 | let bytes = bits 145 | .chunks(8) 146 | .map(|chunk| { 147 | let mut result = 0u8; 148 | for (i, bit) in chunk.iter().enumerate() { 149 | result |= u8::from(*bit) << i 150 | } 151 | result 152 | }) 153 | .collect::>(); 154 | 155 | res.push(F::from_le_bytes_mod_order(&bytes)); 156 | } 157 | 158 | res 159 | } 160 | 161 | #[inline] 162 | fn update(&mut self) -> bool { 163 | let new_bit = self.state[(self.head + 62) % 80] 164 | ^ self.state[(self.head + 51) % 80] 165 | ^ self.state[(self.head + 38) % 80] 166 | ^ self.state[(self.head + 23) % 80] 167 | ^ self.state[(self.head + 13) % 80] 168 | ^ self.state[self.head]; 169 | self.state[self.head] = new_bit; 170 | self.head += 1; 171 | self.head %= 80; 172 | 173 | new_bit 174 | } 175 | 176 | fn init(&mut self) { 177 | for _ in 0..160 { 178 | let _ = self.update(); 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PNova 2 | 3 | As we know [the original Nova implementation](https://github.com/microsoft/Nova) is based on R1CS. Our target is to implement a plonkish versioned NOVA, folding multiple Customer Gate/Lookup instances into one respectively. Thereafter, we can feed the folded instances into the relaxed Plonk SNARK. 4 | 5 |
6 | 7 | ## Intuition About Plonkish Nova 8 | ![Alt text](image-1.png) 9 | 10 |
11 | 12 | ## Few Thoughts 13 | - Jellyfish(TurboPlonk/UltraPlonk) supporting relaxed plonkish circuit SAT 14 | - KZG pcs for primary proof over BN254 curve, and IPA pcs for secondary proof over Grumpkin curve 15 | - Poseidon Circuit based Jellyfish 16 | - Keccak Transcript 17 | - Native verification for final first proof (Bn254 curve) 18 | - Non-native verification for final secondary proof (Grumpkin curve) 19 | 20 |
21 | 22 | ## Details About Implementation 23 | 24 | Before intensive coding we will give more comprehensive details as much as possible, including some attestation code: 25 | - [Thinking in Folding Scheme: Cross Term in R1CS](https://hackmd.io/@70xfCGp1QViTYYJh3AMrQg/BJZPMjIfT) 26 | - [Thinking in Folding Scheme: Cross Term in Plonk](https://hackmd.io/@70xfCGp1QViTYYJh3AMrQg/SkDf2nIzp) 27 | - [Thinking in Folding Scheme: Cycle Curves](https://hackmd.io/@70xfCGp1QViTYYJh3AMrQg/r1bN8nLMp) 28 | - [Plonk From Scratch](https://hackmd.io/@70xfCGp1QViTYYJh3AMrQg/HJzwPUU7a) 29 | 30 |
31 | 32 | ## Roadmap 33 | 34 | | Features | Status | Repo | Doc | 35 | | ------------------------------------------------ | :-----: | :----------------------------------------------------------------------------: | :-------------------------------------------------------------------------------: | 36 | | **Stage One** | | | | 37 | | Uncompressed Relaxed UltraPlonk Backend/Frontend | Ongoing | [relaxed-stage-1](https://github.com/ZKMod-Lab/jellyfish/tree/relaxed-stage-1) | [Relaxed Plonk Step by Step](https://hackmd.io/@70xfCGp1QViTYYJh3AMrQg/BkT0ayKmT) | 38 | | Compressed Relaxed UltraPlonk Backend/Frontend | Ongoing | | | 39 | | **Stage Two** | | | | 40 | | Uncompressed NIFS | Ongoing | [ark-nifs](https://github.com/PayneJoe/PNova/tree/ark-nifs) | [Plonkish Nova](https://hackmd.io/@70xfCGp1QViTYYJh3AMrQg/rkJYGYm46) | 41 | | Uncompressed Cycle-Curve Circuits | TODO | | | 42 | | Support PLookup/LogUp Folding | TODO | | | 43 | | **Stage Three** | | | | 44 | | compressed NIFS | TODO | | | 45 | | Compressed Cycle-Curve Circuits | TODO | | | 46 | | **Stage Four** | | | | 47 | | Cycle-fold Delegated Circuit | TODO | | | 48 | | Improving logUp Argument with GKR | TODO | | | 49 | | Memory Check Based Lookup Argument | TODO | | | 50 | | GKR Based Permutation Check | TODO | | | 51 |
52 | 53 | ## Progress of Specific Task 54 | | Day | Task | Status | 55 | | -------- | ----------------------------------------- | ------------ | 56 | | 11/08/23 | Relaxed Plonk / Data Structure | $\checkmark$ | 57 | | 11/10/23 | Relaxed Plonk / Frontend Dev | $\checkmark$ | 58 | | 11/12/23 | Relaxed Plonk / Frontend Testing | $\checkmark$ | 59 | | 11/15/23 | Relaxed Plonk / Backend Dev | $\checkmark$ | 60 | | 12/05/23 | Transcript Dev | $\checkmark$ | 61 | | 12/06/23 | Poseidon RO Dev | $\checkmark$ | 62 | | 12/07/23 | PCS Dev | $\checkmark$ | 63 | | 12/08/23 | NIFS Dev | - | 64 | | 12/08/23 | Poseidon Circuit RO Dev | - | 65 | | 12/08/23 | Cycle Circuit Dev | - | 66 | | 12/09/23 | Refactor ark-nifs (arkworks + jellyfish) | $\checkmark$ | 67 | | 12/10/23 | In-depth of secondary(delegation) circuit | - | 68 | 69 |
70 | 71 | ## References 72 | [1] NOVA: https://eprint.iacr.org/2021/370.pdf 73 | 74 | [2] CycleFold: https://eprint.iacr.org/2023/1192.pdf 75 | 76 | [3] Protostar: https://eprint.iacr.org/2023/620.pdf 77 | 78 | [4] Multivariate lookup: https://eprint.iacr.org/2022/1530.pdf 79 | 80 | [5] Cached quotients: https://eprint.iacr.org/2022/1763.pdf 81 | 82 | [6] Improving logUp argument with GKR: https://eprint.iacr.org/2023/1284.pdf -------------------------------------------------------------------------------- /src/poseidon/poseidon_constants.rs: -------------------------------------------------------------------------------- 1 | use crate::poseidon::grain_lfsr::PoseidonGrainLFSR; 2 | use ark_crypto_primitives::sponge::poseidon::PoseidonConfig; 3 | use ark_ff::{fields::models::*, FpConfig, PrimeField}; 4 | use ark_std::{vec, vec::Vec}; 5 | 6 | /// An entry in the default Poseidon parameters 7 | pub struct PoseidonDefaultConfigEntry { 8 | /// The rate (in terms of number of field elements). 9 | pub rate: usize, 10 | /// Exponent used in S-boxes. 11 | pub alpha: usize, 12 | /// Number of rounds in a full-round operation. 13 | pub full_rounds: usize, 14 | /// Number of rounds in a partial-round operation. 15 | pub partial_rounds: usize, 16 | /// Number of matrices to skip when generating parameters using the Grain LFSR. 17 | /// 18 | /// The matrices being skipped are those that do not satisfy all the desired properties. 19 | /// See the [reference implementation](https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/generate_parameters_grain.sage) for more detail. 20 | pub skip_matrices: usize, 21 | } 22 | 23 | impl PoseidonDefaultConfigEntry { 24 | /// Create an entry in `PoseidonDefaultConfig`. 25 | pub const fn new( 26 | rate: usize, 27 | alpha: usize, 28 | full_rounds: usize, 29 | partial_rounds: usize, 30 | skip_matrices: usize, 31 | ) -> Self { 32 | Self { 33 | rate, 34 | alpha, 35 | full_rounds, 36 | partial_rounds, 37 | skip_matrices, 38 | } 39 | } 40 | } 41 | 42 | /// A trait for default Poseidon parameters associated with a prime field 43 | pub trait PoseidonDefaultConfig: FpConfig { 44 | /// An array of the parameters optimized for constraints 45 | /// (rate, alpha, full_rounds, partial_rounds, skip_matrices) 46 | /// for rate = 2, 3, 4, 5, 6, 7, 8 47 | /// 48 | /// Here, `skip_matrices` denote how many matrices to skip before 49 | /// finding one that satisfy all the requirements. 50 | const PARAMS_OPT_FOR_CONSTRAINTS: [PoseidonDefaultConfigEntry; 7]; 51 | 52 | /// An array of the parameters optimized for weights 53 | /// (rate, alpha, full_rounds, partial_rounds, skip_matrices) 54 | /// for rate = 2, 3, 4, 5, 6, 7, 8 55 | const PARAMS_OPT_FOR_WEIGHTS: [PoseidonDefaultConfigEntry; 7]; 56 | } 57 | 58 | /// A field with Poseidon parameters associated 59 | pub trait PoseidonDefaultConfigField: PrimeField { 60 | /// Obtain the default Poseidon parameters for this rate and for this prime field, 61 | /// with a specific optimization goal. 62 | fn get_default_poseidon_parameters( 63 | rate: usize, 64 | optimized_for_weights: bool, 65 | ) -> Option>; 66 | } 67 | 68 | /// Internal function that uses the `PoseidonDefaultConfig` to compute the Poseidon parameters. 69 | pub fn get_default_poseidon_parameters_internal, const N: usize>( 70 | rate: usize, 71 | optimized_for_weights: bool, 72 | ) -> Option>> { 73 | let params_set = if !optimized_for_weights { 74 | P::PARAMS_OPT_FOR_CONSTRAINTS 75 | } else { 76 | P::PARAMS_OPT_FOR_WEIGHTS 77 | }; 78 | 79 | for param in params_set.iter() { 80 | if param.rate == rate { 81 | let (ark, mds) = find_poseidon_ark_and_mds::>( 82 | Fp::::MODULUS_BIT_SIZE as u64, 83 | rate, 84 | param.full_rounds as u64, 85 | param.partial_rounds as u64, 86 | param.skip_matrices as u64, 87 | ); 88 | 89 | return Some(PoseidonConfig { 90 | full_rounds: param.full_rounds, 91 | partial_rounds: param.partial_rounds, 92 | alpha: param.alpha as u64, 93 | ark, 94 | mds, 95 | rate: param.rate, 96 | capacity: 1, 97 | }); 98 | } 99 | } 100 | 101 | None 102 | } 103 | 104 | /// Internal function that computes the ark and mds from the Poseidon Grain LFSR. 105 | pub fn find_poseidon_ark_and_mds( 106 | prime_bits: u64, 107 | rate: usize, 108 | full_rounds: u64, 109 | partial_rounds: u64, 110 | skip_matrices: u64, 111 | ) -> (Vec>, Vec>) { 112 | let mut lfsr = PoseidonGrainLFSR::new( 113 | false, 114 | prime_bits, 115 | (rate + 1) as u64, 116 | full_rounds, 117 | partial_rounds, 118 | ); 119 | 120 | let mut ark = Vec::>::with_capacity((full_rounds + partial_rounds) as usize); 121 | for _ in 0..(full_rounds + partial_rounds) { 122 | ark.push(lfsr.get_field_elements_rejection_sampling(rate + 1)); 123 | } 124 | 125 | let mut mds = Vec::>::with_capacity(rate + 1); 126 | mds.resize(rate + 1, vec![F::zero(); rate + 1]); 127 | for _ in 0..skip_matrices { 128 | let _ = lfsr.get_field_elements_mod_p::(2 * (rate + 1)); 129 | } 130 | 131 | // a qualifying matrix must satisfy the following requirements 132 | // - there is no duplication among the elements in x or y 133 | // - there is no i and j such that x[i] + y[j] = p 134 | // - the resultant MDS passes all the three tests 135 | 136 | let xs = lfsr.get_field_elements_mod_p::(rate + 1); 137 | let ys = lfsr.get_field_elements_mod_p::(rate + 1); 138 | 139 | for i in 0..(rate + 1) { 140 | for j in 0..(rate + 1) { 141 | mds[i][j] = (xs[i] + &ys[j]).inverse().unwrap(); 142 | } 143 | } 144 | 145 | (ark, mds) 146 | } 147 | 148 | impl> PoseidonDefaultConfigField for Fp { 149 | fn get_default_poseidon_parameters( 150 | rate: usize, 151 | optimized_for_weights: bool, 152 | ) -> Option> { 153 | get_default_poseidon_parameters_internal::(rate, optimized_for_weights) 154 | } 155 | } 156 | 157 | #[cfg(test)] 158 | mod test { 159 | use super::*; 160 | use crate::primary::bn254_field::*; 161 | use ark_crypto_primitives::sponge::{ 162 | poseidon::PoseidonSponge, CryptographicSponge, FieldBasedCryptographicSponge, 163 | }; 164 | use ark_ff::MontFp; 165 | 166 | #[test] 167 | fn test_bn254_fq_params() { 168 | // 3, 5, 8, 56, 0 is the best option for bn254 base field Fq 169 | let constraints_rate_3 = Fq::get_default_poseidon_parameters(3, false).unwrap(); 170 | assert_eq!( 171 | constraints_rate_3.ark[0][0], 172 | MontFp!( 173 | "11633431549750490989983886834189948010834808234699737327785600195936805266405" 174 | ) 175 | ); 176 | } 177 | 178 | #[test] 179 | fn test_poseidon_end_to_end() { 180 | let sponge_param = Fq::get_default_poseidon_parameters(3, false).unwrap(); 181 | 182 | let mut sponge = PoseidonSponge::::new(&sponge_param); 183 | sponge.absorb(&vec![Fq::from(0u8), Fq::from(1u8), Fq::from(2u8)]); 184 | let res = sponge.squeeze_native_field_elements(3); 185 | assert_eq!( 186 | res[0], 187 | MontFp!( 188 | "13505558253904840554372886088574885784502988543966086461233803614170723033622" 189 | ) 190 | ); 191 | assert_eq!( 192 | res[1], 193 | MontFp!("2010393222813830976204503948151039392318660137898235940886459326805089000473") 194 | ); 195 | assert_eq!( 196 | res[2], 197 | MontFp!( 198 | "14855598803087928644687564007348125840452284810388803200660562111886369342607" 199 | ) 200 | ); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/primary/plonk.rs: -------------------------------------------------------------------------------- 1 | /// plonk instances for primary circuit over BN254 curve 2 | /// 3 | /// computation of cross terms followed from chapter 3.4 of protostar: https://eprint.iacr.org/2023/620.pdf 4 | /// 5 | use ark_ec::pairing::Pairing; 6 | use ark_ec::CurveGroup; 7 | use ark_ff::{Field, PrimeField}; 8 | use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial}; 9 | use jf_primitives::pcs::prelude::Commitment; 10 | use jf_primitives::pcs::{ 11 | prelude::{PCSError, UnivariateKzgPCS, UnivariateProverParam, UnivariateUniversalParams}, 12 | PolynomialCommitmentScheme, StructuredReferenceString, 13 | }; 14 | use rand::rngs::StdRng; 15 | use rayon::prelude::*; 16 | 17 | use crate::error::MyError; 18 | use crate::primary::kzg::gen_srs_for_testing; 19 | 20 | use std::marker::PhantomData; 21 | 22 | pub(crate) type CommitmentKey = UnivariateProverParam; 23 | 24 | /// Public parameters for a given PLONK 25 | #[derive(Clone)] 26 | pub struct PLONK { 27 | _p: PhantomData, 28 | } 29 | 30 | #[derive(Clone, Debug, PartialEq, Eq)] 31 | pub struct PLONKShape { 32 | pub(crate) num_cons: usize, 33 | pub(crate) num_wire_types: usize, 34 | pub(crate) num_public_input: usize, 35 | 36 | pub(crate) q_lc: Vec>, 37 | pub(crate) q_mul: Vec>, 38 | pub(crate) q_hash: Vec>, 39 | pub(crate) q_ecc: Vec, 40 | pub(crate) q_o: Vec, 41 | pub(crate) q_c: Vec, 42 | } 43 | 44 | /// A type that holds a witness for a given Plonk instance 45 | /// w_0, w_1, w_2, w_3, w_o 46 | #[derive(Clone, Debug, PartialEq, Eq)] 47 | pub struct PLONKWitness { 48 | pub(crate) W: Vec>, 49 | } 50 | 51 | /// A type that holds a commitment vector and public io vector 52 | #[derive(Clone, Debug, PartialEq, Eq)] 53 | pub struct PLONKInstance { 54 | pub(crate) comm_W: Vec>, 55 | pub(crate) X: Vec, 56 | } 57 | 58 | /// relaxed witness 59 | #[derive(Clone, Debug, PartialEq, Eq)] 60 | pub struct RelaxedPLONKWitness { 61 | pub(crate) W: Vec>, 62 | pub(crate) E: Vec>, 63 | } 64 | 65 | /// relaxed instance 66 | #[derive(Clone, Debug, PartialEq, Eq)] 67 | pub struct RelaxedPLONKInstance { 68 | pub(crate) comm_W: Vec>, 69 | pub(crate) comm_E: Vec>, 70 | pub(crate) X: Vec, 71 | pub(crate) u: E::ScalarField, 72 | } 73 | 74 | impl PLONK { 75 | pub fn commitment_key(rng: &mut StdRng, degree: usize) -> CommitmentKey { 76 | let pp: UnivariateUniversalParams = gen_srs_for_testing(rng, degree, 1).unwrap(); 77 | let (ck, _) = pp.trim(degree).unwrap(); 78 | ck 79 | } 80 | } 81 | 82 | impl PLONKShape {} 83 | 84 | impl PLONKWitness { 85 | /// A method to create a witness object using a vector of scalars 86 | pub fn new(S: &PLONKShape, W: &[Vec]) -> Result, MyError> { 87 | if S.num_wire_types != W.len() { 88 | Err(MyError::WitnessError) 89 | } else { 90 | Ok(PLONKWitness { W: W.to_owned() }) 91 | } 92 | } 93 | 94 | /// Commits to the witness using the supplied generators 95 | pub fn commit(&self, ck: &CommitmentKey) -> Vec> { 96 | let com_W = self 97 | .W 98 | .iter() 99 | .map(|w| { 100 | let p = as DenseUVPolynomial< 101 | E::ScalarField, 102 | >>::from_coefficients_vec(w.to_vec()); 103 | UnivariateKzgPCS::::commit(ck, &p).unwrap() 104 | }) 105 | .collect::>>(); 106 | com_W 107 | } 108 | } 109 | 110 | impl PLONKInstance { 111 | /// A method to create an instance object using consitituent elements 112 | pub fn new( 113 | S: &PLONKShape, 114 | comm_W: &Vec>, 115 | X: &[E::ScalarField], 116 | ) -> Result, MyError> { 117 | if S.num_public_input != X.len() { 118 | Err(MyError::PublicIntputError) 119 | } else { 120 | Ok(PLONKInstance { 121 | comm_W: comm_W.to_owned(), 122 | X: X.to_owned(), 123 | }) 124 | } 125 | } 126 | } 127 | 128 | impl RelaxedPLONKWitness { 129 | /// Produces a default RelaxedPLONKWitness given an PLONKShape 130 | pub fn default(S: &PLONKShape) -> RelaxedPLONKWitness { 131 | RelaxedPLONKWitness { 132 | W: (0..S.num_wire_types) 133 | .map(|_| vec![::ZERO; S.num_cons]) 134 | .collect::>>(), 135 | E: (0..S.num_wire_types - 1) 136 | .map(|_| vec![::ZERO; S.num_cons]) 137 | .collect::>>(), 138 | } 139 | } 140 | 141 | /// Initializes a new RelaxedPLONKWitness from an R1CSWitness 142 | pub fn from_plonk_witness( 143 | S: &PLONKShape, 144 | witness: &PLONKWitness, 145 | ) -> RelaxedPLONKWitness { 146 | RelaxedPLONKWitness { 147 | W: witness.W.clone(), 148 | E: (0..S.num_wire_types - 1) 149 | .map(|_| vec![::ZERO; S.num_cons]) 150 | .collect::>>(), 151 | } 152 | } 153 | 154 | /// Commits to the witness using the supplied generators 155 | pub fn commit(&self, ck: &CommitmentKey) -> (Vec>, Vec>) { 156 | let com_func = |vecs: &Vec>| { 157 | vecs.iter() 158 | .map(|v| { 159 | let p = as DenseUVPolynomial< 160 | E::ScalarField, 161 | >>::from_coefficients_vec(v.to_vec()); 162 | UnivariateKzgPCS::::commit(ck, &p).unwrap() 163 | }) 164 | .collect::>>() 165 | }; 166 | 167 | let comm_W = com_func(&self.W); 168 | let comm_E = com_func(&self.E); 169 | 170 | (comm_W, comm_E) 171 | } 172 | 173 | pub fn fold( 174 | &self, 175 | W2: &PLONKWitness, 176 | T: &Vec>, 177 | r: &E::ScalarField, 178 | ) -> Result, MyError> { 179 | let (W1, E1) = (&self.W, &self.E); 180 | let W2 = &W2.W; 181 | 182 | if W1.len() != W2.len() { 183 | return Err(MyError::WitnessError); 184 | } 185 | 186 | let fold_scalar_func = |a_vecs: &Vec>, 187 | b_vecs: &Vec>| { 188 | a_vecs 189 | .par_iter() 190 | .zip(b_vecs) 191 | .map(|(a_col, b_col)| { 192 | a_col 193 | .par_iter() 194 | .zip(b_col) 195 | .map(|(a, b)| *a + *r * *b) 196 | .collect::>() 197 | }) 198 | .collect::>>() 199 | }; 200 | let W = fold_scalar_func(&W1, &W2); 201 | let E = fold_scalar_func(&E1, T); 202 | 203 | Ok(RelaxedPLONKWitness { W, E }) 204 | } 205 | } 206 | 207 | impl RelaxedPLONKInstance { 208 | pub fn default(_ck: &CommitmentKey, S: &PLONKShape) -> RelaxedPLONKInstance { 209 | let (comm_W, comm_E) = ( 210 | (0..S.num_wire_types) 211 | .map(|_| Commitment::::default()) 212 | .collect::>>(), 213 | (0..S.num_wire_types - 1) 214 | .map(|_| Commitment::::default()) 215 | .collect::>>(), 216 | ); 217 | RelaxedPLONKInstance { 218 | comm_W, 219 | comm_E, 220 | u: ::ZERO, 221 | X: vec![::ZERO; S.num_public_input], 222 | } 223 | } 224 | 225 | /// Initializes a new RelaxedPLONKInstance from an PLONKInstance 226 | pub fn from_plonk_instance( 227 | ck: &CommitmentKey, 228 | S: &PLONKShape, 229 | instance: &PLONKInstance, 230 | ) -> RelaxedPLONKInstance { 231 | let mut r_instance = RelaxedPLONKInstance::default(ck, S); 232 | r_instance.comm_W = instance.comm_W.clone(); 233 | r_instance.u = ::ONE; 234 | r_instance.X = instance.X.clone(); 235 | r_instance 236 | } 237 | 238 | /// Initializes a new RelaxedPLONKInstance from an PLONKInstance 239 | pub fn from_plonk_instance_unchecked( 240 | comm_W: &Vec>, 241 | X: &[E::ScalarField], 242 | ) -> RelaxedPLONKInstance { 243 | let comm_E = (0..comm_W.len() - 1) 244 | .map(|_| Commitment::::default()) 245 | .collect::>>(); 246 | RelaxedPLONKInstance { 247 | comm_W: comm_W.to_owned(), 248 | comm_E: comm_E, 249 | u: E::ScalarField::ONE, 250 | X: X.to_vec(), 251 | } 252 | } 253 | 254 | /// Folds an incoming RelaxedPLONKInstance into the current one 255 | pub fn fold( 256 | &self, 257 | U2: &PLONKInstance, 258 | comm_T: &Vec>, 259 | r: &E::ScalarField, 260 | ) -> Result, MyError> { 261 | let (X1, u1, comm_W_1, comm_E_1) = 262 | (&self.X, &self.u, &self.comm_W.clone(), &self.comm_E.clone()); 263 | let (X2, comm_W_2) = (&U2.X, &U2.comm_W); 264 | 265 | // weighted sum of X, comm_W, comm_E, and u 266 | let X = X1 267 | .par_iter() 268 | .zip(X2) 269 | .map(|(a, b)| *a + *r * *b) 270 | .collect::>(); 271 | 272 | let fold_comm_func = |comm_1: &Vec>, comm_2: &Vec>| { 273 | comm_1 274 | .par_iter() 275 | .zip(comm_2) 276 | .map(|(a, b)| { 277 | let a_affine: &E::G1Affine = a.as_ref(); 278 | let b_affine: &E::G1Affine = b.as_ref(); 279 | Commitment((*a_affine + *b_affine * *r).into_affine()) 280 | }) 281 | .collect::>>() 282 | }; 283 | 284 | let comm_W = fold_comm_func(&self.comm_W, &U2.comm_W); 285 | let comm_E = fold_comm_func(&self.comm_E, comm_T); 286 | 287 | let u = *u1 + *r; 288 | 289 | Ok(RelaxedPLONKInstance { 290 | comm_W, 291 | comm_E, 292 | X, 293 | u, 294 | }) 295 | } 296 | } 297 | 298 | impl PLONKShape { 299 | pub fn new( 300 | num_cons: usize, 301 | num_wire_types: usize, 302 | num_public_input: usize, 303 | q_c: &Vec, 304 | q_lc: &Vec>, 305 | q_mul: &Vec>, 306 | q_ecc: &Vec, 307 | q_hash: &Vec>, 308 | q_o: &Vec, 309 | ) -> Result, MyError> { 310 | assert!(q_lc.len() == num_wire_types - 1); 311 | assert!(q_mul.len() == 2); 312 | let is_valid = |num_cons: usize, q: &Vec| -> Result<(), MyError> { 313 | if (q.len() == num_cons) { 314 | Ok(()) 315 | } else { 316 | Err(MyError::SelectorError) 317 | } 318 | }; 319 | 320 | let invalid_num: i32 = vec![ 321 | vec![q_c, q_ecc, q_o], 322 | q_lc.into_iter().collect::>>(), 323 | q_mul.into_iter().collect::>>(), 324 | q_hash.into_iter().collect::>>(), 325 | ] 326 | .concat() 327 | .iter() 328 | .map(|q| { 329 | if (is_valid(num_cons, q).is_err()) { 330 | 1 as i32 331 | } else { 332 | 0 as i32 333 | } 334 | }) 335 | .collect::>() 336 | .iter() 337 | .sum(); 338 | 339 | if (invalid_num > 0) { 340 | return Err(MyError::SelectorError); 341 | } 342 | 343 | Ok(PLONKShape { 344 | num_cons: num_cons, 345 | num_wire_types: num_wire_types, 346 | num_public_input: num_public_input, 347 | q_c: q_c.to_owned(), 348 | q_lc: q_lc.to_owned(), 349 | q_mul: q_mul.to_owned(), 350 | q_ecc: q_ecc.to_owned(), 351 | q_hash: q_hash.to_owned(), 352 | q_o: q_o.to_owned(), 353 | }) 354 | } 355 | 356 | fn grand_product(n: usize, vec: Vec<&E::ScalarField>) -> E::ScalarField { 357 | let first: E::ScalarField = *vec[0]; 358 | if n == 1 { 359 | first 360 | } else { 361 | vec[1..].iter().fold(first, |acc, cur| acc * *cur) 362 | } 363 | } 364 | 365 | fn compute_cross_terms( 366 | degree: usize, 367 | u1: E::ScalarField, 368 | u2: E::ScalarField, 369 | inst1: &Vec>, 370 | inst2: &Vec>, 371 | ) -> Vec> { 372 | assert!(inst1.len() == inst2.len(), "compute cross term"); 373 | 374 | let transpose_matrix = |mat: Vec>| { 375 | let num_row = mat[0].len(); 376 | let mut mut_cols: Vec<_> = mat.into_iter().map(|col| col.into_iter()).collect(); 377 | (0..num_row) 378 | .map(|_| { 379 | mut_cols 380 | .iter_mut() 381 | .map(|n| n.next().unwrap()) 382 | .collect::>() 383 | }) 384 | .collect::>>() 385 | }; 386 | let trans_inst1 = transpose_matrix(inst1.clone()); 387 | let trans_inst2 = transpose_matrix(inst2.clone()); 388 | 389 | let max_degree = 5 as usize; 390 | (1..max_degree) 391 | .rev() 392 | .map(|r_degree| { 393 | let l_degree = max_degree - r_degree; 394 | 395 | trans_inst1 396 | .par_iter() 397 | .zip(&trans_inst2) 398 | .map(|(row_a, row_b)| { 399 | let l_vars = vec![ 400 | vec![&u1; degree], 401 | row_a 402 | .into_iter() 403 | .map(|a| a) 404 | .collect::>(), 405 | ] 406 | .concat(); 407 | let r_vars = vec![ 408 | vec![&u2; degree], 409 | row_b 410 | .into_iter() 411 | .map(|a| a) 412 | .collect::>(), 413 | ] 414 | .concat(); 415 | // let l_vars = vec![vec![u1; degree], row_a].concat(); 416 | // let r_vars = vec![vec![u2; degree], row_b].concat(); 417 | Self::grand_product(l_degree, l_vars) 418 | * Self::grand_product(r_degree, r_vars) 419 | }) 420 | .collect::>() 421 | }) 422 | .rev() 423 | .collect::>>() 424 | } 425 | 426 | fn compute_cross_terms_five_exp( 427 | inst1: &Vec, 428 | inst2: &Vec, 429 | ) -> Vec> { 430 | let count_combination = |n: usize, r: usize| { 431 | if r > n { 432 | 0 433 | } else { 434 | (1..=r).fold(1, |acc, val| acc * (n - val + 1) / val) 435 | } 436 | }; 437 | let vec_pow = |n: usize, vec: &Vec| { 438 | vec.par_iter() 439 | .map(|v| { 440 | let first = *v; 441 | if n == 1 { 442 | first 443 | } else { 444 | vec![v; n - 1].iter().fold(first, |a, b| a * *b) 445 | } 446 | }) 447 | .collect::>() 448 | }; 449 | 450 | let max_degree: usize = 5; 451 | (1..max_degree) 452 | .rev() 453 | .map(|r_degree| { 454 | let l_degree = max_degree - r_degree; 455 | let const_var = count_combination(max_degree, r_degree); 456 | let const_scalar = ::from_bigint( 457 | ::BigInt::from(const_var as u32), 458 | ) 459 | .unwrap(); 460 | let ref_const_scalar = &const_scalar; 461 | let l_pow = vec_pow(l_degree, inst1); 462 | let r_pow = vec_pow(r_degree, inst2); 463 | l_pow 464 | .iter() 465 | .zip(r_pow) 466 | .map(|(a, b)| *ref_const_scalar * a * b) 467 | .collect::>() 468 | }) 469 | .rev() 470 | .collect::>>() 471 | } 472 | 473 | //// compute cross terms and their commitments 474 | /// 1. length of cross term vector equals max_degree - 1 475 | pub fn commit_T( 476 | &self, 477 | ck: &CommitmentKey, 478 | U1: &RelaxedPLONKInstance, 479 | W1: &RelaxedPLONKWitness, 480 | U2: &PLONKInstance, 481 | W2: &PLONKWitness, 482 | ) -> Result<(Vec>, Vec>), MyError> { 483 | assert!(W1.W.len() == self.num_wire_types - 1, "wrong wires"); 484 | // q_ecc operation, u^0 * q_ecc * w_0 * w_1 * w_2 * w_3 * w_o 485 | let ecc_T: Vec> = Self::compute_cross_terms( 486 | 0 as usize, 487 | U1.u, 488 | ::ONE, 489 | &W1.W, 490 | &W2.W, 491 | ); 492 | 493 | // q_lc operation, u^4 * (q_lc_0 * w_0 + q_lc_1 * w_1 + q_lc_2 * w_2 + q_lc_3 * w_3) 494 | let lc_T = (0..self.num_wire_types - 1) 495 | .map(|i| { 496 | Self::compute_cross_terms( 497 | 4, 498 | U1.u, 499 | ::ONE, 500 | &W1.W[i..i + 1].to_vec(), 501 | &W2.W[i..i + 1].to_vec(), 502 | ) 503 | }) 504 | .collect::>>>(); 505 | 506 | // q_mul operation, u^3 * (q_mul_0 * w_0 * w_1 + q_mul_1 * w_2 * w_3) 507 | let mul_T = (0..self.num_wire_types - 1) 508 | .step_by(2) 509 | .map(|i| { 510 | Self::compute_cross_terms( 511 | 3, 512 | U1.u, 513 | ::ONE, 514 | &W1.W[i..i + 2].to_vec(), 515 | &W2.W[i..i + 2].to_vec(), 516 | ) 517 | }) 518 | .collect::>>>(); 519 | 520 | // q_out operation, u^4 * (q_o * w_o) 521 | let out_T = Self::compute_cross_terms( 522 | 4, 523 | U1.u, 524 | ::ONE, 525 | &W1.W[self.num_wire_types - 1..].to_vec(), 526 | &W2.W[self.num_wire_types - 1..].to_vec(), 527 | ); 528 | 529 | // q_c operation, u^5 * q_c 530 | let u1_vec = vec![U1.u; self.num_cons]; 531 | let u2_vec = vec![::ONE; self.num_cons]; 532 | let const_T = Self::compute_cross_terms_five_exp(&u1_vec, &u2_vec); 533 | 534 | // q_hash operation, u^0 * (q_hash_0 * w_0^5 + q_hash_1 * w_1^5 + q_hash_2 * w_2^5 + q_hash_3 * w_3^5) 535 | let hash_T = (0..self.num_wire_types - 1) 536 | .map(|i| Self::compute_cross_terms_five_exp(&W1.W[i], &W2.W[i])) 537 | .collect::>>>(); 538 | 539 | //////////////////////////////// apply selectors on cross terms 540 | let apply_selector = |T: &Vec>, selector: &Vec| { 541 | (0..self.num_wire_types - 1) 542 | .map(|i| { 543 | let ref_T = &T[i]; 544 | ref_T 545 | .par_iter() 546 | .zip(selector) 547 | .map(|(a, b)| *a * *b) 548 | .collect::>() 549 | }) 550 | .collect::>>() 551 | }; 552 | 553 | let (ref_ecc_T, ref_out_T, ref_const_T, ref_q_ecc, ref_q_out, ref_q_const) = 554 | (&ecc_T, &out_T, &const_T, &self.q_ecc, &self.q_o, &self.q_c); 555 | let ecc_result = apply_selector(ref_ecc_T, ref_q_ecc); 556 | let out_result = apply_selector(ref_out_T, ref_q_out); 557 | let const_result = apply_selector(ref_const_T, ref_q_const); 558 | 559 | let lc_result = (0..self.num_wire_types - 1) 560 | .map(|i| { 561 | let (ref_lc_T, ref_q_lc) = (&lc_T[i], &self.q_lc[i]); 562 | apply_selector(ref_lc_T, ref_q_lc) 563 | }) 564 | .collect::>>>(); 565 | 566 | let hash_result = (0..self.num_wire_types - 1) 567 | .map(|i| { 568 | let (ref_hash_T, ref_q_hash) = (&hash_T[i], &self.q_hash[i]); 569 | apply_selector(ref_hash_T, ref_q_hash) 570 | }) 571 | .collect::>>>(); 572 | 573 | let mul_result = (0..2) 574 | .map(|i| { 575 | let (ref_mul_T, ref_q_mul) = (&mul_T[i], &self.q_mul[i]); 576 | apply_selector(ref_mul_T, ref_q_mul) 577 | }) 578 | .collect::>>>(); 579 | 580 | ////////////////////////////////////////// add-on all cross terms 581 | let apply_mat_element_add = 582 | |acc: &Vec>, cur: &Vec>| { 583 | acc.into_iter() 584 | .zip(cur) 585 | .map(|(a_col, b_col)| { 586 | a_col 587 | .iter() 588 | .zip(b_col) 589 | .map(|(a, b)| *a + *b) 590 | .collect::>() 591 | }) 592 | .collect::>>() 593 | }; 594 | 595 | let stack_T = vec![ 596 | vec![&ecc_result, &out_result, &const_result], 597 | lc_result.iter().collect::>>>(), 598 | hash_result 599 | .iter() 600 | .collect::>>>(), 601 | mul_result 602 | .iter() 603 | .collect::>>>(), 604 | ] 605 | .concat(); 606 | let T = stack_T[1..].iter().fold(stack_T[0].clone(), |acc, cur| { 607 | apply_mat_element_add(&acc, cur) 608 | }); 609 | 610 | ////////////////////////////////////////// commit T 611 | let com_T = T 612 | .iter() 613 | .map(|coefficients| { 614 | let poly = as DenseUVPolynomial< 615 | E::ScalarField, 616 | >>::from_coefficients_vec(coefficients.clone()); 617 | UnivariateKzgPCS::::commit(ck, &poly).unwrap() 618 | }) 619 | .collect::>>(); 620 | 621 | Ok((T, com_T)) 622 | } 623 | } 624 | -------------------------------------------------------------------------------- /src/plonk.rs: -------------------------------------------------------------------------------- 1 | /// plonk instances for primary circuit over BN254 curve 2 | /// 3 | /// computation of cross terms followed from chapter 3.4 of protostar: https://eprint.iacr.org/2023/620.pdf 4 | /// 5 | // use ark_ec::pairing::Pairing; 6 | use ark_ff::{Field, PrimeField}; 7 | use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial}; 8 | // use jf_primitives::pcs::prelude::Commitment; 9 | // use jf_primitives::pcs::{ 10 | // prelude::{PCSError, UnivariateKzgPCS, UnivariateProverParam, UnivariateUniversalParams}, 11 | // PolynomialCommitmentScheme, StructuredReferenceString, 12 | // }; 13 | use rand::rngs::StdRng; 14 | use rayon::prelude::*; 15 | 16 | use crate::error::MyError; 17 | // use crate::primary::kzg::gen_srs_for_testing; 18 | use crate::{ 19 | traits::{CommitmentEngineTrait, Group}, 20 | Commitment, CommitmentKey, 21 | }; 22 | 23 | use std::marker::PhantomData; 24 | 25 | // pub(crate) type CommitmentKey = UnivariateProverParam; 26 | 27 | /// Public parameters for a given PLONK 28 | #[derive(Clone)] 29 | pub struct PLONK { 30 | _p: PhantomData, 31 | } 32 | 33 | #[derive(Clone, Debug, PartialEq, Eq)] 34 | pub struct PLONKShape { 35 | pub(crate) num_cons: usize, 36 | pub(crate) num_wire_types: usize, 37 | pub(crate) num_public_input: usize, 38 | 39 | pub(crate) q_lc: Vec::ScalarField>>, 40 | pub(crate) q_mul: Vec::ScalarField>>, 41 | pub(crate) q_hash: Vec::ScalarField>>, 42 | pub(crate) q_ecc: Vec<::ScalarField>, 43 | pub(crate) q_o: Vec<::ScalarField>, 44 | pub(crate) q_c: Vec<::ScalarField>, 45 | } 46 | 47 | /// A type that holds a witness for a given Plonk instance 48 | /// w_0, w_1, w_2, w_3, w_o 49 | #[derive(Clone, Debug, PartialEq, Eq)] 50 | pub struct PLONKWitness { 51 | pub(crate) W: Vec::ScalarField>>, 52 | } 53 | 54 | /// A type that holds a commitment vector and public io vector 55 | #[derive(Clone, Debug, PartialEq, Eq)] 56 | pub struct PLONKInstance { 57 | pub(crate) comm_W: Vec>, 58 | pub(crate) X: Vec<::ScalarField>, 59 | } 60 | 61 | /// relaxed witness 62 | #[derive(Clone, Debug, PartialEq, Eq)] 63 | pub struct RelaxedPLONKWitness { 64 | pub(crate) W: Vec::ScalarField>>, 65 | pub(crate) E: Vec::ScalarField>>, 66 | } 67 | 68 | /// relaxed instance 69 | #[derive(Clone, Debug, PartialEq, Eq)] 70 | pub struct RelaxedPLONKInstance { 71 | pub(crate) comm_W: Vec>, 72 | pub(crate) comm_E: Vec>, 73 | pub(crate) X: Vec<::ScalarField>, 74 | pub(crate) u: ::ScalarField, 75 | } 76 | 77 | impl PLONK { 78 | pub fn commitment_key(rng: &mut StdRng, degree: usize) -> CommitmentKey { 79 | G::CE::setup(rng, degree) 80 | } 81 | } 82 | 83 | impl PLONKShape {} 84 | 85 | impl PLONKWitness { 86 | /// A method to create a witness object using a vector of scalars 87 | pub fn new( 88 | S: &PLONKShape, 89 | W: &[Vec<::ScalarField>], 90 | ) -> Result, MyError> { 91 | if S.num_wire_types != W.len() { 92 | Err(MyError::WitnessError) 93 | } else { 94 | Ok(PLONKWitness { W: W.to_owned() }) 95 | } 96 | } 97 | 98 | /// Commits to the witness using the supplied generators 99 | pub fn commit(&self, ck: &CommitmentKey) -> Vec> { 100 | let com_W = self 101 | .W 102 | .iter() 103 | .map(|w| { 104 | G::CE::commit(ck, w.as_slice()).unwarp(); 105 | }) 106 | .collect::>>(); 107 | com_W 108 | } 109 | } 110 | 111 | impl PLONKInstance { 112 | /// A method to create an instance object using consitituent elements 113 | pub fn new( 114 | S: &PLONKShape, 115 | comm_W: &Vec>, 116 | X: &[::ScalarField], 117 | ) -> Result, MyError> { 118 | if S.num_public_input != X.len() { 119 | Err(MyError::PublicIntputError) 120 | } else { 121 | Ok(PLONKInstance { 122 | comm_W: comm_W.to_owned(), 123 | X: X.to_owned(), 124 | }) 125 | } 126 | } 127 | } 128 | 129 | impl RelaxedPLONKWitness { 130 | /// Produces a default RelaxedPLONKWitness given an PLONKShape 131 | pub fn default(S: &PLONKShape) -> RelaxedPLONKWitness { 132 | RelaxedPLONKWitness { 133 | W: (0..S.num_wire_types) 134 | .map(|_| vec![<::ScalarField as Field>::ZERO; S.num_cons]) 135 | .collect::::ScalarField>>>(), 136 | E: (0..S.num_wire_types - 1) 137 | .map(|_| vec![<::ScalarField as Field>::ZERO; S.num_cons]) 138 | .collect::::ScalarField>>>(), 139 | } 140 | } 141 | 142 | /// Initializes a new RelaxedPLONKWitness from an R1CSWitness 143 | pub fn from_plonk_witness( 144 | S: &PLONKShape, 145 | witness: &PLONKWitness, 146 | ) -> RelaxedPLONKWitness { 147 | RelaxedPLONKWitness { 148 | W: witness.W.clone(), 149 | E: (0..S.num_wire_types - 1) 150 | .map(|_| vec![<::ScalarField as Field>::ZERO; S.num_cons]) 151 | .collect::::ScalarField>>>(), 152 | } 153 | } 154 | 155 | /// Commits to the witness using the supplied generators 156 | pub fn commit(&self, ck: &CommitmentKey) -> (Vec>, Vec>) { 157 | let com_func = |vecs: &Vec::ScalarField>>| { 158 | vecs.iter() 159 | .map(|v| { 160 | let p = ::ScalarField> as DenseUVPolynomial< 161 | ::ScalarField, 162 | >>::from_coefficients_vec(v.to_vec()); 163 | UnivariateKzgPCS::::commit(ck, &p).unwrap() 164 | }) 165 | .collect::>>() 166 | }; 167 | 168 | let comm_W = com_func(&self.W); 169 | let comm_E = com_func(&self.E); 170 | 171 | (comm_W, comm_E) 172 | } 173 | 174 | pub fn fold( 175 | &self, 176 | W2: &PLONKWitness, 177 | T: &Vec::ScalarField>>, 178 | r: &::ScalarField, 179 | ) -> Result, MyError> { 180 | let (W1, E1) = (&self.W, &self.E); 181 | let W2 = &W2.W; 182 | 183 | if W1.len() != W2.len() { 184 | return Err(MyError::WitnessError); 185 | } 186 | 187 | let fold_scalar_func = 188 | |a_vecs: &Vec::ScalarField>>, 189 | b_vecs: &Vec::ScalarField>>| { 190 | a_vecs 191 | .par_iter() 192 | .zip(b_vecs) 193 | .map(|(a_col, b_col)| { 194 | a_col 195 | .par_iter() 196 | .zip(b_col) 197 | .map(|(a, b)| *a + *r * *b) 198 | .collect::::ScalarField>>() 199 | }) 200 | .collect::::ScalarField>>>() 201 | }; 202 | let W = fold_scalar_func(&W1, &W2); 203 | let E = fold_scalar_func(&E1, T); 204 | 205 | Ok(RelaxedPLONKWitness { W, E }) 206 | } 207 | } 208 | 209 | impl RelaxedPLONKInstance { 210 | pub fn default(_ck: &CommitmentKey, S: &PLONKShape) -> RelaxedPLONKInstance { 211 | let (comm_W, comm_E) = ( 212 | (0..S.num_wire_types) 213 | .map(|_| Commitment::::default()) 214 | .collect::>>(), 215 | (0..S.num_wire_types - 1) 216 | .map(|_| Commitment::::default()) 217 | .collect::>>(), 218 | ); 219 | RelaxedPLONKInstance { 220 | comm_W, 221 | comm_E, 222 | u: <::ScalarField as Field>::ZERO, 223 | X: vec![<::ScalarField as Field>::ZERO; S.num_public_input], 224 | } 225 | } 226 | 227 | /// Initializes a new RelaxedPLONKInstance from an PLONKInstance 228 | pub fn from_plonk_instance( 229 | ck: &CommitmentKey, 230 | S: &PLONKShape, 231 | instance: &PLONKInstance, 232 | ) -> RelaxedPLONKInstance { 233 | let mut r_instance = RelaxedPLONKInstance::default(ck, S); 234 | r_instance.comm_W = instance.comm_W.clone(); 235 | r_instance.u = <::ScalarField as Field>::ONE; 236 | r_instance.X = instance.X.clone(); 237 | r_instance 238 | } 239 | 240 | /// Initializes a new RelaxedPLONKInstance from an PLONKInstance 241 | pub fn from_plonk_instance_unchecked( 242 | comm_W: &Vec>, 243 | X: &[::ScalarField], 244 | ) -> RelaxedPLONKInstance { 245 | let comm_E = (0..comm_W.len() - 1) 246 | .map(|_| Commitment::::default()) 247 | .collect::>>(); 248 | RelaxedPLONKInstance { 249 | comm_W: comm_W.to_owned(), 250 | comm_E: comm_E, 251 | u: ::ScalarField::ONE, 252 | X: X.to_vec(), 253 | } 254 | } 255 | 256 | /// Folds an incoming RelaxedPLONKInstance into the current one 257 | pub fn fold( 258 | &self, 259 | U2: &PLONKInstance, 260 | comm_T: &Vec>, 261 | r: &::ScalarField, 262 | ) -> Result, MyError> { 263 | let (X1, u1, comm_W_1, comm_E_1) = 264 | (&self.X, &self.u, &self.comm_W.clone(), &self.comm_E.clone()); 265 | let (X2, comm_W_2) = (&U2.X, &U2.comm_W); 266 | 267 | // weighted sum of X, comm_W, comm_E, and u 268 | let X = X1 269 | .par_iter() 270 | .zip(X2) 271 | .map(|(a, b)| *a + *r * *b) 272 | .collect::::ScalarField>>(); 273 | 274 | let fold_comm_func = |comm_1: &Vec>, comm_2: &Vec>| { 275 | comm_1 276 | .par_iter() 277 | .zip(comm_2) 278 | .map(|(a, b)| { 279 | let a_affine: &E::G1Affine = a.as_ref(); 280 | let b_affine: &E::G1Affine = b.as_ref(); 281 | Commitment((*a_affine + *b_affine * *r).into_affine()) 282 | }) 283 | .collect::>>() 284 | }; 285 | 286 | let comm_W = fold_comm_func(&self.comm_W, &U2.comm_W); 287 | let comm_E = fold_comm_func(&self.comm_E, comm_T); 288 | 289 | let u = *u1 + *r; 290 | 291 | Ok(RelaxedPLONKInstance { 292 | comm_W, 293 | comm_E, 294 | X, 295 | u, 296 | }) 297 | } 298 | } 299 | 300 | impl PLONKShape { 301 | pub fn new( 302 | num_cons: usize, 303 | num_wire_types: usize, 304 | num_public_input: usize, 305 | q_c: &Vec<::ScalarField>, 306 | q_lc: &Vec::ScalarField>>, 307 | q_mul: &Vec::ScalarField>>, 308 | q_ecc: &Vec<::ScalarField>, 309 | q_hash: &Vec::ScalarField>>, 310 | q_o: &Vec<::ScalarField>, 311 | ) -> Result, MyError> { 312 | assert!(q_lc.len() == num_wire_types - 1); 313 | assert!(q_mul.len() == 2); 314 | let is_valid = 315 | |num_cons: usize, q: &Vec<::ScalarField>| -> Result<(), MyError> { 316 | if (q.len() == num_cons) { 317 | Ok(()) 318 | } else { 319 | Err(MyError::SelectorError) 320 | } 321 | }; 322 | 323 | let invalid_num: i32 = vec![ 324 | vec![q_c, q_ecc, q_o], 325 | q_lc.into_iter() 326 | .collect::::ScalarField>>>(), 327 | q_mul 328 | .into_iter() 329 | .collect::::ScalarField>>>(), 330 | q_hash 331 | .into_iter() 332 | .collect::::ScalarField>>>(), 333 | ] 334 | .concat() 335 | .iter() 336 | .map(|q| { 337 | if (is_valid(num_cons, q).is_err()) { 338 | 1 as i32 339 | } else { 340 | 0 as i32 341 | } 342 | }) 343 | .collect::>() 344 | .iter() 345 | .sum(); 346 | 347 | if (invalid_num > 0) { 348 | return Err(MyError::SelectorError); 349 | } 350 | 351 | Ok(PLONKShape { 352 | num_cons: num_cons, 353 | num_wire_types: num_wire_types, 354 | num_public_input: num_public_input, 355 | q_c: q_c.to_owned(), 356 | q_lc: q_lc.to_owned(), 357 | q_mul: q_mul.to_owned(), 358 | q_ecc: q_ecc.to_owned(), 359 | q_hash: q_hash.to_owned(), 360 | q_o: q_o.to_owned(), 361 | }) 362 | } 363 | 364 | fn grand_product(n: usize, vec: Vec<&::ScalarField>) -> ::ScalarField { 365 | let first: ::ScalarField = *vec[0]; 366 | if n == 1 { 367 | first 368 | } else { 369 | vec[1..].iter().fold(first, |acc, cur| acc * *cur) 370 | } 371 | } 372 | 373 | fn compute_cross_terms( 374 | degree: usize, 375 | u1: ::ScalarField, 376 | u2: ::ScalarField, 377 | inst1: &Vec::ScalarField>>, 378 | inst2: &Vec::ScalarField>>, 379 | ) -> Vec::ScalarField>> { 380 | assert!(inst1.len() == inst2.len(), "compute cross term"); 381 | 382 | let transpose_matrix = |mat: Vec::ScalarField>>| { 383 | let num_row = mat[0].len(); 384 | let mut mut_cols: Vec<_> = mat.into_iter().map(|col| col.into_iter()).collect(); 385 | (0..num_row) 386 | .map(|_| { 387 | mut_cols 388 | .iter_mut() 389 | .map(|n| n.next().unwrap()) 390 | .collect::::ScalarField>>() 391 | }) 392 | .collect::::ScalarField>>>() 393 | }; 394 | let trans_inst1 = transpose_matrix(inst1.clone()); 395 | let trans_inst2 = transpose_matrix(inst2.clone()); 396 | 397 | let max_degree = 5 as usize; 398 | (1..max_degree) 399 | .rev() 400 | .map(|r_degree| { 401 | let l_degree = max_degree - r_degree; 402 | 403 | trans_inst1 404 | .par_iter() 405 | .zip(&trans_inst2) 406 | .map(|(row_a, row_b)| { 407 | let l_vars = vec![ 408 | vec![&u1; degree], 409 | row_a 410 | .into_iter() 411 | .map(|a| a) 412 | .collect::::ScalarField>>(), 413 | ] 414 | .concat(); 415 | let r_vars = vec![ 416 | vec![&u2; degree], 417 | row_b 418 | .into_iter() 419 | .map(|a| a) 420 | .collect::::ScalarField>>(), 421 | ] 422 | .concat(); 423 | // let l_vars = vec![vec![u1; degree], row_a].concat(); 424 | // let r_vars = vec![vec![u2; degree], row_b].concat(); 425 | Self::grand_product(l_degree, l_vars) 426 | * Self::grand_product(r_degree, r_vars) 427 | }) 428 | .collect::::ScalarField>>() 429 | }) 430 | .rev() 431 | .collect::::ScalarField>>>() 432 | } 433 | 434 | fn compute_cross_terms_five_exp( 435 | inst1: &Vec<::ScalarField>, 436 | inst2: &Vec<::ScalarField>, 437 | ) -> Vec::ScalarField>> { 438 | let count_combination = |n: usize, r: usize| { 439 | if r > n { 440 | 0 441 | } else { 442 | (1..=r).fold(1, |acc, val| acc * (n - val + 1) / val) 443 | } 444 | }; 445 | let vec_pow = |n: usize, vec: &Vec<::ScalarField>| { 446 | vec.par_iter() 447 | .map(|v| { 448 | let first = *v; 449 | if n == 1 { 450 | first 451 | } else { 452 | vec![v; n - 1].iter().fold(first, |a, b| a * *b) 453 | } 454 | }) 455 | .collect::::ScalarField>>() 456 | }; 457 | 458 | let max_degree: usize = 5; 459 | (1..max_degree) 460 | .rev() 461 | .map(|r_degree| { 462 | let l_degree = max_degree - r_degree; 463 | let const_var = count_combination(max_degree, r_degree); 464 | let const_scalar = <::ScalarField as PrimeField>::from_bigint( 465 | <::ScalarField as PrimeField>::BigInt::from(const_var as u32), 466 | ) 467 | .unwrap(); 468 | let ref_const_scalar = &const_scalar; 469 | let l_pow = vec_pow(l_degree, inst1); 470 | let r_pow = vec_pow(r_degree, inst2); 471 | l_pow 472 | .iter() 473 | .zip(r_pow) 474 | .map(|(a, b)| *ref_const_scalar * a * b) 475 | .collect::::ScalarField>>() 476 | }) 477 | .rev() 478 | .collect::::ScalarField>>>() 479 | } 480 | 481 | //// compute cross terms and their commitments 482 | /// 1. length of cross term vector equals max_degree - 1 483 | pub fn commit_T( 484 | &self, 485 | ck: &CommitmentKey, 486 | U1: &RelaxedPLONKInstance, 487 | W1: &RelaxedPLONKWitness, 488 | U2: &PLONKInstance, 489 | W2: &PLONKWitness, 490 | ) -> Result<(Vec::ScalarField>>, Vec>), MyError> { 491 | assert!(W1.W.len() == self.num_wire_types - 1, "wrong wires"); 492 | // q_ecc operation, u^0 * q_ecc * w_0 * w_1 * w_2 * w_3 * w_o 493 | let ecc_T: Vec::ScalarField>> = Self::compute_cross_terms( 494 | 0 as usize, 495 | U1.u, 496 | <::ScalarField as Field>::ONE, 497 | &W1.W, 498 | &W2.W, 499 | ); 500 | 501 | // q_lc operation, u^4 * (q_lc_0 * w_0 + q_lc_1 * w_1 + q_lc_2 * w_2 + q_lc_3 * w_3) 502 | let lc_T = (0..self.num_wire_types - 1) 503 | .map(|i| { 504 | Self::compute_cross_terms( 505 | 4, 506 | U1.u, 507 | <::ScalarField as Field>::ONE, 508 | &W1.W[i..i + 1].to_vec(), 509 | &W2.W[i..i + 1].to_vec(), 510 | ) 511 | }) 512 | .collect::::ScalarField>>>>(); 513 | 514 | // q_mul operation, u^3 * (q_mul_0 * w_0 * w_1 + q_mul_1 * w_2 * w_3) 515 | let mul_T = (0..self.num_wire_types - 1) 516 | .step_by(2) 517 | .map(|i| { 518 | Self::compute_cross_terms( 519 | 3, 520 | U1.u, 521 | <::ScalarField as Field>::ONE, 522 | &W1.W[i..i + 2].to_vec(), 523 | &W2.W[i..i + 2].to_vec(), 524 | ) 525 | }) 526 | .collect::::ScalarField>>>>(); 527 | 528 | // q_out operation, u^4 * (q_o * w_o) 529 | let out_T = Self::compute_cross_terms( 530 | 4, 531 | U1.u, 532 | <::ScalarField as Field>::ONE, 533 | &W1.W[self.num_wire_types - 1..].to_vec(), 534 | &W2.W[self.num_wire_types - 1..].to_vec(), 535 | ); 536 | 537 | // q_c operation, u^5 * q_c 538 | let u1_vec = vec![U1.u; self.num_cons]; 539 | let u2_vec = vec![<::ScalarField as Field>::ONE; self.num_cons]; 540 | let const_T = Self::compute_cross_terms_five_exp(&u1_vec, &u2_vec); 541 | 542 | // q_hash operation, u^0 * (q_hash_0 * w_0^5 + q_hash_1 * w_1^5 + q_hash_2 * w_2^5 + q_hash_3 * w_3^5) 543 | let hash_T = (0..self.num_wire_types - 1) 544 | .map(|i| Self::compute_cross_terms_five_exp(&W1.W[i], &W2.W[i])) 545 | .collect::::ScalarField>>>>(); 546 | 547 | //////////////////////////////// apply selectors on cross terms 548 | let apply_selector = 549 | |T: &Vec::ScalarField>>, selector: &Vec<::ScalarField>| { 550 | (0..self.num_wire_types - 1) 551 | .map(|i| { 552 | let ref_T = &T[i]; 553 | ref_T 554 | .par_iter() 555 | .zip(selector) 556 | .map(|(a, b)| *a * *b) 557 | .collect::::ScalarField>>() 558 | }) 559 | .collect::::ScalarField>>>() 560 | }; 561 | 562 | let (ref_ecc_T, ref_out_T, ref_const_T, ref_q_ecc, ref_q_out, ref_q_const) = 563 | (&ecc_T, &out_T, &const_T, &self.q_ecc, &self.q_o, &self.q_c); 564 | let ecc_result = apply_selector(ref_ecc_T, ref_q_ecc); 565 | let out_result = apply_selector(ref_out_T, ref_q_out); 566 | let const_result = apply_selector(ref_const_T, ref_q_const); 567 | 568 | let lc_result = (0..self.num_wire_types - 1) 569 | .map(|i| { 570 | let (ref_lc_T, ref_q_lc) = (&lc_T[i], &self.q_lc[i]); 571 | apply_selector(ref_lc_T, ref_q_lc) 572 | }) 573 | .collect::::ScalarField>>>>(); 574 | 575 | let hash_result = (0..self.num_wire_types - 1) 576 | .map(|i| { 577 | let (ref_hash_T, ref_q_hash) = (&hash_T[i], &self.q_hash[i]); 578 | apply_selector(ref_hash_T, ref_q_hash) 579 | }) 580 | .collect::::ScalarField>>>>(); 581 | 582 | let mul_result = (0..2) 583 | .map(|i| { 584 | let (ref_mul_T, ref_q_mul) = (&mul_T[i], &self.q_mul[i]); 585 | apply_selector(ref_mul_T, ref_q_mul) 586 | }) 587 | .collect::::ScalarField>>>>(); 588 | 589 | ////////////////////////////////////////// add-on all cross terms 590 | let apply_mat_element_add = 591 | |acc: &Vec::ScalarField>>, 592 | cur: &Vec::ScalarField>>| { 593 | acc.into_iter() 594 | .zip(cur) 595 | .map(|(a_col, b_col)| { 596 | a_col 597 | .iter() 598 | .zip(b_col) 599 | .map(|(a, b)| *a + *b) 600 | .collect::::ScalarField>>() 601 | }) 602 | .collect::::ScalarField>>>() 603 | }; 604 | 605 | let stack_T = vec![ 606 | vec![&ecc_result, &out_result, &const_result], 607 | lc_result 608 | .iter() 609 | .collect::::ScalarField>>>>(), 610 | hash_result 611 | .iter() 612 | .collect::::ScalarField>>>>(), 613 | mul_result 614 | .iter() 615 | .collect::::ScalarField>>>>(), 616 | ] 617 | .concat(); 618 | let T = stack_T[1..].iter().fold(stack_T[0].clone(), |acc, cur| { 619 | apply_mat_element_add(&acc, cur) 620 | }); 621 | 622 | ////////////////////////////////////////// commit T 623 | let com_T = T 624 | .iter() 625 | .map(|coefficients| { 626 | let poly = ::ScalarField> as DenseUVPolynomial< 627 | ::ScalarField, 628 | >>::from_coefficients_vec(coefficients.clone()); 629 | UnivariateKzgPCS::::commit(ck, &poly).unwrap() 630 | }) 631 | .collect::>>(); 632 | 633 | Ok((T, com_T)) 634 | } 635 | } 636 | --------------------------------------------------------------------------------