2021-09-22 10:56:09 -07:00
|
|
|
use std::cmp::max;
|
2021-09-13 16:38:55 -07:00
|
|
|
use std::collections::{BTreeMap, HashMap, HashSet};
|
2021-07-08 17:16:26 +02:00
|
|
|
use std::convert::TryInto;
|
2021-03-25 15:20:14 -07:00
|
|
|
use std::time::Instant;
|
2021-02-26 13:18:41 -08:00
|
|
|
|
2021-08-02 10:38:09 -07:00
|
|
|
use log::{info, Level};
|
2021-03-25 15:20:14 -07:00
|
|
|
|
2021-04-22 16:32:57 -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;
|
2021-07-30 09:03:11 -07:00
|
|
|
use crate::field::extension_field::{Extendable, FieldExtension};
|
2021-09-22 10:56:09 -07:00
|
|
|
use crate::field::fft::fft_root_table;
|
2021-09-07 18:28:28 -07:00
|
|
|
use crate::field::field_types::RichField;
|
2021-07-29 22:00:29 -07:00
|
|
|
use crate::fri::commitment::PolynomialBatchCommitment;
|
Automatically select FRI reduction arities (#282)
* Automatically select FRI reduction arities
This way when a proof's degree changes, we won't need to manually update the `FriConfig`s of any recursive proofs on top of it.
For now I've added two methods of selecting arities. The first, `ConstantArityBits`, just applies a fixed reduciton arity until the degree has shrunk below a certain threshold. The second, `MinSize`, searches for the sequence of arities that minimizes proof size.
Note that this optimization is approximate -- e.g. it doesn't account for the effect of compression, and doesn't count some minor contributions to proof size, like the Merkle roots from the commit phase. It also assumes we're not using Merkle caps in serialized proofs, and that we're inferring one of the evaluations, even though we haven't made those changes yet.
I think we should generally use `ConstantArityBits` for proofs that we will recurse on, since using a single arity tends to be more recursion-friendly. We could use `MinSize` for generating final bridge proofs, since we won't do further recursion on top of those.
* Fix tests
* Feedback
2021-10-04 13:52:05 -07:00
|
|
|
use crate::fri::FriParams;
|
2021-08-17 09:12:40 +02:00
|
|
|
use crate::gates::arithmetic::{ArithmeticExtensionGate, NUM_ARITHMETIC_OPS};
|
2021-03-28 15:36:51 -07:00
|
|
|
use crate::gates::constant::ConstantGate;
|
2021-07-22 23:48:03 -07:00
|
|
|
use crate::gates::gate::{Gate, GateInstance, GateRef, PrefixedGate};
|
2021-06-22 16:54:20 +02:00
|
|
|
use crate::gates::gate_tree::Tree;
|
2021-03-28 15:36:51 -07:00
|
|
|
use crate::gates::noop::NoopGate;
|
2021-07-21 08:26:19 -07:00
|
|
|
use crate::gates::public_input::PublicInputGate;
|
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};
|
2021-07-29 22:00:29 -07:00
|
|
|
use crate::hash::hashing::hash_n_to_hash;
|
2021-09-02 15:03:03 -07:00
|
|
|
use crate::iop::generator::{
|
|
|
|
|
CopyGenerator, RandomValueGenerator, SimpleGenerator, WitnessGenerator,
|
|
|
|
|
};
|
2021-08-14 08:53:39 -07:00
|
|
|
use crate::iop::target::{BoolTarget, Target};
|
2021-07-29 22:00:29 -07:00
|
|
|
use crate::iop::wire::Wire;
|
|
|
|
|
use crate::plonk::circuit_data::{
|
|
|
|
|
CircuitConfig, CircuitData, CommonCircuitData, ProverCircuitData, ProverOnlyCircuitData,
|
|
|
|
|
VerifierCircuitData, VerifierOnlyCircuitData,
|
|
|
|
|
};
|
|
|
|
|
use crate::plonk::copy_constraint::CopyConstraint;
|
2021-09-28 22:31:20 -07:00
|
|
|
use crate::plonk::permutation_argument::Forest;
|
2021-07-29 22:00:29 -07:00
|
|
|
use crate::plonk::plonk_common::PlonkPolynomials;
|
2021-03-30 13:30:31 -07:00
|
|
|
use crate::polynomial::polynomial::PolynomialValues;
|
2021-07-29 22:00:29 -07:00
|
|
|
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;
|
2021-08-02 10:38:09 -07:00
|
|
|
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
|
|
|
|
2021-09-07 18:28:28 -07:00
|
|
|
pub struct CircuitBuilder<F: RichField + Extendable<D>, const D: usize> {
|
2021-03-28 15:36:51 -07:00
|
|
|
pub(crate) config: CircuitConfig,
|
2021-03-21 11:57:33 -07:00
|
|
|
|
|
|
|
|
/// The types of gates used in this circuit.
|
2021-05-30 13:25:53 -07:00
|
|
|
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
|
|
|
|
2021-07-21 08:26:19 -07:00
|
|
|
/// Targets to be made public.
|
|
|
|
|
public_inputs: Vec<Target>,
|
2021-04-25 17:02:02 -07:00
|
|
|
|
2021-07-01 08:12:12 -07:00
|
|
|
/// 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>,
|
|
|
|
|
|
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-25 17:02:02 -07:00
|
|
|
|
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
|
|
|
|
2021-08-16 10:18:10 +02:00
|
|
|
/// 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)>,
|
2021-08-24 18:35:30 -07:00
|
|
|
|
2021-09-01 16:38:10 -07: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)>>,
|
2021-02-26 13:18:41 -08:00
|
|
|
}
|
|
|
|
|
|
2021-09-07 18:28:28 -07: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 {
|
2021-03-25 15:20:14 -07:00
|
|
|
CircuitBuilder {
|
2021-02-26 13:18:41 -08:00
|
|
|
config,
|
|
|
|
|
gates: HashSet::new(),
|
|
|
|
|
gate_instances: Vec::new(),
|
2021-07-21 08:26:19 -07:00
|
|
|
public_inputs: Vec::new(),
|
2021-04-07 22:21:45 -07:00
|
|
|
virtual_target_index: 0,
|
2021-04-25 17:02:02 -07:00
|
|
|
copy_constraints: Vec::new(),
|
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(),
|
|
|
|
|
targets_to_constants: HashMap::new(),
|
2021-08-16 10:18:10 +02:00
|
|
|
free_arithmetic: HashMap::new(),
|
2021-08-24 18:35:30 -07:00
|
|
|
current_switch_gates: Vec::new(),
|
2021-02-26 13:18:41 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-29 12:33:11 -07:00
|
|
|
pub fn num_gates(&self) -> usize {
|
|
|
|
|
self.gate_instances.len()
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-21 08:26:19 -07:00
|
|
|
/// Registers the given target as a public input.
|
|
|
|
|
pub fn register_public_input(&mut self, target: Target) {
|
|
|
|
|
self.public_inputs.push(target);
|
2021-04-25 17:02:02 -07:00
|
|
|
}
|
|
|
|
|
|
2021-07-21 08:26:19 -07:00
|
|
|
/// 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));
|
2021-04-25 17:02:02 -07:00
|
|
|
}
|
|
|
|
|
|
2021-07-01 08:12:12 -07:00
|
|
|
/// 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;
|
2021-07-01 08:12:12 -07:00
|
|
|
Target::VirtualTarget { index }
|
2021-04-07 22:21:45 -07:00
|
|
|
}
|
|
|
|
|
|
2021-07-01 08:12:12 -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
|
|
|
}
|
|
|
|
|
|
2021-07-29 22:00:29 -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))
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-29 22:00:29 -07:00
|
|
|
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()
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-14 08:53:39 -07:00
|
|
|
// 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.
|
2021-07-22 23:48:03 -07:00
|
|
|
pub fn add_gate<G: Gate<F, D>>(&mut self, gate_type: G, constants: Vec<F>) -> usize {
|
|
|
|
|
self.check_gate_compatibility(&gate_type);
|
2021-06-23 18:04:43 +02:00
|
|
|
assert_eq!(
|
2021-07-22 23:48:03 -07:00
|
|
|
gate_type.num_constants(),
|
2021-06-23 18:04:43 +02:00
|
|
|
constants.len(),
|
|
|
|
|
"Number of constants doesn't match."
|
|
|
|
|
);
|
2021-02-26 13:18:41 -08:00
|
|
|
|
|
|
|
|
let index = self.gate_instances.len();
|
2021-07-22 23:48:03 -07:00
|
|
|
self.add_generators(gate_type.generators(index, &constants));
|
2021-04-02 15:29:21 -07:00
|
|
|
|
2021-07-22 23:48:03 -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
|
|
|
|
2021-04-22 16:32:57 -07:00
|
|
|
self.gate_instances.push(GateInstance {
|
2021-07-22 23:48:03 -07:00
|
|
|
gate_ref,
|
2021-04-22 16:32:57 -07:00
|
|
|
constants,
|
|
|
|
|
});
|
2021-02-26 13:18:41 -08:00
|
|
|
index
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-22 23:48:03 -07:00
|
|
|
fn check_gate_compatibility<G: Gate<F, D>>(&self, gate: &G) {
|
2021-04-22 16:32:57 -07:00
|
|
|
assert!(
|
2021-07-22 23:48:03 -07:00
|
|
|
gate.num_wires() <= self.config.num_wires,
|
2021-04-22 16:32:57 -07:00
|
|
|
"{:?} requires {} wires, but our GateConfig has only {}",
|
2021-07-22 23:48:03 -07:00
|
|
|
gate.id(),
|
|
|
|
|
gate.num_wires(),
|
2021-04-22 16:32:57 -07:00
|
|
|
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) {
|
2021-04-22 16:32:57 -07:00
|
|
|
assert!(
|
2021-05-07 11:30:03 +02:00
|
|
|
x.is_routable(&self.config),
|
2021-04-22 16:32:57 -07:00
|
|
|
"Tried to route a wire that isn't routable"
|
|
|
|
|
);
|
|
|
|
|
assert!(
|
2021-05-07 11:30:03 +02:00
|
|
|
y.is_routable(&self.config),
|
2021-04-22 16:32:57 -07:00
|
|
|
"Tried to route a wire that isn't routable"
|
|
|
|
|
);
|
2021-07-14 20:54:30 +02:00
|
|
|
self.copy_constraints
|
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-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)
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-14 08:53:39 -07:00
|
|
|
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-07-22 23:48:03 -07:00
|
|
|
let gate = self.add_gate(ConstantGate, vec![c]);
|
2021-04-22 16:32:57 -07:00
|
|
|
let target = Target::Wire(Wire {
|
|
|
|
|
gate,
|
|
|
|
|
input: ConstantGate::WIRE_OUTPUT,
|
|
|
|
|
});
|
2021-04-21 11:47:18 -07:00
|
|
|
self.constants_to_targets.insert(c, target);
|
|
|
|
|
self.targets_to_constants.insert(target, c);
|
|
|
|
|
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()
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-14 08:53:39 -07:00
|
|
|
pub fn constant_bool(&mut self, b: bool) -> BoolTarget {
|
|
|
|
|
if b {
|
|
|
|
|
self._true()
|
|
|
|
|
} else {
|
|
|
|
|
self._false()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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()
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-30 09:03:11 -07:00
|
|
|
/// 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()
|
2021-07-30 09:03:11 -07:00
|
|
|
.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());
|
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(),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
Automatically select FRI reduction arities (#282)
* Automatically select FRI reduction arities
This way when a proof's degree changes, we won't need to manually update the `FriConfig`s of any recursive proofs on top of it.
For now I've added two methods of selecting arities. The first, `ConstantArityBits`, just applies a fixed reduciton arity until the degree has shrunk below a certain threshold. The second, `MinSize`, searches for the sequence of arities that minimizes proof size.
Note that this optimization is approximate -- e.g. it doesn't account for the effect of compression, and doesn't count some minor contributions to proof size, like the Merkle roots from the commit phase. It also assumes we're not using Merkle caps in serialized proofs, and that we're inferring one of the evaluations, even though we haven't made those changes yet.
I think we should generally use `ConstantArityBits` for proofs that we will recurse on, since using a single arity tends to be more recursion-friendly. We could use `MinSize` for generating final bridge proofs, since we won't do further recursion on top of those.
* Fix tests
* Feedback
2021-10-04 13:52:05 -07:00
|
|
|
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,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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) {
|
Automatically select FRI reduction arities (#282)
* Automatically select FRI reduction arities
This way when a proof's degree changes, we won't need to manually update the `FriConfig`s of any recursive proofs on top of it.
For now I've added two methods of selecting arities. The first, `ConstantArityBits`, just applies a fixed reduciton arity until the degree has shrunk below a certain threshold. The second, `MinSize`, searches for the sequence of arities that minimizes proof size.
Note that this optimization is approximate -- e.g. it doesn't account for the effect of compression, and doesn't count some minor contributions to proof size, like the Merkle roots from the commit phase. It also assumes we're not using Merkle caps in serialized proofs, and that we're inferring one of the evaluations, even though we haven't made those changes yet.
I think we should generally use `ConstantArityBits` for proofs that we will recurse on, since using a single arity tends to be more recursion-friendly. We could use `MinSize` for generating final bridge proofs, since we won't do further recursion on top of those.
* Fix tests
* Feedback
2021-10-04 13:52:05 -07:00
|
|
|
let degree_bits_estimate = log2_strict(degree_estimate);
|
2021-06-24 22:02:20 -07:00
|
|
|
let fri_queries = self.config.fri_config.num_query_rounds;
|
2021-06-28 17:07:27 -07:00
|
|
|
let arities: Vec<usize> = self
|
Automatically select FRI reduction arities (#282)
* Automatically select FRI reduction arities
This way when a proof's degree changes, we won't need to manually update the `FriConfig`s of any recursive proofs on top of it.
For now I've added two methods of selecting arities. The first, `ConstantArityBits`, just applies a fixed reduciton arity until the degree has shrunk below a certain threshold. The second, `MinSize`, searches for the sequence of arities that minimizes proof size.
Note that this optimization is approximate -- e.g. it doesn't account for the effect of compression, and doesn't count some minor contributions to proof size, like the Merkle roots from the commit phase. It also assumes we're not using Merkle caps in serialized proofs, and that we're inferring one of the evaluations, even though we haven't made those changes yet.
I think we should generally use `ConstantArityBits` for proofs that we will recurse on, since using a single arity tends to be more recursion-friendly. We could use `MinSize` for generating final bridge proofs, since we won't do further recursion on top of those.
* Fix tests
* Feedback
2021-10-04 13:52:05 -07:00
|
|
|
.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);
|
2021-06-24 22:02:20 -07:00
|
|
|
|
2021-07-18 23:24:33 -07:00
|
|
|
// We add D for openings at zeta.
|
2021-06-24 22:02:20 -07:00
|
|
|
let regular_poly_openings = D + fri_openings;
|
2021-07-18 23:24:33 -07:00
|
|
|
// We add 2 * D for openings at zeta and g * zeta.
|
2021-06-24 22:02:20 -07:00
|
|
|
let z_openings = 2 * D + fri_openings;
|
|
|
|
|
|
2021-06-28 09:45:56 -07:00
|
|
|
(regular_poly_openings, z_openings)
|
2021-06-24 22:02:20 -07:00
|
|
|
}
|
|
|
|
|
|
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) {
|
2021-06-30 12:54:45 -07:00
|
|
|
let num_gates = self.gate_instances.len();
|
2021-06-24 22:02:20 -07:00
|
|
|
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-24 22:02:20 -07:00
|
|
|
|
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);
|
2021-06-24 22:02:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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) {
|
2021-07-18 23:24:33 -07:00
|
|
|
if self.config.zero_knowledge {
|
|
|
|
|
self.blind();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while !self.gate_instances.len().is_power_of_two() {
|
2021-07-22 23:48:03 -07:00
|
|
|
self.add_gate(NoopGate, vec![]);
|
2021-07-18 23:24:33 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn blind(&mut self) {
|
2021-06-28 09:45:56 -07:00
|
|
|
let (regular_poly_openings, z_openings) = self.blinding_counts();
|
2021-07-01 10:53:42 -07:00
|
|
|
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-24 22:02:20 -07:00
|
|
|
|
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 {
|
2021-07-22 23:48:03 -07:00
|
|
|
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 {
|
2021-07-22 23:48:03 -07:00
|
|
|
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(
|
2021-06-28 11:27:43 +02:00
|
|
|
&self,
|
|
|
|
|
gates: &[PrefixedGate<F, D>],
|
|
|
|
|
num_constants: usize,
|
|
|
|
|
) -> Vec<PolynomialValues<F>> {
|
2021-04-22 16:32:57 -07:00
|
|
|
let constants_per_gate = self
|
|
|
|
|
.gate_instances
|
|
|
|
|
.iter()
|
2021-06-24 18:06:48 +02:00
|
|
|
.map(|gate| {
|
|
|
|
|
let prefix = &gates
|
|
|
|
|
.iter()
|
2021-07-22 23:48:03 -07:00
|
|
|
.find(|g| g.gate.0.id() == gate.gate_ref.0.id())
|
2021-06-24 18:06:48 +02:00
|
|
|
.unwrap()
|
|
|
|
|
.prefix;
|
2021-06-24 20:53:15 +02:00
|
|
|
let mut prefixed_constants = Vec::with_capacity(num_constants);
|
2021-06-24 18:06:48 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2021-09-28 22:31:20 -07:00
|
|
|
fn sigma_vecs(&self, k_is: &[F], subgroup: &[F]) -> (Vec<PolynomialValues<F>>, Forest) {
|
2021-04-25 17:02:02 -07:00
|
|
|
let degree = self.gate_instances.len();
|
|
|
|
|
let degree_log = log2_strict(degree);
|
2021-09-28 22:31:20 -07:00
|
|
|
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,
|
|
|
|
|
);
|
2021-04-25 17:02:02 -07:00
|
|
|
|
|
|
|
|
for gate in 0..degree {
|
2021-09-28 22:31:20 -07:00
|
|
|
for input in 0..config.num_wires {
|
|
|
|
|
forest.add(Target::Wire(Wire { gate, input }));
|
2021-04-25 17:02:02 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-01 08:12:12 -07:00
|
|
|
for index in 0..self.virtual_target_index {
|
2021-09-28 22:31:20 -07:00
|
|
|
forest.add(Target::VirtualTarget { index });
|
2021-04-25 17:02:02 -07:00
|
|
|
}
|
|
|
|
|
|
2021-07-14 20:54:30 +02:00
|
|
|
for &CopyConstraint { pair: (a, b), .. } in &self.copy_constraints {
|
2021-09-28 22:31:20 -07:00
|
|
|
forest.merge(a, b);
|
2021-04-25 17:02:02 -07:00
|
|
|
}
|
|
|
|
|
|
2021-09-28 22:31:20 -07:00
|
|
|
forest.compress_paths();
|
2021-09-27 10:00:44 -07:00
|
|
|
|
2021-09-28 22:31:20 -07:00
|
|
|
let wire_partition = forest.wire_partition();
|
2021-08-19 14:54:11 +02:00
|
|
|
(
|
|
|
|
|
wire_partition.get_sigma_polys(degree_log, k_is, subgroup),
|
2021-09-28 22:31:20 -07:00
|
|
|
forest,
|
2021-08-19 14:54:11 +02:00
|
|
|
)
|
2021-03-25 15:20:14 -07:00
|
|
|
}
|
|
|
|
|
|
2021-08-17 09:12:40 +02:00
|
|
|
/// Fill the remaining unused arithmetic operations with zeros, so that all
|
|
|
|
|
/// `ArithmeticExtensionGenerator` are run.
|
|
|
|
|
fn fill_arithmetic_gates(&mut self) {
|
|
|
|
|
let zero = self.zero_extension();
|
|
|
|
|
let remaining_arithmetic_gates = self.free_arithmetic.values().copied().collect::<Vec<_>>();
|
|
|
|
|
for (gate, i) in remaining_arithmetic_gates {
|
|
|
|
|
for j in i..NUM_ARITHMETIC_OPS {
|
|
|
|
|
let wires_multiplicand_0 = ExtensionTarget::from_range(
|
|
|
|
|
gate,
|
|
|
|
|
ArithmeticExtensionGate::<D>::wires_ith_multiplicand_0(j),
|
|
|
|
|
);
|
|
|
|
|
let wires_multiplicand_1 = ExtensionTarget::from_range(
|
|
|
|
|
gate,
|
|
|
|
|
ArithmeticExtensionGate::<D>::wires_ith_multiplicand_1(j),
|
|
|
|
|
);
|
|
|
|
|
let wires_addend = ExtensionTarget::from_range(
|
|
|
|
|
gate,
|
|
|
|
|
ArithmeticExtensionGate::<D>::wires_ith_addend(j),
|
|
|
|
|
);
|
|
|
|
|
|
2021-08-24 08:25:11 +02:00
|
|
|
self.connect_extension(zero, wires_multiplicand_0);
|
|
|
|
|
self.connect_extension(zero, wires_multiplicand_1);
|
|
|
|
|
self.connect_extension(zero, wires_addend);
|
2021-08-17 09:12:40 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-01 16:38:10 -07:00
|
|
|
/// Fill the remaining unused switch gates with dummy values, so that all
|
|
|
|
|
/// `SwitchGenerator` are run.
|
|
|
|
|
fn fill_switch_gates(&mut self) {
|
|
|
|
|
let zero = self.zero();
|
|
|
|
|
|
|
|
|
|
for chunk_size in 1..=self.current_switch_gates.len() {
|
2021-09-02 15:59:17 -07:00
|
|
|
if let Some((gate, gate_index, mut copy)) =
|
2021-09-01 16:38:10 -07:00
|
|
|
self.current_switch_gates[chunk_size - 1].clone()
|
|
|
|
|
{
|
2021-09-02 15:59:17 -07:00
|
|
|
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;
|
2021-09-01 16:38:10 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-19 12:22:18 -07:00
|
|
|
pub fn print_gate_counts(&self, min_delta: usize) {
|
|
|
|
|
self.context_log
|
|
|
|
|
.filter(self.num_gates(), min_delta)
|
|
|
|
|
.print(self.num_gates());
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-21 11:17:00 -07:00
|
|
|
/// Builds a "full circuit", with both prover and verifier data.
|
2021-05-30 13:25:53 -07:00
|
|
|
pub fn build(mut self) -> CircuitData<F, D> {
|
2021-08-02 10:38:09 -07:00
|
|
|
let mut timing = TimingTree::new("preprocess", Level::Trace);
|
2021-03-25 15:20:14 -07:00
|
|
|
let start = Instant::now();
|
2021-07-21 08:26:19 -07:00
|
|
|
|
2021-08-17 09:12:40 +02:00
|
|
|
self.fill_arithmetic_gates();
|
2021-09-02 15:59:17 -07:00
|
|
|
self.fill_switch_gates();
|
2021-08-17 09:12:40 +02:00
|
|
|
|
2021-07-21 08:26:19 -07: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);
|
2021-07-22 23:48:03 -07:00
|
|
|
let pi_gate = self.add_gate(PublicInputGate, vec![]);
|
2021-07-21 08:26:19 -07:00
|
|
|
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))
|
2021-07-21 08:26:19 -07:00
|
|
|
}
|
|
|
|
|
|
2021-04-22 16:32:57 -07:00
|
|
|
info!(
|
2021-07-01 10:53:42 -07:00
|
|
|
"Degree before blinding & padding: {}",
|
2021-04-22 16:32:57 -07:00
|
|
|
self.gate_instances.len()
|
|
|
|
|
);
|
2021-03-25 15:20:14 -07:00
|
|
|
self.blind_and_pad();
|
|
|
|
|
let degree = self.gate_instances.len();
|
2021-07-01 10:53:42 -07:00
|
|
|
info!("Degree after blinding & padding: {}", degree);
|
2021-07-19 16:24:21 +02:00
|
|
|
let degree_bits = log2_strict(degree);
|
Automatically select FRI reduction arities (#282)
* Automatically select FRI reduction arities
This way when a proof's degree changes, we won't need to manually update the `FriConfig`s of any recursive proofs on top of it.
For now I've added two methods of selecting arities. The first, `ConstantArityBits`, just applies a fixed reduciton arity until the degree has shrunk below a certain threshold. The second, `MinSize`, searches for the sequence of arities that minimizes proof size.
Note that this optimization is approximate -- e.g. it doesn't account for the effect of compression, and doesn't count some minor contributions to proof size, like the Merkle roots from the commit phase. It also assumes we're not using Merkle caps in serialized proofs, and that we're inferring one of the evaluations, even though we haven't made those changes yet.
I think we should generally use `ConstantArityBits` for proofs that we will recurse on, since using a single arity tends to be more recursion-friendly. We could use `MinSize` for generating final bridge proofs, since we won't do further recursion on top of those.
* Fix tests
* Feedback
2021-10-04 13:52:05 -07:00
|
|
|
let fri_params = self.fri_params(degree_bits);
|
2021-07-19 16:24:21 +02:00
|
|
|
assert!(
|
Automatically select FRI reduction arities (#282)
* Automatically select FRI reduction arities
This way when a proof's degree changes, we won't need to manually update the `FriConfig`s of any recursive proofs on top of it.
For now I've added two methods of selecting arities. The first, `ConstantArityBits`, just applies a fixed reduciton arity until the degree has shrunk below a certain threshold. The second, `MinSize`, searches for the sequence of arities that minimizes proof size.
Note that this optimization is approximate -- e.g. it doesn't account for the effect of compression, and doesn't count some minor contributions to proof size, like the Merkle roots from the commit phase. It also assumes we're not using Merkle caps in serialized proofs, and that we're inferring one of the evaluations, even though we haven't made those changes yet.
I think we should generally use `ConstantArityBits` for proofs that we will recurse on, since using a single arity tends to be more recursion-friendly. We could use `MinSize` for generating final bridge proofs, since we won't do further recursion on top of those.
* Fix tests
* Feedback
2021-10-04 13:52:05 -07:00
|
|
|
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();
|
2021-06-28 11:27:43 +02:00
|
|
|
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();
|
|
|
|
|
info!("Quotient degree factor set to: {}.", quotient_degree_factor);
|
2021-06-24 18:06:48 +02:00
|
|
|
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);
|
|
|
|
|
|
2021-06-28 11:27:43 +02:00
|
|
|
let constant_vecs = self.constant_polys(&prefixed_gates, num_constants);
|
2021-03-25 15:20:14 -07:00
|
|
|
|
2021-04-25 17:02:02 -07:00
|
|
|
let k_is = get_unique_coset_shifts(degree, self.config.num_routed_wires);
|
2021-09-28 22:31:20 -07:00
|
|
|
let (sigma_vecs, forest) = self.sigma_vecs(&k_is, &subgroup);
|
2021-04-23 14:18:03 -07:00
|
|
|
|
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();
|
2021-08-09 10:11:35 -07:00
|
|
|
let constants_sigmas_commitment = PolynomialBatchCommitment::from_values(
|
2021-06-25 11:24:26 +02:00
|
|
|
constants_sigmas_vecs,
|
2021-07-18 23:24:33 -07:00
|
|
|
self.config.rate_bits,
|
|
|
|
|
self.config.zero_knowledge & PlonkPolynomials::CONSTANTS_SIGMAS.blinding,
|
2021-08-10 13:33:44 +02:00
|
|
|
self.config.cap_height,
|
2021-08-02 10:38:09 -07:00
|
|
|
&mut timing,
|
2021-09-22 10:56:09 -07:00
|
|
|
Some(&fft_root_table),
|
2021-05-07 11:30:03 +02:00
|
|
|
);
|
2021-04-23 14:18:03 -07:00
|
|
|
|
2021-08-10 15:53:27 +02:00
|
|
|
let constants_sigmas_cap = constants_sigmas_commitment.merkle_tree.cap.clone();
|
2021-04-23 14:18:03 -07:00
|
|
|
let verifier_only = VerifierOnlyCircuitData {
|
2021-08-10 15:53:27 +02:00
|
|
|
constants_sigmas_cap: constants_sigmas_cap.clone(),
|
2021-04-23 14:18:03 -07:00
|
|
|
};
|
2021-03-25 15:20:14 -07:00
|
|
|
|
2021-09-13 16:38:55 -07:00
|
|
|
// 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() {
|
2021-09-28 22:31:20 -07:00
|
|
|
let watch_index = forest.target_index(watch);
|
|
|
|
|
let watch_rep_index = forest.parents[watch_index];
|
2021-09-13 16:38:55 -07:00
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-22 16:32:57 -07:00
|
|
|
let prover_only = ProverOnlyCircuitData {
|
2021-06-23 14:16:05 +02:00
|
|
|
generators: self.generators,
|
2021-09-13 16:38:55 -07:00
|
|
|
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,
|
2021-07-21 08:26:19 -07:00
|
|
|
public_inputs: self.public_inputs,
|
2021-07-14 20:54:30 +02:00
|
|
|
marked_targets: self.marked_targets,
|
2021-09-28 22:31:20 -07:00
|
|
|
representative_map: forest.parents,
|
2021-09-22 10:56:09 -07:00
|
|
|
fft_root_table: Some(fft_root_table),
|
2021-04-22 16:32:57 -07:00
|
|
|
};
|
2021-03-25 15:20:14 -07:00
|
|
|
|
2021-03-28 15:36:51 -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());
|
|
|
|
|
|
2021-04-22 16:32:57 -07:00
|
|
|
let num_gate_constraints = gates
|
|
|
|
|
.iter()
|
2021-03-28 15:36:51 -07:00
|
|
|
.map(|gate| gate.0.num_constraints())
|
|
|
|
|
.max()
|
|
|
|
|
.expect("No gates?");
|
|
|
|
|
|
2021-07-08 15:13:29 +02:00
|
|
|
let num_partial_products =
|
2021-07-08 15:16:05 +02:00
|
|
|
num_partial_products(self.config.num_routed_wires, quotient_degree_factor);
|
2021-07-01 15:41:01 +02:00
|
|
|
|
2021-04-22 16:32:57 -07: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 */],
|
|
|
|
|
];
|
2021-04-22 16:32:57 -07:00
|
|
|
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,
|
Automatically select FRI reduction arities (#282)
* Automatically select FRI reduction arities
This way when a proof's degree changes, we won't need to manually update the `FriConfig`s of any recursive proofs on top of it.
For now I've added two methods of selecting arities. The first, `ConstantArityBits`, just applies a fixed reduciton arity until the degree has shrunk below a certain threshold. The second, `MinSize`, searches for the sequence of arities that minimizes proof size.
Note that this optimization is approximate -- e.g. it doesn't account for the effect of compression, and doesn't count some minor contributions to proof size, like the Merkle roots from the commit phase. It also assumes we're not using Merkle caps in serialized proofs, and that we're inferring one of the evaluations, even though we haven't made those changes yet.
I think we should generally use `ConstantArityBits` for proofs that we will recurse on, since using a single arity tends to be more recursion-friendly. We could use `MinSize` for generating final bridge proofs, since we won't do further recursion on top of those.
* Fix tests
* Feedback
2021-10-04 13:52:05 -07:00
|
|
|
fri_params,
|
2021-04-04 11:35:58 -07:00
|
|
|
degree_bits,
|
2021-06-24 18:06:48 +02:00
|
|
|
gates: prefixed_gates,
|
2021-07-08 15:16:05 +02:00
|
|
|
quotient_degree_factor,
|
2021-03-28 15:36:51 -07:00
|
|
|
num_gate_constraints,
|
2021-06-25 11:24:26 +02:00
|
|
|
num_constants,
|
2021-09-28 22:31:20 -07:00
|
|
|
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,
|
2021-04-22 16:32:57 -07:00
|
|
|
circuit_digest,
|
2021-03-25 15:20:14 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
info!("Building circuit took {}s", start.elapsed().as_secs_f32());
|
|
|
|
|
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.
|
2021-05-30 13:25:53 -07:00
|
|
|
pub fn build_prover(self) -> ProverCircuitData<F, D> {
|
2021-03-25 15:20:14 -07:00
|
|
|
// TODO: Can skip parts of this.
|
2021-04-22 16:32:57 -07:00
|
|
|
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.
|
2021-05-30 13:25:53 -07:00
|
|
|
pub fn build_verifier(self) -> VerifierCircuitData<F, D> {
|
2021-03-25 15:20:14 -07:00
|
|
|
// TODO: Can skip parts of this.
|
2021-04-22 16:32:57 -07:00
|
|
|
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
|
|
|
}
|