Skip to content

Commit

Permalink
refactor(core): move trie db implementations to store crate. (#1973)
Browse files Browse the repository at this point in the history
**Motivation**
To avoid the `trie` crate from being coupled with db implementations,
and having to deal with feature flags.

**Description**
- Moved the TrieDB implementations (libmdx and redb) to `store` crate,
so that the `trie` crate becomes implementation agnostic
- Unfortunately, we still need the `libmdx` flag, because of
https://github.com/lambdaclass/ethrex/blob/39470aa58742ab60fd12625beb807e4e21f0a0d3/crates/storage/trie/node_hash.rs#L93-L110
. We can't implement that trait from within `store`.
  • Loading branch information
mpaulucci authored Feb 18, 2025
1 parent 2b02227 commit fea5ad5
Show file tree
Hide file tree
Showing 26 changed files with 515 additions and 509 deletions.
3 changes: 1 addition & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions cmd/ethrex/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ anyhow = "1.0.86"
rand = "0.8.5"
local-ip-address = "0.6"
tokio-util.workspace = true
libmdbx = { workspace = true, optional = true }
redb = { workspace = true, optional = true }
lazy_static.workspace = true

Expand All @@ -46,7 +45,7 @@ default = ["libmdbx", "c-kzg", "blst"]
dev = ["dep:ethrex-dev"]
c-kzg = ["ethrex-vm/c-kzg", "ethrex-common/c-kzg", "ethrex-blockchain/c-kzg", "ethrex-p2p/c-kzg"]
metrics = ["ethrex-blockchain/metrics", "ethrex-l2/metrics"]
libmdbx = ["dep:libmdbx", "ethrex-storage/libmdbx"]
libmdbx = ["ethrex-storage/libmdbx"]
redb = ["dep:redb", "ethrex-storage/redb"]
blst = ["ethrex-vm/blst"]
l2 = ["dep:ethrex-l2", "ethrex-vm/l2"]
3 changes: 0 additions & 3 deletions crates/blockchain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ tracing.workspace = true
bytes.workspace = true
cfg-if = "1.0.0"



k256 = { version = "0.13.3", features = ["ecdh"] }

ethrex-metrics = { path = "./metrics", default-features = false }
Expand All @@ -33,6 +31,5 @@ path = "./blockchain.rs"

[features]
default = []
libmdbx = ["ethrex-common/libmdbx", "ethrex-storage/default", "ethrex-vm/libmdbx"]
c-kzg = ["ethrex-common/c-kzg", "ethrex-vm/c-kzg", "ethrex-levm/c-kzg"]
metrics = ["ethrex-metrics/transactions"]
2 changes: 0 additions & 2 deletions crates/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ hex-literal.workspace = true

[features]
default = []
libmdbx = ["ethrex-trie/libmdbx"]
redb = ["ethrex-trie/redb"]
c-kzg = ["dep:c-kzg"]

[lib]
Expand Down
5 changes: 3 additions & 2 deletions crates/storage/store/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@ redb = { workspace = true, optional = true }

[features]
default = []
libmdbx = ["dep:libmdbx", "ethrex-trie/libmdbx", "ethrex-common/libmdbx"]
redb = ["dep:redb", "ethrex-trie/redb", "ethrex-common/redb"]
libmdbx = ["dep:libmdbx", "ethrex-trie/libmdbx"]
redb = ["dep:redb"]

[dev-dependencies]
hex.workspace = true
hex-literal.workspace = true
tempdir = "0.3.7"

[lib]
path = "./storage.rs"
4 changes: 3 additions & 1 deletion crates/storage/store/engines/libmdbx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use crate::rlp::{
BlockHashRLP, BlockHeaderRLP, BlockRLP, BlockTotalDifficultyRLP, ReceiptRLP, Rlp,
TransactionHashRLP, TupleRLP,
};
use crate::trie_db::libmdbx::LibmdbxTrieDB;
use crate::trie_db::libmdbx_dupsort::LibmdbxDupsortTrieDB;
use crate::{MAX_SNAPSHOT_READS, STATE_TRIE_SEGMENTS};
use anyhow::Result;
use bytes::Bytes;
Expand All @@ -17,7 +19,7 @@ use ethrex_common::types::{
use ethrex_rlp::decode::RLPDecode;
use ethrex_rlp::encode::RLPEncode;
use ethrex_rlp::error::RLPDecodeError;
use ethrex_trie::{LibmdbxDupsortTrieDB, LibmdbxTrieDB, Nibbles, Trie};
use ethrex_trie::{Nibbles, Trie};
use libmdbx::orm::{Decodable, Encodable, Table};
use libmdbx::{
dupsort,
Expand Down
9 changes: 5 additions & 4 deletions crates/storage/store/engines/redb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@ use ethrex_rlp::decode::RLPDecode;
use ethrex_rlp::encode::RLPEncode;
use ethrex_rlp::error::RLPDecodeError;
use ethrex_trie::Nibbles;
use ethrex_trie::{
db::{redb::RedBTrie, redb_multitable::RedBMultiTableTrieDB},
Trie,
};

use redb::{AccessGuard, Database, Key, MultimapTableDefinition, TableDefinition, TypeName, Value};

use crate::rlp::{
Expand All @@ -26,6 +23,10 @@ use crate::{
TupleRLP,
},
};
use crate::{
trie_db::{redb::RedBTrie, redb_multitable::RedBMultiTableTrieDB},
Trie,
};

use super::utils::SnapStateIndex;
use super::{api::StoreEngine, utils::ChainDataIndex};
Expand Down
1 change: 1 addition & 0 deletions crates/storage/store/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use tracing::info;
mod engines;
pub mod error;
mod rlp;
mod trie_db;

/// Number of state trie segments to fetch concurrently during state sync
pub const STATE_TRIE_SEGMENTS: usize = 2;
Expand Down
222 changes: 222 additions & 0 deletions crates/storage/store/trie_db/libmdbx.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
use ethrex_trie::error::TrieError;
use libmdbx::orm::{Database, Table};
use std::{marker::PhantomData, sync::Arc};
/// Libmdbx implementation for the TrieDB trait, with get and put operations.
pub struct LibmdbxTrieDB<T: Table> {
db: Arc<Database>,
phantom: PhantomData<T>,
}

use ethrex_trie::TrieDB;

impl<T> LibmdbxTrieDB<T>
where
T: Table<Key = Vec<u8>, Value = Vec<u8>>,
{
pub fn new(db: Arc<Database>) -> Self {
Self {
db,
phantom: PhantomData,
}
}
}

impl<T> TrieDB for LibmdbxTrieDB<T>
where
T: Table<Key = Vec<u8>, Value = Vec<u8>>,
{
fn get(&self, key: Vec<u8>) -> Result<Option<Vec<u8>>, TrieError> {
let txn = self.db.begin_read().map_err(TrieError::DbError)?;
txn.get::<T>(key).map_err(TrieError::DbError)
}

fn put(&self, key: Vec<u8>, value: Vec<u8>) -> Result<(), TrieError> {
let txn = self.db.begin_readwrite().map_err(TrieError::DbError)?;
txn.upsert::<T>(key, value).map_err(TrieError::DbError)?;
txn.commit().map_err(TrieError::DbError)
}

fn put_batch(&self, key_values: Vec<(Vec<u8>, Vec<u8>)>) -> Result<(), TrieError> {
let txn = self.db.begin_readwrite().map_err(TrieError::DbError)?;
for (key, value) in key_values {
txn.upsert::<T>(key, value).map_err(TrieError::DbError)?;
}
txn.commit().map_err(TrieError::DbError)
}
}

#[cfg(test)]
mod test {
use super::LibmdbxTrieDB;
use crate::trie_db::test_utils::libmdbx::{new_db, TestNodes};
use ethrex_trie::Trie;
use ethrex_trie::TrieDB;
use libmdbx::{
orm::{table, Database},
table_info,
};
use std::sync::Arc;
use tempdir::TempDir;

#[test]
fn simple_addition() {
table!(
/// NodeHash to Node table
( Nodes ) Vec<u8> => Vec<u8>
);
let inner_db = new_db::<Nodes>();
let db = LibmdbxTrieDB::<Nodes>::new(inner_db);
assert_eq!(db.get("hello".into()).unwrap(), None);
db.put("hello".into(), "value".into()).unwrap();
assert_eq!(db.get("hello".into()).unwrap(), Some("value".into()));
}

#[test]
fn different_tables() {
table!(
/// vec to vec
( TableA ) Vec<u8> => Vec<u8>
);
table!(
/// vec to vec
( TableB ) Vec<u8> => Vec<u8>
);
let tables = [table_info!(TableA), table_info!(TableB)]
.into_iter()
.collect();

let inner_db = Arc::new(Database::create(None, &tables).unwrap());
let db_a = LibmdbxTrieDB::<TableA>::new(inner_db.clone());
let db_b = LibmdbxTrieDB::<TableB>::new(inner_db.clone());
db_a.put("hello".into(), "value".into()).unwrap();
assert_eq!(db_b.get("hello".into()).unwrap(), None);
}

#[test]
fn get_old_state() {
let db = new_db::<TestNodes>();
let mut trie = Trie::new(Box::new(LibmdbxTrieDB::<TestNodes>::new(db.clone())));

trie.insert([0; 32].to_vec(), [0; 32].to_vec()).unwrap();
trie.insert([1; 32].to_vec(), [1; 32].to_vec()).unwrap();

let root = trie.hash().unwrap();

trie.insert([0; 32].to_vec(), [2; 32].to_vec()).unwrap();
trie.insert([1; 32].to_vec(), [3; 32].to_vec()).unwrap();

assert_eq!(trie.get(&[0; 32].to_vec()).unwrap(), Some([2; 32].to_vec()));
assert_eq!(trie.get(&[1; 32].to_vec()).unwrap(), Some([3; 32].to_vec()));

let trie = Trie::open(Box::new(LibmdbxTrieDB::<TestNodes>::new(db.clone())), root);

assert_eq!(trie.get(&[0; 32].to_vec()).unwrap(), Some([0; 32].to_vec()));
assert_eq!(trie.get(&[1; 32].to_vec()).unwrap(), Some([1; 32].to_vec()));
}

#[test]
fn get_old_state_with_removals() {
let db = new_db::<TestNodes>();
let mut trie = Trie::new(Box::new(LibmdbxTrieDB::<TestNodes>::new(db.clone())));

trie.insert([0; 32].to_vec(), [0; 32].to_vec()).unwrap();
trie.insert([1; 32].to_vec(), [1; 32].to_vec()).unwrap();
trie.insert([2; 32].to_vec(), [2; 32].to_vec()).unwrap();

let root = trie.hash().unwrap();

trie.insert([0; 32].to_vec(), vec![0x04]).unwrap();
trie.remove([1; 32].to_vec()).unwrap();
trie.insert([2; 32].to_vec(), vec![0x05]).unwrap();
trie.remove([0; 32].to_vec()).unwrap();

assert_eq!(trie.get(&[0; 32].to_vec()).unwrap(), None);
assert_eq!(trie.get(&[1; 32].to_vec()).unwrap(), None);
assert_eq!(trie.get(&[2; 32].to_vec()).unwrap(), Some(vec![0x05]));

let trie = Trie::open(Box::new(LibmdbxTrieDB::<TestNodes>::new(db.clone())), root);

assert_eq!(trie.get(&[0; 32].to_vec()).unwrap(), Some([0; 32].to_vec()));
assert_eq!(trie.get(&[1; 32].to_vec()).unwrap(), Some([1; 32].to_vec()));
assert_eq!(trie.get(&[2; 32].to_vec()).unwrap(), Some([2; 32].to_vec()));
}

#[test]
fn revert() {
let db = new_db::<TestNodes>();
let mut trie = Trie::new(Box::new(LibmdbxTrieDB::<TestNodes>::new(db.clone())));

trie.insert([0; 32].to_vec(), [0; 32].to_vec()).unwrap();
trie.insert([1; 32].to_vec(), [1; 32].to_vec()).unwrap();

let root = trie.hash().unwrap();

trie.insert([0; 32].to_vec(), [2; 32].to_vec()).unwrap();
trie.insert([1; 32].to_vec(), [3; 32].to_vec()).unwrap();

let mut trie = Trie::open(Box::new(LibmdbxTrieDB::<TestNodes>::new(db.clone())), root);

trie.insert([2; 32].to_vec(), [4; 32].to_vec()).unwrap();

assert_eq!(trie.get(&[0; 32].to_vec()).unwrap(), Some([0; 32].to_vec()));
assert_eq!(trie.get(&[1; 32].to_vec()).unwrap(), Some([1; 32].to_vec()));
assert_eq!(trie.get(&[2; 32].to_vec()).unwrap(), Some([4; 32].to_vec()));
}

#[test]
fn revert_with_removals() {
let db = new_db::<TestNodes>();
let mut trie = Trie::new(Box::new(LibmdbxTrieDB::<TestNodes>::new(db.clone())));

trie.insert([0; 32].to_vec(), [0; 32].to_vec()).unwrap();
trie.insert([1; 32].to_vec(), [1; 32].to_vec()).unwrap();
trie.insert([2; 32].to_vec(), [2; 32].to_vec()).unwrap();

let root = trie.hash().unwrap();

trie.insert([0; 32].to_vec(), [4; 32].to_vec()).unwrap();
trie.remove([1; 32].to_vec()).unwrap();
trie.insert([2; 32].to_vec(), [5; 32].to_vec()).unwrap();
trie.remove([0; 32].to_vec()).unwrap();

let mut trie = Trie::open(Box::new(LibmdbxTrieDB::<TestNodes>::new(db.clone())), root);

trie.remove([2; 32].to_vec()).unwrap();

assert_eq!(trie.get(&[0; 32].to_vec()).unwrap(), Some([0; 32].to_vec()));
assert_eq!(trie.get(&[1; 32].to_vec()).unwrap(), Some([1; 32].to_vec()));
assert_eq!(trie.get(&vec![0x02]).unwrap(), None);
}

#[test]
fn resume_trie() {
use crate::trie_db::test_utils::libmdbx::{new_db_with_path, open_db};

const TRIE_DIR: &str = "trie-db-resume-trie-test";
let trie_dir = TempDir::new(TRIE_DIR).expect("Failed to create temp dir");
let trie_dir = trie_dir.path();

// Create new trie from clean DB
let db = new_db_with_path::<TestNodes>(trie_dir.into());
let mut trie = Trie::new(Box::new(LibmdbxTrieDB::<TestNodes>::new(db.clone())));

trie.insert([0; 32].to_vec(), [1; 32].to_vec()).unwrap();
trie.insert([1; 32].to_vec(), [2; 32].to_vec()).unwrap();
trie.insert([2; 32].to_vec(), [4; 32].to_vec()).unwrap();

// Save current root
let root = trie.hash().unwrap();

// Release DB
drop(db);
drop(trie);

let db2 = open_db::<TestNodes>(trie_dir.to_str().unwrap());
// Create a new trie based on the previous trie's DB
let trie = Trie::open(Box::new(LibmdbxTrieDB::<TestNodes>::new(db2)), root);

assert_eq!(trie.get(&[0; 32].to_vec()).unwrap(), Some([1; 32].to_vec()));
assert_eq!(trie.get(&[1; 32].to_vec()).unwrap(), Some([2; 32].to_vec()));
assert_eq!(trie.get(&[2; 32].to_vec()).unwrap(), Some([4; 32].to_vec()));
}
}
Loading

0 comments on commit fea5ad5

Please sign in to comment.