1#![forbid(unsafe_code)]
17
18#[macro_use]
19extern crate tracing;
20
21mod helpers;
22pub use helpers::*;
23
24mod routes;
25
26use snarkos_node_consensus::Consensus;
27use snarkos_node_router::{
28 Routing,
29 messages::{Message, UnconfirmedTransaction},
30};
31use snarkvm::{
32 console::{program::ProgramID, types::Field},
33 ledger::narwhal::Data,
34 prelude::{Ledger, Network, cfg_into_iter, store::ConsensusStorage},
35};
36
37use anyhow::Result;
38use axum::{
39 Json,
40 body::Body,
41 extract::{ConnectInfo, DefaultBodyLimit, Path, Query, State},
42 http::{Method, Request, StatusCode, header::CONTENT_TYPE},
43 middleware,
44 middleware::Next,
45 response::Response,
46 routing::{get, post},
47};
48use axum_extra::response::ErasedJson;
49use parking_lot::Mutex;
50use std::{net::SocketAddr, sync::Arc};
51use tokio::{net::TcpListener, task::JoinHandle};
52use tower_governor::{GovernorLayer, governor::GovernorConfigBuilder};
53use tower_http::{
54 cors::{Any, CorsLayer},
55 trace::TraceLayer,
56};
57
58#[derive(Clone)]
60pub struct Rest<N: Network, C: ConsensusStorage<N>, R: Routing<N>> {
61 consensus: Option<Consensus<N>>,
63 ledger: Ledger<N, C>,
65 routing: Arc<R>,
67 handles: Arc<Mutex<Vec<JoinHandle<()>>>>,
69}
70
71impl<N: Network, C: 'static + ConsensusStorage<N>, R: Routing<N>> Rest<N, C, R> {
72 pub async fn start(
74 rest_ip: SocketAddr,
75 rest_rps: u32,
76 consensus: Option<Consensus<N>>,
77 ledger: Ledger<N, C>,
78 routing: Arc<R>,
79 ) -> Result<Self> {
80 let mut server = Self { consensus, ledger, routing, handles: Default::default() };
82 server.spawn_server(rest_ip, rest_rps).await;
84 Ok(server)
86 }
87}
88
89impl<N: Network, C: ConsensusStorage<N>, R: Routing<N>> Rest<N, C, R> {
90 pub const fn ledger(&self) -> &Ledger<N, C> {
92 &self.ledger
93 }
94
95 pub const fn handles(&self) -> &Arc<Mutex<Vec<JoinHandle<()>>>> {
97 &self.handles
98 }
99}
100
101impl<N: Network, C: ConsensusStorage<N>, R: Routing<N>> Rest<N, C, R> {
102 async fn spawn_server(&mut self, rest_ip: SocketAddr, rest_rps: u32) {
103 let cors = CorsLayer::new()
104 .allow_origin(Any)
105 .allow_methods([Method::GET, Method::POST, Method::OPTIONS])
106 .allow_headers([CONTENT_TYPE]);
107
108 debug!("REST rate limit per IP - {rest_rps} RPS");
110
111 let governor_config = Box::new(
113 GovernorConfigBuilder::default()
114 .per_nanosecond((1_000_000_000 / rest_rps) as u64)
115 .burst_size(rest_rps)
116 .error_handler(|error| {
117 let error_message = error.to_string();
119 let mut response = Response::new(error_message.clone().into());
120 *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
121 if error_message.contains("Too Many Requests") {
122 *response.status_mut() = StatusCode::TOO_MANY_REQUESTS;
123 }
124 response
125 })
126 .finish()
127 .expect("Couldn't set up rate limiting for the REST server!"),
128 );
129
130 let network = match N::ID {
132 snarkvm::console::network::MainnetV0::ID => "mainnet",
133 snarkvm::console::network::TestnetV0::ID => "testnet",
134 snarkvm::console::network::CanaryV0::ID => "canary",
135 unknown_id => {
136 eprintln!("Unknown network ID ({unknown_id})");
137 return;
138 }
139 };
140
141 let router = {
142 let routes = axum::Router::new()
143
144 .route(&format!("/{network}/node/address"), get(Self::get_node_address))
146 .route(&format!("/{network}/program/:id/mapping/:name"), get(Self::get_mapping_values))
147 .route_layer(middleware::from_fn(auth_middleware))
148
149 .route(&format!("/{network}/block/height/latest"), get(Self::get_block_height_latest))
151 .route(&format!("/{network}/block/hash/latest"), get(Self::get_block_hash_latest))
152 .route(&format!("/{network}/block/latest"), get(Self::get_block_latest))
153 .route(&format!("/{network}/block/:height_or_hash"), get(Self::get_block))
154 .route(&format!("/{network}/block/:height_or_hash/header"), get(Self::get_block_header))
157 .route(&format!("/{network}/block/:height_or_hash/transactions"), get(Self::get_block_transactions))
158
159 .route(&format!("/{network}/transaction/:id"), get(Self::get_transaction))
161 .route(&format!("/{network}/transaction/confirmed/:id"), get(Self::get_confirmed_transaction))
162 .route(&format!("/{network}/transaction/broadcast"), post(Self::transaction_broadcast))
163
164 .route(&format!("/{network}/solution/broadcast"), post(Self::solution_broadcast))
166
167 .route(&format!("/{network}/find/blockHash/:tx_id"), get(Self::find_block_hash))
169 .route(&format!("/{network}/find/blockHeight/:state_root"), get(Self::find_block_height_from_state_root))
170 .route(&format!("/{network}/find/transactionID/deployment/:program_id"), get(Self::find_transaction_id_from_program_id))
171 .route(&format!("/{network}/find/transactionID/:transition_id"), get(Self::find_transaction_id_from_transition_id))
172 .route(&format!("/{network}/find/transitionID/:input_or_output_id"), get(Self::find_transition_id))
173
174 .route(&format!("/{network}/peers/count"), get(Self::get_peers_count))
176 .route(&format!("/{network}/peers/all"), get(Self::get_peers_all))
177 .route(&format!("/{network}/peers/all/metrics"), get(Self::get_peers_all_metrics))
178
179 .route(&format!("/{network}/program/:id"), get(Self::get_program))
181 .route(&format!("/{network}/program/:id/mappings"), get(Self::get_mapping_names))
182 .route(&format!("/{network}/program/:id/mapping/:name/:key"), get(Self::get_mapping_value))
183
184 .route(&format!("/{network}/blocks"), get(Self::get_blocks))
186 .route(&format!("/{network}/height/:hash"), get(Self::get_height))
187 .route(&format!("/{network}/memoryPool/transmissions"), get(Self::get_memory_pool_transmissions))
188 .route(&format!("/{network}/memoryPool/solutions"), get(Self::get_memory_pool_solutions))
189 .route(&format!("/{network}/memoryPool/transactions"), get(Self::get_memory_pool_transactions))
190 .route(&format!("/{network}/statePath/:commitment"), get(Self::get_state_path_for_commitment))
191 .route(&format!("/{network}/stateRoot/latest"), get(Self::get_state_root_latest))
192 .route(&format!("/{network}/stateRoot/:height"), get(Self::get_state_root))
193 .route(&format!("/{network}/committee/latest"), get(Self::get_committee_latest))
194 .route(&format!("/{network}/committee/:height"), get(Self::get_committee))
195 .route(&format!("/{network}/delegators/:validator"), get(Self::get_delegators_for_validator));
196
197 #[cfg(feature = "history")]
199 let routes =
200 routes.route(&format!("/{network}/block/:blockHeight/history/:mapping"), get(Self::get_history));
201
202 routes
203 .with_state(self.clone())
205 .layer(TraceLayer::new_for_http())
207 .layer(middleware::from_fn(log_middleware))
209 .layer(cors)
211 .layer(DefaultBodyLimit::max(512 * 1024))
213 .layer(GovernorLayer {
214 config: Box::leak(governor_config),
216 })
217 };
218
219 let rest_listener = TcpListener::bind(rest_ip).await.unwrap();
220 self.handles.lock().push(tokio::spawn(async move {
221 axum::serve(rest_listener, router.into_make_service_with_connect_info::<SocketAddr>())
222 .await
223 .expect("couldn't start rest server");
224 }))
225 }
226}
227
228async fn log_middleware(
229 ConnectInfo(addr): ConnectInfo<SocketAddr>,
230 request: Request<Body>,
231 next: Next,
232) -> Result<Response, StatusCode> {
233 info!("Received '{} {}' from '{addr}'", request.method(), request.uri());
234
235 Ok(next.run(request).await)
236}
237
238pub fn fmt_id(id: impl ToString) -> String {
240 let id = id.to_string();
241 let mut formatted_id = id.chars().take(16).collect::<String>();
242 if id.chars().count() > 16 {
243 formatted_id.push_str("..");
244 }
245 formatted_id
246}