Feature/validator api client endpoints (#1024)
* Moved mixnode status route to node status api module * Introduced validator-api endpoint for estimating mixnode's reward * Stake saturation endpoint * kebab-cased coconut routes * Created separate crate for validator API models * Additional routes in validator API client * Introduced support for new queries in the wallet * Typescript type derivation * Fixed up date in license notice
This commit is contained in:
committed by
GitHub
parent
835d4f46f6
commit
e2e06df4e6
Generated
+10
@@ -3884,6 +3884,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"topology",
|
||||
"url",
|
||||
"validator-api-requests",
|
||||
"validator-client",
|
||||
"vergen",
|
||||
"version-checker",
|
||||
@@ -7318,6 +7319,14 @@ dependencies = [
|
||||
"getrandom 0.2.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "validator-api-requests"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"ts-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "validator-client"
|
||||
version = "0.1.0"
|
||||
@@ -7342,6 +7351,7 @@ dependencies = [
|
||||
"thiserror",
|
||||
"ts-rs",
|
||||
"url",
|
||||
"validator-api-requests",
|
||||
"vesting-contract",
|
||||
]
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ members = [
|
||||
"mixnode",
|
||||
"service-providers/network-requester",
|
||||
"validator-api",
|
||||
"validator-api/validator-api-requests",
|
||||
]
|
||||
|
||||
default-members = [
|
||||
|
||||
@@ -20,6 +20,7 @@ url = { version = "2.2", features = ["serde"] }
|
||||
|
||||
coconut-interface = { path = "../../coconut-interface" }
|
||||
network-defaults = { path = "../../network-defaults" }
|
||||
validator-api-requests = { path = "../../../validator-api/validator-api-requests" }
|
||||
|
||||
# required for nymd-client
|
||||
# at some point it might be possible to make it wasm-compatible
|
||||
@@ -37,3 +38,4 @@ ts-rs = {version = "5.1", optional = true}
|
||||
|
||||
[features]
|
||||
nymd-client = ["async-trait", "bip39", "config", "cosmrs", "prost", "flate2", "sha2", "itertools", "cosmwasm-std"]
|
||||
typescript-types = ["ts-rs", "validator-api-requests/ts-rs"]
|
||||
|
||||
@@ -14,11 +14,15 @@ use coconut_interface::{BlindSignRequestBody, BlindedSignatureResponse, Verifica
|
||||
use mixnet_contract::{
|
||||
Delegation, MixnetContractVersion, MixnodeRewardingStatusResponse, RewardingIntervalResponse,
|
||||
};
|
||||
use mixnet_contract::{GatewayBond, MixNodeBond};
|
||||
use mixnet_contract::{GatewayBond, IdentityKeyRef, MixNodeBond};
|
||||
|
||||
#[cfg(feature = "nymd-client")]
|
||||
use std::str::FromStr;
|
||||
use url::Url;
|
||||
use validator_api_requests::models::{
|
||||
CoreNodeStatusResponse, MixnodeStatusResponse, RewardEstimationResponse,
|
||||
StakeSaturationResponse,
|
||||
};
|
||||
|
||||
#[cfg(feature = "nymd-client")]
|
||||
pub struct Config {
|
||||
@@ -434,6 +438,55 @@ impl ApiClient {
|
||||
Ok(self.validator_api.get_gateways().await?)
|
||||
}
|
||||
|
||||
pub async fn get_gateway_core_status_count(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
since: Option<i64>,
|
||||
) -> Result<CoreNodeStatusResponse, ValidatorClientError> {
|
||||
Ok(self
|
||||
.validator_api
|
||||
.get_gateway_core_status_count(identity, since)
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_core_status_count(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
since: Option<i64>,
|
||||
) -> Result<CoreNodeStatusResponse, ValidatorClientError> {
|
||||
Ok(self
|
||||
.validator_api
|
||||
.get_mixnode_core_status_count(identity, since)
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_status(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
) -> Result<MixnodeStatusResponse, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_mixnode_status(identity).await?)
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_reward_estimation(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
) -> Result<RewardEstimationResponse, ValidatorClientError> {
|
||||
Ok(self
|
||||
.validator_api
|
||||
.get_mixnode_reward_estimation(identity)
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_stake_saturation(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
) -> Result<StakeSaturationResponse, ValidatorClientError> {
|
||||
Ok(self
|
||||
.validator_api
|
||||
.get_mixnode_stake_saturation(identity)
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub async fn blind_sign(
|
||||
&self,
|
||||
request_body: &BlindSignRequestBody,
|
||||
|
||||
@@ -9,6 +9,7 @@ pub mod validator_api;
|
||||
|
||||
pub use crate::client::ApiClient;
|
||||
pub use crate::error::ValidatorClientError;
|
||||
pub use validator_api_requests::*;
|
||||
|
||||
#[cfg(feature = "nymd-client")]
|
||||
pub use client::{Client, Config};
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::validator_api::error::ValidatorAPIError;
|
||||
use crate::validator_api::routes::{CORE_STATUS_COUNT, SINCE_ARG};
|
||||
use coconut_interface::{BlindSignRequestBody, BlindedSignatureResponse, VerificationKeyResponse};
|
||||
use mixnet_contract::{GatewayBond, MixNodeBond};
|
||||
use mixnet_contract::{GatewayBond, IdentityKeyRef, MixNodeBond};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
use validator_api_requests::models::{
|
||||
CoreNodeStatusResponse, MixnodeStatusResponse, RewardEstimationResponse,
|
||||
StakeSaturationResponse,
|
||||
};
|
||||
|
||||
pub mod error;
|
||||
pub(crate) mod routes;
|
||||
|
||||
type PathSegments<'a> = &'a [&'a str];
|
||||
type Params<'a, K, V> = &'a [(K, V)];
|
||||
|
||||
const NO_PARAMS: Params<'_, &'_ str, &'_ str> = &[];
|
||||
|
||||
pub struct Client {
|
||||
url: Url,
|
||||
@@ -30,24 +38,33 @@ impl Client {
|
||||
self.url = new_url
|
||||
}
|
||||
|
||||
async fn query_validator_api<T>(&self, path: PathSegments<'_>) -> Result<T, ValidatorAPIError>
|
||||
async fn query_validator_api<T, K, V>(
|
||||
&self,
|
||||
path: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
) -> Result<T, ValidatorAPIError>
|
||||
where
|
||||
for<'a> T: Deserialize<'a>,
|
||||
K: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
let url = create_api_url(&self.url, path);
|
||||
let url = create_api_url(&self.url, path, params);
|
||||
Ok(self.reqwest_client.get(url).send().await?.json().await?)
|
||||
}
|
||||
|
||||
async fn post_validator_api<B, T>(
|
||||
async fn post_validator_api<B, T, K, V>(
|
||||
&self,
|
||||
path: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
json_body: &B,
|
||||
) -> Result<T, ValidatorAPIError>
|
||||
where
|
||||
B: Serialize + ?Sized,
|
||||
for<'a> T: Deserialize<'a>,
|
||||
K: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
let url = create_api_url(&self.url, path);
|
||||
let url = create_api_url(&self.url, path, params);
|
||||
Ok(self
|
||||
.reqwest_client
|
||||
.post(url)
|
||||
@@ -59,23 +76,142 @@ impl Client {
|
||||
}
|
||||
|
||||
pub async fn get_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorAPIError> {
|
||||
self.query_validator_api(&[routes::API_VERSION, routes::MIXNODES])
|
||||
self.query_validator_api(&[routes::API_VERSION, routes::MIXNODES], NO_PARAMS)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorAPIError> {
|
||||
self.query_validator_api(&[routes::API_VERSION, routes::GATEWAYS])
|
||||
self.query_validator_api(&[routes::API_VERSION, routes::GATEWAYS], NO_PARAMS)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_active_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorAPIError> {
|
||||
self.query_validator_api(&[routes::API_VERSION, routes::MIXNODES, routes::ACTIVE])
|
||||
.await
|
||||
self.query_validator_api(
|
||||
&[routes::API_VERSION, routes::MIXNODES, routes::ACTIVE],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_rewarded_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorAPIError> {
|
||||
self.query_validator_api(&[routes::API_VERSION, routes::MIXNODES, routes::REWARDED])
|
||||
self.query_validator_api(
|
||||
&[routes::API_VERSION, routes::MIXNODES, routes::REWARDED],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_gateway_core_status_count(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
since: Option<i64>,
|
||||
) -> Result<CoreNodeStatusResponse, ValidatorAPIError> {
|
||||
if let Some(since) = since {
|
||||
self.query_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
routes::GATEWAY,
|
||||
identity,
|
||||
CORE_STATUS_COUNT,
|
||||
],
|
||||
&[(SINCE_ARG, since.to_string())],
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
self.query_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
routes::GATEWAY,
|
||||
identity,
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_core_status_count(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
since: Option<i64>,
|
||||
) -> Result<CoreNodeStatusResponse, ValidatorAPIError> {
|
||||
if let Some(since) = since {
|
||||
self.query_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
routes::MIXNODE,
|
||||
identity,
|
||||
CORE_STATUS_COUNT,
|
||||
],
|
||||
&[(SINCE_ARG, since.to_string())],
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
self.query_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
routes::MIXNODE,
|
||||
identity,
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_status(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
) -> Result<MixnodeStatusResponse, ValidatorAPIError> {
|
||||
self.query_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
routes::MIXNODE,
|
||||
identity,
|
||||
routes::STATUS,
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_reward_estimation(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
) -> Result<RewardEstimationResponse, ValidatorAPIError> {
|
||||
self.query_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
routes::MIXNODE,
|
||||
identity,
|
||||
routes::REWARD_ESTIMATION,
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_stake_saturation(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
) -> Result<StakeSaturationResponse, ValidatorAPIError> {
|
||||
self.query_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
routes::MIXNODE,
|
||||
identity,
|
||||
routes::STAKE_SATURATION,
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn blind_sign(
|
||||
@@ -84,6 +220,7 @@ impl Client {
|
||||
) -> Result<BlindedSignatureResponse, ValidatorAPIError> {
|
||||
self.post_validator_api(
|
||||
&[routes::API_VERSION, routes::COCONUT_BLIND_SIGN],
|
||||
NO_PARAMS,
|
||||
request_body,
|
||||
)
|
||||
.await
|
||||
@@ -92,13 +229,20 @@ impl Client {
|
||||
pub async fn get_coconut_verification_key(
|
||||
&self,
|
||||
) -> Result<VerificationKeyResponse, ValidatorAPIError> {
|
||||
self.query_validator_api(&[routes::API_VERSION, routes::COCONUT_VERIFICATION_KEY])
|
||||
.await
|
||||
self.query_validator_api(
|
||||
&[routes::API_VERSION, routes::COCONUT_VERIFICATION_KEY],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
// utility function that should solve the double slash problem in validator API forever.
|
||||
fn create_api_url(base: &Url, segments: PathSegments<'_>) -> Url {
|
||||
fn create_api_url<K: AsRef<str>, V: AsRef<str>>(
|
||||
base: &Url,
|
||||
segments: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
) -> Url {
|
||||
let mut url = base.clone();
|
||||
let mut path_segments = url
|
||||
.path_segments_mut()
|
||||
@@ -113,6 +257,10 @@ fn create_api_url(base: &Url, segments: PathSegments<'_>) -> Url {
|
||||
// and can be dropped
|
||||
drop(path_segments);
|
||||
|
||||
if !params.is_empty() {
|
||||
url.query_pairs_mut().extend_pairs(params);
|
||||
}
|
||||
|
||||
url
|
||||
}
|
||||
|
||||
@@ -127,51 +275,66 @@ mod tests {
|
||||
// works with 1 segment
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo",
|
||||
create_api_url(&base_url, &["foo"]).as_str()
|
||||
create_api_url(&base_url, &["foo"], NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// works with 2 segments
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar",
|
||||
create_api_url(&base_url, &["foo", "bar"]).as_str()
|
||||
create_api_url(&base_url, &["foo", "bar"], NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// works with leading slash
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo",
|
||||
create_api_url(&base_url, &["/foo"]).as_str()
|
||||
create_api_url(&base_url, &["/foo"], NO_PARAMS).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar",
|
||||
create_api_url(&base_url, &["/foo", "bar"]).as_str()
|
||||
create_api_url(&base_url, &["/foo", "bar"], NO_PARAMS).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar",
|
||||
create_api_url(&base_url, &["foo", "/bar"]).as_str()
|
||||
create_api_url(&base_url, &["foo", "/bar"], NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// works with trailing slash
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo",
|
||||
create_api_url(&base_url, &["foo/"]).as_str()
|
||||
create_api_url(&base_url, &["foo/"], NO_PARAMS).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar",
|
||||
create_api_url(&base_url, &["foo/", "bar"]).as_str()
|
||||
create_api_url(&base_url, &["foo/", "bar"], NO_PARAMS).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar",
|
||||
create_api_url(&base_url, &["foo", "bar/"]).as_str()
|
||||
create_api_url(&base_url, &["foo", "bar/"], NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// works with both leading and trailing slash
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo",
|
||||
create_api_url(&base_url, &["/foo/"]).as_str()
|
||||
create_api_url(&base_url, &["/foo/"], NO_PARAMS).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar",
|
||||
create_api_url(&base_url, &["/foo/", "/bar/"]).as_str()
|
||||
create_api_url(&base_url, &["/foo/", "/bar/"], NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// adds params
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar?foomp=baz",
|
||||
create_api_url(&base_url, &["foo", "bar"], &[("foomp", "baz")]).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar?arg1=val1&arg2=val2",
|
||||
create_api_url(
|
||||
&base_url,
|
||||
&["/foo/", "/bar/"],
|
||||
&[("arg1", "val1"), ("arg2", "val2")]
|
||||
)
|
||||
.as_str()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,5 +10,16 @@ pub const GATEWAYS: &str = "gateways";
|
||||
pub const ACTIVE: &str = "active";
|
||||
pub const REWARDED: &str = "rewarded";
|
||||
|
||||
pub const COCONUT_BLIND_SIGN: &str = "blind_sign";
|
||||
pub const COCONUT_VERIFICATION_KEY: &str = "verification_key";
|
||||
pub const COCONUT_BLIND_SIGN: &str = "blind-sign";
|
||||
pub const COCONUT_VERIFICATION_KEY: &str = "verification-key";
|
||||
|
||||
pub const STATUS_ROUTES: &str = "status";
|
||||
pub const MIXNODE: &str = "mixnode";
|
||||
pub const GATEWAY: &str = "gateway";
|
||||
|
||||
pub const CORE_STATUS_COUNT: &str = "core-status-count";
|
||||
pub const SINCE_ARG: &str = "since";
|
||||
|
||||
pub const STATUS: &str = "status";
|
||||
pub const REWARD_ESTIMATION: &str = "reward-estimation";
|
||||
pub const STAKE_SATURATION: &str = "stake-saturation";
|
||||
|
||||
Generated
+9
@@ -5261,6 +5261,14 @@ dependencies = [
|
||||
"getrandom 0.2.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "validator-api-requests"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"ts-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "validator-client"
|
||||
version = "0.1.0"
|
||||
@@ -5285,6 +5293,7 @@ dependencies = [
|
||||
"thiserror",
|
||||
"ts-rs",
|
||||
"url",
|
||||
"validator-api-requests",
|
||||
"vesting-contract",
|
||||
]
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ features = ["ts-rs"]
|
||||
|
||||
[dev-dependencies.validator-client]
|
||||
path = "../../common/client-libs/validator-client"
|
||||
features = ["ts-rs"]
|
||||
features = ["typescript-types"]
|
||||
|
||||
[features]
|
||||
default = ["custom-protocol"]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use serde::{Serialize, Serializer};
|
||||
use thiserror::Error;
|
||||
use validator_client::nymd::error::NymdError;
|
||||
use validator_client::validator_api::error::ValidatorAPIError;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum BackendError {
|
||||
@@ -29,6 +30,11 @@ pub enum BackendError {
|
||||
#[from]
|
||||
source: eyre::Report,
|
||||
},
|
||||
#[error("{source}")]
|
||||
ValidatorApiError {
|
||||
#[from]
|
||||
source: ValidatorAPIError,
|
||||
},
|
||||
#[error("Client has not been initialized yet, connect with mnemonic to initialize")]
|
||||
ClientNotInitialized,
|
||||
#[error("No balance available for address {0}")]
|
||||
|
||||
@@ -19,6 +19,7 @@ mod utils;
|
||||
|
||||
use crate::menu::AddDefaultSubmenus;
|
||||
use crate::operations::mixnet;
|
||||
use crate::operations::validator_api;
|
||||
use crate::operations::vesting;
|
||||
|
||||
use crate::state::State;
|
||||
@@ -63,6 +64,11 @@ fn main() {
|
||||
vesting::queries::original_vesting,
|
||||
vesting::queries::delegated_free,
|
||||
vesting::queries::delegated_vesting,
|
||||
validator_api::status::mixnode_core_node_status,
|
||||
validator_api::status::gateway_core_node_status,
|
||||
validator_api::status::mixnode_status,
|
||||
validator_api::status::mixnode_reward_estimation,
|
||||
validator_api::status::mixnode_stake_saturation,
|
||||
])
|
||||
.menu(Menu::new().add_default_app_submenu_if_macos())
|
||||
.run(tauri::generate_context!())
|
||||
@@ -82,6 +88,11 @@ mod test {
|
||||
crate::coin::Denom => "../src/types/rust/denom.ts",
|
||||
crate::utils::DelegationResult => "../src/types/rust/delegationresult.ts",
|
||||
crate::mixnet::account::Account => "../src/types/rust/account.ts",
|
||||
crate::mixnet::admin::TauriContractStateParams => "../src/types/rust/stateparams.ts"
|
||||
crate::mixnet::admin::TauriContractStateParams => "../src/types/rust/stateparams.ts",
|
||||
validator_client::models::CoreNodeStatusResponse => "../src/types/corenodestatusresponse.ts",
|
||||
validator_client::models::MixnodeStatus => "../src/types/rust/mixnodestatus.ts",
|
||||
validator_client::models::MixnodeStatusResponse => "../src/types/rust/mixnodestatusresponse.ts",
|
||||
validator_client::models::RewardEstimationResponse => "../src/types/rust/rewardestimationresponse.ts",
|
||||
validator_client::models::StakeSaturationResponse => "../src/types/rust/stakesaturaionresponse.ts",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
pub mod mixnet;
|
||||
pub mod validator_api;
|
||||
pub mod vesting;
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod status;
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::api_client;
|
||||
use crate::error::BackendError;
|
||||
use crate::state::State;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
use validator_client::models::{
|
||||
CoreNodeStatusResponse, MixnodeStatusResponse, RewardEstimationResponse, StakeSaturationResponse,
|
||||
};
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn mixnode_core_node_status(
|
||||
identity: &str,
|
||||
since: Option<i64>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<CoreNodeStatusResponse, BackendError> {
|
||||
Ok(
|
||||
api_client!(state)
|
||||
.get_mixnode_core_status_count(identity, since)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn gateway_core_node_status(
|
||||
identity: &str,
|
||||
since: Option<i64>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<CoreNodeStatusResponse, BackendError> {
|
||||
Ok(
|
||||
api_client!(state)
|
||||
.get_gateway_core_status_count(identity, since)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn mixnode_status(
|
||||
identity: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<MixnodeStatusResponse, BackendError> {
|
||||
Ok(api_client!(state).get_mixnode_status(identity).await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn mixnode_reward_estimation(
|
||||
identity: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<RewardEstimationResponse, BackendError> {
|
||||
Ok(
|
||||
api_client!(state)
|
||||
.get_mixnode_reward_estimation(identity)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn mixnode_stake_saturation(
|
||||
identity: &str,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<StakeSaturationResponse, BackendError> {
|
||||
Ok(
|
||||
api_client!(state)
|
||||
.get_mixnode_stake_saturation(identity)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface CoreNodeStatusResponse {
|
||||
identity: string;
|
||||
count: number;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export type MixnodeStatus = "Active" | "Standby" | "Inactive" | "NotFound";
|
||||
@@ -0,0 +1,5 @@
|
||||
import { MixnodeStatus } from "./mixnodestatus";
|
||||
|
||||
export interface MixnodeStatusResponse {
|
||||
status: MixnodeStatus;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
export interface RewardEstimationResponse {
|
||||
estimated_total_node_reward: bigint;
|
||||
estimated_operator_reward: bigint;
|
||||
estimated_delegators_reward: bigint;
|
||||
current_epoch_start: bigint;
|
||||
current_epoch_end: bigint;
|
||||
current_epoch_uptime: number;
|
||||
as_at: bigint;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface StakeSaturationResponse {
|
||||
saturation: number;
|
||||
as_at: bigint;
|
||||
}
|
||||
@@ -39,7 +39,7 @@ time = { version = "0.3", features = ["serde-human-readable", "parsing"]}
|
||||
anyhow = "1"
|
||||
getset = "0.1.1"
|
||||
|
||||
rocket_sync_db_pools = {version = "0.1.0-rc.1", default-features = false}
|
||||
rocket_sync_db_pools = { version = "0.1.0-rc.1", default-features = false }
|
||||
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]}
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ gateway-client = { path="../common/client-libs/gateway-client" }
|
||||
mixnet-contract = { path="../common/mixnet-contract" }
|
||||
nymsphinx = { path="../common/nymsphinx" }
|
||||
topology = { path="../common/topology" }
|
||||
validator-api-requests = { path = "validator-api-requests" }
|
||||
validator-client = { path="../common/client-libs/validator-client", features = ["nymd-client"] }
|
||||
version-checker = { path="../common/version-checker" }
|
||||
coconut-interface = { path = "../common/coconut-interface", optional = true }
|
||||
|
||||
Vendored
+1
-15
@@ -20,26 +20,12 @@ use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::sync::RwLock;
|
||||
use tokio::time;
|
||||
use validator_api_requests::models::MixnodeStatus;
|
||||
use validator_client::nymd::hash::SHA256_HASH_SIZE;
|
||||
use validator_client::nymd::CosmWasmClient;
|
||||
|
||||
pub(crate) mod routes;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum MixnodeStatus {
|
||||
Active, // in both the active set and the rewarded set
|
||||
Standby, // only in the rewarded set
|
||||
Inactive, // in neither the rewarded set nor the active set, but is bonded
|
||||
NotFound, // doesn't even exist in the bonded set
|
||||
}
|
||||
|
||||
impl MixnodeStatus {
|
||||
pub fn is_active(&self) -> bool {
|
||||
*self == MixnodeStatus::Active
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ValidatorCacheRefresher<C> {
|
||||
nymd_client: Client<C>,
|
||||
cache: ValidatorCache,
|
||||
|
||||
@@ -62,7 +62,7 @@ fn blind_sign(request: InternalSignRequest, key_pair: &KeyPair) -> BlindedSignat
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[post("/blind_sign", data = "<blind_sign_request_body>")]
|
||||
#[post("/blind-sign", data = "<blind_sign_request_body>")]
|
||||
// Until we have serialization and deserialization traits we'll be using a crutch
|
||||
pub async fn post_blind_sign(
|
||||
blind_sign_request_body: Json<BlindSignRequestBody>,
|
||||
@@ -79,7 +79,7 @@ pub async fn post_blind_sign(
|
||||
Json(BlindedSignatureResponse::new(blinded_signature))
|
||||
}
|
||||
|
||||
#[get("/verification_key")]
|
||||
#[get("/verification-key")]
|
||||
pub async fn get_verification_key(key_pair: &State<KeyPair>) -> Json<VerificationKeyResponse> {
|
||||
Json(VerificationKeyResponse::new(key_pair.verification_key()))
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::cache::MixnodeStatus;
|
||||
use crate::node_status_api::utils::NodeUptimes;
|
||||
use crate::storage::models::NodeStatus;
|
||||
use rocket::http::{ContentType, Status};
|
||||
@@ -272,32 +271,3 @@ impl Display for ValidatorApiStorageError {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct CoreNodeStatus {
|
||||
pub(crate) identity: String,
|
||||
pub(crate) count: i32,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub(crate) struct MixnodeStatusResponse {
|
||||
pub(crate) status: MixnodeStatus,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub(crate) struct RewardEstimationResponse {
|
||||
pub(crate) estimated_total_node_reward: u128,
|
||||
pub(crate) estimated_operator_reward: u128,
|
||||
pub(crate) estimated_delegators_reward: u128,
|
||||
|
||||
pub(crate) current_epoch_start: i64,
|
||||
pub(crate) current_epoch_end: i64,
|
||||
pub(crate) current_epoch_uptime: Uptime,
|
||||
pub(crate) as_at: i64,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub(crate) struct StakeSaturationResponse {
|
||||
pub(crate) saturation: f32,
|
||||
pub(crate) as_at: i64,
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::node_status_api::models::{
|
||||
CoreNodeStatus, ErrorResponse, GatewayStatusReport, GatewayUptimeHistory, MixnodeStatusReport,
|
||||
MixnodeStatusResponse, MixnodeUptimeHistory, RewardEstimationResponse, StakeSaturationResponse,
|
||||
ErrorResponse, GatewayStatusReport, GatewayUptimeHistory, MixnodeStatusReport,
|
||||
MixnodeUptimeHistory,
|
||||
};
|
||||
use crate::storage::ValidatorApiStorage;
|
||||
use crate::{Epoch, ValidatorCache};
|
||||
@@ -11,85 +11,89 @@ use rocket::http::Status;
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::State;
|
||||
use time::OffsetDateTime;
|
||||
use validator_api_requests::models::{
|
||||
CoreNodeStatusResponse, MixnodeStatusResponse, RewardEstimationResponse,
|
||||
StakeSaturationResponse,
|
||||
};
|
||||
|
||||
#[get("/mixnode/<pubkey>/report")]
|
||||
#[get("/mixnode/<identity>/report")]
|
||||
pub(crate) async fn mixnode_report(
|
||||
storage: &State<ValidatorApiStorage>,
|
||||
pubkey: &str,
|
||||
identity: &str,
|
||||
) -> Result<Json<MixnodeStatusReport>, ErrorResponse> {
|
||||
storage
|
||||
.construct_mixnode_report(pubkey)
|
||||
.construct_mixnode_report(identity)
|
||||
.await
|
||||
.map(Json)
|
||||
.map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound))
|
||||
}
|
||||
|
||||
#[get("/gateway/<pubkey>/report")]
|
||||
#[get("/gateway/<identity>/report")]
|
||||
pub(crate) async fn gateway_report(
|
||||
storage: &State<ValidatorApiStorage>,
|
||||
pubkey: &str,
|
||||
identity: &str,
|
||||
) -> Result<Json<GatewayStatusReport>, ErrorResponse> {
|
||||
storage
|
||||
.construct_gateway_report(pubkey)
|
||||
.construct_gateway_report(identity)
|
||||
.await
|
||||
.map(Json)
|
||||
.map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound))
|
||||
}
|
||||
|
||||
#[get("/mixnode/<pubkey>/history")]
|
||||
#[get("/mixnode/<identity>/history")]
|
||||
pub(crate) async fn mixnode_uptime_history(
|
||||
storage: &State<ValidatorApiStorage>,
|
||||
pubkey: &str,
|
||||
identity: &str,
|
||||
) -> Result<Json<MixnodeUptimeHistory>, ErrorResponse> {
|
||||
storage
|
||||
.get_mixnode_uptime_history(pubkey)
|
||||
.get_mixnode_uptime_history(identity)
|
||||
.await
|
||||
.map(Json)
|
||||
.map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound))
|
||||
}
|
||||
|
||||
#[get("/gateway/<pubkey>/history")]
|
||||
#[get("/gateway/<identity>/history")]
|
||||
pub(crate) async fn gateway_uptime_history(
|
||||
storage: &State<ValidatorApiStorage>,
|
||||
pubkey: &str,
|
||||
identity: &str,
|
||||
) -> Result<Json<GatewayUptimeHistory>, ErrorResponse> {
|
||||
storage
|
||||
.get_gateway_uptime_history(pubkey)
|
||||
.get_gateway_uptime_history(identity)
|
||||
.await
|
||||
.map(Json)
|
||||
.map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound))
|
||||
}
|
||||
|
||||
#[get("/mixnode/<pubkey>/core-status-count?<since>")]
|
||||
#[get("/mixnode/<identity>/core-status-count?<since>")]
|
||||
pub(crate) async fn mixnode_core_status_count(
|
||||
storage: &State<ValidatorApiStorage>,
|
||||
pubkey: &str,
|
||||
identity: &str,
|
||||
since: Option<i64>,
|
||||
) -> Json<CoreNodeStatus> {
|
||||
) -> Json<CoreNodeStatusResponse> {
|
||||
let count = storage
|
||||
.get_core_mixnode_status_count(pubkey, since)
|
||||
.get_core_mixnode_status_count(identity, since)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
|
||||
Json(CoreNodeStatus {
|
||||
identity: pubkey.to_string(),
|
||||
Json(CoreNodeStatusResponse {
|
||||
identity: identity.to_string(),
|
||||
count,
|
||||
})
|
||||
}
|
||||
|
||||
#[get("/gateway/<pubkey>/core-status-count?<since>")]
|
||||
#[get("/gateway/<identity>/core-status-count?<since>")]
|
||||
pub(crate) async fn gateway_core_status_count(
|
||||
storage: &State<ValidatorApiStorage>,
|
||||
pubkey: &str,
|
||||
identity: &str,
|
||||
since: Option<i64>,
|
||||
) -> Json<CoreNodeStatus> {
|
||||
) -> Json<CoreNodeStatusResponse> {
|
||||
let count = storage
|
||||
.get_core_gateway_status_count(pubkey, since)
|
||||
.get_core_gateway_status_count(identity, since)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
|
||||
Json(CoreNodeStatus {
|
||||
identity: pubkey.to_string(),
|
||||
Json(CoreNodeStatusResponse {
|
||||
identity: identity.to_string(),
|
||||
count,
|
||||
})
|
||||
}
|
||||
@@ -104,7 +108,7 @@ pub(crate) async fn get_mixnode_status(
|
||||
})
|
||||
}
|
||||
|
||||
#[get("/mixnode/<identity>/reward_estimation")]
|
||||
#[get("/mixnode/<identity>/reward-estimation")]
|
||||
pub(crate) async fn get_mixnode_reward_estimation(
|
||||
cache: &State<ValidatorCache>,
|
||||
storage: &State<ValidatorApiStorage>,
|
||||
@@ -136,7 +140,7 @@ pub(crate) async fn get_mixnode_reward_estimation(
|
||||
estimated_delegators_reward,
|
||||
current_epoch_start: current_epoch.start_unix_timestamp(),
|
||||
current_epoch_end: current_epoch.end_unix_timestamp(),
|
||||
current_epoch_uptime: uptime,
|
||||
current_epoch_uptime: uptime.u8(),
|
||||
as_at,
|
||||
}))
|
||||
} else {
|
||||
@@ -147,7 +151,7 @@ pub(crate) async fn get_mixnode_reward_estimation(
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/mixnode/<identity>/stake_saturation")]
|
||||
#[get("/mixnode/<identity>/stake-saturation")]
|
||||
pub(crate) async fn get_mixnode_stake_saturation(
|
||||
cache: &State<ValidatorCache>,
|
||||
identity: String,
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "validator-api-requests"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
serde = "1.0"
|
||||
ts-rs = { version = "5.1", optional = true }
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod models;
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum MixnodeStatus {
|
||||
Active, // in both the active set and the rewarded set
|
||||
Standby, // only in the rewarded set
|
||||
Inactive, // in neither the rewarded set nor the active set, but is bonded
|
||||
NotFound, // doesn't even exist in the bonded set
|
||||
}
|
||||
|
||||
impl MixnodeStatus {
|
||||
pub fn is_active(&self) -> bool {
|
||||
*self == MixnodeStatus::Active
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
|
||||
pub struct CoreNodeStatusResponse {
|
||||
pub identity: String,
|
||||
pub count: i32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
|
||||
pub struct MixnodeStatusResponse {
|
||||
pub status: MixnodeStatus,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
|
||||
pub struct RewardEstimationResponse {
|
||||
pub estimated_total_node_reward: u128,
|
||||
pub estimated_operator_reward: u128,
|
||||
pub estimated_delegators_reward: u128,
|
||||
|
||||
pub current_epoch_start: i64,
|
||||
pub current_epoch_end: i64,
|
||||
pub current_epoch_uptime: u8,
|
||||
pub as_at: i64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
|
||||
pub struct StakeSaturationResponse {
|
||||
pub saturation: f32,
|
||||
pub as_at: i64,
|
||||
}
|
||||
Reference in New Issue
Block a user