From 34dbebdd975592a7cfb2b6b44a738fbbd2ec231d Mon Sep 17 00:00:00 2001 From: John Guibas Date: Sat, 11 May 2024 13:01:10 -0700 Subject: [PATCH 1/7] in progress runtime panic --- Cargo.lock | 1 + core/Cargo.toml | 1 + core/src/runtime/mod.rs | 68 ++++++++++++------- core/src/stark/machine.rs | 4 +- core/src/syscall/precompiles/keccak256/mod.rs | 2 +- core/src/utils/prove.rs | 5 +- 6 files changed, 52 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index de99543221..19df733dae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4357,6 +4357,7 @@ dependencies = [ "strum", "strum_macros", "tempfile", + "thiserror", "tiny-keccak", "tracing", "tracing-forest", diff --git a/core/Cargo.toml b/core/Cargo.toml index e477ec2639..4758fc939b 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -58,6 +58,7 @@ strum = "0.26" web-time = "1.1.0" rayon-scan = "0.1.1" serial_test = "3.1.1" +thiserror = "1.0.60" [dev-dependencies] tiny-keccak = { version = "2.0.2", features = ["keccak"] } diff --git a/core/src/runtime/mod.rs b/core/src/runtime/mod.rs index d5e9363285..6a81f34295 100644 --- a/core/src/runtime/mod.rs +++ b/core/src/runtime/mod.rs @@ -28,13 +28,15 @@ use std::io::Write; use std::rc::Rc; use std::sync::Arc; +use thiserror::Error; + use crate::memory::MemoryInitializeFinalizeEvent; use crate::utils::env; use crate::{alu::AluEvent, cpu::CpuEvent}; pub const MAX_SHARD_CLK: usize = (1 << 24) - 1; -/// An implementation of a runtime for the SP1 VM. +/// An implementation of a runtime for the SP1 RISC-V zkVM. /// /// The runtime is responsible for executing a user program and tracing important events which occur /// during execution (i.e., memory reads, alu operations, etc). @@ -85,6 +87,14 @@ pub struct Runtime { pub emit_events: bool, } +#[derive(Error, Debug)] +pub enum RuntimeError { + #[error("invalid alignment for address {0}")] + InvalidAddressAlignment(u32), + #[error("got unimplemented as opcode")] + Unimplemented(), +} + impl Runtime { // Create a new runtime from a program. pub fn new(program: Program) -> Self { @@ -362,7 +372,6 @@ impl Runtime { } /// Emit a CPU event. - #[allow(clippy::too_many_arguments)] fn emit_cpu( &mut self, shard: u32, @@ -502,7 +511,7 @@ impl Runtime { } /// Execute the given instruction over the current state of the runtime. - fn execute_instruction(&mut self, instruction: Instruction) { + fn execute_instruction(&mut self, instruction: Instruction) -> Result<(), RuntimeError> { let mut pc = self.state.pc; let mut clk = self.state.clk; let mut exit_code = 0u32; @@ -578,7 +587,9 @@ impl Runtime { } Opcode::LH => { (rd, b, c, addr, memory_read_value) = self.load_rr(instruction); - assert_eq!(addr % 2, 0, "addr is not aligned"); + if addr % 2 != 0 { + return Err(RuntimeError::InvalidAddressAlignment(addr)); + } let value = match (addr >> 1) % 2 { 0 => memory_read_value & 0x0000FFFF, 1 => (memory_read_value & 0xFFFF0000) >> 16, @@ -590,7 +601,9 @@ impl Runtime { } Opcode::LW => { (rd, b, c, addr, memory_read_value) = self.load_rr(instruction); - assert_eq!(addr % 4, 0, "addr is not aligned"); + if addr % 4 != 0 { + return Err(RuntimeError::InvalidAddressAlignment(addr)); + } a = memory_read_value; memory_store_value = Some(memory_read_value); self.rw(rd, a); @@ -604,7 +617,9 @@ impl Runtime { } Opcode::LHU => { (rd, b, c, addr, memory_read_value) = self.load_rr(instruction); - assert_eq!(addr % 2, 0, "addr is not aligned"); + if addr % 2 != 0 { + return Err(RuntimeError::InvalidAddressAlignment(addr)); + } let value = match (addr >> 1) % 2 { 0 => memory_read_value & 0x0000FFFF, 1 => (memory_read_value & 0xFFFF0000) >> 16, @@ -630,7 +645,9 @@ impl Runtime { } Opcode::SH => { (a, b, c, addr, memory_read_value) = self.store_rr(instruction); - assert_eq!(addr % 2, 0, "addr is not aligned"); + if addr % 2 != 0 { + return Err(RuntimeError::InvalidAddressAlignment(addr)); + } let value = match (addr >> 1) % 2 { 0 => (a & 0x0000FFFF) + (memory_read_value & 0xFFFF0000), 1 => ((a & 0x0000FFFF) << 16) + (memory_read_value & 0x0000FFFF), @@ -641,7 +658,9 @@ impl Runtime { } Opcode::SW => { (a, b, c, addr, _) = self.store_rr(instruction); - assert_eq!(addr % 4, 0, "addr is not aligned"); + if addr % 4 != 0 { + return Err(RuntimeError::InvalidAddressAlignment(addr)); + } let value = a; memory_store_value = Some(value); self.mw_cpu(align(addr), value, MemoryAccessPosition::Memory); @@ -840,12 +859,14 @@ impl Runtime { self.memory_accesses, exit_code, ); - } + }; + + Ok(()) } /// Executes one cycle of the program, returning whether the program has finished. #[inline] - fn execute_cycle(&mut self) -> bool { + fn execute_cycle(&mut self) -> Result { // Fetch the instruction at the current program counter. let instruction = self.fetch(); @@ -865,23 +886,23 @@ impl Runtime { self.state.clk = 0; } - self.state.pc.wrapping_sub(self.program.pc_base) - >= (self.program.instructions.len() * 4) as u32 + Ok(self.state.pc.wrapping_sub(self.program.pc_base) + >= (self.program.instructions.len() * 4) as u32) } /// Execute up to `self.shard_batch_size` cycles, returning the events emitted and whether the program ended. - pub fn execute_record(&mut self) -> (ExecutionRecord, bool) { + pub fn execute_record(&mut self) -> Result<(ExecutionRecord, bool), RuntimeError> { self.emit_events = true; - let done = self.execute(); - (std::mem::take(&mut self.record), done) + let done = self.execute()?; + Ok((std::mem::take(&mut self.record), done)) } /// Execute up to `self.shard_batch_size` cycles, returning a copy of the prestate and whether the program ended. - pub fn execute_state(&mut self) -> (ExecutionState, bool) { + pub fn execute_state(&mut self) -> Result<(ExecutionState, bool), RuntimeError> { self.emit_events = false; let state = self.state.clone(); - let done = self.execute(); - (state, done) + let done = self.execute()?; + Ok((state, done)) } fn initialize(&mut self) { @@ -907,13 +928,14 @@ impl Runtime { tracing::info!("starting execution"); } - pub fn run(&mut self) { + pub fn run(&mut self) -> Result<(), RuntimeError> { self.emit_events = true; - while !self.execute() {} + while !self.execute()? {} + Ok(()) } /// Executes up to `self.shard_batch_size` cycles of the program, returning whether the program has finished. - fn execute(&mut self) -> bool { + fn execute(&mut self) -> Result { // If it's the first cycle, initialize the program. if self.state.global_clk == 0 { self.initialize(); @@ -924,7 +946,7 @@ impl Runtime { let mut current_shard = self.state.current_shard; let mut num_shards_executed = 0; loop { - if self.execute_cycle() { + if self.execute_cycle()? { done = true; break; } @@ -942,7 +964,7 @@ impl Runtime { self.postprocess(); } - done + Ok(done) } fn postprocess(&mut self) { diff --git a/core/src/stark/machine.rs b/core/src/stark/machine.rs index 89638765f3..03806e385b 100644 --- a/core/src/stark/machine.rs +++ b/core/src/stark/machine.rs @@ -254,9 +254,7 @@ impl>> StarkMachine { // Display some statistics about the workload. let stats = record.stats(); - for (k, v) in stats { - log::info!("{} = {}", k, v); - } + log::info!("Shard: {:?}", stats); // For each chip, shard the events into segments. record.shard(config) diff --git a/core/src/syscall/precompiles/keccak256/mod.rs b/core/src/syscall/precompiles/keccak256/mod.rs index f0b0a71144..b44ce53f05 100644 --- a/core/src/syscall/precompiles/keccak256/mod.rs +++ b/core/src/syscall/precompiles/keccak256/mod.rs @@ -75,7 +75,7 @@ pub mod permute_tests { utils::setup_logger(); let program = keccak_permute_program(); let mut runtime = Runtime::new(program); - runtime.run() + runtime.run().unwrap(); } #[test] diff --git a/core/src/utils/prove.rs b/core/src/utils/prove.rs index 515672a646..664eb5fd18 100644 --- a/core/src/utils/prove.rs +++ b/core/src/utils/prove.rs @@ -128,7 +128,8 @@ fn trace_checkpoint(program: Program, file: &File) -> ExecutionRecord { let mut reader = std::io::BufReader::new(file); let state = bincode::deserialize_from(&mut reader).expect("failed to deserialize state"); let mut runtime = Runtime::recover(program.clone(), state); - let (events, _) = tracing::debug_span!("runtime.trace").in_scope(|| runtime.execute_record()); + let (events, _) = + tracing::debug_span!("runtime.trace").in_scope(|| runtime.execute_record().unwrap()); events } @@ -181,7 +182,7 @@ where let (public_values_stream, public_values) = tracing::info_span!("runtime.state").in_scope(|| loop { // Get checkpoint + move to next checkpoint, then save checkpoint to temp file - let (state, done) = runtime.execute_state(); + let (state, done) = runtime.execute_state().unwrap(); let mut tempfile = tempfile::tempfile().expect("failed to create tempfile"); let mut writer = std::io::BufWriter::new(&mut tempfile); bincode::serialize_into(&mut writer, &state).expect("failed to serialize state"); From c4e85a63dae039512758a8367ff1e3d5e87b9db7 Mon Sep 17 00:00:00 2001 From: John Guibas Date: Sat, 11 May 2024 13:08:10 -0700 Subject: [PATCH 2/7] hm --- core/benches/main.rs | 2 +- core/src/cpu/trace.rs | 2 +- core/src/lookup/debug.rs | 2 +- core/src/memory/global.rs | 8 +++--- core/src/runtime/io.rs | 4 +-- core/src/runtime/mod.rs | 51 ++++++++++++++++++++------------------- core/src/utils/prove.rs | 6 ++--- eval/src/main.rs | 6 ++--- prover/src/lib.rs | 8 +++--- prover/src/utils.rs | 2 +- sdk/src/lib.rs | 2 +- sdk/src/provers/mock.rs | 2 +- 12 files changed, 48 insertions(+), 47 deletions(-) diff --git a/core/benches/main.rs b/core/benches/main.rs index 86e6083448..99312c5c73 100644 --- a/core/benches/main.rs +++ b/core/benches/main.rs @@ -13,7 +13,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { let program = Program::from_elf(&elf_path); let cycles = { let mut runtime = Runtime::new(program.clone()); - runtime.run(); + runtime.run().unwrap(); runtime.state.global_clk }; group.bench_function( diff --git a/core/src/cpu/trace.rs b/core/src/cpu/trace.rs index e5d5b8320d..7000fe0624 100644 --- a/core/src/cpu/trace.rs +++ b/core/src/cpu/trace.rs @@ -662,7 +662,7 @@ mod tests { fn generate_trace_simple_program() { let program = simple_program(); let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); let chip = CpuChip::default(); let trace: RowMajorMatrix = chip.generate_trace(&runtime.record, &mut ExecutionRecord::default()); diff --git a/core/src/lookup/debug.rs b/core/src/lookup/debug.rs index d9d1dd241f..c58d882b7e 100644 --- a/core/src/lookup/debug.rs +++ b/core/src/lookup/debug.rs @@ -223,7 +223,7 @@ mod test { let machine = RiscvAir::machine(config); let (pk, _) = machine.setup(&program); let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); let shards = machine.shard(runtime.record, &ShardingConfig::default()); let ok = debug_interactions_with_all_chips(&machine, &pk, &shards, InteractionKind::all_kinds()); diff --git a/core/src/memory/global.rs b/core/src/memory/global.rs index e5fa81d378..5c9974e09f 100644 --- a/core/src/memory/global.rs +++ b/core/src/memory/global.rs @@ -185,7 +185,7 @@ mod tests { fn test_memory_generate_trace() { let program = simple_program(); let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); let shard = runtime.record.clone(); let chip: MemoryChip = MemoryChip::new(MemoryChipType::Initialize); @@ -211,7 +211,7 @@ mod tests { let program = simple_program(); let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); let chip = MemoryChip::new(MemoryChipType::Initialize); @@ -229,7 +229,7 @@ mod tests { let program = sha_extend_program(); let program_clone = program.clone(); let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); let machine: crate::stark::StarkMachine> = RiscvAir::machine(BabyBearPoseidon2::new()); let (pkey, _) = machine.setup(&program_clone); @@ -252,7 +252,7 @@ mod tests { let program = sha_extend_program(); let program_clone = program.clone(); let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); let machine = RiscvAir::machine(BabyBearPoseidon2::new()); let (pkey, _) = machine.setup(&program_clone); let shards = machine.shard( diff --git a/core/src/runtime/io.rs b/core/src/runtime/io.rs index 14ba344501..9f86a3b43b 100644 --- a/core/src/runtime/io.rs +++ b/core/src/runtime/io.rs @@ -93,7 +93,7 @@ pub mod tests { let points = points(); runtime.write_stdin(&points.0); runtime.write_stdin(&points.1); - runtime.run(); + runtime.run().unwrap(); let added_point = runtime.read_public_values::(); assert_eq!( added_point, @@ -113,7 +113,7 @@ pub mod tests { let points = points(); runtime.write_stdin(&points.0); runtime.write_stdin(&points.1); - runtime.run(); + runtime.run().unwrap(); let config = BabyBearBlake3::new(); prove_core(config, runtime); } diff --git a/core/src/runtime/mod.rs b/core/src/runtime/mod.rs index 6a81f34295..86ab6b4be2 100644 --- a/core/src/runtime/mod.rs +++ b/core/src/runtime/mod.rs @@ -372,6 +372,7 @@ impl Runtime { } /// Emit a CPU event. + #[allow(clippy::too_many_arguments)] fn emit_cpu( &mut self, shard: u32, @@ -874,7 +875,7 @@ impl Runtime { self.log(&instruction); // Execute the instruction. - self.execute_instruction(instruction); + self.execute_instruction(instruction)?; // Increment the clock. self.state.global_clk += 1; @@ -1062,7 +1063,7 @@ pub mod tests { fn test_simple_program_run() { let program = simple_program(); let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); assert_eq!(runtime.register(Register::X31), 42); } @@ -1079,7 +1080,7 @@ pub mod tests { ]; let program = Program::new(instructions, 0, 0); let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); assert_eq!(runtime.register(Register::X31), 42); } @@ -1096,7 +1097,7 @@ pub mod tests { let program = Program::new(instructions, 0, 0); let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); assert_eq!(runtime.register(Register::X31), 32); } @@ -1113,7 +1114,7 @@ pub mod tests { let program = Program::new(instructions, 0, 0); let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); assert_eq!(runtime.register(Register::X31), 32); } @@ -1131,7 +1132,7 @@ pub mod tests { let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); assert_eq!(runtime.register(Register::X31), 37); } @@ -1148,7 +1149,7 @@ pub mod tests { let program = Program::new(instructions, 0, 0); let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); assert_eq!(runtime.register(Register::X31), 5); } @@ -1165,7 +1166,7 @@ pub mod tests { let program = Program::new(instructions, 0, 0); let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); assert_eq!(runtime.register(Register::X31), 1184); } @@ -1182,7 +1183,7 @@ pub mod tests { let program = Program::new(instructions, 0, 0); let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); assert_eq!(runtime.register(Register::X31), 1); } @@ -1199,7 +1200,7 @@ pub mod tests { let program = Program::new(instructions, 0, 0); let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); assert_eq!(runtime.register(Register::X31), 1); } @@ -1216,7 +1217,7 @@ pub mod tests { let program = Program::new(instructions, 0, 0); let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); assert_eq!(runtime.register(Register::X31), 0); } @@ -1233,7 +1234,7 @@ pub mod tests { let program = Program::new(instructions, 0, 0); let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); assert_eq!(runtime.register(Register::X31), 0); } @@ -1250,7 +1251,7 @@ pub mod tests { let program = Program::new(instructions, 0, 0); let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); assert_eq!(runtime.register(Register::X31), 84); } @@ -1266,7 +1267,7 @@ pub mod tests { ]; let program = Program::new(instructions, 0, 0); let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); assert_eq!(runtime.register(Register::X31), 5 - 1 + 4); } @@ -1282,7 +1283,7 @@ pub mod tests { ]; let program = Program::new(instructions, 0, 0); let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); assert_eq!(runtime.register(Register::X31), 10); } @@ -1298,7 +1299,7 @@ pub mod tests { ]; let program = Program::new(instructions, 0, 0); let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); assert_eq!(runtime.register(Register::X31), 47); } @@ -1314,7 +1315,7 @@ pub mod tests { ]; let program = Program::new(instructions, 0, 0); let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); assert_eq!(runtime.register(Register::X31), 0); } @@ -1328,7 +1329,7 @@ pub mod tests { ]; let program = Program::new(instructions, 0, 0); let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); assert_eq!(runtime.register(Register::X31), 80); } @@ -1342,7 +1343,7 @@ pub mod tests { ]; let program = Program::new(instructions, 0, 0); let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); assert_eq!(runtime.register(Register::X31), 2); } @@ -1356,7 +1357,7 @@ pub mod tests { ]; let program = Program::new(instructions, 0, 0); let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); assert_eq!(runtime.register(Register::X31), 2); } @@ -1370,7 +1371,7 @@ pub mod tests { ]; let program = Program::new(instructions, 0, 0); let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); assert_eq!(runtime.register(Register::X31), 0); } @@ -1384,7 +1385,7 @@ pub mod tests { ]; let program = Program::new(instructions, 0, 0); let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); assert_eq!(runtime.register(Register::X31), 0); } @@ -1403,7 +1404,7 @@ pub mod tests { ]; let program = Program::new(instructions, 0, 0); let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); assert_eq!(runtime.registers()[Register::X5 as usize], 8); assert_eq!(runtime.registers()[Register::X11 as usize], 100); assert_eq!(runtime.state.pc, 108); @@ -1417,7 +1418,7 @@ pub mod tests { ]; let program = Program::new(instructions, 0, 0); let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); assert_eq!(runtime.registers()[Register::X12 as usize], expected); } @@ -1635,7 +1636,7 @@ pub mod tests { fn test_simple_memory_program_run() { let program = simple_memory_program(); let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); // Assert SW & LW case assert_eq!(runtime.register(Register::X28), 0x12348765); diff --git a/core/src/utils/prove.rs b/core/src/utils/prove.rs index 664eb5fd18..f70a2498da 100644 --- a/core/src/utils/prove.rs +++ b/core/src/utils/prove.rs @@ -37,7 +37,7 @@ pub fn run_test_io( let runtime = tracing::info_span!("runtime.run(...)").in_scope(|| { let mut runtime = Runtime::new(program); runtime.write_vecs(&inputs.buffer); - runtime.run(); + runtime.run().unwrap(); runtime }); let public_values = SP1PublicValues::from(&runtime.state.public_values_stream); @@ -53,7 +53,7 @@ pub fn run_test( > { let runtime = tracing::info_span!("runtime.run(...)").in_scope(|| { let mut runtime = Runtime::new(program); - runtime.run(); + runtime.run().unwrap(); runtime }); run_test_core(runtime) @@ -164,7 +164,7 @@ where // If we don't need to batch, we can just run the program normally and prove it. if !should_batch { - runtime.run(); + runtime.run().unwrap(); #[cfg(feature = "debug")] { let record_clone = runtime.record.clone(); diff --git a/eval/src/main.rs b/eval/src/main.rs index ee637ba69f..5dabda1d13 100644 --- a/eval/src/main.rs +++ b/eval/src/main.rs @@ -139,7 +139,7 @@ fn run_evaluation(hashfn: &HashFnId, program: &Program, _elf: &[u8]) -> (f64, f6 HashFnId::Blake3 => { let mut runtime = Runtime::new(program.clone()); let execution_start = Instant::now(); - runtime.run(); + runtime.run().unwrap(); let execution_duration = execution_start.elapsed().as_secs_f64(); let config = BabyBearBlake3::new(); @@ -156,7 +156,7 @@ fn run_evaluation(hashfn: &HashFnId, program: &Program, _elf: &[u8]) -> (f64, f6 HashFnId::Poseidon => { let mut runtime = Runtime::new(program.clone()); let execution_start = Instant::now(); - runtime.run(); + runtime.run().unwrap(); let execution_duration = execution_start.elapsed().as_secs_f64(); let config = BabyBearPoseidon2::new(); @@ -173,7 +173,7 @@ fn run_evaluation(hashfn: &HashFnId, program: &Program, _elf: &[u8]) -> (f64, f6 HashFnId::Keccak256 => { let mut runtime = Runtime::new(program.clone()); let execution_start = Instant::now(); - runtime.run(); + runtime.run().unwrap(); let execution_duration = execution_start.elapsed().as_secs_f64(); let config = BabyBearKeccak::new(); diff --git a/prover/src/lib.rs b/prover/src/lib.rs index 5281728cb9..dc7442ec6a 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -28,7 +28,7 @@ use rayon::iter::{IntoParallelIterator, ParallelIterator}; use rayon::prelude::*; use sp1_core::air::PublicValues; pub use sp1_core::io::{SP1PublicValues, SP1Stdin}; -use sp1_core::runtime::Runtime; +use sp1_core::runtime::{Runtime, RuntimeError}; use sp1_core::stark::MachineVerificationError; use sp1_core::stark::{Challenge, StarkProvingKey}; use sp1_core::utils::DIGEST_SIZE; @@ -217,15 +217,15 @@ impl SP1Prover { /// Generate a proof of an SP1 program with the specified inputs. #[instrument(name = "execute", level = "info", skip_all)] - pub fn execute(elf: &[u8], stdin: &SP1Stdin) -> SP1PublicValues { + pub fn execute(elf: &[u8], stdin: &SP1Stdin) -> Result { let program = Program::from(elf); let mut runtime = Runtime::new(program); runtime.write_vecs(&stdin.buffer); for (proof, vkey) in stdin.proofs.iter() { runtime.write_proof(proof.clone(), vkey.clone()); } - runtime.run(); - SP1PublicValues::from(&runtime.state.public_values_stream) + runtime.run()?; + Ok(SP1PublicValues::from(&runtime.state.public_values_stream)) } /// Generate shard proofs which split up and prove the valid execution of a RISC-V program with diff --git a/prover/src/utils.rs b/prover/src/utils.rs index 8983efdefe..6da786033d 100644 --- a/prover/src/utils.rs +++ b/prover/src/utils.rs @@ -30,7 +30,7 @@ pub fn get_cycles(elf: &[u8], stdin: &SP1Stdin) -> u64 { let program = Program::from(elf); let mut runtime = Runtime::new(program); runtime.write_vecs(&stdin.buffer); - runtime.run(); + runtime.run().unwrap(); runtime.state.global_clk } diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index 7d4e47cc06..208d84f6ad 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -173,7 +173,7 @@ impl ProverClient { /// let public_values = client.execute(elf, stdin).unwrap(); /// ``` pub fn execute(&self, elf: &[u8], stdin: SP1Stdin) -> Result { - Ok(SP1Prover::execute(elf, &stdin)) + Ok(SP1Prover::execute(elf, &stdin)?) } /// Setup a program to be proven and verified by the SP1 RISC-V zkVM by computing the proving diff --git a/sdk/src/provers/mock.rs b/sdk/src/provers/mock.rs index addb91b257..9f25eafd38 100644 --- a/sdk/src/provers/mock.rs +++ b/sdk/src/provers/mock.rs @@ -33,7 +33,7 @@ impl Prover for MockProver { } fn prove(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result { - let public_values = SP1Prover::execute(&pk.elf, &stdin); + let public_values = SP1Prover::execute(&pk.elf, &stdin)?; Ok(SP1ProofWithPublicValues { proof: vec![], stdin, From 69c05a376f6530915d2aaf46f0cd919720b1843e Mon Sep 17 00:00:00 2001 From: John Guibas Date: Sat, 11 May 2024 13:48:46 -0700 Subject: [PATCH 3/7] ghm --- core/src/runtime/io.rs | 2 +- core/src/runtime/mod.rs | 24 +- core/src/stark/machine.rs | 2 +- core/src/syscall/precompiles/keccak256/air.rs | 2 +- core/src/utils/prove.rs | 366 +++++++++--------- prover/src/build.rs | 2 +- prover/src/lib.rs | 20 +- recursion/program/src/machine/mod.rs | 3 +- 8 files changed, 219 insertions(+), 202 deletions(-) diff --git a/core/src/runtime/io.rs b/core/src/runtime/io.rs index 9f86a3b43b..996ef5d438 100644 --- a/core/src/runtime/io.rs +++ b/core/src/runtime/io.rs @@ -115,6 +115,6 @@ pub mod tests { runtime.write_stdin(&points.1); runtime.run().unwrap(); let config = BabyBearBlake3::new(); - prove_core(config, runtime); + prove_core(config, runtime).unwrap(); } } diff --git a/core/src/runtime/mod.rs b/core/src/runtime/mod.rs index 86ab6b4be2..c7617a1e34 100644 --- a/core/src/runtime/mod.rs +++ b/core/src/runtime/mod.rs @@ -88,7 +88,7 @@ pub struct Runtime { } #[derive(Error, Debug)] -pub enum RuntimeError { +pub enum ExecutionError { #[error("invalid alignment for address {0}")] InvalidAddressAlignment(u32), #[error("got unimplemented as opcode")] @@ -512,7 +512,7 @@ impl Runtime { } /// Execute the given instruction over the current state of the runtime. - fn execute_instruction(&mut self, instruction: Instruction) -> Result<(), RuntimeError> { + fn execute_instruction(&mut self, instruction: Instruction) -> Result<(), ExecutionError> { let mut pc = self.state.pc; let mut clk = self.state.clk; let mut exit_code = 0u32; @@ -589,7 +589,7 @@ impl Runtime { Opcode::LH => { (rd, b, c, addr, memory_read_value) = self.load_rr(instruction); if addr % 2 != 0 { - return Err(RuntimeError::InvalidAddressAlignment(addr)); + return Err(ExecutionError::InvalidAddressAlignment(addr)); } let value = match (addr >> 1) % 2 { 0 => memory_read_value & 0x0000FFFF, @@ -603,7 +603,7 @@ impl Runtime { Opcode::LW => { (rd, b, c, addr, memory_read_value) = self.load_rr(instruction); if addr % 4 != 0 { - return Err(RuntimeError::InvalidAddressAlignment(addr)); + return Err(ExecutionError::InvalidAddressAlignment(addr)); } a = memory_read_value; memory_store_value = Some(memory_read_value); @@ -619,7 +619,7 @@ impl Runtime { Opcode::LHU => { (rd, b, c, addr, memory_read_value) = self.load_rr(instruction); if addr % 2 != 0 { - return Err(RuntimeError::InvalidAddressAlignment(addr)); + return Err(ExecutionError::InvalidAddressAlignment(addr)); } let value = match (addr >> 1) % 2 { 0 => memory_read_value & 0x0000FFFF, @@ -647,7 +647,7 @@ impl Runtime { Opcode::SH => { (a, b, c, addr, memory_read_value) = self.store_rr(instruction); if addr % 2 != 0 { - return Err(RuntimeError::InvalidAddressAlignment(addr)); + return Err(ExecutionError::InvalidAddressAlignment(addr)); } let value = match (addr >> 1) % 2 { 0 => (a & 0x0000FFFF) + (memory_read_value & 0xFFFF0000), @@ -660,7 +660,7 @@ impl Runtime { Opcode::SW => { (a, b, c, addr, _) = self.store_rr(instruction); if addr % 4 != 0 { - return Err(RuntimeError::InvalidAddressAlignment(addr)); + return Err(ExecutionError::InvalidAddressAlignment(addr)); } let value = a; memory_store_value = Some(value); @@ -867,7 +867,7 @@ impl Runtime { /// Executes one cycle of the program, returning whether the program has finished. #[inline] - fn execute_cycle(&mut self) -> Result { + fn execute_cycle(&mut self) -> Result { // Fetch the instruction at the current program counter. let instruction = self.fetch(); @@ -892,14 +892,14 @@ impl Runtime { } /// Execute up to `self.shard_batch_size` cycles, returning the events emitted and whether the program ended. - pub fn execute_record(&mut self) -> Result<(ExecutionRecord, bool), RuntimeError> { + pub fn execute_record(&mut self) -> Result<(ExecutionRecord, bool), ExecutionError> { self.emit_events = true; let done = self.execute()?; Ok((std::mem::take(&mut self.record), done)) } /// Execute up to `self.shard_batch_size` cycles, returning a copy of the prestate and whether the program ended. - pub fn execute_state(&mut self) -> Result<(ExecutionState, bool), RuntimeError> { + pub fn execute_state(&mut self) -> Result<(ExecutionState, bool), ExecutionError> { self.emit_events = false; let state = self.state.clone(); let done = self.execute()?; @@ -929,14 +929,14 @@ impl Runtime { tracing::info!("starting execution"); } - pub fn run(&mut self) -> Result<(), RuntimeError> { + pub fn run(&mut self) -> Result<(), ExecutionError> { self.emit_events = true; while !self.execute()? {} Ok(()) } /// Executes up to `self.shard_batch_size` cycles of the program, returning whether the program has finished. - fn execute(&mut self) -> Result { + fn execute(&mut self) -> Result { // If it's the first cycle, initialize the program. if self.state.global_clk == 0 { self.initialize(); diff --git a/core/src/stark/machine.rs b/core/src/stark/machine.rs index 03806e385b..29c361de33 100644 --- a/core/src/stark/machine.rs +++ b/core/src/stark/machine.rs @@ -669,7 +669,7 @@ pub mod tests { setup_logger(); let program = fibonacci_program(); let stdin = SP1Stdin::new(); - run_and_prove(program, &stdin, BabyBearPoseidon2::new()); + run_and_prove(program, &stdin, BabyBearPoseidon2::new()).unwrap(); } #[test] diff --git a/core/src/syscall/precompiles/keccak256/air.rs b/core/src/syscall/precompiles/keccak256/air.rs index 36360af218..3de40c696e 100644 --- a/core/src/syscall/precompiles/keccak256/air.rs +++ b/core/src/syscall/precompiles/keccak256/air.rs @@ -169,7 +169,7 @@ mod test { let config = BabyBearPoseidon2::new(); let program = Program::from(KECCAK256_ELF); - let (proof, public_values) = run_and_prove(program, &stdin, config); + let (proof, public_values) = run_and_prove(program, &stdin, config).unwrap(); let mut public_values = SP1PublicValues::from(&public_values); let config = BabyBearPoseidon2::new(); diff --git a/core/src/utils/prove.rs b/core/src/utils/prove.rs index f70a2498da..5c9f4a0539 100644 --- a/core/src/utils/prove.rs +++ b/core/src/utils/prove.rs @@ -1,26 +1,30 @@ use std::fs::File; +use std::io; use std::io::{Seek, Write}; use web_time::Instant; -use crate::air::MachineAir; -use crate::io::{SP1PublicValues, SP1Stdin}; pub use baby_bear_blake3::BabyBearBlake3; use p3_challenger::CanObserve; use p3_field::PrimeField32; use serde::de::DeserializeOwned; use serde::Serialize; use size::Size; +use thiserror::Error; +use crate::air::MachineAir; +use crate::io::{SP1PublicValues, SP1Stdin}; use crate::lookup::InteractionBuilder; +use crate::runtime::ExecutionError; use crate::runtime::{ExecutionRecord, ShardingConfig}; use crate::stark::DebugConstraintBuilder; +use crate::stark::MachineProof; use crate::stark::ProverConstraintFolder; use crate::stark::StarkVerifyingKey; use crate::stark::Val; use crate::stark::VerifierConstraintFolder; use crate::stark::{Com, PcsProverData, RiscvAir, ShardProof, StarkProvingKey, UniConfig}; use crate::stark::{MachineRecord, StarkMachine}; -use crate::utils::env::shard_batch_size; +use crate::utils::env; use crate::{ runtime::{Program, Runtime}, stark::StarkGenericConfig, @@ -29,120 +33,56 @@ use crate::{ const LOG_DEGREE_BOUND: usize = 31; -/// Runs a program and returns the public values stream. -pub fn run_test_io( - program: Program, - inputs: SP1Stdin, -) -> Result> { - let runtime = tracing::info_span!("runtime.run(...)").in_scope(|| { - let mut runtime = Runtime::new(program); - runtime.write_vecs(&inputs.buffer); - runtime.run().unwrap(); - runtime - }); - let public_values = SP1PublicValues::from(&runtime.state.public_values_stream); - let _ = run_test_core(runtime)?; - Ok(public_values) +#[derive(Error, Debug)] +pub enum SP1CoreProvingError { + #[error("failed to execute program: {0}")] + ExecutionError(ExecutionError), + #[error("io error: {0}")] + IoError(io::Error), + #[error("serialization error: {0}")] + SerializationError(bincode::Error), } -pub fn run_test( - program: Program, -) -> Result< - crate::stark::MachineProof, - crate::stark::MachineVerificationError, -> { - let runtime = tracing::info_span!("runtime.run(...)").in_scope(|| { - let mut runtime = Runtime::new(program); - runtime.run().unwrap(); - runtime - }); - run_test_core(runtime) -} - -#[allow(unused_variables)] -pub fn run_test_core( +pub fn prove_core( + config: SC, runtime: Runtime, -) -> Result< - crate::stark::MachineProof, - crate::stark::MachineVerificationError, -> { - let config = BabyBearPoseidon2::new(); - let machine = RiscvAir::machine(config); - let (pk, vk) = machine.setup(runtime.program.as_ref()); - - let record = runtime.record; - run_test_machine(record, machine, pk, vk) -} - -#[allow(unused_variables)] -pub fn run_test_machine( - record: A::Record, - machine: StarkMachine, - pk: StarkProvingKey, - vk: StarkVerifyingKey, -) -> Result, crate::stark::MachineVerificationError> +) -> Result, SP1CoreProvingError> where - A: MachineAir - + for<'a> Air> - + Air>> - + for<'a> Air> - + for<'a> Air, SC::Challenge>>, - SC: StarkGenericConfig, - SC::Val: p3_field::PrimeField32, SC::Challenger: Clone, + OpeningProof: Send + Sync, Com: Send + Sync, PcsProverData: Send + Sync, - OpeningProof: Send + Sync, ShardMainData: Serialize + DeserializeOwned, + ::Val: PrimeField32, { - #[cfg(feature = "debug")] - { - let mut challenger_clone = machine.config().challenger(); - let record_clone = record.clone(); - machine.debug_constraints(&pk, record_clone, &mut challenger_clone); - } - let stats = record.stats().clone(); - let cycles = stats.get("cpu_events").unwrap(); + // Setup the machine. + let machine = RiscvAir::machine(config); + let (pk, _) = machine.setup(runtime.program.as_ref()); - let start = Instant::now(); + // Prove the program. let mut challenger = machine.config().challenger(); - let proof = machine.prove::>(&pk, record, &mut challenger); - let time = start.elapsed().as_millis(); + let proving_start = Instant::now(); + let proof = machine.prove::>(&pk, runtime.record, &mut challenger); + let proving_duration = proving_start.elapsed().as_millis(); let nb_bytes = bincode::serialize(&proof).unwrap().len(); - let mut challenger = machine.config().challenger(); - machine.verify(&vk, &proof, &mut challenger)?; - + // Print the summary. tracing::info!( "summary: cycles={}, e2e={}, khz={:.2}, proofSize={}", - cycles, - time, - (*cycles as f64 / time as f64), + runtime.state.global_clk, + proving_duration, + (runtime.state.global_clk as f64 / proving_duration as f64), Size::from_bytes(nb_bytes), ); Ok(proof) } -fn trace_checkpoint(program: Program, file: &File) -> ExecutionRecord { - let mut reader = std::io::BufReader::new(file); - let state = bincode::deserialize_from(&mut reader).expect("failed to deserialize state"); - let mut runtime = Runtime::recover(program.clone(), state); - let (events, _) = - tracing::debug_span!("runtime.trace").in_scope(|| runtime.execute_record().unwrap()); - events -} - -fn reset_seek(file: &mut File) { - file.seek(std::io::SeekFrom::Start(0)) - .expect("failed to seek to start of tempfile"); -} - pub fn run_and_prove( program: Program, stdin: &SP1Stdin, config: SC, -) -> (crate::stark::MachineProof, Vec) +) -> Result<(MachineProof, Vec), SP1CoreProvingError> where SC::Challenger: Clone, OpeningProof: Send + Sync, @@ -151,83 +91,87 @@ where ShardMainData: Serialize + DeserializeOwned, ::Val: PrimeField32, { - let mut challenger = config.challenger(); + let proving_start = Instant::now(); - let machine = RiscvAir::machine(config); + // Execute the program. let mut runtime = Runtime::new(program.clone()); runtime.write_vecs(&stdin.buffer); for proof in stdin.proofs.iter() { runtime.write_proof(proof.0.clone(), proof.1.clone()); } + + // Setup the machine. + let machine = RiscvAir::machine(config); let (pk, vk) = machine.setup(runtime.program.as_ref()); - let should_batch = shard_batch_size() > 0; // If we don't need to batch, we can just run the program normally and prove it. - if !should_batch { - runtime.run().unwrap(); + if env::shard_batch_size() == 0 { + // Execute the runtime and collect all the events.. + runtime.run().map_err(SP1CoreProvingError::ExecutionError)?; + + // If debugging is enabled, we will also debug the constraints. #[cfg(feature = "debug")] { - let record_clone = runtime.record.clone(); - machine.debug_constraints(&pk, record_clone, &mut challenger); + let mut challenger = machine.config().challenger(); + machine.debug_constraints(&pk, runtime.record.clone(), &mut challenger); } + + // Generate the proof and return the proof and public values. let public_values = std::mem::take(&mut runtime.state.public_values_stream); - let proof = prove_core(machine.config().clone(), runtime); - return (proof, public_values); + let proof = prove_core(machine.config().clone(), runtime)?; + return Ok((proof, public_values)); } // Execute the program, saving checkpoints at the start of every `shard_batch_size` cycle range. - let mut cycles = 0; - let mut prove_time = 0; let mut checkpoints = Vec::new(); - let (public_values_stream, public_values) = - tracing::info_span!("runtime.state").in_scope(|| loop { - // Get checkpoint + move to next checkpoint, then save checkpoint to temp file - let (state, done) = runtime.execute_state().unwrap(); - let mut tempfile = tempfile::tempfile().expect("failed to create tempfile"); - let mut writer = std::io::BufWriter::new(&mut tempfile); - bincode::serialize_into(&mut writer, &state).expect("failed to serialize state"); - writer.flush().expect("failed to flush writer"); - drop(writer); - tempfile - .seek(std::io::SeekFrom::Start(0)) - .expect("failed to seek to start of tempfile"); - checkpoints.push(tempfile); - if done { - return ( - std::mem::take(&mut runtime.state.public_values_stream), - runtime.record.public_values, - ); - } - }); + let (public_values_stream, public_values) = loop { + // Execute the runtime until we reach a checkpoint. + let (checkpoint, done) = runtime + .execute_state() + .map_err(SP1CoreProvingError::ExecutionError)?; + + // Save the checkpoint to a temp file. + let mut tempfile = tempfile::tempfile().map_err(SP1CoreProvingError::IoError)?; + let mut writer = std::io::BufWriter::new(&mut tempfile); + bincode::serialize_into(&mut writer, &checkpoint) + .map_err(SP1CoreProvingError::SerializationError)?; + writer.flush().map_err(SP1CoreProvingError::IoError)?; + drop(writer); + tempfile + .seek(std::io::SeekFrom::Start(0)) + .map_err(SP1CoreProvingError::IoError)?; + checkpoints.push(tempfile); + + // If we've reached the final checkpoint, break out of the loop. + if done { + break ( + std::mem::take(&mut runtime.state.public_values_stream), + runtime.record.public_values, + ); + } + }; // For each checkpoint, generate events, shard them, commit shards, and observe in challenger. let sharding_config = ShardingConfig::default(); let mut shard_main_datas = Vec::new(); + let mut challenger = machine.config().challenger(); + vk.observe_into(&mut challenger); + for checkpoint_file in checkpoints.iter_mut() { + let mut record = trace_checkpoint(program.clone(), checkpoint_file); + record.public_values = public_values; + reset_seek(&mut *checkpoint_file); - // If there's only one batch, it already must fit in memory so reuse it later in open multi - // rather than running the runtime again. - let reuse_shards = checkpoints.len() == 1; - let mut all_shards = None; + // Shard the record into shards. + let checkpoint_shards = + tracing::info_span!("shard").in_scope(|| machine.shard(record, &sharding_config)); - vk.observe_into(&mut challenger); - for file in checkpoints.iter_mut() { - let mut events = trace_checkpoint(program.clone(), file); - events.public_values = public_values; - - reset_seek(&mut *file); - cycles += events.cpu_events.len(); - let shards = - tracing::debug_span!("shard").in_scope(|| machine.shard(events, &sharding_config)); + // Commit to each shard. let (commitments, commit_data) = tracing::info_span!("commit") - .in_scope(|| LocalProver::commit_shards(&machine, &shards)); - + .in_scope(|| LocalProver::commit_shards(&machine, &checkpoint_shards)); shard_main_datas.push(commit_data); - if reuse_shards { - all_shards = Some(shards.clone()); - } - - for (commitment, shard) in commitments.into_iter().zip(shards.iter()) { + // Observe the commitments. + for (commitment, shard) in commitments.into_iter().zip(checkpoint_shards.iter()) { challenger.observe(commitment); challenger.observe_slice(&shard.public_values::()[0..machine.num_pv_elts()]); } @@ -235,17 +179,14 @@ where // For each checkpoint, generate events and shard again, then prove the shards. let mut shard_proofs = Vec::>::new(); - for mut file in checkpoints.into_iter() { - let shards = if reuse_shards { - Option::take(&mut all_shards).unwrap() - } else { - let mut events = trace_checkpoint(program.clone(), &file); + for mut checkpoint_file in checkpoints.into_iter() { + let checkpoint_shards = { + let mut events = trace_checkpoint(program.clone(), &checkpoint_file); events.public_values = public_values; - reset_seek(&mut file); + reset_seek(&mut checkpoint_file); tracing::debug_span!("shard").in_scope(|| machine.shard(events, &sharding_config)) }; - let start = Instant::now(); - let mut new_proofs = shards + let mut checkpoint_proofs = checkpoint_shards .into_iter() .map(|shard| { let config = machine.config(); @@ -266,59 +207,130 @@ where ) }) .collect::>(); - prove_time += start.elapsed().as_millis(); - shard_proofs.append(&mut new_proofs); + shard_proofs.append(&mut checkpoint_proofs); } + let proof = MachineProof:: { shard_proofs }; - let proof = crate::stark::MachineProof:: { shard_proofs }; - - // Prove the program. - let nb_bytes = bincode::serialize(&proof).unwrap().len(); - + // Print the summary. + let proving_time = proving_start.elapsed().as_secs_f64(); tracing::info!( "summary: cycles={}, e2e={}, khz={:.2}, proofSize={}", - cycles, - prove_time, - (cycles as f64 / prove_time as f64), - Size::from_bytes(nb_bytes), + runtime.state.global_clk, + proving_time, + (runtime.state.global_clk as f64 / proving_time as f64), + bincode::serialize(&proof).unwrap().len(), ); - (proof, public_values_stream) + Ok((proof, public_values_stream)) } -pub fn prove_core( - config: SC, +/// Runs a program and returns the public values stream. +pub fn run_test_io( + program: Program, + inputs: SP1Stdin, +) -> Result> { + let runtime = tracing::info_span!("runtime.run(...)").in_scope(|| { + let mut runtime = Runtime::new(program); + runtime.write_vecs(&inputs.buffer); + runtime.run().unwrap(); + runtime + }); + let public_values = SP1PublicValues::from(&runtime.state.public_values_stream); + let _ = run_test_core(runtime)?; + Ok(public_values) +} + +pub fn run_test( + program: Program, +) -> Result< + crate::stark::MachineProof, + crate::stark::MachineVerificationError, +> { + let runtime = tracing::info_span!("runtime.run(...)").in_scope(|| { + let mut runtime = Runtime::new(program); + runtime.run().unwrap(); + runtime + }); + run_test_core(runtime) +} + +#[allow(unused_variables)] +pub fn run_test_core( runtime: Runtime, -) -> crate::stark::MachineProof +) -> Result< + crate::stark::MachineProof, + crate::stark::MachineVerificationError, +> { + let config = BabyBearPoseidon2::new(); + let machine = RiscvAir::machine(config); + let (pk, vk) = machine.setup(runtime.program.as_ref()); + + let record = runtime.record; + run_test_machine(record, machine, pk, vk) +} + +#[allow(unused_variables)] +pub fn run_test_machine( + record: A::Record, + machine: StarkMachine, + pk: StarkProvingKey, + vk: StarkVerifyingKey, +) -> Result, crate::stark::MachineVerificationError> where + A: MachineAir + + for<'a> Air> + + Air>> + + for<'a> Air> + + for<'a> Air, SC::Challenge>>, + SC: StarkGenericConfig, + SC::Val: p3_field::PrimeField32, SC::Challenger: Clone, - OpeningProof: Send + Sync, Com: Send + Sync, PcsProverData: Send + Sync, + OpeningProof: Send + Sync, ShardMainData: Serialize + DeserializeOwned, - ::Val: PrimeField32, { - let mut challenger = config.challenger(); - - let machine = RiscvAir::machine(config); - let (pk, _) = machine.setup(runtime.program.as_ref()); + #[cfg(feature = "debug")] + { + let mut challenger_clone = machine.config().challenger(); + let record_clone = record.clone(); + machine.debug_constraints(&pk, record_clone, &mut challenger_clone); + } + let stats = record.stats().clone(); + let cycles = stats.get("cpu_events").unwrap(); - // Prove the program. let start = Instant::now(); - let cycles = runtime.state.global_clk; - let proof = machine.prove::>(&pk, runtime.record, &mut challenger); + let mut challenger = machine.config().challenger(); + let proof = machine.prove::>(&pk, record, &mut challenger); let time = start.elapsed().as_millis(); let nb_bytes = bincode::serialize(&proof).unwrap().len(); + let mut challenger = machine.config().challenger(); + machine.verify(&vk, &proof, &mut challenger)?; + tracing::info!( "summary: cycles={}, e2e={}, khz={:.2}, proofSize={}", cycles, time, - (cycles as f64 / time as f64), + (*cycles as f64 / time as f64), Size::from_bytes(nb_bytes), ); - proof + Ok(proof) +} + +fn trace_checkpoint(program: Program, file: &File) -> ExecutionRecord { + let mut reader = std::io::BufReader::new(file); + let state = bincode::deserialize_from(&mut reader).expect("failed to deserialize state"); + let mut runtime = Runtime::recover(program.clone(), state); + let (events, _) = + tracing::debug_span!("runtime.trace").in_scope(|| runtime.execute_record().unwrap()); + events +} + +fn reset_seek(file: &mut File) { + file.seek(std::io::SeekFrom::Start(0)) + .expect("failed to seek to start of tempfile"); } #[cfg(debug_assertions)] diff --git a/prover/src/build.rs b/prover/src/build.rs index 0c3325e43c..66be810e0a 100644 --- a/prover/src/build.rs +++ b/prover/src/build.rs @@ -75,7 +75,7 @@ pub fn dummy_proof() -> (StarkVerifyingKey, ShardProof) { tracing::info!("prove core"); let mut stdin = SP1Stdin::new(); stdin.write(&500u32); - let core_proof = prover.prove_core(&pk, &stdin); + let core_proof = prover.prove_core(&pk, &stdin).unwrap(); tracing::info!("compress"); let compressed_proof = prover.compress(&vk, core_proof, vec![]); diff --git a/prover/src/lib.rs b/prover/src/lib.rs index dc7442ec6a..7cb2513859 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -28,7 +28,7 @@ use rayon::iter::{IntoParallelIterator, ParallelIterator}; use rayon::prelude::*; use sp1_core::air::PublicValues; pub use sp1_core::io::{SP1PublicValues, SP1Stdin}; -use sp1_core::runtime::{Runtime, RuntimeError}; +use sp1_core::runtime::{ExecutionError, Runtime}; use sp1_core::stark::MachineVerificationError; use sp1_core::stark::{Challenge, StarkProvingKey}; use sp1_core::utils::DIGEST_SIZE; @@ -37,7 +37,7 @@ use sp1_core::{ stark::{ LocalProver, RiscvAir, ShardProof, StarkGenericConfig, StarkMachine, StarkVerifyingKey, Val, }, - utils::{run_and_prove, BabyBearPoseidon2}, + utils::{run_and_prove, BabyBearPoseidon2, SP1CoreProvingError}, }; use sp1_primitives::hash_deferred_proof; use sp1_recursion_circuit::witness::Witnessable; @@ -217,7 +217,7 @@ impl SP1Prover { /// Generate a proof of an SP1 program with the specified inputs. #[instrument(name = "execute", level = "info", skip_all)] - pub fn execute(elf: &[u8], stdin: &SP1Stdin) -> Result { + pub fn execute(elf: &[u8], stdin: &SP1Stdin) -> Result { let program = Program::from(elf); let mut runtime = Runtime::new(program); runtime.write_vecs(&stdin.buffer); @@ -231,16 +231,20 @@ impl SP1Prover { /// Generate shard proofs which split up and prove the valid execution of a RISC-V program with /// the core prover. #[instrument(name = "prove_core", level = "info", skip_all)] - pub fn prove_core(&self, pk: &SP1ProvingKey, stdin: &SP1Stdin) -> SP1CoreProof { + pub fn prove_core( + &self, + pk: &SP1ProvingKey, + stdin: &SP1Stdin, + ) -> Result { let config = CoreSC::default(); let program = Program::from(&pk.elf); - let (proof, public_values_stream) = run_and_prove(program, stdin, config); + let (proof, public_values_stream) = run_and_prove(program, stdin, config)?; let public_values = SP1PublicValues::from(&public_values_stream); - SP1CoreProof { + Ok(SP1CoreProof { proof: SP1CoreProofData(proof.shard_proofs), stdin: stdin.clone(), public_values, - } + }) } /// Reduce shards proofs to a single shard proof using the recursion prover. @@ -648,7 +652,7 @@ mod tests { tracing::info!("prove core"); let stdin = SP1Stdin::new(); - let core_proof = prover.prove_core(&pk, &stdin); + let core_proof = prover.prove_core(&pk, &stdin).unwrap(); tracing::info!("verify core"); prover.verify(&core_proof.proof, &vk).unwrap(); diff --git a/recursion/program/src/machine/mod.rs b/recursion/program/src/machine/mod.rs index 2d262332a0..5f0744c485 100644 --- a/recursion/program/src/machine/mod.rs +++ b/recursion/program/src/machine/mod.rs @@ -82,7 +82,8 @@ mod tests { let mut challenger = machine.config().challenger(); let time = std::time::Instant::now(); - let (proof, _) = sp1_core::utils::run_and_prove(program, &SP1Stdin::new(), SC::default()); + let (proof, _) = + sp1_core::utils::run_and_prove(program, &SP1Stdin::new(), SC::default()).unwrap(); machine.verify(&vk, &proof, &mut challenger).unwrap(); tracing::info!("Proof generated successfully"); let elapsed = time.elapsed(); From ea4dc5f90a56ef646d41a6dcc257851d21774fad Mon Sep 17 00:00:00 2001 From: John Guibas Date: Mon, 13 May 2024 15:37:39 -0700 Subject: [PATCH 4/7] hm --- prover/src/lib.rs | 9 +-------- prover/src/types.rs | 4 ++++ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/prover/src/lib.rs b/prover/src/lib.rs index f5d08a5166..266884e7e6 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -58,7 +58,6 @@ use sp1_recursion_program::machine::{ SP1RecursionMemoryLayout, SP1RecursiveVerifier, SP1ReduceMemoryLayout, SP1RootMemoryLayout, SP1RootVerifier, }; -use thiserror::Error; use tracing::instrument; pub use types::*; use utils::words_to_bytes; @@ -140,9 +139,6 @@ pub struct SP1Prover { pub wrap_machine: StarkMachine::Val>>, } -#[derive(Error, Debug)] -pub enum SP1RecursionProverError {} - impl SP1Prover { /// Initializes a new [SP1Prover]. #[instrument(name = "initialize prover", level = "info", skip_all)] @@ -255,9 +251,7 @@ impl SP1Prover { proof: SP1CoreProof, deferred_proofs: Vec>, ) -> Result, SP1RecursionProverError> { - // Set the batch size for the reduction tree. let batch_size = 2; - let shard_proofs = &proof.proof.0; // Setup the reconstruct commitments flags to false and save its state. @@ -271,11 +265,10 @@ impl SP1Prover { leaf_challenger.observe(proof.commitment.main_commit); leaf_challenger.observe_slice(&proof.public_values[0..self.core_machine.num_pv_elts()]); }); + // Make sure leaf challenger is not mutable anymore. let leaf_challenger = leaf_challenger; - let mut core_inputs = Vec::new(); - let mut reconstruct_challenger = self.core_machine.config().challenger(); vk.vk.observe_into(&mut reconstruct_challenger); diff --git a/prover/src/types.rs b/prover/src/types.rs index 547eac7707..94ffcc3cee 100644 --- a/prover/src/types.rs +++ b/prover/src/types.rs @@ -16,6 +16,7 @@ use sp1_core::{ use sp1_primitives::poseidon2_hash; use sp1_recursion_core::{air::RecursionPublicValues, stark::config::BabyBearPoseidon2Outer}; use sp1_recursion_gnark_ffi::{plonk_bn254::PlonkBn254Proof, Groth16Proof}; +use thiserror::Error; use crate::utils::words_to_bytes_be; use crate::{utils::babybear_bytes_to_bn254, words_to_bytes}; @@ -194,3 +195,6 @@ pub enum SP1ReduceProofWrapper { Core(SP1ReduceProof), Recursive(SP1ReduceProof), } + +#[derive(Error, Debug)] +pub enum SP1RecursionProverError {} From 8d15330d85acfbbc183e287c3b9e04a7b715bc55 Mon Sep 17 00:00:00 2001 From: John Guibas Date: Mon, 13 May 2024 15:43:49 -0700 Subject: [PATCH 5/7] interesting --- recursion/program/src/machine/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/recursion/program/src/machine/mod.rs b/recursion/program/src/machine/mod.rs index 64ee395ca9..a298427d49 100644 --- a/recursion/program/src/machine/mod.rs +++ b/recursion/program/src/machine/mod.rs @@ -82,8 +82,7 @@ mod tests { let mut challenger = machine.config().challenger(); let time = std::time::Instant::now(); - let (proof, _) = - sp1_core::utils::prove(program, &SP1Stdin::new(), SC::default()).unwrap(); + let (proof, _) = sp1_core::utils::prove(program, &SP1Stdin::new(), SC::default()).unwrap(); machine.verify(&vk, &proof, &mut challenger).unwrap(); tracing::info!("Proof generated successfully"); let elapsed = time.elapsed(); From 97f2dbd08595fd0a35881c2215dbab65fa1b56fa Mon Sep 17 00:00:00 2001 From: John Guibas Date: Mon, 13 May 2024 15:44:47 -0700 Subject: [PATCH 6/7] updated pr --- .github/workflows/pr.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 8fbe253852..e0c623fd8a 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -132,7 +132,7 @@ jobs: - name: Install SP1 CLI run: | cd cli - cargo install --locked --path . + cargo install --force --locked --path . cd ~ - name: Run cargo check @@ -161,7 +161,7 @@ jobs: - name: Install SP1 CLI run: | cd cli - cargo install --locked --path . + cargo install --force --locked --path . cd ~ - name: Run cargo prove new From ea7b9812df5345fd8bd555eaa1b23e2029abbfce Mon Sep 17 00:00:00 2001 From: John Guibas Date: Mon, 13 May 2024 15:58:14 -0700 Subject: [PATCH 7/7] add panic test --- sdk/src/lib.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index 208d84f6ad..7a53636ec1 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -473,7 +473,18 @@ mod tests { include_bytes!("../../examples/fibonacci/program/elf/riscv32im-succinct-zkvm-elf"); let mut stdin = SP1Stdin::new(); stdin.write(&10usize); - let _ = client.execute(elf, stdin).unwrap(); + client.execute(elf, stdin).unwrap(); + } + + #[test] + #[should_panic] + fn test_execute_panic() { + utils::setup_logger(); + let client = ProverClient::local(); + let elf = include_bytes!("../../tests/panic/elf/riscv32im-succinct-zkvm-elf"); + let mut stdin = SP1Stdin::new(); + stdin.write(&10usize); + client.execute(elf, stdin).unwrap(); } #[test]