diff --git a/crates/db/src/kv/cursor.rs b/crates/db/src/kv/cursor.rs index 681480622..999b9b1fb 100644 --- a/crates/db/src/kv/cursor.rs +++ b/crates/db/src/kv/cursor.rs @@ -1,5 +1,7 @@ //! Cursor wrapper for libmdbx-sys. +use std::marker::PhantomData; + use crate::utils::*; use libmdbx::{self, TransactionKind, WriteFlags, RO, RW}; use reth_interfaces::db::{ @@ -67,14 +69,20 @@ impl<'tx, K: TransactionKind, T: Table> DbCursorRO<'tx, T> for Cursor<'tx, K, T> decode!(self.inner.get_current()) } - fn walk(&'tx mut self, start_key: ::Key) -> Result, Error> { + fn walk<'cursor>( + &'cursor mut self, + start_key: T::Key, + ) -> Result, Error> + where + Self: Sized, + { let start = self .inner .set_range(start_key.encode().as_ref()) .map_err(|e| Error::Internal(e.into()))? .map(decoder::); - Ok(Walker::<'tx, T> { cursor: self, start }) + Ok(Walker::<'cursor, 'tx, T, Self> { cursor: self, start, _tx_phantom: PhantomData {} }) } } @@ -99,14 +107,18 @@ impl<'tx, K: TransactionKind, T: DupSort> DbDupCursorRO<'tx, T> for Cursor<'tx, } /// Returns an iterator starting at a key greater or equal than `start_key` of a DUPSORT table. - fn walk_dup(&'tx mut self, key: T::Key, subkey: T::SubKey) -> Result, Error> { + fn walk_dup<'cursor>( + &'cursor mut self, + key: T::Key, + subkey: T::SubKey, + ) -> Result, Error> { let start = self .inner .get_both_range(key.encode().as_ref(), subkey.encode().as_ref()) .map_err(|e| Error::Internal(e.into()))? .map(decode_one::); - Ok(DupWalker::<'tx, T> { cursor: self, start }) + Ok(DupWalker::<'cursor, 'tx, T, Self> { cursor: self, start, _tx_phantom: PhantomData {} }) } } diff --git a/crates/db/src/kv/mod.rs b/crates/db/src/kv/mod.rs index f75acea1e..3be7eec38 100644 --- a/crates/db/src/kv/mod.rs +++ b/crates/db/src/kv/mod.rs @@ -137,7 +137,7 @@ mod tests { use libmdbx::{NoWriteMap, WriteMap}; use reth_interfaces::db::{ tables::{Headers, PlainState}, - Database, DbTx, DbTxMut, + Database, DbCursorRO, DbTx, DbTxMut, }; use reth_primitives::{Account, Address, Header, H256, U256}; use std::str::FromStr; @@ -175,6 +175,31 @@ mod tests { tx.commit().expect(ERROR_COMMIT); } + #[test] + fn db_cursor_walk() { + let env = test_utils::create_test_db::(EnvKind::RW); + + let value = Header::default(); + let key = (1u64, H256::zero()); + + // PUT + let tx = env.tx_mut().expect(ERROR_INIT_TX); + tx.put::(key.into(), value.clone()).expect(ERROR_PUT); + tx.commit().expect(ERROR_COMMIT); + + // Cursor + let tx = env.tx().expect(ERROR_INIT_TX); + let mut cursor = tx.cursor::().unwrap(); + + let first = cursor.first().unwrap(); + assert!(first.is_some(), "First should be our put"); + + // Walk + let walk = cursor.walk(key.into()).unwrap(); + let first = walk.into_iter().next().unwrap().unwrap(); + assert_eq!(first.1, value, "First next should be put value"); + } + #[test] fn db_closure_put_get() { let path = TempDir::new().expect(test_utils::ERROR_TEMPDIR).into_path(); diff --git a/crates/db/src/kv/tx.rs b/crates/db/src/kv/tx.rs index 02495ec3a..1958f2c1f 100644 --- a/crates/db/src/kv/tx.rs +++ b/crates/db/src/kv/tx.rs @@ -2,7 +2,7 @@ use crate::{kv::cursor::Cursor, utils::decode_one}; use libmdbx::{EnvironmentKind, Transaction, TransactionKind, WriteFlags, RW}; -use reth_interfaces::db::{DbTx, DbTxMut, DupSort, Encode, Error, Table}; +use reth_interfaces::db::{DbTx, DbTxGAT, DbTxMut, DbTxMutGAT, DupSort, Encode, Error, Table}; use std::marker::PhantomData; /// Wrapper for the libmdbx transaction. @@ -39,18 +39,24 @@ impl<'env, K: TransactionKind, E: EnvironmentKind> Tx<'env, K, E> { } } -impl<'env, K: TransactionKind, E: EnvironmentKind> DbTx<'env> for Tx<'env, K, E> { - /// Cursor GAT - type Cursor = Cursor<'env, K, T>; - /// DupCursor GAT - type DupCursor = Cursor<'env, K, T>; - /// Iterate over read only values in database. - fn cursor(&self) -> Result, Error> { +impl<'a, K: TransactionKind, E: EnvironmentKind> DbTxGAT<'a> for Tx<'_, K, E> { + type Cursor = Cursor<'a, K, T>; + type DupCursor = Cursor<'a, K, T>; +} + +impl<'a, K: TransactionKind, E: EnvironmentKind> DbTxMutGAT<'a> for Tx<'_, K, E> { + type CursorMut = Cursor<'a, RW, T>; + type DupCursorMut = Cursor<'a, RW, T>; +} + +impl<'tx, K: TransactionKind, E: EnvironmentKind> DbTx<'tx> for Tx<'tx, K, E> { + // Iterate over read only values in database. + fn cursor(&'tx self) -> Result<>::Cursor, Error> { self.new_cursor() } /// Iterate over read only values in database. - fn cursor_dup(&self) -> Result, Error> { + fn cursor_dup(&'tx self) -> Result<>::DupCursor, Error> { self.new_cursor() } @@ -70,11 +76,7 @@ impl<'env, K: TransactionKind, E: EnvironmentKind> DbTx<'env> for Tx<'env, K, E> } } -impl<'env, E: EnvironmentKind> DbTxMut<'env> for Tx<'env, RW, E> { - type CursorMut = Cursor<'env, RW, T>; - - type DupCursorMut = Cursor<'env, RW, T>; - +impl DbTxMut<'_> for Tx<'_, RW, E> { fn put(&self, key: T::Key, value: T::Value) -> Result<(), Error> { self.inner .put( @@ -111,11 +113,13 @@ impl<'env, E: EnvironmentKind> DbTxMut<'env> for Tx<'env, RW, E> { Ok(()) } - fn cursor_mut(&self) -> Result, Error> { + fn cursor_mut(&self) -> Result<>::CursorMut, Error> { self.new_cursor() } - fn cursor_dup_mut(&self) -> Result, Error> { + fn cursor_dup_mut( + &self, + ) -> Result<>::DupCursorMut, Error> { self.new_cursor() } } diff --git a/crates/interfaces/src/db/mock.rs b/crates/interfaces/src/db/mock.rs index 811f64192..ae6dd3d53 100644 --- a/crates/interfaces/src/db/mock.rs +++ b/crates/interfaces/src/db/mock.rs @@ -2,8 +2,8 @@ use std::collections::BTreeMap; use super::{ - Database, DatabaseGAT, DbCursorRO, DbCursorRW, DbDupCursorRO, DbDupCursorRW, DbTx, DbTxMut, - DupSort, Table, + Database, DatabaseGAT, DbCursorRO, DbCursorRW, DbDupCursorRO, DbDupCursorRW, DbTx, DbTxGAT, + DbTxMut, DbTxMutGAT, DupSort, DupWalker, Error, Table, Walker, }; /// Mock database used for testing with inner BTreeMap structure @@ -37,11 +37,17 @@ pub struct TxMock { _table: BTreeMap, Vec>, } +impl<'a> DbTxGAT<'a> for TxMock { + type Cursor = CursorMock; + type DupCursor = CursorMock; +} + +impl<'a> DbTxMutGAT<'a> for TxMock { + type CursorMut = CursorMock; + type DupCursorMut = CursorMock; +} + impl<'a> DbTx<'a> for TxMock { - type Cursor = CursorMock; - - type DupCursor = CursorMock; - fn get(&self, _key: T::Key) -> Result, super::Error> { todo!() } @@ -50,20 +56,18 @@ impl<'a> DbTx<'a> for TxMock { todo!() } - fn cursor(&self) -> Result, super::Error> { + fn cursor(&self) -> Result<>::Cursor, super::Error> { todo!() } - fn cursor_dup(&self) -> Result, super::Error> { + fn cursor_dup( + &self, + ) -> Result<>::DupCursor, super::Error> { todo!() } } impl<'a> DbTxMut<'a> for TxMock { - type CursorMut = CursorMock; - - type DupCursorMut = CursorMock; - fn put(&self, _key: T::Key, _value: T::Value) -> Result<(), super::Error> { todo!() } @@ -76,11 +80,15 @@ impl<'a> DbTxMut<'a> for TxMock { todo!() } - fn cursor_mut(&self) -> Result, super::Error> { + fn cursor_mut( + &self, + ) -> Result<>::CursorMut, super::Error> { todo!() } - fn cursor_dup_mut(&self) -> Result, super::Error> { + fn cursor_dup_mut( + &self, + ) -> Result<>::DupCursorMut, super::Error> { todo!() } @@ -123,7 +131,13 @@ impl<'tx, T: Table> DbCursorRO<'tx, T> for CursorMock { todo!() } - fn walk(&'tx mut self, _start_key: T::Key) -> Result, super::Error> { + fn walk<'cursor>( + &'cursor mut self, + _start_key: T::Key, + ) -> Result, Error> + where + Self: Sized, + { todo!() } } @@ -141,11 +155,14 @@ impl<'tx, T: DupSort> DbDupCursorRO<'tx, T> for CursorMock { todo!() } - fn walk_dup( - &'tx mut self, + fn walk_dup<'cursor>( + &'cursor mut self, _key: ::Key, _subkey: ::SubKey, - ) -> Result, super::Error> { + ) -> Result, Error> + where + Self: Sized, + { todo!() } } diff --git a/crates/interfaces/src/db/mod.rs b/crates/interfaces/src/db/mod.rs index 745d052d0..ee3233c4e 100644 --- a/crates/interfaces/src/db/mod.rs +++ b/crates/interfaces/src/db/mod.rs @@ -6,6 +6,8 @@ pub mod models; mod table; pub mod tables; +use std::marker::PhantomData; + pub use error::Error; pub use table::*; @@ -67,25 +69,22 @@ pub trait Database: for<'a> DatabaseGAT<'a> { } } -/// Read only transaction -pub trait DbTx<'a> { +/// Implements the GAT method from: +/// https://sabrinajewson.org/blog/the-better-alternative-to-lifetime-gats#the-better-gats. +/// +/// Sealed trait which cannot be implemented by 3rd parties, exposed only for implementers +pub trait DbTxGAT<'a, __ImplicitBounds: Sealed = Bounds<&'a Self>>: Send + Sync { /// Cursor GAT type Cursor: DbCursorRO<'a, T>; /// DupCursor GAT type DupCursor: DbDupCursorRO<'a, T> + DbCursorRO<'a, T>; - /// Get value - fn get(&self, key: T::Key) -> Result, Error>; - /// Commit for read only transaction will consume and free transaction and allows - /// freeing of memory pages - fn commit(self) -> Result; - /// Iterate over read only values in table. - fn cursor(&self) -> Result, Error>; - /// Iterate over read only values in dup sorted table. - fn cursor_dup(&self) -> Result, Error>; } -/// Read write transaction that allows writing to database -pub trait DbTxMut<'a> { +/// Implements the GAT method from: +/// https://sabrinajewson.org/blog/the-better-alternative-to-lifetime-gats#the-better-gats. +/// +/// Sealed trait which cannot be implemented by 3rd parties, exposed only for implementers +pub trait DbTxMutGAT<'a, __ImplicitBounds: Sealed = Bounds<&'a Self>>: Send + Sync { /// Cursor GAT type CursorMut: DbCursorRW<'a, T> + DbCursorRO<'a, T>; /// DupCursor GAT @@ -93,6 +92,23 @@ pub trait DbTxMut<'a> { + DbCursorRW<'a, T> + DbDupCursorRO<'a, T> + DbCursorRO<'a, T>; +} + +/// Read only transaction +pub trait DbTx<'tx>: for<'a> DbTxGAT<'a> { + /// Get value + fn get(&self, key: T::Key) -> Result, Error>; + /// Commit for read only transaction will consume and free transaction and allows + /// freeing of memory pages + fn commit(self) -> Result; + /// Iterate over read only values in table. + fn cursor(&'tx self) -> Result<>::Cursor, Error>; + /// Iterate over read only values in dup sorted table. + fn cursor_dup(&'tx self) -> Result<>::DupCursor, Error>; +} + +/// Read write transaction that allows writing to database +pub trait DbTxMut<'tx>: for<'a> DbTxMutGAT<'a> { /// Put value to database fn put(&self, key: T::Key, value: T::Value) -> Result<(), Error>; /// Delete value from database @@ -100,9 +116,11 @@ pub trait DbTxMut<'a> { /// Clears database. fn clear(&self) -> Result<(), Error>; /// Cursor mut - fn cursor_mut(&self) -> Result, Error>; + fn cursor_mut(&self) -> Result<>::CursorMut, Error>; /// DupCursor mut. - fn cursor_dup_mut(&self) -> Result, Error>; + fn cursor_dup_mut( + &self, + ) -> Result<>::DupCursorMut, Error>; } /// Alias type for a `(key, value)` result coming from a cursor. @@ -136,8 +154,18 @@ pub trait DbCursorRO<'tx, T: Table> { /// Returns the current `(key, value)` pair of the cursor. fn current(&mut self) -> PairResult; + /// Inner + fn inner(&'tx mut self) -> &'tx mut Self { + self + } + /// Returns an iterator starting at a key greater or equal than `start_key`. - fn walk(&'tx mut self, start_key: T::Key) -> Result, Error>; + fn walk<'cursor>( + &'cursor mut self, + start_key: T::Key, + ) -> Result, Error> + where + Self: Sized; } /// Read only curor over DupSort table. @@ -153,7 +181,13 @@ pub trait DbDupCursorRO<'tx, T: DupSort> { /// Returns an iterator starting at a key greater or equal than `start_key` of a DUPSORT /// table. - fn walk_dup(&'tx mut self, key: T::Key, subkey: T::SubKey) -> Result, Error>; + fn walk_dup<'cursor>( + &'cursor mut self, + key: T::Key, + subkey: T::SubKey, + ) -> Result, Error> + where + Self: Sized; } /// Read write cursor over table. @@ -178,14 +212,18 @@ pub trait DbDupCursorRW<'tx, T: DupSort> { } /// Provides an iterator to `Cursor` when handling `Table`. -pub struct Walker<'cursor, T: Table> { +pub struct Walker<'cursor, 'tx, T: Table, CURSOR: DbCursorRO<'tx, T> + Sized> { /// Cursor to be used to walk through the table. - pub cursor: &'cursor mut dyn DbCursorRO<'cursor, T>, + pub cursor: &'cursor mut CURSOR, /// `(key, value)` where to start the walk. pub start: IterPairResult, + /// Phantom data for 'tx. + pub _tx_phantom: PhantomData<&'tx T>, } -impl<'cursor, T: Table> std::iter::Iterator for Walker<'cursor, T> { +impl<'cursor, 'tx, T: Table, CURSOR: DbCursorRO<'tx, T> + Sized> std::iter::Iterator + for Walker<'cursor, 'tx, T, CURSOR> +{ type Item = Result<(T::Key, T::Value), Error>; fn next(&mut self) -> Option { let start = self.start.take(); @@ -198,14 +236,18 @@ impl<'cursor, T: Table> std::iter::Iterator for Walker<'cursor, T> { } /// Provides an iterator to `Cursor` when handling a `DupSort` table. -pub struct DupWalker<'cursor, T: DupSort> { +pub struct DupWalker<'cursor, 'tx, T: DupSort, CURSOR: DbDupCursorRO<'tx, T> + Sized> { /// Cursor to be used to walk through the table. - pub cursor: &'cursor mut dyn DbDupCursorRO<'cursor, T>, + pub cursor: &'cursor mut CURSOR, /// Value where to start the walk. pub start: Option>, + /// Phantom data for 'tx. + pub _tx_phantom: PhantomData<&'tx T>, } -impl<'cursor, T: DupSort> std::iter::Iterator for DupWalker<'cursor, T> { +impl<'cursor, 'tx, T: DupSort, CURSOR: DbDupCursorRO<'tx, T> + Sized> std::iter::Iterator + for DupWalker<'cursor, 'tx, T, CURSOR> +{ type Item = Result; fn next(&mut self) -> Option { let start = self.start.take(); diff --git a/crates/interfaces/src/db/models/blocks.rs b/crates/interfaces/src/db/models/blocks.rs index a23b88721..d17f2f7ff 100644 --- a/crates/interfaces/src/db/models/blocks.rs +++ b/crates/interfaces/src/db/models/blocks.rs @@ -21,7 +21,7 @@ pub type HeaderHash = H256; /// element as BlockNumber, helps out with querying/sorting. /// /// Since it's used as a key, the `BlockNumber` is not compressed when encoding it. -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Eq)] #[allow(non_camel_case_types)] pub struct BlockNumHash((BlockNumber, BlockHash)); @@ -58,7 +58,9 @@ impl Decode for BlockNumHash { let value: bytes::Bytes = value.into(); let num = u64::from_be_bytes( - value.as_ref().try_into().map_err(|_| Error::Decode(eyre!("Into bytes error.")))?, + value.as_ref()[..8] + .try_into() + .map_err(|_| Error::Decode(eyre!("Into bytes error.")))?, ); let hash = H256::decode(value.slice(8..))?;