use std::net::SocketAddr;
use std::num::NonZeroUsize;
use anyhow::Context;
use miden_node_proto::server::validator_api;
use miden_node_proto_build::validator_api_descriptor;
use miden_node_store::BlockStore;
use miden_node_utils::clap::GrpcOptionsInternal;
use miden_node_utils::panic::catch_panic_layer_fn;
use miden_node_utils::shutdown::CancellationToken;
use miden_node_utils::tracing::grpc::grpc_trace_fn;
use tokio::net::TcpListener;
use tokio_stream::wrappers::TcpListenerStream;
use tower_http::catch_panic::CatchPanicLayer;
use tower_http::trace::TraceLayer;
use crate::db::{
count_signed_blocks,
count_validated_transactions,
load_chain_tip,
load_with_pool_size,
};
use crate::{DataDirectory, LOG_TARGET, ValidatorSigner};
mod validator_service;
use validator_service::ValidatorService;
pub struct ValidatorServer {
pub address: SocketAddr,
pub grpc_options: GrpcOptionsInternal,
pub signer: ValidatorSigner,
pub data_directory: DataDirectory,
pub sqlite_connection_pool_size: NonZeroUsize,
}
impl ValidatorServer {
pub async fn serve(self, shutdown: CancellationToken) -> anyhow::Result<()> {
tracing::info!(target: LOG_TARGET, endpoint=?self.address, "Initializing server");
let db = load_with_pool_size(
self.data_directory.database_path(),
self.sqlite_connection_pool_size,
)
.await
.context("failed to initialize validator database")?;
let block_store = BlockStore::load(self.data_directory.block_store_dir())
.context("failed to load block store")?;
let (initial_chain_tip, initial_tx_count, initial_block_count) = db
.query("load_initial_metrics", |conn| {
let tip = load_chain_tip(conn)?.map_or(0, |h| h.block_num().as_u32());
let tx_count = u64::try_from(count_validated_transactions(conn)?).unwrap_or(0);
let block_count = u64::try_from(count_signed_blocks(conn)?).unwrap_or(0);
Ok::<_, miden_node_db::DatabaseError>((tip, tx_count, block_count))
})
.await
.context("failed to load initial metrics")?;
let listener = TcpListener::bind(self.address)
.await
.context("failed to bind to block producer address")?;
let reflection_service = tonic_reflection::server::Builder::configure()
.register_file_descriptor_set(validator_api_descriptor())
.build_v1()
.context("failed to build reflection service")?;
tonic::transport::Server::builder()
.layer(CatchPanicLayer::custom(catch_panic_layer_fn))
.layer(TraceLayer::new_for_grpc().make_span_with(grpc_trace_fn))
.timeout(self.grpc_options.request_timeout)
.add_service(validator_api::service(
ValidatorService::new(
self.signer,
db,
block_store,
initial_chain_tip,
initial_tx_count,
initial_block_count,
)
.await
.context("failed to initialize validator server")?,
))
.add_service(reflection_service)
.serve_with_incoming_shutdown(
TcpListenerStream::new(listener),
shutdown.cancelled_owned(),
)
.await
.context("failed to serve validator API")
}
}