diff --git a/Cargo.lock b/Cargo.lock index 92c609284..cafe956fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1849,16 +1849,7 @@ version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e69600ff1703123957937708eb27f7a564e48885c537782722ed0ba3189ce1d7" dependencies = [ - "dirs-sys 0.3.7", -] - -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys 0.4.1", + "dirs-sys", ] [[package]] @@ -1872,18 +1863,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.48.0", -] - [[package]] name = "discv5" version = "0.4.1" @@ -2222,7 +2201,6 @@ dependencies = [ "bimap", "bytes", "c-kzg", - "clap", "const_format", "discv5", "env_logger 0.9.3", @@ -2260,7 +2238,6 @@ dependencies = [ "tree_hash", "tree_hash_derive", "ureq", - "url", "validator", "vergen", ] @@ -2779,21 +2756,6 @@ dependencies = [ "serde", ] -[[package]] -name = "historical-batch" -version = "0.1.0" -dependencies = [ - "anyhow", - "dirs", - "e2store", - "ethereum_ssz", - "ethportal-api", - "regex", - "reqwest", - "tokio", - "tree_hash", -] - [[package]] name = "hkdf" version = "0.12.4" @@ -4144,12 +4106,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - [[package]] name = "overload" version = "0.1.1" @@ -4414,21 +4370,6 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" -[[package]] -name = "poll-latest" -version = "0.1.0" -dependencies = [ - "alloy", - "anyhow", - "clap", - "ethportal-api", - "futures", - "tokio", - "tracing", - "trin-utils", - "url", -] - [[package]] name = "polyval" version = "0.5.3" @@ -4693,21 +4634,6 @@ dependencies = [ "unarray", ] -[[package]] -name = "purge-invalid-history-content" -version = "0.1.0" -dependencies = [ - "alloy", - "anyhow", - "clap", - "discv5", - "ethportal-api", - "portalnet", - "tracing", - "trin-storage", - "trin-utils", -] - [[package]] name = "quick-error" version = "1.2.3" @@ -5482,23 +5408,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "sample-range" -version = "0.1.0" -dependencies = [ - "alloy", - "anyhow", - "clap", - "ethportal-api", - "futures", - "rand", - "tokio", - "tracing", - "trin-utils", - "trin-validation", - "url", -] - [[package]] name = "scale-info" version = "2.11.6" @@ -6301,26 +6210,6 @@ dependencies = [ "syn 2.0.94", ] -[[package]] -name = "test-providers" -version = "0.1.0" -dependencies = [ - "anyhow", - "clap", - "ethereum_ssz", - "ethportal-api", - "lazy_static", - "portal-bridge", - "rand", - "reqwest", - "serde_json", - "tokio", - "tracing", - "trin-utils", - "trin-validation", - "url", -] - [[package]] name = "thiserror" version = "1.0.69" diff --git a/Cargo.toml b/Cargo.toml index 7a899390a..6fb6ecaa7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,5 @@ [workspace] members = [ - "bin/historical-batch", - "bin/poll-latest", - "bin/purge-invalid-history-content", - "bin/sample-range", - "bin/test-providers", "bin/trin", "ethportal-api", "ethportal-peertest", diff --git a/bin/historical-batch/Cargo.toml b/bin/historical-batch/Cargo.toml deleted file mode 100644 index c63b69d2d..000000000 --- a/bin/historical-batch/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "historical-batch" -authors.workspace = true -categories.workspace = true -edition.workspace = true -keywords.workspace = true -license.workspace = true -readme.workspace = true -repository.workspace = true -rust-version.workspace = true -version.workspace = true - -[dependencies] -anyhow.workspace = true -dirs = "5.0.1" -e2store.workspace = true -ethereum_ssz.workspace = true -ethportal-api.workspace = true -regex = "1.10.2" -reqwest.workspace = true -tokio.workspace = true -tree_hash.workspace = true diff --git a/bin/historical-batch/src/main.rs b/bin/historical-batch/src/main.rs deleted file mode 100644 index 979c7ac29..000000000 --- a/bin/historical-batch/src/main.rs +++ /dev/null @@ -1,106 +0,0 @@ -use std::{ - fs::{File, OpenOptions}, - io, - io::{BufRead, Write}, -}; - -use anyhow::ensure; -use e2store::era::Era; -use ethportal_api::{consensus::beacon_state::HistoricalBatch, utils::bytes::hex_encode}; -use regex::Regex; -use reqwest::get; -use ssz::Encode; -use tree_hash::TreeHash; - -const _BELLATRIX_SLOT: u64 = 4700013; -const _CAPELLA_SLOT: u64 = 6209536; - -const ERA_URL: &str = "https://mainnet.era.nimbus.team/"; - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - let download_dir = dirs::download_dir().unwrap(); - let file_path = download_dir.join("historical_batches"); - - let body = get(ERA_URL).await?.text().await.unwrap(); - // Match era files with epoch between 00573 and 00759 - let re = - Regex::new(r#"".*((0057[3-9])|(005[8-9][0-9])|006[0-9][0-9]|(007[0-5][0-9])).*\.era""#) - .unwrap(); - - let mut file_urls = vec![]; - - for cap in re.captures_iter(&body) { - let file_name = &cap[0].strip_prefix('"').unwrap().strip_suffix('"').unwrap(); - let file_url = format!("{ERA_URL}{file_name}"); - file_urls.push(file_url); - } - - ensure!( - file_urls.len() == 187, - "Expected 187 era files, found {:?}", - file_urls.len() - ); - - for url in &file_urls { - let mut url_file = OpenOptions::new() - .read(true) - .write(true) - .create(true) - .truncate(true) - .open(file_path.join("era_urls.txt")) - .unwrap(); - - let re = Regex::new(r#"\d+"#).unwrap(); - let cap = re.captures(url).unwrap(); - let era_number = cap[0].parse::().unwrap(); - println!("Downloading Era number: {:?}", era_number); - - if url_exists_in_file(url_file.try_clone().unwrap(), url).unwrap() { - println!("Era number: {:?} already exists", era_number); - continue; - } - - let res = get(url).await?.bytes().await.unwrap(); - let beacon_state = Era::deserialize_to_beacon_state(&res).unwrap(); - let historical_batch = HistoricalBatch { - block_roots: beacon_state.block_roots().clone(), - state_roots: beacon_state.state_roots().clone(), - }; - - let hash = hex_encode(historical_batch.tree_hash_root().as_slice()) - .strip_prefix("0x") - .unwrap() - .to_string(); - - let first_eight_chars = hash.chars().take(8).collect::(); - - // save `HistoricalBatch` to file - let mut file = File::create(file_path.join(format!( - "historical_batch-{}-{}.ssz", - era_number, first_eight_chars - ))) - .unwrap(); - file.write_all(&historical_batch.as_ssz_bytes()).unwrap(); - // append url to file_url file - url_file.write_all(format!("{}\n", url).as_bytes()).unwrap(); - } - - Ok(()) -} - -fn url_exists_in_file(file: File, url: &str) -> io::Result { - let reader = io::BufReader::new(file); - - // Iterate over the lines in the file - for line in reader.lines() { - let line = line?; - - // Check if the line contains the URL - if line.contains(url) { - return Ok(true); - } - } - - Ok(false) -} diff --git a/bin/poll-latest/Cargo.toml b/bin/poll-latest/Cargo.toml deleted file mode 100644 index ee2517c14..000000000 --- a/bin/poll-latest/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "poll-latest" -authors.workspace = true -categories.workspace = true -edition.workspace = true -keywords.workspace = true -license.workspace = true -readme.workspace = true -repository.workspace = true -rust-version.workspace = true -version.workspace = true - -[dependencies] -alloy = { workspace = true, features = ["provider-ipc", "provider-ws", "pubsub", "reqwest", "rpc-types"] } -anyhow.workspace = true -clap.workspace = true -ethportal-api.workspace = true -futures.workspace = true -tokio.workspace = true -tracing.workspace = true -trin-utils.workspace = true -url.workspace = true diff --git a/bin/poll-latest/src/main.rs b/bin/poll-latest/src/main.rs deleted file mode 100644 index ea963069c..000000000 --- a/bin/poll-latest/src/main.rs +++ /dev/null @@ -1,332 +0,0 @@ -use std::{ - str::FromStr, - sync::{Arc, Mutex}, - time::Instant, -}; - -use alloy::{ - primitives::B256, - providers::{Provider, ProviderBuilder, WsConnect}, -}; -use anyhow::{anyhow, Result}; -use clap::Parser; -use ethportal_api::{ - jsonrpsee::http_client::{HttpClient, HttpClientBuilder}, - types::content_key::overlay::OverlayContentKey, - HistoryContentKey, HistoryNetworkApiClient, -}; -use futures::StreamExt; -use tokio::time::{sleep, Duration}; -use tracing::{debug, info, warn}; -use trin_utils::log::init_tracing_logger; -use url::Url; - -// tldr; -// to poll latest blocks -// cargo run --bin poll_latest -// to poll latest blocks with exponential backoff and give up trying -// to find a piece of data after 240 seconds: -// cargo run --bin poll_latest -- --timeout 240 -// to poll latest blocks, and retry every 3 seconds if the data is not found: -// cargo run --bin poll_latest -- --backoff linear:3 - -const DEFAULT_NODE_IP: &str = "http://127.0.0.1:8545"; - -#[derive(Default)] -struct Metrics { - header_by_hash: Details, - header_by_number: Details, - block_body: Details, - receipts: Details, - // the active audit count measures every individual, active, RFC lookup - active_audit_count: u32, - // the complete audit count measures the total number of blocks whose audits have completed - complete_audit_count: u32, -} - -impl Metrics { - fn display_stats(&self) { - info!( - "Audits (active/complete): {:?}/{:?} // HeaderByHash {:?}% @ {:.2?} // HeaderByNumber {:?}% @ {:.2?} // Bodies {:?}% @ {:.2?} // Receipts {:?}% @ {:.2?}", - self.active_audit_count, - self.complete_audit_count, - (self.header_by_hash.success_count * 100) / self.header_by_hash.total_count(), - self.header_by_hash.average_time, - (self.header_by_number.success_count * 100) / self.header_by_number.total_count(), - self.header_by_number.average_time, - (self.block_body.success_count * 100) / self.block_body.total_count(), - self.block_body.average_time, - (self.receipts.success_count * 100) / self.receipts.total_count(), - self.receipts.average_time); - debug!( - "HeaderByHash: {:?}/{:?} // HeaderByNumber: {:?}/{:?} // Bodies: {:?}/{:?} // Receipts: {:?}/{:?}", - self.header_by_hash.success_count, - self.header_by_hash.total_count(), - self.header_by_number.success_count, - self.header_by_number.total_count(), - self.block_body.success_count, - self.block_body.total_count(), - self.receipts.success_count, - self.receipts.total_count(), - ); - } -} - -pub const MAX_TIMEOUT: Duration = Duration::from_secs(240); - -#[derive(Debug, Default)] -struct Details { - success_count: u32, - failure_count: u32, - average_time: Duration, -} - -impl Details { - fn total_count(&self) -> u32 { - self.success_count + self.failure_count - } - - fn report_success(&mut self, new_time: Duration) { - self.average_time = - (self.average_time * self.success_count + new_time) / (self.success_count + 1); - self.success_count += 1; - } - - fn report_failure(&mut self) { - // don't update average time on failure - self.failure_count += 1; - } -} - -#[tokio::main] -pub async fn main() -> Result<()> { - init_tracing_logger(); - info!("Running Poll Latest script."); - let audit_config = AuditConfig::parse(); - let timeout = match audit_config.timeout { - Some(timeout) => Duration::from_secs(timeout), - None => MAX_TIMEOUT, - }; - let infura_project_id = std::env::var("TRIN_INFURA_PROJECT_ID")?; - let ws = WsConnect::new(format!("wss://mainnet.infura.io/ws/v3/{infura_project_id}")); - let provider = ProviderBuilder::new().on_ws(ws).await?; - let mut stream = provider.subscribe_blocks().await?.into_stream(); - let client = HttpClientBuilder::default().build(audit_config.node_ip)?; - let metrics = Arc::new(Mutex::new(Metrics::default())); - while let Some(block) = stream.next().await { - let block_hash = block.header.hash; - let block_number = block.header.number; - info!("Found new block {block_hash}"); - let timestamp = Instant::now(); - let metrics = metrics.clone(); - tokio::spawn(audit_block( - block_hash, - block_number, - timestamp, - timeout, - audit_config.backoff, - metrics, - client.clone(), - )); - } - Ok(()) -} - -async fn audit_block( - hash: B256, - block_number: u64, - timestamp: Instant, - timeout: Duration, - backoff: Backoff, - metrics: Arc>, - client: HttpClient, -) -> Result<()> { - metrics.lock().unwrap().active_audit_count += 3; - let header_by_hash_handle = tokio::spawn(audit_content_key( - HistoryContentKey::new_block_header_by_hash(hash), - timestamp, - timeout, - backoff, - client.clone(), - )); - let header_by_number_handle = tokio::spawn(audit_content_key( - HistoryContentKey::new_block_header_by_number(block_number), - timestamp, - timeout, - backoff, - client.clone(), - )); - let block_body_handle = tokio::spawn(audit_content_key( - HistoryContentKey::new_block_body(hash), - timestamp, - timeout, - backoff, - client.clone(), - )); - let receipts_handle = tokio::spawn(audit_content_key( - HistoryContentKey::new_block_receipts(hash), - timestamp, - timeout, - backoff, - client.clone(), - )); - match header_by_hash_handle.await? { - Ok(found_time) => { - let mut metrics = metrics.lock().unwrap(); - metrics.active_audit_count -= 1; - let time_diff = found_time - timestamp; - metrics.header_by_hash.report_success(time_diff); - } - Err(_) => { - let mut metrics = metrics.lock().unwrap(); - metrics.active_audit_count -= 1; - metrics.header_by_hash.report_failure(); - } - } - match header_by_number_handle.await? { - Ok(found_time) => { - let mut metrics = metrics.lock().unwrap(); - metrics.active_audit_count -= 1; - let time_diff = found_time - timestamp; - metrics.header_by_number.report_success(time_diff); - } - Err(_) => { - let mut metrics = metrics.lock().unwrap(); - metrics.header_by_number.report_failure(); - metrics.active_audit_count -= 1; - } - } - match block_body_handle.await? { - Ok(found_time) => { - let mut metrics = metrics.lock().unwrap(); - metrics.active_audit_count -= 1; - let time_diff = found_time - timestamp; - metrics.block_body.report_success(time_diff); - } - Err(_) => { - let mut metrics = metrics.lock().unwrap(); - metrics.block_body.report_failure(); - metrics.active_audit_count -= 1; - } - } - match receipts_handle.await? { - Ok(found_time) => { - let mut metrics = metrics.lock().unwrap(); - metrics.active_audit_count -= 1; - let time_diff = found_time - timestamp; - metrics.receipts.report_success(time_diff); - } - Err(_) => { - let mut metrics = metrics.lock().unwrap(); - metrics.receipts.report_failure(); - metrics.active_audit_count -= 1; - } - } - let mut metrics = metrics.lock().unwrap(); - metrics.complete_audit_count += 1; - metrics.display_stats(); - Ok(()) -} - -async fn audit_content_key( - content_key: HistoryContentKey, - timestamp: Instant, - timeout: Duration, - backoff: Backoff, - client: HttpClient, -) -> anyhow::Result { - let mut attempts = 0; - while Instant::now() - timestamp < timeout { - match client.get_content(content_key.clone()).await { - Ok(_) => return Ok(Instant::now()), - _ => { - attempts += 1; - let sleep_time = match backoff { - Backoff::Exponential => attempts * 2, - Backoff::Linear(delay) => delay, - }; - sleep(Duration::from_secs(sleep_time)).await; - } - } - } - let err_msg = format!( - "Unable to find content_key: {:?} within {timeout:?}", - content_key.to_hex(), - ); - warn!("{}", err_msg); - Err(anyhow!("{}", err_msg)) -} - -// CLI Parameter Handling -#[derive(Parser, Debug, PartialEq)] -#[command( - name = "Poll Latest Audit Configuration", - about = "Script to poll availability of latest data" -)] -pub struct AuditConfig { - #[arg(long, help = "max timeout in seconds")] - pub timeout: Option, - - #[arg( - long, - help = "mode for backoff (eg. 'exponential' / 'linear:3' for every 3 seconds)", - default_value = "exponential" - )] - pub backoff: Backoff, - - #[arg(long, help = "ip address of node", default_value = DEFAULT_NODE_IP)] - pub node_ip: Url, -} - -#[derive(Debug, PartialEq, Clone, Copy)] -pub enum Backoff { - Linear(u64), - Exponential, -} - -type ParseError = &'static str; - -impl FromStr for Backoff { - type Err = ParseError; - - fn from_str(s: &str) -> Result { - match s { - "exponential" => Ok(Self::Exponential), - val => { - let index = val.find(':').ok_or("Invalid backoff: unable to find `:`")?; - let (mode, val) = val.split_at(index); - match mode { - "linear" => { - let val = val.trim_start_matches(':'); - let val = val - .parse::() - .map_err(|_| "Invalid backoff: unable to parse delay")?; - Ok(Self::Linear(val)) - } - _ => Err("Invalid backoff: unsupported mode."), - } - } - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_detail() { - let mut details = Details::default(); - assert_eq!(details.total_count(), 0); - details.report_success(Duration::from_secs(1)); - assert_eq!(details.total_count(), 1); - assert_eq!(details.average_time, Duration::from_secs(1)); - details.report_failure(); - assert_eq!(details.total_count(), 2); - details.report_success(Duration::from_secs(3)); - assert_eq!(details.total_count(), 3); - assert_eq!(details.average_time, Duration::from_secs(2)); - details.report_failure(); - assert_eq!(details.total_count(), 4); - } -} diff --git a/bin/purge-invalid-history-content/Cargo.toml b/bin/purge-invalid-history-content/Cargo.toml deleted file mode 100644 index 2d0abfef7..000000000 --- a/bin/purge-invalid-history-content/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "purge-invalid-history-content" -authors.workspace = true -categories.workspace = true -edition.workspace = true -keywords.workspace = true -license.workspace = true -readme.workspace = true -repository.workspace = true -rust-version.workspace = true -version.workspace = true - -[dependencies] -alloy.workspace = true -anyhow.workspace = true -clap.workspace = true -discv5.workspace = true -ethportal-api.workspace = true -portalnet.workspace = true -tracing.workspace = true -trin-storage.workspace = true -trin-utils.workspace = true diff --git a/bin/purge-invalid-history-content/src/main.rs b/bin/purge-invalid-history-content/src/main.rs deleted file mode 100644 index 703d2307b..000000000 --- a/bin/purge-invalid-history-content/src/main.rs +++ /dev/null @@ -1,106 +0,0 @@ -use alloy::primitives::B256; -use anyhow::Result; -use clap::Parser; -use discv5::enr::{CombinedKey, Enr}; -use ethportal_api::types::network::{Network, Subnetwork}; -use portalnet::utils::db::{configure_node_data_dir, configure_trin_data_dir}; -use tracing::info; -use trin_storage::{ - config::StorageCapacityConfig, - versioned::{ContentType, IdIndexedV1StoreConfig}, - PortalStorageConfigFactory, -}; -use trin_utils::log::init_tracing_logger; - -/// iterates history store and removes any invalid network entries -pub fn main() -> Result<()> { - init_tracing_logger(); - let script_config = PurgeConfig::parse(); - - let trin_data_dir = - configure_trin_data_dir(None /* data_dir */, false /* ephemeral */)?; - let (node_data_dir, mut private_key) = - configure_node_data_dir(&trin_data_dir, script_config.private_key, Network::Mainnet)?; - let enr_key = CombinedKey::secp256k1_from_bytes(private_key.as_mut_slice()) - .expect("Failed to create ENR key"); - let enr = Enr::empty(&enr_key).unwrap(); - let node_id = enr.node_id(); - info!("Purging data for NodeID: {node_id}"); - info!("DB Path: {node_data_dir:?}"); - - let config = PortalStorageConfigFactory::new( - StorageCapacityConfig::Combined { - total_mb: script_config.capacity as u32, - subnetworks: vec![Subnetwork::History], - }, - node_id, - node_data_dir, - ) - .unwrap() - .create(&Subnetwork::History) - .unwrap(); - let config = IdIndexedV1StoreConfig::new(ContentType::History, Subnetwork::History, config); - let sql_connection_pool = config.sql_connection_pool.clone(); - let total_count = sql_connection_pool - .get() - .unwrap() - .query_row(&lookup_all_query(), [], |row| row.get::(0)) - .expect("Failed to lookup history content"); - info!("total entry count: {total_count}"); - let lookup_result = sql_connection_pool - .get() - .unwrap() - .query_row(&lookup_epoch_acc_query(), [], |row| { - row.get::(0) - }) - .expect("Failed to fetch history content"); - info!("found {} epoch accumulators", lookup_result); - if script_config.evict { - let removed_count = sql_connection_pool - .get() - .unwrap() - .execute(&delete_epoch_acc_query(), []) - .unwrap(); - info!("removed {} invalid history content values", removed_count); - } - Ok(()) -} - -fn lookup_all_query() -> String { - r#"SELECT COUNT(*) as count FROM ii1_history"#.to_string() -} - -fn lookup_epoch_acc_query() -> String { - r#"SELECT COUNT(*) as count FROM ii1_history WHERE hex(content_key) LIKE "03%""#.to_string() -} - -fn delete_epoch_acc_query() -> String { - r#"DELETE FROM ii1_history WHERE hex(content_key) LIKE '03%'"#.to_string() -} - -// CLI Parameter Handling -#[derive(Parser, Debug, PartialEq)] -#[command( - name = "Trin DB Purge Invalid History Content", - about = "Remove invalid data from Trin History Store" -)] -pub struct PurgeConfig { - #[arg( - long, - help = "(unsafe) Hex private key to generate node id for database namespace (with 0x prefix)" - )] - pub private_key: Option, - - #[arg( - long, - help = "Storage capacity. Must be larger than the current capacity otherwise it will prune data!" - )] - pub capacity: usize, - - #[arg( - long, - help = "Actually evict the history data from db", - default_value = "false" - )] - pub evict: bool, -} diff --git a/bin/sample-range/Cargo.toml b/bin/sample-range/Cargo.toml deleted file mode 100644 index ff2b0673a..000000000 --- a/bin/sample-range/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "sample-range" -authors.workspace = true -categories.workspace = true -edition.workspace = true -keywords.workspace = true -license.workspace = true -readme.workspace = true -repository.workspace = true -rust-version.workspace = true -version.workspace = true - -[dependencies] -alloy = { workspace = true, features = ["eips", "provider-ipc", "provider-ws", "pubsub", "reqwest", "rpc-types"] } -anyhow.workspace = true -clap.workspace = true -ethportal-api.workspace = true -futures.workspace = true -rand.workspace = true -tokio.workspace = true -tracing.workspace = true -trin-utils.workspace = true -trin-validation.workspace = true -url.workspace = true diff --git a/bin/sample-range/src/main.rs b/bin/sample-range/src/main.rs deleted file mode 100644 index e7d8f3214..000000000 --- a/bin/sample-range/src/main.rs +++ /dev/null @@ -1,254 +0,0 @@ -use std::{ - str::FromStr, - sync::{Arc, Mutex}, -}; - -use alloy::{ - eips::BlockNumberOrTag, - primitives::B256, - providers::{Provider, ProviderBuilder}, -}; -use anyhow::Result; -use clap::Parser; -use ethportal_api::{ - jsonrpsee::http_client::{HttpClient, HttpClientBuilder}, - HistoryContentKey, HistoryNetworkApiClient, -}; -use futures::StreamExt; -use rand::seq::SliceRandom; -use tracing::{debug, info, warn}; -use trin_utils::log::init_tracing_logger; -use trin_validation::constants::MERGE_BLOCK_NUMBER; -use url::Url; - -// tldr -// to sample 5 blocks from the shanghai fork: -// cargo run --bin sample_range -- --sample-size 5 --range shanghai -// to sample 50 blocks from the since block #100000: -// cargo run --bin sample_range -- --sample-size 50 --range since:100000 -// to sample 5 blocks from the latest 500 blocks: -// cargo run --bin sample_range -- --sample-size 5 --range latest:500 -// with a custom node ip: -// cargo run --bin sample_range -- --sample-size 1 --range range:1-2 --node-ip http://127.0.0.1:50933 - -#[derive(Default)] -struct Metrics { - header_by_hash: Details, - header_by_number: Details, - block_body: Details, - receipts: Details, -} - -impl Metrics { - fn display_stats(&self) { - info!( - "HeaderByHash {:?}% // HeaderByNumber {:?}% // Bodies {:?}% // Receipts {:?}%", - self.header_by_hash.success_rate(), - self.header_by_number.success_rate(), - self.block_body.success_rate(), - self.receipts.success_rate(), - ); - debug!( - "HeaderByHash: {:?}/{:?} // HeaderByNumber: {:?}/{:?} // Bodies: {:?}/{:?} // Receipts: {:?}/{:?}", - self.header_by_hash.success_count, - self.header_by_hash.total_count(), - self.header_by_number.success_count, - self.header_by_number.total_count(), - self.block_body.success_count, - self.block_body.total_count(), - self.receipts.success_count, - self.receipts.total_count() - ); - } -} - -const FUTURES_BUFFER_SIZE: usize = 8; -const SHANGHAI_BLOCK_NUMBER: u64 = 17034870; -const DEFAULT_NODE_IP: &str = "http://127.0.0.1:8545"; - -#[derive(Debug, Default)] -struct Details { - success_count: u32, - failure_count: u32, -} - -impl Details { - fn total_count(&self) -> u32 { - self.success_count + self.failure_count - } - - fn success_rate(&self) -> u32 { - if self.total_count() == 0 { - 0 - } else { - (self.success_count * 100) / self.total_count() - } - } -} - -#[tokio::main] -pub async fn main() -> Result<()> { - init_tracing_logger(); - let audit_config = SampleConfig::parse(); - info!("Running Sample Range Audit: {:?}", audit_config.range); - let infura_project_id = std::env::var("TRIN_INFURA_PROJECT_ID")?; - let provider = ProviderBuilder::new() - .on_http(format!("https://mainnet.infura.io/v3/{infura_project_id}").parse()?); - let client = HttpClientBuilder::default().build(audit_config.node_ip)?; - let latest_block: u64 = provider.get_block_number().await?; - let (start, end) = match audit_config.range { - SampleRange::Shanghai => (SHANGHAI_BLOCK_NUMBER, latest_block), - SampleRange::FourFours => (0, MERGE_BLOCK_NUMBER), - SampleRange::Since(since) => (since, latest_block), - SampleRange::Latest(latest) => (latest_block - latest, latest_block), - SampleRange::Range(start, end) => (start, end), - }; - let mut blocks: Vec = (start..end).collect(); - let sample_size = std::cmp::min(audit_config.sample_size, blocks.len()); - info!("Sampling {sample_size} blocks from range: {start:?} - {end:?}"); - blocks.shuffle(&mut rand::thread_rng()); - let blocks_to_sample = blocks[0..sample_size].to_vec(); - let metrics = Arc::new(Mutex::new(Metrics::default())); - let futures = futures::stream::iter(blocks_to_sample.into_iter().map(|block_number| { - let client = client.clone(); - let metrics = metrics.clone(); - let provider = provider.clone(); - async move { - let block_hash = provider - .get_block_by_number(BlockNumberOrTag::Number(block_number), false) - .await - .unwrap() - .unwrap() - .header - .hash; - let _ = audit_block(block_number, block_hash, metrics, client).await; - } - })) - .buffer_unordered(FUTURES_BUFFER_SIZE) - .collect::>(); - futures.await; - metrics.lock().unwrap().display_stats(); - Ok(()) -} - -async fn audit_block( - block_number: u64, - hash: B256, - metrics: Arc>, - client: HttpClient, -) -> anyhow::Result<()> { - let header_by_hash_ck = HistoryContentKey::new_block_header_by_hash(hash); - let header_by_number_ck = HistoryContentKey::new_block_header_by_number(block_number); - let body_ck = HistoryContentKey::new_block_body(hash); - let receipts_ck = HistoryContentKey::new_block_receipts(hash); - match client.get_content(header_by_hash_ck).await { - Ok(_) => { - metrics.lock().unwrap().header_by_hash.success_count += 1; - } - Err(_) => { - warn!("Header by hash not found for block #{block_number} - {hash:?}"); - metrics.lock().unwrap().header_by_hash.failure_count += 1; - } - } - match client.get_content(header_by_number_ck).await { - Ok(_) => { - metrics.lock().unwrap().header_by_number.success_count += 1; - } - Err(_) => { - warn!("Header by number not found for block #{block_number} - {hash:?}"); - metrics.lock().unwrap().header_by_number.failure_count += 1; - } - } - match client.get_content(body_ck).await { - Ok(_) => { - metrics.lock().unwrap().block_body.success_count += 1; - } - Err(_) => { - warn!("Body not found for block #{block_number} - {hash:?}"); - metrics.lock().unwrap().block_body.failure_count += 1; - } - } - match client.get_content(receipts_ck).await { - Ok(_) => { - metrics.lock().unwrap().receipts.success_count += 1; - } - Err(_) => { - warn!("Receipts not found for block #{block_number} - {hash:?}"); - metrics.lock().unwrap().receipts.failure_count += 1; - } - } - Ok(()) -} - -// CLI Parameter Handling -#[derive(Parser, Debug, PartialEq)] -#[command( - name = "Sample Config", - about = "Script to sample random blocks from a specified range" -)] -pub struct SampleConfig { - #[arg( - long, - help = "Range to sample blocks from (shanghai, fourfours, since:123, latest:123, range:123-456)" - )] - pub range: SampleRange, - - #[arg(long, help = "Number of blocks to sample")] - pub sample_size: usize, - - #[arg(long, help = "ip address of node", default_value = DEFAULT_NODE_IP)] - pub node_ip: Url, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum SampleRange { - FourFours, - Shanghai, - Since(u64), - Latest(u64), - Range(u64, u64), -} - -type ParseError = &'static str; - -impl FromStr for SampleRange { - type Err = ParseError; - - fn from_str(s: &str) -> Result { - match s { - "fourfours" => Ok(Self::FourFours), - "shanghai" => Ok(Self::Shanghai), - val => { - let index = val.find(':').ok_or("Invalid sample range, missing `:`")?; - let (mode, val) = val.split_at(index); - let val = val.trim_start_matches(':'); - match mode { - "since" => { - let block = val - .parse::() - .map_err(|_| "Invalid sample range: unable to parse block number")?; - Ok(Self::Since(block)) - } - "latest" => { - let block = val - .parse::() - .map_err(|_| "Invalid sample range: unable to parse block number")?; - Ok(Self::Latest(block)) - } - "range" => { - let index = val.find('-').ok_or("Invalid sample range, missing `-`")?; - let (start, end) = val.split_at(index); - let start = start.parse::().map_err(|_| { - "Invalid sample range: unable to parse start block number" - })?; - let end = end.trim_start_matches('-').parse::().map_err(|_| { - "Invalid sample range: unable to parse end block number" - })?; - Ok(Self::Range(start, end)) - } - _ => Err("Invalid sample range: invalid mode"), - } - } - } - } -} diff --git a/bin/test-providers/Cargo.toml b/bin/test-providers/Cargo.toml deleted file mode 100644 index 8fe4ee9db..000000000 --- a/bin/test-providers/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "test-providers" -authors.workspace = true -categories.workspace = true -edition.workspace = true -keywords.workspace = true -license.workspace = true -readme.workspace = true -repository.workspace = true -rust-version.workspace = true -version.workspace = true - -[dependencies] -anyhow.workspace = true -clap.workspace = true -ethereum_ssz.workspace = true -ethportal-api.workspace = true -lazy_static.workspace = true -portal-bridge.workspace = true -rand.workspace = true -reqwest.workspace = true -serde_json = { workspace = true, features = ["preserve_order"]} -tokio.workspace = true -tracing.workspace = true -trin-utils.workspace = true -trin-validation.workspace = true -url.workspace = true diff --git a/bin/test-providers/src/main.rs b/bin/test-providers/src/main.rs deleted file mode 100644 index 3c866bb89..000000000 --- a/bin/test-providers/src/main.rs +++ /dev/null @@ -1,464 +0,0 @@ -use std::{fmt, fs, ops::Range, sync::Arc}; - -use anyhow::{anyhow, Result}; -use clap::Parser; -use ethportal_api::{ - types::{ - execution::accumulator::EpochAccumulator, - jsonrpc::{params::Params, request::JsonRequest}, - }, - utils::bytes::hex_encode, - Header, -}; -use portal_bridge::{api::execution::ExecutionApi, constants::DEFAULT_TOTAL_REQUEST_TIMEOUT}; -use rand::{ - distributions::{Distribution, Uniform}, - thread_rng, -}; -use reqwest::{ - header::{HeaderMap, HeaderValue, CONTENT_TYPE}, - Client, -}; -use serde_json::json; -use ssz::Decode; -use tracing::{debug, info, warn}; -use trin_utils::log::init_tracing_logger; -use trin_validation::{ - accumulator::PreMergeAccumulator, - constants::{ - BERLIN_BLOCK_NUMBER, BYZANTIUM_BLOCK_NUMBER, CONSTANTINOPLE_BLOCK_NUMBER, EPOCH_SIZE, - HOMESTEAD_BLOCK_NUMBER, ISTANBUL_BLOCK_NUMBER, LONDON_BLOCK_NUMBER, MERGE_BLOCK_NUMBER, - SHANGHAI_BLOCK_NUMBER, - }, - header_validator::HeaderValidator, -}; -use url::Url; - -lazy_static::lazy_static! { - static ref PANDAOPS_CLIENT_ID: String = std::env::var("PANDAOPS_CLIENT_ID").unwrap(); - static ref PANDAOPS_CLIENT_SECRET: String = std::env::var("PANDAOPS_CLIENT_SECRET").unwrap(); -} - -// tldr: -// Randomly samples X blocks from every hard fork range. -// Validates that each provider is able to return valid -// headers, receipts, and block bodies for each randomly sampled block. -// Tested Providers: -// - Infura -// - PandaOps-Erigon -// - PandaOps-Geth -// - PandaOps-Archive -// -// cargo run --bin test_providers -- --sample-size 5 -// - -#[tokio::main] -pub async fn main() -> Result<()> { - init_tracing_logger(); - let config = ProviderConfig::parse(); - let latest_block = get_latest_block_number().await?; - info!("Starting to test providers: latest block = {latest_block}"); - let mut all_ranges = Ranges::into_vec(config.sample_size, latest_block); - let mut all_providers: Vec = Providers::into_vec(); - for provider in all_providers.iter_mut() { - info!("Testing Provider: {provider}"); - let mut provider_failures = 0; - let client_url = provider.get_client_url(); - let api = ExecutionApi { - primary: client_url.clone(), - fallback: client_url, - header_validator: HeaderValidator::default(), - request_timeout: DEFAULT_TOTAL_REQUEST_TIMEOUT, - }; - for gossip_range in all_ranges.iter_mut() { - debug!("Testing range: {gossip_range:?}"); - let mut range_failures = 0; - for block in gossip_range.blocks() { - debug!("Testing block: {block}"); - let epoch_acc = match lookup_epoch_acc(*block) { - Ok(epoch_acc) => epoch_acc, - Err(msg) => { - provider_failures += 3; - range_failures += 3; - warn!( - "--- failed to build valid header, receipts, & block body for block: {block}: Invalid epoch acc: {msg}" - ); - continue; - } - }; - let (full_header, _, _, _) = match api.get_header(*block, epoch_acc).await { - Ok(header) => header, - Err(_) => { - provider_failures += 3; - range_failures += 3; - warn!("--- failed to build valid header, receipts, & block body for block: {block}"); - continue; - } - }; - if let Err(msg) = api.get_receipts(&full_header).await { - provider_failures += 1; - range_failures += 1; - warn!("--- failed to build valid receipts for block: {block}: Error: {msg}"); - }; - if let Err(msg) = api.get_block_body(&full_header).await { - provider_failures += 1; - range_failures += 1; - warn!("--- failed to build valid block body for block: {block}: Error: {msg}"); - }; - } - let total = config.sample_size * 3; - gossip_range.update_success_rate(range_failures, total as u64); - debug!( - "Provider: {provider:?} // Range: {gossip_range:?} // Failures: {range_failures}/{total}" - ); - } - let total = - config.sample_size * Ranges::into_vec(config.sample_size, latest_block).len() * 3; - provider.update_success_rate(provider_failures, total as u64); - debug!("Provider Summary: {provider:?} // Failures: {provider_failures}/{total}"); - } - info!("Range Summary:"); - for range in all_ranges.iter() { - range.display_summary(); - } - info!("Provider Summary:"); - for provider in all_providers.iter() { - provider.display_summary(); - } - info!("Finished testing providers"); - Ok(()) -} - -fn lookup_epoch_acc(block: u64) -> Result>> { - if block >= MERGE_BLOCK_NUMBER { - return Ok(None); - } - let epoch_index = block / EPOCH_SIZE; - let pre_merge_acc = PreMergeAccumulator::default(); - let epoch_hash = pre_merge_acc.historical_epochs[epoch_index as usize]; - let epoch_hash_pretty = hex_encode(epoch_hash); - let epoch_hash_pretty = epoch_hash_pretty.trim_start_matches("0x"); - let epoch_acc_path = - format!("./portal-accumulators/bridge_content/0x03{epoch_hash_pretty}.portalcontent"); - let local_epoch_acc = match fs::read(&epoch_acc_path) { - Ok(val) => EpochAccumulator::from_ssz_bytes(&val).map_err(|err| anyhow!("{err:?}"))?, - Err(_) => { - return Err(anyhow!( - "Unable to find local epoch acc at path: {epoch_acc_path:?}" - )) - } - }; - Ok(Some(Arc::new(local_epoch_acc))) -} - -// CLI Parameter Handling -#[derive(Parser, Debug, PartialEq)] -#[command( - name = "Provider Config", - about = "Script to test provider content building stuffs" -)] -pub struct ProviderConfig { - #[arg( - long, - help = "Number of samples to take for each range", - default_value = "5" - )] - pub sample_size: usize, -} - -#[derive(Debug, PartialEq)] -enum Ranges { - // vec of blocks is to store randomly sampled blocks / range - // so that the same blocks are tested across the providers - Frontier((Vec, SuccessRate)), - Homestead((Vec, SuccessRate)), - Byzantium((Vec, SuccessRate)), - Constantinople((Vec, SuccessRate)), - Istanbul((Vec, SuccessRate)), - Berlin((Vec, SuccessRate)), - London((Vec, SuccessRate)), - Merge((Vec, SuccessRate)), - Shanghai((Vec, SuccessRate)), -} - -impl fmt::Display for Ranges { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Ranges::Frontier(_) => write!(f, "Frontier"), - Ranges::Homestead(_) => write!(f, "Homestead"), - Ranges::Byzantium(_) => write!(f, "Byzantium"), - Ranges::Constantinople(_) => write!(f, "Constantinople"), - Ranges::Istanbul(_) => write!(f, "Istanbul"), - Ranges::Berlin(_) => write!(f, "Berlin"), - Ranges::London(_) => write!(f, "London"), - Ranges::Merge(_) => write!(f, "Merge"), - Ranges::Shanghai(_) => write!(f, "Shanghai"), - } - } -} - -impl Ranges { - fn display_summary(&self) { - let success_rate = match self { - Ranges::Frontier((_, success_rate)) - | Ranges::Homestead((_, success_rate)) - | Ranges::Byzantium((_, success_rate)) - | Ranges::Constantinople((_, success_rate)) - | Ranges::Istanbul((_, success_rate)) - | Ranges::Berlin((_, success_rate)) - | Ranges::London((_, success_rate)) - | Ranges::Merge((_, success_rate)) - | Ranges::Shanghai((_, success_rate)) => success_rate, - }; - info!( - "Range: {} // Failure Rate: {}/{}", - self, success_rate.failures, success_rate.total - ); - } - - fn blocks(&self) -> &Vec { - match self { - Ranges::Frontier((blocks, _)) - | Ranges::Homestead((blocks, _)) - | Ranges::Byzantium((blocks, _)) - | Ranges::Constantinople((blocks, _)) - | Ranges::Istanbul((blocks, _)) - | Ranges::Berlin((blocks, _)) - | Ranges::London((blocks, _)) - | Ranges::Merge((blocks, _)) - | Ranges::Shanghai((blocks, _)) => blocks, - } - } - - fn update_success_rate(&mut self, failures: u64, total: u64) { - match self { - Ranges::Frontier((_, success_rate)) - | Ranges::Homestead((_, success_rate)) - | Ranges::Byzantium((_, success_rate)) - | Ranges::Constantinople((_, success_rate)) - | Ranges::Istanbul((_, success_rate)) - | Ranges::Berlin((_, success_rate)) - | Ranges::London((_, success_rate)) - | Ranges::Merge((_, success_rate)) - | Ranges::Shanghai((_, success_rate)) => { - success_rate.failures += failures; - success_rate.total += total; - } - } - } - - pub fn into_vec(sample_size: usize, latest_block: u64) -> Vec { - let mut rng = thread_rng(); - vec![ - Ranges::Frontier(( - Uniform::from(Range { - start: 0, - end: HOMESTEAD_BLOCK_NUMBER, - }) - .sample_iter(&mut rng) - .take(sample_size) - .collect(), - SuccessRate::default(), - )), - Ranges::Homestead(( - Uniform::from(Range { - start: HOMESTEAD_BLOCK_NUMBER, - end: BYZANTIUM_BLOCK_NUMBER, - }) - .sample_iter(&mut rng) - .take(sample_size) - .collect(), - SuccessRate::default(), - )), - Ranges::Byzantium(( - Uniform::from(Range { - start: BYZANTIUM_BLOCK_NUMBER, - end: CONSTANTINOPLE_BLOCK_NUMBER, - }) - .sample_iter(&mut rng) - .take(sample_size) - .collect(), - SuccessRate::default(), - )), - Ranges::Constantinople(( - Uniform::from(Range { - start: CONSTANTINOPLE_BLOCK_NUMBER, - end: ISTANBUL_BLOCK_NUMBER, - }) - .sample_iter(&mut rng) - .take(sample_size) - .collect(), - SuccessRate::default(), - )), - Ranges::Istanbul(( - Uniform::from(Range { - start: ISTANBUL_BLOCK_NUMBER, - end: BERLIN_BLOCK_NUMBER, - }) - .sample_iter(&mut rng) - .take(sample_size) - .collect(), - SuccessRate::default(), - )), - Ranges::Berlin(( - Uniform::from(Range { - start: BERLIN_BLOCK_NUMBER, - end: LONDON_BLOCK_NUMBER, - }) - .sample_iter(&mut rng) - .take(sample_size) - .collect(), - SuccessRate::default(), - )), - Ranges::London(( - Uniform::from(Range { - start: LONDON_BLOCK_NUMBER, - end: MERGE_BLOCK_NUMBER, - }) - .sample_iter(&mut rng) - .take(sample_size) - .collect(), - SuccessRate::default(), - )), - Ranges::Merge(( - Uniform::from(Range { - start: MERGE_BLOCK_NUMBER, - end: SHANGHAI_BLOCK_NUMBER, - }) - .sample_iter(&mut rng) - .take(sample_size) - .collect(), - SuccessRate::default(), - )), - Ranges::Shanghai(( - Uniform::from(Range { - start: SHANGHAI_BLOCK_NUMBER, - end: latest_block, - }) - .sample_iter(&mut rng) - .take(sample_size) - .collect(), - SuccessRate::default(), - )), - ] - } -} - -#[derive(Debug, PartialEq, Default)] -struct SuccessRate { - failures: u64, - total: u64, -} - -#[derive(Debug, PartialEq)] -enum Providers { - PandaGeth(SuccessRate), - PandaErigon(SuccessRate), - PandaArchive(SuccessRate), - Infura(SuccessRate), -} - -impl fmt::Display for Providers { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Providers::PandaGeth(_) => write!(f, "PandaGeth"), - Providers::PandaErigon(_) => write!(f, "PandaErigon"), - Providers::PandaArchive(_) => write!(f, "PandaArchive"), - Providers::Infura(_) => write!(f, "Infura"), - } - } -} - -impl Providers { - fn display_summary(&self) { - let success_rate = match self { - Providers::PandaGeth(success_rate) - | Providers::PandaErigon(success_rate) - | Providers::PandaArchive(success_rate) - | Providers::Infura(success_rate) => success_rate, - }; - info!( - "Provider: {} // Failure Rate: {:?} / {:?}", - self, success_rate.failures, success_rate.total - ); - } - - fn update_success_rate(&mut self, failures: u64, total: u64) { - match self { - Providers::PandaGeth(success_rate) - | Providers::PandaErigon(success_rate) - | Providers::PandaArchive(success_rate) - | Providers::Infura(success_rate) => { - success_rate.failures += failures; - success_rate.total += total; - } - } - } - - fn into_vec() -> Vec { - vec![ - Providers::PandaGeth(SuccessRate::default()), - Providers::PandaErigon(SuccessRate::default()), - Providers::PandaArchive(SuccessRate::default()), - Providers::Infura(SuccessRate::default()), - ] - } - - fn get_client_url(&self) -> Url { - match self { - Providers::Infura(_) => { - let infura_key = std::env::var("TRIN_INFURA_PROJECT_ID").unwrap(); - Url::parse(&format!("https://mainnet.infura.io/v3/{}", infura_key)).unwrap() - } - _ => match self { - Providers::PandaGeth(_) => { - Url::parse("https://geth-lighthouse.mainnet.eu1.ethpandaops.io/") - .expect("to be able to parse static base el endpoint url") - } - Providers::PandaErigon(_) => { - Url::parse("https://erigon-lighthouse.mainnet.eu1.ethpandaops.io/") - .expect("to be able to parse static base el endpoint url") - } - Providers::PandaArchive(_) => Url::parse("https://archive.mainnet.ethpandaops.io/") - .expect("to be able to parse static base el endpoint url"), - _ => panic!("not implemented"), - }, - } - } -} - -async fn get_latest_block_number() -> Result { - let mut headers = HeaderMap::new(); - headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json")); - headers.insert( - "CF-Access-Client-Id", - HeaderValue::from_str(&PANDAOPS_CLIENT_ID) - .map_err(|_| anyhow!("Invalid CF-Access-Client-Id header value"))?, - ); - headers.insert( - "CF-Access-Client-Secret", - HeaderValue::from_str(&PANDAOPS_CLIENT_SECRET) - .map_err(|_| anyhow!("Invalid CF-Access-Client-Secret header value"))?, - ); - let request = JsonRequest::new( - "eth_getBlockByNumber".to_string(), - Params::Array(vec![json!("latest"), json!(false)]), - /* id= */ 1, - ); - let response = Client::new() - .post("https://geth-lighthouse.mainnet.eu1.ethpandaops.io/") - .headers(headers) - .json(&request) - .send() - .await - .map_err(|e| anyhow!("Request failed: {:?}", e))?; - let response = response - .json::() - .await - .map_err(|e| anyhow!("Failed to read response text: {:?}", e))?; - let result = response - .get("result") - .ok_or_else(|| anyhow!("Unable to fetch latest block"))?; - let header: Header = serde_json::from_value(result.clone())?; - Ok(header.number) -} diff --git a/ethportal-api/Cargo.toml b/ethportal-api/Cargo.toml index 74d2a2f5c..17c9788ec 100644 --- a/ethportal-api/Cargo.toml +++ b/ethportal-api/Cargo.toml @@ -20,7 +20,6 @@ base64 = "0.13.0" bimap = "0.6.3" bytes.workspace = true c-kzg = "1.0.0" -clap.workspace = true const_format = {version = "0.2.0", features = ["rust_1_64"]} discv5.workspace = true eth_trie.workspace = true @@ -51,7 +50,6 @@ tokio.workspace = true tree_hash.workspace = true tree_hash_derive.workspace = true ureq.workspace = true -url.workspace = true validator = { version = "0.13.0", features = ["derive"] } [dev-dependencies]