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    sync::{Arc, Mutex},
10};
11
12use linera_base::{data_types::Amount, ensure, identifiers::Account, vm::VmRuntime};
13use linera_views::common::from_bytes_option;
14use revm::{primitives::keccak256, Database, DatabaseCommit, DatabaseRef};
15use revm_context::BlockEnv;
16use revm_context_interface::block::BlobExcessGasAndPrice;
17use revm_database::{AccountState, DBErrorMarker};
18use revm_primitives::{address, Address, B256, U256};
19use revm_state::{AccountInfo, Bytecode, EvmState};
20
21use crate::{
22    evm::inputs::{FAUCET_ADDRESS, FAUCET_BALANCE, ZERO_ADDRESS},
23    BaseRuntime, Batch, ContractRuntime, EvmExecutionError, ExecutionError, ServiceRuntime,
24};
25
26// The runtime costs are not available in service operations.
27// We need to set a limit to gas usage in order to avoid blocking
28// the validator.
29// We set up the limit similarly to Infura to 20 million.
30pub const EVM_SERVICE_GAS_LIMIT: u64 = 20_000_000;
31
32/// The cost of loading from storage.
33const SLOAD_COST: u64 = 2100;
34
35/// The cost of storing a non-zero value in the storage for the first time.
36const SSTORE_COST_SET: u64 = 20000;
37
38/// The cost of not changing the state of the variable in the storage.
39const SSTORE_COST_NO_OPERATION: u64 = 100;
40
41/// The cost of overwriting the storage to a different value.
42const SSTORE_COST_RESET: u64 = 2900;
43
44/// The refund from releasing data.
45const SSTORE_REFUND_RELEASE: u64 = 4800;
46
47/// The number of key writes, reads, release, and no change in EVM has to be accounted for.
48/// Then we remove those costs from the final bill.
49#[derive(Clone, Default)]
50pub(crate) struct StorageStats {
51    key_no_operation: u64,
52    key_reset: u64,
53    key_set: u64,
54    key_release: u64,
55    key_read: u64,
56}
57
58impl StorageStats {
59    pub fn storage_costs(&self) -> u64 {
60        let mut storage_costs = 0;
61        storage_costs += self.key_no_operation * SSTORE_COST_NO_OPERATION;
62        storage_costs += self.key_reset * SSTORE_COST_RESET;
63        storage_costs += self.key_set * SSTORE_COST_SET;
64        storage_costs += self.key_read * SLOAD_COST;
65        storage_costs
66    }
67
68    pub fn storage_refund(&self) -> u64 {
69        self.key_release * SSTORE_REFUND_RELEASE
70    }
71}
72
73/// This is the encapsulation of the `Runtime` corresponding to the contract.
74pub(crate) struct DatabaseRuntime<Runtime> {
75    /// This is the storage statistics of the read/write in order to adjust gas costs.
76    storage_stats: Arc<Mutex<StorageStats>>,
77    /// This is the EVM address of the contract.
78    /// At the creation, it is set to `Address::ZERO` and then later set to the correct value.
79    pub contract_address: Address,
80    /// The caller to the smart contract.
81    pub caller: Address,
82    /// The value of the call to the smart contract.
83    pub value: U256,
84    /// The runtime of the contract.
85    pub runtime: Arc<Mutex<Runtime>>,
86    /// The uncommitted changes to the contract.
87    pub changes: EvmState,
88    /// Whether the contract has been instantiated in REVM.
89    pub is_revm_instantiated: bool,
90    /// The error that can occur during runtime.
91    pub error: Arc<Mutex<Option<String>>>,
92}
93
94impl<Runtime> Clone for DatabaseRuntime<Runtime> {
95    fn clone(&self) -> Self {
96        Self {
97            storage_stats: self.storage_stats.clone(),
98            contract_address: self.contract_address,
99            caller: self.caller,
100            value: self.value,
101            runtime: self.runtime.clone(),
102            changes: self.changes.clone(),
103            is_revm_instantiated: self.is_revm_instantiated,
104            error: self.error.clone(),
105        }
106    }
107}
108
109#[repr(u8)]
110pub enum KeyCategory {
111    AccountInfo,
112    AccountState,
113    Storage,
114}
115
116impl<Runtime: BaseRuntime> DatabaseRuntime<Runtime> {
117    /// Encodes the `index` of the EVM storage associated to the smart contract
118    /// in a Linera key.
119    fn get_linera_key(key_prefix: &[u8], index: U256) -> Vec<u8> {
120        let mut key = key_prefix.to_vec();
121        key.extend(index.as_le_slice());
122        key
123    }
124
125    /// Returns the tag associated to the contract.
126    fn get_address_key(prefix: u8, address: Address) -> Vec<u8> {
127        let mut key = vec![prefix];
128        key.extend(address);
129        key
130    }
131
132    /// Creates a new `DatabaseRuntime`.
133    pub fn new(runtime: Runtime) -> Self {
134        let storage_stats = StorageStats::default();
135        // We cannot acquire a lock on runtime here.
136        // So, we set the contract_address to a default value
137        // and update it later.
138        Self {
139            storage_stats: Arc::new(Mutex::new(storage_stats)),
140            contract_address: Address::ZERO,
141            caller: Address::ZERO,
142            value: U256::ZERO,
143            runtime: Arc::new(Mutex::new(runtime)),
144            changes: HashMap::new(),
145            is_revm_instantiated: false,
146            error: Arc::new(Mutex::new(None)),
147        }
148    }
149
150    /// Returns the current storage states and clears it to default.
151    pub fn take_storage_stats(&self) -> StorageStats {
152        let mut storage_stats_read = self.storage_stats.lock().unwrap();
153        let storage_stats = storage_stats_read.clone();
154        *storage_stats_read = StorageStats::default();
155        storage_stats
156    }
157
158    /// Insert error into the database
159    pub fn insert_error(&self, exec_error: ExecutionError) {
160        let mut error = self.error.lock().unwrap();
161        *error = Some(format!("Runtime error {:?}", exec_error));
162    }
163
164    /// Process the error.
165    pub fn process_any_error(&self) -> Result<(), EvmExecutionError> {
166        let error = self.error.lock().unwrap();
167        if let Some(error) = error.clone() {
168            return Err(EvmExecutionError::RuntimeError(error.clone()));
169        }
170        Ok(())
171    }
172}
173
174impl DBErrorMarker for ExecutionError {}
175
176impl<Runtime> Database for DatabaseRuntime<Runtime>
177where
178    Runtime: BaseRuntime,
179{
180    type Error = ExecutionError;
181
182    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, ExecutionError> {
183        self.basic_ref(address)
184    }
185
186    fn code_by_hash(&mut self, _code_hash: B256) -> Result<Bytecode, ExecutionError> {
187        panic!("Functionality code_by_hash not implemented");
188    }
189
190    fn storage(&mut self, address: Address, index: U256) -> Result<U256, ExecutionError> {
191        self.storage_ref(address, index)
192    }
193
194    fn block_hash(&mut self, number: u64) -> Result<B256, ExecutionError> {
195        <Self as DatabaseRef>::block_hash_ref(self, number)
196    }
197}
198
199impl<Runtime> DatabaseCommit for DatabaseRuntime<Runtime>
200where
201    Runtime: BaseRuntime,
202{
203    fn commit(&mut self, changes: EvmState) {
204        self.changes = changes;
205    }
206}
207
208impl<Runtime> DatabaseRef for DatabaseRuntime<Runtime>
209where
210    Runtime: BaseRuntime,
211{
212    type Error = ExecutionError;
213
214    /// The `basic_ref` is the function for reading the state of the application.
215    fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, ExecutionError> {
216        if address == FAUCET_ADDRESS {
217            return Ok(Some(AccountInfo {
218                balance: FAUCET_BALANCE,
219                ..AccountInfo::default()
220            }));
221        }
222        if !self.changes.is_empty() {
223            // This case occurs in only one scenario:
224            // * A service call to a contract that has not yet been
225            //   initialized by a contract call.
226            // When we do a service calls to a contract that has
227            // already been initialized, then changes will be empty.
228            let account = self.changes.get(&address);
229            return Ok(account.map(|account| account.info.clone()));
230        }
231        let mut runtime = self.runtime.lock().unwrap();
232        let account_owner = address.into();
233        // The balances being used are the ones of Linera. So, we need to
234        // access them at first.
235        let balance = runtime.read_owner_balance(account_owner)?;
236
237        let balance: U256 = balance.into();
238        let key_info = Self::get_address_key(KeyCategory::AccountInfo as u8, address);
239        let promise = runtime.read_value_bytes_new(key_info)?;
240        let result = runtime.read_value_bytes_wait(&promise)?;
241        let mut account_info = match result {
242            None => AccountInfo::default(),
243            Some(bytes) => bcs::from_bytes(&bytes)?,
244        };
245        // The design is the following:
246        // * The funds have been deposited in deposit_funds.
247        // * The order of the operations is the following:
248        //   + Access to the storage (this functions) of relevant accounts.
249        //   + Transfer according to the input.
250        //   + Running the constructor.
251        // * So, the transfer is done twice: One at deposit_funds.
252        //   Another in the transfer by REVM.
253        // * So, we need to correct the balances so that when Revm
254        //   is doing the transfer, the balance are the ones after
255        //   deposit_funds.
256        let start_balance = if self.caller == address {
257            balance + self.value
258        } else if self.contract_address == address {
259            assert!(
260                balance >= self.value,
261                "We should have balance >= self.value"
262            );
263            balance - self.value
264        } else {
265            balance
266        };
267        account_info.balance = start_balance;
268        // We return an account as there is no difference between
269        // a default account and the absence of account.
270        Ok(Some(account_info))
271    }
272
273    fn code_by_hash_ref(&self, _code_hash: B256) -> Result<Bytecode, ExecutionError> {
274        panic!("Functionality code_by_hash_ref not implemented");
275    }
276
277    fn storage_ref(&self, address: Address, index: U256) -> Result<U256, ExecutionError> {
278        if !self.changes.is_empty() {
279            let account = self.changes.get(&address).unwrap();
280            return Ok(match account.storage.get(&index) {
281                None => U256::ZERO,
282                Some(slot) => slot.present_value(),
283            });
284        }
285        let key_prefix = Self::get_address_key(KeyCategory::Storage as u8, address);
286        let key = Self::get_linera_key(&key_prefix, index);
287        {
288            let mut storage_stats = self.storage_stats.lock().unwrap();
289            storage_stats.key_read += 1;
290        }
291        let result = {
292            let mut runtime = self.runtime.lock().unwrap();
293            let promise = runtime.read_value_bytes_new(key)?;
294            runtime.read_value_bytes_wait(&promise)
295        }?;
296        Ok(from_bytes_option::<U256>(&result)?.unwrap_or_default())
297    }
298
299    fn block_hash_ref(&self, number: u64) -> Result<B256, ExecutionError> {
300        Ok(keccak256(number.to_string().as_bytes()))
301    }
302}
303
304impl<Runtime> DatabaseRuntime<Runtime>
305where
306    Runtime: ContractRuntime,
307{
308    /// Effectively commits changes to storage.
309    pub fn commit_changes(&mut self) -> Result<(), ExecutionError> {
310        let mut storage_stats = self.storage_stats.lock().unwrap();
311        let mut runtime = self.runtime.lock().unwrap();
312        let mut batch = Batch::new();
313        for (address, account) in &self.changes {
314            if address == &FAUCET_ADDRESS {
315                // We do not write the faucet address nor expect any coherency from it.
316                continue;
317            }
318            let owner = (*address).into();
319            let linera_balance: U256 = runtime.read_owner_balance(owner)?.into();
320            let revm_balance = account.info.balance;
321            ensure!(
322                linera_balance == revm_balance,
323                EvmExecutionError::IncoherentBalances(*address, linera_balance, revm_balance)
324            );
325            if !account.is_touched() {
326                continue;
327            }
328            let key_prefix = Self::get_address_key(KeyCategory::Storage as u8, *address);
329            let key_info = Self::get_address_key(KeyCategory::AccountInfo as u8, *address);
330            let key_state = Self::get_address_key(KeyCategory::AccountState as u8, *address);
331            if account.is_selfdestructed() {
332                batch.delete_key_prefix(key_prefix);
333                batch.put_key_value(key_info, &AccountInfo::default())?;
334                batch.put_key_value(key_state, &AccountState::NotExisting)?;
335            } else {
336                let is_newly_created = account.is_created();
337                // We write here the state of the user in question. But that does not matter
338                batch.put_key_value(key_info, &account.info)?;
339                let account_state = if is_newly_created {
340                    batch.delete_key_prefix(key_prefix.clone());
341                    AccountState::StorageCleared
342                } else {
343                    let promise = runtime.read_value_bytes_new(key_state.clone())?;
344                    let result = runtime.read_value_bytes_wait(&promise)?;
345                    let account_state =
346                        from_bytes_option::<AccountState>(&result)?.unwrap_or_default();
347                    if account_state.is_storage_cleared() {
348                        AccountState::StorageCleared
349                    } else {
350                        AccountState::Touched
351                    }
352                };
353                batch.put_key_value(key_state, &account_state)?;
354                for (index, value) in &account.storage {
355                    if value.present_value() == value.original_value() {
356                        storage_stats.key_no_operation += 1;
357                    } else {
358                        let key = Self::get_linera_key(&key_prefix, *index);
359                        if value.original_value() == U256::ZERO {
360                            batch.put_key_value(key, &value.present_value())?;
361                            storage_stats.key_set += 1;
362                        } else if value.present_value() == U256::ZERO {
363                            batch.delete_key(key);
364                            storage_stats.key_release += 1;
365                        } else {
366                            batch.put_key_value(key, &value.present_value())?;
367                            storage_stats.key_reset += 1;
368                        }
369                    }
370                }
371            }
372        }
373        runtime.write_batch(batch)?;
374        self.changes.clear();
375        Ok(())
376    }
377}
378
379impl<Runtime> DatabaseRuntime<Runtime>
380where
381    Runtime: BaseRuntime,
382{
383    /// Reads the nonce of the user
384    pub fn get_nonce(&self, address: &Address) -> Result<u64, ExecutionError> {
385        let account_info: Option<AccountInfo> = self.basic_ref(*address)?;
386        Ok(match account_info {
387            None => 0,
388            Some(account_info) => account_info.nonce,
389        })
390    }
391
392    pub fn get_deployed_bytecode(&self) -> Result<Vec<u8>, ExecutionError> {
393        let account_info = self.basic_ref(self.contract_address)?;
394        Ok(match account_info {
395            None => Vec::new(),
396            Some(account_info) => {
397                let bytecode = account_info
398                    .code
399                    .ok_or(EvmExecutionError::MissingBytecode)?;
400                bytecode.bytes_ref().to_vec()
401            }
402        })
403    }
404
405    /// Sets the EVM contract address from the value Address::ZERO.
406    /// The value is set from the `ApplicationId`.
407    pub fn set_contract_address(&mut self) -> Result<(), ExecutionError> {
408        let mut runtime = self.runtime.lock().unwrap();
409        let application_id = runtime.application_id()?;
410        self.contract_address = application_id.evm_address();
411        Ok(())
412    }
413
414    /// A contract is called initialized if the execution of the constructor
415    /// with the constructor argument yield the storage and the deployed
416    /// bytecode. The deployed bytecode is stored in the storage of the
417    /// bytecode address.
418    /// We determine whether the contract is already initialized, sets the
419    /// `is_revm_initialized` and then returns the result.
420    pub fn set_is_initialized(&mut self) -> Result<bool, ExecutionError> {
421        let mut runtime = self.runtime.lock().unwrap();
422        let evm_address = runtime.application_id()?.evm_address();
423        let key_info = Self::get_address_key(KeyCategory::AccountInfo as u8, evm_address);
424        let promise = runtime.contains_key_new(key_info)?;
425        let result = runtime.contains_key_wait(&promise)?;
426        self.is_revm_instantiated = result;
427        Ok(result)
428    }
429
430    pub fn get_block_env(&self) -> Result<BlockEnv, ExecutionError> {
431        let mut runtime = self.runtime.lock().unwrap();
432        // The block height being used
433        let block_height_linera = runtime.block_height()?;
434        let block_height_evm = block_height_linera.0;
435        // This is the receiver address of all the gas spent in the block.
436        let beneficiary = address!("00000000000000000000000000000000000000bb");
437        // The difficulty which is no longer relevant after The Merge.
438        let difficulty = U256::ZERO;
439        // We do not have access to the Resources so we keep it to the maximum
440        // and the control is done elsewhere.
441        let gas_limit = u64::MAX;
442        // The timestamp. Both the EVM and Linera use the same UNIX epoch.
443        // But the Linera epoch is in microseconds since the start and the
444        // Ethereum epoch is in seconds
445        let timestamp_linera = runtime.read_system_timestamp()?;
446        let timestamp_evm = timestamp_linera.micros() / 1_000_000;
447        // The base fee is the minimum fee for executing a transaction.
448        // We have no such concept in Linera.
449        let basefee = 0;
450        let chain_id = runtime.chain_id()?;
451        let entry = format!("{}{}", chain_id, block_height_linera);
452        // The randomness beacon being used.
453        let prevrandao = keccak256(entry.as_bytes());
454        // The blob excess gas and price is not relevant to the execution
455        // on Linera. We set up a default value as in REVM.
456        let entry = BlobExcessGasAndPrice {
457            excess_blob_gas: 0,
458            blob_gasprice: 1,
459        };
460        let blob_excess_gas_and_price = Some(entry);
461        Ok(BlockEnv {
462            number: block_height_evm,
463            beneficiary,
464            difficulty,
465            gas_limit,
466            timestamp: timestamp_evm,
467            basefee,
468            prevrandao: Some(prevrandao),
469            blob_excess_gas_and_price,
470        })
471    }
472
473    pub fn constructor_argument(&self) -> Result<Vec<u8>, ExecutionError> {
474        let mut runtime = self.runtime.lock().unwrap();
475        let constructor_argument = runtime.application_parameters()?;
476        Ok(serde_json::from_slice::<Vec<u8>>(&constructor_argument)?)
477    }
478}
479
480impl<Runtime> DatabaseRuntime<Runtime>
481where
482    Runtime: ContractRuntime,
483{
484    pub fn get_contract_block_env(&self) -> Result<BlockEnv, ExecutionError> {
485        let mut block_env = self.get_block_env()?;
486        let mut runtime = self.runtime.lock().unwrap();
487        // We use the gas_limit from the runtime
488        let gas_limit = runtime.maximum_fuel_per_block(VmRuntime::Evm)?;
489        block_env.gas_limit = gas_limit;
490        Ok(block_env)
491    }
492
493    pub fn deposit_funds(&self) -> Result<(), ExecutionError> {
494        if self.value != U256::ZERO {
495            if self.caller == ZERO_ADDRESS {
496                let error = EvmExecutionError::UnknownSigner;
497                return Err(error.into());
498            }
499            let source = self.caller.into();
500            let amount = Amount::try_from(self.value).map_err(EvmExecutionError::from)?;
501            let mut runtime = self.runtime.lock().expect("The lock should be possible");
502            let chain_id = runtime.chain_id()?;
503            let application_id = runtime.application_id()?;
504            let owner = application_id.into();
505            let destination = Account { chain_id, owner };
506            let authenticated_caller = runtime.authenticated_caller_id()?;
507            if authenticated_caller.is_none() {
508                runtime.transfer(source, destination, amount)?;
509            }
510        }
511        Ok(())
512    }
513}
514
515impl<Runtime> DatabaseRuntime<Runtime>
516where
517    Runtime: ServiceRuntime,
518{
519    pub fn get_service_block_env(&self) -> Result<BlockEnv, ExecutionError> {
520        let mut block_env = self.get_block_env()?;
521        block_env.gas_limit = EVM_SERVICE_GAS_LIMIT;
522        Ok(block_env)
523    }
524}