diff --git a/Cargo.lock b/Cargo.lock index c9e590a376..6c5df6cf4d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -53,6 +53,16 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array 0.14.7", +] + [[package]] name = "aes" version = "0.8.4" @@ -64,6 +74,20 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "ahash" version = "0.8.11" @@ -226,7 +250,7 @@ dependencies = [ "proptest", "rand 0.8.5", "ruint", - "rustc-hash 2.0.0", + "rustc-hash 2.1.0", "serde", "sha3", "tiny-keccak", @@ -798,6 +822,12 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "atomptr" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a1529117e07828468cbe5a354bb0edd58b4d107117fa2f2ebfdd526ef06d144" + [[package]] name = "atty" version = "0.2.14" @@ -1054,6 +1084,24 @@ dependencies = [ "syn 2.0.89", ] +[[package]] +name = "bindgen" +version = "0.71.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" +dependencies = [ + "bitflags 2.6.0", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "proc-macro2", + "quote", + "regex", + "rustc-hash 2.1.0", + "shlex", + "syn 2.0.89", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -1761,6 +1809,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array 0.14.7", + "rand_core 0.6.4", "typenum", ] @@ -1906,6 +1955,20 @@ dependencies = [ "serde", ] +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core 0.9.10", +] + [[package]] name = "dashu" version = "0.4.2" @@ -2777,6 +2840,7 @@ dependencies = [ "lazy_static", "libmdbx", "local-ip-address", + "qmdb", "rand 0.8.5", "redb", "serde_json", @@ -3053,6 +3117,7 @@ dependencies = [ "hex", "hex-literal", "libmdbx", + "qmdb", "redb", "serde", "serde_json", @@ -3512,6 +3577,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + [[package]] name = "gimli" version = "0.29.0" @@ -3884,6 +3959,17 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "hpfile" +version = "0.3.0" +source = "git+https://github.com/LayerZero-Labs/qmdb#23003951b4fa98031bbd816617c21924a5703092" +dependencies = [ + "anyhow", + "dashmap 6.1.0", + "libc", + "parking_lot 0.12.3", +] + [[package]] name = "http" version = "0.2.12" @@ -4616,9 +4702,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.166" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ccc108bbc0b1331bd061864e7cd823c0cab660bbe6970e66e2c0614decde36" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libgit2-sys" @@ -4650,9 +4736,9 @@ checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "libmdbx" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afaecaddb5d9fb8b12a9f1998a079dd8c07c913a92902d414984b1b4608f066b" +checksum = "63a680ffe20d9f3cd4b0bf2587c01b837ac2a12976ff984838b27830366ccc92" dependencies = [ "anyhow", "arrayref", @@ -4666,7 +4752,7 @@ dependencies = [ "parking_lot 0.12.3", "sealed", "tempfile", - "thiserror 1.0.69", + "thiserror 2.0.9", ] [[package]] @@ -4873,11 +4959,11 @@ dependencies = [ [[package]] name = "mdbx-sys" -version = "12.10.1" +version = "12.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3df87547d61cd51660237fb9657a69e2204ad525a22c78e8f8b6cec647ce17c8" +checksum = "970abf35a0fde64c0c72971908bcd2fe0350d7da27d8dd0f02b4a00705d21c5d" dependencies = [ - "bindgen", + "bindgen 0.71.1", "cc", "libc", ] @@ -5969,6 +6055,18 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "portable-atomic" version = "1.10.0" @@ -6202,6 +6300,36 @@ dependencies = [ "parking_lot 0.12.3", ] +[[package]] +name = "qmdb" +version = "0.2.0" +source = "git+https://github.com/LayerZero-Labs/qmdb#23003951b4fa98031bbd816617c21924a5703092" +dependencies = [ + "aead", + "aes-gcm", + "anyhow", + "atomptr", + "bincode", + "blake2", + "byteorder", + "dashmap 6.1.0", + "generic-array 1.1.1", + "hex", + "hpfile", + "lazy_static", + "log", + "parking_lot 0.12.3", + "rand_core 0.6.4", + "rayon", + "rs_merkle", + "serde", + "sha2 0.10.8", + "threadpool", + "tikv-jemallocator", + "time", + "xxhash-rust", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -6218,7 +6346,7 @@ dependencies = [ "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash 2.0.0", + "rustc-hash 2.1.0", "rustls", "socket2", "thiserror 2.0.9", @@ -6236,7 +6364,7 @@ dependencies = [ "getrandom", "rand 0.8.5", "ring 0.17.8", - "rustc-hash 2.0.0", + "rustc-hash 2.1.0", "rustls", "rustls-pki-types", "slab", @@ -7111,6 +7239,15 @@ dependencies = [ "paste", ] +[[package]] +name = "rs_merkle" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b241d2e59b74ef9e98d94c78c47623d04c8392abaf82014dfd372a16041128f" +dependencies = [ + "sha2 0.10.8", +] + [[package]] name = "ruint" version = "1.12.3" @@ -7155,9 +7292,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hash" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" [[package]] name = "rustc-hex" @@ -7369,11 +7506,10 @@ checksum = "49c1eeaf4b6a87c7479688c6d52b9f1153cedd3c489300564f932b065c6eab95" [[package]] name = "sealed" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a8caec23b7800fb97971a1c6ae365b6239aaeddfb934d6265f8505e795699d" +checksum = "22f968c5ea23d555e670b449c1c5e7b2fc399fdaec1d304a17cd48e288abc107" dependencies = [ - "heck 0.4.1", "proc-macro2", "quote", "syn 2.0.89", @@ -7626,6 +7762,16 @@ dependencies = [ "cfg-if", "cpufeatures", "digest 0.10.7", + "sha2-asm", +] + +[[package]] +name = "sha2-asm" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b845214d6175804686b2bd482bcffe96651bb2d1200742b712003504a2dac1ab" +dependencies = [ + "cc", ] [[package]] @@ -8096,7 +8242,7 @@ checksum = "a049cdff6b64bc1cd2bebdf494fd314bc4b45eff9058ea69dace55a0fa77e483" dependencies = [ "anyhow", "bincode", - "bindgen", + "bindgen 0.70.1", "cc", "cfg-if", "hex", @@ -8612,6 +8758,26 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "tikv-jemalloc-sys" +version = "0.6.0+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd3c60906412afa9c2b5b5a48ca6a5abe5736aec9eb48ad05037a677e52e4e2d" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "tikv-jemallocator" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cec5ff18518d81584f477e9bfdf957f5bb0979b0bac3af4ca30b5b3ae2d2865" +dependencies = [ + "libc", + "tikv-jemalloc-sys", +] + [[package]] name = "time" version = "0.3.36" @@ -8688,7 +8854,7 @@ dependencies = [ "aho-corasick 0.7.20", "clap 2.34.0", "crossbeam-channel", - "dashmap", + "dashmap 4.0.2", "dirs 3.0.2", "encoding_rs_io", "env_logger", @@ -9132,6 +9298,16 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + [[package]] name = "untrusted" version = "0.7.1" @@ -9704,6 +9880,12 @@ dependencies = [ "tap", ] +[[package]] +name = "xxhash-rust" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" + [[package]] name = "yoke" version = "0.7.5" diff --git a/Cargo.toml b/Cargo.toml index f1f07d6bc2..604682722f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,7 @@ tracing-subscriber = { version = "0.3.0", features = ["env-filter"] } ethereum-types = { version = "0.15.1", features = ["serialize"] } serde = { version = "1.0.203", features = ["derive"] } serde_json = "1.0.117" -libmdbx = { version = "0.5.0", features = ["orm"] } +libmdbx = { version = "0.5.3", features = ["orm"] } bytes = { version = "1.6.0", features = ["serde"] } tokio = { version = "1.41.1", features = ["full"] } thiserror = "2.0.9" @@ -73,3 +73,4 @@ secp256k1 = { version = "0.29", default-features = false, features = [ ] } keccak-hash = "0.11.0" axum = "0.8.1" +qmdb = { git = "https://github.com/LayerZero-Labs/qmdb", package = "qmdb" } diff --git a/cmd/ethrex/Cargo.toml b/cmd/ethrex/Cargo.toml index 05f707e54a..2324e1fce4 100644 --- a/cmd/ethrex/Cargo.toml +++ b/cmd/ethrex/Cargo.toml @@ -30,6 +30,7 @@ local-ip-address = "0.6" tokio-util.workspace = true libmdbx = { workspace = true, optional = true } redb = { workspace = true, optional = true } +qmdb = { workspace = true, optional = true } lazy_static.workspace = true cfg-if = "1.0.0" @@ -47,5 +48,6 @@ dev = ["dep:ethrex-dev"] metrics = ["ethrex-blockchain/metrics", "ethrex-l2/metrics"] libmdbx = ["dep:libmdbx", "ethrex-storage/libmdbx"] redb = ["dep:redb", "ethrex-storage/redb"] +qmdb = ["dep:qmdb", "ethrex-storage/qmdb"] l2 = ["dep:ethrex-l2", "ethrex-vm/l2"] levm = ["default", "ethrex-vm/levm", "ethrex-blockchain/levm"] diff --git a/cmd/ethrex/ethrex.rs b/cmd/ethrex/ethrex.rs index a25c246efd..2f57edb03a 100644 --- a/cmd/ethrex/ethrex.rs +++ b/cmd/ethrex/ethrex.rs @@ -139,6 +139,8 @@ async fn main() { let store = Store::new(&data_dir, EngineType::RedB).expect("Failed to create Store"); } else if #[cfg(feature = "libmdbx")] { let store = Store::new(&data_dir, EngineType::Libmdbx).expect("Failed to create Store"); + } else if #[cfg(feature = "qmdb")] { + let store = Store::new(&data_dir, EngineType::Qmdb).expect("Failed to create QMDB Store"); } else { let store = Store::new(&data_dir, EngineType::InMemory).expect("Failed to create Store"); } diff --git a/crates/storage/store/Cargo.toml b/crates/storage/store/Cargo.toml index 322d1a3178..08ff165806 100644 --- a/crates/storage/store/Cargo.toml +++ b/crates/storage/store/Cargo.toml @@ -21,11 +21,15 @@ serde = { version = "1.0.203", features = ["derive"] } serde_json = "1.0.117" libmdbx = { workspace = true, optional = true } redb = { workspace = true, optional = true } +qmdb = { workspace = true, optional = true } + [features] -default = [] +# FIXME: Remove qmdb from the defaults before merging to main +default = ["qmdb"] libmdbx = ["dep:libmdbx", "ethrex-trie/libmdbx", "ethrex-core/libmdbx"] redb = ["dep:redb", "ethrex-trie/redb", "ethrex-core/redb"] +qmdb = ["dep:qmdb"] [dev-dependencies] hex.workspace = true diff --git a/crates/storage/store/engines.rs b/crates/storage/store/engines.rs index 1687904e8b..269386e815 100644 --- a/crates/storage/store/engines.rs +++ b/crates/storage/store/engines.rs @@ -2,6 +2,8 @@ pub mod api; pub mod in_memory; #[cfg(feature = "libmdbx")] pub mod libmdbx; +#[cfg(feature = "qmdb")] +pub mod qmdb; #[cfg(feature = "redb")] pub mod redb; mod utils; diff --git a/crates/storage/store/engines/qmdb.rs b/crates/storage/store/engines/qmdb.rs new file mode 100644 index 0000000000..8d444e974e --- /dev/null +++ b/crates/storage/store/engines/qmdb.rs @@ -0,0 +1,540 @@ +use crate::{ + engines::{api::StoreEngine, utils::ChainDataIndex}, + error::StoreError, +}; +use ethrex_core::{ + types::{ + BlobsBundle, Block, BlockBody, BlockHash, BlockHeader, BlockNumber, ChainConfig, Index, + Receipt, + }, + H256, U256, +}; +use ethrex_rlp::{decode::RLPDecode, encode::RLPEncode}; +use ethrex_trie::Trie; +use qmdb::{ + config::Config, + def::{IN_BLOCK_IDX_BITS, OP_CREATE, OP_DELETE, OP_WRITE}, + seqads::SeqAdsWrap, + test_helper::SimpleTask, + utils::{byte0_to_shard_id, changeset::ChangeSet, hasher}, + AdsCore, ADS, +}; +use std::{ + collections::HashMap, + fmt::Debug, + sync::{Arc, Mutex}, +}; + +const STATE_TRIE_NODES_TABLE: &str = "StateTrieNodes"; +const BLOCK_NUMBERS_TABLE: &str = "BlockNumbers"; +const BLOCK_TOTAL_DIFFICULTIES_TABLE: &str = "BlockTotalDifficulties"; +const HEADERS_TABLE: &str = "Headers"; +const BLOCK_BODIES_TABLE: &str = "BlockBodies"; +const ACCOUNT_CODES_TABLE: &str = "AccountCodes"; +const RECEIPTS_TABLE: &str = "Receipts"; +const CANONICAL_BLOCK_HASHES_TABLE: &str = "CanonicalBlockHashes"; +const STORAGE_TRIE_NODES_TABLE: &str = "StorageTrieNodes"; +const CHAIN_DATA_TABLE: &str = "ChainData"; +const PAYLOADS_TABLE: &str = "Payloads"; +const PENDING_BLOCKS_TABLE: &str = "PendingBlocks"; +const TRANSACTION_LOCATIONS_TABLE: &str = "TransactionLocations"; + +const TABLES: [&str; 13] = [ + STATE_TRIE_NODES_TABLE, + BLOCK_NUMBERS_TABLE, + BLOCK_TOTAL_DIFFICULTIES_TABLE, + HEADERS_TABLE, + BLOCK_BODIES_TABLE, + ACCOUNT_CODES_TABLE, + RECEIPTS_TABLE, + CANONICAL_BLOCK_HASHES_TABLE, + STORAGE_TRIE_NODES_TABLE, + CHAIN_DATA_TABLE, + PAYLOADS_TABLE, + PENDING_BLOCKS_TABLE, + TRANSACTION_LOCATIONS_TABLE, +]; + +pub struct Store { + db: Arc>>>, +} + +impl Debug for Store { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Store") + .field("db", &"place holder".to_owned()) + .finish() + } +} + +impl Store { + pub fn new() -> Self { + let db: HashMap> = TABLES + .into_iter() + .map(|table_name| { + let config = Config::from_dir(table_name); + AdsCore::init_dir(&config); + let db: SeqAdsWrap = SeqAdsWrap::new(&config); + (table_name.to_owned(), db) + }) + .collect(); + Self { + db: Arc::new(Mutex::new(db)), + } + } + + fn read(&self, key: K, table: &str) -> Result, StoreError> + where + K: RLPEncode, + V: RLPDecode, + { + let height = 0; + let Some(value) = self._read( + table, + height, + &key.encode_to_vec(), + std::mem::size_of::(), + )? + else { + return Ok(None); + }; + + V::decode(&value).map_err(StoreError::from).map(Some) + } + + fn get_chain_data(&self, key: impl Into) -> Result, StoreError> + where + V: RLPDecode, + { + let Some(bytes) = self.read::<_, Vec>(key.into(), CHAIN_DATA_TABLE)? else { + return Ok(None); + }; + V::decode(&bytes).map_err(StoreError::from).map(Some) + } + + fn get_block_hash_by_block_number( + &self, + block_number: BlockNumber, + ) -> Result, StoreError> { + Ok(self + ._read( + CANONICAL_BLOCK_HASHES_TABLE, + block_number.try_into().map_err(|err| { + StoreError::Custom(format!("Could not convert block number: {err}")) + })?, + &block_number.encode_to_vec(), + std::mem::size_of::(), + )? + .map(|b| BlockHash::from_slice(&b))) + } + + fn _read( + &self, + table: &str, + height: i64, + key: &[u8], + value_size: usize, + ) -> Result>, StoreError> { + self.db + .lock() + .map_err(|err| StoreError::Custom(format!("Could not lock db: {err}"))) + .map(|db_lock| { + let mut buf = vec![0; value_size]; + db_lock + .get(table) + .filter(|table_ads| { + let key_hash = hasher::hash(key); + let (_size, found_it) = + table_ads.read_entry(height, &key_hash, key, &mut buf); + found_it + }) + .is_some() + .then_some(buf) + }) + } + + fn write(&self, key: K, value: V, table: &str, operation: u8) -> Result<(), StoreError> + where + K: RLPEncode, + V: RLPEncode, + { + let mut change_set = ChangeSet::new(); + self._add_op_to_change_set(&mut change_set, key, value, operation); + change_set.sort(); + self._write(table, &vec![change_set]) + } + + fn write_batch( + &self, + key_value_tuples: Vec<(K, V)>, + table: &str, + operation: u8, + ) -> Result<(), StoreError> + where + K: RLPEncode, + V: RLPEncode, + { + let mut change_set = ChangeSet::new(); + for (key, value) in key_value_tuples { + self._add_op_to_change_set(&mut change_set, key, value, operation); + } + change_set.sort(); + self._write(table, &vec![change_set]) + } + + fn _add_op_to_change_set(&self, change_set: &mut ChangeSet, key: K, value: V, op: u8) + where + K: RLPEncode, + V: RLPEncode, + { + let encoded_key = key.encode_to_vec(); + let encoded_value = value.encode_to_vec(); + + let key_hash = hasher::hash(&encoded_key); + let shard_id = byte0_to_shard_id(key_hash[0]) as u8; + change_set.add_op(op, shard_id, &key_hash, &encoded_key, &encoded_value, None); + } + + fn _write(&self, table: &str, change_sets: &Vec) -> Result<(), StoreError> { + self.db + .lock() + .map_err(|err| StoreError::Custom(format!("Could not lock db: {err}"))) + .and_then(|db| { + db.get(table) + .map(|table_ads| { + let task_id = 1 << IN_BLOCK_IDX_BITS; + table_ads.commit_tx(task_id, change_sets); + }) + .ok_or(StoreError::InternalError(format!( + "Table {table} not found" + ))) + }) + } +} + +impl StoreEngine for Store { + fn add_block_header( + &self, + block_hash: BlockHash, + block_header: BlockHeader, + ) -> Result<(), StoreError> { + self.write(block_hash, block_header, HEADERS_TABLE, OP_CREATE) + } + + fn add_block_headers( + &self, + block_hashes: Vec, + block_headers: Vec, + ) -> Result<(), StoreError> { + self.write_batch( + block_hashes.into_iter().zip(block_headers).collect(), + HEADERS_TABLE, + OP_CREATE, + ) + } + + fn get_block_header( + &self, + block_number: BlockNumber, + ) -> Result, StoreError> { + let Some(block_hash) = self.get_block_hash_by_block_number(block_number)? else { + return Ok(None); + }; + self.read(block_hash, HEADERS_TABLE) + } + + fn add_block_body( + &self, + block_hash: BlockHash, + block_body: BlockBody, + ) -> Result<(), StoreError> { + self.write(block_hash, block_body, BLOCK_BODIES_TABLE, OP_CREATE) + } + + fn get_block_body(&self, block_number: BlockNumber) -> Result, StoreError> { + let Some(block_hash) = self.get_block_hash_by_block_number(block_number)? else { + return Ok(None); + }; + self.read(block_hash, BLOCK_BODIES_TABLE) + } + + fn get_block_body_by_hash( + &self, + block_hash: BlockHash, + ) -> Result, StoreError> { + self.read(block_hash, BLOCK_BODIES_TABLE) + } + + fn get_block_header_by_hash( + &self, + block_hash: BlockHash, + ) -> Result, StoreError> { + self.read(block_hash, HEADERS_TABLE) + } + + fn add_pending_block(&self, block: Block) -> Result<(), StoreError> { + self.write(block.hash(), block, PENDING_BLOCKS_TABLE, OP_CREATE) + } + + fn get_pending_block(&self, block_hash: BlockHash) -> Result, StoreError> { + self.read(block_hash, PENDING_BLOCKS_TABLE) + } + + fn add_block_number( + &self, + block_hash: BlockHash, + block_number: BlockNumber, + ) -> Result<(), StoreError> { + self.write(block_hash, block_number, BLOCK_NUMBERS_TABLE, OP_CREATE) + } + + fn get_block_number(&self, block_hash: BlockHash) -> Result, StoreError> { + self.read(block_hash, BLOCK_NUMBERS_TABLE) + } + + fn add_block_total_difficulty( + &self, + block_hash: BlockHash, + block_total_difficulty: U256, + ) -> Result<(), StoreError> { + self.write( + block_hash, + block_total_difficulty, + BLOCK_TOTAL_DIFFICULTIES_TABLE, + OP_CREATE, + ) + } + + fn get_block_total_difficulty( + &self, + block_hash: BlockHash, + ) -> Result, StoreError> { + self.read(block_hash, BLOCK_TOTAL_DIFFICULTIES_TABLE) + } + + fn add_transaction_location( + &self, + _transaction_hash: H256, + _block_number: BlockNumber, + _block_hash: BlockHash, + _index: Index, + ) -> Result<(), StoreError> { + todo!() + } + + fn add_transaction_locations( + &self, + _locations: Vec<(H256, BlockNumber, BlockHash, Index)>, + ) -> Result<(), StoreError> { + todo!() + } + + fn get_transaction_location( + &self, + _transaction_hash: H256, + ) -> Result, StoreError> { + todo!() + } + + fn add_receipt( + &self, + block_hash: BlockHash, + index: Index, + receipt: Receipt, + ) -> Result<(), StoreError> { + self.write((block_hash, index), receipt, RECEIPTS_TABLE, OP_CREATE) + } + + fn add_receipts( + &self, + block_hash: BlockHash, + receipts: Vec, + ) -> Result<(), StoreError> { + self.write_batch( + receipts + .into_iter() + .enumerate() + .map(|(index, receipt)| ((block_hash, index), receipt)) + .collect(), + RECEIPTS_TABLE, + OP_CREATE, + ) + } + + fn get_receipt( + &self, + block_number: BlockNumber, + index: Index, + ) -> Result, StoreError> { + let Some(block_hash) = self.get_block_hash_by_block_number(block_number)? else { + return Ok(None); + }; + self.read((block_hash, index), RECEIPTS_TABLE) + } + + fn add_account_code(&self, code_hash: H256, code: bytes::Bytes) -> Result<(), StoreError> { + self.write(code_hash, code, ACCOUNT_CODES_TABLE, OP_CREATE) + } + + fn get_account_code(&self, code_hash: H256) -> Result, StoreError> { + self.read(code_hash, ACCOUNT_CODES_TABLE) + } + + fn get_canonical_block_hash( + &self, + block_number: BlockNumber, + ) -> Result, StoreError> { + self.read(block_number, CANONICAL_BLOCK_HASHES_TABLE) + } + + fn set_chain_config(&self, chain_config: &ChainConfig) -> Result<(), StoreError> { + self.write( + ChainDataIndex::ChainConfig as u8, + serde_json::to_string(chain_config).map_err(|_err| StoreError::DecodeError)?, + CHAIN_DATA_TABLE, + OP_CREATE, // FIXME: Create or write? + ) + } + + fn get_chain_config(&self) -> Result { + let bytes = self + .read::<_, Vec>(ChainDataIndex::ChainConfig as u8, CHAIN_DATA_TABLE)? + .ok_or(StoreError::Custom("Chain config not found".to_string()))?; + let json = String::from_utf8(bytes).map_err(|_| StoreError::DecodeError)?; + serde_json::from_str(&json).map_err(|_| StoreError::DecodeError) + } + + fn update_earliest_block_number(&self, block_number: BlockNumber) -> Result<(), StoreError> { + self.write( + ChainDataIndex::EarliestBlockNumber as u8, + block_number, + CHAIN_DATA_TABLE, + OP_WRITE, + ) + } + + fn get_earliest_block_number(&self) -> Result, StoreError> { + self.get_chain_data(ChainDataIndex::EarliestBlockNumber as u8) + } + + fn update_finalized_block_number(&self, block_number: BlockNumber) -> Result<(), StoreError> { + self.write( + ChainDataIndex::FinalizedBlockNumber as u8, + block_number, + CHAIN_DATA_TABLE, + OP_WRITE, + ) + } + + fn get_finalized_block_number(&self) -> Result, StoreError> { + self.get_chain_data(ChainDataIndex::FinalizedBlockNumber as u8) + } + + fn update_safe_block_number(&self, block_number: BlockNumber) -> Result<(), StoreError> { + self.write( + ChainDataIndex::SafeBlockNumber as u8, + block_number, + CHAIN_DATA_TABLE, + OP_WRITE, + ) + } + + fn get_safe_block_number(&self) -> Result, StoreError> { + self.get_chain_data(ChainDataIndex::SafeBlockNumber as u8) + } + + fn update_latest_block_number(&self, block_number: BlockNumber) -> Result<(), StoreError> { + self.write( + ChainDataIndex::LatestBlockNumber as u8, + block_number, + CHAIN_DATA_TABLE, + OP_WRITE, + ) + } + + fn get_latest_block_number(&self) -> Result, StoreError> { + self.get_chain_data(ChainDataIndex::LatestBlockNumber as u8) + } + + fn update_latest_total_difficulty( + &self, + latest_total_difficulty: U256, + ) -> Result<(), StoreError> { + self.write( + ChainDataIndex::LatestTotalDifficulty as u8, + latest_total_difficulty, + CHAIN_DATA_TABLE, + OP_WRITE, + ) + } + + fn get_latest_total_difficulty(&self) -> Result, StoreError> { + self.get_chain_data(ChainDataIndex::LatestTotalDifficulty as u8) + } + + fn update_pending_block_number(&self, block_number: BlockNumber) -> Result<(), StoreError> { + self.write( + ChainDataIndex::PendingBlockNumber as u8, + block_number, + CHAIN_DATA_TABLE, + OP_WRITE, + ) + } + + fn get_pending_block_number(&self) -> Result, StoreError> { + self.get_chain_data(ChainDataIndex::PendingBlockNumber as u8) + } + + fn open_storage_trie(&self, _hashed_address: H256, _storage_root: H256) -> Trie { + todo!() + } + + fn open_state_trie(&self, _state_root: H256) -> Trie { + todo!() + } + + fn set_canonical_block(&self, number: BlockNumber, hash: BlockHash) -> Result<(), StoreError> { + self.write(number, hash, CANONICAL_BLOCK_HASHES_TABLE, OP_CREATE) + } + + fn unset_canonical_block(&self, number: BlockNumber) -> Result<(), StoreError> { + self.write(number, (), CANONICAL_BLOCK_HASHES_TABLE, OP_DELETE) + } + + fn add_payload(&self, payload_id: u64, block: Block) -> Result<(), StoreError> { + self.write( + payload_id, + (block, U256::zero(), BlobsBundle::empty(), false), + PAYLOADS_TABLE, + OP_CREATE, + ) + } + + fn get_payload( + &self, + payload_id: u64, + ) -> Result, StoreError> { + self.read(payload_id, PAYLOADS_TABLE) + } + + fn update_payload( + &self, + payload_id: u64, + block: Block, + block_value: U256, + blobs_bundle: BlobsBundle, + completed: bool, + ) -> Result<(), StoreError> { + self.write( + payload_id, + (block, block_value, blobs_bundle, completed), + PAYLOADS_TABLE, + OP_WRITE, + ) + } + + fn get_receipts_for_block(&self, block_hash: &BlockHash) -> Result, StoreError> { + self.read(*block_hash, RECEIPTS_TABLE) + .map(Option::unwrap_or_default) + } +} diff --git a/crates/storage/store/error.rs b/crates/storage/store/error.rs index aaa70dfa0d..3937592d4f 100644 --- a/crates/storage/store/error.rs +++ b/crates/storage/store/error.rs @@ -46,4 +46,6 @@ pub enum StoreError { MissingLatestBlockNumber, #[error("Missing earliest block number")] MissingEarliestBlockNumber, + #[error("Internal error: {0}")] + InternalError(String), } diff --git a/crates/storage/store/storage.rs b/crates/storage/store/storage.rs index f2785c1020..74a2ead5c1 100644 --- a/crates/storage/store/storage.rs +++ b/crates/storage/store/storage.rs @@ -4,6 +4,8 @@ use self::engines::libmdbx::Store as LibmdbxStore; use self::error::StoreError; use bytes::Bytes; use engines::api::StoreEngine; +#[cfg(feature = "qmdb")] +use engines::qmdb::Store as QMDBStore; #[cfg(feature = "redb")] use engines::redb::RedBStore; use ethereum_types::{Address, H256, U256}; @@ -42,6 +44,8 @@ pub enum EngineType { Libmdbx, #[cfg(feature = "redb")] RedB, + #[cfg(feature = "qmdb")] + Qmdb, } #[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] @@ -95,6 +99,12 @@ impl Store { mempool: Arc::new(Mutex::new(HashMap::new())), blobs_bundle_pool: Arc::new(Mutex::new(HashMap::new())), }, + #[cfg(feature = "qmdb")] + EngineType::Qmdb => Self { + engine: Arc::new(QMDBStore::new()), + mempool: Arc::new(Mutex::new(HashMap::new())), + blobs_bundle_pool: Arc::new(Mutex::new(HashMap::new())), + }, }; info!("Started store engine"); Ok(store)