diff --git a/Cargo.lock b/Cargo.lock index a28f8f71cc..15c017f836 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -149,6 +149,18 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", + "serde", +] + [[package]] name = "bumpalo" version = "3.12.0" @@ -416,12 +428,13 @@ dependencies = [ [[package]] name = "csv" -version = "1.2.0" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af91f40b7355f82b0a891f50e70399475945bb0b0da4f1700ce60761c9d3e359" +checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" dependencies = [ + "bstr", "csv-core", - "itoa", + "itoa 0.4.8", "ryu", "serde", ] @@ -468,9 +481,9 @@ dependencies = [ [[package]] name = "either" -version = "1.8.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" [[package]] name = "envmnt" @@ -559,9 +572,9 @@ dependencies = [ [[package]] name = "heck" -version = "0.4.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" [[package]] name = "hermit-abi" @@ -630,6 +643,12 @@ dependencies = [ "either", ] +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + [[package]] name = "itoa" version = "1.0.5" @@ -669,12 +688,6 @@ version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" -[[package]] -name = "libm" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" - [[package]] name = "libmimalloc-sys" version = "0.1.30" @@ -769,7 +782,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", - "libm", ] [[package]] @@ -893,9 +905,9 @@ dependencies = [ [[package]] name = "proptest" -version = "1.1.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29f1b898011ce9595050a68e60f90bad083ff2987a695a42357134c8381fba70" +checksum = "1e0d9cc07f18492d879586c92b485def06bc850da3118075cd45d50e9c95b0e5" dependencies = [ "bit-set", "bitflags", @@ -909,7 +921,6 @@ dependencies = [ "regex-syntax", "rusty-fork", "tempfile", - "unarray", ] [[package]] @@ -1012,6 +1023,12 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" + [[package]] name = "regex-syntax" version = "0.6.28" @@ -1161,7 +1178,7 @@ version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" dependencies = [ - "itoa", + "itoa 1.0.5", "ryu", "serde", ] @@ -1389,12 +1406,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" -[[package]] -name = "unarray" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" - [[package]] name = "unicode-ident" version = "1.0.6" diff --git a/cairo_programs/keccak_builtin.cairo b/cairo_programs/keccak_builtin.cairo new file mode 100644 index 0000000000..2f7b9bf429 --- /dev/null +++ b/cairo_programs/keccak_builtin.cairo @@ -0,0 +1,18 @@ +%builtins keccak + from starkware.cairo.common.cairo_builtins import KeccakBuiltin + from starkware.cairo.common.keccak_state import KeccakBuiltinState + + func main{keccak_ptr: KeccakBuiltin*}() { + assert keccak_ptr[0].input = KeccakBuiltinState(1,2,3,4,5,6,7,8); + let result = keccak_ptr[0].output; + let keccak_ptr = keccak_ptr + KeccakBuiltin.SIZE; + assert result.s0 = 528644516554364142278482415480021626364691973678134577961206; + assert result.s1 = 768681319646568210457759892191562701823009052229295869963057; + assert result.s2 = 1439835513376369408063324968379272676079109225238241190228026; + assert result.s3 = 1150396629165612276474514703759718478742374517669870754478270; + assert result.s4 = 1515147102575186161827863034255579930572231617017100845406254; + assert result.s5 = 1412568161597072838250338588041800080889949791225997426843744; + assert result.s6 = 982235455376248641031519404605670648838699214888770304613539; + assert result.s7 = 1339947803093378278438908448344904300127577306141693325151040; + return (); +} diff --git a/src/hint_processor/builtin_hint_processor/keccak_utils.rs b/src/hint_processor/builtin_hint_processor/keccak_utils.rs index af2c259fdf..af85a30cdd 100644 --- a/src/hint_processor/builtin_hint_processor/keccak_utils.rs +++ b/src/hint_processor/builtin_hint_processor/keccak_utils.rs @@ -181,10 +181,3 @@ fn left_pad(bytes_vector: &mut [u8], n_zeros: usize) -> Vec { res } - -pub(crate) fn left_pad_u64(bytes_vector: &mut [u64], n_zeros: usize) -> Vec { - let mut res: Vec = vec![0; n_zeros]; - res.extend(bytes_vector.iter()); - - res -} diff --git a/src/vm/errors/runner_errors.rs b/src/vm/errors/runner_errors.rs index fc216bfb31..391825e123 100644 --- a/src/vm/errors/runner_errors.rs +++ b/src/vm/errors/runner_errors.rs @@ -6,10 +6,7 @@ use thiserror::Error; use thiserror_no_std::Error; use super::memory_errors::MemoryError; -use crate::types::{ - errors::math_errors::MathError, - relocatable::{MaybeRelocatable, Relocatable}, -}; +use crate::types::{errors::math_errors::MathError, relocatable::Relocatable}; use felt::Felt; #[derive(Debug, PartialEq, Error)] @@ -37,7 +34,7 @@ pub enum RunnerError { #[error("Given builtins are not in appropiate order")] DisorderedBuiltins, #[error("Expected integer at address {0:?} to be smaller than 2^{1}, Got {2}")] - IntegerBiggerThanPowerOfTwo(MaybeRelocatable, u32, Felt), + IntegerBiggerThanPowerOfTwo(Relocatable, u32, Felt), #[error("{0}")] EcOpSameXCoordinate(String), #[error("EcOpBuiltin: point {0:?} is not on the curve")] @@ -84,8 +81,8 @@ pub enum RunnerError { Math(#[from] MathError), #[error("keccak_builtin: Failed to get first input address")] KeccakNoFirstInput, - #[error("keccak_builtin: Failed to convert input cells to u64 values")] - KeccakInputCellsNotU64, #[error("{0}: Expected integer at address {1}")] BuiltinExpectedInteger(&'static str, Relocatable), + #[error("keccak_builtin: Failed to convert input cells to u64 values")] + KeccakInputCellsNotU64, } diff --git a/src/vm/runners/builtin_runner/bitwise.rs b/src/vm/runners/builtin_runner/bitwise.rs index d5d5681785..9935a9575b 100644 --- a/src/vm/runners/builtin_runner/bitwise.rs +++ b/src/vm/runners/builtin_runner/bitwise.rs @@ -88,14 +88,14 @@ impl BitwiseBuiltinRunner { ) { if num_x.bits() > self.bitwise_builtin.total_n_bits as u64 { return Err(RunnerError::IntegerBiggerThanPowerOfTwo( - x_addr.into(), + x_addr, self.bitwise_builtin.total_n_bits, num_x.clone(), )); }; if num_y.bits() > self.bitwise_builtin.total_n_bits as u64 { return Err(RunnerError::IntegerBiggerThanPowerOfTwo( - y_addr.into(), + y_addr, self.bitwise_builtin.total_n_bits, num_y.clone(), )); diff --git a/src/vm/runners/builtin_runner/keccak.rs b/src/vm/runners/builtin_runner/keccak.rs index 4fd7939132..4e4576ce2b 100644 --- a/src/vm/runners/builtin_runner/keccak.rs +++ b/src/vm/runners/builtin_runner/keccak.rs @@ -1,7 +1,5 @@ -use crate::stdlib::prelude::*; - -use crate::hint_processor::builtin_hint_processor::keccak_utils::left_pad_u64; use crate::math_utils::safe_div_usize; +use crate::stdlib::{collections::HashMap, prelude::*}; use crate::types::instance_definitions::keccak_instance_def::KeccakInstanceDef; use crate::types::relocatable::{MaybeRelocatable, Relocatable}; use crate::vm::errors::memory_errors::{InsufficientAllocatedCellsError, MemoryError}; @@ -9,13 +7,15 @@ use crate::vm::errors::runner_errors::RunnerError; use crate::vm::vm_core::VirtualMachine; use crate::vm::vm_memory::memory::Memory; use crate::vm::vm_memory::memory_segments::MemorySegmentManager; +use crate::with_std::cell::RefCell; use felt::Felt; +use num_bigint::BigUint; use num_integer::div_ceil; -use num_traits::{One, ToPrimitive}; +use num_traits::One; use super::KECCAK_BUILTIN_NAME; -const KECCAK_ARRAY_LEN: usize = 25; +const KECCAK_FELT_BYTE_SIZE: usize = 25; // 200 / 8 #[derive(Debug, Clone)] pub struct KeccakBuiltinRunner { @@ -23,11 +23,11 @@ pub struct KeccakBuiltinRunner { pub base: usize, pub(crate) cells_per_instance: u32, pub(crate) n_input_cells: u32, - verified_addresses: Vec, pub(crate) stop_ptr: Option, pub(crate) included: bool, state_rep: Vec, instances_per_component: u32, + cache: RefCell>, } impl KeccakBuiltinRunner { @@ -38,10 +38,10 @@ impl KeccakBuiltinRunner { n_input_cells: instance_def._state_rep.len() as u32, cells_per_instance: instance_def.cells_per_builtin(), stop_ptr: None, - verified_addresses: Vec::new(), included, instances_per_component: instance_def._instance_per_component, state_rep: instance_def._state_rep.clone(), + cache: RefCell::new(HashMap::new()), } } @@ -76,49 +76,56 @@ impl KeccakBuiltinRunner { if index < self.n_input_cells as usize { return Ok(None); } - - let first_input_addr = (address - index).map_err(|_| RunnerError::KeccakNoFirstInput)?; - if self.verified_addresses.contains(&first_input_addr) { - return Ok(None); + if let Some(felt) = self.cache.borrow().get(&address) { + return Ok(Some(felt.into())); } - - let mut input_felts_u64 = vec![]; - - for i in 0..self.n_input_cells { - let val = match memory.get(&(first_input_addr + i as usize)?) { - Some(val) => val - .as_ref() - .get_int_ref() - .and_then(|x| x.to_u64()) - .ok_or(RunnerError::KeccakInputCellsNotU64)?, + let first_input_addr = (address - index)?; + let first_output_addr = (first_input_addr + self.n_input_cells as usize)?; + + let mut input_felts = vec![]; + + for i in 0..self.n_input_cells as usize { + let val = match memory.get(&(first_input_addr + i)?) { + Some(value) => { + let num = value + .get_int_ref() + .ok_or(RunnerError::BuiltinExpectedInteger( + KECCAK_BUILTIN_NAME, + (first_input_addr + i)?, + ))?; + if num >= &(Felt::one() << self.state_rep[i]) { + return Err(RunnerError::IntegerBiggerThanPowerOfTwo( + (first_input_addr + i)?, + self.state_rep[i], + num.clone(), + )); + } + num.clone() + } _ => return Ok(None), }; - input_felts_u64.push(val) + input_felts.push(val) } - if let Some((i, bits)) = self.state_rep.iter().enumerate().next() { - let val = memory.get_integer((first_input_addr + i)?)?; - if val.as_ref() >= &(Felt::one() << *bits) { - return Err(RunnerError::IntegerBiggerThanPowerOfTwo( - (first_input_addr + i)?.into(), - *bits, - val.into_owned(), - )); - } - - let len = input_felts_u64.len(); - let mut input_felts_u64 = left_pad_u64(&mut input_felts_u64, KECCAK_ARRAY_LEN - len) - .try_into() - .map_err(|_| RunnerError::SliceToArrayError)?; - - keccak::f1600(&mut input_felts_u64); - - return Ok(input_felts_u64 - .get(address.offset - 1) - .map(|x| Felt::from(*x).into())); + let input_message: Vec = input_felts + .iter() + .flat_map(|x| Self::right_pad(&x.to_biguint().to_bytes_le(), KECCAK_FELT_BYTE_SIZE)) + .collect(); + let keccak_result = Self::keccak_f(&input_message)?; + + let mut start_index = 0_usize; + for (i, bits) in self.state_rep.iter().enumerate() { + let end_index = start_index + *bits as usize / 8; + self.cache.borrow_mut().insert( + (first_output_addr + i)?, + Felt::from(BigUint::from_bytes_le( + &keccak_result[start_index..end_index], + )), + ); + start_index = end_index; } - Ok(None) + Ok(self.cache.borrow().get(&address).map(|x| x.into())) } pub fn get_allocated_memory_units(&self, vm: &VirtualMachine) -> Result { @@ -243,10 +250,29 @@ impl KeccakBuiltinRunner { // So the real number is 4 * 64 * 1024 = 262144. safe_div_usize(262144_usize, diluted_n_bits as usize).unwrap_or(0) } + + fn right_pad(bytes: &[u8], final_size: usize) -> Vec { + let zeros: Vec = vec![0; final_size - bytes.len()]; + let mut bytes_vector = bytes.to_vec(); + bytes_vector.extend(zeros); + bytes_vector + } + + fn keccak_f(input_message: &[u8]) -> Result, RunnerError> { + let bigint = BigUint::from_bytes_le(input_message); + let mut keccak_input = bigint.to_u64_digits(); + keccak_input.resize(25, 0); + // This unwrap wont fail as keccak_input's size is always 25 + let mut keccak_input: [u64; 25] = keccak_input.try_into().unwrap(); + keccak::f1600(&mut keccak_input); + Ok(keccak_input.iter().flat_map(|x| x.to_le_bytes()).collect()) + } } #[cfg(test)] mod tests { + use num_traits::Num; + use super::*; use crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor; use crate::relocatable; @@ -549,9 +575,13 @@ mod tests { let result = builtin.deduce_memory_cell(Relocatable::from((0, 25)), &memory); assert_eq!( result, - Ok(Some(MaybeRelocatable::from(Felt::new( - 3086936446498698982_u64 - )))) + Ok(Some(MaybeRelocatable::from( + Felt::from_str_radix( + "1006979841721999878391288827876533441431370448293338267890891", + 10 + ) + .unwrap() + ))) ); } @@ -581,56 +611,53 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn deduce_memory_cell_offset_first_addr_error() { - let memory = memory![ - ((0, 16), 43), - ((0, 17), 199), - ((0, 18), 0), - ((0, 19), 0), - ((0, 20), 0), - ((0, 21), 0), - ((0, 22), 0), - ((0, 23), 1), - ((0, 24), 0), - ((0, 25), 0), - ((0, 26), 43), - ((0, 27), 199), - ((0, 28), 0), - ((0, 29), 0), - ((0, 30), 0), - ((0, 31), 0), - ((0, 32), 0), - ((0, 33), 1), - ((0, 34), 0), - ((0, 35), 0) - ]; + fn deduce_memory_cell_expected_integer() { + let memory = memory![((0, 0), (1, 2))]; let mut builtin = KeccakBuiltinRunner::new(&KeccakInstanceDef::default(), true); - builtin.verified_addresses.push(Relocatable::from((0, 16))); + builtin.n_input_cells = 1; + builtin.cells_per_instance = 100; + + let result = builtin.deduce_memory_cell(Relocatable::from((0, 1)), &memory); + + assert_eq!( + result, + Err(RunnerError::BuiltinExpectedInteger( + KECCAK_BUILTIN_NAME, + (0, 0).into() + )) + ); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn deduce_memory_cell_missing_input_cells() { + let memory = memory![((0, 1), (1, 2))]; + + let mut builtin = KeccakBuiltinRunner::new(&KeccakInstanceDef::default(), true); + + builtin.n_input_cells = 1; + builtin.cells_per_instance = 100; + + let result = builtin.deduce_memory_cell(Relocatable::from((0, 1)), &memory); - let result = builtin.deduce_memory_cell(Relocatable::from((0, 25)), &memory); assert_eq!(result, Ok(None)); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn deduce_memory_cell_expected_integer() { + fn deduce_memory_cell_input_cell() { let memory = memory![((0, 0), (1, 2))]; let mut builtin = KeccakBuiltinRunner::new(&KeccakInstanceDef::default(), true); - builtin.n_input_cells = 0; + builtin.n_input_cells = 1; builtin.cells_per_instance = 100; - let result = builtin.deduce_memory_cell(Relocatable::from((0, 99)), &memory); + let result = builtin.deduce_memory_cell(Relocatable::from((0, 0)), &memory); - assert_eq!( - result, - Err(RunnerError::Memory(MemoryError::ExpectedInteger( - (0, 0).into() - ))) - ); + assert_eq!(result, Ok(None)); } #[test] @@ -695,4 +722,19 @@ mod tests { assert_eq!(result, 16384); } + + #[test] + fn right_pad() { + let num = [1_u8]; + let padded_num = KeccakBuiltinRunner::right_pad(&num, 5); + assert_eq!(padded_num, vec![1, 0, 0, 0, 0]); + } + + #[test] + fn keccak_f() { + let input_bytes = b"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; + let expected_output_bytes = b"\xf6\x98\x81\xe1\x00!\x1f.\xc4*\x8c\x0c\x7fF\xc8q8\xdf\xb9\xbe\x07H\xca7T1\xab\x16\x17\xa9\x11\xff-L\x87\xb2iY.\x96\x82x\xde\xbb\\up?uz:0\xee\x08\x1b\x15\xd6\n\xab\r\x0b\x87T:w\x0fH\xe7!f},\x08a\xe5\xbe8\x16\x13\x9a?\xad~<9\xf7\x03`\x8b\xd8\xa3F\x8aQ\xf9\n9\xcdD\xb7.X\xf7\x8e\x1f\x17\x9e \xe5i\x01rr\xdf\xaf\x99k\x9f\x8e\x84\\\xday`\xf1``\x02q+\x8e\xad\x96\xd8\xff\xff3<\xb6\x01o\xd7\xa6\x86\x9d\xea\xbc\xfb\x08\xe1\xa3\x1c\x06z\xab@\xa1\xc1\xb1xZ\x92\x96\xc0.\x01\x13g\x93\x87!\xa6\xa8z\x9c@\x0bY'\xe7\xa7Qr\xe5\xc1\xa3\xa6\x88H\xa5\xc0@9k:y\xd1Kw\xd5"; + let output_bytes = KeccakBuiltinRunner::keccak_f(input_bytes); + assert_eq!(output_bytes, Ok(expected_output_bytes.to_vec())); + } } diff --git a/tests/cairo_run_test.rs b/tests/cairo_run_test.rs index ca0f98a931..93f1cf7e30 100644 --- a/tests/cairo_run_test.rs +++ b/tests/cairo_run_test.rs @@ -1440,3 +1440,18 @@ fn cairo_run_poseidon_hash() { ) .expect("Couldn't run program"); } + +#[test] +fn cairo_run_keccak_builtin() { + let mut hint_executor = BuiltinHintProcessor::new_empty(); + let cairo_run_config = cairo_run::CairoRunConfig { + layout: "recursive", + ..cairo_vm::cairo_run::CairoRunConfig::default() + }; + cairo_run::cairo_run( + include_bytes!("cairo_programs/keccak_builtin.json"), + &cairo_run_config, + &mut hint_executor, + ) + .expect("Couldn't run program"); +}