tempo_revm/
gas_credits.rs1use crate::{
4 TempoInvalidTransaction,
5 evm::{TempoContext, TempoEvm},
6};
7use alloy_evm::Database;
8use alloy_primitives::{Address, U256};
9use revm::{
10 context::{Host as _, JournalTr, result::EVMError},
11 context_interface::cfg::GasParams,
12 interpreter::{
13 Gas, InstructionContext, InstructionResult, SStoreResult, StateLoad,
14 gas::GasTracker,
15 instructions::host::{sstore_default_gas_accounting, sstore_with_gas_accounting},
16 interpreter::EthInterpreter,
17 },
18};
19use tempo_chainspec::constants::gas::STORAGE_CREDIT_VALUE;
20use tempo_precompiles::{
21 STORAGE_CREDITS_ADDRESS,
22 storage::FromWord,
23 storage_credits::{StorageCreditsBackend, TransientState, sstore_storage_credits},
24};
25
26pub fn apply_refund<DB: Database, I>(
34 evm: &mut TempoEvm<DB, I>,
35 gas: &mut Gas,
36) -> Result<(), EVMError<DB::Error, TempoInvalidTransaction>> {
37 if !evm.cfg.spec.is_t7() {
38 return Ok(());
39 }
40
41 let journal = &mut evm.inner.ctx.journaled_state;
42
43 let Some(slots) = journal.transient_storage.remove(&STORAGE_CREDITS_ADDRESS) else {
46 return Ok(());
47 };
48
49 let mut refunds = 0i64;
50 for (key, word) in slots {
51 let transient_state =
52 TransientState::try_from(word).map_err(|err| EVMError::Custom(err.to_string()))?;
53 let pending = transient_state.pending_refunds;
54 if pending == 0 {
55 continue;
56 }
57
58 let old_word = journal.sload(STORAGE_CREDITS_ADDRESS, key)?.data;
60 let mut balance =
61 u64::from_word(old_word).map_err(|err| EVMError::Custom(err.to_string()))?;
62 let settled = pending.min(balance);
63
64 if settled == 0 {
65 continue;
66 }
67
68 balance -= settled;
70 refunds += settled as i64;
71
72 let new_word = U256::from(balance);
73 debug_assert_ne!(new_word, old_word);
74
75 journal.sstore(STORAGE_CREDITS_ADDRESS, key, new_word)?;
76 }
77
78 gas.record_refund(refunds.saturating_mul(STORAGE_CREDIT_VALUE as i64));
80
81 Ok(())
82}
83
84struct StorageCreditsContext<'a, DB: Database> {
89 context: &'a mut TempoContext<DB>,
90 gas_tracker: &'a mut GasTracker,
91}
92
93impl<DB: Database> StorageCreditsBackend for StorageCreditsContext<'_, DB> {
94 type Error = InstructionResult;
95
96 #[inline]
97 fn gas_params(&self) -> &GasParams {
98 self.context.gas_params()
99 }
100
101 #[inline]
102 fn gas_tracker(&mut self) -> &mut GasTracker {
103 self.gas_tracker
104 }
105
106 #[inline]
107 fn sload(
108 &mut self,
109 address: Address,
110 key: U256,
111 skip_cold_load: bool,
112 ) -> Result<StateLoad<U256>, Self::Error> {
113 self.context
114 .load_account_info_skip_cold_load(address, false, false)?;
115 Ok(self
116 .context
117 .sload_skip_cold_load(address, key, skip_cold_load)?)
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 Ok(self
129 .context
130 .sstore_skip_cold_load(address, key, value, skip_cold_load)?)
131 }
132
133 #[inline]
134 fn tload(&mut self, address: Address, key: U256) -> U256 {
135 self.context.tload(address, key)
136 }
137
138 #[inline]
139 fn tstore(&mut self, address: Address, key: U256, value: U256) {
140 self.context.tstore(address, key, value);
141 }
142}
143
144pub(crate) fn sstore<DB: Database>(
146 context: InstructionContext<'_, TempoContext<DB>, EthInterpreter>,
147) -> Result<(), InstructionResult> {
148 sstore_with_gas_accounting(context, |context, owner, values| {
149 {
150 let InstructionContext { interpreter, host } = context;
151 sstore_storage_credits(
152 &mut StorageCreditsContext {
153 context: host,
154 gas_tracker: interpreter.gas.tracker_mut(),
155 },
156 owner,
157 values,
158 )?;
159 }
160
161 sstore_default_gas_accounting(context, owner, values)
164 })
165}