tempo_precompiles/storage/
evm.rs

1use alloy::primitives::{Address, Log, LogData, U256};
2use alloy_evm::{EvmInternals, EvmInternalsError};
3use revm::{
4    context::{Block, CfgEnv},
5    primitives::hardfork::SpecId,
6    state::{AccountInfo, Bytecode},
7};
8use tempo_chainspec::hardfork::TempoHardfork;
9
10use crate::{error::TempoPrecompileError, storage::PrecompileStorageProvider};
11
12pub struct EvmPrecompileStorageProvider<'a> {
13    internals: EvmInternals<'a>,
14    chain_id: u64,
15    gas_remaining: u64,
16    gas_refunded: i64,
17    gas_limit: u64,
18    spec: TempoHardfork,
19}
20
21impl<'a> EvmPrecompileStorageProvider<'a> {
22    /// Create a new storage provider with a specific gas limit.
23    pub fn new(
24        internals: EvmInternals<'a>,
25        gas_limit: u64,
26        chain_id: u64,
27        spec: TempoHardfork,
28    ) -> Self {
29        Self {
30            internals,
31            chain_id,
32            gas_remaining: gas_limit,
33            gas_refunded: 0,
34            gas_limit,
35            spec,
36        }
37    }
38
39    /// Create a new storage provider with maximum gas limit.
40    pub fn new_max_gas(internals: EvmInternals<'a>, cfg: &CfgEnv<TempoHardfork>) -> Self {
41        Self::new(internals, u64::MAX, cfg.chain_id, cfg.spec)
42    }
43
44    pub fn ensure_loaded_account(&mut self, account: Address) -> Result<(), EvmInternalsError> {
45        self.internals.load_account(account)?;
46        self.internals.touch_account(account);
47        Ok(())
48    }
49}
50
51impl<'a> PrecompileStorageProvider for EvmPrecompileStorageProvider<'a> {
52    fn chain_id(&self) -> u64 {
53        self.chain_id
54    }
55
56    fn timestamp(&self) -> U256 {
57        self.internals.block_timestamp()
58    }
59
60    fn beneficiary(&self) -> Address {
61        self.internals.block_env().beneficiary()
62    }
63
64    #[inline]
65    fn set_code(&mut self, address: Address, code: Bytecode) -> Result<(), TempoPrecompileError> {
66        self.ensure_loaded_account(address)?;
67        self.deduct_gas(code.len() as u64 * revm::interpreter::gas::CODEDEPOSIT)?;
68
69        self.internals.set_code(address, code);
70
71        Ok(())
72    }
73
74    #[inline]
75    fn with_account_info(
76        &mut self,
77        address: Address,
78        f: &mut dyn FnMut(&AccountInfo),
79    ) -> Result<(), TempoPrecompileError> {
80        self.ensure_loaded_account(address)?;
81        let account = self.internals.load_account_code(address)?.map(|a| &a.info);
82        let is_cold = account.is_cold;
83
84        // deduct gas
85        self.gas_remaining = self
86            .gas_remaining
87            .checked_sub(revm::interpreter::gas::warm_cold_cost(is_cold))
88            .ok_or(TempoPrecompileError::OutOfGas)?;
89
90        f(account.data);
91        Ok(())
92    }
93
94    #[inline]
95    fn sstore(
96        &mut self,
97        address: Address,
98        key: U256,
99        value: U256,
100    ) -> Result<(), TempoPrecompileError> {
101        self.ensure_loaded_account(address)?;
102        let result = self.internals.sstore(address, key, value)?;
103
104        self.deduct_gas(revm::interpreter::gas::sstore_cost(
105            SpecId::AMSTERDAM,
106            &result.data,
107            result.is_cold,
108        ))?;
109
110        // refund gas.
111        self.refund_gas(revm::interpreter::gas::sstore_refund(
112            SpecId::AMSTERDAM,
113            &result.data,
114        ));
115
116        Ok(())
117    }
118
119    #[inline]
120    fn tstore(
121        &mut self,
122        address: Address,
123        key: U256,
124        value: U256,
125    ) -> Result<(), TempoPrecompileError> {
126        self.ensure_loaded_account(address)?;
127        self.deduct_gas(revm::interpreter::gas::WARM_STORAGE_READ_COST)?;
128
129        self.internals.tstore(address, key, value);
130        Ok(())
131    }
132
133    #[inline]
134    fn emit_event(&mut self, address: Address, event: LogData) -> Result<(), TempoPrecompileError> {
135        self.deduct_gas(
136            revm::interpreter::gas::log_cost(event.topics().len() as u8, event.data.len() as u64)
137                .unwrap_or(u64::MAX),
138        )?;
139
140        self.internals.log(Log {
141            address,
142            data: event,
143        });
144
145        Ok(())
146    }
147
148    #[inline]
149    fn sload(&mut self, address: Address, key: U256) -> Result<U256, TempoPrecompileError> {
150        self.ensure_loaded_account(address)?;
151        let val = self.internals.sload(address, key)?;
152
153        self.deduct_gas(revm::interpreter::gas::sload_cost(
154            SpecId::AMSTERDAM,
155            val.is_cold,
156        ))?;
157
158        Ok(val.data)
159    }
160
161    #[inline]
162    fn tload(&mut self, address: Address, key: U256) -> Result<U256, TempoPrecompileError> {
163        self.ensure_loaded_account(address)?;
164        self.deduct_gas(revm::interpreter::gas::WARM_STORAGE_READ_COST)?;
165
166        Ok(self.internals.tload(address, key))
167    }
168
169    #[inline]
170    fn deduct_gas(&mut self, gas: u64) -> Result<(), TempoPrecompileError> {
171        self.gas_remaining = self
172            .gas_remaining
173            .checked_sub(gas)
174            .ok_or(TempoPrecompileError::OutOfGas)?;
175        Ok(())
176    }
177
178    #[inline]
179    fn refund_gas(&mut self, gas: i64) {
180        self.gas_refunded = self.gas_refunded.saturating_add(gas);
181    }
182
183    #[inline]
184    fn gas_used(&self) -> u64 {
185        self.gas_limit - self.gas_remaining
186    }
187
188    #[inline]
189    fn gas_refunded(&self) -> i64 {
190        self.gas_refunded
191    }
192
193    #[inline]
194    fn spec(&self) -> TempoHardfork {
195        self.spec
196    }
197}
198
199impl From<EvmInternalsError> for TempoPrecompileError {
200    fn from(value: EvmInternalsError) -> Self {
201        Self::Fatal(value.to_string())
202    }
203}
204
205#[cfg(test)]
206mod tests {
207    use super::*;
208    use alloy::primitives::{address, b256, bytes};
209    use alloy_evm::{EvmEnv, EvmFactory, EvmInternals, revm::context::Host};
210    use revm::{
211        database::{CacheDB, EmptyDB},
212        interpreter::StateLoad,
213    };
214    use tempo_evm::TempoEvmFactory;
215
216    #[test]
217    fn test_sstore_sload() -> eyre::Result<()> {
218        let db = CacheDB::new(EmptyDB::new());
219        let mut evm = TempoEvmFactory::default().create_evm(db, EvmEnv::default());
220        let ctx = evm.ctx_mut();
221        let evm_internals = EvmInternals::new(&mut ctx.journaled_state, &ctx.block);
222        let mut provider = EvmPrecompileStorageProvider::new_max_gas(evm_internals, &ctx.cfg);
223
224        let addr = Address::random();
225        let key = U256::random();
226
227        let value = U256::random();
228
229        provider.sstore(addr, key, value)?;
230        let sload_val = provider.sload(addr, key)?;
231
232        assert_eq!(sload_val, value);
233        Ok(())
234    }
235
236    #[test]
237    fn test_set_code() -> eyre::Result<()> {
238        let db = CacheDB::new(EmptyDB::new());
239        let mut evm = TempoEvmFactory::default().create_evm(db, EvmEnv::default());
240        let ctx = evm.ctx_mut();
241        let evm_internals = EvmInternals::new(&mut ctx.journaled_state, &ctx.block);
242        let mut provider = EvmPrecompileStorageProvider::new_max_gas(evm_internals, &ctx.cfg);
243
244        let addr = Address::random();
245        let code = Bytecode::new_raw(vec![0xff].into());
246        provider.set_code(addr, code.clone())?;
247        drop(provider);
248
249        let Some(StateLoad { data, is_cold: _ }) = evm.load_account_code(addr) else {
250            panic!("Failed to load account code")
251        };
252
253        assert_eq!(data, *code.original_bytes());
254        Ok(())
255    }
256
257    #[test]
258    fn test_get_account_info() -> eyre::Result<()> {
259        let db = CacheDB::new(EmptyDB::new());
260        let mut evm = TempoEvmFactory::default().create_evm(db, EvmEnv::default());
261        let ctx = evm.ctx_mut();
262        let evm_internals = EvmInternals::new(&mut ctx.journaled_state, &ctx.block);
263        let mut provider = EvmPrecompileStorageProvider::new_max_gas(evm_internals, &ctx.cfg);
264
265        let address = address!("3000000000000000000000000000000000000003");
266
267        // Get account info for a new account
268        provider.with_account_info(address, &mut |info| {
269            // Should be an empty account
270            assert!(info.balance.is_zero());
271            assert_eq!(info.nonce, 0);
272            // Note: load_account_code may return empty bytecode as Some(empty) for new accounts
273            if let Some(ref code) = info.code {
274                assert!(code.is_empty(), "New account should have empty code");
275            }
276        })?;
277
278        Ok(())
279    }
280
281    #[test]
282    fn test_emit_event() -> eyre::Result<()> {
283        let db = CacheDB::new(EmptyDB::new());
284        let mut evm = TempoEvmFactory::default().create_evm(db, EvmEnv::default());
285        let ctx = evm.ctx_mut();
286        let evm_internals = EvmInternals::new(&mut ctx.journaled_state, &ctx.block);
287        let mut provider = EvmPrecompileStorageProvider::new_max_gas(evm_internals, &ctx.cfg);
288
289        let address = address!("4000000000000000000000000000000000000004");
290        let topic = b256!("0000000000000000000000000000000000000000000000000000000000000001");
291        let data = bytes!(
292            "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001"
293        );
294
295        let log_data = LogData::new_unchecked(vec![topic], data);
296
297        // Should not error even though events can't be emitted from handlers
298        provider.emit_event(address, log_data)?;
299
300        Ok(())
301    }
302
303    #[test]
304    fn test_multiple_storage_operations() -> eyre::Result<()> {
305        let db = CacheDB::new(EmptyDB::new());
306        let mut evm = TempoEvmFactory::default().create_evm(db, EvmEnv::default());
307        let ctx = evm.ctx_mut();
308        let evm_internals = EvmInternals::new(&mut ctx.journaled_state, &ctx.block);
309        let mut provider = EvmPrecompileStorageProvider::new_max_gas(evm_internals, &ctx.cfg);
310
311        let address = address!("5000000000000000000000000000000000000005");
312
313        // Store multiple values
314        for i in 0..10 {
315            let key = U256::from(i);
316            let value = U256::from(i * 100);
317            provider.sstore(address, key, value)?;
318        }
319
320        // Verify all values
321        for i in 0..10 {
322            let key = U256::from(i);
323            let expected_value = U256::from(i * 100);
324            let loaded_value = provider.sload(address, key)?;
325            assert_eq!(loaded_value, expected_value);
326        }
327
328        Ok(())
329    }
330
331    #[test]
332    fn test_overwrite_storage() -> eyre::Result<()> {
333        let db = CacheDB::new(EmptyDB::new());
334        let mut evm = TempoEvmFactory::default().create_evm(db, EvmEnv::default());
335        let ctx = evm.ctx_mut();
336        let evm_internals = EvmInternals::new(&mut ctx.journaled_state, &ctx.block);
337        let mut provider = EvmPrecompileStorageProvider::new_max_gas(evm_internals, &ctx.cfg);
338
339        let address = address!("6000000000000000000000000000000000000006");
340        let key = U256::from(99);
341
342        // Store initial value
343        let initial_value = U256::from(111);
344        provider.sstore(address, key, initial_value)?;
345        assert_eq!(provider.sload(address, key)?, initial_value);
346
347        // Overwrite with new value
348        let new_value = U256::from(999);
349        provider.sstore(address, key, new_value)?;
350        assert_eq!(provider.sload(address, key)?, new_value);
351
352        Ok(())
353    }
354
355    #[test]
356    fn test_different_addresses() -> eyre::Result<()> {
357        let db = CacheDB::new(EmptyDB::new());
358        let mut evm = TempoEvmFactory::default().create_evm(db, EvmEnv::default());
359        let ctx = evm.ctx_mut();
360        let evm_internals = EvmInternals::new(&mut ctx.journaled_state, &ctx.block);
361        let mut provider = EvmPrecompileStorageProvider::new_max_gas(evm_internals, &ctx.cfg);
362
363        let address1 = address!("7000000000000000000000000000000000000001");
364        let address2 = address!("7000000000000000000000000000000000000002");
365        let key = U256::from(42);
366
367        // Store different values at the same key for different addresses
368        let value1 = U256::from(100);
369        let value2 = U256::from(200);
370
371        provider.sstore(address1, key, value1)?;
372        provider.sstore(address2, key, value2)?;
373
374        // Verify values are independent
375        assert_eq!(provider.sload(address1, key)?, value1);
376        assert_eq!(provider.sload(address2, key)?, value2);
377
378        Ok(())
379    }
380
381    #[test]
382    fn test_multiple_transient_storage_operations() -> eyre::Result<()> {
383        let db = CacheDB::new(EmptyDB::new());
384        let mut evm = TempoEvmFactory::default().create_evm(db, EvmEnv::default());
385        let ctx = evm.ctx_mut();
386        let evm_internals = EvmInternals::new(&mut ctx.journaled_state, &ctx.block);
387        let mut provider = EvmPrecompileStorageProvider::new_max_gas(evm_internals, &ctx.cfg);
388
389        let address = address!("8000000000000000000000000000000000000001");
390
391        // Store multiple values
392        for i in 0..10 {
393            let key = U256::from(i);
394            let value = U256::from(i * 100);
395            provider.tstore(address, key, value)?;
396        }
397
398        // Verify all values
399        for i in 0..10 {
400            let key = U256::from(i);
401            let expected_value = U256::from(i * 100);
402            let loaded_value = provider.tload(address, key)?;
403            assert_eq!(loaded_value, expected_value);
404        }
405
406        Ok(())
407    }
408
409    #[test]
410    fn test_overwrite_transient_storage() -> eyre::Result<()> {
411        let db = CacheDB::new(EmptyDB::new());
412        let mut evm = TempoEvmFactory::default().create_evm(db, EvmEnv::default());
413        let ctx = evm.ctx_mut();
414        let evm_internals = EvmInternals::new(&mut ctx.journaled_state, &ctx.block);
415        let mut provider = EvmPrecompileStorageProvider::new_max_gas(evm_internals, &ctx.cfg);
416
417        let address = address!("9000000000000000000000000000000000000001");
418        let key = U256::from(99);
419
420        // Store initial value
421        let initial_value = U256::from(111);
422        provider.tstore(address, key, initial_value)?;
423        assert_eq!(provider.tload(address, key)?, initial_value);
424
425        // Overwrite with new value
426        let new_value = U256::from(999);
427        provider.tstore(address, key, new_value)?;
428        assert_eq!(provider.tload(address, key)?, new_value);
429
430        Ok(())
431    }
432
433    #[test]
434    fn test_transient_storage_different_addresses() -> eyre::Result<()> {
435        let db = CacheDB::new(EmptyDB::new());
436        let mut evm = TempoEvmFactory::default().create_evm(db, EvmEnv::default());
437        let ctx = evm.ctx_mut();
438        let evm_internals = EvmInternals::new(&mut ctx.journaled_state, &ctx.block);
439        let mut provider = EvmPrecompileStorageProvider::new_max_gas(evm_internals, &ctx.cfg);
440
441        let address1 = address!("a000000000000000000000000000000000000001");
442        let address2 = address!("a000000000000000000000000000000000000002");
443        let key = U256::from(42);
444
445        // Store different values at the same key for different addresses
446        let value1 = U256::from(100);
447        let value2 = U256::from(200);
448
449        provider.tstore(address1, key, value1)?;
450        provider.tstore(address2, key, value2)?;
451
452        // Verify values are independent
453        assert_eq!(provider.tload(address1, key)?, value1);
454        assert_eq!(provider.tload(address2, key)?, value2);
455
456        Ok(())
457    }
458
459    #[test]
460    fn test_transient_storage_isolation_from_persistent() -> eyre::Result<()> {
461        let db = CacheDB::new(EmptyDB::new());
462        let mut evm = TempoEvmFactory::default().create_evm(db, EvmEnv::default());
463        let ctx = evm.ctx_mut();
464        let evm_internals = EvmInternals::new(&mut ctx.journaled_state, &ctx.block);
465        let mut provider = EvmPrecompileStorageProvider::new_max_gas(evm_internals, &ctx.cfg);
466
467        let address = address!("b000000000000000000000000000000000000001");
468        let key = U256::from(123);
469        let persistent_value = U256::from(456);
470        let transient_value = U256::from(789);
471
472        // Store in persistent storage
473        provider.sstore(address, key, persistent_value)?;
474
475        // Store in transient storage with same key
476        provider.tstore(address, key, transient_value)?;
477
478        // Verify they are independent
479        assert_eq!(provider.sload(address, key)?, persistent_value);
480        assert_eq!(provider.tload(address, key)?, transient_value);
481
482        Ok(())
483    }
484}