1#![forbid(unsafe_code)]
17
18#[macro_use]
19extern crate tracing;
20
21mod helpers;
22pub use helpers::*;
23
24mod routes;
25
26use snarkos_node_cdn::CdnBlockSync;
27use snarkos_node_consensus::Consensus;
28use snarkos_node_router::{
29 Routing,
30 messages::{Message, UnconfirmedTransaction},
31};
32use snarkos_node_sync::BlockSync;
33use snarkvm::{
34 console::{program::ProgramID, types::Field},
35 ledger::narwhal::Data,
36 prelude::{Ledger, Network, cfg_into_iter, store::ConsensusStorage},
37};
38
39use anyhow::Result;
40use axum::{
41 Json,
42 body::Body,
43 extract::{ConnectInfo, DefaultBodyLimit, Path, Query, State},
44 http::{Method, Request, StatusCode, header::CONTENT_TYPE},
45 middleware,
46 middleware::Next,
47 response::Response,
48 routing::{get, post},
49};
50use axum_extra::response::ErasedJson;
51#[cfg(feature = "locktick")]
52use locktick::parking_lot::Mutex;
53#[cfg(not(feature = "locktick"))]
54use parking_lot::Mutex;
55use std::{net::SocketAddr, sync::Arc};
56use tokio::{net::TcpListener, task::JoinHandle};
57use tower_governor::{GovernorLayer, governor::GovernorConfigBuilder};
58use tower_http::{
59 cors::{Any, CorsLayer},
60 trace::TraceLayer,
61};
62
63pub const DEFAULT_REST_PORT: u16 = 3030;
65
66#[derive(Clone)]
68pub struct Rest<N: Network, C: ConsensusStorage<N>, R: Routing<N>> {
69 cdn_sync: Option<Arc<CdnBlockSync>>,
71 consensus: Option<Consensus<N>>,
73 ledger: Ledger<N, C>,
75 routing: Arc<R>,
77 handles: Arc<Mutex<Vec<JoinHandle<()>>>>,
79 block_sync: Arc<BlockSync<N>>,
81}
82
83impl<N: Network, C: 'static + ConsensusStorage<N>, R: Routing<N>> Rest<N, C, R> {
84 pub async fn start(
86 rest_ip: SocketAddr,
87 rest_rps: u32,
88 consensus: Option<Consensus<N>>,
89 ledger: Ledger<N, C>,
90 routing: Arc<R>,
91 cdn_sync: Option<Arc<CdnBlockSync>>,
92 block_sync: Arc<BlockSync<N>>,
93 ) -> Result<Self> {
94 let mut server = Self { consensus, ledger, routing, cdn_sync, block_sync, handles: Default::default() };
96 server.spawn_server(rest_ip, rest_rps).await;
98 Ok(server)
100 }
101}
102
103impl<N: Network, C: ConsensusStorage<N>, R: Routing<N>> Rest<N, C, R> {
104 pub const fn ledger(&self) -> &Ledger<N, C> {
106 &self.ledger
107 }
108
109 pub const fn handles(&self) -> &Arc<Mutex<Vec<JoinHandle<()>>>> {
111 &self.handles
112 }
113}
114
115impl<N: Network, C: ConsensusStorage<N>, R: Routing<N>> Rest<N, C, R> {
116 async fn spawn_server(&mut self, rest_ip: SocketAddr, rest_rps: u32) {
117 let cors = CorsLayer::new()
118 .allow_origin(Any)
119 .allow_methods([Method::GET, Method::POST, Method::OPTIONS])
120 .allow_headers([CONTENT_TYPE]);
121
122 debug!("REST rate limit per IP - {rest_rps} RPS");
124
125 let governor_config = Box::new(
127 GovernorConfigBuilder::default()
128 .per_nanosecond((1_000_000_000 / rest_rps) as u64)
129 .burst_size(rest_rps)
130 .error_handler(|error| {
131 let error_message = error.to_string();
133 let mut response = Response::new(error_message.clone().into());
134 *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
135 if error_message.contains("Too Many Requests") {
136 *response.status_mut() = StatusCode::TOO_MANY_REQUESTS;
137 }
138 response
139 })
140 .finish()
141 .expect("Couldn't set up rate limiting for the REST server!"),
142 );
143
144 let network = match N::ID {
146 snarkvm::console::network::MainnetV0::ID => "mainnet",
147 snarkvm::console::network::TestnetV0::ID => "testnet",
148 snarkvm::console::network::CanaryV0::ID => "canary",
149 unknown_id => {
150 eprintln!("Unknown network ID ({unknown_id})");
151 return;
152 }
153 };
154
155 let router = {
156 let routes = axum::Router::new()
157
158 .route(&format!("/{network}/node/address"), get(Self::get_node_address))
160 .route(&format!("/{network}/program/{{id}}/mapping/{{name}}"), get(Self::get_mapping_values))
161 .route_layer(middleware::from_fn(auth_middleware))
162
163 .route(&format!("/{network}/consensus_version"), get(Self::get_consensus_version))
165
166 .route(&format!("/{network}/block/height/latest"), get(Self::get_block_height_latest))
168 .route(&format!("/{network}/block/hash/latest"), get(Self::get_block_hash_latest))
169 .route(&format!("/{network}/block/latest"), get(Self::get_block_latest))
170 .route(&format!("/{network}/block/{{height_or_hash}}"), get(Self::get_block))
171 .route(&format!("/{network}/block/{{height_or_hash}}/header"), get(Self::get_block_header))
174 .route(&format!("/{network}/block/{{height_or_hash}}/transactions"), get(Self::get_block_transactions))
175
176 .route(&format!("/{network}/transaction/{{id}}"), get(Self::get_transaction))
178 .route(&format!("/{network}/transaction/confirmed/{{id}}"), get(Self::get_confirmed_transaction))
179 .route(&format!("/{network}/transaction/unconfirmed/{{id}}"), get(Self::get_unconfirmed_transaction))
180 .route(&format!("/{network}/transaction/broadcast"), post(Self::transaction_broadcast))
181
182 .route(&format!("/{network}/solution/broadcast"), post(Self::solution_broadcast))
184
185
186 .route(&format!("/{network}/find/blockHash/{{tx_id}}"), get(Self::find_block_hash))
188 .route(&format!("/{network}/find/blockHeight/{{state_root}}"), get(Self::find_block_height_from_state_root))
189 .route(&format!("/{network}/find/transactionID/deployment/{{program_id}}"), get(Self::find_latest_transaction_id_from_program_id))
190 .route(&format!("/{network}/find/transactionID/deployment/{{program_id}}/{{edition}}"), get(Self::find_transaction_id_from_program_id_and_edition))
191 .route(&format!("/{network}/find/transactionID/{{transition_id}}"), get(Self::find_transaction_id_from_transition_id))
192 .route(&format!("/{network}/find/transitionID/{{input_or_output_id}}"), get(Self::find_transition_id))
193
194 .route(&format!("/{network}/peers/count"), get(Self::get_peers_count))
196 .route(&format!("/{network}/peers/all"), get(Self::get_peers_all))
197 .route(&format!("/{network}/peers/all/metrics"), get(Self::get_peers_all_metrics))
198
199 .route(&format!("/{network}/program/{{id}}"), get(Self::get_program))
201 .route(&format!("/{network}/program/{{id}}/latest_edition"), get(Self::get_latest_program_edition))
202 .route(&format!("/{network}/program/{{id}}/{{edition}}"), get(Self::get_program_for_edition))
203 .route(&format!("/{network}/program/{{id}}/mappings"), get(Self::get_mapping_names))
204 .route(&format!("/{network}/program/{{id}}/mapping/{{name}}/{{key}}"), get(Self::get_mapping_value))
205
206 .route(&format!("/{network}/sync_status"), get(Self::get_sync_status))
209 .route(&format!("/{network}/sync/status"), get(Self::get_sync_status))
210 .route(&format!("/{network}/sync/peers"), get(Self::get_sync_peers))
211 .route(&format!("/{network}/sync/requests"), get(Self::get_sync_requests_summary))
212 .route(&format!("/{network}/sync/requests/list"), get(Self::get_sync_requests_list))
213
214 .route(&format!("/{network}/blocks"), get(Self::get_blocks))
216 .route(&format!("/{network}/height/{{hash}}"), get(Self::get_height))
217 .route(&format!("/{network}/memoryPool/transmissions"), get(Self::get_memory_pool_transmissions))
218 .route(&format!("/{network}/memoryPool/solutions"), get(Self::get_memory_pool_solutions))
219 .route(&format!("/{network}/memoryPool/transactions"), get(Self::get_memory_pool_transactions))
220 .route(&format!("/{network}/statePath/{{commitment}}"), get(Self::get_state_path_for_commitment))
221 .route(&format!("/{network}/stateRoot/latest"), get(Self::get_state_root_latest))
222 .route(&format!("/{network}/stateRoot/{{height}}"), get(Self::get_state_root))
223 .route(&format!("/{network}/committee/latest"), get(Self::get_committee_latest))
224 .route(&format!("/{network}/committee/{{height}}"), get(Self::get_committee))
225 .route(&format!("/{network}/delegators/{{validator}}"), get(Self::get_delegators_for_validator));
226
227 #[cfg(feature = "telemetry")]
229 let routes = match self.consensus {
230 Some(_) => routes.route(
231 &format!("/{network}/validators/participation"),
232 get(Self::get_validator_participation_scores),
233 ),
234 None => routes,
235 };
236
237 #[cfg(feature = "history")]
239 let routes =
240 routes.route(&format!("/{network}/block/{{blockHeight}}/history/{{mapping}}"), get(Self::get_history));
241
242 routes
243 .with_state(self.clone())
245 .layer(TraceLayer::new_for_http())
247 .layer(middleware::from_fn(log_middleware))
249 .layer(cors)
251 .layer(DefaultBodyLimit::max(512 * 1024))
253 .layer(GovernorLayer {
254 config: governor_config.into(),
255 })
256 };
257
258 let rest_listener = TcpListener::bind(rest_ip).await.unwrap();
259 self.handles.lock().push(tokio::spawn(async move {
260 axum::serve(rest_listener, router.into_make_service_with_connect_info::<SocketAddr>())
261 .await
262 .expect("couldn't start rest server");
263 }))
264 }
265}
266
267async fn log_middleware(
268 ConnectInfo(addr): ConnectInfo<SocketAddr>,
269 request: Request<Body>,
270 next: Next,
271) -> Result<Response, StatusCode> {
272 info!("Received '{} {}' from '{addr}'", request.method(), request.uri());
273
274 Ok(next.run(request).await)
275}
276
277pub fn fmt_id(id: impl ToString) -> String {
279 let id = id.to_string();
280 let mut formatted_id = id.chars().take(16).collect::<String>();
281 if id.chars().count() > 16 {
282 formatted_id.push_str("..");
283 }
284 formatted_id
285}