plonky2/src/gates/gmimc.rs

192 lines
6.5 KiB
Rust
Raw Normal View History

2021-02-24 13:07:22 -08:00
use std::sync::Arc;
2021-02-24 12:25:13 -08:00
2021-03-18 12:44:45 -07:00
use num::{BigUint, One};
2021-03-01 12:35:02 -08:00
2021-02-09 21:25:21 -08:00
use crate::circuit_data::CircuitConfig;
use crate::constraint_polynomial::ConstraintPolynomial;
use crate::field::field::Field;
2021-02-26 13:18:41 -08:00
use crate::gates::deterministic_gate::{DeterministicGate, DeterministicGateAdapter};
2021-03-01 13:40:05 -08:00
use crate::gates::gate::GateRef;
2021-02-26 13:18:41 -08:00
use crate::gates::output_graph::{GateOutputLocation, OutputGraph};
2021-02-09 21:25:21 -08:00
2021-02-24 12:25:13 -08:00
/// Evaluates a full GMiMC permutation, and writes the output to the next gate's first `width`
/// wires (which could be the input of another `GMiMCGate`).
2021-02-09 21:25:21 -08:00
#[derive(Debug)]
2021-02-24 12:25:13 -08:00
pub struct GMiMCGate<F: Field, const W: usize, const R: usize> {
2021-02-24 13:15:21 -08:00
constants: Arc<[F; R]>,
2021-02-09 21:25:21 -08:00
}
2021-02-24 12:25:13 -08:00
impl<F: Field, const W: usize, const R: usize> GMiMCGate<F, W, R> {
2021-02-24 13:15:21 -08:00
pub fn with_constants(constants: Arc<[F; R]>) -> GateRef<F> {
2021-02-26 13:18:41 -08:00
let gate = GMiMCGate::<F, W, R> { constants };
let adapter = DeterministicGateAdapter::new(gate);
GateRef::new(adapter)
2021-02-24 13:15:21 -08:00
}
pub fn with_automatic_constants() -> GateRef<F> {
2021-02-09 21:25:21 -08:00
todo!()
}
2021-02-26 13:18:41 -08:00
/// 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.
2021-03-01 12:35:02 -08:00
pub const WIRE_SWITCH: usize = W;
2021-02-26 13:18:41 -08:00
/// The wire index for the i'th input to the permutation.
pub fn wire_input(i: usize) -> usize {
2021-03-01 12:35:02 -08:00
i
2021-02-26 13:18:41 -08:00
}
/// 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 {
2021-03-01 12:35:02 -08:00
i
}
/// Adds a local wire output to this output graph. Returns a `ConstraintPolynomial` which
/// references the newly-created wire.
///
/// This may seem like it belongs in `OutputGraph`, but it is not that general, since it uses
/// a notion of "next available" local wire indices specific to this gate.
2021-03-18 12:44:45 -07:00
// TODO: Switch to `ExpandableOutputGraph`.
2021-03-01 12:35:02 -08:00
fn add_local(
outputs: &mut OutputGraph<F>,
poly: ConstraintPolynomial<F>,
) -> ConstraintPolynomial<F> {
let index = outputs.max_local_output_index().map_or(W + 1, |i| i + 1);
outputs.add(GateOutputLocation::LocalWire(index), poly);
2021-03-18 12:44:45 -07:00
ConstraintPolynomial::local_wire(index)
2021-02-26 13:18:41 -08:00
}
2021-02-09 21:25:21 -08:00
}
2021-02-26 13:18:41 -08:00
impl<F: Field, const W: usize, const R: usize> DeterministicGate<F> for GMiMCGate<F, W, R> {
2021-02-09 21:25:21 -08:00
fn id(&self) -> String {
2021-02-26 13:18:41 -08:00
// TODO: This won't include generic params?
2021-02-09 21:25:21 -08:00
format!("{:?}", self)
}
2021-03-18 12:44:45 -07:00
fn outputs(&self, _config: CircuitConfig) -> OutputGraph<F> {
2021-02-26 13:18:41 -08:00
let original_inputs = (0..W)
2021-03-18 12:44:45 -07:00
.map(|i| ConstraintPolynomial::local_wire(Self::wire_input(i)))
2021-02-24 13:07:22 -08:00
.collect::<Vec<_>>();
2021-03-01 12:35:02 -08:00
let mut outputs = OutputGraph::new();
2021-02-26 13:18:41 -08:00
// Conditionally switch inputs based on the (boolean) switch wire.
2021-03-18 12:44:45 -07:00
let switch = ConstraintPolynomial::local_wire(Self::WIRE_SWITCH);
2021-02-26 13:18:41 -08:00
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());
}
2021-02-24 13:07:22 -08:00
// Value that is implicitly added to each element.
// See https://affine.group/2020/02/starkware-challenge
let mut addition_buffer = ConstraintPolynomial::zero();
for r in 0..R {
let active = r % W;
2021-02-24 13:15:21 -08:00
let round_constant = ConstraintPolynomial::constant(self.constants[r]);
2021-03-01 12:35:02 -08:00
let mut f_input = &state[active] + &addition_buffer + round_constant;
if f_input.degree() > BigUint::one() {
f_input = Self::add_local(&mut outputs, f_input);
}
let f_output = f_input.cube();
addition_buffer += &f_output;
state[active] -= f_output;
2021-02-24 13:07:22 -08:00
}
for i in 0..W {
2021-03-01 12:35:02 -08:00
outputs.add(GateOutputLocation::NextWire(i), &state[i] + &addition_buffer);
2021-02-24 13:07:22 -08:00
}
2021-03-01 12:35:02 -08:00
outputs
2021-02-24 12:25:13 -08:00
}
2021-02-26 13:18:41 -08:00
fn additional_constraints(&self, _config: CircuitConfig) -> Vec<ConstraintPolynomial<F>> {
2021-03-18 12:44:45 -07:00
let switch = ConstraintPolynomial::local_wire(Self::WIRE_SWITCH);
2021-02-26 13:18:41 -08:00
let switch_bool_constraint = &switch * (&switch - 1);
vec![switch_bool_constraint]
2021-02-24 12:25:13 -08:00
}
2021-02-09 21:25:21 -08:00
}
2021-02-26 23:30:22 -08:00
#[cfg(test)]
mod tests {
2021-03-01 13:40:05 -08:00
use std::convert::TryInto;
2021-02-26 23:30:22 -08:00
use std::sync::Arc;
2021-03-01 12:35:02 -08:00
use crate::circuit_data::CircuitConfig;
2021-02-26 23:30:22 -08:00
use crate::field::crandall_field::CrandallField;
use crate::field::field::Field;
use crate::gates::deterministic_gate::DeterministicGate;
2021-03-01 12:35:02 -08:00
use crate::gates::gmimc::GMiMCGate;
2021-03-01 13:40:05 -08:00
use crate::generator::generate_partial_witness;
use crate::gmimc::gmimc_permute_naive;
use crate::wire::Wire;
use crate::witness::PartialWitness;
2021-02-26 23:30:22 -08:00
#[test]
fn degree() {
type F = CrandallField;
const W: usize = 12;
2021-03-01 12:35:02 -08:00
const R: usize = 101;
2021-02-26 23:30:22 -08:00
let gate = GMiMCGate::<F, W, R> { constants: Arc::new([F::TWO; R]) };
let config = CircuitConfig {
num_wires: 200,
num_routed_wires: 200,
2021-03-01 12:35:02 -08:00
security_bits: 128,
2021-02-26 23:30:22 -08:00
};
let outs = gate.outputs(config);
2021-03-01 12:35:02 -08:00
assert_eq!(outs.degree(), 3);
}
#[test]
fn generated_output() {
type F = CrandallField;
const W: usize = 12;
const R: usize = 101;
let constants = Arc::new([F::TWO; R]);
2021-03-01 13:40:05 -08:00
type Gate = GMiMCGate::<F, W, R>;
let gate = Gate::with_constants(constants.clone());
2021-03-01 12:35:02 -08:00
let config = CircuitConfig {
num_wires: 200,
num_routed_wires: 200,
security_bits: 128,
};
2021-03-01 13:40:05 -08:00
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_SWITCH }, F::ZERO);
for i in 0..W {
witness.set_wire(
Wire { gate: 0, input: Gate::wire_input(i) },
permutation_inputs[i]);
}
let generators = gate.0.generators(config, 0, vec![], vec![]);
generate_partial_witness(&mut witness, generators);
let expected_outputs: [F; W] = gmimc_permute_naive(
permutation_inputs.try_into().unwrap(),
constants);
for i in 0..W {
let out = witness.get_wire(
Wire { gate: 1, input: Gate::wire_output(i) });
assert_eq!(out, expected_outputs[i]);
}
2021-02-26 23:30:22 -08:00
}
}