mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-02 22:03:07 +00:00
Merge branch 'main' into exp_gate
This commit is contained in:
commit
da9017bcbc
@ -14,7 +14,7 @@ use crate::field::cosets::get_unique_coset_shifts;
|
||||
use crate::field::extension_field::target::ExtensionTarget;
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::gates::constant::ConstantGate;
|
||||
use crate::gates::gate::{GateInstance, GateRef, PrefixedGate};
|
||||
use crate::gates::gate::{Gate, GateInstance, GateRef, PrefixedGate};
|
||||
use crate::gates::gate_tree::Tree;
|
||||
use crate::gates::noop::NoopGate;
|
||||
use crate::gates::public_input::PublicInputGate;
|
||||
@ -124,41 +124,35 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn add_gate_no_constants(&mut self, gate_type: GateRef<F, D>) -> usize {
|
||||
self.add_gate(gate_type, Vec::new())
|
||||
}
|
||||
|
||||
/// Adds a gate to the circuit, and returns its index.
|
||||
pub fn add_gate(&mut self, gate_type: GateRef<F, D>, constants: Vec<F>) -> usize {
|
||||
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.0.num_constants(),
|
||||
gate_type.num_constants(),
|
||||
constants.len(),
|
||||
"Number of constants doesn't match."
|
||||
);
|
||||
// If we haven't seen a gate of this type before, check that it's compatible with our
|
||||
// circuit configuration, then register it.
|
||||
if !self.gates.contains(&gate_type) {
|
||||
self.check_gate_compatibility(&gate_type);
|
||||
self.gates.insert(gate_type.clone());
|
||||
}
|
||||
|
||||
let index = self.gate_instances.len();
|
||||
self.add_generators(gate_type.generators(index, &constants));
|
||||
|
||||
self.add_generators(gate_type.0.generators(index, &constants));
|
||||
// Register this gate type if we haven't seen it before.
|
||||
let gate_ref = GateRef::new(gate_type);
|
||||
self.gates.insert(gate_ref.clone());
|
||||
|
||||
self.gate_instances.push(GateInstance {
|
||||
gate_type,
|
||||
gate_ref,
|
||||
constants,
|
||||
});
|
||||
index
|
||||
}
|
||||
|
||||
fn check_gate_compatibility(&self, gate: &GateRef<F, D>) {
|
||||
fn check_gate_compatibility<G: Gate<F, D>>(&self, gate: &G) {
|
||||
assert!(
|
||||
gate.0.num_wires() <= self.config.num_wires,
|
||||
gate.num_wires() <= self.config.num_wires,
|
||||
"{:?} requires {} wires, but our GateConfig has only {}",
|
||||
gate.0.id(),
|
||||
gate.0.num_wires(),
|
||||
gate.id(),
|
||||
gate.num_wires(),
|
||||
self.config.num_wires
|
||||
);
|
||||
}
|
||||
@ -287,7 +281,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
return target;
|
||||
}
|
||||
|
||||
let gate = self.add_gate(ConstantGate::get(), vec![c]);
|
||||
let gate = self.add_gate(ConstantGate, vec![c]);
|
||||
let target = Target::Wire(Wire {
|
||||
gate,
|
||||
input: ConstantGate::WIRE_OUTPUT,
|
||||
@ -377,7 +371,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
}
|
||||
|
||||
while !self.gate_instances.len().is_power_of_two() {
|
||||
self.add_gate_no_constants(NoopGate::get());
|
||||
self.add_gate(NoopGate, vec![]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -394,7 +388,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
// For each "regular" blinding factor, we simply add a no-op gate, and insert a random value
|
||||
// for each wire.
|
||||
for _ in 0..regular_poly_openings {
|
||||
let gate = self.add_gate_no_constants(NoopGate::get());
|
||||
let gate = self.add_gate(NoopGate, vec![]);
|
||||
for w in 0..num_wires {
|
||||
self.add_generator(RandomValueGenerator {
|
||||
target: Target::Wire(Wire { gate, input: w }),
|
||||
@ -406,8 +400,8 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
// enforce a copy constraint between them.
|
||||
// See https://mirprotocol.org/blog/Adding-zero-knowledge-to-Plonk-Halo
|
||||
for _ in 0..z_openings {
|
||||
let gate_1 = self.add_gate_no_constants(NoopGate::get());
|
||||
let gate_2 = self.add_gate_no_constants(NoopGate::get());
|
||||
let gate_1 = self.add_gate(NoopGate, vec![]);
|
||||
let gate_2 = self.add_gate(NoopGate, vec![]);
|
||||
|
||||
for w in 0..num_routed_wires {
|
||||
self.add_generator(RandomValueGenerator {
|
||||
@ -441,7 +435,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
.map(|gate| {
|
||||
let prefix = &gates
|
||||
.iter()
|
||||
.find(|g| g.gate.0.id() == gate.gate_type.0.id())
|
||||
.find(|g| g.gate.0.id() == gate.gate_ref.0.id())
|
||||
.unwrap()
|
||||
.prefix;
|
||||
let mut prefixed_constants = Vec::with_capacity(num_constants);
|
||||
@ -498,7 +492,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
// 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_no_constants(PublicInputGate::get());
|
||||
let pi_gate = self.add_gate(PublicInputGate, vec![]);
|
||||
for (&hash_part, wire) in public_inputs_hash
|
||||
.elements
|
||||
.iter()
|
||||
|
||||
@ -56,7 +56,7 @@ impl CircuitConfig {
|
||||
|
||||
pub(crate) fn large_config() -> Self {
|
||||
Self {
|
||||
num_wires: 134,
|
||||
num_wires: 126,
|
||||
num_routed_wires: 34,
|
||||
security_bits: 128,
|
||||
rate_bits: 3,
|
||||
|
||||
@ -20,7 +20,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
fn compute_evaluation(
|
||||
&mut self,
|
||||
x: Target,
|
||||
old_x_index: Target,
|
||||
old_x_index_bits: &[Target],
|
||||
arity_bits: usize,
|
||||
last_evals: &[ExtensionTarget<D>],
|
||||
beta: ExtensionTarget<D>,
|
||||
@ -33,13 +33,9 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
// The evaluation vector needs to be reordered first.
|
||||
let mut evals = last_evals.to_vec();
|
||||
reverse_index_bits_in_place(&mut evals);
|
||||
let mut old_x_index_bits = self.split_le(old_x_index, arity_bits);
|
||||
old_x_index_bits.reverse();
|
||||
// Want `g^(arity - rev_old_x_index)` as in the out-of-circuit version.
|
||||
// Compute it as `g^(arity-1-rev_old_x_index) * g`, where the first term is gotten using two's complement.
|
||||
// TODO: Once the exponentiation gate lands, we won't need the bits and will be able to compute
|
||||
// `g^(arity-rev_old_x_index)` directly.
|
||||
let start = self.exp_from_complement_bits(gt, &old_x_index_bits);
|
||||
let start = self.exp_from_complement_bits(gt, old_x_index_bits.iter().rev());
|
||||
let coset_start = self.mul_many(&[start, gt, x]);
|
||||
|
||||
// The answer is gotten by interpolating {(x*g^i, P(x*g^i))} and evaluating at beta.
|
||||
@ -151,7 +147,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
|
||||
fn fri_verify_initial_proof(
|
||||
&mut self,
|
||||
x_index: Target,
|
||||
x_index_bits: &[Target],
|
||||
proof: &FriInitialTreeProofTarget,
|
||||
initial_merkle_roots: &[HashTarget],
|
||||
) {
|
||||
@ -164,7 +160,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
context!(
|
||||
self,
|
||||
&format!("verify {}'th initial Merkle proof", i),
|
||||
self.verify_merkle_proof(evals.clone(), x_index, root, merkle_proof)
|
||||
self.verify_merkle_proof(evals.clone(), x_index_bits, root, merkle_proof)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -256,28 +252,26 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
let config = &common_data.config.fri_config;
|
||||
let n_log = log2_strict(n);
|
||||
// TODO: Do we need to range check `x_index` to a target smaller than `p`?
|
||||
let mut x_index = challenger.get_challenge(self);
|
||||
x_index = self.split_low_high(x_index, n_log, 64).0;
|
||||
let mut x_index_num_bits = n_log;
|
||||
let x_index = challenger.get_challenge(self);
|
||||
let mut x_index_bits = self.low_bits(x_index, n_log, 64);
|
||||
let mut domain_size = n;
|
||||
context!(
|
||||
self,
|
||||
"check FRI initial proof",
|
||||
self.fri_verify_initial_proof(
|
||||
x_index,
|
||||
&x_index_bits,
|
||||
&round_proof.initial_trees_proof,
|
||||
initial_merkle_roots,
|
||||
)
|
||||
);
|
||||
let mut old_x_index = self.zero();
|
||||
let mut old_x_index_bits = Vec::new();
|
||||
|
||||
// `subgroup_x` is `subgroup[x_index]`, i.e., the actual field element in the domain.
|
||||
let mut subgroup_x = context!(self, "compute x from its index", {
|
||||
let g = self.constant(F::MULTIPLICATIVE_GROUP_GENERATOR);
|
||||
let phi = self.constant(F::primitive_root_of_unity(n_log));
|
||||
|
||||
let reversed_x = self.reverse_limbs::<2>(x_index, n_log);
|
||||
let phi = self.exp(phi, reversed_x, n_log);
|
||||
let phi = self.exp_from_bits(phi, x_index_bits.iter().rev());
|
||||
self.mul(g, phi)
|
||||
});
|
||||
|
||||
@ -305,7 +299,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
"infer evaluation using interpolation",
|
||||
self.compute_evaluation(
|
||||
subgroup_x,
|
||||
old_x_index,
|
||||
&old_x_index_bits,
|
||||
config.reduction_arity_bits[i - 1],
|
||||
last_evals,
|
||||
betas[i - 1],
|
||||
@ -314,15 +308,16 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
};
|
||||
let mut evals = round_proof.steps[i].evals.clone();
|
||||
// Insert P(y) into the evaluation vector, since it wasn't included by the prover.
|
||||
let (low_x_index, high_x_index) =
|
||||
self.split_low_high(x_index, arity_bits, x_index_num_bits);
|
||||
let high_x_index_bits = x_index_bits.split_off(arity_bits);
|
||||
old_x_index_bits = x_index_bits;
|
||||
let low_x_index = self.le_sum(old_x_index_bits.iter());
|
||||
evals = self.insert(low_x_index, e_x, evals);
|
||||
context!(
|
||||
self,
|
||||
"verify FRI round Merkle proof.",
|
||||
self.verify_merkle_proof(
|
||||
flatten_target(&evals),
|
||||
high_x_index,
|
||||
&high_x_index_bits,
|
||||
proof.commit_phase_merkle_roots[i],
|
||||
&round_proof.steps[i].merkle_proof,
|
||||
)
|
||||
@ -334,9 +329,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
subgroup_x = self.exp_power_of_2(subgroup_x, config.reduction_arity_bits[i - 1]);
|
||||
}
|
||||
domain_size = next_domain_size;
|
||||
old_x_index = low_x_index;
|
||||
x_index = high_x_index;
|
||||
x_index_num_bits -= arity_bits;
|
||||
x_index_bits = high_x_index_bits;
|
||||
}
|
||||
|
||||
let last_evals = evaluations.last().unwrap();
|
||||
@ -346,7 +339,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
"infer final evaluation using interpolation",
|
||||
self.compute_evaluation(
|
||||
subgroup_x,
|
||||
old_x_index,
|
||||
&old_x_index_bits,
|
||||
final_arity_bits,
|
||||
last_evals,
|
||||
*betas.last().unwrap(),
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
use std::borrow::Borrow;
|
||||
|
||||
use crate::circuit_builder::CircuitBuilder;
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::target::Target;
|
||||
@ -168,16 +170,18 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
// TODO: Optimize this, maybe with a new gate.
|
||||
// TODO: Test
|
||||
/// Exponentiate `base` to the power of `exponent`, given by its little-endian bits.
|
||||
pub fn exp_from_bits(&mut self, base: Target, exponent_bits: &[Target]) -> Target {
|
||||
pub fn exp_from_bits(
|
||||
&mut self,
|
||||
base: Target,
|
||||
exponent_bits: impl Iterator<Item = impl Borrow<Target>>,
|
||||
) -> Target {
|
||||
let mut current = base;
|
||||
let one_ext = self.one_extension();
|
||||
let mut product = self.one();
|
||||
let one = self.one();
|
||||
let mut product = one;
|
||||
|
||||
for &bit in exponent_bits {
|
||||
// TODO: Add base field select.
|
||||
let current_ext = self.convert_to_ext(current);
|
||||
let multiplicand = self.select(bit, current_ext, one_ext);
|
||||
product = self.mul(product, multiplicand.0[0]);
|
||||
for bit in exponent_bits {
|
||||
let multiplicand = self.select(*bit.borrow(), current, one);
|
||||
product = self.mul(product, multiplicand);
|
||||
current = self.mul(current, current);
|
||||
}
|
||||
|
||||
@ -187,16 +191,18 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
// TODO: Optimize this, maybe with a new gate.
|
||||
// TODO: Test
|
||||
/// Exponentiate `base` to the power of `2^bit_length-1-exponent`, given by its little-endian bits.
|
||||
pub fn exp_from_complement_bits(&mut self, base: Target, exponent_bits: &[Target]) -> Target {
|
||||
pub fn exp_from_complement_bits(
|
||||
&mut self,
|
||||
base: Target,
|
||||
exponent_bits: impl Iterator<Item = impl Borrow<Target>>,
|
||||
) -> Target {
|
||||
let mut current = base;
|
||||
let one_ext = self.one_extension();
|
||||
let mut product = self.one();
|
||||
let one = self.one();
|
||||
let mut product = one;
|
||||
|
||||
for &bit in exponent_bits {
|
||||
let current_ext = self.convert_to_ext(current);
|
||||
// TODO: Add base field select.
|
||||
let multiplicand = self.select(bit, one_ext, current_ext);
|
||||
product = self.mul(product, multiplicand.0[0]);
|
||||
for bit in exponent_bits {
|
||||
let multiplicand = self.select(*bit.borrow(), one, current);
|
||||
product = self.mul(product, multiplicand);
|
||||
current = self.mul(current, current);
|
||||
}
|
||||
|
||||
@ -208,7 +214,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
/// Exponentiate `base` to the power of `exponent`, where `exponent < 2^num_bits`.
|
||||
pub fn exp(&mut self, base: Target, exponent: Target, num_bits: usize) -> Target {
|
||||
let exponent_bits = self.split_le(exponent, num_bits);
|
||||
self.exp_from_bits(base, &exponent_bits)
|
||||
self.exp_from_bits(base, exponent_bits.iter())
|
||||
}
|
||||
|
||||
/// Exponentiate `base` to the power of a known `exponent`.
|
||||
|
||||
@ -24,7 +24,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
second_multiplicand_1: ExtensionTarget<D>,
|
||||
second_addend: ExtensionTarget<D>,
|
||||
) -> (ExtensionTarget<D>, ExtensionTarget<D>) {
|
||||
let gate = self.add_gate(ArithmeticExtensionGate::new(), vec![const_0, const_1]);
|
||||
let gate = self.add_gate(ArithmeticExtensionGate, vec![const_0, const_1]);
|
||||
|
||||
let wire_first_multiplicand_0 = ExtensionTarget::from_range(
|
||||
gate,
|
||||
|
||||
@ -11,8 +11,8 @@ use crate::wire::Wire;
|
||||
impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
pub fn permute(&mut self, inputs: [Target; 12]) -> [Target; 12] {
|
||||
let zero = self.zero();
|
||||
let gate =
|
||||
self.add_gate_no_constants(GMiMCGate::<F, D, GMIMC_ROUNDS>::with_automatic_constants());
|
||||
let gate_type = GMiMCGate::<F, D, GMIMC_ROUNDS>::new_automatic_constants();
|
||||
let gate = self.add_gate(gate_type, vec![]);
|
||||
|
||||
// We don't want to swap any inputs, so set that wire to 0.
|
||||
let swap_wire = GMiMCGate::<F, D, GMIMC_ROUNDS>::WIRE_SWAP;
|
||||
@ -22,15 +22,6 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
});
|
||||
self.route(zero, swap_wire);
|
||||
|
||||
// The old accumulator wire doesn't matter, since we won't read the new accumulator wire.
|
||||
// We do have to set it to something though, so we'll arbitrary pick 0.
|
||||
let old_acc_wire = GMiMCGate::<F, D, GMIMC_ROUNDS>::WIRE_INDEX_ACCUMULATOR_OLD;
|
||||
let old_acc_wire = Target::Wire(Wire {
|
||||
gate,
|
||||
input: old_acc_wire,
|
||||
});
|
||||
self.route(zero, old_acc_wire);
|
||||
|
||||
// Route input wires.
|
||||
for i in 0..12 {
|
||||
let in_wire = GMiMCGate::<F, D, GMIMC_ROUNDS>::wire_input(i);
|
||||
|
||||
@ -15,11 +15,8 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
element: ExtensionTarget<D>,
|
||||
v: Vec<ExtensionTarget<D>>,
|
||||
) -> Vec<ExtensionTarget<D>> {
|
||||
let gate = InsertionGate::<F, D> {
|
||||
vec_size: v.len(),
|
||||
_phantom: PhantomData,
|
||||
};
|
||||
let gate_index = self.add_gate_no_constants(InsertionGate::new(v.len()));
|
||||
let gate = InsertionGate::new(v.len());
|
||||
let gate_index = self.add_gate(gate.clone(), vec![]);
|
||||
|
||||
v.iter().enumerate().for_each(|(i, &val)| {
|
||||
self.route_extension(
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::circuit_builder::CircuitBuilder;
|
||||
use crate::field::extension_field::target::ExtensionTarget;
|
||||
use crate::field::extension_field::Extendable;
|
||||
@ -33,12 +31,8 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
interpolation_points: &[(Target, ExtensionTarget<D>)],
|
||||
evaluation_point: ExtensionTarget<D>,
|
||||
) -> ExtensionTarget<D> {
|
||||
let gate = InterpolationGate::<F, D> {
|
||||
num_points: interpolation_points.len(),
|
||||
_phantom: PhantomData,
|
||||
};
|
||||
let gate_index =
|
||||
self.add_gate_no_constants(InterpolationGate::new(interpolation_points.len()));
|
||||
let gate = InterpolationGate::new(interpolation_points.len());
|
||||
let gate_index = self.add_gate(gate.clone(), vec![]);
|
||||
for (i, &(p, v)) in interpolation_points.iter().enumerate() {
|
||||
self.route(p, Target::wire(gate_index, gate.wire_point(i)));
|
||||
self.route_extension(
|
||||
|
||||
@ -5,6 +5,6 @@ pub mod insert;
|
||||
pub mod interpolation;
|
||||
pub mod polynomial;
|
||||
pub mod range_check;
|
||||
pub mod rotate;
|
||||
pub mod select;
|
||||
pub mod split_base;
|
||||
pub(crate) mod split_join;
|
||||
|
||||
@ -14,6 +14,13 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
self.route(x, sum);
|
||||
}
|
||||
|
||||
/// Returns the first `num_low_bits` little-endian bits of `x`.
|
||||
pub fn low_bits(&mut self, x: Target, num_low_bits: usize, num_bits: usize) -> Vec<Target> {
|
||||
let mut res = self.split_le(x, num_bits);
|
||||
res.truncate(num_low_bits);
|
||||
res
|
||||
}
|
||||
|
||||
/// Returns `(a,b)` such that `x = a + 2^n_log * b` with `a < 2^n_log`.
|
||||
/// `x` is assumed to be range-checked for having `num_bits` bits.
|
||||
pub fn split_low_high(&mut self, x: Target, n_log: usize, num_bits: usize) -> (Target, Target) {
|
||||
|
||||
@ -1,167 +0,0 @@
|
||||
use crate::circuit_builder::CircuitBuilder;
|
||||
use crate::field::extension_field::target::ExtensionTarget;
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::target::Target;
|
||||
use crate::util::log2_ceil;
|
||||
|
||||
impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
/// Selects `x` or `y` based on `b`, which is assumed to be binary.
|
||||
/// In particular, this returns `if b { x } else { y }`.
|
||||
/// Note: This does not range-check `b`.
|
||||
// TODO: This uses 10 gates per call. If addends are added to `MulExtensionGate`, this will be
|
||||
// reduced to 2 gates. We could also use a new degree 2 `SelectGate` for this.
|
||||
// If `num_routed_wire` is larger than 26, we could batch two `select` in one gate.
|
||||
pub fn select(
|
||||
&mut self,
|
||||
b: Target,
|
||||
x: ExtensionTarget<D>,
|
||||
y: ExtensionTarget<D>,
|
||||
) -> ExtensionTarget<D> {
|
||||
let b_y_minus_y = self.scalar_mul_sub_extension(b, y, y);
|
||||
self.scalar_mul_sub_extension(b, x, b_y_minus_y)
|
||||
}
|
||||
|
||||
/// Left-rotates an array `k` times if `b=1` else return the same array.
|
||||
pub fn rotate_left_fixed(
|
||||
&mut self,
|
||||
b: Target,
|
||||
k: usize,
|
||||
v: &[ExtensionTarget<D>],
|
||||
) -> Vec<ExtensionTarget<D>> {
|
||||
let len = v.len();
|
||||
debug_assert!(k < len, "Trying to rotate by more than the vector length.");
|
||||
let mut res = Vec::new();
|
||||
|
||||
for i in 0..len {
|
||||
res.push(self.select(b, v[(i + k) % len], v[i]));
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
/// Left-rotates an array `k` times if `b=1` else return the same array.
|
||||
pub fn rotate_right_fixed(
|
||||
&mut self,
|
||||
b: Target,
|
||||
k: usize,
|
||||
v: &[ExtensionTarget<D>],
|
||||
) -> Vec<ExtensionTarget<D>> {
|
||||
let len = v.len();
|
||||
debug_assert!(k < len, "Trying to rotate by more than the vector length.");
|
||||
let mut res = Vec::new();
|
||||
|
||||
for i in 0..len {
|
||||
res.push(self.select(b, v[(len + i - k) % len], v[i]));
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
/// Left-rotates an vector by the `Target` having bits given in little-endian by `num_rotation_bits`.
|
||||
pub fn rotate_left_from_bits(
|
||||
&mut self,
|
||||
num_rotation_bits: &[Target],
|
||||
v: &[ExtensionTarget<D>],
|
||||
) -> Vec<ExtensionTarget<D>> {
|
||||
let mut v = v.to_vec();
|
||||
|
||||
for i in 0..num_rotation_bits.len() {
|
||||
v = self.rotate_left_fixed(num_rotation_bits[i], 1 << i, &v);
|
||||
}
|
||||
|
||||
v
|
||||
}
|
||||
|
||||
pub fn rotate_right_from_bits(
|
||||
&mut self,
|
||||
num_rotation_bits: &[Target],
|
||||
v: &[ExtensionTarget<D>],
|
||||
) -> Vec<ExtensionTarget<D>> {
|
||||
let mut v = v.to_vec();
|
||||
|
||||
for i in 0..num_rotation_bits.len() {
|
||||
v = self.rotate_right_fixed(num_rotation_bits[i], 1 << i, &v);
|
||||
}
|
||||
|
||||
v
|
||||
}
|
||||
|
||||
/// Left-rotates an array by `num_rotation`. Assumes that `num_rotation` is range-checked to be
|
||||
/// less than `2^len_bits`.
|
||||
pub fn rotate_left(
|
||||
&mut self,
|
||||
num_rotation: Target,
|
||||
v: &[ExtensionTarget<D>],
|
||||
) -> Vec<ExtensionTarget<D>> {
|
||||
let len_bits = log2_ceil(v.len());
|
||||
let bits = self.split_le(num_rotation, len_bits);
|
||||
|
||||
self.rotate_left_from_bits(&bits, v)
|
||||
}
|
||||
|
||||
pub fn rotate_right(
|
||||
&mut self,
|
||||
num_rotation: Target,
|
||||
v: &[ExtensionTarget<D>],
|
||||
) -> Vec<ExtensionTarget<D>> {
|
||||
let len_bits = log2_ceil(v.len());
|
||||
let bits = self.split_le(num_rotation, len_bits);
|
||||
|
||||
self.rotate_right_from_bits(&bits, v)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use anyhow::Result;
|
||||
|
||||
use super::*;
|
||||
use crate::circuit_data::CircuitConfig;
|
||||
use crate::field::crandall_field::CrandallField;
|
||||
use crate::field::extension_field::quartic::QuarticCrandallField;
|
||||
use crate::field::field::Field;
|
||||
use crate::verifier::verify;
|
||||
use crate::witness::PartialWitness;
|
||||
|
||||
fn real_rotate<const D: usize>(
|
||||
num_rotation: usize,
|
||||
v: &[ExtensionTarget<D>],
|
||||
) -> Vec<ExtensionTarget<D>> {
|
||||
let mut res = v.to_vec();
|
||||
res.rotate_left(num_rotation);
|
||||
res
|
||||
}
|
||||
|
||||
fn test_rotate_given_len(len: usize) -> Result<()> {
|
||||
type F = CrandallField;
|
||||
type FF = QuarticCrandallField;
|
||||
let config = CircuitConfig::large_config();
|
||||
let mut builder = CircuitBuilder::<F, 4>::new(config);
|
||||
let v = (0..len)
|
||||
.map(|_| builder.constant_extension(FF::rand()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for i in 0..len {
|
||||
let it = builder.constant(F::from_canonical_usize(i));
|
||||
let rotated = real_rotate(i, &v);
|
||||
let purported_rotated = builder.rotate_left(it, &v);
|
||||
|
||||
for (x, y) in rotated.into_iter().zip(purported_rotated) {
|
||||
builder.assert_equal_extension(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
let data = builder.build();
|
||||
let proof = data.prove(PartialWitness::new())?;
|
||||
|
||||
verify(proof, &data.verifier_only, &data.common)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rotate() -> Result<()> {
|
||||
for len in 1..5 {
|
||||
test_rotate_given_len(len)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
76
src/gadgets/select.rs
Normal file
76
src/gadgets/select.rs
Normal file
@ -0,0 +1,76 @@
|
||||
use crate::circuit_builder::CircuitBuilder;
|
||||
use crate::field::extension_field::target::ExtensionTarget;
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::gates::arithmetic::ArithmeticExtensionGate;
|
||||
use crate::target::Target;
|
||||
|
||||
impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
/// Selects `x` or `y` based on `b`, which is assumed to be binary, i.e., this returns `if b { x } else { y }`.
|
||||
/// This expression is gotten as `bx - (by-y)`, which can be computed with a single `ArithmeticExtensionGate`.
|
||||
/// Note: This does not range-check `b`.
|
||||
pub fn select_ext(
|
||||
&mut self,
|
||||
b: Target,
|
||||
x: ExtensionTarget<D>,
|
||||
y: ExtensionTarget<D>,
|
||||
) -> ExtensionTarget<D> {
|
||||
let b_ext = self.convert_to_ext(b);
|
||||
let gate = self.num_gates();
|
||||
// Holds `by - y`.
|
||||
let first_out =
|
||||
ExtensionTarget::from_range(gate, ArithmeticExtensionGate::<D>::wires_first_output());
|
||||
self.double_arithmetic_extension(F::ONE, F::NEG_ONE, b_ext, y, y, b_ext, x, first_out)
|
||||
.1
|
||||
}
|
||||
|
||||
/// See `select_ext`.
|
||||
pub fn select(&mut self, b: Target, x: Target, y: Target) -> Target {
|
||||
let x_ext = self.convert_to_ext(x);
|
||||
let y_ext = self.convert_to_ext(y);
|
||||
self.select_ext(b, x_ext, y_ext).to_target_array()[0]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use anyhow::Result;
|
||||
|
||||
use super::*;
|
||||
use crate::circuit_data::CircuitConfig;
|
||||
use crate::field::crandall_field::CrandallField;
|
||||
use crate::field::extension_field::quartic::QuarticCrandallField;
|
||||
use crate::field::field::Field;
|
||||
use crate::verifier::verify;
|
||||
use crate::witness::PartialWitness;
|
||||
|
||||
#[test]
|
||||
fn test_select() -> Result<()> {
|
||||
type F = CrandallField;
|
||||
type FF = QuarticCrandallField;
|
||||
let config = CircuitConfig::large_config();
|
||||
let mut builder = CircuitBuilder::<F, 4>::new(config);
|
||||
let mut pw = PartialWitness::new();
|
||||
|
||||
let (x, y) = (FF::rand(), FF::rand());
|
||||
let xt = builder.add_virtual_extension_target();
|
||||
let yt = builder.add_virtual_extension_target();
|
||||
let truet = builder.add_virtual_target();
|
||||
let falset = builder.add_virtual_target();
|
||||
|
||||
pw.set_extension_target(xt, x);
|
||||
pw.set_extension_target(yt, y);
|
||||
pw.set_target(truet, F::ONE);
|
||||
pw.set_target(falset, F::ZERO);
|
||||
|
||||
let should_be_x = builder.select_ext(truet, xt, yt);
|
||||
let should_be_y = builder.select_ext(falset, xt, yt);
|
||||
|
||||
builder.assert_equal_extension(should_be_x, xt);
|
||||
builder.assert_equal_extension(should_be_y, yt);
|
||||
|
||||
let data = builder.build();
|
||||
let proof = data.prove(pw)?;
|
||||
|
||||
verify(proof, &data.verifier_only, &data.common)
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,12 @@
|
||||
use std::borrow::Borrow;
|
||||
|
||||
use crate::circuit_builder::CircuitBuilder;
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::field::field::Field;
|
||||
use crate::gates::base_sum::BaseSumGate;
|
||||
use crate::generator::{GeneratedValues, SimpleGenerator};
|
||||
use crate::target::Target;
|
||||
use crate::witness::PartialWitness;
|
||||
|
||||
impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
/// Split the given element into a list of targets, where each one represents a
|
||||
@ -11,14 +16,12 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
x: Target,
|
||||
num_limbs: usize,
|
||||
) -> Vec<Target> {
|
||||
let gate = self.add_gate(BaseSumGate::<B>::new(num_limbs), vec![]);
|
||||
let gate_type = BaseSumGate::<B>::new(num_limbs);
|
||||
let gate = self.add_gate(gate_type.clone(), vec![]);
|
||||
let sum = Target::wire(gate, BaseSumGate::<B>::WIRE_SUM);
|
||||
self.route(x, sum);
|
||||
|
||||
Target::wires_from_range(
|
||||
gate,
|
||||
BaseSumGate::<B>::START_LIMBS..BaseSumGate::<B>::START_LIMBS + num_limbs,
|
||||
)
|
||||
Target::wires_from_range(gate, gate_type.limbs())
|
||||
}
|
||||
|
||||
/// Asserts that `x`'s big-endian bit representation has at least `leading_zeros` leading zeros.
|
||||
@ -33,11 +36,65 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
|
||||
Target::wire(gate, BaseSumGate::<B>::WIRE_REVERSED_SUM)
|
||||
}
|
||||
|
||||
/// Takes an iterator of bits `(b_i)` and returns `sum b_i * 2^i`, i.e.,
|
||||
/// the number with little-endian bit representation given by `bits`.
|
||||
pub(crate) fn le_sum(
|
||||
&mut self,
|
||||
bits: impl ExactSizeIterator<Item = impl Borrow<Target>> + Clone,
|
||||
) -> Target {
|
||||
let num_bits = bits.len();
|
||||
debug_assert!(
|
||||
BaseSumGate::<2>::START_LIMBS + num_bits <= self.config.num_routed_wires,
|
||||
"Not enough routed wires."
|
||||
);
|
||||
let gate_index = self.add_gate(BaseSumGate::<2>::new(num_bits), vec![]);
|
||||
for (limb, wire) in bits
|
||||
.clone()
|
||||
.zip(BaseSumGate::<2>::START_LIMBS..BaseSumGate::<2>::START_LIMBS + num_bits)
|
||||
{
|
||||
self.route(*limb.borrow(), Target::wire(gate_index, wire));
|
||||
}
|
||||
|
||||
self.add_generator(BaseSumGenerator::<2> {
|
||||
gate_index,
|
||||
limbs: bits.map(|l| *l.borrow()).collect(),
|
||||
});
|
||||
|
||||
Target::wire(gate_index, BaseSumGate::<2>::WIRE_SUM)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct BaseSumGenerator<const B: usize> {
|
||||
gate_index: usize,
|
||||
limbs: Vec<Target>,
|
||||
}
|
||||
|
||||
impl<F: Field, const B: usize> SimpleGenerator<F> for BaseSumGenerator<B> {
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
self.limbs.clone()
|
||||
}
|
||||
|
||||
fn run_once(&self, witness: &PartialWitness<F>) -> GeneratedValues<F> {
|
||||
let sum = self
|
||||
.limbs
|
||||
.iter()
|
||||
.map(|&t| witness.get_target(t))
|
||||
.rev()
|
||||
.fold(F::ZERO, |acc, limb| acc * F::from_canonical_usize(B) + limb);
|
||||
|
||||
GeneratedValues::singleton_target(
|
||||
Target::wire(self.gate_index, BaseSumGate::<B>::WIRE_SUM),
|
||||
sum,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use anyhow::Result;
|
||||
use rand::{thread_rng, Rng};
|
||||
|
||||
use super::*;
|
||||
use crate::circuit_data::CircuitConfig;
|
||||
@ -73,4 +130,36 @@ mod tests {
|
||||
|
||||
verify(proof, &data.verifier_only, &data.common)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_base_sum() -> Result<()> {
|
||||
type F = CrandallField;
|
||||
let config = CircuitConfig::large_config();
|
||||
let mut builder = CircuitBuilder::<F, 4>::new(config);
|
||||
|
||||
let n = thread_rng().gen_range(0, 1 << 10);
|
||||
let x = builder.constant(F::from_canonical_usize(n));
|
||||
|
||||
let zero = builder.zero();
|
||||
let one = builder.one();
|
||||
|
||||
let y = builder.le_sum(
|
||||
(0..10)
|
||||
.scan(n, |acc, _| {
|
||||
let tmp = *acc % 2;
|
||||
*acc /= 2;
|
||||
Some(if tmp == 1 { one } else { zero })
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.iter(),
|
||||
);
|
||||
|
||||
builder.assert_equal(x, y);
|
||||
|
||||
let data = builder.build();
|
||||
|
||||
let proof = data.prove(PartialWitness::new())?;
|
||||
|
||||
verify(proof, &data.verifier_only, &data.common)
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
let bits_per_gate = self.config.num_routed_wires - BaseSumGate::<2>::START_LIMBS;
|
||||
let k = ceil_div_usize(num_bits, bits_per_gate);
|
||||
let gates = (0..k)
|
||||
.map(|_| self.add_gate_no_constants(BaseSumGate::<2>::new(bits_per_gate)))
|
||||
.map(|_| self.add_gate(BaseSumGate::<2>::new(bits_per_gate), vec![]))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut bits = Vec::with_capacity(num_bits);
|
||||
|
||||
@ -3,10 +3,11 @@ use std::ops::Range;
|
||||
use crate::circuit_builder::CircuitBuilder;
|
||||
use crate::field::extension_field::target::ExtensionTarget;
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::gates::gate::{Gate, GateRef};
|
||||
use crate::field::extension_field::FieldExtension;
|
||||
use crate::gates::gate::Gate;
|
||||
use crate::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||
use crate::target::Target;
|
||||
use crate::vars::{EvaluationTargets, EvaluationVars};
|
||||
use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||
use crate::witness::PartialWitness;
|
||||
|
||||
/// A gate which can a linear combination `c0*x*y+c1*z` twice with the same `x`.
|
||||
@ -14,10 +15,6 @@ use crate::witness::PartialWitness;
|
||||
pub struct ArithmeticExtensionGate<const D: usize>;
|
||||
|
||||
impl<const D: usize> ArithmeticExtensionGate<D> {
|
||||
pub fn new<F: Extendable<D>>() -> GateRef<F, D> {
|
||||
GateRef::new(ArithmeticExtensionGate)
|
||||
}
|
||||
|
||||
pub fn wires_first_multiplicand_0() -> Range<usize> {
|
||||
0..D
|
||||
}
|
||||
@ -74,6 +71,31 @@ impl<F: Extendable<D>, const D: usize> Gate<F, D> for ArithmeticExtensionGate<D>
|
||||
constraints
|
||||
}
|
||||
|
||||
fn eval_unfiltered_base(&self, vars: EvaluationVarsBase<F>) -> Vec<F> {
|
||||
let const_0 = vars.local_constants[0];
|
||||
let const_1 = vars.local_constants[1];
|
||||
|
||||
let first_multiplicand_0 = vars.get_local_ext(Self::wires_first_multiplicand_0());
|
||||
let first_multiplicand_1 = vars.get_local_ext(Self::wires_first_multiplicand_1());
|
||||
let first_addend = vars.get_local_ext(Self::wires_first_addend());
|
||||
let second_multiplicand_0 = vars.get_local_ext(Self::wires_second_multiplicand_0());
|
||||
let second_multiplicand_1 = vars.get_local_ext(Self::wires_second_multiplicand_1());
|
||||
let second_addend = vars.get_local_ext(Self::wires_second_addend());
|
||||
let first_output = vars.get_local_ext(Self::wires_first_output());
|
||||
let second_output = vars.get_local_ext(Self::wires_second_output());
|
||||
|
||||
let first_computed_output = first_multiplicand_0 * first_multiplicand_1 * const_0.into()
|
||||
+ first_addend * const_1.into();
|
||||
let second_computed_output = second_multiplicand_0 * second_multiplicand_1 * const_0.into()
|
||||
+ second_addend * const_1.into();
|
||||
|
||||
let mut constraints = (first_output - first_computed_output)
|
||||
.to_basefield_array()
|
||||
.to_vec();
|
||||
constraints.extend((second_output - second_computed_output).to_basefield_array());
|
||||
constraints
|
||||
}
|
||||
|
||||
fn eval_unfiltered_recursively(
|
||||
&self,
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
@ -234,6 +256,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn low_degree() {
|
||||
test_low_degree(ArithmeticExtensionGate::<4>::new::<CrandallField>())
|
||||
test_low_degree::<CrandallField, _, 4>(ArithmeticExtensionGate)
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,19 +8,19 @@ use crate::gates::gate::{Gate, GateRef};
|
||||
use crate::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||
use crate::plonk_common::{reduce_with_powers, reduce_with_powers_recursive};
|
||||
use crate::target::Target;
|
||||
use crate::vars::{EvaluationTargets, EvaluationVars};
|
||||
use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||
use crate::witness::PartialWitness;
|
||||
|
||||
/// A gate which can decompose a number into base B little-endian limbs,
|
||||
/// and compute the limb-reversed (i.e. big-endian) sum.
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BaseSumGate<const B: usize> {
|
||||
num_limbs: usize,
|
||||
}
|
||||
|
||||
impl<const B: usize> BaseSumGate<B> {
|
||||
pub fn new<F: Extendable<D>, const D: usize>(num_limbs: usize) -> GateRef<F, D> {
|
||||
GateRef::new(BaseSumGate::<B> { num_limbs })
|
||||
pub fn new(num_limbs: usize) -> Self {
|
||||
Self { num_limbs }
|
||||
}
|
||||
|
||||
pub const WIRE_SUM: usize = 0;
|
||||
@ -57,6 +57,20 @@ impl<F: Extendable<D>, const D: usize, const B: usize> Gate<F, D> for BaseSumGat
|
||||
constraints
|
||||
}
|
||||
|
||||
fn eval_unfiltered_base(&self, vars: EvaluationVarsBase<F>) -> Vec<F> {
|
||||
let sum = vars.local_wires[Self::WIRE_SUM];
|
||||
let reversed_sum = vars.local_wires[Self::WIRE_REVERSED_SUM];
|
||||
let mut limbs = vars.local_wires[self.limbs()].to_vec();
|
||||
let computed_sum = reduce_with_powers(&limbs, F::from_canonical_usize(B));
|
||||
limbs.reverse();
|
||||
let computed_reversed_sum = reduce_with_powers(&limbs, F::from_canonical_usize(B));
|
||||
let mut constraints = vec![computed_sum - sum, computed_reversed_sum - reversed_sum];
|
||||
for limb in limbs {
|
||||
constraints.push((0..B).map(|i| limb - F::from_canonical_usize(i)).product());
|
||||
}
|
||||
constraints
|
||||
}
|
||||
|
||||
fn eval_unfiltered_recursively(
|
||||
&self,
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
@ -172,10 +186,11 @@ impl<F: Field, const B: usize> SimpleGenerator<F> for BaseSplitGenerator<B> {
|
||||
mod tests {
|
||||
use crate::field::crandall_field::CrandallField;
|
||||
use crate::gates::base_sum::BaseSumGate;
|
||||
use crate::gates::gate::GateRef;
|
||||
use crate::gates::gate_testing::test_low_degree;
|
||||
|
||||
#[test]
|
||||
fn low_degree() {
|
||||
test_low_degree(BaseSumGate::<6>::new::<CrandallField, 4>(11))
|
||||
test_low_degree::<CrandallField, _, 4>(BaseSumGate::<6>::new(11))
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ use crate::field::field::Field;
|
||||
use crate::gates::gate::{Gate, GateRef};
|
||||
use crate::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||
use crate::target::Target;
|
||||
use crate::vars::{EvaluationTargets, EvaluationVars};
|
||||
use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||
use crate::wire::Wire;
|
||||
use crate::witness::PartialWitness;
|
||||
|
||||
@ -13,10 +13,6 @@ use crate::witness::PartialWitness;
|
||||
pub struct ConstantGate;
|
||||
|
||||
impl ConstantGate {
|
||||
pub fn get<F: Extendable<D>, const D: usize>() -> GateRef<F, D> {
|
||||
GateRef::new(ConstantGate)
|
||||
}
|
||||
|
||||
pub const CONST_INPUT: usize = 0;
|
||||
|
||||
pub const WIRE_OUTPUT: usize = 0;
|
||||
@ -33,6 +29,12 @@ impl<F: Extendable<D>, const D: usize> Gate<F, D> for ConstantGate {
|
||||
vec![output - input]
|
||||
}
|
||||
|
||||
fn eval_unfiltered_base(&self, vars: EvaluationVarsBase<F>) -> Vec<F> {
|
||||
let input = vars.local_constants[Self::CONST_INPUT];
|
||||
let output = vars.local_wires[Self::WIRE_OUTPUT];
|
||||
vec![output - input]
|
||||
}
|
||||
|
||||
fn eval_unfiltered_recursively(
|
||||
&self,
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
@ -100,6 +102,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn low_degree() {
|
||||
test_low_degree(ConstantGate::get::<CrandallField, 4>())
|
||||
test_low_degree::<CrandallField, _, 4>(ConstantGate)
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,7 +139,7 @@ impl<F: Extendable<D>, const D: usize> Debug for GateRef<F, D> {
|
||||
|
||||
/// A gate along with any constants used to configure it.
|
||||
pub struct GateInstance<F: Extendable<D>, const D: usize> {
|
||||
pub gate_type: GateRef<F, D>,
|
||||
pub gate_ref: GateRef<F, D>,
|
||||
pub constants: Vec<F>,
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::field::field::Field;
|
||||
use crate::gates::gate::GateRef;
|
||||
use crate::gates::gate::{Gate, GateRef};
|
||||
use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues};
|
||||
use crate::proof::Hash;
|
||||
use crate::util::{log2_ceil, transpose};
|
||||
@ -11,8 +11,7 @@ const WITNESS_DEGREE: usize = WITNESS_SIZE - 1;
|
||||
|
||||
/// Tests that the constraints imposed by the given gate are low-degree by applying them to random
|
||||
/// low-degree witness polynomials.
|
||||
pub(crate) fn test_low_degree<F: Extendable<D>, const D: usize>(gate: GateRef<F, D>) {
|
||||
let gate = gate.0;
|
||||
pub(crate) fn test_low_degree<F: Extendable<D>, G: Gate<F, D>, const D: usize>(gate: G) {
|
||||
let rate_bits = log2_ceil(gate.degree() + 1);
|
||||
|
||||
let wire_ldes = random_low_degree_matrix::<F::Extension>(gate.num_wires(), rate_bits);
|
||||
|
||||
@ -237,12 +237,12 @@ mod tests {
|
||||
const D: usize = 4;
|
||||
|
||||
let gates = vec![
|
||||
NoopGate::get::<F, D>(),
|
||||
ConstantGate::get(),
|
||||
ArithmeticExtensionGate::new(),
|
||||
BaseSumGate::<4>::new(4),
|
||||
GMiMCGate::<F, D, GMIMC_ROUNDS>::with_automatic_constants(),
|
||||
InterpolationGate::new(4),
|
||||
GateRef::new(NoopGate),
|
||||
GateRef::new(ConstantGate),
|
||||
GateRef::new(ArithmeticExtensionGate),
|
||||
GateRef::new(BaseSumGate::<4>::new(4)),
|
||||
GateRef::new(GMiMCGate::<F, D, GMIMC_ROUNDS>::new_automatic_constants()),
|
||||
GateRef::new(InterpolationGate::new(4)),
|
||||
];
|
||||
let len = gates.len();
|
||||
|
||||
|
||||
@ -4,11 +4,11 @@ use crate::circuit_builder::CircuitBuilder;
|
||||
use crate::field::extension_field::target::ExtensionTarget;
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::field::field::Field;
|
||||
use crate::gates::gate::{Gate, GateRef};
|
||||
use crate::gates::gate::Gate;
|
||||
use crate::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||
use crate::gmimc::gmimc_automatic_constants;
|
||||
use crate::target::Target;
|
||||
use crate::vars::{EvaluationTargets, EvaluationVars};
|
||||
use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||
use crate::wire::Wire;
|
||||
use crate::witness::PartialWitness;
|
||||
|
||||
@ -28,14 +28,13 @@ pub struct GMiMCGate<F: Extendable<D>, const D: usize, const R: usize> {
|
||||
}
|
||||
|
||||
impl<F: Extendable<D>, const D: usize, const R: usize> GMiMCGate<F, D, R> {
|
||||
pub fn with_constants(constants: Arc<[F; R]>) -> GateRef<F, D> {
|
||||
let gate = GMiMCGate::<F, D, R> { constants };
|
||||
GateRef::new(gate)
|
||||
pub fn new(constants: Arc<[F; R]>) -> Self {
|
||||
Self { constants }
|
||||
}
|
||||
|
||||
pub fn with_automatic_constants() -> GateRef<F, D> {
|
||||
pub fn new_automatic_constants() -> Self {
|
||||
let constants = Arc::new(gmimc_automatic_constants::<F, R>());
|
||||
Self::with_constants(constants)
|
||||
Self::new(constants)
|
||||
}
|
||||
|
||||
/// The wire index for the `i`th input to the permutation.
|
||||
@ -48,22 +47,18 @@ impl<F: Extendable<D>, const D: usize, const R: usize> GMiMCGate<F, D, R> {
|
||||
W + i
|
||||
}
|
||||
|
||||
/// Used to incrementally compute the index of the leaf based on a series of swap bits.
|
||||
pub const WIRE_INDEX_ACCUMULATOR_OLD: usize = 2 * W;
|
||||
pub const WIRE_INDEX_ACCUMULATOR_NEW: usize = 2 * W + 1;
|
||||
|
||||
/// If this is set to 1, the first four inputs will be swapped with the next four inputs. This
|
||||
/// is useful for ordering hashes in Merkle proofs. Otherwise, this should be set to 0.
|
||||
pub const WIRE_SWAP: usize = 2 * W + 2;
|
||||
pub const WIRE_SWAP: usize = 2 * W;
|
||||
|
||||
/// A wire which stores the input to the `i`th cubing.
|
||||
fn wire_cubing_input(i: usize) -> usize {
|
||||
2 * W + 3 + i
|
||||
2 * W + 1 + i
|
||||
}
|
||||
|
||||
/// End of wire indices, exclusive.
|
||||
fn end() -> usize {
|
||||
2 * W + 3 + R
|
||||
2 * W + 1 + R
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,11 +74,6 @@ impl<F: Extendable<D>, const D: usize, const R: usize> Gate<F, D> for GMiMCGate<
|
||||
let swap = vars.local_wires[Self::WIRE_SWAP];
|
||||
constraints.push(swap * (swap - F::Extension::ONE));
|
||||
|
||||
let old_index_acc = vars.local_wires[Self::WIRE_INDEX_ACCUMULATOR_OLD];
|
||||
let new_index_acc = vars.local_wires[Self::WIRE_INDEX_ACCUMULATOR_NEW];
|
||||
let computed_new_index_acc = F::Extension::TWO * old_index_acc + swap;
|
||||
constraints.push(computed_new_index_acc - new_index_acc);
|
||||
|
||||
let mut state = Vec::with_capacity(12);
|
||||
for i in 0..4 {
|
||||
let a = vars.local_wires[i];
|
||||
@ -121,6 +111,50 @@ impl<F: Extendable<D>, const D: usize, const R: usize> Gate<F, D> for GMiMCGate<
|
||||
constraints
|
||||
}
|
||||
|
||||
fn eval_unfiltered_base(&self, vars: EvaluationVarsBase<F>) -> Vec<F> {
|
||||
let mut constraints = Vec::with_capacity(self.num_constraints());
|
||||
|
||||
// Assert that `swap` is binary.
|
||||
let swap = vars.local_wires[Self::WIRE_SWAP];
|
||||
constraints.push(swap * (swap - F::ONE));
|
||||
|
||||
let mut state = Vec::with_capacity(12);
|
||||
for i in 0..4 {
|
||||
let a = vars.local_wires[i];
|
||||
let b = vars.local_wires[i + 4];
|
||||
state.push(a + swap * (b - a));
|
||||
}
|
||||
for i in 0..4 {
|
||||
let a = vars.local_wires[i + 4];
|
||||
let b = vars.local_wires[i];
|
||||
state.push(a + swap * (b - a));
|
||||
}
|
||||
for i in 8..12 {
|
||||
state.push(vars.local_wires[i]);
|
||||
}
|
||||
|
||||
// Value that is implicitly added to each element.
|
||||
// See https://affine.group/2020/02/starkware-challenge
|
||||
let mut addition_buffer = F::ZERO;
|
||||
|
||||
for r in 0..R {
|
||||
let active = r % W;
|
||||
let cubing_input = state[active] + addition_buffer + self.constants[r].into();
|
||||
let cubing_input_wire = vars.local_wires[Self::wire_cubing_input(r)];
|
||||
constraints.push(cubing_input - cubing_input_wire);
|
||||
let f = cubing_input_wire.cube();
|
||||
addition_buffer += f;
|
||||
state[active] -= f;
|
||||
}
|
||||
|
||||
for i in 0..W {
|
||||
state[i] += addition_buffer;
|
||||
constraints.push(state[i] - vars.local_wires[Self::wire_output(i)]);
|
||||
}
|
||||
|
||||
constraints
|
||||
}
|
||||
|
||||
fn eval_unfiltered_recursively(
|
||||
&self,
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
@ -131,13 +165,6 @@ impl<F: Extendable<D>, const D: usize, const R: usize> Gate<F, D> for GMiMCGate<
|
||||
let swap = vars.local_wires[Self::WIRE_SWAP];
|
||||
constraints.push(builder.mul_sub_extension(swap, swap, swap));
|
||||
|
||||
let old_index_acc = vars.local_wires[Self::WIRE_INDEX_ACCUMULATOR_OLD];
|
||||
let new_index_acc = vars.local_wires[Self::WIRE_INDEX_ACCUMULATOR_NEW];
|
||||
// computed_new_index_acc = 2 * old_index_acc + swap
|
||||
let two = builder.two_extension();
|
||||
let computed_new_index_acc = builder.mul_add_extension(two, old_index_acc, swap);
|
||||
constraints.push(builder.sub_extension(computed_new_index_acc, new_index_acc));
|
||||
|
||||
let mut state = Vec::with_capacity(12);
|
||||
for i in 0..4 {
|
||||
let a = vars.local_wires[i];
|
||||
@ -207,7 +234,7 @@ impl<F: Extendable<D>, const D: usize, const R: usize> Gate<F, D> for GMiMCGate<
|
||||
}
|
||||
|
||||
fn num_constraints(&self) -> usize {
|
||||
R + W + 2
|
||||
R + W + 1
|
||||
}
|
||||
}
|
||||
|
||||
@ -221,12 +248,11 @@ impl<F: Extendable<D>, const D: usize, const R: usize> SimpleGenerator<F>
|
||||
for GMiMCGenerator<F, D, R>
|
||||
{
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
let mut dep_input_indices = Vec::with_capacity(W + 2);
|
||||
let mut dep_input_indices = Vec::with_capacity(W + 1);
|
||||
for i in 0..W {
|
||||
dep_input_indices.push(GMiMCGate::<F, D, R>::wire_input(i));
|
||||
}
|
||||
dep_input_indices.push(GMiMCGate::<F, D, R>::WIRE_SWAP);
|
||||
dep_input_indices.push(GMiMCGate::<F, D, R>::WIRE_INDEX_ACCUMULATOR_OLD);
|
||||
|
||||
dep_input_indices
|
||||
.into_iter()
|
||||
@ -240,7 +266,7 @@ impl<F: Extendable<D>, const D: usize, const R: usize> SimpleGenerator<F>
|
||||
}
|
||||
|
||||
fn run_once(&self, witness: &PartialWitness<F>) -> GeneratedValues<F> {
|
||||
let mut result = GeneratedValues::with_capacity(R + W + 1);
|
||||
let mut result = GeneratedValues::with_capacity(R + W);
|
||||
|
||||
let mut state = (0..W)
|
||||
.map(|i| {
|
||||
@ -262,20 +288,6 @@ impl<F: Extendable<D>, const D: usize, const R: usize> SimpleGenerator<F>
|
||||
}
|
||||
}
|
||||
|
||||
// Update the index accumulator.
|
||||
let old_index_acc_value = witness.get_wire(Wire {
|
||||
gate: self.gate_index,
|
||||
input: GMiMCGate::<F, D, R>::WIRE_INDEX_ACCUMULATOR_OLD,
|
||||
});
|
||||
let new_index_acc_value = F::TWO * old_index_acc_value + swap_value;
|
||||
result.set_wire(
|
||||
Wire {
|
||||
gate: self.gate_index,
|
||||
input: GMiMCGate::<F, D, R>::WIRE_INDEX_ACCUMULATOR_NEW,
|
||||
},
|
||||
new_index_acc_value,
|
||||
);
|
||||
|
||||
// Value that is implicitly added to each element.
|
||||
// See https://affine.group/2020/02/starkware-challenge
|
||||
let mut addition_buffer = F::ZERO;
|
||||
@ -322,6 +334,7 @@ mod tests {
|
||||
use crate::field::crandall_field::CrandallField;
|
||||
use crate::field::extension_field::quartic::QuarticCrandallField;
|
||||
use crate::field::field::Field;
|
||||
use crate::gates::gate::{Gate, GateRef};
|
||||
use crate::gates::gate_testing::test_low_degree;
|
||||
use crate::gates::gmimc::{GMiMCGate, W};
|
||||
use crate::generator::generate_partial_witness;
|
||||
@ -338,24 +351,11 @@ mod tests {
|
||||
const R: usize = 101;
|
||||
let constants = Arc::new([F::TWO; R]);
|
||||
type Gate = GMiMCGate<F, 4, R>;
|
||||
let gate = Gate::with_constants(constants.clone());
|
||||
|
||||
let config = CircuitConfig {
|
||||
num_wires: 134,
|
||||
num_routed_wires: 200,
|
||||
..Default::default()
|
||||
};
|
||||
let gate = Gate::new(constants.clone());
|
||||
|
||||
let permutation_inputs = (0..W).map(F::from_canonical_usize).collect::<Vec<_>>();
|
||||
|
||||
let mut witness = PartialWitness::new();
|
||||
witness.set_wire(
|
||||
Wire {
|
||||
gate: 0,
|
||||
input: Gate::WIRE_INDEX_ACCUMULATOR_OLD,
|
||||
},
|
||||
F::from_canonical_usize(7),
|
||||
);
|
||||
witness.set_wire(
|
||||
Wire {
|
||||
gate: 0,
|
||||
@ -373,7 +373,7 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
let generators = gate.0.generators(0, &[]);
|
||||
let generators = gate.generators(0, &[]);
|
||||
generate_partial_witness(&mut witness, &generators);
|
||||
|
||||
let expected_outputs: [F; W] =
|
||||
@ -386,12 +386,6 @@ mod tests {
|
||||
});
|
||||
assert_eq!(out, expected_outputs[i]);
|
||||
}
|
||||
|
||||
let acc_new = witness.get_wire(Wire {
|
||||
gate: 0,
|
||||
input: Gate::WIRE_INDEX_ACCUMULATOR_NEW,
|
||||
});
|
||||
assert_eq!(acc_new, F::from_canonical_usize(7 * 2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -399,8 +393,7 @@ mod tests {
|
||||
type F = CrandallField;
|
||||
const R: usize = 101;
|
||||
let constants = Arc::new([F::TWO; R]);
|
||||
type Gate = GMiMCGate<F, 4, R>;
|
||||
let gate = Gate::with_constants(constants);
|
||||
let gate = GMiMCGate::<F, 4, R>::new(constants);
|
||||
test_low_degree(gate)
|
||||
}
|
||||
|
||||
@ -414,7 +407,7 @@ mod tests {
|
||||
let mut pw = PartialWitness::<F>::new();
|
||||
let constants = Arc::new([F::TWO; R]);
|
||||
type Gate = GMiMCGate<F, 4, R>;
|
||||
let gate = Gate::with_constants(constants);
|
||||
let gate = Gate::new(constants);
|
||||
|
||||
let wires = FF::rand_vec(Gate::end());
|
||||
let public_inputs_hash = &Hash::rand();
|
||||
@ -424,7 +417,7 @@ mod tests {
|
||||
public_inputs_hash,
|
||||
};
|
||||
|
||||
let ev = gate.0.eval_unfiltered(vars);
|
||||
let ev = gate.eval_unfiltered(vars);
|
||||
|
||||
let wires_t = builder.add_virtual_extension_targets(Gate::end());
|
||||
for i in 0..Gate::end() {
|
||||
@ -440,7 +433,7 @@ mod tests {
|
||||
public_inputs_hash: &public_inputs_hash_t,
|
||||
};
|
||||
|
||||
let ev_t = gate.0.eval_unfiltered_recursively(&mut builder, vars_t);
|
||||
let ev_t = gate.eval_unfiltered_recursively(&mut builder, vars_t);
|
||||
|
||||
assert_eq!(ev.len(), ev_t.len());
|
||||
for (e, e_t) in ev.into_iter().zip(ev_t) {
|
||||
|
||||
@ -9,7 +9,7 @@ use crate::field::field::Field;
|
||||
use crate::gates::gate::{Gate, GateRef};
|
||||
use crate::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||
use crate::target::Target;
|
||||
use crate::vars::{EvaluationTargets, EvaluationVars};
|
||||
use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||
use crate::wire::Wire;
|
||||
use crate::witness::PartialWitness;
|
||||
|
||||
@ -17,16 +17,15 @@ use crate::witness::PartialWitness;
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct InsertionGate<F: Extendable<D>, const D: usize> {
|
||||
pub vec_size: usize,
|
||||
pub _phantom: PhantomData<F>,
|
||||
_phantom: PhantomData<F>,
|
||||
}
|
||||
|
||||
impl<F: Extendable<D>, const D: usize> InsertionGate<F, D> {
|
||||
pub fn new(vec_size: usize) -> GateRef<F, D> {
|
||||
let gate = Self {
|
||||
pub fn new(vec_size: usize) -> Self {
|
||||
Self {
|
||||
vec_size,
|
||||
_phantom: PhantomData,
|
||||
};
|
||||
GateRef::new(gate)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wires_insertion_index(&self) -> usize {
|
||||
@ -114,6 +113,44 @@ impl<F: Extendable<D>, const D: usize> Gate<F, D> for InsertionGate<F, D> {
|
||||
constraints
|
||||
}
|
||||
|
||||
fn eval_unfiltered_base(&self, vars: EvaluationVarsBase<F>) -> Vec<F> {
|
||||
let insertion_index = vars.local_wires[self.wires_insertion_index()];
|
||||
let list_items = (0..self.vec_size)
|
||||
.map(|i| vars.get_local_ext(self.wires_original_list_item(i)))
|
||||
.collect::<Vec<_>>();
|
||||
let output_list_items = (0..=self.vec_size)
|
||||
.map(|i| vars.get_local_ext(self.wires_output_list_item(i)))
|
||||
.collect::<Vec<_>>();
|
||||
let element_to_insert = vars.get_local_ext(self.wires_element_to_insert());
|
||||
|
||||
let mut constraints = Vec::new();
|
||||
let mut already_inserted = F::ZERO;
|
||||
for r in 0..=self.vec_size {
|
||||
let cur_index = F::from_canonical_usize(r);
|
||||
let difference = cur_index - insertion_index;
|
||||
let equality_dummy = vars.local_wires[self.wires_equality_dummy_for_round_r(r)];
|
||||
let insert_here = vars.local_wires[self.wires_insert_here_for_round_r(r)];
|
||||
|
||||
// The two equality constraints.
|
||||
constraints.push(difference * equality_dummy - (F::ONE - insert_here));
|
||||
constraints.push(insert_here * difference);
|
||||
|
||||
let mut new_item = element_to_insert * insert_here.into();
|
||||
if r > 0 {
|
||||
new_item += list_items[r - 1] * already_inserted.into();
|
||||
}
|
||||
already_inserted += insert_here;
|
||||
if r < self.vec_size {
|
||||
new_item += list_items[r] * (F::ONE - already_inserted).into();
|
||||
}
|
||||
|
||||
// Output constraint.
|
||||
constraints.extend((new_item - output_list_items[r]).to_basefield_array());
|
||||
}
|
||||
|
||||
constraints
|
||||
}
|
||||
|
||||
fn eval_unfiltered_recursively(
|
||||
&self,
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
@ -312,8 +349,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn low_degree() {
|
||||
type F = CrandallField;
|
||||
test_low_degree(InsertionGate::<F, 4>::new(4));
|
||||
test_low_degree::<CrandallField, _, 4>(InsertionGate::new(4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -10,8 +10,9 @@ use crate::field::interpolation::interpolant;
|
||||
use crate::gadgets::polynomial::PolynomialCoeffsExtAlgebraTarget;
|
||||
use crate::gates::gate::{Gate, GateRef};
|
||||
use crate::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||
use crate::polynomial::polynomial::PolynomialCoeffs;
|
||||
use crate::target::Target;
|
||||
use crate::vars::{EvaluationTargets, EvaluationVars};
|
||||
use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||
use crate::wire::Wire;
|
||||
use crate::witness::PartialWitness;
|
||||
|
||||
@ -23,16 +24,15 @@ use crate::witness::PartialWitness;
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct InterpolationGate<F: Extendable<D>, const D: usize> {
|
||||
pub num_points: usize,
|
||||
pub _phantom: PhantomData<F>,
|
||||
_phantom: PhantomData<F>,
|
||||
}
|
||||
|
||||
impl<F: Extendable<D>, const D: usize> InterpolationGate<F, D> {
|
||||
pub fn new(num_points: usize) -> GateRef<F, D> {
|
||||
let gate = Self {
|
||||
pub fn new(num_points: usize) -> Self {
|
||||
Self {
|
||||
num_points,
|
||||
_phantom: PhantomData,
|
||||
};
|
||||
GateRef::new(gate)
|
||||
}
|
||||
}
|
||||
|
||||
fn start_points(&self) -> usize {
|
||||
@ -121,6 +121,29 @@ impl<F: Extendable<D>, const D: usize> Gate<F, D> for InterpolationGate<F, D> {
|
||||
constraints
|
||||
}
|
||||
|
||||
fn eval_unfiltered_base(&self, vars: EvaluationVarsBase<F>) -> Vec<F> {
|
||||
let mut constraints = Vec::with_capacity(self.num_constraints());
|
||||
|
||||
let coeffs = (0..self.num_points)
|
||||
.map(|i| vars.get_local_ext(self.wires_coeff(i)))
|
||||
.collect();
|
||||
let interpolant = PolynomialCoeffs::new(coeffs);
|
||||
|
||||
for i in 0..self.num_points {
|
||||
let point = vars.local_wires[self.wire_point(i)];
|
||||
let value = vars.get_local_ext(self.wires_value(i));
|
||||
let computed_value = interpolant.eval(point.into());
|
||||
constraints.extend(&(value - computed_value).to_basefield_array());
|
||||
}
|
||||
|
||||
let evaluation_point = vars.get_local_ext(self.wires_evaluation_point());
|
||||
let evaluation_value = vars.get_local_ext(self.wires_evaluation_value());
|
||||
let computed_evaluation_value = interpolant.eval(evaluation_point);
|
||||
constraints.extend(&(evaluation_value - computed_evaluation_value).to_basefield_array());
|
||||
|
||||
constraints
|
||||
}
|
||||
|
||||
fn eval_unfiltered_recursively(
|
||||
&self,
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
@ -297,7 +320,7 @@ mod tests {
|
||||
#[test]
|
||||
fn low_degree() {
|
||||
type F = CrandallField;
|
||||
test_low_degree(InterpolationGate::<F, 4>::new(4));
|
||||
test_low_degree::<CrandallField, _, 4>(InterpolationGate::new(4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -313,31 +336,15 @@ mod tests {
|
||||
points: Vec<F>,
|
||||
eval_point: FF,
|
||||
) -> Vec<FF> {
|
||||
let mut v = vec![F::ZERO; num_points * 5 + (coeffs.len() + 3) * D];
|
||||
let mut v = Vec::new();
|
||||
v.extend_from_slice(&points);
|
||||
for j in 0..num_points {
|
||||
v[j] = points[j];
|
||||
}
|
||||
for j in 0..num_points {
|
||||
for i in 0..D {
|
||||
v[num_points + D * j + i] = <FF as FieldExtension<D>>::to_basefield_array(
|
||||
&coeffs.eval(points[j].into()),
|
||||
)[i];
|
||||
}
|
||||
}
|
||||
for i in 0..D {
|
||||
v[num_points * 5 + i] =
|
||||
<FF as FieldExtension<D>>::to_basefield_array(&eval_point)[i];
|
||||
}
|
||||
for i in 0..D {
|
||||
v[num_points * 5 + D + i] =
|
||||
<FF as FieldExtension<D>>::to_basefield_array(&coeffs.eval(eval_point))[i];
|
||||
v.extend(coeffs.eval(points[j].into()).0);
|
||||
}
|
||||
v.extend(eval_point.0);
|
||||
v.extend(coeffs.eval(eval_point).0);
|
||||
for i in 0..coeffs.len() {
|
||||
for (j, input) in
|
||||
(0..D).zip(num_points * 5 + (2 + i) * D..num_points * 5 + (3 + i) * D)
|
||||
{
|
||||
v[input] = <FF as FieldExtension<D>>::to_basefield_array(&coeffs.coeffs[i])[j];
|
||||
}
|
||||
v.extend(coeffs.coeffs[i].0);
|
||||
}
|
||||
v.iter().map(|&x| x.into()).collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
@ -3,17 +3,11 @@ use crate::field::extension_field::target::ExtensionTarget;
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::gates::gate::{Gate, GateRef};
|
||||
use crate::generator::WitnessGenerator;
|
||||
use crate::vars::{EvaluationTargets, EvaluationVars};
|
||||
use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||
|
||||
/// A gate which does nothing.
|
||||
pub struct NoopGate;
|
||||
|
||||
impl NoopGate {
|
||||
pub fn get<F: Extendable<D>, const D: usize>() -> GateRef<F, D> {
|
||||
GateRef::new(NoopGate)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Extendable<D>, const D: usize> Gate<F, D> for NoopGate {
|
||||
fn id(&self) -> String {
|
||||
"NoopGate".into()
|
||||
@ -23,6 +17,10 @@ impl<F: Extendable<D>, const D: usize> Gate<F, D> for NoopGate {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
fn eval_unfiltered_base(&self, _vars: EvaluationVarsBase<F>) -> Vec<F> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
fn eval_unfiltered_recursively(
|
||||
&self,
|
||||
_builder: &mut CircuitBuilder<F, D>,
|
||||
@ -64,6 +62,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn low_degree() {
|
||||
test_low_degree(NoopGate::get::<CrandallField, 4>())
|
||||
test_low_degree::<CrandallField, _, 4>(NoopGate)
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,16 +5,12 @@ use crate::field::extension_field::target::ExtensionTarget;
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::gates::gate::{Gate, GateRef};
|
||||
use crate::generator::WitnessGenerator;
|
||||
use crate::vars::{EvaluationTargets, EvaluationVars};
|
||||
use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||
|
||||
/// A gate whose first four wires will be equal to a hash of public inputs.
|
||||
pub struct PublicInputGate;
|
||||
|
||||
impl PublicInputGate {
|
||||
pub fn get<F: Extendable<D>, const D: usize>() -> GateRef<F, D> {
|
||||
GateRef::new(PublicInputGate)
|
||||
}
|
||||
|
||||
pub fn wires_public_inputs_hash() -> Range<usize> {
|
||||
0..4
|
||||
}
|
||||
@ -32,6 +28,13 @@ impl<F: Extendable<D>, const D: usize> Gate<F, D> for PublicInputGate {
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn eval_unfiltered_base(&self, vars: EvaluationVarsBase<F>) -> Vec<F> {
|
||||
Self::wires_public_inputs_hash()
|
||||
.zip(vars.public_inputs_hash.elements)
|
||||
.map(|(wire, hash_part)| vars.local_wires[wire] - hash_part)
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn eval_unfiltered_recursively(
|
||||
&self,
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
@ -79,6 +82,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn low_degree() {
|
||||
test_low_degree(PublicInputGate::get::<CrandallField, 4>())
|
||||
test_low_degree::<CrandallField, _, 4>(PublicInputGate)
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,24 +59,21 @@ pub(crate) fn verify_merkle_proof<F: Field>(
|
||||
|
||||
impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
/// Verifies that the given leaf data is present at the given index in the Merkle tree with the
|
||||
/// given root.
|
||||
/// given root. The index is given by it's little-endian bits.
|
||||
pub(crate) fn verify_merkle_proof(
|
||||
&mut self,
|
||||
leaf_data: Vec<Target>,
|
||||
leaf_index: Target,
|
||||
leaf_index_bits: &[Target],
|
||||
merkle_root: HashTarget,
|
||||
proof: &MerkleProofTarget,
|
||||
) {
|
||||
let zero = self.zero();
|
||||
let height = proof.siblings.len();
|
||||
let purported_index_bits = self.split_le_virtual(leaf_index, height);
|
||||
|
||||
let mut state: HashTarget = self.hash_or_noop(leaf_data);
|
||||
let mut acc_leaf_index = zero;
|
||||
|
||||
for (bit, &sibling) in purported_index_bits.into_iter().zip(&proof.siblings) {
|
||||
let gate = self
|
||||
.add_gate_no_constants(GMiMCGate::<F, D, GMIMC_ROUNDS>::with_automatic_constants());
|
||||
for (&bit, &sibling) in leaf_index_bits.iter().zip(&proof.siblings) {
|
||||
let gate_type = GMiMCGate::<F, D, GMIMC_ROUNDS>::new_automatic_constants();
|
||||
let gate = self.add_gate(gate_type, vec![]);
|
||||
|
||||
let swap_wire = GMiMCGate::<F, D, GMIMC_ROUNDS>::WIRE_SWAP;
|
||||
let swap_wire = Target::Wire(Wire {
|
||||
@ -85,20 +82,6 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
});
|
||||
self.generate_copy(bit, swap_wire);
|
||||
|
||||
let old_acc_wire = GMiMCGate::<F, D, GMIMC_ROUNDS>::WIRE_INDEX_ACCUMULATOR_OLD;
|
||||
let old_acc_wire = Target::Wire(Wire {
|
||||
gate,
|
||||
input: old_acc_wire,
|
||||
});
|
||||
self.route(acc_leaf_index, old_acc_wire);
|
||||
|
||||
let new_acc_wire = GMiMCGate::<F, D, GMIMC_ROUNDS>::WIRE_INDEX_ACCUMULATOR_NEW;
|
||||
let new_acc_wire = Target::Wire(Wire {
|
||||
gate,
|
||||
input: new_acc_wire,
|
||||
});
|
||||
acc_leaf_index = new_acc_wire;
|
||||
|
||||
let input_wires = (0..12)
|
||||
.map(|i| {
|
||||
Target::Wire(Wire {
|
||||
@ -126,10 +109,6 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: this is far from optimal.
|
||||
let leaf_index_rev = self.reverse_limbs::<2>(leaf_index, height);
|
||||
self.assert_equal(acc_leaf_index, leaf_index_rev);
|
||||
|
||||
self.named_assert_hashes_equal(state, merkle_root, "check Merkle root".into())
|
||||
}
|
||||
|
||||
@ -191,13 +170,14 @@ mod tests {
|
||||
pw.set_hash_target(root_t, tree.root);
|
||||
|
||||
let i_c = builder.constant(F::from_canonical_usize(i));
|
||||
let i_bits = builder.split_le(i_c, log_n);
|
||||
|
||||
let data = builder.add_virtual_targets(tree.leaves[i].len());
|
||||
for j in 0..data.len() {
|
||||
pw.set_target(data[j], tree.leaves[i][j]);
|
||||
}
|
||||
|
||||
builder.verify_merkle_proof(data, i_c, root_t, &proof_t);
|
||||
builder.verify_merkle_proof(data, &i_bits, root_t, &proof_t);
|
||||
|
||||
let data = builder.build();
|
||||
let proof = data.prove(pw)?;
|
||||
|
||||
@ -365,7 +365,7 @@ mod tests {
|
||||
type F = CrandallField;
|
||||
const D: usize = 4;
|
||||
let config = CircuitConfig {
|
||||
num_wires: 134,
|
||||
num_wires: 126,
|
||||
num_routed_wires: 33,
|
||||
security_bits: 128,
|
||||
rate_bits: 3,
|
||||
|
||||
11
src/vars.rs
11
src/vars.rs
@ -3,7 +3,7 @@ use std::ops::Range;
|
||||
|
||||
use crate::field::extension_field::algebra::ExtensionAlgebra;
|
||||
use crate::field::extension_field::target::{ExtensionAlgebraTarget, ExtensionTarget};
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::field::extension_field::{Extendable, FieldExtension};
|
||||
use crate::field::field::Field;
|
||||
use crate::proof::{Hash, HashTarget};
|
||||
|
||||
@ -37,6 +37,15 @@ impl<'a, F: Extendable<D>, const D: usize> EvaluationVars<'a, F, D> {
|
||||
}
|
||||
|
||||
impl<'a, F: Field> EvaluationVarsBase<'a, F> {
|
||||
pub fn get_local_ext<const D: usize>(&self, wire_range: Range<usize>) -> F::Extension
|
||||
where
|
||||
F: Extendable<D>,
|
||||
{
|
||||
debug_assert_eq!(wire_range.len(), D);
|
||||
let arr = self.local_wires[wire_range].try_into().unwrap();
|
||||
F::Extension::from_basefield_array(arr)
|
||||
}
|
||||
|
||||
pub fn remove_prefix(&mut self, prefix: &[bool]) {
|
||||
self.local_constants = &self.local_constants[prefix.len()..];
|
||||
}
|
||||
|
||||
@ -170,7 +170,7 @@ impl<F: Field> PartialWitness<F> {
|
||||
"wire {} of gate #{} (`{}`)",
|
||||
input,
|
||||
gate,
|
||||
gate_instances[*gate].gate_type.0.id()
|
||||
gate_instances[*gate].gate_ref.0.id()
|
||||
),
|
||||
Target::VirtualTarget { index } => format!("{}-th virtual target", index),
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user