1use crate::{
4 lockfile::StorageLock,
5 metrics::DatabaseEnvMetrics,
6 tables::{self, Tables},
7 utils::default_page_size,
8 DatabaseError, TableSet,
9};
10use eyre::Context;
11use metrics::{gauge, Label};
12use reth_db_api::{
13 cursor::{DbCursorRO, DbCursorRW},
14 database::Database,
15 database_metrics::DatabaseMetrics,
16 models::ClientVersion,
17 transaction::{DbTx, DbTxMut},
18};
19use reth_libmdbx::{
20 ffi, DatabaseFlags, Environment, EnvironmentFlags, Geometry, HandleSlowReadersReturnCode,
21 MaxReadTransactionDuration, Mode, PageSize, SyncMode, RO, RW,
22};
23use reth_storage_errors::db::LogLevel;
24use rustc_hash::FxHashMap;
25use std::{
26 ops::{Deref, Range},
27 path::{Path, PathBuf},
28 sync::Arc,
29 time::{Duration, SystemTime, UNIX_EPOCH},
30};
31use tracing::error;
32use tx::Tx;
33
34pub mod cursor;
35pub mod tx;
36
37mod utils;
38#[cfg(target_os = "linux")]
39mod value_prefetch;
40
41pub const KILOBYTE: usize = 1024;
43pub const MEGABYTE: usize = KILOBYTE * 1024;
45pub const GIGABYTE: usize = MEGABYTE * 1024;
47pub const TERABYTE: usize = GIGABYTE * 1024;
49
50const DEFAULT_MAX_READERS: u64 = 32_000;
52
53const MAX_SAFE_READER_SPACE: usize = 10 * GIGABYTE;
56
57const NON_DURABLE_SYNC_PERIOD: Duration = Duration::from_secs(60);
67
68#[derive(Clone, Copy, Debug, Eq, PartialEq)]
70pub enum DatabaseEnvKind {
71 RO,
73 RW,
75}
76
77impl DatabaseEnvKind {
78 pub const fn is_rw(&self) -> bool {
80 matches!(self, Self::RW)
81 }
82}
83
84#[derive(Clone, Debug)]
86pub struct DatabaseArguments {
87 client_version: ClientVersion,
89 geometry: Geometry<Range<usize>>,
91 log_level: Option<LogLevel>,
93 max_read_transaction_duration: Option<MaxReadTransactionDuration>,
95 exclusive: Option<bool>,
116 max_readers: Option<u64>,
119 sync_mode: SyncMode,
135}
136
137impl Default for DatabaseArguments {
138 fn default() -> Self {
139 Self::new(ClientVersion::default())
140 }
141}
142
143impl DatabaseArguments {
144 pub fn new(client_version: ClientVersion) -> Self {
146 Self {
147 client_version,
148 geometry: Geometry {
149 size: Some(0..(8 * TERABYTE)),
150 growth_step: Some(4 * GIGABYTE as isize),
151 shrink_threshold: Some(0),
152 page_size: Some(PageSize::Set(default_page_size())),
153 },
154 log_level: None,
155 max_read_transaction_duration: None,
156 exclusive: None,
157 max_readers: None,
158 sync_mode: SyncMode::Durable,
159 }
160 }
161
162 pub fn test() -> Self {
168 Self {
169 geometry: Geometry {
170 size: Some(0..(64 * MEGABYTE)),
171 growth_step: Some(4 * MEGABYTE as isize),
172 shrink_threshold: Some(0),
173 page_size: Some(PageSize::Set(default_page_size())),
174 },
175 max_read_transaction_duration: Some(MaxReadTransactionDuration::Unbounded),
176 ..Self::new(ClientVersion::default())
177 }
178 }
179
180 pub const fn with_geometry_max_size(mut self, max_size: Option<usize>) -> Self {
182 if let Some(max_size) = max_size {
183 self.geometry.size = Some(0..max_size);
184 }
185 self
186 }
187
188 pub const fn with_geometry_page_size(mut self, page_size: Option<usize>) -> Self {
190 if let Some(size) = page_size {
191 self.geometry.page_size = Some(reth_libmdbx::PageSize::Set(size));
192 }
193
194 self
195 }
196
197 pub const fn with_sync_mode(mut self, sync_mode: Option<SyncMode>) -> Self {
199 if let Some(sync_mode) = sync_mode {
200 self.sync_mode = sync_mode;
201 }
202
203 self
204 }
205
206 pub const fn with_growth_step(mut self, growth_step: Option<usize>) -> Self {
208 if let Some(growth_step) = growth_step {
209 self.geometry.growth_step = Some(growth_step as isize);
210 }
211 self
212 }
213
214 pub const fn with_log_level(mut self, log_level: Option<LogLevel>) -> Self {
216 self.log_level = log_level;
217 self
218 }
219
220 pub const fn max_read_transaction_duration(
222 &mut self,
223 max_read_transaction_duration: Option<MaxReadTransactionDuration>,
224 ) {
225 self.max_read_transaction_duration = max_read_transaction_duration;
226 }
227
228 pub const fn with_max_read_transaction_duration(
230 mut self,
231 max_read_transaction_duration: Option<MaxReadTransactionDuration>,
232 ) -> Self {
233 self.max_read_transaction_duration(max_read_transaction_duration);
234 self
235 }
236
237 pub const fn with_exclusive(mut self, exclusive: Option<bool>) -> Self {
239 self.exclusive = exclusive;
240 self
241 }
242
243 pub const fn with_max_readers(mut self, max_readers: Option<u64>) -> Self {
245 self.max_readers = max_readers;
246 self
247 }
248
249 pub const fn client_version(&self) -> &ClientVersion {
251 &self.client_version
252 }
253}
254
255#[derive(Debug, Clone)]
257pub struct DatabaseEnv {
258 inner: Environment,
260 path: PathBuf,
262 dbis: Arc<TableDbis>,
268 metrics: Option<Arc<DatabaseEnvMetrics>>,
270 disable_long_read_transaction_safety: bool,
272 _lock_file: Option<StorageLock>,
274}
275
276#[derive(Debug, Clone)]
278pub(crate) struct TableDbis {
279 tables: [Option<ffi::MDBX_dbi>; Tables::COUNT],
281 other: FxHashMap<&'static str, ffi::MDBX_dbi>,
283}
284
285impl Default for TableDbis {
286 fn default() -> Self {
287 Self { tables: [None; Tables::COUNT], other: FxHashMap::default() }
288 }
289}
290
291impl TableDbis {
292 pub(crate) fn get(&self, table: Option<Tables>, name: &str) -> Option<ffi::MDBX_dbi> {
297 match table {
298 Some(table) => self.tables[table as usize],
299 None => self.other.get(name).copied(),
300 }
301 }
302
303 fn insert(&mut self, name: &'static str, dbi: ffi::MDBX_dbi) {
305 match name.parse::<Tables>() {
306 Ok(table) => self.tables[table as usize] = Some(dbi),
307 Err(_) => {
308 self.other.insert(name, dbi);
309 }
310 }
311 }
312}
313
314impl Database for DatabaseEnv {
315 type TX = tx::Tx<RO>;
316 type TXMut = tx::Tx<RW>;
317
318 fn tx(&self) -> Result<Self::TX, DatabaseError> {
319 let mut tx = Tx::new(
320 self.inner.begin_ro_txn().map_err(|e| DatabaseError::InitTx(e.into()))?,
321 self.dbis.clone(),
322 self.metrics.clone(),
323 )
324 .map_err(|e| DatabaseError::InitTx(e.into()))?;
325 if self.disable_long_read_transaction_safety {
326 tx.disable_long_read_transaction_safety();
327 }
328 Ok(tx)
329 }
330
331 fn tx_mut(&self) -> Result<Self::TXMut, DatabaseError> {
332 let mut tx = Tx::new(
333 self.inner.begin_rw_txn().map_err(|e| DatabaseError::InitTx(e.into()))?,
334 self.dbis.clone(),
335 self.metrics.clone(),
336 )
337 .map_err(|e| DatabaseError::InitTx(e.into()))?;
338 if self.disable_long_read_transaction_safety {
339 tx.disable_long_read_transaction_safety();
340 }
341 Ok(tx)
342 }
343
344 fn path(&self) -> PathBuf {
345 self.path.clone()
346 }
347
348 fn oldest_reader_txnid(&self) -> Option<u64> {
349 let info = self.inner.info().ok()?;
350 let txnid = info.latter_reader_txnid();
351 if txnid == 0 {
352 None
353 } else {
354 Some(txnid)
355 }
356 }
357
358 fn last_txnid(&self) -> Option<u64> {
359 let info = self.inner.info().ok()?;
360 let txnid = info.last_txnid();
361 if txnid == 0 {
362 None
363 } else {
364 Some(txnid as u64)
365 }
366 }
367}
368
369impl DatabaseMetrics for DatabaseEnv {
370 fn report_metrics(&self) {
371 for (name, value, labels) in self.gauge_metrics() {
372 gauge!(name, labels).set(value);
373 }
374 }
375
376 fn gauge_metrics(&self) -> Vec<(&'static str, f64, Vec<Label>)> {
377 let mut metrics = Vec::new();
378
379 let _ = self
380 .view(|tx| {
381 for table in Tables::ALL.iter().map(Tables::name) {
382 let table_db =
383 tx.inner().open_db(Some(table)).wrap_err("Could not open db.")?;
384
385 let stats = tx
386 .inner()
387 .db_stat(table_db.dbi())
388 .wrap_err(format!("Could not find table: {table}"))?;
389
390 let page_size = stats.page_size() as usize;
391 let leaf_pages = stats.leaf_pages();
392 let branch_pages = stats.branch_pages();
393 let overflow_pages = stats.overflow_pages();
394 let num_pages = leaf_pages + branch_pages + overflow_pages;
395 let table_size = page_size * num_pages;
396 let entries = stats.entries();
397
398 metrics.push((
399 "db.table_size",
400 table_size as f64,
401 vec![Label::new("table", table)],
402 ));
403 metrics.push((
404 "db.table_pages",
405 leaf_pages as f64,
406 vec![Label::new("table", table), Label::new("type", "leaf")],
407 ));
408 metrics.push((
409 "db.table_pages",
410 branch_pages as f64,
411 vec![Label::new("table", table), Label::new("type", "branch")],
412 ));
413 metrics.push((
414 "db.table_pages",
415 overflow_pages as f64,
416 vec![Label::new("table", table), Label::new("type", "overflow")],
417 ));
418 metrics.push((
419 "db.table_entries",
420 entries as f64,
421 vec![Label::new("table", table)],
422 ));
423 }
424
425 Ok::<(), eyre::Report>(())
426 })
427 .map_err(|error| error!(%error, "Failed to read db table stats"));
428
429 if let Ok(freelist) =
430 self.freelist().map_err(|error| error!(%error, "Failed to read db.freelist"))
431 {
432 metrics.push(("db.freelist", freelist as f64, vec![]));
433 }
434
435 if let Ok(stat) = self.stat().map_err(|error| error!(%error, "Failed to read db.stat")) {
436 metrics.push(("db.page_size", stat.page_size() as f64, vec![]));
437 }
438
439 metrics.push((
440 "db.timed_out_not_aborted_transactions",
441 self.timed_out_not_aborted_transactions() as f64,
442 vec![],
443 ));
444
445 metrics
446 }
447}
448
449impl DatabaseEnv {
450 pub fn open(
454 path: &Path,
455 kind: DatabaseEnvKind,
456 args: DatabaseArguments,
457 ) -> Result<Self, DatabaseError> {
458 let _lock_file = if kind.is_rw() {
459 StorageLock::try_acquire(path)
460 .map_err(|err| DatabaseError::Other(err.to_string()))?
461 .into()
462 } else {
463 None
464 };
465
466 let mut inner_env = Environment::builder();
467
468 let mode = match kind {
469 DatabaseEnvKind::RO => Mode::ReadOnly,
470 DatabaseEnvKind::RW => {
471 inner_env.write_map();
473 if matches!(args.sync_mode, SyncMode::SafeNoSync | SyncMode::UtterlyNoSync) {
474 inner_env.set_sync_period(NON_DURABLE_SYNC_PERIOD);
475 }
476 Mode::ReadWrite { sync_mode: args.sync_mode }
477 }
478 };
479
480 debug_assert!(Tables::ALL.len() <= 256, "number of tables exceed max dbs");
483 inner_env.set_max_dbs(256);
484 inner_env.set_geometry(args.geometry);
485
486 fn is_current_process(id: u32) -> bool {
487 #[cfg(unix)]
488 {
489 id == std::os::unix::process::parent_id() || id == std::process::id()
490 }
491
492 #[cfg(not(unix))]
493 {
494 id == std::process::id()
495 }
496 }
497
498 extern "C" fn handle_slow_readers(
499 _env: *const ffi::MDBX_env,
500 _txn: *const ffi::MDBX_txn,
501 process_id: ffi::mdbx_pid_t,
502 thread_id: ffi::mdbx_tid_t,
503 read_txn_id: u64,
504 gap: std::ffi::c_uint,
505 space: usize,
506 retry: std::ffi::c_int,
507 ) -> HandleSlowReadersReturnCode {
508 if space > MAX_SAFE_READER_SPACE {
509 let message = if is_current_process(process_id as u32) {
510 "Current process has a long-lived database transaction that grows the database file."
511 } else {
512 "External process has a long-lived database transaction that grows the database file. \
513 Use shorter-lived read transactions or shut down the node."
514 };
515 tracing::warn!(
516 target: "storage::db::mdbx",
517 ?process_id,
518 ?thread_id,
519 ?read_txn_id,
520 ?gap,
521 ?space,
522 ?retry,
523 "{message}"
524 )
525 }
526
527 reth_libmdbx::HandleSlowReadersReturnCode::ProceedWithoutKillingReader
528 }
529 inner_env.set_handle_slow_readers(handle_slow_readers);
530
531 inner_env.set_flags(EnvironmentFlags {
532 mode,
533 no_rdahead: true,
536 coalesce: true,
537 exclusive: args.exclusive.unwrap_or_default(),
538 ..Default::default()
539 });
540 inner_env.set_max_readers(args.max_readers.unwrap_or(DEFAULT_MAX_READERS));
542 inner_env.set_rp_augment_limit(256 * 1024);
568
569 if let Some(log_level) = args.log_level {
570 let is_log_level_available = if cfg!(debug_assertions) {
572 true
573 } else {
574 matches!(
575 log_level,
576 LogLevel::Fatal | LogLevel::Error | LogLevel::Warn | LogLevel::Notice
577 )
578 };
579 if is_log_level_available {
580 inner_env.set_log_level(match log_level {
581 LogLevel::Fatal => 0,
582 LogLevel::Error => 1,
583 LogLevel::Warn => 2,
584 LogLevel::Notice => 3,
585 LogLevel::Verbose => 4,
586 LogLevel::Debug => 5,
587 LogLevel::Trace => 6,
588 LogLevel::Extra => 7,
589 });
590 } else {
591 return Err(DatabaseError::LogLevelUnavailable(log_level))
592 }
593 }
594
595 if let Some(max_read_transaction_duration) = args.max_read_transaction_duration {
596 inner_env.set_max_read_transaction_duration(max_read_transaction_duration);
597 }
598
599 let env = Self {
600 inner: inner_env.open(path).map_err(|e| DatabaseError::Open(e.into()))?,
601 path: path.to_path_buf(),
602 dbis: Arc::default(),
603 metrics: None,
604 disable_long_read_transaction_safety: false,
605 _lock_file,
606 };
607
608 Ok(env)
609 }
610
611 pub fn with_metrics(mut self) -> Self {
613 self.metrics = Some(DatabaseEnvMetrics::new().into());
614 self
615 }
616
617 pub fn with_metrics_if(self, enabled: bool) -> Self {
619 if enabled {
620 self.with_metrics()
621 } else {
622 self
623 }
624 }
625
626 pub fn create_tables(&mut self) -> Result<(), DatabaseError> {
630 self.create_and_track_tables_for::<Tables>()
631 }
632
633 pub fn create_and_track_tables_for<TS: TableSet>(&mut self) -> Result<(), DatabaseError> {
637 let handles = self._create_tables::<TS>()?;
638 let dbis = Arc::make_mut(&mut self.dbis);
641 for (name, dbi) in handles {
642 dbis.insert(name, dbi);
643 }
644
645 Ok(())
646 }
647
648 pub fn create_tables_for<TS: TableSet>(self: &mut Arc<Self>) -> Result<(), DatabaseError> {
655 let handles = self._create_tables::<TS>()?;
656 if let Some(db) = Arc::get_mut(self) {
657 let dbis = Arc::make_mut(&mut db.dbis);
659 for (name, dbi) in handles {
660 dbis.insert(name, dbi);
661 }
662 }
663 Ok(())
664 }
665
666 fn _create_tables<TS: TableSet>(
668 &self,
669 ) -> Result<Vec<(&'static str, ffi::MDBX_dbi)>, DatabaseError> {
670 let mut handles = Vec::new();
671 let tx = self.inner.begin_rw_txn().map_err(|e| DatabaseError::InitTx(e.into()))?;
672
673 for table in TS::tables() {
674 let flags =
675 if table.is_dupsort() { DatabaseFlags::DUP_SORT } else { DatabaseFlags::default() };
676
677 let db = tx
678 .create_db(Some(table.name()), flags)
679 .map_err(|e| DatabaseError::CreateTable(e.into()))?;
680 handles.push((table.name(), db.dbi()));
681 }
682
683 tx.commit().map_err(|e| DatabaseError::Commit(e.into()))?;
684 Ok(handles)
685 }
686
687 pub fn drop_orphan_table(&self, name: &str) -> Result<bool, DatabaseError> {
699 let tx = self.inner.begin_rw_txn().map_err(|e| DatabaseError::InitTx(e.into()))?;
700
701 match tx.open_db(Some(name)) {
702 Ok(db) => {
703 unsafe {
706 tx.drop_db(db.dbi()).map_err(|e| DatabaseError::Delete(e.into()))?;
707 }
708 tx.commit().map_err(|e| DatabaseError::Commit(e.into()))?;
709 Ok(true)
710 }
711 Err(reth_libmdbx::Error::NotFound) => Ok(false),
712 Err(e) => Err(DatabaseError::Open(e.into())),
713 }
714 }
715
716 pub fn record_client_version(&self, version: ClientVersion) -> Result<(), DatabaseError> {
718 if version.is_empty() {
719 return Ok(())
720 }
721
722 let tx = self.tx_mut()?;
723 let mut version_cursor = tx.cursor_write::<tables::VersionHistory>()?;
724
725 let last_version = version_cursor.last()?.map(|(_, v)| v);
726 if Some(&version) != last_version.as_ref() {
727 version_cursor.upsert(
728 SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(),
729 &version,
730 )?;
731 tx.commit()?;
732 }
733
734 Ok(())
735 }
736
737 pub const fn with_disable_long_read_transaction_safety(mut self) -> Self {
742 self.disable_long_read_transaction_safety = true;
743 self
744 }
745}
746
747impl Deref for DatabaseEnv {
748 type Target = Environment;
749
750 fn deref(&self) -> &Self::Target {
751 &self.inner
752 }
753}
754
755#[cfg(test)]
756mod tests {
757
758 use super::*;
759 use crate::{
760 tables::{
761 AccountsHistory, CanonicalHeaders, Headers, PlainAccountState, PlainStorageState,
762 },
763 test_utils::*,
764 AccountChangeSets,
765 };
766 use alloy_consensus::Header;
767 use alloy_primitives::{address, Address, B256, U256};
768 use reth_db_api::{
769 cursor::{DbDupCursorRO, DbDupCursorRW, ReverseWalker, Walker},
770 models::{AccountBeforeTx, IntegerList, ShardedKey},
771 table::{Encode, Table, TableInfo},
772 tables_to_generic,
773 };
774 use reth_libmdbx::Error;
775 use reth_primitives_traits::{Account, StorageEntry};
776 use reth_storage_errors::db::{DatabaseWriteError, DatabaseWriteOperation};
777 use tempfile::TempDir;
778
779 fn create_test_db(kind: DatabaseEnvKind) -> (TempDir, DatabaseEnv) {
781 let tempdir = tempfile::TempDir::new().expect(ERROR_TEMPDIR);
782 let env = create_test_db_with_path(kind, tempdir.path());
783 (tempdir, env)
784 }
785
786 fn create_test_db_with_path(kind: DatabaseEnvKind, path: &Path) -> DatabaseEnv {
788 let mut env =
789 DatabaseEnv::open(path, kind, DatabaseArguments::new(ClientVersion::default()))
790 .expect(ERROR_DB_CREATION);
791 env.create_tables().expect(ERROR_TABLE_CREATION);
792 env
793 }
794
795 const ERROR_DB_CREATION: &str = "Not able to create the mdbx file.";
796 const ERROR_PUT: &str = "Not able to insert value into table.";
797 const ERROR_APPEND: &str = "Not able to append the value to the table.";
798 const ERROR_UPSERT: &str = "Not able to upsert the value to the table.";
799 const ERROR_GET: &str = "Not able to get value from table.";
800 const ERROR_DEL: &str = "Not able to delete from table.";
801 const ERROR_COMMIT: &str = "Not able to commit transaction.";
802 const ERROR_RETURN_VALUE: &str = "Mismatching result.";
803 const ERROR_INIT_TX: &str = "Failed to create a MDBX transaction.";
804
805 #[test]
806 fn db_creation() {
807 let _tempdir = create_test_db(DatabaseEnvKind::RW);
808 }
809
810 #[test]
811 fn db_drop_orphan_table() {
812 let tempdir = tempfile::TempDir::new().expect(ERROR_TEMPDIR);
813 let db = create_test_db_with_path(DatabaseEnvKind::RW, tempdir.path());
814
815 let orphan_table_name = "OrphanTestTable";
817 {
818 let tx = db.inner.begin_rw_txn().expect(ERROR_INIT_TX);
819 tx.create_db(Some(orphan_table_name), DatabaseFlags::empty())
820 .expect("Failed to create orphan table");
821 tx.commit().expect(ERROR_COMMIT);
822 }
823
824 {
826 let tx = db.inner.begin_ro_txn().expect(ERROR_INIT_TX);
827 assert!(tx.open_db(Some(orphan_table_name)).is_ok(), "Orphan table should exist");
828 }
829
830 let result = db.drop_orphan_table(orphan_table_name);
832 assert!(result.is_ok(), "drop_orphan_table should succeed");
833 assert!(result.unwrap(), "drop_orphan_table should return true for existing table");
834
835 {
837 let tx = db.inner.begin_ro_txn().expect(ERROR_INIT_TX);
838 assert!(
839 tx.open_db(Some(orphan_table_name)).is_err(),
840 "Orphan table should no longer exist"
841 );
842 }
843
844 let result = db.drop_orphan_table("NonExistentTable");
846 assert!(result.is_ok(), "drop_orphan_table should succeed for non-existent table");
847 assert!(!result.unwrap(), "drop_orphan_table should return false for non-existent table");
848 }
849
850 #[test]
851 fn db_manual_put_get() {
852 let (_tempdir, env) = create_test_db(DatabaseEnvKind::RW);
853
854 let value = Header::default();
855 let key = 1u64;
856
857 let tx = env.tx_mut().expect(ERROR_INIT_TX);
859 tx.put::<Headers>(key, value.clone()).expect(ERROR_PUT);
860 tx.commit().expect(ERROR_COMMIT);
861
862 let tx = env.tx().expect(ERROR_INIT_TX);
864 let result = tx.get::<Headers>(key).expect(ERROR_GET);
865 assert_eq!(result.expect(ERROR_RETURN_VALUE), value);
866 tx.commit().expect(ERROR_COMMIT);
867 }
868
869 #[test]
870 fn db_dup_cursor_delete_first() {
871 let (_tempdir, db) = create_test_db(DatabaseEnvKind::RW);
872 let tx = db.tx_mut().expect(ERROR_INIT_TX);
873
874 let mut dup_cursor = tx.cursor_dup_write::<PlainStorageState>().unwrap();
875
876 let entry_0 = StorageEntry { key: B256::with_last_byte(1), value: U256::ZERO };
877 let entry_1 = StorageEntry { key: B256::with_last_byte(1), value: U256::ONE };
878
879 dup_cursor.upsert(Address::with_last_byte(1), &entry_0).expect(ERROR_UPSERT);
880 dup_cursor.upsert(Address::with_last_byte(1), &entry_1).expect(ERROR_UPSERT);
881
882 assert_eq!(
883 dup_cursor.walk(None).unwrap().collect::<Result<Vec<_>, _>>().unwrap(),
884 vec![(Address::with_last_byte(1), entry_0), (Address::with_last_byte(1), entry_1),]
885 );
886
887 let mut walker = dup_cursor.walk(None).unwrap();
888 walker.delete_current().expect(ERROR_DEL);
889
890 assert_eq!(walker.next().unwrap().unwrap(), (Address::with_last_byte(1), entry_1));
891
892 assert_eq!(
894 tx.cursor_dup_read::<PlainStorageState>()
895 .unwrap()
896 .walk(None)
897 .unwrap()
898 .collect::<Result<Vec<_>, _>>()
899 .unwrap(),
900 vec![
901 (Address::with_last_byte(1), entry_1), ]
903 );
904
905 assert!(walker.next().is_none());
907 }
908
909 #[test]
910 fn db_cursor_walk() {
911 let (_tempdir, env) = create_test_db(DatabaseEnvKind::RW);
912
913 let value = Header::default();
914 let key = 1u64;
915
916 let tx = env.tx_mut().expect(ERROR_INIT_TX);
918 tx.put::<Headers>(key, value.clone()).expect(ERROR_PUT);
919 tx.commit().expect(ERROR_COMMIT);
920
921 let tx = env.tx().expect(ERROR_INIT_TX);
923 let mut cursor = tx.cursor_read::<Headers>().unwrap();
924
925 let first = cursor.first().unwrap();
926 assert!(first.is_some(), "First should be our put");
927
928 let walk = cursor.walk(Some(key)).unwrap();
930 let first = walk.into_iter().next().unwrap().unwrap();
931 assert_eq!(first.1, value, "First next should be put value");
932 }
933
934 #[test]
935 fn db_cursor_walk_range() {
936 let (_tempdir, db) = create_test_db(DatabaseEnvKind::RW);
937
938 let tx = db.tx_mut().expect(ERROR_INIT_TX);
940 vec![0, 1, 2, 3]
941 .into_iter()
942 .try_for_each(|key| tx.put::<CanonicalHeaders>(key, B256::ZERO))
943 .expect(ERROR_PUT);
944 tx.commit().expect(ERROR_COMMIT);
945
946 let tx = db.tx().expect(ERROR_INIT_TX);
947 let mut cursor = tx.cursor_read::<CanonicalHeaders>().unwrap();
948
949 let mut walker = cursor.walk_range(1..3).unwrap();
951 assert_eq!(walker.next().unwrap().unwrap(), (1, B256::ZERO));
952 assert_eq!(walker.next().unwrap().unwrap(), (2, B256::ZERO));
953 assert!(walker.next().is_none());
954 assert!(walker.next().is_none());
956
957 let mut walker = cursor.walk_range(1..=2).unwrap();
959 assert_eq!(walker.next().unwrap().unwrap(), (1, B256::ZERO));
960 assert_eq!(walker.next().unwrap().unwrap(), (2, B256::ZERO));
961 assert!(walker.next().is_none());
963
964 let mut walker = cursor.walk_range(1..).unwrap();
966 assert_eq!(walker.next().unwrap().unwrap(), (1, B256::ZERO));
967 assert_eq!(walker.next().unwrap().unwrap(), (2, B256::ZERO));
968 assert_eq!(walker.next().unwrap().unwrap(), (3, B256::ZERO));
969 assert!(walker.next().is_none());
971
972 let mut walker = cursor.walk_range(2..4).unwrap();
974 assert_eq!(walker.next().unwrap().unwrap(), (2, B256::ZERO));
975 assert_eq!(walker.next().unwrap().unwrap(), (3, B256::ZERO));
976 assert!(walker.next().is_none());
977 assert!(walker.next().is_none());
979
980 let mut walker = cursor.walk_range(..3).unwrap();
982 assert_eq!(walker.next().unwrap().unwrap(), (0, B256::ZERO));
983 assert_eq!(walker.next().unwrap().unwrap(), (1, B256::ZERO));
984 assert_eq!(walker.next().unwrap().unwrap(), (2, B256::ZERO));
985 assert!(walker.next().is_none());
987
988 let mut walker = cursor.walk_range(..).unwrap();
990 assert_eq!(walker.next().unwrap().unwrap(), (0, B256::ZERO));
991 assert_eq!(walker.next().unwrap().unwrap(), (1, B256::ZERO));
992 assert_eq!(walker.next().unwrap().unwrap(), (2, B256::ZERO));
993 assert_eq!(walker.next().unwrap().unwrap(), (3, B256::ZERO));
994 assert!(walker.next().is_none());
996 }
997
998 #[test]
999 fn db_cursor_walk_range_on_dup_table() {
1000 let (_tempdir, db) = create_test_db(DatabaseEnvKind::RW);
1001
1002 let address0 = Address::ZERO;
1003 let address1 = Address::with_last_byte(1);
1004 let address2 = Address::with_last_byte(2);
1005
1006 let tx = db.tx_mut().expect(ERROR_INIT_TX);
1007 tx.put::<AccountChangeSets>(0, AccountBeforeTx { address: address0, info: None })
1008 .expect(ERROR_PUT);
1009 tx.put::<AccountChangeSets>(0, AccountBeforeTx { address: address1, info: None })
1010 .expect(ERROR_PUT);
1011 tx.put::<AccountChangeSets>(0, AccountBeforeTx { address: address2, info: None })
1012 .expect(ERROR_PUT);
1013 tx.put::<AccountChangeSets>(1, AccountBeforeTx { address: address0, info: None })
1014 .expect(ERROR_PUT);
1015 tx.put::<AccountChangeSets>(1, AccountBeforeTx { address: address1, info: None })
1016 .expect(ERROR_PUT);
1017 tx.put::<AccountChangeSets>(1, AccountBeforeTx { address: address2, info: None })
1018 .expect(ERROR_PUT);
1019 tx.put::<AccountChangeSets>(2, AccountBeforeTx { address: address0, info: None }) .expect(ERROR_PUT);
1021 tx.commit().expect(ERROR_COMMIT);
1022
1023 let tx = db.tx().expect(ERROR_INIT_TX);
1024 let mut cursor = tx.cursor_read::<AccountChangeSets>().unwrap();
1025
1026 let entries = cursor.walk_range(..).unwrap().collect::<Result<Vec<_>, _>>().unwrap();
1027 assert_eq!(entries.len(), 7);
1028
1029 let mut walker = cursor.walk_range(0..=1).unwrap();
1030 assert_eq!(
1031 walker.next().unwrap().unwrap(),
1032 (0, AccountBeforeTx { address: address0, info: None })
1033 );
1034 assert_eq!(
1035 walker.next().unwrap().unwrap(),
1036 (0, AccountBeforeTx { address: address1, info: None })
1037 );
1038 assert_eq!(
1039 walker.next().unwrap().unwrap(),
1040 (0, AccountBeforeTx { address: address2, info: None })
1041 );
1042 assert_eq!(
1043 walker.next().unwrap().unwrap(),
1044 (1, AccountBeforeTx { address: address0, info: None })
1045 );
1046 assert_eq!(
1047 walker.next().unwrap().unwrap(),
1048 (1, AccountBeforeTx { address: address1, info: None })
1049 );
1050 assert_eq!(
1051 walker.next().unwrap().unwrap(),
1052 (1, AccountBeforeTx { address: address2, info: None })
1053 );
1054 assert!(walker.next().is_none());
1055 }
1056
1057 #[expect(clippy::reversed_empty_ranges)]
1058 #[test]
1059 fn db_cursor_walk_range_invalid() {
1060 let (_tempdir, db) = create_test_db(DatabaseEnvKind::RW);
1061
1062 let tx = db.tx_mut().expect(ERROR_INIT_TX);
1064 vec![0, 1, 2, 3]
1065 .into_iter()
1066 .try_for_each(|key| tx.put::<CanonicalHeaders>(key, B256::ZERO))
1067 .expect(ERROR_PUT);
1068 tx.commit().expect(ERROR_COMMIT);
1069
1070 let tx = db.tx().expect(ERROR_INIT_TX);
1071 let mut cursor = tx.cursor_read::<CanonicalHeaders>().unwrap();
1072
1073 let mut res = cursor.walk_range(3..1).unwrap();
1075 assert!(res.next().is_none());
1076
1077 let mut res = cursor.walk_range(15..=2).unwrap();
1079 assert!(res.next().is_none());
1080
1081 let mut walker = cursor.walk_range(1..1).unwrap();
1083 assert!(walker.next().is_none());
1084 }
1085
1086 #[test]
1087 fn db_walker() {
1088 let (_tempdir, db) = create_test_db(DatabaseEnvKind::RW);
1089
1090 let tx = db.tx_mut().expect(ERROR_INIT_TX);
1092 vec![0, 1, 3]
1093 .into_iter()
1094 .try_for_each(|key| tx.put::<CanonicalHeaders>(key, B256::ZERO))
1095 .expect(ERROR_PUT);
1096 tx.commit().expect(ERROR_COMMIT);
1097
1098 let tx = db.tx().expect(ERROR_INIT_TX);
1099 let mut cursor = tx.cursor_read::<CanonicalHeaders>().unwrap();
1100
1101 let mut walker = Walker::new(&mut cursor, None);
1102
1103 assert_eq!(walker.next().unwrap().unwrap(), (0, B256::ZERO));
1104 assert_eq!(walker.next().unwrap().unwrap(), (1, B256::ZERO));
1105 assert_eq!(walker.next().unwrap().unwrap(), (3, B256::ZERO));
1106 assert!(walker.next().is_none());
1107
1108 let mut reverse_walker = walker.rev();
1110 assert_eq!(reverse_walker.next().unwrap().unwrap(), (3, B256::ZERO));
1111 assert_eq!(reverse_walker.next().unwrap().unwrap(), (1, B256::ZERO));
1112 assert_eq!(reverse_walker.next().unwrap().unwrap(), (0, B256::ZERO));
1113 assert!(reverse_walker.next().is_none());
1114 }
1115
1116 #[test]
1117 fn db_reverse_walker() {
1118 let (_tempdir, db) = create_test_db(DatabaseEnvKind::RW);
1119
1120 let tx = db.tx_mut().expect(ERROR_INIT_TX);
1122 vec![0, 1, 3]
1123 .into_iter()
1124 .try_for_each(|key| tx.put::<CanonicalHeaders>(key, B256::ZERO))
1125 .expect(ERROR_PUT);
1126 tx.commit().expect(ERROR_COMMIT);
1127
1128 let tx = db.tx().expect(ERROR_INIT_TX);
1129 let mut cursor = tx.cursor_read::<CanonicalHeaders>().unwrap();
1130
1131 let mut reverse_walker = ReverseWalker::new(&mut cursor, None);
1132
1133 assert_eq!(reverse_walker.next().unwrap().unwrap(), (3, B256::ZERO));
1134 assert_eq!(reverse_walker.next().unwrap().unwrap(), (1, B256::ZERO));
1135 assert_eq!(reverse_walker.next().unwrap().unwrap(), (0, B256::ZERO));
1136 assert!(reverse_walker.next().is_none());
1137
1138 let mut walker = reverse_walker.forward();
1140 assert_eq!(walker.next().unwrap().unwrap(), (0, B256::ZERO));
1141 assert_eq!(walker.next().unwrap().unwrap(), (1, B256::ZERO));
1142 assert_eq!(walker.next().unwrap().unwrap(), (3, B256::ZERO));
1143 assert!(walker.next().is_none());
1144 }
1145
1146 #[test]
1147 fn db_walk_back() {
1148 let (_tempdir, db) = create_test_db(DatabaseEnvKind::RW);
1149
1150 let tx = db.tx_mut().expect(ERROR_INIT_TX);
1152 vec![0, 1, 3]
1153 .into_iter()
1154 .try_for_each(|key| tx.put::<CanonicalHeaders>(key, B256::ZERO))
1155 .expect(ERROR_PUT);
1156 tx.commit().expect(ERROR_COMMIT);
1157
1158 let tx = db.tx().expect(ERROR_INIT_TX);
1159 let mut cursor = tx.cursor_read::<CanonicalHeaders>().unwrap();
1160
1161 let mut reverse_walker = cursor.walk_back(Some(1)).unwrap();
1162 assert_eq!(reverse_walker.next().unwrap().unwrap(), (1, B256::ZERO));
1163 assert_eq!(reverse_walker.next().unwrap().unwrap(), (0, B256::ZERO));
1164 assert!(reverse_walker.next().is_none());
1165
1166 let mut reverse_walker = cursor.walk_back(Some(2)).unwrap();
1167 assert_eq!(reverse_walker.next().unwrap().unwrap(), (3, B256::ZERO));
1168 assert_eq!(reverse_walker.next().unwrap().unwrap(), (1, B256::ZERO));
1169 assert_eq!(reverse_walker.next().unwrap().unwrap(), (0, B256::ZERO));
1170 assert!(reverse_walker.next().is_none());
1171
1172 let mut reverse_walker = cursor.walk_back(Some(4)).unwrap();
1173 assert_eq!(reverse_walker.next().unwrap().unwrap(), (3, B256::ZERO));
1174 assert_eq!(reverse_walker.next().unwrap().unwrap(), (1, B256::ZERO));
1175 assert_eq!(reverse_walker.next().unwrap().unwrap(), (0, B256::ZERO));
1176 assert!(reverse_walker.next().is_none());
1177
1178 let mut reverse_walker = cursor.walk_back(None).unwrap();
1179 assert_eq!(reverse_walker.next().unwrap().unwrap(), (3, B256::ZERO));
1180 assert_eq!(reverse_walker.next().unwrap().unwrap(), (1, B256::ZERO));
1181 assert_eq!(reverse_walker.next().unwrap().unwrap(), (0, B256::ZERO));
1182 assert!(reverse_walker.next().is_none());
1183 }
1184
1185 #[test]
1186 fn db_cursor_seek_exact_or_previous_key() {
1187 let (_tempdir, db) = create_test_db(DatabaseEnvKind::RW);
1188
1189 let tx = db.tx_mut().expect(ERROR_INIT_TX);
1191 vec![0, 1, 3]
1192 .into_iter()
1193 .try_for_each(|key| tx.put::<CanonicalHeaders>(key, B256::ZERO))
1194 .expect(ERROR_PUT);
1195 tx.commit().expect(ERROR_COMMIT);
1196
1197 let missing_key = 2;
1199 let tx = db.tx().expect(ERROR_INIT_TX);
1200 let mut cursor = tx.cursor_read::<CanonicalHeaders>().unwrap();
1201 assert!(cursor.current().unwrap().is_none());
1202
1203 let exact = cursor.seek_exact(missing_key).unwrap();
1205 assert_eq!(exact, None);
1206 assert!(cursor.current().unwrap().is_none());
1207 }
1208
1209 #[test]
1210 fn db_cursor_insert() {
1211 let (_tempdir, db) = create_test_db(DatabaseEnvKind::RW);
1212
1213 let tx = db.tx_mut().expect(ERROR_INIT_TX);
1215 vec![0, 1, 3, 4, 5]
1216 .into_iter()
1217 .try_for_each(|key| tx.put::<CanonicalHeaders>(key, B256::ZERO))
1218 .expect(ERROR_PUT);
1219 tx.commit().expect(ERROR_COMMIT);
1220
1221 let key_to_insert = 2;
1222 let tx = db.tx_mut().expect(ERROR_INIT_TX);
1223 let mut cursor = tx.cursor_write::<CanonicalHeaders>().unwrap();
1224
1225 assert!(cursor.insert(key_to_insert, &B256::ZERO).is_ok());
1227 assert_eq!(cursor.current().unwrap(), Some((key_to_insert, B256::ZERO)));
1228 assert!(matches!(
1230 cursor.insert(key_to_insert, &B256::ZERO).unwrap_err(),
1231 DatabaseError::Write(err) if *err == DatabaseWriteError {
1232 info: Error::KeyExist.into(),
1233 operation: DatabaseWriteOperation::CursorInsert,
1234 table_name: CanonicalHeaders::NAME,
1235 key: key_to_insert.encode().into(),
1236 }));
1237 assert_eq!(cursor.current().unwrap(), Some((key_to_insert, B256::ZERO)));
1238
1239 tx.commit().expect(ERROR_COMMIT);
1240
1241 let tx = db.tx().expect(ERROR_INIT_TX);
1243 let mut cursor = tx.cursor_read::<CanonicalHeaders>().unwrap();
1244 let res = cursor.walk(None).unwrap().map(|res| res.unwrap().0).collect::<Vec<_>>();
1245 assert_eq!(res, vec![0, 1, 2, 3, 4, 5]);
1246 tx.commit().expect(ERROR_COMMIT);
1247 }
1248
1249 #[test]
1250 fn db_cursor_insert_dup() {
1251 let (_tempdir, db) = create_test_db(DatabaseEnvKind::RW);
1252 let tx = db.tx_mut().expect(ERROR_INIT_TX);
1253
1254 let mut dup_cursor = tx.cursor_dup_write::<PlainStorageState>().unwrap();
1255 let key = Address::random();
1256 let subkey1 = B256::random();
1257 let subkey2 = B256::random();
1258
1259 let entry1 = StorageEntry { key: subkey1, value: U256::ZERO };
1260 assert!(dup_cursor.insert(key, &entry1).is_ok());
1261
1262 let entry2 = StorageEntry { key: subkey2, value: U256::ZERO };
1264 assert!(dup_cursor.insert(key, &entry2).is_err());
1265 }
1266
1267 #[test]
1268 fn db_cursor_delete_current_non_existent() {
1269 let (_tempdir, db) = create_test_db(DatabaseEnvKind::RW);
1270 let tx = db.tx_mut().expect(ERROR_INIT_TX);
1271
1272 let key1 = Address::with_last_byte(1);
1273 let key2 = Address::with_last_byte(2);
1274 let key3 = Address::with_last_byte(3);
1275 let mut cursor = tx.cursor_write::<PlainAccountState>().unwrap();
1276
1277 assert!(cursor.insert(key1, &Account::default()).is_ok());
1278 assert!(cursor.insert(key2, &Account::default()).is_ok());
1279 assert!(cursor.insert(key3, &Account::default()).is_ok());
1280
1281 cursor.seek_exact(key2).unwrap();
1283 assert!(cursor.delete_current().is_ok());
1284 assert!(cursor.seek_exact(key2).unwrap().is_none());
1285
1286 assert!(cursor.seek_exact(key2).unwrap().is_none());
1288 assert!(matches!(
1289 cursor.delete_current().unwrap_err(),
1290 DatabaseError::Delete(err) if err == reth_libmdbx::Error::NoData.into()));
1291 assert_eq!(cursor.seek_exact(key1).unwrap(), Some((key1, Account::default())));
1293 assert_eq!(cursor.seek_exact(key3).unwrap(), Some((key3, Account::default())));
1295 }
1296
1297 #[test]
1298 fn db_cursor_insert_wherever_cursor_is() {
1299 let (_tempdir, db) = create_test_db(DatabaseEnvKind::RW);
1300 let tx = db.tx_mut().expect(ERROR_INIT_TX);
1301
1302 vec![0, 1, 3, 5, 7, 9]
1304 .into_iter()
1305 .try_for_each(|key| tx.put::<CanonicalHeaders>(key, B256::ZERO))
1306 .expect(ERROR_PUT);
1307 tx.commit().expect(ERROR_COMMIT);
1308
1309 let tx = db.tx_mut().expect(ERROR_INIT_TX);
1310 let mut cursor = tx.cursor_write::<CanonicalHeaders>().unwrap();
1311
1312 cursor.last().unwrap();
1314 assert_eq!(cursor.current().unwrap(), Some((9, B256::ZERO)));
1315
1316 for pos in (2..=8).step_by(2) {
1317 assert!(cursor.insert(pos, &B256::ZERO).is_ok());
1318 assert_eq!(cursor.current().unwrap(), Some((pos, B256::ZERO)));
1319 }
1320 tx.commit().expect(ERROR_COMMIT);
1321
1322 let tx = db.tx().expect(ERROR_INIT_TX);
1324 let mut cursor = tx.cursor_read::<CanonicalHeaders>().unwrap();
1325 let res = cursor.walk(None).unwrap().map(|res| res.unwrap().0).collect::<Vec<_>>();
1326 assert_eq!(res, vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
1327 tx.commit().expect(ERROR_COMMIT);
1328 }
1329
1330 #[test]
1331 fn db_cursor_append() {
1332 let (_tempdir, db) = create_test_db(DatabaseEnvKind::RW);
1333
1334 let tx = db.tx_mut().expect(ERROR_INIT_TX);
1336 vec![0, 1, 2, 3, 4]
1337 .into_iter()
1338 .try_for_each(|key| tx.put::<CanonicalHeaders>(key, B256::ZERO))
1339 .expect(ERROR_PUT);
1340 tx.commit().expect(ERROR_COMMIT);
1341
1342 let key_to_append = 5;
1344 let tx = db.tx_mut().expect(ERROR_INIT_TX);
1345 let mut cursor = tx.cursor_write::<CanonicalHeaders>().unwrap();
1346 assert!(cursor.append(key_to_append, &B256::ZERO).is_ok());
1347 tx.commit().expect(ERROR_COMMIT);
1348
1349 let tx = db.tx().expect(ERROR_INIT_TX);
1351 let mut cursor = tx.cursor_read::<CanonicalHeaders>().unwrap();
1352 let res = cursor.walk(None).unwrap().map(|res| res.unwrap().0).collect::<Vec<_>>();
1353 assert_eq!(res, vec![0, 1, 2, 3, 4, 5]);
1354 tx.commit().expect(ERROR_COMMIT);
1355 }
1356
1357 #[test]
1358 fn db_cursor_append_failure() {
1359 let (_tempdir, db) = create_test_db(DatabaseEnvKind::RW);
1360
1361 let tx = db.tx_mut().expect(ERROR_INIT_TX);
1363 vec![0, 1, 3, 4, 5]
1364 .into_iter()
1365 .try_for_each(|key| tx.put::<CanonicalHeaders>(key, B256::ZERO))
1366 .expect(ERROR_PUT);
1367 tx.commit().expect(ERROR_COMMIT);
1368
1369 let key_to_append = 2;
1371 let tx = db.tx_mut().expect(ERROR_INIT_TX);
1372 let mut cursor = tx.cursor_write::<CanonicalHeaders>().unwrap();
1373 assert!(matches!(
1374 cursor.append(key_to_append, &B256::ZERO).unwrap_err(),
1375 DatabaseError::Write(err) if *err == DatabaseWriteError {
1376 info: Error::KeyMismatch.into(),
1377 operation: DatabaseWriteOperation::CursorAppend,
1378 table_name: CanonicalHeaders::NAME,
1379 key: key_to_append.encode().into(),
1380 }));
1381 assert_eq!(cursor.current().unwrap(), Some((5, B256::ZERO))); tx.commit().expect(ERROR_COMMIT);
1383
1384 let tx = db.tx().expect(ERROR_INIT_TX);
1386 let mut cursor = tx.cursor_read::<CanonicalHeaders>().unwrap();
1387 let res = cursor.walk(None).unwrap().map(|res| res.unwrap().0).collect::<Vec<_>>();
1388 assert_eq!(res, vec![0, 1, 3, 4, 5]);
1389 tx.commit().expect(ERROR_COMMIT);
1390 }
1391
1392 #[test]
1393 fn db_cursor_upsert() {
1394 let (_tempdir, db) = create_test_db(DatabaseEnvKind::RW);
1395 let tx = db.tx_mut().expect(ERROR_INIT_TX);
1396
1397 let mut cursor = tx.cursor_write::<PlainAccountState>().unwrap();
1398 let key = Address::random();
1399
1400 let account = Account::default();
1401 cursor.upsert(key, &account).expect(ERROR_UPSERT);
1402 assert_eq!(cursor.seek_exact(key).unwrap(), Some((key, account)));
1403
1404 let account = Account { nonce: 1, ..Default::default() };
1405 cursor.upsert(key, &account).expect(ERROR_UPSERT);
1406 assert_eq!(cursor.seek_exact(key).unwrap(), Some((key, account)));
1407
1408 let account = Account { nonce: 2, ..Default::default() };
1409 cursor.upsert(key, &account).expect(ERROR_UPSERT);
1410 assert_eq!(cursor.seek_exact(key).unwrap(), Some((key, account)));
1411
1412 let mut dup_cursor = tx.cursor_dup_write::<PlainStorageState>().unwrap();
1413 let subkey = B256::random();
1414
1415 let value = U256::ONE;
1416 let entry1 = StorageEntry { key: subkey, value };
1417 dup_cursor.upsert(key, &entry1).expect(ERROR_UPSERT);
1418 assert_eq!(dup_cursor.seek_by_key_subkey(key, subkey).unwrap(), Some(entry1));
1419
1420 let value = U256::from(2);
1421 let entry2 = StorageEntry { key: subkey, value };
1422 dup_cursor.upsert(key, &entry2).expect(ERROR_UPSERT);
1423 assert_eq!(dup_cursor.seek_by_key_subkey(key, subkey).unwrap(), Some(entry1));
1424 assert_eq!(dup_cursor.next_dup_val().unwrap(), Some(entry2));
1425 }
1426
1427 #[test]
1428 fn db_cursor_dupsort_append() {
1429 let (_tempdir, db) = create_test_db(DatabaseEnvKind::RW);
1430
1431 let transition_id = 2;
1432
1433 let tx = db.tx_mut().expect(ERROR_INIT_TX);
1434 let mut cursor = tx.cursor_write::<AccountChangeSets>().unwrap();
1435 vec![0, 1, 3, 4, 5]
1436 .into_iter()
1437 .try_for_each(|val| {
1438 cursor.append(
1439 transition_id,
1440 &AccountBeforeTx { address: Address::with_last_byte(val), info: None },
1441 )
1442 })
1443 .expect(ERROR_APPEND);
1444 tx.commit().expect(ERROR_COMMIT);
1445
1446 let subkey_to_append = 2;
1448 let tx = db.tx_mut().expect(ERROR_INIT_TX);
1449 let mut cursor = tx.cursor_write::<AccountChangeSets>().unwrap();
1450 assert!(matches!(
1451 cursor
1452 .append_dup(
1453 transition_id,
1454 AccountBeforeTx {
1455 address: Address::with_last_byte(subkey_to_append),
1456 info: None
1457 }
1458 )
1459 .unwrap_err(),
1460 DatabaseError::Write(err) if *err == DatabaseWriteError {
1461 info: Error::KeyMismatch.into(),
1462 operation: DatabaseWriteOperation::CursorAppendDup,
1463 table_name: AccountChangeSets::NAME,
1464 key: transition_id.encode().into(),
1465 }));
1466 assert!(matches!(
1467 cursor
1468 .append(
1469 transition_id - 1,
1470 &AccountBeforeTx {
1471 address: Address::with_last_byte(subkey_to_append),
1472 info: None
1473 }
1474 )
1475 .unwrap_err(),
1476 DatabaseError::Write(err) if *err == DatabaseWriteError {
1477 info: Error::KeyMismatch.into(),
1478 operation: DatabaseWriteOperation::CursorAppend,
1479 table_name: AccountChangeSets::NAME,
1480 key: (transition_id - 1).encode().into(),
1481 }
1482 ));
1483 assert!(cursor
1484 .append(
1485 transition_id,
1486 &AccountBeforeTx { address: Address::with_last_byte(subkey_to_append), info: None }
1487 )
1488 .is_ok());
1489 }
1490
1491 #[test]
1492 #[allow(clippy::needless_update)]
1493 #[allow(clippy::clone_on_copy)]
1494 fn db_closure_put_get() {
1495 let tempdir = TempDir::new().expect(ERROR_TEMPDIR);
1496 let path = tempdir.path();
1497
1498 let value = Account {
1499 nonce: 18446744073709551615,
1500 bytecode_hash: Some(B256::random()),
1501 balance: U256::MAX,
1502 ..Default::default()
1503 };
1504 let key = address!("0xa2c122be93b0074270ebee7f6b7292c7deb45047");
1505
1506 {
1507 let env = create_test_db_with_path(DatabaseEnvKind::RW, path);
1508
1509 let result = env.update(|tx| {
1511 tx.put::<PlainAccountState>(key, value.clone()).expect(ERROR_PUT);
1512 200
1513 });
1514 assert_eq!(result.expect(ERROR_RETURN_VALUE), 200);
1515 }
1516
1517 let env = DatabaseEnv::open(
1518 path,
1519 DatabaseEnvKind::RO,
1520 DatabaseArguments::new(ClientVersion::default()),
1521 )
1522 .expect(ERROR_DB_CREATION);
1523
1524 let result =
1526 env.view(|tx| tx.get::<PlainAccountState>(key).expect(ERROR_GET)).expect(ERROR_GET);
1527
1528 assert_eq!(result, Some(value))
1529 }
1530
1531 #[test]
1532 fn db_dup_sort() {
1533 let (_tempdir, env) = create_test_db(DatabaseEnvKind::RW);
1534 let key = address!("0xa2c122be93b0074270ebee7f6b7292c7deb45047");
1535
1536 let value00 = StorageEntry::default();
1538 env.update(|tx| tx.put::<PlainStorageState>(key, value00).expect(ERROR_PUT)).unwrap();
1539
1540 let value22 = StorageEntry { key: B256::with_last_byte(2), value: U256::from(2) };
1542 env.update(|tx| tx.put::<PlainStorageState>(key, value22).expect(ERROR_PUT)).unwrap();
1543
1544 let value11 = StorageEntry { key: B256::with_last_byte(1), value: U256::ONE };
1546 env.update(|tx| tx.put::<PlainStorageState>(key, value11).expect(ERROR_PUT)).unwrap();
1547
1548 {
1550 let tx = env.tx().expect(ERROR_INIT_TX);
1551 let mut cursor = tx.cursor_dup_read::<PlainStorageState>().unwrap();
1552
1553 assert_eq!(Some(value00), cursor.next_dup_val().unwrap());
1555 assert_eq!(Some(value11), cursor.next_dup_val().unwrap());
1556 assert_eq!(Some(value22), cursor.next_dup_val().unwrap());
1557 }
1558
1559 {
1561 let tx = env.tx().expect(ERROR_INIT_TX);
1562 let mut cursor = tx.cursor_dup_read::<PlainStorageState>().unwrap();
1563 let mut walker = cursor.walk_dup(Some(key), Some(B256::with_last_byte(1))).unwrap();
1564 assert_eq!(
1565 (key, value11),
1566 walker
1567 .next()
1568 .expect("element should exist.")
1569 .expect("should be able to retrieve it.")
1570 );
1571 }
1572 }
1573
1574 #[test]
1575 fn db_walk_dup_with_not_existing_key() {
1576 let (_tempdir, env) = create_test_db(DatabaseEnvKind::RW);
1577 let key = address!("0xa2c122be93b0074270ebee7f6b7292c7deb45047");
1578
1579 let value00 = StorageEntry::default();
1581 env.update(|tx| tx.put::<PlainStorageState>(key, value00).expect(ERROR_PUT)).unwrap();
1582
1583 let value22 = StorageEntry { key: B256::with_last_byte(2), value: U256::from(2) };
1585 env.update(|tx| tx.put::<PlainStorageState>(key, value22).expect(ERROR_PUT)).unwrap();
1586
1587 let value11 = StorageEntry { key: B256::with_last_byte(1), value: U256::ONE };
1589 env.update(|tx| tx.put::<PlainStorageState>(key, value11).expect(ERROR_PUT)).unwrap();
1590
1591 {
1593 let tx = env.tx().expect(ERROR_INIT_TX);
1594 let mut cursor = tx.cursor_dup_read::<PlainStorageState>().unwrap();
1595 let not_existing_key = Address::ZERO;
1596 let mut walker = cursor.walk_dup(Some(not_existing_key), None).unwrap();
1597 assert!(walker.next().is_none());
1598 }
1599 }
1600
1601 #[test]
1602 fn db_iterate_over_all_dup_values() {
1603 let (_tempdir, env) = create_test_db(DatabaseEnvKind::RW);
1604 let key1 = Address::repeat_byte(0x11);
1605 let key2 = Address::repeat_byte(0x22);
1606
1607 let value00 = StorageEntry::default();
1609 env.update(|tx| tx.put::<PlainStorageState>(key1, value00).expect(ERROR_PUT)).unwrap();
1610
1611 let value11 = StorageEntry { key: B256::with_last_byte(1), value: U256::ONE };
1613 env.update(|tx| tx.put::<PlainStorageState>(key1, value11).expect(ERROR_PUT)).unwrap();
1614
1615 let value22 = StorageEntry { key: B256::with_last_byte(2), value: U256::from(2) };
1617 env.update(|tx| tx.put::<PlainStorageState>(key2, value22).expect(ERROR_PUT)).unwrap();
1618
1619 {
1621 let tx = env.tx().expect(ERROR_INIT_TX);
1622 let mut cursor = tx.cursor_dup_read::<PlainStorageState>().unwrap();
1623 let mut walker = cursor.walk_dup(None, None).unwrap();
1624
1625 assert_eq!((key1, value00), walker.next().unwrap().unwrap());
1627 assert_eq!((key1, value11), walker.next().unwrap().unwrap());
1628 assert!(walker.next().is_none());
1631 }
1632
1633 {
1635 let tx = env.tx().expect(ERROR_INIT_TX);
1636 let mut cursor = tx.cursor_dup_read::<PlainStorageState>().unwrap();
1637 let first = cursor.first().unwrap().unwrap();
1638 let mut walker = cursor.walk(Some(first.0)).unwrap();
1639 assert_eq!((key1, value00), walker.next().unwrap().unwrap());
1640 assert_eq!((key1, value11), walker.next().unwrap().unwrap());
1641 assert_eq!((key2, value22), walker.next().unwrap().unwrap());
1642 }
1643 }
1644
1645 #[test]
1646 fn dup_value_with_same_subkey() {
1647 let (_tempdir, env) = create_test_db(DatabaseEnvKind::RW);
1648 let key1 = Address::repeat_byte(0x11);
1649 let key2 = Address::repeat_byte(0x22);
1650
1651 let value01 = StorageEntry { key: B256::with_last_byte(0), value: U256::ONE };
1653 env.update(|tx| tx.put::<PlainStorageState>(key1, value01).expect(ERROR_PUT)).unwrap();
1654
1655 let value00 = StorageEntry::default();
1657 env.update(|tx| tx.put::<PlainStorageState>(key1, value00).expect(ERROR_PUT)).unwrap();
1658
1659 let value22 = StorageEntry { key: B256::with_last_byte(2), value: U256::from(2) };
1661 env.update(|tx| tx.put::<PlainStorageState>(key2, value22).expect(ERROR_PUT)).unwrap();
1662
1663 {
1665 let tx = env.tx().expect(ERROR_INIT_TX);
1666 let mut cursor = tx.cursor_dup_read::<PlainStorageState>().unwrap();
1667 let first = cursor.first().unwrap().unwrap();
1668 let mut walker = cursor.walk(Some(first.0)).unwrap();
1669
1670 assert_eq!((key1, value00), walker.next().unwrap().unwrap());
1672 assert_eq!((key1, value01), walker.next().unwrap().unwrap());
1673 assert_eq!((key2, value22), walker.next().unwrap().unwrap());
1674 }
1675
1676 {
1678 let tx = env.tx().expect(ERROR_INIT_TX);
1679 let mut cursor = tx.cursor_dup_read::<PlainStorageState>().unwrap();
1680
1681 assert_eq!(value00, cursor.seek_by_key_subkey(key1, value00.key).unwrap().unwrap());
1683 assert_eq!(None, cursor.seek_by_key_subkey(key1, value22.key).unwrap());
1685 }
1686 }
1687
1688 #[test]
1689 fn db_sharded_key() {
1690 let (_tempdir, db) = create_test_db(DatabaseEnvKind::RW);
1691 let real_key = address!("0xa2c122be93b0074270ebee7f6b7292c7deb45047");
1692
1693 let shards = 5;
1694 for i in 1..=shards {
1695 let key = ShardedKey::new(real_key, if i == shards { u64::MAX } else { i * 100 });
1696 let list = IntegerList::new_pre_sorted([i * 100u64]);
1697
1698 db.update(|tx| tx.put::<AccountsHistory>(key.clone(), list.clone()).expect(""))
1699 .unwrap();
1700 }
1701
1702 {
1704 let tx = db.tx().expect(ERROR_INIT_TX);
1705 let mut cursor = tx.cursor_read::<AccountsHistory>().unwrap();
1706
1707 let mut walker = cursor.walk(Some(ShardedKey::new(real_key, 150))).unwrap();
1711 let (key, list) = walker
1712 .next()
1713 .expect("element should exist.")
1714 .expect("should be able to retrieve it.");
1715
1716 assert_eq!(ShardedKey::new(real_key, 200), key);
1717 let list200 = IntegerList::new_pre_sorted([200u64]);
1718 assert_eq!(list200, list);
1719 }
1720 {
1722 let tx = db.tx().expect(ERROR_INIT_TX);
1723 let mut cursor = tx.cursor_read::<AccountsHistory>().unwrap();
1724
1725 let _unknown = cursor.seek_exact(ShardedKey::new(real_key, u64::MAX)).unwrap();
1728 let (key, list) = cursor
1729 .prev()
1730 .expect("element should exist.")
1731 .expect("should be able to retrieve it.");
1732
1733 assert_eq!(ShardedKey::new(real_key, 400), key);
1734 let list400 = IntegerList::new_pre_sorted([400u64]);
1735 assert_eq!(list400, list);
1736 }
1737 }
1738
1739 #[test]
1740 fn db_tracks_table_dbis() {
1741 #[derive(Debug)]
1742 struct CustomTable;
1743
1744 impl Table for CustomTable {
1745 const NAME: &'static str = "CustomTable";
1746 const DUPSORT: bool = false;
1747
1748 type Key = u64;
1749 type Value = u64;
1750 }
1751
1752 impl TableInfo for CustomTable {
1753 fn name(&self) -> &'static str {
1754 Self::NAME
1755 }
1756
1757 fn is_dupsort(&self) -> bool {
1758 Self::DUPSORT
1759 }
1760 }
1761
1762 impl TableSet for CustomTable {
1763 fn tables() -> Box<dyn Iterator<Item = Box<dyn TableInfo>>> {
1764 Box::new(std::iter::once(Box::new(Self) as Box<dyn TableInfo>))
1765 }
1766 }
1767
1768 let (_tempdir, mut env) = create_test_db(DatabaseEnvKind::RW);
1769 env.create_and_track_tables_for::<CustomTable>().expect(ERROR_TABLE_CREATION);
1770 let env = env.with_metrics();
1771
1772 let tx = env.tx_mut().expect(ERROR_INIT_TX);
1773 for table in Tables::ALL {
1774 let dbi = tables_to_generic!(table, |T| tx.get_dbi::<T>()).unwrap();
1775 assert_eq!(env.dbis.get(Some(*table), table.name()), Some(dbi), "{table}");
1776 assert_eq!(tx.inner().open_db(Some(table.name())).unwrap().dbi(), dbi, "{table}");
1777 }
1778
1779 let dbi = tx.get_dbi::<CustomTable>().unwrap();
1780 assert_eq!(env.dbis.get(None, CustomTable::NAME), Some(dbi));
1781 assert_eq!(tx.inner().open_db(Some(CustomTable::NAME)).unwrap().dbi(), dbi);
1782 tx.put::<CustomTable>(1, 2).expect(ERROR_PUT);
1783 tx.cursor_write::<CustomTable>().unwrap().upsert(3, &4).expect(ERROR_UPSERT);
1784 tx.commit().expect(ERROR_COMMIT);
1785
1786 let tx = env.tx().expect(ERROR_INIT_TX);
1787 assert_eq!(tx.get::<CustomTable>(1).expect(ERROR_GET), Some(2));
1788 assert_eq!(tx.get::<CustomTable>(3).expect(ERROR_GET), Some(4));
1789 }
1790
1791 #[test]
1792 fn non_durable_sync_period() {
1793 let sync_period = |sync_mode| {
1794 let tempdir = tempfile::TempDir::new().expect(ERROR_TEMPDIR);
1795 let env = DatabaseEnv::open(
1796 tempdir.path(),
1797 DatabaseEnvKind::RW,
1798 DatabaseArguments::test().with_sync_mode(Some(sync_mode)),
1799 )
1800 .expect(ERROR_DB_CREATION);
1801 let mut period = 0;
1802 let rc = env.with_raw_env_ptr(|env| unsafe {
1804 ffi::mdbx_env_get_option(env, ffi::MDBX_opt_sync_period, &raw mut period)
1805 });
1806 assert_eq!(rc, ffi::MDBX_SUCCESS);
1807 Duration::from_secs_f64(period as f64 / 65536.0)
1809 };
1810
1811 assert_eq!(sync_period(SyncMode::Durable), Duration::ZERO);
1812 assert_eq!(sync_period(SyncMode::SafeNoSync), NON_DURABLE_SYNC_PERIOD);
1813 assert_eq!(sync_period(SyncMode::UtterlyNoSync), NON_DURABLE_SYNC_PERIOD);
1814 }
1815}