From fa5afc8d5929d23fba7896283e94b17d535d72b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Thu, 5 Dec 2024 14:50:34 +0100 Subject: [PATCH 01/14] ecall-keccak: introduce syscall handlers and SyscallEvent --- ceno_emul/src/lib.rs | 2 + ceno_emul/src/syscalls.rs | 54 +++++++++++++++++++++++++++ ceno_emul/src/tracer.rs | 19 ++++++++++ ceno_emul/src/vm_state.rs | 34 ++++++++++++----- ceno_emul/tests/test_elf.rs | 21 +++++++++++ examples-builder/build.rs | 1 + examples/examples/ceno_rt_syscalls.rs | 47 +++++++++++++++++++++++ 7 files changed, 169 insertions(+), 9 deletions(-) create mode 100644 ceno_emul/src/syscalls.rs create mode 100644 examples/examples/ceno_rt_syscalls.rs diff --git a/ceno_emul/src/lib.rs b/ceno_emul/src/lib.rs index c734b1794..9970633b5 100644 --- a/ceno_emul/src/lib.rs +++ b/ceno_emul/src/lib.rs @@ -19,3 +19,5 @@ pub use elf::Program; mod rv32im_encode; pub use rv32im_encode::encode_rv32; + +mod syscalls; \ No newline at end of file diff --git a/ceno_emul/src/syscalls.rs b/ceno_emul/src/syscalls.rs new file mode 100644 index 000000000..70d1e2911 --- /dev/null +++ b/ceno_emul/src/syscalls.rs @@ -0,0 +1,54 @@ +use crate::{Change, EmuContext, VMState, WORD_SIZE, WordAddr, WriteOp}; +use anyhow::Result; +use itertools::{Itertools, izip}; + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct SyscallEvent { + pub mem_writes: Vec, +} + +pub const KECCAK_PERMUTE: u32 = 0x00_01_01_09; + +/// Trace the inputs and effects of a syscall. +pub fn handle_syscall(vm: &VMState, function_code: u32, arg0: u32) -> Result { + match function_code { + KECCAK_PERMUTE => Ok(keccak_permute(vm, arg0)), + _ => Err(anyhow::anyhow!("Unknown syscall: {}", function_code)), + } +} + +const KECCAK_WORDS: usize = 25 * 2; + +/// Trace the execution of a Keccak permutation. +/// +/// Compatible with: +/// https://github.com/succinctlabs/sp1/blob/013c24ea2fa15a0e7ed94f7d11a7ada4baa39ab9/crates/core/executor/src/syscalls/precompiles/keccak256/permute.rs +/// +/// TODO: test compatibility. +fn keccak_permute(vm: &VMState, state_ptr: u32) -> SyscallEvent { + let addrs = (state_ptr..) + .step_by(WORD_SIZE as usize) + .take(KECCAK_WORDS) + .map(WordAddr::from) + .collect_vec(); + + // Read Keccak state. + let input = addrs + .iter() + .map(|&addr| vm.peek_memory(addr)) + .collect::>(); + + // TODO: Compute Keccak permutation. + let output = input.clone(); + + // Write permuted state. + let mem_writes = izip!(addrs, input, output) + .map(|(addr, before, after)| WriteOp { + addr, + value: Change { before, after }, + previous_cycle: 0, // Set later by Tracer. + }) + .collect_vec(); + + SyscallEvent { mem_writes } +} diff --git a/ceno_emul/src/tracer.rs b/ceno_emul/src/tracer.rs index bbb1e6a51..617540e31 100644 --- a/ceno_emul/src/tracer.rs +++ b/ceno_emul/src/tracer.rs @@ -5,6 +5,7 @@ use crate::{ addr::{ByteAddr, Cycle, RegIdx, Word, WordAddr}, encode_rv32, rv32im::DecodedInstruction, + syscalls::SyscallEvent, }; /// An instruction and its context in an execution trace. That is concrete values of registers and memory. @@ -30,6 +31,8 @@ pub struct StepRecord { rd: Option, memory_op: Option, + + syscall: Option, } #[derive(Clone, Debug, PartialEq, Eq, Hash)] @@ -242,6 +245,7 @@ impl StepRecord { previous_cycle, }), memory_op, + syscall: None, } } @@ -383,6 +387,21 @@ impl Tracer { }); } + pub fn track_syscall(&mut self, mut event: SyscallEvent) { + let cycle = self.record.cycle + Self::SUBCYCLE_MEM; + for op in &mut event.mem_writes { + op.previous_cycle = self.track_access(op.addr, Self::SUBCYCLE_MEM); + assert_ne!( + op.previous_cycle, cycle, + "Memory address {:?} was accessed twice in the same cycle", + op.addr + ); + } + + assert!(self.record.syscall.is_none(), "Only one syscall per step"); + self.record.syscall = Some(event); + } + /// - Return the cycle when an address was last accessed. /// - Return 0 if this is the first access. /// - Record the current instruction as the origin of the latest access. diff --git a/ceno_emul/src/vm_state.rs b/ceno_emul/src/vm_state.rs index d8eff9701..101b11f43 100644 --- a/ceno_emul/src/vm_state.rs +++ b/ceno_emul/src/vm_state.rs @@ -6,6 +6,7 @@ use crate::{ addr::{ByteAddr, RegIdx, Word, WordAddr}, platform::Platform, rv32im::{DecodedInstruction, Emulator, TrapCause}, + syscalls::{KECCAK_PERMUTE, SyscallEvent, handle_syscall}, tracer::{Change, StepRecord, Tracer}, }; use anyhow::{Result, anyhow}; @@ -106,6 +107,13 @@ impl VMState { self.set_pc(0.into()); self.halted = true; } + + fn apply_syscall(&mut self, event: SyscallEvent) { + for write_op in &event.mem_writes { + self.memory.insert(write_op.addr, write_op.value.after); + } + self.tracer.track_syscall(event); + } } impl EmuContext for VMState { @@ -119,15 +127,23 @@ impl EmuContext for VMState { self.halt(); Ok(true) } else if self.platform.unsafe_ecall_nop { - // Treat unknown ecalls as all powerful instructions: - // Read two registers, write one register, write one memory word, and branch. - tracing::warn!("ecall ignored: syscall_id={}", function); - self.store_register(DecodedInstruction::RD_NULL as RegIdx, 0)?; - // Example ecall effect - any writable address will do. - let addr = (self.platform.stack_top - WORD_SIZE as u32).into(); - self.store_memory(addr, self.peek_memory(addr))?; - self.set_pc(ByteAddr(self.pc) + PC_STEP_SIZE); - Ok(true) + if function == KECCAK_PERMUTE { + let event = handle_syscall(self, function, arg0)?; + self.apply_syscall(event); + self.set_pc(ByteAddr(self.pc) + PC_STEP_SIZE); + Ok(true) + } else { + // TODO: remove this example. + // Treat unknown ecalls as all powerful instructions: + // Read two registers, write one register, write one memory word, and branch. + tracing::warn!("ecall ignored: syscall_id={}", function); + self.store_register(DecodedInstruction::RD_NULL as RegIdx, 0)?; + // Example ecall effect - any writable address will do. + let addr = (self.platform.stack_top - WORD_SIZE as u32).into(); + self.store_memory(addr, self.peek_memory(addr))?; + self.set_pc(ByteAddr(self.pc) + PC_STEP_SIZE); + Ok(true) + } } else { self.trap(TrapCause::EcallError) } diff --git a/ceno_emul/tests/test_elf.rs b/ceno_emul/tests/test_elf.rs index ca7a14c1c..8ba16632d 100644 --- a/ceno_emul/tests/test_elf.rs +++ b/ceno_emul/tests/test_elf.rs @@ -1,5 +1,6 @@ use anyhow::Result; use ceno_emul::{ByteAddr, CENO_PLATFORM, EmuContext, InsnKind, Platform, StepRecord, VMState}; +use itertools::Itertools; #[test] fn test_ceno_rt_mini() -> Result<()> { @@ -72,6 +73,26 @@ fn test_ceno_rt_io() -> Result<()> { Ok(()) } +#[test] +fn test_ceno_rt_syscalls() -> Result<()> { + let program_elf = ceno_examples::ceno_rt_syscalls; + let mut state = VMState::new_from_elf(unsafe_platform(), program_elf)?; + let _steps = run(&mut state)?; + + for (&addr, &cycle) in state.tracer().final_accesses().iter().sorted() { + let value = state.peek_memory(addr); + println!("{:?} = {:08x} (cycle {})", addr, value, cycle); + } + + Ok(()) +} + +fn unsafe_platform() -> Platform { + let mut platform = CENO_PLATFORM; + platform.unsafe_ecall_nop = true; + platform +} + fn run(state: &mut VMState) -> Result> { let steps = state.iter_until_halt().collect::>>()?; eprintln!("Emulator ran for {} steps.", steps.len()); diff --git a/examples-builder/build.rs b/examples-builder/build.rs index 7ea9b9628..fd5cca76e 100644 --- a/examples-builder/build.rs +++ b/examples-builder/build.rs @@ -14,6 +14,7 @@ const EXAMPLES: &[&str] = &[ "ceno_rt_mem", "ceno_rt_mini", "ceno_rt_panic", + "ceno_rt_syscalls", ]; const CARGO_MANIFEST_DIR: &str = env!("CARGO_MANIFEST_DIR"); diff --git a/examples/examples/ceno_rt_syscalls.rs b/examples/examples/ceno_rt_syscalls.rs new file mode 100644 index 000000000..ba3336a4c --- /dev/null +++ b/examples/examples/ceno_rt_syscalls.rs @@ -0,0 +1,47 @@ +#![no_main] +#![no_std] +use core::ptr::{addr_of, read_volatile}; + +extern crate ceno_rt; + +static mut OUTPUT: u32 = 0; + +ceno_rt::entry!(main); +fn main() { + let mut state = [0_u64; 25]; + + syscalls::syscall_keccak_permute(&mut state); +} + +/// Prevent compiler optimizations. +fn black_box(x: *const T) -> T { + unsafe { read_volatile(x) } +} + +mod syscalls { + + // Based on https://github.com/succinctlabs/sp1/blob/013c24ea2fa15a0e7ed94f7d11a7ada4baa39ab9/crates/zkvm/entrypoint/src/syscalls/keccak_permute.rs + + const KECCAK_PERMUTE: u32 = 0x00_01_01_09; + + use core::arch::asm; + + /// Executes the Keccak256 permutation on the given state. + /// + /// ### Safety + /// + /// The caller must ensure that `state` is valid pointer to data that is aligned along a four + /// byte boundary. + #[allow(unused_variables)] + #[no_mangle] + pub extern "C" fn syscall_keccak_permute(state: &mut [u64; 25]) { + unsafe { + asm!( + "ecall", + in("t0") KECCAK_PERMUTE, + in("a0") state as *mut [u64; 25], + in("a1") 0 + ); + } + } +} From 31760af11135a154317b42cd59a6d8ce9e09fbb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Thu, 5 Dec 2024 18:05:30 +0100 Subject: [PATCH 02/14] ecall-keccak: support return value --- ceno_emul/src/syscalls.rs | 19 +++++++++++++++---- ceno_emul/src/tracer.rs | 10 +++++----- ceno_emul/src/vm_state.rs | 14 +++++++++----- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/ceno_emul/src/syscalls.rs b/ceno_emul/src/syscalls.rs index 70d1e2911..c2be069a9 100644 --- a/ceno_emul/src/syscalls.rs +++ b/ceno_emul/src/syscalls.rs @@ -2,15 +2,23 @@ use crate::{Change, EmuContext, VMState, WORD_SIZE, WordAddr, WriteOp}; use anyhow::Result; use itertools::{Itertools, izip}; +/// A syscall event, available to the circuit witness generators. #[derive(Clone, Debug, Default, PartialEq, Eq)] -pub struct SyscallEvent { +pub struct SyscallWitness { pub mem_writes: Vec, } +/// The effects of a syscall to apply on the VM. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct SyscallEffects { + pub witness: SyscallWitness, + pub return_value: Option, +} + pub const KECCAK_PERMUTE: u32 = 0x00_01_01_09; /// Trace the inputs and effects of a syscall. -pub fn handle_syscall(vm: &VMState, function_code: u32, arg0: u32) -> Result { +pub fn handle_syscall(vm: &VMState, function_code: u32, arg0: u32) -> Result { match function_code { KECCAK_PERMUTE => Ok(keccak_permute(vm, arg0)), _ => Err(anyhow::anyhow!("Unknown syscall: {}", function_code)), @@ -25,7 +33,7 @@ const KECCAK_WORDS: usize = 25 * 2; /// https://github.com/succinctlabs/sp1/blob/013c24ea2fa15a0e7ed94f7d11a7ada4baa39ab9/crates/core/executor/src/syscalls/precompiles/keccak256/permute.rs /// /// TODO: test compatibility. -fn keccak_permute(vm: &VMState, state_ptr: u32) -> SyscallEvent { +fn keccak_permute(vm: &VMState, state_ptr: u32) -> SyscallEffects { let addrs = (state_ptr..) .step_by(WORD_SIZE as usize) .take(KECCAK_WORDS) @@ -50,5 +58,8 @@ fn keccak_permute(vm: &VMState, state_ptr: u32) -> SyscallEvent { }) .collect_vec(); - SyscallEvent { mem_writes } + SyscallEffects { + witness: SyscallWitness { mem_writes }, + return_value: None, + } } diff --git a/ceno_emul/src/tracer.rs b/ceno_emul/src/tracer.rs index 617540e31..e9b9ba96c 100644 --- a/ceno_emul/src/tracer.rs +++ b/ceno_emul/src/tracer.rs @@ -5,7 +5,7 @@ use crate::{ addr::{ByteAddr, Cycle, RegIdx, Word, WordAddr}, encode_rv32, rv32im::DecodedInstruction, - syscalls::SyscallEvent, + syscalls::{SyscallEffects, SyscallWitness}, }; /// An instruction and its context in an execution trace. That is concrete values of registers and memory. @@ -32,7 +32,7 @@ pub struct StepRecord { memory_op: Option, - syscall: Option, + syscall: Option, } #[derive(Clone, Debug, PartialEq, Eq, Hash)] @@ -387,9 +387,9 @@ impl Tracer { }); } - pub fn track_syscall(&mut self, mut event: SyscallEvent) { + pub fn track_syscall(&mut self, mut effects: SyscallEffects) { let cycle = self.record.cycle + Self::SUBCYCLE_MEM; - for op in &mut event.mem_writes { + for op in &mut effects.witness.mem_writes { op.previous_cycle = self.track_access(op.addr, Self::SUBCYCLE_MEM); assert_ne!( op.previous_cycle, cycle, @@ -399,7 +399,7 @@ impl Tracer { } assert!(self.record.syscall.is_none(), "Only one syscall per step"); - self.record.syscall = Some(event); + self.record.syscall = Some(effects.witness); } /// - Return the cycle when an address was last accessed. diff --git a/ceno_emul/src/vm_state.rs b/ceno_emul/src/vm_state.rs index 101b11f43..9560a3412 100644 --- a/ceno_emul/src/vm_state.rs +++ b/ceno_emul/src/vm_state.rs @@ -6,7 +6,7 @@ use crate::{ addr::{ByteAddr, RegIdx, Word, WordAddr}, platform::Platform, rv32im::{DecodedInstruction, Emulator, TrapCause}, - syscalls::{KECCAK_PERMUTE, SyscallEvent, handle_syscall}, + syscalls::{KECCAK_PERMUTE, SyscallEffects, handle_syscall}, tracer::{Change, StepRecord, Tracer}, }; use anyhow::{Result, anyhow}; @@ -108,11 +108,15 @@ impl VMState { self.halted = true; } - fn apply_syscall(&mut self, event: SyscallEvent) { - for write_op in &event.mem_writes { + fn apply_syscall(&mut self, effects: SyscallEffects) -> Result<()> { + for write_op in &effects.witness.mem_writes { self.memory.insert(write_op.addr, write_op.value.after); } - self.tracer.track_syscall(event); + if let Some(return_value) = effects.return_value { + self.store_register(Platform::reg_arg0(), return_value)?; + } + self.tracer.track_syscall(effects); + Ok(()) } } @@ -129,7 +133,7 @@ impl EmuContext for VMState { } else if self.platform.unsafe_ecall_nop { if function == KECCAK_PERMUTE { let event = handle_syscall(self, function, arg0)?; - self.apply_syscall(event); + self.apply_syscall(event)?; self.set_pc(ByteAddr(self.pc) + PC_STEP_SIZE); Ok(true) } else { From 60da95c68f9a47aebefb2d740286d12c234626ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Thu, 5 Dec 2024 18:27:24 +0100 Subject: [PATCH 03/14] ecall-keccak: support next_pc --- ceno_emul/src/lib.rs | 2 +- ceno_emul/src/syscalls.rs | 4 +++- ceno_emul/src/vm_state.rs | 10 +++++++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/ceno_emul/src/lib.rs b/ceno_emul/src/lib.rs index 9970633b5..536887b7d 100644 --- a/ceno_emul/src/lib.rs +++ b/ceno_emul/src/lib.rs @@ -20,4 +20,4 @@ pub use elf::Program; mod rv32im_encode; pub use rv32im_encode::encode_rv32; -mod syscalls; \ No newline at end of file +mod syscalls; diff --git a/ceno_emul/src/syscalls.rs b/ceno_emul/src/syscalls.rs index c2be069a9..72242c7ad 100644 --- a/ceno_emul/src/syscalls.rs +++ b/ceno_emul/src/syscalls.rs @@ -13,6 +13,7 @@ pub struct SyscallWitness { pub struct SyscallEffects { pub witness: SyscallWitness, pub return_value: Option, + pub next_pc: Option, } pub const KECCAK_PERMUTE: u32 = 0x00_01_01_09; @@ -35,7 +36,7 @@ const KECCAK_WORDS: usize = 25 * 2; /// TODO: test compatibility. fn keccak_permute(vm: &VMState, state_ptr: u32) -> SyscallEffects { let addrs = (state_ptr..) - .step_by(WORD_SIZE as usize) + .step_by(WORD_SIZE) .take(KECCAK_WORDS) .map(WordAddr::from) .collect_vec(); @@ -61,5 +62,6 @@ fn keccak_permute(vm: &VMState, state_ptr: u32) -> SyscallEffects { SyscallEffects { witness: SyscallWitness { mem_writes }, return_value: None, + next_pc: None, } } diff --git a/ceno_emul/src/vm_state.rs b/ceno_emul/src/vm_state.rs index 9560a3412..e39e6613d 100644 --- a/ceno_emul/src/vm_state.rs +++ b/ceno_emul/src/vm_state.rs @@ -112,9 +112,14 @@ impl VMState { for write_op in &effects.witness.mem_writes { self.memory.insert(write_op.addr, write_op.value.after); } + if let Some(return_value) = effects.return_value { self.store_register(Platform::reg_arg0(), return_value)?; } + + let next_pc = effects.next_pc.unwrap_or(self.pc + PC_STEP_SIZE as u32); + self.set_pc(next_pc.into()); + self.tracer.track_syscall(effects); Ok(()) } @@ -132,9 +137,8 @@ impl EmuContext for VMState { Ok(true) } else if self.platform.unsafe_ecall_nop { if function == KECCAK_PERMUTE { - let event = handle_syscall(self, function, arg0)?; - self.apply_syscall(event)?; - self.set_pc(ByteAddr(self.pc) + PC_STEP_SIZE); + let effects = handle_syscall(self, function, arg0)?; + self.apply_syscall(effects)?; Ok(true) } else { // TODO: remove this example. From 206f1dd399841d06f43422de4776d528df2030c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Mon, 9 Dec 2024 13:29:16 +0100 Subject: [PATCH 04/14] ecall-keccak: Compute actual Keccak-f --- Cargo.lock | 1 + ceno_emul/Cargo.toml | 1 + ceno_emul/src/syscalls.rs | 18 +++++++++++++++--- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 52d969efd..4952a9896 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -246,6 +246,7 @@ dependencies = [ "num-traits", "strum", "strum_macros", + "tiny-keccak", "tracing", ] diff --git a/ceno_emul/Cargo.toml b/ceno_emul/Cargo.toml index 38f0a8bfd..40197036a 100644 --- a/ceno_emul/Cargo.toml +++ b/ceno_emul/Cargo.toml @@ -17,6 +17,7 @@ num-derive.workspace = true num-traits.workspace = true strum.workspace = true strum_macros.workspace = true +tiny-keccak = { version = "2.0.2", features = ["keccak"] } tracing.workspace = true [dev-dependencies] diff --git a/ceno_emul/src/syscalls.rs b/ceno_emul/src/syscalls.rs index 72242c7ad..5c2ddf3b7 100644 --- a/ceno_emul/src/syscalls.rs +++ b/ceno_emul/src/syscalls.rs @@ -1,6 +1,7 @@ use crate::{Change, EmuContext, VMState, WORD_SIZE, WordAddr, WriteOp}; use anyhow::Result; use itertools::{Itertools, izip}; +use tiny_keccak::keccakf; /// A syscall event, available to the circuit witness generators. #[derive(Clone, Debug, Default, PartialEq, Eq)] @@ -26,7 +27,8 @@ pub fn handle_syscall(vm: &VMState, function_code: u32, arg0: u32) -> Result SyscallEffects { .map(|&addr| vm.peek_memory(addr)) .collect::>(); - // TODO: Compute Keccak permutation. - let output = input.clone(); + // Compute Keccak permutation. + let output = { + let mut state = [0_u64; KECCAK_CELLS]; + izip!(state.iter_mut(), input.chunks_exact(2)).for_each(|(cell, chunk)| { + let lo = chunk[0] as u64; + let hi = chunk[1] as u64; + *cell = lo | hi << 32; + }); + keccakf(&mut state); + state.into_iter().flat_map(|c| [c as u32, (c >> 32) as u32]) + }; // Write permuted state. let mem_writes = izip!(addrs, input, output) @@ -59,6 +70,7 @@ fn keccak_permute(vm: &VMState, state_ptr: u32) -> SyscallEffects { }) .collect_vec(); + assert_eq!(mem_writes.len(), KECCAK_WORDS); SyscallEffects { witness: SyscallWitness { mem_writes }, return_value: None, From 1cf67b2407513868bc176ee733665dc45aed6c12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Mon, 9 Dec 2024 14:21:02 +0100 Subject: [PATCH 05/14] ecall-keccak: test actual Keccak computation --- ceno_emul/tests/test_elf.rs | 28 +++++++++++++++++++++++---- examples/examples/ceno_rt_syscalls.rs | 19 +++++++++++------- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/ceno_emul/tests/test_elf.rs b/ceno_emul/tests/test_elf.rs index 8ba16632d..ca4891511 100644 --- a/ceno_emul/tests/test_elf.rs +++ b/ceno_emul/tests/test_elf.rs @@ -1,6 +1,7 @@ use anyhow::Result; use ceno_emul::{ByteAddr, CENO_PLATFORM, EmuContext, InsnKind, Platform, StepRecord, VMState}; -use itertools::Itertools; +use itertools::{Itertools, izip}; +use tiny_keccak::keccakf; #[test] fn test_ceno_rt_mini() -> Result<()> { @@ -79,9 +80,16 @@ fn test_ceno_rt_syscalls() -> Result<()> { let mut state = VMState::new_from_elf(unsafe_platform(), program_elf)?; let _steps = run(&mut state)?; - for (&addr, &cycle) in state.tracer().final_accesses().iter().sorted() { - let value = state.peek_memory(addr); - println!("{:?} = {:08x} (cycle {})", addr, value, cycle); + const ITERATIONS: usize = 3; + let keccak_outs = sample_keccak_f(ITERATIONS); + + let all_messages = read_all_messages(&state); + for (got, expect) in izip!(&all_messages, keccak_outs) { + let got = got + .chunks_exact(8) + .map(|chunk| u64::from_le_bytes(chunk.try_into().unwrap())) + .collect_vec(); + assert_eq!(got, expect); } Ok(()) @@ -93,6 +101,18 @@ fn unsafe_platform() -> Platform { platform } +fn sample_keccak_f(count: usize) -> Vec> { + let input = [0_u64; 25]; + let mut state = input.clone(); + + (0..count) + .map(|_| { + keccakf(&mut state); + state.into() + }) + .collect_vec() +} + fn run(state: &mut VMState) -> Result> { let steps = state.iter_until_halt().collect::>>()?; eprintln!("Emulator ran for {} steps.", steps.len()); diff --git a/examples/examples/ceno_rt_syscalls.rs b/examples/examples/ceno_rt_syscalls.rs index ba3336a4c..ed3621b2b 100644 --- a/examples/examples/ceno_rt_syscalls.rs +++ b/examples/examples/ceno_rt_syscalls.rs @@ -1,21 +1,26 @@ #![no_main] #![no_std] -use core::ptr::{addr_of, read_volatile}; - extern crate ceno_rt; +use ceno_rt::info_out; +use core::{ptr::read_volatile, slice}; -static mut OUTPUT: u32 = 0; +const ITERATIONS: usize = 3; ceno_rt::entry!(main); fn main() { let mut state = [0_u64; 25]; - syscalls::syscall_keccak_permute(&mut state); + for _ in 0..ITERATIONS { + syscalls::syscall_keccak_permute(&mut state); + log_state(&state); + } } -/// Prevent compiler optimizations. -fn black_box(x: *const T) -> T { - unsafe { read_volatile(x) } +fn log_state(state: &[u64; 25]) { + let out = unsafe { + slice::from_raw_parts_mut(state.as_ptr() as *mut u8, state.len() * size_of::()) + }; + info_out().write_frame(out); } mod syscalls { From 762bb5dbbc83e05a929f95404c9689ec7b27a4c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Mon, 9 Dec 2024 14:24:29 +0100 Subject: [PATCH 06/14] ecall-keccak: rename --- ceno_emul/tests/test_elf.rs | 5 +++-- examples-builder/build.rs | 2 +- examples/examples/{ceno_rt_syscalls.rs => ceno_rt_keccak.rs} | 4 ++++ 3 files changed, 8 insertions(+), 3 deletions(-) rename examples/examples/{ceno_rt_syscalls.rs => ceno_rt_keccak.rs} (91%) diff --git a/ceno_emul/tests/test_elf.rs b/ceno_emul/tests/test_elf.rs index ca4891511..7f5c328d6 100644 --- a/ceno_emul/tests/test_elf.rs +++ b/ceno_emul/tests/test_elf.rs @@ -75,11 +75,12 @@ fn test_ceno_rt_io() -> Result<()> { } #[test] -fn test_ceno_rt_syscalls() -> Result<()> { - let program_elf = ceno_examples::ceno_rt_syscalls; +fn test_ceno_rt_keccak() -> Result<()> { + let program_elf = ceno_examples::ceno_rt_keccak; let mut state = VMState::new_from_elf(unsafe_platform(), program_elf)?; let _steps = run(&mut state)?; + // Expect the program to have written successive states between Keccak permutations. const ITERATIONS: usize = 3; let keccak_outs = sample_keccak_f(ITERATIONS); diff --git a/examples-builder/build.rs b/examples-builder/build.rs index fd5cca76e..9b29b90d2 100644 --- a/examples-builder/build.rs +++ b/examples-builder/build.rs @@ -14,7 +14,7 @@ const EXAMPLES: &[&str] = &[ "ceno_rt_mem", "ceno_rt_mini", "ceno_rt_panic", - "ceno_rt_syscalls", + "ceno_rt_keccak", ]; const CARGO_MANIFEST_DIR: &str = env!("CARGO_MANIFEST_DIR"); diff --git a/examples/examples/ceno_rt_syscalls.rs b/examples/examples/ceno_rt_keccak.rs similarity index 91% rename from examples/examples/ceno_rt_syscalls.rs rename to examples/examples/ceno_rt_keccak.rs index ed3621b2b..f436b9432 100644 --- a/examples/examples/ceno_rt_syscalls.rs +++ b/examples/examples/ceno_rt_keccak.rs @@ -1,3 +1,7 @@ +//! Compute the Keccak permutation using a syscall. +//! +//! Iterate multiple times and log the state after each iteration. + #![no_main] #![no_std] extern crate ceno_rt; From 8adf10095e1a815ec9e25afdfc8ecb9d0976445c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Mon, 9 Dec 2024 14:26:57 +0100 Subject: [PATCH 07/14] ecall-keccak: move syscall to ceno_rt --- Cargo.lock | 180 ++++++++++++++-------------- ceno_rt/src/lib.rs | 3 + ceno_rt/src/syscalls.rs | 24 ++++ examples/examples/ceno_rt_keccak.rs | 32 +---- 4 files changed, 119 insertions(+), 120 deletions(-) create mode 100644 ceno_rt/src/syscalls.rs diff --git a/Cargo.lock b/Cargo.lock index 4952a9896..7e4928c15 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -59,9 +59,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -74,43 +74,43 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" +checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" [[package]] name = "ark-std" @@ -205,9 +205,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" +checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a" [[package]] name = "byteorder" @@ -223,9 +223,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.1.31" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" +checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d" dependencies = [ "shlex", ] @@ -340,9 +340,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.21" +version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" +checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" dependencies = [ "clap_builder", "clap_derive", @@ -350,9 +350,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.21" +version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" +checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" dependencies = [ "anstream", "anstyle", @@ -369,20 +369,20 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "colored" @@ -431,9 +431,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] @@ -645,19 +645,19 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "fastrand" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" [[package]] name = "ff" @@ -819,9 +819,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "heck" @@ -849,12 +849,12 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", - "hashbrown 0.15.0", + "hashbrown 0.15.2", ] [[package]] @@ -939,16 +939,17 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "js-sys" -version = "0.3.72" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -973,9 +974,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.161" +version = "0.2.167" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" [[package]] name = "libredox" @@ -1148,7 +1149,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -1274,9 +1275,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "plonky2" @@ -1433,9 +1434,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.88" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -1537,13 +1538,13 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "regex-syntax 0.8.5", ] @@ -1558,9 +1559,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -1609,7 +1610,7 @@ checksum = "f265be5d634272320a7de94cea15c22a3bfdd4eb42eb43edc528415f066a1f25" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -1626,9 +1627,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustix" -version = "0.38.40" +version = "0.38.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" +checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" dependencies = [ "bitflags 2.6.0", "errno", @@ -1681,7 +1682,7 @@ checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -1763,7 +1764,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -1792,9 +1793,9 @@ dependencies = [ [[package]] name = "symbolic-common" -version = "12.12.0" +version = "12.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "366f1b4c6baf6cfefc234bbd4899535fca0b06c74443039a73f6dfb2fad88d77" +checksum = "e5ba5365997a4e375660bed52f5b42766475d5bc8ceb1bb13fea09c469ea0f49" dependencies = [ "debugid", "memmap2", @@ -1804,9 +1805,9 @@ dependencies = [ [[package]] name = "symbolic-demangle" -version = "12.12.0" +version = "12.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aba05ba5b9962ea5617baf556293720a8b2d0a282aa14ee4bf10e22efc7da8c8" +checksum = "beff338b2788519120f38c59ff4bb15174f52a183e547bac3d6072c2c0aa48aa" dependencies = [ "cpp_demangle", "rustc-demangle", @@ -1826,9 +1827,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.82" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83540f837a8afc019423a8edb95b52a8effe46957ee402287f4292fae35be021" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -1867,22 +1868,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.64" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.64" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -1933,7 +1934,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] [[package]] @@ -2019,9 +2020,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-width" @@ -2081,9 +2082,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ "cfg-if", "once_cell", @@ -2092,24 +2093,23 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2117,28 +2117,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" [[package]] name = "web-sys" -version = "0.3.72" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" dependencies = [ "js-sys", "wasm-bindgen", @@ -2360,5 +2360,5 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.90", ] diff --git a/ceno_rt/src/lib.rs b/ceno_rt/src/lib.rs index 8de456c41..6e2623ee0 100644 --- a/ceno_rt/src/lib.rs +++ b/ceno_rt/src/lib.rs @@ -12,6 +12,9 @@ pub use io::info_out; mod params; pub use params::*; +mod syscalls; +pub use syscalls::*; + #[cfg(not(test))] mod panic_handler { use core::panic::PanicInfo; diff --git a/ceno_rt/src/syscalls.rs b/ceno_rt/src/syscalls.rs new file mode 100644 index 000000000..90ace85da --- /dev/null +++ b/ceno_rt/src/syscalls.rs @@ -0,0 +1,24 @@ +// Based on https://github.com/succinctlabs/sp1/blob/013c24ea2fa15a0e7ed94f7d11a7ada4baa39ab9/crates/zkvm/entrypoint/src/syscalls/keccak_permute.rs + +const KECCAK_PERMUTE: u32 = 0x00_01_01_09; + +use core::arch::asm; + +/// Executes the Keccak256 permutation on the given state. +/// +/// ### Safety +/// +/// The caller must ensure that `state` is valid pointer to data that is aligned along a four +/// byte boundary. +#[allow(unused_variables)] +#[no_mangle] +pub extern "C" fn syscall_keccak_permute(state: &mut [u64; 25]) { + unsafe { + asm!( + "ecall", + in("t0") KECCAK_PERMUTE, + in("a0") state as *mut [u64; 25], + in("a1") 0 + ); + } +} diff --git a/examples/examples/ceno_rt_keccak.rs b/examples/examples/ceno_rt_keccak.rs index f436b9432..07fb7b9ca 100644 --- a/examples/examples/ceno_rt_keccak.rs +++ b/examples/examples/ceno_rt_keccak.rs @@ -5,7 +5,7 @@ #![no_main] #![no_std] extern crate ceno_rt; -use ceno_rt::info_out; +use ceno_rt::{info_out, syscall_keccak_permute}; use core::{ptr::read_volatile, slice}; const ITERATIONS: usize = 3; @@ -15,7 +15,7 @@ fn main() { let mut state = [0_u64; 25]; for _ in 0..ITERATIONS { - syscalls::syscall_keccak_permute(&mut state); + syscall_keccak_permute(&mut state); log_state(&state); } } @@ -26,31 +26,3 @@ fn log_state(state: &[u64; 25]) { }; info_out().write_frame(out); } - -mod syscalls { - - // Based on https://github.com/succinctlabs/sp1/blob/013c24ea2fa15a0e7ed94f7d11a7ada4baa39ab9/crates/zkvm/entrypoint/src/syscalls/keccak_permute.rs - - const KECCAK_PERMUTE: u32 = 0x00_01_01_09; - - use core::arch::asm; - - /// Executes the Keccak256 permutation on the given state. - /// - /// ### Safety - /// - /// The caller must ensure that `state` is valid pointer to data that is aligned along a four - /// byte boundary. - #[allow(unused_variables)] - #[no_mangle] - pub extern "C" fn syscall_keccak_permute(state: &mut [u64; 25]) { - unsafe { - asm!( - "ecall", - in("t0") KECCAK_PERMUTE, - in("a0") state as *mut [u64; 25], - in("a1") 0 - ); - } - } -} From d854fa5963648bc7b201ee4641958f6f2e8ec210 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Mon, 9 Dec 2024 14:56:21 +0100 Subject: [PATCH 08/14] ecall-keccak: test memory records --- ceno_emul/src/tracer.rs | 4 ++++ ceno_emul/tests/test_elf.rs | 32 +++++++++++++++++++++++++---- examples/examples/ceno_rt_keccak.rs | 4 ++-- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/ceno_emul/src/tracer.rs b/ceno_emul/src/tracer.rs index 54dfafe78..8aa55cf79 100644 --- a/ceno_emul/src/tracer.rs +++ b/ceno_emul/src/tracer.rs @@ -286,6 +286,10 @@ impl StepRecord { pub fn is_busy_loop(&self) -> bool { self.pc.before == self.pc.after } + + pub fn syscall(&self) -> Option<&SyscallWitness> { + self.syscall.as_ref() + } } #[derive(Debug)] diff --git a/ceno_emul/tests/test_elf.rs b/ceno_emul/tests/test_elf.rs index 7f5c328d6..afbd67a12 100644 --- a/ceno_emul/tests/test_elf.rs +++ b/ceno_emul/tests/test_elf.rs @@ -78,18 +78,43 @@ fn test_ceno_rt_io() -> Result<()> { fn test_ceno_rt_keccak() -> Result<()> { let program_elf = ceno_examples::ceno_rt_keccak; let mut state = VMState::new_from_elf(unsafe_platform(), program_elf)?; - let _steps = run(&mut state)?; + let steps = run(&mut state)?; // Expect the program to have written successive states between Keccak permutations. const ITERATIONS: usize = 3; let keccak_outs = sample_keccak_f(ITERATIONS); let all_messages = read_all_messages(&state); - for (got, expect) in izip!(&all_messages, keccak_outs) { + assert_eq!(all_messages.len(), ITERATIONS); + for (got, expect) in izip!(&all_messages, &keccak_outs) { let got = got .chunks_exact(8) .map(|chunk| u64::from_le_bytes(chunk.try_into().unwrap())) .collect_vec(); + assert_eq!(&got, expect); + } + + // Find the syscall records. + let syscalls = steps.iter().filter_map(|step| step.syscall()).collect_vec(); + assert_eq!(syscalls.len(), ITERATIONS); + + // Check the syscall effects. + for (witness, expect) in izip!(syscalls, keccak_outs) { + assert_eq!(witness.mem_writes.len(), expect.len() * 2); + + let got = witness + .mem_writes + .chunks_exact(2) + .map(|write_ops| { + assert_eq!( + write_ops[1].addr.baddr(), + write_ops[0].addr.baddr() + WORD_SIZE as u32 + ); + let lo = write_ops[0].value.after as u64; + let hi = write_ops[1].value.after as u64; + lo | (hi << 32) + }) + .collect_vec(); assert_eq!(got, expect); } @@ -103,8 +128,7 @@ fn unsafe_platform() -> Platform { } fn sample_keccak_f(count: usize) -> Vec> { - let input = [0_u64; 25]; - let mut state = input.clone(); + let mut state = [0_u64; 25]; (0..count) .map(|_| { diff --git a/examples/examples/ceno_rt_keccak.rs b/examples/examples/ceno_rt_keccak.rs index 07fb7b9ca..cd68d17db 100644 --- a/examples/examples/ceno_rt_keccak.rs +++ b/examples/examples/ceno_rt_keccak.rs @@ -6,7 +6,7 @@ #![no_std] extern crate ceno_rt; use ceno_rt::{info_out, syscall_keccak_permute}; -use core::{ptr::read_volatile, slice}; +use core::slice; const ITERATIONS: usize = 3; @@ -22,7 +22,7 @@ fn main() { fn log_state(state: &[u64; 25]) { let out = unsafe { - slice::from_raw_parts_mut(state.as_ptr() as *mut u8, state.len() * size_of::()) + slice::from_raw_parts(state.as_ptr() as *const u8, state.len() * size_of::()) }; info_out().write_frame(out); } From 5be0bc101394a9c995109c1cdfb3eacff7c7c18b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Tue, 10 Dec 2024 09:50:07 +0100 Subject: [PATCH 09/14] ecall-keccak: reorganize ecall cases --- ceno_emul/src/vm_state.rs | 42 +++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/ceno_emul/src/vm_state.rs b/ceno_emul/src/vm_state.rs index d2ca1991d..27d6b12c4 100644 --- a/ceno_emul/src/vm_state.rs +++ b/ceno_emul/src/vm_state.rs @@ -6,7 +6,7 @@ use crate::{ addr::{ByteAddr, RegIdx, Word, WordAddr}, platform::Platform, rv32im::{DecodedInstruction, Emulator, TrapCause}, - syscalls::{KECCAK_PERMUTE, SyscallEffects, handle_syscall}, + syscalls::{SyscallEffects, handle_syscall}, tracer::{Change, StepRecord, Tracer}, }; use anyhow::{Result, anyhow}; @@ -134,25 +134,29 @@ impl EmuContext for VMState { self.halt(); Ok(true) - } else if self.platform.unsafe_ecall_nop { - if function == KECCAK_PERMUTE { - let effects = handle_syscall(self, function, arg0)?; - self.apply_syscall(effects)?; - Ok(true) - } else { - // TODO: remove this example. - // Treat unknown ecalls as all powerful instructions: - // Read two registers, write one register, write one memory word, and branch. - tracing::warn!("ecall ignored: syscall_id={}", function); - self.store_register(DecodedInstruction::RD_NULL as RegIdx, 0)?; - // Example ecall effect - any writable address will do. - let addr = (self.platform.stack_top - WORD_SIZE as u32).into(); - self.store_memory(addr, self.peek_memory(addr))?; - self.set_pc(ByteAddr(self.pc) + PC_STEP_SIZE); - Ok(true) - } } else { - self.trap(TrapCause::EcallError) + match handle_syscall(self, function, arg0) { + Ok(effects) => { + self.apply_syscall(effects)?; + Ok(true) + } + Err(err) if self.platform.unsafe_ecall_nop => { + tracing::warn!("ecall ignored with unsafe_ecall_nop: {:?}", err); + // TODO: remove this example. + // Treat unknown ecalls as all powerful instructions: + // Read two registers, write one register, write one memory word, and branch. + self.store_register(DecodedInstruction::RD_NULL as RegIdx, 0)?; + // Example ecall effect - any writable address will do. + let addr = (self.platform.stack_top - WORD_SIZE as u32).into(); + self.store_memory(addr, self.peek_memory(addr))?; + self.set_pc(ByteAddr(self.pc) + PC_STEP_SIZE); + Ok(true) + } + Err(err) => { + tracing::error!("ecall error: {:?}", err); + self.trap(TrapCause::EcallError) + } + } } } From 241452b4eea2434ad319673d5ae4c22ac2a34d01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Tue, 10 Dec 2024 10:51:20 +0100 Subject: [PATCH 10/14] ecall-keccak: move register accesses to syscall module --- ceno_emul/src/syscalls.rs | 25 ++++++++++++++++++------- ceno_emul/src/tracer.rs | 24 +++++++++++++++++++----- ceno_emul/src/vm_state.rs | 12 ++++++------ ceno_emul/tests/test_elf.rs | 7 ++++++- 4 files changed, 49 insertions(+), 19 deletions(-) diff --git a/ceno_emul/src/syscalls.rs b/ceno_emul/src/syscalls.rs index 5c2ddf3b7..99b764364 100644 --- a/ceno_emul/src/syscalls.rs +++ b/ceno_emul/src/syscalls.rs @@ -1,4 +1,4 @@ -use crate::{Change, EmuContext, VMState, WORD_SIZE, WordAddr, WriteOp}; +use crate::{Change, EmuContext, Platform, VMState, WORD_SIZE, WordAddr, WriteOp}; use anyhow::Result; use itertools::{Itertools, izip}; use tiny_keccak::keccakf; @@ -7,22 +7,22 @@ use tiny_keccak::keccakf; #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct SyscallWitness { pub mem_writes: Vec, + pub reg_accesses: Vec, } /// The effects of a syscall to apply on the VM. #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct SyscallEffects { pub witness: SyscallWitness, - pub return_value: Option, pub next_pc: Option, } pub const KECCAK_PERMUTE: u32 = 0x00_01_01_09; /// Trace the inputs and effects of a syscall. -pub fn handle_syscall(vm: &VMState, function_code: u32, arg0: u32) -> Result { +pub fn handle_syscall(vm: &VMState, function_code: u32) -> Result { match function_code { - KECCAK_PERMUTE => Ok(keccak_permute(vm, arg0)), + KECCAK_PERMUTE => Ok(keccak_permute(vm)), _ => Err(anyhow::anyhow!("Unknown syscall: {}", function_code)), } } @@ -36,7 +36,16 @@ const KECCAK_WORDS: usize = KECCAK_CELLS * 2; // u32 words /// https://github.com/succinctlabs/sp1/blob/013c24ea2fa15a0e7ed94f7d11a7ada4baa39ab9/crates/core/executor/src/syscalls/precompiles/keccak256/permute.rs /// /// TODO: test compatibility. -fn keccak_permute(vm: &VMState, state_ptr: u32) -> SyscallEffects { +fn keccak_permute(vm: &VMState) -> SyscallEffects { + let state_ptr = vm.peek_register(Platform::reg_arg0()); + + // Read the argument `state_ptr`. + let reg_accesses = vec![WriteOp::new_register_op( + Platform::reg_arg0(), + Change::new(state_ptr, state_ptr), + 0, // Set later by Tracer. + )]; + let addrs = (state_ptr..) .step_by(WORD_SIZE) .take(KECCAK_WORDS) @@ -72,8 +81,10 @@ fn keccak_permute(vm: &VMState, state_ptr: u32) -> SyscallEffects { assert_eq!(mem_writes.len(), KECCAK_WORDS); SyscallEffects { - witness: SyscallWitness { mem_writes }, - return_value: None, + witness: SyscallWitness { + mem_writes, + reg_accesses, + }, next_pc: None, } } diff --git a/ceno_emul/src/tracer.rs b/ceno_emul/src/tracer.rs index 8aa55cf79..88ef444cc 100644 --- a/ceno_emul/src/tracer.rs +++ b/ceno_emul/src/tracer.rs @@ -1,5 +1,7 @@ use std::{collections::HashMap, fmt, mem}; +use itertools::chain; + use crate::{ CENO_PLATFORM, InsnKind, PC_STEP_SIZE, Platform, addr::{ByteAddr, Cycle, RegIdx, Word, WordAddr}, @@ -47,6 +49,15 @@ pub struct MemOp { } impl MemOp { + pub fn new_register_op(idx: RegIdx, value: T, previous_cycle: Cycle) -> MemOp { + let addr = Platform::register_vma(idx).into(); + MemOp { + addr, + value, + previous_cycle, + } + } + /// Get the register index of this operation. pub fn register_index(&self) -> RegIdx { Platform::register_index(self.addr.into()) @@ -395,12 +406,15 @@ impl Tracer { } pub fn track_syscall(&mut self, mut effects: SyscallEffects) { - let cycle = self.record.cycle + Self::SUBCYCLE_MEM; - for op in &mut effects.witness.mem_writes { - op.previous_cycle = self.track_access(op.addr, Self::SUBCYCLE_MEM); + // Keep track of the cycles of registers and memory accesses. + for op in chain( + &mut effects.witness.reg_accesses, + &mut effects.witness.mem_writes, + ) { + op.previous_cycle = self.track_access(op.addr, 0); assert_ne!( - op.previous_cycle, cycle, - "Memory address {:?} was accessed twice in the same cycle", + op.previous_cycle, self.record.cycle, + "Address {:?} was accessed twice in the same cycle", op.addr ); } diff --git a/ceno_emul/src/vm_state.rs b/ceno_emul/src/vm_state.rs index 27d6b12c4..0eba5fe9c 100644 --- a/ceno_emul/src/vm_state.rs +++ b/ceno_emul/src/vm_state.rs @@ -112,8 +112,8 @@ impl VMState { self.memory.insert(write_op.addr, write_op.value.after); } - if let Some(return_value) = effects.return_value { - self.store_register(Platform::reg_arg0(), return_value)?; + for reg_access in &effects.witness.reg_accesses { + self.registers[reg_access.register_index()] = reg_access.value.after; } let next_pc = effects.next_pc.unwrap_or(self.pc + PC_STEP_SIZE as u32); @@ -128,14 +128,13 @@ impl EmuContext for VMState { // Expect an ecall to terminate the program: function HALT with argument exit_code. fn ecall(&mut self) -> Result { let function = self.load_register(Platform::reg_ecall())?; - let arg0 = self.load_register(Platform::reg_arg0())?; if function == Platform::ecall_halt() { - tracing::debug!("halt with exit_code={}", arg0); - + let exit_code = self.load_register(Platform::reg_arg0())?; + tracing::debug!("halt with exit_code={}", exit_code); self.halt(); Ok(true) } else { - match handle_syscall(self, function, arg0) { + match handle_syscall(self, function) { Ok(effects) => { self.apply_syscall(effects)?; Ok(true) @@ -145,6 +144,7 @@ impl EmuContext for VMState { // TODO: remove this example. // Treat unknown ecalls as all powerful instructions: // Read two registers, write one register, write one memory word, and branch. + let _arg0 = self.load_register(Platform::reg_arg0())?; self.store_register(DecodedInstruction::RD_NULL as RegIdx, 0)?; // Example ecall effect - any writable address will do. let addr = (self.platform.stack_top - WORD_SIZE as u32).into(); diff --git a/ceno_emul/tests/test_elf.rs b/ceno_emul/tests/test_elf.rs index afbd67a12..0aa3da539 100644 --- a/ceno_emul/tests/test_elf.rs +++ b/ceno_emul/tests/test_elf.rs @@ -100,8 +100,13 @@ fn test_ceno_rt_keccak() -> Result<()> { // Check the syscall effects. for (witness, expect) in izip!(syscalls, keccak_outs) { - assert_eq!(witness.mem_writes.len(), expect.len() * 2); + assert_eq!(witness.reg_accesses.len(), 1); + assert_eq!( + witness.reg_accesses[0].register_index(), + Platform::reg_arg0() + ); + assert_eq!(witness.mem_writes.len(), expect.len() * 2); let got = witness .mem_writes .chunks_exact(2) From 62737a3f2b1a0db99e76c9c5161f33d5759d0b77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Tue, 10 Dec 2024 11:17:31 +0100 Subject: [PATCH 11/14] ecall-keccak: encapsulate construction details --- ceno_emul/src/syscalls.rs | 37 ++++++++++++++++++++++++++++++++++--- ceno_emul/src/tracer.rs | 21 ++++----------------- ceno_emul/src/vm_state.rs | 8 ++++---- 3 files changed, 42 insertions(+), 24 deletions(-) diff --git a/ceno_emul/src/syscalls.rs b/ceno_emul/src/syscalls.rs index 99b764364..3c37b117a 100644 --- a/ceno_emul/src/syscalls.rs +++ b/ceno_emul/src/syscalls.rs @@ -1,6 +1,8 @@ -use crate::{Change, EmuContext, Platform, VMState, WORD_SIZE, WordAddr, WriteOp}; +use crate::{ + Change, EmuContext, Platform, RegIdx, Tracer, VMState, WORD_SIZE, Word, WordAddr, WriteOp, +}; use anyhow::Result; -use itertools::{Itertools, izip}; +use itertools::{Itertools, chain, izip}; use tiny_keccak::keccakf; /// A syscall event, available to the circuit witness generators. @@ -13,10 +15,39 @@ pub struct SyscallWitness { /// The effects of a syscall to apply on the VM. #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct SyscallEffects { - pub witness: SyscallWitness, + /// The witness being built. Get it with `finalize`. + witness: SyscallWitness, + + /// The next PC after the syscall. Defaults to the next instruction. pub next_pc: Option, } +impl SyscallEffects { + /// Iterate over the register values after the syscall. + pub fn iter_reg_values(&self) -> impl Iterator + '_ { + self.witness + .reg_accesses + .iter() + .map(|op| (op.register_index(), op.value.after)) + } + + /// Iterate over the memory values after the syscall. + pub fn iter_mem_values(&self) -> impl Iterator + '_ { + self.witness + .mem_writes + .iter() + .map(|op| (op.addr, op.value.after)) + } + + /// Keep track of the cycles of registers and memory accesses. + pub fn finalize(mut self, tracer: &mut Tracer) -> SyscallWitness { + for op in chain(&mut self.witness.reg_accesses, &mut self.witness.mem_writes) { + op.previous_cycle = tracer.track_access(op.addr, 0); + } + self.witness + } +} + pub const KECCAK_PERMUTE: u32 = 0x00_01_01_09; /// Trace the inputs and effects of a syscall. diff --git a/ceno_emul/src/tracer.rs b/ceno_emul/src/tracer.rs index 88ef444cc..fd9a12cf0 100644 --- a/ceno_emul/src/tracer.rs +++ b/ceno_emul/src/tracer.rs @@ -1,7 +1,5 @@ use std::{collections::HashMap, fmt, mem}; -use itertools::chain; - use crate::{ CENO_PLATFORM, InsnKind, PC_STEP_SIZE, Platform, addr::{ByteAddr, Cycle, RegIdx, Word, WordAddr}, @@ -405,29 +403,18 @@ impl Tracer { }); } - pub fn track_syscall(&mut self, mut effects: SyscallEffects) { - // Keep track of the cycles of registers and memory accesses. - for op in chain( - &mut effects.witness.reg_accesses, - &mut effects.witness.mem_writes, - ) { - op.previous_cycle = self.track_access(op.addr, 0); - assert_ne!( - op.previous_cycle, self.record.cycle, - "Address {:?} was accessed twice in the same cycle", - op.addr - ); - } + pub fn track_syscall(&mut self, effects: SyscallEffects) { + let witness = effects.finalize(self); assert!(self.record.syscall.is_none(), "Only one syscall per step"); - self.record.syscall = Some(effects.witness); + self.record.syscall = Some(witness); } /// - Return the cycle when an address was last accessed. /// - Return 0 if this is the first access. /// - Record the current instruction as the origin of the latest access. /// - Accesses within the same instruction are distinguished by `subcycle ∈ [0, 3]`. - fn track_access(&mut self, addr: WordAddr, subcycle: Cycle) -> Cycle { + pub fn track_access(&mut self, addr: WordAddr, subcycle: Cycle) -> Cycle { self.latest_accesses .insert(addr, self.record.cycle + subcycle) .unwrap_or(0) diff --git a/ceno_emul/src/vm_state.rs b/ceno_emul/src/vm_state.rs index 0eba5fe9c..536d77d3e 100644 --- a/ceno_emul/src/vm_state.rs +++ b/ceno_emul/src/vm_state.rs @@ -108,12 +108,12 @@ impl VMState { } fn apply_syscall(&mut self, effects: SyscallEffects) -> Result<()> { - for write_op in &effects.witness.mem_writes { - self.memory.insert(write_op.addr, write_op.value.after); + for (addr, value) in effects.iter_mem_values() { + self.memory.insert(addr, value); } - for reg_access in &effects.witness.reg_accesses { - self.registers[reg_access.register_index()] = reg_access.value.after; + for (idx, value) in effects.iter_reg_values() { + self.registers[idx] = value; } let next_pc = effects.next_pc.unwrap_or(self.pc + PC_STEP_SIZE as u32); From e987066e85adea6d39011a2711afdc12d28079f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Tue, 10 Dec 2024 11:29:58 +0100 Subject: [PATCH 12/14] ecall-keccak: move Keccak to its own module --- ceno_emul/src/syscalls.rs | 91 ++++-------------------- ceno_emul/src/syscalls/keccak_permute.rs | 68 ++++++++++++++++++ ceno_emul/src/tracer.rs | 3 +- 3 files changed, 83 insertions(+), 79 deletions(-) create mode 100644 ceno_emul/src/syscalls/keccak_permute.rs diff --git a/ceno_emul/src/syscalls.rs b/ceno_emul/src/syscalls.rs index 3c37b117a..e5b3413cd 100644 --- a/ceno_emul/src/syscalls.rs +++ b/ceno_emul/src/syscalls.rs @@ -1,9 +1,18 @@ -use crate::{ - Change, EmuContext, Platform, RegIdx, Tracer, VMState, WORD_SIZE, Word, WordAddr, WriteOp, -}; +use crate::{RegIdx, Tracer, VMState, Word, WordAddr, WriteOp}; use anyhow::Result; -use itertools::{Itertools, chain, izip}; -use tiny_keccak::keccakf; +use itertools::chain; + +mod keccak_permute; + +pub const KECCAK_PERMUTE: u32 = 0x00_01_01_09; + +/// Trace the inputs and effects of a syscall. +pub fn handle_syscall(vm: &VMState, function_code: u32) -> Result { + match function_code { + KECCAK_PERMUTE => Ok(keccak_permute::keccak_permute(vm)), + _ => Err(anyhow::anyhow!("Unknown syscall: {}", function_code)), + } +} /// A syscall event, available to the circuit witness generators. #[derive(Clone, Debug, Default, PartialEq, Eq)] @@ -47,75 +56,3 @@ impl SyscallEffects { self.witness } } - -pub const KECCAK_PERMUTE: u32 = 0x00_01_01_09; - -/// Trace the inputs and effects of a syscall. -pub fn handle_syscall(vm: &VMState, function_code: u32) -> Result { - match function_code { - KECCAK_PERMUTE => Ok(keccak_permute(vm)), - _ => Err(anyhow::anyhow!("Unknown syscall: {}", function_code)), - } -} - -const KECCAK_CELLS: usize = 25; // u64 cells -const KECCAK_WORDS: usize = KECCAK_CELLS * 2; // u32 words - -/// Trace the execution of a Keccak permutation. -/// -/// Compatible with: -/// https://github.com/succinctlabs/sp1/blob/013c24ea2fa15a0e7ed94f7d11a7ada4baa39ab9/crates/core/executor/src/syscalls/precompiles/keccak256/permute.rs -/// -/// TODO: test compatibility. -fn keccak_permute(vm: &VMState) -> SyscallEffects { - let state_ptr = vm.peek_register(Platform::reg_arg0()); - - // Read the argument `state_ptr`. - let reg_accesses = vec![WriteOp::new_register_op( - Platform::reg_arg0(), - Change::new(state_ptr, state_ptr), - 0, // Set later by Tracer. - )]; - - let addrs = (state_ptr..) - .step_by(WORD_SIZE) - .take(KECCAK_WORDS) - .map(WordAddr::from) - .collect_vec(); - - // Read Keccak state. - let input = addrs - .iter() - .map(|&addr| vm.peek_memory(addr)) - .collect::>(); - - // Compute Keccak permutation. - let output = { - let mut state = [0_u64; KECCAK_CELLS]; - izip!(state.iter_mut(), input.chunks_exact(2)).for_each(|(cell, chunk)| { - let lo = chunk[0] as u64; - let hi = chunk[1] as u64; - *cell = lo | hi << 32; - }); - keccakf(&mut state); - state.into_iter().flat_map(|c| [c as u32, (c >> 32) as u32]) - }; - - // Write permuted state. - let mem_writes = izip!(addrs, input, output) - .map(|(addr, before, after)| WriteOp { - addr, - value: Change { before, after }, - previous_cycle: 0, // Set later by Tracer. - }) - .collect_vec(); - - assert_eq!(mem_writes.len(), KECCAK_WORDS); - SyscallEffects { - witness: SyscallWitness { - mem_writes, - reg_accesses, - }, - next_pc: None, - } -} diff --git a/ceno_emul/src/syscalls/keccak_permute.rs b/ceno_emul/src/syscalls/keccak_permute.rs new file mode 100644 index 000000000..05c04c872 --- /dev/null +++ b/ceno_emul/src/syscalls/keccak_permute.rs @@ -0,0 +1,68 @@ +use itertools::{Itertools, izip}; +use tiny_keccak::keccakf; + +use crate::{Change, EmuContext, Platform, VMState, WORD_SIZE, WordAddr, WriteOp}; + +use super::{SyscallEffects, SyscallWitness}; + +const KECCAK_CELLS: usize = 25; // u64 cells +const KECCAK_WORDS: usize = KECCAK_CELLS * 2; // u32 words + +/// Trace the execution of a Keccak permutation. +/// +/// Compatible with: +/// https://github.com/succinctlabs/sp1/blob/013c24ea2fa15a0e7ed94f7d11a7ada4baa39ab9/crates/core/executor/src/syscalls/precompiles/keccak256/permute.rs +/// +/// TODO: test compatibility. +pub fn keccak_permute(vm: &VMState) -> SyscallEffects { + let state_ptr = vm.peek_register(Platform::reg_arg0()); + + // Read the argument `state_ptr`. + let reg_accesses = vec![WriteOp::new_register_op( + Platform::reg_arg0(), + Change::new(state_ptr, state_ptr), + 0, // Cycle set later in finalize(). + )]; + + let addrs = (state_ptr..) + .step_by(WORD_SIZE) + .take(KECCAK_WORDS) + .map(WordAddr::from) + .collect_vec(); + + // Read Keccak state. + let input = addrs + .iter() + .map(|&addr| vm.peek_memory(addr)) + .collect::>(); + + // Compute Keccak permutation. + let output = { + let mut state = [0_u64; KECCAK_CELLS]; + izip!(state.iter_mut(), input.chunks_exact(2)).for_each(|(cell, chunk)| { + let lo = chunk[0] as u64; + let hi = chunk[1] as u64; + *cell = lo | hi << 32; + }); + keccakf(&mut state); + state.into_iter().flat_map(|c| [c as u32, (c >> 32) as u32]) + }; + + // Write permuted state. + let mem_writes = izip!(addrs, input, output) + .map(|(addr, before, after)| WriteOp { + addr, + value: Change { before, after }, + previous_cycle: 0, // Cycle set later in finalize(). + }) + .collect_vec(); + + assert_eq!(mem_writes.len(), KECCAK_WORDS); + SyscallEffects { + witness: SyscallWitness { + mem_writes, + reg_accesses, + }, + next_pc: None, + } +} diff --git a/ceno_emul/src/tracer.rs b/ceno_emul/src/tracer.rs index fd9a12cf0..a7e5b6fd6 100644 --- a/ceno_emul/src/tracer.rs +++ b/ceno_emul/src/tracer.rs @@ -48,9 +48,8 @@ pub struct MemOp { impl MemOp { pub fn new_register_op(idx: RegIdx, value: T, previous_cycle: Cycle) -> MemOp { - let addr = Platform::register_vma(idx).into(); MemOp { - addr, + addr: Platform::register_vma(idx).into(), value, previous_cycle, } From f88cf8e18c5a0a3a44e19047b2ea81de412d632a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Fri, 13 Dec 2024 08:08:45 +0100 Subject: [PATCH 13/14] ecall-keccak: for_each to for --- ceno_emul/src/syscalls/keccak_permute.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ceno_emul/src/syscalls/keccak_permute.rs b/ceno_emul/src/syscalls/keccak_permute.rs index 05c04c872..63decd3eb 100644 --- a/ceno_emul/src/syscalls/keccak_permute.rs +++ b/ceno_emul/src/syscalls/keccak_permute.rs @@ -39,12 +39,12 @@ pub fn keccak_permute(vm: &VMState) -> SyscallEffects { // Compute Keccak permutation. let output = { let mut state = [0_u64; KECCAK_CELLS]; - izip!(state.iter_mut(), input.chunks_exact(2)).for_each(|(cell, chunk)| { - let lo = chunk[0] as u64; - let hi = chunk[1] as u64; - *cell = lo | hi << 32; - }); + for (cell, (&lo, &hi)) in izip!(&mut state, input.iter().tuples()) { + *cell = lo as u64 | (hi as u64) << 32; + } + keccakf(&mut state); + state.into_iter().flat_map(|c| [c as u32, (c >> 32) as u32]) }; From ff9bf415da2867e38db39971e4c60e430d8a2cc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Fri, 13 Dec 2024 08:33:34 +0100 Subject: [PATCH 14/14] ecall-keccak: add reference --- ceno_emul/src/syscalls.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ceno_emul/src/syscalls.rs b/ceno_emul/src/syscalls.rs index e5b3413cd..5980506cb 100644 --- a/ceno_emul/src/syscalls.rs +++ b/ceno_emul/src/syscalls.rs @@ -4,6 +4,9 @@ use itertools::chain; mod keccak_permute; +// Using the same function codes as sp1: +// https://github.com/succinctlabs/sp1/blob/013c24ea2fa15a0e7ed94f7d11a7ada4baa39ab9/crates/core/executor/src/syscalls/code.rs + pub const KECCAK_PERMUTE: u32 = 0x00_01_01_09; /// Trace the inputs and effects of a syscall.