diff --git a/Cargo.toml b/Cargo.toml index 0baec55..bf87c71 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ members = [ # "evm-vrfier", "pasta-tree", "w3f-plonk-common", - "w3f-ring-proof", +# "w3f-ring-proof", # "w3f-ring-vrf-snark", ] diff --git a/pasta-tree/Cargo.toml b/pasta-tree/Cargo.toml index 8a4348b..0697a1e 100644 --- a/pasta-tree/Cargo.toml +++ b/pasta-tree/Cargo.toml @@ -11,7 +11,7 @@ repository = "https://github.com/w3f/ring-proof" [dependencies] w3f-pcs.workspace = true w3f-plonk-common.workspace = true -w3f-ring-proof = { path = "../w3f-ring-proof", default-features = false } +#w3f-ring-proof = { path = "../w3f-ring-proof", default-features = false } ark-pallas = { version = "0.6", default-features = false, features = ["curve"] } ark-vesta = { version = "0.6", default-features = false } ark-transcript.workspace = true @@ -23,7 +23,9 @@ ark-serialize.workspace = true rayon = { workspace = true, optional = true } [dev-dependencies] +num-format = "0.4" ark-bls12-381.workspace = true +ark-ed-on-bls12-381-bandersnatch.workspace = true criterion.workspace = true [features] @@ -36,7 +38,7 @@ std = [ "ark-serialize/std", "w3f-pcs/std", "w3f-plonk-common/std", - "w3f-ring-proof/std", +# "w3f-ring-proof/std", ] parallel = [ "std", @@ -47,7 +49,7 @@ parallel = [ "ark-poly/parallel", "w3f-pcs/parallel", "w3f-plonk-common/parallel", - "w3f-ring-proof/parallel", +# "w3f-ring-proof/parallel", ] print-trace = ["ark-std/print-trace", ] asm = ["w3f-pcs/asm"] \ No newline at end of file diff --git a/pasta-tree/src/auth_path/blinded.rs b/pasta-tree/src/auth_path/blinded.rs index 041b4e3..d7955bb 100644 --- a/pasta-tree/src/auth_path/blinded.rs +++ b/pasta-tree/src/auth_path/blinded.rs @@ -1,7 +1,8 @@ use crate::auth_path::node::LevelWitnessWithBlinding; -use crate::{CycleParams, CycleSide}; +use crate::{CircuitParams, CycleParams, CycleSide}; use ark_ec::CurveGroup; use ark_ff::PrimeField; +use w3f_pcs::pcs::ipa::hiding::HidingIpa; pub struct AuthenticationPathWithBlinding { pub(crate) c0_path: Vec>, @@ -14,41 +15,36 @@ pub struct BlindedAuthenticationPath { pub(crate) c1_path: Vec, } -impl AuthenticationPathWithBlinding +impl AuthenticationPathWithBlinding where - F0: PrimeField, - F1: PrimeField, - C0: CurveGroup, - C1: CurveGroup, + C0: CurveGroup, + C1: CurveGroup, { pub(crate) fn apply_bfs( &self, - params: &CycleParams, + c0_pcs_params: &HidingIpa, + c1_pcs_params: &HidingIpa, ) -> BlindedAuthenticationPath { let c0_path = self .c0_path .iter() - .map(|c0_level| { - c0_level - .blinded_path_node(¶ms.c0_params.pcs_params) - .unwrap() - }) + .map(|c0_level| c0_level.blinded_path_node(c0_pcs_params).unwrap()) .collect(); let c1_path = self .c1_path .iter() - .map(|c1_level| { - c1_level - .blinded_path_node(¶ms.c1_params.pcs_params) - .unwrap() - }) + .map(|c1_level| c1_level.blinded_path_node(c1_pcs_params).unwrap()) .collect(); BlindedAuthenticationPath { c0_path, c1_path } } - pub fn compute_root( + pub fn compute_root( &self, - params: &CycleParams, - ) -> Result, ()> { + params: &CycleParams, + ) -> Result, ()> + where + P0: CircuitParams, + P1: CircuitParams, + { let mut c0_path_iter = self.c0_path.iter(); let c0_nodes = c0_path_iter.next().unwrap(); let mut parent_on_c1 = c0_nodes.compute_parent(¶ms.c1_params)?; diff --git a/pasta-tree/src/auth_path/node.rs b/pasta-tree/src/auth_path/node.rs index 27c13fb..ed16793 100644 --- a/pasta-tree/src/auth_path/node.rs +++ b/pasta-tree/src/auth_path/node.rs @@ -1,4 +1,4 @@ -use crate::CycleSideParams; +use crate::{CircuitParams, CycleSideParams}; use ark_ec::{AffineRepr, CurveGroup}; use ark_ff::{PrimeField, Zero}; use ark_std::UniformRand; @@ -53,29 +53,31 @@ impl LevelWitness { self.with_blinding(G::ScalarField::rand(rng), parent_bf) } - pub fn compute_parent>( - &self, - params: &CycleSideParams, - ) -> Result + pub fn compute_parent(&self, params: &CycleSideParams) -> Result where G::BaseField: PrimeField, + C: CurveGroup, + P: CircuitParams, { self.compute_parent_with_bf(params, C::ScalarField::zero()) } - fn compute_parent_with_bf>( + fn compute_parent_with_bf( &self, - params: &CycleSideParams, + params: &CycleSideParams, bf: C::ScalarField, ) -> Result where G::BaseField: PrimeField, + C: CurveGroup, + P: CircuitParams, { - params.commit_nodes(&self.siblings, bf) + params.commit_tree_nodes(&self.x_coords(), bf).map(|c| c.0) } } /// NB! It is not "blinded", meaning that the blinding factor hasn't been applied. +#[derive(Clone)] pub struct LevelWitnessWithBlinding { pub(crate) level_witness: LevelWitness, /// the verifier gets `Ci' = siblings[i] + bf.H` @@ -98,12 +100,14 @@ impl LevelWitnessWithBlinding { Ok(blinded_path_node.0) } - pub(crate) fn compute_parent>( + pub(crate) fn compute_parent( &self, - params: &CycleSideParams, + params: &CycleSideParams, ) -> Result where G::BaseField: PrimeField, + C: CurveGroup, + P: CircuitParams, { self.level_witness .compute_parent_with_bf(params, self.parent_bf) diff --git a/pasta-tree/src/auth_path/path.rs b/pasta-tree/src/auth_path/path.rs index d23d388..5ad436b 100644 --- a/pasta-tree/src/auth_path/path.rs +++ b/pasta-tree/src/auth_path/path.rs @@ -1,8 +1,9 @@ use crate::auth_path::blinded::AuthenticationPathWithBlinding; use crate::auth_path::node::LevelWitness; -use crate::{CycleParams, CycleSide}; +use crate::{CircuitParams, CycleParams, CycleSide}; use ark_ec::CurveGroup; -use ark_ff::PrimeField; +use ark_ff::UniformRand; +use ark_ff::{PrimeField, Zero}; use ark_std::rand::Rng; /// A non-hiding authentication path from a leaf to the root, split between the curves of the cycle. @@ -19,12 +20,10 @@ pub struct AuthenticationPath { pub c1_path: Vec>, } -impl AuthenticationPath +impl AuthenticationPath where - F0: PrimeField, - F1: PrimeField, - C0: CurveGroup, - C1: CurveGroup, + C0: CurveGroup, + C1: CurveGroup, { pub fn with_blinding(&self, rng: &mut R) -> AuthenticationPathWithBlinding { let mut path_0 = Vec::with_capacity(self.c0_path.len()); @@ -32,14 +31,14 @@ where let mut c0_path_iter = self.c0_path.iter(); let mut c0_nodes = c0_path_iter.next().unwrap(); // shouldn't be empty - let mut c0_bf = C0::ScalarField::rand(rng); + let mut c0_bf = C0::ScalarField::from(u128::rand(rng)); for c1_nodes in self.c1_path.iter() { - let c1_bf = C1::ScalarField::rand(rng); + let c1_bf = C1::ScalarField::from(u128::rand(rng)); path_0.push(c0_nodes.with_blinding(c0_bf, c1_bf)); match c0_path_iter.next() { Some(c0_nodes_) => { c0_nodes = c0_nodes_; - c0_bf = C0::ScalarField::rand(rng); + c0_bf = C0::ScalarField::from(u128::rand(rng)); path_1.push(c1_nodes.with_blinding(c1_bf, c0_bf)); } None => { @@ -69,10 +68,14 @@ where self.c0_path[0].path_node() } - pub fn compute_root( + pub fn compute_root( &self, - params: &CycleParams, - ) -> Result, ()> { + params: &CycleParams, + ) -> Result, ()> + where + P0: CircuitParams, + P1: CircuitParams, + { let mut c0_path_iter = self.c0_path.iter(); let c0_nodes = c0_path_iter.next().unwrap(); // shouldn't be empty let mut parent_on_c1 = c0_nodes.compute_parent(¶ms.c1_params)?; @@ -100,6 +103,7 @@ where #[cfg(test)] mod tests { use super::*; + use crate::circuit_tall::params::PiopParams; use crate::tests::random_path; use ark_std::test_rng; @@ -108,8 +112,12 @@ mod tests { let rng = &mut test_rng(); let domain_size = 2usize.pow(9); - let params = - CycleParams::::setup(domain_size, rng); + let params = CycleParams::< + ark_pallas::Projective, + ark_vesta::Projective, + PiopParams, + PiopParams, + >::setup(domain_size, rng); let (leaf, path, root) = random_path(¶ms, 2, rng); diff --git a/pasta-tree/src/circuit_fat/mod.rs b/pasta-tree/src/circuit_fat/mod.rs new file mode 100644 index 0000000..e7e53b4 --- /dev/null +++ b/pasta-tree/src/circuit_fat/mod.rs @@ -0,0 +1,72 @@ +use ark_ec::CurveGroup; +use ark_ff::PrimeField; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{vec, vec::Vec}; +use w3f_pcs::pcs::commitment::WrappedAffine; +use w3f_plonk_common::{ColumnsCommited, ColumnsEvaluated}; + +pub mod params; +pub mod prover; +pub mod verifier; + +#[derive(Clone, Debug, CanonicalSerialize, CanonicalDeserialize)] +pub struct ProofComms { + pub(crate) node_idx: WrappedAffine, + pub(crate) bf_bits: WrappedAffine, + pub(crate) selected_node_acc: WrappedAffine, + pub(crate) blinded_node_acc: [WrappedAffine; 2], + pub(crate) node_idx_sum_acc: WrappedAffine, +} + +impl ColumnsCommited> for ProofComms { + fn to_vec(self) -> Vec> { + self.into() + } +} + +impl From> for Vec> { + fn from(value: ProofComms) -> Self { + let [blinded_node_acc_x, blinded_node_acc_y] = value.blinded_node_acc; + vec![ + value.node_idx, + value.bf_bits, + value.selected_node_acc, + blinded_node_acc_x, + blinded_node_acc_y, + value.node_idx_sum_acc, + ] + } +} + +#[derive(Clone, Debug, CanonicalSerialize, CanonicalDeserialize)] +pub struct ProofEvals { + pub(crate) x_coords: F, + pub(crate) h_powers: [F; 2], + pub(crate) node_idx: F, + pub(crate) bf_bits: F, + pub(crate) selected_node_acc: F, + pub(crate) blinded_node_acc: [F; 2], + pub(crate) node_idx_sum_acc: F, +} + +impl From> for Vec { + fn from(value: ProofEvals) -> Self { + vec![ + value.x_coords, + value.h_powers[0], + value.h_powers[1], + value.node_idx, + value.bf_bits, + value.selected_node_acc, + value.blinded_node_acc[0], + value.blinded_node_acc[1], + value.node_idx_sum_acc, + ] + } +} + +impl ColumnsEvaluated for ProofEvals { + fn to_vec(self) -> Vec { + self.into() + } +} diff --git a/pasta-tree/src/circuit_fat/params.rs b/pasta-tree/src/circuit_fat/params.rs new file mode 100644 index 0000000..5d8679d --- /dev/null +++ b/pasta-tree/src/circuit_fat/params.rs @@ -0,0 +1,144 @@ +use crate::CircuitParams; +use crate::auth_path::node::LevelWitnessWithBlinding; +use crate::circuit_fat::prover::PiopProver; +use crate::circuit_fat::verifier::PiopVerifier; +// use ark_ec::short_weierstrass::{Affine as SwAffine, SWCurveConfig}; +use ark_ec::{AffineRepr, CurveGroup}; +use ark_ff::One; +use ark_ff::{AdditiveGroup, BigInteger, PrimeField, Zero}; +use ark_std::{vec, vec::Vec}; +use w3f_pcs::pcs::commitment::WrappedAffine; +use w3f_plonk_common::FieldColumn; +use w3f_plonk_common::domain::Domain; +use w3f_plonk_common::gadgets::booleanity::BitColumn; +use w3f_plonk_common::gadgets::ec::AffineColumn; + +/// Plonk Interactive Oracle Proofs (PIOP) parameters. +#[derive(Clone)] +pub struct PiopParams> { + /// Domain over which the piop is represented. + pub domain: Domain, + /// Number of bits used to represent a scalar. + pub scalar_bitlen: usize, + /// Blinding base point. + pub h: G, +} + +impl> CircuitParams + for PiopParams +{ + type Commitments = crate::circuit_fat::ProofComms; + type Evaluations = crate::circuit_fat::ProofEvals; + type ProverCircuit = PiopProver; + type VerifierCircuit = PiopVerifier; + + fn prover_circuit(&self, level: LevelWitnessWithBlinding) -> Self::ProverCircuit { + PiopProver::build(&self, level) + } + + fn verifier_circuit( + &self, + instance: (G, C::Affine), + fixed_cols: &[WrappedAffine], + cols: Self::Commitments, + evals: Self::Evaluations, + zeta: C::ScalarField, + ) -> Self::VerifierCircuit { + let h_powers_comm: &[_; 2] = fixed_cols.try_into().expect("Expected 2 fixed columns"); + let domain_at_zeta = self.domain.evaluate(zeta); + let (child, x_parent) = instance; + PiopVerifier::init( + child, + WrappedAffine(x_parent), + domain_at_zeta, + h_powers_comm.clone(), + cols, + evals, + ) + } + + fn fixed_columns(&self) -> Vec> { + let h_powers_col = self.h_powers_column(); + vec![h_powers_col.xs, h_powers_col.ys] + } + + fn tree_nodes_column( + &self, + children_x_coords: &[C::ScalarField], + ) -> FieldColumn { + self.x_coords_column(children_x_coords) + } + + fn max_children(&self) -> usize { + self.max_nodes() + } + + #[cfg(test)] + fn setup(domain: Domain, h: G, _seed: G) -> Self { + Self::setup(domain, h) + } +} + +impl> PiopParams { + pub fn setup(domain: Domain, h: G) -> Self { + let scalar_bitlen = G::ScalarField::MODULUS_BIT_SIZE as usize; + Self { + domain, + scalar_bitlen, + h, + } + } + + pub fn max_nodes(&self) -> usize { + self.domain.capacity - 1 + } + + pub fn x_coords_column(&self, x_coords: &[G::BaseField]) -> FieldColumn { + let c = self.max_nodes(); + assert!(x_coords.len() <= c); + let mut x_coords = x_coords.to_vec(); + x_coords.resize(self.domain.domain_size(), G::BaseField::zero()); + x_coords[c] = G::BaseField::one(); + self.domain.domains.column_from_evals(x_coords, c) + } + + pub fn h_powers_column(&self) -> AffineColumn { + let mut h_powers = self.powers_of_h(); + h_powers.truncate(self.max_nodes()); + AffineColumn::public_column(h_powers, &self.domain) + } + + pub fn node_selector(&self, node_index: usize) -> BitColumn { + let c = self.max_nodes(); + let mut node_selector = vec![false; c]; + assert!(node_index < c); // allows to select a padding node + node_selector[node_index] = true; + BitColumn::init(node_selector, &self.domain) + } + + pub fn bf_bits_column(&self, bf: G::ScalarField) -> BitColumn { + let mut bf_bits = self.scalar_part(bf); + bf_bits.truncate(self.max_nodes()); + BitColumn::init(bf_bits, &self.domain) + } + + fn powers_of_h(&self) -> Vec { + let mut h = self.h.into_group(); + let mut multiples = Vec::with_capacity(self.scalar_bitlen); + multiples.push(h); + for _ in 1..self.scalar_bitlen { + h.double_in_place(); + multiples.push(h); + } + CurveGroup::normalize_batch(&multiples) + } + + fn scalar_part(&self, e: G::ScalarField) -> Vec { + let bits_with_trailing_zeroes = e.into_bigint().to_bits_le(); + let significant_bits = &bits_with_trailing_zeroes[..self.scalar_bitlen]; + significant_bits.to_vec() + } +} + +#[cfg(test)] +mod tests {} diff --git a/pasta-tree/src/circuit_fat/prover.rs b/pasta-tree/src/circuit_fat/prover.rs new file mode 100644 index 0000000..a5aa88a --- /dev/null +++ b/pasta-tree/src/circuit_fat/prover.rs @@ -0,0 +1,287 @@ +use crate::auth_path::node::LevelWitnessWithBlinding; +use crate::circuit_fat::params::PiopParams; +use crate::circuit_fat::{ProofComms, ProofEvals}; +use ark_ec::AffineRepr; +use ark_ec::CurveGroup; +// use ark_ec::short_weierstrass::{Affine as SwAffine, SWCurveConfig}; +use ark_ff::One; +use ark_ff::{FftField, PrimeField, Zero}; +use ark_poly::Evaluations; +use ark_poly::univariate::DensePolynomial; +use ark_std::{vec, vec::Vec}; +use w3f_pcs::pcs::commitment::WrappedAffine; +use w3f_plonk_common::FieldColumn; +use w3f_plonk_common::domain::Domain; +use w3f_plonk_common::gadgets::ProverGadget; +use w3f_plonk_common::gadgets::booleanity::{BitColumn, Booleanity}; +use w3f_plonk_common::gadgets::column_sum::ColumnSumPolys; +use w3f_plonk_common::gadgets::ec::AffineColumn; +use w3f_plonk_common::gadgets::ec::CondAdd; +use w3f_plonk_common::gadgets::equal_cells::CellsEqPolys; +use w3f_plonk_common::gadgets::fixed_cells::FixedCells; +use w3f_plonk_common::gadgets::inner_prod_inv::InnerProdInv; +use w3f_plonk_common::piop::ProverPiop; + +pub struct PiopProver> { + domain: Domain, + // `x` coordinates of all the children of a node. Public input. + nodes: FieldColumn, + // `H, 2H, 4H,...,2^sH` Fixed column. + h_powers: AffineColumn, + // `node_x = self.x_coords[self.node_idx]` Private input. + node_idx: BitColumn, + // Bits of the chosen blinding factor. Private input. + bf_bits: BitColumn, + selected_node: InnerProdInv, + blinded_node: CondAdd, // blinded_node.acc[0] = (x_i, y_i) = Ci, blinded_node.acc[capacity] = Ci + bf.H = Ci' + node_idx_bool: Booleanity, + bf_bits_bool: Booleanity, + node_idx_sum: ColumnSumPolys, + node_idx_sum_vals: FixedCells, + seed_eq_node: CellsEqPolys, +} + +impl> PiopProver { + pub fn build(params: &PiopParams, level: LevelWitnessWithBlinding) -> Self { + let domain = params.domain.clone(); + let x_coords = params.x_coords_column(&level.level_witness.x_coords()); + let h_powers = params.h_powers_column(); + let node_idx = params.node_selector(level.level_witness.path_node_idx); + let bf_bits = params.bf_bits_column(level.bf); + let selected_node = InnerProdInv::init(x_coords.clone(), node_idx.col.clone(), &domain); + + let node = level.level_witness.path_node(); + debug_assert_eq!(selected_node.acc.evals[0], node.x().unwrap()); + // here we witness yi + let blinded_node = CondAdd::init(bf_bits.clone(), h_powers.clone(), node, &domain); + debug_assert_eq!( + blinded_node.seed_plus_sum(), + (node + params.h * level.bf).into_affine() + ); + debug_assert_eq!(blinded_node.acc.xs.evals[0], node.x().unwrap()); + debug_assert_eq!(blinded_node.acc.ys.evals[0], node.y().unwrap()); + let node_idx_bool = Booleanity::init(node_idx.clone()); + let bf_bits_bool = Booleanity::init(bf_bits.clone()); + let node_idx_sum = ColumnSumPolys::init(node_idx.col.clone(), &domain); + let node_idx_sum_vals = FixedCells::init( + node_idx_sum.acc.clone(), + &domain, + G::BaseField::zero(), + G::BaseField::one(), + ); + let seed_eq_node = CellsEqPolys::first_cells( + selected_node.acc.clone(), + blinded_node.acc.xs.clone(), + &domain, + ); + + Self { + domain, + nodes: x_coords, + h_powers, + node_idx, + bf_bits, + selected_node, + blinded_node, + node_idx_bool, + bf_bits_bool, + node_idx_sum, + node_idx_sum_vals, + seed_eq_node, + } + } + + fn _committed_columns< + C: CurveGroup, + Fun: Fn(&DensePolynomial) -> WrappedAffine, + >( + &self, + commit: Fun, + ) -> ProofComms { + let node_idx = commit(self.node_idx.as_poly()); + let bf_bits = commit(self.bf_bits.as_poly()); + let selected_node_acc = commit(self.selected_node.acc.as_poly()); + let blinded_node_acc = [ + commit(self.blinded_node.acc.xs.as_poly()), + commit(self.blinded_node.acc.ys.as_poly()), + ]; + let node_idx_sum_acc = commit(self.node_idx_sum.acc.as_poly()); + ProofComms { + node_idx, + bf_bits, + selected_node_acc, + blinded_node_acc, + node_idx_sum_acc, + } + } + + // Should return polynomials in the consistent with + // Self::Evaluations::to_vec() and Self::Commitments::to_vec(). + fn _columns(&self) -> Vec> { + vec![ + self.nodes.as_poly().clone(), + self.h_powers.xs.as_poly().clone(), + self.h_powers.ys.as_poly().clone(), + self.node_idx.as_poly().clone(), + self.bf_bits.as_poly().clone(), + self.selected_node.acc.as_poly().clone(), + self.blinded_node.acc.xs.as_poly().clone(), + self.blinded_node.acc.ys.as_poly().clone(), + self.node_idx_sum.acc.as_poly().clone(), + ] + } + + fn _columns_evaluated(&self, zeta: &G::BaseField) -> ProofEvals { + let x_coords = self.nodes.evaluate(zeta); + let h_powers = [ + self.h_powers.xs.evaluate(zeta), + self.h_powers.ys.evaluate(zeta), + ]; + let node_idx = self.node_idx.evaluate(zeta); + let bf_bits = self.bf_bits.evaluate(zeta); + let blinded_node_acc = [ + self.blinded_node.acc.xs.evaluate(zeta), + self.blinded_node.acc.ys.evaluate(zeta), + ]; + let selected_node_acc = self.selected_node.acc.evaluate(zeta); + let node_idx_sum_acc = self.node_idx_sum.acc.evaluate(zeta); + ProofEvals { + x_coords, + h_powers, + node_idx, + bf_bits, + selected_node_acc, + blinded_node_acc, + node_idx_sum_acc, + } + } +} + +impl> + ProverPiop> for PiopProver +{ + const N_COLUMNS: usize = 9; + const N_CONSTRAINTS: usize = 12; + type Commitments = ProofComms; + type Evaluations = ProofEvals; + type Instance = G; + + fn committed_columns) -> WrappedAffine>( + &self, + commit: Fun, + ) -> Self::Commitments { + self._committed_columns(commit) + } + + // Should return polynomials in the consistent with + // Self::Evaluations::to_vec() and Self::Commitments::to_vec(). + fn columns(&self) -> Vec> { + self._columns() + } + + fn columns_evaluated(&self, zeta: &C::ScalarField) -> Self::Evaluations { + self._columns_evaluated(zeta) + } + + fn constraints(&self) -> Vec> { + let (node_blinded_x, node_blinded_y) = self.blinded_node.seed_plus_sum().xy().unwrap(); + vec![ + self.selected_node.constraints(), + self.blinded_node.constraints(), + self.node_idx_sum.constraints(), + self.node_idx_bool.constraints(), + self.bf_bits_bool.constraints(), + self.node_idx_sum_vals.constraints(), + vec![FixedCells::constraint_cell( + &self.blinded_node.acc.xs, + &self.domain.l_last, + self.domain.capacity - 1, + node_blinded_x, + )], + vec![FixedCells::constraint_cell( + &self.blinded_node.acc.ys, + &self.domain.l_last, + self.domain.capacity - 1, + node_blinded_y, + )], + vec![FixedCells::constraint_cell( + &self.selected_node.acc, + &self.domain.l_last, + self.domain.capacity - 1, + C::ScalarField::zero(), + )], + self.seed_eq_node.constraints(), + // vec![self.blinded_node.acc.on_curve_constraint()], + // this prevents opening to -parent=(x,-y) + // parent = commit([x1, ..., xl, 1, 0, 0, 0]; 0) = x1.G1 + ... + xl.Gl + 1.G_{l+1} + // then -parent = commit([-x1, ..., -xl, -1, 0, 0, 0]; 0) + vec![FixedCells::constraint_cell( + &self.nodes, + &self.domain.l_last, + self.domain.capacity - 1, + C::ScalarField::one(), + )], + ] + .concat() + } + + fn constraints_lin(&self, zeta: &C::ScalarField) -> Vec> { + vec![ + self.selected_node.constraints_linearized(zeta), + self.blinded_node.constraints_linearized(zeta), + self.node_idx_sum.constraints_linearized(zeta), + self.node_idx_bool.constraints_linearized(zeta), + self.bf_bits_bool.constraints_linearized(zeta), + self.node_idx_sum_vals.constraints_linearized(zeta), + vec![DensePolynomial::zero()], + vec![DensePolynomial::zero()], + vec![DensePolynomial::zero()], + self.seed_eq_node.constraints_linearized(zeta), + // vec![DensePolynomial::zero()], + vec![DensePolynomial::zero()], + ] + .concat() + } + + fn domain(&self) -> &Domain { + &self.domain + } + + fn result(&self) -> Self::Instance { + self.blinded_node.seed_plus_sum() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::tests::random_witness; + use ark_bls12_381::G1Projective; + use ark_ed_on_bls12_381_bandersnatch::{Fq, Fr, SWAffine}; + use ark_std::{UniformRand, test_rng}; + use w3f_pcs::pcs::commitment::WrappedAffine; + + #[test] + fn test_constraints() { + let rng = &mut test_rng(); + + let domain_size = 256; + let domain = Domain::::new(domain_size, true); + + let node = SWAffine::rand(rng); + let h = SWAffine::rand(rng); + let bf = Fr::from(u128::rand(rng)); + let blinded_node = (node + h * bf).into_affine(); + + let piop_params = PiopParams::setup(domain, h); + let witness = + random_witness(piop_params.max_nodes(), node, rng).with_blinding(bf, Fq::zero()); + let piop = PiopProver::build(&piop_params, witness); + + assert!(ProverPiop::<_, WrappedAffine>::constraints_satisfied(&piop)); + assert_eq!( + ProverPiop::<_, WrappedAffine>::result(&piop), + blinded_node + ); + } +} diff --git a/pasta-tree/src/circuit_fat/verifier.rs b/pasta-tree/src/circuit_fat/verifier.rs new file mode 100644 index 0000000..05df06e --- /dev/null +++ b/pasta-tree/src/circuit_fat/verifier.rs @@ -0,0 +1,188 @@ +use crate::circuit_fat::{ProofComms, ProofEvals}; +// use ark_ec::short_weierstrass::{Affine as SwAffine, SWCurveConfig}; +use ark_ec::{AffineRepr, CurveGroup}; +use ark_ff::One; +use ark_ff::Zero; +use ark_std::marker::PhantomData; +use ark_std::{vec, vec::Vec}; +use w3f_pcs::pcs::commitment::WrappedAffine; +use w3f_plonk_common::domain::EvaluatedDomain; +use w3f_plonk_common::gadgets::VerifierGadget; +use w3f_plonk_common::gadgets::booleanity::BooleanityValues; +use w3f_plonk_common::gadgets::column_sum::ColumnSumEvals; +use w3f_plonk_common::gadgets::ec::CondAddValues; +use w3f_plonk_common::gadgets::equal_cells::EqualCells; +use w3f_plonk_common::gadgets::fixed_cells::FixedCellsValues; +use w3f_plonk_common::gadgets::inner_prod_inv::InnerProdInvValues; +use w3f_plonk_common::piop::VerifierPiop; + +pub struct PiopVerifier> { + domain_evals: EvaluatedDomain, + instance: G, + x_coords_comm: WrappedAffine, + h_powers_comm: [WrappedAffine; 2], + witness_columns: ProofComms, + // Gadget verifiers: + selected_node: InnerProdInvValues, + blinded_node: CondAddValues, + node_idx_sum: ColumnSumEvals, + node_idx_bool: BooleanityValues, + bf_bits_bool: BooleanityValues, + node_idx_sum_vals: FixedCellsValues, + seed_eq_node: EqualCells, +} + +impl> PiopVerifier { + pub fn init( + instance: G, + blinded_parent: WrappedAffine, + domain_evals: EvaluatedDomain, + h_powers_comm: [WrappedAffine; 2], + witness_columns: ProofComms, + all_evals: ProofEvals, + ) -> Self { + let selected_node = InnerProdInvValues { + a: all_evals.x_coords, + b: all_evals.node_idx, + not_last: domain_evals.not_last_row, + acc: all_evals.selected_node_acc, + }; + let blinded_node = CondAddValues { + bitmask: all_evals.bf_bits, + points: (all_evals.h_powers[0], all_evals.h_powers[1]), + not_last: domain_evals.not_last_row, + acc: (all_evals.blinded_node_acc[0], all_evals.blinded_node_acc[1]), + _phantom: PhantomData, + }; + let node_idx_sum = ColumnSumEvals { + col: all_evals.node_idx, + acc: all_evals.node_idx_sum_acc, + not_last: domain_evals.not_last_row, + }; + let node_idx_bool = BooleanityValues { + bits: all_evals.node_idx, + }; + let bf_bits_bool = BooleanityValues { + bits: all_evals.bf_bits, + }; + let node_idx_sum_vals = FixedCellsValues { + col: all_evals.node_idx_sum_acc, + col_first: C::ScalarField::zero(), + col_last: C::ScalarField::one(), + l_first: domain_evals.l_first, + l_last: domain_evals.l_last, + }; + let seed_eq_node = EqualCells { + a: selected_node.acc, + b: blinded_node.acc.0, + li: domain_evals.l_first, + }; + Self { + instance, + domain_evals, + x_coords_comm: blinded_parent, + h_powers_comm, + witness_columns, + // gadgets + selected_node, + blinded_node, + node_idx_sum, + node_idx_bool, + bf_bits_bool, + node_idx_sum_vals, + seed_eq_node, + } + } +} + +impl> + VerifierPiop> for PiopVerifier +{ + const N_COLUMNS: usize = 9; + const N_CONSTRAINTS: usize = 12; + + fn precommitted_columns(&self) -> Vec> { + vec![ + self.x_coords_comm.clone(), + self.h_powers_comm[0].clone(), + self.h_powers_comm[1].clone(), + ] + } + + fn evaluate_constraints_main(&self) -> Vec { + let (x, y) = self.instance.xy().unwrap(); + vec![ + self.selected_node.evaluate_constraints_main(), + self.blinded_node.evaluate_constraints_main(), + self.node_idx_sum.evaluate_constraints_main(), + self.node_idx_bool.evaluate_constraints_main(), + self.bf_bits_bool.evaluate_constraints_main(), + self.node_idx_sum_vals.evaluate_constraints_main(), + vec![FixedCellsValues::evaluate_for_cell( + self.blinded_node.acc.0, + self.domain_evals.l_last, + x, + )], + vec![FixedCellsValues::evaluate_for_cell( + self.blinded_node.acc.1, + self.domain_evals.l_last, + y, + )], + vec![FixedCellsValues::evaluate_for_cell( + self.selected_node.acc, + self.domain_evals.l_last, + C::ScalarField::zero(), + )], + self.seed_eq_node.evaluate_constraints_main(), + // vec![AffineColumn::>::on_curve_eval( + // self.blinded_node.acc, + // )], + vec![FixedCellsValues::evaluate_for_cell( + self.selected_node.a, + self.domain_evals.l_last, + C::ScalarField::one(), + )], + ] + .concat() + } + + fn lin_poly_commitment( + &self, + agg_coeffs: &[C::ScalarField], + ) -> (Vec, Vec>) { + assert_eq!(agg_coeffs.len(), Self::N_CONSTRAINTS); + + let selected_node_acc = self.witness_columns.selected_node_acc.clone(); + let selected_node_coeff = -agg_coeffs[0] * self.selected_node.not_last; + + let blinded_node_acc_x = self.witness_columns.blinded_node_acc[0].clone(); + let blinded_node_acc_y = self.witness_columns.blinded_node_acc[1].clone(); + let (c_acc_x, c_acc_y) = self.blinded_node.acc_coeffs_1(); + let mut blinded_node_x_coeff = agg_coeffs[1] * c_acc_x; + let mut blinded_node_y_coeff = agg_coeffs[1] * c_acc_y; + let (c_acc_x, c_acc_y) = self.blinded_node.acc_coeffs_2(); + blinded_node_x_coeff += agg_coeffs[2] * c_acc_x; + blinded_node_y_coeff += agg_coeffs[2] * c_acc_y; + + let node_idx_sum_acc = self.witness_columns.node_idx_sum_acc.clone(); + let node_idx_sum_coeff = agg_coeffs[3] * self.node_idx_sum.not_last; + ( + vec![ + selected_node_coeff, + blinded_node_x_coeff, + blinded_node_y_coeff, + node_idx_sum_coeff, + ], + vec![ + selected_node_acc, + blinded_node_acc_x, + blinded_node_acc_y, + node_idx_sum_acc, + ], + ) + } + + fn domain_evaluated(&self) -> &EvaluatedDomain { + &self.domain_evals + } +} diff --git a/pasta-tree/src/circuit_tall/mod.rs b/pasta-tree/src/circuit_tall/mod.rs new file mode 100644 index 0000000..f4f1b6f --- /dev/null +++ b/pasta-tree/src/circuit_tall/mod.rs @@ -0,0 +1,77 @@ +use ark_ec::{CurveGroup, PrimeGroup}; +use ark_ff::PrimeField; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{vec, vec::Vec}; +use w3f_pcs::pcs::commitment::WrappedAffine; +use w3f_plonk_common::{ColumnsCommited, ColumnsEvaluated}; + +pub mod params; +pub mod prover; +pub mod verifier; + +pub type PiopProof = w3f_plonk_common::PiopProof< + ::ScalarField, + WrappedAffine, + ProofComms, + ProofEvals<::ScalarField>, +>; + +#[derive(Clone, Debug, CanonicalSerialize, CanonicalDeserialize)] +pub struct ProofComms { + /// Witnessed Y-coordinates of the point vector `nodes || h_powers` TODO: last 4 elements + pub(crate) points_y: WrappedAffine, // aka y_parent + /// 0/1 vector `node_idx || bl` + pub(crate) bits: WrappedAffine, + /// Inner product gadget accumulator + pub(crate) inn_prod_acc: WrappedAffine, + /// EC addition (= fixed point multiplication) gadget accumulator + pub(crate) cond_add_acc: [WrappedAffine; 2], +} + +impl ColumnsCommited> for ProofComms { + fn to_vec(self) -> Vec> { + self.into() + } +} + +impl From> for Vec> { + fn from(value: ProofComms) -> Self { + let [cond_add_acc_x, cond_add_acc_y] = value.cond_add_acc; + vec![ + value.points_y, + value.bits, + value.inn_prod_acc, + cond_add_acc_x, + cond_add_acc_y, + ] + } +} + +#[derive(Clone, Debug, CanonicalSerialize, CanonicalDeserialize)] +pub struct ProofEvals { + pub(crate) points: [F; 2], + pub(crate) ring_selector: F, + pub(crate) bits: F, + pub(crate) inn_prod_acc: F, + pub(crate) cond_add_acc: [F; 2], +} + +impl ColumnsEvaluated for ProofEvals { + fn to_vec(self) -> Vec { + self.into() + } +} + +impl From> for Vec { + fn from(value: ProofEvals) -> Self { + vec![ + value.points[0], + value.ring_selector, + value.points[1], + value.bits, + value.inn_prod_acc, + value.cond_add_acc[0], + value.cond_add_acc[1], + ] + } +} diff --git a/pasta-tree/src/circuit_tall/params.rs b/pasta-tree/src/circuit_tall/params.rs new file mode 100644 index 0000000..4971f27 --- /dev/null +++ b/pasta-tree/src/circuit_tall/params.rs @@ -0,0 +1,184 @@ +use crate::CircuitParams; +use crate::auth_path::node::LevelWitnessWithBlinding; +use crate::circuit_tall::prover::PiopProver; +use crate::circuit_tall::verifier::PiopVerifier; +// use ark_ec::short_weierstrass::{Affine as SwAffine, SWCurveConfig}; +use ark_ec::{AffineRepr, CurveGroup}; +use ark_ff::{AdditiveGroup, BigInteger, PrimeField, Zero}; +use ark_ff::{FftField, One}; +use ark_std::{vec, vec::Vec}; +use w3f_pcs::pcs::commitment::WrappedAffine; +use w3f_plonk_common::FieldColumn; +use w3f_plonk_common::domain::Domain; +use w3f_plonk_common::gadgets::booleanity::BitColumn; +use w3f_plonk_common::gadgets::ec::AffineColumn; + +// `max_nodes + blinding_bits = domain.capacity - 1` +// where `1` acounts for the `seed` point. +/// Circuit parameters +#[derive(Clone)] +pub struct PiopParams> { + /// Domain over which the circuit is represented. + pub domain: Domain, + /// Maximal number of children per tree node. + pub max_nodes: usize, + /// Number of bits used to represent a blinding factor. + pub blinding_bits: usize, + /// Point that initializes the EC addition gadget accumulator. + pub seed: G, + /// Pedersen blinding base point. + pub h: G, +} + +impl> CircuitParams + for PiopParams +{ + type Commitments = crate::circuit_tall::ProofComms; + type Evaluations = crate::circuit_tall::ProofEvals; + type ProverCircuit = PiopProver; + type VerifierCircuit = PiopVerifier; + + fn prover_circuit(&self, level: LevelWitnessWithBlinding) -> Self::ProverCircuit { + PiopProver::build(&self, level) + } + + fn verifier_circuit( + &self, + instance: (G, C::Affine), + fixed_cols: &[WrappedAffine], + cols: Self::Commitments, + evals: Self::Evaluations, + zeta: C::ScalarField, + ) -> Self::VerifierCircuit { + assert_eq!(fixed_cols.len(), 1, "Expected 1 fixed columns"); + let selector = fixed_cols[0].clone(); + let domain_at_zeta = self.domain.evaluate(zeta); + let (child, x_parent) = instance; + PiopVerifier::init( + domain_at_zeta, + WrappedAffine(x_parent), + selector, + cols, + evals, + self.seed, + child, + ) + } + + fn fixed_columns(&self) -> Vec> { + vec![self.select_part()] + } + + fn tree_nodes_column(&self, children_x_coords: &[G::BaseField]) -> FieldColumn { + assert!(children_x_coords.len() <= self.max_nodes); + let mut x_coords = children_x_coords.to_vec(); + // padding + x_coords.resize(self.max_nodes, G::BaseField::zero()); + // `powers_of_h` x-coords + let powers_of_h = self.power_of_h(); + assert_eq!(powers_of_h.len(), self.blinding_bits); + let powers_of_h_xs = powers_of_h.into_iter().filter_map(|p| p.x()); + x_coords.extend(powers_of_h_xs); + let payload_len = self.domain.capacity - 1; + assert_eq!(x_coords.len(), payload_len); + // x_coords.push(G::BaseField::one()); + // assert_eq!(x_coords.len(), self.domain.capacity); + + // zk_rows + x_coords.resize(self.domain.domain_size(), G::BaseField::zero()); + self.domain.domains.column_from_evals(x_coords, payload_len) + } + + fn max_children(&self) -> usize { + self.max_nodes + } + + #[cfg(test)] + fn setup(domain: Domain, h: G, seed: G) -> Self { + Self::setup(domain, h, seed) + } +} + +impl> PiopParams { + pub fn setup(domain: Domain, h: G, seed: G) -> Self { + assert!(domain.domain_size() > 256); + let actual_capacity = domain.capacity - 1; + let scalar_size = Domain::::new(256, domain.is_hiding()).capacity - 1; + let blinding_bits = + ark_std::cmp::min(G::ScalarField::MODULUS_BIT_SIZE as usize, scalar_size); + let max_nodes = actual_capacity - blinding_bits; + Self { + domain, + max_nodes, + blinding_bits, + seed, + h, + } + } + + // fn x_coords_from_points(&self, child_nodes: Vec) -> FieldColumn { + // let points = self.siblings_with_blinding(child_nodes); + // let (mut x_coords, mut y_coords): (Vec, Vec) = + // points.iter().map(|p| p.xy().unwrap()).unzip(); + // let payload_len = self.domain.capacity - 1; + // assert_eq!(x_coords.len(), payload_len); + // // x_coords.push(G::BaseField::one()); + // // assert_eq!(x_coords.len(), self.domain.capacity); + // + // // zk_rows + // x_coords.resize(self.domain.domain_size(), G::BaseField::zero()); + // y_coords.resize(self.domain.domain_size(), G::BaseField::zero()); + // self.domain.domains.column_from_evals(x_coords, payload_len) + // } + + pub fn points_column(&self, child_nodes: Vec) -> AffineColumn { + let points = self.siblings_with_blinding(child_nodes); + assert_eq!(points.len(), self.domain.capacity - 1); + AffineColumn::public_column(points, &self.domain) + } + + fn siblings_with_blinding(&self, siblings: Vec) -> Vec { + assert!(siblings.len() <= self.max_nodes); + let mut points = siblings; + points.resize(self.max_nodes, G::ZERO); // padding + points.extend(self.power_of_h()); // powers of `H` + points + } + + pub fn bits_column(&self, node_index: usize, bf: G::ScalarField) -> BitColumn { + let mut bits = vec![false; self.max_nodes]; + assert!(node_index < self.max_nodes); // allows to select a padding node + bits[node_index] = true; + bits.extend(self.scalar_part(bf)); + BitColumn::init(bits, &self.domain) + } + + pub(super) fn select_part(&self) -> FieldColumn { + let selector = [ + vec![G::BaseField::one(); self.max_nodes], + vec![G::BaseField::zero(); self.blinding_bits], + ] + .concat(); + self.domain.public_column(selector) + } + + fn power_of_h(&self) -> Vec { + let mut h = self.h.into_group(); + let mut res = Vec::with_capacity(self.blinding_bits); + res.push(h); + for _ in 1..self.blinding_bits { + h.double_in_place(); + res.push(h); + } + CurveGroup::normalize_batch(&res) + } + + fn scalar_part(&self, e: G::ScalarField) -> Vec { + let bits_with_trailing_zeroes = e.into_bigint().to_bits_le(); + let significant_bits = &bits_with_trailing_zeroes[..self.blinding_bits]; + significant_bits.to_vec() + } +} + +#[cfg(test)] +mod tests {} diff --git a/pasta-tree/src/circuit_tall/prover.rs b/pasta-tree/src/circuit_tall/prover.rs new file mode 100644 index 0000000..4ac3b60 --- /dev/null +++ b/pasta-tree/src/circuit_tall/prover.rs @@ -0,0 +1,213 @@ +use crate::auth_path::node::LevelWitnessWithBlinding; +use crate::circuit_tall::params::PiopParams; +use crate::circuit_tall::{ProofComms, ProofEvals}; +// use ark_ec::short_weierstrass::{Affine as SwAffine, SWCurveConfig}; +use ark_ec::{AffineRepr, CurveGroup}; +use ark_ff::{FftField, One, Zero}; +use ark_poly::Evaluations; +use ark_poly::Polynomial; +use ark_poly::univariate::DensePolynomial; +use ark_std::{vec, vec::Vec}; +use w3f_pcs::pcs::commitment::WrappedAffine; +use w3f_plonk_common::FieldColumn; +use w3f_plonk_common::domain::Domain; +use w3f_plonk_common::gadgets::ProverGadget; +use w3f_plonk_common::gadgets::booleanity::{BitColumn, Booleanity}; +use w3f_plonk_common::gadgets::ec::AffineColumn; +use w3f_plonk_common::gadgets::ec::CondAdd; +use w3f_plonk_common::gadgets::fixed_cells::FixedCells; +use w3f_plonk_common::gadgets::inner_prod::InnerProd; +use w3f_plonk_common::piop::ProverPiop; + +// struct Witness> { +// // `x` coordinates of all the children of a node. Public input. +// // `H, 2H, 4H,...,2^sH` Fixed column. +// points: AffineColumn, +// // `node_x = self.x_coords[self.node_idx]` Private input. +// // Bits of the chosen blinding factor. Private input. +// bits: BitColumn, +// select_part: FieldColumn, +// inner_prod_acc: DensePolynomial, +// cond_add_acc_x: DensePolynomial, +// cond_add_acc_y: DensePolynomial, +// } + +pub struct PiopProver> { + domain: Domain, + // `x` coordinates of all the children of a node. Public input. + // `H, 2H, 4H,...,2^sH` Fixed column. + points: AffineColumn, + // `node_x = self.x_coords[self.node_idx]` Private input. + // Bits of the chosen blinding factor. Private input. + bits: BitColumn, + select_part: FieldColumn, + inner_prod_acc: DensePolynomial, + cond_add_acc_x: DensePolynomial, + cond_add_acc_y: DensePolynomial, + // columns: Witness, + gadgets: Vec>>, + result: G, +} + +impl> PiopProver { + pub fn build(params: &PiopParams, level: LevelWitnessWithBlinding) -> Self { + let domain = params.domain.clone(); + let points = params.points_column(level.level_witness.siblings); + let bits = params.bits_column(level.level_witness.path_node_idx, level.bf); + let bits_bool = Booleanity::init(bits.clone()); + let select_part = params.select_part(); + let inner_prod = InnerProd::init(select_part.clone(), bits.col.clone(), &domain); + let inner_prod_vals = FixedCells::init( + inner_prod.acc.clone(), + &domain, + G::BaseField::zero(), + G::BaseField::one(), + ); + let cond_add = CondAdd::init(bits.clone(), points.clone(), params.seed, &domain); + let (seed_x, seed_y) = params.seed.xy().unwrap(); + let (result_x, result_y) = cond_add.seed_plus_sum().xy().unwrap(); + let cond_add_vals_x = FixedCells::init(cond_add.acc.xs.clone(), &domain, seed_x, result_x); + let cond_add_vals_y = FixedCells::init(cond_add.acc.ys.clone(), &domain, seed_y, result_y); + + let inner_prod_acc = inner_prod.acc.as_poly().clone(); + let cond_add_acc_x = cond_add.acc.xs.as_poly().clone(); + let cond_add_acc_y = cond_add.acc.ys.as_poly().clone(); + let result = cond_add.result(); + + let mut gadgets: Vec>> = Vec::new(); + gadgets.push(Box::new(inner_prod)); + gadgets.push(Box::new(cond_add)); + gadgets.push(Box::new(bits_bool)); + gadgets.push(Box::new(cond_add_vals_x)); + gadgets.push(Box::new(cond_add_vals_y)); + gadgets.push(Box::new(inner_prod_vals)); + + Self { + domain, + + points, + bits, + select_part, + + gadgets, + inner_prod_acc, + cond_add_acc_x, + cond_add_acc_y, + result, + } + } +} + +impl> + ProverPiop> for PiopProver +{ + const N_COLUMNS: usize = 7; + const N_CONSTRAINTS: usize = 7; + type Commitments = ProofComms; + type Evaluations = ProofEvals; + type Instance = G; + + fn committed_columns) -> WrappedAffine>( + &self, + commit: Fun, + ) -> Self::Commitments { + let points_y = commit(self.points.ys.as_poly()); + let bits = commit(self.bits.as_poly()); + let cond_add_acc = [commit(&self.cond_add_acc_x), commit(&self.cond_add_acc_y)]; + let inn_prod_acc = commit(&self.inner_prod_acc); + ProofComms { + points_y, + bits, + cond_add_acc, + inn_prod_acc, + } + } + + // Should return polynomials in the consistent with + // Self::Evaluations::to_vec() and Self::Commitments::to_vec(). + fn columns(&self) -> Vec> { + vec![ + self.points.xs.as_poly().clone(), + self.select_part.as_poly().clone(), + self.points.ys.as_poly().clone(), + self.bits.as_poly().clone(), + self.inner_prod_acc.clone(), + self.cond_add_acc_x.clone(), + self.cond_add_acc_y.clone(), + ] + } + + fn columns_evaluated(&self, zeta: &C::ScalarField) -> Self::Evaluations { + let points = [self.points.xs.evaluate(zeta), self.points.ys.evaluate(zeta)]; + let ring_selector = self.select_part.evaluate(zeta); + let bits = self.bits.evaluate(zeta); + let inn_prod_acc = self.inner_prod_acc.evaluate(zeta); + let cond_add_acc = [ + self.cond_add_acc_x.evaluate(zeta), + self.cond_add_acc_y.evaluate(zeta), + ]; + ProofEvals { + points, + ring_selector, + bits, + inn_prod_acc, + cond_add_acc, + } + } + + fn constraints(&self) -> Vec> { + self.gadgets.iter().flat_map(|g| g.constraints()).collect() + } + + fn constraints_lin(&self, zeta: &C::ScalarField) -> Vec> { + self.gadgets + .iter() + .flat_map(|g| g.constraints_linearized(zeta)) + .collect() + } + + fn domain(&self) -> &Domain { + &self.domain + } + + fn result(&self) -> Self::Instance { + self.result + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::tests::random_witness; + use ark_bls12_381::G1Projective; + use ark_ec::CurveGroup; + use ark_ff::Zero; + + use ark_ed_on_bls12_381_bandersnatch::{Fq, Fr, SWAffine}; + use ark_std::{UniformRand, test_rng}; + + #[test] + fn test_constraints() { + let rng = &mut test_rng(); + + let domain_size = 512; + let domain = Domain::::new(domain_size, true); + + let node = SWAffine::rand(rng); + let h = SWAffine::rand(rng); + let seed = SWAffine::rand(rng); + let bf = Fr::from(u128::rand(rng)); + let blinded_node = (node + h * bf).into_affine(); + + let piop_params = PiopParams::setup(domain, h, seed); + let witness = + random_witness(piop_params.max_nodes, node, rng).with_blinding(bf, Fq::zero()); + let piop = PiopProver::build(&piop_params, witness); + + assert!(ProverPiop::<_, WrappedAffine>::constraints_satisfied(&piop)); + assert_eq!( + ProverPiop::<_, WrappedAffine>::result(&piop), + blinded_node + ); + } +} diff --git a/pasta-tree/src/circuit_tall/verifier.rs b/pasta-tree/src/circuit_tall/verifier.rs new file mode 100644 index 0000000..715d35e --- /dev/null +++ b/pasta-tree/src/circuit_tall/verifier.rs @@ -0,0 +1,150 @@ +use crate::circuit_tall::{ProofComms, ProofEvals}; +use ark_ec::AffineRepr; +use ark_ec::CurveGroup; +// use ark_ec::short_weierstrass::{Affine as SwAffine, SWCurveConfig}; +use ark_ff::{One, Zero}; +use ark_std::marker::PhantomData; +use ark_std::{vec, vec::Vec}; +use w3f_pcs::pcs::commitment::WrappedAffine; +use w3f_plonk_common::domain::EvaluatedDomain; +use w3f_plonk_common::gadgets::VerifierGadget; +use w3f_plonk_common::gadgets::booleanity::BooleanityValues; +use w3f_plonk_common::gadgets::ec::CondAddValues; +use w3f_plonk_common::gadgets::fixed_cells::FixedCellsValues; +use w3f_plonk_common::gadgets::inner_prod::InnerProdValues; +use w3f_plonk_common::piop::VerifierPiop; + +pub struct PiopVerifier> { + domain_evals: EvaluatedDomain, + + points_x: WrappedAffine, + select_part: WrappedAffine, + witness_cols: ProofComms, + + // Gadget verifiers: + booleanity: BooleanityValues, + inner_prod: InnerProdValues, + inner_prod_acc: FixedCellsValues, + cond_add: CondAddValues, + cond_add_acc_x: FixedCellsValues, + cond_add_acc_y: FixedCellsValues, +} + +impl> PiopVerifier { + pub fn init( + domain_evals: EvaluatedDomain, + points_x: WrappedAffine, + select_part: WrappedAffine, + witness_cols: ProofComms, + evals: ProofEvals, + seed: G, + result: G, + ) -> Self { + let cond_add = CondAddValues { + bitmask: evals.bits, + points: (evals.points[0], evals.points[1]), + not_last: domain_evals.not_last_row, + acc: (evals.cond_add_acc[0], evals.cond_add_acc[1]), + _phantom: PhantomData, + }; + + let inner_prod = InnerProdValues { + a: evals.ring_selector, + b: evals.bits, + not_last: domain_evals.not_last_row, + acc: evals.inn_prod_acc, + }; + + let booleanity = BooleanityValues { bits: evals.bits }; + + let (seed_x, seed_y) = seed.xy().unwrap(); + let (res_x, res_y) = (seed + result).into_affine().xy().unwrap(); + + let cond_add_acc_x = FixedCellsValues { + col: evals.cond_add_acc[0], + col_first: seed_x, + col_last: res_x, + l_first: domain_evals.l_first, + l_last: domain_evals.l_last, + }; + + let cond_add_acc_y = FixedCellsValues { + col: evals.cond_add_acc[1], + col_first: seed_y, + col_last: res_y, + l_first: domain_evals.l_first, + l_last: domain_evals.l_last, + }; + + let inner_prod_acc = FixedCellsValues { + col: evals.inn_prod_acc, + col_first: C::ScalarField::zero(), + col_last: C::ScalarField::one(), + l_first: domain_evals.l_first, + l_last: domain_evals.l_last, + }; + + Self { + domain_evals, + points_x, + select_part, + witness_cols, + booleanity, + inner_prod, + inner_prod_acc, + cond_add, + cond_add_acc_x, + cond_add_acc_y, + } + } +} + +impl> + VerifierPiop> for PiopVerifier +{ + const N_COLUMNS: usize = 7; + const N_CONSTRAINTS: usize = 7; + + fn precommitted_columns(&self) -> Vec> { + vec![self.points_x.clone(), self.select_part.clone()] + } + + fn evaluate_constraints_main(&self) -> Vec { + vec![ + self.inner_prod.evaluate_constraints_main(), + self.cond_add.evaluate_constraints_main(), + self.booleanity.evaluate_constraints_main(), + self.cond_add_acc_x.evaluate_constraints_main(), + self.cond_add_acc_y.evaluate_constraints_main(), + self.inner_prod_acc.evaluate_constraints_main(), + ] + .concat() + } + + fn lin_poly_commitment( + &self, + alphas: &[C::ScalarField], + ) -> (Vec, Vec>) { + assert_eq!(alphas.len(), Self::N_CONSTRAINTS); + + let inner_prod_acc = self.witness_cols.inn_prod_acc.clone(); + let inner_prod_coeff = alphas[0] * self.inner_prod.not_last; + + let cond_add_acc_x = self.witness_cols.cond_add_acc[0].clone(); + let cond_add_acc_y = self.witness_cols.cond_add_acc[1].clone(); + let (c_acc_x, c_acc_y) = self.cond_add.acc_coeffs_1(); + let mut cond_add_x_coeff = alphas[1] * c_acc_x; + let mut cond_add_y_coeff = alphas[1] * c_acc_y; + let (c_acc_x, c_acc_y) = self.cond_add.acc_coeffs_2(); + cond_add_x_coeff += alphas[2] * c_acc_x; + cond_add_y_coeff += alphas[2] * c_acc_y; + ( + vec![inner_prod_coeff, cond_add_x_coeff, cond_add_y_coeff], + vec![inner_prod_acc.clone(), cond_add_acc_x, cond_add_acc_y], + ) + } + + fn domain_evaluated(&self) -> &EvaluatedDomain { + &self.domain_evals + } +} diff --git a/pasta-tree/src/level/mod.rs b/pasta-tree/src/level/mod.rs index 0f0f8b1..13b82e5 100644 --- a/pasta-tree/src/level/mod.rs +++ b/pasta-tree/src/level/mod.rs @@ -7,7 +7,6 @@ use w3f_pcs::pcs::PCS; use w3f_pcs::pcs::ipa::hiding::HidingIpa; use w3f_pcs::shplonk::AggregateProof; use w3f_plonk_common::PiopProof; -use w3f_ring_proof::piop::{RingCommitments, RingEvaluations}; pub struct LevelProof { piop_proof: PiopProof< diff --git a/pasta-tree/src/level/prover.rs b/pasta-tree/src/level/prover.rs index 651e3fc..995b8fc 100644 --- a/pasta-tree/src/level/prover.rs +++ b/pasta-tree/src/level/prover.rs @@ -13,8 +13,6 @@ use w3f_pcs::pcs::ipa::hiding::HidingIpa; use w3f_pcs::shplonk::Shplonk; use w3f_plonk_common::piop::ProverPiop; use w3f_plonk_common::prover::{PcsOpeningAt2Points, PlonkProver}; -use w3f_ring_proof::ArkTranscript; -use w3f_ring_proof::piop::prover::PiopProver; impl> CycleSideParams> @@ -24,7 +22,7 @@ impl>, rng: &mut R, ) -> (Affine, LevelProof) { - let (fixed_columns, verifier_key) = + let (fixed_columns, verifxier_key) = self.commit_children(&witness.level_witness.siblings, witness.parent_bf); let piop = PiopProver::build( &self.piop_params, @@ -39,7 +37,7 @@ impl, _>::init( self.pcs_params.ck(), - verifier_key, + blinded_node, ArkTranscript::new(b"pasta-tree-level-proof"), ); let (pcs_openings, piop_proof, _transcript) = plonk_prover.reduce_to_pcs_opening(piop); diff --git a/pasta-tree/src/level/verifier.rs b/pasta-tree/src/level/verifier.rs index 4c47ecd..53566c5 100644 --- a/pasta-tree/src/level/verifier.rs +++ b/pasta-tree/src/level/verifier.rs @@ -8,8 +8,6 @@ use w3f_pcs::pcs::ipa::hiding::HidingIpa; use w3f_pcs::shplonk::Shplonk; use w3f_plonk_common::piop::VerifierPiop; use w3f_plonk_common::verifier::{PcsOpeningAt2Points, PlonkVerifier}; -use w3f_ring_proof::piop::verifier::PiopVerifier; -use w3f_ring_proof::{ArkTranscript, FixedColumnsCommitted, VerifierKey}; impl> CycleSideParams> diff --git a/pasta-tree/src/lib.rs b/pasta-tree/src/lib.rs index e85f1ac..b9353a4 100644 --- a/pasta-tree/src/lib.rs +++ b/pasta-tree/src/lib.rs @@ -1,140 +1,139 @@ +use crate::auth_path::node::LevelWitnessWithBlinding; use ark_ec::{AffineRepr, CurveGroup, PrimeGroup}; -use ark_ff::{PrimeField, Zero}; -use ark_std::rand::Rng; +use ark_ff::PrimeField; +use ark_ff::Zero; +use ark_serialize::CanonicalSerialize; +use ark_std::rand::RngCore; use std::marker::PhantomData; use w3f_pcs::aggregation::multiple::ShplonkTranscript; use w3f_pcs::pcs::PCS; -use w3f_pcs::pcs::PcsParams; use w3f_pcs::pcs::commitment::WrappedAffine; use w3f_pcs::pcs::ipa::hiding::HidingIpa; use w3f_pcs::shplonk::AggregateProof; -use w3f_plonk_common::PiopProof; +#[cfg(test)] use w3f_plonk_common::domain::Domain; -use w3f_ring_proof::piop::{FixedColumns, RingCommitments, RingEvaluations}; -use w3f_ring_proof::{FixedColumnsCommitted, PiopParams, VerifierKey}; +use w3f_plonk_common::piop::{ProverPiop, VerifierPiop}; +use w3f_plonk_common::{ColumnsCommited, ColumnsEvaluated, FieldColumn}; pub mod auth_path; -// pub mod circuit; -pub mod level; +pub mod circuit_fat; +pub mod circuit_tall; +// pub mod level; pub mod prover; pub mod verifier; -type IPACommitment = as PCS<::ScalarField>>::C; +// TODO: goes vto plonk-common in some form +/// A circuit over `C::ScalarField`. +pub trait CircuitParams> { + type Commitments: ColumnsCommited>; + type Evaluations: ColumnsEvaluated; + type ProverCircuit: ProverPiop< + C::ScalarField, + WrappedAffine, + Instance = G, + Commitments = Self::Commitments, + Evaluations = Self::Evaluations, + >; + type VerifierCircuit: VerifierPiop>; + + fn prover_circuit(&self, level: LevelWitnessWithBlinding) -> Self::ProverCircuit; + + fn verifier_circuit( + &self, + instance: (G, C::Affine), + fixed_cols: &[WrappedAffine], + cols: Self::Commitments, + evals: Self::Evaluations, + zeta: C::ScalarField, + ) -> Self::VerifierCircuit; + + fn fixed_columns(&self) -> Vec>; -pub struct CycleSideParams> { + fn tree_nodes_column( + &self, + children_x_coords: &[C::ScalarField], + ) -> FieldColumn; + + fn max_children(&self) -> usize; + + #[cfg(test)] // an "application" runs usually a single circuit + /// `h` is the pedersen blinding base (from the opposite side) to prove `C' = Ci + rH` + fn setup(domain: Domain, h: G, seed: G) -> Self; +} + +pub struct CycleSideParams< + C: CurveGroup, + G: AffineRepr, + P: CircuitParams, +> { pcs_params: HidingIpa, - piop_params: PiopParams, + piop_params: P, + phantomm: PhantomData, } pub struct CycleParams< C0: CurveGroup, C1: CurveGroup, + P0: CircuitParams, + P1: CircuitParams, > { - c0_params: CycleSideParams, - c1_params: CycleSideParams, + c0_params: CycleSideParams, + c1_params: CycleSideParams, } +type LevelProof = w3f_plonk_common::PiopProof< + ::ScalarField, + WrappedAffine, +

>::Commitments, +

>::Evaluations, +>; + #[derive(Clone)] -pub struct CycleSideProof> { - piop_proofs: Vec< - PiopProof, RingCommitments>, RingEvaluations>, - >, - pcs_proof: AggregateProof>, - todo: Coeffs, - fixed_columns_committed: Vec>>, +pub struct CycleSideProof< + C: CurveGroup, + G: AffineRepr, + P: CircuitParams, +> { + piop_proofs: Vec>, + pcs_proof: AggregateProof>, + todo: Coeffs, } #[derive(Clone)] pub struct CurveTreeProof< - F0: PrimeField, - F1: PrimeField, - C0: CurveGroup, - C1: CurveGroup, + C0: CurveGroup, + C1: CurveGroup, + P0: CircuitParams, + P1: CircuitParams, > { - c0_proof: CycleSideProof, - c1_proof: CycleSideProof, + c0_proof: CycleSideProof, + c1_proof: CycleSideProof, } -impl CycleParams -where - F0: PrimeField, - F1: PrimeField, - C0: CurveGroup, - C1: CurveGroup, +impl, P: CircuitParams> + CycleSideParams { - pub fn setup(domain_size: usize, rng: &mut R) -> Self { - let setup_degree = 3 * domain_size; - let c0_pcs_params = HidingIpa::::setup(setup_degree, rng); - let c1_pcs_params = HidingIpa::::setup(setup_degree, rng); - let c0_piop_params = piop_params(domain_size, c1_pcs_params.h, rng); - let c1_piop_params = piop_params(domain_size, c0_pcs_params.h, rng); - Self { - c0_params: CycleSideParams { - pcs_params: c0_pcs_params, - piop_params: c0_piop_params, - }, - c1_params: CycleSideParams { - pcs_params: c1_pcs_params, - piop_params: c1_piop_params, - }, - } - } -} - -fn piop_params, R: Rng>( - domain_size: usize, - h: G, - rng: &mut R, -) -> PiopParams { - let domain = Domain::::new(domain_size, true); - let seed = G::rand(rng); - let padding = G::rand(rng); - PiopParams::setup(domain, h, seed, padding) -} - -impl> CycleSideParams { - pub fn commit_children( + pub fn commit_tree_nodes( &self, - children: &[G], + nodes_x_coords: &[C::ScalarField], bf: C::ScalarField, - ) -> ( - FixedColumns, - VerifierKey>, - ) { - let fixed_columns = self.piop_params.fixed_columns(&children); - let xs = fixed_columns.points.xs.as_poly(); - let ys = fixed_columns.points.ys.as_poly(); - let fixed_columns_committed = FixedColumnsCommitted { - points: [ - self.pcs_params.commit_hiding(xs, bf).unwrap(), - self.pcs_params - .commit_hiding(ys, C::ScalarField::zero()) - .unwrap(), - ], - ring_selector: self - .pcs_params - .commit_hiding( - fixed_columns.ring_selector.as_poly(), - C::ScalarField::zero(), - ) - .unwrap(), - phantom: PhantomData, - }; - let verifier_key = VerifierKey { - pcs_raw_vk: self.pcs_params.raw_vk(), - fixed_columns_committed, - }; - (fixed_columns, verifier_key) + ) -> Result, ()> { + let nodes_column = +

>::tree_nodes_column(&self.piop_params, nodes_x_coords); + let parent_node = self.pcs_params.commit_hiding(nodes_column.as_poly(), bf); + parent_node } - pub fn commit_nodes( - &self, - nodes: &[G], - // children_x_coords: Vec, - blinding: C::ScalarField, - ) -> Result { - let xs = self.piop_params.points_column(nodes).xs; - Ok(self.pcs_params.commit_hiding(xs.as_poly(), blinding)?.0) + pub fn commit_fixed_columns(&self) -> Vec> { + let fixed_columns =

>::fixed_columns(&self.piop_params); + fixed_columns + .iter() + .map(|c| { + self.pcs_params + .commit_hiding(c.as_poly(), C::ScalarField::zero()) + .unwrap() + }) + .collect() } } @@ -144,6 +143,32 @@ pub enum CycleSide { C1(C1), } +#[derive(Clone)] +pub struct ArkTranscript(ark_transcript::Transcript); + +impl> w3f_plonk_common::transcript::PlonkTranscript + for ArkTranscript +{ + fn _128_bit_point(&mut self, label: &'static [u8]) -> F { + self.0.challenge(label).read_reduce() + } + + fn _add_serializable(&mut self, label: &'static [u8], message: &impl CanonicalSerialize) { + self.0.label(label); + self.0.append(message); + } + + fn to_rng(mut self) -> impl RngCore { + self.0.challenge(b"transcript_rng") + } +} + +impl ArkTranscript { + pub fn new(label: &'static [u8]) -> Self { + Self(ark_transcript::Transcript::new_labeled(label)) + } +} + #[derive(Clone)] pub struct Coeffs(F, F); impl> ShplonkTranscript for Coeffs { @@ -161,37 +186,159 @@ impl> ShplonkTranscript for Coeffs { #[cfg(test)] mod tests { use super::*; + use crate::auth_path::node::LevelWitness; + use crate::auth_path::path::AuthenticationPath; + use crate::circuit_fat::params::PiopParams as CircuitParamsFat; + use crate::circuit_tall::params::PiopParams as CircuitParamsTall; use ark_ec::AdditiveGroup; use ark_ec::scalar_mul::glv::GLVConfig; use ark_ec::scalar_mul::wnaf::WnafContext; - use ark_ec::short_weierstrass::{Affine, Projective, SWCurveConfig}; + use ark_ec::short_weierstrass::{Affine, SWCurveConfig}; use ark_ec::{AffineRepr, CurveGroup}; - use ark_ff::PrimeField; use ark_ff::{BigInteger, Field, Zero}; + use ark_ff::{FftField, PrimeField}; use ark_pallas::PallasConfig; use ark_poly::DenseUVPolynomial; use ark_std::rand::Rng; use ark_std::{UniformRand, cfg_iter_mut, end_timer, start_timer, test_rng}; - use ark_vesta::VestaConfig; + use num_format::{Locale, ToFormattedString}; use w3f_pcs::Poly; use w3f_pcs::pcs::PCS; use w3f_pcs::pcs::PcsParams; use w3f_pcs::pcs::ipa::IPA; use w3f_plonk_common::test_helpers::random_vec; - use crate::auth_path::node::LevelWitness; - use crate::auth_path::path::AuthenticationPath; #[cfg(feature = "parallel")] use rayon::prelude::*; type PallasIPA = IPA; - fn random_witness, R: Rng>( - params: &CycleSideParams, + impl CycleParams + where + C0: CurveGroup, + C1: CurveGroup, + P0: CircuitParams, + P1: CircuitParams, + { + pub fn setup(domain_size: usize, rng: &mut R) -> Self { + let setup_degree = 3 * domain_size; + let c0_pcs_params = HidingIpa::::setup(setup_degree, rng); + let c1_pcs_params = HidingIpa::::setup(setup_degree, rng); + let c0_domain = Domain::::new(domain_size, true); + let c0_piop_params = P0::setup(c0_domain, c1_pcs_params.h, C1::Affine::rand(rng)); + let c1_domain = Domain::::new(domain_size, true); + let c1_piop_params = P1::setup(c1_domain, c0_pcs_params.h, C0::Affine::rand(rng)); + Self { + c0_params: CycleSideParams { + pcs_params: c0_pcs_params, + piop_params: c0_piop_params, + phantomm: PhantomData, + }, + c1_params: CycleSideParams { + pcs_params: c1_pcs_params, + piop_params: c1_piop_params, + phantomm: PhantomData, + }, + } + } + } + + // cargo test test_bench_curve_tree --release --features="print-trace" -- --show-output + // cargo test test_bench_curve_tree --release --features="print-trace parallel" -- --show-output + #[test] + #[ignore] + fn test_bench_curve_tree() { + let (log_n, h) = (8, 2); + println!("n = {}, height = {h}, FAT", 1 << log_n); + _test_proof::< + ark_pallas::Projective, + ark_vesta::Projective, + CircuitParamsFat, + CircuitParamsFat, + >(log_n, h); + println!(); + + let (log_n, h) = (9, 2); + println!("n = {}, height = {h}, TALL", 1 << log_n); + _test_proof::< + ark_pallas::Projective, + ark_vesta::Projective, + CircuitParamsTall, + CircuitParamsTall, + >(log_n, h); + println!(); + + let (log_n, h) = (10, 2); + println!("n = {}, height = {h}, TALL", 1 << log_n); + _test_proof::< + ark_pallas::Projective, + ark_vesta::Projective, + CircuitParamsTall, + CircuitParamsTall, + >(log_n, h); + println!(); + + let (log_n, h) = (8, 4); + println!("n = {}, height = {h}, FAT", 1 << log_n); + _test_proof::< + ark_pallas::Projective, + ark_vesta::Projective, + CircuitParamsFat, + CircuitParamsFat, + >(log_n, h); + println!(); + + let (log_n, h) = (9, 4); + println!("n = {}, height = {h}, TALL", 1 << log_n); + _test_proof::< + ark_pallas::Projective, + ark_vesta::Projective, + CircuitParamsTall, + CircuitParamsTall, + >(log_n, h); + println!(); + } + + fn _test_proof(log_n: usize, height: usize) + where + C0: CurveGroup, + C1: CurveGroup, + P0: CircuitParams, + P1: CircuitParams, + { + let rng = &mut test_rng(); + let domain_size = 1 << log_n; + let params = CycleParams::::setup(domain_size, rng); + let (_leaf, path, wrapped_root) = random_path(¶ms, height, rng); + let root = match wrapped_root { + CycleSide::C0(root) => root, //TODO: panics on odd height + _ => panic!(), + }; + let max_nodes = params.c0_params.piop_params.max_children(); + println!( + "capacity=**{}**, arity={max_nodes}", + max_nodes + .pow(height as u32) + .to_formatted_string(&Locale::en) + ); + let t_prove = start_timer!(|| format!( + "Proving CurveTree membership, height={height}, domain={domain_size}, arity={max_nodes}, capacity={}", + max_nodes.pow(height as u32) + )); + let (auth_path, proof) = params.prove(path, rng); + end_timer!(t_prove); + + let t_verify = start_timer!(|| "Verifying CurveTree membership"); + let valid = params.verify(auth_path, proof, root); + end_timer!(t_verify); + assert!(valid); + } + + pub fn random_witness, R: Rng>( + capacity: usize, path_node: G, rng: &mut R, ) -> LevelWitness { - let capacity = params.piop_params.keyset_part_size; let mut nodes = random_vec::(capacity, rng); let i = rng.gen_range(0..capacity); nodes[i] = path_node; @@ -201,22 +348,29 @@ mod tests { } } - pub fn random_nodes, R: Rng>( - params: &CycleSideParams, + pub fn random_nodes< + C: CurveGroup, + G: AffineRepr, + P: CircuitParams, + R: Rng, + >( + params: &CycleSideParams, path_node: G, rng: &mut R, ) -> (C::Affine, LevelWitness) { - let level_witness = random_witness(params, path_node, rng); + let level_witness = random_witness(params.piop_params.max_children(), path_node, rng); let parent = level_witness.compute_parent(params).unwrap(); (parent, level_witness) } pub fn random_path< - C0: CurveGroup, + C0: CurveGroup, C1: CurveGroup, + P0: CircuitParams, + P1: CircuitParams, R: Rng, >( - params: &CycleParams, + params: &CycleParams, length: usize, rng: &mut R, ) -> ( @@ -252,46 +406,6 @@ mod tests { (leaf, path, root) } - fn _test_proof(log_n: usize, height: usize) - where - F0: PrimeField, - F1: PrimeField, - C0: SWCurveConfig, - C1: SWCurveConfig, - { - let rng = &mut test_rng(); - - let domain_size = 1 << log_n; - let params = CycleParams::, Projective>::setup(domain_size, rng); - let (_leaf, path, wrapped_root) = random_path(¶ms, height, rng); - let _root = match wrapped_root { - CycleSide::C0(root) => root, //TODO: panics on odd height - _ => panic!(), - }; - - let capacity = params.c0_params.piop_params.keyset_part_size; - let t_prove = start_timer!(|| format!( - "Proving CurveTree membership, H={height}, M={}, C={}, C^{height}={}", - domain_size, - capacity, - capacity.pow(height as u32) - )); - let (auth_path, proof) = params.prove(path, rng); - end_timer!(t_prove); - - let t_verify = start_timer!(|| "Verifying CurveTree opening"); - let valid = params.verify(auth_path, proof, wrapped_root); - end_timer!(t_verify); - assert!(valid); - } - - // cargo test test_proof --release --features="print-trace" -- --show-output - // cargo test test_proof --release --features="print-trace parallel" -- --show-output - #[test] - fn test_proof() { - _test_proof::<_, _, PallasConfig, VestaConfig>(9, 4); - } - fn _bench_msm(log_n: u32) { let rng = &mut test_rng(); let n = 2usize.pow(log_n); diff --git a/pasta-tree/src/prover.rs b/pasta-tree/src/prover.rs index 0a070f9..1095d77 100644 --- a/pasta-tree/src/prover.rs +++ b/pasta-tree/src/prover.rs @@ -1,87 +1,88 @@ use crate::auth_path::blinded::BlindedAuthenticationPath; use crate::auth_path::node::LevelWitnessWithBlinding; use crate::auth_path::path::AuthenticationPath; -use crate::{Coeffs, CurveTreeProof, CycleParams, CycleSideParams, CycleSideProof, IPACommitment}; -use ark_ec::CurveGroup; -use ark_ec::short_weierstrass::{Affine, Projective, SWCurveConfig}; +use crate::{CircuitParams, CycleParams, CycleSideParams}; +use crate::{Coeffs, CurveTreeProof, CycleSideProof}; +use ark_ec::{AffineRepr, CurveGroup}; +// use ark_ec::short_weierstrass::{Affine as SwAffine, Projective, SWCurveConfig}; +use crate::ArkTranscript; use ark_ff::{PrimeField, Zero}; -use ark_std::UniformRand; +use ark_poly::Polynomial; use ark_std::rand::Rng; +use ark_std::{UniformRand, end_timer, start_timer}; use std::collections::BTreeSet; use w3f_pcs::pcs::PcsParams; +use w3f_pcs::pcs::commitment::WrappedAffine; use w3f_pcs::pcs::ipa::hiding::HidingIpa; use w3f_pcs::shplonk::Shplonk; -use w3f_plonk_common::piop::ProverPiop; +use w3f_plonk_common::piop::{ProverPiop, VerifierPiop}; use w3f_plonk_common::prover::{PcsOpeningAt2Points, PlonkProver}; -use w3f_ring_proof::ArkTranscript; -use w3f_ring_proof::piop::prover::PiopProver; -impl CycleParams, Projective> +impl CycleParams where - F0: PrimeField, - F1: PrimeField, - C0: SWCurveConfig, - C1: SWCurveConfig, + C0: CurveGroup, + C1: CurveGroup, + P0: CircuitParams, + P1: CircuitParams, { pub fn prove( &self, - auth_path: AuthenticationPath, Projective>, + auth_path: AuthenticationPath, rng: &mut R, ) -> ( - BlindedAuthenticationPath, Projective>, - CurveTreeProof, Projective>, + BlindedAuthenticationPath, + CurveTreeProof, ) { let auth_path_with_bf = auth_path.with_blinding(rng); - let blinded_auth_path = auth_path_with_bf.apply_bfs(&self); + let blinded_auth_path = + auth_path_with_bf.apply_bfs(&self.c0_params.pcs_params, &self.c1_params.pcs_params); let auth_path = blinded_auth_path.clone(); let c0_proof = self.c0_params - .prove_side(blinded_auth_path.c1_path, &auth_path_with_bf.c1_path, rng); + .prove_side(blinded_auth_path.c1_path, auth_path_with_bf.c1_path, rng); let c1_proof = self.c1_params - .prove_side(blinded_auth_path.c0_path, &auth_path_with_bf.c0_path, rng); + .prove_side(blinded_auth_path.c0_path, auth_path_with_bf.c0_path, rng); (auth_path, CurveTreeProof { c0_proof, c1_proof }) } } -impl> - CycleSideParams> +impl, P: CircuitParams> + CycleSideParams { pub fn prove_side( &self, - blinded_path: Vec>, - witness: &[LevelWitnessWithBlinding>], + blinded_path: Vec, + witness: Vec>, rng: &mut R, - ) -> CycleSideProof { + ) -> CycleSideProof { + let curve_name = &std::any::type_name::()[70..]; + // println!("\n\nprover {curve_name}\nchildren={blinded_path:?}\n"); + debug_assert_eq!(blinded_path.len(), witness.len()); + let n_polys = P::VerifierCircuit::N_COLUMNS + 2; // plus the quotient and the linearization polys let mut piop_proofs = Vec::with_capacity(witness.len()); - let mut fixed_columns_committed = Vec::with_capacity(witness.len()); - let mut polys = Vec::with_capacity(witness.len() * 9); - let mut coords = Vec::with_capacity(witness.len() * 9); - let mut bfs = Vec::with_capacity(witness.len() * 9); + let mut polys = Vec::with_capacity(witness.len() * n_polys); + let mut coords = Vec::with_capacity(witness.len() * n_polys); + let mut bfs = Vec::with_capacity(witness.len() * n_polys); let plonk_prover = PlonkProver::, _>::init( self.pcs_params.ck(), - blinded_path.clone(), + (), ArkTranscript::new(b"pasta-tree-level-proof"), ); - for (level, blinded_node) in witness.iter().zip(blinded_path.into_iter()) { - let (fixed_columns, verifier_key) = - self.commit_children(&level.level_witness.siblings, level.parent_bf); - // debug_assert_eq!(verifier_key.fixed_columns_committed.points[0].0, *blinded_node); - fixed_columns_committed.push(verifier_key.fixed_columns_committed); - let piop = PiopProver::build( - &self.piop_params, - fixed_columns, - level.level_witness.path_node_idx, - level.bf, - ); - let blinded_node_ = > as ProverPiop< - C::ScalarField, - IPACommitment, - >>::result(&piop); - debug_assert_eq!(blinded_node_, blinded_node); + let t_commit_side = start_timer!(|| format!( + "Committing to {} polynomials at {curve_name}", + witness.len() * (n_polys - 1) + )); + for (level, blinded_node) in witness.into_iter().zip(blinded_path.into_iter()) { + let t_commit_level = start_timer!(|| format!("Committing to {} polynomials", n_polys)); + let piop: P::ProverCircuit = +

