evm_rs_emulator/core_module/
state.rs

1use std::{collections::HashMap, fmt};
2
3use ethers::prelude::*;
4use ethers::{types::U256, utils::keccak256};
5
6use crate::core_module::utils;
7
8use super::utils::errors::ExecutionError;
9
10// Colored output
11use colored::*;
12
13/* -------------------------------------------------------------------------- */
14/*                             AccountState struct                            */
15/* -------------------------------------------------------------------------- */
16
17/// Represents the state of an account on the Ethereum Virtual Machine.
18pub struct AccountState {
19    /// The account's nonce, which is incremented each time a transaction is sent from the account.
20    pub nonce: u64,
21    /// The account's balance, represented as a 32-byte array.
22    pub balance: [u8; 32],
23    /// The account's storage, represented as a hashmap where the keys and values are both 32-byte arrays.
24    pub storage: HashMap<[u8; 32], [u8; 32]>,
25    /// The hash of the account's code, represented as a 32-byte array.
26    pub code_hash: [u8; 32],
27}
28
29/// Implements the Debug trait for the AccountState struct, which allows for the struct to be printed in a formatted way.
30/// The function prints the nonce, balance, code hash, and storage of the account state.
31/// If the code hash is empty, it prints "Empty code" instead of the hash.
32/// For each storage slot and value, it prints them in a formatted way.
33/// If the storage is empty, it prints "Empty storage".
34impl fmt::Debug for AccountState {
35    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36        let mut code_hash: String = utils::debug::to_hex_string(self.code_hash);
37        if self.code_hash == [0u8; 32] {
38            code_hash = format!("{}", "Empty code".red()).to_string()
39        }
40
41        writeln!(f, "  {}: {}", "Nonce".magenta(), self.nonce)?;
42        writeln!(
43            f,
44            "  {}: {}",
45            "Balance".magenta(),
46            U256::from(self.balance).to_string()
47        )?;
48        writeln!(f, "  {}: {}", "Code Hash".magenta(), code_hash)?;
49        write!(f, "  {}: ", "Storage".magenta())?;
50        for (slot, value) in &self.storage {
51            println!("\n┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐");
52            // Print the slot
53            let hex: String = utils::debug::to_hex_string(slot.to_owned());
54            println!("│ {}:  {} │", "Slot".bright_blue(), hex);
55
56            // Print the value
57            let hex: String = utils::debug::to_hex_string(value.to_owned());
58            println!("│ {}: {} │", "Value".blue(), hex);
59
60            println!("└────────────────────────────────────────────────────────────────────────────────────────────────────────┘");
61        }
62        if self.storage.is_empty() {
63            write!(f, "  {}", "Empty storage".red())?;
64        }
65        Ok(())
66    }
67}
68
69/* -------------------------------------------------------------------------- */
70/*                                 Log struct                                 */
71/* -------------------------------------------------------------------------- */
72
73/// Represents a log entry in the Ethereum Virtual Machine (EVM) state.
74pub struct Log {
75    /// The address of the contract that generated the log.
76    pub address: [u8; 20],
77    /// The topics associated with the log.
78    pub topics: Vec<[u8; 32]>,
79    /// The data associated with the log.
80    pub data: Vec<u8>,
81}
82
83/// Implements the Debug trait for the Log struct, which allows for the struct to be printed in a formatted way.
84/// The function writes the address, topics, and data of the Log struct to the provided formatter.
85/// If the topics vector is not empty, it prints each topic in a formatted way.
86/// Otherwise, it prints "No topics".
87impl fmt::Debug for Log {
88    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89        writeln!(
90            f,
91            "{}: {}",
92            "Address".magenta(),
93            utils::debug::to_hex_address(self.address)
94        )?;
95
96        write!(f, "{}: ", "Topics".magenta())?;
97        if !self.topics.is_empty() {
98            for (idx, topic) in self.topics.iter().enumerate() {
99                println!("\n┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐");
100                let hex: String = utils::debug::to_hex_string(topic.to_owned());
101                println!("│ {}: {} {} │", "Topic".bright_blue(), idx, hex);
102                println!("└────────────────────────────────────────────────────────────────────────────────────────────────────────┘");
103            }
104        } else {
105            writeln!(f, "{}", "No topics".red())?;
106        }
107
108        writeln!(
109            f,
110            "{}: {}",
111            "Data".magenta(),
112            utils::debug::vec_to_hex_string(self.data.clone())
113        )?;
114
115        Ok(())
116    }
117}
118
119/* -------------------------------------------------------------------------- */
120/*                              EVM state struct                              */
121/* -------------------------------------------------------------------------- */
122
123/// Represents the state of the Ethereum Virtual Machine (EVM).
124#[derive(Debug)]
125pub struct EvmState {
126    /// A mapping of account addresses to their respective account states.
127    pub accounts: HashMap<[u8; 20], AccountState>,
128    /// A mapping of code hashes to their respective code.
129    pub codes: HashMap<[u8; 32], Vec<u8>>,
130    /// A vector of logs generated during the execution of the EVM.
131    pub logs: Vec<Log>,
132    /// A flag indicating whether the EVM is in static mode or not.
133    pub static_mode: bool,
134    /// An optional provider for interacting with the Ethereum network.
135    pub provider: Option<Provider<Http>>,
136}
137
138/// Implementation of the EVM state.
139impl EvmState {
140    /// Creates a new instance of the `State` struct.
141    ///
142    /// # Arguments
143    ///
144    /// * `fork_url` - An optional `String` representing the URL of the fork to use.
145    ///
146    /// # Returns
147    ///
148    /// A new instance of the `State` struct.
149    pub fn new(fork_url: Option<String>) -> Self {
150        Self {
151            accounts: HashMap::new(),
152            codes: HashMap::new(),
153            logs: Vec::new(),
154            static_mode: false,
155            provider: if fork_url.is_some() {
156                Some(Provider::<Http>::try_from(fork_url.unwrap()).unwrap())
157            } else {
158                None
159            },
160        }
161    }
162
163    // Transfer value from one account to another
164    /// Transfers a given value from one account to another.
165    ///
166    /// # Arguments
167    ///
168    /// * `from` - An array of 20 bytes representing the address of the account to transfer from.
169    /// * `to` - An array of 20 bytes representing the address of the account to transfer to.
170    /// * `value` - An array of 32 bytes representing the value to transfer.
171    ///
172    /// # Errors
173    ///
174    /// Returns an `ExecutionError` if:
175    ///
176    /// * The static mode is enabled (static call).
177    /// * The account to transfer from does not exist.
178    /// * The account to transfer to does not exist.
179    /// * The balance of the account to transfer from is insufficient.
180    ///
181    /// # Returns
182    ///
183    /// Returns `Ok(())` if the transfer was successful.
184    pub fn transfer(
185        &mut self,
186        from: [u8; 20],
187        to: [u8; 20],
188        value: [u8; 32],
189    ) -> Result<(), ExecutionError> {
190        // Check if static mode is enabled
191        if self.static_mode {
192            return Err(ExecutionError::StaticCallStateChanged);
193        }
194
195        let value_u256 = U256::from_big_endian(&value);
196
197        let from_balance = U256::from_big_endian(
198            &self
199                .accounts
200                .get(&from)
201                .ok_or(ExecutionError::AccountNotFound)?
202                .balance,
203        );
204
205        let to_balance = U256::from_big_endian(
206            &self
207                .accounts
208                .get(&to)
209                .ok_or(ExecutionError::AccountNotFound)?
210                .balance,
211        );
212
213        // Check if the balance is sufficient
214        if from_balance < value_u256 {
215            return Err(ExecutionError::InsufficientBalance);
216        }
217
218        // Transfer the value
219        let new_from_balance = from_balance - value_u256;
220        let new_to_balance = to_balance + value_u256;
221
222        if let Some(from_account) = self.accounts.get_mut(&from) {
223            let mut result_bytes = [0u8; 32];
224            new_from_balance.to_big_endian(&mut result_bytes);
225            from_account.balance = result_bytes;
226        }
227
228        if let Some(to_account) = self.accounts.get_mut(&to) {
229            let mut result_bytes = [0u8; 32];
230            new_to_balance.to_big_endian(&mut result_bytes);
231            to_account.balance = result_bytes;
232        }
233
234        Ok(())
235    }
236
237    /// Loads a 256-bit value from the storage of the given account at the given slot.
238    /// If the account is not found in the emulator's local state, the storage value is fetched from the provider.
239    /// If the provider is not set, or if the storage fetch fails, the function returns a zero-filled 256-bit value.
240    ///
241    /// # Arguments
242    ///
243    /// * `account` - An array of 20 bytes representing the address of the account to load from.
244    /// * `slot` - An array of 32 bytes representing the slot to load from.
245    ///
246    /// # Errors
247    ///
248    /// Never return an error, but returns a 32-byte array of zero instead.
249    ///
250    /// # Returns
251    ///
252    /// Returns a 32-byte array representing the value at the given slot.
253    pub fn sload(&mut self, account: [u8; 20], slot: [u8; 32]) -> Result<[u8; 32], ExecutionError> {
254        match self.accounts.get(&account) {
255            Some(account_state) => match account_state.storage.get(&slot) {
256                Some(value) => Ok(*value),
257                None => Ok([0u8; 32]),
258            },
259            None => {
260                let provider = self.provider.as_ref();
261
262                if provider.is_none() {
263                    return Ok([0u8; 32]);
264                }
265
266                let contract_address = Address::from(account);
267                let future =
268                    provider
269                        .unwrap()
270                        .get_storage_at(contract_address, H256::from(&slot), None);
271
272                // Block on the future and get the result
273                let storage_result = tokio::runtime::Runtime::new()
274                    .expect("Could not create a Runtime")
275                    .block_on(future);
276
277                match storage_result {
278                    Ok(storage) => {
279                        let storage_bytes = storage.to_fixed_bytes();
280
281                        // Save the fetched storage data locally
282                        if let Some(account_state) = self.accounts.get_mut(&account) {
283                            account_state.storage.insert(slot, storage_bytes);
284                        }
285
286                        Ok(storage_bytes)
287                    }
288                    Err(_) => Ok([0u8; 32]),
289                }
290            }
291        }
292    }
293
294    // Store a 32 bytes word in storage of a specific account
295    /// Stores a value in the storage of an account.
296    ///
297    /// # Arguments
298    ///
299    /// * `account` - The address of the account to store the value in.
300    /// * `slot` - The slot in the storage to store the value in.
301    /// * `value` - The value to store in the specified slot.
302    ///
303    /// # Errors
304    ///
305    /// Returns an `ExecutionError` if the static mode is enabled or if the account is not found.
306    pub fn sstore(
307        &mut self,
308        account: [u8; 20],
309        slot: [u8; 32],
310        value: [u8; 32],
311    ) -> Result<(), ExecutionError> {
312        // Check if static mode is enabled
313        if self.static_mode {
314            return Err(ExecutionError::StaticCallStateChanged);
315        }
316
317        match self.accounts.get_mut(&account) {
318            Some(account_state) => {
319                account_state.storage.insert(slot, value);
320                Ok(())
321            }
322            None => Err(ExecutionError::AccountNotFound),
323        }
324    }
325
326    /// Returns the code at the given address. If the code is not already in the state, it will be fetched from the blockchain using the provider.
327    ///
328    /// # Arguments
329    ///
330    /// * `address` - The address of the contract to get the code for.
331    ///
332    /// # Errors
333    ///
334    /// Returns an `ExecutionError` if the code is not found.
335    ///
336    /// # Returns
337    ///
338    /// Returns a reference to the Vec<u8> of the code.
339    pub fn get_code_at(&mut self, address: [u8; 20]) -> Result<&Vec<u8>, ExecutionError> {
340        match self.accounts.get(&address) {
341            Some(account_state) => {
342                let code_hash = account_state.code_hash;
343                self.get_code(code_hash)
344            }
345            None => {
346                let provider = self.provider.as_ref();
347
348                if provider.is_none() {
349                    return Err(ExecutionError::CodeNotFound);
350                }
351
352                // Asynchronously fetch the code from the blockchain
353                let contract_address = Address::from(address);
354
355                let future = provider.unwrap().get_code(contract_address, None);
356
357                // Block on the future and get the result
358                let code_result = tokio::runtime::Runtime::new()
359                    .expect("Could not create a Runtime")
360                    .block_on(future);
361
362                match code_result {
363                    Ok(code) => {
364                        let code_hash = keccak256(&code.0);
365                        if let Some(account) = self.accounts.get_mut(&address) {
366                            account.code_hash = code_hash;
367                        }
368                        self.codes.insert(code_hash, code.to_vec());
369                        Ok(&self.codes[&code_hash])
370                    }
371                    Err(_) => Err(ExecutionError::CodeNotFound),
372                }
373            }
374        }
375    }
376
377    /// Stores the code of an account at the given address.
378    ///
379    /// # Arguments
380    ///
381    /// * `address` - A 20-byte array representing the address of the account.
382    /// * `code` - A vector of bytes representing the code to be stored.
383    ///
384    /// # Errors
385    ///
386    /// Returns an `ExecutionError` if the account is not found.
387    ///
388    /// # Returns
389    ///
390    /// Returns `Ok(())` if the code is successfully stored.
391    pub fn put_code_at(&mut self, address: [u8; 20], code: Vec<u8>) -> Result<(), ExecutionError> {
392        let code_hash = self.put_code(code)?;
393
394        match self.accounts.get_mut(&address) {
395            Some(account_state) => {
396                account_state.code_hash = code_hash.to_owned();
397                Ok(())
398            }
399            None => Err(ExecutionError::AccountNotFound),
400        }
401    }
402
403    /// Returns a reference to the code associated with the given code hash.
404    ///
405    /// # Arguments
406    ///
407    /// * `code_hash` - A 32-byte array representing the hash of the code to retrieve.
408    ///
409    /// # Errors
410    ///
411    /// Returns an `ExecutionError::CodeNotFound` error if the code hash is not found in the state.
412    ///
413    /// # Returns
414    ///
415    /// Returns a reference to the code associated with the given code hash.
416    fn get_code(&self, code_hash: [u8; 32]) -> Result<&Vec<u8>, ExecutionError> {
417        self.codes
418            .get(&code_hash)
419            .ok_or(ExecutionError::CodeNotFound)
420    }
421
422    /// Store contract code and return its hash
423    fn put_code(&mut self, code: Vec<u8>) -> Result<[u8; 32], ExecutionError> {
424        // Check if static mode is enabled
425        if self.static_mode {
426            return Err(ExecutionError::StaticCallStateChanged);
427        }
428
429        let code_hash = keccak256(&code);
430        self.codes.insert(code_hash, code);
431        Ok(code_hash)
432    }
433
434    /// Print the state of the EVM
435    /// This function is used for debugging purposes.
436    pub fn debug_state(&mut self) {
437        let border_line =
438            "\n╔═══════════════════════════════════════════════════════════════════════════════════════════════════════╗";
439        let footer_line =
440            "╚═══════════════════════════════════════════════════════════════════════════════════════════════════════╝";
441
442        // Print out the storage
443        println!("\n{}", border_line.clone().red());
444        println!(
445            "{} {:<101} {}",
446            "║".red(),
447            "Final storage".yellow(),
448            "║".red()
449        );
450        println!("{}", footer_line.clone().red());
451
452        // ... other code ...
453
454        // Create a vector of all addresses
455        let addresses: Vec<_> = self.accounts.keys().cloned().collect();
456
457        // Iterate over the vector of addresses
458        for address in addresses {
459            let account_state = &self.accounts[&address];
460            let hex: String = utils::debug::to_hex_address(address.to_owned());
461            println!("{}", hex.blue());
462            println!("{:?}", account_state);
463            let code_hash = account_state.code_hash;
464            if code_hash != [0u8; 32] {
465                let code = self.get_code_at(address.to_owned()).unwrap();
466                let code_hex: String = utils::debug::vec_to_hex_string(code.to_owned());
467                println!("  {}: {}", "Code".magenta(), code_hex);
468            }
469            println!("\n");
470        }
471
472        if self.accounts.is_empty() {
473            println!("Empty EVM state");
474        }
475    }
476}