plonky2/src/plonk/circuit_builder.rs

1119 lines
42 KiB
Rust
Raw Normal View History

2021-09-22 10:56:09 -07:00
use std::cmp::max;
use std::collections::{BTreeMap, HashMap, HashSet};
2021-03-25 15:20:14 -07:00
use std::time::Instant;
2021-02-26 13:18:41 -08:00
2021-10-25 09:12:42 -07:00
use log::{debug, info, Level};
2021-03-25 15:20:14 -07:00
use crate::field::cosets::get_unique_coset_shifts;
2021-06-07 17:55:27 +02:00
use crate::field::extension_field::target::ExtensionTarget;
use crate::field::extension_field::{Extendable, FieldExtension};
2021-09-22 10:56:09 -07:00
use crate::field::fft::fft_root_table;
use crate::field::field_types::{Field, RichField};
use crate::fri::commitment::PolynomialBatchCommitment;
use crate::fri::{FriConfig, FriParams};
use crate::gadgets::arithmetic::BaseArithmeticOperation;
use crate::gadgets::arithmetic_extension::ExtensionArithmeticOperation;
2021-11-10 09:53:27 -08:00
use crate::gadgets::arithmetic_u32::U32Target;
use crate::gates::arithmetic_base::ArithmeticGate;
use crate::gates::arithmetic_extension::ArithmeticExtensionGate;
2021-11-10 09:56:42 -08:00
use crate::gates::arithmetic_u32::{U32ArithmeticGate, NUM_U32_ARITHMETIC_OPS};
use crate::gates::constant::ConstantGate;
use crate::gates::gate::{Gate, GateInstance, GateRef, PrefixedGate};
2021-06-22 16:54:20 +02:00
use crate::gates::gate_tree::Tree;
use crate::gates::noop::NoopGate;
use crate::gates::public_input::PublicInputGate;
use crate::gates::random_access::RandomAccessGate;
2021-11-10 09:56:42 -08:00
use crate::gates::subtraction_u32::{U32SubtractionGate, NUM_U32_SUBTRACTION_OPS};
2021-09-01 16:38:10 -07:00
use crate::gates::switch::SwitchGate;
2021-08-10 13:33:44 +02:00
use crate::hash::hash_types::{HashOutTarget, MerkleCapTarget};
use crate::hash::hashing::hash_n_to_hash;
2021-09-02 15:03:03 -07:00
use crate::iop::generator::{
CopyGenerator, RandomValueGenerator, SimpleGenerator, WitnessGenerator,
};
use crate::iop::target::{BoolTarget, Target};
use crate::iop::wire::Wire;
use crate::plonk::circuit_data::{
CircuitConfig, CircuitData, CommonCircuitData, ProverCircuitData, ProverOnlyCircuitData,
VerifierCircuitData, VerifierOnlyCircuitData,
};
use crate::plonk::copy_constraint::CopyConstraint;
use crate::plonk::permutation_argument::Forest;
use crate::plonk::plonk_common::PlonkPolynomials;
2021-03-30 13:30:31 -07:00
use crate::polynomial::polynomial::PolynomialValues;
use crate::util::context_tree::ContextTree;
2021-07-14 20:54:30 +02:00
use crate::util::marking::{Markable, MarkedTargets};
2021-07-01 15:41:01 +02:00
use crate::util::partial_products::num_partial_products;
use crate::util::timing::TimingTree;
2021-06-29 10:07:05 -07:00
use crate::util::{log2_ceil, log2_strict, transpose, transpose_poly_values};
2021-02-26 13:18:41 -08:00
pub struct CircuitBuilder<F: RichField + Extendable<D>, const D: usize> {
pub(crate) config: CircuitConfig,
2021-03-21 11:57:33 -07:00
/// The types of gates used in this circuit.
gates: HashSet<GateRef<F, D>>,
2021-03-21 11:57:33 -07:00
2021-04-07 22:21:45 -07:00
/// The concrete placement of each gate.
2021-08-24 18:35:30 -07:00
pub(crate) gate_instances: Vec<GateInstance<F, D>>,
2021-03-21 11:57:33 -07:00
/// Targets to be made public.
public_inputs: Vec<Target>,
/// The next available index for a `VirtualTarget`.
2021-07-15 10:55:18 +02:00
virtual_target_index: usize,
2021-04-07 22:21:45 -07:00
2021-07-14 20:54:30 +02:00
copy_constraints: Vec<CopyConstraint>,
Tree of scopes (#106) * Tree of scopes This is an extension of the context concept. Earlier I was planning to store a simple stack of contexts, but I ended up storing the whole history, in a tree structure. This gives us more control over the output, i.e. we can print the gate count of a parent scope before those of its child scopes, which seems more user-friendly. Sample gate count output: [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] 27829 gates to root [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | 2373 gates to evaluate the vanishing polynomial at our challenge point, zeta. [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | 1284 gates to evaluate gate constraints [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | 25312 gates to verify FRI proof [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | 650 gates to verify 0'th FRI query [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | | 96 gates to check FRI initial proof [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | | 65 gates to compute x from its index [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | | 233 gates to combine initial oracles ... Sample copy constraint failure: Error: Copy constraint 'root > verify FRI proof > verify 0'th FRI query > check FRI initial proof > verify 0'th initial Merkle proof > check Merkle root: 0-th hash element' between wire 12 of gate #2550 [...] and wire 0 of gate #0 [...] is not satisfied. Got values of 6861386743364621393 and 0 respectively. * No min * info -> debug * Move to its own file
2021-07-19 12:22:18 -07:00
/// A tree of named scopes, used for debugging.
context_log: ContextTree,
2021-07-14 20:54:30 +02:00
2021-07-15 10:24:11 +02:00
/// A vector of marked targets. The values assigned to these targets will be displayed by the prover.
marked_targets: Vec<MarkedTargets<D>>,
2021-04-07 22:21:45 -07:00
/// Generators used to generate the witness.
2021-07-15 10:13:13 +02:00
generators: Vec<Box<dyn WitnessGenerator<F>>>,
2021-04-21 11:47:18 -07:00
constants_to_targets: HashMap<F, Target>,
targets_to_constants: HashMap<Target, F>,
2021-08-13 14:28:05 +02:00
/// Memoized results of `arithmetic` calls.
pub(crate) base_arithmetic_results: HashMap<BaseArithmeticOperation<F>, Target>,
/// Memoized results of `arithmetic_extension` calls.
pub(crate) arithmetic_results: HashMap<ExtensionArithmeticOperation<F, D>, ExtensionTarget<D>>,
2021-11-10 10:54:35 -08:00
batched_gates: BatchedGates<F, D>,
2021-02-26 13:18:41 -08:00
}
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
2021-02-26 13:18:41 -08:00
pub fn new(config: CircuitConfig) -> Self {
let builder = CircuitBuilder {
2021-02-26 13:18:41 -08:00
config,
gates: HashSet::new(),
gate_instances: Vec::new(),
public_inputs: Vec::new(),
2021-04-07 22:21:45 -07:00
virtual_target_index: 0,
copy_constraints: Vec::new(),
Tree of scopes (#106) * Tree of scopes This is an extension of the context concept. Earlier I was planning to store a simple stack of contexts, but I ended up storing the whole history, in a tree structure. This gives us more control over the output, i.e. we can print the gate count of a parent scope before those of its child scopes, which seems more user-friendly. Sample gate count output: [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] 27829 gates to root [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | 2373 gates to evaluate the vanishing polynomial at our challenge point, zeta. [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | 1284 gates to evaluate gate constraints [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | 25312 gates to verify FRI proof [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | 650 gates to verify 0'th FRI query [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | | 96 gates to check FRI initial proof [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | | 65 gates to compute x from its index [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | | 233 gates to combine initial oracles ... Sample copy constraint failure: Error: Copy constraint 'root > verify FRI proof > verify 0'th FRI query > check FRI initial proof > verify 0'th initial Merkle proof > check Merkle root: 0-th hash element' between wire 12 of gate #2550 [...] and wire 0 of gate #0 [...] is not satisfied. Got values of 6861386743364621393 and 0 respectively. * No min * info -> debug * Move to its own file
2021-07-19 12:22:18 -07:00
context_log: ContextTree::new(),
2021-07-14 20:54:30 +02:00
marked_targets: Vec::new(),
2021-02-26 13:18:41 -08:00
generators: Vec::new(),
2021-04-21 11:47:18 -07:00
constants_to_targets: HashMap::new(),
base_arithmetic_results: HashMap::new(),
arithmetic_results: HashMap::new(),
2021-04-21 11:47:18 -07:00
targets_to_constants: HashMap::new(),
2021-11-10 09:56:21 -08:00
batched_gates: BatchedGates::new(),
};
builder.check_config();
builder
}
fn check_config(&self) {
let &CircuitConfig {
security_bits,
rate_bits,
fri_config:
FriConfig {
proof_of_work_bits,
num_query_rounds,
..
},
..
} = &self.config;
// Conjectured FRI security; see the ethSTARK paper.
let fri_field_bits = F::Extension::order().bits() as usize;
let fri_query_security_bits = num_query_rounds * rate_bits + proof_of_work_bits as usize;
let fri_security_bits = fri_field_bits.min(fri_query_security_bits);
assert!(
fri_security_bits >= security_bits,
"FRI params fall short of target security"
);
2021-02-26 13:18:41 -08:00
}
pub fn num_gates(&self) -> usize {
self.gate_instances.len()
}
/// Registers the given target as a public input.
pub fn register_public_input(&mut self, target: Target) {
self.public_inputs.push(target);
}
/// Registers the given targets as public inputs.
pub fn register_public_inputs(&mut self, targets: &[Target]) {
targets.iter().for_each(|&t| self.register_public_input(t));
}
/// Adds a new "virtual" target. This is not an actual wire in the witness, but just a target
/// that help facilitate witness generation. In particular, a generator can assign a values to a
/// virtual target, which can then be copied to other (virtual or concrete) targets. When we
/// generate the final witness (a grid of wire values), these virtual targets will go away.
pub fn add_virtual_target(&mut self) -> Target {
2021-04-07 22:21:45 -07:00
let index = self.virtual_target_index;
self.virtual_target_index += 1;
Target::VirtualTarget { index }
2021-04-07 22:21:45 -07:00
}
pub fn add_virtual_targets(&mut self, n: usize) -> Vec<Target> {
(0..n).map(|_i| self.add_virtual_target()).collect()
2021-04-07 22:21:45 -07:00
}
pub fn add_virtual_hash(&mut self) -> HashOutTarget {
HashOutTarget::from_vec(self.add_virtual_targets(4))
2021-07-08 17:16:26 +02:00
}
2021-08-10 13:33:44 +02:00
pub fn add_virtual_cap(&mut self, cap_height: usize) -> MerkleCapTarget {
MerkleCapTarget(self.add_virtual_hashes(1 << cap_height))
}
pub fn add_virtual_hashes(&mut self, n: usize) -> Vec<HashOutTarget> {
2021-07-08 17:16:26 +02:00
(0..n).map(|_i| self.add_virtual_hash()).collect()
}
pub fn add_virtual_extension_target(&mut self) -> ExtensionTarget<D> {
ExtensionTarget(self.add_virtual_targets(D).try_into().unwrap())
}
pub fn add_virtual_extension_targets(&mut self, n: usize) -> Vec<ExtensionTarget<D>> {
(0..n)
.map(|_i| self.add_virtual_extension_target())
.collect()
}
// TODO: Unsafe
pub fn add_virtual_bool_target(&mut self) -> BoolTarget {
BoolTarget::new_unsafe(self.add_virtual_target())
}
2021-02-26 13:18:41 -08:00
/// 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);
assert_eq!(
gate_type.num_constants(),
constants.len(),
"Number of constants doesn't match."
);
2021-02-26 13:18:41 -08:00
let index = self.gate_instances.len();
// Note that we can't immediately add this gate's generators, because the list of constants
// could be modified later, i.e. in the case of `ConstantGate`. We will add them later in
// `build` instead.
2021-04-02 15:29:21 -07:00
// Register this gate type if we haven't seen it before.
let gate_ref = GateRef::new(gate_type);
self.gates.insert(gate_ref.clone());
2021-04-02 15:29:21 -07:00
self.gate_instances.push(GateInstance {
gate_ref,
constants,
});
2021-10-25 15:49:44 -07:00
2021-02-26 13:18:41 -08:00
index
}
fn check_gate_compatibility<G: Gate<F, D>>(&self, gate: &G) {
assert!(
gate.num_wires() <= self.config.num_wires,
"{:?} requires {} wires, but our GateConfig has only {}",
gate.id(),
gate.num_wires(),
self.config.num_wires
);
2021-02-26 13:18:41 -08:00
}
2021-08-24 08:25:11 +02:00
pub fn connect_extension(&mut self, src: ExtensionTarget<D>, dst: ExtensionTarget<D>) {
2021-06-07 17:55:27 +02:00
for i in 0..D {
2021-08-24 08:25:11 +02:00
self.connect(src.0[i], dst.0[i]);
2021-06-07 17:55:27 +02:00
}
}
2021-02-26 13:18:41 -08:00
/// Adds a generator which will copy `src` to `dst`.
pub fn generate_copy(&mut self, src: Target, dst: Target) {
2021-09-02 15:03:03 -07:00
self.add_simple_generator(CopyGenerator { src, dst });
2021-02-26 13:18:41 -08:00
}
/// Uses Plonk's permutation argument to require that two elements be equal.
/// Both elements must be routable, otherwise this method will panic.
2021-08-24 08:25:11 +02:00
pub fn connect(&mut self, x: Target, y: Target) {
assert!(
2021-05-07 11:30:03 +02:00
x.is_routable(&self.config),
"Tried to route a wire that isn't routable"
);
assert!(
2021-05-07 11:30:03 +02:00
y.is_routable(&self.config),
"Tried to route a wire that isn't routable"
);
2021-07-14 20:54:30 +02:00
self.copy_constraints
Tree of scopes (#106) * Tree of scopes This is an extension of the context concept. Earlier I was planning to store a simple stack of contexts, but I ended up storing the whole history, in a tree structure. This gives us more control over the output, i.e. we can print the gate count of a parent scope before those of its child scopes, which seems more user-friendly. Sample gate count output: [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] 27829 gates to root [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | 2373 gates to evaluate the vanishing polynomial at our challenge point, zeta. [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | 1284 gates to evaluate gate constraints [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | 25312 gates to verify FRI proof [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | 650 gates to verify 0'th FRI query [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | | 96 gates to check FRI initial proof [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | | 65 gates to compute x from its index [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | | 233 gates to combine initial oracles ... Sample copy constraint failure: Error: Copy constraint 'root > verify FRI proof > verify 0'th FRI query > check FRI initial proof > verify 0'th initial Merkle proof > check Merkle root: 0-th hash element' between wire 12 of gate #2550 [...] and wire 0 of gate #0 [...] is not satisfied. Got values of 6861386743364621393 and 0 respectively. * No min * info -> debug * Move to its own file
2021-07-19 12:22:18 -07:00
.push(CopyConstraint::new((x, y), self.context_log.open_stack()));
2021-07-14 20:54:30 +02:00
}
2021-06-04 16:02:48 +02:00
pub fn assert_zero(&mut self, x: Target) {
let zero = self.zero();
2021-08-24 08:25:11 +02:00
self.connect(x, zero);
2021-06-04 16:02:48 +02:00
}
2021-10-18 15:04:54 -07:00
pub fn assert_one(&mut self, x: Target) {
let one = self.one();
self.connect(x, one);
}
2021-04-02 15:29:21 -07:00
pub fn add_generators(&mut self, generators: Vec<Box<dyn WitnessGenerator<F>>>) {
self.generators.extend(generators);
}
2021-09-02 15:03:03 -07:00
pub fn add_simple_generator<G: SimpleGenerator<F>>(&mut self, generator: G) {
self.generators.push(Box::new(generator.adapter()));
2021-02-26 13:18:41 -08:00
}
/// Returns a routable target with a value of 0.
pub fn zero(&mut self) -> Target {
self.constant(F::ZERO)
}
/// Returns a routable target with a value of 1.
pub fn one(&mut self) -> Target {
self.constant(F::ONE)
}
/// Returns a routable target with a value of 2.
pub fn two(&mut self) -> Target {
self.constant(F::TWO)
}
2021-07-21 13:05:32 -07:00
/// Returns a routable target with a value of `order() - 1`.
2021-02-26 13:18:41 -08:00
pub fn neg_one(&mut self) -> Target {
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())
}
2021-02-26 13:18:41 -08:00
/// Returns a routable target with the given constant value.
pub fn constant(&mut self, c: F) -> Target {
2021-04-21 11:47:18 -07:00
if let Some(&target) = self.constants_to_targets.get(&c) {
// We already have a wire for this constant.
return target;
}
2021-11-10 10:54:35 -08:00
let (gate, instance) = self.constant_gate_instance();
let target = Target::wire(gate, instance);
self.gate_instances[gate].constants[instance] = c;
2021-04-21 11:47:18 -07:00
self.constants_to_targets.insert(c, target);
self.targets_to_constants.insert(target, c);
2021-04-21 11:47:18 -07:00
target
2021-02-26 13:18:41 -08:00
}
2021-03-21 11:17:00 -07:00
2021-04-02 14:00:26 -07:00
pub fn constants(&mut self, constants: &[F]) -> Vec<Target> {
constants.iter().map(|&c| self.constant(c)).collect()
}
pub fn constant_bool(&mut self, b: bool) -> BoolTarget {
if b {
self._true()
} else {
self._false()
}
}
2021-11-10 09:53:27 -08:00
/// Returns a U32Target for the value `c`, which is assumed to be at most 32 bits.
2021-11-09 16:36:29 -08:00
pub fn constant_u32(&mut self, c: u32) -> U32Target {
U32Target(self.constant(F::from_canonical_u32(c)))
2021-11-10 09:53:27 -08:00
}
2021-04-21 11:47:18 -07:00
/// 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> {
self.targets_to_constants.get(&target).cloned()
}
/// If the given `ExtensionTarget` is a constant (i.e. it was created by the
/// `constant_extension(F)` method), returns its constant value. Otherwise, returns `None`.
pub fn target_as_constant_ext(&self, target: ExtensionTarget<D>) -> Option<F::Extension> {
// Get a Vec of any coefficients that are constant. If we end up with exactly D of them,
// then the `ExtensionTarget` as a whole is constant.
let const_coeffs: Vec<F> = target
.0
2021-08-02 10:58:03 -07:00
.iter()
.filter_map(|&t| self.target_as_constant(t))
.collect();
if let Ok(d_const_coeffs) = const_coeffs.try_into() {
Some(F::Extension::from_basefield_array(d_const_coeffs))
} else {
None
}
}
Reduce noise in FRI logging (#129) * Reduce noise in FRI logging Previously, all logs related to gate counts were at the `Debug` log level. This PR gives us more flexibility to adjust the log levels of particular scopes. In particular, our circuit checks 40 FRI queries, and we log a bunch of steps for each query, creating a lot of noise. With this change, we log just a single FRI query at the `Debug` level, and demote others to the `Trace` level. With `RUST_LOG=debug`, our logs now look like ``` [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] 17631 gates to root [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | 8 gates to observe proof and generates challenges [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | 4150 gates to evaluate the vanishing polynomial at our challenge point, zeta. [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | 3184 gates to evaluate gate constraints [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | 227 gates to evaluate InterpolationGate { num_points: 4, _phantom: PhantomData }<D=4> constraints [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | 867 gates to evaluate <R=101> GMiMCGate { ... } constraints [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | 576 gates to evaluate BaseSumGate { num_limbs: 63 } + Base: 2 constraints [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | 78 gates to evaluate ArithmeticExtensionGate constraints [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | 288 gates to evaluate BaseSumGate { num_limbs: 31 } + Base: 2 constraints [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | 115 gates to evaluate InsertionGate { vec_size: 3, _phantom: PhantomData }<D=4> constraints [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | 26 gates to evaluate BaseSumGate { num_limbs: 2 } + Base: 2 constraints [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | 633 gates to evaluate ReducingGate { num_coeffs: 21 } constraints [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | 4 gates to evaluate ConstantGate constraints [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | 11 gates to evaluate PublicInputGate constraints [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | 2 gates to evaluate NoopGate constraints [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | 16 gates to check vanishing and quotient polynomials. [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | 13336 gates to verify FRI proof [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | 6 gates to recover the random betas used in the FRI reductions. [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | 4 gates to check PoW [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | 104 gates to precompute reduced evaluations [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | 330 gates to verify one (of 40) query rounds [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | 95 gates to check FRI initial proof [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | | 22 gates to verify 0'th initial Merkle proof [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | | 33 gates to verify 1'th initial Merkle proof [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | | 20 gates to verify 2'th initial Merkle proof [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | | 20 gates to verify 3'th initial Merkle proof [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | 34 gates to compute x from its index [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | 32 gates to combine initial oracles [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | 17 gates to verify FRI round Merkle proof. [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | 10 gates to infer evaluation using interpolation [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | 15 gates to verify FRI round Merkle proof. [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | 10 gates to infer evaluation using interpolation [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | 13 gates to verify FRI round Merkle proof. [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | 10 gates to infer evaluation using interpolation [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | 11 gates to verify FRI round Merkle proof. [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | 10 gates to infer evaluation using interpolation [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | 9 gates to verify FRI round Merkle proof. [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | 10 gates to infer evaluation using interpolation [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | 7 gates to verify FRI round Merkle proof. [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | 10 gates to infer final evaluation using interpolation [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | | 8 gates to evaluate final polynomial ``` This bit corresponds to the single FRI query being shown: ``` [2021-07-26T21:07:45Z DEBUG plonky2::context_tree] | | 330 gates to verify one (of 40) query rounds ``` * Minor cleanup * Address feedback
2021-07-26 16:21:14 -07:00
pub fn push_context(&mut self, level: log::Level, ctx: &str) {
self.context_log.push(ctx, level, self.num_gates());
Tree of scopes (#106) * Tree of scopes This is an extension of the context concept. Earlier I was planning to store a simple stack of contexts, but I ended up storing the whole history, in a tree structure. This gives us more control over the output, i.e. we can print the gate count of a parent scope before those of its child scopes, which seems more user-friendly. Sample gate count output: [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] 27829 gates to root [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | 2373 gates to evaluate the vanishing polynomial at our challenge point, zeta. [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | 1284 gates to evaluate gate constraints [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | 25312 gates to verify FRI proof [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | 650 gates to verify 0'th FRI query [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | | 96 gates to check FRI initial proof [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | | 65 gates to compute x from its index [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | | 233 gates to combine initial oracles ... Sample copy constraint failure: Error: Copy constraint 'root > verify FRI proof > verify 0'th FRI query > check FRI initial proof > verify 0'th initial Merkle proof > check Merkle root: 0-th hash element' between wire 12 of gate #2550 [...] and wire 0 of gate #0 [...] is not satisfied. Got values of 6861386743364621393 and 0 respectively. * No min * info -> debug * Move to its own file
2021-07-19 12:22:18 -07:00
}
pub fn pop_context(&mut self) {
self.context_log.pop(self.num_gates());
2021-07-14 20:54:30 +02:00
}
2021-07-15 09:52:42 +02:00
pub fn add_marked(&mut self, targets: Markable<D>, name: &str) {
2021-07-14 20:54:30 +02:00
self.marked_targets.push(MarkedTargets {
targets,
name: name.to_string(),
})
}
fn fri_params(&self, degree_bits_estimate: usize) -> FriParams {
let fri_config = &self.config.fri_config;
let reduction_arity_bits = fri_config.reduction_strategy.reduction_arity_bits(
degree_bits_estimate,
self.config.rate_bits,
fri_config.num_query_rounds,
);
FriParams {
reduction_arity_bits,
}
}
/// The number of (base field) `arithmetic` operations that can be performed in a single gate.
pub(crate) fn num_base_arithmetic_ops_per_gate(&self) -> usize {
if self.config.use_base_arithmetic_gate {
ArithmeticGate::new_from_config(&self.config).num_ops
} else {
self.num_ext_arithmetic_ops_per_gate()
}
}
/// The number of `arithmetic_extension` operations that can be performed in a single gate.
pub(crate) fn num_ext_arithmetic_ops_per_gate(&self) -> usize {
ArithmeticExtensionGate::<D>::new_from_config(&self.config).num_ops
}
2021-06-28 23:08:24 -07:00
/// The number of polynomial values that will be revealed per opening, both for the "regular"
/// polynomials and for the Z polynomials. Because calculating these values involves a recursive
/// dependence (the amount of blinding depends on the degree, which depends on the blinding),
/// this function takes in an estimate of the degree.
2021-06-28 09:45:56 -07:00
fn num_blinding_gates(&self, degree_estimate: usize) -> (usize, usize) {
let degree_bits_estimate = log2_strict(degree_estimate);
let fri_queries = self.config.fri_config.num_query_rounds;
2021-06-28 17:07:27 -07:00
let arities: Vec<usize> = self
.fri_params(degree_bits_estimate)
2021-06-28 17:07:27 -07:00
.reduction_arity_bits
.iter()
.map(|x| 1 << x)
.collect();
let total_fri_folding_points: usize = arities.iter().map(|x| x - 1).sum::<usize>();
2021-06-29 11:04:05 -07:00
let final_poly_coeffs: usize = degree_estimate / arities.iter().product::<usize>();
2021-06-28 23:08:24 -07:00
let fri_openings = fri_queries * (1 + D * total_fri_folding_points + D * final_poly_coeffs);
// We add D for openings at zeta.
let regular_poly_openings = D + fri_openings;
// We add 2 * D for openings at zeta and g * zeta.
let z_openings = 2 * D + fri_openings;
2021-06-28 09:45:56 -07:00
(regular_poly_openings, z_openings)
}
2021-06-28 23:08:24 -07:00
/// The number of polynomial values that will be revealed per opening, both for the "regular"
/// polynomials (which are opened at only one location) and for the Z polynomials (which are
/// opened at two).
2021-06-28 09:45:56 -07:00
fn blinding_counts(&self) -> (usize, usize) {
let num_gates = self.gate_instances.len();
let mut degree_estimate = 1 << log2_ceil(num_gates);
loop {
2021-06-28 09:45:56 -07:00
let (regular_poly_openings, z_openings) = self.num_blinding_gates(degree_estimate);
2021-06-28 09:45:56 -07:00
// For most polynomials, we add one random element to offset each opened value.
// But blinding Z is separate. For that, we add two random elements with a copy
// constraint between them.
let total_blinding_count = regular_poly_openings + 2 * z_openings;
if num_gates + total_blinding_count <= degree_estimate {
return (regular_poly_openings, z_openings);
}
// The blinding gates do not fit within our estimated degree; increase our estimate.
degree_estimate *= 2;
}
}
2021-07-15 10:13:13 +02:00
fn blind_and_pad(&mut self) {
if self.config.zero_knowledge {
self.blind();
}
while !self.gate_instances.len().is_power_of_two() {
self.add_gate(NoopGate, vec![]);
}
}
fn blind(&mut self) {
2021-06-28 09:45:56 -07:00
let (regular_poly_openings, z_openings) = self.blinding_counts();
info!(
"Adding {} blinding terms for witness polynomials, and {}*2 for Z polynomials",
regular_poly_openings, z_openings
);
2021-06-28 09:45:56 -07:00
let num_routed_wires = self.config.num_routed_wires;
let num_wires = self.config.num_wires;
2021-06-28 23:08:24 -07:00
// For each "regular" blinding factor, we simply add a no-op gate, and insert a random value
// for each wire.
2021-06-28 17:07:09 -07:00
for _ in 0..regular_poly_openings {
let gate = self.add_gate(NoopGate, vec![]);
2021-06-28 09:45:56 -07:00
for w in 0..num_wires {
2021-09-02 15:03:03 -07:00
self.add_simple_generator(RandomValueGenerator {
2021-06-28 09:45:56 -07:00
target: Target::Wire(Wire { gate, input: w }),
});
}
}
2021-06-28 23:08:24 -07:00
// For each z poly blinding factor, we add two new gates with the same random value, and
// enforce a copy constraint between them.
// See https://mirprotocol.org/blog/Adding-zero-knowledge-to-Plonk-Halo
2021-06-28 17:07:09 -07:00
for _ in 0..z_openings {
let gate_1 = self.add_gate(NoopGate, vec![]);
let gate_2 = self.add_gate(NoopGate, vec![]);
2021-06-28 09:45:56 -07:00
2021-06-28 17:07:09 -07:00
for w in 0..num_routed_wires {
2021-09-02 15:03:03 -07:00
self.add_simple_generator(RandomValueGenerator {
2021-06-28 17:07:27 -07:00
target: Target::Wire(Wire {
gate: gate_1,
input: w,
}),
2021-06-28 09:45:56 -07:00
});
2021-06-30 16:58:06 -07:00
self.generate_copy(
Target::Wire(Wire {
2021-06-28 17:07:27 -07:00
gate: gate_1,
input: w,
}),
2021-06-30 16:58:06 -07:00
Target::Wire(Wire {
2021-06-28 17:07:27 -07:00
gate: gate_2,
input: w,
}),
2021-06-30 16:58:06 -07:00
);
2021-06-28 09:45:56 -07:00
}
}
2021-03-25 15:20:14 -07:00
}
2021-07-15 10:13:13 +02:00
fn constant_polys(
&self,
gates: &[PrefixedGate<F, D>],
num_constants: usize,
) -> Vec<PolynomialValues<F>> {
let constants_per_gate = self
.gate_instances
.iter()
.map(|gate| {
let prefix = &gates
.iter()
.find(|g| g.gate.0.id() == gate.gate_ref.0.id())
.unwrap()
.prefix;
2021-06-24 20:53:15 +02:00
let mut prefixed_constants = Vec::with_capacity(num_constants);
prefixed_constants.extend(prefix.iter().map(|&b| if b { F::ONE } else { F::ZERO }));
prefixed_constants.extend_from_slice(&gate.constants);
2021-06-22 16:54:20 +02:00
prefixed_constants.resize(num_constants, F::ZERO);
prefixed_constants
2021-03-25 15:20:14 -07:00
})
.collect::<Vec<_>>();
2021-03-30 13:30:31 -07:00
2021-03-25 15:20:14 -07:00
transpose(&constants_per_gate)
2021-03-30 13:30:31 -07:00
.into_iter()
.map(PolynomialValues::new)
.collect()
2021-03-25 15:20:14 -07:00
}
fn sigma_vecs(&self, k_is: &[F], subgroup: &[F]) -> (Vec<PolynomialValues<F>>, Forest) {
let degree = self.gate_instances.len();
let degree_log = log2_strict(degree);
let config = &self.config;
let mut forest = Forest::new(
config.num_wires,
config.num_routed_wires,
2021-08-22 10:36:44 +02:00
degree,
self.virtual_target_index,
);
for gate in 0..degree {
for input in 0..config.num_wires {
forest.add(Target::Wire(Wire { gate, input }));
}
}
for index in 0..self.virtual_target_index {
forest.add(Target::VirtualTarget { index });
}
2021-07-14 20:54:30 +02:00
for &CopyConstraint { pair: (a, b), .. } in &self.copy_constraints {
forest.merge(a, b);
}
forest.compress_paths();
let wire_partition = forest.wire_partition();
2021-08-19 14:54:11 +02:00
(
wire_partition.get_sigma_polys(degree_log, k_is, subgroup),
forest,
2021-08-19 14:54:11 +02:00
)
2021-03-25 15:20:14 -07:00
}
Tree of scopes (#106) * Tree of scopes This is an extension of the context concept. Earlier I was planning to store a simple stack of contexts, but I ended up storing the whole history, in a tree structure. This gives us more control over the output, i.e. we can print the gate count of a parent scope before those of its child scopes, which seems more user-friendly. Sample gate count output: [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] 27829 gates to root [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | 2373 gates to evaluate the vanishing polynomial at our challenge point, zeta. [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | 1284 gates to evaluate gate constraints [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | 25312 gates to verify FRI proof [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | 650 gates to verify 0'th FRI query [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | | 96 gates to check FRI initial proof [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | | 65 gates to compute x from its index [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | | 233 gates to combine initial oracles ... Sample copy constraint failure: Error: Copy constraint 'root > verify FRI proof > verify 0'th FRI query > check FRI initial proof > verify 0'th initial Merkle proof > check Merkle root: 0-th hash element' between wire 12 of gate #2550 [...] and wire 0 of gate #0 [...] is not satisfied. Got values of 6861386743364621393 and 0 respectively. * No min * info -> debug * Move to its own file
2021-07-19 12:22:18 -07:00
pub fn print_gate_counts(&self, min_delta: usize) {
2021-10-25 09:12:42 -07:00
// Print gate counts for each context.
Tree of scopes (#106) * Tree of scopes This is an extension of the context concept. Earlier I was planning to store a simple stack of contexts, but I ended up storing the whole history, in a tree structure. This gives us more control over the output, i.e. we can print the gate count of a parent scope before those of its child scopes, which seems more user-friendly. Sample gate count output: [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] 27829 gates to root [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | 2373 gates to evaluate the vanishing polynomial at our challenge point, zeta. [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | 1284 gates to evaluate gate constraints [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | 25312 gates to verify FRI proof [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | 650 gates to verify 0'th FRI query [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | | 96 gates to check FRI initial proof [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | | 65 gates to compute x from its index [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | | 233 gates to combine initial oracles ... Sample copy constraint failure: Error: Copy constraint 'root > verify FRI proof > verify 0'th FRI query > check FRI initial proof > verify 0'th initial Merkle proof > check Merkle root: 0-th hash element' between wire 12 of gate #2550 [...] and wire 0 of gate #0 [...] is not satisfied. Got values of 6861386743364621393 and 0 respectively. * No min * info -> debug * Move to its own file
2021-07-19 12:22:18 -07:00
self.context_log
.filter(self.num_gates(), min_delta)
.print(self.num_gates());
2021-10-25 09:12:42 -07:00
// Print total count of each gate type.
debug!("Total gate counts:");
for gate in self.gates.iter().cloned() {
let count = self
.gate_instances
.iter()
.filter(|inst| inst.gate_ref == gate)
.count();
debug!("- {} instances of {}", count, gate.0.id());
}
Tree of scopes (#106) * Tree of scopes This is an extension of the context concept. Earlier I was planning to store a simple stack of contexts, but I ended up storing the whole history, in a tree structure. This gives us more control over the output, i.e. we can print the gate count of a parent scope before those of its child scopes, which seems more user-friendly. Sample gate count output: [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] 27829 gates to root [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | 2373 gates to evaluate the vanishing polynomial at our challenge point, zeta. [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | 1284 gates to evaluate gate constraints [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | 25312 gates to verify FRI proof [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | 650 gates to verify 0'th FRI query [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | | 96 gates to check FRI initial proof [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | | 65 gates to compute x from its index [2021-07-19T18:09:24Z INFO plonky2::circuit_builder] | | | 233 gates to combine initial oracles ... Sample copy constraint failure: Error: Copy constraint 'root > verify FRI proof > verify 0'th FRI query > check FRI initial proof > verify 0'th initial Merkle proof > check Merkle root: 0-th hash element' between wire 12 of gate #2550 [...] and wire 0 of gate #0 [...] is not satisfied. Got values of 6861386743364621393 and 0 respectively. * No min * info -> debug * Move to its own file
2021-07-19 12:22:18 -07:00
}
2021-03-21 11:17:00 -07:00
/// Builds a "full circuit", with both prover and verifier data.
pub fn build(mut self) -> CircuitData<F, D> {
let mut timing = TimingTree::new("preprocess", Level::Trace);
2021-03-25 15:20:14 -07:00
let start = Instant::now();
2021-11-10 09:58:14 -08:00
self.fill_batched_gates();
2021-08-17 09:12:40 +02:00
// Hash the public inputs, and route them to a `PublicInputGate` which will enforce that
// those hash wires match the claimed public inputs.
let public_inputs_hash = self.hash_n_to_hash(self.public_inputs.clone(), true);
let pi_gate = self.add_gate(PublicInputGate, vec![]);
for (&hash_part, wire) in public_inputs_hash
.elements
.iter()
.zip(PublicInputGate::wires_public_inputs_hash())
{
2021-08-24 08:25:11 +02:00
self.connect(hash_part, Target::wire(pi_gate, wire))
}
info!(
"Degree before blinding & padding: {}",
self.gate_instances.len()
);
2021-03-25 15:20:14 -07:00
self.blind_and_pad();
let degree = self.gate_instances.len();
info!("Degree after blinding & padding: {}", degree);
2021-07-19 16:24:21 +02:00
let degree_bits = log2_strict(degree);
let fri_params = self.fri_params(degree_bits);
2021-07-19 16:24:21 +02:00
assert!(
fri_params.total_arities() <= degree_bits,
"FRI total reduction arity is too large.",
2021-07-19 16:24:21 +02:00
);
2021-03-25 15:20:14 -07:00
2021-06-22 16:54:20 +02:00
let gates = self.gates.iter().cloned().collect();
let (gate_tree, max_filtered_constraint_degree, num_constants) = Tree::from_gates(gates);
2021-08-09 19:08:52 +02:00
// `quotient_degree_factor` has to be between `max_filtered_constraint_degree-1` and `1<<rate_bits`.
// We find the value that minimizes `num_partial_product + quotient_degree_factor`.
let quotient_degree_factor = (max_filtered_constraint_degree - 1
..=1 << self.config.rate_bits)
.min_by_key(|&q| num_partial_products(self.config.num_routed_wires, q).0 + q)
.unwrap();
2021-11-16 22:53:08 -08:00
debug!("Quotient degree factor set to: {}.", quotient_degree_factor);
let prefixed_gates = PrefixedGate::from_tree(gate_tree);
2021-06-22 16:54:20 +02:00
2021-06-16 17:43:41 +02:00
let subgroup = F::two_adic_subgroup(degree_bits);
let constant_vecs = self.constant_polys(&prefixed_gates, num_constants);
2021-03-25 15:20:14 -07:00
let k_is = get_unique_coset_shifts(degree, self.config.num_routed_wires);
let (sigma_vecs, forest) = self.sigma_vecs(&k_is, &subgroup);
2021-09-22 10:56:09 -07:00
// Precompute FFT roots.
let max_fft_points =
1 << degree_bits + max(self.config.rate_bits, log2_ceil(quotient_degree_factor));
let fft_root_table = fft_root_table(max_fft_points);
2021-06-28 08:56:36 -07:00
let constants_sigmas_vecs = [constant_vecs, sigma_vecs.clone()].concat();
let constants_sigmas_commitment = PolynomialBatchCommitment::from_values(
2021-06-25 11:24:26 +02:00
constants_sigmas_vecs,
self.config.rate_bits,
self.config.zero_knowledge & PlonkPolynomials::CONSTANTS_SIGMAS.blinding,
2021-08-10 13:33:44 +02:00
self.config.cap_height,
&mut timing,
2021-09-22 10:56:09 -07:00
Some(&fft_root_table),
2021-05-07 11:30:03 +02:00
);
2021-08-10 15:53:27 +02:00
let constants_sigmas_cap = constants_sigmas_commitment.merkle_tree.cap.clone();
let verifier_only = VerifierOnlyCircuitData {
2021-08-10 15:53:27 +02:00
constants_sigmas_cap: constants_sigmas_cap.clone(),
};
2021-03-25 15:20:14 -07:00
// Add gate generators.
self.add_generators(
self.gate_instances
.iter()
.enumerate()
.flat_map(|(index, gate)| gate.gate_ref.0.generators(index, &gate.constants))
.collect(),
);
// Index generator indices by their watched targets.
let mut generator_indices_by_watches = BTreeMap::new();
for (i, generator) in self.generators.iter().enumerate() {
for watch in generator.watch_list() {
let watch_index = forest.target_index(watch);
let watch_rep_index = forest.parents[watch_index];
generator_indices_by_watches
.entry(watch_rep_index)
.or_insert(vec![])
.push(i);
}
}
for indices in generator_indices_by_watches.values_mut() {
indices.dedup();
indices.shrink_to_fit();
}
let prover_only = ProverOnlyCircuitData {
generators: self.generators,
generator_indices_by_watches,
2021-06-25 11:24:26 +02:00
constants_sigmas_commitment,
2021-06-28 08:56:36 -07:00
sigmas: transpose_poly_values(sigma_vecs),
2021-06-16 17:43:41 +02:00
subgroup,
public_inputs: self.public_inputs,
2021-07-14 20:54:30 +02:00
marked_targets: self.marked_targets,
representative_map: forest.parents,
2021-09-22 10:56:09 -07:00
fft_root_table: Some(fft_root_table),
};
2021-03-25 15:20:14 -07:00
// The HashSet of gates will have a non-deterministic order. When converting to a Vec, we
// sort by ID to make the ordering deterministic.
let mut gates = self.gates.iter().cloned().collect::<Vec<_>>();
gates.sort_unstable_by_key(|gate| gate.0.id());
let num_gate_constraints = gates
.iter()
.map(|gate| gate.0.num_constraints())
.max()
.expect("No gates?");
2021-07-08 15:13:29 +02:00
let num_partial_products =
num_partial_products(self.config.num_routed_wires, quotient_degree_factor);
2021-07-01 15:41:01 +02:00
// TODO: This should also include an encoding of gate constraints.
2021-06-25 11:49:29 +02:00
let circuit_digest_parts = [
2021-08-14 08:47:03 -07:00
constants_sigmas_cap.flatten(),
2021-06-25 11:49:29 +02:00
vec![/* Add other circuit data here */],
];
let circuit_digest = hash_n_to_hash(circuit_digest_parts.concat(), false);
2021-03-25 15:20:14 -07:00
let common = CommonCircuitData {
config: self.config,
fri_params,
degree_bits,
gates: prefixed_gates,
quotient_degree_factor,
num_gate_constraints,
2021-06-25 11:24:26 +02:00
num_constants,
num_virtual_targets: self.virtual_target_index,
2021-03-30 23:12:47 -07:00
k_is,
2021-07-01 15:41:01 +02:00
num_partial_products,
circuit_digest,
2021-03-25 15:20:14 -07:00
};
2021-11-16 22:53:08 -08:00
debug!("Building circuit took {}s", start.elapsed().as_secs_f32());
2021-03-25 15:20:14 -07:00
CircuitData {
prover_only,
verifier_only,
common,
}
2021-03-21 11:17:00 -07:00
}
/// Builds a "prover circuit", with data needed to generate proofs but not verify them.
pub fn build_prover(self) -> ProverCircuitData<F, D> {
2021-03-25 15:20:14 -07:00
// TODO: Can skip parts of this.
let CircuitData {
prover_only,
common,
..
} = self.build();
ProverCircuitData {
prover_only,
common,
}
2021-03-21 11:17:00 -07:00
}
/// Builds a "verifier circuit", with data needed to verify proofs but not generate them.
pub fn build_verifier(self) -> VerifierCircuitData<F, D> {
2021-03-25 15:20:14 -07:00
// TODO: Can skip parts of this.
let CircuitData {
verifier_only,
common,
..
} = self.build();
VerifierCircuitData {
verifier_only,
common,
}
2021-03-21 11:17:00 -07:00
}
2021-02-26 13:18:41 -08:00
}
2021-11-10 09:56:21 -08:00
/// Various gate types can contain multiple copies in a single Gate. This helper struct lets a
/// CircuitBuilder track such gates that are currently being "filled up."
2021-11-10 09:56:21 -08:00
pub struct BatchedGates<F: RichField + Extendable<D>, const D: usize> {
/// A map `(c0, c1) -> (g, i)` from constants `(c0,c1)` to an available arithmetic gate using
/// these constants with gate index `g` and already using `i` arithmetic operations.
pub(crate) free_arithmetic: HashMap<(F, F), (usize, usize)>,
pub(crate) free_base_arithmetic: HashMap<(F, F), (usize, usize)>,
2021-11-10 09:56:21 -08:00
/// A map `b -> (g, i)` from `b` bits to an available random access gate of that size with gate
/// index `g` and already using `i` random accesses.
2021-11-10 10:54:35 -08:00
pub(crate) free_random_access: HashMap<usize, (usize, usize)>,
2021-11-10 09:56:21 -08:00
/// `current_switch_gates[chunk_size - 1]` contains None if we have no switch gates with the value
/// chunk_size, and contains `(g, i, c)`, if the gate `g`, at index `i`, already contains `c` copies
/// of switches
pub(crate) current_switch_gates: Vec<Option<(SwitchGate<F, D>, usize, usize)>>,
/// The `U32ArithmeticGate` currently being filled (so new u32 arithmetic operations will be added to this gate before creating a new one)
pub(crate) current_u32_arithmetic_gate: Option<(usize, usize)>,
/// The `U32SubtractionGate` currently being filled (so new u32 subtraction operations will be added to this gate before creating a new one)
pub(crate) current_u32_subtraction_gate: Option<(usize, usize)>,
/// An available `ConstantGate` instance, if any.
pub(crate) free_constant: Option<(usize, usize)>,
}
impl<F: RichField + Extendable<D>, const D: usize> BatchedGates<F, D> {
pub fn new() -> Self {
Self {
free_arithmetic: HashMap::new(),
free_base_arithmetic: HashMap::new(),
2021-11-10 10:54:35 -08:00
free_random_access: HashMap::new(),
2021-11-10 09:56:21 -08:00
current_switch_gates: Vec::new(),
current_u32_arithmetic_gate: None,
current_u32_subtraction_gate: None,
free_constant: None,
}
}
}
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
/// Finds the last available arithmetic gate with the given constants or add one if there aren't any.
/// Returns `(g,i)` such that there is an arithmetic gate with the given constants at index
/// `g` and the gate's `i`-th operation is available.
pub(crate) fn find_base_arithmetic_gate(&mut self, const_0: F, const_1: F) -> (usize, usize) {
let (gate, i) = self
.batched_gates
.free_base_arithmetic
.get(&(const_0, const_1))
.copied()
.unwrap_or_else(|| {
let gate = self.add_gate(
ArithmeticGate::new_from_config(&self.config),
vec![const_0, const_1],
);
(gate, 0)
});
// Update `free_arithmetic` with new values.
if i < ArithmeticGate::num_ops(&self.config) - 1 {
self.batched_gates
.free_base_arithmetic
.insert((const_0, const_1), (gate, i + 1));
} else {
self.batched_gates
.free_base_arithmetic
.remove(&(const_0, const_1));
}
(gate, i)
}
2021-11-10 09:56:21 -08:00
/// Finds the last available arithmetic gate with the given constants or add one if there aren't any.
/// Returns `(g,i)` such that there is an arithmetic gate with the given constants at index
/// `g` and the gate's `i`-th operation is available.
2021-11-10 09:57:32 -08:00
pub(crate) fn find_arithmetic_gate(&mut self, const_0: F, const_1: F) -> (usize, usize) {
2021-11-10 09:56:21 -08:00
let (gate, i) = self
.batched_gates
.free_arithmetic
.get(&(const_0, const_1))
.copied()
.unwrap_or_else(|| {
let gate = self.add_gate(
ArithmeticExtensionGate::new_from_config(&self.config),
vec![const_0, const_1],
);
(gate, 0)
});
// Update `free_arithmetic` with new values.
if i < ArithmeticExtensionGate::<D>::num_ops(&self.config) - 1 {
self.batched_gates
.free_arithmetic
.insert((const_0, const_1), (gate, i + 1));
} else {
2021-11-10 09:56:42 -08:00
self.batched_gates
.free_arithmetic
.remove(&(const_0, const_1));
2021-11-10 09:56:21 -08:00
}
(gate, i)
}
2021-11-10 10:54:35 -08:00
/// Finds the last available random access gate with the given `vec_size` or add one if there aren't any.
/// Returns `(g,i)` such that there is a random access gate with the given `vec_size` at index
/// `g` and the gate's `i`-th random access is available.
pub(crate) fn find_random_access_gate(&mut self, bits: usize) -> (usize, usize) {
2021-11-10 10:54:35 -08:00
let (gate, i) = self
.batched_gates
.free_random_access
.get(&bits)
2021-11-10 10:54:35 -08:00
.copied()
.unwrap_or_else(|| {
let gate = self.add_gate(
RandomAccessGate::new_from_config(&self.config, bits),
2021-11-10 10:54:35 -08:00
vec![],
);
(gate, 0)
});
// Update `free_random_access` with new values.
if i + 1 < RandomAccessGate::<F, D>::new_from_config(&self.config, bits).num_copies {
2021-11-10 10:54:35 -08:00
self.batched_gates
.free_random_access
.insert(bits, (gate, i + 1));
2021-11-10 10:54:35 -08:00
} else {
self.batched_gates.free_random_access.remove(&bits);
2021-11-10 10:54:35 -08:00
}
(gate, i)
}
2021-11-10 09:57:32 -08:00
pub(crate) fn find_switch_gate(
&mut self,
chunk_size: usize,
) -> (SwitchGate<F, D>, usize, usize) {
2021-11-10 09:56:21 -08:00
if self.batched_gates.current_switch_gates.len() < chunk_size {
2021-11-10 09:56:42 -08:00
self.batched_gates.current_switch_gates.extend(vec![
None;
2021-11-10 09:57:32 -08:00
chunk_size
- self
.batched_gates
.current_switch_gates
.len()
2021-11-10 09:56:42 -08:00
]);
2021-11-10 09:56:21 -08:00
}
2021-10-29 16:03:58 -07:00
let (gate, gate_index, next_copy) =
2021-11-10 09:56:42 -08:00
match self.batched_gates.current_switch_gates[chunk_size - 1].clone() {
2021-11-10 09:56:21 -08:00
None => {
2021-11-10 10:54:35 -08:00
let gate = SwitchGate::<F, D>::new_from_config(&self.config, chunk_size);
2021-11-10 09:56:21 -08:00
let gate_index = self.add_gate(gate.clone(), vec![]);
(gate, gate_index, 0)
}
Some((gate, idx, next_copy)) => (gate, idx, next_copy),
};
2021-11-10 09:56:42 -08:00
2021-11-10 09:56:21 -08:00
let num_copies = gate.num_copies;
2021-11-10 09:56:42 -08:00
2021-10-29 16:03:58 -07:00
if next_copy == num_copies - 1 {
2021-11-10 09:56:21 -08:00
self.batched_gates.current_switch_gates[chunk_size - 1] = None;
} else {
2021-11-10 09:56:42 -08:00
self.batched_gates.current_switch_gates[chunk_size - 1] =
2021-11-10 09:57:32 -08:00
Some((gate.clone(), gate_index, next_copy + 1));
2021-11-10 09:56:21 -08:00
}
(gate, gate_index, next_copy)
}
2021-11-10 09:57:32 -08:00
pub(crate) fn find_u32_arithmetic_gate(&mut self) -> (usize, usize) {
2021-11-10 09:56:21 -08:00
let (gate_index, copy) = match self.batched_gates.current_u32_arithmetic_gate {
None => {
let gate = U32ArithmeticGate::new();
let gate_index = self.add_gate(gate, vec![]);
(gate_index, 0)
}
Some((gate_index, copy)) => (gate_index, copy),
};
if copy == NUM_U32_ARITHMETIC_OPS - 1 {
2021-11-10 09:56:42 -08:00
self.batched_gates.current_u32_arithmetic_gate = None;
2021-11-10 09:56:21 -08:00
} else {
2021-11-10 09:56:42 -08:00
self.batched_gates.current_u32_arithmetic_gate = Some((gate_index, copy + 1));
2021-11-10 09:56:21 -08:00
}
(gate_index, copy)
}
2021-11-10 09:57:32 -08:00
pub(crate) fn find_u32_subtraction_gate(&mut self) -> (usize, usize) {
2021-11-10 09:56:21 -08:00
let (gate_index, copy) = match self.batched_gates.current_u32_subtraction_gate {
None => {
let gate = U32SubtractionGate::new();
let gate_index = self.add_gate(gate, vec![]);
(gate_index, 0)
}
Some((gate_index, copy)) => (gate_index, copy),
};
if copy == NUM_U32_SUBTRACTION_OPS - 1 {
2021-11-10 09:56:42 -08:00
self.batched_gates.current_u32_subtraction_gate = None;
2021-11-10 09:56:21 -08:00
} else {
2021-11-10 09:56:42 -08:00
self.batched_gates.current_u32_subtraction_gate = Some((gate_index, copy + 1));
2021-11-10 09:56:21 -08:00
}
(gate_index, copy)
}
/// Returns the gate index and copy index of a free `ConstantGate` slot, potentially adding a
/// new `ConstantGate` if needed.
fn constant_gate_instance(&mut self) -> (usize, usize) {
2021-11-10 10:54:35 -08:00
if self.batched_gates.free_constant.is_none() {
2021-11-10 09:56:21 -08:00
let num_consts = self.config.constant_gate_size;
// We will fill this `ConstantGate` with zero constants initially.
// These will be overwritten by `constant` as the gate instances are filled.
let gate = self.add_gate(ConstantGate { num_consts }, vec![F::ZERO; num_consts]);
2021-11-10 10:54:35 -08:00
self.batched_gates.free_constant = Some((gate, 0));
2021-11-10 09:56:21 -08:00
}
2021-11-10 10:54:35 -08:00
let (gate, instance) = self.batched_gates.free_constant.unwrap();
2021-11-10 09:56:21 -08:00
if instance + 1 < self.config.constant_gate_size {
2021-11-10 10:54:35 -08:00
self.batched_gates.free_constant = Some((gate, instance + 1));
2021-11-10 09:56:21 -08:00
} else {
2021-11-10 10:54:35 -08:00
self.batched_gates.free_constant = None;
2021-11-10 09:56:21 -08:00
}
(gate, instance)
}
2021-11-10 10:54:35 -08:00
/// Fill the remaining unused arithmetic operations with zeros, so that all
/// `ArithmeticGate` are run.
fn fill_base_arithmetic_gates(&mut self) {
let zero = self.zero();
for ((c0, c1), (_gate, i)) in self.batched_gates.free_base_arithmetic.clone() {
for _ in i..ArithmeticGate::num_ops(&self.config) {
// If we directly wire in zero, an optimization will skip doing anything and return
// zero. So we pass in a virtual target and connect it to zero afterward.
let dummy = self.add_virtual_target();
self.arithmetic(c0, c1, dummy, dummy, dummy);
self.connect(dummy, zero);
}
}
assert!(self.batched_gates.free_base_arithmetic.is_empty());
}
2021-11-10 09:57:32 -08:00
/// Fill the remaining unused arithmetic operations with zeros, so that all
/// `ArithmeticExtensionGenerator`s are run.
fn fill_arithmetic_gates(&mut self) {
let zero = self.zero_extension();
for ((c0, c1), (_gate, i)) in self.batched_gates.free_arithmetic.clone() {
for _ in i..ArithmeticExtensionGate::<D>::num_ops(&self.config) {
// If we directly wire in zero, an optimization will skip doing anything and return
// zero. So we pass in a virtual target and connect it to zero afterward.
let dummy = self.add_virtual_extension_target();
self.arithmetic_extension(c0, c1, dummy, dummy, dummy);
self.connect_extension(dummy, zero);
2021-11-10 09:57:32 -08:00
}
}
assert!(self.batched_gates.free_arithmetic.is_empty());
2021-11-10 09:57:32 -08:00
}
2021-11-10 12:10:32 -08:00
/// Fill the remaining unused random access operations with zeros, so that all
/// `RandomAccessGenerator`s are run.
fn fill_random_access_gates(&mut self) {
let zero = self.zero();
for (bits, (_, i)) in self.batched_gates.free_random_access.clone() {
let max_copies =
RandomAccessGate::<F, D>::new_from_config(&self.config, bits).num_copies;
2021-11-10 12:10:32 -08:00
for _ in i..max_copies {
self.random_access(zero, zero, vec![zero; 1 << bits]);
2021-11-10 12:10:32 -08:00
}
}
}
2021-11-10 09:57:32 -08:00
/// Fill the remaining unused switch gates with dummy values, so that all
/// `SwitchGenerator`s are run.
fn fill_switch_gates(&mut self) {
let zero = self.zero();
for chunk_size in 1..=self.batched_gates.current_switch_gates.len() {
if let Some((gate, gate_index, mut copy)) =
self.batched_gates.current_switch_gates[chunk_size - 1].clone()
{
while copy < gate.num_copies {
for element in 0..chunk_size {
let wire_first_input =
Target::wire(gate_index, gate.wire_first_input(copy, element));
let wire_second_input =
Target::wire(gate_index, gate.wire_second_input(copy, element));
let wire_switch_bool =
Target::wire(gate_index, gate.wire_switch_bool(copy));
self.connect(zero, wire_first_input);
self.connect(zero, wire_second_input);
self.connect(zero, wire_switch_bool);
}
copy += 1;
}
}
}
}
/// Fill the remaining unused U32 arithmetic operations with zeros, so that all
/// `U32ArithmeticGenerator`s are run.
fn fill_u32_arithmetic_gates(&mut self) {
let zero = self.zero();
if let Some((gate_index, copy)) = self.batched_gates.current_u32_arithmetic_gate {
for i in copy..NUM_U32_ARITHMETIC_OPS {
let wire_multiplicand_0 = Target::wire(
gate_index,
U32ArithmeticGate::<F, D>::wire_ith_multiplicand_0(i),
);
let wire_multiplicand_1 = Target::wire(
gate_index,
U32ArithmeticGate::<F, D>::wire_ith_multiplicand_1(i),
);
let wire_addend =
Target::wire(gate_index, U32ArithmeticGate::<F, D>::wire_ith_addend(i));
self.connect(zero, wire_multiplicand_0);
self.connect(zero, wire_multiplicand_1);
self.connect(zero, wire_addend);
}
}
}
/// Fill the remaining unused U32 subtraction operations with zeros, so that all
/// `U32SubtractionGenerator`s are run.
fn fill_u32_subtraction_gates(&mut self) {
let zero = self.zero();
if let Some((gate_index, copy)) = self.batched_gates.current_u32_subtraction_gate {
for i in copy..NUM_U32_ARITHMETIC_OPS {
let wire_input_x =
Target::wire(gate_index, U32SubtractionGate::<F, D>::wire_ith_input_x(i));
let wire_input_y =
Target::wire(gate_index, U32SubtractionGate::<F, D>::wire_ith_input_y(i));
let wire_input_borrow = Target::wire(
gate_index,
U32SubtractionGate::<F, D>::wire_ith_input_borrow(i),
);
self.connect(zero, wire_input_x);
self.connect(zero, wire_input_y);
self.connect(zero, wire_input_borrow);
}
}
}
2021-11-10 09:58:14 -08:00
fn fill_batched_gates(&mut self) {
self.fill_arithmetic_gates();
self.fill_base_arithmetic_gates();
2021-11-10 12:10:32 -08:00
self.fill_random_access_gates();
2021-11-10 09:58:14 -08:00
self.fill_switch_gates();
self.fill_u32_arithmetic_gates();
self.fill_u32_subtraction_gates();
}
2021-11-10 09:56:21 -08:00
}