mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-08 00:33:06 +00:00
Gate infra
This commit is contained in:
parent
33bd3edd11
commit
9fdff8ea08
@ -7,6 +7,7 @@ edition = "2018"
|
||||
[dependencies]
|
||||
unroll = "0.1.5"
|
||||
rayon = "1.5.0"
|
||||
num = "0.3"
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
|
||||
94
src/circuit_builder.rs
Normal file
94
src/circuit_builder.rs
Normal file
@ -0,0 +1,94 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::circuit_data::CircuitConfig;
|
||||
use crate::field::field::Field;
|
||||
use crate::gates::gate::{GateInstance, GateRef};
|
||||
use crate::generator::{CopyGenerator, WitnessGenerator2};
|
||||
use crate::target::Target;
|
||||
use crate::gates::constant::ConstantGate2;
|
||||
use crate::wire::Wire;
|
||||
|
||||
pub struct CircuitBuilder2<F: Field> {
|
||||
config: CircuitConfig,
|
||||
gates: HashSet<GateRef<F>>,
|
||||
gate_instances: Vec<GateInstance<F>>,
|
||||
generators: Vec<Box<dyn WitnessGenerator2<F>>>,
|
||||
}
|
||||
|
||||
impl<F: Field> CircuitBuilder2<F> {
|
||||
pub fn new(config: CircuitConfig) -> Self {
|
||||
CircuitBuilder2 {
|
||||
config,
|
||||
gates: HashSet::new(),
|
||||
gate_instances: Vec::new(),
|
||||
generators: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a gate to the circuit, and returns its index.
|
||||
pub fn add_gate(&mut self, gate_type: GateRef<F>, constants: Vec<F>) -> usize {
|
||||
// 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.gate_instances.push(GateInstance { gate_type, constants });
|
||||
index
|
||||
}
|
||||
|
||||
fn check_gate_compatibility(&self, gate: &GateRef<F>) {
|
||||
assert!(gate.0.min_wires(self.config) <= self.config.num_wires);
|
||||
}
|
||||
|
||||
/// Shorthand for `generate_copy` and `assert_equal`.
|
||||
/// Both elements must be routable, otherwise this method will panic.
|
||||
pub fn route(&mut self, src: Target, dst: Target) {
|
||||
self.generate_copy(src, dst);
|
||||
self.assert_equal(src, dst);
|
||||
}
|
||||
|
||||
/// Adds a generator which will copy `src` to `dst`.
|
||||
pub fn generate_copy(&mut self, src: Target, dst: Target) {
|
||||
self.add_generator(CopyGenerator { src, dst });
|
||||
}
|
||||
|
||||
/// Uses Plonk's permutation argument to require that two elements be equal.
|
||||
/// Both elements must be routable, otherwise this method will panic.
|
||||
pub fn assert_equal(&mut self, x: Target, y: Target) {
|
||||
assert!(x.is_routable(self.config));
|
||||
assert!(y.is_routable(self.config));
|
||||
}
|
||||
|
||||
pub fn add_generator<G: WitnessGenerator2<F>>(&mut self, generator: G) {
|
||||
self.generators.push(Box::new(generator));
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// Returns a routable target with a value of `ORDER - 1`.
|
||||
pub fn neg_one(&mut self) -> Target {
|
||||
self.constant(F::NEG_ONE)
|
||||
}
|
||||
|
||||
/// Returns a routable target with the given constant value.
|
||||
pub fn constant(&mut self, c: F) -> Target {
|
||||
let gate = self.add_gate(ConstantGate2::get(), vec![c]);
|
||||
Target::Wire(Wire { gate, input: ConstantGate2::WIRE_OUTPUT })
|
||||
}
|
||||
}
|
||||
@ -5,8 +5,11 @@ use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
|
||||
use num::{BigUint, FromPrimitive, One, Zero};
|
||||
|
||||
use crate::field::field::Field;
|
||||
use crate::wire::Wire;
|
||||
use crate::gates::output_graph::GateOutputLocation;
|
||||
|
||||
pub(crate) struct EvaluationVars<'a, F: Field> {
|
||||
pub(crate) local_constants: &'a [F],
|
||||
@ -58,6 +61,13 @@ impl<F: Field> ConstraintPolynomial<F> {
|
||||
Self::from_inner(ConstraintPolynomialInner::NextWireValue(index))
|
||||
}
|
||||
|
||||
pub fn from_gate_output(gate_output: GateOutputLocation) -> Self {
|
||||
match gate_output {
|
||||
GateOutputLocation::LocalWire(i) => Self::local_wire_value(i),
|
||||
GateOutputLocation::NextWire(i) => Self::next_wire_value(i),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Have these take references?
|
||||
pub fn add(&self, rhs: &Self) -> Self {
|
||||
// TODO: Special case for either operand being 0.
|
||||
@ -105,7 +115,7 @@ impl<F: Field> ConstraintPolynomial<F> {
|
||||
self * self * self
|
||||
}
|
||||
|
||||
pub(crate) fn degree(&self) -> usize {
|
||||
pub(crate) fn degree(&self) -> BigUint {
|
||||
(self.0).0.degree()
|
||||
}
|
||||
|
||||
@ -149,6 +159,15 @@ impl<F: Field> ConstraintPolynomial<F> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Replace all occurrences of `from` with `to` in this polynomial graph.
|
||||
pub(crate) fn replace_all(
|
||||
&self,
|
||||
from: Self,
|
||||
to: Self,
|
||||
) -> Self {
|
||||
Self(self.0.replace_all(from.0, to.0))
|
||||
}
|
||||
|
||||
fn from_inner(inner: ConstraintPolynomialInner<F>) -> Self {
|
||||
Self(ConstraintPolynomialRef::new(inner))
|
||||
}
|
||||
@ -413,16 +432,17 @@ impl<F: Field> ConstraintPolynomialInner<F> {
|
||||
}
|
||||
}
|
||||
|
||||
fn degree(&self) -> usize {
|
||||
fn degree(&self) -> BigUint {
|
||||
match self {
|
||||
ConstraintPolynomialInner::Constant(_) => 0,
|
||||
ConstraintPolynomialInner::LocalConstant(_) => 1,
|
||||
ConstraintPolynomialInner::NextConstant(_) => 1,
|
||||
ConstraintPolynomialInner::LocalWireValue(_) => 1,
|
||||
ConstraintPolynomialInner::NextWireValue(_) => 1,
|
||||
ConstraintPolynomialInner::Constant(_) => BigUint::zero(),
|
||||
ConstraintPolynomialInner::LocalConstant(_) => BigUint::one(),
|
||||
ConstraintPolynomialInner::NextConstant(_) => BigUint::one(),
|
||||
ConstraintPolynomialInner::LocalWireValue(_) => BigUint::one(),
|
||||
ConstraintPolynomialInner::NextWireValue(_) => BigUint::one(),
|
||||
ConstraintPolynomialInner::Sum { lhs, rhs } => lhs.0.degree().max(rhs.0.degree()),
|
||||
ConstraintPolynomialInner::Product { lhs, rhs } => lhs.0.degree() + rhs.0.degree(),
|
||||
ConstraintPolynomialInner::Exponentiation { base, exponent } => base.0.degree() * exponent,
|
||||
ConstraintPolynomialInner::Exponentiation { base, exponent } =>
|
||||
base.0.degree() * BigUint::from_usize(*exponent).unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -431,7 +451,7 @@ impl<F: Field> ConstraintPolynomialInner<F> {
|
||||
/// than content. This is useful when we want to use constraint polynomials as `HashMap` keys, but
|
||||
/// we want address-based hashing for performance reasons.
|
||||
#[derive(Clone)]
|
||||
struct ConstraintPolynomialRef<F: Field>(Rc<ConstraintPolynomialInner<F>>);
|
||||
pub(crate) struct ConstraintPolynomialRef<F: Field>(Rc<ConstraintPolynomialInner<F>>);
|
||||
|
||||
impl<F: Field> ConstraintPolynomialRef<F> {
|
||||
fn new(inner: ConstraintPolynomialInner<F>) -> Self {
|
||||
@ -451,6 +471,15 @@ impl<F: Field> ConstraintPolynomialRef<F> {
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
/// Replace all occurrences of `from` with `to` in this polynomial graph.
|
||||
fn replace_all(
|
||||
&self,
|
||||
from: ConstraintPolynomialRef<F>,
|
||||
to: ConstraintPolynomialRef<F>,
|
||||
) -> ConstraintPolynomialRef<F> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> PartialEq for ConstraintPolynomialRef<F> {
|
||||
|
||||
@ -38,6 +38,7 @@ impl Debug for CrandallField {
|
||||
impl Field for CrandallField {
|
||||
const ZERO: Self = Self(0);
|
||||
const ONE: Self = Self(1);
|
||||
const TWO: Self = Self(2);
|
||||
const NEG_ONE: Self = Self(P - 1);
|
||||
|
||||
#[inline(always)]
|
||||
|
||||
@ -20,6 +20,7 @@ pub trait Field: 'static
|
||||
+ Sync {
|
||||
const ZERO: Self;
|
||||
const ONE: Self;
|
||||
const TWO: Self;
|
||||
const NEG_ONE: Self;
|
||||
|
||||
fn sq(&self) -> Self;
|
||||
|
||||
31
src/gates/constant.rs
Normal file
31
src/gates/constant.rs
Normal file
@ -0,0 +1,31 @@
|
||||
use crate::circuit_data::CircuitConfig;
|
||||
use crate::constraint_polynomial::ConstraintPolynomial;
|
||||
use crate::field::field::Field;
|
||||
use crate::gates::deterministic_gate::{DeterministicGate, DeterministicGateAdapter};
|
||||
use crate::gates::gate::GateRef;
|
||||
use crate::gates::output_graph::{GateOutputLocation, OutputGraph};
|
||||
|
||||
/// A gate which takes a single constant parameter and outputs that value.
|
||||
pub struct ConstantGate2;
|
||||
|
||||
impl ConstantGate2 {
|
||||
pub fn get<F: Field>() -> GateRef<F> {
|
||||
GateRef::new(DeterministicGateAdapter::new(ConstantGate2))
|
||||
}
|
||||
|
||||
pub const CONST_INPUT: usize = 0;
|
||||
|
||||
pub const WIRE_OUTPUT: usize = 0;
|
||||
}
|
||||
|
||||
impl<F: Field> DeterministicGate<F> for ConstantGate2 {
|
||||
fn id(&self) -> String {
|
||||
"ConstantGate".into()
|
||||
}
|
||||
|
||||
fn outputs(&self, _config: CircuitConfig) -> OutputGraph<F> {
|
||||
let loc = GateOutputLocation::LocalWire(Self::WIRE_OUTPUT);
|
||||
let out = ConstraintPolynomial::local_constant(Self::CONST_INPUT);
|
||||
OutputGraph::single_output(loc, out)
|
||||
}
|
||||
}
|
||||
@ -5,9 +5,10 @@ use crate::constraint_polynomial::{ConstraintPolynomial, EvaluationVars};
|
||||
use crate::field::field::Field;
|
||||
use crate::gates::gate::Gate;
|
||||
use crate::generator::{SimpleGenerator, WitnessGenerator2};
|
||||
use crate::target::Target2;
|
||||
use crate::target::Target;
|
||||
use crate::wire::Wire;
|
||||
use crate::witness::PartialWitness;
|
||||
use crate::gates::output_graph::{OutputGraph, GateOutputLocation};
|
||||
|
||||
/// A deterministic gate. Each entry in `outputs()` describes how that output is evaluated; this is
|
||||
/// used to create both the constraint set and the generator set.
|
||||
@ -18,9 +19,9 @@ pub trait DeterministicGate<F: Field>: 'static {
|
||||
/// A unique identifier for this gate.
|
||||
fn id(&self) -> String;
|
||||
|
||||
/// A vector of `(i, c)` pairs, where `i` is the index of an output and `c` is the polynomial
|
||||
/// defining how that output is evaluated.
|
||||
fn outputs(&self, config: CircuitConfig) -> Vec<(usize, ConstraintPolynomial<F>)>;
|
||||
/// A vector of `(loc, out)` pairs, where `loc` is the location of an output and `out` is a
|
||||
/// polynomial defining how that output is evaluated.
|
||||
fn outputs(&self, config: CircuitConfig) -> OutputGraph<F>;
|
||||
|
||||
/// Any additional constraints to be enforced, besides the (automatically provided) ones that
|
||||
/// constraint output values.
|
||||
@ -60,8 +61,8 @@ impl<F: Field, DG: DeterministicGate<F>> Gate<F> for DeterministicGateAdapter<F,
|
||||
fn constraints(&self, config: CircuitConfig) -> Vec<ConstraintPolynomial<F>> {
|
||||
// For each output, we add a constraint of the form `out - expression = 0`,
|
||||
// then we append any additional constraints that the gate defines.
|
||||
self.gate.outputs(config).into_iter()
|
||||
.map(|(i, out)| out - ConstraintPolynomial::local_wire_value(i))
|
||||
self.gate.outputs(config).outputs.into_iter()
|
||||
.map(|(output_loc, out)| out - ConstraintPolynomial::from_gate_output(output_loc))
|
||||
.chain(self.gate.additional_constraints(config).into_iter())
|
||||
.collect()
|
||||
}
|
||||
@ -73,12 +74,12 @@ impl<F: Field, DG: DeterministicGate<F>> Gate<F> for DeterministicGateAdapter<F,
|
||||
local_constants: Vec<F>,
|
||||
next_constants: Vec<F>,
|
||||
) -> Vec<Box<dyn WitnessGenerator2<F>>> {
|
||||
self.gate.outputs(config)
|
||||
self.gate.outputs(config).outputs
|
||||
.into_iter()
|
||||
.map(|(input_index, out)| {
|
||||
.map(|(location, out)| {
|
||||
let og = OutputGenerator {
|
||||
gate_index,
|
||||
input_index,
|
||||
location,
|
||||
out,
|
||||
local_constants: local_constants.clone(),
|
||||
next_constants: next_constants.clone(),
|
||||
@ -96,17 +97,17 @@ impl<F: Field, DG: DeterministicGate<F>> Gate<F> for DeterministicGateAdapter<F,
|
||||
|
||||
struct OutputGenerator<F: Field> {
|
||||
gate_index: usize,
|
||||
input_index: usize,
|
||||
location: GateOutputLocation,
|
||||
out: ConstraintPolynomial<F>,
|
||||
local_constants: Vec<F>,
|
||||
next_constants: Vec<F>,
|
||||
}
|
||||
|
||||
impl<F: Field> SimpleGenerator<F> for OutputGenerator<F> {
|
||||
fn dependencies(&self) -> Vec<Target2> {
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
self.out.dependencies(self.gate_index)
|
||||
.into_iter()
|
||||
.map(Target2::Wire)
|
||||
.map(Target::Wire)
|
||||
.collect()
|
||||
}
|
||||
|
||||
@ -125,8 +126,8 @@ impl<F: Field> SimpleGenerator<F> for OutputGenerator<F> {
|
||||
// Lookup the values if they exist. If not, we can just insert a zero, knowing
|
||||
// that it will not be used. (If it was used, it would have been included in our
|
||||
// dependencies, and this generator would not have run yet.)
|
||||
let local_value = witness.try_get_target(Target2::Wire(local_wire)).unwrap_or(F::ZERO);
|
||||
let next_value = witness.try_get_target(Target2::Wire(next_wire)).unwrap_or(F::ZERO);
|
||||
let local_value = witness.try_get_target(Target::Wire(local_wire)).unwrap_or(F::ZERO);
|
||||
let next_value = witness.try_get_target(Target::Wire(next_wire)).unwrap_or(F::ZERO);
|
||||
|
||||
local_wire_values.push(local_value);
|
||||
next_wire_values.push(next_value);
|
||||
@ -139,8 +140,14 @@ impl<F: Field> SimpleGenerator<F> for OutputGenerator<F> {
|
||||
next_wire_values: &next_wire_values,
|
||||
};
|
||||
|
||||
let result_wire = Wire { gate: self.gate_index, input: self.input_index };
|
||||
let result_wire = match self.location {
|
||||
GateOutputLocation::LocalWire(input) =>
|
||||
Wire { gate: self.gate_index, input },
|
||||
GateOutputLocation::NextWire(input) =>
|
||||
Wire { gate: self.gate_index + 1, input },
|
||||
};
|
||||
|
||||
let result_value = self.out.evaluate(vars);
|
||||
PartialWitness::singleton(Target2::Wire(result_wire), result_value)
|
||||
PartialWitness::singleton(Target::Wire(result_wire), result_value)
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ use crate::circuit_data::CircuitConfig;
|
||||
use crate::constraint_polynomial::ConstraintPolynomial;
|
||||
use crate::field::field::Field;
|
||||
use crate::generator::WitnessGenerator2;
|
||||
use num::ToPrimitive;
|
||||
|
||||
/// A custom gate.
|
||||
// TODO: Remove CircuitConfig params? Could just use fields within each struct.
|
||||
@ -44,7 +45,7 @@ pub trait Gate<F: Field>: 'static {
|
||||
fn degree(&self, config: CircuitConfig) -> usize {
|
||||
self.constraints(config)
|
||||
.into_iter()
|
||||
.map(|c| c.degree())
|
||||
.map(|c| c.degree().to_usize().expect("degree too large"))
|
||||
.max()
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
@ -4,11 +4,12 @@ use std::sync::Arc;
|
||||
use crate::circuit_data::CircuitConfig;
|
||||
use crate::constraint_polynomial::ConstraintPolynomial;
|
||||
use crate::field::field::Field;
|
||||
use crate::gates::deterministic_gate::DeterministicGate;
|
||||
use crate::gates::deterministic_gate::{DeterministicGate, DeterministicGateAdapter};
|
||||
use crate::gates::gate::{Gate, GateRef};
|
||||
use crate::gates::output_graph::{GateOutputLocation, OutputGraph};
|
||||
use crate::generator::{SimpleGenerator, WitnessGenerator2};
|
||||
use crate::gmimc::{gmimc_permute_array, gmimc_permute};
|
||||
use crate::target::Target2;
|
||||
use crate::gmimc::{gmimc_permute, gmimc_permute_array};
|
||||
use crate::target::Target;
|
||||
use crate::wire::Wire;
|
||||
use crate::witness::PartialWitness;
|
||||
|
||||
@ -21,25 +22,60 @@ pub struct GMiMCGate<F: Field, const W: usize, const R: usize> {
|
||||
|
||||
impl<F: Field, const W: usize, const R: usize> GMiMCGate<F, W, R> {
|
||||
pub fn with_constants(constants: Arc<[F; R]>) -> GateRef<F> {
|
||||
GateRef::new(GMiMCGate::<F, W, R> { constants })
|
||||
let gate = GMiMCGate::<F, W, R> { constants };
|
||||
let adapter = DeterministicGateAdapter::new(gate);
|
||||
GateRef::new(adapter)
|
||||
}
|
||||
|
||||
pub fn with_automatic_constants() -> GateRef<F> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// 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.
|
||||
// TODO: Assert binary.
|
||||
pub const WIRE_SWITCH: usize = 0;
|
||||
|
||||
/// The wire index for the i'th input to the permutation.
|
||||
pub fn wire_input(i: usize) -> usize {
|
||||
i + 1
|
||||
}
|
||||
|
||||
/// The wire index for the i'th output to the permutation.
|
||||
/// Note that outputs are written to the next gate's wires.
|
||||
pub fn wire_output(i: usize) -> usize {
|
||||
i + 1
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field, const W: usize, const R: usize> Gate<F> for GMiMCGate<F, W, R> {
|
||||
impl<F: Field, const W: usize, const R: usize> DeterministicGate<F> for GMiMCGate<F, W, R> {
|
||||
fn id(&self) -> String {
|
||||
// TODO: Add W/R
|
||||
// TODO: This won't include generic params?
|
||||
format!("{:?}", self)
|
||||
}
|
||||
|
||||
fn constraints(&self, config: CircuitConfig) -> Vec<ConstraintPolynomial<F>> {
|
||||
let mut state = (0..W)
|
||||
.map(|i| ConstraintPolynomial::local_wire_value(i))
|
||||
fn outputs(&self, config: CircuitConfig) -> OutputGraph<F> {
|
||||
let original_inputs = (0..W)
|
||||
.map(|i| ConstraintPolynomial::local_wire_value(Self::wire_input(i)))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Conditionally switch inputs based on the (boolean) switch wire.
|
||||
let switch = ConstraintPolynomial::local_wire_value(Self::WIRE_SWITCH);
|
||||
let mut state = Vec::new();
|
||||
for i in 0..4 {
|
||||
let a = &original_inputs[i];
|
||||
let b = &original_inputs[i + 4];
|
||||
state.push(a + &switch * (b - a));
|
||||
}
|
||||
for i in 0..4 {
|
||||
let a = &original_inputs[i + 4];
|
||||
let b = &original_inputs[i];
|
||||
state.push(a + &switch * (b - a));
|
||||
}
|
||||
for i in 8..W {
|
||||
state.push(original_inputs[i].clone());
|
||||
}
|
||||
|
||||
// Value that is implicitly added to each element.
|
||||
// See https://affine.group/2020/02/starkware-challenge
|
||||
let mut addition_buffer = ConstraintPolynomial::zero();
|
||||
@ -56,52 +92,19 @@ impl<F: Field, const W: usize, const R: usize> Gate<F> for GMiMCGate<F, W, R> {
|
||||
state[i] += &addition_buffer;
|
||||
}
|
||||
|
||||
state
|
||||
let outputs = state.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, out)| (GateOutputLocation::NextWire(Self::wire_output(i)), out))
|
||||
.collect();
|
||||
|
||||
// A degree of 9 is reasonable for most circuits, and it means that we only need wires for
|
||||
// every other addition buffer state.
|
||||
OutputGraph { outputs }.shrink_degree(9)
|
||||
}
|
||||
|
||||
fn generators(
|
||||
&self,
|
||||
config: CircuitConfig,
|
||||
gate_index: usize,
|
||||
local_constants: Vec<F>,
|
||||
next_constants: Vec<F>,
|
||||
) -> Vec<Box<dyn WitnessGenerator2<F>>> {
|
||||
let generator = GMiMCGenerator::<F, W, R> {
|
||||
round_constants: self.constants.clone(),
|
||||
gate_index,
|
||||
};
|
||||
vec![Box::new(generator)]
|
||||
}
|
||||
}
|
||||
|
||||
struct GMiMCGenerator<F: Field, const W: usize, const R: usize> {
|
||||
round_constants: Arc<[F; R]>,
|
||||
gate_index: usize,
|
||||
}
|
||||
|
||||
impl<F: Field, const W: usize, const R: usize> SimpleGenerator<F> for GMiMCGenerator<F, W, R> {
|
||||
fn dependencies(&self) -> Vec<Target2> {
|
||||
(0..W)
|
||||
.map(|i| Target2::Wire(
|
||||
Wire { gate: self.gate_index, input: i }))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn run_once(&mut self, witness: &PartialWitness<F>) -> PartialWitness<F> {
|
||||
let mut inputs: [F; W] = [F::ZERO; W];
|
||||
for i in 0..W {
|
||||
inputs[i] = witness.get_wire(
|
||||
Wire { gate: self.gate_index, input: i });
|
||||
}
|
||||
|
||||
let outputs = gmimc_permute::<F, W, R>(inputs, self.round_constants.clone());
|
||||
|
||||
let mut result = PartialWitness::new();
|
||||
for i in 0..W {
|
||||
result.set_wire(
|
||||
Wire { gate: self.gate_index + 1, input: i },
|
||||
outputs[i]);
|
||||
}
|
||||
result
|
||||
fn additional_constraints(&self, _config: CircuitConfig) -> Vec<ConstraintPolynomial<F>> {
|
||||
let switch = ConstraintPolynomial::local_wire_value(Self::WIRE_SWITCH);
|
||||
let switch_bool_constraint = &switch * (&switch - 1);
|
||||
vec![switch_bool_constraint]
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
pub(crate) mod constant;
|
||||
pub(crate) mod deterministic_gate;
|
||||
pub(crate) mod gate;
|
||||
pub(crate) mod gmimc;
|
||||
pub(crate) mod output_graph;
|
||||
|
||||
66
src/gates/output_graph.rs
Normal file
66
src/gates/output_graph.rs
Normal file
@ -0,0 +1,66 @@
|
||||
use std::iter;
|
||||
|
||||
use crate::constraint_polynomial::{ConstraintPolynomial, ConstraintPolynomialRef};
|
||||
use crate::field::field::Field;
|
||||
|
||||
/// Represents a set of deterministic gate outputs, expressed as polynomials over witness
|
||||
/// values.
|
||||
pub struct OutputGraph<F: Field> {
|
||||
pub(crate) outputs: Vec<(GateOutputLocation, ConstraintPolynomial<F>)>
|
||||
}
|
||||
|
||||
/// Represents an output location of a deterministic gate.
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum GateOutputLocation {
|
||||
/// A wire belonging to the gate itself.
|
||||
LocalWire(usize),
|
||||
/// A wire belonging to the following gate.
|
||||
NextWire(usize),
|
||||
}
|
||||
|
||||
impl<F: Field> OutputGraph<F> {
|
||||
/// Creates an output graph with a single output.
|
||||
pub fn single_output(loc: GateOutputLocation, out: ConstraintPolynomial<F>) -> Self {
|
||||
Self { outputs: vec![(loc, out)] }
|
||||
}
|
||||
|
||||
/// Compiles an output graph with potentially high-degree polynomials to one with low-degree
|
||||
/// polynomials by introducing extra wires for some intermediate values.
|
||||
///
|
||||
/// Note that this uses a simple greedy algorithm, so the result may not be optimal in terms of wire
|
||||
/// count.
|
||||
pub fn shrink_degree(&self, new_degree: usize) -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Allocate a new wire for the given target polynomial, and return a new output graph with
|
||||
/// references to the target polynomial replaced with references to that wire.
|
||||
fn allocate_wire(&self, target: ConstraintPolynomial<F>) -> Self {
|
||||
let new_wire_index = self.outputs.iter()
|
||||
.flat_map(|(loc, out)| out.max_wire_input_index())
|
||||
.max()
|
||||
.map_or(0, |i| i + 1);
|
||||
|
||||
let new_wire = ConstraintPolynomial::local_wire_value(new_wire_index);
|
||||
|
||||
let outputs = self.outputs.iter()
|
||||
.map(|(loc, out)| (*loc, out.replace_all(target.clone(), new_wire.clone())))
|
||||
.chain(iter::once((GateOutputLocation::LocalWire(new_wire_index), target.clone())))
|
||||
.collect();
|
||||
Self { outputs }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::constraint_polynomial::ConstraintPolynomial;
|
||||
use crate::gates::output_graph::shrink_degree;
|
||||
|
||||
#[test]
|
||||
fn shrink_exp() {
|
||||
let original = ConstraintPolynomial::local_wire_value(0).exp(10);
|
||||
let shrunk = shrink_degree(original, 3);
|
||||
// `shrunk` should be something similar to (wire0^3)^3 * wire0.
|
||||
assert_eq!(shrunk.max_wire_input_index(), Some(2))
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,12 @@
|
||||
use crate::field::field::Field;
|
||||
use crate::target::Target2;
|
||||
use crate::target::Target;
|
||||
use crate::witness::PartialWitness;
|
||||
|
||||
/// A generator participates in the generation of the witness.
|
||||
pub trait WitnessGenerator2<F: Field>: 'static {
|
||||
/// Targets to be "watched" by this generator. Whenever a target in the watch list is populated,
|
||||
/// the generator will be queued to run.
|
||||
fn watch_list(&self) -> Vec<Target2>;
|
||||
fn watch_list(&self) -> Vec<Target>;
|
||||
|
||||
/// Run this generator, returning a `PartialWitness` containing any new witness elements, and a
|
||||
/// flag indicating whether the generator is finished. If the flag is true, the generator will
|
||||
@ -17,13 +17,13 @@ pub trait WitnessGenerator2<F: Field>: 'static {
|
||||
|
||||
/// A generator which runs once after a list of dependencies is present in the witness.
|
||||
pub trait SimpleGenerator<F: Field>: 'static {
|
||||
fn dependencies(&self) -> Vec<Target2>;
|
||||
fn dependencies(&self) -> Vec<Target>;
|
||||
|
||||
fn run_once(&mut self, witness: &PartialWitness<F>) -> PartialWitness<F>;
|
||||
}
|
||||
|
||||
impl<F: Field, SG: SimpleGenerator<F>> WitnessGenerator2<F> for SG {
|
||||
fn watch_list(&self) -> Vec<Target2> {
|
||||
fn watch_list(&self) -> Vec<Target> {
|
||||
self.dependencies()
|
||||
}
|
||||
|
||||
@ -38,12 +38,12 @@ impl<F: Field, SG: SimpleGenerator<F>> WitnessGenerator2<F> for SG {
|
||||
|
||||
/// A generator which copies one wire to another.
|
||||
pub(crate) struct CopyGenerator {
|
||||
pub(crate) src: Target2,
|
||||
pub(crate) dst: Target2,
|
||||
pub(crate) src: Target,
|
||||
pub(crate) dst: Target,
|
||||
}
|
||||
|
||||
impl<F: Field> SimpleGenerator<F> for CopyGenerator {
|
||||
fn dependencies(&self) -> Vec<Target2> {
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
vec![self.src]
|
||||
}
|
||||
|
||||
|
||||
@ -11,8 +11,8 @@ use field::fft::fft_precompute;
|
||||
|
||||
use crate::field::field::Field;
|
||||
use crate::util::log2_ceil;
|
||||
use std::sync::Arc;
|
||||
|
||||
mod circuit_builder;
|
||||
mod circuit_data;
|
||||
mod constraint_polynomial;
|
||||
mod field;
|
||||
@ -22,6 +22,7 @@ mod generator;
|
||||
mod gmimc;
|
||||
mod proof;
|
||||
mod prover;
|
||||
mod recursive_verifier;
|
||||
mod rescue;
|
||||
mod target;
|
||||
mod util;
|
||||
|
||||
33
src/proof.rs
33
src/proof.rs
@ -1,12 +1,12 @@
|
||||
use crate::field::field::Field;
|
||||
use crate::target::Target2;
|
||||
use crate::target::Target;
|
||||
|
||||
pub struct Hash<F: Field> {
|
||||
elements: Vec<F>,
|
||||
}
|
||||
|
||||
pub struct HashTarget {
|
||||
elements: Vec<Target2>,
|
||||
elements: Vec<Target>,
|
||||
}
|
||||
|
||||
pub struct Proof2<F: Field> {
|
||||
@ -34,7 +34,24 @@ pub struct ProofTarget2 {
|
||||
/// Purported values of each polynomial at each challenge point.
|
||||
pub openings: Vec<OpeningSetTarget>,
|
||||
|
||||
// TODO: FRI Merkle proofs.
|
||||
/// A FRI argument for each FRI query.
|
||||
pub fri_proofs: Vec<FriProofTarget>,
|
||||
}
|
||||
|
||||
/// Represents a single FRI query, i.e. a path through the reduction tree.
|
||||
pub struct FriProofTarget {
|
||||
/// Merkle proofs for the original purported codewords, i.e. the subject of the LDT.
|
||||
pub initial_merkle_proofs: Vec<MerkleProofTarget>,
|
||||
/// Merkle proofs for the reduced polynomials that were sent in the commit phase.
|
||||
pub intermediate_merkle_proofs: Vec<MerkleProofTarget>,
|
||||
/// The final polynomial in point-value form.
|
||||
pub final_poly: Vec<Target>,
|
||||
}
|
||||
|
||||
pub struct MerkleProofTarget {
|
||||
pub leaf: Vec<Target>,
|
||||
pub siblings: Vec<Target>,
|
||||
// TODO: Also need left/right turn info.
|
||||
}
|
||||
|
||||
/// The purported values of each polynomial at a single point.
|
||||
@ -49,10 +66,10 @@ pub struct OpeningSet<F: Field> {
|
||||
|
||||
/// The purported values of each polynomial at a single point.
|
||||
pub struct OpeningSetTarget {
|
||||
pub constants: Vec<Target2>,
|
||||
pub plonk_sigmas: Vec<Target2>,
|
||||
pub wires: Vec<Target2>,
|
||||
pub constants: Vec<Target>,
|
||||
pub plonk_sigmas: Vec<Target>,
|
||||
pub wires: Vec<Target>,
|
||||
// TODO: One or multiple?
|
||||
pub plonk_z: Vec<Target2>,
|
||||
pub plonk_t: Vec<Target2>,
|
||||
pub plonk_z: Vec<Target>,
|
||||
pub plonk_t: Vec<Target>,
|
||||
}
|
||||
|
||||
5
src/recursive_verifier.rs
Normal file
5
src/recursive_verifier.rs
Normal file
@ -0,0 +1,5 @@
|
||||
use crate::circuit_builder::CircuitBuilder2;
|
||||
use crate::field::field::Field;
|
||||
|
||||
pub fn add_recursive_verifier<F: Field>(builder: &mut CircuitBuilder2<F>) {
|
||||
}
|
||||
@ -7,22 +7,22 @@ use crate::wire::Wire;
|
||||
|
||||
/// A location in the witness.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub enum Target2 {
|
||||
pub enum Target {
|
||||
Wire(Wire),
|
||||
PublicInput { index: usize },
|
||||
VirtualAdviceTarget { index: usize },
|
||||
}
|
||||
|
||||
impl Target2 {
|
||||
impl Target {
|
||||
pub fn wire(gate: usize, input: usize) -> Self {
|
||||
Self::Wire(Wire { gate, input })
|
||||
}
|
||||
|
||||
pub fn is_routable(&self, config: CircuitConfig) -> bool {
|
||||
match self {
|
||||
Target2::Wire(wire) => wire.is_routable(config),
|
||||
Target2::PublicInput { .. } => true,
|
||||
Target2::VirtualAdviceTarget { .. } => false,
|
||||
Target::Wire(wire) => wire.is_routable(config),
|
||||
Target::PublicInput { .. } => true,
|
||||
Target::VirtualAdviceTarget { .. } => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::field::field::Field;
|
||||
use crate::target::Target2;
|
||||
use crate::target::Target;
|
||||
use crate::wire::Wire;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PartialWitness<F: Field> {
|
||||
target_values: HashMap<Target2, F>,
|
||||
target_values: HashMap<Target, F>,
|
||||
}
|
||||
|
||||
impl<F: Field> PartialWitness<F> {
|
||||
@ -16,7 +16,7 @@ impl<F: Field> PartialWitness<F> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn singleton(target: Target2, value: F) -> Self {
|
||||
pub fn singleton(target: Target, value: F) -> Self {
|
||||
let mut witness = PartialWitness::new();
|
||||
witness.set_target(target, value);
|
||||
witness
|
||||
@ -26,31 +26,31 @@ impl<F: Field> PartialWitness<F> {
|
||||
self.target_values.is_empty()
|
||||
}
|
||||
|
||||
pub fn get_target(&self, target: Target2) -> F {
|
||||
pub fn get_target(&self, target: Target) -> F {
|
||||
self.target_values[&target]
|
||||
}
|
||||
|
||||
pub fn try_get_target(&self, target: Target2) -> Option<F> {
|
||||
pub fn try_get_target(&self, target: Target) -> Option<F> {
|
||||
self.target_values.get(&target).cloned()
|
||||
}
|
||||
|
||||
pub fn get_wire(&self, wire: Wire) -> F {
|
||||
self.get_target(Target2::Wire(wire))
|
||||
self.get_target(Target::Wire(wire))
|
||||
}
|
||||
|
||||
pub fn contains(&self, target: Target2) -> bool {
|
||||
pub fn contains(&self, target: Target) -> bool {
|
||||
self.target_values.contains_key(&target)
|
||||
}
|
||||
|
||||
pub fn contains_all(&self, targets: &[Target2]) -> bool {
|
||||
pub fn contains_all(&self, targets: &[Target]) -> bool {
|
||||
targets.iter().all(|&t| self.contains(t))
|
||||
}
|
||||
|
||||
pub fn set_target(&mut self, target: Target2, value: F) {
|
||||
pub fn set_target(&mut self, target: Target, value: F) {
|
||||
self.target_values.insert(target, value);
|
||||
}
|
||||
|
||||
pub fn set_wire(&mut self, wire: Wire, value: F) {
|
||||
self.set_target(Target2::Wire(wire), value)
|
||||
self.set_target(Target::Wire(wire), value)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user