hotshot_query_service/
merklized_state.rs1use std::{
19 fmt::{Debug, Display},
20 path::PathBuf,
21};
22
23use derive_more::From;
24use futures::FutureExt;
25use hotshot_types::traits::node_implementation::NodeType;
26use serde::{Deserialize, Serialize};
27use snafu::{ResultExt, Snafu};
28use tagged_base64::TaggedBase64;
29use tide_disco::{api::ApiError, method::ReadState, Api, RequestError, StatusCode};
30use vbs::version::StaticVersionType;
31
32use crate::{api::load_api, QueryError};
33
34pub(crate) mod data_source;
35pub use data_source::*;
36
37#[derive(Default)]
38pub struct Options {
39 pub api_path: Option<PathBuf>,
40
41 pub extensions: Vec<toml::Value>,
46}
47
48#[derive(Clone, Debug, From, Snafu, Deserialize, Serialize)]
49pub enum Error {
50 Request {
51 source: RequestError,
52 },
53 #[snafu(display("{source}"))]
54 Query {
55 source: QueryError,
56 },
57 #[snafu(display("error {status}: {message}"))]
58 Custom {
59 message: String,
60 status: StatusCode,
61 },
62}
63
64impl Error {
65 pub fn status(&self) -> StatusCode {
66 match self {
67 Self::Request { .. } => StatusCode::BAD_REQUEST,
68 Self::Query { source, .. } => source.status(),
69 Self::Custom { status, .. } => *status,
70 }
71 }
72}
73
74pub fn define_api<
75 State,
76 Types: NodeType,
77 M: MerklizedState<Types, ARITY>,
78 Ver: StaticVersionType + 'static,
79 const ARITY: usize,
80>(
81 options: &Options,
82 api_ver: semver::Version,
83) -> Result<Api<State, Error, Ver>, ApiError>
84where
85 State: 'static + Send + Sync + ReadState,
86 <State as ReadState>::State:
87 MerklizedStateDataSource<Types, M, ARITY> + MerklizedStateHeightPersistence + Send + Sync,
88 for<'a> <M::Commit as TryFrom<&'a TaggedBase64>>::Error: Display,
89{
90 let mut api = load_api::<State, Error, Ver>(
91 options.api_path.as_ref(),
92 include_str!("../api/state.toml"),
93 options.extensions.clone(),
94 )?;
95
96 api.with_version(api_ver)
97 .get("get_path", move |req, state| {
98 async move {
99 let snapshot = if let Some(height) = req.opt_integer_param("height")? {
101 Snapshot::Index(height)
102 } else {
103 Snapshot::Commit(req.blob_param("commit")?)
104 };
105
106 let key = req.string_param("key")?;
107 let key = key.parse::<M::Key>().map_err(|_| Error::Custom {
108 message: "failed to parse Key param".to_string(),
109 status: StatusCode::INTERNAL_SERVER_ERROR,
110 })?;
111
112 state.get_path(snapshot, key).await.context(QuerySnafu)
113 }
114 .boxed()
115 })?
116 .get("get_height", move |_, state| {
117 async move { state.get_last_state_height().await.context(QuerySnafu) }.boxed()
118 })?;
119
120 Ok(api)
121}