>::prover_circuit(&self.piop_params, level.clone()); + let result = + >>::result(&piop); + debug_assert_eq!(result, blinded_node); let (pcs_openings, piop_proof, _transcript) = plonk_prover.reduce_to_pcs_opening(piop); piop_proofs.push(piop_proof); let PcsOpeningAt2Points { @@ -90,7 +91,13 @@ impl>::open_many_hiding( &self.pcs_params, @@ -111,12 +126,12 @@ impl CycleParams, Projective> +impl CycleParams where - F0: PrimeField, - F1: PrimeField, - C0: SWCurveConfig, - C1: SWCurveConfig, + C0: CurveGroup, + C1: CurveGroup, + P0: CircuitParams, + P1: CircuitParams, { pub fn verify( &self, - auth_path: BlindedAuthenticationPath, Projective>, - proof: CurveTreeProof, Projective>, - _root: CycleSide, Affine>, + auth_path: BlindedAuthenticationPath, + proof: CurveTreeProof, + root: C0::Affine, ) -> bool { - // println!("leaf = {}", auth_path.c0_path[0]); - // println!("root = {:?}", root); - let _c0_x_coords: Vec> = proof - .c0_proof - .fixed_columns_committed - .iter() - .map(|c| c.points[0].0) - .collect(); - let _c1_x_coords: Vec> = proof - .c1_proof - .fixed_columns_committed - .iter() - .map(|c| c.points[0].0) - .collect(); - // match root { - // CycleSide::C0(c0_root) => { - // assert_eq!(c0_root, c0_x_coords[c0_x_coords.len() - 1]); - // assert_eq!(auth_path.c1_path, c1_x_coords); - // assert_eq!(auth_path.c0_path[1..], c0_x_coords[..c0_x_coords.len() - 1]); - // } - // CycleSide::C1(c1_root) => { - // assert_eq!(c1_root, c1_x_coords[c1_x_coords.len() - 1]); - // assert_eq!(auth_path.c1_path, c1_x_coords[..c1_x_coords.len() - 1]); - // assert_eq!(auth_path.c0_path[1..], c0_x_coords); - // } - // } + let BlindedAuthenticationPath { c0_path, c1_path } = auth_path; + let mut c0_parents = c0_path[1..].to_vec(); + c0_parents.push(root); let c0_proof = self .c0_params - .verify_side(auth_path.c1_path, proof.c0_proof); + .verify_side(c1_path.clone(), c0_parents, proof.c0_proof); assert!(c0_proof); - let c1_proof = self - .c1_params - .verify_side(auth_path.c0_path, proof.c1_proof); + let c1_proof = self.c1_params.verify_side(c0_path, c1_path, proof.c1_proof); assert!(c1_proof); c0_proof && c1_proof } } -impl> - CycleSideParams> +impl, P: CircuitParams> + CycleSideParams { pub fn verify_side( &self, - blinded_path: Vec>, - side_proof: CycleSideProof, + // selected re-randomized children + children: Vec, + // parents, re-randomized at the previous step + parents: Vec, + side_proof: CycleSideProof, ) -> bool { + // let mut s = std::any::type_name::(); + // s = &s[65..s.len()]; + // println!("\n\nverifier {s}\nchildren={children:?}\nparents={parents:?}\n"); + let plonk_verifier: PlonkVerifier, _> = PlonkVerifier::init( self.pcs_params.vk(), - &blinded_path, + &(), ArkTranscript::new(b"pasta-tree-level-proof"), ); - let mut polys = Vec::with_capacity(side_proof.piop_proofs.len() * 9); - let mut coords = Vec::with_capacity(side_proof.piop_proofs.len() * 9); - let mut vals = Vec::with_capacity(side_proof.piop_proofs.len() * 9); + let n_polys = P::VerifierCircuit::N_COLUMNS + 2; // plus the quotient and the linearization polys + let mut polys = Vec::with_capacity(side_proof.piop_proofs.len() * n_polys); + let mut coords = Vec::with_capacity(side_proof.piop_proofs.len() * n_polys); + let mut vals = Vec::with_capacity(side_proof.piop_proofs.len() * n_polys); + + //TODO: precompute + let fixed_cols = self.commit_fixed_columns(); - for ((blinded_node, piop_proof), parent) in blinded_path - .iter() + for ((child, parent), level_proof) in children + .into_iter() + .zip(parents.into_iter()) .zip(side_proof.piop_proofs.into_iter()) - .zip(side_proof.fixed_columns_committed.into_iter()) { let (challenges, _rng) = plonk_verifier.restore_challenges( - blinded_node, - &piop_proof, + &child, + &level_proof, // '1' accounts for the quotient polynomial that is aggregated together with the columns - 8, - 7, + P::VerifierCircuit::N_COLUMNS + 1, + P::VerifierCircuit::N_CONSTRAINTS, ); - let seed = self.piop_params.seed; - let seed_plus_result = (seed + blinded_node).into_affine(); - let domain_at_zeta = self.piop_params.domain.evaluate(challenges.zeta); - let piop = PiopVerifier::<_, _, Affine>::init( - domain_at_zeta, - parent, - piop_proof.column_commitments.clone(), - piop_proof.columns_at_zeta.clone(), - (seed.x, seed.y), - (seed_plus_result.x, seed_plus_result.y), + let piop = self.piop_params.verifier_circuit( + (child, parent), + &fixed_cols, + level_proof.column_commitments.clone(), + level_proof.columns_at_zeta.clone(), + challenges.zeta, ); - let PcsOpeningAt2Points { open_at_zeta, open_at_zeta_omega, @@ -110,8 +92,13 @@ impl Domains { Self { x1, x4 } } - fn column_from_evals(&self, padded_evals: Vec, payload_len: usize) -> FieldColumn { + pub fn column_from_evals(&self, padded_evals: Vec, payload_len: usize) -> FieldColumn { debug_assert_eq!(padded_evals.len(), self.x1.size()); let evals = Evaluations::from_vec_and_domain(padded_evals, self.x1); let poly = evals.interpolate_by_ref(); diff --git a/w3f-plonk-common/src/gadgets/booleanity.rs b/w3f-plonk-common/src/gadgets/booleanity.rs index e8fa94e..001041b 100644 --- a/w3f-plonk-common/src/gadgets/booleanity.rs +++ b/w3f-plonk-common/src/gadgets/booleanity.rs @@ -5,7 +5,7 @@ use ark_poly::{Evaluations, GeneralEvaluationDomain, Polynomial}; use ark_std::{vec, vec::Vec}; use crate::domain::Domain; -use crate::gadgets::VerifierGadget; +use crate::gadgets::{ProverGadget, VerifierGadget}; use crate::{const_evals, Column, FieldColumn}; #[derive(Clone)] @@ -51,12 +51,18 @@ pub struct Booleanity { bits: BitColumn, } -impl<'a, F: FftField> Booleanity { +impl Booleanity { pub fn init(bits: BitColumn) -> Self { Self { bits } } +} + +impl ProverGadget for Booleanity { + fn witness_columns(&self) -> Vec> { + todo!() + } - pub fn constraints(&self) -> Vec> { + fn constraints(&self) -> Vec> { let mut c = const_evals(F::one(), self.bits.domain_4x()); // c = 1 let b = &self.bits.col.evals_4x; c -= b; // c = 1 - b @@ -64,9 +70,13 @@ impl<'a, F: FftField> Booleanity { vec![c] } - pub fn constraints_linearized(&self, _z: &F) -> Vec> { + fn constraints_linearized(&self, _z: &F) -> Vec> { vec![DensePolynomial::zero()] } + + fn domain(&self) -> GeneralEvaluationDomain { + todo!() + } } pub struct BooleanityValues { diff --git a/w3f-plonk-common/src/gadgets/ec/mod.rs b/w3f-plonk-common/src/gadgets/ec/mod.rs index 7c54d37..04b023e 100644 --- a/w3f-plonk-common/src/gadgets/ec/mod.rs +++ b/w3f-plonk-common/src/gadgets/ec/mod.rs @@ -8,8 +8,11 @@ use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::marker::PhantomData; use ark_std::vec::Vec; +#[cfg(not(feature = "twisted_edwards"))] pub mod sw_cond_add; +#[cfg(feature = "twisted_edwards")] pub mod te_cond_add; +#[cfg(feature = "twisted_edwards")] pub mod te_doubling; // A vec of affine points from the prime-order subgroup of the curve whose base field enables FFTs, diff --git a/w3f-plonk-common/src/gadgets/ec/sw_cond_add.rs b/w3f-plonk-common/src/gadgets/ec/sw_cond_add.rs index a34769f..dbebae2 100644 --- a/w3f-plonk-common/src/gadgets/ec/sw_cond_add.rs +++ b/w3f-plonk-common/src/gadgets/ec/sw_cond_add.rs @@ -1,4 +1,5 @@ -use ark_ec::short_weierstrass::{Affine, SWCurveConfig}; +use ark_ec::AffineRepr; +// use ark_ec::short_weierstrass::{Affine, SWCurveConfig}; use ark_ff::{FftField, Field}; use ark_poly::univariate::DensePolynomial; use ark_poly::{Evaluations, GeneralEvaluationDomain}; @@ -8,10 +9,29 @@ use crate::gadgets::ec::{CondAdd, CondAddValues}; use crate::gadgets::{ProverGadget, VerifierGadget}; use crate::{const_evals, Column}; -impl ProverGadget for CondAdd> +// impl> AffineColumn> { +// // y^2 = x^3 + ax + b +// pub fn on_curve_constraint(&self) -> Evaluations { +// let domain = self.xs.domain_4x(); +// let sw_coeff_a = &const_evals(Curve::COEFF_A, domain); +// let sw_coeff_b = &const_evals(Curve::COEFF_B, domain); +// let x = &self.xs.evals_4x; +// let y = &self.ys.evals_4x; +// +// &(&(y * y) - &(&(x * x) * x)) - &(&(sw_coeff_a * x) + &sw_coeff_b) +// } +// +// pub fn on_curve_eval((x, y): (F, F)) -> F { +// y * y - x * x * x - Curve::COEFF_A * x - Curve::COEFF_B +// } +// } + +// impl ProverGadget for CondAdd> +impl ProverGadget for CondAdd where F: FftField, - Curve: SWCurveConfig, + // Curve: SWCurveConfig, + Curve: AffineRepr, { fn witness_columns(&self) -> Vec> { vec![self.acc.xs.poly.clone(), self.acc.ys.poly.clone()] @@ -90,7 +110,8 @@ where } } -impl> CondAddValues> { +// impl> CondAddValues> { +impl> CondAddValues { pub fn acc_coeffs_1(&self) -> (F, F) { let b = self.bitmask; let (x1, _y1) = self.acc; @@ -120,7 +141,8 @@ impl> CondAddValues> { } } -impl> VerifierGadget for CondAddValues> { +// impl> VerifierGadget for CondAddValues> { +impl> VerifierGadget for CondAddValues { fn evaluate_constraints_main(&self) -> Vec { let b = self.bitmask; let (x1, y1) = self.acc; diff --git a/w3f-plonk-common/src/gadgets/equal_cells.rs b/w3f-plonk-common/src/gadgets/equal_cells.rs new file mode 100644 index 0000000..0cf5d38 --- /dev/null +++ b/w3f-plonk-common/src/gadgets/equal_cells.rs @@ -0,0 +1,130 @@ +use ark_ff::{FftField, Field, Zero}; +use ark_poly::univariate::DensePolynomial; +use ark_poly::Evaluations; + +use ark_std::{vec, vec::Vec}; + +use crate::domain::Domain; +use crate::gadgets::VerifierGadget; +use crate::{Column, FieldColumn}; + +pub struct CellsEqPolys { + a: FieldColumn, + b: FieldColumn, + li: FieldColumn, +} + +pub struct EqualCells { + pub a: F, + pub b: F, + pub li: F, +} + +impl CellsEqPolys { + pub fn first_cells(a: FieldColumn, b: FieldColumn, domain: &Domain) -> Self { + Self::cells(a, b, 0, domain.l_first.clone(), domain) + } + + pub fn last_cells(a: FieldColumn, b: FieldColumn, domain: &Domain) -> Self { + Self::cells(a, b, domain.capacity - 1, domain.l_last.clone(), domain) + } + + pub fn cells( + a: FieldColumn, + b: FieldColumn, + i: usize, + li: FieldColumn, + domain: &Domain, + ) -> Self { + assert_eq!(a.payload_len(), domain.capacity); + assert_eq!(b.payload_len(), domain.capacity); + assert_eq!(a.evals.evals[i], b.evals.evals[i]); + Self { a, b, li } + } + + pub fn constraints(&self) -> Vec> { + let a = &self.a.evals_4x; + let b = &self.b.evals_4x; + let li = &self.li.evals_4x; + let c = li * &(a - b); + vec![c] + } + + pub fn constraints_linearized(&self, _z: &F) -> Vec> { + Self::constraints_lin() + } + + pub fn first_constraints( + a: FieldColumn, + b: FieldColumn, + domain: &Domain, + ) -> Vec> { + let gadget = Self::first_cells(a, b, domain); + gadget.constraints() + } + + pub fn last_constraints( + a: FieldColumn, + b: FieldColumn, + domain: &Domain, + ) -> Vec> { + let gadget = Self::last_cells(a, b, domain); + gadget.constraints() + } + + pub fn constraints_lin() -> Vec> { + vec![DensePolynomial::zero()] + } +} + +impl VerifierGadget for EqualCells { + fn evaluate_constraints_main(&self) -> Vec { + let c = self.li * (self.a - self.b); + vec![c] + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_helpers::random_vec; + use ark_ed_on_bls12_381_bandersnatch::Fq; + use ark_poly::Polynomial; + use ark_std::test_rng; + + fn _test_equal_cells_gadget(hiding: bool) { + let rng = &mut test_rng(); + + let log_n = 8; + let n = 1 << log_n; + let domain = Domain::new(n, hiding); + + let a = random_vec(domain.capacity, rng); + let mut b = random_vec(domain.capacity, rng); + b[0] = a[0]; + let a = domain.column(a); + let b = domain.column(b); + + let constraints_first = CellsEqPolys::::first_constraints(a, b, &domain); + let constraint_poly = constraints_first[0].interpolate_by_ref(); + assert_eq!(constraint_poly.degree(), 2 * n - 2); + assert!(domain.compute_quotient(&constraint_poly).is_some()); + + let a = random_vec(domain.capacity, rng); + let mut b = random_vec(domain.capacity, rng); + b[domain.capacity - 1] = a[domain.capacity - 1]; + let a = domain.column(a); + let b = domain.column(b); + + let constraints_last = CellsEqPolys::::last_constraints(a, b, &domain); + let constraint_poly = constraints_last[0].interpolate_by_ref(); + assert_eq!(constraint_poly.degree(), 2 * n - 2); + assert!(domain.compute_quotient(&constraint_poly).is_some()); + } + + #[test] + fn test_equal_cells_gadget() { + _test_equal_cells_gadget(false); + _test_equal_cells_gadget(true); + } +} diff --git a/w3f-plonk-common/src/gadgets/fixed_cells.rs b/w3f-plonk-common/src/gadgets/fixed_cells.rs index 35eec37..d4d5ca0 100644 --- a/w3f-plonk-common/src/gadgets/fixed_cells.rs +++ b/w3f-plonk-common/src/gadgets/fixed_cells.rs @@ -1,11 +1,11 @@ use ark_ff::{FftField, Field, Zero}; use ark_poly::univariate::DensePolynomial; -use ark_poly::Evaluations; +use ark_poly::{Evaluations, GeneralEvaluationDomain}; use ark_std::{vec, vec::Vec}; use crate::domain::Domain; -use crate::gadgets::VerifierGadget; +use crate::gadgets::{ProverGadget, VerifierGadget}; use crate::{const_evals, Column, FieldColumn}; pub struct FixedCells { @@ -45,17 +45,6 @@ impl FixedCells { } } - pub fn constraints(&self) -> Vec> { - let domain_capacity = self.col.payload_len(); // that's an ugly way to learn the capacity, but we've asserted it above. - let c = &Self::constraint_cell(&self.col, &self.l_first, 0, self.col_first) - + &Self::constraint_cell(&self.col, &self.l_last, domain_capacity - 1, self.col_last); - vec![c] - } - - pub fn constraints_linearized(&self, _z: &F) -> Vec> { - vec![DensePolynomial::zero()] - } - /// Constraints the column `col` to have the value `col[i]` at index `i`. /// `li` should be the `i-th` Lagrange basis polynomial `li = L_i(X)`. /// The constraint polynomial is `c(X) = L_i(X).col(X) - col[i].L_i(X)`. @@ -74,6 +63,27 @@ impl FixedCells { } } +impl ProverGadget for FixedCells { + fn witness_columns(&self) -> Vec> { + todo!() + } + + fn constraints(&self) -> Vec> { + let domain_capacity = self.col.payload_len(); // that's an ugly way to learn the capacity, but we've asserted it above. + let c = &Self::constraint_cell(&self.col, &self.l_first, 0, self.col_first) + + &Self::constraint_cell(&self.col, &self.l_last, domain_capacity - 1, self.col_last); + vec![c] + } + + fn constraints_linearized(&self, _z: &F) -> Vec> { + vec![DensePolynomial::zero()] + } + + fn domain(&self) -> GeneralEvaluationDomain { + todo!() + } +} + impl FixedCellsValues { pub fn evaluate_for_cell(col_eval: F, li_eval: F, cell_val: F) -> F { li_eval * (col_eval - cell_val) diff --git a/w3f-plonk-common/src/gadgets/inner_prod_inv.rs b/w3f-plonk-common/src/gadgets/inner_prod_inv.rs new file mode 100644 index 0000000..bbde15f --- /dev/null +++ b/w3f-plonk-common/src/gadgets/inner_prod_inv.rs @@ -0,0 +1,140 @@ +use ark_ff::{FftField, Field}; +use ark_poly::univariate::DensePolynomial; +use ark_poly::{Evaluations, GeneralEvaluationDomain}; + +use ark_std::{vec, vec::Vec}; + +use crate::domain::Domain; +use crate::gadgets::{ProverGadget, VerifierGadget}; +use crate::{Column, FieldColumn}; + +/// Does the same as `inner_prod.rs`, but with the witness column reversed. +/// The input vectors keep the normal ordering. The witness column contains +/// the seed at `acc[domain.capacity - 1] = seed` +/// and the inner product result at `acc[0] = seed + `. +pub struct InnerProdInv { + a: FieldColumn, + b: FieldColumn, + not_last: FieldColumn, + pub acc: FieldColumn, +} + +pub struct InnerProdInvValues { + pub a: F, + pub b: F, + pub not_last: F, + pub acc: F, +} + +impl InnerProdInv { + pub fn init(a: FieldColumn, b: FieldColumn, domain: &Domain) -> Self { + // we need an extra slot to seed the partial inner products acc with `0`. + assert_eq!(a.payload_len(), domain.capacity - 1); + assert_eq!(b.payload_len(), domain.capacity - 1); + let inner_prods = Self::partial_inner_prods(a.payload(), b.payload()); + let mut acc = vec![F::zero()]; + acc.extend(inner_prods); + acc.reverse(); + let acc = domain.column(acc); + Self { + a, + b, + not_last: domain.not_last_row.clone(), + acc, + } + } + + /// Returns a[n-1]b[n-1], a[n-1]b[n-1] + a[n-2]b[n-2], ..., a[0]b[0] + a[1]b[1] + ... + a[n-1]b[n-1] + fn partial_inner_prods(a: &[F], b: &[F]) -> Vec { + assert_eq!(a.len(), b.len()); + a.iter() + .rev() + .zip(b.iter().rev()) + .scan(F::zero(), |state, (&a, b)| { + *state += a * b; + Some(*state) + }) + .collect() + } +} + +impl ProverGadget for InnerProdInv { + fn witness_columns(&self) -> Vec> { + vec![self.acc.poly.clone()] + } + + fn constraints(&self) -> Vec> { + let a = &self.a.evals_4x; + let b = &self.b.evals_4x; + let acc = &self.acc.evals_4x; + let acc_shifted = &self.acc.shifted_4x(); + let not_last = &self.not_last.evals_4x; + let c = &(&(acc - acc_shifted) - &(a * b)) * not_last; + vec![c] + } + + fn constraints_linearized(&self, _z: &F) -> Vec> { + let c = -(&self.acc.poly * self.not_last.evaluate(_z)); + vec![c] + } + + fn domain(&self) -> GeneralEvaluationDomain { + self.a.evals.domain() + } +} + +impl VerifierGadget for InnerProdInvValues { + fn evaluate_constraints_main(&self) -> Vec { + let c = (self.acc - self.a * self.b) * self.not_last; + vec![c] + } +} + +#[cfg(test)] +mod tests { + use ark_ed_on_bls12_381_bandersnatch::Fq; + use ark_ff::{Field, Zero}; + use ark_poly::Polynomial; + use ark_std::test_rng; + + use crate::domain::Domain; + use crate::test_helpers::random_vec; + + use super::*; + + fn inner_prod(a: &[F], b: &[F]) -> F { + assert_eq!(a.len(), b.len()); + a.iter().zip(b).map(|(a, b)| *a * b).sum() + } + + fn _test_inner_prod_inv_gadget(hiding: bool) { + let rng = &mut test_rng(); + + let log_n = 10; + let n = 2usize.pow(log_n); + let domain = Domain::new(n, hiding); + + let a = random_vec(domain.capacity - 1, rng); + let b = random_vec(domain.capacity - 1, rng); + let ab = inner_prod(&a, &b); + let a = domain.column(a); + let b = domain.column(b); + + let gadget = InnerProdInv::::init(a, b, &domain); + + let acc = &gadget.acc.evals.evals; + assert!(acc[domain.capacity - 1].is_zero()); + assert_eq!(acc[0], ab); + + let constraint_poly = gadget.constraints()[0].interpolate_by_ref(); + + assert_eq!(constraint_poly.degree(), 2 * n - 1); + assert!(domain.compute_quotient(&constraint_poly).is_some()); + } + + #[test] + fn test_inner_prod_inv_gadget() { + _test_inner_prod_inv_gadget(false); + _test_inner_prod_inv_gadget(true); + } +} diff --git a/w3f-plonk-common/src/gadgets/mod.rs b/w3f-plonk-common/src/gadgets/mod.rs index 9d8bdf2..9fd7d5d 100644 --- a/w3f-plonk-common/src/gadgets/mod.rs +++ b/w3f-plonk-common/src/gadgets/mod.rs @@ -7,8 +7,10 @@ pub mod booleanity; // pub mod inner_prod_pub; pub mod column_sum; pub mod ec; +pub mod equal_cells; pub mod fixed_cells; pub mod inner_prod; +pub mod inner_prod_inv; pub trait ProverGadget { // Columns populated by the gadget. diff --git a/w3f-plonk-common/src/piop.rs b/w3f-plonk-common/src/piop.rs index 1facf2c..fd9ec12 100644 --- a/w3f-plonk-common/src/piop.rs +++ b/w3f-plonk-common/src/piop.rs @@ -9,6 +9,7 @@ use crate::domain::{Domain, EvaluatedDomain}; use crate::{ColumnsCommited, ColumnsEvaluated}; pub trait ProverPiop> { + const N_COLUMNS: usize; const N_CONSTRAINTS: usize; type Commitments: ColumnsCommited; @@ -78,8 +79,8 @@ pub fn aggregate_evaluations( } pub trait VerifierPiop> { - const N_CONSTRAINTS: usize; const N_COLUMNS: usize; + const N_CONSTRAINTS: usize; // Columns the commitments to which are publicly known. These commitments are omitted from the proof. fn precommitted_columns(&self) -> Vec; diff --git a/w3f-plonk-common/src/prover.rs b/w3f-plonk-common/src/prover.rs index b1049df..0779926 100644 --- a/w3f-plonk-common/src/prover.rs +++ b/w3f-plonk-common/src/prover.rs @@ -56,17 +56,29 @@ impl, T: PlonkTranscript> PlonkProver { let mut transcript = self.transcript_prelude.clone(); transcript.add_instance(&piop.result()); + // ROUND 1 // The prover commits to the columns. + let t_commit_cols = start_timer!(|| format!( + "Committing to {} degree-{} columns", + P::N_COLUMNS, + piop.domain().domain_size() - 1 + )); let column_commitments = piop.committed_columns(|p| CS::commit(&self.pcs_ck, p).unwrap()); transcript.add_committed_cols(&column_commitments); + end_timer!(t_commit_cols); // ROUND 2 let alphas = transcript.get_constraints_aggregation_coeffs(P::N_CONSTRAINTS); let quotient_poly = piop.compute_quotient(&alphas).unwrap(); + let t_commit_q = start_timer!(|| format!( + "Committing to the degree-{} quotient", + quotient_poly.degree() + )); // The prover commits to the quotient polynomial... let quotient_commitment = CS::commit(&self.pcs_ck, "ient_poly).unwrap(); transcript.add_quotient_commitment("ient_commitment); + end_timer!(t_commit_q); // and receives the evaluation point in response diff --git a/w3f-ring-proof/Cargo.toml b/w3f-ring-proof/Cargo.toml index 893c03f..230ba94 100644 --- a/w3f-ring-proof/Cargo.toml +++ b/w3f-ring-proof/Cargo.toml @@ -29,26 +29,27 @@ name = "ring_proof" harness = false [features] -default = [ "std" ] +default = ["std", "twisted_edwards"] std = [ - "ark-std/std", - "ark-ff/std", - "ark-ec/std", - "ark-poly/std", - "ark-serialize/std", - "w3f-pcs/std", - "w3f-plonk-common/std" + "ark-std/std", + "ark-ff/std", + "ark-ec/std", + "ark-poly/std", + "ark-serialize/std", + "w3f-pcs/std", + "w3f-plonk-common/std" ] parallel = [ - "std", - "rayon", - "ark-std/parallel", - "ark-ff/parallel", - "ark-ec/parallel", - "ark-poly/parallel", - "w3f-plonk-common/parallel", - "w3f-pcs/parallel" + "std", + "rayon", + "ark-std/parallel", + "ark-ff/parallel", + "ark-ec/parallel", + "ark-poly/parallel", + "w3f-plonk-common/parallel", + "w3f-pcs/parallel" ] print-trace = ["ark-std/print-trace"] -asm = [ "w3f-pcs/asm" ] -test-vectors = [ "w3f-plonk-common/test-vectors" ] +asm = ["w3f-pcs/asm"] +test-vectors = ["w3f-plonk-common/test-vectors"] +twisted_edwards = ["w3f-plonk-common/twisted_edwards"] diff --git a/w3f-ring-proof/src/piop/prover.rs b/w3f-ring-proof/src/piop/prover.rs index 285f7fb..3aaa4bc 100644 --- a/w3f-ring-proof/src/piop/prover.rs +++ b/w3f-ring-proof/src/piop/prover.rs @@ -1,5 +1,3 @@ -use ark_ec::short_weierstrass::{Affine as SwAffine, SWCurveConfig}; -use ark_ec::twisted_edwards::{Affine as TeAffine, TECurveConfig}; use ark_ec::AffineRepr; use ark_ff::PrimeField; use ark_poly::univariate::DensePolynomial; @@ -140,12 +138,16 @@ impl> PiopProver { } } +#[cfg(feature = "twisted_edwards")] +use ark_ec::twisted_edwards::{Affine as TeAffine, TECurveConfig}; +#[cfg(feature = "twisted_edwards")] impl ProverPiop for PiopProver> where F: PrimeField, C: Commitment, Curve: TECurveConfig, { + const N_COLUMNS: usize = 7; const N_CONSTRAINTS: usize = 7; type Commitments = RingCommitments; @@ -202,12 +204,16 @@ where } } +#[cfg(not(feature = "twisted_edwards"))] +use ark_ec::short_weierstrass::{Affine as SwAffine, SWCurveConfig}; +#[cfg(not(feature = "twisted_edwards"))] impl ProverPiop for PiopProver> where F: PrimeField, C: Commitment, Curve: SWCurveConfig, { + const N_COLUMNS: usize = 7; const N_CONSTRAINTS: usize = 7; type Commitments = RingCommitments; diff --git a/w3f-ring-proof/src/piop/verifier.rs b/w3f-ring-proof/src/piop/verifier.rs index eeb53b0..15c90c7 100644 --- a/w3f-ring-proof/src/piop/verifier.rs +++ b/w3f-ring-proof/src/piop/verifier.rs @@ -1,5 +1,3 @@ -use ark_ec::short_weierstrass::{Affine as SwAffine, SWCurveConfig}; -use ark_ec::twisted_edwards::{Affine as TeAffine, TECurveConfig}; use ark_ec::AffineRepr; use ark_ff::PrimeField; use ark_std::marker::PhantomData; @@ -102,6 +100,9 @@ impl, P: AffineRepr> PiopVerifier } } +#[cfg(feature = "twisted_edwards")] +use ark_ec::twisted_edwards::{Affine as TeAffine, TECurveConfig}; +#[cfg(feature = "twisted_edwards")] impl, Jubjub: TECurveConfig> VerifierPiop for PiopVerifier> { @@ -150,11 +151,14 @@ impl, Jubjub: TECurveConfig> Veri } } +#[cfg(not(feature = "twisted_edwards"))] +use ark_ec::short_weierstrass::{Affine as SwAffine, SWCurveConfig}; +#[cfg(not(feature = "twisted_edwards"))] impl, Jubjub: SWCurveConfig> VerifierPiop for PiopVerifier> { - const N_CONSTRAINTS: usize = 7; const N_COLUMNS: usize = 7; + const N_CONSTRAINTS: usize = 7; fn precommitted_columns(&self) -> Vec { self.fixed_columns_committed.as_vec()