tempo_precompiles/storage/
hashmap.rs1use alloy::primitives::{Address, LogData, U256};
2use revm::{
3 context::journaled_state::JournalCheckpoint,
4 state::{AccountInfo, Bytecode},
5};
6use std::collections::HashMap;
7use tempo_chainspec::hardfork::TempoHardfork;
8
9use crate::{error::TempoPrecompileError, storage::PrecompileStorageProvider};
10
11pub struct HashMapStorageProvider {
15 internals: HashMap<(Address, U256), U256>,
16 transient: HashMap<(Address, U256), U256>,
17 accounts: HashMap<Address, AccountInfo>,
18 chain_id: u64,
19 timestamp: U256,
20 beneficiary: Address,
21 block_number: u64,
22 spec: TempoHardfork,
23 is_static: bool,
24 snapshots: Vec<Snapshot>,
25
26 pub events: HashMap<Address, Vec<LogData>>,
28}
29
30struct Snapshot {
34 internals: HashMap<(Address, U256), U256>,
35 events: HashMap<Address, Vec<LogData>>,
36}
37
38impl HashMapStorageProvider {
39 pub fn new(chain_id: u64) -> Self {
41 Self::new_with_spec(chain_id, TempoHardfork::default())
42 }
43
44 pub fn new_with_spec(chain_id: u64, spec: TempoHardfork) -> Self {
46 Self {
47 internals: HashMap::new(),
48 transient: HashMap::new(),
49 accounts: HashMap::new(),
50 events: HashMap::new(),
51 snapshots: Vec::new(),
52 chain_id,
53 #[expect(clippy::disallowed_methods)]
54 timestamp: U256::from(
55 std::time::SystemTime::now()
56 .duration_since(std::time::UNIX_EPOCH)
57 .unwrap()
58 .as_secs(),
59 ),
60 beneficiary: Address::ZERO,
61 block_number: 0,
62 spec,
63 is_static: false,
64 }
65 }
66
67 pub fn with_spec(mut self, spec: TempoHardfork) -> Self {
69 self.spec = spec;
70 self
71 }
72}
73
74impl PrecompileStorageProvider for HashMapStorageProvider {
75 fn chain_id(&self) -> u64 {
76 self.chain_id
77 }
78
79 fn timestamp(&self) -> U256 {
80 self.timestamp
81 }
82
83 fn beneficiary(&self) -> Address {
84 self.beneficiary
85 }
86
87 fn block_number(&self) -> u64 {
88 self.block_number
89 }
90
91 fn set_code(&mut self, address: Address, code: Bytecode) -> Result<(), TempoPrecompileError> {
92 let account = self.accounts.entry(address).or_default();
93 account.code_hash = code.hash_slow();
94 account.code = Some(code);
95 Ok(())
96 }
97
98 fn with_account_info(
99 &mut self,
100 address: Address,
101 f: &mut dyn FnMut(&AccountInfo),
102 ) -> Result<(), TempoPrecompileError> {
103 let account = self.accounts.entry(address).or_default();
104 f(&*account);
105 Ok(())
106 }
107
108 fn sstore(
109 &mut self,
110 address: Address,
111 key: U256,
112 value: U256,
113 ) -> Result<(), TempoPrecompileError> {
114 self.internals.insert((address, key), value);
115 Ok(())
116 }
117
118 fn tstore(
119 &mut self,
120 address: Address,
121 key: U256,
122 value: U256,
123 ) -> Result<(), TempoPrecompileError> {
124 self.transient.insert((address, key), value);
125 Ok(())
126 }
127
128 fn emit_event(&mut self, address: Address, event: LogData) -> Result<(), TempoPrecompileError> {
129 self.events.entry(address).or_default().push(event);
130 Ok(())
131 }
132
133 fn sload(&mut self, address: Address, key: U256) -> Result<U256, TempoPrecompileError> {
134 Ok(self
135 .internals
136 .get(&(address, key))
137 .copied()
138 .unwrap_or(U256::ZERO))
139 }
140
141 fn tload(&mut self, address: Address, key: U256) -> Result<U256, TempoPrecompileError> {
142 Ok(self
143 .transient
144 .get(&(address, key))
145 .copied()
146 .unwrap_or(U256::ZERO))
147 }
148
149 fn deduct_gas(&mut self, _gas: u64) -> Result<(), TempoPrecompileError> {
150 Ok(())
151 }
152
153 fn refund_gas(&mut self, _gas: i64) {
154 }
156
157 fn gas_used(&self) -> u64 {
158 0
159 }
160
161 fn gas_refunded(&self) -> i64 {
162 0
163 }
164
165 fn spec(&self) -> TempoHardfork {
166 self.spec
167 }
168
169 fn is_static(&self) -> bool {
170 self.is_static
171 }
172
173 fn checkpoint(&mut self) -> JournalCheckpoint {
174 let idx = self.snapshots.len();
175 self.snapshots.push(Snapshot {
176 internals: self.internals.clone(),
177 events: self.events.clone(),
178 });
179 JournalCheckpoint {
180 log_i: 0,
181 journal_i: idx,
182 selfdestructed_i: 0,
183 }
184 }
185
186 fn checkpoint_commit(&mut self, checkpoint: JournalCheckpoint) {
187 assert_eq!(
188 checkpoint.journal_i,
189 self.snapshots.len() - 1,
190 "out-of-order checkpoint commit (expected top of stack)"
191 );
192 self.snapshots.pop();
193 }
194
195 fn checkpoint_revert(&mut self, checkpoint: JournalCheckpoint) {
196 assert_eq!(
197 checkpoint.journal_i,
198 self.snapshots.len() - 1,
199 "out-of-order checkpoint revert (expected top of stack)"
200 );
201 if let Some(snapshot) = self.snapshots.drain(checkpoint.journal_i..).next() {
202 self.internals = snapshot.internals;
203 self.events = snapshot.events;
204 }
205 }
206}
207
208#[cfg(any(test, feature = "test-utils"))]
209impl HashMapStorageProvider {
210 pub fn get_account_info(&self, address: Address) -> Option<&AccountInfo> {
212 self.accounts.get(&address)
213 }
214
215 pub fn get_events(&self, address: Address) -> &Vec<LogData> {
217 static EMPTY: Vec<LogData> = Vec::new();
218 self.events.get(&address).unwrap_or(&EMPTY)
219 }
220
221 pub fn set_nonce(&mut self, address: Address, nonce: u64) {
223 let account = self.accounts.entry(address).or_default();
224 account.nonce = nonce;
225 }
226
227 pub fn set_timestamp(&mut self, timestamp: U256) {
229 self.timestamp = timestamp;
230 }
231
232 pub fn set_beneficiary(&mut self, beneficiary: Address) {
234 self.beneficiary = beneficiary;
235 }
236
237 pub fn set_block_number(&mut self, block_number: u64) {
239 self.block_number = block_number;
240 }
241
242 pub fn set_spec(&mut self, spec: TempoHardfork) {
244 self.spec = spec;
245 }
246
247 pub fn clear_transient(&mut self) {
249 self.transient.clear();
250 }
251
252 pub fn clear_events(&mut self, address: Address) {
254 let _ = self
255 .events
256 .entry(address)
257 .and_modify(|v| v.clear())
258 .or_default();
259 }
260}