1pub mod routes;
4
5use aide::{
6 axum::{ApiRouter, routing::get_with},
7 openapi::{Info, OpenApi},
8 operation::OperationOutput,
9 redoc::Redoc,
10 scalar::Scalar,
11};
12use axum::{
13 Extension, Json, Router,
14 body::Bytes,
15 extract::{Path, Request, State, ws::WebSocketUpgrade},
16 http::{HeaderMap, StatusCode, Uri, header},
17 middleware::{self, Next},
18 response::{Html, IntoResponse, Response},
19 routing::get,
20};
21use futures::stream::BoxStream;
22use schemars::transform::Transform;
23use serde::Serialize;
24use serialization_api::v2::{
25 GetIncorrectEncodingProofRequest, GetNamespaceProofRequest, GetRewardAccountProofRequest,
26 GetRewardBalanceRequest, GetRewardBalancesRequest, GetRewardClaimInputRequest,
27 GetRewardMerkleTreeRequest, GetStakeTableRequest, GetStateCertificateRequest,
28};
29use vbs::{BinarySerializer, Serializer, version::StaticVersion};
30
31use crate::{
32 error::{ApiError, AvailabilityError},
33 handlers, v1, v2,
34};
35
36#[derive(Debug, Serialize)]
44struct ErrorResponse {
45 #[serde(rename = "Custom")]
46 custom: CustomError,
47}
48
49#[derive(Debug, Serialize)]
50struct CustomError {
51 message: String,
54 status: u16,
55}
56
57impl IntoResponse for ApiError {
58 fn into_response(self) -> Response {
59 let status = match &self {
60 ApiError::BadRequest(_) => StatusCode::BAD_REQUEST,
61 ApiError::NotFound(_) => StatusCode::NOT_FOUND,
62 ApiError::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR,
63 };
64
65 let body = Json(ErrorResponse {
66 custom: CustomError {
67 message: self.to_string(),
68 status: status.as_u16(),
69 },
70 });
71
72 (status, body).into_response()
73 }
74}
75
76fn encode_response<T: Serialize>(headers: &HeaderMap, value: T) -> Result<Response, ApiError> {
83 let accept = headers
84 .get(header::ACCEPT)
85 .and_then(|v| v.to_str().ok())
86 .unwrap_or("");
87 if accept.contains("application/octet-stream") {
88 let bytes = Serializer::<StaticVersion<0, 1>>::serialize(&value)
89 .map_err(|e| ApiError::Internal(anyhow::anyhow!("vbs serialize: {e}")))?;
90 Ok(([(header::CONTENT_TYPE, "application/octet-stream")], bytes).into_response())
91 } else {
92 Ok(Json(value).into_response())
93 }
94}
95
96fn decode_body<T: serde::de::DeserializeOwned>(
104 headers: &HeaderMap,
105 body: &[u8],
106) -> Result<T, ApiError> {
107 let content_type = headers
108 .get(header::CONTENT_TYPE)
109 .and_then(|v| v.to_str().ok());
110 match content_type {
111 Some(ct) if ct.starts_with("application/octet-stream") => {
112 Serializer::<StaticVersion<0, 1>>::deserialize(body)
113 .map_err(|e| ApiError::BadRequest(anyhow::anyhow!("invalid binary body: {e}")))
114 },
115 Some(ct) if ct.starts_with("application/json") => serde_json::from_slice(body)
116 .map_err(|e| ApiError::BadRequest(anyhow::anyhow!("invalid json body: {e}"))),
117 Some(other) => Err(ApiError::BadRequest(anyhow::anyhow!(
118 "unsupported Content-Type: {other}"
119 ))),
120 None => Err(ApiError::BadRequest(anyhow::anyhow!(
121 "missing Content-Type header"
122 ))),
123 }
124}
125
126fn classify_availability_error(err: anyhow::Error) -> ApiError {
130 let is_not_found = err
131 .downcast_ref::<AvailabilityError>()
132 .map(|e| matches!(e, AvailabilityError::NotFound(_)));
133 match is_not_found {
134 Some(true) => ApiError::NotFound(err),
135 Some(false) => ApiError::BadRequest(err),
136 None => ApiError::Internal(err),
137 }
138}
139
140impl OperationOutput for ApiError {
141 type Inner = Self;
142}
143
144async fn serve_openapi_spec(Extension(api): Extension<OpenApi>) -> Json<OpenApi> {
146 Json(api)
147}
148
149async fn serve_swagger_ui() -> Html<&'static str> {
151 Html(include_str!("../templates/swagger.html"))
152}
153
154async fn rewrite_root_to_v2(mut req: Request, next: Next) -> Response {
161 let uri = req.uri().clone();
162 let path = uri.path();
163
164 if !path.starts_with("/v2") && path != "/" {
166 let new_path = format!("/v2{}", path);
167 let pq = if let Some(q) = uri.query() {
168 format!("{}?{}", new_path, q)
169 } else {
170 new_path
171 };
172 if let Ok(new_uri) = Uri::builder().path_and_query(pq).build() {
173 *req.uri_mut() = new_uri;
174 }
175 }
176
177 next.run(req).await
178}
179
180async fn redirect_to_docs() -> axum::response::Redirect {
182 axum::response::Redirect::permanent("/v2")
183}
184
185struct SendQuery<T>(T);
186
187impl<T, S> axum::extract::FromRequestParts<S> for SendQuery<T>
188where
189 T: serde::de::DeserializeOwned + Send,
190 S: Send + Sync,
191{
192 type Rejection = axum::extract::rejection::QueryRejection;
193
194 async fn from_request_parts(
195 parts: &mut axum::http::request::Parts,
196 state: &S,
197 ) -> Result<Self, Self::Rejection> {
198 axum::extract::Query::<T>::from_request_parts(parts, state)
199 .await
200 .map(|axum::extract::Query(inner)| SendQuery(inner))
201 }
202}
203
204impl<T: schemars::JsonSchema> aide::operation::OperationInput for SendQuery<T> {
205 fn operation_input(
206 ctx: &mut aide::generate::GenContext,
207 operation: &mut aide::openapi::Operation,
208 ) {
209 let schema = ctx.schema.subschema_for::<T>();
210 let params = aide::operation::parameters_from_schema(
211 ctx,
212 schema,
213 aide::operation::ParamLocation::Query,
214 );
215 aide::operation::add_parameters(ctx, operation, params);
216 }
217}
218
219#[derive(Clone, Copy)]
223enum WsFormat {
224 Binary,
225 Json,
226}
227
228fn ws_format(headers: &HeaderMap) -> WsFormat {
229 let accept = headers
230 .get(header::ACCEPT)
231 .and_then(|v| v.to_str().ok())
232 .unwrap_or("");
233 if accept.contains("application/octet-stream") {
234 WsFormat::Binary
235 } else {
236 WsFormat::Json
237 }
238}
239
240async fn drive_ws_stream<T: Serialize>(
241 mut socket: axum::extract::ws::WebSocket,
242 stream: BoxStream<'static, T>,
243 format: WsFormat,
244) {
245 use axum::extract::ws::Message;
246 use futures::StreamExt as _;
247 futures::pin_mut!(stream);
248 while let Some(item) = stream.next().await {
249 let msg = match format {
250 WsFormat::Binary => match Serializer::<StaticVersion<0, 1>>::serialize(&item) {
251 Ok(bytes) => Message::Binary(bytes.into()),
252 Err(_) => break,
253 },
254 WsFormat::Json => match serde_json::to_string(&item) {
255 Ok(json) => Message::Text(json.into()),
256 Err(_) => break,
257 },
258 };
259 if socket.send(msg).await.is_err() {
260 break;
261 }
262 }
263}
264
265pub fn create_combined_router<S>(state: S) -> Router
267where
268 S: v1::RewardApi
269 + v1::AvailabilityApi
270 + v1::HotShotAvailabilityApi
271 + v1::BlockStateApi
272 + v1::FeeStateApi
273 + v1::StatusApi
274 + v1::ConfigApi
275 + v1::NodeApi
276 + v1::CatchupApi
277 + v1::SubmitApi
278 + v1::StateSignatureApi
279 + v1::HotShotEventsApi
280 + v1::LightClientApi
281 + v1::ExplorerApi
282 + v1::TokenApi
283 + v1::DatabaseApi
284 + v2::RewardApi
285 + v2::DataApi
286 + v2::ConsensusApi
287 + Clone
288 + Send
289 + Sync
290 + 'static,
291{
292 let router_v1 = create_router_v1(state.clone());
293 let router_v2 = create_router_v2(state).layer(middleware::from_fn(rewrite_root_to_v2));
294
295 router_v2.merge(router_v1).route("/", get(redirect_to_docs))
296}
297
298pub fn create_router_v1<S>(state: S) -> Router
300where
301 S: v1::RewardApi
302 + v1::AvailabilityApi
303 + v1::HotShotAvailabilityApi
304 + v1::BlockStateApi
305 + v1::FeeStateApi
306 + v1::StatusApi
307 + v1::ConfigApi
308 + v1::NodeApi
309 + v1::CatchupApi
310 + v1::SubmitApi
311 + v1::StateSignatureApi
312 + v1::HotShotEventsApi
313 + v1::LightClientApi
314 + v1::ExplorerApi
315 + v1::TokenApi
316 + v1::DatabaseApi
317 + Clone
318 + Send
319 + Sync
320 + 'static,
321{
322 let get_reward_claim_input =
324 |State(state): State<S>, Path((height, address)): Path<(u64, String)>| async move {
325 state
326 .get_reward_claim_input(height, address)
327 .await
328 .map(Json)
329 .map_err(ApiError::Internal)
330 };
331
332 let get_reward_balance =
333 |State(state): State<S>, Path((height, address)): Path<(u64, String)>| async move {
334 state
335 .get_reward_balance(height, address)
336 .await
337 .map(Json)
338 .map_err(ApiError::Internal)
339 };
340
341 let get_latest_reward_balance = |State(state): State<S>, Path(address): Path<String>| async move {
342 state
343 .get_latest_reward_balance(address)
344 .await
345 .map(Json)
346 .map_err(ApiError::Internal)
347 };
348
349 let get_reward_account_proof =
350 |State(state): State<S>, Path((height, address)): Path<(u64, String)>| async move {
351 state
352 .get_reward_account_proof(height, address)
353 .await
354 .map(Json)
355 .map_err(ApiError::Internal)
356 };
357
358 let get_latest_reward_account_proof = |State(state): State<S>, Path(address): Path<String>| async move {
359 state
360 .get_latest_reward_account_proof(address)
361 .await
362 .map(Json)
363 .map_err(ApiError::Internal)
364 };
365
366 let get_reward_amounts =
367 |State(state): State<S>, Path((height, offset, limit)): Path<(u64, u64, u64)>| async move {
368 state
369 .get_reward_amounts(height, offset, limit)
370 .await
371 .map(Json)
372 .map_err(ApiError::Internal)
373 };
374
375 let get_reward_merkle_tree_v2 = |State(state): State<S>, Path(height): Path<u64>| async move {
376 <S as v1::RewardApi>::get_reward_merkle_tree_v2(&state, height)
377 .await
378 .map(Json)
379 .map_err(ApiError::Internal)
380 };
381
382 let get_namespace_proof_by_height =
385 |State(state): State<S>, Path((height, namespace)): Path<(u64, u32)>| async move {
386 state
387 .get_namespace_proof(v1::availability::BlockId::Height(height), namespace)
388 .await
389 .map(Json)
390 .map_err(classify_availability_error)
391 };
392
393 let get_namespace_proof_by_hash =
395 |State(state): State<S>, Path((hash, namespace)): Path<(String, u32)>| async move {
396 state
397 .get_namespace_proof(v1::availability::BlockId::Hash(hash), namespace)
398 .await
399 .map(Json)
400 .map_err(classify_availability_error)
401 };
402
403 let get_namespace_proof_by_payload_hash =
405 |State(state): State<S>, Path((payload_hash, namespace)): Path<(String, u32)>| async move {
406 state
407 .get_namespace_proof(
408 v1::availability::BlockId::PayloadHash(payload_hash),
409 namespace,
410 )
411 .await
412 .map(Json)
413 .map_err(classify_availability_error)
414 };
415
416 let get_namespace_proof_range =
418 |State(state): State<S>, Path((from, until, namespace)): Path<(u64, u64, u32)>| async move {
419 state
420 .get_namespace_proof_range(from, until, namespace)
421 .await
422 .map(Json)
423 .map_err(classify_availability_error)
424 };
425
426 let get_incorrect_encoding_proof =
427 |State(state): State<S>, Path((block_number, namespace)): Path<(u64, u32)>| async move {
428 state
429 .get_incorrect_encoding_proof(
430 v1::availability::BlockId::Height(block_number),
431 namespace,
432 )
433 .await
434 .map(Json)
435 .map_err(classify_availability_error)
436 };
437
438 let get_state_cert_v1 = |State(state): State<S>, Path(epoch): Path<u64>| async move {
439 <S as v1::AvailabilityApi>::get_state_cert(&state, epoch)
440 .await
441 .map(Json)
442 .map_err(classify_availability_error)
443 };
444
445 let get_state_cert_v2 = |State(state): State<S>, Path(epoch): Path<u64>| async move {
446 state
447 .get_state_cert_v2(epoch)
448 .await
449 .map(Json)
450 .map_err(classify_availability_error)
451 };
452
453 let get_leaf_by_height = |State(state): State<S>, Path(height): Path<u64>| async move {
456 state
457 .get_leaf(v1::LeafId::Height(height))
458 .await
459 .map(Json)
460 .map_err(classify_availability_error)
461 };
462 let get_leaf_by_hash = |State(state): State<S>, Path(hash): Path<String>| async move {
463 state
464 .get_leaf(v1::LeafId::Hash(hash))
465 .await
466 .map(Json)
467 .map_err(classify_availability_error)
468 };
469 let get_leaf_range = |State(state): State<S>, Path((from, until)): Path<(usize, usize)>| async move {
470 state
471 .get_leaf_range(from, until)
472 .await
473 .map(Json)
474 .map_err(classify_availability_error)
475 };
476
477 let get_header_by_height = |State(state): State<S>, Path(height): Path<u64>| async move {
478 state
479 .get_header(v1::BlockId::Height(height))
480 .await
481 .map(Json)
482 .map_err(classify_availability_error)
483 };
484 let get_header_by_hash = |State(state): State<S>, Path(hash): Path<String>| async move {
485 state
486 .get_header(v1::BlockId::Hash(hash))
487 .await
488 .map(Json)
489 .map_err(classify_availability_error)
490 };
491 let get_header_by_payload_hash = |State(state): State<S>, Path(payload_hash): Path<String>| async move {
492 state
493 .get_header(v1::BlockId::PayloadHash(payload_hash))
494 .await
495 .map(Json)
496 .map_err(classify_availability_error)
497 };
498 let get_header_range = |State(state): State<S>, Path((from, until)): Path<(usize, usize)>| async move {
499 state
500 .get_header_range(from, until)
501 .await
502 .map(Json)
503 .map_err(classify_availability_error)
504 };
505
506 let get_block_by_height = |State(state): State<S>, Path(height): Path<u64>| async move {
507 state
508 .get_block(v1::BlockId::Height(height))
509 .await
510 .map(Json)
511 .map_err(classify_availability_error)
512 };
513 let get_block_by_hash = |State(state): State<S>, Path(hash): Path<String>| async move {
514 state
515 .get_block(v1::BlockId::Hash(hash))
516 .await
517 .map(Json)
518 .map_err(classify_availability_error)
519 };
520 let get_block_by_payload_hash = |State(state): State<S>, Path(payload_hash): Path<String>| async move {
521 state
522 .get_block(v1::BlockId::PayloadHash(payload_hash))
523 .await
524 .map(Json)
525 .map_err(classify_availability_error)
526 };
527 let get_block_range = |State(state): State<S>, Path((from, until)): Path<(usize, usize)>| async move {
528 state
529 .get_block_range(from, until)
530 .await
531 .map(Json)
532 .map_err(classify_availability_error)
533 };
534
535 let get_payload_by_height = |State(state): State<S>, Path(height): Path<u64>| async move {
536 state
537 .get_payload(v1::PayloadId::Height(height))
538 .await
539 .map(Json)
540 .map_err(classify_availability_error)
541 };
542 let get_payload_by_hash = |State(state): State<S>, Path(hash): Path<String>| async move {
543 state
544 .get_payload(v1::PayloadId::Hash(hash))
545 .await
546 .map(Json)
547 .map_err(classify_availability_error)
548 };
549 let get_payload_by_block_hash = |State(state): State<S>, Path(block_hash): Path<String>| async move {
550 state
551 .get_payload(v1::PayloadId::BlockHash(block_hash))
552 .await
553 .map(Json)
554 .map_err(classify_availability_error)
555 };
556 let get_payload_range = |State(state): State<S>, Path((from, until)): Path<(usize, usize)>| async move {
557 state
558 .get_payload_range(from, until)
559 .await
560 .map(Json)
561 .map_err(classify_availability_error)
562 };
563
564 let get_vid_common_by_height = |State(state): State<S>, Path(height): Path<u64>| async move {
565 state
566 .get_vid_common(v1::BlockId::Height(height))
567 .await
568 .map(Json)
569 .map_err(classify_availability_error)
570 };
571 let get_vid_common_by_hash = |State(state): State<S>, Path(hash): Path<String>| async move {
572 state
573 .get_vid_common(v1::BlockId::Hash(hash))
574 .await
575 .map(Json)
576 .map_err(classify_availability_error)
577 };
578 let get_vid_common_by_payload_hash =
579 |State(state): State<S>, Path(payload_hash): Path<String>| async move {
580 state
581 .get_vid_common(v1::BlockId::PayloadHash(payload_hash))
582 .await
583 .map(Json)
584 .map_err(classify_availability_error)
585 };
586 let get_vid_common_range =
587 |State(state): State<S>, Path((from, until)): Path<(usize, usize)>| async move {
588 state
589 .get_vid_common_range(from, until)
590 .await
591 .map(Json)
592 .map_err(classify_availability_error)
593 };
594
595 let get_transaction_by_position =
596 |State(state): State<S>, Path((height, index)): Path<(u64, u64)>| async move {
597 state
598 .get_transaction_by_position(height, index)
599 .await
600 .map(Json)
601 .map_err(classify_availability_error)
602 };
603 let get_transaction_by_hash = |State(state): State<S>, Path(hash): Path<String>| async move {
604 state
605 .get_transaction_by_hash(hash)
606 .await
607 .map(Json)
608 .map_err(classify_availability_error)
609 };
610 let get_transaction_proof_by_position =
611 |State(state): State<S>, Path((height, index)): Path<(u64, u64)>| async move {
612 state
613 .get_transaction_proof_by_position(height, index)
614 .await
615 .map(Json)
616 .map_err(classify_availability_error)
617 };
618 let get_transaction_proof_by_hash = |State(state): State<S>, Path(hash): Path<String>| async move {
619 state
620 .get_transaction_proof_by_hash(hash)
621 .await
622 .map(Json)
623 .map_err(classify_availability_error)
624 };
625
626 let get_block_summary_by_height = |State(state): State<S>, Path(height): Path<usize>| async move {
627 state
628 .get_block_summary(height)
629 .await
630 .map(Json)
631 .map_err(classify_availability_error)
632 };
633 let get_block_summary_range =
634 |State(state): State<S>, Path((from, until)): Path<(usize, usize)>| async move {
635 state
636 .get_block_summary_range(from, until)
637 .await
638 .map(Json)
639 .map_err(classify_availability_error)
640 };
641
642 let get_limits = |State(state): State<S>| async move {
643 state
644 .get_limits()
645 .await
646 .map(Json)
647 .map_err(ApiError::Internal)
648 };
649
650 let get_cert2 = |State(state): State<S>, Path(height): Path<u64>| async move {
651 <S as v1::HotShotAvailabilityApi>::get_cert2(&state, height)
652 .await
653 .map(Json)
654 .map_err(ApiError::Internal)
655 };
656
657 let stream_leaves = |ws: WebSocketUpgrade,
659 State(state): State<S>,
660 headers: HeaderMap,
661 Path(height): Path<usize>| async move {
662 let format = ws_format(&headers);
663 ws.on_upgrade(move |socket| async move {
664 match state.stream_leaves(height).await {
665 Ok(stream) => drive_ws_stream(socket, stream, format).await,
666 Err(e) => tracing::warn!("stream_leaves: {e}"),
667 }
668 })
669 };
670 let stream_headers = |ws: WebSocketUpgrade,
671 State(state): State<S>,
672 headers: HeaderMap,
673 Path(height): Path<usize>| async move {
674 let format = ws_format(&headers);
675 ws.on_upgrade(move |socket| async move {
676 match state.stream_headers(height).await {
677 Ok(stream) => drive_ws_stream(socket, stream, format).await,
678 Err(e) => tracing::warn!("stream_headers: {e}"),
679 }
680 })
681 };
682 let stream_blocks = |ws: WebSocketUpgrade,
683 State(state): State<S>,
684 headers: HeaderMap,
685 Path(height): Path<usize>| async move {
686 let format = ws_format(&headers);
687 ws.on_upgrade(move |socket| async move {
688 match state.stream_blocks(height).await {
689 Ok(stream) => drive_ws_stream(socket, stream, format).await,
690 Err(e) => tracing::warn!("stream_blocks: {e}"),
691 }
692 })
693 };
694 let stream_payloads = |ws: WebSocketUpgrade,
695 State(state): State<S>,
696 headers: HeaderMap,
697 Path(height): Path<usize>| async move {
698 let format = ws_format(&headers);
699 ws.on_upgrade(move |socket| async move {
700 match state.stream_payloads(height).await {
701 Ok(stream) => drive_ws_stream(socket, stream, format).await,
702 Err(e) => tracing::warn!("stream_payloads: {e}"),
703 }
704 })
705 };
706 let stream_vid_common = |ws: WebSocketUpgrade,
707 State(state): State<S>,
708 headers: HeaderMap,
709 Path(height): Path<usize>| async move {
710 let format = ws_format(&headers);
711 ws.on_upgrade(move |socket| async move {
712 match state.stream_vid_common(height).await {
713 Ok(stream) => drive_ws_stream(socket, stream, format).await,
714 Err(e) => tracing::warn!("stream_vid_common: {e}"),
715 }
716 })
717 };
718 let stream_transactions = |ws: WebSocketUpgrade,
719 State(state): State<S>,
720 headers: HeaderMap,
721 Path(height): Path<usize>| async move {
722 let format = ws_format(&headers);
723 ws.on_upgrade(move |socket| async move {
724 match state.stream_transactions(height, None).await {
725 Ok(stream) => drive_ws_stream(socket, stream, format).await,
726 Err(e) => tracing::warn!("stream_transactions: {e}"),
727 }
728 })
729 };
730 let stream_transactions_ns =
731 |ws: WebSocketUpgrade,
732 State(state): State<S>,
733 headers: HeaderMap,
734 Path((height, namespace)): Path<(usize, u32)>| async move {
735 let format = ws_format(&headers);
736 ws.on_upgrade(move |socket| async move {
737 match state.stream_transactions(height, Some(namespace)).await {
738 Ok(stream) => drive_ws_stream(socket, stream, format).await,
739 Err(e) => tracing::warn!("stream_transactions_ns: {e}"),
740 }
741 })
742 };
743 let stream_namespace_proofs =
744 |ws: WebSocketUpgrade,
745 State(state): State<S>,
746 headers: HeaderMap,
747 Path((height, namespace)): Path<(usize, u32)>| async move {
748 let format = ws_format(&headers);
749 ws.on_upgrade(move |socket| async move {
750 match state.stream_namespace_proofs(height, namespace).await {
751 Ok(stream) => drive_ws_stream(socket, stream, format).await,
752 Err(e) => tracing::warn!("stream_namespace_proofs: {e}"),
753 }
754 })
755 };
756
757 let get_block_state_path_by_height =
759 |State(state): State<S>, Path((height, key)): Path<(u64, String)>| async move {
760 <S as v1::BlockStateApi>::get_block_state_path(
761 &state,
762 v1::Snapshot::Height(height),
763 key,
764 )
765 .await
766 .map(Json)
767 .map_err(classify_availability_error)
768 };
769 let get_block_state_path_by_commit =
770 |State(state): State<S>, Path((commit, key)): Path<(String, String)>| async move {
771 <S as v1::BlockStateApi>::get_block_state_path(
772 &state,
773 v1::Snapshot::Commit(commit),
774 key,
775 )
776 .await
777 .map(Json)
778 .map_err(classify_availability_error)
779 };
780 let get_block_state_height = |State(state): State<S>| async move {
781 <S as v1::BlockStateApi>::get_block_state_height(&state)
782 .await
783 .map(Json)
784 .map_err(classify_availability_error)
785 };
786
787 let get_fee_state_path_by_height =
789 |State(state): State<S>, Path((height, key)): Path<(u64, String)>| async move {
790 <S as v1::FeeStateApi>::get_fee_state_path(&state, v1::Snapshot::Height(height), key)
791 .await
792 .map(Json)
793 .map_err(classify_availability_error)
794 };
795 let get_fee_state_path_by_commit =
796 |State(state): State<S>, Path((commit, key)): Path<(String, String)>| async move {
797 <S as v1::FeeStateApi>::get_fee_state_path(&state, v1::Snapshot::Commit(commit), key)
798 .await
799 .map(Json)
800 .map_err(classify_availability_error)
801 };
802 let get_fee_state_height = |State(state): State<S>| async move {
803 <S as v1::FeeStateApi>::get_fee_state_height(&state)
804 .await
805 .map(Json)
806 .map_err(classify_availability_error)
807 };
808 let get_fee_balance_latest = |State(state): State<S>, Path(address): Path<String>| async move {
809 state
810 .get_fee_balance_latest(address)
811 .await
812 .map(Json)
813 .map_err(classify_availability_error)
814 };
815
816 let status_block_height = |State(state): State<S>| async move {
817 <S as v1::StatusApi>::block_height(&state)
818 .await
819 .map(Json)
820 .map_err(ApiError::Internal)
821 };
822 let status_success_rate = |State(state): State<S>| async move {
823 <S as v1::StatusApi>::success_rate(&state)
824 .await
825 .map(Json)
826 .map_err(ApiError::Internal)
827 };
828 let status_time_since_last_decide = |State(state): State<S>| async move {
829 <S as v1::StatusApi>::time_since_last_decide(&state)
830 .await
831 .map(Json)
832 .map_err(ApiError::Internal)
833 };
834 let status_metrics = |State(state): State<S>| async move {
835 match <S as v1::StatusApi>::metrics(&state).await {
836 Ok(text) => Ok((
837 [(
838 axum::http::header::CONTENT_TYPE,
839 "text/plain; charset=utf-8",
840 )],
841 text,
842 )),
843 Err(e) => Err(ApiError::Internal(e)),
844 }
845 };
846
847 let config_hotshot = |State(state): State<S>| async move {
848 <S as v1::ConfigApi>::hotshot_config(&state)
849 .await
850 .map(Json)
851 .map_err(ApiError::Internal)
852 };
853 let config_env = |State(state): State<S>| async move {
854 <S as v1::ConfigApi>::env(&state)
855 .await
856 .map(Json)
857 .map_err(ApiError::Internal)
858 };
859 let config_runtime = |State(state): State<S>| async move {
860 <S as v1::ConfigApi>::runtime_config(&state)
861 .await
862 .map(Json)
863 .map_err(classify_availability_error)
864 };
865
866 let node_block_height = |State(state): State<S>| async move {
867 <S as v1::NodeApi>::block_height(&state)
868 .await
869 .map(Json)
870 .map_err(classify_availability_error)
871 };
872
873 let node_count_txs = |State(state): State<S>| async move {
874 state
875 .count_transactions(None, None, None)
876 .await
877 .map(Json)
878 .map_err(classify_availability_error)
879 };
880 let node_count_txs_to = |State(state): State<S>, Path(to): Path<u64>| async move {
881 state
882 .count_transactions(None, Some(to), None)
883 .await
884 .map(Json)
885 .map_err(classify_availability_error)
886 };
887 let node_count_txs_from_to = |State(state): State<S>, Path((from, to)): Path<(u64, u64)>| async move {
888 state
889 .count_transactions(Some(from), Some(to), None)
890 .await
891 .map(Json)
892 .map_err(classify_availability_error)
893 };
894 let node_count_txs_ns = |State(state): State<S>, Path(namespace): Path<u64>| async move {
895 state
896 .count_transactions(None, None, Some(namespace))
897 .await
898 .map(Json)
899 .map_err(classify_availability_error)
900 };
901 let node_count_txs_ns_to = |State(state): State<S>, Path((namespace, to)): Path<(u64, u64)>| async move {
902 state
903 .count_transactions(None, Some(to), Some(namespace))
904 .await
905 .map(Json)
906 .map_err(classify_availability_error)
907 };
908 let node_count_txs_ns_from_to =
909 |State(state): State<S>, Path((namespace, from, to)): Path<(u64, u64, u64)>| async move {
910 state
911 .count_transactions(Some(from), Some(to), Some(namespace))
912 .await
913 .map(Json)
914 .map_err(classify_availability_error)
915 };
916
917 let node_payload_size = |State(state): State<S>| async move {
918 state
919 .payload_size(None, None, None)
920 .await
921 .map(Json)
922 .map_err(classify_availability_error)
923 };
924 let node_payload_size_to = |State(state): State<S>, Path(to): Path<u64>| async move {
925 state
926 .payload_size(None, Some(to), None)
927 .await
928 .map(Json)
929 .map_err(classify_availability_error)
930 };
931 let node_payload_size_from_to = |State(state): State<S>, Path((from, to)): Path<(u64, u64)>| async move {
932 state
933 .payload_size(Some(from), Some(to), None)
934 .await
935 .map(Json)
936 .map_err(classify_availability_error)
937 };
938 let node_payload_size_ns = |State(state): State<S>, Path(namespace): Path<u64>| async move {
939 state
940 .payload_size(None, None, Some(namespace))
941 .await
942 .map(Json)
943 .map_err(classify_availability_error)
944 };
945 let node_payload_size_ns_to =
946 |State(state): State<S>, Path((namespace, to)): Path<(u64, u64)>| async move {
947 state
948 .payload_size(None, Some(to), Some(namespace))
949 .await
950 .map(Json)
951 .map_err(classify_availability_error)
952 };
953 let node_payload_size_ns_from_to =
954 |State(state): State<S>, Path((namespace, from, to)): Path<(u64, u64, u64)>| async move {
955 state
956 .payload_size(Some(from), Some(to), Some(namespace))
957 .await
958 .map(Json)
959 .map_err(classify_availability_error)
960 };
961
962 let node_vid_share_by_height = |State(state): State<S>, Path(height): Path<u64>| async move {
963 state
964 .get_vid_share(v1::VidShareId::Height(height))
965 .await
966 .map(Json)
967 .map_err(classify_availability_error)
968 };
969 let node_vid_share_by_hash = |State(state): State<S>, Path(hash): Path<String>| async move {
970 state
971 .get_vid_share(v1::VidShareId::Hash(hash))
972 .await
973 .map(Json)
974 .map_err(classify_availability_error)
975 };
976 let node_vid_share_by_payload_hash =
977 |State(state): State<S>, Path(payload_hash): Path<String>| async move {
978 state
979 .get_vid_share(v1::VidShareId::PayloadHash(payload_hash))
980 .await
981 .map(Json)
982 .map_err(classify_availability_error)
983 };
984
985 let node_sync_status = |State(state): State<S>| async move {
986 state
987 .sync_status()
988 .await
989 .map(Json)
990 .map_err(classify_availability_error)
991 };
992
993 let node_header_window_time = |State(state): State<S>, Path((start, end)): Path<(u64, u64)>| async move {
994 state
995 .get_header_window(v1::HeaderWindowStart::Time(start), end)
996 .await
997 .map(Json)
998 .map_err(classify_availability_error)
999 };
1000 let node_header_window_height =
1001 |State(state): State<S>, Path((height, end)): Path<(u64, u64)>| async move {
1002 state
1003 .get_header_window(v1::HeaderWindowStart::Height(height), end)
1004 .await
1005 .map(Json)
1006 .map_err(classify_availability_error)
1007 };
1008 let node_header_window_hash =
1009 |State(state): State<S>, Path((hash, end)): Path<(String, u64)>| async move {
1010 state
1011 .get_header_window(v1::HeaderWindowStart::Hash(hash), end)
1012 .await
1013 .map(Json)
1014 .map_err(classify_availability_error)
1015 };
1016
1017 let node_limits = |State(state): State<S>| async move {
1018 <S as v1::NodeApi>::limits(&state)
1019 .await
1020 .map(Json)
1021 .map_err(ApiError::Internal)
1022 };
1023
1024 let node_stake_table_current = |State(state): State<S>| async move {
1025 state
1026 .stake_table_current()
1027 .await
1028 .map(Json)
1029 .map_err(ApiError::Internal)
1030 };
1031 let node_stake_table = |State(state): State<S>, Path(epoch): Path<u64>| async move {
1032 state
1033 .stake_table(epoch)
1034 .await
1035 .map(Json)
1036 .map_err(ApiError::Internal)
1037 };
1038 let node_da_stake_table_current = |State(state): State<S>| async move {
1039 state
1040 .da_stake_table_current()
1041 .await
1042 .map(Json)
1043 .map_err(ApiError::Internal)
1044 };
1045 let node_da_stake_table = |State(state): State<S>, Path(epoch): Path<u64>| async move {
1046 state
1047 .da_stake_table(epoch)
1048 .await
1049 .map(Json)
1050 .map_err(ApiError::Internal)
1051 };
1052
1053 let node_validators = |State(state): State<S>, Path(epoch): Path<u64>| async move {
1054 state
1055 .get_validators(epoch)
1056 .await
1057 .map(Json)
1058 .map_err(ApiError::Internal)
1059 };
1060 let node_all_validators =
1061 |State(state): State<S>, Path((epoch, offset, limit)): Path<(u64, u64, u64)>| async move {
1062 state
1063 .get_all_validators(epoch, offset, limit)
1064 .await
1065 .map(Json)
1066 .map_err(ApiError::BadRequest)
1067 };
1068
1069 let node_proposal_participation_current = |State(state): State<S>| async move {
1070 state
1071 .current_proposal_participation()
1072 .await
1073 .map(Json)
1074 .map_err(ApiError::Internal)
1075 };
1076 let node_proposal_participation = |State(state): State<S>, Path(epoch): Path<u64>| async move {
1077 state
1078 .proposal_participation(epoch)
1079 .await
1080 .map(Json)
1081 .map_err(ApiError::Internal)
1082 };
1083 let node_vote_participation_current = |State(state): State<S>| async move {
1084 state
1085 .current_vote_participation()
1086 .await
1087 .map(Json)
1088 .map_err(ApiError::Internal)
1089 };
1090 let node_vote_participation = |State(state): State<S>, Path(epoch): Path<u64>| async move {
1091 state
1092 .vote_participation(epoch)
1093 .await
1094 .map(Json)
1095 .map_err(ApiError::Internal)
1096 };
1097
1098 let node_block_reward = |State(state): State<S>| async move {
1099 state
1100 .get_block_reward(None)
1101 .await
1102 .map(Json)
1103 .map_err(ApiError::Internal)
1104 };
1105 let node_block_reward_epoch = |State(state): State<S>, Path(epoch): Path<u64>| async move {
1106 state
1107 .get_block_reward(Some(epoch))
1108 .await
1109 .map(Json)
1110 .map_err(ApiError::Internal)
1111 };
1112
1113 let node_oldest_block = |State(state): State<S>| async move {
1114 state
1115 .get_oldest_block()
1116 .await
1117 .map(Json)
1118 .map_err(ApiError::Internal)
1119 };
1120 let node_oldest_leaf = |State(state): State<S>| async move {
1121 state
1122 .get_oldest_leaf()
1123 .await
1124 .map(Json)
1125 .map_err(ApiError::Internal)
1126 };
1127
1128 let catchup_account =
1130 |State(state): State<S>, Path((height, view, address)): Path<(u64, u64, String)>| async move {
1131 state
1132 .get_account(height, view, address)
1133 .await
1134 .map(Json)
1135 .map_err(classify_availability_error)
1136 };
1137 let catchup_accounts = |State(state): State<S>,
1138 Path((height, view)): Path<(u64, u64)>,
1139 headers: HeaderMap,
1140 body: Bytes| async move {
1141 let accounts: Vec<<S as v1::CatchupApi>::FeeAccount> = decode_body(&headers, &body)?;
1142 let tree = state
1143 .get_accounts(height, view, accounts)
1144 .await
1145 .map_err(classify_availability_error)?;
1146 encode_response(&headers, tree)
1147 };
1148 let catchup_blocks = |State(state): State<S>, Path((height, view)): Path<(u64, u64)>| async move {
1149 state
1150 .get_blocks_frontier(height, view)
1151 .await
1152 .map(Json)
1153 .map_err(classify_availability_error)
1154 };
1155 let catchup_chainconfig = |State(state): State<S>, Path(commitment): Path<String>| async move {
1156 <S as v1::CatchupApi>::get_chain_config(&state, commitment)
1157 .await
1158 .map(Json)
1159 .map_err(classify_availability_error)
1160 };
1161 let catchup_leafchain = |State(state): State<S>, Path(height): Path<u64>| async move {
1162 state
1163 .get_leaf_chain(height)
1164 .await
1165 .map(Json)
1166 .map_err(classify_availability_error)
1167 };
1168 let catchup_cert2 = |State(state): State<S>, Path(height): Path<u64>| async move {
1169 <S as v1::CatchupApi>::get_cert2(&state, height)
1170 .await
1171 .map(Json)
1172 .map_err(classify_availability_error)
1173 };
1174 let catchup_reward_account =
1175 |State(state): State<S>, Path((height, view, address)): Path<(u64, u64, String)>| async move {
1176 state
1177 .get_reward_account_v1(height, view, address)
1178 .await
1179 .map(Json)
1180 .map_err(classify_availability_error)
1181 };
1182 let catchup_reward_accounts = |State(state): State<S>,
1183 Path((height, view)): Path<(u64, u64)>,
1184 headers: HeaderMap,
1185 body: Bytes| async move {
1186 let accounts: Vec<<S as v1::CatchupApi>::RewardAccountV1> = decode_body(&headers, &body)?;
1187 let tree = state
1188 .get_reward_accounts_v1(height, view, accounts)
1189 .await
1190 .map_err(classify_availability_error)?;
1191 encode_response(&headers, tree)
1192 };
1193 let catchup_reward_account_v2 =
1194 |State(state): State<S>, Path((height, view, address)): Path<(u64, u64, String)>| async move {
1195 state
1196 .get_reward_account_v2(height, view, address)
1197 .await
1198 .map(Json)
1199 .map_err(classify_availability_error)
1200 };
1201 let catchup_reward_accounts_v2 =
1202 |State(_): State<S>, Path((_height, _view)): Path<(u64, u64)>| async move {
1203 Err::<Json<()>, ApiError>(ApiError::NotFound(anyhow::anyhow!(
1204 "catchup/reward-accounts-v2 is deprecated"
1205 )))
1206 };
1207 let catchup_reward_amounts =
1208 |State(_): State<S>, Path((_height, _limit, _offset)): Path<(u64, u64, u64)>| async move {
1209 Err::<Json<()>, ApiError>(ApiError::NotFound(anyhow::anyhow!(
1210 "catchup/reward-amounts is deprecated"
1211 )))
1212 };
1213 let catchup_reward_merkle_tree_v2 =
1214 |State(state): State<S>, Path((height, view)): Path<(u64, u64)>| async move {
1215 <S as v1::CatchupApi>::get_reward_merkle_tree_v2(&state, height, view)
1216 .await
1217 .map(Json)
1218 .map_err(classify_availability_error)
1219 };
1220 let catchup_state_cert = |State(state): State<S>, Path(epoch): Path<u64>| async move {
1221 <S as v1::CatchupApi>::get_state_cert(&state, epoch)
1222 .await
1223 .map(Json)
1224 .map_err(classify_availability_error)
1225 };
1226
1227 let submit_submit = |State(state): State<S>, headers: HeaderMap, body: Bytes| async move {
1230 let tx: <S as v1::SubmitApi>::Transaction = decode_body(&headers, &body)?;
1231 let hash = state.submit(tx).await.map_err(ApiError::Internal)?;
1232 encode_response(&headers, hash)
1233 };
1234
1235 let state_signature_block = |State(state): State<S>, Path(height): Path<u64>| async move {
1237 state
1238 .get_state_signature(height)
1239 .await
1240 .map(Json)
1241 .map_err(classify_availability_error)
1242 };
1243
1244 let hotshot_events_startup = |State(state): State<S>| async move {
1246 state
1247 .startup_info()
1248 .await
1249 .map(Json)
1250 .map_err(ApiError::Internal)
1251 };
1252 let hotshot_events_stream =
1253 |State(state): State<S>, headers: HeaderMap, ws: WebSocketUpgrade| async move {
1254 let format = ws_format(&headers);
1255 match <S as v1::HotShotEventsApi>::events(&state).await {
1256 Ok(stream) => ws.on_upgrade(move |socket| drive_ws_stream(socket, stream, format)),
1257 Err(err) => ApiError::Internal(err).into_response(),
1258 }
1259 };
1260
1261 let lc_leaf_by_height = |State(state): State<S>, Path(height): Path<u64>| async move {
1263 state
1264 .get_leaf_proof(v1::LeafQuery::Height(height), None)
1265 .await
1266 .map(Json)
1267 .map_err(classify_availability_error)
1268 };
1269 let lc_leaf_by_height_finalized =
1270 |State(state): State<S>, Path((height, finalized)): Path<(u64, u64)>| async move {
1271 state
1272 .get_leaf_proof(v1::LeafQuery::Height(height), Some(finalized))
1273 .await
1274 .map(Json)
1275 .map_err(classify_availability_error)
1276 };
1277 let lc_leaf_by_hash = |State(state): State<S>, Path(hash): Path<String>| async move {
1278 state
1279 .get_leaf_proof(v1::LeafQuery::Hash(hash), None)
1280 .await
1281 .map(Json)
1282 .map_err(classify_availability_error)
1283 };
1284 let lc_leaf_by_hash_finalized =
1285 |State(state): State<S>, Path((hash, finalized)): Path<(String, u64)>| async move {
1286 state
1287 .get_leaf_proof(v1::LeafQuery::Hash(hash), Some(finalized))
1288 .await
1289 .map(Json)
1290 .map_err(classify_availability_error)
1291 };
1292 let lc_leaf_by_block_hash = |State(state): State<S>, Path(block_hash): Path<String>| async move {
1293 state
1294 .get_leaf_proof(v1::LeafQuery::BlockHash(block_hash), None)
1295 .await
1296 .map(Json)
1297 .map_err(classify_availability_error)
1298 };
1299 let lc_leaf_by_block_hash_finalized =
1300 |State(state): State<S>, Path((block_hash, finalized)): Path<(String, u64)>| async move {
1301 state
1302 .get_leaf_proof(v1::LeafQuery::BlockHash(block_hash), Some(finalized))
1303 .await
1304 .map(Json)
1305 .map_err(classify_availability_error)
1306 };
1307 let lc_leaf_by_payload_hash = |State(state): State<S>, Path(payload_hash): Path<String>| async move {
1308 state
1309 .get_leaf_proof(v1::LeafQuery::PayloadHash(payload_hash), None)
1310 .await
1311 .map(Json)
1312 .map_err(classify_availability_error)
1313 };
1314 let lc_leaf_by_payload_hash_finalized =
1315 |State(state): State<S>, Path((payload_hash, finalized)): Path<(String, u64)>| async move {
1316 state
1317 .get_leaf_proof(v1::LeafQuery::PayloadHash(payload_hash), Some(finalized))
1318 .await
1319 .map(Json)
1320 .map_err(classify_availability_error)
1321 };
1322
1323 let lc_header_by_height = |State(state): State<S>, Path((root, height)): Path<(u64, u64)>| async move {
1324 state
1325 .get_header_proof(root, v1::HeaderQuery::Height(height))
1326 .await
1327 .map(Json)
1328 .map_err(classify_availability_error)
1329 };
1330 let lc_header_by_hash = |State(state): State<S>, Path((root, hash)): Path<(u64, String)>| async move {
1331 state
1332 .get_header_proof(root, v1::HeaderQuery::Hash(hash))
1333 .await
1334 .map(Json)
1335 .map_err(classify_availability_error)
1336 };
1337 let lc_header_by_payload_hash =
1338 |State(state): State<S>, Path((root, payload_hash)): Path<(u64, String)>| async move {
1339 state
1340 .get_header_proof(root, v1::HeaderQuery::PayloadHash(payload_hash))
1341 .await
1342 .map(Json)
1343 .map_err(classify_availability_error)
1344 };
1345 let lc_stake_table = |State(state): State<S>, Path(epoch): Path<u64>| async move {
1346 state
1347 .get_light_client_stake_table(epoch)
1348 .await
1349 .map(Json)
1350 .map_err(classify_availability_error)
1351 };
1352 let lc_payload = |State(state): State<S>, Path(height): Path<u64>| async move {
1353 state
1354 .get_payload_proof(height)
1355 .await
1356 .map(Json)
1357 .map_err(classify_availability_error)
1358 };
1359 let lc_payload_range = |State(state): State<S>, Path((start, end)): Path<(u64, u64)>| async move {
1360 state
1361 .get_payload_proof_range(start, end)
1362 .await
1363 .map(Json)
1364 .map_err(classify_availability_error)
1365 };
1366 let lc_namespace = |State(state): State<S>, Path((height, namespace)): Path<(u64, u64)>| async move {
1367 state
1368 .get_lc_namespace_proof(height, namespace)
1369 .await
1370 .map(Json)
1371 .map_err(classify_availability_error)
1372 };
1373 let lc_namespace_range =
1374 |State(state): State<S>, Path((start, end, namespace)): Path<(u64, u64, u64)>| async move {
1375 state
1376 .get_lc_namespace_proof_range(start, end, namespace)
1377 .await
1378 .map(Json)
1379 .map_err(classify_availability_error)
1380 };
1381
1382 let explorer_block_detail_by_height = |State(state): State<S>, Path(height): Path<u64>| async move {
1384 state
1385 .get_block_detail(v1::BlockIdent::Height(height))
1386 .await
1387 .map(Json)
1388 .map_err(classify_availability_error)
1389 };
1390 let explorer_block_detail_by_hash = |State(state): State<S>, Path(hash): Path<String>| async move {
1391 state
1392 .get_block_detail(v1::BlockIdent::Hash(hash))
1393 .await
1394 .map(Json)
1395 .map_err(classify_availability_error)
1396 };
1397 let explorer_block_summaries_latest = |State(state): State<S>, Path(limit): Path<u64>| async move {
1398 state
1399 .get_block_summaries(v1::BlockIdent::Latest, limit)
1400 .await
1401 .map(Json)
1402 .map_err(classify_availability_error)
1403 };
1404 let explorer_block_summaries_from =
1405 |State(state): State<S>, Path((from, limit)): Path<(u64, u64)>| async move {
1406 state
1407 .get_block_summaries(v1::BlockIdent::Height(from), limit)
1408 .await
1409 .map(Json)
1410 .map_err(classify_availability_error)
1411 };
1412 let explorer_tx_detail_by_position =
1413 |State(state): State<S>, Path((height, offset)): Path<(u64, u64)>| async move {
1414 state
1415 .get_transaction_detail(v1::TxIdent::HeightAndOffset(height, offset))
1416 .await
1417 .map(Json)
1418 .map_err(classify_availability_error)
1419 };
1420 let explorer_tx_detail_by_hash = |State(state): State<S>, Path(hash): Path<String>| async move {
1421 state
1422 .get_transaction_detail(v1::TxIdent::Hash(hash))
1423 .await
1424 .map(Json)
1425 .map_err(classify_availability_error)
1426 };
1427 let explorer_tx_summaries_latest = |State(state): State<S>, Path(limit): Path<u64>| async move {
1428 state
1429 .get_transaction_summaries(v1::TxIdent::Latest, limit, v1::TxSummaryFilter::None)
1430 .await
1431 .map(Json)
1432 .map_err(classify_availability_error)
1433 };
1434 let explorer_tx_summaries_from =
1435 |State(state): State<S>, Path((height, offset, limit)): Path<(u64, u64, u64)>| async move {
1436 state
1437 .get_transaction_summaries(
1438 v1::TxIdent::HeightAndOffset(height, offset),
1439 limit,
1440 v1::TxSummaryFilter::None,
1441 )
1442 .await
1443 .map(Json)
1444 .map_err(classify_availability_error)
1445 };
1446 let explorer_tx_summaries_by_hash =
1447 |State(state): State<S>, Path((hash, limit)): Path<(String, u64)>| async move {
1448 state
1449 .get_transaction_summaries(
1450 v1::TxIdent::Hash(hash),
1451 limit,
1452 v1::TxSummaryFilter::None,
1453 )
1454 .await
1455 .map(Json)
1456 .map_err(classify_availability_error)
1457 };
1458 let explorer_tx_summaries_latest_block =
1459 |State(state): State<S>, Path((limit, block)): Path<(u64, u64)>| async move {
1460 state
1461 .get_transaction_summaries(
1462 v1::TxIdent::Latest,
1463 limit,
1464 v1::TxSummaryFilter::Block(block),
1465 )
1466 .await
1467 .map(Json)
1468 .map_err(classify_availability_error)
1469 };
1470 let explorer_tx_summaries_from_block =
1471 |State(state): State<S>,
1472 Path((height, offset, limit, block)): Path<(u64, u64, u64, u64)>| async move {
1473 state
1474 .get_transaction_summaries(
1475 v1::TxIdent::HeightAndOffset(height, offset),
1476 limit,
1477 v1::TxSummaryFilter::Block(block),
1478 )
1479 .await
1480 .map(Json)
1481 .map_err(classify_availability_error)
1482 };
1483 let explorer_tx_summaries_by_hash_block =
1484 |State(state): State<S>, Path((hash, limit, block)): Path<(String, u64, u64)>| async move {
1485 state
1486 .get_transaction_summaries(
1487 v1::TxIdent::Hash(hash),
1488 limit,
1489 v1::TxSummaryFilter::Block(block),
1490 )
1491 .await
1492 .map(Json)
1493 .map_err(classify_availability_error)
1494 };
1495 let explorer_tx_summaries_latest_ns =
1496 |State(state): State<S>, Path((limit, namespace)): Path<(u64, i64)>| async move {
1497 state
1498 .get_transaction_summaries(
1499 v1::TxIdent::Latest,
1500 limit,
1501 v1::TxSummaryFilter::Namespace(namespace),
1502 )
1503 .await
1504 .map(Json)
1505 .map_err(classify_availability_error)
1506 };
1507 let explorer_tx_summaries_from_ns =
1508 |State(state): State<S>,
1509 Path((height, offset, limit, namespace)): Path<(u64, u64, u64, i64)>| async move {
1510 state
1511 .get_transaction_summaries(
1512 v1::TxIdent::HeightAndOffset(height, offset),
1513 limit,
1514 v1::TxSummaryFilter::Namespace(namespace),
1515 )
1516 .await
1517 .map(Json)
1518 .map_err(classify_availability_error)
1519 };
1520 let explorer_tx_summaries_by_hash_ns = |State(state): State<S>,
1521 Path((hash, limit, namespace)): Path<(
1522 String,
1523 u64,
1524 i64,
1525 )>| async move {
1526 state
1527 .get_transaction_summaries(
1528 v1::TxIdent::Hash(hash),
1529 limit,
1530 v1::TxSummaryFilter::Namespace(namespace),
1531 )
1532 .await
1533 .map(Json)
1534 .map_err(classify_availability_error)
1535 };
1536 let explorer_summary = |State(state): State<S>| async move {
1537 state
1538 .get_explorer_summary()
1539 .await
1540 .map(Json)
1541 .map_err(classify_availability_error)
1542 };
1543 let explorer_search = |State(state): State<S>, Path(query): Path<String>| async move {
1544 state
1545 .get_search_result(query)
1546 .await
1547 .map(Json)
1548 .map_err(classify_availability_error)
1549 };
1550
1551 let token_total_minted = |State(state): State<S>| async move {
1553 state
1554 .total_minted_supply()
1555 .await
1556 .map(Json)
1557 .map_err(classify_availability_error)
1558 };
1559 let token_circulating = |State(state): State<S>| async move {
1560 state
1561 .circulating_supply()
1562 .await
1563 .map(Json)
1564 .map_err(classify_availability_error)
1565 };
1566 let token_circulating_eth = |State(state): State<S>| async move {
1567 state
1568 .circulating_supply_ethereum()
1569 .await
1570 .map(Json)
1571 .map_err(classify_availability_error)
1572 };
1573 let token_total_issued = |State(state): State<S>| async move {
1574 state
1575 .total_issued_supply()
1576 .await
1577 .map(Json)
1578 .map_err(classify_availability_error)
1579 };
1580 let token_total_reward_distributed = |State(state): State<S>| async move {
1581 state
1582 .total_reward_distributed()
1583 .await
1584 .map(Json)
1585 .map_err(classify_availability_error)
1586 };
1587
1588 let database_table_sizes = |State(state): State<S>| async move {
1590 <S as v1::DatabaseApi>::get_table_sizes(&state)
1591 .await
1592 .map(Json)
1593 .map_err(ApiError::Internal)
1594 };
1595
1596 Router::new()
1598 .route(
1599 routes::v1::REWARD_CLAIM_INPUT_ROUTE,
1600 get(get_reward_claim_input),
1601 )
1602 .route(routes::v1::REWARD_BALANCE_ROUTE, get(get_reward_balance))
1603 .route(
1604 routes::v1::LATEST_REWARD_BALANCE_ROUTE,
1605 get(get_latest_reward_balance),
1606 )
1607 .route(
1608 routes::v1::REWARD_ACCOUNT_PROOF_ROUTE,
1609 get(get_reward_account_proof),
1610 )
1611 .route(
1612 routes::v1::LATEST_REWARD_ACCOUNT_PROOF_ROUTE,
1613 get(get_latest_reward_account_proof),
1614 )
1615 .route(routes::v1::REWARD_AMOUNTS_ROUTE, get(get_reward_amounts))
1616 .route(
1617 routes::v1::REWARD_MERKLE_TREE_V2_ROUTE,
1618 get(get_reward_merkle_tree_v2),
1619 )
1620 .route(
1622 routes::v1::NAMESPACE_PROOF_BY_HEIGHT_ROUTE,
1623 get(get_namespace_proof_by_height),
1624 )
1625 .route(
1626 routes::v1::NAMESPACE_PROOF_BY_HASH_ROUTE,
1627 get(get_namespace_proof_by_hash),
1628 )
1629 .route(
1630 routes::v1::NAMESPACE_PROOF_BY_PAYLOAD_HASH_ROUTE,
1631 get(get_namespace_proof_by_payload_hash),
1632 )
1633 .route(
1634 routes::v1::NAMESPACE_PROOF_RANGE_ROUTE,
1635 get(get_namespace_proof_range),
1636 )
1637 .route(
1638 routes::v1::INCORRECT_ENCODING_PROOF_ROUTE,
1639 get(get_incorrect_encoding_proof),
1640 )
1641 .route(routes::v1::STATE_CERT_V1_ROUTE, get(get_state_cert_v1))
1642 .route(routes::v1::STATE_CERT_V2_ROUTE, get(get_state_cert_v2))
1643 .route(routes::v1::LEAF_BY_HEIGHT_ROUTE, get(get_leaf_by_height))
1645 .route(routes::v1::LEAF_BY_HASH_ROUTE, get(get_leaf_by_hash))
1646 .route(routes::v1::LEAF_RANGE_ROUTE, get(get_leaf_range))
1647 .route(
1648 routes::v1::HEADER_BY_HEIGHT_ROUTE,
1649 get(get_header_by_height),
1650 )
1651 .route(routes::v1::HEADER_BY_HASH_ROUTE, get(get_header_by_hash))
1652 .route(
1653 routes::v1::HEADER_BY_PAYLOAD_HASH_ROUTE,
1654 get(get_header_by_payload_hash),
1655 )
1656 .route(routes::v1::HEADER_RANGE_ROUTE, get(get_header_range))
1657 .route(routes::v1::BLOCK_BY_HEIGHT_ROUTE, get(get_block_by_height))
1658 .route(routes::v1::BLOCK_BY_HASH_ROUTE, get(get_block_by_hash))
1659 .route(
1660 routes::v1::BLOCK_BY_PAYLOAD_HASH_ROUTE,
1661 get(get_block_by_payload_hash),
1662 )
1663 .route(routes::v1::BLOCK_RANGE_ROUTE, get(get_block_range))
1664 .route(
1665 routes::v1::PAYLOAD_BY_HEIGHT_ROUTE,
1666 get(get_payload_by_height),
1667 )
1668 .route(
1669 routes::v1::PAYLOAD_BY_HASH_ROUTE,
1670 get(get_payload_by_hash),
1671 )
1672 .route(
1673 routes::v1::PAYLOAD_BY_BLOCK_HASH_ROUTE,
1674 get(get_payload_by_block_hash),
1675 )
1676 .route(routes::v1::PAYLOAD_RANGE_ROUTE, get(get_payload_range))
1677 .route(
1678 routes::v1::VID_COMMON_BY_HEIGHT_ROUTE,
1679 get(get_vid_common_by_height),
1680 )
1681 .route(
1682 routes::v1::VID_COMMON_BY_HASH_ROUTE,
1683 get(get_vid_common_by_hash),
1684 )
1685 .route(
1686 routes::v1::VID_COMMON_BY_PAYLOAD_HASH_ROUTE,
1687 get(get_vid_common_by_payload_hash),
1688 )
1689 .route(
1690 routes::v1::VID_COMMON_RANGE_ROUTE,
1691 get(get_vid_common_range),
1692 )
1693 .route(
1694 routes::v1::TRANSACTION_BY_POSITION_NOPROOF_ROUTE,
1695 get(get_transaction_by_position),
1696 )
1697 .route(
1698 routes::v1::TRANSACTION_BY_HASH_NOPROOF_ROUTE,
1699 get(get_transaction_by_hash),
1700 )
1701 .route(
1702 routes::v1::TRANSACTION_PROOF_BY_POSITION_ROUTE,
1703 get(get_transaction_proof_by_position),
1704 )
1705 .route(
1706 routes::v1::TRANSACTION_PROOF_BY_HASH_ROUTE,
1707 get(get_transaction_proof_by_hash),
1708 )
1709 .route(
1710 routes::v1::TRANSACTION_BY_POSITION_ROUTE,
1711 get(get_transaction_proof_by_position),
1712 )
1713 .route(
1714 routes::v1::TRANSACTION_BY_HASH_ROUTE,
1715 get(get_transaction_proof_by_hash),
1716 )
1717 .route(
1718 routes::v1::BLOCK_SUMMARY_BY_HEIGHT_ROUTE,
1719 get(get_block_summary_by_height),
1720 )
1721 .route(
1722 routes::v1::BLOCK_SUMMARY_RANGE_ROUTE,
1723 get(get_block_summary_range),
1724 )
1725 .route(routes::v1::LIMITS_ROUTE, get(get_limits))
1726 .route(routes::v1::CERT2_BY_HEIGHT_ROUTE, get(get_cert2))
1727 .route(routes::v1::STREAM_LEAVES_ROUTE, get(stream_leaves))
1729 .route(routes::v1::STREAM_HEADERS_ROUTE, get(stream_headers))
1730 .route(routes::v1::STREAM_BLOCKS_ROUTE, get(stream_blocks))
1731 .route(routes::v1::STREAM_PAYLOADS_ROUTE, get(stream_payloads))
1732 .route(routes::v1::STREAM_VID_COMMON_ROUTE, get(stream_vid_common))
1733 .route(
1734 routes::v1::STREAM_TRANSACTIONS_ROUTE,
1735 get(stream_transactions),
1736 )
1737 .route(
1738 routes::v1::STREAM_TRANSACTIONS_NS_ROUTE,
1739 get(stream_transactions_ns),
1740 )
1741 .route(
1742 routes::v1::STREAM_NAMESPACE_PROOFS_ROUTE,
1743 get(stream_namespace_proofs),
1744 )
1745 .route(
1747 routes::v1::BLOCK_STATE_HEIGHT_ROUTE,
1748 get(get_block_state_height),
1749 )
1750 .route(
1751 routes::v1::BLOCK_STATE_PATH_BY_COMMIT_ROUTE,
1752 get(get_block_state_path_by_commit),
1753 )
1754 .route(
1755 routes::v1::BLOCK_STATE_PATH_BY_HEIGHT_ROUTE,
1756 get(get_block_state_path_by_height),
1757 )
1758 .route(
1760 routes::v1::FEE_STATE_HEIGHT_ROUTE,
1761 get(get_fee_state_height),
1762 )
1763 .route(
1764 routes::v1::FEE_STATE_BALANCE_LATEST_ROUTE,
1765 get(get_fee_balance_latest),
1766 )
1767 .route(
1768 routes::v1::FEE_STATE_PATH_BY_COMMIT_ROUTE,
1769 get(get_fee_state_path_by_commit),
1770 )
1771 .route(
1772 routes::v1::FEE_STATE_PATH_BY_HEIGHT_ROUTE,
1773 get(get_fee_state_path_by_height),
1774 )
1775 .route(routes::v1::STATUS_BLOCK_HEIGHT_ROUTE, get(status_block_height))
1777 .route(routes::v1::STATUS_SUCCESS_RATE_ROUTE, get(status_success_rate))
1778 .route(
1779 routes::v1::STATUS_TIME_SINCE_LAST_DECIDE_ROUTE,
1780 get(status_time_since_last_decide),
1781 )
1782 .route(routes::v1::STATUS_METRICS_ROUTE, get(status_metrics))
1783 .route(routes::v1::CONFIG_HOTSHOT_ROUTE, get(config_hotshot))
1785 .route(routes::v1::CONFIG_ENV_ROUTE, get(config_env))
1786 .route(routes::v1::CONFIG_RUNTIME_ROUTE, get(config_runtime))
1787 .route(routes::v1::NODE_BLOCK_HEIGHT_ROUTE, get(node_block_height))
1789 .route(routes::v1::NODE_TRANSACTIONS_COUNT_ROUTE, get(node_count_txs))
1790 .route(
1791 routes::v1::NODE_TRANSACTIONS_COUNT_NS_ROUTE,
1792 get(node_count_txs_ns),
1793 )
1794 .route(
1795 routes::v1::NODE_TRANSACTIONS_COUNT_NS_TO_ROUTE,
1796 get(node_count_txs_ns_to),
1797 )
1798 .route(
1799 routes::v1::NODE_TRANSACTIONS_COUNT_NS_FROM_TO_ROUTE,
1800 get(node_count_txs_ns_from_to),
1801 )
1802 .route(
1803 routes::v1::NODE_TRANSACTIONS_COUNT_TO_ROUTE,
1804 get(node_count_txs_to),
1805 )
1806 .route(
1807 routes::v1::NODE_TRANSACTIONS_COUNT_FROM_TO_ROUTE,
1808 get(node_count_txs_from_to),
1809 )
1810 .route(routes::v1::NODE_PAYLOADS_SIZE_ROUTE, get(node_payload_size))
1811 .route(
1812 routes::v1::NODE_PAYLOADS_TOTAL_SIZE_ROUTE,
1813 get(node_payload_size),
1814 )
1815 .route(
1816 routes::v1::NODE_PAYLOADS_SIZE_NS_ROUTE,
1817 get(node_payload_size_ns),
1818 )
1819 .route(
1820 routes::v1::NODE_PAYLOADS_SIZE_NS_TO_ROUTE,
1821 get(node_payload_size_ns_to),
1822 )
1823 .route(
1824 routes::v1::NODE_PAYLOADS_SIZE_NS_FROM_TO_ROUTE,
1825 get(node_payload_size_ns_from_to),
1826 )
1827 .route(
1828 routes::v1::NODE_PAYLOADS_SIZE_TO_ROUTE,
1829 get(node_payload_size_to),
1830 )
1831 .route(
1832 routes::v1::NODE_PAYLOADS_SIZE_FROM_TO_ROUTE,
1833 get(node_payload_size_from_to),
1834 )
1835 .route(
1836 routes::v1::NODE_VID_SHARE_BY_HASH_ROUTE,
1837 get(node_vid_share_by_hash),
1838 )
1839 .route(
1840 routes::v1::NODE_VID_SHARE_BY_PAYLOAD_HASH_ROUTE,
1841 get(node_vid_share_by_payload_hash),
1842 )
1843 .route(
1844 routes::v1::NODE_VID_SHARE_BY_HEIGHT_ROUTE,
1845 get(node_vid_share_by_height),
1846 )
1847 .route(routes::v1::NODE_SYNC_STATUS_ROUTE, get(node_sync_status))
1848 .route(
1849 routes::v1::NODE_HEADER_WINDOW_HASH_ROUTE,
1850 get(node_header_window_hash),
1851 )
1852 .route(
1853 routes::v1::NODE_HEADER_WINDOW_HEIGHT_ROUTE,
1854 get(node_header_window_height),
1855 )
1856 .route(
1857 routes::v1::NODE_HEADER_WINDOW_TIME_ROUTE,
1858 get(node_header_window_time),
1859 )
1860 .route(routes::v1::NODE_LIMITS_ROUTE, get(node_limits))
1861 .route(
1862 routes::v1::NODE_STAKE_TABLE_CURRENT_ROUTE,
1863 get(node_stake_table_current),
1864 )
1865 .route(routes::v1::NODE_STAKE_TABLE_ROUTE, get(node_stake_table))
1866 .route(
1867 routes::v1::NODE_DA_STAKE_TABLE_CURRENT_ROUTE,
1868 get(node_da_stake_table_current),
1869 )
1870 .route(routes::v1::NODE_DA_STAKE_TABLE_ROUTE, get(node_da_stake_table))
1871 .route(routes::v1::NODE_VALIDATORS_ROUTE, get(node_validators))
1872 .route(routes::v1::NODE_ALL_VALIDATORS_ROUTE, get(node_all_validators))
1873 .route(
1874 routes::v1::NODE_PROPOSAL_PARTICIPATION_CURRENT_ROUTE,
1875 get(node_proposal_participation_current),
1876 )
1877 .route(
1878 routes::v1::NODE_PROPOSAL_PARTICIPATION_ROUTE,
1879 get(node_proposal_participation),
1880 )
1881 .route(
1882 routes::v1::NODE_VOTE_PARTICIPATION_CURRENT_ROUTE,
1883 get(node_vote_participation_current),
1884 )
1885 .route(
1886 routes::v1::NODE_VOTE_PARTICIPATION_ROUTE,
1887 get(node_vote_participation),
1888 )
1889 .route(routes::v1::NODE_BLOCK_REWARD_ROUTE, get(node_block_reward))
1890 .route(
1891 routes::v1::NODE_BLOCK_REWARD_EPOCH_ROUTE,
1892 get(node_block_reward_epoch),
1893 )
1894 .route(routes::v1::NODE_OLDEST_BLOCK_ROUTE, get(node_oldest_block))
1895 .route(routes::v1::NODE_OLDEST_LEAF_ROUTE, get(node_oldest_leaf))
1896 .route(routes::v1::CATCHUP_ACCOUNT_ROUTE, get(catchup_account))
1898 .route(routes::v1::CATCHUP_ACCOUNTS_ROUTE, ::axum::routing::post(catchup_accounts))
1899 .route(routes::v1::CATCHUP_BLOCKS_ROUTE, get(catchup_blocks))
1900 .route(routes::v1::CATCHUP_CHAINCONFIG_ROUTE, get(catchup_chainconfig))
1901 .route(routes::v1::CATCHUP_LEAFCHAIN_ROUTE, get(catchup_leafchain))
1902 .route(routes::v1::CATCHUP_CERT2_ROUTE, get(catchup_cert2))
1903 .route(
1904 routes::v1::CATCHUP_REWARD_ACCOUNT_ROUTE,
1905 get(catchup_reward_account),
1906 )
1907 .route(
1908 routes::v1::CATCHUP_REWARD_ACCOUNTS_ROUTE,
1909 ::axum::routing::post(catchup_reward_accounts),
1910 )
1911 .route(
1912 routes::v1::CATCHUP_REWARD_ACCOUNT_V2_ROUTE,
1913 get(catchup_reward_account_v2),
1914 )
1915 .route(
1916 routes::v1::CATCHUP_REWARD_ACCOUNTS_V2_ROUTE,
1917 ::axum::routing::post(catchup_reward_accounts_v2),
1918 )
1919 .route(
1920 routes::v1::CATCHUP_REWARD_AMOUNTS_ROUTE,
1921 get(catchup_reward_amounts),
1922 )
1923 .route(
1924 routes::v1::CATCHUP_REWARD_MERKLE_TREE_V2_ROUTE,
1925 get(catchup_reward_merkle_tree_v2),
1926 )
1927 .route(routes::v1::CATCHUP_STATE_CERT_ROUTE, get(catchup_state_cert))
1928 .route(routes::v1::SUBMIT_ROUTE, ::axum::routing::post(submit_submit))
1930 .route(
1932 routes::v1::STATE_SIGNATURE_BLOCK_ROUTE,
1933 get(state_signature_block),
1934 )
1935 .route(
1937 routes::v1::HOTSHOT_EVENTS_STARTUP_ROUTE,
1938 get(hotshot_events_startup),
1939 )
1940 .route(
1941 routes::v1::HOTSHOT_EVENTS_STREAM_ROUTE,
1942 get(hotshot_events_stream),
1943 )
1944 .route(routes::v1::LC_LEAF_BY_HEIGHT_ROUTE, get(lc_leaf_by_height))
1946 .route(
1947 routes::v1::LC_LEAF_BY_HEIGHT_FINALIZED_ROUTE,
1948 get(lc_leaf_by_height_finalized),
1949 )
1950 .route(routes::v1::LC_LEAF_BY_HASH_ROUTE, get(lc_leaf_by_hash))
1951 .route(
1952 routes::v1::LC_LEAF_BY_HASH_FINALIZED_ROUTE,
1953 get(lc_leaf_by_hash_finalized),
1954 )
1955 .route(
1956 routes::v1::LC_LEAF_BY_BLOCK_HASH_ROUTE,
1957 get(lc_leaf_by_block_hash),
1958 )
1959 .route(
1960 routes::v1::LC_LEAF_BY_BLOCK_HASH_FINALIZED_ROUTE,
1961 get(lc_leaf_by_block_hash_finalized),
1962 )
1963 .route(
1964 routes::v1::LC_LEAF_BY_PAYLOAD_HASH_ROUTE,
1965 get(lc_leaf_by_payload_hash),
1966 )
1967 .route(
1968 routes::v1::LC_LEAF_BY_PAYLOAD_HASH_FINALIZED_ROUTE,
1969 get(lc_leaf_by_payload_hash_finalized),
1970 )
1971 .route(routes::v1::LC_HEADER_BY_HEIGHT_ROUTE, get(lc_header_by_height))
1972 .route(routes::v1::LC_HEADER_BY_HASH_ROUTE, get(lc_header_by_hash))
1973 .route(
1974 routes::v1::LC_HEADER_BY_PAYLOAD_HASH_ROUTE,
1975 get(lc_header_by_payload_hash),
1976 )
1977 .route(routes::v1::LC_STAKE_TABLE_ROUTE, get(lc_stake_table))
1978 .route(routes::v1::LC_PAYLOAD_ROUTE, get(lc_payload))
1979 .route(routes::v1::LC_PAYLOAD_RANGE_ROUTE, get(lc_payload_range))
1980 .route(routes::v1::LC_NAMESPACE_ROUTE, get(lc_namespace))
1981 .route(
1982 routes::v1::LC_NAMESPACE_RANGE_ROUTE,
1983 get(lc_namespace_range),
1984 )
1985 .route(
1987 routes::v1::EXPLORER_BLOCK_DETAIL_BY_HEIGHT_ROUTE,
1988 get(explorer_block_detail_by_height),
1989 )
1990 .route(
1991 routes::v1::EXPLORER_BLOCK_DETAIL_BY_HASH_ROUTE,
1992 get(explorer_block_detail_by_hash),
1993 )
1994 .route(
1995 routes::v1::EXPLORER_BLOCK_SUMMARIES_LATEST_ROUTE,
1996 get(explorer_block_summaries_latest),
1997 )
1998 .route(
1999 routes::v1::EXPLORER_BLOCK_SUMMARIES_FROM_ROUTE,
2000 get(explorer_block_summaries_from),
2001 )
2002 .route(
2003 routes::v1::EXPLORER_TX_DETAIL_BY_POSITION_ROUTE,
2004 get(explorer_tx_detail_by_position),
2005 )
2006 .route(
2007 routes::v1::EXPLORER_TX_DETAIL_BY_HASH_ROUTE,
2008 get(explorer_tx_detail_by_hash),
2009 )
2010 .route(
2011 routes::v1::EXPLORER_TX_SUMMARIES_LATEST_BLOCK_ROUTE,
2012 get(explorer_tx_summaries_latest_block),
2013 )
2014 .route(
2015 routes::v1::EXPLORER_TX_SUMMARIES_FROM_BLOCK_ROUTE,
2016 get(explorer_tx_summaries_from_block),
2017 )
2018 .route(
2019 routes::v1::EXPLORER_TX_SUMMARIES_BY_HASH_BLOCK_ROUTE,
2020 get(explorer_tx_summaries_by_hash_block),
2021 )
2022 .route(
2023 routes::v1::EXPLORER_TX_SUMMARIES_LATEST_NS_ROUTE,
2024 get(explorer_tx_summaries_latest_ns),
2025 )
2026 .route(
2027 routes::v1::EXPLORER_TX_SUMMARIES_FROM_NS_ROUTE,
2028 get(explorer_tx_summaries_from_ns),
2029 )
2030 .route(
2031 routes::v1::EXPLORER_TX_SUMMARIES_BY_HASH_NS_ROUTE,
2032 get(explorer_tx_summaries_by_hash_ns),
2033 )
2034 .route(
2035 routes::v1::EXPLORER_TX_SUMMARIES_LATEST_ROUTE,
2036 get(explorer_tx_summaries_latest),
2037 )
2038 .route(
2039 routes::v1::EXPLORER_TX_SUMMARIES_FROM_ROUTE,
2040 get(explorer_tx_summaries_from),
2041 )
2042 .route(
2043 routes::v1::EXPLORER_TX_SUMMARIES_BY_HASH_ROUTE,
2044 get(explorer_tx_summaries_by_hash),
2045 )
2046 .route(routes::v1::EXPLORER_SUMMARY_ROUTE, get(explorer_summary))
2047 .route(routes::v1::EXPLORER_SEARCH_ROUTE, get(explorer_search))
2048 .route(
2050 routes::v1::TOKEN_TOTAL_MINTED_SUPPLY_ROUTE,
2051 get(token_total_minted),
2052 )
2053 .route(
2054 routes::v1::TOKEN_CIRCULATING_SUPPLY_ROUTE,
2055 get(token_circulating),
2056 )
2057 .route(
2058 routes::v1::TOKEN_CIRCULATING_SUPPLY_ETHEREUM_ROUTE,
2059 get(token_circulating_eth),
2060 )
2061 .route(
2062 routes::v1::TOKEN_TOTAL_ISSUED_SUPPLY_ROUTE,
2063 get(token_total_issued),
2064 )
2065 .route(
2066 routes::v1::TOKEN_TOTAL_REWARD_DISTRIBUTED_ROUTE,
2067 get(token_total_reward_distributed),
2068 )
2069 .route(
2071 routes::v1::DATABASE_TABLE_SIZES_ROUTE,
2072 get(database_table_sizes),
2073 )
2074 .with_state(state)
2075}
2076
2077pub fn create_router_v2<S>(state: S) -> Router
2079where
2080 S: v2::RewardApi + v2::DataApi + v2::ConsensusApi + Clone + Send + Sync + 'static,
2081{
2082 let mut api = OpenApi {
2083 info: Info {
2084 title: "Espresso Node API v2".to_string(),
2085 description: None,
2086 version: "1.0.0".to_string(),
2087 ..Default::default()
2088 },
2089 ..Default::default()
2090 };
2091
2092 let get_reward_claim_input =
2093 |State(state): State<S>, SendQuery(request): SendQuery<GetRewardClaimInputRequest>| async move {
2094 handlers::get_reward_claim_input(&state, request)
2095 .await
2096 .map(Json)
2097 };
2098
2099 let get_reward_balance =
2100 |State(state): State<S>, SendQuery(request): SendQuery<GetRewardBalanceRequest>| async move {
2101 handlers::get_reward_balance(&state, request)
2102 .await
2103 .map(Json)
2104 };
2105
2106 let get_reward_account_proof =
2107 |State(state): State<S>, SendQuery(request): SendQuery<GetRewardAccountProofRequest>| async move {
2108 handlers::get_reward_account_proof(&state, request)
2109 .await
2110 .map(Json)
2111 };
2112
2113 let get_reward_balances =
2114 |State(state): State<S>, SendQuery(request): SendQuery<GetRewardBalancesRequest>| async move {
2115 handlers::get_reward_balances(&state, request)
2116 .await
2117 .map(Json)
2118 };
2119
2120 let get_reward_merkle_tree_v2 =
2121 |State(state): State<S>, SendQuery(request): SendQuery<GetRewardMerkleTreeRequest>| async move {
2122 handlers::get_reward_merkle_tree_v2(&state, request)
2123 .await
2124 .map(Json)
2125 };
2126
2127 let get_state_certificate =
2128 |State(state): State<S>, SendQuery(request): SendQuery<GetStateCertificateRequest>| async move {
2129 handlers::get_state_certificate(&state, request)
2130 .await
2131 .map(Json)
2132 };
2133
2134 let get_stake_table =
2135 |State(state): State<S>, SendQuery(request): SendQuery<GetStakeTableRequest>| async move {
2136 handlers::get_stake_table(&state, request).await.map(Json)
2137 };
2138
2139 let get_namespace_proof =
2140 |State(state): State<S>, SendQuery(query): SendQuery<GetNamespaceProofRequest>| async move {
2141 handlers::get_namespace_proof(&state, query).await.map(Json)
2142 };
2143
2144 let get_incorrect_encoding_proof = |State(state): State<S>,
2145 SendQuery(query): SendQuery<
2146 GetIncorrectEncodingProofRequest,
2147 >| async move {
2148 handlers::get_incorrect_encoding_proof(&state, query)
2149 .await
2150 .map(Json)
2151 };
2152
2153 let router = ApiRouter::new()
2154 .api_route(
2155 routes::v2::REWARD_CLAIM_INPUT_ROUTE.http,
2156 get_with(get_reward_claim_input, |op| {
2157 op.description(routes::v2::REWARD_CLAIM_INPUT_ROUTE.description)
2158 .tag(routes::v2::REWARD_CLAIM_INPUT_ROUTE.tag)
2159 }),
2160 )
2161 .api_route(
2162 routes::v2::REWARD_BALANCE_ROUTE.http,
2163 get_with(get_reward_balance, |op| {
2164 op.description(routes::v2::REWARD_BALANCE_ROUTE.description)
2165 .tag(routes::v2::REWARD_BALANCE_ROUTE.tag)
2166 }),
2167 )
2168 .api_route(
2169 routes::v2::REWARD_ACCOUNT_PROOF_ROUTE.http,
2170 get_with(get_reward_account_proof, |op| {
2171 op.description(routes::v2::REWARD_ACCOUNT_PROOF_ROUTE.description)
2172 .tag(routes::v2::REWARD_ACCOUNT_PROOF_ROUTE.tag)
2173 }),
2174 )
2175 .api_route(
2176 routes::v2::REWARD_BALANCES_ROUTE.http,
2177 get_with(get_reward_balances, |op| {
2178 op.description(routes::v2::REWARD_BALANCES_ROUTE.description)
2179 .tag(routes::v2::REWARD_BALANCES_ROUTE.tag)
2180 }),
2181 )
2182 .api_route(
2183 routes::v2::REWARD_MERKLE_TREE_V2_ROUTE.http,
2184 get_with(get_reward_merkle_tree_v2, |op| {
2185 op.description(routes::v2::REWARD_MERKLE_TREE_V2_ROUTE.description)
2186 .tag(routes::v2::REWARD_MERKLE_TREE_V2_ROUTE.tag)
2187 }),
2188 )
2189 .api_route(
2190 routes::v2::NAMESPACE_PROOF_ROUTE.http,
2191 get_with(get_namespace_proof, |op| {
2192 op.description(routes::v2::NAMESPACE_PROOF_ROUTE.description)
2193 .tag(routes::v2::NAMESPACE_PROOF_ROUTE.tag)
2194 }),
2195 )
2196 .api_route(
2197 routes::v2::INCORRECT_ENCODING_PROOF_ROUTE.http,
2198 get_with(get_incorrect_encoding_proof, |op| {
2199 op.description(routes::v2::INCORRECT_ENCODING_PROOF_ROUTE.description)
2200 .tag(routes::v2::INCORRECT_ENCODING_PROOF_ROUTE.tag)
2201 }),
2202 )
2203 .api_route(
2204 routes::v2::STATE_CERTIFICATE_ROUTE.http,
2205 get_with(get_state_certificate, |op| {
2206 op.description(routes::v2::STATE_CERTIFICATE_ROUTE.description)
2207 .tag(routes::v2::STATE_CERTIFICATE_ROUTE.tag)
2208 }),
2209 )
2210 .api_route(
2211 routes::v2::STAKE_TABLE_ROUTE.http,
2212 get_with(get_stake_table, |op| {
2213 op.description(routes::v2::STAKE_TABLE_ROUTE.description)
2214 .tag(routes::v2::STAKE_TABLE_ROUTE.tag)
2215 }),
2216 )
2217 .finish_api(&mut api);
2218
2219 if let Some(ref mut components) = api.components {
2221 let mut transform = schemars::transform::SetSingleExample::default();
2222 for schema in components.schemas.values_mut() {
2223 transform.transform(&mut schema.json_schema);
2224 }
2225 }
2226
2227 if let Some(ref mut paths) = api.paths {
2229 let mut transform = schemars::transform::SetSingleExample::default();
2230 for path_item_ref in paths.paths.values_mut() {
2231 if let aide::openapi::ReferenceOr::Item(path_item) = path_item_ref {
2232 for operation in [
2233 &mut path_item.get,
2234 &mut path_item.post,
2235 &mut path_item.put,
2236 &mut path_item.delete,
2237 &mut path_item.patch,
2238 ]
2239 .into_iter()
2240 .flatten()
2241 {
2242 for param in &mut operation.parameters {
2243 if let aide::openapi::ReferenceOr::Item(param_item) = param {
2244 let parameter_data = match param_item {
2245 aide::openapi::Parameter::Query { parameter_data, .. } => {
2246 parameter_data
2247 },
2248 aide::openapi::Parameter::Header { parameter_data, .. } => {
2249 parameter_data
2250 },
2251 aide::openapi::Parameter::Path { parameter_data, .. } => {
2252 parameter_data
2253 },
2254 aide::openapi::Parameter::Cookie { parameter_data, .. } => {
2255 parameter_data
2256 },
2257 };
2258 if let aide::openapi::ParameterSchemaOrContent::Schema(ref mut schema) =
2259 parameter_data.format
2260 {
2261 transform.transform(&mut schema.json_schema);
2262 }
2263 }
2264 }
2265 }
2266 }
2267 }
2268 }
2269
2270 router
2271 .route(routes::v2::OPENAPI_SPEC_ROUTE, get(serve_openapi_spec))
2272 .route(routes::v2::SWAGGER_ROUTE, get(serve_swagger_ui))
2273 .route("/v2/", get(serve_swagger_ui))
2274 .route(
2275 routes::v2::SCALAR_ROUTE,
2276 get(Scalar::new(routes::v2::OPENAPI_SPEC_ROUTE)
2277 .with_title("Espresso Node API v2")
2278 .axum_handler()),
2279 )
2280 .route(
2281 routes::v2::REDOC_ROUTE,
2282 get(Redoc::new(routes::v2::OPENAPI_SPEC_ROUTE)
2283 .with_title("Espresso Node API v2")
2284 .axum_handler()),
2285 )
2286 .layer(Extension(api))
2287 .with_state(state)
2288}