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::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    ApplicationId, BaseRuntime, Batch, ContractRuntime, EvmExecutionError, ExecutionError,
23    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 runtime of the contract.
81    pub runtime: Arc<Mutex<Runtime>>,
82    /// The uncommitted changes to the contract.
83    pub changes: EvmState,
84    /// Whether the contract has been instantiated in REVM.
85    pub is_revm_instantiated: bool,
86    /// The error that can occur during runtime.
87    pub error: Arc<Mutex<Option<String>>>,
88}
89
90impl<Runtime> Clone for DatabaseRuntime<Runtime> {
91    fn clone(&self) -> Self {
92        Self {
93            storage_stats: self.storage_stats.clone(),
94            contract_address: self.contract_address,
95            runtime: self.runtime.clone(),
96            changes: self.changes.clone(),
97            is_revm_instantiated: self.is_revm_instantiated,
98            error: self.error.clone(),
99        }
100    }
101}
102
103#[repr(u8)]
104pub enum KeyCategory {
105    AccountInfo,
106    AccountState,
107    Storage,
108}
109
110fn application_id_to_address(application_id: ApplicationId) -> Address {
111    let application_id: [u64; 4] = <[u64; 4]>::from(application_id.application_description_hash);
112    let application_id: [u8; 32] = linera_base::crypto::u64_array_to_be_bytes(application_id);
113    Address::from_slice(&application_id[0..20])
114}
115
116impl<Runtime: BaseRuntime> DatabaseRuntime<Runtime> {
117    /// Encode 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) -> Result<Vec<u8>, ExecutionError> {
120        let mut key = key_prefix.to_vec();
121        bcs::serialize_into(&mut key, &index)?;
122        Ok(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            runtime: Arc::new(Mutex::new(runtime)),
142            changes: HashMap::new(),
143            is_revm_instantiated: false,
144            error: Arc::new(Mutex::new(None)),
145        }
146    }
147
148    /// Returns the current storage states and clears it to default.
149    pub fn take_storage_stats(&self) -> StorageStats {
150        let mut storage_stats_read = self.storage_stats.lock().unwrap();
151        let storage_stats = storage_stats_read.clone();
152        *storage_stats_read = StorageStats::default();
153        storage_stats
154    }
155
156    /// Insert error into the database
157    pub fn insert_error(&self, exec_error: ExecutionError) {
158        let mut error = self.error.lock().unwrap();
159        *error = Some(format!("Runtime error {:?}", exec_error));
160    }
161
162    /// Process the error.
163    pub fn process_any_error(&self) -> Result<(), EvmExecutionError> {
164        let error = self.error.lock().unwrap();
165        if let Some(error) = error.clone() {
166            return Err(EvmExecutionError::RuntimeError(error.clone()));
167        }
168        Ok(())
169    }
170}
171
172impl DBErrorMarker for ExecutionError {}
173
174impl<Runtime> Database for DatabaseRuntime<Runtime>
175where
176    Runtime: BaseRuntime,
177{
178    type Error = ExecutionError;
179
180    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, ExecutionError> {
181        self.basic_ref(address)
182    }
183
184    fn code_by_hash(&mut self, _code_hash: B256) -> Result<Bytecode, ExecutionError> {
185        panic!("Functionality code_by_hash not implemented");
186    }
187
188    fn storage(&mut self, address: Address, index: U256) -> Result<U256, ExecutionError> {
189        self.storage_ref(address, index)
190    }
191
192    fn block_hash(&mut self, number: u64) -> Result<B256, ExecutionError> {
193        <Self as DatabaseRef>::block_hash_ref(self, number)
194    }
195}
196
197impl<Runtime> DatabaseCommit for DatabaseRuntime<Runtime>
198where
199    Runtime: BaseRuntime,
200{
201    fn commit(&mut self, changes: EvmState) {
202        self.changes = changes;
203    }
204}
205
206impl<Runtime> DatabaseRef for DatabaseRuntime<Runtime>
207where
208    Runtime: BaseRuntime,
209{
210    type Error = ExecutionError;
211
212    fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, ExecutionError> {
213        if !self.changes.is_empty() {
214            let account = self.changes.get(&address).unwrap();
215            return Ok(Some(account.info.clone()));
216        }
217        let mut runtime = self.runtime.lock().unwrap();
218        let key_info = Self::get_address_key(KeyCategory::AccountInfo as u8, address);
219        let promise = runtime.read_value_bytes_new(key_info)?;
220        let result = runtime.read_value_bytes_wait(&promise)?;
221        let account_info = from_bytes_option::<AccountInfo>(&result)?;
222        Ok(account_info)
223    }
224
225    fn code_by_hash_ref(&self, _code_hash: B256) -> Result<Bytecode, ExecutionError> {
226        panic!("Functionality code_by_hash_ref not implemented");
227    }
228
229    fn storage_ref(&self, address: Address, index: U256) -> Result<U256, ExecutionError> {
230        if !self.changes.is_empty() {
231            let account = self.changes.get(&address).unwrap();
232            return Ok(match account.storage.get(&index) {
233                None => U256::ZERO,
234                Some(slot) => slot.present_value(),
235            });
236        }
237        let key_prefix = Self::get_address_key(KeyCategory::Storage as u8, address);
238        let key = Self::get_linera_key(&key_prefix, index)?;
239        {
240            let mut storage_stats = self.storage_stats.lock().unwrap();
241            storage_stats.key_read += 1;
242        }
243        let result = {
244            let mut runtime = self.runtime.lock().unwrap();
245            let promise = runtime.read_value_bytes_new(key)?;
246            runtime.read_value_bytes_wait(&promise)
247        }?;
248        Ok(from_bytes_option::<U256>(&result)?.unwrap_or_default())
249    }
250
251    fn block_hash_ref(&self, number: u64) -> Result<B256, ExecutionError> {
252        Ok(keccak256(number.to_string().as_bytes()))
253    }
254}
255
256impl<Runtime> DatabaseRuntime<Runtime>
257where
258    Runtime: ContractRuntime,
259{
260    /// Effectively commits changes to storage.
261    pub fn commit_changes(&mut self) -> Result<(), ExecutionError> {
262        let mut storage_stats = self.storage_stats.lock().unwrap();
263        let mut runtime = self.runtime.lock().unwrap();
264        let mut batch = Batch::new();
265        for (address, account) in &self.changes {
266            if !account.is_touched() {
267                continue;
268            }
269            let key_prefix = Self::get_address_key(KeyCategory::Storage as u8, *address);
270            let key_info = Self::get_address_key(KeyCategory::AccountInfo as u8, *address);
271            let key_state = Self::get_address_key(KeyCategory::AccountState as u8, *address);
272            if account.is_selfdestructed() {
273                batch.delete_key_prefix(key_prefix);
274                batch.put_key_value(key_info, &AccountInfo::default())?;
275                batch.put_key_value(key_state, &AccountState::NotExisting)?;
276            } else {
277                let is_newly_created = account.is_created();
278                batch.put_key_value(key_info, &account.info)?;
279                let account_state = if is_newly_created {
280                    batch.delete_key_prefix(key_prefix.clone());
281                    AccountState::StorageCleared
282                } else {
283                    let promise = runtime.read_value_bytes_new(key_state.clone())?;
284                    let result = runtime.read_value_bytes_wait(&promise)?;
285                    let account_state =
286                        from_bytes_option::<AccountState>(&result)?.unwrap_or_default();
287                    if account_state.is_storage_cleared() {
288                        AccountState::StorageCleared
289                    } else {
290                        AccountState::Touched
291                    }
292                };
293                batch.put_key_value(key_state, &account_state)?;
294                for (index, value) in &account.storage {
295                    if value.present_value() == value.original_value() {
296                        storage_stats.key_no_operation += 1;
297                    } else {
298                        let key = Self::get_linera_key(&key_prefix, *index)?;
299                        if value.original_value() == U256::ZERO {
300                            batch.put_key_value(key, &value.present_value())?;
301                            storage_stats.key_set += 1;
302                        } else if value.present_value() == U256::ZERO {
303                            batch.delete_key(key);
304                            storage_stats.key_release += 1;
305                        } else {
306                            batch.put_key_value(key, &value.present_value())?;
307                            storage_stats.key_reset += 1;
308                        }
309                    }
310                }
311            }
312        }
313        runtime.write_batch(batch)?;
314        self.changes.clear();
315        Ok(())
316    }
317}
318
319impl<Runtime> DatabaseRuntime<Runtime>
320where
321    Runtime: BaseRuntime,
322{
323    /// Reads the nonce of the user
324    pub fn get_nonce(&self, address: &Address) -> Result<u64, ExecutionError> {
325        let account_info: Option<AccountInfo> = self.basic_ref(*address)?;
326        Ok(match account_info {
327            None => 0,
328            Some(account_info) => account_info.nonce,
329        })
330    }
331
332    pub fn get_deployed_bytecode(&self) -> Result<Vec<u8>, ExecutionError> {
333        let account_info = self.basic_ref(self.contract_address)?;
334        Ok(match account_info {
335            None => Vec::new(),
336            Some(account_info) => {
337                let bytecode = account_info
338                    .code
339                    .ok_or(EvmExecutionError::MissingBytecode)?;
340                bytecode.bytes_ref().to_vec()
341            }
342        })
343    }
344
345    /// Sets the EVM contract address from the value Address::ZERO.
346    /// The value is set from the `ApplicationId`.
347    pub fn set_contract_address(&mut self) -> Result<(), ExecutionError> {
348        let mut runtime = self.runtime.lock().unwrap();
349        let application_id = runtime.application_id()?;
350        self.contract_address = application_id_to_address(application_id);
351        Ok(())
352    }
353
354    /// A contract is called initialized if the execution of the constructor
355    /// with the constructor argument yield the storage and the deployed
356    /// bytecode. The deployed bytecode is stored in the storage of the
357    /// bytecode address.
358    /// We determine whether the contract is already initialized, sets the
359    /// `is_revm_initialized` and then returns the result.
360    pub fn set_is_initialized(&mut self) -> Result<bool, ExecutionError> {
361        let mut runtime = self.runtime.lock().unwrap();
362        let evm_address = runtime.application_id()?.evm_address();
363        let key_info = Self::get_address_key(KeyCategory::AccountInfo as u8, evm_address);
364        let promise = runtime.contains_key_new(key_info)?;
365        let result = runtime.contains_key_wait(&promise)?;
366        self.is_revm_instantiated = result;
367        Ok(result)
368    }
369
370    pub fn get_block_env(&self) -> Result<BlockEnv, ExecutionError> {
371        let mut runtime = self.runtime.lock().unwrap();
372        // The block height being used
373        let block_height_linera = runtime.block_height()?;
374        let block_height_evm = block_height_linera.0;
375        // This is the receiver address of all the gas spent in the block.
376        let beneficiary = address!("00000000000000000000000000000000000000bb");
377        // The difficulty which is no longer relevant after The Merge.
378        let difficulty = U256::ZERO;
379        // We do not have access to the Resources so we keep it to the maximum
380        // and the control is done elsewhere.
381        let gas_limit = u64::MAX;
382        // The timestamp. Both the EVM and Linera use the same UNIX epoch.
383        // But the Linera epoch is in microseconds since the start and the
384        // Ethereum epoch is in seconds
385        let timestamp_linera = runtime.read_system_timestamp()?;
386        let timestamp_evm = timestamp_linera.micros() / 1_000_000;
387        // The basefee is the minimum feee for executing. We have no such
388        // concept in Linera
389        let basefee = 0;
390        let chain_id = runtime.chain_id()?;
391        let entry = format!("{}{}", chain_id, block_height_linera);
392        // The randomness beacon being used.
393        let prevrandao = keccak256(entry.as_bytes());
394        // The blob excess gas and price is not relevant to the execution
395        // on Linera. We set up a default value as in REVM.
396        let entry = BlobExcessGasAndPrice {
397            excess_blob_gas: 0,
398            blob_gasprice: 1,
399        };
400        let blob_excess_gas_and_price = Some(entry);
401        Ok(BlockEnv {
402            number: block_height_evm,
403            beneficiary,
404            difficulty,
405            gas_limit,
406            timestamp: timestamp_evm,
407            basefee,
408            prevrandao: Some(prevrandao),
409            blob_excess_gas_and_price,
410        })
411    }
412
413    pub fn constructor_argument(&self) -> Result<Vec<u8>, ExecutionError> {
414        let mut runtime = self.runtime.lock().unwrap();
415        let constructor_argument = runtime.application_parameters()?;
416        Ok(serde_json::from_slice::<Vec<u8>>(&constructor_argument)?)
417    }
418}
419
420impl<Runtime> DatabaseRuntime<Runtime>
421where
422    Runtime: ContractRuntime,
423{
424    pub fn get_contract_block_env(&self) -> Result<BlockEnv, ExecutionError> {
425        let mut block_env = self.get_block_env()?;
426        let mut runtime = self.runtime.lock().unwrap();
427        // We use the gas_limit from the runtime
428        let gas_limit = runtime.maximum_fuel_per_block(VmRuntime::Evm)?;
429        block_env.gas_limit = gas_limit;
430        Ok(block_env)
431    }
432}
433
434impl<Runtime> DatabaseRuntime<Runtime>
435where
436    Runtime: ServiceRuntime,
437{
438    pub fn get_service_block_env(&self) -> Result<BlockEnv, ExecutionError> {
439        let mut block_env = self.get_block_env()?;
440        block_env.gas_limit = EVM_SERVICE_GAS_LIMIT;
441        Ok(block_env)
442    }
443}