Skip to content

Commit

Permalink
feat(beacon): store LightClientBootstrap in separate db table (#1413)
Browse files Browse the repository at this point in the history
  • Loading branch information
ogenev authored Sep 4, 2024
1 parent c61e276 commit 23918cb
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 86 deletions.
160 changes: 103 additions & 57 deletions trin-beacon/src/storage.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use ethportal_api::{
consensus::fork::ForkName,
types::{
content_key::beacon::{
HISTORICAL_SUMMARIES_WITH_PROOF_KEY_PREFIX, LIGHT_CLIENT_BOOTSTRAP_KEY_PREFIX,
LIGHT_CLIENT_FINALITY_UPDATE_KEY_PREFIX, LIGHT_CLIENT_OPTIMISTIC_UPDATE_KEY_PREFIX,
LIGHT_CLIENT_UPDATES_BY_RANGE_KEY_PREFIX,
},
content_value::beacon::{
ForkVersionedLightClientFinalityUpdate, ForkVersionedLightClientOptimisticUpdate,
ForkVersionedLightClientUpdate, LightClientUpdatesByRange,
ForkVersionedLightClientBootstrap, ForkVersionedLightClientFinalityUpdate,
ForkVersionedLightClientOptimisticUpdate, ForkVersionedLightClientUpdate,
LightClientUpdatesByRange,
},
distance::Distance,
portal::PaginateLocalContentInfo,
Expand All @@ -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,
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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();
Expand All @@ -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();
Expand Down Expand Up @@ -327,21 +328,16 @@ impl BeaconStorage {
Ok(storage)
}

fn db_insert(
fn db_insert_lc_bootstrap(
&self,
content_id: &[u8; 32],
content_key: &Vec<u8>,
block_root: &[u8; 32],
value: &Vec<u8>,
slot: u64,
) -> Result<usize, ContentStoreError> {
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],
)?)
}

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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<Option<Vec<u8>>> {
/// 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<Option<Vec<u8>>> {
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<Vec<BeaconContentKey>, ContentStoreError> = query
.query_map([id.as_slice()], |row| {
.query_map([block_root], |row| {
let row: Vec<u8> = row.get(0)?;
Ok(row)
})?
Expand Down Expand Up @@ -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<Option<Vec<u8>>> {
/// 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<Option<Vec<u8>>> {
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<Vec<Vec<u8>>, ContentStoreError> = query
.query_map([id.as_slice()], |row| {
.query_map([block_root], |row| {
let row: Vec<u8> = row.get(0)?;
Ok(row)
})?
Expand Down Expand Up @@ -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]
Expand Down
47 changes: 20 additions & 27 deletions trin-storage/src/sql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -52,21 +47,19 @@ 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 =
"CREATE TABLE IF NOT EXISTS historical_summaries (
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.
Expand Down
4 changes: 2 additions & 2 deletions trin-storage/src/utils.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -20,7 +20,7 @@ pub fn setup_sql(node_data_dir: &Path) -> Result<Pool<SqliteConnectionManager>,
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)?;
Expand Down

0 comments on commit 23918cb

Please sign in to comment.