1use crate::{
2 error::TempoPrecompileError, storage::PrecompileStorageProvider,
3 tip1060_storage_credits::sstore_storage_credits,
4};
5use alloy::primitives::{Address, Log, LogData, U256};
6use alloy_evm::EvmInternals;
7use revm::{
8 context::{Block, CfgEnv, journaled_state::JournalCheckpoint},
9 context_interface::cfg::{GasParams, gas},
10 interpreter::{SStoreResult, StateLoad, gas::GasTracker},
11 state::{AccountInfo, Bytecode},
12};
13use tempo_chainspec::hardfork::TempoHardfork;
14
15pub struct EvmPrecompileStorageProvider<'a> {
19 internals: EvmInternals<'a>,
20 gas_tracker: GasTracker,
21 spec: TempoHardfork,
22 amsterdam_eip8037_enabled: bool,
23 is_static: bool,
24 gas_params: GasParams,
25 tip1060_storage_credits_enabled: bool,
26 #[cfg(debug_assertions)]
28 checkpoint_stack: Vec<(usize, usize)>,
29}
30
31impl<'a> EvmPrecompileStorageProvider<'a> {
32 pub fn new(
34 internals: EvmInternals<'a>,
35 gas_limit: u64,
36 reservoir: u64,
37 spec: TempoHardfork,
38 amsterdam_eip8037_enabled: bool,
39 is_static: bool,
40 gas_params: GasParams,
41 ) -> Self {
42 Self {
43 internals,
44 gas_tracker: GasTracker::new(gas_limit, gas_limit, reservoir),
45 spec,
46 amsterdam_eip8037_enabled,
47 is_static,
48 gas_params,
49 tip1060_storage_credits_enabled: spec.is_t7(),
50 #[cfg(debug_assertions)]
51 checkpoint_stack: Vec::new(),
52 }
53 }
54
55 pub fn new_max_gas(internals: EvmInternals<'a>, cfg: &CfgEnv<TempoHardfork>) -> Self {
57 Self::new(
58 internals,
59 u64::MAX,
60 0,
61 cfg.spec,
62 cfg.enable_amsterdam_eip8037,
63 false,
64 cfg.gas_params.clone(),
65 )
66 }
67
68 pub fn new_with_gas_limit(
70 internals: EvmInternals<'a>,
71 cfg: &CfgEnv<TempoHardfork>,
72 gas_limit: u64,
73 reservoir: u64,
74 ) -> Self {
75 Self::new(
76 internals,
77 gas_limit,
78 reservoir,
79 cfg.spec,
80 cfg.enable_amsterdam_eip8037,
81 false,
82 cfg.gas_params.clone(),
83 )
84 }
85
86 #[inline]
87 fn deduct_state_gas(&mut self, gas: u64) -> Result<(), TempoPrecompileError> {
88 if !self.gas_tracker.record_state_cost(gas) {
89 return Err(TempoPrecompileError::OutOfGas);
90 }
91 Ok(())
92 }
93}
94
95impl crate::tip1060_storage_credits::StorageCreditsBackend for EvmPrecompileStorageProvider<'_> {
96 type Error = TempoPrecompileError;
97
98 #[inline]
99 fn gas_tracker(&mut self) -> &mut GasTracker {
100 &mut self.gas_tracker
101 }
102
103 #[inline]
104 fn gas_params(&self) -> &GasParams {
105 &self.gas_params
106 }
107
108 #[inline]
109 fn sload(
110 &mut self,
111 address: Address,
112 key: U256,
113 skip_cold_load: bool,
114 ) -> Result<StateLoad<U256>, Self::Error> {
115 let mut account = self.internals.load_account_mut(address)?;
116 let val = account.sload(key, skip_cold_load)?;
117 Ok(StateLoad::new(val.present_value, val.is_cold))
118 }
119
120 #[inline]
121 fn sstore(
122 &mut self,
123 address: Address,
124 key: U256,
125 value: U256,
126 skip_cold_load: bool,
127 ) -> Result<StateLoad<SStoreResult>, Self::Error> {
128 self.internals
129 .load_account_mut(address)?
130 .sstore(key, value, skip_cold_load)
131 .map_err(Into::into)
132 }
133
134 #[inline]
135 fn tload(&mut self, address: Address, key: U256) -> U256 {
136 self.internals.tload(address, key)
137 }
138
139 #[inline]
140 fn tstore(&mut self, address: Address, key: U256, value: U256) {
141 self.internals.tstore(address, key, value);
142 }
143
144 #[inline]
145 fn emit_event(&mut self, address: Address, event: LogData) -> Result<(), Self::Error> {
146 PrecompileStorageProvider::emit_event(self, address, event)
147 }
148}
149
150impl<'a> PrecompileStorageProvider for EvmPrecompileStorageProvider<'a> {
151 fn chain_id(&self) -> u64 {
152 self.internals.chain_id()
153 }
154
155 fn timestamp(&self) -> U256 {
156 self.internals.block_timestamp()
157 }
158
159 fn beneficiary(&self) -> Address {
160 self.internals.block_env().beneficiary()
161 }
162
163 fn block_number(&self) -> u64 {
164 self.internals.block_env().number().to::<u64>()
165 }
166
167 #[inline]
168 fn set_code(&mut self, address: Address, code: Bytecode) -> Result<(), TempoPrecompileError> {
169 let code_len = code.len();
170 self.deduct_gas(self.gas_params.code_deposit_cost(code_len))?;
171
172 self.deduct_state_gas(self.gas_params.code_deposit_state_gas(code_len))?;
174
175 let was_empty = {
176 let mut account = self.internals.load_account_mut(address)?;
177 let was_empty = account.data.account().info.is_empty();
178 account.set_code_and_hash_slow(code);
179 was_empty
180 };
181
182 if self.amsterdam_eip8037_enabled && was_empty {
184 self.deduct_gas(self.gas_params.create_cost())?;
185 self.deduct_state_gas(self.gas_params.create_state_gas())?;
186 self.deduct_gas(self.gas_params.keccak256_cost(code_len.div_ceil(32)))?;
187 }
188
189 Ok(())
190 }
191
192 #[inline]
193 fn with_account_info(
194 &mut self,
195 address: Address,
196 f: &mut dyn FnMut(&AccountInfo),
197 ) -> Result<(), TempoPrecompileError> {
198 let additional_cost = self.gas_params.cold_account_additional_cost();
199
200 let insufficient_gas_for_cold_load = if self.spec.is_t4() {
202 self.deduct_gas(self.gas_params.warm_storage_read_cost())?;
203 self.gas_tracker.remaining() < additional_cost
204 } else {
205 false
206 };
207
208 let mut account = self
209 .internals
210 .load_account_mut_skip_cold_load(address, insufficient_gas_for_cold_load)?;
211
212 if !self.spec.is_t4() {
213 deduct_gas(
214 &mut self.gas_tracker,
215 self.gas_params.warm_storage_read_cost(),
216 )?;
217 }
218
219 if account.is_cold {
221 deduct_gas(&mut self.gas_tracker, additional_cost)?;
222 }
223
224 account.load_code()?;
225
226 f(&account.data.account().info);
227 Ok(())
228 }
229
230 #[inline]
231 fn sstore(
232 &mut self,
233 address: Address,
234 key: U256,
235 value: U256,
236 ) -> Result<(), TempoPrecompileError> {
237 let insufficient_gas_for_cold_load = if self.spec.is_t4() {
239 self.deduct_gas(self.gas_params.sstore_static_gas())?;
240 self.gas_tracker.remaining() < self.gas_params.cold_storage_additional_cost()
241 } else {
242 false
243 };
244
245 let result = self.internals.load_account_mut(address)?.sstore(
246 key,
247 value,
248 insufficient_gas_for_cold_load,
249 )?;
250
251 if !self.spec.is_t4() {
252 self.deduct_gas(self.gas_params.sstore_static_gas())?;
253 }
254
255 if self.tip1060_storage_credits_enabled {
258 sstore_storage_credits(self, address, &result)?
259 }
260
261 self.deduct_gas(
263 self.gas_params
264 .sstore_dynamic_gas(true, &result.data, result.is_cold),
265 )?;
266
267 self.deduct_state_gas(self.gas_params.sstore_state_gas(&result.data))?;
269
270 self.refund_gas(self.gas_params.sstore_refund(true, &result.data));
272
273 Ok(())
274 }
275
276 #[inline]
277 fn tstore(
278 &mut self,
279 address: Address,
280 key: U256,
281 value: U256,
282 ) -> Result<(), TempoPrecompileError> {
283 self.deduct_gas(self.gas_params.warm_storage_read_cost())?;
284 self.internals.tstore(address, key, value);
285 Ok(())
286 }
287
288 #[inline]
289 fn emit_event(&mut self, address: Address, event: LogData) -> Result<(), TempoPrecompileError> {
290 self.deduct_gas(
291 gas::LOG
292 + self
293 .gas_params
294 .log_cost(event.topics().len() as u8, event.data.len() as u64),
295 )?;
296
297 self.internals.log(Log {
298 address,
299 data: event,
300 });
301
302 Ok(())
303 }
304
305 #[inline]
306 fn sload(&mut self, address: Address, key: U256) -> Result<U256, TempoPrecompileError> {
307 let additional_cost = self.gas_params.cold_storage_additional_cost();
308
309 let insufficient_gas_for_cold_load = if self.spec.is_t4() {
311 self.deduct_gas(self.gas_params.warm_storage_read_cost())?;
312 self.gas_tracker.remaining() < additional_cost
313 } else {
314 false
315 };
316
317 let value;
318 let is_cold;
319 {
320 let mut account = self.internals.load_account_mut(address)?;
321 let val = account.sload(key, insufficient_gas_for_cold_load)?;
322
323 value = val.present_value;
324 is_cold = val.is_cold;
325 };
326
327 if !self.spec.is_t4() {
328 self.deduct_gas(self.gas_params.warm_storage_read_cost())?;
329 }
330
331 if is_cold {
333 self.deduct_gas(additional_cost)?;
334 }
335
336 Ok(value)
337 }
338
339 #[inline]
340 fn tload(&mut self, address: Address, key: U256) -> Result<U256, TempoPrecompileError> {
341 self.deduct_gas(self.gas_params.warm_storage_read_cost())?;
342
343 Ok(self.internals.tload(address, key))
344 }
345
346 #[inline]
347 fn deduct_gas(&mut self, gas: u64) -> Result<(), TempoPrecompileError> {
348 deduct_gas(&mut self.gas_tracker, gas)
349 }
350
351 #[inline]
352 fn refund_gas(&mut self, gas: i64) {
353 self.gas_tracker.record_refund(gas);
354 }
355
356 #[inline]
357 fn gas_limit(&self) -> u64 {
358 self.gas_tracker.limit()
359 }
360
361 #[inline]
362 fn gas_used(&self) -> u64 {
363 self.gas_tracker.limit() - self.gas_tracker.remaining()
364 }
365
366 #[inline]
367 fn state_gas_used(&self) -> u64 {
368 self.gas_tracker.state_gas_spent() as u64
370 }
371
372 #[inline]
373 fn gas_refunded(&self) -> i64 {
374 self.gas_tracker.refunded()
375 }
376
377 #[inline]
378 fn reservoir(&self) -> u64 {
379 self.gas_tracker.reservoir()
380 }
381
382 #[inline]
383 fn spec(&self) -> TempoHardfork {
384 self.spec
385 }
386
387 #[inline]
388 fn amsterdam_eip8037_enabled(&self) -> bool {
389 self.amsterdam_eip8037_enabled
390 }
391
392 #[inline]
393 fn is_static(&self) -> bool {
394 self.is_static
395 }
396
397 #[inline]
398 fn checkpoint(&mut self) -> JournalCheckpoint {
399 let cp = self.internals.checkpoint();
400 #[cfg(debug_assertions)]
401 self.track_checkpoint(&cp);
402 cp
403 }
404
405 #[inline]
406 fn checkpoint_commit(&mut self, _checkpoint: JournalCheckpoint) {
407 #[cfg(debug_assertions)]
408 self.assert_lifo(&_checkpoint, "commit");
409 self.internals.checkpoint_commit()
410 }
411
412 #[inline]
413 fn checkpoint_revert(&mut self, checkpoint: JournalCheckpoint) {
414 #[cfg(debug_assertions)]
415 self.assert_lifo(&checkpoint, "revert");
416 self.internals.checkpoint_revert(checkpoint)
417 }
418
419 #[inline]
420 fn set_tip1060_storage_credits(&mut self, enabled: bool) {
421 self.tip1060_storage_credits_enabled = enabled && self.spec.is_t7();
422 }
423}
424
425#[cfg(debug_assertions)]
431impl EvmPrecompileStorageProvider<'_> {
432 fn track_checkpoint(&mut self, cp: &JournalCheckpoint) {
434 self.checkpoint_stack.push((cp.journal_i, cp.log_i));
435 }
436
437 fn assert_lifo(&mut self, cp: &JournalCheckpoint, op: &str) {
439 let top = self
440 .checkpoint_stack
441 .pop()
442 .unwrap_or_else(|| panic!("checkpoint_{op}: no active checkpoint"));
443
444 assert_eq!(
445 (cp.journal_i, cp.log_i),
446 top,
447 "out-of-order checkpoint {op} (expected top of stack)"
448 );
449 }
450}
451
452#[inline]
454pub fn deduct_gas(
455 gas_tracker: &mut GasTracker,
456 additional_cost: u64,
457) -> Result<(), TempoPrecompileError> {
458 if !gas_tracker.record_regular_cost(additional_cost) {
459 return Err(TempoPrecompileError::OutOfGas);
460 }
461 Ok(())
462}
463
464#[cfg(test)]
465mod tests {
466 use super::*;
467 use alloy::primitives::{B256, b256, bytes, keccak256};
468 use alloy_evm::{EvmEnv, EvmFactory, EvmInternals, revm::context::Host};
469 use alloy_signer::SignerSync;
470 use alloy_signer_local::PrivateKeySigner;
471 use revm::{
472 database::{CacheDB, EmptyDB},
473 interpreter::StateLoad,
474 };
475 use tempo_chainspec::hardfork::TempoHardfork;
476 use tempo_evm::{TempoEvmFactory, evm::TempoEvm};
477 use tempo_revm::gas_params::tempo_gas_params_with_amsterdam;
478
479 struct TestEvm(TempoEvm<CacheDB<EmptyDB>>);
480
481 impl TestEvm {
482 fn new(spec: TempoHardfork) -> Self {
483 Self::with_amsterdam(spec, false)
484 }
485
486 fn new_with_tip1016(spec: TempoHardfork) -> Self {
492 Self::with_amsterdam(spec, true)
493 }
494
495 fn with_amsterdam(spec: TempoHardfork, amsterdam_eip8037_enabled: bool) -> Self {
496 let db = CacheDB::new(EmptyDB::new());
497 let mut cfg = revm::context::CfgEnv::<TempoHardfork>::default();
498 cfg.spec = spec;
499 cfg.enable_amsterdam_eip8037 = amsterdam_eip8037_enabled;
500 cfg.gas_params = tempo_gas_params_with_amsterdam(spec, amsterdam_eip8037_enabled);
501
502 Self(TempoEvmFactory::default().create_evm(
503 db,
504 EvmEnv {
505 cfg_env: cfg,
506 ..Default::default()
507 },
508 ))
509 }
510
511 fn provider_with_gas_limit(
512 &mut self,
513 gas_limit: u64,
514 reservoir: u64,
515 ) -> EvmPrecompileStorageProvider<'_> {
516 let ctx = self.0.ctx_mut();
517 let spec = ctx.cfg.spec;
518 let amsterdam_eip8037_enabled = ctx.cfg.enable_amsterdam_eip8037;
519 let gas_params = ctx.cfg.gas_params.clone();
520 let evm_internals =
521 EvmInternals::new(&mut ctx.journaled_state, &ctx.block, &ctx.cfg, &ctx.tx);
522
523 EvmPrecompileStorageProvider::new(
524 evm_internals,
525 gas_limit,
526 reservoir,
527 spec,
528 amsterdam_eip8037_enabled,
529 false,
530 gas_params,
531 )
532 }
533
534 fn provider_with_reservoir(&mut self, reservoir: u64) -> EvmPrecompileStorageProvider<'_> {
535 self.provider_with_gas_limit(u64::MAX, reservoir)
536 }
537
538 fn provider_max_gas(&mut self) -> EvmPrecompileStorageProvider<'_> {
539 let ctx = self.0.ctx_mut();
540 let evm_internals =
541 EvmInternals::new(&mut ctx.journaled_state, &ctx.block, &ctx.cfg, &ctx.tx);
542 EvmPrecompileStorageProvider::new_max_gas(evm_internals, &ctx.cfg)
543 }
544 }
545
546 impl Default for TestEvm {
547 fn default() -> Self {
548 Self::new(TempoHardfork::default())
549 }
550 }
551
552 impl std::ops::Deref for TestEvm {
553 type Target = TempoEvm<CacheDB<EmptyDB>>;
554 fn deref(&self) -> &Self::Target {
555 &self.0
556 }
557 }
558
559 impl std::ops::DerefMut for TestEvm {
560 fn deref_mut(&mut self) -> &mut Self::Target {
561 &mut self.0
562 }
563 }
564
565 #[test]
566 fn test_sstore_sload() -> eyre::Result<()> {
567 let mut evm = TestEvm::default();
568 let mut provider = evm.provider_max_gas();
569
570 let addr = Address::random();
571 let key = U256::random();
572 let value = U256::random();
573
574 provider.sstore(addr, key, value)?;
575 let sload_val = provider.sload(addr, key)?;
576 assert_eq!(sload_val, value);
577 Ok(())
578 }
579
580 #[test]
581 fn test_set_code() -> eyre::Result<()> {
582 let mut evm = TestEvm::default();
583 let mut provider = evm.provider_max_gas();
584
585 let addr = Address::random();
586 let code = Bytecode::new_raw(vec![0xff].into());
587
588 provider.set_code(addr, code.clone())?;
589 std::mem::drop(provider);
590
591 let Some(StateLoad { data, is_cold: _ }) = evm.load_account_code(addr) else {
592 panic!("Failed to load account code")
593 };
594
595 assert_eq!(data, *code.original_bytes());
596 Ok(())
597 }
598
599 #[test]
600 fn test_get_account_info() -> eyre::Result<()> {
601 let mut evm = TestEvm::default();
602 let mut provider = evm.provider_max_gas();
603
604 provider.with_account_info(Address::random(), &mut |info| {
606 assert!(info.balance.is_zero());
608 assert_eq!(info.nonce, 0);
609 if let Some(ref code) = info.code {
611 assert!(code.is_empty(), "New account should have empty code");
612 }
613 })?;
614
615 Ok(())
616 }
617
618 #[test]
619 fn test_emit_event() -> eyre::Result<()> {
620 let mut evm = TestEvm::default();
621 let mut provider = evm.provider_max_gas();
622
623 let topic = b256!("0000000000000000000000000000000000000000000000000000000000000001");
624 let data = bytes!(
625 "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001"
626 );
627
628 let log_data = LogData::new_unchecked(vec![topic], data);
629
630 provider.emit_event(Address::random(), log_data)?;
632
633 Ok(())
634 }
635
636 #[test]
637 fn test_multiple_storage_operations() -> eyre::Result<()> {
638 let mut evm = TestEvm::default();
639 let mut provider = evm.provider_max_gas();
640 let address = Address::random();
641
642 for i in 0..10 {
644 let key = U256::from(i);
645 let value = U256::from(i * 100);
646 provider.sstore(address, key, value)?;
647 }
648
649 for i in 0..10 {
651 let key = U256::from(i);
652 let expected_value = U256::from(i * 100);
653 let loaded_value = provider.sload(address, key)?;
654 assert_eq!(loaded_value, expected_value);
655 }
656
657 Ok(())
658 }
659
660 #[test]
661 fn test_overwrite_storage() -> eyre::Result<()> {
662 let mut evm = TestEvm::default();
663 let mut provider = evm.provider_max_gas();
664 let address = Address::random();
665 let key = U256::from(99);
666
667 let initial_value = U256::from(111);
669 provider.sstore(address, key, initial_value)?;
670 assert_eq!(provider.sload(address, key)?, initial_value);
671
672 let new_value = U256::from(999);
674 provider.sstore(address, key, new_value)?;
675 assert_eq!(provider.sload(address, key)?, new_value);
676
677 Ok(())
678 }
679
680 #[test]
681 fn test_different_addresses() -> eyre::Result<()> {
682 let mut evm = TestEvm::default();
683 let mut provider = evm.provider_max_gas();
684 let (address1, address2) = (Address::random(), Address::random());
685 let key = U256::from(42);
686
687 let value1 = U256::from(100);
689 let value2 = U256::from(200);
690
691 provider.sstore(address1, key, value1)?;
692 provider.sstore(address2, key, value2)?;
693
694 assert_eq!(provider.sload(address1, key)?, value1);
696 assert_eq!(provider.sload(address2, key)?, value2);
697
698 Ok(())
699 }
700
701 #[test]
702 fn test_multiple_transient_storage_operations() -> eyre::Result<()> {
703 let mut evm = TestEvm::default();
704 let mut provider = evm.provider_max_gas();
705 let address = Address::random();
706
707 for i in 0..10 {
709 let key = U256::from(i);
710 let value = U256::from(i * 100);
711 provider.tstore(address, key, value)?;
712 }
713
714 for i in 0..10 {
716 let key = U256::from(i);
717 let expected_value = U256::from(i * 100);
718 let loaded_value = provider.tload(address, key)?;
719 assert_eq!(loaded_value, expected_value);
720 }
721
722 Ok(())
723 }
724
725 #[test]
726 fn test_overwrite_transient_storage() -> eyre::Result<()> {
727 let mut evm = TestEvm::default();
728 let mut provider = evm.provider_max_gas();
729 let address = Address::random();
730 let key = U256::from(99);
731
732 let initial_value = U256::from(111);
734 provider.tstore(address, key, initial_value)?;
735 assert_eq!(provider.tload(address, key)?, initial_value);
736
737 let new_value = U256::from(999);
739 provider.tstore(address, key, new_value)?;
740 assert_eq!(provider.tload(address, key)?, new_value);
741
742 Ok(())
743 }
744
745 #[test]
746 fn test_transient_storage_different_addresses() -> eyre::Result<()> {
747 let mut evm = TestEvm::default();
748 let mut provider = evm.provider_max_gas();
749 let (address1, address2) = (Address::random(), Address::random());
750 let key = U256::ONE;
751
752 let value1 = U256::from(100);
754 let value2 = U256::from(200);
755
756 provider.tstore(address1, key, value1)?;
757 provider.tstore(address2, key, value2)?;
758
759 assert_eq!(provider.tload(address1, key)?, value1);
761 assert_eq!(provider.tload(address2, key)?, value2);
762
763 Ok(())
764 }
765
766 #[test]
767 fn test_transient_storage_isolation_from_persistent() -> eyre::Result<()> {
768 let mut evm = TestEvm::default();
769 let mut provider = evm.provider_max_gas();
770 let address = Address::random();
771 let key = U256::from(123);
772 let persistent_value = U256::from(456);
773 let transient_value = U256::from(789);
774
775 provider.sstore(address, key, persistent_value)?;
777
778 provider.tstore(address, key, transient_value)?;
780
781 assert_eq!(provider.sload(address, key)?, persistent_value);
783 assert_eq!(provider.tload(address, key)?, transient_value);
784
785 Ok(())
786 }
787
788 #[test]
789 fn test_keccak256_gas() -> eyre::Result<()> {
790 let mut evm = TestEvm::default();
791 let mut provider = evm.provider_max_gas();
792
793 assert_eq!(
795 provider.keccak256(b"hello world")?,
796 keccak256(b"hello world")
797 );
798 assert_eq!(provider.gas_used(), 36);
799 provider.keccak256(&[0u8; 64])?;
801 assert_eq!(provider.gas_used(), 78);
802 std::mem::drop(provider);
803
804 let mut provider = evm.provider_with_gas_limit(30, 0);
806 assert!(matches!(
807 provider.keccak256(b"hello"),
808 Err(TempoPrecompileError::OutOfGas)
809 ));
810
811 Ok(())
812 }
813
814 #[test]
815 fn test_recover_signer_gas() -> eyre::Result<()> {
816 let mut evm = TestEvm::default();
817 let mut provider = evm.provider_max_gas();
818
819 let signer = PrivateKeySigner::random();
820 let digest = keccak256(b"test message");
821 let sig = signer.sign_hash_sync(&digest).unwrap();
822 let v = u8::from(sig.v()) + 27;
823 let r: B256 = sig.r().into();
824 let s: B256 = sig.s().into();
825
826 assert!(
828 provider
829 .recover_signer(B256::ZERO, 0, B256::ZERO, B256::ZERO)?
830 .is_none()
831 );
832 assert_eq!(provider.gas_used(), crate::ECRECOVER_GAS);
833
834 assert_eq!(
836 provider.recover_signer(digest, v, r, s)?,
837 Some(signer.address())
838 );
839 assert_eq!(provider.gas_used(), crate::ECRECOVER_GAS * 2);
840 std::mem::drop(provider);
841
842 let mut provider = evm.provider_with_gas_limit(100, 0);
844 assert!(matches!(
845 provider.recover_signer(digest, v, r, s),
846 Err(TempoPrecompileError::OutOfGas)
847 ));
848
849 Ok(())
850 }
851
852 #[test]
853 fn test_state_gas_used_only_counts_state_creating_ops() -> eyre::Result<()> {
854 let mut evm = TestEvm::new_with_tip1016(TempoHardfork::T4);
855 let gas_params = evm.ctx().cfg.gas_params.clone();
856 let mut provider = evm.provider_with_reservoir(0);
857
858 let (address, code_address, slot) = (Address::random(), Address::random(), U256::ONE);
859
860 provider.sload(address, slot)?;
862 assert_eq!(
863 provider.state_gas_used(),
864 0,
865 "SLOAD should not add state gas"
866 );
867 assert!(provider.gas_used() > 0, "SLOAD should consume regular gas");
868
869 let gas_before = provider.gas_used();
871 provider.sstore(address, slot, U256::from(1))?;
872 let state_gas_after_set = provider.state_gas_used();
873 assert_eq!(
874 state_gas_after_set, 230_000,
875 "SSTORE zero->non-zero should add 230k state gas"
876 );
877 assert!(
878 provider.gas_used() > gas_before,
879 "SSTORE should consume gas"
880 );
881
882 provider.sstore(address, slot, U256::from(2))?;
884 assert_eq!(
885 provider.state_gas_used(),
886 state_gas_after_set,
887 "SSTORE non-zero->non-zero should not add state gas"
888 );
889
890 let state_gas_before_code = provider.state_gas_used();
892 provider.set_code(
893 code_address,
894 revm::state::Bytecode::new_raw(vec![0xef].into()),
895 )?;
896 assert_eq!(
897 provider.state_gas_used(),
898 state_gas_before_code
899 + gas_params.create_state_gas()
900 + gas_params.code_deposit_state_gas(1),
901 "set_code(new account, 1 byte) should add CREATE state gas plus 2,300 code deposit state gas"
902 );
903
904 Ok(())
905 }
906
907 #[test]
910 fn test_state_gas_spills_from_reservoir_to_regular_gas() -> eyre::Result<()> {
911 let mut evm = TestEvm::new_with_tip1016(TempoHardfork::T4);
912
913 let gas_limit = 1_000_000u64;
916 let reservoir = 500_000u64;
917 let state_gas_per_sstore = 230_000u64;
918 let mut provider = evm.provider_with_gas_limit(gas_limit, reservoir);
919 let address = Address::random();
920
921 provider.sstore(address, U256::from(1), U256::from(42))?;
923
924 let regular_gas_per_sstore = provider.gas_used(); assert_eq!(
926 provider.state_gas_used(),
927 state_gas_per_sstore,
928 "first SSTORE should consume 230k state gas"
929 );
930 assert_eq!(
931 provider.reservoir(),
932 reservoir - state_gas_per_sstore,
933 "reservoir should decrease by state gas cost"
934 );
935
936 provider.sstore(address, U256::from(2), U256::from(43))?;
938
939 assert_eq!(
940 provider.state_gas_used(),
941 2 * state_gas_per_sstore,
942 "two SSTOREs should consume 460k state gas"
943 );
944 assert_eq!(
945 provider.reservoir(),
946 reservoir - 2 * state_gas_per_sstore,
947 "reservoir should have 40k left after 2 SSTOREs"
948 );
949 let remaining_reservoir = provider.reservoir(); let regular_gas_before_spill = provider.gas_used();
951
952 provider.sstore(address, U256::from(3), U256::from(44))?;
954
955 assert_eq!(
956 provider.state_gas_used(),
957 3 * state_gas_per_sstore,
958 "three SSTOREs should consume 690k state gas total"
959 );
960 assert_eq!(
961 provider.reservoir(),
962 0,
963 "reservoir should be fully exhausted"
964 );
965
966 let spill = state_gas_per_sstore - remaining_reservoir; let expected_regular_after = regular_gas_before_spill + regular_gas_per_sstore + spill;
969 assert_eq!(
970 provider.gas_used(),
971 expected_regular_after,
972 "regular gas should include spill of {spill} from exhausted reservoir"
973 );
974
975 Ok(())
976 }
977
978 #[test]
979 fn test_t4_cold_sstore_matches_tip1016_spec() -> eyre::Result<()> {
980 let mut evm = TestEvm::new_with_tip1016(TempoHardfork::T4);
981 let mut provider = evm.provider_with_reservoir(460_000);
982
983 let (address, cold_slot, warm_slot) = (Address::random(), U256::ONE, U256::from(2));
984
985 provider.sstore(address, cold_slot, U256::ONE)?;
986 assert_eq!(
987 provider.gas_used(),
988 22_200,
989 "TIP-1016 cold SSTORE should consume 22,200 regular gas including the retained Berlin cold-slot access charge"
990 );
991 assert_eq!(
992 provider.state_gas_used(),
993 230_000,
994 "TIP-1016 cold SSTORE should consume 230,000 state gas"
995 );
996
997 provider.sload(address, warm_slot)?;
998 let gas_before_warm_sstore = provider.gas_used();
999 let state_gas_before_warm_sstore = provider.state_gas_used();
1000
1001 provider.sstore(address, warm_slot, U256::ONE)?;
1002 assert_eq!(
1003 provider.gas_used() - gas_before_warm_sstore,
1004 20_100,
1005 "TIP-1016 warm zero-to-non-zero SSTORE should consume 20,100 regular gas after the slot is warmed by SLOAD"
1006 );
1007 assert_eq!(
1008 provider.state_gas_used() - state_gas_before_warm_sstore,
1009 230_000,
1010 "TIP-1016 warm zero-to-non-zero SSTORE should still consume 230,000 state gas"
1011 );
1012
1013 Ok(())
1014 }
1015
1016 #[test]
1017 fn test_t4_set_code_new_account_matches_tip1016_success_path() -> eyre::Result<()> {
1018 let mut evm = TestEvm::new_with_tip1016(TempoHardfork::T4);
1019 let gas_params = evm.ctx().cfg.gas_params.clone();
1020
1021 let code = Bytecode::new_raw(vec![0xef].into());
1022 let expected_state_gas =
1023 gas_params.create_state_gas() + gas_params.code_deposit_state_gas(code.len());
1024 let expected_regular_gas = gas_params.create_cost()
1025 + gas_params.code_deposit_cost(code.len())
1026 + gas_params.keccak256_cost(code.len().div_ceil(32));
1027 let mut provider = evm.provider_with_reservoir(expected_state_gas);
1028
1029 provider.set_code(Address::random(), code)?;
1030 assert_eq!(
1031 provider.gas_used(),
1032 expected_regular_gas,
1033 "TIP-1016 CREATE success path should charge CREATE + code deposit"
1034 );
1035 assert_eq!(
1036 provider.state_gas_used(),
1037 expected_state_gas,
1038 "set_code on a new account should charge CREATE state gas plus code deposit state gas"
1039 );
1040
1041 Ok(())
1042 }
1043
1044 #[test]
1045 fn test_sstore_t4_fork_sufficient_gas() -> eyre::Result<()> {
1046 let mut evm = TestEvm::new(TempoHardfork::T4);
1048 let mut provider = evm.provider_max_gas();
1049
1050 let address = Address::random();
1051 let key = U256::from(42);
1052 let value = U256::from(999);
1053
1054 provider.sstore(address, key, value)?;
1055 assert_eq!(provider.sload(address, key)?, value);
1056 Ok(())
1057 }
1058
1059 #[test]
1060 fn test_sload_t4_fork_sufficient_gas() -> eyre::Result<()> {
1061 let mut evm = TestEvm::new(TempoHardfork::T4);
1063 let mut provider = evm.provider_max_gas();
1064
1065 let address = Address::random();
1066 let key = U256::from(100);
1067 let value = U256::from(12345);
1068
1069 provider.sstore(address, key, value)?;
1070 assert_eq!(provider.sload(address, key)?, value);
1071 assert_eq!(provider.sload(address, key)?, value);
1073 Ok(())
1074 }
1075
1076 #[test]
1077 fn test_with_account_info_t4_fork() -> eyre::Result<()> {
1078 let mut evm = TestEvm::new(TempoHardfork::T4);
1080 let mut provider = evm.provider_max_gas();
1081
1082 let mut account_nonce = u64::MAX;
1083 provider.with_account_info(Address::random(), &mut |info| {
1084 account_nonce = info.nonce;
1085 assert!(info.balance.is_zero());
1086 })?;
1087
1088 assert_eq!(account_nonce, 0);
1089 Ok(())
1090 }
1091
1092 #[test]
1093 fn test_sstore_sload_cold_storage_t4() -> eyre::Result<()> {
1094 let mut evm = TestEvm::new(TempoHardfork::T4);
1096 let mut provider = evm.provider_max_gas();
1097
1098 let addr1 = Address::random();
1099 let addr2 = Address::random();
1100 let key1 = U256::from(1);
1101 let key2 = U256::from(2);
1102
1103 provider.sstore(addr1, key1, U256::from(100))?;
1105 provider.sstore(addr2, key2, U256::from(200))?;
1106
1107 provider.sstore(addr1, key1, U256::from(110))?;
1109 provider.sstore(addr2, key2, U256::from(210))?;
1110
1111 assert_eq!(provider.sload(addr1, key1)?, U256::from(110));
1112 assert_eq!(provider.sload(addr2, key2)?, U256::from(210));
1113 Ok(())
1114 }
1115
1116 #[test]
1117 fn test_sstore_insufficient_gas_for_cold_load_t4() -> eyre::Result<()> {
1118 let mut evm = TestEvm::new_with_tip1016(TempoHardfork::T4);
1121 let gas_params = evm.ctx().cfg.gas_params.clone();
1122
1123 let static_gas = gas_params.sstore_static_gas();
1124 let dynamic_gas = 25_000u64;
1125 let gas_limit = static_gas + dynamic_gas;
1126
1127 let mut provider = evm.provider_with_gas_limit(gas_limit, u64::MAX);
1129
1130 let initial_gas = provider.gas_used();
1131 let address = Address::random();
1132 let key = U256::from(42);
1133 let value = U256::from(999);
1134
1135 provider.sstore(address, key, value)?;
1136 let gas_after_sstore = provider.gas_used();
1137 assert!(gas_after_sstore > initial_gas, "sstore should consume gas");
1138
1139 assert_eq!(provider.sload(address, key)?, value);
1140 assert!(
1141 provider.gas_used() > gas_after_sstore,
1142 "sload should consume additional gas"
1143 );
1144 Ok(())
1145 }
1146
1147 #[test]
1148 fn test_sload_insufficient_gas_for_cold_load_t4() -> eyre::Result<()> {
1149 let mut evm = TestEvm::new(TempoHardfork::T4);
1151 let address = Address::random();
1152 let key = U256::from(100);
1153 let value = U256::from(555);
1154
1155 {
1157 let mut provider = evm.provider_max_gas();
1158 provider.sstore(address, key, value)?;
1159 }
1160
1161 let gas_params = evm.ctx().cfg.gas_params.clone();
1162 let warm_read_gas = gas_params.warm_storage_read_cost();
1163 let dynamic_gas = 2_100u64;
1164 let gas_limit = warm_read_gas + dynamic_gas;
1165
1166 let mut provider = evm.provider_with_gas_limit(gas_limit, 0);
1167 let initial_gas = provider.gas_used();
1168
1169 assert_eq!(provider.sload(address, key)?, value);
1170 assert!(
1171 provider.gas_used() > initial_gas,
1172 "sload should consume gas"
1173 );
1174 Ok(())
1175 }
1176
1177 #[test]
1178 fn test_with_account_info_insufficient_gas_for_cold_load_t4() -> eyre::Result<()> {
1179 let mut evm = TestEvm::new(TempoHardfork::T4);
1181 let gas_params = evm.ctx().cfg.gas_params.clone();
1182
1183 let static_gas = gas_params.sstore_static_gas();
1184 let gas_limit = static_gas + 10_000u64;
1185
1186 let mut provider = evm.provider_with_gas_limit(gas_limit, 0);
1187 let initial_gas = provider.gas_used();
1188
1189 let mut retrieved_nonce = u64::MAX;
1190 provider.with_account_info(Address::random(), &mut |info| {
1191 retrieved_nonce = info.nonce;
1192 })?;
1193
1194 assert_eq!(retrieved_nonce, 0);
1195 assert!(
1196 provider.gas_used() > initial_gas,
1197 "with_account_info should consume gas"
1198 );
1199 Ok(())
1200 }
1201
1202 #[test]
1203 fn test_multiple_sstore_insufficient_gas_scenarios_t4() -> eyre::Result<()> {
1204 let mut evm = TestEvm::new_with_tip1016(TempoHardfork::T4);
1206 let gas_params = evm.ctx().cfg.gas_params.clone();
1207
1208 let static_gas = gas_params.sstore_static_gas();
1209 let dynamic_gas = 20_000u64;
1210 let gas_per_sstore = static_gas + dynamic_gas;
1211 let gas_limit = gas_per_sstore * 3;
1212
1213 let mut provider = evm.provider_with_gas_limit(gas_limit, u64::MAX);
1214 let address = Address::random();
1215 let mut prev_gas = provider.gas_used();
1216
1217 for i in 0..3 {
1218 provider.sstore(address, U256::from(i), U256::from(i * 1000))?;
1219 let current_gas = provider.gas_used();
1220 assert!(
1221 current_gas > prev_gas,
1222 "each sstore should increase gas usage"
1223 );
1224 prev_gas = current_gas;
1225 }
1226
1227 for i in 0..3 {
1228 assert_eq!(
1229 provider.sload(address, U256::from(i))?,
1230 U256::from(i * 1000)
1231 );
1232 }
1233 Ok(())
1234 }
1235
1236 #[test]
1237 #[ignore = "TIP-1016 mismatch: 0->X->0 refund math does not net to GAS_WARM_ACCESS (100 gas) yet"]
1238 fn test_t4_sstore_restore_refund_matches_tip1016_spec() -> eyre::Result<()> {
1239 let mut evm = TestEvm::new(TempoHardfork::T4);
1240 let mut provider = evm.provider_with_reservoir(230_000);
1241
1242 let (address, slot) = (Address::random(), U256::ONE);
1243 provider.sstore(address, slot, U256::ONE)?;
1244 provider.sstore(address, slot, U256::ZERO)?;
1245 assert_eq!(provider.gas_refunded(), 247_800);
1246 let net_gas_after_refund =
1247 provider.gas_used() + provider.state_gas_used() - provider.gas_refunded() as u64;
1248 assert_eq!(
1249 net_gas_after_refund, 100,
1250 "TIP-1016 says 0->X->0 should net to GAS_WARM_ACCESS (100)"
1251 );
1252
1253 Ok(())
1254 }
1255}