From e348071aafa7cf1d2a64cf185bdd8446d875e692 Mon Sep 17 00:00:00 2001 From: Paul Cheng Date: Fri, 10 Nov 2023 22:43:34 +0800 Subject: [PATCH] refact: remove redunction code in generate witness --- algebraic/Cargo.toml | 2 +- algebraic/src/witness/circom.rs | 107 ++--- algebraic/src/witness/memory.rs | 429 ++++++++++---------- algebraic/src/witness/mod.rs | 4 +- algebraic/src/witness/witness_calculator.rs | 17 +- 5 files changed, 283 insertions(+), 276 deletions(-) diff --git a/algebraic/Cargo.toml b/algebraic/Cargo.toml index 5bbdbf72..3ecc1c1b 100644 --- a/algebraic/Cargo.toml +++ b/algebraic/Cargo.toml @@ -22,7 +22,7 @@ num-traits = "0.2.8" serde = { version = "1.0", features = [ "derive" ] } serde_json = { version = "1.0", features = [ "arbitrary_precision" ] } hex = "*" -wasmer = { version = "3.1.1", default-features = false } +wasmer = { version = "3.3.0", default-features = false } thiserror="1.0" fnv = { version = "1.0.3", default-features = false } num = { version = "0.4.0" } diff --git a/algebraic/src/witness/circom.rs b/algebraic/src/witness/circom.rs index 976c2077..efb8e272 100644 --- a/algebraic/src/witness/circom.rs +++ b/algebraic/src/witness/circom.rs @@ -5,93 +5,94 @@ use wasmer::{Function, Instance, Value}; #[derive(Clone, Debug)] pub struct Wasm(Instance); -pub trait CircomBase { - fn init(&self, sanity_check: bool) -> Result<()>; - fn func(&self, name: &str) -> &Function; - fn get_ptr_witness_buffer(&self) -> Result; - fn get_ptr_witness(&self, w: u32) -> Result; - fn get_signal_offset32( - &self, - p_sig_offset: u32, - component: u32, - hash_msb: u32, - hash_lsb: u32, - ) -> Result<()>; - fn set_signal(&self, c_idx: u32, component: u32, signal: u32, p_val: u32) -> Result<()>; - fn get_u32(&self, name: &str) -> Result; - // Only exists natively in Circom2, hardcoded for Circom - fn get_version(&self) -> Result; -} - -pub trait Circom { - fn get_field_num_len32(&self) -> Result; - fn get_raw_prime(&self) -> Result<()>; - fn read_shared_rw_memory(&self, i: u32) -> Result; - fn write_shared_rw_memory(&self, i: u32, v: u32) -> Result<()>; - fn set_input_signal(&self, hmsb: u32, hlsb: u32, pos: u32) -> Result<()>; - fn get_witness(&self, i: u32) -> Result<()>; - fn get_witness_size(&self) -> Result; -} - -impl Circom for Wasm { - fn get_field_num_len32(&self) -> Result { +// pub trait CircomBase { +// fn init(&self, sanity_check: bool) -> Result<()>; +// fn func(&self, name: &str) -> &Function; +// fn get_ptr_witness_buffer(&self) -> Result; +// fn get_ptr_witness(&self, w: u32) -> Result; +// fn get_signal_offset32( +// &self, +// p_sig_offset: u32, +// component: u32, +// hash_msb: u32, +// hash_lsb: u32, +// ) -> Result<()>; +// fn set_signal(&self, c_idx: u32, component: u32, signal: u32, p_val: u32) -> Result<()>; +// fn get_u32(&self, name: &str) -> Result; +// // Only exists natively in Circom2, hardcoded for Circom +// fn get_version(&self) -> Result; +// } +// +// pub trait Circom { +// fn get_field_num_len32(&self) -> Result; +// fn get_raw_prime(&self) -> Result<()>; +// fn read_shared_rw_memory(&self, i: u32) -> Result; +// fn write_shared_rw_memory(&self, i: u32, v: u32) -> Result<()>; +// fn set_input_signal(&self, hmsb: u32, hlsb: u32, pos: u32) -> Result<()>; +// fn get_witness(&self, i: u32) -> Result<()>; +// fn get_witness_size(&self) -> Result; +// } + +// impl Circom for Wasm { +impl Wasm { + pub(crate) fn get_field_num_len32(&self) -> Result { self.get_u32("getFieldNumLen32") } - fn get_raw_prime(&self) -> Result<()> { + pub(crate) fn get_raw_prime(&self) -> Result<()> { let func = self.func("getRawPrime"); func.call(&[])?; Ok(()) } - fn read_shared_rw_memory(&self, i: u32) -> Result { + pub(crate) fn read_shared_rw_memory(&self, i: u32) -> Result { let func = self.func("readSharedRWMemory"); let result = func.call(&[i.into()])?; Ok(result[0].unwrap_i32() as u32) } - fn write_shared_rw_memory(&self, i: u32, v: u32) -> Result<()> { + pub(crate) fn write_shared_rw_memory(&self, i: u32, v: u32) -> Result<()> { let func = self.func("writeSharedRWMemory"); func.call(&[i.into(), v.into()])?; Ok(()) } - fn set_input_signal(&self, hmsb: u32, hlsb: u32, pos: u32) -> Result<()> { + pub(crate) fn set_input_signal(&self, hmsb: u32, hlsb: u32, pos: u32) -> Result<()> { let func = self.func("setInputSignal"); func.call(&[hmsb.into(), hlsb.into(), pos.into()])?; Ok(()) } - fn get_witness(&self, i: u32) -> Result<()> { + pub(crate) fn get_witness(&self, i: u32) -> Result<()> { let func = self.func("getWitness"); func.call(&[i.into()])?; Ok(()) } - fn get_witness_size(&self) -> Result { + pub(crate) fn get_witness_size(&self) -> Result { self.get_u32("getWitnessSize") } -} - -impl CircomBase for Wasm { - fn init(&self, sanity_check: bool) -> Result<()> { + // } + // + // impl CircomBase for Wasm { + pub(crate) fn init(&self, sanity_check: bool) -> Result<()> { let func = self.func("init"); func.call(&[Value::I32(sanity_check as i32)])?; Ok(()) } - fn get_ptr_witness_buffer(&self) -> Result { + pub(crate) fn get_ptr_witness_buffer(&self) -> Result { self.get_u32("getWitnessBuffer") } - fn get_ptr_witness(&self, w: u32) -> Result { + pub(crate) fn get_ptr_witness(&self, w: u32) -> Result { let func = self.func("getPWitness"); let res = func.call(&[w.into()])?; Ok(res[0].unwrap_i32() as u32) } - fn get_signal_offset32( + pub(crate) fn get_signal_offset32( &self, p_sig_offset: u32, component: u32, @@ -109,7 +110,13 @@ impl CircomBase for Wasm { Ok(()) } - fn set_signal(&self, c_idx: u32, component: u32, signal: u32, p_val: u32) -> Result<()> { + pub(crate) fn set_signal( + &self, + c_idx: u32, + component: u32, + signal: u32, + p_val: u32, + ) -> Result<()> { let func = self.func("setSignal"); func.call(&[c_idx.into(), component.into(), signal.into(), p_val.into()])?; @@ -117,28 +124,28 @@ impl CircomBase for Wasm { } // Default to version 1 if it isn't explicitly defined - fn get_version(&self) -> Result { + pub(crate) fn get_version(&self) -> Result { match self.0.exports.get_function("getVersion") { Ok(func) => Ok(func.call(&[])?[0].unwrap_i32() as u32), Err(_) => Ok(1), } } - fn get_u32(&self, name: &str) -> Result { + pub(crate) fn get_u32(&self, name: &str) -> Result { let func = self.func(name); let result = func.call(&[])?; Ok(result[0].unwrap_i32() as u32) } - fn func(&self, name: &str) -> &Function { + pub(crate) fn func(&self, name: &str) -> &Function { self.0 .exports .get_function(name) .unwrap_or_else(|_| panic!("function {} not found", name)) } -} - -impl Wasm { + // } + // + // impl Wasm { pub fn new(instance: Instance) -> Self { Self(instance) } diff --git a/algebraic/src/witness/memory.rs b/algebraic/src/witness/memory.rs index 532546b3..b8f32b92 100644 --- a/algebraic/src/witness/memory.rs +++ b/algebraic/src/witness/memory.rs @@ -21,13 +21,13 @@ pub struct SafeMemory { n32: usize, } -impl Deref for SafeMemory { - type Target = Memory; - - fn deref(&self) -> &Self::Target { - &self.memory - } -} +// impl Deref for SafeMemory { +// type Target = Memory; +// +// fn deref(&self) -> &Self::Target { +// &self.memory +// } +// } // TODO: remove dead code impl SafeMemory { @@ -50,7 +50,6 @@ impl SafeMemory { Self { memory, prime, - short_max, short_min, r_inv, @@ -58,212 +57,212 @@ impl SafeMemory { } } - /// Gets an immutable view to the memory in 32 byte chunks - pub fn view(&self) -> MemoryView { - self.memory.view() - } - - /// Returns the next free position in the memory - pub fn free_pos(&self) -> u32 { - self.view()[0].get() - } - - /// Sets the next free position in the memory - pub fn set_free_pos(&mut self, ptr: u32) { - self.write_u32(0, ptr); - } - - /// Allocates a U32 in memory - pub fn alloc_u32(&mut self) -> u32 { - let p = self.free_pos(); - self.set_free_pos(p + 8); - p - } - - /// Writes a u32 to the specified memory offset - pub fn write_u32(&mut self, ptr: usize, num: u32) { - let buf = unsafe { self.memory.data_unchecked_mut() }; - buf[ptr..ptr + std::mem::size_of::()].copy_from_slice(&num.to_le_bytes()); - } - - /// Reads a u32 from the specified memory offset - pub fn read_u32(&self, ptr: usize) -> u32 { - let buf = unsafe { self.memory.data_unchecked() }; - - let mut bytes = [0; 4]; - bytes.copy_from_slice(&buf[ptr..ptr + std::mem::size_of::()]); - - u32::from_le_bytes(bytes) - } - - /// Allocates `self.n32 * 4 + 8` bytes in the memory - pub fn alloc_fr(&mut self) -> u32 { - let p = self.free_pos(); - self.set_free_pos(p + self.n32 as u32 * 4 + 8); - p - } - - /// Writes a Field Element to memory at the specified offset, truncating - /// to smaller u32 types if needed and adjusting the sign via 2s complement - pub fn write_fr(&mut self, ptr: usize, fr: &BigInt) -> Result<()> { - if fr < &self.short_max && fr > &self.short_min { - if fr >= &BigInt::zero() { - self.write_short_positive(ptr, fr)?; - } else { - self.write_short_negative(ptr, fr)?; - } - } else { - self.write_long_normal::(ptr, fr)?; - } - - Ok(()) - } - - /// Reads a Field Element from the memory at the specified offset - pub fn read_fr(&self, ptr: usize) -> Result { - let view = self.memory.view::(); - - let res = if view[ptr + 4 + 3].get() & 0x80 != 0 { - let mut num = self.read_big::(ptr + 8, self.n32)?; - if view[ptr + 4 + 3].get() & 0x40 != 0 { - num = (num * &self.r_inv) % &self.prime - } - num - } else if view[ptr + 3].get() & 0x40 != 0 { - let mut num = self.read_u32(ptr).into(); - // handle small negative - num -= BigInt::from(0x100000000i64); - num - } else { - self.read_u32(ptr).into() - }; - - Ok(res) - } - - fn write_short_positive(&mut self, ptr: usize, fr: &BigInt) -> Result<()> { - let num = fr.to_i32().expect("not a short positive"); - self.write_u32(ptr, num as u32); - self.write_u32(ptr + 4, 0); - Ok(()) - } - - fn write_short_negative(&mut self, ptr: usize, fr: &BigInt) -> Result<()> { - // 2s complement - let num = fr - &self.short_min; - let num = num - &self.short_max; - let num = num + BigInt::from(0x0001_0000_0000i64); - - let num = num - .to_u32() - .expect("could not cast as u32 (should never happen)"); - - self.write_u32(ptr, num); - self.write_u32(ptr + 4, 0); - Ok(()) - } - - fn write_long_normal(&mut self, ptr: usize, fr: &BigInt) -> Result<()> { - self.write_u32(ptr, 0); - self.write_u32(ptr + 4, i32::MIN as u32); // 0x80000000 - self.write_big::(ptr + 8, fr)?; - Ok(()) - } - - fn write_big(&self, ptr: usize, num: &BigInt) -> Result<()> { - let buf = unsafe { self.memory.data_unchecked_mut() }; - let num = E::Fr::from_str(&num.to_string()).unwrap(); - - let repr = num.into_repr(); - let required_length = repr.as_ref().len() * 8; - let mut bytes: Vec = Vec::with_capacity(required_length); - repr.write_le(&mut bytes).unwrap(); - let len = bytes.len(); - buf[ptr..ptr + len].copy_from_slice(&bytes); - - Ok(()) - } - - /// Reads `num_bytes * 32` from the specified memory offset in a Big Integer - pub fn read_big(&self, ptr: usize, num_bytes: usize) -> Result { - let buf = unsafe { self.memory.data_unchecked() }; - let mut buf = &buf[ptr..ptr + num_bytes * 32]; - - let mut repr = E::Fr::zero().into_repr(); - repr.read_le(&mut buf)?; - let fr = E::Fr::from_repr(repr)?; - - let big = BigUint::from_str_radix(&to_hex(&fr), 16)?; - Ok(big.into()) - } + // /// Gets an immutable view to the memory in 32 byte chunks + // pub fn view(&self) -> MemoryView { + // self.memory.view() + // } + // + // /// Returns the next free position in the memory + // pub fn free_pos(&self) -> u32 { + // self.view()[0].get() + // } + // + // /// Sets the next free position in the memory + // pub fn set_free_pos(&mut self, ptr: u32) { + // self.write_u32(0, ptr); + // } + // + // /// Allocates a U32 in memory + // pub fn alloc_u32(&mut self) -> u32 { + // let p = self.free_pos(); + // self.set_free_pos(p + 8); + // p + // } + // + // /// Writes a u32 to the specified memory offset + // pub fn write_u32(&mut self, ptr: usize, num: u32) { + // let buf = unsafe { self.memory.data_unchecked_mut() }; + // buf[ptr..ptr + std::mem::size_of::()].copy_from_slice(&num.to_le_bytes()); + // } + // + // /// Reads a u32 from the specified memory offset + // pub fn read_u32(&self, ptr: usize) -> u32 { + // let buf = unsafe { self.memory.data_unchecked() }; + // + // let mut bytes = [0; 4]; + // bytes.copy_from_slice(&buf[ptr..ptr + std::mem::size_of::()]); + // + // u32::from_le_bytes(bytes) + // } + // + // /// Allocates `self.n32 * 4 + 8` bytes in the memory + // pub fn alloc_fr(&mut self) -> u32 { + // let p = self.free_pos(); + // self.set_free_pos(p + self.n32 as u32 * 4 + 8); + // p + // } + // + // /// Writes a Field Element to memory at the specified offset, truncating + // /// to smaller u32 types if needed and adjusting the sign via 2s complement + // pub fn write_fr(&mut self, ptr: usize, fr: &BigInt) -> Result<()> { + // if fr < &self.short_max && fr > &self.short_min { + // if fr >= &BigInt::zero() { + // self.write_short_positive(ptr, fr)?; + // } else { + // self.write_short_negative(ptr, fr)?; + // } + // } else { + // self.write_long_normal::(ptr, fr)?; + // } + // + // Ok(()) + // } + // + // /// Reads a Field Element from the memory at the specified offset + // pub fn read_fr(&self, ptr: usize) -> Result { + // let view = self.memory.view::(); + // + // let res = if view[ptr + 4 + 3].get() & 0x80 != 0 { + // let mut num = self.read_big::(ptr + 8, self.n32)?; + // if view[ptr + 4 + 3].get() & 0x40 != 0 { + // num = (num * &self.r_inv) % &self.prime + // } + // num + // } else if view[ptr + 3].get() & 0x40 != 0 { + // let mut num = self.read_u32(ptr).into(); + // // handle small negative + // num -= BigInt::from(0x100000000i64); + // num + // } else { + // self.read_u32(ptr).into() + // }; + // + // Ok(res) + // } + // + // fn write_short_positive(&mut self, ptr: usize, fr: &BigInt) -> Result<()> { + // let num = fr.to_i32().expect("not a short positive"); + // self.write_u32(ptr, num as u32); + // self.write_u32(ptr + 4, 0); + // Ok(()) + // } + // + // fn write_short_negative(&mut self, ptr: usize, fr: &BigInt) -> Result<()> { + // // 2s complement + // let num = fr - &self.short_min; + // let num = num - &self.short_max; + // let num = num + BigInt::from(0x0001_0000_0000i64); + // + // let num = num + // .to_u32() + // .expect("could not cast as u32 (should never happen)"); + // + // self.write_u32(ptr, num); + // self.write_u32(ptr + 4, 0); + // Ok(()) + // } + // + // fn write_long_normal(&mut self, ptr: usize, fr: &BigInt) -> Result<()> { + // self.write_u32(ptr, 0); + // self.write_u32(ptr + 4, i32::MIN as u32); // 0x80000000 + // self.write_big::(ptr + 8, fr)?; + // Ok(()) + // } + // + // fn write_big(&self, ptr: usize, num: &BigInt) -> Result<()> { + // let buf = unsafe { self.memory.data_unchecked_mut() }; + // let num = E::Fr::from_str(&num.to_string()).unwrap(); + // + // let repr = num.into_repr(); + // let required_length = repr.as_ref().len() * 8; + // let mut bytes: Vec = Vec::with_capacity(required_length); + // repr.write_le(&mut bytes).unwrap(); + // let len = bytes.len(); + // buf[ptr..ptr + len].copy_from_slice(&bytes); + // + // Ok(()) + // } + // + // /// Reads `num_bytes * 32` from the specified memory offset in a Big Integer + // pub fn read_big(&self, ptr: usize, num_bytes: usize) -> Result { + // let buf = unsafe { self.memory.data_unchecked() }; + // let mut buf = &buf[ptr..ptr + num_bytes * 32]; + // + // let mut repr = E::Fr::zero().into_repr(); + // repr.read_le(&mut buf)?; + // let fr = E::Fr::from_repr(repr)?; + // + // let big = BigUint::from_str_radix(&to_hex(&fr), 16)?; + // Ok(big.into()) + // } } -#[cfg(test)] -mod tests { - use super::*; - use num_traits::ToPrimitive; - use std::str::FromStr; - use wasmer::{MemoryType, Store}; - - fn new() -> SafeMemory { - SafeMemory::new( - Memory::new(&Store::default(), MemoryType::new(1, None, false)).unwrap(), - 2, - BigInt::from_str( - "21888242871839275222246405745257275088548364400416034343698204186575808495617", - ) - .unwrap(), - ) - } - - #[test] - fn i32_bounds() { - let mem = new(); - let i32_max = i32::MAX as i64 + 1; - assert_eq!(mem.short_min.to_i64().unwrap(), -i32_max); - assert_eq!(mem.short_max.to_i64().unwrap(), i32_max); - } - - #[test] - fn read_write_32() { - let mut mem = new(); - let num = u32::MAX; - - let inp = mem.read_u32(0); - assert_eq!(inp, 0); - - mem.write_u32(0, num); - let inp = mem.read_u32(0); - assert_eq!(inp, num); - } - - #[test] - fn read_write_fr_small_positive() { - read_write_fr(BigInt::from(1_000_000)); - } - - #[test] - fn read_write_fr_small_negative() { - read_write_fr(BigInt::from(-1_000_000)); - } - - #[test] - fn read_write_fr_big_positive() { - read_write_fr(BigInt::from(500000000000i64)); - } - - // TODO: How should this be handled? - #[test] - #[ignore] - fn read_write_fr_big_negative() { - read_write_fr(BigInt::from_str("-500000000000").unwrap()) - } - - fn read_write_fr(num: BigInt) { - use crate::bellman_ce::pairing::bn256::Bn256; - let mut mem = new(); - mem.write_fr::(0, &num).unwrap(); - let res = mem.read_fr::(0).unwrap(); - assert_eq!(res, num); - } -} +// #[cfg(test)] +// mod tests { +// use super::*; +// use num_traits::ToPrimitive; +// use std::str::FromStr; +// use wasmer::{MemoryType, Store}; +// +// fn new() -> SafeMemory { +// SafeMemory::new( +// Memory::new(&Store::default(), MemoryType::new(1, None, false)).unwrap(), +// 2, +// BigInt::from_str( +// "21888242871839275222246405745257275088548364400416034343698204186575808495617", +// ) +// .unwrap(), +// ) +// } +// +// #[test] +// fn i32_bounds() { +// let mem = new(); +// let i32_max = i32::MAX as i64 + 1; +// assert_eq!(mem.short_min.to_i64().unwrap(), -i32_max); +// assert_eq!(mem.short_max.to_i64().unwrap(), i32_max); +// } +// +// #[test] +// fn read_write_32() { +// let mut mem = new(); +// let num = u32::MAX; +// +// let inp = mem.read_u32(0); +// assert_eq!(inp, 0); +// +// mem.write_u32(0, num); +// let inp = mem.read_u32(0); +// assert_eq!(inp, num); +// } +// +// #[test] +// fn read_write_fr_small_positive() { +// read_write_fr(BigInt::from(1_000_000)); +// } +// +// #[test] +// fn read_write_fr_small_negative() { +// read_write_fr(BigInt::from(-1_000_000)); +// } +// +// #[test] +// fn read_write_fr_big_positive() { +// read_write_fr(BigInt::from(500000000000i64)); +// } +// +// // TODO: How should this be handled? +// #[test] +// #[ignore] +// fn read_write_fr_big_negative() { +// read_write_fr(BigInt::from_str("-500000000000").unwrap()) +// } +// +// fn read_write_fr(num: BigInt) { +// use crate::bellman_ce::pairing::bn256::Bn256; +// let mut mem = new(); +// mem.write_fr::(0, &num).unwrap(); +// let res = mem.read_fr::(0).unwrap(); +// assert_eq!(res, num); +// } +// } diff --git a/algebraic/src/witness/mod.rs b/algebraic/src/witness/mod.rs index d281e180..6670fcd1 100644 --- a/algebraic/src/witness/mod.rs +++ b/algebraic/src/witness/mod.rs @@ -8,8 +8,8 @@ use std::str::FromStr; pub use witness_calculator::flat_array; pub use witness_calculator::WitnessCalculator; -mod memory; -pub(super) use memory::SafeMemory; +pub(crate) mod memory; +// pub(super) use memory::SafeMemory; mod circom; pub(super) use circom::{CircomBase, Wasm}; diff --git a/algebraic/src/witness/witness_calculator.rs b/algebraic/src/witness/witness_calculator.rs index 9ac1824b..dc599437 100644 --- a/algebraic/src/witness/witness_calculator.rs +++ b/algebraic/src/witness/witness_calculator.rs @@ -1,8 +1,9 @@ // copied and modified by https://github.com/arkworks-rs/circom-compat/blob/master/src/witness/witness_calculator.rs use super::Circom; -use super::{fnv, CircomBase, SafeMemory, Wasm}; +use super::{fnv, CircomBase, Wasm}; use crate::bellman_ce::{PrimeField, ScalarEngine}; use crate::errors::{EigenError, Result}; +use crate::witness::memory::SafeMemory; use num::ToPrimitive; use num_bigint::BigInt; use num_bigint::BigUint; @@ -14,7 +15,6 @@ use wasmer::{imports, Function, Instance, Memory, MemoryType, Module, Store}; #[cfg(not(feature = "wasm"))] use std::fs::OpenOptions; - #[cfg(not(feature = "wasm"))] use std::io::{BufWriter, Write}; @@ -63,10 +63,11 @@ impl WitnessCalculator { } pub fn from_module(module: Module) -> Result { - let store = module.store(); + // let store = module.store(); + let mut store = Store::default(); // Set up the memory - let memory = Memory::new(store, MemoryType::new(2000, None, false)).unwrap(); + let memory = Memory::new(&mut store, MemoryType::new(2000, None, false)).unwrap(); let import_object = imports! { "env" => { "memory" => memory.clone(), @@ -87,10 +88,10 @@ impl WitnessCalculator { }; let instance = Wasm::new(Instance::new(&module, &import_object)?); - let version = instance.get_version().unwrap_or(1); - // Circom 2 feature flag with version 2 - fn new_circom(instance: Wasm, memory: Memory, version: u32) -> Result { + fn new_circom(instance: Wasm, memory: Memory) -> Result { + let version = instance.get_version().unwrap_or(1); + let n32 = instance.get_field_num_len32()?; let mut safe_memory = SafeMemory::new(memory, n32 as usize, BigInt::zero()); instance.get_raw_prime()?; @@ -112,7 +113,7 @@ impl WitnessCalculator { }) } - new_circom(instance, memory, version) + new_circom(instance, memory) } pub fn calculate_witness)>>(