1use alloy::primitives::{Address, Log, LogData, U256};
2use alloy_evm::{EvmInternals, EvmInternalsError};
3use revm::{
4 context::{Block, CfgEnv, journaled_state::JournalCheckpoint},
5 context_interface::cfg::{GasParams, gas},
6 state::{AccountInfo, Bytecode},
7};
8use tempo_chainspec::hardfork::TempoHardfork;
9
10use crate::{error::TempoPrecompileError, storage::PrecompileStorageProvider};
11
12pub struct EvmPrecompileStorageProvider<'a> {
16 internals: EvmInternals<'a>,
17 gas_remaining: u64,
18 gas_refunded: i64,
19 gas_limit: u64,
20 spec: TempoHardfork,
21 is_static: bool,
22 gas_params: GasParams,
23 #[cfg(debug_assertions)]
25 checkpoint_stack: Vec<(usize, usize)>,
26}
27
28impl<'a> EvmPrecompileStorageProvider<'a> {
29 pub fn new(
31 internals: EvmInternals<'a>,
32 gas_limit: u64,
33 spec: TempoHardfork,
34 is_static: bool,
35 gas_params: GasParams,
36 ) -> Self {
37 Self {
38 internals,
39 gas_remaining: gas_limit,
40 gas_refunded: 0,
41 gas_limit,
42 spec,
43 is_static,
44 gas_params,
45 #[cfg(debug_assertions)]
46 checkpoint_stack: Vec::new(),
47 }
48 }
49
50 pub fn new_max_gas(internals: EvmInternals<'a>, cfg: &CfgEnv<TempoHardfork>) -> Self {
52 Self::new(internals, u64::MAX, cfg.spec, false, cfg.gas_params.clone())
53 }
54
55 pub fn new_with_gas_limit(
57 internals: EvmInternals<'a>,
58 cfg: &CfgEnv<TempoHardfork>,
59 gas_limit: u64,
60 ) -> Self {
61 Self::new(
62 internals,
63 gas_limit,
64 cfg.spec,
65 false,
66 cfg.gas_params.clone(),
67 )
68 }
69}
70
71impl<'a> PrecompileStorageProvider for EvmPrecompileStorageProvider<'a> {
72 fn chain_id(&self) -> u64 {
73 self.internals.chain_id()
74 }
75
76 fn timestamp(&self) -> U256 {
77 self.internals.block_timestamp()
78 }
79
80 fn beneficiary(&self) -> Address {
81 self.internals.block_env().beneficiary()
82 }
83
84 fn block_number(&self) -> u64 {
85 self.internals.block_env().number().to::<u64>()
86 }
87
88 #[inline]
89 fn set_code(&mut self, address: Address, code: Bytecode) -> Result<(), TempoPrecompileError> {
90 self.deduct_gas(self.gas_params.code_deposit_cost(code.len()))?;
91
92 self.internals
93 .load_account_mut(address)?
94 .set_code_and_hash_slow(code);
95
96 Ok(())
97 }
98
99 #[inline]
100 fn with_account_info(
101 &mut self,
102 address: Address,
103 f: &mut dyn FnMut(&AccountInfo),
104 ) -> Result<(), TempoPrecompileError> {
105 let additional_cost = self.gas_params.cold_account_additional_cost();
106
107 let mut account = self
108 .internals
109 .load_account_mut_skip_cold_load(address, false)?;
110
111 deduct_gas(
113 &mut self.gas_remaining,
114 self.gas_params.warm_storage_read_cost(),
115 )?;
116
117 if account.is_cold {
119 deduct_gas(&mut self.gas_remaining, additional_cost)?;
120 }
121
122 account.load_code()?;
123
124 f(&account.data.account().info);
125 Ok(())
126 }
127
128 #[inline]
129 fn sstore(
130 &mut self,
131 address: Address,
132 key: U256,
133 value: U256,
134 ) -> Result<(), TempoPrecompileError> {
135 let result = self
136 .internals
137 .load_account_mut(address)?
138 .sstore(key, value, false)?;
139
140 self.deduct_gas(self.gas_params.sstore_static_gas())?;
142
143 self.deduct_gas(
145 self.gas_params
146 .sstore_dynamic_gas(true, &result.data, result.is_cold),
147 )?;
148
149 self.refund_gas(self.gas_params.sstore_refund(true, &result.data));
151
152 Ok(())
153 }
154
155 #[inline]
156 fn tstore(
157 &mut self,
158 address: Address,
159 key: U256,
160 value: U256,
161 ) -> Result<(), TempoPrecompileError> {
162 self.deduct_gas(self.gas_params.warm_storage_read_cost())?;
163 self.internals.tstore(address, key, value);
164 Ok(())
165 }
166
167 #[inline]
168 fn emit_event(&mut self, address: Address, event: LogData) -> Result<(), TempoPrecompileError> {
169 self.deduct_gas(
170 gas::LOG
171 + self
172 .gas_params
173 .log_cost(event.topics().len() as u8, event.data.len() as u64),
174 )?;
175
176 self.internals.log(Log {
177 address,
178 data: event,
179 });
180
181 Ok(())
182 }
183
184 #[inline]
185 fn sload(&mut self, address: Address, key: U256) -> Result<U256, TempoPrecompileError> {
186 let additional_cost = self.gas_params.cold_storage_additional_cost();
187
188 let value;
189 let is_cold;
190 {
191 let mut account = self.internals.load_account_mut(address)?;
192 let val = account.sload(key, false)?;
193
194 value = val.present_value;
195 is_cold = val.is_cold;
196 };
197
198 self.deduct_gas(self.gas_params.warm_storage_read_cost())?;
200
201 if is_cold {
202 self.deduct_gas(additional_cost)?;
203 }
204
205 Ok(value)
206 }
207
208 #[inline]
209 fn tload(&mut self, address: Address, key: U256) -> Result<U256, TempoPrecompileError> {
210 self.deduct_gas(self.gas_params.warm_storage_read_cost())?;
211
212 Ok(self.internals.tload(address, key))
213 }
214
215 #[inline]
216 fn deduct_gas(&mut self, gas: u64) -> Result<(), TempoPrecompileError> {
217 self.gas_remaining = self
218 .gas_remaining
219 .checked_sub(gas)
220 .ok_or(TempoPrecompileError::OutOfGas)?;
221 Ok(())
222 }
223
224 #[inline]
225 fn refund_gas(&mut self, gas: i64) {
226 self.gas_refunded = self.gas_refunded.saturating_add(gas);
227 }
228
229 #[inline]
230 fn gas_used(&self) -> u64 {
231 self.gas_limit - self.gas_remaining
232 }
233
234 #[inline]
235 fn gas_refunded(&self) -> i64 {
236 self.gas_refunded
237 }
238
239 #[inline]
240 fn spec(&self) -> TempoHardfork {
241 self.spec
242 }
243
244 #[inline]
245 fn is_static(&self) -> bool {
246 self.is_static
247 }
248
249 #[inline]
250 fn checkpoint(&mut self) -> JournalCheckpoint {
251 let cp = self.internals.checkpoint();
252 #[cfg(debug_assertions)]
253 self.track_checkpoint(&cp);
254 cp
255 }
256
257 #[inline]
258 fn checkpoint_commit(&mut self, _checkpoint: JournalCheckpoint) {
259 #[cfg(debug_assertions)]
260 self.assert_lifo(&_checkpoint, "commit");
261 self.internals.checkpoint_commit()
262 }
263
264 #[inline]
265 fn checkpoint_revert(&mut self, checkpoint: JournalCheckpoint) {
266 #[cfg(debug_assertions)]
267 self.assert_lifo(&checkpoint, "revert");
268 self.internals.checkpoint_revert(checkpoint)
269 }
270}
271
272#[cfg(debug_assertions)]
278impl EvmPrecompileStorageProvider<'_> {
279 fn track_checkpoint(&mut self, cp: &JournalCheckpoint) {
281 self.checkpoint_stack.push((cp.journal_i, cp.log_i));
282 }
283
284 fn assert_lifo(&mut self, cp: &JournalCheckpoint, op: &str) {
286 let top = self
287 .checkpoint_stack
288 .pop()
289 .unwrap_or_else(|| panic!("checkpoint_{op}: no active checkpoint"));
290
291 assert_eq!(
292 (cp.journal_i, cp.log_i),
293 top,
294 "out-of-order checkpoint {op} (expected top of stack)"
295 );
296 }
297}
298
299impl From<EvmInternalsError> for TempoPrecompileError {
300 fn from(value: EvmInternalsError) -> Self {
301 Self::Fatal(value.to_string())
302 }
303}
304
305#[inline]
307pub fn deduct_gas(gas: &mut u64, additional_cost: u64) -> Result<(), TempoPrecompileError> {
308 *gas = gas
309 .checked_sub(additional_cost)
310 .ok_or(TempoPrecompileError::OutOfGas)?;
311 Ok(())
312}
313
314#[cfg(test)]
315mod tests {
316 use super::*;
317 use alloy::primitives::{B256, address, b256, bytes, keccak256};
318 use alloy_evm::{EvmEnv, EvmFactory, EvmInternals, revm::context::Host};
319 use alloy_signer::SignerSync;
320 use alloy_signer_local::PrivateKeySigner;
321 use revm::{
322 database::{CacheDB, EmptyDB},
323 interpreter::StateLoad,
324 };
325 use tempo_evm::TempoEvmFactory;
326
327 #[test]
328 fn test_sstore_sload() -> eyre::Result<()> {
329 let db = CacheDB::new(EmptyDB::new());
330 let mut evm = TempoEvmFactory::default().create_evm(db, EvmEnv::default());
331 let ctx = evm.ctx_mut();
332 let evm_internals =
333 EvmInternals::new(&mut ctx.journaled_state, &ctx.block, &ctx.cfg, &ctx.tx);
334 let mut provider = EvmPrecompileStorageProvider::new_max_gas(evm_internals, &ctx.cfg);
335
336 let addr = Address::random();
337 let key = U256::random();
338
339 let value = U256::random();
340
341 provider.sstore(addr, key, value)?;
342 let sload_val = provider.sload(addr, key)?;
343
344 assert_eq!(sload_val, value);
345 Ok(())
346 }
347
348 #[test]
349 fn test_set_code() -> eyre::Result<()> {
350 let db = CacheDB::new(EmptyDB::new());
351 let mut evm = TempoEvmFactory::default().create_evm(db, EvmEnv::default());
352 let ctx = evm.ctx_mut();
353 let evm_internals =
354 EvmInternals::new(&mut ctx.journaled_state, &ctx.block, &ctx.cfg, &ctx.tx);
355 let mut provider = EvmPrecompileStorageProvider::new_max_gas(evm_internals, &ctx.cfg);
356
357 let addr = Address::random();
358 let code = Bytecode::new_raw(vec![0xff].into());
359 provider.set_code(addr, code.clone())?;
360 drop(provider);
361
362 let Some(StateLoad { data, is_cold: _ }) = evm.load_account_code(addr) else {
363 panic!("Failed to load account code")
364 };
365
366 assert_eq!(data, *code.original_bytes());
367 Ok(())
368 }
369
370 #[test]
371 fn test_get_account_info() -> eyre::Result<()> {
372 let db = CacheDB::new(EmptyDB::new());
373 let mut evm = TempoEvmFactory::default().create_evm(db, EvmEnv::default());
374 let ctx = evm.ctx_mut();
375 let evm_internals =
376 EvmInternals::new(&mut ctx.journaled_state, &ctx.block, &ctx.cfg, &ctx.tx);
377 let mut provider = EvmPrecompileStorageProvider::new_max_gas(evm_internals, &ctx.cfg);
378
379 let address = address!("3000000000000000000000000000000000000003");
380
381 provider.with_account_info(address, &mut |info| {
383 assert!(info.balance.is_zero());
385 assert_eq!(info.nonce, 0);
386 if let Some(ref code) = info.code {
388 assert!(code.is_empty(), "New account should have empty code");
389 }
390 })?;
391
392 Ok(())
393 }
394
395 #[test]
396 fn test_emit_event() -> eyre::Result<()> {
397 let db = CacheDB::new(EmptyDB::new());
398 let mut evm = TempoEvmFactory::default().create_evm(db, EvmEnv::default());
399 let ctx = evm.ctx_mut();
400 let evm_internals =
401 EvmInternals::new(&mut ctx.journaled_state, &ctx.block, &ctx.cfg, &ctx.tx);
402 let mut provider = EvmPrecompileStorageProvider::new_max_gas(evm_internals, &ctx.cfg);
403
404 let address = address!("4000000000000000000000000000000000000004");
405 let topic = b256!("0000000000000000000000000000000000000000000000000000000000000001");
406 let data = bytes!(
407 "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001"
408 );
409
410 let log_data = LogData::new_unchecked(vec![topic], data);
411
412 provider.emit_event(address, log_data)?;
414
415 Ok(())
416 }
417
418 #[test]
419 fn test_multiple_storage_operations() -> eyre::Result<()> {
420 let db = CacheDB::new(EmptyDB::new());
421 let mut evm = TempoEvmFactory::default().create_evm(db, EvmEnv::default());
422 let ctx = evm.ctx_mut();
423 let evm_internals =
424 EvmInternals::new(&mut ctx.journaled_state, &ctx.block, &ctx.cfg, &ctx.tx);
425 let mut provider = EvmPrecompileStorageProvider::new_max_gas(evm_internals, &ctx.cfg);
426
427 let address = address!("5000000000000000000000000000000000000005");
428
429 for i in 0..10 {
431 let key = U256::from(i);
432 let value = U256::from(i * 100);
433 provider.sstore(address, key, value)?;
434 }
435
436 for i in 0..10 {
438 let key = U256::from(i);
439 let expected_value = U256::from(i * 100);
440 let loaded_value = provider.sload(address, key)?;
441 assert_eq!(loaded_value, expected_value);
442 }
443
444 Ok(())
445 }
446
447 #[test]
448 fn test_overwrite_storage() -> eyre::Result<()> {
449 let db = CacheDB::new(EmptyDB::new());
450 let mut evm = TempoEvmFactory::default().create_evm(db, EvmEnv::default());
451 let ctx = evm.ctx_mut();
452 let evm_internals =
453 EvmInternals::new(&mut ctx.journaled_state, &ctx.block, &ctx.cfg, &ctx.tx);
454 let mut provider = EvmPrecompileStorageProvider::new_max_gas(evm_internals, &ctx.cfg);
455
456 let address = address!("6000000000000000000000000000000000000006");
457 let key = U256::from(99);
458
459 let initial_value = U256::from(111);
461 provider.sstore(address, key, initial_value)?;
462 assert_eq!(provider.sload(address, key)?, initial_value);
463
464 let new_value = U256::from(999);
466 provider.sstore(address, key, new_value)?;
467 assert_eq!(provider.sload(address, key)?, new_value);
468
469 Ok(())
470 }
471
472 #[test]
473 fn test_different_addresses() -> eyre::Result<()> {
474 let db = CacheDB::new(EmptyDB::new());
475 let mut evm = TempoEvmFactory::default().create_evm(db, EvmEnv::default());
476 let ctx = evm.ctx_mut();
477 let evm_internals =
478 EvmInternals::new(&mut ctx.journaled_state, &ctx.block, &ctx.cfg, &ctx.tx);
479 let mut provider = EvmPrecompileStorageProvider::new_max_gas(evm_internals, &ctx.cfg);
480
481 let address1 = address!("7000000000000000000000000000000000000001");
482 let address2 = address!("7000000000000000000000000000000000000002");
483 let key = U256::from(42);
484
485 let value1 = U256::from(100);
487 let value2 = U256::from(200);
488
489 provider.sstore(address1, key, value1)?;
490 provider.sstore(address2, key, value2)?;
491
492 assert_eq!(provider.sload(address1, key)?, value1);
494 assert_eq!(provider.sload(address2, key)?, value2);
495
496 Ok(())
497 }
498
499 #[test]
500 fn test_multiple_transient_storage_operations() -> eyre::Result<()> {
501 let db = CacheDB::new(EmptyDB::new());
502 let mut evm = TempoEvmFactory::default().create_evm(db, EvmEnv::default());
503 let ctx = evm.ctx_mut();
504 let evm_internals =
505 EvmInternals::new(&mut ctx.journaled_state, &ctx.block, &ctx.cfg, &ctx.tx);
506 let mut provider = EvmPrecompileStorageProvider::new_max_gas(evm_internals, &ctx.cfg);
507
508 let address = address!("8000000000000000000000000000000000000001");
509
510 for i in 0..10 {
512 let key = U256::from(i);
513 let value = U256::from(i * 100);
514 provider.tstore(address, key, value)?;
515 }
516
517 for i in 0..10 {
519 let key = U256::from(i);
520 let expected_value = U256::from(i * 100);
521 let loaded_value = provider.tload(address, key)?;
522 assert_eq!(loaded_value, expected_value);
523 }
524
525 Ok(())
526 }
527
528 #[test]
529 fn test_overwrite_transient_storage() -> eyre::Result<()> {
530 let db = CacheDB::new(EmptyDB::new());
531 let mut evm = TempoEvmFactory::default().create_evm(db, EvmEnv::default());
532 let ctx = evm.ctx_mut();
533 let evm_internals =
534 EvmInternals::new(&mut ctx.journaled_state, &ctx.block, &ctx.cfg, &ctx.tx);
535 let mut provider = EvmPrecompileStorageProvider::new_max_gas(evm_internals, &ctx.cfg);
536
537 let address = address!("9000000000000000000000000000000000000001");
538 let key = U256::from(99);
539
540 let initial_value = U256::from(111);
542 provider.tstore(address, key, initial_value)?;
543 assert_eq!(provider.tload(address, key)?, initial_value);
544
545 let new_value = U256::from(999);
547 provider.tstore(address, key, new_value)?;
548 assert_eq!(provider.tload(address, key)?, new_value);
549
550 Ok(())
551 }
552
553 #[test]
554 fn test_transient_storage_different_addresses() -> eyre::Result<()> {
555 let db = CacheDB::new(EmptyDB::new());
556 let mut evm = TempoEvmFactory::default().create_evm(db, EvmEnv::default());
557 let ctx = evm.ctx_mut();
558 let evm_internals =
559 EvmInternals::new(&mut ctx.journaled_state, &ctx.block, &ctx.cfg, &ctx.tx);
560 let mut provider = EvmPrecompileStorageProvider::new_max_gas(evm_internals, &ctx.cfg);
561
562 let address1 = address!("a000000000000000000000000000000000000001");
563 let address2 = address!("a000000000000000000000000000000000000002");
564 let key = U256::from(42);
565
566 let value1 = U256::from(100);
568 let value2 = U256::from(200);
569
570 provider.tstore(address1, key, value1)?;
571 provider.tstore(address2, key, value2)?;
572
573 assert_eq!(provider.tload(address1, key)?, value1);
575 assert_eq!(provider.tload(address2, key)?, value2);
576
577 Ok(())
578 }
579
580 #[test]
581 fn test_transient_storage_isolation_from_persistent() -> eyre::Result<()> {
582 let db = CacheDB::new(EmptyDB::new());
583 let mut evm = TempoEvmFactory::default().create_evm(db, EvmEnv::default());
584 let ctx = evm.ctx_mut();
585 let evm_internals =
586 EvmInternals::new(&mut ctx.journaled_state, &ctx.block, &ctx.cfg, &ctx.tx);
587 let mut provider = EvmPrecompileStorageProvider::new_max_gas(evm_internals, &ctx.cfg);
588
589 let address = address!("b000000000000000000000000000000000000001");
590 let key = U256::from(123);
591 let persistent_value = U256::from(456);
592 let transient_value = U256::from(789);
593
594 provider.sstore(address, key, persistent_value)?;
596
597 provider.tstore(address, key, transient_value)?;
599
600 assert_eq!(provider.sload(address, key)?, persistent_value);
602 assert_eq!(provider.tload(address, key)?, transient_value);
603
604 Ok(())
605 }
606
607 #[test]
608 fn test_keccak256_gas() -> eyre::Result<()> {
609 let db = CacheDB::new(EmptyDB::new());
610 let mut evm = TempoEvmFactory::default().create_evm(db, EvmEnv::default());
611 let ctx = evm.ctx_mut();
612 let evm_internals =
613 EvmInternals::new(&mut ctx.journaled_state, &ctx.block, &ctx.cfg, &ctx.tx);
614 let mut provider = EvmPrecompileStorageProvider::new_max_gas(evm_internals, &ctx.cfg);
615
616 let data = b"hello world";
618 assert_eq!(provider.keccak256(data)?, keccak256(data));
619 assert_eq!(provider.gas_used(), 36);
620
621 provider.keccak256(&[0u8; 64])?;
623 assert_eq!(provider.gas_used(), 78);
624 std::mem::drop(provider);
625
626 let evm_internals =
628 EvmInternals::new(&mut ctx.journaled_state, &ctx.block, &ctx.cfg, &ctx.tx);
629 let mut provider =
630 EvmPrecompileStorageProvider::new_with_gas_limit(evm_internals, &ctx.cfg, 30);
631 assert!(matches!(
632 provider.keccak256(b"hello"),
633 Err(TempoPrecompileError::OutOfGas)
634 ));
635
636 Ok(())
637 }
638
639 #[test]
640 fn test_recover_signer_gas() -> eyre::Result<()> {
641 let db = CacheDB::new(EmptyDB::new());
642 let mut evm = TempoEvmFactory::default().create_evm(db, EvmEnv::default());
643 let ctx = evm.ctx_mut();
644 let evm_internals =
645 EvmInternals::new(&mut ctx.journaled_state, &ctx.block, &ctx.cfg, &ctx.tx);
646 let mut provider = EvmPrecompileStorageProvider::new_max_gas(evm_internals, &ctx.cfg);
647
648 assert!(
650 provider
651 .recover_signer(B256::ZERO, 0, B256::ZERO, B256::ZERO)?
652 .is_none()
653 );
654 assert_eq!(provider.gas_used(), crate::ECRECOVER_GAS);
655
656 let signer = PrivateKeySigner::random();
658 let digest = keccak256(b"test message");
659 let sig = signer.sign_hash_sync(&digest).unwrap();
660 let v = sig.v() as u8 + 27;
661 let r: B256 = sig.r().into();
662 let s: B256 = sig.s().into();
663 assert_eq!(
664 provider.recover_signer(digest, v, r, s)?,
665 Some(signer.address())
666 );
667 assert_eq!(provider.gas_used(), crate::ECRECOVER_GAS * 2);
668 std::mem::drop(provider);
669
670 let evm_internals =
672 EvmInternals::new(&mut ctx.journaled_state, &ctx.block, &ctx.cfg, &ctx.tx);
673 let mut provider =
674 EvmPrecompileStorageProvider::new_with_gas_limit(evm_internals, &ctx.cfg, 100);
675 assert!(matches!(
676 provider.recover_signer(digest, v, r, s),
677 Err(TempoPrecompileError::OutOfGas)
678 ));
679
680 Ok(())
681 }
682}