//! `start` subcommand - entry point for starting a zebra node //! //! ## Application Structure //! //! A zebra node consists of the following services and tasks: //! //! * Network Service //! * primary interface to the node //! * handles all external network requests for the Zcash protocol //! * via zebra_network::Message and zebra_network::Response //! * provides an interface to the rest of the network for other services and //! tasks running within this node //! * via zebra_network::Request //! * Consensus Service //! * handles all validation logic for the node //! * verifies blocks using zebra-chain and zebra-script, then stores verified //! blocks in zebra-state //! * Sync Task //! * runs in the background and continuously queries the network for //! new blocks to be verified and added to the local state //! * Inbound Service //! * handles requests from peers for network data and chain data //! * performs transaction and block diffusion //! * downloads and verifies gossiped blocks and transactions use abscissa_core::{config, Command, FrameworkError, Options, Runnable}; use color_eyre::eyre::{eyre, Report}; use futures::{select, FutureExt}; use tokio::sync::oneshot; use tower::{builder::ServiceBuilder, util::BoxService}; use crate::{ components::{ mempool::{self, Mempool}, sync, tokio::{RuntimeRun, TokioComponent}, ChainSync, Inbound, }, config::ZebradConfig, prelude::*, }; /// `start` subcommand #[derive(Command, Debug, Options)] pub struct StartCmd { /// Filter strings #[options(free)] filters: Vec, } impl StartCmd { async fn start(&self) -> Result<(), Report> { let config = app_config().clone(); info!(?config); info!("initializing node state"); // TODO: use ChainTipChange to get tip changes (#2374, #2710, #2711, #2712, #2713, #2714) let (state_service, latest_chain_tip, chain_tip_change) = zebra_state::init(config.state.clone(), config.network.network); let state = ServiceBuilder::new().buffer(20).service(state_service); info!("initializing verifiers"); // TODO: use the transaction verifier to verify mempool transactions (#2637, #2606) let (chain_verifier, tx_verifier) = zebra_consensus::chain::init( config.consensus.clone(), config.network.network, state.clone(), ) .await; info!("initializing network"); // The service that our node uses to respond to requests by peers. The // load_shed middleware ensures that we reduce the size of the peer set // in response to excess load. let (setup_tx, setup_rx) = oneshot::channel(); let inbound = ServiceBuilder::new() .load_shed() .buffer(20) .service(Inbound::new( setup_rx, state.clone(), chain_verifier.clone(), )); let (peer_set, address_book) = zebra_network::init(config.network.clone(), inbound, latest_chain_tip.clone()).await; info!("initializing syncer"); let (syncer, sync_status) = ChainSync::new(&config, peer_set.clone(), state.clone(), chain_verifier); info!("initializing mempool"); let (mempool, mempool_transaction_receiver) = Mempool::new( &config.mempool, peer_set.clone(), state, tx_verifier, sync_status.clone(), latest_chain_tip, chain_tip_change.clone(), ); let mempool = BoxService::new(mempool); let mempool = ServiceBuilder::new().buffer(20).service(mempool); setup_tx .send((peer_set.clone(), address_book, mempool.clone())) .map_err(|_| eyre!("could not send setup data to inbound service"))?; let syncer_error_future = syncer.sync(); let sync_gossip_task_handle = tokio::spawn(sync::gossip_best_tip_block_hashes( sync_status.clone(), chain_tip_change.clone(), peer_set.clone(), )); let mempool_crawler_task_handle = mempool::Crawler::spawn( &config.mempool, peer_set.clone(), mempool, sync_status, chain_tip_change, ); let tx_gossip_task_handle = tokio::spawn(mempool::gossip_mempool_transaction_id( mempool_transaction_receiver, peer_set, )); select! { sync_result = syncer_error_future.fuse() => sync_result, sync_gossip_result = sync_gossip_task_handle.fuse() => sync_gossip_result .expect("unexpected panic in the chain tip block gossip task") .map_err(|e| eyre!(e)), mempool_crawl_result = mempool_crawler_task_handle.fuse() => mempool_crawl_result .expect("unexpected panic in the mempool crawler") .map_err(|e| eyre!(e)), tx_gossip_result = tx_gossip_task_handle.fuse() => tx_gossip_result .expect("unexpected panic in the transaction gossip task") .map_err(|e| eyre!(e)), } } } impl Runnable for StartCmd { /// Start the application. fn run(&self) { info!("Starting zebrad"); let rt = app_writer() .state_mut() .components .get_downcast_mut::() .expect("TokioComponent should be available") .rt .take(); rt.expect("runtime should not already be taken") .run(self.start()); } } impl config::Override for StartCmd { // Process the given command line options, overriding settings from // a configuration file using explicit flags taken from command-line // arguments. fn override_config(&self, mut config: ZebradConfig) -> Result { if !self.filters.is_empty() { config.tracing.filter = Some(self.filters.join(",")); } Ok(config) } }