From 7c289ed872597e1d3176d90ec2aeef87c53aef82 Mon Sep 17 00:00:00 2001 From: Shashank Trivedi <100513286+lordshashank@users.noreply.github.com> Date: Mon, 30 Sep 2024 15:38:07 +0530 Subject: [PATCH] Optimizes conversion of bytes to words (#985) * ceil to bytes32 * overflow and nits --- crates/evm/src/create_helpers.cairo | 4 +-- crates/evm/src/gas.cairo | 4 +-- .../environmental_information.cairo | 11 +++---- .../src/instructions/memory_operations.cairo | 4 +-- crates/evm/src/instructions/sha3.cairo | 4 +-- crates/utils/src/helpers.cairo | 31 +++++++++---------- 6 files changed, 28 insertions(+), 30 deletions(-) diff --git a/crates/evm/src/create_helpers.cairo b/crates/evm/src/create_helpers.cairo index e324e17b7..cdf91ed26 100644 --- a/crates/evm/src/create_helpers.cairo +++ b/crates/evm/src/create_helpers.cairo @@ -13,7 +13,7 @@ use crate::stack::StackTrait; use crate::state::StateTrait; use utils::address::{compute_contract_address, compute_create2_contract_address}; use utils::constants; -use utils::helpers::ceil32; +use utils::helpers::bytes_32_words_size; use utils::set::SetTrait; use utils::traits::{ BoolIntoNumeric, EthAddressIntoU256, U256TryIntoResult, SpanU8TryIntoResultEthAddress @@ -48,7 +48,7 @@ pub impl CreateHelpersImpl of CreateHelpers { let charged_gas = match create_type { CreateType::Create => gas::CREATE + memory_expansion.expansion_cost + init_code_gas, CreateType::Create2 => { - let calldata_words = ceil32(size) / 32; + let calldata_words = bytes_32_words_size(size); gas::CREATE + gas::KECCAK256WORD * calldata_words.into() + memory_expansion.expansion_cost diff --git a/crates/evm/src/gas.cairo b/crates/evm/src/gas.cairo index 0a479e47c..60011fd8e 100644 --- a/crates/evm/src/gas.cairo +++ b/crates/evm/src/gas.cairo @@ -194,7 +194,7 @@ pub fn memory_expansion( } }?; - let new_size = helpers::ceil32(max_size); + let new_size = helpers::bytes_32_words_size(max_size) * 32; if new_size <= current_size { return Result::Ok(MemoryExpansion { new_size: current_size, expansion_cost: 0 }); @@ -218,7 +218,7 @@ pub fn memory_expansion( /// * `init_code_gas` - The gas to be charged for the init code. #[inline(always)] pub fn init_code_cost(code_size: usize) -> u64 { - let code_size_in_words = helpers::ceil32(code_size) / 32; + let code_size_in_words = helpers::bytes_32_words_size(code_size); code_size_in_words.into() * INITCODE_WORD_COST } diff --git a/crates/evm/src/instructions/environmental_information.cairo b/crates/evm/src/instructions/environmental_information.cairo index bcb0729f0..cd91df08d 100644 --- a/crates/evm/src/instructions/environmental_information.cairo +++ b/crates/evm/src/instructions/environmental_information.cairo @@ -8,7 +8,7 @@ use crate::model::vm::{VM, VMTrait}; use crate::model::{AddressTrait}; use crate::stack::StackTrait; use crate::state::StateTrait; -use utils::helpers::{ceil32, load_word}; +use utils::helpers::{bytes_32_words_size, load_word}; use utils::set::SetTrait; use utils::traits::{EthAddressIntoU256}; @@ -113,7 +113,7 @@ pub impl EnvironmentInformationImpl of EnvironmentInformationTrait { let offset = self.stack.pop_saturating_usize()?; let size = self.stack.pop_usize()?; - let words_size = (ceil32(size) / 32).into(); + let words_size = bytes_32_words_size(size).into(); let copy_gas_cost = gas::COPY * words_size; let memory_expansion = gas::memory_expansion( self.memory.size(), [(dest_offset, size)].span() @@ -143,7 +143,7 @@ pub impl EnvironmentInformationImpl of EnvironmentInformationTrait { let offset = self.stack.pop_saturating_usize()?; let size = self.stack.pop_usize()?; - let words_size = (ceil32(size) / 32).into(); + let words_size = bytes_32_words_size(size).into(); let copy_gas_cost = gas::COPY * words_size; let memory_expansion = gas::memory_expansion( self.memory.size(), [(dest_offset, size)].span() @@ -193,7 +193,7 @@ pub impl EnvironmentInformationImpl of EnvironmentInformationTrait { let size = self.stack.pop_usize()?; // GAS - let words_size = (ceil32(size) / 32).into(); + let words_size = bytes_32_words_size(size).into(); let memory_expansion = gas::memory_expansion( self.memory.size(), [(dest_offset, size)].span() )?; @@ -236,8 +236,7 @@ pub impl EnvironmentInformationImpl of EnvironmentInformationTrait { } ensure(!(last_returndata_index > return_data.len()), EVMError::ReturnDataOutOfBounds)?; - //TODO: handle overflow in ceil32 function. - let words_size = (ceil32(size.into()) / 32).into(); + let words_size = bytes_32_words_size(size).into(); let copy_gas_cost = gas::COPY * words_size; let memory_expansion = gas::memory_expansion( diff --git a/crates/evm/src/instructions/memory_operations.cairo b/crates/evm/src/instructions/memory_operations.cairo index cecc865a6..fc56ebdc1 100644 --- a/crates/evm/src/instructions/memory_operations.cairo +++ b/crates/evm/src/instructions/memory_operations.cairo @@ -7,7 +7,7 @@ use crate::memory::MemoryTrait; use crate::model::vm::{VM, VMTrait}; use crate::stack::StackTrait; use crate::state::StateTrait; -use utils::helpers::ceil32; +use utils::helpers::bytes_32_words_size; use utils::set::SetTrait; #[inline(always)] @@ -282,7 +282,7 @@ pub impl MemoryOperation of MemoryOperationTrait { let source_offset = self.stack.pop_usize()?; let size = self.stack.pop_usize()?; - let words_size = (ceil32(size) / 32).into(); + let words_size = bytes_32_words_size(size).into(); let copy_gas_cost = gas::COPY * words_size; let memory_expansion = gas::memory_expansion( self.memory.size(), [(max(dest_offset, source_offset), size)].span() diff --git a/crates/evm/src/instructions/sha3.cairo b/crates/evm/src/instructions/sha3.cairo index 563a5fa12..28f740398 100644 --- a/crates/evm/src/instructions/sha3.cairo +++ b/crates/evm/src/instructions/sha3.cairo @@ -8,7 +8,7 @@ use crate::gas; use crate::memory::MemoryTrait; use crate::model::vm::{VM, VMTrait}; use crate::stack::StackTrait; -use utils::helpers::ceil32; +use utils::helpers::bytes_32_words_size; use utils::traits::array::ArrayExtTrait; use utils::traits::integer::U256Trait; @@ -26,7 +26,7 @@ pub impl Sha3Impl of Sha3Trait { let offset: usize = self.stack.pop_usize()?; let mut size: usize = self.stack.pop_usize()?; - let words_size = (ceil32(size) / 32).into(); + let words_size = bytes_32_words_size(size).into(); let word_gas_cost = gas::KECCAK256WORD * words_size; let memory_expansion = gas::memory_expansion(self.memory.size(), [(offset, size)].span())?; self.memory.ensure_length(memory_expansion.new_size); diff --git a/crates/utils/src/helpers.cairo b/crates/utils/src/helpers.cairo index d50eb3418..97c0b100e 100644 --- a/crates/utils/src/helpers.cairo +++ b/crates/utils/src/helpers.cairo @@ -2,6 +2,7 @@ use core::array::ArrayTrait; use core::array::SpanTrait; use core::cmp::min; use core::hash::{HashStateExTrait, HashStateTrait}; +use core::num::traits::SaturatingAdd; use core::panic_with_felt252; use core::pedersen::PedersenTrait; @@ -31,28 +32,20 @@ pub fn u128_split(input: u128) -> (u64, u64) { (high.try_into().unwrap(), low.try_into().unwrap()) } - -/// Converts a value to the next closest multiple of 32 +/// Computes the number of 32-byte words required to represent `size` bytes /// /// # Arguments -/// * `value` - The value to ceil to the next multiple of 32 +/// * `size` - The size in bytes /// /// # Returns -/// The same value if it's a perfect multiple of 32 -/// else it returns the smallest multiple of 32 -/// that is greater than `value`. +/// The number of 32-byte words required to represent `size` bytes /// /// # Examples -/// ceil32(2) = 32 -/// ceil32(34) = 64 -pub fn ceil32(value: usize) -> usize { - let ceiling = 32_u32; - let (_q, r) = DivRem::div_rem(value, ceiling.try_into().unwrap()); - if r == 0_u8.into() { - return value; - } else { - return (value + ceiling - r).into(); - } +/// bytes_32_words_size(2) = 1 +/// bytes_32_words_size(34) = 2 +#[inline(always)] +pub fn bytes_32_words_size(size: usize) -> usize { + size.saturating_add(31) / 32 } /// Computes 256 ** (16 - i) for 0 <= i <= 16. @@ -357,4 +350,10 @@ mod tests { assert_eq!(*dst4[counter], 0xff); }; } + + #[test] + fn test_bytes_32_words_size_edge_case() { + let max_usize = core::num::traits::Bounded::::MAX; + assert_eq!(helpers::bytes_32_words_size(max_usize), (max_usize / 32)); + } }