Compare commits

..

1 Commits

Author SHA1 Message Date
Tommy Verrall 37f0b02cee Sign based upon github ref 2025-04-22 11:36:13 +02:00
50 changed files with 711 additions and 1791 deletions
+12 -8
View File
@@ -44,21 +44,24 @@ jobs:
- name: Download EV CodeSignTool from ssl.com
working-directory: nym-wallet/src-tauri
if: ${{ inputs.sign }}
if: ${{ inputs.sign || startsWith(github.ref, 'refs/tags/nym-wallet-') }}
shell: bash
run: |
curl -L0 https://www.ssl.com/download/codesigntool-for-linux-and-macos/ -o codesigntool.zip
unzip codesigntool.zip
chmod +x ./CodeSignTool.sh
- name: Get EV certificate credential id
working-directory: nym-wallet/src-tauri
if: ${{ inputs.sign }}
if: ${{ inputs.sign || startsWith(github.ref, 'refs/tags/nym-wallet-') }}
id: get_credential_ids
shell: bash
run: |
echo "SSL_COM_CREDENTIAL_ID=$(./CodeSignTool.sh get_credential_ids -username=${{ secrets.SSL_COM_USERNAME }} -password=${{ secrets.SSL_COM_PASSWORD }} | sed -n '1!p' | sed 's/- //')" >> "$GITHUB_OUTPUT"
- name: Add custom sign command to tauri.conf.json
working-directory: nym-wallet/src-tauri
if: ${{ inputs.sign }}
if: ${{ inputs.sign || startsWith(github.ref, 'refs/tags/nym-wallet-') }}
shell: bash
run: |
yq eval --inplace '.bundle.windows +=
@@ -79,6 +82,7 @@ jobs:
]
}
}' tauri.conf.json
- name: Install project dependencies
shell: bash
run: cd .. && yarn --network-timeout 100000
@@ -93,10 +97,10 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
SSL_COM_USERNAME: ${{ inputs.sign && secrets.SSL_COM_USERNAME }}
SSL_COM_PASSWORD: ${{ inputs.sign && secrets.SSL_COM_PASSWORD }}
SSL_COM_CREDENTIAL_ID: ${{ inputs.sign && steps.get_credential_ids.outputs.SSL_COM_CREDENTIAL_ID }}
SSL_COM_TOTP_SECRET: ${{ inputs.sign && secrets.SSL_COM_TOTP_SECRET }}
SSL_COM_USERNAME: ${{ (inputs.sign || startsWith(github.ref, 'refs/tags/nym-wallet-')) && secrets.SSL_COM_USERNAME }}
SSL_COM_PASSWORD: ${{ (inputs.sign || startsWith(github.ref, 'refs/tags/nym-wallet-')) && secrets.SSL_COM_PASSWORD }}
SSL_COM_CREDENTIAL_ID: ${{ (inputs.sign || startsWith(github.ref, 'refs/tags/nym-wallet-')) && steps.get_credential_ids.outputs.SSL_COM_CREDENTIAL_ID }}
SSL_COM_TOTP_SECRET: ${{ (inputs.sign || startsWith(github.ref, 'refs/tags/nym-wallet-')) && secrets.SSL_COM_TOTP_SECRET }}
run: |
echo "Starting build process..."
yarn build
@@ -140,7 +144,7 @@ jobs:
- id: create-release
name: Upload to release based on tag name
uses: softprops/action-gh-release@v2
if: github.event_name == 'release'
if: ${{ startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release' }}
with:
files: |
nym-wallet/${{ env.BUNDLE_PATH }}/msi/*.msi
-34
View File
@@ -4,40 +4,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [2025.8-tourist] (2025-04-29)
- add reserved byte to reply surb serialisation ([#5731])
- Remove inactive peers ([#5721])
- Update Hickory DNS "0.24.4" to "0.25" ([#5709])
- build(deps): bump the patch-updates group across 1 directory with 7 updates ([#5708])
- Peer handle should die more gracefully ([#5704])
- build(deps): bump crossbeam-channel from 0.5.14 to 0.5.15 ([#5702])
- build(deps): bump actions/checkout from 3 to 4 ([#5700])
- Feature/updated sphinx payload keys ([#5698])
- Bump the nym-vpn deb metapackage to 1.0 ([#5697])
- Make mix hops optional for Mixnet Client ([#5696])
- build(deps): bump tokio from 1.44.1 to 1.44.2 ([#5693])
- Feature/replay protection ([#5682])
- Adding fresh nym-api tests and workflow ([#5659])
- build(deps): bump next from 14.2.21 to 14.2.25 ([#5655])
- build(deps): bump pnpm/action-setup from 4.0.0 to 4.1.0 ([#5436])
[#5731]: https://github.com/nymtech/nym/pull/5731
[#5721]: https://github.com/nymtech/nym/pull/5721
[#5709]: https://github.com/nymtech/nym/pull/5709
[#5708]: https://github.com/nymtech/nym/pull/5708
[#5704]: https://github.com/nymtech/nym/pull/5704
[#5702]: https://github.com/nymtech/nym/pull/5702
[#5700]: https://github.com/nymtech/nym/pull/5700
[#5698]: https://github.com/nymtech/nym/pull/5698
[#5697]: https://github.com/nymtech/nym/pull/5697
[#5696]: https://github.com/nymtech/nym/pull/5696
[#5693]: https://github.com/nymtech/nym/pull/5693
[#5682]: https://github.com/nymtech/nym/pull/5682
[#5659]: https://github.com/nymtech/nym/pull/5659
[#5655]: https://github.com/nymtech/nym/pull/5655
[#5436]: https://github.com/nymtech/nym/pull/5436
## [2025.7-tex] (2025-04-14)
- Expand /v3/nym-nodes with geodata ([#5686])
Generated
+7 -10
View File
@@ -4987,7 +4987,7 @@ dependencies = [
[[package]]
name = "nym-cli"
version = "1.1.54"
version = "1.1.53"
dependencies = [
"anyhow",
"base64 0.22.1",
@@ -5070,7 +5070,7 @@ dependencies = [
[[package]]
name = "nym-client"
version = "1.1.54"
version = "1.1.53"
dependencies = [
"bs58",
"clap",
@@ -5785,14 +5785,12 @@ name = "nym-http-api-client"
version = "0.1.0"
dependencies = [
"async-trait",
"bincode",
"bytes",
"encoding_rs",
"hickory-resolver",
"http 1.3.1",
"mime",
"nym-bin-common",
"nym-http-api-common",
"once_cell",
"reqwest 0.12.15",
"serde",
@@ -5810,15 +5808,14 @@ version = "0.1.0"
dependencies = [
"axum 0.7.9",
"axum-client-ip",
"bincode",
"bytes",
"colored",
"futures",
"mime",
"serde",
"serde_json",
"serde_yaml",
"subtle 2.6.1",
"time",
"tower 0.5.2",
"tracing",
"utoipa",
@@ -6071,7 +6068,7 @@ dependencies = [
[[package]]
name = "nym-network-requester"
version = "1.1.55"
version = "1.1.54"
dependencies = [
"addr",
"anyhow",
@@ -6122,7 +6119,7 @@ dependencies = [
[[package]]
name = "nym-node"
version = "1.10.0"
version = "1.9.0"
dependencies = [
"anyhow",
"arc-swap",
@@ -6516,7 +6513,7 @@ dependencies = [
[[package]]
name = "nym-socks5-client"
version = "1.1.54"
version = "1.1.53"
dependencies = [
"bs58",
"clap",
@@ -7120,7 +7117,7 @@ dependencies = [
[[package]]
name = "nymvisor"
version = "0.1.19"
version = "0.1.18"
dependencies = [
"anyhow",
"bytes",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "1.1.54"
version = "1.1.53"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.54"
version = "1.1.53"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021"
@@ -70,10 +70,6 @@ impl NymApiTopologyProvider {
}
}
pub fn disable_bincode(&mut self) {
self.validator_client.use_bincode = false;
}
fn use_next_nym_api(&mut self) {
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");
@@ -345,47 +345,25 @@ impl<C, S> Client<C, S> {
#[derive(Clone)]
pub struct NymApiClient {
pub use_bincode: bool,
pub nym_api: nym_api::Client,
// 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
}
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
#[allow(deprecated)]
impl NymApiClient {
pub fn new(api_url: Url) -> Self {
let nym_api = nym_api::Client::new(api_url, None);
NymApiClient {
use_bincode: true,
nym_api,
}
NymApiClient { nym_api }
}
#[cfg(not(target_arch = "wasm32"))]
pub fn new_with_timeout(api_url: Url, timeout: std::time::Duration) -> Self {
let nym_api = nym_api::Client::new(api_url, Some(timeout));
NymApiClient {
use_bincode: true,
nym_api,
}
}
#[must_use]
pub fn with_bincode(mut self, use_bincode: bool) -> Self {
self.use_bincode = use_bincode;
self
NymApiClient { nym_api }
}
pub fn new_with_user_agent(api_url: Url, user_agent: impl Into<UserAgent>) -> Self {
@@ -395,10 +373,7 @@ impl NymApiClient {
.build::<ValidatorClientError>()
.expect("failed to build nym api client");
NymApiClient {
use_bincode: false,
nym_api,
}
NymApiClient { nym_api }
}
pub fn api_url(&self) -> &Url {
@@ -435,7 +410,7 @@ impl NymApiClient {
loop {
let mut res = self
.nym_api
.get_basic_entry_assigned_nodes(false, Some(page), None, self.use_bincode)
.get_basic_entry_assigned_nodes(false, Some(page), None)
.await?;
nodes.append(&mut res.nodes.data);
@@ -461,7 +436,7 @@ impl NymApiClient {
loop {
let mut res = self
.nym_api
.get_basic_active_mixing_assigned_nodes(false, Some(page), None, self.use_bincode)
.get_basic_active_mixing_assigned_nodes(false, Some(page), None)
.await?;
nodes.append(&mut res.nodes.data);
@@ -487,7 +462,7 @@ impl NymApiClient {
loop {
let mut res = self
.nym_api
.get_basic_mixing_capable_nodes(false, Some(page), None, self.use_bincode)
.get_basic_mixing_capable_nodes(false, Some(page), None)
.await?;
nodes.append(&mut res.nodes.data);
@@ -510,7 +485,7 @@ impl NymApiClient {
loop {
let mut res = self
.nym_api
.get_basic_nodes(false, Some(page), None, self.use_bincode)
.get_basic_nodes(false, Some(page), None)
.await?;
nodes.append(&mut res.nodes.data);
@@ -318,7 +318,6 @@ pub trait NymApiClientExt: ApiClient {
no_legacy: bool,
page: Option<u32>,
per_page: Option<u32>,
use_bincode: bool,
) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> {
let mut params = Vec::new();
@@ -334,11 +333,7 @@ pub trait NymApiClientExt: ApiClient {
params.push(("per_page", per_page.to_string()))
}
if use_bincode {
params.push(("output", "bincode".to_string()))
}
self.get_response(
self.get_json(
&[
routes::API_VERSION,
"unstable",
@@ -360,7 +355,6 @@ pub trait NymApiClientExt: ApiClient {
no_legacy: bool,
page: Option<u32>,
per_page: Option<u32>,
use_bincode: bool,
) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> {
let mut params = Vec::new();
@@ -376,11 +370,7 @@ pub trait NymApiClientExt: ApiClient {
params.push(("per_page", per_page.to_string()))
}
if use_bincode {
params.push(("output", "bincode".to_string()))
}
self.get_response(
self.get_json(
&[
routes::API_VERSION,
"unstable",
@@ -402,7 +392,6 @@ pub trait NymApiClientExt: ApiClient {
no_legacy: bool,
page: Option<u32>,
per_page: Option<u32>,
use_bincode: bool,
) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> {
let mut params = Vec::new();
@@ -418,11 +407,7 @@ pub trait NymApiClientExt: ApiClient {
params.push(("per_page", per_page.to_string()))
}
if use_bincode {
params.push(("output", "bincode".to_string()))
}
self.get_response(
self.get_json(
&[
routes::API_VERSION,
"unstable",
@@ -442,7 +427,6 @@ pub trait NymApiClientExt: ApiClient {
no_legacy: bool,
page: Option<u32>,
per_page: Option<u32>,
use_bincode: bool,
) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> {
let mut params = Vec::new();
@@ -458,11 +442,7 @@ pub trait NymApiClientExt: ApiClient {
params.push(("per_page", per_page.to_string()))
}
if use_bincode {
params.push(("output", "bincode".to_string()))
}
self.get_response(
self.get_json(
&[
routes::API_VERSION,
"unstable",
@@ -98,10 +98,11 @@ impl SqliteEcashTicketbookManager {
pub(crate) async fn contains_ticketbook_data(&self, data: &[u8]) -> Result<bool, sqlx::Error> {
let exists = sqlx::query(
r#"
SELECT 1
FROM ecash_ticketbook
WHERE ticketbook_data = ?
SELECT EXISTS (
SELECT 1
FROM ecash_ticketbook
WHERE ticketbook_data = ?
)
"#,
)
.bind(data)
+2 -4
View File
@@ -12,8 +12,7 @@ license.workspace = true
[dependencies]
async-trait = { workspace = true }
bincode = { workspace = true }
reqwest = { workspace = true, features = ["json", "gzip", "deflate", "brotli", "zstd"] }
reqwest = { workspace = true, features = ["json", "gzip"] }
http.workspace = true
url = { workspace = true }
once_cell = { workspace = true }
@@ -27,7 +26,7 @@ bytes = { workspace = true }
encoding_rs = { workspace = true }
mime = { workspace = true }
nym-http-api-common = { path = "../http-api-common", default-features = false }
nym-bin-common = { path = "../bin-common" }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies]
@@ -40,4 +39,3 @@ features = ["tokio"]
[dev-dependencies]
tokio = { workspace = true, features = ["rt", "macros"] }
+35 -104
View File
@@ -144,13 +144,10 @@ use serde::{Deserialize, Serialize};
use std::fmt::Display;
use std::time::Duration;
use thiserror::Error;
use tracing::{debug, instrument, warn};
use tracing::{instrument, warn};
use url::Url;
use bytes::Bytes;
use http::header::CONTENT_TYPE;
use http::HeaderMap;
use mime::Mime;
pub use reqwest::IntoUrl;
#[cfg(not(target_arch = "wasm32"))]
use std::net::SocketAddr;
@@ -213,8 +210,11 @@ pub enum HttpClientError<E: Display = String> {
#[error("failed to resolve request. status: '{status}', additional error message: {error}")]
EndpointFailure { status: StatusCode, error: E },
#[error("failed to decode response body: {message} from {content}")]
ResponseDecodeFailure { message: String, content: String },
#[error("failed to decode response body: {source} from {content}")]
ResponseDecodeFailure {
source: serde_json::Error,
content: String,
},
#[cfg(target_arch = "wasm32")]
#[error("the request has timed out")]
@@ -265,18 +265,14 @@ impl ClientBuilder {
#[cfg(not(target_arch = "wasm32"))]
let reqwest_client_builder = {
// Note: I believe the manual enable calls for the compression methods are extra
// as the various compression features for `reqwest` crate should be enabled
// just by including the feature which:
// `"Enable[s] auto decompression by checking the Content-Encoding response header."`
let r = reqwest::ClientBuilder::new();
// Note this is extra as the `gzip` feature for `reqwest` crate should be enabled which
// `"Enable[s] auto gzip decompression by checking the Content-Encoding response header."`
//
// I am going to leave these here anyways so that removing a decompression method
// from the features list will throw an error if it is not also removed here.
reqwest::ClientBuilder::new()
.gzip(true)
.deflate(true)
.brotli(true)
.zstd(true)
// I am going to leave it here anyways so that gzip decompression is attempted even if
// that feature is removed.
r.gzip(true)
};
ClientBuilder {
@@ -539,6 +535,11 @@ impl ApiClientCore for Client {
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 {
request = request.json(body);
}
@@ -697,29 +698,11 @@ pub trait ApiClient: ApiClientCore {
/// defined key-value parameters, e.g. `[("since", "12345")]`. Attempt to parse the response
/// into the provided type `T`.
#[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>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> 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
for<'a> T: Deserialize<'a>,
K: AsRef<str> + Sync,
@@ -894,10 +877,14 @@ fn sanitize_url<K: AsRef<str>, V: AsRef<str>>(
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 mime::Mime;
let content_type = try_get_mime_type(headers);
let content_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
.as_ref()
@@ -910,7 +897,7 @@ fn decode_as_text(bytes: &bytes::Bytes, headers: &HeaderMap) -> String {
text.into_owned()
}
/// Attempt to parse a response object from an HTTP response
/// Attempt to parse a json object from an HTTP response
#[instrument(level = "debug", skip_all)]
pub async fn parse_response<T, E>(res: Response, allow_empty: bool) -> Result<T, HttpClientError<E>>
where
@@ -932,7 +919,16 @@ where
// internally reqwest is first retrieving bytes and then performing parsing via serde_json
// (and similarly does the same thing for text())
let full = res.bytes().await?;
decode_raw_response(&headers, full)
match serde_json::from_slice(&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 {
Err(HttpClientError::NotFound)
} else {
@@ -951,71 +947,6 @@ 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)]
mod tests {
use super::*;
+11 -35
View File
@@ -11,44 +11,20 @@ license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
axum = { workspace = true, optional = true }
axum-client-ip = { workspace = true, optional = true }
bincode = { workspace = true }
bytes = { workspace = true, optional = true }
colored = { workspace = true, optional = true }
futures = { workspace = true, optional = true }
mime = { workspace = true, optional = true }
axum-client-ip.workspace = true
axum.workspace = true
bytes = { workspace = true }
colored.workspace = true
futures = { workspace = true }
mime = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_yaml = { workspace = true, optional = true }
subtle = { workspace = true, optional = true }
time = { workspace = true, optional = true, features = ["macros"] }
tower = { workspace = true, optional = true }
serde_json.workspace = true
serde_yaml = { workspace = true }
subtle.workspace = true
tower = { workspace = true }
tracing.workspace = true
utoipa = { workspace = true, optional = true }
zeroize = { workspace = true, optional = true }
zeroize = { workspace = true }
[features]
default = []
output = [
"axum",
"bytes",
"mime",
"serde_yaml",
"time",
"time/formatting"
]
middleware = [
"axum",
"axum-client-ip",
"colored",
"futures",
"subtle",
"tower",
"zeroize"
]
utoipa = ["dep:utoipa"]
[lints]
workspace = true
+87 -15
View File
@@ -1,20 +1,92 @@
// Copyright 2023-2025 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#[cfg(feature = "middleware")]
use axum::http::{header, HeaderValue, StatusCode};
use axum::response::{IntoResponse, Response};
use axum::Json;
use bytes::{BufMut, BytesMut};
use serde::{Deserialize, Serialize};
pub mod middleware;
#[cfg(feature = "output")]
pub mod response;
// don't break existing imports
#[cfg(feature = "output")]
pub use response::*;
// be explicit about those values because bincode uses different defaults in different places
pub fn make_bincode_serializer() -> impl ::bincode::Options {
use ::bincode::Options;
::bincode::DefaultOptions::new()
.with_little_endian()
.with_varint_encoding()
#[derive(Debug, Clone)]
pub enum FormattedResponse<T> {
Json(Json<T>),
Yaml(Yaml<T>),
}
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(),
}
}
}
#[derive(Default, Debug, Serialize, Deserialize, Copy, Clone)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[serde(rename_all = "lowercase")]
pub enum Output {
#[default]
Json,
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(),
}
}
}
@@ -1,49 +0,0 @@
// 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),
}
}
}
@@ -1,49 +0,0 @@
// 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),
}
}
}
-190
View File
@@ -1,190 +0,0 @@
// 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)
);
}
}
@@ -1,47 +0,0 @@
// 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),
}
}
}
@@ -9,6 +9,7 @@ use nym_sphinx_addressing::nodes::{
};
use nym_sphinx_params::packet_sizes::PacketSize;
use nym_sphinx_params::{PacketType, ReplySurbKeyDigestAlgorithm};
use nym_sphinx_types::constants::PAYLOAD_KEY_SEED_SIZE;
use nym_sphinx_types::{
NymPacket, SURBMaterial, SphinxError, HEADER_SIZE, NODE_ADDRESS_LENGTH, SURB,
X25519_WITH_EXPLICIT_PAYLOAD_KEYS_VERSION,
@@ -126,6 +127,12 @@ impl ReplySurb {
})
}
/// Returns the expected number of bytes the [`ReplySURB`] will take after serialization using the new encoding format.
/// Useful for deserialization from a bytes stream.
pub fn v2_serialised_len(num_hops: u8) -> usize {
Self::BASE_OVERHEAD + num_hops as usize * PAYLOAD_KEY_SEED_SIZE
}
pub fn encryption_key(&self) -> &SurbEncryptionKey {
&self.encryption_key
}
@@ -32,28 +32,24 @@ fn v2_reply_surbs_serialised_len(surbs: &[ReplySurb]) -> usize {
}
}
// when serialising surbs are always prepended with:
// - u16-encoded count,
// - u8-encoded number of hops
// - u8 reserved value
4 + num_surbs * v2_reply_surb_serialised_len(num_hops)
// when serialising surbs are always prepended with u16-encoded count an u8-encoded number of hops
3 + num_surbs * v2_reply_surb_serialised_len(num_hops)
}
// NUM_SURBS (u16) || HOPS (u8) || RESERVED (u8) || SURB_DATA
// NUM_SURBS (u16) || HOPS (u8) || SURB_DATA
fn recover_reply_surbs_v2(
bytes: &[u8],
) -> Result<(Vec<ReplySurb>, usize), InvalidReplyRequestError> {
if bytes.len() < 4 {
if bytes.len() < 2 {
return Err(InvalidReplyRequestError::RequestTooShortToDeserialize);
}
// we're not attaching more than 65k surbs...
let num_surbs = u16::from_be_bytes([bytes[0], bytes[1]]);
let num_hops = bytes[2];
let _reserved = bytes[3];
let mut consumed = 4;
let mut consumed = 3;
let surb_size = v2_reply_surb_serialised_len(num_hops);
let surb_size = ReplySurb::v2_serialised_len(num_hops);
if bytes[consumed..].len() < num_surbs as usize * surb_size {
return Err(InvalidReplyRequestError::RequestTooShortToDeserialize);
}
@@ -73,13 +69,11 @@ fn recover_reply_surbs_v2(
fn reply_surbs_bytes_v2(reply_surbs: &[ReplySurb]) -> impl Iterator<Item = u8> + use<'_> {
let num_surbs = reply_surbs.len() as u16;
let num_hops = reply_surbs_hops(reply_surbs);
let reserved = 0;
num_surbs
.to_be_bytes()
.into_iter()
.chain(once(num_hops))
.chain(once(reserved))
.chain(reply_surbs.iter().flat_map(|surb| surb.to_bytes()))
}
+2 -2
View File
@@ -4,7 +4,7 @@
[package]
name = "nym-api"
license = "GPL-3.0"
version = "1.1.56"
version = "1.1.55"
authors.workspace = true
edition = "2021"
rust-version.workspace = true
@@ -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-requests = { path = "../nym-node/nym-node-requests" }
nym-types = { path = "../common/types" }
nym-http-api-common = { path = "../common/http-api-common", features = ["utoipa", "output", "middleware"] }
nym-http-api-common = { path = "../common/http-api-common", features = ["utoipa"] }
nym-serde-helpers = { path = "../common/serde-helpers", features = ["date"] }
nym-ticketbooks-merkle = { path = "../common/ticketbooks-merkle" }
nym-statistics-common = { path = "../common/statistics" }
+12 -42
View File
@@ -3,10 +3,8 @@
use crate::node_status_api::models::{AxumErrorResponse, AxumResult};
use crate::support::http::state::AppState;
use axum::extract::Query;
use axum::{extract, Router};
use nym_api_requests::models::CirculatingSupplyResponse;
use nym_http_api_common::{FormattedResponse, OutputParams};
use nym_validator_client::nyxd::Coin;
pub(crate) fn circulating_supply_routes() -> Router<AppState> {
@@ -27,26 +25,18 @@ pub(crate) fn circulating_supply_routes() -> Router<AppState> {
get,
path = "/v1/circulating-supply",
responses(
(status = 200, content(
(CirculatingSupplyResponse = "application/json"),
(CirculatingSupplyResponse = "application/yaml"),
(CirculatingSupplyResponse = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = CirculatingSupplyResponse)
)
)]
async fn get_full_circulating_supply(
Query(output): Query<OutputParams>,
extract::State(state): extract::State<AppState>,
) -> AxumResult<FormattedResponse<CirculatingSupplyResponse>> {
let output = output.output.unwrap_or_default();
) -> AxumResult<axum::Json<CirculatingSupplyResponse>> {
match state
.circulating_supply_cache()
.get_circulating_supply()
.await
{
Some(value) => Ok(output.to_response(value)),
Some(value) => Ok(value.into()),
None => Err(AxumErrorResponse::internal_msg("unavailable")),
}
}
@@ -56,19 +46,12 @@ async fn get_full_circulating_supply(
get,
path = "/v1/circulating-supply/total-supply-value",
responses(
(status = 200, content(
(f64 = "application/json"),
(f64 = "application/yaml"),
(f64 = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = [f64])
)
)]
async fn get_total_supply(
Query(output): Query<OutputParams>,
extract::State(state): extract::State<AppState>,
) -> AxumResult<FormattedResponse<f64>> {
let output = output.output.unwrap_or_default();
) -> AxumResult<axum::Json<f64>> {
let full_circulating_supply = match state
.circulating_supply_cache()
.get_circulating_supply()
@@ -78,9 +61,7 @@ async fn get_total_supply(
None => return Err(AxumErrorResponse::internal_msg("unavailable")),
};
let total_supply = unym_coin_to_float_unym(full_circulating_supply.total_supply.into());
Ok(output.to_response(total_supply))
Ok(unym_coin_to_float_unym(full_circulating_supply.total_supply.into()).into())
}
#[utoipa::path(
@@ -88,20 +69,12 @@ async fn get_total_supply(
get,
path = "/v1/circulating-supply/circulating-supply-value",
responses(
(status = 200, content(
(f64 = "application/json"),
(f64 = "application/yaml"),
(f64 = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = [f64])
)
)]
async fn get_circulating_supply(
Query(output): Query<OutputParams>,
extract::State(state): extract::State<AppState>,
) -> AxumResult<FormattedResponse<f64>> {
let output = output.output.unwrap_or_default();
) -> AxumResult<axum::Json<f64>> {
let full_circulating_supply = match state
.circulating_supply_cache()
.get_circulating_supply()
@@ -111,10 +84,7 @@ async fn get_circulating_supply(
None => return Err(AxumErrorResponse::internal_msg("unavailable")),
};
let circulating_supply =
unym_coin_to_float_unym(full_circulating_supply.circulating_supply.into());
Ok(output.to_response(circulating_supply))
Ok(unym_coin_to_float_unym(full_circulating_supply.circulating_supply.into()).into())
}
// TODO: this is not the best place to put it, it should be more centralised,
+20 -42
View File
@@ -7,13 +7,12 @@ use crate::ecash::state::EcashState;
use crate::node_status_api::models::AxumResult;
use crate::support::http::state::AppState;
use axum::extract::{Query, State};
use axum::Router;
use axum::{Json, Router};
use nym_api_requests::ecash::models::{
AggregatedCoinIndicesSignatureResponse, AggregatedExpirationDateSignatureResponse,
};
use nym_api_requests::ecash::VerificationKeyResponse;
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 serde::Deserialize;
use std::sync::Arc;
@@ -46,32 +45,26 @@ pub(crate) fn aggregation_routes() -> Router<AppState> {
),
path = "/v1/ecash/master-verification-key",
responses(
(status = 200, content(
(VerificationKeyResponse = "application/json"),
(VerificationKeyResponse = "application/yaml"),
(VerificationKeyResponse = "application/bincode")
))
),
(status = 200, body = VerificationKeyResponse)
)
)]
async fn master_verification_key(
State(state): State<Arc<EcashState>>,
Query(EpochIdParam { epoch_id, output }): Query<EpochIdParam>,
) -> AxumResult<FormattedResponse<VerificationKeyResponse>> {
Query(EpochIdParam { epoch_id }): Query<EpochIdParam>,
) -> AxumResult<Json<VerificationKeyResponse>> {
trace!("aggregated_verification_key request");
let output = output.unwrap_or_default();
// see if we're not in the middle of new dkg
state.ensure_dkg_not_in_progress().await?;
let key = state.master_verification_key(epoch_id).await?;
Ok(output.to_response(VerificationKeyResponse::new(key.clone())))
Ok(Json(VerificationKeyResponse::new(key.clone())))
}
#[derive(Deserialize, IntoParams)]
struct ExpirationDateParam {
expiration_date: Option<String>,
output: Option<Output>,
}
#[utoipa::path(
@@ -82,22 +75,14 @@ struct ExpirationDateParam {
),
path = "/v1/ecash/aggregated-expiration-date-signatures",
responses(
(status = 200, content(
(AggregatedExpirationDateSignatureResponse = "application/json"),
(AggregatedExpirationDateSignatureResponse = "application/yaml"),
(AggregatedExpirationDateSignatureResponse = "application/bincode")
))
),
(status = 200, body = AggregatedExpirationDateSignatureResponse)
)
)]
async fn expiration_date_signatures(
State(state): State<Arc<EcashState>>,
Query(ExpirationDateParam {
expiration_date,
output,
}): Query<ExpirationDateParam>,
) -> AxumResult<FormattedResponse<AggregatedExpirationDateSignatureResponse>> {
Query(ExpirationDateParam { expiration_date }): Query<ExpirationDateParam>,
) -> AxumResult<Json<AggregatedExpirationDateSignatureResponse>> {
trace!("aggregated_expiration_date_signatures request");
let output = output.unwrap_or_default();
let expiration_date = match expiration_date {
None => cred_exp_date().ecash_date(),
@@ -112,13 +97,11 @@ async fn expiration_date_signatures(
.master_expiration_date_signatures(expiration_date)
.await?;
Ok(
output.to_response(AggregatedExpirationDateSignatureResponse {
epoch_id: expiration_date_signatures.epoch_id,
expiration_date,
signatures: expiration_date_signatures.signatures.clone(),
}),
)
Ok(Json(AggregatedExpirationDateSignatureResponse {
epoch_id: expiration_date_signatures.epoch_id,
expiration_date,
signatures: expiration_date_signatures.signatures.clone(),
}))
}
#[utoipa::path(
@@ -129,26 +112,21 @@ async fn expiration_date_signatures(
),
path = "/v1/ecash/aggregated-coin-indices-signatures",
responses(
(status = 200, content(
(AggregatedCoinIndicesSignatureResponse = "application/json"),
(AggregatedCoinIndicesSignatureResponse = "application/yaml"),
(AggregatedCoinIndicesSignatureResponse = "application/bincode")
))
),
(status = 200, body = AggregatedCoinIndicesSignatureResponse)
)
)]
async fn coin_indices_signatures(
Query(EpochIdParam { epoch_id, output }): Query<EpochIdParam>,
Query(EpochIdParam { epoch_id }): Query<EpochIdParam>,
State(state): State<Arc<EcashState>>,
) -> AxumResult<FormattedResponse<AggregatedCoinIndicesSignatureResponse>> {
) -> AxumResult<Json<AggregatedCoinIndicesSignatureResponse>> {
trace!("aggregated_coin_indices_signatures request");
let output = output.unwrap_or_default();
// see if we're not in the middle of new dkg
state.ensure_dkg_not_in_progress().await?;
let coin_indices_signatures = state.master_coin_index_signatures(epoch_id).await?;
Ok(output.to_response(AggregatedCoinIndicesSignatureResponse {
Ok(Json(AggregatedCoinIndicesSignatureResponse {
epoch_id: coin_indices_signatures.epoch_id,
signatures: coin_indices_signatures.signatures.clone(),
}))
-3
View File
@@ -1,10 +1,7 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use nym_http_api_common::Output;
#[derive(serde::Deserialize, utoipa::IntoParams)]
pub(super) struct EpochIdParam {
pub(super) epoch_id: Option<u64>,
pub(super) output: Option<Output>,
}
+27 -61
View File
@@ -13,7 +13,6 @@ use nym_api_requests::ecash::models::{
IssuedTicketbooksForCountResponse, IssuedTicketbooksForResponse,
IssuedTicketbooksOnCountResponse, SignableMessageBody,
};
use nym_http_api_common::{FormattedResponse, OutputParams};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::cmp::min;
@@ -81,20 +80,15 @@ pub(crate) struct IssuanceDatePathParam {
path = "/issued-ticketbooks-count",
context_path = "/v1/ecash",
responses(
(status = 200, content(
(IssuedTicketbooksCountResponse = "application/json"),
(IssuedTicketbooksCountResponse = "application/yaml"),
(IssuedTicketbooksCountResponse = "application/bincode")
)),
(status = 200, body = IssuedTicketbooksCountResponse),
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
)
)]
async fn issued_ticketbooks_count(
Query(pagination): Query<PaginationRequest>,
State(state): State<Arc<EcashState>>,
) -> AxumResult<FormattedResponse<IssuedTicketbooksCountResponse>> {
) -> AxumResult<Json<IssuedTicketbooksCountResponse>> {
state.ensure_signer().await?;
let output = pagination.output.unwrap_or_default();
let page = pagination.page.unwrap_or_default();
let per_page = min(
@@ -104,7 +98,9 @@ async fn issued_ticketbooks_count(
MAX_ISSUANCE_COUNT_PAGE_SIZE,
);
Ok(output.to_response(state.get_issued_ticketbooks_count(page, per_page).await?))
Ok(Json(
state.get_issued_ticketbooks_count(page, per_page).await?,
))
}
/// Returns number of issued ticketbooks for particular expiration date.
@@ -113,28 +109,22 @@ async fn issued_ticketbooks_count(
tag = "Ecash",
get,
params(
ExpirationDatePathParam, OutputParams
ExpirationDatePathParam
),
path = "/issued-ticketbooks-for-count/{expiration_date}",
context_path = "/v1/ecash",
responses(
(status = 200, content(
(IssuedTicketbooksForCountResponse = "application/json"),
(IssuedTicketbooksForCountResponse = "application/yaml"),
(IssuedTicketbooksForCountResponse = "application/bincode")
)),
(status = 200, body = IssuedTicketbooksForCountResponse),
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
)
)]
async fn issued_ticketbooks_for_count(
Query(output): Query<OutputParams>,
Path(ExpirationDatePathParam { expiration_date }): Path<ExpirationDatePathParam>,
State(state): State<Arc<EcashState>>,
) -> AxumResult<FormattedResponse<IssuedTicketbooksForCountResponse>> {
) -> AxumResult<Json<IssuedTicketbooksForCountResponse>> {
state.ensure_signer().await?;
let output = output.output.unwrap_or_default();
Ok(output.to_response(
Ok(Json(
state
.get_issued_ticketbooks_for_count(expiration_date)
.await?,
@@ -147,56 +137,46 @@ async fn issued_ticketbooks_for_count(
tag = "Ecash",
get,
params(
IssuanceDatePathParam, OutputParams
IssuanceDatePathParam
),
path = "/issued-ticketbooks-on-count/{issuance_date}",
context_path = "/v1/ecash",
responses(
(status = 200, content(
(IssuedTicketbooksOnCountResponse = "application/json"),
(IssuedTicketbooksOnCountResponse = "application/yaml"),
(IssuedTicketbooksOnCountResponse = "application/bincode")
)),
(status = 200, body = IssuedTicketbooksOnCountResponse),
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
)
)]
async fn issued_ticketbooks_on_count(
Query(output): Query<OutputParams>,
Path(IssuanceDatePathParam { issuance_date }): Path<IssuanceDatePathParam>,
State(state): State<Arc<EcashState>>,
) -> AxumResult<FormattedResponse<IssuedTicketbooksOnCountResponse>> {
) -> AxumResult<Json<IssuedTicketbooksOnCountResponse>> {
state.ensure_signer().await?;
let output = output.output.unwrap_or_default();
Ok(output.to_response(state.get_issued_ticketbooks_on_count(issuance_date).await?))
Ok(Json(
state.get_issued_ticketbooks_on_count(issuance_date).await?,
))
}
#[utoipa::path(
tag = "Ecash",
get,
params(
ExpirationDatePathParam, OutputParams
ExpirationDatePathParam
),
path = "/issued-ticketbooks-for/{expiration_date}",
context_path = "/v1/ecash",
responses(
(status = 200, content(
(IssuedTicketbooksForResponse = "application/json"),
(IssuedTicketbooksForResponse = "application/yaml"),
(IssuedTicketbooksForResponse = "application/bincode")
)),
(status = 200, body = IssuedTicketbooksForResponse),
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
)
)]
async fn issued_ticketbooks_for(
Query(output): Query<OutputParams>,
State(state): State<Arc<EcashState>>,
Path(ExpirationDatePathParam { expiration_date }): Path<ExpirationDatePathParam>,
) -> AxumResult<FormattedResponse<IssuedTicketbooksForResponse>> {
) -> AxumResult<Json<IssuedTicketbooksForResponse>> {
state.ensure_signer().await?;
let output = output.output.unwrap_or_default();
Ok(output.to_response(
Ok(Json(
state
.get_issued_ticketbooks_deposits_on(expiration_date)
.await?
@@ -211,24 +191,17 @@ async fn issued_ticketbooks_for(
path = "/issued-ticketbooks-challenge-commitment",
context_path = "/v1/ecash",
responses(
(status = 200, content(
(IssuedTicketbooksChallengeCommitmentResponse = "application/json"),
(IssuedTicketbooksChallengeCommitmentResponse = "application/yaml"),
(IssuedTicketbooksChallengeCommitmentResponse = "application/bincode")
)),
(status = 200, body = IssuedTicketbooksChallengeCommitmentResponse),
(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(
Query(output): Query<OutputParams>,
State(state): State<Arc<EcashState>>,
Json(request): Json<IssuedTicketbooksChallengeCommitmentRequest>,
) -> AxumResult<FormattedResponse<IssuedTicketbooksChallengeCommitmentResponse>> {
) -> AxumResult<Json<IssuedTicketbooksChallengeCommitmentResponse>> {
state.ensure_signer().await?;
let output = output.output.unwrap_or_default();
Ok(output.to_response(
Ok(Json(
state
.get_issued_ticketbooks_challenge_commitment(request)
.await?
@@ -243,24 +216,17 @@ async fn issued_ticketbooks_challenge_commitment(
path = "/issued-ticketbooks-data",
context_path = "/v1/ecash",
responses(
(status = 200, content(
(IssuedTicketbooksDataResponse = "application/json"),
(IssuedTicketbooksDataResponse = "application/yaml"),
(IssuedTicketbooksDataResponse = "application/bincode")
)),
(status = 200, body = IssuedTicketbooksDataResponse),
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
),
params(OutputParams)
)
)]
async fn issued_ticketbooks_data(
Query(output): Query<OutputParams>,
State(state): State<Arc<EcashState>>,
Json(request): Json<IssuedTicketbooksDataRequest>,
) -> AxumResult<FormattedResponse<IssuedTicketbooksDataResponse>> {
) -> AxumResult<Json<IssuedTicketbooksDataResponse>> {
state.ensure_signer().await?;
let output = output.output.unwrap_or_default();
Ok(output.to_response(
Ok(Json(
state
.get_issued_ticketbooks_data(request)
.await?
+17 -39
View File
@@ -14,7 +14,6 @@ use nym_api_requests::ecash::{
PartialExpirationDateSignatureResponse,
};
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 serde::Deserialize;
use std::ops::Deref;
@@ -42,22 +41,16 @@ pub(crate) fn partial_signing_routes() -> Router<AppState> {
request_body = BlindSignRequestBody,
path = "/v1/ecash/blind-sign",
responses(
(status = 200, content(
(BlindedSignatureResponse = "application/json"),
(BlindedSignatureResponse = "application/yaml"),
(BlindedSignatureResponse = "application/bincode")
)),
(status = 200, body = BlindedSignatureResponse),
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
),
params(OutputParams)
)
)]
async fn post_blind_sign(
Query(output): Query<OutputParams>,
State(state): State<Arc<EcashState>>,
Json(blind_sign_request_body): Json<BlindSignRequestBody>,
) -> AxumResult<FormattedResponse<BlindedSignatureResponse>> {
) -> AxumResult<Json<BlindedSignatureResponse>> {
state.ensure_signer().await?;
let output = output.output.unwrap_or_default();
debug!("Received blind sign request");
trace!("body: {:?}", blind_sign_request_body);
@@ -80,7 +73,7 @@ async fn post_blind_sign(
"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? {
return Ok(output.to_response(BlindedSignatureResponse { blinded_signature }));
return Ok(Json(BlindedSignatureResponse { blinded_signature }));
}
//check if account was blacklisted
@@ -108,13 +101,12 @@ async fn post_blind_sign(
.await?;
// finally return the credential to the client
Ok(output.to_response(BlindedSignatureResponse { blinded_signature }))
Ok(Json(BlindedSignatureResponse { blinded_signature }))
}
#[derive(Deserialize, IntoParams)]
struct ExpirationDateParam {
expiration_date: Option<String>,
output: Option<Output>,
}
#[utoipa::path(
@@ -125,23 +117,15 @@ struct ExpirationDateParam {
),
path = "/v1/ecash/partial-expiration-date-signatures",
responses(
(status = 200, content(
(PartialExpirationDateSignatureResponse = "application/json"),
(PartialExpirationDateSignatureResponse = "application/yaml"),
(PartialExpirationDateSignatureResponse = "application/bincode")
)),
(status = 200, body = PartialExpirationDateSignatureResponse),
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
)
)]
async fn partial_expiration_date_signatures(
State(state): State<Arc<EcashState>>,
Query(ExpirationDateParam {
expiration_date,
output,
}): Query<ExpirationDateParam>,
) -> AxumResult<FormattedResponse<PartialExpirationDateSignatureResponse>> {
Query(ExpirationDateParam { expiration_date }): Query<ExpirationDateParam>,
) -> AxumResult<Json<PartialExpirationDateSignatureResponse>> {
state.ensure_signer().await?;
let output = output.unwrap_or_default();
let expiration_date = match expiration_date {
None => cred_exp_date().ecash_date(),
@@ -156,7 +140,7 @@ async fn partial_expiration_date_signatures(
.partial_expiration_date_signatures(expiration_date)
.await?;
Ok(output.to_response(PartialExpirationDateSignatureResponse {
Ok(Json(PartialExpirationDateSignatureResponse {
epoch_id: expiration_date_signatures.epoch_id,
expiration_date,
signatures: expiration_date_signatures.signatures.clone(),
@@ -171,18 +155,14 @@ async fn partial_expiration_date_signatures(
),
path = "/v1/ecash/partial-coin-indices-signatures",
responses(
(status = 200, content(
(PartialCoinIndicesSignatureResponse = "application/json"),
(PartialCoinIndicesSignatureResponse = "application/yaml"),
(PartialCoinIndicesSignatureResponse = "application/bincode")
)),
(status = 200, body = PartialExpirationDateSignatureResponse),
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
)
)]
async fn partial_coin_indices_signatures(
State(state): State<Arc<EcashState>>,
Query(EpochIdParam { epoch_id, output }): Query<EpochIdParam>,
) -> AxumResult<FormattedResponse<PartialCoinIndicesSignatureResponse>> {
Query(EpochIdParam { epoch_id }): Query<EpochIdParam>,
) -> AxumResult<Json<PartialCoinIndicesSignatureResponse>> {
state.ensure_signer().await?;
// see if we're not in the middle of new dkg
@@ -190,10 +170,8 @@ async fn partial_coin_indices_signatures(
let coin_indices_signatures = state.partial_coin_index_signatures(epoch_id).await?;
Ok(output
.unwrap_or_default()
.to_response(PartialCoinIndicesSignatureResponse {
epoch_id: coin_indices_signatures.epoch_id,
signatures: coin_indices_signatures.signatures.clone(),
}))
Ok(Json(PartialCoinIndicesSignatureResponse {
epoch_id: coin_indices_signatures.epoch_id,
signatures: coin_indices_signatures.signatures.clone(),
}))
}
+21 -39
View File
@@ -5,7 +5,7 @@ use crate::ecash::error::EcashError;
use crate::ecash::state::EcashState;
use crate::node_status_api::models::{AxumErrorResponse, AxumResult};
use crate::support::http::state::AppState;
use axum::extract::{Query, State};
use axum::extract::State;
use axum::http::StatusCode;
use axum::{Json, Router};
use nym_api_requests::constants::MIN_BATCH_REDEMPTION_DELAY;
@@ -15,7 +15,6 @@ use nym_api_requests::ecash::models::{
};
use nym_compact_ecash::identify::IdentifyResult;
use nym_ecash_time::EcashTime;
use nym_http_api_common::{FormattedResponse, Output, OutputParams};
use std::collections::HashSet;
use std::ops::Deref;
use std::sync::Arc;
@@ -40,10 +39,9 @@ pub(crate) fn spending_routes() -> Router<AppState> {
const ONE_AM: Time = time!(1:00);
fn reject_ticket(
output: Output,
reason: EcashTicketVerificationRejection,
) -> AxumResult<FormattedResponse<EcashTicketVerificationResponse>> {
Ok(output.to_response(EcashTicketVerificationResponse::reject(reason)))
) -> AxumResult<Json<EcashTicketVerificationResponse>> {
Ok(Json(EcashTicketVerificationResponse::reject(reason)))
}
// TODO: optimise it; for now it's just dummy split of the original `verify_offline_credential`
@@ -54,29 +52,23 @@ fn reject_ticket(
request_body = VerifyEcashTicketBody,
path = "/v1/ecash/verify-ecash-ticket",
responses(
(status = 200, content(
(EcashTicketVerificationResponse = "application/json"),
(EcashTicketVerificationResponse = "application/yaml"),
(EcashTicketVerificationResponse = "application/bincode")
)),
(status = 200, body = EcashTicketVerificationResponse),
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
)
)]
async fn verify_ticket(
Query(output): Query<OutputParams>,
State(state): State<Arc<EcashState>>,
// TODO in the future: make it send binary data rather than json
Json(verify_ticket_body): Json<VerifyEcashTicketBody>,
) -> AxumResult<FormattedResponse<EcashTicketVerificationResponse>> {
) -> AxumResult<Json<EcashTicketVerificationResponse>> {
state.ensure_signer().await?;
let output = output.output.unwrap_or_default();
let credential_data = &verify_ticket_body.credential;
let gateway_cosmos_addr = &verify_ticket_body.gateway_cosmos_addr;
// easy check: is there only a single payment attached?
if credential_data.payment.spend_value != 1 {
return reject_ticket(output, EcashTicketVerificationRejection::MultipleTickets);
return reject_ticket(EcashTicketVerificationRejection::MultipleTickets);
}
let sn = &credential_data.encoded_serial_number();
@@ -91,14 +83,11 @@ async fn verify_ticket(
// 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 {
return reject_ticket(
output,
EcashTicketVerificationRejection::InvalidSpentDate {
today: today_ecash,
yesterday: yesterday_ecash,
received: spend_date,
},
);
return reject_ticket(EcashTicketVerificationRejection::InvalidSpentDate {
today: today_ecash,
yesterday: yesterday_ecash,
received: spend_date,
});
}
// actual double spend detection with storage
@@ -112,7 +101,7 @@ async fn verify_ticket(
IdentifyResult::NotADuplicatePayment => {} //SW NOTE This should never happen, quick message?
IdentifyResult::DuplicatePayInfo(_) => {
warn!("Identical payInfo");
return reject_ticket(output, EcashTicketVerificationRejection::ReplayedTicket);
return reject_ticket(EcashTicketVerificationRejection::ReplayedTicket);
}
IdentifyResult::DoubleSpendingPublicKeys(pub_key) => {
//Actual double spending
@@ -121,7 +110,7 @@ async fn verify_ticket(
pub_key.to_base58_string()
);
error!("UNIMPLEMENTED: blacklisting the double spend key");
return reject_ticket(output, EcashTicketVerificationRejection::DoubleSpend);
return reject_ticket(EcashTicketVerificationRejection::DoubleSpend);
}
}
}
@@ -130,7 +119,7 @@ async fn verify_ticket(
// perform actual crypto verification
if credential_data.verify(&verification_key).is_err() {
return reject_ticket(output, EcashTicketVerificationRejection::InvalidTicket);
return reject_ticket(EcashTicketVerificationRejection::InvalidTicket);
}
// store credential and check whether it wasn't already there (due to a parallel request)
@@ -138,10 +127,10 @@ async fn verify_ticket(
.store_verified_ticket(credential_data, gateway_cosmos_addr)
.await?;
if !was_inserted {
return reject_ticket(output, EcashTicketVerificationRejection::ReplayedTicket);
return reject_ticket(EcashTicketVerificationRejection::ReplayedTicket);
}
Ok(output.to_response(EcashTicketVerificationResponse { verified: Ok(()) }))
Ok(Json(EcashTicketVerificationResponse { verified: Ok(()) }))
}
#[utoipa::path(
@@ -150,23 +139,16 @@ async fn verify_ticket(
request_body = BatchRedeemTicketsBody,
path = "/v1/ecash/batch-redeem-ecash-tickets",
responses(
(status = 200, content(
(EcashBatchTicketRedemptionResponse = "application/json"),
(EcashBatchTicketRedemptionResponse = "application/yaml"),
(EcashBatchTicketRedemptionResponse = "application/bincode")
)),
(status = 200, body = EcashBatchTicketRedemptionResponse),
(status = 400, body = String, description = "this nym-api is not an ecash signer in the current epoch"),
),
params(OutputParams)
)
)]
async fn batch_redeem_tickets(
Query(output): Query<OutputParams>,
State(state): State<Arc<EcashState>>,
// TODO in the future: make it send binary data rather than json
Json(batch_redeem_credentials_body): Json<BatchRedeemTicketsBody>,
) -> AxumResult<FormattedResponse<EcashBatchTicketRedemptionResponse>> {
) -> AxumResult<Json<EcashBatchTicketRedemptionResponse>> {
state.ensure_signer().await?;
let output = output.output.unwrap_or_default();
// 1. see if that gateway has even submitted any tickets
let Some(provider_info) = state
@@ -226,7 +208,7 @@ async fn batch_redeem_tickets(
// 7. update the time of the last verification for this provider
state.update_last_batch_verification(&provider_info).await?;
Ok(output.to_response(EcashBatchTicketRedemptionResponse {
Ok(Json(EcashBatchTicketRedemptionResponse {
proposal_accepted: true,
}))
}
@@ -242,7 +224,7 @@ async fn batch_redeem_tickets(
)
)]
#[deprecated]
async fn double_spending_filter_v1() -> AxumResult<FormattedResponse<SpentCredentialsResponse>> {
async fn double_spending_filter_v1() -> AxumResult<Json<SpentCredentialsResponse>> {
AxumResult::Err(AxumErrorResponse::new(
"permanently restricted",
StatusCode::GONE,
+40 -77
View File
@@ -4,11 +4,10 @@
use crate::network::models::{ContractInformation, NetworkDetails};
use crate::node_status_api::models::AxumResult;
use crate::support::http::state::AppState;
use axum::extract::{Query, State};
use axum::{extract, Router};
use axum::extract::State;
use axum::{extract, Json, Router};
use nym_api_requests::models::ChainStatusResponse;
use nym_contracts_common::ContractBuildInformation;
use nym_http_api_common::{FormattedResponse, OutputParams};
use std::collections::HashMap;
use tower_http::compression::CompressionLayer;
use utoipa::ToSchema;
@@ -30,21 +29,13 @@ pub(crate) fn nym_network_routes() -> Router<AppState> {
get,
path = "/v1/network/details",
responses(
(status = 200, content(
(NetworkDetails = "application/json"),
(NetworkDetails = "application/yaml"),
(NetworkDetails = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = NetworkDetails)
)
)]
async fn network_details(
Query(output): Query<OutputParams>,
extract::State(state): extract::State<AppState>,
) -> FormattedResponse<NetworkDetails> {
let output = output.output.unwrap_or_default();
output.to_response(state.network_details().to_owned())
) -> axum::Json<NetworkDetails> {
state.network_details().to_owned().into()
}
#[utoipa::path(
@@ -52,20 +43,10 @@ async fn network_details(
get,
path = "/v1/network/chain-status",
responses(
(status = 200, content(
(ChainStatusResponse = "application/json"),
(ChainStatusResponse = "application/yaml"),
(ChainStatusResponse = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = ChainStatusResponse)
)
)]
async fn chain_status(
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> AxumResult<FormattedResponse<ChainStatusResponse>> {
let output = output.output.unwrap_or_default();
async fn chain_status(State(state): State<AppState>) -> AxumResult<Json<ChainStatusResponse>> {
let chain_status = state
.chain_status_cache
.get_or_refresh(&state.nyxd_client)
@@ -73,7 +54,7 @@ async fn chain_status(
let connected_nyxd = state.network_details.connected_nyxd;
Ok(output.to_response(ChainStatusResponse {
Ok(Json(ChainStatusResponse {
connected_nyxd,
status: chain_status,
}))
@@ -106,34 +87,25 @@ pub struct ContractInformationContractVersion {
get,
path = "/v1/network/nym-contracts",
responses(
(status = 200, content(
(HashMap<String, ContractInformationContractVersion> = "application/json"),
(HashMap<String, ContractInformationContractVersion> = "application/yaml"),
(HashMap<String, ContractInformationContractVersion> = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = HashMap<String, ContractInformationContractVersion>)
)
)]
async fn nym_contracts(
Query(output): Query<OutputParams>,
extract::State(state): extract::State<AppState>,
) -> FormattedResponse<HashMap<String, ContractInformation<cw2::ContractVersion>>> {
let output = output.output.unwrap_or_default();
) -> axum::Json<HashMap<String, ContractInformation<cw2::ContractVersion>>> {
let info = state.nym_contract_cache().contract_details().await;
output.to_response(
info.iter()
.map(|(contract, info)| {
(
contract.to_owned(),
ContractInformation {
address: info.address.as_ref().map(|a| a.to_string()),
details: info.base.clone(),
},
)
})
.collect::<HashMap<_, _>>(),
)
info.iter()
.map(|(contract, info)| {
(
contract.to_owned(),
ContractInformation {
address: info.address.as_ref().map(|a| a.to_string()),
details: info.base.clone(),
},
)
})
.collect::<HashMap<_, _>>()
.into()
}
#[allow(dead_code)] // not dead, used in OpenAPI docs
@@ -148,32 +120,23 @@ pub struct ContractInformationBuildInformation {
get,
path = "/v1/network/nym-contracts-detailed",
responses(
(status = 200, content(
(HashMap<String, ContractInformationBuildInformation> = "application/json"),
(HashMap<String, ContractInformationBuildInformation> = "application/yaml"),
(HashMap<String, ContractInformationBuildInformation> = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = HashMap<String, ContractInformationBuildInformation>)
)
)]
async fn nym_contracts_detailed(
Query(output): Query<OutputParams>,
extract::State(state): extract::State<AppState>,
) -> FormattedResponse<HashMap<String, ContractInformation<ContractBuildInformation>>> {
let output = output.output.unwrap_or_default();
) -> axum::Json<HashMap<String, ContractInformation<ContractBuildInformation>>> {
let info = state.nym_contract_cache().contract_details().await;
output.to_response(
info.iter()
.map(|(contract, info)| {
(
contract.to_owned(),
ContractInformation {
address: info.address.as_ref().map(|a| a.to_string()),
details: info.detailed.clone(),
},
)
})
.collect::<HashMap<_, _>>(),
)
info.iter()
.map(|(contract, info)| {
(
contract.to_owned(),
ContractInformation {
address: info.address.as_ref().map(|a| a.to_string()),
details: info.detailed.clone(),
},
)
})
.collect::<HashMap<_, _>>()
.into()
}
+5 -14
View File
@@ -4,11 +4,10 @@
use crate::node_status_api::models::AxumResult;
use crate::support::caching::cache::UninitialisedCache;
use crate::support::http::state::AppState;
use axum::extract::{Query, State};
use axum::extract::State;
use axum::routing::get;
use axum::Router;
use axum::{Json, Router};
use nym_api_requests::models::ConfigScoreDataResponse;
use nym_http_api_common::{FormattedResponse, OutputParams};
use nym_mixnet_contract_common::NodeId;
use serde::Deserialize;
use utoipa::IntoParams;
@@ -44,25 +43,17 @@ struct MixIdParam {
path = "/config-score-details",
context_path = "/v1/status",
responses(
(status = 200, content(
(ConfigScoreDataResponse = "application/json"),
(ConfigScoreDataResponse = "application/yaml"),
(ConfigScoreDataResponse = "application/bincode")
))
(status = 200, body = ConfigScoreDataResponse)
),
params(OutputParams)
)]
async fn config_score_details(
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> AxumResult<FormattedResponse<ConfigScoreDataResponse>> {
let output = output.output.unwrap_or_default();
) -> AxumResult<Json<ConfigScoreDataResponse>> {
let data = state
.nym_contract_cache()
.maybe_config_score_data_owned()
.await
.ok_or(UninitialisedCache)?;
Ok(output.to_response(data.into_inner().into()))
Ok(Json(data.into_inner().into()))
}
@@ -22,7 +22,6 @@ use nym_api_requests::models::{
MixNodeBondAnnotated, MixnodeCoreStatusResponse, MixnodeStatusReportResponse,
MixnodeUptimeHistoryResponse, RewardEstimationResponse, UptimeResponse,
};
use nym_http_api_common::{FormattedResponse, Output, OutputParams};
use serde::Deserialize;
use utoipa::IntoParams;
@@ -104,23 +103,17 @@ pub(super) fn network_monitor_routes() -> Router<AppState> {
get,
path = "/v1/status/gateway/{identity}/report",
responses(
(status = 200, content(
(GatewayStatusReportResponse = "application/json"),
(GatewayStatusReportResponse = "application/yaml"),
(GatewayStatusReportResponse = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = GatewayStatusReportResponse)
)
)]
#[deprecated]
async fn gateway_report(
Path(identity): Path<String>,
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> AxumResult<FormattedResponse<GatewayStatusReportResponse>> {
let output = output.output.unwrap_or_default();
Ok(output.to_response(_gateway_report(state.node_status_cache(), &identity).await?))
) -> AxumResult<Json<GatewayStatusReportResponse>> {
Ok(Json(
_gateway_report(state.node_status_cache(), &identity).await?,
))
}
#[utoipa::path(
@@ -128,23 +121,15 @@ async fn gateway_report(
get,
path = "/v1/status/gateway/{identity}/history",
responses(
(status = 200, content(
(GatewayUptimeHistoryResponse = "application/json"),
(GatewayUptimeHistoryResponse = "application/yaml"),
(GatewayUptimeHistoryResponse = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = GatewayUptimeHistoryResponse)
)
)]
#[deprecated]
async fn gateway_uptime_history(
Path(identity): Path<String>,
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> AxumResult<FormattedResponse<GatewayUptimeHistoryResponse>> {
let output = output.output.unwrap_or_default();
Ok(output.to_response(
) -> AxumResult<Json<GatewayUptimeHistoryResponse>> {
Ok(Json(
_gateway_uptime_history(state.storage(), state.nym_contract_cache(), &identity).await?,
))
}
@@ -153,7 +138,6 @@ async fn gateway_uptime_history(
#[into_params(parameter_in = Query)]
struct SinceQueryParams {
since: Option<i64>,
output: Option<Output>,
}
#[utoipa::path(
@@ -164,22 +148,18 @@ struct SinceQueryParams {
),
path = "/v1/status/gateway/{identity}/core-status-count",
responses(
(status = 200, content(
(GatewayCoreStatusResponse = "application/json"),
(GatewayCoreStatusResponse = "application/yaml"),
(GatewayCoreStatusResponse = "application/bincode")
))
),
(status = 200, body = GatewayCoreStatusResponse)
)
)]
#[deprecated]
async fn gateway_core_status_count(
Path(identity): Path<String>,
Query(SinceQueryParams { since, output }): Query<SinceQueryParams>,
Query(SinceQueryParams { since }): Query<SinceQueryParams>,
State(state): State<AppState>,
) -> AxumResult<FormattedResponse<GatewayCoreStatusResponse>> {
Ok(output
.unwrap_or_default()
.to_response(_gateway_core_status_count(state.storage(), &identity, since).await?))
) -> AxumResult<Json<GatewayCoreStatusResponse>> {
Ok(Json(
_gateway_core_status_count(state.storage(), &identity, since).await?,
))
}
#[utoipa::path(
@@ -187,71 +167,57 @@ async fn gateway_core_status_count(
get,
path = "/v1/status/gateway/{identity}/avg_uptime",
responses(
(status = 200, content(
(GatewayUptimeResponse = "application/json"),
(GatewayUptimeResponse = "application/yaml"),
(GatewayUptimeResponse = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = GatewayUptimeResponse)
)
)]
#[deprecated]
async fn get_gateway_avg_uptime(
Path(identity): Path<String>,
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> AxumResult<FormattedResponse<GatewayUptimeResponse>> {
let output = output.output.unwrap_or_default();
Ok(output.to_response(_get_gateway_avg_uptime(state.node_status_cache(), &identity).await?))
) -> AxumResult<Json<GatewayUptimeResponse>> {
Ok(Json(
_get_gateway_avg_uptime(state.node_status_cache(), &identity).await?,
))
}
#[utoipa::path(
tag = "network-monitor-status",
get,
params(
MixIdParam
),
path = "/v1/status/mixnode/{mix_id}/report",
responses(
(status = 200, content(
(MixnodeStatusReportResponse = "application/json"),
(MixnodeStatusReportResponse = "application/yaml"),
(MixnodeStatusReportResponse = "application/bincode")
))
),
params(OutputParams, MixIdParam)
(status = 200, body = MixnodeStatusReportResponse)
)
)]
#[deprecated]
async fn mixnode_report(
Path(MixIdParam { mix_id }): Path<MixIdParam>,
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> AxumResult<FormattedResponse<MixnodeStatusReportResponse>> {
let output = output.output.unwrap_or_default();
Ok(output.to_response(_mixnode_report(state.node_status_cache(), mix_id).await?))
) -> AxumResult<Json<MixnodeStatusReportResponse>> {
Ok(Json(
_mixnode_report(state.node_status_cache(), mix_id).await?,
))
}
#[utoipa::path(
tag = "network-monitor-status",
get,
params(
MixIdParam
),
path = "/v1/status/mixnode/{mix_id}/history",
responses(
(status = 200, content(
(MixnodeUptimeHistoryResponse = "application/json"),
(MixnodeUptimeHistoryResponse = "application/yaml"),
(MixnodeUptimeHistoryResponse = "application/bincode")
))
),
params(MixIdParam, OutputParams)
(status = 200, body = MixnodeUptimeHistoryResponse)
)
)]
#[deprecated]
async fn mixnode_uptime_history(
Path(MixIdParam { mix_id }): Path<MixIdParam>,
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> AxumResult<FormattedResponse<MixnodeUptimeHistoryResponse>> {
let output = output.output.unwrap_or_default();
Ok(output.to_response(
) -> AxumResult<Json<MixnodeUptimeHistoryResponse>> {
Ok(Json(
_mixnode_uptime_history(state.storage(), state.nym_contract_cache(), mix_id).await?,
))
}
@@ -264,48 +230,37 @@ async fn mixnode_uptime_history(
),
path = "/v1/status/mixnode/{mix_id}/core-status-count",
responses(
(status = 200, content(
(MixnodeCoreStatusResponse = "application/json"),
(MixnodeCoreStatusResponse = "application/yaml"),
(MixnodeCoreStatusResponse = "application/bincode")
))
),
(status = 200, body = MixnodeCoreStatusResponse)
)
)]
#[deprecated]
async fn mixnode_core_status_count(
Path(MixIdParam { mix_id }): Path<MixIdParam>,
Query(SinceQueryParams { since, output }): Query<SinceQueryParams>,
Query(SinceQueryParams { since }): Query<SinceQueryParams>,
State(state): State<AppState>,
) -> AxumResult<FormattedResponse<MixnodeCoreStatusResponse>> {
let output = output.unwrap_or_default();
Ok(output.to_response(_mixnode_core_status_count(state.storage(), mix_id, since).await?))
) -> AxumResult<Json<MixnodeCoreStatusResponse>> {
Ok(Json(
_mixnode_core_status_count(state.storage(), mix_id, since).await?,
))
}
#[utoipa::path(
tag = "network-monitor-status",
get,
params(
MixIdParam, OutputParams
MixIdParam
),
path = "/v1/status/mixnode/{mix_id}/reward-estimation",
responses(
(status = 200, content(
(RewardEstimationResponse = "application/json"),
(RewardEstimationResponse = "application/yaml"),
(RewardEstimationResponse = "application/bincode")
))
),
(status = 200, body = RewardEstimationResponse)
)
)]
#[deprecated]
async fn get_mixnode_reward_estimation(
Path(MixIdParam { mix_id }): Path<MixIdParam>,
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> AxumResult<FormattedResponse<RewardEstimationResponse>> {
let output = output.output.unwrap_or_default();
Ok(output.to_response(
) -> AxumResult<Json<RewardEstimationResponse>> {
Ok(Json(
_get_mixnode_reward_estimation(
state.node_status_cache(),
state.nym_contract_cache(),
@@ -319,28 +274,21 @@ async fn get_mixnode_reward_estimation(
tag = "network-monitor-status",
post,
params(
OutputParams, MixIdParam
ComputeRewardEstParam, MixIdParam
),
path = "/v1/status/mixnode/{mix_id}/compute-reward-estimation",
request_body = ComputeRewardEstParam,
responses(
(status = 200, content(
(RewardEstimationResponse = "application/json"),
(RewardEstimationResponse = "application/yaml"),
(RewardEstimationResponse = "application/bincode")
))
),
(status = 200, body = RewardEstimationResponse)
)
)]
#[deprecated]
async fn compute_mixnode_reward_estimation(
Path(MixIdParam { mix_id }): Path<MixIdParam>,
Query(output): Query<OutputParams>,
State(state): State<AppState>,
Json(user_reward_param): Json<ComputeRewardEstParam>,
) -> AxumResult<FormattedResponse<RewardEstimationResponse>> {
let output = output.output.unwrap_or_default();
Ok(output.to_response(
) -> AxumResult<Json<RewardEstimationResponse>> {
Ok(Json(
_compute_mixnode_reward_estimation(
&user_reward_param,
state.node_status_cache(),
@@ -355,26 +303,21 @@ async fn compute_mixnode_reward_estimation(
tag = "network-monitor-status",
get,
params(
MixIdParam, OutputParams
MixIdParam
),
path = "/v1/status/mixnode/{mix_id}/avg_uptime",
responses(
(status = 200, content(
(UptimeResponse = "application/json"),
(UptimeResponse = "application/yaml"),
(UptimeResponse = "application/bincode")
))
),
(status = 200, body = UptimeResponse)
)
)]
#[deprecated]
async fn get_mixnode_avg_uptime(
Path(MixIdParam { mix_id }): Path<MixIdParam>,
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> AxumResult<FormattedResponse<UptimeResponse>> {
let output = output.output.unwrap_or_default();
Ok(output.to_response(_get_mixnode_avg_uptime(state.node_status_cache(), mix_id).await?))
) -> AxumResult<Json<UptimeResponse>> {
Ok(Json(
_get_mixnode_avg_uptime(state.node_status_cache(), mix_id).await?,
))
}
#[utoipa::path(
@@ -382,22 +325,14 @@ async fn get_mixnode_avg_uptime(
get,
path = "/v1/status/mixnodes/detailed-unfiltered",
responses(
(status = 200, content(
(MixNodeBondAnnotated = "application/json"),
(MixNodeBondAnnotated = "application/yaml"),
(MixNodeBondAnnotated = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = MixNodeBondAnnotated)
)
)]
#[deprecated]
pub async fn get_mixnodes_detailed_unfiltered(
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> FormattedResponse<Vec<MixNodeBondAnnotated>> {
let output = output.output.unwrap_or_default();
output.to_response(_get_mixnodes_detailed_unfiltered(state.node_status_cache()).await)
) -> Json<Vec<MixNodeBondAnnotated>> {
Json(_get_mixnodes_detailed_unfiltered(state.node_status_cache()).await)
}
#[utoipa::path(
@@ -405,22 +340,14 @@ pub async fn get_mixnodes_detailed_unfiltered(
get,
path = "/v1/status/gateways/detailed",
responses(
(status = 200, content(
(GatewayBondAnnotated = "application/json"),
(GatewayBondAnnotated = "application/yaml"),
(GatewayBondAnnotated = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = GatewayBondAnnotated)
)
)]
#[deprecated]
pub async fn get_gateways_detailed(
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> FormattedResponse<Vec<GatewayBondAnnotated>> {
let output = output.output.unwrap_or_default();
output.to_response(_get_legacy_gateways_detailed(state.node_status_cache()).await)
) -> Json<Vec<GatewayBondAnnotated>> {
Json(_get_legacy_gateways_detailed(state.node_status_cache()).await)
}
#[utoipa::path(
@@ -428,20 +355,12 @@ pub async fn get_gateways_detailed(
get,
path = "/v1/status/gateways/detailed-unfiltered",
responses(
(status = 200, content(
(GatewayBondAnnotated = "application/json"),
(GatewayBondAnnotated = "application/yaml"),
(GatewayBondAnnotated = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = GatewayBondAnnotated)
)
)]
#[deprecated]
pub async fn get_gateways_detailed_unfiltered(
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> FormattedResponse<Vec<GatewayBondAnnotated>> {
let output = output.output.unwrap_or_default();
output.to_response(_get_legacy_gateways_detailed_unfiltered(state.node_status_cache()).await)
) -> Json<Vec<GatewayBondAnnotated>> {
Json(_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 anyhow::bail;
use axum::extract::{Path, Query, State};
use axum::Json;
use nym_api_requests::models::{
GatewayTestResultResponse, MixnodeTestResultResponse, NetworkMonitorRunDetailsResponse,
PartialTestResult, TestNode, TestRoute,
};
use nym_api_requests::pagination::Pagination;
use nym_http_api_common::{FormattedResponse, OutputParams};
use nym_mixnet_contract_common::NodeId;
use std::cmp::min;
use std::collections::{BTreeMap, HashMap};
@@ -173,18 +173,14 @@ async fn _mixnode_test_results(
),
path = "/v1/status/mixnodes/unstable/{mix_id}/test-results",
responses(
(status = 200, content(
(MixnodeTestResultResponse = "application/json"),
(MixnodeTestResultResponse = "application/yaml"),
(MixnodeTestResultResponse = "application/bincode")
))
),
(status = 200, body = MixnodeTestResultResponse)
)
)]
pub async fn mixnode_test_results(
Path(mix_id): Path<NodeId>,
Query(pagination): Query<PaginationRequest>,
State(state): State<AppState>,
) -> AxumResult<FormattedResponse<MixnodeTestResultResponse>> {
) -> AxumResult<Json<MixnodeTestResultResponse>> {
let page = pagination.page.unwrap_or_default();
let per_page = min(
pagination
@@ -192,7 +188,6 @@ pub async fn mixnode_test_results(
.unwrap_or(DEFAULT_TEST_RESULTS_PAGE_SIZE),
MAX_TEST_RESULTS_PAGE_SIZE,
);
let output = pagination.output.unwrap_or_default();
match _mixnode_test_results(
mix_id,
@@ -203,7 +198,7 @@ pub async fn mixnode_test_results(
)
.await
{
Ok(res) => Ok(output.to_response(res)),
Ok(res) => Ok(Json(res)),
Err(err) => Err(AxumErrorResponse::internal_msg(format!(
"failed to retrieve mixnode test results for node {mix_id}: {err}"
))),
@@ -277,18 +272,14 @@ async fn _gateway_test_results(
),
path = "/v1/status/gateways/unstable/{identity}/test-results",
responses(
(status = 200, content(
(GatewayTestResultResponse = "application/json"),
(GatewayTestResultResponse = "application/yaml"),
(GatewayTestResultResponse = "application/bincode")
))
),
(status = 200, body = GatewayTestResultResponse)
)
)]
pub async fn gateway_test_results(
Path(gateway_identity): Path<String>,
Query(pagination): Query<PaginationRequest>,
State(state): State<AppState>,
) -> AxumResult<FormattedResponse<GatewayTestResultResponse>> {
) -> AxumResult<Json<GatewayTestResultResponse>> {
let page = pagination.page.unwrap_or_default();
let per_page = min(
pagination
@@ -296,7 +287,6 @@ pub async fn gateway_test_results(
.unwrap_or(DEFAULT_TEST_RESULTS_PAGE_SIZE),
MAX_TEST_RESULTS_PAGE_SIZE,
);
let output = pagination.output.unwrap_or_default();
match _gateway_test_results(
&gateway_identity,
@@ -307,7 +297,7 @@ pub async fn gateway_test_results(
)
.await
{
Ok(res) => Ok(output.to_response(res)),
Ok(res) => Ok(Json(res)),
Err(err) => Err(AxumErrorResponse::internal_msg(format!(
"failed to retrieve mixnode test results for gateway {gateway_identity}: {err}"
))),
@@ -359,23 +349,15 @@ async fn _latest_monitor_run_report(
get,
path = "/v1/status/network-monitor/unstable/run/{monitor_run_id}/details",
responses(
(status = 200, content(
(NetworkMonitorRunDetailsResponse = "application/json"),
(NetworkMonitorRunDetailsResponse = "application/yaml"),
(NetworkMonitorRunDetailsResponse = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = NetworkMonitorRunDetailsResponse)
)
)]
pub async fn monitor_run_report(
Path(monitor_run_id): Path<i64>,
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> AxumResult<FormattedResponse<NetworkMonitorRunDetailsResponse>> {
let output = output.output.unwrap_or_default();
) -> AxumResult<Json<NetworkMonitorRunDetailsResponse>> {
match _monitor_run_report(monitor_run_id, state.storage()).await {
Ok(res) => Ok(output.to_response(res)),
Ok(res) => Ok(Json(res)),
Err(err) => Err(AxumErrorResponse::internal_msg(format!(
"failed to retrieve monitor run report for run {monitor_run_id}: {err}"
))),
@@ -387,22 +369,14 @@ pub async fn monitor_run_report(
get,
path = "/v1/status/network-monitor/unstable/run/latest/details",
responses(
(status = 200, content(
(NetworkMonitorRunDetailsResponse = "application/json"),
(NetworkMonitorRunDetailsResponse = "application/yaml"),
(NetworkMonitorRunDetailsResponse = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = NetworkMonitorRunDetailsResponse)
)
)]
pub async fn latest_monitor_run_report(
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> AxumResult<FormattedResponse<NetworkMonitorRunDetailsResponse>> {
let output = output.output.unwrap_or_default();
) -> AxumResult<Json<NetworkMonitorRunDetailsResponse>> {
match _latest_monitor_run_report(state.storage()).await {
Ok(res) => Ok(output.to_response(res)),
Ok(res) => Ok(Json(res)),
Err(err) => Err(AxumErrorResponse::internal_msg(format!(
"failed to retrieve the latest monitor run report: {err}"
))),
@@ -11,14 +11,13 @@ use crate::node_status_api::helpers::{
};
use crate::node_status_api::models::{AxumErrorResponse, AxumResult};
use crate::support::http::state::AppState;
use axum::extract::{Path, Query, State};
use axum::extract::{Path, State};
use axum::routing::{get, post};
use axum::Json;
use axum::Router;
use nym_api_requests::models::{
MixNodeBondAnnotated, MixnodeStatusResponse, StakeSaturationResponse,
};
use nym_http_api_common::{FormattedResponse, OutputParams};
use nym_mixnet_contract_common::NodeId;
use nym_types::monitoring::MonitorMessage;
use tracing::error;
@@ -154,22 +153,15 @@ pub(crate) async fn submit_node_monitoring_results(
),
path = "/v1/status/mixnode/{mix_id}/status",
responses(
(status = 200, content(
(MixnodeStatusResponse = "application/json"),
(MixnodeStatusResponse = "application/yaml"),
(MixnodeStatusResponse = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = MixnodeStatusResponse)
)
)]
#[deprecated]
async fn get_mixnode_status(
Path(MixIdParam { mix_id }): Path<MixIdParam>,
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> FormattedResponse<MixnodeStatusResponse> {
let output = output.output.unwrap_or_default();
output.to_response(_get_mixnode_status(state.nym_contract_cache(), mix_id).await)
) -> Json<MixnodeStatusResponse> {
Json(_get_mixnode_status(state.nym_contract_cache(), mix_id).await)
}
#[utoipa::path(
@@ -180,23 +172,15 @@ async fn get_mixnode_status(
),
path = "/v1/status/mixnode/{mix_id}/stake-saturation",
responses(
(status = 200, content(
(StakeSaturationResponse = "application/json"),
(StakeSaturationResponse = "application/yaml"),
(StakeSaturationResponse = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = StakeSaturationResponse)
)
)]
#[deprecated]
async fn get_mixnode_stake_saturation(
Path(mix_id): Path<NodeId>,
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> AxumResult<FormattedResponse<StakeSaturationResponse>> {
let output = output.output.unwrap_or_default();
Ok(output.to_response(
) -> AxumResult<Json<StakeSaturationResponse>> {
Ok(Json(
_get_mixnode_stake_saturation(
state.node_status_cache(),
state.nym_contract_cache(),
@@ -210,28 +194,22 @@ async fn get_mixnode_stake_saturation(
tag = "status",
get,
params(
MixIdParam, OutputParams
MixIdParam
),
path = "/v1/status/mixnode/{mix_id}/inclusion-probability",
responses(
(status = 200, content(
(nym_api_requests::models::InclusionProbabilityResponse = "application/json"),
(nym_api_requests::models::InclusionProbabilityResponse = "application/yaml"),
(nym_api_requests::models::InclusionProbabilityResponse = "application/bincode")
))
),
(status = 200, body = nym_api_requests::models::InclusionProbabilityResponse)
)
)]
#[deprecated]
#[allow(deprecated)]
async fn get_mixnode_inclusion_probability(
Path(mix_id): Path<NodeId>,
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> AxumResult<FormattedResponse<nym_api_requests::models::InclusionProbabilityResponse>> {
let output = output.output.unwrap_or_default();
Ok(output
.to_response(_get_mixnode_inclusion_probability(state.node_status_cache(), mix_id).await?))
) -> AxumResult<Json<nym_api_requests::models::InclusionProbabilityResponse>> {
Ok(Json(
_get_mixnode_inclusion_probability(state.node_status_cache(), mix_id).await?,
))
}
#[utoipa::path(
@@ -239,23 +217,17 @@ async fn get_mixnode_inclusion_probability(
get,
path = "/v1/status/mixnodes/inclusion-probability",
responses(
(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)
(status = 200, body = nym_api_requests::models::AllInclusionProbabilitiesResponse)
)
)]
#[deprecated]
#[allow(deprecated)]
async fn get_mixnode_inclusion_probabilities(
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> AxumResult<FormattedResponse<nym_api_requests::models::AllInclusionProbabilitiesResponse>> {
let output = output.output.unwrap_or_default();
Ok(output.to_response(_get_mixnode_inclusion_probabilities(state.node_status_cache()).await?))
) -> AxumResult<Json<nym_api_requests::models::AllInclusionProbabilitiesResponse>> {
Ok(Json(
_get_mixnode_inclusion_probabilities(state.node_status_cache()).await?,
))
}
#[utoipa::path(
@@ -263,22 +235,14 @@ async fn get_mixnode_inclusion_probabilities(
get,
path = "/v1/status/mixnodes/detailed",
responses(
(status = 200, content(
(MixNodeBondAnnotated = "application/json"),
(MixNodeBondAnnotated = "application/yaml"),
(MixNodeBondAnnotated = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = MixNodeBondAnnotated)
)
)]
#[deprecated]
pub async fn get_mixnodes_detailed(
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> FormattedResponse<Vec<MixNodeBondAnnotated>> {
let output = output.output.unwrap_or_default();
output.to_response(_get_legacy_mixnodes_detailed(state.node_status_cache()).await)
) -> Json<Vec<MixNodeBondAnnotated>> {
Json(_get_legacy_mixnodes_detailed(state.node_status_cache()).await)
}
#[utoipa::path(
@@ -286,22 +250,14 @@ pub async fn get_mixnodes_detailed(
get,
path = "/v1/status/mixnodes/rewarded/detailed",
responses(
(status = 200, content(
(MixNodeBondAnnotated = "application/json"),
(MixNodeBondAnnotated = "application/yaml"),
(MixNodeBondAnnotated = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = MixNodeBondAnnotated)
)
)]
#[deprecated]
pub async fn get_rewarded_set_detailed(
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> FormattedResponse<Vec<MixNodeBondAnnotated>> {
let output = output.output.unwrap_or_default();
output.to_response(
) -> Json<Vec<MixNodeBondAnnotated>> {
Json(
_get_rewarded_set_legacy_mixnodes_detailed(
state.node_status_cache(),
state.nym_contract_cache(),
@@ -315,22 +271,14 @@ pub async fn get_rewarded_set_detailed(
get,
path = "/v1/status/mixnodes/active/detailed",
responses(
(status = 200, content(
(MixNodeBondAnnotated = "application/json"),
(MixNodeBondAnnotated = "application/yaml"),
(MixNodeBondAnnotated = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = MixNodeBondAnnotated)
)
)]
#[deprecated]
pub async fn get_active_set_detailed(
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> FormattedResponse<Vec<MixNodeBondAnnotated>> {
let output = output.output.unwrap_or_default();
output.to_response(
) -> Json<Vec<MixNodeBondAnnotated>> {
Json(
_get_active_set_legacy_mixnodes_detailed(
state.node_status_cache(),
state.nym_contract_cache(),
+77 -185
View File
@@ -7,11 +7,10 @@ use crate::node_status_api::helpers::{
};
use crate::support::http::state::AppState;
use crate::support::legacy_helpers::{to_legacy_gateway, to_legacy_mixnode};
use axum::extract::{Query, State};
use axum::Router;
use axum::extract::State;
use axum::{Json, Router};
use nym_api_requests::legacy::LegacyMixNodeDetailsWithLayer;
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::RewardingParams, GatewayBond, Interval, NodeId};
use std::collections::HashSet;
@@ -56,32 +55,23 @@ pub(crate) fn nym_contract_cache_routes() -> Router<AppState> {
get,
path = "/v1/mixnodes",
responses(
(status = 200, content(
(Vec<LegacyMixNodeDetailsWithLayer> = "application/json"),
(Vec<LegacyMixNodeDetailsWithLayer> = "application/yaml"),
(Vec<LegacyMixNodeDetailsWithLayer> = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = Vec<LegacyMixNodeDetailsWithLayer>)
)
)]
#[deprecated]
async fn get_mixnodes(
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> FormattedResponse<Vec<LegacyMixNodeDetailsWithLayer>> {
let output = output.output.unwrap_or_default();
async fn get_mixnodes(State(state): State<AppState>) -> Json<Vec<LegacyMixNodeDetailsWithLayer>> {
let mut out = state.nym_contract_cache().legacy_mixnodes_filtered().await;
let Ok(describe_cache) = state.described_nodes_cache.get().await else {
return output.to_response(out);
return Json(out);
};
let Some(migrated_nymnodes) = state.nym_contract_cache().all_cached_nym_nodes().await else {
return output.to_response(out);
return Json(out);
};
let Ok(annotations) = state.node_annotations().await else {
return output.to_response(out);
return Json(out);
};
// safety: valid percentage value
@@ -110,7 +100,7 @@ async fn get_mixnodes(
out.push(node);
}
output.to_response(out)
Json(out)
}
// DEPRECATED: this endpoint now lives in `node_status_api`. Once all consumers are updated,
@@ -125,22 +115,14 @@ async fn get_mixnodes(
get,
path = "/v1/mixnodes/detailed",
responses(
(status = 200, content(
(Vec<MixNodeBondAnnotated> = "application/json"),
(Vec<MixNodeBondAnnotated> = "application/yaml"),
(Vec<MixNodeBondAnnotated> = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = Vec<MixNodeBondAnnotated>)
)
)]
#[deprecated]
async fn get_mixnodes_detailed(
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> FormattedResponse<Vec<MixNodeBondAnnotated>> {
let output = output.output.unwrap_or_default();
output.to_response(_get_legacy_mixnodes_detailed(state.node_status_cache()).await)
async fn get_mixnodes_detailed(State(state): State<AppState>) -> Json<Vec<MixNodeBondAnnotated>> {
_get_legacy_mixnodes_detailed(state.node_status_cache())
.await
.into()
}
#[utoipa::path(
@@ -148,21 +130,11 @@ async fn get_mixnodes_detailed(
get,
path = "/v1/gateways",
responses(
(status = 200, content(
(Vec<GatewayBond> = "application/json"),
(Vec<GatewayBond> = "application/yaml"),
(Vec<GatewayBond> = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = Vec<GatewayBond>)
)
)]
#[deprecated]
async fn get_gateways(
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> FormattedResponse<Vec<GatewayBond>> {
let output = output.output.unwrap_or_default();
async fn get_gateways(State(state): State<AppState>) -> Json<Vec<GatewayBond>> {
// legacy
let mut out: Vec<GatewayBond> = state
.nym_contract_cache()
@@ -173,15 +145,15 @@ async fn get_gateways(
.collect();
let Ok(describe_cache) = state.described_nodes_cache.get().await else {
return output.to_response(out);
return Json(out);
};
let Some(migrated_nymnodes) = state.nym_contract_cache().all_cached_nym_nodes().await else {
return output.to_response(out);
return Json(out);
};
let Ok(annotations) = state.node_annotations().await else {
return output.to_response(out);
return Json(out);
};
// safety: valid percentage value
@@ -210,7 +182,7 @@ async fn get_gateways(
out.push(node);
}
output.to_response(out)
Json(out)
}
#[utoipa::path(
@@ -218,22 +190,14 @@ async fn get_gateways(
get,
path = "/v1/mixnodes/rewarded",
responses(
(status = 200, content(
(Vec<LegacyMixNodeDetailsWithLayer> = "application/json"),
(Vec<LegacyMixNodeDetailsWithLayer> = "application/yaml"),
(Vec<LegacyMixNodeDetailsWithLayer> = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = Vec<LegacyMixNodeDetailsWithLayer>)
)
)]
#[deprecated]
async fn get_rewarded_set(
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> FormattedResponse<Vec<LegacyMixNodeDetailsWithLayer>> {
let output = output.output.unwrap_or_default();
output.to_response(
) -> Json<Vec<LegacyMixNodeDetailsWithLayer>> {
Json(
state
.nym_contract_cache()
.legacy_v1_rewarded_set_mixnodes()
@@ -254,28 +218,19 @@ async fn get_rewarded_set(
get,
path = "/v1/mixnodes/rewarded/detailed",
responses(
(status = 200, content(
(Vec<MixNodeBondAnnotated> = "application/json"),
(Vec<MixNodeBondAnnotated> = "application/yaml"),
(Vec<MixNodeBondAnnotated> = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = Vec<MixNodeBondAnnotated>)
)
)]
#[deprecated]
async fn get_rewarded_set_detailed(
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> FormattedResponse<Vec<MixNodeBondAnnotated>> {
let output = output.output.unwrap_or_default();
output.to_response(
_get_rewarded_set_legacy_mixnodes_detailed(
state.node_status_cache(),
state.nym_contract_cache(),
)
.await,
) -> Json<Vec<MixNodeBondAnnotated>> {
_get_rewarded_set_legacy_mixnodes_detailed(
state.node_status_cache(),
state.nym_contract_cache(),
)
.await
.into()
}
#[utoipa::path(
@@ -283,21 +238,11 @@ async fn get_rewarded_set_detailed(
get,
path = "/v1/mixnodes/active",
responses(
(status = 200, content(
(Vec<LegacyMixNodeDetailsWithLayer> = "application/json"),
(Vec<LegacyMixNodeDetailsWithLayer> = "application/yaml"),
(Vec<LegacyMixNodeDetailsWithLayer> = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = Vec<LegacyMixNodeDetailsWithLayer>)
)
)]
#[deprecated]
async fn get_active_set(
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> FormattedResponse<Vec<LegacyMixNodeDetailsWithLayer>> {
let output = output.output.unwrap_or_default();
async fn get_active_set(State(state): State<AppState>) -> Json<Vec<LegacyMixNodeDetailsWithLayer>> {
let mut out = state
.nym_contract_cache()
.legacy_v1_active_set_mixnodes()
@@ -305,19 +250,19 @@ async fn get_active_set(
.clone();
let Some(rewarded_set) = state.nym_contract_cache().rewarded_set().await else {
return output.to_response(out);
return Json(out);
};
let Ok(describe_cache) = state.described_nodes_cache.get().await else {
return output.to_response(out);
return Json(out);
};
let Some(migrated_nymnodes) = state.nym_contract_cache().all_cached_nym_nodes().await else {
return output.to_response(out);
return Json(out);
};
let Ok(annotations) = state.node_annotations().await else {
return output.to_response(out);
return Json(out);
};
// safety: valid percentage value
@@ -350,7 +295,7 @@ async fn get_active_set(
out.push(node);
}
output.to_response(out)
Json(out)
}
// DEPRECATED: this endpoint now lives in `node_status_api`. Once all consumers are updated,
@@ -366,28 +311,14 @@ async fn get_active_set(
get,
path = "/v1/mixnodes/active/detailed",
responses(
(status = 200, content(
(Vec<MixNodeBondAnnotated> = "application/json"),
(Vec<MixNodeBondAnnotated> = "application/yaml"),
(Vec<MixNodeBondAnnotated> = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = Vec<MixNodeBondAnnotated>)
)
)]
#[deprecated]
async fn get_active_set_detailed(
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> 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,
)
async fn get_active_set_detailed(State(state): State<AppState>) -> Json<Vec<MixNodeBondAnnotated>> {
_get_active_set_legacy_mixnodes_detailed(state.node_status_cache(), state.nym_contract_cache())
.await
.into()
}
#[utoipa::path(
@@ -395,31 +326,22 @@ async fn get_active_set_detailed(
get,
path = "/v1/mixnodes/blacklisted",
responses(
(status = 200, content(
(Option<HashSet<NodeId>> = "application/json"),
(Option<HashSet<NodeId>> = "application/yaml"),
(Option<HashSet<NodeId>> = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = Option<HashSet<NodeId>>)
)
)]
#[deprecated]
async fn get_blacklisted_mixnodes(
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> FormattedResponse<Option<HashSet<NodeId>>> {
let output = output.output.unwrap_or_default();
async fn get_blacklisted_mixnodes(State(state): State<AppState>) -> Json<Option<HashSet<NodeId>>> {
let blacklist = state
.nym_contract_cache()
.mixnodes_blacklist()
.await
.to_owned();
if blacklist.is_empty() {
output.to_response(None)
None
} else {
output.to_response(Some(blacklist))
Some(blacklist)
}
.into()
}
#[utoipa::path(
@@ -427,28 +349,18 @@ async fn get_blacklisted_mixnodes(
get,
path = "/v1/gateways/blacklisted",
responses(
(status = 200, content(
(Option<HashSet<NodeId>> = "application/json"),
(Option<HashSet<NodeId>> = "application/yaml"),
(Option<HashSet<NodeId>> = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = Option<HashSet<String>>)
)
)]
#[deprecated]
async fn get_blacklisted_gateways(
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> FormattedResponse<Option<HashSet<String>>> {
let output = output.output.unwrap_or_default();
async fn get_blacklisted_gateways(State(state): State<AppState>) -> Json<Option<HashSet<String>>> {
let cache = state.nym_contract_cache();
let blacklist = cache.gateways_blacklist().await.clone();
if blacklist.is_empty() {
output.to_response(None)
Json(None)
} else {
let gateways = cache.legacy_gateways_all().await;
output.to_response(Some(
Json(Some(
gateways
.into_iter()
.filter(|g| blacklist.contains(&g.node_id))
@@ -463,27 +375,18 @@ async fn get_blacklisted_gateways(
get,
path = "/v1/epoch/reward_params",
responses(
(status = 200, content(
(Option<RewardingParams> = "application/json"),
(Option<RewardingParams> = "application/yaml"),
(Option<RewardingParams> = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = Option<RewardingParams>)
)
)]
async fn get_interval_reward_params(
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> FormattedResponse<Option<RewardingParams>> {
let output = output.output.unwrap_or_default();
output.to_response(
state
.nym_contract_cache()
.interval_reward_params()
.await
.to_owned(),
)
) -> Json<Option<RewardingParams>> {
state
.nym_contract_cache()
.interval_reward_params()
.await
.to_owned()
.into()
}
#[utoipa::path(
@@ -491,25 +394,14 @@ async fn get_interval_reward_params(
get,
path = "/v1/epoch/current",
responses(
(status = 200, content(
(Option<Interval> = "application/json"),
(Option<Interval> = "application/yaml"),
(Option<Interval> = "application/bincode")
))
),
params(OutputParams)
)]
async fn get_current_epoch(
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> FormattedResponse<Option<Interval>> {
let output = output.output.unwrap_or_default();
output.to_response(
state
.nym_contract_cache()
.current_interval()
.await
.to_owned(),
(status = 200, body = Option<Interval>)
)
)]
async fn get_current_epoch(State(state): State<AppState>) -> Json<Option<Interval>> {
state
.nym_contract_cache()
.current_interval()
.await
.to_owned()
.into()
}
+12 -27
View File
@@ -3,11 +3,10 @@
use crate::support::http::state::AppState;
use crate::support::legacy_helpers::{to_legacy_gateway, to_legacy_mixnode};
use axum::extract::{Query, State};
use axum::Router;
use axum::extract::State;
use axum::{Json, Router};
use nym_api_requests::legacy::LegacyMixNodeBondWithLayer;
use nym_api_requests::models::{LegacyDescribedGateway, LegacyDescribedMixNode};
use nym_http_api_common::{FormattedResponse, OutputParams};
use tower_http::compression::CompressionLayer;
// we want to mark the routes as deprecated in swagger, but still expose them
@@ -30,29 +29,22 @@ pub(crate) fn legacy_nym_node_routes() -> Router<AppState> {
get,
path = "/v1/gateways/described",
responses(
(status = 200, content(
(Vec<LegacyDescribedGateway> = "application/json"),
(Vec<LegacyDescribedGateway> = "application/yaml"),
(Vec<LegacyDescribedGateway> = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = Vec<LegacyDescribedGateway>)
)
)]
#[deprecated]
async fn get_gateways_described(
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> FormattedResponse<Vec<LegacyDescribedGateway>> {
) -> Json<Vec<LegacyDescribedGateway>> {
let contract_cache = state.nym_contract_cache();
let describe_cache = state.described_nodes_cache();
let output = output.output.unwrap_or_default();
// legacy
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
let Ok(describe_cache) = describe_cache.get().await else {
return output.to_response(legacy.into_iter().map(Into::into).collect());
return Json(legacy.into_iter().map(Into::into).collect());
};
let migrated_nymnodes = state.nym_contract_cache().nym_nodes().await;
@@ -83,7 +75,7 @@ async fn get_gateways_described(
})
}
output.to_response(out)
Json(out)
}
#[utoipa::path(
@@ -91,22 +83,15 @@ async fn get_gateways_described(
get,
path = "/v1/mixnodes/described",
responses(
(status = 200, content(
(Vec<LegacyDescribedMixNode> = "application/json"),
(Vec<LegacyDescribedMixNode> = "application/yaml"),
(Vec<LegacyDescribedMixNode> = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = Vec<LegacyDescribedMixNode>)
)
)]
#[deprecated]
async fn get_mixnodes_described(
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> FormattedResponse<Vec<LegacyDescribedMixNode>> {
) -> Json<Vec<LegacyDescribedMixNode>> {
let contract_cache = state.nym_contract_cache();
let describe_cache = state.described_nodes_cache();
let output = output.output.unwrap_or_default();
let legacy: Vec<LegacyMixNodeBondWithLayer> = contract_cache
.legacy_mixnodes_filtered()
@@ -117,7 +102,7 @@ async fn get_mixnodes_described(
// 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 {
return output.to_response(legacy.into_iter().map(Into::into).collect());
return Json(legacy.into_iter().map(Into::into).collect());
};
let migrated_nymnodes = state.nym_contract_cache().nym_nodes().await;
@@ -146,5 +131,5 @@ async fn get_mixnodes_described(
})
}
output.to_response(out)
Json(out)
}
+33 -89
View File
@@ -15,9 +15,9 @@ use nym_api_requests::models::{
};
use nym_api_requests::pagination::{PaginatedResponse, Pagination};
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::NymNodeDetails;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::time::Duration;
use time::{Date, OffsetDateTime};
@@ -55,20 +55,10 @@ pub(crate) fn nym_node_routes() -> Router<AppState> {
path = "/rewarded-set",
context_path = "/v1/nym-nodes",
responses(
(status = 200, content(
(RewardedSetResponse = "application/json"),
(RewardedSetResponse = "application/yaml"),
(RewardedSetResponse = "application/bincode")
))
(status = 200, body = RewardedSetResponse)
),
params(OutputParams)
)]
async fn rewarded_set(
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> AxumResult<FormattedResponse<RewardedSetResponse>> {
let output = output.output.unwrap_or_default();
async fn rewarded_set(State(state): State<AppState>) -> AxumResult<Json<RewardedSetResponse>> {
let cached_rewarded_set = state
.nym_contract_cache()
.rewarded_set()
@@ -77,7 +67,7 @@ async fn rewarded_set(
.ok_or(UninitialisedCache)?
.into_inner();
Ok(output.to_response(
Ok(Json(
nym_mixnet_contract_common::EpochRewardedSet::from(cached_rewarded_set).into(),
))
}
@@ -146,21 +136,16 @@ async fn refresh_described(
path = "/noise",
context_path = "/v1/nym-nodes",
responses(
(status = 200, content(
(PaginatedResponse<NoiseDetails> = "application/json"),
(PaginatedResponse<NoiseDetails> = "application/yaml"),
(PaginatedResponse<NoiseDetails> = "application/bincode")
))
(status = 200, body = PaginatedResponse<NoiseDetails>)
),
params(PaginationRequest)
)]
async fn nodes_noise(
State(state): State<AppState>,
Query(pagination): Query<PaginationRequest>,
) -> AxumResult<FormattedResponse<PaginatedResponse<NoiseDetails>>> {
) -> AxumResult<Json<PaginatedResponse<NoiseDetails>>> {
// TODO: implement it
let _ = pagination;
let output = pagination.output.unwrap_or_default();
let describe_cache = state.describe_nodes_cache_data().await?;
@@ -182,7 +167,7 @@ async fn nodes_noise(
let total = nodes.len();
Ok(output.to_response(PaginatedResponse {
Ok(Json(PaginatedResponse {
pagination: Pagination {
total,
page: 0,
@@ -198,26 +183,21 @@ async fn nodes_noise(
path = "/bonded",
context_path = "/v1/nym-nodes",
responses(
(status = 200, content(
(PaginatedResponse<NymNodeDetails> = "application/json"),
(PaginatedResponse<NymNodeDetails> = "application/yaml"),
(PaginatedResponse<NymNodeDetails> = "application/bincode")
))
(status = 200, body = PaginatedResponse<NymNodeDetails>)
),
params(PaginationRequest)
)]
async fn get_bonded_nodes(
State(state): State<AppState>,
Query(pagination): Query<PaginationRequest>,
) -> FormattedResponse<PaginatedResponse<NymNodeDetails>> {
) -> Json<PaginatedResponse<NymNodeDetails>> {
// TODO: implement it
let _ = pagination;
let output = pagination.output.unwrap_or_default();
let details = state.nym_contract_cache().nym_nodes().await;
let total = details.len();
output.to_response(PaginatedResponse {
Json(PaginatedResponse {
pagination: Pagination {
total,
page: 0,
@@ -233,26 +213,21 @@ async fn get_bonded_nodes(
path = "/described",
context_path = "/v1/nym-nodes",
responses(
(status = 200, content(
(PaginatedResponse<NymNodeDescription> = "application/json"),
(PaginatedResponse<NymNodeDescription> = "application/yaml"),
(PaginatedResponse<NymNodeDescription> = "application/bincode")
))
(status = 200, body = PaginatedResponse<NymNodeDescription>)
),
params(PaginationRequest)
)]
async fn get_described_nodes(
State(state): State<AppState>,
Query(pagination): Query<PaginationRequest>,
) -> AxumResult<FormattedResponse<PaginatedResponse<NymNodeDescription>>> {
) -> AxumResult<Json<PaginatedResponse<NymNodeDescription>>> {
// TODO: implement it
let _ = pagination;
let output = pagination.output.unwrap_or_default();
let cache = state.described_nodes_cache.get().await?;
let descriptions = cache.all_nodes().cloned().collect::<Vec<_>>();
Ok(output.to_response(PaginatedResponse {
Ok(Json(PaginatedResponse {
pagination: Pagination {
total: descriptions.len(),
page: 0,
@@ -268,28 +243,21 @@ async fn get_described_nodes(
path = "/annotation/{node_id}",
context_path = "/v1/nym-nodes",
responses(
(status = 200, content(
(AnnotationResponse = "application/json"),
(AnnotationResponse = "application/yaml"),
(AnnotationResponse = "application/bincode")
))
(status = 200, body = AnnotationResponse)
),
params(NodeIdParam, OutputParams),
params(NodeIdParam),
)]
async fn get_node_annotation(
Path(NodeIdParam { node_id }): Path<NodeIdParam>,
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> AxumResult<FormattedResponse<AnnotationResponse>> {
let output = output.output.unwrap_or_default();
) -> AxumResult<Json<AnnotationResponse>> {
let annotations = state
.node_status_cache
.node_annotations()
.await
.ok_or_else(AxumErrorResponse::internal)?;
Ok(output.to_response(AnnotationResponse {
Ok(Json(AnnotationResponse {
node_id,
annotation: annotations.get(&node_id).cloned(),
}))
@@ -301,28 +269,21 @@ async fn get_node_annotation(
path = "/performance/{node_id}",
context_path = "/v1/nym-nodes",
responses(
(status = 200, content(
(NodePerformanceResponse = "application/json"),
(NodePerformanceResponse = "application/yaml"),
(NodePerformanceResponse = "application/bincode")
))
(status = 200, body = NodePerformanceResponse)
),
params(NodeIdParam, OutputParams),
params(NodeIdParam),
)]
async fn get_current_node_performance(
Path(NodeIdParam { node_id }): Path<NodeIdParam>,
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> AxumResult<FormattedResponse<NodePerformanceResponse>> {
let output = output.output.unwrap_or_default();
) -> AxumResult<Json<NodePerformanceResponse>> {
let annotations = state
.node_status_cache
.node_annotations()
.await
.ok_or_else(AxumErrorResponse::internal)?;
Ok(output.to_response(NodePerformanceResponse {
Ok(Json(NodePerformanceResponse {
node_id,
performance: annotations
.get(&node_id)
@@ -331,13 +292,12 @@ async fn get_current_node_performance(
}
// todo; probably extract it to requests crate
#[derive(Debug, Serialize, Deserialize, Copy, Clone, IntoParams, ToSchema)]
#[derive(Debug, Serialize, Deserialize, Copy, Clone, IntoParams, ToSchema, JsonSchema)]
#[into_params(parameter_in = Query)]
pub(crate) struct DateQuery {
#[schema(value_type = String, example = "1970-01-01")]
#[schemars(with = "String")]
pub(crate) date: Date,
pub(crate) output: Option<Output>,
}
#[utoipa::path(
@@ -346,27 +306,21 @@ pub(crate) struct DateQuery {
path = "/historical-performance/{node_id}",
context_path = "/v1/nym-nodes",
responses(
(status = 200, content(
(NodeDatePerformanceResponse = "application/json"),
(NodeDatePerformanceResponse = "application/yaml"),
(NodeDatePerformanceResponse = "application/bincode")
))
(status = 200, body = NodeDatePerformanceResponse)
),
params(DateQuery, NodeIdParam)
)]
async fn get_historical_performance(
Path(NodeIdParam { node_id }): Path<NodeIdParam>,
Query(DateQuery { date, output }): Query<DateQuery>,
Query(DateQuery { date }): Query<DateQuery>,
State(state): State<AppState>,
) -> AxumResult<FormattedResponse<NodeDatePerformanceResponse>> {
let output = output.unwrap_or_default();
) -> AxumResult<Json<NodeDatePerformanceResponse>> {
let uptime = state
.storage()
.get_historical_node_uptime_on(node_id, date)
.await?;
Ok(output.to_response(NodeDatePerformanceResponse {
Ok(Json(NodeDatePerformanceResponse {
node_id,
date,
performance: uptime.and_then(|u| {
@@ -383,11 +337,7 @@ async fn get_historical_performance(
path = "/performance-history/{node_id}",
context_path = "/v1/nym-nodes",
responses(
(status = 200, content(
(PerformanceHistoryResponse = "application/json"),
(PerformanceHistoryResponse = "application/yaml"),
(PerformanceHistoryResponse = "application/bincode")
))
(status = 200, body = PerformanceHistoryResponse)
),
params(PaginationRequest, NodeIdParam)
)]
@@ -395,10 +345,9 @@ async fn get_node_performance_history(
Path(NodeIdParam { node_id }): Path<NodeIdParam>,
State(state): State<AppState>,
Query(pagination): Query<PaginationRequest>,
) -> AxumResult<FormattedResponse<PerformanceHistoryResponse>> {
) -> AxumResult<Json<PerformanceHistoryResponse>> {
// TODO: implement it
let _ = pagination;
let output = pagination.output.unwrap_or_default();
let history = state
.storage()
@@ -409,7 +358,7 @@ async fn get_node_performance_history(
.collect::<Vec<_>>();
let total = history.len();
Ok(output.to_response(PerformanceHistoryResponse {
Ok(Json(PerformanceHistoryResponse {
node_id,
history: PaginatedResponse {
pagination: Pagination {
@@ -428,11 +377,7 @@ async fn get_node_performance_history(
path = "/uptime-history/{node_id}",
context_path = "/v1/nym-nodes",
responses(
(status = 200, content(
(PerformanceHistoryResponse = "application/json"),
(PerformanceHistoryResponse = "application/yaml"),
(PerformanceHistoryResponse = "application/bincode")
))
(status = 200, body = PerformanceHistoryResponse)
),
params(PaginationRequest, NodeIdParam)
)]
@@ -440,10 +385,9 @@ async fn get_node_uptime_history(
Path(NodeIdParam { node_id }): Path<NodeIdParam>,
State(state): State<AppState>,
Query(pagination): Query<PaginationRequest>,
) -> AxumResult<FormattedResponse<UptimeHistoryResponse>> {
) -> AxumResult<Json<UptimeHistoryResponse>> {
// TODO: implement it
let _ = pagination;
let output = pagination.output.unwrap_or_default();
let history = state
.storage()
@@ -454,7 +398,7 @@ async fn get_node_uptime_history(
.collect::<Vec<_>>();
let total = history.len();
Ok(output.to_response(UptimeHistoryResponse {
Ok(Json(UptimeHistoryResponse {
node_id,
history: PaginatedResponse {
pagination: Pagination {
@@ -5,8 +5,8 @@ use crate::node_status_api::models::{AxumErrorResponse, AxumResult};
use crate::nym_nodes::handlers::unstable::NodesParamsWithRole;
use crate::support::http::state::AppState;
use axum::extract::{Query, State};
use axum::Json;
use nym_api_requests::nym_nodes::{CachedNodesResponse, FullFatNode};
use nym_http_api_common::FormattedResponse;
#[utoipa::path(
tag = "Unstable Nym Nodes",
@@ -22,6 +22,6 @@ use nym_http_api_common::FormattedResponse;
pub(super) async fn nodes_detailed(
_state: State<AppState>,
_query_params: Query<NodesParamsWithRole>,
) -> AxumResult<FormattedResponse<CachedNodesResponse<FullFatNode>>> {
) -> AxumResult<Json<CachedNodesResponse<FullFatNode>>> {
Err(AxumErrorResponse::not_implemented())
}
+5 -19
View File
@@ -30,13 +30,12 @@ use crate::nym_nodes::handlers::unstable::skimmed::{
};
use crate::support::http::helpers::PaginationRequest;
use crate::support::http::state::AppState;
use axum::extract::{Query, State};
use axum::extract::State;
use axum::routing::{get, post};
use axum::{Json, Router};
use nym_api_requests::nym_nodes::{
NodeRoleQueryParam, NodesByAddressesRequestBody, NodesByAddressesResponse,
};
use nym_http_api_common::{FormattedResponse, Output, OutputParams};
use serde::Deserialize;
use std::collections::HashMap;
use tower_http::compression::CompressionLayer;
@@ -99,8 +98,6 @@ struct NodesParamsWithRole {
// the client already knows about the latest topology state, allowing a `no-updates` response
// instead of wasting bandwidth serving an unchanged topology.
epoch_id: Option<u32>,
output: Option<Output>,
}
#[derive(Debug, Deserialize, utoipa::IntoParams)]
@@ -116,7 +113,6 @@ struct NodesParams {
// the client already knows about the latest topology state, allowing a `no-updates` response
// instead of wasting bandwidth serving an unchanged topology.
epoch_id: Option<u32>,
output: Option<Output>,
}
impl From<NodesParamsWithRole> for NodesParams {
@@ -127,7 +123,6 @@ impl From<NodesParamsWithRole> for NodesParams {
page: params.page,
per_page: params.per_page,
epoch_id: params.epoch_id,
output: params.output,
}
}
}
@@ -135,7 +130,6 @@ impl From<NodesParamsWithRole> for NodesParams {
impl<'a> From<&'a NodesParams> for PaginationRequest {
fn from(params: &'a NodesParams) -> Self {
PaginationRequest {
output: params.output,
page: params.page,
per_page: params.per_page,
}
@@ -149,19 +143,13 @@ impl<'a> From<&'a NodesParams> for PaginationRequest {
path = "/by-addresses",
context_path = "/v1/unstable/nym-nodes",
responses(
(status = 200, content(
(NodesByAddressesResponse = "application/json"),
(NodesByAddressesResponse = "application/yaml"),
(NodesByAddressesResponse = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = NodesByAddressesResponse)
)
)]
async fn nodes_by_addresses(
Query(output): Query<OutputParams>,
state: State<AppState>,
Json(body): Json<NodesByAddressesRequestBody>,
) -> AxumResult<FormattedResponse<NodesByAddressesResponse>> {
) -> AxumResult<Json<NodesByAddressesResponse>> {
// if the request is too big, simply reject it
if body.addresses.len() > 100 {
return Err(AxumErrorResponse::bad_request(
@@ -169,8 +157,6 @@ async fn nodes_by_addresses(
));
}
let output = output.output.unwrap_or_default();
// TODO: perhaps introduce different cache because realistically nym-api will receive
// request for the same couple addresses from all nodes in quick succession
let describe_cache = state.describe_nodes_cache_data().await?;
@@ -180,5 +166,5 @@ async fn nodes_by_addresses(
existence.insert(address, describe_cache.node_with_address(address));
}
Ok(output.to_response(NodesByAddressesResponse { existence }))
Ok(Json(NodesByAddressesResponse { existence }))
}
@@ -5,8 +5,8 @@ use crate::node_status_api::models::{AxumErrorResponse, AxumResult};
use crate::nym_nodes::handlers::unstable::NodesParamsWithRole;
use crate::support::http::state::AppState;
use axum::extract::{Query, State};
use axum::Json;
use nym_api_requests::nym_nodes::{CachedNodesResponse, SemiSkimmedNode};
use nym_http_api_common::FormattedResponse;
#[utoipa::path(
tag = "Unstable Nym Nodes",
@@ -22,6 +22,6 @@ use nym_http_api_common::FormattedResponse;
pub(super) async fn nodes_expanded(
_state: State<AppState>,
_query_params: Query<NodesParamsWithRole>,
) -> AxumResult<FormattedResponse<CachedNodesResponse<SemiSkimmedNode>>> {
) -> AxumResult<Json<CachedNodesResponse<SemiSkimmedNode>>> {
Err(AxumErrorResponse::not_implemented())
}
@@ -8,6 +8,7 @@ use crate::nym_nodes::handlers::unstable::{NodesParams, NodesParamsWithRole};
use crate::support::caching::Cache;
use crate::support::http::state::AppState;
use axum::extract::{Query, State};
use axum::Json;
use nym_api_requests::models::{
NodeAnnotation, NymNodeDescription, OffsetDateTimeJsonSchemaWrapper,
};
@@ -15,18 +16,15 @@ use nym_api_requests::nym_nodes::{
CachedNodesResponse, NodeRole, NodeRoleQueryParam, PaginatedCachedNodesResponse, SkimmedNode,
};
use nym_api_requests::pagination::PaginatedResponse;
use nym_http_api_common::{FormattedResponse, Output};
use nym_mixnet_contract_common::NodeId;
use nym_topology::CachedEpochRewardedSet;
use std::collections::HashMap;
use std::future::Future;
use std::time::Duration;
use tokio::sync::RwLockReadGuard;
use tracing::trace;
use utoipa::ToSchema;
pub type PaginatedSkimmedNodes =
AxumResult<FormattedResponse<PaginatedCachedNodesResponse<SkimmedNode>>>;
pub type PaginatedSkimmedNodes = AxumResult<Json<PaginatedCachedNodesResponse<SkimmedNode>>>;
/// Given all relevant caches, build part of response for JUST Nym Nodes
fn build_nym_nodes_response<'a, NI>(
@@ -99,7 +97,6 @@ async fn build_skimmed_nodes_response<'a, NI, LG, Fut, LN>(
nym_nodes_subset: NI,
annotated_legacy_nodes_getter: LG,
active_only: bool,
output: Output,
) -> PaginatedSkimmedNodes
where
// iterator returning relevant subset of nym-nodes (like mixing nym-nodes, entries, etc.)
@@ -127,20 +124,18 @@ where
// (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 Some(interval) = state
let maybe_interval = state
.nym_contract_cache()
.current_interval()
.await
.to_owned()
else {
// if we can't obtain interval information, it means caches are not valid
return Err(AxumErrorResponse::service_unavailable());
};
.to_owned();
// 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 client_known_epoch == interval.current_epoch_id() {
return Ok(output.to_response(PaginatedCachedNodesResponse::no_updates()));
if let Some(ref interval) = maybe_interval {
if client_known_epoch == interval.current_epoch_id() {
return Ok(Json(PaginatedCachedNodesResponse::no_updates()));
}
}
}
@@ -157,8 +152,8 @@ where
describe_cache.timestamp(),
]);
return Ok(output.to_response(
PaginatedCachedNodesResponse::new_full(refreshed_at, nodes).fresh(Some(interval)),
return Ok(Json(
PaginatedCachedNodesResponse::new_full(refreshed_at, nodes).fresh(maybe_interval),
));
}
@@ -181,19 +176,9 @@ where
annotated_legacy_nodes.timestamp(),
]);
let base_response = output.to_response(
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))
Ok(Json(
PaginatedCachedNodesResponse::new_full(refreshed_at, nodes).fresh(maybe_interval),
))
}
/// Deprecated query that gets ALL gateways
@@ -204,30 +189,22 @@ where
path = "/gateways/skimmed",
context_path = "/v1/unstable/nym-nodes",
responses(
(status = 200, content(
(CachedNodesResponse<SkimmedNode> = "application/json"),
(CachedNodesResponse<SkimmedNode> = "application/yaml"),
(CachedNodesResponse<SkimmedNode> = "application/bincode")
))
),
(status = 200, body = CachedNodesResponse<SkimmedNode>)
)
)]
#[deprecated(note = "use '/v1/unstable/nym-nodes/entry-gateways/skimmed/all' instead")]
pub(super) async fn deprecated_gateways_basic(
state: State<AppState>,
query_params: Query<NodesParams>,
) -> AxumResult<FormattedResponse<CachedNodesResponse<SkimmedNode>>> {
let output = query_params.output.unwrap_or_default();
) -> AxumResult<Json<CachedNodesResponse<SkimmedNode>>> {
// 1. call '/v1/unstable/skimmed/entry-gateways/all'
let all_gateways = entry_gateways_basic_all(state, query_params)
.await?
.into_inner();
let all_gateways = entry_gateways_basic_all(state, query_params).await?;
// 3. return result
Ok(output.to_response(CachedNodesResponse {
Ok(Json(CachedNodesResponse {
refreshed_at: all_gateways.refreshed_at,
// 2. remove pagination
nodes: all_gateways.nodes.data,
nodes: all_gateways.0.nodes.data,
}))
}
@@ -239,40 +216,30 @@ pub(super) async fn deprecated_gateways_basic(
path = "/mixnodes/skimmed",
context_path = "/v1/unstable/nym-nodes",
responses(
(status = 200, content(
(CachedNodesResponse<SkimmedNode> = "application/json"),
(CachedNodesResponse<SkimmedNode> = "application/yaml"),
(CachedNodesResponse<SkimmedNode> = "application/bincode")
))
),
(status = 200, body = CachedNodesResponse<SkimmedNode>)
)
)]
#[deprecated(note = "use '/v1/unstable/nym-nodes/skimmed/mixnodes/active' instead")]
pub(super) async fn deprecated_mixnodes_basic(
state: State<AppState>,
query_params: Query<NodesParams>,
) -> AxumResult<FormattedResponse<CachedNodesResponse<SkimmedNode>>> {
let output = query_params.output.unwrap_or_default();
) -> AxumResult<Json<CachedNodesResponse<SkimmedNode>>> {
// 1. call '/v1/unstable/nym-nodes/skimmed/mixnodes/active'
let active_mixnodes = mixnodes_basic_active(state, query_params)
.await?
.into_inner();
let active_mixnodes = mixnodes_basic_active(state, query_params).await?;
// 3. return result
Ok(output.to_response(CachedNodesResponse {
Ok(Json(CachedNodesResponse {
refreshed_at: active_mixnodes.refreshed_at,
// 2. remove pagination
nodes: active_mixnodes.nodes.data,
nodes: active_mixnodes.0.nodes.data,
}))
}
async fn nodes_basic(
state: State<AppState>,
Query(query_params): Query<NodesParams>,
Query(_query_params): Query<NodesParams>,
active_only: bool,
) -> 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
// 1. grab all relevant described nym-nodes
@@ -314,7 +281,10 @@ async fn nodes_basic(
legacy_gateways.timestamp(),
]);
Ok(output.to_response(PaginatedCachedNodesResponse::new_full(refreshed_at, nodes)))
Ok(Json(PaginatedCachedNodesResponse::new_full(
refreshed_at,
nodes,
)))
}
#[allow(dead_code)] // not dead, used in OpenAPI docs
@@ -335,12 +305,8 @@ pub struct PaginatedCachedNodesResponseSchema {
path = "",
context_path = "/v1/unstable/nym-nodes/skimmed",
responses(
(status = 200, content(
(PaginatedCachedNodesResponseSchema = "application/json"),
(PaginatedCachedNodesResponseSchema = "application/yaml"),
(PaginatedCachedNodesResponseSchema = "application/bincode")
))
),
(status = 200, body = PaginatedCachedNodesResponseSchema)
)
)]
pub(super) async fn nodes_basic_all(
state: State<AppState>,
@@ -372,12 +338,8 @@ pub(super) async fn nodes_basic_all(
path = "/active",
context_path = "/v1/unstable/nym-nodes/skimmed",
responses(
(status = 200, content(
(PaginatedCachedNodesResponseSchema = "application/json"),
(PaginatedCachedNodesResponseSchema = "application/yaml"),
(PaginatedCachedNodesResponseSchema = "application/bincode")
))
),
(status = 200, body = PaginatedCachedNodesResponseSchema)
)
)]
pub(super) async fn nodes_basic_active(
state: State<AppState>,
@@ -405,8 +367,6 @@ async fn mixnodes_basic(
query_params: Query<NodesParams>,
active_only: bool,
) -> PaginatedSkimmedNodes {
let output = query_params.output.unwrap_or_default();
// 1. grab all relevant described nym-nodes
let describe_cache = state.describe_nodes_cache_data().await?;
let mixing_nym_nodes = describe_cache.mixing_nym_nodes();
@@ -417,7 +377,6 @@ async fn mixnodes_basic(
mixing_nym_nodes,
|state| state.legacy_mixnode_annotations(),
active_only,
output,
)
.await
}
@@ -431,12 +390,8 @@ async fn mixnodes_basic(
path = "/mixnodes/all",
context_path = "/v1/unstable/nym-nodes/skimmed",
responses(
(status = 200, content(
(PaginatedCachedNodesResponseSchema = "application/json"),
(PaginatedCachedNodesResponseSchema = "application/yaml"),
(PaginatedCachedNodesResponseSchema = "application/bincode")
))
),
(status = 200, body = PaginatedCachedNodesResponseSchema)
)
)]
pub(super) async fn mixnodes_basic_all(
state: State<AppState>,
@@ -454,12 +409,8 @@ pub(super) async fn mixnodes_basic_all(
path = "/mixnodes/active",
context_path = "/v1/unstable/nym-nodes/skimmed",
responses(
(status = 200, content(
(PaginatedCachedNodesResponseSchema = "application/json"),
(PaginatedCachedNodesResponseSchema = "application/yaml"),
(PaginatedCachedNodesResponseSchema = "application/bincode")
))
),
(status = 200, body = PaginatedCachedNodesResponseSchema)
)
)]
pub(super) async fn mixnodes_basic_active(
state: State<AppState>,
@@ -473,8 +424,6 @@ async fn entry_gateways_basic(
query_params: Query<NodesParams>,
active_only: bool,
) -> PaginatedSkimmedNodes {
let output = query_params.output.unwrap_or_default();
// 1. grab all relevant described nym-nodes
let describe_cache = state.describe_nodes_cache_data().await?;
let mixing_nym_nodes = describe_cache.entry_capable_nym_nodes();
@@ -485,7 +434,6 @@ async fn entry_gateways_basic(
mixing_nym_nodes,
|state| state.legacy_gateways_annotations(),
active_only,
output,
)
.await
}
@@ -499,12 +447,8 @@ async fn entry_gateways_basic(
path = "/entry-gateways/active",
context_path = "/v1/unstable/nym-nodes/skimmed",
responses(
(status = 200, content(
(PaginatedCachedNodesResponseSchema = "application/json"),
(PaginatedCachedNodesResponseSchema = "application/yaml"),
(PaginatedCachedNodesResponseSchema = "application/bincode")
))
),
(status = 200, body = PaginatedCachedNodesResponseSchema)
)
)]
pub(super) async fn entry_gateways_basic_active(
state: State<AppState>,
@@ -522,12 +466,8 @@ pub(super) async fn entry_gateways_basic_active(
path = "/entry-gateways/all",
context_path = "/v1/unstable/nym-nodes/skimmed",
responses(
(status = 200, content(
(PaginatedCachedNodesResponseSchema = "application/json"),
(PaginatedCachedNodesResponseSchema = "application/yaml"),
(PaginatedCachedNodesResponseSchema = "application/bincode")
))
),
(status = 200, body = PaginatedCachedNodesResponseSchema)
)
)]
pub(super) async fn entry_gateways_basic_all(
state: State<AppState>,
@@ -541,8 +481,6 @@ async fn exit_gateways_basic(
query_params: Query<NodesParams>,
active_only: bool,
) -> PaginatedSkimmedNodes {
let output = query_params.output.unwrap_or_default();
// 1. grab all relevant described nym-nodes
let describe_cache = state.describe_nodes_cache_data().await?;
let mixing_nym_nodes = describe_cache.exit_capable_nym_nodes();
@@ -553,7 +491,6 @@ async fn exit_gateways_basic(
mixing_nym_nodes,
|state| state.legacy_gateways_annotations(),
active_only,
output,
)
.await
}
@@ -567,12 +504,8 @@ async fn exit_gateways_basic(
path = "/exit-gateways/active",
context_path = "/v1/unstable/nym-nodes/skimmed",
responses(
(status = 200, content(
(PaginatedCachedNodesResponseSchema = "application/json"),
(PaginatedCachedNodesResponseSchema = "application/yaml"),
(PaginatedCachedNodesResponseSchema = "application/bincode")
))
),
(status = 200, body = PaginatedCachedNodesResponseSchema)
)
)]
pub(super) async fn exit_gateways_basic_active(
state: State<AppState>,
@@ -590,12 +523,8 @@ pub(super) async fn exit_gateways_basic_active(
path = "/exit-gateways/all",
context_path = "/v1/unstable/nym-nodes/skimmed",
responses(
(status = 200, content(
(PaginatedCachedNodesResponseSchema = "application/json"),
(PaginatedCachedNodesResponseSchema = "application/yaml"),
(PaginatedCachedNodesResponseSchema = "application/bincode")
))
),
(status = 200, body = PaginatedCachedNodesResponseSchema)
)
)]
pub(super) async fn exit_gateways_basic_all(
state: State<AppState>,
+14 -40
View File
@@ -4,14 +4,14 @@
use crate::node_status_api::models::{AxumErrorResponse, AxumResult};
use crate::status::ApiStatusState;
use crate::support::http::state::AppState;
use axum::extract::{Query, State};
use axum::extract::State;
use axum::Json;
use axum::Router;
use nym_api_requests::models::{
ApiHealthResponse, ApiStatus, ChainStatus, SignerInformationResponse,
};
use nym_bin_common::build_information::BinaryBuildInformationOwned;
use nym_compact_ecash::Base58;
use nym_http_api_common::{FormattedResponse, OutputParams};
use std::time::Duration;
use time::OffsetDateTime;
@@ -30,22 +30,12 @@ pub(crate) fn api_status_routes() -> Router<AppState> {
get,
path = "/v1/api-status/health",
responses(
(status = 200, content(
(ApiHealthResponse = "application/json"),
(ApiHealthResponse = "application/yaml"),
(ApiHealthResponse = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = ApiHealthResponse)
)
)]
async fn health(
Query(output): Query<OutputParams>,
State(state): State<AppState>,
) -> FormattedResponse<ApiHealthResponse> {
async fn health(State(state): State<AppState>) -> Json<ApiHealthResponse> {
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 chain_status = match state
.chain_status_cache
@@ -71,7 +61,7 @@ async fn health(
chain_status,
uptime: uptime.as_secs(),
};
output.to_response(health)
Json(health)
}
#[utoipa::path(
@@ -79,21 +69,13 @@ async fn health(
get,
path = "/v1/api-status/build-information",
responses(
(status = 200, content(
(BinaryBuildInformationOwned = "application/json"),
(BinaryBuildInformationOwned = "application/yaml"),
(BinaryBuildInformationOwned = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = BinaryBuildInformationOwned)
)
)]
async fn build_information(
Query(output): Query<OutputParams>,
State(state): State<ApiStatusState>,
) -> FormattedResponse<BinaryBuildInformationOwned> {
let output = output.output.unwrap_or_default();
output.to_response(state.build_information.to_owned())
) -> Json<BinaryBuildInformationOwned> {
Json(state.build_information.to_owned())
}
#[utoipa::path(
@@ -101,25 +83,17 @@ async fn build_information(
get,
path = "/v1/api-status/signer-information",
responses(
(status = 200, content(
(SignerInformationResponse = "application/json"),
(SignerInformationResponse = "application/yaml"),
(SignerInformationResponse = "application/bincode")
))
),
params(OutputParams)
(status = 200, body = SignerInformationResponse)
)
)]
async fn signer_information(
Query(output): Query<OutputParams>,
State(state): State<ApiStatusState>,
) -> AxumResult<FormattedResponse<SignerInformationResponse>> {
) -> AxumResult<Json<SignerInformationResponse>> {
let signer_state = state.signer_information.as_ref().ok_or_else(|| {
AxumErrorResponse::internal_msg("this api does not expose zk-nym signing functionalities")
})?;
let output = output.output.unwrap_or_default();
Ok(output.to_response(SignerInformationResponse {
Ok(Json(SignerInformationResponse {
cosmos_address: signer_state.cosmos_address.clone(),
identity: signer_state.identity.clone(),
announce_address: signer_state.announce_address.clone(),
+2 -3
View File
@@ -1,15 +1,14 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use nym_http_api_common::Output;
use nym_mixnet_contract_common::NodeId;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use utoipa::{IntoParams, ToSchema};
#[derive(Serialize, Deserialize, Debug, ToSchema, IntoParams)]
#[derive(Serialize, Deserialize, Debug, JsonSchema, ToSchema, IntoParams)]
#[into_params(parameter_in = Query)]
pub struct PaginationRequest {
pub output: Option<Output>,
pub page: Option<u32>,
pub per_page: Option<u32>,
}
@@ -36,6 +36,6 @@ features = ["tokio"]
[features]
default = ["query-types"]
query-types = ["nym-http-api-common", "nym-http-api-common/output"]
query-types = ["nym-http-api-common"]
openapi = ["utoipa"]
tsify = ["dep:tsify", "wasm-bindgen"]
@@ -102,7 +102,7 @@ impl Monitor {
.with_timeout(self.nym_api_client_timeout)
.build::<&str>()?;
let api_client = NymApiClient::from(nym_api);
let api_client = NymApiClient { nym_api };
let described_nodes = api_client
.get_all_described_nodes()
@@ -187,7 +187,7 @@ impl Monitor {
tracing::info!("🟣 mixnodes_described: {}", mixnodes_described.len());
let mixing_assigned_nodes = api_client
.nym_api
.get_basic_active_mixing_assigned_nodes(false, None, None, false)
.get_basic_active_mixing_assigned_nodes(false, None, None)
.await
.log_error("get_basic_active_mixing_assigned_nodes")?
.nodes
@@ -62,7 +62,7 @@ async fn run(
.with_timeout(nym_api_client_timeout)
.build::<&str>()?;
let api_client = NymApiClient::from(nym_api);
let api_client = NymApiClient { nym_api };
//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?;
+1 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-node"
version = "1.10.0"
version = "1.9.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
+1 -1
View File
@@ -842,7 +842,7 @@ impl NymNode {
}
};
let client = NymApiClient::from(nym_api);
let client = NymApiClient { nym_api };
// 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());
+1 -1
View File
@@ -177,7 +177,7 @@ impl NetworkRefresher {
let mut this = NetworkRefresher {
querier: NodesQuerier {
client: NymApiClient::from(nym_api),
client: NymApiClient { nym_api },
nym_api_urls,
currently_used_api: 0,
},
+53 -70
View File
@@ -659,7 +659,7 @@ dependencies = [
"rand_core 0.6.4",
"ripemd",
"secp256k1",
"sha2 0.10.9",
"sha2 0.10.8",
"subtle",
"zeroize",
]
@@ -816,7 +816,7 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
dependencies = [
"sha2 0.10.9",
"sha2 0.10.8",
"tinyvec",
]
@@ -931,7 +931,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02260d489095346e5cafd04dea8e8cb54d1d74fcd759022a9b72986ebe9a1257"
dependencies = [
"serde",
"toml 0.8.22",
"toml 0.8.20",
]
[[package]]
@@ -1245,7 +1245,7 @@ dependencies = [
"p256",
"rand_core 0.6.4",
"rayon",
"sha2 0.10.9",
"sha2 0.10.8",
"thiserror 1.0.69",
]
@@ -1303,7 +1303,7 @@ dependencies = [
"schemars",
"serde",
"serde-json-wasm",
"sha2 0.10.9",
"sha2 0.10.8",
"static_assertions",
"thiserror 1.0.69",
]
@@ -1902,7 +1902,7 @@ dependencies = [
"ed25519",
"rand_core 0.6.4",
"serde",
"sha2 0.10.9",
"sha2 0.10.8",
"subtle",
"zeroize",
]
@@ -1918,7 +1918,7 @@ dependencies = [
"hashbrown 0.14.5",
"hex",
"rand_core 0.6.4",
"sha2 0.10.9",
"sha2 0.10.8",
"zeroize",
]
@@ -1957,7 +1957,7 @@ dependencies = [
"cc",
"memchr",
"rustc_version",
"toml 0.8.22",
"toml 0.8.20",
"vswhom",
"winreg 0.52.0",
]
@@ -3556,7 +3556,7 @@ dependencies = [
"ecdsa",
"elliptic-curve",
"once_cell",
"sha2 0.10.9",
"sha2 0.10.8",
"signature",
]
@@ -3965,7 +3965,7 @@ dependencies = [
"schemars",
"serde",
"serde_json",
"sha2 0.10.9",
"sha2 0.10.8",
"tendermint",
"tendermint-rpc",
"thiserror 2.0.12",
@@ -3979,6 +3979,7 @@ version = "0.6.0"
dependencies = [
"const-str",
"log",
"pretty_env_logger",
"schemars",
"serde",
"utoipa",
@@ -4014,7 +4015,7 @@ dependencies = [
"nym-pemstore",
"rand 0.8.5",
"serde",
"sha2 0.10.9",
"sha2 0.10.8",
"subtle",
"thiserror 2.0.12",
"zeroize",
@@ -4030,7 +4031,7 @@ dependencies = [
"nym-network-defaults",
"serde",
"thiserror 2.0.12",
"toml 0.8.22",
"toml 0.8.20",
"url",
]
@@ -4128,14 +4129,12 @@ name = "nym-http-api-client"
version = "0.1.0"
dependencies = [
"async-trait",
"bincode",
"bytes",
"encoding_rs",
"hickory-resolver",
"http 1.3.1",
"mime",
"nym-bin-common",
"nym-http-api-common",
"once_cell",
"reqwest 0.12.15",
"serde",
@@ -4146,15 +4145,6 @@ dependencies = [
"wasmtimer",
]
[[package]]
name = "nym-http-api-common"
version = "0.1.0"
dependencies = [
"bincode",
"serde",
"tracing",
]
[[package]]
name = "nym-mixnet-contract-common"
version = "0.6.0"
@@ -4278,7 +4268,7 @@ dependencies = [
"rs_merkle",
"schemars",
"serde",
"sha2 0.10.9",
"sha2 0.10.8",
"time",
"utoipa",
]
@@ -4303,7 +4293,7 @@ dependencies = [
"schemars",
"serde",
"serde_json",
"sha2 0.10.9",
"sha2 0.10.8",
"strum 0.26.3",
"thiserror 2.0.12",
"ts-rs",
@@ -4349,7 +4339,7 @@ dependencies = [
"reqwest 0.12.15",
"serde",
"serde_json",
"sha2 0.10.9",
"sha2 0.10.8",
"tendermint-rpc",
"thiserror 2.0.12",
"time",
@@ -4769,7 +4759,7 @@ dependencies = [
"ecdsa",
"elliptic-curve",
"primeorder",
"sha2 0.10.9",
"sha2 0.10.8",
]
[[package]]
@@ -4944,7 +4934,7 @@ checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0"
dependencies = [
"once_cell",
"pest",
"sha2 0.10.9",
"sha2 0.10.8",
]
[[package]]
@@ -5268,7 +5258,7 @@ version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35"
dependencies = [
"toml_edit 0.22.26",
"toml_edit 0.22.24",
]
[[package]]
@@ -5814,7 +5804,7 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb09b49230ba22e8c676e7b75dfe2887dea8121f18b530ae0ba519ce442d2b21"
dependencies = [
"sha2 0.10.9",
"sha2 0.10.8",
]
[[package]]
@@ -6309,9 +6299,9 @@ dependencies = [
[[package]]
name = "sha2"
version = "0.10.9"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -6672,7 +6662,7 @@ dependencies = [
"cfg-expr",
"heck 0.5.0",
"pkg-config",
"toml 0.8.22",
"toml 0.8.20",
"version-compare",
]
@@ -6817,7 +6807,7 @@ dependencies = [
"serde_json",
"tauri-utils",
"tauri-winres",
"toml 0.8.22",
"toml 0.8.20",
"walkdir",
]
@@ -6838,7 +6828,7 @@ dependencies = [
"semver",
"serde",
"serde_json",
"sha2 0.10.9",
"sha2 0.10.8",
"syn 2.0.100",
"tauri-utils",
"thiserror 2.0.12",
@@ -6875,7 +6865,7 @@ dependencies = [
"serde",
"serde_json",
"tauri-utils",
"toml 0.8.22",
"toml 0.8.20",
"walkdir",
]
@@ -7057,7 +7047,7 @@ dependencies = [
"serde_with",
"swift-rs",
"thiserror 2.0.12",
"toml 0.8.22",
"toml 0.8.20",
"url",
"urlpattern",
"uuid",
@@ -7071,7 +7061,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56eaa45f707bedf34d19312c26d350bc0f3c59a47e58e8adbeecdc850d2c13a0"
dependencies = [
"embed-resource",
"toml 0.8.22",
"toml 0.8.20",
]
[[package]]
@@ -7089,9 +7079,9 @@ dependencies = [
[[package]]
name = "tendermint"
version = "0.40.4"
version = "0.40.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc997743ecfd4864bbca8170d68d9b2bee24653b034210752c2d883ef4b838b1"
checksum = "ab2972a56891bc9173eaebdd51290bc848e448aa281881f3a4712bd8fe1899b3"
dependencies = [
"bytes",
"digest 0.10.7",
@@ -7108,7 +7098,7 @@ dependencies = [
"serde_bytes",
"serde_json",
"serde_repr",
"sha2 0.10.9",
"sha2 0.10.8",
"signature",
"subtle",
"subtle-encoding",
@@ -7119,23 +7109,23 @@ dependencies = [
[[package]]
name = "tendermint-config"
version = "0.40.4"
version = "0.40.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "069d1791f9b02a596abcd26eb72003b2e9906c6169a60fa82ffc080dd3a43fda"
checksum = "651cb680d41e586bb688cc17ab0b6dfe2e62a959c5742d1351fe0b084cd75d59"
dependencies = [
"flex-error",
"serde",
"serde_json",
"tendermint",
"toml 0.8.22",
"toml 0.8.20",
"url",
]
[[package]]
name = "tendermint-proto"
version = "0.40.4"
version = "0.40.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2c40e13d39ca19082d8a7ed22de7595979350319833698f8b1080f29620a094"
checksum = "ca44b9eaaa98e8440fbafe5593b8a32a1c395c094ac566b3eb50497f8bd2bee0"
dependencies = [
"bytes",
"flex-error",
@@ -7148,9 +7138,9 @@ dependencies = [
[[package]]
name = "tendermint-rpc"
version = "0.40.4"
version = "0.40.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35e0569a4b4cc42ff00df5a665be2858a39ff79df4790b176f1cd0e169bc0fc2"
checksum = "9367678af1a9fc6c064fab8b5d05f03c0fc243fe411581c00c7eed83f8ced380"
dependencies = [
"async-trait",
"bytes",
@@ -7397,21 +7387,21 @@ dependencies = [
[[package]]
name = "toml"
version = "0.8.22"
version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae"
checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit 0.22.26",
"toml_edit 0.22.24",
]
[[package]]
name = "toml_datetime"
version = "0.6.9"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3"
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
dependencies = [
"serde",
]
@@ -7440,24 +7430,17 @@ dependencies = [
[[package]]
name = "toml_edit"
version = "0.22.26"
version = "0.22.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e"
checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474"
dependencies = [
"indexmap 2.8.0",
"serde",
"serde_spanned",
"toml_datetime",
"toml_write",
"winnow 0.7.10",
"winnow 0.7.4",
]
[[package]]
name = "toml_write"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076"
[[package]]
name = "tower"
version = "0.5.2"
@@ -8670,9 +8653,9 @@ dependencies = [
[[package]]
name = "winnow"
version = "0.7.10"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec"
checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36"
dependencies = [
"memchr",
]
@@ -8767,7 +8750,7 @@ dependencies = [
"once_cell",
"percent-encoding",
"raw-window-handle",
"sha2 0.10.9",
"sha2 0.10.8",
"soup3",
"tao-macros",
"thiserror 2.0.12",
@@ -8904,7 +8887,7 @@ dependencies = [
"tracing",
"uds_windows",
"windows-sys 0.59.0",
"winnow 0.7.10",
"winnow 0.7.4",
"xdg-home",
"zbus_macros",
"zbus_names",
@@ -8934,7 +8917,7 @@ checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97"
dependencies = [
"serde",
"static_assertions",
"winnow 0.7.10",
"winnow 0.7.4",
"zvariant",
]
@@ -9065,7 +9048,7 @@ dependencies = [
"enumflags2",
"serde",
"static_assertions",
"winnow 0.7.10",
"winnow 0.7.4",
"zvariant_derive",
"zvariant_utils",
]
@@ -9094,5 +9077,5 @@ dependencies = [
"serde",
"static_assertions",
"syn 2.0.100",
"winnow 0.7.10",
"winnow 0.7.4",
]
@@ -4,7 +4,7 @@
[package]
name = "nym-network-requester"
license = "GPL-3.0"
version = "1.1.55"
version = "1.1.54"
authors.workspace = true
edition.workspace = true
rust-version = "1.70"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-cli"
version = "1.1.54"
version = "1.1.53"
authors.workspace = true
edition = "2021"
license.workspace = true
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nymvisor"
version = "0.1.19"
version = "0.1.18"
authors.workspace = true
repository.workspace = true
homepage.workspace = true