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
78const KEYS_PER_SHARD: usize = 256;
85const MAX_SHARDS: usize = 64;
86
87fn shard_count(n: usize) -> usize {
89 n.div_ceil(KEYS_PER_SHARD).clamp(1, MAX_SHARDS)
90}
91
92pub const DEFAULT_ROCKSDB_BLOCK_CACHE_SIZE_BYTES: usize = 12 * 1024 * 1024 * 1024;
102
103#[derive(Debug, Clone, Copy)]
108pub struct StoreConfig {
109 pub rocksdb_block_cache_size: usize,
114 pub persist_channel_capacity: usize,
119}
120
121impl Default for StoreConfig {
122 fn default() -> Self {
123 Self {
124 rocksdb_block_cache_size: DEFAULT_ROCKSDB_BLOCK_CACHE_SIZE_BYTES,
125 persist_channel_capacity: DEFAULT_PERSIST_CHANNEL_CAPACITY,
126 }
127 }
128}
129
130#[derive(Debug, PartialEq)]
132enum FKVGeneratorControlMessage {
133 Stop,
134 Continue,
135}
136
137const CODE_CACHE_MAX_SIZE: u64 = 64 * 1024 * 1024;
139
140const FLUSHED_UPTO_KEY: &[u8] = b"bodies_flushed_upto";
142
143const BAD_BLOCKS_KEY: &[u8] = b"bad_blocks";
145
146const MAX_BAD_BLOCKS: usize = 16;
148
149#[derive(Debug)]
150struct CodeCache {
151 inner_cache: LruCache<H256, Code, FxBuildHasher>,
152 cache_size: u64,
153}
154
155impl Default for CodeCache {
156 fn default() -> Self {
157 Self {
158 inner_cache: LruCache::unbounded_with_hasher(FxBuildHasher),
159 cache_size: 0,
160 }
161 }
162}
163
164impl CodeCache {
165 fn get(&mut self, code_hash: &H256) -> Result<Option<Code>, StoreError> {
166 Ok(self.inner_cache.get(code_hash).cloned())
167 }
168
169 fn insert(&mut self, code: &Code) -> Result<(), StoreError> {
170 let code_size = code.size();
171 let cache_len = self.inner_cache.len() + 1;
172 self.cache_size += code_size as u64;
173 let current_size = self.cache_size;
174 debug!(
175 "[ACCOUNT CODE CACHE] cache elements (): {cache_len}, total size: {current_size} bytes"
176 );
177
178 while self.cache_size > CODE_CACHE_MAX_SIZE {
179 if let Some((_, code)) = self.inner_cache.pop_lru() {
180 self.cache_size -= code.size() as u64;
181 } else {
182 break;
183 }
184 }
185
186 self.inner_cache.get_or_insert(code.hash, || code.clone());
187 Ok(())
188 }
189}
190
191#[derive(Debug, Clone)]
197pub struct Store {
198 db_path: PathBuf,
200 backend: Arc<dyn StorageBackend>,
202 chain_config: ChainConfig,
204 trie_cache: Arc<RwLock<Arc<TrieLayerCache>>>,
206 flatkeyvalue_control_tx: std::sync::mpsc::SyncSender<FKVGeneratorControlMessage>,
208 block_data_buffer: Arc<RwLock<Arc<BlockDataBuffer>>>,
210 persist_tx: std::sync::mpsc::SyncSender<PersistMessage>,
214 pending_trie_roots: Arc<PendingTrieRoots>,
218 latest_block_header: LatestBlockHeaderCache,
221 last_computed_flatkeyvalue: Arc<RwLock<Vec<u8>>>,
223
224 account_code_cache: Arc<Mutex<CodeCache>>,
229
230 code_metadata_cache: Arc<Mutex<rustc_hash::FxHashMap<H256, CodeMetadata>>>,
233
234 fcu_lock: Arc<tokio::sync::Mutex<()>>,
237
238 safe_commit_root: Arc<RwLock<H256>>,
244
245 journal_pruning_paused: Arc<std::sync::atomic::AtomicBool>,
255
256 background_threads: Arc<ThreadList>,
257}
258
259#[derive(Debug, Default)]
260struct ThreadList {
261 list: Vec<JoinHandle<()>>,
262}
263
264impl Drop for ThreadList {
265 fn drop(&mut self) {
266 for handle in self.list.drain(..) {
267 let _ = handle.join();
268 }
269 }
270}
271
272pub type StorageTrieNodes = Vec<(H256, Vec<(Nibbles, Vec<u8>)>)>;
277type StorageTries = HashMap<Address, (TrieWitness, Trie)>;
278
279#[derive(Debug, Clone, Copy, PartialEq, Eq)]
283pub enum EngineType {
284 InMemory,
286 #[cfg(feature = "rocksdb")]
288 RocksDB,
289}
290
291pub struct UpdateBatch {
296 pub account_updates: Vec<TrieNode>,
298 pub storage_updates: Vec<(H256, Vec<TrieNode>)>,
300 pub blocks: Vec<Block>,
302 pub receipts: Vec<(H256, Vec<Receipt>)>,
304 pub code_updates: Vec<(H256, Code)>,
306 pub commit_depth: Option<usize>,
314 pub wait_for_flush: bool,
325}
326
327pub struct BackfilledBlock {
335 pub header: BlockHeader,
336 pub body: BlockBody,
337 pub receipts: Vec<Receipt>,
338 pub index_transactions: bool,
339}
340
341pub type StorageUpdates = Vec<(H256, Vec<(Nibbles, Vec<u8>)>)>;
343
344pub struct AccountUpdatesList {
349 pub state_trie_hash: H256,
351 pub state_updates: Vec<(Nibbles, Vec<u8>)>,
353 pub storage_updates: StorageUpdates,
355 pub code_updates: Vec<(H256, Code)>,
357}
358
359pub(crate) fn encode_tx_location_operand(
370 block_number: BlockNumber,
371 block_hash: BlockHash,
372 index: Index,
373) -> Vec<u8> {
374 vec![(block_number, block_hash, index)].encode_to_vec()
375}
376
377pub fn tx_locations_merge(
395 existing: Option<&[u8]>,
396 operands: impl IntoIterator<Item = impl AsRef<[u8]>>,
397) -> Option<Vec<u8>> {
398 fn fold_chunk(
402 list: &mut Vec<(BlockNumber, BlockHash, Index)>,
403 bytes: &[u8],
404 what: &str,
405 ) -> bool {
406 match <Vec<(BlockNumber, BlockHash, Index)>>::decode(bytes) {
407 Ok(entries) => {
408 for (bn, bh, idx) in entries {
409 list.retain(|(_, existing_bh, _)| *existing_bh != bh);
410 list.push((bn, bh, idx));
411 }
412 true
413 }
414 Err(e) => {
415 error!(
416 "tx_locations_merge: failed to decode {what} ({} bytes): {e}; \
417 aborting merge to avoid silent data loss",
418 bytes.len()
419 );
420 false
421 }
422 }
423 }
424
425 let mut list: Vec<(BlockNumber, BlockHash, Index)> = Vec::new();
426
427 if let Some(bytes) = existing
429 && !fold_chunk(&mut list, bytes, "existing value")
430 {
431 return None;
432 }
433 for op in operands {
434 if !fold_chunk(&mut list, op.as_ref(), "operand") {
435 return None;
436 }
437 }
438 Some(list.encode_to_vec())
439}
440
441impl Store {
442 pub async fn wait_for_persistence_idle(&self) -> Result<(), StoreError> {
453 let tx = self.persist_tx.clone();
454 tokio::task::spawn_blocking(move || {
455 let (ack_tx, ack_rx) = sync_channel::<Result<(), StoreError>>(1);
456 tx.send(PersistMessage::Ping(ack_tx))
457 .map_err(|e| StoreError::Custom(format!("wait_for_persistence_idle send: {e}")))?;
458 ack_rx
459 .recv()
460 .map_err(|e| StoreError::Custom(format!("wait_for_persistence_idle ack: {e}")))?
461 })
462 .await
463 .map_err(|e| StoreError::Custom(format!("wait_for_persistence_idle join: {e}")))?
464 }
465
466 pub async fn shutdown(&self) -> Result<(), StoreError> {
485 let tx = self.persist_tx.clone();
486 let backend = self.backend.clone();
487 tokio::task::spawn_blocking(move || {
488 let (ack_tx, ack_rx) = sync_channel::<Result<(), StoreError>>(1);
489 tx.send(PersistMessage::Shutdown { ack: ack_tx })
490 .map_err(|e| StoreError::Custom(format!("shutdown send: {e}")))?;
491 ack_rx
492 .recv()
493 .map_err(|e| StoreError::Custom(format!("shutdown ack: {e}")))??;
494 backend.flush()
497 })
498 .await
499 .map_err(|e| StoreError::Custom(format!("shutdown join: {e}")))?
500 }
501
502 pub async fn add_block(&self, block: Block) -> Result<(), StoreError> {
505 self.add_blocks(vec![block]).await
506 }
507
508 pub async fn add_blocks(&self, blocks: Vec<Block>) -> Result<(), StoreError> {
511 let db = self.backend.clone();
512 tokio::task::spawn_blocking(move || {
513 let mut tx = db.begin_write()?;
514
515 for block in blocks {
516 write_block_data(
517 tx.as_mut(),
518 block.header.number,
519 block.hash(),
520 &block.header,
521 &block.body,
522 )?;
523 }
524
525 tx.commit()
526 })
527 .await
528 .map_err(|e| StoreError::Custom(format!("Task panicked: {}", e)))?
529 }
530
531 pub async fn add_block_header(
533 &self,
534 block_hash: BlockHash,
535 block_header: BlockHeader,
536 ) -> Result<(), StoreError> {
537 let hash_key = block_hash.encode_to_vec();
538 let header_value = BlockHeaderRLP::from(block_header).into_vec();
539 self.write_async(HEADERS, hash_key, header_value).await
540 }
541
542 pub async fn add_block_headers(
544 &self,
545 block_headers: Vec<BlockHeader>,
546 ) -> Result<(), StoreError> {
547 let mut txn = self.backend.begin_write()?;
548
549 for header in block_headers {
550 let block_hash = header.hash();
551 let block_number = header.number;
552 let hash_key = block_hash.encode_to_vec();
553 let header_value = BlockHeaderRLP::from(header).into_vec();
554
555 txn.put(HEADERS, &hash_key, &header_value)?;
556
557 let number_key = block_number.to_le_bytes().to_vec();
558 txn.put(BLOCK_NUMBERS, &hash_key, &number_key)?;
559 }
560 txn.commit()?;
561 Ok(())
562 }
563
564 pub fn get_block_header(
566 &self,
567 block_number: BlockNumber,
568 ) -> Result<Option<BlockHeader>, StoreError> {
569 let latest = self.latest_block_header.get();
570 if block_number == latest.number {
571 return Ok(Some((*latest).clone()));
572 }
573 let Some(block_hash) = self.get_canonical_block_hash_sync(block_number)? else {
578 return Ok(None);
579 };
580 self.get_block_header_by_hash(block_hash)
581 }
582
583 pub async fn add_block_body(
585 &self,
586 block_hash: BlockHash,
587 block_body: BlockBody,
588 ) -> Result<(), StoreError> {
589 let hash_key = block_hash.encode_to_vec();
590 let body_value = BlockBodyRLP::from(block_body).into_vec();
591 self.write_async(BODIES, hash_key, body_value).await
592 }
593
594 pub async fn add_backfilled_blocks(
612 &self,
613 blocks: Vec<BackfilledBlock>,
614 new_earliest: BlockNumber,
615 ) -> Result<(), StoreError> {
616 let backend = self.backend.clone();
617 tokio::task::spawn_blocking(move || {
618 let mut txn = backend.begin_write()?;
619 for block in &blocks {
620 let hash = block.header.hash();
621 let hash_key = hash.encode_to_vec();
622 txn.put(
623 BODIES,
624 &hash_key,
625 &BlockBodyRLP::from(block.body.clone()).into_vec(),
626 )?;
627 for (index, receipt) in block.receipts.iter().enumerate() {
628 txn.put(
629 RECEIPTS_V2,
630 &receipt_key(&hash, index as u64),
631 &receipt.encode_storage(),
632 )?;
633 }
634 if block.index_transactions {
635 for (index, transaction) in block.body.transactions.iter().enumerate() {
636 txn.merge(
637 TRANSACTION_LOCATIONS,
638 transaction.hash(&NativeCrypto).as_bytes(),
639 &encode_tx_location_operand(block.header.number, hash, index as u64),
640 )?;
641 }
642 }
643 }
644 txn.put(
645 CHAIN_DATA,
646 &chain_data_key(ChainDataIndex::EarliestBlockNumber),
647 &new_earliest.to_le_bytes(),
648 )?;
649 txn.commit()
650 })
651 .await
652 .map_err(|e| StoreError::Custom(format!("Backfill write task panicked: {e}")))?
653 }
654
655 pub fn rocksdb_stats(&self) -> Option<crate::api::RocksDbStats> {
659 self.backend.db_stats()
660 }
661
662 pub async fn get_block_body(
664 &self,
665 block_number: BlockNumber,
666 ) -> Result<Option<BlockBody>, StoreError> {
667 let Some(block_hash) = self.get_canonical_block_hash_sync(block_number)? else {
668 return Ok(None);
669 };
670
671 self.get_block_body_by_hash(block_hash).await
672 }
673
674 pub async fn remove_block(&self, block_number: BlockNumber) -> Result<(), StoreError> {
676 let Some(hash) = self.get_canonical_block_hash_sync(block_number)? else {
677 return Ok(());
678 };
679
680 let backend = self.backend.clone();
681 tokio::task::spawn_blocking(move || {
682 let hash_key = hash.encode_to_vec();
683
684 let mut txn = backend.begin_write()?;
685 txn.delete(
686 CANONICAL_BLOCK_HASHES,
687 block_number.to_le_bytes().as_slice(),
688 )?;
689 txn.delete(BODIES, &hash_key)?;
690 txn.delete(HEADERS, &hash_key)?;
691 txn.delete(BLOCK_NUMBERS, &hash_key)?;
692 txn.commit()
693 })
694 .await
695 .map_err(|e| StoreError::Custom(format!("Task panicked: {}", e)))?
696 }
697
698 pub async fn get_block_bodies(
700 &self,
701 from: BlockNumber,
702 to: BlockNumber,
703 ) -> Result<Vec<Option<BlockBody>>, StoreError> {
704 let buffer = self.buffer()?;
706 let backend = self.backend.clone();
707 tokio::task::spawn_blocking(move || {
708 let numbers: Vec<BlockNumber> = (from..=to).collect();
709 let mut block_bodies = Vec::new();
710
711 let txn = backend.begin_read()?;
712 for number in numbers {
713 let Some(hash) = txn
714 .get(CANONICAL_BLOCK_HASHES, number.to_le_bytes().as_slice())?
715 .map(|bytes| H256::decode(bytes.as_slice()))
716 .transpose()?
717 else {
718 block_bodies.push(None);
719 continue;
720 };
721 if let Some(body) = buffer.get_body(&hash) {
724 block_bodies.push(Some(body));
725 continue;
726 }
727 let hash_key = hash.encode_to_vec();
728 let block_body_opt = txn
729 .get(BODIES, &hash_key)?
730 .map(|bytes| BlockBodyRLP::from_bytes(bytes).to())
731 .transpose()
732 .map_err(StoreError::from)?;
733
734 block_bodies.push(block_body_opt);
735 }
736
737 Ok(block_bodies)
738 })
739 .await
740 .map_err(|e| StoreError::Custom(format!("Task panicked: {}", e)))?
741 }
742
743 pub async fn get_block_bodies_by_hash(
745 &self,
746 hashes: Vec<BlockHash>,
747 ) -> Result<Vec<BlockBody>, StoreError> {
748 let buffer = self.buffer()?;
749 let backend = self.backend.clone();
750 tokio::task::spawn_blocking(move || {
752 let txn = backend.begin_read()?;
753 let mut block_bodies = Vec::new();
754 for hash in hashes {
755 if let Some(body) = buffer.get_body(&hash) {
758 block_bodies.push(body);
759 continue;
760 }
761 let hash_key = hash.encode_to_vec();
762
763 let Some(block_body) = txn
764 .get(BODIES, &hash_key)?
765 .map(|bytes| BlockBodyRLP::from_bytes(bytes).to())
766 .transpose()
767 .map_err(StoreError::from)?
768 else {
769 return Err(StoreError::Custom(format!(
770 "Block body not found for hash: {hash}"
771 )));
772 };
773 block_bodies.push(block_body);
774 }
775 Ok(block_bodies)
776 })
777 .await
778 .map_err(|e| StoreError::Custom(format!("Task panicked: {}", e)))?
779 }
780
781 pub async fn get_block_body_by_hash(
783 &self,
784 block_hash: BlockHash,
785 ) -> Result<Option<BlockBody>, StoreError> {
786 if let Some(b) = self.buffer()?.get_body(&block_hash) {
787 return Ok(Some(b));
788 }
789 self.read_async(BODIES, block_hash.encode_to_vec())
790 .await?
791 .map(|bytes| BlockBodyRLP::from_bytes(bytes).to())
792 .transpose()
793 .map_err(StoreError::from)
794 }
795
796 pub fn get_block_header_by_hash(
797 &self,
798 block_hash: BlockHash,
799 ) -> Result<Option<BlockHeader>, StoreError> {
800 let latest = self.latest_block_header.get();
801 if block_hash == latest.hash() {
802 return Ok(Some((*latest).clone()));
803 }
804 if let Some(h) = self.buffer()?.get_header(&block_hash) {
805 return Ok(Some(h));
806 }
807 self.load_block_header_by_hash(block_hash)
808 }
809
810 pub fn add_pending_block(&self, block: Block) -> Result<(), StoreError> {
811 let block_hash = block.hash();
812 let block_value = BlockRLP::from(block).into_vec();
813 self.write(PENDING_BLOCKS, block_hash.as_bytes().to_vec(), block_value)
814 }
815
816 pub async fn get_pending_block(
817 &self,
818 block_hash: BlockHash,
819 ) -> Result<Option<Block>, StoreError> {
820 self.read_async(PENDING_BLOCKS, block_hash.as_bytes().to_vec())
821 .await?
822 .map(|bytes| BlockRLP::from_bytes(bytes).to())
823 .transpose()
824 .map_err(StoreError::from)
825 }
826
827 pub async fn add_block_number(
829 &self,
830 block_hash: BlockHash,
831 block_number: BlockNumber,
832 ) -> Result<(), StoreError> {
833 let number_value = block_number.to_le_bytes().to_vec();
834 self.write_async(BLOCK_NUMBERS, block_hash.encode_to_vec(), number_value)
835 .await
836 }
837
838 pub async fn get_block_number(
840 &self,
841 block_hash: BlockHash,
842 ) -> Result<Option<BlockNumber>, StoreError> {
843 if let Some(n) = self.buffer()?.get_number(&block_hash) {
844 return Ok(Some(n));
845 }
846 self.read_async(BLOCK_NUMBERS, block_hash.encode_to_vec())
847 .await?
848 .map(decode_block_number)
849 .transpose()
850 }
851
852 pub async fn add_transaction_location(
854 &self,
855 transaction_hash: H256,
856 block_number: BlockNumber,
857 block_hash: BlockHash,
858 index: Index,
859 ) -> Result<(), StoreError> {
860 self.add_transaction_locations(vec![(transaction_hash, block_number, block_hash, index)])
861 .await
862 }
863
864 pub async fn add_transaction_locations(
866 &self,
867 locations: Vec<(H256, BlockNumber, BlockHash, Index)>,
868 ) -> Result<(), StoreError> {
869 let db = self.backend.clone();
870 tokio::task::spawn_blocking(move || {
871 let mut tx = db.begin_write()?;
872 for (tx_hash, block_number, block_hash, index) in locations {
873 tx.merge(
874 TRANSACTION_LOCATIONS,
875 tx_hash.as_bytes(),
876 &encode_tx_location_operand(block_number, block_hash, index),
877 )?;
878 }
879 tx.commit()
880 })
881 .await
882 .map_err(|e| StoreError::Custom(format!("Task panicked: {}", e)))?
883 }
884
885 pub async fn get_transaction_location(
887 &self,
888 transaction_hash: H256,
889 ) -> Result<Option<(BlockNumber, BlockHash, Index)>, StoreError> {
890 let buffered = self.buffer()?.get_tx_locations(&transaction_hash);
891 let db = self.backend.clone();
892 tokio::task::spawn_blocking(move || {
893 let tx = db.begin_read()?;
894 let mut locations = buffered;
895 if let Some(bytes) = tx.get(TRANSACTION_LOCATIONS, transaction_hash.as_bytes())? {
896 locations.extend(<Vec<(BlockNumber, BlockHash, Index)>>::decode(&bytes)?);
897 }
898 for (block_number, block_hash, index) in locations {
899 let canonical_hash = tx
900 .get(
901 CANONICAL_BLOCK_HASHES,
902 block_number.to_le_bytes().as_slice(),
903 )?
904 .map(|bytes| H256::decode(bytes.as_slice()))
905 .transpose()?;
906 if canonical_hash == Some(block_hash) {
907 return Ok(Some((block_number, block_hash, index)));
908 }
909 }
910 Ok(None)
911 })
912 .await
913 .map_err(|e| StoreError::Custom(format!("Task panicked: {}", e)))?
914 }
915
916 pub async fn add_receipt(
918 &self,
919 block_hash: BlockHash,
920 index: Index,
921 receipt: Receipt,
922 ) -> Result<(), StoreError> {
923 let key = receipt_key(&block_hash, index);
924 let value = receipt.encode_storage();
928 self.write_async(RECEIPTS_V2, key, value).await
929 }
930
931 pub async fn add_receipts(
933 &self,
934 block_hash: BlockHash,
935 receipts: Vec<Receipt>,
936 ) -> Result<(), StoreError> {
937 let batch_items: Vec<_> = receipts
938 .into_iter()
939 .enumerate()
940 .map(|(index, receipt)| {
941 let key = receipt_key(&block_hash, index as u64);
942 let value = receipt.encode_storage();
943 (key, value)
944 })
945 .collect();
946 self.write_batch_async(RECEIPTS_V2, batch_items).await
947 }
948
949 pub async fn get_receipt(
951 &self,
952 block_number: BlockNumber,
953 index: Index,
954 ) -> Result<Option<Receipt>, StoreError> {
955 let Some(block_hash) = self.get_canonical_block_hash(block_number).await? else {
957 return Ok(None);
958 };
959 self.get_receipt_by_block_hash(block_hash, index).await
960 }
961
962 async fn get_receipt_by_block_hash(
964 &self,
965 block_hash: BlockHash,
966 index: Index,
967 ) -> Result<Option<Receipt>, StoreError> {
968 if let Some(r) = self.buffer()?.get_receipt(&block_hash, index) {
969 return Ok(Some(r));
970 }
971 let key = receipt_key(&block_hash, index);
972 self.read_async(RECEIPTS_V2, key)
973 .await?
974 .map(|bytes| Receipt::decode_storage(bytes.as_slice()))
975 .transpose()
976 .map_err(StoreError::from)
977 }
978
979 pub fn get_account_code(&self, code_hash: H256) -> Result<Option<Code>, StoreError> {
986 if let Some(code) = self.buffer()?.get_code(&code_hash) {
987 return Ok(Some(code));
988 }
989 if let Some(code) = self
991 .account_code_cache
992 .lock()
993 .map_err(|_| StoreError::LockError)?
994 .get(&code_hash)?
995 {
996 return Ok(Some(code));
997 }
998
999 let Some(bytes) = self
1000 .backend
1001 .begin_read()?
1002 .get(ACCOUNT_CODES, code_hash.as_bytes())?
1003 else {
1004 return Ok(None);
1005 };
1006 let (bytecode_slice, targets) = decode_bytes(&bytes)?;
1007 let code = Code::from_parts_unchecked(
1008 code_hash,
1009 bytecode_slice,
1010 <Vec<u32>>::decode(targets)?.into(),
1011 );
1012
1013 self.account_code_cache
1015 .lock()
1016 .map_err(|_| StoreError::LockError)?
1017 .insert(&code)?;
1018
1019 Ok(Some(code))
1020 }
1021
1022 pub fn code_exists(&self, code_hash: H256) -> Result<bool, StoreError> {
1027 if self.buffer()?.get_code(&code_hash).is_some() {
1031 return Ok(true);
1032 }
1033 if self
1035 .account_code_cache
1036 .lock()
1037 .map_err(|_| StoreError::LockError)?
1038 .get(&code_hash)?
1039 .is_some()
1040 {
1041 return Ok(true);
1042 }
1043 Ok(self
1045 .backend
1046 .begin_read()?
1047 .get(ACCOUNT_CODES, code_hash.as_bytes())?
1048 .is_some())
1049 }
1050
1051 pub fn get_code_metadata(&self, code_hash: H256) -> Result<Option<CodeMetadata>, StoreError> {
1056 use ethrex_common::constants::EMPTY_KECCAK_HASH;
1057
1058 if code_hash == *EMPTY_KECCAK_HASH {
1060 return Ok(Some(CodeMetadata { length: 0 }));
1061 }
1062
1063 if let Some(metadata) = self
1065 .code_metadata_cache
1066 .lock()
1067 .map_err(|_| StoreError::LockError)?
1068 .get(&code_hash)
1069 .copied()
1070 {
1071 return Ok(Some(metadata));
1072 }
1073
1074 let metadata = if let Some(bytes) = self
1076 .backend
1077 .begin_read()?
1078 .get(ACCOUNT_CODE_METADATA, code_hash.as_bytes())?
1079 {
1080 let length =
1081 u64::from_be_bytes(bytes.try_into().map_err(|_| {
1082 StoreError::Custom("Invalid metadata length encoding".to_string())
1083 })?);
1084 CodeMetadata { length }
1085 } else {
1086 let Some(code) = self.get_account_code(code_hash)? else {
1088 return Ok(None);
1089 };
1090 let metadata = CodeMetadata {
1091 length: code.len() as u64,
1092 };
1093
1094 let metadata_buf = metadata.length.to_be_bytes().to_vec();
1096 let hash_key = code_hash.0.to_vec();
1097 let backend = self.backend.clone();
1098 tokio::task::spawn(async move {
1099 if let Err(e) = async {
1100 let mut tx = backend.begin_write()?;
1101 tx.put(ACCOUNT_CODE_METADATA, &hash_key, &metadata_buf)?;
1102 tx.commit()
1103 }
1104 .await
1105 {
1106 tracing::warn!("Failed to write code metadata during auto-migration: {}", e);
1107 }
1108 });
1109
1110 metadata
1111 };
1112
1113 self.code_metadata_cache
1115 .lock()
1116 .map_err(|_| StoreError::LockError)?
1117 .insert(code_hash, metadata);
1118
1119 Ok(Some(metadata))
1120 }
1121
1122 pub async fn add_account_code(&self, code: Code) -> Result<(), StoreError> {
1124 let hash_key = code.hash.0.to_vec();
1125 let buf = encode_code(&code);
1126 let metadata_buf = (code.len() as u64).to_be_bytes();
1127
1128 let backend = self.backend.clone();
1130 tokio::task::spawn_blocking(move || {
1131 let mut tx = backend.begin_write()?;
1132 tx.put(ACCOUNT_CODES, &hash_key, &buf)?;
1133 tx.put(ACCOUNT_CODE_METADATA, &hash_key, &metadata_buf)?;
1134 tx.commit()
1135 })
1136 .await
1137 .map_err(|e| StoreError::Custom(format!("Task panicked: {}", e)))?
1138 }
1139
1140 pub async fn clear_snap_state(&self) -> Result<(), StoreError> {
1142 let db = self.backend.clone();
1143 tokio::task::spawn_blocking(move || db.clear_table(SNAP_STATE))
1144 .await
1145 .map_err(|e| StoreError::Custom(format!("Task panicked: {}", e)))?
1146 }
1147
1148 pub async fn get_transaction_by_hash(
1149 &self,
1150 transaction_hash: H256,
1151 ) -> Result<Option<Transaction>, StoreError> {
1152 let (_block_number, block_hash, index) =
1153 match self.get_transaction_location(transaction_hash).await? {
1154 Some(location) => location,
1155 None => return Ok(None),
1156 };
1157 self.get_transaction_by_location(block_hash, index).await
1158 }
1159
1160 pub async fn get_transaction_by_location(
1161 &self,
1162 block_hash: H256,
1163 index: u64,
1164 ) -> Result<Option<Transaction>, StoreError> {
1165 let block_body = match self.get_block_body_by_hash(block_hash).await? {
1166 Some(body) => body,
1167 None => return Ok(None),
1168 };
1169 let index: usize = index.try_into()?;
1170 Ok(block_body.transactions.get(index).cloned())
1171 }
1172
1173 pub async fn get_block_by_hash(
1174 &self,
1175 block_hash: BlockHash,
1176 ) -> Result<Option<Block>, StoreError> {
1177 let header = match self.get_block_header_by_hash(block_hash)? {
1178 Some(header) => header,
1179 None => return Ok(None),
1180 };
1181 let body = match self.get_block_body_by_hash(block_hash).await? {
1182 Some(body) => body,
1183 None => return Ok(None),
1184 };
1185 Ok(Some(Block::new(header, body)))
1186 }
1187
1188 pub async fn get_block_by_number(
1189 &self,
1190 block_number: BlockNumber,
1191 ) -> Result<Option<Block>, StoreError> {
1192 let Some(block_hash) = self.get_canonical_block_hash(block_number).await? else {
1193 return Ok(None);
1194 };
1195 self.get_block_by_hash(block_hash).await
1196 }
1197
1198 pub async fn get_canonical_block_hash(
1200 &self,
1201 block_number: BlockNumber,
1202 ) -> Result<Option<BlockHash>, StoreError> {
1203 let last = self.latest_block_header.get();
1204 if last.number == block_number {
1205 return Ok(Some(last.hash()));
1206 }
1207 let backend = self.backend.clone();
1208 tokio::task::spawn_blocking(move || {
1209 backend
1210 .begin_read()?
1211 .get(
1212 CANONICAL_BLOCK_HASHES,
1213 block_number.to_le_bytes().as_slice(),
1214 )?
1215 .map(|bytes| H256::decode(bytes.as_slice()))
1216 .transpose()
1217 .map_err(StoreError::from)
1218 })
1219 .await
1220 .map_err(|e| StoreError::Custom(format!("Task panicked: {}", e)))?
1221 }
1222
1223 pub async fn set_chain_config(&mut self, chain_config: &ChainConfig) -> Result<(), StoreError> {
1226 self.chain_config = *chain_config;
1227 let key = chain_data_key(ChainDataIndex::ChainConfig);
1228 let value = serde_json::to_string(chain_config)
1229 .map_err(|_| StoreError::Custom("Failed to serialize chain config".to_string()))?
1230 .into_bytes();
1231 self.write_async(CHAIN_DATA, key, value).await
1232 }
1233
1234 pub async fn update_earliest_block_number(
1236 &self,
1237 block_number: BlockNumber,
1238 ) -> Result<(), StoreError> {
1239 let key = chain_data_key(ChainDataIndex::EarliestBlockNumber);
1240 let value = block_number.to_le_bytes().to_vec();
1241 self.write_async(CHAIN_DATA, key, value).await
1242 }
1243
1244 async fn read_chain_data_block_number(
1246 &self,
1247 index: ChainDataIndex,
1248 ) -> Result<Option<BlockNumber>, StoreError> {
1249 self.read_async(CHAIN_DATA, chain_data_key(index))
1250 .await?
1251 .map(decode_block_number)
1252 .transpose()
1253 }
1254
1255 pub async fn get_earliest_block_number(&self) -> Result<BlockNumber, StoreError> {
1257 self.read_chain_data_block_number(ChainDataIndex::EarliestBlockNumber)
1258 .await?
1259 .ok_or(StoreError::MissingEarliestBlockNumber)
1260 }
1261
1262 pub async fn get_finalized_block_number(&self) -> Result<Option<BlockNumber>, StoreError> {
1264 self.read_chain_data_block_number(ChainDataIndex::FinalizedBlockNumber)
1265 .await
1266 }
1267
1268 pub async fn get_safe_block_number(&self) -> Result<Option<BlockNumber>, StoreError> {
1270 self.read_chain_data_block_number(ChainDataIndex::SafeBlockNumber)
1271 .await
1272 }
1273
1274 pub fn get_latest_block_number(&self) -> Result<BlockNumber, StoreError> {
1276 Ok(self.latest_block_header.get().number)
1277 }
1278
1279 pub async fn update_pending_block_number(
1281 &self,
1282 block_number: BlockNumber,
1283 ) -> Result<(), StoreError> {
1284 let key = chain_data_key(ChainDataIndex::PendingBlockNumber);
1285 let value = block_number.to_le_bytes().to_vec();
1286 self.write_async(CHAIN_DATA, key, value).await
1287 }
1288
1289 pub async fn get_pending_block_number(&self) -> Result<Option<BlockNumber>, StoreError> {
1291 self.read_chain_data_block_number(ChainDataIndex::PendingBlockNumber)
1292 .await
1293 }
1294
1295 async fn forkchoice_update_inner(
1310 &self,
1311 new_canonical_blocks: Vec<(BlockNumber, BlockHash)>,
1312 head_number: BlockNumber,
1313 head_hash: BlockHash,
1314 safe: Option<BlockNumber>,
1315 finalized: Option<BlockNumber>,
1316 ) -> Result<(), StoreError> {
1317 let latest = self.load_latest_block_number().await?.unwrap_or(0);
1318 let db = self.backend.clone();
1319 let journal_pruning_paused = self.journal_pruning_paused.clone();
1320 tokio::task::spawn_blocking(move || {
1321 let mut txn = db.begin_write()?;
1322
1323 for (block_number, block_hash) in new_canonical_blocks {
1324 let head_key = block_number.to_le_bytes();
1325 let head_value = block_hash.encode_to_vec();
1326 txn.put(CANONICAL_BLOCK_HASHES, &head_key, &head_value)?;
1327 }
1328
1329 for number in (head_number + 1)..=(latest) {
1336 txn.delete(CANONICAL_BLOCK_HASHES, number.to_le_bytes().as_slice())?;
1337 }
1338
1339 let head_key = head_number.to_le_bytes();
1341 let head_value = head_hash.encode_to_vec();
1342 txn.put(CANONICAL_BLOCK_HASHES, &head_key, &head_value)?;
1343
1344 let latest_key = chain_data_key(ChainDataIndex::LatestBlockNumber);
1346 txn.put(CHAIN_DATA, &latest_key, &head_number.to_le_bytes())?;
1347
1348 if let Some(safe) = safe {
1349 let safe_key = chain_data_key(ChainDataIndex::SafeBlockNumber);
1350 txn.put(CHAIN_DATA, &safe_key, &safe.to_le_bytes())?;
1351 }
1352
1353 if let Some(finalized) = finalized {
1354 let finalized_key = chain_data_key(ChainDataIndex::FinalizedBlockNumber);
1355
1356 let prev_finalized = match db.begin_read()?.get(CHAIN_DATA, &finalized_key)? {
1366 Some(bytes) => {
1367 let arr: [u8; 8] = bytes.as_slice().try_into().map_err(|_| {
1368 StoreError::Custom(format!(
1369 "FinalizedBlockNumber has unexpected length {} (want 8)",
1370 bytes.len()
1371 ))
1372 })?;
1373 BlockNumber::from_le_bytes(arr)
1374 }
1375 None => 0,
1376 };
1377
1378 txn.put(CHAIN_DATA, &finalized_key, &finalized.to_le_bytes())?;
1379
1380 if finalized > prev_finalized
1392 && !journal_pruning_paused.load(std::sync::atomic::Ordering::Acquire)
1393 {
1394 let start = 0u64.to_be_bytes();
1395 let end = finalized.saturating_add(1).to_be_bytes();
1396 txn.delete_range(STATE_HISTORY, &start, &end)?;
1397 }
1398 }
1399
1400 txn.commit()
1401 })
1402 .await
1403 .map_err(|e| StoreError::Custom(format!("Task panicked: {}", e)))?
1404 }
1405
1406 pub async fn get_receipts_for_block(
1407 &self,
1408 block_hash: &BlockHash,
1409 ) -> Result<Vec<Receipt>, StoreError> {
1410 self.get_receipts_for_block_from_index(block_hash, 0, None)
1411 .await
1412 }
1413
1414 pub async fn get_receipts_for_block_from_index(
1423 &self,
1424 block_hash: &BlockHash,
1425 start_index: u64,
1426 max_count: Option<usize>,
1427 ) -> Result<Vec<Receipt>, StoreError> {
1428 if let Some(all) = self.buffer()?.get_receipts(block_hash) {
1429 let start = start_index as usize;
1430 let slice = all.into_iter().skip(start);
1431 return Ok(match max_count {
1432 Some(max) => slice.take(max).collect(),
1433 None => slice.collect(),
1434 });
1435 }
1436 let backend = self.backend.clone();
1437 let block_hash = *block_hash;
1438
1439 tokio::task::spawn_blocking(move || {
1440 let txn = backend.begin_read()?;
1441 let prefix = block_hash.as_bytes().to_vec();
1442 let mut seek_key = prefix.clone();
1445 seek_key.extend_from_slice(&start_index.to_be_bytes());
1446 let iter = txn.prefix_iterator(RECEIPTS_V2, &seek_key)?;
1447 let mut receipts = Vec::new();
1448 for result in iter {
1449 let (k, v) = result?;
1450 if !k.starts_with(&prefix) {
1451 break;
1452 }
1453 if k.len() != 40 {
1454 continue;
1455 }
1456 receipts.push(Receipt::decode_storage(v.as_ref())?);
1457 if let Some(max) = max_count
1458 && receipts.len() >= max
1459 {
1460 break;
1461 }
1462 }
1463 Ok(receipts)
1464 })
1465 .await
1466 .map_err(|e| StoreError::Custom(format!("Task panicked: {e}")))?
1467 }
1468
1469 pub async fn set_header_download_checkpoint(
1473 &self,
1474 block_hash: BlockHash,
1475 ) -> Result<(), StoreError> {
1476 let key = snap_state_key(SnapStateIndex::HeaderDownloadCheckpoint);
1477 let value = block_hash.encode_to_vec();
1478 self.write_async(SNAP_STATE, key, value).await
1479 }
1480
1481 pub async fn get_header_download_checkpoint(&self) -> Result<Option<BlockHash>, StoreError> {
1483 let key = snap_state_key(SnapStateIndex::HeaderDownloadCheckpoint);
1484 self.backend
1485 .begin_read()?
1486 .get(SNAP_STATE, &key)?
1487 .map(|bytes| H256::decode(bytes.as_slice()))
1488 .transpose()
1489 .map_err(StoreError::from)
1490 }
1491
1492 pub async fn set_latest_valid_ancestor(
1498 &self,
1499 bad_block: BlockHash,
1500 latest_valid: BlockHash,
1501 ) -> Result<(), StoreError> {
1502 let value = latest_valid.encode_to_vec();
1503 self.write_async(INVALID_CHAINS, bad_block.as_bytes().to_vec(), value)
1504 .await
1505 }
1506
1507 pub async fn get_latest_valid_ancestor(
1510 &self,
1511 block: BlockHash,
1512 ) -> Result<Option<BlockHash>, StoreError> {
1513 self.read_async(INVALID_CHAINS, block.as_bytes().to_vec())
1514 .await?
1515 .map(|bytes| H256::decode(bytes.as_slice()))
1516 .transpose()
1517 .map_err(StoreError::from)
1518 }
1519
1520 pub async fn add_bad_block(&self, block: Block) -> Result<(), StoreError> {
1525 let mut bad_blocks = self.get_bad_blocks().await?;
1526 let block_number = block.header.number;
1527 let block_hash = block.hash();
1528 if bad_blocks
1529 .iter()
1530 .any(|b| b.header.number == block_number && b.hash() == block_hash)
1531 {
1532 return Ok(());
1533 }
1534 bad_blocks.push(block);
1535 bad_blocks.sort_by(|a, b| b.header.number.cmp(&a.header.number));
1536 bad_blocks.truncate(MAX_BAD_BLOCKS);
1537 self.write_async(
1538 BAD_BLOCKS,
1539 BAD_BLOCKS_KEY.to_vec(),
1540 bad_blocks.encode_to_vec(),
1541 )
1542 .await
1543 }
1544
1545 pub async fn get_bad_blocks(&self) -> Result<Vec<Block>, StoreError> {
1548 match self.read_async(BAD_BLOCKS, BAD_BLOCKS_KEY.to_vec()).await? {
1549 Some(bytes) => Vec::<Block>::decode(&bytes).map_err(StoreError::from),
1550 None => Ok(Vec::new()),
1551 }
1552 }
1553
1554 pub fn get_block_number_sync(
1556 &self,
1557 block_hash: BlockHash,
1558 ) -> Result<Option<BlockNumber>, StoreError> {
1559 if let Some(n) = self.buffer()?.get_number(&block_hash) {
1560 return Ok(Some(n));
1561 }
1562 let txn = self.backend.begin_read()?;
1563 txn.get(BLOCK_NUMBERS, &block_hash.encode_to_vec())?
1564 .map(decode_block_number)
1565 .transpose()
1566 }
1567
1568 pub fn get_canonical_block_hash_sync(
1570 &self,
1571 block_number: BlockNumber,
1572 ) -> Result<Option<BlockHash>, StoreError> {
1573 let last = self.latest_block_header.get();
1574 if last.number == block_number {
1575 return Ok(Some(last.hash()));
1576 }
1577 let txn = self.backend.begin_read()?;
1578 txn.get(
1579 CANONICAL_BLOCK_HASHES,
1580 block_number.to_le_bytes().as_slice(),
1581 )?
1582 .map(|bytes| H256::decode(bytes.as_slice()))
1583 .transpose()
1584 .map_err(StoreError::from)
1585 }
1586
1587 pub async fn write_storage_trie_nodes_batch(
1590 &self,
1591 storage_trie_nodes: StorageUpdates,
1592 ) -> Result<(), StoreError> {
1593 let mut txn = self.backend.begin_write()?;
1594 tokio::task::spawn_blocking(move || {
1595 for (address_hash, nodes) in storage_trie_nodes {
1596 for (node_path, node_data) in nodes {
1597 let key = apply_prefix(Some(address_hash), node_path);
1598 if node_data.is_empty() {
1599 txn.delete(STORAGE_TRIE_NODES, key.as_ref())?;
1600 } else {
1601 txn.put(STORAGE_TRIE_NODES, key.as_ref(), &node_data)?;
1602 }
1603 }
1604 }
1605 txn.commit()
1606 })
1607 .await
1608 .map_err(|e| StoreError::Custom(format!("Task panicked: {}", e)))?
1609 }
1610
1611 pub async fn write_account_code_batch(
1614 &self,
1615 account_codes: Vec<(H256, Code)>,
1616 ) -> Result<(), StoreError> {
1617 let mut code_batch_items = Vec::new();
1618 let mut metadata_batch_items = Vec::new();
1619
1620 for (code_hash, code) in account_codes {
1621 let buf = encode_code(&code);
1622 let metadata_buf = (code.len() as u64).to_be_bytes().to_vec();
1623 code_batch_items.push((code_hash.as_bytes().to_vec(), buf));
1624 metadata_batch_items.push((code_hash.as_bytes().to_vec(), metadata_buf));
1625 }
1626
1627 self.write_batch_async(ACCOUNT_CODES, code_batch_items)
1629 .await?;
1630 self.write_batch_async(ACCOUNT_CODE_METADATA, metadata_batch_items)
1631 .await
1632 }
1633
1634 fn buffer(&self) -> Result<Arc<BlockDataBuffer>, StoreError> {
1636 Ok(self
1637 .block_data_buffer
1638 .read()
1639 .map_err(|_| StoreError::LockError)?
1640 .clone())
1641 }
1642
1643 pub fn write(
1649 &self,
1650 table: &'static str,
1651 key: Vec<u8>,
1652 value: Vec<u8>,
1653 ) -> Result<(), StoreError> {
1654 let backend = self.backend.clone();
1655 let mut txn = backend.begin_write()?;
1656 txn.put(table, &key, &value)?;
1657 txn.commit()
1658 }
1659
1660 async fn write_async(
1663 &self,
1664 table: &'static str,
1665 key: Vec<u8>,
1666 value: Vec<u8>,
1667 ) -> Result<(), StoreError> {
1668 let backend = self.backend.clone();
1669
1670 tokio::task::spawn_blocking(move || {
1671 let mut txn = backend.begin_write()?;
1672 txn.put(table, &key, &value)?;
1673 txn.commit()
1674 })
1675 .await
1676 .map_err(|e| StoreError::Custom(format!("Task panicked: {}", e)))?
1677 }
1678
1679 pub async fn read_async(
1682 &self,
1683 table: &'static str,
1684 key: Vec<u8>,
1685 ) -> Result<Option<Vec<u8>>, StoreError> {
1686 let backend = self.backend.clone();
1687
1688 tokio::task::spawn_blocking(move || {
1689 let txn = backend.begin_read()?;
1690 txn.get(table, &key)
1691 })
1692 .await
1693 .map_err(|e| StoreError::Custom(format!("Task panicked: {}", e)))?
1694 }
1695
1696 pub fn read(&self, table: &'static str, key: Vec<u8>) -> Result<Option<Vec<u8>>, StoreError> {
1699 let backend = self.backend.clone();
1700 let txn = backend.begin_read()?;
1701 txn.get(table, &key)
1702 }
1703
1704 pub async fn write_batch_async(
1708 &self,
1709 table: &'static str,
1710 batch_ops: Vec<(Vec<u8>, Vec<u8>)>,
1711 ) -> Result<(), StoreError> {
1712 let backend = self.backend.clone();
1713
1714 tokio::task::spawn_blocking(move || {
1715 let mut txn = backend.begin_write()?;
1716 txn.put_batch(table, batch_ops)?;
1717 txn.commit()
1718 })
1719 .await
1720 .map_err(|e| StoreError::Custom(format!("Task panicked: {}", e)))?
1721 }
1722
1723 pub fn write_batch(
1725 &self,
1726 table: &'static str,
1727 batch_ops: Vec<(Vec<u8>, Vec<u8>)>,
1728 ) -> Result<(), StoreError> {
1729 let backend = self.backend.clone();
1730 let mut txn = backend.begin_write()?;
1731 txn.put_batch(table, batch_ops)?;
1732 txn.commit()
1733 }
1734
1735 pub async fn add_fullsync_batch(&self, headers: Vec<BlockHeader>) -> Result<(), StoreError> {
1736 self.write_batch_async(
1737 FULLSYNC_HEADERS,
1738 headers
1739 .into_iter()
1740 .map(|header| (header.number.to_le_bytes().to_vec(), header.encode_to_vec()))
1741 .collect(),
1742 )
1743 .await
1744 }
1745
1746 pub async fn read_fullsync_batch(
1747 &self,
1748 start: BlockNumber,
1749 limit: u64,
1750 ) -> Result<Vec<Option<BlockHeader>>, StoreError> {
1751 let mut res = vec![];
1752 let read_tx = self.backend.begin_read()?;
1753 for key in start..start + limit {
1755 let header_opt = read_tx
1756 .get(FULLSYNC_HEADERS, &key.to_le_bytes())?
1757 .map(|header| BlockHeader::decode(&header))
1758 .transpose()?;
1759 res.push(header_opt);
1760 }
1761 Ok(res)
1762 }
1763
1764 pub async fn clear_fullsync_headers(&self) -> Result<(), StoreError> {
1765 self.backend.clear_table(FULLSYNC_HEADERS)
1766 }
1767
1768 pub fn delete(&self, table: &'static str, key: Vec<u8>) -> Result<(), StoreError> {
1770 let mut txn = self.backend.begin_write()?;
1771 txn.delete(table, &key)?;
1772 txn.commit()
1773 }
1774
1775 pub fn store_block_updates(&self, update_batch: UpdateBatch) -> Result<(), StoreError> {
1776 self.apply_updates(update_batch)
1777 }
1778
1779 fn batch_state_roots(
1786 &self,
1787 update_batch: &UpdateBatch,
1788 ) -> Result<(H256, H256, BlockNumber, H256), StoreError> {
1789 let parent_state_root = self
1790 .get_block_header_by_hash(
1791 update_batch
1792 .blocks
1793 .first()
1794 .ok_or(StoreError::UpdateBatchNoBlocks)?
1795 .header
1796 .parent_hash,
1797 )?
1798 .map(|header| header.state_root)
1799 .unwrap_or_default();
1800 let last_block = update_batch
1801 .blocks
1802 .last()
1803 .ok_or(StoreError::UpdateBatchNoBlocks)?;
1804 let last_state_root = last_block.header.state_root;
1805 let last_block_number = last_block.header.number;
1806 let last_block_hash = last_block.hash();
1807 Ok((
1808 parent_state_root,
1809 last_state_root,
1810 last_block_number,
1811 last_block_hash,
1812 ))
1813 }
1814
1815 fn apply_updates(&self, update_batch: UpdateBatch) -> Result<(), StoreError> {
1819 let (parent_state_root, last_state_root, last_block_number, last_block_hash) =
1820 self.batch_state_roots(&update_batch)?;
1821
1822 let UpdateBatch {
1823 account_updates,
1824 storage_updates,
1825 blocks,
1826 receipts,
1827 code_updates,
1828 commit_depth,
1829 wait_for_flush,
1830 } = update_batch;
1831
1832 self.pending_trie_roots.register(last_state_root)?;
1836
1837 let blocks_with_receipts: Vec<(Block, Vec<Receipt>)> = if blocks.len() == 1 {
1840 let block = blocks.into_iter().next().expect("len == 1");
1841 let hash = block.hash();
1842 let r = receipts
1843 .into_iter()
1844 .find(|(h, _)| *h == hash)
1845 .map(|(_, r)| r)
1846 .unwrap_or_default();
1847 vec![(block, r)]
1848 } else {
1849 let mut receipts_by_hash: std::collections::HashMap<BlockHash, Vec<Receipt>> =
1850 receipts.into_iter().collect();
1851 blocks
1852 .into_iter()
1853 .map(|b| {
1854 let r = receipts_by_hash.remove(&b.hash()).unwrap_or_default();
1855 (b, r)
1856 })
1857 .collect()
1858 };
1859
1860 let (ack_tx, ack_rx) = sync_channel(1);
1865 self.persist_tx
1866 .send(PersistMessage::Block(Box::new(BlockPersist {
1867 blocks: blocks_with_receipts,
1868 codes: code_updates,
1869 parent_state_root,
1870 child_state_root: last_state_root,
1871 account_updates,
1872 storage_updates,
1873 commit_depth,
1874 wait_for_flush,
1875 block_number: last_block_number,
1876 block_hash: last_block_hash,
1877 ack: ack_tx,
1878 })))
1879 .map_err(|e| StoreError::Custom(format!("failed to send block persist: {e}")))?;
1880 ack_rx
1881 .recv()
1882 .map_err(|e| StoreError::Custom(format!("block persist ack failed: {e}")))??;
1883
1884 Ok(())
1885 }
1886
1887 pub fn new(path: impl AsRef<Path>, engine_type: EngineType) -> Result<Self, StoreError> {
1892 Self::new_with_config(path, engine_type, StoreConfig::default())
1893 }
1894
1895 pub fn new_with_config(
1897 path: impl AsRef<Path>,
1898 engine_type: EngineType,
1899 #[cfg_attr(not(feature = "rocksdb"), allow(unused_variables))] config: StoreConfig,
1901 ) -> Result<Self, StoreError> {
1902 let db_path = path.as_ref().to_path_buf();
1903
1904 if engine_type != EngineType::InMemory {
1905 let version = read_store_schema_version(&db_path)?;
1906
1907 match version {
1908 None if db_path.exists() && dir_contains_legacy_db(&db_path)? => {
1909 return Err(StoreError::NotFoundDBVersion);
1911 }
1912 None => {
1913 init_metadata_file(&db_path)?;
1919 }
1920 Some(v) if v < 1 => {
1921 return Err(StoreError::MigrationFailed {
1922 from: v,
1923 to: STORE_SCHEMA_VERSION,
1924 reason: format!("DB version v{v} is invalid (predates migrations)"),
1925 });
1926 }
1927 Some(v) if v > STORE_SCHEMA_VERSION => {
1928 return Err(StoreError::MigrationFailed {
1929 from: v,
1930 to: STORE_SCHEMA_VERSION,
1931 reason: format!(
1932 "DB version v{v} is more recent than the client expects (v{STORE_SCHEMA_VERSION}). Rolling back is not supported"
1933 ),
1934 });
1935 }
1936 #[cfg(feature = "rocksdb")]
1937 Some(v) if v < STORE_SCHEMA_VERSION => {
1938 let rocksdb = Arc::new(RocksDBBackend::open(
1942 &path,
1943 config.rocksdb_block_cache_size,
1944 )?);
1945 crate::migrations::run_pending_migrations(rocksdb.as_ref(), &db_path, v)?;
1946 rocksdb.drop_obsolete_cfs(&path);
1947 let backend: Arc<dyn crate::api::StorageBackend> = rocksdb;
1948 return Self::from_backend(
1949 backend,
1950 db_path,
1951 DB_COMMIT_THRESHOLD,
1952 config.persist_channel_capacity,
1953 );
1954 }
1955 Some(_) => {
1956 }
1961 }
1962 }
1963
1964 match engine_type {
1965 #[cfg(feature = "rocksdb")]
1966 EngineType::RocksDB => {
1967 let rocksdb = RocksDBBackend::open(&path, config.rocksdb_block_cache_size)?;
1968 rocksdb.drop_obsolete_cfs(&path);
1969 let backend: Arc<dyn StorageBackend> = Arc::new(rocksdb);
1970 Self::from_backend(
1971 backend,
1972 db_path,
1973 DB_COMMIT_THRESHOLD,
1974 config.persist_channel_capacity,
1975 )
1976 }
1977 EngineType::InMemory => {
1978 let backend = Arc::new(InMemoryBackend::open()?);
1979 Self::from_backend(
1980 backend,
1981 db_path,
1982 IN_MEMORY_COMMIT_THRESHOLD,
1983 config.persist_channel_capacity,
1984 )
1985 }
1986 }
1987 }
1988
1989 fn from_backend(
1990 backend: Arc<dyn StorageBackend>,
1991 db_path: PathBuf,
1992 commit_threshold: usize,
1993 persist_channel_capacity: usize,
1994 ) -> Result<Self, StoreError> {
1995 debug!("Initializing Store with {commit_threshold} in-memory diff-layers");
1996 let (fkv_tx, fkv_rx) = std::sync::mpsc::sync_channel(0);
1997 let persist_cap = persist_channel_capacity.max(1); let (persist_tx, persist_rx) = std::sync::mpsc::sync_channel(persist_cap);
1999
2000 let (last_written, initial_flushed_upto) = {
2001 let tx = backend.begin_read()?;
2002 let last_written = tx
2003 .get(MISC_VALUES, "last_written".as_bytes())?
2004 .unwrap_or_else(|| vec![0u8; 64]);
2005 let last_written = if last_written == [0xff] {
2006 vec![0xff; 64]
2007 } else {
2008 last_written
2009 };
2010 let initial_flushed_upto = match tx.get(MISC_VALUES, FLUSHED_UPTO_KEY)? {
2011 Some(bytes) => decode_flushed_upto(&bytes)?,
2012 None => 0,
2013 };
2014 (last_written, initial_flushed_upto)
2015 };
2016 let mut initial_buffer = BlockDataBuffer::new();
2017 initial_buffer.set_flushed_upto(initial_flushed_upto);
2018
2019 let mut background_threads = Vec::new();
2020 let safe_commit_root = Arc::new(RwLock::new(H256::zero()));
2021 let mut store = Self {
2022 db_path,
2023 backend,
2024 chain_config: Default::default(),
2025 latest_block_header: Default::default(),
2026 trie_cache: Arc::new(RwLock::new(Arc::new(TrieLayerCache::new_with_safe_commit(
2027 commit_threshold,
2028 safe_commit_root.clone(),
2029 )))),
2030 flatkeyvalue_control_tx: fkv_tx,
2031 block_data_buffer: Arc::new(RwLock::new(Arc::new(initial_buffer))),
2032 persist_tx,
2033 pending_trie_roots: Arc::new(PendingTrieRoots::default()),
2034 last_computed_flatkeyvalue: Arc::new(RwLock::new(last_written)),
2035 account_code_cache: Arc::new(Mutex::new(CodeCache::default())),
2036 code_metadata_cache: Arc::new(Mutex::new(rustc_hash::FxHashMap::default())),
2037 fcu_lock: Arc::new(tokio::sync::Mutex::new(())),
2038 safe_commit_root,
2039 journal_pruning_paused: Arc::new(std::sync::atomic::AtomicBool::new(false)),
2040 background_threads: Default::default(),
2041 };
2042 let backend_clone = store.backend.clone();
2043 let last_computed_fkv = store.last_computed_flatkeyvalue.clone();
2044 background_threads.push(std::thread::spawn(move || {
2045 let rx = fkv_rx;
2046 loop {
2048 match rx.recv() {
2049 Ok(FKVGeneratorControlMessage::Continue) => break,
2050 Ok(FKVGeneratorControlMessage::Stop) => {}
2051 Err(std::sync::mpsc::RecvError) => {
2052 debug!("Closing FlatKeyValue generator.");
2053 return;
2054 }
2055 }
2056 }
2057
2058 let _ = flatkeyvalue_generator(&backend_clone, &last_computed_fkv, &rx)
2059 .inspect_err(|err| error!("Error while generating FlatKeyValue: {err}"));
2060 }));
2061 let persist_backend = store.backend.clone();
2064 let persist_buffer = store.block_data_buffer.clone();
2065 let persist_trie_cache = store.trie_cache.clone();
2066 let persist_pending_roots = store.pending_trie_roots.clone();
2067 let persist_fkv_ctl = store.flatkeyvalue_control_tx.clone();
2068 background_threads.push(std::thread::spawn(move || {
2069 let rx = persist_rx;
2070 let mut last_flush_result: Result<(), StoreError> = Ok(());
2073 loop {
2074 match rx.recv() {
2075 Ok(PersistMessage::Block(bp)) => {
2076 let bp = *bp;
2077 let staged = mutate_block_buffer(&persist_buffer, move |b| {
2080 let mut codes = Some(bp.codes);
2081 for (block, receipts) in bp.blocks {
2082 b.insert(block, receipts, codes.take().unwrap_or_default());
2083 }
2084 });
2085 if let Err(e) = staged {
2086 persist_pending_roots.clear(bp.child_state_root);
2091 let _ = bp.ack.send(Err(e));
2092 continue;
2093 }
2094 if !bp.wait_for_flush {
2103 let _ = bp
2104 .ack
2105 .send(std::mem::replace(&mut last_flush_result, Ok(())));
2106 }
2107 if let Err(err) = apply_trie_phase1(
2109 &persist_trie_cache,
2110 &persist_pending_roots,
2111 bp.parent_state_root,
2112 bp.child_state_root,
2113 bp.block_number,
2114 bp.block_hash,
2115 bp.account_updates,
2116 bp.storage_updates,
2117 ) {
2118 error!("persist worker trie phase-1 failed: {err}");
2119 if bp.wait_for_flush {
2120 let _ = bp.ack.send(Err(err));
2121 } else {
2122 last_flush_result = Err(err);
2123 }
2124 continue;
2125 }
2126 let flushed = flush_block_data(persist_backend.as_ref(), &persist_buffer)
2128 .inspect_err(|err| error!("flush_block_data failed: {err}"))
2129 .and_then(|_| {
2130 commit_trie_if_due(
2131 persist_backend.as_ref(),
2132 &persist_trie_cache,
2133 &persist_fkv_ctl,
2134 bp.parent_state_root,
2135 bp.commit_depth,
2136 bp.wait_for_flush,
2137 )
2138 });
2139 if bp.wait_for_flush {
2142 let prior = std::mem::replace(&mut last_flush_result, Ok(()));
2143 let _ = bp.ack.send(prior.and(flushed));
2144 } else {
2145 last_flush_result = flushed;
2146 }
2147 }
2148 Ok(PersistMessage::Commit(root)) => match persist_trie_cache.read() {
2149 Ok(guard) => {
2150 let trie = guard.clone();
2151 drop(guard);
2152 let _ = commit_to_disk(
2155 persist_backend.as_ref(),
2156 &persist_fkv_ctl,
2157 &persist_trie_cache,
2158 &trie,
2159 root,
2160 false,
2161 )
2162 .inspect_err(|err| error!("commit_to_disk failed: {err}"));
2163 }
2164 Err(_) => error!("trie cache lock poisoned during commit"),
2165 },
2166 Ok(PersistMessage::Ping(ack)) => {
2167 let _ = ack.send(std::mem::replace(&mut last_flush_result, Ok(())));
2171 }
2172 Ok(PersistMessage::Shutdown { ack }) => {
2173 let result = flush_block_data(persist_backend.as_ref(), &persist_buffer);
2180 let prior = std::mem::replace(&mut last_flush_result, Ok(()));
2181 let _ = ack.send(prior.and(result));
2182 return;
2184 }
2185 Err(_) => return,
2186 }
2187 }
2188 }));
2189 store.background_threads = Arc::new(ThreadList {
2190 list: background_threads,
2191 });
2192 Ok(store)
2193 }
2194
2195 pub async fn new_from_genesis(
2198 store_path: &Path,
2199 engine_type: EngineType,
2200 genesis_path: &str,
2201 ) -> Result<Self, StoreError> {
2202 Self::new_from_genesis_with_config(
2203 store_path,
2204 engine_type,
2205 genesis_path,
2206 StoreConfig::default(),
2207 )
2208 .await
2209 }
2210
2211 pub async fn new_from_genesis_with_config(
2214 store_path: &Path,
2215 engine_type: EngineType,
2216 genesis_path: &str,
2217 config: StoreConfig,
2218 ) -> Result<Self, StoreError> {
2219 let file = std::fs::File::open(genesis_path)
2220 .map_err(|error| StoreError::Custom(format!("Failed to open genesis file: {error}")))?;
2221 let reader = std::io::BufReader::new(file);
2222 let genesis: Genesis = serde_json::from_reader(reader)
2223 .map_err(|e| StoreError::Custom(format!("Failed to deserialize genesis file: {e}")))?;
2224 let mut store = Self::new_with_config(store_path, engine_type, config)?;
2225 store.add_initial_state(genesis).await?;
2226 Ok(store)
2227 }
2228
2229 pub async fn get_account_info(
2230 &self,
2231 block_number: BlockNumber,
2232 address: Address,
2233 ) -> Result<Option<AccountInfo>, StoreError> {
2234 match self.get_canonical_block_hash(block_number).await? {
2235 Some(block_hash) => self.get_account_info_by_hash(block_hash, address),
2236 None => Ok(None),
2237 }
2238 }
2239
2240 pub fn get_account_info_by_hash(
2241 &self,
2242 block_hash: BlockHash,
2243 address: Address,
2244 ) -> Result<Option<AccountInfo>, StoreError> {
2245 let Some(state_trie) = self.state_trie(block_hash)? else {
2246 return Ok(None);
2247 };
2248 let hashed_address = hash_address_fixed(&address);
2249
2250 let Some(encoded_state) = state_trie.get(hashed_address.as_bytes())? else {
2251 return Ok(None);
2252 };
2253
2254 let account_state = AccountState::decode(&encoded_state)?;
2255 Ok(Some(AccountInfo {
2256 code_hash: account_state.code_hash,
2257 balance: account_state.balance,
2258 nonce: account_state.nonce,
2259 }))
2260 }
2261
2262 pub fn get_account_state_by_acc_hash(
2263 &self,
2264 block_hash: BlockHash,
2265 account_hash: H256,
2266 ) -> Result<Option<AccountState>, StoreError> {
2267 let Some(state_trie) = self.state_trie(block_hash)? else {
2268 return Ok(None);
2269 };
2270 let Some(encoded_state) = state_trie.get(account_hash.as_bytes())? else {
2271 return Ok(None);
2272 };
2273 let account_state = AccountState::decode(&encoded_state)?;
2274 Ok(Some(account_state))
2275 }
2276
2277 pub async fn get_fork_id(&self) -> Result<ForkId, StoreError> {
2278 let chain_config = self.get_chain_config();
2279 let genesis_header = self
2280 .load_block_header(0)?
2281 .ok_or(StoreError::MissingEarliestBlockNumber)?;
2282 let block_header = self.latest_block_header.get();
2283
2284 Ok(ForkId::new(
2285 chain_config,
2286 genesis_header,
2287 block_header.timestamp,
2288 block_header.number,
2289 ))
2290 }
2291
2292 pub async fn get_code_by_account_address(
2293 &self,
2294 block_number: BlockNumber,
2295 address: Address,
2296 ) -> Result<Option<Code>, StoreError> {
2297 let Some(block_hash) = self.get_canonical_block_hash(block_number).await? else {
2298 return Ok(None);
2299 };
2300 let Some(state_trie) = self.state_trie(block_hash)? else {
2301 return Ok(None);
2302 };
2303 let hashed_address = hash_address_fixed(&address);
2304 let Some(encoded_state) = state_trie.get(hashed_address.as_bytes())? else {
2305 return Ok(None);
2306 };
2307 let account_state = AccountState::decode(&encoded_state)?;
2308 self.get_account_code(account_state.code_hash)
2309 }
2310
2311 pub async fn get_nonce_by_account_address(
2312 &self,
2313 block_number: BlockNumber,
2314 address: Address,
2315 ) -> Result<Option<u64>, StoreError> {
2316 let Some(block_hash) = self.get_canonical_block_hash(block_number).await? else {
2317 return Ok(None);
2318 };
2319 let Some(state_trie) = self.state_trie(block_hash)? else {
2320 return Ok(None);
2321 };
2322 let hashed_address = hash_address_fixed(&address);
2323 let Some(encoded_state) = state_trie.get(hashed_address.as_bytes())? else {
2324 return Ok(None);
2325 };
2326 let account_state = AccountState::decode(&encoded_state)?;
2327 Ok(Some(account_state.nonce))
2328 }
2329
2330 pub fn apply_account_updates_batch(
2333 &self,
2334 block_hash: BlockHash,
2335 account_updates: &[AccountUpdate],
2336 ) -> Result<Option<AccountUpdatesList>, StoreError> {
2337 let Some(mut state_trie) = self.state_trie(block_hash)? else {
2338 return Ok(None);
2339 };
2340
2341 Ok(Some(self.apply_account_updates_from_trie_batch(
2342 &mut state_trie,
2343 account_updates,
2344 )?))
2345 }
2346
2347 pub fn apply_account_updates_from_trie_batch<'a>(
2348 &self,
2349 state_trie: &mut Trie,
2350 account_updates: impl IntoIterator<Item = &'a AccountUpdate>,
2351 ) -> Result<AccountUpdatesList, StoreError> {
2352 let mut ret_storage_updates = Vec::new();
2353 let mut code_updates = Vec::new();
2354 let state_root = state_trie.hash_no_commit(&NativeCrypto);
2355 for update in account_updates {
2356 let hashed_address = hash_address_fixed(&update.address);
2357 if update.removed {
2358 state_trie.remove(hashed_address.as_bytes())?;
2360 continue;
2361 }
2362 let mut account_state = match state_trie.get(hashed_address.as_bytes())? {
2365 Some(encoded_state) => AccountState::decode(&encoded_state)?,
2366 None => AccountState::default(),
2367 };
2368 if update.removed_storage {
2369 account_state.storage_root = EMPTY_TRIE_HASH;
2370 }
2371 if let Some(info) = &update.info {
2372 account_state.nonce = info.nonce;
2373 account_state.balance = info.balance;
2374 account_state.code_hash = info.code_hash;
2375 if let Some(code) = &update.code {
2377 code_updates.push((info.code_hash, code.clone()));
2378 }
2379 }
2380 if !update.added_storage.is_empty() {
2382 let mut storage_trie =
2383 self.open_storage_trie(hashed_address, state_root, account_state.storage_root)?;
2384 for (storage_key, storage_value) in &update.added_storage {
2385 let hashed_key = hash_key(storage_key);
2386 if storage_value.is_zero() {
2387 storage_trie.remove(&hashed_key)?;
2388 } else {
2389 storage_trie.insert(hashed_key, storage_value.encode_to_vec())?;
2390 }
2391 }
2392 let (storage_hash, storage_updates) =
2393 storage_trie.collect_changes_since_last_hash(&NativeCrypto);
2394 account_state.storage_root = storage_hash;
2395 ret_storage_updates.push((hashed_address, storage_updates));
2396 }
2397 state_trie.insert(
2398 hashed_address.as_bytes().to_vec(),
2399 account_state.encode_to_vec(),
2400 )?;
2401 }
2402 let (state_trie_hash, state_updates) =
2403 state_trie.collect_changes_since_last_hash(&NativeCrypto);
2404
2405 Ok(AccountUpdatesList {
2406 state_trie_hash,
2407 state_updates,
2408 storage_updates: ret_storage_updates,
2409 code_updates,
2410 })
2411 }
2412
2413 pub fn apply_account_updates_from_trie_with_witness(
2416 &self,
2417 mut state_trie: Trie,
2418 account_updates: &[AccountUpdate],
2419 mut storage_tries: StorageTries,
2420 ) -> Result<(StorageTries, AccountUpdatesList), StoreError> {
2421 let mut ret_storage_updates = Vec::new();
2422
2423 let mut code_updates = Vec::new();
2424
2425 let state_root = state_trie.hash_no_commit(&NativeCrypto);
2426
2427 for update in account_updates.iter() {
2428 let hashed_address = hash_address(&update.address);
2429
2430 if update.removed {
2431 state_trie.remove(&hashed_address)?;
2433
2434 continue;
2435 }
2436
2437 let mut account_state = match state_trie.get(&hashed_address)? {
2440 Some(encoded_state) => AccountState::decode(&encoded_state)?,
2441 None => AccountState::default(),
2442 };
2443
2444 if update.removed_storage {
2445 account_state.storage_root = EMPTY_TRIE_HASH;
2446 }
2447
2448 if let Some(info) = &update.info {
2449 account_state.nonce = info.nonce;
2450
2451 account_state.balance = info.balance;
2452
2453 account_state.code_hash = info.code_hash;
2454
2455 if let Some(code) = &update.code {
2457 code_updates.push((info.code_hash, code.clone()));
2458 }
2459 }
2460
2461 if !update.added_storage.is_empty() {
2463 let (_witness, storage_trie) = match storage_tries.entry(update.address) {
2464 Entry::Occupied(value) => value.into_mut(),
2465 Entry::Vacant(vacant) => {
2466 let trie = self.open_storage_trie(
2467 H256::from_slice(&hashed_address),
2468 state_root,
2469 account_state.storage_root,
2470 )?;
2471 vacant.insert(TrieLogger::open_trie(trie))
2472 }
2473 };
2474
2475 for (storage_key, storage_value) in &update.added_storage {
2476 let hashed_key = hash_key(storage_key);
2477
2478 if storage_value.is_zero() {
2479 storage_trie.remove(&hashed_key)?;
2480 } else {
2481 storage_trie.insert(hashed_key, storage_value.encode_to_vec())?;
2482 }
2483 }
2484
2485 let (storage_hash, storage_updates) =
2486 storage_trie.collect_changes_since_last_hash(&NativeCrypto);
2487
2488 account_state.storage_root = storage_hash;
2489
2490 ret_storage_updates.push((H256::from_slice(&hashed_address), storage_updates));
2491 }
2492
2493 state_trie.insert(hashed_address, account_state.encode_to_vec())?;
2494 }
2495
2496 let (state_trie_hash, state_updates) =
2497 state_trie.collect_changes_since_last_hash(&NativeCrypto);
2498
2499 let account_updates_list = AccountUpdatesList {
2500 state_trie_hash,
2501 state_updates,
2502 storage_updates: ret_storage_updates,
2503 code_updates,
2504 };
2505
2506 Ok((storage_tries, account_updates_list))
2507 }
2508
2509 pub async fn setup_genesis_state_trie(
2511 &self,
2512 genesis_accounts: BTreeMap<Address, GenesisAccount>,
2513 ) -> Result<H256, StoreError> {
2514 let mut storage_trie_nodes = vec![];
2515 let mut genesis_state_trie = self.open_direct_state_trie(EMPTY_TRIE_HASH)?;
2516 for (address, account) in genesis_accounts {
2517 let hashed_address = hash_address(&address);
2518 let h256_hashed_address = H256::from_slice(&hashed_address);
2519
2520 let code = Code::from_bytecode(account.code, &NativeCrypto);
2522 let code_hash = code.hash;
2523 self.add_account_code(code).await?;
2524
2525 let mut storage_trie =
2527 self.open_direct_storage_trie(h256_hashed_address, EMPTY_TRIE_HASH)?;
2528 for (storage_key, storage_value) in account.storage {
2529 if !storage_value.is_zero() {
2530 let hashed_key = hash_key(&H256(storage_key.to_big_endian()));
2531 storage_trie.insert(hashed_key, storage_value.encode_to_vec())?;
2532 }
2533 }
2534
2535 let (storage_root, storage_nodes) =
2536 storage_trie.collect_changes_since_last_hash(&NativeCrypto);
2537
2538 storage_trie_nodes.extend(
2539 storage_nodes
2540 .into_iter()
2541 .map(|(path, n)| (apply_prefix(Some(h256_hashed_address), path).into_vec(), n)),
2542 );
2543
2544 let account_state = AccountState {
2546 nonce: account.nonce,
2547 balance: account.balance,
2548 storage_root,
2549 code_hash,
2550 };
2551 genesis_state_trie.insert(hashed_address, account_state.encode_to_vec())?;
2552 }
2553
2554 let (state_root, account_trie_nodes) =
2555 genesis_state_trie.collect_changes_since_last_hash(&NativeCrypto);
2556 let account_trie_nodes = account_trie_nodes
2557 .into_iter()
2558 .map(|(path, n)| (apply_prefix(None, path).into_vec(), n))
2559 .collect::<Vec<_>>();
2560
2561 let mut tx = self.backend.begin_write()?;
2562 tx.put_batch(ACCOUNT_TRIE_NODES, account_trie_nodes)?;
2563 tx.put_batch(STORAGE_TRIE_NODES, storage_trie_nodes)?;
2564 tx.commit()?;
2565
2566 Ok(state_root)
2567 }
2568
2569 fn make_witness_key(block_number: u64, block_hash: &BlockHash) -> Vec<u8> {
2571 let mut composite_key = Vec::with_capacity(8 + 32);
2572 composite_key.extend_from_slice(&block_number.to_be_bytes());
2573 composite_key.extend_from_slice(block_hash.as_bytes());
2574 composite_key
2575 }
2576
2577 pub fn store_witness(
2583 &self,
2584 block_hash: BlockHash,
2585 block_number: u64,
2586 witness: ExecutionWitness,
2587 ) -> Result<(), StoreError> {
2588 let rpc_witness = RpcExecutionWitness::try_from(witness)?;
2590 let key = Self::make_witness_key(block_number, &block_hash);
2591 let value = serde_json::to_vec(&rpc_witness)?;
2592 self.write(EXECUTION_WITNESSES, key, value)?;
2593 self.cleanup_old_witnesses(block_number)
2595 }
2596
2597 fn cleanup_old_witnesses(&self, latest_block_number: u64) -> Result<(), StoreError> {
2598 if latest_block_number <= MAX_WITNESSES {
2600 return Ok(());
2601 }
2602
2603 let threshold = latest_block_number - MAX_WITNESSES;
2604
2605 if let Some(oldest_block_number) = self.get_oldest_witness_number()? {
2606 let prefix = oldest_block_number.to_be_bytes();
2607 let mut to_delete = Vec::new();
2608
2609 {
2610 let read_txn = self.backend.begin_read()?;
2611 let iter = read_txn.prefix_iterator(EXECUTION_WITNESSES, &prefix)?;
2612
2613 for item in iter {
2615 let (key, _value) = item?;
2616 let mut block_number_bytes = [0u8; 8];
2617 block_number_bytes.copy_from_slice(&key[0..8]);
2618 let block_number = u64::from_be_bytes(block_number_bytes);
2619 if block_number > threshold {
2620 break;
2621 }
2622 to_delete.push(key.to_vec());
2623 }
2624 }
2625
2626 for key in to_delete {
2627 self.delete(EXECUTION_WITNESSES, key)?;
2628 }
2629 };
2630
2631 self.update_oldest_witness_number(threshold + 1)?;
2632
2633 Ok(())
2634 }
2635
2636 fn update_oldest_witness_number(&self, oldest_block_number: u64) -> Result<(), StoreError> {
2637 self.write(
2638 MISC_VALUES,
2639 b"oldest_witness_block_number".to_vec(),
2640 oldest_block_number.to_le_bytes().to_vec(),
2641 )?;
2642 Ok(())
2643 }
2644
2645 fn get_oldest_witness_number(&self) -> Result<Option<u64>, StoreError> {
2646 let Some(value) = self.read(MISC_VALUES, b"oldest_witness_block_number".to_vec())? else {
2647 return Ok(None);
2648 };
2649
2650 let array: [u8; 8] = value.as_slice().try_into().map_err(|_| {
2651 StoreError::Custom("Invalid oldest witness block number bytes".to_string())
2652 })?;
2653 Ok(Some(u64::from_le_bytes(array)))
2654 }
2655
2656 pub fn get_witness_json_bytes(
2662 &self,
2663 block_number: u64,
2664 block_hash: BlockHash,
2665 ) -> Result<Option<Vec<u8>>, StoreError> {
2666 let key = Self::make_witness_key(block_number, &block_hash);
2667 self.read(EXECUTION_WITNESSES, key)
2668 }
2669
2670 pub fn get_witness_by_number_and_hash(
2675 &self,
2676 block_number: u64,
2677 block_hash: BlockHash,
2678 ) -> Result<Option<RpcExecutionWitness>, StoreError> {
2679 let key = Self::make_witness_key(block_number, &block_hash);
2680 match self.read(EXECUTION_WITNESSES, key)? {
2681 Some(value) => {
2682 let witness: RpcExecutionWitness = serde_json::from_slice(&value)?;
2683 Ok(Some(witness))
2684 }
2685 None => Ok(None),
2686 }
2687 }
2688
2689 pub fn store_block_access_list(
2691 &self,
2692 block_hash: BlockHash,
2693 bal: &BlockAccessList,
2694 ) -> Result<(), StoreError> {
2695 let key = block_hash.as_bytes().to_vec();
2696 let mut value = vec![];
2697 bal.encode(&mut value);
2698 self.write(BLOCK_ACCESS_LISTS, key, value)
2699 }
2700
2701 pub fn get_block_access_list(
2703 &self,
2704 block_hash: BlockHash,
2705 ) -> Result<Option<BlockAccessList>, StoreError> {
2706 let key = block_hash.as_bytes().to_vec();
2707 match self.read(BLOCK_ACCESS_LISTS, key)? {
2708 Some(value) => {
2709 let bal = BlockAccessList::decode(&value)
2710 .map_err(|e| StoreError::Custom(format!("Failed to decode BAL: {e}")))?;
2711 Ok(Some(bal))
2712 }
2713 None => Ok(None),
2714 }
2715 }
2716
2717 pub async fn add_initial_state(&mut self, genesis: Genesis) -> Result<(), StoreError> {
2718 self.add_initial_state_inner(genesis, false).await
2719 }
2720
2721 pub async fn add_initial_state_skip_validation(
2732 &mut self,
2733 genesis: Genesis,
2734 ) -> Result<(), StoreError> {
2735 self.add_initial_state_inner(genesis, true).await
2736 }
2737
2738 async fn add_initial_state_inner(
2739 &mut self,
2740 genesis: Genesis,
2741 skip_genesis_validation: bool,
2742 ) -> Result<(), StoreError> {
2743 debug!("Storing initial state from genesis");
2744
2745 let genesis_block = genesis.get_block();
2747 let genesis_block_number = genesis_block.header.number;
2748
2749 let genesis_hash = genesis_block.hash();
2750
2751 let stored_genesis_header = self.load_block_header(genesis_block_number)?;
2752
2753 self.set_chain_config(&genesis.config).await?;
2760
2761 if let Some(latest) = self.load_latest_block_number().await? {
2766 self.anchor_to_durable_head(latest).await?;
2767 }
2768
2769 match stored_genesis_header {
2770 Some(header) if skip_genesis_validation => {
2771 info!(
2772 stored_genesis = %header.hash(),
2773 "Skipping genesis state validation; trusting the genesis header and state already stored in the datadir"
2774 );
2775 return Ok(());
2776 }
2777 Some(header) if header.hash() == genesis_hash => {
2778 info!("Received genesis file matching a previously stored one, nothing to do");
2779 return Ok(());
2780 }
2781 Some(_) => {
2782 error!(
2783 "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."
2784 );
2785 return Err(StoreError::IncompatibleChainConfig);
2786 }
2787 None => {
2788 self.add_block_header(genesis_hash, genesis_block.header.clone())
2789 .await?
2790 }
2791 }
2792 let genesis_state_root = self.setup_genesis_state_trie(genesis.alloc).await?;
2795 debug_assert_eq!(genesis_state_root, genesis_block.header.state_root);
2796
2797 info!(hash = %genesis_hash, "Storing genesis block");
2799
2800 self.add_block(genesis_block).await?;
2801 self.update_earliest_block_number(genesis_block_number)
2802 .await?;
2803 self.forkchoice_update(vec![], genesis_block_number, genesis_hash, None, None)
2804 .await?;
2805 Ok(())
2806 }
2807
2808 pub async fn load_initial_state(&self) -> Result<(), StoreError> {
2809 info!("Loading initial state from DB");
2810 let Some(latest) = self.load_latest_block_number().await? else {
2811 return Err(StoreError::MissingLatestBlockNumber);
2812 };
2813 self.anchor_to_durable_head(latest).await?;
2817 Ok(())
2818 }
2819
2820 pub fn get_storage_at(
2821 &self,
2822 block_number: BlockNumber,
2823 address: Address,
2824 storage_key: H256,
2825 ) -> Result<Option<U256>, StoreError> {
2826 match self.get_block_header(block_number)? {
2827 Some(header) => self.get_storage_at_root(header.state_root, address, storage_key),
2828 None => Ok(None),
2829 }
2830 }
2831
2832 pub fn get_storage_at_root(
2833 &self,
2834 state_root: H256,
2835 address: Address,
2836 storage_key: H256,
2837 ) -> Result<Option<U256>, StoreError> {
2838 let account_hash = hash_address_fixed(&address);
2839
2840 let read_view = self.backend.begin_read()?;
2842 let cache = self.gated_snapshot(state_root)?;
2843 let last_written = self.last_written()?;
2844 let use_fkv = Self::flatkeyvalue_computed_with_last_written(account_hash, &last_written)
2851 && !cache.overlay_serves(state_root);
2852
2853 let storage_root = if use_fkv {
2854 EMPTY_TRIE_HASH
2856 } else {
2857 let state_trie = self.open_state_trie_shared(
2858 state_root,
2859 read_view.clone(),
2860 cache.clone(),
2861 last_written.clone(),
2862 )?;
2863 let Some(encoded_account) = state_trie.get(account_hash.as_bytes())? else {
2864 return Ok(None);
2865 };
2866 let account = AccountState::decode(&encoded_account)?;
2867 account.storage_root
2868 };
2869 let storage_trie = self.open_storage_trie_shared(
2870 account_hash,
2871 state_root,
2872 storage_root,
2873 read_view,
2874 cache,
2875 last_written,
2876 )?;
2877
2878 let hashed_key = hash_key_fixed(&storage_key);
2879 storage_trie
2880 .get(&hashed_key)?
2881 .map(|rlp| U256::decode(&rlp).map_err(StoreError::RLPDecode))
2882 .transpose()
2883 }
2884
2885 pub fn get_storage_at_root_with_known_storage_root(
2890 &self,
2891 state_root: H256,
2892 account_hash: H256,
2893 storage_root: H256,
2894 storage_key: H256,
2895 ) -> Result<Option<U256>, StoreError> {
2896 let read_view = self.backend.begin_read()?;
2897 let cache = self.gated_snapshot(state_root)?;
2898 let last_written = self.last_written()?;
2899 let storage_root =
2906 if Self::flatkeyvalue_computed_with_last_written(account_hash, &last_written)
2907 && !cache.overlay_serves(state_root)
2908 {
2909 EMPTY_TRIE_HASH
2910 } else {
2911 storage_root
2912 };
2913 let storage_trie = self.open_storage_trie_shared(
2914 account_hash,
2915 state_root,
2916 storage_root,
2917 read_view,
2918 cache,
2919 last_written,
2920 )?;
2921
2922 let hashed_key = hash_key_fixed(&storage_key);
2923 storage_trie
2924 .get(&hashed_key)?
2925 .map(|rlp| U256::decode(&rlp).map_err(StoreError::RLPDecode))
2926 .transpose()
2927 }
2928
2929 pub fn get_chain_config(&self) -> ChainConfig {
2930 self.chain_config
2931 }
2932
2933 pub async fn get_latest_canonical_block_hash(&self) -> Result<Option<BlockHash>, StoreError> {
2934 Ok(Some(self.latest_block_header.get().hash()))
2935 }
2936
2937 pub async fn forkchoice_update(
2942 &self,
2943 new_canonical_blocks: Vec<(BlockNumber, BlockHash)>,
2944 head_number: BlockNumber,
2945 head_hash: BlockHash,
2946 safe: Option<BlockNumber>,
2947 finalized: Option<BlockNumber>,
2948 ) -> Result<(), StoreError> {
2949 let _guard = self.fcu_lock.lock().await;
2955
2956 let previous_head = self.latest_block_header.get();
2961 let new_head = self
2962 .get_block_header_by_hash(head_hash)?
2963 .ok_or_else(|| StoreError::MissingLatestBlockNumber)?;
2964 self.latest_block_header.update(new_head);
2965 if let Err(err) = self
2966 .forkchoice_update_inner(
2967 new_canonical_blocks,
2968 head_number,
2969 head_hash,
2970 safe,
2971 finalized,
2972 )
2973 .await
2974 {
2975 self.latest_block_header.update((*previous_head).clone());
2976 return Err(err);
2977 }
2978
2979 if let Some(root) = self.compute_safe_commit_root(head_number)? {
2985 if self.set_safe_commit_root(root)? {
2990 let tx = self.persist_tx.clone();
2991 let _ = tokio::task::spawn_blocking(move || tx.send(PersistMessage::Commit(root)))
2992 .await;
2993 }
2994 }
2995
2996 Ok(())
2997 }
2998
2999 pub(crate) fn set_safe_commit_root(&self, root: H256) -> Result<bool, StoreError> {
3007 let mut guard = self
3008 .safe_commit_root
3009 .write()
3010 .map_err(|_| StoreError::LockError)?;
3011 let changed = *guard != root;
3012 *guard = root;
3013 Ok(changed)
3014 }
3015
3016 pub(crate) fn compute_safe_commit_root(
3025 &self,
3026 head: BlockNumber,
3027 ) -> Result<Option<H256>, StoreError> {
3028 let commit_threshold = self
3029 .trie_cache
3030 .read()
3031 .map_err(|_| StoreError::LockError)?
3032 .commit_threshold;
3033 let Some(target) = head.checked_sub(commit_threshold as u64) else {
3034 return Ok(None);
3035 };
3036 let Some(hash) = self.get_canonical_block_hash_sync(target)? else {
3037 return Ok(None);
3038 };
3039 let Some(header) = self.get_block_header_by_hash(hash)? else {
3040 return Ok(None);
3041 };
3042 Ok(Some(header.state_root))
3043 }
3044
3045 pub fn state_trie(&self, block_hash: BlockHash) -> Result<Option<Trie>, StoreError> {
3047 let Some(header) = self.get_block_header_by_hash(block_hash)? else {
3048 return Ok(None);
3049 };
3050 Ok(Some(self.open_state_trie(header.state_root)?))
3051 }
3052
3053 pub fn storage_trie(
3055 &self,
3056 block_hash: BlockHash,
3057 address: Address,
3058 ) -> Result<Option<Trie>, StoreError> {
3059 let Some(header) = self.get_block_header_by_hash(block_hash)? else {
3060 return Ok(None);
3061 };
3062 let Some(state_trie) = self.state_trie(block_hash)? else {
3064 return Ok(None);
3065 };
3066 let hashed_address = hash_address_fixed(&address);
3067 let Some(encoded_account) = state_trie.get(hashed_address.as_bytes())? else {
3068 return Ok(None);
3069 };
3070 let account = AccountState::decode(&encoded_account)?;
3071 let storage_root = account.storage_root;
3073 Ok(Some(self.open_storage_trie(
3074 hashed_address,
3075 header.state_root,
3076 storage_root,
3077 )?))
3078 }
3079
3080 pub async fn get_account_state(
3081 &self,
3082 block_number: BlockNumber,
3083 address: Address,
3084 ) -> Result<Option<AccountState>, StoreError> {
3085 let Some(block_hash) = self.get_canonical_block_hash(block_number).await? else {
3086 return Ok(None);
3087 };
3088 let Some(state_trie) = self.state_trie(block_hash)? else {
3089 return Ok(None);
3090 };
3091 self.get_account_state_from_trie(&state_trie, address)
3092 }
3093
3094 pub fn get_account_state_by_root(
3095 &self,
3096 state_root: H256,
3097 address: Address,
3098 ) -> Result<Option<AccountState>, StoreError> {
3099 let state_trie = self.open_state_trie(state_root)?;
3100 self.get_account_state_from_trie(&state_trie, address)
3101 }
3102
3103 pub fn get_account_state_from_trie(
3104 &self,
3105 state_trie: &Trie,
3106 address: Address,
3107 ) -> Result<Option<AccountState>, StoreError> {
3108 let hashed_address = hash_address_fixed(&address);
3109 let Some(encoded_state) = state_trie.get(hashed_address.as_bytes())? else {
3110 return Ok(None);
3111 };
3112 Ok(Some(AccountState::decode(&encoded_state)?))
3113 }
3114
3115 pub fn get_account_states_batch_by_root(
3124 &self,
3125 state_root: H256,
3126 addresses: &[Address],
3127 ) -> Result<Vec<Option<AccountState>>, StoreError> {
3128 if addresses.is_empty() {
3129 return Ok(Vec::new());
3130 }
3131
3132 let last_written = self.last_written()?;
3133 let trie_cache = self
3134 .trie_cache
3135 .read()
3136 .map_err(|_| StoreError::LockError)?
3137 .clone();
3138
3139 let mut results: Vec<Option<AccountState>> = vec![None; addresses.len()];
3140 let leaf_paths: Vec<Vec<u8>> = addresses
3142 .iter()
3143 .map(|addr| {
3144 let hashed = hash_address_fixed(addr);
3145 Nibbles::from_bytes(hashed.as_bytes()).into_vec()
3146 })
3147 .collect();
3148
3149 let mut fkv_indices: Vec<usize> = Vec::new();
3150 let mut trie_indices: Vec<usize> = Vec::new();
3151
3152 let overlay_active = trie_cache.overlay_serves(state_root);
3168 let fkv_cursor: &[u8] = last_written.as_slice();
3169 for (i, path) in leaf_paths.iter().enumerate() {
3170 if let Some(value) = trie_cache.get(state_root, path.as_slice()) {
3171 if !value.is_empty() {
3172 results[i] = Some(AccountState::decode(&value)?);
3173 }
3174 continue;
3175 }
3176 if !overlay_active && fkv_cursor >= path.as_slice() {
3177 fkv_indices.push(i);
3178 } else {
3179 trie_indices.push(i);
3180 }
3181 }
3182
3183 if !fkv_indices.is_empty() {
3184 fkv_indices.sort_unstable_by(|&a, &b| leaf_paths[a].cmp(&leaf_paths[b]));
3191 let read_view = self.backend.begin_read()?;
3192 let keys: Vec<&[u8]> = fkv_indices
3193 .iter()
3194 .map(|&i| leaf_paths[i].as_slice())
3195 .collect();
3196 let shards = shard_count(keys.len());
3197 let raw: Vec<Result<Option<Vec<u8>>, StoreError>> = if shards <= 1 {
3198 read_view.multi_get(ACCOUNT_FLATKEYVALUE, &keys)
3199 } else {
3200 let chunk = keys.len().div_ceil(shards);
3201 let rv = read_view.as_ref();
3202 std::thread::scope(|scope| {
3203 let handles: Vec<_> = keys
3204 .chunks(chunk)
3205 .map(|ck| scope.spawn(move || rv.multi_get(ACCOUNT_FLATKEYVALUE, ck)))
3206 .collect();
3207 handles
3208 .into_iter()
3209 .flat_map(|h| {
3215 h.join().unwrap_or_else(|_| {
3216 vec![Err(StoreError::Custom(
3217 "account prefetch shard panicked".into(),
3218 ))]
3219 })
3220 })
3221 .collect()
3222 })
3223 };
3224 for (slot, res) in fkv_indices.iter().zip(raw.into_iter()) {
3225 let Some(encoded) = res? else { continue };
3226 if encoded.is_empty() {
3227 continue;
3228 }
3229 results[*slot] = Some(AccountState::decode(&encoded)?);
3230 }
3231 }
3232
3233 if !trie_indices.is_empty() {
3234 let state_trie = self.open_state_trie(state_root)?;
3240 let fetched: Result<Vec<(usize, Option<AccountState>)>, StoreError> = trie_indices
3241 .par_iter()
3242 .map(|&i| {
3243 self.get_account_state_from_trie(&state_trie, addresses[i])
3244 .map(|s| (i, s))
3245 })
3246 .collect();
3247 for (i, s) in fetched? {
3248 results[i] = s;
3249 }
3250 }
3251
3252 Ok(results)
3253 }
3254
3255 pub fn get_storage_values_batch_by_root(
3275 &self,
3276 state_root: H256,
3277 slots: &[(H256, H256, H256)],
3278 ) -> Result<Vec<Option<U256>>, StoreError> {
3279 if slots.is_empty() {
3280 return Ok(Vec::new());
3281 }
3282
3283 let last_written = self.last_written()?;
3284 let trie_cache = self
3285 .trie_cache
3286 .read()
3287 .map_err(|_| StoreError::LockError)?
3288 .clone();
3289
3290 let mut results: Vec<Option<U256>> = vec![None; slots.len()];
3291 let leaf_paths: Vec<Vec<u8>> = slots
3297 .iter()
3298 .map(|(account_hash, _storage_root, slot)| {
3299 let hashed_slot = hash_key_fixed(slot);
3300 apply_prefix(Some(*account_hash), Nibbles::from_bytes(&hashed_slot)).into_vec()
3301 })
3302 .collect();
3303
3304 let mut fkv_indices: Vec<usize> = Vec::new();
3305 let mut trie_indices: Vec<usize> = Vec::new();
3306
3307 let overlay_active = trie_cache.overlay_serves(state_root);
3321 let fkv_cursor: &[u8] = last_written.as_slice();
3322 for (i, path) in leaf_paths.iter().enumerate() {
3323 if let Some(value) = trie_cache.get(state_root, path.as_slice()) {
3328 if !value.is_empty() {
3329 results[i] = Some(U256::decode(&value).map_err(StoreError::RLPDecode)?);
3330 }
3331 continue;
3332 }
3333 if !overlay_active && fkv_cursor >= path.as_slice() {
3334 fkv_indices.push(i);
3335 } else {
3336 trie_indices.push(i);
3337 }
3338 }
3339
3340 if !fkv_indices.is_empty() {
3341 fkv_indices.sort_unstable_by(|&a, &b| leaf_paths[a].cmp(&leaf_paths[b]));
3346 let read_view = self.backend.begin_read()?;
3347 let keys: Vec<&[u8]> = fkv_indices
3348 .iter()
3349 .map(|&i| leaf_paths[i].as_slice())
3350 .collect();
3351 let shards = shard_count(keys.len());
3364 let raw: Vec<Result<Option<Vec<u8>>, StoreError>> = if shards <= 1 {
3365 read_view.multi_get(STORAGE_FLATKEYVALUE, &keys)
3366 } else {
3367 let chunk = keys.len().div_ceil(shards);
3368 let rv = read_view.as_ref();
3369 std::thread::scope(|scope| {
3370 let handles: Vec<_> = keys
3371 .chunks(chunk)
3372 .map(|ck| scope.spawn(move || rv.multi_get(STORAGE_FLATKEYVALUE, ck)))
3373 .collect();
3374 handles
3375 .into_iter()
3376 .flat_map(|h| {
3382 h.join().unwrap_or_else(|_| {
3383 vec![Err(StoreError::Custom(
3384 "storage prefetch shard panicked".into(),
3385 ))]
3386 })
3387 })
3388 .collect()
3389 })
3390 };
3391 for (slot_idx, res) in fkv_indices.iter().zip(raw.into_iter()) {
3392 let Some(encoded) = res? else { continue };
3393 if encoded.is_empty() {
3394 continue;
3395 }
3396 results[*slot_idx] = Some(U256::decode(&encoded).map_err(StoreError::RLPDecode)?);
3397 }
3398 }
3399
3400 if !trie_indices.is_empty() {
3401 let fetched: Result<Vec<(usize, Option<U256>)>, StoreError> = trie_indices
3409 .par_iter()
3410 .map(|&i| {
3411 let (account_hash, storage_root, slot) = slots[i];
3412 self.get_storage_at_root_with_known_storage_root(
3413 state_root,
3414 account_hash,
3415 storage_root,
3416 slot,
3417 )
3418 .map(|v| (i, v))
3419 })
3420 .collect();
3421 for (i, v) in fetched? {
3422 results[i] = v;
3423 }
3424 }
3425
3426 Ok(results)
3427 }
3428
3429 pub fn prefetch_trie_nodes(
3456 &self,
3457 storage_slots: &[(Address, H256)],
3458 accounts: &[Address],
3459 ) -> Result<usize, StoreError> {
3460 const MAX_PREFETCH_DEPTH: usize = 8;
3465
3466 let mut storage_keys: Vec<Vec<u8>> =
3469 Vec::with_capacity(storage_slots.len() * (MAX_PREFETCH_DEPTH + 1));
3470 for (address, slot) in storage_slots {
3471 let hashed_address = hash_address_fixed(address);
3472 let hashed_slot = hash_key_fixed(slot);
3473 let slot_nibbles = Nibbles::from_bytes(&hashed_slot);
3474 let max_d = MAX_PREFETCH_DEPTH.min(slot_nibbles.len());
3475 for d in 0..=max_d {
3476 let prefix = apply_prefix(Some(hashed_address), slot_nibbles.slice(0, d));
3477 storage_keys.push(prefix.into_vec());
3478 }
3479 }
3480
3481 let mut account_keys: Vec<Vec<u8>> =
3483 Vec::with_capacity(accounts.len() * (MAX_PREFETCH_DEPTH + 1));
3484 for address in accounts {
3485 let hashed_address = hash_address_fixed(address);
3486 let addr_nibbles = Nibbles::from_bytes(hashed_address.as_bytes());
3487 let max_d = MAX_PREFETCH_DEPTH.min(addr_nibbles.len());
3488 for d in 0..=max_d {
3489 account_keys.push(addr_nibbles.slice(0, d).into_vec());
3490 }
3491 }
3492
3493 let hits = self.warm_trie_node_keys(STORAGE_TRIE_NODES, storage_keys)?
3494 + self.warm_trie_node_keys(ACCOUNT_TRIE_NODES, account_keys)?;
3495 Ok(hits)
3496 }
3497
3498 fn warm_trie_node_keys(
3505 &self,
3506 table: &'static str,
3507 mut keys: Vec<Vec<u8>>,
3508 ) -> Result<usize, StoreError> {
3509 if keys.is_empty() {
3510 return Ok(0);
3511 }
3512 keys.sort_unstable();
3513 keys.dedup();
3514
3515 let shards = shard_count(keys.len());
3516 let read_view = self.backend.begin_read()?;
3517 let count_hits = |res: Vec<Result<Option<Vec<u8>>, StoreError>>| {
3518 res.into_iter().filter(|r| matches!(r, Ok(Some(_)))).count()
3519 };
3520 if shards <= 1 {
3521 let refs: Vec<&[u8]> = keys.iter().map(|k| k.as_slice()).collect();
3522 return Ok(count_hits(read_view.multi_get(table, &refs)));
3523 }
3524 let chunk = keys.len().div_ceil(shards);
3525 let rv = read_view.as_ref();
3526 let hits = std::thread::scope(|scope| {
3527 let handles: Vec<_> = keys
3528 .chunks(chunk)
3529 .map(|ck| {
3530 scope.spawn(move || {
3531 let refs: Vec<&[u8]> = ck.iter().map(|k| k.as_slice()).collect();
3532 count_hits(rv.multi_get(table, &refs))
3534 })
3535 })
3536 .collect();
3537 handles
3538 .into_iter()
3539 .map(|h| h.join().unwrap_or(0))
3544 .sum::<usize>()
3545 });
3546 Ok(hits)
3547 }
3548
3549 pub async fn get_account_proof(
3554 &self,
3555 state_root: H256,
3556 address: Address,
3557 storage_keys: &[H256],
3558 ) -> Result<Option<AccountProof>, StoreError> {
3559 let state_trie = self.open_state_trie(state_root)?;
3564 let address_path = hash_address_fixed(&address);
3565 let proof = state_trie.get_proof(address_path.as_bytes())?;
3566 let account_opt = state_trie
3567 .get(address_path.as_bytes())?
3568 .map(|encoded_state| AccountState::decode(&encoded_state))
3569 .transpose()?;
3570
3571 let mut storage_proof = Vec::with_capacity(storage_keys.len());
3572
3573 if let Some(account) = &account_opt {
3574 let storage_trie =
3575 self.open_storage_trie(address_path, state_root, account.storage_root)?;
3576
3577 for key in storage_keys {
3578 let hashed_key = hash_key(key);
3579 let proof = storage_trie.get_proof(&hashed_key)?;
3580 let value = storage_trie
3581 .get(&hashed_key)?
3582 .map(|rlp| U256::decode(&rlp).map_err(StoreError::RLPDecode))
3583 .transpose()?
3584 .unwrap_or_default();
3585
3586 let slot_proof = StorageSlotProof {
3587 proof,
3588 key: *key,
3589 value,
3590 };
3591 storage_proof.push(slot_proof);
3592 }
3593 } else {
3594 storage_proof.extend(storage_keys.iter().map(|key| StorageSlotProof {
3595 proof: Vec::new(),
3596 key: *key,
3597 value: U256::zero(),
3598 }));
3599 }
3600 let account = account_opt.unwrap_or_default();
3601 let account_proof = AccountProof {
3602 proof,
3603 account,
3604 storage_proof,
3605 };
3606 Ok(Some(account_proof))
3607 }
3608
3609 pub fn iter_accounts_from(
3612 &self,
3613 state_root: H256,
3614 starting_address: H256,
3615 ) -> Result<impl Iterator<Item = (H256, AccountState)>, StoreError> {
3616 let mut iter = self.open_locked_state_trie(state_root)?.into_iter();
3617 iter.advance(starting_address.0.to_vec())?;
3618 Ok(iter.content().map_while(|(path, value)| {
3619 Some((H256::from_slice(&path), AccountState::decode(&value).ok()?))
3620 }))
3621 }
3622
3623 pub fn iter_accounts(
3626 &self,
3627 state_root: H256,
3628 ) -> Result<impl Iterator<Item = (H256, AccountState)>, StoreError> {
3629 self.iter_accounts_from(state_root, H256::zero())
3630 }
3631
3632 pub fn iter_storage_from(
3635 &self,
3636 state_root: H256,
3637 hashed_address: H256,
3638 starting_slot: H256,
3639 ) -> Result<Option<impl Iterator<Item = (H256, U256)>>, StoreError> {
3640 let state_trie = self.open_locked_state_trie(state_root)?;
3641 let Some(account_rlp) = state_trie.get(hashed_address.as_bytes())? else {
3642 return Ok(None);
3643 };
3644 let storage_root = AccountState::decode(&account_rlp)?.storage_root;
3645 let mut iter = self
3646 .open_locked_storage_trie(hashed_address, state_root, storage_root)?
3647 .into_iter();
3648 iter.advance(starting_slot.0.to_vec())?;
3649 Ok(Some(iter.content().map_while(|(path, value)| {
3650 Some((H256::from_slice(&path), U256::decode(&value).ok()?))
3651 })))
3652 }
3653
3654 pub fn iter_storage(
3657 &self,
3658 state_root: H256,
3659 hashed_address: H256,
3660 ) -> Result<Option<impl Iterator<Item = (H256, U256)>>, StoreError> {
3661 self.iter_storage_from(state_root, hashed_address, H256::zero())
3662 }
3663
3664 pub fn get_account_range_proof(
3665 &self,
3666 state_root: H256,
3667 starting_hash: H256,
3668 last_hash: Option<H256>,
3669 ) -> Result<Vec<Vec<u8>>, StoreError> {
3670 let state_trie = self.open_state_trie(state_root)?;
3671 let mut proof = state_trie.get_proof(starting_hash.as_bytes())?;
3672 if let Some(last_hash) = last_hash {
3673 proof.extend_from_slice(&state_trie.get_proof(last_hash.as_bytes())?);
3674 }
3675 Ok(proof)
3676 }
3677
3678 pub fn get_storage_range_proof(
3679 &self,
3680 state_root: H256,
3681 hashed_address: H256,
3682 starting_hash: H256,
3683 last_hash: Option<H256>,
3684 ) -> Result<Option<Vec<Vec<u8>>>, StoreError> {
3685 let state_trie = self.open_state_trie(state_root)?;
3686 let Some(account_rlp) = state_trie.get(hashed_address.as_bytes())? else {
3687 return Ok(None);
3688 };
3689 let storage_root = AccountState::decode(&account_rlp)?.storage_root;
3690 let storage_trie = self.open_storage_trie(hashed_address, state_root, storage_root)?;
3691 let mut proof = storage_trie.get_proof(starting_hash.as_bytes())?;
3692 if let Some(last_hash) = last_hash {
3693 proof.extend_from_slice(&storage_trie.get_proof(last_hash.as_bytes())?);
3694 }
3695 Ok(Some(proof))
3696 }
3697
3698 pub fn get_trie_nodes(
3705 &self,
3706 state_root: H256,
3707 paths: Vec<Vec<u8>>,
3708 byte_limit: u64,
3709 ) -> Result<Vec<Vec<u8>>, StoreError> {
3710 let Some(account_path) = paths.first() else {
3711 return Ok(vec![]);
3712 };
3713 let state_trie = self.open_state_trie(state_root)?;
3714 if paths.len() == 1 {
3716 let node = state_trie.get_node(account_path)?;
3718 return Ok(vec![node]);
3719 }
3720 let Some(account_state) = state_trie
3722 .get(account_path)?
3723 .map(|ref rlp| AccountState::decode(rlp))
3724 .transpose()?
3725 else {
3726 return Ok(vec![]);
3727 };
3728 let Ok(hashed_address) = account_path.clone().try_into().map(H256) else {
3730 return Ok(vec![]);
3731 };
3732 let storage_trie =
3733 self.open_storage_trie(hashed_address, state_root, account_state.storage_root)?;
3734 let mut nodes = vec![];
3736 let mut bytes_used = 0;
3737 for path in paths.iter().skip(1) {
3741 if bytes_used >= byte_limit {
3742 break;
3743 }
3744 let node = storage_trie.get_node(path)?;
3745 bytes_used += node.len() as u64;
3746 nodes.push(node);
3747 }
3748 Ok(nodes)
3749 }
3750
3751 fn gated_snapshot(&self, state_root: H256) -> Result<Arc<TrieLayerCache>, StoreError> {
3760 self.pending_trie_roots.wait_until_ready(state_root)?;
3761 Ok(self
3762 .trie_cache
3763 .read()
3764 .map_err(|_| StoreError::LockError)?
3765 .clone())
3766 }
3767
3768 pub fn open_state_trie(&self, state_root: H256) -> Result<Trie, StoreError> {
3772 let trie_db = TrieWrapper::new(
3773 state_root,
3774 self.gated_snapshot(state_root)?,
3775 Box::new(BackendTrieDB::new_for_accounts(
3776 self.backend.clone(),
3777 self.last_written()?,
3778 )?),
3779 None,
3780 );
3781 Ok(Trie::open(Box::new(trie_db), state_root))
3782 }
3783
3784 pub fn open_direct_state_trie(&self, state_root: H256) -> Result<Trie, StoreError> {
3788 Ok(Trie::open(
3789 Box::new(BackendTrieDB::new_for_accounts(
3790 self.backend.clone(),
3791 self.last_written()?,
3792 )?),
3793 state_root,
3794 ))
3795 }
3796
3797 pub fn open_locked_state_trie(&self, state_root: H256) -> Result<Trie, StoreError> {
3801 let trie_db = TrieWrapper::new(
3802 state_root,
3803 self.gated_snapshot(state_root)?,
3804 Box::new(state_trie_locked_backend(
3805 self.backend.as_ref(),
3806 self.last_written()?,
3807 )?),
3808 None,
3809 );
3810 Ok(Trie::open(Box::new(trie_db), state_root))
3811 }
3812
3813 pub fn open_storage_trie(
3816 &self,
3817 account_hash: H256,
3818 state_root: H256,
3819 storage_root: H256,
3820 ) -> Result<Trie, StoreError> {
3821 let trie_db = TrieWrapper::new(
3822 state_root,
3823 self.gated_snapshot(state_root)?,
3824 Box::new(BackendTrieDB::new_for_storages(
3825 self.backend.clone(),
3826 self.last_written()?,
3827 )?),
3828 Some(account_hash),
3829 );
3830 Ok(Trie::open(Box::new(trie_db), storage_root))
3831 }
3832
3833 fn open_state_trie_shared(
3837 &self,
3838 state_root: H256,
3839 read_view: Arc<dyn StorageReadView>,
3840 cache: Arc<TrieLayerCache>,
3841 last_written: Vec<u8>,
3842 ) -> Result<Trie, StoreError> {
3843 let trie_db = TrieWrapper::new(
3844 state_root,
3845 cache,
3846 Box::new(BackendTrieDB::new_for_accounts_with_view(
3847 self.backend.clone(),
3848 read_view,
3849 last_written,
3850 )?),
3851 None,
3852 );
3853 Ok(Trie::open(Box::new(trie_db), state_root))
3854 }
3855
3856 fn open_storage_trie_shared(
3858 &self,
3859 account_hash: H256,
3860 state_root: H256,
3861 storage_root: H256,
3862 read_view: Arc<dyn StorageReadView>,
3863 cache: Arc<TrieLayerCache>,
3864 last_written: Vec<u8>,
3865 ) -> Result<Trie, StoreError> {
3866 let trie_db = TrieWrapper::new(
3867 state_root,
3868 cache,
3869 Box::new(BackendTrieDB::new_for_storages_with_view(
3870 self.backend.clone(),
3871 read_view,
3872 last_written,
3873 )?),
3874 Some(account_hash),
3875 );
3876 Ok(Trie::open(Box::new(trie_db), storage_root))
3877 }
3878
3879 pub fn open_direct_storage_trie(
3882 &self,
3883 account_hash: H256,
3884 storage_root: H256,
3885 ) -> Result<Trie, StoreError> {
3886 Ok(Trie::open(
3887 Box::new(BackendTrieDB::new_for_account_storage(
3888 self.backend.clone(),
3889 account_hash,
3890 self.last_written()?,
3891 )?),
3892 storage_root,
3893 ))
3894 }
3895
3896 pub fn open_locked_storage_trie(
3899 &self,
3900 account_hash: H256,
3901 state_root: H256,
3902 storage_root: H256,
3903 ) -> Result<Trie, StoreError> {
3904 let trie_db = TrieWrapper::new(
3905 state_root,
3906 self.gated_snapshot(state_root)?,
3907 Box::new(state_trie_locked_backend(
3908 self.backend.as_ref(),
3909 self.last_written()?,
3910 )?),
3911 Some(account_hash),
3912 );
3913 Ok(Trie::open(Box::new(trie_db), storage_root))
3914 }
3915
3916 pub fn has_state_root(&self, state_root: H256) -> Result<bool, StoreError> {
3917 if state_root == EMPTY_TRIE_HASH {
3919 return Ok(true);
3920 }
3921 let trie = self.open_state_trie(state_root)?;
3922 let Some(root) = trie.db().get(Nibbles::default())? else {
3924 return Ok(false);
3925 };
3926 let root_hash = ethrex_trie::Node::decode(&root)?
3927 .compute_hash(&NativeCrypto)
3928 .finalize(&NativeCrypto);
3929 Ok(state_root == root_hash)
3930 }
3931
3932 pub fn is_state_in_layer_cache(&self, state_root: H256) -> Result<bool, StoreError> {
3941 let trie = self
3942 .trie_cache
3943 .read()
3944 .map_err(|_| StoreError::LockError)?
3945 .clone();
3946 Ok(trie.contains(state_root))
3947 }
3948
3949 pub fn highest_state_history_block_number(&self) -> Result<Option<BlockNumber>, StoreError> {
3955 let read = self.backend.begin_read()?;
3956 let Some(key) = read.last_key(STATE_HISTORY)? else {
3957 return Ok(None);
3958 };
3959 let arr = <[u8; 8]>::try_from(key.as_slice()).map_err(|_| {
3960 StoreError::Custom(format!(
3961 "STATE_HISTORY key has unexpected length: {}",
3962 key.len()
3963 ))
3964 })?;
3965 Ok(Some(BlockNumber::from_be_bytes(arr)))
3966 }
3967
3968 pub fn lowest_state_history_block_number(&self) -> Result<Option<BlockNumber>, StoreError> {
3973 let read = self.backend.begin_read()?;
3974 let Some(key) = read.first_key(STATE_HISTORY)? else {
3975 return Ok(None);
3976 };
3977 let arr = <[u8; 8]>::try_from(key.as_slice()).map_err(|_| {
3978 StoreError::Custom(format!(
3979 "STATE_HISTORY key has unexpected length: {}",
3980 key.len()
3981 ))
3982 })?;
3983 Ok(Some(BlockNumber::from_be_bytes(arr)))
3984 }
3985
3986 pub fn reorg_retention(&self) -> Result<u64, StoreError> {
3991 let cache = self.trie_cache.read().map_err(|_| StoreError::LockError)?;
3992 Ok(cache.commit_threshold() as u64)
3993 }
3994
3995 #[doc(hidden)]
3999 pub fn put_state_history_entry_for_test(
4000 &self,
4001 block_number: BlockNumber,
4002 encoded: &[u8],
4003 ) -> Result<(), StoreError> {
4004 let mut tx = self.backend.begin_write()?;
4005 tx.put(STATE_HISTORY, &block_number.to_be_bytes(), encoded)?;
4006 tx.commit()?;
4007 Ok(())
4008 }
4009
4010 pub fn install_overlay_for_reorg(
4031 &self,
4032 from_block: BlockNumber,
4033 to_block: BlockNumber,
4034 expected_hash: impl Fn(BlockNumber) -> Option<H256>,
4035 ) -> Result<(), StoreError> {
4036 let overlay =
4038 Overlay::from_journal(self.backend.as_ref(), from_block, to_block, expected_hash)
4039 .map_err(|e| StoreError::Custom(format!("overlay construction failed: {e}")))?;
4040
4041 let threshold = {
4042 let current = self.trie_cache.read().map_err(|_| StoreError::LockError)?;
4043 current.commit_threshold()
4044 };
4045 let mut fresh =
4048 TrieLayerCache::new_with_safe_commit(threshold, self.safe_commit_root.clone());
4049 fresh.set_overlay(Arc::new(overlay));
4050
4051 self.rendezvous_persist_worker("install_overlay_for_reorg")?;
4054
4055 let mut guard = self.trie_cache.write().map_err(|_| StoreError::LockError)?;
4056 *guard = Arc::new(fresh);
4057 Ok(())
4058 }
4059
4060 fn rendezvous_persist_worker(&self, caller: &str) -> Result<(), StoreError> {
4073 let (ack_tx, ack_rx) = sync_channel::<Result<(), StoreError>>(1);
4074 self.persist_tx
4075 .send(PersistMessage::Ping(ack_tx))
4076 .map_err(|e| {
4077 StoreError::Custom(format!("{caller}: failed to ping persist worker: {e}"))
4078 })?;
4079 ack_rx.recv().map_err(|e| {
4080 StoreError::Custom(format!("{caller}: persist worker ping ack failed: {e}"))
4081 })??;
4082 Ok(())
4083 }
4084
4085 pub fn reorg_overlay_size_hint(&self) -> Result<(usize, usize), StoreError> {
4090 let guard = self.trie_cache.read().map_err(|_| StoreError::LockError)?;
4091 match guard.overlay() {
4092 Some(ov) => Ok((ov.len(), ov.byte_size())),
4093 None => Ok((0, 0)),
4094 }
4095 }
4096
4097 pub fn set_journal_pruning_paused(&self, paused: bool) {
4101 self.journal_pruning_paused
4102 .store(paused, std::sync::atomic::Ordering::Release);
4103 }
4104
4105 pub fn clear_reorg_overlay(&self) -> Result<(), StoreError> {
4109 let mut guard = self.trie_cache.write().map_err(|_| StoreError::LockError)?;
4110 let mut updated = (**guard).clone();
4111 updated.clear_overlay();
4112 *guard = Arc::new(updated);
4113 Ok(())
4114 }
4115
4116 pub fn abort_reorg(&self) -> Result<(), StoreError> {
4122 self.rendezvous_persist_worker("abort_reorg")?;
4130 let mut guard = self.trie_cache.write().map_err(|_| StoreError::LockError)?;
4131 let threshold = guard.commit_threshold();
4132 *guard = Arc::new(TrieLayerCache::new_with_safe_commit(
4133 threshold,
4134 self.safe_commit_root.clone(),
4135 ));
4136 Ok(())
4137 }
4138
4139 pub fn ancestors(&self, block_hash: BlockHash) -> AncestorIterator {
4142 AncestorIterator {
4143 store: self.clone(),
4144 next_hash: block_hash,
4145 }
4146 }
4147
4148 pub fn is_canonical_sync(&self, block_hash: BlockHash) -> Result<bool, StoreError> {
4150 let Some(block_number) = self.get_block_number_sync(block_hash)? else {
4151 return Ok(false);
4152 };
4153 Ok(self
4154 .get_canonical_block_hash_sync(block_number)?
4155 .is_some_and(|h| h == block_hash))
4156 }
4157
4158 pub fn generate_flatkeyvalue(&self) -> Result<(), StoreError> {
4159 self.flatkeyvalue_control_tx
4160 .send(FKVGeneratorControlMessage::Continue)
4161 .map_err(|_| StoreError::Custom("FlatKeyValue thread disconnected.".to_string()))
4162 }
4163
4164 pub fn create_checkpoint(&self, path: impl AsRef<Path>) -> Result<(), StoreError> {
4165 self.backend.create_checkpoint(path.as_ref())?;
4166 init_metadata_file(path.as_ref())?;
4167 Ok(())
4168 }
4169
4170 pub fn get_store_directory(&self) -> Result<PathBuf, StoreError> {
4171 Ok(self.db_path.clone())
4172 }
4173
4174 async fn load_latest_block_number(&self) -> Result<Option<BlockNumber>, StoreError> {
4176 self.read_chain_data_block_number(ChainDataIndex::LatestBlockNumber)
4177 .await
4178 }
4179
4180 fn load_canonical_block_hash(
4181 &self,
4182 block_number: BlockNumber,
4183 ) -> Result<Option<BlockHash>, StoreError> {
4184 let txn = self.backend.begin_read()?;
4185 txn.get(
4186 CANONICAL_BLOCK_HASHES,
4187 block_number.to_le_bytes().as_slice(),
4188 )?
4189 .map(|bytes| H256::decode(bytes.as_slice()))
4190 .transpose()
4191 .map_err(StoreError::from)
4192 }
4193
4194 fn load_block_header(
4195 &self,
4196 block_number: BlockNumber,
4197 ) -> Result<Option<BlockHeader>, StoreError> {
4198 let Some(block_hash) = self.load_canonical_block_hash(block_number)? else {
4199 return Ok(None);
4200 };
4201 self.load_block_header_by_hash(block_hash)
4202 }
4203
4204 fn load_block_header_by_hash(
4206 &self,
4207 block_hash: BlockHash,
4208 ) -> Result<Option<BlockHeader>, StoreError> {
4209 let txn = self.backend.begin_read()?;
4210 let hash_key = block_hash.encode_to_vec();
4211 let header_value = txn.get(HEADERS, hash_key.as_slice())?;
4212 let mut header = header_value
4213 .map(|bytes| BlockHeaderRLP::from_bytes(bytes).to())
4214 .transpose()
4215 .map_err(StoreError::from)?;
4216 header.as_mut().inspect(|h| {
4217 let _ = h.hash.set(block_hash);
4219 });
4220 Ok(header)
4221 }
4222
4223 pub fn last_written(&self) -> Result<Vec<u8>, StoreError> {
4224 let last_computed_flatkeyvalue = self
4225 .last_computed_flatkeyvalue
4226 .read()
4227 .map_err(|_| StoreError::LockError)?;
4228 Ok(last_computed_flatkeyvalue.clone())
4229 }
4230
4231 pub fn flatkeyvalue_fully_generated(&self) -> Result<bool, StoreError> {
4242 let tx = self.backend.begin_read()?;
4243 let marker = tx.get(MISC_VALUES, "last_written".as_bytes())?;
4244 Ok(Self::flatkeyvalue_generation_complete(marker.as_deref()))
4245 }
4246
4247 fn flatkeyvalue_generation_complete(marker: Option<&[u8]>) -> bool {
4251 marker == Some([0xff].as_slice())
4252 }
4253
4254 fn flatkeyvalue_computed_with_last_written(account: H256, last_written: &[u8]) -> bool {
4255 let account_nibbles = Nibbles::from_bytes(account.as_bytes());
4256 &last_written[0..64] > account_nibbles.as_ref()
4257 }
4258
4259 pub fn read_flushed_upto(&self) -> Result<BlockNumber, StoreError> {
4264 Ok(self.read_flushed_upto_opt()?.unwrap_or(0))
4265 }
4266
4267 fn read_flushed_upto_opt(&self) -> Result<Option<BlockNumber>, StoreError> {
4270 let tx = self.backend.begin_read()?;
4271 match tx.get(MISC_VALUES, FLUSHED_UPTO_KEY)? {
4272 Some(bytes) => Ok(Some(decode_flushed_upto(&bytes)?)),
4273 None => Ok(None),
4274 }
4275 }
4276
4277 #[cfg(any(test, feature = "testing"))]
4280 pub fn buffer_block_for_test(&self, block: &Block) {
4281 mutate_block_buffer(&self.block_data_buffer, |b| {
4282 b.insert(block.clone(), vec![], vec![])
4283 })
4284 .expect("block_data_buffer lock poisoned");
4285 }
4286
4287 #[cfg(any(test, feature = "testing"))]
4290 pub fn flush_block_data_for_test(&self) -> Result<(), StoreError> {
4291 flush_block_data(self.backend.as_ref(), &self.block_data_buffer)
4292 }
4293
4294 #[cfg(any(test, feature = "testing"))]
4298 pub fn get_trie_node_for_test(
4299 &self,
4300 is_account: bool,
4301 key: &[u8],
4302 ) -> Result<Option<Vec<u8>>, StoreError> {
4303 let table = if is_account {
4304 ACCOUNT_TRIE_NODES
4305 } else {
4306 STORAGE_TRIE_NODES
4307 };
4308 self.backend.begin_read()?.get(table, key)
4309 }
4310
4311 #[cfg(any(test, feature = "testing"))]
4315 pub fn buffer_block_with_codes_for_test(&self, block: &Block, codes: Vec<(H256, Code)>) {
4316 mutate_block_buffer(&self.block_data_buffer, |b| {
4317 b.insert(block.clone(), vec![], codes)
4318 })
4319 .expect("block_data_buffer lock poisoned");
4320 }
4321
4322 #[cfg(any(test, feature = "testing"))]
4327 pub fn register_pending_root_for_test(&self, root: H256) -> Result<(), StoreError> {
4328 self.pending_trie_roots.register(root)
4329 }
4330
4331 #[cfg(any(test, feature = "testing"))]
4334 pub fn clear_pending_root_for_test(&self, root: H256) {
4335 self.pending_trie_roots.clear(root)
4336 }
4337
4338 async fn anchor_to_durable_head(&self, latest: BlockNumber) -> Result<(), StoreError> {
4357 let marker = self.read_flushed_upto_opt()?;
4358 let start = match marker {
4359 Some(flushed) => flushed.min(latest),
4360 None => latest,
4361 };
4362
4363 let mut head = start;
4364 let latest_block_header = loop {
4365 match self.load_block_header(head)? {
4366 Some(header) => break header,
4367 None if marker.is_none() => return Err(StoreError::MissingLatestBlockNumber),
4370 None if head == 0 => return Err(StoreError::MissingLatestBlockNumber),
4371 None => {
4372 warn!(
4373 "durable head {head}: canonical hash has no on-disk header \
4374 (reorg inside flush window); rewinding"
4375 );
4376 head -= 1;
4377 }
4378 }
4379 };
4380 self.latest_block_header.update(latest_block_header);
4381
4382 let reanchor = head != latest;
4387 let rewrite_marker = marker != Some(head);
4388 if reanchor || rewrite_marker {
4389 let mut tx = self.backend.begin_write()?;
4390 if reanchor {
4391 let latest_key = chain_data_key(ChainDataIndex::LatestBlockNumber);
4394 tx.put(CHAIN_DATA, &latest_key, &head.to_le_bytes())?;
4395 }
4396 if rewrite_marker {
4397 write_flushed_upto(tx.as_mut(), head)?;
4398 }
4399 tx.commit()?;
4400 }
4401 Ok(())
4402 }
4403}
4404
4405pub fn write_flushed_upto(
4409 tx: &mut dyn StorageWriteBatch,
4410 n: BlockNumber,
4411) -> Result<(), StoreError> {
4412 tx.put(MISC_VALUES, FLUSHED_UPTO_KEY, &n.to_le_bytes())
4413}
4414
4415fn decode_flushed_upto(bytes: &[u8]) -> Result<BlockNumber, StoreError> {
4421 let arr: [u8; 8] = bytes
4422 .try_into()
4423 .map_err(|_| StoreError::Custom("Invalid flushed_upto bytes".to_string()))?;
4424 Ok(BlockNumber::from_le_bytes(arr))
4425}
4426
4427fn mutate_block_buffer(
4430 buffer: &Arc<RwLock<Arc<BlockDataBuffer>>>,
4431 f: impl FnOnce(&mut BlockDataBuffer),
4432) -> Result<(), StoreError> {
4433 let mut new_buf = (*buffer.read().map_err(|_| StoreError::LockError)?.clone()).clone();
4434 f(&mut new_buf);
4435 *buffer.write().map_err(|_| StoreError::LockError)? = Arc::new(new_buf);
4436 Ok(())
4437}
4438
4439const DEFAULT_PERSIST_CHANNEL_CAPACITY: usize = 2;
4441
4442struct BlockPersist {
4448 blocks: Vec<(Block, Vec<Receipt>)>,
4449 codes: Vec<(H256, Code)>,
4450 parent_state_root: H256,
4451 child_state_root: H256,
4452 account_updates: TrieNodesUpdate,
4453 storage_updates: Vec<(H256, TrieNodesUpdate)>,
4454 commit_depth: Option<usize>,
4455 wait_for_flush: bool,
4456 block_number: BlockNumber,
4468 block_hash: H256,
4470 ack: std::sync::mpsc::SyncSender<Result<(), StoreError>>,
4471}
4472
4473enum PersistMessage {
4477 Block(Box<BlockPersist>),
4478 Commit(H256),
4484 Ping(std::sync::mpsc::SyncSender<Result<(), StoreError>>),
4485 Shutdown {
4490 ack: std::sync::mpsc::SyncSender<Result<(), StoreError>>,
4491 },
4492}
4493
4494fn write_block_data(
4499 tx: &mut dyn StorageWriteBatch,
4500 number: BlockNumber,
4501 hash: BlockHash,
4502 header: &BlockHeader,
4503 body: &BlockBody,
4504) -> Result<(), StoreError> {
4505 let hash_key = hash.encode_to_vec();
4506 tx.put(
4507 HEADERS,
4508 &hash_key,
4509 BlockHeaderRLP::from(header.clone()).bytes(),
4510 )?;
4511 tx.put(
4512 BODIES,
4513 &hash_key,
4514 BlockBodyRLP::from_bytes(body.encode_to_vec()).bytes(),
4515 )?;
4516 tx.put(BLOCK_NUMBERS, &hash_key, &number.to_le_bytes())?;
4517 for (index, transaction) in body.transactions.iter().enumerate() {
4518 tx.merge(
4519 TRANSACTION_LOCATIONS,
4520 transaction.hash(&NativeCrypto).as_bytes(),
4521 &encode_tx_location_operand(number, hash, index as u64),
4522 )?;
4523 }
4524 Ok(())
4525}
4526
4527fn flush_block_data(
4530 backend: &dyn StorageBackend,
4531 buffer: &Arc<RwLock<Arc<BlockDataBuffer>>>,
4532) -> Result<(), StoreError> {
4533 let snapshot = buffer.read().map_err(|_| StoreError::LockError)?.clone();
4534 let to_flush = snapshot.flushable();
4535 if to_flush.is_empty() {
4536 return Ok(());
4537 }
4538 let hashes: Vec<_> = to_flush.iter().map(|b| b.header.hash()).collect();
4539 let codes = snapshot.codes_for(&hashes);
4540 let mut max_number = snapshot.flushed_upto();
4541
4542 let mut tx = backend.begin_write()?;
4543 for b in &to_flush {
4544 let hash = b.header.hash();
4545 write_block_data(tx.as_mut(), b.number, hash, &b.header, &b.body)?;
4546 for (index, receipt) in b.receipts.iter().enumerate() {
4547 tx.put(
4548 RECEIPTS_V2,
4549 &receipt_key(&hash, index as u64),
4550 &receipt.encode_to_vec(),
4551 )?;
4552 }
4553 max_number = max_number.max(b.number);
4554 }
4555 for (code_hash, code) in codes {
4556 let buf = encode_code(&code);
4557 tx.put(ACCOUNT_CODES, code_hash.as_ref(), &buf)?;
4558 tx.put(
4559 ACCOUNT_CODE_METADATA,
4560 code_hash.as_ref(),
4561 &(code.len() as u64).to_be_bytes(),
4562 )?;
4563 }
4564 write_flushed_upto(tx.as_mut(), max_number)?;
4565 tx.commit()?;
4566
4567 mutate_block_buffer(buffer, |b| b.evict_flushed(max_number))
4569}
4570
4571type TrieNodesUpdate = Vec<(Nibbles, Vec<u8>)>;
4572
4573#[derive(Debug, Default)]
4579struct PendingTrieRoots {
4580 count: AtomicUsize,
4582 roots: Mutex<HashSet<H256>>,
4583 ready: Condvar,
4584}
4585
4586impl PendingTrieRoots {
4587 fn register(&self, root: H256) -> Result<(), StoreError> {
4591 let mut roots = self.roots.lock().map_err(|_| StoreError::LockError)?;
4592 if roots.insert(root) {
4593 self.count.fetch_add(1, Ordering::Release);
4594 }
4595 Ok(())
4596 }
4597
4598 fn clear(&self, root: H256) {
4603 let Ok(mut roots) = self.roots.lock() else {
4604 return;
4605 };
4606 if roots.remove(&root) {
4607 self.count.fetch_sub(1, Ordering::Release);
4608 self.ready.notify_all();
4609 }
4610 }
4611
4612 fn wait_until_ready(&self, root: H256) -> Result<(), StoreError> {
4615 if self.count.load(Ordering::Acquire) == 0 {
4616 return Ok(());
4617 }
4618 let mut roots = self.roots.lock().map_err(|_| StoreError::LockError)?;
4619 while roots.contains(&root) {
4620 roots = self.ready.wait(roots).map_err(|_| StoreError::LockError)?;
4621 }
4622 Ok(())
4623 }
4624}
4625
4626#[allow(clippy::too_many_arguments)]
4630fn apply_trie_phase1(
4631 trie_cache: &Arc<RwLock<Arc<TrieLayerCache>>>,
4632 pending_roots: &PendingTrieRoots,
4633 parent_state_root: H256,
4634 child_state_root: H256,
4635 block_number: BlockNumber,
4636 block_hash: H256,
4637 account_updates: TrieNodesUpdate,
4638 storage_updates: Vec<(H256, TrieNodesUpdate)>,
4639) -> Result<(), StoreError> {
4640 let build: Result<(), StoreError> = (|| {
4641 let new_layer = storage_updates
4642 .into_iter()
4643 .flat_map(|(account_hash, nodes)| {
4644 nodes
4645 .into_iter()
4646 .map(move |(path, node)| (apply_prefix(Some(account_hash), path), node))
4647 })
4648 .chain(account_updates)
4649 .collect();
4650 let trie = trie_cache
4651 .read()
4652 .map_err(|_| StoreError::LockError)?
4653 .clone();
4654 let mut trie_mut = (*trie).clone();
4655 trie_mut.put_batch(
4656 parent_state_root,
4657 child_state_root,
4658 block_number,
4659 block_hash,
4660 new_layer,
4661 );
4662 *trie_cache.write().map_err(|_| StoreError::LockError)? = Arc::new(trie_mut);
4663 Ok(())
4664 })();
4665 pending_roots.clear(child_state_root);
4670 build
4671}
4672
4673fn commit_trie_if_due(
4697 backend: &dyn StorageBackend,
4698 trie_cache: &Arc<RwLock<Arc<TrieLayerCache>>>,
4699 fkv_ctl: &SyncSender<FKVGeneratorControlMessage>,
4700 parent_state_root: H256,
4701 commit_depth: Option<usize>,
4702 is_batch: bool,
4703) -> Result<(), StoreError> {
4704 let trie = trie_cache
4705 .read()
4706 .map_err(|_| StoreError::LockError)?
4707 .clone();
4708 let commitable = match commit_depth {
4710 Some(depth) => trie.get_commitable_by_depth(parent_state_root, depth),
4711 None => trie.get_commitable(parent_state_root),
4712 };
4713 let Some(root) = commitable else {
4714 return Ok(());
4716 };
4717 commit_to_disk(backend, fkv_ctl, trie_cache, &trie, root, is_batch)
4718}
4719
4720fn commit_to_disk(
4730 backend: &dyn StorageBackend,
4731 fkv_ctl: &SyncSender<FKVGeneratorControlMessage>,
4732 trie_cache: &Arc<RwLock<Arc<TrieLayerCache>>>,
4733 trie: &Arc<TrieLayerCache>,
4734 root: H256,
4735 is_batch: bool,
4736) -> Result<(), StoreError> {
4737 if !trie.has_layer(root) {
4741 debug!(
4742 root = ?root,
4743 layers = trie.layer_count(),
4744 is_batch,
4745 "Skipping trie commit: state root has no in-memory layer. Expected when the block \
4746 did not change the state root (empty L2 blocks) or the root was already flushed."
4747 );
4748 return Ok(());
4749 }
4750
4751 let _ = fkv_ctl.send(FKVGeneratorControlMessage::Stop);
4754
4755 let mut trie_mut = (**trie).clone();
4757
4758 let read_view = backend.begin_read()?;
4772 let last_written = read_view
4773 .get(MISC_VALUES, "last_written".as_bytes())?
4774 .unwrap_or_default();
4775
4776 let mut write_tx = backend.begin_write()?;
4777
4778 let overlay_for_reconciliation = if !is_batch {
4787 trie.overlay().cloned()
4788 } else {
4789 None
4790 };
4791
4792 let root = if overlay_for_reconciliation.is_some() {
4802 trie.bottom_layer_root(root)
4803 } else {
4804 root
4805 };
4806
4807 let committed_layers = trie_mut.commit(root).ok_or_else(|| {
4817 StoreError::Custom(format!(
4818 "trie layer for state root {root:?} disappeared between the has_layer check and \
4819 commit (layers={}, is_batch={is_batch}): the TrieLayerCache clone lost the layer \
4820 map, so this block's state was not written",
4821 trie.layer_count(),
4822 ))
4823 })?;
4824
4825 debug_assert!(
4830 overlay_for_reconciliation.is_none() || committed_layers.len() == 1,
4831 "overlay-backed reconciliation must commit exactly one layer (T), got {}",
4832 committed_layers.len()
4833 );
4834
4835 let extra_writes: Vec<(Vec<u8>, Vec<u8>)> = match &overlay_for_reconciliation {
4841 Some(overlay) => {
4842 let layer_keys: rustc_hash::FxHashSet<&Vec<u8>> = committed_layers
4843 .iter()
4844 .flat_map(|l| l.nodes.iter().map(|(k, _)| k))
4845 .collect();
4846 overlay
4847 .iter_all_entries()
4848 .filter(|(_, key, _)| !layer_keys.contains(key))
4849 .map(|(_, key, value)| (key.clone(), value.clone().unwrap_or_default()))
4850 .collect()
4851 }
4852 None => Vec::new(),
4853 };
4854 let reorg_heights = overlay_for_reconciliation
4856 .as_ref()
4857 .map(|ov| (ov.to_block(), ov.from_block()));
4858
4859 let mut overlay: HashMap<Vec<u8>, Option<Vec<u8>>> = HashMap::new();
4869
4870 let mut result = Ok(());
4871 'layers: for layer in &committed_layers {
4872 let mut journal_account_trie: FlatDiff = Vec::new();
4878 let mut journal_storage_trie: FlatDiff = Vec::new();
4879 let mut journal_account_flat: FlatDiff = Vec::new();
4880 let mut journal_storage_flat: FlatDiff = Vec::new();
4881
4882 let is_reconciliation_layer = overlay_for_reconciliation.is_some();
4893 let extra: &[(Vec<u8>, Vec<u8>)] = if is_reconciliation_layer {
4894 &extra_writes
4895 } else {
4896 &[]
4897 };
4898
4899 for (key, value) in layer.nodes.iter().chain(extra.iter()) {
4900 let (is_leaf, is_account) = classify_trie_key(key.len());
4901
4902 if is_leaf && key.as_slice() > last_written.as_slice() {
4907 continue;
4908 }
4909 let table = if is_leaf {
4910 if is_account {
4911 &ACCOUNT_FLATKEYVALUE
4912 } else {
4913 &STORAGE_FLATKEYVALUE
4914 }
4915 } else if is_account {
4916 &ACCOUNT_TRIE_NODES
4917 } else {
4918 &STORAGE_TRIE_NODES
4919 };
4920
4921 let prev_value = if !is_batch {
4924 match overlay.get(key) {
4925 Some(v) => Some(v.clone()),
4926 None => match read_view.get(table, key) {
4927 Ok(v) => Some(v),
4928 Err(e) => {
4929 result = Err(e);
4930 break 'layers;
4931 }
4932 },
4933 }
4934 } else {
4935 None
4936 };
4937
4938 let new_value = if value.is_empty() {
4939 result = write_tx.delete(table, key);
4940 None
4941 } else {
4942 result = write_tx.put(table, key, value);
4943 Some(value.clone())
4944 };
4945 if result.is_err() {
4946 break 'layers;
4947 }
4948
4949 if let Some(prev) = prev_value {
4952 let bucket = match (is_leaf, is_account) {
4953 (false, true) => &mut journal_account_trie,
4954 (false, false) => &mut journal_storage_trie,
4955 (true, true) => &mut journal_account_flat,
4956 (true, false) => &mut journal_storage_flat,
4957 };
4958 bucket.push((key.clone(), prev));
4959 overlay.insert(key.clone(), new_value);
4962 }
4963 }
4964
4965 if !is_batch && let Some((t, d)) = reorg_heights {
4970 debug_assert_eq!(
4971 layer.block_number, t,
4972 "first new-chain commit must be at the pivot's T height (overlay.to_block)"
4973 );
4974 if layer.block_number == t {
4981 let start = t.to_be_bytes();
4982 let end = d.saturating_add(1).to_be_bytes();
4983 result = write_tx.delete_range(STATE_HISTORY, &start, &end);
4984 if result.is_err() {
4985 break 'layers;
4986 }
4987 } else {
4988 error!(
4989 block_number = layer.block_number,
4990 t,
4991 d,
4992 "deep-reorg reconciliation skipped: committed layer not at pivot height; skipping STATE_HISTORY delete_range to avoid history gaps"
4993 );
4994 }
4995 }
4996
4997 if !is_batch {
5003 let entry = JournalEntry {
5004 block_hash: layer.block_hash,
5005 parent_state_root: layer.parent_state_root,
5006 account_trie_diff: journal_account_trie,
5007 storage_trie_diff: journal_storage_trie,
5008 account_flat_diff: journal_account_flat,
5009 storage_flat_diff: journal_storage_flat,
5010 };
5011 result = write_tx.put(
5012 STATE_HISTORY,
5013 &layer.block_number.to_be_bytes(),
5014 &entry.encode(),
5015 );
5016 if result.is_err() {
5017 break 'layers;
5018 }
5019 }
5020 }
5021
5022 if result.is_ok() {
5023 result = write_tx.commit();
5024 }
5025 let _ = fkv_ctl.send(FKVGeneratorControlMessage::Continue);
5027 result?;
5028
5029 if overlay_for_reconciliation.is_some() {
5032 trie_mut.clear_overlay();
5033 }
5034
5035 *trie_cache.write().map_err(|_| StoreError::LockError)? = Arc::new(trie_mut);
5041 Ok(())
5042}
5043
5044fn flatkeyvalue_generator(
5047 backend: &Arc<dyn StorageBackend>,
5048 last_computed_fkv: &RwLock<Vec<u8>>,
5049 control_rx: &std::sync::mpsc::Receiver<FKVGeneratorControlMessage>,
5050) -> Result<(), StoreError> {
5051 info!("Generation of FlatKeyValue started.");
5052 let initial_last_written = backend
5053 .begin_read()?
5054 .get(MISC_VALUES, "last_written".as_bytes())?
5055 .unwrap_or_default();
5056
5057 if initial_last_written.is_empty() {
5058 backend.clear_table(ACCOUNT_FLATKEYVALUE)?;
5060 backend.clear_table(STORAGE_FLATKEYVALUE)?;
5061 } else if initial_last_written == [0xff] {
5062 info!("FlatKeyValue already generated. Skipping.");
5064 return Ok(());
5065 }
5066
5067 loop {
5068 let read_tx = backend.begin_read()?;
5071 let root = read_tx
5072 .get(ACCOUNT_TRIE_NODES, &[])?
5073 .ok_or(StoreError::MissingLatestBlockNumber)?;
5074 let root: Node = ethrex_trie::Node::decode(&root)?;
5075 let state_root = root.compute_hash(&NativeCrypto).finalize(&NativeCrypto);
5076
5077 let last_written = read_tx
5078 .get(MISC_VALUES, "last_written".as_bytes())?
5079 .unwrap_or_default();
5080 let last_written_account = last_written
5081 .get(0..64)
5082 .map(|v| Nibbles::from_hex(v.to_vec()))
5083 .unwrap_or_default();
5084 let mut last_written_storage = last_written
5085 .get(66..130)
5086 .map(|v| Nibbles::from_hex(v.to_vec()))
5087 .unwrap_or_default();
5088
5089 debug!("Starting FlatKeyValue loop pivot={last_written:?} SR={state_root:x}");
5090
5091 let mut ctr = 0;
5092 let mut write_txn = backend.begin_write()?;
5093 let mut iter = Trie::open(
5094 Box::new(BackendTrieDB::new_for_accounts_with_view(
5095 backend.clone(),
5096 read_tx.clone(),
5097 last_written.clone(),
5098 )?),
5099 state_root,
5100 )
5101 .into_iter();
5102 if last_written_account > Nibbles::default() {
5103 iter.advance(last_written_account.to_bytes())?;
5104 }
5105 let res = iter.try_for_each(|(path, node)| -> Result<(), StoreError> {
5106 let Node::Leaf(node) = node else {
5107 return Ok(());
5108 };
5109 let account_state = AccountState::decode(&node.value)?;
5110 let account_hash = H256::from_slice(&path.to_bytes());
5111 write_txn.put(MISC_VALUES, "last_written".as_bytes(), path.as_ref())?;
5112 write_txn.put(ACCOUNT_FLATKEYVALUE, path.as_ref(), &node.value)?;
5113 ctr += 1;
5114 if ctr > 10_000 {
5115 write_txn.commit()?;
5116 write_txn = backend.begin_write()?;
5117 *last_computed_fkv
5118 .write()
5119 .map_err(|_| StoreError::LockError)? = path.as_ref().to_vec();
5120 ctr = 0;
5121 }
5122
5123 let mut iter_inner = Trie::open(
5124 Box::new(BackendTrieDB::new_for_account_storage_with_view(
5125 backend.clone(),
5126 read_tx.clone(),
5127 account_hash,
5128 path.as_ref().to_vec(),
5129 )?),
5130 account_state.storage_root,
5131 )
5132 .into_iter();
5133 if last_written_storage > Nibbles::default() {
5134 iter_inner.advance(last_written_storage.to_bytes())?;
5135 last_written_storage = Nibbles::default();
5136 }
5137 iter_inner.try_for_each(|(path, node)| -> Result<(), StoreError> {
5138 let Node::Leaf(node) = node else {
5139 return Ok(());
5140 };
5141 let key = apply_prefix(Some(account_hash), path);
5142 write_txn.put(MISC_VALUES, "last_written".as_bytes(), key.as_ref())?;
5143 write_txn.put(STORAGE_FLATKEYVALUE, key.as_ref(), &node.value)?;
5144 ctr += 1;
5145 if ctr > 10_000 {
5146 write_txn.commit()?;
5147 write_txn = backend.begin_write()?;
5148 *last_computed_fkv
5149 .write()
5150 .map_err(|_| StoreError::LockError)? = key.into_vec();
5151 ctr = 0;
5152 }
5153 fkv_check_for_stop_msg(control_rx)?;
5154 Ok(())
5155 })?;
5156 fkv_check_for_stop_msg(control_rx)?;
5157 Ok(())
5158 });
5159 match res {
5160 Err(StoreError::PivotChanged) => {
5161 match control_rx.recv() {
5162 Ok(FKVGeneratorControlMessage::Continue) => {}
5163 Ok(FKVGeneratorControlMessage::Stop) => {
5164 return Err(StoreError::Custom("Unexpected Stop message".to_string()));
5165 }
5166 Err(std::sync::mpsc::RecvError) => {
5168 info!("Store closed, stopping FlatKeyValue generation.");
5169 return Ok(());
5170 }
5171 }
5172 }
5173 Err(err) => return Err(err),
5174 Ok(()) => {
5175 write_txn.put(MISC_VALUES, "last_written".as_bytes(), &[0xff])?;
5176 write_txn.commit()?;
5177 *last_computed_fkv
5178 .write()
5179 .map_err(|_| StoreError::LockError)? = vec![0xff; 131];
5180 info!("FlatKeyValue generation finished.");
5181 return Ok(());
5182 }
5183 };
5184 }
5185}
5186
5187fn fkv_check_for_stop_msg(
5188 control_rx: &std::sync::mpsc::Receiver<FKVGeneratorControlMessage>,
5189) -> Result<(), StoreError> {
5190 match control_rx.try_recv() {
5191 Ok(FKVGeneratorControlMessage::Stop) | Err(TryRecvError::Disconnected) => {
5192 return Err(StoreError::PivotChanged);
5193 }
5194 Ok(FKVGeneratorControlMessage::Continue) => {
5195 return Err(StoreError::Custom(
5196 "Unexpected Continue message".to_string(),
5197 ));
5198 }
5199 Err(TryRecvError::Empty) => {}
5200 }
5201 Ok(())
5202}
5203
5204fn state_trie_locked_backend(
5205 backend: &dyn StorageBackend,
5206 last_written: Vec<u8>,
5207) -> Result<BackendTrieDBLocked, StoreError> {
5208 BackendTrieDBLocked::new(backend, last_written)
5210}
5211
5212pub struct AccountProof {
5213 pub proof: Vec<NodeRLP>,
5214 pub account: AccountState,
5215 pub storage_proof: Vec<StorageSlotProof>,
5216}
5217
5218pub struct StorageSlotProof {
5219 pub proof: Vec<NodeRLP>,
5220 pub key: H256,
5221 pub value: U256,
5222}
5223
5224pub struct AncestorIterator {
5225 store: Store,
5226 next_hash: BlockHash,
5227}
5228
5229impl Iterator for AncestorIterator {
5230 type Item = Result<(BlockHash, BlockHeader), StoreError>;
5231
5232 fn next(&mut self) -> Option<Self::Item> {
5233 let next_hash = self.next_hash;
5234 match self.store.get_block_header_by_hash(next_hash) {
5238 Ok(Some(header)) => {
5239 let ret_hash = self.next_hash;
5240 self.next_hash = header.parent_hash;
5241 Some(Ok((ret_hash, header)))
5242 }
5243 Ok(None) => None,
5244 Err(e) => Some(Err(e)),
5245 }
5246 }
5247}
5248
5249pub fn hash_address(address: &Address) -> Vec<u8> {
5250 keccak_hash(address.to_fixed_bytes()).to_vec()
5251}
5252
5253fn hash_address_fixed(address: &Address) -> H256 {
5254 keccak(address.to_fixed_bytes())
5255}
5256
5257pub fn hash_key(key: &H256) -> Vec<u8> {
5258 keccak_hash(key.to_fixed_bytes()).to_vec()
5259}
5260
5261pub fn hash_key_fixed(key: &H256) -> [u8; 32] {
5262 keccak_hash(key.to_fixed_bytes())
5263}
5264
5265fn chain_data_key(index: ChainDataIndex) -> Vec<u8> {
5266 (index as u8).encode_to_vec()
5267}
5268
5269fn decode_block_number(bytes: Vec<u8>) -> Result<BlockNumber, StoreError> {
5271 let array: [u8; 8] = bytes
5272 .try_into()
5273 .map_err(|_| StoreError::Custom("Invalid BlockNumber bytes".to_string()))?;
5274 Ok(BlockNumber::from_le_bytes(array))
5275}
5276
5277fn snap_state_key(index: SnapStateIndex) -> Vec<u8> {
5278 (index as u8).encode_to_vec()
5279}
5280
5281pub fn receipt_key(block_hash: &BlockHash, index: u64) -> Vec<u8> {
5283 let mut key = Vec::with_capacity(40);
5284 key.extend_from_slice(block_hash.as_bytes());
5285 key.extend_from_slice(&index.to_be_bytes());
5286 key
5287}
5288
5289fn encode_code(code: &Code) -> Vec<u8> {
5290 let mut buf =
5291 Vec::with_capacity(6 + code.len() + std::mem::size_of_val::<[u32]>(&code.jump_targets));
5292 code.code().encode(&mut buf);
5293 code.jump_targets.to_vec().encode(&mut buf);
5296 buf
5297}
5298
5299#[derive(Debug, Default, Clone)]
5300struct LatestBlockHeaderCache {
5301 current: Arc<Mutex<Arc<BlockHeader>>>,
5302}
5303
5304impl LatestBlockHeaderCache {
5305 pub fn get(&self) -> Arc<BlockHeader> {
5306 self.current.lock().expect("poisoned mutex").clone()
5307 }
5308
5309 pub fn update(&self, header: BlockHeader) {
5310 let new = Arc::new(header);
5311 *self.current.lock().expect("poisoned mutex") = new;
5312 }
5313}
5314
5315#[derive(Debug, Serialize, Deserialize)]
5316pub struct StoreMetadata {
5317 pub schema_version: u64,
5318}
5319
5320impl StoreMetadata {
5321 pub fn new(schema_version: u64) -> Self {
5322 Self { schema_version }
5323 }
5324}
5325
5326fn read_store_schema_version(path: &Path) -> Result<Option<u64>, StoreError> {
5331 let metadata_path = path.join(STORE_METADATA_FILENAME);
5332 if !metadata_path.exists() {
5333 return Ok(None);
5334 }
5335 if !metadata_path.is_file() {
5336 return Err(StoreError::Custom(
5337 "store schema path exists but is not a file".to_string(),
5338 ));
5339 }
5340 let file_contents = std::fs::read_to_string(metadata_path)?;
5341 let metadata: StoreMetadata = serde_json::from_str(&file_contents)?;
5342 Ok(Some(metadata.schema_version))
5343}
5344
5345fn init_metadata_file(parent_path: &Path) -> Result<(), StoreError> {
5346 std::fs::create_dir_all(parent_path)?;
5347
5348 let metadata_path = parent_path.join(STORE_METADATA_FILENAME);
5349 let metadata = StoreMetadata::new(STORE_SCHEMA_VERSION);
5350 let serialized_metadata = serde_json::to_string_pretty(&metadata)?;
5351 let mut new_file = std::fs::File::create_new(metadata_path)?;
5352 new_file.write_all(serialized_metadata.as_bytes())?;
5353 Ok(())
5354}
5355
5356fn dir_contains_legacy_db(path: &Path) -> Result<bool, StoreError> {
5368 if path.join("CURRENT").is_file() {
5372 return Ok(true);
5373 }
5374 for entry in std::fs::read_dir(path)? {
5378 let entry = entry?;
5379 if !entry.file_type()?.is_file() {
5380 continue;
5381 }
5382 if entry.file_name().to_string_lossy().starts_with("MANIFEST-") {
5383 return Ok(true);
5384 }
5385 }
5386 Ok(false)
5387}
5388
5389pub fn has_valid_db(path: &Path) -> bool {
5393 let metadata_path = path.join(STORE_METADATA_FILENAME);
5394 if !metadata_path.is_file() {
5395 return false;
5396 }
5397 let Ok(contents) = std::fs::read_to_string(&metadata_path) else {
5398 return false;
5399 };
5400 let Ok(metadata) = serde_json::from_str::<StoreMetadata>(&contents) else {
5401 return false;
5402 };
5403 metadata.schema_version >= 1 && metadata.schema_version <= STORE_SCHEMA_VERSION
5404}
5405
5406pub fn read_chain_id_from_db(path: &Path) -> Option<u64> {
5415 if !has_valid_db(path) {
5416 return None;
5417 }
5418 #[cfg(feature = "rocksdb")]
5419 {
5420 let backend = match RocksDBBackend::open(path, DEFAULT_ROCKSDB_BLOCK_CACHE_SIZE_BYTES) {
5423 Ok(backend) => backend,
5424 Err(e) => {
5425 warn!("Failed to open RocksDB at {path:?} to read chain ID: {e}");
5426 return None;
5427 }
5428 };
5429 let read = match backend.begin_read() {
5430 Ok(read) => read,
5431 Err(e) => {
5432 warn!("Failed to begin read transaction at {path:?}: {e}");
5433 return None;
5434 }
5435 };
5436 let key = chain_data_key(ChainDataIndex::ChainConfig);
5437 let bytes = match read.get(CHAIN_DATA, &key) {
5438 Ok(Some(bytes)) => bytes,
5439 Ok(None) => {
5440 warn!("Chain config entry not found in database at {path:?}");
5441 return None;
5442 }
5443 Err(e) => {
5444 warn!("Failed to read chain config from database at {path:?}: {e}");
5445 return None;
5446 }
5447 };
5448 #[derive(serde::Deserialize)]
5453 #[serde(rename_all = "camelCase")]
5454 struct ChainIdOnly {
5455 chain_id: u64,
5456 }
5457 match serde_json::from_slice::<ChainIdOnly>(&bytes) {
5458 Ok(partial) => Some(partial.chain_id),
5459 Err(e) => {
5460 warn!("Failed to deserialize chain ID from database at {path:?}: {e}");
5461 None
5462 }
5463 }
5464 }
5465 #[cfg(not(feature = "rocksdb"))]
5466 {
5467 let _ = path;
5468 None
5469 }
5470}
5471
5472#[cfg(test)]
5473mod state_history_tests {
5474 use super::*;
5475 use crate::api::tables::STATE_HISTORY;
5476 use crate::backend::in_memory::InMemoryBackend;
5477 use crate::journal::JournalEntry;
5478 use ethrex_common::types::{BlockBody, BlockHeader};
5479 use ethrex_trie::Nibbles;
5480 use std::time::{Duration, Instant};
5481
5482 fn make_block(number: BlockNumber, parent_hash: H256, state_root: H256) -> Block {
5483 let header = BlockHeader {
5484 number,
5485 parent_hash,
5486 state_root,
5487 ..Default::default()
5488 };
5489 Block::new(header, BlockBody::default())
5490 }
5491
5492 fn await_journal_entry(
5496 backend: &Arc<dyn StorageBackend>,
5497 block_number: BlockNumber,
5498 timeout: Duration,
5499 ) -> Option<Vec<u8>> {
5500 let key = block_number.to_be_bytes();
5501 let deadline = Instant::now() + timeout;
5502 loop {
5503 let read = backend.begin_read().ok()?;
5504 if let Ok(Some(v)) = read.get(STATE_HISTORY, &key) {
5505 return Some(v);
5506 }
5507 if Instant::now() >= deadline {
5508 return None;
5509 }
5510 std::thread::sleep(Duration::from_millis(10));
5511 }
5512 }
5513
5514 fn assert_no_journal_entry(backend: &Arc<dyn StorageBackend>, block_number: BlockNumber) {
5519 let window = Duration::from_millis(500);
5520 let key = block_number.to_be_bytes();
5521 let deadline = Instant::now() + window;
5522 loop {
5523 let read = backend.begin_read().expect("read view");
5524 let v = read.get(STATE_HISTORY, &key).expect("get");
5525 assert!(
5526 v.is_none(),
5527 "expected no STATE_HISTORY entry for block {block_number}, got {v:?}"
5528 );
5529 if Instant::now() >= deadline {
5530 return;
5531 }
5532 std::thread::sleep(Duration::from_millis(20));
5533 }
5534 }
5535
5536 #[test]
5542 fn journal_entry_written_per_block_in_regular_mode() {
5543 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
5544 let dir = tempfile::tempdir().unwrap();
5545 let store = Store::from_backend(
5546 backend.clone(),
5547 dir.path().to_path_buf(),
5548 1,
5549 DEFAULT_PERSIST_CHANNEL_CAPACITY,
5550 )
5551 .unwrap();
5552
5553 let state_root_1 = H256::repeat_byte(0x11);
5554 let block1 = make_block(1, H256::zero(), state_root_1);
5555 let block1_hash = block1.hash();
5556 store
5557 .store_block_updates(UpdateBatch {
5558 account_updates: vec![(Nibbles::from_raw(&[0x00, 0x01], false), vec![0xab, 0xcd])],
5559 storage_updates: vec![],
5560 blocks: vec![block1],
5561 receipts: vec![],
5562 code_updates: vec![],
5563 commit_depth: None,
5564 wait_for_flush: false,
5565 })
5566 .unwrap();
5567
5568 store.set_safe_commit_root(state_root_1).unwrap();
5571 let state_root_2 = H256::repeat_byte(0x22);
5572 let block2 = make_block(2, block1_hash, state_root_2);
5573 let block2_hash = block2.hash();
5574 store
5575 .store_block_updates(UpdateBatch {
5576 account_updates: vec![(Nibbles::from_raw(&[0x00, 0x02], false), vec![0xef, 0x11])],
5577 storage_updates: vec![],
5578 blocks: vec![block2],
5579 receipts: vec![],
5580 code_updates: vec![],
5581 commit_depth: None,
5582 wait_for_flush: false,
5583 })
5584 .unwrap();
5585
5586 let bytes = await_journal_entry(&backend, 1, Duration::from_secs(2))
5587 .expect("STATE_HISTORY entry for block 1 should appear after block 2 commits it");
5588 let entry = JournalEntry::decode(&bytes).unwrap();
5589 assert_eq!(entry.block_hash, block1_hash);
5590 assert_eq!(entry.parent_state_root, H256::zero());
5591 assert!(!entry.account_trie_diff.is_empty());
5592 let (path, prev) = &entry.account_trie_diff[0];
5593 assert_eq!(prev, &None, "first-time write means previous value is None");
5594 assert!(path.len() < 65);
5595
5596 store.set_safe_commit_root(state_root_2).unwrap();
5599 let state_root_3 = H256::repeat_byte(0x33);
5600 let block3 = make_block(3, block2_hash, state_root_3);
5601 store
5602 .store_block_updates(UpdateBatch {
5603 account_updates: vec![(Nibbles::from_raw(&[0x00, 0x03], false), vec![0x77])],
5604 storage_updates: vec![],
5605 blocks: vec![block3],
5606 receipts: vec![],
5607 code_updates: vec![],
5608 commit_depth: None,
5609 wait_for_flush: false,
5610 })
5611 .unwrap();
5612
5613 let bytes = await_journal_entry(&backend, 2, Duration::from_secs(2))
5614 .expect("STATE_HISTORY entry for block 2 should appear after block 3 commits it");
5615 let entry = JournalEntry::decode(&bytes).unwrap();
5616 assert_eq!(entry.block_hash, block2_hash);
5617 assert_eq!(entry.parent_state_root, state_root_1);
5618 assert!(!entry.account_trie_diff.is_empty());
5619 }
5620
5621 #[test]
5631 fn depth_gated_per_block_commit_journals_with_correct_pre_image() {
5632 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
5633 let dir = tempfile::tempdir().unwrap();
5634 let store = Store::from_backend(
5635 backend.clone(),
5636 dir.path().to_path_buf(),
5637 1,
5638 DEFAULT_PERSIST_CHANNEL_CAPACITY,
5639 )
5640 .unwrap();
5641
5642 let shared = Nibbles::from_raw(&[0x0a, 0x0b], false);
5644 let mut prev_hash = H256::zero();
5645 let mut hashes = vec![H256::zero()];
5646 let mut roots = vec![H256::zero()];
5647 for n in 1..=6u64 {
5648 let state_root = H256::repeat_byte(0xc0 | (n as u8));
5649 let block = make_block(n, prev_hash, state_root);
5650 prev_hash = block.hash();
5651 hashes.push(prev_hash);
5652 roots.push(state_root);
5653 store
5654 .store_block_updates(UpdateBatch {
5655 account_updates: vec![(shared.clone(), vec![0xb0 | (n as u8)])],
5656 storage_updates: vec![],
5657 blocks: vec![block],
5658 receipts: vec![],
5659 code_updates: vec![],
5660 commit_depth: Some(3),
5661 wait_for_flush: false,
5662 })
5663 .unwrap();
5664 }
5665
5666 let bytes = await_journal_entry(&backend, 1, Duration::from_secs(2))
5667 .expect("a depth-gated per-block commit must journal");
5668 let entry = JournalEntry::decode(&bytes).unwrap();
5669 assert_eq!(
5670 entry.block_hash, hashes[1],
5671 "entry 1 carries block 1's identity"
5672 );
5673 assert_eq!(entry.parent_state_root, H256::zero());
5674 assert_eq!(
5675 entry.account_trie_diff[0].1, None,
5676 "block 1 first-writes the key, so its pre-image is absence"
5677 );
5678
5679 let bytes =
5680 await_journal_entry(&backend, 2, Duration::from_secs(2)).expect("entry for block 2");
5681 let entry = JournalEntry::decode(&bytes).unwrap();
5682 assert_eq!(
5683 entry.block_hash, hashes[2],
5684 "entry 2 carries block 2's identity"
5685 );
5686 assert_eq!(
5687 entry.parent_state_root, roots[1],
5688 "entry 2's parent_state_root is block 1's state root"
5689 );
5690 assert_eq!(
5691 entry.account_trie_diff[0].1,
5692 Some(vec![0xb0 | 1u8]),
5693 "block 2's pre-image is the value block 1 wrote"
5694 );
5695 }
5696
5697 #[test]
5706 fn journal_skipped_in_batch_mode() {
5707 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
5708 let dir = tempfile::tempdir().unwrap();
5709 let store = Store::from_backend(
5710 backend.clone(),
5711 dir.path().to_path_buf(),
5712 1,
5713 DEFAULT_PERSIST_CHANNEL_CAPACITY,
5714 )
5715 .unwrap();
5716
5717 let mut prev_hash = H256::zero();
5718 for n in 1..=5u64 {
5719 let state_root = H256::repeat_byte(0xa0 | (n as u8));
5720 let block = make_block(n, prev_hash, state_root);
5721 prev_hash = block.hash();
5722 store
5723 .store_block_updates(UpdateBatch {
5724 account_updates: vec![(
5725 Nibbles::from_raw(&[n as u8], false),
5726 vec![0xde, 0xad, n as u8],
5727 )],
5728 storage_updates: vec![],
5729 blocks: vec![block],
5730 receipts: vec![],
5731 code_updates: vec![],
5732 commit_depth: Some(BATCH_COMMIT_THRESHOLD),
5733 wait_for_flush: true,
5734 })
5735 .unwrap();
5736 }
5737
5738 for n in 1..=5u64 {
5739 assert_no_journal_entry(&backend, n);
5740 }
5741 }
5742
5743 #[test]
5746 fn journal_storage_updates_appear_in_storage_diff() {
5747 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
5748 let dir = tempfile::tempdir().unwrap();
5749 let store = Store::from_backend(
5750 backend.clone(),
5751 dir.path().to_path_buf(),
5752 1,
5753 DEFAULT_PERSIST_CHANNEL_CAPACITY,
5754 )
5755 .unwrap();
5756
5757 let account_hash_a = H256::repeat_byte(0xa0);
5758 let account_hash_b = H256::repeat_byte(0xb0);
5759
5760 let state_root_1 = H256::repeat_byte(0x33);
5761 let block1 = make_block(1, H256::zero(), state_root_1);
5762 let block1_hash = block1.hash();
5763 store
5764 .store_block_updates(UpdateBatch {
5765 account_updates: vec![],
5766 storage_updates: vec![
5767 (
5768 account_hash_a,
5769 vec![(Nibbles::from_raw(&[0x05], false), vec![0x01])],
5770 ),
5771 (
5772 account_hash_b,
5773 vec![(Nibbles::from_raw(&[0x06], false), vec![0x02])],
5774 ),
5775 ],
5776 blocks: vec![block1],
5777 receipts: vec![],
5778 code_updates: vec![],
5779 commit_depth: None,
5780 wait_for_flush: false,
5781 })
5782 .unwrap();
5783
5784 store.set_safe_commit_root(state_root_1).unwrap();
5787 let state_root_2 = H256::repeat_byte(0x44);
5788 let block2 = make_block(2, block1_hash, state_root_2);
5789 store
5790 .store_block_updates(UpdateBatch {
5791 account_updates: vec![(Nibbles::from_raw(&[0xee], false), vec![0xff])],
5792 storage_updates: vec![],
5793 blocks: vec![block2],
5794 receipts: vec![],
5795 code_updates: vec![],
5796 commit_depth: None,
5797 wait_for_flush: false,
5798 })
5799 .unwrap();
5800
5801 let bytes = await_journal_entry(&backend, 1, Duration::from_secs(2))
5802 .expect("STATE_HISTORY entry for block 1");
5803 let entry = JournalEntry::decode(&bytes).unwrap();
5804 assert_eq!(entry.block_hash, block1_hash);
5805 assert_eq!(
5806 entry.storage_trie_diff.len(),
5807 2,
5808 "two distinct account hashes must produce two storage_trie entries"
5809 );
5810 for (_key, prev) in &entry.storage_trie_diff {
5811 assert_eq!(prev, &None, "first-time storage write has None pre-image");
5812 }
5813 assert!(entry.account_trie_diff.is_empty());
5814 }
5815
5816 fn seed_journal_entries(backend: &Arc<dyn StorageBackend>, block_numbers: &[BlockNumber]) {
5817 let mut tx = backend.begin_write().unwrap();
5818 for n in block_numbers {
5819 let entry = JournalEntry {
5820 block_hash: H256::repeat_byte(*n as u8),
5821 parent_state_root: H256::zero(),
5822 account_trie_diff: vec![(vec![*n as u8], None)],
5823 storage_trie_diff: vec![],
5824 account_flat_diff: vec![],
5825 storage_flat_diff: vec![],
5826 };
5827 tx.put(STATE_HISTORY, &n.to_be_bytes(), &entry.encode())
5828 .unwrap();
5829 }
5830 tx.commit().unwrap();
5831 }
5832
5833 fn journal_entry_exists(backend: &Arc<dyn StorageBackend>, block_number: BlockNumber) -> bool {
5834 backend
5835 .begin_read()
5836 .unwrap()
5837 .get(STATE_HISTORY, &block_number.to_be_bytes())
5838 .unwrap()
5839 .is_some()
5840 }
5841
5842 #[tokio::test]
5845 async fn finality_advance_prunes_journal_below_boundary() {
5846 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
5847 let dir = tempfile::tempdir().unwrap();
5848 let store = Store::from_backend(
5849 backend.clone(),
5850 dir.path().to_path_buf(),
5851 1,
5852 DEFAULT_PERSIST_CHANNEL_CAPACITY,
5853 )
5854 .unwrap();
5855
5856 seed_journal_entries(&backend, &(1..=10).collect::<Vec<_>>());
5857 for n in 1..=10 {
5858 assert!(journal_entry_exists(&backend, n), "seed entry {n} present");
5859 }
5860
5861 store
5862 .forkchoice_update_inner(vec![], 100, H256::zero(), None, Some(5))
5863 .await
5864 .unwrap();
5865
5866 for n in 1..=5 {
5867 assert!(
5868 !journal_entry_exists(&backend, n),
5869 "entry {n} should have been pruned (<= finalized)"
5870 );
5871 }
5872 for n in 6..=10 {
5873 assert!(
5874 journal_entry_exists(&backend, n),
5875 "entry {n} should remain (> finalized)"
5876 );
5877 }
5878 }
5879
5880 #[tokio::test]
5882 async fn finality_no_op_does_not_prune() {
5883 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
5884 let dir = tempfile::tempdir().unwrap();
5885 let store = Store::from_backend(
5886 backend.clone(),
5887 dir.path().to_path_buf(),
5888 1,
5889 DEFAULT_PERSIST_CHANNEL_CAPACITY,
5890 )
5891 .unwrap();
5892
5893 store
5894 .forkchoice_update_inner(vec![], 100, H256::zero(), None, Some(5))
5895 .await
5896 .unwrap();
5897
5898 seed_journal_entries(&backend, &(6..=10).collect::<Vec<_>>());
5899
5900 store
5902 .forkchoice_update_inner(vec![], 100, H256::zero(), None, Some(5))
5903 .await
5904 .unwrap();
5905
5906 for n in 6..=10 {
5907 assert!(
5908 journal_entry_exists(&backend, n),
5909 "entry {n} should still exist after no-op finality update"
5910 );
5911 }
5912
5913 store
5915 .forkchoice_update_inner(vec![], 100, H256::zero(), None, None)
5916 .await
5917 .unwrap();
5918
5919 for n in 6..=10 {
5920 assert!(
5921 journal_entry_exists(&backend, n),
5922 "entry {n} should still exist when finalized is None"
5923 );
5924 }
5925 }
5926
5927 #[tokio::test]
5931 async fn malformed_finalized_returns_error_not_silent_zero() {
5932 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
5933 let dir = tempfile::tempdir().unwrap();
5934 let store = Store::from_backend(
5935 backend.clone(),
5936 dir.path().to_path_buf(),
5937 1,
5938 DEFAULT_PERSIST_CHANNEL_CAPACITY,
5939 )
5940 .unwrap();
5941
5942 let mut tx = backend.begin_write().unwrap();
5944 let finalized_key = chain_data_key(ChainDataIndex::FinalizedBlockNumber);
5945 tx.put(CHAIN_DATA, &finalized_key, &[0u8, 0, 0, 0]).unwrap();
5946 tx.commit().unwrap();
5947
5948 seed_journal_entries(&backend, &(1..=5).collect::<Vec<_>>());
5949
5950 let err = store
5951 .forkchoice_update_inner(vec![], 100, H256::zero(), None, Some(3))
5952 .await
5953 .expect_err("malformed finalized must surface as an error");
5954 let msg = format!("{err}");
5955 assert!(
5956 msg.contains("FinalizedBlockNumber has unexpected length"),
5957 "expected length-mismatch error, got: {msg}"
5958 );
5959
5960 for n in 1..=5 {
5962 assert!(
5963 journal_entry_exists(&backend, n),
5964 "entry {n} must NOT be pruned when finalized read failed"
5965 );
5966 }
5967 }
5968
5969 #[tokio::test]
5976 async fn highest_state_history_block_number_finds_max() {
5977 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
5978 let dir = tempfile::tempdir().unwrap();
5979 let store = Store::from_backend(
5980 backend.clone(),
5981 dir.path().to_path_buf(),
5982 1,
5983 DEFAULT_PERSIST_CHANNEL_CAPACITY,
5984 )
5985 .unwrap();
5986
5987 assert_eq!(store.highest_state_history_block_number().unwrap(), None);
5988
5989 seed_journal_entries(&backend, &[3, 7, 5, 11, 8]);
5990 assert_eq!(
5991 store.highest_state_history_block_number().unwrap(),
5992 Some(11),
5993 "max over present entries"
5994 );
5995 }
5996
5997 #[tokio::test]
6001 async fn lowest_state_history_block_number_finds_min() {
6002 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
6003 let dir = tempfile::tempdir().unwrap();
6004 let store = Store::from_backend(
6005 backend.clone(),
6006 dir.path().to_path_buf(),
6007 1,
6008 DEFAULT_PERSIST_CHANNEL_CAPACITY,
6009 )
6010 .unwrap();
6011
6012 assert_eq!(store.lowest_state_history_block_number().unwrap(), None);
6013
6014 seed_journal_entries(&backend, &[7, 3, 11, 5, 8]);
6015 assert_eq!(
6016 store.lowest_state_history_block_number().unwrap(),
6017 Some(3),
6018 "min over present entries"
6019 );
6020 }
6021
6022 #[tokio::test]
6026 async fn install_overlay_replaces_cache_with_fresh_one() {
6027 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
6028 let dir = tempfile::tempdir().unwrap();
6029 let store = Store::from_backend(
6030 backend.clone(),
6031 dir.path().to_path_buf(),
6032 4,
6033 DEFAULT_PERSIST_CHANNEL_CAPACITY,
6034 )
6035 .unwrap();
6036
6037 seed_journal_entries(&backend, &[3, 4]);
6038
6039 {
6041 let mut guard = store.trie_cache.write().unwrap();
6042 let mut updated = (**guard).clone();
6043 updated.put_batch(
6044 H256::zero(),
6045 H256::repeat_byte(0xff),
6046 42,
6047 H256::repeat_byte(0x42),
6048 vec![],
6049 );
6050 *guard = Arc::new(updated);
6051 }
6052 assert!(
6053 store
6054 .is_state_in_layer_cache(H256::repeat_byte(0xff))
6055 .unwrap()
6056 );
6057
6058 store
6059 .install_overlay_for_reorg(4, 3, |_| None)
6060 .expect("install must succeed");
6061
6062 assert!(
6063 !store
6064 .is_state_in_layer_cache(H256::repeat_byte(0xff))
6065 .unwrap(),
6066 "old cache layer must be gone after swap"
6067 );
6068 let cache = store.trie_cache.read().unwrap().clone();
6069 assert!(cache.overlay().is_some(), "overlay must be installed");
6070 assert_eq!(
6071 cache.commit_threshold(),
6072 4,
6073 "fresh cache must inherit threshold"
6074 );
6075 }
6076
6077 #[tokio::test]
6080 async fn install_overlay_failure_preserves_cache() {
6081 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
6082 let dir = tempfile::tempdir().unwrap();
6083 let store = Store::from_backend(
6084 backend.clone(),
6085 dir.path().to_path_buf(),
6086 4,
6087 DEFAULT_PERSIST_CHANNEL_CAPACITY,
6088 )
6089 .unwrap();
6090
6091 seed_journal_entries(&backend, &[5]);
6093 {
6094 let mut guard = store.trie_cache.write().unwrap();
6095 let mut updated = (**guard).clone();
6096 updated.put_batch(
6097 H256::zero(),
6098 H256::repeat_byte(0xaa),
6099 7,
6100 H256::repeat_byte(0x77),
6101 vec![],
6102 );
6103 *guard = Arc::new(updated);
6104 }
6105 assert!(
6106 store
6107 .is_state_in_layer_cache(H256::repeat_byte(0xaa))
6108 .unwrap()
6109 );
6110
6111 let err = store
6112 .install_overlay_for_reorg(5, 3, |_| None)
6113 .expect_err("missing entries 3 and 4 must abort");
6114 let msg = format!("{err}");
6115 assert!(
6116 msg.contains("overlay construction failed"),
6117 "error should explain construction failure: {msg}"
6118 );
6119
6120 assert!(
6122 store
6123 .is_state_in_layer_cache(H256::repeat_byte(0xaa))
6124 .unwrap(),
6125 "cache must survive a failed overlay install"
6126 );
6127 let cache = store.trie_cache.read().unwrap().clone();
6128 assert!(cache.overlay().is_none(), "no overlay should be installed");
6129 }
6130
6131 #[tokio::test]
6135 async fn abort_reorg_resets_cache_to_fresh() {
6136 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
6137 let dir = tempfile::tempdir().unwrap();
6138 let store = Store::from_backend(
6139 backend.clone(),
6140 dir.path().to_path_buf(),
6141 4,
6142 DEFAULT_PERSIST_CHANNEL_CAPACITY,
6143 )
6144 .unwrap();
6145
6146 seed_journal_entries(&backend, &[3, 4]);
6147 store.install_overlay_for_reorg(4, 3, |_| None).unwrap();
6148 {
6150 let mut guard = store.trie_cache.write().unwrap();
6151 let mut updated = (**guard).clone();
6152 updated.put_batch(
6153 H256::zero(),
6154 H256::repeat_byte(0xbb),
6155 4,
6156 H256::repeat_byte(0xb4),
6157 vec![],
6158 );
6159 *guard = Arc::new(updated);
6160 }
6161 assert!(
6162 store
6163 .is_state_in_layer_cache(H256::repeat_byte(0xbb))
6164 .unwrap()
6165 );
6166
6167 store.abort_reorg().unwrap();
6168
6169 assert!(
6170 !store
6171 .is_state_in_layer_cache(H256::repeat_byte(0xbb))
6172 .unwrap(),
6173 "partial new-chain layer must be discarded"
6174 );
6175 let cache = store.trie_cache.read().unwrap().clone();
6176 assert!(cache.overlay().is_none(), "overlay must be cleared");
6177 assert_eq!(
6178 cache.commit_threshold(),
6179 4,
6180 "fresh cache must inherit threshold"
6181 );
6182 }
6183
6184 #[tokio::test]
6187 async fn clear_reorg_overlay_removes_overlay_and_is_idempotent() {
6188 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
6189 let dir = tempfile::tempdir().unwrap();
6190 let store = Store::from_backend(
6191 backend.clone(),
6192 dir.path().to_path_buf(),
6193 4,
6194 DEFAULT_PERSIST_CHANNEL_CAPACITY,
6195 )
6196 .unwrap();
6197
6198 store.clear_reorg_overlay().unwrap();
6200 assert!(store.trie_cache.read().unwrap().overlay().is_none());
6201
6202 seed_journal_entries(&backend, &[7]);
6203 store.install_overlay_for_reorg(7, 7, |_| None).unwrap();
6204 assert!(store.trie_cache.read().unwrap().overlay().is_some());
6205
6206 store.clear_reorg_overlay().unwrap();
6207 assert!(store.trie_cache.read().unwrap().overlay().is_none());
6208
6209 store.clear_reorg_overlay().unwrap();
6211 assert!(store.trie_cache.read().unwrap().overlay().is_none());
6212 }
6213
6214 #[tokio::test]
6222 async fn deep_reorg_overlay_disables_flat_kv_fast_paths() {
6223 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
6224
6225 {
6229 let mut tx = backend.begin_write().unwrap();
6230 tx.put(MISC_VALUES, "last_written".as_bytes(), &[0xff])
6231 .unwrap();
6232 tx.commit().unwrap();
6233 }
6234
6235 let dir = tempfile::tempdir().unwrap();
6236 let store = Store::from_backend(
6237 backend.clone(),
6238 dir.path().to_path_buf(),
6239 1,
6240 DEFAULT_PERSIST_CHANNEL_CAPACITY,
6241 )
6242 .unwrap();
6243
6244 let address = Address::from_low_u64_be(0xbeef);
6249 let account_hash = hash_address_fixed(&address);
6250 let slot = H256::from_low_u64_be(1);
6251 let slot_hash = hash_key_fixed(&slot);
6252 let generator_account = AccountState {
6253 nonce: 7,
6254 balance: U256::from(999u64),
6255 storage_root: H256::zero(),
6256 code_hash: H256::zero(),
6257 };
6258 let generator_slot_value = U256::from(42u64);
6259 {
6260 let mut tx = backend.begin_write().unwrap();
6261 tx.put(
6262 ACCOUNT_FLATKEYVALUE,
6263 &Nibbles::from_bytes(account_hash.as_bytes()).into_vec(),
6264 &generator_account.encode_to_vec(),
6265 )
6266 .unwrap();
6267 tx.put(
6268 STORAGE_FLATKEYVALUE,
6269 &apply_prefix(Some(account_hash), Nibbles::from_bytes(&slot_hash)).into_vec(),
6270 &generator_slot_value.encode_to_vec(),
6271 )
6272 .unwrap();
6273 tx.commit().unwrap();
6274 }
6275
6276 let pivot_root = EMPTY_TRIE_HASH;
6281 {
6282 let mut tx = backend.begin_write().unwrap();
6283 for (n, parent_root) in [(10u64, pivot_root), (11, H256::repeat_byte(0x10))] {
6284 let entry = JournalEntry {
6285 block_hash: H256::repeat_byte(n as u8),
6286 parent_state_root: parent_root,
6287 account_trie_diff: vec![],
6288 storage_trie_diff: vec![],
6289 account_flat_diff: vec![],
6290 storage_flat_diff: vec![],
6291 };
6292 tx.put(STATE_HISTORY, &n.to_be_bytes(), &entry.encode())
6293 .unwrap();
6294 }
6295 tx.commit().unwrap();
6296 }
6297
6298 store
6299 .install_overlay_for_reorg(11, 10, |_| None)
6300 .expect("overlay install must succeed");
6301
6302 let accounts = store
6305 .get_account_states_batch_by_root(pivot_root, &[address])
6306 .unwrap();
6307 assert_eq!(
6308 accounts,
6309 vec![None],
6310 "batch account read must not serve the generator's stale flat-KV value"
6311 );
6312
6313 let slot_value = store
6314 .get_storage_at_root(pivot_root, address, slot)
6315 .unwrap();
6316 assert_eq!(
6317 slot_value, None,
6318 "storage read must not serve the generator's stale flat-KV value"
6319 );
6320
6321 let batched_slots = store
6322 .get_storage_values_batch_by_root(
6323 pivot_root,
6324 &[(hash_address_fixed(&address), EMPTY_TRIE_HASH, slot)],
6325 )
6326 .unwrap();
6327 assert_eq!(
6328 batched_slots,
6329 vec![None],
6330 "batch storage read must not serve the generator's stale flat-KV value"
6331 );
6332
6333 let state_trie = store.open_state_trie(pivot_root).unwrap();
6334 assert_eq!(
6335 state_trie.get(account_hash.as_bytes()).unwrap(),
6336 None,
6337 "trie read must not serve the generator's stale flat-KV value"
6338 );
6339
6340 let unserved_root = H256::repeat_byte(0x99);
6343 let accounts = store
6344 .get_account_states_batch_by_root(unserved_root, &[address])
6345 .unwrap();
6346 assert_eq!(
6347 accounts,
6348 vec![Some(generator_account)],
6349 "unserved roots must keep the flat-KV fast path"
6350 );
6351 let slot_value = store
6352 .get_storage_at_root(unserved_root, address, slot)
6353 .unwrap();
6354 assert_eq!(
6355 slot_value,
6356 Some(generator_slot_value),
6357 "unserved roots must keep the flat-KV fast path"
6358 );
6359
6360 let batched_slots = store
6361 .get_storage_values_batch_by_root(
6362 unserved_root,
6363 &[(hash_address_fixed(&address), EMPTY_TRIE_HASH, slot)],
6364 )
6365 .unwrap();
6366 assert_eq!(
6367 batched_slots,
6368 vec![Some(generator_slot_value)],
6369 "unserved roots must keep the flat-KV fast path"
6370 );
6371 }
6372
6373 #[tokio::test]
6381 async fn overlay_backed_commit_only_commits_bottom_layer_per_pass() {
6382 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
6383 let dir = tempfile::tempdir().unwrap();
6384 let store = Store::from_backend(
6385 backend.clone(),
6386 dir.path().to_path_buf(),
6387 4,
6388 DEFAULT_PERSIST_CHANNEL_CAPACITY,
6389 )
6390 .unwrap();
6391
6392 let pivot_root = H256::repeat_byte(0x09);
6395 {
6396 let mut tx = backend.begin_write().unwrap();
6397 for (n, parent_root) in [(10u64, pivot_root), (11, H256::repeat_byte(0x0a))] {
6398 let entry = JournalEntry {
6399 block_hash: H256::repeat_byte(n as u8),
6400 parent_state_root: parent_root,
6401 account_trie_diff: vec![(vec![0x00, n as u8], None)],
6402 storage_trie_diff: vec![],
6403 account_flat_diff: vec![],
6404 storage_flat_diff: vec![],
6405 };
6406 tx.put(STATE_HISTORY, &n.to_be_bytes(), &entry.encode())
6407 .unwrap();
6408 }
6409 tx.commit().unwrap();
6410 }
6411 store.install_overlay_for_reorg(11, 10, |_| None).unwrap();
6412
6413 let roots: Vec<H256> = (10..=12u8).map(H256::repeat_byte).collect();
6415 {
6416 let mut guard = store.trie_cache.write().unwrap();
6417 let mut updated = (**guard).clone();
6418 let mut parent = pivot_root;
6419 for (i, root) in roots.iter().enumerate() {
6420 let n = 10 + i as u64;
6421 updated.put_batch(
6422 parent,
6423 *root,
6424 n,
6425 H256::repeat_byte(n as u8),
6426 vec![(Nibbles::from_raw(&[0x01, n as u8], false), vec![n as u8])],
6427 );
6428 parent = *root;
6429 }
6430 *guard = Arc::new(updated);
6431 }
6432
6433 let trie = store.trie_cache.read().unwrap().clone();
6436 commit_to_disk(
6437 store.backend.as_ref(),
6438 &store.flatkeyvalue_control_tx,
6439 &store.trie_cache,
6440 &trie,
6441 roots[2],
6442 false,
6443 )
6444 .expect("bottom-layer commit must succeed (no debug_assert panic)");
6445
6446 assert!(
6448 !store.is_state_in_layer_cache(roots[0]).unwrap(),
6449 "bottom layer must be committed and pruned"
6450 );
6451 assert!(
6452 store.is_state_in_layer_cache(roots[1]).unwrap()
6453 && store.is_state_in_layer_cache(roots[2]).unwrap(),
6454 "upper layers must remain resident after the bottom-layer-only commit"
6455 );
6456 assert!(
6458 store.trie_cache.read().unwrap().overlay().is_none(),
6459 "reconciliation must clear the overlay"
6460 );
6461 assert!(
6463 journal_entry_exists(&backend, 10),
6464 "bottom layer T must be journaled"
6465 );
6466 assert!(!journal_entry_exists(&backend, 11));
6467 assert!(!journal_entry_exists(&backend, 12));
6468
6469 let entry_bytes = backend
6474 .begin_read()
6475 .unwrap()
6476 .get(STATE_HISTORY, &10u64.to_be_bytes())
6477 .unwrap()
6478 .expect("journal entry for block 10");
6479 let entry = JournalEntry::decode(&entry_bytes).unwrap();
6480 let diff_keys: Vec<&Vec<u8>> = entry.account_trie_diff.iter().map(|(k, _)| k).collect();
6481 assert!(
6482 diff_keys.contains(&&vec![0x00, 10]) && diff_keys.contains(&&vec![0x00, 11]),
6483 "bridge keys from the overlay must be folded into T's journal entry, got {diff_keys:?}"
6484 );
6485
6486 let trie = store.trie_cache.read().unwrap().clone();
6488 commit_to_disk(
6489 store.backend.as_ref(),
6490 &store.flatkeyvalue_control_tx,
6491 &store.trie_cache,
6492 &trie,
6493 roots[2],
6494 false,
6495 )
6496 .unwrap();
6497 assert!(!store.is_state_in_layer_cache(roots[1]).unwrap());
6498 assert!(!store.is_state_in_layer_cache(roots[2]).unwrap());
6499 assert!(journal_entry_exists(&backend, 11));
6500 assert!(journal_entry_exists(&backend, 12));
6501 }
6502
6503 #[tokio::test]
6508 async fn journal_pruning_pauses_during_reorg_and_catches_up_after() {
6509 let backend: Arc<dyn StorageBackend> = Arc::new(InMemoryBackend::open().unwrap());
6510 let dir = tempfile::tempdir().unwrap();
6511 let store = Store::from_backend(
6512 backend.clone(),
6513 dir.path().to_path_buf(),
6514 1,
6515 DEFAULT_PERSIST_CHANNEL_CAPACITY,
6516 )
6517 .unwrap();
6518
6519 seed_journal_entries(&backend, &(1..=5).collect::<Vec<_>>());
6520
6521 store.set_journal_pruning_paused(true);
6523 store
6524 .forkchoice_update_inner(vec![], 100, H256::zero(), None, Some(3))
6525 .await
6526 .unwrap();
6527 for n in 1..=5 {
6528 assert!(
6529 journal_entry_exists(&backend, n),
6530 "entry {n} must survive pruning while the reorg pause is held"
6531 );
6532 }
6533
6534 store.set_journal_pruning_paused(false);
6536 store
6537 .forkchoice_update_inner(vec![], 100, H256::zero(), None, Some(4))
6538 .await
6539 .unwrap();
6540 for n in 1..=4 {
6541 assert!(
6542 !journal_entry_exists(&backend, n),
6543 "entry {n} must be pruned once the pause is released"
6544 );
6545 }
6546 assert!(journal_entry_exists(&backend, 5));
6547 }
6548}
6549
6550#[cfg(test)]
6551mod merge_tests {
6552 use super::*;
6553
6554 fn h256(b: u8) -> H256 {
6555 H256::from_low_u64_be(b as u64)
6556 }
6557
6558 fn op(bn: BlockNumber, bh: H256, idx: Index) -> Vec<u8> {
6559 encode_tx_location_operand(bn, bh, idx)
6560 }
6561
6562 fn decode(v: &[u8]) -> Vec<(BlockNumber, BlockHash, Index)> {
6563 <Vec<(BlockNumber, BlockHash, Index)>>::decode(v).unwrap()
6564 }
6565
6566 #[test]
6567 fn single_operand_on_empty_base() {
6568 let out = tx_locations_merge(None, vec![op(100, h256(0x10), 0)]).unwrap();
6569 assert_eq!(decode(&out), vec![(100, h256(0x10), 0)]);
6570 }
6571
6572 #[test]
6573 fn operand_appended_to_existing_base() {
6574 let base = vec![(100u64, h256(0x10), 0u64)].encode_to_vec();
6575 let out = tx_locations_merge(Some(&base), vec![op(101, h256(0x11), 5)]).unwrap();
6576 let mut got = decode(&out);
6577 got.sort();
6578 let mut want = vec![(100, h256(0x10), 0), (101, h256(0x11), 5)];
6579 want.sort();
6580 assert_eq!(got, want);
6581 }
6582
6583 #[test]
6584 fn multiple_operands_combined() {
6585 let out = tx_locations_merge(
6586 None,
6587 vec![
6588 op(100, h256(0x10), 0),
6589 op(100, h256(0x11), 1),
6590 op(101, h256(0x12), 2),
6591 ],
6592 )
6593 .unwrap();
6594 assert_eq!(decode(&out).len(), 3);
6595 }
6596
6597 #[test]
6598 fn same_block_hash_is_deduped() {
6599 let out =
6601 tx_locations_merge(None, vec![op(100, h256(0x10), 0), op(100, h256(0x10), 7)]).unwrap();
6602 assert_eq!(decode(&out), vec![(100, h256(0x10), 7)]);
6603 }
6604
6605 #[test]
6606 fn malformed_operand_aborts_merge() {
6607 let out = tx_locations_merge(None, vec![vec![0xff, 0xff], op(100, h256(0x10), 0)]);
6610 assert!(out.is_none(), "merge must abort on a malformed operand");
6611 }
6612
6613 #[test]
6614 fn malformed_base_value_aborts_merge() {
6615 let out = tx_locations_merge(Some(&[0xff, 0xff]), vec![op(100, h256(0x10), 0)]);
6616 assert!(out.is_none(), "merge must abort on a corrupt base value");
6617 }
6618
6619 #[test]
6626 fn partial_merge_result_is_a_valid_operand() {
6627 let partial =
6629 tx_locations_merge(None, vec![op(100, h256(0x10), 0), op(101, h256(0x11), 1)]).unwrap();
6630
6631 let base = vec![(99u64, h256(0x09), 9u64)].encode_to_vec();
6635 let out = tx_locations_merge(Some(&base), vec![partial]).unwrap();
6636
6637 let mut got = decode(&out);
6638 got.sort();
6639 let mut want = vec![
6640 (99, h256(0x09), 9),
6641 (100, h256(0x10), 0),
6642 (101, h256(0x11), 1),
6643 ];
6644 want.sort();
6645 assert_eq!(
6646 got, want,
6647 "no entries may be lost when re-merging a partial result"
6648 );
6649 }
6650
6651 #[test]
6654 fn operand_encoding_matches_value_encoding() {
6655 let operand = op(100, h256(0x10), 3);
6656 assert_eq!(decode(&operand), vec![(100, h256(0x10), 3)]);
6658 }
6659
6660 #[test]
6662 fn chained_partial_merges() {
6663 let p1 = tx_locations_merge(None, vec![op(1, h256(0x01), 0)]).unwrap();
6664 let p2 = tx_locations_merge(None, vec![p1, op(2, h256(0x02), 0)]).unwrap();
6665 let p3 = tx_locations_merge(None, vec![p2, op(3, h256(0x03), 0)]).unwrap();
6666 let out = tx_locations_merge(None, vec![p3]).unwrap();
6667 assert_eq!(decode(&out).len(), 3);
6668 }
6669}
6670
6671#[cfg(test)]
6672mod backfill_write_tests {
6673 use super::*;
6674 use ethrex_common::types::{EIP1559Transaction, TxType};
6675
6676 fn test_header(number: BlockNumber) -> BlockHeader {
6677 BlockHeader {
6678 number,
6679 ..Default::default()
6680 }
6681 }
6682
6683 fn test_tx(nonce: u64) -> Transaction {
6684 Transaction::EIP1559Transaction(EIP1559Transaction {
6685 nonce,
6686 ..Default::default()
6687 })
6688 }
6689
6690 fn test_receipt() -> Receipt {
6691 Receipt {
6692 tx_type: TxType::EIP1559,
6693 succeeded: true,
6694 cumulative_gas_used: 21_000,
6695 logs: vec![],
6696 payer: None,
6697 frame_receipts: None,
6698 }
6699 }
6700
6701 #[tokio::test]
6705 async fn writes_data_indexes_txs_and_lowers_frontier() {
6706 let store = Store::new("", EngineType::InMemory).expect("in-memory store");
6707 store.update_earliest_block_number(100).await.unwrap();
6708
6709 let header = test_header(99);
6710 let hash = header.hash();
6711 let tx = test_tx(7);
6712 let body = BlockBody {
6713 transactions: vec![tx.clone()],
6714 ommers: vec![],
6715 withdrawals: Some(vec![]),
6716 };
6717 store.add_block_headers(vec![header.clone()]).await.unwrap();
6721 store
6722 .forkchoice_update(vec![(99, hash)], 99, hash, None, None)
6723 .await
6724 .unwrap();
6725
6726 store
6727 .add_backfilled_blocks(
6728 vec![BackfilledBlock {
6729 header,
6730 body,
6731 receipts: vec![test_receipt()],
6732 index_transactions: true,
6733 }],
6734 99,
6735 )
6736 .await
6737 .unwrap();
6738
6739 assert_eq!(
6740 store.get_earliest_block_number().await.unwrap(),
6741 99,
6742 "frontier must be lowered atomically with the data write"
6743 );
6744 assert!(
6745 store.get_block_body_by_hash(hash).await.unwrap().is_some(),
6746 "body must be stored"
6747 );
6748 assert_eq!(
6749 store.get_receipts_for_block(&hash).await.unwrap().len(),
6750 1,
6751 "receipts must be stored"
6752 );
6753 assert_eq!(
6754 store
6755 .get_transaction_location(tx.hash(&NativeCrypto))
6756 .await
6757 .unwrap(),
6758 Some((99, hash, 0)),
6759 "indexed transaction must be locatable"
6760 );
6761 }
6762
6763 #[tokio::test]
6767 async fn skips_tx_index_when_disabled() {
6768 let store = Store::new("", EngineType::InMemory).expect("in-memory store");
6769 store.update_earliest_block_number(100).await.unwrap();
6770
6771 let header = test_header(99);
6772 let hash = header.hash();
6773 let tx = test_tx(7);
6774 let body = BlockBody {
6775 transactions: vec![tx.clone()],
6776 ommers: vec![],
6777 withdrawals: Some(vec![]),
6778 };
6779 store.add_block_headers(vec![header.clone()]).await.unwrap();
6780 store
6781 .forkchoice_update(vec![(99, hash)], 99, hash, None, None)
6782 .await
6783 .unwrap();
6784
6785 store
6786 .add_backfilled_blocks(
6787 vec![BackfilledBlock {
6788 header,
6789 body,
6790 receipts: vec![test_receipt()],
6791 index_transactions: false,
6792 }],
6793 99,
6794 )
6795 .await
6796 .unwrap();
6797
6798 assert!(
6799 store.get_block_body_by_hash(hash).await.unwrap().is_some(),
6800 "body must still be stored"
6801 );
6802 assert_eq!(
6803 store.get_receipts_for_block(&hash).await.unwrap().len(),
6804 1,
6805 "receipts must still be stored"
6806 );
6807 assert_eq!(
6808 store
6809 .get_transaction_location(tx.hash(&NativeCrypto))
6810 .await
6811 .unwrap(),
6812 None,
6813 "transaction index must be skipped when index_transactions is false"
6814 );
6815 }
6816
6817 #[tokio::test]
6821 async fn historical_getters_transition_from_null_to_present() {
6822 let store = Store::new("", EngineType::InMemory).expect("in-memory store");
6823 store.update_earliest_block_number(100).await.unwrap();
6824
6825 let header = test_header(99);
6826 let hash = header.hash();
6827 let tx = test_tx(7);
6828 let tx_hash = tx.hash(&NativeCrypto);
6829 let body = BlockBody {
6830 transactions: vec![tx],
6831 ommers: vec![],
6832 withdrawals: Some(vec![]),
6833 };
6834 store.add_block_headers(vec![header.clone()]).await.unwrap();
6836 store
6837 .forkchoice_update(vec![(99, hash)], 99, hash, None, None)
6838 .await
6839 .unwrap();
6840
6841 assert!(
6843 store.get_block_body(99).await.unwrap().is_none(),
6844 "body absent before backfill"
6845 );
6846 assert!(
6847 store
6848 .get_transaction_by_hash(tx_hash)
6849 .await
6850 .unwrap()
6851 .is_none(),
6852 "tx unlocatable before backfill"
6853 );
6854 assert!(
6855 store.get_receipt(99, 0).await.unwrap().is_none(),
6856 "receipt absent before backfill"
6857 );
6858
6859 store
6860 .add_backfilled_blocks(
6861 vec![BackfilledBlock {
6862 header,
6863 body,
6864 receipts: vec![test_receipt()],
6865 index_transactions: true,
6866 }],
6867 99,
6868 )
6869 .await
6870 .unwrap();
6871
6872 assert!(
6874 store.get_block_body(99).await.unwrap().is_some(),
6875 "body present after backfill"
6876 );
6877 assert!(
6878 store
6879 .get_transaction_by_hash(tx_hash)
6880 .await
6881 .unwrap()
6882 .is_some(),
6883 "tx locatable after backfill"
6884 );
6885 assert!(
6886 store.get_receipt(99, 0).await.unwrap().is_some(),
6887 "receipt present after backfill"
6888 );
6889 }
6890
6891 #[tokio::test]
6894 async fn resume_fills_contiguously_without_gaps() {
6895 let store = Store::new("", EngineType::InMemory).expect("in-memory store");
6896 store.update_earliest_block_number(100).await.unwrap();
6897
6898 let make_batch = |range: std::ops::Range<u64>| -> Vec<BackfilledBlock> {
6899 range
6900 .rev()
6901 .map(|n| BackfilledBlock {
6902 header: test_header(n),
6903 body: BlockBody {
6904 transactions: vec![],
6905 ommers: vec![],
6906 withdrawals: Some(vec![]),
6907 },
6908 receipts: vec![],
6909 index_transactions: false,
6910 })
6911 .collect()
6912 };
6913
6914 store
6916 .add_backfilled_blocks(make_batch(90..100), 90)
6917 .await
6918 .unwrap();
6919 assert_eq!(store.get_earliest_block_number().await.unwrap(), 90);
6920
6921 let resume_from = store.get_earliest_block_number().await.unwrap();
6923 assert_eq!(resume_from, 90, "resume reads the persisted frontier");
6924
6925 store
6927 .add_backfilled_blocks(make_batch(80..90), 80)
6928 .await
6929 .unwrap();
6930 assert_eq!(store.get_earliest_block_number().await.unwrap(), 80);
6931
6932 for n in 80..100 {
6934 assert!(
6935 store
6936 .get_block_body_by_hash(test_header(n).hash())
6937 .await
6938 .unwrap()
6939 .is_some(),
6940 "block {n} must have a body (no gap left by resume)"
6941 );
6942 }
6943 }
6944}
6945
6946#[cfg(test)]
6947mod datadir_tests {
6948 use super::*;
6949 use std::fs;
6950
6951 #[test]
6952 fn empty_dir_has_no_existing_db() {
6953 let dir = tempfile::tempdir().unwrap();
6954 assert!(!dir_contains_legacy_db(dir.path()).unwrap());
6955 }
6956
6957 #[test]
6958 fn dir_with_only_unrelated_files_has_no_existing_db() {
6959 let dir = tempfile::tempdir().unwrap();
6962 fs::write(dir.path().join("jwt.hex"), "0xdeadbeef").unwrap();
6963 fs::write(dir.path().join("LOG"), "noise").unwrap();
6964 assert!(!dir_contains_legacy_db(dir.path()).unwrap());
6965 }
6966
6967 #[test]
6968 fn dir_with_rocksdb_markers_has_existing_db() {
6969 let dir = tempfile::tempdir().unwrap();
6971 fs::write(dir.path().join("CURRENT"), "MANIFEST-000001\n").unwrap();
6972 assert!(dir_contains_legacy_db(dir.path()).unwrap());
6973
6974 let dir2 = tempfile::tempdir().unwrap();
6975 fs::write(dir2.path().join("MANIFEST-000007"), "x").unwrap();
6976 assert!(dir_contains_legacy_db(dir2.path()).unwrap());
6977 }
6978
6979 #[test]
6980 fn dir_with_marker_named_subdirectories_has_no_existing_db() {
6981 let dir = tempfile::tempdir().unwrap();
6984 fs::create_dir(dir.path().join("CURRENT")).unwrap();
6985 fs::create_dir(dir.path().join("MANIFEST-000001")).unwrap();
6986 assert!(!dir_contains_legacy_db(dir.path()).unwrap());
6987 }
6988}
6989
6990#[cfg(test)]
6991mod flatkeyvalue_completeness_tests {
6992 use super::*;
6993
6994 #[test]
6999 fn only_the_one_byte_ff_sentinel_counts_as_complete() {
7000 assert!(Store::flatkeyvalue_generation_complete(Some(&[0xff])));
7002
7003 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])));
7011 assert!(!Store::flatkeyvalue_generation_complete(Some(&[
7012 0xff, 0xff
7013 ])));
7014 }
7015}