miden_node_rpc/server/
mod.rs1use std::num::NonZeroUsize;
2
3use accept::AcceptHeaderLayer;
4use anyhow::Context;
5use miden_node_proto::generated::rpc::api_server;
6use miden_node_proto_build::rpc_api_descriptor;
7use miden_node_utils::clap::GrpcOptionsExternal;
8use miden_node_utils::cors::cors_for_grpc_web_layer;
9use miden_node_utils::grpc;
10use miden_node_utils::panic::{CatchPanicLayer, catch_panic_layer_fn};
11use miden_node_utils::tracing::grpc::grpc_trace_fn;
12use tokio::net::TcpListener;
13use tokio_stream::wrappers::TcpListenerStream;
14use tonic_reflection::server;
15use tonic_web::GrpcWebLayer;
16use tower_http::classify::{GrpcCode, GrpcErrorsAsFailures, SharedClassifier};
17use tower_http::trace::TraceLayer;
18use tracing::info;
19use url::Url;
20
21use crate::COMPONENT;
22use crate::server::health::HealthCheckLayer;
23
24mod accept;
25mod api;
26mod health;
27
28pub struct Rpc {
34 pub listener: TcpListener,
35 pub store_url: Url,
36 pub block_producer_url: Option<Url>,
37 pub validator_url: Url,
38 pub ntx_builder_url: Option<Url>,
39 pub grpc_options: GrpcOptionsExternal,
40}
41
42impl Rpc {
43 pub async fn serve(self) -> anyhow::Result<()> {
48 let mut api = api::RpcService::new(
49 self.store_url.clone(),
50 self.block_producer_url.clone(),
51 self.validator_url,
52 self.ntx_builder_url.clone(),
53 NonZeroUsize::new(1_000_000).unwrap(),
54 );
55
56 let genesis = api
57 .get_genesis_header_with_retry()
58 .await
59 .context("Fetching genesis header from store")?;
60
61 api.set_genesis_commitment(genesis.commitment())?;
62
63 let api_service = api_server::ApiServer::new(api);
64 let reflection_service = server::Builder::configure()
65 .register_file_descriptor_set(rpc_api_descriptor())
66 .build_v1()
67 .context("failed to build reflection service")?;
68
69 info!(target: COMPONENT, endpoint=?self.listener, store=%self.store_url, block_producer=?self.block_producer_url, "Server initialized");
70
71 let rpc_version = env!("CARGO_PKG_VERSION");
72 let rpc_version =
73 semver::Version::parse(rpc_version).context("failed to parse crate version")?;
74
75 tonic::transport::Server::builder()
76 .accept_http1(true)
77 .max_connection_age(self.grpc_options.max_connection_age)
78 .timeout(self.grpc_options.request_timeout)
79 .layer(CatchPanicLayer::custom(catch_panic_layer_fn))
80 .layer(
81 TraceLayer::new(SharedClassifier::new(
82 GrpcErrorsAsFailures::new()
83 .with_success(GrpcCode::InvalidArgument)
84 .with_success(GrpcCode::NotFound)
85 .with_success(GrpcCode::ResourceExhausted)
86 .with_success(GrpcCode::Unimplemented)
87 .with_success(GrpcCode::Unknown),
88 ))
89 .make_span_with(grpc_trace_fn),
90 )
91 .layer(HealthCheckLayer)
92 .layer(cors_for_grpc_web_layer())
93 .layer(GrpcWebLayer::new())
96 .layer(grpc::rate_limit_concurrent_connections(self.grpc_options))
97 .layer(grpc::rate_limit_per_ip(self.grpc_options)?)
98 .layer(
102 AcceptHeaderLayer::new(&rpc_version, genesis.commitment())
103 .with_genesis_enforced_method("SubmitProvenTransaction")
104 .with_genesis_enforced_method("SubmitProvenBatch"),
105 )
106 .add_service(api_service)
107 .add_service(reflection_service)
109 .serve_with_incoming(TcpListenerStream::new(self.listener))
110 .await
111 .context("failed to serve RPC API")
112 }
113}