Skip to main content

miden_validator/server/
mod.rs

1use std::net::SocketAddr;
2use std::num::NonZeroUsize;
3
4use anyhow::Context;
5use miden_node_proto::server::validator_api;
6use miden_node_proto_build::validator_api_descriptor;
7use miden_node_store::BlockStore;
8use miden_node_utils::clap::GrpcOptionsInternal;
9use miden_node_utils::panic::catch_panic_layer_fn;
10use miden_node_utils::shutdown::CancellationToken;
11use miden_node_utils::tracing::grpc::grpc_trace_fn;
12use tokio::net::TcpListener;
13use tokio_stream::wrappers::TcpListenerStream;
14use tower_http::catch_panic::CatchPanicLayer;
15use tower_http::trace::TraceLayer;
16
17use crate::db::{
18    count_signed_blocks,
19    count_validated_transactions,
20    load_chain_tip,
21    load_with_pool_size,
22};
23use crate::{DataDirectory, LOG_TARGET, ValidatorSigner};
24
25mod validator_service;
26
27use validator_service::ValidatorService;
28
29// VALIDATOR SERVER
30// ================================================================================
31
32/// The handle into running the gRPC validator server.
33///
34/// Facilitates the running of the gRPC server which implements the validator API.
35pub struct ValidatorServer {
36    /// The address of the validator component.
37    pub address: SocketAddr,
38    /// gRPC server options for internal services (timeouts, connection caps).
39    ///
40    /// If the handler takes longer than this duration, the server cancels the call.
41    pub grpc_options: GrpcOptionsInternal,
42
43    /// The signer used to sign blocks.
44    pub signer: ValidatorSigner,
45
46    /// The data directory for the validator component's database files.
47    pub data_directory: DataDirectory,
48
49    /// Maximum number of SQLite connections in the validator database connection pool.
50    pub sqlite_connection_pool_size: NonZeroUsize,
51}
52
53impl ValidatorServer {
54    /// Serves the validator RPC API.
55    ///
56    /// Executes in place (i.e. not spawned) and will run indefinitely until a fatal error is
57    /// encountered.
58    pub async fn serve(self, shutdown: CancellationToken) -> anyhow::Result<()> {
59        tracing::info!(target: LOG_TARGET, endpoint=?self.address, "Initializing server");
60
61        // Initialize database connection.
62        let db = load_with_pool_size(
63            self.data_directory.database_path(),
64            self.sqlite_connection_pool_size,
65        )
66        .await
67        .context("failed to initialize validator database")?;
68
69        // Initialize block store.
70        let block_store = BlockStore::load(self.data_directory.block_store_dir())
71            .context("failed to load block store")?;
72
73        // Load initial metrics from the database for the in-memory counters.
74        let (initial_chain_tip, initial_tx_count, initial_block_count) = db
75            .query("load_initial_metrics", |conn| {
76                let tip = load_chain_tip(conn)?.map_or(0, |h| h.block_num().as_u32());
77                let tx_count = u64::try_from(count_validated_transactions(conn)?).unwrap_or(0);
78                let block_count = u64::try_from(count_signed_blocks(conn)?).unwrap_or(0);
79                Ok::<_, miden_node_db::DatabaseError>((tip, tx_count, block_count))
80            })
81            .await
82            .context("failed to load initial metrics")?;
83
84        let listener = TcpListener::bind(self.address)
85            .await
86            .context("failed to bind to block producer address")?;
87
88        let reflection_service = tonic_reflection::server::Builder::configure()
89            .register_file_descriptor_set(validator_api_descriptor())
90            .build_v1()
91            .context("failed to build reflection service")?;
92
93        // Build the gRPC server with the API service and trace layer.
94        tonic::transport::Server::builder()
95            .layer(CatchPanicLayer::custom(catch_panic_layer_fn))
96            .layer(TraceLayer::new_for_grpc().make_span_with(grpc_trace_fn))
97            .timeout(self.grpc_options.request_timeout)
98            .add_service(validator_api::service(
99                ValidatorService::new(
100                    self.signer,
101                    db,
102                    block_store,
103                    initial_chain_tip,
104                    initial_tx_count,
105                    initial_block_count,
106                )
107                .await
108                .context("failed to initialize validator server")?,
109            ))
110            .add_service(reflection_service)
111            .serve_with_incoming_shutdown(
112                TcpListenerStream::new(listener),
113                shutdown.cancelled_owned(),
114            )
115            .await
116            .context("failed to serve validator API")
117    }
118}