linera_execution/evm/
database.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Code specific to the usage of the [Revm](https://bluealloy.github.io/revm/) runtime.
5//! Here we implement the Database traits of Revm.
6
7use std::{
8    collections::HashMap,
9    mem,
10    ops::DerefMut,
11    sync::{Arc, Mutex},
12};
13
14use linera_base::{
15    data_types::Amount,
16    ensure,
17    identifiers::{self, ApplicationId, ModuleId},
18    vm::{EvmInstantiation, EvmQuery, VmRuntime},
19};
20use linera_views::common::from_bytes_option;
21use revm::{primitives::keccak256, Database, DatabaseCommit, DatabaseRef};
22use revm_context::BlockEnv;
23use revm_context_interface::block::BlobExcessGasAndPrice;
24use revm_database::DBErrorMarker;
25use revm_primitives::{address, Address, B256, KECCAK_EMPTY, U256};
26use revm_state::{AccountInfo, Bytecode, EvmState};
27
28use crate::{
29    evm::{
30        inputs::{FAUCET_ADDRESS, FAUCET_BALANCE, ZERO_ADDRESS},
31        revm::{
32            address_to_user_application_id, ALREADY_CREATED_CONTRACT_SELECTOR,
33            COMMIT_CONTRACT_CHANGES_SELECTOR, GET_ACCOUNT_INFO_SELECTOR,
34            GET_CONTRACT_STORAGE_SELECTOR, JSON_EMPTY_VECTOR,
35        },
36    },
37    BaseRuntime, Batch, ContractRuntime, EvmExecutionError, ExecutionError, ServiceRuntime,
38};
39
40// The runtime costs are not available in service operations.
41// We need to set a limit to gas usage in order to avoid blocking
42// the validator.
43// We set up the limit similarly to Infura to 20 million.
44pub const EVM_SERVICE_GAS_LIMIT: u64 = 20_000_000;
45
46impl DBErrorMarker for ExecutionError {}
47
48/// Core database implementation shared by both contract and service execution modes.
49///
50/// This structure bridges Linera's storage layer with Revm's database requirements,
51/// managing state for EVM contract execution. It is used as the foundation for both
52/// `ContractDatabase` (mutable operations) and `ServiceDatabase` (read-only queries).
53///
54/// # Lifecycle
55///
56/// 1. Created with `contract_address` set to `Address::ZERO`
57/// 2. Address updated via `set_contract_address()` once runtime is available
58/// 3. Execution proceeds with proper address context
59///
60/// # Error Handling
61///
62/// Errors during database operations are captured in the `error` field rather than
63/// immediately propagating, allowing Revm to complete its execution flow before
64/// error handling occurs.
65pub(crate) struct InnerDatabase<Runtime> {
66    /// The EVM address of the contract being executed.
67    ///
68    /// Initially set to `Address::ZERO` and updated to the actual contract address
69    /// derived from the Linera `ApplicationId` once the runtime becomes available.
70    pub contract_address: Address,
71
72    /// The address of the entity calling this smart contract.
73    ///
74    /// Corresponds to `msg.sender` in Solidity. May be an EOA address, another
75    /// contract address, or `Address::ZERO` for contracts for which the caller
76    /// does not have an EVM address or has not been authenticated.
77    pub caller: Address,
78
79    /// The amount of native tokens being transferred with this call.
80    ///
81    /// Corresponds to `msg.value` in Solidity.
82    pub value: U256,
83
84    /// The Linera runtime providing access to storage and system operations.
85    ///
86    /// Wrapped in `Arc<Mutex<>>` to allow shared access across database clones
87    /// while maintaining safe mutation.
88    pub runtime: Arc<Mutex<Runtime>>,
89
90    /// Uncommitted state changes from EVM execution.
91    ///
92    /// For contract operations, accumulated during execution and committed at the end.
93    /// For service queries on uninitialized contracts, holds the temporary state
94    /// since persistent storage is not available.
95    pub changes: EvmState,
96
97    /// Whether the contract has been instantiated in Revm's execution context.
98    ///
99    /// A contract may be instantiated in Linera (storage allocated) but not yet
100    /// in Revm (constructor not executed). This flag tracks the Revm state.
101    pub is_revm_instantiated: bool,
102
103    /// Runtime errors captured during database operations.
104    ///
105    /// Errors are stored here rather than immediately returned to allow Revm
106    /// to complete its execution flow. Checked via `process_any_error()` after
107    /// execution completes.
108    pub error: Arc<Mutex<Option<String>>>,
109}
110
111impl<Runtime> Clone for InnerDatabase<Runtime> {
112    fn clone(&self) -> Self {
113        Self {
114            contract_address: self.contract_address,
115            caller: self.caller,
116            value: self.value,
117            runtime: self.runtime.clone(),
118            changes: self.changes.clone(),
119            is_revm_instantiated: self.is_revm_instantiated,
120            error: self.error.clone(),
121        }
122    }
123}
124
125/// Encodes the `index` of the EVM storage associated with the smart contract
126/// in a Linera key.
127fn get_storage_key(index: U256) -> Vec<u8> {
128    let mut key = vec![KeyCategory::Storage as u8];
129    key.extend(index.as_le_slice());
130    key
131}
132
133/// Returns the storage key for a given category.
134fn get_category_key(category: KeyCategory) -> Vec<u8> {
135    vec![category as u8]
136}
137
138impl<Runtime> InnerDatabase<Runtime>
139where
140    Runtime: BaseRuntime,
141{
142    /// Creates a new database instance with default values.
143    ///
144    /// The contract address is initially set to `Address::ZERO` and must be updated
145    /// later via `set_contract_address()` once the runtime can be safely accessed.
146    /// This deferred initialization is necessary because locking the runtime during
147    /// construction is not possible in the use cases of this code.
148    pub fn new(runtime: Runtime) -> Self {
149        Self {
150            contract_address: Address::ZERO,
151            caller: Address::ZERO,
152            value: U256::ZERO,
153            runtime: Arc::new(Mutex::new(runtime)),
154            changes: HashMap::new(),
155            is_revm_instantiated: false,
156            error: Arc::new(Mutex::new(None)),
157        }
158    }
159
160    /// Acquires exclusive access to the runtime.
161    ///
162    /// # Returns
163    ///
164    /// A mutex guard providing mutable access to the runtime. The lock is
165    /// automatically released when the guard goes out of scope.
166    ///
167    /// # Panics
168    ///
169    /// Panics if the mutex is poisoned (another thread panicked while holding the lock).
170    /// This should not occur in normal operation as the runtime is not shared across threads.
171    pub fn lock_runtime(&self) -> std::sync::MutexGuard<'_, Runtime> {
172        self.runtime.lock().unwrap()
173    }
174
175    /// Captures an execution error for later processing.
176    ///
177    /// Errors are stored rather than immediately returned to allow Revm to complete
178    /// its execution flow. The error can be checked later via `process_any_error()`.
179    pub fn insert_error(&self, exec_error: ExecutionError) {
180        let mut error = self.error.lock().unwrap();
181        *error = Some(format!("Runtime error {:?}", exec_error));
182    }
183
184    /// Checks for and returns any captured errors.
185    ///
186    /// This should be called after Revm execution completes to handle any errors
187    /// that were captured during database operations.
188    ///
189    /// # Errors
190    ///
191    /// Returns an error if one was previously captured via `insert_error()`.
192    pub fn process_any_error(&self) -> Result<(), EvmExecutionError> {
193        let error = self.error.lock().unwrap();
194        if let Some(error) = error.clone() {
195            return Err(EvmExecutionError::RuntimeError(error.clone()));
196        }
197        Ok(())
198    }
199
200    /// Reads the account info from the storage for a contract A
201    /// whose address is `address`.
202    /// * The function `f` is accessing the state for an account
203    ///   whose address is different from `contract_address` (e.g.
204    ///   a contract created in the contract A or accessed by A,
205    ///   e.g. ERC20).
206    ///   For `ContractRuntime` and `ServiceRuntime` the method
207    ///   to access is different. So, the function has to be
208    ///   provided as argument.
209    /// * `address`: The address being read.
210    /// * `is_newly_created`: Whether the contract is newly created
211    ///   or not. For newly created contract, the balance is the
212    ///   one of Revm. For existing contract, the balance has to
213    ///   be accessed from Linera.
214    ///
215    /// For Externally Owned Accounts, the function is indeed
216    /// called, but it does not access the storage. Instead it
217    /// creates a default `AccountInfo` whose balance is computed
218    /// from the one in Linera. This is the case both for the faucet
219    /// and for other accounts.
220    ///
221    /// For the contract for which `address == contract_address` we
222    /// access the `AccountInfo` locally from the storage. For other
223    /// contracts we need to access other contracts (with the
224    /// function `f`)
225    fn read_basic_ref(
226        &self,
227        f: fn(&Self, Address) -> Result<Option<AccountInfo>, ExecutionError>,
228        address: Address,
229        is_newly_created: bool,
230    ) -> Result<Option<AccountInfo>, ExecutionError> {
231        if address == FAUCET_ADDRESS {
232            return Ok(Some(AccountInfo {
233                balance: FAUCET_BALANCE,
234                ..AccountInfo::default()
235            }));
236        }
237        let mut account_info = self
238            .account_info_from_storage(f, address)?
239            .unwrap_or_default();
240        if !is_newly_created {
241            // For EOA and old contract the balance comes from Linera.
242            account_info.balance = self.get_start_balance(address)?;
243        }
244        // We return an account as there is no difference between
245        // a default account and the absence of account.
246        Ok(Some(account_info))
247    }
248
249    /// Reads the state from the local storage.
250    fn account_info_from_local_storage(&self) -> Result<Option<AccountInfo>, ExecutionError> {
251        let mut runtime = self.runtime.lock().unwrap();
252        let key_info = get_category_key(KeyCategory::AccountInfo);
253        let promise = runtime.read_value_bytes_new(key_info)?;
254        let result = runtime.read_value_bytes_wait(&promise)?;
255        Ok(from_bytes_option::<AccountInfo>(&result)?)
256    }
257
258    /// Reads the state from the inner database.
259    ///
260    /// If `changes` is not empty, then it means that
261    /// the contract has been instantiated for a
262    /// service query. In that case we do not have
263    /// any storage possible to access, just changes.
264    ///
265    /// In the other case, we access the storage directly.
266    fn account_info_from_inner_database(
267        &self,
268        address: Address,
269    ) -> Result<Option<AccountInfo>, ExecutionError> {
270        if !self.changes.is_empty() {
271            // This case occurs in only one scenario:
272            // * A service call to a contract that has not yet been
273            //   initialized by a contract call.
274            // When we do a service calls to a contract that has
275            // already been initialized, then changes will be empty.
276            let account = self.changes.get(&address);
277            return Ok(account.map(|account| account.info.clone()));
278        }
279        if address == self.contract_address {
280            // This is for the contract and its storage.
281            self.account_info_from_local_storage()
282        } else {
283            // This matches EOA and other contracts.
284            Ok(None)
285        }
286    }
287
288    /// Reads the state from local contract storage.
289    fn account_info_from_storage(
290        &self,
291        f: fn(&Self, Address) -> Result<Option<AccountInfo>, ExecutionError>,
292        address: Address,
293    ) -> Result<Option<AccountInfo>, ExecutionError> {
294        let account_info = self.account_info_from_inner_database(address)?;
295        if let Some(account_info) = account_info {
296            // This matches service calls or the contract itself.
297            return Ok(Some(account_info));
298        }
299        if self.has_empty_storage(address)? {
300            // This matches EOA
301            Ok(None)
302        } else {
303            // This matches other EVM contracts.
304            f(self, address)
305        }
306    }
307
308    /// Returns whether the address has empty storage.
309    /// An address has an empty storage if and only if it is
310    /// an externally owned account (EOA).
311    fn has_empty_storage(&self, address: Address) -> Result<bool, ExecutionError> {
312        let application_id = address_to_user_application_id(address);
313        let mut runtime = self.runtime.lock().unwrap();
314        runtime.has_empty_storage(application_id)
315    }
316
317    /// Reads the starting balance for an account, adjusting for double-transfer prevention.
318    ///
319    /// # Balance Adjustment Logic
320    ///
321    /// To prevent double-transfers, balances are adjusted based on the account role:
322    ///
323    /// 1. **Execution Flow:**
324    ///    - `deposit_funds()` transfers value from caller to contract (Linera layer)
325    ///    - Account balances are read (this function)
326    ///    - Revm performs its own transfer during execution
327    ///
328    /// 2. **Adjustments:**
329    ///    - **Caller:** Balance increased by `self.value` (compensates for pre-transfer)
330    ///    - **Contract:** Balance decreased by `self.value` (compensates for pre-receipt)
331    ///    - **Others:** Balance unchanged
332    ///
333    /// This ensures Revm sees the correct post-`deposit_funds` state when it
334    /// performs its transfer, avoiding double-counting.
335    fn get_start_balance(&self, address: Address) -> Result<U256, ExecutionError> {
336        let mut runtime = self.runtime.lock().unwrap();
337        let account_owner = address.into();
338        let balance = runtime.read_owner_balance(account_owner)?;
339        let balance: U256 = balance.into();
340
341        Ok(if self.caller == address {
342            // Caller has already transferred funds, so we add them back
343            balance + self.value
344        } else if self.contract_address == address {
345            // Contract has already received funds, so we subtract them
346            assert!(
347                balance >= self.value,
348                "Contract balance should be >= transferred value"
349            );
350            balance - self.value
351        } else {
352            // Other accounts are unaffected
353            balance
354        })
355    }
356
357    pub fn get_account_info(&self) -> Result<AccountInfo, ExecutionError> {
358        let address = self.contract_address;
359        let account_info = self.account_info_from_inner_database(address)?;
360        let mut account_info = account_info.ok_or(EvmExecutionError::MissingAccountInfo)?;
361        account_info.balance = self.get_start_balance(address)?;
362        Ok(account_info)
363    }
364
365    /// Reads the storage entry.
366    /// * The function `f` is about accessing a storage value.
367    ///   The function varies for `Contract` and `Service`.
368    /// * The `address` and `index` are the one of the query.
369    fn read_storage(
370        &self,
371        f: fn(&Self, Address, U256) -> Result<U256, ExecutionError>,
372        address: Address,
373        index: U256,
374    ) -> Result<U256, ExecutionError> {
375        if !self.changes.is_empty() {
376            // This is the case of a contract instantiated for a service call.
377            // The storage values are accessed there.
378            let account = self.changes.get(&address).unwrap();
379            return Ok(match account.storage.get(&index) {
380                None => U256::ZERO,
381                Some(slot) => slot.present_value(),
382            });
383        }
384        if address == self.contract_address {
385            // In that case we access the value from the
386            // local storage.
387            return self.read_from_local_storage(index);
388        }
389        // Use the function for accessing the value.
390        f(self, address, index)
391    }
392
393    /// Reads the value from the local storage.
394    pub fn read_from_local_storage(&self, index: U256) -> Result<U256, ExecutionError> {
395        let key = get_storage_key(index);
396        let mut runtime = self.runtime.lock().unwrap();
397        let promise = runtime.read_value_bytes_new(key)?;
398        let result = runtime.read_value_bytes_wait(&promise)?;
399        Ok(from_bytes_option::<U256>(&result)?.unwrap_or_default())
400    }
401
402    /// Initializes the contract address from the Linera `ApplicationId`.
403    ///
404    /// During database construction, the contract address is set to `Address::ZERO`
405    /// because the runtime cannot be locked at that time. This method updates it to
406    /// the actual EVM address derived from the Linera `ApplicationId`.
407    ///
408    /// # Errors
409    ///
410    /// Returns an error if some step fails.
411    pub fn set_contract_address(&mut self) -> Result<(), ExecutionError> {
412        let mut runtime = self.runtime.lock().unwrap();
413        let application_id = runtime.application_id()?;
414        self.contract_address = application_id.evm_address();
415        Ok(())
416    }
417
418    /// Checks whether the contract has been initialized in Revm and updates the flag.
419    ///
420    /// A contract is considered Revm-initialized if the constructor has been executed,
421    /// producing both the deployed bytecode and initial storage state. This is distinct
422    /// from Linera initialization, which only allocates storage without executing the
423    /// constructor.
424    ///
425    /// The initialization status is determined by checking for the presence of
426    /// `AccountInfo` in storage, which is written only after successful constructor
427    /// execution.
428    ///
429    /// # Returns
430    ///
431    /// Returns `true` if the contract is initialized (has `AccountInfo` in storage),
432    /// `false` otherwise. Also updates `self.is_revm_instantiated` with the result.
433    ///
434    /// # Errors
435    ///
436    /// Returns an error if some step fails.
437    pub fn set_is_initialized(&mut self) -> Result<bool, ExecutionError> {
438        let mut runtime = self.runtime.lock().unwrap();
439        let key_info = get_category_key(KeyCategory::AccountInfo);
440        let promise = runtime.contains_key_new(key_info)?;
441        let result = runtime.contains_key_wait(&promise)?;
442        self.is_revm_instantiated = result;
443        Ok(result)
444    }
445
446    pub fn get_block_env(&self) -> Result<BlockEnv, ExecutionError> {
447        let mut runtime = self.runtime.lock().unwrap();
448        // The block height being used
449        let block_height_linera = runtime.block_height()?;
450        let block_height_evm = block_height_linera.0;
451        // This is the receiver address of all the gas spent in the block.
452        let beneficiary = address!("00000000000000000000000000000000000000bb");
453        // The difficulty which is no longer relevant after The Merge.
454        let difficulty = U256::ZERO;
455        // We do not have access to the Resources so we keep it to the maximum
456        // and the control is done elsewhere.
457        let gas_limit = u64::MAX;
458        // The timestamp. Both the EVM and Linera use the same UNIX epoch.
459        // But the Linera epoch is in microseconds since the start and the
460        // Ethereum epoch is in seconds
461        let timestamp_linera = runtime.read_system_timestamp()?;
462        let timestamp_evm = timestamp_linera.micros() / 1_000_000;
463        // The base fee is the minimum fee for executing a transaction.
464        // We have no such concept in Linera.
465        let basefee = 0;
466        let chain_id = runtime.chain_id()?;
467        let entry = format!("{}{}", chain_id, block_height_linera);
468        // The randomness beacon being used.
469        let prevrandao = keccak256(entry.as_bytes());
470        // The blob excess gas and price is not relevant to the execution
471        // on Linera. We set up a default value as in REVM.
472        let entry = BlobExcessGasAndPrice {
473            excess_blob_gas: 0,
474            blob_gasprice: 1,
475        };
476        let blob_excess_gas_and_price = Some(entry);
477        Ok(BlockEnv {
478            number: block_height_evm,
479            beneficiary,
480            difficulty,
481            gas_limit,
482            timestamp: timestamp_evm,
483            basefee,
484            prevrandao: Some(prevrandao),
485            blob_excess_gas_and_price,
486        })
487    }
488
489    pub fn constructor_argument(&self) -> Result<Vec<u8>, ExecutionError> {
490        let mut runtime = self.runtime.lock().unwrap();
491        let constructor_argument = runtime.application_parameters()?;
492        Ok(serde_json::from_slice::<Vec<u8>>(&constructor_argument)?)
493    }
494}
495
496impl<Runtime> InnerDatabase<Runtime>
497where
498    Runtime: ContractRuntime,
499{
500    /// Gets the smart contract code if existing.
501    fn get_contract_account_info(
502        &self,
503        address: Address,
504    ) -> Result<Option<AccountInfo>, ExecutionError> {
505        let application_id = address_to_user_application_id(address);
506        let argument = GET_ACCOUNT_INFO_SELECTOR.to_vec();
507        let mut runtime = self.runtime.lock().unwrap();
508        let account_info = runtime.try_call_application(false, application_id, argument)?;
509        let account_info = bcs::from_bytes(&account_info)?;
510        Ok(Some(account_info))
511    }
512
513    /// Gets the storage value of another contract.
514    fn get_contract_storage_value(
515        &self,
516        address: Address,
517        index: U256,
518    ) -> Result<U256, ExecutionError> {
519        let application_id = address_to_user_application_id(address);
520        let mut argument = GET_CONTRACT_STORAGE_SELECTOR.to_vec();
521        argument.extend(bcs::to_bytes(&index)?);
522        let mut runtime = self.runtime.lock().unwrap();
523        let value = runtime.try_call_application(false, application_id, argument)?;
524        let value = bcs::from_bytes(&value)?;
525        Ok(value)
526    }
527
528    pub fn deposit_funds(&self) -> Result<(), ExecutionError> {
529        if self.value != U256::ZERO {
530            if self.caller == ZERO_ADDRESS {
531                let error = EvmExecutionError::UnknownSigner;
532                return Err(error.into());
533            }
534            let source = self.caller.into();
535            let amount = Amount::try_from(self.value).map_err(EvmExecutionError::from)?;
536            let mut runtime = self.runtime.lock().expect("The lock should be possible");
537            let chain_id = runtime.chain_id()?;
538            let application_id = runtime.application_id()?;
539            let owner = application_id.into();
540            let destination = identifiers::Account { chain_id, owner };
541            let authenticated_caller = runtime.authenticated_caller_id()?;
542            if authenticated_caller.is_none() {
543                runtime.transfer(source, destination, amount)?;
544            }
545        }
546        Ok(())
547    }
548}
549
550impl<Runtime> InnerDatabase<Runtime>
551where
552    Runtime: ServiceRuntime,
553{
554    /// Gets the account info via a service query.
555    fn get_service_account_info(
556        &self,
557        address: Address,
558    ) -> Result<Option<AccountInfo>, ExecutionError> {
559        let application_id = address_to_user_application_id(address);
560        let argument = serde_json::to_vec(&EvmQuery::AccountInfo)?;
561        let mut runtime = self.runtime.lock().expect("The lock should be possible");
562        let account_info = runtime.try_query_application(application_id, argument)?;
563        let account_info = serde_json::from_slice::<AccountInfo>(&account_info)?;
564        Ok(Some(account_info))
565    }
566
567    /// Gets the storage value by doing a storage service query.
568    fn get_service_storage_value(
569        &self,
570        address: Address,
571        index: U256,
572    ) -> Result<U256, ExecutionError> {
573        let application_id = address_to_user_application_id(address);
574        let argument = serde_json::to_vec(&EvmQuery::Storage(index))?;
575        let mut runtime = self.runtime.lock().expect("The lock should be possible");
576        let value = runtime.try_query_application(application_id, argument)?;
577        let value = serde_json::from_slice::<U256>(&value)?;
578        Ok(value)
579    }
580}
581
582impl<Runtime> ContractDatabase<Runtime>
583where
584    Runtime: ContractRuntime,
585{
586    pub fn new(runtime: Runtime) -> Self {
587        Self {
588            inner: InnerDatabase::new(runtime),
589            modules: Arc::new(Mutex::new(HashMap::new())),
590        }
591    }
592
593    pub fn lock_runtime(&self) -> std::sync::MutexGuard<'_, Runtime> {
594        self.inner.lock_runtime()
595    }
596
597    /// Balances of the contracts have to be checked when
598    /// writing. There is a balance in Linera and a balance
599    /// in EVM and they have to be coherent.
600    fn check_balance(
601        &mut self,
602        address: Address,
603        revm_balance: U256,
604    ) -> Result<(), ExecutionError> {
605        let mut runtime = self.inner.runtime.lock().unwrap();
606        let owner = address.into();
607        let linera_balance: U256 = runtime.read_owner_balance(owner)?.into();
608        ensure!(
609            linera_balance == revm_balance,
610            EvmExecutionError::IncoherentBalances(address, linera_balance, revm_balance)
611        );
612        Ok(())
613    }
614
615    /// Effectively commits changes to storage.
616    pub fn commit_contract_changes(
617        &mut self,
618        account: &revm_state::Account,
619    ) -> Result<(), ExecutionError> {
620        let mut runtime = self.inner.runtime.lock().unwrap();
621        let mut batch = Batch::new();
622        let key_prefix = get_category_key(KeyCategory::Storage);
623        let key_info = get_category_key(KeyCategory::AccountInfo);
624        if account.is_selfdestructed() {
625            batch.delete_key_prefix(key_prefix);
626            batch.put_key_value(key_info, &AccountInfo::default())?;
627        } else {
628            batch.put_key_value(key_info, &account.info)?;
629            for (index, value) in &account.storage {
630                if value.present_value() != value.original_value() {
631                    let key = get_storage_key(*index);
632                    if value.present_value() == U256::ZERO {
633                        batch.delete_key(key);
634                    } else {
635                        batch.put_key_value(key, &value.present_value())?;
636                    }
637                }
638            }
639        }
640        runtime.write_batch(batch)?;
641        Ok(())
642    }
643
644    /// Returns whether the account is writable.
645    /// We do not write the accounts of Externally Owned Accounts.
646    fn is_account_writable(&self, address: &Address, account: &revm_state::Account) -> bool {
647        if *address == FAUCET_ADDRESS {
648            // We do not write the faucet address nor expect any coherency from it.
649            return false;
650        }
651        if !account.is_touched() {
652            // Not modified accounts do not need to be written down.
653            return false;
654        }
655        let code_hash = account.info.code_hash;
656        // User accounts are not written. This is fine since the balance
657        // is accessed from Linera and the nonce are not accessible in
658        // EVM smart contracts.
659        let code_empty = code_hash == KECCAK_EMPTY || code_hash.is_zero();
660        !code_empty
661    }
662
663    /// Whether the balance of this account needs to be checked.
664    fn is_account_checkable(&self, address: &Address) -> bool {
665        if *address == FAUCET_ADDRESS {
666            // We do not check the FAUCET balance.
667            return false;
668        }
669        true
670    }
671
672    /// Creates a new contract. The `account` contains
673    /// the AccountInfo and the storage to be written.
674    /// The parameters is empty because the constructor
675    /// does not need to be concatenated as it has
676    /// already been concatenated to the bytecode in the
677    /// init_code.
678    fn create_new_contract(
679        &mut self,
680        address: Address,
681        account: revm_state::Account,
682        module_id: ModuleId,
683    ) -> Result<(), ExecutionError> {
684        let application_id = address_to_user_application_id(address);
685        let mut argument = ALREADY_CREATED_CONTRACT_SELECTOR.to_vec();
686        argument.extend(bcs::to_bytes(&account)?);
687        let evm_instantiation = EvmInstantiation {
688            value: U256::ZERO,
689            argument,
690        };
691        let argument = serde_json::to_vec(&evm_instantiation)?;
692        let parameters = JSON_EMPTY_VECTOR.to_vec(); // No constructor
693        let required_application_ids = Vec::new();
694        let mut runtime = self.inner.runtime.lock().unwrap();
695        let created_application_id = runtime.create_application(
696            module_id,
697            parameters,
698            argument,
699            required_application_ids,
700        )?;
701        ensure!(
702            application_id == created_application_id,
703            EvmExecutionError::IncorrectApplicationId
704        );
705        Ok(())
706    }
707
708    /// Commits the changes to another contract.
709    /// This is done by doing a call application.
710    fn commit_remote_contract(
711        &mut self,
712        address: Address,
713        account: revm_state::Account,
714    ) -> Result<(), ExecutionError> {
715        let application_id = address_to_user_application_id(address);
716        let mut argument = COMMIT_CONTRACT_CHANGES_SELECTOR.to_vec();
717        argument.extend(bcs::to_bytes(&account)?);
718        let mut runtime = self.inner.runtime.lock().unwrap();
719        runtime.try_call_application(false, application_id, argument)?;
720        Ok(())
721    }
722
723    /// Effectively commits changes to storage.
724    /// This is done in the following way:
725    /// * Identify the balances that need to be checked
726    /// * Write down the state of the contract for `contract_address` locally.
727    /// * For the other contracts, if it already created, commit it.
728    ///
729    /// If not insert them into the map.
730    /// * Iterates over the entries of the map and creates the contracts in the
731    ///   right order.
732    pub fn commit_changes(&mut self) -> Result<(), ExecutionError> {
733        let changes = mem::take(&mut self.inner.changes);
734        let mut balances = Vec::new();
735        let modules = mem::take(self.modules.lock().unwrap().deref_mut());
736        let mut contracts_to_create = vec![None; modules.len()];
737        for (address, account) in changes {
738            if self.is_account_checkable(&address) {
739                let revm_balance = account.info.balance;
740                balances.push((address, revm_balance));
741            }
742            if self.is_account_writable(&address, &account) {
743                if address == self.inner.contract_address {
744                    self.commit_contract_changes(&account)?;
745                } else {
746                    let application_id = address_to_user_application_id(address);
747                    if let Some((module_id, index)) = modules.get(&application_id) {
748                        contracts_to_create[*index as usize] = Some((address, account, *module_id));
749                    } else {
750                        self.commit_remote_contract(address, account)?;
751                    }
752                }
753            }
754        }
755        for entry in contracts_to_create {
756            let (address, account, module_id) =
757                entry.expect("An entry since all have been matched above");
758            self.create_new_contract(address, account, module_id)?;
759        }
760        for (address, revm_balance) in balances {
761            self.check_balance(address, revm_balance)?;
762        }
763        Ok(())
764    }
765}
766
767/// Categories for organizing different types of data in the storage.
768///
769/// Each category is prefixed with a unique byte when encoding storage keys,
770/// allowing different types of data to coexist without key collisions.
771#[repr(u8)]
772pub enum KeyCategory {
773    /// Account information including code hash, nonce, and balance.
774    AccountInfo,
775    /// Contract storage values indexed by U256 keys.
776    Storage,
777}
778
779/// The Database for contracts
780pub(crate) struct ContractDatabase<Runtime> {
781    pub inner: InnerDatabase<Runtime>,
782    pub modules: Arc<Mutex<HashMap<ApplicationId, (ModuleId, u32)>>>,
783}
784
785impl<Runtime> Clone for ContractDatabase<Runtime> {
786    fn clone(&self) -> Self {
787        Self {
788            inner: self.inner.clone(),
789            modules: self.modules.clone(),
790        }
791    }
792}
793
794impl<Runtime> DatabaseRef for ContractDatabase<Runtime>
795where
796    Runtime: ContractRuntime,
797{
798    type Error = ExecutionError;
799
800    /// The `basic_ref` is the function for reading the state of the application.
801    /// The code `read_basic_ref` is used with the relevant access function.
802    fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, ExecutionError> {
803        let is_newly_created = {
804            let modules = self.modules.lock().unwrap();
805            let application_id = address_to_user_application_id(address);
806            modules.contains_key(&application_id)
807        };
808        self.inner.read_basic_ref(
809            InnerDatabase::<Runtime>::get_contract_account_info,
810            address,
811            is_newly_created,
812        )
813    }
814
815    /// There are two ways to implement the trait:
816    /// * Returns entries with "code: Some(...)"
817    /// * Returns entries with "code: None".
818    ///
819    /// Since we choose the first design, `code_by_hash_ref` is not needed. There
820    /// is an example in the Revm source code of this kind.
821    fn code_by_hash_ref(&self, _code_hash: B256) -> Result<Bytecode, ExecutionError> {
822        panic!("Returned AccountInfo should have code: Some(...) and so code_by_hash_ref should never be called");
823    }
824
825    /// Accesses the storage by the relevant remote access function.
826    fn storage_ref(&self, address: Address, index: U256) -> Result<U256, ExecutionError> {
827        self.inner.read_storage(
828            InnerDatabase::<Runtime>::get_contract_storage_value,
829            address,
830            index,
831        )
832    }
833
834    fn block_hash_ref(&self, number: u64) -> Result<B256, ExecutionError> {
835        Ok(keccak256(number.to_string().as_bytes()))
836    }
837}
838
839impl<Runtime> Database for ContractDatabase<Runtime>
840where
841    Runtime: ContractRuntime,
842{
843    type Error = ExecutionError;
844
845    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, ExecutionError> {
846        self.basic_ref(address)
847    }
848
849    fn code_by_hash(&mut self, _code_hash: B256) -> Result<Bytecode, ExecutionError> {
850        panic!("Returned AccountInfo should have code: Some(...) and so code_by_hash should never be called");
851    }
852
853    fn storage(&mut self, address: Address, index: U256) -> Result<U256, ExecutionError> {
854        self.storage_ref(address, index)
855    }
856
857    fn block_hash(&mut self, number: u64) -> Result<B256, ExecutionError> {
858        <Self as DatabaseRef>::block_hash_ref(self, number)
859    }
860}
861
862impl<Runtime> DatabaseCommit for ContractDatabase<Runtime>
863where
864    Runtime: ContractRuntime,
865{
866    fn commit(&mut self, changes: EvmState) {
867        self.inner.changes = changes;
868    }
869}
870
871impl<Runtime> ContractDatabase<Runtime>
872where
873    Runtime: ContractRuntime,
874{
875    pub fn get_block_env(&self) -> Result<BlockEnv, ExecutionError> {
876        let mut block_env = self.inner.get_block_env()?;
877        let mut runtime = self.inner.runtime.lock().unwrap();
878        // We use the gas_limit from the runtime
879        let gas_limit = runtime.maximum_fuel_per_block(VmRuntime::Evm)?;
880        block_env.gas_limit = gas_limit;
881        Ok(block_env)
882    }
883
884    /// Reads the nonce of the user
885    pub fn get_nonce(&self, address: &Address) -> Result<u64, ExecutionError> {
886        let account_info = self.basic_ref(*address)?;
887        Ok(match account_info {
888            None => 0,
889            Some(account_info) => account_info.nonce,
890        })
891    }
892}
893
894// The Database for service
895
896pub(crate) struct ServiceDatabase<Runtime> {
897    pub inner: InnerDatabase<Runtime>,
898}
899
900impl<Runtime> Clone for ServiceDatabase<Runtime> {
901    fn clone(&self) -> Self {
902        Self {
903            inner: self.inner.clone(),
904        }
905    }
906}
907
908impl<Runtime> DatabaseRef for ServiceDatabase<Runtime>
909where
910    Runtime: ServiceRuntime,
911{
912    type Error = ExecutionError;
913
914    /// The `basic_ref` is the function for reading the state of the application.
915    /// There is no newly created contracts for services.
916    fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, ExecutionError> {
917        let is_newly_created = false; // No contract creation in service
918        self.inner.read_basic_ref(
919            InnerDatabase::<Runtime>::get_service_account_info,
920            address,
921            is_newly_created,
922        )
923    }
924
925    /// There are two ways to implements the trait:
926    /// * Returns entries with "code: Some(...)"
927    /// * Returns entries with "code: None".
928    ///
929    /// Since we choose the first design, `code_by_hash_ref` is not needed. There
930    /// is an example in the Revm source code of this kind.
931    fn code_by_hash_ref(&self, _code_hash: B256) -> Result<Bytecode, ExecutionError> {
932        panic!("Returned AccountInfo should have code: Some(...) and so code_by_hash_ref should never be called");
933    }
934
935    fn storage_ref(&self, address: Address, index: U256) -> Result<U256, ExecutionError> {
936        self.inner.read_storage(
937            InnerDatabase::<Runtime>::get_service_storage_value,
938            address,
939            index,
940        )
941    }
942
943    fn block_hash_ref(&self, number: u64) -> Result<B256, ExecutionError> {
944        Ok(keccak256(number.to_string().as_bytes()))
945    }
946}
947
948impl<Runtime> Database for ServiceDatabase<Runtime>
949where
950    Runtime: ServiceRuntime,
951{
952    type Error = ExecutionError;
953
954    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, ExecutionError> {
955        self.basic_ref(address)
956    }
957
958    fn code_by_hash(&mut self, _code_hash: B256) -> Result<Bytecode, ExecutionError> {
959        panic!("Returned AccountInfo should have code: Some(...) and so code_by_hash should never be called");
960    }
961
962    fn storage(&mut self, address: Address, index: U256) -> Result<U256, ExecutionError> {
963        self.storage_ref(address, index)
964    }
965
966    fn block_hash(&mut self, number: u64) -> Result<B256, ExecutionError> {
967        <Self as DatabaseRef>::block_hash_ref(self, number)
968    }
969}
970
971impl<Runtime> DatabaseCommit for ServiceDatabase<Runtime>
972where
973    Runtime: ServiceRuntime,
974{
975    fn commit(&mut self, changes: EvmState) {
976        self.inner.changes = changes;
977    }
978}
979
980impl<Runtime> ServiceDatabase<Runtime>
981where
982    Runtime: ServiceRuntime,
983{
984    pub fn new(runtime: Runtime) -> Self {
985        Self {
986            inner: InnerDatabase::new(runtime),
987        }
988    }
989
990    pub fn lock_runtime(&self) -> std::sync::MutexGuard<'_, Runtime> {
991        self.inner.lock_runtime()
992    }
993
994    pub fn get_block_env(&self) -> Result<BlockEnv, ExecutionError> {
995        let mut block_env = self.inner.get_block_env()?;
996        block_env.gas_limit = EVM_SERVICE_GAS_LIMIT;
997        Ok(block_env)
998    }
999
1000    /// Reads the nonce of the user
1001    pub fn get_nonce(&self, address: &Address) -> Result<u64, ExecutionError> {
1002        let account_info = self.basic_ref(*address)?;
1003        Ok(match account_info {
1004            None => 0,
1005            Some(account_info) => account_info.nonce,
1006        })
1007    }
1008}