Skip to main content

signet_hot/model/
revm.rs

1use crate::{
2    db::HistoryRead,
3    model::{HotKvError, HotKvRead, HotKvWrite},
4    tables::{self, Bytecodes, DualKey, PlainAccountState, SingleKey, Table},
5};
6use alloy::primitives::{Address, B256, KECCAK256_EMPTY};
7use core::fmt;
8use signet_storage_types::Account;
9use std::borrow::Cow;
10use trevm::revm::{
11    database::{DBErrorMarker, Database, DatabaseRef, TryDatabaseCommit},
12    primitives::{HashMap, StorageKey, StorageValue},
13    state::{self, AccountInfo, Bytecode as RevmBytecode},
14};
15
16// Error marker implementation
17impl DBErrorMarker for HotKvError {}
18
19/// Read-only [`Database`] and [`DatabaseRef`] adapter.
20///
21/// When `height` is `Some`, reads return state as it was at that block
22/// height by consulting history and change set tables. When `None`,
23/// reads use the current plain state tables.
24pub struct RevmRead<T: HotKvRead> {
25    reader: T,
26    height: Option<u64>,
27}
28
29impl<T: HotKvRead> RevmRead<T> {
30    /// Create a new read adapter that reads current state.
31    pub const fn new(reader: T) -> Self {
32        Self { reader, height: None }
33    }
34
35    /// Create a read adapter that reads state at a specific block height.
36    ///
37    /// **Note:** This constructor does **not** validate `height` against the
38    /// stored block range. Heights past the chain tip silently return
39    /// current state, and heights before the first block return the
40    /// pre-state of the earliest change. Use
41    /// [`HotKv::revm_reader_at_height`] which validates, or call
42    /// [`HistoryRead::check_height`] manually.
43    ///
44    /// [`HotKv::revm_reader_at_height`]: crate::model::HotKv::revm_reader_at_height
45    /// [`HistoryRead::check_height`]: crate::db::HistoryRead::check_height
46    pub const fn at_height(reader: T, height: u64) -> Self {
47        Self { reader, height: Some(height) }
48    }
49}
50
51impl<T: HotKvRead> fmt::Debug for RevmRead<T> {
52    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53        f.debug_struct("RevmRead").finish()
54    }
55}
56
57// HotKvRead implementation for RevmRead
58impl<U: HotKvRead> HotKvRead for RevmRead<U> {
59    type Error = U::Error;
60
61    type Traverse<'a>
62        = U::Traverse<'a>
63    where
64        U: 'a;
65
66    fn raw_traverse<'a>(&'a self, table: &'static str) -> Result<Self::Traverse<'a>, Self::Error> {
67        self.reader.raw_traverse(table)
68    }
69
70    fn raw_get<'a>(
71        &'a self,
72        table: &'static str,
73        key: &[u8],
74    ) -> Result<Option<std::borrow::Cow<'a, [u8]>>, Self::Error> {
75        self.reader.raw_get(table, key)
76    }
77
78    fn raw_get_dual<'a>(
79        &'a self,
80        table: &'static str,
81        key1: &[u8],
82        key2: &[u8],
83    ) -> Result<Option<Cow<'a, [u8]>>, Self::Error> {
84        self.reader.raw_get_dual(table, key1, key2)
85    }
86
87    fn get<T: SingleKey>(&self, key: &T::Key) -> Result<Option<T::Value>, Self::Error> {
88        self.reader.get::<T>(key)
89    }
90
91    fn get_dual<T: DualKey>(
92        &self,
93        key1: &T::Key,
94        key2: &T::Key2,
95    ) -> Result<Option<T::Value>, Self::Error> {
96        self.reader.get_dual::<T>(key1, key2)
97    }
98}
99
100/// Read-write REVM database adapter. This adapter allows committing changes.
101/// Despite the naming of [`TryDatabaseCommit::try_commit`], the changes are
102/// only persisted when [`Self::persist`] is called. This is because of a
103/// mismatch in semantics between the two systems.
104pub struct RevmWrite<U: HotKvWrite> {
105    writer: U,
106}
107
108impl<U: HotKvWrite> RevmWrite<U> {
109    /// Create a new write adapter
110    pub const fn new(writer: U) -> Self {
111        Self { writer }
112    }
113
114    /// Persist the changes made in this write transaction.
115    pub fn persist(self) -> Result<(), U::Error> {
116        self.writer.raw_commit()
117    }
118}
119
120impl<U: HotKvWrite> fmt::Debug for RevmWrite<U> {
121    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122        f.debug_struct("RevmWrite").finish()
123    }
124}
125
126// HotKvWrite implementation for RevmWrite
127impl<U: HotKvWrite> HotKvRead for RevmWrite<U> {
128    type Error = U::Error;
129
130    type Traverse<'a>
131        = U::Traverse<'a>
132    where
133        U: 'a;
134
135    fn raw_traverse<'a>(&'a self, table: &'static str) -> Result<Self::Traverse<'a>, Self::Error> {
136        self.writer.raw_traverse(table)
137    }
138
139    fn raw_get<'a>(
140        &'a self,
141        table: &'static str,
142        key: &[u8],
143    ) -> Result<Option<std::borrow::Cow<'a, [u8]>>, Self::Error> {
144        self.writer.raw_get(table, key)
145    }
146
147    fn raw_get_dual<'a>(
148        &'a self,
149        table: &'static str,
150        key1: &[u8],
151        key2: &[u8],
152    ) -> Result<Option<Cow<'a, [u8]>>, Self::Error> {
153        self.writer.raw_get_dual(table, key1, key2)
154    }
155
156    fn get<T: SingleKey>(&self, key: &T::Key) -> Result<Option<T::Value>, Self::Error> {
157        self.writer.get::<T>(key)
158    }
159
160    fn get_dual<T: DualKey>(
161        &self,
162        key1: &T::Key,
163        key2: &T::Key2,
164    ) -> Result<Option<T::Value>, Self::Error> {
165        self.writer.get_dual::<T>(key1, key2)
166    }
167}
168
169impl<U: HotKvWrite> HotKvWrite for RevmWrite<U> {
170    type TraverseMut<'a>
171        = U::TraverseMut<'a>
172    where
173        U: 'a;
174
175    fn raw_traverse_mut<'a>(
176        &'a self,
177        table: &'static str,
178    ) -> Result<Self::TraverseMut<'a>, Self::Error> {
179        self.writer.raw_traverse_mut(table)
180    }
181
182    fn queue_raw_put(
183        &self,
184        table: &'static str,
185        key: &[u8],
186        value: &[u8],
187    ) -> Result<(), Self::Error> {
188        self.writer.queue_raw_put(table, key, value)
189    }
190
191    fn queue_raw_put_dual(
192        &self,
193        table: &'static str,
194        key1: &[u8],
195        key2: &[u8],
196        value: &[u8],
197    ) -> Result<(), Self::Error> {
198        self.writer.queue_raw_put_dual(table, key1, key2, value)
199    }
200
201    fn queue_raw_delete(&self, table: &'static str, key: &[u8]) -> Result<(), Self::Error> {
202        self.writer.queue_raw_delete(table, key)
203    }
204
205    fn queue_raw_delete_dual(
206        &self,
207        table: &'static str,
208        key1: &[u8],
209        key2: &[u8],
210    ) -> Result<(), Self::Error> {
211        self.writer.queue_raw_delete_dual(table, key1, key2)
212    }
213
214    fn queue_raw_clear(&self, table: &'static str) -> Result<(), Self::Error> {
215        self.writer.queue_raw_clear(table)
216    }
217
218    fn queue_raw_create(
219        &self,
220        table: &'static str,
221        dual_key: Option<usize>,
222        dual_fixed: Option<usize>,
223    ) -> Result<(), Self::Error> {
224        self.writer.queue_raw_create(table, dual_key, dual_fixed)
225    }
226
227    fn raw_commit(self) -> Result<(), Self::Error> {
228        self.writer.raw_commit()
229    }
230
231    fn queue_put<T: SingleKey>(&self, key: &T::Key, value: &T::Value) -> Result<(), Self::Error> {
232        self.writer.queue_put::<T>(key, value)
233    }
234
235    fn queue_put_dual<T: DualKey>(
236        &self,
237        key1: &T::Key,
238        key2: &T::Key2,
239        value: &T::Value,
240    ) -> Result<(), Self::Error> {
241        self.writer.queue_put_dual::<T>(key1, key2, value)
242    }
243
244    fn queue_delete<T: SingleKey>(&self, key: &T::Key) -> Result<(), Self::Error> {
245        self.writer.queue_delete::<T>(key)
246    }
247
248    fn queue_create<T>(&self) -> Result<(), Self::Error>
249    where
250        T: Table,
251    {
252        self.writer.queue_create::<T>()
253    }
254
255    fn queue_clear<T>(&self) -> Result<(), Self::Error>
256    where
257        T: Table,
258    {
259        self.writer.queue_clear::<T>()
260    }
261}
262
263// DatabaseRef implementation for RevmRead
264impl<T: HotKvRead> DatabaseRef for RevmRead<T>
265where
266    T::Error: DBErrorMarker,
267{
268    type Error = T::Error;
269
270    fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
271        let Some(account) = self.reader.get_account_at_height(&address, self.height)? else {
272            return Ok(None);
273        };
274
275        let code_hash = account.bytecode_hash.unwrap_or(KECCAK256_EMPTY);
276        // Bytecodes are content-addressed (immutable), no height awareness needed
277        let code = if code_hash != KECCAK256_EMPTY {
278            self.reader.get::<Bytecodes>(&code_hash)?
279        } else {
280            None
281        };
282
283        Ok(Some(AccountInfo {
284            balance: account.balance,
285            nonce: account.nonce,
286            code_hash,
287            code,
288            account_id: None,
289        }))
290    }
291
292    fn code_by_hash_ref(&self, code_hash: B256) -> Result<RevmBytecode, Self::Error> {
293        Ok(self.reader.get::<Bytecodes>(&code_hash)?.unwrap_or_default())
294    }
295
296    fn storage_ref(
297        &self,
298        address: Address,
299        index: StorageKey,
300    ) -> Result<StorageValue, Self::Error> {
301        Ok(self.reader.get_storage_at_height(&address, &index, self.height)?.unwrap_or_default())
302    }
303
304    fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
305        Ok(self.reader.get::<tables::Headers>(&number)?.map_or(B256::ZERO, |h| h.hash()))
306    }
307}
308
309// Database implementation for RevmRead
310impl<T: HotKvRead> Database for RevmRead<T>
311where
312    T::Error: DBErrorMarker,
313{
314    type Error = T::Error;
315
316    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
317        self.basic_ref(address)
318    }
319
320    fn code_by_hash(&mut self, code_hash: B256) -> Result<RevmBytecode, Self::Error> {
321        self.code_by_hash_ref(code_hash)
322    }
323
324    fn storage(
325        &mut self,
326        address: Address,
327        index: StorageKey,
328    ) -> Result<StorageValue, Self::Error> {
329        self.storage_ref(address, index)
330    }
331
332    fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
333        self.block_hash_ref(number)
334    }
335}
336
337// DatabaseRef implementation for RevmWrite (delegates to read operations)
338impl<T: HotKvWrite> DatabaseRef for RevmWrite<T>
339where
340    T::Error: DBErrorMarker,
341{
342    type Error = T::Error;
343
344    fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
345        let account_opt = self.writer.get::<PlainAccountState>(&address)?;
346
347        let Some(account) = account_opt else {
348            return Ok(None);
349        };
350
351        let code_hash = account.bytecode_hash.unwrap_or(KECCAK256_EMPTY);
352        let code = if code_hash != KECCAK256_EMPTY {
353            self.writer.get::<Bytecodes>(&code_hash)?
354        } else {
355            None
356        };
357
358        Ok(Some(AccountInfo {
359            balance: account.balance,
360            nonce: account.nonce,
361            code_hash,
362            code,
363            account_id: None,
364        }))
365    }
366
367    fn code_by_hash_ref(&self, code_hash: B256) -> Result<RevmBytecode, Self::Error> {
368        Ok(self.writer.get::<Bytecodes>(&code_hash)?.unwrap_or_default())
369    }
370
371    fn storage_ref(
372        &self,
373        address: Address,
374        index: StorageKey,
375    ) -> Result<StorageValue, Self::Error> {
376        Ok(self.writer.get_dual::<tables::PlainStorageState>(&address, &index)?.unwrap_or_default())
377    }
378
379    fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
380        Ok(self.writer.get::<tables::Headers>(&number)?.map_or(B256::ZERO, |h| h.hash()))
381    }
382}
383
384// Database implementation for RevmWrite
385impl<T: HotKvWrite> Database for RevmWrite<T>
386where
387    T::Error: DBErrorMarker,
388{
389    type Error = T::Error;
390
391    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
392        self.basic_ref(address)
393    }
394
395    fn code_by_hash(&mut self, code_hash: B256) -> Result<RevmBytecode, Self::Error> {
396        self.code_by_hash_ref(code_hash)
397    }
398
399    fn storage(
400        &mut self,
401        address: Address,
402        index: StorageKey,
403    ) -> Result<StorageValue, Self::Error> {
404        self.storage_ref(address, index)
405    }
406
407    fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
408        self.block_hash_ref(number)
409    }
410}
411
412// TryDatabaseCommit implementation for RevmWrite
413impl<T: HotKvWrite> TryDatabaseCommit for RevmWrite<T>
414where
415    T::Error: DBErrorMarker,
416{
417    type Error = T::Error;
418
419    fn try_commit(&mut self, changes: HashMap<Address, state::Account>) -> Result<(), Self::Error> {
420        for (address, account) in changes {
421            // Handle account info changes
422            let account_data = Account {
423                nonce: account.info.nonce,
424                balance: account.info.balance,
425                bytecode_hash: (account.info.code_hash != KECCAK256_EMPTY)
426                    .then_some(account.info.code_hash),
427            };
428            self.writer.queue_put::<PlainAccountState>(&address, &account_data)?;
429
430            // Handle storage changes
431            for (key, value) in account.storage {
432                self.writer.queue_put_dual::<tables::PlainStorageState>(
433                    &address,
434                    &key,
435                    &value.present_value(),
436                )?;
437            }
438        }
439
440        Ok(())
441    }
442}
443
444#[cfg(test)]
445mod tests {
446    use super::*;
447    use crate::{
448        db::{HistoryRead, UnsafeDbWrite, UnsafeHistoryWrite},
449        mem::MemKv,
450        model::{HotKv, HotKvRead, HotKvWrite},
451        tables::{Bytecodes, PlainAccountState},
452    };
453    use alloy::{
454        consensus::{Header, Sealable},
455        primitives::{Address, B256, U256},
456    };
457    use signet_storage_types::{Account, BlockNumberList};
458    use trevm::revm::{
459        database::{Database, DatabaseRef, TryDatabaseCommit},
460        primitives::{HashMap, StorageKey, StorageValue},
461        state::{Account as RevmAccount, AccountInfo, Bytecode},
462    };
463
464    /// Create a test account with some data
465    fn create_test_account() -> (Address, Account) {
466        let address = Address::from_slice(&[0x1; 20]);
467        let account = Account {
468            nonce: 42,
469            balance: U256::from(1000u64),
470            bytecode_hash: Some(B256::from_slice(&[0x2; 32])),
471        };
472        (address, account)
473    }
474
475    /// Create test bytecode
476    fn create_test_bytecode() -> (B256, Bytecode) {
477        let hash = B256::from_slice(&[0x2; 32]);
478        let bytecode = RevmBytecode::new_raw(vec![0x60, 0x80, 0x60, 0x40].into());
479        (hash, bytecode)
480    }
481
482    #[test]
483    fn test_database_ref_traits() -> Result<(), Box<dyn std::error::Error>> {
484        let mem_kv = MemKv::default();
485
486        let (address, account) = create_test_account();
487        let (hash, bytecode) = create_test_bytecode();
488
489        {
490            // Setup data using HotKv
491            let writer = mem_kv.revm_writer()?;
492            writer.queue_put::<PlainAccountState>(&address, &account)?;
493            writer.queue_put::<Bytecodes>(&hash, &bytecode)?;
494            writer.persist()?;
495        }
496
497        {
498            // Read using REVM DatabaseRef traits
499            let reader = mem_kv.revm_reader()?;
500
501            // Test basic_ref
502            let account_info = reader.basic_ref(address)?;
503            assert!(account_info.is_some());
504            let info = account_info.unwrap();
505            assert_eq!(info.nonce, 42);
506            assert_eq!(info.balance, U256::from(1000u64));
507            assert_eq!(info.code_hash, hash);
508
509            // Test code_by_hash_ref
510            let retrieved_code = reader.code_by_hash_ref(hash)?;
511            assert_eq!(retrieved_code, bytecode);
512
513            // Test storage_ref (should be zero for non-existent storage)
514            let storage_val = reader.storage_ref(address, StorageKey::from(U256::from(123u64)))?;
515            assert_eq!(storage_val, U256::ZERO);
516
517            // Test block_hash_ref returns ZERO for missing block
518            let block_hash = reader.block_hash_ref(999)?;
519            assert_eq!(block_hash, B256::ZERO);
520        }
521
522        // Test block_hash_ref returns correct hash for stored header
523        let header = Header { number: 123, gas_limit: 1_000_000, ..Default::default() };
524        let sealed = header.seal_slow();
525        let expected_hash = sealed.hash();
526        {
527            let writer = mem_kv.writer()?;
528            writer.queue_put::<tables::Headers>(&123u64, &sealed)?;
529            writer.commit()?;
530        }
531        {
532            let reader = mem_kv.revm_reader()?;
533            let block_hash = reader.block_hash_ref(123)?;
534            assert_eq!(block_hash, expected_hash);
535        }
536
537        Ok(())
538    }
539
540    #[test]
541    fn test_database_mutable_traits() -> Result<(), Box<dyn std::error::Error>> {
542        let mem_kv = MemKv::default();
543
544        let (address, account) = create_test_account();
545        let (hash, bytecode) = create_test_bytecode();
546
547        {
548            // Setup data using HotKv
549            let writer = mem_kv.revm_writer()?;
550            writer.queue_put::<PlainAccountState>(&address, &account)?;
551            writer.queue_put::<Bytecodes>(&hash, &bytecode)?;
552            writer.persist()?;
553        }
554
555        {
556            // Read using mutable REVM Database traits
557            let mut reader = mem_kv.revm_reader()?;
558
559            // Test basic
560            let account_info = reader.basic(address)?;
561            assert!(account_info.is_some());
562            let info = account_info.unwrap();
563            assert_eq!(info.nonce, 42);
564            assert_eq!(info.balance, U256::from(1000u64));
565
566            // Test code_by_hash
567            let retrieved_code = reader.code_by_hash(hash)?;
568            assert_eq!(retrieved_code, bytecode);
569
570            // Test storage
571            let storage_val = reader.storage(address, StorageKey::from(U256::from(123u64)))?;
572            assert_eq!(storage_val, U256::ZERO);
573
574            // Test block_hash returns ZERO for missing block
575            let block_hash = reader.block_hash(999)?;
576            assert_eq!(block_hash, B256::ZERO);
577        }
578
579        // Test block_hash returns correct hash for stored header
580        let header = Header { number: 456, gas_limit: 1_000_000, ..Default::default() };
581        let sealed = header.seal_slow();
582        let expected_hash = sealed.hash();
583        {
584            let writer = mem_kv.writer()?;
585            writer.queue_put::<tables::Headers>(&456u64, &sealed)?;
586            writer.commit()?;
587        }
588        {
589            let mut reader = mem_kv.revm_reader()?;
590            let block_hash = reader.block_hash(456)?;
591            assert_eq!(block_hash, expected_hash);
592        }
593
594        Ok(())
595    }
596
597    #[test]
598    fn test_write_database_traits() -> Result<(), Box<dyn std::error::Error>> {
599        let mem_kv = MemKv::default();
600
601        let (address, account) = create_test_account();
602        let (hash, bytecode) = create_test_bytecode();
603
604        {
605            // Setup initial data
606            let writer = mem_kv.revm_writer()?;
607            writer.queue_put::<PlainAccountState>(&address, &account)?;
608            writer.queue_put::<Bytecodes>(&hash, &bytecode)?;
609            writer.persist()?;
610        }
611
612        {
613            // Test write operations using DatabaseRef and Database traits
614            let mut writer = mem_kv.revm_writer()?;
615
616            // Test read operations work on writer
617            let account_info = writer.basic_ref(address)?;
618            assert!(account_info.is_some());
619
620            let account_info_mut = writer.basic(address)?;
621            assert!(account_info_mut.is_some());
622
623            let code = writer.code_by_hash_ref(hash)?;
624            assert_eq!(code, bytecode);
625
626            let code_mut = writer.code_by_hash(hash)?;
627            assert_eq!(code_mut, bytecode);
628
629            // Don't persist this writer to test that reads work
630        }
631
632        Ok(())
633    }
634
635    #[test]
636    fn test_try_database_commit() -> Result<(), Box<dyn std::error::Error>> {
637        let mem_kv = MemKv::default();
638
639        let address = Address::from_slice(&[0x1; 20]);
640
641        {
642            let mut writer = mem_kv.revm_writer()?;
643
644            // Create REVM state changes
645            let mut changes = HashMap::default();
646            let account_info = AccountInfo {
647                nonce: 55,
648                balance: U256::from(2000u64),
649                code_hash: KECCAK256_EMPTY,
650                code: None,
651                account_id: None,
652            };
653
654            let mut storage = HashMap::default();
655            storage.insert(
656                StorageKey::from(U256::from(100u64)),
657                trevm::revm::state::EvmStorageSlot::new(U256::from(200u64), 0),
658            );
659
660            let revm_account = RevmAccount {
661                info: account_info,
662                storage,
663                status: trevm::revm::state::AccountStatus::Touched,
664                transaction_id: 0,
665                original_info: Box::default(),
666            };
667
668            changes.insert(address, revm_account);
669
670            // Commit changes using REVM trait
671            writer.try_commit(changes)?;
672            writer.persist()?;
673        }
674
675        {
676            // Verify changes were persisted using HotKv traits
677            let reader = mem_kv.revm_reader()?;
678
679            let account: Option<Account> = reader.get::<PlainAccountState>(&address)?;
680            assert!(account.is_some());
681            let acc = account.unwrap();
682            assert_eq!(acc.nonce, 55);
683            assert_eq!(acc.balance, U256::from(2000u64));
684            assert_eq!(acc.bytecode_hash, None);
685
686            let key = U256::from(100);
687            let storage_val: Option<StorageValue> =
688                reader.get_dual::<tables::PlainStorageState>(&address, &key)?;
689            assert_eq!(storage_val, Some(U256::from(200u64)));
690        }
691
692        Ok(())
693    }
694
695    #[test]
696    fn test_mixed_usage_patterns() -> Result<(), Box<dyn std::error::Error>> {
697        let mem_kv = MemKv::default();
698
699        let address1 = Address::from_slice(&[0x1; 20]);
700        let address2 = Address::from_slice(&[0x2; 20]);
701
702        // Write some data using HotKv
703        {
704            let writer = mem_kv.revm_writer()?;
705            let account = Account { nonce: 10, balance: U256::from(500u64), bytecode_hash: None };
706            writer.queue_put::<PlainAccountState>(&address1, &account)?;
707            writer.persist()?;
708        }
709
710        // Write more data using REVM traits
711        {
712            let mut writer = mem_kv.revm_writer()?;
713            let mut changes = HashMap::default();
714            let revm_account = RevmAccount {
715                info: AccountInfo {
716                    nonce: 20,
717                    balance: U256::from(1500u64),
718                    code_hash: KECCAK256_EMPTY,
719                    code: None,
720                    account_id: None,
721                },
722                storage: HashMap::default(),
723                status: trevm::revm::state::AccountStatus::Touched,
724                transaction_id: 0,
725                original_info: Box::default(),
726            };
727            changes.insert(address2, revm_account);
728            writer.try_commit(changes)?;
729            writer.persist()?;
730        }
731
732        // Read using mixed approaches
733        {
734            let reader = mem_kv.revm_reader()?;
735
736            // Read address1 using HotKv
737            let account1: Option<Account> = reader.get::<PlainAccountState>(&address1)?;
738            assert!(account1.is_some());
739            assert_eq!(account1.unwrap().nonce, 10);
740
741            // Read address2 using REVM DatabaseRef
742            let account2_info = reader.basic_ref(address2)?;
743            assert!(account2_info.is_some());
744            assert_eq!(account2_info.unwrap().nonce, 20);
745        }
746
747        Ok(())
748    }
749
750    #[test]
751    fn test_error_handling() -> Result<(), Box<dyn std::error::Error>> {
752        let mem_kv = MemKv::default();
753
754        let address = Address::from_slice(&[0x1; 20]);
755        let hash = B256::from_slice(&[0x99; 32]);
756
757        let reader = mem_kv.revm_reader()?;
758
759        // Test reading non-existent account
760        let account_info = reader.basic_ref(address)?;
761        assert!(account_info.is_none());
762
763        // Test reading non-existent code
764        let code = reader.code_by_hash_ref(hash)?;
765        assert!(code.is_empty());
766
767        // Test reading non-existent storage
768        let storage = reader.storage_ref(address, StorageKey::from(U256::from(123u64)))?;
769        assert_eq!(storage, U256::ZERO);
770
771        Ok(())
772    }
773
774    #[test]
775    fn test_concurrent_readers() -> Result<(), Box<dyn std::error::Error>> {
776        let mem_kv = MemKv::default();
777
778        let (address, account) = create_test_account();
779
780        // Setup data
781        {
782            let writer = mem_kv.revm_writer()?;
783            writer.queue_put::<PlainAccountState>(&address, &account)?;
784            writer.persist()?;
785        }
786
787        // Create multiple readers
788        let reader1 = mem_kv.revm_reader()?;
789        let reader2 = mem_kv.revm_reader()?;
790
791        // Both should read the same data
792        let account1 = reader1.basic_ref(address)?;
793        let account2 = reader2.basic_ref(address)?;
794
795        assert_eq!(account1, account2);
796        assert!(account1.is_some());
797
798        Ok(())
799    }
800
801    /// Set up a MemKv with history data for height-aware reading tests.
802    ///
803    /// Scenario: account A
804    ///   - Genesis/current state: nonce=10, balance=1000
805    ///   - Block 5 changed account: pre-state was nonce=1, balance=100
806    ///   - Block 10 changed account: pre-state was nonce=5, balance=500
807    ///   - Current (PlainAccountState): nonce=10, balance=1000
808    ///   - History shard: (A, 10) → [5, 10]
809    ///
810    /// Storage slot 0x42 for address A:
811    ///   - Block 5 changed slot: pre-state was 0
812    ///   - Block 10 changed slot: pre-state was 100
813    ///   - Current (PlainStorageState): 200
814    ///   - History shard: (A, ShardedKey(0x42, 10)) → [5, 10]
815    fn setup_history_kv() -> (MemKv, Address) {
816        let mem_kv = MemKv::default();
817        let address = Address::from_slice(&[0x1; 20]);
818        let slot = U256::from(0x42u64);
819
820        let writer = mem_kv.writer().unwrap();
821
822        // Write headers so get_execution_range() returns Some((1, 15))
823        let header1 = alloy::consensus::Header { number: 1, ..Default::default() }.seal_slow();
824        writer.put_header_inconsistent(&header1).unwrap();
825
826        let header15 = alloy::consensus::Header { number: 15, ..Default::default() }.seal_slow();
827        writer.put_header_inconsistent(&header15).unwrap();
828
829        // Current plain state
830        let current_account =
831            Account { nonce: 10, balance: U256::from(1000u64), bytecode_hash: None };
832        writer.put_account(&address, &current_account).unwrap();
833        writer.put_storage(&address, &slot, &U256::from(200u64)).unwrap();
834
835        // Account history shard: blocks 5 and 10 touched address
836        let history = BlockNumberList::new([5, 10]).unwrap();
837        writer.write_account_history(&address, 10, &history).unwrap();
838
839        // Account change sets (pre-states)
840        let pre_state_5 = Account { nonce: 1, balance: U256::from(100u64), bytecode_hash: None };
841        writer.write_account_prestate(5, address, &pre_state_5).unwrap();
842
843        let pre_state_10 = Account { nonce: 5, balance: U256::from(500u64), bytecode_hash: None };
844        writer.write_account_prestate(10, address, &pre_state_10).unwrap();
845
846        // Storage history shard: blocks 5 and 10 touched (address, slot)
847        let storage_history = BlockNumberList::new([5, 10]).unwrap();
848        writer.write_storage_history(&address, slot, 10, &storage_history).unwrap();
849
850        // Storage change sets (pre-states)
851        writer.write_storage_prestate(5, address, &slot, &U256::ZERO).unwrap();
852        writer.write_storage_prestate(10, address, &slot, &U256::from(100u64)).unwrap();
853
854        writer.raw_commit().unwrap();
855
856        (mem_kv, address)
857    }
858
859    #[test]
860    fn test_account_at_height_before_any_changes() {
861        let (mem_kv, address) = setup_history_kv();
862        let reader = mem_kv.reader().unwrap();
863
864        // Height 3: before block 5 (first change). Should return pre-state of block 5.
865        let account = reader.get_account_at_height(&address, Some(3)).unwrap().unwrap();
866        assert_eq!(account.nonce, 1);
867        assert_eq!(account.balance, U256::from(100u64));
868    }
869
870    #[test]
871    fn test_account_at_height_between_changes() {
872        let (mem_kv, address) = setup_history_kv();
873        let reader = mem_kv.reader().unwrap();
874
875        // Height 7: between blocks 5 and 10. Should return pre-state of block 10.
876        let account = reader.get_account_at_height(&address, Some(7)).unwrap().unwrap();
877        assert_eq!(account.nonce, 5);
878        assert_eq!(account.balance, U256::from(500u64));
879    }
880
881    #[test]
882    fn test_account_at_height_after_all_changes() {
883        let (mem_kv, address) = setup_history_kv();
884        let reader = mem_kv.reader().unwrap();
885
886        // Height 15: after all changes. Should return current plain state.
887        let account = reader.get_account_at_height(&address, Some(15)).unwrap().unwrap();
888        assert_eq!(account.nonce, 10);
889        assert_eq!(account.balance, U256::from(1000u64));
890    }
891
892    #[test]
893    fn test_account_at_height_exactly_at_change() {
894        let (mem_kv, address) = setup_history_kv();
895        let reader = mem_kv.reader().unwrap();
896
897        // Height 5: exactly at block 5. The change AT block 5 is already applied,
898        // so earliest block > 5 is 10, returning pre-state of block 10.
899        let account = reader.get_account_at_height(&address, Some(5)).unwrap().unwrap();
900        assert_eq!(account.nonce, 5);
901        assert_eq!(account.balance, U256::from(500u64));
902    }
903
904    #[test]
905    fn test_account_at_height_exactly_at_last_change() {
906        let (mem_kv, address) = setup_history_kv();
907        let reader = mem_kv.reader().unwrap();
908
909        // Height 10: exactly at last change block. No blocks > 10 in history,
910        // so returns current plain state.
911        let account = reader.get_account_at_height(&address, Some(10)).unwrap().unwrap();
912        assert_eq!(account.nonce, 10);
913        assert_eq!(account.balance, U256::from(1000u64));
914    }
915
916    #[test]
917    fn test_storage_at_height_before_any_changes() {
918        let (mem_kv, address) = setup_history_kv();
919        let reader = mem_kv.reader().unwrap();
920        let slot = U256::from(0x42u64);
921
922        // Height 3: before block 5. Should return pre-state of block 5 (zero).
923        let value = reader.get_storage_at_height(&address, &slot, Some(3)).unwrap();
924        assert_eq!(value, Some(U256::ZERO));
925    }
926
927    #[test]
928    fn test_storage_at_height_between_changes() {
929        let (mem_kv, address) = setup_history_kv();
930        let reader = mem_kv.reader().unwrap();
931        let slot = U256::from(0x42u64);
932
933        // Height 7: between blocks 5 and 10. Should return pre-state of block 10.
934        let value = reader.get_storage_at_height(&address, &slot, Some(7)).unwrap();
935        assert_eq!(value, Some(U256::from(100u64)));
936    }
937
938    #[test]
939    fn test_storage_at_height_after_all_changes() {
940        let (mem_kv, address) = setup_history_kv();
941        let reader = mem_kv.reader().unwrap();
942        let slot = U256::from(0x42u64);
943
944        // Height 15: after all changes. Should return current plain state.
945        let value = reader.get_storage_at_height(&address, &slot, Some(15)).unwrap();
946        assert_eq!(value, Some(U256::from(200u64)));
947    }
948
949    #[test]
950    fn test_account_at_height_no_history() {
951        let (mem_kv, _) = setup_history_kv();
952        let reader = mem_kv.reader().unwrap();
953
954        // Unknown address with no history — should return current (None).
955        let unknown = Address::from_slice(&[0xFF; 20]);
956        let result = reader.get_account_at_height(&unknown, Some(5)).unwrap();
957        assert!(result.is_none());
958    }
959
960    #[test]
961    fn test_storage_at_height_no_history() {
962        let (mem_kv, address) = setup_history_kv();
963        let reader = mem_kv.reader().unwrap();
964
965        // Unknown slot with no history — should return current (None).
966        let unknown_slot = U256::from(0x99u64);
967        let result = reader.get_storage_at_height(&address, &unknown_slot, Some(5)).unwrap();
968        assert!(result.is_none());
969    }
970
971    #[test]
972    fn test_revm_read_at_height() {
973        let (mem_kv, address) = setup_history_kv();
974        let slot = U256::from(0x42u64);
975
976        // Reader at height 3: should see pre-block-5 state
977        let reader = mem_kv.revm_reader_at_height(3).unwrap();
978        let info = reader.basic_ref(address).unwrap().unwrap();
979        assert_eq!(info.nonce, 1);
980        assert_eq!(info.balance, U256::from(100u64));
981
982        let storage = reader.storage_ref(address, StorageKey::from(slot)).unwrap();
983        assert_eq!(storage, U256::ZERO);
984    }
985
986    #[test]
987    fn test_revm_read_at_height_current_state() {
988        let (mem_kv, address) = setup_history_kv();
989        let slot = U256::from(0x42u64);
990
991        // Reader at height 15: after all changes, should see current state
992        let reader = mem_kv.revm_reader_at_height(15).unwrap();
993        let info = reader.basic_ref(address).unwrap().unwrap();
994        assert_eq!(info.nonce, 10);
995        assert_eq!(info.balance, U256::from(1000u64));
996
997        let storage = reader.storage_ref(address, StorageKey::from(slot)).unwrap();
998        assert_eq!(storage, U256::from(200u64));
999    }
1000
1001    #[test]
1002    fn test_revm_read_none_height_uses_current() {
1003        let (mem_kv, address) = setup_history_kv();
1004        let slot = U256::from(0x42u64);
1005
1006        // Reader with no height (None) — backward compatible, reads current state
1007        let reader = mem_kv.revm_reader().unwrap();
1008        let info = reader.basic_ref(address).unwrap().unwrap();
1009        assert_eq!(info.nonce, 10);
1010        assert_eq!(info.balance, U256::from(1000u64));
1011
1012        let storage = reader.storage_ref(address, StorageKey::from(slot)).unwrap();
1013        assert_eq!(storage, U256::from(200u64));
1014    }
1015
1016    #[test]
1017    fn test_revm_reader_at_height_past_tip() {
1018        let (mem_kv, _) = setup_history_kv();
1019
1020        // Height 20 with tip at 15 → HeightOutOfRange
1021        let err = mem_kv.revm_reader_at_height(20).unwrap_err();
1022        assert!(
1023            matches!(err, HotKvError::HeightOutOfRange { height: 20, first: 1, last: 15 }),
1024            "expected HeightOutOfRange, got {err:?}"
1025        );
1026    }
1027
1028    #[test]
1029    fn test_revm_reader_at_height_before_first_block() {
1030        let (mem_kv, _) = setup_history_kv();
1031
1032        // Height 0 with first block at 1 → HeightOutOfRange
1033        let err = mem_kv.revm_reader_at_height(0).unwrap_err();
1034        assert!(
1035            matches!(err, HotKvError::HeightOutOfRange { height: 0, first: 1, last: 15 }),
1036            "expected HeightOutOfRange, got {err:?}"
1037        );
1038    }
1039
1040    #[test]
1041    fn test_revm_reader_at_height_empty_db() {
1042        let mem_kv = MemKv::default();
1043
1044        // Empty database → NoBlocks
1045        let err = mem_kv.revm_reader_at_height(5).unwrap_err();
1046        assert!(matches!(err, HotKvError::NoBlocks), "expected NoBlocks, got {err:?}");
1047    }
1048
1049    #[test]
1050    fn test_checked_account_at_height_out_of_range() {
1051        let (mem_kv, address) = setup_history_kv();
1052        let reader = mem_kv.reader().unwrap();
1053
1054        // Height 20 with tip at 15 → HeightOutOfRange
1055        let err = reader.get_account_at_height_checked(&address, Some(20)).unwrap_err();
1056        assert!(
1057            matches!(
1058                err,
1059                crate::db::HistoryError::HeightOutOfRange { height: 20, first: 1, last: 15 }
1060            ),
1061            "expected HeightOutOfRange, got {err:?}"
1062        );
1063    }
1064
1065    #[test]
1066    fn test_checked_storage_at_height_out_of_range() {
1067        let (mem_kv, address) = setup_history_kv();
1068        let reader = mem_kv.reader().unwrap();
1069        let slot = U256::from(0x42u64);
1070
1071        // Height 20 with tip at 15 → HeightOutOfRange
1072        let err = reader.get_storage_at_height_checked(&address, &slot, Some(20)).unwrap_err();
1073        assert!(
1074            matches!(
1075                err,
1076                crate::db::HistoryError::HeightOutOfRange { height: 20, first: 1, last: 15 }
1077            ),
1078            "expected HeightOutOfRange, got {err:?}"
1079        );
1080    }
1081
1082    #[test]
1083    fn test_checked_methods_pass_for_valid_height() {
1084        let (mem_kv, address) = setup_history_kv();
1085        let reader = mem_kv.reader().unwrap();
1086        let slot = U256::from(0x42u64);
1087
1088        // Height 7 is within range [1, 15] — should succeed and match unchecked
1089        let account = reader.get_account_at_height_checked(&address, Some(7)).unwrap().unwrap();
1090        assert_eq!(account.nonce, 5);
1091
1092        let storage =
1093            reader.get_storage_at_height_checked(&address, &slot, Some(7)).unwrap().unwrap();
1094        assert_eq!(storage, U256::from(100u64));
1095    }
1096
1097    #[test]
1098    fn test_checked_methods_none_height() {
1099        let (mem_kv, address) = setup_history_kv();
1100        let reader = mem_kv.reader().unwrap();
1101        let slot = U256::from(0x42u64);
1102
1103        // None height — returns current state, no validation error
1104        let account = reader.get_account_at_height_checked(&address, None).unwrap().unwrap();
1105        assert_eq!(account.nonce, 10);
1106
1107        let storage = reader.get_storage_at_height_checked(&address, &slot, None).unwrap().unwrap();
1108        assert_eq!(storage, U256::from(200u64));
1109    }
1110}