diff --git a/.circleci/config.yml b/.circleci/config.yml index d449e6ab77..c37730977f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -38,7 +38,7 @@ commands: name: Install libssl-dev for openssl-sys command: sudo apt install -y libssl-dev - run: - name: Install libclang for rocksdb + name: Install libclang command: sudo apt install clang orbs: rust: circleci/rust@1.6.0 @@ -215,7 +215,7 @@ jobs: name: Install libssl-dev for openssl-sys command: sudo NEEDRESTART_MODE=a apt install -y libssl-dev - run: - name: Install libclang for rocksdb + name: Install libclang command: sudo NEEDRESTART_MODE=a apt install clang - run: name: Install modprobe diff --git a/Cargo.lock b/Cargo.lock index f89b3d6031..f0e4712bd4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -791,27 +791,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bindgen" -version = "0.65.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" -dependencies = [ - "bitflags 1.3.2", - "cexpr", - "clang-sys", - "lazy_static", - "lazycell", - "peeking_take_while", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn 2.0.39", -] - [[package]] name = "bindgen" version = "0.66.1" @@ -1017,23 +996,12 @@ dependencies = [ "serde", ] -[[package]] -name = "bzip2-sys" -version = "0.1.11+1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - [[package]] name = "c-kzg" version = "0.1.0" source = "git+https://github.com/ethereum/c-kzg-4844?rev=f5f6f863d475847876a2bd5ee252058d37c3a15d#f5f6f863d475847876a2bd5ee252058d37c3a15d" dependencies = [ - "bindgen 0.66.1", + "bindgen", "blst", "cc", "glob", @@ -1047,7 +1015,7 @@ name = "c-kzg" version = "0.1.0" source = "git+https://github.com/ethereum/c-kzg-4844#712ccb629d64552561e2eeb1a1bf094f65c7f3ea" dependencies = [ - "bindgen 0.66.1", + "bindgen", "blst", "cc", "glob", @@ -2184,7 +2152,6 @@ dependencies = [ "portal-bridge", "rand 0.8.5", "reth-ipc", - "rocksdb", "rpc", "serde_json", "serde_yaml", @@ -3611,22 +3578,6 @@ dependencies = [ "redox_syscall 0.4.1", ] -[[package]] -name = "librocksdb-sys" -version = "0.11.0+8.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3386f101bcb4bd252d8e9d2fb41ec3b0862a15a62b478c355b2982efa469e3e" -dependencies = [ - "bindgen 0.65.1", - "bzip2-sys", - "cc", - "glob", - "libc", - "libz-sys", - "lz4-sys", - "zstd-sys", -] - [[package]] name = "libsqlite3-sys" version = "0.23.2" @@ -3751,16 +3702,6 @@ dependencies = [ "hashbrown 0.14.3", ] -[[package]] -name = "lz4-sys" -version = "1.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "matchers" version = "0.1.0" @@ -4508,7 +4449,6 @@ dependencies = [ "r2d2_sqlite", "rand 0.8.5", "rlp", - "rocksdb", "rstest 0.18.2", "rusqlite", "serde", @@ -5284,16 +5224,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "rocksdb" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb6f170a4041d50a0ce04b0d2e14916d6ca863ea2e422689a5b694395d299ffe" -dependencies = [ - "libc", - "librocksdb-sys", -] - [[package]] name = "route-recognizer" version = "0.3.1" @@ -7106,7 +7036,6 @@ dependencies = [ "rand 0.8.5", "reth-ipc", "rlp", - "rocksdb", "rpc", "serde_json", "serde_yaml", @@ -7192,12 +7121,10 @@ dependencies = [ "async-trait", "discv5", "env_logger 0.9.3", - "eth_trie", "ethereum-types", "ethportal-api", "parking_lot 0.11.2", "portalnet", - "rocksdb", "test-log", "tokio", "tracing", diff --git a/Cargo.toml b/Cargo.toml index b7b26db83e..de72c1fdf8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,6 @@ prometheus_exporter = "0.8.4" rand = "0.8.4" reth-ipc = { tag = "v0.1.0-alpha.10", git = "https://github.com/paradigmxyz/reth.git"} rlp = "0.5.0" -rocksdb = "0.21.0" rpc = { path = "rpc"} serde_json = {version = "1.0.89", features = ["preserve_order"]} sha3 = "0.9.1" diff --git a/book/src/developers/architecture/database.md b/book/src/developers/architecture/database.md index 193aaae83e..223ae69c1d 100644 --- a/book/src/developers/architecture/database.md +++ b/book/src/developers/architecture/database.md @@ -4,17 +4,16 @@ The database related code is located in `./trin-core/src/portalnet/storage.rs`. There are three main database kinds: -|DB Name|Kind|Location|Purpose|Keys|Values| -|-|-|-|-|-|-| -|Main|RocksDB|Disk|Data store|Content ID|Content data bytes| -|Memory|HashMap|Memory|Kademlia cache|Content key|Content data bytes| -|Meta|SQLite|Disk|Manage DB size|Content ID|Content key, content size| +| DB Name |Kind|Location| Purpose |Keys| Values | +|---------|-|-|----------------|-|------------------------------------------| +| Main |SQLite|Disk| Data store |Content ID| Content key, content value, content size | +| Memory |HashMap|Memory| Kademlia cache |Content key| Content data bytes | ## Main content database -This is a persistent file-based database that uses RocksDB. -It is also called the "radius" database because content management rules are based on -the radius of content (specifically the content distance to the node ID). +This is an SQLite database that stores content data. For a piece of content, this includes +the content ID, content key, content value and the size of the content. It makes assessing the size of +the database quicker by avoiding the need to repeatedly compute the size of each content. ## Memory content database @@ -22,12 +21,3 @@ This uses is an in-memory hashmap to keep content that may not be required for l storage. An overlay service uses this database when receiving data from a peer as part of Kademlia-related actions. If required, data is later moved to disk in the main content database. - -## Meta database - -This is an SQLite database that stores metadata. For a piece of content, this includes -the content ID, content key and the size of the content. It makes assessing the size of -the main database quicker by avoiding the need to repeatedly compute the size of each content. - -Database updates occur in tandum with the main database, where if an operation in one database -fails, the other can revert the operation to remain synced. diff --git a/book/src/developers/contributing/build_instructions/linux.md b/book/src/developers/contributing/build_instructions/linux.md index 3fc7916d50..bf45070dce 100644 --- a/book/src/developers/contributing/build_instructions/linux.md +++ b/book/src/developers/contributing/build_instructions/linux.md @@ -14,7 +14,7 @@ These steps are for setting up a Trin node as a service on Ubuntu. ### Installation ```sh -$ sudo apt install libssl-dev librocksdb-dev libclang-dev pkg-config build-essential +$ sudo apt install libssl-dev libclang-dev pkg-config build-essential ``` Install Trin: > Tip: If you intend to submit code changes to trin, first fork the repo and diff --git a/book/src/developers/contributing/build_instructions/windows.md b/book/src/developers/contributing/build_instructions/windows.md index 25a5020ce3..55dfa470b9 100644 --- a/book/src/developers/contributing/build_instructions/windows.md +++ b/book/src/developers/contributing/build_instructions/windows.md @@ -9,8 +9,7 @@ If you don't already have Rust install it $ winget install Rustlang.Rustup ``` -Install clang/llvm as it is required to compile rocksdb -If you don't already have Rust install it +Install clang/llvm as it is required to compile c-kzg ```sh $ winget install LLVM.LLVM ``` diff --git a/book/src/developers/quick_setup.md b/book/src/developers/quick_setup.md index 3192ba2cff..2297f000a1 100644 --- a/book/src/developers/quick_setup.md +++ b/book/src/developers/quick_setup.md @@ -16,7 +16,7 @@ Note: If you use a VPN, you should disable it before running Trin. Install dependencies (Ubuntu/Debian): ```sh -apt install libssl-dev librocksdb-dev libclang-dev pkg-config build-essential +apt install libssl-dev libclang-dev pkg-config build-essential ``` Environment variables: diff --git a/docker/Dockerfile b/docker/Dockerfile index c2e54d6e1a..21891cb43f 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -38,7 +38,6 @@ FROM ubuntu:22.04 # copy build artifacts from build stage COPY --from=builder /trin/target/release/trin /usr/bin/ -COPY --from=builder /trin/target/release/purge_db /usr/bin/ ENV RUST_LOG=debug diff --git a/docker/Dockerfile.bridge b/docker/Dockerfile.bridge index 1afec91b6b..ae851aa540 100644 --- a/docker/Dockerfile.bridge +++ b/docker/Dockerfile.bridge @@ -40,7 +40,6 @@ FROM ubuntu:22.04 # copy build artifacts from build stage COPY --from=builder /trin/target/release/trin /usr/bin/ COPY --from=builder /trin/target/release/portal-bridge /usr/bin/ -COPY --from=builder /trin/target/release/purge_db /usr/bin/ # These steps copy over the epoch accumulators repo for the bridge to use # This data is too large to be kept inside trin-source code # It must be downloaded separately and moved to the correct location diff --git a/ethportal-api/src/types/content_key/history.rs b/ethportal-api/src/types/content_key/history.rs index db71baeca7..de7a6f9cfa 100644 --- a/ethportal-api/src/types/content_key/history.rs +++ b/ethportal-api/src/types/content_key/history.rs @@ -61,7 +61,7 @@ impl<'de> Deserialize<'de> for HistoryContentKey { } /// A key for a block header. -#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq)] +#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, Default)] pub struct BlockHeaderKey { /// Hash of the block. pub block_hash: [u8; 32], diff --git a/ethportal-api/src/types/portal_wire.rs b/ethportal-api/src/types/portal_wire.rs index 9d5f4351e0..4b15abf10c 100644 --- a/ethportal-api/src/types/portal_wire.rs +++ b/ethportal-api/src/types/portal_wire.rs @@ -219,6 +219,19 @@ impl TryFrom for Vec { } } +impl From for u8 { + fn from(protocol_id: ProtocolId) -> Self { + match protocol_id { + ProtocolId::State => 2, + ProtocolId::History => 0, + ProtocolId::TransactionGossip => 3, + ProtocolId::CanonicalIndices => 4, + ProtocolId::Beacon => 1, + ProtocolId::Utp => 99, + } + } +} + /// A Portal protocol message. #[derive(Debug, PartialEq, Clone, Encode, Decode)] #[ssz(enum_behaviour = "union")] @@ -592,6 +605,33 @@ mod test { assert_eq!(hex, expected_hex); } + #[test] + fn prtocol_id_to_u8() { + let protocol_id = ProtocolId::History; + let expected_u8 = 0; + assert_eq!(expected_u8, u8::from(protocol_id)); + + let protocol_id = ProtocolId::Beacon; + let expected_u8 = 1; + assert_eq!(expected_u8, u8::from(protocol_id)); + + let protocol_id = ProtocolId::State; + let expected_u8 = 2; + assert_eq!(expected_u8, u8::from(protocol_id)); + + let protocol_id = ProtocolId::TransactionGossip; + let expected_u8 = 3; + assert_eq!(expected_u8, u8::from(protocol_id)); + + let protocol_id = ProtocolId::CanonicalIndices; + let expected_u8 = 4; + assert_eq!(expected_u8, u8::from(protocol_id)); + + let protocol_id = ProtocolId::Utp; + let expected_u8 = 99; + assert_eq!(expected_u8, u8::from(protocol_id)); + } + // Wire message test vectors available in Ethereum Portal Network specs repo: // github.com/ethereum/portal-network-specs diff --git a/ethportal-peertest/Cargo.toml b/ethportal-peertest/Cargo.toml index d9673fc12e..0c713ee1b2 100644 --- a/ethportal-peertest/Cargo.toml +++ b/ethportal-peertest/Cargo.toml @@ -23,7 +23,6 @@ hyper = { version = "0.14", features = ["full"] } jsonrpsee = {version="0.20.0", features = ["async-client", "client", "macros", "server"]} rand = "0.8.4" reth-ipc = { tag = "v0.1.0-alpha.10", git = "https://github.com/paradigmxyz/reth.git"} -rocksdb = "0.21.0" rpc = { path = "../rpc" } serde_json = "1.0.89" serde_yaml = "0.9.25" diff --git a/portalnet/Cargo.toml b/portalnet/Cargo.toml index 2bdeec2f2b..d8fb9cac0d 100644 --- a/portalnet/Cargo.toml +++ b/portalnet/Cargo.toml @@ -32,7 +32,6 @@ lru = "0.7.8" parking_lot = "0.11.2" rand = "0.8.4" rlp = "0.5.0" -rocksdb = "0.21.0" rusqlite = { version = "0.26.3", features = ["bundled"] } r2d2 = "0.8.9" r2d2_sqlite = "0.19.0" diff --git a/portalnet/src/storage.rs b/portalnet/src/storage.rs index e048c65b3f..221869d238 100644 --- a/portalnet/src/storage.rs +++ b/portalnet/src/storage.rs @@ -2,15 +2,12 @@ use std::{ convert::TryInto, fs, path::{Path, PathBuf}, - sync::Arc, }; -use anyhow::anyhow; use discv5::enr::NodeId; use ethportal_api::types::portal::PaginateLocalContentInfo; use r2d2::Pool; use r2d2_sqlite::SqliteConnectionManager; -use rocksdb::{Options, DB}; use rusqlite::params; use thiserror::Error; use tracing::{debug, error, info}; @@ -51,9 +48,6 @@ pub enum ContentStoreError { #[error("data invalid {message}")] InvalidData { message: String }, - #[error("rocksdb error {0}")] - Rocksdb(#[from] rocksdb::Error), - #[error("rusqlite error {0}")] Rusqlite(#[from] rusqlite::Error), @@ -185,7 +179,6 @@ pub struct PortalStorageConfig { pub node_id: NodeId, pub node_data_dir: PathBuf, pub distance_fn: DistanceFunction, - pub db: Arc, pub sql_connection_pool: Pool, } @@ -195,14 +188,12 @@ impl PortalStorageConfig { node_data_dir: PathBuf, node_id: NodeId, ) -> anyhow::Result { - let db = Arc::new(PortalStorage::setup_rocksdb(&node_data_dir)?); let sql_connection_pool = PortalStorage::setup_sql(&node_data_dir)?; Ok(Self { storage_capacity_mb, node_id, node_data_dir, distance_fn: DistanceFunction::Xor, - db, sql_connection_pool, }) } @@ -215,16 +206,18 @@ pub struct PortalStorage { node_data_dir: PathBuf, storage_capacity_in_bytes: u64, radius: Distance, - db: Arc, sql_connection_pool: Pool, distance_fn: DistanceFunction, metrics: StorageMetricsReporter, + network: ProtocolId, } impl ContentStore for PortalStorage { fn get(&self, key: &K) -> Result>, ContentStoreError> { let content_id = key.content_id(); - Ok(self.db.get(content_id)?) + self.lookup_content_value(content_id).map_err(|err| { + ContentStoreError::Database(format!("Error looking up content value: {err:?}")) + }) } fn put>( @@ -245,7 +238,12 @@ impl ContentStore for PortalStorage { } let key = key.content_id(); - let is_key_available = self.db.get_pinned(key)?.is_some(); + let is_key_available = self + .lookup_content_key(key) + .map_err(|err| { + ContentStoreError::Database(format!("Error looking up content key: {err:?}")) + })? + .is_some(); if is_key_available { return Ok(ShouldWeStoreContent::AlreadyStored); } @@ -274,10 +272,10 @@ impl PortalStorage { node_data_dir: config.node_data_dir, storage_capacity_in_bytes: config.storage_capacity_mb * BYTES_IN_MB_U64, radius: Distance::MAX, - db: config.db, sql_connection_pool: config.sql_connection_pool, distance_fn: config.distance_fn, metrics, + network: protocol, }; // Set the metrics to the default radius, to start @@ -399,18 +397,12 @@ impl PortalStorage { }); } - // Store the data in radius db - self.db_insert(&content_id, value)?; + // Store the data in db let content_key: Vec = key.clone().into(); // store content key w/o the 0x prefix let content_key = hex_encode(content_key).trim_start_matches("0x").to_string(); - // Revert rocks db action if there's an error with writing to metadata db - if let Err(err) = self.meta_db_insert(&content_id, &content_key, value) { - debug!( - "Error writing content ID {:?} to meta db. Reverting: {:?}", - content_id, err - ); - self.db.delete(content_id)?; + if let Err(err) = self.db_insert(&content_id, &content_key, value) { + debug!("Error writing content ID {content_id:?} to db: {err:?}"); return Err(err); } else { self.metrics.increase_entry_count(); @@ -450,7 +442,7 @@ impl PortalStorage { hex_encode(id_to_remove) ); if let Err(err) = self.evict(id_to_remove) { - debug!("Error writing content ID {id_to_remove:?} to meta db. Reverted: {err:?}",); + debug!("Error writing content ID {id_to_remove:?} to db: {err:?}",); } else { num_removed_items += 1; } @@ -506,18 +498,9 @@ impl PortalStorage { Ok(byte_size as u64) } - /// Public method for evicting a certain content id. Will revert RocksDB deletion if meta_db - /// deletion fails. + /// Public method for evicting a certain content id. pub fn evict(&self, id: [u8; 32]) -> anyhow::Result<()> { - let deleted_value = self.db.get(id)?; - self.db.delete(id)?; - // Revert rocksdb action if there's an error with writing to metadata db - if let Err(err) = self.meta_db_remove(&id) { - if let Some(value) = deleted_value { - self.db_insert(&id, &value)?; - } - return Err(anyhow!("failed deletion {err}")); - } + self.db_remove(&id)?; self.metrics.decrease_entry_count(); Ok(()) } @@ -545,6 +528,25 @@ impl PortalStorage { } } + /// Public method for looking up a content value by its content id + pub fn lookup_content_value(&self, id: [u8; 32]) -> anyhow::Result>> { + let conn = self.sql_connection_pool.get()?; + let mut query = conn.prepare(CONTENT_VALUE_LOOKUP_QUERY)?; + let id = id.to_vec(); + let result: Result>, ContentStoreError> = query + .query_map([id], |row| { + let row: String = row.get(0)?; + Ok(row) + })? + .map(|row| hex_decode(row?.as_str()).map_err(ContentStoreError::ByteUtilsError)) + .collect(); + + match result?.first() { + Some(val) => Ok(Some(val.to_vec())), + None => Ok(None), + } + } + /// Public method for retrieving the node's current radius. pub fn radius(&self) -> Distance { self.radius @@ -558,13 +560,7 @@ impl PortalStorage { } /// Internal method for inserting data into the db. - fn db_insert(&self, content_id: &[u8; 32], value: &Vec) -> Result<(), ContentStoreError> { - self.db.put(content_id, value)?; - Ok(()) - } - - /// Internal method for inserting data into the meta db. - fn meta_db_insert( + fn db_insert( &self, content_id: &[u8; 32], content_key: &String, @@ -583,6 +579,8 @@ impl PortalStorage { content_id.to_vec(), content_id_as_u32, content_key, + hex_encode(value), + u8::from(self.network), value_size ], ) { @@ -591,8 +589,8 @@ impl PortalStorage { } } - /// Internal method for removing a given content-id from the meta db. - fn meta_db_remove(&self, content_id: &[u8; 32]) -> Result<(), ContentStoreError> { + /// Internal method for removing a given content-id from the db. + fn db_remove(&self, content_id: &[u8; 32]) -> Result<(), ContentStoreError> { self.sql_connection_pool .get()? .execute(DELETE_QUERY, [content_id.to_vec()])?; @@ -640,11 +638,12 @@ impl PortalStorage { let conn = self.sql_connection_pool.get()?; let mut query = conn.prepare(XOR_FIND_FARTHEST_QUERY)?; - let mut result = query.query_map([node_id_u32], |row| { - Ok(ContentId { - id_long: row.get(0)?, - }) - })?; + let mut result = + query.query_map([node_id_u32, u8::from(self.network).into()], |row| { + Ok(ContentId { + id_long: row.get(0)?, + }) + })?; let result = match result.next() { Some(row) => row, @@ -726,26 +725,6 @@ impl PortalStorage { u32::from_be_bytes(array) } - /// Helper function for opening a RocksDB connection for the radius-constrained db. - pub fn setup_rocksdb(node_data_dir: &Path) -> Result { - let rocksdb_path = node_data_dir.join("rocksdb"); - info!(path = %rocksdb_path.display(), "Setting up RocksDB"); - - let mut db_opts = Options::default(); - db_opts.create_if_missing(true); - Ok(DB::open(&db_opts, rocksdb_path)?) - } - - /// Helper function for opening a RocksDB connection for the trie db. - pub fn setup_triedb(node_data_dir: &Path) -> Result { - let trie_db_path = node_data_dir.join("triedb"); - info!(path = %trie_db_path.display(), "Setting up triedb"); - - let mut db_opts = Options::default(); - db_opts.create_if_missing(true); - Ok(DB::open(&db_opts, trie_db_path)?) - } - /// Helper function for opening a SQLite connection. pub fn setup_sql( node_data_dir: &Path, @@ -766,40 +745,47 @@ impl PortalStorage { } // SQLite Statements -const CREATE_QUERY: &str = "CREATE TABLE IF NOT EXISTS content_metadata ( +const CREATE_QUERY: &str = "CREATE TABLE IF NOT EXISTS content_data ( content_id_long TEXT PRIMARY KEY, content_id_short INTEGER NOT NULL, content_key TEXT NOT NULL, + content_value TEXT NOT NULL, + network INTEGER NOT NULL DEFAULT 0, content_size INTEGER ); - CREATE INDEX content_size_idx ON content_metadata(content_size); - CREATE INDEX content_id_short_idx ON content_metadata(content_id_short); - CREATE INDEX content_id_long_idx ON content_metadata(content_id_long);"; + CREATE INDEX content_size_idx ON content_data(content_size); + CREATE INDEX content_id_short_idx ON content_data(content_id_short); + CREATE INDEX content_id_long_idx ON content_data(content_id_long); + CREATE INDEX network_idx ON content_data(network);"; const INSERT_QUERY: &str = - "INSERT OR IGNORE INTO content_metadata (content_id_long, content_id_short, content_key, content_size) - VALUES (?1, ?2, ?3, ?4)"; + "INSERT OR IGNORE INTO content_data (content_id_long, content_id_short, content_key, content_value, network, content_size) + VALUES (?1, ?2, ?3, ?4, ?5, ?6)"; -const DELETE_QUERY: &str = "DELETE FROM content_metadata +const DELETE_QUERY: &str = "DELETE FROM content_data WHERE content_id_long = (?1)"; const XOR_FIND_FARTHEST_QUERY: &str = "SELECT content_id_long - FROM content_metadata + FROM content_data + WHERE network = (?2) ORDER BY ((?1 | content_id_short) - (?1 & content_id_short)) DESC"; const CONTENT_KEY_LOOKUP_QUERY: &str = - "SELECT content_key FROM content_metadata WHERE content_id_long = (?1)"; + "SELECT content_key FROM content_data WHERE content_id_long = (?1) LIMIT 1"; -const TOTAL_DATA_SIZE_QUERY: &str = "SELECT TOTAL(content_size) FROM content_metadata"; +const CONTENT_VALUE_LOOKUP_QUERY: &str = + "SELECT content_value FROM content_data WHERE content_id_long = (?1) LIMIT 1"; -const TOTAL_ENTRY_COUNT_QUERY: &str = "SELECT COUNT(content_id_long) FROM content_metadata"; +const TOTAL_DATA_SIZE_QUERY: &str = "SELECT TOTAL(content_size) FROM content_data"; + +const TOTAL_ENTRY_COUNT_QUERY: &str = "SELECT COUNT(content_id_long) FROM content_data"; const PAGINATE_QUERY: &str = - "SELECT content_key FROM content_metadata ORDER BY content_key LIMIT :limit OFFSET :offset"; + "SELECT content_key FROM content_data ORDER BY content_key LIMIT :limit OFFSET :offset"; const CONTENT_SIZE_LOOKUP_QUERY: &str = - "SELECT content_size FROM content_metadata WHERE content_id_long = (?1)"; + "SELECT content_size FROM content_data WHERE content_id_long = (?1)"; // SQLite Result Containers struct ContentId { @@ -825,6 +811,7 @@ pub mod test { use crate::utils::db::{configure_node_data_dir, setup_temp_dir}; use ethportal_api::types::content_key::overlay::IdentityContentKey; + use ethportal_api::BlockHeaderKey; const CAPACITY_MB: u64 = 2; @@ -890,7 +877,7 @@ pub mod test { let storage_config = PortalStorageConfig::new(CAPACITY_MB, temp_dir.path().to_path_buf(), node_id).unwrap(); let mut storage = PortalStorage::new(storage_config, ProtocolId::History)?; - let content_key = generate_random_content_key(); + let content_key = HistoryContentKey::BlockHeaderWithProof(BlockHeaderKey::default()); let value: Vec = "OGFWs179fWnqmjvHQFGHszXloc3Wzdb4".into(); storage.store(&content_key, &value)?; @@ -898,7 +885,7 @@ pub mod test { assert_eq!(result, value); - std::mem::drop(storage); + drop(storage); temp_dir.close()?; Ok(()) } diff --git a/src/bin/purge_db.rs b/src/bin/purge_db.rs deleted file mode 100644 index 038c489d7f..0000000000 --- a/src/bin/purge_db.rs +++ /dev/null @@ -1,181 +0,0 @@ -use anyhow::Result; -use clap::{Parser, ValueEnum}; -use discv5::enr::{CombinedKey, Enr}; -use ethereum_types::H256; -use rocksdb::IteratorMode; -use ssz::Decode; -use tracing::{info, warn}; - -use ethportal_api::types::execution::accumulator::EpochAccumulator; -use ethportal_api::types::execution::block_body::BlockBody; -use ethportal_api::types::execution::header::HeaderWithProof; -use ethportal_api::types::execution::receipts::Receipts; -use ethportal_api::types::portal_wire::ProtocolId; -use ethportal_api::utils::bytes::hex_encode; -use ethportal_api::HistoryContentKey; -use portalnet::storage::{PortalStorage, PortalStorageConfig}; -use portalnet::utils::db::{configure_node_data_dir, configure_trin_data_dir}; -use trin_utils::log::init_tracing_logger; - -/// -/// This script will iterate through all content id / key pairs in rocksd & meta db. -/// However, if it is run in "invalid-only" mode, it will error if it encounters any -/// non-history network content. Since we only support history network content, this -/// shouldn't be a problem, but as we add support for more sub-networks this script will -/// need to be updated to avoid panicking. -/// -pub fn main() -> Result<()> { - init_tracing_logger(); - let purge_config = PurgeConfig::parse(); - - let enr_key = - CombinedKey::secp256k1_from_bytes(purge_config.private_key.0.clone().as_mut_slice()) - .expect("Failed to create ENR key"); - let enr = Enr::empty(&enr_key).unwrap(); - let node_id = enr.node_id(); - let trin_data_dir = configure_trin_data_dir(false)?; - let (node_data_dir, _) = - configure_node_data_dir(trin_data_dir, Some(purge_config.private_key))?; - info!("Purging data for NodeID: {node_id}"); - info!("DB Path: {node_data_dir:?}"); - - // Capacity is 0 since it (eg. for data radius calculation) is irrelevant when only removing data. - let capacity = 0; - let protocol = ProtocolId::History; - let config = PortalStorageConfig::new(capacity, node_data_dir, node_id)?; - let storage = - PortalStorage::new(config.clone(), protocol).expect("Failed to create portal storage"); - let iter = config.db.iterator(IteratorMode::Start); - let mut item_count = 0; - let mut remove_count = 0; - for element in iter { - let (id, value) = element?; - item_count += 1; - let mut content_id = [0u8; 32]; - content_id.copy_from_slice(&id); - match purge_config.mode { - PurgeMode::All => match storage.evict(content_id) { - Ok(_) => remove_count += 1, - Err(err) => { - warn!("Error occurred while evicting content id: {content_id:?} - {err:?}") - } - }, - PurgeMode::InvalidOnly => { - // naked unwrap since we shouldn't be storing any invalid content keys - let key_bytes = match storage.lookup_content_key(content_id) { - Ok(Some(k)) => k, - Ok(None) => { - warn!( - content.id = hex_encode(content_id), - "Couldn't find corresponding content key in meta db", - ); - continue; - } - Err(e) => { - warn!( - content.id = hex_encode(content_id), - "Error during lookup of content key in meta db {e}", - ); - continue; - } - }; - let key_hex = hex_encode(&key_bytes); - let content_key = match HistoryContentKey::try_from(key_bytes) { - Ok(key) => key, - Err(e) => { - warn!( - content.key = key_hex, - "Could not convert bytes into content key {e}" - ); - continue; - } - }; - - if !is_content_valid(&content_key, &value.into_vec()) { - match storage.evict(content_id) { - Ok(_) => remove_count += 1, - Err(err) => warn!( - "Error occurred while evicting content key: {key_hex:?} - {err:?}" - ), - } - } - } - } - } - info!("Found {item_count:?} total items - Removed {remove_count:?} items"); - Ok(()) -} - -fn is_content_valid(content_key: &HistoryContentKey, value: &[u8]) -> bool { - match content_key { - HistoryContentKey::BlockHeaderWithProof(_) => { - HeaderWithProof::from_ssz_bytes(value).is_ok() - } - HistoryContentKey::BlockBody(_) => BlockBody::from_ssz_bytes(value).is_ok(), - HistoryContentKey::BlockReceipts(_) => Receipts::from_ssz_bytes(value).is_ok(), - HistoryContentKey::EpochAccumulator(_) => EpochAccumulator::from_ssz_bytes(value).is_ok(), - } -} - -// CLI Parameter Handling -#[derive(Parser, Debug, PartialEq)] -#[command( - name = "Trin DB Purge Util", - about = "Remove undesired or invalid data from Trin DB" -)] -pub struct PurgeConfig { - #[arg( - long, - help = "(unsafe) Hex private key to generate node id for database namespace (with 0x prefix)" - )] - pub private_key: H256, - - #[arg( - default_value = "all", - long, - help = "Purge all content or only invalidly encoded content" - )] - pub mode: PurgeMode, -} - -#[derive(ValueEnum, Debug, PartialEq, Eq, Clone)] -pub enum PurgeMode { - All, - InvalidOnly, -} - -#[cfg(test)] -mod test { - use super::*; - use std::str::FromStr; - - #[test] - fn test_default_purge_config() { - const PRIVATE_KEY: &str = - "0x9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"; - let purge_config = PurgeConfig::parse_from(["test", "--private-key", PRIVATE_KEY]); - assert_eq!( - purge_config.private_key, - H256::from_str(PRIVATE_KEY).unwrap() - ); - assert_eq!(purge_config.mode, PurgeMode::All); - } - - #[test] - fn test_purge_mode_with_invalid_only() { - const PRIVATE_KEY: &str = - "0x9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"; - let purge_config = PurgeConfig::parse_from([ - "test", - "--private-key", - PRIVATE_KEY, - "--mode", - "invalid-only", - ]); - assert_eq!( - purge_config.private_key, - H256::from_str(PRIVATE_KEY).unwrap() - ); - assert_eq!(purge_config.mode, PurgeMode::InvalidOnly); - } -} diff --git a/trin-state/Cargo.toml b/trin-state/Cargo.toml index 72869d3791..0ff89b0507 100644 --- a/trin-state/Cargo.toml +++ b/trin-state/Cargo.toml @@ -16,10 +16,8 @@ async-trait = "0.1.53" discv5 = { version = "0.4.0", features = ["serde"] } ethereum-types = "0.14.1" ethportal-api = { path = "../ethportal-api" } -eth_trie = "0.4.0" parking_lot = "0.11.2" portalnet = { path = "../portalnet" } -rocksdb = "0.21.0" tracing = "0.1.36" tokio = {version = "1.14.0", features = ["full"]} trin-validation = { path = "../trin-validation" } diff --git a/trin-state/src/lib.rs b/trin-state/src/lib.rs index dddd135774..227248431a 100644 --- a/trin-state/src/lib.rs +++ b/trin-state/src/lib.rs @@ -25,7 +25,6 @@ use trin_validation::oracle::HeaderOracle; pub mod events; mod jsonrpc; pub mod network; -mod trie; pub mod utils; pub mod validation; diff --git a/trin-state/src/network.rs b/trin-state/src/network.rs index d8ecd17e56..9f9a377792 100644 --- a/trin-state/src/network.rs +++ b/trin-state/src/network.rs @@ -1,7 +1,5 @@ -use std::sync::Arc; - -use eth_trie::EthTrie; use parking_lot::RwLock as PLRwLock; +use std::sync::Arc; use tokio::sync::RwLock; use utp_rs::socket::UtpSocket; @@ -17,13 +15,12 @@ use portalnet::{ }; use trin_validation::oracle::HeaderOracle; -use crate::{trie::TrieDB, validation::StateValidator}; +use crate::validation::StateValidator; /// State network layer on top of the overlay protocol. Encapsulates state network specific data and logic. #[derive(Clone)] pub struct StateNetwork { pub overlay: Arc>, - pub trie: Arc>, } impl StateNetwork { @@ -34,9 +31,6 @@ impl StateNetwork { portal_config: PortalnetConfig, header_oracle: Arc>, ) -> anyhow::Result { - let db = PortalStorage::setup_triedb(&storage_config.node_data_dir)?; - let triedb = TrieDB::new(Arc::new(db)); - let trie = EthTrie::new(Arc::new(triedb)); let storage = Arc::new(PLRwLock::new(PortalStorage::new( storage_config, ProtocolId::State, @@ -59,7 +53,6 @@ impl StateNetwork { Ok(Self { overlay: Arc::new(overlay), - trie: Arc::new(trie), }) } } diff --git a/trin-state/src/trie.rs b/trin-state/src/trie.rs deleted file mode 100644 index 3f45844230..0000000000 --- a/trin-state/src/trie.rs +++ /dev/null @@ -1,31 +0,0 @@ -use std::sync::Arc; - -pub struct TrieDB { - db: Arc, -} - -impl TrieDB { - pub fn new(db: Arc) -> TrieDB { - TrieDB { db } - } -} - -impl eth_trie::DB for TrieDB { - type Error = rocksdb::Error; - - fn get(&self, key: &[u8]) -> Result>, Self::Error> { - self.db.get(key) - } - - fn insert(&self, key: &[u8], value: Vec) -> Result<(), Self::Error> { - self.db.put(key, value) - } - - fn remove(&self, key: &[u8]) -> Result<(), Self::Error> { - self.db.delete(key) - } - - fn flush(&self) -> Result<(), Self::Error> { - Ok(()) - } -}