use std::net::SocketAddr;
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::GrpcOptions;
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::{ValidatorDbReader, ValidatorDbWriter};
use crate::{
DataDirectory,
GoldenOperatorKey,
LOG_TARGET,
PrivateRecordSealer,
TransactionInputDecrypter,
ValidatorSigner,
};
mod admin_service;
mod validator_service;
use validator_service::ValidatorService;
pub struct ValidatorServer {
pub address: SocketAddr,
pub grpc_options: GrpcOptions,
pub signer: ValidatorSigner,
pub decrypter: std::sync::Arc<dyn TransactionInputDecrypter>,
pub private_record_sealer: PrivateRecordSealer,
pub data_directory: DataDirectory,
pub db: ValidatorDbWriter,
}
pub struct ValidatorAdminServer {
pub address: SocketAddr,
pub operator_key: GoldenOperatorKey,
pub reader: ValidatorDbReader,
}
impl ValidatorAdminServer {
pub async fn serve(self, shutdown: CancellationToken) -> anyhow::Result<()> {
let listener =
TcpListener::bind(self.address).await.context("failed to bind admin address")?;
self.serve_on(listener, shutdown).await
}
async fn serve_on(
self,
listener: TcpListener,
shutdown: CancellationToken,
) -> anyhow::Result<()> {
let endpoint =
listener.local_addr().context("failed to read validator admin listen address")?;
tracing::info!(
target: LOG_TARGET,
{
service.name = "miden-validator-admin",
validator.admin_listen = %endpoint,
},
"Validator admin server ready",
);
axum::serve(listener, admin_service::router(self.operator_key, self.reader))
.with_graceful_shutdown(shutdown.cancelled_owned())
.await
.context("failed to serve validator admin API")
}
}
impl ValidatorServer {
pub async fn serve(self, shutdown: CancellationToken) -> anyhow::Result<()> {
let db = self.db;
let block_store = BlockStore::load(self.data_directory.block_store_dir())
.context("failed to load block store")?;
let metrics = db.load_initial_metrics().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")?;
let service = ValidatorService::new(
self.signer,
db,
self.decrypter,
self.private_record_sealer,
block_store,
metrics,
)
.await
.context("failed to initialize validator server")?;
let endpoint = listener.local_addr().context("failed to read validator listen address")?;
tracing::info!(
target: LOG_TARGET,
{
service.name = "miden-validator",
service.version = env!("CARGO_PKG_VERSION"),
validator.listen = %endpoint,
block.number = metrics.chain_tip,
},
"Validator ready",
);
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(service))
.add_service(reflection_service)
.serve_with_incoming_shutdown(
TcpListenerStream::new(listener),
shutdown.cancelled_owned(),
)
.await
.context("failed to serve validator API")
}
}