1#[cfg(feature = "rocksdb")]
2use crate::backend::rocksdb::RocksDBBackend;
3use crate::{
4 STORE_METADATA_FILENAME, STORE_SCHEMA_VERSION,
5 api::{
6 StorageBackend, StorageReadView, StorageWriteBatch,
7 tables::{
8 ACCOUNT_CODE_METADATA, ACCOUNT_CODES, ACCOUNT_FLATKEYVALUE, ACCOUNT_TRIE_NODES,
9 BAD_BLOCKS, BLOCK_ACCESS_LISTS, BLOCK_NUMBERS, BODIES, CANONICAL_BLOCK_HASHES,
10 CHAIN_DATA, EXECUTION_WITNESSES, FULLSYNC_HEADERS, HEADERS, INVALID_CHAINS,
11 MISC_VALUES, PENDING_BLOCKS, RECEIPTS_V2, SNAP_STATE, STATE_HISTORY,
12 STORAGE_FLATKEYVALUE, STORAGE_TRIE_NODES, TRANSACTION_LOCATIONS,
13 },
14 },
15 apply_prefix,
16 backend::in_memory::InMemoryBackend,
17 block_data_buffer::BlockDataBuffer,
18 error::StoreError,
19 journal::{FlatDiff, JournalEntry},
20 layering::{Overlay, TrieLayerCache, TrieWrapper},
21 rlp::{BlockBodyRLP, BlockHeaderRLP, BlockRLP},
22 trie::{BackendTrieDB, BackendTrieDBLocked, classify_trie_key},
23 utils::{ChainDataIndex, SnapStateIndex},
24};
25
26use ethrex_common::{
27 Address, H256, U256,
28 types::{
29 AccountInfo, AccountState, AccountUpdate, Block, BlockBody, BlockHash, BlockHeader,
30 BlockNumber, ChainConfig, Code, CodeMetadata, ForkId, Genesis, GenesisAccount, Index,
31 Receipt, Transaction,
32 block_access_list::BlockAccessList,
33 block_execution_witness::{ExecutionWitness, RpcExecutionWitness},
34 },
35 utils::keccak,
36};
37use ethrex_crypto::{NativeCrypto, keccak::keccak_hash};
38use ethrex_rlp::{
39 decode::{RLPDecode, decode_bytes},
40 encode::RLPEncode,
41};
42use ethrex_trie::{EMPTY_TRIE_HASH, Nibbles, Trie, TrieLogger, TrieNode, TrieWitness};
43use ethrex_trie::{Node, NodeRLP};
44use lru::LruCache;
45use rayon::prelude::*;
46use rustc_hash::FxBuildHasher;
47use serde::{Deserialize, Serialize};
48use std::{
49 collections::{BTreeMap, HashMap, HashSet, hash_map::Entry},
50 fmt::Debug,
51 io::Write,
52 path::{Path, PathBuf},
53 sync::{
54 Arc, Condvar, Mutex, RwLock,
55 atomic::{AtomicUsize, Ordering},
56 mpsc::{SyncSender, TryRecvError, sync_channel},
57 },
58 thread::JoinHandle,
59};
60use tracing::{debug, error, info, warn};
61
62pub const MAX_WITNESSES: u64 = 128;
64
65pub const DB_COMMIT_THRESHOLD: usize = 128;
69const IN_MEMORY_COMMIT_THRESHOLD: usize = 10000;
70
71pub const BATCH_COMMIT_THRESHOLD: usize = 4;
77
78pub const DEFAULT_ROCKSDB_BLOCK_CACHE_SIZE_BYTES: usize = 12 * 1024 * 1024 * 1024;
88
89#[derive(Debug, Clone, Copy)]
94pub struct StoreConfig {
95 pub rocksdb_block_cache_size: usize,
100 pub persist_channel_capacity: usize,
105}
106
107impl Default for StoreConfig {
108 fn default() -> Self {
109 Self {
110 rocksdb_block_cache_size: DEFAULT_ROCKSDB_BLOCK_CACHE_SIZE_BYTES,
111 persist_channel_capacity: DEFAULT_PERSIST_CHANNEL_CAPACITY,
112 }
113 }
114}
115
116#[derive(Debug, PartialEq)]
118enum FKVGeneratorControlMessage {
119 Stop,
120 Continue,
121}
122
123const CODE_CACHE_MAX_SIZE: u64 = 64 * 1024 * 1024;
125
126const FLUSHED_UPTO_KEY: &[u8] = b"bodies_flushed_upto";
128
129const BAD_BLOCKS_KEY: &[u8] = b"bad_blocks";
131
132const MAX_BAD_BLOCKS: usize = 16;
134
135#[derive(Debug)]
136struct CodeCache {
137 inner_cache: LruCache<H256, Code, FxBuildHasher>,
138 cache_size: u64,
139}
140
141impl Default for CodeCache {
142 fn default() -> Self {
143 Self {
144 inner_cache: LruCache::unbounded_with_hasher(FxBuildHasher),
145 cache_size: 0,
146 }
147 }
148}
149
150impl CodeCache {
151 fn get(&mut self, code_hash: &H256) -> Result<Option<Code>, StoreError> {
152 Ok(self.inner_cache.get(code_hash).cloned())
153 }
154
155 fn insert(&mut self, code: &Code) -> Result<(), StoreError> {
156 let code_size = code.size();
157 let cache_len = self.inner_cache.len() + 1;
158 self.cache_size += code_size as u64;
159 let current_size = self.cache_size;
160 debug!(
161 "[ACCOUNT CODE CACHE] cache elements (): {cache_len}, total size: {current_size} bytes"
162 );
163
164 while self.cache_size > CODE_CACHE_MAX_SIZE {
165 if let Some((_, code)) = self.inner_cache.pop_lru() {
166 self.cache_size -= code.size() as u64;
167 } else {
168 break;
169 }
170 }
171
172 self.inner_cache.get_or_insert(code.hash, || code.clone());
173 Ok(())
174 }
175}
176
177#[derive(Debug, Clone)]
183pub struct Store {
184 db_path: PathBuf,
186 backend: Arc<dyn StorageBackend>,
188 chain_config: ChainConfig,
190 trie_cache: Arc<RwLock<Arc<TrieLayerCache>>>,
192 flatkeyvalue_control_tx: std::sync::mpsc::SyncSender<FKVGeneratorControlMessage>,
194 block_data_buffer: Arc<RwLock<Arc<BlockDataBuffer>>>,
196 persist_tx: std::sync::mpsc::SyncSender<PersistMessage>,
200 pending_trie_roots: Arc<PendingTrieRoots>,
204 latest_block_header: LatestBlockHeaderCache,
207 last_computed_flatkeyvalue: Arc<RwLock<Vec<u8>>>,
209
210 account_code_cache: Arc<Mutex<CodeCache>>,
215
216 code_metadata_cache: Arc<Mutex<rustc_hash::FxHashMap<H256, CodeMetadata>>>,
219
220 fcu_lock: Arc<tokio::sync::Mutex<()>>,
223
224 safe_commit_root: Arc<RwLock<H256>>,
230
231 journal_pruning_paused: Arc<std::sync::atomic::AtomicBool>,
241
242 background_threads: Arc<ThreadList>,
243}
244
245#[derive(Debug, Default)]
246struct ThreadList {
247 list: Vec<JoinHandle<()>>,
248}
249
250impl Drop for ThreadList {
251 fn drop(&mut self) {
252 for handle in self.list.drain(..) {
253 let _ = handle.join();
254 }
255 }
256}
257
258pub type StorageTrieNodes = Vec<(H256, Vec<(Nibbles, Vec<u8>)>)>;
263type StorageTries = HashMap<Address, (TrieWitness, Trie)>;
264
265#[derive(Debug, Clone, Copy, PartialEq, Eq)]
269pub enum EngineType {
270 InMemory,
272 #[cfg(feature = "rocksdb")]
274 RocksDB,
275}
276
277pub struct UpdateBatch {
282 pub account_updates: Vec<TrieNode>,
284 pub storage_updates: Vec<(H256, Vec<TrieNode>)>,
286 pub blocks: Vec<Block>,
288 pub receipts: Vec<(H256, Vec<Receipt>)>,
290 pub code_updates: Vec<(H256, Code)>,
292 pub commit_depth: Option<usize>,
300 pub wait_for_flush: bool,
311}
312
313pub type StorageUpdates = Vec<(H256, Vec<(Nibbles, Vec<u8>)>)>;
315
316pub struct AccountUpdatesList {
321 pub state_trie_hash: H256,
323 pub state_updates: Vec<(Nibbles, Vec<u8>)>,
325 pub storage_updates: StorageUpdates,
327 pub code_updates: Vec<(H256, Code)>,
329}
330
331pub(crate) fn encode_tx_location_operand(
342 block_number: BlockNumber,
343 block_hash: BlockHash,
344 index: Index,
345) -> Vec<u8> {
346 vec![(block_number, block_hash, index)].encode_to_vec()
347}
348
349pub fn tx_locations_merge(
367 existing: Option<&[u8]>,
368 operands: impl IntoIterator<Item = impl AsRef<[u8]>>,
369) -> Option<Vec<u8>> {
370 fn fold_chunk(
374 list: &mut Vec<(BlockNumber, BlockHash, Index)>,
375 bytes: &[u8],
376 what: &str,
377 ) -> bool {
378 match <Vec<(BlockNumber, BlockHash, Index)>>::decode(bytes) {
379 Ok(entries) => {
380 for (bn, bh, idx) in entries {
381 list.retain(|(_, existing_bh, _)| *existing_bh != bh);
382 list.push((bn, bh, idx));
383 }
384 true
385 }
386 Err(e) => {
387 error!(
388 "tx_locations_merge: failed to decode {what} ({} bytes): {e}; \
389 aborting merge to avoid silent data loss",
390 bytes.len()
391 );
392 false
393 }
394 }
395 }
396
397 let mut list: Vec<(BlockNumber, BlockHash, Index)> = Vec::new();
398
399 if let Some(bytes) = existing
401 && !fold_chunk(&mut list, bytes, "existing value")
402 {
403 return None;
404 }
405 for op in operands {
406 if !fold_chunk(&mut list, op.as_ref(), "operand") {
407 return None;
408 }
409 }
410 Some(list.encode_to_vec())
411}
412
413impl Store {
414 pub async fn wait_for_persistence_idle(&self) -> Result<(), StoreError> {
425 let tx = self.persist_tx.clone();
426 tokio::task::spawn_blocking(move || {
427 let (ack_tx, ack_rx) = sync_channel::<Result<(), StoreError>>(1);
428 tx.send(PersistMessage::Ping(ack_tx))
429 .map_err(|e| StoreError::Custom(format!("wait_for_persistence_idle send: {e}")))?;
430 ack_rx
431 .recv()
432 .map_err(|e| StoreError::Custom(format!("wait_for_persistence_idle ack: {e}")))?
433 })
434 .await
435 .map_err(|e| StoreError::Custom(format!("wait_for_persistence_idle join: {e}")))?
436 }
437
438 pub async fn shutdown(&self) -> Result<(), StoreError> {
457 let tx = self.persist_tx.clone();
458 let backend = self.backend.clone();
459 tokio::task::spawn_blocking(move || {
460 let (ack_tx, ack_rx) = sync_channel::<Result<(), StoreError>>(1);
461 tx.send(PersistMessage::Shutdown { ack: ack_tx })
462 .map_err(|e| StoreError::Custom(format!("shutdown send: {e}")))?;
463 ack_rx
464 .recv()
465 .map_err(|e| StoreError::Custom(format!("shutdown ack: {e}")))??;
466 backend.flush()
469 })
470 .await
471 .map_err(|e| StoreError::Custom(format!("shutdown join: {e}")))?
472 }
473
474 pub async fn add_block(&self, block: Block) -> Result<(), StoreError> {
477 self.add_blocks(vec![block]).await
478 }
479
480 pub async fn add_blocks(&self, blocks: Vec<Block>) -> Result<(), StoreError> {
483 let db = self.backend.clone();
484 tokio::task::spawn_blocking(move || {
485 let mut tx = db.begin_write()?;
486
487 for block in blocks {
488 write_block_data(
489 tx.as_mut(),
490 block.header.number,
491 block.hash(),
492 &block.header,
493 &block.body,
494 )?;
495 }
496
497 tx.commit()
498 })
499 .await
500 .map_err(|e| StoreError::Custom(format!("Task panicked: {}", e)))?
501 }
502
503 pub async fn add_block_header(
505 &self,
506 block_hash: BlockHash,
507 block_header: BlockHeader,
508 ) -> Result<(), StoreError> {
509 let hash_key = block_hash.encode_to_vec();
510 let header_value = BlockHeaderRLP::from(block_header).into_vec();
511 self.write_async(HEADERS, hash_key, header_value).await
512 }
513
514 pub async fn add_block_headers(
516 &self,
517 block_headers: Vec<BlockHeader>,
518 ) -> Result<(), StoreError> {
519 let mut txn = self.backend.begin_write()?;
520
521 for header in block_headers {
522 let block_hash = header.hash();
523 let block_number = header.number;
524 let hash_key = block_hash.encode_to_vec();
525 let header_value = BlockHeaderRLP::from(header).into_vec();
526
527 txn.put(HEADERS, &hash_key, &header_value)?;
528
529 let number_key = block_number.to_le_bytes().to_vec();
530 txn.put(BLOCK_NUMBERS, &hash_key, &number_key)?;
531 }
532 txn.commit()?;
533 Ok(())
534 }
535
536 pub fn get_block_header(
538 &self,
539 block_number: BlockNumber,
540 ) -> Result<Option<BlockHeader>, StoreError> {
541 let latest = self.latest_block_header.get();
542 if block_number == latest.number {
543 return Ok(Some((*latest).clone()));
544 }
545 let Some(block_hash) = self.get_canonical_block_hash_sync(block_number)? else {
550 return Ok(None);
551 };
552 self.get_block_header_by_hash(block_hash)
553 }
554
555 pub async fn add_block_body(
557 &self,
558 block_hash: BlockHash,
559 block_body: BlockBody,
560 ) -> Result<(), StoreError> {
561 let hash_key = block_hash.encode_to_vec();
562 let body_value = BlockBodyRLP::from(block_body).into_vec();
563 self.write_async(BODIES, hash_key, body_value).await
564 }
565
566 pub async fn get_block_body(
568 &self,
569 block_number: BlockNumber,
570 ) -> Result<Option<BlockBody>, StoreError> {
571 let Some(block_hash) = self.get_canonical_block_hash_sync(block_number)? else {
572 return Ok(None);
573 };
574
575 self.get_block_body_by_hash(block_hash).await
576 }
577
578 pub async fn remove_block(&self, block_number: BlockNumber) -> Result<(), StoreError> {
580 let Some(hash) = self.get_canonical_block_hash_sync(block_number)? else {
581 return Ok(());
582 };
583
584 let backend = self.backend.clone();
585 tokio::task::spawn_blocking(move || {
586 let hash_key = hash.encode_to_vec();
587
588 let mut txn = backend.begin_write()?;
589 txn.delete(
590 CANONICAL_BLOCK_HASHES,
591 block_number.to_le_bytes().as_slice(),
592 )?;
593 txn.delete(BODIES, &hash_key)?;
594 txn.delete(HEADERS, &hash_key)?;
595 txn.delete(BLOCK_NUMBERS, &hash_key)?;
596 txn.commit()
597 })
598 .await
599 .map_err(|e| StoreError::Custom(format!("Task panicked: {}", e)))?
600 }
601
602 pub async fn get_block_bodies(
604 &self,
605 from: BlockNumber,
606 to: BlockNumber,
607 ) -> Result<Vec<Option<BlockBody>>, StoreError> {
608 let buffer = self.buffer()?;
610 let backend = self.backend.clone();
611 tokio::task::spawn_blocking(move || {
612 let numbers: Vec<BlockNumber> = (from..=to).collect();
613 let mut block_bodies = Vec::new();
614
615 let txn = backend.begin_read()?;
616 for number in numbers {
617 let Some(hash) = txn
618 .get(CANONICAL_BLOCK_HASHES, number.to_le_bytes().as_slice())?
619 .map(|bytes| H256::decode(bytes.as_slice()))
620 .transpose()?
621 else {
622 block_bodies.push(None);
623 continue;
624 };
625 if let Some(body) = buffer.get_body(&hash) {
628 block_bodies.push(Some(body));
629 continue;
630 }
631 let hash_key = hash.encode_to_vec();
632 let block_body_opt = txn
633 .get(BODIES, &hash_key)?
634 .map(|bytes| BlockBodyRLP::from_bytes(bytes).to())
635 .transpose()
636 .map_err(StoreError::from)?;
637
638 block_bodies.push(block_body_opt);
639 }
640
641 Ok(block_bodies)
642 })
643 .await
644 .map_err(|e| StoreError::Custom(format!("Task panicked: {}", e)))?
645 }
646
647 pub async fn get_block_bodies_by_hash(
649 &self,
650 hashes: Vec<BlockHash>,
651 ) -> Result<Vec<BlockBody>, StoreError> {
652 let buffer = self.buffer()?;
653 let backend = self.backend.clone();
654 tokio::task::spawn_blocking(move || {
656 let txn = backend.begin_read()?;
657 let mut block_bodies = Vec::new();
658 for hash in hashes {
659 if let Some(body) = buffer.get_body(&hash) {
662 block_bodies.push(body);
663 continue;
664 }
665 let hash_key = hash.encode_to_vec();
666
667 let Some(block_body) = txn
668 .get(BODIES, &hash_key)?
669 .map(|bytes| BlockBodyRLP::from_bytes(bytes).to())
670 .transpose()
671 .map_err(StoreError::from)?
672 else {
673 return Err(StoreError::Custom(format!(
674 "Block body not found for hash: {hash}"
675 )));
676 };
677 block_bodies.push(block_body);
678 }
679 Ok(block_bodies)
680 })
681 .await
682 .map_err(|e| StoreError::Custom(format!("Task panicked: {}", e)))?
683 }
684
685 pub async fn get_block_body_by_hash(
687 &self,
688 block_hash: BlockHash,
689 ) -> Result<Option<BlockBody>, StoreError> {
690 if let Some(b) = self.buffer()?.get_body(&block_hash) {
691 return Ok(Some(b));
692 }
693 self.read_async(BODIES, block_hash.encode_to_vec())
694 .await?
695 .map(|bytes| BlockBodyRLP::from_bytes(bytes).to())
696 .transpose()
697 .map_err(StoreError::from)
698 }
699
700 pub fn get_block_header_by_hash(
701 &self,
702 block_hash: BlockHash,
703 ) -> Result<Option<BlockHeader>, StoreError> {
704 let latest = self.latest_block_header.get();
705 if block_hash == latest.hash() {
706 return Ok(Some((*latest).clone()));
707 }
708 if let Some(h) = self.buffer()?.get_header(&block_hash) {
709 return Ok(Some(h));
710 }
711 self.load_block_header_by_hash(block_hash)
712 }
713
714 pub fn add_pending_block(&self, block: Block) -> Result<(), StoreError> {
715 let block_hash = block.hash();
716 let block_value = BlockRLP::from(block).into_vec();
717 self.write(PENDING_BLOCKS, block_hash.as_bytes().to_vec(), block_value)
718 }
719
720 pub async fn get_pending_block(
721 &self,
722 block_hash: BlockHash,
723 ) -> Result<Option<Block>, StoreError> {
724 self.read_async(PENDING_BLOCKS, block_hash.as_bytes().to_vec())
725 .await?
726 .map(|bytes| BlockRLP::from_bytes(bytes).to())
727 .transpose()
728 .map_err(StoreError::from)
729 }
730
731 pub async fn add_block_number(
733 &self,
734 block_hash: BlockHash,
735 block_number: BlockNumber,
736 ) -> Result<(), StoreError> {
737 let number_value = block_number.to_le_bytes().to_vec();
738 self.write_async(BLOCK_NUMBERS, block_hash.encode_to_vec(), number_value)
739 .await
740 }
741
742 pub async fn get_block_number(
744 &self,
745 block_hash: BlockHash,
746 ) -> Result<Option<BlockNumber>, StoreError> {
747 if let Some(n) = self.buffer()?.get_number(&block_hash) {
748 return Ok(Some(n));
749 }
750 self.read_async(BLOCK_NUMBERS, block_hash.encode_to_vec())
751 .await?
752 .map(|bytes| -> Result<BlockNumber, StoreError> {
753 let array: [u8; 8] = bytes
754 .try_into()
755 .map_err(|_| StoreError::Custom("Invalid BlockNumber bytes".to_string()))?;
756 Ok(BlockNumber::from_le_bytes(array))
757 })
758 .transpose()
759 }
760
761 pub async fn add_transaction_location(
763 &self,
764 transaction_hash: H256,
765 block_number: BlockNumber,
766 block_hash: BlockHash,
767 index: Index,
768 ) -> Result<(), StoreError> {
769 self.add_transaction_locations(vec![(transaction_hash, block_number, block_hash, index)])
770 .await
771 }
772
773 pub async fn add_transaction_locations(
775 &self,
776 locations: Vec<(H256, BlockNumber, BlockHash, Index)>,
777 ) -> Result<(), StoreError> {
778 let db = self.backend.clone();
779 tokio::task::spawn_blocking(move || {
780 let mut tx = db.begin_write()?;
781 for (tx_hash, block_number, block_hash, index) in locations {
782 tx.merge(
783 TRANSACTION_LOCATIONS,
784 tx_hash.as_bytes(),
785 &encode_tx_location_operand(block_number, block_hash, index),
786 )?;
787 }
788 tx.commit()
789 })
790 .await
791 .map_err(|e| StoreError::Custom(format!("Task panicked: {}", e)))?
792 }
793
794 pub async fn get_transaction_location(
796 &self,
797 transaction_hash: H256,
798 ) -> Result<Option<(BlockNumber, BlockHash, Index)>, StoreError> {
799 let buffered = self.buffer()?.get_tx_locations(&transaction_hash);
800 let db = self.backend.clone();
801 tokio::task::spawn_blocking(move || {
802 let tx = db.begin_read()?;
803 let mut locations = buffered;
804 if let Some(bytes) = tx.get(TRANSACTION_LOCATIONS, transaction_hash.as_bytes())? {
805 locations.extend(<Vec<(BlockNumber, BlockHash, Index)>>::decode(&bytes)?);
806 }
807 for (block_number, block_hash, index) in locations {
808 let canonical_hash = tx
809 .get(
810 CANONICAL_BLOCK_HASHES,
811 block_number.to_le_bytes().as_slice(),
812 )?
813 .map(|bytes| H256::decode(bytes.as_slice()))
814 .transpose()?;
815 if canonical_hash == Some(block_hash) {
816 return Ok(Some((block_number, block_hash, index)));
817 }
818 }
819 Ok(None)
820 })
821 .await
822 .map_err(|e| StoreError::Custom(format!("Task panicked: {}", e)))?
823 }
824
825 pub async fn add_receipt(
827 &self,
828 block_hash: BlockHash,
829 index: Index,
830 receipt: Receipt,
831 ) -> Result<(), StoreError> {
832 let key = receipt_key(&block_hash, index);
833 let value = receipt.encode_storage();
837 self.write_async(RECEIPTS_V2, key, value).await
838 }
839
840 pub async fn add_receipts(
842 &self,
843 block_hash: BlockHash,
844 receipts: Vec<Receipt>,
845 ) -> Result<(), StoreError> {
846 let batch_items: Vec<_> = receipts
847 .into_iter()
848 .enumerate()
849 .map(|(index, receipt)| {
850 let key = receipt_key(&block_hash, index as u64);
851 let value = receipt.encode_storage();
852 (key, value)
853 })
854 .collect();
855 self.write_batch_async(RECEIPTS_V2, batch_items).await
856 }
857
858 pub async fn get_receipt(
860 &self,
861 block_number: BlockNumber,
862 index: Index,
863 ) -> Result<Option<Receipt>, StoreError> {
864 let Some(block_hash) = self.get_canonical_block_hash(block_number).await? else {
866 return Ok(None);
867 };
868 self.get_receipt_by_block_hash(block_hash, index).await
869 }
870
871 async fn get_receipt_by_block_hash(
873 &self,
874 block_hash: BlockHash,
875 index: Index,
876 ) -> Result<Option<Receipt>, StoreError> {
877 if let Some(r) = self.buffer()?.get_receipt(&block_hash, index) {
878 return Ok(Some(r));
879 }
880 let key = receipt_key(&block_hash, index);
881 self.read_async(RECEIPTS_V2, key)
882 .await?
883 .map(|bytes| Receipt::decode_storage(bytes.as_slice()))
884 .transpose()
885 .map_err(StoreError::from)
886 }
887
888 pub fn get_account_code(&self, code_hash: H256) -> Result<Option<Code>, StoreError> {
895 if let Some(code) = self.buffer()?.get_code(&code_hash) {
896 return Ok(Some(code));
897 }
898 if let Some(code) = self
900 .account_code_cache
901 .lock()
902 .map_err(|_| StoreError::LockError)?
903 .get(&code_hash)?
904 {
905 return Ok(Some(code));
906 }
907
908 let Some(bytes) = self
909 .backend
910 .begin_read()?
911 .get(ACCOUNT_CODES, code_hash.as_bytes())?
912 else {
913 return Ok(None);
914 };
915 let (bytecode_slice, targets) = decode_bytes(&bytes)?;
916 let code = Code::from_parts_unchecked(
917 code_hash,
918 bytecode_slice,
919 <Vec<u32>>::decode(targets)?.into(),
920 );
921
922 self.account_code_cache
924 .lock()
925 .map_err(|_| StoreError::LockError)?
926 .insert(&code)?;
927
928 Ok(Some(code))
929 }
930
931 pub fn code_exists(&self, code_hash: H256) -> Result<bool, StoreError> {
936 if self.buffer()?.get_code(&code_hash).is_some() {
940 return Ok(true);
941 }
942 if self
944 .account_code_cache
945 .lock()
946 .map_err(|_| StoreError::LockError)?
947 .get(&code_hash)?
948 .is_some()
949 {
950 return Ok(true);
951 }
952 Ok(self
954 .backend
955 .begin_read()?
956 .get(ACCOUNT_CODES, code_hash.as_bytes())?
957 .is_some())
958 }
959
960 pub fn get_code_metadata(&self, code_hash: H256) -> Result<Option<CodeMetadata>, StoreError> {
965 use ethrex_common::constants::EMPTY_KECCAK_HASH;
966
967 if code_hash == *EMPTY_KECCAK_HASH {
969 return Ok(Some(CodeMetadata { length: 0 }));
970 }
971
972 if let Some(metadata) = self
974 .code_metadata_cache
975 .lock()
976 .map_err(|_| StoreError::LockError)?
977 .get(&code_hash)
978 .copied()
979 {
980 return Ok(Some(metadata));
981 }
982
983 let metadata = if let Some(bytes) = self
985 .backend
986 .begin_read()?
987 .get(ACCOUNT_CODE_METADATA, code_hash.as_bytes())?
988 {
989 let length =
990 u64::from_be_bytes(bytes.try_into().map_err(|_| {
991 StoreError::Custom("Invalid metadata length encoding".to_string())
992 })?);
993 CodeMetadata { length }
994 } else {
995 let Some(code) = self.get_account_code(code_hash)? else {
997 return Ok(None);
998 };
999 let metadata = CodeMetadata {
1000 length: code.len() as u64,
1001 };
1002
1003 let metadata_buf = metadata.length.to_be_bytes().to_vec();
1005 let hash_key = code_hash.0.to_vec();
1006 let backend = self.backend.clone();
1007 tokio::task::spawn(async move {
1008 if let Err(e) = async {
1009 let mut tx = backend.begin_write()?;
1010 tx.put(ACCOUNT_CODE_METADATA, &hash_key, &metadata_buf)?;
1011 tx.commit()
1012 }
1013 .await
1014 {
1015 tracing::warn!("Failed to write code metadata during auto-migration: {}", e);
1016 }
1017 });
1018
1019 metadata
1020 };
1021
1022 self.code_metadata_cache
1024 .lock()
1025 .map_err(|_| StoreError::LockError)?
1026 .insert(code_hash, metadata);
1027
1028 Ok(Some(metadata))
1029 }
1030
1031 pub async fn add_account_code(&self, code: Code) -> Result<(), StoreError> {
1033 let hash_key = code.hash.0.to_vec();
1034 let buf = encode_code(&code);
1035 let metadata_buf = (code.len() as u64).to_be_bytes();
1036
1037 let backend = self.backend.clone();
1039 tokio::task::spawn_blocking(move || {
1040 let mut tx = backend.begin_write()?;
1041 tx.put(ACCOUNT_CODES, &hash_key, &buf)?;
1042 tx.put(ACCOUNT_CODE_METADATA, &hash_key, &metadata_buf)?;
1043 tx.commit()
1044 })
1045 .await
1046 .map_err(|e| StoreError::Custom(format!("Task panicked: {}", e)))?
1047 }
1048
1049 pub async fn clear_snap_state(&self) -> Result<(), StoreError> {
1051 let db = self.backend.clone();
1052 tokio::task::spawn_blocking(move || db.clear_table(SNAP_STATE))
1053 .await
1054 .map_err(|e| StoreError::Custom(format!("Task panicked: {}", e)))?
1055 }
1056
1057 pub async fn get_transaction_by_hash(
1058 &self,
1059 transaction_hash: H256,
1060 ) -> Result<Option<Transaction>, StoreError> {
1061 let (_block_number, block_hash, index) =
1062 match self.get_transaction_location(transaction_hash).await? {
1063 Some(location) => location,
1064 None => return Ok(None),
1065 };
1066 self.get_transaction_by_location(block_hash, index).await
1067 }
1068
1069 pub async fn get_transaction_by_location(
1070 &self,
1071 block_hash: H256,
1072 index: u64,
1073 ) -> Result<Option<Transaction>, StoreError> {
1074 let block_body = match self.get_block_body_by_hash(block_hash).await? {
1075 Some(body) => body,
1076 None => return Ok(None),
1077 };
1078 let index: usize = index.try_into()?;
1079 Ok(block_body.transactions.get(index).cloned())
1080 }
1081
1082 pub async fn get_block_by_hash(
1083 &self,
1084 block_hash: BlockHash,
1085 ) -> Result<Option<Block>, StoreError> {
1086 let header = match self.get_block_header_by_hash(block_hash)? {
1087 Some(header) => header,
1088 None => return Ok(None),
1089 };
1090 let body = match self.get_block_body_by_hash(block_hash).await? {
1091 Some(body) => body,
1092 None => return Ok(None),
1093 };
1094 Ok(Some(Block::new(header, body)))
1095 }
1096
1097 pub async fn get_block_by_number(
1098 &self,
1099 block_number: BlockNumber,
1100 ) -> Result<Option<Block>, StoreError> {
1101 let Some(block_hash) = self.get_canonical_block_hash(block_number).await? else {
1102 return Ok(None);
1103 };
1104 self.get_block_by_hash(block_hash).await
1105 }
1106
1107 pub async fn get_canonical_block_hash(
1109 &self,
1110 block_number: BlockNumber,
1111 ) -> Result<Option<BlockHash>, StoreError> {
1112 let last = self.latest_block_header.get();
1113 if last.number == block_number {
1114 return Ok(Some(last.hash()));
1115 }
1116 let backend = self.backend.clone();
1117 tokio::task::spawn_blocking(move || {
1118 backend
1119 .begin_read()?
1120 .get(
1121 CANONICAL_BLOCK_HASHES,
1122 block_number.to_le_bytes().as_slice(),
1123 )?
1124 .map(|bytes| H256::decode(bytes.as_slice()))
1125 .transpose()
1126 .map_err(StoreError::from)
1127 })
1128 .await
1129 .map_err(|e| StoreError::Custom(format!("Task panicked: {}", e)))?
1130 }
1131
1132 pub async fn set_chain_config(&mut self, chain_config: &ChainConfig) -> Result<(), StoreError> {
1135 self.chain_config = *chain_config;
1136 let key = chain_data_key(ChainDataIndex::ChainConfig);
1137 let value = serde_json::to_string(chain_config)
1138 .map_err(|_| StoreError::Custom("Failed to serialize chain config".to_string()))?
1139 .into_bytes();
1140 self.write_async(CHAIN_DATA, key, value).await
1141 }
1142
1143 pub async fn update_earliest_block_number(
1145 &self,
1146 block_number: BlockNumber,
1147 ) -> Result<(), StoreError> {
1148 let key = chain_data_key(ChainDataIndex::EarliestBlockNumber);
1149 let value = block_number.to_le_bytes().to_vec();
1150 self.write_async(CHAIN_DATA, key, value).await
1151 }
1152
1153 pub async fn get_earliest_block_number(&self) -> Result<BlockNumber, StoreError> {
1155 let key = chain_data_key(ChainDataIndex::EarliestBlockNumber);
1156 self.read_async(CHAIN_DATA, key)
1157 .await?
1158 .map(|bytes| -> Result<BlockNumber, StoreError> {
1159 let array: [u8; 8] = bytes
1160 .try_into()
1161 .map_err(|_| StoreError::Custom("Invalid BlockNumber bytes".to_string()))?;
1162 Ok(BlockNumber::from_le_bytes(array))
1163 })
1164 .ok_or(StoreError::MissingEarliestBlockNumber)?
1165 }
1166
1167 pub async fn get_finalized_block_number(&self) -> Result<Option<BlockNumber>, StoreError> {
1169 let key = chain_data_key(ChainDataIndex::FinalizedBlockNumber);
1170 self.read_async(CHAIN_DATA, key)
1171 .await?
1172 .map(|bytes| -> Result<BlockNumber, StoreError> {
1173 let array: [u8; 8] = bytes
1174 .try_into()
1175 .map_err(|_| StoreError::Custom("Invalid BlockNumber bytes".to_string()))?;
1176 Ok(BlockNumber::from_le_bytes(array))
1177 })
1178 .transpose()
1179 }
1180
1181 pub async fn get_safe_block_number(&self) -> Result<Option<BlockNumber>, StoreError> {
1183 let key = chain_data_key(ChainDataIndex::SafeBlockNumber);
1184 self.read_async(CHAIN_DATA, key)
1185 .await?
1186 .map(|bytes| -> Result<BlockNumber, StoreError> {
1187 let array: [u8; 8] = bytes
1188 .try_into()
1189 .map_err(|_| StoreError::Custom("Invalid BlockNumber bytes".to_string()))?;
1190 Ok(BlockNumber::from_le_bytes(array))
1191 })
1192 .transpose()
1193 }
1194
1195 pub async fn get_latest_block_number(&self) -> Result<BlockNumber, StoreError> {
1197 Ok(self.latest_block_header.get().number)
1198 }
1199
1200 pub async fn update_pending_block_number(
1202 &self,
1203 block_number: BlockNumber,
1204 ) -> Result<(), StoreError> {
1205 let key = chain_data_key(ChainDataIndex::PendingBlockNumber);
1206 let value = block_number.to_le_bytes().to_vec();
1207 self.write_async(CHAIN_DATA, key, value).await
1208 }
1209
1210 pub async fn get_pending_block_number(&self) -> Result<Option<BlockNumber>, StoreError> {
1212 let key = chain_data_key(ChainDataIndex::PendingBlockNumber);
1213 self.read_async(CHAIN_DATA, key)
1214 .await?
1215 .map(|bytes| -> Result<BlockNumber, StoreError> {
1216 let array: [u8; 8] = bytes
1217 .try_into()
1218 .map_err(|_| StoreError::Custom("Invalid BlockNumber bytes".to_string()))?;
1219 Ok(BlockNumber::from_le_bytes(array))
1220 })
1221 .transpose()
1222 }
1223
1224 async fn forkchoice_update_inner(
1239 &self,
1240 new_canonical_blocks: Vec<(BlockNumber, BlockHash)>,
1241 head_number: BlockNumber,
1242 head_hash: BlockHash,
1243 safe: Option<BlockNumber>,
1244 finalized: Option<BlockNumber>,
1245 ) -> Result<(), StoreError> {
1246 let latest = self.load_latest_block_number().await?.unwrap_or(0);
1247 let db = self.backend.clone();
1248 let journal_pruning_paused = self.journal_pruning_paused.clone();
1249 tokio::task::spawn_blocking(move || {
1250 let mut txn = db.begin_write()?;
1251
1252 for (block_number, block_hash) in new_canonical_blocks {
1253 let head_key = block_number.to_le_bytes();
1254 let head_value = block_hash.encode_to_vec();
1255 txn.put(CANONICAL_BLOCK_HASHES, &head_key, &head_value)?;
1256 }
1257
1258 for number in (head_number + 1)..=(latest) {
1265 txn.delete(CANONICAL_BLOCK_HASHES, number.to_le_bytes().as_slice())?;
1266 }
1267
1268 let head_key = head_number.to_le_bytes();
1270 let head_value = head_hash.encode_to_vec();
1271 txn.put(CANONICAL_BLOCK_HASHES, &head_key, &head_value)?;
1272
1273 let latest_key = chain_data_key(ChainDataIndex::LatestBlockNumber);
1275 txn.put(CHAIN_DATA, &latest_key, &head_number.to_le_bytes())?;
1276
1277 if let Some(safe) = safe {
1278 let safe_key = chain_data_key(ChainDataIndex::SafeBlockNumber);
1279 txn.put(CHAIN_DATA, &safe_key, &safe.to_le_bytes())?;
1280 }
1281
1282 if let Some(finalized) = finalized {
1283 let finalized_key = chain_data_key(ChainDataIndex::FinalizedBlockNumber);
1284
1285 let prev_finalized = match db.begin_read()?.get(CHAIN_DATA, &finalized_key)? {
1295 Some(bytes) => {
1296 let arr: [u8; 8] = bytes.as_slice().try_into().map_err(|_| {
1297 StoreError::Custom(format!(
1298 "FinalizedBlockNumber has unexpected length {} (want 8)",
1299 bytes.len()
1300 ))
1301 })?;
1302 BlockNumber::from_le_bytes(arr)
1303 }
1304 None => 0,
1305 };
1306
1307 txn.put(CHAIN_DATA, &finalized_key, &finalized.to_le_bytes())?;
1308
1309 if finalized > prev_finalized
1321 && !journal_pruning_paused.load(std::sync::atomic::Ordering::Acquire)
1322 {
1323 let start = 0u64.to_be_bytes();
1324 let end = finalized.saturating_add(1).to_be_bytes();
1325 txn.delete_range(STATE_HISTORY, &start, &end)?;
1326 }
1327 }
1328
1329 txn.commit()
1330 })
1331 .await
1332 .map_err(|e| StoreError::Custom(format!("Task panicked: {}", e)))?
1333 }
1334
1335 pub async fn get_receipts_for_block(
1336 &self,
1337 block_hash: &BlockHash,
1338 ) -> Result<Vec<Receipt>, StoreError> {
1339 self.get_receipts_for_block_from_index(block_hash, 0, None)
1340 .await
1341 }
1342
1343 pub async fn get_receipts_for_block_from_index(
1352 &self,
1353 block_hash: &BlockHash,
1354 start_index: u64,
1355 max_count: Option<usize>,
1356 ) -> Result<Vec<Receipt>, StoreError> {
1357 if let Some(all) = self.buffer()?.get_receipts(block_hash) {
1358 let start = start_index as usize;
1359 let slice = all.into_iter().skip(start);
1360 return Ok(match max_count {
1361 Some(max) => slice.take(max).collect(),
1362 None => slice.collect(),
1363 });
1364 }
1365 let backend = self.backend.clone();
1366 let block_hash = *block_hash;
1367
1368 tokio::task::spawn_blocking(move || {
1369 let txn = backend.begin_read()?;
1370 let prefix = block_hash.as_bytes().to_vec();
1371 let mut seek_key = prefix.clone();
1374 seek_key.extend_from_slice(&start_index.to_be_bytes());
1375 let iter = txn.prefix_iterator(RECEIPTS_V2, &seek_key)?;
1376 let mut receipts = Vec::new();
1377 for result in iter {
1378 let (k, v) = result?;
1379 if !k.starts_with(&prefix) {
1380 break;
1381 }
1382 if k.len() != 40 {
1383 continue;
1384 }
1385 receipts.push(Receipt::decode_storage(v.as_ref())?);
1386 if let Some(max) = max_count
1387 && receipts.len() >= max
1388 {
1389 break;
1390 }
1391 }
1392 Ok(receipts)
1393 })
1394 .await
1395 .map_err(|e| StoreError::Custom(format!("Task panicked: {e}")))?
1396 }
1397
1398 pub async fn set_header_download_checkpoint(
1402 &self,
1403 block_hash: BlockHash,
1404 ) -> Result<(), StoreError> {
1405 let key = snap_state_key(SnapStateIndex::HeaderDownloadCheckpoint);
1406 let value = block_hash.encode_to_vec();
1407 self.write_async(SNAP_STATE, key, value).await
1408 }
1409
1410 pub async fn get_header_download_checkpoint(&self) -> Result<Option<BlockHash>, StoreError> {
1412 let key = snap_state_key(SnapStateIndex::HeaderDownloadCheckpoint);
1413 self.backend
1414 .begin_read()?
1415 .get(SNAP_STATE, &key)?
1416 .map(|bytes| H256::decode(bytes.as_slice()))
1417 .transpose()
1418 .map_err(StoreError::from)
1419 }
1420
1421 pub async fn set_latest_valid_ancestor(
1427 &self,
1428 bad_block: BlockHash,
1429 latest_valid: BlockHash,
1430 ) -> Result<(), StoreError> {
1431 let value = latest_valid.encode_to_vec();
1432 self.write_async(INVALID_CHAINS, bad_block.as_bytes().to_vec(), value)
1433 .await
1434 }
1435
1436 pub async fn get_latest_valid_ancestor(
1439 &self,
1440 block: BlockHash,
1441 ) -> Result<Option<BlockHash>, StoreError> {
1442 self.read_async(INVALID_CHAINS, block.as_bytes().to_vec())
1443 .await?
1444 .map(|bytes| H256::decode(bytes.as_slice()))
1445 .transpose()
1446 .map_err(StoreError::from)
1447 }
1448
1449 pub async fn add_bad_block(&self, block: Block) -> Result<(), StoreError> {
1454 let mut bad_blocks = self.get_bad_blocks().await?;
1455 let block_number = block.header.number;
1456 let block_hash = block.hash();
1457 if bad_blocks
1458 .iter()
1459 .any(|b| b.header.number == block_number && b.hash() == block_hash)
1460 {
1461 return Ok(());
1462 }
1463 bad_blocks.push(block);
1464 bad_blocks.sort_by(|a, b| b.header.number.cmp(&a.header.number));
1465 bad_blocks.truncate(MAX_BAD_BLOCKS);
1466 self.write_async(
1467 BAD_BLOCKS,
1468 BAD_BLOCKS_KEY.to_vec(),
1469 bad_blocks.encode_to_vec(),
1470 )
1471 .await
1472 }
1473
1474 pub async fn get_bad_blocks(&self) -> Result<Vec<Block>, StoreError> {
1477 match self.read_async(BAD_BLOCKS, BAD_BLOCKS_KEY.to_vec()).await? {
1478 Some(bytes) => Vec::<Block>::decode(&bytes).map_err(StoreError::from),
1479 None => Ok(Vec::new()),
1480 }
1481 }
1482
1483 pub fn get_block_number_sync(
1485 &self,
1486 block_hash: BlockHash,
1487 ) -> Result<Option<BlockNumber>, StoreError> {
1488 if let Some(n) = self.buffer()?.get_number(&block_hash) {
1489 return Ok(Some(n));
1490 }
1491 let txn = self.backend.begin_read()?;
1492 txn.get(BLOCK_NUMBERS, &block_hash.encode_to_vec())?
1493 .map(|bytes| -> Result<BlockNumber, StoreError> {
1494 let array: [u8; 8] = bytes
1495 .try_into()
1496 .map_err(|_| StoreError::Custom("Invalid BlockNumber bytes".to_string()))?;
1497 Ok(BlockNumber::from_le_bytes(array))
1498 })
1499 .transpose()
1500 }
1501
1502 pub fn get_canonical_block_hash_sync(
1504 &self,
1505 block_number: BlockNumber,
1506 ) -> Result<Option<BlockHash>, StoreError> {
1507 let last = self.latest_block_header.get();
1508 if last.number == block_number {
1509 return Ok(Some(last.hash()));
1510 }
1511 let txn = self.backend.begin_read()?;
1512 txn.get(
1513 CANONICAL_BLOCK_HASHES,
1514 block_number.to_le_bytes().as_slice(),
1515 )?
1516 .map(|bytes| H256::decode(bytes.as_slice()))
1517 .transpose()
1518 .map_err(StoreError::from)
1519 }
1520
1521 pub async fn write_storage_trie_nodes_batch(
1524 &self,
1525 storage_trie_nodes: StorageUpdates,
1526 ) -> Result<(), StoreError> {
1527 let mut txn = self.backend.begin_write()?;
1528 tokio::task::spawn_blocking(move || {
1529 for (address_hash, nodes) in storage_trie_nodes {
1530 for (node_path, node_data) in nodes {
1531 let key = apply_prefix(Some(address_hash), node_path);
1532 if node_data.is_empty() {
1533 txn.delete(STORAGE_TRIE_NODES, key.as_ref())?;
1534 } else {
1535 txn.put(STORAGE_TRIE_NODES, key.as_ref(), &node_data)?;
1536 }
1537 }
1538 }
1539 txn.commit()
1540 })
1541 .await
1542 .map_err(|e| StoreError::Custom(format!("Task panicked: {}", e)))?
1543 }
1544
1545 pub async fn write_account_code_batch(
1548 &self,
1549 account_codes: Vec<(H256, Code)>,
1550 ) -> Result<(), StoreError> {
1551 let mut code_batch_items = Vec::new();
1552 let mut metadata_batch_items = Vec::new();
1553
1554 for (code_hash, code) in account_codes {
1555 let buf = encode_code(&code);
1556 let metadata_buf = (code.len() as u64).to_be_bytes().to_vec();
1557 code_batch_items.push((code_hash.as_bytes().to_vec(), buf));
1558 metadata_batch_items.push((code_hash.as_bytes().to_vec(), metadata_buf));
1559 }
1560
1561 self.write_batch_async(ACCOUNT_CODES, code_batch_items)
1563 .await?;
1564 self.write_batch_async(ACCOUNT_CODE_METADATA, metadata_batch_items)
1565 .await
1566 }
1567
1568 fn buffer(&self) -> Result<Arc<BlockDataBuffer>, StoreError> {
1570 Ok(self
1571 .block_data_buffer
1572 .read()
1573 .map_err(|_| StoreError::LockError)?
1574 .clone())
1575 }
1576
1577 pub fn write(
1583 &self,
1584 table: &'static str,
1585 key: Vec<u8>,
1586 value: Vec<u8>,
1587 ) -> Result<(), StoreError> {
1588 let backend = self.backend.clone();
1589 let mut txn = backend.begin_write()?;
1590 txn.put(table, &key, &value)?;
1591 txn.commit()
1592 }
1593
1594 async fn write_async(
1597 &self,
1598 table: &'static str,
1599 key: Vec<u8>,
1600 value: Vec<u8>,
1601 ) -> Result<(), StoreError> {
1602 let backend = self.backend.clone();
1603
1604 tokio::task::spawn_blocking(move || {
1605 let mut txn = backend.begin_write()?;
1606 txn.put(table, &key, &value)?;
1607 txn.commit()
1608 })
1609 .await
1610 .map_err(|e| StoreError::Custom(format!("Task panicked: {}", e)))?
1611 }
1612
1613 pub async fn read_async(
1616 &self,
1617 table: &'static str,
1618 key: Vec<u8>,
1619 ) -> Result<Option<Vec<u8>>, StoreError> {
1620 let backend = self.backend.clone();
1621
1622 tokio::task::spawn_blocking(move || {
1623 let txn = backend.begin_read()?;
1624 txn.get(table, &key)
1625 })
1626 .await
1627 .map_err(|e| StoreError::Custom(format!("Task panicked: {}", e)))?
1628 }
1629
1630 pub fn read(&self, table: &'static str, key: Vec<u8>) -> Result<Option<Vec<u8>>, StoreError> {
1633 let backend = self.backend.clone();
1634 let txn = backend.begin_read()?;
1635 txn.get(table, &key)
1636 }
1637
1638 pub async fn write_batch_async(
1642 &self,
1643 table: &'static str,
1644 batch_ops: Vec<(Vec<u8>, Vec<u8>)>,
1645 ) -> Result<(), StoreError> {
1646 let backend = self.backend.clone();
1647
1648 tokio::task::spawn_blocking(move || {
1649 let mut txn = backend.begin_write()?;
1650 txn.put_batch(table, batch_ops)?;
1651 txn.commit()
1652 })
1653 .await
1654 .map_err(|e| StoreError::Custom(format!("Task panicked: {}", e)))?
1655 }
1656
1657 pub fn write_batch(
1659 &self,
1660 table: &'static str,
1661 batch_ops: Vec<(Vec<u8>, Vec<u8>)>,
1662 ) -> Result<(), StoreError> {
1663 let backend = self.backend.clone();
1664 let mut txn = backend.begin_write()?;
1665 txn.put_batch(table, batch_ops)?;
1666 txn.commit()
1667 }
1668
1669 pub async fn add_fullsync_batch(&self, headers: Vec<BlockHeader>) -> Result<(), StoreError> {
1670 self.write_batch_async(
1671 FULLSYNC_HEADERS,
1672 headers
1673 .into_iter()
1674 .map(|header| (header.number.to_le_bytes().to_vec(), header.encode_to_vec()))
1675 .collect(),
1676 )
1677 .await
1678 }
1679
1680 pub async fn read_fullsync_batch(
1681 &self,
1682 start: BlockNumber,
1683 limit: u64,
1684 ) -> Result<Vec<Option<BlockHeader>>, StoreError> {
1685 let mut res = vec![];
1686 let read_tx = self.backend.begin_read()?;
1687 for key in start..start + limit {
1689 let header_opt = read_tx
1690 .get(FULLSYNC_HEADERS, &key.to_le_bytes())?
1691 .map(|header| BlockHeader::decode(&header))
1692 .transpose()?;
1693 res.push(header_opt);
1694 }
1695 Ok(res)
1696 }
1697
1698 pub async fn clear_fullsync_headers(&self) -> Result<(), StoreError> {
1699 self.backend.clear_table(FULLSYNC_HEADERS)
1700 }
1701
1702 pub fn delete(&self, table: &'static str, key: Vec<u8>) -> Result<(), StoreError> {
1704 let mut txn = self.backend.begin_write()?;
1705 txn.delete(table, &key)?;
1706 txn.commit()
1707 }
1708
1709 pub fn store_block_updates(&self, update_batch: UpdateBatch) -> Result<(), StoreError> {
1710 self.apply_updates(update_batch)
1711 }
1712
1713 fn batch_state_roots(
1720 &self,
1721 update_batch: &UpdateBatch,
1722 ) -> Result<(H256, H256, BlockNumber, H256), StoreError> {
1723 let parent_state_root = self
1724 .get_block_header_by_hash(
1725 update_batch
1726 .blocks
1727 .first()
1728 .ok_or(StoreError::UpdateBatchNoBlocks)?
1729 .header
1730 .parent_hash,
1731 )?
1732 .map(|header| header.state_root)
1733 .unwrap_or_default();
1734 let last_block = update_batch
1735 .blocks
1736 .last()
1737 .ok_or(StoreError::UpdateBatchNoBlocks)?;
1738 let last_state_root = last_block.header.state_root;
1739 let last_block_number = last_block.header.number;
1740 let last_block_hash = last_block.hash();
1741 Ok((
1742 parent_state_root,
1743 last_state_root,
1744 last_block_number,
1745 last_block_hash,
1746 ))
1747 }
1748
1749 fn apply_updates(&self, update_batch: UpdateBatch) -> Result<(), StoreError> {
1753 let (parent_state_root, last_state_root, last_block_number, last_block_hash) =
1754 self.batch_state_roots(&update_batch)?;
1755
1756 let UpdateBatch {
1757 account_updates,
1758 storage_updates,
1759 blocks,
1760 receipts,
1761 code_updates,
1762 commit_depth,
1763 wait_for_flush,
1764 } = update_batch;
1765
1766 self.pending_trie_roots.register(last_state_root)?;
1770
1771 let blocks_with_receipts: Vec<(Block, Vec<Receipt>)> = if blocks.len() == 1 {
1774 let block = blocks.into_iter().next().expect("len == 1");
1775 let hash = block.hash();
1776 let r = receipts
1777 .into_iter()
1778 .find(|(h, _)| *h == hash)
1779 .map(|(_, r)| r)
1780 .unwrap_or_default();
1781 vec![(block, r)]
1782 } else {
1783 let mut receipts_by_hash: std::collections::HashMap<BlockHash, Vec<Receipt>> =
1784 receipts.into_iter().collect();
1785 blocks
1786 .into_iter()
1787 .map(|b| {
1788 let r = receipts_by_hash.remove(&b.hash()).unwrap_or_default();
1789 (b, r)
1790 })
1791 .collect()
1792 };
1793
1794 let (ack_tx, ack_rx) = sync_channel(1);
1799 self.persist_tx
1800 .send(PersistMessage::Block(Box::new(BlockPersist {
1801 blocks: blocks_with_receipts,
1802 codes: code_updates,
1803 parent_state_root,
1804 child_state_root: last_state_root,
1805 account_updates,
1806 storage_updates,
1807 commit_depth,
1808 wait_for_flush,
1809 block_number: last_block_number,
1810 block_hash: last_block_hash,
1811 ack: ack_tx,
1812 })))
1813 .map_err(|e| StoreError::Custom(format!("failed to send block persist: {e}")))?;
1814 ack_rx
1815 .recv()
1816 .map_err(|e| StoreError::Custom(format!("block persist ack failed: {e}")))??;
1817
1818 Ok(())
1819 }
1820
1821 pub fn new(path: impl AsRef<Path>, engine_type: EngineType) -> Result<Self, StoreError> {
1826 Self::new_with_config(path, engine_type, StoreConfig::default())
1827 }
1828
1829 pub fn new_with_config(
1831 path: impl AsRef<Path>,
1832 engine_type: EngineType,
1833 #[cfg_attr(not(feature = "rocksdb"), allow(unused_variables))] config: StoreConfig,
1835 ) -> Result<Self, StoreError> {
1836 let db_path = path.as_ref().to_path_buf();
1837
1838 if engine_type != EngineType::InMemory {
1839 let version = read_store_schema_version(&db_path)?;
1840
1841 match version {
1842 None if db_path.exists() && dir_contains_legacy_db(&db_path)? => {
1843 return Err(StoreError::NotFoundDBVersion);
1845 }
1846 None => {
1847 init_metadata_file(&db_path)?;
1853 }
1854 Some(v) if v < 1 => {
1855 return Err(StoreError::MigrationFailed {
1856 from: v,
1857 to: STORE_SCHEMA_VERSION,
1858 reason: format!("DB version v{v} is invalid (predates migrations)"),
1859 });
1860 }
1861 Some(v) if v > STORE_SCHEMA_VERSION => {
1862 return Err(StoreError::MigrationFailed {
1863 from: v,
1864 to: STORE_SCHEMA_VERSION,
1865 reason: format!(
1866 "DB version v{v} is more recent than the client expects (v{STORE_SCHEMA_VERSION}). Rolling back is not supported"
1867 ),
1868 });
1869 }
1870 #[cfg(feature = "rocksdb")]
1871 Some(v) if v < STORE_SCHEMA_VERSION => {
1872 let rocksdb = Arc::new(RocksDBBackend::open(
1876 &path,
1877 config.rocksdb_block_cache_size,
1878 )?);
1879 crate::migrations::run_pending_migrations(rocksdb.as_ref(), &db_path, v)?;
1880 rocksdb.drop_obsolete_cfs(&path);
1881 let backend: Arc<dyn crate::api::StorageBackend> = rocksdb;
1882 return Self::from_backend(
1883 backend,
1884 db_path,
1885 DB_COMMIT_THRESHOLD,
1886 config.persist_channel_capacity,
1887 );
1888 }
1889 Some(_) => {
1890 }
1895 }
1896 }
1897
1898 match engine_type {
1899 #[cfg(feature = "rocksdb")]
1900 EngineType::RocksDB => {
1901 let rocksdb = RocksDBBackend::open(&path, config.rocksdb_block_cache_size)?;
1902 rocksdb.drop_obsolete_cfs(&path);
1903 let backend: Arc<dyn StorageBackend> = Arc::new(rocksdb);
1904 Self::from_backend(
1905 backend,
1906 db_path,
1907 DB_COMMIT_THRESHOLD,
1908 config.persist_channel_capacity,
1909 )
1910 }
1911 EngineType::InMemory => {
1912 let backend = Arc::new(InMemoryBackend::open()?);
1913 Self::from_backend(
1914 backend,
1915 db_path,
1916 IN_MEMORY_COMMIT_THRESHOLD,
1917 config.persist_channel_capacity,
1918 )
1919 }
1920 }
1921 }
1922
1923 fn from_backend(
1924 backend: Arc<dyn StorageBackend>,
1925 db_path: PathBuf,
1926 commit_threshold: usize,
1927 persist_channel_capacity: usize,
1928 ) -> Result<Self, StoreError> {
1929 debug!("Initializing Store with {commit_threshold} in-memory diff-layers");
1930 let (fkv_tx, fkv_rx) = std::sync::mpsc::sync_channel(0);
1931 let persist_cap = persist_channel_capacity.max(1); let (persist_tx, persist_rx) = std::sync::mpsc::sync_channel(persist_cap);
1933
1934 let (last_written, initial_flushed_upto) = {
1935 let tx = backend.begin_read()?;
1936 let last_written = tx
1937 .get(MISC_VALUES, "last_written".as_bytes())?
1938 .unwrap_or_else(|| vec![0u8; 64]);
1939 let last_written = if last_written == [0xff] {
1940 vec![0xff; 64]
1941 } else {
1942 last_written
1943 };
1944 let initial_flushed_upto = match tx.get(MISC_VALUES, FLUSHED_UPTO_KEY)? {
1945 Some(bytes) => decode_flushed_upto(&bytes)?,
1946 None => 0,
1947 };
1948 (last_written, initial_flushed_upto)
1949 };
1950 let mut initial_buffer = BlockDataBuffer::new();
1951 initial_buffer.set_flushed_upto(initial_flushed_upto);
1952
1953 let mut background_threads = Vec::new();
1954 let safe_commit_root = Arc::new(RwLock::new(H256::zero()));
1955 let mut store = Self {
1956 db_path,
1957 backend,
1958 chain_config: Default::default(),
1959 latest_block_header: Default::default(),
1960 trie_cache: Arc::new(RwLock::new(Arc::new(TrieLayerCache::new_with_safe_commit(
1961 commit_threshold,
1962 safe_commit_root.clone(),
1963 )))),
1964 flatkeyvalue_control_tx: fkv_tx,
1965 block_data_buffer: Arc::new(RwLock::new(Arc::new(initial_buffer))),
1966 persist_tx,
1967 pending_trie_roots: Arc::new(PendingTrieRoots::default()),
1968 last_computed_flatkeyvalue: Arc::new(RwLock::new(last_written)),
1969 account_code_cache: Arc::new(Mutex::new(CodeCache::default())),
1970 code_metadata_cache: Arc::new(Mutex::new(rustc_hash::FxHashMap::default())),
1971 fcu_lock: Arc::new(tokio::sync::Mutex::new(())),
1972 safe_commit_root,
1973 journal_pruning_paused: Arc::new(std::sync::atomic::AtomicBool::new(false)),
1974 background_threads: Default::default(),
1975 };
1976 let backend_clone = store.backend.clone();
1977 let last_computed_fkv = store.last_computed_flatkeyvalue.clone();
1978 background_threads.push(std::thread::spawn(move || {
1979 let rx = fkv_rx;
1980 loop {
1982 match rx.recv() {
1983 Ok(FKVGeneratorControlMessage::Continue) => break,
1984 Ok(FKVGeneratorControlMessage::Stop) => {}
1985 Err(std::sync::mpsc::RecvError) => {
1986 debug!("Closing FlatKeyValue generator.");
1987 return;
1988 }
1989 }
1990 }
1991
1992 let _ = flatkeyvalue_generator(&backend_clone, &last_computed_fkv, &rx)
1993 .inspect_err(|err| error!("Error while generating FlatKeyValue: {err}"));
1994 }));
1995 let persist_backend = store.backend.clone();
1998 let persist_buffer = store.block_data_buffer.clone();
1999 let persist_trie_cache = store.trie_cache.clone();
2000 let persist_pending_roots = store.pending_trie_roots.clone();
2001 let persist_fkv_ctl = store.flatkeyvalue_control_tx.clone();
2002 background_threads.push(std::thread::spawn(move || {
2003 let rx = persist_rx;
2004 let mut last_flush_result: Result<(), StoreError> = Ok(());
2007 loop {
2008 match rx.recv() {
2009 Ok(PersistMessage::Block(bp)) => {
2010 let bp = *bp;
2011 let staged = mutate_block_buffer(&persist_buffer, move |b| {
2014 let mut codes = Some(bp.codes);
2015 for (block, receipts) in bp.blocks {
2016 b.insert(block, receipts, codes.take().unwrap_or_default());
2017 }
2018 });
2019 if let Err(e) = staged {
2020 persist_pending_roots.clear(bp.child_state_root);
2025 let _ = bp.ack.send(Err(e));
2026 continue;
2027 }
2028 if !bp.wait_for_flush {
2037 let _ = bp
2038 .ack
2039 .send(std::mem::replace(&mut last_flush_result, Ok(())));
2040 }
2041 if let Err(err) = apply_trie_phase1(
2043 &persist_trie_cache,
2044 &persist_pending_roots,
2045 bp.parent_state_root,
2046 bp.child_state_root,
2047 bp.block_number,
2048 bp.block_hash,
2049 bp.account_updates,
2050 bp.storage_updates,
2051 ) {
2052 error!("persist worker trie phase-1 failed: {err}");
2053 if bp.wait_for_flush {
2054 let _ = bp.ack.send(Err(err));
2055 } else {
2056 last_flush_result = Err(err);
2057 }
2058 continue;
2059 }
2060 let flushed = flush_block_data(persist_backend.as_ref(), &persist_buffer)
2062 .inspect_err(|err| error!("flush_block_data failed: {err}"))
2063 .and_then(|_| {
2064 commit_trie_if_due(
2065 persist_backend.as_ref(),
2066 &persist_trie_cache,
2067 &persist_fkv_ctl,
2068 bp.parent_state_root,
2069 bp.commit_depth,
2070 bp.wait_for_flush,
2071 )
2072 });
2073 if bp.wait_for_flush {
2076 let prior = std::mem::replace(&mut last_flush_result, Ok(()));
2077 let _ = bp.ack.send(prior.and(flushed));
2078 } else {
2079 last_flush_result = flushed;
2080 }
2081 }
2082 Ok(PersistMessage::Commit(root)) => match persist_trie_cache.read() {
2083 Ok(guard) => {
2084 let trie = guard.clone();
2085 drop(guard);
2086 let _ = commit_to_disk(
2089 persist_backend.as_ref(),
2090 &persist_fkv_ctl,
2091 &persist_trie_cache,
2092 &trie,
2093 root,
2094 false,
2095 )
2096 .inspect_err(|err| error!("commit_to_disk failed: {err}"));
2097 }
2098 Err(_) => error!("trie cache lock poisoned during commit"),
2099 },
2100 Ok(PersistMessage::Ping(ack)) => {
2101 let _ = ack.send(std::mem::replace(&mut last_flush_result, Ok(())));
2105 }
2106 Ok(PersistMessage::Shutdown { ack }) => {
2107 let result = flush_block_data(persist_backend.as_ref(), &persist_buffer);
2114 let prior = std::mem::replace(&mut last_flush_result, Ok(()));
2115 let _ = ack.send(prior.and(result));
2116 return;
2118 }
2119 Err(_) => return,
2120 }
2121 }
2122 }));
2123 store.background_threads = Arc::new(ThreadList {
2124 list: background_threads,
2125 });
2126 Ok(store)
2127 }
2128
2129 pub async fn new_from_genesis(
2132 store_path: &Path,
2133 engine_type: EngineType,
2134 genesis_path: &str,
2135 ) -> Result<Self, StoreError> {
2136 Self::new_from_genesis_with_config(
2137 store_path,
2138 engine_type,
2139 genesis_path,
2140 StoreConfig::default(),
2141 )
2142 .await
2143 }
2144
2145 pub async fn new_from_genesis_with_config(
2148 store_path: &Path,
2149 engine_type: EngineType,
2150 genesis_path: &str,
2151 config: StoreConfig,
2152 ) -> Result<Self, StoreError> {
2153 let file = std::fs::File::open(genesis_path)
2154 .map_err(|error| StoreError::Custom(format!("Failed to open genesis file: {error}")))?;
2155 let reader = std::io::BufReader::new(file);
2156 let genesis: Genesis = serde_json::from_reader(reader)
2157 .map_err(|e| StoreError::Custom(format!("Failed to deserialize genesis file: {e}")))?;
2158 let mut store = Self::new_with_config(store_path, engine_type, config)?;
2159 store.add_initial_state(genesis).await?;
2160 Ok(store)
2161 }
2162
2163 pub async fn get_account_info(
2164 &self,
2165 block_number: BlockNumber,
2166 address: Address,
2167 ) -> Result<Option<AccountInfo>, StoreError> {
2168 match self.get_canonical_block_hash(block_number).await? {
2169 Some(block_hash) => self.get_account_info_by_hash(block_hash, address),
2170 None => Ok(None),
2171 }
2172 }
2173
2174 pub fn get_account_info_by_hash(
2175 &self,
2176 block_hash: BlockHash,
2177 address: Address,
2178 ) -> Result<Option<AccountInfo>, StoreError> {
2179 let Some(state_trie) = self.state_trie(block_hash)? else {
2180 return Ok(None);
2181 };
2182 let hashed_address = hash_address_fixed(&address);
2183
2184 let Some(encoded_state) = state_trie.get(hashed_address.as_bytes())? else {
2185 return Ok(None);
2186 };
2187
2188 let account_state = AccountState::decode(&encoded_state)?;
2189 Ok(Some(AccountInfo {
2190 code_hash: account_state.code_hash,
2191 balance: account_state.balance,
2192 nonce: account_state.nonce,
2193 }))
2194 }
2195
2196 pub fn get_account_state_by_acc_hash(
2197 &self,
2198 block_hash: BlockHash,
2199 account_hash: H256,
2200 ) -> Result<Option<AccountState>, StoreError> {
2201 let Some(state_trie) = self.state_trie(block_hash)? else {
2202 return Ok(None);
2203 };
2204 let Some(encoded_state) = state_trie.get(account_hash.as_bytes())? else {
2205 return Ok(None);
2206 };
2207 let account_state = AccountState::decode(&encoded_state)?;
2208 Ok(Some(account_state))
2209 }
2210
2211 pub async fn get_fork_id(&self) -> Result<ForkId, StoreError> {
2212 let chain_config = self.get_chain_config();
2213 let genesis_header = self
2214 .load_block_header(0)?
2215 .ok_or(StoreError::MissingEarliestBlockNumber)?;
2216 let block_header = self.latest_block_header.get();
2217
2218 Ok(ForkId::new(
2219 chain_config,
2220 genesis_header,
2221 block_header.timestamp,
2222 block_header.number,
2223 ))
2224 }
2225
2226 pub async fn get_code_by_account_address(
2227 &self,
2228 block_number: BlockNumber,
2229 address: Address,
2230 ) -> Result<Option<Code>, StoreError> {
2231 let Some(block_hash) = self.get_canonical_block_hash(block_number).await? else {
2232 return Ok(None);
2233 };
2234 let Some(state_trie) = self.state_trie(block_hash)? else {
2235 return Ok(None);
2236 };
2237 let hashed_address = hash_address_fixed(&address);
2238 let Some(encoded_state) = state_trie.get(hashed_address.as_bytes())? else {
2239 return Ok(None);
2240 };
2241 let account_state = AccountState::decode(&encoded_state)?;
2242 self.get_account_code(account_state.code_hash)
2243 }
2244
2245 pub async fn get_nonce_by_account_address(
2246 &self,
2247 block_number: BlockNumber,
2248 address: Address,
2249 ) -> Result<Option<u64>, StoreError> {
2250 let Some(block_hash) = self.get_canonical_block_hash(block_number).await? else {
2251 return Ok(None);
2252 };
2253 let Some(state_trie) = self.state_trie(block_hash)? else {
2254 return Ok(None);
2255 };
2256 let hashed_address = hash_address_fixed(&address);
2257 let Some(encoded_state) = state_trie.get(hashed_address.as_bytes())? else {
2258 return Ok(None);
2259 };
2260 let account_state = AccountState::decode(&encoded_state)?;
2261 Ok(Some(account_state.nonce))
2262 }
2263
2264 pub fn apply_account_updates_batch(
2267 &self,
2268 block_hash: BlockHash,
2269 account_updates: &[AccountUpdate],
2270 ) -> Result<Option<AccountUpdatesList>, StoreError> {
2271 let Some(mut state_trie) = self.state_trie(block_hash)? else {
2272 return Ok(None);
2273 };
2274
2275 Ok(Some(self.apply_account_updates_from_trie_batch(
2276 &mut state_trie,
2277 account_updates,
2278 )?))
2279 }
2280
2281 pub fn apply_account_updates_from_trie_batch<'a>(
2282 &self,
2283 state_trie: &mut Trie,
2284 account_updates: impl IntoIterator<Item = &'a AccountUpdate>,
2285 ) -> Result<AccountUpdatesList, StoreError> {
2286 let mut ret_storage_updates = Vec::new();
2287 let mut code_updates = Vec::new();
2288 let state_root = state_trie.hash_no_commit(&NativeCrypto);
2289 for update in account_updates {
2290 let hashed_address = hash_address_fixed(&update.address);
2291 if update.removed {
2292 state_trie.remove(hashed_address.as_bytes())?;
2294 continue;
2295 }
2296 let mut account_state = match state_trie.get(hashed_address.as_bytes())? {
2299 Some(encoded_state) => AccountState::decode(&encoded_state)?,
2300 None => AccountState::default(),
2301 };
2302 if update.removed_storage {
2303 account_state.storage_root = EMPTY_TRIE_HASH;
2304 }
2305 if let Some(info) = &update.info {
2306 account_state.nonce = info.nonce;
2307 account_state.balance = info.balance;
2308 account_state.code_hash = info.code_hash;
2309 if let Some(code) = &update.code {
2311 code_updates.push((info.code_hash, code.clone()));
2312 }
2313 }
2314 if !update.added_storage.is_empty() {
2316 let mut storage_trie =
2317 self.open_storage_trie(hashed_address, state_root, account_state.storage_root)?;
2318 for (storage_key, storage_value) in &update.added_storage {
2319 let hashed_key = hash_key(storage_key);
2320 if storage_value.is_zero() {
2321 storage_trie.remove(&hashed_key)?;
2322 } else {
2323 storage_trie.insert(hashed_key, storage_value.encode_to_vec())?;
2324 }
2325 }
2326 let (storage_hash, storage_updates) =
2327 storage_trie.collect_changes_since_last_hash(&NativeCrypto);
2328 account_state.storage_root = storage_hash;
2329 ret_storage_updates.push((hashed_address, storage_updates));
2330 }
2331 state_trie.insert(
2332 hashed_address.as_bytes().to_vec(),
2333 account_state.encode_to_vec(),
2334 )?;
2335 }
2336 let (state_trie_hash, state_updates) =
2337 state_trie.collect_changes_since_last_hash(&NativeCrypto);
2338
2339 Ok(AccountUpdatesList {
2340 state_trie_hash,
2341 state_updates,
2342 storage_updates: ret_storage_updates,
2343 code_updates,
2344 })
2345 }
2346
2347 pub fn apply_account_updates_from_trie_with_witness(
2350 &self,
2351 mut state_trie: Trie,
2352 account_updates: &[AccountUpdate],
2353 mut storage_tries: StorageTries,
2354 ) -> Result<(StorageTries, AccountUpdatesList), StoreError> {
2355 let mut ret_storage_updates = Vec::new();
2356
2357 let mut code_updates = Vec::new();
2358
2359 let state_root = state_trie.hash_no_commit(&NativeCrypto);
2360
2361 for update in account_updates.iter() {
2362 let hashed_address = hash_address(&update.address);
2363
2364 if update.removed {
2365 state_trie.remove(&hashed_address)?;
2367
2368 continue;
2369 }
2370
2371 let mut account_state = match state_trie.get(&hashed_address)? {
2374 Some(encoded_state) => AccountState::decode(&encoded_state)?,
2375 None => AccountState::default(),
2376 };
2377
2378 if update.removed_storage {
2379 account_state.storage_root = EMPTY_TRIE_HASH;
2380 }
2381
2382 if let Some(info) = &update.info {
2383 account_state.nonce = info.nonce;
2384
2385 account_state.balance = info.balance;
2386
2387 account_state.code_hash = info.code_hash;
2388
2389 if let Some(code) = &update.code {
2391 code_updates.push((info.code_hash, code.clone()));
2392 }
2393 }
2394
2395 if !update.added_storage.is_empty() {
2397 let (_witness, storage_trie) = match storage_tries.entry(update.address) {
2398 Entry::Occupied(value) => value.into_mut(),
2399 Entry::Vacant(vacant) => {
2400 let trie = self.open_storage_trie(
2401 H256::from_slice(&hashed_address),
2402 state_root,
2403 account_state.storage_root,
2404 )?;
2405 vacant.insert(TrieLogger::open_trie(trie))
2406 }
2407 };
2408
2409 for (storage_key, storage_value) in &update.added_storage {
2410 let hashed_key = hash_key(storage_key);
2411
2412 if storage_value.is_zero() {
2413 storage_trie.remove(&hashed_key)?;
2414 } else {
2415 storage_trie.insert(hashed_key, storage_value.encode_to_vec())?;
2416 }
2417 }
2418
2419 let (storage_hash, storage_updates) =
2420 storage_trie.collect_changes_since_last_hash(&NativeCrypto);
2421
2422 account_state.storage_root = storage_hash;
2423
2424 ret_storage_updates.push((H256::from_slice(&hashed_address), storage_updates));
2425 }
2426
2427 state_trie.insert(hashed_address, account_state.encode_to_vec())?;
2428 }
2429
2430 let (state_trie_hash, state_updates) =
2431 state_trie.collect_changes_since_last_hash(&NativeCrypto);
2432
2433 let account_updates_list = AccountUpdatesList {
2434 state_trie_hash,
2435 state_updates,
2436 storage_updates: ret_storage_updates,
2437 code_updates,
2438 };
2439
2440 Ok((storage_tries, account_updates_list))
2441 }
2442
2443 pub async fn setup_genesis_state_trie(
2445 &self,
2446 genesis_accounts: BTreeMap<Address, GenesisAccount>,
2447 ) -> Result<H256, StoreError> {
2448 let mut storage_trie_nodes = vec![];
2449 let mut genesis_state_trie = self.open_direct_state_trie(EMPTY_TRIE_HASH)?;
2450 for (address, account) in genesis_accounts {
2451 let hashed_address = hash_address(&address);
2452 let h256_hashed_address = H256::from_slice(&hashed_address);
2453
2454 let code = Code::from_bytecode(account.code, &NativeCrypto);
2456 let code_hash = code.hash;
2457 self.add_account_code(code).await?;
2458
2459 let mut storage_trie =
2461 self.open_direct_storage_trie(h256_hashed_address, EMPTY_TRIE_HASH)?;
2462 for (storage_key, storage_value) in account.storage {
2463 if !storage_value.is_zero() {
2464 let hashed_key = hash_key(&H256(storage_key.to_big_endian()));
2465 storage_trie.insert(hashed_key, storage_value.encode_to_vec())?;
2466 }
2467 }
2468
2469 let (storage_root, storage_nodes) =
2470 storage_trie.collect_changes_since_last_hash(&NativeCrypto);
2471
2472 storage_trie_nodes.extend(
2473 storage_nodes
2474 .into_iter()
2475 .map(|(path, n)| (apply_prefix(Some(h256_hashed_address), path).into_vec(), n)),
2476 );
2477
2478 let account_state = AccountState {
2480 nonce: account.nonce,
2481 balance: account.balance,
2482 storage_root,
2483 code_hash,
2484 };
2485 genesis_state_trie.insert(hashed_address, account_state.encode_to_vec())?;
2486 }
2487
2488 let (state_root, account_trie_nodes) =
2489 genesis_state_trie.collect_changes_since_last_hash(&NativeCrypto);
2490 let account_trie_nodes = account_trie_nodes
2491 .into_iter()
2492 .map(|(path, n)| (apply_prefix(None, path).into_vec(), n))
2493 .collect::<Vec<_>>();
2494
2495 let mut tx = self.backend.begin_write()?;
2496 tx.put_batch(ACCOUNT_TRIE_NODES, account_trie_nodes)?;
2497 tx.put_batch(STORAGE_TRIE_NODES, storage_trie_nodes)?;
2498 tx.commit()?;
2499
2500 Ok(state_root)
2501 }
2502
2503 fn make_witness_key(block_number: u64, block_hash: &BlockHash) -> Vec<u8> {
2505 let mut composite_key = Vec::with_capacity(8 + 32);
2506 composite_key.extend_from_slice(&block_number.to_be_bytes());
2507 composite_key.extend_from_slice(block_hash.as_bytes());
2508 composite_key
2509 }
2510
2511 pub fn store_witness(
2517 &self,
2518 block_hash: BlockHash,
2519 block_number: u64,
2520 witness: ExecutionWitness,
2521 ) -> Result<(), StoreError> {
2522 let rpc_witness = RpcExecutionWitness::try_from(witness)?;
2524 let key = Self::make_witness_key(block_number, &block_hash);
2525 let value = serde_json::to_vec(&rpc_witness)?;
2526 self.write(EXECUTION_WITNESSES, key, value)?;
2527 self.cleanup_old_witnesses(block_number)
2529 }
2530
2531 fn cleanup_old_witnesses(&self, latest_block_number: u64) -> Result<(), StoreError> {
2532 if latest_block_number <= MAX_WITNESSES {
2534 return Ok(());
2535 }
2536
2537 let threshold = latest_block_number - MAX_WITNESSES;
2538
2539 if let Some(oldest_block_number) = self.get_oldest_witness_number()? {
2540 let prefix = oldest_block_number.to_be_bytes();
2541 let mut to_delete = Vec::new();
2542
2543 {
2544 let read_txn = self.backend.begin_read()?;
2545 let iter = read_txn.prefix_iterator(EXECUTION_WITNESSES, &prefix)?;
2546
2547 for item in iter {
2549 let (key, _value) = item?;
2550 let mut block_number_bytes = [0u8; 8];
2551 block_number_bytes.copy_from_slice(&key[0..8]);
2552 let block_number = u64::from_be_bytes(block_number_bytes);
2553 if block_number > threshold {
2554 break;
2555 }
2556 to_delete.push(key.to_vec());
2557 }
2558 }
2559
2560 for key in to_delete {
2561 self.delete(EXECUTION_WITNESSES, key)?;
2562 }
2563 };
2564
2565 self.update_oldest_witness_number(threshold + 1)?;
2566
2567 Ok(())
2568 }
2569
2570 fn update_oldest_witness_number(&self, oldest_block_number: u64) -> Result<(), StoreError> {
2571 self.write(
2572 MISC_VALUES,
2573 b"oldest_witness_block_number".to_vec(),
2574 oldest_block_number.to_le_bytes().to_vec(),
2575 )?;
2576 Ok(())
2577 }
2578
2579 fn get_oldest_witness_number(&self) -> Result<Option<u64>, StoreError> {
2580 let Some(value) = self.read(MISC_VALUES, b"oldest_witness_block_number".to_vec())? else {
2581 return Ok(None);
2582 };
2583
2584 let array: [u8; 8] = value.as_slice().try_into().map_err(|_| {
2585 StoreError::Custom("Invalid oldest witness block number bytes".to_string())
2586 })?;
2587 Ok(Some(u64::from_le_bytes(array)))
2588 }
2589
2590 pub fn get_witness_json_bytes(
2596 &self,
2597 block_number: u64,
2598 block_hash: BlockHash,
2599 ) -> Result<Option<Vec<u8>>, StoreError> {
2600 let key = Self::make_witness_key(block_number, &block_hash);
2601 self.read(EXECUTION_WITNESSES, key)
2602 }
2603
2604 pub fn get_witness_by_number_and_hash(
2609 &self,
2610 block_number: u64,
2611 block_hash: BlockHash,
2612 ) -> Result<Option<RpcExecutionWitness>, StoreError> {
2613 let key = Self::make_witness_key(block_number, &block_hash);
2614 match self.read(EXECUTION_WITNESSES, key)? {
2615 Some(value) => {
2616 let witness: RpcExecutionWitness = serde_json::from_slice(&value)?;
2617 Ok(Some(witness))
2618 }
2619 None => Ok(None),
2620 }
2621 }
2622
2623 pub fn store_block_access_list(
2625 &self,
2626 block_hash: BlockHash,
2627 bal: &BlockAccessList,
2628 ) -> Result<(), StoreError> {
2629 let key = block_hash.as_bytes().to_vec();
2630 let mut value = vec![];
2631 bal.encode(&mut value);
2632 self.write(BLOCK_ACCESS_LISTS, key, value)
2633 }
2634
2635 pub fn get_block_access_list(
2637 &self,
2638 block_hash: BlockHash,
2639 ) -> Result<Option<BlockAccessList>, StoreError> {
2640 let key = block_hash.as_bytes().to_vec();
2641 match self.read(BLOCK_ACCESS_LISTS, key)? {
2642 Some(value) => {
2643 let bal = BlockAccessList::decode(&value)
2644 .map_err(|e| StoreError::Custom(format!("Failed to decode BAL: {e}")))?;
2645 Ok(Some(bal))
2646 }
2647 None => Ok(None),
2648 }
2649 }
2650
2651 pub async fn add_initial_state(&mut self, genesis: Genesis) -> Result<(), StoreError> {
2652 self.add_initial_state_inner(genesis, false).await
2653 }
2654
2655 pub async fn add_initial_state_skip_validation(
2666 &mut self,
2667 genesis: Genesis,
2668 ) -> Result<(), StoreError> {
2669 self.add_initial_state_inner(genesis, true).await
2670 }
2671
2672 async fn add_initial_state_inner(
2673 &mut self,
2674 genesis: Genesis,
2675 skip_genesis_validation: bool,
2676 ) -> Result<(), StoreError> {
2677 debug!("Storing initial state from genesis");
2678
2679 let genesis_block = genesis.get_block();
2681 let genesis_block_number = genesis_block.header.number;
2682
2683 let genesis_hash = genesis_block.hash();
2684
2685 let stored_genesis_header = self.load_block_header(genesis_block_number)?;
2686
2687 self.set_chain_config(&genesis.config).await?;
2694
2695 if let Some(latest) = self.load_latest_block_number().await? {
2700 self.anchor_to_durable_head(latest).await?;
2701 }
2702
2703 match stored_genesis_header {
2704 Some(header) if skip_genesis_validation => {
2705 info!(
2706 stored_genesis = %header.hash(),
2707 "Skipping genesis state validation; trusting the genesis header and state already stored in the datadir"
2708 );
2709 return Ok(());
2710 }
2711 Some(header) if header.hash() == genesis_hash => {
2712 info!("Received genesis file matching a previously stored one, nothing to do");
2713 return Ok(());
2714 }
2715 Some(_) => {
2716 error!(
2717 "The chain configuration stored in the database is incompatible with the provided configuration. If you intended to switch networks, choose another datadir or clear the database (e.g., run `ethrex removedb`) and try again."
2718 );
2719 return Err(StoreError::IncompatibleChainConfig);
2720 }
2721 None => {
2722 self.add_block_header(genesis_hash, genesis_block.header.clone())
2723 .await?
2724 }
2725 }
2726 let genesis_state_root = self.setup_genesis_state_trie(genesis.alloc).await?;
2729 debug_assert_eq!(genesis_state_root, genesis_block.header.state_root);
2730
2731 info!(hash = %genesis_hash, "Storing genesis block");
2733
2734 self.add_block(genesis_block).await?;
2735 self.update_earliest_block_number(genesis_block_number)
2736 .await?;
2737 self.forkchoice_update(vec![], genesis_block_number, genesis_hash, None, None)
2738 .await?;
2739 Ok(())
2740 }
2741
2742 pub async fn load_initial_state(&self) -> Result<(), StoreError> {
2743 info!("Loading initial state from DB");
2744 let Some(latest) = self.load_latest_block_number().await? else {
2745 return Err(StoreError::MissingLatestBlockNumber);
2746 };
2747 self.anchor_to_durable_head(latest).await?;
2751 Ok(())
2752 }
2753
2754 pub fn get_storage_at(
2755 &self,
2756 block_number: BlockNumber,
2757 address: Address,
2758 storage_key: H256,
2759 ) -> Result<Option<U256>, StoreError> {
2760 match self.get_block_header(block_number)? {
2761 Some(header) => self.get_storage_at_root(header.state_root, address, storage_key),
2762 None => Ok(None),
2763 }
2764 }
2765
2766 pub fn get_storage_at_root(
2767 &self,
2768 state_root: H256,
2769 address: Address,
2770 storage_key: H256,
2771 ) -> Result<Option<U256>, StoreError> {
2772 let account_hash = hash_address_fixed(&address);
2773
2774 let read_view = self.backend.begin_read()?;
2776 let cache = self.gated_snapshot(state_root)?;
2777 let last_written = self.last_written()?;
2778 let use_fkv = Self::flatkeyvalue_computed_with_last_written(account_hash, &last_written)
2785 && !cache.overlay_serves(state_root);
2786
2787 let storage_root = if use_fkv {
2788 EMPTY_TRIE_HASH
2790 } else {
2791 let state_trie = self.open_state_trie_shared(
2792 state_root,
2793 read_view.clone(),
2794 cache.clone(),
2795 last_written.clone(),
2796 )?;
2797 let Some(encoded_account) = state_trie.get(account_hash.as_bytes())? else {
2798 return Ok(None);
2799 };
2800 let account = AccountState::decode(&encoded_account)?;
2801 account.storage_root
2802 };
2803 let storage_trie = self.open_storage_trie_shared(
2804 account_hash,
2805 state_root,
2806 storage_root,
2807 read_view,
2808 cache,
2809 last_written,
2810 )?;
2811
2812 let hashed_key = hash_key_fixed(&storage_key);
2813 storage_trie
2814 .get(&hashed_key)?
2815 .map(|rlp| U256::decode(&rlp).map_err(StoreError::RLPDecode))
2816 .transpose()
2817 }
2818
2819 pub fn get_storage_at_root_with_known_storage_root(
2824 &self,
2825 state_root: H256,
2826 account_hash: H256,
2827 storage_root: H256,
2828 storage_key: H256,
2829 ) -> Result<Option<U256>, StoreError> {
2830 let read_view = self.backend.begin_read()?;
2831 let cache = self.gated_snapshot(state_root)?;
2832 let last_written = self.last_written()?;
2833 let storage_root =
2840 if Self::flatkeyvalue_computed_with_last_written(account_hash, &last_written)
2841 && !cache.overlay_serves(state_root)
2842 {
2843 EMPTY_TRIE_HASH
2844 } else {
2845 storage_root
2846 };
2847 let storage_trie = self.open_storage_trie_shared(
2848 account_hash,
2849 state_root,
2850 storage_root,
2851 read_view,
2852 cache,
2853 last_written,
2854 )?;
2855
2856 let hashed_key = hash_key_fixed(&storage_key);
2857 storage_trie
2858 .get(&hashed_key)?
2859 .map(|rlp| U256::decode(&rlp).map_err(StoreError::RLPDecode))
2860 .transpose()
2861 }
2862
2863 pub fn get_chain_config(&self) -> ChainConfig {
2864 self.chain_config
2865 }
2866
2867 pub async fn get_latest_canonical_block_hash(&self) -> Result<Option<BlockHash>, StoreError> {
2868 Ok(Some(self.latest_block_header.get().hash()))
2869 }
2870
2871 pub async fn forkchoice_update(
2876 &self,
2877 new_canonical_blocks: Vec<(BlockNumber, BlockHash)>,
2878 head_number: BlockNumber,
2879 head_hash: BlockHash,
2880 safe: Option<BlockNumber>,
2881 finalized: Option<BlockNumber>,
2882 ) -> Result<(), StoreError> {
2883 let _guard = self.fcu_lock.lock().await;
2889
2890 let previous_head = self.latest_block_header.get();
2895 let new_head = self
2896 .get_block_header_by_hash(head_hash)?
2897 .ok_or_else(|| StoreError::MissingLatestBlockNumber)?;
2898 self.latest_block_header.update(new_head);
2899 if let Err(err) = self
2900 .forkchoice_update_inner(
2901 new_canonical_blocks,
2902 head_number,
2903 head_hash,
2904 safe,
2905 finalized,
2906 )
2907 .await
2908 {
2909 self.latest_block_header.update((*previous_head).clone());
2910 return Err(err);
2911 }
2912
2913 if let Some(root) = self.compute_safe_commit_root(head_number)? {
2919 if self.set_safe_commit_root(root)? {
2924 let tx = self.persist_tx.clone();
2925 let _ = tokio::task::spawn_blocking(move || tx.send(PersistMessage::Commit(root)))
2926 .await;
2927 }
2928 }
2929
2930 Ok(())
2931 }
2932
2933 pub(crate) fn set_safe_commit_root(&self, root: H256) -> Result<bool, StoreError> {
2941 let mut guard = self
2942 .safe_commit_root
2943 .write()
2944 .map_err(|_| StoreError::LockError)?;
2945 let changed = *guard != root;
2946 *guard = root;
2947 Ok(changed)
2948 }
2949
2950 pub(crate) fn compute_safe_commit_root(
2959 &self,
2960 head: BlockNumber,
2961 ) -> Result<Option<H256>, StoreError> {
2962 let commit_threshold = self
2963 .trie_cache
2964 .read()
2965 .map_err(|_| StoreError::LockError)?
2966 .commit_threshold;
2967 let Some(target) = head.checked_sub(commit_threshold as u64) else {
2968 return Ok(None);
2969 };
2970 let Some(hash) = self.get_canonical_block_hash_sync(target)? else {
2971 return Ok(None);
2972 };
2973 let Some(header) = self.get_block_header_by_hash(hash)? else {
2974 return Ok(None);
2975 };
2976 Ok(Some(header.state_root))
2977 }
2978
2979 pub fn state_trie(&self, block_hash: BlockHash) -> Result<Option<Trie>, StoreError> {
2981 let Some(header) = self.get_block_header_by_hash(block_hash)? else {
2982 return Ok(None);
2983 };
2984 Ok(Some(self.open_state_trie(header.state_root)?))
2985 }
2986
2987 pub fn storage_trie(
2989 &self,
2990 block_hash: BlockHash,
2991 address: Address,
2992 ) -> Result<Option<Trie>, StoreError> {
2993 let Some(header) = self.get_block_header_by_hash(block_hash)? else {
2994 return Ok(None);
2995 };
2996 let Some(state_trie) = self.state_trie(block_hash)? else {
2998 return Ok(None);
2999 };
3000 let hashed_address = hash_address_fixed(&address);
3001 let Some(encoded_account) = state_trie.get(hashed_address.as_bytes())? else {
3002 return Ok(None);
3003 };
3004 let account = AccountState::decode(&encoded_account)?;
3005 let storage_root = account.storage_root;
3007 Ok(Some(self.open_storage_trie(
3008 hashed_address,
3009 header.state_root,
3010 storage_root,
3011 )?))
3012 }
3013
3014 pub async fn get_account_state(
3015 &self,
3016 block_number: BlockNumber,
3017 address: Address,
3018 ) -> Result<Option<AccountState>, StoreError> {
3019 let Some(block_hash) = self.get_canonical_block_hash(block_number).await? else {
3020 return Ok(None);
3021 };
3022 let Some(state_trie) = self.state_trie(block_hash)? else {
3023 return Ok(None);
3024 };
3025 self.get_account_state_from_trie(&state_trie, address)
3026 }
3027
3028 pub fn get_account_state_by_root(
3029 &self,
3030 state_root: H256,
3031 address: Address,
3032 ) -> Result<Option<AccountState>, StoreError> {
3033 let state_trie = self.open_state_trie(state_root)?;
3034 self.get_account_state_from_trie(&state_trie, address)
3035 }
3036
3037 pub fn get_account_state_from_trie(
3038 &self,
3039 state_trie: &Trie,
3040 address: Address,
3041 ) -> Result<Option<AccountState>, StoreError> {
3042 let hashed_address = hash_address_fixed(&address);
3043 let Some(encoded_state) = state_trie.get(hashed_address.as_bytes())? else {
3044 return Ok(None);
3045 };
3046 Ok(Some(AccountState::decode(&encoded_state)?))
3047 }
3048
3049 pub fn get_account_states_batch_by_root(
3058 &self,
3059 state_root: H256,
3060 addresses: &[Address],
3061 ) -> Result<Vec<Option<AccountState>>, StoreError> {
3062 if addresses.is_empty() {
3063 return Ok(Vec::new());
3064 }
3065
3066 let last_written = self.last_written()?;
3067 let trie_cache = self
3068 .trie_cache
3069 .read()
3070 .map_err(|_| StoreError::LockError)?
3071 .clone();
3072
3073 let mut results: Vec<Option<AccountState>> = vec![None; addresses.len()];
3074 let leaf_paths: Vec<Vec<u8>> = addresses
3076 .iter()
3077 .map(|addr| {
3078 let hashed = hash_address_fixed(addr);
3079 Nibbles::from_bytes(hashed.as_bytes()).into_vec()
3080 })
3081 .collect();
3082
3083 let mut fkv_indices: Vec<usize> = Vec::new();
3084 let mut trie_indices: Vec<usize> = Vec::new();
3085
3086 let overlay_active = trie_cache.overlay_serves(state_root);
3102 let fkv_cursor: &[u8] = last_written.as_slice();
3103 for (i, path) in leaf_paths.iter().enumerate() {
3104 if let Some(value) = trie_cache.get(state_root, path.as_slice()) {
3105 if !value.is_empty() {
3106 results[i] = Some(AccountState::decode(&value)?);
3107 }
3108 continue;
3109 }
3110 if !overlay_active && fkv_cursor >= path.as_slice() {
3111 fkv_indices.push(i);
3112 } else {
3113 trie_indices.push(i);
3114 }
3115 }
3116
3117 if !fkv_indices.is_empty() {
3118 let read_view = self.backend.begin_read()?;
3119 let keys: Vec<&[u8]> = fkv_indices
3120 .iter()
3121 .map(|&i| leaf_paths[i].as_slice())
3122 .collect();
3123 let raw = read_view.multi_get(ACCOUNT_FLATKEYVALUE, &keys);
3124 for (slot, res) in fkv_indices.iter().zip(raw.into_iter()) {
3125 let Some(encoded) = res? else { continue };
3126 if encoded.is_empty() {
3127 continue;
3128 }
3129 results[*slot] = Some(AccountState::decode(&encoded)?);
3130 }
3131 }
3132
3133 if !trie_indices.is_empty() {
3134 let state_trie = self.open_state_trie(state_root)?;
3140 let fetched: Result<Vec<(usize, Option<AccountState>)>, StoreError> = trie_indices
3141 .par_iter()
3142 .map(|&i| {
3143 self.get_account_state_from_trie(&state_trie, addresses[i])
3144 .map(|s| (i, s))
3145 })
3146 .collect();
3147 for (i, s) in fetched? {
3148 results[i] = s;
3149 }
3150 }
3151
3152 Ok(results)
3153 }
3154
3155 pub async fn get_account_proof(
3160 &self,
3161 state_root: H256,
3162 address: Address,
3163 storage_keys: &[H256],
3164 ) -> Result<Option<AccountProof>, StoreError> {
3165 let state_trie = self.open_state_trie(state_root)?;
3170 let address_path = hash_address_fixed(&address);
3171 let proof = state_trie.get_proof(address_path.as_bytes())?;
3172 let account_opt = state_trie
3173 .get(address_path.as_bytes())?
3174 .map(|encoded_state| AccountState::decode(&encoded_state))
3175 .transpose()?;
3176
3177 let mut storage_proof = Vec::with_capacity(storage_keys.len());
3178
3179 if let Some(account) = &account_opt {
3180 let storage_trie =
3181 self.open_storage_trie(address_path, state_root, account.storage_root)?;
3182
3183 for key in storage_keys {
3184 let hashed_key = hash_key(key);
3185 let proof = storage_trie.get_proof(&hashed_key)?;
3186 let value = storage_trie
3187 .get(&hashed_key)?
3188 .map(|rlp| U256::decode(&rlp).map_err(StoreError::RLPDecode))
3189 .transpose()?
3190 .unwrap_or_default();
3191
3192 let slot_proof = StorageSlotProof {
3193 proof,
3194 key: *key,
3195 value,
3196 };
3197 storage_proof.push(slot_proof);
3198 }
3199 } else {
3200 storage_proof.extend(storage_keys.iter().map(|key| StorageSlotProof {
3201 proof: Vec::new(),
3202 key: *key,
3203 value: U256::zero(),
3204 }));
3205 }
3206 let account = account_opt.unwrap_or_default();
3207 let account_proof = AccountProof {
3208 proof,
3209 account,
3210 storage_proof,
3211 };
3212 Ok(Some(account_proof))
3213 }
3214
3215 pub fn iter_accounts_from(
3218 &self,
3219 state_root: H256,
3220 starting_address: H256,
3221 ) -> Result<impl Iterator<Item = (H256, AccountState)>, StoreError> {
3222 let mut iter = self.open_locked_state_trie(state_root)?.into_iter();
3223 iter.advance(starting_address.0.to_vec())?;
3224 Ok(iter.content().map_while(|(path, value)| {
3225 Some((H256::from_slice(&path), AccountState::decode(&value).ok()?))
3226 }))
3227 }
3228
3229 pub fn iter_accounts(
3232 &self,
3233 state_root: H256,
3234 ) -> Result<impl Iterator<Item = (H256, AccountState)>, StoreError> {
3235 self.iter_accounts_from(state_root, H256::zero())
3236 }
3237
3238 pub fn iter_storage_from(
3241 &self,
3242 state_root: H256,
3243 hashed_address: H256,
3244 starting_slot: H256,
3245 ) -> Result<Option<impl Iterator<Item = (H256, U256)>>, StoreError> {
3246 let state_trie = self.open_locked_state_trie(state_root)?;
3247 let Some(account_rlp) = state_trie.get(hashed_address.as_bytes())? else {
3248 return Ok(None);
3249 };
3250 let storage_root = AccountState::decode(&account_rlp)?.storage_root;
3251 let mut iter = self
3252 .open_locked_storage_trie(hashed_address, state_root, storage_root)?
3253 .into_iter();
3254 iter.advance(starting_slot.0.to_vec())?;
3255 Ok(Some(iter.content().map_while(|(path, value)| {
3256 Some((H256::from_slice(&path), U256::decode(&value).ok()?))
3257 })))
3258 }
3259
3260 pub fn iter_storage(
3263 &self,
3264 state_root: H256,
3265 hashed_address: H256,
3266 ) -> Result<Option<impl Iterator<Item = (H256, U256)>>, StoreError> {
3267 self.iter_storage_from(state_root, hashed_address, H256::zero())
3268 }
3269
3270 pub fn get_account_range_proof(
3271 &self,
3272 state_root: H256,
3273 starting_hash: H256,
3274 last_hash: Option<H256>,
3275 ) -> Result<Vec<Vec<u8>>, StoreError> {
3276 let state_trie = self.open_state_trie(state_root)?;
3277 let mut proof = state_trie.get_proof(starting_hash.as_bytes())?;
3278 if let Some(last_hash) = last_hash {
3279 proof.extend_from_slice(&state_trie.get_proof(last_hash.as_bytes())?);
3280 }
3281 Ok(proof)
3282 }
3283
3284 pub fn get_storage_range_proof(
3285 &self,
3286 state_root: H256,
3287 hashed_address: H256,
3288 starting_hash: H256,
3289 last_hash: Option<H256>,
3290 ) -> Result<Option<Vec<Vec<u8>>>, StoreError> {
3291 let state_trie = self.open_state_trie(state_root)?;
3292 let Some(account_rlp) = state_trie.get(hashed_address.as_bytes())? else {
3293 return Ok(None);
3294 };
3295 let storage_root = AccountState::decode(&account_rlp)?.storage_root;
3296 let storage_trie = self.open_storage_trie(hashed_address, state_root, storage_root)?;
3297 let mut proof = storage_trie.get_proof(starting_hash.as_bytes())?;
3298 if let Some(last_hash) = last_hash {
3299 proof.extend_from_slice(&storage_trie.get_proof(last_hash.as_bytes())?);
3300 }
3301 Ok(Some(proof))
3302 }
3303
3304 pub fn get_trie_nodes(
3311 &self,
3312 state_root: H256,
3313 paths: Vec<Vec<u8>>,
3314 byte_limit: u64,
3315 ) -> Result<Vec<Vec<u8>>, StoreError> {
3316 let Some(account_path) = paths.first() else {
3317 return Ok(vec![]);
3318 };
3319 let state_trie = self.open_state_trie(state_root)?;
3320 if paths.len() == 1 {
3322 let node = state_trie.get_node(account_path)?;
3324 return Ok(vec![node]);
3325 }
3326 let Some(account_state) = state_trie
3328 .get(account_path)?
3329 .map(|ref rlp| AccountState::decode(rlp))
3330 .transpose()?
3331 else {
3332 return Ok(vec![]);
3333 };
3334 let Ok(hashed_address) = account_path.clone().try_into().map(H256) else {
3336 return Ok(vec![]);
3337 };
3338 let storage_trie =
3339 self.open_storage_trie(hashed_address, state_root, account_state.storage_root)?;
3340 let mut nodes = vec![];
3342 let mut bytes_used = 0;
3343 for path in paths.iter().skip(1) {
3347 if bytes_used >= byte_limit {
3348 break;
3349 }
3350 let node = storage_trie.get_node(path)?;
3351 bytes_used += node.len() as u64;
3352 nodes.push(node);
3353 }
3354 Ok(nodes)
3355 }
3356
3357 fn gated_snapshot(&self, state_root: H256) -> Result<Arc<TrieLayerCache>, StoreError> {
3366 self.pending_trie_roots.wait_until_ready(state_root)?;
3367 Ok(self
3368 .trie_cache
3369 .read()
3370 .map_err(|_| StoreError::LockError)?
3371 .clone())
3372 }
3373
3374 pub fn open_state_trie(&self, state_root: H256) -> Result<Trie, StoreError> {
3378 let trie_db = TrieWrapper::new(
3379 state_root,
3380 self.gated_snapshot(state_root)?,
3381 Box::new(BackendTrieDB::new_for_accounts(
3382 self.backend.clone(),
3383 self.last_written()?,
3384 )?),
3385 None,
3386 );
3387 Ok(Trie::open(Box::new(trie_db), state_root))
3388 }
3389
3390 pub fn open_direct_state_trie(&self, state_root: H256) -> Result<Trie, StoreError> {
3394 Ok(Trie::open(
3395 Box::new(BackendTrieDB::new_for_accounts(
3396 self.backend.clone(),
3397 self.last_written()?,
3398 )?),
3399 state_root,
3400 ))
3401 }
3402
3403 pub fn open_locked_state_trie(&self, state_root: H256) -> Result<Trie, StoreError> {
3407 let trie_db = TrieWrapper::new(
3408 state_root,
3409 self.gated_snapshot(state_root)?,
3410 Box::new(state_trie_locked_backend(
3411 self.backend.as_ref(),
3412 self.last_written()?,
3413 )?),
3414 None,
3415 );
3416 Ok(Trie::open(Box::new(trie_db), state_root))
3417 }
3418
3419 pub fn open_storage_trie(
3422 &self,
3423 account_hash: H256,
3424 state_root: H256,
3425 storage_root: H256,
3426 ) -> Result<Trie, StoreError> {
3427 let trie_db = TrieWrapper::new(
3428 state_root,
3429 self.gated_snapshot(state_root)?,
3430 Box::new(BackendTrieDB::new_for_storages(
3431 self.backend.clone(),
3432 self.last_written()?,
3433 )?),
3434 Some(account_hash),
3435 );
3436 Ok(Trie::open(Box::new(trie_db), storage_root))
3437 }
3438
3439 fn open_state_trie_shared(
3443 &self,
3444 state_root: H256,
3445 read_view: Arc<dyn StorageReadView>,
3446 cache: Arc<TrieLayerCache>,
3447 last_written: Vec<u8>,
3448 ) -> Result<Trie, StoreError> {
3449 let trie_db = TrieWrapper::new(
3450 state_root,
3451 cache,
3452 Box::new(BackendTrieDB::new_for_accounts_with_view(
3453 self.backend.clone(),
3454 read_view,
3455 last_written,
3456 )?),
3457 None,
3458 );
3459 Ok(Trie::open(Box::new(trie_db), state_root))
3460 }
3461
3462 fn open_storage_trie_shared(
3464 &self,
3465 account_hash: H256,
3466 state_root: H256,
3467 storage_root: H256,
3468 read_view: Arc<dyn StorageReadView>,
3469 cache: Arc<TrieLayerCache>,
3470 last_written: Vec<u8>,
3471 ) -> Result<Trie, StoreError> {
3472 let trie_db = TrieWrapper::new(
3473 state_root,
3474 cache,
3475 Box::new(BackendTrieDB::new_for_storages_with_view(
3476 self.backend.clone(),
3477 read_view,
3478 last_written,
3479 )?),
3480 Some(account_hash),
3481 );
3482 Ok(Trie::open(Box::new(trie_db), storage_root))
3483 }
3484
3485 pub fn open_direct_storage_trie(
3488 &self,
3489 account_hash: H256,
3490 storage_root: H256,
3491 ) -> Result<Trie, StoreError> {
3492 Ok(Trie::open(
3493 Box::new(BackendTrieDB::new_for_account_storage(
3494 self.backend.clone(),
3495 account_hash,
3496 self.last_written()?,
3497 )?),
3498 storage_root,
3499 ))
3500 }
3501
3502 pub fn open_locked_storage_trie(
3505 &self,
3506 account_hash: H256,
3507 state_root: H256,
3508 storage_root: H256,
3509 ) -> Result<Trie, StoreError> {
3510 let trie_db = TrieWrapper::new(
3511 state_root,
3512 self.gated_snapshot(state_root)?,
3513 Box::new(state_trie_locked_backend(
3514 self.backend.as_ref(),
3515 self.last_written()?,
3516 )?),
3517 Some(account_hash),
3518 );
3519 Ok(Trie::open(Box::new(trie_db), storage_root))
3520 }
3521
3522 pub fn has_state_root(&self, state_root: H256) -> Result<bool, StoreError> {
3523 if state_root == EMPTY_TRIE_HASH {
3525 return Ok(true);
3526 }
3527 let trie = self.open_state_trie(state_root)?;
3528 let Some(root) = trie.db().get(Nibbles::default())? else {
3530 return Ok(false);
3531 };
3532 let root_hash = ethrex_trie::Node::decode(&root)?
3533 .compute_hash(&NativeCrypto)
3534 .finalize(&NativeCrypto);
3535 Ok(state_root == root_hash)
3536 }
3537
3538 pub fn is_state_in_layer_cache(&self, state_root: H256) -> Result<bool, StoreError> {
3547 let trie = self
3548 .trie_cache
3549 .read()
3550 .map_err(|_| StoreError::LockError)?
3551 .clone();
3552 Ok(trie.contains(state_root))
3553 }
3554
3555 pub fn highest_state_history_block_number(&self) -> Result<Option<BlockNumber>, StoreError> {
3561 let read = self.backend.begin_read()?;
3562 let Some(key) = read.last_key(STATE_HISTORY)? else {
3563 return Ok(None);
3564 };
3565 let arr = <[u8; 8]>::try_from(key.as_slice()).map_err(|_| {
3566 StoreError::Custom(format!(
3567 "STATE_HISTORY key has unexpected length: {}",
3568 key.len()
3569 ))
3570 })?;
3571 Ok(Some(BlockNumber::from_be_bytes(arr)))
3572 }
3573
3574 pub fn lowest_state_history_block_number(&self) -> Result<Option<BlockNumber>, StoreError> {
3579 let read = self.backend.begin_read()?;
3580 let Some(key) = read.first_key(STATE_HISTORY)? else {
3581 return Ok(None);
3582 };
3583 let arr = <[u8; 8]>::try_from(key.as_slice()).map_err(|_| {
3584 StoreError::Custom(format!(
3585 "STATE_HISTORY key has unexpected length: {}",
3586 key.len()
3587 ))
3588 })?;
3589 Ok(Some(BlockNumber::from_be_bytes(arr)))
3590 }
3591
3592 pub fn reorg_retention(&self) -> Result<u64, StoreError> {
3597 let cache = self.trie_cache.read().map_err(|_| StoreError::LockError)?;
3598 Ok(cache.commit_threshold() as u64)
3599 }
3600
3601 #[doc(hidden)]
3605 pub fn put_state_history_entry_for_test(
3606 &self,
3607 block_number: BlockNumber,
3608 encoded: &[u8],
3609 ) -> Result<(), StoreError> {
3610 let mut tx = self.backend.begin_write()?;
3611 tx.put(STATE_HISTORY, &block_number.to_be_bytes(), encoded)?;
3612 tx.commit()?;
3613 Ok(())
3614 }
3615
3616 pub fn install_overlay_for_reorg(
3637 &self,
3638 from_block: BlockNumber,
3639 to_block: BlockNumber,
3640 expected_hash: impl Fn(BlockNumber) -> Option<H256>,
3641 ) -> Result<(), StoreError> {
3642 let overlay =
3644 Overlay::from_journal(self.backend.as_ref(), from_block, to_block, expected_hash)
3645 .map_err(|e| StoreError::Custom(format!("overlay construction failed: {e}")))?;
3646
3647 let threshold = {
3648 let current = self.trie_cache.read().map_err(|_| StoreError::LockError)?;
3649 current.commit_threshold()
3650 };
3651 let mut fresh =
3654 TrieLayerCache::new_with_safe_commit(threshold, self.safe_commit_root.clone());
3655 fresh.set_overlay(Arc::new(overlay));
3656
3657 self.rendezvous_persist_worker("install_overlay_for_reorg")?;
3660
3661 let mut guard = self.trie_cache.write().map_err(|_| StoreError::LockError)?;
3662 *guard = Arc::new(fresh);
3663 Ok(())
3664 }
3665
3666 fn rendezvous_persist_worker(&self, caller: &str) -> Result<(), StoreError> {
3679 let (ack_tx, ack_rx) = sync_channel::<Result<(), StoreError>>(1);
3680 self.persist_tx
3681 .send(PersistMessage::Ping(ack_tx))
3682 .map_err(|e| {
3683 StoreError::Custom(format!("{caller}: failed to ping persist worker: {e}"))
3684 })?;
3685 ack_rx.recv().map_err(|e| {
3686 StoreError::Custom(format!("{caller}: persist worker ping ack failed: {e}"))
3687 })??;
3688 Ok(())
3689 }
3690
3691 pub fn reorg_overlay_size_hint(&self) -> Result<(usize, usize), StoreError> {
3696 let guard = self.trie_cache.read().map_err(|_| StoreError::LockError)?;
3697 match guard.overlay() {
3698 Some(ov) => Ok((ov.len(), ov.byte_size())),
3699 None => Ok((0, 0)),
3700 }
3701 }
3702
3703 pub fn set_journal_pruning_paused(&self, paused: bool) {
3707 self.journal_pruning_paused
3708 .store(paused, std::sync::atomic::Ordering::Release);
3709 }
3710
3711 pub fn clear_reorg_overlay(&self) -> Result<(), StoreError> {
3715 let mut guard = self.trie_cache.write().map_err(|_| StoreError::LockError)?;
3716 let mut updated = (**guard).clone();
3717 updated.clear_overlay();
3718 *guard = Arc::new(updated);
3719 Ok(())
3720 }
3721
3722 pub fn abort_reorg(&self) -> Result<(), StoreError> {
3728 self.rendezvous_persist_worker("abort_reorg")?;
3736 let mut guard = self.trie_cache.write().map_err(|_| StoreError::LockError)?;
3737 let threshold = guard.commit_threshold();
3738 *guard = Arc::new(TrieLayerCache::new_with_safe_commit(
3739 threshold,
3740 self.safe_commit_root.clone(),
3741 ));
3742 Ok(())
3743 }
3744
3745 pub fn ancestors(&self, block_hash: BlockHash) -> AncestorIterator {
3748 AncestorIterator {
3749 store: self.clone(),
3750 next_hash: block_hash,
3751 }
3752 }
3753
3754 pub fn is_canonical_sync(&self, block_hash: BlockHash) -> Result<bool, StoreError> {
3756 let Some(block_number) = self.get_block_number_sync(block_hash)? else {
3757 return Ok(false);
3758 };
3759 Ok(self
3760 .get_canonical_block_hash_sync(block_number)?
3761 .is_some_and(|h| h == block_hash))
3762 }
3763
3764 pub fn generate_flatkeyvalue(&self) -> Result<(), StoreError> {
3765 self.flatkeyvalue_control_tx
3766 .send(FKVGeneratorControlMessage::Continue)
3767 .map_err(|_| StoreError::Custom("FlatKeyValue thread disconnected.".to_string()))
3768 }
3769
3770 pub fn create_checkpoint(&self, path: impl AsRef<Path>) -> Result<(), StoreError> {
3771 self.backend.create_checkpoint(path.as_ref())?;
3772 init_metadata_file(path.as_ref())?;
3773 Ok(())
3774 }
3775
3776 pub fn get_store_directory(&self) -> Result<PathBuf, StoreError> {
3777 Ok(self.db_path.clone())
3778 }
3779
3780 async fn load_latest_block_number(&self) -> Result<Option<BlockNumber>, StoreError> {
3782 let key = chain_data_key(ChainDataIndex::LatestBlockNumber);
3783 self.read_async(CHAIN_DATA, key)
3784 .await?
3785 .map(|bytes| -> Result<BlockNumber, StoreError> {
3786 let array: [u8; 8] = bytes
3787 .try_into()
3788 .map_err(|_| StoreError::Custom("Invalid BlockNumber bytes".to_string()))?;
3789 Ok(BlockNumber::from_le_bytes(array))
3790 })
3791 .transpose()
3792 }
3793
3794 fn load_canonical_block_hash(
3795 &self,
3796 block_number: BlockNumber,
3797 ) -> Result<Option<BlockHash>, StoreError> {
3798 let txn = self.backend.begin_read()?;
3799 txn.get(
3800 CANONICAL_BLOCK_HASHES,
3801 block_number.to_le_bytes().as_slice(),
3802 )?
3803 .map(|bytes| H256::decode(bytes.as_slice()))
3804 .transpose()
3805 .map_err(StoreError::from)
3806 }
3807
3808 fn load_block_header(
3809 &self,
3810 block_number: BlockNumber,
3811 ) -> Result<Option<BlockHeader>, StoreError> {
3812 let Some(block_hash) = self.load_canonical_block_hash(block_number)? else {
3813 return Ok(None);
3814 };
3815 self.load_block_header_by_hash(block_hash)
3816 }
3817
3818 fn load_block_header_by_hash(
3820 &self,
3821 block_hash: BlockHash,
3822 ) -> Result<Option<BlockHeader>, StoreError> {
3823 let txn = self.backend.begin_read()?;
3824 let hash_key = block_hash.encode_to_vec();
3825 let header_value = txn.get(HEADERS, hash_key.as_slice())?;
3826 let mut header = header_value
3827 .map(|bytes| BlockHeaderRLP::from_bytes(bytes).to())
3828 .transpose()
3829 .map_err(StoreError::from)?;
3830 header.as_mut().inspect(|h| {
3831 let _ = h.hash.set(block_hash);
3833 });
3834 Ok(header)
3835 }
3836
3837 pub fn last_written(&self) -> Result<Vec<u8>, StoreError> {
3838 let last_computed_flatkeyvalue = self
3839 .last_computed_flatkeyvalue
3840 .read()
3841 .map_err(|_| StoreError::LockError)?;
3842 Ok(last_computed_flatkeyvalue.clone())
3843 }
3844
3845 pub fn flatkeyvalue_fully_generated(&self) -> Result<bool, StoreError> {
3856 let tx = self.backend.begin_read()?;
3857 let marker = tx.get(MISC_VALUES, "last_written".as_bytes())?;
3858 Ok(Self::flatkeyvalue_generation_complete(marker.as_deref()))
3859 }
3860
3861 fn flatkeyvalue_generation_complete(marker: Option<&[u8]>) -> bool {
3865 marker == Some([0xff].as_slice())
3866 }
3867
3868 fn flatkeyvalue_computed_with_last_written(account: H256, last_written: &[u8]) -> bool {
3869 let account_nibbles = Nibbles::from_bytes(account.as_bytes());
3870 &last_written[0..64] > account_nibbles.as_ref()
3871 }
3872
3873 pub fn read_flushed_upto(&self) -> Result<BlockNumber, StoreError> {
3878 Ok(self.read_flushed_upto_opt()?.unwrap_or(0))
3879 }
3880
3881 fn read_flushed_upto_opt(&self) -> Result<Option<BlockNumber>, StoreError> {
3884 let tx = self.backend.begin_read()?;
3885 match tx.get(MISC_VALUES, FLUSHED_UPTO_KEY)? {
3886 Some(bytes) => Ok(Some(decode_flushed_upto(&bytes)?)),
3887 None => Ok(None),
3888 }
3889 }
3890
3891 #[cfg(any(test, feature = "testing"))]
3894 pub fn buffer_block_for_test(&self, block: &Block) {
3895 mutate_block_buffer(&self.block_data_buffer, |b| {
3896 b.insert(block.clone(), vec![], vec![])
3897 })
3898 .expect("block_data_buffer lock poisoned");
3899 }
3900
3901 #[cfg(any(test, feature = "testing"))]
3904 pub fn flush_block_data_for_test(&self) -> Result<(), StoreError> {
3905 flush_block_data(self.backend.as_ref(), &self.block_data_buffer)
3906 }
3907
3908 #[cfg(any(test, feature = "testing"))]
3912 pub fn get_trie_node_for_test(
3913 &self,
3914 is_account: bool,
3915 key: &[u8],
3916 ) -> Result<Option<Vec<u8>>, StoreError> {
3917 let table = if is_account {
3918 ACCOUNT_TRIE_NODES
3919 } else {
3920 STORAGE_TRIE_NODES
3921 };
3922 self.backend.begin_read()?.get(table, key)
3923 }
3924
3925 #[cfg(any(test, feature = "testing"))]
3929 pub fn buffer_block_with_codes_for_test(&self, block: &Block, codes: Vec<(H256, Code)>) {
3930 mutate_block_buffer(&self.block_data_buffer, |b| {
3931 b.insert(block.clone(), vec![], codes)
3932 })
3933 .expect("block_data_buffer lock poisoned");
3934 }
3935
3936 #[cfg(any(test, feature = "testing"))]
3941 pub fn register_pending_root_for_test(&self, root: H256) -> Result<(), StoreError> {
3942 self.pending_trie_roots.register(root)
3943 }
3944
3945 #[cfg(any(test, feature = "testing"))]
3948 pub fn clear_pending_root_for_test(&self, root: H256) {
3949 self.pending_trie_roots.clear(root)
3950 }
3951
3952 async fn anchor_to_durable_head(&self, latest: BlockNumber) -> Result<(), StoreError> {
3971 let marker = self.read_flushed_upto_opt()?;
3972 let start = match marker {
3973 Some(flushed) => flushed.min(latest),
3974 None => latest,
3975 };
3976
3977 let mut head = start;
3978 let latest_block_header = loop {
3979 match self.load_block_header(head)? {
3980 Some(header) => break header,
3981 None if marker.is_none() => return Err(StoreError::MissingLatestBlockNumber),
3984 None if head == 0 => return Err(StoreError::MissingLatestBlockNumber),
3985 None => {
3986 warn!(
3987 "durable head {head}: canonical hash has no on-disk header \
3988 (reorg inside flush window); rewinding"
3989 );
3990 head -= 1;
3991 }
3992 }
3993 };
3994 self.latest_block_header.update(latest_block_header);
3995
3996 let reanchor = head != latest;
4001 let rewrite_marker = marker != Some(head);
4002 if reanchor || rewrite_marker {
4003 let mut tx = self.backend.begin_write()?;
4004 if reanchor {
4005 let latest_key = chain_data_key(ChainDataIndex::LatestBlockNumber);
4008 tx.put(CHAIN_DATA, &latest_key, &head.to_le_bytes())?;
4009 }
4010 if rewrite_marker {
4011 write_flushed_upto(tx.as_mut(), head)?;
4012 }
4013 tx.commit()?;
4014 }
4015 Ok(())
4016 }
4017}
4018
4019pub fn write_flushed_upto(
4023 tx: &mut dyn StorageWriteBatch,
4024 n: BlockNumber,
4025) -> Result<(), StoreError> {
4026 tx.put(MISC_VALUES, FLUSHED_UPTO_KEY, &n.to_le_bytes())
4027}
4028
4029fn decode_flushed_upto(bytes: &[u8]) -> Result<BlockNumber, StoreError> {
4035 let arr: [u8; 8] = bytes
4036 .try_into()
4037 .map_err(|_| StoreError::Custom("Invalid flushed_upto bytes".to_string()))?;
4038 Ok(BlockNumber::from_le_bytes(arr))
4039}
4040
4041fn mutate_block_buffer(
4044 buffer: &Arc<RwLock<Arc<BlockDataBuffer>>>,
4045 f: impl FnOnce(&mut BlockDataBuffer),
4046) -> Result<(), StoreError> {
4047 let mut new_buf = (*buffer.read().map_err(|_| StoreError::LockError)?.clone()).clone();
4048 f(&mut new_buf);
4049 *buffer.write().map_err(|_| StoreError::LockError)? = Arc::new(new_buf);
4050 Ok(())
4051}
4052
4053const DEFAULT_PERSIST_CHANNEL_CAPACITY: usize = 2;
4055
4056struct BlockPersist {
4062 blocks: Vec<(Block, Vec<Receipt>)>,
4063 codes: Vec<(H256, Code)>,
4064 parent_state_root: H256,
4065 child_state_root: H256,
4066 account_updates: TrieNodesUpdate,
4067 storage_updates: Vec<(H256, TrieNodesUpdate)>,
4068 commit_depth: Option<usize>,
4069 wait_for_flush: bool,
4070 block_number: BlockNumber,
4076 block_hash: H256,
4078 ack: std::sync::mpsc::SyncSender<Result<(), StoreError>>,
4079}
4080
4081enum PersistMessage {
4085 Block(Box<BlockPersist>),
4086 Commit(H256),
4092 Ping(std::sync::mpsc::SyncSender<Result<(), StoreError>>),
4093 Shutdown {
4098 ack: std::sync::mpsc::SyncSender<Result<(), StoreError>>,
4099 },
4100}
4101
4102fn write_block_data(
4107 tx: &mut dyn StorageWriteBatch,
4108 number: BlockNumber,
4109 hash: BlockHash,
4110 header: &BlockHeader,
4111 body: &BlockBody,
4112) -> Result<(), StoreError> {
4113 let hash_key = hash.encode_to_vec();
4114 tx.put(
4115 HEADERS,
4116 &hash_key,
4117 BlockHeaderRLP::from(header.clone()).bytes(),
4118 )?;
4119 tx.put(
4120 BODIES,
4121 &hash_key,
4122 BlockBodyRLP::from_bytes(body.encode_to_vec()).bytes(),
4123 )?;
4124 tx.put(BLOCK_NUMBERS, &hash_key, &number.to_le_bytes())?;
4125 for (index, transaction) in body.transactions.iter().enumerate() {
4126 tx.merge(
4127 TRANSACTION_LOCATIONS,
4128 transaction.hash(&NativeCrypto).as_bytes(),
4129 &encode_tx_location_operand(number, hash, index as u64),
4130 )?;
4131 }
4132 Ok(())
4133}
4134
4135fn flush_block_data(
4138 backend: &dyn StorageBackend,
4139 buffer: &Arc<RwLock<Arc<BlockDataBuffer>>>,
4140) -> Result<(), StoreError> {
4141 let snapshot = buffer.read().map_err(|_| StoreError::LockError)?.clone();
4142 let to_flush = snapshot.flushable();
4143 if to_flush.is_empty() {
4144 return Ok(());
4145 }
4146 let hashes: Vec<_> = to_flush.iter().map(|b| b.header.hash()).collect();
4147 let codes = snapshot.codes_for(&hashes);
4148 let mut max_number = snapshot.flushed_upto();
4149
4150 let mut tx = backend.begin_write()?;
4151 for b in &to_flush {
4152 let hash = b.header.hash();
4153 write_block_data(tx.as_mut(), b.number, hash, &b.header, &b.body)?;
4154 for (index, receipt) in b.receipts.iter().enumerate() {
4155 tx.put(
4156 RECEIPTS_V2,
4157 &receipt_key(&hash, index as u64),
4158 &receipt.encode_to_vec(),
4159 )?;
4160 }
4161 max_number = max_number.max(b.number);
4162 }
4163 for (code_hash, code) in codes {
4164 let buf = encode_code(&code);
4165 tx.put(ACCOUNT_CODES, code_hash.as_ref(), &buf)?;
4166 tx.put(
4167 ACCOUNT_CODE_METADATA,
4168 code_hash.as_ref(),
4169 &(code.len() as u64).to_be_bytes(),
4170 )?;
4171 }
4172 write_flushed_upto(tx.as_mut(), max_number)?;
4173 tx.commit()?;
4174
4175 mutate_block_buffer(buffer, |b| b.evict_flushed(max_number))
4177}
4178
4179type TrieNodesUpdate = Vec<(Nibbles, Vec<u8>)>;
4180
4181#[derive(Debug, Default)]
4187struct PendingTrieRoots {
4188 count: AtomicUsize,
4190 roots: Mutex<HashSet<H256>>,
4191 ready: Condvar,
4192}
4193
4194impl PendingTrieRoots {
4195 fn register(&self, root: H256) -> Result<(), StoreError> {
4199 let mut roots = self.roots.lock().map_err(|_| StoreError::LockError)?;
4200 if roots.insert(root) {
4201 self.count.fetch_add(1, Ordering::Release);
4202 }
4203 Ok(())
4204 }
4205
4206 fn clear(&self, root: H256) {
4211 let Ok(mut roots) = self.roots.lock() else {
4212 return;
4213 };
4214 if roots.remove(&root) {
4215 self.count.fetch_sub(1, Ordering::Release);
4216 self.ready.notify_all();
4217 }
4218 }
4219
4220 fn wait_until_ready(&self, root: H256) -> Result<(), StoreError> {
4223 if self.count.load(Ordering::Acquire) == 0 {
4224 return Ok(());
4225 }
4226 let mut roots = self.roots.lock().map_err(|_| StoreError::LockError)?;
4227 while roots.contains(&root) {
4228 roots = self.ready.wait(roots).map_err(|_| StoreError::LockError)?;
4229 }
4230 Ok(())
4231 }
4232}
4233
4234#[allow(clippy::too_many_arguments)]
4238fn apply_trie_phase1(
4239 trie_cache: &Arc<RwLock<Arc<TrieLayerCache>>>,
4240 pending_roots: &PendingTrieRoots,
4241 parent_state_root: H256,
4242 child_state_root: H256,
4243 block_number: BlockNumber,
4244 block_hash: H256,
4245 account_updates: TrieNodesUpdate,
4246 storage_updates: Vec<(H256, TrieNodesUpdate)>,
4247) -> Result<(), StoreError> {
4248 let build: Result<(), StoreError> = (|| {
4249 let new_layer = storage_updates
4250 .into_iter()
4251 .flat_map(|(account_hash, nodes)| {
4252 nodes
4253 .into_iter()
4254 .map(move |(path, node)| (apply_prefix(Some(account_hash), path), node))
4255 })
4256 .chain(account_updates)
4257 .collect();
4258 let trie = trie_cache
4259 .read()
4260 .map_err(|_| StoreError::LockError)?
4261 .clone();
4262 let mut trie_mut = (*trie).clone();
4263 trie_mut.put_batch(
4264 parent_state_root,
4265 child_state_root,
4266 block_number,
4267 block_hash,
4268 new_layer,
4269 );
4270 *trie_cache.write().map_err(|_| StoreError::LockError)? = Arc::new(trie_mut);
4271 Ok(())
4272 })();
4273 pending_roots.clear(child_state_root);
4278 build
4279}
4280
4281fn commit_trie_if_due(
4305 backend: &dyn StorageBackend,
4306 trie_cache: &Arc<RwLock<Arc<TrieLayerCache>>>,
4307 fkv_ctl: &SyncSender<FKVGeneratorControlMessage>,
4308 parent_state_root: H256,
4309 commit_depth: Option<usize>,
4310 is_batch: bool,
4311) -> Result<(), StoreError> {
4312 let trie = trie_cache
4313 .read()
4314 .map_err(|_| StoreError::LockError)?
4315 .clone();
4316 let commitable = match commit_depth {
4318 Some(depth) => trie.get_commitable_by_depth(parent_state_root, depth),
4319 None => trie.get_commitable(parent_state_root),
4320 };
4321 let Some(root) = commitable else {
4322 return Ok(());
4324 };
4325 commit_to_disk(backend, fkv_ctl, trie_cache, &trie, root, is_batch)
4326}
4327
4328fn commit_to_disk(
4338 backend: &dyn StorageBackend,
4339 fkv_ctl: &SyncSender<FKVGeneratorControlMessage>,
4340 trie_cache: &Arc<RwLock<Arc<TrieLayerCache>>>,
4341 trie: &Arc<TrieLayerCache>,
4342 root: H256,
4343 is_batch: bool,
4344) -> Result<(), StoreError> {
4345 if !trie.has_layer(root) {
4349 debug!(
4350 root = ?root,
4351 layers = trie.layer_count(),
4352 is_batch,
4353 "Skipping trie commit: state root has no in-memory layer. Expected when the block \
4354 did not change the state root (empty L2 blocks) or the root was already flushed."
4355 );
4356 return Ok(());
4357 }
4358
4359 let _ = fkv_ctl.send(FKVGeneratorControlMessage::Stop);
4362
4363 let mut trie_mut = (**trie).clone();
4365
4366 let read_view = backend.begin_read()?;
4380 let last_written = read_view
4381 .get(MISC_VALUES, "last_written".as_bytes())?
4382 .unwrap_or_default();
4383
4384 let mut write_tx = backend.begin_write()?;
4385
4386 let overlay_for_reconciliation = if !is_batch {
4395 trie.overlay().cloned()
4396 } else {
4397 None
4398 };
4399
4400 let root = if overlay_for_reconciliation.is_some() {
4410 trie.bottom_layer_root(root)
4411 } else {
4412 root
4413 };
4414
4415 let committed_layers = trie_mut.commit(root).ok_or_else(|| {
4425 StoreError::Custom(format!(
4426 "trie layer for state root {root:?} disappeared between the has_layer check and \
4427 commit (layers={}, is_batch={is_batch}): the TrieLayerCache clone lost the layer \
4428 map, so this block's state was not written",
4429 trie.layer_count(),
4430 ))
4431 })?;
4432
4433 debug_assert!(
4438 overlay_for_reconciliation.is_none() || committed_layers.len() == 1,
4439 "overlay-backed reconciliation must commit exactly one layer (T), got {}",
4440 committed_layers.len()
4441 );
4442
4443 let extra_writes: Vec<(Vec<u8>, Vec<u8>)> = match &overlay_for_reconciliation {
4449 Some(overlay) => {
4450 let layer_keys: rustc_hash::FxHashSet<&Vec<u8>> = committed_layers
4451 .iter()
4452 .flat_map(|l| l.nodes.iter().map(|(k, _)| k))
4453 .collect();
4454 overlay
4455 .iter_all_entries()
4456 .filter(|(_, key, _)| !layer_keys.contains(key))
4457 .map(|(_, key, value)| (key.clone(), value.clone().unwrap_or_default()))
4458 .collect()
4459 }
4460 None => Vec::new(),
4461 };
4462 let reorg_heights = overlay_for_reconciliation
4464 .as_ref()
4465 .map(|ov| (ov.to_block(), ov.from_block()));
4466
4467 let mut overlay: HashMap<Vec<u8>, Option<Vec<u8>>> = HashMap::new();
4477
4478 let mut result = Ok(());
4479 'layers: for layer in &committed_layers {
4480 let mut journal_account_trie: FlatDiff = Vec::new();
4486 let mut journal_storage_trie: FlatDiff = Vec::new();
4487 let mut journal_account_flat: FlatDiff = Vec::new();
4488 let mut journal_storage_flat: FlatDiff = Vec::new();
4489
4490 let is_reconciliation_layer = overlay_for_reconciliation.is_some();
4501 let extra: &[(Vec<u8>, Vec<u8>)] = if is_reconciliation_layer {
4502 &extra_writes
4503 } else {
4504 &[]
4505 };
4506
4507 for (key, value) in layer.nodes.iter().chain(extra.iter()) {
4508 let (is_leaf, is_account) = classify_trie_key(key.len());
4509
4510 if is_leaf && key.as_slice() > last_written.as_slice() {
4515 continue;
4516 }
4517 let table = if is_leaf {
4518 if is_account {
4519 &ACCOUNT_FLATKEYVALUE
4520 } else {
4521 &STORAGE_FLATKEYVALUE
4522 }
4523 } else if is_account {
4524 &ACCOUNT_TRIE_NODES
4525 } else {
4526 &STORAGE_TRIE_NODES
4527 };
4528
4529 let prev_value = if !is_batch {
4532 match overlay.get(key) {
4533 Some(v) => Some(v.clone()),
4534 None => match read_view.get(table, key) {
4535 Ok(v) => Some(v),
4536 Err(e) => {
4537 result = Err(e);
4538 break 'layers;
4539 }
4540 },
4541 }
4542 } else {
4543 None
4544 };
4545
4546 let new_value = if value.is_empty() {
4547 result = write_tx.delete(table, key);
4548 None
4549 } else {
4550 result = write_tx.put(table, key, value);
4551 Some(value.clone())
4552 };
4553 if result.is_err() {
4554 break 'layers;
4555 }
4556
4557 if let Some(prev) = prev_value {
4560 let bucket = match (is_leaf, is_account) {
4561 (false, true) => &mut journal_account_trie,
4562 (false, false) => &mut journal_storage_trie,
4563 (true, true) => &mut journal_account_flat,
4564 (true, false) => &mut journal_storage_flat,
4565 };
4566 bucket.push((key.clone(), prev));
4567 overlay.insert(key.clone(), new_value);
4570 }
4571 }
4572
4573 if !is_batch && let Some((t, d)) = reorg_heights {
4578 debug_assert_eq!(
4579 layer.block_number, t,
4580 "first new-chain commit must be at the pivot's T height (overlay.to_block)"
4581 );
4582 if layer.block_number == t {
4589 let start = t.to_be_bytes();
4590 let end = d.saturating_add(1).to_be_bytes();
4591 result = write_tx.delete_range(STATE_HISTORY, &start, &end);
4592 if result.is_err() {
4593 break 'layers;
4594 }
4595 } else {
4596 error!(
4597 block_number = layer.block_number,
4598 t,
4599 d,
4600 "deep-reorg reconciliation skipped: committed layer not at pivot height; skipping STATE_HISTORY delete_range to avoid history gaps"
4601 );
4602 }
4603 }
4604
4605 if !is_batch {
4611 let entry = JournalEntry {
4612 block_hash: layer.block_hash,
4613 parent_state_root: layer.parent_state_root,
4614 account_trie_diff: journal_account_trie,
4615 storage_trie_diff: journal_storage_trie,
4616 account_flat_diff: journal_account_flat,
4617 storage_flat_diff: journal_storage_flat,
4618 };
4619 result = write_tx.put(
4620 STATE_HISTORY,
4621 &layer.block_number.to_be_bytes(),
4622 &entry.encode(),
4623 );
4624 if result.is_err() {
4625 break 'layers;
4626 }
4627 }
4628 }
4629
4630 if result.is_ok() {
4631 result = write_tx.commit();
4632 }
4633 let _ = fkv_ctl.send(FKVGeneratorControlMessage::Continue);
4635 result?;
4636
4637 if overlay_for_reconciliation.is_some() {
4640 trie_mut.clear_overlay();
4641 }
4642
4643 *trie_cache.write().map_err(|_| StoreError::LockError)? = Arc::new(trie_mut);
4649 Ok(())
4650}
4651
4652fn flatkeyvalue_generator(
4655 backend: &Arc<dyn StorageBackend>,
4656 last_computed_fkv: &RwLock<Vec<u8>>,
4657 control_rx: &std::sync::mpsc::Receiver<FKVGeneratorControlMessage>,
4658) -> Result<(), StoreError> {
4659 info!("Generation of FlatKeyValue started.");
4660 let initial_last_written = backend
4661 .begin_read()?
4662 .get(MISC_VALUES, "last_written".as_bytes())?
4663 .unwrap_or_default();
4664
4665 if initial_last_written.is_empty() {
4666 backend.clear_table(ACCOUNT_FLATKEYVALUE)?;
4668 backend.clear_table(STORAGE_FLATKEYVALUE)?;
4669 } else if initial_last_written == [0xff] {
4670 info!("FlatKeyValue already generated. Skipping.");
4672 return Ok(());
4673 }
4674
4675 loop {
4676 let read_tx = backend.begin_read()?;
4679 let root = read_tx
4680 .get(ACCOUNT_TRIE_NODES, &[])?
4681 .ok_or(StoreError::MissingLatestBlockNumber)?;
4682 let root: Node = ethrex_trie::Node::decode(&root)?;
4683 let state_root = root.compute_hash(&NativeCrypto).finalize(&NativeCrypto);
4684
4685 let last_written = read_tx
4686 .get(MISC_VALUES, "last_written".as_bytes())?
4687 .unwrap_or_default();
4688 let last_written_account = last_written
4689 .get(0..64)
4690 .map(|v| Nibbles::from_hex(v.to_vec()))
4691 .unwrap_or_default();
4692 let mut last_written_storage = last_written
4693 .get(66..130)
4694 .map(|v| Nibbles::from_hex(v.to_vec()))
4695 .unwrap_or_default();
4696
4697 debug!("Starting FlatKeyValue loop pivot={last_written:?} SR={state_root:x}");
4698
4699 let mut ctr = 0;
4700 let mut write_txn = backend.begin_write()?;
4701 let mut iter = Trie::open(
4702 Box::new(BackendTrieDB::new_for_accounts_with_view(
4703 backend.clone(),
4704 read_tx.clone(),
4705 last_written.clone(),
4706 )?),
4707 state_root,
4708 )
4709 .into_iter();
4710 if last_written_account > Nibbles::default() {
4711 iter.advance(last_written_account.to_bytes())?;
4712 }
4713 let res = iter.try_for_each(|(path, node)| -> Result<(), StoreError> {
4714 let Node::Leaf(node) = node else {
4715 return Ok(());
4716 };
4717 let account_state = AccountState::decode(&node.value)?;
4718 let account_hash = H256::from_slice(&path.to_bytes());
4719 write_txn.put(MISC_VALUES, "last_written".as_bytes(), path.as_ref())?;
4720 write_txn.put(ACCOUNT_FLATKEYVALUE, path.as_ref(), &node.value)?;
4721 ctr += 1;
4722 if ctr > 10_000 {
4723 write_txn.commit()?;
4724 write_txn = backend.begin_write()?;
4725 *last_computed_fkv
4726 .write()
4727 .map_err(|_| StoreError::LockError)? = path.as_ref().to_vec();
4728 ctr = 0;
4729 }
4730
4731 let mut iter_inner = Trie::open(
4732 Box::new(BackendTrieDB::new_for_account_storage_with_view(
4733 backend.clone(),
4734 read_tx.clone(),
4735 account_hash,
4736 path.as_ref().to_vec(),
4737 )?),
4738 account_state.storage_root,
4739 )
4740 .into_iter();
4741 if last_written_storage > Nibbles::default() {
4742 iter_inner.advance(last_written_storage.to_bytes())?;
4743 last_written_storage = Nibbles::default();
4744 }
4745 iter_inner.try_for_each(|(path, node)| -> Result<(), StoreError> {
4746 let Node::Leaf(node) = node else {
4747 return Ok(());
4748 };
4749 let key = apply_prefix(Some(account_hash), path);
4750 write_txn.put(MISC_VALUES, "last_written".as_bytes(), key.as_ref())?;
4751 write_txn.put(STORAGE_FLATKEYVALUE, key.as_ref(), &node.value)?;
4752 ctr += 1;
4753 if ctr > 10_000 {
4754 write_txn.commit()?;
4755 write_txn = backend.begin_write()?;
4756 *last_computed_fkv
4757 .write()
4758 .map_err(|_| StoreError::LockError)? = key.into_vec();
4759 ctr = 0;
4760 }
4761 fkv_check_for_stop_msg(control_rx)?;
4762 Ok(())
4763 })?;
4764 fkv_check_for_stop_msg(control_rx)?;
4765 Ok(())
4766 });
4767 match res {
4768 Err(StoreError::PivotChanged) => {
4769 match control_rx.recv() {
4770 Ok(FKVGeneratorControlMessage::Continue) => {}
4771 Ok(FKVGeneratorControlMessage::Stop) => {
4772 return Err(StoreError::Custom("Unexpected Stop message".to_string()));
4773 }
4774 Err(std::sync::mpsc::RecvError) => {
4776 info!("Store closed, stopping FlatKeyValue generation.");
4777 return Ok(());
4778 }
4779 }
4780 }
4781 Err(err) => return Err(err),
4782 Ok(()) => {
4783 write_txn.put(MISC_VALUES, "last_written".as_bytes(), &[0xff])?;
4784 write_txn.commit()?;
4785 *last_computed_fkv
4786 .write()
4787 .map_err(|_| StoreError::LockError)? = vec![0xff; 131];
4788 info!("FlatKeyValue generation finished.");
4789 return Ok(());
4790 }
4791 };
4792 }
4793}
4794
4795fn fkv_check_for_stop_msg(
4796 control_rx: &std::sync::mpsc::Receiver<FKVGeneratorControlMessage>,
4797) -> Result<(), StoreError> {
4798 match control_rx.try_recv() {
4799 Ok(FKVGeneratorControlMessage::Stop) | Err(TryRecvError::Disconnected) => {
4800 return Err(StoreError::PivotChanged);
4801 }
4802 Ok(FKVGeneratorControlMessage::Continue) => {
4803 return Err(StoreError::Custom(
4804 "Unexpected Continue message".to_string(),
4805 ));
4806 }
4807 Err(TryRecvError::Empty) => {}
4808 }
4809 Ok(())
4810}
4811
4812fn state_trie_locked_backend(
4813 backend: &dyn StorageBackend,
4814 last_written: Vec<u8>,
4815) -> Result<BackendTrieDBLocked, StoreError> {
4816 BackendTrieDBLocked::new(backend, last_written)
4818}
4819
4820pub struct AccountProof {
4821 pub proof: Vec<NodeRLP>,
4822 pub account: AccountState,
4823 pub storage_proof: Vec<StorageSlotProof>,
4824}
4825
4826pub struct StorageSlotProof {
4827 pub proof: Vec<NodeRLP>,
4828 pub key: H256,
4829 pub value: U256,
4830}
4831
4832pub struct AncestorIterator {
4833 store: Store,
4834 next_hash: BlockHash,
4835}
4836
4837impl Iterator for AncestorIterator {
4838 type Item = Result<(BlockHash, BlockHeader), StoreError>;
4839
4840 fn next(&mut self) -> Option<Self::Item> {
4841 let next_hash = self.next_hash;
4842 match self.store.get_block_header_by_hash(next_hash) {
4846 Ok(Some(header)) => {
4847 let ret_hash = self.next_hash;
4848 self.next_hash = header.parent_hash;
4849 Some(Ok((ret_hash, header)))
4850 }
4851 Ok(None) => None,
4852 Err(e) => Some(Err(e)),
4853 }
4854 }
4855}
4856
4857pub fn hash_address(address: &Address) -> Vec<u8> {
4858 keccak_hash(address.to_fixed_bytes()).to_vec()
4859}
4860
4861fn hash_address_fixed(address: &Address) -> H256 {
4862 keccak(address.to_fixed_bytes())
4863}
4864
4865pub fn hash_key(key: &H256) -> Vec<u8> {
4866 keccak_hash(key.to_fixed_bytes()).to_vec()
4867}
4868
4869pub fn hash_key_fixed(key: &H256) -> [u8; 32] {
4870 keccak_hash(key.to_fixed_bytes())
4871}
4872
4873fn chain_data_key(index: ChainDataIndex) -> Vec<u8> {
4874 (index as u8).encode_to_vec()
4875}
4876
4877fn snap_state_key(index: SnapStateIndex) -> Vec<u8> {
4878 (index as u8).encode_to_vec()
4879}
4880
4881pub fn receipt_key(block_hash: &BlockHash, index: u64) -> Vec<u8> {
4883 let mut key = Vec::with_capacity(40);
4884 key.extend_from_slice(block_hash.as_bytes());
4885 key.extend_from_slice(&index.to_be_bytes());
4886 key
4887}
4888
4889fn encode_code(code: &Code) -> Vec<u8> {
4890 let mut buf =
4891 Vec::with_capacity(6 + code.len() + std::mem::size_of_val::<[u32]>(&code.jump_targets));
4892 code.code().encode(&mut buf);
4893 code.jump_targets.to_vec().encode(&mut buf);
4896 buf
4897}
4898
4899#[derive(Debug, Default, Clone)]
4900struct LatestBlockHeaderCache {
4901 current: Arc<Mutex<Arc<BlockHeader>>>,
4902}
4903
4904impl LatestBlockHeaderCache {
4905 pub fn get(&self) -> Arc<BlockHeader> {
4906 self.current.lock().expect("poisoned mutex").clone()
4907 }
4908
4909 pub fn update(&self, header: BlockHeader) {
4910 let new = Arc::new(header);
4911 *self.current.lock().expect("poisoned mutex") = new;
4912 }
4913}
4914
4915#[derive(Debug, Serialize, Deserialize)]
4916pub struct StoreMetadata {
4917 pub schema_version: u64,
4918}
4919
4920impl StoreMetadata {
4921 pub fn new(schema_version: u64) -> Self {
4922 Self { schema_version }
4923 }
4924}
4925
4926fn read_store_schema_version(path: &Path) -> Result<Option<u64>, StoreError> {
4931 let metadata_path = path.join(STORE_METADATA_FILENAME);
4932 if !metadata_path.exists() {
4933 return Ok(None);
4934 }
4935 if !metadata_path.is_file() {
4936 return Err(StoreError::Custom(
4937 "store schema path exists but is not a file".to_string(),
4938 ));
4939 }
4940 let file_contents = std::fs::read_to_string(metadata_path)?;
4941 let metadata: StoreMetadata = serde_json::from_str(&file_contents)?;
4942 Ok(Some(metadata.schema_version))
4943}
4944
4945fn init_metadata_file(parent_path: &Path) -> Result<(), StoreError> {
4946 std::fs::create_dir_all(parent_path)?;
4947
4948 let metadata_path = parent_path.join(STORE_METADATA_FILENAME);
4949 let metadata = StoreMetadata::new(STORE_SCHEMA_VERSION);
4950 let serialized_metadata = serde_json::to_string_pretty(&metadata)?;
4951 let mut new_file = std::fs::File::create_new(metadata_path)?;
4952 new_file.write_all(serialized_metadata.as_bytes())?;
4953 Ok(())
4954}
4955
4956fn dir_contains_legacy_db(path: &Path) -> Result<bool, StoreError> {
4968 if path.join("CURRENT").is_file() {
4972 return Ok(true);
4973 }
4974 for entry in std::fs::read_dir(path)? {
4978 let entry = entry?;
4979 if !entry.file_type()?.is_file() {
4980 continue;
4981 }
4982 if entry.file_name().to_string_lossy().starts_with("MANIFEST-") {
4983 return Ok(true);
4984 }
4985 }
4986 Ok(false)
4987}
4988
4989pub fn has_valid_db(path: &Path) -> bool {
4993 let metadata_path = path.join(STORE_METADATA_FILENAME);
4994 if !metadata_path.is_file() {
4995 return false;
4996 }
4997 let Ok(contents) = std::fs::read_to_string(&metadata_path) else {
4998 return false;
4999 };
5000 let Ok(metadata) = serde_json::from_str::<StoreMetadata>(&contents) else {
5001 return false;
5002 };
5003 metadata.schema_version >= 1 && metadata.schema_version <= STORE_SCHEMA_VERSION
5004}
5005
5006pub fn read_chain_id_from_db(path: &Path) -> Option<u64> {
5015 if !has_valid_db(path) {
5016 return None;
5017 }
5018 #[cfg(feature = "rocksdb")]
5019 {
5020 let backend = match RocksDBBackend::open(path, DEFAULT_ROCKSDB_BLOCK_CACHE_SIZE_BYTES) {
5023 Ok(backend) => backend,
5024 Err(e) => {
5025 warn!("Failed to open RocksDB at {path:?} to read chain ID: {e}");
5026 return None;
5027 }
5028 };
5029 let read = match backend.begin_read() {
5030 Ok(read) => read,
5031 Err(e) => {
5032 warn!("Failed to begin read transaction at {path:?}: {e}");
5033 return None;
5034 }
5035 };
5036 let key = chain_data_key(ChainDataIndex::ChainConfig);
5037 let bytes = match read.get(CHAIN_DATA, &key) {
5038 Ok(Some(bytes)) => bytes,
5039 Ok(None) => {
5040 warn!("Chain config entry not found in database at {path:?}");
5041 return None;
5042 }
5043 Err(e) => {
5044 warn!("Failed to read chain config from database at {path:?}: {e}");
5045 return None;
5046 }
5047 };
5048 #[derive(serde::Deserialize)]
5053 #[serde(rename_all = "camelCase")]
5054 struct ChainIdOnly {
5055 chain_id: u64,
5056 }
5057 match serde_json::from_slice::<ChainIdOnly>(&bytes) {
5058 Ok(partial) => Some(partial.chain_id),
5059 Err(e) => {
5060 warn!("Failed to deserialize chain ID from database at {path:?}: {e}");
5061 None
5062 }
5063 }
5064 }
5065 #[cfg(not(feature = "rocksdb"))]
5066 {
5067 let _ = path;
5068 None
5069 }
5070}
5071
5072#[cfg(test)]
5073mod state_history_tests {
5074 use super::*;
5075 use crate::api::tables::STATE_HISTORY;
5076 use crate::backend::in_memory::InMemoryBackend;
5077 use crate::journal::JournalEntry;
5078 use ethrex_common::types::{BlockBody, BlockHeader};
5079 use ethrex_trie::Nibbles;
5080 use std::time::{Duration, Instant};
5081
5082 fn make_block(number: BlockNumber, parent_hash: H256, state_root: H256) -> Block {
5083 let header = BlockHeader {
5084 number,
5085 parent_hash,
5086 state_root,
5087 ..Default::default()
5088 };
5089 Block::new(header, BlockBody::default())
5090 }
5091
5092 fn await_journal_entry(
5096 backend: &Arc<dyn StorageBackend>,
5097 block_number: BlockNumber,
5098 timeout: Duration,
5099 ) -> Option<Vec<u8>> {
5100 let key = block_number.to_be_bytes();
5101 let deadline = Instant::now() + timeout;
5102 loop {
5103 let read = backend.begin_read().ok()?;
5104 if let Ok(Some(v)) = read.get(STATE_HISTORY, &key) {
5105 return Some(v);
5106 }
5107 if Instant::now() >= deadline {
5108 return None;
5109 }
5110 std::thread::sleep(Duration::from_millis(10));
5111 }
5112 }
5113
5114 fn assert_no_journal_entry(backend: &Arc<dyn StorageBackend>, block_number: BlockNumber) {
5119 let window = Duration::from_millis(500);
5120 let key = block_number.to_be_bytes();
5121 let deadline = Instant::now() + window;
5122 loop {
5123 let read = backend.begin_read().expect("read view");
5124 let v = read.get(STATE_HISTORY, &key).expect("get");
5125 assert!(
5126 v.is_none(),
5127 "expected no STATE_HISTORY entry for block {block_number}, got {v:?}"
5128 );
5129 if Instant::now() >= deadline {
5130 return;
5131 }
5132 std::thread::sleep(Duration::from_millis(20));
5133 }
5134 }
5135
5136 #[test]
5142 fn journal_entry_written_per_block_in_regular_mode() {
5143 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
5144 let dir = tempfile::tempdir().unwrap();
5145 let store = Store::from_backend(
5146 backend.clone(),
5147 dir.path().to_path_buf(),
5148 1,
5149 DEFAULT_PERSIST_CHANNEL_CAPACITY,
5150 )
5151 .unwrap();
5152
5153 let state_root_1 = H256::repeat_byte(0x11);
5154 let block1 = make_block(1, H256::zero(), state_root_1);
5155 let block1_hash = block1.hash();
5156 store
5157 .store_block_updates(UpdateBatch {
5158 account_updates: vec![(Nibbles::from_raw(&[0x00, 0x01], false), vec![0xab, 0xcd])],
5159 storage_updates: vec![],
5160 blocks: vec![block1],
5161 receipts: vec![],
5162 code_updates: vec![],
5163 commit_depth: None,
5164 wait_for_flush: false,
5165 })
5166 .unwrap();
5167
5168 store.set_safe_commit_root(state_root_1).unwrap();
5171 let state_root_2 = H256::repeat_byte(0x22);
5172 let block2 = make_block(2, block1_hash, state_root_2);
5173 let block2_hash = block2.hash();
5174 store
5175 .store_block_updates(UpdateBatch {
5176 account_updates: vec![(Nibbles::from_raw(&[0x00, 0x02], false), vec![0xef, 0x11])],
5177 storage_updates: vec![],
5178 blocks: vec![block2],
5179 receipts: vec![],
5180 code_updates: vec![],
5181 commit_depth: None,
5182 wait_for_flush: false,
5183 })
5184 .unwrap();
5185
5186 let bytes = await_journal_entry(&backend, 1, Duration::from_secs(2))
5187 .expect("STATE_HISTORY entry for block 1 should appear after block 2 commits it");
5188 let entry = JournalEntry::decode(&bytes).unwrap();
5189 assert_eq!(entry.block_hash, block1_hash);
5190 assert_eq!(entry.parent_state_root, H256::zero());
5191 assert!(!entry.account_trie_diff.is_empty());
5192 let (path, prev) = &entry.account_trie_diff[0];
5193 assert_eq!(prev, &None, "first-time write means previous value is None");
5194 assert!(path.len() < 65);
5195
5196 store.set_safe_commit_root(state_root_2).unwrap();
5199 let state_root_3 = H256::repeat_byte(0x33);
5200 let block3 = make_block(3, block2_hash, state_root_3);
5201 store
5202 .store_block_updates(UpdateBatch {
5203 account_updates: vec![(Nibbles::from_raw(&[0x00, 0x03], false), vec![0x77])],
5204 storage_updates: vec![],
5205 blocks: vec![block3],
5206 receipts: vec![],
5207 code_updates: vec![],
5208 commit_depth: None,
5209 wait_for_flush: false,
5210 })
5211 .unwrap();
5212
5213 let bytes = await_journal_entry(&backend, 2, Duration::from_secs(2))
5214 .expect("STATE_HISTORY entry for block 2 should appear after block 3 commits it");
5215 let entry = JournalEntry::decode(&bytes).unwrap();
5216 assert_eq!(entry.block_hash, block2_hash);
5217 assert_eq!(entry.parent_state_root, state_root_1);
5218 assert!(!entry.account_trie_diff.is_empty());
5219 }
5220
5221 #[test]
5231 fn depth_gated_per_block_commit_journals_with_correct_pre_image() {
5232 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
5233 let dir = tempfile::tempdir().unwrap();
5234 let store = Store::from_backend(
5235 backend.clone(),
5236 dir.path().to_path_buf(),
5237 1,
5238 DEFAULT_PERSIST_CHANNEL_CAPACITY,
5239 )
5240 .unwrap();
5241
5242 let shared = Nibbles::from_raw(&[0x0a, 0x0b], false);
5244 let mut prev_hash = H256::zero();
5245 let mut hashes = vec![H256::zero()];
5246 let mut roots = vec![H256::zero()];
5247 for n in 1..=6u64 {
5248 let state_root = H256::repeat_byte(0xc0 | (n as u8));
5249 let block = make_block(n, prev_hash, state_root);
5250 prev_hash = block.hash();
5251 hashes.push(prev_hash);
5252 roots.push(state_root);
5253 store
5254 .store_block_updates(UpdateBatch {
5255 account_updates: vec![(shared.clone(), vec![0xb0 | (n as u8)])],
5256 storage_updates: vec![],
5257 blocks: vec![block],
5258 receipts: vec![],
5259 code_updates: vec![],
5260 commit_depth: Some(3),
5261 wait_for_flush: false,
5262 })
5263 .unwrap();
5264 }
5265
5266 let bytes = await_journal_entry(&backend, 1, Duration::from_secs(2))
5267 .expect("a depth-gated per-block commit must journal");
5268 let entry = JournalEntry::decode(&bytes).unwrap();
5269 assert_eq!(
5270 entry.block_hash, hashes[1],
5271 "entry 1 carries block 1's identity"
5272 );
5273 assert_eq!(entry.parent_state_root, H256::zero());
5274 assert_eq!(
5275 entry.account_trie_diff[0].1, None,
5276 "block 1 first-writes the key, so its pre-image is absence"
5277 );
5278
5279 let bytes =
5280 await_journal_entry(&backend, 2, Duration::from_secs(2)).expect("entry for block 2");
5281 let entry = JournalEntry::decode(&bytes).unwrap();
5282 assert_eq!(
5283 entry.block_hash, hashes[2],
5284 "entry 2 carries block 2's identity"
5285 );
5286 assert_eq!(
5287 entry.parent_state_root, roots[1],
5288 "entry 2's parent_state_root is block 1's state root"
5289 );
5290 assert_eq!(
5291 entry.account_trie_diff[0].1,
5292 Some(vec![0xb0 | 1u8]),
5293 "block 2's pre-image is the value block 1 wrote"
5294 );
5295 }
5296
5297 #[test]
5306 fn journal_skipped_in_batch_mode() {
5307 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
5308 let dir = tempfile::tempdir().unwrap();
5309 let store = Store::from_backend(
5310 backend.clone(),
5311 dir.path().to_path_buf(),
5312 1,
5313 DEFAULT_PERSIST_CHANNEL_CAPACITY,
5314 )
5315 .unwrap();
5316
5317 let mut prev_hash = H256::zero();
5318 for n in 1..=5u64 {
5319 let state_root = H256::repeat_byte(0xa0 | (n as u8));
5320 let block = make_block(n, prev_hash, state_root);
5321 prev_hash = block.hash();
5322 store
5323 .store_block_updates(UpdateBatch {
5324 account_updates: vec![(
5325 Nibbles::from_raw(&[n as u8], false),
5326 vec![0xde, 0xad, n as u8],
5327 )],
5328 storage_updates: vec![],
5329 blocks: vec![block],
5330 receipts: vec![],
5331 code_updates: vec![],
5332 commit_depth: Some(BATCH_COMMIT_THRESHOLD),
5333 wait_for_flush: true,
5334 })
5335 .unwrap();
5336 }
5337
5338 for n in 1..=5u64 {
5339 assert_no_journal_entry(&backend, n);
5340 }
5341 }
5342
5343 #[test]
5346 fn journal_storage_updates_appear_in_storage_diff() {
5347 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
5348 let dir = tempfile::tempdir().unwrap();
5349 let store = Store::from_backend(
5350 backend.clone(),
5351 dir.path().to_path_buf(),
5352 1,
5353 DEFAULT_PERSIST_CHANNEL_CAPACITY,
5354 )
5355 .unwrap();
5356
5357 let account_hash_a = H256::repeat_byte(0xa0);
5358 let account_hash_b = H256::repeat_byte(0xb0);
5359
5360 let state_root_1 = H256::repeat_byte(0x33);
5361 let block1 = make_block(1, H256::zero(), state_root_1);
5362 let block1_hash = block1.hash();
5363 store
5364 .store_block_updates(UpdateBatch {
5365 account_updates: vec![],
5366 storage_updates: vec![
5367 (
5368 account_hash_a,
5369 vec![(Nibbles::from_raw(&[0x05], false), vec![0x01])],
5370 ),
5371 (
5372 account_hash_b,
5373 vec![(Nibbles::from_raw(&[0x06], false), vec![0x02])],
5374 ),
5375 ],
5376 blocks: vec![block1],
5377 receipts: vec![],
5378 code_updates: vec![],
5379 commit_depth: None,
5380 wait_for_flush: false,
5381 })
5382 .unwrap();
5383
5384 store.set_safe_commit_root(state_root_1).unwrap();
5387 let state_root_2 = H256::repeat_byte(0x44);
5388 let block2 = make_block(2, block1_hash, state_root_2);
5389 store
5390 .store_block_updates(UpdateBatch {
5391 account_updates: vec![(Nibbles::from_raw(&[0xee], false), vec![0xff])],
5392 storage_updates: vec![],
5393 blocks: vec![block2],
5394 receipts: vec![],
5395 code_updates: vec![],
5396 commit_depth: None,
5397 wait_for_flush: false,
5398 })
5399 .unwrap();
5400
5401 let bytes = await_journal_entry(&backend, 1, Duration::from_secs(2))
5402 .expect("STATE_HISTORY entry for block 1");
5403 let entry = JournalEntry::decode(&bytes).unwrap();
5404 assert_eq!(entry.block_hash, block1_hash);
5405 assert_eq!(
5406 entry.storage_trie_diff.len(),
5407 2,
5408 "two distinct account hashes must produce two storage_trie entries"
5409 );
5410 for (_key, prev) in &entry.storage_trie_diff {
5411 assert_eq!(prev, &None, "first-time storage write has None pre-image");
5412 }
5413 assert!(entry.account_trie_diff.is_empty());
5414 }
5415
5416 fn seed_journal_entries(backend: &Arc<dyn StorageBackend>, block_numbers: &[BlockNumber]) {
5417 let mut tx = backend.begin_write().unwrap();
5418 for n in block_numbers {
5419 let entry = JournalEntry {
5420 block_hash: H256::repeat_byte(*n as u8),
5421 parent_state_root: H256::zero(),
5422 account_trie_diff: vec![(vec![*n as u8], None)],
5423 storage_trie_diff: vec![],
5424 account_flat_diff: vec![],
5425 storage_flat_diff: vec![],
5426 };
5427 tx.put(STATE_HISTORY, &n.to_be_bytes(), &entry.encode())
5428 .unwrap();
5429 }
5430 tx.commit().unwrap();
5431 }
5432
5433 fn journal_entry_exists(backend: &Arc<dyn StorageBackend>, block_number: BlockNumber) -> bool {
5434 backend
5435 .begin_read()
5436 .unwrap()
5437 .get(STATE_HISTORY, &block_number.to_be_bytes())
5438 .unwrap()
5439 .is_some()
5440 }
5441
5442 #[tokio::test]
5445 async fn finality_advance_prunes_journal_below_boundary() {
5446 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
5447 let dir = tempfile::tempdir().unwrap();
5448 let store = Store::from_backend(
5449 backend.clone(),
5450 dir.path().to_path_buf(),
5451 1,
5452 DEFAULT_PERSIST_CHANNEL_CAPACITY,
5453 )
5454 .unwrap();
5455
5456 seed_journal_entries(&backend, &(1..=10).collect::<Vec<_>>());
5457 for n in 1..=10 {
5458 assert!(journal_entry_exists(&backend, n), "seed entry {n} present");
5459 }
5460
5461 store
5462 .forkchoice_update_inner(vec![], 100, H256::zero(), None, Some(5))
5463 .await
5464 .unwrap();
5465
5466 for n in 1..=5 {
5467 assert!(
5468 !journal_entry_exists(&backend, n),
5469 "entry {n} should have been pruned (<= finalized)"
5470 );
5471 }
5472 for n in 6..=10 {
5473 assert!(
5474 journal_entry_exists(&backend, n),
5475 "entry {n} should remain (> finalized)"
5476 );
5477 }
5478 }
5479
5480 #[tokio::test]
5482 async fn finality_no_op_does_not_prune() {
5483 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
5484 let dir = tempfile::tempdir().unwrap();
5485 let store = Store::from_backend(
5486 backend.clone(),
5487 dir.path().to_path_buf(),
5488 1,
5489 DEFAULT_PERSIST_CHANNEL_CAPACITY,
5490 )
5491 .unwrap();
5492
5493 store
5494 .forkchoice_update_inner(vec![], 100, H256::zero(), None, Some(5))
5495 .await
5496 .unwrap();
5497
5498 seed_journal_entries(&backend, &(6..=10).collect::<Vec<_>>());
5499
5500 store
5502 .forkchoice_update_inner(vec![], 100, H256::zero(), None, Some(5))
5503 .await
5504 .unwrap();
5505
5506 for n in 6..=10 {
5507 assert!(
5508 journal_entry_exists(&backend, n),
5509 "entry {n} should still exist after no-op finality update"
5510 );
5511 }
5512
5513 store
5515 .forkchoice_update_inner(vec![], 100, H256::zero(), None, None)
5516 .await
5517 .unwrap();
5518
5519 for n in 6..=10 {
5520 assert!(
5521 journal_entry_exists(&backend, n),
5522 "entry {n} should still exist when finalized is None"
5523 );
5524 }
5525 }
5526
5527 #[tokio::test]
5531 async fn malformed_finalized_returns_error_not_silent_zero() {
5532 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
5533 let dir = tempfile::tempdir().unwrap();
5534 let store = Store::from_backend(
5535 backend.clone(),
5536 dir.path().to_path_buf(),
5537 1,
5538 DEFAULT_PERSIST_CHANNEL_CAPACITY,
5539 )
5540 .unwrap();
5541
5542 let mut tx = backend.begin_write().unwrap();
5544 let finalized_key = chain_data_key(ChainDataIndex::FinalizedBlockNumber);
5545 tx.put(CHAIN_DATA, &finalized_key, &[0u8, 0, 0, 0]).unwrap();
5546 tx.commit().unwrap();
5547
5548 seed_journal_entries(&backend, &(1..=5).collect::<Vec<_>>());
5549
5550 let err = store
5551 .forkchoice_update_inner(vec![], 100, H256::zero(), None, Some(3))
5552 .await
5553 .expect_err("malformed finalized must surface as an error");
5554 let msg = format!("{err}");
5555 assert!(
5556 msg.contains("FinalizedBlockNumber has unexpected length"),
5557 "expected length-mismatch error, got: {msg}"
5558 );
5559
5560 for n in 1..=5 {
5562 assert!(
5563 journal_entry_exists(&backend, n),
5564 "entry {n} must NOT be pruned when finalized read failed"
5565 );
5566 }
5567 }
5568
5569 #[tokio::test]
5576 async fn highest_state_history_block_number_finds_max() {
5577 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
5578 let dir = tempfile::tempdir().unwrap();
5579 let store = Store::from_backend(
5580 backend.clone(),
5581 dir.path().to_path_buf(),
5582 1,
5583 DEFAULT_PERSIST_CHANNEL_CAPACITY,
5584 )
5585 .unwrap();
5586
5587 assert_eq!(store.highest_state_history_block_number().unwrap(), None);
5588
5589 seed_journal_entries(&backend, &[3, 7, 5, 11, 8]);
5590 assert_eq!(
5591 store.highest_state_history_block_number().unwrap(),
5592 Some(11),
5593 "max over present entries"
5594 );
5595 }
5596
5597 #[tokio::test]
5601 async fn lowest_state_history_block_number_finds_min() {
5602 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
5603 let dir = tempfile::tempdir().unwrap();
5604 let store = Store::from_backend(
5605 backend.clone(),
5606 dir.path().to_path_buf(),
5607 1,
5608 DEFAULT_PERSIST_CHANNEL_CAPACITY,
5609 )
5610 .unwrap();
5611
5612 assert_eq!(store.lowest_state_history_block_number().unwrap(), None);
5613
5614 seed_journal_entries(&backend, &[7, 3, 11, 5, 8]);
5615 assert_eq!(
5616 store.lowest_state_history_block_number().unwrap(),
5617 Some(3),
5618 "min over present entries"
5619 );
5620 }
5621
5622 #[tokio::test]
5626 async fn install_overlay_replaces_cache_with_fresh_one() {
5627 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
5628 let dir = tempfile::tempdir().unwrap();
5629 let store = Store::from_backend(
5630 backend.clone(),
5631 dir.path().to_path_buf(),
5632 4,
5633 DEFAULT_PERSIST_CHANNEL_CAPACITY,
5634 )
5635 .unwrap();
5636
5637 seed_journal_entries(&backend, &[3, 4]);
5638
5639 {
5641 let mut guard = store.trie_cache.write().unwrap();
5642 let mut updated = (**guard).clone();
5643 updated.put_batch(
5644 H256::zero(),
5645 H256::repeat_byte(0xff),
5646 42,
5647 H256::repeat_byte(0x42),
5648 vec![],
5649 );
5650 *guard = Arc::new(updated);
5651 }
5652 assert!(
5653 store
5654 .is_state_in_layer_cache(H256::repeat_byte(0xff))
5655 .unwrap()
5656 );
5657
5658 store
5659 .install_overlay_for_reorg(4, 3, |_| None)
5660 .expect("install must succeed");
5661
5662 assert!(
5663 !store
5664 .is_state_in_layer_cache(H256::repeat_byte(0xff))
5665 .unwrap(),
5666 "old cache layer must be gone after swap"
5667 );
5668 let cache = store.trie_cache.read().unwrap().clone();
5669 assert!(cache.overlay().is_some(), "overlay must be installed");
5670 assert_eq!(
5671 cache.commit_threshold(),
5672 4,
5673 "fresh cache must inherit threshold"
5674 );
5675 }
5676
5677 #[tokio::test]
5680 async fn install_overlay_failure_preserves_cache() {
5681 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
5682 let dir = tempfile::tempdir().unwrap();
5683 let store = Store::from_backend(
5684 backend.clone(),
5685 dir.path().to_path_buf(),
5686 4,
5687 DEFAULT_PERSIST_CHANNEL_CAPACITY,
5688 )
5689 .unwrap();
5690
5691 seed_journal_entries(&backend, &[5]);
5693 {
5694 let mut guard = store.trie_cache.write().unwrap();
5695 let mut updated = (**guard).clone();
5696 updated.put_batch(
5697 H256::zero(),
5698 H256::repeat_byte(0xaa),
5699 7,
5700 H256::repeat_byte(0x77),
5701 vec![],
5702 );
5703 *guard = Arc::new(updated);
5704 }
5705 assert!(
5706 store
5707 .is_state_in_layer_cache(H256::repeat_byte(0xaa))
5708 .unwrap()
5709 );
5710
5711 let err = store
5712 .install_overlay_for_reorg(5, 3, |_| None)
5713 .expect_err("missing entries 3 and 4 must abort");
5714 let msg = format!("{err}");
5715 assert!(
5716 msg.contains("overlay construction failed"),
5717 "error should explain construction failure: {msg}"
5718 );
5719
5720 assert!(
5722 store
5723 .is_state_in_layer_cache(H256::repeat_byte(0xaa))
5724 .unwrap(),
5725 "cache must survive a failed overlay install"
5726 );
5727 let cache = store.trie_cache.read().unwrap().clone();
5728 assert!(cache.overlay().is_none(), "no overlay should be installed");
5729 }
5730
5731 #[tokio::test]
5735 async fn abort_reorg_resets_cache_to_fresh() {
5736 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
5737 let dir = tempfile::tempdir().unwrap();
5738 let store = Store::from_backend(
5739 backend.clone(),
5740 dir.path().to_path_buf(),
5741 4,
5742 DEFAULT_PERSIST_CHANNEL_CAPACITY,
5743 )
5744 .unwrap();
5745
5746 seed_journal_entries(&backend, &[3, 4]);
5747 store.install_overlay_for_reorg(4, 3, |_| None).unwrap();
5748 {
5750 let mut guard = store.trie_cache.write().unwrap();
5751 let mut updated = (**guard).clone();
5752 updated.put_batch(
5753 H256::zero(),
5754 H256::repeat_byte(0xbb),
5755 4,
5756 H256::repeat_byte(0xb4),
5757 vec![],
5758 );
5759 *guard = Arc::new(updated);
5760 }
5761 assert!(
5762 store
5763 .is_state_in_layer_cache(H256::repeat_byte(0xbb))
5764 .unwrap()
5765 );
5766
5767 store.abort_reorg().unwrap();
5768
5769 assert!(
5770 !store
5771 .is_state_in_layer_cache(H256::repeat_byte(0xbb))
5772 .unwrap(),
5773 "partial new-chain layer must be discarded"
5774 );
5775 let cache = store.trie_cache.read().unwrap().clone();
5776 assert!(cache.overlay().is_none(), "overlay must be cleared");
5777 assert_eq!(
5778 cache.commit_threshold(),
5779 4,
5780 "fresh cache must inherit threshold"
5781 );
5782 }
5783
5784 #[tokio::test]
5787 async fn clear_reorg_overlay_removes_overlay_and_is_idempotent() {
5788 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
5789 let dir = tempfile::tempdir().unwrap();
5790 let store = Store::from_backend(
5791 backend.clone(),
5792 dir.path().to_path_buf(),
5793 4,
5794 DEFAULT_PERSIST_CHANNEL_CAPACITY,
5795 )
5796 .unwrap();
5797
5798 store.clear_reorg_overlay().unwrap();
5800 assert!(store.trie_cache.read().unwrap().overlay().is_none());
5801
5802 seed_journal_entries(&backend, &[7]);
5803 store.install_overlay_for_reorg(7, 7, |_| None).unwrap();
5804 assert!(store.trie_cache.read().unwrap().overlay().is_some());
5805
5806 store.clear_reorg_overlay().unwrap();
5807 assert!(store.trie_cache.read().unwrap().overlay().is_none());
5808
5809 store.clear_reorg_overlay().unwrap();
5811 assert!(store.trie_cache.read().unwrap().overlay().is_none());
5812 }
5813
5814 #[tokio::test]
5822 async fn deep_reorg_overlay_disables_flat_kv_fast_paths() {
5823 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
5824
5825 {
5829 let mut tx = backend.begin_write().unwrap();
5830 tx.put(MISC_VALUES, "last_written".as_bytes(), &[0xff])
5831 .unwrap();
5832 tx.commit().unwrap();
5833 }
5834
5835 let dir = tempfile::tempdir().unwrap();
5836 let store = Store::from_backend(
5837 backend.clone(),
5838 dir.path().to_path_buf(),
5839 1,
5840 DEFAULT_PERSIST_CHANNEL_CAPACITY,
5841 )
5842 .unwrap();
5843
5844 let address = Address::from_low_u64_be(0xbeef);
5849 let account_hash = hash_address_fixed(&address);
5850 let slot = H256::from_low_u64_be(1);
5851 let slot_hash = hash_key_fixed(&slot);
5852 let generator_account = AccountState {
5853 nonce: 7,
5854 balance: U256::from(999u64),
5855 storage_root: H256::zero(),
5856 code_hash: H256::zero(),
5857 };
5858 let generator_slot_value = U256::from(42u64);
5859 {
5860 let mut tx = backend.begin_write().unwrap();
5861 tx.put(
5862 ACCOUNT_FLATKEYVALUE,
5863 &Nibbles::from_bytes(account_hash.as_bytes()).into_vec(),
5864 &generator_account.encode_to_vec(),
5865 )
5866 .unwrap();
5867 tx.put(
5868 STORAGE_FLATKEYVALUE,
5869 &apply_prefix(Some(account_hash), Nibbles::from_bytes(&slot_hash)).into_vec(),
5870 &generator_slot_value.encode_to_vec(),
5871 )
5872 .unwrap();
5873 tx.commit().unwrap();
5874 }
5875
5876 let pivot_root = EMPTY_TRIE_HASH;
5881 {
5882 let mut tx = backend.begin_write().unwrap();
5883 for (n, parent_root) in [(10u64, pivot_root), (11, H256::repeat_byte(0x10))] {
5884 let entry = JournalEntry {
5885 block_hash: H256::repeat_byte(n as u8),
5886 parent_state_root: parent_root,
5887 account_trie_diff: vec![],
5888 storage_trie_diff: vec![],
5889 account_flat_diff: vec![],
5890 storage_flat_diff: vec![],
5891 };
5892 tx.put(STATE_HISTORY, &n.to_be_bytes(), &entry.encode())
5893 .unwrap();
5894 }
5895 tx.commit().unwrap();
5896 }
5897
5898 store
5899 .install_overlay_for_reorg(11, 10, |_| None)
5900 .expect("overlay install must succeed");
5901
5902 let accounts = store
5905 .get_account_states_batch_by_root(pivot_root, &[address])
5906 .unwrap();
5907 assert_eq!(
5908 accounts,
5909 vec![None],
5910 "batch account read must not serve the generator's stale flat-KV value"
5911 );
5912
5913 let slot_value = store
5914 .get_storage_at_root(pivot_root, address, slot)
5915 .unwrap();
5916 assert_eq!(
5917 slot_value, None,
5918 "storage read must not serve the generator's stale flat-KV value"
5919 );
5920
5921 let state_trie = store.open_state_trie(pivot_root).unwrap();
5922 assert_eq!(
5923 state_trie.get(account_hash.as_bytes()).unwrap(),
5924 None,
5925 "trie read must not serve the generator's stale flat-KV value"
5926 );
5927
5928 let unserved_root = H256::repeat_byte(0x99);
5931 let accounts = store
5932 .get_account_states_batch_by_root(unserved_root, &[address])
5933 .unwrap();
5934 assert_eq!(
5935 accounts,
5936 vec![Some(generator_account)],
5937 "unserved roots must keep the flat-KV fast path"
5938 );
5939 let slot_value = store
5940 .get_storage_at_root(unserved_root, address, slot)
5941 .unwrap();
5942 assert_eq!(
5943 slot_value,
5944 Some(generator_slot_value),
5945 "unserved roots must keep the flat-KV fast path"
5946 );
5947 }
5948
5949 #[tokio::test]
5957 async fn overlay_backed_commit_only_commits_bottom_layer_per_pass() {
5958 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
5959 let dir = tempfile::tempdir().unwrap();
5960 let store = Store::from_backend(
5961 backend.clone(),
5962 dir.path().to_path_buf(),
5963 4,
5964 DEFAULT_PERSIST_CHANNEL_CAPACITY,
5965 )
5966 .unwrap();
5967
5968 let pivot_root = H256::repeat_byte(0x09);
5971 {
5972 let mut tx = backend.begin_write().unwrap();
5973 for (n, parent_root) in [(10u64, pivot_root), (11, H256::repeat_byte(0x0a))] {
5974 let entry = JournalEntry {
5975 block_hash: H256::repeat_byte(n as u8),
5976 parent_state_root: parent_root,
5977 account_trie_diff: vec![(vec![0x00, n as u8], None)],
5978 storage_trie_diff: vec![],
5979 account_flat_diff: vec![],
5980 storage_flat_diff: vec![],
5981 };
5982 tx.put(STATE_HISTORY, &n.to_be_bytes(), &entry.encode())
5983 .unwrap();
5984 }
5985 tx.commit().unwrap();
5986 }
5987 store.install_overlay_for_reorg(11, 10, |_| None).unwrap();
5988
5989 let roots: Vec<H256> = (10..=12u8).map(H256::repeat_byte).collect();
5991 {
5992 let mut guard = store.trie_cache.write().unwrap();
5993 let mut updated = (**guard).clone();
5994 let mut parent = pivot_root;
5995 for (i, root) in roots.iter().enumerate() {
5996 let n = 10 + i as u64;
5997 updated.put_batch(
5998 parent,
5999 *root,
6000 n,
6001 H256::repeat_byte(n as u8),
6002 vec![(Nibbles::from_raw(&[0x01, n as u8], false), vec![n as u8])],
6003 );
6004 parent = *root;
6005 }
6006 *guard = Arc::new(updated);
6007 }
6008
6009 let trie = store.trie_cache.read().unwrap().clone();
6012 commit_to_disk(
6013 store.backend.as_ref(),
6014 &store.flatkeyvalue_control_tx,
6015 &store.trie_cache,
6016 &trie,
6017 roots[2],
6018 false,
6019 )
6020 .expect("bottom-layer commit must succeed (no debug_assert panic)");
6021
6022 assert!(
6024 !store.is_state_in_layer_cache(roots[0]).unwrap(),
6025 "bottom layer must be committed and pruned"
6026 );
6027 assert!(
6028 store.is_state_in_layer_cache(roots[1]).unwrap()
6029 && store.is_state_in_layer_cache(roots[2]).unwrap(),
6030 "upper layers must remain resident after the bottom-layer-only commit"
6031 );
6032 assert!(
6034 store.trie_cache.read().unwrap().overlay().is_none(),
6035 "reconciliation must clear the overlay"
6036 );
6037 assert!(
6039 journal_entry_exists(&backend, 10),
6040 "bottom layer T must be journaled"
6041 );
6042 assert!(!journal_entry_exists(&backend, 11));
6043 assert!(!journal_entry_exists(&backend, 12));
6044
6045 let entry_bytes = backend
6050 .begin_read()
6051 .unwrap()
6052 .get(STATE_HISTORY, &10u64.to_be_bytes())
6053 .unwrap()
6054 .expect("journal entry for block 10");
6055 let entry = JournalEntry::decode(&entry_bytes).unwrap();
6056 let diff_keys: Vec<&Vec<u8>> = entry.account_trie_diff.iter().map(|(k, _)| k).collect();
6057 assert!(
6058 diff_keys.contains(&&vec![0x00, 10]) && diff_keys.contains(&&vec![0x00, 11]),
6059 "bridge keys from the overlay must be folded into T's journal entry, got {diff_keys:?}"
6060 );
6061
6062 let trie = store.trie_cache.read().unwrap().clone();
6064 commit_to_disk(
6065 store.backend.as_ref(),
6066 &store.flatkeyvalue_control_tx,
6067 &store.trie_cache,
6068 &trie,
6069 roots[2],
6070 false,
6071 )
6072 .unwrap();
6073 assert!(!store.is_state_in_layer_cache(roots[1]).unwrap());
6074 assert!(!store.is_state_in_layer_cache(roots[2]).unwrap());
6075 assert!(journal_entry_exists(&backend, 11));
6076 assert!(journal_entry_exists(&backend, 12));
6077 }
6078
6079 #[tokio::test]
6084 async fn journal_pruning_pauses_during_reorg_and_catches_up_after() {
6085 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
6086 let dir = tempfile::tempdir().unwrap();
6087 let store = Store::from_backend(
6088 backend.clone(),
6089 dir.path().to_path_buf(),
6090 1,
6091 DEFAULT_PERSIST_CHANNEL_CAPACITY,
6092 )
6093 .unwrap();
6094
6095 seed_journal_entries(&backend, &(1..=5).collect::<Vec<_>>());
6096
6097 store.set_journal_pruning_paused(true);
6099 store
6100 .forkchoice_update_inner(vec![], 100, H256::zero(), None, Some(3))
6101 .await
6102 .unwrap();
6103 for n in 1..=5 {
6104 assert!(
6105 journal_entry_exists(&backend, n),
6106 "entry {n} must survive pruning while the reorg pause is held"
6107 );
6108 }
6109
6110 store.set_journal_pruning_paused(false);
6112 store
6113 .forkchoice_update_inner(vec![], 100, H256::zero(), None, Some(4))
6114 .await
6115 .unwrap();
6116 for n in 1..=4 {
6117 assert!(
6118 !journal_entry_exists(&backend, n),
6119 "entry {n} must be pruned once the pause is released"
6120 );
6121 }
6122 assert!(journal_entry_exists(&backend, 5));
6123 }
6124}
6125
6126#[cfg(test)]
6127mod merge_tests {
6128 use super::*;
6129
6130 fn h256(b: u8) -> H256 {
6131 H256::from_low_u64_be(b as u64)
6132 }
6133
6134 fn op(bn: BlockNumber, bh: H256, idx: Index) -> Vec<u8> {
6135 encode_tx_location_operand(bn, bh, idx)
6136 }
6137
6138 fn decode(v: &[u8]) -> Vec<(BlockNumber, BlockHash, Index)> {
6139 <Vec<(BlockNumber, BlockHash, Index)>>::decode(v).unwrap()
6140 }
6141
6142 #[test]
6143 fn single_operand_on_empty_base() {
6144 let out = tx_locations_merge(None, vec![op(100, h256(0x10), 0)]).unwrap();
6145 assert_eq!(decode(&out), vec![(100, h256(0x10), 0)]);
6146 }
6147
6148 #[test]
6149 fn operand_appended_to_existing_base() {
6150 let base = vec![(100u64, h256(0x10), 0u64)].encode_to_vec();
6151 let out = tx_locations_merge(Some(&base), vec![op(101, h256(0x11), 5)]).unwrap();
6152 let mut got = decode(&out);
6153 got.sort();
6154 let mut want = vec![(100, h256(0x10), 0), (101, h256(0x11), 5)];
6155 want.sort();
6156 assert_eq!(got, want);
6157 }
6158
6159 #[test]
6160 fn multiple_operands_combined() {
6161 let out = tx_locations_merge(
6162 None,
6163 vec![
6164 op(100, h256(0x10), 0),
6165 op(100, h256(0x11), 1),
6166 op(101, h256(0x12), 2),
6167 ],
6168 )
6169 .unwrap();
6170 assert_eq!(decode(&out).len(), 3);
6171 }
6172
6173 #[test]
6174 fn same_block_hash_is_deduped() {
6175 let out =
6177 tx_locations_merge(None, vec![op(100, h256(0x10), 0), op(100, h256(0x10), 7)]).unwrap();
6178 assert_eq!(decode(&out), vec![(100, h256(0x10), 7)]);
6179 }
6180
6181 #[test]
6182 fn malformed_operand_aborts_merge() {
6183 let out = tx_locations_merge(None, vec![vec![0xff, 0xff], op(100, h256(0x10), 0)]);
6186 assert!(out.is_none(), "merge must abort on a malformed operand");
6187 }
6188
6189 #[test]
6190 fn malformed_base_value_aborts_merge() {
6191 let out = tx_locations_merge(Some(&[0xff, 0xff]), vec![op(100, h256(0x10), 0)]);
6192 assert!(out.is_none(), "merge must abort on a corrupt base value");
6193 }
6194
6195 #[test]
6202 fn partial_merge_result_is_a_valid_operand() {
6203 let partial =
6205 tx_locations_merge(None, vec![op(100, h256(0x10), 0), op(101, h256(0x11), 1)]).unwrap();
6206
6207 let base = vec![(99u64, h256(0x09), 9u64)].encode_to_vec();
6211 let out = tx_locations_merge(Some(&base), vec![partial]).unwrap();
6212
6213 let mut got = decode(&out);
6214 got.sort();
6215 let mut want = vec![
6216 (99, h256(0x09), 9),
6217 (100, h256(0x10), 0),
6218 (101, h256(0x11), 1),
6219 ];
6220 want.sort();
6221 assert_eq!(
6222 got, want,
6223 "no entries may be lost when re-merging a partial result"
6224 );
6225 }
6226
6227 #[test]
6230 fn operand_encoding_matches_value_encoding() {
6231 let operand = op(100, h256(0x10), 3);
6232 assert_eq!(decode(&operand), vec![(100, h256(0x10), 3)]);
6234 }
6235
6236 #[test]
6238 fn chained_partial_merges() {
6239 let p1 = tx_locations_merge(None, vec![op(1, h256(0x01), 0)]).unwrap();
6240 let p2 = tx_locations_merge(None, vec![p1, op(2, h256(0x02), 0)]).unwrap();
6241 let p3 = tx_locations_merge(None, vec![p2, op(3, h256(0x03), 0)]).unwrap();
6242 let out = tx_locations_merge(None, vec![p3]).unwrap();
6243 assert_eq!(decode(&out).len(), 3);
6244 }
6245}
6246
6247#[cfg(test)]
6248mod datadir_tests {
6249 use super::*;
6250 use std::fs;
6251
6252 #[test]
6253 fn empty_dir_has_no_existing_db() {
6254 let dir = tempfile::tempdir().unwrap();
6255 assert!(!dir_contains_legacy_db(dir.path()).unwrap());
6256 }
6257
6258 #[test]
6259 fn dir_with_only_unrelated_files_has_no_existing_db() {
6260 let dir = tempfile::tempdir().unwrap();
6263 fs::write(dir.path().join("jwt.hex"), "0xdeadbeef").unwrap();
6264 fs::write(dir.path().join("LOG"), "noise").unwrap();
6265 assert!(!dir_contains_legacy_db(dir.path()).unwrap());
6266 }
6267
6268 #[test]
6269 fn dir_with_rocksdb_markers_has_existing_db() {
6270 let dir = tempfile::tempdir().unwrap();
6272 fs::write(dir.path().join("CURRENT"), "MANIFEST-000001\n").unwrap();
6273 assert!(dir_contains_legacy_db(dir.path()).unwrap());
6274
6275 let dir2 = tempfile::tempdir().unwrap();
6276 fs::write(dir2.path().join("MANIFEST-000007"), "x").unwrap();
6277 assert!(dir_contains_legacy_db(dir2.path()).unwrap());
6278 }
6279
6280 #[test]
6281 fn dir_with_marker_named_subdirectories_has_no_existing_db() {
6282 let dir = tempfile::tempdir().unwrap();
6285 fs::create_dir(dir.path().join("CURRENT")).unwrap();
6286 fs::create_dir(dir.path().join("MANIFEST-000001")).unwrap();
6287 assert!(!dir_contains_legacy_db(dir.path()).unwrap());
6288 }
6289}
6290
6291#[cfg(test)]
6292mod flatkeyvalue_completeness_tests {
6293 use super::*;
6294
6295 #[test]
6300 fn only_the_one_byte_ff_sentinel_counts_as_complete() {
6301 assert!(Store::flatkeyvalue_generation_complete(Some(&[0xff])));
6303
6304 assert!(!Store::flatkeyvalue_generation_complete(None)); assert!(!Store::flatkeyvalue_generation_complete(Some(&[]))); assert!(!Store::flatkeyvalue_generation_complete(Some(&[0u8; 64]))); assert!(!Store::flatkeyvalue_generation_complete(Some(&[0x0a; 64]))); assert!(!Store::flatkeyvalue_generation_complete(Some(&[0xff; 64])));
6312 assert!(!Store::flatkeyvalue_generation_complete(Some(&[
6313 0xff, 0xff
6314 ])));
6315 }
6316}