1use parking_lot::RwLockReadGuard;
2use tracing::Span;
3
4use super::InMemoryTemporaryStorage;
5use super::RocksPermanentStorage;
6use super::StorageCache;
7#[cfg(feature = "dev")]
8use crate::eth::genesis::GenesisConfig;
9use crate::eth::primitives::Account;
10use crate::eth::primitives::Address;
11use crate::eth::primitives::Block;
12use crate::eth::primitives::BlockFilter;
13use crate::eth::primitives::BlockNumber;
14#[cfg(feature = "dev")]
15use crate::eth::primitives::Bytes;
16use crate::eth::primitives::ExecutionChanges;
17use crate::eth::primitives::Hash;
18use crate::eth::primitives::LogFilter;
19use crate::eth::primitives::LogMined;
20#[cfg(feature = "dev")]
21use crate::eth::primitives::Nonce;
22use crate::eth::primitives::PendingBlock;
23use crate::eth::primitives::PendingBlockHeader;
24use crate::eth::primitives::PointInTime;
25use crate::eth::primitives::Slot;
26use crate::eth::primitives::SlotIndex;
27#[cfg(feature = "dev")]
28use crate::eth::primitives::SlotValue;
29use crate::eth::primitives::StorageError;
30use crate::eth::primitives::TransactionExecution;
31use crate::eth::primitives::TransactionStage;
32#[cfg(feature = "dev")]
33use crate::eth::primitives::Wei;
34#[cfg(feature = "dev")]
35use crate::eth::primitives::test_accounts;
36use crate::eth::storage::ReadKind;
37use crate::eth::storage::TxCount;
38use crate::ext::not;
39use crate::infra::metrics;
40use crate::infra::metrics::timed;
41use crate::infra::tracing::SpanExt;
42
43mod label {
44 pub(super) const TEMP: &str = "temporary";
45 pub(super) const PERM: &str = "permanent";
46 pub(super) const CACHE: &str = "cache";
47}
48
49pub struct StratusStorage {
53 temp: InMemoryTemporaryStorage,
54 cache: StorageCache,
55 pub perm: RocksPermanentStorage,
56 transient_state_lock: parking_lot::RwLock<()>,
58 #[cfg(feature = "dev")]
59 perm_config: crate::eth::storage::permanent::PermanentStorageConfig,
60}
61
62impl StratusStorage {
63 pub fn new(
65 temp: InMemoryTemporaryStorage,
66 perm: RocksPermanentStorage,
67 cache: StorageCache,
68 #[cfg(feature = "dev")] perm_config: crate::eth::storage::permanent::PermanentStorageConfig,
69 ) -> Result<Self, StorageError> {
70 let this = Self {
71 temp,
72 cache,
73 perm,
74 transient_state_lock: parking_lot::RwLock::new(()),
75 #[cfg(feature = "dev")]
76 perm_config,
77 };
78
79 #[cfg(feature = "dev")]
81 if !this.has_genesis()? {
82 this.reset_to_genesis()?;
83 }
84
85 Ok(this)
86 }
87
88 pub fn has_genesis(&self) -> Result<bool, StorageError> {
90 self.perm.has_genesis()
91 }
92
93 pub fn clear_cache(&self) {
95 tracing::info!("clearing storage cache");
96 self.cache.clear();
97 }
98
99 #[cfg(test)]
100 pub fn new_test() -> Result<Self, StorageError> {
101 use tempfile::tempdir;
102
103 use super::cache::CacheConfig;
104
105 let temp = InMemoryTemporaryStorage::new(0.into());
106
107 let rocks_dir = tempdir().expect("Failed to create temporary directory for tests");
109 let rocks_path_prefix = rocks_dir.path().to_str().unwrap().to_string();
110
111 let perm = RocksPermanentStorage::new(Some(rocks_path_prefix.clone()), std::time::Duration::from_secs(240), None, true, None, 1024)
112 .expect("Failed to create RocksPermanentStorage for tests");
113
114 let cache = CacheConfig {
115 slot_cache_capacity: 100000,
116 account_cache_capacity: 20000,
117 account_history_cache_capacity: 20000,
118 slot_history_cache_capacity: 100000,
119 }
120 .init();
121
122 Self::new(
123 temp,
124 perm,
125 cache,
126 #[cfg(feature = "dev")]
127 super::permanent::PermanentStorageConfig {
128 rocks_path_prefix: Some(rocks_path_prefix),
129 rocks_shutdown_timeout: std::time::Duration::from_secs(240),
130 rocks_cache_size_multiplier: None,
131 rocks_disable_sync_write: false,
132 rocks_cf_size_metrics_interval: None,
133 genesis_file: crate::config::GenesisFileConfig::default(),
134 rocks_min_file_descriptors: 1024,
135 },
136 )
137 }
138
139 pub fn read_block_number_to_resume_import(&self) -> Result<BlockNumber, StorageError> {
140 #[cfg(feature = "tracing")]
141 let _span = tracing::info_span!("storage::read_block_number_to_resume_import").entered();
142
143 let number = self.read_pending_block_header().0.number;
144 tracing::info!(?number, "got block number to resume import");
145
146 Ok(number)
147 }
148
149 pub fn read_pending_block_header(&self) -> (PendingBlockHeader, TxCount) {
150 #[cfg(feature = "tracing")]
151 let _span = tracing::info_span!("storage::read_pending_block_number").entered();
152 tracing::debug!(storage = %label::TEMP, "reading pending block number");
153
154 timed(|| self.temp.read_pending_block_header()).with(|m| {
155 metrics::inc_storage_read_pending_block_number(m.elapsed, label::TEMP, true);
156 })
157 }
158
159 pub fn read_mined_block_number(&self) -> BlockNumber {
160 #[cfg(feature = "tracing")]
161 let _span = tracing::info_span!("storage::read_mined_block_number").entered();
162 tracing::debug!(storage = %label::PERM, "reading mined block number");
163
164 timed(|| self.perm.read_mined_block_number()).with(|m| {
165 metrics::inc_storage_read_mined_block_number(m.elapsed, label::PERM, true);
166 })
167 }
168
169 pub fn set_mined_block_number(&self, block_number: BlockNumber) {
171 #[cfg(feature = "tracing")]
172 let _span = tracing::info_span!("storage::set_mined_block_number", %block_number).entered();
173 tracing::debug!(storage = %label::PERM, %block_number, "setting mined block number");
174
175 timed(|| self.perm.set_mined_block_number(block_number)).with(|m| {
176 metrics::inc_storage_set_mined_block_number(m.elapsed, label::PERM, true);
177 });
178 }
179
180 pub fn save_accounts(&self, accounts: Vec<Account>) -> Result<(), StorageError> {
185 #[cfg(feature = "tracing")]
186 let _span = tracing::info_span!("storage::save_accounts").entered();
187
188 let mut missing_accounts = Vec::new();
190 for account in accounts {
191 let perm_account = self.perm.read_account(account.address, PointInTime::Mined)?;
192 if perm_account.is_none() {
193 missing_accounts.push(account);
194 }
195 }
196
197 tracing::debug!(storage = %label::PERM, accounts = ?missing_accounts, "saving initial accounts");
198 timed(|| self.perm.save_accounts(missing_accounts)).with(|m| {
199 metrics::inc_storage_save_accounts(m.elapsed, label::PERM, m.result.is_ok());
200 if let Err(ref e) = m.result {
201 tracing::error!(reason = ?e, "failed to save accounts");
202 }
203 })
204 }
205
206 fn _read_account_pending_cache(&self, address: Address) -> Option<Account> {
207 timed(|| self.cache.get_account(address)).with(|m| {
208 if m.result.is_some() {
209 tracing::debug!(storage = %label::CACHE, %address, "account found in cache");
210 metrics::inc_storage_read_account(m.elapsed, label::CACHE, PointInTime::Pending);
211 }
212 })
213 }
214
215 fn _read_account_latest_cache(&self, address: Address) -> Option<Account> {
216 timed(|| self.cache.get_account_latest(address)).with(|m| {
217 if m.result.is_some() {
218 tracing::debug!(storage = %label::CACHE, %address, "account found in cache");
219 metrics::inc_storage_read_account(m.elapsed, label::CACHE, PointInTime::Mined);
220 }
221 })
222 }
223
224 fn _read_account_temp(&self, address: Address, kind: ReadKind) -> Result<Option<Account>, StorageError> {
225 tracing::debug!(storage = %label::TEMP, %address, "reading account");
226 timed(|| self.temp.read_account(address, kind)).with(|m| {
227 if m.result.as_ref().is_ok_and(|opt| opt.is_some()) {
228 metrics::inc_storage_read_account(m.elapsed, label::TEMP, PointInTime::Pending);
229 }
230 if let Err(ref e) = m.result {
231 tracing::error!(reason = ?e, "failed to read account from temporary storage");
232 }
233 })
234 }
235
236 fn _read_account_perm(&self, address: Address, point_in_time: PointInTime) -> Result<Account, StorageError> {
237 tracing::debug!(storage = %label::PERM, %address, "reading account");
238 let account = timed(|| self.perm.read_account(address, point_in_time)).with(|m| {
239 if m.result.as_ref().is_ok_and(|opt| opt.is_some()) {
240 metrics::inc_storage_read_account(m.elapsed, label::PERM, point_in_time);
241 }
242 if let Err(ref e) = m.result {
243 tracing::error!(reason = ?e, "failed to read account from permanent storage");
244 }
245 })?;
246 Ok(match account {
247 Some(account) => {
248 tracing::debug!(storage = %label::PERM, %address, ?account, "account found in permanent storage");
249 account
250 }
251 None => {
252 tracing::debug!(storage = %label::PERM, %address, "account not found, assuming default value");
253 Account::new_empty(address)
254 }
255 })
256 }
257
258 fn _latest_is_valid(&self, point_in_time: PointInTime, kind: ReadKind) -> (bool, Option<RwLockReadGuard<'_, ()>>) {
260 if matches!(point_in_time, PointInTime::MinedPast(_)) {
261 return (false, None);
262 }
263 match kind {
264 ReadKind::Call((block_number, _)) => {
265 let guard = self.transient_state_lock.read();
266 let is_valid = block_number <= self.read_mined_block_number();
268 (is_valid, (is_valid).then_some(guard))
269 }
270 _ => (true, None),
271 }
272 }
273
274 pub fn read_account(&self, address: Address, mut point_in_time: PointInTime, kind: ReadKind) -> Result<Account, StorageError> {
275 #[cfg(feature = "tracing")]
276 let _span = tracing::debug_span!("storage::read_account", %address, %point_in_time).entered();
277
278 let (account, found_in_perm) = 'query: {
279 if point_in_time == PointInTime::Pending {
280 if matches!(kind, ReadKind::Transaction)
281 && let Some(account) = self._read_account_pending_cache(address)
282 {
283 return Ok(account);
284 }
285
286 if let Some(account) = self._read_account_temp(address, kind)? {
287 tracing::debug!(storage = %label::TEMP, %address, ?account, "account found in temporary storage");
288 break 'query (account, false);
289 }
290 }
291
292 let (is_valid, guard) = self._latest_is_valid(point_in_time, kind);
293 if is_valid {
294 if let Some(account) = self._read_account_latest_cache(address) {
295 return Ok(account);
296 }
297 } else if let ReadKind::Call((block_number, _)) = kind
298 && !matches!(point_in_time, PointInTime::MinedPast(_))
299 {
300 point_in_time = PointInTime::MinedPast(block_number);
301 }
302
303 let ret = (self._read_account_perm(address, point_in_time)?, true);
305 if let Some(inner) = guard {
306 RwLockReadGuard::unlock_fair(inner);
307 }
308 ret
309 };
310
311 match (point_in_time, found_in_perm) {
312 (PointInTime::Pending, true) => {
314 self.cache.cache_account_if_missing(account.clone());
315 self.cache.cache_account_latest_if_missing(address, account.clone());
316 }
317 (PointInTime::Pending, false) => {
318 self.cache.cache_account_if_missing(account.clone());
319 }
320 (PointInTime::Mined, _) => {
321 self.cache.cache_account_latest_if_missing(address, account.clone());
322 }
323 _ => {}
324 }
325 Ok(account)
326 }
327
328 fn _read_slot_pending_cache(&self, address: Address, index: SlotIndex) -> Option<Slot> {
329 timed(|| self.cache.get_slot(address, index)).with(|m| {
330 if m.result.is_some() {
331 tracing::debug!(storage = %label::CACHE, %address, %index, "slot found in cache");
332 metrics::inc_storage_read_slot(m.elapsed, label::CACHE, PointInTime::Pending);
333 }
334 })
335 }
336
337 fn _read_slot_latest_cache(&self, address: Address, index: SlotIndex) -> Option<Slot> {
338 timed(|| self.cache.get_slot_latest(address, index)).with(|m| {
339 if m.result.is_some() {
340 tracing::debug!(storage = %label::CACHE, %address, %index, "slot found in cache");
341 metrics::inc_storage_read_slot(m.elapsed, label::CACHE, PointInTime::Mined);
342 }
343 })
344 }
345
346 fn _read_slot_temp(&self, address: Address, index: SlotIndex, kind: ReadKind) -> Result<Option<Slot>, StorageError> {
347 tracing::debug!(storage = %label::TEMP, %address, %index, "reading slot");
348 timed(|| self.temp.read_slot(address, index, kind)).with(|m| {
349 if m.result.as_ref().is_ok_and(|opt| opt.is_some()) {
350 metrics::inc_storage_read_slot(m.elapsed, label::TEMP, PointInTime::Pending);
351 }
352 if let Err(ref e) = m.result {
353 tracing::error!(reason = ?e, "failed to read slot from temporary storage");
354 }
355 })
356 }
357
358 fn _read_slot_perm(&self, address: Address, index: SlotIndex, point_in_time: PointInTime) -> Result<Slot, StorageError> {
359 tracing::debug!(storage = %label::PERM, %address, %index, %point_in_time, "reading slot");
360 let slot = timed(|| self.perm.read_slot(address, index, point_in_time)).with(|m| {
361 if m.result.as_ref().is_ok_and(|opt| opt.is_some()) {
362 metrics::inc_storage_read_slot(m.elapsed, label::PERM, point_in_time);
363 }
364 if let Err(ref e) = m.result {
365 tracing::error!(reason = ?e, "failed to read slot from permanent storage");
366 }
367 })?;
368 Ok(match slot {
369 Some(slot) => {
370 tracing::debug!(storage = %label::PERM, %address, %index, value = %slot.value, "slot found in permanent storage");
371 slot
372 }
373 None => {
374 tracing::debug!(storage = %label::PERM, %address, %index, "slot not found, assuming default value");
375 Slot::new_empty(index)
376 }
377 })
378 }
379
380 pub fn read_slot(&self, address: Address, index: SlotIndex, mut point_in_time: PointInTime, kind: ReadKind) -> Result<Slot, StorageError> {
381 #[cfg(feature = "tracing")]
382 let _span = tracing::debug_span!("storage::read_slot", %address, %index, %point_in_time).entered();
383
384 let (slot, found_in_perm) = 'query: {
385 if point_in_time == PointInTime::Pending {
386 if matches!(kind, ReadKind::Transaction)
388 && let Some(slot) = self._read_slot_pending_cache(address, index)
389 {
390 return Ok(slot);
391 }
392
393 if let Some(slot) = self._read_slot_temp(address, index, kind)? {
394 tracing::debug!(storage = %label::TEMP, %address, %index, value = %slot.value, "slot found in temporary storage");
395 break 'query (slot, false);
396 }
397 }
398
399 let (is_valid, guard) = self._latest_is_valid(point_in_time, kind);
400 if is_valid {
401 if let Some(slot) = self._read_slot_latest_cache(address, index) {
402 return Ok(slot);
403 }
404 } else if let ReadKind::Call((block_number, _)) = kind
405 && !matches!(point_in_time, PointInTime::MinedPast(_))
406 {
407 point_in_time = PointInTime::MinedPast(block_number);
408 }
409
410 let ret = (self._read_slot_perm(address, index, point_in_time)?, true);
412 if let Some(inner) = guard {
413 RwLockReadGuard::unlock_fair(inner);
414 }
415 ret
416 };
417
418 match (point_in_time, found_in_perm) {
419 (PointInTime::Pending, true) => {
421 self.cache.cache_slot_if_missing(address, slot);
422 self.cache.cache_slot_latest_if_missing(address, slot);
423 }
424 (PointInTime::Pending, false) => {
425 self.cache.cache_slot_if_missing(address, slot);
426 }
427 (PointInTime::Mined, _) => {
428 self.cache.cache_slot_latest_if_missing(address, slot);
429 }
430 _ => {}
431 }
432 Ok(slot)
433 }
434
435 pub fn save_execution(&self, tx: TransactionExecution, is_local: bool) -> Result<(), StorageError> {
440 let changes = tx.result.execution.changes.clone();
441
442 #[cfg(feature = "tracing")]
443 let _span = tracing::info_span!("storage::save_execution", tx_hash = %tx.input.hash).entered();
444 tracing::debug!(storage = %label::TEMP, tx_hash = %tx.input.hash, "saving execution");
445
446 timed(|| self.temp.save_pending_execution(tx, is_local))
447 .with(|m| {
448 metrics::inc_storage_save_execution(m.elapsed, label::TEMP, m.result.is_ok());
449 match &m.result {
450 Err(StorageError::EvmInputMismatch { .. }) => {
451 tracing::warn!("failed to save execution due to mismatch, will retry");
452 }
453 Err(e) => tracing::error!(reason = ?e, "failed to save execution"),
454 _ => (),
455 }
456 })
457 .inspect(|_| self.cache.cache_account_and_slots_from_changes(changes))
458 }
459
460 pub fn pending_transactions(&self) -> Vec<TransactionExecution> {
462 self.temp.read_pending_executions()
463 }
464
465 pub fn finish_pending_block(&self) -> Result<(PendingBlock, ExecutionChanges), StorageError> {
466 #[cfg(feature = "tracing")]
467 let _span = tracing::info_span!("storage::finish_pending_block", block_number = tracing::field::Empty).entered();
468 tracing::debug!(storage = %label::TEMP, "finishing pending block");
469
470 let result = timed(|| self.temp.finish_pending_block()).with(|m| {
471 metrics::inc_storage_finish_pending_block(m.elapsed, label::TEMP, m.result.is_ok());
472 if let Err(ref e) = m.result {
473 tracing::error!(reason = ?e, "failed to finish pending block");
474 }
475 });
476
477 if let Ok((ref block, _)) = result {
478 Span::with(|s| s.rec_str("block_number", &block.header.number));
479 }
480
481 result
482 }
483
484 pub fn save_genesis_block(&self, block: Block, accounts: Vec<Account>, changes: ExecutionChanges) -> Result<(), StorageError> {
485 let block_number = block.number();
486
487 #[cfg(feature = "tracing")]
488 let _span = tracing::info_span!("storage::save_genesis_block", block_number = %block_number).entered();
489 tracing::debug!(storage = %label::PERM, "saving genesis block");
490
491 timed(|| self.perm.save_genesis_block(block, accounts, changes)).with(|m| {
492 metrics::inc_storage_save_block(m.elapsed, label::PERM, "genesis", "genesis", m.result.is_ok());
493 if let Err(ref e) = m.result {
494 tracing::error!(reason = ?e, "failed to save genesis block");
495 }
496 })
497 }
498
499 pub fn save_block(&self, block: Block, changes: ExecutionChanges) -> Result<(), StorageError> {
500 let block_number = block.number();
501
502 #[cfg(feature = "tracing")]
503 let _span = tracing::info_span!("storage::save_block", block_number = %block.number()).entered();
504 tracing::debug!(storage = %label::PERM, block_number = %block_number, transactions_len = %block.transactions.len(), "saving block");
505
506 let mined_number = self.read_mined_block_number();
508 if not(block_number.is_zero()) && block_number != mined_number.next_block_number() {
509 tracing::error!(%block_number, %mined_number, "failed to save block because mismatch with mined block number");
510 return Err(StorageError::MinedNumberConflict {
511 new: block_number,
512 mined: mined_number,
513 });
514 }
515
516 let pending_header = self.read_pending_block_header();
518 if block_number >= pending_header.0.number {
519 tracing::error!(%block_number, pending_number = %pending_header.0.number, "failed to save block because mismatch with pending block number");
520 return Err(StorageError::PendingNumberConflict {
521 new: block_number,
522 pending: pending_header.0.number,
523 });
524 }
525
526 let existing_block = self.read_block(BlockFilter::Number(block_number))?;
528 if existing_block.is_some() {
529 tracing::error!(%block_number, %mined_number, "failed to save block because block with the same number already exists in the permanent storage");
530 return Err(StorageError::BlockConflict { number: block_number });
531 }
532
533 let (label_size_by_tx, label_size_by_gas) = (block.label_size_by_transactions(), block.label_size_by_gas());
535 timed(|| {
536 let guard = self.transient_state_lock.write();
537 self.perm.save_block(block, changes.clone())?;
538 self.cache.cache_account_and_slots_latest_from_changes(changes);
539 drop(guard);
540 Ok(())
541 })
542 .with(|m| {
543 metrics::inc_storage_save_block(m.elapsed, label::PERM, label_size_by_tx, label_size_by_gas, m.result.is_ok());
544 if let Err(ref e) = m.result {
545 tracing::error!(reason = ?e, %block_number, "failed to save block");
546 }
547 })?;
548
549 self.set_mined_block_number(block_number);
550
551 Ok(())
552 }
553
554 pub fn read_block(&self, filter: BlockFilter) -> Result<Option<Block>, StorageError> {
555 #[cfg(feature = "tracing")]
556 let _span = tracing::info_span!("storage::read_block", %filter).entered();
557 tracing::debug!(storage = %label::PERM, ?filter, "reading block");
558
559 timed(|| self.perm.read_block(filter)).with(|m| {
560 metrics::inc_storage_read_block(m.elapsed, label::PERM, m.result.is_ok());
561 if let Err(ref e) = m.result {
562 tracing::error!(reason = ?e, "failed to read block");
563 }
564 })
565 }
566
567 pub fn read_block_with_changes(&self, filter: BlockFilter) -> Result<Option<(Block, ExecutionChanges)>, StorageError> {
568 #[cfg(feature = "tracing")]
569 let _span = tracing::info_span!("storage::read_block_with_changes", %filter).entered();
570 tracing::debug!(storage = %label::PERM, ?filter, "reading block with changes");
571
572 timed(|| self.perm.read_block_with_changes(filter)).with(|m| {
573 metrics::inc_storage_read_block_with_changes(m.elapsed, label::PERM, m.result.is_ok());
574 if let Err(ref e) = m.result {
575 tracing::error!(reason = ?e, "failed to read block with changes");
576 }
577 })
578 }
579
580 pub fn read_transaction(&self, tx_hash: Hash) -> Result<Option<TransactionStage>, StorageError> {
581 #[cfg(feature = "tracing")]
582 let _span = tracing::info_span!("storage::read_transaction", %tx_hash).entered();
583
584 tracing::debug!(storage = %label::TEMP, %tx_hash, "reading transaction");
586 let temp_tx = timed(|| self.temp.read_pending_execution(tx_hash)).with(|m| {
587 metrics::inc_storage_read_transaction(m.elapsed, label::TEMP, m.result.is_ok());
588 if let Err(ref e) = m.result {
589 tracing::error!(reason = ?e, "failed to read transaction from temporary storage");
590 }
591 })?;
592 if let Some(tx_temp) = temp_tx {
593 return Ok(Some(TransactionStage::new_executed(tx_temp)));
594 }
595
596 tracing::debug!(storage = %label::PERM, %tx_hash, "reading transaction");
598 let perm_tx = timed(|| self.perm.read_transaction(tx_hash)).with(|m| {
599 metrics::inc_storage_read_transaction(m.elapsed, label::PERM, m.result.is_ok());
600 if let Err(ref e) = m.result {
601 tracing::error!(reason = ?e, "failed to read transaction from permanent storage");
602 }
603 })?;
604 match perm_tx {
605 Some(tx) => Ok(Some(TransactionStage::new_mined(tx))),
606 None => Ok(None),
607 }
608 }
609
610 pub fn read_logs(&self, filter: &LogFilter) -> Result<Vec<LogMined>, StorageError> {
611 #[cfg(feature = "tracing")]
612 let _span = tracing::info_span!("storage::read_logs", ?filter).entered();
613 tracing::debug!(storage = %label::PERM, ?filter, "reading logs");
614
615 timed(|| self.perm.read_logs(filter)).with(|m| {
616 metrics::inc_storage_read_logs(m.elapsed, label::PERM, m.result.is_ok());
617 if let Err(ref e) = m.result {
618 tracing::error!(reason = ?e, "failed to read logs");
619 }
620 })
621 }
622
623 #[cfg(feature = "dev")]
628 pub fn set_storage_at(&self, address: Address, index: SlotIndex, value: SlotValue) -> Result<(), StorageError> {
629 let slot = Slot::new(index, value);
631 self.cache.clear();
632
633 self.perm.save_slot(address, slot)?;
635
636 self.temp.save_slot(address, slot)?;
638
639 Ok(())
640 }
641
642 #[cfg(feature = "dev")]
643 pub fn set_nonce(&self, address: Address, nonce: Nonce) -> Result<(), StorageError> {
644 self.cache.clear();
645
646 self.perm.save_account_nonce(address, nonce)?;
648
649 self.temp.save_account_nonce(address, nonce)?;
651
652 Ok(())
653 }
654
655 #[cfg(feature = "dev")]
656 pub fn set_balance(&self, address: Address, balance: Wei) -> Result<(), StorageError> {
657 self.cache.clear();
658
659 self.perm.save_account_balance(address, balance)?;
661
662 self.temp.save_account_balance(address, balance)?;
664
665 Ok(())
666 }
667
668 #[cfg(feature = "dev")]
669 pub fn set_code(&self, address: Address, code: Bytes) -> Result<(), StorageError> {
670 self.cache.clear();
671 self.perm.save_account_code(address, code.clone())?;
673
674 self.temp.save_account_code(address, code)?;
676
677 Ok(())
678 }
679
680 #[cfg(feature = "dev")]
685 pub fn reset_to_genesis(&self) -> Result<(), StorageError> {
689 tracing::info!("resetting storage to genesis state");
690
691 self.cache.clear();
692
693 #[cfg(feature = "tracing")]
694 let _span = tracing::info_span!("storage::reset").entered();
695
696 tracing::debug!(storage = %label::PERM, "resetting permanent storage");
698 timed(|| self.perm.reset()).with(|m| {
699 metrics::inc_storage_reset(m.elapsed, label::PERM, m.result.is_ok());
700 if let Err(ref e) = m.result {
701 tracing::error!(reason = ?e, "failed to reset permanent storage");
702 }
703 })?;
704
705 tracing::debug!(storage = %label::TEMP, "reseting temporary storage");
707 timed(|| self.temp.reset()).with(|m| {
708 metrics::inc_storage_reset(m.elapsed, label::TEMP, m.result.is_ok());
709 if let Err(ref e) = m.result {
710 tracing::error!(reason = ?e, "failed to reset temporary storage");
711 }
712 })?;
713
714 let genesis_block = if let Some(genesis_path) = &self.perm_config.genesis_file.genesis_path {
716 if std::path::Path::new(genesis_path).exists() {
717 match GenesisConfig::load_from_file(genesis_path) {
718 Ok(genesis_config) => match genesis_config.to_genesis_block() {
719 Ok(block) => {
720 tracing::info!("using genesis block from file: {:?}", genesis_path);
721 block
722 }
723 Err(e) => {
724 tracing::error!("failed to create genesis block from file: {:?}", e);
725 Block::genesis()
726 }
727 },
728 Err(e) => {
729 tracing::error!("failed to load genesis file: {:?}", e);
730 Block::genesis()
731 }
732 }
733 } else {
734 tracing::error!("genesis file not found at: {:?}", genesis_path);
735 Block::genesis()
736 }
737 } else {
738 tracing::info!("using default genesis block");
739 Block::genesis()
740 };
741 let (genesis_accounts, genesis_slots) = if let Some(genesis_path) = &self.perm_config.genesis_file.genesis_path {
744 if std::path::Path::new(genesis_path).exists() {
745 tracing::info!("found genesis file at: {:?}", genesis_path);
746 match GenesisConfig::load_from_file(genesis_path) {
747 Ok(genesis) => match genesis.to_stratus_accounts_and_slots() {
748 Ok((accounts, slots)) => {
749 tracing::info!("loaded {} accounts from genesis.json", accounts.len());
750 if !slots.is_empty() {
751 tracing::info!("loaded {} storage slots from genesis.json", slots.len());
752 }
753 (accounts, slots)
754 }
755 Err(e) => {
756 tracing::error!("failed to convert genesis accounts: {:?}", e);
757 (test_accounts(), vec![])
759 }
760 },
761 Err(e) => {
762 tracing::error!("failed to load genesis file: {:?}", e);
763 (test_accounts(), vec![])
765 }
766 }
767 } else {
768 tracing::error!("genesis file not found at: {:?}", genesis_path);
769 (test_accounts(), vec![])
771 }
772 } else {
773 match GenesisConfig::default().to_stratus_accounts_and_slots() {
775 Ok((accounts, slots)) => {
776 tracing::info!("using default genesis configuration with {} accounts", accounts.len());
777 (accounts, slots)
778 }
779 Err(e) => {
780 tracing::error!("failed to convert default genesis accounts: {:?}", e);
781 (test_accounts(), vec![])
783 }
784 }
785 };
786 self.save_block(genesis_block, ExecutionChanges::default())?;
788
789 self.save_accounts(genesis_accounts)?;
791
792 if !genesis_slots.is_empty() {
794 tracing::info!("saving {} storage slots from genesis", genesis_slots.len());
795 for (address, slot) in genesis_slots {
796 self.perm.save_slot(address, slot)?;
797 }
798 }
799
800 self.set_mined_block_number(BlockNumber::ZERO);
802
803 Ok(())
804 }
805
806 pub fn translate_to_point_in_time(&self, block_filter: BlockFilter) -> Result<PointInTime, StorageError> {
812 match block_filter {
813 BlockFilter::Pending => Ok(PointInTime::Pending),
814 BlockFilter::Latest => Ok(PointInTime::Mined),
815 BlockFilter::Earliest => Ok(PointInTime::MinedPast(BlockNumber::ZERO)),
816 BlockFilter::Number(number) => Ok(PointInTime::MinedPast(number)),
817 BlockFilter::Hash(_) => match self.read_block(block_filter)? {
818 Some(block) => Ok(PointInTime::MinedPast(block.header.number)),
819 None => Err(StorageError::BlockNotFound { filter: block_filter }),
820 },
821 }
822 }
823}