1#![forbid(unsafe_code)]
17
18#[macro_use]
19extern crate tracing;
20
21mod helpers;
22pub use helpers::*;
24
25mod routes;
26
27mod version;
28
29use snarkos_node_cdn::CdnBlockSync;
30use snarkos_node_consensus::Consensus;
31use snarkos_node_router::{
32 Routing,
33 messages::{Message, UnconfirmedTransaction},
34};
35use snarkos_node_sync::BlockSync;
36use snarkvm::{
37 console::{program::ProgramID, types::Field},
38 ledger::narwhal::Data,
39 prelude::{Ledger, Network, cfg_into_iter, store::ConsensusStorage},
40};
41
42use anyhow::{Context, Result};
43use axum::{
44 body::Body,
45 extract::{ConnectInfo, DefaultBodyLimit, Query, State},
46 http::{Method, Request, StatusCode, header::CONTENT_TYPE},
47 middleware,
48 response::Response,
49 routing::{get, post},
50};
51use axum_extra::response::ErasedJson;
52#[cfg(feature = "locktick")]
53use locktick::parking_lot::Mutex;
54#[cfg(not(feature = "locktick"))]
55use parking_lot::Mutex;
56use std::{
57 net::SocketAddr,
58 sync::{Arc, atomic::AtomicUsize},
59};
60use tokio::{net::TcpListener, task::JoinHandle};
61use tower_governor::{GovernorLayer, governor::GovernorConfigBuilder};
62use tower_http::{
63 cors::{Any, CorsLayer},
64 trace::TraceLayer,
65};
66
67pub const DEFAULT_REST_PORT: u16 = 3030;
69
70pub const API_VERSION_V1: &str = "v1";
72pub const API_VERSION_V2: &str = "v2";
73
74#[derive(Clone)]
76pub struct Rest<N: Network, C: ConsensusStorage<N>, R: Routing<N>> {
77 cdn_sync: Option<Arc<CdnBlockSync>>,
79 consensus: Option<Consensus<N>>,
81 ledger: Ledger<N, C>,
83 routing: Arc<R>,
85 handles: Arc<Mutex<Vec<JoinHandle<()>>>>,
87 block_sync: Arc<BlockSync<N>>,
89 num_verifying_deploys: Arc<AtomicUsize>,
91 num_verifying_executions: Arc<AtomicUsize>,
93}
94
95impl<N: Network, C: 'static + ConsensusStorage<N>, R: Routing<N>> Rest<N, C, R> {
96 pub async fn start(
98 rest_ip: SocketAddr,
99 rest_rps: u32,
100 consensus: Option<Consensus<N>>,
101 ledger: Ledger<N, C>,
102 routing: Arc<R>,
103 cdn_sync: Option<Arc<CdnBlockSync>>,
104 block_sync: Arc<BlockSync<N>>,
105 ) -> Result<Self> {
106 let mut server = Self {
108 consensus,
109 ledger,
110 routing,
111 cdn_sync,
112 block_sync,
113 handles: Default::default(),
114 num_verifying_deploys: Default::default(),
115 num_verifying_executions: Default::default(),
116 };
117 server.spawn_server(rest_ip, rest_rps).await?;
119 Ok(server)
121 }
122}
123
124impl<N: Network, C: ConsensusStorage<N>, R: Routing<N>> Rest<N, C, R> {
125 pub const fn ledger(&self) -> &Ledger<N, C> {
127 &self.ledger
128 }
129
130 pub const fn handles(&self) -> &Arc<Mutex<Vec<JoinHandle<()>>>> {
132 &self.handles
133 }
134}
135
136impl<N: Network, C: ConsensusStorage<N>, R: Routing<N>> Rest<N, C, R> {
137 fn build_routes(&self, rest_rps: u32) -> axum::Router {
138 let cors = CorsLayer::new()
139 .allow_origin(Any)
140 .allow_methods([Method::GET, Method::POST, Method::OPTIONS])
141 .allow_headers([CONTENT_TYPE]);
142
143 let governor_config = Box::new(
145 GovernorConfigBuilder::default()
146 .per_nanosecond((1_000_000_000 / rest_rps) as u64)
147 .burst_size(rest_rps)
148 .error_handler(|error| {
149 let error_message = error.to_string();
151 let mut response = Response::new(error_message.clone().into());
152 *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
153 if error_message.contains("Too Many Requests") {
154 *response.status_mut() = StatusCode::TOO_MANY_REQUESTS;
155 }
156 response
157 })
158 .finish()
159 .expect("Couldn't set up rate limiting for the REST server!"),
160 );
161
162 let routes = axum::Router::new()
163
164 .route("/node/address", get(Self::get_node_address))
166 .route("/program/{id}/mapping/{name}", get(Self::get_mapping_values))
167 .route("/db_backup", post(Self::db_backup))
168 .route_layer(middleware::from_fn(auth_middleware))
169
170 .route("/consensus_version", get(Self::get_consensus_version))
172
173 .route("/block/height/latest", get(Self::get_block_height_latest))
175 .route("/block/hash/latest", get(Self::get_block_hash_latest))
176 .route("/block/latest", get(Self::get_block_latest))
177 .route("/block/{height_or_hash}", get(Self::get_block))
178 .route("/block/{height_or_hash}/header", get(Self::get_block_header))
181 .route("/block/{height_or_hash}/transactions", get(Self::get_block_transactions))
182
183 .route("/transaction/{id}", get(Self::get_transaction))
185 .route("/transaction/confirmed/{id}", get(Self::get_confirmed_transaction))
186 .route("/transaction/unconfirmed/{id}", get(Self::get_unconfirmed_transaction))
187 .route("/transaction/broadcast", post(Self::transaction_broadcast))
188
189 .route("/solution/broadcast", post(Self::solution_broadcast))
191
192 .route("/find/blockHash/{tx_id}", get(Self::find_block_hash))
194 .route("/find/blockHeight/{state_root}", get(Self::find_block_height_from_state_root))
195 .route("/find/transactionID/deployment/{program_id}", get(Self::find_latest_transaction_id_from_program_id))
196 .route("/find/transactionID/deployment/{program_id}/{edition}", get(Self::find_transaction_id_from_program_id_and_edition))
197 .route("/find/transactionID/{transition_id}", get(Self::find_transaction_id_from_transition_id))
198 .route("/find/transitionID/{input_or_output_id}", get(Self::find_transition_id))
199
200 .route("/peers/count", get(Self::get_peers_count))
202 .route("/peers/all", get(Self::get_peers_all))
203 .route("/peers/all/metrics", get(Self::get_peers_all_metrics))
204
205 .route("/program/{id}", get(Self::get_program))
207 .route("/program/{id}/latest_edition", get(Self::get_latest_program_edition))
208 .route("/program/{id}/{edition}", get(Self::get_program_for_edition))
209 .route("/program/{id}/mappings", get(Self::get_mapping_names))
210 .route("/program/{id}/mapping/{name}/{key}", get(Self::get_mapping_value))
211
212 .route("/sync_status", get(Self::get_sync_status))
215 .route("/sync/status", get(Self::get_sync_status))
216 .route("/sync/peers", get(Self::get_sync_peers))
217 .route("/sync/requests", get(Self::get_sync_requests_summary))
218 .route("/sync/requests/list", get(Self::get_sync_requests_list))
219
220 .route("/version", get(Self::get_version))
222 .route("/blocks", get(Self::get_blocks))
223 .route("/height/{hash}", get(Self::get_height))
224 .route("/memoryPool/transmissions", get(Self::get_memory_pool_transmissions))
225 .route("/memoryPool/solutions", get(Self::get_memory_pool_solutions))
226 .route("/memoryPool/transactions", get(Self::get_memory_pool_transactions))
227 .route("/statePath/{commitment}", get(Self::get_state_path_for_commitment))
228 .route("/statePaths", get(Self::get_state_paths_for_commitments))
229 .route("/stateRoot/latest", get(Self::get_state_root_latest))
230 .route("/stateRoot/{height}", get(Self::get_state_root))
231 .route("/committee/latest", get(Self::get_committee_latest))
232 .route("/committee/{height}", get(Self::get_committee))
233 .route("/delegators/{validator}", get(Self::get_delegators_for_validator));
234
235 #[cfg(feature = "telemetry")]
237 let routes = match self.consensus {
238 Some(_) => routes.route("/validators/participation", get(Self::get_validator_participation_scores)),
239 None => routes,
240 };
241
242 #[cfg(feature = "history")]
244 let routes = routes.route("/block/{blockHeight}/history/{mapping}", get(Self::get_history));
245
246 routes
247 .with_state(self.clone())
249 .layer(TraceLayer::new_for_http())
251 .layer(middleware::map_request(log_middleware))
253 .layer(cors)
255 .layer(DefaultBodyLimit::max(512 * 1024))
257 .layer(GovernorLayer {
258 config: governor_config.into(),
259 })
260 }
261
262 async fn spawn_server(&mut self, rest_ip: SocketAddr, rest_rps: u32) -> Result<()> {
263 debug!("REST rate limit per IP - {rest_rps} RPS");
265
266 let default_router = axum::Router::new().nest(
268 &format!("/{}", N::SHORT_NAME),
269 self.build_routes(rest_rps).layer(middleware::map_response(v1_error_middleware)),
270 );
271 let v1_router = axum::Router::new().nest(
272 &format!("/{API_VERSION_V1}/{}", N::SHORT_NAME),
273 self.build_routes(rest_rps).layer(middleware::map_response(v1_error_middleware)),
274 );
275
276 let v2_router =
278 axum::Router::new().nest(&format!("/{API_VERSION_V2}/{}", N::SHORT_NAME), self.build_routes(rest_rps));
279
280 let router = default_router.merge(v1_router).merge(v2_router);
282
283 let rest_listener =
284 TcpListener::bind(rest_ip).await.with_context(|| "Failed to bind TCP port for REST endpoints")?;
285
286 let handle = tokio::spawn(async move {
287 axum::serve(rest_listener, router.into_make_service_with_connect_info::<SocketAddr>())
288 .await
289 .expect("couldn't start rest server");
290 });
291
292 self.handles.lock().push(handle);
293 Ok(())
294 }
295}
296
297async fn log_middleware(ConnectInfo(addr): ConnectInfo<SocketAddr>, request: Request<Body>) -> Request<Body> {
299 info!("Received '{} {}' from '{addr}'", request.method(), request.uri());
300 request
301}
302
303async fn v1_error_middleware(response: Response) -> Response {
306 const V1_STATUS_CODE: StatusCode = StatusCode::INTERNAL_SERVER_ERROR;
308
309 if response.status().is_success() {
310 return response;
311 }
312
313 let fallback = || {
315 let mut response = Response::new(Body::from("Failed to convert error"));
316 *response.status_mut() = V1_STATUS_CODE;
317 response
318 };
319
320 let Ok(bytes) = axum::body::to_bytes(response.into_body(), usize::MAX).await else {
321 return fallback();
322 };
323
324 let Ok(json_err) = serde_json::from_slice::<SerializedRestError>(&bytes) else {
326 return fallback();
327 };
328
329 let mut message = json_err.message;
330 for next in json_err.chain.into_iter() {
331 message = format!("{message} — {next}");
332 }
333
334 let mut response = Response::new(Body::from(message));
335
336 *response.status_mut() = V1_STATUS_CODE;
337
338 response
339}
340
341pub fn fmt_id(id: impl ToString) -> String {
343 let id = id.to_string();
344 let mut formatted_id = id.chars().take(16).collect::<String>();
345 if id.chars().count() > 16 {
346 formatted_id.push_str("..");
347 }
348 formatted_id
349}
350
351#[cfg(test)]
352mod tests {
353 use super::*;
354 use anyhow::anyhow;
355 use axum::{
356 Router,
357 body::Body,
358 http::{Request, StatusCode},
359 middleware,
360 routing::get,
361 };
362 use tower::ServiceExt; fn test_app() -> Router {
365 let build_routes = || {
366 Router::new()
367 .route("/not_found", get(|| async { Err::<(), RestError>(RestError::not_found(anyhow!("missing"))) }))
368 .route("/bad_request", get(|| async { Err::<(), RestError>(RestError::bad_request(anyhow!("bad"))) }))
369 .route(
370 "/service_unavailable",
371 get(|| async { Err::<(), RestError>(RestError::service_unavailable(anyhow!("gone"))) }),
372 )
373 };
374 let router_v1 = build_routes().route_layer(middleware::map_response(v1_error_middleware));
375 let router_v2 = Router::new().nest(&format!("/{API_VERSION_V2}"), build_routes());
376 router_v1.merge(router_v2)
377 }
378
379 #[tokio::test]
380 async fn v1_routes_force_internal_server_error() {
381 let app = test_app();
382
383 let res = app.clone().oneshot(Request::builder().uri("/not_found").body(Body::empty()).unwrap()).await.unwrap();
384 assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR);
385
386 let res =
387 app.clone().oneshot(Request::builder().uri("/bad_request").body(Body::empty()).unwrap()).await.unwrap();
388 assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR);
389
390 let res =
391 app.oneshot(Request::builder().uri("/service_unavailable").body(Body::empty()).unwrap()).await.unwrap();
392 assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR);
393 }
394
395 #[tokio::test]
396 async fn v2_routes_return_specific_errors() {
397 let app = test_app();
398
399 let res =
400 app.clone().oneshot(Request::builder().uri("/v2/not_found").body(Body::empty()).unwrap()).await.unwrap();
401 assert_eq!(res.status(), StatusCode::NOT_FOUND);
402
403 let res =
404 app.clone().oneshot(Request::builder().uri("/v2/bad_request").body(Body::empty()).unwrap()).await.unwrap();
405 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
406
407 let res =
408 app.oneshot(Request::builder().uri("/v2/service_unavailable").body(Body::empty()).unwrap()).await.unwrap();
409 assert_eq!(res.status(), StatusCode::SERVICE_UNAVAILABLE);
410 }
411}