From 23918cba4d9d47651746e4c25c6d9f28122495d1 Mon Sep 17 00:00:00 2001 From: Ognyan Genev <39865181+ogenev@users.noreply.github.com> Date: Wed, 4 Sep 2024 13:00:03 +0300 Subject: [PATCH] feat(beacon): store `LightClientBootstrap` in separate db table (#1413) --- trin-beacon/src/storage.rs | 160 ++++++++++++++++++++++++------------- trin-storage/src/sql.rs | 47 +++++------ trin-storage/src/utils.rs | 4 +- 3 files changed, 125 insertions(+), 86 deletions(-) diff --git a/trin-beacon/src/storage.rs b/trin-beacon/src/storage.rs index dfb6a41bd..b7c4a20af 100644 --- a/trin-beacon/src/storage.rs +++ b/trin-beacon/src/storage.rs @@ -1,4 +1,5 @@ use ethportal_api::{ + consensus::fork::ForkName, types::{ content_key::beacon::{ HISTORICAL_SUMMARIES_WITH_PROOF_KEY_PREFIX, LIGHT_CLIENT_BOOTSTRAP_KEY_PREFIX, @@ -6,8 +7,9 @@ use ethportal_api::{ LIGHT_CLIENT_UPDATES_BY_RANGE_KEY_PREFIX, }, content_value::beacon::{ - ForkVersionedLightClientFinalityUpdate, ForkVersionedLightClientOptimisticUpdate, - ForkVersionedLightClientUpdate, LightClientUpdatesByRange, + ForkVersionedLightClientBootstrap, ForkVersionedLightClientFinalityUpdate, + ForkVersionedLightClientOptimisticUpdate, ForkVersionedLightClientUpdate, + LightClientUpdatesByRange, }, distance::Distance, portal::PaginateLocalContentInfo, @@ -22,14 +24,15 @@ use ssz::{Decode, Encode}; use ssz_types::{typenum::U128, VariableList}; use std::path::PathBuf; use tracing::debug; +use tree_hash::TreeHash; use trin_metrics::storage::StorageMetricsReporter; use trin_storage::{ error::ContentStoreError, sql::{ - CONTENT_KEY_LOOKUP_QUERY_BEACON, CONTENT_VALUE_LOOKUP_QUERY_BEACON, HISTORICAL_SUMMARIES_EPOCH_LOOKUP_QUERY, HISTORICAL_SUMMARIES_LOOKUP_QUERY, - INSERT_LC_UPDATE_QUERY, INSERT_OR_REPLACE_HISTORICAL_SUMMARIES_QUERY, INSERT_QUERY_BEACON, - LC_UPDATE_LOOKUP_QUERY, LC_UPDATE_PERIOD_LOOKUP_QUERY, LC_UPDATE_TOTAL_SIZE_QUERY, + INSERT_BOOTSTRAP_QUERY, INSERT_LC_UPDATE_QUERY, + INSERT_OR_REPLACE_HISTORICAL_SUMMARIES_QUERY, LC_BOOTSTRAP_LOOKUP_QUERY, + LC_BOOTSTRAP_ROOT_LOOKUP_QUERY, LC_UPDATE_LOOKUP_QUERY, LC_UPDATE_PERIOD_LOOKUP_QUERY, TOTAL_DATA_SIZE_QUERY_BEACON, }, utils::get_total_size_of_directory_in_bytes, @@ -129,14 +132,13 @@ impl ContentStore for BeaconStorage { })?; match beacon_content_key { - BeaconContentKey::LightClientBootstrap(_) => { - let content_id = key.content_id(); - self.lookup_content_value(content_id).map_err(|err| { + BeaconContentKey::LightClientBootstrap(content_key) => self + .lookup_lc_bootstrap_value(&content_key.block_hash) + .map_err(|err| { ContentStoreError::Database(format!( "Error looking up LightClientBootstrap content value: {err:?}" )) - }) - } + }), BeaconContentKey::LightClientUpdatesByRange(content_key) => { let periods = content_key.start_period..(content_key.start_period + content_key.count); @@ -222,13 +224,12 @@ impl ContentStore for BeaconStorage { })?; match beacon_content_key { - BeaconContentKey::LightClientBootstrap(_) => { - let key = key.content_id(); + BeaconContentKey::LightClientBootstrap(content_key) => { let is_key_available = self - .lookup_content_key(key) + .lookup_lc_bootstrap_block_root(&content_key.block_hash) .map_err(|err| { ContentStoreError::Database(format!( - "Error looking up content key: {err:?}" + "Error looking up light client bootstrap block root: {err:?}" )) })? .is_some(); @@ -248,7 +249,7 @@ impl ContentStore for BeaconStorage { .lookup_lc_update_period(period) .map_err(|err| { ContentStoreError::Database(format!( - "Error looking up lc update period: {err:?}" + "Error looking up light client update period: {err:?}" )) })? .is_some(); @@ -327,21 +328,16 @@ impl BeaconStorage { Ok(storage) } - fn db_insert( + fn db_insert_lc_bootstrap( &self, - content_id: &[u8; 32], - content_key: &Vec, + block_root: &[u8; 32], value: &Vec, + slot: u64, ) -> Result { let conn = self.sql_connection_pool.get()?; Ok(conn.execute( - INSERT_QUERY_BEACON, - params![ - content_id.as_slice(), - content_key, - value, - 32 + content_key.len() + value.len() - ], + INSERT_BOOTSTRAP_QUERY, + params![block_root, value, slot, 32 + value.len() + 8], )?) } @@ -386,8 +382,60 @@ impl BeaconStorage { match content_key.first() { Some(&LIGHT_CLIENT_BOOTSTRAP_KEY_PREFIX) => { - if let Err(err) = self.db_insert(&content_id, &content_key, value) { - debug!("Error writing light client bootstrap content ID {content_id:?} to beacon network db: {err:?}"); + let bootstrap = ForkVersionedLightClientBootstrap::from_ssz_bytes(value.as_slice()) + .map_err(|err| ContentStoreError::InvalidData { + message: format!( + "Error deserializing ForkVersionedLightClientBootstrap value: {err:?}" + ), + })?; + + let (slot, block_root) = match bootstrap.fork_name { + ForkName::Bellatrix => { + let bootstrap_header = bootstrap.bootstrap.header_bellatrix().map_err(|err| { + ContentStoreError::InvalidData { + message: format!( + "Error getting header from bellatrix ForkVersionedLightClientBootstrap value: {err:?}" + ), + } + })?; + + ( + bootstrap_header.beacon.slot, + bootstrap_header.beacon.tree_hash_root(), + ) + } + ForkName::Capella => { + let bootstrap_header = bootstrap.bootstrap.header_capella().map_err(|err| { + ContentStoreError::InvalidData { + message: format!( + "Error getting header from capella ForkVersionedLightClientBootstrap value: {err:?}" + ), + } + })?; + + ( + bootstrap_header.beacon.slot, + bootstrap_header.beacon.tree_hash_root(), + ) + } + ForkName::Deneb => { + let bootstrap_header = bootstrap.bootstrap.header_deneb().map_err(|err| { + ContentStoreError::InvalidData { + message: format!( + "Error getting header from deneb ForkVersionedLightClientBootstrap value: {err:?}" + ), + } + })?; + + ( + bootstrap_header.beacon.slot, + bootstrap_header.beacon.tree_hash_root(), + ) + } + }; + + if let Err(err) = self.db_insert_lc_bootstrap(&block_root, value, slot) { + debug!(block_root = %block_root, "Error writing light client bootstrap to lc_bootstrap db table: {err:?}"); return Err(err); } else { self.metrics.increase_entry_count(); @@ -531,33 +579,21 @@ impl BeaconStorage { }? .num_bytes; - let mut lc_update_stmt = conn.prepare(LC_UPDATE_TOTAL_SIZE_QUERY)?; - let lc_update_result = lc_update_stmt.query_map([], |row| { - Ok(DataSize { - num_bytes: row.get(0)?, - }) - }); - let lc_update_sum = match lc_update_result?.next() { - Some(total) => total, - None => { - let err = "Unable to compute sum over lc update item sizes".to_string(); - return Err(ContentStoreError::Database(err)); - } - }? - .num_bytes; - - let sum = content_data_sum + lc_update_sum; - self.metrics.report_content_data_storage_bytes(sum); + self.metrics + .report_content_data_storage_bytes(content_data_sum); - Ok(sum as u64) + Ok(content_data_sum as u64) } - /// Public method for looking up a content key by its content id - pub fn lookup_content_key(&self, id: [u8; 32]) -> anyhow::Result>> { + /// Public method for looking up a light client bootstrap by block root + pub fn lookup_lc_bootstrap_block_root( + &self, + block_root: &[u8; 32], + ) -> anyhow::Result>> { let conn = self.sql_connection_pool.get()?; - let mut query = conn.prepare(CONTENT_KEY_LOOKUP_QUERY_BEACON)?; + let mut query = conn.prepare(LC_BOOTSTRAP_ROOT_LOOKUP_QUERY)?; let result: Result, ContentStoreError> = query - .query_map([id.as_slice()], |row| { + .query_map([block_root], |row| { let row: Vec = row.get(0)?; Ok(row) })? @@ -606,12 +642,15 @@ impl BeaconStorage { } } - /// Public method for looking up a content value by its content id - pub fn lookup_content_value(&self, id: [u8; 32]) -> anyhow::Result>> { + /// Public method for looking up a light client bootstrap value by block root + pub fn lookup_lc_bootstrap_value( + &self, + block_root: &[u8; 32], + ) -> anyhow::Result>> { let conn = self.sql_connection_pool.get()?; - let mut query = conn.prepare(CONTENT_VALUE_LOOKUP_QUERY_BEACON)?; + let mut query = conn.prepare(LC_BOOTSTRAP_LOOKUP_QUERY)?; let result: Result>, ContentStoreError> = query - .query_map([id.as_slice()], |row| { + .query_map([block_root], |row| { let row: Vec = row.get(0)?; Ok(row) })? @@ -674,19 +713,26 @@ mod test { }, LightClientBootstrapKey, LightClientUpdatesByRangeKey, }; + use tree_hash::TreeHash; use trin_storage::test_utils::create_test_portal_storage_config_with_capacity; #[test] fn test_beacon_storage_get_put_bootstrap() { let (_temp_dir, config) = create_test_portal_storage_config_with_capacity(10).unwrap(); let mut storage = BeaconStorage::new(config).unwrap(); + let value = test_utils::get_light_client_bootstrap(0); + let block_root = value + .bootstrap + .header_capella() + .unwrap() + .beacon + .tree_hash_root(); let key = BeaconContentKey::LightClientBootstrap(LightClientBootstrapKey { - block_hash: [1; 32], + block_hash: *block_root, }); - let value = vec![1, 2, 3, 4, 5]; - storage.put(key.clone(), &value).unwrap(); + storage.put(key.clone(), &value.as_ssz_bytes()).unwrap(); let result = storage.get(&key).unwrap().unwrap(); - assert_eq!(result, value); + assert_eq!(result, value.as_ssz_bytes()); } #[test] diff --git a/trin-storage/src/sql.rs b/trin-storage/src/sql.rs index b8cf5d7e1..1e4b86c86 100644 --- a/trin-storage/src/sql.rs +++ b/trin-storage/src/sql.rs @@ -2,37 +2,32 @@ // Beacon Specific SQL -pub const CREATE_QUERY_DB_BEACON: &str = "CREATE TABLE IF NOT EXISTS beacon ( - content_id blob PRIMARY KEY, - content_key blob NOT NULL, - content_value blob NOT NULL, +pub const LC_BOOTSTRAP_CREATE_TABLE: &str = "CREATE TABLE IF NOT EXISTS lc_bootstrap ( + block_root blob PRIMARY KEY, + value blob NOT NULL, + slot INTEGER NOT NULL, content_size INTEGER NOT NULL ); -CREATE INDEX IF NOT EXISTS beacon_content_size_idx ON beacon(content_size); +CREATE INDEX IF NOT EXISTS bootstrap_slot_idx ON lc_bootstrap(slot); +CREATE INDEX IF NOT EXISTS bootstrap_content_size_idx ON lc_bootstrap(content_size); "; -pub const INSERT_QUERY_BEACON: &str = - "INSERT OR IGNORE INTO beacon (content_id, content_key, content_value, content_size) +pub const INSERT_BOOTSTRAP_QUERY: &str = + "INSERT OR IGNORE INTO lc_bootstrap (block_root, value, slot, content_size) VALUES (?1, ?2, ?3, ?4)"; -pub const DELETE_QUERY_BEACON: &str = "DELETE FROM beacon - WHERE content_id = (?1)"; +pub const LC_BOOTSTRAP_ROOT_LOOKUP_QUERY: &str = + "SELECT block_root FROM lc_bootstrap WHERE block_root = (?1) LIMIT 1"; -pub const CONTENT_KEY_LOOKUP_QUERY_BEACON: &str = - "SELECT content_key FROM beacon WHERE content_id = (?1) LIMIT 1"; +pub const LC_BOOTSTRAP_LOOKUP_QUERY: &str = + "SELECT value FROM lc_bootstrap WHERE block_root = (?1) LIMIT 1"; -pub const CONTENT_VALUE_LOOKUP_QUERY_BEACON: &str = - "SELECT content_value FROM beacon WHERE content_id = (?1) LIMIT 1"; - -pub const TOTAL_DATA_SIZE_QUERY_BEACON: &str = "SELECT TOTAL(content_size) FROM beacon"; - -pub const TOTAL_ENTRY_COUNT_QUERY_BEACON: &str = "SELECT COUNT(*) FROM beacon"; - -pub const PAGINATE_QUERY_BEACON: &str = - "SELECT content_key FROM beacon ORDER BY content_key LIMIT (?1) OFFSET (?2)"; - -pub const CONTENT_SIZE_LOOKUP_QUERY_BEACON: &str = - "SELECT content_size FROM beacon WHERE content_id = (?1)"; +/// Total beacon data size is the combination of lc_bootstrap, lc_update and historical_summaries +/// tables +pub const TOTAL_DATA_SIZE_QUERY_BEACON: &str = "SELECT + (SELECT TOTAL(content_size) FROM lc_bootstrap) + + (SELECT TOTAL(update_size) FROM lc_update) + + (SELECT TOTAL(content_size) FROM historical_summaries) AS total_data_size;"; pub const LC_UPDATE_CREATE_TABLE: &str = "CREATE TABLE IF NOT EXISTS lc_update ( period INTEGER PRIMARY KEY, @@ -52,8 +47,6 @@ pub const LC_UPDATE_LOOKUP_QUERY: &str = "SELECT value FROM lc_update WHERE peri pub const LC_UPDATE_PERIOD_LOOKUP_QUERY: &str = "SELECT period FROM lc_update WHERE period = (?1) LIMIT 1"; -pub const LC_UPDATE_TOTAL_SIZE_QUERY: &str = "SELECT TOTAL(update_size) FROM lc_update"; - /// Create the historical summaries table. Add CHECK constraint to ensure that only one row is /// inserted. pub const HISTORICAL_SUMMARIES_CREATE_TABLE: &str = @@ -61,12 +54,12 @@ pub const HISTORICAL_SUMMARIES_CREATE_TABLE: &str = ID INTEGER PRIMARY KEY CHECK (ID = 1), epoch INTEGER NOT NULL, value BLOB NOT NULL, - update_size INTEGER + content_size INTEGER );"; /// Query to insert or update the historical summaries table. pub const INSERT_OR_REPLACE_HISTORICAL_SUMMARIES_QUERY: &str = - "INSERT OR REPLACE INTO historical_summaries (id, epoch, value, update_size) + "INSERT OR REPLACE INTO historical_summaries (id, epoch, value, content_size) VALUES (?1, ?2, ?3, ?4)"; /// Query to get the historical summary that is greater than or equal to the given epoch. diff --git a/trin-storage/src/utils.rs b/trin-storage/src/utils.rs index e78d36ebf..bb71565c7 100644 --- a/trin-storage/src/utils.rs +++ b/trin-storage/src/utils.rs @@ -1,7 +1,7 @@ use crate::{ error::ContentStoreError, sql::{ - CREATE_QUERY_DB_BEACON, DROP_USAGE_STATS_DB, HISTORICAL_SUMMARIES_CREATE_TABLE, + DROP_USAGE_STATS_DB, HISTORICAL_SUMMARIES_CREATE_TABLE, LC_BOOTSTRAP_CREATE_TABLE, LC_UPDATE_CREATE_TABLE, }, versioned::sql::STORE_INFO_CREATE_TABLE, @@ -20,7 +20,7 @@ pub fn setup_sql(node_data_dir: &Path) -> Result, let manager = SqliteConnectionManager::file(sql_path); let pool = Pool::new(manager)?; let conn = pool.get()?; - conn.execute_batch(CREATE_QUERY_DB_BEACON)?; + conn.execute_batch(LC_BOOTSTRAP_CREATE_TABLE)?; conn.execute_batch(LC_UPDATE_CREATE_TABLE)?; conn.execute_batch(HISTORICAL_SUMMARIES_CREATE_TABLE)?; conn.execute_batch(STORE_INFO_CREATE_TABLE)?;