Skip to main content

miden_validator/server/
mod.rs

1use std::net::SocketAddr;
2
3use anyhow::Context;
4use miden_node_proto::server::miden_validator_v1_validator_service;
5use miden_node_proto_build::validator_api_descriptor;
6use miden_node_store::BlockStore;
7use miden_node_tracing::grpc::grpc_trace_fn;
8use miden_node_tracing::info;
9use miden_node_tracing::panic::catch_panic_layer_fn;
10use miden_node_utils::clap::GrpcOptions;
11use miden_node_utils::shutdown::CancellationToken;
12use tokio::net::TcpListener;
13use tokio_stream::wrappers::TcpListenerStream;
14use tower_http::catch_panic::CatchPanicLayer;
15use tower_http::trace::TraceLayer;
16
17use crate::db::{ValidatorDbReader, ValidatorDbWriter};
18use crate::{
19    DataDirectory,
20    GoldenOperatorKey,
21    LOG_TARGET,
22    PrivateRecordSealer,
23    TransactionInputDecrypter,
24    ValidatorSigner,
25};
26
27mod admin_service;
28mod validator_service;
29
30use validator_service::ValidatorService;
31
32// VALIDATOR SERVER
33// ================================================================================
34
35/// The handle into running the gRPC validator server.
36///
37/// Facilitates the running of the gRPC server which implements the validator API.
38pub struct ValidatorServer {
39    /// The address of the validator component.
40    pub address: SocketAddr,
41    /// gRPC server options for internal services (timeouts, connection caps).
42    ///
43    /// If the handler takes longer than this duration, the server cancels the call.
44    pub grpc_options: GrpcOptions,
45
46    /// The signer used to sign blocks.
47    pub signer: ValidatorSigner,
48
49    /// The decrypter for the shared transaction encryption key, used to unseal encrypted
50    /// transaction inputs.
51    pub decrypter: std::sync::Arc<dyn TransactionInputDecrypter>,
52
53    /// The public Golden key used to seal private records.
54    pub private_record_sealer: PrivateRecordSealer,
55
56    /// The data directory for the validator component's database files.
57    pub data_directory: DataDirectory,
58
59    /// Handle to the shared validator database. Carrying the write handle makes the public API the
60    /// database's single writer; reads reach the shared read handle through its `Deref`.
61    pub db: ValidatorDbWriter,
62}
63
64/// Serves the private validator administration API on a network-isolated listener.
65pub struct ValidatorAdminServer {
66    /// Address of the private administration listener.
67    pub address: SocketAddr,
68    /// Golden key material used to issue this validator's decryption shares.
69    pub operator_key: GoldenOperatorKey,
70    /// Read handle to the shared validator database. The administration API only ever reads, so it
71    /// holds a [`ValidatorDbReader`] and cannot mutate validator state.
72    pub reader: ValidatorDbReader,
73}
74
75impl ValidatorAdminServer {
76    /// Serves the private validator administration API.
77    pub async fn serve(self, shutdown: CancellationToken) -> anyhow::Result<()> {
78        let listener =
79            TcpListener::bind(self.address).await.context("failed to bind admin address")?;
80        self.serve_on(listener, shutdown).await
81    }
82
83    async fn serve_on(
84        self,
85        listener: TcpListener,
86        shutdown: CancellationToken,
87    ) -> anyhow::Result<()> {
88        let endpoint =
89            listener.local_addr().context("failed to read validator admin listen address")?;
90        info!(
91            target: LOG_TARGET,
92            "Validator admin server ready",
93            service.name = "miden-validator-admin",
94            validator.admin_listen = endpoint.to_string()
95        );
96
97        axum::serve(listener, admin_service::router(self.operator_key, self.reader))
98            .with_graceful_shutdown(shutdown.cancelled_owned())
99            .await
100            .context("failed to serve validator admin API")
101    }
102}
103
104impl ValidatorServer {
105    /// Serves the validator RPC API.
106    ///
107    /// Executes in place (i.e. not spawned) and will run indefinitely until a fatal error is
108    /// encountered.
109    pub async fn serve(self, shutdown: CancellationToken) -> anyhow::Result<()> {
110        // The database pool is opened once by the caller and shared with the admin server, so this
111        // takes the handle rather than opening its own connection.
112        let db = self.db;
113
114        // Initialize block store.
115        let block_store = BlockStore::load(self.data_directory.block_store_dir())
116            .context("failed to load block store")?;
117
118        // Load initial metrics from the database for the in-memory counters.
119        let metrics = db.load_initial_metrics().await.context("failed to load initial metrics")?;
120
121        let listener = TcpListener::bind(self.address)
122            .await
123            .context("failed to bind to block producer address")?;
124
125        let reflection_service = tonic_reflection::server::Builder::configure()
126            .register_file_descriptor_set(validator_api_descriptor())
127            .build_v1()
128            .context("failed to build reflection service")?;
129
130        let service = ValidatorService::new(
131            self.signer,
132            db,
133            self.decrypter,
134            self.private_record_sealer,
135            block_store,
136            metrics,
137        )
138        .await
139        .context("failed to initialize validator server")?;
140        let endpoint = listener.local_addr().context("failed to read validator listen address")?;
141        info!(
142            target: LOG_TARGET,
143            "Validator ready",
144            service.name = "miden-validator",
145            service.version = env!("CARGO_PKG_VERSION"),
146            validator.listen = endpoint.to_string(),
147            block.number = metrics.chain_tip
148        );
149
150        // Build the gRPC server with the API service and trace layer.
151        tonic::transport::Server::builder()
152            .layer(CatchPanicLayer::custom(catch_panic_layer_fn))
153            .layer(TraceLayer::new_for_grpc().make_span_with(grpc_trace_fn))
154            .timeout(self.grpc_options.request_timeout)
155            .add_service(miden_validator_v1_validator_service::service(service))
156            .add_service(reflection_service)
157            .serve_with_incoming_shutdown(
158                TcpListenerStream::new(listener),
159                shutdown.cancelled_owned(),
160            )
161            .await
162            .context("failed to serve validator API")
163    }
164}