1use 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
23pub const EVM_SERVICE_GAS_LIMIT: u64 = 20_000_000;
28
29const SLOAD_COST: u64 = 2100;
31
32const SSTORE_COST_SET: u64 = 20000;
34
35const SSTORE_COST_NO_OPERATION: u64 = 100;
37
38const SSTORE_COST_RESET: u64 = 2900;
40
41const SSTORE_REFUND_RELEASE: u64 = 4800;
43
44#[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
70pub(crate) struct DatabaseRuntime<Runtime> {
72 storage_stats: Arc<Mutex<StorageStats>>,
74 pub contract_address: Address,
77 pub runtime: Arc<Mutex<Runtime>>,
79 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 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 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 pub fn new(runtime: Runtime) -> Self {
125 let storage_stats = StorageStats::default();
126 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 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 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 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 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 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 let block_height_linera = runtime.block_height()?;
339 let block_height_evm = block_height_linera.0;
340 let beneficiary = address!("00000000000000000000000000000000000000bb");
342 let difficulty = U256::ZERO;
344 let gas_limit = u64::MAX;
347 let timestamp_linera = runtime.read_system_timestamp()?;
351 let timestamp_evm = timestamp_linera.micros() / 1_000_000;
352 let basefee = 0;
355 let chain_id = runtime.chain_id()?;
356 let entry = format!("{}{}", chain_id, block_height_linera);
357 let prevrandao = keccak256(entry.as_bytes());
359 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 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}