1use std::{fmt, panic};
11
12use cookie::Cookie;
13use jsonrpsee::server::{middleware::rpc::RpcServiceBuilder, Server, ServerHandle};
14use tokio::task::JoinHandle;
15use tracing::*;
16
17use zebra_chain::{
18 block::MAX_BLOCK_BYTES, chain_sync_status::ChainSyncStatus, chain_tip::ChainTip,
19 parameters::Network,
20};
21use zebra_consensus::router::service_trait::BlockVerifierService;
22use zebra_network::AddressBookPeers;
23use zebra_node_services::mempool::MempoolService;
24use zebra_state::{ReadState as ReadStateService, State as StateService};
25
26use crate::{
27 config,
28 methods::{RpcImpl, RpcServer as _},
29 server::{
30 http_request_compatibility::HttpRequestMiddlewareLayer,
31 rpc_call_compatibility::FixRpcResponseMiddleware, rpc_metrics::RpcMetricsMiddleware,
32 rpc_tracing::RpcTracingMiddleware,
33 },
34};
35
36pub mod cookie;
37pub mod error;
38pub mod http_request_compatibility;
39pub mod rpc_call_compatibility;
40pub mod rpc_metrics;
41pub mod rpc_tracing;
42
43#[cfg(test)]
44mod tests;
45
46#[derive(Clone)]
48pub struct RpcServer {
49 config: config::rpc::Config,
51
52 network: Network,
54
55 build_version: String,
57
58 close_handle: ServerHandle,
60}
61
62impl fmt::Debug for RpcServer {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 f.debug_struct("RpcServer")
65 .field("config", &self.config)
66 .field("network", &self.network)
67 .field("build_version", &self.build_version)
68 .field(
69 "close_handle",
70 &"ServerHandle",
72 )
73 .finish()
74 }
75}
76
77pub const OPENED_RPC_ENDPOINT_MSG: &str = "Opened RPC endpoint at ";
79
80pub const OPENED_LIGHTWALLETD_ENDPOINT_MSG: &str = "Opened lightwalletd gRPC endpoint at ";
86
87type ServerTask = JoinHandle<Result<(), tower::BoxError>>;
88
89impl RpcServer {
90 #[allow(clippy::too_many_arguments)]
105 pub async fn start<
106 Mempool,
107 State,
108 ReadState,
109 Tip,
110 BlockVerifierRouter,
111 SyncStatus,
112 AddressBook,
113 >(
114 rpc: RpcImpl<Mempool, State, ReadState, Tip, AddressBook, BlockVerifierRouter, SyncStatus>,
115 conf: config::rpc::Config,
116 ) -> Result<ServerTask, tower::BoxError>
117 where
118 Mempool: MempoolService,
119 State: StateService,
120 ReadState: ReadStateService,
121 Tip: ChainTip + Clone + Send + Sync + 'static,
122 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
123 BlockVerifierRouter: BlockVerifierService,
124 SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
125 {
126 let listen_addr = conf
127 .listen_addr
128 .expect("caller should make sure listen_addr is set");
129
130 let max_request_body_size = (MAX_BLOCK_BYTES as usize) * 2 + 1024;
133 let http_middleware_layer = if conf.enable_cookie_auth {
134 let cookie = Cookie::default();
135 cookie::write_to_disk(&cookie, &conf.cookie_dir)
136 .expect("Zebra must be able to write the auth cookie to the disk");
137 HttpRequestMiddlewareLayer::new(Some(cookie), max_request_body_size)
138 } else {
139 HttpRequestMiddlewareLayer::new(None, max_request_body_size)
140 };
141
142 let http_middleware = tower::ServiceBuilder::new().layer(http_middleware_layer);
143
144 let rpc_middleware = RpcServiceBuilder::new()
145 .rpc_logger(1024)
146 .layer_fn(FixRpcResponseMiddleware::new)
147 .layer_fn(RpcMetricsMiddleware::new)
148 .layer_fn(RpcTracingMiddleware::new);
149
150 let server = Server::builder()
151 .http_only()
152 .set_http_middleware(http_middleware)
153 .set_rpc_middleware(rpc_middleware)
154 .max_response_body_size(conf.max_response_body_size)
155 .build(listen_addr)
156 .await?;
157
158 info!("{OPENED_RPC_ENDPOINT_MSG}{}", server.local_addr()?);
159
160 Ok(tokio::spawn(async move {
161 server.start(rpc.into_rpc()).stopped().await;
162 Ok(())
163 }))
164 }
165
166 pub fn shutdown_blocking(&self) {
171 Self::shutdown_blocking_inner(self.close_handle.clone(), self.config.clone())
172 }
173
174 pub fn shutdown(&self) -> JoinHandle<()> {
177 let close_handle = self.close_handle.clone();
178 let config = self.config.clone();
179 let span = Span::current();
180
181 tokio::task::spawn_blocking(move || {
182 span.in_scope(|| Self::shutdown_blocking_inner(close_handle, config))
183 })
184 }
185
186 fn shutdown_blocking_inner(close_handle: ServerHandle, config: config::rpc::Config) {
190 let span = Span::current();
193 let wait_on_shutdown = move || {
194 span.in_scope(|| {
195 if config.enable_cookie_auth {
196 if let Err(err) = cookie::remove_from_disk(&config.cookie_dir) {
197 warn!(
198 ?err,
199 "unexpectedly could not remove the rpc auth cookie from the disk"
200 )
201 }
202 }
203
204 info!("Stopping RPC server");
205 let _ = close_handle.stop();
206 debug!("Stopped RPC server");
207 })
208 };
209
210 let span = Span::current();
211 let thread_handle = std::thread::spawn(wait_on_shutdown);
212
213 span.in_scope(|| match thread_handle.join() {
215 Ok(()) => (),
216 Err(panic_object) => panic::resume_unwind(panic_object),
217 })
218 }
219}
220
221impl Drop for RpcServer {
222 fn drop(&mut self) {
223 self.shutdown_blocking();
228 }
229}