Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 080f28d626 | |||
| ae7f29313e | |||
| ab7b88d091 | |||
| 57cd4896d9 | |||
| 193baf13b4 | |||
| 5742a67014 |
Generated
+4
-1
@@ -5785,12 +5785,14 @@ name = "nym-http-api-client"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
"bincode",
|
||||||
"bytes",
|
"bytes",
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
"hickory-resolver",
|
"hickory-resolver",
|
||||||
"http 1.3.1",
|
"http 1.3.1",
|
||||||
"mime",
|
"mime",
|
||||||
"nym-bin-common",
|
"nym-bin-common",
|
||||||
|
"nym-http-api-common",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"reqwest 0.12.15",
|
"reqwest 0.12.15",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -5808,14 +5810,15 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"axum 0.7.9",
|
"axum 0.7.9",
|
||||||
"axum-client-ip",
|
"axum-client-ip",
|
||||||
|
"bincode",
|
||||||
"bytes",
|
"bytes",
|
||||||
"colored",
|
"colored",
|
||||||
"futures",
|
"futures",
|
||||||
"mime",
|
"mime",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
"subtle 2.6.1",
|
"subtle 2.6.1",
|
||||||
|
"time",
|
||||||
"tower 0.5.2",
|
"tower 0.5.2",
|
||||||
"tracing",
|
"tracing",
|
||||||
"utoipa",
|
"utoipa",
|
||||||
|
|||||||
@@ -70,6 +70,10 @@ impl NymApiTopologyProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn disable_bincode(&mut self) {
|
||||||
|
self.validator_client.use_bincode = false;
|
||||||
|
}
|
||||||
|
|
||||||
fn use_next_nym_api(&mut self) {
|
fn use_next_nym_api(&mut self) {
|
||||||
if self.nym_api_urls.len() == 1 {
|
if self.nym_api_urls.len() == 1 {
|
||||||
warn!("There's only a single nym API available - it won't be possible to use a different one");
|
warn!("There's only a single nym API available - it won't be possible to use a different one");
|
||||||
|
|||||||
@@ -345,25 +345,47 @@ impl<C, S> Client<C, S> {
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct NymApiClient {
|
pub struct NymApiClient {
|
||||||
|
pub use_bincode: bool,
|
||||||
pub nym_api: nym_api::Client,
|
pub nym_api: nym_api::Client,
|
||||||
// TODO: perhaps if we really need it at some (currently I don't see any reasons for it)
|
// TODO: perhaps if we really need it at some (currently I don't see any reasons for it)
|
||||||
// we could re-implement the communication with the REST API on port 1317
|
// we could re-implement the communication with the REST API on port 1317
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<nym_api::Client> for NymApiClient {
|
||||||
|
fn from(nym_api: nym_api::Client) -> Self {
|
||||||
|
NymApiClient {
|
||||||
|
use_bincode: false,
|
||||||
|
nym_api,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// we have to allow the use of deprecated method here as they're calling the deprecated trait methods
|
// we have to allow the use of deprecated method here as they're calling the deprecated trait methods
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
impl NymApiClient {
|
impl NymApiClient {
|
||||||
pub fn new(api_url: Url) -> Self {
|
pub fn new(api_url: Url) -> Self {
|
||||||
let nym_api = nym_api::Client::new(api_url, None);
|
let nym_api = nym_api::Client::new(api_url, None);
|
||||||
|
|
||||||
NymApiClient { nym_api }
|
NymApiClient {
|
||||||
|
use_bincode: true,
|
||||||
|
nym_api,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub fn new_with_timeout(api_url: Url, timeout: std::time::Duration) -> Self {
|
pub fn new_with_timeout(api_url: Url, timeout: std::time::Duration) -> Self {
|
||||||
let nym_api = nym_api::Client::new(api_url, Some(timeout));
|
let nym_api = nym_api::Client::new(api_url, Some(timeout));
|
||||||
|
|
||||||
NymApiClient { nym_api }
|
NymApiClient {
|
||||||
|
use_bincode: true,
|
||||||
|
nym_api,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_bincode(mut self, use_bincode: bool) -> Self {
|
||||||
|
self.use_bincode = use_bincode;
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_with_user_agent(api_url: Url, user_agent: impl Into<UserAgent>) -> Self {
|
pub fn new_with_user_agent(api_url: Url, user_agent: impl Into<UserAgent>) -> Self {
|
||||||
@@ -373,7 +395,10 @@ impl NymApiClient {
|
|||||||
.build::<ValidatorClientError>()
|
.build::<ValidatorClientError>()
|
||||||
.expect("failed to build nym api client");
|
.expect("failed to build nym api client");
|
||||||
|
|
||||||
NymApiClient { nym_api }
|
NymApiClient {
|
||||||
|
use_bincode: false,
|
||||||
|
nym_api,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn api_url(&self) -> &Url {
|
pub fn api_url(&self) -> &Url {
|
||||||
@@ -410,7 +435,7 @@ impl NymApiClient {
|
|||||||
loop {
|
loop {
|
||||||
let mut res = self
|
let mut res = self
|
||||||
.nym_api
|
.nym_api
|
||||||
.get_basic_entry_assigned_nodes(false, Some(page), None)
|
.get_basic_entry_assigned_nodes(false, Some(page), None, self.use_bincode)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
nodes.append(&mut res.nodes.data);
|
nodes.append(&mut res.nodes.data);
|
||||||
@@ -436,7 +461,7 @@ impl NymApiClient {
|
|||||||
loop {
|
loop {
|
||||||
let mut res = self
|
let mut res = self
|
||||||
.nym_api
|
.nym_api
|
||||||
.get_basic_active_mixing_assigned_nodes(false, Some(page), None)
|
.get_basic_active_mixing_assigned_nodes(false, Some(page), None, self.use_bincode)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
nodes.append(&mut res.nodes.data);
|
nodes.append(&mut res.nodes.data);
|
||||||
@@ -462,7 +487,7 @@ impl NymApiClient {
|
|||||||
loop {
|
loop {
|
||||||
let mut res = self
|
let mut res = self
|
||||||
.nym_api
|
.nym_api
|
||||||
.get_basic_mixing_capable_nodes(false, Some(page), None)
|
.get_basic_mixing_capable_nodes(false, Some(page), None, self.use_bincode)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
nodes.append(&mut res.nodes.data);
|
nodes.append(&mut res.nodes.data);
|
||||||
@@ -485,7 +510,7 @@ impl NymApiClient {
|
|||||||
loop {
|
loop {
|
||||||
let mut res = self
|
let mut res = self
|
||||||
.nym_api
|
.nym_api
|
||||||
.get_basic_nodes(false, Some(page), None)
|
.get_basic_nodes(false, Some(page), None, self.use_bincode)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
nodes.append(&mut res.nodes.data);
|
nodes.append(&mut res.nodes.data);
|
||||||
|
|||||||
@@ -318,6 +318,7 @@ pub trait NymApiClientExt: ApiClient {
|
|||||||
no_legacy: bool,
|
no_legacy: bool,
|
||||||
page: Option<u32>,
|
page: Option<u32>,
|
||||||
per_page: Option<u32>,
|
per_page: Option<u32>,
|
||||||
|
use_bincode: bool,
|
||||||
) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> {
|
) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> {
|
||||||
let mut params = Vec::new();
|
let mut params = Vec::new();
|
||||||
|
|
||||||
@@ -333,7 +334,11 @@ pub trait NymApiClientExt: ApiClient {
|
|||||||
params.push(("per_page", per_page.to_string()))
|
params.push(("per_page", per_page.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
self.get_json(
|
if use_bincode {
|
||||||
|
params.push(("output", "bincode".to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
self.get_response(
|
||||||
&[
|
&[
|
||||||
routes::API_VERSION,
|
routes::API_VERSION,
|
||||||
"unstable",
|
"unstable",
|
||||||
@@ -355,6 +360,7 @@ pub trait NymApiClientExt: ApiClient {
|
|||||||
no_legacy: bool,
|
no_legacy: bool,
|
||||||
page: Option<u32>,
|
page: Option<u32>,
|
||||||
per_page: Option<u32>,
|
per_page: Option<u32>,
|
||||||
|
use_bincode: bool,
|
||||||
) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> {
|
) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> {
|
||||||
let mut params = Vec::new();
|
let mut params = Vec::new();
|
||||||
|
|
||||||
@@ -370,7 +376,11 @@ pub trait NymApiClientExt: ApiClient {
|
|||||||
params.push(("per_page", per_page.to_string()))
|
params.push(("per_page", per_page.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
self.get_json(
|
if use_bincode {
|
||||||
|
params.push(("output", "bincode".to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
self.get_response(
|
||||||
&[
|
&[
|
||||||
routes::API_VERSION,
|
routes::API_VERSION,
|
||||||
"unstable",
|
"unstable",
|
||||||
@@ -392,6 +402,7 @@ pub trait NymApiClientExt: ApiClient {
|
|||||||
no_legacy: bool,
|
no_legacy: bool,
|
||||||
page: Option<u32>,
|
page: Option<u32>,
|
||||||
per_page: Option<u32>,
|
per_page: Option<u32>,
|
||||||
|
use_bincode: bool,
|
||||||
) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> {
|
) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> {
|
||||||
let mut params = Vec::new();
|
let mut params = Vec::new();
|
||||||
|
|
||||||
@@ -407,7 +418,11 @@ pub trait NymApiClientExt: ApiClient {
|
|||||||
params.push(("per_page", per_page.to_string()))
|
params.push(("per_page", per_page.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
self.get_json(
|
if use_bincode {
|
||||||
|
params.push(("output", "bincode".to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
self.get_response(
|
||||||
&[
|
&[
|
||||||
routes::API_VERSION,
|
routes::API_VERSION,
|
||||||
"unstable",
|
"unstable",
|
||||||
@@ -427,6 +442,7 @@ pub trait NymApiClientExt: ApiClient {
|
|||||||
no_legacy: bool,
|
no_legacy: bool,
|
||||||
page: Option<u32>,
|
page: Option<u32>,
|
||||||
per_page: Option<u32>,
|
per_page: Option<u32>,
|
||||||
|
use_bincode: bool,
|
||||||
) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> {
|
) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> {
|
||||||
let mut params = Vec::new();
|
let mut params = Vec::new();
|
||||||
|
|
||||||
@@ -442,7 +458,11 @@ pub trait NymApiClientExt: ApiClient {
|
|||||||
params.push(("per_page", per_page.to_string()))
|
params.push(("per_page", per_page.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
self.get_json(
|
if use_bincode {
|
||||||
|
params.push(("output", "bincode".to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
self.get_response(
|
||||||
&[
|
&[
|
||||||
routes::API_VERSION,
|
routes::API_VERSION,
|
||||||
"unstable",
|
"unstable",
|
||||||
|
|||||||
@@ -98,11 +98,10 @@ impl SqliteEcashTicketbookManager {
|
|||||||
pub(crate) async fn contains_ticketbook_data(&self, data: &[u8]) -> Result<bool, sqlx::Error> {
|
pub(crate) async fn contains_ticketbook_data(&self, data: &[u8]) -> Result<bool, sqlx::Error> {
|
||||||
let exists = sqlx::query(
|
let exists = sqlx::query(
|
||||||
r#"
|
r#"
|
||||||
SELECT EXISTS (
|
SELECT 1
|
||||||
SELECT 1
|
FROM ecash_ticketbook
|
||||||
FROM ecash_ticketbook
|
WHERE ticketbook_data = ?
|
||||||
WHERE ticketbook_data = ?
|
|
||||||
)
|
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.bind(data)
|
.bind(data)
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ license.workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
reqwest = { workspace = true, features = ["json", "gzip"] }
|
bincode = { workspace = true }
|
||||||
|
reqwest = { workspace = true, features = ["json", "gzip", "deflate", "brotli", "zstd"] }
|
||||||
http.workspace = true
|
http.workspace = true
|
||||||
url = { workspace = true }
|
url = { workspace = true }
|
||||||
once_cell = { workspace = true }
|
once_cell = { workspace = true }
|
||||||
@@ -26,7 +27,7 @@ bytes = { workspace = true }
|
|||||||
encoding_rs = { workspace = true }
|
encoding_rs = { workspace = true }
|
||||||
mime = { workspace = true }
|
mime = { workspace = true }
|
||||||
|
|
||||||
|
nym-http-api-common = { path = "../http-api-common", default-features = false }
|
||||||
nym-bin-common = { path = "../bin-common" }
|
nym-bin-common = { path = "../bin-common" }
|
||||||
|
|
||||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies]
|
[target."cfg(not(target_arch = \"wasm32\"))".dependencies]
|
||||||
@@ -39,3 +40,4 @@ features = ["tokio"]
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tokio = { workspace = true, features = ["rt", "macros"] }
|
tokio = { workspace = true, features = ["rt", "macros"] }
|
||||||
|
|
||||||
|
|||||||
@@ -144,10 +144,13 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tracing::{instrument, warn};
|
use tracing::{debug, instrument, warn};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
use bytes::Bytes;
|
||||||
|
use http::header::CONTENT_TYPE;
|
||||||
use http::HeaderMap;
|
use http::HeaderMap;
|
||||||
|
use mime::Mime;
|
||||||
pub use reqwest::IntoUrl;
|
pub use reqwest::IntoUrl;
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
@@ -210,11 +213,8 @@ pub enum HttpClientError<E: Display = String> {
|
|||||||
#[error("failed to resolve request. status: '{status}', additional error message: {error}")]
|
#[error("failed to resolve request. status: '{status}', additional error message: {error}")]
|
||||||
EndpointFailure { status: StatusCode, error: E },
|
EndpointFailure { status: StatusCode, error: E },
|
||||||
|
|
||||||
#[error("failed to decode response body: {source} from {content}")]
|
#[error("failed to decode response body: {message} from {content}")]
|
||||||
ResponseDecodeFailure {
|
ResponseDecodeFailure { message: String, content: String },
|
||||||
source: serde_json::Error,
|
|
||||||
content: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
#[error("the request has timed out")]
|
#[error("the request has timed out")]
|
||||||
@@ -265,14 +265,18 @@ impl ClientBuilder {
|
|||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
let reqwest_client_builder = {
|
let reqwest_client_builder = {
|
||||||
let r = reqwest::ClientBuilder::new();
|
// Note: I believe the manual enable calls for the compression methods are extra
|
||||||
|
// as the various compression features for `reqwest` crate should be enabled
|
||||||
// Note this is extra as the `gzip` feature for `reqwest` crate should be enabled which
|
// just by including the feature which:
|
||||||
// `"Enable[s] auto gzip decompression by checking the Content-Encoding response header."`
|
// `"Enable[s] auto decompression by checking the Content-Encoding response header."`
|
||||||
//
|
//
|
||||||
// I am going to leave it here anyways so that gzip decompression is attempted even if
|
// I am going to leave these here anyways so that removing a decompression method
|
||||||
// that feature is removed.
|
// from the features list will throw an error if it is not also removed here.
|
||||||
r.gzip(true)
|
reqwest::ClientBuilder::new()
|
||||||
|
.gzip(true)
|
||||||
|
.deflate(true)
|
||||||
|
.brotli(true)
|
||||||
|
.zstd(true)
|
||||||
};
|
};
|
||||||
|
|
||||||
ClientBuilder {
|
ClientBuilder {
|
||||||
@@ -535,11 +539,6 @@ impl ApiClientCore for Client {
|
|||||||
|
|
||||||
let mut request = self.reqwest_client.request(method.clone(), url);
|
let mut request = self.reqwest_client.request(method.clone(), url);
|
||||||
|
|
||||||
// Indicate that compressed responses are preferred, but if not supported other encodings are fine.
|
|
||||||
// TODO: Down the road we can be more selective about adding this, but it's inclusion here guarantees
|
|
||||||
// that we use compression when available.
|
|
||||||
request = request.header(reqwest::header::ACCEPT_ENCODING, "gzip;q=1.0, *;q=0.5");
|
|
||||||
|
|
||||||
if let Some(body) = json_body {
|
if let Some(body) = json_body {
|
||||||
request = request.json(body);
|
request = request.json(body);
|
||||||
}
|
}
|
||||||
@@ -698,11 +697,29 @@ pub trait ApiClient: ApiClientCore {
|
|||||||
/// defined key-value parameters, e.g. `[("since", "12345")]`. Attempt to parse the response
|
/// defined key-value parameters, e.g. `[("since", "12345")]`. Attempt to parse the response
|
||||||
/// into the provided type `T`.
|
/// into the provided type `T`.
|
||||||
#[instrument(level = "debug", skip_all)]
|
#[instrument(level = "debug", skip_all)]
|
||||||
|
// TODO: deprecate in favour of get_response that works based on mime type in the response
|
||||||
async fn get_json<T, K, V, E>(
|
async fn get_json<T, K, V, E>(
|
||||||
&self,
|
&self,
|
||||||
path: PathSegments<'_>,
|
path: PathSegments<'_>,
|
||||||
params: Params<'_, K, V>,
|
params: Params<'_, K, V>,
|
||||||
) -> Result<T, HttpClientError<E>>
|
) -> Result<T, HttpClientError<E>>
|
||||||
|
where
|
||||||
|
for<'a> T: Deserialize<'a>,
|
||||||
|
K: AsRef<str> + Sync,
|
||||||
|
V: AsRef<str> + Sync,
|
||||||
|
E: Display + DeserializeOwned,
|
||||||
|
{
|
||||||
|
self.get_response(path, params).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 'get' data from the segment-defined path, e.g. `["api", "v1", "mixnodes"]`, with tuple
|
||||||
|
/// defined key-value parameters, e.g. `[("since", "12345")]`. Attempt to parse the response
|
||||||
|
/// into the provided type `T` based on the content type header
|
||||||
|
async fn get_response<T, K, V, E>(
|
||||||
|
&self,
|
||||||
|
path: PathSegments<'_>,
|
||||||
|
params: Params<'_, K, V>,
|
||||||
|
) -> Result<T, HttpClientError<E>>
|
||||||
where
|
where
|
||||||
for<'a> T: Deserialize<'a>,
|
for<'a> T: Deserialize<'a>,
|
||||||
K: AsRef<str> + Sync,
|
K: AsRef<str> + Sync,
|
||||||
@@ -877,14 +894,10 @@ fn sanitize_url<K: AsRef<str>, V: AsRef<str>>(
|
|||||||
url
|
url
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_as_text(bytes: &bytes::Bytes, headers: HeaderMap) -> String {
|
fn decode_as_text(bytes: &bytes::Bytes, headers: &HeaderMap) -> String {
|
||||||
use encoding_rs::{Encoding, UTF_8};
|
use encoding_rs::{Encoding, UTF_8};
|
||||||
use mime::Mime;
|
|
||||||
|
|
||||||
let content_type = headers
|
let content_type = try_get_mime_type(headers);
|
||||||
.get(http::header::CONTENT_TYPE)
|
|
||||||
.and_then(|value| value.to_str().ok())
|
|
||||||
.and_then(|value| value.parse::<Mime>().ok());
|
|
||||||
|
|
||||||
let encoding_name = content_type
|
let encoding_name = content_type
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@@ -897,7 +910,7 @@ fn decode_as_text(bytes: &bytes::Bytes, headers: HeaderMap) -> String {
|
|||||||
text.into_owned()
|
text.into_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to parse a json object from an HTTP response
|
/// Attempt to parse a response object from an HTTP response
|
||||||
#[instrument(level = "debug", skip_all)]
|
#[instrument(level = "debug", skip_all)]
|
||||||
pub async fn parse_response<T, E>(res: Response, allow_empty: bool) -> Result<T, HttpClientError<E>>
|
pub async fn parse_response<T, E>(res: Response, allow_empty: bool) -> Result<T, HttpClientError<E>>
|
||||||
where
|
where
|
||||||
@@ -919,16 +932,7 @@ where
|
|||||||
// internally reqwest is first retrieving bytes and then performing parsing via serde_json
|
// internally reqwest is first retrieving bytes and then performing parsing via serde_json
|
||||||
// (and similarly does the same thing for text())
|
// (and similarly does the same thing for text())
|
||||||
let full = res.bytes().await?;
|
let full = res.bytes().await?;
|
||||||
match serde_json::from_slice(&full) {
|
decode_raw_response(&headers, full)
|
||||||
Ok(data) => Ok(data),
|
|
||||||
Err(err) => {
|
|
||||||
let content = decode_as_text(&full, headers);
|
|
||||||
Err(HttpClientError::ResponseDecodeFailure {
|
|
||||||
source: err,
|
|
||||||
content,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if res.status() == StatusCode::NOT_FOUND {
|
} else if res.status() == StatusCode::NOT_FOUND {
|
||||||
Err(HttpClientError::NotFound)
|
Err(HttpClientError::NotFound)
|
||||||
} else {
|
} else {
|
||||||
@@ -947,6 +951,71 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn decode_as_json<T, E>(headers: &HeaderMap, content: Bytes) -> Result<T, HttpClientError<E>>
|
||||||
|
where
|
||||||
|
T: DeserializeOwned,
|
||||||
|
E: DeserializeOwned + Display,
|
||||||
|
{
|
||||||
|
match serde_json::from_slice(&content) {
|
||||||
|
Ok(data) => Ok(data),
|
||||||
|
Err(err) => {
|
||||||
|
let content = decode_as_text(&content, headers);
|
||||||
|
Err(HttpClientError::ResponseDecodeFailure {
|
||||||
|
message: err.to_string(),
|
||||||
|
content,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_as_bincode<T, E>(headers: &HeaderMap, content: Bytes) -> Result<T, HttpClientError<E>>
|
||||||
|
where
|
||||||
|
T: DeserializeOwned,
|
||||||
|
E: DeserializeOwned + Display,
|
||||||
|
{
|
||||||
|
use bincode::Options;
|
||||||
|
|
||||||
|
let opts = nym_http_api_common::make_bincode_serializer();
|
||||||
|
match opts.deserialize(&content) {
|
||||||
|
Ok(data) => Ok(data),
|
||||||
|
Err(err) => {
|
||||||
|
let content = decode_as_text(&content, headers);
|
||||||
|
Err(HttpClientError::ResponseDecodeFailure {
|
||||||
|
message: err.to_string(),
|
||||||
|
content,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_raw_response<T, E>(headers: &HeaderMap, content: Bytes) -> Result<T, HttpClientError<E>>
|
||||||
|
where
|
||||||
|
T: DeserializeOwned,
|
||||||
|
E: DeserializeOwned + Display,
|
||||||
|
{
|
||||||
|
// if content type header is missing, fallback to our old default, json
|
||||||
|
let mime = try_get_mime_type(headers).unwrap_or(mime::APPLICATION_JSON);
|
||||||
|
|
||||||
|
debug!("attempting to parse response as {mime}");
|
||||||
|
|
||||||
|
// unfortunately we can't use stronger typing for subtype as "bincode" is not a defined mime type
|
||||||
|
match (mime.type_(), mime.subtype().as_str()) {
|
||||||
|
(mime::APPLICATION, "json") => decode_as_json(headers, content),
|
||||||
|
(mime::APPLICATION, "bincode") => decode_as_bincode(headers, content),
|
||||||
|
(_, _) => {
|
||||||
|
debug!("unrecognised mime type {mime}. falling back to json decoding...");
|
||||||
|
decode_as_json(headers, content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_get_mime_type(headers: &HeaderMap) -> Option<Mime> {
|
||||||
|
headers
|
||||||
|
.get(CONTENT_TYPE)
|
||||||
|
.and_then(|value| value.to_str().ok())
|
||||||
|
.and_then(|value| value.parse::<Mime>().ok())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@@ -11,20 +11,44 @@ license.workspace = true
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
axum-client-ip.workspace = true
|
axum = { workspace = true, optional = true }
|
||||||
axum.workspace = true
|
axum-client-ip = { workspace = true, optional = true }
|
||||||
bytes = { workspace = true }
|
bincode = { workspace = true }
|
||||||
colored.workspace = true
|
bytes = { workspace = true, optional = true }
|
||||||
futures = { workspace = true }
|
colored = { workspace = true, optional = true }
|
||||||
mime = { workspace = true }
|
futures = { workspace = true, optional = true }
|
||||||
|
mime = { workspace = true, optional = true }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json.workspace = true
|
serde_yaml = { workspace = true, optional = true }
|
||||||
serde_yaml = { workspace = true }
|
subtle = { workspace = true, optional = true }
|
||||||
subtle.workspace = true
|
time = { workspace = true, optional = true, features = ["macros"] }
|
||||||
tower = { workspace = true }
|
tower = { workspace = true, optional = true }
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
utoipa = { workspace = true, optional = true }
|
utoipa = { workspace = true, optional = true }
|
||||||
zeroize = { workspace = true }
|
zeroize = { workspace = true, optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
default = []
|
||||||
|
output = [
|
||||||
|
"axum",
|
||||||
|
"bytes",
|
||||||
|
"mime",
|
||||||
|
"serde_yaml",
|
||||||
|
"time",
|
||||||
|
"time/formatting"
|
||||||
|
]
|
||||||
|
|
||||||
|
middleware = [
|
||||||
|
"axum",
|
||||||
|
"axum-client-ip",
|
||||||
|
"colored",
|
||||||
|
"futures",
|
||||||
|
"subtle",
|
||||||
|
"tower",
|
||||||
|
"zeroize"
|
||||||
|
]
|
||||||
|
|
||||||
utoipa = ["dep:utoipa"]
|
utoipa = ["dep:utoipa"]
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
@@ -1,92 +1,20 @@
|
|||||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2023-2025 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use axum::http::{header, HeaderValue, StatusCode};
|
#[cfg(feature = "middleware")]
|
||||||
use axum::response::{IntoResponse, Response};
|
|
||||||
use axum::Json;
|
|
||||||
use bytes::{BufMut, BytesMut};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
pub mod middleware;
|
pub mod middleware;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[cfg(feature = "output")]
|
||||||
pub enum FormattedResponse<T> {
|
pub mod response;
|
||||||
Json(Json<T>),
|
|
||||||
Yaml(Yaml<T>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> IntoResponse for FormattedResponse<T>
|
// don't break existing imports
|
||||||
where
|
#[cfg(feature = "output")]
|
||||||
T: Serialize,
|
pub use response::*;
|
||||||
{
|
|
||||||
fn into_response(self) -> Response {
|
|
||||||
match self {
|
|
||||||
FormattedResponse::Json(json_response) => json_response.into_response(),
|
|
||||||
FormattedResponse::Yaml(yaml_response) => yaml_response.into_response(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Serialize, Deserialize, Copy, Clone)]
|
// be explicit about those values because bincode uses different defaults in different places
|
||||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
pub fn make_bincode_serializer() -> impl ::bincode::Options {
|
||||||
#[serde(rename_all = "lowercase")]
|
use ::bincode::Options;
|
||||||
pub enum Output {
|
::bincode::DefaultOptions::new()
|
||||||
#[default]
|
.with_little_endian()
|
||||||
Json,
|
.with_varint_encoding()
|
||||||
Yaml,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Serialize, Deserialize, Copy, Clone)]
|
|
||||||
#[cfg_attr(feature = "utoipa", derive(utoipa::IntoParams, utoipa::ToSchema))]
|
|
||||||
#[serde(default)]
|
|
||||||
pub struct OutputParams {
|
|
||||||
pub output: Option<Output>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Output {
|
|
||||||
pub fn to_response<T: Serialize>(self, data: T) -> FormattedResponse<T> {
|
|
||||||
match self {
|
|
||||||
Output::Json => FormattedResponse::Json(Json(data)),
|
|
||||||
Output::Yaml => FormattedResponse::Yaml(Yaml(data)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
|
||||||
#[must_use]
|
|
||||||
pub struct Yaml<T>(pub T);
|
|
||||||
|
|
||||||
impl<T> From<T> for Yaml<T> {
|
|
||||||
fn from(inner: T) -> Self {
|
|
||||||
Self(inner)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> IntoResponse for Yaml<T>
|
|
||||||
where
|
|
||||||
T: Serialize,
|
|
||||||
{
|
|
||||||
// replicates axum's Json
|
|
||||||
fn into_response(self) -> Response {
|
|
||||||
let mut buf = BytesMut::with_capacity(128).writer();
|
|
||||||
match serde_yaml::to_writer(&mut buf, &self.0) {
|
|
||||||
Ok(()) => (
|
|
||||||
[(
|
|
||||||
header::CONTENT_TYPE,
|
|
||||||
HeaderValue::from_static("application/yaml"),
|
|
||||||
)],
|
|
||||||
buf.into_inner().freeze(),
|
|
||||||
)
|
|
||||||
.into_response(),
|
|
||||||
Err(err) => (
|
|
||||||
StatusCode::INTERNAL_SERVER_ERROR,
|
|
||||||
[(
|
|
||||||
header::CONTENT_TYPE,
|
|
||||||
HeaderValue::from_static(mime::TEXT_PLAIN_UTF_8.as_ref()),
|
|
||||||
)],
|
|
||||||
err.to_string(),
|
|
||||||
)
|
|
||||||
.into_response(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::response::{error_response, ResponseWrapper};
|
||||||
|
use axum::http::header::IntoHeaderName;
|
||||||
|
use axum::http::{header, HeaderValue};
|
||||||
|
use axum::response::{IntoResponse, Response};
|
||||||
|
use bytes::{BufMut, BytesMut};
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
#[must_use]
|
||||||
|
pub struct Bincode<T>(pub(crate) ResponseWrapper<T>);
|
||||||
|
|
||||||
|
impl<T> From<T> for Bincode<T> {
|
||||||
|
fn from(response: T) -> Self {
|
||||||
|
Bincode(ResponseWrapper::new(response).with_header(
|
||||||
|
header::CONTENT_TYPE,
|
||||||
|
HeaderValue::from_static("application/bincode"),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Bincode<T> {
|
||||||
|
pub(crate) fn with_header(
|
||||||
|
mut self,
|
||||||
|
name: impl IntoHeaderName,
|
||||||
|
value: impl Into<HeaderValue>,
|
||||||
|
) -> Self {
|
||||||
|
self.0.headers.insert(name, value.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> IntoResponse for Bincode<T>
|
||||||
|
where
|
||||||
|
T: Serialize,
|
||||||
|
{
|
||||||
|
// replicates axum's Json
|
||||||
|
fn into_response(self) -> Response {
|
||||||
|
use bincode::Options;
|
||||||
|
let mut buf = BytesMut::with_capacity(128).writer();
|
||||||
|
|
||||||
|
match crate::make_bincode_serializer().serialize_into(&mut buf, &self.0.data) {
|
||||||
|
Ok(()) => (self.0.headers, buf.into_inner().freeze()).into_response(),
|
||||||
|
Err(err) => error_response(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::response::{error_response, ResponseWrapper};
|
||||||
|
use axum::http::header::IntoHeaderName;
|
||||||
|
use axum::http::{header, HeaderValue};
|
||||||
|
use axum::response::{IntoResponse, Response};
|
||||||
|
use bytes::{BufMut, BytesMut};
|
||||||
|
use serde::Serialize;
|
||||||
|
use utoipa::gen::serde_json;
|
||||||
|
|
||||||
|
// don't use axum's Json directly as we need to be able to define custom headers
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
#[must_use]
|
||||||
|
pub struct Json<T>(pub(crate) ResponseWrapper<T>);
|
||||||
|
|
||||||
|
impl<T> From<T> for Json<T> {
|
||||||
|
fn from(response: T) -> Self {
|
||||||
|
Json(ResponseWrapper::new(response).with_header(
|
||||||
|
header::CONTENT_TYPE,
|
||||||
|
HeaderValue::from_static(mime::APPLICATION_JSON.as_ref()),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Json<T> {
|
||||||
|
pub(crate) fn with_header(
|
||||||
|
mut self,
|
||||||
|
name: impl IntoHeaderName,
|
||||||
|
value: impl Into<HeaderValue>,
|
||||||
|
) -> Self {
|
||||||
|
self.0.headers.insert(name, value.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> IntoResponse for Json<T>
|
||||||
|
where
|
||||||
|
T: Serialize,
|
||||||
|
{
|
||||||
|
fn into_response(self) -> Response {
|
||||||
|
let mut buf = BytesMut::with_capacity(128).writer();
|
||||||
|
|
||||||
|
match serde_json::to_writer(&mut buf, &self.0.data) {
|
||||||
|
Ok(()) => (self.0.headers, buf.into_inner().freeze()).into_response(),
|
||||||
|
Err(err) => error_response(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use axum::http::header::IntoHeaderName;
|
||||||
|
use axum::http::{header, HeaderMap, HeaderValue, StatusCode};
|
||||||
|
use axum::response::{IntoResponse, Response};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::time::Duration;
|
||||||
|
use time::format_description::BorrowedFormatItem;
|
||||||
|
use time::macros::{format_description, offset};
|
||||||
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
|
pub mod bincode;
|
||||||
|
pub mod json;
|
||||||
|
pub mod yaml;
|
||||||
|
|
||||||
|
pub use json::Json;
|
||||||
|
pub use yaml::Yaml;
|
||||||
|
|
||||||
|
pub use bincode::Bincode;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub(crate) struct ResponseWrapper<T> {
|
||||||
|
data: T,
|
||||||
|
headers: HeaderMap,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ResponseWrapper<T> {
|
||||||
|
pub(crate) fn new(response: T) -> ResponseWrapper<T> {
|
||||||
|
ResponseWrapper {
|
||||||
|
data: response,
|
||||||
|
headers: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub(crate) fn with_header(
|
||||||
|
mut self,
|
||||||
|
name: impl IntoHeaderName,
|
||||||
|
value: impl Into<HeaderValue>,
|
||||||
|
) -> Self {
|
||||||
|
self.headers.insert(name, value.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum FormattedResponse<T> {
|
||||||
|
Json(Json<T>),
|
||||||
|
Yaml(Yaml<T>),
|
||||||
|
Bincode(Bincode<T>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> FormattedResponse<T> {
|
||||||
|
pub fn into_inner(self) -> T {
|
||||||
|
match self {
|
||||||
|
FormattedResponse::Json(inner) => inner.0.data,
|
||||||
|
FormattedResponse::Yaml(inner) => inner.0.data,
|
||||||
|
FormattedResponse::Bincode(inner) => inner.0.data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_header(
|
||||||
|
self,
|
||||||
|
name: impl IntoHeaderName,
|
||||||
|
value: impl Into<HeaderValue>,
|
||||||
|
) -> FormattedResponse<T> {
|
||||||
|
match self {
|
||||||
|
FormattedResponse::Json(inner) => {
|
||||||
|
FormattedResponse::Json(inner.with_header(name, value))
|
||||||
|
}
|
||||||
|
FormattedResponse::Yaml(inner) => {
|
||||||
|
FormattedResponse::Yaml(inner.with_header(name, value))
|
||||||
|
}
|
||||||
|
FormattedResponse::Bincode(inner) => {
|
||||||
|
FormattedResponse::Bincode(inner.with_header(name, value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the `expires` header on the response to the provided expiration.
|
||||||
|
/// Internally it will perform conversions to make sure the value is set in GMT offset,
|
||||||
|
/// e.g. `Expires: Wed, 21 Oct 2015 07:28:00 GMT`
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_expires_header(self, expiration: OffsetDateTime) -> FormattedResponse<T> {
|
||||||
|
// as per RFC-7234 (section 5.3) EXPIRES header has to use value formatted
|
||||||
|
// as defined in RFC-7231 (section 7.1.1.1)
|
||||||
|
// (preferred format (IMF-fixdate) uses RFC-5322 (section 3.3)
|
||||||
|
let formatted = format_rfc5352(expiration);
|
||||||
|
|
||||||
|
// SAFETY: our formatted datetime doesn't contain forbidden characters
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
self.with_header(header::EXPIRES, HeaderValue::try_from(formatted).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Work similarly to `with_expires_header`, but rather than setting explicit expiration value,
|
||||||
|
/// it adds the provided time delta to the current time instead.
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_expires_header_delta(self, expires_in: Duration) -> FormattedResponse<T> {
|
||||||
|
self.with_expires_header(OffsetDateTime::now_utc() + expires_in)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> IntoResponse for FormattedResponse<T>
|
||||||
|
where
|
||||||
|
T: Serialize,
|
||||||
|
{
|
||||||
|
fn into_response(self) -> Response {
|
||||||
|
match self {
|
||||||
|
FormattedResponse::Json(json_response) => json_response.into_response(),
|
||||||
|
FormattedResponse::Yaml(yaml_response) => yaml_response.into_response(),
|
||||||
|
FormattedResponse::Bincode(bincode_response) => bincode_response.into_response(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Serialize, Deserialize, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
pub enum Output {
|
||||||
|
#[default]
|
||||||
|
Json,
|
||||||
|
Yaml,
|
||||||
|
Bincode,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Serialize, Deserialize, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "utoipa", derive(utoipa::IntoParams, utoipa::ToSchema))]
|
||||||
|
#[serde(default)]
|
||||||
|
pub struct OutputParams {
|
||||||
|
pub output: Option<Output>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Output {
|
||||||
|
pub fn to_response<T: Serialize>(self, data: T) -> FormattedResponse<T> {
|
||||||
|
match self {
|
||||||
|
Output::Json => FormattedResponse::Json(Json::from(data)),
|
||||||
|
Output::Yaml => FormattedResponse::Yaml(Yaml::from(data)),
|
||||||
|
Output::Bincode => FormattedResponse::Bincode(Bincode::from(data)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn error_response<E: ToString>(err: E) -> Response {
|
||||||
|
(
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
[(
|
||||||
|
header::CONTENT_TYPE,
|
||||||
|
HeaderValue::from_static(mime::TEXT_PLAIN_UTF_8.as_ref()),
|
||||||
|
)],
|
||||||
|
err.to_string(),
|
||||||
|
)
|
||||||
|
.into_response()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: this hardcoded datetime formatter is valid
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
fn format_rfc5352(datetime: OffsetDateTime) -> String {
|
||||||
|
// the time must be using GMT (UTC) offset
|
||||||
|
let normalised = datetime.to_offset(offset!(UTC));
|
||||||
|
normalised.format(&rfc5322()).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: this function is purposely not made public as it cannot guarantee caller
|
||||||
|
// has correctly ensured their date is using correct GMT offset
|
||||||
|
fn rfc5322() -> &'static [BorrowedFormatItem<'static>] {
|
||||||
|
// D, d M Y H:i:s T
|
||||||
|
format_description!(
|
||||||
|
"[weekday repr:short], [day] [month repr:short] [year] [hour]:[minute]:[second] GMT"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::response::format_rfc5352;
|
||||||
|
use time::macros::datetime;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rfc5322_formatting() {
|
||||||
|
let utc_date = datetime!(2021-08-23 12:13:14 UTC);
|
||||||
|
let non_utc_date = datetime!(2021-08-23 12:13:14 -1);
|
||||||
|
|
||||||
|
assert_eq!("Mon, 23 Aug 2021 12:13:14 GMT", format_rfc5352(utc_date));
|
||||||
|
assert_eq!(
|
||||||
|
"Mon, 23 Aug 2021 13:13:14 GMT",
|
||||||
|
format_rfc5352(non_utc_date)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::response::{error_response, ResponseWrapper};
|
||||||
|
use axum::http::header::IntoHeaderName;
|
||||||
|
use axum::http::{header, HeaderValue};
|
||||||
|
use axum::response::{IntoResponse, Response};
|
||||||
|
use bytes::{BufMut, BytesMut};
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
#[must_use]
|
||||||
|
pub struct Yaml<T>(pub(crate) ResponseWrapper<T>);
|
||||||
|
|
||||||
|
impl<T> From<T> for Yaml<T> {
|
||||||
|
fn from(response: T) -> Self {
|
||||||
|
Yaml(ResponseWrapper::new(response).with_header(
|
||||||
|
header::CONTENT_TYPE,
|
||||||
|
HeaderValue::from_static("application/yaml"),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Yaml<T> {
|
||||||
|
pub(crate) fn with_header(
|
||||||
|
mut self,
|
||||||
|
name: impl IntoHeaderName,
|
||||||
|
value: impl Into<HeaderValue>,
|
||||||
|
) -> Self {
|
||||||
|
self.0.headers.insert(name, value.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> IntoResponse for Yaml<T>
|
||||||
|
where
|
||||||
|
T: Serialize,
|
||||||
|
{
|
||||||
|
// replicates axum's Json
|
||||||
|
fn into_response(self) -> Response {
|
||||||
|
let mut buf = BytesMut::with_capacity(128).writer();
|
||||||
|
match serde_yaml::to_writer(&mut buf, &self.0.data) {
|
||||||
|
Ok(()) => (self.0.headers, buf.into_inner().freeze()).into_response(),
|
||||||
|
Err(err) => error_response(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -116,7 +116,7 @@ nym-bin-common = { path = "../common/bin-common", features = ["output_format", "
|
|||||||
nym-node-tester-utils = { path = "../common/node-tester-utils" }
|
nym-node-tester-utils = { path = "../common/node-tester-utils" }
|
||||||
nym-node-requests = { path = "../nym-node/nym-node-requests" }
|
nym-node-requests = { path = "../nym-node/nym-node-requests" }
|
||||||
nym-types = { path = "../common/types" }
|
nym-types = { path = "../common/types" }
|
||||||
nym-http-api-common = { path = "../common/http-api-common", features = ["utoipa"] }
|
nym-http-api-common = { path = "../common/http-api-common", features = ["utoipa", "output", "middleware"] }
|
||||||
nym-serde-helpers = { path = "../common/serde-helpers", features = ["date"] }
|
nym-serde-helpers = { path = "../common/serde-helpers", features = ["date"] }
|
||||||
nym-ticketbooks-merkle = { path = "../common/ticketbooks-merkle" }
|
nym-ticketbooks-merkle = { path = "../common/ticketbooks-merkle" }
|
||||||
nym-statistics-common = { path = "../common/statistics" }
|
nym-statistics-common = { path = "../common/statistics" }
|
||||||
|
|||||||
@@ -3,8 +3,10 @@
|
|||||||
|
|
||||||
use crate::node_status_api::models::{AxumErrorResponse, AxumResult};
|
use crate::node_status_api::models::{AxumErrorResponse, AxumResult};
|
||||||
use crate::support::http::state::AppState;
|
use crate::support::http::state::AppState;
|
||||||
|
use axum::extract::Query;
|
||||||
use axum::{extract, Router};
|
use axum::{extract, Router};
|
||||||
use nym_api_requests::models::CirculatingSupplyResponse;
|
use nym_api_requests::models::CirculatingSupplyResponse;
|
||||||
|
use nym_http_api_common::{FormattedResponse, OutputParams};
|
||||||
use nym_validator_client::nyxd::Coin;
|
use nym_validator_client::nyxd::Coin;
|
||||||
|
|
||||||
pub(crate) fn circulating_supply_routes() -> Router<AppState> {
|
pub(crate) fn circulating_supply_routes() -> Router<AppState> {
|
||||||
@@ -25,18 +27,26 @@ pub(crate) fn circulating_supply_routes() -> Router<AppState> {
|
|||||||
get,
|
get,
|
||||||
path = "/v1/circulating-supply",
|
path = "/v1/circulating-supply",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = CirculatingSupplyResponse)
|
(status = 200, content(
|
||||||
)
|
(CirculatingSupplyResponse = "application/json"),
|
||||||
|
(CirculatingSupplyResponse = "application/yaml"),
|
||||||
|
(CirculatingSupplyResponse = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
async fn get_full_circulating_supply(
|
async fn get_full_circulating_supply(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
extract::State(state): extract::State<AppState>,
|
extract::State(state): extract::State<AppState>,
|
||||||
) -> AxumResult<axum::Json<CirculatingSupplyResponse>> {
|
) -> AxumResult<FormattedResponse<CirculatingSupplyResponse>> {
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
match state
|
match state
|
||||||
.circulating_supply_cache()
|
.circulating_supply_cache()
|
||||||
.get_circulating_supply()
|
.get_circulating_supply()
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Some(value) => Ok(value.into()),
|
Some(value) => Ok(output.to_response(value)),
|
||||||
None => Err(AxumErrorResponse::internal_msg("unavailable")),
|
None => Err(AxumErrorResponse::internal_msg("unavailable")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -46,12 +56,19 @@ async fn get_full_circulating_supply(
|
|||||||
get,
|
get,
|
||||||
path = "/v1/circulating-supply/total-supply-value",
|
path = "/v1/circulating-supply/total-supply-value",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = [f64])
|
(status = 200, content(
|
||||||
)
|
(f64 = "application/json"),
|
||||||
|
(f64 = "application/yaml"),
|
||||||
|
(f64 = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
async fn get_total_supply(
|
async fn get_total_supply(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
extract::State(state): extract::State<AppState>,
|
extract::State(state): extract::State<AppState>,
|
||||||
) -> AxumResult<axum::Json<f64>> {
|
) -> AxumResult<FormattedResponse<f64>> {
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
let full_circulating_supply = match state
|
let full_circulating_supply = match state
|
||||||
.circulating_supply_cache()
|
.circulating_supply_cache()
|
||||||
.get_circulating_supply()
|
.get_circulating_supply()
|
||||||
@@ -61,7 +78,9 @@ async fn get_total_supply(
|
|||||||
None => return Err(AxumErrorResponse::internal_msg("unavailable")),
|
None => return Err(AxumErrorResponse::internal_msg("unavailable")),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(unym_coin_to_float_unym(full_circulating_supply.total_supply.into()).into())
|
let total_supply = unym_coin_to_float_unym(full_circulating_supply.total_supply.into());
|
||||||
|
|
||||||
|
Ok(output.to_response(total_supply))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -69,12 +88,20 @@ async fn get_total_supply(
|
|||||||
get,
|
get,
|
||||||
path = "/v1/circulating-supply/circulating-supply-value",
|
path = "/v1/circulating-supply/circulating-supply-value",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = [f64])
|
(status = 200, content(
|
||||||
)
|
(f64 = "application/json"),
|
||||||
|
(f64 = "application/yaml"),
|
||||||
|
(f64 = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
async fn get_circulating_supply(
|
async fn get_circulating_supply(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
extract::State(state): extract::State<AppState>,
|
extract::State(state): extract::State<AppState>,
|
||||||
) -> AxumResult<axum::Json<f64>> {
|
) -> AxumResult<FormattedResponse<f64>> {
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
let full_circulating_supply = match state
|
let full_circulating_supply = match state
|
||||||
.circulating_supply_cache()
|
.circulating_supply_cache()
|
||||||
.get_circulating_supply()
|
.get_circulating_supply()
|
||||||
@@ -84,7 +111,10 @@ async fn get_circulating_supply(
|
|||||||
None => return Err(AxumErrorResponse::internal_msg("unavailable")),
|
None => return Err(AxumErrorResponse::internal_msg("unavailable")),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(unym_coin_to_float_unym(full_circulating_supply.circulating_supply.into()).into())
|
let circulating_supply =
|
||||||
|
unym_coin_to_float_unym(full_circulating_supply.circulating_supply.into());
|
||||||
|
|
||||||
|
Ok(output.to_response(circulating_supply))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: this is not the best place to put it, it should be more centralised,
|
// TODO: this is not the best place to put it, it should be more centralised,
|
||||||
|
|||||||
@@ -7,12 +7,13 @@ use crate::ecash::state::EcashState;
|
|||||||
use crate::node_status_api::models::AxumResult;
|
use crate::node_status_api::models::AxumResult;
|
||||||
use crate::support::http::state::AppState;
|
use crate::support::http::state::AppState;
|
||||||
use axum::extract::{Query, State};
|
use axum::extract::{Query, State};
|
||||||
use axum::{Json, Router};
|
use axum::Router;
|
||||||
use nym_api_requests::ecash::models::{
|
use nym_api_requests::ecash::models::{
|
||||||
AggregatedCoinIndicesSignatureResponse, AggregatedExpirationDateSignatureResponse,
|
AggregatedCoinIndicesSignatureResponse, AggregatedExpirationDateSignatureResponse,
|
||||||
};
|
};
|
||||||
use nym_api_requests::ecash::VerificationKeyResponse;
|
use nym_api_requests::ecash::VerificationKeyResponse;
|
||||||
use nym_ecash_time::{cred_exp_date, EcashTime};
|
use nym_ecash_time::{cred_exp_date, EcashTime};
|
||||||
|
use nym_http_api_common::{FormattedResponse, Output};
|
||||||
use nym_validator_client::nym_api::rfc_3339_date;
|
use nym_validator_client::nym_api::rfc_3339_date;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -45,26 +46,32 @@ pub(crate) fn aggregation_routes() -> Router<AppState> {
|
|||||||
),
|
),
|
||||||
path = "/v1/ecash/master-verification-key",
|
path = "/v1/ecash/master-verification-key",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = VerificationKeyResponse)
|
(status = 200, content(
|
||||||
)
|
(VerificationKeyResponse = "application/json"),
|
||||||
|
(VerificationKeyResponse = "application/yaml"),
|
||||||
|
(VerificationKeyResponse = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
)]
|
)]
|
||||||
async fn master_verification_key(
|
async fn master_verification_key(
|
||||||
State(state): State<Arc<EcashState>>,
|
State(state): State<Arc<EcashState>>,
|
||||||
Query(EpochIdParam { epoch_id }): Query<EpochIdParam>,
|
Query(EpochIdParam { epoch_id, output }): Query<EpochIdParam>,
|
||||||
) -> AxumResult<Json<VerificationKeyResponse>> {
|
) -> AxumResult<FormattedResponse<VerificationKeyResponse>> {
|
||||||
trace!("aggregated_verification_key request");
|
trace!("aggregated_verification_key request");
|
||||||
|
let output = output.unwrap_or_default();
|
||||||
|
|
||||||
// see if we're not in the middle of new dkg
|
// see if we're not in the middle of new dkg
|
||||||
state.ensure_dkg_not_in_progress().await?;
|
state.ensure_dkg_not_in_progress().await?;
|
||||||
|
|
||||||
let key = state.master_verification_key(epoch_id).await?;
|
let key = state.master_verification_key(epoch_id).await?;
|
||||||
|
|
||||||
Ok(Json(VerificationKeyResponse::new(key.clone())))
|
Ok(output.to_response(VerificationKeyResponse::new(key.clone())))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, IntoParams)]
|
#[derive(Deserialize, IntoParams)]
|
||||||
struct ExpirationDateParam {
|
struct ExpirationDateParam {
|
||||||
expiration_date: Option<String>,
|
expiration_date: Option<String>,
|
||||||
|
output: Option<Output>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -75,14 +82,22 @@ struct ExpirationDateParam {
|
|||||||
),
|
),
|
||||||
path = "/v1/ecash/aggregated-expiration-date-signatures",
|
path = "/v1/ecash/aggregated-expiration-date-signatures",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = AggregatedExpirationDateSignatureResponse)
|
(status = 200, content(
|
||||||
)
|
(AggregatedExpirationDateSignatureResponse = "application/json"),
|
||||||
|
(AggregatedExpirationDateSignatureResponse = "application/yaml"),
|
||||||
|
(AggregatedExpirationDateSignatureResponse = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
)]
|
)]
|
||||||
async fn expiration_date_signatures(
|
async fn expiration_date_signatures(
|
||||||
State(state): State<Arc<EcashState>>,
|
State(state): State<Arc<EcashState>>,
|
||||||
Query(ExpirationDateParam { expiration_date }): Query<ExpirationDateParam>,
|
Query(ExpirationDateParam {
|
||||||
) -> AxumResult<Json<AggregatedExpirationDateSignatureResponse>> {
|
expiration_date,
|
||||||
|
output,
|
||||||
|
}): Query<ExpirationDateParam>,
|
||||||
|
) -> AxumResult<FormattedResponse<AggregatedExpirationDateSignatureResponse>> {
|
||||||
trace!("aggregated_expiration_date_signatures request");
|
trace!("aggregated_expiration_date_signatures request");
|
||||||
|
let output = output.unwrap_or_default();
|
||||||
|
|
||||||
let expiration_date = match expiration_date {
|
let expiration_date = match expiration_date {
|
||||||
None => cred_exp_date().ecash_date(),
|
None => cred_exp_date().ecash_date(),
|
||||||
@@ -97,11 +112,13 @@ async fn expiration_date_signatures(
|
|||||||
.master_expiration_date_signatures(expiration_date)
|
.master_expiration_date_signatures(expiration_date)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(Json(AggregatedExpirationDateSignatureResponse {
|
Ok(
|
||||||
epoch_id: expiration_date_signatures.epoch_id,
|
output.to_response(AggregatedExpirationDateSignatureResponse {
|
||||||
expiration_date,
|
epoch_id: expiration_date_signatures.epoch_id,
|
||||||
signatures: expiration_date_signatures.signatures.clone(),
|
expiration_date,
|
||||||
}))
|
signatures: expiration_date_signatures.signatures.clone(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -112,21 +129,26 @@ async fn expiration_date_signatures(
|
|||||||
),
|
),
|
||||||
path = "/v1/ecash/aggregated-coin-indices-signatures",
|
path = "/v1/ecash/aggregated-coin-indices-signatures",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = AggregatedCoinIndicesSignatureResponse)
|
(status = 200, content(
|
||||||
)
|
(AggregatedCoinIndicesSignatureResponse = "application/json"),
|
||||||
|
(AggregatedCoinIndicesSignatureResponse = "application/yaml"),
|
||||||
|
(AggregatedCoinIndicesSignatureResponse = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
)]
|
)]
|
||||||
async fn coin_indices_signatures(
|
async fn coin_indices_signatures(
|
||||||
Query(EpochIdParam { epoch_id }): Query<EpochIdParam>,
|
Query(EpochIdParam { epoch_id, output }): Query<EpochIdParam>,
|
||||||
State(state): State<Arc<EcashState>>,
|
State(state): State<Arc<EcashState>>,
|
||||||
) -> AxumResult<Json<AggregatedCoinIndicesSignatureResponse>> {
|
) -> AxumResult<FormattedResponse<AggregatedCoinIndicesSignatureResponse>> {
|
||||||
trace!("aggregated_coin_indices_signatures request");
|
trace!("aggregated_coin_indices_signatures request");
|
||||||
|
|
||||||
|
let output = output.unwrap_or_default();
|
||||||
// see if we're not in the middle of new dkg
|
// see if we're not in the middle of new dkg
|
||||||
state.ensure_dkg_not_in_progress().await?;
|
state.ensure_dkg_not_in_progress().await?;
|
||||||
|
|
||||||
let coin_indices_signatures = state.master_coin_index_signatures(epoch_id).await?;
|
let coin_indices_signatures = state.master_coin_index_signatures(epoch_id).await?;
|
||||||
|
|
||||||
Ok(Json(AggregatedCoinIndicesSignatureResponse {
|
Ok(output.to_response(AggregatedCoinIndicesSignatureResponse {
|
||||||
epoch_id: coin_indices_signatures.epoch_id,
|
epoch_id: coin_indices_signatures.epoch_id,
|
||||||
signatures: coin_indices_signatures.signatures.clone(),
|
signatures: coin_indices_signatures.signatures.clone(),
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
use nym_http_api_common::Output;
|
||||||
|
|
||||||
#[derive(serde::Deserialize, utoipa::IntoParams)]
|
#[derive(serde::Deserialize, utoipa::IntoParams)]
|
||||||
pub(super) struct EpochIdParam {
|
pub(super) struct EpochIdParam {
|
||||||
pub(super) epoch_id: Option<u64>,
|
pub(super) epoch_id: Option<u64>,
|
||||||
|
pub(super) output: Option<Output>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use nym_api_requests::ecash::models::{
|
|||||||
IssuedTicketbooksForCountResponse, IssuedTicketbooksForResponse,
|
IssuedTicketbooksForCountResponse, IssuedTicketbooksForResponse,
|
||||||
IssuedTicketbooksOnCountResponse, SignableMessageBody,
|
IssuedTicketbooksOnCountResponse, SignableMessageBody,
|
||||||
};
|
};
|
||||||
|
use nym_http_api_common::{FormattedResponse, OutputParams};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
@@ -80,15 +81,20 @@ pub(crate) struct IssuanceDatePathParam {
|
|||||||
path = "/issued-ticketbooks-count",
|
path = "/issued-ticketbooks-count",
|
||||||
context_path = "/v1/ecash",
|
context_path = "/v1/ecash",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = IssuedTicketbooksCountResponse),
|
(status = 200, content(
|
||||||
|
(IssuedTicketbooksCountResponse = "application/json"),
|
||||||
|
(IssuedTicketbooksCountResponse = "application/yaml"),
|
||||||
|
(IssuedTicketbooksCountResponse = "application/bincode")
|
||||||
|
)),
|
||||||
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
|
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
|
||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
async fn issued_ticketbooks_count(
|
async fn issued_ticketbooks_count(
|
||||||
Query(pagination): Query<PaginationRequest>,
|
Query(pagination): Query<PaginationRequest>,
|
||||||
State(state): State<Arc<EcashState>>,
|
State(state): State<Arc<EcashState>>,
|
||||||
) -> AxumResult<Json<IssuedTicketbooksCountResponse>> {
|
) -> AxumResult<FormattedResponse<IssuedTicketbooksCountResponse>> {
|
||||||
state.ensure_signer().await?;
|
state.ensure_signer().await?;
|
||||||
|
let output = pagination.output.unwrap_or_default();
|
||||||
|
|
||||||
let page = pagination.page.unwrap_or_default();
|
let page = pagination.page.unwrap_or_default();
|
||||||
let per_page = min(
|
let per_page = min(
|
||||||
@@ -98,9 +104,7 @@ async fn issued_ticketbooks_count(
|
|||||||
MAX_ISSUANCE_COUNT_PAGE_SIZE,
|
MAX_ISSUANCE_COUNT_PAGE_SIZE,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(Json(
|
Ok(output.to_response(state.get_issued_ticketbooks_count(page, per_page).await?))
|
||||||
state.get_issued_ticketbooks_count(page, per_page).await?,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns number of issued ticketbooks for particular expiration date.
|
/// Returns number of issued ticketbooks for particular expiration date.
|
||||||
@@ -109,22 +113,28 @@ async fn issued_ticketbooks_count(
|
|||||||
tag = "Ecash",
|
tag = "Ecash",
|
||||||
get,
|
get,
|
||||||
params(
|
params(
|
||||||
ExpirationDatePathParam
|
ExpirationDatePathParam, OutputParams
|
||||||
),
|
),
|
||||||
path = "/issued-ticketbooks-for-count/{expiration_date}",
|
path = "/issued-ticketbooks-for-count/{expiration_date}",
|
||||||
context_path = "/v1/ecash",
|
context_path = "/v1/ecash",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = IssuedTicketbooksForCountResponse),
|
(status = 200, content(
|
||||||
|
(IssuedTicketbooksForCountResponse = "application/json"),
|
||||||
|
(IssuedTicketbooksForCountResponse = "application/yaml"),
|
||||||
|
(IssuedTicketbooksForCountResponse = "application/bincode")
|
||||||
|
)),
|
||||||
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
|
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
|
||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
async fn issued_ticketbooks_for_count(
|
async fn issued_ticketbooks_for_count(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
Path(ExpirationDatePathParam { expiration_date }): Path<ExpirationDatePathParam>,
|
Path(ExpirationDatePathParam { expiration_date }): Path<ExpirationDatePathParam>,
|
||||||
State(state): State<Arc<EcashState>>,
|
State(state): State<Arc<EcashState>>,
|
||||||
) -> AxumResult<Json<IssuedTicketbooksForCountResponse>> {
|
) -> AxumResult<FormattedResponse<IssuedTicketbooksForCountResponse>> {
|
||||||
state.ensure_signer().await?;
|
state.ensure_signer().await?;
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
Ok(Json(
|
Ok(output.to_response(
|
||||||
state
|
state
|
||||||
.get_issued_ticketbooks_for_count(expiration_date)
|
.get_issued_ticketbooks_for_count(expiration_date)
|
||||||
.await?,
|
.await?,
|
||||||
@@ -137,46 +147,56 @@ async fn issued_ticketbooks_for_count(
|
|||||||
tag = "Ecash",
|
tag = "Ecash",
|
||||||
get,
|
get,
|
||||||
params(
|
params(
|
||||||
IssuanceDatePathParam
|
IssuanceDatePathParam, OutputParams
|
||||||
),
|
),
|
||||||
path = "/issued-ticketbooks-on-count/{issuance_date}",
|
path = "/issued-ticketbooks-on-count/{issuance_date}",
|
||||||
context_path = "/v1/ecash",
|
context_path = "/v1/ecash",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = IssuedTicketbooksOnCountResponse),
|
(status = 200, content(
|
||||||
|
(IssuedTicketbooksOnCountResponse = "application/json"),
|
||||||
|
(IssuedTicketbooksOnCountResponse = "application/yaml"),
|
||||||
|
(IssuedTicketbooksOnCountResponse = "application/bincode")
|
||||||
|
)),
|
||||||
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
|
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
|
||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
async fn issued_ticketbooks_on_count(
|
async fn issued_ticketbooks_on_count(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
Path(IssuanceDatePathParam { issuance_date }): Path<IssuanceDatePathParam>,
|
Path(IssuanceDatePathParam { issuance_date }): Path<IssuanceDatePathParam>,
|
||||||
State(state): State<Arc<EcashState>>,
|
State(state): State<Arc<EcashState>>,
|
||||||
) -> AxumResult<Json<IssuedTicketbooksOnCountResponse>> {
|
) -> AxumResult<FormattedResponse<IssuedTicketbooksOnCountResponse>> {
|
||||||
state.ensure_signer().await?;
|
state.ensure_signer().await?;
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
Ok(Json(
|
Ok(output.to_response(state.get_issued_ticketbooks_on_count(issuance_date).await?))
|
||||||
state.get_issued_ticketbooks_on_count(issuance_date).await?,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
tag = "Ecash",
|
tag = "Ecash",
|
||||||
get,
|
get,
|
||||||
params(
|
params(
|
||||||
ExpirationDatePathParam
|
ExpirationDatePathParam, OutputParams
|
||||||
),
|
),
|
||||||
path = "/issued-ticketbooks-for/{expiration_date}",
|
path = "/issued-ticketbooks-for/{expiration_date}",
|
||||||
context_path = "/v1/ecash",
|
context_path = "/v1/ecash",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = IssuedTicketbooksForResponse),
|
(status = 200, content(
|
||||||
|
(IssuedTicketbooksForResponse = "application/json"),
|
||||||
|
(IssuedTicketbooksForResponse = "application/yaml"),
|
||||||
|
(IssuedTicketbooksForResponse = "application/bincode")
|
||||||
|
)),
|
||||||
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
|
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
|
||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
async fn issued_ticketbooks_for(
|
async fn issued_ticketbooks_for(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<Arc<EcashState>>,
|
State(state): State<Arc<EcashState>>,
|
||||||
Path(ExpirationDatePathParam { expiration_date }): Path<ExpirationDatePathParam>,
|
Path(ExpirationDatePathParam { expiration_date }): Path<ExpirationDatePathParam>,
|
||||||
) -> AxumResult<Json<IssuedTicketbooksForResponse>> {
|
) -> AxumResult<FormattedResponse<IssuedTicketbooksForResponse>> {
|
||||||
state.ensure_signer().await?;
|
state.ensure_signer().await?;
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
Ok(Json(
|
Ok(output.to_response(
|
||||||
state
|
state
|
||||||
.get_issued_ticketbooks_deposits_on(expiration_date)
|
.get_issued_ticketbooks_deposits_on(expiration_date)
|
||||||
.await?
|
.await?
|
||||||
@@ -191,17 +211,24 @@ async fn issued_ticketbooks_for(
|
|||||||
path = "/issued-ticketbooks-challenge-commitment",
|
path = "/issued-ticketbooks-challenge-commitment",
|
||||||
context_path = "/v1/ecash",
|
context_path = "/v1/ecash",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = IssuedTicketbooksChallengeCommitmentResponse),
|
(status = 200, content(
|
||||||
|
(IssuedTicketbooksChallengeCommitmentResponse = "application/json"),
|
||||||
|
(IssuedTicketbooksChallengeCommitmentResponse = "application/yaml"),
|
||||||
|
(IssuedTicketbooksChallengeCommitmentResponse = "application/bincode")
|
||||||
|
)),
|
||||||
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
|
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
|
||||||
)
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
async fn issued_ticketbooks_challenge_commitment(
|
async fn issued_ticketbooks_challenge_commitment(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<Arc<EcashState>>,
|
State(state): State<Arc<EcashState>>,
|
||||||
Json(request): Json<IssuedTicketbooksChallengeCommitmentRequest>,
|
Json(request): Json<IssuedTicketbooksChallengeCommitmentRequest>,
|
||||||
) -> AxumResult<Json<IssuedTicketbooksChallengeCommitmentResponse>> {
|
) -> AxumResult<FormattedResponse<IssuedTicketbooksChallengeCommitmentResponse>> {
|
||||||
state.ensure_signer().await?;
|
state.ensure_signer().await?;
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
Ok(Json(
|
Ok(output.to_response(
|
||||||
state
|
state
|
||||||
.get_issued_ticketbooks_challenge_commitment(request)
|
.get_issued_ticketbooks_challenge_commitment(request)
|
||||||
.await?
|
.await?
|
||||||
@@ -216,17 +243,24 @@ async fn issued_ticketbooks_challenge_commitment(
|
|||||||
path = "/issued-ticketbooks-data",
|
path = "/issued-ticketbooks-data",
|
||||||
context_path = "/v1/ecash",
|
context_path = "/v1/ecash",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = IssuedTicketbooksDataResponse),
|
(status = 200, content(
|
||||||
|
(IssuedTicketbooksDataResponse = "application/json"),
|
||||||
|
(IssuedTicketbooksDataResponse = "application/yaml"),
|
||||||
|
(IssuedTicketbooksDataResponse = "application/bincode")
|
||||||
|
)),
|
||||||
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
|
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
|
||||||
)
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
async fn issued_ticketbooks_data(
|
async fn issued_ticketbooks_data(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<Arc<EcashState>>,
|
State(state): State<Arc<EcashState>>,
|
||||||
Json(request): Json<IssuedTicketbooksDataRequest>,
|
Json(request): Json<IssuedTicketbooksDataRequest>,
|
||||||
) -> AxumResult<Json<IssuedTicketbooksDataResponse>> {
|
) -> AxumResult<FormattedResponse<IssuedTicketbooksDataResponse>> {
|
||||||
state.ensure_signer().await?;
|
state.ensure_signer().await?;
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
Ok(Json(
|
Ok(output.to_response(
|
||||||
state
|
state
|
||||||
.get_issued_ticketbooks_data(request)
|
.get_issued_ticketbooks_data(request)
|
||||||
.await?
|
.await?
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ use nym_api_requests::ecash::{
|
|||||||
PartialExpirationDateSignatureResponse,
|
PartialExpirationDateSignatureResponse,
|
||||||
};
|
};
|
||||||
use nym_ecash_time::{cred_exp_date, EcashTime};
|
use nym_ecash_time::{cred_exp_date, EcashTime};
|
||||||
|
use nym_http_api_common::{FormattedResponse, Output, OutputParams};
|
||||||
use nym_validator_client::nym_api::rfc_3339_date;
|
use nym_validator_client::nym_api::rfc_3339_date;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
@@ -41,16 +42,22 @@ pub(crate) fn partial_signing_routes() -> Router<AppState> {
|
|||||||
request_body = BlindSignRequestBody,
|
request_body = BlindSignRequestBody,
|
||||||
path = "/v1/ecash/blind-sign",
|
path = "/v1/ecash/blind-sign",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = BlindedSignatureResponse),
|
(status = 200, content(
|
||||||
|
(BlindedSignatureResponse = "application/json"),
|
||||||
|
(BlindedSignatureResponse = "application/yaml"),
|
||||||
|
(BlindedSignatureResponse = "application/bincode")
|
||||||
|
)),
|
||||||
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
|
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
|
||||||
|
),
|
||||||
)
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
async fn post_blind_sign(
|
async fn post_blind_sign(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<Arc<EcashState>>,
|
State(state): State<Arc<EcashState>>,
|
||||||
Json(blind_sign_request_body): Json<BlindSignRequestBody>,
|
Json(blind_sign_request_body): Json<BlindSignRequestBody>,
|
||||||
) -> AxumResult<Json<BlindedSignatureResponse>> {
|
) -> AxumResult<FormattedResponse<BlindedSignatureResponse>> {
|
||||||
state.ensure_signer().await?;
|
state.ensure_signer().await?;
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
debug!("Received blind sign request");
|
debug!("Received blind sign request");
|
||||||
trace!("body: {:?}", blind_sign_request_body);
|
trace!("body: {:?}", blind_sign_request_body);
|
||||||
@@ -73,7 +80,7 @@ async fn post_blind_sign(
|
|||||||
"checking if we have already issued credential for this deposit (deposit_id: {deposit_id})",
|
"checking if we have already issued credential for this deposit (deposit_id: {deposit_id})",
|
||||||
);
|
);
|
||||||
if let Some(blinded_signature) = state.already_issued(deposit_id).await? {
|
if let Some(blinded_signature) = state.already_issued(deposit_id).await? {
|
||||||
return Ok(Json(BlindedSignatureResponse { blinded_signature }));
|
return Ok(output.to_response(BlindedSignatureResponse { blinded_signature }));
|
||||||
}
|
}
|
||||||
|
|
||||||
//check if account was blacklisted
|
//check if account was blacklisted
|
||||||
@@ -101,12 +108,13 @@ async fn post_blind_sign(
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// finally return the credential to the client
|
// finally return the credential to the client
|
||||||
Ok(Json(BlindedSignatureResponse { blinded_signature }))
|
Ok(output.to_response(BlindedSignatureResponse { blinded_signature }))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, IntoParams)]
|
#[derive(Deserialize, IntoParams)]
|
||||||
struct ExpirationDateParam {
|
struct ExpirationDateParam {
|
||||||
expiration_date: Option<String>,
|
expiration_date: Option<String>,
|
||||||
|
output: Option<Output>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -117,15 +125,23 @@ struct ExpirationDateParam {
|
|||||||
),
|
),
|
||||||
path = "/v1/ecash/partial-expiration-date-signatures",
|
path = "/v1/ecash/partial-expiration-date-signatures",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = PartialExpirationDateSignatureResponse),
|
(status = 200, content(
|
||||||
|
(PartialExpirationDateSignatureResponse = "application/json"),
|
||||||
|
(PartialExpirationDateSignatureResponse = "application/yaml"),
|
||||||
|
(PartialExpirationDateSignatureResponse = "application/bincode")
|
||||||
|
)),
|
||||||
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
|
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
|
||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
async fn partial_expiration_date_signatures(
|
async fn partial_expiration_date_signatures(
|
||||||
State(state): State<Arc<EcashState>>,
|
State(state): State<Arc<EcashState>>,
|
||||||
Query(ExpirationDateParam { expiration_date }): Query<ExpirationDateParam>,
|
Query(ExpirationDateParam {
|
||||||
) -> AxumResult<Json<PartialExpirationDateSignatureResponse>> {
|
expiration_date,
|
||||||
|
output,
|
||||||
|
}): Query<ExpirationDateParam>,
|
||||||
|
) -> AxumResult<FormattedResponse<PartialExpirationDateSignatureResponse>> {
|
||||||
state.ensure_signer().await?;
|
state.ensure_signer().await?;
|
||||||
|
let output = output.unwrap_or_default();
|
||||||
|
|
||||||
let expiration_date = match expiration_date {
|
let expiration_date = match expiration_date {
|
||||||
None => cred_exp_date().ecash_date(),
|
None => cred_exp_date().ecash_date(),
|
||||||
@@ -140,7 +156,7 @@ async fn partial_expiration_date_signatures(
|
|||||||
.partial_expiration_date_signatures(expiration_date)
|
.partial_expiration_date_signatures(expiration_date)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(Json(PartialExpirationDateSignatureResponse {
|
Ok(output.to_response(PartialExpirationDateSignatureResponse {
|
||||||
epoch_id: expiration_date_signatures.epoch_id,
|
epoch_id: expiration_date_signatures.epoch_id,
|
||||||
expiration_date,
|
expiration_date,
|
||||||
signatures: expiration_date_signatures.signatures.clone(),
|
signatures: expiration_date_signatures.signatures.clone(),
|
||||||
@@ -155,14 +171,18 @@ async fn partial_expiration_date_signatures(
|
|||||||
),
|
),
|
||||||
path = "/v1/ecash/partial-coin-indices-signatures",
|
path = "/v1/ecash/partial-coin-indices-signatures",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = PartialExpirationDateSignatureResponse),
|
(status = 200, content(
|
||||||
|
(PartialCoinIndicesSignatureResponse = "application/json"),
|
||||||
|
(PartialCoinIndicesSignatureResponse = "application/yaml"),
|
||||||
|
(PartialCoinIndicesSignatureResponse = "application/bincode")
|
||||||
|
)),
|
||||||
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
|
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
|
||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
async fn partial_coin_indices_signatures(
|
async fn partial_coin_indices_signatures(
|
||||||
State(state): State<Arc<EcashState>>,
|
State(state): State<Arc<EcashState>>,
|
||||||
Query(EpochIdParam { epoch_id }): Query<EpochIdParam>,
|
Query(EpochIdParam { epoch_id, output }): Query<EpochIdParam>,
|
||||||
) -> AxumResult<Json<PartialCoinIndicesSignatureResponse>> {
|
) -> AxumResult<FormattedResponse<PartialCoinIndicesSignatureResponse>> {
|
||||||
state.ensure_signer().await?;
|
state.ensure_signer().await?;
|
||||||
|
|
||||||
// see if we're not in the middle of new dkg
|
// see if we're not in the middle of new dkg
|
||||||
@@ -170,8 +190,10 @@ async fn partial_coin_indices_signatures(
|
|||||||
|
|
||||||
let coin_indices_signatures = state.partial_coin_index_signatures(epoch_id).await?;
|
let coin_indices_signatures = state.partial_coin_index_signatures(epoch_id).await?;
|
||||||
|
|
||||||
Ok(Json(PartialCoinIndicesSignatureResponse {
|
Ok(output
|
||||||
epoch_id: coin_indices_signatures.epoch_id,
|
.unwrap_or_default()
|
||||||
signatures: coin_indices_signatures.signatures.clone(),
|
.to_response(PartialCoinIndicesSignatureResponse {
|
||||||
}))
|
epoch_id: coin_indices_signatures.epoch_id,
|
||||||
|
signatures: coin_indices_signatures.signatures.clone(),
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use crate::ecash::error::EcashError;
|
|||||||
use crate::ecash::state::EcashState;
|
use crate::ecash::state::EcashState;
|
||||||
use crate::node_status_api::models::{AxumErrorResponse, AxumResult};
|
use crate::node_status_api::models::{AxumErrorResponse, AxumResult};
|
||||||
use crate::support::http::state::AppState;
|
use crate::support::http::state::AppState;
|
||||||
use axum::extract::State;
|
use axum::extract::{Query, State};
|
||||||
use axum::http::StatusCode;
|
use axum::http::StatusCode;
|
||||||
use axum::{Json, Router};
|
use axum::{Json, Router};
|
||||||
use nym_api_requests::constants::MIN_BATCH_REDEMPTION_DELAY;
|
use nym_api_requests::constants::MIN_BATCH_REDEMPTION_DELAY;
|
||||||
@@ -15,6 +15,7 @@ use nym_api_requests::ecash::models::{
|
|||||||
};
|
};
|
||||||
use nym_compact_ecash::identify::IdentifyResult;
|
use nym_compact_ecash::identify::IdentifyResult;
|
||||||
use nym_ecash_time::EcashTime;
|
use nym_ecash_time::EcashTime;
|
||||||
|
use nym_http_api_common::{FormattedResponse, Output, OutputParams};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -39,9 +40,10 @@ pub(crate) fn spending_routes() -> Router<AppState> {
|
|||||||
const ONE_AM: Time = time!(1:00);
|
const ONE_AM: Time = time!(1:00);
|
||||||
|
|
||||||
fn reject_ticket(
|
fn reject_ticket(
|
||||||
|
output: Output,
|
||||||
reason: EcashTicketVerificationRejection,
|
reason: EcashTicketVerificationRejection,
|
||||||
) -> AxumResult<Json<EcashTicketVerificationResponse>> {
|
) -> AxumResult<FormattedResponse<EcashTicketVerificationResponse>> {
|
||||||
Ok(Json(EcashTicketVerificationResponse::reject(reason)))
|
Ok(output.to_response(EcashTicketVerificationResponse::reject(reason)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: optimise it; for now it's just dummy split of the original `verify_offline_credential`
|
// TODO: optimise it; for now it's just dummy split of the original `verify_offline_credential`
|
||||||
@@ -52,23 +54,29 @@ fn reject_ticket(
|
|||||||
request_body = VerifyEcashTicketBody,
|
request_body = VerifyEcashTicketBody,
|
||||||
path = "/v1/ecash/verify-ecash-ticket",
|
path = "/v1/ecash/verify-ecash-ticket",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = EcashTicketVerificationResponse),
|
(status = 200, content(
|
||||||
|
(EcashTicketVerificationResponse = "application/json"),
|
||||||
|
(EcashTicketVerificationResponse = "application/yaml"),
|
||||||
|
(EcashTicketVerificationResponse = "application/bincode")
|
||||||
|
)),
|
||||||
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
|
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
|
||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
async fn verify_ticket(
|
async fn verify_ticket(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<Arc<EcashState>>,
|
State(state): State<Arc<EcashState>>,
|
||||||
// TODO in the future: make it send binary data rather than json
|
// TODO in the future: make it send binary data rather than json
|
||||||
Json(verify_ticket_body): Json<VerifyEcashTicketBody>,
|
Json(verify_ticket_body): Json<VerifyEcashTicketBody>,
|
||||||
) -> AxumResult<Json<EcashTicketVerificationResponse>> {
|
) -> AxumResult<FormattedResponse<EcashTicketVerificationResponse>> {
|
||||||
state.ensure_signer().await?;
|
state.ensure_signer().await?;
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
let credential_data = &verify_ticket_body.credential;
|
let credential_data = &verify_ticket_body.credential;
|
||||||
let gateway_cosmos_addr = &verify_ticket_body.gateway_cosmos_addr;
|
let gateway_cosmos_addr = &verify_ticket_body.gateway_cosmos_addr;
|
||||||
|
|
||||||
// easy check: is there only a single payment attached?
|
// easy check: is there only a single payment attached?
|
||||||
if credential_data.payment.spend_value != 1 {
|
if credential_data.payment.spend_value != 1 {
|
||||||
return reject_ticket(EcashTicketVerificationRejection::MultipleTickets);
|
return reject_ticket(output, EcashTicketVerificationRejection::MultipleTickets);
|
||||||
}
|
}
|
||||||
|
|
||||||
let sn = &credential_data.encoded_serial_number();
|
let sn = &credential_data.encoded_serial_number();
|
||||||
@@ -83,11 +91,14 @@ async fn verify_ticket(
|
|||||||
|
|
||||||
// only accept yesterday date if we're near the day transition, i.e. before 1AM UTC
|
// only accept yesterday date if we're near the day transition, i.e. before 1AM UTC
|
||||||
if spend_date != today_ecash && now.time() > ONE_AM && spend_date != yesterday_ecash {
|
if spend_date != today_ecash && now.time() > ONE_AM && spend_date != yesterday_ecash {
|
||||||
return reject_ticket(EcashTicketVerificationRejection::InvalidSpentDate {
|
return reject_ticket(
|
||||||
today: today_ecash,
|
output,
|
||||||
yesterday: yesterday_ecash,
|
EcashTicketVerificationRejection::InvalidSpentDate {
|
||||||
received: spend_date,
|
today: today_ecash,
|
||||||
});
|
yesterday: yesterday_ecash,
|
||||||
|
received: spend_date,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// actual double spend detection with storage
|
// actual double spend detection with storage
|
||||||
@@ -101,7 +112,7 @@ async fn verify_ticket(
|
|||||||
IdentifyResult::NotADuplicatePayment => {} //SW NOTE This should never happen, quick message?
|
IdentifyResult::NotADuplicatePayment => {} //SW NOTE This should never happen, quick message?
|
||||||
IdentifyResult::DuplicatePayInfo(_) => {
|
IdentifyResult::DuplicatePayInfo(_) => {
|
||||||
warn!("Identical payInfo");
|
warn!("Identical payInfo");
|
||||||
return reject_ticket(EcashTicketVerificationRejection::ReplayedTicket);
|
return reject_ticket(output, EcashTicketVerificationRejection::ReplayedTicket);
|
||||||
}
|
}
|
||||||
IdentifyResult::DoubleSpendingPublicKeys(pub_key) => {
|
IdentifyResult::DoubleSpendingPublicKeys(pub_key) => {
|
||||||
//Actual double spending
|
//Actual double spending
|
||||||
@@ -110,7 +121,7 @@ async fn verify_ticket(
|
|||||||
pub_key.to_base58_string()
|
pub_key.to_base58_string()
|
||||||
);
|
);
|
||||||
error!("UNIMPLEMENTED: blacklisting the double spend key");
|
error!("UNIMPLEMENTED: blacklisting the double spend key");
|
||||||
return reject_ticket(EcashTicketVerificationRejection::DoubleSpend);
|
return reject_ticket(output, EcashTicketVerificationRejection::DoubleSpend);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,7 +130,7 @@ async fn verify_ticket(
|
|||||||
|
|
||||||
// perform actual crypto verification
|
// perform actual crypto verification
|
||||||
if credential_data.verify(&verification_key).is_err() {
|
if credential_data.verify(&verification_key).is_err() {
|
||||||
return reject_ticket(EcashTicketVerificationRejection::InvalidTicket);
|
return reject_ticket(output, EcashTicketVerificationRejection::InvalidTicket);
|
||||||
}
|
}
|
||||||
|
|
||||||
// store credential and check whether it wasn't already there (due to a parallel request)
|
// store credential and check whether it wasn't already there (due to a parallel request)
|
||||||
@@ -127,10 +138,10 @@ async fn verify_ticket(
|
|||||||
.store_verified_ticket(credential_data, gateway_cosmos_addr)
|
.store_verified_ticket(credential_data, gateway_cosmos_addr)
|
||||||
.await?;
|
.await?;
|
||||||
if !was_inserted {
|
if !was_inserted {
|
||||||
return reject_ticket(EcashTicketVerificationRejection::ReplayedTicket);
|
return reject_ticket(output, EcashTicketVerificationRejection::ReplayedTicket);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Json(EcashTicketVerificationResponse { verified: Ok(()) }))
|
Ok(output.to_response(EcashTicketVerificationResponse { verified: Ok(()) }))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -139,16 +150,23 @@ async fn verify_ticket(
|
|||||||
request_body = BatchRedeemTicketsBody,
|
request_body = BatchRedeemTicketsBody,
|
||||||
path = "/v1/ecash/batch-redeem-ecash-tickets",
|
path = "/v1/ecash/batch-redeem-ecash-tickets",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = EcashBatchTicketRedemptionResponse),
|
(status = 200, content(
|
||||||
|
(EcashBatchTicketRedemptionResponse = "application/json"),
|
||||||
|
(EcashBatchTicketRedemptionResponse = "application/yaml"),
|
||||||
|
(EcashBatchTicketRedemptionResponse = "application/bincode")
|
||||||
|
)),
|
||||||
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
|
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
|
||||||
)
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
async fn batch_redeem_tickets(
|
async fn batch_redeem_tickets(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<Arc<EcashState>>,
|
State(state): State<Arc<EcashState>>,
|
||||||
// TODO in the future: make it send binary data rather than json
|
// TODO in the future: make it send binary data rather than json
|
||||||
Json(batch_redeem_credentials_body): Json<BatchRedeemTicketsBody>,
|
Json(batch_redeem_credentials_body): Json<BatchRedeemTicketsBody>,
|
||||||
) -> AxumResult<Json<EcashBatchTicketRedemptionResponse>> {
|
) -> AxumResult<FormattedResponse<EcashBatchTicketRedemptionResponse>> {
|
||||||
state.ensure_signer().await?;
|
state.ensure_signer().await?;
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
// 1. see if that gateway has even submitted any tickets
|
// 1. see if that gateway has even submitted any tickets
|
||||||
let Some(provider_info) = state
|
let Some(provider_info) = state
|
||||||
@@ -208,7 +226,7 @@ async fn batch_redeem_tickets(
|
|||||||
// 7. update the time of the last verification for this provider
|
// 7. update the time of the last verification for this provider
|
||||||
state.update_last_batch_verification(&provider_info).await?;
|
state.update_last_batch_verification(&provider_info).await?;
|
||||||
|
|
||||||
Ok(Json(EcashBatchTicketRedemptionResponse {
|
Ok(output.to_response(EcashBatchTicketRedemptionResponse {
|
||||||
proposal_accepted: true,
|
proposal_accepted: true,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@@ -224,7 +242,7 @@ async fn batch_redeem_tickets(
|
|||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
async fn double_spending_filter_v1() -> AxumResult<Json<SpentCredentialsResponse>> {
|
async fn double_spending_filter_v1() -> AxumResult<FormattedResponse<SpentCredentialsResponse>> {
|
||||||
AxumResult::Err(AxumErrorResponse::new(
|
AxumResult::Err(AxumErrorResponse::new(
|
||||||
"permanently restricted",
|
"permanently restricted",
|
||||||
StatusCode::GONE,
|
StatusCode::GONE,
|
||||||
|
|||||||
@@ -4,10 +4,11 @@
|
|||||||
use crate::network::models::{ContractInformation, NetworkDetails};
|
use crate::network::models::{ContractInformation, NetworkDetails};
|
||||||
use crate::node_status_api::models::AxumResult;
|
use crate::node_status_api::models::AxumResult;
|
||||||
use crate::support::http::state::AppState;
|
use crate::support::http::state::AppState;
|
||||||
use axum::extract::State;
|
use axum::extract::{Query, State};
|
||||||
use axum::{extract, Json, Router};
|
use axum::{extract, Router};
|
||||||
use nym_api_requests::models::ChainStatusResponse;
|
use nym_api_requests::models::ChainStatusResponse;
|
||||||
use nym_contracts_common::ContractBuildInformation;
|
use nym_contracts_common::ContractBuildInformation;
|
||||||
|
use nym_http_api_common::{FormattedResponse, OutputParams};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use tower_http::compression::CompressionLayer;
|
use tower_http::compression::CompressionLayer;
|
||||||
use utoipa::ToSchema;
|
use utoipa::ToSchema;
|
||||||
@@ -29,13 +30,21 @@ pub(crate) fn nym_network_routes() -> Router<AppState> {
|
|||||||
get,
|
get,
|
||||||
path = "/v1/network/details",
|
path = "/v1/network/details",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = NetworkDetails)
|
(status = 200, content(
|
||||||
)
|
(NetworkDetails = "application/json"),
|
||||||
|
(NetworkDetails = "application/yaml"),
|
||||||
|
(NetworkDetails = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
async fn network_details(
|
async fn network_details(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
extract::State(state): extract::State<AppState>,
|
extract::State(state): extract::State<AppState>,
|
||||||
) -> axum::Json<NetworkDetails> {
|
) -> FormattedResponse<NetworkDetails> {
|
||||||
state.network_details().to_owned().into()
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
|
output.to_response(state.network_details().to_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -43,10 +52,20 @@ async fn network_details(
|
|||||||
get,
|
get,
|
||||||
path = "/v1/network/chain-status",
|
path = "/v1/network/chain-status",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = ChainStatusResponse)
|
(status = 200, content(
|
||||||
)
|
(ChainStatusResponse = "application/json"),
|
||||||
|
(ChainStatusResponse = "application/yaml"),
|
||||||
|
(ChainStatusResponse = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
async fn chain_status(State(state): State<AppState>) -> AxumResult<Json<ChainStatusResponse>> {
|
async fn chain_status(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
|
State(state): State<AppState>,
|
||||||
|
) -> AxumResult<FormattedResponse<ChainStatusResponse>> {
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
let chain_status = state
|
let chain_status = state
|
||||||
.chain_status_cache
|
.chain_status_cache
|
||||||
.get_or_refresh(&state.nyxd_client)
|
.get_or_refresh(&state.nyxd_client)
|
||||||
@@ -54,7 +73,7 @@ async fn chain_status(State(state): State<AppState>) -> AxumResult<Json<ChainSta
|
|||||||
|
|
||||||
let connected_nyxd = state.network_details.connected_nyxd;
|
let connected_nyxd = state.network_details.connected_nyxd;
|
||||||
|
|
||||||
Ok(Json(ChainStatusResponse {
|
Ok(output.to_response(ChainStatusResponse {
|
||||||
connected_nyxd,
|
connected_nyxd,
|
||||||
status: chain_status,
|
status: chain_status,
|
||||||
}))
|
}))
|
||||||
@@ -87,25 +106,34 @@ pub struct ContractInformationContractVersion {
|
|||||||
get,
|
get,
|
||||||
path = "/v1/network/nym-contracts",
|
path = "/v1/network/nym-contracts",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = HashMap<String, ContractInformationContractVersion>)
|
(status = 200, content(
|
||||||
)
|
(HashMap<String, ContractInformationContractVersion> = "application/json"),
|
||||||
|
(HashMap<String, ContractInformationContractVersion> = "application/yaml"),
|
||||||
|
(HashMap<String, ContractInformationContractVersion> = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
async fn nym_contracts(
|
async fn nym_contracts(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
extract::State(state): extract::State<AppState>,
|
extract::State(state): extract::State<AppState>,
|
||||||
) -> axum::Json<HashMap<String, ContractInformation<cw2::ContractVersion>>> {
|
) -> FormattedResponse<HashMap<String, ContractInformation<cw2::ContractVersion>>> {
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
let info = state.nym_contract_cache().contract_details().await;
|
let info = state.nym_contract_cache().contract_details().await;
|
||||||
info.iter()
|
output.to_response(
|
||||||
.map(|(contract, info)| {
|
info.iter()
|
||||||
(
|
.map(|(contract, info)| {
|
||||||
contract.to_owned(),
|
(
|
||||||
ContractInformation {
|
contract.to_owned(),
|
||||||
address: info.address.as_ref().map(|a| a.to_string()),
|
ContractInformation {
|
||||||
details: info.base.clone(),
|
address: info.address.as_ref().map(|a| a.to_string()),
|
||||||
},
|
details: info.base.clone(),
|
||||||
)
|
},
|
||||||
})
|
)
|
||||||
.collect::<HashMap<_, _>>()
|
})
|
||||||
.into()
|
.collect::<HashMap<_, _>>(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)] // not dead, used in OpenAPI docs
|
#[allow(dead_code)] // not dead, used in OpenAPI docs
|
||||||
@@ -120,23 +148,32 @@ pub struct ContractInformationBuildInformation {
|
|||||||
get,
|
get,
|
||||||
path = "/v1/network/nym-contracts-detailed",
|
path = "/v1/network/nym-contracts-detailed",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = HashMap<String, ContractInformationBuildInformation>)
|
(status = 200, content(
|
||||||
)
|
(HashMap<String, ContractInformationBuildInformation> = "application/json"),
|
||||||
|
(HashMap<String, ContractInformationBuildInformation> = "application/yaml"),
|
||||||
|
(HashMap<String, ContractInformationBuildInformation> = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
async fn nym_contracts_detailed(
|
async fn nym_contracts_detailed(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
extract::State(state): extract::State<AppState>,
|
extract::State(state): extract::State<AppState>,
|
||||||
) -> axum::Json<HashMap<String, ContractInformation<ContractBuildInformation>>> {
|
) -> FormattedResponse<HashMap<String, ContractInformation<ContractBuildInformation>>> {
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
let info = state.nym_contract_cache().contract_details().await;
|
let info = state.nym_contract_cache().contract_details().await;
|
||||||
info.iter()
|
output.to_response(
|
||||||
.map(|(contract, info)| {
|
info.iter()
|
||||||
(
|
.map(|(contract, info)| {
|
||||||
contract.to_owned(),
|
(
|
||||||
ContractInformation {
|
contract.to_owned(),
|
||||||
address: info.address.as_ref().map(|a| a.to_string()),
|
ContractInformation {
|
||||||
details: info.detailed.clone(),
|
address: info.address.as_ref().map(|a| a.to_string()),
|
||||||
},
|
details: info.detailed.clone(),
|
||||||
)
|
},
|
||||||
})
|
)
|
||||||
.collect::<HashMap<_, _>>()
|
})
|
||||||
.into()
|
.collect::<HashMap<_, _>>(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,11 @@
|
|||||||
use crate::node_status_api::models::AxumResult;
|
use crate::node_status_api::models::AxumResult;
|
||||||
use crate::support::caching::cache::UninitialisedCache;
|
use crate::support::caching::cache::UninitialisedCache;
|
||||||
use crate::support::http::state::AppState;
|
use crate::support::http::state::AppState;
|
||||||
use axum::extract::State;
|
use axum::extract::{Query, State};
|
||||||
use axum::routing::get;
|
use axum::routing::get;
|
||||||
use axum::{Json, Router};
|
use axum::Router;
|
||||||
use nym_api_requests::models::ConfigScoreDataResponse;
|
use nym_api_requests::models::ConfigScoreDataResponse;
|
||||||
|
use nym_http_api_common::{FormattedResponse, OutputParams};
|
||||||
use nym_mixnet_contract_common::NodeId;
|
use nym_mixnet_contract_common::NodeId;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use utoipa::IntoParams;
|
use utoipa::IntoParams;
|
||||||
@@ -43,17 +44,25 @@ struct MixIdParam {
|
|||||||
path = "/config-score-details",
|
path = "/config-score-details",
|
||||||
context_path = "/v1/status",
|
context_path = "/v1/status",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = ConfigScoreDataResponse)
|
(status = 200, content(
|
||||||
|
(ConfigScoreDataResponse = "application/json"),
|
||||||
|
(ConfigScoreDataResponse = "application/yaml"),
|
||||||
|
(ConfigScoreDataResponse = "application/bincode")
|
||||||
|
))
|
||||||
),
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
async fn config_score_details(
|
async fn config_score_details(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> AxumResult<Json<ConfigScoreDataResponse>> {
|
) -> AxumResult<FormattedResponse<ConfigScoreDataResponse>> {
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
let data = state
|
let data = state
|
||||||
.nym_contract_cache()
|
.nym_contract_cache()
|
||||||
.maybe_config_score_data_owned()
|
.maybe_config_score_data_owned()
|
||||||
.await
|
.await
|
||||||
.ok_or(UninitialisedCache)?;
|
.ok_or(UninitialisedCache)?;
|
||||||
|
|
||||||
Ok(Json(data.into_inner().into()))
|
Ok(output.to_response(data.into_inner().into()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ use nym_api_requests::models::{
|
|||||||
MixNodeBondAnnotated, MixnodeCoreStatusResponse, MixnodeStatusReportResponse,
|
MixNodeBondAnnotated, MixnodeCoreStatusResponse, MixnodeStatusReportResponse,
|
||||||
MixnodeUptimeHistoryResponse, RewardEstimationResponse, UptimeResponse,
|
MixnodeUptimeHistoryResponse, RewardEstimationResponse, UptimeResponse,
|
||||||
};
|
};
|
||||||
|
use nym_http_api_common::{FormattedResponse, Output, OutputParams};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use utoipa::IntoParams;
|
use utoipa::IntoParams;
|
||||||
|
|
||||||
@@ -103,17 +104,23 @@ pub(super) fn network_monitor_routes() -> Router<AppState> {
|
|||||||
get,
|
get,
|
||||||
path = "/v1/status/gateway/{identity}/report",
|
path = "/v1/status/gateway/{identity}/report",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = GatewayStatusReportResponse)
|
(status = 200, content(
|
||||||
)
|
(GatewayStatusReportResponse = "application/json"),
|
||||||
|
(GatewayStatusReportResponse = "application/yaml"),
|
||||||
|
(GatewayStatusReportResponse = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
async fn gateway_report(
|
async fn gateway_report(
|
||||||
Path(identity): Path<String>,
|
Path(identity): Path<String>,
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> AxumResult<Json<GatewayStatusReportResponse>> {
|
) -> AxumResult<FormattedResponse<GatewayStatusReportResponse>> {
|
||||||
Ok(Json(
|
let output = output.output.unwrap_or_default();
|
||||||
_gateway_report(state.node_status_cache(), &identity).await?,
|
|
||||||
))
|
Ok(output.to_response(_gateway_report(state.node_status_cache(), &identity).await?))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -121,15 +128,23 @@ async fn gateway_report(
|
|||||||
get,
|
get,
|
||||||
path = "/v1/status/gateway/{identity}/history",
|
path = "/v1/status/gateway/{identity}/history",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = GatewayUptimeHistoryResponse)
|
(status = 200, content(
|
||||||
)
|
(GatewayUptimeHistoryResponse = "application/json"),
|
||||||
|
(GatewayUptimeHistoryResponse = "application/yaml"),
|
||||||
|
(GatewayUptimeHistoryResponse = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
async fn gateway_uptime_history(
|
async fn gateway_uptime_history(
|
||||||
Path(identity): Path<String>,
|
Path(identity): Path<String>,
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> AxumResult<Json<GatewayUptimeHistoryResponse>> {
|
) -> AxumResult<FormattedResponse<GatewayUptimeHistoryResponse>> {
|
||||||
Ok(Json(
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
|
Ok(output.to_response(
|
||||||
_gateway_uptime_history(state.storage(), state.nym_contract_cache(), &identity).await?,
|
_gateway_uptime_history(state.storage(), state.nym_contract_cache(), &identity).await?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -138,6 +153,7 @@ async fn gateway_uptime_history(
|
|||||||
#[into_params(parameter_in = Query)]
|
#[into_params(parameter_in = Query)]
|
||||||
struct SinceQueryParams {
|
struct SinceQueryParams {
|
||||||
since: Option<i64>,
|
since: Option<i64>,
|
||||||
|
output: Option<Output>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -148,18 +164,22 @@ struct SinceQueryParams {
|
|||||||
),
|
),
|
||||||
path = "/v1/status/gateway/{identity}/core-status-count",
|
path = "/v1/status/gateway/{identity}/core-status-count",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = GatewayCoreStatusResponse)
|
(status = 200, content(
|
||||||
)
|
(GatewayCoreStatusResponse = "application/json"),
|
||||||
|
(GatewayCoreStatusResponse = "application/yaml"),
|
||||||
|
(GatewayCoreStatusResponse = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
async fn gateway_core_status_count(
|
async fn gateway_core_status_count(
|
||||||
Path(identity): Path<String>,
|
Path(identity): Path<String>,
|
||||||
Query(SinceQueryParams { since }): Query<SinceQueryParams>,
|
Query(SinceQueryParams { since, output }): Query<SinceQueryParams>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> AxumResult<Json<GatewayCoreStatusResponse>> {
|
) -> AxumResult<FormattedResponse<GatewayCoreStatusResponse>> {
|
||||||
Ok(Json(
|
Ok(output
|
||||||
_gateway_core_status_count(state.storage(), &identity, since).await?,
|
.unwrap_or_default()
|
||||||
))
|
.to_response(_gateway_core_status_count(state.storage(), &identity, since).await?))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -167,57 +187,71 @@ async fn gateway_core_status_count(
|
|||||||
get,
|
get,
|
||||||
path = "/v1/status/gateway/{identity}/avg_uptime",
|
path = "/v1/status/gateway/{identity}/avg_uptime",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = GatewayUptimeResponse)
|
(status = 200, content(
|
||||||
)
|
(GatewayUptimeResponse = "application/json"),
|
||||||
|
(GatewayUptimeResponse = "application/yaml"),
|
||||||
|
(GatewayUptimeResponse = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
async fn get_gateway_avg_uptime(
|
async fn get_gateway_avg_uptime(
|
||||||
Path(identity): Path<String>,
|
Path(identity): Path<String>,
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> AxumResult<Json<GatewayUptimeResponse>> {
|
) -> AxumResult<FormattedResponse<GatewayUptimeResponse>> {
|
||||||
Ok(Json(
|
let output = output.output.unwrap_or_default();
|
||||||
_get_gateway_avg_uptime(state.node_status_cache(), &identity).await?,
|
|
||||||
))
|
Ok(output.to_response(_get_gateway_avg_uptime(state.node_status_cache(), &identity).await?))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
tag = "network-monitor-status",
|
tag = "network-monitor-status",
|
||||||
get,
|
get,
|
||||||
params(
|
|
||||||
MixIdParam
|
|
||||||
),
|
|
||||||
path = "/v1/status/mixnode/{mix_id}/report",
|
path = "/v1/status/mixnode/{mix_id}/report",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = MixnodeStatusReportResponse)
|
(status = 200, content(
|
||||||
)
|
(MixnodeStatusReportResponse = "application/json"),
|
||||||
|
(MixnodeStatusReportResponse = "application/yaml"),
|
||||||
|
(MixnodeStatusReportResponse = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams, MixIdParam)
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
async fn mixnode_report(
|
async fn mixnode_report(
|
||||||
Path(MixIdParam { mix_id }): Path<MixIdParam>,
|
Path(MixIdParam { mix_id }): Path<MixIdParam>,
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> AxumResult<Json<MixnodeStatusReportResponse>> {
|
) -> AxumResult<FormattedResponse<MixnodeStatusReportResponse>> {
|
||||||
Ok(Json(
|
let output = output.output.unwrap_or_default();
|
||||||
_mixnode_report(state.node_status_cache(), mix_id).await?,
|
|
||||||
))
|
Ok(output.to_response(_mixnode_report(state.node_status_cache(), mix_id).await?))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
tag = "network-monitor-status",
|
tag = "network-monitor-status",
|
||||||
get,
|
get,
|
||||||
params(
|
|
||||||
MixIdParam
|
|
||||||
),
|
|
||||||
path = "/v1/status/mixnode/{mix_id}/history",
|
path = "/v1/status/mixnode/{mix_id}/history",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = MixnodeUptimeHistoryResponse)
|
(status = 200, content(
|
||||||
)
|
(MixnodeUptimeHistoryResponse = "application/json"),
|
||||||
|
(MixnodeUptimeHistoryResponse = "application/yaml"),
|
||||||
|
(MixnodeUptimeHistoryResponse = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(MixIdParam, OutputParams)
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
async fn mixnode_uptime_history(
|
async fn mixnode_uptime_history(
|
||||||
Path(MixIdParam { mix_id }): Path<MixIdParam>,
|
Path(MixIdParam { mix_id }): Path<MixIdParam>,
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> AxumResult<Json<MixnodeUptimeHistoryResponse>> {
|
) -> AxumResult<FormattedResponse<MixnodeUptimeHistoryResponse>> {
|
||||||
Ok(Json(
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
|
Ok(output.to_response(
|
||||||
_mixnode_uptime_history(state.storage(), state.nym_contract_cache(), mix_id).await?,
|
_mixnode_uptime_history(state.storage(), state.nym_contract_cache(), mix_id).await?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -230,37 +264,48 @@ async fn mixnode_uptime_history(
|
|||||||
),
|
),
|
||||||
path = "/v1/status/mixnode/{mix_id}/core-status-count",
|
path = "/v1/status/mixnode/{mix_id}/core-status-count",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = MixnodeCoreStatusResponse)
|
(status = 200, content(
|
||||||
)
|
(MixnodeCoreStatusResponse = "application/json"),
|
||||||
|
(MixnodeCoreStatusResponse = "application/yaml"),
|
||||||
|
(MixnodeCoreStatusResponse = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
async fn mixnode_core_status_count(
|
async fn mixnode_core_status_count(
|
||||||
Path(MixIdParam { mix_id }): Path<MixIdParam>,
|
Path(MixIdParam { mix_id }): Path<MixIdParam>,
|
||||||
Query(SinceQueryParams { since }): Query<SinceQueryParams>,
|
Query(SinceQueryParams { since, output }): Query<SinceQueryParams>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> AxumResult<Json<MixnodeCoreStatusResponse>> {
|
) -> AxumResult<FormattedResponse<MixnodeCoreStatusResponse>> {
|
||||||
Ok(Json(
|
let output = output.unwrap_or_default();
|
||||||
_mixnode_core_status_count(state.storage(), mix_id, since).await?,
|
|
||||||
))
|
Ok(output.to_response(_mixnode_core_status_count(state.storage(), mix_id, since).await?))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
tag = "network-monitor-status",
|
tag = "network-monitor-status",
|
||||||
get,
|
get,
|
||||||
params(
|
params(
|
||||||
MixIdParam
|
MixIdParam, OutputParams
|
||||||
),
|
),
|
||||||
path = "/v1/status/mixnode/{mix_id}/reward-estimation",
|
path = "/v1/status/mixnode/{mix_id}/reward-estimation",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = RewardEstimationResponse)
|
(status = 200, content(
|
||||||
)
|
(RewardEstimationResponse = "application/json"),
|
||||||
|
(RewardEstimationResponse = "application/yaml"),
|
||||||
|
(RewardEstimationResponse = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
async fn get_mixnode_reward_estimation(
|
async fn get_mixnode_reward_estimation(
|
||||||
Path(MixIdParam { mix_id }): Path<MixIdParam>,
|
Path(MixIdParam { mix_id }): Path<MixIdParam>,
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> AxumResult<Json<RewardEstimationResponse>> {
|
) -> AxumResult<FormattedResponse<RewardEstimationResponse>> {
|
||||||
Ok(Json(
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
|
Ok(output.to_response(
|
||||||
_get_mixnode_reward_estimation(
|
_get_mixnode_reward_estimation(
|
||||||
state.node_status_cache(),
|
state.node_status_cache(),
|
||||||
state.nym_contract_cache(),
|
state.nym_contract_cache(),
|
||||||
@@ -274,21 +319,28 @@ async fn get_mixnode_reward_estimation(
|
|||||||
tag = "network-monitor-status",
|
tag = "network-monitor-status",
|
||||||
post,
|
post,
|
||||||
params(
|
params(
|
||||||
ComputeRewardEstParam, MixIdParam
|
OutputParams, MixIdParam
|
||||||
),
|
),
|
||||||
path = "/v1/status/mixnode/{mix_id}/compute-reward-estimation",
|
path = "/v1/status/mixnode/{mix_id}/compute-reward-estimation",
|
||||||
request_body = ComputeRewardEstParam,
|
request_body = ComputeRewardEstParam,
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = RewardEstimationResponse)
|
(status = 200, content(
|
||||||
)
|
(RewardEstimationResponse = "application/json"),
|
||||||
|
(RewardEstimationResponse = "application/yaml"),
|
||||||
|
(RewardEstimationResponse = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
async fn compute_mixnode_reward_estimation(
|
async fn compute_mixnode_reward_estimation(
|
||||||
Path(MixIdParam { mix_id }): Path<MixIdParam>,
|
Path(MixIdParam { mix_id }): Path<MixIdParam>,
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Json(user_reward_param): Json<ComputeRewardEstParam>,
|
Json(user_reward_param): Json<ComputeRewardEstParam>,
|
||||||
) -> AxumResult<Json<RewardEstimationResponse>> {
|
) -> AxumResult<FormattedResponse<RewardEstimationResponse>> {
|
||||||
Ok(Json(
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
|
Ok(output.to_response(
|
||||||
_compute_mixnode_reward_estimation(
|
_compute_mixnode_reward_estimation(
|
||||||
&user_reward_param,
|
&user_reward_param,
|
||||||
state.node_status_cache(),
|
state.node_status_cache(),
|
||||||
@@ -303,21 +355,26 @@ async fn compute_mixnode_reward_estimation(
|
|||||||
tag = "network-monitor-status",
|
tag = "network-monitor-status",
|
||||||
get,
|
get,
|
||||||
params(
|
params(
|
||||||
MixIdParam
|
MixIdParam, OutputParams
|
||||||
),
|
),
|
||||||
path = "/v1/status/mixnode/{mix_id}/avg_uptime",
|
path = "/v1/status/mixnode/{mix_id}/avg_uptime",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = UptimeResponse)
|
(status = 200, content(
|
||||||
)
|
(UptimeResponse = "application/json"),
|
||||||
|
(UptimeResponse = "application/yaml"),
|
||||||
|
(UptimeResponse = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
async fn get_mixnode_avg_uptime(
|
async fn get_mixnode_avg_uptime(
|
||||||
Path(MixIdParam { mix_id }): Path<MixIdParam>,
|
Path(MixIdParam { mix_id }): Path<MixIdParam>,
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> AxumResult<Json<UptimeResponse>> {
|
) -> AxumResult<FormattedResponse<UptimeResponse>> {
|
||||||
Ok(Json(
|
let output = output.output.unwrap_or_default();
|
||||||
_get_mixnode_avg_uptime(state.node_status_cache(), mix_id).await?,
|
|
||||||
))
|
Ok(output.to_response(_get_mixnode_avg_uptime(state.node_status_cache(), mix_id).await?))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -325,14 +382,22 @@ async fn get_mixnode_avg_uptime(
|
|||||||
get,
|
get,
|
||||||
path = "/v1/status/mixnodes/detailed-unfiltered",
|
path = "/v1/status/mixnodes/detailed-unfiltered",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = MixNodeBondAnnotated)
|
(status = 200, content(
|
||||||
)
|
(MixNodeBondAnnotated = "application/json"),
|
||||||
|
(MixNodeBondAnnotated = "application/yaml"),
|
||||||
|
(MixNodeBondAnnotated = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
pub async fn get_mixnodes_detailed_unfiltered(
|
pub async fn get_mixnodes_detailed_unfiltered(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> Json<Vec<MixNodeBondAnnotated>> {
|
) -> FormattedResponse<Vec<MixNodeBondAnnotated>> {
|
||||||
Json(_get_mixnodes_detailed_unfiltered(state.node_status_cache()).await)
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
|
output.to_response(_get_mixnodes_detailed_unfiltered(state.node_status_cache()).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -340,14 +405,22 @@ pub async fn get_mixnodes_detailed_unfiltered(
|
|||||||
get,
|
get,
|
||||||
path = "/v1/status/gateways/detailed",
|
path = "/v1/status/gateways/detailed",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = GatewayBondAnnotated)
|
(status = 200, content(
|
||||||
)
|
(GatewayBondAnnotated = "application/json"),
|
||||||
|
(GatewayBondAnnotated = "application/yaml"),
|
||||||
|
(GatewayBondAnnotated = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
pub async fn get_gateways_detailed(
|
pub async fn get_gateways_detailed(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> Json<Vec<GatewayBondAnnotated>> {
|
) -> FormattedResponse<Vec<GatewayBondAnnotated>> {
|
||||||
Json(_get_legacy_gateways_detailed(state.node_status_cache()).await)
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
|
output.to_response(_get_legacy_gateways_detailed(state.node_status_cache()).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -355,12 +428,20 @@ pub async fn get_gateways_detailed(
|
|||||||
get,
|
get,
|
||||||
path = "/v1/status/gateways/detailed-unfiltered",
|
path = "/v1/status/gateways/detailed-unfiltered",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = GatewayBondAnnotated)
|
(status = 200, content(
|
||||||
)
|
(GatewayBondAnnotated = "application/json"),
|
||||||
|
(GatewayBondAnnotated = "application/yaml"),
|
||||||
|
(GatewayBondAnnotated = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
pub async fn get_gateways_detailed_unfiltered(
|
pub async fn get_gateways_detailed_unfiltered(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> Json<Vec<GatewayBondAnnotated>> {
|
) -> FormattedResponse<Vec<GatewayBondAnnotated>> {
|
||||||
Json(_get_legacy_gateways_detailed_unfiltered(state.node_status_cache()).await)
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
|
output.to_response(_get_legacy_gateways_detailed_unfiltered(state.node_status_cache()).await)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ use crate::support::http::state::AppState;
|
|||||||
use crate::support::storage::NymApiStorage;
|
use crate::support::storage::NymApiStorage;
|
||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
use axum::extract::{Path, Query, State};
|
use axum::extract::{Path, Query, State};
|
||||||
use axum::Json;
|
|
||||||
use nym_api_requests::models::{
|
use nym_api_requests::models::{
|
||||||
GatewayTestResultResponse, MixnodeTestResultResponse, NetworkMonitorRunDetailsResponse,
|
GatewayTestResultResponse, MixnodeTestResultResponse, NetworkMonitorRunDetailsResponse,
|
||||||
PartialTestResult, TestNode, TestRoute,
|
PartialTestResult, TestNode, TestRoute,
|
||||||
};
|
};
|
||||||
use nym_api_requests::pagination::Pagination;
|
use nym_api_requests::pagination::Pagination;
|
||||||
|
use nym_http_api_common::{FormattedResponse, OutputParams};
|
||||||
use nym_mixnet_contract_common::NodeId;
|
use nym_mixnet_contract_common::NodeId;
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use std::collections::{BTreeMap, HashMap};
|
use std::collections::{BTreeMap, HashMap};
|
||||||
@@ -173,14 +173,18 @@ async fn _mixnode_test_results(
|
|||||||
),
|
),
|
||||||
path = "/v1/status/mixnodes/unstable/{mix_id}/test-results",
|
path = "/v1/status/mixnodes/unstable/{mix_id}/test-results",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = MixnodeTestResultResponse)
|
(status = 200, content(
|
||||||
)
|
(MixnodeTestResultResponse = "application/json"),
|
||||||
|
(MixnodeTestResultResponse = "application/yaml"),
|
||||||
|
(MixnodeTestResultResponse = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
)]
|
)]
|
||||||
pub async fn mixnode_test_results(
|
pub async fn mixnode_test_results(
|
||||||
Path(mix_id): Path<NodeId>,
|
Path(mix_id): Path<NodeId>,
|
||||||
Query(pagination): Query<PaginationRequest>,
|
Query(pagination): Query<PaginationRequest>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> AxumResult<Json<MixnodeTestResultResponse>> {
|
) -> AxumResult<FormattedResponse<MixnodeTestResultResponse>> {
|
||||||
let page = pagination.page.unwrap_or_default();
|
let page = pagination.page.unwrap_or_default();
|
||||||
let per_page = min(
|
let per_page = min(
|
||||||
pagination
|
pagination
|
||||||
@@ -188,6 +192,7 @@ pub async fn mixnode_test_results(
|
|||||||
.unwrap_or(DEFAULT_TEST_RESULTS_PAGE_SIZE),
|
.unwrap_or(DEFAULT_TEST_RESULTS_PAGE_SIZE),
|
||||||
MAX_TEST_RESULTS_PAGE_SIZE,
|
MAX_TEST_RESULTS_PAGE_SIZE,
|
||||||
);
|
);
|
||||||
|
let output = pagination.output.unwrap_or_default();
|
||||||
|
|
||||||
match _mixnode_test_results(
|
match _mixnode_test_results(
|
||||||
mix_id,
|
mix_id,
|
||||||
@@ -198,7 +203,7 @@ pub async fn mixnode_test_results(
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(res) => Ok(Json(res)),
|
Ok(res) => Ok(output.to_response(res)),
|
||||||
Err(err) => Err(AxumErrorResponse::internal_msg(format!(
|
Err(err) => Err(AxumErrorResponse::internal_msg(format!(
|
||||||
"failed to retrieve mixnode test results for node {mix_id}: {err}"
|
"failed to retrieve mixnode test results for node {mix_id}: {err}"
|
||||||
))),
|
))),
|
||||||
@@ -272,14 +277,18 @@ async fn _gateway_test_results(
|
|||||||
),
|
),
|
||||||
path = "/v1/status/gateways/unstable/{identity}/test-results",
|
path = "/v1/status/gateways/unstable/{identity}/test-results",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = GatewayTestResultResponse)
|
(status = 200, content(
|
||||||
)
|
(GatewayTestResultResponse = "application/json"),
|
||||||
|
(GatewayTestResultResponse = "application/yaml"),
|
||||||
|
(GatewayTestResultResponse = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
)]
|
)]
|
||||||
pub async fn gateway_test_results(
|
pub async fn gateway_test_results(
|
||||||
Path(gateway_identity): Path<String>,
|
Path(gateway_identity): Path<String>,
|
||||||
Query(pagination): Query<PaginationRequest>,
|
Query(pagination): Query<PaginationRequest>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> AxumResult<Json<GatewayTestResultResponse>> {
|
) -> AxumResult<FormattedResponse<GatewayTestResultResponse>> {
|
||||||
let page = pagination.page.unwrap_or_default();
|
let page = pagination.page.unwrap_or_default();
|
||||||
let per_page = min(
|
let per_page = min(
|
||||||
pagination
|
pagination
|
||||||
@@ -287,6 +296,7 @@ pub async fn gateway_test_results(
|
|||||||
.unwrap_or(DEFAULT_TEST_RESULTS_PAGE_SIZE),
|
.unwrap_or(DEFAULT_TEST_RESULTS_PAGE_SIZE),
|
||||||
MAX_TEST_RESULTS_PAGE_SIZE,
|
MAX_TEST_RESULTS_PAGE_SIZE,
|
||||||
);
|
);
|
||||||
|
let output = pagination.output.unwrap_or_default();
|
||||||
|
|
||||||
match _gateway_test_results(
|
match _gateway_test_results(
|
||||||
&gateway_identity,
|
&gateway_identity,
|
||||||
@@ -297,7 +307,7 @@ pub async fn gateway_test_results(
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(res) => Ok(Json(res)),
|
Ok(res) => Ok(output.to_response(res)),
|
||||||
Err(err) => Err(AxumErrorResponse::internal_msg(format!(
|
Err(err) => Err(AxumErrorResponse::internal_msg(format!(
|
||||||
"failed to retrieve mixnode test results for gateway {gateway_identity}: {err}"
|
"failed to retrieve mixnode test results for gateway {gateway_identity}: {err}"
|
||||||
))),
|
))),
|
||||||
@@ -349,15 +359,23 @@ async fn _latest_monitor_run_report(
|
|||||||
get,
|
get,
|
||||||
path = "/v1/status/network-monitor/unstable/run/{monitor_run_id}/details",
|
path = "/v1/status/network-monitor/unstable/run/{monitor_run_id}/details",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = NetworkMonitorRunDetailsResponse)
|
(status = 200, content(
|
||||||
)
|
(NetworkMonitorRunDetailsResponse = "application/json"),
|
||||||
|
(NetworkMonitorRunDetailsResponse = "application/yaml"),
|
||||||
|
(NetworkMonitorRunDetailsResponse = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
pub async fn monitor_run_report(
|
pub async fn monitor_run_report(
|
||||||
Path(monitor_run_id): Path<i64>,
|
Path(monitor_run_id): Path<i64>,
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> AxumResult<Json<NetworkMonitorRunDetailsResponse>> {
|
) -> AxumResult<FormattedResponse<NetworkMonitorRunDetailsResponse>> {
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
match _monitor_run_report(monitor_run_id, state.storage()).await {
|
match _monitor_run_report(monitor_run_id, state.storage()).await {
|
||||||
Ok(res) => Ok(Json(res)),
|
Ok(res) => Ok(output.to_response(res)),
|
||||||
Err(err) => Err(AxumErrorResponse::internal_msg(format!(
|
Err(err) => Err(AxumErrorResponse::internal_msg(format!(
|
||||||
"failed to retrieve monitor run report for run {monitor_run_id}: {err}"
|
"failed to retrieve monitor run report for run {monitor_run_id}: {err}"
|
||||||
))),
|
))),
|
||||||
@@ -369,14 +387,22 @@ pub async fn monitor_run_report(
|
|||||||
get,
|
get,
|
||||||
path = "/v1/status/network-monitor/unstable/run/latest/details",
|
path = "/v1/status/network-monitor/unstable/run/latest/details",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = NetworkMonitorRunDetailsResponse)
|
(status = 200, content(
|
||||||
)
|
(NetworkMonitorRunDetailsResponse = "application/json"),
|
||||||
|
(NetworkMonitorRunDetailsResponse = "application/yaml"),
|
||||||
|
(NetworkMonitorRunDetailsResponse = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
pub async fn latest_monitor_run_report(
|
pub async fn latest_monitor_run_report(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> AxumResult<Json<NetworkMonitorRunDetailsResponse>> {
|
) -> AxumResult<FormattedResponse<NetworkMonitorRunDetailsResponse>> {
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
match _latest_monitor_run_report(state.storage()).await {
|
match _latest_monitor_run_report(state.storage()).await {
|
||||||
Ok(res) => Ok(Json(res)),
|
Ok(res) => Ok(output.to_response(res)),
|
||||||
Err(err) => Err(AxumErrorResponse::internal_msg(format!(
|
Err(err) => Err(AxumErrorResponse::internal_msg(format!(
|
||||||
"failed to retrieve the latest monitor run report: {err}"
|
"failed to retrieve the latest monitor run report: {err}"
|
||||||
))),
|
))),
|
||||||
|
|||||||
@@ -11,13 +11,14 @@ use crate::node_status_api::helpers::{
|
|||||||
};
|
};
|
||||||
use crate::node_status_api::models::{AxumErrorResponse, AxumResult};
|
use crate::node_status_api::models::{AxumErrorResponse, AxumResult};
|
||||||
use crate::support::http::state::AppState;
|
use crate::support::http::state::AppState;
|
||||||
use axum::extract::{Path, State};
|
use axum::extract::{Path, Query, State};
|
||||||
use axum::routing::{get, post};
|
use axum::routing::{get, post};
|
||||||
use axum::Json;
|
use axum::Json;
|
||||||
use axum::Router;
|
use axum::Router;
|
||||||
use nym_api_requests::models::{
|
use nym_api_requests::models::{
|
||||||
MixNodeBondAnnotated, MixnodeStatusResponse, StakeSaturationResponse,
|
MixNodeBondAnnotated, MixnodeStatusResponse, StakeSaturationResponse,
|
||||||
};
|
};
|
||||||
|
use nym_http_api_common::{FormattedResponse, OutputParams};
|
||||||
use nym_mixnet_contract_common::NodeId;
|
use nym_mixnet_contract_common::NodeId;
|
||||||
use nym_types::monitoring::MonitorMessage;
|
use nym_types::monitoring::MonitorMessage;
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
@@ -153,15 +154,22 @@ pub(crate) async fn submit_node_monitoring_results(
|
|||||||
),
|
),
|
||||||
path = "/v1/status/mixnode/{mix_id}/status",
|
path = "/v1/status/mixnode/{mix_id}/status",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = MixnodeStatusResponse)
|
(status = 200, content(
|
||||||
)
|
(MixnodeStatusResponse = "application/json"),
|
||||||
|
(MixnodeStatusResponse = "application/yaml"),
|
||||||
|
(MixnodeStatusResponse = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
async fn get_mixnode_status(
|
async fn get_mixnode_status(
|
||||||
Path(MixIdParam { mix_id }): Path<MixIdParam>,
|
Path(MixIdParam { mix_id }): Path<MixIdParam>,
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> Json<MixnodeStatusResponse> {
|
) -> FormattedResponse<MixnodeStatusResponse> {
|
||||||
Json(_get_mixnode_status(state.nym_contract_cache(), mix_id).await)
|
let output = output.output.unwrap_or_default();
|
||||||
|
output.to_response(_get_mixnode_status(state.nym_contract_cache(), mix_id).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -172,15 +180,23 @@ async fn get_mixnode_status(
|
|||||||
),
|
),
|
||||||
path = "/v1/status/mixnode/{mix_id}/stake-saturation",
|
path = "/v1/status/mixnode/{mix_id}/stake-saturation",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = StakeSaturationResponse)
|
(status = 200, content(
|
||||||
)
|
(StakeSaturationResponse = "application/json"),
|
||||||
|
(StakeSaturationResponse = "application/yaml"),
|
||||||
|
(StakeSaturationResponse = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
async fn get_mixnode_stake_saturation(
|
async fn get_mixnode_stake_saturation(
|
||||||
Path(mix_id): Path<NodeId>,
|
Path(mix_id): Path<NodeId>,
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> AxumResult<Json<StakeSaturationResponse>> {
|
) -> AxumResult<FormattedResponse<StakeSaturationResponse>> {
|
||||||
Ok(Json(
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
|
Ok(output.to_response(
|
||||||
_get_mixnode_stake_saturation(
|
_get_mixnode_stake_saturation(
|
||||||
state.node_status_cache(),
|
state.node_status_cache(),
|
||||||
state.nym_contract_cache(),
|
state.nym_contract_cache(),
|
||||||
@@ -194,22 +210,28 @@ async fn get_mixnode_stake_saturation(
|
|||||||
tag = "status",
|
tag = "status",
|
||||||
get,
|
get,
|
||||||
params(
|
params(
|
||||||
MixIdParam
|
MixIdParam, OutputParams
|
||||||
),
|
),
|
||||||
path = "/v1/status/mixnode/{mix_id}/inclusion-probability",
|
path = "/v1/status/mixnode/{mix_id}/inclusion-probability",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = nym_api_requests::models::InclusionProbabilityResponse)
|
(status = 200, content(
|
||||||
)
|
(nym_api_requests::models::InclusionProbabilityResponse = "application/json"),
|
||||||
|
(nym_api_requests::models::InclusionProbabilityResponse = "application/yaml"),
|
||||||
|
(nym_api_requests::models::InclusionProbabilityResponse = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
async fn get_mixnode_inclusion_probability(
|
async fn get_mixnode_inclusion_probability(
|
||||||
Path(mix_id): Path<NodeId>,
|
Path(mix_id): Path<NodeId>,
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> AxumResult<Json<nym_api_requests::models::InclusionProbabilityResponse>> {
|
) -> AxumResult<FormattedResponse<nym_api_requests::models::InclusionProbabilityResponse>> {
|
||||||
Ok(Json(
|
let output = output.output.unwrap_or_default();
|
||||||
_get_mixnode_inclusion_probability(state.node_status_cache(), mix_id).await?,
|
|
||||||
))
|
Ok(output
|
||||||
|
.to_response(_get_mixnode_inclusion_probability(state.node_status_cache(), mix_id).await?))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -217,17 +239,23 @@ async fn get_mixnode_inclusion_probability(
|
|||||||
get,
|
get,
|
||||||
path = "/v1/status/mixnodes/inclusion-probability",
|
path = "/v1/status/mixnodes/inclusion-probability",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = nym_api_requests::models::AllInclusionProbabilitiesResponse)
|
(status = 200, content(
|
||||||
)
|
(nym_api_requests::models::AllInclusionProbabilitiesResponse = "application/json"),
|
||||||
|
(nym_api_requests::models::AllInclusionProbabilitiesResponse = "application/yaml"),
|
||||||
|
(nym_api_requests::models::AllInclusionProbabilitiesResponse = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
async fn get_mixnode_inclusion_probabilities(
|
async fn get_mixnode_inclusion_probabilities(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> AxumResult<Json<nym_api_requests::models::AllInclusionProbabilitiesResponse>> {
|
) -> AxumResult<FormattedResponse<nym_api_requests::models::AllInclusionProbabilitiesResponse>> {
|
||||||
Ok(Json(
|
let output = output.output.unwrap_or_default();
|
||||||
_get_mixnode_inclusion_probabilities(state.node_status_cache()).await?,
|
|
||||||
))
|
Ok(output.to_response(_get_mixnode_inclusion_probabilities(state.node_status_cache()).await?))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -235,14 +263,22 @@ async fn get_mixnode_inclusion_probabilities(
|
|||||||
get,
|
get,
|
||||||
path = "/v1/status/mixnodes/detailed",
|
path = "/v1/status/mixnodes/detailed",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = MixNodeBondAnnotated)
|
(status = 200, content(
|
||||||
)
|
(MixNodeBondAnnotated = "application/json"),
|
||||||
|
(MixNodeBondAnnotated = "application/yaml"),
|
||||||
|
(MixNodeBondAnnotated = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
pub async fn get_mixnodes_detailed(
|
pub async fn get_mixnodes_detailed(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> Json<Vec<MixNodeBondAnnotated>> {
|
) -> FormattedResponse<Vec<MixNodeBondAnnotated>> {
|
||||||
Json(_get_legacy_mixnodes_detailed(state.node_status_cache()).await)
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
|
output.to_response(_get_legacy_mixnodes_detailed(state.node_status_cache()).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -250,14 +286,22 @@ pub async fn get_mixnodes_detailed(
|
|||||||
get,
|
get,
|
||||||
path = "/v1/status/mixnodes/rewarded/detailed",
|
path = "/v1/status/mixnodes/rewarded/detailed",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = MixNodeBondAnnotated)
|
(status = 200, content(
|
||||||
)
|
(MixNodeBondAnnotated = "application/json"),
|
||||||
|
(MixNodeBondAnnotated = "application/yaml"),
|
||||||
|
(MixNodeBondAnnotated = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
pub async fn get_rewarded_set_detailed(
|
pub async fn get_rewarded_set_detailed(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> Json<Vec<MixNodeBondAnnotated>> {
|
) -> FormattedResponse<Vec<MixNodeBondAnnotated>> {
|
||||||
Json(
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
|
output.to_response(
|
||||||
_get_rewarded_set_legacy_mixnodes_detailed(
|
_get_rewarded_set_legacy_mixnodes_detailed(
|
||||||
state.node_status_cache(),
|
state.node_status_cache(),
|
||||||
state.nym_contract_cache(),
|
state.nym_contract_cache(),
|
||||||
@@ -271,14 +315,22 @@ pub async fn get_rewarded_set_detailed(
|
|||||||
get,
|
get,
|
||||||
path = "/v1/status/mixnodes/active/detailed",
|
path = "/v1/status/mixnodes/active/detailed",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = MixNodeBondAnnotated)
|
(status = 200, content(
|
||||||
)
|
(MixNodeBondAnnotated = "application/json"),
|
||||||
|
(MixNodeBondAnnotated = "application/yaml"),
|
||||||
|
(MixNodeBondAnnotated = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
pub async fn get_active_set_detailed(
|
pub async fn get_active_set_detailed(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> Json<Vec<MixNodeBondAnnotated>> {
|
) -> FormattedResponse<Vec<MixNodeBondAnnotated>> {
|
||||||
Json(
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
|
output.to_response(
|
||||||
_get_active_set_legacy_mixnodes_detailed(
|
_get_active_set_legacy_mixnodes_detailed(
|
||||||
state.node_status_cache(),
|
state.node_status_cache(),
|
||||||
state.nym_contract_cache(),
|
state.nym_contract_cache(),
|
||||||
|
|||||||
@@ -7,10 +7,11 @@ use crate::node_status_api::helpers::{
|
|||||||
};
|
};
|
||||||
use crate::support::http::state::AppState;
|
use crate::support::http::state::AppState;
|
||||||
use crate::support::legacy_helpers::{to_legacy_gateway, to_legacy_mixnode};
|
use crate::support::legacy_helpers::{to_legacy_gateway, to_legacy_mixnode};
|
||||||
use axum::extract::State;
|
use axum::extract::{Query, State};
|
||||||
use axum::{Json, Router};
|
use axum::Router;
|
||||||
use nym_api_requests::legacy::LegacyMixNodeDetailsWithLayer;
|
use nym_api_requests::legacy::LegacyMixNodeDetailsWithLayer;
|
||||||
use nym_api_requests::models::MixNodeBondAnnotated;
|
use nym_api_requests::models::MixNodeBondAnnotated;
|
||||||
|
use nym_http_api_common::{FormattedResponse, OutputParams};
|
||||||
use nym_mixnet_contract_common::reward_params::Performance;
|
use nym_mixnet_contract_common::reward_params::Performance;
|
||||||
use nym_mixnet_contract_common::{reward_params::RewardingParams, GatewayBond, Interval, NodeId};
|
use nym_mixnet_contract_common::{reward_params::RewardingParams, GatewayBond, Interval, NodeId};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
@@ -55,23 +56,32 @@ pub(crate) fn nym_contract_cache_routes() -> Router<AppState> {
|
|||||||
get,
|
get,
|
||||||
path = "/v1/mixnodes",
|
path = "/v1/mixnodes",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = Vec<LegacyMixNodeDetailsWithLayer>)
|
(status = 200, content(
|
||||||
)
|
(Vec<LegacyMixNodeDetailsWithLayer> = "application/json"),
|
||||||
|
(Vec<LegacyMixNodeDetailsWithLayer> = "application/yaml"),
|
||||||
|
(Vec<LegacyMixNodeDetailsWithLayer> = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
async fn get_mixnodes(State(state): State<AppState>) -> Json<Vec<LegacyMixNodeDetailsWithLayer>> {
|
async fn get_mixnodes(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
|
State(state): State<AppState>,
|
||||||
|
) -> FormattedResponse<Vec<LegacyMixNodeDetailsWithLayer>> {
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
let mut out = state.nym_contract_cache().legacy_mixnodes_filtered().await;
|
let mut out = state.nym_contract_cache().legacy_mixnodes_filtered().await;
|
||||||
|
|
||||||
let Ok(describe_cache) = state.described_nodes_cache.get().await else {
|
let Ok(describe_cache) = state.described_nodes_cache.get().await else {
|
||||||
return Json(out);
|
return output.to_response(out);
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(migrated_nymnodes) = state.nym_contract_cache().all_cached_nym_nodes().await else {
|
let Some(migrated_nymnodes) = state.nym_contract_cache().all_cached_nym_nodes().await else {
|
||||||
return Json(out);
|
return output.to_response(out);
|
||||||
};
|
};
|
||||||
|
|
||||||
let Ok(annotations) = state.node_annotations().await else {
|
let Ok(annotations) = state.node_annotations().await else {
|
||||||
return Json(out);
|
return output.to_response(out);
|
||||||
};
|
};
|
||||||
|
|
||||||
// safety: valid percentage value
|
// safety: valid percentage value
|
||||||
@@ -100,7 +110,7 @@ async fn get_mixnodes(State(state): State<AppState>) -> Json<Vec<LegacyMixNodeDe
|
|||||||
out.push(node);
|
out.push(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
Json(out)
|
output.to_response(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DEPRECATED: this endpoint now lives in `node_status_api`. Once all consumers are updated,
|
// DEPRECATED: this endpoint now lives in `node_status_api`. Once all consumers are updated,
|
||||||
@@ -115,14 +125,22 @@ async fn get_mixnodes(State(state): State<AppState>) -> Json<Vec<LegacyMixNodeDe
|
|||||||
get,
|
get,
|
||||||
path = "/v1/mixnodes/detailed",
|
path = "/v1/mixnodes/detailed",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = Vec<MixNodeBondAnnotated>)
|
(status = 200, content(
|
||||||
)
|
(Vec<MixNodeBondAnnotated> = "application/json"),
|
||||||
|
(Vec<MixNodeBondAnnotated> = "application/yaml"),
|
||||||
|
(Vec<MixNodeBondAnnotated> = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
async fn get_mixnodes_detailed(State(state): State<AppState>) -> Json<Vec<MixNodeBondAnnotated>> {
|
async fn get_mixnodes_detailed(
|
||||||
_get_legacy_mixnodes_detailed(state.node_status_cache())
|
Query(output): Query<OutputParams>,
|
||||||
.await
|
State(state): State<AppState>,
|
||||||
.into()
|
) -> FormattedResponse<Vec<MixNodeBondAnnotated>> {
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
|
output.to_response(_get_legacy_mixnodes_detailed(state.node_status_cache()).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -130,11 +148,21 @@ async fn get_mixnodes_detailed(State(state): State<AppState>) -> Json<Vec<MixNod
|
|||||||
get,
|
get,
|
||||||
path = "/v1/gateways",
|
path = "/v1/gateways",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = Vec<GatewayBond>)
|
(status = 200, content(
|
||||||
)
|
(Vec<GatewayBond> = "application/json"),
|
||||||
|
(Vec<GatewayBond> = "application/yaml"),
|
||||||
|
(Vec<GatewayBond> = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
async fn get_gateways(State(state): State<AppState>) -> Json<Vec<GatewayBond>> {
|
async fn get_gateways(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
|
State(state): State<AppState>,
|
||||||
|
) -> FormattedResponse<Vec<GatewayBond>> {
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
// legacy
|
// legacy
|
||||||
let mut out: Vec<GatewayBond> = state
|
let mut out: Vec<GatewayBond> = state
|
||||||
.nym_contract_cache()
|
.nym_contract_cache()
|
||||||
@@ -145,15 +173,15 @@ async fn get_gateways(State(state): State<AppState>) -> Json<Vec<GatewayBond>> {
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let Ok(describe_cache) = state.described_nodes_cache.get().await else {
|
let Ok(describe_cache) = state.described_nodes_cache.get().await else {
|
||||||
return Json(out);
|
return output.to_response(out);
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(migrated_nymnodes) = state.nym_contract_cache().all_cached_nym_nodes().await else {
|
let Some(migrated_nymnodes) = state.nym_contract_cache().all_cached_nym_nodes().await else {
|
||||||
return Json(out);
|
return output.to_response(out);
|
||||||
};
|
};
|
||||||
|
|
||||||
let Ok(annotations) = state.node_annotations().await else {
|
let Ok(annotations) = state.node_annotations().await else {
|
||||||
return Json(out);
|
return output.to_response(out);
|
||||||
};
|
};
|
||||||
|
|
||||||
// safety: valid percentage value
|
// safety: valid percentage value
|
||||||
@@ -182,7 +210,7 @@ async fn get_gateways(State(state): State<AppState>) -> Json<Vec<GatewayBond>> {
|
|||||||
out.push(node);
|
out.push(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
Json(out)
|
output.to_response(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -190,14 +218,22 @@ async fn get_gateways(State(state): State<AppState>) -> Json<Vec<GatewayBond>> {
|
|||||||
get,
|
get,
|
||||||
path = "/v1/mixnodes/rewarded",
|
path = "/v1/mixnodes/rewarded",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = Vec<LegacyMixNodeDetailsWithLayer>)
|
(status = 200, content(
|
||||||
)
|
(Vec<LegacyMixNodeDetailsWithLayer> = "application/json"),
|
||||||
|
(Vec<LegacyMixNodeDetailsWithLayer> = "application/yaml"),
|
||||||
|
(Vec<LegacyMixNodeDetailsWithLayer> = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
async fn get_rewarded_set(
|
async fn get_rewarded_set(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> Json<Vec<LegacyMixNodeDetailsWithLayer>> {
|
) -> FormattedResponse<Vec<LegacyMixNodeDetailsWithLayer>> {
|
||||||
Json(
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
|
output.to_response(
|
||||||
state
|
state
|
||||||
.nym_contract_cache()
|
.nym_contract_cache()
|
||||||
.legacy_v1_rewarded_set_mixnodes()
|
.legacy_v1_rewarded_set_mixnodes()
|
||||||
@@ -218,19 +254,28 @@ async fn get_rewarded_set(
|
|||||||
get,
|
get,
|
||||||
path = "/v1/mixnodes/rewarded/detailed",
|
path = "/v1/mixnodes/rewarded/detailed",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = Vec<MixNodeBondAnnotated>)
|
(status = 200, content(
|
||||||
)
|
(Vec<MixNodeBondAnnotated> = "application/json"),
|
||||||
|
(Vec<MixNodeBondAnnotated> = "application/yaml"),
|
||||||
|
(Vec<MixNodeBondAnnotated> = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
async fn get_rewarded_set_detailed(
|
async fn get_rewarded_set_detailed(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> Json<Vec<MixNodeBondAnnotated>> {
|
) -> FormattedResponse<Vec<MixNodeBondAnnotated>> {
|
||||||
_get_rewarded_set_legacy_mixnodes_detailed(
|
let output = output.output.unwrap_or_default();
|
||||||
state.node_status_cache(),
|
|
||||||
state.nym_contract_cache(),
|
output.to_response(
|
||||||
|
_get_rewarded_set_legacy_mixnodes_detailed(
|
||||||
|
state.node_status_cache(),
|
||||||
|
state.nym_contract_cache(),
|
||||||
|
)
|
||||||
|
.await,
|
||||||
)
|
)
|
||||||
.await
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -238,11 +283,21 @@ async fn get_rewarded_set_detailed(
|
|||||||
get,
|
get,
|
||||||
path = "/v1/mixnodes/active",
|
path = "/v1/mixnodes/active",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = Vec<LegacyMixNodeDetailsWithLayer>)
|
(status = 200, content(
|
||||||
)
|
(Vec<LegacyMixNodeDetailsWithLayer> = "application/json"),
|
||||||
|
(Vec<LegacyMixNodeDetailsWithLayer> = "application/yaml"),
|
||||||
|
(Vec<LegacyMixNodeDetailsWithLayer> = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
async fn get_active_set(State(state): State<AppState>) -> Json<Vec<LegacyMixNodeDetailsWithLayer>> {
|
async fn get_active_set(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
|
State(state): State<AppState>,
|
||||||
|
) -> FormattedResponse<Vec<LegacyMixNodeDetailsWithLayer>> {
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
let mut out = state
|
let mut out = state
|
||||||
.nym_contract_cache()
|
.nym_contract_cache()
|
||||||
.legacy_v1_active_set_mixnodes()
|
.legacy_v1_active_set_mixnodes()
|
||||||
@@ -250,19 +305,19 @@ async fn get_active_set(State(state): State<AppState>) -> Json<Vec<LegacyMixNode
|
|||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
let Some(rewarded_set) = state.nym_contract_cache().rewarded_set().await else {
|
let Some(rewarded_set) = state.nym_contract_cache().rewarded_set().await else {
|
||||||
return Json(out);
|
return output.to_response(out);
|
||||||
};
|
};
|
||||||
|
|
||||||
let Ok(describe_cache) = state.described_nodes_cache.get().await else {
|
let Ok(describe_cache) = state.described_nodes_cache.get().await else {
|
||||||
return Json(out);
|
return output.to_response(out);
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(migrated_nymnodes) = state.nym_contract_cache().all_cached_nym_nodes().await else {
|
let Some(migrated_nymnodes) = state.nym_contract_cache().all_cached_nym_nodes().await else {
|
||||||
return Json(out);
|
return output.to_response(out);
|
||||||
};
|
};
|
||||||
|
|
||||||
let Ok(annotations) = state.node_annotations().await else {
|
let Ok(annotations) = state.node_annotations().await else {
|
||||||
return Json(out);
|
return output.to_response(out);
|
||||||
};
|
};
|
||||||
|
|
||||||
// safety: valid percentage value
|
// safety: valid percentage value
|
||||||
@@ -295,7 +350,7 @@ async fn get_active_set(State(state): State<AppState>) -> Json<Vec<LegacyMixNode
|
|||||||
out.push(node);
|
out.push(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
Json(out)
|
output.to_response(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DEPRECATED: this endpoint now lives in `node_status_api`. Once all consumers are updated,
|
// DEPRECATED: this endpoint now lives in `node_status_api`. Once all consumers are updated,
|
||||||
@@ -311,14 +366,28 @@ async fn get_active_set(State(state): State<AppState>) -> Json<Vec<LegacyMixNode
|
|||||||
get,
|
get,
|
||||||
path = "/v1/mixnodes/active/detailed",
|
path = "/v1/mixnodes/active/detailed",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = Vec<MixNodeBondAnnotated>)
|
(status = 200, content(
|
||||||
)
|
(Vec<MixNodeBondAnnotated> = "application/json"),
|
||||||
|
(Vec<MixNodeBondAnnotated> = "application/yaml"),
|
||||||
|
(Vec<MixNodeBondAnnotated> = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
async fn get_active_set_detailed(State(state): State<AppState>) -> Json<Vec<MixNodeBondAnnotated>> {
|
async fn get_active_set_detailed(
|
||||||
_get_active_set_legacy_mixnodes_detailed(state.node_status_cache(), state.nym_contract_cache())
|
Query(output): Query<OutputParams>,
|
||||||
.await
|
State(state): State<AppState>,
|
||||||
.into()
|
) -> FormattedResponse<Vec<MixNodeBondAnnotated>> {
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
|
output.to_response(
|
||||||
|
_get_active_set_legacy_mixnodes_detailed(
|
||||||
|
state.node_status_cache(),
|
||||||
|
state.nym_contract_cache(),
|
||||||
|
)
|
||||||
|
.await,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -326,22 +395,31 @@ async fn get_active_set_detailed(State(state): State<AppState>) -> Json<Vec<MixN
|
|||||||
get,
|
get,
|
||||||
path = "/v1/mixnodes/blacklisted",
|
path = "/v1/mixnodes/blacklisted",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = Option<HashSet<NodeId>>)
|
(status = 200, content(
|
||||||
)
|
(Option<HashSet<NodeId>> = "application/json"),
|
||||||
|
(Option<HashSet<NodeId>> = "application/yaml"),
|
||||||
|
(Option<HashSet<NodeId>> = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
async fn get_blacklisted_mixnodes(State(state): State<AppState>) -> Json<Option<HashSet<NodeId>>> {
|
async fn get_blacklisted_mixnodes(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
|
State(state): State<AppState>,
|
||||||
|
) -> FormattedResponse<Option<HashSet<NodeId>>> {
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
let blacklist = state
|
let blacklist = state
|
||||||
.nym_contract_cache()
|
.nym_contract_cache()
|
||||||
.mixnodes_blacklist()
|
.mixnodes_blacklist()
|
||||||
.await
|
.await
|
||||||
.to_owned();
|
.to_owned();
|
||||||
if blacklist.is_empty() {
|
if blacklist.is_empty() {
|
||||||
None
|
output.to_response(None)
|
||||||
} else {
|
} else {
|
||||||
Some(blacklist)
|
output.to_response(Some(blacklist))
|
||||||
}
|
}
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -349,18 +427,28 @@ async fn get_blacklisted_mixnodes(State(state): State<AppState>) -> Json<Option<
|
|||||||
get,
|
get,
|
||||||
path = "/v1/gateways/blacklisted",
|
path = "/v1/gateways/blacklisted",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = Option<HashSet<String>>)
|
(status = 200, content(
|
||||||
)
|
(Option<HashSet<NodeId>> = "application/json"),
|
||||||
|
(Option<HashSet<NodeId>> = "application/yaml"),
|
||||||
|
(Option<HashSet<NodeId>> = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
async fn get_blacklisted_gateways(State(state): State<AppState>) -> Json<Option<HashSet<String>>> {
|
async fn get_blacklisted_gateways(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
|
State(state): State<AppState>,
|
||||||
|
) -> FormattedResponse<Option<HashSet<String>>> {
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
let cache = state.nym_contract_cache();
|
let cache = state.nym_contract_cache();
|
||||||
let blacklist = cache.gateways_blacklist().await.clone();
|
let blacklist = cache.gateways_blacklist().await.clone();
|
||||||
if blacklist.is_empty() {
|
if blacklist.is_empty() {
|
||||||
Json(None)
|
output.to_response(None)
|
||||||
} else {
|
} else {
|
||||||
let gateways = cache.legacy_gateways_all().await;
|
let gateways = cache.legacy_gateways_all().await;
|
||||||
Json(Some(
|
output.to_response(Some(
|
||||||
gateways
|
gateways
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|g| blacklist.contains(&g.node_id))
|
.filter(|g| blacklist.contains(&g.node_id))
|
||||||
@@ -375,18 +463,27 @@ async fn get_blacklisted_gateways(State(state): State<AppState>) -> Json<Option<
|
|||||||
get,
|
get,
|
||||||
path = "/v1/epoch/reward_params",
|
path = "/v1/epoch/reward_params",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = Option<RewardingParams>)
|
(status = 200, content(
|
||||||
)
|
(Option<RewardingParams> = "application/json"),
|
||||||
|
(Option<RewardingParams> = "application/yaml"),
|
||||||
|
(Option<RewardingParams> = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
async fn get_interval_reward_params(
|
async fn get_interval_reward_params(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> Json<Option<RewardingParams>> {
|
) -> FormattedResponse<Option<RewardingParams>> {
|
||||||
state
|
let output = output.output.unwrap_or_default();
|
||||||
.nym_contract_cache()
|
|
||||||
.interval_reward_params()
|
output.to_response(
|
||||||
.await
|
state
|
||||||
.to_owned()
|
.nym_contract_cache()
|
||||||
.into()
|
.interval_reward_params()
|
||||||
|
.await
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -394,14 +491,25 @@ async fn get_interval_reward_params(
|
|||||||
get,
|
get,
|
||||||
path = "/v1/epoch/current",
|
path = "/v1/epoch/current",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = Option<Interval>)
|
(status = 200, content(
|
||||||
)
|
(Option<Interval> = "application/json"),
|
||||||
|
(Option<Interval> = "application/yaml"),
|
||||||
|
(Option<Interval> = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
async fn get_current_epoch(State(state): State<AppState>) -> Json<Option<Interval>> {
|
async fn get_current_epoch(
|
||||||
state
|
Query(output): Query<OutputParams>,
|
||||||
.nym_contract_cache()
|
State(state): State<AppState>,
|
||||||
.current_interval()
|
) -> FormattedResponse<Option<Interval>> {
|
||||||
.await
|
let output = output.output.unwrap_or_default();
|
||||||
.to_owned()
|
|
||||||
.into()
|
output.to_response(
|
||||||
|
state
|
||||||
|
.nym_contract_cache()
|
||||||
|
.current_interval()
|
||||||
|
.await
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,11 @@
|
|||||||
|
|
||||||
use crate::support::http::state::AppState;
|
use crate::support::http::state::AppState;
|
||||||
use crate::support::legacy_helpers::{to_legacy_gateway, to_legacy_mixnode};
|
use crate::support::legacy_helpers::{to_legacy_gateway, to_legacy_mixnode};
|
||||||
use axum::extract::State;
|
use axum::extract::{Query, State};
|
||||||
use axum::{Json, Router};
|
use axum::Router;
|
||||||
use nym_api_requests::legacy::LegacyMixNodeBondWithLayer;
|
use nym_api_requests::legacy::LegacyMixNodeBondWithLayer;
|
||||||
use nym_api_requests::models::{LegacyDescribedGateway, LegacyDescribedMixNode};
|
use nym_api_requests::models::{LegacyDescribedGateway, LegacyDescribedMixNode};
|
||||||
|
use nym_http_api_common::{FormattedResponse, OutputParams};
|
||||||
use tower_http::compression::CompressionLayer;
|
use tower_http::compression::CompressionLayer;
|
||||||
|
|
||||||
// we want to mark the routes as deprecated in swagger, but still expose them
|
// we want to mark the routes as deprecated in swagger, but still expose them
|
||||||
@@ -29,22 +30,29 @@ pub(crate) fn legacy_nym_node_routes() -> Router<AppState> {
|
|||||||
get,
|
get,
|
||||||
path = "/v1/gateways/described",
|
path = "/v1/gateways/described",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = Vec<LegacyDescribedGateway>)
|
(status = 200, content(
|
||||||
)
|
(Vec<LegacyDescribedGateway> = "application/json"),
|
||||||
|
(Vec<LegacyDescribedGateway> = "application/yaml"),
|
||||||
|
(Vec<LegacyDescribedGateway> = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
async fn get_gateways_described(
|
async fn get_gateways_described(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> Json<Vec<LegacyDescribedGateway>> {
|
) -> FormattedResponse<Vec<LegacyDescribedGateway>> {
|
||||||
let contract_cache = state.nym_contract_cache();
|
let contract_cache = state.nym_contract_cache();
|
||||||
let describe_cache = state.described_nodes_cache();
|
let describe_cache = state.described_nodes_cache();
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
// legacy
|
// legacy
|
||||||
let legacy = contract_cache.legacy_gateways_filtered().await;
|
let legacy = contract_cache.legacy_gateways_filtered().await;
|
||||||
|
|
||||||
// if the self describe cache is unavailable, well, don't attach describe data and only return legacy gateways
|
// if the self describe cache is unavailable, well, don't attach describe data and only return legacy gateways
|
||||||
let Ok(describe_cache) = describe_cache.get().await else {
|
let Ok(describe_cache) = describe_cache.get().await else {
|
||||||
return Json(legacy.into_iter().map(Into::into).collect());
|
return output.to_response(legacy.into_iter().map(Into::into).collect());
|
||||||
};
|
};
|
||||||
|
|
||||||
let migrated_nymnodes = state.nym_contract_cache().nym_nodes().await;
|
let migrated_nymnodes = state.nym_contract_cache().nym_nodes().await;
|
||||||
@@ -75,7 +83,7 @@ async fn get_gateways_described(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
Json(out)
|
output.to_response(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -83,15 +91,22 @@ async fn get_gateways_described(
|
|||||||
get,
|
get,
|
||||||
path = "/v1/mixnodes/described",
|
path = "/v1/mixnodes/described",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = Vec<LegacyDescribedMixNode>)
|
(status = 200, content(
|
||||||
)
|
(Vec<LegacyDescribedMixNode> = "application/json"),
|
||||||
|
(Vec<LegacyDescribedMixNode> = "application/yaml"),
|
||||||
|
(Vec<LegacyDescribedMixNode> = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
async fn get_mixnodes_described(
|
async fn get_mixnodes_described(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> Json<Vec<LegacyDescribedMixNode>> {
|
) -> FormattedResponse<Vec<LegacyDescribedMixNode>> {
|
||||||
let contract_cache = state.nym_contract_cache();
|
let contract_cache = state.nym_contract_cache();
|
||||||
let describe_cache = state.described_nodes_cache();
|
let describe_cache = state.described_nodes_cache();
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
let legacy: Vec<LegacyMixNodeBondWithLayer> = contract_cache
|
let legacy: Vec<LegacyMixNodeBondWithLayer> = contract_cache
|
||||||
.legacy_mixnodes_filtered()
|
.legacy_mixnodes_filtered()
|
||||||
@@ -102,7 +117,7 @@ async fn get_mixnodes_described(
|
|||||||
|
|
||||||
// if the self describe cache is unavailable, well, don't attach describe data and only return legacy mixnodes
|
// if the self describe cache is unavailable, well, don't attach describe data and only return legacy mixnodes
|
||||||
let Ok(describe_cache) = describe_cache.get().await else {
|
let Ok(describe_cache) = describe_cache.get().await else {
|
||||||
return Json(legacy.into_iter().map(Into::into).collect());
|
return output.to_response(legacy.into_iter().map(Into::into).collect());
|
||||||
};
|
};
|
||||||
|
|
||||||
let migrated_nymnodes = state.nym_contract_cache().nym_nodes().await;
|
let migrated_nymnodes = state.nym_contract_cache().nym_nodes().await;
|
||||||
@@ -131,5 +146,5 @@ async fn get_mixnodes_described(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
Json(out)
|
output.to_response(out)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ use nym_api_requests::models::{
|
|||||||
};
|
};
|
||||||
use nym_api_requests::pagination::{PaginatedResponse, Pagination};
|
use nym_api_requests::pagination::{PaginatedResponse, Pagination};
|
||||||
use nym_contracts_common::NaiveFloat;
|
use nym_contracts_common::NaiveFloat;
|
||||||
|
use nym_http_api_common::{FormattedResponse, Output, OutputParams};
|
||||||
use nym_mixnet_contract_common::reward_params::Performance;
|
use nym_mixnet_contract_common::reward_params::Performance;
|
||||||
use nym_mixnet_contract_common::NymNodeDetails;
|
use nym_mixnet_contract_common::NymNodeDetails;
|
||||||
use schemars::JsonSchema;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use time::{Date, OffsetDateTime};
|
use time::{Date, OffsetDateTime};
|
||||||
@@ -55,10 +55,20 @@ pub(crate) fn nym_node_routes() -> Router<AppState> {
|
|||||||
path = "/rewarded-set",
|
path = "/rewarded-set",
|
||||||
context_path = "/v1/nym-nodes",
|
context_path = "/v1/nym-nodes",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = RewardedSetResponse)
|
(status = 200, content(
|
||||||
|
(RewardedSetResponse = "application/json"),
|
||||||
|
(RewardedSetResponse = "application/yaml"),
|
||||||
|
(RewardedSetResponse = "application/bincode")
|
||||||
|
))
|
||||||
),
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
async fn rewarded_set(State(state): State<AppState>) -> AxumResult<Json<RewardedSetResponse>> {
|
async fn rewarded_set(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
|
State(state): State<AppState>,
|
||||||
|
) -> AxumResult<FormattedResponse<RewardedSetResponse>> {
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
let cached_rewarded_set = state
|
let cached_rewarded_set = state
|
||||||
.nym_contract_cache()
|
.nym_contract_cache()
|
||||||
.rewarded_set()
|
.rewarded_set()
|
||||||
@@ -67,7 +77,7 @@ async fn rewarded_set(State(state): State<AppState>) -> AxumResult<Json<Rewarded
|
|||||||
.ok_or(UninitialisedCache)?
|
.ok_or(UninitialisedCache)?
|
||||||
.into_inner();
|
.into_inner();
|
||||||
|
|
||||||
Ok(Json(
|
Ok(output.to_response(
|
||||||
nym_mixnet_contract_common::EpochRewardedSet::from(cached_rewarded_set).into(),
|
nym_mixnet_contract_common::EpochRewardedSet::from(cached_rewarded_set).into(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -136,16 +146,21 @@ async fn refresh_described(
|
|||||||
path = "/noise",
|
path = "/noise",
|
||||||
context_path = "/v1/nym-nodes",
|
context_path = "/v1/nym-nodes",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = PaginatedResponse<NoiseDetails>)
|
(status = 200, content(
|
||||||
|
(PaginatedResponse<NoiseDetails> = "application/json"),
|
||||||
|
(PaginatedResponse<NoiseDetails> = "application/yaml"),
|
||||||
|
(PaginatedResponse<NoiseDetails> = "application/bincode")
|
||||||
|
))
|
||||||
),
|
),
|
||||||
params(PaginationRequest)
|
params(PaginationRequest)
|
||||||
)]
|
)]
|
||||||
async fn nodes_noise(
|
async fn nodes_noise(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Query(pagination): Query<PaginationRequest>,
|
Query(pagination): Query<PaginationRequest>,
|
||||||
) -> AxumResult<Json<PaginatedResponse<NoiseDetails>>> {
|
) -> AxumResult<FormattedResponse<PaginatedResponse<NoiseDetails>>> {
|
||||||
// TODO: implement it
|
// TODO: implement it
|
||||||
let _ = pagination;
|
let _ = pagination;
|
||||||
|
let output = pagination.output.unwrap_or_default();
|
||||||
|
|
||||||
let describe_cache = state.describe_nodes_cache_data().await?;
|
let describe_cache = state.describe_nodes_cache_data().await?;
|
||||||
|
|
||||||
@@ -167,7 +182,7 @@ async fn nodes_noise(
|
|||||||
|
|
||||||
let total = nodes.len();
|
let total = nodes.len();
|
||||||
|
|
||||||
Ok(Json(PaginatedResponse {
|
Ok(output.to_response(PaginatedResponse {
|
||||||
pagination: Pagination {
|
pagination: Pagination {
|
||||||
total,
|
total,
|
||||||
page: 0,
|
page: 0,
|
||||||
@@ -183,21 +198,26 @@ async fn nodes_noise(
|
|||||||
path = "/bonded",
|
path = "/bonded",
|
||||||
context_path = "/v1/nym-nodes",
|
context_path = "/v1/nym-nodes",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = PaginatedResponse<NymNodeDetails>)
|
(status = 200, content(
|
||||||
|
(PaginatedResponse<NymNodeDetails> = "application/json"),
|
||||||
|
(PaginatedResponse<NymNodeDetails> = "application/yaml"),
|
||||||
|
(PaginatedResponse<NymNodeDetails> = "application/bincode")
|
||||||
|
))
|
||||||
),
|
),
|
||||||
params(PaginationRequest)
|
params(PaginationRequest)
|
||||||
)]
|
)]
|
||||||
async fn get_bonded_nodes(
|
async fn get_bonded_nodes(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Query(pagination): Query<PaginationRequest>,
|
Query(pagination): Query<PaginationRequest>,
|
||||||
) -> Json<PaginatedResponse<NymNodeDetails>> {
|
) -> FormattedResponse<PaginatedResponse<NymNodeDetails>> {
|
||||||
// TODO: implement it
|
// TODO: implement it
|
||||||
let _ = pagination;
|
let _ = pagination;
|
||||||
|
let output = pagination.output.unwrap_or_default();
|
||||||
|
|
||||||
let details = state.nym_contract_cache().nym_nodes().await;
|
let details = state.nym_contract_cache().nym_nodes().await;
|
||||||
let total = details.len();
|
let total = details.len();
|
||||||
|
|
||||||
Json(PaginatedResponse {
|
output.to_response(PaginatedResponse {
|
||||||
pagination: Pagination {
|
pagination: Pagination {
|
||||||
total,
|
total,
|
||||||
page: 0,
|
page: 0,
|
||||||
@@ -213,21 +233,26 @@ async fn get_bonded_nodes(
|
|||||||
path = "/described",
|
path = "/described",
|
||||||
context_path = "/v1/nym-nodes",
|
context_path = "/v1/nym-nodes",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = PaginatedResponse<NymNodeDescription>)
|
(status = 200, content(
|
||||||
|
(PaginatedResponse<NymNodeDescription> = "application/json"),
|
||||||
|
(PaginatedResponse<NymNodeDescription> = "application/yaml"),
|
||||||
|
(PaginatedResponse<NymNodeDescription> = "application/bincode")
|
||||||
|
))
|
||||||
),
|
),
|
||||||
params(PaginationRequest)
|
params(PaginationRequest)
|
||||||
)]
|
)]
|
||||||
async fn get_described_nodes(
|
async fn get_described_nodes(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Query(pagination): Query<PaginationRequest>,
|
Query(pagination): Query<PaginationRequest>,
|
||||||
) -> AxumResult<Json<PaginatedResponse<NymNodeDescription>>> {
|
) -> AxumResult<FormattedResponse<PaginatedResponse<NymNodeDescription>>> {
|
||||||
// TODO: implement it
|
// TODO: implement it
|
||||||
let _ = pagination;
|
let _ = pagination;
|
||||||
|
let output = pagination.output.unwrap_or_default();
|
||||||
|
|
||||||
let cache = state.described_nodes_cache.get().await?;
|
let cache = state.described_nodes_cache.get().await?;
|
||||||
let descriptions = cache.all_nodes().cloned().collect::<Vec<_>>();
|
let descriptions = cache.all_nodes().cloned().collect::<Vec<_>>();
|
||||||
|
|
||||||
Ok(Json(PaginatedResponse {
|
Ok(output.to_response(PaginatedResponse {
|
||||||
pagination: Pagination {
|
pagination: Pagination {
|
||||||
total: descriptions.len(),
|
total: descriptions.len(),
|
||||||
page: 0,
|
page: 0,
|
||||||
@@ -243,21 +268,28 @@ async fn get_described_nodes(
|
|||||||
path = "/annotation/{node_id}",
|
path = "/annotation/{node_id}",
|
||||||
context_path = "/v1/nym-nodes",
|
context_path = "/v1/nym-nodes",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = AnnotationResponse)
|
(status = 200, content(
|
||||||
|
(AnnotationResponse = "application/json"),
|
||||||
|
(AnnotationResponse = "application/yaml"),
|
||||||
|
(AnnotationResponse = "application/bincode")
|
||||||
|
))
|
||||||
),
|
),
|
||||||
params(NodeIdParam),
|
params(NodeIdParam, OutputParams),
|
||||||
)]
|
)]
|
||||||
async fn get_node_annotation(
|
async fn get_node_annotation(
|
||||||
Path(NodeIdParam { node_id }): Path<NodeIdParam>,
|
Path(NodeIdParam { node_id }): Path<NodeIdParam>,
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> AxumResult<Json<AnnotationResponse>> {
|
) -> AxumResult<FormattedResponse<AnnotationResponse>> {
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
let annotations = state
|
let annotations = state
|
||||||
.node_status_cache
|
.node_status_cache
|
||||||
.node_annotations()
|
.node_annotations()
|
||||||
.await
|
.await
|
||||||
.ok_or_else(AxumErrorResponse::internal)?;
|
.ok_or_else(AxumErrorResponse::internal)?;
|
||||||
|
|
||||||
Ok(Json(AnnotationResponse {
|
Ok(output.to_response(AnnotationResponse {
|
||||||
node_id,
|
node_id,
|
||||||
annotation: annotations.get(&node_id).cloned(),
|
annotation: annotations.get(&node_id).cloned(),
|
||||||
}))
|
}))
|
||||||
@@ -269,21 +301,28 @@ async fn get_node_annotation(
|
|||||||
path = "/performance/{node_id}",
|
path = "/performance/{node_id}",
|
||||||
context_path = "/v1/nym-nodes",
|
context_path = "/v1/nym-nodes",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = NodePerformanceResponse)
|
(status = 200, content(
|
||||||
|
(NodePerformanceResponse = "application/json"),
|
||||||
|
(NodePerformanceResponse = "application/yaml"),
|
||||||
|
(NodePerformanceResponse = "application/bincode")
|
||||||
|
))
|
||||||
),
|
),
|
||||||
params(NodeIdParam),
|
params(NodeIdParam, OutputParams),
|
||||||
)]
|
)]
|
||||||
async fn get_current_node_performance(
|
async fn get_current_node_performance(
|
||||||
Path(NodeIdParam { node_id }): Path<NodeIdParam>,
|
Path(NodeIdParam { node_id }): Path<NodeIdParam>,
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> AxumResult<Json<NodePerformanceResponse>> {
|
) -> AxumResult<FormattedResponse<NodePerformanceResponse>> {
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
let annotations = state
|
let annotations = state
|
||||||
.node_status_cache
|
.node_status_cache
|
||||||
.node_annotations()
|
.node_annotations()
|
||||||
.await
|
.await
|
||||||
.ok_or_else(AxumErrorResponse::internal)?;
|
.ok_or_else(AxumErrorResponse::internal)?;
|
||||||
|
|
||||||
Ok(Json(NodePerformanceResponse {
|
Ok(output.to_response(NodePerformanceResponse {
|
||||||
node_id,
|
node_id,
|
||||||
performance: annotations
|
performance: annotations
|
||||||
.get(&node_id)
|
.get(&node_id)
|
||||||
@@ -292,12 +331,13 @@ async fn get_current_node_performance(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// todo; probably extract it to requests crate
|
// todo; probably extract it to requests crate
|
||||||
#[derive(Debug, Serialize, Deserialize, Copy, Clone, IntoParams, ToSchema, JsonSchema)]
|
#[derive(Debug, Serialize, Deserialize, Copy, Clone, IntoParams, ToSchema)]
|
||||||
#[into_params(parameter_in = Query)]
|
#[into_params(parameter_in = Query)]
|
||||||
pub(crate) struct DateQuery {
|
pub(crate) struct DateQuery {
|
||||||
#[schema(value_type = String, example = "1970-01-01")]
|
#[schema(value_type = String, example = "1970-01-01")]
|
||||||
#[schemars(with = "String")]
|
|
||||||
pub(crate) date: Date,
|
pub(crate) date: Date,
|
||||||
|
|
||||||
|
pub(crate) output: Option<Output>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -306,21 +346,27 @@ pub(crate) struct DateQuery {
|
|||||||
path = "/historical-performance/{node_id}",
|
path = "/historical-performance/{node_id}",
|
||||||
context_path = "/v1/nym-nodes",
|
context_path = "/v1/nym-nodes",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = NodeDatePerformanceResponse)
|
(status = 200, content(
|
||||||
|
(NodeDatePerformanceResponse = "application/json"),
|
||||||
|
(NodeDatePerformanceResponse = "application/yaml"),
|
||||||
|
(NodeDatePerformanceResponse = "application/bincode")
|
||||||
|
))
|
||||||
),
|
),
|
||||||
params(DateQuery, NodeIdParam)
|
params(DateQuery, NodeIdParam)
|
||||||
)]
|
)]
|
||||||
async fn get_historical_performance(
|
async fn get_historical_performance(
|
||||||
Path(NodeIdParam { node_id }): Path<NodeIdParam>,
|
Path(NodeIdParam { node_id }): Path<NodeIdParam>,
|
||||||
Query(DateQuery { date }): Query<DateQuery>,
|
Query(DateQuery { date, output }): Query<DateQuery>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
) -> AxumResult<Json<NodeDatePerformanceResponse>> {
|
) -> AxumResult<FormattedResponse<NodeDatePerformanceResponse>> {
|
||||||
|
let output = output.unwrap_or_default();
|
||||||
|
|
||||||
let uptime = state
|
let uptime = state
|
||||||
.storage()
|
.storage()
|
||||||
.get_historical_node_uptime_on(node_id, date)
|
.get_historical_node_uptime_on(node_id, date)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(Json(NodeDatePerformanceResponse {
|
Ok(output.to_response(NodeDatePerformanceResponse {
|
||||||
node_id,
|
node_id,
|
||||||
date,
|
date,
|
||||||
performance: uptime.and_then(|u| {
|
performance: uptime.and_then(|u| {
|
||||||
@@ -337,7 +383,11 @@ async fn get_historical_performance(
|
|||||||
path = "/performance-history/{node_id}",
|
path = "/performance-history/{node_id}",
|
||||||
context_path = "/v1/nym-nodes",
|
context_path = "/v1/nym-nodes",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = PerformanceHistoryResponse)
|
(status = 200, content(
|
||||||
|
(PerformanceHistoryResponse = "application/json"),
|
||||||
|
(PerformanceHistoryResponse = "application/yaml"),
|
||||||
|
(PerformanceHistoryResponse = "application/bincode")
|
||||||
|
))
|
||||||
),
|
),
|
||||||
params(PaginationRequest, NodeIdParam)
|
params(PaginationRequest, NodeIdParam)
|
||||||
)]
|
)]
|
||||||
@@ -345,9 +395,10 @@ async fn get_node_performance_history(
|
|||||||
Path(NodeIdParam { node_id }): Path<NodeIdParam>,
|
Path(NodeIdParam { node_id }): Path<NodeIdParam>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Query(pagination): Query<PaginationRequest>,
|
Query(pagination): Query<PaginationRequest>,
|
||||||
) -> AxumResult<Json<PerformanceHistoryResponse>> {
|
) -> AxumResult<FormattedResponse<PerformanceHistoryResponse>> {
|
||||||
// TODO: implement it
|
// TODO: implement it
|
||||||
let _ = pagination;
|
let _ = pagination;
|
||||||
|
let output = pagination.output.unwrap_or_default();
|
||||||
|
|
||||||
let history = state
|
let history = state
|
||||||
.storage()
|
.storage()
|
||||||
@@ -358,7 +409,7 @@ async fn get_node_performance_history(
|
|||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let total = history.len();
|
let total = history.len();
|
||||||
|
|
||||||
Ok(Json(PerformanceHistoryResponse {
|
Ok(output.to_response(PerformanceHistoryResponse {
|
||||||
node_id,
|
node_id,
|
||||||
history: PaginatedResponse {
|
history: PaginatedResponse {
|
||||||
pagination: Pagination {
|
pagination: Pagination {
|
||||||
@@ -377,7 +428,11 @@ async fn get_node_performance_history(
|
|||||||
path = "/uptime-history/{node_id}",
|
path = "/uptime-history/{node_id}",
|
||||||
context_path = "/v1/nym-nodes",
|
context_path = "/v1/nym-nodes",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = PerformanceHistoryResponse)
|
(status = 200, content(
|
||||||
|
(PerformanceHistoryResponse = "application/json"),
|
||||||
|
(PerformanceHistoryResponse = "application/yaml"),
|
||||||
|
(PerformanceHistoryResponse = "application/bincode")
|
||||||
|
))
|
||||||
),
|
),
|
||||||
params(PaginationRequest, NodeIdParam)
|
params(PaginationRequest, NodeIdParam)
|
||||||
)]
|
)]
|
||||||
@@ -385,9 +440,10 @@ async fn get_node_uptime_history(
|
|||||||
Path(NodeIdParam { node_id }): Path<NodeIdParam>,
|
Path(NodeIdParam { node_id }): Path<NodeIdParam>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Query(pagination): Query<PaginationRequest>,
|
Query(pagination): Query<PaginationRequest>,
|
||||||
) -> AxumResult<Json<UptimeHistoryResponse>> {
|
) -> AxumResult<FormattedResponse<UptimeHistoryResponse>> {
|
||||||
// TODO: implement it
|
// TODO: implement it
|
||||||
let _ = pagination;
|
let _ = pagination;
|
||||||
|
let output = pagination.output.unwrap_or_default();
|
||||||
|
|
||||||
let history = state
|
let history = state
|
||||||
.storage()
|
.storage()
|
||||||
@@ -398,7 +454,7 @@ async fn get_node_uptime_history(
|
|||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let total = history.len();
|
let total = history.len();
|
||||||
|
|
||||||
Ok(Json(UptimeHistoryResponse {
|
Ok(output.to_response(UptimeHistoryResponse {
|
||||||
node_id,
|
node_id,
|
||||||
history: PaginatedResponse {
|
history: PaginatedResponse {
|
||||||
pagination: Pagination {
|
pagination: Pagination {
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ use crate::node_status_api::models::{AxumErrorResponse, AxumResult};
|
|||||||
use crate::nym_nodes::handlers::unstable::NodesParamsWithRole;
|
use crate::nym_nodes::handlers::unstable::NodesParamsWithRole;
|
||||||
use crate::support::http::state::AppState;
|
use crate::support::http::state::AppState;
|
||||||
use axum::extract::{Query, State};
|
use axum::extract::{Query, State};
|
||||||
use axum::Json;
|
|
||||||
use nym_api_requests::nym_nodes::{CachedNodesResponse, FullFatNode};
|
use nym_api_requests::nym_nodes::{CachedNodesResponse, FullFatNode};
|
||||||
|
use nym_http_api_common::FormattedResponse;
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
tag = "Unstable Nym Nodes",
|
tag = "Unstable Nym Nodes",
|
||||||
@@ -22,6 +22,6 @@ use nym_api_requests::nym_nodes::{CachedNodesResponse, FullFatNode};
|
|||||||
pub(super) async fn nodes_detailed(
|
pub(super) async fn nodes_detailed(
|
||||||
_state: State<AppState>,
|
_state: State<AppState>,
|
||||||
_query_params: Query<NodesParamsWithRole>,
|
_query_params: Query<NodesParamsWithRole>,
|
||||||
) -> AxumResult<Json<CachedNodesResponse<FullFatNode>>> {
|
) -> AxumResult<FormattedResponse<CachedNodesResponse<FullFatNode>>> {
|
||||||
Err(AxumErrorResponse::not_implemented())
|
Err(AxumErrorResponse::not_implemented())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,12 +30,13 @@ use crate::nym_nodes::handlers::unstable::skimmed::{
|
|||||||
};
|
};
|
||||||
use crate::support::http::helpers::PaginationRequest;
|
use crate::support::http::helpers::PaginationRequest;
|
||||||
use crate::support::http::state::AppState;
|
use crate::support::http::state::AppState;
|
||||||
use axum::extract::State;
|
use axum::extract::{Query, State};
|
||||||
use axum::routing::{get, post};
|
use axum::routing::{get, post};
|
||||||
use axum::{Json, Router};
|
use axum::{Json, Router};
|
||||||
use nym_api_requests::nym_nodes::{
|
use nym_api_requests::nym_nodes::{
|
||||||
NodeRoleQueryParam, NodesByAddressesRequestBody, NodesByAddressesResponse,
|
NodeRoleQueryParam, NodesByAddressesRequestBody, NodesByAddressesResponse,
|
||||||
};
|
};
|
||||||
|
use nym_http_api_common::{FormattedResponse, Output, OutputParams};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use tower_http::compression::CompressionLayer;
|
use tower_http::compression::CompressionLayer;
|
||||||
@@ -98,6 +99,8 @@ struct NodesParamsWithRole {
|
|||||||
// the client already knows about the latest topology state, allowing a `no-updates` response
|
// the client already knows about the latest topology state, allowing a `no-updates` response
|
||||||
// instead of wasting bandwidth serving an unchanged topology.
|
// instead of wasting bandwidth serving an unchanged topology.
|
||||||
epoch_id: Option<u32>,
|
epoch_id: Option<u32>,
|
||||||
|
|
||||||
|
output: Option<Output>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, utoipa::IntoParams)]
|
#[derive(Debug, Deserialize, utoipa::IntoParams)]
|
||||||
@@ -113,6 +116,7 @@ struct NodesParams {
|
|||||||
// the client already knows about the latest topology state, allowing a `no-updates` response
|
// the client already knows about the latest topology state, allowing a `no-updates` response
|
||||||
// instead of wasting bandwidth serving an unchanged topology.
|
// instead of wasting bandwidth serving an unchanged topology.
|
||||||
epoch_id: Option<u32>,
|
epoch_id: Option<u32>,
|
||||||
|
output: Option<Output>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<NodesParamsWithRole> for NodesParams {
|
impl From<NodesParamsWithRole> for NodesParams {
|
||||||
@@ -123,6 +127,7 @@ impl From<NodesParamsWithRole> for NodesParams {
|
|||||||
page: params.page,
|
page: params.page,
|
||||||
per_page: params.per_page,
|
per_page: params.per_page,
|
||||||
epoch_id: params.epoch_id,
|
epoch_id: params.epoch_id,
|
||||||
|
output: params.output,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -130,6 +135,7 @@ impl From<NodesParamsWithRole> for NodesParams {
|
|||||||
impl<'a> From<&'a NodesParams> for PaginationRequest {
|
impl<'a> From<&'a NodesParams> for PaginationRequest {
|
||||||
fn from(params: &'a NodesParams) -> Self {
|
fn from(params: &'a NodesParams) -> Self {
|
||||||
PaginationRequest {
|
PaginationRequest {
|
||||||
|
output: params.output,
|
||||||
page: params.page,
|
page: params.page,
|
||||||
per_page: params.per_page,
|
per_page: params.per_page,
|
||||||
}
|
}
|
||||||
@@ -143,13 +149,19 @@ impl<'a> From<&'a NodesParams> for PaginationRequest {
|
|||||||
path = "/by-addresses",
|
path = "/by-addresses",
|
||||||
context_path = "/v1/unstable/nym-nodes",
|
context_path = "/v1/unstable/nym-nodes",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = NodesByAddressesResponse)
|
(status = 200, content(
|
||||||
)
|
(NodesByAddressesResponse = "application/json"),
|
||||||
|
(NodesByAddressesResponse = "application/yaml"),
|
||||||
|
(NodesByAddressesResponse = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
async fn nodes_by_addresses(
|
async fn nodes_by_addresses(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
state: State<AppState>,
|
state: State<AppState>,
|
||||||
Json(body): Json<NodesByAddressesRequestBody>,
|
Json(body): Json<NodesByAddressesRequestBody>,
|
||||||
) -> AxumResult<Json<NodesByAddressesResponse>> {
|
) -> AxumResult<FormattedResponse<NodesByAddressesResponse>> {
|
||||||
// if the request is too big, simply reject it
|
// if the request is too big, simply reject it
|
||||||
if body.addresses.len() > 100 {
|
if body.addresses.len() > 100 {
|
||||||
return Err(AxumErrorResponse::bad_request(
|
return Err(AxumErrorResponse::bad_request(
|
||||||
@@ -157,6 +169,8 @@ async fn nodes_by_addresses(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
// TODO: perhaps introduce different cache because realistically nym-api will receive
|
// TODO: perhaps introduce different cache because realistically nym-api will receive
|
||||||
// request for the same couple addresses from all nodes in quick succession
|
// request for the same couple addresses from all nodes in quick succession
|
||||||
let describe_cache = state.describe_nodes_cache_data().await?;
|
let describe_cache = state.describe_nodes_cache_data().await?;
|
||||||
@@ -166,5 +180,5 @@ async fn nodes_by_addresses(
|
|||||||
existence.insert(address, describe_cache.node_with_address(address));
|
existence.insert(address, describe_cache.node_with_address(address));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Json(NodesByAddressesResponse { existence }))
|
Ok(output.to_response(NodesByAddressesResponse { existence }))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ use crate::node_status_api::models::{AxumErrorResponse, AxumResult};
|
|||||||
use crate::nym_nodes::handlers::unstable::NodesParamsWithRole;
|
use crate::nym_nodes::handlers::unstable::NodesParamsWithRole;
|
||||||
use crate::support::http::state::AppState;
|
use crate::support::http::state::AppState;
|
||||||
use axum::extract::{Query, State};
|
use axum::extract::{Query, State};
|
||||||
use axum::Json;
|
|
||||||
use nym_api_requests::nym_nodes::{CachedNodesResponse, SemiSkimmedNode};
|
use nym_api_requests::nym_nodes::{CachedNodesResponse, SemiSkimmedNode};
|
||||||
|
use nym_http_api_common::FormattedResponse;
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
tag = "Unstable Nym Nodes",
|
tag = "Unstable Nym Nodes",
|
||||||
@@ -22,6 +22,6 @@ use nym_api_requests::nym_nodes::{CachedNodesResponse, SemiSkimmedNode};
|
|||||||
pub(super) async fn nodes_expanded(
|
pub(super) async fn nodes_expanded(
|
||||||
_state: State<AppState>,
|
_state: State<AppState>,
|
||||||
_query_params: Query<NodesParamsWithRole>,
|
_query_params: Query<NodesParamsWithRole>,
|
||||||
) -> AxumResult<Json<CachedNodesResponse<SemiSkimmedNode>>> {
|
) -> AxumResult<FormattedResponse<CachedNodesResponse<SemiSkimmedNode>>> {
|
||||||
Err(AxumErrorResponse::not_implemented())
|
Err(AxumErrorResponse::not_implemented())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ use crate::nym_nodes::handlers::unstable::{NodesParams, NodesParamsWithRole};
|
|||||||
use crate::support::caching::Cache;
|
use crate::support::caching::Cache;
|
||||||
use crate::support::http::state::AppState;
|
use crate::support::http::state::AppState;
|
||||||
use axum::extract::{Query, State};
|
use axum::extract::{Query, State};
|
||||||
use axum::Json;
|
|
||||||
use nym_api_requests::models::{
|
use nym_api_requests::models::{
|
||||||
NodeAnnotation, NymNodeDescription, OffsetDateTimeJsonSchemaWrapper,
|
NodeAnnotation, NymNodeDescription, OffsetDateTimeJsonSchemaWrapper,
|
||||||
};
|
};
|
||||||
@@ -16,15 +15,18 @@ use nym_api_requests::nym_nodes::{
|
|||||||
CachedNodesResponse, NodeRole, NodeRoleQueryParam, PaginatedCachedNodesResponse, SkimmedNode,
|
CachedNodesResponse, NodeRole, NodeRoleQueryParam, PaginatedCachedNodesResponse, SkimmedNode,
|
||||||
};
|
};
|
||||||
use nym_api_requests::pagination::PaginatedResponse;
|
use nym_api_requests::pagination::PaginatedResponse;
|
||||||
|
use nym_http_api_common::{FormattedResponse, Output};
|
||||||
use nym_mixnet_contract_common::NodeId;
|
use nym_mixnet_contract_common::NodeId;
|
||||||
use nym_topology::CachedEpochRewardedSet;
|
use nym_topology::CachedEpochRewardedSet;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
|
use std::time::Duration;
|
||||||
use tokio::sync::RwLockReadGuard;
|
use tokio::sync::RwLockReadGuard;
|
||||||
use tracing::trace;
|
use tracing::trace;
|
||||||
use utoipa::ToSchema;
|
use utoipa::ToSchema;
|
||||||
|
|
||||||
pub type PaginatedSkimmedNodes = AxumResult<Json<PaginatedCachedNodesResponse<SkimmedNode>>>;
|
pub type PaginatedSkimmedNodes =
|
||||||
|
AxumResult<FormattedResponse<PaginatedCachedNodesResponse<SkimmedNode>>>;
|
||||||
|
|
||||||
/// Given all relevant caches, build part of response for JUST Nym Nodes
|
/// Given all relevant caches, build part of response for JUST Nym Nodes
|
||||||
fn build_nym_nodes_response<'a, NI>(
|
fn build_nym_nodes_response<'a, NI>(
|
||||||
@@ -97,6 +99,7 @@ async fn build_skimmed_nodes_response<'a, NI, LG, Fut, LN>(
|
|||||||
nym_nodes_subset: NI,
|
nym_nodes_subset: NI,
|
||||||
annotated_legacy_nodes_getter: LG,
|
annotated_legacy_nodes_getter: LG,
|
||||||
active_only: bool,
|
active_only: bool,
|
||||||
|
output: Output,
|
||||||
) -> PaginatedSkimmedNodes
|
) -> PaginatedSkimmedNodes
|
||||||
where
|
where
|
||||||
// iterator returning relevant subset of nym-nodes (like mixing nym-nodes, entries, etc.)
|
// iterator returning relevant subset of nym-nodes (like mixing nym-nodes, entries, etc.)
|
||||||
@@ -124,18 +127,20 @@ where
|
|||||||
// (ideally it'd be tied directly to the NI iterator, but I couldn't defeat the compiler)
|
// (ideally it'd be tied directly to the NI iterator, but I couldn't defeat the compiler)
|
||||||
let describe_cache = state.describe_nodes_cache_data().await?;
|
let describe_cache = state.describe_nodes_cache_data().await?;
|
||||||
|
|
||||||
let maybe_interval = state
|
let Some(interval) = state
|
||||||
.nym_contract_cache()
|
.nym_contract_cache()
|
||||||
.current_interval()
|
.current_interval()
|
||||||
.await
|
.await
|
||||||
.to_owned();
|
.to_owned()
|
||||||
|
else {
|
||||||
|
// if we can't obtain interval information, it means caches are not valid
|
||||||
|
return Err(AxumErrorResponse::service_unavailable());
|
||||||
|
};
|
||||||
|
|
||||||
// 4.0 If the client indicates that they already know about the current topology send empty response
|
// 4.0 If the client indicates that they already know about the current topology send empty response
|
||||||
if let Some(client_known_epoch) = query_params.epoch_id {
|
if let Some(client_known_epoch) = query_params.epoch_id {
|
||||||
if let Some(ref interval) = maybe_interval {
|
if client_known_epoch == interval.current_epoch_id() {
|
||||||
if client_known_epoch == interval.current_epoch_id() {
|
return Ok(output.to_response(PaginatedCachedNodesResponse::no_updates()));
|
||||||
return Ok(Json(PaginatedCachedNodesResponse::no_updates()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,8 +157,8 @@ where
|
|||||||
describe_cache.timestamp(),
|
describe_cache.timestamp(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return Ok(Json(
|
return Ok(output.to_response(
|
||||||
PaginatedCachedNodesResponse::new_full(refreshed_at, nodes).fresh(maybe_interval),
|
PaginatedCachedNodesResponse::new_full(refreshed_at, nodes).fresh(Some(interval)),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,9 +181,19 @@ where
|
|||||||
annotated_legacy_nodes.timestamp(),
|
annotated_legacy_nodes.timestamp(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Ok(Json(
|
let base_response = output.to_response(
|
||||||
PaginatedCachedNodesResponse::new_full(refreshed_at, nodes).fresh(maybe_interval),
|
PaginatedCachedNodesResponse::new_full(refreshed_at, nodes).fresh(Some(interval)),
|
||||||
))
|
);
|
||||||
|
|
||||||
|
if !active_only {
|
||||||
|
return Ok(base_response);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if caller requested only active nodes, the response is valid until the epoch changes
|
||||||
|
// (but add 2 minutes due to epoch transition not being instantaneous
|
||||||
|
let epoch_end = interval.current_epoch_end();
|
||||||
|
let expiration = epoch_end + Duration::from_secs(120);
|
||||||
|
Ok(base_response.with_expires_header(expiration))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deprecated query that gets ALL gateways
|
/// Deprecated query that gets ALL gateways
|
||||||
@@ -189,22 +204,30 @@ where
|
|||||||
path = "/gateways/skimmed",
|
path = "/gateways/skimmed",
|
||||||
context_path = "/v1/unstable/nym-nodes",
|
context_path = "/v1/unstable/nym-nodes",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = CachedNodesResponse<SkimmedNode>)
|
(status = 200, content(
|
||||||
)
|
(CachedNodesResponse<SkimmedNode> = "application/json"),
|
||||||
|
(CachedNodesResponse<SkimmedNode> = "application/yaml"),
|
||||||
|
(CachedNodesResponse<SkimmedNode> = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
)]
|
)]
|
||||||
#[deprecated(note = "use '/v1/unstable/nym-nodes/entry-gateways/skimmed/all' instead")]
|
#[deprecated(note = "use '/v1/unstable/nym-nodes/entry-gateways/skimmed/all' instead")]
|
||||||
pub(super) async fn deprecated_gateways_basic(
|
pub(super) async fn deprecated_gateways_basic(
|
||||||
state: State<AppState>,
|
state: State<AppState>,
|
||||||
query_params: Query<NodesParams>,
|
query_params: Query<NodesParams>,
|
||||||
) -> AxumResult<Json<CachedNodesResponse<SkimmedNode>>> {
|
) -> AxumResult<FormattedResponse<CachedNodesResponse<SkimmedNode>>> {
|
||||||
|
let output = query_params.output.unwrap_or_default();
|
||||||
|
|
||||||
// 1. call '/v1/unstable/skimmed/entry-gateways/all'
|
// 1. call '/v1/unstable/skimmed/entry-gateways/all'
|
||||||
let all_gateways = entry_gateways_basic_all(state, query_params).await?;
|
let all_gateways = entry_gateways_basic_all(state, query_params)
|
||||||
|
.await?
|
||||||
|
.into_inner();
|
||||||
|
|
||||||
// 3. return result
|
// 3. return result
|
||||||
Ok(Json(CachedNodesResponse {
|
Ok(output.to_response(CachedNodesResponse {
|
||||||
refreshed_at: all_gateways.refreshed_at,
|
refreshed_at: all_gateways.refreshed_at,
|
||||||
// 2. remove pagination
|
// 2. remove pagination
|
||||||
nodes: all_gateways.0.nodes.data,
|
nodes: all_gateways.nodes.data,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,30 +239,40 @@ pub(super) async fn deprecated_gateways_basic(
|
|||||||
path = "/mixnodes/skimmed",
|
path = "/mixnodes/skimmed",
|
||||||
context_path = "/v1/unstable/nym-nodes",
|
context_path = "/v1/unstable/nym-nodes",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = CachedNodesResponse<SkimmedNode>)
|
(status = 200, content(
|
||||||
)
|
(CachedNodesResponse<SkimmedNode> = "application/json"),
|
||||||
|
(CachedNodesResponse<SkimmedNode> = "application/yaml"),
|
||||||
|
(CachedNodesResponse<SkimmedNode> = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
)]
|
)]
|
||||||
#[deprecated(note = "use '/v1/unstable/nym-nodes/skimmed/mixnodes/active' instead")]
|
#[deprecated(note = "use '/v1/unstable/nym-nodes/skimmed/mixnodes/active' instead")]
|
||||||
pub(super) async fn deprecated_mixnodes_basic(
|
pub(super) async fn deprecated_mixnodes_basic(
|
||||||
state: State<AppState>,
|
state: State<AppState>,
|
||||||
query_params: Query<NodesParams>,
|
query_params: Query<NodesParams>,
|
||||||
) -> AxumResult<Json<CachedNodesResponse<SkimmedNode>>> {
|
) -> AxumResult<FormattedResponse<CachedNodesResponse<SkimmedNode>>> {
|
||||||
|
let output = query_params.output.unwrap_or_default();
|
||||||
|
|
||||||
// 1. call '/v1/unstable/nym-nodes/skimmed/mixnodes/active'
|
// 1. call '/v1/unstable/nym-nodes/skimmed/mixnodes/active'
|
||||||
let active_mixnodes = mixnodes_basic_active(state, query_params).await?;
|
let active_mixnodes = mixnodes_basic_active(state, query_params)
|
||||||
|
.await?
|
||||||
|
.into_inner();
|
||||||
|
|
||||||
// 3. return result
|
// 3. return result
|
||||||
Ok(Json(CachedNodesResponse {
|
Ok(output.to_response(CachedNodesResponse {
|
||||||
refreshed_at: active_mixnodes.refreshed_at,
|
refreshed_at: active_mixnodes.refreshed_at,
|
||||||
// 2. remove pagination
|
// 2. remove pagination
|
||||||
nodes: active_mixnodes.0.nodes.data,
|
nodes: active_mixnodes.nodes.data,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn nodes_basic(
|
async fn nodes_basic(
|
||||||
state: State<AppState>,
|
state: State<AppState>,
|
||||||
Query(_query_params): Query<NodesParams>,
|
Query(query_params): Query<NodesParams>,
|
||||||
active_only: bool,
|
active_only: bool,
|
||||||
) -> PaginatedSkimmedNodes {
|
) -> PaginatedSkimmedNodes {
|
||||||
|
let output = query_params.output.unwrap_or_default();
|
||||||
|
|
||||||
// unfortunately we have to build the response semi-manually here as we need to add two sources of legacy nodes
|
// unfortunately we have to build the response semi-manually here as we need to add two sources of legacy nodes
|
||||||
|
|
||||||
// 1. grab all relevant described nym-nodes
|
// 1. grab all relevant described nym-nodes
|
||||||
@@ -281,10 +314,7 @@ async fn nodes_basic(
|
|||||||
legacy_gateways.timestamp(),
|
legacy_gateways.timestamp(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Ok(Json(PaginatedCachedNodesResponse::new_full(
|
Ok(output.to_response(PaginatedCachedNodesResponse::new_full(refreshed_at, nodes)))
|
||||||
refreshed_at,
|
|
||||||
nodes,
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)] // not dead, used in OpenAPI docs
|
#[allow(dead_code)] // not dead, used in OpenAPI docs
|
||||||
@@ -305,8 +335,12 @@ pub struct PaginatedCachedNodesResponseSchema {
|
|||||||
path = "",
|
path = "",
|
||||||
context_path = "/v1/unstable/nym-nodes/skimmed",
|
context_path = "/v1/unstable/nym-nodes/skimmed",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = PaginatedCachedNodesResponseSchema)
|
(status = 200, content(
|
||||||
)
|
(PaginatedCachedNodesResponseSchema = "application/json"),
|
||||||
|
(PaginatedCachedNodesResponseSchema = "application/yaml"),
|
||||||
|
(PaginatedCachedNodesResponseSchema = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
)]
|
)]
|
||||||
pub(super) async fn nodes_basic_all(
|
pub(super) async fn nodes_basic_all(
|
||||||
state: State<AppState>,
|
state: State<AppState>,
|
||||||
@@ -338,8 +372,12 @@ pub(super) async fn nodes_basic_all(
|
|||||||
path = "/active",
|
path = "/active",
|
||||||
context_path = "/v1/unstable/nym-nodes/skimmed",
|
context_path = "/v1/unstable/nym-nodes/skimmed",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = PaginatedCachedNodesResponseSchema)
|
(status = 200, content(
|
||||||
)
|
(PaginatedCachedNodesResponseSchema = "application/json"),
|
||||||
|
(PaginatedCachedNodesResponseSchema = "application/yaml"),
|
||||||
|
(PaginatedCachedNodesResponseSchema = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
)]
|
)]
|
||||||
pub(super) async fn nodes_basic_active(
|
pub(super) async fn nodes_basic_active(
|
||||||
state: State<AppState>,
|
state: State<AppState>,
|
||||||
@@ -367,6 +405,8 @@ async fn mixnodes_basic(
|
|||||||
query_params: Query<NodesParams>,
|
query_params: Query<NodesParams>,
|
||||||
active_only: bool,
|
active_only: bool,
|
||||||
) -> PaginatedSkimmedNodes {
|
) -> PaginatedSkimmedNodes {
|
||||||
|
let output = query_params.output.unwrap_or_default();
|
||||||
|
|
||||||
// 1. grab all relevant described nym-nodes
|
// 1. grab all relevant described nym-nodes
|
||||||
let describe_cache = state.describe_nodes_cache_data().await?;
|
let describe_cache = state.describe_nodes_cache_data().await?;
|
||||||
let mixing_nym_nodes = describe_cache.mixing_nym_nodes();
|
let mixing_nym_nodes = describe_cache.mixing_nym_nodes();
|
||||||
@@ -377,6 +417,7 @@ async fn mixnodes_basic(
|
|||||||
mixing_nym_nodes,
|
mixing_nym_nodes,
|
||||||
|state| state.legacy_mixnode_annotations(),
|
|state| state.legacy_mixnode_annotations(),
|
||||||
active_only,
|
active_only,
|
||||||
|
output,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@@ -390,8 +431,12 @@ async fn mixnodes_basic(
|
|||||||
path = "/mixnodes/all",
|
path = "/mixnodes/all",
|
||||||
context_path = "/v1/unstable/nym-nodes/skimmed",
|
context_path = "/v1/unstable/nym-nodes/skimmed",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = PaginatedCachedNodesResponseSchema)
|
(status = 200, content(
|
||||||
)
|
(PaginatedCachedNodesResponseSchema = "application/json"),
|
||||||
|
(PaginatedCachedNodesResponseSchema = "application/yaml"),
|
||||||
|
(PaginatedCachedNodesResponseSchema = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
)]
|
)]
|
||||||
pub(super) async fn mixnodes_basic_all(
|
pub(super) async fn mixnodes_basic_all(
|
||||||
state: State<AppState>,
|
state: State<AppState>,
|
||||||
@@ -409,8 +454,12 @@ pub(super) async fn mixnodes_basic_all(
|
|||||||
path = "/mixnodes/active",
|
path = "/mixnodes/active",
|
||||||
context_path = "/v1/unstable/nym-nodes/skimmed",
|
context_path = "/v1/unstable/nym-nodes/skimmed",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = PaginatedCachedNodesResponseSchema)
|
(status = 200, content(
|
||||||
)
|
(PaginatedCachedNodesResponseSchema = "application/json"),
|
||||||
|
(PaginatedCachedNodesResponseSchema = "application/yaml"),
|
||||||
|
(PaginatedCachedNodesResponseSchema = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
)]
|
)]
|
||||||
pub(super) async fn mixnodes_basic_active(
|
pub(super) async fn mixnodes_basic_active(
|
||||||
state: State<AppState>,
|
state: State<AppState>,
|
||||||
@@ -424,6 +473,8 @@ async fn entry_gateways_basic(
|
|||||||
query_params: Query<NodesParams>,
|
query_params: Query<NodesParams>,
|
||||||
active_only: bool,
|
active_only: bool,
|
||||||
) -> PaginatedSkimmedNodes {
|
) -> PaginatedSkimmedNodes {
|
||||||
|
let output = query_params.output.unwrap_or_default();
|
||||||
|
|
||||||
// 1. grab all relevant described nym-nodes
|
// 1. grab all relevant described nym-nodes
|
||||||
let describe_cache = state.describe_nodes_cache_data().await?;
|
let describe_cache = state.describe_nodes_cache_data().await?;
|
||||||
let mixing_nym_nodes = describe_cache.entry_capable_nym_nodes();
|
let mixing_nym_nodes = describe_cache.entry_capable_nym_nodes();
|
||||||
@@ -434,6 +485,7 @@ async fn entry_gateways_basic(
|
|||||||
mixing_nym_nodes,
|
mixing_nym_nodes,
|
||||||
|state| state.legacy_gateways_annotations(),
|
|state| state.legacy_gateways_annotations(),
|
||||||
active_only,
|
active_only,
|
||||||
|
output,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@@ -447,8 +499,12 @@ async fn entry_gateways_basic(
|
|||||||
path = "/entry-gateways/active",
|
path = "/entry-gateways/active",
|
||||||
context_path = "/v1/unstable/nym-nodes/skimmed",
|
context_path = "/v1/unstable/nym-nodes/skimmed",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = PaginatedCachedNodesResponseSchema)
|
(status = 200, content(
|
||||||
)
|
(PaginatedCachedNodesResponseSchema = "application/json"),
|
||||||
|
(PaginatedCachedNodesResponseSchema = "application/yaml"),
|
||||||
|
(PaginatedCachedNodesResponseSchema = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
)]
|
)]
|
||||||
pub(super) async fn entry_gateways_basic_active(
|
pub(super) async fn entry_gateways_basic_active(
|
||||||
state: State<AppState>,
|
state: State<AppState>,
|
||||||
@@ -466,8 +522,12 @@ pub(super) async fn entry_gateways_basic_active(
|
|||||||
path = "/entry-gateways/all",
|
path = "/entry-gateways/all",
|
||||||
context_path = "/v1/unstable/nym-nodes/skimmed",
|
context_path = "/v1/unstable/nym-nodes/skimmed",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = PaginatedCachedNodesResponseSchema)
|
(status = 200, content(
|
||||||
)
|
(PaginatedCachedNodesResponseSchema = "application/json"),
|
||||||
|
(PaginatedCachedNodesResponseSchema = "application/yaml"),
|
||||||
|
(PaginatedCachedNodesResponseSchema = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
)]
|
)]
|
||||||
pub(super) async fn entry_gateways_basic_all(
|
pub(super) async fn entry_gateways_basic_all(
|
||||||
state: State<AppState>,
|
state: State<AppState>,
|
||||||
@@ -481,6 +541,8 @@ async fn exit_gateways_basic(
|
|||||||
query_params: Query<NodesParams>,
|
query_params: Query<NodesParams>,
|
||||||
active_only: bool,
|
active_only: bool,
|
||||||
) -> PaginatedSkimmedNodes {
|
) -> PaginatedSkimmedNodes {
|
||||||
|
let output = query_params.output.unwrap_or_default();
|
||||||
|
|
||||||
// 1. grab all relevant described nym-nodes
|
// 1. grab all relevant described nym-nodes
|
||||||
let describe_cache = state.describe_nodes_cache_data().await?;
|
let describe_cache = state.describe_nodes_cache_data().await?;
|
||||||
let mixing_nym_nodes = describe_cache.exit_capable_nym_nodes();
|
let mixing_nym_nodes = describe_cache.exit_capable_nym_nodes();
|
||||||
@@ -491,6 +553,7 @@ async fn exit_gateways_basic(
|
|||||||
mixing_nym_nodes,
|
mixing_nym_nodes,
|
||||||
|state| state.legacy_gateways_annotations(),
|
|state| state.legacy_gateways_annotations(),
|
||||||
active_only,
|
active_only,
|
||||||
|
output,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@@ -504,8 +567,12 @@ async fn exit_gateways_basic(
|
|||||||
path = "/exit-gateways/active",
|
path = "/exit-gateways/active",
|
||||||
context_path = "/v1/unstable/nym-nodes/skimmed",
|
context_path = "/v1/unstable/nym-nodes/skimmed",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = PaginatedCachedNodesResponseSchema)
|
(status = 200, content(
|
||||||
)
|
(PaginatedCachedNodesResponseSchema = "application/json"),
|
||||||
|
(PaginatedCachedNodesResponseSchema = "application/yaml"),
|
||||||
|
(PaginatedCachedNodesResponseSchema = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
)]
|
)]
|
||||||
pub(super) async fn exit_gateways_basic_active(
|
pub(super) async fn exit_gateways_basic_active(
|
||||||
state: State<AppState>,
|
state: State<AppState>,
|
||||||
@@ -523,8 +590,12 @@ pub(super) async fn exit_gateways_basic_active(
|
|||||||
path = "/exit-gateways/all",
|
path = "/exit-gateways/all",
|
||||||
context_path = "/v1/unstable/nym-nodes/skimmed",
|
context_path = "/v1/unstable/nym-nodes/skimmed",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = PaginatedCachedNodesResponseSchema)
|
(status = 200, content(
|
||||||
)
|
(PaginatedCachedNodesResponseSchema = "application/json"),
|
||||||
|
(PaginatedCachedNodesResponseSchema = "application/yaml"),
|
||||||
|
(PaginatedCachedNodesResponseSchema = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
)]
|
)]
|
||||||
pub(super) async fn exit_gateways_basic_all(
|
pub(super) async fn exit_gateways_basic_all(
|
||||||
state: State<AppState>,
|
state: State<AppState>,
|
||||||
|
|||||||
@@ -4,14 +4,14 @@
|
|||||||
use crate::node_status_api::models::{AxumErrorResponse, AxumResult};
|
use crate::node_status_api::models::{AxumErrorResponse, AxumResult};
|
||||||
use crate::status::ApiStatusState;
|
use crate::status::ApiStatusState;
|
||||||
use crate::support::http::state::AppState;
|
use crate::support::http::state::AppState;
|
||||||
use axum::extract::State;
|
use axum::extract::{Query, State};
|
||||||
use axum::Json;
|
|
||||||
use axum::Router;
|
use axum::Router;
|
||||||
use nym_api_requests::models::{
|
use nym_api_requests::models::{
|
||||||
ApiHealthResponse, ApiStatus, ChainStatus, SignerInformationResponse,
|
ApiHealthResponse, ApiStatus, ChainStatus, SignerInformationResponse,
|
||||||
};
|
};
|
||||||
use nym_bin_common::build_information::BinaryBuildInformationOwned;
|
use nym_bin_common::build_information::BinaryBuildInformationOwned;
|
||||||
use nym_compact_ecash::Base58;
|
use nym_compact_ecash::Base58;
|
||||||
|
use nym_http_api_common::{FormattedResponse, OutputParams};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
@@ -30,12 +30,22 @@ pub(crate) fn api_status_routes() -> Router<AppState> {
|
|||||||
get,
|
get,
|
||||||
path = "/v1/api-status/health",
|
path = "/v1/api-status/health",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = ApiHealthResponse)
|
(status = 200, content(
|
||||||
)
|
(ApiHealthResponse = "application/json"),
|
||||||
|
(ApiHealthResponse = "application/yaml"),
|
||||||
|
(ApiHealthResponse = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
async fn health(State(state): State<AppState>) -> Json<ApiHealthResponse> {
|
async fn health(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
|
State(state): State<AppState>,
|
||||||
|
) -> FormattedResponse<ApiHealthResponse> {
|
||||||
const CHAIN_STALL_THRESHOLD: Duration = Duration::from_secs(5 * 60);
|
const CHAIN_STALL_THRESHOLD: Duration = Duration::from_secs(5 * 60);
|
||||||
|
|
||||||
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
let uptime = state.api_status.startup_time.elapsed();
|
let uptime = state.api_status.startup_time.elapsed();
|
||||||
let chain_status = match state
|
let chain_status = match state
|
||||||
.chain_status_cache
|
.chain_status_cache
|
||||||
@@ -61,7 +71,7 @@ async fn health(State(state): State<AppState>) -> Json<ApiHealthResponse> {
|
|||||||
chain_status,
|
chain_status,
|
||||||
uptime: uptime.as_secs(),
|
uptime: uptime.as_secs(),
|
||||||
};
|
};
|
||||||
Json(health)
|
output.to_response(health)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -69,13 +79,21 @@ async fn health(State(state): State<AppState>) -> Json<ApiHealthResponse> {
|
|||||||
get,
|
get,
|
||||||
path = "/v1/api-status/build-information",
|
path = "/v1/api-status/build-information",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = BinaryBuildInformationOwned)
|
(status = 200, content(
|
||||||
)
|
(BinaryBuildInformationOwned = "application/json"),
|
||||||
|
(BinaryBuildInformationOwned = "application/yaml"),
|
||||||
|
(BinaryBuildInformationOwned = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
async fn build_information(
|
async fn build_information(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<ApiStatusState>,
|
State(state): State<ApiStatusState>,
|
||||||
) -> Json<BinaryBuildInformationOwned> {
|
) -> FormattedResponse<BinaryBuildInformationOwned> {
|
||||||
Json(state.build_information.to_owned())
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
|
output.to_response(state.build_information.to_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -83,17 +101,25 @@ async fn build_information(
|
|||||||
get,
|
get,
|
||||||
path = "/v1/api-status/signer-information",
|
path = "/v1/api-status/signer-information",
|
||||||
responses(
|
responses(
|
||||||
(status = 200, body = SignerInformationResponse)
|
(status = 200, content(
|
||||||
)
|
(SignerInformationResponse = "application/json"),
|
||||||
|
(SignerInformationResponse = "application/yaml"),
|
||||||
|
(SignerInformationResponse = "application/bincode")
|
||||||
|
))
|
||||||
|
),
|
||||||
|
params(OutputParams)
|
||||||
)]
|
)]
|
||||||
async fn signer_information(
|
async fn signer_information(
|
||||||
|
Query(output): Query<OutputParams>,
|
||||||
State(state): State<ApiStatusState>,
|
State(state): State<ApiStatusState>,
|
||||||
) -> AxumResult<Json<SignerInformationResponse>> {
|
) -> AxumResult<FormattedResponse<SignerInformationResponse>> {
|
||||||
let signer_state = state.signer_information.as_ref().ok_or_else(|| {
|
let signer_state = state.signer_information.as_ref().ok_or_else(|| {
|
||||||
AxumErrorResponse::internal_msg("this api does not expose zk-nym signing functionalities")
|
AxumErrorResponse::internal_msg("this api does not expose zk-nym signing functionalities")
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(Json(SignerInformationResponse {
|
let output = output.output.unwrap_or_default();
|
||||||
|
|
||||||
|
Ok(output.to_response(SignerInformationResponse {
|
||||||
cosmos_address: signer_state.cosmos_address.clone(),
|
cosmos_address: signer_state.cosmos_address.clone(),
|
||||||
identity: signer_state.identity.clone(),
|
identity: signer_state.identity.clone(),
|
||||||
announce_address: signer_state.announce_address.clone(),
|
announce_address: signer_state.announce_address.clone(),
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
use nym_http_api_common::Output;
|
||||||
use nym_mixnet_contract_common::NodeId;
|
use nym_mixnet_contract_common::NodeId;
|
||||||
use schemars::JsonSchema;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use utoipa::{IntoParams, ToSchema};
|
use utoipa::{IntoParams, ToSchema};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, JsonSchema, ToSchema, IntoParams)]
|
#[derive(Serialize, Deserialize, Debug, ToSchema, IntoParams)]
|
||||||
#[into_params(parameter_in = Query)]
|
#[into_params(parameter_in = Query)]
|
||||||
pub struct PaginationRequest {
|
pub struct PaginationRequest {
|
||||||
|
pub output: Option<Output>,
|
||||||
pub page: Option<u32>,
|
pub page: Option<u32>,
|
||||||
pub per_page: Option<u32>,
|
pub per_page: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,6 @@ features = ["tokio"]
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["query-types"]
|
default = ["query-types"]
|
||||||
query-types = ["nym-http-api-common"]
|
query-types = ["nym-http-api-common", "nym-http-api-common/output"]
|
||||||
openapi = ["utoipa"]
|
openapi = ["utoipa"]
|
||||||
tsify = ["dep:tsify", "wasm-bindgen"]
|
tsify = ["dep:tsify", "wasm-bindgen"]
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ impl Monitor {
|
|||||||
.with_timeout(self.nym_api_client_timeout)
|
.with_timeout(self.nym_api_client_timeout)
|
||||||
.build::<&str>()?;
|
.build::<&str>()?;
|
||||||
|
|
||||||
let api_client = NymApiClient { nym_api };
|
let api_client = NymApiClient::from(nym_api);
|
||||||
|
|
||||||
let described_nodes = api_client
|
let described_nodes = api_client
|
||||||
.get_all_described_nodes()
|
.get_all_described_nodes()
|
||||||
@@ -187,7 +187,7 @@ impl Monitor {
|
|||||||
tracing::info!("🟣 mixnodes_described: {}", mixnodes_described.len());
|
tracing::info!("🟣 mixnodes_described: {}", mixnodes_described.len());
|
||||||
let mixing_assigned_nodes = api_client
|
let mixing_assigned_nodes = api_client
|
||||||
.nym_api
|
.nym_api
|
||||||
.get_basic_active_mixing_assigned_nodes(false, None, None)
|
.get_basic_active_mixing_assigned_nodes(false, None, None, false)
|
||||||
.await
|
.await
|
||||||
.log_error("get_basic_active_mixing_assigned_nodes")?
|
.log_error("get_basic_active_mixing_assigned_nodes")?
|
||||||
.nodes
|
.nodes
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ async fn run(
|
|||||||
.with_timeout(nym_api_client_timeout)
|
.with_timeout(nym_api_client_timeout)
|
||||||
.build::<&str>()?;
|
.build::<&str>()?;
|
||||||
|
|
||||||
let api_client = NymApiClient { nym_api };
|
let api_client = NymApiClient::from(nym_api);
|
||||||
|
|
||||||
//SW TBC what nodes exactly need to be scraped, the skimmed node endpoint seems to return more nodes
|
//SW TBC what nodes exactly need to be scraped, the skimmed node endpoint seems to return more nodes
|
||||||
let bonded_nodes = api_client.get_all_bonded_nym_nodes().await?;
|
let bonded_nodes = api_client.get_all_bonded_nym_nodes().await?;
|
||||||
|
|||||||
@@ -842,7 +842,7 @@ impl NymNode {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let client = NymApiClient { nym_api };
|
let client = NymApiClient::from(nym_api);
|
||||||
|
|
||||||
// make new request every time in case previous one takes longer and invalidates the signature
|
// make new request every time in case previous one takes longer and invalidates the signature
|
||||||
let request = NodeRefreshBody::new(self.ed25519_identity_keys.private_key());
|
let request = NodeRefreshBody::new(self.ed25519_identity_keys.private_key());
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ impl NetworkRefresher {
|
|||||||
|
|
||||||
let mut this = NetworkRefresher {
|
let mut this = NetworkRefresher {
|
||||||
querier: NodesQuerier {
|
querier: NodesQuerier {
|
||||||
client: NymApiClient { nym_api },
|
client: NymApiClient::from(nym_api),
|
||||||
nym_api_urls,
|
nym_api_urls,
|
||||||
currently_used_api: 0,
|
currently_used_api: 0,
|
||||||
},
|
},
|
||||||
|
|||||||
Generated
+70
-53
@@ -659,7 +659,7 @@ dependencies = [
|
|||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
"ripemd",
|
"ripemd",
|
||||||
"secp256k1",
|
"secp256k1",
|
||||||
"sha2 0.10.8",
|
"sha2 0.10.9",
|
||||||
"subtle",
|
"subtle",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
@@ -816,7 +816,7 @@ version = "0.5.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
|
checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"sha2 0.10.8",
|
"sha2 0.10.9",
|
||||||
"tinyvec",
|
"tinyvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -931,7 +931,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "02260d489095346e5cafd04dea8e8cb54d1d74fcd759022a9b72986ebe9a1257"
|
checksum = "02260d489095346e5cafd04dea8e8cb54d1d74fcd759022a9b72986ebe9a1257"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"toml 0.8.20",
|
"toml 0.8.22",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1245,7 +1245,7 @@ dependencies = [
|
|||||||
"p256",
|
"p256",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
"rayon",
|
"rayon",
|
||||||
"sha2 0.10.8",
|
"sha2 0.10.9",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1303,7 +1303,7 @@ dependencies = [
|
|||||||
"schemars",
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
"serde-json-wasm",
|
"serde-json-wasm",
|
||||||
"sha2 0.10.8",
|
"sha2 0.10.9",
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
]
|
]
|
||||||
@@ -1902,7 +1902,7 @@ dependencies = [
|
|||||||
"ed25519",
|
"ed25519",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
"serde",
|
"serde",
|
||||||
"sha2 0.10.8",
|
"sha2 0.10.9",
|
||||||
"subtle",
|
"subtle",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
@@ -1918,7 +1918,7 @@ dependencies = [
|
|||||||
"hashbrown 0.14.5",
|
"hashbrown 0.14.5",
|
||||||
"hex",
|
"hex",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
"sha2 0.10.8",
|
"sha2 0.10.9",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1957,7 +1957,7 @@ dependencies = [
|
|||||||
"cc",
|
"cc",
|
||||||
"memchr",
|
"memchr",
|
||||||
"rustc_version",
|
"rustc_version",
|
||||||
"toml 0.8.20",
|
"toml 0.8.22",
|
||||||
"vswhom",
|
"vswhom",
|
||||||
"winreg 0.52.0",
|
"winreg 0.52.0",
|
||||||
]
|
]
|
||||||
@@ -3556,7 +3556,7 @@ dependencies = [
|
|||||||
"ecdsa",
|
"ecdsa",
|
||||||
"elliptic-curve",
|
"elliptic-curve",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"sha2 0.10.8",
|
"sha2 0.10.9",
|
||||||
"signature",
|
"signature",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -3965,7 +3965,7 @@ dependencies = [
|
|||||||
"schemars",
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2 0.10.8",
|
"sha2 0.10.9",
|
||||||
"tendermint",
|
"tendermint",
|
||||||
"tendermint-rpc",
|
"tendermint-rpc",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
@@ -3979,7 +3979,6 @@ version = "0.6.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"const-str",
|
"const-str",
|
||||||
"log",
|
"log",
|
||||||
"pretty_env_logger",
|
|
||||||
"schemars",
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
"utoipa",
|
"utoipa",
|
||||||
@@ -4015,7 +4014,7 @@ dependencies = [
|
|||||||
"nym-pemstore",
|
"nym-pemstore",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"serde",
|
"serde",
|
||||||
"sha2 0.10.8",
|
"sha2 0.10.9",
|
||||||
"subtle",
|
"subtle",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
@@ -4031,7 +4030,7 @@ dependencies = [
|
|||||||
"nym-network-defaults",
|
"nym-network-defaults",
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
"toml 0.8.20",
|
"toml 0.8.22",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4129,12 +4128,14 @@ name = "nym-http-api-client"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
"bincode",
|
||||||
"bytes",
|
"bytes",
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
"hickory-resolver",
|
"hickory-resolver",
|
||||||
"http 1.3.1",
|
"http 1.3.1",
|
||||||
"mime",
|
"mime",
|
||||||
"nym-bin-common",
|
"nym-bin-common",
|
||||||
|
"nym-http-api-common",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"reqwest 0.12.15",
|
"reqwest 0.12.15",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -4145,6 +4146,15 @@ dependencies = [
|
|||||||
"wasmtimer",
|
"wasmtimer",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nym-http-api-common"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bincode",
|
||||||
|
"serde",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-mixnet-contract-common"
|
name = "nym-mixnet-contract-common"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
@@ -4268,7 +4278,7 @@ dependencies = [
|
|||||||
"rs_merkle",
|
"rs_merkle",
|
||||||
"schemars",
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
"sha2 0.10.8",
|
"sha2 0.10.9",
|
||||||
"time",
|
"time",
|
||||||
"utoipa",
|
"utoipa",
|
||||||
]
|
]
|
||||||
@@ -4293,7 +4303,7 @@ dependencies = [
|
|||||||
"schemars",
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2 0.10.8",
|
"sha2 0.10.9",
|
||||||
"strum 0.26.3",
|
"strum 0.26.3",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
"ts-rs",
|
"ts-rs",
|
||||||
@@ -4339,7 +4349,7 @@ dependencies = [
|
|||||||
"reqwest 0.12.15",
|
"reqwest 0.12.15",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2 0.10.8",
|
"sha2 0.10.9",
|
||||||
"tendermint-rpc",
|
"tendermint-rpc",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
"time",
|
"time",
|
||||||
@@ -4759,7 +4769,7 @@ dependencies = [
|
|||||||
"ecdsa",
|
"ecdsa",
|
||||||
"elliptic-curve",
|
"elliptic-curve",
|
||||||
"primeorder",
|
"primeorder",
|
||||||
"sha2 0.10.8",
|
"sha2 0.10.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4934,7 +4944,7 @@ checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"pest",
|
"pest",
|
||||||
"sha2 0.10.8",
|
"sha2 0.10.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5258,7 +5268,7 @@ version = "3.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35"
|
checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"toml_edit 0.22.24",
|
"toml_edit 0.22.26",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5804,7 +5814,7 @@ version = "1.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bb09b49230ba22e8c676e7b75dfe2887dea8121f18b530ae0ba519ce442d2b21"
|
checksum = "bb09b49230ba22e8c676e7b75dfe2887dea8121f18b530ae0ba519ce442d2b21"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"sha2 0.10.8",
|
"sha2 0.10.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -6299,9 +6309,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha2"
|
name = "sha2"
|
||||||
version = "0.10.8"
|
version = "0.10.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
|
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cpufeatures",
|
"cpufeatures",
|
||||||
@@ -6662,7 +6672,7 @@ dependencies = [
|
|||||||
"cfg-expr",
|
"cfg-expr",
|
||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
"toml 0.8.20",
|
"toml 0.8.22",
|
||||||
"version-compare",
|
"version-compare",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -6807,7 +6817,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri-utils",
|
"tauri-utils",
|
||||||
"tauri-winres",
|
"tauri-winres",
|
||||||
"toml 0.8.20",
|
"toml 0.8.22",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -6828,7 +6838,7 @@ dependencies = [
|
|||||||
"semver",
|
"semver",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2 0.10.8",
|
"sha2 0.10.9",
|
||||||
"syn 2.0.100",
|
"syn 2.0.100",
|
||||||
"tauri-utils",
|
"tauri-utils",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
@@ -6865,7 +6875,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri-utils",
|
"tauri-utils",
|
||||||
"toml 0.8.20",
|
"toml 0.8.22",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -7047,7 +7057,7 @@ dependencies = [
|
|||||||
"serde_with",
|
"serde_with",
|
||||||
"swift-rs",
|
"swift-rs",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
"toml 0.8.20",
|
"toml 0.8.22",
|
||||||
"url",
|
"url",
|
||||||
"urlpattern",
|
"urlpattern",
|
||||||
"uuid",
|
"uuid",
|
||||||
@@ -7061,7 +7071,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "56eaa45f707bedf34d19312c26d350bc0f3c59a47e58e8adbeecdc850d2c13a0"
|
checksum = "56eaa45f707bedf34d19312c26d350bc0f3c59a47e58e8adbeecdc850d2c13a0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"embed-resource",
|
"embed-resource",
|
||||||
"toml 0.8.20",
|
"toml 0.8.22",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -7079,9 +7089,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tendermint"
|
name = "tendermint"
|
||||||
version = "0.40.3"
|
version = "0.40.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ab2972a56891bc9173eaebdd51290bc848e448aa281881f3a4712bd8fe1899b3"
|
checksum = "fc997743ecfd4864bbca8170d68d9b2bee24653b034210752c2d883ef4b838b1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"digest 0.10.7",
|
"digest 0.10.7",
|
||||||
@@ -7098,7 +7108,7 @@ dependencies = [
|
|||||||
"serde_bytes",
|
"serde_bytes",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_repr",
|
"serde_repr",
|
||||||
"sha2 0.10.8",
|
"sha2 0.10.9",
|
||||||
"signature",
|
"signature",
|
||||||
"subtle",
|
"subtle",
|
||||||
"subtle-encoding",
|
"subtle-encoding",
|
||||||
@@ -7109,23 +7119,23 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tendermint-config"
|
name = "tendermint-config"
|
||||||
version = "0.40.3"
|
version = "0.40.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "651cb680d41e586bb688cc17ab0b6dfe2e62a959c5742d1351fe0b084cd75d59"
|
checksum = "069d1791f9b02a596abcd26eb72003b2e9906c6169a60fa82ffc080dd3a43fda"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"flex-error",
|
"flex-error",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tendermint",
|
"tendermint",
|
||||||
"toml 0.8.20",
|
"toml 0.8.22",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tendermint-proto"
|
name = "tendermint-proto"
|
||||||
version = "0.40.3"
|
version = "0.40.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ca44b9eaaa98e8440fbafe5593b8a32a1c395c094ac566b3eb50497f8bd2bee0"
|
checksum = "d2c40e13d39ca19082d8a7ed22de7595979350319833698f8b1080f29620a094"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"flex-error",
|
"flex-error",
|
||||||
@@ -7138,9 +7148,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tendermint-rpc"
|
name = "tendermint-rpc"
|
||||||
version = "0.40.3"
|
version = "0.40.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9367678af1a9fc6c064fab8b5d05f03c0fc243fe411581c00c7eed83f8ced380"
|
checksum = "35e0569a4b4cc42ff00df5a665be2858a39ff79df4790b176f1cd0e169bc0fc2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -7387,21 +7397,21 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "0.8.20"
|
version = "0.8.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148"
|
checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
"toml_edit 0.22.24",
|
"toml_edit 0.22.26",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_datetime"
|
name = "toml_datetime"
|
||||||
version = "0.6.8"
|
version = "0.6.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
|
checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
@@ -7430,17 +7440,24 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_edit"
|
name = "toml_edit"
|
||||||
version = "0.22.24"
|
version = "0.22.26"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474"
|
checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap 2.8.0",
|
"indexmap 2.8.0",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
"winnow 0.7.4",
|
"toml_write",
|
||||||
|
"winnow 0.7.10",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_write"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower"
|
name = "tower"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
@@ -8653,9 +8670,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.7.4"
|
version = "0.7.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36"
|
checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
@@ -8750,7 +8767,7 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"sha2 0.10.8",
|
"sha2 0.10.9",
|
||||||
"soup3",
|
"soup3",
|
||||||
"tao-macros",
|
"tao-macros",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
@@ -8887,7 +8904,7 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
"uds_windows",
|
"uds_windows",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
"winnow 0.7.4",
|
"winnow 0.7.10",
|
||||||
"xdg-home",
|
"xdg-home",
|
||||||
"zbus_macros",
|
"zbus_macros",
|
||||||
"zbus_names",
|
"zbus_names",
|
||||||
@@ -8917,7 +8934,7 @@ checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
"winnow 0.7.4",
|
"winnow 0.7.10",
|
||||||
"zvariant",
|
"zvariant",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -9048,7 +9065,7 @@ dependencies = [
|
|||||||
"enumflags2",
|
"enumflags2",
|
||||||
"serde",
|
"serde",
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
"winnow 0.7.4",
|
"winnow 0.7.10",
|
||||||
"zvariant_derive",
|
"zvariant_derive",
|
||||||
"zvariant_utils",
|
"zvariant_utils",
|
||||||
]
|
]
|
||||||
@@ -9077,5 +9094,5 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
"syn 2.0.100",
|
"syn 2.0.100",
|
||||||
"winnow 0.7.4",
|
"winnow 0.7.10",
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user