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 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 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 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 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 provider.with_account_info(address, &mut |info| {
269 assert!(info.balance.is_zero());
271 assert_eq!(info.nonce, 0);
272 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 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 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 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 let initial_value = U256::from(111);
344 provider.sstore(address, key, initial_value)?;
345 assert_eq!(provider.sload(address, key)?, initial_value);
346
347 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 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 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 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 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 let initial_value = U256::from(111);
422 provider.tstore(address, key, initial_value)?;
423 assert_eq!(provider.tload(address, key)?, initial_value);
424
425 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 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 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 provider.sstore(address, key, persistent_value)?;
474
475 provider.tstore(address, key, transient_value)?;
477
478 assert_eq!(provider.sload(address, key)?, persistent_value);
480 assert_eq!(provider.tload(address, key)?, transient_value);
481
482 Ok(())
483 }
484}