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::{ApplicationId, BaseRuntime, Batch, ContractRuntime, ExecutionError, ServiceRuntime};
22
23// The runtime costs are not available in service operations.
24// We need to set a limit to gas usage in order to avoid blocking
25// the validator.
26// We set up the limit similarly to Infura to 20 million.
27pub const EVM_SERVICE_GAS_LIMIT: u64 = 20_000_000;
28
29/// The cost of loading from storage.
30const SLOAD_COST: u64 = 2100;
31
32/// The cost of storing a non-zero value in the storage for the first time.
33const SSTORE_COST_SET: u64 = 20000;
34
35/// The cost of not changing the state of the variable in the storage.
36const SSTORE_COST_NO_OPERATION: u64 = 100;
37
38/// The cost of overwriting the storage to a different value.
39const SSTORE_COST_RESET: u64 = 2900;
40
41/// The refund from releasing data.
42const SSTORE_REFUND_RELEASE: u64 = 4800;
43
44/// The number of key writes, reads, release, and no change in EVM has to be accounted for.
45/// Then we remove those costs from the final bill.
46#[derive(Clone, Default)]
47pub(crate) struct StorageStats {
48    key_no_operation: u64,
49    key_reset: u64,
50    key_set: u64,
51    key_release: u64,
52    key_read: u64,
53}
54
55impl StorageStats {
56    pub fn storage_costs(&self) -> u64 {
57        let mut storage_costs = 0;
58        storage_costs += self.key_no_operation * SSTORE_COST_NO_OPERATION;
59        storage_costs += self.key_reset * SSTORE_COST_RESET;
60        storage_costs += self.key_set * SSTORE_COST_SET;
61        storage_costs += self.key_read * SLOAD_COST;
62        storage_costs
63    }
64
65    pub fn storage_refund(&self) -> u64 {
66        self.key_release * SSTORE_REFUND_RELEASE
67    }
68}
69
70/// This is the encapsulation of the `Runtime` corresponding to the contract.
71pub(crate) struct DatabaseRuntime<Runtime> {
72    /// This is the storage statistics of the read/write in order to adjust gas costs.
73    storage_stats: Arc<Mutex<StorageStats>>,
74    /// This is the EVM address of the contract.
75    /// At the creation, it is set to `Address::ZERO` and then later set to the correct value.
76    pub contract_address: Address,
77    /// The runtime of the contract.
78    pub runtime: Arc<Mutex<Runtime>>,
79    /// The uncommitted changes to the contract.
80    pub changes: EvmState,
81}
82
83impl<Runtime> Clone for DatabaseRuntime<Runtime> {
84    fn clone(&self) -> Self {
85        Self {
86            storage_stats: self.storage_stats.clone(),
87            contract_address: self.contract_address,
88            runtime: self.runtime.clone(),
89            changes: self.changes.clone(),
90        }
91    }
92}
93
94#[repr(u8)]
95pub enum KeyCategory {
96    AccountInfo,
97    AccountState,
98    Storage,
99}
100
101fn application_id_to_address(application_id: ApplicationId) -> Address {
102    let application_id: [u64; 4] = <[u64; 4]>::from(application_id.application_description_hash);
103    let application_id: [u8; 32] = linera_base::crypto::u64_array_to_be_bytes(application_id);
104    Address::from_slice(&application_id[0..20])
105}
106
107impl<Runtime: BaseRuntime> DatabaseRuntime<Runtime> {
108    /// Encode the `index` of the EVM storage associated to the smart contract
109    /// in a linera key.
110    fn get_linera_key(key_prefix: &[u8], index: U256) -> Result<Vec<u8>, ExecutionError> {
111        let mut key = key_prefix.to_vec();
112        bcs::serialize_into(&mut key, &index)?;
113        Ok(key)
114    }
115
116    /// Returns the tag associated to the contract.
117    fn get_address_key(&self, prefix: u8, address: Address) -> Vec<u8> {
118        let mut key = vec![prefix];
119        key.extend(address);
120        key
121    }
122
123    /// Creates a new `DatabaseRuntime`.
124    pub fn new(runtime: Runtime) -> Self {
125        let storage_stats = StorageStats::default();
126        // We cannot acquire a lock on runtime here.
127        // So, we set the contract_address to a default value
128        // and update it later.
129        Self {
130            storage_stats: Arc::new(Mutex::new(storage_stats)),
131            contract_address: Address::ZERO,
132            runtime: Arc::new(Mutex::new(runtime)),
133            changes: HashMap::new(),
134        }
135    }
136
137    /// Returns the current storage states and clears it to default.
138    pub fn take_storage_stats(&self) -> StorageStats {
139        let mut storage_stats_read = self
140            .storage_stats
141            .lock()
142            .expect("The lock should be possible");
143        let storage_stats = storage_stats_read.clone();
144        *storage_stats_read = StorageStats::default();
145        storage_stats
146    }
147}
148
149impl DBErrorMarker for ExecutionError {}
150
151impl<Runtime> Database for DatabaseRuntime<Runtime>
152where
153    Runtime: BaseRuntime,
154{
155    type Error = ExecutionError;
156
157    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, ExecutionError> {
158        self.basic_ref(address)
159    }
160
161    fn code_by_hash(&mut self, _code_hash: B256) -> Result<Bytecode, ExecutionError> {
162        panic!("Functionality code_by_hash not implemented");
163    }
164
165    fn storage(&mut self, address: Address, index: U256) -> Result<U256, ExecutionError> {
166        self.storage_ref(address, index)
167    }
168
169    fn block_hash(&mut self, number: u64) -> Result<B256, ExecutionError> {
170        <Self as DatabaseRef>::block_hash_ref(self, number)
171    }
172}
173
174impl<Runtime> DatabaseCommit for DatabaseRuntime<Runtime>
175where
176    Runtime: BaseRuntime,
177{
178    fn commit(&mut self, changes: EvmState) {
179        self.changes = changes;
180    }
181}
182
183impl<Runtime> DatabaseRef for DatabaseRuntime<Runtime>
184where
185    Runtime: BaseRuntime,
186{
187    type Error = ExecutionError;
188
189    fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, ExecutionError> {
190        if !self.changes.is_empty() {
191            let account = self.changes.get(&address).unwrap();
192            return Ok(Some(account.info.clone()));
193        }
194        let mut runtime = self.runtime.lock().expect("The lock should be possible");
195        let key_info = self.get_address_key(KeyCategory::AccountInfo as u8, address);
196        let promise = runtime.read_value_bytes_new(key_info)?;
197        let result = runtime.read_value_bytes_wait(&promise)?;
198        let account_info = from_bytes_option::<AccountInfo>(&result)?;
199        Ok(account_info)
200    }
201
202    fn code_by_hash_ref(&self, _code_hash: B256) -> Result<Bytecode, ExecutionError> {
203        panic!("Functionality code_by_hash_ref not implemented");
204    }
205
206    fn storage_ref(&self, address: Address, index: U256) -> Result<U256, ExecutionError> {
207        if !self.changes.is_empty() {
208            let account = self.changes.get(&address).unwrap();
209            return Ok(match account.storage.get(&index) {
210                None => U256::ZERO,
211                Some(slot) => slot.present_value(),
212            });
213        }
214        let key_prefix = self.get_address_key(KeyCategory::Storage as u8, address);
215        let key = Self::get_linera_key(&key_prefix, index)?;
216        {
217            let mut storage_stats = self
218                .storage_stats
219                .lock()
220                .expect("The lock should be possible");
221            storage_stats.key_read += 1;
222        }
223        let result = {
224            let mut runtime = self.runtime.lock().expect("The lock should be possible");
225            let promise = runtime.read_value_bytes_new(key)?;
226            runtime.read_value_bytes_wait(&promise)
227        }?;
228        Ok(from_bytes_option::<U256>(&result)?.unwrap_or_default())
229    }
230
231    fn block_hash_ref(&self, number: u64) -> Result<B256, ExecutionError> {
232        Ok(keccak256(number.to_string().as_bytes()))
233    }
234}
235
236impl<Runtime> DatabaseRuntime<Runtime>
237where
238    Runtime: ContractRuntime,
239{
240    /// Effectively commits changes to storage.
241    pub fn commit_changes(&mut self) -> Result<(), ExecutionError> {
242        let mut storage_stats = self
243            .storage_stats
244            .lock()
245            .expect("The lock should be possible");
246        let mut runtime = self.runtime.lock().expect("The lock should be possible");
247        let mut batch = Batch::new();
248        for (address, account) in &self.changes {
249            if !account.is_touched() {
250                continue;
251            }
252            let key_prefix = self.get_address_key(KeyCategory::Storage as u8, *address);
253            let key_info = self.get_address_key(KeyCategory::AccountInfo as u8, *address);
254            let key_state = self.get_address_key(KeyCategory::AccountState as u8, *address);
255            if account.is_selfdestructed() {
256                batch.delete_key_prefix(key_prefix);
257                batch.put_key_value(key_info, &AccountInfo::default())?;
258                batch.put_key_value(key_state, &AccountState::NotExisting)?;
259            } else {
260                let is_newly_created = account.is_created();
261                batch.put_key_value(key_info, &account.info)?;
262                let account_state = if is_newly_created {
263                    batch.delete_key_prefix(key_prefix.clone());
264                    AccountState::StorageCleared
265                } else {
266                    let promise = runtime.read_value_bytes_new(key_state.clone())?;
267                    let result = runtime.read_value_bytes_wait(&promise)?;
268                    let account_state =
269                        from_bytes_option::<AccountState>(&result)?.unwrap_or_default();
270                    if account_state.is_storage_cleared() {
271                        AccountState::StorageCleared
272                    } else {
273                        AccountState::Touched
274                    }
275                };
276                batch.put_key_value(key_state, &account_state)?;
277                for (index, value) in &account.storage {
278                    if value.present_value() == value.original_value() {
279                        storage_stats.key_no_operation += 1;
280                    } else {
281                        let key = Self::get_linera_key(&key_prefix, *index)?;
282                        if value.original_value() == U256::ZERO {
283                            batch.put_key_value(key, &value.present_value())?;
284                            storage_stats.key_set += 1;
285                        } else if value.present_value() == U256::ZERO {
286                            batch.delete_key(key);
287                            storage_stats.key_release += 1;
288                        } else {
289                            batch.put_key_value(key, &value.present_value())?;
290                            storage_stats.key_reset += 1;
291                        }
292                    }
293                }
294            }
295        }
296        runtime.write_batch(batch)?;
297        self.changes.clear();
298        Ok(())
299    }
300}
301
302impl<Runtime> DatabaseRuntime<Runtime>
303where
304    Runtime: BaseRuntime,
305{
306    /// Reads the nonce of the user
307    pub fn get_nonce(&self, address: &Address) -> Result<u64, ExecutionError> {
308        let account_info = self.basic_ref(*address)?;
309        Ok(match account_info {
310            None => 0,
311            Some(account_info) => account_info.nonce,
312        })
313    }
314
315    /// Sets the EVM contract address from the value Address::ZERO.
316    /// The value is set from the `ApplicationId`.
317    pub fn set_contract_address(&mut self) -> Result<(), ExecutionError> {
318        let mut runtime = self.runtime.lock().expect("The lock should be possible");
319        let application_id = runtime.application_id()?;
320        self.contract_address = application_id_to_address(application_id);
321        Ok(())
322    }
323
324    /// Checks if the contract is already initialized. It is possible
325    /// that the constructor has not yet been called.
326    pub fn is_initialized(&self) -> Result<bool, ExecutionError> {
327        let mut runtime = self.runtime.lock().expect("The lock should be possible");
328        let evm_address = runtime.application_id()?.evm_address();
329        let key_info = self.get_address_key(KeyCategory::AccountInfo as u8, evm_address);
330        let promise = runtime.contains_key_new(key_info)?;
331        let result = runtime.contains_key_wait(&promise)?;
332        Ok(result)
333    }
334
335    pub fn get_block_env(&self) -> Result<BlockEnv, ExecutionError> {
336        let mut runtime = self.runtime.lock().expect("The lock should be possible");
337        // The block height being used
338        let block_height_linera = runtime.block_height()?;
339        let block_height_evm = block_height_linera.0;
340        // This is the receiver address of all the gas spent in the block.
341        let beneficiary = address!("00000000000000000000000000000000000000bb");
342        // The difficulty which is no longer relevant after The Merge.
343        let difficulty = U256::ZERO;
344        // We do not have access to the Resources so we keep it to the maximum
345        // and the control is done elsewhere.
346        let gas_limit = u64::MAX;
347        // The timestamp. Both the EVM and Linera use the same UNIX epoch.
348        // But the Linera epoch is in microseconds since the start and the
349        // Ethereum epoch is in seconds
350        let timestamp_linera = runtime.read_system_timestamp()?;
351        let timestamp_evm = timestamp_linera.micros() / 1_000_000;
352        // The basefee is the minimum feee for executing. We have no such
353        // concept in Linera
354        let basefee = 0;
355        let chain_id = runtime.chain_id()?;
356        let entry = format!("{}{}", chain_id, block_height_linera);
357        // The randomness beacon being used.
358        let prevrandao = keccak256(entry.as_bytes());
359        // The blob excess gas and price is not relevant to the execution
360        // on Linera. We set up a default value as in REVM.
361        let entry = BlobExcessGasAndPrice {
362            excess_blob_gas: 0,
363            blob_gasprice: 1,
364        };
365        let blob_excess_gas_and_price = Some(entry);
366        Ok(BlockEnv {
367            number: block_height_evm,
368            beneficiary,
369            difficulty,
370            gas_limit,
371            timestamp: timestamp_evm,
372            basefee,
373            prevrandao: Some(prevrandao),
374            blob_excess_gas_and_price,
375        })
376    }
377
378    pub fn constructor_argument(&self) -> Result<Vec<u8>, ExecutionError> {
379        let mut runtime = self.runtime.lock().expect("The lock should be possible");
380        let constructor_argument = runtime.application_parameters()?;
381        Ok(serde_json::from_slice::<Vec<u8>>(&constructor_argument)?)
382    }
383}
384
385impl<Runtime> DatabaseRuntime<Runtime>
386where
387    Runtime: ContractRuntime,
388{
389    pub fn get_contract_block_env(&self) -> Result<BlockEnv, ExecutionError> {
390        let mut block_env = self.get_block_env()?;
391        let mut runtime = self.runtime.lock().expect("The lock should be possible");
392        // We use the gas_limit from the runtime
393        let gas_limit = runtime.maximum_fuel_per_block(VmRuntime::Evm)?;
394        block_env.gas_limit = gas_limit;
395        Ok(block_env)
396    }
397}
398
399impl<Runtime> DatabaseRuntime<Runtime>
400where
401    Runtime: ServiceRuntime,
402{
403    pub fn get_service_block_env(&self) -> Result<BlockEnv, ExecutionError> {
404        let mut block_env = self.get_block_env()?;
405        block_env.gas_limit = EVM_SERVICE_GAS_LIMIT;
406        Ok(block_env)
407    }
408}