Compare commits
9 Commits
goblin
...
feature/nova
| Author | SHA1 | Date | |
|---|---|---|---|
| ae0883241e | |||
| 11baa99c4b | |||
| 8bc23434ab | |||
| 8f6daf1e03 | |||
| 7f63377d22 | |||
| 46cb3eca38 | |||
| 7b22872c6b | |||
| 3342cb13c7 | |||
| 3cd5fc3b22 |
Generated
+9
-6
@@ -3972,10 +3972,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "okapi"
|
||||
version = "0.6.0-alpha-1"
|
||||
version = "0.7.0-rc.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb085e00daf8d75b9dbf0ffdb4738e69503e28898d9641fa8bdc6ad536c7bcf4"
|
||||
checksum = "ce66b6366e049880a35c378123fddb630b1a1a3c37fa1ca70caaf4a09f6e2893"
|
||||
dependencies = [
|
||||
"log",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -5212,10 +5213,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rocket_okapi"
|
||||
version = "0.7.0-alpha-1"
|
||||
version = "0.8.0-rc.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b2f4f48fb070f9f6c56d5663df5fa8a514406207744f4abd84661bfb24efd7d"
|
||||
checksum = "0025aa04994af8cd8e1fcdd5a73579a395c941ae090ecb0a39b41cca7e237a20"
|
||||
dependencies = [
|
||||
"either",
|
||||
"log",
|
||||
"okapi",
|
||||
"rocket",
|
||||
"rocket_okapi_codegen",
|
||||
@@ -5226,9 +5229,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rocket_okapi_codegen"
|
||||
version = "0.7.0-alpha-1"
|
||||
version = "0.8.0-rc.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88ccf1550e1c806461a6b08e2ab64eb10701d41bf50bde59ab9aa3a57ab14d41"
|
||||
checksum = "dc114779fc27afb78179233e966f469e47fd7a98dc15181cff2574cdddb65612"
|
||||
dependencies = [
|
||||
"darling 0.13.0",
|
||||
"proc-macro2",
|
||||
|
||||
@@ -207,9 +207,9 @@ mod tests {
|
||||
fn generating_account_addresses() {
|
||||
// test vectors produced from our js wallet
|
||||
let mnemonic_address = vec![
|
||||
("crush minute paddle tobacco message debate cabin peace bar jacket execute twenty winner view sure mask popular couch penalty fragile demise fresh pizza stove", "punk1jw6mp7d5xqc7w6xm79lha27glmd0vdt32a3fj2"),
|
||||
("acquire rebel spot skin gun such erupt pull swear must define ill chief turtle today flower chunk truth battle claw rigid detail gym feel", "punk1h5hgn94nsq4kh99rjj794hr5h5q6yfm22mcqqn"),
|
||||
("step income throw wheat mobile ship wave drink pool sudden upset jaguar bar globe rifle spice frost bless glimpse size regular carry aspect ball", "punk17n9flp6jflljg6fp05dsy07wcprf2uuujse962")
|
||||
("crush minute paddle tobacco message debate cabin peace bar jacket execute twenty winner view sure mask popular couch penalty fragile demise fresh pizza stove", "nymt1jw6mp7d5xqc7w6xm79lha27glmd0vdt339me94"),
|
||||
("acquire rebel spot skin gun such erupt pull swear must define ill chief turtle today flower chunk truth battle claw rigid detail gym feel", "nymt1h5hgn94nsq4kh99rjj794hr5h5q6yfm23rjshv"),
|
||||
("step income throw wheat mobile ship wave drink pool sudden upset jaguar bar globe rifle spice frost bless glimpse size regular carry aspect ball", "nymt17n9flp6jflljg6fp05dsy07wcprf2uuufgn4d4")
|
||||
];
|
||||
|
||||
for (mnemonic, address) in mnemonic_address.into_iter() {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
fn main() {
|
||||
match option_env!("NETWORK") {
|
||||
None | Some("milhon") => println!("cargo:rustc-cfg=network=\"milhon\"",),
|
||||
Some("sandbox") => println!("cargo:rustc-cfg=network=\"sandbox\"",),
|
||||
Some("qa") => println!("cargo:rustc-cfg=network=\"qa\""),
|
||||
_ => panic!("No such network"),
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,15 @@ use time::OffsetDateTime;
|
||||
use url::Url;
|
||||
|
||||
pub mod eth_contract;
|
||||
#[cfg(network = "milhon")]
|
||||
pub mod milhon;
|
||||
#[cfg(network = "sandbox")]
|
||||
pub mod sandbox;
|
||||
|
||||
#[cfg(network = "milhon")]
|
||||
pub use milhon::*;
|
||||
#[cfg(network = "sandbox")]
|
||||
pub use sandbox::*;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct ValidatorDetails {
|
||||
@@ -38,6 +47,7 @@ impl ValidatorDetails {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(network = "milhon")]
|
||||
pub fn default_validators() -> Vec<ValidatorDetails> {
|
||||
vec![
|
||||
ValidatorDetails::new(
|
||||
@@ -48,6 +58,14 @@ pub fn default_validators() -> Vec<ValidatorDetails> {
|
||||
]
|
||||
}
|
||||
|
||||
#[cfg(network = "sandbox")]
|
||||
pub fn default_validators() -> Vec<ValidatorDetails> {
|
||||
vec![ValidatorDetails::new(
|
||||
"https://sandbox-validator.nymtech.net",
|
||||
Some("https://sandbox-validator.nymtech.net/api"),
|
||||
)]
|
||||
}
|
||||
|
||||
pub fn default_nymd_endpoints() -> Vec<Url> {
|
||||
default_validators()
|
||||
.iter()
|
||||
@@ -62,10 +80,7 @@ pub fn default_api_endpoints() -> Vec<Url> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = "punk10pyejy66429refv3g35g2t7am0was7yalwrzen";
|
||||
pub const DEFAULT_VESTING_CONTRACT_ADDRESS: &str = "";
|
||||
pub const REWARDING_VALIDATOR_ADDRESS: &str = "punk1v9qauwdq5terag6uvfsdytcs2d0sdmfdy7hgk3";
|
||||
|
||||
// Ethereum constants used for token bridge
|
||||
/// How much bandwidth (in bytes) one token can buy
|
||||
const BYTES_PER_TOKEN: u64 = 1024 * 1024 * 1024;
|
||||
/// How many ERC20 tokens should be burned to buy bandwidth
|
||||
@@ -73,20 +88,10 @@ pub const TOKENS_TO_BURN: u64 = 10;
|
||||
/// Default bandwidth (in bytes) that we try to buy
|
||||
pub const BANDWIDTH_VALUE: u64 = TOKENS_TO_BURN * BYTES_PER_TOKEN;
|
||||
|
||||
// Ethereum constants used for token bridge
|
||||
pub const ETH_CONTRACT_ADDRESS: [u8; 20] =
|
||||
hex_literal::hex!("9fEE3e28c17dbB87310A51F13C4fbf4331A6f102");
|
||||
pub const ETH_MIN_BLOCK_DEPTH: usize = 7;
|
||||
pub const COSMOS_CONTRACT_ADDRESS: &str = "punk1jld76tqw4wnpfenmay2xkv86nr3j0w426eka82";
|
||||
// Name of the event triggered by the eth contract. If the event name is changed,
|
||||
// this would also need to be changed; It is currently tested against the json abi
|
||||
pub const ETH_EVENT_NAME: &str = "Burned";
|
||||
pub const ETH_BURN_FUNCTION_NAME: &str = "burnTokenForAccessCode";
|
||||
|
||||
/// Defaults Cosmos Hub/ATOM path
|
||||
pub const COSMOS_DERIVATION_PATH: &str = "m/44'/118'/0'/0/0";
|
||||
pub const BECH32_PREFIX: &str = "punk";
|
||||
pub const DENOM: &str = "upunk";
|
||||
// as set by validators in their configs
|
||||
// (note that the 'amount' postfix is relevant here as the full gas price also includes denom)
|
||||
pub const GAS_PRICE_AMOUNT: f64 = 0.025;
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub const BECH32_PREFIX: &str = "punk";
|
||||
pub const DENOM: &str = "upunk";
|
||||
|
||||
pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = "punk10pyejy66429refv3g35g2t7am0was7yalwrzen";
|
||||
pub const DEFAULT_VESTING_CONTRACT_ADDRESS: &str = "";
|
||||
pub const COSMOS_CONTRACT_ADDRESS: &str = "punk1jld76tqw4wnpfenmay2xkv86nr3j0w426eka82";
|
||||
pub const REWARDING_VALIDATOR_ADDRESS: &str = "punk1v9qauwdq5terag6uvfsdytcs2d0sdmfdy7hgk3";
|
||||
pub const ETH_CONTRACT_ADDRESS: [u8; 20] =
|
||||
hex_literal::hex!("9fEE3e28c17dbB87310A51F13C4fbf4331A6f102");
|
||||
|
||||
// Name of the event triggered by the eth contract. If the event name is changed,
|
||||
// this would also need to be changed; It is currently tested against the json abi
|
||||
pub const ETH_EVENT_NAME: &str = "Burned";
|
||||
pub const ETH_BURN_FUNCTION_NAME: &str = "burnTokenForAccessCode";
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub const BECH32_PREFIX: &str = "nymt";
|
||||
pub const DENOM: &str = "unymt";
|
||||
|
||||
pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = "nymt14hj2tavq8fpesdwxxcu44rty3hh90vhuysqrsr";
|
||||
pub const DEFAULT_VESTING_CONTRACT_ADDRESS: &str = "nymt1nc5tatafv6eyq7llkr2gv50ff9e22mnfp9pc5s";
|
||||
pub const COSMOS_CONTRACT_ADDRESS: &str = "nymt17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9f8xzkv";
|
||||
pub const REWARDING_VALIDATOR_ADDRESS: &str = "nymt17zujduc46wvkwvp6f062mm5xhr7jc3fewvqu9e";
|
||||
pub const ETH_CONTRACT_ADDRESS: [u8; 20] =
|
||||
hex_literal::hex!("9fEE3e28c17dbB87310A51F13C4fbf4331A6f102");
|
||||
|
||||
// Name of the event triggered by the eth contract. If the event name is changed,
|
||||
// this would also need to be changed; It is currently tested against the json abi
|
||||
pub const ETH_EVENT_NAME: &str = "Burned";
|
||||
pub const ETH_BURN_FUNCTION_NAME: &str = "burnTokenForAccessCode";
|
||||
@@ -56,14 +56,7 @@ pub(crate) fn try_delegate_to_mixnode_on_behalf(
|
||||
// check if the delegation contains any funds of the appropriate denomination
|
||||
let amount = validate_delegation_stake(info.funds)?;
|
||||
|
||||
_try_delegate_to_mixnode(
|
||||
deps,
|
||||
env,
|
||||
mix_identity,
|
||||
&delegate,
|
||||
amount,
|
||||
Some(info.sender),
|
||||
)
|
||||
_try_delegate_to_mixnode(deps, env, mix_identity, &delegate, amount, None)
|
||||
}
|
||||
|
||||
pub(crate) fn _try_delegate_to_mixnode(
|
||||
|
||||
@@ -52,16 +52,8 @@ pub fn try_add_gateway_on_behalf(
|
||||
.minimum_mixnode_pledge;
|
||||
let pledge = validate_gateway_pledge(info.funds, minimum_pledge)?;
|
||||
|
||||
let proxy = info.sender;
|
||||
_try_add_gateway(
|
||||
deps,
|
||||
env,
|
||||
gateway,
|
||||
pledge,
|
||||
&owner,
|
||||
owner_signature,
|
||||
Some(proxy),
|
||||
)
|
||||
let _proxy = info.sender;
|
||||
_try_add_gateway(deps, env, gateway, pledge, &owner, owner_signature, None)
|
||||
}
|
||||
|
||||
pub(crate) fn _try_add_gateway(
|
||||
|
||||
@@ -54,16 +54,8 @@ pub fn try_add_mixnode_on_behalf(
|
||||
.minimum_mixnode_pledge;
|
||||
let pledge = validate_mixnode_pledge(info.funds, minimum_pledge)?;
|
||||
|
||||
let proxy = info.sender;
|
||||
_try_add_mixnode(
|
||||
deps,
|
||||
env,
|
||||
mix_node,
|
||||
pledge,
|
||||
&owner,
|
||||
owner_signature,
|
||||
Some(proxy),
|
||||
)
|
||||
let _proxy = info.sender;
|
||||
_try_add_mixnode(deps, env, mix_node, pledge, &owner, owner_signature, None)
|
||||
}
|
||||
|
||||
fn _try_add_mixnode(
|
||||
|
||||
@@ -47,12 +47,15 @@ pub(crate) fn ensure_no_existing_bond(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
#[allow(unused_variables)]
|
||||
pub(crate) fn validate_node_identity_signature(
|
||||
deps: Deps,
|
||||
owner: &Addr,
|
||||
signature: String,
|
||||
identity: IdentityKeyRef,
|
||||
) -> Result<(), ContractError> {
|
||||
return Ok(());
|
||||
let owner_bytes = owner.as_bytes();
|
||||
|
||||
let mut identity_bytes = [0u8; 32];
|
||||
@@ -96,6 +99,7 @@ mod tests {
|
||||
use rand_chacha::rand_core::SeedableRng;
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn validating_node_signature() {
|
||||
let deps = mock_dependencies();
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ serde_json = "1.0.66"
|
||||
tokio = {version = "1.9.0", features = ["full"] }
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
schemars = { version = "0.8", features = ["preserve_order"] }
|
||||
okapi = { version = "0.6.0-alpha-1", features = ["derive_json_schema"] }
|
||||
rocket_okapi = "0.7.0-alpha-1"
|
||||
okapi = { version = "0.7.0-rc.1", features = ["impl_json_schema"] }
|
||||
rocket_okapi = { version = "0.8.0-rc.1", features = ["swagger"] }
|
||||
log = "0.4.0"
|
||||
pretty_env_logger = "0.4.0"
|
||||
thiserror = "1.0.29"
|
||||
|
||||
@@ -15,6 +15,13 @@ impl GeoLocateTask {
|
||||
}
|
||||
|
||||
pub(crate) fn start(mut self) {
|
||||
if ::std::env::var("GEO_IP_SERVICE_API_KEY").is_err() {
|
||||
error!(
|
||||
"Env var GEO_IP_SERVICE_API_KEY is not set. Geolocation tasks will not be started."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
info!("Spawning mix node locator task runner...");
|
||||
tokio::spawn(async move {
|
||||
let mut interval_timer = tokio::time::interval(std::time::Duration::from_millis(50));
|
||||
|
||||
@@ -2,9 +2,12 @@ use crate::country_statistics::country_nodes_distribution::CountryNodesDistribut
|
||||
use crate::state::ExplorerApiStateContext;
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::{Route, State};
|
||||
use rocket_okapi::okapi::openapi3::OpenApi;
|
||||
use rocket_okapi::openapi_get_routes_spec;
|
||||
use rocket_okapi::settings::OpenApiSettings;
|
||||
|
||||
pub fn country_statistics_make_default_routes() -> Vec<Route> {
|
||||
routes_with_openapi![index]
|
||||
pub fn country_statistics_make_default_routes(settings: &OpenApiSettings) -> (Vec<Route>, OpenApi) {
|
||||
openapi_get_routes_spec![settings: index]
|
||||
}
|
||||
|
||||
// We could either separate stuff by structure (like this, http is separate), or we could just
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use log::info;
|
||||
use rocket::http::Method;
|
||||
use rocket::Request;
|
||||
use rocket::{Build, Request, Rocket};
|
||||
use rocket_cors::{AllowedHeaders, AllowedOrigins};
|
||||
use rocket_okapi::swagger_ui::make_swagger_ui;
|
||||
|
||||
use crate::country_statistics::http::country_statistics_make_default_routes;
|
||||
use crate::http::swagger::get_docs;
|
||||
use crate::mix_node::http::mix_node_make_default_routes;
|
||||
use crate::mix_nodes::http::mix_nodes_make_default_routes;
|
||||
use crate::ping::http::ping_make_default_routes;
|
||||
use crate::state::ExplorerApiStateContext;
|
||||
|
||||
@@ -15,35 +16,45 @@ mod swagger;
|
||||
pub(crate) fn start(state: ExplorerApiStateContext) {
|
||||
tokio::spawn(async move {
|
||||
info!("Starting up...");
|
||||
|
||||
let allowed_origins = AllowedOrigins::all();
|
||||
|
||||
// You can also deserialize this
|
||||
let cors = rocket_cors::CorsOptions {
|
||||
allowed_origins,
|
||||
allowed_methods: vec![Method::Get].into_iter().map(From::from).collect(),
|
||||
allowed_headers: AllowedHeaders::some(&["*"]),
|
||||
allow_credentials: true,
|
||||
..Default::default()
|
||||
}
|
||||
.to_cors()
|
||||
.unwrap();
|
||||
|
||||
let config = rocket::config::Config::release_default();
|
||||
rocket::build()
|
||||
.configure(config)
|
||||
.mount("/countries", country_statistics_make_default_routes())
|
||||
.mount("/ping", ping_make_default_routes())
|
||||
.mount("/mix-node", mix_node_make_default_routes())
|
||||
.mount("/swagger", make_swagger_ui(&get_docs()))
|
||||
.register("/", catchers![not_found])
|
||||
.manage(state)
|
||||
.attach(cors)
|
||||
.launch()
|
||||
.await
|
||||
configure_rocket(state).launch().await
|
||||
});
|
||||
}
|
||||
|
||||
fn configure_rocket(state: ExplorerApiStateContext) -> Rocket<Build> {
|
||||
let allowed_origins = AllowedOrigins::all();
|
||||
|
||||
// You can also deserialize this
|
||||
let cors = rocket_cors::CorsOptions {
|
||||
allowed_origins,
|
||||
allowed_methods: vec![Method::Get].into_iter().map(From::from).collect(),
|
||||
allowed_headers: AllowedHeaders::some(&["*"]),
|
||||
allow_credentials: true,
|
||||
..Default::default()
|
||||
}
|
||||
.to_cors()
|
||||
.unwrap();
|
||||
|
||||
let openapi_settings = rocket_okapi::settings::OpenApiSettings::default();
|
||||
let config = rocket::config::Config::release_default();
|
||||
let mut building_rocket = rocket::build().configure(config);
|
||||
|
||||
mount_endpoints_and_merged_docs! {
|
||||
building_rocket,
|
||||
"/v1".to_owned(),
|
||||
openapi_settings,
|
||||
"/ping" => ping_make_default_routes(&openapi_settings),
|
||||
"/countries" => country_statistics_make_default_routes(&openapi_settings),
|
||||
"/mix-node" => mix_node_make_default_routes(&openapi_settings),
|
||||
"/mix-nodes" => mix_nodes_make_default_routes(&openapi_settings),
|
||||
};
|
||||
|
||||
building_rocket
|
||||
.mount("/swagger", make_swagger_ui(&get_docs()))
|
||||
.register("/", catchers![not_found])
|
||||
.manage(state)
|
||||
.attach(cors)
|
||||
}
|
||||
|
||||
#[catch(404)]
|
||||
pub(crate) fn not_found(req: &Request) -> String {
|
||||
format!("I couldn't find '{}'. Try something else?", req.uri())
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
use rocket_okapi::swagger_ui::{SwaggerUIConfig, UrlObject};
|
||||
use rocket_okapi::swagger_ui::SwaggerUIConfig;
|
||||
|
||||
pub(crate) fn get_docs() -> SwaggerUIConfig {
|
||||
SwaggerUIConfig {
|
||||
urls: vec![
|
||||
UrlObject::new("Country statistics", "/countries/openapi.json"),
|
||||
UrlObject::new("Node ping", "/ping/openapi.json"),
|
||||
UrlObject::new("Mix node", "/mix-node/openapi.json"),
|
||||
],
|
||||
url: "../v1/openapi.json".to_owned(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,11 @@ impl ExplorerApi {
|
||||
async fn run(&mut self) {
|
||||
info!("Explorer API starting up...");
|
||||
|
||||
info!(
|
||||
"Using validator API - {}",
|
||||
network_defaults::default_api_endpoints()[0].clone()
|
||||
);
|
||||
|
||||
// spawn concurrent tasks
|
||||
mix_nodes::tasks::MixNodesTasks::new(self.state.clone()).start();
|
||||
country_statistics::distribution::CountryStatisticsDistributionTask::new(
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
use reqwest::Error as ReqwestError;
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::{Route, State};
|
||||
use rocket_okapi::okapi::openapi3::OpenApi;
|
||||
use rocket_okapi::openapi_get_routes_spec;
|
||||
use rocket_okapi::settings::OpenApiSettings;
|
||||
use serde::Serialize;
|
||||
|
||||
use mixnet_contract::{Addr, Coin, Delegation, Layer, MixNode};
|
||||
@@ -9,13 +12,12 @@ use crate::mix_node::models::{NodeDescription, NodeStats};
|
||||
use crate::mix_nodes::{get_mixnode_delegations, get_single_mixnode_delegations, Location};
|
||||
use crate::state::ExplorerApiStateContext;
|
||||
|
||||
pub fn mix_node_make_default_routes() -> Vec<Route> {
|
||||
routes_with_openapi![
|
||||
get_delegations,
|
||||
pub fn mix_node_make_default_routes(settings: &OpenApiSettings) -> (Vec<Route>, OpenApi) {
|
||||
openapi_get_routes_spec![
|
||||
settings: get_delegations,
|
||||
get_all_delegations,
|
||||
get_description,
|
||||
get_stats,
|
||||
list
|
||||
]
|
||||
}
|
||||
|
||||
@@ -29,14 +31,6 @@ pub(crate) struct PrettyMixNodeBondWithLocation {
|
||||
pub mix_node: MixNode,
|
||||
}
|
||||
|
||||
#[openapi(tag = "mix_node")]
|
||||
#[get("/")]
|
||||
pub(crate) async fn list(
|
||||
state: &State<ExplorerApiStateContext>,
|
||||
) -> Json<Vec<PrettyMixNodeBondWithLocation>> {
|
||||
Json(state.inner.mix_nodes.get_mixnodes_with_location().await)
|
||||
}
|
||||
|
||||
#[openapi(tag = "mix_node")]
|
||||
#[get("/<pubkey>/delegations")]
|
||||
pub(crate) async fn get_delegations(pubkey: &str) -> Json<Vec<Delegation>> {
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::{Route, State};
|
||||
use rocket_okapi::okapi::openapi3::OpenApi;
|
||||
use rocket_okapi::openapi_get_routes_spec;
|
||||
use rocket_okapi::settings::OpenApiSettings;
|
||||
|
||||
use crate::mix_node::http::PrettyMixNodeBondWithLocation;
|
||||
use crate::state::ExplorerApiStateContext;
|
||||
|
||||
pub fn mix_nodes_make_default_routes(settings: &OpenApiSettings) -> (Vec<Route>, OpenApi) {
|
||||
openapi_get_routes_spec![settings: list]
|
||||
}
|
||||
|
||||
#[openapi(tag = "mix_nodes")]
|
||||
#[get("/")]
|
||||
pub(crate) async fn list(
|
||||
state: &State<ExplorerApiStateContext>,
|
||||
) -> Json<Vec<PrettyMixNodeBondWithLocation>> {
|
||||
Json(state.inner.mix_nodes.get_mixnodes_with_location().await)
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
pub(crate) mod http;
|
||||
pub(crate) mod tasks;
|
||||
mod utils;
|
||||
|
||||
|
||||
@@ -4,6 +4,9 @@ use std::time::Duration;
|
||||
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::{Route, State};
|
||||
use rocket_okapi::okapi::openapi3::OpenApi;
|
||||
use rocket_okapi::openapi_get_routes_spec;
|
||||
use rocket_okapi::settings::OpenApiSettings;
|
||||
|
||||
use mixnet_contract::MixNodeBond;
|
||||
|
||||
@@ -12,8 +15,8 @@ use crate::state::ExplorerApiStateContext;
|
||||
|
||||
const CONNECTION_TIMEOUT_SECONDS: Duration = Duration::from_secs(10);
|
||||
|
||||
pub fn ping_make_default_routes() -> Vec<Route> {
|
||||
routes_with_openapi![index]
|
||||
pub fn ping_make_default_routes(settings: &OpenApiSettings) -> (Vec<Route>, OpenApi) {
|
||||
openapi_get_routes_spec![settings: index]
|
||||
}
|
||||
|
||||
#[openapi(tag = "ping")]
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
EXPLORER_API_URL=https://testnet-milhon-explorer.nymtech.net
|
||||
VALIDATOR_API_URL=https://testnet-milhon-validator1.nymtech.net
|
||||
BIG_DIPPER_URL=https://testnet-milhon-blocks.nymtech.net
|
||||
+5
-3
@@ -1,3 +1,5 @@
|
||||
EXPLORER_API_URL=https://testnet-milhon-explorer.nymtech.net
|
||||
VALIDATOR_API_URL=https://testnet-milhon-validator1.nymtech.net
|
||||
BIG_DIPPER_URL=https://testnet-milhon-blocks.nymtech.net
|
||||
EXPLORER_API_URL=https://sandbox-explorer.nymtech.net/api/v1
|
||||
VALIDATOR_API_URL=https://sandbox-validator.nymtech.net
|
||||
BIG_DIPPER_URL=https://sandbox-blocks.nymtech.net
|
||||
CURRENCY_DENOM=unymt
|
||||
CURRENCY_STAKING_DENOM=unyxt
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
// master APIs
|
||||
export const MASTER_URL = process.env.EXPLORER_API_URL;
|
||||
export const MASTER_VALIDATOR_URL = process.env.VALIDATOR_API_URL;
|
||||
export const API_BASE_URL = process.env.EXPLORER_API_URL;
|
||||
export const VALIDATOR_API_BASE_URL = process.env.VALIDATOR_API_URL;
|
||||
export const BIG_DIPPER = process.env.BIG_DIPPER_URL;
|
||||
|
||||
// specific API routes
|
||||
export const MIXNODE_PING = `${MASTER_URL}/api/ping`;
|
||||
export const MIXNODES_API = `${MASTER_URL}/api/mix-node`;
|
||||
export const GATEWAYS_API = `${MASTER_VALIDATOR_URL}/api/v1/gateways`;
|
||||
export const VALIDATORS_API = `${MASTER_VALIDATOR_URL}/validators`;
|
||||
export const BLOCK_API = `${MASTER_VALIDATOR_URL}/block`;
|
||||
export const COUNTRY_DATA_API = `${MASTER_URL}/api/countries`;
|
||||
export const UPTIME_STORY_API = `${MASTER_VALIDATOR_URL}/api/v1/status/mixnode`; // add ID then '/history' to this.
|
||||
export const MIXNODE_PING = `${API_BASE_URL}/ping`;
|
||||
export const MIXNODES_API = `${API_BASE_URL}/mix-nodes`;
|
||||
export const MIXNODE_API = `${API_BASE_URL}/mix-node`;
|
||||
export const GATEWAYS_API = `${VALIDATOR_API_BASE_URL}/api/v1/gateways`;
|
||||
export const VALIDATORS_API = `${VALIDATOR_API_BASE_URL}/validators`;
|
||||
export const BLOCK_API = `${VALIDATOR_API_BASE_URL}/block`;
|
||||
export const COUNTRY_DATA_API = `${API_BASE_URL}/countries`;
|
||||
export const UPTIME_STORY_API = `${VALIDATOR_API_BASE_URL}/api/v1/status/mixnode`; // add ID then '/history' to this.
|
||||
|
||||
// errors
|
||||
export const MIXNODE_API_ERROR =
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
COUNTRY_DATA_API,
|
||||
MIXNODE_PING,
|
||||
UPTIME_STORY_API,
|
||||
MIXNODE_API,
|
||||
} from './constants';
|
||||
|
||||
import {
|
||||
@@ -87,10 +88,10 @@ export class Api {
|
||||
static fetchDelegationsById = async (
|
||||
id: string,
|
||||
): Promise<DelegationsResponse> =>
|
||||
(await fetch(`${MIXNODES_API}/${id}/delegations`)).json();
|
||||
(await fetch(`${MIXNODE_API}/${id}/delegations`)).json();
|
||||
|
||||
static fetchStatsById = async (id: string): Promise<StatsResponse> =>
|
||||
(await fetch(`${MIXNODES_API}/${id}/stats`)).json();
|
||||
(await fetch(`${MIXNODE_API}/${id}/stats`)).json();
|
||||
|
||||
static fetchStatusById = async (id: string): Promise<StatusResponse> =>
|
||||
(await fetch(`${MIXNODE_PING}/${id}`)).json();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { printableCoin } from '@nymproject/nym-validator-client';
|
||||
import { Alert, CircularProgress, useMediaQuery, Box } from '@mui/material';
|
||||
import { Alert, Box, CircularProgress, useMediaQuery } from '@mui/material';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import Table from '@mui/material/Table';
|
||||
import TableBody from '@mui/material/TableBody';
|
||||
@@ -11,6 +10,7 @@ import TableRow from '@mui/material/TableRow';
|
||||
import Paper from '@mui/material/Paper';
|
||||
import { useMainContext } from 'src/context/main';
|
||||
import { ExpandMore } from '@mui/icons-material';
|
||||
import { currencyToString } from '../utils/currency';
|
||||
|
||||
export const BondBreakdownTable: React.FC = () => {
|
||||
const { mixnodeDetailInfo, delegations } = useMainContext();
|
||||
@@ -34,24 +34,23 @@ export const BondBreakdownTable: React.FC = () => {
|
||||
const thisMixnode = mixnodeDetailInfo?.data[0];
|
||||
|
||||
// delegations
|
||||
const decimalisedDelegations = printableCoin({
|
||||
amount: thisMixnode.total_delegation.amount.toString(),
|
||||
denom: thisMixnode.total_delegation.denom,
|
||||
});
|
||||
const decimalisedDelegations = currencyToString(
|
||||
thisMixnode.total_delegation.amount.toString(),
|
||||
thisMixnode.total_delegation.denom,
|
||||
);
|
||||
|
||||
// pledges
|
||||
const decimalisedPledges = printableCoin({
|
||||
amount: thisMixnode.bond_amount.amount.toString(),
|
||||
denom: thisMixnode.bond_amount.denom,
|
||||
});
|
||||
const decimalisedPledges = currencyToString(
|
||||
thisMixnode.pledge_amount.amount.toString(),
|
||||
thisMixnode.pledge_amount.denom,
|
||||
);
|
||||
|
||||
// bonds total (del + pledges)
|
||||
const pledgesSum = Number(thisMixnode.bond_amount.amount);
|
||||
const pledgesSum = Number(thisMixnode.pledge_amount.amount);
|
||||
const delegationsSum = Number(thisMixnode.total_delegation.amount);
|
||||
const bondsTotal = printableCoin({
|
||||
amount: (delegationsSum + pledgesSum).toString(),
|
||||
denom: 'upunk',
|
||||
});
|
||||
const bondsTotal = currencyToString(
|
||||
(delegationsSum + pledgesSum).toString(),
|
||||
);
|
||||
|
||||
setBonds({
|
||||
delegations: decimalisedDelegations,
|
||||
@@ -89,7 +88,7 @@ export const BondBreakdownTable: React.FC = () => {
|
||||
mixnodeDetailInfo.data[0].total_delegation.amount,
|
||||
);
|
||||
const rawPledgeAmount = Number(
|
||||
mixnodeDetailInfo.data[0].bond_amount.amount,
|
||||
mixnodeDetailInfo.data[0].pledge_amount.amount,
|
||||
);
|
||||
const rawTotalBondsAmount = rawDelegationAmount + rawPledgeAmount;
|
||||
return ((num * 100) / rawTotalBondsAmount).toFixed(1);
|
||||
@@ -203,7 +202,7 @@ export const BondBreakdownTable: React.FC = () => {
|
||||
{owner}
|
||||
</TableCell>
|
||||
<TableCell align="left">
|
||||
{printableCoin({ amount: amount.toString(), denom })}
|
||||
{currencyToString(amount.toString(), denom)}
|
||||
</TableCell>
|
||||
<TableCell align="left">
|
||||
{calcBondPercentage(amount)}%
|
||||
|
||||
@@ -8,9 +8,9 @@ import {
|
||||
TableHead,
|
||||
TableRow,
|
||||
} from '@mui/material';
|
||||
import { printableCoin } from '@nymproject/nym-validator-client';
|
||||
import { cellStyles } from './Universal-DataGrid';
|
||||
import { MixnodeRowType } from '../utils/index';
|
||||
import { currencyToString } from '../utils/currency';
|
||||
|
||||
export type ColumnsType = {
|
||||
field: string;
|
||||
@@ -28,7 +28,7 @@ export interface UniversalTableProps {
|
||||
|
||||
function formatCellValues(val: string | number, field: string) {
|
||||
if (field === 'bond') {
|
||||
return printableCoin({ amount: val.toString(), denom: 'upunk' });
|
||||
return currencyToString(val.toString());
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import { Button, Card, Grid, Typography } from '@mui/material';
|
||||
import { GridColDef, GridRenderCellParams } from '@mui/x-data-grid';
|
||||
import { printableCoin } from '@nymproject/nym-validator-client';
|
||||
import { SelectChangeEvent } from '@mui/material/Select';
|
||||
import { useMainContext } from 'src/context/main';
|
||||
import { gatewayToGridRow } from 'src/utils';
|
||||
@@ -13,6 +12,7 @@ import {
|
||||
cellStyles,
|
||||
UniversalDataGrid,
|
||||
} from 'src/components/Universal-DataGrid';
|
||||
import { currencyToString } from '../../utils/currency';
|
||||
|
||||
export const PageGateways: React.FC = () => {
|
||||
const { gateways } = useMainContext();
|
||||
@@ -79,17 +79,11 @@ export const PageGateways: React.FC = () => {
|
||||
renderHeader: () => <CustomColumnHeading headingTitle="Pledge" />,
|
||||
headerClassName: 'MuiDataGrid-header-override',
|
||||
headerAlign: 'left',
|
||||
renderCell: (params: GridRenderCellParams) => {
|
||||
const bondAsPunk = printableCoin({
|
||||
amount: params.value as string,
|
||||
denom: 'upunk',
|
||||
});
|
||||
return (
|
||||
<Typography sx={cellStyles} data-testid="pledge-amount">
|
||||
{bondAsPunk}
|
||||
</Typography>
|
||||
);
|
||||
},
|
||||
renderCell: (params: GridRenderCellParams) => (
|
||||
<Typography sx={cellStyles} data-testid="pledge-amount">
|
||||
{currencyToString(params.value)}
|
||||
</Typography>
|
||||
),
|
||||
},
|
||||
{
|
||||
field: 'host',
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { GridColDef, GridRenderCellParams } from '@mui/x-data-grid';
|
||||
import { printableCoin } from '@nymproject/nym-validator-client';
|
||||
import { Button, Grid, Link as MuiLink, Card } from '@mui/material';
|
||||
import { Link as RRDLink } from 'react-router-dom';
|
||||
import { SelectChangeEvent } from '@mui/material/Select';
|
||||
@@ -15,6 +14,7 @@ import {
|
||||
UniversalDataGrid,
|
||||
cellStyles,
|
||||
} from 'src/components/Universal-DataGrid';
|
||||
import { currencyToString } from '../../utils/currency';
|
||||
|
||||
export const PageMixnodes: React.FC = () => {
|
||||
const { mixnodes } = useMainContext();
|
||||
@@ -92,21 +92,15 @@ export const PageMixnodes: React.FC = () => {
|
||||
headerClassName: 'MuiDataGrid-header-override',
|
||||
width: 150,
|
||||
headerAlign: 'left',
|
||||
renderCell: (params: GridRenderCellParams) => {
|
||||
const bondAsPunk = printableCoin({
|
||||
amount: params.value as string,
|
||||
denom: 'upunk',
|
||||
});
|
||||
return (
|
||||
<MuiLink
|
||||
sx={cellStyles}
|
||||
component={RRDLink}
|
||||
to={`/network-components/mixnodes/${params.row.identity_key}`}
|
||||
>
|
||||
{bondAsPunk}
|
||||
</MuiLink>
|
||||
);
|
||||
},
|
||||
renderCell: (params: GridRenderCellParams) => (
|
||||
<MuiLink
|
||||
sx={cellStyles}
|
||||
component={RRDLink}
|
||||
to={`/network-components/mixnodes/${params.row.identity_key}`}
|
||||
>
|
||||
{currencyToString(params.value)}
|
||||
</MuiLink>
|
||||
),
|
||||
},
|
||||
{
|
||||
field: 'location',
|
||||
|
||||
@@ -7,13 +7,14 @@ export interface ClientConfig {
|
||||
|
||||
export interface MixNode {
|
||||
host: string;
|
||||
location: string;
|
||||
mix_port: number;
|
||||
http_api_port: number;
|
||||
verloc_port: number;
|
||||
sphinx_key: string;
|
||||
identity_key: string;
|
||||
version: string;
|
||||
mix_port: number;
|
||||
verloc_port: number;
|
||||
http_api_port: number;
|
||||
profit_margin_percent: number;
|
||||
location: string;
|
||||
}
|
||||
|
||||
export interface Gateway {
|
||||
@@ -32,7 +33,7 @@ export interface Amount {
|
||||
}
|
||||
|
||||
export interface MixNodeResponseItem {
|
||||
bond_amount: Amount;
|
||||
pledge_amount: Amount;
|
||||
total_delegation: Amount;
|
||||
owner: string;
|
||||
layer: string;
|
||||
@@ -74,7 +75,7 @@ export type MixNodeHistoryResponse = StatsResponse;
|
||||
|
||||
export interface GatewayResponseItem {
|
||||
block_height: number;
|
||||
bond_amount: Amount;
|
||||
pledge_amount: Amount;
|
||||
total_delegation: Amount;
|
||||
owner: string;
|
||||
gateway: Gateway;
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { printableCoin } from '@nymproject/nym-validator-client';
|
||||
|
||||
const DENOM = process.env.CURRENCY_DENOM || 'unym';
|
||||
const DENOM_STAKING = process.env.CURRENCY_STAKING_DENOM || 'unyx';
|
||||
|
||||
export const currencyToString = (amount: string, denom: string = DENOM) =>
|
||||
printableCoin({
|
||||
amount,
|
||||
denom,
|
||||
});
|
||||
|
||||
export const stakingCurrencyToString = (
|
||||
amount: string,
|
||||
denom: string = DENOM_STAKING,
|
||||
) =>
|
||||
printableCoin({
|
||||
amount,
|
||||
denom,
|
||||
});
|
||||
@@ -69,7 +69,7 @@ export function mixnodeToGridRow(arrayOfMixnodes: MixNodeResponse): any {
|
||||
return !arrayOfMixnodes
|
||||
? []
|
||||
: arrayOfMixnodes.map((mn) => {
|
||||
const pledge = Number(mn.bond_amount.amount) || 0;
|
||||
const pledge = Number(mn.pledge_amount.amount) || 0;
|
||||
const delegations = Number(mn.total_delegation.amount) || 0;
|
||||
const totalBond = pledge + delegations;
|
||||
const selfPercentage = ((pledge * 100) / totalBond).toFixed(2);
|
||||
@@ -96,7 +96,7 @@ export function gatewayToGridRow(
|
||||
owner: gw.owner,
|
||||
identity_key: gw.gateway.identity_key || '',
|
||||
location: gw?.gateway?.location || '',
|
||||
bond: gw.bond_amount.amount || 0,
|
||||
bond: gw.pledge_amount.amount || 0,
|
||||
host: gw.gateway.host || '',
|
||||
}));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user