diff --git a/crates/cli/src/app_builder.rs b/crates/cli/src/app_builder.rs index 4ce9c0160..9a354cba4 100644 --- a/crates/cli/src/app_builder.rs +++ b/crates/cli/src/app_builder.rs @@ -148,6 +148,19 @@ impl ArgBuilder for SkipArg { } } +struct PaddingArg; +impl ArgBuilder> for PaddingArg { + fn builder() -> Arg<'static> { + arg!(--padding [AT_LEAST_N] "Insert trivial slices so that the number of proofs is at least AT_LEAST_N") + .value_parser(value_parser!(usize)) + .multiple_values(false) + } + + fn parse(matches: &ArgMatches) -> Option { + matches.get_one("padding").copied() + } +} + fn setup_command() -> Command<'static> { let command = Command::new("setup") .about("Setup a new zkWasm circuit for provided Wasm image") @@ -206,7 +219,7 @@ fn prove_command() -> Command<'static> { .arg(FileBackendArg::builder()); if cfg!(feature = "continuation") { - command.arg(SkipArg::builder()) + command.arg(SkipArg::builder()).arg(PaddingArg::builder()) } else { command } @@ -280,6 +293,7 @@ impl From<&ArgMatches> for ProveArg { mock_test: MockTestArg::parse(val), file_backend: FileBackendArg::parse(val), skip: SkipArg::parse(val), + padding: PaddingArg::parse(val), } } } diff --git a/crates/cli/src/command.rs b/crates/cli/src/command.rs index bc17a3b9f..b853ffbb8 100644 --- a/crates/cli/src/command.rs +++ b/crates/cli/src/command.rs @@ -263,6 +263,8 @@ pub(crate) struct ProveArg { pub(crate) file_backend: bool, // skip first n slice(s) proving. pub(crate) skip: usize, + // add trivial circuits to padding + pub(crate) padding: Option, } /// Verify the proof. diff --git a/crates/cli/src/config.rs b/crates/cli/src/config.rs index e5c0ac988..b9d6be77b 100644 --- a/crates/cli/src/config.rs +++ b/crates/cli/src/config.rs @@ -246,6 +246,7 @@ impl Config { mock_test: bool, table_backend: TraceBackend, skip: usize, + padding: Option, ) -> anyhow::Result<()> { let mut cached_proving_key = None; @@ -320,14 +321,18 @@ impl Config { let mut proof_load_info = ProofGenerationInfo::new(&self.name, self.k as usize, HashType::Poseidon); - let progress_bar = ProgressBar::new(tables.execution_tables.etable.len() as u64); + let progress_bar = ProgressBar::new(if let Some(padding) = padding { + usize::max(tables.execution_tables.etable.len(), padding) as u64 + } else { + tables.execution_tables.etable.len() as u64 + }); if skip != 0 { progress_bar.inc(skip as u64); println!("skip first {} slice(s)", skip); } - let mut slices = Slices::new(self.k, tables)? + let mut slices = Slices::new(self.k, tables, padding)? .enumerate() .skip(skip) .peekable(); diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index d0fae44e2..9524a312f 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -156,6 +156,7 @@ fn main() -> Result<()> { arg.mock_test, trace_backend, arg.skip, + arg.padding, )?; } Subcommands::Verify(arg) => { diff --git a/crates/playground/examples/binary_search.rs b/crates/playground/examples/binary_search.rs index 7fc694036..b56bf8b81 100644 --- a/crates/playground/examples/binary_search.rs +++ b/crates/playground/examples/binary_search.rs @@ -30,7 +30,7 @@ fn main() -> Result<()> { let result = loader.run(runner, &mut monitor)?; let instances = result.public_inputs_and_outputs::(); - let slices = Slices::new(K, monitor.into_tables())?; + let slices = Slices::new(K, monitor.into_tables(), None)?; slices.mock_test_all(instances)?; Ok(()) diff --git a/crates/playground/examples/context.rs b/crates/playground/examples/context.rs index 9810ea32a..ecbf5a75b 100644 --- a/crates/playground/examples/context.rs +++ b/crates/playground/examples/context.rs @@ -31,7 +31,7 @@ fn main() -> Result<()> { let runner = loader.compile(&module, &mut monitor)?; let result = loader.run(runner, &mut monitor)?; - let slices: Slices = Slices::new(K, monitor.into_tables())?; + let slices: Slices = Slices::new(K, monitor.into_tables(), None)?; slices.mock_test_all(result.public_inputs_and_outputs())?; result.context_outputs @@ -53,7 +53,7 @@ fn main() -> Result<()> { let runner = loader.compile(&module, &mut monitor)?; let result = loader.run(runner, &mut monitor)?; - let slices: Slices = Slices::new(K, monitor.into_tables())?; + let slices: Slices = Slices::new(K, monitor.into_tables(), None)?; slices.mock_test_all(result.public_inputs_and_outputs())?; } diff --git a/crates/playground/examples/fibonacci.rs b/crates/playground/examples/fibonacci.rs index 23343826f..fd5f53140 100644 --- a/crates/playground/examples/fibonacci.rs +++ b/crates/playground/examples/fibonacci.rs @@ -30,7 +30,7 @@ fn main() -> Result<()> { let result = loader.run(runner, &mut monitor)?; let instances = result.public_inputs_and_outputs::(); - let slices = Slices::new(K, monitor.into_tables())?; + let slices = Slices::new(K, monitor.into_tables(), None)?; slices.mock_test_all(instances)?; Ok(()) diff --git a/crates/playground/examples/phantom.rs b/crates/playground/examples/phantom.rs index 47aca0700..5821c47e2 100644 --- a/crates/playground/examples/phantom.rs +++ b/crates/playground/examples/phantom.rs @@ -30,7 +30,7 @@ fn main() -> Result<()> { let result = loader.run(runner, &mut monitor)?; let instances = result.public_inputs_and_outputs::(); - let slices = Slices::new(K, monitor.into_tables())?; + let slices = Slices::new(K, monitor.into_tables(), None)?; slices.mock_test_all(instances)?; Ok(()) diff --git a/crates/zkwasm/src/circuits/etable/mod.rs b/crates/zkwasm/src/circuits/etable/mod.rs index 9da93b9fa..1d8e3b307 100644 --- a/crates/zkwasm/src/circuits/etable/mod.rs +++ b/crates/zkwasm/src/circuits/etable/mod.rs @@ -613,6 +613,7 @@ impl EventTableConfig { vec![ (encode_instruction_table_entry(fid_cell.expr(meta), iid_cell.expr(meta), opcode) - itable_lookup_cell.curr_expr(meta)) + * enabled_cell.curr_expr(meta) * fixed_curr!(meta, step_sel), ] }); diff --git a/crates/zkwasm/src/loader/slice.rs b/crates/zkwasm/src/loader/slice.rs index a54b659ab..027cfe77e 100644 --- a/crates/zkwasm/src/loader/slice.rs +++ b/crates/zkwasm/src/loader/slice.rs @@ -5,8 +5,10 @@ use specs::configure_table::ConfigureTable; use specs::etable::EventTable; use specs::imtable::InitMemoryTable; use specs::itable::InstructionTable; +use specs::jtable::CalledFrameTable; use specs::jtable::FrameTable; use specs::jtable::InheritedFrameTable; +use specs::slice::FrameTableSlice; use specs::slice::Slice; use specs::state::InitializationState; use specs::TableBackend; @@ -22,6 +24,9 @@ use crate::runtime::state::UpdateInitializationState; pub struct Slices { k: u32, + // The number of trivial circuits left. + padding: usize, + itable: Arc, br_table: Arc, elem_table: Arc, @@ -37,18 +42,27 @@ pub struct Slices { } impl Slices { - pub fn new(k: u32, tables: Tables) -> Result { - if cfg!(not(feature = "continuation")) { - let slices = tables.execution_tables.etable.len(); - - if slices != 1 { - return Err(BuildingCircuitError::MultiSlicesNotSupport(slices)); - } + /* + * padding: Insert trivial slices so that the number of proofs is at least padding. + */ + pub fn new( + k: u32, + tables: Tables, + padding: Option, + ) -> Result { + let slices_len = tables.execution_tables.etable.len(); + + if cfg!(not(feature = "continuation")) && slices_len != 1 { + return Err(BuildingCircuitError::MultiSlicesNotSupport(slices_len)); } + let padding = padding.map_or(0, |padding| padding.saturating_sub(slices_len)); + Ok(Self { k, + padding, + itable: tables.compilation_tables.itable, br_table: tables.compilation_tables.br_table, elem_table: tables.compilation_tables.elem_table, @@ -89,6 +103,40 @@ impl Slices { } } +impl Slices { + // create a circuit slice with all entries disabled. + fn trivial_slice(&mut self) -> Result, BuildingCircuitError> { + self.padding -= 1; + + let frame_table = Arc::new(FrameTableSlice { + inherited: self.initial_frame_table.clone(), + called: CalledFrameTable::default(), + }); + + 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(), + initial_frame_table: self.initial_frame_table.clone(), + + frame_table, + post_inherited_frame_table: self.initial_frame_table.clone(), + + imtable: self.imtable.clone(), + post_imtable: self.imtable.clone(), + + initialization_state: self.initialization_state.clone(), + post_initialization_state: self.initialization_state.clone(), + + etable: Arc::new(EventTable::new(vec![])), + is_last_slice: false, + }; + + ZkWasmCircuit::new(self.k, slice) + } +} + impl Iterator for Slices { type Item = Result, BuildingCircuitError>; @@ -97,6 +145,10 @@ impl Iterator for Slices { return None; } + if self.padding > 0 { + return Some(self.trivial_slice()); + } + let etable = match self.etables.pop_front().unwrap() { TableBackend::Memory(etable) => etable, TableBackend::Json(path) => EventTable::read(&path).unwrap(), diff --git a/crates/zkwasm/src/test/mod.rs b/crates/zkwasm/src/test/mod.rs index 672000001..442054276 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(instances)?; + Slices::new(k, monitor.into_tables(), None)?.mock_test_all(instances)?; Ok(()) }