diff --git a/crates/cli/src/command.rs b/crates/cli/src/command.rs index cd2446c59..09d56c15e 100644 --- a/crates/cli/src/command.rs +++ b/crates/cli/src/command.rs @@ -91,11 +91,18 @@ impl SetupArg { Slice::from_compilation_table(compilation_tables, is_last_slice), )?; - SetupArg::_setup_circuit_data( - params, - &setup_circuit, - params_dir.join(name_of_circuit_data(name, is_last_slice)), - ) + match setup_circuit { + ZkWasmCircuit::Ongoing(circuit) => SetupArg::_setup_circuit_data( + params, + &circuit, + params_dir.join(name_of_circuit_data(name, is_last_slice)), + ), + ZkWasmCircuit::LastSliceCircuit(circuit) => SetupArg::_setup_circuit_data( + params, + &circuit, + params_dir.join(name_of_circuit_data(name, is_last_slice)), + ), + } }; #[cfg(feature = "continuation")] diff --git a/crates/cli/src/config.rs b/crates/cli/src/config.rs index 838729c99..06facb22d 100644 --- a/crates/cli/src/config.rs +++ b/crates/cli/src/config.rs @@ -12,6 +12,7 @@ use circuits_batcher::proof::ProofInfo; use circuits_batcher::proof::ProofPieceInfo; use circuits_batcher::proof::Prover; use console::style; +use delphinus_zkwasm::circuits::ZkWasmCircuit; use delphinus_zkwasm::loader::slice::Slices; use delphinus_zkwasm::loader::Module; use delphinus_zkwasm::loader::ZkWasmLoader; @@ -31,6 +32,7 @@ use specs::TraceBackend; use crate::args::HostMode; use crate::names::name_of_circuit_data; use crate::names::name_of_etable_slice; +use crate::names::name_of_frame_table_slice; use crate::names::name_of_instance; use crate::names::name_of_loadinfo; use crate::names::name_of_params; @@ -241,7 +243,7 @@ impl Config { arg: ExecutionArg, context_output_filename: Option, mock_test: bool, - backend: TraceBackend, + table_backend: TraceBackend, ) -> anyhow::Result<()> { let mut cached_proving_key = None; @@ -253,7 +255,7 @@ impl Config { let env = env_builder.create_env(self.k, arg); - let mut monitor = TableMonitor::new(self.k, &self.phantom_functions, backend, &env); + let mut monitor = TableMonitor::new(self.k, &self.phantom_functions, table_backend, &env); let (result, tables) = { println!("{} Executing...", style("[3/8]").bold().dim(),); @@ -297,7 +299,11 @@ impl Config { style("[5/8]").bold().dim(), dir ); - tables.write(&dir, |slice| name_of_etable_slice(&self.name, slice)); + tables.write( + &dir, + |slice| name_of_etable_slice(&self.name, slice), + |slice| name_of_frame_table_slice(&self.name, slice), + ); } println!("{} Build circuit(s)...", style("[6/8]").bold().dim(),); @@ -381,14 +387,25 @@ impl Config { transcript: name_of_transcript(&self.name, index), }; - let proof = proof_piece_info.create_proof::( - &circuit, - &vec![instances.clone()], - ¶ms, - &cached_proving_key.as_ref().unwrap().1, - proof_load_info.hashtype, - OpenSchema::Shplonk, - ); + let proof = match circuit { + ZkWasmCircuit::Ongoing(circuit) => proof_piece_info.create_proof::( + &circuit, + &vec![instances.clone()], + ¶ms, + &cached_proving_key.as_ref().unwrap().1, + proof_load_info.hashtype, + OpenSchema::Shplonk, + ), + ZkWasmCircuit::LastSliceCircuit(circuit) => proof_piece_info + .create_proof::( + &circuit, + &vec![instances.clone()], + ¶ms, + &cached_proving_key.as_ref().unwrap().1, + proof_load_info.hashtype, + OpenSchema::Shplonk, + ), + }; proof_piece_info.save_proof_data(&vec![instances.clone()], &proof, &output_dir); diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index d914e963e..0177e161c 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -15,7 +15,10 @@ use config::Config; use delphinus_zkwasm::runtime::host::HostEnvBuilder; use names::name_of_config; use names::name_of_etable_slice; +use names::name_of_frame_table_slice; use specs::args::parse_args; +use specs::etable::EventTable; +use specs::jtable::FrameTable; use specs::TraceBackend; mod app_builder; @@ -94,17 +97,41 @@ fn main() -> Result<()> { let private_inputs = parse_args(&arg.running_arg.private_inputs); let context_inputs = parse_args(&arg.running_arg.context_inputs); - let file_backend = arg.file_backend; - let backend = if file_backend { - TraceBackend::File(Box::new(move |slice, etable| { - let filename_of_etable_slice = - PathBuf::from(name_of_etable_slice(&cli.name, slice)); - let path = trace_dir.join(&filename_of_etable_slice); + let trace_backend: TraceBackend = if arg.file_backend { + let event_table_writer = { + let name = cli.name.clone(); + let trace_dir = trace_dir.clone(); - etable.write(&path).unwrap(); + Box::new(move |slice, etable: &EventTable| { + let filename_of_etable_slice = + PathBuf::from(name_of_etable_slice(&name, slice)); + let path = trace_dir.join(&filename_of_etable_slice); - path - })) + etable.write(&path).unwrap(); + + path + }) + }; + + let frame_table_writer = { + let name = cli.name.clone(); + let trace_dir = trace_dir.clone(); + + Box::new(move |slice, frame_table: &FrameTable| { + let filename_of_frame_table_slice = + PathBuf::from(name_of_frame_table_slice(&name, slice)); + let path = trace_dir.join(&filename_of_frame_table_slice); + + frame_table.write(&path).unwrap(); + + path + }) + }; + + TraceBackend::File { + event_table_writer, + frame_table_writer, + } } else { TraceBackend::Memory }; @@ -126,7 +153,7 @@ fn main() -> Result<()> { }, arg.running_arg.context_output, arg.mock_test, - backend, + trace_backend, )?; } Subcommands::Verify(arg) => { diff --git a/crates/cli/src/names.rs b/crates/cli/src/names.rs index fe3cee29b..e7405cf1f 100644 --- a/crates/cli/src/names.rs +++ b/crates/cli/src/names.rs @@ -42,3 +42,8 @@ pub(crate) fn name_of_transcript(name: &str, index: usize) -> String { pub(crate) fn name_of_etable_slice(name: &str, index: usize) -> String { format!("{}.etable.{}.data", name, index) } + +#[inline(always)] +pub(crate) fn name_of_frame_table_slice(name: &str, index: usize) -> String { + format!("{}.frame_table.{}.data", name, index) +} diff --git a/crates/specs/src/encode/frame_table.rs b/crates/specs/src/encode/frame_table.rs index 1f2def0b8..36153b6e9 100644 --- a/crates/specs/src/encode/frame_table.rs +++ b/crates/specs/src/encode/frame_table.rs @@ -2,8 +2,9 @@ use num_bigint::BigUint; use num_bigint::ToBigUint; use crate::encode::COMMON_RANGE_OFFSET; -use crate::jtable::JumpTableEntry; -use crate::jtable::StaticFrameEntry; +use crate::jtable::CalledFrameTableEntry; +use crate::jtable::FrameTableEntryInternal; +use crate::jtable::InheritedFrameTableEntry; use super::FromBn; @@ -27,7 +28,7 @@ pub fn encode_frame_table_entry( + iid } -impl StaticFrameEntry { +impl FrameTableEntryInternal { pub fn encode(&self) -> BigUint { encode_frame_table_entry( self.frame_id.to_biguint().unwrap(), @@ -39,14 +40,18 @@ impl StaticFrameEntry { } } -impl JumpTableEntry { +impl CalledFrameTableEntry { pub fn encode(&self) -> BigUint { - encode_frame_table_entry( - self.eid.to_biguint().unwrap(), - self.last_jump_eid.to_biguint().unwrap(), - self.callee_fid.to_biguint().unwrap(), - self.fid.to_biguint().unwrap(), - self.iid.to_biguint().unwrap(), - ) + self.0.encode() + } +} + +impl InheritedFrameTableEntry { + pub fn encode(&self) -> BigUint { + if let Some(entry) = self.0 { + entry.encode() + } else { + FrameTableEntryInternal::default().encode() + } } } diff --git a/crates/specs/src/etable.rs b/crates/specs/src/etable.rs index bc9ca875c..aa5dd5c2d 100644 --- a/crates/specs/src/etable.rs +++ b/crates/specs/src/etable.rs @@ -10,6 +10,8 @@ use crate::itable::InstructionTable; use crate::itable::InstructionTableEntry; use crate::step::StepInfo; +const JSON: bool = false; + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct EventTableEntry { pub eid: u32, @@ -61,7 +63,12 @@ impl EventTable { pub fn write(&self, path: &PathBuf) -> std::io::Result<()> { let mut fd = std::fs::File::create(path)?; - fd.write_all(&bincode::serialize(self).unwrap())?; + + if JSON { + fd.write_all(&serde_json::to_string_pretty(self).unwrap().as_bytes())?; + } else { + fd.write_all(&bincode::serialize(self).unwrap())?; + } Ok(()) } @@ -69,8 +76,12 @@ impl EventTable { let mut fd = std::fs::File::open(path)?; let mut buf = Vec::new(); fd.read_to_end(&mut buf)?; - let etable = bincode::deserialize(&mut buf).unwrap(); - Ok(etable) + + if JSON { + Ok(serde_json::from_slice(&buf).unwrap()) + } else { + Ok(bincode::deserialize(&mut buf).unwrap()) + } } pub fn unwrap(self) -> Vec { @@ -96,8 +107,3 @@ impl EventTable { .collect::>() } } - -pub enum EventTableBackend { - Memory(EventTable), - Json(PathBuf), -} diff --git a/crates/specs/src/jtable.rs b/crates/specs/src/jtable.rs index 9bb392d71..5c654a6fd 100644 --- a/crates/specs/src/jtable.rs +++ b/crates/specs/src/jtable.rs @@ -1,45 +1,137 @@ +use std::io::Read; +use std::io::Write; +use std::path::PathBuf; +use std::sync::Arc; + use serde::Deserialize; use serde::Serialize; -// 1. jumps to zkmain -// 2. jumps to start(if exists) -pub const STATIC_FRAME_ENTRY_NUMBER: usize = 2; +// Inherited frame table entries: +// 1. zkmain +// 2. start(optional) +// 3. active frames inherited from last slice +pub const INHERITED_FRAME_TABLE_ENTRIES: usize = 4096; + +const JSON: bool = false; -#[derive(Default, Serialize, Deserialize, Debug, Clone)] -pub struct StaticFrameEntry { - pub enable: bool, +#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)] +pub struct FrameTableEntryInternal { + // caller eid (unique) pub frame_id: u32, pub next_frame_id: u32, pub callee_fid: u32, pub fid: u32, pub iid: u32, + pub returned: bool, } -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct JumpTableEntry { - // caller eid (unique) - pub eid: u32, - pub last_jump_eid: u32, - pub callee_fid: u32, - pub fid: u32, - pub iid: u32, +#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)] +pub struct InheritedFrameTableEntry(pub Option); + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct InheritedFrameEntries(Vec); + +impl From> for InheritedFrameEntries { + fn from(value: Vec) -> Self { + Self(value) + } +} + +#[derive(Debug)] +pub struct InheritedFrameTable(pub Box<[InheritedFrameTableEntry; INHERITED_FRAME_TABLE_ENTRIES]>); + +impl Default for InheritedFrameTable { + fn default() -> Self { + Self(Box::new( + [InheritedFrameTableEntry::default(); INHERITED_FRAME_TABLE_ENTRIES], + )) + } } -impl JumpTableEntry { - pub fn to_string(&self) -> String { - serde_json::to_string(self).unwrap() +impl TryFrom> for InheritedFrameTable { + type Error = Vec; + + fn try_from(value: Vec) -> Result { + let mut value = value; + if value.len() > INHERITED_FRAME_TABLE_ENTRIES { + return Err(value); + } + value.resize_with(INHERITED_FRAME_TABLE_ENTRIES, Default::default); + Ok(Self(Box::new(value.try_into()?))) } } +impl TryFrom for InheritedFrameTable { + type Error = InheritedFrameEntries; + + fn try_from(value: InheritedFrameEntries) -> Result { + if value.0.len() > INHERITED_FRAME_TABLE_ENTRIES { + return Err(value); + } + + let mut value = value.0; + value.resize_with(INHERITED_FRAME_TABLE_ENTRIES, Default::default); + Ok(Self(Box::new(value.try_into()?))) + } +} + +impl InheritedFrameTable { + pub fn iter(&self) -> std::slice::Iter<'_, InheritedFrameTableEntry> { + self.0.iter() + } +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct CalledFrameTableEntry(pub FrameTableEntryInternal); + #[derive(Clone, Debug, Default, Serialize, Deserialize)] -pub struct JumpTable(Vec); +pub struct CalledFrameTable(Vec); + +impl CalledFrameTable { + pub fn new(entries: Vec) -> Self { + Self(entries) + } + + pub fn iter(&self) -> std::slice::Iter<'_, CalledFrameTableEntry> { + self.0.iter() + } -impl JumpTable { - pub fn entries(&self) -> &Vec { - &self.0 + pub fn into_inner(self) -> Vec { + self.0 } - pub fn push(&mut self, entry: JumpTableEntry) { - self.0.push(entry) + pub fn len(&self) -> usize { + self.0.len() + } +} + +#[derive(Serialize, Deserialize)] +pub struct FrameTable { + pub inherited: Arc, + pub called: CalledFrameTable, +} + +impl FrameTable { + pub fn read(path: &PathBuf) -> std::io::Result { + let mut fd = std::fs::File::open(path)?; + let mut buf = Vec::new(); + fd.read_to_end(&mut buf)?; + + if JSON { + Ok(serde_json::from_slice(&buf).unwrap()) + } else { + Ok(bincode::deserialize(&mut buf).unwrap()) + } + } + + pub fn write(&self, path: &PathBuf) -> std::io::Result<()> { + let mut fd = std::fs::File::create(path)?; + + if JSON { + fd.write_all(&serde_json::to_string_pretty(self).unwrap().as_bytes())?; + } else { + fd.write_all(&bincode::serialize(self).unwrap())?; + } + Ok(()) } } diff --git a/crates/specs/src/lib.rs b/crates/specs/src/lib.rs index 187f54067..b7b41ccdf 100644 --- a/crates/specs/src/lib.rs +++ b/crates/specs/src/lib.rs @@ -11,15 +11,10 @@ use brtable::BrTable; use brtable::ElemTable; use configure_table::ConfigureTable; use etable::EventTable; -use etable::EventTableBackend; use imtable::InitMemoryTable; use itable::InstructionTable; -use jtable::JumpTable; -use jtable::StaticFrameEntry; -use jtable::STATIC_FRAME_ENTRY_NUMBER; -use num_bigint::BigUint; -use serde::Deserialize; -use serde::Serialize; +use jtable::FrameTable; +use jtable::InheritedFrameTable; use state::InitializationState; use crate::external_host_call_table::ExternalHostCallTable; @@ -43,28 +38,34 @@ pub mod state; pub mod step; pub mod types; -pub const MASK: bool = true; - pub enum TraceBackend { - File(Box PathBuf>), + File { + event_table_writer: Box PathBuf>, + frame_table_writer: Box PathBuf>, + }, Memory, } -#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum TableBackend { + Memory(Table), + Json(PathBuf), +} + +#[derive(Debug)] pub struct CompilationTable { pub itable: Arc, pub imtable: Arc, pub br_table: Arc, pub elem_table: Arc, pub configure_table: Arc, - pub static_jtable: Arc<[StaticFrameEntry; STATIC_FRAME_ENTRY_NUMBER]>, - pub initialization_state: Arc>, + pub initial_frame_table: Arc, + pub initialization_state: Arc>, } #[derive(Default)] pub struct ExecutionTable { - pub etable: Vec, - pub jtable: JumpTable, + pub etable: Vec>, + pub frame_table: Vec>, } pub struct Tables { @@ -73,7 +74,12 @@ pub struct Tables { } impl Tables { - pub fn write(&self, dir: &PathBuf, name_of_etable_slice: impl Fn(usize) -> String) { + pub fn write( + &self, + dir: &PathBuf, + name_of_etable_slice: impl Fn(usize) -> String, + name_of_frame_table_slice: impl Fn(usize) -> String, + ) { fn write_file(folder: &PathBuf, filename: &str, buf: &String) { let folder = folder.join(filename); let mut fd = File::create(folder.as_path()).unwrap(); @@ -87,14 +93,14 @@ impl Tables { .iter() .enumerate() .for_each(|(slice, e)| match e { - EventTableBackend::Memory(etable) => { + TableBackend::Memory(etable) => { external_host_call_table.extend(etable.filter_external_host_call_table().0); let path = dir.join(name_of_etable_slice(slice)); etable.write(&path).unwrap(); } - EventTableBackend::Json(path) => { + TableBackend::Json(path) => { let etable = EventTable::read(&path).unwrap(); external_host_call_table.extend(etable.filter_external_host_call_table().0); } @@ -106,11 +112,19 @@ impl Tables { "itable.json", &serde_json::to_string_pretty(&self.compilation_tables.itable).unwrap(), ); - write_file( - dir, - "jtable.json", - &serde_json::to_string_pretty(&self.execution_tables.jtable).unwrap(), - ); + self.execution_tables + .frame_table + .iter() + .enumerate() + .for_each(|(slice, frame_table)| { + if let TableBackend::Memory(frame_table) = frame_table { + write_file( + dir, + &name_of_frame_table_slice(slice), + &serde_json::to_string_pretty(frame_table).unwrap(), + ); + } + }); write_file( dir, "external_host_table.json", diff --git a/crates/specs/src/slice.rs b/crates/specs/src/slice.rs index 3b0145dc7..0de972b56 100644 --- a/crates/specs/src/slice.rs +++ b/crates/specs/src/slice.rs @@ -1,7 +1,7 @@ +use std::collections::HashMap; use std::collections::HashSet; use std::sync::Arc; -use num_bigint::BigUint; use rayon::iter::IntoParallelRefIterator; use rayon::iter::ParallelIterator; @@ -12,31 +12,63 @@ use crate::etable::EventTable; use crate::etable::EventTableEntry; use crate::imtable::InitMemoryTable; use crate::itable::InstructionTable; -use crate::jtable::JumpTable; +use crate::jtable::CalledFrameTable; +use crate::jtable::FrameTable; +use crate::jtable::InheritedFrameTable; use crate::mtable::AccessType; use crate::mtable::LocationType; use crate::mtable::MTable; use crate::mtable::MemoryTableEntry; use crate::state::InitializationState; use crate::CompilationTable; -use crate::StaticFrameEntry; -use crate::STATIC_FRAME_ENTRY_NUMBER; + +#[derive(Debug)] +pub struct FrameTableSlice { + pub inherited: Arc, + pub called: CalledFrameTable, +} + +impl From for FrameTableSlice { + fn from(frame_table: FrameTable) -> Self { + FrameTableSlice { + inherited: Arc::new((*frame_table.inherited).clone().try_into().unwrap()), + called: frame_table.called, + } + } +} + +impl FrameTableSlice { + pub fn build_returned_lookup_mapping(&self) -> HashMap<(u32, u32), bool> { + let mut lookup_table = HashMap::new(); + for entry in self.called.iter() { + lookup_table.insert((entry.0.frame_id, entry.0.callee_fid), entry.0.returned); + } + for entry in self.inherited.0.iter() { + if let Some(entry) = entry.0.as_ref() { + lookup_table.insert((entry.frame_id, entry.callee_fid), entry.returned); + } + } + + lookup_table + } +} pub struct Slice { pub itable: Arc, pub br_table: Arc, pub elem_table: Arc, pub configure_table: Arc, - pub static_jtable: Arc<[StaticFrameEntry; STATIC_FRAME_ENTRY_NUMBER]>, + pub initial_frame_table: Arc, pub etable: Arc, - pub frame_table: Arc, + pub frame_table: Arc, + pub post_inherited_frame_table: Arc, pub imtable: Arc, pub post_imtable: Arc, - pub initialization_state: Arc>, - pub post_initialization_state: Arc>, + pub initialization_state: Arc>, + pub post_initialization_state: Arc>, pub is_last_slice: bool, } @@ -51,10 +83,14 @@ impl Slice { br_table: compilation_table.br_table.clone(), elem_table: compilation_table.elem_table.clone(), configure_table: compilation_table.configure_table.clone(), - static_jtable: compilation_table.static_jtable.clone(), + initial_frame_table: compilation_table.initial_frame_table.clone(), etable: EventTable::default().into(), - frame_table: JumpTable::default().into(), + frame_table: Arc::new(FrameTableSlice { + inherited: compilation_table.initial_frame_table.clone(), + called: CalledFrameTable::default(), + }), + post_inherited_frame_table: compilation_table.initial_frame_table.clone(), imtable: compilation_table.imtable.clone(), post_imtable: compilation_table.imtable.clone(), diff --git a/crates/specs/src/state.rs b/crates/specs/src/state.rs index 305c8302d..ee7be588a 100644 --- a/crates/specs/src/state.rs +++ b/crates/specs/src/state.rs @@ -1,10 +1,8 @@ -use num_bigint::BigUint; use serde::Deserialize; use serde::Serialize; -#[cfg(feature = "continuation")] #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct InitializationState { +pub struct InitializationState { pub eid: T, pub fid: T, pub iid: T, @@ -18,31 +16,14 @@ pub struct InitializationState { pub initial_memory_pages: T, pub maximal_memory_pages: T, - - pub jops: U, } -#[cfg(not(feature = "continuation"))] -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct InitializationState { - pub eid: T, - pub fid: T, - pub iid: T, - pub frame_id: T, - pub sp: T, - - pub host_public_inputs: T, - pub context_in_index: T, - pub context_out_index: T, - pub external_host_call_call_index: T, - - pub initial_memory_pages: T, - pub maximal_memory_pages: T, - - pub _phantom: std::marker::PhantomData, -} +impl InitializationState { + // TODO: try to remove the magic number + pub fn field_count() -> usize { + 11 + } -impl InitializationState { pub fn zip_for_each( &self, other: &Self, @@ -65,14 +46,33 @@ impl InitializationState { closure(&self.initial_memory_pages, &other.initial_memory_pages)?; closure(&self.maximal_memory_pages, &other.maximal_memory_pages)?; - #[cfg(feature = "continuation")] - closure(&self.jops, &other.jops)?; - Ok(()) } + + pub fn for_each(&self, f: impl FnMut(&T) -> U) { + self.map(f); + } + + pub fn map(&self, mut f: impl FnMut(&T) -> U) -> InitializationState { + InitializationState { + eid: f(&self.eid), + fid: f(&self.fid), + iid: f(&self.iid), + frame_id: f(&self.frame_id), + sp: f(&self.sp), + + host_public_inputs: f(&self.host_public_inputs), + context_in_index: f(&self.context_in_index), + context_out_index: f(&self.context_out_index), + external_host_call_call_index: f(&self.external_host_call_call_index), + + initial_memory_pages: f(&self.initial_memory_pages), + maximal_memory_pages: f(&self.maximal_memory_pages), + } + } } -impl Default for InitializationState { +impl Default for InitializationState { fn default() -> Self { Self { eid: Default::default(), @@ -88,17 +88,11 @@ impl Default for InitializationState { initial_memory_pages: Default::default(), maximal_memory_pages: Default::default(), - - #[cfg(feature = "continuation")] - jops: Default::default(), - - #[cfg(not(feature = "continuation"))] - _phantom: std::marker::PhantomData, } } } -impl InitializationState { +impl InitializationState { pub fn plain(&self) -> Vec { let mut v = vec![]; @@ -116,57 +110,12 @@ impl InitializationState { v.push(self.initial_memory_pages.clone()); v.push(self.maximal_memory_pages.clone()); - #[cfg(feature = "continuation")] - v.push(self.jops.clone()); - v } } -impl InitializationState { - pub fn field_count() -> usize { - if cfg!(feature = "continuation") { - 12 - } else { - 11 - } - } - - pub fn for_each(&self, f: impl FnMut(&T) -> V, g: impl FnMut(&U) -> V) { - self.map(f, g); - } - - pub fn map( - &self, - mut f: impl FnMut(&T) -> V, - mut _g: impl FnMut(&U) -> V, - ) -> InitializationState { - InitializationState { - eid: f(&self.eid), - fid: f(&self.fid), - iid: f(&self.iid), - frame_id: f(&self.frame_id), - sp: f(&self.sp), - - host_public_inputs: f(&self.host_public_inputs), - context_in_index: f(&self.context_in_index), - context_out_index: f(&self.context_out_index), - external_host_call_call_index: f(&self.external_host_call_call_index), - - initial_memory_pages: f(&self.initial_memory_pages), - maximal_memory_pages: f(&self.maximal_memory_pages), - - #[cfg(feature = "continuation")] - jops: _g(&self.jops), - - #[cfg(not(feature = "continuation"))] - _phantom: std::marker::PhantomData, - } - } -} - -impl InitializationState, Result> { - pub fn transpose(self) -> Result, E> { +impl InitializationState> { + pub fn transpose(self) -> Result, E> { Ok(InitializationState { eid: self.eid?, fid: self.fid?, @@ -179,12 +128,6 @@ impl InitializationState, Result> { external_host_call_call_index: self.external_host_call_call_index?, initial_memory_pages: self.initial_memory_pages?, maximal_memory_pages: self.maximal_memory_pages?, - - #[cfg(feature = "continuation")] - jops: self.jops?, - - #[cfg(not(feature = "continuation"))] - _phantom: std::marker::PhantomData, }) } } diff --git a/crates/zkwasm/src/checksum/mod.rs b/crates/zkwasm/src/checksum/mod.rs index c9bd60cb6..d11851139 100644 --- a/crates/zkwasm/src/checksum/mod.rs +++ b/crates/zkwasm/src/checksum/mod.rs @@ -16,7 +16,7 @@ impl ImageCheckSum> for CompilationTable { &self.itable, &self.br_table, &self.elem_table, - &self.static_jtable, + &self.initial_frame_table, &self.initialization_state, &self.imtable, ) diff --git a/crates/zkwasm/src/circuits/etable/allocator.rs b/crates/zkwasm/src/circuits/etable/allocator.rs index af43fac64..798da643b 100644 --- a/crates/zkwasm/src/circuits/etable/allocator.rs +++ b/crates/zkwasm/src/circuits/etable/allocator.rs @@ -4,7 +4,6 @@ use crate::circuits::bit_table::BitTableOp; use crate::circuits::cell::*; use crate::circuits::config::common_range_max; use crate::circuits::etable::ConstraintBuilder; -use crate::circuits::jtable::JumpTableConfig; use crate::circuits::rtable::RangeTableConfig; use crate::circuits::traits::ConfigureLookupTable; use crate::circuits::utils::bit::BitColumn; @@ -12,7 +11,6 @@ use crate::circuits::utils::common_range::CommonRangeColumn; use crate::circuits::utils::u16::U16Column; use crate::circuits::utils::u8::U8Column; use crate::circuits::Context; -use crate::circuits::Lookup; use crate::constant_from; use crate::curr; use crate::fixed_curr; @@ -64,12 +62,6 @@ impl_cell!(AllocatedU8Cell); impl_cell!(AllocatedU16Cell); impl_cell!(AllocatedCommonRangeCell); impl_cell!(AllocatedUnlimitedCell); -impl_cell!(AllocatedJumpTableLookupCell); - -#[derive(Debug, Clone, Copy)] -pub(crate) struct AllocatedJumpTableLookupCell { - pub(crate) cell: AllocatedCell, -} #[derive(Debug, Clone, Copy)] pub(crate) struct AllocatedMemoryTableLookupReadCell { @@ -161,7 +153,6 @@ pub(crate) enum EventTableCellType { CommonRange, Unlimited, MTableLookup, - JTableLookup, } const BIT_COLUMNS: usize = 12; @@ -179,10 +170,9 @@ const COMMON_RANGE_COLUMNS: usize = if cfg!(feature = "continuation") { 4 } else const UNLIMITED_COLUMNS: usize = if cfg!(feature = "continuation") { 10 } else { - 7 + 8 }; const MEMORY_TABLE_LOOKUP_COLUMNS: usize = 2; -const JUMP_TABLE_LOOKUP_COLUMNS: usize = 1; #[derive(Clone, Copy)] pub(crate) struct AllocatedBitTableLookupCells { @@ -362,10 +352,9 @@ impl EventTableCellAllocator { sel: Column, rtable: &RangeTableConfig, mtable: &impl ConfigureLookupTable, - jtable: &JumpTableConfig, cols: &mut impl Iterator>, ) -> Self { - let mut allocator = Self::_new(meta, k, sel, rtable, mtable, jtable, cols); + let mut allocator = Self::_new(meta, k, sel, rtable, mtable, cols); for _ in 0..U32_CELLS { let cell = allocator.prepare_alloc_u32_cell(); allocator.free_u32_cells.push(cell); @@ -388,7 +377,6 @@ impl EventTableCellAllocator { sel: Column, rtable: &RangeTableConfig, mtable: &impl ConfigureLookupTable, - jtable: &JumpTableConfig, cols: &mut impl Iterator>, ) -> Self { let mut all_cols = BTreeMap::new(); @@ -431,19 +419,6 @@ impl EventTableCellAllocator { .into_iter() .collect(), ); - all_cols.insert( - EventTableCellType::JTableLookup, - [0; JUMP_TABLE_LOOKUP_COLUMNS] - .map(|_| { - let col = cols.next().unwrap(); - jtable.configure_in_table(meta, "c8c. jtable_lookup in jtable", |meta| { - curr!(meta, col) - }); - vec![col] - }) - .into_iter() - .collect(), - ); all_cols.insert( EventTableCellType::MTableLookup, [0; MEMORY_TABLE_LOOKUP_COLUMNS] @@ -477,7 +452,6 @@ impl EventTableCellAllocator { (EventTableCellType::CommonRange, (0, 0)), (EventTableCellType::Unlimited, (0, 0)), (EventTableCellType::MTableLookup, (0, 0)), - (EventTableCellType::JTableLookup, (0, 0)), ] .into_iter(), ), @@ -568,12 +542,6 @@ impl EventTableCellAllocator { } } - pub(crate) fn alloc_jump_table_lookup_cell(&mut self) -> AllocatedJumpTableLookupCell { - AllocatedJumpTableLookupCell { - cell: self.alloc(&EventTableCellType::JTableLookup), - } - } - pub(crate) fn alloc_memory_table_lookup_read_cell( &mut self, name: &'static str, diff --git a/crates/zkwasm/src/circuits/etable/assign.rs b/crates/zkwasm/src/circuits/etable/assign.rs index 5adfac308..a6412e2a6 100644 --- a/crates/zkwasm/src/circuits/etable/assign.rs +++ b/crates/zkwasm/src/circuits/etable/assign.rs @@ -4,13 +4,13 @@ use halo2_proofs::circuit::Layouter; use halo2_proofs::circuit::Region; use halo2_proofs::plonk::Error; use log::debug; -use num_bigint::BigUint; use rayon::iter::IndexedParallelIterator; use rayon::iter::IntoParallelRefIterator; use rayon::iter::ParallelIterator; use specs::configure_table::ConfigureTable; use specs::itable::InstructionTable; use specs::itable::OpcodeClassPlain; +use specs::slice::FrameTableSlice; use specs::state::InitializationState; use std::collections::BTreeMap; use std::sync::Arc; @@ -19,6 +19,7 @@ use super::EventTableChip; use super::OpcodeConfig; use super::EVENT_TABLE_ENTRY_ROWS; use crate::circuits::cell::CellExpression; +use crate::circuits::jtable::FrameEtablePermutationCells; use crate::circuits::utils::bn_to_field; use crate::circuits::utils::step_status::Status; use crate::circuits::utils::step_status::StepStatus; @@ -64,19 +65,17 @@ use crate::circuits::utils::Context; pub(in crate::circuits) struct EventTablePermutationCells { pub(in crate::circuits) rest_mops: AssignedCell, // rest_jops cell at first step - pub(in crate::circuits) rest_jops: Option>, - pub(in crate::circuits) pre_initialization_state: - InitializationState, AssignedCell>, - pub(in crate::circuits) post_initialization_state: - InitializationState, AssignedCell>, + pub(in crate::circuits) rest_jops: FrameEtablePermutationCells, + pub(in crate::circuits) pre_initialization_state: InitializationState>, + pub(in crate::circuits) post_initialization_state: InitializationState>, } impl EventTableChip { fn assign_step_state( &self, ctx: &mut Context<'_, F>, - state: &InitializationState, - ) -> Result, AssignedCell>, Error> { + state: &InitializationState, + ) -> Result>, Error> { cfg_if::cfg_if! { if #[cfg(feature="continuation")] { macro_rules! assign_u32_state { @@ -102,16 +101,6 @@ impl EventTableChip { }; } - #[cfg(feature = "continuation")] - macro_rules! assign_biguint { - ($cell:ident, $value:expr) => { - self.config - .common_config - .$cell - .assign(ctx, bn_to_field(&$value))? - }; - } - let eid = assign_u32_state!(eid_cell, state.eid); let fid = assign_common_range_advice!(fid_cell, state.fid); let iid = assign_common_range_advice!(iid_cell, state.iid); @@ -134,9 +123,6 @@ impl EventTableChip { let maximal_memory_pages = assign_common_range_advice!(maximal_memory_pages_cell, state.maximal_memory_pages); - #[cfg(feature = "continuation")] - let jops = assign_biguint!(jops_cell, state.jops); - ctx.step(EVENT_TABLE_ENTRY_ROWS as usize); Ok(InitializationState { @@ -153,12 +139,6 @@ impl EventTableChip { initial_memory_pages, maximal_memory_pages, - - #[cfg(feature = "continuation")] - jops, - - #[cfg(not(feature = "continuation"))] - _phantom: core::marker::PhantomData, }) } @@ -167,29 +147,23 @@ impl EventTableChip { op_configs: Arc>>, itable: &InstructionTable, event_table: &EventTableWithMemoryInfo, - _initialization_state: &InitializationState, - ) -> (u32, BigUint) { - let (rest_mops, _rest_jops) = event_table.0.iter().fold( - (0, BigUint::from(0u64)), - |(rest_mops_sum, rest_jops_sum), entry| { + ) -> (u32, u32, u32) { + let (rest_mops, rest_call_ops, rest_return_ops) = event_table.0.iter().fold( + (0, 0, 0), + |(rest_mops_sum, rest_call_ops_sum, rest_return_ops_sum), entry| { let instruction = entry.eentry.get_instruction(itable); let op_config = op_configs.get(&((&instruction.opcode).into())).unwrap(); ( rest_mops_sum + op_config.0.memory_writing_ops(&entry.eentry), - rest_jops_sum + op_config.0.jops(), + rest_call_ops_sum + op_config.0.call_ops(), + rest_return_ops_sum + op_config.0.return_ops(), ) }, ); - cfg_if::cfg_if! { - if #[cfg(feature="continuation")] { - (rest_mops, _initialization_state.jops.clone()) - } else { - (rest_mops, _rest_jops) - } - } + (rest_mops, rest_call_ops, rest_return_ops) } fn init(&self, ctx: &mut Context<'_, F>) -> Result<(), Error> { @@ -211,10 +185,16 @@ impl EventTableChip { F::zero(), )?; - #[cfg(not(feature = "continuation"))] ctx.region.assign_advice_from_constant( - || "etable: rest jops terminates", - self.config.common_config.jops_cell.cell.col, + || "etable: rest call ops terminates", + self.config.common_config.rest_call_ops_cell.cell.col, + ctx.offset, + F::zero(), + )?; + + ctx.region.assign_advice_from_constant( + || "etable: rest return ops terminates", + self.config.common_config.rest_return_ops_cell.cell.col, ctx.offset, F::zero(), )?; @@ -226,23 +206,38 @@ impl EventTableChip { fn assign_rest_ops_first_step( &self, ctx: &mut Context<'_, F>, - ) -> Result<(AssignedCell, AssignedCell), Error> { + ) -> Result<(AssignedCell, FrameEtablePermutationCells), Error> { let rest_mops_cell = self .config .common_config .rest_mops_cell .assign(ctx, F::zero())?; - let rest_jops_cell = self.config.common_config.jops_cell.assign(ctx, F::zero())?; + let rest_call_ops_cell = self + .config + .common_config + .rest_call_ops_cell + .assign(ctx, F::zero())?; + let rest_return_ops_cell = self + .config + .common_config + .rest_return_ops_cell + .assign(ctx, F::zero())?; - Ok((rest_mops_cell, rest_jops_cell)) + Ok(( + rest_mops_cell, + FrameEtablePermutationCells { + rest_call_ops: rest_call_ops_cell, + rest_return_ops: rest_return_ops_cell, + }, + )) } fn assign_padding_and_post_initialization_state( &self, ctx: &mut Context<'_, F>, - initialization_state: &InitializationState, - ) -> Result, AssignedCell>, Error> { + initialization_state: &InitializationState, + ) -> Result>, Error> { while ctx.offset < self.capability * EVENT_TABLE_ENTRY_ROWS as usize { self.assign_step_state(ctx, initialization_state)?; } @@ -257,10 +252,12 @@ impl EventTableChip { itable: &InstructionTable, event_table: &EventTableWithMemoryInfo, configure_table: &ConfigureTable, - initialization_state: &InitializationState, - post_initialization_state: &InitializationState, + frame_table: &FrameTableSlice, + initialization_state: &InitializationState, + post_initialization_state: &InitializationState, rest_mops: u32, - jops: BigUint, + rest_call_ops: u32, + rest_return_ops: u32, ) -> Result<(), Error> { macro_rules! assign_advice { ($ctx:expr, $cell:ident, $value:expr) => { @@ -285,6 +282,8 @@ impl EventTableChip { return Ok(()); } + let frame_table_returned_lookup = frame_table.build_returned_lookup_mapping(); + let status = { let mut host_public_inputs = initialization_state.host_public_inputs; let mut context_in_index = initialization_state.context_in_index; @@ -293,7 +292,8 @@ impl EventTableChip { initialization_state.external_host_call_call_index; let mut rest_mops = rest_mops; - let mut jops = jops; + let mut rest_call_ops = rest_call_ops; + let mut rest_return_ops = rest_return_ops; let mut status = event_table .0 @@ -312,7 +312,8 @@ impl EventTableChip { allocated_memory_pages: entry.eentry.allocated_memory_pages, rest_mops, - jops: jops.clone(), + rest_call_ops, + rest_return_ops, host_public_inputs, context_in_index, @@ -336,11 +337,8 @@ impl EventTableChip { } rest_mops -= op_config.0.memory_writing_ops(&entry.eentry); - if cfg!(feature = "continuation") { - jops += op_config.0.jops() - } else { - jops -= op_config.0.jops() - } + rest_call_ops -= op_config.0.call_ops(); + rest_return_ops -= op_config.0.return_ops(); status }) @@ -375,7 +373,8 @@ impl EventTableChip { .external_host_call_call_index, rest_mops, - jops, + rest_call_ops, + rest_return_ops, itable, }; @@ -399,6 +398,7 @@ impl EventTableChip { current: &status[index], next: &status[index + 1], configure_table, + frame_table_returned_lookup: &frame_table_returned_lookup, }; { @@ -419,7 +419,16 @@ impl EventTableChip { itable_lookup_cell, bn_to_field(&instruction.encode) ); - assign_advice!(&mut ctx, jops_cell, bn_to_field(&status[index].jops)); + assign_advice!( + &mut ctx, + rest_call_ops_cell, + F::from(status[index].rest_call_ops as u64) + ); + assign_advice!( + &mut ctx, + rest_return_ops_cell, + F::from(status[index].rest_return_ops as u64) + ); { let op_config = op_configs.get(&((&instruction.opcode).into())).unwrap(); @@ -443,12 +452,6 @@ impl EventTableChip { initial_memory_pages: entry.eentry.allocated_memory_pages, maximal_memory_pages: configure_table.maximal_memory_pages, - - #[cfg(feature = "continuation")] - jops: status[index].jops.clone(), - - #[cfg(not(feature = "continuation"))] - _phantom: core::marker::PhantomData, }, ) .unwrap(); @@ -463,8 +466,9 @@ impl EventTableChip { itable: &InstructionTable, event_table: &EventTableWithMemoryInfo, configure_table: &ConfigureTable, - initialization_state: &InitializationState, - post_initialization_state: &InitializationState, + frame_table: &FrameTableSlice, + initialization_state: &InitializationState, + post_initialization_state: &InitializationState, _is_last_slice: bool, ) -> Result, Error> { layouter.assign_region( @@ -483,13 +487,13 @@ impl EventTableChip { self.assign_step_state(&mut ctx, initialization_state)?; ctx.reset(); - let (rest_mops_cell, _jops_cell) = self.assign_rest_ops_first_step(&mut ctx)?; + let (rest_mops_cell, rest_frame_table_cells) = + self.assign_rest_ops_first_step(&mut ctx)?; - let (rest_mops, jops) = self.compute_rest_mops_and_jops( + let (rest_mops, rest_call_ops, rest_return_ops) = self.compute_rest_mops_and_jops( self.config.op_configs.clone(), itable, event_table, - initialization_state, ); self.assign_entries( @@ -498,10 +502,12 @@ impl EventTableChip { itable, event_table, configure_table, + frame_table, &initialization_state, post_initialization_state, rest_mops, - jops, + rest_call_ops, + rest_return_ops, )?; ctx.step(EVENT_TABLE_ENTRY_ROWS as usize * event_table.0.len()); @@ -511,23 +517,12 @@ impl EventTableChip { &post_initialization_state, )?; - cfg_if::cfg_if! { - if #[cfg(feature = "continuation")] { - Ok(EventTablePermutationCells { - rest_mops: rest_mops_cell, - rest_jops: None, - pre_initialization_state, - post_initialization_state: post_initialization_state_cells, - }) - } else { - Ok(EventTablePermutationCells { - rest_mops: rest_mops_cell, - rest_jops: Some(_jops_cell), - pre_initialization_state, - post_initialization_state: post_initialization_state_cells, - }) - } - } + Ok(EventTablePermutationCells { + rest_mops: rest_mops_cell, + rest_jops: rest_frame_table_cells, + pre_initialization_state, + post_initialization_state: post_initialization_state_cells, + }) }, ) } diff --git a/crates/zkwasm/src/circuits/etable/mod.rs b/crates/zkwasm/src/circuits/etable/mod.rs index ccb616a08..e0dd23403 100644 --- a/crates/zkwasm/src/circuits/etable/mod.rs +++ b/crates/zkwasm/src/circuits/etable/mod.rs @@ -54,8 +54,6 @@ use halo2_proofs::plonk::Error; use halo2_proofs::plonk::Expression; use halo2_proofs::plonk::Fixed; use halo2_proofs::plonk::VirtualCells; -use num_bigint::BigUint; -use num_traits::Zero; use specs::encode::instruction_table::encode_instruction_table_entry; use specs::etable::EventTableEntry; use specs::itable::OpcodeClass; @@ -85,8 +83,8 @@ pub struct EventTableCommonConfig { ops: [AllocatedBitCell; OP_CAPABILITY], rest_mops_cell: AllocatedCommonRangeCell, - // If continuation is enabled, it's an incremental counter; otherwise, it's decremental. - jops_cell: AllocatedUnlimitedCell, + rest_call_ops_cell: AllocatedUnlimitedCell, + rest_return_ops_cell: AllocatedUnlimitedCell, pub(crate) input_index_cell: AllocatedCommonRangeCell, pub(crate) context_input_index_cell: AllocatedCommonRangeCell, pub(crate) context_output_index_cell: AllocatedCommonRangeCell, @@ -101,7 +99,9 @@ pub struct EventTableCommonConfig { itable_lookup_cell: AllocatedUnlimitedCell, brtable_lookup_cell: AllocatedUnlimitedCell, - jtable_lookup_cell: AllocatedJumpTableLookupCell, + jtable_lookup_cell: AllocatedUnlimitedCell, + is_returned_cell: AllocatedBitCell, + pow_table_lookup_modulus_cell: AllocatedUnlimitedCell, pow_table_lookup_power_cell: AllocatedUnlimitedCell, bit_table_lookup_cells: AllocatedBitTableLookupCells, @@ -136,11 +136,17 @@ pub trait EventTableOpcodeConfig { ) -> Option> { None } - fn jops_expr(&self, _meta: &mut VirtualCells<'_, F>) -> Option> { + fn call_ops_expr(&self, _meta: &mut VirtualCells<'_, F>) -> Option> { + None + } + fn call_ops(&self) -> u32 { + 0 + } + fn return_ops_expr(&self, _meta: &mut VirtualCells<'_, F>) -> Option> { None } - fn jops(&self) -> BigUint { - BigUint::zero() + fn return_ops(&self) -> u32 { + 0 } fn mops(&self, _meta: &mut VirtualCells<'_, F>) -> Option> { None @@ -237,14 +243,14 @@ impl EventTableConfig { ) -> EventTableConfig { let step_sel = meta.fixed_column(); - let mut allocator = - EventTableCellAllocator::new(meta, k, step_sel, rtable, mtable, jtable, cols); + let mut allocator = EventTableCellAllocator::new(meta, k, step_sel, rtable, mtable, cols); let ops = [0; OP_CAPABILITY].map(|_| allocator.alloc_bit_cell()); let enabled_cell = allocator.alloc_bit_cell(); let rest_mops_cell = allocator.alloc_common_range_cell(); - let jops_cell = allocator.alloc_unlimited_cell(); + let rest_call_ops_cell = allocator.alloc_unlimited_cell(); + let rest_return_ops_cell = allocator.alloc_unlimited_cell(); let input_index_cell = allocator.alloc_common_range_cell(); let context_input_index_cell = allocator.alloc_common_range_cell(); let context_output_index_cell = allocator.alloc_common_range_cell(); @@ -268,6 +274,7 @@ impl EventTableConfig { used_common_range_cells_for_state.0 + (used_common_range_cells_for_state.1 != 0) as usize, ); + let used_unlimited_cells_for_state = allocator .free_cells .get(&EventTableCellType::Unlimited) @@ -280,7 +287,8 @@ impl EventTableConfig { let itable_lookup_cell = allocator.alloc_unlimited_cell(); let brtable_lookup_cell = allocator.alloc_unlimited_cell(); - let jtable_lookup_cell = allocator.alloc_jump_table_lookup_cell(); + let jtable_lookup_cell = allocator.alloc_unlimited_cell(); + let is_returned_cell = allocator.alloc_bit_cell(); let pow_table_lookup_modulus_cell = allocator.alloc_unlimited_cell(); let pow_table_lookup_power_cell = allocator.alloc_unlimited_cell(); let external_foreign_call_lookup_cell = allocator.alloc_unlimited_cell(); @@ -294,7 +302,8 @@ impl EventTableConfig { enabled_cell, ops, rest_mops_cell, - jops_cell, + rest_call_ops_cell, + rest_return_ops_cell, input_index_cell, context_input_index_cell, context_output_index_cell, @@ -309,6 +318,7 @@ impl EventTableConfig { itable_lookup_cell, brtable_lookup_cell, jtable_lookup_cell, + is_returned_cell, pow_table_lookup_modulus_cell, pow_table_lookup_power_cell, bit_table_lookup_cells, @@ -471,17 +481,21 @@ impl EventTableConfig { )] }); - meta.create_gate("c5b. jops change(increase if continuation)", |meta| { - vec![sum_ops_expr_with_init( - if cfg!(feature = "continuation") { - jops_cell.curr_expr(meta) - jops_cell.next_expr(meta) - } else { - jops_cell.next_expr(meta) - jops_cell.curr_expr(meta) - }, - meta, - &|meta, config: &OpcodeConfig| config.0.jops_expr(meta), - None, - )] + meta.create_gate("c5b. rest jops change", |meta| { + vec![ + sum_ops_expr_with_init( + rest_call_ops_cell.next_expr(meta) - rest_call_ops_cell.curr_expr(meta), + meta, + &|meta, config: &OpcodeConfig| config.0.call_ops_expr(meta), + None, + ), + sum_ops_expr_with_init( + rest_return_ops_cell.next_expr(meta) - rest_return_ops_cell.curr_expr(meta), + meta, + &|meta, config: &OpcodeConfig| config.0.return_ops_expr(meta), + None, + ), + ] }); meta.create_gate("c5c. input_index change", |meta| { @@ -620,6 +634,14 @@ impl EventTableConfig { brtable_lookup_cell.curr_expr(meta) * fixed_curr!(meta, step_sel) }); + jtable.configure_in_event_table(meta, "c8c. jtable_lookup in jtable", |meta| { + ( + fixed_curr!(meta, step_sel), + common_config.is_returned_cell.curr_expr(meta) * fixed_curr!(meta, step_sel), + common_config.jtable_lookup_cell.curr_expr(meta) * fixed_curr!(meta, step_sel), + ) + }); + rtable.configure_in_pow_set( meta, "c8d. pow_table_lookup in pow_table", diff --git a/crates/zkwasm/src/circuits/etable/op_configure/op_call.rs b/crates/zkwasm/src/circuits/etable/op_configure/op_call.rs index 845bd0173..a69c2fec8 100644 --- a/crates/zkwasm/src/circuits/etable/op_configure/op_call.rs +++ b/crates/zkwasm/src/circuits/etable/op_configure/op_call.rs @@ -4,7 +4,6 @@ use crate::circuits::etable::ConstraintBuilder; use crate::circuits::etable::EventTableCommonConfig; use crate::circuits::etable::EventTableOpcodeConfig; use crate::circuits::etable::EventTableOpcodeConfigBuilder; -use crate::circuits::jtable::encode_jops; use crate::circuits::jtable::expression::JtableLookupEntryEncode; use crate::circuits::jtable::JumpTableConfig; use crate::circuits::utils::bn_to_field; @@ -12,19 +11,19 @@ use crate::circuits::utils::step_status::StepStatus; use crate::circuits::utils::table_entry::EventTableEntryWithMemoryInfo; use crate::circuits::utils::Context; use crate::constant_from; -use crate::constant_from_bn; use halo2_proofs::arithmetic::FieldExt; use halo2_proofs::plonk::Error; use halo2_proofs::plonk::Expression; use halo2_proofs::plonk::VirtualCells; -use num_bigint::BigUint; use specs::encode::frame_table::encode_frame_table_entry; use specs::encode::opcode::encode_call; use specs::step::StepInfo; pub struct CallConfig { + // indicates if the calling returned in current slice. + is_returned_cell: AllocatedBitCell, index_cell: AllocatedCommonRangeCell, - frame_table_lookup: AllocatedJumpTableLookupCell, + frame_table_lookup: AllocatedUnlimitedCell, } pub struct CallConfigBuilder {} @@ -60,6 +59,7 @@ impl EventTableOpcodeConfigBuilder for CallConfigBuilder { )); Box::new(CallConfig { + is_returned_cell: common_config.is_returned_cell, index_cell, frame_table_lookup, }) @@ -90,6 +90,15 @@ impl EventTableOpcodeConfig for CallConfig { (step.current.iid + 1).into(), )), )?; + self.is_returned_cell.assign( + ctx, + (*step + .frame_table_returned_lookup + .get(&(step.current.eid, *index)) + .unwrap()) + .into(), + )?; + Ok(()) } @@ -97,12 +106,12 @@ impl EventTableOpcodeConfig for CallConfig { } } - fn jops_expr(&self, _meta: &mut VirtualCells<'_, F>) -> Option> { - Some(constant_from_bn!(&self.jops())) + fn call_ops_expr(&self, _meta: &mut VirtualCells<'_, F>) -> Option> { + Some(constant_from!(self.call_ops() as u64)) } - fn jops(&self) -> BigUint { - encode_jops(0, 1) + fn call_ops(&self) -> u32 { + 1 } fn next_frame_id( diff --git a/crates/zkwasm/src/circuits/etable/op_configure/op_call_indirect.rs b/crates/zkwasm/src/circuits/etable/op_configure/op_call_indirect.rs index 55b52ee15..467a06d90 100644 --- a/crates/zkwasm/src/circuits/etable/op_configure/op_call_indirect.rs +++ b/crates/zkwasm/src/circuits/etable/op_configure/op_call_indirect.rs @@ -4,16 +4,13 @@ use crate::circuits::etable::ConstraintBuilder; use crate::circuits::etable::EventTableCommonConfig; use crate::circuits::etable::EventTableOpcodeConfig; use crate::circuits::etable::EventTableOpcodeConfigBuilder; -use crate::circuits::jtable::encode_jops; use crate::circuits::jtable::expression::JtableLookupEntryEncode; use crate::circuits::jtable::JumpTableConfig; -use crate::circuits::utils::bn_to_field; use crate::circuits::utils::step_status::StepStatus; use crate::circuits::utils::table_entry::EventTableEntryWithMemoryInfo; use crate::circuits::utils::Context; use crate::constant; use crate::constant_from; -use crate::constant_from_bn; use halo2_proofs::arithmetic::FieldExt; use halo2_proofs::plonk::Error; use halo2_proofs::plonk::Expression; @@ -26,6 +23,8 @@ use specs::mtable::LocationType; use specs::step::StepInfo; pub struct CallIndirectConfig { + is_returned_cell: AllocatedBitCell, + type_index: AllocatedCommonRangeCell, func_index: AllocatedCommonRangeCell, offset: AllocatedCommonRangeCell, @@ -33,7 +32,7 @@ pub struct CallIndirectConfig { memory_table_lookup_stack_read: AllocatedMemoryTableLookupReadCell, elem_lookup: AllocatedUnlimitedCell, - frame_table_lookup: AllocatedJumpTableLookupCell, + frame_table_lookup: AllocatedUnlimitedCell, } pub struct CallIndirectConfigBuilder {} @@ -109,6 +108,7 @@ impl EventTableOpcodeConfigBuilder for CallIndirectConfigBuilder )); Box::new(CallIndirectConfig { + is_returned_cell: common_config.is_returned_cell, type_index, func_index, offset, @@ -176,6 +176,15 @@ impl EventTableOpcodeConfig for CallIndirectConfig { ), )?; + self.is_returned_cell.assign( + ctx, + (*step + .frame_table_returned_lookup + .get(&(step.current.eid, *func_index)) + .unwrap()) + .into(), + )?; + Ok(()) } @@ -187,12 +196,12 @@ impl EventTableOpcodeConfig for CallIndirectConfig { Some(constant!(F::one())) } - fn jops_expr(&self, _meta: &mut VirtualCells<'_, F>) -> Option> { - Some(constant_from_bn!(&self.jops())) + fn call_ops_expr(&self, _meta: &mut VirtualCells<'_, F>) -> Option> { + Some(constant_from!(self.call_ops() as u64)) } - fn jops(&self) -> BigUint { - encode_jops(0, 1) + fn call_ops(&self) -> u32 { + 1 } fn next_frame_id( diff --git a/crates/zkwasm/src/circuits/etable/op_configure/op_return.rs b/crates/zkwasm/src/circuits/etable/op_configure/op_return.rs index 5c9867d16..7700952cd 100644 --- a/crates/zkwasm/src/circuits/etable/op_configure/op_return.rs +++ b/crates/zkwasm/src/circuits/etable/op_configure/op_return.rs @@ -4,7 +4,6 @@ use crate::circuits::etable::ConstraintBuilder; use crate::circuits::etable::EventTableCommonConfig; use crate::circuits::etable::EventTableOpcodeConfig; use crate::circuits::etable::EventTableOpcodeConfigBuilder; -use crate::circuits::jtable::encode_jops; use crate::circuits::jtable::expression::JtableLookupEntryEncode; use crate::circuits::jtable::JumpTableConfig; use crate::circuits::utils::bn_to_field; @@ -13,7 +12,6 @@ use crate::circuits::utils::table_entry::EventTableEntryWithMemoryInfo; use crate::circuits::utils::Context; use crate::constant; use crate::constant_from; -use crate::constant_from_bn; use halo2_proofs::arithmetic::FieldExt; use halo2_proofs::plonk::Error; use halo2_proofs::plonk::Expression; @@ -35,7 +33,9 @@ pub struct ReturnConfig { drop: AllocatedCommonRangeCell, is_i32: AllocatedBitCell, value: AllocatedU64Cell, - frame_table_lookup: AllocatedJumpTableLookupCell, + // always assign to one to support sliced frame table lookup + is_returned_cell: AllocatedBitCell, + frame_table_lookup: AllocatedUnlimitedCell, memory_table_lookup_stack_read: AllocatedMemoryTableLookupReadCell, memory_table_lookup_stack_write: AllocatedMemoryTableLookupWriteCell, } @@ -60,6 +60,7 @@ impl EventTableOpcodeConfigBuilder for ReturnConfigBuilder { let frame_id_cell = common_config.frame_id_cell; let eid = common_config.eid_cell; let sp = common_config.sp_cell; + let is_returned_cell = common_config.is_returned_cell; let memory_table_lookup_stack_read = allocator.alloc_memory_table_lookup_read_cell( "op_return stack read", @@ -98,12 +99,18 @@ impl EventTableOpcodeConfigBuilder for ReturnConfigBuilder { }), )); + constraint_builder.constraints.push(( + "return: returned bit", + Box::new(move |meta| vec![is_returned_cell.expr(meta) - constant_from!(1)]), + )); + Box::new(ReturnConfig { keep, drop, is_i32, value, frame_table_lookup, + is_returned_cell, memory_table_lookup_stack_read, memory_table_lookup_stack_write, }) @@ -179,6 +186,9 @@ impl EventTableOpcodeConfig for ReturnConfig { step.next.iid.to_biguint().unwrap(), ), )?; + + self.is_returned_cell.assign(ctx, 1.into())?; + Ok(()) } _ => unreachable!(), @@ -207,12 +217,12 @@ impl EventTableOpcodeConfig for ReturnConfig { } } - fn jops_expr(&self, _meta: &mut VirtualCells<'_, F>) -> Option> { - Some(constant_from_bn!(&self.jops())) + fn return_ops_expr(&self, _meta: &mut VirtualCells<'_, F>) -> Option> { + Some(constant_from!(self.return_ops() as u64)) } - fn jops(&self) -> BigUint { - encode_jops(1, 0) + fn return_ops(&self) -> u32 { + 1 } fn next_frame_id( diff --git a/crates/zkwasm/src/circuits/image_table/assign.rs b/crates/zkwasm/src/circuits/image_table/assign.rs index 4db073839..ed77af576 100644 --- a/crates/zkwasm/src/circuits/image_table/assign.rs +++ b/crates/zkwasm/src/circuits/image_table/assign.rs @@ -5,7 +5,7 @@ use halo2_proofs::arithmetic::FieldExt; use halo2_proofs::circuit::AssignedCell; use halo2_proofs::circuit::Layouter; use halo2_proofs::plonk::Error; -use specs::jtable::STATIC_FRAME_ENTRY_NUMBER; +use specs::jtable::INHERITED_FRAME_TABLE_ENTRIES; use super::ImageTableChip; use crate::circuits::utils::image_table::ImageTableAssigner; @@ -77,31 +77,25 @@ impl ImageTableChip { let initialization_state_handler = |base_offset| { ctx.borrow_mut().offset = base_offset; - let assign_handler = |field: &F| assign!(ctx, self.config.col, *field); - let initialization_state = image_table .initialization_state - .map(assign_handler, assign_handler); + .map(|field: &F| assign!(ctx, self.config.col, *field)); initialization_state.transpose() }; - let static_frame_entries_handler = |base_offset| { + let inherited_frame_entries_handler = |base_offset| { ctx.borrow_mut().offset = base_offset; - let mut cells = vec![]; + let mut cells = Vec::with_capacity(INHERITED_FRAME_TABLE_ENTRIES); - for (enable, entry) in &image_table.static_frame_entries { - let enable = assign!(ctx, self.config.col, *enable)?; + for entry in image_table.inherited_frame_entries.iter() { let entry = assign!(ctx, self.config.col, *entry)?; - cells.push((enable, entry)); + cells.push(entry); } - Ok(cells.try_into().expect(&format!( - "The number of static frame entries should be {}", - STATIC_FRAME_ENTRY_NUMBER - ))) + Ok(cells.try_into().unwrap()) }; let instruction_handler = |base_offset| { @@ -145,7 +139,7 @@ impl ImageTableChip { let result = image_table_assigner.exec( initialization_state_handler, - static_frame_entries_handler, + inherited_frame_entries_handler, instruction_handler, br_table_handler, padding_handler, @@ -154,7 +148,7 @@ impl ImageTableChip { Ok(ImageTableLayouter { initialization_state: result.initialization_state, - static_frame_entries: result.static_frame_entries, + inherited_frame_entries: result.inherited_frame_entries, instructions: result.instructions, br_table_entires: result.br_table_entires, padding_entires: result.padding_entires, diff --git a/crates/zkwasm/src/circuits/jtable/assign.rs b/crates/zkwasm/src/circuits/jtable/assign.rs index 1805abb6d..4eba11fbd 100644 --- a/crates/zkwasm/src/circuits/jtable/assign.rs +++ b/crates/zkwasm/src/circuits/jtable/assign.rs @@ -2,188 +2,274 @@ use halo2_proofs::arithmetic::FieldExt; use halo2_proofs::circuit::AssignedCell; use halo2_proofs::circuit::Layouter; use halo2_proofs::plonk::Error; -use num_bigint::BigUint; -use specs::jtable::JumpTable; -use specs::jtable::StaticFrameEntry; -use specs::jtable::STATIC_FRAME_ENTRY_NUMBER; +use specs::jtable::CalledFrameTable; +use specs::jtable::InheritedFrameTable; +use specs::jtable::INHERITED_FRAME_TABLE_ENTRIES; +use specs::slice::FrameTableSlice; +use wasmi::DEFAULT_CALL_STACK_LIMIT; -use super::encode_jops; -use super::JtableOffset; +use super::FrameEtablePermutationCells; use super::JumpTableChip; +use crate::circuits::jtable::FrameTableValueOffset; use crate::circuits::utils::bn_to_field; use crate::circuits::utils::Context; impl JumpTableChip { - /// Frame Table Constraint 1. The etable and jtable must have the same jops count." + /// Frame Table Constraint 1. The etable and frame table: must have the same jops count." fn assign_first_rest_jops( &self, ctx: &mut Context<'_, F>, - rest_jops: BigUint, - ) -> Result, Error> { - let cell = ctx.region.assign_advice( - || "jtable rest", - self.config.data, - JtableOffset::JtableOffsetRest as usize, - || Ok(bn_to_field(&rest_jops)), + rest_call_ops: u32, + rest_return_ops: u32, + ) -> Result, Error> { + assert_eq!(ctx.offset, 0); + + let assigned_rest_call_cell = ctx.region.assign_advice( + || "frame table: rest call ops", + self.config.value, + ctx.offset + FrameTableValueOffset::CallOps as usize, + || Ok(F::from(rest_call_ops as u64)), + )?; + + let assigned_rest_return_cell = ctx.region.assign_advice( + || "frame table: rest return ops", + self.config.value, + ctx.offset + FrameTableValueOffset::ReturnOps as usize, + || Ok(F::from(rest_return_ops as u64)), )?; - Ok(cell) + Ok(FrameEtablePermutationCells { + rest_call_ops: assigned_rest_call_cell, + rest_return_ops: assigned_rest_return_cell, + }) } fn init(&self, ctx: &mut Context<'_, F>) -> Result<(), Error> { - let capability = self.max_available_rows / JtableOffset::JtableOffsetMax as usize; + let capability = self.max_available_rows / FrameTableValueOffset::Max as usize; + + assert_eq!(ctx.offset, 0); + assert_eq!(INHERITED_FRAME_TABLE_ENTRIES, DEFAULT_CALL_STACK_LIMIT); + assert!(INHERITED_FRAME_TABLE_ENTRIES < capability); for i in 0..capability { ctx.region.assign_fixed( - || "jtable sel", + || "frame table: sel", self.config.sel, ctx.offset, || Ok(F::one()), )?; + if i < INHERITED_FRAME_TABLE_ENTRIES { + ctx.region.assign_fixed( + || "frame table: inherited", + self.config.inherited, + ctx.offset, + || Ok(F::one()), + )?; + } + if i == capability - 1 { ctx.region.assign_advice_from_constant( - || "jtable entry terminate", - self.config.data, - ctx.offset + JtableOffset::JtableOffsetRest as usize, + || "frame table: entry terminate", + self.config.value, + ctx.offset + FrameTableValueOffset::CallOps as usize, + F::zero(), + )?; + + ctx.region.assign_advice_from_constant( + || "frame table: entry terminate", + self.config.value, + ctx.offset + FrameTableValueOffset::ReturnOps as usize, F::zero(), )?; } - ctx.step(JtableOffset::JtableOffsetMax as usize); + ctx.step(FrameTableValueOffset::Max as usize); } + ctx.region.assign_fixed( + || "frame table: inherited", + self.config.inherited, + ctx.offset, + || Ok(F::zero()), + )?; + + ctx.region.assign_advice( + || "frame table: disabled row", + self.config.value, + ctx.offset + FrameTableValueOffset::Enable as usize, + || Ok(F::zero()), + )?; + + ctx.region.assign_advice( + || "frame table: disabled row", + self.config.value, + ctx.offset + FrameTableValueOffset::Returned as usize, + || Ok(F::zero()), + )?; + + ctx.region.assign_advice( + || "frame table: disabled row", + self.config.value, + ctx.offset + FrameTableValueOffset::Encode as usize, + || Ok(F::zero()), + )?; + + ctx.region.assign_advice( + || "frame table: disabled row", + self.config.value, + ctx.offset + FrameTableValueOffset::CallOps as usize, + || Ok(F::zero()), + )?; + + ctx.region.assign_advice( + || "frame table: disabled row", + self.config.value, + ctx.offset + FrameTableValueOffset::ReturnOps as usize, + || Ok(F::zero()), + )?; + Ok(()) } - fn assign_static_entries_and_first_rest_jops( + fn assign_inherited_entries_and_first_rest_jops( &self, ctx: &mut Context<'_, F>, - rest_jops: &mut BigUint, - static_entries: &[StaticFrameEntry; STATIC_FRAME_ENTRY_NUMBER], - ) -> Result<[(AssignedCell, AssignedCell); STATIC_FRAME_ENTRY_NUMBER], Error> { + rest_call_ops: &mut u32, + rest_return_ops: &mut u32, + inherited_table: &InheritedFrameTable, + ) -> Result; INHERITED_FRAME_TABLE_ENTRIES]>, Error> { let mut cells = vec![]; - for entry in static_entries { - ctx.region.assign_fixed( - || "jtable start entries", - self.config.static_bit, - ctx.offset, - || Ok(F::one()), + for entry in inherited_table.0.iter() { + let entry_cell = ctx.region.assign_advice( + || "frame table: encode", + self.config.value, + ctx.offset + FrameTableValueOffset::Encode as usize, + || Ok(bn_to_field(&entry.encode())), )?; - let enable_cell = ctx.region.assign_advice( - || "jtable enable", - self.config.data, - ctx.offset, - || Ok(F::from(entry.enable as u64)), + ctx.region.assign_advice( + || "frame table: rest call ops", + self.config.value, + ctx.offset + FrameTableValueOffset::CallOps as usize, + || Ok(F::from(*rest_call_ops as u64)), )?; - ctx.next(); ctx.region.assign_advice( - || "jtable rest", - self.config.data, - ctx.offset, - || Ok(bn_to_field(rest_jops)), + || "frame table: rest return ops", + self.config.value, + ctx.offset + FrameTableValueOffset::ReturnOps as usize, + || Ok(F::from(*rest_return_ops as u64)), )?; - ctx.next(); - let entry_cell = ctx.region.assign_advice( - || "jtable entry", - self.config.data, - ctx.offset, - || Ok(bn_to_field(&entry.encode())), - )?; - ctx.next(); + if let Some(entry) = entry.0.as_ref() { + ctx.region.assign_advice( + || "frame table: enable", + self.config.value, + ctx.offset + FrameTableValueOffset::Enable as usize, + || Ok(F::one()), + )?; - cells.push((enable_cell, entry_cell)); + if entry.returned { + ctx.region.assign_advice( + || "frame table: returned", + self.config.value, + ctx.offset + FrameTableValueOffset::Returned as usize, + || Ok(F::one()), + )?; - if entry.enable { - *rest_jops -= encode_jops(1, 0); + *rest_return_ops -= 1; + } } + + cells.push(entry_cell); + + ctx.step(FrameTableValueOffset::Max as usize); } Ok(cells.try_into().expect(&format!( - "The number of static frame entries should be {}", - STATIC_FRAME_ENTRY_NUMBER + "The number of inherited frame entries should be {}", + INHERITED_FRAME_TABLE_ENTRIES ))) } - fn assign_jtable_entries( + fn assign_frame_table_entries( &self, ctx: &mut Context<'_, F>, - rest_jops: &mut BigUint, - jtable: &JumpTable, + rest_call_ops: &mut u32, + rest_return_ops: &mut u32, + frame_table: &CalledFrameTable, ) -> Result<(), Error> { - for entry in jtable.entries().iter() { - let rest_f = bn_to_field(rest_jops); - let entry_f = bn_to_field(&entry.encode()); - + for entry in frame_table.iter() { ctx.region.assign_advice( - || "jtable enable", - self.config.data, - ctx.offset, + || "frame table: enable", + self.config.value, + ctx.offset + FrameTableValueOffset::Enable as usize, || Ok(F::one()), )?; - ctx.next(); ctx.region.assign_advice( - || "jtable rest", - self.config.data, - ctx.offset, - || Ok(rest_f), + || "frame table: encode", + self.config.value, + ctx.offset + FrameTableValueOffset::Encode as usize, + || Ok(bn_to_field(&entry.encode())), )?; - ctx.next(); ctx.region.assign_advice( - || "jtable entry", - self.config.data, - ctx.offset, - || Ok(entry_f), + || "frame table: rest call ops", + self.config.value, + ctx.offset + FrameTableValueOffset::CallOps as usize, + || Ok(F::from(*rest_call_ops as u64)), )?; - ctx.next(); - - *rest_jops -= encode_jops(1, 1); - } - { ctx.region.assign_advice( - || "jtable enable", - self.config.data, - ctx.offset, - || Ok(F::zero()), + || "frame table: entry", + self.config.value, + ctx.offset + FrameTableValueOffset::ReturnOps as usize, + || Ok(F::from(*rest_return_ops as u64)), )?; - ctx.next(); - ctx.region.assign_advice( - || "jtable rest", - self.config.data, - ctx.offset, - || Ok(F::zero()), - )?; - ctx.next(); + if entry.0.returned { + ctx.region.assign_advice( + || "frame table: returned", + self.config.value, + ctx.offset + FrameTableValueOffset::Returned as usize, + || Ok(F::one()), + )?; - ctx.region.assign_advice( - || "jtable entry", - self.config.data, - ctx.offset, - || Ok(F::zero()), - )?; - ctx.next(); + *rest_return_ops -= 1 as u32; + } + + *rest_call_ops -= 1; + + ctx.step(FrameTableValueOffset::Max as usize); } Ok(()) } - pub fn assign( + fn compute_call_ops(&self, frame_table: &FrameTableSlice) -> u32 { + frame_table.called.len() as u32 + } + + fn compute_returned_ops(&self, frame_table: &FrameTableSlice) -> u32 { + frame_table + .inherited + .iter() + .filter(|e| e.0.as_ref().map_or(false, |entry| entry.returned)) + .count() as u32 + + frame_table.called.iter().filter(|e| e.0.returned).count() as u32 + } + + pub(crate) fn assign( &self, layouter: impl Layouter, - static_entries: &[StaticFrameEntry; STATIC_FRAME_ENTRY_NUMBER], - jtable: &JumpTable, + frame_table: &FrameTableSlice, ) -> Result< ( - AssignedCell, - [(AssignedCell, AssignedCell); STATIC_FRAME_ENTRY_NUMBER], + FrameEtablePermutationCells, + Box<[AssignedCell; INHERITED_FRAME_TABLE_ENTRIES]>, ), Error, > { @@ -195,22 +281,26 @@ impl JumpTableChip { self.init(&mut ctx)?; ctx.reset(); - // non-static entry includes `call`` and `return`` op, static entry only includes `return` op - let mut rest_jops = encode_jops( - jtable.entries().len() as u32 - + static_entries.iter().filter(|entry| entry.enable).count() as u32, - jtable.entries().len() as u32, - ); + let mut rest_call_ops = self.compute_call_ops(frame_table); + let mut rest_return_ops = self.compute_returned_ops(frame_table); + + let frame_etable_permutation_cells = + self.assign_first_rest_jops(&mut ctx, rest_call_ops, rest_return_ops)?; + let inherited_cells = self.assign_inherited_entries_and_first_rest_jops( + &mut ctx, + &mut rest_call_ops, + &mut rest_return_ops, + &frame_table.inherited, + )?; - let rest_jopss = self.assign_first_rest_jops(&mut ctx, rest_jops.clone())?; - let cells_to_permutation = self.assign_static_entries_and_first_rest_jops( + self.assign_frame_table_entries( &mut ctx, - &mut rest_jops, - static_entries, + &mut rest_call_ops, + &mut rest_return_ops, + &frame_table.called, )?; - self.assign_jtable_entries(&mut ctx, &mut rest_jops, jtable)?; - Ok((rest_jopss, cells_to_permutation)) + Ok((frame_etable_permutation_cells, inherited_cells)) }, ) } diff --git a/crates/zkwasm/src/circuits/jtable/configure.rs b/crates/zkwasm/src/circuits/jtable/configure.rs index 7c71737be..89ed1e882 100644 --- a/crates/zkwasm/src/circuits/jtable/configure.rs +++ b/crates/zkwasm/src/circuits/jtable/configure.rs @@ -1,115 +1,118 @@ use super::JumpTableConfig; -use crate::circuits::jtable::encode_jops; -use crate::circuits::utils::bn_to_field; -use crate::circuits::Lookup; use crate::constant_from; -use crate::constant_from_bn; -use crate::fixed_curr; use halo2_proofs::arithmetic::FieldExt; -use halo2_proofs::plonk::Advice; -use halo2_proofs::plonk::Column; use halo2_proofs::plonk::ConstraintSystem; use halo2_proofs::plonk::Expression; use halo2_proofs::plonk::VirtualCells; pub trait JTableConstraint { - fn configure(&self, meta: &mut ConstraintSystem) { - self.enable_is_bit(meta); - self.enable_rest_jops_permutation(meta); + fn configure(&self, meta: &mut ConstraintSystem, is_last_slice: bool) { + self.enable_returned_are_bit(meta); + self.enable_permutation(meta); self.configure_rest_jops_decrease(meta); - self.disabled_block_should_be_end(meta); + self.disabled_block_should_be_end(meta, is_last_slice); self.disabled_block_has_no_entry_value(meta); } - fn enable_rest_jops_permutation(&self, meta: &mut ConstraintSystem); - fn enable_is_bit(&self, meta: &mut ConstraintSystem); + fn enable_permutation(&self, meta: &mut ConstraintSystem); + fn enable_returned_are_bit(&self, meta: &mut ConstraintSystem); fn configure_rest_jops_decrease(&self, meta: &mut ConstraintSystem); - fn disabled_block_should_be_end(&self, meta: &mut ConstraintSystem); + fn disabled_block_should_be_end(&self, meta: &mut ConstraintSystem, is_last_slice: bool); fn disabled_block_has_no_entry_value(&self, meta: &mut ConstraintSystem); } impl JTableConstraint for JumpTableConfig { - fn enable_rest_jops_permutation(&self, meta: &mut ConstraintSystem) { - meta.enable_equality(self.data); + fn enable_permutation(&self, meta: &mut ConstraintSystem) { + meta.enable_equality(self.value); } - fn enable_is_bit(&self, meta: &mut ConstraintSystem) { - meta.create_gate("enable is bit", |meta| { + fn enable_returned_are_bit(&self, meta: &mut ConstraintSystem) { + meta.create_gate("enable and returned are bit", |meta| { vec![ - self.enable(meta) - * (self.enable(meta) - constant_from!(1)) - * fixed_curr!(meta, self.sel), + self.enable(meta) * (self.enable(meta) - constant_from!(1)) * self.sel(meta), + self.returned(meta) * (self.returned(meta) - constant_from!(1)) * self.sel(meta), ] }); } fn configure_rest_jops_decrease(&self, meta: &mut ConstraintSystem) { + /* + * Why we do not need `enable == 1 -> encode != 0`. + * If enable == 1 but encode == 0, it means the number of ops may greater than the number of encoding. However + * - If the number of ops is not correct, the equality between etable and frame table will fail. + * - If the number of ops is correct, encode == 0 implies an entry is missing and etable cannot + * lookup the correct entry in frame table. + */ meta.create_gate("c3. jtable rest decrease", |meta| { vec![ - (self.rest(meta) - self.next_rest(meta) - constant_from_bn!(&encode_jops(1, 1)) - + self.static_bit(meta)) - * self.enable(meta) - * fixed_curr!(meta, self.sel), - (self.rest(meta) - self.next_rest(meta)) - * (self.enable(meta) - constant_from!(1)) - * fixed_curr!(meta, self.sel), + (self.rest_return_ops(meta) + - self.next_rest_return_ops(meta) + - self.returned(meta) * self.enable(meta)) + * self.sel(meta), + (self.rest_call_ops(meta) - self.next_rest_call_ops(meta) - self.enable(meta) + + self.inherited_bit(meta) * self.enable(meta)) + * self.sel(meta), ] }); } - fn disabled_block_should_be_end(&self, meta: &mut ConstraintSystem) { + fn disabled_block_should_be_end(&self, meta: &mut ConstraintSystem, is_last_slice: bool) { meta.create_gate("c5. jtable ends up", |meta| { vec![ (constant_from!(1) - self.enable(meta)) - * (constant_from!(1) - self.static_bit(meta)) - * self.rest(meta) - * fixed_curr!(meta, self.sel), + * (constant_from!(1) - self.inherited_bit(meta)) + * self.rest_call_ops(meta) + * self.sel(meta), + (constant_from!(1) - self.enable(meta)) + * (constant_from!(1) - self.inherited_bit(meta)) + * self.rest_return_ops(meta) + * self.sel(meta), ] }); + + if is_last_slice { + meta.create_gate("c5. jtable ends up", |meta| { + vec![(constant_from!(1) - self.returned(meta)) * self.enable(meta) * self.sel(meta)] + }); + } } fn disabled_block_has_no_entry_value(&self, meta: &mut ConstraintSystem) { meta.create_gate("c6. jtable entry is zero on disabled", |meta| { vec![ - (constant_from!(1) - self.enable(meta)) - * self.entry(meta) - * fixed_curr!(meta, self.sel), + (constant_from!(1) - self.enable(meta)) * self.encode(meta) * self.sel(meta), + (constant_from!(1) - self.enable(meta)) * self.returned(meta) * self.sel(meta), ] }); } } -impl Lookup for JumpTableConfig { +impl JumpTableConfig { /// Frame Table Constraint 4. Etable step's call/return record can be found on jtable_entry - fn configure_in_table( + pub(in crate::circuits) fn configure_in_event_table( &self, meta: &mut ConstraintSystem, key: &'static str, - expr: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression, + expr: impl FnOnce(&mut VirtualCells<'_, F>) -> (Expression, Expression, Expression), ) { meta.lookup_any(key, |meta| { - vec![(expr(meta), self.entry(meta) * fixed_curr!(meta, self.sel))] - }); - } + let (sel, is_returned_or_call, encode) = expr(meta); - fn encode(&self, _meta: &mut VirtualCells<'_, F>) -> Expression { - unimplemented!() + vec![ + (sel, self.sel(meta)), + (is_returned_or_call, self.returned(meta)), + (encode, self.encode(meta)), + ] + }); } } impl JumpTableConfig { - pub(super) fn new( - meta: &mut ConstraintSystem, - cols: &mut impl Iterator>, - ) -> Self { - let sel = meta.fixed_column(); - let static_bit = meta.fixed_column(); - let data = cols.next().unwrap(); - + pub(super) fn new(meta: &mut ConstraintSystem) -> Self { JumpTableConfig { - sel, - static_bit, - data, + sel: meta.fixed_column(), + inherited: meta.fixed_column(), + value: meta.advice_column(), _m: std::marker::PhantomData, } } diff --git a/crates/zkwasm/src/circuits/jtable/expression.rs b/crates/zkwasm/src/circuits/jtable/expression.rs index 42565ea3b..5ef45fac6 100644 --- a/crates/zkwasm/src/circuits/jtable/expression.rs +++ b/crates/zkwasm/src/circuits/jtable/expression.rs @@ -1,5 +1,5 @@ -use super::JtableOffset; use super::JumpTableConfig; +use crate::circuits::jtable::FrameTableValueOffset; use crate::fixed_curr; use crate::nextn; use halo2_proofs::arithmetic::FieldExt; @@ -9,27 +9,47 @@ use specs::encode::frame_table::encode_frame_table_entry; impl JumpTableConfig { pub(super) fn enable(&self, meta: &mut VirtualCells) -> Expression { - nextn!(meta, self.data, JtableOffset::JtableOffsetEnable as i32) + nextn!(meta, self.value, FrameTableValueOffset::Enable as i32) } - pub(super) fn rest(&self, meta: &mut VirtualCells) -> Expression { - nextn!(meta, self.data, JtableOffset::JtableOffsetRest as i32) + pub(super) fn rest_call_ops(&self, meta: &mut VirtualCells) -> Expression { + nextn!(meta, self.value, FrameTableValueOffset::CallOps as i32) } - pub(super) fn next_rest(&self, meta: &mut VirtualCells) -> Expression { + pub(super) fn next_rest_call_ops(&self, meta: &mut VirtualCells) -> Expression { nextn!( meta, - self.data, - JtableOffset::JtableOffsetRest as i32 + JtableOffset::JtableOffsetMax as i32 + self.value, + FrameTableValueOffset::CallOps as i32 + FrameTableValueOffset::Max as i32 ) } - pub(super) fn entry(&self, meta: &mut VirtualCells) -> Expression { - nextn!(meta, self.data, JtableOffset::JtableOffsetEntry as i32) + pub(super) fn rest_return_ops(&self, meta: &mut VirtualCells) -> Expression { + nextn!(meta, self.value, FrameTableValueOffset::ReturnOps as i32) } - pub(super) fn static_bit(&self, meta: &mut VirtualCells) -> Expression { - fixed_curr!(meta, self.static_bit) + pub(super) fn next_rest_return_ops(&self, meta: &mut VirtualCells) -> Expression { + nextn!( + meta, + self.value, + FrameTableValueOffset::ReturnOps as i32 + FrameTableValueOffset::Max as i32 + ) + } + + pub(super) fn returned(&self, meta: &mut VirtualCells) -> Expression { + nextn!(meta, self.value, FrameTableValueOffset::Returned as i32) + } + + pub(super) fn encode(&self, meta: &mut VirtualCells) -> Expression { + nextn!(meta, self.value, FrameTableValueOffset::Encode as i32) + } + + pub(super) fn inherited_bit(&self, meta: &mut VirtualCells) -> Expression { + fixed_curr!(meta, self.inherited) + } + + pub(super) fn sel(&self, meta: &mut VirtualCells) -> Expression { + fixed_curr!(meta, self.sel) } } diff --git a/crates/zkwasm/src/circuits/jtable/mod.rs b/crates/zkwasm/src/circuits/jtable/mod.rs index b5041f5c3..9b121096d 100644 --- a/crates/zkwasm/src/circuits/jtable/mod.rs +++ b/crates/zkwasm/src/circuits/jtable/mod.rs @@ -1,50 +1,38 @@ use self::configure::JTableConstraint; use halo2_proofs::arithmetic::FieldExt; +use halo2_proofs::circuit::AssignedCell; use halo2_proofs::plonk::Advice; use halo2_proofs::plonk::Column; use halo2_proofs::plonk::ConstraintSystem; use halo2_proofs::plonk::Fixed; -use num_bigint::BigUint; -use num_bigint::ToBigUint; -use specs::jtable::STATIC_FRAME_ENTRY_NUMBER; use std::marker::PhantomData; mod assign; mod configure; pub(crate) mod expression; -// enable and data should be encoded in image table -pub(crate) const STATIC_FRAME_ENTRY_IMAGE_TABLE_ENTRY: usize = STATIC_FRAME_ENTRY_NUMBER * 2; - -// high 128 bit counts 'return' instructions, low 128 bit counts 'call' instructions. -pub(crate) const JOPS_SEPARATE: usize = 128; -pub fn encode_jops(return_instructions: u32, call_instructions: u32) -> BigUint { - return_instructions.to_biguint().unwrap() << JOPS_SEPARATE - | call_instructions.to_biguint().unwrap() -} - -pub enum JtableOffset { - JtableOffsetEnable = 0, - JtableOffsetRest = 1, - JtableOffsetEntry = 2, - JtableOffsetMax = 3, +#[repr(i32)] +pub(self) enum FrameTableValueOffset { + Enable = 0, + Returned = 1, + Encode = 2, + CallOps = 3, + ReturnOps = 4, + Max = 5, } #[derive(Clone)] pub struct JumpTableConfig { sel: Column, - static_bit: Column, - data: Column, + inherited: Column, + value: Column, _m: PhantomData, } impl JumpTableConfig { - pub fn configure( - meta: &mut ConstraintSystem, - cols: &mut impl Iterator>, - ) -> Self { - let jtable = Self::new(meta, cols); - jtable.configure(meta); + pub fn configure(meta: &mut ConstraintSystem, is_last_slice: bool) -> Self { + let jtable = Self::new(meta); + jtable.configure(meta, is_last_slice); jtable } } @@ -62,3 +50,9 @@ impl JumpTableChip { } } } + +#[derive(Debug)] +pub(crate) struct FrameEtablePermutationCells { + pub(crate) rest_call_ops: AssignedCell, + pub(crate) rest_return_ops: AssignedCell, +} diff --git a/crates/zkwasm/src/circuits/mod.rs b/crates/zkwasm/src/circuits/mod.rs index b3ac6162b..fc836a827 100644 --- a/crates/zkwasm/src/circuits/mod.rs +++ b/crates/zkwasm/src/circuits/mod.rs @@ -44,13 +44,61 @@ pub(crate) fn compute_slice_capability(k: u32) -> u32 { ((1 << k) - RESERVE_ROWS as u32 - 1024) / EVENT_TABLE_ENTRY_ROWS as u32 } -pub struct ZkWasmCircuit { +pub struct OngoingCircuit { pub k: u32, pub slice: Slice, _data: PhantomData, } -impl ZkWasmCircuit { +impl OngoingCircuit { + pub fn new(k: u32, slice: Slice) -> Result { + { + // entries is empty when called by without_witness + let allocated_memory_pages = slice + .etable + .entries() + .last() + .map(|entry| entry.allocated_memory_pages); + let maximal_pages = compute_maximal_pages(k); + if let Some(allocated_memory_pages) = allocated_memory_pages { + if allocated_memory_pages > maximal_pages { + return Err(BuildingCircuitError::PagesExceedLimit( + allocated_memory_pages, + maximal_pages, + k, + )); + } + } + } + + { + let etable_entires = slice.etable.entries().len() as u32; + let etable_capacity = compute_slice_capability(k); + + if etable_entires > etable_capacity { + return Err(BuildingCircuitError::EtableEntriesExceedLimit( + etable_entires as u32, + etable_capacity as u32, + k, + )); + } + } + + Ok(OngoingCircuit { + k, + slice, + _data: PhantomData, + }) + } +} + +pub struct LastSliceCircuit { + pub k: u32, + pub slice: Slice, + _data: PhantomData, +} + +impl LastSliceCircuit { pub fn new(k: u32, slice: Slice) -> Result { { // entries is empty when called by without_witness @@ -84,16 +132,40 @@ impl ZkWasmCircuit { } } - Ok(ZkWasmCircuit { + Ok(LastSliceCircuit { k, slice, _data: PhantomData, }) } +} +pub enum ZkWasmCircuit { + Ongoing(OngoingCircuit), + LastSliceCircuit(LastSliceCircuit), +} + +impl ZkWasmCircuit { + pub fn new(k: u32, slice: Slice) -> Result { + if slice.is_last_slice { + Ok(ZkWasmCircuit::LastSliceCircuit(LastSliceCircuit::new( + k, slice, + )?)) + } else { + Ok(ZkWasmCircuit::Ongoing(OngoingCircuit::new(k, slice)?)) + } + } pub fn mock_test(&self, instances: Vec) -> anyhow::Result<()> { - let prover = MockProver::run(self.k, self, vec![instances])?; - assert_eq!(prover.verify(), Ok(())); + match self { + ZkWasmCircuit::Ongoing(circuit) => { + let prover = MockProver::run(circuit.k, circuit, vec![instances])?; + assert_eq!(prover.verify(), Ok(())); + } + ZkWasmCircuit::LastSliceCircuit(circuit) => { + let prover = MockProver::run(circuit.k, circuit, vec![instances])?; + assert_eq!(prover.verify(), Ok(())); + } + } Ok(()) } diff --git a/crates/zkwasm/src/circuits/post_image_table/continuation.rs b/crates/zkwasm/src/circuits/post_image_table/continuation.rs index efdff1e80..f60286fc4 100644 --- a/crates/zkwasm/src/circuits/post_image_table/continuation.rs +++ b/crates/zkwasm/src/circuits/post_image_table/continuation.rs @@ -16,10 +16,10 @@ use halo2_proofs::plonk::Fixed; use num_bigint::BigUint; use specs::encode::init_memory_table::encode_init_memory_table_address; use specs::encode::init_memory_table::MEMORY_ADDRESS_OFFSET; -use specs::jtable::STATIC_FRAME_ENTRY_NUMBER; use specs::mtable::LocationType; use crate::circuits::image_table::ImageTableConfig; +use crate::circuits::jtable::JumpTableConfig; use crate::circuits::mtable::MemoryTableConfig; use crate::circuits::utils::bn_to_field; use crate::circuits::utils::image_table::image_table_offset_to_memory_location; @@ -88,6 +88,7 @@ macro_rules! assign { #[derive(Clone)] pub(in crate::circuits) struct PostImageTableConfig { memory_addr_sel: Column, + inherited_frame_table_sel: Column, post_image_table: Column, update: Column, rest_memory_finalized_count: Column, @@ -100,6 +101,7 @@ impl PostImageTableConfig { meta: &mut ConstraintSystem, memory_addr_sel: Option>, memory_table: &MemoryTableConfig, + frame_table: &JumpTableConfig, pre_image_table: &ImageTableConfig, ) -> Self { let memory_addr_sel = memory_addr_sel.unwrap(); @@ -107,6 +109,7 @@ impl PostImageTableConfig { let rest_memory_finalized_count = meta.advice_column(); let post_image_table = meta.named_advice_column(POST_IMAGE_TABLE.to_owned()); let memory_finalized_lookup_encode = meta.advice_column(); + let inherited_frame_table_sel = meta.fixed_column(); meta.enable_equality(rest_memory_finalized_count); meta.enable_equality(post_image_table); @@ -145,8 +148,21 @@ impl PostImageTableConfig { |meta| curr!(meta, memory_finalized_lookup_encode), ); + frame_table.configure_in_event_table( + meta, + "post image table: extract unreturned frame table entries", + |meta| { + ( + fixed_curr!(meta, inherited_frame_table_sel), + constant_from!(0), + curr!(meta, post_image_table) * fixed_curr!(meta, inherited_frame_table_sel), + ) + }, + ); + Self { memory_addr_sel, + inherited_frame_table_sel, post_image_table, update, rest_memory_finalized_count, @@ -183,32 +199,34 @@ impl PostImageTableChip { let initialization_state_handler = |base_offset| { ctx.borrow_mut().offset = base_offset; - let assign_handler = - |field: &F| Ok(assign!(ctx, self.config.post_image_table, *field)?); - let initialization_state = post_image_table .initialization_state - .map(assign_handler, assign_handler); + .map(|field: &F| Ok(assign!(ctx, self.config.post_image_table, *field)?)); initialization_state.transpose() }; - let static_frame_entries_handler = |base_offset| { + let inherited_frame_entries_handler = |base_offset| { ctx.borrow_mut().offset = base_offset; + let mut offset = base_offset; let mut cells = vec![]; - for (enable, entry) in &post_image_table.static_frame_entries { - let enable = assign!(ctx, self.config.post_image_table, *enable)?; + for entry in post_image_table.inherited_frame_entries.iter() { + region.assign_fixed( + || "post image table: inherited frame table", + self.config.inherited_frame_table_sel, + offset, + || Ok(F::one()), + )?; + let entry = assign!(ctx, self.config.post_image_table, *entry)?; - cells.push((enable, entry)); + cells.push(entry); + offset += 1; } - Ok(cells.try_into().expect(&format!( - "The number of static frame entries should be {}", - STATIC_FRAME_ENTRY_NUMBER - ))) + Ok(cells.try_into().unwrap()) }; let instruction_handler = |base_offset| { @@ -373,7 +391,7 @@ impl PostImageTableChip { let layouter = image_table_assigner.exec( initialization_state_handler, - static_frame_entries_handler, + inherited_frame_entries_handler, instruction_handler, br_table_handler, padding_handler, diff --git a/crates/zkwasm/src/circuits/post_image_table/trivial.rs b/crates/zkwasm/src/circuits/post_image_table/trivial.rs index 26304fcc7..5ee111664 100644 --- a/crates/zkwasm/src/circuits/post_image_table/trivial.rs +++ b/crates/zkwasm/src/circuits/post_image_table/trivial.rs @@ -15,6 +15,8 @@ use crate::circuits::mtable::MemoryTableConfig; use crate::circuits::utils::image_table::ImageTableAssigner; use crate::circuits::utils::image_table::ImageTableLayouter; +use super::jtable::JumpTableConfig; + #[derive(Clone)] pub(in crate::circuits) struct PostImageTableConfig { _mark: PhantomData, @@ -25,6 +27,7 @@ impl PostImageTableConfig { _meta: &mut ConstraintSystem, _memory_addr_sel: Option>, _memory_table: &MemoryTableConfig, + _frame_table: &JumpTableConfig, _pre_image_table: &ImageTableConfig, ) -> Self { Self { _mark: PhantomData } diff --git a/crates/zkwasm/src/circuits/utils/image_table.rs b/crates/zkwasm/src/circuits/utils/image_table.rs index f071909e8..5ee7eaa07 100644 --- a/crates/zkwasm/src/circuits/utils/image_table.rs +++ b/crates/zkwasm/src/circuits/utils/image_table.rs @@ -7,8 +7,8 @@ use specs::encode::image_table::ImageTableEncoder; use specs::imtable::InitMemoryTable; use specs::imtable::InitMemoryTableEntry; use specs::itable::InstructionTable; -use specs::jtable::StaticFrameEntry; -use specs::jtable::STATIC_FRAME_ENTRY_NUMBER; +use specs::jtable::InheritedFrameTable; +use specs::jtable::INHERITED_FRAME_TABLE_ENTRIES; use specs::mtable::LocationType; use specs::mtable::VarType; use specs::slice::Slice; @@ -17,7 +17,6 @@ use wasmi::DEFAULT_VALUE_STACK_LIMIT; use crate::circuits::image_table::compute_maximal_pages; use crate::circuits::image_table::PAGE_ENTRIES; -use crate::circuits::jtable::STATIC_FRAME_ENTRY_IMAGE_TABLE_ENTRY; use crate::circuits::utils::bn_to_field; pub const STACK_CAPABILITY: usize = DEFAULT_VALUE_STACK_LIMIT; @@ -83,8 +82,8 @@ pub fn image_table_offset_to_memory_location(offset: usize) -> (LocationType, u3 */ #[derive(Debug)] pub struct ImageTableLayouter { - pub(crate) initialization_state: InitializationState, - pub(crate) static_frame_entries: [(T, T); STATIC_FRAME_ENTRY_NUMBER], + pub(crate) initialization_state: InitializationState, + pub(crate) inherited_frame_entries: Box<[T; INHERITED_FRAME_TABLE_ENTRIES]>, pub(crate) instructions: Vec, pub(crate) br_table_entires: Vec, // NOTE: unused instructions and br_table entries. @@ -97,7 +96,7 @@ pub struct ImageTableAssigner { pub heap_capability: u32, initialization_state_offset: usize, - static_frame_entries_offset: usize, + inherited_frame_entries_offset: usize, instruction_offset: usize, br_table_offset: usize, padding_offset: usize, @@ -109,9 +108,9 @@ impl ImageTableAssigner { /// are compacted within a fixed range. `page_capability` is computed based on K. pub fn new(instruction_number: usize, br_table_number: usize, pages_capability: u32) -> Self { let initialization_state_offset = 0; - let static_frame_entries_offset = - initialization_state_offset + InitializationState::::field_count(); - let instruction_offset = static_frame_entries_offset + STATIC_FRAME_ENTRY_IMAGE_TABLE_ENTRY; + let inherited_frame_entries_offset = + initialization_state_offset + InitializationState::::field_count(); + let instruction_offset = inherited_frame_entries_offset + INHERITED_FRAME_TABLE_ENTRIES; let br_table_offset = instruction_offset + instruction_number; let padding_offset = br_table_offset + br_table_number; let init_memory_offset = INIT_MEMORY_ENTRIES_OFFSET; @@ -126,7 +125,7 @@ impl ImageTableAssigner { heap_capability: pages_capability * PAGE_ENTRIES, initialization_state_offset, - static_frame_entries_offset, + inherited_frame_entries_offset, instruction_offset, br_table_offset, padding_offset, @@ -136,21 +135,21 @@ impl ImageTableAssigner { pub fn exec_initialization_state( &self, - mut initialization_state_handler: impl FnMut(usize) -> Result, Error>, - ) -> Result, Error> { + mut initialization_state_handler: impl FnMut(usize) -> Result, Error>, + ) -> Result, Error> { initialization_state_handler(self.initialization_state_offset) } - pub fn exec_static_frame_entries( + pub fn exec_inherited_frame_entries( &self, - mut static_frame_entries_handler: impl FnMut( + mut inherited_frame_entries_handler: impl FnMut( usize, ) -> Result< - [(T, T); STATIC_FRAME_ENTRY_NUMBER], + Box<[T; INHERITED_FRAME_TABLE_ENTRIES]>, Error, >, - ) -> Result<[(T, T); STATIC_FRAME_ENTRY_NUMBER], Error> { - static_frame_entries_handler(self.static_frame_entries_offset) + ) -> Result, Error> { + inherited_frame_entries_handler(self.inherited_frame_entries_offset) } pub fn exec_instruction( @@ -183,26 +182,28 @@ impl ImageTableAssigner { pub fn exec( &self, - initialization_state_handler: impl FnMut(usize) -> Result, Error>, - static_frame_entries_handler: impl FnMut( + initialization_state_handler: impl FnMut(usize) -> Result, Error>, + inherited_frame_entries_handler: impl FnMut( usize, - ) - -> Result<[(T, T); STATIC_FRAME_ENTRY_NUMBER], Error>, + ) -> Result< + Box<[T; INHERITED_FRAME_TABLE_ENTRIES]>, + Error, + >, instruction_handler: impl FnMut(usize) -> Result, Error>, br_table_handler: impl FnMut(usize) -> Result, Error>, padding_handler: impl FnMut(usize, usize) -> Result, Error>, init_memory_entries_handler: impl FnMut(usize) -> Result, Error>, ) -> Result, Error> { let initialization_state = self.exec_initialization_state(initialization_state_handler)?; - let static_frame_entries = self.exec_static_frame_entries(static_frame_entries_handler)?; + let inherited_frame_entries = + self.exec_inherited_frame_entries(inherited_frame_entries_handler)?; let instructions = self.exec_instruction(instruction_handler)?; let br_table_entires = self.exec_br_table_entires(br_table_handler)?; let padding_entires = self.exec_padding_entires(padding_handler)?; let init_memory_entries = self.exec_init_memory_entries(init_memory_entries_handler)?; - Ok(ImageTableLayouter { initialization_state, - static_frame_entries, + inherited_frame_entries, instructions, br_table_entires, padding_entires, @@ -216,26 +217,22 @@ pub(crate) fn encode_compilation_table_values( itable: &InstructionTable, br_table: &BrTable, elem_table: &ElemTable, - static_frame_entries: &[StaticFrameEntry; STATIC_FRAME_ENTRY_NUMBER], - initialization_state: &InitializationState, + inherited_frame_table: &InheritedFrameTable, + initialization_state: &InitializationState, init_memory_table: &InitMemoryTable, ) -> ImageTableLayouter { let page_capability = compute_maximal_pages(k); - let initialization_state_handler = - |_| Ok(initialization_state.map(|v| F::from((*v) as u64), |v| bn_to_field(v))); + let initialization_state_handler = |_| Ok(initialization_state.map(|v| F::from((*v) as u64))); - let static_frame_entries_handler = |_| { - let mut cells = vec![]; + let inherited_frame_entries_handler = |_| { + let mut cells = Box::new([F::zero(); INHERITED_FRAME_TABLE_ENTRIES]); - for entry in static_frame_entries.as_ref() { - cells.push((F::from(entry.enable as u64), bn_to_field(&entry.encode()))); + for (index, entry) in inherited_frame_table.iter().enumerate() { + cells[index] = bn_to_field(&entry.encode()); } - Ok(cells.try_into().expect(&format!( - "The number of static frame entries should be {}", - STATIC_FRAME_ENTRY_NUMBER - ))) + Ok(cells) }; let instruction_handler = |_| { @@ -322,7 +319,7 @@ pub(crate) fn encode_compilation_table_values( let layouter = assigner .exec::<_, Error>( initialization_state_handler, - static_frame_entries_handler, + inherited_frame_entries_handler, instruction_handler, br_table_handler, padding_handler, @@ -346,7 +343,7 @@ impl EncodeImageTable for Slice { &self.itable, &self.br_table, &self.elem_table, - &self.static_jtable, + &self.frame_table.inherited, &self.initialization_state, &self.imtable, ) @@ -358,7 +355,7 @@ impl EncodeImageTable for Slice { &self.itable, &self.br_table, &self.elem_table, - &self.static_jtable, + &self.post_inherited_frame_table, &self.post_initialization_state, &self.post_imtable, ) @@ -370,14 +367,7 @@ impl ImageTableLayouter { let mut buf = vec![]; buf.append(&mut self.initialization_state.plain()); - buf.append( - &mut self - .static_frame_entries - .map(|(enable, fid)| vec![enable, fid]) - .into_iter() - .collect::>>() - .concat(), - ); + buf.append(&mut self.inherited_frame_entries.to_vec()); buf.append(&mut self.instructions.clone()); buf.append(&mut self.br_table_entires.clone()); buf.append(&mut self.padding_entires.clone()); diff --git a/crates/zkwasm/src/circuits/utils/step_status.rs b/crates/zkwasm/src/circuits/utils/step_status.rs index b1f13b007..04bb1ebef 100644 --- a/crates/zkwasm/src/circuits/utils/step_status.rs +++ b/crates/zkwasm/src/circuits/utils/step_status.rs @@ -1,4 +1,5 @@ -use num_bigint::BigUint; +use std::collections::HashMap; + use specs::configure_table::ConfigureTable; use specs::itable::InstructionTable; @@ -12,7 +13,8 @@ pub struct Status<'a> { pub allocated_memory_pages: u32, pub rest_mops: u32, - pub jops: BigUint, + pub rest_call_ops: u32, + pub rest_return_ops: u32, pub host_public_inputs: u32, pub context_in_index: u32, @@ -22,8 +24,9 @@ pub struct Status<'a> { pub itable: &'a InstructionTable, } -pub struct StepStatus<'a, 'b> { +pub struct StepStatus<'a, 'b, 'c> { pub current: &'a Status<'b>, pub next: &'a Status<'b>, pub configure_table: &'b ConfigureTable, + pub frame_table_returned_lookup: &'c HashMap<(u32, u32), bool>, } diff --git a/crates/zkwasm/src/circuits/zkwasm_circuit/mod.rs b/crates/zkwasm/src/circuits/zkwasm_circuit/mod.rs index 66434cb39..41b0e2079 100644 --- a/crates/zkwasm/src/circuits/zkwasm_circuit/mod.rs +++ b/crates/zkwasm/src/circuits/zkwasm_circuit/mod.rs @@ -16,8 +16,9 @@ use halo2_proofs::plonk::Fixed; use log::debug; use log::info; use specs::etable::EventTable; -use specs::jtable::JumpTable; -use specs::jtable::STATIC_FRAME_ENTRY_NUMBER; +use specs::jtable::CalledFrameTable; +use specs::jtable::INHERITED_FRAME_TABLE_ENTRIES; +use specs::slice::FrameTableSlice; use specs::slice::Slice; use crate::circuits::bit_table::BitTableChip; @@ -42,7 +43,6 @@ use crate::circuits::utils::image_table::ImageTableAssigner; use crate::circuits::utils::image_table::ImageTableLayouter; use crate::circuits::utils::table_entry::EventTableWithMemoryInfo; use crate::circuits::utils::table_entry::MemoryWritingTable; -use crate::circuits::ZkWasmCircuit; use crate::exec_with_profile; use crate::foreign::context::circuits::assign::ContextContHelperTableChip; use crate::foreign::context::circuits::assign::ExtractContextFromTrace; @@ -57,12 +57,15 @@ use crate::runtime::memory_event_of_step; use super::config::zkwasm_k; use super::etable::assign::EventTablePermutationCells; use super::image_table::ImageTableConfig; +use super::jtable::FrameEtablePermutationCells; use super::post_image_table::PostImageTableConfig; +use super::LastSliceCircuit; +use super::OngoingCircuit; pub const VAR_COLUMNS: usize = if cfg!(feature = "continuation") { - 58 + 56 } else { - 50 + 49 }; // Reserve a few rows to keep usable rows away from blind rows. @@ -77,9 +80,9 @@ struct AssignedCells { mtable_rest_mops: Arc>>>, rest_memory_finalize_ops_cell: Arc>>>>, etable_cells: Arc>>>, - rest_jops_cell_in_frame_table: Arc>>>, - static_frame_entry_in_frame_table: - Arc, AssignedCell); STATIC_FRAME_ENTRY_NUMBER]>>>, + rest_ops_cell_in_frame_table: Arc>>>, + inherited_frame_entry_in_frame_table: + Arc; INHERITED_FRAME_TABLE_ENTRIES]>>>>, } #[derive(Clone)] @@ -88,7 +91,7 @@ pub struct ZkWasmCircuitConfig { image_table: ImageTableConfig, post_image_table: PostImageTableConfig, mtable: MemoryTableConfig, - jtable: JumpTableConfig, + frame_table: JumpTableConfig, etable: EventTableConfig, bit_table: BitTableConfig, external_host_call_table: ExternalHostCallTableConfig, @@ -102,475 +105,466 @@ pub struct ZkWasmCircuitConfig { k: u32, } -impl Circuit for ZkWasmCircuit { - type Config = ZkWasmCircuitConfig; - - type FloorPlanner = FlatFloorPlanner; - - fn without_witnesses(&self) -> Self { - ZkWasmCircuit::new( - self.k, - // fill slice like circuit_without_witness - Slice { - itable: self.slice.itable.clone(), - br_table: self.slice.br_table.clone(), - elem_table: self.slice.elem_table.clone(), - configure_table: self.slice.configure_table.clone(), - static_jtable: self.slice.static_jtable.clone(), - - etable: Arc::new(EventTable::default()), - frame_table: Arc::new(JumpTable::default()), - - imtable: self.slice.imtable.clone(), - post_imtable: self.slice.imtable.clone(), - - initialization_state: self.slice.initialization_state.clone(), - post_initialization_state: self.slice.initialization_state.clone(), - - is_last_slice: self.slice.is_last_slice, - }, - ) - .unwrap() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let k = zkwasm_k(); - - /* - * Allocate a column to enable assign_advice_from_constant. - */ - { - let constants = meta.fixed_column(); - meta.enable_constant(constants); - meta.enable_equality(constants); - } +macro_rules! impl_zkwasm_circuit { + ($name:ident, $last_slice:expr) => { + impl Circuit for $name { + type Config = ZkWasmCircuitConfig; + + type FloorPlanner = FlatFloorPlanner; + + fn without_witnesses(&self) -> Self { + $name::new( + self.k, + // fill slice like circuit_without_witness + Slice { + itable: self.slice.itable.clone(), + br_table: self.slice.br_table.clone(), + elem_table: self.slice.elem_table.clone(), + configure_table: self.slice.configure_table.clone(), + initial_frame_table: self.slice.initial_frame_table.clone(), + + etable: Arc::new(EventTable::default()), + frame_table: Arc::new(FrameTableSlice { + inherited: self.slice.initial_frame_table.clone(), + called: CalledFrameTable::default(), + }), + post_inherited_frame_table: self.slice.initial_frame_table.clone(), + + imtable: self.slice.imtable.clone(), + post_imtable: self.slice.imtable.clone(), + + initialization_state: self.slice.initialization_state.clone(), + post_initialization_state: self.slice.initialization_state.clone(), + + is_last_slice: self.slice.is_last_slice, + }, + ) + .unwrap() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let k = zkwasm_k(); + + /* + * Allocate a column to enable assign_advice_from_constant. + */ + { + let constants = meta.fixed_column(); + meta.enable_constant(constants); + meta.enable_equality(constants); + } - let memory_addr_sel = if cfg!(feature = "continuation") { - Some(meta.fixed_column()) - } else { - None - }; - - let foreign_table_from_zero_index = meta.fixed_column(); - - let mut cols = [(); VAR_COLUMNS].map(|_| meta.advice_column()).into_iter(); - - let rtable = RangeTableConfig::configure(meta); - let image_table = ImageTableConfig::configure(meta, memory_addr_sel); - let mtable = MemoryTableConfig::configure(meta, k, &mut cols, &rtable, &image_table); - let post_image_table = - PostImageTableConfig::configure(meta, memory_addr_sel, &mtable, &image_table); - let jtable = JumpTableConfig::configure(meta, &mut cols); - let external_host_call_table = ExternalHostCallTableConfig::configure(meta); - let bit_table = BitTableConfig::configure(meta, &rtable); - - let wasm_input_helper_table = - WasmInputHelperTableConfig::configure(meta, foreign_table_from_zero_index); - let context_helper_table = - ContextContHelperTableConfig::configure(meta, foreign_table_from_zero_index); - - let mut foreign_table_configs: BTreeMap<_, Box<(dyn ForeignTableConfig)>> = - BTreeMap::new(); - foreign_table_configs.insert( - WASM_INPUT_FOREIGN_TABLE_KEY, - Box::new(wasm_input_helper_table.clone()), - ); - foreign_table_configs.insert( - CONTEXT_FOREIGN_TABLE_KEY, - Box::new(context_helper_table.clone()), - ); - - let etable = EventTableConfig::configure( - meta, - k, - &mut cols, - &rtable, - &image_table, - &mtable, - &jtable, - &bit_table, - &external_host_call_table, - &foreign_table_configs, - ); - - assert_eq!(cols.count(), 0); - - let max_available_rows = (1 << k) - (meta.blinding_factors() + 1 + RESERVE_ROWS); - debug!("max_available_rows: {:?}", max_available_rows); - - let circuit_maximal_pages = compute_maximal_pages(k); - info!( - "Circuit K: {} supports up to {} pages.", - k, circuit_maximal_pages - ); - - Self::Config { - rtable, - image_table, - post_image_table, - mtable, - jtable, - etable, - bit_table, - external_host_call_table, - context_helper_table, - foreign_table_from_zero_index, - - max_available_rows, - circuit_maximal_pages, - - k, - } - } - - fn synthesize(&self, config: Self::Config, layouter: impl Layouter) -> Result<(), Error> { - let assign_timer = start_timer!(|| "Assign"); - - let rchip = RangeTableChip::new(config.rtable); - let image_chip = ImageTableChip::new(config.image_table); - let post_image_chip = PostImageTableChip::new(config.post_image_table); - let mchip = MemoryTableChip::new(config.mtable, config.max_available_rows); - let frame_table_chip = JumpTableChip::new(config.jtable, config.max_available_rows); - let echip = EventTableChip::new( - config.etable, - compute_slice_capability(self.k) as usize, - config.max_available_rows, - ); - let bit_chip = BitTableChip::new(config.bit_table, config.max_available_rows); - let external_host_call_chip = - ExternalHostCallChip::new(config.external_host_call_table, config.max_available_rows); - let context_chip = ContextContHelperTableChip::new(config.context_helper_table); - - let image_table_assigner = ImageTableAssigner::new( - // Add one for default lookup value - self.slice.itable.len() + 1, - self.slice.br_table.entries().len() + self.slice.elem_table.entries().len() + 1, - config.circuit_maximal_pages, - ); - - let memory_writing_table: MemoryWritingTable = MemoryWritingTable::from( - config.k, - self.slice.create_memory_table(memory_event_of_step), - ); - - let etable = exec_with_profile!( - || "Prepare memory info for etable", - EventTableWithMemoryInfo::new(&self.slice.etable, &memory_writing_table,) - ); - - let assigned_cells = AssignedCells::default(); - - let layouter_cloned = layouter.clone(); - let assigned_cells_cloned = assigned_cells.clone(); - - rayon::scope(move |s| { - let memory_writing_table = Arc::new(memory_writing_table); - let etable = Arc::new(etable); - - let _layouter = layouter.clone(); - s.spawn(move |_| { - exec_with_profile!( - || "Init range chip", - rchip.init(_layouter, config.k).unwrap() + let memory_addr_sel = if cfg!(feature = "continuation") { + Some(meta.fixed_column()) + } else { + None + }; + + let foreign_table_from_zero_index = meta.fixed_column(); + + let mut cols = [(); VAR_COLUMNS].map(|_| meta.advice_column()).into_iter(); + + let rtable = RangeTableConfig::configure(meta); + let image_table = ImageTableConfig::configure(meta, memory_addr_sel); + let mtable = + MemoryTableConfig::configure(meta, k, &mut cols, &rtable, &image_table); + let frame_table = JumpTableConfig::configure(meta, $last_slice); + let post_image_table = PostImageTableConfig::configure( + meta, + memory_addr_sel, + &mtable, + &frame_table, + &image_table, ); - }); - - let _layouter = layouter.clone(); - s.spawn(move |_| { - exec_with_profile!( - || "Init foreign table index", - _layouter - .assign_region( - || "foreign helper", - |region| { - for offset in 0..foreign_table_enable_lines(config.k) { - region.assign_fixed( - || "foreign table from zero index", - config.foreign_table_from_zero_index, - offset, - || Ok(F::from(offset as u64)), - )?; - } - - Ok(()) - }, - ) - .unwrap() + let external_host_call_table = ExternalHostCallTableConfig::configure(meta); + let bit_table = BitTableConfig::configure(meta, &rtable); + + let wasm_input_helper_table = + WasmInputHelperTableConfig::configure(meta, foreign_table_from_zero_index); + let context_helper_table = + ContextContHelperTableConfig::configure(meta, foreign_table_from_zero_index); + + let mut foreign_table_configs: BTreeMap<_, Box<(dyn ForeignTableConfig)>> = + BTreeMap::new(); + foreign_table_configs.insert( + WASM_INPUT_FOREIGN_TABLE_KEY, + Box::new(wasm_input_helper_table.clone()), ); - }); - - let _layouter = layouter.clone(); - let _etable = etable.clone(); - s.spawn(move |_| { - exec_with_profile!( - || "Assign bit table", - bit_chip - .assign(_layouter, _etable.filter_bit_table_entries()) - .unwrap() + foreign_table_configs.insert( + CONTEXT_FOREIGN_TABLE_KEY, + Box::new(context_helper_table.clone()), ); - }); - - let _layouter = layouter.clone(); - s.spawn(move |_| { - exec_with_profile!( - || "Assign external host call table", - external_host_call_chip - .assign( - _layouter, - &self.slice.etable.filter_external_host_call_table(), - ) - .unwrap() + + let etable = EventTableConfig::configure( + meta, + k, + &mut cols, + &rtable, + &image_table, + &mtable, + &frame_table, + &bit_table, + &external_host_call_table, + &foreign_table_configs, ); - }); - - let _layouter = layouter.clone(); - s.spawn(move |_| { - exec_with_profile!( - || "Assign context cont chip", - context_chip - .assign( - _layouter, - &self.slice.etable.get_context_inputs(), - &self.slice.etable.get_context_outputs() - ) - .unwrap() + + assert_eq!(cols.count(), 0); + + let max_available_rows = (1 << k) - (meta.blinding_factors() + 1 + RESERVE_ROWS); + debug!("max_available_rows: {:?}", max_available_rows); + + let circuit_maximal_pages = compute_maximal_pages(k); + info!( + "Circuit K: {} supports up to {} pages.", + k, circuit_maximal_pages ); - }); - - let _layouter = layouter.clone(); - let _assigned_cells = assigned_cells.clone(); - s.spawn(move |_| { - let pre_image_table = self.slice.encode_pre_compilation_table_values(config.k); - - let cells = exec_with_profile!( - || "Assign pre image table chip", - image_chip - .assign(_layouter, &image_table_assigner, pre_image_table) - .unwrap() + + Self::Config { + rtable, + image_table, + post_image_table, + mtable, + frame_table, + etable, + bit_table, + external_host_call_table, + context_helper_table, + foreign_table_from_zero_index, + + max_available_rows, + circuit_maximal_pages, + + k, + } + } + + fn synthesize( + &self, + config: Self::Config, + layouter: impl Layouter, + ) -> Result<(), Error> { + let assign_timer = start_timer!(|| "Assign"); + + let rchip = RangeTableChip::new(config.rtable); + let image_chip = ImageTableChip::new(config.image_table); + let post_image_chip = PostImageTableChip::new(config.post_image_table); + let mchip = MemoryTableChip::new(config.mtable, config.max_available_rows); + let frame_table_chip = + JumpTableChip::new(config.frame_table, config.max_available_rows); + let echip = EventTableChip::new( + config.etable, + compute_slice_capability(self.k) as usize, + config.max_available_rows, ); + let bit_chip = BitTableChip::new(config.bit_table, config.max_available_rows); + let external_host_call_chip = ExternalHostCallChip::new( + config.external_host_call_table, + config.max_available_rows, + ); + let context_chip = ContextContHelperTableChip::new(config.context_helper_table); - *_assigned_cells.pre_image_table_cells.lock().unwrap() = Some(cells); - }); - - let _layouter = layouter.clone(); - let _assigned_cells = assigned_cells.clone(); - let _memory_writing_table = memory_writing_table.clone(); - s.spawn(move |_| { - let post_image_table: ImageTableLayouter = - self.slice.encode_post_compilation_table_values(config.k); - - let (rest_memory_writing_ops, memory_finalized_set) = - _memory_writing_table.count_rest_memory_finalize_ops(); - - let cells = post_image_chip - .assign( - _layouter, - &image_table_assigner, - post_image_table, - rest_memory_writing_ops, - memory_finalized_set, - ) - .unwrap(); - - *_assigned_cells.post_image_table_cells.lock().unwrap() = Some(cells); - }); - - let _layouter = layouter.clone(); - let _assigned_cells = assigned_cells.clone(); - s.spawn(move |_| { - exec_with_profile!(|| "Assign frame table", { - let (rest_jops_cell, static_frame_entry_cells) = frame_table_chip - .assign( - _layouter, - &self.slice.static_jtable, - &self.slice.frame_table, - ) - .unwrap(); - - *_assigned_cells - .rest_jops_cell_in_frame_table - .lock() - .unwrap() = Some(rest_jops_cell); - *_assigned_cells - .static_frame_entry_in_frame_table - .lock() - .unwrap() = Some(static_frame_entry_cells); - }); - }); - - let _layouter = layouter.clone(); - let _assigned_cells = assigned_cells.clone(); - s.spawn(move |_| { - exec_with_profile!(|| "Assign mtable", { - let (rest_mops, rest_memory_finalize_ops_cell) = - mchip.assign(_layouter, &memory_writing_table).unwrap(); - - *_assigned_cells.mtable_rest_mops.lock().unwrap() = Some(rest_mops); - *_assigned_cells - .rest_memory_finalize_ops_cell - .lock() - .unwrap() = Some(rest_memory_finalize_ops_cell); - }); - }); - - let _layouter = layouter.clone(); - let _assigned_cells = assigned_cells.clone(); - s.spawn(move |_| { - exec_with_profile!(|| "Assign etable", { - let cells = echip - .assign( - _layouter, - &self.slice.itable, - &etable, - &self.slice.configure_table, - &self.slice.initialization_state, - &self.slice.post_initialization_state, - self.slice.is_last_slice, - ) - .unwrap(); - - *_assigned_cells.etable_cells.lock().unwrap() = Some(cells); - }); - }); - }); - - macro_rules! into_inner { - ($arc:ident) => { - let $arc = Arc::try_unwrap(assigned_cells_cloned.$arc) - .unwrap() - .into_inner() - .unwrap() - .unwrap(); - }; - } + let image_table_assigner = ImageTableAssigner::new( + // Add one for default lookup value + self.slice.itable.len() + 1, + self.slice.br_table.entries().len() + self.slice.elem_table.entries().len() + 1, + config.circuit_maximal_pages, + ); - into_inner!(static_frame_entry_in_frame_table); - into_inner!(etable_cells); - into_inner!(mtable_rest_mops); - into_inner!(rest_memory_finalize_ops_cell); - into_inner!(pre_image_table_cells); - into_inner!(post_image_table_cells); - into_inner!(rest_jops_cell_in_frame_table); - /* - * Permutation between chips - * - */ - layouter_cloned.assign_region( - || "permutation between tables", - |region| { - // 1. static frame entries - // 1.1. between frame table and pre image table - for (left, right) in static_frame_entry_in_frame_table - .iter() - .zip(pre_image_table_cells.static_frame_entries.iter()) - { - // enable - region.constrain_equal(left.0.cell(), right.0.cell())?; - // entry - region.constrain_equal(left.1.cell(), right.1.cell())?; - } + let memory_writing_table: MemoryWritingTable = MemoryWritingTable::from( + config.k, + self.slice.create_memory_table(memory_event_of_step), + ); - // 1.2 (if continuation) between frame table and post image table - if let Some((post_image_table_cells, _)) = post_image_table_cells.as_ref() { - for (left, right) in static_frame_entry_in_frame_table - .iter() - .zip(post_image_table_cells.static_frame_entries.iter()) - { - // enable - region.constrain_equal(left.0.cell(), right.0.cell())?; - // entry - region.constrain_equal(left.1.cell(), right.1.cell())?; - } - } + let etable = exec_with_profile!( + || "Prepare memory info for etable", + EventTableWithMemoryInfo::new(&self.slice.etable, &memory_writing_table,) + ); - // 2. rest jops - // 2.1 (if not continuation) rest_jops between event chip and frame chip - if let Some(rest_jops_in_event_chip) = etable_cells.rest_jops.as_ref() { - region.constrain_equal( - rest_jops_in_event_chip.cell(), - rest_jops_cell_in_frame_table.cell(), - )?; + let assigned_cells = AssignedCells::default(); + + let layouter_cloned = layouter.clone(); + let assigned_cells_cloned = assigned_cells.clone(); + + rayon::scope(move |s| { + let memory_writing_table = Arc::new(memory_writing_table); + let etable = Arc::new(etable); + + let _layouter = layouter.clone(); + s.spawn(move |_| { + exec_with_profile!( + || "Init range chip", + rchip.init(_layouter, config.k).unwrap() + ); + }); + + let _layouter = layouter.clone(); + s.spawn(move |_| { + exec_with_profile!( + || "Init foreign table index", + _layouter + .assign_region( + || "foreign helper", + |region| { + for offset in 0..foreign_table_enable_lines(config.k) { + region.assign_fixed( + || "foreign table from zero index", + config.foreign_table_from_zero_index, + offset, + || Ok(F::from(offset as u64)), + )?; + } + + Ok(()) + }, + ) + .unwrap() + ); + }); + + let _layouter = layouter.clone(); + let _etable = etable.clone(); + s.spawn(move |_| { + exec_with_profile!( + || "Assign bit table", + bit_chip + .assign(_layouter, _etable.filter_bit_table_entries()) + .unwrap() + ); + }); + + let _layouter = layouter.clone(); + s.spawn(move |_| { + exec_with_profile!( + || "Assign external host call table", + external_host_call_chip + .assign( + _layouter, + &self.slice.etable.filter_external_host_call_table(), + ) + .unwrap() + ); + }); + + let _layouter = layouter.clone(); + s.spawn(move |_| { + exec_with_profile!( + || "Assign context cont chip", + context_chip + .assign( + _layouter, + &self.slice.etable.get_context_inputs(), + &self.slice.etable.get_context_outputs() + ) + .unwrap() + ); + }); + + let _layouter = layouter.clone(); + let _assigned_cells = assigned_cells.clone(); + s.spawn(move |_| { + let pre_image_table = + self.slice.encode_pre_compilation_table_values(config.k); + + let cells = exec_with_profile!( + || "Assign pre image table chip", + image_chip + .assign(_layouter, &image_table_assigner, pre_image_table) + .unwrap() + ); + + *_assigned_cells.pre_image_table_cells.lock().unwrap() = Some(cells); + }); + + let _layouter = layouter.clone(); + let _assigned_cells = assigned_cells.clone(); + let _memory_writing_table = memory_writing_table.clone(); + s.spawn(move |_| { + let post_image_table: ImageTableLayouter = + self.slice.encode_post_compilation_table_values(config.k); + + let (rest_memory_writing_ops, memory_finalized_set) = + _memory_writing_table.count_rest_memory_finalize_ops(); + + let cells = post_image_chip + .assign( + _layouter, + &image_table_assigner, + post_image_table, + rest_memory_writing_ops, + memory_finalized_set, + ) + .unwrap(); + + *_assigned_cells.post_image_table_cells.lock().unwrap() = Some(cells); + }); + + let _layouter = layouter.clone(); + let _assigned_cells = assigned_cells.clone(); + s.spawn(move |_| { + exec_with_profile!(|| "Assign frame table", { + let (rest_ops_cell, inherited_frame_entry_cells) = frame_table_chip + .assign(_layouter, &self.slice.frame_table) + .unwrap(); + + *_assigned_cells.rest_ops_cell_in_frame_table.lock().unwrap() = + Some(rest_ops_cell); + *_assigned_cells + .inherited_frame_entry_in_frame_table + .lock() + .unwrap() = Some(inherited_frame_entry_cells); + }); + }); + + let _layouter = layouter.clone(); + let _assigned_cells = assigned_cells.clone(); + s.spawn(move |_| { + exec_with_profile!(|| "Assign mtable", { + let (rest_mops, rest_memory_finalize_ops_cell) = + mchip.assign(_layouter, &memory_writing_table).unwrap(); + + *_assigned_cells.mtable_rest_mops.lock().unwrap() = Some(rest_mops); + *_assigned_cells + .rest_memory_finalize_ops_cell + .lock() + .unwrap() = Some(rest_memory_finalize_ops_cell); + }); + }); + + let _layouter = layouter.clone(); + let _assigned_cells = assigned_cells.clone(); + s.spawn(move |_| { + exec_with_profile!(|| "Assign etable", { + let cells = echip + .assign( + _layouter, + &self.slice.itable, + &etable, + &self.slice.configure_table, + &self.slice.frame_table, + &self.slice.initialization_state, + &self.slice.post_initialization_state, + self.slice.is_last_slice, + ) + .unwrap(); + + *_assigned_cells.etable_cells.lock().unwrap() = Some(cells); + }); + }); + }); + + macro_rules! into_inner { + ($arc:ident) => { + let $arc = Arc::try_unwrap(assigned_cells_cloned.$arc) + .unwrap() + .into_inner() + .unwrap() + .unwrap(); + }; } - // 2.2 (if continuation and last slice circuit) rest_jops between post image chip and frame chip - #[cfg(feature = "continuation")] - if self.slice.is_last_slice { - if let Some((assigned_post_image_table_cells, _)) = - post_image_table_cells.as_ref() - { + into_inner!(inherited_frame_entry_in_frame_table); + into_inner!(etable_cells); + into_inner!(mtable_rest_mops); + into_inner!(rest_memory_finalize_ops_cell); + into_inner!(pre_image_table_cells); + into_inner!(post_image_table_cells); + into_inner!(rest_ops_cell_in_frame_table); + /* + * Permutation between chips + */ + layouter_cloned.assign_region( + || "permutation between tables", + |region| { + // 1. inherited frame entries + // 1.1. between frame table and pre image table + for (left, right) in inherited_frame_entry_in_frame_table + .iter() + .zip(pre_image_table_cells.inherited_frame_entries.iter()) + { + region.constrain_equal(left.cell(), right.cell())?; + } + + // 2. rest jops between event chip and frame chip region.constrain_equal( - assigned_post_image_table_cells - .initialization_state - .jops - .cell(), - rest_jops_cell_in_frame_table.cell(), + etable_cells.rest_jops.rest_call_ops.cell(), + rest_ops_cell_in_frame_table.rest_call_ops.cell(), )?; - } - } - // 3. rest_mops between event chip and memory chip - region.constrain_equal(etable_cells.rest_mops.cell(), mtable_rest_mops.cell())?; - - // 4. (if continuation) memory finalized count between memory chip and post image chip - if let Some((_, rest_memory_finalized_ops_in_post_image_table)) = - post_image_table_cells.as_ref() - { - region.constrain_equal( - rest_memory_finalized_ops_in_post_image_table.cell(), - rest_memory_finalize_ops_cell.as_ref().unwrap().cell(), - )?; - } + region.constrain_equal( + etable_cells.rest_jops.rest_return_ops.cell(), + rest_ops_cell_in_frame_table.rest_return_ops.cell(), + )?; - // 5. initialization state - // 5.1 between event chip and pre image chip - etable_cells - .pre_initialization_state - .zip_for_each(&pre_image_table_cells.initialization_state, |l, r| { - region.constrain_equal(l.cell(), r.cell()) - })?; - - // 5.2 (if continuation) between event chip and post image chip - if let Some((post_image_table_cells, _)) = post_image_table_cells.as_ref() { - etable_cells - .post_initialization_state - .zip_for_each(&post_image_table_cells.initialization_state, |l, r| { - region.constrain_equal(l.cell(), r.cell()) - })?; - } + // 3. rest_mops between event chip and memory chip + region.constrain_equal( + etable_cells.rest_mops.cell(), + mtable_rest_mops.cell(), + )?; - // 6. fixed part(instructions, br_tables, padding) within pre image chip and post image chip - if let Some((post_image_table_cells, _)) = post_image_table_cells.as_ref() { - for (l, r) in pre_image_table_cells - .instructions - .iter() - .zip(post_image_table_cells.instructions.iter()) - { - region.constrain_equal(l.cell(), r.cell())?; - } - - for (l, r) in pre_image_table_cells - .br_table_entires - .iter() - .zip(post_image_table_cells.br_table_entires.iter()) - { - region.constrain_equal(l.cell(), r.cell())?; - } - - for (l, r) in pre_image_table_cells - .padding_entires - .iter() - .zip(post_image_table_cells.padding_entires.iter()) - { - region.constrain_equal(l.cell(), r.cell())?; - } - } + // 4. (if continuation) memory finalized count between memory chip and post image chip + if let Some((_, rest_memory_finalized_ops_in_post_image_table)) = + post_image_table_cells.as_ref() + { + region.constrain_equal( + rest_memory_finalized_ops_in_post_image_table.cell(), + rest_memory_finalize_ops_cell.as_ref().unwrap().cell(), + )?; + } + + // 5. initialization state + // 5.1 between event chip and pre image chip + etable_cells + .pre_initialization_state + .zip_for_each(&pre_image_table_cells.initialization_state, |l, r| { + region.constrain_equal(l.cell(), r.cell()) + })?; + + // 5.2 (if continuation) between event chip and post image chip + if let Some((post_image_table_cells, _)) = post_image_table_cells.as_ref() { + etable_cells.post_initialization_state.zip_for_each( + &post_image_table_cells.initialization_state, + |l, r| region.constrain_equal(l.cell(), r.cell()), + )?; + } + + // 6. fixed part(instructions, br_tables, padding) within pre image chip and post image chip + if let Some((post_image_table_cells, _)) = post_image_table_cells.as_ref() { + for (l, r) in pre_image_table_cells + .instructions + .iter() + .zip(post_image_table_cells.instructions.iter()) + { + region.constrain_equal(l.cell(), r.cell())?; + } + + for (l, r) in pre_image_table_cells + .br_table_entires + .iter() + .zip(post_image_table_cells.br_table_entires.iter()) + { + region.constrain_equal(l.cell(), r.cell())?; + } + + for (l, r) in pre_image_table_cells + .padding_entires + .iter() + .zip(post_image_table_cells.padding_entires.iter()) + { + region.constrain_equal(l.cell(), r.cell())?; + } + } + + Ok(()) + }, + )?; + + end_timer!(assign_timer); Ok(()) - }, - )?; - - end_timer!(assign_timer); - - Ok(()) - } + } + } + }; } + +impl_zkwasm_circuit!(OngoingCircuit, false); +impl_zkwasm_circuit!(LastSliceCircuit, true); diff --git a/crates/zkwasm/src/loader/mod.rs b/crates/zkwasm/src/loader/mod.rs index cd41b5f7a..a072f76a0 100644 --- a/crates/zkwasm/src/loader/mod.rs +++ b/crates/zkwasm/src/loader/mod.rs @@ -1,19 +1,8 @@ use anyhow::Result; use halo2_proofs::arithmetic::CurveAffine; use halo2_proofs::arithmetic::MultiMillerLoop; -use halo2_proofs::dev::MockProver; -use halo2_proofs::plonk::create_proof; -use halo2_proofs::plonk::keygen_vk; -use halo2_proofs::plonk::verify_proof; -use halo2_proofs::plonk::ProvingKey; -use halo2_proofs::plonk::SingleVerifier; -use halo2_proofs::plonk::VerifyingKey; use halo2_proofs::poly::commitment::Params; -use halo2_proofs::poly::commitment::ParamsVerifier; -use halo2_proofs::transcript::Blake2bRead; -use halo2_proofs::transcript::Blake2bWrite; use log::warn; -use rand::rngs::OsRng; use specs::CompilationTable; @@ -24,7 +13,6 @@ use wasmi::RuntimeValue; use crate::checksum::ImageCheckSum; use crate::circuits::config::init_zkwasm_runtime; -use crate::circuits::ZkWasmCircuit; use crate::error::BuildingCircuitError; use crate::loader::err::Error; use crate::loader::err::PreCheckErr; @@ -111,40 +99,6 @@ impl ZkWasmLoader { WasmInterpreter::compile(monitor, module, &imports, self.entry.as_str()) } - pub fn circuit_without_witness( - &mut self, - _is_last_slice: bool, - ) -> Result, BuildingCircuitError> { - todo!() - /* - let k = self.k; - - let env = env_builder.create_env_without_value(k); - - let compiled_module = self.compile(&env, false, TraceBackend::Memory).unwrap(); - - ZkWasmCircuit::new( - k, - Slice { - itable: compiled_module.tables.itable.clone(), - br_table: compiled_module.tables.br_table.clone(), - elem_table: compiled_module.tables.elem_table.clone(), - configure_table: compiled_module.tables.configure_table.clone(), - static_jtable: compiled_module.tables.static_jtable.clone(), - imtable: compiled_module.tables.imtable.clone(), - initialization_state: compiled_module.tables.initialization_state.clone(), - post_imtable: compiled_module.tables.imtable.clone(), - post_initialization_state: compiled_module.tables.initialization_state.clone(), - - etable: Arc::new(EventTable::default()), - frame_table: Arc::new(JumpTable::default()), - - is_last_slice, - }, - ) - */ - } - /// Create a ZkWasm Loader /// /// Arguments: @@ -166,14 +120,6 @@ impl ZkWasmLoader { pub(crate) fn set_entry(&mut self, entry: String) { self.entry = entry; } - - pub fn create_vkey( - &self, - params: &Params, - circuit: &ZkWasmCircuit, - ) -> Result> { - Ok(keygen_vk(¶ms, circuit).unwrap()) - } } impl ZkWasmLoader { @@ -194,67 +140,12 @@ impl ZkWasmLoader { // Slices::new(self.k, execution_result.tables) } - #[deprecated] - pub fn mock_test( - &self, - circuit: &ZkWasmCircuit, - instances: &Vec, - ) -> Result<()> { - let prover = MockProver::run(self.k, circuit, vec![instances.clone()])?; - assert_eq!(prover.verify(), Ok(())); - - Ok(()) - } - - pub fn create_proof( - &self, - params: &Params, - pk: &ProvingKey, - circuit: &ZkWasmCircuit, - instances: &Vec, - ) -> Result> { - let mut transcript = Blake2bWrite::init(vec![]); - - create_proof( - params, - pk, - std::slice::from_ref(circuit), - &[&[&instances[..]]], - OsRng, - &mut transcript, - )?; - - Ok(transcript.finalize()) - } - fn init_env(&self) -> Result<()> { init_zkwasm_runtime(self.k); Ok(()) } - pub fn verify_proof( - &self, - params: &Params, - vkey: &VerifyingKey, - instances: &Vec, - proof: Vec, - ) -> Result<()> { - let params_verifier: ParamsVerifier = params.verifier(instances.len()).unwrap(); - let strategy = SingleVerifier::new(¶ms_verifier); - - verify_proof( - ¶ms_verifier, - vkey, - strategy, - &[&[&instances]], - &mut Blake2bRead::init(&proof[..]), - ) - .unwrap(); - - Ok(()) - } - /// Compute the checksum of the compiled wasm image. pub fn checksum( &self, @@ -264,61 +155,3 @@ impl ZkWasmLoader { Ok(compilation_table.checksum(self.k, params)) } } - -// #[cfg(test)] -// mod tests { -// use ark_std::end_timer; -// use ark_std::start_timer; -// use halo2_proofs::pairing::bn256::Bn256; -// use halo2_proofs::pairing::bn256::Fr; -// use halo2_proofs::pairing::bn256::G1Affine; -// use halo2_proofs::plonk::keygen_pk; -// use halo2_proofs::poly::commitment::Params; -// use std::fs::File; -// use std::io::Cursor; -// use std::io::Read; -// use std::path::PathBuf; - -// use crate::circuits::ZkWasmCircuit; -// use crate::runtime::host::default_env::DefaultHostEnvBuilder; -// use crate::runtime::host::default_env::ExecutionArg; - -// use super::ZkWasmLoader; - -// impl ZkWasmLoader { -// pub(crate) fn bench_test(&self, circuit: ZkWasmCircuit, instances: &Vec) { -// fn prepare_param(k: u32) -> Params { -// let path = PathBuf::from(format!("test_param.{}.data", k)); - -// if path.exists() { -// let mut fd = File::open(path.as_path()).unwrap(); -// let mut buf = vec![]; - -// fd.read_to_end(&mut buf).unwrap(); -// Params::::read(Cursor::new(buf)).unwrap() -// } else { -// // Initialize the polynomial commitment parameters -// let timer = start_timer!(|| format!("build params with K = {}", k)); -// let params: Params = Params::::unsafe_setup::(k); -// end_timer!(timer); - -// let mut fd = File::create(path.as_path()).unwrap(); -// params.write(&mut fd).unwrap(); - -// params -// } -// } - -// let params = prepare_param(self.k); -// let vkey = self.create_vkey(¶ms, &circuit).unwrap(); -// let pkey = keygen_pk(¶ms, vkey, &circuit).unwrap(); - -// let proof = self -// .create_proof(¶ms, &pkey, &circuit, &instances) -// .unwrap(); -// self.verify_proof(¶ms, pkey.get_vk(), instances, proof) -// .unwrap(); -// } -// } -// } -// diff --git a/crates/zkwasm/src/loader/slice.rs b/crates/zkwasm/src/loader/slice.rs index 78984bcf8..7e18ec243 100644 --- a/crates/zkwasm/src/loader/slice.rs +++ b/crates/zkwasm/src/loader/slice.rs @@ -1,17 +1,15 @@ use halo2_proofs::arithmetic::FieldExt; -use num_bigint::BigUint; use specs::brtable::BrTable; use specs::brtable::ElemTable; use specs::configure_table::ConfigureTable; use specs::etable::EventTable; -use specs::etable::EventTableBackend; use specs::imtable::InitMemoryTable; use specs::itable::InstructionTable; -use specs::jtable::JumpTable; -use specs::jtable::StaticFrameEntry; -use specs::jtable::STATIC_FRAME_ENTRY_NUMBER; +use specs::jtable::FrameTable; +use specs::jtable::InheritedFrameTable; use specs::slice::Slice; use specs::state::InitializationState; +use specs::TableBackend; use specs::Tables; use std::collections::VecDeque; use std::sync::Arc; @@ -28,12 +26,12 @@ pub struct Slices { br_table: Arc, elem_table: Arc, configure_table: Arc, - static_jtable: Arc<[StaticFrameEntry; STATIC_FRAME_ENTRY_NUMBER]>, - frame_table: Arc, + initial_frame_table: Arc, + frame_table: VecDeque>, imtable: Arc, - initialization_state: Arc>, - etables: VecDeque, + initialization_state: Arc>, + etables: VecDeque>, _marker: std::marker::PhantomData, } @@ -55,8 +53,9 @@ impl Slices { br_table: tables.compilation_tables.br_table, elem_table: tables.compilation_tables.elem_table, configure_table: tables.compilation_tables.configure_table, - static_jtable: tables.compilation_tables.static_jtable, - frame_table: Arc::new(tables.execution_tables.jtable), + initial_frame_table: tables.compilation_tables.initial_frame_table, + + frame_table: tables.execution_tables.frame_table.into(), imtable: tables.compilation_tables.imtable, initialization_state: tables.compilation_tables.initialization_state, @@ -74,8 +73,16 @@ impl Slices { let mut iter = self.into_iter(); while let Some(slice) = iter.next() { - let prover = MockProver::run(k, &slice?, vec![instances.clone()])?; - assert_eq!(prover.verify(), Ok(())); + match slice? { + ZkWasmCircuit::Ongoing(circuit) => { + let prover = MockProver::run(k, &circuit, vec![instances.clone()])?; + assert_eq!(prover.verify(), Ok(())); + } + ZkWasmCircuit::LastSliceCircuit(circuit) => { + let prover = MockProver::run(k, &circuit, vec![instances.clone()])?; + assert_eq!(prover.verify(), Ok(())); + } + } } Ok(()) @@ -91,16 +98,16 @@ impl Iterator for Slices { } let etable = match self.etables.pop_front().unwrap() { - EventTableBackend::Memory(etable) => etable, - EventTableBackend::Json(path) => EventTable::read(&path).unwrap(), + TableBackend::Memory(etable) => etable, + TableBackend::Json(path) => EventTable::read(&path).unwrap(), }; let post_imtable = Arc::new(self.imtable.update_init_memory_table(&etable)); let post_initialization_state = Arc::new({ let next_event_entry = if let Some(next_event_table) = self.etables.front() { match next_event_table { - EventTableBackend::Memory(etable) => etable.entries().first().cloned(), - EventTableBackend::Json(path) => { + TableBackend::Memory(etable) => etable.entries().first().cloned(), + TableBackend::Json(path) => { let etable = EventTable::read(&path).unwrap(); etable.entries().first().cloned() } @@ -116,13 +123,33 @@ impl Iterator for Slices { ) }); + let frame_table = match self.frame_table.pop_front().unwrap() { + TableBackend::Memory(frame_table) => frame_table, + TableBackend::Json(path) => FrameTable::read(&path).unwrap(), + } + .into(); + + let post_inherited_frame_table = self.frame_table.front().map_or( + Arc::new(InheritedFrameTable::default()), + |frame_table| { + let post_inherited_frame_table = match frame_table { + TableBackend::Memory(frame_table) => frame_table.inherited.clone(), + TableBackend::Json(path) => FrameTable::read(&path).unwrap().inherited, + }; + + Arc::new((*post_inherited_frame_table).clone().try_into().unwrap()) + }, + ); + let slice = Slice { itable: self.itable.clone(), br_table: self.br_table.clone(), elem_table: self.elem_table.clone(), configure_table: self.configure_table.clone(), - static_jtable: self.static_jtable.clone(), - frame_table: self.frame_table.clone(), + initial_frame_table: self.initial_frame_table.clone(), + + frame_table: Arc::new(frame_table), + post_inherited_frame_table, imtable: self.imtable.clone(), post_imtable: post_imtable.clone(), diff --git a/crates/zkwasm/src/runtime/monitor/plugins/table/etable.rs b/crates/zkwasm/src/runtime/monitor/plugins/table/etable.rs index 85d2b06cb..b59daba8b 100644 --- a/crates/zkwasm/src/runtime/monitor/plugins/table/etable.rs +++ b/crates/zkwasm/src/runtime/monitor/plugins/table/etable.rs @@ -1,20 +1,22 @@ +use std::rc::Rc; + use specs::etable::EventTable; -use specs::etable::EventTableBackend; use specs::etable::EventTableEntry; use specs::step::StepInfo; +use specs::TableBackend; use specs::TraceBackend; use wasmi::DEFAULT_VALUE_STACK_LIMIT; pub(super) struct ETable { pub(crate) eid: u32, - slices: Vec, + slices: Vec>, entries: Vec, capacity: u32, - backend: TraceBackend, + backend: Rc, } impl ETable { - pub(crate) fn new(capacity: u32, backend: TraceBackend) -> Self { + pub(crate) fn new(capacity: u32, backend: Rc) -> Self { Self { eid: 0, slices: Vec::default(), @@ -24,17 +26,18 @@ impl ETable { } } - fn flush(&mut self) { + pub(crate) fn flush(&mut self) { let empty = Vec::with_capacity(self.capacity as usize); let entries = std::mem::replace(&mut self.entries, empty); - let event_table = match &self.backend { - TraceBackend::File(write_file_fn) => { - let path = write_file_fn(self.slices.len(), &EventTable::new(entries)); - - EventTableBackend::Json(path) - } - TraceBackend::Memory => EventTableBackend::Memory(EventTable::new(entries)), + let event_table = match self.backend.as_ref() { + TraceBackend::File { + event_table_writer, .. + } => TableBackend::Json(event_table_writer( + self.slices.len(), + &EventTable::new(entries), + )), + TraceBackend::Memory => TableBackend::Memory(EventTable::new(entries)), }; self.slices.push(event_table); @@ -49,10 +52,6 @@ impl ETable { last_jump_eid: u32, step_info: StepInfo, ) { - if self.entries.len() == self.capacity as usize { - self.flush(); - } - self.eid += 1; let sp = (DEFAULT_VALUE_STACK_LIMIT as u32) @@ -82,7 +81,7 @@ impl ETable { &mut self.entries } - pub fn finalized(mut self) -> Vec { + pub fn finalized(mut self) -> Vec> { self.flush(); self.slices diff --git a/crates/zkwasm/src/runtime/monitor/plugins/table/frame_table.rs b/crates/zkwasm/src/runtime/monitor/plugins/table/frame_table.rs new file mode 100644 index 000000000..9ee4bce30 --- /dev/null +++ b/crates/zkwasm/src/runtime/monitor/plugins/table/frame_table.rs @@ -0,0 +1,193 @@ +use std::rc::Rc; +use std::sync::Arc; + +use specs::jtable::CalledFrameTable; +use specs::jtable::CalledFrameTableEntry; +use specs::jtable::FrameTableEntryInternal; +use specs::jtable::InheritedFrameTable; +use specs::jtable::InheritedFrameTableEntry; +use specs::TableBackend; +use specs::TraceBackend; + +#[derive(Clone)] +struct FrameTableEntry { + frame_id: u32, + next_frame_id: u32, + callee_fid: u32, + fid: u32, + iid: u32, + inherited: bool, + returned: bool, +} + +impl Into for &FrameTableEntry { + fn into(self) -> CalledFrameTableEntry { + assert!(!self.inherited); + + CalledFrameTableEntry(FrameTableEntryInternal { + frame_id: self.frame_id, + next_frame_id: self.next_frame_id, + callee_fid: self.callee_fid, + fid: self.fid, + iid: self.iid, + returned: self.returned, + }) + } +} + +impl Into for &FrameTableEntry { + fn into(self) -> InheritedFrameTableEntry { + assert!(self.inherited); + + InheritedFrameTableEntry(Some(FrameTableEntryInternal { + frame_id: self.frame_id, + next_frame_id: self.next_frame_id, + callee_fid: self.callee_fid, + fid: self.fid, + iid: self.iid, + returned: self.returned, + })) + } +} + +pub(super) struct FrameTable { + initial_frame_entries: Vec, + slices: Vec>, + + current_unreturned: Vec, + current_returned: Vec, + + backend: Rc, +} + +impl FrameTable { + pub(super) fn new(backend: Rc) -> Self { + Self { + initial_frame_entries: Vec::new(), + slices: Vec::new(), + + current_unreturned: Vec::new(), + current_returned: Vec::new(), + + backend, + } + } + + pub(super) fn push( + &mut self, + frame_id: u32, + next_frame_id: u32, + callee_fid: u32, + fid: u32, + iid: u32, + ) { + self.current_unreturned.push(FrameTableEntry { + frame_id, + next_frame_id, + callee_fid, + fid, + iid, + inherited: false, + returned: false, + }); + } + + pub(super) fn push_static_entry( + &mut self, + frame_id: u32, + next_frame_id: u32, + callee_fid: u32, + fid: u32, + iid: u32, + ) { + let entry = FrameTableEntry { + frame_id, + next_frame_id, + callee_fid, + fid, + iid, + inherited: true, + returned: false, + }; + + self.current_unreturned.push(entry.clone()); + self.initial_frame_entries.push(entry); + } + + // Prepare for the next slice. This will remove all the entries that are returned + pub(super) fn flush(&mut self) { + let frame_table = { + let frame_table = { + let inherited = self + .current_returned + .iter() + .chain(self.current_unreturned.iter()) + .filter(|entry| entry.inherited) + .map(Into::into) + .collect::>(); + + let called = self + .current_returned + .iter() + .chain(self.current_unreturned.iter()) + .filter(|entry| !entry.inherited) + .map(Into::into) + .collect::>(); + + specs::jtable::FrameTable { + inherited: Arc::new(inherited.into()), + called: CalledFrameTable::new(called), + } + }; + + match self.backend.as_ref() { + TraceBackend::Memory => TableBackend::Memory(frame_table), + TraceBackend::File { + frame_table_writer, .. + } => TableBackend::Json(frame_table_writer(self.slices.len(), &frame_table)), + } + }; + + self.slices.push(frame_table); + + self.current_returned.clear(); + for entry in self.current_unreturned.iter_mut() { + entry.inherited = true; + } + } + + pub(super) fn pop(&mut self) { + let mut entry = self.current_unreturned.pop().unwrap(); + entry.returned = true; + self.current_returned.push(entry); + } + + pub(super) fn finalized(mut self) -> Vec> { + self.flush(); + + assert!( + self.current_unreturned.is_empty(), + "all frames should be returned" + ); + + self.slices + } + + pub(super) fn build_initial_frame_table(&self) -> InheritedFrameTable { + self.initial_frame_entries + .iter() + .map(|entry| { + InheritedFrameTableEntry(Some(FrameTableEntryInternal { + frame_id: entry.frame_id, + next_frame_id: entry.next_frame_id, + callee_fid: entry.callee_fid, + fid: entry.fid, + iid: entry.iid, + returned: false, + })) + }) + .collect::>() + .try_into() + .unwrap() + } +} diff --git a/crates/zkwasm/src/runtime/monitor/plugins/table/mod.rs b/crates/zkwasm/src/runtime/monitor/plugins/table/mod.rs index 0e3fd6632..f3013b845 100644 --- a/crates/zkwasm/src/runtime/monitor/plugins/table/mod.rs +++ b/crates/zkwasm/src/runtime/monitor/plugins/table/mod.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use std::rc::Rc; use std::sync::Arc; use parity_wasm::elements::External; @@ -11,10 +12,6 @@ use specs::imtable::InitMemoryTable; use specs::imtable::InitMemoryTableEntry; use specs::itable::InstructionTable; use specs::itable::InstructionTableInternal; -use specs::jtable::JumpTable; -use specs::jtable::JumpTableEntry; -use specs::jtable::StaticFrameEntry; -use specs::jtable::STATIC_FRAME_ENTRY_NUMBER; use specs::mtable::LocationType; use specs::mtable::VarType; use specs::state::InitializationState; @@ -45,6 +42,7 @@ use wasmi::DEFAULT_VALUE_STACK_LIMIT; use crate::circuits::compute_slice_capability; use self::etable::ETable; +use self::frame_table::FrameTable; use self::instruction::run_instruction_pre; use self::instruction::FuncDesc; use self::instruction::InstructionIntoOpcode; @@ -54,12 +52,15 @@ use self::instruction::RunInstructionTracePre; use super::phantom::PhantomHelper; mod etable; +mod frame_table; mod instruction; const DEFAULT_MEMORY_INDEX: u32 = 0; const DEFAULT_TABLE_INDEX: u32 = 0; pub struct TablePlugin { + capacity: u32, + phantom_helper: PhantomHelper, host_function_desc: HashMap, @@ -69,11 +70,10 @@ pub struct TablePlugin { elements: Vec, configure_table: ConfigureTable, init_memory_table: Vec, - static_frame_table: Vec, start_fid: Option, etable: ETable, - frame_table: JumpTable, + frame_table: FrameTable, last_jump_eid: Vec, module_ref: Option, @@ -86,9 +86,14 @@ impl TablePlugin { host_function_desc: HashMap, phantom_regex: &Vec, wasm_input: FuncRef, - backend: TraceBackend, + trace_backend: TraceBackend, ) -> Self { + let capacity = compute_slice_capability(k); + let trace_backend = Rc::new(trace_backend); + Self { + capacity, + host_function_desc, phantom_helper: PhantomHelper::new(phantom_regex, wasm_input), @@ -98,12 +103,11 @@ impl TablePlugin { configure_table: ConfigureTable::default(), init_memory_table: vec![], function_table: vec![], - static_frame_table: vec![], start_fid: None, last_jump_eid: vec![], - etable: ETable::new(compute_slice_capability(k), backend), - frame_table: JumpTable::default(), + etable: ETable::new(capacity, trace_backend.clone()), + frame_table: FrameTable::new(trace_backend), module_ref: None, unresolved_event: None, @@ -116,10 +120,6 @@ impl TablePlugin { let br_table = Arc::new(itable.create_brtable()); let elem_table = Arc::new(ElemTable::new(self.elements.clone())); let configure_table = Arc::new(self.configure_table.clone()); - let static_jtable = Arc::new(self.static_frame_table.clone().try_into().expect(&format!( - "The number of static frame entries should be {}", - STATIC_FRAME_ENTRY_NUMBER - ))); let initialization_state = Arc::new(InitializationState { eid: 1, fid: self.start_fid.unwrap(), @@ -134,12 +134,6 @@ impl TablePlugin { initial_memory_pages: configure_table.init_memory_pages, maximal_memory_pages: configure_table.maximal_memory_pages, - - #[cfg(feature = "continuation")] - jops: num_bigint::BigUint::from(0u64), - - #[cfg(not(feature = "continuation"))] - _phantom: core::marker::PhantomData, }); CompilationTable { @@ -148,7 +142,7 @@ impl TablePlugin { br_table, elem_table, configure_table, - static_jtable, + initial_frame_table: Arc::new(self.frame_table.build_initial_frame_table()), initialization_state, } } @@ -158,26 +152,65 @@ impl TablePlugin { compilation_tables: self.into_compilation_table(), execution_tables: ExecutionTable { etable: self.etable.finalized(), - jtable: self.frame_table, + frame_table: self.frame_table.finalized(), }, } } } impl TablePlugin { - fn push_frame(&mut self, eid: u32, last_jump_eid: u32, callee_fid: u32, fid: u32, iid: u32) { - self.frame_table.push(JumpTableEntry { - eid, - last_jump_eid, - callee_fid, + fn push_event( + &mut self, + fid: u32, + iid: u32, + sp: u32, + allocated_memory_pages: u32, + last_jump_eid: u32, + step_info: StepInfo, + ) { + if self.etable.entries().len() == self.capacity as usize { + self.etable.flush(); + self.frame_table.flush(); + } + + self.etable.push( fid, iid, - }); + sp, + allocated_memory_pages, + last_jump_eid, + step_info, + ) + } - self.last_jump_eid.push(eid); + fn push_frame( + &mut self, + frame_id: u32, + next_frame_id: u32, + callee_fid: u32, + fid: u32, + iid: u32, + ) { + self.frame_table + .push(frame_id, next_frame_id, callee_fid, fid, iid); + + self.last_jump_eid.push(frame_id); + } + + fn push_static_frame( + &mut self, + frame_id: u32, + next_frame_id: u32, + callee_fid: u32, + fid: u32, + iid: u32, + ) { + self.frame_table + .push_static_entry(frame_id, next_frame_id, callee_fid, fid, iid); } fn pop_frame(&mut self) { + self.frame_table.pop(); self.last_jump_eid.pop(); } @@ -204,7 +237,7 @@ impl TablePlugin { }; if has_return_value { - self.etable.push( + self.push_event( fid, iid, current_sp, @@ -215,7 +248,7 @@ impl TablePlugin { iid += 1; - self.etable.push( + self.push_event( fid, iid, current_sp + 1, @@ -238,7 +271,7 @@ impl TablePlugin { iid += 1; if callee_sig.return_type() != Some(wasmi::ValueType::I64) { - self.etable.push( + self.push_event( fid, iid, current_sp + 1, @@ -254,7 +287,7 @@ impl TablePlugin { } } - self.etable.push( + self.push_event( fid, iid, current_sp + has_return_value as u32, @@ -295,36 +328,13 @@ impl Monitor for TablePlugin { _ => unreachable!(), }; - self.static_frame_table.push(StaticFrameEntry { - enable: true, - frame_id: 0, - next_frame_id: 0, - callee_fid: *zkmain_idx as u32, - fid: 0, - iid: 0, - }); + self.push_static_frame(0, 0, *zkmain_idx as u32, 0, 0); if let Some(start_idx) = module.start_section() { - self.static_frame_table.push(StaticFrameEntry { - enable: true, - frame_id: 0, - next_frame_id: 0, - callee_fid: start_idx, - fid: *zkmain_idx as u32, - iid: 0, - }); + self.push_static_frame(0, 0, start_idx, *zkmain_idx as u32, 0); self.start_fid = Some(start_idx); } else { - self.static_frame_table.push(StaticFrameEntry { - enable: false, - frame_id: 0, - next_frame_id: 0, - callee_fid: 0, - fid: 0, - iid: 0, - }); - self.start_fid = Some(*zkmain_idx as u32); } } @@ -573,7 +583,7 @@ impl Monitor for TablePlugin { instruction, ); - self.etable.push( + self.push_event( fid, iid, sp, diff --git a/crates/zkwasm/src/runtime/state.rs b/crates/zkwasm/src/runtime/state.rs index 2356b17a4..7353e0f54 100644 --- a/crates/zkwasm/src/runtime/state.rs +++ b/crates/zkwasm/src/runtime/state.rs @@ -1,4 +1,3 @@ -use num_bigint::BigUint; use specs::configure_table::ConfigureTable; use specs::etable::EventTable; use specs::etable::EventTableEntry; @@ -54,22 +53,19 @@ impl UpdateInitMemoryTable for InitMemoryTable { } } -impl UpdateInitializationState for InitializationState { +impl UpdateInitializationState for InitializationState { fn update_initialization_state( &self, execution_table: &EventTable, configure_table: &ConfigureTable, // None indicates last slice next_event_entry: Option<&EventTableEntry>, - ) -> InitializationState { + ) -> InitializationState { let mut host_public_inputs = self.host_public_inputs; let mut context_in_index = self.context_in_index; let mut context_out_index = self.context_out_index; let mut external_host_call_call_index = self.external_host_call_call_index; - #[cfg(feature = "continuation")] - let mut jops = self.jops.clone(); - for entry in execution_table.entries() { match &entry.step_info { // TODO: fix hard code @@ -94,18 +90,6 @@ impl UpdateInitializationState for InitializationState { } } StepInfo::ExternalHostCall { .. } => external_host_call_call_index += 1, - StepInfo::Call { .. } | StepInfo::CallIndirect { .. } => { - #[cfg(feature = "continuation")] - { - jops += crate::circuits::jtable::encode_jops(0, 1); - } - } - StepInfo::Return { .. } => { - #[cfg(feature = "continuation")] - { - jops += crate::circuits::jtable::encode_jops(1, 0); - } - } _ => (), } } @@ -133,12 +117,6 @@ impl UpdateInitializationState for InitializationState { initial_memory_pages: last_entry.allocated_memory_pages, maximal_memory_pages: configure_table.maximal_memory_pages, - - #[cfg(feature = "continuation")] - jops, - - #[cfg(not(feature = "continuation"))] - _phantom: std::marker::PhantomData, } } else { let next_entry = next_event_entry.unwrap(); @@ -157,12 +135,6 @@ impl UpdateInitializationState for InitializationState { initial_memory_pages: next_entry.allocated_memory_pages, maximal_memory_pages: configure_table.maximal_memory_pages, - - #[cfg(feature = "continuation")] - jops, - - #[cfg(not(feature = "continuation"))] - _phantom: std::marker::PhantomData, } }; diff --git a/crates/zkwasm/src/test/mod.rs b/crates/zkwasm/src/test/mod.rs index 9e723813a..07a1bd3d5 100644 --- a/crates/zkwasm/src/test/mod.rs +++ b/crates/zkwasm/src/test/mod.rs @@ -46,7 +46,7 @@ pub fn test_circuit_with_env( let execution_result = loader.run(runner, &mut monitor)?; let instances: Vec = execution_result.public_inputs_and_outputs(); - Slices::new(k, monitor.into_tables())?.mock_test_all(k, instances)?; + Slices::new(k, monitor.into_tables())?.mock_test_all(instances)?; Ok(()) } diff --git a/crates/zkwasm/src/test/test_wasm_instructions/op_call_host.rs b/crates/zkwasm/src/test/test_wasm_instructions/op_call_host.rs index 856d18527..847921a38 100644 --- a/crates/zkwasm/src/test/test_wasm_instructions/op_call_host.rs +++ b/crates/zkwasm/src/test/test_wasm_instructions/op_call_host.rs @@ -1,6 +1,5 @@ use specs::external_host_call_table::ExternalHostCallSignature; use std::rc::Rc; -use wasmi::tracer::Observer; use crate::circuits::config::MIN_K; use crate::runtime::host::host_env::HostEnv; diff --git a/third-party/wasmi b/third-party/wasmi index 7b715a2dd..659fcf7a8 160000 --- a/third-party/wasmi +++ b/third-party/wasmi @@ -1 +1 @@ -Subproject commit 7b715a2dd7af3330e4f3bab46137cb38fd9cb4a2 +Subproject commit 659fcf7a84b1a67f6cbaa72cce7f953dc1580790