From b74cbc0283dfa5400ad1fdfc63500d646578cdba Mon Sep 17 00:00:00 2001 From: Gabriel Barreto Date: Wed, 11 Oct 2023 18:54:04 -0300 Subject: [PATCH 1/5] WIP bit decomposition slot --- src/lem/circuit.rs | 5 ++++- src/lem/eval.rs | 1 + src/lem/interpreter.rs | 11 +++------- src/lem/slot.rs | 50 ++++++++++++++++++++++++++++++++++++------ src/lem/tests/misc.rs | 8 +++---- 5 files changed, 55 insertions(+), 20 deletions(-) diff --git a/src/lem/circuit.rs b/src/lem/circuit.rs index 88966c633b..4997241fd4 100644 --- a/src/lem/circuit.rs +++ b/src/lem/circuit.rs @@ -48,7 +48,7 @@ use crate::{ }; use super::{ - interpreter::{Frame, PreimageData}, + interpreter::Frame, pointers::{Ptr, ZPtr}, slot::*, store::Store, @@ -173,6 +173,7 @@ fn allocate_img_for_slot>( let lt = or(&mut cs.namespace(|| "or"), &and1, &and2)?; AllocatedVal::Boolean(lt) } + SlotType::BitDecomp => todo!(), } }; Ok(preallocated_img) @@ -201,6 +202,7 @@ fn allocate_slots>( idx: slot_idx, typ: slot_type, }; + assert!(slot_type.is_compatible(preimg_data)); // Allocate the preimage because the image depends on it let mut preallocated_preimg = Vec::with_capacity(slot_type.preimg_size()); @@ -259,6 +261,7 @@ fn allocate_slots>( || *b, )); } + PreimageData::F(..) => todo!(), } // Allocate the image by calling the arithmetic function according diff --git a/src/lem/eval.rs b/src/lem/eval.rs index 260cf73f0e..f4d466a950 100644 --- a/src/lem/eval.rs +++ b/src/lem/eval.rs @@ -1699,6 +1699,7 @@ mod tests { hash8: 4, commitment: 1, less_than: 1, + bit_decomp: 1, }; #[test] diff --git a/src/lem/interpreter.rs b/src/lem/interpreter.rs index 885cb89d52..c5afb8a664 100644 --- a/src/lem/interpreter.rs +++ b/src/lem/interpreter.rs @@ -2,7 +2,8 @@ use anyhow::{anyhow, bail, Result}; use std::collections::VecDeque; use super::{ - path::Path, pointers::Ptr, store::Store, var_map::VarMap, Block, Ctrl, Func, Op, Tag, Var, + path::Path, pointers::Ptr, slot::PreimageData, store::Store, var_map::VarMap, Block, Ctrl, + Func, Op, Tag, Var, }; use crate::{ @@ -14,13 +15,6 @@ use crate::{ tag::ExprTag::{Comm, Nil, Num, Sym}, }; -#[derive(Clone, Debug)] -pub enum PreimageData { - PtrVec(Vec>), - FPtr(F, Ptr), - FPair(F, F), -} - pub enum Val { Pointer(Ptr), Boolean(bool), @@ -297,6 +291,7 @@ impl Block { assert!(*n <= 64); let a = bindings.get_ptr(a)?; let c = if let Ptr::Atom(_, f) = a { + preimages.less_than.push(Some(PreimageData::F(f))); let b = if *n < 64 { (1 << *n) - 1 } else { u64::MAX }; Ptr::Atom(Tag::Expr(Num), F::from_u64(f.to_u64_unchecked() & b)) } else { diff --git a/src/lem/slot.rs b/src/lem/slot.rs index 94ca84b44b..3640219d63 100644 --- a/src/lem/slot.rs +++ b/src/lem/slot.rs @@ -103,7 +103,8 @@ //! STEP 2 will need as many iterations as it takes to evaluate the Lurk //! expression and so will STEP 3. -use super::{Block, Ctrl, Op}; +use super::{pointers::Ptr, Block, Ctrl, Op}; +use crate::field::LurkField; #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] pub struct SlotsCounter { @@ -112,18 +113,20 @@ pub struct SlotsCounter { pub hash8: usize, pub commitment: usize, pub less_than: usize, + pub bit_decomp: usize, } impl SlotsCounter { /// This interface is mostly for testing #[inline] - pub fn new(num_slots: (usize, usize, usize, usize, usize)) -> Self { + pub fn new(num_slots: (usize, usize, usize, usize, usize, usize)) -> Self { Self { hash4: num_slots.0, hash6: num_slots.1, hash8: num_slots.2, commitment: num_slots.3, less_than: num_slots.4, + bit_decomp: num_slots.5, } } @@ -157,6 +160,12 @@ impl SlotsCounter { self.less_than - 1 } + #[inline] + pub fn consume_bit_decomp(&mut self) -> usize { + self.bit_decomp += 1; + self.bit_decomp - 1 + } + #[inline] pub fn max(&self, other: Self) -> Self { use std::cmp::max; @@ -166,6 +175,7 @@ impl SlotsCounter { hash8: max(self.hash8, other.hash8), commitment: max(self.commitment, other.commitment), less_than: max(self.less_than, other.less_than), + bit_decomp: max(self.bit_decomp, other.bit_decomp), } } @@ -177,6 +187,7 @@ impl SlotsCounter { hash8: self.hash8 + other.hash8, commitment: self.commitment + other.commitment, less_than: self.less_than + other.less_than, + bit_decomp: self.bit_decomp + other.bit_decomp, } } @@ -190,11 +201,12 @@ impl Block { pub fn count_slots(&self) -> SlotsCounter { let ops_slots = self.ops.iter().fold(SlotsCounter::default(), |acc, op| { let val = match op { - Op::Cons2(..) | Op::Decons2(..) => SlotsCounter::new((1, 0, 0, 0, 0)), - Op::Cons3(..) | Op::Decons3(..) => SlotsCounter::new((0, 1, 0, 0, 0)), - Op::Cons4(..) | Op::Decons4(..) => SlotsCounter::new((0, 0, 1, 0, 0)), - Op::Hide(..) | Op::Open(..) => SlotsCounter::new((0, 0, 0, 1, 0)), - Op::Lt(..) => SlotsCounter::new((0, 0, 0, 0, 1)), + Op::Cons2(..) | Op::Decons2(..) => SlotsCounter::new((1, 0, 0, 0, 0, 0)), + Op::Cons3(..) | Op::Decons3(..) => SlotsCounter::new((0, 1, 0, 0, 0, 0)), + Op::Cons4(..) | Op::Decons4(..) => SlotsCounter::new((0, 0, 1, 0, 0, 0)), + Op::Hide(..) | Op::Open(..) => SlotsCounter::new((0, 0, 0, 1, 0, 0)), + Op::Lt(..) => SlotsCounter::new((0, 0, 0, 0, 1, 0)), + Op::Trunc(..) => SlotsCounter::new((0, 0, 0, 0, 0, 1)), Op::Call(_, func, _) => func.slot, _ => SlotsCounter::default(), }; @@ -227,6 +239,14 @@ impl Block { } } +#[derive(Clone, Debug)] +pub enum PreimageData { + PtrVec(Vec>), + FPtr(F, Ptr), + FPair(F, F), + F(F), +} + #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub(crate) enum SlotType { Hash4, @@ -234,6 +254,7 @@ pub(crate) enum SlotType { Hash8, Commitment, LessThan, + BitDecomp, } impl SlotType { @@ -244,6 +265,20 @@ impl SlotType { Self::Hash8 => 8, Self::Commitment => 3, Self::LessThan => 2, + Self::BitDecomp => 1, + } + } + + pub(crate) fn is_compatible(&self, preimg: &PreimageData) -> bool { + use PreimageData::*; + match (self, preimg) { + (Self::Hash4, PtrVec(..)) + | (Self::Hash6, PtrVec(..)) + | (Self::Hash8, PtrVec(..)) + | (Self::Commitment, FPtr(..)) + | (Self::LessThan, FPair(..)) + | (Self::BitDecomp, F(..)) => true, + _ => false, } } } @@ -256,6 +291,7 @@ impl std::fmt::Display for SlotType { Self::Hash8 => write!(f, "Hash8"), Self::Commitment => write!(f, "Commitment"), Self::LessThan => write!(f, "LessThan"), + Self::BitDecomp => write!(f, "BitDecomp"), } } } diff --git a/src/lem/tests/misc.rs b/src/lem/tests/misc.rs index 710ef01b75..46f5ab357a 100644 --- a/src/lem/tests/misc.rs +++ b/src/lem/tests/misc.rs @@ -123,7 +123,7 @@ fn handles_non_ssa() { }); let inputs = vec![Ptr::num(Fr::from_u64(42))]; - synthesize_test_helper(&func, inputs, SlotsCounter::new((2, 0, 0, 0, 0))); + synthesize_test_helper(&func, inputs, SlotsCounter::new((2, 0, 0, 0, 0, 0))); } #[test] @@ -184,7 +184,7 @@ fn test_hash_slots() { }); let inputs = vec![Ptr::num(Fr::from_u64(42)), Ptr::char('c')]; - synthesize_test_helper(&lem, inputs, SlotsCounter::new((2, 2, 2, 0, 0))); + synthesize_test_helper(&lem, inputs, SlotsCounter::new((2, 2, 2, 0, 0, 0))); } #[test] @@ -218,7 +218,7 @@ fn test_unhash_slots() { }); let inputs = vec![Ptr::num(Fr::from_u64(42)), Ptr::char('c')]; - synthesize_test_helper(&lem, inputs, SlotsCounter::new((3, 3, 3, 0, 0))); + synthesize_test_helper(&lem, inputs, SlotsCounter::new((3, 3, 3, 0, 0, 0))); } #[test] @@ -265,5 +265,5 @@ fn test_unhash_nested_slots() { }); let inputs = vec![Ptr::num(Fr::from_u64(42)), Ptr::char('c')]; - synthesize_test_helper(&lem, inputs, SlotsCounter::new((4, 4, 4, 0, 0))); + synthesize_test_helper(&lem, inputs, SlotsCounter::new((4, 4, 4, 0, 0, 0))); } From 880deb4e3484dbee4e3208a017e3978ec3c7bc07 Mon Sep 17 00:00:00 2001 From: Gabriel Barreto Date: Wed, 11 Oct 2023 19:53:30 -0300 Subject: [PATCH 2/5] finished bit decomp of truncation --- src/lem/circuit.rs | 61 +++++++++++++++++++++++++++++++----------- src/lem/eval.rs | 4 +-- src/lem/interpreter.rs | 14 ++++++++-- src/lem/slot.rs | 19 +++++++------ 4 files changed, 69 insertions(+), 29 deletions(-) diff --git a/src/lem/circuit.rs b/src/lem/circuit.rs index 4997241fd4..11edef245d 100644 --- a/src/lem/circuit.rs +++ b/src/lem/circuit.rs @@ -34,9 +34,10 @@ use std::collections::{HashMap, HashSet, VecDeque}; use crate::{ circuit::gadgets::{ constraints::{ - add, alloc_equal, alloc_is_zero, allocate_is_negative, and, div, enforce_pack, + add, alloc_equal, alloc_is_zero, allocate_is_negative, and, div, enforce_product_and_sum, enforce_selector_with_premise, implies_equal, - implies_equal_const, implies_u64, implies_unequal_const, mul, or, pick, sub, + implies_equal_const, implies_pack, implies_u64, implies_unequal_const, mul, or, pick, + sub, }, data::{allocate_constant, hash_poseidon}, pointer::AllocatedPtr, @@ -60,6 +61,7 @@ pub enum AllocatedVal { Pointer(AllocatedPtr), Number(AllocatedNum), Boolean(Boolean), + Bits(Vec), } /// Manages global allocations for constants in a constraint system @@ -173,7 +175,13 @@ fn allocate_img_for_slot>( let lt = or(&mut cs.namespace(|| "or"), &and1, &and2)?; AllocatedVal::Boolean(lt) } - SlotType::BitDecomp => todo!(), + SlotType::BitDecomp => { + let a_num = &preallocated_preimg[0]; + let bits = a_num + .to_bits_le_strict(&mut cs.namespace(|| "a_num_bits")) + .unwrap(); + AllocatedVal::Bits(bits) + } } }; Ok(preallocated_img) @@ -261,7 +269,12 @@ fn allocate_slots>( || *b, )); } - PreimageData::F(..) => todo!(), + PreimageData::F(a) => { + preallocated_preimg.push(AllocatedNum::alloc_infallible( + cs.namespace(|| format!("component 0 slot {slot}")), + || *a, + )); + } } // Allocate the image by calling the arithmetic function according @@ -502,6 +515,14 @@ impl Func { store, )?; + let preallocated_bit_decomp_slots = allocate_slots( + cs, + &frame.preimages.bit_decomp, + SlotType::BitDecomp, + self.slot.bit_decomp, + store, + )?; + struct Globals<'a, F: LurkField, C: Coprocessor> { lang: &'a Lang, store: &'a Store, @@ -511,6 +532,7 @@ impl Func { preallocated_hash8_slots: Vec<(Vec>, AllocatedVal)>, preallocated_commitment_slots: Vec<(Vec>, AllocatedVal)>, preallocated_less_than_slots: Vec<(Vec>, AllocatedVal)>, + preallocated_bit_decomp_slots: Vec<(Vec>, AllocatedVal)>, call_outputs: &'a VecDeque>>, call_idx: usize, cproc_outputs: &'a [Vec>], @@ -903,9 +925,16 @@ impl Func { Op::Trunc(tgt, a, n) => { assert!(*n <= 64); let a = bound_allocations.get_ptr(a)?; - let mut trunc_bits = - a.hash().to_bits_le_strict(cs.namespace(|| "to_bits_le"))?; - trunc_bits.truncate(*n as usize); + let (preallocated_preimg, trunc_bits) = + &g.preallocated_bit_decomp_slots[next_slot.consume_bit_decomp()]; + implies_equal( + &mut cs.namespace(|| "implies equal component trunc"), + not_dummy, + a.hash(), + &preallocated_preimg[0], + ); + let AllocatedVal::Bits(trunc_bits) = trunc_bits else { panic!("Expected bits") }; + let trunc_bits = &trunc_bits[0..*n as usize]; let trunc = AllocatedNum::alloc(cs.namespace(|| "trunc"), || { let b = if *n < 64 { (1 << *n) - 1 } else { u64::MAX }; a.hash() @@ -913,7 +942,12 @@ impl Func { .map(|a| F::from_u64(a.to_u64_unchecked() & b)) .ok_or(SynthesisError::AssignmentMissing) })?; - enforce_pack(cs.namespace(|| "enforce_trunc"), &trunc_bits, &trunc); + implies_pack( + cs.namespace(|| "implies_trunc"), + not_dummy, + trunc_bits, + &trunc, + ); let tag = g .global_allocator .get_allocated_const_cloned(Tag::Expr(Num).to_field())?; @@ -1255,6 +1289,7 @@ impl Func { preallocated_hash8_slots, preallocated_commitment_slots, preallocated_less_than_slots, + preallocated_bit_decomp_slots, call_outputs, call_idx, cproc_outputs, @@ -1331,15 +1366,10 @@ impl Func { globals.insert(FWrap(F::ONE)); num_constraints += 5; } - Op::Lt(..) => { + Op::Lt(..) | Op::Trunc(..) => { globals.insert(FWrap(Tag::Expr(Num).to_field())); num_constraints += 2; } - Op::Trunc(..) => { - globals.insert(FWrap(Tag::Expr(Num).to_field())); - // bit decomposition + enforce_pack - num_constraints += 389; - } Op::DivRem64(..) => { globals.insert(FWrap(Tag::Expr(Num).to_field())); // three implies_u64, one sub and one linear @@ -1436,7 +1466,8 @@ impl Func { + 337 * self.slot.hash6 + 388 * self.slot.hash8 + 265 * self.slot.commitment - + 1172 * self.slot.less_than; + + 1172 * self.slot.less_than + + 388 * self.slot.bit_decomp; let num_constraints = recurse(&self.body, globals, store, false); slot_constraints + num_constraints + globals.len() } diff --git a/src/lem/eval.rs b/src/lem/eval.rs index f4d466a950..92de7a02d0 100644 --- a/src/lem/eval.rs +++ b/src/lem/eval.rs @@ -1691,8 +1691,8 @@ mod tests { use blstrs::Scalar as Fr; const NUM_INPUTS: usize = 1; - const NUM_AUX: usize = 10510; - const NUM_CONSTRAINTS: usize = 12437; + const NUM_AUX: usize = 9737; + const NUM_CONSTRAINTS: usize = 11664; const NUM_SLOTS: SlotsCounter = SlotsCounter { hash4: 14, hash6: 3, diff --git a/src/lem/interpreter.rs b/src/lem/interpreter.rs index c5afb8a664..47cd0996f9 100644 --- a/src/lem/interpreter.rs +++ b/src/lem/interpreter.rs @@ -59,6 +59,7 @@ pub struct Preimages { pub hash8: Vec>>, pub commitment: Vec>>, pub less_than: Vec>>, + pub bit_decomp: Vec>>, pub call_outputs: VecDeque>>, pub cproc_outputs: Vec>>, } @@ -71,6 +72,7 @@ impl Preimages { let hash8 = Vec::with_capacity(slot.hash8); let commitment = Vec::with_capacity(slot.commitment); let less_than = Vec::with_capacity(slot.less_than); + let bit_decomp = Vec::with_capacity(slot.bit_decomp); let call_outputs = VecDeque::new(); let cproc_outputs = Vec::new(); Preimages { @@ -79,6 +81,7 @@ impl Preimages { hash8, commitment, less_than, + bit_decomp, call_outputs, cproc_outputs, } @@ -91,6 +94,7 @@ impl Preimages { let hash8 = vec![None; slot.hash8]; let commitment = vec![None; slot.commitment]; let less_than = vec![None; slot.less_than]; + let bit_decomp = vec![None; slot.bit_decomp]; let call_outputs = VecDeque::new(); let cproc_outputs = Vec::new(); Preimages { @@ -99,6 +103,7 @@ impl Preimages { hash8, commitment, less_than, + bit_decomp, call_outputs, cproc_outputs, } @@ -291,11 +296,11 @@ impl Block { assert!(*n <= 64); let a = bindings.get_ptr(a)?; let c = if let Ptr::Atom(_, f) = a { - preimages.less_than.push(Some(PreimageData::F(f))); + preimages.bit_decomp.push(Some(PreimageData::F(f))); let b = if *n < 64 { (1 << *n) - 1 } else { u64::MAX }; Ptr::Atom(Tag::Expr(Num), F::from_u64(f.to_u64_unchecked() & b)) } else { - bail!("`Trunc` only works a leaf") + bail!("`Trunc` only works on atoms") }; bindings.insert_ptr(tgt.clone(), c); } @@ -514,6 +519,7 @@ impl Func { let hash8_init = preimages.hash8.len(); let commitment_init = preimages.commitment.len(); let less_than_init = preimages.less_than.len(); + let bit_decomp_init = preimages.bit_decomp.len(); let mut res = self.body.run( args, @@ -532,6 +538,7 @@ impl Func { let hash8_used = preimages.hash8.len() - hash8_init; let commitment_used = preimages.commitment.len() - commitment_init; let less_than_used = preimages.less_than.len() - less_than_init; + let bit_decomp_used = preimages.bit_decomp.len() - bit_decomp_init; for _ in hash4_used..self.slot.hash4 { preimages.hash4.push(None); @@ -548,6 +555,9 @@ impl Func { for _ in less_than_used..self.slot.less_than { preimages.less_than.push(None); } + for _ in bit_decomp_used..self.slot.bit_decomp { + preimages.bit_decomp.push(None); + } Ok(res) } diff --git a/src/lem/slot.rs b/src/lem/slot.rs index 3640219d63..cb34ed7ca6 100644 --- a/src/lem/slot.rs +++ b/src/lem/slot.rs @@ -270,16 +270,15 @@ impl SlotType { } pub(crate) fn is_compatible(&self, preimg: &PreimageData) -> bool { - use PreimageData::*; - match (self, preimg) { - (Self::Hash4, PtrVec(..)) - | (Self::Hash6, PtrVec(..)) - | (Self::Hash8, PtrVec(..)) - | (Self::Commitment, FPtr(..)) - | (Self::LessThan, FPair(..)) - | (Self::BitDecomp, F(..)) => true, - _ => false, - } + matches!( + (self, preimg), + (Self::Hash4, PreimageData::PtrVec(..)) + | (Self::Hash6, PreimageData::PtrVec(..)) + | (Self::Hash8, PreimageData::PtrVec(..)) + | (Self::Commitment, PreimageData::FPtr(..)) + | (Self::LessThan, PreimageData::FPair(..)) + | (Self::BitDecomp, PreimageData::F(..)) + ) } } From 118acb38f9ff49e00817c53b23609f46a81f77d2 Mon Sep 17 00:00:00 2001 From: Gabriel Barreto Date: Wed, 11 Oct 2023 20:44:09 -0300 Subject: [PATCH 3/5] bit decomposition slots for less_than --- src/lem/circuit.rs | 131 +++++++++++++++++++++-------------------- src/lem/eval.rs | 7 +-- src/lem/interpreter.rs | 17 ++---- src/lem/slot.rs | 31 +++------- src/lem/tests/misc.rs | 8 +-- 5 files changed, 87 insertions(+), 107 deletions(-) diff --git a/src/lem/circuit.rs b/src/lem/circuit.rs index 11edef245d..bdfa2ca905 100644 --- a/src/lem/circuit.rs +++ b/src/lem/circuit.rs @@ -34,10 +34,9 @@ use std::collections::{HashMap, HashSet, VecDeque}; use crate::{ circuit::gadgets::{ constraints::{ - add, alloc_equal, alloc_is_zero, allocate_is_negative, and, div, - enforce_product_and_sum, enforce_selector_with_premise, implies_equal, - implies_equal_const, implies_pack, implies_u64, implies_unequal_const, mul, or, pick, - sub, + add, alloc_equal, alloc_is_zero, and, div, enforce_product_and_sum, + enforce_selector_with_premise, implies_equal, implies_equal_const, implies_pack, + implies_u64, implies_unequal_const, mul, or, pick, sub, }, data::{allocate_constant, hash_poseidon}, pointer::AllocatedPtr, @@ -152,29 +151,6 @@ fn allocate_img_for_slot>( preallocated_preimg, store.poseidon_cache.constants.c3(), )?), - SlotType::LessThan => { - // When a and b have the same sign, a < b iff a - b < 0 - // When a and b have different signs, a < b iff a is negative - let a_num = &preallocated_preimg[0]; - let b_num = &preallocated_preimg[1]; - let a_is_negative = allocate_is_negative(cs.namespace(|| "a_is_negative"), a_num)?; - let b_is_negative = allocate_is_negative(cs.namespace(|| "b_is_negative"), b_num)?; - // (same_sign && diff_is_neg) || (!same_sign && a_is_neg) - let same_sign = - Boolean::xor(cs.namespace(|| "same_sign"), &a_is_negative, &b_is_negative)? - .not(); - let diff = sub(cs.namespace(|| "diff"), a_num, b_num)?; - let diff_is_negative = - allocate_is_negative(cs.namespace(|| "diff_is_negative"), &diff)?; - let and1 = and(&mut cs.namespace(|| "and1"), &same_sign, &diff_is_negative)?; - let and2 = and( - &mut cs.namespace(|| "and2"), - &same_sign.not(), - &a_is_negative, - )?; - let lt = or(&mut cs.namespace(|| "or"), &and1, &and2)?; - AllocatedVal::Boolean(lt) - } SlotType::BitDecomp => { let a_num = &preallocated_preimg[0]; let bits = a_num @@ -256,19 +232,6 @@ fn allocate_slots>( || *z_ptr.value(), )); } - PreimageData::FPair(a, b) => { - // allocate first component - preallocated_preimg.push(AllocatedNum::alloc_infallible( - cs.namespace(|| format!("component 0 slot {slot}")), - || *a, - )); - - // allocate second component - preallocated_preimg.push(AllocatedNum::alloc_infallible( - cs.namespace(|| format!("component 1 slot {slot}")), - || *b, - )); - } PreimageData::F(a) => { preallocated_preimg.push(AllocatedNum::alloc_infallible( cs.namespace(|| format!("component 0 slot {slot}")), @@ -507,14 +470,6 @@ impl Func { store, )?; - let preallocated_less_than_slots = allocate_slots( - cs, - &frame.preimages.less_than, - SlotType::LessThan, - self.slot.less_than, - store, - )?; - let preallocated_bit_decomp_slots = allocate_slots( cs, &frame.preimages.bit_decomp, @@ -531,7 +486,6 @@ impl Func { preallocated_hash6_slots: Vec<(Vec>, AllocatedVal)>, preallocated_hash8_slots: Vec<(Vec>, AllocatedVal)>, preallocated_commitment_slots: Vec<(Vec>, AllocatedVal)>, - preallocated_less_than_slots: Vec<(Vec>, AllocatedVal)>, preallocated_bit_decomp_slots: Vec<(Vec>, AllocatedVal)>, call_outputs: &'a VecDeque>>, call_idx: usize, @@ -907,19 +861,63 @@ impl Func { bound_allocations.insert_ptr(tgt.clone(), c); } Op::Lt(tgt, a, b) => { - let a = bound_allocations.get_ptr(a)?; - let b = bound_allocations.get_ptr(b)?; - let (preallocated_preimg, lt) = - &g.preallocated_less_than_slots[next_slot.consume_less_than()]; - for (i, n) in [a.hash(), b.hash()].into_iter().enumerate() { - implies_equal( - &mut cs.namespace(|| format!("implies equal component {i}")), - not_dummy, - n, - &preallocated_preimg[i], - ); - } - let AllocatedVal::Boolean(lt) = lt else { panic!("Expected boolean") }; + // Retrieve a, b, a-b + let a_num = bound_allocations.get_ptr(a)?.hash(); + let b_num = bound_allocations.get_ptr(b)?.hash(); + let diff = sub(cs.namespace(|| "diff"), a_num, b_num)?; + // Duplicate a, b, a-b + let double_a = add(cs.namespace(|| "double_a"), a_num, a_num)?; + let double_b = add(cs.namespace(|| "double_b"), b_num, b_num)?; + let double_diff = add(cs.namespace(|| "double_diff"), &diff, &diff)?; + // Get preimg/bits for the double of a, b, a-b + let (double_a_preimg, double_a_bits) = + &g.preallocated_bit_decomp_slots[next_slot.consume_bit_decomp()]; + let AllocatedVal::Bits(double_a_bits) = double_a_bits else { panic!("Expected bits") }; + let (double_b_preimg, double_b_bits) = + &g.preallocated_bit_decomp_slots[next_slot.consume_bit_decomp()]; + let AllocatedVal::Bits(double_b_bits) = double_b_bits else { panic!("Expected bits") }; + let (double_diff_preimg, double_diff_bits) = + &g.preallocated_bit_decomp_slots[next_slot.consume_bit_decomp()]; + let AllocatedVal::Bits(double_diff_bits) = double_diff_bits else { panic!("Expected bits") }; + // Check that the preimg is double of a, b, a-b + implies_equal( + &mut cs.namespace(|| "implies equal for a_preimg"), + not_dummy, + &double_a, + &double_a_preimg[0], + ); + implies_equal( + &mut cs.namespace(|| "implies equal for b_preimg"), + not_dummy, + &double_b, + &double_b_preimg[0], + ); + implies_equal( + &mut cs.namespace(|| "implies equal for diff_preimg"), + not_dummy, + &double_diff, + &double_diff_preimg[0], + ); + + // The number is negative if the first bit of its double is 1 + let a_is_negative = double_a_bits.get(0).unwrap(); + let b_is_negative = double_b_bits.get(0).unwrap(); + let diff_is_negative = double_diff_bits.get(0).unwrap(); + // When a and b have the same sign, a < b iff a - b < 0 + // When a and b have different signs, a < b iff a is negative + let same_sign = Boolean::xor( + cs.namespace(|| "same_sign"), + a_is_negative, + b_is_negative, + )? + .not(); + let and1 = and(&mut cs.namespace(|| "and1"), &same_sign, diff_is_negative)?; + let and2 = and( + &mut cs.namespace(|| "and2"), + &same_sign.not(), + a_is_negative, + )?; + let lt = or(&mut cs.namespace(|| "or"), &and1, &and2)?; bound_allocations.insert_bool(tgt.clone(), lt.clone()); } Op::Trunc(tgt, a, n) => { @@ -1288,7 +1286,6 @@ impl Func { preallocated_hash6_slots, preallocated_hash8_slots, preallocated_commitment_slots, - preallocated_less_than_slots, preallocated_bit_decomp_slots, call_outputs, call_idx, @@ -1366,8 +1363,13 @@ impl Func { globals.insert(FWrap(F::ONE)); num_constraints += 5; } - Op::Lt(..) | Op::Trunc(..) => { + Op::Lt(..) => { + globals.insert(FWrap(Tag::Expr(Num).to_field())); + num_constraints += 11; + } + Op::Trunc(..) => { globals.insert(FWrap(Tag::Expr(Num).to_field())); + // 1 implies_equal, 1 implies_pack num_constraints += 2; } Op::DivRem64(..) => { @@ -1466,7 +1468,6 @@ impl Func { + 337 * self.slot.hash6 + 388 * self.slot.hash8 + 265 * self.slot.commitment - + 1172 * self.slot.less_than + 388 * self.slot.bit_decomp; let num_constraints = recurse(&self.body, globals, store, false); slot_constraints + num_constraints + globals.len() diff --git a/src/lem/eval.rs b/src/lem/eval.rs index 92de7a02d0..73d270a870 100644 --- a/src/lem/eval.rs +++ b/src/lem/eval.rs @@ -1691,15 +1691,14 @@ mod tests { use blstrs::Scalar as Fr; const NUM_INPUTS: usize = 1; - const NUM_AUX: usize = 9737; - const NUM_CONSTRAINTS: usize = 11664; + const NUM_AUX: usize = 9390; + const NUM_CONSTRAINTS: usize = 11322; const NUM_SLOTS: SlotsCounter = SlotsCounter { hash4: 14, hash6: 3, hash8: 4, commitment: 1, - less_than: 1, - bit_decomp: 1, + bit_decomp: 3, }; #[test] diff --git a/src/lem/interpreter.rs b/src/lem/interpreter.rs index 47cd0996f9..9ac2a5b427 100644 --- a/src/lem/interpreter.rs +++ b/src/lem/interpreter.rs @@ -58,7 +58,6 @@ pub struct Preimages { pub hash6: Vec>>, pub hash8: Vec>>, pub commitment: Vec>>, - pub less_than: Vec>>, pub bit_decomp: Vec>>, pub call_outputs: VecDeque>>, pub cproc_outputs: Vec>>, @@ -71,7 +70,6 @@ impl Preimages { let hash6 = Vec::with_capacity(slot.hash6); let hash8 = Vec::with_capacity(slot.hash8); let commitment = Vec::with_capacity(slot.commitment); - let less_than = Vec::with_capacity(slot.less_than); let bit_decomp = Vec::with_capacity(slot.bit_decomp); let call_outputs = VecDeque::new(); let cproc_outputs = Vec::new(); @@ -80,7 +78,6 @@ impl Preimages { hash6, hash8, commitment, - less_than, bit_decomp, call_outputs, cproc_outputs, @@ -93,7 +90,6 @@ impl Preimages { let hash6 = vec![None; slot.hash6]; let hash8 = vec![None; slot.hash8]; let commitment = vec![None; slot.commitment]; - let less_than = vec![None; slot.less_than]; let bit_decomp = vec![None; slot.bit_decomp]; let call_outputs = VecDeque::new(); let cproc_outputs = Vec::new(); @@ -102,7 +98,6 @@ impl Preimages { hash6, hash8, commitment, - less_than, bit_decomp, call_outputs, cproc_outputs, @@ -283,7 +278,12 @@ impl Block { let a = bindings.get_ptr(a)?; let b = bindings.get_ptr(b)?; let c = if let (Ptr::Atom(_, f), Ptr::Atom(_, g)) = (a, b) { - preimages.less_than.push(Some(PreimageData::FPair(f, g))); + let diff = f - g; + preimages.bit_decomp.push(Some(PreimageData::F(f + f))); + preimages.bit_decomp.push(Some(PreimageData::F(g + g))); + preimages + .bit_decomp + .push(Some(PreimageData::F(diff + diff))); let f = BaseNum::Scalar(f); let g = BaseNum::Scalar(g); f < g @@ -518,7 +518,6 @@ impl Func { let hash6_init = preimages.hash6.len(); let hash8_init = preimages.hash8.len(); let commitment_init = preimages.commitment.len(); - let less_than_init = preimages.less_than.len(); let bit_decomp_init = preimages.bit_decomp.len(); let mut res = self.body.run( @@ -537,7 +536,6 @@ impl Func { let hash6_used = preimages.hash6.len() - hash6_init; let hash8_used = preimages.hash8.len() - hash8_init; let commitment_used = preimages.commitment.len() - commitment_init; - let less_than_used = preimages.less_than.len() - less_than_init; let bit_decomp_used = preimages.bit_decomp.len() - bit_decomp_init; for _ in hash4_used..self.slot.hash4 { @@ -552,9 +550,6 @@ impl Func { for _ in commitment_used..self.slot.commitment { preimages.commitment.push(None); } - for _ in less_than_used..self.slot.less_than { - preimages.less_than.push(None); - } for _ in bit_decomp_used..self.slot.bit_decomp { preimages.bit_decomp.push(None); } diff --git a/src/lem/slot.rs b/src/lem/slot.rs index cb34ed7ca6..345ba99201 100644 --- a/src/lem/slot.rs +++ b/src/lem/slot.rs @@ -112,21 +112,19 @@ pub struct SlotsCounter { pub hash6: usize, pub hash8: usize, pub commitment: usize, - pub less_than: usize, pub bit_decomp: usize, } impl SlotsCounter { /// This interface is mostly for testing #[inline] - pub fn new(num_slots: (usize, usize, usize, usize, usize, usize)) -> Self { + pub fn new(num_slots: (usize, usize, usize, usize, usize)) -> Self { Self { hash4: num_slots.0, hash6: num_slots.1, hash8: num_slots.2, commitment: num_slots.3, - less_than: num_slots.4, - bit_decomp: num_slots.5, + bit_decomp: num_slots.4, } } @@ -154,12 +152,6 @@ impl SlotsCounter { self.commitment - 1 } - #[inline] - pub fn consume_less_than(&mut self) -> usize { - self.less_than += 1; - self.less_than - 1 - } - #[inline] pub fn consume_bit_decomp(&mut self) -> usize { self.bit_decomp += 1; @@ -174,7 +166,6 @@ impl SlotsCounter { hash6: max(self.hash6, other.hash6), hash8: max(self.hash8, other.hash8), commitment: max(self.commitment, other.commitment), - less_than: max(self.less_than, other.less_than), bit_decomp: max(self.bit_decomp, other.bit_decomp), } } @@ -186,7 +177,6 @@ impl SlotsCounter { hash6: self.hash6 + other.hash6, hash8: self.hash8 + other.hash8, commitment: self.commitment + other.commitment, - less_than: self.less_than + other.less_than, bit_decomp: self.bit_decomp + other.bit_decomp, } } @@ -201,12 +191,12 @@ impl Block { pub fn count_slots(&self) -> SlotsCounter { let ops_slots = self.ops.iter().fold(SlotsCounter::default(), |acc, op| { let val = match op { - Op::Cons2(..) | Op::Decons2(..) => SlotsCounter::new((1, 0, 0, 0, 0, 0)), - Op::Cons3(..) | Op::Decons3(..) => SlotsCounter::new((0, 1, 0, 0, 0, 0)), - Op::Cons4(..) | Op::Decons4(..) => SlotsCounter::new((0, 0, 1, 0, 0, 0)), - Op::Hide(..) | Op::Open(..) => SlotsCounter::new((0, 0, 0, 1, 0, 0)), - Op::Lt(..) => SlotsCounter::new((0, 0, 0, 0, 1, 0)), - Op::Trunc(..) => SlotsCounter::new((0, 0, 0, 0, 0, 1)), + Op::Cons2(..) | Op::Decons2(..) => SlotsCounter::new((1, 0, 0, 0, 0)), + Op::Cons3(..) | Op::Decons3(..) => SlotsCounter::new((0, 1, 0, 0, 0)), + Op::Cons4(..) | Op::Decons4(..) => SlotsCounter::new((0, 0, 1, 0, 0)), + Op::Hide(..) | Op::Open(..) => SlotsCounter::new((0, 0, 0, 1, 0)), + Op::Lt(..) => SlotsCounter::new((0, 0, 0, 0, 3)), + Op::Trunc(..) => SlotsCounter::new((0, 0, 0, 0, 1)), Op::Call(_, func, _) => func.slot, _ => SlotsCounter::default(), }; @@ -243,7 +233,6 @@ impl Block { pub enum PreimageData { PtrVec(Vec>), FPtr(F, Ptr), - FPair(F, F), F(F), } @@ -253,7 +242,6 @@ pub(crate) enum SlotType { Hash6, Hash8, Commitment, - LessThan, BitDecomp, } @@ -264,7 +252,6 @@ impl SlotType { Self::Hash6 => 6, Self::Hash8 => 8, Self::Commitment => 3, - Self::LessThan => 2, Self::BitDecomp => 1, } } @@ -276,7 +263,6 @@ impl SlotType { | (Self::Hash6, PreimageData::PtrVec(..)) | (Self::Hash8, PreimageData::PtrVec(..)) | (Self::Commitment, PreimageData::FPtr(..)) - | (Self::LessThan, PreimageData::FPair(..)) | (Self::BitDecomp, PreimageData::F(..)) ) } @@ -289,7 +275,6 @@ impl std::fmt::Display for SlotType { Self::Hash6 => write!(f, "Hash6"), Self::Hash8 => write!(f, "Hash8"), Self::Commitment => write!(f, "Commitment"), - Self::LessThan => write!(f, "LessThan"), Self::BitDecomp => write!(f, "BitDecomp"), } } diff --git a/src/lem/tests/misc.rs b/src/lem/tests/misc.rs index 46f5ab357a..710ef01b75 100644 --- a/src/lem/tests/misc.rs +++ b/src/lem/tests/misc.rs @@ -123,7 +123,7 @@ fn handles_non_ssa() { }); let inputs = vec![Ptr::num(Fr::from_u64(42))]; - synthesize_test_helper(&func, inputs, SlotsCounter::new((2, 0, 0, 0, 0, 0))); + synthesize_test_helper(&func, inputs, SlotsCounter::new((2, 0, 0, 0, 0))); } #[test] @@ -184,7 +184,7 @@ fn test_hash_slots() { }); let inputs = vec![Ptr::num(Fr::from_u64(42)), Ptr::char('c')]; - synthesize_test_helper(&lem, inputs, SlotsCounter::new((2, 2, 2, 0, 0, 0))); + synthesize_test_helper(&lem, inputs, SlotsCounter::new((2, 2, 2, 0, 0))); } #[test] @@ -218,7 +218,7 @@ fn test_unhash_slots() { }); let inputs = vec![Ptr::num(Fr::from_u64(42)), Ptr::char('c')]; - synthesize_test_helper(&lem, inputs, SlotsCounter::new((3, 3, 3, 0, 0, 0))); + synthesize_test_helper(&lem, inputs, SlotsCounter::new((3, 3, 3, 0, 0))); } #[test] @@ -265,5 +265,5 @@ fn test_unhash_nested_slots() { }); let inputs = vec![Ptr::num(Fr::from_u64(42)), Ptr::char('c')]; - synthesize_test_helper(&lem, inputs, SlotsCounter::new((4, 4, 4, 0, 0, 0))); + synthesize_test_helper(&lem, inputs, SlotsCounter::new((4, 4, 4, 0, 0))); } From 3654d48b037100976720af4b956e350936135cb8 Mon Sep 17 00:00:00 2001 From: Gabriel Barreto Date: Thu, 12 Oct 2023 09:40:07 -0300 Subject: [PATCH 4/5] Suggestions and explanations --- src/lem/circuit.rs | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/lem/circuit.rs b/src/lem/circuit.rs index bdfa2ca905..88ad61d88e 100644 --- a/src/lem/circuit.rs +++ b/src/lem/circuit.rs @@ -153,9 +153,7 @@ fn allocate_img_for_slot>( )?), SlotType::BitDecomp => { let a_num = &preallocated_preimg[0]; - let bits = a_num - .to_bits_le_strict(&mut cs.namespace(|| "a_num_bits")) - .unwrap(); + let bits = a_num.to_bits_le_strict(&mut cs.namespace(|| "a_num_bits"))?; AllocatedVal::Bits(bits) } } @@ -861,15 +859,21 @@ impl Func { bound_allocations.insert_ptr(tgt.clone(), c); } Op::Lt(tgt, a, b) => { + // To find out whether a < b, we will use the following reasoning: + // 1) when a and b have the same sign, a < b iff a - b is negative + // 2) when a and b have different signs, a < b iff a is negative + // 3) a number is negative iff its double is odd + // 4) a number is odd iff its least significant bit is 1 + // Retrieve a, b, a-b let a_num = bound_allocations.get_ptr(a)?.hash(); let b_num = bound_allocations.get_ptr(b)?.hash(); let diff = sub(cs.namespace(|| "diff"), a_num, b_num)?; - // Duplicate a, b, a-b + // Double a, b, a-b let double_a = add(cs.namespace(|| "double_a"), a_num, a_num)?; let double_b = add(cs.namespace(|| "double_b"), b_num, b_num)?; let double_diff = add(cs.namespace(|| "double_diff"), &diff, &diff)?; - // Get preimg/bits for the double of a, b, a-b + // Get slot allocated preimages/bits for the double of a, b, a-b let (double_a_preimg, double_a_bits) = &g.preallocated_bit_decomp_slots[next_slot.consume_bit_decomp()]; let AllocatedVal::Bits(double_a_bits) = double_a_bits else { panic!("Expected bits") }; @@ -879,7 +883,7 @@ impl Func { let (double_diff_preimg, double_diff_bits) = &g.preallocated_bit_decomp_slots[next_slot.consume_bit_decomp()]; let AllocatedVal::Bits(double_diff_bits) = double_diff_bits else { panic!("Expected bits") }; - // Check that the preimg is double of a, b, a-b + // Check that the slot allocated preimages are the double of a, b, a-b implies_equal( &mut cs.namespace(|| "implies equal for a_preimg"), not_dummy, @@ -899,18 +903,20 @@ impl Func { &double_diff_preimg[0], ); - // The number is negative if the first bit of its double is 1 + // The number is negative if the least significant bit of its double is 1 let a_is_negative = double_a_bits.get(0).unwrap(); let b_is_negative = double_b_bits.get(0).unwrap(); let diff_is_negative = double_diff_bits.get(0).unwrap(); - // When a and b have the same sign, a < b iff a - b < 0 - // When a and b have different signs, a < b iff a is negative + + // Two numbers have the same sign if both are negative or both are positive, i.e. let same_sign = Boolean::xor( cs.namespace(|| "same_sign"), a_is_negative, b_is_negative, )? .not(); + + // Finally, a < b iff (same_sign && diff < 0) || (!same_sign && a < 0) let and1 = and(&mut cs.namespace(|| "and1"), &same_sign, diff_is_negative)?; let and2 = and( &mut cs.namespace(|| "and2"), From c598b7b55049e3b9dfab18447fc73d24baaf5fac Mon Sep 17 00:00:00 2001 From: Gabriel Barreto Date: Thu, 12 Oct 2023 10:07:33 -0300 Subject: [PATCH 5/5] Renames --- src/lem/circuit.rs | 32 ++++----- src/lem/eval.rs | 4 +- src/lem/interpreter.rs | 146 +++++++++++++++++++---------------------- src/lem/slot.rs | 16 ++--- 4 files changed, 94 insertions(+), 104 deletions(-) diff --git a/src/lem/circuit.rs b/src/lem/circuit.rs index 88ad61d88e..043aa42a90 100644 --- a/src/lem/circuit.rs +++ b/src/lem/circuit.rs @@ -164,13 +164,13 @@ fn allocate_img_for_slot>( /// Allocates unconstrained slots fn allocate_slots>( cs: &mut CS, - preimg_data: &[Option>], + slots_data: &[Option>], slot_type: SlotType, num_slots: usize, store: &Store, ) -> Result>, AllocatedVal)>> { assert!( - preimg_data.len() == num_slots, + slots_data.len() == num_slots, "collected preimages not equal to the number of available slots" ); @@ -178,19 +178,19 @@ fn allocate_slots>( // We must perform the allocations for the slots containing data collected // by the interpreter. The `None` cases must be filled with dummy values - for (slot_idx, maybe_preimg_data) in preimg_data.iter().enumerate() { - if let Some(preimg_data) = maybe_preimg_data { + for (slot_idx, maybe_slot_data) in slots_data.iter().enumerate() { + if let Some(slot_data) = maybe_slot_data { let slot = Slot { idx: slot_idx, typ: slot_type, }; - assert!(slot_type.is_compatible(preimg_data)); + assert!(slot_type.is_compatible(slot_data)); // Allocate the preimage because the image depends on it let mut preallocated_preimg = Vec::with_capacity(slot_type.preimg_size()); - match preimg_data { - PreimageData::PtrVec(ptr_vec) => { + match slot_data { + SlotData::PtrVec(ptr_vec) => { let mut component_idx = 0; for ptr in ptr_vec { let z_ptr = store.hash_ptr(ptr)?; @@ -212,7 +212,7 @@ fn allocate_slots>( component_idx += 1; } } - PreimageData::FPtr(f, ptr) => { + SlotData::FPtr(f, ptr) => { let z_ptr = store.hash_ptr(ptr)?; // allocate first component preallocated_preimg.push(AllocatedNum::alloc_infallible( @@ -230,7 +230,7 @@ fn allocate_slots>( || *z_ptr.value(), )); } - PreimageData::F(a) => { + SlotData::F(a) => { preallocated_preimg.push(AllocatedNum::alloc_infallible( cs.namespace(|| format!("component 0 slot {slot}")), || *a, @@ -438,7 +438,7 @@ impl Func { // that's why they are filled with dummies let preallocated_hash4_slots = allocate_slots( cs, - &frame.preimages.hash4, + &frame.advices.hash4, SlotType::Hash4, self.slot.hash4, store, @@ -446,7 +446,7 @@ impl Func { let preallocated_hash6_slots = allocate_slots( cs, - &frame.preimages.hash6, + &frame.advices.hash6, SlotType::Hash6, self.slot.hash6, store, @@ -454,7 +454,7 @@ impl Func { let preallocated_hash8_slots = allocate_slots( cs, - &frame.preimages.hash8, + &frame.advices.hash8, SlotType::Hash8, self.slot.hash8, store, @@ -462,7 +462,7 @@ impl Func { let preallocated_commitment_slots = allocate_slots( cs, - &frame.preimages.commitment, + &frame.advices.commitment, SlotType::Commitment, self.slot.commitment, store, @@ -470,7 +470,7 @@ impl Func { let preallocated_bit_decomp_slots = allocate_slots( cs, - &frame.preimages.bit_decomp, + &frame.advices.bit_decomp, SlotType::BitDecomp, self.slot.bit_decomp, store, @@ -1274,9 +1274,9 @@ impl Func { } } - let call_outputs = &frame.preimages.call_outputs; + let call_outputs = &frame.advices.call_outputs; let call_idx = 0; - let cproc_outputs = &frame.preimages.cproc_outputs; + let cproc_outputs = &frame.advices.cproc_outputs; recurse( cs, &self.body, diff --git a/src/lem/eval.rs b/src/lem/eval.rs index 73d270a870..b561ab90dd 100644 --- a/src/lem/eval.rs +++ b/src/lem/eval.rs @@ -19,7 +19,7 @@ use crate::{ }; use super::{ - interpreter::{Frame, Preimages}, + interpreter::{Advices, Frame}, pointers::Ptr, store::Store, Ctrl, Func, Op, Tag, Var, @@ -72,7 +72,7 @@ fn compute_frame>( .expect("Program counter outside range") }; assert_eq!(func.input_params.len(), input.len()); - let preimages = Preimages::new_from_func(func); + let preimages = Advices::new_from_func(func); let (frame, _) = func.call(input, store, preimages, emitted, lang, pc)?; let must_break = matches!(frame.output[2].tag(), Tag::Cont(Terminal | Error)); Ok((frame, must_break)) diff --git a/src/lem/interpreter.rs b/src/lem/interpreter.rs index 9ac2a5b427..5266c5c218 100644 --- a/src/lem/interpreter.rs +++ b/src/lem/interpreter.rs @@ -2,8 +2,8 @@ use anyhow::{anyhow, bail, Result}; use std::collections::VecDeque; use super::{ - path::Path, pointers::Ptr, slot::PreimageData, store::Store, var_map::VarMap, Block, Ctrl, - Func, Op, Tag, Var, + path::Path, pointers::Ptr, slot::SlotData, store::Store, var_map::VarMap, Block, Ctrl, Func, + Op, Tag, Var, }; use crate::{ @@ -49,22 +49,22 @@ impl VarMap> { } #[derive(Clone, Debug, Default)] -/// `Preimages` hold the non-deterministic advices for hashes and `Func` calls. +/// `Advices` hold the non-deterministic advices for hashes and `Func` calls. /// The hash preimages must have the same shape as the allocated slots for the /// `Func`, and the `None` values are used to fill the unused slots, which are /// later filled by dummy values. -pub struct Preimages { - pub hash4: Vec>>, - pub hash6: Vec>>, - pub hash8: Vec>>, - pub commitment: Vec>>, - pub bit_decomp: Vec>>, +pub struct Advices { + pub hash4: Vec>>, + pub hash6: Vec>>, + pub hash8: Vec>>, + pub commitment: Vec>>, + pub bit_decomp: Vec>>, pub call_outputs: VecDeque>>, pub cproc_outputs: Vec>>, } -impl Preimages { - pub fn new_from_func(func: &Func) -> Preimages { +impl Advices { + pub fn new_from_func(func: &Func) -> Advices { let slot = func.slot; let hash4 = Vec::with_capacity(slot.hash4); let hash6 = Vec::with_capacity(slot.hash6); @@ -73,7 +73,7 @@ impl Preimages { let bit_decomp = Vec::with_capacity(slot.bit_decomp); let call_outputs = VecDeque::new(); let cproc_outputs = Vec::new(); - Preimages { + Advices { hash4, hash6, hash8, @@ -84,7 +84,7 @@ impl Preimages { } } - pub fn blank(func: &Func) -> Preimages { + pub fn blank(func: &Func) -> Advices { let slot = func.slot; let hash4 = vec![None; slot.hash4]; let hash6 = vec![None; slot.hash6]; @@ -93,7 +93,7 @@ impl Preimages { let bit_decomp = vec![None; slot.bit_decomp]; let call_outputs = VecDeque::new(); let cproc_outputs = Vec::new(); - Preimages { + Advices { hash4, hash6, hash8, @@ -115,7 +115,7 @@ pub struct Frame { pub input: Vec>, pub output: Vec>, pub emitted: Vec>, - pub preimages: Preimages, + pub advices: Advices, pub blank: bool, pub pc: usize, } @@ -124,12 +124,12 @@ impl Frame { pub fn blank(func: &Func, pc: usize) -> Frame { let input = vec![Ptr::null(Tag::Expr(Nil)); func.input_params.len()]; let output = vec![Ptr::null(Tag::Expr(Nil)); func.output_size]; - let preimages = Preimages::blank(func); + let advices = Advices::blank(func); Frame { input, output, emitted: Vec::default(), - preimages, + advices, blank: true, pc, } @@ -145,7 +145,7 @@ impl Block { input: &[Ptr], store: &Store, mut bindings: VarMap>, - mut preimages: Preimages, + mut advices: Advices, mut path: Path, emitted: &mut Vec>, lang: &Lang, @@ -165,7 +165,7 @@ impl Block { for (var, ptr) in out.iter().zip(&out_ptrs) { bindings.insert(var.clone(), Val::Pointer(*ptr)); } - preimages.cproc_outputs.push(out_ptrs); + advices.cproc_outputs.push(out_ptrs); } Op::Call(out, func, inp) => { // Get the argument values @@ -176,10 +176,10 @@ impl Block { // save all the inner call outputs, push the output of the call in front // of it, then extend `call_outputs` let mut inner_call_outputs = VecDeque::new(); - std::mem::swap(&mut inner_call_outputs, &mut preimages.call_outputs); + std::mem::swap(&mut inner_call_outputs, &mut advices.call_outputs); let (mut frame, func_path) = - func.call(&inp_ptrs, store, preimages, emitted, lang, pc)?; - std::mem::swap(&mut inner_call_outputs, &mut frame.preimages.call_outputs); + func.call(&inp_ptrs, store, advices, emitted, lang, pc)?; + std::mem::swap(&mut inner_call_outputs, &mut frame.advices.call_outputs); // Extend the path and bind the output variables to the output values path.extend_from_path(&func_path); @@ -187,10 +187,10 @@ impl Block { bindings.insert_ptr(var.clone(), *ptr); } - // Update `preimages` correctly + // Update `advices` correctly inner_call_outputs.push_front(frame.output); - preimages = frame.preimages; - preimages.call_outputs.extend(inner_call_outputs); + advices = frame.advices; + advices.call_outputs.extend(inner_call_outputs); } Op::Null(tgt, tag) => { bindings.insert_ptr(tgt.clone(), Ptr::null(*tag)); @@ -279,11 +279,9 @@ impl Block { let b = bindings.get_ptr(b)?; let c = if let (Ptr::Atom(_, f), Ptr::Atom(_, g)) = (a, b) { let diff = f - g; - preimages.bit_decomp.push(Some(PreimageData::F(f + f))); - preimages.bit_decomp.push(Some(PreimageData::F(g + g))); - preimages - .bit_decomp - .push(Some(PreimageData::F(diff + diff))); + advices.bit_decomp.push(Some(SlotData::F(f + f))); + advices.bit_decomp.push(Some(SlotData::F(g + g))); + advices.bit_decomp.push(Some(SlotData::F(diff + diff))); let f = BaseNum::Scalar(f); let g = BaseNum::Scalar(g); f < g @@ -296,7 +294,7 @@ impl Block { assert!(*n <= 64); let a = bindings.get_ptr(a)?; let c = if let Ptr::Atom(_, f) = a { - preimages.bit_decomp.push(Some(PreimageData::F(f))); + advices.bit_decomp.push(Some(SlotData::F(f))); let b = if *n < 64 { (1 << *n) - 1 } else { u64::MAX }; Ptr::Atom(Tag::Expr(Num), F::from_u64(f.to_u64_unchecked() & b)) } else { @@ -331,18 +329,14 @@ impl Block { let preimg_ptrs = bindings.get_many_ptr(preimg)?; let tgt_ptr = store.intern_2_ptrs(*tag, preimg_ptrs[0], preimg_ptrs[1]); bindings.insert_ptr(img.clone(), tgt_ptr); - preimages - .hash4 - .push(Some(PreimageData::PtrVec(preimg_ptrs))); + advices.hash4.push(Some(SlotData::PtrVec(preimg_ptrs))); } Op::Cons3(img, tag, preimg) => { let preimg_ptrs = bindings.get_many_ptr(preimg)?; let tgt_ptr = store.intern_3_ptrs(*tag, preimg_ptrs[0], preimg_ptrs[1], preimg_ptrs[2]); bindings.insert_ptr(img.clone(), tgt_ptr); - preimages - .hash6 - .push(Some(PreimageData::PtrVec(preimg_ptrs))); + advices.hash6.push(Some(SlotData::PtrVec(preimg_ptrs))); } Op::Cons4(img, tag, preimg) => { let preimg_ptrs = bindings.get_many_ptr(preimg)?; @@ -354,9 +348,7 @@ impl Block { preimg_ptrs[3], ); bindings.insert_ptr(img.clone(), tgt_ptr); - preimages - .hash8 - .push(Some(PreimageData::PtrVec(preimg_ptrs))); + advices.hash8.push(Some(SlotData::PtrVec(preimg_ptrs))); } Op::Decons2(preimg, img) => { let img_ptr = bindings.get_ptr(img)?; @@ -370,9 +362,9 @@ impl Block { for (var, ptr) in preimg.iter().zip(preimg_ptrs.iter()) { bindings.insert_ptr(var.clone(), *ptr); } - preimages + advices .hash4 - .push(Some(PreimageData::PtrVec(preimg_ptrs.to_vec()))); + .push(Some(SlotData::PtrVec(preimg_ptrs.to_vec()))); } Op::Decons3(preimg, img) => { let img_ptr = bindings.get_ptr(img)?; @@ -386,9 +378,9 @@ impl Block { for (var, ptr) in preimg.iter().zip(preimg_ptrs.iter()) { bindings.insert_ptr(var.clone(), *ptr); } - preimages + advices .hash6 - .push(Some(PreimageData::PtrVec(preimg_ptrs.to_vec()))); + .push(Some(SlotData::PtrVec(preimg_ptrs.to_vec()))); } Op::Decons4(preimg, img) => { let img_ptr = bindings.get_ptr(img)?; @@ -402,9 +394,9 @@ impl Block { for (var, ptr) in preimg.iter().zip(preimg_ptrs.iter()) { bindings.insert_ptr(var.clone(), *ptr); } - preimages + advices .hash8 - .push(Some(PreimageData::PtrVec(preimg_ptrs.to_vec()))); + .push(Some(SlotData::PtrVec(preimg_ptrs.to_vec()))); } Op::Hide(tgt, sec, src) => { let src_ptr = bindings.get_ptr(src)?; @@ -412,9 +404,9 @@ impl Block { bail!("{sec} is not a numeric pointer") }; let tgt_ptr = store.hide(secret, src_ptr)?; - preimages + advices .commitment - .push(Some(PreimageData::FPtr(secret, src_ptr))); + .push(Some(SlotData::FPtr(secret, src_ptr))); bindings.insert_ptr(tgt.clone(), tgt_ptr); } Op::Open(tgt_secret, tgt_ptr, comm) => { @@ -426,9 +418,7 @@ impl Block { }; bindings.insert_ptr(tgt_ptr.clone(), *ptr); bindings.insert_ptr(tgt_secret.clone(), Ptr::Atom(Tag::Expr(Num), *secret)); - preimages - .commitment - .push(Some(PreimageData::FPtr(*secret, *ptr))) + advices.commitment.push(Some(SlotData::FPtr(*secret, *ptr))) } } } @@ -438,13 +428,13 @@ impl Block { let tag = ptr.tag(); if let Some(block) = cases.get(tag) { path.push_tag_inplace(*tag); - block.run(input, store, bindings, preimages, path, emitted, lang, pc) + block.run(input, store, bindings, advices, path, emitted, lang, pc) } else { path.push_default_inplace(); let Some(def) = def else { bail!("No match for tag {}", tag) }; - def.run(input, store, bindings, preimages, path, emitted, lang, pc) + def.run(input, store, bindings, advices, path, emitted, lang, pc) } } Ctrl::MatchSymbol(match_var, cases, def) => { @@ -457,22 +447,22 @@ impl Block { }; if let Some(block) = cases.get(&sym) { path.push_symbol_inplace(sym); - block.run(input, store, bindings, preimages, path, emitted, lang, pc) + block.run(input, store, bindings, advices, path, emitted, lang, pc) } else { path.push_default_inplace(); let Some(def) = def else { bail!("No match for symbol {sym}") }; - def.run(input, store, bindings, preimages, path, emitted, lang, pc) + def.run(input, store, bindings, advices, path, emitted, lang, pc) } } Ctrl::If(b, true_block, false_block) => { let b = bindings.get_bool(b)?; path.push_bool_inplace(b); if b { - true_block.run(input, store, bindings, preimages, path, emitted, lang, pc) + true_block.run(input, store, bindings, advices, path, emitted, lang, pc) } else { - false_block.run(input, store, bindings, preimages, path, emitted, lang, pc) + false_block.run(input, store, bindings, advices, path, emitted, lang, pc) } } Ctrl::Return(output_vars) => { @@ -486,7 +476,7 @@ impl Block { input, output, emitted: emitted.clone(), - preimages, + advices, blank: false, pc, }, @@ -502,7 +492,7 @@ impl Func { &self, args: &[Ptr], store: &Store, - preimages: Preimages, + advices: Advices, emitted: &mut Vec>, lang: &Lang, pc: usize, @@ -513,45 +503,45 @@ impl Func { } // We must fill any unused slots with `None` values so we save - // the initial size of preimages, which might not be zero - let hash4_init = preimages.hash4.len(); - let hash6_init = preimages.hash6.len(); - let hash8_init = preimages.hash8.len(); - let commitment_init = preimages.commitment.len(); - let bit_decomp_init = preimages.bit_decomp.len(); + // the initial size of advices, which might not be zero + let hash4_init = advices.hash4.len(); + let hash6_init = advices.hash6.len(); + let hash8_init = advices.hash8.len(); + let commitment_init = advices.commitment.len(); + let bit_decomp_init = advices.bit_decomp.len(); let mut res = self.body.run( args, store, bindings, - preimages, + advices, Path::default(), emitted, lang, pc, )?; - let preimages = &mut res.0.preimages; + let advices = &mut res.0.advices; - let hash4_used = preimages.hash4.len() - hash4_init; - let hash6_used = preimages.hash6.len() - hash6_init; - let hash8_used = preimages.hash8.len() - hash8_init; - let commitment_used = preimages.commitment.len() - commitment_init; - let bit_decomp_used = preimages.bit_decomp.len() - bit_decomp_init; + let hash4_used = advices.hash4.len() - hash4_init; + let hash6_used = advices.hash6.len() - hash6_init; + let hash8_used = advices.hash8.len() - hash8_init; + let commitment_used = advices.commitment.len() - commitment_init; + let bit_decomp_used = advices.bit_decomp.len() - bit_decomp_init; for _ in hash4_used..self.slot.hash4 { - preimages.hash4.push(None); + advices.hash4.push(None); } for _ in hash6_used..self.slot.hash6 { - preimages.hash6.push(None); + advices.hash6.push(None); } for _ in hash8_used..self.slot.hash8 { - preimages.hash8.push(None); + advices.hash8.push(None); } for _ in commitment_used..self.slot.commitment { - preimages.commitment.push(None); + advices.commitment.push(None); } for _ in bit_decomp_used..self.slot.bit_decomp { - preimages.bit_decomp.push(None); + advices.bit_decomp.push(None); } Ok(res) @@ -569,7 +559,7 @@ impl Func { .call( args, store, - Preimages::new_from_func(self), + Advices::new_from_func(self), &mut vec![], lang, pc, diff --git a/src/lem/slot.rs b/src/lem/slot.rs index 345ba99201..c50a50f252 100644 --- a/src/lem/slot.rs +++ b/src/lem/slot.rs @@ -230,7 +230,7 @@ impl Block { } #[derive(Clone, Debug)] -pub enum PreimageData { +pub enum SlotData { PtrVec(Vec>), FPtr(F, Ptr), F(F), @@ -256,14 +256,14 @@ impl SlotType { } } - pub(crate) fn is_compatible(&self, preimg: &PreimageData) -> bool { + pub(crate) fn is_compatible(&self, slot_data: &SlotData) -> bool { matches!( - (self, preimg), - (Self::Hash4, PreimageData::PtrVec(..)) - | (Self::Hash6, PreimageData::PtrVec(..)) - | (Self::Hash8, PreimageData::PtrVec(..)) - | (Self::Commitment, PreimageData::FPtr(..)) - | (Self::BitDecomp, PreimageData::F(..)) + (self, slot_data), + (Self::Hash4, SlotData::PtrVec(..)) + | (Self::Hash6, SlotData::PtrVec(..)) + | (Self::Hash8, SlotData::PtrVec(..)) + | (Self::Commitment, SlotData::FPtr(..)) + | (Self::BitDecomp, SlotData::F(..)) ) } }