zebra_scanner/
main.rs
1use color_eyre::eyre::eyre;
5use lazy_static::lazy_static;
6use structopt::StructOpt;
7use tracing::*;
8
9use zebra_chain::{block::Height, parameters::Network};
10use zebra_state::SaplingScanningKey;
11
12use core::net::SocketAddr;
13use std::path::{Path, PathBuf};
14
15#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize)]
17pub struct SaplingKey {
18 key: SaplingScanningKey,
19 #[serde(default = "min_height")]
20 birthday_height: Height,
21}
22
23fn min_height() -> Height {
24 Height(0)
25}
26
27impl std::str::FromStr for SaplingKey {
28 type Err = Box<dyn std::error::Error>;
29 fn from_str(value: &str) -> Result<Self, Self::Err> {
30 Ok(serde_json::from_str(value)?)
31 }
32}
33
34#[tokio::main]
35async fn main() -> Result<(), Box<dyn std::error::Error>> {
37 let tracing_filter: String = match std::env::var("RUST_LOG") {
39 Ok(val) if !val.is_empty() => val,
40 _ => "info".to_string(),
41 };
42
43 tracing_subscriber::fmt::fmt()
44 .with_env_filter(tracing_filter)
45 .init();
46
47 let args = Args::from_args();
49
50 let zebrad_cache_dir = args.zebrad_cache_dir;
51 validate_dir(&zebrad_cache_dir)?;
52
53 let scanning_cache_dir = args.scanning_cache_dir;
54 let mut db_config = zebra_scan::Config::default().db_config;
55 db_config.cache_dir = scanning_cache_dir;
56 let network = args.network;
57 let sapling_keys_to_scan = args
58 .sapling_keys_to_scan
59 .into_iter()
60 .map(|key| (key.key, key.birthday_height.0))
61 .collect();
62 let listen_addr = args.listen_addr;
63
64 let state_config = zebra_state::Config {
66 cache_dir: zebrad_cache_dir,
67 ..zebra_state::Config::default()
68 };
69
70 let scanner_config = zebra_scan::Config {
72 sapling_keys_to_scan,
73 listen_addr,
74 db_config,
75 };
76
77 let (read_state, _latest_chain_tip, chain_tip_change, sync_task) =
79 zebra_rpc::sync::init_read_state_with_syncer(
80 state_config,
81 &network,
82 args.zebra_rpc_listen_addr,
83 )
84 .await?
85 .map_err(|err| eyre!(err))?;
86
87 let scan_task_handle =
89 { zebra_scan::spawn_init(scanner_config, network, read_state, chain_tip_change) };
90
91 tokio::pin!(scan_task_handle);
93 tokio::pin!(sync_task);
94
95 tokio::select! {
97 scan_result = &mut scan_task_handle => scan_result
98 .expect("unexpected panic in the scan task")
99 .map(|_| info!("scan task exited"))
100 .map_err(Into::into),
101 sync_result = &mut sync_task => {
102 sync_result.expect("unexpected panic in the scan task");
103 Ok(())
104 }
105 }
106}
107
108lazy_static! {
110 static ref DEFAULT_ZEBRAD_CACHE_DIR: String = zebra_state::Config::default()
111 .cache_dir
112 .to_str()
113 .expect("default cache dir is valid")
114 .to_string();
115 static ref DEFAULT_SCANNER_CACHE_DIR: String = zebra_scan::Config::default()
116 .db_config
117 .cache_dir
118 .to_str()
119 .expect("default cache dir is valid")
120 .to_string();
121 static ref DEFAULT_NETWORK: String = Network::default().to_string();
122}
123
124#[derive(Clone, Debug, Eq, PartialEq, StructOpt)]
126pub struct Args {
127 #[structopt(default_value = &DEFAULT_ZEBRAD_CACHE_DIR, long)]
129 pub zebrad_cache_dir: PathBuf,
130
131 #[structopt(default_value = &DEFAULT_SCANNER_CACHE_DIR, long)]
133 pub scanning_cache_dir: PathBuf,
134
135 #[structopt(default_value = &DEFAULT_NETWORK, long)]
137 pub network: Network,
138
139 #[structopt(long)]
141 pub sapling_keys_to_scan: Vec<SaplingKey>,
142
143 #[structopt(long)]
146 pub zebra_rpc_listen_addr: SocketAddr,
147
148 #[structopt(long)]
150 pub listen_addr: Option<SocketAddr>,
151}
152
153fn validate_dir(dir: &Path) -> Result<(), Box<dyn std::error::Error>> {
155 match dir.try_exists() {
156 Ok(true) => Ok(()),
157 Ok(false) => {
158 let err_msg = format!("directory {} does not exist.", dir.display());
159 error!("{}", err_msg);
160 Err(std::io::Error::new(std::io::ErrorKind::NotFound, err_msg).into())
161 }
162 Err(e) => {
163 error!("directory {} could not be accessed: {:?}", dir.display(), e);
164 Err(e.into())
165 }
166 }
167}