Compare commits

..

2 Commits

Author SHA1 Message Date
durch da8fd4a2e6 If not exists 2024-11-27 10:33:14 +01:00
durch 50405de8d4 Add indexes to monitor run and testing route 2024-11-27 10:30:11 +01:00
46 changed files with 495 additions and 729 deletions
@@ -8,7 +8,7 @@ on:
description: Which gateway probe git ref to build the image with
env:
WORKING_DIRECTORY: "nym-node-status-api/nym-node-status-agent"
WORKING_DIRECTORY: "nym-node-status-agent"
CONTAINER_NAME: "node-status-agent"
jobs:
@@ -58,4 +58,4 @@ jobs:
- name: BuildAndPushImageOnHarbor
run: |
docker build --build-arg GIT_REF=${{ github.event.inputs.gateway_probe_git_ref }} -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.result }}-${{ steps.cleanup_gateway_probe_ref.outputs.git_ref }}
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
+1 -1
View File
@@ -3,7 +3,7 @@ on:
workflow_dispatch:
env:
WORKING_DIRECTORY: "nym-node-status-api/nym-node-status-api"
WORKING_DIRECTORY: "nym-node-status-api"
CONTAINER_NAME: "node-status-api"
jobs:
@@ -29,7 +29,7 @@ jobs:
uses: mikefarah/yq@v4.44.5
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-credential-proxy/Cargo.toml
- name: Remove existing tag if exists
run: |
Generated
+3 -3
View File
@@ -6061,7 +6061,7 @@ dependencies = [
[[package]]
name = "nym-node-status-api"
version = "1.0.0-rc.4-fix"
version = "1.0.0-rc.2"
dependencies = [
"anyhow",
"axum 0.7.7",
@@ -7019,9 +7019,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.104"
version = "0.9.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741"
checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2"
dependencies = [
"cc",
"libc",
@@ -38,7 +38,7 @@ pub struct TopologyReadPermit<'a> {
permit: RwLockReadGuard<'a, Option<NymTopology>>,
}
impl Deref for TopologyReadPermit<'_> {
impl<'a> Deref for TopologyReadPermit<'a> {
type Target = Option<NymTopology>;
fn deref(&self) -> &Self::Target {
@@ -32,7 +32,7 @@ impl Div<GasPrice> for Coin {
}
}
impl Div<GasPrice> for &Coin {
impl<'a> Div<GasPrice> for &'a Coin {
type Output = Gas;
fn div(self, rhs: GasPrice) -> Self::Output {
@@ -22,7 +22,7 @@ pub struct GasPrice {
pub denom: String,
}
impl Mul<Gas> for &GasPrice {
impl<'a> Mul<Gas> for &'a GasPrice {
type Output = Coin;
fn mul(self, gas_limit: Gas) -> Self::Output {
@@ -32,7 +32,7 @@ pub(crate) mod string_rfc3339_offset_date_time {
struct Rfc3339OffsetDateTimeVisitor;
impl Visitor<'_> for Rfc3339OffsetDateTimeVisitor {
impl<'de> Visitor<'de> for Rfc3339OffsetDateTimeVisitor {
type Value = OffsetDateTime;
fn expecting(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
+2 -1
View File
@@ -26,8 +26,9 @@ const PARALLEL_RUNS: usize = 32;
/// `lambda` ($\lambda$) in the DKG paper
const SECURITY_PARAMETER: usize = 256;
// note: ceiling in integer division can be achieved via q = (x + y - 1) / y;
/// ceil(SECURITY_PARAMETER / PARALLEL_RUNS) in the paper
const NUM_CHALLENGE_BITS: usize = SECURITY_PARAMETER.div_ceil(PARALLEL_RUNS);
const NUM_CHALLENGE_BITS: usize = (SECURITY_PARAMETER + PARALLEL_RUNS - 1) / PARALLEL_RUNS;
// type alias for ease of use
type FirstChallenge = Vec<Vec<Vec<u64>>>;
+2 -2
View File
@@ -196,7 +196,7 @@ impl<'b> Add<&'b Polynomial> for Polynomial {
}
}
impl Add<Polynomial> for &Polynomial {
impl<'a> Add<Polynomial> for &'a Polynomial {
type Output = Polynomial;
fn add(self, rhs: Polynomial) -> Polynomial {
@@ -212,7 +212,7 @@ impl Add<Polynomial> for Polynomial {
}
}
impl<'b> Add<&'b Polynomial> for &Polynomial {
impl<'a, 'b> Add<&'b Polynomial> for &'a Polynomial {
type Output = Polynomial;
fn add(self, rhs: &'b Polynomial) -> Self::Output {
@@ -37,7 +37,7 @@ pub struct GatewayHandshake<'a> {
handshake_future: BoxFuture<'a, Result<SharedGatewayKey, HandshakeError>>,
}
impl Future for GatewayHandshake<'_> {
impl<'a> Future for GatewayHandshake<'a> {
type Output = Result<SharedGatewayKey, HandshakeError>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
@@ -324,6 +324,18 @@ pub fn unchecked_aggregate_indices_signatures(
_aggregate_indices_signatures(params, vk, signatures_shares, false)
}
/// Generates parameters for the scheme setup.
///
/// # Arguments
///
/// * `total_coins` - it is the number of coins in a freshly generated wallet. It is the public parameter of the scheme.
///
/// # Returns
///
/// A `Parameters` struct containing group parameters, public key, the number of signatures (`total_coins`),
/// and a map of signatures for each index `l`.
///
#[cfg(test)]
mod tests {
use super::*;
@@ -264,7 +264,7 @@ impl<'b> Add<&'b VerificationKeyAuth> for VerificationKeyAuth {
}
}
impl Mul<Scalar> for &VerificationKeyAuth {
impl<'a> Mul<Scalar> for &'a VerificationKeyAuth {
type Output = VerificationKeyAuth;
#[inline]
@@ -984,7 +984,7 @@ pub struct SerialNumberRef<'a> {
pub(crate) inner: &'a [G1Projective],
}
impl SerialNumberRef<'_> {
impl<'a> SerialNumberRef<'a> {
pub fn to_bytes(&self) -> Vec<u8> {
let ss_len = self.inner.len();
let mut bytes: Vec<u8> = Vec::with_capacity(ss_len * 48);
+1 -1
View File
@@ -206,7 +206,7 @@ impl Deref for PublicKey {
}
}
impl<'b> Mul<&'b Scalar> for &PublicKey {
impl<'a, 'b> Mul<&'b Scalar> for &'a PublicKey {
type Output = G1Projective;
fn mul(self, rhs: &'b Scalar) -> Self::Output {
+1 -1
View File
@@ -305,7 +305,7 @@ impl<'b> Add<&'b VerificationKey> for VerificationKey {
}
}
impl Mul<Scalar> for &VerificationKey {
impl<'a> Mul<Scalar> for &'a VerificationKey {
type Output = VerificationKey;
#[inline]
+1 -1
View File
@@ -64,7 +64,7 @@ impl<'de> Deserialize<'de> for Recipient {
{
struct RecipientVisitor;
impl Visitor<'_> for RecipientVisitor {
impl<'de> Visitor<'de> for RecipientVisitor {
type Value = Recipient;
fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
+7 -7
View File
@@ -1,13 +1,6 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
//! Encodoing and decoding node routing information.
//!
//! This module is responsible for encoding and decoding node routing information, so that
//! they could be later put into an appropriate field in a sphinx header.
//! Currently, that routing information is an IP address, but in principle it can be anything
//! for as long as it's going to fit in the field.
use nym_crypto::asymmetric::identity;
use nym_sphinx_types::{NodeAddressBytes, NODE_ADDRESS_LENGTH};
@@ -19,6 +12,13 @@ use thiserror::Error;
pub type NodeIdentity = identity::PublicKey;
pub const NODE_IDENTITY_SIZE: usize = identity::PUBLIC_KEY_LENGTH;
/// Encodoing and decoding node routing information.
///
/// This module is responsible for encoding and decoding node routing information, so that
/// they could be later put into an appropriate field in a sphinx header.
/// Currently, that routing information is an IP address, but in principle it can be anything
/// for as long as it's going to fit in the field.
/// MAX_UNPADDED_LEN represents maximum length an unpadded address could have.
/// In this case it's an ipv6 socket address (with version prefix)
pub const MAX_NODE_ADDRESS_UNPADDED_LEN: usize = 19;
@@ -56,7 +56,7 @@ impl<'de> Deserialize<'de> for ReplySurb {
{
struct ReplySurbVisitor;
impl Visitor<'_> for ReplySurbVisitor {
impl<'de> Visitor<'de> for ReplySurbVisitor {
type Value = ReplySurb;
fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
+13
View File
@@ -253,12 +253,25 @@ impl Socks5RequestContent {
/// Deserialize the request type, connection id, destination address and port,
/// and the request body from bytes.
///
// TODO: this was already inaccurate
// /// Serialized bytes looks like this:
// ///
// /// --------------------------------------------------------------------------------------
// /// request_flag | connection_id | address_length | remote_address_bytes | request_data |
// /// 1 | 8 | 2 | address_length | ... |
// /// --------------------------------------------------------------------------------------
///
/// The request_flag tells us whether this is a new connection request (`new_connect`),
/// an already-established connection we should send up (`new_send`), or
/// a request to close an established connection (`new_close`).
// connect:
// RequestFlag::Connect || CONN_ID || ADDR_LEN || ADDR || <RETURN_ADDR>
//
// send:
// RequestFlag::Send || CONN_ID || LOCAL_CLOSED || DATA
// where DATA: SEQ || TRUE_DATA
pub fn try_from_bytes(b: &[u8]) -> Result<Socks5RequestContent, RequestDeserializationError> {
// each request needs to at least contain flag and ConnectionId
if b.is_empty() {
@@ -34,28 +34,6 @@ This page displays a full list of all the changes during our release cycle from
<VarInfo />
## `magura-drift`
Second patch to `v2024.13-magura` release version.
- [Release binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2024.13-magura-drift)
- [`nym-node`](nodes/nym-node.mdx) version `1.1.12`
```sh
nym-node
Binary Name: nym-node
Build Timestamp: 2024-11-29T13:10:51.813092288Z
Build Version: 1.1.12
Commit SHA: 4a9a5579c40ad956163ea02e01d7b53aef2ac8ef
Commit Date: 2024-11-29T14:06:32.000000000+01:00
Commit Branch: HEAD
rustc Version: 1.83.0
rustc Channel: stable
cargo Profile: release
```
- This patch adds a peer storage manager to fix issues causing external clients to be blocked, ensuring they can successfully connect to different nodes.
## `v2024.13-magura-patched`
- [Release binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2024.13-magura-patched)
@@ -76,11 +54,12 @@ cargo Profile: release
<Callout type="warning" emoji="⚠️">
After changes coming along with `v2024.13-magura` (`nym-node v1.1.10`), Nym Explorer is no longer picking all values correctly. Instead of fixing this outdated explorer, we are working on a new one, coming out soon.
After changes coming along with `v2024.13-magura` (`nym-node v1.1.10`), Nym Explorer is no longer picking all values correctly. Insstead of fixing this outdated explorer, we are working on a new one, coming out soon.
[Nym Harbourmaster](https://harbourmaster.nymtech.net) has cache of 90min, expect your values to be updated with delay. We are aware of some issues with Nym Harbourmaster and working hard to resolve them in the upcoming explorer v2. To check your routing values in real time, you can use [`nym-gateway-probe`](nodes/performance-and-testing/gateway-probe).
</Callout>
### Operators Updates & Tools
- Updated [`network_tunnel_manager.sh`](https://github.com/nymtech/nym/blob/develop/scripts/network_tunnel_manager.sh) (moved to our monorepo) helps operators to configure their IP tables rules for `nymtun` and `wireguard` routing.
@@ -53,7 +53,7 @@ journalctl -f -u nym-node.service
</Steps>
<Callout type="warning" emoji="⚠️">
After changes coming along with `v2024.13-magura` (`nym-node v1.1.10`), Nym Explorer is no longer picking all values correctly. Instead of fixing this outdated explorer, we are working on a new one, coming out soon.
After changes coming along with `v2024.13-magura` (`nym-node v1.1.10`), Nym Explorer is no longer picking all values correctly. Insstead of fixing this outdated explorer, we are working on a new one, coming out soon.
[Nym Harbourmaster](https://harbourmaster.nymtech.net) has cache of 90min, expect your values to be updated with delay. We are aware of some issues with Nym Harbourmaster and working hard to resolve them in the upcoming explorer v2. To check your routing values in real time, you can use [`nym-gateway-probe`](../performance-and-testing/gateway-probe).
</Callout>
@@ -120,7 +120,7 @@ From `nym-wallet` version `1.2.15` onward the application allows and prompts ope
###### 2. Verify the binary and extract it if needed
- Download [`hashes.json`](https://github.com/nymtech/nym/releases/download/nym-wallet-v1.2.15/hashes.json)
- Download [`hashes.json`]https://github.com/nymtech/nym/releases/download/nym-wallet-v1.2.15/hashes.json
- Open it with your text editor or print it's content with `cat hashes.json`
- Run `sha256sum <WALLET_BINARY>` for example `sha256sum ./nym-wallet_1.2.15_amd64.AppImage`
- If your have to extract it (like `.tar.gz`) do it
@@ -17,12 +17,12 @@ This documentation page provides a guide on how to set up and run a [NYM NODE](.
```sh
nym-node
Binary Name: nym-node
Build Timestamp: 2024-11-29T13:10:51.813092288Z
Build Version: 1.1.12
Commit SHA: 4a9a5579c40ad956163ea02e01d7b53aef2ac8ef
Commit Date: 2024-11-29T14:06:32.000000000+01:00
Build Timestamp: 2024-11-22T14:30:48.067329245Z
Build Version: 1.1.11
Commit SHA: 01c7b2819ee3d328deccd303b4113ff415d7e276
Commit Date: 2024-11-22T10:50:59.000000000+01:00
Commit Branch: HEAD
rustc Version: 1.83.0
rustc Version: 1.82.0
rustc Channel: stable
cargo Profile: release
```
+1 -1
View File
@@ -166,7 +166,7 @@ impl GeoIp {
}
}
impl TryFrom<&City<'_>> for Location {
impl<'a> TryFrom<&City<'a>> for Location {
type Error = String;
fn try_from(city: &City) -> Result<Self, Self::Error> {
+1 -1
View File
@@ -65,7 +65,7 @@ impl<'r> FromRequest<'r> for Location {
}
}
impl OpenApiFromRequest<'_> for Location {
impl<'a> OpenApiFromRequest<'a> for Location {
fn from_request_input(
_gen: &mut OpenApiGenerator,
_name: String,
@@ -0,0 +1,8 @@
/*
* Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
* SPDX-License-Identifier: Apache-2.0
*/
CREATE INDEX IF NOT EXISTS monitor_run_timestamp on monitor_run(timestamp);
CREATE INDEX IF NOT EXISTS monitor_run_id on monitor_run(id);
CREATE INDEX IF NOT EXISTS testing_route_monitor_run_id on testing_route(monitor_run_id)
+1 -1
View File
@@ -64,7 +64,7 @@ pub(crate) mod overengineered_offset_date_time_serde {
])),
];
impl Visitor<'_> for OffsetDateTimeVisitor {
impl<'de> Visitor<'de> for OffsetDateTimeVisitor {
type Value = OffsetDateTime;
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
+1 -1
View File
@@ -76,7 +76,7 @@ pub(crate) async fn submit_public_keys(controllers: &mut [TestingDkgController],
.unwrap();
}
let threshold = (2 * controllers.len() as u64).div_ceil(3);
let threshold = (2 * controllers.len() as u64 + 3 - 1) / 3;
let mut guard = controllers[0].chain_state.lock().unwrap();
guard.dkg_contract.epoch.state = EpochState::DealingExchange { resharing };
@@ -59,7 +59,7 @@ impl GatewayClientHandle {
}
}
impl UnlockedGatewayClientHandle<'_> {
impl<'a> UnlockedGatewayClientHandle<'a> {
pub(crate) fn get_mut_unchecked(
&mut self,
) -> &mut GatewayClient<nyxd::Client, PersistentStorage> {
@@ -674,7 +674,7 @@ pub(crate) struct ChainWritePermit<'a> {
inner: RwLockWriteGuard<'a, DirectSigningHttpRpcNyxdClient>,
}
impl Deref for ChainWritePermit<'_> {
impl<'a> Deref for ChainWritePermit<'a> {
type Target = DirectSigningHttpRpcNyxdClient;
fn deref(&self) -> &Self::Target {
@@ -3,7 +3,7 @@
[package]
name = "nym-node-status-api"
version = "1.0.0-rc.4-fix"
version = "1.0.0-rc.2"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
@@ -45,6 +45,11 @@ pub(crate) struct Cli {
#[arg(value_parser = parse_duration)]
pub(crate) nym_api_client_timeout: Duration,
/// Explorer api client timeout.
#[clap(long, default_value = "15", env = "EXPLORER_CLIENT_TIMEOUT")]
#[arg(value_parser = parse_duration)]
pub(crate) explorer_client_timeout: Duration,
/// Connection url for the database.
#[clap(long, env = "DATABASE_URL")]
pub(crate) database_url: String,
@@ -65,18 +70,10 @@ pub(crate) struct Cli {
#[arg(value_parser = parse_duration)]
pub(crate) testruns_refresh_interval: Duration,
#[clap(long, default_value = "86400", env = "NODE_STATUS_API_GEODATA_TTL")]
#[arg(value_parser = parse_duration)]
pub(crate) geodata_ttl: Duration,
#[clap(env = "NODE_STATUS_API_AGENT_KEY_LIST")]
#[arg(value_delimiter = ',')]
pub(crate) agent_key_list: Vec<String>,
/// https://github.com/ipinfo/rust
#[clap(long, env = "IPINFO_API_TOKEN")]
pub(crate) ipinfo_api_token: String,
#[clap(
long,
default_value_t = 40,
@@ -12,7 +12,6 @@ pub(crate) struct GatewayRecord {
pub(crate) bonded: bool,
pub(crate) blacklisted: bool,
pub(crate) self_described: String,
// TODO dz shouldn't be an option
pub(crate) explorer_pretty_bond: Option<String>,
pub(crate) last_updated_utc: i64,
pub(crate) performance: u8,
@@ -216,6 +215,7 @@ pub(crate) const MIXNODES_BONDED_RESERVE: &str = "mixnodes.bonded.reserve";
pub(crate) const MIXNODES_BLACKLISTED_COUNT: &str = "mixnodes.blacklisted.count";
pub(crate) const GATEWAYS_BONDED_COUNT: &str = "gateways.bonded.count";
pub(crate) const GATEWAYS_EXPLORER_COUNT: &str = "gateways.explorer.count";
pub(crate) const GATEWAYS_BLACKLISTED_COUNT: &str = "gateways.blacklisted.count";
pub(crate) const MIXNODES_HISTORICAL_COUNT: &str = "mixnodes.historical.count";
@@ -272,6 +272,7 @@ pub(crate) mod gateway {
pub(crate) bonded: GatewaySummaryBonded,
pub(crate) blacklisted: GatewaySummaryBlacklisted,
pub(crate) historical: GatewaySummaryHistorical,
pub(crate) explorer: GatewaySummaryExplorer,
}
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
@@ -8,7 +8,7 @@ use crate::{
models::{
gateway::{
GatewaySummary, GatewaySummaryBlacklisted, GatewaySummaryBonded,
GatewaySummaryHistorical,
GatewaySummaryExplorer, GatewaySummaryHistorical,
},
mixnode::{
MixnodeSummary, MixnodeSummaryBlacklisted, MixnodeSummaryBonded,
@@ -82,6 +82,7 @@ async fn from_summary_dto(items: Vec<SummaryDto>) -> HttpResult<NetworkSummary>
const MIXNODES_BONDED_RESERVE: &str = "mixnodes.bonded.reserve";
const MIXNODES_BLACKLISTED_COUNT: &str = "mixnodes.blacklisted.count";
const GATEWAYS_BONDED_COUNT: &str = "gateways.bonded.count";
const GATEWAYS_EXPLORER_COUNT: &str = "gateways.explorer.count";
const GATEWAYS_BLACKLISTED_COUNT: &str = "gateways.blacklisted.count";
const MIXNODES_HISTORICAL_COUNT: &str = "mixnodes.historical.count";
const GATEWAYS_HISTORICAL_COUNT: &str = "gateways.historical.count";
@@ -95,6 +96,7 @@ async fn from_summary_dto(items: Vec<SummaryDto>) -> HttpResult<NetworkSummary>
// check we have all the keys we are expecting, and build up a map of errors for missing one
let keys = [
GATEWAYS_BONDED_COUNT,
GATEWAYS_EXPLORER_COUNT,
GATEWAYS_HISTORICAL_COUNT,
GATEWAYS_BLACKLISTED_COUNT,
MIXNODES_BLACKLISTED_COUNT,
@@ -137,6 +139,10 @@ async fn from_summary_dto(items: Vec<SummaryDto>) -> HttpResult<NetworkSummary>
.unwrap_or_default();
let gateways_bonded_count: SummaryDto =
map.get(GATEWAYS_BONDED_COUNT).cloned().unwrap_or_default();
let gateways_explorer_count: SummaryDto = map
.get(GATEWAYS_EXPLORER_COUNT)
.cloned()
.unwrap_or_default();
let mixnodes_historical_count: SummaryDto = map
.get(MIXNODES_HISTORICAL_COUNT)
.cloned()
@@ -181,6 +187,10 @@ async fn from_summary_dto(items: Vec<SummaryDto>) -> HttpResult<NetworkSummary>
count: to_count_i32(&gateways_historical_count),
last_updated_utc: to_timestamp(&gateways_historical_count),
},
explorer: GatewaySummaryExplorer {
count: to_count_i32(&gateways_explorer_count),
last_updated_utc: to_timestamp(&gateways_explorer_count),
},
},
})
}
@@ -28,15 +28,13 @@ async fn main() -> anyhow::Result<()> {
let storage = db::Storage::init(connection_url).await?;
let db_pool = storage.pool_owned();
let args_clone = args.clone();
tokio::spawn(async move {
monitor::spawn_in_background(
db_pool,
args_clone.explorer_client_timeout,
args_clone.nym_api_client_timeout,
args_clone.nyxd_addr,
&args_clone.nyxd_addr,
args_clone.monitor_refresh_interval,
args_clone.ipinfo_api_token,
args_clone.geodata_ttl,
)
.await;
tracing::info!("Started monitor task");
@@ -1,151 +0,0 @@
use cosmwasm_std::{Addr, Coin};
use serde::{Deserialize, Deserializer, Serialize};
pub(crate) struct IpInfoClient {
client: reqwest::Client,
token: String,
}
impl IpInfoClient {
pub(crate) fn new(token: impl Into<String>) -> Self {
let client = reqwest::Client::new();
let token = token.into();
Self { client, token }
}
pub(crate) async fn locate_ip(&self, ip: impl AsRef<str>) -> anyhow::Result<Location> {
let url = format!("https://ipinfo.io/{}?token={}", ip.as_ref(), &self.token);
let response = self
.client
.get(url)
.send()
.await
// map non 2xx responses to error
.and_then(|res| res.error_for_status())
.map_err(|err| {
if matches!(err.status(), Some(reqwest::StatusCode::TOO_MANY_REQUESTS)) {
tracing::error!("ipinfo rate limit exceeded");
}
anyhow::Error::from(err)
})?;
let raw_response = response.text().await?;
let response: LocationResponse =
serde_json::from_str(&raw_response).inspect_err(|e| tracing::error!("{e}"))?;
let location = response.into();
Ok(location)
}
/// check DOESN'T consume bandwidth allowance
pub(crate) async fn check_remaining_bandwidth(
&self,
) -> anyhow::Result<ipinfo::MeResponseRequests> {
let url = format!("https://ipinfo.io/me?token={}", &self.token);
let response = self
.client
.get(url)
.send()
.await
// map non 2xx responses to error
.and_then(|res| res.error_for_status())
.map_err(|err| {
if matches!(err.status(), Some(reqwest::StatusCode::TOO_MANY_REQUESTS)) {
tracing::error!("ipinfo rate limit exceeded");
}
anyhow::Error::from(err)
})?;
let response: ipinfo::MeResponse = response.json().await?;
Ok(response.requests)
}
}
#[derive(Debug, Clone, Serialize)]
pub(crate) struct NodeGeoData {
pub(crate) identity_key: String,
pub(crate) owner: Addr,
pub(crate) pledge_amount: Coin,
pub(crate) location: Location,
}
#[derive(Debug, Clone, Serialize)]
pub(crate) struct Location {
pub(crate) two_letter_iso_country_code: String,
#[serde(flatten)]
pub(crate) location: Coordinates,
}
impl From<LocationResponse> for Location {
fn from(value: LocationResponse) -> Self {
Self {
two_letter_iso_country_code: value.two_letter_iso_country_code,
location: value.loc,
}
}
}
#[derive(Debug, Clone, Deserialize)]
pub(crate) struct LocationResponse {
#[serde(rename = "country")]
pub(crate) two_letter_iso_country_code: String,
#[serde(deserialize_with = "deserialize_loc")]
pub(crate) loc: Coordinates,
}
fn deserialize_loc<'de, D>(deserializer: D) -> Result<Coordinates, D::Error>
where
D: Deserializer<'de>,
{
let loc_raw = String::deserialize(deserializer)?;
return match loc_raw.split_once(',') {
Some((lat, long)) => Ok(Coordinates {
latitude: lat.parse().map_err(|err| serde::de::Error::custom(err))?,
longitude: long.parse().map_err(|err| serde::de::Error::custom(err))?,
}),
None => Err(serde::de::Error::custom("coordinates")),
};
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub(crate) struct Coordinates {
pub(crate) latitude: f64,
pub(crate) longitude: f64,
}
impl Location {
pub(crate) fn empty() -> Self {
Self {
two_letter_iso_country_code: String::default(),
location: Coordinates {
..Default::default()
},
}
}
}
pub(crate) mod ipinfo {
use super::*;
// clippy doesn't understand it's used for typed deserialization
#[allow(dead_code)]
#[derive(Debug, Clone, Deserialize)]
/// `/me` is undocumented in their developers page
/// https://ipinfo.io/developers/responses
/// but explained here
/// https://community.ipinfo.io/t/easy-way-to-check-allowance-usage/5755/2
pub(crate) struct MeResponse {
token: String,
pub(crate) requests: MeResponseRequests,
}
// clippy doesn't understand it's used for typed deserialization
#[allow(dead_code)]
#[derive(Debug, Clone, Deserialize)]
pub(crate) struct MeResponseRequests {
pub(crate) day: u64,
pub(crate) month: u64,
pub(crate) limit: u64,
pub(crate) remaining: u64,
}
}
@@ -2,17 +2,16 @@
use crate::db::models::{
gateway, mixnode, GatewayRecord, MixnodeRecord, NetworkSummary, GATEWAYS_BLACKLISTED_COUNT,
GATEWAYS_BONDED_COUNT, GATEWAYS_HISTORICAL_COUNT, MIXNODES_BLACKLISTED_COUNT,
MIXNODES_BONDED_ACTIVE, MIXNODES_BONDED_COUNT, MIXNODES_BONDED_INACTIVE,
MIXNODES_BONDED_RESERVE, MIXNODES_HISTORICAL_COUNT,
GATEWAYS_BONDED_COUNT, GATEWAYS_EXPLORER_COUNT, GATEWAYS_HISTORICAL_COUNT,
MIXNODES_BLACKLISTED_COUNT, MIXNODES_BONDED_ACTIVE, MIXNODES_BONDED_COUNT,
MIXNODES_BONDED_INACTIVE, MIXNODES_BONDED_RESERVE, MIXNODES_HISTORICAL_COUNT,
};
use crate::db::{queries, DbPool};
use crate::monitor::geodata::{Location, NodeGeoData};
use anyhow::anyhow;
use cosmwasm_std::Decimal;
use moka::future::Cache;
use nym_explorer_client::{ExplorerClient, PrettyDetailedGatewayBond};
use nym_network_defaults::NymNetworkDetails;
use nym_validator_client::client::{NodeId, NymApiClientExt};
use nym_validator_client::client::NymApiClientExt;
use nym_validator_client::models::{
LegacyDescribedMixNode, MixNodeBondAnnotated, NymNodeDescription,
};
@@ -21,55 +20,40 @@ use nym_validator_client::nyxd::contract_traits::PagedMixnetQueryClient;
use nym_validator_client::nyxd::{AccountId, NyxdClient};
use nym_validator_client::NymApiClient;
use reqwest::Url;
use std::collections::{HashMap, HashSet};
use std::collections::HashSet;
use std::str::FromStr;
use tokio::time::Duration;
use tracing::instrument;
pub(crate) use geodata::IpInfoClient;
mod geodata;
// TODO dz should be configurable
const FAILURE_RETRY_DELAY: Duration = Duration::from_secs(60);
static DELEGATION_PROGRAM_WALLET: &str = "n1rnxpdpx3kldygsklfft0gech7fhfcux4zst5lw";
struct Monitor {
db_pool: DbPool,
network_details: NymNetworkDetails,
nym_api_client_timeout: Duration,
nyxd_addr: Url,
ipinfo: IpInfoClient,
geocache: Cache<NodeId, Location>,
}
// TODO dz: query many NYM APIs:
// multiple instances running directory cache, ask sachin
#[instrument(level = "debug", name = "data_monitor", skip_all)]
pub(crate) async fn spawn_in_background(
db_pool: DbPool,
explorer_client_timeout: Duration,
nym_api_client_timeout: Duration,
nyxd_addr: Url,
nyxd_addr: &Url,
refresh_interval: Duration,
ipinfo_api_token: String,
geodata_ttl: Duration,
) {
let geocache = Cache::builder().time_to_live(geodata_ttl).build();
let ipinfo = IpInfoClient::new(ipinfo_api_token.clone());
let mut monitor = Monitor {
db_pool,
network_details: nym_network_defaults::NymNetworkDetails::new_from_env(),
nym_api_client_timeout,
nyxd_addr,
ipinfo,
geocache,
};
let network_defaults = nym_network_defaults::NymNetworkDetails::new_from_env();
loop {
tracing::info!("Refreshing node info...");
if let Err(e) = monitor.run().await {
if let Err(e) = run(
&db_pool,
&network_defaults,
explorer_client_timeout,
nym_api_client_timeout,
nyxd_addr,
)
.await
{
tracing::error!(
"Monitor run failed: {e}, retrying in {}s...",
FAILURE_RETRY_DELAY.as_secs()
@@ -86,370 +70,344 @@ pub(crate) async fn spawn_in_background(
}
}
impl Monitor {
async fn run(&mut self) -> anyhow::Result<()> {
self.check_ipinfo_bandwidth().await;
async fn run(
pool: &DbPool,
network_details: &NymNetworkDetails,
explorer_client_timeout: Duration,
nym_api_client_timeout: Duration,
nyxd_addr: &Url,
) -> anyhow::Result<()> {
let default_api_url = network_details
.endpoints
.first()
.expect("rust sdk mainnet default incorrectly configured")
.api_url()
.clone()
.expect("rust sdk mainnet default missing api_url");
let default_explorer_url = network_details.explorer_api.clone().map(|url| {
url.parse()
.expect("rust sdk mainnet default explorer url not parseable")
});
let default_api_url = self
.network_details
.endpoints
.first()
.expect("rust sdk mainnet default incorrectly configured")
.api_url()
.clone()
.expect("rust sdk mainnet default missing api_url");
// TODO dz replace explorer api with ipinfo.io
let default_explorer_url =
default_explorer_url.expect("explorer url missing in network config");
let explorer_client =
ExplorerClient::new_with_timeout(default_explorer_url, explorer_client_timeout)?;
let explorer_gateways = explorer_client
.unstable_get_gateways()
.await
.log_error("unstable_get_gateways")?;
let api_client =
NymApiClient::new_with_timeout(default_api_url, self.nym_api_client_timeout);
let api_client = NymApiClient::new_with_timeout(default_api_url, nym_api_client_timeout);
let all_nodes = api_client
.get_all_described_nodes()
.await
.log_error("get_all_described_nodes")?;
tracing::debug!("Fetched {} total nodes", all_nodes.len());
let all_nodes = api_client
.get_all_described_nodes()
.await
.log_error("get_all_described_nodes")?;
tracing::debug!("Fetched {} total nodes", all_nodes.len());
let gateways = all_nodes
.iter()
.filter(|node| node.description.declared_role.entry)
.collect::<Vec<_>>();
tracing::debug!(
"{}/{} with declared entry gateway capability",
gateways.len(),
all_nodes.len()
);
let gateways = all_nodes
.iter()
.filter(|node| node.description.declared_role.entry)
.collect::<Vec<_>>();
tracing::debug!("Of those, {} gateways", gateways.len());
for gw in gateways.iter() {
tracing::debug!("{}", gw.ed25519_identity_key().to_base58_string());
}
let mixnodes = all_nodes
.iter()
.filter(|node| node.description.declared_role.mixnode)
.collect::<Vec<_>>();
tracing::debug!(
"{}/{} with declared mixnode capability",
mixnodes.len(),
all_nodes.len()
);
let mixnodes = all_nodes
.iter()
.filter(|node| node.description.declared_role.mixnode)
.collect::<Vec<_>>();
tracing::debug!("Of those, {} mixnodes", mixnodes.len());
let bonded_node_info = api_client
.get_all_bonded_nym_nodes()
.await?
.into_iter()
.map(|node| (node.bond_information.node_id, node.bond_information))
// for faster reads
.collect::<HashMap<_, _>>();
log_gw_in_explorer_not_api(explorer_gateways.as_slice(), gateways.as_slice());
let mut gateway_geodata = Vec::new();
for gateway in gateways.iter() {
if let Some(node_info) = bonded_node_info.get(&gateway.node_id) {
let gw_geodata = NodeGeoData {
identity_key: node_info.node.identity_key.to_owned(),
owner: node_info.owner.to_owned(),
pledge_amount: node_info.original_pledge.to_owned(),
location: self.location_cached(gateway).await,
};
gateway_geodata.push(gw_geodata);
let all_skimmed_nodes = api_client
.get_all_basic_nodes(None)
.await
.log_error("get_all_basic_nodes")?;
let mixnodes = api_client
.get_cached_mixnodes()
.await
.log_error("get_cached_mixnodes")?;
tracing::debug!("Fetched {} mixnodes", mixnodes.len());
// let gateways_blacklisted = gateways.iter().filter(|gw|gw.)
let gateways_blacklisted = all_skimmed_nodes
.iter()
.filter_map(|node| {
if node.performance.round_to_integer() <= 50 && node.supported_roles.entry {
Some(node.ed25519_identity_pubkey.to_base58_string())
} else {
None
}
}
})
.collect::<HashSet<_>>();
// contains performance data
let all_skimmed_nodes = api_client
.get_all_basic_nodes(None)
.await
.log_error("get_all_basic_nodes")?;
// Cached mixnodes don't include blacklisted nodes
// We need that to calculate the total locked tokens later
let mixnodes = api_client
.nym_api
.get_mixnodes_detailed_unfiltered()
.await
.log_error("get_mixnodes_detailed_unfiltered")?;
let mixnodes_described = api_client
.nym_api
.get_mixnodes_described()
.await
.log_error("get_mixnodes_described")?;
let mixnodes_active = api_client
.nym_api
.get_active_mixnodes()
.await
.log_error("get_active_mixnodes")?;
let delegation_program_members =
get_delegation_program_details(network_details, nyxd_addr).await?;
let gateways_blacklisted = all_skimmed_nodes
.iter()
.filter_map(|node| {
if node.performance.round_to_integer() <= 50 && node.supported_roles.entry {
Some(node.ed25519_identity_pubkey.to_base58_string())
} else {
None
}
})
.collect::<HashSet<_>>();
// keep stats for later
let count_bonded_mixnodes = mixnodes.len();
let count_bonded_gateways = gateways.len();
let count_explorer_gateways = explorer_gateways.len();
let count_bonded_mixnodes_active = mixnodes_active.len();
// Cached mixnodes don't include blacklisted nodes
// We need that to calculate the total locked tokens later
// TODO dz deprecated API, remove
let legacy_mixnodes = api_client
.nym_api
.get_mixnodes_detailed_unfiltered()
.await
.log_error("get_mixnodes_detailed_unfiltered")?;
let mixnodes_described = api_client
.nym_api
.get_mixnodes_described()
.await
.log_error("get_mixnodes_described")?;
let mixnodes_active = api_client
.nym_api
.get_basic_active_mixing_assigned_nodes(None, false, None, None)
.await
.log_error("get_active_mixnodes")?
.nodes
.data;
let delegation_program_members =
get_delegation_program_details(&self.network_details, &self.nyxd_addr).await?;
let gateway_records = prepare_gateway_data(
&gateways,
&gateways_blacklisted,
explorer_gateways,
all_skimmed_nodes,
)?;
queries::insert_gateways(pool, gateway_records)
.await
.map(|_| {
tracing::debug!("Gateway info written to DB!");
})?;
// keep stats for later
let count_bonded_mixnodes = mixnodes.len();
let count_bonded_gateways = gateways.len();
let count_bonded_mixnodes_active = mixnodes_active.len();
// instead of counting blacklisted GWs returned from API cache, count from the active set
let count_gateways_blacklisted = gateways
.iter()
.filter(|gw| {
let gw_identity = gw.ed25519_identity_key().to_base58_string();
gateways_blacklisted.contains(&gw_identity)
})
.count();
let gateway_records = self.prepare_gateway_data(
&gateways,
&gateways_blacklisted,
gateway_geodata,
all_skimmed_nodes,
)?;
let pool = self.db_pool.clone();
queries::insert_gateways(&pool, gateway_records)
if count_gateways_blacklisted > 0 {
queries::write_blacklisted_gateways_to_db(pool, gateways_blacklisted.iter())
.await
.map(|_| {
tracing::debug!("Gateway info written to DB!");
tracing::debug!(
"Gateway blacklist info written to DB! {} blacklisted by Nym API",
count_gateways_blacklisted
)
})?;
}
let count_gateways_blacklisted = gateways
let mixnode_records =
prepare_mixnode_data(&mixnodes, mixnodes_described, delegation_program_members)?;
queries::insert_mixnodes(pool, mixnode_records)
.await
.map(|_| {
tracing::debug!("Mixnode info written to DB!");
})?;
let count_mixnodes_blacklisted = mixnodes.iter().filter(|elem| elem.blacklisted).count();
let recently_unbonded_gateways = queries::ensure_gateways_still_bonded(pool, &gateways).await?;
let recently_unbonded_mixnodes = queries::ensure_mixnodes_still_bonded(pool, &mixnodes).await?;
let count_bonded_mixnodes_reserve = 0; // TODO: NymAPI doesn't report the reserve set size
let count_bonded_mixnodes_inactive = count_bonded_mixnodes - count_bonded_mixnodes_active;
let (all_historical_gateways, all_historical_mixnodes) = calculate_stats(pool).await?;
//
// write summary keys and values to table
//
let nodes_summary = vec![
(MIXNODES_BONDED_COUNT, &count_bonded_mixnodes),
(MIXNODES_BONDED_ACTIVE, &count_bonded_mixnodes_active),
(MIXNODES_BONDED_INACTIVE, &count_bonded_mixnodes_inactive),
(MIXNODES_BONDED_RESERVE, &count_bonded_mixnodes_reserve),
(MIXNODES_BLACKLISTED_COUNT, &count_mixnodes_blacklisted),
(GATEWAYS_BONDED_COUNT, &count_bonded_gateways),
(GATEWAYS_EXPLORER_COUNT, &count_explorer_gateways),
(MIXNODES_HISTORICAL_COUNT, &all_historical_mixnodes),
(GATEWAYS_HISTORICAL_COUNT, &all_historical_gateways),
(GATEWAYS_BLACKLISTED_COUNT, &count_gateways_blacklisted),
];
let last_updated = chrono::offset::Utc::now();
let last_updated_utc = last_updated.timestamp().to_string();
let network_summary = NetworkSummary {
mixnodes: mixnode::MixnodeSummary {
bonded: mixnode::MixnodeSummaryBonded {
count: count_bonded_mixnodes.cast_checked()?,
active: count_bonded_mixnodes_active.cast_checked()?,
inactive: count_bonded_mixnodes_inactive.cast_checked()?,
reserve: count_bonded_mixnodes_reserve.cast_checked()?,
last_updated_utc: last_updated_utc.to_owned(),
},
blacklisted: mixnode::MixnodeSummaryBlacklisted {
count: count_mixnodes_blacklisted.cast_checked()?,
last_updated_utc: last_updated_utc.to_owned(),
},
historical: mixnode::MixnodeSummaryHistorical {
count: all_historical_mixnodes.cast_checked()?,
last_updated_utc: last_updated_utc.to_owned(),
},
},
gateways: gateway::GatewaySummary {
bonded: gateway::GatewaySummaryBonded {
count: count_bonded_gateways.cast_checked()?,
last_updated_utc: last_updated_utc.to_owned(),
},
blacklisted: gateway::GatewaySummaryBlacklisted {
count: count_gateways_blacklisted.cast_checked()?,
last_updated_utc: last_updated_utc.to_owned(),
},
historical: gateway::GatewaySummaryHistorical {
count: all_historical_gateways.cast_checked()?,
last_updated_utc: last_updated_utc.to_owned(),
},
explorer: gateway::GatewaySummaryExplorer {
count: count_explorer_gateways.cast_checked()?,
last_updated_utc: last_updated_utc.to_owned(),
},
},
};
queries::insert_summaries(pool, &nodes_summary, &network_summary, last_updated).await?;
let mut log_lines: Vec<String> = vec![];
for (key, value) in nodes_summary.iter() {
log_lines.push(format!("{} = {}", key, value));
}
log_lines.push(format!(
"recently_unbonded_mixnodes = {}",
recently_unbonded_mixnodes
));
log_lines.push(format!(
"recently_unbonded_gateways = {}",
recently_unbonded_gateways
));
tracing::info!("Directory summary: \n{}", log_lines.join("\n"));
Ok(())
}
fn prepare_gateway_data(
gateways: &[&NymNodeDescription],
gateways_blacklisted: &HashSet<String>,
explorer_gateways: Vec<PrettyDetailedGatewayBond>,
skimmed_gateways: Vec<SkimmedNode>,
) -> anyhow::Result<Vec<GatewayRecord>> {
let mut gateway_records = Vec::new();
for gateway in gateways {
let identity_key = gateway.ed25519_identity_key().to_base58_string();
let bonded = true;
let last_updated_utc = chrono::offset::Utc::now().timestamp();
let blacklisted = gateways_blacklisted.contains(&identity_key);
let self_described = serde_json::to_string(&gateway.description)?;
let explorer_pretty_bond = explorer_gateways
.iter()
.filter(|gw| {
let gw_identity = gw.ed25519_identity_key().to_base58_string();
gateways_blacklisted.contains(&gw_identity)
.find(|g| g.gateway.identity_key.eq(&identity_key));
let explorer_pretty_bond = explorer_pretty_bond.and_then(|g| serde_json::to_string(g).ok());
let performance = skimmed_gateways
.iter()
.find(|g| {
g.ed25519_identity_pubkey
.to_base58_string()
.eq(&identity_key)
})
.count();
.map(|g| g.performance)
.unwrap_or_default()
.round_to_integer();
if count_gateways_blacklisted > 0 {
queries::write_blacklisted_gateways_to_db(&pool, gateways_blacklisted.iter())
.await
.map(|_| {
tracing::debug!(
"Gateway blacklist info written to DB! {} blacklisted by Nym API",
count_gateways_blacklisted
)
})?;
}
let mixnode_records = self.prepare_mixnode_data(
&legacy_mixnodes,
mixnodes_described,
delegation_program_members,
)?;
queries::insert_mixnodes(&pool, mixnode_records)
.await
.map(|_| {
tracing::debug!("Mixnode info written to DB!");
})?;
let count_mixnodes_blacklisted = legacy_mixnodes
.iter()
.filter(|elem| elem.blacklisted)
.count();
let recently_unbonded_gateways =
queries::ensure_gateways_still_bonded(&pool, &gateways).await?;
let recently_unbonded_mixnodes =
queries::ensure_mixnodes_still_bonded(&pool, &legacy_mixnodes).await?;
let count_bonded_mixnodes_reserve = 0; // TODO: NymAPI doesn't report the reserve set size
let count_bonded_mixnodes_inactive = count_bonded_mixnodes - count_bonded_mixnodes_active;
let (all_historical_gateways, all_historical_mixnodes) = calculate_stats(&pool).await?;
//
// write summary keys and values to table
//
let nodes_summary = vec![
(MIXNODES_BONDED_COUNT, &count_bonded_mixnodes),
(MIXNODES_BONDED_ACTIVE, &count_bonded_mixnodes_active),
(MIXNODES_BONDED_INACTIVE, &count_bonded_mixnodes_inactive),
(MIXNODES_BONDED_RESERVE, &count_bonded_mixnodes_reserve),
(MIXNODES_BLACKLISTED_COUNT, &count_mixnodes_blacklisted),
(GATEWAYS_BONDED_COUNT, &count_bonded_gateways),
(MIXNODES_HISTORICAL_COUNT, &all_historical_mixnodes),
(GATEWAYS_HISTORICAL_COUNT, &all_historical_gateways),
(GATEWAYS_BLACKLISTED_COUNT, &count_gateways_blacklisted),
];
let last_updated = chrono::offset::Utc::now();
let last_updated_utc = last_updated.timestamp().to_string();
let network_summary = NetworkSummary {
mixnodes: mixnode::MixnodeSummary {
bonded: mixnode::MixnodeSummaryBonded {
count: count_bonded_mixnodes.cast_checked()?,
active: count_bonded_mixnodes_active.cast_checked()?,
inactive: count_bonded_mixnodes_inactive.cast_checked()?,
reserve: count_bonded_mixnodes_reserve.cast_checked()?,
last_updated_utc: last_updated_utc.to_owned(),
},
blacklisted: mixnode::MixnodeSummaryBlacklisted {
count: count_mixnodes_blacklisted.cast_checked()?,
last_updated_utc: last_updated_utc.to_owned(),
},
historical: mixnode::MixnodeSummaryHistorical {
count: all_historical_mixnodes.cast_checked()?,
last_updated_utc: last_updated_utc.to_owned(),
},
},
gateways: gateway::GatewaySummary {
bonded: gateway::GatewaySummaryBonded {
count: count_bonded_gateways.cast_checked()?,
last_updated_utc: last_updated_utc.to_owned(),
},
blacklisted: gateway::GatewaySummaryBlacklisted {
count: count_gateways_blacklisted.cast_checked()?,
last_updated_utc: last_updated_utc.to_owned(),
},
historical: gateway::GatewaySummaryHistorical {
count: all_historical_gateways.cast_checked()?,
last_updated_utc: last_updated_utc.to_owned(),
},
},
};
queries::insert_summaries(&pool, &nodes_summary, &network_summary, last_updated).await?;
let mut log_lines: Vec<String> = vec![];
for (key, value) in nodes_summary.iter() {
log_lines.push(format!("{} = {}", key, value));
}
log_lines.push(format!(
"recently_unbonded_mixnodes = {}",
recently_unbonded_mixnodes
));
log_lines.push(format!(
"recently_unbonded_gateways = {}",
recently_unbonded_gateways
));
tracing::info!("Directory summary: \n{}", log_lines.join("\n"));
Ok(())
gateway_records.push(GatewayRecord {
identity_key: identity_key.to_owned(),
bonded,
blacklisted,
self_described,
explorer_pretty_bond,
last_updated_utc,
performance,
});
}
#[instrument(level = "debug", skip_all)]
async fn location_cached(&mut self, node: &NymNodeDescription) -> Location {
let node_id = node.node_id;
Ok(gateway_records)
}
match self.geocache.get(&node_id).await {
Some(location) => return location,
None => {
for ip in node.description.host_information.ip_address.iter() {
if let Ok(location) = self.ipinfo.locate_ip(ip.to_string()).await {
self.geocache.insert(node_id, location.clone()).await;
return location;
}
}
// if no data could be retrieved
tracing::debug!("No geodata could be retrieved for {}", node_id);
Location::empty()
}
}
fn prepare_mixnode_data(
mixnodes: &[MixNodeBondAnnotated],
mixnodes_described: Vec<LegacyDescribedMixNode>,
delegation_program_members: Vec<u32>,
) -> anyhow::Result<Vec<MixnodeRecord>> {
let mut mixnode_records = Vec::new();
for mixnode in mixnodes {
let mix_id = mixnode.mix_id();
let identity_key = mixnode.identity_key();
let bonded = true;
let total_stake = decimal_to_i64(mixnode.mixnode_details.total_stake());
let blacklisted = mixnode.blacklisted;
let node_info = mixnode.mix_node();
let host = node_info.host.clone();
let http_port = node_info.http_api_port;
// Contains all the information including what's above
let full_details = serde_json::to_string(&mixnode)?;
let mixnode_described = mixnodes_described.iter().find(|m| m.bond.mix_id == mix_id);
let self_described = mixnode_described.and_then(|v| serde_json::to_string(v).ok());
let is_dp_delegatee = delegation_program_members.contains(&mix_id);
let last_updated_utc = chrono::offset::Utc::now().timestamp();
mixnode_records.push(MixnodeRecord {
mix_id,
identity_key: identity_key.to_owned(),
bonded,
total_stake,
host,
http_port,
blacklisted,
full_details,
self_described,
last_updated_utc,
is_dp_delegatee,
});
}
fn prepare_gateway_data(
&self,
gateways: &[&NymNodeDescription],
gateways_blacklisted: &HashSet<String>,
gateway_geodata: Vec<NodeGeoData>,
skimmed_gateways: Vec<SkimmedNode>,
) -> anyhow::Result<Vec<GatewayRecord>> {
let mut gateway_records = Vec::new();
Ok(mixnode_records)
}
for gateway in gateways {
let identity_key = gateway.ed25519_identity_key().to_base58_string();
let bonded = true;
let last_updated_utc = chrono::offset::Utc::now().timestamp();
let blacklisted = gateways_blacklisted.contains(&identity_key);
fn log_gw_in_explorer_not_api(
explorer: &[PrettyDetailedGatewayBond],
api_gateways: &[&NymNodeDescription],
) {
let api_gateways = api_gateways
.iter()
.map(|gw| gw.ed25519_identity_key().to_base58_string())
.collect::<HashSet<_>>();
let explorer_only = explorer
.iter()
.filter(|gw| !api_gateways.contains(&gw.gateway.identity_key.to_string()))
.collect::<Vec<_>>();
let self_described = serde_json::to_string(&gateway.description)?;
let explorer_pretty_bond = gateway_geodata
.iter()
.find(|g| g.identity_key.eq(&identity_key));
let explorer_pretty_bond =
explorer_pretty_bond.and_then(|g| serde_json::to_string(g).ok());
let performance = skimmed_gateways
.iter()
.find(|g| {
g.ed25519_identity_pubkey
.to_base58_string()
.eq(&identity_key)
})
.map(|g| g.performance)
.unwrap_or_default()
.round_to_integer();
gateway_records.push(GatewayRecord {
identity_key: identity_key.to_owned(),
bonded,
blacklisted,
self_described,
explorer_pretty_bond,
last_updated_utc,
performance,
});
}
Ok(gateway_records)
}
fn prepare_mixnode_data(
&self,
mixnodes: &[MixNodeBondAnnotated],
mixnodes_described: Vec<LegacyDescribedMixNode>,
delegation_program_members: Vec<u32>,
) -> anyhow::Result<Vec<MixnodeRecord>> {
let mut mixnode_records = Vec::new();
for mixnode in mixnodes {
let mix_id = mixnode.mix_id();
let identity_key = mixnode.identity_key();
let bonded = true;
let total_stake = decimal_to_i64(mixnode.mixnode_details.total_stake());
let blacklisted = mixnode.blacklisted;
let node_info = mixnode.mix_node();
let host = node_info.host.clone();
let http_port = node_info.http_api_port;
// Contains all the information including what's above
let full_details = serde_json::to_string(&mixnode)?;
let mixnode_described = mixnodes_described.iter().find(|m| m.bond.mix_id == mix_id);
let self_described = mixnode_described.and_then(|v| serde_json::to_string(v).ok());
let is_dp_delegatee = delegation_program_members.contains(&mix_id);
let last_updated_utc = chrono::offset::Utc::now().timestamp();
mixnode_records.push(MixnodeRecord {
mix_id,
identity_key: identity_key.to_owned(),
bonded,
total_stake,
host,
http_port,
blacklisted,
full_details,
self_described,
last_updated_utc,
is_dp_delegatee,
});
}
Ok(mixnode_records)
}
async fn check_ipinfo_bandwidth(&self) {
match self.ipinfo.check_remaining_bandwidth().await {
Ok(bandwidth) => {
tracing::info!(
"ipinfo monthly bandwidth: {}/{} spent",
bandwidth.month,
bandwidth.limit
);
}
Err(err) => {
tracing::debug!("Couldn't check ipinfo bandwidth: {}", err);
}
}
tracing::debug!(
"Gateways listed by explorer but not by Nym API: {}",
explorer_only.len()
);
for gw in explorer_only.iter() {
tracing::debug!("{}", gw.gateway.identity_key.to_string());
}
}
+57 -114
View File
@@ -27,57 +27,15 @@ fetch_and_display_ipv6() {
ipv6_address=$(ip -6 addr show "$network_device" scope global | grep inet6 | awk '{print $2}')
if [[ -z "$ipv6_address" ]]; then
echo "no global IPv6 address found on $network_device."
else
elsen
echo "IPv6 address on $network_device: $ipv6_address"
fi
}
remove_duplicate_rules() {
local interface=$1
local script_name=$(basename "$0")
if [[ -z "$interface" ]]; then
echo "error: no interface specified. please enter the interface (nymwg or nymtun0):"
read -r interface
fi
if [[ "$interface" != "nymwg" && "$interface" != "nymtun0" ]]; then
echo "error: invalid interface '$interface'. allowed values are 'nymwg' or 'nymtun0'." >&2
exit 1
fi
echo "removing duplicate rules for $interface..."
iptables-save | grep "$interface" | while read -r line; do
sudo iptables -D ${line#-A } || echo "Failed to delete rule: $line"
done
ip6tables-save | grep "$interface" | while read -r line; do
sudo ip6tables -D ${line#-A } || echo "Failed to delete rule: $line"
done
echo "duplicates removed for $interface."
echo "!!-important-!! you need to now reapply the iptables rules for $interface."
if [ "$interface" == "nymwg" ]; then
echo "run: ./$script_name apply_iptables_rules_wg"
else
echo "run: ./$script_name apply_iptables_rules"
fi
}
adjust_ip_forwarding() {
ipv6_forwarding_setting="net.ipv6.conf.all.forwarding=1"
ipv4_forwarding_setting="net.ipv4.ip_forward=1"
# remove duplicate entries for these settings from the file
sudo sed -i "/^net.ipv6.conf.all.forwarding=/d" /etc/sysctl.conf
sudo sed -i "/^net.ipv4.ip_forward=/d" /etc/sysctl.conf
echo "$ipv6_forwarding_setting" | sudo tee -a /etc/sysctl.conf
echo "$ipv4_forwarding_setting" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p /etc/sysctl.conf
echo "adjusting IP forwarding settings..."
sudo sysctl -w net.ipv6.conf.all.forwarding=1
sudo sysctl -w net.ipv4.ip_forward=1
}
apply_iptables_rules() {
@@ -85,10 +43,22 @@ apply_iptables_rules() {
echo "applying IPtables rules for $interface..."
sleep 2
# remove duplicates for IPv4
sudo iptables -D FORWARD -i "$interface" -o "$network_device" -j ACCEPT 2>/dev/null || true
sudo iptables -D FORWARD -i "$network_device" -o "$interface" -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || true
sudo iptables -t nat -D POSTROUTING -o "$network_device" -j MASQUERADE 2>/dev/null || true
# remove duplicates for IPv6
sudo ip6tables -D FORWARD -i "$interface" -o "$network_device" -j ACCEPT 2>/dev/null || true
sudo ip6tables -D FORWARD -i "$network_device" -o "$interface" -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || true
sudo ip6tables -t nat -D POSTROUTING -o "$network_device" -j MASQUERADE 2>/dev/null || true
# add new rules for IPv4
sudo iptables -t nat -A POSTROUTING -o "$network_device" -j MASQUERADE
sudo iptables -A FORWARD -i "$interface" -o "$network_device" -j ACCEPT
sudo iptables -A FORWARD -i "$network_device" -o "$interface" -m state --state RELATED,ESTABLISHED -j ACCEPT
# add new rules for IPv6
sudo ip6tables -t nat -A POSTROUTING -o "$network_device" -j MASQUERADE
sudo ip6tables -A FORWARD -i "$interface" -o "$network_device" -j ACCEPT
sudo ip6tables -A FORWARD -i "$network_device" -o "$interface" -m state --state RELATED,ESTABLISHED -j ACCEPT
@@ -97,6 +67,36 @@ apply_iptables_rules() {
sudo ip6tables-save | sudo tee /etc/iptables/rules.v6
}
apply_iptables_rules_wg() {
local interface=$wg_tunnel_interface
echo "applying IPtables rules for WireGuard ($interface)..."
sleep 2
# remove duplicates for IPv4
sudo iptables -D FORWARD -i "$interface" -o "$network_device" -j ACCEPT 2>/dev/null || true
sudo iptables -D FORWARD -i "$network_device" -o "$interface" -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || true
sudo iptables -t nat -D POSTROUTING -o "$network_device" -j MASQUERADE 2>/dev/null || true
# remove duplicates for IPv6
sudo ip6tables -D FORWARD -i "$interface" -o "$network_device" -j ACCEPT 2>/dev/null || true
sudo ip6tables -D FORWARD -i "$network_device" -o "$interface" -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || true
sudo ip6tables -t nat -D POSTROUTING -o "$network_device" -j MASQUERADE 2>/dev/null || true
# add new rules for IPv4
sudo iptables -t nat -A POSTROUTING -o "$network_device" -j MASQUERADE
sudo iptables -A FORWARD -i "$interface" -o "$network_device" -j ACCEPT
sudo iptables -A FORWARD -i "$network_device" -o "$interface" -m state --state RELATED,ESTABLISHED -j ACCEPT
# add new rules for IPv6
sudo ip6tables -t nat -A POSTROUTING -o "$network_device" -j MASQUERADE
sudo ip6tables -A FORWARD -i "$interface" -o "$network_device" -j ACCEPT
sudo ip6tables -A FORWARD -i "$network_device" -o "$interface" -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables-save | sudo tee /etc/iptables/rules.v4
sudo ip6tables-save | sudo tee /etc/iptables/rules.v6
}
check_tunnel_iptables() {
local interface=$1
echo "inspecting IPtables rules for $interface..."
@@ -133,75 +133,26 @@ perform_pings() {
joke_through_tunnel() {
local interface=$1
local green="\033[0;32m"
local reset="\033[0m"
local red="\033[0;31m"
local yellow="\033[0;33m"
sleep 1
echo
echo -e "${yellow}checking tunnel connectivity and fetching a joke for $interface...${reset}"
echo -e "${yellow}if these test succeeds, it confirms your machine can reach the outside world via IPv4 and IPv6.${reset}"
echo -e "${yellow}however, probes and external clients may experience different connectivity to your nym-node.${reset}"
ipv4_address=$(ip addr show "$interface" | awk '/inet / {print $2}' | cut -d'/' -f1)
ipv6_address=$(ip addr show "$interface" | awk '/inet6 / && $2 !~ /^fe80/ {print $2}' | cut -d'/' -f1)
echo "checking tunnel connectivity and fetching a joke for $interface..."
ipv4_address=$(ip addr show "$interface" | grep 'inet ' | awk '{print $2}' | cut -d'/' -f1)
ipv6_address=$(ip addr show "$interface" | grep 'inet6 ' | awk '{print $2}' | grep -v '^fe80:' | cut -d'/' -f1)
if [[ -z "$ipv4_address" && -z "$ipv6_address" ]]; then
echo -e "${red}no IP address found on $interface. unable to fetch a joke.${reset}"
echo -e "${red}please verify your tunnel configuration and ensure the interface is up.${reset}"
return 1
echo "no IP address found on $interface. Unable to fetch a joke."
return
fi
if [[ -n "$ipv4_address" ]]; then
echo
echo -e "------------------------------------"
echo -e "detected IPv4 address: $ipv4_address"
echo -e "testing IPv4 connectivity..."
echo
if ping -c 1 -I "$ipv4_address" google.com >/dev/null 2>&1; then
echo -e "${green}IPv4 connectivity is working. fetching a joke...${reset}"
joke=$(curl -s -H "Accept: application/json" --interface "$ipv4_address" https://icanhazdadjoke.com/ | jq -r .joke)
[[ -n "$joke" && "$joke" != "null" ]] && echo -e "${green}IPv4 joke: $joke${reset}" || echo -e "failed to fetch a joke via IPv4."
else
echo -e "${red}IPv4 connectivity is not working for $interface. verify your routing and NAT settings.${reset}"
fi
if [[ -n "$ipv4_address" ]]; then
joke=$(curl -s -H "Accept: application/json" --interface "$ipv4_address" https://icanhazdadjoke.com/ | jq -r .joke)
[[ -n "$joke" && "$joke" != "null" ]] && echo "IPv4 joke: $joke" || echo "Failed to fetch a joke via IPv4."
fi
if [[ -n "$ipv6_address" ]]; then
echo
echo -e "------------------------------------"
echo -e "detected IPv6 address: $ipv6_address"
echo -e "testing IPv6 connectivity..."
echo
if ping6 -c 1 -I "$ipv6_address" google.com >/dev/null 2>&1; then
echo -e "${green}IPv6 connectivity is working. fetching a joke...${reset}"
joke=$(curl -s -H "Accept: application/json" --interface "$ipv6_address" https://icanhazdadjoke.com/ | jq -r .joke)
[[ -n "$joke" && "$joke" != "null" ]] && echo -e "${green}IPv6 joke: $joke${reset}" || echo -e "${red}failed to fetch a joke via IPv6.${reset}"
else
echo -e "${red}IPv6 connectivity is not working for $interface. verify your routing and NAT settings.${reset}"
fi
joke=$(curl -s -H "Accept: application/json" --interface "$ipv6_address" https://icanhazdadjoke.com/ | jq -r .joke)
[[ -n "$joke" && "$joke" != "null" ]] && echo "IPv6 joke: $joke" || echo "Failed to fetch a joke via IPv6."
fi
echo -e "${green}joke fetching processes completed for $interface.${reset}"
echo -e "------------------------------------"
sleep 3
echo
echo
echo -e "${yellow}### connectivity testing recommendations ###${reset}"
echo -e "${yellow}- use the following command to test WebSocket connectivity from an external client:${reset}"
echo -e "${yellow} wscat -c wss://<your-ip-address/ hostname>:9001 ${reset}"
echo -e "${yellow}- test UDP connectivity on port 51822 (commonly used for nym wireguard) ${reset}"
echo -e "${yellow} from another machine, use tools like nc or socat to send UDP packets ${reset}"
echo -e "${yellow} echo 'test message' | nc -u <your-ip-address> 51822 ${reset}"
echo -e "${yellow}if connectivity issues persist, ensure port forwarding and firewall rules are correctly configured ${reset}"
echo
}
configure_dns_and_icmp_wg() {
echo "allowing icmp (ping)..."
sudo iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
@@ -214,7 +165,7 @@ configure_dns_and_icmp_wg() {
sudo iptables -A INPUT -p tcp --dport 53 -j ACCEPT
echo "saving iptables rules..."
sudo iptables-save >/etc/iptables/rules.v4
sudo iptables-save > /etc/iptables/rules.v4
echo "dns and icmp configuration completed."
}
@@ -256,12 +207,6 @@ joke_through_wg_tunnel)
configure_dns_and_icmp_wg)
configure_dns_and_icmp_wg
;;
adjust_ip_forwarding)
adjust_ip_forwarding
;;
remove_duplicate_rules)
remove_duplicate_rules "$2"
;;
*)
echo "Usage: $0 [command]"
echo "Commands:"
@@ -277,8 +222,6 @@ remove_duplicate_rules)
echo " joke_through_the_mixnet - Fetch a joke via nymtun0."
echo " joke_through_wg_tunnel - Fetch a joke via nymwg."
echo " configure_dns_and_icmp_wg - Allows icmp ping tests for probes alongside configuring dns"
echo " adjust_ip_forwarding - Enable IPV6 and IPV4 forwarding"
echo " remove_duplicate_rules <interface> - Remove duplicate iptables rules. Valid interfaces: nymwg, nymtun0"
exit 1
;;
esac
-3
View File
@@ -1,9 +1,6 @@
// Copyright 2023-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
// due to autogenerated code
#![allow(clippy::empty_line_after_doc_comments)]
use nym_sdk::mixnet::Recipient;
use nym_sphinx_anonymous_replies::requests::AnonymousSenderTag;
uniffi::include_scaffolding!("bindings");
+3 -3
View File
@@ -1794,9 +1794,9 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7:
sha.js "^2.4.8"
cross-spawn@^7.0.2:
version "7.0.6"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
dependencies:
path-key "^3.1.0"
shebang-command "^2.0.0"
@@ -33,7 +33,7 @@ pub(crate) struct VkShareIndex<'a> {
pub(crate) epoch_id: MultiIndex<'a, EpochId, ContractVKShare, VKShareKey<'a>>,
}
impl IndexList<ContractVKShare> for VkShareIndex<'_> {
impl<'a> IndexList<ContractVKShare> for VkShareIndex<'a> {
fn get_indexes(&'_ self) -> Box<dyn Iterator<Item = &'_ dyn Index<ContractVKShare>> + '_> {
let v: Vec<&dyn Index<ContractVKShare>> = vec![&self.epoch_id];
Box::new(v.into_iter())
@@ -87,7 +87,7 @@ pub fn query(_: Deps<'_>, _: Env, _: EmptyMessage) -> Result<QueryResponse, StdE
#[cfg_attr(not(feature = "library"), cosmwasm_std::entry_point)]
pub fn migrate(deps: DepsMut<'_>, env: Env, msg: MigrateMsg) -> Result<Response, StdError> {
// on migration immediately attempt to rewrite the storage
let threshold = (2 * msg.dealers.len() as u64).div_ceil(3);
let threshold = (2 * msg.dealers.len() as u64 + 3 - 1) / 3;
let epoch = CURRENT_EPOCH.load(deps.storage)?;
assert_eq!(0, epoch.epoch_id);
@@ -58,7 +58,7 @@ impl<'a> FakeDkgKey<'a> {
}
}
impl PemStorableKey for FakeDkgKey<'_> {
impl<'a> PemStorableKey for FakeDkgKey<'a> {
type Error = NetworkManagerError;
fn pem_type() -> &'static str {
@@ -84,7 +84,7 @@ struct DkgSkipCtx<'a> {
ecash_signers: Vec<EcashSignerWithPaths>,
}
impl ProgressCtx for DkgSkipCtx<'_> {
impl<'a> ProgressCtx for DkgSkipCtx<'a> {
fn progress_tracker(&self) -> &ProgressTracker {
&self.progress
}
@@ -138,7 +138,7 @@ impl NetworkManager {
// generate required materials
let n = api_endpoints.len();
let threshold = (2 * n).div_ceil(3);
let threshold = (2 * n + 3 - 1) / 3;
let ecash_keys = ttp_keygen(threshold as u64, n as u64)?;
@@ -24,7 +24,7 @@ struct LocalApisCtx<'a> {
signers: Vec<EcashSignerWithPaths>,
}
impl ProgressCtx for LocalApisCtx<'_> {
impl<'a> ProgressCtx for LocalApisCtx<'a> {
fn progress_tracker(&self) -> &ProgressTracker {
&self.progress
}
@@ -30,7 +30,7 @@ struct LocalClientCtx<'a> {
network: &'a LoadedNetwork,
}
impl ProgressCtx for LocalClientCtx<'_> {
impl<'a> ProgressCtx for LocalClientCtx<'a> {
fn progress_tracker(&self) -> &ProgressTracker {
&self.progress
}
@@ -30,7 +30,7 @@ struct LocalNodesCtx<'a> {
gateways: Vec<NymNode>,
}
impl ProgressCtx for LocalNodesCtx<'_> {
impl<'a> ProgressCtx for LocalNodesCtx<'a> {
fn progress_tracker(&self) -> &ProgressTracker {
&self.progress
}