Skip to content

Commit

Permalink
Merge pull request #10 from levs57/feat/external-interface
Browse files Browse the repository at this point in the history
feat/external interface
  • Loading branch information
rebenkoy committed Nov 22, 2023
2 parents 5b37c3f + 3584e61 commit c8c5d1f
Show file tree
Hide file tree
Showing 12 changed files with 267 additions and 120 deletions.
6 changes: 4 additions & 2 deletions benches/bench_ecmul.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ pub fn ecmul_pseudo_fold(c: &mut Criterion) {

assemble_ecmul_circuit(&mut circuit, &pi, num_limbs);

let mut instance = circuit.finalize();
let constructed = circuit.finalize();
let mut instance = constructed.spawn();

let pi_a_ext = (pi[0], pi[1]);
let pi_b_ext = (pi[2], pi[3]); // a*(1+9+...+9^{nl-1})+b=0 must be checked out of band
Expand Down Expand Up @@ -141,7 +142,8 @@ pub fn ecmul_msm(c: &mut Criterion) {

assemble_ecmul_circuit(&mut circuit, &pi, num_limbs);

let mut instance = circuit.finalize();
let constructed = circuit.finalize();
let mut instance = constructed.spawn();

let pi_a_ext = (pi[0], pi[1]);
let pi_b_ext = (pi[2], pi[3]); // a*(1+9+...+9^{nl-1})+b=0 must be checked out of band
Expand Down
6 changes: 4 additions & 2 deletions benches/bench_poseidons.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ pub fn poseidons_pseudo_fold(c: &mut Criterion) {

assemble_poseidon_circuit(&mut circuit, &cfg, pi);

let mut instance = circuit.finalize();
let constructed = circuit.finalize();
let mut instance = constructed.spawn();

instance.set_ext(pi, F::random(OsRng));
instance.execute(0);
Expand Down Expand Up @@ -99,7 +100,8 @@ pub fn poseidons_msm(c: &mut Criterion) {
let pi = circuit.ext_val(1)[0];
assemble_poseidon_circuit(&mut circuit, &cfg, pi);

let mut instance = circuit.finalize();
let constructed = circuit.finalize();
let mut instance = constructed.spawn();

instance.set_ext(pi, F::random(OsRng));
instance.execute(0);
Expand Down
91 changes: 56 additions & 35 deletions src/circuit.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::{rc::Rc, marker::PhantomData, iter::repeat_with};
use std::{rc::{Rc, Weak}, marker::PhantomData, iter::repeat_with, cell::RefCell};
use elsa::map::FrozenMap;
use ff::PrimeField;

use crate::{witness::CSWtns, gate::{Gatebb, Gate}, constraint_system::{Variable, ConstraintSystem, CommitKind, Visibility, CS, Constraint}, utils::poly_utils::check_poly, circuit::circuit_operations::{AttachedAdvice, AttachedPolynomialAdvice, AttachedAdvicePub}};
use crate::{witness::CSWtns, gate::{Gatebb, Gate}, constraint_system::{Variable, ConstraintSystem, CommitKind, Visibility, CS, Constraint}, utils::poly_utils::check_poly, circuit::circuit_operations::{AttachedAdvice, AttachedPolynomialAdvice, AttachedAdvicePub}, external_interface::{RunIndex, RunAllocator} };

use self::circuit_operations::CircuitOperation;

Expand Down Expand Up @@ -74,11 +74,11 @@ impl<F: PrimeField> InternalValue<F> {
pub ivar: usize,
pub iext: usize,
pub o: usize,
pub f: Rc<dyn Fn(&[F], &[F])-> Vec<F> + 'closure>,
pub f: Rc<dyn Fn(&[F], &RunIndex)-> Vec<F> + 'closure>,
}

impl<'closure, F: PrimeField> Advice<'closure, F> {
pub fn new(ivar: usize, iext: usize, o: usize, f: impl Fn(&[F], &[F]) -> Vec<F> + 'closure) -> Self {
pub fn new(ivar: usize, iext: usize, o: usize, f: impl Fn(&[F], &RunIndex) -> Vec<F> + 'closure) -> Self {
let f = Rc::new(f);

Self { ivar, iext, o, f }
Expand All @@ -104,10 +104,10 @@ pub mod circuit_operations {
use std::rc::Rc;
use ff::PrimeField;
use crate::{constraint_system::Variable, gate::Gate, witness::CSWtns};
use super::{ExternalValue, InternalValue};
use super::{ExternalValue, RunIndex};

pub trait CircuitOperation<'a, F: PrimeField, G: Gate<'a, F>> {
fn execute(&self, witness: &mut CSWtns<'a, F, G>);
fn execute(&self, witness: &mut CSWtns<'a, F, G>, idx: &RunIndex);
}

pub struct AttachedAdvicePub<'advice, F: PrimeField> {
Expand All @@ -123,7 +123,7 @@ pub mod circuit_operations {
}

impl<'advice, F: PrimeField, G: Gate<'advice, F>> CircuitOperation<'advice, F, G> for AttachedAdvicePub<'advice, F> {
fn execute(&self, witness: &mut CSWtns<'advice, F, G>) {
fn execute(&self, witness: &mut CSWtns<'advice, F, G>, idx: &RunIndex) {
let aux: Vec<_> = self.aux.iter().map(|ev| witness.getext(*ev)).collect();

let output = (self.closure)(&aux);
Expand All @@ -136,23 +136,21 @@ pub mod circuit_operations {

pub struct AttachedAdvice<'advice, F: PrimeField> {
input: Vec<Variable>,
aux: Vec<&'advice InternalValue<F>>,
output: Vec<Variable>,
closure: Rc<dyn Fn(&[F], &[F]) -> Vec<F> + 'advice>,
closure: Rc<dyn Fn(&[F], &RunIndex) -> Vec<F> + 'advice>,
}

impl<'advice, F: PrimeField> AttachedAdvice<'advice, F> {
pub fn new(input: Vec<Variable>, aux: Vec<&'advice InternalValue<F>>, output: Vec<Variable>, closure: Rc<dyn Fn(&[F], &[F]) -> Vec<F> + 'advice>) -> Self {
Self { input, aux, output, closure }
pub fn new(input: Vec<Variable>, output: Vec<Variable>, closure: Rc<dyn Fn(&[F], &RunIndex) -> Vec<F> + 'advice>) -> Self {
Self { input, output, closure }
}
}

impl<'advice, F: PrimeField, G: Gate<'advice, F>> CircuitOperation<'advice, F, G> for AttachedAdvice<'advice, F> {
fn execute(&self, witness: &mut CSWtns<'advice, F, G>) {
fn execute(&self, witness: &mut CSWtns<'advice, F, G>, idx: &RunIndex) {
let input = witness.get_vars(&self.input);
let aux: Vec<_> = self.aux.iter().map(|ev| ev.get().expect("external values should be set before execution")).collect();

let output = (self.closure)(&input, &aux);
let output = (self.closure)(&input, &idx);

let value_set: Vec<_> = self.output.iter().cloned().zip(output).collect();
witness.set_vars(&value_set);
Expand All @@ -172,7 +170,7 @@ pub mod circuit_operations {
}

impl<'closure, F: PrimeField, G: Gate<'closure, F>> CircuitOperation<'closure, F, G> for AttachedPolynomialAdvice<'closure, F> {
fn execute(&self, witness: &mut CSWtns<'closure, F, G>) {
fn execute(&self, witness: &mut CSWtns<'closure, F, G>, _: &RunIndex) {
let input = witness.get_vars(&self.input);

let output = (self.closure)(&input, &[]);
Expand Down Expand Up @@ -216,7 +214,7 @@ where
prep
}

pub fn advice(&mut self, round: usize, advice: Advice<'circuit, F>, input: Vec<Variable>, aux: Vec<&'circuit InternalValue<F>>) -> Vec<Variable> {
pub fn advice(&mut self, round: usize, advice: Advice<'circuit, F>, input: Vec<Variable>) -> Vec<Variable> {
assert!(round < self.ops.len(), "The round is too large.");

let op_index = self.ops[round].len();
Expand All @@ -226,10 +224,9 @@ where
}

assert!(input.len() == advice.ivar, "Incorrect amount of input vars at operation #{} (round {})", op_index, round);
assert!(aux.len() == advice.iext, "Incorrect amount of internal vals at operation #{} (round {})", op_index, round);

let output = self.cs.alloc_in_round(round, Visibility::Private, advice.o);
let operation = Box::new(AttachedAdvice::new(input, aux, output.clone(), advice.f.clone()));
let operation = Box::new(AttachedAdvice::new(input, output.clone(), advice.f.clone()));
self.ops[round].push(operation);

output
Expand Down Expand Up @@ -310,15 +307,10 @@ where
self.advice_pub(round, adv, vec![pi])[0]
}

pub fn finalize(self) -> SealedCircuit<'circuit, F, G> {
let cs = CSWtns::<F,G>::new(&self.cs);

let circuit = Rc::new(self);

SealedCircuit {
cs,
circuit,
round_counter: 0,
pub fn finalize(self) -> ConstructedCircuit<'circuit, F, G> {
ConstructedCircuit {
circuit: self,
run_allocator: RefCell::new(RunAllocator::new()),
}
}

Expand All @@ -331,13 +323,34 @@ where
}
}

pub struct SealedCircuit<'circuit, F: PrimeField, G: Gate<'circuit, F> + From<PolyOp<'circuit, F>>>{
circuit: Rc <Circuit<'circuit, F, G>>,
pub struct ConstructedCircuit<'circuit, F: PrimeField, G: Gate<'circuit, F> + From<PolyOp<'circuit, F>>> {
circuit: Circuit<'circuit, F, G>,
run_allocator: RefCell<RunAllocator>,
}

impl<'circuit, F: PrimeField, G: Gate<'circuit, F> + From<PolyOp<'circuit, F>>> ConstructedCircuit<'circuit, F, G> {
pub fn spawn<'constructed>(&'constructed self) -> CircuitRun<'constructed, 'circuit, F, G> {
CircuitRun {
constructed: &self,
cs: CSWtns::<F,G>::new(&self.circuit.cs),
round_counter: 0,
run_idx: self.run_allocator.borrow_mut().allocate(),
}
}

fn deallocate<'constructed>(&'constructed self, idx: RunIndex) {
self.run_allocator.borrow_mut().deallocate(idx);
}
}

pub struct CircuitRun<'constructed, 'circuit, F: PrimeField, G: Gate<'circuit, F> + From<PolyOp<'circuit, F>>>{
constructed: &'constructed ConstructedCircuit<'circuit, F, G>,
pub cs : CSWtns<'circuit, F, G>,
round_counter : usize,
run_idx: RunIndex,
}

impl<'circuit, F, G> SealedCircuit<'circuit, F, G>
impl<'constructed, 'circuit, F, G> CircuitRun<'constructed, 'circuit, F, G>
where
F: PrimeField,
G: Gate<'circuit, F> + From<PolyOp<'circuit, F>>,
Expand All @@ -347,8 +360,8 @@ where
assert!(self.round_counter <= round, "Execution is already at round {}, tried to execute up to round {}", self.round_counter, round);

while self.round_counter <= round {
for op in &self.circuit.ops[self.round_counter] {
op.execute(&mut self.cs);
for op in &self.constructed.circuit.ops[self.round_counter] {
op.execute(&mut self.cs, &self.run_idx);
}
self.round_counter += 1;
}
Expand All @@ -360,7 +373,7 @@ where


pub fn valid_witness(&self) -> () {
for constr in self.circuit.cs.iter_constraints() {
for constr in self.constructed.circuit.cs.iter_constraints() {
let input_values: Vec<_> = constr.inputs.iter().map(|&x| self.cs.getvar(x)).collect();
let result = constr.gate.exec(&input_values);

Expand All @@ -369,6 +382,14 @@ where
}

pub fn iter_constraints(&self) -> impl Iterator<Item = &Constraint<'circuit, F, G>> {
self.circuit.cs.iter_constraints()
self.constructed.circuit.cs.iter_constraints()
}
}

pub fn finish(self) -> (CSWtns<'circuit, F, G>, &'constructed ConstraintSystem<'circuit, F, G>) {
let Self {constructed, cs, round_counter: _, run_idx} = self;

constructed.deallocate(run_idx);
(cs, &constructed.circuit.cs)
}
}

105 changes: 105 additions & 0 deletions src/external_interface.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
use std::{cell::RefCell, rc::Rc};

pub struct RunAllocator {
free_slots: Vec<usize>,
total_spawned: usize,
active: usize,
allocated: usize,
}

impl RunAllocator {
pub fn new() -> Self {
Self { free_slots: vec![], total_spawned: 0, active: 0, allocated: 0 }
}

pub fn allocate(&mut self) -> RunIndex {
let slot = self.free_slots.pop().unwrap_or_else(|| {
self.allocated += 1;
self.allocated - 1
});
self.active += 1;
self.total_spawned += 1;
RunIndex { muid: self.total_spawned - 1, slot }
}

pub fn deallocate(&mut self, idx: RunIndex) {
self.free_slots.push(idx.slot);
self.active -= 1;
}
}

#[derive(Debug)]
pub struct RunIndex {
pub(crate) muid: usize,
pub(crate) slot: usize,
}

struct CuratedSlot<T> {
value: T,
muid: usize,
}

#[derive(Clone)]
pub struct InnerValue<T: Clone> {
data: Rc<RefCell<Vec<Option<CuratedSlot<T>>>>>
}

impl<T: Clone> InnerValue<T> {
pub fn new() -> Self {
Self {
data: Rc::new(RefCell::new(vec![])),
}
}

pub fn get(&self, idx: &RunIndex) -> Option<T> {
match self.data.borrow().get(idx.slot) {
Some(opt) => match opt {
Some(CuratedSlot{value, muid}) => match muid == &idx.muid {
true => Some(value.clone()),
false => match muid < &idx.muid {
true => None,
false => panic!("Accessing value with muid {} by idx: {:?}", muid, idx),
},
},
None => None,
},
None => None,
}

}

pub fn set(&self, idx: &RunIndex, value: T) {
assert!(self.replace(idx, value).is_none(), "Attempting to set value by idx: {:?}, but is already set", idx)
}

#[must_use = "if you intended to set a value, consider `set` method instead"]
pub fn replace(&self, idx: &RunIndex, value: T) -> Option<T> {
let mut data = self.data.borrow_mut();
if data.len() <= idx.slot {
data.resize_with(idx.slot + 1, || None);
}
match data[idx.slot] {
Some(CuratedSlot{muid, value: _}) => assert!(muid <= idx.muid, "Writing value with muid {} by idx: {:?}", muid, idx),
None => (),
}
data[idx.slot].replace(CuratedSlot { value: value.clone(), muid: idx.muid }).and_then(|CuratedSlot{value, muid}| if muid == idx.muid {Some(value)} else {None})
}
}


#[test]
fn normal_usage() {
let x = InnerValue::new();

x.set(&RunIndex { muid: 0, slot: 0 }, 0);
x.set(&RunIndex { muid: 1, slot: 1 }, 1);
x.set(&RunIndex { muid: 2, slot: 2 }, 2);
assert_eq!(x.get(&RunIndex { muid: 0, slot: 0 }), Some(0));
assert_eq!(x.get(&RunIndex { muid: 1, slot: 1 }), Some(1));
assert_eq!(x.get(&RunIndex { muid: 2, slot: 2 }), Some(2));
assert_eq!(x.get(&RunIndex { muid: 3, slot: 0 }), None);
x.set(&RunIndex { muid: 3, slot: 0 }, 3);
assert_eq!(x.get(&RunIndex { muid: 3, slot: 0 }), Some(3));
assert_eq!(x.replace(&RunIndex { muid: 1, slot: 1 }, 5), Some(1))
}

1 change: 0 additions & 1 deletion src/gadgets/bits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ pub fn bit_decomposition_gadget<'a, F: PrimeField+FieldUtils>(circuit: &mut Circ
}
),
vec![input],
vec![]
);

let bitcheck_gate = Gatebb::new(2, 1, 1, Rc::new(|args, _| bitcheck::<F>(args)), vec![]);
Expand Down
3 changes: 0 additions & 3 deletions src/gadgets/ecmul.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,6 @@ pub fn ecadd_gadget<'a, F: PrimeField+FieldUtils, C: CurveExt<Base=F>>(
}
),
vec![pt1.x, pt1.y, pt2.x, pt2.y],
vec![]
);

let pt3 = EcAffinePoint::<F,C>::new(circuit, tmp[0], tmp[1]);
Expand Down Expand Up @@ -223,7 +222,6 @@ pub fn ecdouble_gadget<'a, F: PrimeField+FieldUtils, C: CurveExt<Base=F>>(
}
),
vec![pt.x, pt.y],
vec![]
);

let pt2 = EcAffinePoint::<F,C>::new(circuit, tmp[0], tmp[1]);
Expand Down Expand Up @@ -384,7 +382,6 @@ pub fn escalarmul_gadget_9<'a, F: PrimeField + FieldUtils, C: CurveExt<Base=F>>(
round,
adv,
pts_limbs.clone(),
vec![]
);

let (acc, rest) = advices.split_at(2*(num_limbs-1));
Expand Down
Loading

0 comments on commit c8c5d1f

Please sign in to comment.