use tokio::time::Instant;
use tracing::info;
use zaino_fetch::jsonrpsee::connector::test_node_and_return_url;
use zaino_serve::server::{config::GrpcServerConfig, grpc::TonicServer, jsonrpc::JsonRpcServer};
#[allow(deprecated)]
use zaino_state::{
BackendType, FetchService, FetchServiceConfig, IndexerService, LightWalletService,
StateService, StateServiceConfig, StatusType, ZcashIndexer, ZcashService,
};
use crate::{config::ZainodConfig, error::IndexerError};
pub struct Indexer<Service: ZcashService + LightWalletService> {
json_server: Option<JsonRpcServer>,
server: Option<TonicServer>,
service: Option<IndexerService<Service>>,
}
pub async fn start_indexer(
config: ZainodConfig,
) -> Result<tokio::task::JoinHandle<Result<(), IndexerError>>, IndexerError> {
startup_message();
info!("Starting Zaino");
spawn_indexer(config).await
}
pub async fn spawn_indexer(
config: ZainodConfig,
) -> Result<tokio::task::JoinHandle<Result<(), IndexerError>>, IndexerError> {
config.check_config()?;
info!(
address = %config.validator_settings.validator_jsonrpc_listen_address,
"Checking connection with node"
);
if let Some(donation_address) = &config.donation_address {
info!("Instance donation address: {}", donation_address);
}
let zebrad_uri = test_node_and_return_url(
&config.validator_settings.validator_jsonrpc_listen_address,
config.validator_settings.validator_cookie_path.clone(),
config.validator_settings.validator_user.clone(),
config.validator_settings.validator_password.clone(),
)
.await?;
info!(uri = %zebrad_uri, "Connected to node via JsonRPSee");
#[allow(deprecated)]
match config.backend {
BackendType::State => {
let state_config = StateServiceConfig::try_from(config.clone())?;
Indexer::<StateService>::launch_inner(state_config, config)
.await
.map(|res| res.0)
}
BackendType::Fetch => {
let fetch_config = FetchServiceConfig::try_from(config.clone())?;
Indexer::<FetchService>::launch_inner(fetch_config, config)
.await
.map(|res| res.0)
}
}
}
impl<Service: ZcashService + LightWalletService + Send + Sync + 'static> Indexer<Service>
where
IndexerError: From<<Service::Subscriber as ZcashIndexer>::Error>,
{
pub async fn launch_inner(
service_config: Service::Config,
indexer_config: ZainodConfig,
) -> Result<
(
tokio::task::JoinHandle<Result<(), IndexerError>>,
Service::Subscriber,
),
IndexerError,
> {
let service = IndexerService::<Service>::spawn(service_config).await?;
let service_subscriber = service.inner_ref().get_subscriber();
let json_server = match indexer_config.json_server_settings {
Some(json_server_config) => Some(
JsonRpcServer::spawn(service.inner_ref().get_subscriber(), json_server_config)
.await
.unwrap(),
),
None => None,
};
let grpc_server = TonicServer::spawn(
service.inner_ref().get_subscriber(),
GrpcServerConfig {
listen_address: indexer_config.grpc_settings.listen_address,
tls: indexer_config.grpc_settings.tls,
},
)
.await
.unwrap();
let mut indexer = Self {
json_server,
server: Some(grpc_server),
service: Some(service),
};
let mut server_interval = tokio::time::interval(tokio::time::Duration::from_millis(100));
let mut last_log_time = Instant::now();
let log_interval = tokio::time::Duration::from_secs(10);
let serve_task = tokio::task::spawn(async move {
loop {
if last_log_time.elapsed() >= log_interval {
indexer.log_status();
last_log_time = Instant::now();
}
if indexer.check_for_critical_errors() {
indexer.close().await;
return Err(IndexerError::Restart);
}
if indexer.check_for_shutdown() {
indexer.close().await;
return Ok(());
}
server_interval.tick().await;
}
});
Ok((serve_task, service_subscriber.inner()))
}
fn check_for_critical_errors(&self) -> bool {
let status = self.status_int();
if status == 5 || status >= 7 {
let service_status = self
.service
.as_ref()
.map(|s| s.inner_ref().status())
.unwrap_or(StatusType::Offline);
let server_status = self
.server
.as_ref()
.map(|s| s.status())
.unwrap_or(StatusType::Offline);
tracing::error!(
combined_status = status,
?service_status,
?server_status,
"check_for_critical_errors triggered"
);
return true;
}
false
}
fn check_for_shutdown(&self) -> bool {
if self.status_int() == 4 {
return true;
}
false
}
async fn close(&mut self) {
if let Some(mut json_server) = self.json_server.take() {
json_server.close().await;
json_server.status.store(StatusType::Offline);
}
if let Some(mut server) = self.server.take() {
server.close().await;
server.status.store(StatusType::Offline);
}
if let Some(service) = self.service.take() {
let mut service = service.inner();
service.close();
}
}
fn status_int(&self) -> usize {
let service_status = match &self.service {
Some(service) => service.inner_ref().status(),
None => return 7,
};
let json_server_status = self
.json_server
.as_ref()
.map(|json_server| json_server.status());
let mut server_status = match &self.server {
Some(server) => server.status(),
None => return 7,
};
if let Some(json_status) = json_server_status {
server_status = StatusType::combine(server_status, json_status);
}
usize::from(StatusType::combine(service_status, server_status))
}
pub fn status(&self) -> StatusType {
StatusType::from(self.status_int())
}
pub fn log_status(&self) {
let service_status = match &self.service {
Some(service) => service.inner_ref().status(),
None => StatusType::Offline,
};
let json_server_status = match &self.json_server {
Some(json_server) => json_server.status(),
None => StatusType::Offline,
};
let grpc_server_status = match &self.server {
Some(server) => server.status(),
None => StatusType::Offline,
};
let service_status_symbol = service_status.get_status_symbol();
let json_server_status_symbol = json_server_status.get_status_symbol();
let grpc_server_status_symbol = grpc_server_status.get_status_symbol();
info!(
"Zaino status check - ChainState Service:{}{} JsonRPC Server:{}{} gRPC Server:{}{}",
service_status_symbol,
service_status,
json_server_status_symbol,
json_server_status,
grpc_server_status_symbol,
grpc_server_status
);
}
}
fn startup_message() {
let welcome_message = r#"
░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒████▓░▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒████▓▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▓▓▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒██▓▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒██▓▒▒▒▒▒
▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓███▓██▓▒▒▒▒▒
▒▒▒▒▒▒▒▓▓▓▓▒███▓░▒▓▓████████████████▓▓▒▒▒▒▒▒▒
▒▒▒▒▒▒▓▓▓▓▒▓████▓▓███████████████████▓▒▓▓▒▒▒▒
▒▒▒▒▒▓▓▓▓▓▒▒▓▓▓▓████████████████████▓▒▓▓▓▒▒▒▒
▒▒▒▒▒▓▓▓▓▓█████████████████████████▓▒▓▓▓▓▓▒▒▒
▒▒▒▒▓▓▓▒▓█████████████████████████▓▓▓▓▓▓▓▓▒▒▒
▒▒▒▒▒▓▓▓████████████████████████▓▓▓▓▓▓▓▓▓▒▒▒▒
▒▒▒▒▒▓▒███████████████████████▒▓▓▓▓▓▓▓▓▓▓▒▒▒▒
▒▒▒▒▒▒▓███████████████████▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒
▒▒▒▒▒▒▓███████████████▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒
▒▒▒▒▒▒▓██████████▓▓▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒
▒▒▒▒███▓▒▓▓▓▓▓▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▓████▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
Thank you for using ZingoLabs Zaino!
- Donate to us at https://free2z.cash/zingolabs.
****** Please note Zaino is currently in development and should not be used to run mainnet nodes. ******
"#;
println!("{welcome_message}");
}