From e4c725c0a7905894836bb81215352e590f76f27d Mon Sep 17 00:00:00 2001 From: Quentash Date: Mon, 4 Sep 2023 13:17:22 +0200 Subject: [PATCH 01/24] testing herodotus fct --- crates/evm/src/helpers.cairo | 21 ++ crates/evm/src/instructions.cairo | 1 + crates/evm/src/instructions/sha3.cairo | 163 +++++++++- crates/evm/src/tests/test_instructions.cairo | 1 + .../tests/test_instructions/test_sha3.cairo | 284 ++++++++++++++++++ crates/utils/src/bitwise.cairo | 78 +++++ crates/utils/src/hashing.cairo | 3 + crates/utils/src/hashing/README.md | 4 + crates/utils/src/hashing/hasher.cairo | 6 + crates/utils/src/hashing/keccak.cairo | 140 +++++++++ crates/utils/src/hashing/poseidon.cairo | 21 ++ crates/utils/src/hashing/tests.cairo | 2 + .../utils/src/hashing/tests/test_keccak.cairo | 128 ++++++++ .../src/hashing/tests/test_poseidon.cairo | 12 + crates/utils/src/helpers.cairo | 14 + crates/utils/src/lib.cairo | 3 + crates/utils/src/math.cairo | 34 +++ crates/utils/src/types.cairo | 2 + crates/utils/src/types/byte.cairo | 17 ++ crates/utils/src/types/bytes.cairo | 104 +++++++ 20 files changed, 1032 insertions(+), 6 deletions(-) create mode 100644 crates/evm/src/tests/test_instructions/test_sha3.cairo create mode 100644 crates/utils/src/bitwise.cairo create mode 100644 crates/utils/src/hashing.cairo create mode 100644 crates/utils/src/hashing/README.md create mode 100644 crates/utils/src/hashing/hasher.cairo create mode 100644 crates/utils/src/hashing/keccak.cairo create mode 100644 crates/utils/src/hashing/poseidon.cairo create mode 100644 crates/utils/src/hashing/tests.cairo create mode 100644 crates/utils/src/hashing/tests/test_keccak.cairo create mode 100644 crates/utils/src/hashing/tests/test_poseidon.cairo create mode 100644 crates/utils/src/types.cairo create mode 100644 crates/utils/src/types/byte.cairo create mode 100644 crates/utils/src/types/bytes.cairo diff --git a/crates/evm/src/helpers.cairo b/crates/evm/src/helpers.cairo index 1e5642410..0e7bfc9f1 100644 --- a/crates/evm/src/helpers.cairo +++ b/crates/evm/src/helpers.cairo @@ -12,3 +12,24 @@ impl U256IntoResultU32 of Into> { } } } + +// Try converting u256 to u64 +impl U256IntoResultU64 of Into> { + fn into(self: u256) -> Result { + match self.try_into() { + Option::Some(value) => Result::Ok(value), + Option::None(_) => Result::Err(EVMError::TypeConversionError(TYPE_CONVERSION_ERROR)) + } + } +} + +// Try converting u64 to u32 +impl U64IntoResultU32 of Into> { + fn into(self: u64) -> Result { + match self.try_into() { + Option::Some(value) => Result::Ok(value), + Option::None(_) => Result::Err(EVMError::TypeConversionError(TYPE_CONVERSION_ERROR)) + } + } +} + diff --git a/crates/evm/src/instructions.cairo b/crates/evm/src/instructions.cairo index 1c92b2be3..3ccf7b26a 100644 --- a/crates/evm/src/instructions.cairo +++ b/crates/evm/src/instructions.cairo @@ -21,6 +21,7 @@ mod push_operations; use push_operations::PushOperationsTrait; mod sha3; +use sha3::Sha3Trait; mod stop_and_arithmetic_operations; use stop_and_arithmetic_operations::StopAndArithmeticOperationsTrait; diff --git a/crates/evm/src/instructions/sha3.cairo b/crates/evm/src/instructions/sha3.cairo index f5a89f91c..78836b2f3 100644 --- a/crates/evm/src/instructions/sha3.cairo +++ b/crates/evm/src/instructions/sha3.cairo @@ -1,10 +1,161 @@ //! SHA3. // Internal imports -use evm::context::ExecutionContext; -use evm::context::ExecutionContextTrait; +use evm::context::{ExecutionContext, ExecutionContextTrait, BoxDynamicExecutionContextDestruct}; +use evm::stack::StackTrait; +use evm::errors::EVMError; +use evm::helpers::U256IntoResultU32; +use evm::helpers::U256IntoResultU64; +use evm::helpers::U64IntoResultU32; +//use keccak::keccak_u256s_be_inputs; +use keccak::keccak_u256s_le_inputs; +use keccak::cairo_keccak; +use evm::memory::{InternalMemoryTrait, MemoryTrait}; +use utils::helpers::u256_to_bytes_array; +use utils::helpers::u128_split; -/// SHA3 operation. -/// Hashes n bytes in memory at a given offset in memory. -/// # Specification: https://www.evm.codes/#20?fork=shanghai -fn exec_sha3(ref context: ExecutionContext) {} +use utils::hashing::keccak::KeccakTrait; +use utils::types::bytes::Bytes; + +use starknet::SyscallResultTrait; +use array::ArrayTrait; + +use debug::PrintTrait; + +#[generate_trait] +impl Sha3Impl of Sha3Trait { + /// SHA3 operation. + /// Hashes n bytes in memory at a given offset in memory. + /// # Specification: https://www.evm.codes/#20?fork=shanghai + fn exec_sha3(ref self: ExecutionContext) -> Result<(), EVMError> { + let offset: u32 = Into::>::into((self.stack.pop()?))?; + let mut size: u64 = Into::>::into((self.stack.pop()?))?; + + // let mut toHash: Array = ArrayTrait::::new(); + // let mut counter = 0; + // if size == 32 { + // let mut mem = self.memory.load_internal(offset+(32*counter)); + // let toHash: Bytes = u256_to_bytes_array(mem).span(); + + // let mut hash = KeccakTrait::keccak_cairo(toHash); + // hash.low = integer::u128_byte_reverse(hash.low); + // hash.high = integer::u128_byte_reverse(hash.high); + // let tmp = hash.low; + // hash.low = hash.high; + // hash.high = tmp; + + // return self.stack.push(hash); + + // } + + // loop { + // if size < 32 { + // break; + // } + + // let mut mem = self.memory.load_internal(offset+(32*counter)); + // let memu8: Bytes = u256_to_bytes_array(mem).span(); + + // let mut i = 0; + // loop { + // if i == 32 { + // break; + // } + + // toHash.append(*memu8[i]); + // i+=1; + // }; + + // counter += 1; + // size -= 32; + // }; + // if size > 0 { + // let mut mem = self.memory.load_internal(offset+(32*counter)); + // let memu8: Bytes = u256_to_bytes_array(mem).span(); + // let mut i = size; + // loop { + // if i == 0 { + // break; + // } + + // toHash.append(*memu8[size-i]); + // i-=1; + // }; + // } + + // let mut hash = KeccakTrait::keccak_cairo(toHash.span()); + // hash.low = integer::u128_byte_reverse(hash.low); + // hash.high = integer::u128_byte_reverse(hash.high); + // let tmp = hash.low; + // hash.low = hash.high; + // hash.high = tmp; + + // self.stack.push(hash) + + let mut toHash: Array = ArrayTrait::::new(); + let mut last_input: u64 = 0; + let mut counter = 0; + loop { + if size < 32 { + break; + } + + let mut mem = self.memory.load_internal(offset + (32 * counter)); + mem.low = integer::u128_byte_reverse(mem.low); + mem.high = integer::u128_byte_reverse(mem.high); + let tmp = mem.low; + mem.low = mem.high; + mem.high = tmp; + + let (highL, lowL) = u128_split(mem.low); + let (highH, lowH) = u128_split(mem.high); + toHash.append(lowL); + toHash.append(highL); + toHash.append(lowH); + toHash.append(highH); + + counter += 1; + size -= 32; + }; + if size > 0 { + let mut mem = self.memory.load_internal(offset + (32 * counter)); + mem.low = integer::u128_byte_reverse(mem.low); + mem.high = integer::u128_byte_reverse(mem.high); + let tmp = mem.low; + mem.low = mem.high; + mem.high = tmp; + let (highL, lowL) = u128_split(mem.low); + let (highH, lowH) = u128_split(mem.high); + + if size < 8 { + last_input = lowL; + } else if size < 16 { + size -= 8; + toHash.append(lowL); + last_input = highL; + } else if size < 24 { + size -= 16; + toHash.append(lowL); + toHash.append(highL); + last_input = lowH; + } else { + size -= 24; + toHash.append(lowL); + toHash.append(highL); + toHash.append(lowH); + last_input = highH; + } + } + + let mut hash = cairo_keccak( + ref toHash, last_input, Into::>::into((size))? + ); + hash.low = integer::u128_byte_reverse(hash.low); + hash.high = integer::u128_byte_reverse(hash.high); + let tmp = hash.low; + hash.low = hash.high; + hash.high = tmp; + + self.stack.push(hash) + } +} diff --git a/crates/evm/src/tests/test_instructions.cairo b/crates/evm/src/tests/test_instructions.cairo index 1a1048b11..1c7118bc8 100644 --- a/crates/evm/src/tests/test_instructions.cairo +++ b/crates/evm/src/tests/test_instructions.cairo @@ -4,3 +4,4 @@ mod test_duplication_operations; mod test_block_information; mod test_environment_information; mod test_push_operations; +mod test_sha3; diff --git a/crates/evm/src/tests/test_instructions/test_sha3.cairo b/crates/evm/src/tests/test_instructions/test_sha3.cairo new file mode 100644 index 000000000..4f1c67a14 --- /dev/null +++ b/crates/evm/src/tests/test_instructions/test_sha3.cairo @@ -0,0 +1,284 @@ +use evm::instructions::Sha3Trait; +use evm::tests::test_utils::setup_execution_context; +use evm::stack::StackTrait; +use option::OptionTrait; +use debug::PrintTrait; +use integer::BoundedInt; + +use evm::context::{ + ExecutionContext, ExecutionContextTrait, CallContextTrait, BoxDynamicExecutionContextDestruct +}; +use evm::memory::{InternalMemoryTrait, MemoryTrait}; + + +#[test] +#[available_gas(20000000)] +fn test_sha3_with_size_32() { + // Given + let bytecode: Span = array![1, 2, 3, 4, 5].span(); + let mut ctx = setup_execution_context(); + + ctx.stack.push(0x20); + ctx.stack.push(0x00); + + ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0); + + // When + ctx.exec_sha3(); + + // Then + // Resultat expected : 0x1b00e6bf2213698a78d172620d2852921faf54cf9645342bdb34ca455f69d44 + let result = ctx.stack.peek().unwrap(); + + result.print(); + assert( + result == 0x1b00e6bf2213698a78d172620d2852921faf54cf9645342bdb34ca455f69d44, 'wrong result' + ); +} + + +#[test] +#[available_gas(20000000)] +fn test_sha3_with_size_different_than_32() { + // Given + let bytecode: Span = array![1, 2, 3, 4, 5].span(); + let mut ctx = setup_execution_context(); + + ctx.stack.push(0x04); + ctx.stack.push(0x00); + + ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0); + + // When + ctx.exec_sha3(); + + // Then + // Resultat expected : 0x388880d56bbbf972c2ce97caeabfaf73cf1665aea364a2b06441a925d47db63e + let result = ctx.stack.peek().unwrap(); + assert( + result == 0x388880d56bbbf972c2ce97caeabfaf73cf1665aea364a2b06441a925d47db63e, 'wrong result' + ); +} + +#[test] +#[available_gas(20000000)] +fn test_sha3_with_size_32_and_offset() { + // Given + let bytecode: Span = array![1, 2, 3, 4, 5].span(); + let mut ctx = setup_execution_context(); + + ctx.stack.push(0x20); + ctx.stack.push(0x02); + + ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0); + + // When + ctx.exec_sha3(); + + // Then + // Resultat expected : 4c8266c7f1c12d2a0c99f03f5fb7314fcd4b762b34d58442fc0f23d2629b9dae + let result = ctx.stack.peek().unwrap(); + assert( + result == 0x4c8266c7f1c12d2a0c99f03f5fb7314fcd4b762b34d58442fc0f23d2629b9dae, 'wrong result' + ); +} + +#[test] +#[available_gas(20000000)] +fn test_sha3_with_size_different_than_32_and_offset() { + // Given + let bytecode: Span = array![1, 2, 3, 4, 5].span(); + let mut ctx = setup_execution_context(); + + ctx.stack.push(0x02); + ctx.stack.push(0x02); + + ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0); + + // When + ctx.exec_sha3(); + + // Then + // Resultat expected : 0xc142f597160775368dce2d1d8d9d847ee26f05edd4b6f00ef2451c3cab95e1a0 + let result = ctx.stack.peek().unwrap(); + assert( + result == 0xc142f597160775368dce2d1d8d9d847ee26f05edd4b6f00ef2451c3cab95e1a0, 'wrong result' + ); +} + +#[test] +#[available_gas(20000000)] +fn test_sha3_with_size_0() { + // Given + let bytecode: Span = array![1, 2, 3, 4, 5].span(); + let mut ctx = setup_execution_context(); + + ctx.stack.push(0x00); + ctx.stack.push(0x00); + + ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0); + + // When + ctx.exec_sha3(); + + // Then + // Resultat expected : 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 + let result = ctx.stack.peek().unwrap(); + assert( + result == 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470, 'wrong result' + ); +} + +#[test] +#[available_gas(20000000)] +fn test_sha3_with_size_0_and_offset() { + // Given + let bytecode: Span = array![1, 2, 3, 4, 5].span(); + let mut ctx = setup_execution_context(); + + ctx.stack.push(0x00); + ctx.stack.push(0x02); + + ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0); + + // When + ctx.exec_sha3(); + + // Then + // Resultat expected : 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 + let result = ctx.stack.peek().unwrap(); + assert( + result == 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470, 'wrong result' + ); +} + +#[test] +#[available_gas(20000000)] +fn test_sha3_with_offset_beyond_memory() { + // Given + let bytecode: Span = array![1, 2, 3, 4, 5].span(); + let mut ctx = setup_execution_context(); + + ctx.stack.push(0x20); + ctx.stack.push(0x21); + + ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0); + + // When + ctx.exec_sha3(); + + // Then + // Resultat expected : 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563 + let result = ctx.stack.peek().unwrap(); + assert( + result == 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563, 'wrong result' + ); +} + +#[test] +#[available_gas(20000000)] +fn test_sha3_with_offset_plus_size_beyond_memory() { + // Given + let bytecode: Span = array![1, 2, 3, 4, 5].span(); + let mut ctx = setup_execution_context(); + + ctx.stack.push(0x20); + ctx.stack.push(0x02); + + ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0); + + // When + ctx.exec_sha3(); + + // Then + // Resultat expected : 0x4c8266c7f1c12d2a0c99f03f5fb7314fcd4b762b34d58442fc0f23d2629b9dae + let result = ctx.stack.peek().unwrap(); + assert( + result == 0x4c8266c7f1c12d2a0c99f03f5fb7314fcd4b762b34d58442fc0f23d2629b9dae, 'wrong result' + ); +} + +#[test] +#[available_gas(20000000)] +fn test_sha3_with_size_128() { + // Given + let bytecode: Span = array![1, 2, 3, 4, 5].span(); + let mut ctx = setup_execution_context(); + + ctx.stack.push(0x80); + ctx.stack.push(0x00); + + ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0); + ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0x20); + ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0x40); + ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0x60); + + // When + ctx.exec_sha3(); + + // Then + // Resultat expected : 0x425c8158d6661daea881641c267cb0bf9e551e12af9c1fa7737ef3b219ab0eed + let result = ctx.stack.peek().unwrap(); + assert( + result == 0x425c8158d6661daea881641c267cb0bf9e551e12af9c1fa7737ef3b219ab0eed, 'wrong result' + ); +} + +#[test] +#[available_gas(20000000)] +fn test_sha3_with_size_133() { + // Given + let bytecode: Span = array![1, 2, 3, 4, 5].span(); + let mut ctx = setup_execution_context(); + + ctx.stack.push(0x85); + ctx.stack.push(0x00); + + ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0); + ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0x20); + ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0x40); + ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0x60); + ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0x80); + + // When + ctx.exec_sha3(); + + // Then + // Resultat expected : 0x73cfd6eb245c512d8247245573437a38db4e265a6945f119df30cc9ed2bac584 + let result = ctx.stack.peek().unwrap(); + assert( + result == 0x73cfd6eb245c512d8247245573437a38db4e265a6945f119df30cc9ed2bac584, 'wrong result' + ); +} + +#[test] +#[available_gas(20000000000)] +fn test_sha3_with_big_size() { + // Given + let bytecode: Span = array![1, 2, 3, 4, 5].span(); + let mut ctx = setup_execution_context(); + + ctx.stack.push(0x0C80); + ctx.stack.push(0x00); + + let mut memDst: u32 = 0; + loop { + if memDst > 0x0C80 { + break; + } + ctx + .memory + .store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, memDst); + memDst += 0x20; + }; + + // When + ctx.exec_sha3(); + + // Then + // Resultat expected : 0x2022ae07f3a362b08ac0a4bcb785c830cb5c368dc0ce6972249c6abbc68a5291 + let result = ctx.stack.peek().unwrap(); + assert( + result == 0x2022ae07f3a362b08ac0a4bcb785c830cb5c368dc0ce6972249c6abbc68a5291, 'wrong result' + ); +} diff --git a/crates/utils/src/bitwise.cairo b/crates/utils/src/bitwise.cairo new file mode 100644 index 000000000..81c6ae311 --- /dev/null +++ b/crates/utils/src/bitwise.cairo @@ -0,0 +1,78 @@ +use utils::math::{pow, pow_felt252}; +use math::Oneable; +use zeroable::Zeroable; + +// @notice Bitwise left shift +// @param num The number to be shifted +// @param shift The number of bits to shift +// @return The left shifted number +fn left_shift< + T, + impl TZeroable: Zeroable, + impl TAdd: Add, + impl TSub: Sub, + impl TMul: Mul, + impl TOneable: Oneable, + impl TCopy: Copy, + impl TDrop: Drop +// TODO refactor shift type from T to usize +>( + num: T, shift: T +) -> T { + let two = TOneable::one() + TOneable::one(); + num * pow(two, shift) +} + +fn left_shift_felt252(num: felt252, shift: felt252) -> felt252 { + num * pow_felt252(2, shift) +} + +// @notice Bitwise right shift +// @param num The number to be shifted +// @param shift The number of bits to shift +// @return The right shifted number +fn right_shift< + T, + impl TZeroable: Zeroable, + impl TAdd: Add, + impl TSub: Sub, + impl TMul: Mul, + impl TDiv: Div, + impl TOneable: Oneable, + impl TCopy: Copy, + impl TDrop: Drop +// TODO refactor shift type from T to usize +>( + num: T, shift: T +) -> T { + let two = TOneable::one() + TOneable::one(); + num / pow(two, shift) +} + +// @notice Bit length of a number +// @param num The number to be measured +// @return The number of bits in the number +fn bit_length< + T, + impl TZeroable: Zeroable, + impl TPartialOrd: PartialOrd, + impl TAddImpl: Add, + impl TSub: Sub, + impl TMul: Mul, + impl TOneable: Oneable, + impl TCopy: Copy, + impl TDrop: Drop +>( + num: T +) -> T { + let mut bit_position = TZeroable::zero(); + let mut cur_n = TOneable::one(); + loop { + if cur_n > num { + break (); + }; + bit_position = bit_position + TOneable::one(); + cur_n = left_shift(cur_n, TOneable::one()); + }; + bit_position +} diff --git a/crates/utils/src/hashing.cairo b/crates/utils/src/hashing.cairo new file mode 100644 index 000000000..01dc92816 --- /dev/null +++ b/crates/utils/src/hashing.cairo @@ -0,0 +1,3 @@ +mod hasher; +mod keccak; +mod poseidon; diff --git a/crates/utils/src/hashing/README.md b/crates/utils/src/hashing/README.md new file mode 100644 index 000000000..12c6c1649 --- /dev/null +++ b/crates/utils/src/hashing/README.md @@ -0,0 +1,4 @@ +# Hashing + +We provide a `Hasher` trait to ensure consistent and straightforward usage of generic hashers across your code. +In addition to the generic hasher trait, `cairo-lib` includes Ethereum-compatible keccak256, ensuring that your applications maintain compatibility with Ethereum-based and EVM based systems. diff --git a/crates/utils/src/hashing/hasher.cairo b/crates/utils/src/hashing/hasher.cairo new file mode 100644 index 000000000..5c5f078bc --- /dev/null +++ b/crates/utils/src/hashing/hasher.cairo @@ -0,0 +1,6 @@ +// @notice Hasher trait, a common interface for all hashers +trait Hasher { + fn hash_single(a: T) -> V; + fn hash_double(a: T, b: T) -> V; + fn hash_many(input: Span) -> V; +} diff --git a/crates/utils/src/hashing/keccak.cairo b/crates/utils/src/hashing/keccak.cairo new file mode 100644 index 000000000..e64165a4d --- /dev/null +++ b/crates/utils/src/hashing/keccak.cairo @@ -0,0 +1,140 @@ +use utils::hashing::hasher::Hasher; +use utils::math::pow; +use utils::types::bytes::Bytes; +use array::{ArrayTrait, SpanTrait}; +use keccak::{keccak_u256s_le_inputs, cairo_keccak}; +use traits::{Into, TryInto}; +use option::OptionTrait; +use starknet::SyscallResultTrait; + +#[derive(Drop)] +struct Keccak {} + +#[generate_trait] +impl KeccakHasher of KeccakTrait { + // @notice keccak256 hashes the input, matching Solidity keccak + // @param input The input to hash, in big endian + // @return The hash of the input, in little endian + fn keccak_cairo(bytes: Bytes) -> u256 { + let n = bytes.len(); + let q = n / 8; + let r = n % 8; + + let mut keccak_input = ArrayTrait::new(); + let mut i: usize = 0; + loop { + if i >= q { + break (); + } + + let val = (*bytes.at(8 * i)).into() + + (*bytes.at(8 * i + 1)).into() * 256 + + (*bytes.at(8 * i + 2)).into() * 65536 + + (*bytes.at(8 * i + 3)).into() * 16777216 + + (*bytes.at(8 * i + 4)).into() * 4294967296 + + (*bytes.at(8 * i + 5)).into() * 1099511627776 + + (*bytes.at(8 * i + 6)).into() * 281474976710656 + + (*bytes.at(8 * i + 7)).into() * 72057594037927936; + + keccak_input.append(val); + + i += 1; + }; + + let mut last_word: u64 = 0; + let mut k: usize = 0; + loop { + if k >= r { + break (); + } + + let current: u64 = (*bytes.at(8 * q + k)).into(); + last_word += current * pow(256, k.into()); + + k += 1; + }; + + cairo_keccak(ref keccak_input, last_word, r) + } +} + +impl KeccakHasherU256 of Hasher { + fn hash_single(a: u256) -> u256 { + let mut arr = array![a]; + keccak_u256s_le_inputs(arr.span()) + } + + fn hash_double(a: u256, b: u256) -> u256 { + let mut arr = array![a, b]; + keccak_u256s_le_inputs(arr.span()) + } + + fn hash_many(input: Span) -> u256 { + keccak_u256s_le_inputs(input) + } +} + +impl KeccakHasherSpanU8 of Hasher, u256> { + fn hash_single(a: Span) -> u256 { + let mut arr = ArrayTrait::new(); + let mut i: usize = 0; + loop { + if i >= a.len() { + break arr.span(); + } + let current = *a.at(i); + arr.append(current.into()); + i += 1; + }; + keccak_u256s_le_inputs(arr.span()) + } + + fn hash_double(a: Span, b: Span) -> u256 { + let mut arr = ArrayTrait::new(); + let mut i: usize = 0; + loop { + if i >= a.len() { + break arr.span(); + } + let current = *a.at(i); + arr.append(current.into()); + i += 1; + }; + + i = 0; + loop { + if i >= b.len() { + break arr.span(); + } + let current = *b.at(i); + arr.append(current.into()); + i += 1; + }; + keccak_u256s_le_inputs(arr.span()) + } + + fn hash_many(input: Span>) -> u256 { + let mut arr = ArrayTrait::new(); + let mut i: usize = 0; + let mut j: usize = 0; + loop { + if i >= input.len() { + break arr.span(); + } + + let current = *input.at(i); + loop { + if j >= current.len() { + break; + } + let current = *current.at(j); + arr.append(current.into()); + j += 1; + }; + i += 1; + }; + + keccak_u256s_le_inputs(arr.span()) + } +} + diff --git a/crates/utils/src/hashing/poseidon.cairo b/crates/utils/src/hashing/poseidon.cairo new file mode 100644 index 000000000..a72327077 --- /dev/null +++ b/crates/utils/src/hashing/poseidon.cairo @@ -0,0 +1,21 @@ +use utils::hashing::hasher::Hasher; +use poseidon::{poseidon_hash_span, hades_permutation}; + +struct Poseidon {} + +// Permutation params: https://docs.starknet.io/documentation/architecture_and_concepts/Cryptography/hash-functions/#poseidon_hash +impl PoseidonHasher of Hasher { + fn hash_single(a: felt252) -> felt252 { + let (single, _, _) = hades_permutation(a, 0, 1); + single + } + + fn hash_double(a: felt252, b: felt252) -> felt252 { + let (double, _, _) = hades_permutation(a, b, 2); + double + } + + fn hash_many(input: Span) -> felt252 { + poseidon_hash_span(input) + } +} diff --git a/crates/utils/src/hashing/tests.cairo b/crates/utils/src/hashing/tests.cairo new file mode 100644 index 000000000..ec4d3cf85 --- /dev/null +++ b/crates/utils/src/hashing/tests.cairo @@ -0,0 +1,2 @@ +mod test_keccak; +mod test_poseidon; diff --git a/crates/utils/src/hashing/tests/test_keccak.cairo b/crates/utils/src/hashing/tests/test_keccak.cairo new file mode 100644 index 000000000..b6172fb59 --- /dev/null +++ b/crates/utils/src/hashing/tests/test_keccak.cairo @@ -0,0 +1,128 @@ +use cairo_lib::hashing::keccak::KeccakTrait; +use array::ArrayTrait; +use debug::PrintTrait; + +#[test] +#[available_gas(99999999)] +fn test_keccak_cairo_full_byte() { + let mut input = array![0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]; + + let res = KeccakTrait::keccak_cairo(input.span()); + assert( + res == 0xAF7D4E460ACF8E540E682A9EE91EA1C08C1615C3889D75EB0A70660A4BFB0BAD, + 'Keccak output not matching' + ); +} + +#[test] +#[available_gas(99999999)] +fn test_keccak_cairo_empty_bytes() { + let mut input = array![]; + + let mut res = KeccakTrait::keccak_cairo(input.span()); + res.low = integer::u128_byte_reverse(res.low); + res.high = integer::u128_byte_reverse(res.high); + let tmp = res.low; + res.low = res.high; + res.high = tmp; + assert( + res == 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470, + 'Keccak output not matching' + ); +} + +#[test] +#[available_gas(99999999)] +fn test_keccak_cairo_32_bytes() { + let mut input = array![]; + input.append(0xFA); + input.append(0xFA); + input.append(0xFA); + input.append(0xFA); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); +} + +#[test] +#[available_gas(99999999999)] +fn test_keccak_cairo_lot_of_bytes() { + let mut input = array![]; + + let mut memDst: u32 = 0; + loop { + if memDst >= 0x0C80 { + break; + } + input.append(0xFA); + input.append(0xFA); + input.append(0xFA); + input.append(0xFA); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + input.append(0x00); + memDst += 0x20; + }; +} + +#[test] +#[available_gas(99999999)] +fn test_keccak_cairo_remainder() { + let mut input = array![0xab, 0x76, 0x8c, 0xf7, 0x89, 0xae, 0xfd, 0x23, 0x4a, 0xbc, 0xd2, 0x45]; + + let res = KeccakTrait::keccak_cairo(input.span()); + assert( + res == 0x82CBD5B00CD06A188C831D69CB9629C92A2D5E7A78CEA913C5F9AFF62E66BBB9, + 'Keccak output not matching' + ); +} diff --git a/crates/utils/src/hashing/tests/test_poseidon.cairo b/crates/utils/src/hashing/tests/test_poseidon.cairo new file mode 100644 index 000000000..785204a34 --- /dev/null +++ b/crates/utils/src/hashing/tests/test_poseidon.cairo @@ -0,0 +1,12 @@ +use cairo_lib::hashing::poseidon::PoseidonHasher; + +#[test] +#[available_gas(99999999)] +fn test_poseidon_hash_double() { + let a = 0x6109f1949f6a7555eccf4e15ce1f10fbd78091dfe715cc2e0c5a244d9d17761; + let b = 0x0194791558611599fe4ae0fcfa48f095659c90db18e54de86f2d2f547f7369bf; + let hash = PoseidonHasher::hash_double(a, b); + + let res = 0x7b8180db85fa1e0b5041f38f57926743905702c498576991f04998b5d9476b4; + assert(hash == res, 'Hash does not match'); +} diff --git a/crates/utils/src/helpers.cairo b/crates/utils/src/helpers.cairo index 8516f64dd..dc746ae55 100644 --- a/crates/utils/src/helpers.cairo +++ b/crates/utils/src/helpers.cairo @@ -101,6 +101,20 @@ fn split_word_128(value: u256, ref dst: Array) { split_word(value, 16, ref dst) } +/// Try to convert u128 to u64, +fn u128_to_u64(input: u128) -> u64 { + input.try_into().unwrap() +} + +/// Splits a u128 into two u64. +fn u128_split(input: u128) -> (u64, u64) { + let (high, low) = integer::u128_safe_divmod( + input, 0x10000000000000000_u128.try_into().unwrap() + ); + + (u128_to_u64(high), u128_to_u64(low)) +} + /// Loads a sequence of bytes into a single u128 in big-endian /// diff --git a/crates/utils/src/lib.cairo b/crates/utils/src/lib.cairo index eaeb53ecd..303107177 100644 --- a/crates/utils/src/lib.cairo +++ b/crates/utils/src/lib.cairo @@ -7,6 +7,9 @@ mod math; mod eth_transaction; mod rlp; mod traits; +mod types; +mod bitwise; +mod hashing; #[cfg(test)] diff --git a/crates/utils/src/math.cairo b/crates/utils/src/math.cairo index 5c43b8457..b52336010 100644 --- a/crates/utils/src/math.cairo +++ b/crates/utils/src/math.cairo @@ -1,5 +1,9 @@ use integer::{u256_overflow_mul, u256_overflowing_add, u512, BoundedInt}; +use zeroable::Zeroable; +use math::Oneable; +use traits::{Sub, Mul}; + trait Exponentiation { // Raise a number to a power. /// * `base` - The number to raise. @@ -85,3 +89,33 @@ fn u256_wide_add(a: u256, b: u256) -> u512 { u512 { limb0, limb1, limb2, limb3 } } + +// @notice Computes `base ^ exp` +// @param base The base of the exponentiation +// @param exp The exponent of the exponentiation +// @return The exponentiation result +fn pow< + T, + impl TZeroable: Zeroable, + impl TSub: Sub, + impl TMul: Mul, + impl TOneable: Oneable, + impl TCopy: Copy, + impl TDrop: Drop +>( + base: T, mut exp: T +) -> T { + if exp.is_zero() { + TOneable::one() + } else { + base * pow(base, exp - TOneable::one()) + } +} + +fn pow_felt252(base: felt252, exp: felt252) -> felt252 { + if exp == 0 { + 1 + } else { + base * pow_felt252(base, exp - 1) + } +} diff --git a/crates/utils/src/types.cairo b/crates/utils/src/types.cairo new file mode 100644 index 000000000..a42c43be0 --- /dev/null +++ b/crates/utils/src/types.cairo @@ -0,0 +1,2 @@ +mod byte; +mod bytes; diff --git a/crates/utils/src/types/byte.cairo b/crates/utils/src/types/byte.cairo new file mode 100644 index 000000000..c15b9d8fc --- /dev/null +++ b/crates/utils/src/types/byte.cairo @@ -0,0 +1,17 @@ +use utils::bitwise::right_shift; + +type Byte = u8; + +#[generate_trait] +impl ByteImpl of ByteTrait { + // @notice Extracts the high and low nibbles from a byte + // @return (high, low) + fn extract_nibbles(self: Byte) -> (Byte, Byte) { + let masked = self & 0xf0; + let high = right_shift(masked, 4); + let low = self & 0x0f; + + (high, low) + } +} + diff --git a/crates/utils/src/types/bytes.cairo b/crates/utils/src/types/bytes.cairo new file mode 100644 index 000000000..1e7b5b455 --- /dev/null +++ b/crates/utils/src/types/bytes.cairo @@ -0,0 +1,104 @@ +use utils::bitwise::{left_shift, left_shift_felt252}; +use utils::types::byte::Byte; +use array::SpanTrait; +use traits::{TryInto, Into}; + +type Bytes = Span; + +impl BytesTryIntoU256 of TryInto { + fn try_into(self: Bytes) -> Option { + let len = self.len(); + if len > 32 { + return Option::None(()); + } + + if self.is_empty() { + return Option::Some(0); + } + + let offset = len.into() - 1; + let mut result: u256 = 0; + let mut i: usize = 0; + loop { + if i >= len { + break (); + } + let byte: u256 = (*self.at(i)).into(); + result += left_shift(byte, 8 * (offset - i.into())); + + i += 1; + }; + + Option::Some(result) + } +} + +impl BytesTryIntoFelt252 of TryInto { + fn try_into(self: Bytes) -> Option { + let len = self.len(); + if len > 31 { + return Option::None(()); + } + + if self.is_empty() { + return Option::Some(0); + } + + let offset = len.into() - 1; + let mut result: felt252 = 0; + let mut i: usize = 0; + loop { + if i >= len { + break (); + } + let byte: felt252 = (*self.at(i)).into(); + result += left_shift_felt252(byte, 8 * (offset - i.into())); + + i += 1; + }; + + Option::Some(result) + } +} + +impl BytesPartialEq of PartialEq { + fn eq(lhs: @Bytes, rhs: @Bytes) -> bool { + let len_lhs = (*lhs).len(); + if len_lhs != (*rhs).len() { + return false; + } + + let mut i: usize = 0; + loop { + if i >= len_lhs { + break true; + } + + if (*lhs).at(i) != (*rhs).at(i) { + break false; + } + + i += 1; + } + } + + fn ne(lhs: @Bytes, rhs: @Bytes) -> bool { + let len_lhs = (*lhs).len(); + if len_lhs != (*rhs).len() { + return true; + } + + let mut i: usize = 0; + loop { + if i >= len_lhs { + break false; + } + + if (*lhs).at(i) != (*rhs).at(i) { + break true; + } + + i += 1; + } + } +} From de98b54e8e2cf9a67d576c71f7f65a3da8900b7c Mon Sep 17 00:00:00 2001 From: Quentash Date: Tue, 5 Sep 2023 12:14:31 +0200 Subject: [PATCH 02/24] preclean --- crates/evm/src/instructions/sha3.cairo | 117 +++------- .../tests/test_instructions/test_sha3.cairo | 219 ++++++++++++------ crates/evm/src/tests/test_utils.cairo | 2 +- 3 files changed, 186 insertions(+), 152 deletions(-) diff --git a/crates/evm/src/instructions/sha3.cairo b/crates/evm/src/instructions/sha3.cairo index 78836b2f3..08933f31b 100644 --- a/crates/evm/src/instructions/sha3.cairo +++ b/crates/evm/src/instructions/sha3.cairo @@ -28,70 +28,9 @@ impl Sha3Impl of Sha3Trait { /// Hashes n bytes in memory at a given offset in memory. /// # Specification: https://www.evm.codes/#20?fork=shanghai fn exec_sha3(ref self: ExecutionContext) -> Result<(), EVMError> { - let offset: u32 = Into::>::into((self.stack.pop()?))?; + let offset: u64 = Into::>::into((self.stack.pop()?))?; let mut size: u64 = Into::>::into((self.stack.pop()?))?; - // let mut toHash: Array = ArrayTrait::::new(); - // let mut counter = 0; - // if size == 32 { - // let mut mem = self.memory.load_internal(offset+(32*counter)); - // let toHash: Bytes = u256_to_bytes_array(mem).span(); - - // let mut hash = KeccakTrait::keccak_cairo(toHash); - // hash.low = integer::u128_byte_reverse(hash.low); - // hash.high = integer::u128_byte_reverse(hash.high); - // let tmp = hash.low; - // hash.low = hash.high; - // hash.high = tmp; - - // return self.stack.push(hash); - - // } - - // loop { - // if size < 32 { - // break; - // } - - // let mut mem = self.memory.load_internal(offset+(32*counter)); - // let memu8: Bytes = u256_to_bytes_array(mem).span(); - - // let mut i = 0; - // loop { - // if i == 32 { - // break; - // } - - // toHash.append(*memu8[i]); - // i+=1; - // }; - - // counter += 1; - // size -= 32; - // }; - // if size > 0 { - // let mut mem = self.memory.load_internal(offset+(32*counter)); - // let memu8: Bytes = u256_to_bytes_array(mem).span(); - // let mut i = size; - // loop { - // if i == 0 { - // break; - // } - - // toHash.append(*memu8[size-i]); - // i-=1; - // }; - // } - - // let mut hash = KeccakTrait::keccak_cairo(toHash.span()); - // hash.low = integer::u128_byte_reverse(hash.low); - // hash.high = integer::u128_byte_reverse(hash.high); - // let tmp = hash.low; - // hash.low = hash.high; - // hash.high = tmp; - - // self.stack.push(hash) - let mut toHash: Array = ArrayTrait::::new(); let mut last_input: u64 = 0; let mut counter = 0; @@ -99,16 +38,21 @@ impl Sha3Impl of Sha3Trait { if size < 32 { break; } - - let mut mem = self.memory.load_internal(offset + (32 * counter)); + if (offset + (32 * counter)) > self.memory.bytes_len.into() { + toHash.append(0); + toHash.append(0); + toHash.append(0); + toHash.append(0); + counter += 1; + size -= 32; + continue; + } + let mut mem = self.memory.load_internal((offset + (32 * counter)).try_into().unwrap()); mem.low = integer::u128_byte_reverse(mem.low); mem.high = integer::u128_byte_reverse(mem.high); - let tmp = mem.low; - mem.low = mem.high; - mem.high = tmp; - let (highL, lowL) = u128_split(mem.low); - let (highH, lowH) = u128_split(mem.high); + let (highL, lowL) = u128_split(mem.high); + let (highH, lowH) = u128_split(mem.low); toHash.append(lowL); toHash.append(highL); toHash.append(lowH); @@ -117,39 +61,42 @@ impl Sha3Impl of Sha3Trait { counter += 1; size -= 32; }; - if size > 0 { - let mut mem = self.memory.load_internal(offset + (32 * counter)); + let mut last_input_size: u32 = size.try_into().unwrap(); + if last_input_size > 0 { + let mut mem = 0; + if (offset + (32 * counter)) > self.memory.bytes_len.into() { + mem = 0; + } else { + mem = self.memory.load_internal((offset + (32 * counter)).try_into().unwrap()); + } + mem.low = integer::u128_byte_reverse(mem.low); mem.high = integer::u128_byte_reverse(mem.high); - let tmp = mem.low; - mem.low = mem.high; - mem.high = tmp; - let (highL, lowL) = u128_split(mem.low); - let (highH, lowH) = u128_split(mem.high); + let (highL, lowL) = u128_split(mem.high); + let (highH, lowH) = u128_split(mem.low); - if size < 8 { + if last_input_size < 8 { last_input = lowL; - } else if size < 16 { - size -= 8; + } else if last_input_size < 16 { + last_input_size -= 8; toHash.append(lowL); last_input = highL; - } else if size < 24 { - size -= 16; + } else if last_input_size < 24 { + last_input_size -= 16; toHash.append(lowL); toHash.append(highL); last_input = lowH; } else { - size -= 24; + last_input_size -= 24; toHash.append(lowL); toHash.append(highL); toHash.append(lowH); last_input = highH; } } + self.memory.ensure_length((offset.into() + size).try_into().unwrap()); - let mut hash = cairo_keccak( - ref toHash, last_input, Into::>::into((size))? - ); + let mut hash = cairo_keccak(ref toHash, last_input, last_input_size); hash.low = integer::u128_byte_reverse(hash.low); hash.high = integer::u128_byte_reverse(hash.high); let tmp = hash.low; diff --git a/crates/evm/src/tests/test_instructions/test_sha3.cairo b/crates/evm/src/tests/test_instructions/test_sha3.cairo index 4f1c67a14..cf158df53 100644 --- a/crates/evm/src/tests/test_instructions/test_sha3.cairo +++ b/crates/evm/src/tests/test_instructions/test_sha3.cairo @@ -13,241 +13,328 @@ use evm::memory::{InternalMemoryTrait, MemoryTrait}; #[test] #[available_gas(20000000)] -fn test_sha3_with_size_32() { +fn test_sha3_with_size_0_offset_0() { // Given - let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); - ctx.stack.push(0x20); + ctx.stack.push(0x00); ctx.stack.push(0x00); - ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0); + ctx.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); // When ctx.exec_sha3(); // Then - // Resultat expected : 0x1b00e6bf2213698a78d172620d2852921faf54cf9645342bdb34ca455f69d44 let result = ctx.stack.peek().unwrap(); - result.print(); assert( - result == 0x1b00e6bf2213698a78d172620d2852921faf54cf9645342bdb34ca455f69d44, 'wrong result' + result == 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470, 'wrong result' ); } #[test] #[available_gas(20000000)] -fn test_sha3_with_size_different_than_32() { +fn test_sha3_with_size_5_offset_4() { // Given let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); + ctx.stack.push(0x05); ctx.stack.push(0x04); - ctx.stack.push(0x00); - ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0); + ctx.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); // When ctx.exec_sha3(); // Then - // Resultat expected : 0x388880d56bbbf972c2ce97caeabfaf73cf1665aea364a2b06441a925d47db63e let result = ctx.stack.peek().unwrap(); assert( - result == 0x388880d56bbbf972c2ce97caeabfaf73cf1665aea364a2b06441a925d47db63e, 'wrong result' + result == 0xc41589e7559804ea4a2080dad19d876a024ccb05117835447d72ce08c1d020ec, 'wrong result' ); } #[test] #[available_gas(20000000)] -fn test_sha3_with_size_32_and_offset() { +fn test_sha3_with_size_10_offset_10() { // Given let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); - ctx.stack.push(0x20); - ctx.stack.push(0x02); + ctx.stack.push(10); + ctx.stack.push(10); - ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0); + ctx.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); // When ctx.exec_sha3(); // Then - // Resultat expected : 4c8266c7f1c12d2a0c99f03f5fb7314fcd4b762b34d58442fc0f23d2629b9dae let result = ctx.stack.peek().unwrap(); assert( - result == 0x4c8266c7f1c12d2a0c99f03f5fb7314fcd4b762b34d58442fc0f23d2629b9dae, 'wrong result' + result == 0x6bd2dd6bd408cbee33429358bf24fdc64612fbf8b1b4db604518f40ffd34b607, 'wrong result' + ); +} + +#[test] +#[available_gas(1000000000000000)] +fn test_sha3_with_size_0xFFFFF_offset_1000() { + //https://www.evm.codes/playground?unit=Wei&codeType=Mnemonic&code=%27sPutkrequired%20valugin%20memoryj32%200xFFFFFFFFffffz0wMSTOREwwsCallkopcodez4z0wSHA3%27~0000000zj1%20w%5Cns%2F%2F%20k%20thgjwPUSHge%20f~~%01fgjkswz~_ + // Given + let bytecode: Span = array![1, 2, 3, 4, 5].span(); + let mut ctx = setup_execution_context(); + + ctx.stack.push(0xFFFFF); + ctx.stack.push(1000); + + ctx.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); + + // When + ctx.exec_sha3(); + + // Then + let result = ctx.stack.peek().unwrap(); + assert( + result == 0xbe6f1b42b34644f918560a07f959d23e532dea5338e4b9f63db0caeb608018fa, 'wrong result' ); } #[test] #[available_gas(20000000)] -fn test_sha3_with_size_different_than_32_and_offset() { +fn test_sha3_with_size_1000000_offset_2() { // Given let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); - ctx.stack.push(0x02); - ctx.stack.push(0x02); + ctx.stack.push(1000000); + ctx.stack.push(2); - ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0); + ctx.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); // When ctx.exec_sha3(); // Then - // Resultat expected : 0xc142f597160775368dce2d1d8d9d847ee26f05edd4b6f00ef2451c3cab95e1a0 + // Resultat expected : 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563 let result = ctx.stack.peek().unwrap(); + result.print(); assert( - result == 0xc142f597160775368dce2d1d8d9d847ee26f05edd4b6f00ef2451c3cab95e1a0, 'wrong result' + result == 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563, 'wrong result' ); } #[test] #[available_gas(20000000)] -fn test_sha3_with_size_0() { +fn test_sha3_with_size_1_offset_960() { // Given let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); - ctx.stack.push(0x00); - ctx.stack.push(0x00); + ctx.stack.push(1); + ctx.stack.push(960); - ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0); + ctx.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); // When ctx.exec_sha3(); // Then - // Resultat expected : 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 + // Resultat expected : 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a let result = ctx.stack.peek().unwrap(); + result.print(); assert( - result == 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470, 'wrong result' + result == 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a, 'wrong result' ); } #[test] #[available_gas(20000000)] -fn test_sha3_with_size_0_and_offset() { +fn test_sha3_with_size_1_offset_992() { // Given let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); - ctx.stack.push(0x00); - ctx.stack.push(0x02); + ctx.stack.push(1); + ctx.stack.push(992); - ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0); + ctx.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); // When ctx.exec_sha3(); // Then - // Resultat expected : 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 + // Resultat expected : 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a let result = ctx.stack.peek().unwrap(); + result.print(); assert( - result == 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470, 'wrong result' + result == 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a, 'wrong result' ); } #[test] #[available_gas(20000000)] -fn test_sha3_with_offset_beyond_memory() { +fn test_sha3_with_size_1_offset_1024() { // Given let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); - ctx.stack.push(0x20); - ctx.stack.push(0x21); + ctx.stack.push(1); + ctx.stack.push(1024); - ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0); + ctx.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); // When ctx.exec_sha3(); // Then - // Resultat expected : 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563 + // Resultat expected : 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a let result = ctx.stack.peek().unwrap(); + result.print(); assert( - result == 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563, 'wrong result' + result == 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a, 'wrong result' ); } #[test] #[available_gas(20000000)] -fn test_sha3_with_offset_plus_size_beyond_memory() { +fn test_sha3_with_size_1_offset_1984() { // Given let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); - ctx.stack.push(0x20); - ctx.stack.push(0x02); + ctx.stack.push(1); + ctx.stack.push(1984); - ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0); + ctx.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); // When ctx.exec_sha3(); // Then - // Resultat expected : 0x4c8266c7f1c12d2a0c99f03f5fb7314fcd4b762b34d58442fc0f23d2629b9dae + // Resultat expected : 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a let result = ctx.stack.peek().unwrap(); + result.print(); assert( - result == 0x4c8266c7f1c12d2a0c99f03f5fb7314fcd4b762b34d58442fc0f23d2629b9dae, 'wrong result' + result == 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a, 'wrong result' ); } #[test] #[available_gas(20000000)] -fn test_sha3_with_size_128() { +fn test_sha3_with_size_1_offset_2016() { // Given let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); - ctx.stack.push(0x80); - ctx.stack.push(0x00); + ctx.stack.push(1); + ctx.stack.push(2016); - ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0); - ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0x20); - ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0x40); - ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0x60); + ctx.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); // When ctx.exec_sha3(); // Then - // Resultat expected : 0x425c8158d6661daea881641c267cb0bf9e551e12af9c1fa7737ef3b219ab0eed + // Resultat expected : 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a let result = ctx.stack.peek().unwrap(); + result.print(); assert( - result == 0x425c8158d6661daea881641c267cb0bf9e551e12af9c1fa7737ef3b219ab0eed, 'wrong result' + result == 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a, 'wrong result' ); } #[test] #[available_gas(20000000)] -fn test_sha3_with_size_133() { +fn test_sha3_with_size_1_offset_2048() { // Given let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); - ctx.stack.push(0x85); - ctx.stack.push(0x00); + ctx.stack.push(1); + ctx.stack.push(2048); + + ctx.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); + + // When + ctx.exec_sha3(); + + // Then + // Resultat expected : 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a + let result = ctx.stack.peek().unwrap(); + result.print(); + assert( + result == 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a, 'wrong result' + ); +} + +#[test] +#[available_gas(20000000)] +fn test_sha3_with_size_0_offset_1024() { + // Given + let bytecode: Span = array![1, 2, 3, 4, 5].span(); + let mut ctx = setup_execution_context(); + + ctx.stack.push(0); + ctx.stack.push(1024); + + ctx.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); + + // When + ctx.exec_sha3(); + + // Then + let result = ctx.stack.peek().unwrap(); + result.print(); + assert( + result == 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470, 'wrong result' + ); +} + +#[test] +#[available_gas(20000000)] +fn test_sha3_with_size_32_offset_2016() { + // Given + let bytecode: Span = array![1, 2, 3, 4, 5].span(); + let mut ctx = setup_execution_context(); + + ctx.stack.push(32); + ctx.stack.push(2016); + + ctx.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); + + // When + ctx.exec_sha3(); + + // Then + let result = ctx.stack.peek().unwrap(); + result.print(); + assert( + result == 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563, 'wrong result' + ); +} + +#[test] +#[available_gas(20000000)] +fn test_sha3_with_offset_plus_size_beyond_memory() { + // Given + let bytecode: Span = array![1, 2, 3, 4, 5].span(); + let mut ctx = setup_execution_context(); + + ctx.stack.push(0x20); + ctx.stack.push(0x02); ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0); - ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0x20); - ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0x40); - ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0x60); - ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0x80); // When ctx.exec_sha3(); // Then - // Resultat expected : 0x73cfd6eb245c512d8247245573437a38db4e265a6945f119df30cc9ed2bac584 + // Resultat expected : 0x4c8266c7f1c12d2a0c99f03f5fb7314fcd4b762b34d58442fc0f23d2629b9dae let result = ctx.stack.peek().unwrap(); assert( - result == 0x73cfd6eb245c512d8247245573437a38db4e265a6945f119df30cc9ed2bac584, 'wrong result' + result == 0x4c8266c7f1c12d2a0c99f03f5fb7314fcd4b762b34d58442fc0f23d2629b9dae, 'wrong result' ); } diff --git a/crates/evm/src/tests/test_utils.cairo b/crates/evm/src/tests/test_utils.cairo index a11cbe782..e603714f4 100644 --- a/crates/evm/src/tests/test_utils.cairo +++ b/crates/evm/src/tests/test_utils.cairo @@ -32,7 +32,7 @@ fn setup_execution_context() -> ExecutionContext { let call_context = setup_call_context(); let starknet_address: ContractAddress = starknet_address(); let evm_address: EthAddress = evm_address(); - let gas_limit: u64 = 1000; + let gas_limit: u64 = 100000; let gas_price: u64 = 10; let read_only: bool = false; let returned_data = Default::default(); From ad153f7be3272794050db4f9a4419c8f273760b9 Mon Sep 17 00:00:00 2001 From: Quentash Date: Tue, 5 Sep 2023 15:02:05 +0200 Subject: [PATCH 03/24] cleaned code and tests --- crates/evm/src/helpers.cairo | 21 --- crates/evm/src/instructions/sha3.cairo | 73 +++++---- .../tests/test_instructions/test_sha3.cairo | 117 +++++++++------ crates/utils/src/bitwise.cairo | 78 ---------- crates/utils/src/hashing.cairo | 3 - crates/utils/src/hashing/README.md | 4 - crates/utils/src/hashing/hasher.cairo | 6 - crates/utils/src/hashing/keccak.cairo | 140 ------------------ crates/utils/src/hashing/poseidon.cairo | 21 --- crates/utils/src/hashing/tests.cairo | 2 - .../utils/src/hashing/tests/test_keccak.cairo | 128 ---------------- .../src/hashing/tests/test_poseidon.cairo | 12 -- crates/utils/src/lib.cairo | 3 - crates/utils/src/types.cairo | 2 - crates/utils/src/types/byte.cairo | 17 --- crates/utils/src/types/bytes.cairo | 104 ------------- 16 files changed, 103 insertions(+), 628 deletions(-) delete mode 100644 crates/utils/src/bitwise.cairo delete mode 100644 crates/utils/src/hashing.cairo delete mode 100644 crates/utils/src/hashing/README.md delete mode 100644 crates/utils/src/hashing/hasher.cairo delete mode 100644 crates/utils/src/hashing/keccak.cairo delete mode 100644 crates/utils/src/hashing/poseidon.cairo delete mode 100644 crates/utils/src/hashing/tests.cairo delete mode 100644 crates/utils/src/hashing/tests/test_keccak.cairo delete mode 100644 crates/utils/src/hashing/tests/test_poseidon.cairo delete mode 100644 crates/utils/src/types.cairo delete mode 100644 crates/utils/src/types/byte.cairo delete mode 100644 crates/utils/src/types/bytes.cairo diff --git a/crates/evm/src/helpers.cairo b/crates/evm/src/helpers.cairo index 0e7bfc9f1..1e5642410 100644 --- a/crates/evm/src/helpers.cairo +++ b/crates/evm/src/helpers.cairo @@ -12,24 +12,3 @@ impl U256IntoResultU32 of Into> { } } } - -// Try converting u256 to u64 -impl U256IntoResultU64 of Into> { - fn into(self: u256) -> Result { - match self.try_into() { - Option::Some(value) => Result::Ok(value), - Option::None(_) => Result::Err(EVMError::TypeConversionError(TYPE_CONVERSION_ERROR)) - } - } -} - -// Try converting u64 to u32 -impl U64IntoResultU32 of Into> { - fn into(self: u64) -> Result { - match self.try_into() { - Option::Some(value) => Result::Ok(value), - Option::None(_) => Result::Err(EVMError::TypeConversionError(TYPE_CONVERSION_ERROR)) - } - } -} - diff --git a/crates/evm/src/instructions/sha3.cairo b/crates/evm/src/instructions/sha3.cairo index 08933f31b..2f8aec2e4 100644 --- a/crates/evm/src/instructions/sha3.cairo +++ b/crates/evm/src/instructions/sha3.cairo @@ -3,33 +3,23 @@ // Internal imports use evm::context::{ExecutionContext, ExecutionContextTrait, BoxDynamicExecutionContextDestruct}; use evm::stack::StackTrait; +use evm::memory::{InternalMemoryTrait, MemoryTrait}; use evm::errors::EVMError; use evm::helpers::U256IntoResultU32; -use evm::helpers::U256IntoResultU64; -use evm::helpers::U64IntoResultU32; -//use keccak::keccak_u256s_be_inputs; -use keccak::keccak_u256s_le_inputs; use keccak::cairo_keccak; -use evm::memory::{InternalMemoryTrait, MemoryTrait}; -use utils::helpers::u256_to_bytes_array; -use utils::helpers::u128_split; - -use utils::hashing::keccak::KeccakTrait; -use utils::types::bytes::Bytes; +use utils::helpers::{u256_to_bytes_array, u128_split}; -use starknet::SyscallResultTrait; use array::ArrayTrait; -use debug::PrintTrait; - #[generate_trait] impl Sha3Impl of Sha3Trait { /// SHA3 operation. /// Hashes n bytes in memory at a given offset in memory. /// # Specification: https://www.evm.codes/#20?fork=shanghai fn exec_sha3(ref self: ExecutionContext) -> Result<(), EVMError> { - let offset: u64 = Into::>::into((self.stack.pop()?))?; - let mut size: u64 = Into::>::into((self.stack.pop()?))?; + let offset: u32 = Into::>::into((self.stack.pop()?))?; + let mut size: u32 = Into::>::into((self.stack.pop()?))?; + let init_size = size; let mut toHash: Array = ArrayTrait::::new(); let mut last_input: u64 = 0; @@ -38,7 +28,9 @@ impl Sha3Impl of Sha3Trait { if size < 32 { break; } - if (offset + (32 * counter)) > self.memory.bytes_len.into() { + // If we try to read unallocted memory slot, we'll feed the data to hash with 0s + // and allocated the memory space the end of the process, which is cheaper than allocating every load(). + if (offset + (32 * counter)) > self.memory.bytes_len { toHash.append(0); toHash.append(0); toHash.append(0); @@ -47,12 +39,14 @@ impl Sha3Impl of Sha3Trait { size -= 32; continue; } - let mut mem = self.memory.load_internal((offset + (32 * counter)).try_into().unwrap()); - mem.low = integer::u128_byte_reverse(mem.low); - mem.high = integer::u128_byte_reverse(mem.high); + // Load the 32 words and reverse the bytes order, + let mut loaded = self.memory.load_internal((offset + (32 * counter))); + loaded.low = integer::u128_byte_reverse(loaded.low); + loaded.high = integer::u128_byte_reverse(loaded.high); - let (highL, lowL) = u128_split(mem.high); - let (highH, lowH) = u128_split(mem.low); + // Split the loaded word into u64 to feed cairo_keccak + let (highL, lowL) = u128_split(loaded.high); + let (highH, lowH) = u128_split(loaded.low); toHash.append(lowL); toHash.append(highL); toHash.append(lowH); @@ -61,42 +55,43 @@ impl Sha3Impl of Sha3Trait { counter += 1; size -= 32; }; - let mut last_input_size: u32 = size.try_into().unwrap(); - if last_input_size > 0 { - let mut mem = 0; - if (offset + (32 * counter)) > self.memory.bytes_len.into() { - mem = 0; + + if size > 0 { + let mut loaded = 0; + if (offset + (32 * counter)) > self.memory.bytes_len { + loaded = 0; } else { - mem = self.memory.load_internal((offset + (32 * counter)).try_into().unwrap()); + loaded = self.memory.load_internal((offset + (32 * counter))); } - mem.low = integer::u128_byte_reverse(mem.low); - mem.high = integer::u128_byte_reverse(mem.high); - let (highL, lowL) = u128_split(mem.high); - let (highH, lowH) = u128_split(mem.low); + loaded.low = integer::u128_byte_reverse(loaded.low); + loaded.high = integer::u128_byte_reverse(loaded.high); + let (highL, lowL) = u128_split(loaded.high); + let (highH, lowH) = u128_split(loaded.low); - if last_input_size < 8 { + if size < 8 { last_input = lowL; - } else if last_input_size < 16 { - last_input_size -= 8; + } else if size < 16 { + size -= 8; toHash.append(lowL); last_input = highL; - } else if last_input_size < 24 { - last_input_size -= 16; + } else if size < 24 { + size -= 16; toHash.append(lowL); toHash.append(highL); last_input = lowH; } else { - last_input_size -= 24; + size -= 24; toHash.append(lowL); toHash.append(highL); toHash.append(lowH); last_input = highH; } } - self.memory.ensure_length((offset.into() + size).try_into().unwrap()); - let mut hash = cairo_keccak(ref toHash, last_input, last_input_size); + self.memory.ensure_length(offset + init_size); + + let mut hash = cairo_keccak(ref toHash, last_input, size); hash.low = integer::u128_byte_reverse(hash.low); hash.high = integer::u128_byte_reverse(hash.high); let tmp = hash.low; diff --git a/crates/evm/src/tests/test_instructions/test_sha3.cairo b/crates/evm/src/tests/test_instructions/test_sha3.cairo index cf158df53..99e5bbd8c 100644 --- a/crates/evm/src/tests/test_instructions/test_sha3.cairo +++ b/crates/evm/src/tests/test_instructions/test_sha3.cairo @@ -1,19 +1,16 @@ use evm::instructions::Sha3Trait; use evm::tests::test_utils::setup_execution_context; +use evm::context::{ExecutionContext, ExecutionContextTrait, BoxDynamicExecutionContextDestruct}; +use evm::memory::{InternalMemoryTrait, MemoryTrait}; use evm::stack::StackTrait; use option::OptionTrait; -use debug::PrintTrait; -use integer::BoundedInt; - -use evm::context::{ - ExecutionContext, ExecutionContextTrait, CallContextTrait, BoxDynamicExecutionContextDestruct -}; -use evm::memory::{InternalMemoryTrait, MemoryTrait}; +use evm::errors::{EVMError, TYPE_CONVERSION_ERROR}; +use debug::PrintTrait; #[test] #[available_gas(20000000)] -fn test_sha3_with_size_0_offset_0() { +fn test_sha3_size_0_offset_0() { // Given let mut ctx = setup_execution_context(); @@ -36,7 +33,7 @@ fn test_sha3_with_size_0_offset_0() { #[test] #[available_gas(20000000)] -fn test_sha3_with_size_5_offset_4() { +fn test_sha3_size_5_offset_4() { // Given let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); @@ -58,7 +55,7 @@ fn test_sha3_with_size_5_offset_4() { #[test] #[available_gas(20000000)] -fn test_sha3_with_size_10_offset_10() { +fn test_sha3_size_10_offset_10() { // Given let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); @@ -80,8 +77,7 @@ fn test_sha3_with_size_10_offset_10() { #[test] #[available_gas(1000000000000000)] -fn test_sha3_with_size_0xFFFFF_offset_1000() { - //https://www.evm.codes/playground?unit=Wei&codeType=Mnemonic&code=%27sPutkrequired%20valugin%20memoryj32%200xFFFFFFFFffffz0wMSTOREwwsCallkopcodez4z0wSHA3%27~0000000zj1%20w%5Cns%2F%2F%20k%20thgjwPUSHge%20f~~%01fgjkswz~_ +fn test_sha3_size_0xFFFFF_offset_1000() { // Given let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); @@ -102,8 +98,8 @@ fn test_sha3_with_size_0xFFFFF_offset_1000() { } #[test] -#[available_gas(20000000)] -fn test_sha3_with_size_1000000_offset_2() { +#[available_gas(8000000000)] +fn test_sha3_size_1000000_offset_2() { // Given let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); @@ -115,19 +111,16 @@ fn test_sha3_with_size_1000000_offset_2() { // When ctx.exec_sha3(); - // Then - // Resultat expected : 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563 let result = ctx.stack.peek().unwrap(); - result.print(); assert( - result == 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563, 'wrong result' + result == 0x4aa461ae9513f3b03ae397740ade979809dd02ae2c14e101b32842fbee21f0a, 'wrong result' ); } #[test] #[available_gas(20000000)] -fn test_sha3_with_size_1_offset_960() { +fn test_sha3_size_1_offset_960() { // Given let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); @@ -141,9 +134,7 @@ fn test_sha3_with_size_1_offset_960() { ctx.exec_sha3(); // Then - // Resultat expected : 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a let result = ctx.stack.peek().unwrap(); - result.print(); assert( result == 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a, 'wrong result' ); @@ -151,7 +142,7 @@ fn test_sha3_with_size_1_offset_960() { #[test] #[available_gas(20000000)] -fn test_sha3_with_size_1_offset_992() { +fn test_sha3_size_1_offset_992() { // Given let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); @@ -165,9 +156,7 @@ fn test_sha3_with_size_1_offset_992() { ctx.exec_sha3(); // Then - // Resultat expected : 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a let result = ctx.stack.peek().unwrap(); - result.print(); assert( result == 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a, 'wrong result' ); @@ -175,7 +164,7 @@ fn test_sha3_with_size_1_offset_992() { #[test] #[available_gas(20000000)] -fn test_sha3_with_size_1_offset_1024() { +fn test_sha3_size_1_offset_1024() { // Given let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); @@ -189,9 +178,7 @@ fn test_sha3_with_size_1_offset_1024() { ctx.exec_sha3(); // Then - // Resultat expected : 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a let result = ctx.stack.peek().unwrap(); - result.print(); assert( result == 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a, 'wrong result' ); @@ -199,7 +186,7 @@ fn test_sha3_with_size_1_offset_1024() { #[test] #[available_gas(20000000)] -fn test_sha3_with_size_1_offset_1984() { +fn test_sha3_size_1_offset_1984() { // Given let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); @@ -213,9 +200,7 @@ fn test_sha3_with_size_1_offset_1984() { ctx.exec_sha3(); // Then - // Resultat expected : 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a let result = ctx.stack.peek().unwrap(); - result.print(); assert( result == 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a, 'wrong result' ); @@ -223,7 +208,7 @@ fn test_sha3_with_size_1_offset_1984() { #[test] #[available_gas(20000000)] -fn test_sha3_with_size_1_offset_2016() { +fn test_sha3_size_1_offset_2016() { // Given let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); @@ -237,9 +222,7 @@ fn test_sha3_with_size_1_offset_2016() { ctx.exec_sha3(); // Then - // Resultat expected : 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a let result = ctx.stack.peek().unwrap(); - result.print(); assert( result == 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a, 'wrong result' ); @@ -247,7 +230,7 @@ fn test_sha3_with_size_1_offset_2016() { #[test] #[available_gas(20000000)] -fn test_sha3_with_size_1_offset_2048() { +fn test_sha3_size_1_offset_2048() { // Given let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); @@ -261,9 +244,7 @@ fn test_sha3_with_size_1_offset_2048() { ctx.exec_sha3(); // Then - // Resultat expected : 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a let result = ctx.stack.peek().unwrap(); - result.print(); assert( result == 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a, 'wrong result' ); @@ -271,7 +252,7 @@ fn test_sha3_with_size_1_offset_2048() { #[test] #[available_gas(20000000)] -fn test_sha3_with_size_0_offset_1024() { +fn test_sha3_size_0_offset_1024() { // Given let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); @@ -286,7 +267,6 @@ fn test_sha3_with_size_0_offset_1024() { // Then let result = ctx.stack.peek().unwrap(); - result.print(); assert( result == 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470, 'wrong result' ); @@ -294,7 +274,7 @@ fn test_sha3_with_size_0_offset_1024() { #[test] #[available_gas(20000000)] -fn test_sha3_with_size_32_offset_2016() { +fn test_sha3_size_32_offset_2016() { // Given let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); @@ -309,7 +289,6 @@ fn test_sha3_with_size_32_offset_2016() { // Then let result = ctx.stack.peek().unwrap(); - result.print(); assert( result == 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563, 'wrong result' ); @@ -317,30 +296,73 @@ fn test_sha3_with_size_32_offset_2016() { #[test] #[available_gas(20000000)] -fn test_sha3_with_offset_plus_size_beyond_memory() { +fn test_sha3_size_32_offset_0() { + // Given + let bytecode: Span = array![1, 2, 3, 4, 5].span(); + let mut ctx = setup_execution_context(); + + ctx.stack.push(32); + ctx.stack.push(0); + + ctx.memory.store(0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000, 0); + + // When + ctx.exec_sha3(); + + // Then + let result = ctx.stack.peek().unwrap(); + assert( + result == 0x567d6b045256961aee949d6bb4d5f814c5b42e6b8bb49a833e8e89fbcddee86c, 'wrong result' + ); +} + +#[test] +#[available_gas(20000000)] +fn test_sha3_size_31_offset_0() { // Given let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); - ctx.stack.push(0x20); - ctx.stack.push(0x02); + ctx.stack.push(31); + ctx.stack.push(0); + + ctx.memory.store(0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000, 0); + + // When + ctx.exec_sha3(); + + // Then + let result = ctx.stack.peek().unwrap(); + assert( + result == 0x4b13f212816c02cc818ba4802e81a4ac1904d2c920fe8d8cf3e4f05233a57d2e, 'wrong result' + ); +} + +#[test] +#[available_gas(20000000)] +fn test_sha3_size_33_offset_0() { + // Given + let bytecode: Span = array![1, 2, 3, 4, 5].span(); + let mut ctx = setup_execution_context(); + + ctx.stack.push(33); + ctx.stack.push(0); - ctx.memory.store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, 0); + ctx.memory.store(0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000, 0); // When ctx.exec_sha3(); // Then - // Resultat expected : 0x4c8266c7f1c12d2a0c99f03f5fb7314fcd4b762b34d58442fc0f23d2629b9dae let result = ctx.stack.peek().unwrap(); assert( - result == 0x4c8266c7f1c12d2a0c99f03f5fb7314fcd4b762b34d58442fc0f23d2629b9dae, 'wrong result' + result == 0xa6fa3edfabbe64b6ce26120b21ac9b8191005115d5e7e03fa58ec9cc74c0f2f4, 'wrong result' ); } #[test] #[available_gas(20000000000)] -fn test_sha3_with_big_size() { +fn test_sha3_size_0x0C80_offset_0() { // Given let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); @@ -363,7 +385,6 @@ fn test_sha3_with_big_size() { ctx.exec_sha3(); // Then - // Resultat expected : 0x2022ae07f3a362b08ac0a4bcb785c830cb5c368dc0ce6972249c6abbc68a5291 let result = ctx.stack.peek().unwrap(); assert( result == 0x2022ae07f3a362b08ac0a4bcb785c830cb5c368dc0ce6972249c6abbc68a5291, 'wrong result' diff --git a/crates/utils/src/bitwise.cairo b/crates/utils/src/bitwise.cairo deleted file mode 100644 index 81c6ae311..000000000 --- a/crates/utils/src/bitwise.cairo +++ /dev/null @@ -1,78 +0,0 @@ -use utils::math::{pow, pow_felt252}; -use math::Oneable; -use zeroable::Zeroable; - -// @notice Bitwise left shift -// @param num The number to be shifted -// @param shift The number of bits to shift -// @return The left shifted number -fn left_shift< - T, - impl TZeroable: Zeroable, - impl TAdd: Add, - impl TSub: Sub, - impl TMul: Mul, - impl TOneable: Oneable, - impl TCopy: Copy, - impl TDrop: Drop -// TODO refactor shift type from T to usize ->( - num: T, shift: T -) -> T { - let two = TOneable::one() + TOneable::one(); - num * pow(two, shift) -} - -fn left_shift_felt252(num: felt252, shift: felt252) -> felt252 { - num * pow_felt252(2, shift) -} - -// @notice Bitwise right shift -// @param num The number to be shifted -// @param shift The number of bits to shift -// @return The right shifted number -fn right_shift< - T, - impl TZeroable: Zeroable, - impl TAdd: Add, - impl TSub: Sub, - impl TMul: Mul, - impl TDiv: Div, - impl TOneable: Oneable, - impl TCopy: Copy, - impl TDrop: Drop -// TODO refactor shift type from T to usize ->( - num: T, shift: T -) -> T { - let two = TOneable::one() + TOneable::one(); - num / pow(two, shift) -} - -// @notice Bit length of a number -// @param num The number to be measured -// @return The number of bits in the number -fn bit_length< - T, - impl TZeroable: Zeroable, - impl TPartialOrd: PartialOrd, - impl TAddImpl: Add, - impl TSub: Sub, - impl TMul: Mul, - impl TOneable: Oneable, - impl TCopy: Copy, - impl TDrop: Drop ->( - num: T -) -> T { - let mut bit_position = TZeroable::zero(); - let mut cur_n = TOneable::one(); - loop { - if cur_n > num { - break (); - }; - bit_position = bit_position + TOneable::one(); - cur_n = left_shift(cur_n, TOneable::one()); - }; - bit_position -} diff --git a/crates/utils/src/hashing.cairo b/crates/utils/src/hashing.cairo deleted file mode 100644 index 01dc92816..000000000 --- a/crates/utils/src/hashing.cairo +++ /dev/null @@ -1,3 +0,0 @@ -mod hasher; -mod keccak; -mod poseidon; diff --git a/crates/utils/src/hashing/README.md b/crates/utils/src/hashing/README.md deleted file mode 100644 index 12c6c1649..000000000 --- a/crates/utils/src/hashing/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# Hashing - -We provide a `Hasher` trait to ensure consistent and straightforward usage of generic hashers across your code. -In addition to the generic hasher trait, `cairo-lib` includes Ethereum-compatible keccak256, ensuring that your applications maintain compatibility with Ethereum-based and EVM based systems. diff --git a/crates/utils/src/hashing/hasher.cairo b/crates/utils/src/hashing/hasher.cairo deleted file mode 100644 index 5c5f078bc..000000000 --- a/crates/utils/src/hashing/hasher.cairo +++ /dev/null @@ -1,6 +0,0 @@ -// @notice Hasher trait, a common interface for all hashers -trait Hasher { - fn hash_single(a: T) -> V; - fn hash_double(a: T, b: T) -> V; - fn hash_many(input: Span) -> V; -} diff --git a/crates/utils/src/hashing/keccak.cairo b/crates/utils/src/hashing/keccak.cairo deleted file mode 100644 index e64165a4d..000000000 --- a/crates/utils/src/hashing/keccak.cairo +++ /dev/null @@ -1,140 +0,0 @@ -use utils::hashing::hasher::Hasher; -use utils::math::pow; -use utils::types::bytes::Bytes; -use array::{ArrayTrait, SpanTrait}; -use keccak::{keccak_u256s_le_inputs, cairo_keccak}; -use traits::{Into, TryInto}; -use option::OptionTrait; -use starknet::SyscallResultTrait; - -#[derive(Drop)] -struct Keccak {} - -#[generate_trait] -impl KeccakHasher of KeccakTrait { - // @notice keccak256 hashes the input, matching Solidity keccak - // @param input The input to hash, in big endian - // @return The hash of the input, in little endian - fn keccak_cairo(bytes: Bytes) -> u256 { - let n = bytes.len(); - let q = n / 8; - let r = n % 8; - - let mut keccak_input = ArrayTrait::new(); - let mut i: usize = 0; - loop { - if i >= q { - break (); - } - - let val = (*bytes.at(8 * i)).into() - + (*bytes.at(8 * i + 1)).into() * 256 - + (*bytes.at(8 * i + 2)).into() * 65536 - + (*bytes.at(8 * i + 3)).into() * 16777216 - + (*bytes.at(8 * i + 4)).into() * 4294967296 - + (*bytes.at(8 * i + 5)).into() * 1099511627776 - + (*bytes.at(8 * i + 6)).into() * 281474976710656 - + (*bytes.at(8 * i + 7)).into() * 72057594037927936; - - keccak_input.append(val); - - i += 1; - }; - - let mut last_word: u64 = 0; - let mut k: usize = 0; - loop { - if k >= r { - break (); - } - - let current: u64 = (*bytes.at(8 * q + k)).into(); - last_word += current * pow(256, k.into()); - - k += 1; - }; - - cairo_keccak(ref keccak_input, last_word, r) - } -} - -impl KeccakHasherU256 of Hasher { - fn hash_single(a: u256) -> u256 { - let mut arr = array![a]; - keccak_u256s_le_inputs(arr.span()) - } - - fn hash_double(a: u256, b: u256) -> u256 { - let mut arr = array![a, b]; - keccak_u256s_le_inputs(arr.span()) - } - - fn hash_many(input: Span) -> u256 { - keccak_u256s_le_inputs(input) - } -} - -impl KeccakHasherSpanU8 of Hasher, u256> { - fn hash_single(a: Span) -> u256 { - let mut arr = ArrayTrait::new(); - let mut i: usize = 0; - loop { - if i >= a.len() { - break arr.span(); - } - let current = *a.at(i); - arr.append(current.into()); - i += 1; - }; - keccak_u256s_le_inputs(arr.span()) - } - - fn hash_double(a: Span, b: Span) -> u256 { - let mut arr = ArrayTrait::new(); - let mut i: usize = 0; - loop { - if i >= a.len() { - break arr.span(); - } - let current = *a.at(i); - arr.append(current.into()); - i += 1; - }; - - i = 0; - loop { - if i >= b.len() { - break arr.span(); - } - let current = *b.at(i); - arr.append(current.into()); - i += 1; - }; - keccak_u256s_le_inputs(arr.span()) - } - - fn hash_many(input: Span>) -> u256 { - let mut arr = ArrayTrait::new(); - let mut i: usize = 0; - let mut j: usize = 0; - loop { - if i >= input.len() { - break arr.span(); - } - - let current = *input.at(i); - loop { - if j >= current.len() { - break; - } - let current = *current.at(j); - arr.append(current.into()); - j += 1; - }; - i += 1; - }; - - keccak_u256s_le_inputs(arr.span()) - } -} - diff --git a/crates/utils/src/hashing/poseidon.cairo b/crates/utils/src/hashing/poseidon.cairo deleted file mode 100644 index a72327077..000000000 --- a/crates/utils/src/hashing/poseidon.cairo +++ /dev/null @@ -1,21 +0,0 @@ -use utils::hashing::hasher::Hasher; -use poseidon::{poseidon_hash_span, hades_permutation}; - -struct Poseidon {} - -// Permutation params: https://docs.starknet.io/documentation/architecture_and_concepts/Cryptography/hash-functions/#poseidon_hash -impl PoseidonHasher of Hasher { - fn hash_single(a: felt252) -> felt252 { - let (single, _, _) = hades_permutation(a, 0, 1); - single - } - - fn hash_double(a: felt252, b: felt252) -> felt252 { - let (double, _, _) = hades_permutation(a, b, 2); - double - } - - fn hash_many(input: Span) -> felt252 { - poseidon_hash_span(input) - } -} diff --git a/crates/utils/src/hashing/tests.cairo b/crates/utils/src/hashing/tests.cairo deleted file mode 100644 index ec4d3cf85..000000000 --- a/crates/utils/src/hashing/tests.cairo +++ /dev/null @@ -1,2 +0,0 @@ -mod test_keccak; -mod test_poseidon; diff --git a/crates/utils/src/hashing/tests/test_keccak.cairo b/crates/utils/src/hashing/tests/test_keccak.cairo deleted file mode 100644 index b6172fb59..000000000 --- a/crates/utils/src/hashing/tests/test_keccak.cairo +++ /dev/null @@ -1,128 +0,0 @@ -use cairo_lib::hashing::keccak::KeccakTrait; -use array::ArrayTrait; -use debug::PrintTrait; - -#[test] -#[available_gas(99999999)] -fn test_keccak_cairo_full_byte() { - let mut input = array![0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]; - - let res = KeccakTrait::keccak_cairo(input.span()); - assert( - res == 0xAF7D4E460ACF8E540E682A9EE91EA1C08C1615C3889D75EB0A70660A4BFB0BAD, - 'Keccak output not matching' - ); -} - -#[test] -#[available_gas(99999999)] -fn test_keccak_cairo_empty_bytes() { - let mut input = array![]; - - let mut res = KeccakTrait::keccak_cairo(input.span()); - res.low = integer::u128_byte_reverse(res.low); - res.high = integer::u128_byte_reverse(res.high); - let tmp = res.low; - res.low = res.high; - res.high = tmp; - assert( - res == 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470, - 'Keccak output not matching' - ); -} - -#[test] -#[available_gas(99999999)] -fn test_keccak_cairo_32_bytes() { - let mut input = array![]; - input.append(0xFA); - input.append(0xFA); - input.append(0xFA); - input.append(0xFA); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); -} - -#[test] -#[available_gas(99999999999)] -fn test_keccak_cairo_lot_of_bytes() { - let mut input = array![]; - - let mut memDst: u32 = 0; - loop { - if memDst >= 0x0C80 { - break; - } - input.append(0xFA); - input.append(0xFA); - input.append(0xFA); - input.append(0xFA); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - input.append(0x00); - memDst += 0x20; - }; -} - -#[test] -#[available_gas(99999999)] -fn test_keccak_cairo_remainder() { - let mut input = array![0xab, 0x76, 0x8c, 0xf7, 0x89, 0xae, 0xfd, 0x23, 0x4a, 0xbc, 0xd2, 0x45]; - - let res = KeccakTrait::keccak_cairo(input.span()); - assert( - res == 0x82CBD5B00CD06A188C831D69CB9629C92A2D5E7A78CEA913C5F9AFF62E66BBB9, - 'Keccak output not matching' - ); -} diff --git a/crates/utils/src/hashing/tests/test_poseidon.cairo b/crates/utils/src/hashing/tests/test_poseidon.cairo deleted file mode 100644 index 785204a34..000000000 --- a/crates/utils/src/hashing/tests/test_poseidon.cairo +++ /dev/null @@ -1,12 +0,0 @@ -use cairo_lib::hashing::poseidon::PoseidonHasher; - -#[test] -#[available_gas(99999999)] -fn test_poseidon_hash_double() { - let a = 0x6109f1949f6a7555eccf4e15ce1f10fbd78091dfe715cc2e0c5a244d9d17761; - let b = 0x0194791558611599fe4ae0fcfa48f095659c90db18e54de86f2d2f547f7369bf; - let hash = PoseidonHasher::hash_double(a, b); - - let res = 0x7b8180db85fa1e0b5041f38f57926743905702c498576991f04998b5d9476b4; - assert(hash == res, 'Hash does not match'); -} diff --git a/crates/utils/src/lib.cairo b/crates/utils/src/lib.cairo index 303107177..eaeb53ecd 100644 --- a/crates/utils/src/lib.cairo +++ b/crates/utils/src/lib.cairo @@ -7,9 +7,6 @@ mod math; mod eth_transaction; mod rlp; mod traits; -mod types; -mod bitwise; -mod hashing; #[cfg(test)] diff --git a/crates/utils/src/types.cairo b/crates/utils/src/types.cairo deleted file mode 100644 index a42c43be0..000000000 --- a/crates/utils/src/types.cairo +++ /dev/null @@ -1,2 +0,0 @@ -mod byte; -mod bytes; diff --git a/crates/utils/src/types/byte.cairo b/crates/utils/src/types/byte.cairo deleted file mode 100644 index c15b9d8fc..000000000 --- a/crates/utils/src/types/byte.cairo +++ /dev/null @@ -1,17 +0,0 @@ -use utils::bitwise::right_shift; - -type Byte = u8; - -#[generate_trait] -impl ByteImpl of ByteTrait { - // @notice Extracts the high and low nibbles from a byte - // @return (high, low) - fn extract_nibbles(self: Byte) -> (Byte, Byte) { - let masked = self & 0xf0; - let high = right_shift(masked, 4); - let low = self & 0x0f; - - (high, low) - } -} - diff --git a/crates/utils/src/types/bytes.cairo b/crates/utils/src/types/bytes.cairo deleted file mode 100644 index 1e7b5b455..000000000 --- a/crates/utils/src/types/bytes.cairo +++ /dev/null @@ -1,104 +0,0 @@ -use utils::bitwise::{left_shift, left_shift_felt252}; -use utils::types::byte::Byte; -use array::SpanTrait; -use traits::{TryInto, Into}; - -type Bytes = Span; - -impl BytesTryIntoU256 of TryInto { - fn try_into(self: Bytes) -> Option { - let len = self.len(); - if len > 32 { - return Option::None(()); - } - - if self.is_empty() { - return Option::Some(0); - } - - let offset = len.into() - 1; - let mut result: u256 = 0; - let mut i: usize = 0; - loop { - if i >= len { - break (); - } - let byte: u256 = (*self.at(i)).into(); - result += left_shift(byte, 8 * (offset - i.into())); - - i += 1; - }; - - Option::Some(result) - } -} - -impl BytesTryIntoFelt252 of TryInto { - fn try_into(self: Bytes) -> Option { - let len = self.len(); - if len > 31 { - return Option::None(()); - } - - if self.is_empty() { - return Option::Some(0); - } - - let offset = len.into() - 1; - let mut result: felt252 = 0; - let mut i: usize = 0; - loop { - if i >= len { - break (); - } - let byte: felt252 = (*self.at(i)).into(); - result += left_shift_felt252(byte, 8 * (offset - i.into())); - - i += 1; - }; - - Option::Some(result) - } -} - -impl BytesPartialEq of PartialEq { - fn eq(lhs: @Bytes, rhs: @Bytes) -> bool { - let len_lhs = (*lhs).len(); - if len_lhs != (*rhs).len() { - return false; - } - - let mut i: usize = 0; - loop { - if i >= len_lhs { - break true; - } - - if (*lhs).at(i) != (*rhs).at(i) { - break false; - } - - i += 1; - } - } - - fn ne(lhs: @Bytes, rhs: @Bytes) -> bool { - let len_lhs = (*lhs).len(); - if len_lhs != (*rhs).len() { - return true; - } - - let mut i: usize = 0; - loop { - if i >= len_lhs { - break false; - } - - if (*lhs).at(i) != (*rhs).at(i) { - break true; - } - - i += 1; - } - } -} From 098b586915bf6d45eb1e91afb2ad7bc6a0262463 Mon Sep 17 00:00:00 2001 From: Quentash Date: Tue, 5 Sep 2023 15:12:29 +0200 Subject: [PATCH 04/24] merge main --- crates/evm/src/tests/test_instructions.cairo | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/evm/src/tests/test_instructions.cairo b/crates/evm/src/tests/test_instructions.cairo index 011916282..e579c726c 100644 --- a/crates/evm/src/tests/test_instructions.cairo +++ b/crates/evm/src/tests/test_instructions.cairo @@ -4,8 +4,5 @@ mod test_duplication_operations; mod test_block_information; mod test_environment_information; mod test_push_operations; -<<<<<<< HEAD -mod test_sha3; -======= mod test_memory_operations; ->>>>>>> main +mod test_sha3; From 39cc37a55f86db62fe69b4a4c75b9c8ced88f54c Mon Sep 17 00:00:00 2001 From: Quentash Date: Tue, 5 Sep 2023 15:31:59 +0200 Subject: [PATCH 05/24] forgot to change gas_limit back to 1k --- crates/evm/src/tests/test_utils.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/evm/src/tests/test_utils.cairo b/crates/evm/src/tests/test_utils.cairo index d3295a4f6..6b8ac76c6 100644 --- a/crates/evm/src/tests/test_utils.cairo +++ b/crates/evm/src/tests/test_utils.cairo @@ -32,7 +32,7 @@ fn setup_execution_context() -> ExecutionContext { let call_context = setup_call_context(); let starknet_address: ContractAddress = starknet_address(); let evm_address: EthAddress = evm_address(); - let gas_limit: u64 = 100000; + let gas_limit: u64 = 1000; let gas_price: u64 = 10; let read_only: bool = false; let return_data = Default::default(); From 5675256a29b5f509c9762bc930080f86a5c1125c Mon Sep 17 00:00:00 2001 From: Quentash Date: Fri, 8 Sep 2023 14:00:12 +0200 Subject: [PATCH 06/24] review correction --- crates/evm/src/instructions/sha3.cairo | 87 ++++++++++++++------------ crates/utils/src/helpers.cairo | 14 ----- 2 files changed, 47 insertions(+), 54 deletions(-) diff --git a/crates/evm/src/instructions/sha3.cairo b/crates/evm/src/instructions/sha3.cairo index 2f8aec2e4..485de6399 100644 --- a/crates/evm/src/instructions/sha3.cairo +++ b/crates/evm/src/instructions/sha3.cairo @@ -6,92 +6,99 @@ use evm::stack::StackTrait; use evm::memory::{InternalMemoryTrait, MemoryTrait}; use evm::errors::EVMError; use evm::helpers::U256IntoResultU32; -use keccak::cairo_keccak; -use utils::helpers::{u256_to_bytes_array, u128_split}; +use keccak::{cairo_keccak, u128_split}; +use utils::helpers::{u256_to_bytes_array}; use array::ArrayTrait; #[generate_trait] impl Sha3Impl of Sha3Trait { - /// SHA3 operation. - /// Hashes n bytes in memory at a given offset in memory. + /// SHA3 operation : Hashes n bytes in memory at a given offset in memory. + /// + /// # Arguments + /// * `offset` - The offset in memory where to read the datas + /// * `size` - The amount of bytes to read + /// + /// Format 32 bytes chunk of data read from memory into 64 bits chunk in little endian + /// to be able to call cairro_keccak. + /// /// # Specification: https://www.evm.codes/#20?fork=shanghai fn exec_sha3(ref self: ExecutionContext) -> Result<(), EVMError> { let offset: u32 = Into::>::into((self.stack.pop()?))?; let mut size: u32 = Into::>::into((self.stack.pop()?))?; let init_size = size; - let mut toHash: Array = ArrayTrait::::new(); + let mut to_hash: Array = Default::default(); let mut last_input: u64 = 0; - let mut counter = 0; + let mut slot_to_read = offset; loop { if size < 32 { break; } - // If we try to read unallocted memory slot, we'll feed the data to hash with 0s - // and allocated the memory space the end of the process, which is cheaper than allocating every load(). - if (offset + (32 * counter)) > self.memory.bytes_len { - toHash.append(0); - toHash.append(0); - toHash.append(0); - toHash.append(0); - counter += 1; + // Pad left the hash input with zeroes for out-of-bound bytes + if slot_to_read > self.memory.bytes_len { + to_hash.append(0); + to_hash.append(0); + to_hash.append(0); + to_hash.append(0); + slot_to_read += 32; size -= 32; continue; } // Load the 32 words and reverse the bytes order, - let mut loaded = self.memory.load_internal((offset + (32 * counter))); + let mut loaded = self.memory.load_internal(slot_to_read); loaded.low = integer::u128_byte_reverse(loaded.low); loaded.high = integer::u128_byte_reverse(loaded.high); // Split the loaded word into u64 to feed cairo_keccak - let (highL, lowL) = u128_split(loaded.high); - let (highH, lowH) = u128_split(loaded.low); - toHash.append(lowL); - toHash.append(highL); - toHash.append(lowH); - toHash.append(highH); + let (high_l, low_l) = u128_split(loaded.high); + let (high_h, low_h) = u128_split(loaded.low); + to_hash.append(low_l); + to_hash.append(high_l); + to_hash.append(low_h); + to_hash.append(high_h); - counter += 1; + slot_to_read += 32; size -= 32; }; if size > 0 { - let mut loaded = 0; - if (offset + (32 * counter)) > self.memory.bytes_len { - loaded = 0; + // Load the last 32 bytes chunk containing required bytes + let mut loaded: u256 = if slot_to_read > self.memory.bytes_len { + 0 } else { - loaded = self.memory.load_internal((offset + (32 * counter))); - } + self.memory.load_internal(slot_to_read) + }; loaded.low = integer::u128_byte_reverse(loaded.low); loaded.high = integer::u128_byte_reverse(loaded.high); - let (highL, lowL) = u128_split(loaded.high); - let (highH, lowH) = u128_split(loaded.low); + let (high_l, low_l) = u128_split(loaded.high); + let (high_h, low_h) = u128_split(loaded.low); + // Assign the last input accordingly to required bytes amount if size < 8 { - last_input = lowL; + last_input = low_l; } else if size < 16 { size -= 8; - toHash.append(lowL); - last_input = highL; + to_hash.append(low_l); + last_input = high_l; } else if size < 24 { size -= 16; - toHash.append(lowL); - toHash.append(highL); - last_input = lowH; + to_hash.append(low_l); + to_hash.append(high_l); + last_input = low_h; } else { size -= 24; - toHash.append(lowL); - toHash.append(highL); - toHash.append(lowH); - last_input = highH; + to_hash.append(low_l); + to_hash.append(high_l); + to_hash.append(low_h); + last_input = high_h; } } self.memory.ensure_length(offset + init_size); - let mut hash = cairo_keccak(ref toHash, last_input, size); + let mut hash = cairo_keccak(ref to_hash, last_input, size); hash.low = integer::u128_byte_reverse(hash.low); hash.high = integer::u128_byte_reverse(hash.high); let tmp = hash.low; diff --git a/crates/utils/src/helpers.cairo b/crates/utils/src/helpers.cairo index 2a84a2719..d89a6ff5e 100644 --- a/crates/utils/src/helpers.cairo +++ b/crates/utils/src/helpers.cairo @@ -102,20 +102,6 @@ fn split_word_128(value: u256, ref dst: Array) { split_word(value, 16, ref dst) } -/// Try to convert u128 to u64, -fn u128_to_u64(input: u128) -> u64 { - input.try_into().unwrap() -} - -/// Splits a u128 into two u64. -fn u128_split(input: u128) -> (u64, u64) { - let (high, low) = integer::u128_safe_divmod( - input, 0x10000000000000000_u128.try_into().unwrap() - ); - - (u128_to_u64(high), u128_to_u64(low)) -} - /// Loads a sequence of bytes into a single u256 in big-endian /// From daa9d42a7362c8abd66f38efc26622f0549dc8a5 Mon Sep 17 00:00:00 2001 From: Quentash Date: Tue, 12 Sep 2023 12:42:06 +0200 Subject: [PATCH 07/24] added helpers --- crates/evm/src/instructions/sha3.cairo | 56 +++++++++----------------- crates/utils/src/helpers.cairo | 20 +++++++++ 2 files changed, 40 insertions(+), 36 deletions(-) diff --git a/crates/evm/src/instructions/sha3.cairo b/crates/evm/src/instructions/sha3.cairo index 485de6399..e448526e3 100644 --- a/crates/evm/src/instructions/sha3.cairo +++ b/crates/evm/src/instructions/sha3.cairo @@ -7,7 +7,7 @@ use evm::memory::{InternalMemoryTrait, MemoryTrait}; use evm::errors::EVMError; use evm::helpers::U256IntoResultU32; use keccak::{cairo_keccak, u128_split}; -use utils::helpers::{u256_to_bytes_array}; +use utils::helpers::{split_u256_into_u64_little, u256_bytes_reverse}; use array::ArrayTrait; @@ -35,28 +35,20 @@ impl Sha3Impl of Sha3Trait { if size < 32 { break; } - // Pad left the hash input with zeroes for out-of-bound bytes - if slot_to_read > self.memory.bytes_len { - to_hash.append(0); - to_hash.append(0); - to_hash.append(0); - to_hash.append(0); - slot_to_read += 32; - size -= 32; - continue; + let mut loaded = 0; + + if slot_to_read <= self.memory.bytes_len { + loaded = self.memory.load_internal(slot_to_read); } + // Load the 32 words and reverse the bytes order, - let mut loaded = self.memory.load_internal(slot_to_read); - loaded.low = integer::u128_byte_reverse(loaded.low); - loaded.high = integer::u128_byte_reverse(loaded.high); + let ((high_h, low_h), (high_l, low_l)) = split_u256_into_u64_little(loaded); // Split the loaded word into u64 to feed cairo_keccak - let (high_l, low_l) = u128_split(loaded.high); - let (high_h, low_h) = u128_split(loaded.low); - to_hash.append(low_l); - to_hash.append(high_l); to_hash.append(low_h); to_hash.append(high_h); + to_hash.append(low_l); + to_hash.append(high_l); slot_to_read += 32; size -= 32; @@ -70,41 +62,33 @@ impl Sha3Impl of Sha3Trait { self.memory.load_internal(slot_to_read) }; - loaded.low = integer::u128_byte_reverse(loaded.low); - loaded.high = integer::u128_byte_reverse(loaded.high); - let (high_l, low_l) = u128_split(loaded.high); - let (high_h, low_h) = u128_split(loaded.low); + let ((high_h, low_h), (high_l, low_l)) = split_u256_into_u64_little(loaded); // Assign the last input accordingly to required bytes amount if size < 8 { - last_input = low_l; + last_input = low_h; } else if size < 16 { size -= 8; - to_hash.append(low_l); - last_input = high_l; + to_hash.append(low_h); + last_input = high_h; } else if size < 24 { size -= 16; - to_hash.append(low_l); - to_hash.append(high_l); + to_hash.append(low_h); + to_hash.append(high_h); last_input = low_h; } else { size -= 24; - to_hash.append(low_l); - to_hash.append(high_l); to_hash.append(low_h); - last_input = high_h; + to_hash.append(high_h); + to_hash.append(low_l); + last_input = high_l; } } - self.memory.ensure_length(offset + init_size); + self.memory.load(offset + init_size); let mut hash = cairo_keccak(ref to_hash, last_input, size); - hash.low = integer::u128_byte_reverse(hash.low); - hash.high = integer::u128_byte_reverse(hash.high); - let tmp = hash.low; - hash.low = hash.high; - hash.high = tmp; - self.stack.push(hash) + self.stack.push(u256_bytes_reverse(hash)) } } diff --git a/crates/utils/src/helpers.cairo b/crates/utils/src/helpers.cairo index e1c0507b2..fb3049262 100644 --- a/crates/utils/src/helpers.cairo +++ b/crates/utils/src/helpers.cairo @@ -6,6 +6,7 @@ use debug::PrintTrait; use starknet::{EthAddress, EthAddressIntoFelt252}; use cmp::min; use utils::constants; +use keccak::u128_split; /// Ceils a number of bits to the next word (32 bytes) @@ -102,6 +103,25 @@ fn split_word_128(value: u256, ref dst: Array) { split_word(value, 16, ref dst) } +/// Splits a u256 into 4 u64 in little-endian. +/// Returns ยง(high_high, high_low),(low_high, low_low)) +fn split_u256_into_u64_little(value: u256) -> ((u64, u64), (u64, u64)) { + let mut to_split = value; + to_split.low = integer::u128_byte_reverse(to_split.low); + to_split.high = integer::u128_byte_reverse(to_split.high); + + (u128_split(to_split.high), u128_split(to_split.low)) +} + +/// Splits a u256 into 4 u64 in little-endian. +fn u256_bytes_reverse(value: u256) -> u256 { + let mut reversed: u256 = 0; + reversed.high = integer::u128_byte_reverse(value.low); + reversed.low = integer::u128_byte_reverse(value.high); + + reversed +} + /// Loads a sequence of bytes into a single u256 in big-endian /// From 66f36a1fcc95712a7660fd15101413b4b2756859 Mon Sep 17 00:00:00 2001 From: Quentash Date: Tue, 12 Sep 2023 20:18:27 +0200 Subject: [PATCH 08/24] soluce2 + check on memory load --- crates/evm/src/instructions/sha3.cairo | 104 ++++++++------- crates/evm/src/memory.cairo | 3 +- .../tests/test_instructions/test_sha3.cairo | 124 +----------------- crates/utils/src/helpers.cairo | 4 +- 4 files changed, 66 insertions(+), 169 deletions(-) diff --git a/crates/evm/src/instructions/sha3.cairo b/crates/evm/src/instructions/sha3.cairo index e448526e3..ccdc5819d 100644 --- a/crates/evm/src/instructions/sha3.cairo +++ b/crates/evm/src/instructions/sha3.cairo @@ -3,7 +3,7 @@ // Internal imports use evm::context::{ExecutionContext, ExecutionContextTrait, BoxDynamicExecutionContextDestruct}; use evm::stack::StackTrait; -use evm::memory::{InternalMemoryTrait, MemoryTrait}; +use evm::memory::MemoryTrait; use evm::errors::EVMError; use evm::helpers::U256IntoResultU32; use keccak::{cairo_keccak, u128_split}; @@ -24,71 +24,87 @@ impl Sha3Impl of Sha3Trait { /// /// # Specification: https://www.evm.codes/#20?fork=shanghai fn exec_sha3(ref self: ExecutionContext) -> Result<(), EVMError> { - let offset: u32 = Into::>::into((self.stack.pop()?))?; + let mut offset: u32 = Into::>::into((self.stack.pop()?))?; let mut size: u32 = Into::>::into((self.stack.pop()?))?; - let init_size = size; let mut to_hash: Array = Default::default(); let mut last_input: u64 = 0; - let mut slot_to_read = offset; + + // Fill to_hash with bytes from memory loop { - if size < 32 { + if (size < 32) | (offset > self.memory.bytes_len) { break; } - let mut loaded = 0; - - if slot_to_read <= self.memory.bytes_len { - loaded = self.memory.load_internal(slot_to_read); - } - - // Load the 32 words and reverse the bytes order, + let (loaded, _) = self.memory.load(offset); let ((high_h, low_h), (high_l, low_l)) = split_u256_into_u64_little(loaded); - - // Split the loaded word into u64 to feed cairo_keccak to_hash.append(low_h); to_hash.append(high_h); to_hash.append(low_l); to_hash.append(high_l); - slot_to_read += 32; + offset += 32; size -= 32; }; - if size > 0 { - // Load the last 32 bytes chunk containing required bytes - let mut loaded: u256 = if slot_to_read > self.memory.bytes_len { - 0 - } else { - self.memory.load_internal(slot_to_read) - }; + // Bytes from unallocated memory are set to 0 + loop { + if size < 32 { + break; + } + to_hash.append(0); + to_hash.append(0); + to_hash.append(0); + to_hash.append(0); - let ((high_h, low_h), (high_l, low_l)) = split_u256_into_u64_little(loaded); + offset += 32; + size -= 32; + }; - // Assign the last input accordingly to required bytes amount - if size < 8 { - last_input = low_h; - } else if size < 16 { - size -= 8; - to_hash.append(low_h); - last_input = high_h; - } else if size < 24 { - size -= 16; - to_hash.append(low_h); - to_hash.append(high_h); - last_input = low_h; - } else { - size -= 24; - to_hash.append(low_h); - to_hash.append(high_h); - to_hash.append(low_l); - last_input = high_l; - } + // Fill last_input with last bytes to hash + if size > 0 { + let (loaded, _) = self.memory.load(offset); + last_input = InternalSha3Trait::get_last_input(ref to_hash, loaded, size); + size %= 8; } - self.memory.load(offset + init_size); - let mut hash = cairo_keccak(ref to_hash, last_input, size); self.stack.push(u256_bytes_reverse(hash)) } } + + +#[generate_trait] +impl InternalSha3Methods of InternalSha3Trait { + /// Return the last u64 chunk to hash, given a size, from an u256. + /// This function is used to prepare inputs for keccak::cairo_keccak. + /// + /// This function will split a given u256 into little endian u64 and + /// return the chunk containing the last bytes to hash while + /// appending the chunks prior to this one. + /// + /// # Arguments + /// + /// * `to_hash` - A reference to the array containing previous bytes + /// * `value` - The `u256` element to get the last u64 input from + /// * `size` - The amonut of bytes to append to to_hash + #[inline(always)] + fn get_last_input(ref to_hash: Array, value: u256, size: u32) -> u64 { + let ((high_h, low_h), (high_l, low_l)) = split_u256_into_u64_little(value); + if size < 8 { + return low_h; + } else if size < 16 { + to_hash.append(low_h); + return high_h; + } else if size < 24 { + to_hash.append(low_h); + to_hash.append(high_h); + return low_h; + } else { + to_hash.append(low_h); + to_hash.append(high_h); + to_hash.append(low_l); + return high_l; + } + } +} diff --git a/crates/evm/src/memory.cairo b/crates/evm/src/memory.cairo index 5552afa10..381aea86e 100644 --- a/crates/evm/src/memory.cairo +++ b/crates/evm/src/memory.cairo @@ -143,7 +143,8 @@ impl MemoryImpl of MemoryTrait { /// * `usize` - The gas cost of expanding the memory. #[inline(always)] fn load(ref self: Memory, offset: usize) -> (u256, usize) { - let gas_cost = self.ensure_length(32 + offset); + //let gas_cost = self.ensure_length(32 + offset); + let gas_cost = 0; let loaded_element = self.load_internal(offset); (loaded_element, gas_cost) } diff --git a/crates/evm/src/tests/test_instructions/test_sha3.cairo b/crates/evm/src/tests/test_instructions/test_sha3.cairo index 99e5bbd8c..395724c98 100644 --- a/crates/evm/src/tests/test_instructions/test_sha3.cairo +++ b/crates/evm/src/tests/test_instructions/test_sha3.cairo @@ -35,7 +35,6 @@ fn test_sha3_size_0_offset_0() { #[available_gas(20000000)] fn test_sha3_size_5_offset_4() { // Given - let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); ctx.stack.push(0x05); @@ -57,7 +56,6 @@ fn test_sha3_size_5_offset_4() { #[available_gas(20000000)] fn test_sha3_size_10_offset_10() { // Given - let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); ctx.stack.push(10); @@ -79,7 +77,6 @@ fn test_sha3_size_10_offset_10() { #[available_gas(1000000000000000)] fn test_sha3_size_0xFFFFF_offset_1000() { // Given - let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); ctx.stack.push(0xFFFFF); @@ -98,10 +95,9 @@ fn test_sha3_size_0xFFFFF_offset_1000() { } #[test] -#[available_gas(8000000000)] +#[available_gas(1000000000000000)] fn test_sha3_size_1000000_offset_2() { // Given - let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); ctx.stack.push(1000000); @@ -109,122 +105,13 @@ fn test_sha3_size_1000000_offset_2() { ctx.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); - // When - ctx.exec_sha3(); - // Then - let result = ctx.stack.peek().unwrap(); - assert( - result == 0x4aa461ae9513f3b03ae397740ade979809dd02ae2c14e101b32842fbee21f0a, 'wrong result' - ); -} - -#[test] -#[available_gas(20000000)] -fn test_sha3_size_1_offset_960() { - // Given - let bytecode: Span = array![1, 2, 3, 4, 5].span(); - let mut ctx = setup_execution_context(); - - ctx.stack.push(1); - ctx.stack.push(960); - - ctx.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); - - // When - ctx.exec_sha3(); - - // Then - let result = ctx.stack.peek().unwrap(); - assert( - result == 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a, 'wrong result' - ); -} - -#[test] -#[available_gas(20000000)] -fn test_sha3_size_1_offset_992() { - // Given - let bytecode: Span = array![1, 2, 3, 4, 5].span(); - let mut ctx = setup_execution_context(); - - ctx.stack.push(1); - ctx.stack.push(992); - - ctx.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); - // When ctx.exec_sha3(); // Then let result = ctx.stack.peek().unwrap(); assert( - result == 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a, 'wrong result' - ); -} - -#[test] -#[available_gas(20000000)] -fn test_sha3_size_1_offset_1024() { - // Given - let bytecode: Span = array![1, 2, 3, 4, 5].span(); - let mut ctx = setup_execution_context(); - - ctx.stack.push(1); - ctx.stack.push(1024); - - ctx.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); - - // When - ctx.exec_sha3(); - - // Then - let result = ctx.stack.peek().unwrap(); - assert( - result == 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a, 'wrong result' - ); -} - -#[test] -#[available_gas(20000000)] -fn test_sha3_size_1_offset_1984() { - // Given - let bytecode: Span = array![1, 2, 3, 4, 5].span(); - let mut ctx = setup_execution_context(); - - ctx.stack.push(1); - ctx.stack.push(1984); - - ctx.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); - - // When - ctx.exec_sha3(); - - // Then - let result = ctx.stack.peek().unwrap(); - assert( - result == 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a, 'wrong result' - ); -} - -#[test] -#[available_gas(20000000)] -fn test_sha3_size_1_offset_2016() { - // Given - let bytecode: Span = array![1, 2, 3, 4, 5].span(); - let mut ctx = setup_execution_context(); - - ctx.stack.push(1); - ctx.stack.push(2016); - - ctx.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); - - // When - ctx.exec_sha3(); - - // Then - let result = ctx.stack.peek().unwrap(); - assert( - result == 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a, 'wrong result' + result == 0x4aa461ae9513f3b03ae397740ade979809dd02ae2c14e101b32842fbee21f0a, 'wrong result' ); } @@ -232,7 +119,6 @@ fn test_sha3_size_1_offset_2016() { #[available_gas(20000000)] fn test_sha3_size_1_offset_2048() { // Given - let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); ctx.stack.push(1); @@ -254,7 +140,6 @@ fn test_sha3_size_1_offset_2048() { #[available_gas(20000000)] fn test_sha3_size_0_offset_1024() { // Given - let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); ctx.stack.push(0); @@ -276,7 +161,6 @@ fn test_sha3_size_0_offset_1024() { #[available_gas(20000000)] fn test_sha3_size_32_offset_2016() { // Given - let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); ctx.stack.push(32); @@ -298,7 +182,6 @@ fn test_sha3_size_32_offset_2016() { #[available_gas(20000000)] fn test_sha3_size_32_offset_0() { // Given - let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); ctx.stack.push(32); @@ -320,7 +203,6 @@ fn test_sha3_size_32_offset_0() { #[available_gas(20000000)] fn test_sha3_size_31_offset_0() { // Given - let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); ctx.stack.push(31); @@ -342,7 +224,6 @@ fn test_sha3_size_31_offset_0() { #[available_gas(20000000)] fn test_sha3_size_33_offset_0() { // Given - let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); ctx.stack.push(33); @@ -364,7 +245,6 @@ fn test_sha3_size_33_offset_0() { #[available_gas(20000000000)] fn test_sha3_size_0x0C80_offset_0() { // Given - let bytecode: Span = array![1, 2, 3, 4, 5].span(); let mut ctx = setup_execution_context(); ctx.stack.push(0x0C80); diff --git a/crates/utils/src/helpers.cairo b/crates/utils/src/helpers.cairo index f89eba2ca..9c5ce135e 100644 --- a/crates/utils/src/helpers.cairo +++ b/crates/utils/src/helpers.cairo @@ -99,7 +99,7 @@ fn split_word_128(value: u256, ref dst: Array) { split_word(value, 16, ref dst) } -/// Splits a u256 into 4 u64 in little-endian. +/// Splits a u256 into 4 little endian u64. /// Returns ยง(high_high, high_low),(low_high, low_low)) fn split_u256_into_u64_little(value: u256) -> ((u64, u64), (u64, u64)) { let mut to_split = value; @@ -109,7 +109,7 @@ fn split_u256_into_u64_little(value: u256) -> ((u64, u64), (u64, u64)) { (u128_split(to_split.high), u128_split(to_split.low)) } -/// Splits a u256 into 4 u64 in little-endian. +/// Reverse the bytes of an u256 fn u256_bytes_reverse(value: u256) -> u256 { let mut reversed: u256 = 0; reversed.high = integer::u128_byte_reverse(value.low); From c0fd0a57e8d6d085398dc8fef93f2367b0df50ed Mon Sep 17 00:00:00 2001 From: Quentash Date: Tue, 12 Sep 2023 20:21:46 +0200 Subject: [PATCH 09/24] typos --- crates/evm/src/instructions/sha3.cairo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/evm/src/instructions/sha3.cairo b/crates/evm/src/instructions/sha3.cairo index ccdc5819d..d5482a525 100644 --- a/crates/evm/src/instructions/sha3.cairo +++ b/crates/evm/src/instructions/sha3.cairo @@ -86,8 +86,8 @@ impl InternalSha3Methods of InternalSha3Trait { /// # Arguments /// /// * `to_hash` - A reference to the array containing previous bytes - /// * `value` - The `u256` element to get the last u64 input from - /// * `size` - The amonut of bytes to append to to_hash + /// * `value` - The `u256` element to get the last input from + /// * `size` - The amount of bytes to append to to_hash #[inline(always)] fn get_last_input(ref to_hash: Array, value: u256, size: u32) -> u64 { let ((high_h, low_h), (high_l, low_l)) = split_u256_into_u64_little(value); From 4deaf224db0c3d8a2df5f1877a0cbc1417d224ed Mon Sep 17 00:00:00 2001 From: Quentash Date: Wed, 13 Sep 2023 10:35:23 +0200 Subject: [PATCH 10/24] added internal sha3 tests --- crates/evm/src/instructions/sha3.cairo | 3 +- .../tests/test_instructions/test_sha3.cairo | 49 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/crates/evm/src/instructions/sha3.cairo b/crates/evm/src/instructions/sha3.cairo index d5482a525..490c0146c 100644 --- a/crates/evm/src/instructions/sha3.cairo +++ b/crates/evm/src/instructions/sha3.cairo @@ -88,6 +88,7 @@ impl InternalSha3Methods of InternalSha3Trait { /// * `to_hash` - A reference to the array containing previous bytes /// * `value` - The `u256` element to get the last input from /// * `size` - The amount of bytes to append to to_hash + /// Returns the last_input. #[inline(always)] fn get_last_input(ref to_hash: Array, value: u256, size: u32) -> u64 { let ((high_h, low_h), (high_l, low_l)) = split_u256_into_u64_little(value); @@ -99,7 +100,7 @@ impl InternalSha3Methods of InternalSha3Trait { } else if size < 24 { to_hash.append(low_h); to_hash.append(high_h); - return low_h; + return low_l; } else { to_hash.append(low_h); to_hash.append(high_h); diff --git a/crates/evm/src/tests/test_instructions/test_sha3.cairo b/crates/evm/src/tests/test_instructions/test_sha3.cairo index 395724c98..65f738001 100644 --- a/crates/evm/src/tests/test_instructions/test_sha3.cairo +++ b/crates/evm/src/tests/test_instructions/test_sha3.cairo @@ -1,4 +1,5 @@ use evm::instructions::Sha3Trait; +use evm::instructions::sha3::InternalSha3Trait; use evm::tests::test_utils::setup_execution_context; use evm::context::{ExecutionContext, ExecutionContextTrait, BoxDynamicExecutionContextDestruct}; use evm::memory::{InternalMemoryTrait, MemoryTrait}; @@ -270,3 +271,51 @@ fn test_sha3_size_0x0C80_offset_0() { result == 0x2022ae07f3a362b08ac0a4bcb785c830cb5c368dc0ce6972249c6abbc68a5291, 'wrong result' ); } + +#[test] +#[available_gas(20000000000)] +fn test_sha3_internal_get_last_input_size_5() { + // Given + let mut to_hash: Array = Default::default(); + let value = 0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000; + let size = 5; + + // When + let result = InternalSha3Trait::get_last_input(ref to_hash, value, size); + + // Then + assert(result == 0xE5000000FFFFFFFA, 'wrong result'); + assert(to_hash.len() == 0, 'wrong result'); +} + +#[test] +#[available_gas(20000000000)] +fn test_sha3_internal_get_last_input_size_20() { + // Given + let mut to_hash: Array = Default::default(); + let value = 0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000; + let size = 20; + + // When + let result = InternalSha3Trait::get_last_input(ref to_hash, value, size); + + // Then + assert(result == 0x00200400000000AD, 'wrong result'); + assert(to_hash.len() == 2, 'wrong result'); +} + +#[test] +#[available_gas(20000000000)] +fn test_sha3_internal_get_last_input_size_50() { + // Given + let mut to_hash: Array = Default::default(); + let value = 0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000; + let size = 50; + + // When + let result = InternalSha3Trait::get_last_input(ref to_hash, value, size); + + // Then + assert(result == 0x0000450000DEFA00, 'wrong result'); + assert(to_hash.len() == 3, 'wrong result'); +} From ed2a4d159ac88c74fde6e2179a285c0efdf7e8db Mon Sep 17 00:00:00 2001 From: Quentash Date: Wed, 13 Sep 2023 11:26:31 +0200 Subject: [PATCH 11/24] added helpers tests --- crates/utils/src/tests/test_helpers.cairo | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/crates/utils/src/tests/test_helpers.cairo b/crates/utils/src/tests/test_helpers.cairo index 003687c35..eebf634f5 100644 --- a/crates/utils/src/tests/test_helpers.cairo +++ b/crates/utils/src/tests/test_helpers.cairo @@ -162,3 +162,25 @@ fn test_clone_pad_zeroes() { let res = original.pad_right(3); assert(res == array![1, 2, 3, 4, 0, 0, 0].span(), 'padding mismatch'); } + +#[test] +#[available_gas(2000000000)] +fn test_reverse_bytes_u256() { + let value: u256 = 0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000; + let res = helpers::u256_bytes_reverse(value); + assert( + res == 0x0000450000DEFA0000200400000000ADDE00000077000000E5000000FFFFFFFA, + 'reverse mismatch' + ); +} + +#[test] +#[available_gas(2000000000)] +fn test_split_u256_into_u64_little() { + let value: u256 = 0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000; + let ((high_h, low_h), (high_l, low_l)) = helpers::split_u256_into_u64_little(value); + assert(high_h == 0xDE00000077000000, 'reverse mismatch'); + assert(low_h == 0xE5000000FFFFFFFA, 'reverse mismatch'); + assert(high_l == 0x0000450000DEFA00, 'reverse mismatch'); + assert(low_l == 0x00200400000000AD, 'reverse mismatch'); +} From 3af3926ab94ad5db562a0fb0b6fe328e35deff4c Mon Sep 17 00:00:00 2001 From: Quentash Date: Wed, 13 Sep 2023 11:31:22 +0200 Subject: [PATCH 12/24] wrong error message --- crates/utils/src/tests/test_helpers.cairo | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/utils/src/tests/test_helpers.cairo b/crates/utils/src/tests/test_helpers.cairo index eebf634f5..ff90fbcf3 100644 --- a/crates/utils/src/tests/test_helpers.cairo +++ b/crates/utils/src/tests/test_helpers.cairo @@ -179,8 +179,8 @@ fn test_reverse_bytes_u256() { fn test_split_u256_into_u64_little() { let value: u256 = 0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000; let ((high_h, low_h), (high_l, low_l)) = helpers::split_u256_into_u64_little(value); - assert(high_h == 0xDE00000077000000, 'reverse mismatch'); - assert(low_h == 0xE5000000FFFFFFFA, 'reverse mismatch'); - assert(high_l == 0x0000450000DEFA00, 'reverse mismatch'); - assert(low_l == 0x00200400000000AD, 'reverse mismatch'); + assert(high_h == 0xDE00000077000000, 'split mismatch'); + assert(low_h == 0xE5000000FFFFFFFA, 'split mismatch'); + assert(high_l == 0x0000450000DEFA00, 'split mismatch'); + assert(low_l == 0x00200400000000AD, 'split mismatch'); } From c81a09f55d6e1fb7039fed5428b0e8403c0ceee8 Mon Sep 17 00:00:00 2001 From: Quentash Date: Wed, 13 Sep 2023 14:42:48 +0200 Subject: [PATCH 13/24] used mod internal instead of trait --- crates/evm/src/instructions/sha3.cairo | 7 +++---- .../src/tests/test_instructions/test_sha3.cairo | 14 +++++++------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/crates/evm/src/instructions/sha3.cairo b/crates/evm/src/instructions/sha3.cairo index 490c0146c..4d125959f 100644 --- a/crates/evm/src/instructions/sha3.cairo +++ b/crates/evm/src/instructions/sha3.cairo @@ -63,7 +63,7 @@ impl Sha3Impl of Sha3Trait { // Fill last_input with last bytes to hash if size > 0 { let (loaded, _) = self.memory.load(offset); - last_input = InternalSha3Trait::get_last_input(ref to_hash, loaded, size); + last_input = internal::get_last_input(ref to_hash, loaded, size); size %= 8; } @@ -74,8 +74,8 @@ impl Sha3Impl of Sha3Trait { } -#[generate_trait] -impl InternalSha3Methods of InternalSha3Trait { +mod internal { + use utils::helpers::split_u256_into_u64_little; /// Return the last u64 chunk to hash, given a size, from an u256. /// This function is used to prepare inputs for keccak::cairo_keccak. /// @@ -89,7 +89,6 @@ impl InternalSha3Methods of InternalSha3Trait { /// * `value` - The `u256` element to get the last input from /// * `size` - The amount of bytes to append to to_hash /// Returns the last_input. - #[inline(always)] fn get_last_input(ref to_hash: Array, value: u256, size: u32) -> u64 { let ((high_h, low_h), (high_l, low_l)) = split_u256_into_u64_little(value); if size < 8 { diff --git a/crates/evm/src/tests/test_instructions/test_sha3.cairo b/crates/evm/src/tests/test_instructions/test_sha3.cairo index 65f738001..69eb9751e 100644 --- a/crates/evm/src/tests/test_instructions/test_sha3.cairo +++ b/crates/evm/src/tests/test_instructions/test_sha3.cairo @@ -1,5 +1,5 @@ use evm::instructions::Sha3Trait; -use evm::instructions::sha3::InternalSha3Trait; +use evm::instructions::sha3::internal; use evm::tests::test_utils::setup_execution_context; use evm::context::{ExecutionContext, ExecutionContextTrait, BoxDynamicExecutionContextDestruct}; use evm::memory::{InternalMemoryTrait, MemoryTrait}; @@ -277,11 +277,11 @@ fn test_sha3_size_0x0C80_offset_0() { fn test_sha3_internal_get_last_input_size_5() { // Given let mut to_hash: Array = Default::default(); - let value = 0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000; + let value: u256 = 0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000; let size = 5; // When - let result = InternalSha3Trait::get_last_input(ref to_hash, value, size); + let result = internal::get_last_input(ref to_hash, value, size); // Then assert(result == 0xE5000000FFFFFFFA, 'wrong result'); @@ -293,11 +293,11 @@ fn test_sha3_internal_get_last_input_size_5() { fn test_sha3_internal_get_last_input_size_20() { // Given let mut to_hash: Array = Default::default(); - let value = 0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000; + let value: u256 = 0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000; let size = 20; // When - let result = InternalSha3Trait::get_last_input(ref to_hash, value, size); + let result = internal::get_last_input(ref to_hash, value, size); // Then assert(result == 0x00200400000000AD, 'wrong result'); @@ -309,11 +309,11 @@ fn test_sha3_internal_get_last_input_size_20() { fn test_sha3_internal_get_last_input_size_50() { // Given let mut to_hash: Array = Default::default(); - let value = 0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000; + let value: u256 = 0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000; let size = 50; // When - let result = InternalSha3Trait::get_last_input(ref to_hash, value, size); + let result = internal::get_last_input(ref to_hash, value, size); // Then assert(result == 0x0000450000DEFA00, 'wrong result'); From ecfc4e02bdc8aa7ed6cc13dfcd9e6c1f73f2dc01 Mon Sep 17 00:00:00 2001 From: Quentash Date: Thu, 14 Sep 2023 17:29:36 +0200 Subject: [PATCH 14/24] functions and comments rework --- crates/evm/src/instructions/sha3.cairo | 130 ++++++++++++------ crates/evm/src/memory.cairo | 3 +- .../tests/test_instructions/test_sha3.cairo | 86 +++++++++++- crates/utils/src/helpers.cairo | 24 ++-- crates/utils/src/tests/test_helpers.cairo | 2 +- 5 files changed, 181 insertions(+), 64 deletions(-) diff --git a/crates/evm/src/instructions/sha3.cairo b/crates/evm/src/instructions/sha3.cairo index 4d125959f..3539ff0d8 100644 --- a/crates/evm/src/instructions/sha3.cairo +++ b/crates/evm/src/instructions/sha3.cairo @@ -7,32 +7,88 @@ use evm::memory::MemoryTrait; use evm::errors::EVMError; use evm::helpers::U256IntoResultU32; use keccak::{cairo_keccak, u128_split}; -use utils::helpers::{split_u256_into_u64_little, u256_bytes_reverse}; +use utils::helpers::{split_u256_into_u64_little, reverse_endianness}; use array::ArrayTrait; #[generate_trait] impl Sha3Impl of Sha3Trait { - /// SHA3 operation : Hashes n bytes in memory at a given offset in memory. - /// - /// # Arguments + /// SHA3 operation : Hashes n bytes in memory at a given offset in memory + /// and push the hash result to the stack. + /// + /// # Inputs /// * `offset` - The offset in memory where to read the datas /// * `size` - The amount of bytes to read /// - /// Format 32 bytes chunk of data read from memory into 64 bits chunk in little endian - /// to be able to call cairro_keccak. - /// /// # Specification: https://www.evm.codes/#20?fork=shanghai fn exec_sha3(ref self: ExecutionContext) -> Result<(), EVMError> { let mut offset: u32 = Into::>::into((self.stack.pop()?))?; let mut size: u32 = Into::>::into((self.stack.pop()?))?; let mut to_hash: Array = Default::default(); - let mut last_input: u64 = 0; - // Fill to_hash with bytes from memory + let (chunks_from_mem, chunks_of_zeroes) = internal::compute_data_origin( + size, offset, self.memory.bytes_len + ); + internal::fill_array_with_memory_chunks(ref self, ref to_hash, ref offset, chunks_from_mem); + internal::fill_array_with_zeroes(ref self, ref to_hash, chunks_of_zeroes); + + // Fill last_input with last bytes to hash + let last_input: u64 = if (size % 32 != 0) { + let (loaded, _) = self.memory.load(offset); + internal::fill_array_with_last_inputs(ref to_hash, loaded, size % 32) + } else { + 0 + }; + + let mut hash = cairo_keccak(ref to_hash, last_input, size % 8); + self.stack.push(reverse_endianness(hash)) + } +} + + +mod internal { + use evm::context::{ExecutionContext, ExecutionContextTrait, BoxDynamicExecutionContextDestruct}; + use evm::stack::StackTrait; + use evm::memory::MemoryTrait; + use utils::helpers::split_u256_into_u64_little; + + /// This function will compute how many chunks of 32 Bytes data are read + /// from the memory and how many chunks of 32 Bytes data must be filled + /// with zeroes. + /// + /// # Arguments + /// + /// * `size` - The amount of bytes to hash + /// * `offset` - Offset in memory + /// * `mem_len` - Size of the memory + /// Returns : (chunk_from_mem, chunks_of_zeroes) + fn compute_data_origin(size: u32, offset: u32, mem_len: u32) -> (u32, u32) { + if offset > mem_len { + return (0, size / 32); + } + if (mem_len - offset < 32) && (size > 32) { + let chunk_from_mem = (mem_len - offset) / 32; + return (chunk_from_mem + 1, (size / 32) - chunk_from_mem - 1); + } + let chunk_from_mem = (cmp::min(mem_len - offset, size)) / 32; + (chunk_from_mem, (size / 32) - chunk_from_mem) + } + + /// This function will fill an array with little endian u64 + /// by splitting 32 Bytes chunk read from the memory. + /// + /// # Arguments + /// + /// * `self` - The context in which the memory is read + /// * `to_hash` - A reference to the array to fill + /// * `offset` - Offset in memory + /// * `amount` - The amount of 32 Bytes chunks to read from memory + fn fill_array_with_memory_chunks( + ref self: ExecutionContext, ref to_hash: Array, ref offset: u32, mut amount: u32 + ) { loop { - if (size < 32) | (offset > self.memory.bytes_len) { + if amount == 0 { break; } let (loaded, _) = self.memory.load(offset); @@ -43,12 +99,23 @@ impl Sha3Impl of Sha3Trait { to_hash.append(high_l); offset += 32; - size -= 32; + amount -= 1; }; + } - // Bytes from unallocated memory are set to 0 + /// This function will fill an u64 array with a given amount of + /// 32 Bytes chunks of zeroes. + /// + /// # Arguments + /// + /// * `self` - The context in which the memory is read + /// * `to_hash` - A reference to the array to fill + /// * `amount` - The amount of 32 Bytes chunks of zeroes to append to to hash + fn fill_array_with_zeroes( + ref self: ExecutionContext, ref to_hash: Array, mut amount: u32 + ) { loop { - if size < 32 { + if amount == 0 { break; } to_hash.append(0); @@ -56,40 +123,21 @@ impl Sha3Impl of Sha3Trait { to_hash.append(0); to_hash.append(0); - offset += 32; - size -= 32; + amount -= 1; }; - - // Fill last_input with last bytes to hash - if size > 0 { - let (loaded, _) = self.memory.load(offset); - last_input = internal::get_last_input(ref to_hash, loaded, size); - size %= 8; - } - - let mut hash = cairo_keccak(ref to_hash, last_input, size); - - self.stack.push(u256_bytes_reverse(hash)) } -} - -mod internal { - use utils::helpers::split_u256_into_u64_little; - /// Return the last u64 chunk to hash, given a size, from an u256. - /// This function is used to prepare inputs for keccak::cairo_keccak. - /// - /// This function will split a given u256 into little endian u64 and - /// return the chunk containing the last bytes to hash while - /// appending the chunks prior to this one. + /// This function will fill an array with the remaining little endian u64 + /// depending on size from a 32 Bytes chunk of data and return + /// the u64 chunk containing the last bytes that aren't 8 Bytes long. /// /// # Arguments /// - /// * `to_hash` - A reference to the array containing previous bytes - /// * `value` - The `u256` element to get the last input from - /// * `size` - The amount of bytes to append to to_hash - /// Returns the last_input. - fn get_last_input(ref to_hash: Array, value: u256, size: u32) -> u64 { + /// * `to_hash` - A reference to the array to fill + /// * `value` - The 32 Bytes chunk to split and get the bytes from + /// * `size` - The amount of bytes still required to hash + /// Returns the last u64 chunk that isn't 8 Bytes long. + fn fill_array_with_last_inputs(ref to_hash: Array, value: u256, size: u32) -> u64 { let ((high_h, low_h), (high_l, low_l)) = split_u256_into_u64_little(value); if size < 8 { return low_h; diff --git a/crates/evm/src/memory.cairo b/crates/evm/src/memory.cairo index e793308c8..026cc9ef0 100644 --- a/crates/evm/src/memory.cairo +++ b/crates/evm/src/memory.cairo @@ -149,8 +149,7 @@ impl MemoryImpl of MemoryTrait { /// * `usize` - The gas cost of expanding the memory. #[inline(always)] fn load(ref self: Memory, offset: usize) -> (u256, usize) { - //let gas_cost = self.ensure_length(32 + offset); - let gas_cost = 0; + let gas_cost = self.ensure_length(32 + offset); let loaded_element = self.load_internal(offset); (loaded_element, gas_cost) } diff --git a/crates/evm/src/tests/test_instructions/test_sha3.cairo b/crates/evm/src/tests/test_instructions/test_sha3.cairo index 69eb9751e..08bf1730b 100644 --- a/crates/evm/src/tests/test_instructions/test_sha3.cairo +++ b/crates/evm/src/tests/test_instructions/test_sha3.cairo @@ -274,14 +274,83 @@ fn test_sha3_size_0x0C80_offset_0() { #[test] #[available_gas(20000000000)] -fn test_sha3_internal_get_last_input_size_5() { +fn test_sha3_internal_fill_array_with_memory_chunks() { + // Given + let mut ctx = setup_execution_context(); + let mut to_hash: Array = Default::default(); + + ctx.memory.store(0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000, 0); + let mut size = 32; + let mut offset = 0; + + // When + let (chunks_from_mem, _) = internal::compute_data_origin(size, offset, ctx.memory.bytes_len); + internal::fill_array_with_memory_chunks(ref ctx, ref to_hash, ref offset, chunks_from_mem); + + // Then + assert(to_hash.len() == 4, 'wrong array length'); + assert((*to_hash[0]) == 0xE5000000FFFFFFFA, 'wrong array value'); + assert((*to_hash[1]) == 0xDE00000077000000, 'wrong array value'); + assert((*to_hash[2]) == 0x00200400000000AD, 'wrong array value'); + assert((*to_hash[3]) == 0x0000450000DEFA00, 'wrong array value'); +} + +#[test] +#[available_gas(20000000000)] +fn test_sha3_internal_fill_array_with_memory_chunks_size_33() { + // Given + let mut ctx = setup_execution_context(); + let mut to_hash: Array = Default::default(); + + ctx.memory.store(0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000, 0); + let mut size = 33; + let mut offset = 0; + + // When + let (chunks_from_mem, _) = internal::compute_data_origin(size, offset, ctx.memory.bytes_len); + internal::fill_array_with_memory_chunks(ref ctx, ref to_hash, ref offset, chunks_from_mem); + + // Then + assert(to_hash.len() == 4, 'wrong array length'); + assert((*to_hash[0]) == 0xE5000000FFFFFFFA, 'wrong array value'); + assert((*to_hash[1]) == 0xDE00000077000000, 'wrong array value'); + assert((*to_hash[2]) == 0x00200400000000AD, 'wrong array value'); + assert((*to_hash[3]) == 0x0000450000DEFA00, 'wrong array value'); +} + +#[test] +#[available_gas(20000000000)] +fn test_sha3_internal_fill_array_with_zeroes() { + // Given + let mut ctx = setup_execution_context(); + let mut to_hash: Array = Default::default(); + + ctx.memory.store(0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000, 0); + let mut size = 33; + let mut offset = 50; + + // When + let (_, chunks_of_zeroes) = internal::compute_data_origin(size, offset, ctx.memory.bytes_len); + internal::fill_array_with_zeroes(ref ctx, ref to_hash, chunks_of_zeroes); + + // Then + assert(to_hash.len() == 4, 'wrong array length'); + assert((*to_hash[0]) == 0x0, 'wrong array value'); + assert((*to_hash[1]) == 0x0, 'wrong array value'); + assert((*to_hash[2]) == 0x0, 'wrong array value'); + assert((*to_hash[3]) == 0x0, 'wrong array value'); +} + +#[test] +#[available_gas(20000000000)] +fn test_sha3_internal_fill_array_with_last_inputs_size_5() { // Given let mut to_hash: Array = Default::default(); let value: u256 = 0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000; let size = 5; // When - let result = internal::get_last_input(ref to_hash, value, size); + let result = internal::fill_array_with_last_inputs(ref to_hash, value, size); // Then assert(result == 0xE5000000FFFFFFFA, 'wrong result'); @@ -290,32 +359,37 @@ fn test_sha3_internal_get_last_input_size_5() { #[test] #[available_gas(20000000000)] -fn test_sha3_internal_get_last_input_size_20() { +fn test_sha3_internal_fill_array_with_last_inputs_size_20() { // Given let mut to_hash: Array = Default::default(); let value: u256 = 0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000; let size = 20; // When - let result = internal::get_last_input(ref to_hash, value, size); + let result = internal::fill_array_with_last_inputs(ref to_hash, value, size); // Then assert(result == 0x00200400000000AD, 'wrong result'); assert(to_hash.len() == 2, 'wrong result'); + assert((*to_hash[0]) == 0xE5000000FFFFFFFA, 'wrong array value'); + assert((*to_hash[1]) == 0xDE00000077000000, 'wrong array value'); } #[test] #[available_gas(20000000000)] -fn test_sha3_internal_get_last_input_size_50() { +fn test_sha3_internal_fill_array_with_last_inputs_size_50() { // Given let mut to_hash: Array = Default::default(); let value: u256 = 0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000; let size = 50; // When - let result = internal::get_last_input(ref to_hash, value, size); + let result = internal::fill_array_with_last_inputs(ref to_hash, value, size); // Then assert(result == 0x0000450000DEFA00, 'wrong result'); assert(to_hash.len() == 3, 'wrong result'); + assert((*to_hash[0]) == 0xE5000000FFFFFFFA, 'wrong array value'); + assert((*to_hash[1]) == 0xDE00000077000000, 'wrong array value'); + assert((*to_hash[2]) == 0x00200400000000AD, 'wrong array value'); } diff --git a/crates/utils/src/helpers.cairo b/crates/utils/src/helpers.cairo index df61cf931..9c22c9de6 100644 --- a/crates/utils/src/helpers.cairo +++ b/crates/utils/src/helpers.cairo @@ -104,23 +104,19 @@ fn split_word_128(value: u256, ref dst: Array) { split_word(value, 16, ref dst) } -/// Splits a u256 into 4 little endian u64. -/// Returns ยง(high_high, high_low),(low_high, low_low)) +/// Splits an u256 into 4 little endian u64. +/// Returns ((high_high, high_low),(low_high, low_low)) fn split_u256_into_u64_little(value: u256) -> ((u64, u64), (u64, u64)) { - let mut to_split = value; - to_split.low = integer::u128_byte_reverse(to_split.low); - to_split.high = integer::u128_byte_reverse(to_split.high); - - (u128_split(to_split.high), u128_split(to_split.low)) + let low_le = integer::u128_byte_reverse(value.low); + let high_le = integer::u128_byte_reverse(value.high); + (u128_split(high_le), u128_split(low_le)) } -/// Reverse the bytes of an u256 -fn u256_bytes_reverse(value: u256) -> u256 { - let mut reversed: u256 = 0; - reversed.high = integer::u128_byte_reverse(value.low); - reversed.low = integer::u128_byte_reverse(value.high); - - reversed +/// Reverse the endianness of an u256 +fn reverse_endianness(value: u256) -> u256 { + let new_low = integer::u128_byte_reverse(value.high); + let new_high = integer::u128_byte_reverse(value.low); + u256 { low: new_low, high: new_high } } diff --git a/crates/utils/src/tests/test_helpers.cairo b/crates/utils/src/tests/test_helpers.cairo index ff90fbcf3..15eee7a66 100644 --- a/crates/utils/src/tests/test_helpers.cairo +++ b/crates/utils/src/tests/test_helpers.cairo @@ -167,7 +167,7 @@ fn test_clone_pad_zeroes() { #[available_gas(2000000000)] fn test_reverse_bytes_u256() { let value: u256 = 0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000; - let res = helpers::u256_bytes_reverse(value); + let res = helpers::reverse_endianness(value); assert( res == 0x0000450000DEFA0000200400000000ADDE00000077000000E5000000FFFFFFFA, 'reverse mismatch' From 02f17401cb4e3cf167052056cc4ed77439e758d2 Mon Sep 17 00:00:00 2001 From: Quentash Date: Thu, 14 Sep 2023 17:34:04 +0200 Subject: [PATCH 15/24] bug correction due to merge --- crates/evm/src/instructions/sha3.cairo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/evm/src/instructions/sha3.cairo b/crates/evm/src/instructions/sha3.cairo index 3539ff0d8..a24bedb50 100644 --- a/crates/evm/src/instructions/sha3.cairo +++ b/crates/evm/src/instructions/sha3.cairo @@ -35,7 +35,7 @@ impl Sha3Impl of Sha3Trait { // Fill last_input with last bytes to hash let last_input: u64 = if (size % 32 != 0) { - let (loaded, _) = self.memory.load(offset); + let loaded = self.memory.load(offset); internal::fill_array_with_last_inputs(ref to_hash, loaded, size % 32) } else { 0 @@ -91,7 +91,7 @@ mod internal { if amount == 0 { break; } - let (loaded, _) = self.memory.load(offset); + let loaded = self.memory.load(offset); let ((high_h, low_h), (high_l, low_l)) = split_u256_into_u64_little(loaded); to_hash.append(low_h); to_hash.append(high_h); From dc95762f37b3335eda73163104a241f0f4086e2a Mon Sep 17 00:00:00 2001 From: Quentash Date: Thu, 14 Sep 2023 17:44:25 +0200 Subject: [PATCH 16/24] removed unnecessary computation --- crates/evm/src/instructions/sha3.cairo | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/evm/src/instructions/sha3.cairo b/crates/evm/src/instructions/sha3.cairo index a24bedb50..ca3f8895f 100644 --- a/crates/evm/src/instructions/sha3.cairo +++ b/crates/evm/src/instructions/sha3.cairo @@ -68,8 +68,7 @@ mod internal { return (0, size / 32); } if (mem_len - offset < 32) && (size > 32) { - let chunk_from_mem = (mem_len - offset) / 32; - return (chunk_from_mem + 1, (size / 32) - chunk_from_mem - 1); + return (1, (size / 32) - 1); } let chunk_from_mem = (cmp::min(mem_len - offset, size)) / 32; (chunk_from_mem, (size / 32) - chunk_from_mem) From d9f6bc62ae94d67bc23899355d56383b3908b381 Mon Sep 17 00:00:00 2001 From: Quentash Date: Thu, 14 Sep 2023 18:33:05 +0200 Subject: [PATCH 17/24] quick fix review --- crates/evm/src/instructions/sha3.cairo | 53 ++++++------------- .../tests/test_instructions/test_sha3.cairo | 41 ++++---------- 2 files changed, 27 insertions(+), 67 deletions(-) diff --git a/crates/evm/src/instructions/sha3.cairo b/crates/evm/src/instructions/sha3.cairo index ca3f8895f..d3cbbf1ad 100644 --- a/crates/evm/src/instructions/sha3.cairo +++ b/crates/evm/src/instructions/sha3.cairo @@ -7,7 +7,7 @@ use evm::memory::MemoryTrait; use evm::errors::EVMError; use evm::helpers::U256IntoResultU32; use keccak::{cairo_keccak, u128_split}; -use utils::helpers::{split_u256_into_u64_little, reverse_endianness}; +use utils::helpers::{ArrayExtensionTrait, reverse_endianness}; use array::ArrayTrait; @@ -27,16 +27,16 @@ impl Sha3Impl of Sha3Trait { let mut to_hash: Array = Default::default(); - let (chunks_from_mem, chunks_of_zeroes) = internal::compute_data_origin( + let (nb_chunks, nb_zeroes) = internal::compute_memory_chunks_amount( size, offset, self.memory.bytes_len ); - internal::fill_array_with_memory_chunks(ref self, ref to_hash, ref offset, chunks_from_mem); - internal::fill_array_with_zeroes(ref self, ref to_hash, chunks_of_zeroes); + offset = internal::fill_array_with_memory_chunks(ref self, ref to_hash, offset, nb_chunks); + to_hash.append_n(0, 4 * nb_zeroes); // Fill last_input with last bytes to hash let last_input: u64 = if (size % 32 != 0) { let loaded = self.memory.load(offset); - internal::fill_array_with_last_inputs(ref to_hash, loaded, size % 32) + internal::prepare_last_input(ref to_hash, loaded, size % 32) } else { 0 }; @@ -62,20 +62,21 @@ mod internal { /// * `size` - The amount of bytes to hash /// * `offset` - Offset in memory /// * `mem_len` - Size of the memory - /// Returns : (chunk_from_mem, chunks_of_zeroes) - fn compute_data_origin(size: u32, offset: u32, mem_len: u32) -> (u32, u32) { + /// Returns : (nb_chunks, nb_zeroes) + fn compute_memory_chunks_amount(size: u32, offset: u32, mem_len: u32) -> (u32, u32) { if offset > mem_len { return (0, size / 32); } if (mem_len - offset < 32) && (size > 32) { return (1, (size / 32) - 1); } - let chunk_from_mem = (cmp::min(mem_len - offset, size)) / 32; - (chunk_from_mem, (size / 32) - chunk_from_mem) + let nb_chunks = (cmp::min(mem_len - offset, size)) / 32; + (nb_chunks, (size / 32) - nb_chunks) } /// This function will fill an array with little endian u64 - /// by splitting 32 Bytes chunk read from the memory. + /// by splitting 32 Bytes chunk read from the memory and + /// returns the new offset. /// /// # Arguments /// @@ -83,9 +84,10 @@ mod internal { /// * `to_hash` - A reference to the array to fill /// * `offset` - Offset in memory /// * `amount` - The amount of 32 Bytes chunks to read from memory + /// Return the new offset fn fill_array_with_memory_chunks( - ref self: ExecutionContext, ref to_hash: Array, ref offset: u32, mut amount: u32 - ) { + ref self: ExecutionContext, ref to_hash: Array, mut offset: u32, mut amount: u32 + ) -> u32 { loop { if amount == 0 { break; @@ -100,30 +102,7 @@ mod internal { offset += 32; amount -= 1; }; - } - - /// This function will fill an u64 array with a given amount of - /// 32 Bytes chunks of zeroes. - /// - /// # Arguments - /// - /// * `self` - The context in which the memory is read - /// * `to_hash` - A reference to the array to fill - /// * `amount` - The amount of 32 Bytes chunks of zeroes to append to to hash - fn fill_array_with_zeroes( - ref self: ExecutionContext, ref to_hash: Array, mut amount: u32 - ) { - loop { - if amount == 0 { - break; - } - to_hash.append(0); - to_hash.append(0); - to_hash.append(0); - to_hash.append(0); - - amount -= 1; - }; + offset } /// This function will fill an array with the remaining little endian u64 @@ -136,7 +115,7 @@ mod internal { /// * `value` - The 32 Bytes chunk to split and get the bytes from /// * `size` - The amount of bytes still required to hash /// Returns the last u64 chunk that isn't 8 Bytes long. - fn fill_array_with_last_inputs(ref to_hash: Array, value: u256, size: u32) -> u64 { + fn prepare_last_input(ref to_hash: Array, value: u256, size: u32) -> u64 { let ((high_h, low_h), (high_l, low_l)) = split_u256_into_u64_little(value); if size < 8 { return low_h; diff --git a/crates/evm/src/tests/test_instructions/test_sha3.cairo b/crates/evm/src/tests/test_instructions/test_sha3.cairo index 08bf1730b..ca32b67ab 100644 --- a/crates/evm/src/tests/test_instructions/test_sha3.cairo +++ b/crates/evm/src/tests/test_instructions/test_sha3.cairo @@ -284,8 +284,10 @@ fn test_sha3_internal_fill_array_with_memory_chunks() { let mut offset = 0; // When - let (chunks_from_mem, _) = internal::compute_data_origin(size, offset, ctx.memory.bytes_len); - internal::fill_array_with_memory_chunks(ref ctx, ref to_hash, ref offset, chunks_from_mem); + let (chunks_from_mem, _) = internal::compute_memory_chunks_amount( + size, offset, ctx.memory.bytes_len + ); + internal::fill_array_with_memory_chunks(ref ctx, ref to_hash, offset, chunks_from_mem); // Then assert(to_hash.len() == 4, 'wrong array length'); @@ -307,8 +309,10 @@ fn test_sha3_internal_fill_array_with_memory_chunks_size_33() { let mut offset = 0; // When - let (chunks_from_mem, _) = internal::compute_data_origin(size, offset, ctx.memory.bytes_len); - internal::fill_array_with_memory_chunks(ref ctx, ref to_hash, ref offset, chunks_from_mem); + let (chunks_from_mem, _) = internal::compute_memory_chunks_amount( + size, offset, ctx.memory.bytes_len + ); + internal::fill_array_with_memory_chunks(ref ctx, ref to_hash, offset, chunks_from_mem); // Then assert(to_hash.len() == 4, 'wrong array length'); @@ -318,29 +322,6 @@ fn test_sha3_internal_fill_array_with_memory_chunks_size_33() { assert((*to_hash[3]) == 0x0000450000DEFA00, 'wrong array value'); } -#[test] -#[available_gas(20000000000)] -fn test_sha3_internal_fill_array_with_zeroes() { - // Given - let mut ctx = setup_execution_context(); - let mut to_hash: Array = Default::default(); - - ctx.memory.store(0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000, 0); - let mut size = 33; - let mut offset = 50; - - // When - let (_, chunks_of_zeroes) = internal::compute_data_origin(size, offset, ctx.memory.bytes_len); - internal::fill_array_with_zeroes(ref ctx, ref to_hash, chunks_of_zeroes); - - // Then - assert(to_hash.len() == 4, 'wrong array length'); - assert((*to_hash[0]) == 0x0, 'wrong array value'); - assert((*to_hash[1]) == 0x0, 'wrong array value'); - assert((*to_hash[2]) == 0x0, 'wrong array value'); - assert((*to_hash[3]) == 0x0, 'wrong array value'); -} - #[test] #[available_gas(20000000000)] fn test_sha3_internal_fill_array_with_last_inputs_size_5() { @@ -350,7 +331,7 @@ fn test_sha3_internal_fill_array_with_last_inputs_size_5() { let size = 5; // When - let result = internal::fill_array_with_last_inputs(ref to_hash, value, size); + let result = internal::prepare_last_input(ref to_hash, value, size); // Then assert(result == 0xE5000000FFFFFFFA, 'wrong result'); @@ -366,7 +347,7 @@ fn test_sha3_internal_fill_array_with_last_inputs_size_20() { let size = 20; // When - let result = internal::fill_array_with_last_inputs(ref to_hash, value, size); + let result = internal::prepare_last_input(ref to_hash, value, size); // Then assert(result == 0x00200400000000AD, 'wrong result'); @@ -384,7 +365,7 @@ fn test_sha3_internal_fill_array_with_last_inputs_size_50() { let size = 50; // When - let result = internal::fill_array_with_last_inputs(ref to_hash, value, size); + let result = internal::prepare_last_input(ref to_hash, value, size); // Then assert(result == 0x0000450000DEFA00, 'wrong result'); From 04bd9a17adcc8260504acbfc336faed7849de0f9 Mon Sep 17 00:00:00 2001 From: Quentash Date: Fri, 15 Sep 2023 10:51:16 +0200 Subject: [PATCH 18/24] corrected tests names --- .../tests/test_instructions/test_sha3.cairo | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/crates/evm/src/tests/test_instructions/test_sha3.cairo b/crates/evm/src/tests/test_instructions/test_sha3.cairo index ca32b67ab..56a3bf8ff 100644 --- a/crates/evm/src/tests/test_instructions/test_sha3.cairo +++ b/crates/evm/src/tests/test_instructions/test_sha3.cairo @@ -11,7 +11,7 @@ use debug::PrintTrait; #[test] #[available_gas(20000000)] -fn test_sha3_size_0_offset_0() { +fn test_exec_sha3_size_0_offset_0() { // Given let mut ctx = setup_execution_context(); @@ -34,7 +34,7 @@ fn test_sha3_size_0_offset_0() { #[test] #[available_gas(20000000)] -fn test_sha3_size_5_offset_4() { +fn test_exec_sha3_size_5_offset_4() { // Given let mut ctx = setup_execution_context(); @@ -55,7 +55,7 @@ fn test_sha3_size_5_offset_4() { #[test] #[available_gas(20000000)] -fn test_sha3_size_10_offset_10() { +fn test_exec_sha3_size_10_offset_10() { // Given let mut ctx = setup_execution_context(); @@ -76,7 +76,7 @@ fn test_sha3_size_10_offset_10() { #[test] #[available_gas(1000000000000000)] -fn test_sha3_size_0xFFFFF_offset_1000() { +fn test_exec_sha3_size_0xFFFFF_offset_1000() { // Given let mut ctx = setup_execution_context(); @@ -97,7 +97,7 @@ fn test_sha3_size_0xFFFFF_offset_1000() { #[test] #[available_gas(1000000000000000)] -fn test_sha3_size_1000000_offset_2() { +fn test_exec_sha3_size_1000000_offset_2() { // Given let mut ctx = setup_execution_context(); @@ -118,7 +118,7 @@ fn test_sha3_size_1000000_offset_2() { #[test] #[available_gas(20000000)] -fn test_sha3_size_1_offset_2048() { +fn test_exec_sha3_size_1_offset_2048() { // Given let mut ctx = setup_execution_context(); @@ -139,7 +139,7 @@ fn test_sha3_size_1_offset_2048() { #[test] #[available_gas(20000000)] -fn test_sha3_size_0_offset_1024() { +fn test_exec_sha3_size_0_offset_1024() { // Given let mut ctx = setup_execution_context(); @@ -160,7 +160,7 @@ fn test_sha3_size_0_offset_1024() { #[test] #[available_gas(20000000)] -fn test_sha3_size_32_offset_2016() { +fn test_exec_sha3_size_32_offset_2016() { // Given let mut ctx = setup_execution_context(); @@ -181,7 +181,7 @@ fn test_sha3_size_32_offset_2016() { #[test] #[available_gas(20000000)] -fn test_sha3_size_32_offset_0() { +fn test_exec_sha3_size_32_offset_0() { // Given let mut ctx = setup_execution_context(); @@ -202,7 +202,7 @@ fn test_sha3_size_32_offset_0() { #[test] #[available_gas(20000000)] -fn test_sha3_size_31_offset_0() { +fn test_exec_sha3_size_31_offset_0() { // Given let mut ctx = setup_execution_context(); @@ -223,7 +223,7 @@ fn test_sha3_size_31_offset_0() { #[test] #[available_gas(20000000)] -fn test_sha3_size_33_offset_0() { +fn test_exec_sha3_size_33_offset_0() { // Given let mut ctx = setup_execution_context(); @@ -244,22 +244,22 @@ fn test_sha3_size_33_offset_0() { #[test] #[available_gas(20000000000)] -fn test_sha3_size_0x0C80_offset_0() { +fn test_exec_sha3_size_0x0C80_offset_0() { // Given let mut ctx = setup_execution_context(); ctx.stack.push(0x0C80); ctx.stack.push(0x00); - let mut memDst: u32 = 0; + let mut mem_dst: u32 = 0; loop { - if memDst > 0x0C80 { + if mem_dst > 0x0C80 { break; } ctx .memory - .store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, memDst); - memDst += 0x20; + .store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, mem_dst); + mem_dst += 0x20; }; // When @@ -274,7 +274,7 @@ fn test_sha3_size_0x0C80_offset_0() { #[test] #[available_gas(20000000000)] -fn test_sha3_internal_fill_array_with_memory_chunks() { +fn test_internal_fill_array_with_memory_chunks() { // Given let mut ctx = setup_execution_context(); let mut to_hash: Array = Default::default(); @@ -299,7 +299,7 @@ fn test_sha3_internal_fill_array_with_memory_chunks() { #[test] #[available_gas(20000000000)] -fn test_sha3_internal_fill_array_with_memory_chunks_size_33() { +fn test_internal_fill_array_with_memory_chunks_size_33() { // Given let mut ctx = setup_execution_context(); let mut to_hash: Array = Default::default(); @@ -324,7 +324,7 @@ fn test_sha3_internal_fill_array_with_memory_chunks_size_33() { #[test] #[available_gas(20000000000)] -fn test_sha3_internal_fill_array_with_last_inputs_size_5() { +fn test_internal_fill_array_with_last_inputs_size_5() { // Given let mut to_hash: Array = Default::default(); let value: u256 = 0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000; @@ -340,7 +340,7 @@ fn test_sha3_internal_fill_array_with_last_inputs_size_5() { #[test] #[available_gas(20000000000)] -fn test_sha3_internal_fill_array_with_last_inputs_size_20() { +fn test_internal_fill_array_with_last_inputs_size_20() { // Given let mut to_hash: Array = Default::default(); let value: u256 = 0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000; @@ -358,7 +358,7 @@ fn test_sha3_internal_fill_array_with_last_inputs_size_20() { #[test] #[available_gas(20000000000)] -fn test_sha3_internal_fill_array_with_last_inputs_size_50() { +fn test_internal_fill_array_with_last_inputs_size_50() { // Given let mut to_hash: Array = Default::default(); let value: u256 = 0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000; From e321a0aa0d6f31bfaab8d7d44f02398c5bbf14d0 Mon Sep 17 00:00:00 2001 From: msaug Date: Mon, 18 Sep 2023 20:25:41 +0700 Subject: [PATCH 19/24] docs: added fn docs --- crates/evm/src/instructions/sha3.cairo | 40 ++++++++++++++------------ 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/crates/evm/src/instructions/sha3.cairo b/crates/evm/src/instructions/sha3.cairo index d3cbbf1ad..d75db008e 100644 --- a/crates/evm/src/instructions/sha3.cairo +++ b/crates/evm/src/instructions/sha3.cairo @@ -15,27 +15,29 @@ use array::ArrayTrait; impl Sha3Impl of Sha3Trait { /// SHA3 operation : Hashes n bytes in memory at a given offset in memory /// and push the hash result to the stack. - /// + /// /// # Inputs /// * `offset` - The offset in memory where to read the datas /// * `size` - The amount of bytes to read - /// + /// /// # Specification: https://www.evm.codes/#20?fork=shanghai fn exec_sha3(ref self: ExecutionContext) -> Result<(), EVMError> { - let mut offset: u32 = Into::>::into((self.stack.pop()?))?; - let mut size: u32 = Into::>::into((self.stack.pop()?))?; + let offset: usize = self.stack.pop_usize()?; + let mut size: usize = self.stack.pop_usize()?; let mut to_hash: Array = Default::default(); let (nb_chunks, nb_zeroes) = internal::compute_memory_chunks_amount( size, offset, self.memory.bytes_len ); - offset = internal::fill_array_with_memory_chunks(ref self, ref to_hash, offset, nb_chunks); + let mut last_input_offset = internal::fill_array_with_memory_chunks( + ref self, ref to_hash, offset, nb_chunks + ); to_hash.append_n(0, 4 * nb_zeroes); // Fill last_input with last bytes to hash let last_input: u64 = if (size % 32 != 0) { - let loaded = self.memory.load(offset); + let loaded = self.memory.load(last_input_offset); internal::prepare_last_input(ref to_hash, loaded, size % 32) } else { 0 @@ -53,9 +55,9 @@ mod internal { use evm::memory::MemoryTrait; use utils::helpers::split_u256_into_u64_little; - /// This function will compute how many chunks of 32 Bytes data are read - /// from the memory and how many chunks of 32 Bytes data must be filled - /// with zeroes. + /// Computes how many chunks of 32 bytes data are read + /// from the memory and how many chunks of 32 bytes data must be filled + /// with zeroes given a target size, an memory offset and the length of the memory. /// /// # Arguments /// @@ -70,20 +72,20 @@ mod internal { if (mem_len - offset < 32) && (size > 32) { return (1, (size / 32) - 1); } - let nb_chunks = (cmp::min(mem_len - offset, size)) / 32; + let nb_chunks = cmp::min(mem_len - offset, size) / 32; (nb_chunks, (size / 32) - nb_chunks) } - /// This function will fill an array with little endian u64 - /// by splitting 32 Bytes chunk read from the memory and - /// returns the new offset. + /// Fills the `to_hash` array with little endian u64s + /// by splitting 32 bytes chunk read from the memory and + /// returns the next offset to read from. /// /// # Arguments /// /// * `self` - The context in which the memory is read /// * `to_hash` - A reference to the array to fill - /// * `offset` - Offset in memory - /// * `amount` - The amount of 32 Bytes chunks to read from memory + /// * `offset` - Offset in memory to start reading from + /// * `amount` - The amount of 32 bytes chunks to read from memory /// Return the new offset fn fill_array_with_memory_chunks( ref self: ExecutionContext, ref to_hash: Array, mut offset: u32, mut amount: u32 @@ -105,14 +107,14 @@ mod internal { offset } - /// This function will fill an array with the remaining little endian u64 - /// depending on size from a 32 Bytes chunk of data and return - /// the u64 chunk containing the last bytes that aren't 8 Bytes long. + /// Fills the `to_hash` array with the n-1 remaining little endian u64 + /// depending on size from a 32 bytes chunk of data and returns + /// the u64 chunk containing the last 64 bytes word to hash. /// /// # Arguments /// /// * `to_hash` - A reference to the array to fill - /// * `value` - The 32 Bytes chunk to split and get the bytes from + /// * `value` - The 32 bytes chunk to split in u64 words /// * `size` - The amount of bytes still required to hash /// Returns the last u64 chunk that isn't 8 Bytes long. fn prepare_last_input(ref to_hash: Array, value: u256, size: u32) -> u64 { From cd224ffe66a775959b66a335d18b383dfc4e00a5 Mon Sep 17 00:00:00 2001 From: Quentash Date: Wed, 20 Sep 2023 13:50:50 +0200 Subject: [PATCH 20/24] made compute_words more verbose --- crates/evm/src/instructions/sha3.cairo | 44 +++++++++++-------- .../tests/test_instructions/test_sha3.cairo | 34 +++++++++++--- 2 files changed, 54 insertions(+), 24 deletions(-) diff --git a/crates/evm/src/instructions/sha3.cairo b/crates/evm/src/instructions/sha3.cairo index d75db008e..66e12504c 100644 --- a/crates/evm/src/instructions/sha3.cairo +++ b/crates/evm/src/instructions/sha3.cairo @@ -27,11 +27,11 @@ impl Sha3Impl of Sha3Trait { let mut to_hash: Array = Default::default(); - let (nb_chunks, nb_zeroes) = internal::compute_memory_chunks_amount( + let (nb_words, nb_zeroes) = internal::compute_memory_words_amount( size, offset, self.memory.bytes_len ); - let mut last_input_offset = internal::fill_array_with_memory_chunks( - ref self, ref to_hash, offset, nb_chunks + let mut last_input_offset = internal::fill_array_with_memory_words( + ref self, ref to_hash, offset, nb_words ); to_hash.append_n(0, 4 * nb_zeroes); @@ -55,29 +55,37 @@ mod internal { use evm::memory::MemoryTrait; use utils::helpers::split_u256_into_u64_little; - /// Computes how many chunks of 32 bytes data are read - /// from the memory and how many chunks of 32 bytes data must be filled - /// with zeroes given a target size, an memory offset and the length of the memory. + /// Computes how many words are read from the memory + /// and how many words must be filled with zeroes + /// given a target size, a memory offset and the length of the memory. /// /// # Arguments /// /// * `size` - The amount of bytes to hash /// * `offset` - Offset in memory /// * `mem_len` - Size of the memory - /// Returns : (nb_chunks, nb_zeroes) - fn compute_memory_chunks_amount(size: u32, offset: u32, mem_len: u32) -> (u32, u32) { + /// Returns : (nb_words, nb_zeroes) + fn compute_memory_words_amount(size: u32, offset: u32, mem_len: u32) -> (u32, u32) { + // Bytes to hash are less than a word size + if size < 32 { + return (0, 0); + } + // Bytes out of memory bound are zeroes if offset > mem_len { return (0, size / 32); } - if (mem_len - offset < 32) && (size > 32) { + // The only word to read from memory is less than 32 bytes + if mem_len - offset < 32 { return (1, (size / 32) - 1); } - let nb_chunks = cmp::min(mem_len - offset, size) / 32; - (nb_chunks, (size / 32) - nb_chunks) + + let bytes_to_read = cmp::min(mem_len - offset, size); + let nb_words = bytes_to_read / 32; + (nb_words, (size / 32) - nb_words) } /// Fills the `to_hash` array with little endian u64s - /// by splitting 32 bytes chunk read from the memory and + /// by splitting words read from the memory and /// returns the next offset to read from. /// /// # Arguments @@ -85,9 +93,9 @@ mod internal { /// * `self` - The context in which the memory is read /// * `to_hash` - A reference to the array to fill /// * `offset` - Offset in memory to start reading from - /// * `amount` - The amount of 32 bytes chunks to read from memory + /// * `amount` - The amount of words to read from memory /// Return the new offset - fn fill_array_with_memory_chunks( + fn fill_array_with_memory_words( ref self: ExecutionContext, ref to_hash: Array, mut offset: u32, mut amount: u32 ) -> u32 { loop { @@ -108,15 +116,15 @@ mod internal { } /// Fills the `to_hash` array with the n-1 remaining little endian u64 - /// depending on size from a 32 bytes chunk of data and returns - /// the u64 chunk containing the last 64 bytes word to hash. + /// depending on size from a word and returns + /// the u64 containing the last 8 bytes word to hash. /// /// # Arguments /// /// * `to_hash` - A reference to the array to fill - /// * `value` - The 32 bytes chunk to split in u64 words + /// * `value` - The word to split in u64 words /// * `size` - The amount of bytes still required to hash - /// Returns the last u64 chunk that isn't 8 Bytes long. + /// Returns the last u64 word that isn't 8 Bytes long. fn prepare_last_input(ref to_hash: Array, value: u256, size: u32) -> u64 { let ((high_h, low_h), (high_l, low_l)) = split_u256_into_u64_little(value); if size < 8 { diff --git a/crates/evm/src/tests/test_instructions/test_sha3.cairo b/crates/evm/src/tests/test_instructions/test_sha3.cairo index 56a3bf8ff..19b0291e0 100644 --- a/crates/evm/src/tests/test_instructions/test_sha3.cairo +++ b/crates/evm/src/tests/test_instructions/test_sha3.cairo @@ -116,6 +116,28 @@ fn test_exec_sha3_size_1000000_offset_2() { ); } +#[test] +#[available_gas(1000000000000000)] +fn test_exec_sha3_size_1000000_offset_23() { + // Given + let mut ctx = setup_execution_context(); + + ctx.stack.push(1000000); + ctx.stack.push(2); + + ctx.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); + ctx.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); + + // When + ctx.exec_sha3(); + + // Then + let result = ctx.stack.peek().unwrap(); + assert( + result == 0x4aa461ae9513f3b03ae397740ade979809dd02ae2c14e101b32842fbee21f0a, 'wrong result' + ); +} + #[test] #[available_gas(20000000)] fn test_exec_sha3_size_1_offset_2048() { @@ -274,7 +296,7 @@ fn test_exec_sha3_size_0x0C80_offset_0() { #[test] #[available_gas(20000000000)] -fn test_internal_fill_array_with_memory_chunks() { +fn test_internal_fill_array_with_memory_words() { // Given let mut ctx = setup_execution_context(); let mut to_hash: Array = Default::default(); @@ -284,10 +306,10 @@ fn test_internal_fill_array_with_memory_chunks() { let mut offset = 0; // When - let (chunks_from_mem, _) = internal::compute_memory_chunks_amount( + let (words_from_mem, _) = internal::compute_memory_words_amount( size, offset, ctx.memory.bytes_len ); - internal::fill_array_with_memory_chunks(ref ctx, ref to_hash, offset, chunks_from_mem); + internal::fill_array_with_memory_words(ref ctx, ref to_hash, offset, words_from_mem); // Then assert(to_hash.len() == 4, 'wrong array length'); @@ -299,7 +321,7 @@ fn test_internal_fill_array_with_memory_chunks() { #[test] #[available_gas(20000000000)] -fn test_internal_fill_array_with_memory_chunks_size_33() { +fn test_internal_fill_array_with_memory_words_size_33() { // Given let mut ctx = setup_execution_context(); let mut to_hash: Array = Default::default(); @@ -309,10 +331,10 @@ fn test_internal_fill_array_with_memory_chunks_size_33() { let mut offset = 0; // When - let (chunks_from_mem, _) = internal::compute_memory_chunks_amount( + let (words_from_mem, _) = internal::compute_memory_words_amount( size, offset, ctx.memory.bytes_len ); - internal::fill_array_with_memory_chunks(ref ctx, ref to_hash, offset, chunks_from_mem); + internal::fill_array_with_memory_words(ref ctx, ref to_hash, offset, words_from_mem); // Then assert(to_hash.len() == 4, 'wrong array length'); From b229e6357fc8132f76e225fc936a2fd707a1863d Mon Sep 17 00:00:00 2001 From: Quentash Date: Thu, 21 Sep 2023 10:51:26 +0200 Subject: [PATCH 21/24] Made helper fcts on u256trait --- crates/evm/src/instructions/sha3.cairo | 10 +++---- crates/utils/src/helpers.cairo | 33 ++++++++++++----------- crates/utils/src/tests/test_helpers.cairo | 8 +++--- 3 files changed, 28 insertions(+), 23 deletions(-) diff --git a/crates/evm/src/instructions/sha3.cairo b/crates/evm/src/instructions/sha3.cairo index 66e12504c..39250ad7d 100644 --- a/crates/evm/src/instructions/sha3.cairo +++ b/crates/evm/src/instructions/sha3.cairo @@ -7,7 +7,7 @@ use evm::memory::MemoryTrait; use evm::errors::EVMError; use evm::helpers::U256IntoResultU32; use keccak::{cairo_keccak, u128_split}; -use utils::helpers::{ArrayExtensionTrait, reverse_endianness}; +use utils::helpers::{ArrayExtensionTrait, U256Trait}; use array::ArrayTrait; @@ -44,7 +44,7 @@ impl Sha3Impl of Sha3Trait { }; let mut hash = cairo_keccak(ref to_hash, last_input, size % 8); - self.stack.push(reverse_endianness(hash)) + self.stack.push(hash.reverse_endianness()) } } @@ -53,7 +53,7 @@ mod internal { use evm::context::{ExecutionContext, ExecutionContextTrait, BoxDynamicExecutionContextDestruct}; use evm::stack::StackTrait; use evm::memory::MemoryTrait; - use utils::helpers::split_u256_into_u64_little; + use utils::helpers::U256Trait; /// Computes how many words are read from the memory /// and how many words must be filled with zeroes @@ -103,7 +103,7 @@ mod internal { break; } let loaded = self.memory.load(offset); - let ((high_h, low_h), (high_l, low_l)) = split_u256_into_u64_little(loaded); + let ((high_h, low_h), (high_l, low_l)) = loaded.split_into_u64_le(); to_hash.append(low_h); to_hash.append(high_h); to_hash.append(low_l); @@ -126,7 +126,7 @@ mod internal { /// * `size` - The amount of bytes still required to hash /// Returns the last u64 word that isn't 8 Bytes long. fn prepare_last_input(ref to_hash: Array, value: u256, size: u32) -> u64 { - let ((high_h, low_h), (high_l, low_l)) = split_u256_into_u64_little(value); + let ((high_h, low_h), (high_l, low_l)) = value.split_into_u64_le(); if size < 8 { return low_h; } else if size < 16 { diff --git a/crates/utils/src/helpers.cairo b/crates/utils/src/helpers.cairo index ba872a958..172d0a415 100644 --- a/crates/utils/src/helpers.cairo +++ b/crates/utils/src/helpers.cairo @@ -104,21 +104,6 @@ fn split_word_128(value: u256, ref dst: Array) { split_word(value, 16, ref dst) } -/// Splits an u256 into 4 little endian u64. -/// Returns ((high_high, high_low),(low_high, low_low)) -fn split_u256_into_u64_little(value: u256) -> ((u64, u64), (u64, u64)) { - let low_le = integer::u128_byte_reverse(value.low); - let high_le = integer::u128_byte_reverse(value.high); - (u128_split(high_le), u128_split(low_le)) -} - -/// Reverse the endianness of an u256 -fn reverse_endianness(value: u256) -> u256 { - let new_low = integer::u128_byte_reverse(value.high); - let new_high = integer::u128_byte_reverse(value.low); - u256 { low: new_low, high: new_high } -} - /// Loads a sequence of bytes into a single u256 in big-endian /// @@ -274,3 +259,21 @@ impl SpanExtension of SpanExtensionTrait { } } + +#[generate_trait] +impl U256Impl of U256Trait { + /// Splits an u256 into 4 little endian u64. + /// Returns ((high_high, high_low),(low_high, low_low)) + fn split_into_u64_le(self: u256) -> ((u64, u64), (u64, u64)) { + let low_le = integer::u128_byte_reverse(self.low); + let high_le = integer::u128_byte_reverse(self.high); + (u128_split(high_le), u128_split(low_le)) + } + + /// Reverse the endianness of an u256 + fn reverse_endianness(self: u256) -> u256 { + let new_low = integer::u128_byte_reverse(self.high); + let new_high = integer::u128_byte_reverse(self.low); + u256 { low: new_low, high: new_high } + } +} diff --git a/crates/utils/src/tests/test_helpers.cairo b/crates/utils/src/tests/test_helpers.cairo index 6c5378e8b..9fafa31b6 100644 --- a/crates/utils/src/tests/test_helpers.cairo +++ b/crates/utils/src/tests/test_helpers.cairo @@ -1,5 +1,7 @@ use utils::helpers; -use utils::helpers::{SpanExtension, SpanExtensionTrait, ArrayExtension, ArrayExtensionTrait}; +use utils::helpers::{ + SpanExtension, SpanExtensionTrait, ArrayExtension, ArrayExtensionTrait, U256Trait +}; use debug::PrintTrait; #[test] @@ -185,7 +187,7 @@ fn test_append_n() { #[available_gas(2000000000)] fn test_reverse_bytes_u256() { let value: u256 = 0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000; - let res = helpers::reverse_endianness(value); + let res = value.reverse_endianness(); assert( res == 0x0000450000DEFA0000200400000000ADDE00000077000000E5000000FFFFFFFA, 'reverse mismatch' @@ -196,7 +198,7 @@ fn test_reverse_bytes_u256() { #[available_gas(2000000000)] fn test_split_u256_into_u64_little() { let value: u256 = 0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000; - let ((high_h, low_h), (high_l, low_l)) = helpers::split_u256_into_u64_little(value); + let ((high_h, low_h), (high_l, low_l)) = value.split_into_u64_le(); assert(high_h == 0xDE00000077000000, 'split mismatch'); assert(low_h == 0xE5000000FFFFFFFA, 'split mismatch'); assert(high_l == 0x0000450000DEFA00, 'split mismatch'); From 45f4b185111a39664a7d185207ceb9ab9d94c5fa Mon Sep 17 00:00:00 2001 From: Quentash Date: Thu, 21 Sep 2023 13:51:36 +0200 Subject: [PATCH 22/24] modified comments --- crates/evm/src/instructions/sha3.cairo | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/evm/src/instructions/sha3.cairo b/crates/evm/src/instructions/sha3.cairo index 39250ad7d..ad1eca890 100644 --- a/crates/evm/src/instructions/sha3.cairo +++ b/crates/evm/src/instructions/sha3.cairo @@ -33,9 +33,13 @@ impl Sha3Impl of Sha3Trait { let mut last_input_offset = internal::fill_array_with_memory_words( ref self, ref to_hash, offset, nb_words ); + // Fill array to hash with zeroes for bytes out of memory bound + // which is faster than reading them from memory to_hash.append_n(0, 4 * nb_zeroes); - // Fill last_input with last bytes to hash + // For cases where the size of bytes to hash isn't a multiple of 8, + // prepare the last bytes to hash into last_input instead of appending + // it to to_hash. let last_input: u64 = if (size % 32 != 0) { let loaded = self.memory.load(last_input_offset); internal::prepare_last_input(ref to_hash, loaded, size % 32) From cfdf5b4379aec308cc712bbf39c2d2828dedbc34 Mon Sep 17 00:00:00 2001 From: Quentash Date: Fri, 22 Sep 2023 10:57:51 +0200 Subject: [PATCH 23/24] Ensured memory lenght --- crates/evm/src/instructions/sha3.cairo | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/evm/src/instructions/sha3.cairo b/crates/evm/src/instructions/sha3.cairo index ad1eca890..5bd79d370 100644 --- a/crates/evm/src/instructions/sha3.cairo +++ b/crates/evm/src/instructions/sha3.cairo @@ -44,6 +44,8 @@ impl Sha3Impl of Sha3Trait { let loaded = self.memory.load(last_input_offset); internal::prepare_last_input(ref to_hash, loaded, size % 32) } else { + // Properly set the memory length in case we skipped reading zeroes + self.memory.ensure_length(size); 0 }; From d323bf9799e3ee3d26918d7e65195e1f348921ec Mon Sep 17 00:00:00 2001 From: Quentash Date: Fri, 22 Sep 2023 11:58:35 +0200 Subject: [PATCH 24/24] added memory size verification to tests --- crates/evm/src/instructions/sha3.cairo | 4 ++-- .../src/tests/test_instructions/test_sha3.cairo | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/crates/evm/src/instructions/sha3.cairo b/crates/evm/src/instructions/sha3.cairo index 5bd79d370..5302368d5 100644 --- a/crates/evm/src/instructions/sha3.cairo +++ b/crates/evm/src/instructions/sha3.cairo @@ -44,10 +44,10 @@ impl Sha3Impl of Sha3Trait { let loaded = self.memory.load(last_input_offset); internal::prepare_last_input(ref to_hash, loaded, size % 32) } else { - // Properly set the memory length in case we skipped reading zeroes - self.memory.ensure_length(size); 0 }; + // Properly set the memory length in case we skipped reading zeroes + self.memory.ensure_length(size + offset); let mut hash = cairo_keccak(ref to_hash, last_input, size % 8); self.stack.push(hash.reverse_endianness()) diff --git a/crates/evm/src/tests/test_instructions/test_sha3.cairo b/crates/evm/src/tests/test_instructions/test_sha3.cairo index 19b0291e0..044ad9031 100644 --- a/crates/evm/src/tests/test_instructions/test_sha3.cairo +++ b/crates/evm/src/tests/test_instructions/test_sha3.cairo @@ -7,8 +7,6 @@ use evm::stack::StackTrait; use option::OptionTrait; use evm::errors::{EVMError, TYPE_CONVERSION_ERROR}; -use debug::PrintTrait; - #[test] #[available_gas(20000000)] fn test_exec_sha3_size_0_offset_0() { @@ -29,6 +27,7 @@ fn test_exec_sha3_size_0_offset_0() { assert( result == 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470, 'wrong result' ); + assert(ctx.memory.bytes_len == 32, 'wrong memory size'); } @@ -51,6 +50,7 @@ fn test_exec_sha3_size_5_offset_4() { assert( result == 0xc41589e7559804ea4a2080dad19d876a024ccb05117835447d72ce08c1d020ec, 'wrong result' ); + assert(ctx.memory.bytes_len == 64, 'wrong memory size'); } #[test] @@ -72,6 +72,7 @@ fn test_exec_sha3_size_10_offset_10() { assert( result == 0x6bd2dd6bd408cbee33429358bf24fdc64612fbf8b1b4db604518f40ffd34b607, 'wrong result' ); + assert(ctx.memory.bytes_len == 64, 'wrong memory size'); } #[test] @@ -93,6 +94,7 @@ fn test_exec_sha3_size_0xFFFFF_offset_1000() { assert( result == 0xbe6f1b42b34644f918560a07f959d23e532dea5338e4b9f63db0caeb608018fa, 'wrong result' ); + assert(ctx.memory.bytes_len == (((0xFFFFF + 1000) + 31) / 32) * 32, 'wrong memory size'); } #[test] @@ -114,6 +116,7 @@ fn test_exec_sha3_size_1000000_offset_2() { assert( result == 0x4aa461ae9513f3b03ae397740ade979809dd02ae2c14e101b32842fbee21f0a, 'wrong result' ); + assert(ctx.memory.bytes_len == (((1000000 + 2) + 31) / 32) * 32, 'wrong memory size'); } #[test] @@ -136,6 +139,7 @@ fn test_exec_sha3_size_1000000_offset_23() { assert( result == 0x4aa461ae9513f3b03ae397740ade979809dd02ae2c14e101b32842fbee21f0a, 'wrong result' ); + assert(ctx.memory.bytes_len == (((1000000 + 23) + 31) / 32) * 32, 'wrong memory size'); } #[test] @@ -157,6 +161,7 @@ fn test_exec_sha3_size_1_offset_2048() { assert( result == 0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a, 'wrong result' ); + assert(ctx.memory.bytes_len == (((2048 + 1) + 31) / 32) * 32, 'wrong memory size'); } #[test] @@ -178,6 +183,7 @@ fn test_exec_sha3_size_0_offset_1024() { assert( result == 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470, 'wrong result' ); + assert(ctx.memory.bytes_len == 1024, 'wrong memory size'); } #[test] @@ -199,6 +205,7 @@ fn test_exec_sha3_size_32_offset_2016() { assert( result == 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563, 'wrong result' ); + assert(ctx.memory.bytes_len == (((2016 + 32) + 31) / 32) * 32, 'wrong memory size'); } #[test] @@ -220,6 +227,7 @@ fn test_exec_sha3_size_32_offset_0() { assert( result == 0x567d6b045256961aee949d6bb4d5f814c5b42e6b8bb49a833e8e89fbcddee86c, 'wrong result' ); + assert(ctx.memory.bytes_len == 32, 'wrong memory size'); } #[test] @@ -241,6 +249,7 @@ fn test_exec_sha3_size_31_offset_0() { assert( result == 0x4b13f212816c02cc818ba4802e81a4ac1904d2c920fe8d8cf3e4f05233a57d2e, 'wrong result' ); + assert(ctx.memory.bytes_len == 32, 'wrong memory size'); } #[test] @@ -262,6 +271,7 @@ fn test_exec_sha3_size_33_offset_0() { assert( result == 0xa6fa3edfabbe64b6ce26120b21ac9b8191005115d5e7e03fa58ec9cc74c0f2f4, 'wrong result' ); + assert(ctx.memory.bytes_len == 64, 'wrong memory size'); } #[test] @@ -292,6 +302,7 @@ fn test_exec_sha3_size_0x0C80_offset_0() { assert( result == 0x2022ae07f3a362b08ac0a4bcb785c830cb5c368dc0ce6972249c6abbc68a5291, 'wrong result' ); + assert(ctx.memory.bytes_len == 0x0C80 + 32, 'wrong memory size'); } #[test]