From f3e82ad018e3fa9fa252229c84f92dd9e06840f9 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Wed, 12 Jun 2024 18:37:04 -0300 Subject: [PATCH 1/7] add a new `zebra-scanner` binary --- Cargo.lock | 3 + zebra-scan/Cargo.toml | 13 +++ zebra-scan/src/bin/scanner/main.rs | 104 ++++++++++++++++++ zebra-scan/src/config.rs | 2 +- zebra-scan/src/service/scan_task/scan.rs | 23 ++-- zebra-scan/src/service/tests.rs | 8 +- .../shielded_scan/scan_task_commands.rs | 13 ++- 7 files changed, 143 insertions(+), 23 deletions(-) create mode 100644 zebra-scan/src/bin/scanner/main.rs diff --git a/Cargo.lock b/Cargo.lock index ea09e646716..252da40761f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6046,15 +6046,18 @@ dependencies = [ "insta", "itertools 0.13.0", "jubjub", + "lazy_static", "proptest", "proptest-derive", "rand 0.8.5", "sapling-crypto", "semver 1.0.23", "serde", + "structopt", "tokio", "tower", "tracing", + "tracing-subscriber", "zcash_address", "zcash_client_backend", "zcash_keys", diff --git a/zebra-scan/Cargo.toml b/zebra-scan/Cargo.toml index 3ac701e678c..bdb0f0a3915 100644 --- a/zebra-scan/Cargo.toml +++ b/zebra-scan/Cargo.toml @@ -19,6 +19,11 @@ name = "scanner-grpc-server" path = "src/bin/rpc_server.rs" required-features = ["proptest-impl"] +[[bin]] # Bin to run the Scanner tool +name = "zebra-scanner" +path = "src/bin/scanner/main.rs" +required-features = ["shielded-scan"] + [features] # Production features that activate extra dependencies, or extra features in dependencies @@ -39,6 +44,9 @@ proptest-impl = [ "zcash_note_encryption", ] +# Needed for the zebra-scanner binary. +shielded-scan = [] + [dependencies] color-eyre = "0.6.3" @@ -77,6 +85,11 @@ zcash_note_encryption = { version = "0.4.0", optional = true } zebra-test = { path = "../zebra-test", version = "1.0.0-beta.38", optional = true } +# zebra-scanner binary dependencies +tracing-subscriber = "0.3.18" +structopt = "0.3.26" +lazy_static = "1.4.0" + [dev-dependencies] insta = { version = "1.39.0", features = ["ron", "redactions"] } tokio = { version = "1.37.0", features = ["test-util"] } diff --git a/zebra-scan/src/bin/scanner/main.rs b/zebra-scan/src/bin/scanner/main.rs new file mode 100644 index 00000000000..6c295bce404 --- /dev/null +++ b/zebra-scan/src/bin/scanner/main.rs @@ -0,0 +1,104 @@ +//! The zebra-scanner binary. +//! +//! The zebra-scanner binary is a standalone binary that scans the Zcash blockchain for transactions using the given sapling keys. +use lazy_static::lazy_static; +use structopt::StructOpt; +use tracing::*; + +use zebra_chain::parameters::Network; +use zebra_state::{ChainTipSender, SaplingScanningKey}; + +use core::net::SocketAddr; +use std::path::PathBuf; + +#[tokio::main] +/// Runs the zebra scanner binary with the given arguments. +async fn main() -> Result<(), Box> { + // Display all logs from the zebra-scan crate. + tracing_subscriber::fmt::fmt() + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .init(); + + // Parse command line arguments. + let args = Args::from_args(); + let network = args.network; + let sapling_keys_to_scan = args + .sapling_keys_to_scan + .into_iter() + .map(|key| (key, 1)) + .collect(); + let cache_dir = args.cache_dir; + let listen_addr = args.listen_addr; + + // Create a state config with arguments. + let state_config = zebra_state::Config { + cache_dir, + ..zebra_state::Config::default() + }; + + // Create a scanner config with arguments. + let scanner_config = zebra_scan::Config { + sapling_keys_to_scan, + listen_addr, + ..zebra_scan::Config::default() + }; + + // Get a read-only state and the database. + let (read_state, db, _) = zebra_state::init_read_only(state_config, &network); + + // Get the initial tip block from the database. + let initial_tip = db + .tip_block() + .map(zebra_state::CheckpointVerifiedBlock::from) + .map(zebra_state::ChainTipBlock::from); + + // Create a chain tip sender and use it to get a chain tip change. + let (_chain_tip_sender, _latest_chain_tip, chain_tip_change) = + ChainTipSender::new(initial_tip, &network); + + // Spawn the scan task. + let scan_task_handle = + { zebra_scan::spawn_init(scanner_config, network, read_state, chain_tip_change) }; + + // Pin the scan task handle. + tokio::pin!(scan_task_handle); + + // Wait for task to finish + loop { + let _result = tokio::select! { + scan_result = &mut scan_task_handle => scan_result + .expect("unexpected panic in the scan task") + .map(|_| info!("scan task exited")), + }; + } +} + +// Default values for the zebra-scanner arguments. +lazy_static! { + static ref DEFAULT_CACHE_DIR: String = zebra_state::Config::default() + .cache_dir + .to_str() + .expect("default cache dir is valid") + .to_string(); + static ref DEFAULT_NETWORK: String = Network::default().to_string(); +} + +/// zebra-scanner arguments +#[derive(Clone, Debug, Eq, PartialEq, StructOpt)] +pub struct Args { + /// Path to an existing zebra state cache directory. + #[structopt(default_value = &DEFAULT_CACHE_DIR, short, long)] + pub cache_dir: PathBuf, + + /// The Zcash network where the scanner will run. + #[structopt(default_value = &DEFAULT_NETWORK, short, long)] + pub network: Network, + + /// The sapling keys to scan for. + #[structopt(short, long)] + pub sapling_keys_to_scan: Vec, + + /// IP address and port for the zebra-scan gRPC server. + #[structopt(short, long)] + pub listen_addr: Option, +} diff --git a/zebra-scan/src/config.rs b/zebra-scan/src/config.rs index 915a5e32342..5688141b247 100644 --- a/zebra-scan/src/config.rs +++ b/zebra-scan/src/config.rs @@ -38,7 +38,7 @@ pub struct Config { // // TODO: Remove fields that are only used by the state, and create a common database config. #[serde(flatten)] - db_config: DbConfig, + pub db_config: DbConfig, } impl Debug for Config { diff --git a/zebra-scan/src/service/scan_task/scan.rs b/zebra-scan/src/service/scan_task/scan.rs index d605328fb45..e2a36581ae1 100644 --- a/zebra-scan/src/service/scan_task/scan.rs +++ b/zebra-scan/src/service/scan_task/scan.rs @@ -12,7 +12,7 @@ use tokio::{ sync::{mpsc::Sender, watch}, task::JoinHandle, }; -use tower::{buffer::Buffer, util::BoxService, Service, ServiceExt}; +use tower::{Service, ServiceExt}; use tracing::Instrument; use zcash_address::unified::{Encoding, Fvk, Ufvk}; @@ -38,7 +38,7 @@ use zebra_chain::{ transaction::Transaction, }; use zebra_node_services::scan_service::response::ScanResult; -use zebra_state::{ChainTipChange, SaplingScannedResult, TransactionIndex}; +use zebra_state::{ChainTipChange, ReadStateService, SaplingScannedResult, TransactionIndex}; use crate::{ service::{ScanTask, ScanTaskCommand}, @@ -51,11 +51,8 @@ mod scan_range; pub use scan_range::ScanRangeTaskBuilder; -/// The generic state type used by the scanner. -pub type State = Buffer< - BoxService, - zebra_state::Request, ->; +/// The read state type used by the scanner. +pub type State = ReadStateService; /// Wait a few seconds at startup for some blocks to get verified. /// @@ -262,13 +259,13 @@ pub async fn scan_height_and_store_results( .ready() .await .map_err(|e| eyre!(e))? - .call(zebra_state::Request::Block(height.into())) + .call(zebra_state::ReadRequest::Block(height.into())) .await .map_err(|e| eyre!(e))?; let block = match block { - zebra_state::Response::Block(Some(block)) => block, - zebra_state::Response::Block(None) => return Ok(None), + zebra_state::ReadResponse::Block(Some(block)) => block, + zebra_state::ReadResponse::Block(None) => return Ok(None), _ => unreachable!("unmatched response to a state::Block request"), }; @@ -515,13 +512,13 @@ async fn tip_height(mut state: State) -> Result { .ready() .await .map_err(|e| eyre!(e))? - .call(zebra_state::Request::Tip) + .call(zebra_state::ReadRequest::Tip) .await .map_err(|e| eyre!(e))?; match tip { - zebra_state::Response::Tip(Some((height, _hash))) => Ok(height), - zebra_state::Response::Tip(None) => Ok(Height(0)), + zebra_state::ReadResponse::Tip(Some((height, _hash))) => Ok(height), + zebra_state::ReadResponse::Tip(None) => Ok(Height(0)), _ => unreachable!("unmatched response to a state::Tip request"), } } diff --git a/zebra-scan/src/service/tests.rs b/zebra-scan/src/service/tests.rs index fdc8e465d81..83c99ad8fd3 100644 --- a/zebra-scan/src/service/tests.rs +++ b/zebra-scan/src/service/tests.rs @@ -273,12 +273,12 @@ pub async fn scan_service_registers_keys_correctly() -> Result<()> { async fn scan_service_registers_keys_correctly_for(network: &Network) -> Result<()> { // Mock the state. - let (state, _, _, chain_tip_change) = zebra_state::populated_state(vec![], network).await; + let (_, read_state, _, chain_tip_change) = zebra_state::populated_state(vec![], network).await; // Instantiate the scan service. - let mut scan_service = ServiceBuilder::new() - .buffer(2) - .service(ScanService::new(&Config::ephemeral(), network, state, chain_tip_change).await); + let mut scan_service = ServiceBuilder::new().buffer(2).service( + ScanService::new(&Config::ephemeral(), network, read_state, chain_tip_change).await, + ); // Mock three Sapling keys. let mocked_keys = mock_sapling_scanning_keys(3, network); diff --git a/zebrad/tests/common/shielded_scan/scan_task_commands.rs b/zebrad/tests/common/shielded_scan/scan_task_commands.rs index f626c0485a8..a7693b8c812 100644 --- a/zebrad/tests/common/shielded_scan/scan_task_commands.rs +++ b/zebrad/tests/common/shielded_scan/scan_task_commands.rs @@ -11,7 +11,6 @@ use std::{fs, time::Duration}; use color_eyre::{eyre::eyre, Result}; use tokio::sync::mpsc::error::TryRecvError; -use tower::ServiceBuilder; use zebra_chain::{ block::Height, chain_tip::ChainTip, @@ -82,9 +81,15 @@ pub(crate) async fn run() -> Result<()> { let scan_db_path = zebrad_state_path.join(SCANNER_DATABASE_KIND); fs::remove_dir_all(std::path::Path::new(&scan_db_path)).ok(); - let (state_service, _read_state_service, latest_chain_tip, chain_tip_change) = + let (_state_service, _read_state_service, latest_chain_tip, chain_tip_change) = start_state_service_with_cache_dir(&network, zebrad_state_path.clone()).await?; + let state_config = zebra_state::Config { + cache_dir: zebrad_state_path.clone(), + ..zebra_state::Config::default() + }; + let (read_state, _db, _) = zebra_state::init_read_only(state_config, &network); + let chain_tip_height = latest_chain_tip .best_tip_height() .ok_or_else(|| eyre!("State directory doesn't have a chain tip block"))?; @@ -105,11 +110,9 @@ pub(crate) async fn run() -> Result<()> { tracing::info!("opened state service with valid chain tip height, starting scan task",); - let state = ServiceBuilder::new().buffer(10).service(state_service); - // Create an ephemeral `Storage` instance let storage = Storage::new(&scan_config, &network, false); - let mut scan_task = ScanTask::spawn(storage, state, chain_tip_change); + let mut scan_task = ScanTask::spawn(storage, read_state, chain_tip_change); tracing::info!("started scan task, sending register/subscribe keys messages with zecpages key to start scanning for a new key",); From cd2f4887f9b193bbdccbbbbd2bc5be0dbadfb989 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Fri, 14 Jun 2024 10:43:09 -0300 Subject: [PATCH 2/7] update arguments --- zebra-scan/src/bin/scanner/main.rs | 38 ++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/zebra-scan/src/bin/scanner/main.rs b/zebra-scan/src/bin/scanner/main.rs index 6c295bce404..82819a8a03f 100644 --- a/zebra-scan/src/bin/scanner/main.rs +++ b/zebra-scan/src/bin/scanner/main.rs @@ -21,18 +21,22 @@ async fn main() -> Result<(), Box> { // Parse command line arguments. let args = Args::from_args(); + + let zebrad_cache_dir = args.zebrad_cache_dir; + let scanning_cache_dir = args.scanning_cache_dir; + let mut db_config = zebra_scan::Config::default().db_config; + db_config.cache_dir = scanning_cache_dir; let network = args.network; let sapling_keys_to_scan = args .sapling_keys_to_scan .into_iter() .map(|key| (key, 1)) .collect(); - let cache_dir = args.cache_dir; let listen_addr = args.listen_addr; // Create a state config with arguments. let state_config = zebra_state::Config { - cache_dir, + cache_dir: zebrad_cache_dir, ..zebra_state::Config::default() }; @@ -40,7 +44,7 @@ async fn main() -> Result<(), Box> { let scanner_config = zebra_scan::Config { sapling_keys_to_scan, listen_addr, - ..zebra_scan::Config::default() + db_config, }; // Get a read-only state and the database. @@ -75,7 +79,13 @@ async fn main() -> Result<(), Box> { // Default values for the zebra-scanner arguments. lazy_static! { - static ref DEFAULT_CACHE_DIR: String = zebra_state::Config::default() + static ref DEFAULT_ZEBRAD_CACHE_DIR: String = zebra_state::Config::default() + .cache_dir + .to_str() + .expect("default cache dir is valid") + .to_string(); + static ref DEFAULT_SCANNER_CACHE_DIR: String = zebra_scan::Config::default() + .db_config .cache_dir .to_str() .expect("default cache dir is valid") @@ -86,19 +96,23 @@ lazy_static! { /// zebra-scanner arguments #[derive(Clone, Debug, Eq, PartialEq, StructOpt)] pub struct Args { - /// Path to an existing zebra state cache directory. - #[structopt(default_value = &DEFAULT_CACHE_DIR, short, long)] - pub cache_dir: PathBuf, + /// Path to zebrad state. + #[structopt(default_value = &DEFAULT_ZEBRAD_CACHE_DIR, long)] + pub zebrad_cache_dir: PathBuf, + + /// Path to scanning state. + #[structopt(default_value = &DEFAULT_SCANNER_CACHE_DIR, long)] + pub scanning_cache_dir: PathBuf, - /// The Zcash network where the scanner will run. - #[structopt(default_value = &DEFAULT_NETWORK, short, long)] + /// The Zcash network. + #[structopt(default_value = &DEFAULT_NETWORK, long)] pub network: Network, /// The sapling keys to scan for. - #[structopt(short, long)] + #[structopt(long)] pub sapling_keys_to_scan: Vec, - /// IP address and port for the zebra-scan gRPC server. - #[structopt(short, long)] + /// IP address and port for the gRPC server. + #[structopt(long)] pub listen_addr: Option, } From 171610f3a1dcccc28a8e229e959e907b003e6cce Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Mon, 17 Jun 2024 11:55:04 -0300 Subject: [PATCH 3/7] allow birthday in config --- Cargo.lock | 1 + zebra-scan/Cargo.toml | 1 + zebra-scan/src/bin/scanner/main.rs | 25 ++++++++++++++++++++++--- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 252da40761f..be34e2ed7a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6053,6 +6053,7 @@ dependencies = [ "sapling-crypto", "semver 1.0.23", "serde", + "serde_json", "structopt", "tokio", "tower", diff --git a/zebra-scan/Cargo.toml b/zebra-scan/Cargo.toml index bdb0f0a3915..4c28a0f0300 100644 --- a/zebra-scan/Cargo.toml +++ b/zebra-scan/Cargo.toml @@ -89,6 +89,7 @@ zebra-test = { path = "../zebra-test", version = "1.0.0-beta.38", optional = tru tracing-subscriber = "0.3.18" structopt = "0.3.26" lazy_static = "1.4.0" +serde_json = "1.0.117" [dev-dependencies] insta = { version = "1.39.0", features = ["ron", "redactions"] } diff --git a/zebra-scan/src/bin/scanner/main.rs b/zebra-scan/src/bin/scanner/main.rs index 82819a8a03f..42b0328a98b 100644 --- a/zebra-scan/src/bin/scanner/main.rs +++ b/zebra-scan/src/bin/scanner/main.rs @@ -5,12 +5,31 @@ use lazy_static::lazy_static; use structopt::StructOpt; use tracing::*; -use zebra_chain::parameters::Network; +use zebra_chain::{block::Height, parameters::Network}; use zebra_state::{ChainTipSender, SaplingScanningKey}; use core::net::SocketAddr; use std::path::PathBuf; +/// A strucure with sapling key and birthday height. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize)] +pub struct SaplingKey { + key: SaplingScanningKey, + #[serde(default = "min_height")] + birthday_height: Height, +} + +fn min_height() -> Height { + Height(0) +} + +impl std::str::FromStr for SaplingKey { + type Err = Box; + fn from_str(value: &str) -> Result { + Ok(serde_json::from_str(value)?) + } +} + #[tokio::main] /// Runs the zebra scanner binary with the given arguments. async fn main() -> Result<(), Box> { @@ -30,7 +49,7 @@ async fn main() -> Result<(), Box> { let sapling_keys_to_scan = args .sapling_keys_to_scan .into_iter() - .map(|key| (key, 1)) + .map(|key| (key.key, key.birthday_height.0)) .collect(); let listen_addr = args.listen_addr; @@ -110,7 +129,7 @@ pub struct Args { /// The sapling keys to scan for. #[structopt(long)] - pub sapling_keys_to_scan: Vec, + pub sapling_keys_to_scan: Vec, /// IP address and port for the gRPC server. #[structopt(long)] From a9bc25cab97a705236bbd82d9578e1114778f28c Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Mon, 17 Jun 2024 12:58:58 -0300 Subject: [PATCH 4/7] remove required feature --- zebra-scan/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/zebra-scan/Cargo.toml b/zebra-scan/Cargo.toml index 4c28a0f0300..b316e899291 100644 --- a/zebra-scan/Cargo.toml +++ b/zebra-scan/Cargo.toml @@ -22,7 +22,6 @@ required-features = ["proptest-impl"] [[bin]] # Bin to run the Scanner tool name = "zebra-scanner" path = "src/bin/scanner/main.rs" -required-features = ["shielded-scan"] [features] From 8713575455312b27f4cf5c76ed8cceae760b418d Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Mon, 17 Jun 2024 13:18:19 -0300 Subject: [PATCH 5/7] add `env-filter` feature to `tracing-subscriber` dependency --- zebra-scan/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zebra-scan/Cargo.toml b/zebra-scan/Cargo.toml index b316e899291..6ab800e6b3d 100644 --- a/zebra-scan/Cargo.toml +++ b/zebra-scan/Cargo.toml @@ -85,7 +85,7 @@ zcash_note_encryption = { version = "0.4.0", optional = true } zebra-test = { path = "../zebra-test", version = "1.0.0-beta.38", optional = true } # zebra-scanner binary dependencies -tracing-subscriber = "0.3.18" +tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } structopt = "0.3.26" lazy_static = "1.4.0" serde_json = "1.0.117" From 923ba846a4b5229799ef6756454dc34eeaffb854 Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 20 Jun 2024 12:49:35 -0400 Subject: [PATCH 6/7] use sync task --- Cargo.lock | 1 + zebra-scan/Cargo.toml | 1 + zebra-scan/src/bin/scanner/main.rs | 43 +++++++++++++++++------------- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index be34e2ed7a5..4741d56a66d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6067,6 +6067,7 @@ dependencies = [ "zebra-chain", "zebra-grpc", "zebra-node-services", + "zebra-rpc", "zebra-state", "zebra-test", ] diff --git a/zebra-scan/Cargo.toml b/zebra-scan/Cargo.toml index 6ab800e6b3d..314fd73ca57 100644 --- a/zebra-scan/Cargo.toml +++ b/zebra-scan/Cargo.toml @@ -68,6 +68,7 @@ zebra-chain = { path = "../zebra-chain", version = "1.0.0-beta.38", features = [ zebra-state = { path = "../zebra-state", version = "1.0.0-beta.38", features = ["shielded-scan"] } zebra-node-services = { path = "../zebra-node-services", version = "1.0.0-beta.38", features = ["shielded-scan"] } zebra-grpc = { path = "../zebra-grpc", version = "0.1.0-alpha.5" } +zebra-rpc = { path = "../zebra-rpc", version = "1.0.0-beta.38" } chrono = { version = "0.4.38", default-features = false, features = ["clock", "std", "serde"] } diff --git a/zebra-scan/src/bin/scanner/main.rs b/zebra-scan/src/bin/scanner/main.rs index 42b0328a98b..38b2f2a76fc 100644 --- a/zebra-scan/src/bin/scanner/main.rs +++ b/zebra-scan/src/bin/scanner/main.rs @@ -1,12 +1,13 @@ //! The zebra-scanner binary. //! //! The zebra-scanner binary is a standalone binary that scans the Zcash blockchain for transactions using the given sapling keys. +use color_eyre::eyre::eyre; use lazy_static::lazy_static; use structopt::StructOpt; use tracing::*; use zebra_chain::{block::Height, parameters::Network}; -use zebra_state::{ChainTipSender, SaplingScanningKey}; +use zebra_state::SaplingScanningKey; use core::net::SocketAddr; use std::path::PathBuf; @@ -67,17 +68,14 @@ async fn main() -> Result<(), Box> { }; // Get a read-only state and the database. - let (read_state, db, _) = zebra_state::init_read_only(state_config, &network); - - // Get the initial tip block from the database. - let initial_tip = db - .tip_block() - .map(zebra_state::CheckpointVerifiedBlock::from) - .map(zebra_state::ChainTipBlock::from); - - // Create a chain tip sender and use it to get a chain tip change. - let (_chain_tip_sender, _latest_chain_tip, chain_tip_change) = - ChainTipSender::new(initial_tip, &network); + let (read_state, _latest_chain_tip, chain_tip_change, sync_task) = + zebra_rpc::sync::init_read_state_with_syncer( + state_config, + &network, + args.zebra_rpc_listen_addr, + ) + .await? + .map_err(|err| eyre!(err))?; // Spawn the scan task. let scan_task_handle = @@ -85,14 +83,18 @@ async fn main() -> Result<(), Box> { // Pin the scan task handle. tokio::pin!(scan_task_handle); + tokio::pin!(sync_task); // Wait for task to finish - loop { - let _result = tokio::select! { - scan_result = &mut scan_task_handle => scan_result - .expect("unexpected panic in the scan task") - .map(|_| info!("scan task exited")), - }; + tokio::select! { + scan_result = &mut scan_task_handle => scan_result + .expect("unexpected panic in the scan task") + .map(|_| info!("scan task exited")) + .map_err(Into::into), + sync_result = &mut sync_task => { + sync_result.expect("unexpected panic in the scan task"); + Ok(()) + } } } @@ -131,6 +133,11 @@ pub struct Args { #[structopt(long)] pub sapling_keys_to_scan: Vec, + /// The listen address of Zebra's RPC server used by the syncer to check for chain tip changes + /// and get blocks in Zebra's non-finalized state. + #[structopt(long)] + pub zebra_rpc_listen_addr: SocketAddr, + /// IP address and port for the gRPC server. #[structopt(long)] pub listen_addr: Option, From 23f72e918fbb0064713c002148c24add6f6df845 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Fri, 21 Jun 2024 20:12:03 -0300 Subject: [PATCH 7/7] codespell --- zebra-scan/src/bin/scanner/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zebra-scan/src/bin/scanner/main.rs b/zebra-scan/src/bin/scanner/main.rs index 38b2f2a76fc..97cbd095bdc 100644 --- a/zebra-scan/src/bin/scanner/main.rs +++ b/zebra-scan/src/bin/scanner/main.rs @@ -12,7 +12,7 @@ use zebra_state::SaplingScanningKey; use core::net::SocketAddr; use std::path::PathBuf; -/// A strucure with sapling key and birthday height. +/// A structure with sapling key and birthday height. #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize)] pub struct SaplingKey { key: SaplingScanningKey,