From 7cbfdae2021cfcff0be0cf2b0a50dc782e8eed56 Mon Sep 17 00:00:00 2001 From: Mathieu <60658558+enitrat@users.noreply.github.com> Date: Fri, 6 Sep 2024 17:25:07 +0200 Subject: [PATCH] fix: memory expansions (#913) * fix: memory extensions and gas costs * fix: memory expansion timing --- crates/evm/src/create_helpers.cairo | 3 +- crates/evm/src/gas.cairo | 35 ++++- .../environmental_information.cairo | 25 ++-- .../src/instructions/logging_operations.cairo | 43 +++--- .../src/instructions/memory_operations.cairo | 45 +++---- crates/evm/src/instructions/sha3.cairo | 119 ++++++++++++----- .../src/instructions/system_operations.cairo | 80 ++++------- crates/evm/src/memory.cairo | 124 ++++++------------ crates/evm/src/precompiles/ec_recover.cairo | 6 +- crates/evm/src/precompiles/identity.cairo | 5 +- crates/evm/src/precompiles/sha256.cairo | 5 +- crates/evm/src/test_utils.cairo | 13 ++ scripts/gas_debug_call.py | 41 +++--- 13 files changed, 286 insertions(+), 258 deletions(-) diff --git a/crates/evm/src/create_helpers.cairo b/crates/evm/src/create_helpers.cairo index 5d9c33500..5e7a7d061 100644 --- a/crates/evm/src/create_helpers.cairo +++ b/crates/evm/src/create_helpers.cairo @@ -52,7 +52,8 @@ impl CreateHelpersImpl of CreateHelpers { let offset = self.stack.pop_usize()?; let size = self.stack.pop_usize()?; - let memory_expansion = gas::memory_expansion(self.memory.size(), offset + size); + let memory_expansion = gas::memory_expansion(self.memory.size(), [(offset, size)].span()); + self.memory.ensure_length(memory_expansion.new_size); let init_code_gas = gas::init_code_cost(size); let charged_gas = match create_type { CreateType::Create => gas::CREATE + memory_expansion.expansion_cost + init_code_gas, diff --git a/crates/evm/src/gas.cairo b/crates/evm/src/gas.cairo index 0b4f630cd..dd9e90a7b 100644 --- a/crates/evm/src/gas.cairo +++ b/crates/evm/src/gas.cairo @@ -155,21 +155,42 @@ fn calculate_memory_gas_cost(size_in_bytes: usize) -> u128 { } -fn memory_expansion(memory_size: usize, max_offset: usize) -> MemoryExpansion { - let new_size = helpers::ceil32(max_offset); +/// Calculates memory expansion based on multiple memory operations. +/// +/// # Arguments +/// +/// * `current_size`: Current size of the memory. +/// * `operations`: A span of tuples (offset, size) representing memory operations. +/// +/// # Returns +/// +/// * `MemoryExpansion`: New size and expansion cost. +fn memory_expansion(current_size: usize, operations: Span<(usize, usize)>) -> MemoryExpansion { + let mut max_size = current_size; + + for ( + offset, size + ) in operations { + if *size != 0 { + let end = *offset + *size; + if end > max_size { + max_size = end; + } + } + }; + + let new_size = helpers::ceil32(max_size); - if new_size <= memory_size { - return MemoryExpansion { new_size: memory_size, expansion_cost: 0 }; + if new_size <= current_size { + return MemoryExpansion { new_size: current_size, expansion_cost: 0 }; } - let prev_cost = calculate_memory_gas_cost(memory_size); + let prev_cost = calculate_memory_gas_cost(current_size); let new_cost = calculate_memory_gas_cost(new_size); let expansion_cost = new_cost - prev_cost; - MemoryExpansion { new_size, expansion_cost } } - /// Calculates the gas to be charged for the init code in CREATE/CREATE2 /// opcodes as well as create transactions. /// diff --git a/crates/evm/src/instructions/environmental_information.cairo b/crates/evm/src/instructions/environmental_information.cairo index 5c32df1fa..3cee0f22b 100644 --- a/crates/evm/src/instructions/environmental_information.cairo +++ b/crates/evm/src/instructions/environmental_information.cairo @@ -129,7 +129,10 @@ impl EnvironmentInformationImpl of EnvironmentInformationTrait { let words_size: u128 = (ceil32(size) / 32).into(); let copy_gas_cost = gas::COPY * words_size; - let memory_expansion = gas::memory_expansion(self.memory.size(), dest_offset + size); + let memory_expansion = gas::memory_expansion( + self.memory.size(), [(dest_offset, size)].span() + ); + self.memory.ensure_length(memory_expansion.new_size); self.charge_gas(gas::VERYLOW + copy_gas_cost + memory_expansion.expansion_cost)?; let calldata: Span = self.message().data; @@ -156,7 +159,10 @@ impl EnvironmentInformationImpl of EnvironmentInformationTrait { let words_size: u128 = (ceil32(size) / 32).into(); let copy_gas_cost = gas::COPY * words_size; - let memory_expansion = gas::memory_expansion(self.memory.size(), dest_offset + size); + let memory_expansion = gas::memory_expansion( + self.memory.size(), [(dest_offset, size)].span() + ); + self.memory.ensure_length(memory_expansion.new_size); self.charge_gas(gas::VERYLOW + copy_gas_cost + memory_expansion.expansion_cost)?; let bytecode: Span = self.message().code; @@ -202,7 +208,10 @@ impl EnvironmentInformationImpl of EnvironmentInformationTrait { // GAS let words_size: u128 = (ceil32(size) / 32).into(); - let memory_expansion = gas::memory_expansion(self.memory.size(), dest_offset + size); + let memory_expansion = gas::memory_expansion( + self.memory.size(), [(dest_offset, size)].span() + ); + self.memory.ensure_length(memory_expansion.new_size); let copy_gas_cost = gas::COPY * words_size; let access_gas_cost = if self.accessed_addresses.contains(evm_address) { gas::WARM_ACCESS_COST @@ -245,12 +254,10 @@ impl EnvironmentInformationImpl of EnvironmentInformationTrait { let words_size: u128 = (ceil32(size.into()) / 32).into(); let copy_gas_cost = gas::COPY * words_size; - let (max_memory_size, overflow) = dest_offset.overflowing_add(size); - if overflow { - return Result::Err(EVMError::OutOfGas); - } - - let memory_expansion = gas::memory_expansion(self.memory.size(), max_memory_size); + let memory_expansion = gas::memory_expansion( + self.memory.size(), [(dest_offset, size)].span() + ); + self.memory.ensure_length(memory_expansion.new_size); self.charge_gas(gas::VERYLOW + copy_gas_cost + memory_expansion.expansion_cost)?; let data_to_copy: Span = return_data.slice(offset, size); diff --git a/crates/evm/src/instructions/logging_operations.cairo b/crates/evm/src/instructions/logging_operations.cairo index 02db776e9..89b8e21d0 100644 --- a/crates/evm/src/instructions/logging_operations.cairo +++ b/crates/evm/src/instructions/logging_operations.cairo @@ -69,7 +69,8 @@ mod internal { let size = self.stack.pop_usize()?; let topics: Array = self.stack.pop_n(topics_len.into())?; - let memory_expansion = gas::memory_expansion(self.memory.size(), offset + size); + let memory_expansion = gas::memory_expansion(self.memory.size(), [(offset, size)].span()); + self.memory.ensure_length(memory_expansion.new_size); self .charge_gas( gas::LOG @@ -97,7 +98,7 @@ mod tests { use evm::memory::MemoryTrait; use evm::stack::StackTrait; use evm::state::StateTrait; - use evm::test_utils::{VMBuilderTrait}; + use evm::test_utils::{VMBuilderTrait, MemoryTestUtilsTrait}; use utils::helpers::u256_to_bytes_array; #[test] @@ -105,7 +106,7 @@ mod tests { // Given let mut vm = VMBuilderTrait::new_with_presets().build(); - vm.memory.store(Bounded::::MAX, 0); + vm.memory.store_with_expansion(Bounded::::MAX, 0); vm.stack.push(0x1F).expect('push failed'); vm.stack.push(0x00).expect('push failed'); @@ -133,7 +134,7 @@ mod tests { // Given let mut vm = VMBuilderTrait::new_with_presets().build(); - vm.memory.store(Bounded::::MAX, 0); + vm.memory.store_with_expansion(Bounded::::MAX, 0); vm.stack.push(0x0123456789ABCDEF).expect('push failed'); vm.stack.push(0x20).expect('push failed'); @@ -163,7 +164,7 @@ mod tests { // Given let mut vm = VMBuilderTrait::new_with_presets().build(); - vm.memory.store(Bounded::::MAX, 0); + vm.memory.store_with_expansion(Bounded::::MAX, 0); vm.stack.push(Bounded::::MAX).expect('push failed'); vm.stack.push(0x0123456789ABCDEF).expect('push failed'); @@ -195,8 +196,12 @@ mod tests { // Given let mut vm = VMBuilderTrait::new_with_presets().build(); - vm.memory.store(Bounded::::MAX, 0); - vm.memory.store(0x0123456789ABCDEF000000000000000000000000000000000000000000000000, 0x20); + vm.memory.store_with_expansion(Bounded::::MAX, 0); + vm + .memory + .store_with_expansion( + 0x0123456789ABCDEF000000000000000000000000000000000000000000000000, 0x20 + ); vm.stack.push(0x00).expect('push failed'); vm.stack.push(Bounded::::MAX).expect('push failed'); @@ -232,8 +237,12 @@ mod tests { // Given let mut vm = VMBuilderTrait::new_with_presets().build(); - vm.memory.store(Bounded::::MAX, 0); - vm.memory.store(0x0123456789ABCDEF000000000000000000000000000000000000000000000000, 0x20); + vm.memory.store_with_expansion(Bounded::::MAX, 0); + vm + .memory + .store_with_expansion( + 0x0123456789ABCDEF000000000000000000000000000000000000000000000000, 0x20 + ); vm.stack.push(Bounded::::MAX).expect('push failed'); vm.stack.push(0x00).expect('push failed'); @@ -269,7 +278,7 @@ mod tests { // Given let mut vm = VMBuilderTrait::new().with_read_only().build(); - vm.memory.store(Bounded::::MAX, 0); + vm.memory.store_with_expansion(Bounded::::MAX, 0); vm.stack.push(0x0123456789ABCDEF).expect('push failed'); vm.stack.push(0x20).expect('push failed'); @@ -290,7 +299,7 @@ mod tests { // Given let mut vm = VMBuilderTrait::new_with_presets().build(); - vm.memory.store(Bounded::::MAX, 0); + vm.memory.store_with_expansion(Bounded::::MAX, 0); vm.stack.push(0x0123456789ABCDEF).expect('push failed'); vm.stack.push(0x00).expect('push failed'); @@ -318,7 +327,7 @@ mod tests { // Given let mut vm = VMBuilderTrait::new_with_presets().build(); - vm.memory.store(Bounded::::MAX, 0); + vm.memory.store_with_expansion(Bounded::::MAX, 0); vm.stack.push(0x0123456789ABCDEF).expect('push failed'); vm.stack.push(Bounded::::MAX).expect('push failed'); @@ -340,7 +349,7 @@ mod tests { // Given let mut vm = VMBuilderTrait::new_with_presets().build(); - vm.memory.store(Bounded::::MAX, 0); + vm.memory.store_with_expansion(Bounded::::MAX, 0); vm.stack.push(0x0123456789ABCDEF).expect('push failed'); vm.stack.push(0x20).expect('push failed'); @@ -362,8 +371,12 @@ mod tests { // Given let mut vm = VMBuilderTrait::new_with_presets().build(); - vm.memory.store(Bounded::::MAX, 0); - vm.memory.store(0x0123456789ABCDEF000000000000000000000000000000000000000000000000, 0x20); + vm.memory.store_with_expansion(Bounded::::MAX, 0); + vm + .memory + .store_with_expansion( + 0x0123456789ABCDEF000000000000000000000000000000000000000000000000, 0x20 + ); vm.stack.push(Bounded::::MAX).expect('push failed'); vm.stack.push(0x00).expect('push failed'); diff --git a/crates/evm/src/instructions/memory_operations.cairo b/crates/evm/src/instructions/memory_operations.cairo index 42d428c3f..0212d578f 100644 --- a/crates/evm/src/instructions/memory_operations.cairo +++ b/crates/evm/src/instructions/memory_operations.cairo @@ -43,7 +43,8 @@ impl MemoryOperation of MemoryOperationTrait { fn exec_mload(ref self: VM) -> Result<(), EVMError> { let offset: usize = self.stack.pop_usize()?; - let memory_expansion = gas::memory_expansion(self.memory.size(), offset + 32); + let memory_expansion = gas::memory_expansion(self.memory.size(), [(offset, 32)].span()); + self.memory.ensure_length(memory_expansion.new_size); self.charge_gas(gas::VERYLOW + memory_expansion.expansion_cost)?; let result = self.memory.load(offset); @@ -56,7 +57,8 @@ impl MemoryOperation of MemoryOperationTrait { fn exec_mstore(ref self: VM) -> Result<(), EVMError> { let offset: usize = self.stack.pop_usize()?; let value: u256 = self.stack.pop()?; - let memory_expansion = gas::memory_expansion(self.memory.size(), offset + 32); + let memory_expansion = gas::memory_expansion(self.memory.size(), [(offset, 32)].span()); + self.memory.ensure_length(memory_expansion.new_size); self.charge_gas(gas::VERYLOW + memory_expansion.expansion_cost)?; self.memory.store(value, offset); @@ -71,7 +73,8 @@ impl MemoryOperation of MemoryOperationTrait { let value = self.stack.pop()?; let value: u8 = (value.low & 0xFF).try_into().unwrap(); - let memory_expansion = gas::memory_expansion(self.memory.size(), offset + 1); + let memory_expansion = gas::memory_expansion(self.memory.size(), [(offset, 1)].span()); + self.memory.ensure_length(memory_expansion.new_size); self.charge_gas(gas::VERYLOW + memory_expansion.expansion_cost)?; self.memory.store_byte(value, offset); @@ -289,8 +292,9 @@ impl MemoryOperation of MemoryOperationTrait { let dest_offset = self.stack.pop_usize()?; let memory_expansion = gas::memory_expansion( - self.memory.size(), max(dest_offset, source_offset) + size + self.memory.size(), [(max(dest_offset, source_offset), size)].span() ); + self.memory.ensure_length(memory_expansion.new_size); self.charge_gas(gas::VERYLOW + memory_expansion.expansion_cost)?; if size == 0 { @@ -322,8 +326,8 @@ mod tests { use evm::stack::StackTrait; use evm::state::{StateTrait, compute_storage_address}; use evm::test_utils::{ - evm_address, VMBuilderTrait, setup_test_storages, register_account, uninitialized_account, - native_token + evm_address, VMBuilderTrait, MemoryTestUtilsTrait, setup_test_storages, register_account, + uninitialized_account, native_token }; use snforge_std::{test_address, start_mock_call, store}; use snforge_utils::snforge_utils::store_evm; @@ -377,7 +381,7 @@ mod tests { fn assert_mload(value: u256, offset: u256, expected_value: u256, expected_memory_size: u32) { // Given let mut vm = VMBuilderTrait::new_with_presets().build(); - vm.memory.store(value, 0); + vm.memory.store_with_expansion(value, 0); vm.stack.push(offset).expect('push failed'); @@ -565,10 +569,10 @@ mod tests { } #[test] - fn test_exec_msize_store_max_offset_0() { + fn test_exec_msize_should_return_size_of_memory() { // Given let mut vm = VMBuilderTrait::new_with_presets().build(); - vm.memory.store(Bounded::::MAX, 0x00); + vm.memory.store_with_expansion(Bounded::::MAX, 0x00); // When let result = vm.exec_msize(); @@ -576,22 +580,7 @@ mod tests { // Then assert(result.is_ok(), 'should have succeeded'); assert(vm.stack.len() == 1, 'stack should have one element'); - assert(vm.stack.pop().unwrap() == 32, 'should 32 bytes after MSTORE'); - } - - #[test] - fn test_exec_msize_store_max_offset_1() { - // Given - let mut vm = VMBuilderTrait::new_with_presets().build(); - vm.memory.store(Bounded::::MAX, 0x01); - - // When - let result = vm.exec_msize(); - - // Then - assert(result.is_ok(), 'should have succeeded'); - assert(vm.stack.len() == 1, 'stack should have one element'); - assert(vm.stack.pop().unwrap() == 64, 'should 64 bytes after MSTORE'); + assert(vm.stack.pop().unwrap() == 32, 'should 32 bytes after MSIZE'); } #[test] @@ -1045,7 +1034,7 @@ mod tests { let mut i = 0; for element in values .span() { - vm.memory.store((*element).into(), source_offset + 0x20 * i); + vm.memory.store_with_expansion((*element).into(), source_offset + 0x20 * i); i += 1; }; vm.stack.push(dest_offset.into()).expect('push failed'); @@ -1054,7 +1043,9 @@ mod tests { // When let expected_gas = gas::VERYLOW - + gas::memory_expansion(vm.memory.size(), max(dest_offset, source_offset) + size) + + gas::memory_expansion( + vm.memory.size(), [(max(dest_offset, source_offset), size)].span() + ) .expansion_cost; let gas_before = vm.gas_left(); let result = vm.exec_mcopy(); diff --git a/crates/evm/src/instructions/sha3.cairo b/crates/evm/src/instructions/sha3.cairo index cd30c36e4..e5b04c5da 100644 --- a/crates/evm/src/instructions/sha3.cairo +++ b/crates/evm/src/instructions/sha3.cairo @@ -25,7 +25,8 @@ impl Sha3Impl of Sha3Trait { let words_size: u128 = (ceil32(size) / 32).into(); let word_gas_cost = gas::KECCAK256WORD * words_size; - let memory_expansion = gas::memory_expansion(self.memory.size(), offset + size); + let memory_expansion = gas::memory_expansion(self.memory.size(), [(offset, size)].span()); + self.memory.ensure_length(memory_expansion.new_size); self.charge_gas(gas::KECCAK256 + word_gas_cost + memory_expansion.expansion_cost)?; let mut to_hash: Array = Default::default(); @@ -158,7 +159,7 @@ mod tests { use evm::instructions::sha3::internal; use evm::memory::{InternalMemoryTrait, MemoryTrait}; use evm::stack::StackTrait; - use evm::test_utils::VMBuilderTrait; + use evm::test_utils::{VMBuilderTrait, MemoryTestUtilsTrait}; #[test] fn test_exec_sha3_size_0_offset_0() { @@ -168,7 +169,11 @@ mod tests { vm.stack.push(0x00).expect('push failed'); vm.stack.push(0x00).expect('push failed'); - vm.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); + vm + .memory + .store_with_expansion( + 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0 + ); // When vm.exec_sha3().expect('exec_sha3 failed'); @@ -185,47 +190,49 @@ mod tests { #[test] - fn test_exec_sha3_size_5_offset_4() { + fn test_exec_sha3_should_not_expand_memory() { // Given let mut vm = VMBuilderTrait::new_with_presets().build(); vm.stack.push(0x05).expect('push failed'); vm.stack.push(0x04).expect('push failed'); - vm.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); + vm + .memory + .store_with_expansion( + 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0 + ); // When vm.exec_sha3().expect('exec_sha3 failed'); // Then let result = vm.stack.peek().unwrap(); - assert( - result == 0xc41589e7559804ea4a2080dad19d876a024ccb05117835447d72ce08c1d020ec, - 'wrong result' - ); - assert(vm.memory.size() == 64, 'wrong memory size'); + assert_eq!(result, 0xc41589e7559804ea4a2080dad19d876a024ccb05117835447d72ce08c1d020ec); + assert_eq!(vm.memory.size(), 32); } #[test] - fn test_exec_sha3_size_10_offset_10() { + fn test_exec_sha3_should_expand_memory() { // Given let mut vm = VMBuilderTrait::new_with_presets().build(); - vm.stack.push(10).expect('push failed'); + vm.stack.push(24).expect('push failed'); vm.stack.push(10).expect('push failed'); - vm.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); + vm + .memory + .store_with_expansion( + 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0 + ); // When vm.exec_sha3().expect('exec_sha3 failed'); // Then let result = vm.stack.peek().unwrap(); - assert( - result == 0x6bd2dd6bd408cbee33429358bf24fdc64612fbf8b1b4db604518f40ffd34b607, - 'wrong result' - ); - assert(vm.memory.size() == 64, 'wrong memory size'); + assert_eq!(result, 0x827b659bbda2a0bdecce2c91b8b68462545758f3eba2dbefef18e0daf84f5ccd); + assert_eq!(vm.memory.size(), 64); } #[test] @@ -236,7 +243,11 @@ mod tests { vm.stack.push(0xFFFFF).expect('push failed'); vm.stack.push(1000).expect('push failed'); - vm.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); + vm + .memory + .store_with_expansion( + 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0 + ); // When vm.exec_sha3().expect('exec_sha3 failed'); @@ -258,7 +269,11 @@ mod tests { vm.stack.push(1000000).expect('push failed'); vm.stack.push(2).expect('push failed'); - vm.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); + vm + .memory + .store_with_expansion( + 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0 + ); // When vm.exec_sha3().expect('exec_sha3 failed'); @@ -280,8 +295,16 @@ mod tests { vm.stack.push(1000000).expect('push failed'); vm.stack.push(2).expect('push failed'); - vm.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); - vm.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); + vm + .memory + .store_with_expansion( + 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0 + ); + vm + .memory + .store_with_expansion( + 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0 + ); // When vm.exec_sha3().expect('exec_sha3 failed'); @@ -303,7 +326,11 @@ mod tests { vm.stack.push(1).expect('push failed'); vm.stack.push(2048).expect('push failed'); - vm.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); + vm + .memory + .store_with_expansion( + 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0 + ); // When vm.exec_sha3().expect('exec_sha3 failed'); @@ -325,7 +352,11 @@ mod tests { vm.stack.push(0).expect('push failed'); vm.stack.push(1024).expect('push failed'); - vm.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); + vm + .memory + .store_with_expansion( + 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0 + ); // When vm.exec_sha3().expect('exec_sha3 failed'); @@ -347,7 +378,11 @@ mod tests { vm.stack.push(32).expect('push failed'); vm.stack.push(2016).expect('push failed'); - vm.memory.store(0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0); + vm + .memory + .store_with_expansion( + 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000, 0 + ); // When vm.exec_sha3().expect('exec_sha3 failed'); @@ -369,7 +404,11 @@ mod tests { vm.stack.push(32).expect('push failed'); vm.stack.push(0).expect('push failed'); - vm.memory.store(0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000, 0); + vm + .memory + .store_with_expansion( + 0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000, 0 + ); // When vm.exec_sha3().expect('exec_sha3 failed'); @@ -391,7 +430,11 @@ mod tests { vm.stack.push(31).expect('push failed'); vm.stack.push(0).expect('push failed'); - vm.memory.store(0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000, 0); + vm + .memory + .store_with_expansion( + 0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000, 0 + ); // When vm.exec_sha3().expect('exec_sha3 failed'); @@ -413,7 +456,11 @@ mod tests { vm.stack.push(33).expect('push failed'); vm.stack.push(0).expect('push failed'); - vm.memory.store(0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000, 0); + vm + .memory + .store_with_expansion( + 0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000, 0 + ); // When vm.exec_sha3().expect('exec_sha3 failed'); @@ -439,7 +486,9 @@ mod tests { while mem_dst <= 0x0C80 { vm .memory - .store(0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, mem_dst); + .store_with_expansion( + 0xFAFAFAFA00000000000000000000000000000000000000000000000000000000, mem_dst + ); mem_dst += 0x20; }; @@ -461,7 +510,11 @@ mod tests { let mut vm = VMBuilderTrait::new_with_presets().build(); let mut to_hash: Array = Default::default(); - vm.memory.store(0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000, 0); + vm + .memory + .store_with_expansion( + 0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000, 0 + ); let mut size = 32; let mut offset = 0; @@ -485,7 +538,11 @@ mod tests { let mut vm = VMBuilderTrait::new_with_presets().build(); let mut to_hash: Array = Default::default(); - vm.memory.store(0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000, 0); + vm + .memory + .store_with_expansion( + 0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000, 0 + ); let mut size = 33; let mut offset = 0; diff --git a/crates/evm/src/instructions/system_operations.cairo b/crates/evm/src/instructions/system_operations.cairo index a10d9ac54..fb1cf4bed 100644 --- a/crates/evm/src/instructions/system_operations.cairo +++ b/crates/evm/src/instructions/system_operations.cairo @@ -38,19 +38,11 @@ impl SystemOperations of SystemOperationsTrait { let ret_offset = self.stack.pop_usize()?; let ret_size = self.stack.pop_usize()?; - let args_max_offset = args_offset + args_size; - let ret_max_offset = ret_offset + ret_size; - - let max_memory_size = if args_max_offset > ret_max_offset { - args_max_offset - } else { - ret_max_offset - }; - // GAS - //TODO(optimization): if we know how much the memory is going to be expanded, - // we can return the new size and save a computation later. - let memory_expansion = gas::memory_expansion(self.memory.size(), max_memory_size); + let memory_expansion = gas::memory_expansion( + self.memory.size(), [(args_offset, args_size), (ret_offset, ret_size)].span() + ); + self.memory.ensure_length(memory_expansion.new_size); let access_gas_cost = if self.accessed_addresses.contains(to) { gas::WARM_ACCESS_COST @@ -125,21 +117,13 @@ impl SystemOperations of SystemOperationsTrait { let ret_offset = self.stack.pop_usize()?; let ret_size = self.stack.pop_usize()?; - let args_max_offset = args_offset + args_size; - let ret_max_offset = ret_offset + ret_size; - let to = self.message().target.evm; - let max_memory_size = if args_max_offset > ret_max_offset { - args_max_offset - } else { - ret_max_offset - }; - // GAS - //TODO(optimization): if we know how much the memory is going to be expanded, - // we can return the new size and save a computation later. - let memory_expansion = gas::memory_expansion(self.memory.size(), max_memory_size); + let memory_expansion = gas::memory_expansion( + self.memory.size(), [(args_offset, args_size), (ret_offset, ret_size)].span() + ); + self.memory.ensure_length(memory_expansion.new_size); let access_gas_cost = if self.accessed_addresses.contains(code_address) { gas::WARM_ACCESS_COST @@ -193,7 +177,8 @@ impl SystemOperations of SystemOperationsTrait { fn exec_return(ref self: VM) -> Result<(), EVMError> { let offset = self.stack.pop_usize()?; let size = self.stack.pop_usize()?; - let memory_expansion = gas::memory_expansion(self.memory.size(), offset + size); + let memory_expansion = gas::memory_expansion(self.memory.size(), [(offset, size)].span()); + self.memory.ensure_length(memory_expansion.new_size); self.charge_gas(gas::ZERO + memory_expansion.expansion_cost)?; let mut return_data = Default::default(); @@ -216,19 +201,11 @@ impl SystemOperations of SystemOperationsTrait { let ret_offset = self.stack.pop_usize()?; let ret_size = self.stack.pop_usize()?; - let args_max_offset = args_offset + args_size; - let ret_max_offset = ret_offset + ret_size; - - let max_memory_size = if args_max_offset > ret_max_offset { - args_max_offset - } else { - ret_max_offset - }; - // GAS - //TODO(optimization): if we know how much the memory is going to be expanded, - // we can return the new size and save a computation later. - let memory_expansion = gas::memory_expansion(self.memory.size(), max_memory_size); + let memory_expansion = gas::memory_expansion( + self.memory.size(), [(args_offset, args_size), (ret_offset, ret_size)].span() + ); + self.memory.ensure_length(memory_expansion.new_size); let access_gas_cost = if self.accessed_addresses.contains(code_address) { gas::WARM_ACCESS_COST @@ -277,19 +254,11 @@ impl SystemOperations of SystemOperationsTrait { let ret_offset = self.stack.pop_usize()?; let ret_size = self.stack.pop_usize()?; - let args_max_offset = args_offset + args_size; - let ret_max_offset = ret_offset + ret_size; - - let max_memory_size = if args_max_offset > ret_max_offset { - args_max_offset - } else { - ret_max_offset - }; - // GAS - //TODO(optimization): if we know how much the memory is going to be expanded, - // we can return the new size and save a computation later. - let memory_expansion = gas::memory_expansion(self.memory.size(), max_memory_size); + let memory_expansion = gas::memory_expansion( + self.memory.size(), [(args_offset, args_size), (ret_offset, ret_size)].span() + ); + self.memory.ensure_length(memory_expansion.new_size); let access_gas_cost = if self.accessed_addresses.contains(to) { gas::WARM_ACCESS_COST @@ -326,7 +295,8 @@ impl SystemOperations of SystemOperationsTrait { let offset = self.stack.pop_usize()?; let size = self.stack.pop_usize()?; - let memory_expansion = gas::memory_expansion(self.memory.size(), offset + size); + let memory_expansion = gas::memory_expansion(self.memory.size(), [(offset, size)].span()); + self.memory.ensure_length(memory_expansion.new_size); self.charge_gas(memory_expansion.expansion_cost)?; let mut return_data = Default::default(); @@ -420,8 +390,8 @@ mod tests { use evm::stack::StackTrait; use evm::state::{StateTrait, State}; use evm::test_utils::{ - VMBuilderTrait, native_token, evm_address, test_dual_address, other_evm_address, - setup_test_storages, register_account, origin, uninitialized_account + VMBuilderTrait, MemoryTestUtilsTrait, native_token, evm_address, test_dual_address, + other_evm_address, setup_test_storages, register_account, origin, uninitialized_account }; use openzeppelin::token::erc20::interface::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait}; use snforge_std::{test_address, start_mock_call}; @@ -978,7 +948,7 @@ mod tests { // Load into memory the bytecode of Storage.sol let storage_initcode = storage_evm_initcode(); - vm.memory.store_n(storage_initcode, 0); + vm.memory.store_n_with_expansion(storage_initcode, 0); vm.stack.push(storage_initcode.len().into()).unwrap(); vm.stack.push(0).expect('push failed'); @@ -1044,7 +1014,7 @@ mod tests { // Load into memory the bytecode to init, which is the revert opcode let revert_initcode = [0xFD].span(); - vm.memory.store_n(revert_initcode, 0); + vm.memory.store_n_with_expansion(revert_initcode, 0); vm.stack.push(revert_initcode.len().into()).unwrap(); vm.stack.push(0).expect('push failed'); @@ -1108,7 +1078,7 @@ mod tests { // Load into memory the bytecode of Storage.sol let storage_initcode = storage_evm_initcode(); - vm.memory.store_n(storage_initcode, 0); + vm.memory.store_n_with_expansion(storage_initcode, 0); vm.stack.push(0).expect('push failed'); vm.stack.push(storage_initcode.len().into()).unwrap(); diff --git a/crates/evm/src/memory.cairo b/crates/evm/src/memory.cairo index c768881ce..230fbd877 100644 --- a/crates/evm/src/memory.cairo +++ b/crates/evm/src/memory.cairo @@ -56,10 +56,6 @@ impl MemoryImpl of MemoryTrait { /// index = Y + i * MEMORY_SEGMENT_SIZE #[inline(always)] fn store(ref self: Memory, element: u256, offset: usize) { - //TODO(optimization): new bytes len was already compute when charging the gas - let new_min_bytes_len = helpers::ceil32(offset + 32); - self.bytes_len = max(new_min_bytes_len, self.size()); - // Check alignment of offset to bytes16 chunks let (chunk_index, offset_in_chunk) = u32_safe_divmod(offset, u32_as_non_zero(16)); @@ -92,9 +88,6 @@ impl MemoryImpl of MemoryTrait { /// * `offset` - The offset within memory to store the byte at. #[inline(always)] fn store_byte(ref self: Memory, value: u8, offset: usize) { - let new_min_bytes_len = helpers::ceil32(offset + 1); - self.bytes_len = max(new_min_bytes_len, self.size()); - // Compute actual offset in Memory, given active_segment of Memory (current Execution // Context id) // And Memory Segment Size @@ -137,10 +130,6 @@ impl MemoryImpl of MemoryTrait { return; } - // Compute new bytes_len. - let new_min_bytes_len = helpers::ceil32(offset + elements.len()); - self.bytes_len = max(new_min_bytes_len, self.size()); - // Compute the offset inside the Memory, given its active segment, following the formula: // index = offset + self.active_segment * 125000 @@ -224,8 +213,6 @@ impl MemoryImpl of MemoryTrait { /// * `u256` - The loaded value. #[inline(always)] fn load(ref self: Memory, offset: usize) -> u256 { - self.ensure_length(32 + offset); - self.load_internal(offset) } @@ -233,8 +220,6 @@ impl MemoryImpl of MemoryTrait { /// inside elements. #[inline(always)] fn load_n(ref self: Memory, elements_len: usize, ref elements: Array, offset: usize) { - self.ensure_length(elements_len + offset); - self.load_n_internal(elements_len, ref elements, offset); } @@ -705,8 +690,8 @@ mod tests { memory.store(value, 0); // Then - let len = memory.size(); - assert(len == 32, 'memory should be 32bytes'); + assert_eq!(memory.items.get(0), 0); + assert_eq!(memory.items.get(1), 1); } #[test] @@ -716,11 +701,14 @@ mod tests { // When let value: u256 = 1; - memory.store(value, 1); + let offset = 1; + memory.store(value, offset); // Then - let len = memory.size(); - assert(len == 64, 'memory should be 64bytes'); + let internal_index = offset / 2; + assert_eq!(memory.items.get(internal_index.into()), 0); + assert_eq!(memory.items.get(internal_index.into() + 1), 0); + assert_eq!(memory.items.get(internal_index.into() + 2), 0x01000000000000000000000000000000); } #[test] @@ -730,20 +718,26 @@ mod tests { // When let value: u256 = 1; + let offset = 0; let bytes_array = helpers::u256_to_bytes_array(value); - memory.store_n(bytes_array.span(), 0); + memory.store_n(bytes_array.span(), offset); // Then - let len = memory.size(); - assert(len == 32, 'memory should be 32bytes'); + let internal_index = offset / 2; + assert_eq!(memory.items.get(internal_index.into()), 0); + assert_eq!(memory.items.get(internal_index.into() + 1), 1); } #[test] fn test_store_n_no_aligned_words() { let mut memory = MemoryTrait::new(); - memory.store_n([1, 2].span(), 15); - assert(memory.size() == 32, 'memory should be 32 bytes'); + let byte_offset = 15; + memory.store_n([1, 2].span(), byte_offset); + + let internal_index = byte_offset / 16; + assert_eq!(memory.items.get(internal_index.into()), 0x01); + assert_eq!(memory.items.get(internal_index.into() + 1), 0x02000000000000000000000000000000); } #[test] @@ -789,7 +783,6 @@ mod tests { memory.store_n(bytes_arr, 15); // value [1], will be stored in first word, values [2:34] will be stored in aligned words, // value [35] will be stored in final word - assert(memory.size() == 64, 'memory should be 64 bytes'); let mut stored_bytes = Default::default(); memory.load_n_internal(35, ref stored_bytes, 15); @@ -891,6 +884,7 @@ mod tests { let mut memory = MemoryTrait::new(); let value: u256 = 1; let bytes_array = helpers::u256_to_bytes_array(value); + memory.bytes_len = 32; memory.store_n(bytes_array.span(), 0); // When @@ -903,33 +897,15 @@ mod tests { } #[test] - fn test_expand__should_return_expanded_memory_and_cost() { + fn test_expand__should_return_expanded_memory_by_one_word() { // Given let mut memory = MemoryTrait::new(); - let value: u256 = 1; - let bytes_array = helpers::u256_to_bytes_array(value); - - memory.store_n(bytes_array.span(), 0); // When memory.expand(1); // Then - assert(memory.size() == 64, 'memory should be 64bytes'); - let value = memory.load_internal(0); - assert(value == 1, 'value should be 1'); - } - - #[test] - fn test_expand__should_return_expanded_memory_by_one_word_and_cost() { - // Given - let mut memory = MemoryTrait::new(); - - // When - memory.expand(1); - - // Then - assert(memory.size() == 32, 'memory should be 32bytes'); + assert_eq!(memory.size(), 32); } #[test] @@ -941,7 +917,7 @@ mod tests { memory.expand(32); // Then - assert(memory.size() == 32, 'memory should be 32bytes'); + assert_eq!(memory.size(), 32); } #[test] @@ -953,7 +929,7 @@ mod tests { memory.expand(33); // Then - assert(memory.size() == 64, 'memory should be 96bytes'); + assert_eq!(memory.size(), 64); } #[test] @@ -963,15 +939,16 @@ mod tests { let value: u256 = 1; let bytes_array = helpers::u256_to_bytes_array(value); + memory.bytes_len = 32; memory.store_n(bytes_array.span(), 0); // When memory.ensure_length(1); // Then - assert(memory.size() == 32, 'memory should be 32bytes'); + assert_eq!(memory.size(), 32); let value = memory.load_internal(0); - assert(value == 1, 'value should be 1'); + assert_eq!(value, 1); } #[test] @@ -981,35 +958,32 @@ mod tests { let value: u256 = 1; let bytes_array = helpers::u256_to_bytes_array(value); + memory.bytes_len = 32; memory.store_n(bytes_array.span(), 0); // When memory.ensure_length(33); // Then - assert(memory.size() == 64, 'memory should be 64bytes'); + assert_eq!(memory.size(), 64); let value = memory.load_internal(0); - assert(value == 1, 'value should be 1'); + assert_eq!(value, 1); } #[test] - fn test_expand_and_load_should_return_expanded_memory_and_element_and_cost() { + fn test_load_should_return_element() { // Given let mut memory = MemoryTrait::new(); let value: u256 = 1; let bytes_array = helpers::u256_to_bytes_array(value); + memory.bytes_len = 32; memory.store_n(bytes_array.span(), 0); // When - memory.load(32); + let value = memory.load(32); // Then - assert(memory.size() == 64, 'memory should be 64 bytes'); - let value = memory.load_internal(0); - assert(value == 1, 'loaded_element should be 1'); - - let value = memory.load_internal(32); - assert(value == 0, 'value should be 0'); + assert_eq!(value, 0); } #[test] @@ -1022,12 +996,14 @@ mod tests { memory.store_padded_segment(0, 0, bytes); // Then - let len = memory.size(); - assert(len == 0, 'memory should be 0bytes'); + let item_0 = memory.items.get(0); + let item_1 = memory.items.get(1); + assert_eq!(item_0, 0); + assert_eq!(item_1, 0); } #[test] - fn test_store_padded_segment_should_expand_memory() { + fn test_store_padded_segment_should_write_to_memory() { // Given let mut memory = MemoryTrait::new(); @@ -1036,10 +1012,8 @@ mod tests { memory.store_padded_segment(10, 10, bytes); // Then - let len = memory.size(); - assert(len == 32, 'memory should be length 32'); let word = memory.load(10); - assert(word == 0, 'word should be 0'); + assert_eq!(word, 0); } #[test] @@ -1052,9 +1026,6 @@ mod tests { memory.store_padded_segment(0, 5, bytes); // Then - let len = memory.size(); - assert(len == 32, 'memory should be 32bytes'); - let first_word = memory.load_internal(0); assert( first_word == 0x0102030405000000000000000000000000000000000000000000000000000000, @@ -1077,9 +1048,6 @@ mod tests { memory.store_padded_segment(0, 10, bytes); // Then - let len = memory.size(); - assert(len == 32, 'memory should be 32bytes'); - let first_word = memory.load_internal(0); assert( first_word == 0x01020304050000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, @@ -1101,10 +1069,6 @@ mod tests { let bytes = [1, 2, 3, 4, 5].span(); memory.store_padded_segment(5, 10, bytes); - // Then - let len = memory.size(); - assert(len == 32, 'memory should be 32bytes'); - let first_word = memory.load_internal(0); assert( first_word == 0xFFFFFFFFFF01020304050000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, @@ -1128,9 +1092,6 @@ mod tests { memory.store_padded_segment(30, 10, bytes); // Then - let len = memory.size(); - assert(len == 64, 'memory should be 64bytes'); - let first_word = memory.load_internal(0); assert( first_word == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0102, @@ -1156,7 +1117,6 @@ mod tests { // Then assert(memory.items[0] == 0x01, 'Wrong value for word 0'); assert(memory.items[1] == 0x00, 'Wrong value for word 1'); - assert(memory.size() == 32, 'Wrong memory length'); } #[test] fn test_store_byte_should_store_byte_at_offset_2() { @@ -1169,7 +1129,6 @@ mod tests { // Then assert(memory.items[0] == 0xff00, 'Wrong value for word 0'); assert(memory.items[1] == 0x00, 'Wrong value for word 1'); - assert(memory.size() == 32, 'Wrong memory length'); } #[test] @@ -1185,7 +1144,6 @@ mod tests { // Then assert(memory.items[0] == 0xFFFF, 'Wrong value for word 0'); assert(memory.items[1] == 0x01FF, 'Wrong value for word 1'); - assert(memory.size() == 32, 'Wrong memory length'); } #[test] @@ -1200,7 +1158,6 @@ mod tests { assert(memory.items[0] == 0x0, 'Wrong value for word 0'); assert(memory.items[1] == 0x0, 'Wrong value for word 1'); assert(memory.items[2] == 0x01000000000000000000000000000000, 'Wrong value for word 2'); - assert(memory.size() == 64, 'Wrong memory length'); } #[test] @@ -1216,6 +1173,5 @@ mod tests { // Then assert(memory.items[0] == 0x0100, 'Wrong value in word 0'); assert(memory.items[1] == 0xffABffffffffffffffffffffffffffff, 'Wrong value in word 1'); - assert(memory.size() == 32, 'Wrong memory length'); } } diff --git a/crates/evm/src/precompiles/ec_recover.cairo b/crates/evm/src/precompiles/ec_recover.cairo index 7774b3db6..1336de202 100644 --- a/crates/evm/src/precompiles/ec_recover.cairo +++ b/crates/evm/src/precompiles/ec_recover.cairo @@ -80,7 +80,9 @@ mod tests { use evm::precompiles::ec_recover::EcRecover; use evm::stack::StackTrait; use evm::test_utils::setup_test_storages; - use evm::test_utils::{VMBuilderTrait, native_token, other_starknet_address}; + use evm::test_utils::{ + VMBuilderTrait, MemoryTestUtilsTrait, native_token, other_starknet_address + }; use snforge_std::{start_mock_call, test_address}; use utils::helpers::{U256Trait, ToBytes, FromBytes}; @@ -122,7 +124,7 @@ mod tests { .store( 0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3, 0x0 ); // msg_hash - vm.memory.store(0x1C, 0x20); // v + vm.memory.store_with_expansion(0x1C, 0x20); // v vm .memory .store(0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608, 0x40); // r diff --git a/crates/evm/src/precompiles/identity.cairo b/crates/evm/src/precompiles/identity.cairo index c9f602f33..9e478f013 100644 --- a/crates/evm/src/precompiles/identity.cairo +++ b/crates/evm/src/precompiles/identity.cairo @@ -32,7 +32,8 @@ mod tests { use evm::precompiles::identity::Identity; use evm::stack::StackTrait; use evm::test_utils::{ - VMBuilderTrait, native_token, other_starknet_address, setup_test_storages + VMBuilderTrait, MemoryTestUtilsTrait, native_token, other_starknet_address, + setup_test_storages }; use snforge_std::{start_mock_call, test_address}; @@ -64,7 +65,7 @@ mod tests { vm.stack.push(0x4).unwrap(); // address vm.stack.push(0xFFFFFFFF).unwrap(); // gas - vm.memory.store(0x2A, 0x1F); + vm.memory.store_with_expansion(0x2A, 0x1F); start_mock_call::(native_token(), selector!("balanceOf"), 0); vm.exec_staticcall().unwrap(); diff --git a/crates/evm/src/precompiles/sha256.cairo b/crates/evm/src/precompiles/sha256.cairo index 437f910f5..489fc9a15 100644 --- a/crates/evm/src/precompiles/sha256.cairo +++ b/crates/evm/src/precompiles/sha256.cairo @@ -62,7 +62,8 @@ mod tests { use evm::precompiles::sha256::Sha256; use evm::stack::StackTrait; use evm::test_utils::{ - VMBuilderTrait, native_token, other_starknet_address, setup_test_storages + VMBuilderTrait, MemoryTestUtilsTrait, native_token, other_starknet_address, + setup_test_storages }; use snforge_std::{start_mock_call}; use utils::helpers::ToBytes; @@ -165,7 +166,7 @@ mod tests { vm.stack.push(0x2).unwrap(); // address vm.stack.push(0xFFFFFFFF).unwrap(); // gas - vm.memory.store(0xFF, 0x0); + vm.memory.store_with_expansion(0xFF, 0x0); start_mock_call::(native_token(), selector!("balanceOf"), 0); vm.exec_staticcall().unwrap(); diff --git a/crates/evm/src/test_utils.cairo b/crates/evm/src/test_utils.cairo index 19c016792..f64eccd4f 100644 --- a/crates/evm/src/test_utils.cairo +++ b/crates/evm/src/test_utils.cairo @@ -49,6 +49,19 @@ fn register_account(evm_address: EthAddress, starknet_address: ContractAddress) } +#[generate_trait] +impl MemoryUtilsImpl of MemoryTestUtilsTrait { + fn store_with_expansion(ref self: Memory, element: u256, offset: usize) { + self.ensure_length(offset + 32); + self.store(element, offset); + } + + fn store_n_with_expansion(ref self: Memory, elements: Span, offset: usize) { + self.ensure_length(offset + elements.len()); + self.store_n(elements, offset); + } +} + #[derive(Destruct)] struct VMBuilder { vm: VM diff --git a/scripts/gas_debug_call.py b/scripts/gas_debug_call.py index c3cda12ae..f9a5a49a7 100644 --- a/scripts/gas_debug_call.py +++ b/scripts/gas_debug_call.py @@ -2,42 +2,37 @@ def process_logs(logs): - current_address = None - previous_gas = None - accumulated_gas = 0 + gas_consumption = {} + previous_gas = {} - pattern = re.compile(r"Address (\d+), gas left in call (\d+)") + pattern = re.compile( + r"Address (\d+), opcode (\w+), pc (\d+), gas left in call (\d+)" + ) for line in logs.split("\n"): match = pattern.search(line) if match: - address, gas_left = match.groups() + address, opcode, pc, gas_left = match.groups() + address = int(address) + pc = int(pc) gas_left = int(gas_left) - if address != current_address: - if current_address is not None: - print( - f"Total gas used for {hex(int(current_address))}: {accumulated_gas}" - ) - current_address = address - previous_gas = gas_left - accumulated_gas = 0 + if address not in gas_consumption: + gas_consumption[address] = 0 + previous_gas[address] = gas_left else: - gas_used = previous_gas - gas_left - accumulated_gas += gas_used - print( - f"Gas used in step for {hex(int(current_address))}: {gas_used} (Total: {accumulated_gas})" - ) - previous_gas = gas_left + gas_used = previous_gas[address] - gas_left + gas_consumption[address] += gas_used + previous_gas[address] = gas_left - if current_address is not None: - print(f"Total gas used for {hex(int(current_address))}: {accumulated_gas}") + print( + f"{hex(address)} - {pc} - {opcode} --> total gas used: {gas_consumption[address]}" + ) # Example usage logs = """ -Address 1169201309864722334562947866173026415724746034380, gas left in call 79978528 -Address 1169201309864722334562947866173026415724746034380, gas left in call 79978525 +Address 1169201309864722334562947866173026415724746034380, opcode 96, pc 1, gas left in call 79978644 """ process_logs(logs)