mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
958 lines
37 KiB
Rust
958 lines
37 KiB
Rust
//! Module that interacts with MDBX.
|
|
|
|
use crate::{
|
|
database::{Database, DatabaseGAT},
|
|
tables::{TableType, Tables},
|
|
utils::default_page_size,
|
|
DatabaseError,
|
|
};
|
|
use reth_libmdbx::{
|
|
DatabaseFlags, Environment, EnvironmentFlags, EnvironmentKind, Geometry, Mode, PageSize,
|
|
SyncMode, RO, RW,
|
|
};
|
|
use std::{ops::Deref, path::Path};
|
|
use tx::Tx;
|
|
|
|
pub mod cursor;
|
|
pub mod tx;
|
|
|
|
const GIGABYTE: usize = 1024 * 1024 * 1024;
|
|
const TERABYTE: usize = GIGABYTE * 1024;
|
|
|
|
/// MDBX allows up to 32767 readers (`MDBX_READERS_LIMIT`), but we limit it to slightly below that
|
|
const DEFAULT_MAX_READERS: u64 = 32_000;
|
|
|
|
/// Environment used when opening a MDBX environment. RO/RW.
|
|
#[derive(Debug)]
|
|
pub enum EnvKind {
|
|
/// Read-only MDBX environment.
|
|
RO,
|
|
/// Read-write MDBX environment.
|
|
RW,
|
|
}
|
|
|
|
/// Wrapper for the libmdbx environment.
|
|
#[derive(Debug)]
|
|
pub struct Env<E: EnvironmentKind> {
|
|
/// Libmdbx-sys environment.
|
|
pub inner: Environment<E>,
|
|
}
|
|
|
|
impl<'a, E: EnvironmentKind> DatabaseGAT<'a> for Env<E> {
|
|
type TX = tx::Tx<'a, RO, E>;
|
|
type TXMut = tx::Tx<'a, RW, E>;
|
|
}
|
|
|
|
impl<E: EnvironmentKind> Database for Env<E> {
|
|
fn tx(&self) -> Result<<Self as DatabaseGAT<'_>>::TX, DatabaseError> {
|
|
Ok(Tx::new(
|
|
self.inner.begin_ro_txn().map_err(|e| DatabaseError::InitTransaction(e.into()))?,
|
|
))
|
|
}
|
|
|
|
fn tx_mut(&self) -> Result<<Self as DatabaseGAT<'_>>::TXMut, DatabaseError> {
|
|
Ok(Tx::new(
|
|
self.inner.begin_rw_txn().map_err(|e| DatabaseError::InitTransaction(e.into()))?,
|
|
))
|
|
}
|
|
}
|
|
|
|
impl<E: EnvironmentKind> Env<E> {
|
|
/// Opens the database at the specified path with the given `EnvKind`.
|
|
///
|
|
/// It does not create the tables, for that call [`Env::create_tables`].
|
|
pub fn open(path: &Path, kind: EnvKind) -> Result<Env<E>, DatabaseError> {
|
|
let mode = match kind {
|
|
EnvKind::RO => Mode::ReadOnly,
|
|
EnvKind::RW => Mode::ReadWrite { sync_mode: SyncMode::Durable },
|
|
};
|
|
|
|
let env = Env {
|
|
inner: Environment::new()
|
|
.set_max_dbs(Tables::ALL.len())
|
|
.set_geometry(Geometry {
|
|
// Maximum database size of 4 terabytes
|
|
size: Some(0..(4 * TERABYTE)),
|
|
// We grow the database in increments of 4 gigabytes
|
|
growth_step: Some(4 * GIGABYTE as isize),
|
|
// The database never shrinks
|
|
shrink_threshold: None,
|
|
page_size: Some(PageSize::Set(default_page_size())),
|
|
})
|
|
.set_flags(EnvironmentFlags {
|
|
mode,
|
|
// We disable readahead because it improves performance for linear scans, but
|
|
// worsens it for random access (which is our access pattern outside of sync)
|
|
no_rdahead: true,
|
|
coalesce: true,
|
|
..Default::default()
|
|
})
|
|
// configure more readers
|
|
.set_max_readers(DEFAULT_MAX_READERS)
|
|
.open(path)
|
|
.map_err(|e| DatabaseError::FailedToOpen(e.into()))?,
|
|
};
|
|
|
|
Ok(env)
|
|
}
|
|
|
|
/// Creates all the defined tables, if necessary.
|
|
pub fn create_tables(&self) -> Result<(), DatabaseError> {
|
|
let tx = self.inner.begin_rw_txn().map_err(|e| DatabaseError::InitTransaction(e.into()))?;
|
|
|
|
for table in Tables::ALL {
|
|
let flags = match table.table_type() {
|
|
TableType::Table => DatabaseFlags::default(),
|
|
TableType::DupSort => DatabaseFlags::DUP_SORT,
|
|
};
|
|
|
|
tx.create_db(Some(table.name()), flags)
|
|
.map_err(|e| DatabaseError::TableCreation(e.into()))?;
|
|
}
|
|
|
|
tx.commit().map_err(|e| DatabaseError::Commit(e.into()))?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl<E: EnvironmentKind> Deref for Env<E> {
|
|
type Target = reth_libmdbx::Environment<E>;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.inner
|
|
}
|
|
}
|
|
|
|
/// Collection of database test utilities
|
|
#[cfg(any(test, feature = "test-utils"))]
|
|
pub mod test_utils {
|
|
use super::*;
|
|
use reth_libmdbx::WriteMap;
|
|
use std::sync::Arc;
|
|
|
|
/// Error during database creation
|
|
pub const ERROR_DB_CREATION: &str = "Not able to create the mdbx file.";
|
|
/// Error during table creation
|
|
pub const ERROR_TABLE_CREATION: &str = "Not able to create tables in the database.";
|
|
/// Error during tempdir creation
|
|
pub const ERROR_TEMPDIR: &str = "Not able to create a temporary directory.";
|
|
|
|
/// Create rw database for testing
|
|
pub fn create_test_rw_db() -> Arc<Env<WriteMap>> {
|
|
create_test_db(EnvKind::RW)
|
|
}
|
|
/// Create database for testing
|
|
pub fn create_test_db<E: EnvironmentKind>(kind: EnvKind) -> Arc<Env<E>> {
|
|
Arc::new(create_test_db_with_path(
|
|
kind,
|
|
&tempfile::TempDir::new().expect(ERROR_TEMPDIR).into_path(),
|
|
))
|
|
}
|
|
|
|
/// Create database for testing with specified path
|
|
pub fn create_test_db_with_path<E: EnvironmentKind>(kind: EnvKind, path: &Path) -> Env<E> {
|
|
let env = Env::<E>::open(path, kind).expect(ERROR_DB_CREATION);
|
|
env.create_tables().expect(ERROR_TABLE_CREATION);
|
|
env
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::{test_utils, Env, EnvKind};
|
|
use crate::{
|
|
cursor::{DbCursorRO, DbCursorRW, DbDupCursorRO, DbDupCursorRW, ReverseWalker, Walker},
|
|
database::Database,
|
|
models::{AccountBeforeTx, ShardedKey},
|
|
tables::{AccountHistory, CanonicalHeaders, Headers, PlainAccountState, PlainStorageState},
|
|
transaction::{DbTx, DbTxMut},
|
|
AccountChangeSet, DatabaseError,
|
|
};
|
|
use reth_libmdbx::{NoWriteMap, WriteMap};
|
|
use reth_primitives::{Account, Address, Header, IntegerList, StorageEntry, H160, H256, U256};
|
|
use std::{str::FromStr, sync::Arc};
|
|
use tempfile::TempDir;
|
|
|
|
const ERROR_DB_CREATION: &str = "Not able to create the mdbx file.";
|
|
const ERROR_PUT: &str = "Not able to insert value into table.";
|
|
const ERROR_APPEND: &str = "Not able to append the value to the table.";
|
|
const ERROR_UPSERT: &str = "Not able to upsert the value to the table.";
|
|
const ERROR_GET: &str = "Not able to get value from table.";
|
|
const ERROR_COMMIT: &str = "Not able to commit transaction.";
|
|
const ERROR_RETURN_VALUE: &str = "Mismatching result.";
|
|
const ERROR_INIT_TX: &str = "Failed to create a MDBX transaction.";
|
|
const ERROR_ETH_ADDRESS: &str = "Invalid address.";
|
|
|
|
#[test]
|
|
fn db_creation() {
|
|
test_utils::create_test_db::<NoWriteMap>(EnvKind::RW);
|
|
}
|
|
|
|
#[test]
|
|
fn db_manual_put_get() {
|
|
let env = test_utils::create_test_db::<NoWriteMap>(EnvKind::RW);
|
|
|
|
let value = Header::default();
|
|
let key = 1u64;
|
|
|
|
// PUT
|
|
let tx = env.tx_mut().expect(ERROR_INIT_TX);
|
|
tx.put::<Headers>(key, value.clone()).expect(ERROR_PUT);
|
|
tx.commit().expect(ERROR_COMMIT);
|
|
|
|
// GET
|
|
let tx = env.tx().expect(ERROR_INIT_TX);
|
|
let result = tx.get::<Headers>(key).expect(ERROR_GET);
|
|
assert!(result.expect(ERROR_RETURN_VALUE) == value);
|
|
tx.commit().expect(ERROR_COMMIT);
|
|
}
|
|
|
|
#[test]
|
|
fn db_cursor_walk() {
|
|
let env = test_utils::create_test_db::<NoWriteMap>(EnvKind::RW);
|
|
|
|
let value = Header::default();
|
|
let key = 1u64;
|
|
|
|
// PUT
|
|
let tx = env.tx_mut().expect(ERROR_INIT_TX);
|
|
tx.put::<Headers>(key, value.clone()).expect(ERROR_PUT);
|
|
tx.commit().expect(ERROR_COMMIT);
|
|
|
|
// Cursor
|
|
let tx = env.tx().expect(ERROR_INIT_TX);
|
|
let mut cursor = tx.cursor_read::<Headers>().unwrap();
|
|
|
|
let first = cursor.first().unwrap();
|
|
assert!(first.is_some(), "First should be our put");
|
|
|
|
// Walk
|
|
let walk = cursor.walk(Some(key)).unwrap();
|
|
let first = walk.into_iter().next().unwrap().unwrap();
|
|
assert_eq!(first.1, value, "First next should be put value");
|
|
}
|
|
|
|
#[test]
|
|
fn db_cursor_walk_range() {
|
|
let db: Arc<Env<WriteMap>> = test_utils::create_test_db(EnvKind::RW);
|
|
|
|
// PUT (0, 0), (1, 0), (2, 0), (3, 0)
|
|
let tx = db.tx_mut().expect(ERROR_INIT_TX);
|
|
vec![0, 1, 2, 3]
|
|
.into_iter()
|
|
.try_for_each(|key| tx.put::<CanonicalHeaders>(key, H256::zero()))
|
|
.expect(ERROR_PUT);
|
|
tx.commit().expect(ERROR_COMMIT);
|
|
|
|
let tx = db.tx().expect(ERROR_INIT_TX);
|
|
let mut cursor = tx.cursor_read::<CanonicalHeaders>().unwrap();
|
|
|
|
// [1, 3)
|
|
let mut walker = cursor.walk_range(1..3).unwrap();
|
|
assert_eq!(walker.next(), Some(Ok((1, H256::zero()))));
|
|
assert_eq!(walker.next(), Some(Ok((2, H256::zero()))));
|
|
assert_eq!(walker.next(), None);
|
|
// next() returns None after walker is done
|
|
assert_eq!(walker.next(), None);
|
|
|
|
// [1, 2]
|
|
let mut walker = cursor.walk_range(1..=2).unwrap();
|
|
assert_eq!(walker.next(), Some(Ok((1, H256::zero()))));
|
|
assert_eq!(walker.next(), Some(Ok((2, H256::zero()))));
|
|
// next() returns None after walker is done
|
|
assert_eq!(walker.next(), None);
|
|
|
|
// [1, ∞)
|
|
let mut walker = cursor.walk_range(1..).unwrap();
|
|
assert_eq!(walker.next(), Some(Ok((1, H256::zero()))));
|
|
assert_eq!(walker.next(), Some(Ok((2, H256::zero()))));
|
|
assert_eq!(walker.next(), Some(Ok((3, H256::zero()))));
|
|
// next() returns None after walker is done
|
|
assert_eq!(walker.next(), None);
|
|
|
|
// [2, 4)
|
|
let mut walker = cursor.walk_range(2..4).unwrap();
|
|
assert_eq!(walker.next(), Some(Ok((2, H256::zero()))));
|
|
assert_eq!(walker.next(), Some(Ok((3, H256::zero()))));
|
|
assert_eq!(walker.next(), None);
|
|
// next() returns None after walker is done
|
|
assert_eq!(walker.next(), None);
|
|
|
|
// (∞, 3)
|
|
let mut walker = cursor.walk_range(..3).unwrap();
|
|
assert_eq!(walker.next(), Some(Ok((0, H256::zero()))));
|
|
assert_eq!(walker.next(), Some(Ok((1, H256::zero()))));
|
|
assert_eq!(walker.next(), Some(Ok((2, H256::zero()))));
|
|
// next() returns None after walker is done
|
|
assert_eq!(walker.next(), None);
|
|
|
|
// (∞, ∞)
|
|
let mut walker = cursor.walk_range(..).unwrap();
|
|
assert_eq!(walker.next(), Some(Ok((0, H256::zero()))));
|
|
assert_eq!(walker.next(), Some(Ok((1, H256::zero()))));
|
|
assert_eq!(walker.next(), Some(Ok((2, H256::zero()))));
|
|
assert_eq!(walker.next(), Some(Ok((3, H256::zero()))));
|
|
// next() returns None after walker is done
|
|
assert_eq!(walker.next(), None);
|
|
}
|
|
|
|
#[test]
|
|
fn db_cursor_walk_range_on_dup_table() {
|
|
let db: Arc<Env<WriteMap>> = test_utils::create_test_db(EnvKind::RW);
|
|
|
|
let address0 = Address::zero();
|
|
let address1 = Address::from_low_u64_be(1);
|
|
let address2 = Address::from_low_u64_be(2);
|
|
|
|
let tx = db.tx_mut().expect(ERROR_INIT_TX);
|
|
tx.put::<AccountChangeSet>(0, AccountBeforeTx { address: address0, info: None })
|
|
.expect(ERROR_PUT);
|
|
tx.put::<AccountChangeSet>(0, AccountBeforeTx { address: address1, info: None })
|
|
.expect(ERROR_PUT);
|
|
tx.put::<AccountChangeSet>(0, AccountBeforeTx { address: address2, info: None })
|
|
.expect(ERROR_PUT);
|
|
tx.put::<AccountChangeSet>(1, AccountBeforeTx { address: address0, info: None })
|
|
.expect(ERROR_PUT);
|
|
tx.put::<AccountChangeSet>(1, AccountBeforeTx { address: address1, info: None })
|
|
.expect(ERROR_PUT);
|
|
tx.put::<AccountChangeSet>(1, AccountBeforeTx { address: address2, info: None })
|
|
.expect(ERROR_PUT);
|
|
tx.put::<AccountChangeSet>(2, AccountBeforeTx { address: address0, info: None }) // <- should not be returned by the walker
|
|
.expect(ERROR_PUT);
|
|
tx.commit().expect(ERROR_COMMIT);
|
|
|
|
let tx = db.tx().expect(ERROR_INIT_TX);
|
|
let mut cursor = tx.cursor_read::<AccountChangeSet>().unwrap();
|
|
|
|
let entries = cursor.walk_range(..).unwrap().collect::<Result<Vec<_>, _>>().unwrap();
|
|
assert_eq!(entries.len(), 7);
|
|
|
|
let mut walker = cursor.walk_range(0..=1).unwrap();
|
|
assert_eq!(walker.next(), Some(Ok((0, AccountBeforeTx { address: address0, info: None }))));
|
|
assert_eq!(walker.next(), Some(Ok((0, AccountBeforeTx { address: address1, info: None }))));
|
|
assert_eq!(walker.next(), Some(Ok((0, AccountBeforeTx { address: address2, info: None }))));
|
|
assert_eq!(walker.next(), Some(Ok((1, AccountBeforeTx { address: address0, info: None }))));
|
|
assert_eq!(walker.next(), Some(Ok((1, AccountBeforeTx { address: address1, info: None }))));
|
|
assert_eq!(walker.next(), Some(Ok((1, AccountBeforeTx { address: address2, info: None }))));
|
|
assert_eq!(walker.next(), None);
|
|
}
|
|
|
|
#[allow(clippy::reversed_empty_ranges)]
|
|
#[test]
|
|
fn db_cursor_walk_range_invalid() {
|
|
let db: Arc<Env<WriteMap>> = test_utils::create_test_db(EnvKind::RW);
|
|
|
|
// PUT (0, 0), (1, 0), (2, 0), (3, 0)
|
|
let tx = db.tx_mut().expect(ERROR_INIT_TX);
|
|
vec![0, 1, 2, 3]
|
|
.into_iter()
|
|
.try_for_each(|key| tx.put::<CanonicalHeaders>(key, H256::zero()))
|
|
.expect(ERROR_PUT);
|
|
tx.commit().expect(ERROR_COMMIT);
|
|
|
|
let tx = db.tx().expect(ERROR_INIT_TX);
|
|
let mut cursor = tx.cursor_read::<CanonicalHeaders>().unwrap();
|
|
|
|
// start bound greater than end bound
|
|
let mut res = cursor.walk_range(3..1).unwrap();
|
|
assert_eq!(res.next(), None);
|
|
|
|
// start bound greater than end bound
|
|
let mut res = cursor.walk_range(15..=2).unwrap();
|
|
assert_eq!(res.next(), None);
|
|
|
|
// returning nothing
|
|
let mut walker = cursor.walk_range(1..1).unwrap();
|
|
assert_eq!(walker.next(), None);
|
|
}
|
|
|
|
#[test]
|
|
fn db_walker() {
|
|
let db: Arc<Env<WriteMap>> = test_utils::create_test_db(EnvKind::RW);
|
|
|
|
// PUT (0, 0), (1, 0), (3, 0)
|
|
let tx = db.tx_mut().expect(ERROR_INIT_TX);
|
|
vec![0, 1, 3]
|
|
.into_iter()
|
|
.try_for_each(|key| tx.put::<CanonicalHeaders>(key, H256::zero()))
|
|
.expect(ERROR_PUT);
|
|
tx.commit().expect(ERROR_COMMIT);
|
|
|
|
let tx = db.tx().expect(ERROR_INIT_TX);
|
|
let mut cursor = tx.cursor_read::<CanonicalHeaders>().unwrap();
|
|
|
|
let mut walker = Walker::new(&mut cursor, None);
|
|
|
|
assert_eq!(walker.next(), Some(Ok((0, H256::zero()))));
|
|
assert_eq!(walker.next(), Some(Ok((1, H256::zero()))));
|
|
assert_eq!(walker.next(), Some(Ok((3, H256::zero()))));
|
|
assert_eq!(walker.next(), None);
|
|
|
|
// transform to ReverseWalker
|
|
let mut reverse_walker = walker.rev();
|
|
assert_eq!(reverse_walker.next(), Some(Ok((3, H256::zero()))));
|
|
assert_eq!(reverse_walker.next(), Some(Ok((1, H256::zero()))));
|
|
assert_eq!(reverse_walker.next(), Some(Ok((0, H256::zero()))));
|
|
assert_eq!(reverse_walker.next(), None);
|
|
}
|
|
|
|
#[test]
|
|
fn db_reverse_walker() {
|
|
let db: Arc<Env<WriteMap>> = test_utils::create_test_db(EnvKind::RW);
|
|
|
|
// PUT (0, 0), (1, 0), (3, 0)
|
|
let tx = db.tx_mut().expect(ERROR_INIT_TX);
|
|
vec![0, 1, 3]
|
|
.into_iter()
|
|
.try_for_each(|key| tx.put::<CanonicalHeaders>(key, H256::zero()))
|
|
.expect(ERROR_PUT);
|
|
tx.commit().expect(ERROR_COMMIT);
|
|
|
|
let tx = db.tx().expect(ERROR_INIT_TX);
|
|
let mut cursor = tx.cursor_read::<CanonicalHeaders>().unwrap();
|
|
|
|
let mut reverse_walker = ReverseWalker::new(&mut cursor, None);
|
|
|
|
assert_eq!(reverse_walker.next(), Some(Ok((3, H256::zero()))));
|
|
assert_eq!(reverse_walker.next(), Some(Ok((1, H256::zero()))));
|
|
assert_eq!(reverse_walker.next(), Some(Ok((0, H256::zero()))));
|
|
assert_eq!(reverse_walker.next(), None);
|
|
|
|
// transform to Walker
|
|
let mut walker = reverse_walker.forward();
|
|
assert_eq!(walker.next(), Some(Ok((0, H256::zero()))));
|
|
assert_eq!(walker.next(), Some(Ok((1, H256::zero()))));
|
|
assert_eq!(walker.next(), Some(Ok((3, H256::zero()))));
|
|
assert_eq!(walker.next(), None);
|
|
}
|
|
|
|
#[test]
|
|
fn db_walk_back() {
|
|
let db: Arc<Env<WriteMap>> = test_utils::create_test_db(EnvKind::RW);
|
|
|
|
// PUT (0, 0), (1, 0), (3, 0)
|
|
let tx = db.tx_mut().expect(ERROR_INIT_TX);
|
|
vec![0, 1, 3]
|
|
.into_iter()
|
|
.try_for_each(|key| tx.put::<CanonicalHeaders>(key, H256::zero()))
|
|
.expect(ERROR_PUT);
|
|
tx.commit().expect(ERROR_COMMIT);
|
|
|
|
let tx = db.tx().expect(ERROR_INIT_TX);
|
|
let mut cursor = tx.cursor_read::<CanonicalHeaders>().unwrap();
|
|
|
|
let mut reverse_walker = cursor.walk_back(Some(1)).unwrap();
|
|
assert_eq!(reverse_walker.next(), Some(Ok((1, H256::zero()))));
|
|
assert_eq!(reverse_walker.next(), Some(Ok((0, H256::zero()))));
|
|
assert_eq!(reverse_walker.next(), None);
|
|
|
|
let mut reverse_walker = cursor.walk_back(Some(2)).unwrap();
|
|
assert_eq!(reverse_walker.next(), Some(Ok((3, H256::zero()))));
|
|
assert_eq!(reverse_walker.next(), Some(Ok((1, H256::zero()))));
|
|
assert_eq!(reverse_walker.next(), Some(Ok((0, H256::zero()))));
|
|
assert_eq!(reverse_walker.next(), None);
|
|
|
|
let mut reverse_walker = cursor.walk_back(Some(4)).unwrap();
|
|
assert_eq!(reverse_walker.next(), Some(Ok((3, H256::zero()))));
|
|
assert_eq!(reverse_walker.next(), Some(Ok((1, H256::zero()))));
|
|
assert_eq!(reverse_walker.next(), Some(Ok((0, H256::zero()))));
|
|
assert_eq!(reverse_walker.next(), None);
|
|
|
|
let mut reverse_walker = cursor.walk_back(None).unwrap();
|
|
assert_eq!(reverse_walker.next(), Some(Ok((3, H256::zero()))));
|
|
assert_eq!(reverse_walker.next(), Some(Ok((1, H256::zero()))));
|
|
assert_eq!(reverse_walker.next(), Some(Ok((0, H256::zero()))));
|
|
assert_eq!(reverse_walker.next(), None);
|
|
}
|
|
|
|
#[test]
|
|
fn db_cursor_seek_exact_or_previous_key() {
|
|
let db: Arc<Env<WriteMap>> = test_utils::create_test_db(EnvKind::RW);
|
|
|
|
// PUT
|
|
let tx = db.tx_mut().expect(ERROR_INIT_TX);
|
|
vec![0, 1, 3]
|
|
.into_iter()
|
|
.try_for_each(|key| tx.put::<CanonicalHeaders>(key, H256::zero()))
|
|
.expect(ERROR_PUT);
|
|
tx.commit().expect(ERROR_COMMIT);
|
|
|
|
// Cursor
|
|
let missing_key = 2;
|
|
let tx = db.tx().expect(ERROR_INIT_TX);
|
|
let mut cursor = tx.cursor_read::<CanonicalHeaders>().unwrap();
|
|
assert_eq!(cursor.current(), Ok(None));
|
|
|
|
// Seek exact
|
|
let exact = cursor.seek_exact(missing_key).unwrap();
|
|
assert_eq!(exact, None);
|
|
assert_eq!(cursor.current(), Ok(Some((missing_key + 1, H256::zero()))));
|
|
assert_eq!(cursor.prev(), Ok(Some((missing_key - 1, H256::zero()))));
|
|
assert_eq!(cursor.prev(), Ok(Some((missing_key - 2, H256::zero()))));
|
|
}
|
|
|
|
#[test]
|
|
fn db_cursor_insert() {
|
|
let db: Arc<Env<WriteMap>> = test_utils::create_test_db(EnvKind::RW);
|
|
|
|
// PUT
|
|
let tx = db.tx_mut().expect(ERROR_INIT_TX);
|
|
vec![0, 1, 3, 4, 5]
|
|
.into_iter()
|
|
.try_for_each(|key| tx.put::<CanonicalHeaders>(key, H256::zero()))
|
|
.expect(ERROR_PUT);
|
|
tx.commit().expect(ERROR_COMMIT);
|
|
|
|
let key_to_insert = 2;
|
|
let tx = db.tx_mut().expect(ERROR_INIT_TX);
|
|
let mut cursor = tx.cursor_write::<CanonicalHeaders>().unwrap();
|
|
|
|
// INSERT
|
|
assert_eq!(cursor.insert(key_to_insert, H256::zero()), Ok(()));
|
|
assert_eq!(cursor.current(), Ok(Some((key_to_insert, H256::zero()))));
|
|
|
|
// INSERT (failure)
|
|
assert_eq!(cursor.insert(key_to_insert, H256::zero()), Err(DatabaseError::Write(-30799)));
|
|
assert_eq!(cursor.current(), Ok(Some((key_to_insert, H256::zero()))));
|
|
|
|
tx.commit().expect(ERROR_COMMIT);
|
|
|
|
// Confirm the result
|
|
let tx = db.tx().expect(ERROR_INIT_TX);
|
|
let mut cursor = tx.cursor_read::<CanonicalHeaders>().unwrap();
|
|
let res = cursor.walk(None).unwrap().map(|res| res.unwrap().0).collect::<Vec<_>>();
|
|
assert_eq!(res, vec![0, 1, 2, 3, 4, 5]);
|
|
tx.commit().expect(ERROR_COMMIT);
|
|
}
|
|
|
|
#[test]
|
|
fn db_cursor_insert_dup() {
|
|
let db: Arc<Env<WriteMap>> = test_utils::create_test_db(EnvKind::RW);
|
|
let tx = db.tx_mut().expect(ERROR_INIT_TX);
|
|
|
|
let mut dup_cursor = tx.cursor_dup_write::<PlainStorageState>().unwrap();
|
|
let key = Address::random();
|
|
let subkey1 = H256::random();
|
|
let subkey2 = H256::random();
|
|
|
|
let entry1 = StorageEntry { key: subkey1, value: U256::ZERO };
|
|
assert!(dup_cursor.insert(key, entry1).is_ok());
|
|
|
|
// Can't insert
|
|
let entry2 = StorageEntry { key: subkey2, value: U256::ZERO };
|
|
assert!(dup_cursor.insert(key, entry2).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn db_cursor_delete_current_non_existent() {
|
|
let db: Arc<Env<WriteMap>> = test_utils::create_test_db(EnvKind::RW);
|
|
let tx = db.tx_mut().expect(ERROR_INIT_TX);
|
|
|
|
let key1 = Address::from_low_u64_be(1);
|
|
let key2 = Address::from_low_u64_be(2);
|
|
let key3 = Address::from_low_u64_be(3);
|
|
let mut cursor = tx.cursor_write::<PlainAccountState>().unwrap();
|
|
|
|
assert!(cursor.insert(key1, Account::default()).is_ok());
|
|
assert!(cursor.insert(key2, Account::default()).is_ok());
|
|
assert!(cursor.insert(key3, Account::default()).is_ok());
|
|
|
|
// Seek & delete key2
|
|
cursor.seek_exact(key2).unwrap();
|
|
assert_eq!(cursor.delete_current(), Ok(()));
|
|
assert_eq!(cursor.seek_exact(key2), Ok(None));
|
|
|
|
// Seek & delete key2 again
|
|
assert_eq!(cursor.seek_exact(key2), Ok(None));
|
|
assert_eq!(cursor.delete_current(), Ok(()));
|
|
// Assert that key1 is still there
|
|
assert_eq!(cursor.seek_exact(key1), Ok(Some((key1, Account::default()))));
|
|
// Assert that key3 was deleted
|
|
assert_eq!(cursor.seek_exact(key3), Ok(None));
|
|
}
|
|
|
|
#[test]
|
|
fn db_cursor_insert_wherever_cursor_is() {
|
|
let db: Arc<Env<WriteMap>> = test_utils::create_test_db(EnvKind::RW);
|
|
let tx = db.tx_mut().expect(ERROR_INIT_TX);
|
|
|
|
// PUT
|
|
vec![0, 1, 3, 5, 7, 9]
|
|
.into_iter()
|
|
.try_for_each(|key| tx.put::<CanonicalHeaders>(key, H256::zero()))
|
|
.expect(ERROR_PUT);
|
|
tx.commit().expect(ERROR_COMMIT);
|
|
|
|
let tx = db.tx_mut().expect(ERROR_INIT_TX);
|
|
let mut cursor = tx.cursor_write::<CanonicalHeaders>().unwrap();
|
|
|
|
// INSERT (cursor starts at last)
|
|
cursor.last().unwrap();
|
|
assert_eq!(cursor.current(), Ok(Some((9, H256::zero()))));
|
|
|
|
for pos in (2..=8).step_by(2) {
|
|
assert_eq!(cursor.insert(pos, H256::zero()), Ok(()));
|
|
assert_eq!(cursor.current(), Ok(Some((pos, H256::zero()))));
|
|
}
|
|
tx.commit().expect(ERROR_COMMIT);
|
|
|
|
// Confirm the result
|
|
let tx = db.tx().expect(ERROR_INIT_TX);
|
|
let mut cursor = tx.cursor_read::<CanonicalHeaders>().unwrap();
|
|
let res = cursor.walk(None).unwrap().map(|res| res.unwrap().0).collect::<Vec<_>>();
|
|
assert_eq!(res, vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
|
tx.commit().expect(ERROR_COMMIT);
|
|
}
|
|
|
|
#[test]
|
|
fn db_cursor_append() {
|
|
let db: Arc<Env<WriteMap>> = test_utils::create_test_db(EnvKind::RW);
|
|
|
|
// PUT
|
|
let tx = db.tx_mut().expect(ERROR_INIT_TX);
|
|
vec![0, 1, 2, 3, 4]
|
|
.into_iter()
|
|
.try_for_each(|key| tx.put::<CanonicalHeaders>(key, H256::zero()))
|
|
.expect(ERROR_PUT);
|
|
tx.commit().expect(ERROR_COMMIT);
|
|
|
|
// APPEND
|
|
let key_to_append = 5;
|
|
let tx = db.tx_mut().expect(ERROR_INIT_TX);
|
|
let mut cursor = tx.cursor_write::<CanonicalHeaders>().unwrap();
|
|
assert_eq!(cursor.append(key_to_append, H256::zero()), Ok(()));
|
|
tx.commit().expect(ERROR_COMMIT);
|
|
|
|
// Confirm the result
|
|
let tx = db.tx().expect(ERROR_INIT_TX);
|
|
let mut cursor = tx.cursor_read::<CanonicalHeaders>().unwrap();
|
|
let res = cursor.walk(None).unwrap().map(|res| res.unwrap().0).collect::<Vec<_>>();
|
|
assert_eq!(res, vec![0, 1, 2, 3, 4, 5]);
|
|
tx.commit().expect(ERROR_COMMIT);
|
|
}
|
|
|
|
#[test]
|
|
fn db_cursor_append_failure() {
|
|
let db: Arc<Env<WriteMap>> = test_utils::create_test_db(EnvKind::RW);
|
|
|
|
// PUT
|
|
let tx = db.tx_mut().expect(ERROR_INIT_TX);
|
|
vec![0, 1, 3, 4, 5]
|
|
.into_iter()
|
|
.try_for_each(|key| tx.put::<CanonicalHeaders>(key, H256::zero()))
|
|
.expect(ERROR_PUT);
|
|
tx.commit().expect(ERROR_COMMIT);
|
|
|
|
// APPEND
|
|
let key_to_append = 2;
|
|
let tx = db.tx_mut().expect(ERROR_INIT_TX);
|
|
let mut cursor = tx.cursor_write::<CanonicalHeaders>().unwrap();
|
|
assert_eq!(cursor.append(key_to_append, H256::zero()), Err(DatabaseError::Write(-30418)));
|
|
assert_eq!(cursor.current(), Ok(Some((5, H256::zero())))); // the end of table
|
|
tx.commit().expect(ERROR_COMMIT);
|
|
|
|
// Confirm the result
|
|
let tx = db.tx().expect(ERROR_INIT_TX);
|
|
let mut cursor = tx.cursor_read::<CanonicalHeaders>().unwrap();
|
|
let res = cursor.walk(None).unwrap().map(|res| res.unwrap().0).collect::<Vec<_>>();
|
|
assert_eq!(res, vec![0, 1, 3, 4, 5]);
|
|
tx.commit().expect(ERROR_COMMIT);
|
|
}
|
|
|
|
#[test]
|
|
fn db_cursor_upsert() {
|
|
let db: Arc<Env<WriteMap>> = test_utils::create_test_db(EnvKind::RW);
|
|
let tx = db.tx_mut().expect(ERROR_INIT_TX);
|
|
|
|
let mut cursor = tx.cursor_write::<PlainAccountState>().unwrap();
|
|
let key = Address::random();
|
|
|
|
let account = Account::default();
|
|
cursor.upsert(key, account).expect(ERROR_UPSERT);
|
|
assert_eq!(cursor.seek_exact(key), Ok(Some((key, account))));
|
|
|
|
let account = Account { nonce: 1, ..Default::default() };
|
|
cursor.upsert(key, account).expect(ERROR_UPSERT);
|
|
assert_eq!(cursor.seek_exact(key), Ok(Some((key, account))));
|
|
|
|
let account = Account { nonce: 2, ..Default::default() };
|
|
cursor.upsert(key, account).expect(ERROR_UPSERT);
|
|
assert_eq!(cursor.seek_exact(key), Ok(Some((key, account))));
|
|
|
|
let mut dup_cursor = tx.cursor_dup_write::<PlainStorageState>().unwrap();
|
|
let subkey = H256::random();
|
|
|
|
let value = U256::from(1);
|
|
let entry1 = StorageEntry { key: subkey, value };
|
|
dup_cursor.upsert(key, entry1).expect(ERROR_UPSERT);
|
|
assert_eq!(dup_cursor.seek_by_key_subkey(key, subkey), Ok(Some(entry1)));
|
|
|
|
let value = U256::from(2);
|
|
let entry2 = StorageEntry { key: subkey, value };
|
|
dup_cursor.upsert(key, entry2).expect(ERROR_UPSERT);
|
|
assert_eq!(dup_cursor.seek_by_key_subkey(key, subkey), Ok(Some(entry1)));
|
|
assert_eq!(dup_cursor.next_dup_val(), Ok(Some(entry2)));
|
|
}
|
|
|
|
#[test]
|
|
fn db_cursor_dupsort_append() {
|
|
let db: Arc<Env<WriteMap>> = test_utils::create_test_db(EnvKind::RW);
|
|
|
|
let transition_id = 2;
|
|
|
|
let tx = db.tx_mut().expect(ERROR_INIT_TX);
|
|
let mut cursor = tx.cursor_write::<AccountChangeSet>().unwrap();
|
|
vec![0, 1, 3, 4, 5]
|
|
.into_iter()
|
|
.try_for_each(|val| {
|
|
cursor.append(
|
|
transition_id,
|
|
AccountBeforeTx { address: Address::from_low_u64_be(val), info: None },
|
|
)
|
|
})
|
|
.expect(ERROR_APPEND);
|
|
tx.commit().expect(ERROR_COMMIT);
|
|
|
|
// APPEND DUP & APPEND
|
|
let subkey_to_append = 2;
|
|
let tx = db.tx_mut().expect(ERROR_INIT_TX);
|
|
let mut cursor = tx.cursor_write::<AccountChangeSet>().unwrap();
|
|
assert_eq!(
|
|
cursor.append_dup(
|
|
transition_id,
|
|
AccountBeforeTx { address: Address::from_low_u64_be(subkey_to_append), info: None }
|
|
),
|
|
Err(DatabaseError::Write(-30418))
|
|
);
|
|
assert_eq!(
|
|
cursor.append(
|
|
transition_id - 1,
|
|
AccountBeforeTx { address: Address::from_low_u64_be(subkey_to_append), info: None }
|
|
),
|
|
Err(DatabaseError::Write(-30418))
|
|
);
|
|
assert_eq!(
|
|
cursor.append(
|
|
transition_id,
|
|
AccountBeforeTx { address: Address::from_low_u64_be(subkey_to_append), info: None }
|
|
),
|
|
Ok(())
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn db_closure_put_get() {
|
|
let path = TempDir::new().expect(test_utils::ERROR_TEMPDIR).into_path();
|
|
|
|
let value = Account {
|
|
nonce: 18446744073709551615,
|
|
bytecode_hash: Some(H256::random()),
|
|
balance: U256::MAX,
|
|
};
|
|
let key = Address::from_str("0xa2c122be93b0074270ebee7f6b7292c7deb45047")
|
|
.expect(ERROR_ETH_ADDRESS);
|
|
|
|
{
|
|
let env = test_utils::create_test_db_with_path::<WriteMap>(EnvKind::RW, &path);
|
|
|
|
// PUT
|
|
let result = env.update(|tx| {
|
|
tx.put::<PlainAccountState>(key, value).expect(ERROR_PUT);
|
|
200
|
|
});
|
|
assert!(result.expect(ERROR_RETURN_VALUE) == 200);
|
|
}
|
|
|
|
let env = Env::<WriteMap>::open(&path, EnvKind::RO).expect(ERROR_DB_CREATION);
|
|
|
|
// GET
|
|
let result =
|
|
env.view(|tx| tx.get::<PlainAccountState>(key).expect(ERROR_GET)).expect(ERROR_GET);
|
|
|
|
assert!(result == Some(value))
|
|
}
|
|
|
|
#[test]
|
|
fn db_dup_sort() {
|
|
let env = test_utils::create_test_db::<NoWriteMap>(EnvKind::RW);
|
|
let key = Address::from_str("0xa2c122be93b0074270ebee7f6b7292c7deb45047")
|
|
.expect(ERROR_ETH_ADDRESS);
|
|
|
|
// PUT (0,0)
|
|
let value00 = StorageEntry::default();
|
|
env.update(|tx| tx.put::<PlainStorageState>(key, value00).expect(ERROR_PUT)).unwrap();
|
|
|
|
// PUT (2,2)
|
|
let value22 = StorageEntry { key: H256::from_low_u64_be(2), value: U256::from(2) };
|
|
env.update(|tx| tx.put::<PlainStorageState>(key, value22).expect(ERROR_PUT)).unwrap();
|
|
|
|
// PUT (1,1)
|
|
let value11 = StorageEntry { key: H256::from_low_u64_be(1), value: U256::from(1) };
|
|
env.update(|tx| tx.put::<PlainStorageState>(key, value11).expect(ERROR_PUT)).unwrap();
|
|
|
|
// Iterate with cursor
|
|
{
|
|
let tx = env.tx().expect(ERROR_INIT_TX);
|
|
let mut cursor = tx.cursor_dup_read::<PlainStorageState>().unwrap();
|
|
|
|
// Notice that value11 and value22 have been ordered in the DB.
|
|
assert!(Some(value00) == cursor.next_dup_val().unwrap());
|
|
assert!(Some(value11) == cursor.next_dup_val().unwrap());
|
|
assert!(Some(value22) == cursor.next_dup_val().unwrap());
|
|
}
|
|
|
|
// Seek value with exact subkey
|
|
{
|
|
let tx = env.tx().expect(ERROR_INIT_TX);
|
|
let mut cursor = tx.cursor_dup_read::<PlainStorageState>().unwrap();
|
|
let mut walker = cursor.walk_dup(Some(key), Some(H256::from_low_u64_be(1))).unwrap();
|
|
assert_eq!(
|
|
(key, value11),
|
|
walker
|
|
.next()
|
|
.expect("element should exist.")
|
|
.expect("should be able to retrieve it.")
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn db_iterate_over_all_dup_values() {
|
|
let env = test_utils::create_test_db::<NoWriteMap>(EnvKind::RW);
|
|
let key1 = Address::from_str("0x1111111111111111111111111111111111111111")
|
|
.expect(ERROR_ETH_ADDRESS);
|
|
let key2 = Address::from_str("0x2222222222222222222222222222222222222222")
|
|
.expect(ERROR_ETH_ADDRESS);
|
|
|
|
// PUT key1 (0,0)
|
|
let value00 = StorageEntry::default();
|
|
env.update(|tx| tx.put::<PlainStorageState>(key1, value00).expect(ERROR_PUT)).unwrap();
|
|
|
|
// PUT key1 (1,1)
|
|
let value11 = StorageEntry { key: H256::from_low_u64_be(1), value: U256::from(1) };
|
|
env.update(|tx| tx.put::<PlainStorageState>(key1, value11).expect(ERROR_PUT)).unwrap();
|
|
|
|
// PUT key2 (2,2)
|
|
let value22 = StorageEntry { key: H256::from_low_u64_be(2), value: U256::from(2) };
|
|
env.update(|tx| tx.put::<PlainStorageState>(key2, value22).expect(ERROR_PUT)).unwrap();
|
|
|
|
// Iterate with walk_dup
|
|
{
|
|
let tx = env.tx().expect(ERROR_INIT_TX);
|
|
let mut cursor = tx.cursor_dup_read::<PlainStorageState>().unwrap();
|
|
let mut walker = cursor.walk_dup(None, None).unwrap();
|
|
|
|
// Notice that value11 and value22 have been ordered in the DB.
|
|
assert_eq!(Some(Ok((key1, value00))), walker.next());
|
|
assert_eq!(Some(Ok((key1, value11))), walker.next());
|
|
// NOTE: Dup cursor does NOT iterates on all values but only on duplicated values of the
|
|
// same key. assert_eq!(Ok(Some(value22.clone())), walker.next());
|
|
assert_eq!(None, walker.next());
|
|
}
|
|
|
|
// Iterate by using `walk`
|
|
{
|
|
let tx = env.tx().expect(ERROR_INIT_TX);
|
|
let mut cursor = tx.cursor_dup_read::<PlainStorageState>().unwrap();
|
|
let first = cursor.first().unwrap().unwrap();
|
|
let mut walker = cursor.walk(Some(first.0)).unwrap();
|
|
assert_eq!(Some(Ok((key1, value00))), walker.next());
|
|
assert_eq!(Some(Ok((key1, value11))), walker.next());
|
|
assert_eq!(Some(Ok((key2, value22))), walker.next());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn dup_value_with_same_subkey() {
|
|
let env = test_utils::create_test_db::<NoWriteMap>(EnvKind::RW);
|
|
let key1 = H160([0x11; 20]);
|
|
let key2 = H160([0x22; 20]);
|
|
|
|
// PUT key1 (0,1)
|
|
let value01 = StorageEntry { key: H256::from_low_u64_be(0), value: U256::from(1) };
|
|
env.update(|tx| tx.put::<PlainStorageState>(key1, value01).expect(ERROR_PUT)).unwrap();
|
|
|
|
// PUT key1 (0,0)
|
|
let value00 = StorageEntry::default();
|
|
env.update(|tx| tx.put::<PlainStorageState>(key1, value00).expect(ERROR_PUT)).unwrap();
|
|
|
|
// PUT key2 (2,2)
|
|
let value22 = StorageEntry { key: H256::from_low_u64_be(2), value: U256::from(2) };
|
|
env.update(|tx| tx.put::<PlainStorageState>(key2, value22).expect(ERROR_PUT)).unwrap();
|
|
|
|
// Iterate with walk
|
|
{
|
|
let tx = env.tx().expect(ERROR_INIT_TX);
|
|
let mut cursor = tx.cursor_dup_read::<PlainStorageState>().unwrap();
|
|
let first = cursor.first().unwrap().unwrap();
|
|
let mut walker = cursor.walk(Some(first.0)).unwrap();
|
|
|
|
// NOTE: Both values are present
|
|
assert_eq!(Some(Ok((key1, value00))), walker.next());
|
|
assert_eq!(Some(Ok((key1, value01))), walker.next());
|
|
assert_eq!(Some(Ok((key2, value22))), walker.next());
|
|
}
|
|
|
|
// seek_by_key_subkey
|
|
{
|
|
let tx = env.tx().expect(ERROR_INIT_TX);
|
|
let mut cursor = tx.cursor_dup_read::<PlainStorageState>().unwrap();
|
|
|
|
// NOTE: There are two values with same SubKey but only first one is shown
|
|
assert_eq!(Ok(Some(value00)), cursor.seek_by_key_subkey(key1, value00.key));
|
|
// key1 but value is greater than the one in the DB
|
|
assert_eq!(Ok(None), cursor.seek_by_key_subkey(key1, value22.key));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn db_sharded_key() {
|
|
let db: Arc<Env<WriteMap>> = test_utils::create_test_db(EnvKind::RW);
|
|
let real_key = Address::from_str("0xa2c122be93b0074270ebee7f6b7292c7deb45047").unwrap();
|
|
|
|
for i in 1..5 {
|
|
let key = ShardedKey::new(real_key, i * 100);
|
|
let list: IntegerList = vec![i * 100u64].into();
|
|
|
|
db.update(|tx| tx.put::<AccountHistory>(key.clone(), list.clone()).expect("")).unwrap();
|
|
}
|
|
|
|
// Seek value with non existing key.
|
|
{
|
|
let tx = db.tx().expect(ERROR_INIT_TX);
|
|
let mut cursor = tx.cursor_read::<AccountHistory>().unwrap();
|
|
|
|
// It will seek the one greater or equal to the query. Since we have `Address | 100`,
|
|
// `Address | 200` in the database and we're querying `Address | 150` it will return us
|
|
// `Address | 200`.
|
|
let mut walker = cursor.walk(Some(ShardedKey::new(real_key, 150))).unwrap();
|
|
let (key, list) = walker
|
|
.next()
|
|
.expect("element should exist.")
|
|
.expect("should be able to retrieve it.");
|
|
|
|
assert_eq!(ShardedKey::new(real_key, 200), key);
|
|
let list200: IntegerList = vec![200u64].into();
|
|
assert_eq!(list200, list);
|
|
}
|
|
// Seek greatest index
|
|
{
|
|
let tx = db.tx().expect(ERROR_INIT_TX);
|
|
let mut cursor = tx.cursor_read::<AccountHistory>().unwrap();
|
|
|
|
// It will seek the MAX value of transition index and try to use prev to get first
|
|
// biggers.
|
|
let _unknown = cursor.seek_exact(ShardedKey::new(real_key, u64::MAX)).unwrap();
|
|
let (key, list) = cursor
|
|
.prev()
|
|
.expect("element should exist.")
|
|
.expect("should be able to retrieve it.");
|
|
|
|
assert_eq!(ShardedKey::new(real_key, 400), key);
|
|
let list400: IntegerList = vec![400u64].into();
|
|
assert_eq!(list400, list);
|
|
}
|
|
}
|
|
}
|