From 3396ce8eaf59d4d5fed09d27cd32a7ee4095a5a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Arjovsky?= Date: Thu, 5 Dec 2024 17:47:44 +0100 Subject: [PATCH 1/4] feat(l1): new pooled transaction hashes (#1373) Changes: - Parses and handles new pooled transactions incoming message. - Sends a get pooled transactions for missing transactions. Checks the mempool to not ask for already present ones. This also adds the NewPooledTx hive test. The next PR will add support for the PooledTransactions response so we can fully exchange mempools with other nodes + receive blob transactions via p2p. Closes #383 --- .github/workflows/ci_l1.yaml | 2 +- crates/networking/p2p/rlpx/connection.rs | 17 ++++++++++++++-- .../networking/p2p/rlpx/eth/transactions.rs | 13 ++++++++---- crates/networking/p2p/rlpx/message.rs | 20 ++++++++++++++++++- crates/storage/store/storage.rs | 20 ++++++++++++++++++- 5 files changed, 63 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci_l1.yaml b/.github/workflows/ci_l1.yaml index 3c495456f..9ebf98baf 100644 --- a/.github/workflows/ci_l1.yaml +++ b/.github/workflows/ci_l1.yaml @@ -157,7 +157,7 @@ jobs: test_pattern: /AccountRange|StorageRanges|ByteCodes|TrieNodes - name: "Devp2p eth tests" simulation: devp2p - test_pattern: eth/Status|GetBlockHeaders|SimultaneousRequests|SameRequestID|ZeroRequestID|GetBlockBodies|MaliciousHandshake|MaliciousStatus|Transaction|InvalidTxs + test_pattern: eth/Status|GetBlockHeaders|SimultaneousRequests|SameRequestID|ZeroRequestID|GetBlockBodies|MaliciousHandshake|MaliciousStatus|Transaction|InvalidTxs|NewPooledTxs - name: "Engine Auth and EC tests" simulation: ethereum/engine test_pattern: engine-(auth|exchange-capabilities)/ diff --git a/crates/networking/p2p/rlpx/connection.rs b/crates/networking/p2p/rlpx/connection.rs index 1d4e5e09a..0e97fcd26 100644 --- a/crates/networking/p2p/rlpx/connection.rs +++ b/crates/networking/p2p/rlpx/connection.rs @@ -22,6 +22,7 @@ use crate::{ use super::{ error::RLPxError, + eth::transactions::GetPooledTransactions, frame, handshake::{decode_ack_message, decode_auth_message, encode_auth_message}, message::{self as rlpx}, @@ -29,7 +30,7 @@ use super::{ utils::{ecdh_xchng, pubkey2id}, }; use aes::cipher::KeyIvInit; -use ethrex_blockchain::mempool; +use ethrex_blockchain::mempool::{self}; use ethrex_core::{H256, H512}; use ethrex_rlp::decode::RLPDecode; use ethrex_storage::Store; @@ -37,6 +38,7 @@ use k256::{ ecdsa::{RecoveryId, Signature, SigningKey, VerifyingKey}, PublicKey, SecretKey, }; +use rand::random; use sha3::{Digest, Keccak256}; use tokio::{ io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}, @@ -337,7 +339,7 @@ impl RLPxConnection { match message { Message::Disconnect(msg_data) => { debug!("Received Disconnect: {:?}", msg_data.reason); - // Returning a Disonnect error to be handled later at the call stack + // Returning a Disconnect error to be handled later at the call stack return Err(RLPxError::Disconnect()); } Message::Ping(_) => { @@ -377,6 +379,17 @@ impl RLPxConnection { }; self.send(Message::BlockBodies(response)).await?; } + Message::NewPooledTransactionHashes(new_pooled_transaction_hashes) + if peer_supports_eth => + { + //TODO(#1415): evaluate keeping track of requests to avoid sending the same twice. + let hashes = + new_pooled_transaction_hashes.get_transactions_to_request(&self.storage)?; + + //TODO(#1416): Evaluate keeping track of the request-id. + let request = GetPooledTransactions::new(random(), hashes); + self.send(Message::GetPooledTransactions(request)).await?; + } Message::GetStorageRanges(req) => { let response = process_storage_ranges_request(req, self.storage.clone())?; self.send(Message::StorageRanges(response)).await? diff --git a/crates/networking/p2p/rlpx/eth/transactions.rs b/crates/networking/p2p/rlpx/eth/transactions.rs index 0f2af96b0..0f8a614c4 100644 --- a/crates/networking/p2p/rlpx/eth/transactions.rs +++ b/crates/networking/p2p/rlpx/eth/transactions.rs @@ -1,9 +1,11 @@ use bytes::BufMut; +use bytes::Bytes; use ethrex_core::{types::Transaction, H256}; use ethrex_rlp::{ error::{RLPDecodeError, RLPEncodeError}, structs::{Decoder, Encoder}, }; +use ethrex_storage::{error::StoreError, Store}; use crate::rlpx::{ message::RLPxMessage, @@ -64,7 +66,7 @@ impl RLPxMessage for Transactions { // Broadcast message #[derive(Debug)] pub(crate) struct NewPooledTransactionHashes { - transaction_types: Vec, + transaction_types: Bytes, transaction_sizes: Vec, transaction_hashes: Vec, } @@ -88,11 +90,15 @@ impl NewPooledTransactionHashes { transaction_hashes.push(transaction_hash); } Self { - transaction_types, + transaction_types: transaction_types.into(), transaction_sizes, transaction_hashes, } } + + pub fn get_transactions_to_request(&self, storage: &Store) -> Result, StoreError> { + storage.filter_unknown_transactions(&self.transaction_hashes) + } } impl RLPxMessage for NewPooledTransactionHashes { @@ -112,8 +118,7 @@ impl RLPxMessage for NewPooledTransactionHashes { fn decode(msg_data: &[u8]) -> Result { let decompressed_data = snappy_decompress(msg_data)?; let decoder = Decoder::new(&decompressed_data)?; - let (transaction_types, decoder): (Vec, _) = - decoder.decode_field("transactionTypes")?; + let (transaction_types, decoder): (Bytes, _) = decoder.decode_field("transactionTypes")?; let (transaction_sizes, decoder): (Vec, _) = decoder.decode_field("transactionSizes")?; let (transaction_hashes, _): (Vec, _) = decoder.decode_field("transactionHashes")?; diff --git a/crates/networking/p2p/rlpx/message.rs b/crates/networking/p2p/rlpx/message.rs index 377d6966a..7176cf96b 100644 --- a/crates/networking/p2p/rlpx/message.rs +++ b/crates/networking/p2p/rlpx/message.rs @@ -5,7 +5,7 @@ use std::fmt::Display; use super::eth::blocks::{BlockBodies, BlockHeaders, GetBlockBodies, GetBlockHeaders}; use super::eth::receipts::Receipts; use super::eth::status::StatusMessage; -use super::eth::transactions::Transactions; +use super::eth::transactions::{GetPooledTransactions, NewPooledTransactionHashes, Transactions}; use super::p2p::{DisconnectMessage, HelloMessage, PingMessage, PongMessage}; use super::snap::{ AccountRange, ByteCodes, GetAccountRange, GetByteCodes, GetStorageRanges, GetTrieNodes, @@ -32,6 +32,8 @@ pub(crate) enum Message { Transactions(Transactions), GetBlockBodies(GetBlockBodies), BlockBodies(BlockBodies), + NewPooledTransactionHashes(NewPooledTransactionHashes), + GetPooledTransactions(GetPooledTransactions), Receipts(Receipts), // snap capability GetAccountRange(GetAccountRange), @@ -67,6 +69,12 @@ impl Message { 0x14 => Ok(Message::BlockHeaders(BlockHeaders::decode(msg_data)?)), 0x15 => Ok(Message::GetBlockBodies(GetBlockBodies::decode(msg_data)?)), 0x16 => Ok(Message::BlockBodies(BlockBodies::decode(msg_data)?)), + 0x18 => Ok(Message::NewPooledTransactionHashes( + NewPooledTransactionHashes::decode(msg_data)?, + )), + 0x19 => Ok(Message::GetPooledTransactions( + GetPooledTransactions::decode(msg_data)?, + )), 0x20 => Ok(Message::Receipts(Receipts::decode(msg_data)?)), 0x21 => Ok(Message::GetAccountRange(GetAccountRange::decode(msg_data)?)), 0x22 => Ok(Message::AccountRange(AccountRange::decode(msg_data)?)), @@ -124,6 +132,14 @@ impl Message { 0x16_u8.encode(buf); msg.encode(buf) } + Message::NewPooledTransactionHashes(msg) => { + 0x18_u8.encode(buf); + msg.encode(buf) + } + Message::GetPooledTransactions(msg) => { + 0x19_u8.encode(buf); + msg.encode(buf) + } Message::Receipts(msg) => { 0x20_u8.encode(buf); msg.encode(buf) @@ -175,6 +191,8 @@ impl Display for Message { Message::GetBlockHeaders(_) => "eth:getBlockHeaders".fmt(f), Message::BlockHeaders(_) => "eth:BlockHeaders".fmt(f), Message::BlockBodies(_) => "eth:BlockBodies".fmt(f), + Message::NewPooledTransactionHashes(_) => "eth:NewPooledTransactionHashes".fmt(f), + Message::GetPooledTransactions(_) => "eth::GetPooledTransactions".fmt(f), Message::Transactions(_) => "eth:TransactionsMessage".fmt(f), Message::GetBlockBodies(_) => "eth:GetBlockBodies".fmt(f), Message::Receipts(_) => "eth:Receipts".fmt(f), diff --git a/crates/storage/store/storage.rs b/crates/storage/store/storage.rs index 0a012b8d5..aa169b777 100644 --- a/crates/storage/store/storage.rs +++ b/crates/storage/store/storage.rs @@ -17,7 +17,7 @@ use ethrex_rlp::encode::RLPEncode; use ethrex_trie::Trie; use serde::{Deserialize, Serialize}; use sha3::{Digest as _, Keccak256}; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::fmt::Debug; use std::sync::{Arc, Mutex}; use tracing::info; @@ -325,6 +325,24 @@ impl Store { Ok(txs_by_sender) } + /// Gets hashes from possible_hashes that are not already known in the mempool. + pub fn filter_unknown_transactions( + &self, + possible_hashes: &[H256], + ) -> Result, StoreError> { + let mempool = self + .mempool + .lock() + .map_err(|error| StoreError::Custom(error.to_string()))?; + + let tx_set: HashSet<_> = mempool.iter().map(|(hash, _)| hash).collect(); + Ok(possible_hashes + .iter() + .filter(|hash| !tx_set.contains(hash)) + .copied() + .collect()) + } + fn add_account_code(&self, code_hash: H256, code: Bytes) -> Result<(), StoreError> { self.engine.add_account_code(code_hash, code) } From 6777bfb26d5f3ce2258b11733c1bf4875053d328 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jerem=C3=ADas=20Salom=C3=B3n?= <48994069+JereSalo@users.noreply.github.com> Date: Thu, 5 Dec 2024 15:47:31 -0300 Subject: [PATCH 2/4] fix(levm): ginitcodeword specification, fix gas (#1402) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Motivation** **Description** - `number_of_words` is now calculated in a more performant way, without iterating calldata. - The initial_call_frame in Create is now initialized with empty bytecode and the corresponding calldata send in the transaction. -> After validations the calldata will be assigned to the bytecode and it will be erased. This is mostly for using calldata for calculating some gas costs. Closes #1362, #1363 Running tests: - ✓ Summary: 1547/4100 (37.73) --- crates/vm/levm/src/vm.rs | 21 ++++++++++----------- crates/vm/levm/tests/edge_case_tests.rs | 2 ++ 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/crates/vm/levm/src/vm.rs b/crates/vm/levm/src/vm.rs index 63e7cc5ce..f103ca860 100644 --- a/crates/vm/levm/src/vm.rs +++ b/crates/vm/levm/src/vm.rs @@ -138,15 +138,13 @@ impl VM { let created_contract = Account::new(value, calldata.clone(), 1, HashMap::new()); cache::insert_account(&mut cache, new_contract_address, created_contract); - let bytecode: Bytes = calldata.clone(); - let initial_call_frame = CallFrame::new( env.origin, new_contract_address, new_contract_address, - bytecode, + Bytes::new(), // Bytecode is assigned after passing validations. value, - Bytes::new(), // Contract creation does not have calldata + calldata, // Calldata is removed after passing validations. false, env.gas_limit, U256::zero(), @@ -378,12 +376,7 @@ impl VM { .checked_add(CREATE_BASE_COST) .ok_or(OutOfGasError::ConsumedGasOverflow)?; - let number_of_words: u64 = initial_call_frame - .calldata - .chunks(WORD_SIZE) - .len() - .try_into() - .map_err(|_| InternalError::ConversionError)?; + let number_of_words = initial_call_frame.calldata.len().div_ceil(WORD_SIZE); intrinsic_gas = intrinsic_gas .checked_add( @@ -493,7 +486,7 @@ impl VM { // (4) INITCODE_SIZE_EXCEEDED if self.is_create() { // INITCODE_SIZE_EXCEEDED - if initial_call_frame.bytecode.len() > INIT_CODE_MAX_SIZE { + if initial_call_frame.calldata.len() > INIT_CODE_MAX_SIZE { return Err(VMError::TxValidation( TxValidationError::InitcodeSizeExceeded, )); @@ -589,6 +582,12 @@ impl VM { let cache_before_execution = self.cache.clone(); self.validate_transaction(&mut initial_call_frame)?; + if self.is_create() { + // Assign bytecode to context and empty calldata + initial_call_frame.bytecode = initial_call_frame.calldata.clone(); + initial_call_frame.calldata = Bytes::new(); + } + // Maybe can be done in validate_transaction let sender = initial_call_frame.msg_sender; let receiver_address = initial_call_frame.to; diff --git a/crates/vm/levm/tests/edge_case_tests.rs b/crates/vm/levm/tests/edge_case_tests.rs index 6a4456e3d..f390ed11c 100644 --- a/crates/vm/levm/tests/edge_case_tests.rs +++ b/crates/vm/levm/tests/edge_case_tests.rs @@ -1,3 +1,5 @@ +// Here add #![allow(clippy::)] if necessary, we don't want to lint the test code. + use std::str::FromStr; use bytes::Bytes; From fc489fe6a77f77b5118d467364c5d2580a9029f0 Mon Sep 17 00:00:00 2001 From: Damian Ramirez Date: Thu, 5 Dec 2024 15:51:50 -0300 Subject: [PATCH 3/4] fix(levm): fix EF test of past PR (#1417) **Motivation** This PR increases the amount of EF test passed. There was some mistakes in PR [#1398](https://github.com/lambdaclass/ethrex/pull/1398). **Description** - Check if the "quotient" is negative and negate it in that case. - The implementation of `gas_cost::exp()` was wrong. Forgot to use `U256::bits()`. --- crates/vm/levm/src/gas_cost.rs | 13 +++++++------ .../vm/levm/src/opcode_handlers/arithmetic.rs | 17 ++++++++++++----- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/crates/vm/levm/src/gas_cost.rs b/crates/vm/levm/src/gas_cost.rs index 944d97c22..188562d76 100644 --- a/crates/vm/levm/src/gas_cost.rs +++ b/crates/vm/levm/src/gas_cost.rs @@ -159,15 +159,16 @@ pub const CALLDATA_COST_NON_ZERO_BYTE: U256 = U256([16, 0, 0, 0]); pub const BLOB_GAS_PER_BLOB: U256 = U256([131072, 0, 0, 0]); pub fn exp(exponent: U256) -> Result { - let exponent_byte_size = exponent - .checked_add(U256::from(7)) - .ok_or(OutOfGasError::GasCostOverflow)? - .checked_div(U256::from(8)) - .ok_or(OutOfGasError::ArithmeticOperationDividedByZero)?; // '8' will never be zero + let exponent_byte_size = (exponent + .bits() + .checked_add(7) + .ok_or(OutOfGasError::GasCostOverflow)?) + / 8; let exponent_byte_size_cost = EXP_DYNAMIC_BASE - .checked_mul(exponent_byte_size) + .checked_mul(exponent_byte_size.into()) .ok_or(OutOfGasError::GasCostOverflow)?; + EXP_STATIC .checked_add(exponent_byte_size_cost) .ok_or(OutOfGasError::GasCostOverflow) diff --git a/crates/vm/levm/src/opcode_handlers/arithmetic.rs b/crates/vm/levm/src/opcode_handlers/arithmetic.rs index a12642868..2ed77cf8f 100644 --- a/crates/vm/levm/src/opcode_handlers/arithmetic.rs +++ b/crates/vm/levm/src/opcode_handlers/arithmetic.rs @@ -76,11 +76,18 @@ impl VM { return Ok(OpcodeSuccess::Continue); } - let dividend = abs(dividend); - let divisor = abs(divisor); - - let quotient = match dividend.checked_div(divisor) { - Some(quot) => quot, + let abs_dividend = abs(dividend); + let abs_divisor = abs(divisor); + + let quotient = match abs_dividend.checked_div(abs_divisor) { + Some(quot) => { + let quotient_is_negative = is_negative(dividend) ^ is_negative(divisor); + if quotient_is_negative { + negate(quot) + } else { + quot + } + } None => U256::zero(), }; From 0d11b5a19d3c5d61619206779f3ad79b954a7e70 Mon Sep 17 00:00:00 2001 From: Ivan Litteri <67517699+ilitteri@users.noreply.github.com> Date: Thu, 5 Dec 2024 15:58:19 -0300 Subject: [PATCH 4/4] refactor(levm): memory module (#1403) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Motivation** - The `Memory` implementation was similar to type aliasing a `Vec`. The current implementation unnecessarily adds an extra layer of indirection. - The current `Memory` implementation has some bugs and not all its methods are panic safe. **Description** - `Memory` is now a type alias for `Vec`. - All the `Memory` "methods" are implemented as functions in the `memory` module and called taking advantage of the namespacing (e.g. `memory::load_word(...)`). - All the `Memory` functions are panic safe. - Memory access and expansion cost functions were refactored and moved to the `memory` module. As a side note, in a future refactor we will be updating the gas cost type from `U256` to `usize`, this PR kind of introduces this notion, but it turns the `usize`s into `U256` wherever is necessary for backward compatibility the implementation. --------- Co-authored-by: Maximo Palopoli <96491141+maximopalopoli@users.noreply.github.com> Co-authored-by: Jeremías Salomón <48994069+JereSalo@users.noreply.github.com> Co-authored-by: maximopalopoli --- crates/vm/levm/src/errors.rs | 2 + crates/vm/levm/src/gas_cost.rs | 234 +++++------- crates/vm/levm/src/memory.rs | 354 ++++++++---------- .../levm/src/opcode_handlers/environment.rs | 185 +++++---- crates/vm/levm/src/opcode_handlers/keccak.rs | 31 +- crates/vm/levm/src/opcode_handlers/logging.rs | 25 +- .../stack_memory_storage_flow.rs | 98 +++-- crates/vm/levm/src/opcode_handlers/system.rs | 103 ++--- crates/vm/levm/src/vm.rs | 27 +- crates/vm/levm/tests/tests.rs | 62 ++- 10 files changed, 544 insertions(+), 577 deletions(-) diff --git a/crates/vm/levm/src/errors.rs b/crates/vm/levm/src/errors.rs index c56a3f428..99ff007ed 100644 --- a/crates/vm/levm/src/errors.rs +++ b/crates/vm/levm/src/errors.rs @@ -78,6 +78,8 @@ pub enum VMError { Internal(#[from] InternalError), #[error("Transaction validation error: {0}")] TxValidation(#[from] TxValidationError), + #[error("Offset out of bounds")] + OutOfOffset, } impl VMError { diff --git a/crates/vm/levm/src/gas_cost.rs b/crates/vm/levm/src/gas_cost.rs index 188562d76..8f9eecc3d 100644 --- a/crates/vm/levm/src/gas_cost.rs +++ b/crates/vm/levm/src/gas_cost.rs @@ -1,6 +1,5 @@ use crate::{ - call_frame::CallFrame, - constants::{WORD_SIZE, WORD_SIZE_IN_BYTES}, + constants::WORD_SIZE, errors::{InternalError, OutOfGasError, VMError}, memory, StorageSlot, }; @@ -175,93 +174,93 @@ pub fn exp(exponent: U256) -> Result { } pub fn calldatacopy( - current_call_frame: &CallFrame, + new_memory_size: usize, + current_memory_size: usize, size: usize, - dest_offset: usize, -) -> Result { +) -> Result { copy_behavior( + new_memory_size, + current_memory_size, + size, CALLDATACOPY_DYNAMIC_BASE, CALLDATACOPY_STATIC, - current_call_frame, - size, - dest_offset, ) } pub fn codecopy( - current_call_frame: &CallFrame, + new_memory_size: usize, + current_memory_size: usize, size: usize, - dest_offset: usize, -) -> Result { +) -> Result { copy_behavior( + new_memory_size, + current_memory_size, + size, CODECOPY_DYNAMIC_BASE, CODECOPY_STATIC, - current_call_frame, - size, - dest_offset, ) } pub fn returndatacopy( - current_call_frame: &CallFrame, + new_memory_size: usize, + current_memory_size: usize, size: usize, - dest_offset: usize, -) -> Result { +) -> Result { copy_behavior( + new_memory_size, + current_memory_size, + size, RETURNDATACOPY_DYNAMIC_BASE, RETURNDATACOPY_STATIC, - current_call_frame, - size, - dest_offset, ) } fn copy_behavior( + new_memory_size: usize, + current_memory_size: usize, + size: usize, dynamic_base: U256, static_cost: U256, - current_call_frame: &CallFrame, - size: usize, - offset: usize, -) -> Result { +) -> Result { let minimum_word_size = (size .checked_add(WORD_SIZE) .ok_or(OutOfGasError::GasCostOverflow)? .saturating_sub(1)) / WORD_SIZE; - let memory_expansion_cost = current_call_frame.memory.expansion_cost(offset, size)?; + let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?; let minimum_word_size_cost = dynamic_base .checked_mul(minimum_word_size.into()) .ok_or(OutOfGasError::GasCostOverflow)?; - static_cost + Ok(static_cost .checked_add(minimum_word_size_cost) .ok_or(OutOfGasError::GasCostOverflow)? - .checked_add(memory_expansion_cost) - .ok_or(OutOfGasError::GasCostOverflow) + .checked_add(memory_expansion_cost.into()) + .ok_or(OutOfGasError::GasCostOverflow)?) } pub fn keccak256( - current_call_frame: &CallFrame, + new_memory_size: usize, + current_memory_size: usize, size: usize, - offset: usize, -) -> Result { +) -> Result { copy_behavior( + new_memory_size, + current_memory_size, + size, KECCAK25_DYNAMIC_BASE, KECCAK25_STATIC, - current_call_frame, - size, - offset, ) } pub fn log( - current_call_frame: &CallFrame, + new_memory_size: usize, + current_memory_size: usize, size: usize, - offset: usize, number_of_topics: u8, -) -> Result { - let memory_expansion_cost = current_call_frame.memory.expansion_cost(offset, size)?; +) -> Result { + let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?; let topics_cost = LOGN_DYNAMIC_BASE .checked_mul(number_of_topics.into()) @@ -269,39 +268,36 @@ pub fn log( let bytes_cost = LOGN_DYNAMIC_BYTE_BASE .checked_mul(size.into()) .ok_or(OutOfGasError::GasCostOverflow)?; - topics_cost + Ok(topics_cost .checked_add(LOGN_STATIC) .ok_or(OutOfGasError::GasCostOverflow)? .checked_add(bytes_cost) .ok_or(OutOfGasError::GasCostOverflow)? - .checked_add(memory_expansion_cost) - .ok_or(OutOfGasError::GasCostOverflow) + .checked_add(memory_expansion_cost.into()) + .ok_or(OutOfGasError::GasCostOverflow)?) } -pub fn mload(current_call_frame: &CallFrame, offset: usize) -> Result { - mem_expansion_behavior(current_call_frame, offset, WORD_SIZE, MLOAD_STATIC) +pub fn mload(new_memory_size: usize, current_memory_size: usize) -> Result { + mem_expansion_behavior(new_memory_size, current_memory_size, MLOAD_STATIC) } -pub fn mstore(current_call_frame: &CallFrame, offset: usize) -> Result { - mem_expansion_behavior(current_call_frame, offset, WORD_SIZE, MSTORE_STATIC) +pub fn mstore(new_memory_size: usize, current_memory_size: usize) -> Result { + mem_expansion_behavior(new_memory_size, current_memory_size, MSTORE_STATIC) } -pub fn mstore8(current_call_frame: &CallFrame, offset: usize) -> Result { - mem_expansion_behavior(current_call_frame, offset, 1, MSTORE8_STATIC) +pub fn mstore8(new_memory_size: usize, current_memory_size: usize) -> Result { + mem_expansion_behavior(new_memory_size, current_memory_size, MSTORE8_STATIC) } fn mem_expansion_behavior( - current_call_frame: &CallFrame, - offset: usize, - offset_add: usize, + new_memory_size: usize, + current_memory_size: usize, static_cost: U256, -) -> Result { - let memory_expansion_cost = current_call_frame - .memory - .expansion_cost(offset, offset_add)?; - static_cost - .checked_add(memory_expansion_cost) - .ok_or(OutOfGasError::GasCostOverflow) +) -> Result { + let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?; + Ok(static_cost + .checked_add(memory_expansion_cost.into()) + .ok_or(OutOfGasError::GasCostOverflow)?) } pub fn sload(storage_slot_was_cold: bool) -> Result { @@ -349,74 +345,61 @@ pub fn sstore( } pub fn mcopy( - current_call_frame: &CallFrame, + new_memory_size: usize, + current_memory_size: usize, size: usize, - src_offset: usize, - dest_offset: usize, -) -> Result { +) -> Result { let words_copied = (size .checked_add(WORD_SIZE) .ok_or(OutOfGasError::GasCostOverflow)? .saturating_sub(1)) / WORD_SIZE; - let src_sum = src_offset - .checked_add(size) - .ok_or(OutOfGasError::GasCostOverflow)?; - let dest_sum = dest_offset - .checked_add(size) - .ok_or(OutOfGasError::GasCostOverflow)?; - - let memory_expansion_cost = if src_sum > dest_sum { - current_call_frame.memory.expansion_cost(src_offset, size)? - } else { - current_call_frame - .memory - .expansion_cost(dest_offset, size)? - }; + let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?; let copied_words_cost = MCOPY_DYNAMIC_BASE .checked_mul(words_copied.into()) .ok_or(OutOfGasError::GasCostOverflow)?; - MCOPY_STATIC + + Ok(MCOPY_STATIC .checked_add(copied_words_cost) .ok_or(OutOfGasError::GasCostOverflow)? - .checked_add(memory_expansion_cost) - .ok_or(OutOfGasError::GasCostOverflow) + .checked_add(memory_expansion_cost.into()) + .ok_or(OutOfGasError::GasCostOverflow)?) } pub fn create( - current_call_frame: &mut CallFrame, - code_offset_in_memory: usize, + new_memory_size: usize, + current_memory_size: usize, code_size_in_memory: usize, -) -> Result { +) -> Result { compute_gas_create( - current_call_frame, - code_offset_in_memory, + new_memory_size, + current_memory_size, code_size_in_memory, false, ) } pub fn create_2( - current_call_frame: &mut CallFrame, - code_offset_in_memory: usize, + new_memory_size: usize, + current_memory_size: usize, code_size_in_memory: usize, -) -> Result { +) -> Result { compute_gas_create( - current_call_frame, - code_offset_in_memory, + new_memory_size, + current_memory_size, code_size_in_memory, true, ) } fn compute_gas_create( - current_call_frame: &mut CallFrame, - code_offset_in_memory: usize, + new_memory_size: usize, + current_memory_size: usize, code_size_in_memory: usize, is_create_2: bool, -) -> Result { +) -> Result { let minimum_word_size = (code_size_in_memory .checked_add(31) .ok_or(OutOfGasError::GasCostOverflow)?) @@ -431,9 +414,7 @@ fn compute_gas_create( .checked_mul(CODE_DEPOSIT_COST.as_usize()) // will not panic since it's 200 .ok_or(OutOfGasError::GasCostOverflow)?; - let memory_expansion_cost = current_call_frame - .memory - .expansion_cost(code_offset_in_memory, code_size_in_memory)?; + let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?; let hash_cost = if is_create_2 { minimum_word_size @@ -443,7 +424,7 @@ fn compute_gas_create( 0 }; - memory_expansion_cost + Ok(U256::from(memory_expansion_cost) .checked_add(init_code_cost.into()) .ok_or(OutOfGasError::CreationCostIsTooHigh)? .checked_add(code_deposit_cost.into()) @@ -451,7 +432,7 @@ fn compute_gas_create( .checked_add(CREATE_BASE_COST) .ok_or(OutOfGasError::CreationCostIsTooHigh)? .checked_add(hash_cost.into()) - .ok_or(OutOfGasError::CreationCostIsTooHigh) + .ok_or(OutOfGasError::CreationCostIsTooHigh)?) } pub fn selfdestruct(address_was_cold: bool, account_is_empty: bool) -> Result { @@ -525,37 +506,6 @@ fn address_access_cost( .ok_or(OutOfGasError::GasCostOverflow)?) } -fn memory_access_cost( - new_memory_size: U256, - current_memory_size: U256, - static_cost: U256, - dynamic_base_cost: U256, -) -> Result { - let minimum_word_size = new_memory_size - .checked_add( - WORD_SIZE_IN_BYTES - .checked_sub(U256::one()) - .ok_or(InternalError::ArithmeticOperationUnderflow)?, - ) - .ok_or(OutOfGasError::MemoryExpansionCostOverflow)? - .checked_div(WORD_SIZE_IN_BYTES) - .ok_or(OutOfGasError::MemoryExpansionCostOverflow)?; - - let static_gas = static_cost; - let dynamic_cost = dynamic_base_cost - .checked_mul(minimum_word_size) - .ok_or(OutOfGasError::MemoryExpansionCostOverflow)? - .checked_add(memory::expansion_cost( - new_memory_size, - current_memory_size, - )?) - .ok_or(OutOfGasError::MemoryExpansionCostOverflow)?; - - Ok(static_gas - .checked_add(dynamic_cost) - .ok_or(OutOfGasError::GasCostOverflow)?) -} - pub fn balance(address_was_cold: bool) -> Result { address_access_cost( address_was_cold, @@ -575,11 +525,11 @@ pub fn extcodesize(address_was_cold: bool) -> Result { } pub fn extcodecopy( - new_memory_size: U256, - current_memory_size: U256, + new_memory_size: usize, + current_memory_size: usize, address_was_cold: bool, ) -> Result { - Ok(memory_access_cost( + Ok(memory::access_cost( new_memory_size, current_memory_size, EXTCODECOPY_STATIC, @@ -604,8 +554,8 @@ pub fn extcodehash(address_was_cold: bool) -> Result { } pub fn call( - new_memory_size: U256, - current_memory_size: U256, + new_memory_size: usize, + current_memory_size: usize, address_was_cold: bool, address_is_empty: bool, value_to_transfer: U256, @@ -633,7 +583,7 @@ pub fn call( }; // Note: code_execution_cost will be charged from the sub context post-state. - let dynamic_gas = memory_expansion_cost + let dynamic_gas = U256::from(memory_expansion_cost) .checked_add(address_access_cost) .ok_or(OutOfGasError::GasCostOverflow)? .checked_add(positive_value_cost) @@ -647,8 +597,8 @@ pub fn call( } pub fn callcode( - new_memory_size: U256, - current_memory_size: U256, + new_memory_size: usize, + current_memory_size: usize, address_was_cold: bool, value_to_transfer: U256, ) -> Result { @@ -670,7 +620,7 @@ pub fn callcode( }; // Note: code_execution_cost will be charged from the sub context post-state. - let dynamic_gas = memory_expansion_cost + let dynamic_gas = U256::from(memory_expansion_cost) .checked_add(address_access_cost) .ok_or(OutOfGasError::GasCostOverflow)? .checked_add(positive_value_cost) @@ -682,8 +632,8 @@ pub fn callcode( } pub fn delegatecall( - new_memory_size: U256, - current_memory_size: U256, + new_memory_size: usize, + current_memory_size: usize, address_was_cold: bool, ) -> Result { let static_gas = DELEGATECALL_STATIC; @@ -697,7 +647,7 @@ pub fn delegatecall( )?; // Note: code_execution_cost will be charged from the sub context post-state. - let dynamic_gas = memory_expansion_cost + let dynamic_gas = U256::from(memory_expansion_cost) .checked_add(address_access_cost) .ok_or(OutOfGasError::GasCostOverflow)?; @@ -707,8 +657,8 @@ pub fn delegatecall( } pub fn staticcall( - new_memory_size: U256, - current_memory_size: U256, + new_memory_size: usize, + current_memory_size: usize, address_was_cold: bool, ) -> Result { let static_gas = STATICCALL_STATIC; @@ -722,7 +672,7 @@ pub fn staticcall( )?; // Note: code_execution_cost will be charged from the sub context post-state. - let dynamic_gas = memory_expansion_cost + let dynamic_gas = U256::from(memory_expansion_cost) .checked_add(address_access_cost) .ok_or(OutOfGasError::GasCostOverflow)?; diff --git a/crates/vm/levm/src/memory.rs b/crates/vm/levm/src/memory.rs index d7f9ea7b3..6a2de0bf7 100644 --- a/crates/vm/levm/src/memory.rs +++ b/crates/vm/levm/src/memory.rs @@ -1,245 +1,209 @@ use crate::{ - constants::{MEMORY_EXPANSION_QUOTIENT, WORD_SIZE, WORD_SIZE_IN_BYTES}, + constants::{MEMORY_EXPANSION_QUOTIENT, WORD_SIZE_IN_BYTES_USIZE}, errors::{InternalError, OutOfGasError, VMError}, }; use ethrex_core::U256; -#[derive(Debug, Clone, Default, PartialEq)] -pub struct Memory { - pub data: Vec, -} +pub type Memory = Vec; -impl From> for Memory { - fn from(data: Vec) -> Self { - Memory { data } +pub fn try_resize(memory: &mut Memory, unchecked_new_size: usize) -> Result<(), VMError> { + if unchecked_new_size == 0 || unchecked_new_size <= memory.len() { + return Ok(()); } -} -impl Memory { - pub fn new() -> Self { - Self { data: Vec::new() } - } + let new_size = unchecked_new_size + .checked_next_multiple_of(WORD_SIZE_IN_BYTES_USIZE) + .ok_or(VMError::OutOfOffset)?; - pub fn new_from_vec(data: Vec) -> Self { - Self { data } + if new_size > memory.len() { + let additional_size = new_size.checked_sub(memory.len()).ok_or(VMError::Internal( + InternalError::ArithmeticOperationUnderflow, + ))?; + memory + .try_reserve(additional_size) + .map_err(|_err| VMError::MemorySizeOverflow)?; + memory.resize(new_size, 0); } - fn resize(&mut self, offset: usize) -> Result<(), VMError> { - let new_offset = offset - .checked_next_multiple_of(WORD_SIZE) - .ok_or(VMError::Internal( - InternalError::ArithmeticOperationOverflow, - ))?; - if new_offset > self.data.len() { - // Expand the size - let size_to_expand = - new_offset - .checked_sub(self.data.len()) - .ok_or(VMError::Internal( - InternalError::ArithmeticOperationUnderflow, - ))?; - self.data - .try_reserve(size_to_expand) - .map_err(|_err| VMError::MemorySizeOverflow)?; - - // Fill the new space with zeros - self.data.extend(std::iter::repeat(0).take(size_to_expand)); - } - Ok(()) - } + Ok(()) +} - pub fn load(&mut self, offset: usize) -> Result { - self.resize(offset.checked_add(WORD_SIZE).ok_or(VMError::Internal( - InternalError::ArithmeticOperationOverflow, // MemoryLoadOutOfBounds? - ))?)?; - let value_bytes = self - .data - .get( - offset - ..offset.checked_add(WORD_SIZE).ok_or(VMError::Internal( - InternalError::ArithmeticOperationOverflow, // MemoryLoadOutOfBounds? - ))?, - ) - .ok_or(VMError::MemoryLoadOutOfBounds)?; +pub fn load_word(memory: &mut Memory, offset: usize) -> Result { + load_range(memory, offset, WORD_SIZE_IN_BYTES_USIZE).map(U256::from_big_endian) +} - Ok(U256::from_big_endian(value_bytes)) +pub fn load_range(memory: &mut Memory, offset: usize, size: usize) -> Result<&[u8], VMError> { + if size == 0 { + return Ok(&[]); } - pub fn load_range(&mut self, offset: usize, size: usize) -> Result, VMError> { - if size == 0 { - return Ok(Vec::new()); - } + try_resize( + memory, + offset.checked_add(size).ok_or(VMError::OutOfOffset)?, + )?; - let size_to_load = offset.checked_add(size).ok_or(VMError::Internal( - InternalError::ArithmeticOperationOverflow, - ))?; - self.resize(size_to_load)?; - self.data - .get(offset..size_to_load) - .ok_or(VMError::MemoryLoadOutOfBounds) - .map(|slice| slice.to_vec()) - } + memory + .get(offset..offset.checked_add(size).ok_or(VMError::OutOfOffset)?) + .ok_or(VMError::OutOfOffset) +} - pub fn store_bytes(&mut self, offset: usize, value: &[u8]) -> Result<(), VMError> { - let len = value.len(); - let size_to_store = offset.checked_add(len).ok_or(VMError::Internal( - InternalError::ArithmeticOperationOverflow, - ))?; - self.resize(size_to_store)?; - self.data - .splice(offset..size_to_store, value.iter().copied()); +pub fn try_store_word(memory: &mut Memory, offset: usize, word: U256) -> Result<(), VMError> { + try_resize( + memory, + offset + .checked_add(WORD_SIZE_IN_BYTES_USIZE) + .ok_or(VMError::OutOfOffset)?, + )?; + let mut word_bytes = [0u8; WORD_SIZE_IN_BYTES_USIZE]; + word.to_big_endian(&mut word_bytes); + try_store(memory, &word_bytes, offset, WORD_SIZE_IN_BYTES_USIZE) +} - Ok(()) - } +pub fn try_store_data(memory: &mut Memory, offset: usize, data: &[u8]) -> Result<(), VMError> { + try_resize( + memory, + offset.checked_add(data.len()).ok_or(VMError::OutOfOffset)?, + )?; + try_store(memory, data, offset, data.len()) +} - pub fn store_n_bytes( - &mut self, - offset: usize, - value: &[u8], - size: usize, - ) -> Result<(), VMError> { - let size_to_store = offset.checked_add(size).ok_or(VMError::Internal( - InternalError::ArithmeticOperationOverflow, - ))?; - self.resize(size_to_store)?; - self.data - .splice(offset..size_to_store, value.iter().copied()); +pub fn try_store_range( + memory: &mut Memory, + offset: usize, + size: usize, + data: &[u8], +) -> Result<(), VMError> { + try_resize( + memory, + offset.checked_add(size).ok_or(VMError::OutOfOffset)?, + )?; + try_store(memory, data, offset, size) +} - Ok(()) +fn try_store( + memory: &mut Memory, + data: &[u8], + at_offset: usize, + data_size: usize, +) -> Result<(), VMError> { + if data_size == 0 { + return Ok(()); } - pub fn size(&self) -> U256 { - U256::from(self.data.len()) + for (byte_to_store, memory_slot) in data.iter().zip( + memory + .get_mut( + at_offset + ..at_offset + .checked_add(data_size) + .ok_or(VMError::OutOfOffset)?, + ) + .ok_or(VMError::OutOfOffset)? + .iter_mut(), + ) { + *memory_slot = *byte_to_store; } + Ok(()) +} - pub fn copy( - &mut self, - src_offset: usize, - dest_offset: usize, - size: usize, - ) -> Result<(), VMError> { - let src_copy_size = src_offset.checked_add(size).ok_or(VMError::Internal( - InternalError::ArithmeticOperationOverflow, - ))?; - let dest_copy_size = dest_offset.checked_add(size).ok_or(VMError::Internal( - InternalError::ArithmeticOperationOverflow, - ))?; - let max_size = std::cmp::max(src_copy_size, dest_copy_size); - - if max_size > self.data.len() { - self.resize(max_size)?; - } +pub fn try_copy_within( + memory: &mut Memory, + from_offset: usize, + to_offset: usize, + size: usize, +) -> Result<(), VMError> { + if size == 0 { + return Ok(()); + } - let mut temp = vec![0u8; size]; - - temp.copy_from_slice( - self.data - .get(src_offset..src_copy_size) - .ok_or(VMError::Internal(InternalError::SlicingError))?, - ); - - for i in 0..size { - if let Some(temp_byte) = temp.get_mut(i) { - *temp_byte = *self - .data - .get( - src_offset - .checked_add(i) - .ok_or(VMError::MemoryLoadOutOfBounds)?, - ) - .unwrap_or(&0u8); - } + try_resize( + memory, + to_offset.checked_add(size).ok_or(VMError::OutOfOffset)?, + )?; + + let mut temporary_buffer = vec![0u8; size]; + for i in 0..size { + if let Some(temporary_buffer_byte) = temporary_buffer.get_mut(i) { + *temporary_buffer_byte = *memory + .get(from_offset.checked_add(i).ok_or(VMError::OutOfOffset)?) + .unwrap_or(&0u8); } + } - for i in 0..size { - if let Some(memory_byte) = self.data.get_mut( - dest_offset - .checked_add(i) - .ok_or(VMError::MemoryLoadOutOfBounds)?, - ) { - *memory_byte = *temp.get(i).unwrap_or(&0u8); - } + for i in 0..size { + if let Some(memory_byte) = + memory.get_mut(to_offset.checked_add(i).ok_or(VMError::OutOfOffset)?) + { + *memory_byte = *temporary_buffer.get(i).unwrap_or(&0u8); } - Ok(()) } - pub fn expansion_cost(&self, offset: usize, size: usize) -> Result { - if size == 0 { - return Ok(U256::zero()); - } + Ok(()) +} - let memory_byte_size = offset - .checked_add(size) - .ok_or(OutOfGasError::GasCostOverflow)?; - if memory_byte_size <= self.data.len() { - return Ok(U256::zero()); - } +pub fn access_cost( + new_memory_size: usize, + current_memory_size: usize, + static_cost: U256, + dynamic_base_cost: U256, +) -> Result { + let minimum_word_size = new_memory_size + .checked_add( + WORD_SIZE_IN_BYTES_USIZE + .checked_sub(1) + .ok_or(InternalError::ArithmeticOperationUnderflow)?, + ) + .ok_or(OutOfGasError::MemoryExpansionCostOverflow)? + / WORD_SIZE_IN_BYTES_USIZE; - let new_memory_size_word = memory_byte_size - .checked_add(WORD_SIZE - 1) - .ok_or(OutOfGasError::GasCostOverflow)? - / WORD_SIZE; - - let new_memory_cost = new_memory_size_word - .checked_mul(new_memory_size_word) - .map(|square| square / MEMORY_EXPANSION_QUOTIENT) - .and_then(|cost| cost.checked_add(new_memory_size_word.checked_mul(3)?)) - .ok_or(OutOfGasError::GasCostOverflow)?; - - let last_memory_size_word = self - .data - .len() - .checked_add(WORD_SIZE - 1) - .ok_or(OutOfGasError::GasCostOverflow)? - / WORD_SIZE; - - let last_memory_cost = last_memory_size_word - .checked_mul(last_memory_size_word) - .map(|square| square / MEMORY_EXPANSION_QUOTIENT) - .and_then(|cost| cost.checked_add(last_memory_size_word.checked_mul(3)?)) - .ok_or(OutOfGasError::GasCostOverflow)?; - - Ok((new_memory_cost - .checked_sub(last_memory_cost) - .ok_or(OutOfGasError::GasCostOverflow)?) - .into()) - } + let static_gas = static_cost; + let dynamic_cost = dynamic_base_cost + .checked_mul(minimum_word_size.into()) + .ok_or(OutOfGasError::MemoryExpansionCostOverflow)? + .checked_add(expansion_cost(new_memory_size, current_memory_size)?.into()) + .ok_or(OutOfGasError::MemoryExpansionCostOverflow)?; + + Ok(static_gas + .checked_add(dynamic_cost) + .ok_or(OutOfGasError::GasCostOverflow)?) +} + +/// When a memory expansion is triggered, only the additional bytes of memory +/// must be paid for. +pub fn expansion_cost( + new_memory_size: usize, + current_memory_size: usize, +) -> Result { + let cost = if new_memory_size <= current_memory_size { + 0 + } else { + cost(new_memory_size)? + .checked_sub(cost(current_memory_size)?) + .ok_or(InternalError::ArithmeticOperationUnderflow)? + }; + Ok(cost) } /// The total cost for a given memory size. -pub fn cost(memory_size: U256) -> Result { +fn cost(memory_size: usize) -> Result { let memory_size_word = memory_size .checked_add( - WORD_SIZE_IN_BYTES - .checked_sub(U256::one()) + WORD_SIZE_IN_BYTES_USIZE + .checked_sub(1) .ok_or(InternalError::ArithmeticOperationUnderflow)?, ) .ok_or(OutOfGasError::MemoryExpansionCostOverflow)? - .checked_div(WORD_SIZE_IN_BYTES) - .ok_or(OutOfGasError::MemoryExpansionCostOverflow)?; + / WORD_SIZE_IN_BYTES_USIZE; Ok(memory_size_word - .checked_pow(U256::from(2)) + .checked_pow(2) .ok_or(OutOfGasError::MemoryExpansionCostOverflow)? - .checked_div(U256::from(512)) + .checked_div(MEMORY_EXPANSION_QUOTIENT) .ok_or(OutOfGasError::MemoryExpansionCostOverflow)? .checked_add( - U256::from(3) + 3usize .checked_mul(memory_size_word) .ok_or(OutOfGasError::MemoryExpansionCostOverflow)?, ) .ok_or(OutOfGasError::MemoryExpansionCostOverflow)?) } - -/// When a memory expansion is triggered, only the additional bytes of memory -/// must be paid for. -pub fn expansion_cost(new_memory_size: U256, old_memory_size: U256) -> Result { - let cost = if new_memory_size <= old_memory_size { - U256::zero() - } else { - cost(new_memory_size)? - .checked_sub(cost(old_memory_size)?) - .ok_or(InternalError::ArithmeticOperationUnderflow)? - }; - Ok(cost) -} diff --git a/crates/vm/levm/src/opcode_handlers/environment.rs b/crates/vm/levm/src/opcode_handlers/environment.rs index f106ff34f..642f84c48 100644 --- a/crates/vm/levm/src/opcode_handlers/environment.rs +++ b/crates/vm/levm/src/opcode_handlers/environment.rs @@ -1,8 +1,8 @@ use crate::{ call_frame::CallFrame, - constants::{WORD_SIZE, WORD_SIZE_IN_BYTES_USIZE}, - errors::{InternalError, OpcodeSuccess, OutOfGasError, VMError}, - gas_cost, + constants::WORD_SIZE_IN_BYTES_USIZE, + errors::{OpcodeSuccess, OutOfGasError, VMError}, + gas_cost, memory, vm::{word_to_address, VM}, }; use ethrex_core::U256; @@ -154,10 +154,18 @@ impl VM { .try_into() .map_err(|_err| VMError::VeryLargeNumber)?; - let gas_cost = gas_cost::calldatacopy(current_call_frame, size, dest_offset) - .map_err(VMError::OutOfGas)?; - - self.increase_consumed_gas(current_call_frame, gas_cost)?; + self.increase_consumed_gas( + current_call_frame, + gas_cost::calldatacopy( + dest_offset + .checked_add(size) + .ok_or(VMError::OutOfOffset)? + .checked_next_multiple_of(WORD_SIZE_IN_BYTES_USIZE) + .ok_or(VMError::OutOfOffset)?, + current_call_frame.memory.len(), + size, + )?, + )?; if size == 0 { return Ok(OpcodeSuccess::Continue); @@ -176,7 +184,7 @@ impl VM { } } - current_call_frame.memory.store_bytes(dest_offset, &data)?; + memory::try_store_data(&mut current_call_frame.memory, dest_offset, &data)?; Ok(OpcodeSuccess::Continue) } @@ -226,53 +234,38 @@ impl VM { .try_into() .map_err(|_| VMError::VeryLargeNumber)?; - let gas_cost = gas_cost::codecopy(current_call_frame, size, destination_offset) - .map_err(VMError::OutOfGas)?; - - self.increase_consumed_gas(current_call_frame, gas_cost)?; + self.increase_consumed_gas( + current_call_frame, + gas_cost::codecopy( + destination_offset + .checked_add(size) + .ok_or(VMError::OutOfOffset)? + .checked_next_multiple_of(WORD_SIZE_IN_BYTES_USIZE) + .ok_or(VMError::OutOfOffset)?, + current_call_frame.memory.len(), + size, + )?, + )?; if size == 0 { return Ok(OpcodeSuccess::Continue); } - let new_memory_size = (destination_offset - .checked_add(size) - .ok_or(VMError::Internal( - InternalError::ArithmeticOperationOverflow, - ))?) - .checked_next_multiple_of(WORD_SIZE) - .ok_or(VMError::Internal( - InternalError::ArithmeticOperationOverflow, - ))?; - let current_memory_size = current_call_frame.memory.data.len(); - - if current_memory_size < new_memory_size { - current_call_frame - .memory - .data - .try_reserve(new_memory_size) - .map_err(|_err| VMError::MemorySizeOverflow)?; - current_call_frame.memory.data.resize(new_memory_size, 0); - } - - for i in 0..size { - if let Some(memory_byte) = - current_call_frame - .memory - .data - .get_mut(destination_offset.checked_add(i).ok_or(VMError::Internal( - InternalError::ArithmeticOperationOverflow, - ))?) - { - *memory_byte = *current_call_frame - .bytecode - .get(code_offset.checked_add(i).ok_or(VMError::Internal( - InternalError::ArithmeticOperationOverflow, - ))?) - .unwrap_or(&0u8); + let mut data = vec![0u8; size]; + for (i, byte) in current_call_frame + .bytecode + .iter() + .skip(code_offset) + .take(size) + .enumerate() + { + if let Some(data_byte) = data.get_mut(i) { + *data_byte = *byte; } } + memory::try_store_data(&mut current_call_frame.memory, destination_offset, &data)?; + Ok(OpcodeSuccess::Continue) } @@ -330,22 +323,15 @@ impl VM { let (account_info, address_was_cold) = self.access_account(address); - let new_memory_size = dest_offset - .checked_add(size) - .ok_or(VMError::Internal( - InternalError::ArithmeticOperationOverflow, - ))? - .checked_next_multiple_of(WORD_SIZE_IN_BYTES_USIZE) - .ok_or(VMError::Internal( - InternalError::ArithmeticOperationOverflow, - ))?; - let current_memory_size = current_call_frame.memory.data.len(); - self.increase_consumed_gas( current_call_frame, gas_cost::extcodecopy( - new_memory_size.into(), - current_memory_size.into(), + dest_offset + .checked_add(size) + .ok_or(VMError::OutOfOffset)? + .checked_next_multiple_of(WORD_SIZE_IN_BYTES_USIZE) + .ok_or(VMError::OutOfOffset)?, + current_call_frame.memory.len(), address_was_cold, )?, )?; @@ -354,36 +340,21 @@ impl VM { return Ok(OpcodeSuccess::Continue); } - if current_memory_size < new_memory_size { - current_call_frame - .memory - .data - .try_reserve(new_memory_size) - .map_err(|_err| VMError::MemorySizeOverflow)?; - current_call_frame - .memory - .data - .extend(std::iter::repeat(0).take(new_memory_size)); - } - - for i in 0..size { - if let Some(memory_byte) = - current_call_frame - .memory - .data - .get_mut(dest_offset.checked_add(i).ok_or(VMError::Internal( - InternalError::ArithmeticOperationOverflow, - ))?) - { - *memory_byte = *account_info - .bytecode - .get(offset.checked_add(i).ok_or(VMError::Internal( - InternalError::ArithmeticOperationOverflow, - ))?) - .unwrap_or(&0u8); + let mut data = vec![0u8; size]; + for (i, byte) in account_info + .bytecode + .iter() + .skip(offset) + .take(size) + .enumerate() + { + if let Some(data_byte) = data.get_mut(i) { + *data_byte = *byte; } } + memory::try_store_data(&mut current_call_frame.memory, dest_offset, &data)?; + Ok(OpcodeSuccess::Continue) } @@ -422,10 +393,18 @@ impl VM { .try_into() .map_err(|_| VMError::VeryLargeNumber)?; - let gas_cost = gas_cost::returndatacopy(current_call_frame, size, dest_offset) - .map_err(VMError::OutOfGas)?; - - self.increase_consumed_gas(current_call_frame, gas_cost)?; + self.increase_consumed_gas( + current_call_frame, + gas_cost::returndatacopy( + dest_offset + .checked_add(size) + .ok_or(VMError::OutOfOffset)? + .checked_next_multiple_of(WORD_SIZE_IN_BYTES_USIZE) + .ok_or(VMError::OutOfOffset)?, + current_call_frame.memory.len(), + size, + )?, + )?; if size == 0 { return Ok(OpcodeSuccess::Continue); @@ -436,17 +415,21 @@ impl VM { if returndata_offset >= sub_return_data_len { return Err(VMError::VeryLargeNumber); // Maybe can create a new error instead of using this one } - let data = current_call_frame.sub_return_data.slice( - returndata_offset - ..(returndata_offset - .checked_add(size) - .ok_or(VMError::Internal( - InternalError::ArithmeticOperationOverflow, - ))?) - .min(sub_return_data_len), - ); - current_call_frame.memory.store_bytes(dest_offset, &data)?; + let mut data = vec![0u8; size]; + for (i, byte) in current_call_frame + .sub_return_data + .iter() + .skip(returndata_offset) + .take(size) + .enumerate() + { + if let Some(data_byte) = data.get_mut(i) { + *data_byte = *byte; + } + } + + memory::try_store_data(&mut current_call_frame.memory, dest_offset, &data)?; Ok(OpcodeSuccess::Continue) } diff --git a/crates/vm/levm/src/opcode_handlers/keccak.rs b/crates/vm/levm/src/opcode_handlers/keccak.rs index 29cd2872a..2be644af9 100644 --- a/crates/vm/levm/src/opcode_handlers/keccak.rs +++ b/crates/vm/levm/src/opcode_handlers/keccak.rs @@ -1,7 +1,8 @@ use crate::{ call_frame::CallFrame, + constants::WORD_SIZE_IN_BYTES_USIZE, errors::{OpcodeSuccess, VMError}, - gas_cost, + gas_cost, memory, vm::VM, }; use ethrex_core::U256; @@ -26,19 +27,25 @@ impl VM { .try_into() .map_err(|_| VMError::VeryLargeNumber)?; - let gas_cost = - gas_cost::keccak256(current_call_frame, size, offset).map_err(VMError::OutOfGas)?; - - self.increase_consumed_gas(current_call_frame, gas_cost)?; - - let value_bytes = if size == 0 { - vec![] - } else { - current_call_frame.memory.load_range(offset, size)? - }; + self.increase_consumed_gas( + current_call_frame, + gas_cost::keccak256( + offset + .checked_add(size) + .ok_or(VMError::OutOfOffset)? + .checked_next_multiple_of(WORD_SIZE_IN_BYTES_USIZE) + .ok_or(VMError::OutOfOffset)?, + current_call_frame.memory.len(), + size, + )?, + )?; let mut hasher = Keccak256::new(); - hasher.update(value_bytes); + hasher.update(memory::load_range( + &mut current_call_frame.memory, + offset, + size, + )?); current_call_frame .stack .push(U256::from_big_endian(&hasher.finalize()))?; diff --git a/crates/vm/levm/src/opcode_handlers/logging.rs b/crates/vm/levm/src/opcode_handlers/logging.rs index 8efa48268..3b45a68b6 100644 --- a/crates/vm/levm/src/opcode_handlers/logging.rs +++ b/crates/vm/levm/src/opcode_handlers/logging.rs @@ -1,7 +1,8 @@ use crate::{ call_frame::CallFrame, + constants::WORD_SIZE_IN_BYTES_USIZE, errors::{OpcodeSuccess, VMError}, - gas_cost, + gas_cost, memory, opcodes::Opcode, vm::VM, }; @@ -44,16 +45,26 @@ impl VM { topics.push(H256::from_slice(&topic_bytes)); } - let gas_cost = gas_cost::log(current_call_frame, size, offset, number_of_topics) - .map_err(VMError::OutOfGas)?; + self.increase_consumed_gas( + current_call_frame, + gas_cost::log( + offset + .checked_add(size) + .ok_or(VMError::OutOfOffset)? + .checked_next_multiple_of(WORD_SIZE_IN_BYTES_USIZE) + .ok_or(VMError::OutOfOffset)?, + current_call_frame.memory.len(), + size, + number_of_topics, + )?, + )?; - self.increase_consumed_gas(current_call_frame, gas_cost)?; - - let data = current_call_frame.memory.load_range(offset, size)?; let log = Log { address: current_call_frame.msg_sender, // Should change the addr if we are on a Call/Create transaction (Call should be the contract we are calling, Create should be the original caller) topics, - data: Bytes::from(data), + data: Bytes::from( + memory::load_range(&mut current_call_frame.memory, offset, size)?.to_vec(), + ), }; current_call_frame.logs.push(log); diff --git a/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs b/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs index 0c9ba084e..a64cab94a 100644 --- a/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs +++ b/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs @@ -1,8 +1,8 @@ use crate::{ call_frame::CallFrame, - constants::WORD_SIZE, + constants::{WORD_SIZE, WORD_SIZE_IN_BYTES_USIZE}, errors::{InternalError, OpcodeSuccess, OutOfGasError, VMError}, - gas_cost, + gas_cost, memory, vm::VM, }; use ethrex_core::{H256, U256}; @@ -63,12 +63,21 @@ impl VM { .try_into() .map_err(|_| VMError::VeryLargeNumber)?; - let gas_cost = gas_cost::mload(current_call_frame, offset).map_err(VMError::OutOfGas)?; - - self.increase_consumed_gas(current_call_frame, gas_cost)?; + self.increase_consumed_gas( + current_call_frame, + gas_cost::mload( + offset + .checked_add(WORD_SIZE_IN_BYTES_USIZE) + .ok_or(VMError::OutOfOffset)? + .checked_next_multiple_of(WORD_SIZE_IN_BYTES_USIZE) + .ok_or(VMError::OutOfOffset)?, + current_call_frame.memory.len(), + )?, + )?; - let value = current_call_frame.memory.load(offset)?; - current_call_frame.stack.push(value)?; + current_call_frame + .stack + .push(memory::load_word(&mut current_call_frame.memory, offset)?)?; Ok(OpcodeSuccess::Continue) } @@ -84,17 +93,23 @@ impl VM { .try_into() .map_err(|_err| VMError::VeryLargeNumber)?; - let gas_cost = gas_cost::mstore(current_call_frame, offset).map_err(VMError::OutOfGas)?; - - self.increase_consumed_gas(current_call_frame, gas_cost)?; + self.increase_consumed_gas( + current_call_frame, + gas_cost::mstore( + offset + .checked_add(WORD_SIZE_IN_BYTES_USIZE) + .ok_or(VMError::OutOfOffset)? + .checked_next_multiple_of(WORD_SIZE_IN_BYTES_USIZE) + .ok_or(VMError::OutOfOffset)?, + current_call_frame.memory.len(), + )?, + )?; let value = current_call_frame.stack.pop()?; let mut value_bytes = [0u8; WORD_SIZE]; value.to_big_endian(&mut value_bytes); - current_call_frame - .memory - .store_bytes(offset, &value_bytes)?; + memory::try_store_data(&mut current_call_frame.memory, offset, &value_bytes)?; Ok(OpcodeSuccess::Continue) } @@ -111,17 +126,27 @@ impl VM { .try_into() .map_err(|_| VMError::VeryLargeNumber)?; - let gas_cost = gas_cost::mstore8(current_call_frame, offset).map_err(VMError::OutOfGas)?; - - self.increase_consumed_gas(current_call_frame, gas_cost)?; + self.increase_consumed_gas( + current_call_frame, + gas_cost::mstore8( + offset + .checked_add(1) + .ok_or(VMError::OutOfOffset)? + .checked_next_multiple_of(WORD_SIZE_IN_BYTES_USIZE) + .ok_or(VMError::OutOfOffset)?, + current_call_frame.memory.len(), + )?, + )?; let value = current_call_frame.stack.pop()?; let mut value_bytes = [0u8; WORD_SIZE]; value.to_big_endian(&mut value_bytes); - current_call_frame - .memory - .store_bytes(offset, value_bytes[WORD_SIZE - 1..WORD_SIZE].as_ref())?; + memory::try_store_data( + &mut current_call_frame.memory, + offset, + &value_bytes[WORD_SIZE - 1..WORD_SIZE], + )?; Ok(OpcodeSuccess::Continue) } @@ -225,7 +250,7 @@ impl VM { self.increase_consumed_gas(current_call_frame, gas_cost::MSIZE)?; current_call_frame .stack - .push(current_call_frame.memory.size())?; + .push(current_call_frame.memory.len().into())?; Ok(OpcodeSuccess::Continue) } @@ -265,16 +290,33 @@ impl VM { .try_into() .map_err(|_| VMError::VeryLargeNumber)?; - let gas_cost = gas_cost::mcopy(current_call_frame, size, src_offset, dest_offset) - .map_err(VMError::OutOfGas)?; + let new_memory_size_for_dest = dest_offset + .checked_add(size) + .ok_or(VMError::OutOfOffset)? + .checked_next_multiple_of(WORD_SIZE_IN_BYTES_USIZE) + .ok_or(VMError::OutOfOffset)?; - self.increase_consumed_gas(current_call_frame, gas_cost)?; + let new_memory_size_for_src = src_offset + .checked_add(size) + .ok_or(VMError::OutOfOffset)? + .checked_next_multiple_of(WORD_SIZE_IN_BYTES_USIZE) + .ok_or(VMError::OutOfOffset)?; - if size > 0 { - current_call_frame - .memory - .copy(src_offset, dest_offset, size)?; - } + self.increase_consumed_gas( + current_call_frame, + gas_cost::mcopy( + new_memory_size_for_dest.max(new_memory_size_for_src), + current_call_frame.memory.len(), + size, + )?, + )?; + + memory::try_copy_within( + &mut current_call_frame.memory, + src_offset, + dest_offset, + size, + )?; Ok(OpcodeSuccess::Continue) } diff --git a/crates/vm/levm/src/opcode_handlers/system.rs b/crates/vm/levm/src/opcode_handlers/system.rs index 88458ecb9..973242785 100644 --- a/crates/vm/levm/src/opcode_handlers/system.rs +++ b/crates/vm/levm/src/opcode_handlers/system.rs @@ -1,8 +1,8 @@ use crate::{ call_frame::CallFrame, constants::WORD_SIZE_IN_BYTES_USIZE, - errors::{InternalError, OpcodeSuccess, ResultReason, VMError}, - gas_cost, + errors::{InternalError, OpcodeSuccess, OutOfGasError, ResultReason, VMError}, + gas_cost, memory, vm::{word_to_address, VM}, }; use ethrex_core::{types::TxKind, Address, U256}; @@ -60,15 +60,15 @@ impl VM { InternalError::ArithmeticOperationOverflow, ))?; let new_memory_size = new_memory_size_for_args.max(new_memory_size_for_return_data); - let current_memory_size = current_call_frame.memory.data.len(); + let current_memory_size = current_call_frame.memory.len(); let (account_info, address_was_cold) = self.access_account(callee); self.increase_consumed_gas( current_call_frame, gas_cost::call( - new_memory_size.into(), - current_memory_size.into(), + new_memory_size, + current_memory_size, address_was_cold, account_info.is_empty(), value_to_transfer, @@ -141,15 +141,15 @@ impl VM { InternalError::ArithmeticOperationOverflow, ))?; let new_memory_size = new_memory_size_for_args.max(new_memory_size_for_return_data); - let current_memory_size = current_call_frame.memory.data.len(); + let current_memory_size = current_call_frame.memory.len(); let (_account_info, address_was_cold) = self.access_account(code_address); self.increase_consumed_gas( current_call_frame, gas_cost::callcode( - new_memory_size.into(), - current_memory_size.into(), + new_memory_size, + current_memory_size, address_was_cold, value_to_transfer, )?, @@ -193,12 +193,18 @@ impl VM { .try_into() .map_err(|_| VMError::VeryLargeNumber)?; - let gas_cost = current_call_frame.memory.expansion_cost(offset, size)?; - - self.increase_consumed_gas(current_call_frame, gas_cost)?; + let new_memory_size = offset + .checked_add(size) + .ok_or(VMError::OutOfGas(OutOfGasError::GasCostOverflow))?; + self.increase_consumed_gas( + current_call_frame, + memory::expansion_cost(new_memory_size, current_call_frame.memory.len())?.into(), + )?; - let return_data = current_call_frame.memory.load_range(offset, size)?.into(); - current_call_frame.returndata = return_data; + current_call_frame.returndata = + memory::load_range(&mut current_call_frame.memory, offset, size)? + .to_vec() + .into(); Ok(OpcodeSuccess::Result(ResultReason::Return)) } @@ -254,15 +260,11 @@ impl VM { InternalError::ArithmeticOperationOverflow, ))?; let new_memory_size = new_memory_size_for_args.max(new_memory_size_for_return_data); - let current_memory_size = current_call_frame.memory.data.len(); + let current_memory_size = current_call_frame.memory.len(); self.increase_consumed_gas( current_call_frame, - gas_cost::delegatecall( - new_memory_size.into(), - current_memory_size.into(), - address_was_cold, - )?, + gas_cost::delegatecall(new_memory_size, current_memory_size, address_was_cold)?, )?; self.generic_call( @@ -328,15 +330,11 @@ impl VM { InternalError::ArithmeticOperationOverflow, ))?; let new_memory_size = new_memory_size_for_args.max(new_memory_size_for_return_data); - let current_memory_size = current_call_frame.memory.data.len(); + let current_memory_size = current_call_frame.memory.len(); self.increase_consumed_gas( current_call_frame, - gas_cost::staticcall( - new_memory_size.into(), - current_memory_size.into(), - address_was_cold, - )?, + gas_cost::staticcall(new_memory_size, current_memory_size, address_was_cold)?, )?; let value = U256::zero(); @@ -367,7 +365,7 @@ impl VM { current_call_frame: &mut CallFrame, ) -> Result { let value_in_wei_to_send = current_call_frame.stack.pop()?; - let code_offset_in_memory = current_call_frame + let code_offset_in_memory: usize = current_call_frame .stack .pop()? .try_into() @@ -378,15 +376,18 @@ impl VM { .try_into() .map_err(|_err| VMError::VeryLargeNumber)?; - // Gas Cost - let gas_cost = gas_cost::create( + self.increase_consumed_gas( current_call_frame, - code_offset_in_memory, - code_size_in_memory, - ) - .map_err(VMError::OutOfGas)?; - - self.increase_consumed_gas(current_call_frame, gas_cost)?; + gas_cost::create( + code_offset_in_memory + .checked_add(code_size_in_memory) + .ok_or(VMError::OutOfOffset)? + .checked_next_multiple_of(WORD_SIZE_IN_BYTES_USIZE) + .ok_or(VMError::OutOfOffset)?, + current_call_frame.memory.len(), + code_size_in_memory, + )?, + )?; self.create( value_in_wei_to_send, @@ -416,15 +417,18 @@ impl VM { .map_err(|_err| VMError::VeryLargeNumber)?; let salt = current_call_frame.stack.pop()?; - // Gas Cost - let gas_cost = gas_cost::create_2( + self.increase_consumed_gas( current_call_frame, - code_offset_in_memory, - code_size_in_memory, - ) - .map_err(VMError::OutOfGas)?; - - self.increase_consumed_gas(current_call_frame, gas_cost)?; + gas_cost::create_2( + code_offset_in_memory + .checked_add(code_size_in_memory) + .ok_or(VMError::OutOfOffset)? + .checked_next_multiple_of(WORD_SIZE_IN_BYTES_USIZE) + .ok_or(VMError::OutOfOffset)?, + current_call_frame.memory.len(), + code_size_in_memory, + )?, + )?; self.create( value_in_wei_to_send, @@ -457,11 +461,18 @@ impl VM { .try_into() .map_err(|_err| VMError::VeryLargeNumber)?; - let gas_cost = current_call_frame.memory.expansion_cost(offset, size)?; - - self.increase_consumed_gas(current_call_frame, gas_cost)?; + let new_memory_size = offset + .checked_add(size) + .ok_or(VMError::OutOfGas(OutOfGasError::GasCostOverflow))?; + self.increase_consumed_gas( + current_call_frame, + memory::expansion_cost(new_memory_size, current_call_frame.memory.len())?.into(), + )?; - current_call_frame.returndata = current_call_frame.memory.load_range(offset, size)?.into(); + current_call_frame.returndata = + memory::load_range(&mut current_call_frame.memory, offset, size)? + .to_vec() + .into(); Err(VMError::RevertOpcode) } diff --git a/crates/vm/levm/src/vm.rs b/crates/vm/levm/src/vm.rs index f103ca860..752db87da 100644 --- a/crates/vm/levm/src/vm.rs +++ b/crates/vm/levm/src/vm.rs @@ -12,6 +12,7 @@ use crate::{ TxValidationError, VMError, }, gas_cost::{self, fake_exponential, BLOB_GAS_PER_BLOB, CREATE_BASE_COST}, + memory, opcodes::Opcode, AccountInfo, }; @@ -740,10 +741,8 @@ impl VM { // self.cache.increment_account_nonce(&code_address); // Internal call doesn't increment account nonce. - let calldata = current_call_frame - .memory - .load_range(args_offset, args_size)? - .into(); + let calldata = + memory::load_range(&mut current_call_frame.memory, args_offset, args_size)?.to_vec(); // I don't know if this gas limit should be calculated before or after consuming gas let mut potential_remaining_gas = current_call_frame @@ -768,7 +767,7 @@ impl VM { code_address, code_account_info.bytecode, value, - calldata, + calldata.into(), is_static, gas_limit, U256::zero(), @@ -793,9 +792,12 @@ impl VM { .checked_add(tx_report.gas_used.into()) .ok_or(VMError::OutOfGas(OutOfGasError::ConsumedGasOverflow))?; current_call_frame.logs.extend(tx_report.logs); - current_call_frame - .memory - .store_n_bytes(ret_offset, &tx_report.output, ret_size)?; + memory::try_store_range( + &mut current_call_frame.memory, + ret_offset, + ret_size, + &tx_report.output, + )?; current_call_frame.sub_return_data = tx_report.output; // What to do, depending on TxResult @@ -910,9 +912,12 @@ impl VM { }; let code = Bytes::from( - current_call_frame - .memory - .load_range(code_offset_in_memory, code_size_in_memory)?, + memory::load_range( + &mut current_call_frame.memory, + code_offset_in_memory, + code_size_in_memory, + )? + .to_vec(), ); let new_address = match salt { diff --git a/crates/vm/levm/tests/tests.rs b/crates/vm/levm/tests/tests.rs index 1b05547de..534be1910 100644 --- a/crates/vm/levm/tests/tests.rs +++ b/crates/vm/levm/tests/tests.rs @@ -8,7 +8,7 @@ use ethrex_levm::{ constants::*, db::{cache, CacheDB, Db}, errors::{TxResult, VMError}, - gas_cost, + gas_cost, memory, operations::Operation, utils::{new_vm_with_ops, new_vm_with_ops_addr_bal_db, new_vm_with_ops_db, ops_to_bytecode}, vm::{word_to_address, Storage, VM}, @@ -1490,7 +1490,8 @@ fn mstore_saves_correct_value() { let mut current_call_frame = vm.call_frames.pop().unwrap(); vm.execute(&mut current_call_frame).unwrap(); - let stored_value = vm.current_call_frame_mut().unwrap().memory.load(0).unwrap(); + let stored_value = + memory::load_word(&mut vm.current_call_frame_mut().unwrap().memory, 0).unwrap(); assert_eq!(stored_value, U256::from(0x33333)); @@ -1513,7 +1514,8 @@ fn mstore8() { let mut current_call_frame = vm.call_frames.pop().unwrap(); vm.execute(&mut current_call_frame).unwrap(); - let stored_value = vm.current_call_frame_mut().unwrap().memory.load(0).unwrap(); + let stored_value = + memory::load_word(&mut vm.current_call_frame_mut().unwrap().memory, 0).unwrap(); let mut value_bytes = [0u8; 32]; stored_value.to_big_endian(&mut value_bytes); @@ -1541,12 +1543,8 @@ fn mcopy() { let mut current_call_frame = vm.call_frames.pop().unwrap(); vm.execute(&mut current_call_frame).unwrap(); - let copied_value = vm - .current_call_frame_mut() - .unwrap() - .memory - .load(64) - .unwrap(); + let copied_value = + memory::load_word(&mut vm.current_call_frame_mut().unwrap().memory, 64).unwrap(); assert_eq!(copied_value, U256::from(0x33333)); let memory_size = vm.current_call_frame_mut().unwrap().stack.pop().unwrap(); @@ -1789,12 +1787,10 @@ fn call_changes_callframe_and_stores() { let ret_size = current_call_frame.sub_return_data_size; // Return data of the sub-context will be in the memory position of the current context reserved for that purpose (ret_offset and ret_size) - let return_data = current_call_frame - .memory - .load_range(ret_offset, ret_size) - .unwrap(); + let return_data = + memory::load_range(&mut current_call_frame.memory, ret_offset, ret_size).unwrap(); - assert_eq!(U256::from_big_endian(&return_data), U256::from(0xAAAAAAA)); + assert_eq!(U256::from_big_endian(return_data), U256::from(0xAAAAAAA)); } #[test] @@ -1957,12 +1953,10 @@ fn staticcall_changes_callframe_is_static() { let ret_offset = 0; let ret_size = 32; - let return_data = current_call_frame - .memory - .load_range(ret_offset, ret_size) - .unwrap(); + let return_data = + memory::load_range(&mut current_call_frame.memory, ret_offset, ret_size).unwrap(); - assert_eq!(U256::from_big_endian(&return_data), U256::from(0xAAAAAAA)); + assert_eq!(U256::from_big_endian(return_data), U256::from(0xAAAAAAA)); assert!(current_call_frame.is_static); } @@ -2489,8 +2483,14 @@ fn calldataload_being_set_by_parent() { let expected_data = U256::from_big_endian(&calldata[..32]); - assert_eq!(expected_data, current_call_frame.memory.load(0).unwrap()); - assert_eq!(expected_data, current_call_frame.memory.load(0).unwrap()); + assert_eq!( + expected_data, + memory::load_word(&mut current_call_frame.memory, 0).unwrap() + ); + assert_eq!( + expected_data, + memory::load_word(&mut current_call_frame.memory, 0).unwrap() + ); } #[test] @@ -2528,7 +2528,7 @@ fn calldatacopy() { vm.execute(&mut current_call_frame).unwrap(); let current_call_frame = vm.current_call_frame_mut().unwrap(); - let memory = current_call_frame.memory.load_range(0, 2).unwrap(); + let memory = memory::load_range(&mut current_call_frame.memory, 0, 2).unwrap(); assert_eq!(memory, vec![0x22, 0x33]); assert_eq!(vm.env.consumed_gas, TX_BASE_COST + 18); } @@ -2568,7 +2568,7 @@ fn returndatacopy() { vm.execute(&mut current_call_frame).unwrap(); let current_call_frame = vm.current_call_frame_mut().unwrap(); - let memory = current_call_frame.memory.load_range(0, 2).unwrap(); + let memory = memory::load_range(&mut current_call_frame.memory, 0, 2).unwrap(); assert_eq!(memory, vec![0xBB, 0xCC]); assert_eq!(vm.env.consumed_gas, TX_BASE_COST + 18); } @@ -2618,7 +2618,7 @@ fn returndatacopy_being_set_by_parent() { let current_call_frame = vm.current_call_frame_mut().unwrap(); - let result = current_call_frame.memory.load(0).unwrap(); + let result = memory::load_word(&mut current_call_frame.memory, 0).unwrap(); assert_eq!(result, U256::from(0xAAAAAAA)); } @@ -4485,7 +4485,7 @@ fn codecopy_op() { vm.execute(&mut current_call_frame).unwrap(); assert_eq!( - vm.current_call_frame_mut().unwrap().memory.load(0).unwrap(), + memory::load_word(&mut vm.current_call_frame_mut().unwrap().memory, 0).unwrap(), expected_memory ); assert_eq!( @@ -4565,11 +4565,7 @@ fn extcodecopy_existing_account() { let mut current_call_frame = vm.call_frames.pop().unwrap(); vm.execute(&mut current_call_frame).unwrap(); assert_eq!( - vm.current_call_frame_mut() - .unwrap() - .memory - .load_range(0, size) - .unwrap(), + memory::load_range(&mut vm.current_call_frame_mut().unwrap().memory, 0, size).unwrap(), vec![0x60] ); assert_eq!(vm.env.consumed_gas, 23616.into()); @@ -4594,11 +4590,7 @@ fn extcodecopy_non_existing_account() { let mut current_call_frame = vm.call_frames.pop().unwrap(); vm.execute(&mut current_call_frame).unwrap(); assert_eq!( - vm.current_call_frame_mut() - .unwrap() - .memory - .load_range(0, size) - .unwrap(), + memory::load_range(&mut vm.current_call_frame_mut().unwrap().memory, 0, size).unwrap(), vec![0; size] ); assert_eq!(vm.env.consumed_gas, 23616.into());