//! The gRPC server implementation use futures_util::future::TryFutureExt; use tonic::{transport::Server, Response, Status}; use tower::ServiceExt; use scanner::scanner_server::{Scanner, ScannerServer}; use scanner::{ClearResultsRequest, DeleteKeysRequest, Empty, InfoReply}; use zebra_node_services::scan_service::{ request::Request as ScanServiceRequest, response::Response as ScanServiceResponse, }; /// The generated scanner proto pub mod scanner { tonic::include_proto!("scanner"); } type BoxError = Box; #[derive(Debug)] /// The server implementation pub struct ScannerRPC where ScanService: tower::Service + Clone + Send + Sync + 'static, >::Future: Send, { scan_service: ScanService, } #[tonic::async_trait] impl Scanner for ScannerRPC where ScanService: tower::Service + Clone + Send + Sync + 'static, >::Future: Send, { async fn get_info( &self, _request: tonic::Request, ) -> Result, Status> { let ScanServiceResponse::Info { min_sapling_birthday_height, } = self .scan_service .clone() .ready() .and_then(|service| service.call(ScanServiceRequest::Info)) .await .map_err(|_| Status::unknown("scan service was unavailable"))? else { return Err(Status::unknown( "scan service returned an unexpected response", )); }; let reply = scanner::InfoReply { min_sapling_birthday_height: min_sapling_birthday_height.0, }; Ok(Response::new(reply)) } async fn clear_results( &self, request: tonic::Request, ) -> Result, Status> { let keys = request.into_inner().keys; if keys.is_empty() { let msg = "must provide at least 1 key for which to clear results"; return Err(Status::invalid_argument(msg)); } let ScanServiceResponse::ClearedResults = self .scan_service .clone() .ready() .and_then(|service| service.call(ScanServiceRequest::ClearResults(keys))) .await .map_err(|err| Status::unknown(format!("scan service returned error: {err}")))? else { return Err(Status::unknown( "scan service returned an unexpected response", )); }; Ok(Response::new(Empty {})) } async fn delete_keys( &self, request: tonic::Request, ) -> Result, Status> { let keys = request.into_inner().keys; if keys.is_empty() { let msg = "must provide at least 1 key to delete"; return Err(Status::invalid_argument(msg)); } let ScanServiceResponse::DeletedKeys = self .scan_service .clone() .ready() .and_then(|service| service.call(ScanServiceRequest::DeleteKeys(keys))) .await .map_err(|err| Status::unknown(format!("scan service returned error: {err}")))? else { return Err(Status::unknown( "scan service returned an unexpected response", )); }; Ok(Response::new(Empty {})) } } /// Initializes the zebra-scan gRPC server pub async fn init(scan_service: ScanService) -> Result<(), color_eyre::Report> where ScanService: tower::Service + Clone + Send + Sync + 'static, >::Future: Send, { let addr = "[::1]:50051".parse()?; let service = ScannerRPC { scan_service }; Server::builder() .add_service(ScannerServer::new(service)) .serve(addr) .await?; Ok(()) }