mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-07 00:03:10 +00:00
Merge branch 'main' into push_to_8192
# Conflicts: # src/fri/recursive_verifier.rs # src/plonk/circuit_data.rs # src/plonk/recursive_verifier.rs # src/util/reducing.rs
This commit is contained in:
commit
6ba6201b94
@ -32,7 +32,6 @@ fn bench_prove<F: Field + Extendable<D>, const D: usize>() -> Result<()> {
|
||||
proof_of_work_bits: 20,
|
||||
reduction_arity_bits: vec![2, 2, 2, 2, 2, 2],
|
||||
num_query_rounds: 35,
|
||||
cap_height: 1,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -185,6 +185,10 @@ pub trait Field:
|
||||
Self::from_canonical_u64(n as u64)
|
||||
}
|
||||
|
||||
fn from_bool(b: bool) -> Self {
|
||||
Self::from_canonical_u64(b as u64)
|
||||
}
|
||||
|
||||
fn to_canonical_biguint(&self) -> BigUint;
|
||||
|
||||
fn from_canonical_biguint(n: BigUint) -> Self;
|
||||
|
||||
@ -205,7 +205,7 @@ impl<F: Field> PolynomialBatchCommitment<F> {
|
||||
lde_final_poly,
|
||||
lde_final_values,
|
||||
challenger,
|
||||
&config.fri_config,
|
||||
&config,
|
||||
timing,
|
||||
);
|
||||
|
||||
@ -281,8 +281,8 @@ mod tests {
|
||||
proof_of_work_bits: 2,
|
||||
reduction_arity_bits: vec![2, 3, 1, 2],
|
||||
num_query_rounds: 3,
|
||||
cap_height: 1,
|
||||
};
|
||||
|
||||
// We only care about `fri_config, num_constants`, and `num_routed_wires` here.
|
||||
let common_data = CommonCircuitData {
|
||||
config: CircuitConfig {
|
||||
@ -301,12 +301,12 @@ mod tests {
|
||||
circuit_digest: HashOut::from_partial(vec![]),
|
||||
};
|
||||
|
||||
let lpcs = (0..4)
|
||||
let commitments = (0..4)
|
||||
.map(|i| {
|
||||
PolynomialBatchCommitment::<F>::from_values(
|
||||
gen_random_test_case(ks[i], degree_bits),
|
||||
common_data.config.rate_bits,
|
||||
PlonkPolynomials::polynomials(i).blinding,
|
||||
common_data.config.zero_knowledge && PlonkPolynomials::polynomials(i).blinding,
|
||||
common_data.config.cap_height,
|
||||
&mut TimingTree::default(),
|
||||
)
|
||||
@ -315,7 +315,12 @@ mod tests {
|
||||
|
||||
let zeta = gen_random_point::<F, D>(degree_bits);
|
||||
let (proof, os) = PolynomialBatchCommitment::open_plonk::<D>(
|
||||
&[&lpcs[0], &lpcs[1], &lpcs[2], &lpcs[3]],
|
||||
&[
|
||||
&commitments[0],
|
||||
&commitments[1],
|
||||
&commitments[2],
|
||||
&commitments[3],
|
||||
],
|
||||
zeta,
|
||||
&mut Challenger::new(),
|
||||
&common_data,
|
||||
@ -323,10 +328,10 @@ mod tests {
|
||||
);
|
||||
|
||||
let merkle_caps = &[
|
||||
lpcs[0].merkle_tree.cap.clone(),
|
||||
lpcs[1].merkle_tree.cap.clone(),
|
||||
lpcs[2].merkle_tree.cap.clone(),
|
||||
lpcs[3].merkle_tree.cap.clone(),
|
||||
commitments[0].merkle_tree.cap.clone(),
|
||||
commitments[1].merkle_tree.cap.clone(),
|
||||
commitments[2].merkle_tree.cap.clone(),
|
||||
commitments[3].merkle_tree.cap.clone(),
|
||||
];
|
||||
|
||||
verify_fri_proof(
|
||||
|
||||
@ -4,10 +4,6 @@ pub mod prover;
|
||||
pub mod recursive_verifier;
|
||||
pub mod verifier;
|
||||
|
||||
/// Somewhat arbitrary. Smaller values will increase delta, but with diminishing returns,
|
||||
/// while increasing L, potentially requiring more challenge points.
|
||||
const EPSILON: f64 = 0.01;
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct FriConfig {
|
||||
pub proof_of_work_bits: u32,
|
||||
@ -20,29 +16,4 @@ pub struct FriConfig {
|
||||
|
||||
/// Number of query rounds to perform.
|
||||
pub num_query_rounds: usize,
|
||||
|
||||
pub cap_height: usize,
|
||||
}
|
||||
|
||||
fn fri_delta(rate_log: usize, conjecture: bool) -> f64 {
|
||||
let rate = (1 << rate_log) as f64;
|
||||
if conjecture {
|
||||
// See Conjecture 2.3 in DEEP-FRI.
|
||||
1.0 - rate - EPSILON
|
||||
} else {
|
||||
// See the Johnson radius.
|
||||
1.0 - rate.sqrt() - EPSILON
|
||||
}
|
||||
}
|
||||
|
||||
fn fri_l(codeword_len: usize, rate_log: usize, conjecture: bool) -> f64 {
|
||||
let rate = (1 << rate_log) as f64;
|
||||
if conjecture {
|
||||
// See Conjecture 2.3 in DEEP-FRI.
|
||||
// We assume the conjecture holds with a constant of 1 (as do other STARK implementations).
|
||||
(codeword_len as f64) / EPSILON
|
||||
} else {
|
||||
// See the Johnson bound.
|
||||
1.0 / (2.0 * EPSILON * rate.sqrt())
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ use crate::hash::hash_types::HashOut;
|
||||
use crate::hash::hashing::hash_n_to_1;
|
||||
use crate::hash::merkle_tree::MerkleTree;
|
||||
use crate::iop::challenger::Challenger;
|
||||
use crate::plonk::circuit_data::CircuitConfig;
|
||||
use crate::plonk::plonk_common::reduce_with_powers;
|
||||
use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues};
|
||||
use crate::timed;
|
||||
@ -22,7 +23,7 @@ pub fn fri_proof<F: Field + Extendable<D>, const D: usize>(
|
||||
// Evaluation of the polynomial on the large domain.
|
||||
lde_polynomial_values: PolynomialValues<F::Extension>,
|
||||
challenger: &mut Challenger<F>,
|
||||
config: &FriConfig,
|
||||
config: &CircuitConfig,
|
||||
timing: &mut TimingTree,
|
||||
) -> FriProof<F, D> {
|
||||
let n = lde_polynomial_values.values.len();
|
||||
@ -45,12 +46,17 @@ pub fn fri_proof<F: Field + Extendable<D>, const D: usize>(
|
||||
let pow_witness = timed!(
|
||||
timing,
|
||||
"find for proof-of-work witness",
|
||||
fri_proof_of_work(current_hash, config)
|
||||
fri_proof_of_work(current_hash, &config.fri_config)
|
||||
);
|
||||
|
||||
// Query phase
|
||||
let query_round_proofs =
|
||||
fri_prover_query_rounds(initial_merkle_trees, &trees, challenger, n, config);
|
||||
let query_round_proofs = fri_prover_query_rounds(
|
||||
initial_merkle_trees,
|
||||
&trees,
|
||||
challenger,
|
||||
n,
|
||||
&config.fri_config,
|
||||
);
|
||||
|
||||
FriProof {
|
||||
commit_phase_merkle_caps: trees.iter().map(|t| t.cap.clone()).collect(),
|
||||
@ -64,14 +70,14 @@ fn fri_committed_trees<F: Field + Extendable<D>, const D: usize>(
|
||||
mut coeffs: PolynomialCoeffs<F::Extension>,
|
||||
mut values: PolynomialValues<F::Extension>,
|
||||
challenger: &mut Challenger<F>,
|
||||
config: &FriConfig,
|
||||
config: &CircuitConfig,
|
||||
) -> (Vec<MerkleTree<F>>, PolynomialCoeffs<F::Extension>) {
|
||||
let mut trees = Vec::new();
|
||||
|
||||
let mut shift = F::MULTIPLICATIVE_GROUP_GENERATOR;
|
||||
let num_reductions = config.reduction_arity_bits.len();
|
||||
let num_reductions = config.fri_config.reduction_arity_bits.len();
|
||||
for i in 0..num_reductions {
|
||||
let arity = 1 << config.reduction_arity_bits[i];
|
||||
let arity = 1 << config.fri_config.reduction_arity_bits[i];
|
||||
|
||||
reverse_index_bits_in_place(&mut values.values);
|
||||
let chunked_values = values
|
||||
@ -97,7 +103,9 @@ fn fri_committed_trees<F: Field + Extendable<D>, const D: usize>(
|
||||
values = coeffs.coset_fft(shift.into())
|
||||
}
|
||||
|
||||
coeffs.trim();
|
||||
/// The coefficients being removed here should always be zero.
|
||||
coeffs.coeffs.truncate(coeffs.len() >> config.rate_bits);
|
||||
|
||||
challenger.observe_extension_elements(&coeffs.coeffs);
|
||||
(trees, coeffs)
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ use crate::fri::proof::{FriInitialTreeProofTarget, FriProofTarget, FriQueryRound
|
||||
use crate::fri::FriConfig;
|
||||
use crate::hash::hash_types::MerkleCapTarget;
|
||||
use crate::iop::challenger::RecursiveChallenger;
|
||||
use crate::iop::target::Target;
|
||||
use crate::iop::target::{BoolTarget, Target};
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::plonk::circuit_data::CommonCircuitData;
|
||||
use crate::plonk::plonk_common::PlonkPolynomials;
|
||||
@ -20,20 +20,20 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
fn compute_evaluation(
|
||||
&mut self,
|
||||
x: Target,
|
||||
x_index_within_coset_bits: &[Target],
|
||||
x_index_within_coset_bits: &[BoolTarget],
|
||||
arity_bits: usize,
|
||||
last_evals: &[ExtensionTarget<D>],
|
||||
evals: &[ExtensionTarget<D>],
|
||||
beta: ExtensionTarget<D>,
|
||||
) -> ExtensionTarget<D> {
|
||||
let arity = 1 << arity_bits;
|
||||
debug_assert_eq!(last_evals.len(), arity);
|
||||
debug_assert_eq!(evals.len(), arity);
|
||||
|
||||
let g = F::primitive_root_of_unity(arity_bits);
|
||||
let g_inv = g.exp((arity as u64) - 1);
|
||||
let g_inv_t = self.constant(g_inv);
|
||||
|
||||
// The evaluation vector needs to be reordered first.
|
||||
let mut evals = last_evals.to_vec();
|
||||
let mut evals = evals.to_vec();
|
||||
reverse_index_bits_in_place(&mut evals);
|
||||
// Want `g^(arity - rev_x_index_within_coset)` as in the out-of-circuit version. Compute it
|
||||
// as `(g^-1)^rev_x_index_within_coset`.
|
||||
@ -181,7 +181,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
|
||||
fn fri_verify_initial_proof(
|
||||
&mut self,
|
||||
x_index_bits: &[Target],
|
||||
x_index_bits: &[BoolTarget],
|
||||
proof: &FriInitialTreeProofTarget,
|
||||
initial_merkle_caps: &[MerkleCapTarget],
|
||||
cap_index: Target,
|
||||
@ -291,15 +291,13 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
round_proof: &FriQueryRoundTarget<D>,
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
) {
|
||||
let config = &common_data.config.fri_config;
|
||||
let config = &common_data.config;
|
||||
let n_log = log2_strict(n);
|
||||
// TODO: Do we need to range check `x_index` to a target smaller than `p`?
|
||||
let x_index = challenger.get_challenge(self);
|
||||
let mut x_index_bits = self.low_bits(x_index, n_log, 64);
|
||||
let cap_index = self.le_sum(
|
||||
x_index_bits[x_index_bits.len() - common_data.config.fri_config.cap_height..]
|
||||
.into_iter(),
|
||||
);
|
||||
let cap_index = self
|
||||
.le_sum(x_index_bits[x_index_bits.len() - common_data.config.cap_height..].into_iter());
|
||||
with_context!(
|
||||
self,
|
||||
"check FRI initial proof",
|
||||
@ -320,6 +318,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
let g_ext = self.convert_to_ext(g);
|
||||
let phi_ext = self.convert_to_ext(phi);
|
||||
let zero = self.zero_extension();
|
||||
// `subgroup_x = g*phi, vanish_zeta = g*phi - zeta`
|
||||
let tmp = self.double_arithmetic_extension(
|
||||
F::ONE,
|
||||
F::NEG_ONE,
|
||||
@ -348,7 +347,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
)
|
||||
);
|
||||
|
||||
for (i, &arity_bits) in config.reduction_arity_bits.iter().enumerate() {
|
||||
for (i, &arity_bits) in config.fri_config.reduction_arity_bits.iter().enumerate() {
|
||||
let evals = &round_proof.steps[i].evals;
|
||||
|
||||
// Split x_index into the index of the coset x is in, and the index of x within that coset.
|
||||
@ -411,6 +410,7 @@ struct PrecomputedReducedEvalsTarget<const D: usize> {
|
||||
pub single: ExtensionTarget<D>,
|
||||
pub zs: ExtensionTarget<D>,
|
||||
pub zs_right: ExtensionTarget<D>,
|
||||
/// Slope of the line from `(zeta, zs)` to `(zeta_right, zs_right)`.
|
||||
pub slope: ExtensionTarget<D>,
|
||||
pub zeta_right: ExtensionTarget<D>,
|
||||
}
|
||||
|
||||
@ -21,24 +21,24 @@ fn compute_evaluation<F: Field + Extendable<D>, const D: usize>(
|
||||
x: F,
|
||||
x_index_within_coset: usize,
|
||||
arity_bits: usize,
|
||||
last_evals: &[F::Extension],
|
||||
evals: &[F::Extension],
|
||||
beta: F::Extension,
|
||||
) -> F::Extension {
|
||||
let arity = 1 << arity_bits;
|
||||
debug_assert_eq!(last_evals.len(), arity);
|
||||
debug_assert_eq!(evals.len(), arity);
|
||||
|
||||
let g = F::primitive_root_of_unity(arity_bits);
|
||||
|
||||
// The evaluation vector needs to be reordered first.
|
||||
let mut evals = last_evals.to_vec();
|
||||
let mut evals = evals.to_vec();
|
||||
reverse_index_bits_in_place(&mut evals);
|
||||
let rev_x_index_within_coset = reverse_bits(x_index_within_coset, arity_bits);
|
||||
let coset_start = x * g.exp((arity - rev_x_index_within_coset) as u64);
|
||||
// The answer is gotten by interpolating {(x*g^i, P(x*g^i))} and evaluating at beta.
|
||||
let points = g
|
||||
.powers()
|
||||
.map(|y| (coset_start * y).into())
|
||||
.zip(evals)
|
||||
.map(|(y, e)| ((coset_start * y).into(), e))
|
||||
.collect::<Vec<_>>();
|
||||
let barycentric_weights = barycentric_weights(&points);
|
||||
interpolate(&points, beta, &barycentric_weights)
|
||||
|
||||
@ -2,7 +2,7 @@ use std::borrow::Borrow;
|
||||
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::gates::exponentiation::ExponentiationGate;
|
||||
use crate::iop::target::Target;
|
||||
use crate::iop::target::{BoolTarget, Target};
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
|
||||
impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
@ -115,21 +115,21 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
pub fn exp_from_bits(
|
||||
&mut self,
|
||||
base: Target,
|
||||
exponent_bits: impl IntoIterator<Item = impl Borrow<Target>>,
|
||||
exponent_bits: impl IntoIterator<Item = impl Borrow<BoolTarget>>,
|
||||
) -> Target {
|
||||
let zero = self.zero();
|
||||
let _false = self._false();
|
||||
let gate = ExponentiationGate::new(self.config.clone());
|
||||
let num_power_bits = gate.num_power_bits;
|
||||
let mut exp_bits_vec: Vec<Target> =
|
||||
let mut exp_bits_vec: Vec<BoolTarget> =
|
||||
exponent_bits.into_iter().map(|b| *b.borrow()).collect();
|
||||
while exp_bits_vec.len() < num_power_bits {
|
||||
exp_bits_vec.push(zero);
|
||||
exp_bits_vec.push(_false);
|
||||
}
|
||||
let gate_index = self.add_gate(gate.clone(), vec![]);
|
||||
|
||||
self.route(base, Target::wire(gate_index, gate.wire_base()));
|
||||
exp_bits_vec.iter().enumerate().for_each(|(i, bit)| {
|
||||
self.route(*bit, Target::wire(gate_index, gate.wire_power_bit(i)));
|
||||
self.route(bit.target, Target::wire(gate_index, gate.wire_power_bit(i)));
|
||||
});
|
||||
|
||||
Target::wire(gate_index, gate.wire_output())
|
||||
@ -148,8 +148,8 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
pub fn exp_u64(&mut self, base: Target, mut exponent: u64) -> Target {
|
||||
let mut exp_bits = Vec::new();
|
||||
while exponent != 0 {
|
||||
let bit = exponent & 1;
|
||||
let bit_target = self.constant(F::from_canonical_u64(bit));
|
||||
let bit = (exponent & 1) == 1;
|
||||
let bit_target = self.constant_bool(bit);
|
||||
exp_bits.push(bit_target);
|
||||
exponent >>= 1;
|
||||
}
|
||||
|
||||
@ -802,7 +802,7 @@ mod tests {
|
||||
type FF = QuarticCrandallField;
|
||||
const D: usize = 4;
|
||||
|
||||
let config = CircuitConfig::large_config();
|
||||
let config = CircuitConfig::large_zk_config();
|
||||
|
||||
let pw = PartialWitness::new(config.num_wires);
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
|
||||
@ -113,7 +113,7 @@ mod tests {
|
||||
fn test_interpolate2() -> Result<()> {
|
||||
type F = CrandallField;
|
||||
type FF = QuarticCrandallField;
|
||||
let config = CircuitConfig::large_config();
|
||||
let config = CircuitConfig::large_zk_config();
|
||||
let pw = PartialWitness::new(config.num_wires);
|
||||
let mut builder = CircuitBuilder::<F, 4>::new(config);
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ use crate::field::extension_field::Extendable;
|
||||
use crate::field::field_types::Field;
|
||||
use crate::gates::base_sum::BaseSumGate;
|
||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator};
|
||||
use crate::iop::target::Target;
|
||||
use crate::iop::target::{BoolTarget, Target};
|
||||
use crate::iop::witness::PartialWitness;
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
|
||||
@ -15,7 +15,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
}
|
||||
|
||||
/// Returns the first `num_low_bits` little-endian bits of `x`.
|
||||
pub fn low_bits(&mut self, x: Target, num_low_bits: usize, num_bits: usize) -> Vec<Target> {
|
||||
pub fn low_bits(&mut self, x: Target, num_low_bits: usize, num_bits: usize) -> Vec<BoolTarget> {
|
||||
let mut res = self.split_le(x, num_bits);
|
||||
res.truncate(num_low_bits);
|
||||
res
|
||||
|
||||
@ -1,14 +1,25 @@
|
||||
use crate::field::extension_field::target::ExtensionTarget;
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::gates::arithmetic::ArithmeticExtensionGate;
|
||||
use crate::iop::target::Target;
|
||||
use crate::iop::target::{BoolTarget, Target};
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
|
||||
impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
/// Selects `x` or `y` based on `b`, which is assumed to be binary, i.e., this returns `if b { x } else { y }`.
|
||||
/// This expression is gotten as `bx - (by-y)`, which can be computed with a single `ArithmeticExtensionGate`.
|
||||
/// Note: This does not range-check `b`.
|
||||
/// Selects `x` or `y` based on `b`, i.e., this returns `if b { x } else { y }`.
|
||||
pub fn select_ext(
|
||||
&mut self,
|
||||
b: BoolTarget,
|
||||
x: ExtensionTarget<D>,
|
||||
y: ExtensionTarget<D>,
|
||||
) -> ExtensionTarget<D> {
|
||||
let b_ext = self.convert_to_ext(b.target);
|
||||
self.select_ext_generalized(b_ext, x, y)
|
||||
}
|
||||
|
||||
/// Like `select_ext`, but accepts a condition input which does not necessarily have to be
|
||||
/// binary. In this case, it computes the arithmetic generalization of `if b { x } else { y }`,
|
||||
/// i.e. `bx - (by-y)`, which can be computed with a single `ArithmeticExtensionGate`.
|
||||
pub fn select_ext_generalized(
|
||||
&mut self,
|
||||
b: ExtensionTarget<D>,
|
||||
x: ExtensionTarget<D>,
|
||||
@ -36,11 +47,10 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
}
|
||||
|
||||
/// See `select_ext`.
|
||||
pub fn select(&mut self, b: Target, x: Target, y: Target) -> Target {
|
||||
let b_ext = self.convert_to_ext(b);
|
||||
pub fn select(&mut self, b: BoolTarget, x: Target, y: Target) -> Target {
|
||||
let x_ext = self.convert_to_ext(x);
|
||||
let y_ext = self.convert_to_ext(y);
|
||||
self.select_ext(b_ext, x_ext, y_ext).to_target_array()[0]
|
||||
self.select_ext(b, x_ext, y_ext).to_target_array()[0]
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,13 +77,11 @@ mod tests {
|
||||
let (x, y) = (FF::rand(), FF::rand());
|
||||
let xt = builder.add_virtual_extension_target();
|
||||
let yt = builder.add_virtual_extension_target();
|
||||
let truet = builder.add_virtual_extension_target();
|
||||
let falset = builder.add_virtual_extension_target();
|
||||
let truet = builder._true();
|
||||
let falset = builder._false();
|
||||
|
||||
pw.set_extension_target(xt, x);
|
||||
pw.set_extension_target(yt, y);
|
||||
pw.set_extension_target(truet, FF::ONE);
|
||||
pw.set_extension_target(falset, FF::ZERO);
|
||||
|
||||
let should_be_x = builder.select_ext(truet, xt, yt);
|
||||
let should_be_y = builder.select_ext(falset, xt, yt);
|
||||
|
||||
@ -4,7 +4,7 @@ use crate::field::extension_field::Extendable;
|
||||
use crate::field::field_types::Field;
|
||||
use crate::gates::base_sum::BaseSumGate;
|
||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator};
|
||||
use crate::iop::target::Target;
|
||||
use crate::iop::target::{BoolTarget, Target};
|
||||
use crate::iop::witness::PartialWitness;
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
|
||||
@ -33,7 +33,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
/// the number with little-endian bit representation given by `bits`.
|
||||
pub(crate) fn le_sum(
|
||||
&mut self,
|
||||
bits: impl ExactSizeIterator<Item = impl Borrow<Target>> + Clone,
|
||||
bits: impl ExactSizeIterator<Item = impl Borrow<BoolTarget>> + Clone,
|
||||
) -> Target {
|
||||
let num_bits = bits.len();
|
||||
debug_assert!(
|
||||
@ -45,7 +45,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
.clone()
|
||||
.zip(BaseSumGate::<2>::START_LIMBS..BaseSumGate::<2>::START_LIMBS + num_bits)
|
||||
{
|
||||
self.route(*limb.borrow(), Target::wire(gate_index, wire));
|
||||
self.route(limb.borrow().target, Target::wire(gate_index, wire));
|
||||
}
|
||||
|
||||
self.add_generator(BaseSumGenerator::<2> {
|
||||
@ -60,21 +60,23 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
#[derive(Debug)]
|
||||
struct BaseSumGenerator<const B: usize> {
|
||||
gate_index: usize,
|
||||
limbs: Vec<Target>,
|
||||
limbs: Vec<BoolTarget>,
|
||||
}
|
||||
|
||||
impl<F: Field, const B: usize> SimpleGenerator<F> for BaseSumGenerator<B> {
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
self.limbs.clone()
|
||||
self.limbs.iter().map(|b| b.target).collect()
|
||||
}
|
||||
|
||||
fn run_once(&self, witness: &PartialWitness<F>, out_buffer: &mut GeneratedValues<F>) {
|
||||
let sum = self
|
||||
.limbs
|
||||
.iter()
|
||||
.map(|&t| witness.get_target(t))
|
||||
.map(|&t| witness.get_bool_target(t))
|
||||
.rev()
|
||||
.fold(F::ZERO, |acc, limb| acc * F::from_canonical_usize(B) + limb);
|
||||
.fold(F::ZERO, |acc, limb| {
|
||||
acc * F::from_canonical_usize(B) + F::from_bool(limb)
|
||||
});
|
||||
|
||||
out_buffer.set_target(
|
||||
Target::wire(self.gate_index, BaseSumGate::<B>::WIRE_SUM),
|
||||
@ -131,8 +133,8 @@ mod tests {
|
||||
let n = thread_rng().gen_range(0..(1 << 10));
|
||||
let x = builder.constant(F::from_canonical_usize(n));
|
||||
|
||||
let zero = builder.zero();
|
||||
let one = builder.one();
|
||||
let zero = builder._false();
|
||||
let one = builder._true();
|
||||
|
||||
let y = builder.le_sum(
|
||||
(0..10)
|
||||
|
||||
@ -1,34 +1,18 @@
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::field::field_types::Field;
|
||||
use crate::gates::base_sum::BaseSumGate;
|
||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||
use crate::iop::target::Target;
|
||||
use crate::iop::wire::Wire;
|
||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator};
|
||||
use crate::iop::target::{BoolTarget, Target};
|
||||
use crate::iop::witness::PartialWitness;
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::util::ceil_div_usize;
|
||||
|
||||
impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
/// Split the given integer into a list of virtual targets, where each one represents a bit of
|
||||
/// the integer, with little-endian ordering.
|
||||
///
|
||||
/// Note that this only handles witness generation; it does not enforce that the decomposition
|
||||
/// is correct. The output should be treated as a "purported" decomposition which must be
|
||||
/// enforced elsewhere.
|
||||
pub(crate) fn split_le_virtual(&mut self, integer: Target, num_bits: usize) -> Vec<Target> {
|
||||
let bit_targets = self.add_virtual_targets(num_bits);
|
||||
self.add_generator(SplitGenerator {
|
||||
integer,
|
||||
bits: bit_targets.clone(),
|
||||
});
|
||||
bit_targets
|
||||
}
|
||||
|
||||
/// Split the given integer into a list of wires, where each one represents a
|
||||
/// bit of the integer, with little-endian ordering.
|
||||
/// Verifies that the decomposition is correct by using `k` `BaseSum<2>` gates
|
||||
/// with `k` such that `k*num_routed_wires>=num_bits`.
|
||||
pub(crate) fn split_le(&mut self, integer: Target, num_bits: usize) -> Vec<Target> {
|
||||
/// with `k` such that `k * num_routed_wires >= num_bits`.
|
||||
pub(crate) fn split_le(&mut self, integer: Target, num_bits: usize) -> Vec<BoolTarget> {
|
||||
if num_bits == 0 {
|
||||
return Vec::new();
|
||||
}
|
||||
@ -40,10 +24,11 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
|
||||
let mut bits = Vec::with_capacity(num_bits);
|
||||
for &gate in &gates {
|
||||
bits.extend(Target::wires_from_range(
|
||||
gate,
|
||||
BaseSumGate::<2>::START_LIMBS..BaseSumGate::<2>::START_LIMBS + bits_per_gate,
|
||||
));
|
||||
let start_limbs = BaseSumGate::<2>::START_LIMBS;
|
||||
for limb_input in start_limbs..start_limbs + bits_per_gate {
|
||||
// `new_unsafe` is safe here because BaseSumGate::<2> forces it to be in `{0, 1}`.
|
||||
bits.push(BoolTarget::new_unsafe(Target::wire(gate, limb_input)));
|
||||
}
|
||||
}
|
||||
bits.drain(num_bits..);
|
||||
|
||||
@ -72,33 +57,6 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Generator for a little-endian split.
|
||||
#[must_use]
|
||||
pub fn split_le_generator<F: Field>(
|
||||
integer: Target,
|
||||
bits: Vec<Target>,
|
||||
) -> Box<dyn WitnessGenerator<F>> {
|
||||
Box::new(SplitGenerator { integer, bits })
|
||||
}
|
||||
|
||||
/// Generator for a little-endian split.
|
||||
#[must_use]
|
||||
pub fn split_le_generator_local_wires<F: Field>(
|
||||
gate: usize,
|
||||
integer_input_index: usize,
|
||||
bit_input_indices: &[usize],
|
||||
) -> Box<dyn WitnessGenerator<F>> {
|
||||
let integer = Target::Wire(Wire {
|
||||
gate,
|
||||
input: integer_input_index,
|
||||
});
|
||||
let bits = bit_input_indices
|
||||
.iter()
|
||||
.map(|&input| Target::Wire(Wire { gate, input }))
|
||||
.collect();
|
||||
Box::new(SplitGenerator { integer, bits })
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SplitGenerator {
|
||||
integer: Target,
|
||||
|
||||
@ -158,7 +158,7 @@ impl<F: Extendable<D>, const D: usize> Gate<F, D> for ExponentiationGate<F, D> {
|
||||
|
||||
// power_bits is in LE order, but we accumulate in BE order.
|
||||
let cur_bit = power_bits[self.num_power_bits - i - 1];
|
||||
let mul_by = builder.select_ext(cur_bit, base, one);
|
||||
let mul_by = builder.select_ext_generalized(cur_bit, base, one);
|
||||
let intermediate_value_diff =
|
||||
builder.mul_sub_extension(prev_intermediate_value, mul_by, intermediate_values[i]);
|
||||
constraints.push(intermediate_value_diff);
|
||||
@ -322,14 +322,14 @@ mod tests {
|
||||
|
||||
let num_power_bits = power_bits.len();
|
||||
|
||||
let power_bits_F: Vec<_> = power_bits
|
||||
let power_bits_f: Vec<_> = power_bits
|
||||
.iter()
|
||||
.map(|b| F::from_canonical_u64(*b))
|
||||
.collect();
|
||||
|
||||
let mut v = Vec::new();
|
||||
v.push(base);
|
||||
v.extend(power_bits_F.clone());
|
||||
v.extend(power_bits_f.clone());
|
||||
|
||||
let mut intermediate_values = Vec::new();
|
||||
let mut current_intermediate_value = F::ONE;
|
||||
|
||||
@ -137,9 +137,7 @@ pub(crate) fn test_eval_fns<F: Extendable<D>, G: Gate<F, D>, const D: usize>(
|
||||
let vars = EvaluationVars {
|
||||
local_constants: &constants,
|
||||
local_wires: &wires,
|
||||
public_inputs_hash: &HashOut {
|
||||
elements: [F::ZERO; 4],
|
||||
},
|
||||
public_inputs_hash: &public_inputs_hash,
|
||||
};
|
||||
let evals = gate.eval_unfiltered(vars);
|
||||
|
||||
|
||||
@ -56,7 +56,6 @@ impl<F: Extendable<D>, const D: usize> Gate<F, D> for NoopGate {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use crate::field::crandall_field::CrandallField;
|
||||
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
||||
use crate::gates::noop::NoopGate;
|
||||
|
||||
@ -77,11 +77,16 @@ impl<F: Extendable<D>, const D: usize> Gate<F, D> for PublicInputGate {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::field::crandall_field::CrandallField;
|
||||
use crate::gates::gate_testing::test_low_degree;
|
||||
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
||||
use crate::gates::public_input::PublicInputGate;
|
||||
|
||||
#[test]
|
||||
fn low_degree() {
|
||||
test_low_degree::<CrandallField, _, 4>(PublicInputGate)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_fns() -> anyhow::Result<()> {
|
||||
test_eval_fns::<CrandallField, _, 4>(PublicInputGate)
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ use crate::gates::gmimc::GMiMCGate;
|
||||
use crate::hash::hash_types::{HashOut, HashOutTarget, MerkleCapTarget};
|
||||
use crate::hash::hashing::{compress, hash_or_noop, GMIMC_ROUNDS};
|
||||
use crate::hash::merkle_tree::MerkleCap;
|
||||
use crate::iop::target::Target;
|
||||
use crate::iop::target::{BoolTarget, Target};
|
||||
use crate::iop::wire::Wire;
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
|
||||
@ -66,7 +66,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
pub(crate) fn verify_merkle_proof(
|
||||
&mut self,
|
||||
leaf_data: Vec<Target>,
|
||||
leaf_index_bits: &[Target],
|
||||
leaf_index_bits: &[BoolTarget],
|
||||
merkle_cap: &MerkleCapTarget,
|
||||
proof: &MerkleProofTarget,
|
||||
) {
|
||||
@ -83,7 +83,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
gate,
|
||||
input: swap_wire,
|
||||
});
|
||||
self.generate_copy(bit, swap_wire);
|
||||
self.generate_copy(bit.target, swap_wire);
|
||||
|
||||
let input_wires = (0..12)
|
||||
.map(|i| {
|
||||
@ -131,7 +131,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
pub(crate) fn verify_merkle_proof_with_cap_index(
|
||||
&mut self,
|
||||
leaf_data: Vec<Target>,
|
||||
leaf_index_bits: &[Target],
|
||||
leaf_index_bits: &[BoolTarget],
|
||||
cap_index: Target,
|
||||
merkle_cap: &MerkleCapTarget,
|
||||
proof: &MerkleProofTarget,
|
||||
@ -149,7 +149,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
gate,
|
||||
input: swap_wire,
|
||||
});
|
||||
self.generate_copy(bit, swap_wire);
|
||||
self.generate_copy(bit.target, swap_wire);
|
||||
|
||||
let input_wires = (0..12)
|
||||
.map(|i| {
|
||||
|
||||
@ -13,6 +13,12 @@ use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place};
|
||||
#[serde(bound = "")]
|
||||
pub struct MerkleCap<F: Field>(pub Vec<HashOut<F>>);
|
||||
|
||||
impl<F: Field> MerkleCap<F> {
|
||||
pub fn flatten(&self) -> Vec<F> {
|
||||
self.0.iter().flat_map(|h| h.elements).collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MerkleTree<F: Field> {
|
||||
/// The data in the leaves of the Merkle tree.
|
||||
|
||||
@ -31,3 +31,20 @@ impl Target {
|
||||
range.map(|i| Self::wire(gate, i)).collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// A `Target` which has already been constrained such that it can only be 0 or 1.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct BoolTarget {
|
||||
pub target: Target,
|
||||
/// This private field is here to force all instantiations to go through `new_unsafe`.
|
||||
_private: (),
|
||||
}
|
||||
|
||||
impl BoolTarget {
|
||||
pub fn new_unsafe(target: Target) -> BoolTarget {
|
||||
BoolTarget {
|
||||
target,
|
||||
_private: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@ use crate::gates::gate::GateInstance;
|
||||
use crate::hash::hash_types::HashOutTarget;
|
||||
use crate::hash::hash_types::{HashOut, MerkleCapTarget};
|
||||
use crate::hash::merkle_tree::MerkleCap;
|
||||
use crate::iop::target::Target;
|
||||
use crate::iop::target::{BoolTarget, Target};
|
||||
use crate::iop::wire::Wire;
|
||||
use crate::plonk::copy_constraint::CopyConstraint;
|
||||
|
||||
@ -70,6 +70,15 @@ impl<F: Field> PartialWitness<F> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_bool_target(&self, target: BoolTarget) -> bool {
|
||||
let value = self.get_target(target.target).to_canonical_u64();
|
||||
match value {
|
||||
0 => false,
|
||||
1 => true,
|
||||
_ => panic!("not a bool"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_hash_target(&self, ht: HashOutTarget) -> HashOut<F> {
|
||||
HashOut {
|
||||
elements: self.get_targets(&ht.elements).try_into().unwrap(),
|
||||
@ -180,6 +189,10 @@ impl<F: Field> PartialWitness<F> {
|
||||
.for_each(|(&et, &v)| self.set_extension_target(et, v));
|
||||
}
|
||||
|
||||
pub fn set_bool_target(&mut self, target: BoolTarget, value: bool) {
|
||||
self.set_target(target.target, F::from_bool(value))
|
||||
}
|
||||
|
||||
pub fn set_wire(&mut self, wire: Wire, value: F) {
|
||||
self.set_target(Target::Wire(wire), value)
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ use crate::gates::public_input::PublicInputGate;
|
||||
use crate::hash::hash_types::{HashOutTarget, MerkleCapTarget};
|
||||
use crate::hash::hashing::hash_n_to_hash;
|
||||
use crate::iop::generator::{CopyGenerator, RandomValueGenerator, WitnessGenerator};
|
||||
use crate::iop::target::Target;
|
||||
use crate::iop::target::{BoolTarget, Target};
|
||||
use crate::iop::wire::Wire;
|
||||
use crate::plonk::circuit_data::{
|
||||
CircuitConfig, CircuitData, CommonCircuitData, ProverCircuitData, ProverOnlyCircuitData,
|
||||
@ -132,6 +132,11 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
// TODO: Unsafe
|
||||
pub fn add_virtual_bool_target(&mut self) -> BoolTarget {
|
||||
BoolTarget::new_unsafe(self.add_virtual_target())
|
||||
}
|
||||
|
||||
/// Adds a gate to the circuit, and returns its index.
|
||||
pub fn add_gate<G: Gate<F, D>>(&mut self, gate_type: G, constants: Vec<F>) -> usize {
|
||||
self.check_gate_compatibility(&gate_type);
|
||||
@ -282,6 +287,14 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
self.constant(F::NEG_ONE)
|
||||
}
|
||||
|
||||
pub fn _false(&mut self) -> BoolTarget {
|
||||
BoolTarget::new_unsafe(self.zero())
|
||||
}
|
||||
|
||||
pub fn _true(&mut self) -> BoolTarget {
|
||||
BoolTarget::new_unsafe(self.one())
|
||||
}
|
||||
|
||||
/// Returns a routable target with the given constant value.
|
||||
pub fn constant(&mut self, c: F) -> Target {
|
||||
if let Some(&target) = self.constants_to_targets.get(&c) {
|
||||
@ -303,6 +316,14 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
constants.iter().map(|&c| self.constant(c)).collect()
|
||||
}
|
||||
|
||||
pub fn constant_bool(&mut self, b: bool) -> BoolTarget {
|
||||
if b {
|
||||
self._true()
|
||||
} else {
|
||||
self._false()
|
||||
}
|
||||
}
|
||||
|
||||
/// If the given target is a constant (i.e. it was created by the `constant(F)` method), returns
|
||||
/// its constant value. Otherwise, returns `None`.
|
||||
pub fn target_as_constant(&self, target: Target) -> Option<F> {
|
||||
@ -605,11 +626,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
|
||||
// TODO: This should also include an encoding of gate constraints.
|
||||
let circuit_digest_parts = [
|
||||
constants_sigmas_cap
|
||||
.0
|
||||
.into_iter()
|
||||
.flat_map(|h| h.elements)
|
||||
.collect::<Vec<_>>(),
|
||||
constants_sigmas_cap.flatten(),
|
||||
vec![/* Add other circuit data here */],
|
||||
];
|
||||
let circuit_digest = hash_n_to_hash(circuit_digest_parts.concat(), false);
|
||||
|
||||
@ -48,7 +48,6 @@ impl Default for CircuitConfig {
|
||||
proof_of_work_bits: 1,
|
||||
reduction_arity_bits: vec![1, 1, 1, 1],
|
||||
num_query_rounds: 1,
|
||||
cap_height: 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -72,10 +71,22 @@ impl CircuitConfig {
|
||||
proof_of_work_bits: 1,
|
||||
reduction_arity_bits: vec![1],
|
||||
num_query_rounds: 1,
|
||||
cap_height: 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn large_zk_config() -> Self {
|
||||
CircuitConfig {
|
||||
zero_knowledge: true,
|
||||
cap_height: 1,
|
||||
fri_config: FriConfig {
|
||||
proof_of_work_bits: 1,
|
||||
reduction_arity_bits: vec![1, 1, 1, 1],
|
||||
num_query_rounds: 1,
|
||||
},
|
||||
..Self::large_config()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Circuit data required by the prover or the verifier.
|
||||
|
||||
@ -9,9 +9,6 @@ use crate::plonk::vars::EvaluationTargets;
|
||||
use crate::util::reducing::ReducingFactorTarget;
|
||||
use crate::with_context;
|
||||
|
||||
const MIN_WIRES: usize = 120; // TODO: Double check.
|
||||
const MIN_ROUTED_WIRES: usize = 28; // TODO: Double check.
|
||||
|
||||
impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
/// Recursively verifies an inner proof.
|
||||
pub fn add_recursive_verifier(
|
||||
@ -21,8 +18,6 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
inner_verifier_data: &VerifierCircuitTarget,
|
||||
inner_common_data: &CommonCircuitData<F, D>,
|
||||
) {
|
||||
assert!(self.config.num_wires >= MIN_WIRES);
|
||||
assert!(self.config.num_wires >= MIN_ROUTED_WIRES);
|
||||
let ProofWithPublicInputsTarget {
|
||||
proof,
|
||||
public_inputs,
|
||||
@ -380,7 +375,6 @@ mod tests {
|
||||
proof_of_work_bits: 1,
|
||||
reduction_arity_bits: vec![2, 2, 2, 2, 2, 2],
|
||||
num_query_rounds: 40,
|
||||
cap_height: 1,
|
||||
},
|
||||
};
|
||||
let (proof_with_pis, vd, cd) = {
|
||||
@ -436,7 +430,6 @@ mod tests {
|
||||
proof_of_work_bits: 20,
|
||||
reduction_arity_bits: vec![3, 3, 3],
|
||||
num_query_rounds: 27,
|
||||
cap_height: 3,
|
||||
},
|
||||
};
|
||||
let (proof_with_pis, vd, cd) = {
|
||||
|
||||
@ -26,7 +26,7 @@ pub(crate) fn log2_ceil(n: usize) -> usize {
|
||||
|
||||
/// Computes `log_2(n)`, panicking if `n` is not a power of two.
|
||||
pub(crate) fn log2_strict(n: usize) -> usize {
|
||||
assert!(n.is_power_of_two(), "Not a power of two");
|
||||
assert!(n.is_power_of_two(), "Not a power of two: {}", n);
|
||||
log2_ceil(n)
|
||||
}
|
||||
|
||||
|
||||
@ -301,10 +301,7 @@ mod tests {
|
||||
type FF = QuarticCrandallField;
|
||||
const D: usize = 4;
|
||||
|
||||
let config = CircuitConfig {
|
||||
num_routed_wires: 64,
|
||||
..CircuitConfig::large_config()
|
||||
};
|
||||
let config = CircuitConfig::large_config();
|
||||
|
||||
let pw = PartialWitness::new(config.num_wires);
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user