Compare commits

..

24 Commits

Author SHA1 Message Date
benedetta davico 8d5a41a790 Merge pull request #5277 from nymtech/feature/modify_changelog
Modify CHANGELOG
2024-12-18 11:07:49 +01:00
Bogdan-Ștefan Neacşu 039b05cf7e Modify CHANGELOG 2024-12-17 18:59:49 +02:00
benedetta davico 37b10b59aa update changelog for nym-node v1.2.1 2024-12-17 17:54:18 +01:00
benedetta davico a9ede22bbd update nym-node version 2024-12-17 17:41:12 +01:00
Bogdan-Ștefan Neacşu b656003306 Expect that previously regitrated clients don't have v6 addr 2024-12-17 16:59:01 +02:00
benedettadavico a491e6a71a update changelog for crunch 2024-12-11 10:28:47 +01:00
Jędrzej Stuczyński 96f99bb9e4 bugfix: added explicit openapi servers to account for route prefixes (#5237) 2024-12-10 10:37:04 +00:00
benedettadavico 10933ff8f1 update node version to 1.2.0 again 2024-12-09 16:58:55 +01:00
Jędrzej Stuczyński 5454b36022 Further config score adjustments (#5225)
* wip

* changed minor/patch weights and introduced full release chain history for more accurate calculations

* clippy

* updated contract schema

* added nym-api endpoint for current rewarded set nodes

* added nym-api endpoint for internal config score data

* guard mixnet contract against decreasing semver

* fixed config score calculation if there are skipped versions
2024-12-09 14:33:34 +00:00
Jędrzej Stuczyński 675e5a0305 removed semver filtering (#5224) 2024-12-06 17:21:21 +00:00
Tommy Verrall 210cc5286e Update Cargo.toml
amend version back to 13
2024-12-06 17:29:08 +01:00
benedettadavico d07e293cb5 amend nym-node version 2024-12-06 11:34:21 +01:00
benedettadavico 4b055a9bf0 bumping nym-node version 2024-12-05 18:13:13 +01:00
Jędrzej Stuczyński 80d1a24164 dont consider legacy nodes for rewarded set selection (#5215)
* dont consider legacy nodes for rewarded set selection

* removed dead imports
2024-12-05 16:50:34 +00:00
Jędrzej Stuczyński b481da9c55 nym-api NMv1 adjustments (#5209)
* ignore legacy nodes for test route selection and bias selection with existing score

* feature: dont keep persistent GatewayClient inside NMv1 (#5211)

* removed overly complex logic for requesting mutex permits for packet processing

* dont keep persistent gateway connections. instead make them on demand

* clippy
2024-12-05 16:18:14 +00:00
Bogdan-Ștefan Neacşu 585d752c83 Extend raw ws fd for gateway client (#5218) (#5220) 2024-12-05 17:43:43 +02:00
Jon Häggblad 6eddc913f4 Derive serialize for UserAgent (#5210) (#5217) 2024-12-05 11:34:44 +01:00
Mark Sinclair 15c3012199 explorer-api: add nym node endpoints + UI to show nym-nodes and account balances (#5183)
* explorer-api: add nym node endpoints + UI to show nym-nodes and account balances

* explorer-api: add endpoints to get operator rewards
explorer-ui: show delegations on nym-nodes, show operator rewards, bug fixes

* explorer-ui: change summary screen to only show nym-node stats

* explorer-api: add unstable routes to get legacy mixnodes and gateways from the contract instead of the Nym API
explorer-ui: adapt front-end to show less information in legacy nodes with plain bond types

* explorer-ui: fix up source of legacy mixnode data

* explorer-ui: add more account page null and undefined checks

* explorer-ui: filter out null gateway versions

* explorer-ui: sanitise gateway versions

* explorer-ui: add more guards on the balance parts to check that greater than 0

* explorer-api: make /tmp/unstable/gateways endpoint compatible with the current Harbour Master API

* explorer-ui: fix typo

* cargo fmt

* Add node-id, total stake and links to nodes list

---------

Co-authored-by: Mark Sinclair <mmsinclair@users.noreply.github.com>
Co-authored-by: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
2024-12-05 08:17:30 +00:00
Jędrzej Stuczyński 78bf413e6a introduce UNSTABLE endpoints for returning network monitor run details (#5214) 2024-12-04 16:49:26 +00:00
Jędrzej Stuczyński 29ea4623c8 adjusted config score penalty calculation (#5206)
* adjusted config score penalty calculation

* updated contract schema
2024-12-03 11:24:46 +00:00
Bogdan-Ștefan Neacşu 60c21a8d1d Fix backwards compat mac generation (#5202) 2024-12-02 19:52:59 +02:00
Jędrzej Stuczyński feefde9022 Bugfix/credential proxy sequencing (#5187)
* using common middleware for all http servers

* improved span handling in credential-proxy

* ensure increase in sequence number upon making deposit

* added explicit connect options for the db

* fixed further instances of incorrect span instrumentation

* batch deposit requests together to improve concurrency

* ignore cancelled requests

* updated credential proxy version to 0.1.4

* adjusted Dockerfile with new binary location

* log binary version on startup

* reduce default log level

* guard against unavaiable commit sha

* apply review comments: dont exit(0), instead just shutdown normally

* add skip_webhook parameter to obtain-async

* removing dead code
2024-12-02 14:52:35 +00:00
benedetta davico 645be5fa22 Update ci-build-upload-binaries.yml 2024-12-02 14:03:44 +01:00
benedetta davico ac56717b23 Update ci-build-upload-binaries.yml 2024-12-02 13:48:05 +01:00
309 changed files with 6970 additions and 4927 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: |
+147
View File
@@ -4,6 +4,153 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [2024.14-crunch-patched] (2024-12-17)
- Fixes an issue to allow previously registred clients to connect to latest nym-nodes
- Fixes compatibility issues between nym-nodes and older clients
## [2024.14-crunch] (2024-12-11)
- Merge/release/2024.14-crunch ([#5242])
- bugfix: added explicit openapi servers to account for route prefixes ([#5237])
- Further config score adjustments ([#5225])
- feature: remve any filtering on node semver ([#5224])
- Backport #5218 ([#5220])
- Derive serialize for UserAgent (#5210) ([#5217])
- dont consider legacy nodes for rewarded set selection ([#5215])
- introduce UNSTABLE endpoints for returning network monitor run details ([#5214])
- Nmv2 add debug config ([#5212])
- nym-api NMv1 adjustments ([#5209])
- adjusted config score penalty calculation ([#5206])
- Fix backwards compat mac generation ([#5202])
- merge crunch into develop ([#5199])
- Update Security disclosure email, public key and policy ([#5195])
- Guard storage access with cache ([#5193])
- chore: apply 1.84 linter suggestions ([#5192])
- improvement: make internal gateway clients use the same topology cache ([#5191])
- Bugfix/credential proxy sequencing ([#5187])
- Add monitor_run and testing_route indexes ([#5182])
- Add indexes to monitor run and testing route ([#5181])
- bugfix: fixed nym-node config migrations (again) ([#5179])
- bugfix: use default value for verloc config when deserialising missing values ([#5177])
- Remove peers with no allowed ip from storage ([#5175])
- Move two minor jobs to free tier github hosted runners ([#5169])
- Add support for DELETE to nym-http-api-client ([#5166])
- Fix env var name ([#5165])
- Add strum::EnumIter for TicketType ([#5164])
- Add export_to_env to NymNetworkDetails ([#5162])
- bugfix: correctly expose ecash-related data on nym-api ([#5155])
- fix: validator-rewarder GH job ([#5151])
- build(deps): bump cross-spawn from 7.0.3 to 7.0.6 in /testnet-faucet ([#5150])
- build(deps): bump mikefarah/yq from 4.44.3 to 4.44.5 ([#5149])
- start session collection for exit gateways ([#5148])
- add version to clientStatsReport ([#5147])
- update serde_json_path due to compilation issue ([#5144])
- chore: remove standalone legacy mixnode/gateway binaries ([#5135])
- [Product Data] Set up country reporting from vpn-client ([#5134])
- removed ci-nym-api-tests.yml which was running outdated (and broken) tests ([#5133])
- CI: reduce jobs running on cluster ([#5132])
- [DOCS/operators]: Release changes v2024.13-magura & Tokenomics pages v1.0 ([#5128])
- NS Agent auth with NS API ([#5127])
- [Product Data] Config deserialization bug fix ([#5126])
- bugfix: don't send empty BankMsg in ecash contract ([#5121])
- [Product data] Data consumption with ecash ticket ([#5120])
- feat: add GH workflow for nym-validator-rewarder ([#5119])
- feat: add Dockerfile and add env vars for clap arguments ([#5118])
- feature: config score ([#5117])
- [Product Data] Add stats reporting configuration in client config ([#5115])
- Correct IPv6 address generation ([#5113])
- feature: rewarding for ticketbook issuance ([#5112])
- Add granular log on nym-node ([#5111])
- Send mixnet packet stats using task client ([#5109])
- Expose time range ([#5108])
- [Product Data] Client-side stats collection ([#5107])
- chore: ecash contract migration to remove unused 'redemption_gateway_share' ([#5104])
- [Product Data] Better unique user count on gateways ([#5084])
- feat: add nym node GH workflow ([#5080])
- IPv6 support for wireguard ([#5059])
- Node Status API ([#5050])
- Authenticator CLI client mode ([#5044])
- Integrate nym-credential-proxy into workspace ([#5027])
- [Product Data] Introduce data persistence on gateways ([#5022])
- Bump the patch-updates group across 1 directory with 10 updates ([#5011])
- build(deps): bump once_cell from 1.19.0 to 1.20.2 ([#4952])
- Create TaskStatusEvent trait instead of piggybacking on Error ([#4919])
- build(deps): bump lazy_static from 1.4.0 to 1.5.0 ([#4913])
- Sync code with .env in build.rs ([#4876])
- build(deps): bump axios from 1.6.0 to 1.7.5 in /nym-api/tests ([#4790])
- Bump elliptic from 6.5.4 to 6.5.7 in /testnet-faucet ([#4768])
[#5242]: https://github.com/nymtech/nym/pull/5242
[#5237]: https://github.com/nymtech/nym/pull/5237
[#5225]: https://github.com/nymtech/nym/pull/5225
[#5224]: https://github.com/nymtech/nym/pull/5224
[#5220]: https://github.com/nymtech/nym/pull/5220
[#5217]: https://github.com/nymtech/nym/pull/5217
[#5215]: https://github.com/nymtech/nym/pull/5215
[#5214]: https://github.com/nymtech/nym/pull/5214
[#5212]: https://github.com/nymtech/nym/pull/5212
[#5209]: https://github.com/nymtech/nym/pull/5209
[#5206]: https://github.com/nymtech/nym/pull/5206
[#5202]: https://github.com/nymtech/nym/pull/5202
[#5199]: https://github.com/nymtech/nym/pull/5199
[#5195]: https://github.com/nymtech/nym/pull/5195
[#5193]: https://github.com/nymtech/nym/pull/5193
[#5192]: https://github.com/nymtech/nym/pull/5192
[#5191]: https://github.com/nymtech/nym/pull/5191
[#5187]: https://github.com/nymtech/nym/pull/5187
[#5182]: https://github.com/nymtech/nym/pull/5182
[#5181]: https://github.com/nymtech/nym/pull/5181
[#5179]: https://github.com/nymtech/nym/pull/5179
[#5177]: https://github.com/nymtech/nym/pull/5177
[#5175]: https://github.com/nymtech/nym/pull/5175
[#5169]: https://github.com/nymtech/nym/pull/5169
[#5166]: https://github.com/nymtech/nym/pull/5166
[#5165]: https://github.com/nymtech/nym/pull/5165
[#5164]: https://github.com/nymtech/nym/pull/5164
[#5162]: https://github.com/nymtech/nym/pull/5162
[#5155]: https://github.com/nymtech/nym/pull/5155
[#5151]: https://github.com/nymtech/nym/pull/5151
[#5150]: https://github.com/nymtech/nym/pull/5150
[#5149]: https://github.com/nymtech/nym/pull/5149
[#5148]: https://github.com/nymtech/nym/pull/5148
[#5147]: https://github.com/nymtech/nym/pull/5147
[#5144]: https://github.com/nymtech/nym/pull/5144
[#5135]: https://github.com/nymtech/nym/pull/5135
[#5134]: https://github.com/nymtech/nym/pull/5134
[#5133]: https://github.com/nymtech/nym/pull/5133
[#5132]: https://github.com/nymtech/nym/pull/5132
[#5128]: https://github.com/nymtech/nym/pull/5128
[#5127]: https://github.com/nymtech/nym/pull/5127
[#5126]: https://github.com/nymtech/nym/pull/5126
[#5121]: https://github.com/nymtech/nym/pull/5121
[#5120]: https://github.com/nymtech/nym/pull/5120
[#5119]: https://github.com/nymtech/nym/pull/5119
[#5118]: https://github.com/nymtech/nym/pull/5118
[#5117]: https://github.com/nymtech/nym/pull/5117
[#5115]: https://github.com/nymtech/nym/pull/5115
[#5113]: https://github.com/nymtech/nym/pull/5113
[#5112]: https://github.com/nymtech/nym/pull/5112
[#5111]: https://github.com/nymtech/nym/pull/5111
[#5109]: https://github.com/nymtech/nym/pull/5109
[#5108]: https://github.com/nymtech/nym/pull/5108
[#5107]: https://github.com/nymtech/nym/pull/5107
[#5104]: https://github.com/nymtech/nym/pull/5104
[#5084]: https://github.com/nymtech/nym/pull/5084
[#5080]: https://github.com/nymtech/nym/pull/5080
[#5059]: https://github.com/nymtech/nym/pull/5059
[#5050]: https://github.com/nymtech/nym/pull/5050
[#5044]: https://github.com/nymtech/nym/pull/5044
[#5027]: https://github.com/nymtech/nym/pull/5027
[#5022]: https://github.com/nymtech/nym/pull/5022
[#5011]: https://github.com/nymtech/nym/pull/5011
[#4952]: https://github.com/nymtech/nym/pull/4952
[#4919]: https://github.com/nymtech/nym/pull/4919
[#4913]: https://github.com/nymtech/nym/pull/4913
[#4876]: https://github.com/nymtech/nym/pull/4876
[#4790]: https://github.com/nymtech/nym/pull/4790
[#4768]: https://github.com/nymtech/nym/pull/4768
## [2024.13-magura-drift] (2024-11-29)
- Optimised syncing bandwidth information to storage
Generated
+28 -29
View File
@@ -4674,7 +4674,6 @@ dependencies = [
"opentelemetry-jaeger",
"pretty_env_logger",
"schemars",
"semver 1.0.23",
"serde",
"serde_json",
"tracing-opentelemetry",
@@ -4752,14 +4751,12 @@ dependencies = [
"nym-coconut-dkg-common",
"nym-config",
"nym-contracts-common",
"nym-credential-proxy-requests",
"nym-credential-storage",
"nym-credential-utils",
"nym-credentials",
"nym-credentials-interface",
"nym-crypto",
"nym-ecash-contract-common",
"nym-ecash-time",
"nym-id",
"nym-mixnet-contract-common",
"nym-multisig-contract-common",
@@ -5009,6 +5006,16 @@ dependencies = [
"nym-multisig-contract-common",
]
[[package]]
name = "nym-common-models"
version = "0.1.0"
dependencies = [
"anyhow",
"bincode",
"nym-crypto",
"serde",
]
[[package]]
name = "nym-compact-ecash"
version = "0.1.0"
@@ -5085,7 +5092,7 @@ dependencies = [
[[package]]
name = "nym-credential-proxy"
version = "0.1.3"
version = "0.1.6"
dependencies = [
"anyhow",
"async-trait",
@@ -5105,6 +5112,7 @@ dependencies = [
"nym-credentials",
"nym-credentials-interface",
"nym-crypto",
"nym-ecash-contract-common",
"nym-http-api-common",
"nym-network-defaults",
"nym-validator-client",
@@ -5536,7 +5544,6 @@ version = "0.1.0"
dependencies = [
"nym-credentials-interface",
"nym-sphinx",
"nym-statistics-common",
"sqlx",
"thiserror",
"time",
@@ -5613,12 +5620,15 @@ dependencies = [
"axum-client-ip",
"bytes",
"colored",
"futures",
"mime",
"serde",
"serde_json",
"serde_yaml",
"tower 0.4.13",
"tracing",
"utoipa",
"zeroize",
]
[[package]]
@@ -5943,7 +5953,7 @@ dependencies = [
[[package]]
name = "nym-node"
version = "1.1.12"
version = "1.2.0"
dependencies = [
"anyhow",
"bip39",
@@ -6049,23 +6059,30 @@ name = "nym-node-status-agent"
version = "1.0.0-rc.1"
dependencies = [
"anyhow",
"bincode",
"chrono",
"clap 4.5.20",
"nym-bin-common",
"nym-common-models",
"nym-crypto",
"nym-node-status-client",
"rand",
"reqwest 0.12.4",
"serde",
"serde_json",
"tempfile",
"tokio",
"tokio-util",
"tracing",
"tracing-subscriber",
]
[[package]]
name = "nym-node-status-api"
version = "1.0.0-rc.4"
version = "1.0.0-rc.2"
dependencies = [
"anyhow",
"axum 0.7.7",
"bincode",
"chrono",
"clap 4.5.20",
"cosmwasm-std",
@@ -6073,13 +6090,11 @@ dependencies = [
"futures-util",
"moka",
"nym-bin-common",
"nym-common-models",
"nym-crypto",
"nym-explorer-client",
"nym-network-defaults",
"nym-node-requests",
"nym-node-status-client",
"nym-serde-helpers",
"nym-statistics-common",
"nym-task",
"nym-validator-client",
"regex",
@@ -6091,7 +6106,6 @@ dependencies = [
"strum 0.26.3",
"strum_macros 0.26.4",
"thiserror",
"time",
"tokio",
"tokio-util",
"tower-http",
@@ -6103,21 +6117,6 @@ dependencies = [
"utoipauto",
]
[[package]]
name = "nym-node-status-client"
version = "0.1.0"
dependencies = [
"anyhow",
"bincode",
"chrono",
"nym-crypto",
"nym-http-api-client",
"reqwest 0.12.4",
"serde",
"serde_json",
"tracing",
]
[[package]]
name = "nym-node-tester-utils"
version = "0.1.0"
@@ -7024,9 +7023,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",
+6 -5
View File
@@ -61,6 +61,7 @@ members = [
"common/ip-packet-requests",
"common/ledger",
"common/mixnode-common",
"common/models",
"common/network-defaults",
"common/node-tester-utils",
"common/nonexhaustive-delayqueue",
@@ -125,9 +126,8 @@ members = [
"nym-node",
"nym-node/nym-node-http-api",
"nym-node/nym-node-requests",
"nym-node-status-api/nym-node-status-agent",
"nym-node-status-api/nym-node-status-api",
"nym-node-status-api/nym-node-status-client",
"nym-node-status-api",
"nym-node-status-agent",
"nym-outfox",
"nym-validator-rewarder",
"tools/echo-server",
@@ -155,6 +155,7 @@ members = [
default-members = [
"clients/native",
"clients/socks5",
"common/models",
"explorer-api",
"gateway",
"mixnode",
@@ -162,9 +163,9 @@ default-members = [
"nym-credential-proxy/nym-credential-proxy",
"nym-data-observatory",
"nym-node",
"nym-node-status-api/nym-node-status-agent",
"nym-node-status-api/nym-node-status-api",
"nym-node-status-api",
"nym-validator-rewarder",
"nym-node-status-api",
"service-providers/authenticator",
"service-providers/ip-packet-router",
"service-providers/network-requester",
+1 -28
View File
@@ -3,13 +3,10 @@
use crate::commands::try_load_current_config;
use crate::{
client::{config::Config, SocketClient},
client::SocketClient,
commands::{override_config, OverrideConfig},
error::ClientError,
};
use clap::Args;
use log::*;
use nym_bin_common::version_checker::is_minor_version_compatible;
use nym_client_core::cli_helpers::client_run::CommonClientRunArgs;
use std::error::Error;
use std::net::IpAddr;
@@ -48,36 +45,12 @@ impl From<Run> for OverrideConfig {
}
}
// this only checks compatibility between config the binary. It does not take into consideration
// network version. It might do so in the future.
fn version_check(cfg: &Config) -> bool {
let binary_version = env!("CARGO_PKG_VERSION");
let config_version = &cfg.base.client.version;
if binary_version == config_version {
true
} else {
warn!("The native-client binary has different version than what is specified in config file! {} and {}", binary_version, config_version);
if is_minor_version_compatible(binary_version, config_version) {
info!("but they are still semver compatible. However, consider running the `upgrade` command");
true
} else {
error!("and they are semver incompatible! - please run the `upgrade` command before attempting `run` again");
false
}
}
}
pub(crate) async fn execute(args: Run) -> Result<(), Box<dyn Error + Send + Sync>> {
eprintln!("Starting client {}...", args.common_args.id);
let mut config = try_load_current_config(&args.common_args.id).await?;
config = override_config(config, OverrideConfig::from(args.clone()));
if !version_check(&config) {
error!("failed the local version check");
return Err(Box::new(ClientError::FailedLocalVersionCheck));
}
SocketClient::new(config, args.common_args.custom_mixnet)
.run_socket_forever()
.await
-3
View File
@@ -17,9 +17,6 @@ pub enum ClientError {
#[error("Failed to validate the loaded config")]
ConfigValidationFailure,
#[error("Failed local version check, client and config mismatch")]
FailedLocalVersionCheck,
#[error("Attempted to start the client in invalid socket mode")]
InvalidSocketMode,
+1 -33
View File
@@ -2,14 +2,8 @@
// SPDX-License-Identifier: Apache-2.0
use crate::commands::try_load_current_config;
use crate::config::Config;
use crate::{
commands::{override_config, OverrideConfig},
error::Socks5ClientError,
};
use crate::commands::{override_config, OverrideConfig};
use clap::Args;
use log::*;
use nym_bin_common::version_checker::is_minor_version_compatible;
use nym_client_core::cli_helpers::client_run::CommonClientRunArgs;
use nym_client_core::client::base_client::storage::OnDiskPersistent;
use nym_client_core::client::topology_control::geo_aware_provider::CountryGroup;
@@ -82,38 +76,12 @@ fn validate_country_group(s: &str) -> Result<CountryGroup, String> {
}
}
// this only checks compatibility between config the binary. It does not take into consideration
// network version. It might do so in the future.
fn version_check(cfg: &Config) -> bool {
let binary_version = env!("CARGO_PKG_VERSION");
let config_version = &cfg.core.base.client.version;
if binary_version == config_version {
true
} else {
warn!(
"The socks5-client binary has different version than what is specified in config file! {binary_version} and {config_version}",
);
if is_minor_version_compatible(binary_version, config_version) {
info!("but they are still semver compatible. However, consider running the `upgrade` command");
true
} else {
error!("and they are semver incompatible! - please run the `upgrade` command before attempting `run` again");
false
}
}
}
pub(crate) async fn execute(args: Run) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
eprintln!("Starting client {}...", args.common_args.id);
let mut config = try_load_current_config(&args.common_args.id).await?;
config = override_config(config, OverrideConfig::from(args.clone()));
if !version_check(&config) {
error!("failed the local version check");
return Err(Box::new(Socks5ClientError::FailedLocalVersionCheck));
}
let storage =
OnDiskPersistent::from_paths(config.storage_paths.common_paths, &config.core.base.debug)
.await?;
-3
View File
@@ -14,9 +14,6 @@ pub enum Socks5ClientError {
#[error("Failed to validate the loaded config")]
ConfigValidationFailure,
#[error("Failed local version check, client and config mismatch")]
FailedLocalVersionCheck,
#[error("Fail to bind address")]
FailToBindAddress,
@@ -17,7 +17,7 @@ use nym_validator_client::coconut::all_ecash_api_clients;
use nym_validator_client::nym_api::EpochId;
use nym_validator_client::nyxd::contract_traits::EcashSigningClient;
use nym_validator_client::nyxd::contract_traits::{DkgQueryClient, EcashQueryClient};
use nym_validator_client::nyxd::cosmwasm_client::ToSingletonContractData;
use nym_validator_client::nyxd::cosmwasm_client::ContractResponseData;
use nym_validator_client::EcashApiClient;
use rand::rngs::OsRng;
+1 -2
View File
@@ -15,7 +15,6 @@ const-str = { workspace = true }
log = { workspace = true }
pretty_env_logger = { workspace = true }
schemars = { workspace = true, features = ["preserve_order"], optional = true }
semver.workspace = true
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true, optional = true }
@@ -44,5 +43,5 @@ tracing = [
"tracing-opentelemetry",
"opentelemetry",
]
clap = [ "dep:clap", "dep:clap_complete", "dep:clap_complete_fig" ]
clap = ["dep:clap", "dep:clap_complete", "dep:clap_complete_fig"]
models = []
-1
View File
@@ -3,7 +3,6 @@
pub mod build_information;
pub mod logging;
pub mod version_checker;
#[cfg(feature = "clap")]
pub mod completions;
@@ -1,78 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub use semver::Version;
/// Checks if the version is minor version compatible.
///
/// Checks whether given `version` is compatible with a given semantic version requirement `req`
/// according to major-minor semver rules. The semantic version requirement can be passed as a full,
/// concrete version number, because that's what we'll have in our Cargo.toml files (e.g. 0.3.2).
/// The patch number in the requirement gets dropped and replaced with a wildcard (0.3.*) as all
/// minor versions should be compatible with each other.
pub fn is_minor_version_compatible(version: &str, req: &str) -> bool {
let expected_version = match Version::parse(version) {
Ok(v) => v,
Err(_) => return false,
};
let req_version = match Version::parse(req) {
Ok(v) => v,
Err(_) => return false,
};
expected_version.major == req_version.major && expected_version.minor == req_version.minor
}
pub fn parse_version(raw_version: &str) -> Result<Version, semver::Error> {
Version::parse(raw_version)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn version_0_3_0_is_compatible_with_requirement_0_3_x() {
assert!(is_minor_version_compatible("0.3.0", "0.3.2"));
}
#[test]
fn version_0_3_1_is_compatible_with_minimum_requirement_0_3_x() {
assert!(is_minor_version_compatible("0.3.1", "0.3.2"));
}
#[test]
fn version_0_3_2_is_compatible_with_minimum_requirement_0_3_x() {
assert!(is_minor_version_compatible("0.3.2", "0.3.0"));
}
#[test]
fn version_0_2_0_is_not_compatible_with_requirement_0_3_x() {
assert!(!is_minor_version_compatible("0.2.0", "0.3.2"));
}
#[test]
fn version_0_4_0_is_not_compatible_with_requirement_0_3_x() {
assert!(!is_minor_version_compatible("0.4.0", "0.3.2"));
}
#[test]
fn version_1_3_2_is_not_compatible_with_requirement_0_3_x() {
assert!(!is_minor_version_compatible("1.3.2", "0.3.2"));
}
#[test]
fn version_0_4_0_rc_1_is_compatible_with_version_0_4_0_rc_1() {
assert!(is_minor_version_compatible("0.4.0-rc.1", "0.4.0-rc.1"));
}
#[test]
fn returns_false_on_foo_version() {
assert!(!is_minor_version_compatible("foo", "0.3.2"));
}
#[test]
fn returns_false_on_bar_version() {
assert!(!is_minor_version_compatible("0.3.2", "bar"));
}
}
@@ -514,15 +514,10 @@ where
min_gateway_performance: config_topology.minimum_gateway_performance,
},
nym_api_urls,
env!("CARGO_PKG_VERSION").to_string(),
user_agent,
)),
config::TopologyStructure::GeoAware(group_by) => {
Box::new(GeoAwareTopologyProvider::new(
nym_api_urls,
env!("CARGO_PKG_VERSION").to_string(),
group_by,
))
Box::new(GeoAwareTopologyProvider::new(nym_api_urls, group_by))
}
})
}
@@ -85,15 +85,10 @@ fn check_layer_integrity(topology: NymTopology) -> Result<(), ()> {
pub struct GeoAwareTopologyProvider {
validator_client: nym_validator_client::client::NymApiClient,
filter_on: GroupBy,
client_version: String,
}
impl GeoAwareTopologyProvider {
pub fn new(
mut nym_api_urls: Vec<Url>,
client_version: String,
filter_on: GroupBy,
) -> GeoAwareTopologyProvider {
pub fn new(mut nym_api_urls: Vec<Url>, filter_on: GroupBy) -> GeoAwareTopologyProvider {
log::info!(
"Creating geo-aware topology provider with filter on {}",
filter_on
@@ -105,14 +100,13 @@ impl GeoAwareTopologyProvider {
nym_api_urls[0].clone(),
),
filter_on,
client_version,
}
}
async fn get_topology(&self) -> Option<NymTopology> {
let mixnodes = match self
.validator_client
.get_all_basic_active_mixing_assigned_nodes(Some(self.client_version.clone()))
.get_all_basic_active_mixing_assigned_nodes()
.await
{
Err(err) => {
@@ -124,7 +118,7 @@ impl GeoAwareTopologyProvider {
let gateways = match self
.validator_client
.get_all_basic_entry_assigned_nodes(Some(self.client_version.clone()))
.get_all_basic_entry_assigned_nodes()
.await
{
Err(err) => {
@@ -35,18 +35,11 @@ pub struct NymApiTopologyProvider {
validator_client: nym_validator_client::client::NymApiClient,
nym_api_urls: Vec<Url>,
client_version: String,
currently_used_api: usize,
}
impl NymApiTopologyProvider {
pub fn new(
config: Config,
mut nym_api_urls: Vec<Url>,
client_version: String,
user_agent: Option<UserAgent>,
) -> Self {
pub fn new(config: Config, mut nym_api_urls: Vec<Url>, user_agent: Option<UserAgent>) -> Self {
nym_api_urls.shuffle(&mut thread_rng());
let validator_client = if let Some(user_agent) = user_agent {
@@ -62,7 +55,6 @@ impl NymApiTopologyProvider {
config,
validator_client,
nym_api_urls,
client_version,
currently_used_api: 0,
}
}
@@ -99,7 +91,7 @@ impl NymApiTopologyProvider {
async fn get_current_compatible_topology(&mut self) -> Option<NymTopology> {
let mixnodes = match self
.validator_client
.get_all_basic_active_mixing_assigned_nodes(Some(self.client_version.clone()))
.get_all_basic_active_mixing_assigned_nodes()
.await
{
Err(err) => {
@@ -111,7 +103,7 @@ impl NymApiTopologyProvider {
let gateways = match self
.validator_client
.get_all_basic_entry_assigned_nodes(Some(self.client_version.clone()))
.get_all_basic_entry_assigned_nodes()
.await
{
Err(err) => {
+2 -4
View File
@@ -94,7 +94,7 @@ pub async fn current_gateways<R: Rng>(
log::debug!("Fetching list of gateways from: {nym_api}");
let gateways = client.get_all_basic_entry_assigned_nodes(None).await?;
let gateways = client.get_all_basic_entry_assigned_nodes().await?;
log::debug!("Found {} gateways", gateways.len());
log::trace!("Gateways: {:#?}", gateways);
@@ -121,9 +121,7 @@ pub async fn current_mixnodes<R: Rng>(
log::trace!("Fetching list of mixnodes from: {nym_api}");
let mixnodes = client
.get_all_basic_active_mixing_assigned_nodes(None)
.await?;
let mixnodes = client.get_all_basic_active_mixing_assigned_nodes().await?;
let valid_mixnodes = mixnodes
.iter()
.filter_map(|mixnode| mixnode.try_into().ok())
@@ -87,8 +87,10 @@ impl ClientBandwidth {
if remaining < 0 {
tracing::warn!("OUT OF BANDWIDTH. remaining: {remaining_bi2}");
} else {
} else if remaining < 1_000_000 {
tracing::info!("remaining bandwidth: {remaining_bi2}");
} else {
tracing::debug!("remaining bandwidth: {remaining_bi2}");
}
self.inner
@@ -139,6 +139,10 @@ impl<C, St> GatewayClient<C, St> {
self.gateway_identity
}
pub fn shared_key(&self) -> Option<Arc<SharedGatewayKey>> {
self.shared_key.clone()
}
pub fn ws_fd(&self) -> Option<RawFd> {
match &self.connection {
SocketState::Available(conn) => ws_fd(conn.as_ref()),
@@ -408,7 +412,7 @@ impl<C, St> GatewayClient<C, St> {
}
Some(_) => {
info!("the gateway is using exactly the same (or older) protocol version as we are. We're good to continue!");
debug!("the gateway is using exactly the same (or older) protocol version as we are. We're good to continue!");
Ok(())
}
}
@@ -992,24 +996,6 @@ impl<C, St> GatewayClient<C, St> {
}
Ok(())
}
#[deprecated(note = "this method does not deal with upgraded keys for legacy clients")]
pub async fn authenticate_and_start(
&mut self,
) -> Result<AuthenticationResponse, GatewayClientError>
where
C: DkgQueryClient + Send + Sync,
St: CredentialStorage,
<St as CredentialStorage>::StorageError: Send + Sync + 'static,
{
let shared_key = self.perform_initial_authentication().await?;
self.claim_initial_bandwidth().await?;
// this call is NON-blocking
self.start_listening_for_mixnet_messages()?;
Ok(shared_key)
}
}
// type alias for an ease of use
@@ -46,7 +46,8 @@ pub(crate) fn ws_fd(_conn: &WsConn) -> Option<RawFd> {
#[cfg(unix)]
match _conn.get_ref() {
MaybeTlsStream::Plain(stream) => Some(stream.as_raw_fd()),
&_ => None,
MaybeTlsStream::Rustls(tls_stream) => Some(tls_stream.as_raw_fd()),
_ => None,
}
#[cfg(not(unix))]
None
@@ -110,6 +111,11 @@ impl PartiallyDelegatedRouter {
}
};
if self.stream_return.is_canceled() {
// nothing to do, receiver has been dropped
return;
}
let return_res = match ret {
Err(err) => self.stream_return.send(Err(err)),
Ok(_) => {
@@ -19,8 +19,8 @@ use nym_api_requests::ecash::{
PartialExpirationDateSignatureResponse, VerificationKeyResponse,
};
use nym_api_requests::models::{
ApiHealthResponse, GatewayCoreStatusResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse,
NymNodeDescription, RewardEstimationResponse, StakeSaturationResponse,
ApiHealthResponse, GatewayBondAnnotated, GatewayCoreStatusResponse, MixnodeCoreStatusResponse,
MixnodeStatusResponse, NymNodeDescription, RewardEstimationResponse, StakeSaturationResponse,
};
use nym_api_requests::models::{LegacyDescribedGateway, MixNodeBondAnnotated};
use nym_api_requests::nym_nodes::SkimmedNode;
@@ -257,6 +257,13 @@ impl<C, S> Client<C, S> {
Ok(self.nym_api.get_gateways().await?)
}
#[deprecated]
pub async fn get_cached_gateways_detailed_unfiltered(
&self,
) -> Result<Vec<GatewayBondAnnotated>, ValidatorClientError> {
Ok(self.nym_api.get_gateways_detailed_unfiltered().await?)
}
// TODO: combine with NymApiClient...
pub async fn get_all_cached_described_nodes(
&self,
@@ -351,34 +358,19 @@ impl NymApiClient {
}
#[deprecated(note = "use get_all_basic_active_mixing_assigned_nodes instead")]
pub async fn get_basic_mixnodes(
&self,
semver_compatibility: Option<String>,
) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
Ok(self
.nym_api
.get_basic_mixnodes(semver_compatibility)
.await?
.nodes)
pub async fn get_basic_mixnodes(&self) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
Ok(self.nym_api.get_basic_mixnodes().await?.nodes)
}
#[deprecated(note = "use get_all_basic_entry_assigned_nodes instead")]
pub async fn get_basic_gateways(
&self,
semver_compatibility: Option<String>,
) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
Ok(self
.nym_api
.get_basic_gateways(semver_compatibility)
.await?
.nodes)
pub async fn get_basic_gateways(&self) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
Ok(self.nym_api.get_basic_gateways().await?.nodes)
}
/// retrieve basic information for nodes are capable of operating as an entry gateway
/// this includes legacy gateways and nym-nodes
pub async fn get_all_basic_entry_assigned_nodes(
&self,
semver_compatibility: Option<String>,
) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
let mut page = 0;
@@ -387,12 +379,7 @@ impl NymApiClient {
loop {
let mut res = self
.nym_api
.get_basic_entry_assigned_nodes(
semver_compatibility.clone(),
false,
Some(page),
None,
)
.get_basic_entry_assigned_nodes(false, Some(page), None)
.await?;
nodes.append(&mut res.nodes.data);
@@ -410,7 +397,6 @@ impl NymApiClient {
/// this includes legacy mixnodes and nym-nodes
pub async fn get_all_basic_active_mixing_assigned_nodes(
&self,
semver_compatibility: Option<String>,
) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
let mut page = 0;
@@ -419,12 +405,7 @@ impl NymApiClient {
loop {
let mut res = self
.nym_api
.get_basic_active_mixing_assigned_nodes(
semver_compatibility.clone(),
false,
Some(page),
None,
)
.get_basic_active_mixing_assigned_nodes(false, Some(page), None)
.await?;
nodes.append(&mut res.nodes.data);
@@ -442,7 +423,6 @@ impl NymApiClient {
/// this includes legacy mixnodes and nym-nodes
pub async fn get_all_basic_mixing_capable_nodes(
&self,
semver_compatibility: Option<String>,
) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
let mut page = 0;
@@ -451,12 +431,7 @@ impl NymApiClient {
loop {
let mut res = self
.nym_api
.get_basic_mixing_capable_nodes(
semver_compatibility.clone(),
false,
Some(page),
None,
)
.get_basic_mixing_capable_nodes(false, Some(page), None)
.await?;
nodes.append(&mut res.nodes.data);
@@ -471,10 +446,7 @@ impl NymApiClient {
}
/// retrieve basic information for all bonded nodes on the network
pub async fn get_all_basic_nodes(
&self,
semver_compatibility: Option<String>,
) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
pub async fn get_all_basic_nodes(&self) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
let mut page = 0;
let mut nodes = Vec::new();
@@ -482,7 +454,7 @@ impl NymApiClient {
loop {
let mut res = self
.nym_api
.get_basic_nodes(semver_compatibility.clone(), false, Some(page), None)
.get_basic_nodes(false, Some(page), None)
.await?;
nodes.append(&mut res.nodes.data);
@@ -102,6 +102,23 @@ pub trait NymApiClientExt: ApiClient {
.await
}
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn get_gateways_detailed_unfiltered(
&self,
) -> Result<Vec<GatewayBondAnnotated>, NymAPIError> {
self.get_json(
&[
routes::API_VERSION,
routes::STATUS,
routes::GATEWAYS,
routes::DETAILED_UNFILTERED,
],
NO_PARAMS,
)
.await
}
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn get_mixnodes_detailed_unfiltered(
@@ -188,16 +205,7 @@ pub trait NymApiClientExt: ApiClient {
#[deprecated]
#[tracing::instrument(level = "debug", skip_all)]
async fn get_basic_mixnodes(
&self,
semver_compatibility: Option<String>,
) -> Result<CachedNodesResponse<SkimmedNode>, NymAPIError> {
let params = if let Some(semver_compatibility) = &semver_compatibility {
vec![("semver_compatibility", semver_compatibility.as_str())]
} else {
vec![]
};
async fn get_basic_mixnodes(&self) -> Result<CachedNodesResponse<SkimmedNode>, NymAPIError> {
self.get_json(
&[
routes::API_VERSION,
@@ -206,23 +214,14 @@ pub trait NymApiClientExt: ApiClient {
"mixnodes",
"skimmed",
],
&params,
NO_PARAMS,
)
.await
}
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn get_basic_gateways(
&self,
semver_compatibility: Option<String>,
) -> Result<CachedNodesResponse<SkimmedNode>, NymAPIError> {
let params = if let Some(semver_compatibility) = &semver_compatibility {
vec![("semver_compatibility", semver_compatibility.as_str())]
} else {
vec![]
};
async fn get_basic_gateways(&self) -> Result<CachedNodesResponse<SkimmedNode>, NymAPIError> {
self.get_json(
&[
routes::API_VERSION,
@@ -231,7 +230,7 @@ pub trait NymApiClientExt: ApiClient {
"gateways",
"skimmed",
],
&params,
NO_PARAMS,
)
.await
}
@@ -241,17 +240,12 @@ pub trait NymApiClientExt: ApiClient {
#[instrument(level = "debug", skip(self))]
async fn get_basic_entry_assigned_nodes(
&self,
semver_compatibility: Option<String>,
no_legacy: bool,
page: Option<u32>,
per_page: Option<u32>,
) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> {
let mut params = Vec::new();
if let Some(arg) = &semver_compatibility {
params.push(("semver_compatibility", arg.clone()))
}
if no_legacy {
params.push(("no_legacy", "true".to_string()))
}
@@ -283,17 +277,12 @@ pub trait NymApiClientExt: ApiClient {
#[instrument(level = "debug", skip(self))]
async fn get_basic_active_mixing_assigned_nodes(
&self,
semver_compatibility: Option<String>,
no_legacy: bool,
page: Option<u32>,
per_page: Option<u32>,
) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> {
let mut params = Vec::new();
if let Some(arg) = &semver_compatibility {
params.push(("semver_compatibility", arg.clone()))
}
if no_legacy {
params.push(("no_legacy", "true".to_string()))
}
@@ -325,17 +314,12 @@ pub trait NymApiClientExt: ApiClient {
#[instrument(level = "debug", skip(self))]
async fn get_basic_mixing_capable_nodes(
&self,
semver_compatibility: Option<String>,
no_legacy: bool,
page: Option<u32>,
per_page: Option<u32>,
) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> {
let mut params = Vec::new();
if let Some(arg) = &semver_compatibility {
params.push(("semver_compatibility", arg.clone()))
}
if no_legacy {
params.push(("no_legacy", "true".to_string()))
}
@@ -365,17 +349,12 @@ pub trait NymApiClientExt: ApiClient {
#[instrument(level = "debug", skip(self))]
async fn get_basic_nodes(
&self,
semver_compatibility: Option<String>,
no_legacy: bool,
page: Option<u32>,
per_page: Option<u32>,
) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> {
let mut params = Vec::new();
if let Some(arg) = &semver_compatibility {
params.push(("semver_compatibility", arg.clone()))
}
if no_legacy {
params.push(("no_legacy", "true".to_string()))
}
@@ -26,10 +26,11 @@ use nym_mixnet_contract_common::{
reward_params::{Performance, RewardingParams},
rewarding::{EstimatedCurrentEpochRewardResponse, PendingRewardResponse},
ContractBuildInformation, ContractState, ContractStateParams, CurrentIntervalResponse,
Delegation, EpochEventId, EpochStatus, GatewayBond, GatewayBondResponse,
GatewayOwnershipResponse, IdentityKey, IdentityKeyRef, IntervalEventId, MixNodeBond,
MixNodeDetails, MixOwnershipResponse, MixnodeDetailsByIdentityResponse, MixnodeDetailsResponse,
NodeId, NumberOfPendingEventsResponse, NymNodeBond, NymNodeDetails,
CurrentNymNodeVersionResponse, Delegation, EpochEventId, EpochStatus, GatewayBond,
GatewayBondResponse, GatewayOwnershipResponse, HistoricalNymNodeVersionEntry, IdentityKey,
IdentityKeyRef, IntervalEventId, MixNodeBond, MixNodeDetails, MixOwnershipResponse,
MixnodeDetailsByIdentityResponse, MixnodeDetailsResponse, NodeId,
NumberOfPendingEventsResponse, NymNodeBond, NymNodeDetails, NymNodeVersionHistoryResponse,
PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse, PagedGatewayResponse,
PagedMixnodeBondsResponse, PagedNodeDelegationsResponse, PendingEpochEvent,
PendingEpochEventResponse, PendingEpochEventsResponse, PendingIntervalEvent,
@@ -71,6 +72,22 @@ pub trait MixnetQueryClient {
.await
}
async fn get_nym_node_version_history_paged(
&self,
start_after: Option<u32>,
limit: Option<u32>,
) -> Result<NymNodeVersionHistoryResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetNymNodeVersionHistory { limit, start_after })
.await
}
async fn get_current_nym_node_version(
&self,
) -> Result<CurrentNymNodeVersionResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetCurrentNymNodeVersion {})
.await
}
async fn get_mixnet_contract_state(&self) -> Result<ContractState, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetState {})
.await
@@ -638,6 +655,12 @@ pub trait PagedMixnetQueryClient: MixnetQueryClient {
) -> Result<Vec<PendingIntervalEvent>, NyxdError> {
collect_paged!(self, get_pending_interval_events_paged, events)
}
async fn get_full_nym_node_version_history(
&self,
) -> Result<Vec<HistoricalNymNodeVersionEntry>, NyxdError> {
collect_paged!(self, get_nym_node_version_history_paged, history)
}
}
#[async_trait]
@@ -724,6 +747,7 @@ where
mod tests {
use super::*;
use crate::nyxd::contract_traits::tests::IgnoreValue;
use nym_mixnet_contract_common::QueryMsg;
// it's enough that this compiles and clippy is happy about it
#[allow(dead_code)]
@@ -924,6 +948,10 @@ mod tests {
MixnetQueryMsg::GetRewardedSetMetadata {} => {
client.get_rewarded_set_metadata().ignore()
}
QueryMsg::GetCurrentNymNodeVersion {} => client.get_current_nym_node_version().ignore(),
QueryMsg::GetNymNodeVersionHistory { limit, start_after } => client
.get_nym_node_version_history_paged(start_after, limit)
.ignore(),
}
}
}
@@ -13,6 +13,44 @@ use tracing::error;
pub use cosmrs::abci::MsgResponse;
pub fn parse_singleton_u32_from_contract_response(b: Vec<u8>) -> Result<u32, NyxdError> {
if b.len() != 4 {
return Err(NyxdError::MalformedResponseData {
got: b.len(),
expected: 4,
});
}
Ok(u32::from_be_bytes([b[0], b[1], b[2], b[3]]))
}
pub fn parse_singleton_u64_from_contract_response(b: Vec<u8>) -> Result<u64, NyxdError> {
if b.len() != 8 {
return Err(NyxdError::MalformedResponseData {
got: b.len(),
expected: 8,
});
}
Ok(u64::from_be_bytes([
b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
]))
}
#[derive(Debug, Clone)]
pub struct ParsedContractResponse {
pub message_index: usize,
pub response: Vec<u8>,
}
impl ParsedContractResponse {
pub fn parse_singleton_u32_contract_data(self) -> Result<u32, NyxdError> {
parse_singleton_u32_from_contract_response(self.response)
}
pub fn parse_singleton_u64_contract_data(self) -> Result<u64, NyxdError> {
parse_singleton_u64_from_contract_response(self.response)
}
}
pub fn parse_msg_responses(data: Bytes) -> Vec<MsgResponse> {
// it seems that currently, on wasmd 0.43 + tendermint-rs 0.37 + cosmrs 0.17.0-pre
// the data is left in undecoded base64 form, but I'd imagine this might change so if the decoding fails,
@@ -34,35 +72,25 @@ pub fn parse_msg_responses(data: Bytes) -> Vec<MsgResponse> {
}
// requires there's a single response message
pub trait ToSingletonContractData: Sized {
pub trait ContractResponseData: Sized {
fn parse_singleton_u32_contract_data(&self) -> Result<u32, NyxdError> {
let b = self.to_singleton_contract_data()?;
if b.len() != 4 {
return Err(NyxdError::MalformedResponseData {
got: b.len(),
expected: 4,
});
}
Ok(u32::from_be_bytes([b[0], b[1], b[2], b[3]]))
parse_singleton_u32_from_contract_response(b)
}
fn parse_singleton_u64_contract_data(&self) -> Result<u64, NyxdError> {
let b = self.to_singleton_contract_data()?;
if b.len() != 8 {
return Err(NyxdError::MalformedResponseData {
got: b.len(),
expected: 8,
});
}
Ok(u64::from_be_bytes([
b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
]))
parse_singleton_u64_from_contract_response(b)
}
fn to_singleton_contract_data(&self) -> Result<Vec<u8>, NyxdError>;
fn to_unchecked_contract_data(&self) -> Result<Vec<Vec<u8>>, NyxdError>;
fn to_contract_data(&self) -> Result<Vec<ParsedContractResponse>, NyxdError>;
}
impl ToSingletonContractData for ExecuteResult {
impl ContractResponseData for ExecuteResult {
fn to_singleton_contract_data(&self) -> Result<Vec<u8>, NyxdError> {
if self.msg_responses.len() != 1 {
return Err(NyxdError::UnexpectedNumberOfMsgResponses {
@@ -72,6 +100,30 @@ impl ToSingletonContractData for ExecuteResult {
self.msg_responses[0].to_contract_response_data()
}
fn to_unchecked_contract_data(&self) -> Result<Vec<Vec<u8>>, NyxdError> {
self.msg_responses
.iter()
.map(ToContractResponseData::to_contract_response_data)
.collect()
}
fn to_contract_data(&self) -> Result<Vec<ParsedContractResponse>, NyxdError> {
let mut response = Vec::new();
for (message_index, msg) in self.msg_responses.iter().enumerate() {
// unfortunately `Name` trait has not been derived for `MsgExecuteContractResponse`,
// so we have to make an explicit string comparison instead
if msg.type_url == "/cosmwasm.wasm.v1.MsgExecuteContractResponse" {
response.push(ParsedContractResponse {
message_index,
response: msg.to_contract_response_data()?,
})
}
}
Ok(response)
}
}
pub trait ToContractResponseData: Sized {
@@ -23,7 +23,7 @@ use tendermint_rpc::endpoint::*;
use tendermint_rpc::query::Query;
use tendermint_rpc::{Error as TendermintRpcError, Order, Paging, SimpleRequest};
pub use helpers::{ToContractResponseData, ToSingletonContractData};
pub use helpers::{ContractResponseData, ToContractResponseData};
#[cfg(feature = "http-client")]
use crate::http_client;
-2
View File
@@ -48,7 +48,6 @@ nym-vesting-contract-common = { path = "../cosmwasm-smart-contracts/vesting-cont
nym-coconut-dkg-common = { path = "../cosmwasm-smart-contracts/coconut-dkg" }
nym-multisig-contract-common = { path = "../cosmwasm-smart-contracts/multisig-contract" }
nym-ecash-contract-common = { path = "../cosmwasm-smart-contracts/ecash-contract" }
nym-ecash-time = { path = "../../common/ecash-time" }
nym-sphinx = { path = "../../common/nymsphinx" }
nym-client-core = { path = "../../common/client-core" }
nym-config = { path = "../../common/config" }
@@ -57,7 +56,6 @@ nym-credentials-interface = { path = "../../common/credentials-interface" }
nym-credential-storage = { path = "../../common/credential-storage" }
nym-credential-utils = { path = "../../common/credential-utils" }
nym-id = { path = "../nym-id" }
nym-credential-proxy-requests = { path = "../../nym-credential-proxy/nym-credential-proxy-requests" }
nym-pemstore = { path = "../../common/pemstore", version = "0.3.0" }
nym-types = { path = "../../common/types" }
@@ -1,41 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use log::trace;
use nym_credentials_interface::{generate_keypair_user, generate_keypair_user_from_seed, Base58};
use serde::{Deserialize, Serialize};
use std::io::stdout;
#[derive(Serialize, Deserialize)]
pub struct Bs58EncodedKeys {
pub secret_key: String,
pub public_key: String,
}
#[derive(Debug, Parser)]
pub struct Args {
/// Secret value that's used for deriving underlying ecash keypair
#[clap(long)]
pub(crate) bs58_encoded_client_secret: Option<String>,
}
pub fn generate_ecash_keypair(args: Args) -> anyhow::Result<()> {
trace!("args: {args:?}");
let keypair = if let Some(secret) = args.bs58_encoded_client_secret {
let seed = bs58::decode(&secret).into_vec()?;
generate_keypair_user_from_seed(&seed)
} else {
generate_keypair_user()
};
let encoded = Bs58EncodedKeys {
secret_key: keypair.secret_key().to_bs58(),
public_key: keypair.public_key().to_bs58(),
};
serde_json::to_writer_pretty(stdout(), &encoded)?;
Ok(())
}
-23
View File
@@ -1,23 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod generate_keypair;
pub mod withdrawal_request;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct InternalEcash {
#[clap(subcommand)]
pub command: InternalEcashCommands,
}
#[derive(Debug, Subcommand)]
pub enum InternalEcashCommands {
/// Generate a dummy withdrawal request
GenerateWithdrawalRequest(withdrawal_request::Args),
/// Generate dummy ecash keypair
GenerateKeypair(generate_keypair::Args),
}
@@ -1,78 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use log::trace;
use nym_credential_proxy_requests::api::v1::ticketbook::models::TicketbookRequest;
use nym_credentials_interface::{
generate_keypair_user, withdrawal_request, Base58, SecretKeyUser, TicketType,
};
use nym_ecash_time::{ecash_default_expiration_date, EcashTime};
use serde::{Deserialize, Serialize};
use std::io::stdout;
use time::macros::format_description;
use time::Date;
use zeroize::Zeroizing;
fn parse_date(raw: &str) -> Result<Date, time::error::Parse> {
let format = format_description!("[year]-[month]-[day]");
Date::parse(raw, &format)
}
#[derive(Serialize, Deserialize)]
pub struct Bs58EncodedOutput {
pub ecash_proxy_request: TicketbookRequest,
pub ecash_secret: String,
/// Needed to later unblind shares
pub ecash_request_info_bs58: String,
}
#[derive(Debug, Parser)]
pub struct Args {
/// Specify which type of ticketbook
#[clap(long, default_value_t = TicketType::V1MixnetEntry)]
pub(crate) ticketbook_type: TicketType,
/// Set expiration date for the ticketbook
#[clap(long, value_parser = parse_date, default_value_t = ecash_default_expiration_date())]
pub(crate) expiration_date: Date,
/// Provide ecash secret key (or generate a fresh one)
#[clap(long)]
pub(crate) ecash_secret_key_bs58: Option<String>,
}
pub async fn generate_withdrawal_request(args: Args) -> anyhow::Result<()> {
trace!("args: {args:?}");
let ecash_keypair = if let Some(secret_key) = args.ecash_secret_key_bs58 {
let secret_key = Zeroizing::new(bs58::decode(Zeroizing::new(secret_key)).into_vec()?);
let sk = SecretKeyUser::from_bytes(&secret_key)?;
sk.into()
} else {
generate_keypair_user()
};
let (withdrawal_request, request_info) = withdrawal_request(
ecash_keypair.secret_key(),
args.expiration_date.ecash_unix_timestamp(),
args.ticketbook_type.encode(),
)?;
let encoded = Bs58EncodedOutput {
ecash_proxy_request: TicketbookRequest {
withdrawal_request: withdrawal_request.into(),
ecash_pubkey: ecash_keypair.public_key(),
expiration_date: args.expiration_date,
ticketbook_type: args.ticketbook_type,
is_freepass_request: false,
},
ecash_secret: ecash_keypair.secret_key().to_bs58(),
ecash_request_info_bs58: request_info.to_bs58(),
};
serde_json::to_writer_pretty(stdout(), &encoded)?;
Ok(())
}
-19
View File
@@ -1,19 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod ecash;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct Internal {
#[clap(subcommand)]
pub command: InternalCommands,
}
#[derive(Debug, Subcommand)]
pub enum InternalCommands {
/// Ecash related internal commands
Ecash(ecash::InternalEcash),
}
-1
View File
@@ -3,6 +3,5 @@
pub mod context;
pub mod ecash;
pub mod internal;
pub mod utils;
pub mod validator;
@@ -0,0 +1,673 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_schema::cw_serde;
use cosmwasm_std::Decimal;
use std::cmp::Ordering;
use std::ops::{Add, Sub};
#[cw_serde]
pub struct HistoricalNymNodeVersion {
/// Version of the nym node that is going to be used for determining the version score of a node.
/// note: value stored here is pre-validated `semver::Version`
pub semver: String,
/// Block height of when this version has been added to the contract
pub introduced_at_height: u64,
/// The absolute version difference as compared against the first version introduced into the contract.
pub difference_since_genesis: TotalVersionDifference,
}
impl HistoricalNymNodeVersion {
pub fn genesis(semver: String, height: u64) -> HistoricalNymNodeVersion {
HistoricalNymNodeVersion {
semver,
introduced_at_height: height,
difference_since_genesis: Default::default(),
}
}
// SAFETY: the value stored in the contract is always valid
// if you manually construct that struct with invalid value, it's on you.
#[allow(clippy::unwrap_used)]
pub fn semver_unchecked(&self) -> semver::Version {
self.semver.parse().unwrap()
}
/// Return [`TotalVersionDifference`] for a new release version that is going to be pushed right after this one
/// this function cannot be called against 2 arbitrary versions
#[inline]
pub fn cumulative_difference_since_genesis(
&self,
new_version: &semver::Version,
) -> TotalVersionDifference {
let self_semver = self.semver_unchecked();
let mut new_absolute = self.difference_since_genesis;
if new_version.major > self_semver.major {
new_absolute.major += (new_version.major - self_semver.major) as u32
} else if new_version.minor > self_semver.minor {
new_absolute.minor += (new_version.minor - self_semver.minor) as u32
} else if new_version.patch > self_semver.patch {
new_absolute.patch += (new_version.patch - self_semver.patch) as u32
} else if new_version.pre != self_semver.pre {
new_absolute.prerelease += 1
}
new_absolute
}
pub fn relative_difference(&self, other: &Self) -> TotalVersionDifference {
if self.difference_since_genesis > other.difference_since_genesis {
self.difference_since_genesis - other.difference_since_genesis
} else {
other.difference_since_genesis - self.difference_since_genesis
}
}
pub fn difference_against_legacy(
&self,
legacy_version: &semver::Version,
) -> TotalVersionDifference {
let current = self.semver_unchecked();
let major_diff = (current.major as i64 - legacy_version.major as i64).unsigned_abs() as u32;
let minor_diff = (current.minor as i64 - legacy_version.minor as i64).unsigned_abs() as u32;
let patch_diff = (current.patch as i64 - legacy_version.patch as i64).unsigned_abs() as u32;
let prerelease_diff = if current.pre == legacy_version.pre {
0
} else {
1
};
let mut diff = TotalVersionDifference::default();
// if there's a major increase, ignore minor and patch and treat it as 0
if major_diff != 0 {
diff.major += major_diff;
return diff;
}
// if there's a minor increase, ignore patch and treat is as 0
if minor_diff != 0 {
diff.minor += minor_diff;
return diff;
}
diff.patch = patch_diff;
diff.prerelease = prerelease_diff;
diff
}
}
#[cw_serde]
#[derive(Default, Copy, PartialOrd, Ord, Eq)]
pub struct TotalVersionDifference {
pub major: u32,
pub minor: u32,
pub patch: u32,
pub prerelease: u32,
}
impl Add for TotalVersionDifference {
type Output = TotalVersionDifference;
fn add(self, rhs: TotalVersionDifference) -> Self::Output {
TotalVersionDifference {
major: self.major.add(rhs.major),
minor: self.minor.add(rhs.minor),
patch: self.patch.add(rhs.patch),
prerelease: self.prerelease.add(rhs.prerelease),
}
}
}
impl Sub for TotalVersionDifference {
type Output = TotalVersionDifference;
fn sub(self, rhs: TotalVersionDifference) -> Self::Output {
TotalVersionDifference {
major: self.major.saturating_sub(rhs.major),
minor: self.minor.saturating_sub(rhs.minor),
patch: self.patch.saturating_sub(rhs.patch),
prerelease: self.prerelease.saturating_sub(rhs.prerelease),
}
}
}
#[cw_serde]
pub struct HistoricalNymNodeVersionEntry {
/// The unique, ordered, id of this particular entry
pub id: u32,
/// Data associated with this particular version
pub version_information: HistoricalNymNodeVersion,
}
impl From<(u32, HistoricalNymNodeVersion)> for HistoricalNymNodeVersionEntry {
fn from((id, version_information): (u32, HistoricalNymNodeVersion)) -> Self {
HistoricalNymNodeVersionEntry {
id,
version_information,
}
}
}
impl PartialOrd for HistoricalNymNodeVersionEntry {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
// we only care about id for the purposes of ordering as they should have unique data
self.id.partial_cmp(&other.id)
}
}
#[cw_serde]
pub struct NymNodeVersionHistoryResponse {
pub history: Vec<HistoricalNymNodeVersionEntry>,
/// Field indicating paging information for the following queries if the caller wishes to get further entries.
pub start_next_after: Option<u32>,
}
#[cw_serde]
pub struct CurrentNymNodeVersionResponse {
pub version: Option<HistoricalNymNodeVersionEntry>,
}
#[cw_serde]
pub struct ConfigScoreParams {
/// Defines weights for calculating numbers of versions behind the current release.
pub version_weights: OutdatedVersionWeights,
/// Defines the parameters of the formula for calculating the version score
pub version_score_formula_params: VersionScoreFormulaParams,
}
/// Defines weights for calculating numbers of versions behind the current release.
#[cw_serde]
#[derive(Copy)]
pub struct OutdatedVersionWeights {
pub major: u32,
pub minor: u32,
pub patch: u32,
pub prerelease: u32,
}
fn is_one_semver_difference(this: &semver::Version, other: &semver::Version) -> bool {
let major_diff = (this.major as i64 - other.major as i64).unsigned_abs() as u32;
let minor_diff = (this.minor as i64 - other.minor as i64).unsigned_abs() as u32;
let patch_diff = (this.patch as i64 - other.patch as i64).unsigned_abs() as u32;
let prerelease_diff = if this.pre == other.pre { 0 } else { 1 };
if major_diff == 1 {
return true;
}
if major_diff == 0 && minor_diff == 1 {
return true;
}
if major_diff == 0 && minor_diff == 0 && patch_diff == 1 {
return true;
}
prerelease_diff == 1
}
impl OutdatedVersionWeights {
pub fn difference_to_versions_behind_factor(&self, diff: TotalVersionDifference) -> u32 {
diff.major * self.major
+ diff.minor * self.minor
+ diff.patch * self.patch
+ diff.prerelease * self.prerelease
}
// INVARIANT: release chain is sorted
// do NOT call this method directly from inside the contract. it's too inefficient
// it relies on some external caching.
pub fn versions_behind_factor(
&self,
node_version: &semver::Version,
release_chain: &[HistoricalNymNodeVersionEntry],
) -> u32 {
let Some(latest) = release_chain.last() else {
return 0;
};
let latest_semver = latest.version_information.semver_unchecked();
// if you're more recent than the latest, you get the benefit of the doubt, the release might have not yet been commited to the chain
// but only if you're only a single semver ahead, otherwise you get penalty equivalent of being major version behind for cheating
if node_version > &latest_semver {
return if is_one_semver_difference(node_version, &latest_semver) {
0
} else {
self.major
};
}
// find your position in the release chain, if we fail, we assume that the node comes from before the changes were introduced
// in which case we simply calculate the absolute difference between the genesis entry and add up the total difference
let version_diff = match release_chain
.iter()
.rfind(|h| &h.version_information.semver_unchecked() <= node_version)
{
Some(h) => {
// first chain entry that is smaller (or equal) to the provided node version
// now, calculate the difference to the genesis version and ultimately against the current head
let diff_since_genesis = if h.version_information.semver == node_version.to_string()
{
h.version_information.difference_since_genesis
} else {
h.version_information
.cumulative_difference_since_genesis(node_version)
};
latest.version_information.difference_since_genesis - diff_since_genesis
}
None => {
// SAFETY: since we managed to get 'last' entry, it means the release chain is not empty,
// so we must be able to obtain the first entry
#[allow(clippy::unwrap_used)]
let genesis = release_chain.first().unwrap();
let difference_from_genesis = genesis
.version_information
.difference_against_legacy(node_version);
difference_from_genesis + latest.version_information.difference_since_genesis
}
};
self.difference_to_versions_behind_factor(version_diff)
}
}
impl Default for OutdatedVersionWeights {
fn default() -> Self {
OutdatedVersionWeights {
major: 100,
minor: 10,
patch: 1,
prerelease: 1,
}
}
}
/// Given the formula of version_score = penalty ^ (versions_behind_factor ^ penalty_scaling)
/// define the relevant parameters
#[cw_serde]
#[derive(Copy)]
pub struct VersionScoreFormulaParams {
pub penalty: Decimal,
pub penalty_scaling: Decimal,
}
impl Default for VersionScoreFormulaParams {
fn default() -> Self {
#[allow(clippy::unwrap_used)]
VersionScoreFormulaParams {
penalty: "0.995".parse().unwrap(),
penalty_scaling: "1.65".parse().unwrap(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::ops::Deref;
// simple wrapper for tests
struct ReleaseChain {
inner: Vec<HistoricalNymNodeVersionEntry>,
}
impl Deref for ReleaseChain {
type Target = [HistoricalNymNodeVersionEntry];
fn deref(&self) -> &Self::Target {
self.inner.deref()
}
}
impl ReleaseChain {
fn new(initial: &str) -> Self {
ReleaseChain {
inner: vec![HistoricalNymNodeVersionEntry {
id: 0,
version_information: HistoricalNymNodeVersion {
semver: initial.to_string(),
introduced_at_height: 123,
difference_since_genesis: TotalVersionDifference::default(),
},
}],
}
}
fn with_release(mut self, raw: &str) -> Self {
self.push_new(raw);
self
}
fn push_new(&mut self, raw: &str) {
let latest = self.inner.last().unwrap();
let new_version: semver::Version = raw.parse().unwrap();
let new_absolute = latest
.version_information
.cumulative_difference_since_genesis(&new_version);
self.inner.push(HistoricalNymNodeVersionEntry {
id: latest.id + 1,
version_information: HistoricalNymNodeVersion {
semver: new_version.to_string(),
introduced_at_height: latest.version_information.introduced_at_height + 1,
difference_since_genesis: new_absolute,
},
})
}
}
#[test]
fn versions_behind_factor() {
// helper to compact the parsing
fn s(raw: &str) -> semver::Version {
raw.parse().unwrap()
}
let weights = OutdatedVersionWeights::default();
// no releases:
let res = weights.versions_behind_factor(&s("1.1.13"), &[]);
assert_eq!(0, res);
// ###############################
// single released version (1.1.13)
// ###############################
let mut release_chain = ReleaseChain::new("1.1.13");
// "legacy" versions
let res = weights.versions_behind_factor(&s("1.0.12"), &release_chain);
assert_eq!(10, res);
let res = weights.versions_behind_factor(&s("1.0.4"), &release_chain);
assert_eq!(10, res);
let res = weights.versions_behind_factor(&s("1.0.1"), &release_chain);
assert_eq!(10, res);
let res = weights.versions_behind_factor(&s("0.1.12"), &release_chain);
assert_eq!(100, res);
let res = weights.versions_behind_factor(&s("1.1.12"), &release_chain);
assert_eq!(1, res);
let res = weights.versions_behind_factor(&s("1.1.11"), &release_chain);
assert_eq!(2, res);
let res = weights.versions_behind_factor(&s("1.1.9"), &release_chain);
assert_eq!(4, res);
// current version
let res = weights.versions_behind_factor(&s("1.1.13"), &release_chain);
assert_eq!(0, res);
// "ahead" versions
let res = weights.versions_behind_factor(&s("1.1.14"), &release_chain);
assert_eq!(0, res);
let res = weights.versions_behind_factor(&s("1.2.0"), &release_chain);
assert_eq!(0, res);
let res = weights.versions_behind_factor(&s("2.0.0"), &release_chain);
assert_eq!(0, res);
// cheating ahead:
let res = weights.versions_behind_factor(&s("1.1.15"), &release_chain);
assert_eq!(100, res);
let res = weights.versions_behind_factor(&s("1.3.0"), &release_chain);
assert_eq!(100, res);
let res = weights.versions_behind_factor(&s("3.0.0"), &release_chain);
assert_eq!(100, res);
// ###############################
// small patch release chain (1.1.13 => 1.1.14 => 1.1.15 => 1.1.16)
// ###############################
release_chain.push_new("1.1.14");
release_chain.push_new("1.1.15");
release_chain.push_new("1.1.16");
// "legacy" versions
let res = weights.versions_behind_factor(&s("1.0.12"), &release_chain);
assert_eq!(13, res);
let res = weights.versions_behind_factor(&s("1.0.4"), &release_chain);
assert_eq!(13, res);
let res = weights.versions_behind_factor(&s("1.0.1"), &release_chain);
assert_eq!(13, res);
let res = weights.versions_behind_factor(&s("0.1.12"), &release_chain);
assert_eq!(103, res);
let res = weights.versions_behind_factor(&s("1.1.12"), &release_chain);
assert_eq!(4, res);
let res = weights.versions_behind_factor(&s("1.1.11"), &release_chain);
assert_eq!(5, res);
let res = weights.versions_behind_factor(&s("1.1.9"), &release_chain);
assert_eq!(7, res);
// current version
let res = weights.versions_behind_factor(&s("1.1.16"), &release_chain);
assert_eq!(0, res);
// present in the chain
let res = weights.versions_behind_factor(&s("1.1.15"), &release_chain);
assert_eq!(1, res);
let res = weights.versions_behind_factor(&s("1.1.14"), &release_chain);
assert_eq!(2, res);
let res = weights.versions_behind_factor(&s("1.1.13"), &release_chain);
assert_eq!(3, res);
// "ahead" versions
let res = weights.versions_behind_factor(&s("1.1.17"), &release_chain);
assert_eq!(0, res);
let res = weights.versions_behind_factor(&s("1.2.0"), &release_chain);
assert_eq!(0, res);
let res = weights.versions_behind_factor(&s("2.0.0"), &release_chain);
assert_eq!(0, res);
// cheating ahead:
let res = weights.versions_behind_factor(&s("1.1.18"), &release_chain);
assert_eq!(100, res);
let res = weights.versions_behind_factor(&s("1.3.0"), &release_chain);
assert_eq!(100, res);
let res = weights.versions_behind_factor(&s("3.0.0"), &release_chain);
assert_eq!(100, res);
// ###############################
// small minor release chain (1.2.0 => 1.3.0 => 1.4.0)
// ###############################
let release_chain = ReleaseChain::new("1.2.0")
.with_release("1.3.0")
.with_release("1.4.0");
// "legacy" versions
let res = weights.versions_behind_factor(&s("1.0.12"), &release_chain);
assert_eq!(40, res);
let res = weights.versions_behind_factor(&s("1.0.4"), &release_chain);
assert_eq!(40, res);
let res = weights.versions_behind_factor(&s("1.0.1"), &release_chain);
assert_eq!(40, res);
let res = weights.versions_behind_factor(&s("0.1.12"), &release_chain);
assert_eq!(120, res);
let res = weights.versions_behind_factor(&s("1.1.12"), &release_chain);
assert_eq!(30, res);
let res = weights.versions_behind_factor(&s("1.1.11"), &release_chain);
assert_eq!(30, res);
let res = weights.versions_behind_factor(&s("1.1.9"), &release_chain);
assert_eq!(30, res);
// current version
let res = weights.versions_behind_factor(&s("1.4.0"), &release_chain);
assert_eq!(0, res);
// present in the chain
let res = weights.versions_behind_factor(&s("1.2.0"), &release_chain);
assert_eq!(20, res);
let res = weights.versions_behind_factor(&s("1.3.0"), &release_chain);
assert_eq!(10, res);
// weird in between
let res = weights.versions_behind_factor(&s("1.2.1"), &release_chain);
assert_eq!(20, res);
let res = weights.versions_behind_factor(&s("1.3.3"), &release_chain);
assert_eq!(10, res);
// "ahead" versions
let res = weights.versions_behind_factor(&s("1.4.1"), &release_chain);
assert_eq!(0, res);
let res = weights.versions_behind_factor(&s("1.5.0"), &release_chain);
assert_eq!(0, res);
let res = weights.versions_behind_factor(&s("2.0.0"), &release_chain);
assert_eq!(0, res);
// cheating ahead:
let res = weights.versions_behind_factor(&s("1.4.2"), &release_chain);
assert_eq!(100, res);
let res = weights.versions_behind_factor(&s("1.6.0"), &release_chain);
assert_eq!(100, res);
let res = weights.versions_behind_factor(&s("3.0.0"), &release_chain);
assert_eq!(100, res);
// ###############################
// mixed release chain (1.1.13 => 1.2.0 => 1.2.1 => 1.3.0 => 1.3.1 => 1.3.2 => 1.4.0)
// ###############################
let release_chain = ReleaseChain::new("1.1.13")
.with_release("1.2.0")
.with_release("1.2.1")
.with_release("1.3.0")
.with_release("1.3.1-importantpre")
.with_release("1.3.1")
.with_release("1.3.2")
.with_release("1.4.0");
// "legacy" versions
let res = weights.versions_behind_factor(&s("1.0.12"), &release_chain);
assert_eq!(44, res);
let res = weights.versions_behind_factor(&s("1.0.4"), &release_chain);
assert_eq!(44, res);
let res = weights.versions_behind_factor(&s("1.0.1"), &release_chain);
assert_eq!(44, res);
let res = weights.versions_behind_factor(&s("0.1.12"), &release_chain);
assert_eq!(134, res);
let res = weights.versions_behind_factor(&s("1.1.12"), &release_chain);
assert_eq!(35, res);
let res = weights.versions_behind_factor(&s("1.1.11"), &release_chain);
assert_eq!(36, res);
let res = weights.versions_behind_factor(&s("1.1.9"), &release_chain);
assert_eq!(38, res);
// current version
let res = weights.versions_behind_factor(&s("1.4.0"), &release_chain);
assert_eq!(0, res);
// present in the chain
let res = weights.versions_behind_factor(&s("1.1.13"), &release_chain);
assert_eq!(34, res);
let res = weights.versions_behind_factor(&s("1.2.0"), &release_chain);
assert_eq!(24, res);
let res = weights.versions_behind_factor(&s("1.2.1"), &release_chain);
assert_eq!(23, res);
let res = weights.versions_behind_factor(&s("1.3.0"), &release_chain);
assert_eq!(13, res);
let res = weights.versions_behind_factor(&s("1.3.1-importantpre"), &release_chain);
assert_eq!(12, res);
let res = weights.versions_behind_factor(&s("1.3.1"), &release_chain);
assert_eq!(11, res);
let res = weights.versions_behind_factor(&s("1.3.2"), &release_chain);
assert_eq!(10, res);
// weird in between
let res = weights.versions_behind_factor(&s("1.2.3"), &release_chain);
assert_eq!(21, res);
let res = weights.versions_behind_factor(&s("1.3.69"), &release_chain);
assert_eq!(10, res);
// "ahead" versions
let res = weights.versions_behind_factor(&s("1.4.1"), &release_chain);
assert_eq!(0, res);
let res = weights.versions_behind_factor(&s("1.5.0"), &release_chain);
assert_eq!(0, res);
let res = weights.versions_behind_factor(&s("2.0.0"), &release_chain);
assert_eq!(0, res);
// cheating ahead:
let res = weights.versions_behind_factor(&s("1.4.2"), &release_chain);
assert_eq!(100, res);
let res = weights.versions_behind_factor(&s("1.6.0"), &release_chain);
assert_eq!(100, res);
let res = weights.versions_behind_factor(&s("3.0.0"), &release_chain);
assert_eq!(100, res);
// ###############################
// skipped patch chain (1.1.13 => 1.2.0 => 1.2.1 => 1.2.4 => [1.3.0])
// ###############################
let mut release_chain = ReleaseChain::new("1.1.13")
.with_release("1.2.0")
.with_release("1.2.1")
.with_release("1.2.4");
// current
let res = weights.versions_behind_factor(&s("1.2.4"), &release_chain);
assert_eq!(0, res);
// on 'skipped' version
let res = weights.versions_behind_factor(&s("1.2.2"), &release_chain);
assert_eq!(2, res);
// on version before the skip
let res = weights.versions_behind_factor(&s("1.2.1"), &release_chain);
assert_eq!(3, res);
release_chain.push_new("1.3.0");
// current
let res = weights.versions_behind_factor(&s("1.3.0"), &release_chain);
assert_eq!(0, res);
// on 'skipped' version
let res = weights.versions_behind_factor(&s("1.2.2"), &release_chain);
assert_eq!(12, res);
// on version before the skip
let res = weights.versions_behind_factor(&s("1.2.1"), &release_chain);
assert_eq!(13, res);
// ###############################
// skipped minor chain (1.1.13 => 1.2.0 => 1.2.1 => 1.4.0 => [1.5.0])
// ###############################
let mut release_chain = ReleaseChain::new("1.1.13")
.with_release("1.2.0")
.with_release("1.2.1")
.with_release("1.4.0");
// current
let res = weights.versions_behind_factor(&s("1.4.0"), &release_chain);
assert_eq!(0, res);
// on 'skipped' version
let res = weights.versions_behind_factor(&s("1.3.0"), &release_chain);
assert_eq!(10, res);
// on version before the skip
let res = weights.versions_behind_factor(&s("1.2.1"), &release_chain);
assert_eq!(20, res);
let res = weights.versions_behind_factor(&s("1.2.0"), &release_chain);
assert_eq!(21, res);
release_chain.push_new("1.5.0");
// current
let res = weights.versions_behind_factor(&s("1.5.0"), &release_chain);
assert_eq!(0, res);
let res = weights.versions_behind_factor(&s("1.4.0"), &release_chain);
assert_eq!(10, res);
// on 'skipped' version
let res = weights.versions_behind_factor(&s("1.3.0"), &release_chain);
assert_eq!(20, res);
// on version before the skip
let res = weights.versions_behind_factor(&s("1.2.1"), &release_chain);
assert_eq!(30, res);
let res = weights.versions_behind_factor(&s("1.2.0"), &release_chain);
assert_eq!(31, res);
}
}
@@ -275,6 +275,9 @@ pub enum MixnetContractError {
#[error("the provided nym-node version is not a valid semver. got: {provided}")]
InvalidNymNodeSemver { provided: String },
#[error("the provided nym-node version is not greater than the current one. got: {provided}. current: {current}")]
NonIncreasingSemver { provided: String, current: String },
}
impl MixnetContractError {
@@ -141,6 +141,7 @@ pub const NEW_INTERVAL_OPERATING_COST_RANGE_KEY: &str = "new_interval_operating_
pub const NEW_VERSION_WEIGHTS_RANGE_KEY: &str = "new_version_weights_range";
pub const NEW_VERSION_SCORE_FORMULA_PARAMS_KEY: &str = "new_version_score_formula_params";
pub const NYM_NODE_CURRENT_SEMVER_KEY: &str = "new_current_semver";
pub const NYM_NODE_CURRENT_SEMVER_ID_KEY: &str = "new_current_semver_id";
pub const OLD_REWARDING_VALIDATOR_ADDRESS_KEY: &str = "old_rewarding_validator_address";
pub const NEW_REWARDING_VALIDATOR_ADDRESS_KEY: &str = "new_rewarding_validator_address";
@@ -481,12 +482,6 @@ pub fn new_settings_update_event(update: &ContractStateParamsUpdate) -> Event {
// check for config score params updates
if let Some(config_score_update) = &update.config_score_params {
if let Some(current_nym_node_semver) = &config_score_update.current_nym_node_semver {
event.attributes.push(attr(
NYM_NODE_CURRENT_SEMVER_KEY,
current_nym_node_semver.to_string(),
))
}
if let Some(version_weights) = &config_score_update.version_weights {
event.attributes.push(attr(
NEW_VERSION_WEIGHTS_RANGE_KEY,
@@ -506,9 +501,10 @@ pub fn new_settings_update_event(update: &ContractStateParamsUpdate) -> Event {
event
}
pub fn new_update_nym_node_semver_event(new_version: &str) -> Event {
pub fn new_update_nym_node_semver_event(new_version: &str, new_id: u32) -> Event {
Event::new(MixnetEventType::NymNodeSemverUpdate)
.add_attribute(NYM_NODE_CURRENT_SEMVER_KEY, new_version)
.add_attribute(NYM_NODE_CURRENT_SEMVER_ID_KEY, new_id.to_string())
}
pub fn new_not_found_node_operator_rewarding_event(interval: Interval, node_id: NodeId) -> Event {
@@ -5,6 +5,7 @@
#![warn(clippy::unwrap_used)]
#![warn(clippy::todo)]
mod config_score;
pub mod constants;
pub mod delegation;
pub mod error;
@@ -21,6 +22,7 @@ pub mod rewarding;
pub mod signing_types;
pub mod types;
pub use config_score::*;
pub use constants::*;
pub use contracts_common::types::*;
pub use cosmwasm_std::{Addr, Coin, Decimal, Fraction};
@@ -25,6 +25,7 @@ use std::time::Duration;
#[cfg(feature = "schema")]
use crate::{
config_score::{CurrentNymNodeVersionResponse, NymNodeVersionHistoryResponse},
delegation::{
NodeDelegationResponse, PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse,
PagedNodeDelegationsResponse,
@@ -423,6 +424,20 @@ pub enum QueryMsg {
#[cfg_attr(feature = "schema", returns(ContractState))]
GetState {},
/// Get the current expected version of a Nym Node.
#[cfg_attr(feature = "schema", returns(CurrentNymNodeVersionResponse))]
GetCurrentNymNodeVersion {},
/// Get the version history of Nym Node.
#[cfg_attr(feature = "schema", returns(NymNodeVersionHistoryResponse))]
GetNymNodeVersionHistory {
/// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.
limit: Option<u32>,
/// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.
start_after: Option<u32>,
},
/// Gets the current parameters used for reward calculation.
#[cfg_attr(feature = "schema", returns(RewardingParams))]
GetRewardingParams {},
@@ -1,11 +1,12 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config_score::{ConfigScoreParams, OutdatedVersionWeights, VersionScoreFormulaParams};
use crate::nym_node::Role;
use contracts_common::Percent;
use cosmwasm_schema::cw_serde;
use cosmwasm_std::Coin;
use cosmwasm_std::{Addr, Uint128};
use cosmwasm_std::{Coin, Decimal};
use std::fmt::{Display, Formatter};
// type aliases for better reasoning about available data
@@ -221,96 +222,14 @@ impl OperatorsParamsUpdate {
}
}
#[cw_serde]
pub struct ConfigScoreParams {
/// Current version of the nym node that is going to be used for determining the version score of a node.
/// note: value stored here is pre-validated `semver::Version`
pub current_nym_node_semver: String,
/// Defines weights for calculating numbers of versions behind the current release.
pub version_weights: OutdatedVersionWeights,
/// Defines the parameters of the formula for calculating the version score
pub version_score_formula_params: VersionScoreFormulaParams,
}
impl ConfigScoreParams {
// SAFETY: the value stored in the contract is always valid
#[allow(clippy::unwrap_used)]
pub fn unchecked_nym_node_version(&self) -> semver::Version {
self.current_nym_node_semver.parse().unwrap()
}
pub fn versions_behind(&self, node_semver: &semver::Version) -> u32 {
let expected = self.unchecked_nym_node_version();
let major_diff = (node_semver.major as i64 - expected.major as i64).unsigned_abs() as u32;
let minor_diff = (node_semver.minor as i64 - expected.minor as i64).unsigned_abs() as u32;
let patch_diff = (node_semver.patch as i64 - expected.patch as i64).unsigned_abs() as u32;
let prerelease_diff = if node_semver.pre == expected.pre {
0
} else {
1
};
major_diff * self.version_weights.major
+ minor_diff * self.version_weights.minor
+ patch_diff * self.version_weights.patch
+ prerelease_diff * self.version_weights.prerelease
}
}
/// Defines weights for calculating numbers of versions behind the current release.
#[cw_serde]
#[derive(Copy)]
pub struct OutdatedVersionWeights {
pub major: u32,
pub minor: u32,
pub patch: u32,
pub prerelease: u32,
}
impl Default for OutdatedVersionWeights {
fn default() -> Self {
OutdatedVersionWeights {
major: 100,
minor: 10,
patch: 1,
prerelease: 1,
}
}
}
/// Given the formula of version_score = penalty ^ (num_versions_behind ^ penalty_scaling)
/// define the relevant parameters
#[cw_serde]
#[derive(Copy)]
pub struct VersionScoreFormulaParams {
pub penalty: Decimal,
pub penalty_scaling: Decimal,
}
impl Default for VersionScoreFormulaParams {
fn default() -> Self {
#[allow(clippy::unwrap_used)]
VersionScoreFormulaParams {
penalty: "0.8".parse().unwrap(),
penalty_scaling: "2.0".parse().unwrap(),
}
}
}
#[cw_serde]
pub struct ConfigScoreParamsUpdate {
pub current_nym_node_semver: Option<String>,
pub version_weights: Option<OutdatedVersionWeights>,
pub version_score_formula_params: Option<VersionScoreFormulaParams>,
}
impl ConfigScoreParamsUpdate {
pub fn contains_updates(&self) -> bool {
self.current_nym_node_semver.is_some()
|| self.version_weights.is_some()
|| self.version_score_formula_params.is_some()
self.version_weights.is_some() || self.version_score_formula_params.is_some()
}
}
@@ -18,7 +18,7 @@ use nym_validator_client::nym_api::EpochId;
use nym_validator_client::nyxd::contract_traits::{
EcashSigningClient, MultisigQueryClient, MultisigSigningClient, PagedMultisigQueryClient,
};
use nym_validator_client::nyxd::cosmwasm_client::ToSingletonContractData;
use nym_validator_client::nyxd::cosmwasm_client::ContractResponseData;
use nym_validator_client::nyxd::cw3::Status;
use nym_validator_client::nyxd::AccountId;
use nym_validator_client::EcashApiClient;
@@ -6,6 +6,7 @@ use ed25519_dalek::{Signer, SigningKey};
pub use ed25519_dalek::{Verifier, PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, SIGNATURE_LENGTH};
use nym_pemstore::traits::{PemStorableKey, PemStorableKeyPair};
use std::fmt::{self, Debug, Display, Formatter};
use std::hash::{Hash, Hasher};
use std::str::FromStr;
use thiserror::Error;
use zeroize::{Zeroize, ZeroizeOnDrop};
@@ -122,6 +123,14 @@ impl PemStorableKeyPair for KeyPair {
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct PublicKey(ed25519_dalek::VerifyingKey);
impl Hash for PublicKey {
fn hash<H: Hasher>(&self, state: &mut H) {
// each public key has unique bytes representation which can be used
// for the hash implementation
self.to_bytes().hash(state)
}
}
impl Display for PublicKey {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&self.to_base58_string(), f)
-1
View File
@@ -22,7 +22,6 @@ tracing = { workspace = true }
nym-sphinx = { path = "../nymsphinx" }
nym-credentials-interface = { path = "../credentials-interface" }
nym-statistics-common = { path = "../statistics" }
[build-dependencies]
+1 -2
View File
@@ -2,9 +2,8 @@
// SPDX-License-Identifier: GPL-3.0-only
use error::StatsStorageError;
use models::{ActiveSession, FinishedSession, StoredFinishedSession};
use models::{ActiveSession, FinishedSession, SessionType, StoredFinishedSession};
use nym_sphinx::DestinationAddressBytes;
use nym_statistics_common::gateways::SessionType;
use sessions::SessionManager;
use sqlx::ConnectOptions;
use std::path::Path;
+36 -1
View File
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: GPL-3.0-only
use nym_credentials_interface::TicketType;
use nym_statistics_common::gateways::SessionType;
use sqlx::prelude::FromRow;
use time::{Duration, OffsetDateTime};
@@ -26,6 +25,42 @@ pub struct FinishedSession {
pub typ: SessionType,
}
#[derive(PartialEq)]
pub enum SessionType {
Vpn,
Mixnet,
Unknown,
}
impl SessionType {
pub fn to_string(&self) -> &str {
match self {
Self::Vpn => "vpn",
Self::Mixnet => "mixnet",
Self::Unknown => "unknown",
}
}
pub fn from_string(s: &str) -> Self {
match s {
"vpn" => Self::Vpn,
"mixnet" => Self::Mixnet,
_ => Self::Unknown,
}
}
}
impl From<TicketType> for SessionType {
fn from(value: TicketType) -> Self {
match value {
TicketType::V1MixnetEntry => Self::Mixnet,
TicketType::V1MixnetExit => Self::Mixnet,
TicketType::V1WireguardEntry => Self::Vpn,
TicketType::V1WireguardExit => Self::Vpn,
}
}
}
#[derive(FromRow)]
pub(crate) struct StoredActiveSession {
start_time: OffsetDateTime,
+2 -1
View File
@@ -5,8 +5,9 @@ use std::fmt;
use http::HeaderValue;
use nym_bin_common::build_information::{BinaryBuildInformation, BinaryBuildInformationOwned};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct UserAgent {
pub application: String,
pub version: String,
+3
View File
@@ -15,12 +15,15 @@ axum-client-ip.workspace = true
axum.workspace = true
bytes = { workspace = true }
colored.workspace = true
futures = { workspace = true }
mime = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
serde_yaml = { workspace = true }
tower = { workspace = true }
tracing.workspace = true
utoipa = { workspace = true, optional = true }
zeroize = { workspace = true }
[features]
utoipa = ["dep:utoipa"]
+1 -1
View File
@@ -7,7 +7,7 @@ use axum::Json;
use bytes::{BufMut, BytesMut};
use serde::{Deserialize, Serialize};
pub mod logging;
pub mod middleware;
#[derive(Debug, Clone)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
@@ -1,5 +1,5 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
// SPDX-License-Identifier: Apache-2.0
use axum::http::{header, HeaderValue, StatusCode};
use axum::response::IntoResponse;
@@ -1,5 +1,5 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
// SPDX-License-Identifier: Apache-2.0
use axum::extract::Request;
use axum::http::header::{HOST, USER_AGENT};
@@ -11,6 +11,7 @@ use colored::Colorize;
use std::time::Instant;
use tracing::info;
/// Simple logger for requests
pub async fn logger(
InsecureClientIp(addr): InsecureClientIp,
request: Request,
@@ -0,0 +1,5 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod bearer_auth;
pub mod logging;
-13
View File
@@ -6,7 +6,6 @@ use crate::verloc::sender::{PacketSender, TestedNode};
use futures::stream::FuturesUnordered;
use futures::StreamExt;
use log::*;
use nym_bin_common::version_checker::{self, parse_version};
use nym_crypto::asymmetric::identity;
use nym_network_defaults::mainnet::NYM_API;
use nym_node_http_api::state::metrics::{SharedVerlocStats, VerlocNodeResult};
@@ -30,9 +29,6 @@ pub(crate) mod measurement;
pub(crate) mod packet;
pub(crate) mod sender;
// TODO: MUST BE UPDATED BEFORE ACTUAL RELEASE!!
pub const MINIMUM_NODE_VERSION: &str = "0.10.1";
// by default all of those are overwritten by config data from mixnodes directly
const DEFAULT_VERLOC_PORT: u16 = 1790;
const DEFAULT_PACKETS_PER_NODE: usize = 100;
@@ -45,9 +41,6 @@ const DEFAULT_RETRY_TIMEOUT: Duration = Duration::from_secs(60 * 30);
#[derive(Clone, Debug)]
pub struct Config {
/// Minimum semver version of a node (gateway or mixnode) that is capable of replying to echo packets.
minimum_compatible_node_version: version_checker::Version,
/// Socket address of this node on which it will be listening for the measurement packets.
listening_address: SocketAddr,
@@ -91,11 +84,6 @@ impl ConfigBuilder {
Self::default()
}
pub fn minimum_compatible_node_version(mut self, version: version_checker::Version) -> Self {
self.0.minimum_compatible_node_version = version;
self
}
pub fn listening_address(mut self, listening_address: SocketAddr) -> Self {
self.0.listening_address = listening_address;
self
@@ -154,7 +142,6 @@ impl ConfigBuilder {
impl Default for ConfigBuilder {
fn default() -> Self {
ConfigBuilder(Config {
minimum_compatible_node_version: parse_version(MINIMUM_NODE_VERSION).unwrap(),
listening_address: format!("[::]:{DEFAULT_VERLOC_PORT}").parse().unwrap(),
packets_per_node: DEFAULT_PACKETS_PER_NODE,
packet_timeout: DEFAULT_PACKET_TIMEOUT,
+17
View File
@@ -0,0 +1,17 @@
[package]
name = "nym-common-models"
version = "0.1.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
readme.workspace = true
[dependencies]
anyhow = { workspace = true }
bincode = { workspace = true }
nym-crypto = { path = "../crypto", features = ["asymmetric", "serde"] }
serde = { workspace = true, features = ["derive"] }
+1
View File
@@ -0,0 +1 @@
pub mod ns_api;
@@ -1,9 +1,7 @@
use nym_crypto::asymmetric::ed25519::{PublicKey, Signature};
use nym_crypto::asymmetric::ed25519::{PublicKey, Signature, SignatureError};
use serde::{Deserialize, Serialize};
pub mod get_testrun {
use crate::auth::SignedRequest;
use super::*;
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Payload {
@@ -42,8 +40,6 @@ pub struct TestrunAssignment {
}
pub mod submit_results {
use crate::auth::SignedRequest;
use super::*;
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Payload {
@@ -74,3 +70,32 @@ pub mod submit_results {
}
}
}
pub trait SignedRequest {
type Payload: serde::Serialize;
fn public_key(&self) -> &PublicKey;
fn signature(&self) -> &Signature;
fn payload(&self) -> &Self::Payload;
}
pub trait VerifiableRequest: SignedRequest {
type Error: From<bincode::Error> + From<SignatureError>;
fn verify_signature(&self) -> Result<(), Self::Error> {
bincode::serialize(self.payload())
.map_err(Self::Error::from)
.and_then(|serialized| {
self.public_key()
.verify(serialized, self.signature())
.map_err(Self::Error::from)
})
}
}
impl<T> VerifiableRequest for T
where
T: SignedRequest,
{
type Error = anyhow::Error;
}
@@ -530,15 +530,6 @@ impl From<KeyPairUser> for SecretKeyUser {
}
}
impl From<SecretKeyUser> for KeyPairUser {
fn from(value: SecretKeyUser) -> Self {
KeyPairUser {
public_key: value.public_key(),
secret_key: value,
}
}
}
impl KeyPairUser {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
+1 -2
View File
@@ -1,13 +1,12 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
//! Encoding and decoding node routing information.
//! 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};
-36
View File
@@ -87,39 +87,3 @@ pub enum SessionEvent {
client: DestinationAddressBytes,
},
}
#[derive(PartialEq)]
pub enum SessionType {
Vpn,
Mixnet,
Unknown,
}
impl SessionType {
pub fn to_string(&self) -> &str {
match self {
Self::Vpn => "vpn",
Self::Mixnet => "mixnet",
Self::Unknown => "unknown",
}
}
pub fn from_string(s: &str) -> Self {
match s {
"vpn" => Self::Vpn,
"mixnet" => Self::Mixnet,
_ => Self::Unknown,
}
}
}
impl From<TicketType> for SessionType {
fn from(value: TicketType) -> Self {
match value {
TicketType::V1MixnetEntry => Self::Mixnet,
TicketType::V1MixnetExit => Self::Mixnet,
TicketType::V1WireguardEntry => Self::Vpn,
TicketType::V1WireguardExit => Self::Vpn,
}
}
}
-55
View File
@@ -1,55 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_bin_common::version_checker;
use std::collections::{BTreeMap, HashMap};
use std::hash::Hash;
pub trait Versioned: Clone {
fn version(&self) -> String;
}
pub trait VersionFilterable<T> {
#[must_use]
fn filter_by_version(&self, expected_version: &str) -> Self;
}
impl<T> VersionFilterable<T> for Vec<T>
where
T: Versioned,
{
fn filter_by_version(&self, expected_version: &str) -> Self {
self.iter()
.filter(|node| {
version_checker::is_minor_version_compatible(&node.version(), expected_version)
})
.cloned()
.collect()
}
}
impl<T, K, V> VersionFilterable<T> for HashMap<K, V>
where
K: Eq + Hash + Clone,
V: VersionFilterable<T>,
T: Versioned,
{
fn filter_by_version(&self, expected_version: &str) -> Self {
self.iter()
.map(|(k, v)| (k.clone(), v.filter_by_version(expected_version)))
.collect()
}
}
impl<T, K, V> VersionFilterable<T> for BTreeMap<K, V>
where
K: Eq + Ord + Clone,
V: VersionFilterable<T>,
T: Versioned,
{
fn filter_by_version(&self, expected_version: &str) -> Self {
self.iter()
.map(|(k, v)| (k.clone(), v.filter_by_version(expected_version)))
.collect()
}
}
+1 -8
View File
@@ -1,7 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{filter, NetworkAddress, NodeVersion};
use crate::{NetworkAddress, NodeVersion};
use nym_api_requests::nym_nodes::SkimmedNode;
use nym_crypto::asymmetric::{encryption, identity};
use nym_mixnet_contract_common::NodeId;
@@ -126,13 +126,6 @@ impl fmt::Display for LegacyNode {
}
}
impl filter::Versioned for LegacyNode {
fn version(&self) -> String {
// TODO: return semver instead
self.version.to_string()
}
}
impl<'a> From<&'a LegacyNode> for SphinxNode {
fn from(node: &'a LegacyNode) -> Self {
let node_address_bytes = NymNodeRoutingAddress::from(node.mix_host)
-15
View File
@@ -4,7 +4,6 @@
#![allow(unknown_lints)]
// clippy::to_string_trait_impl is not on stable as of 1.77
use crate::filter::VersionFilterable;
pub use error::NymTopologyError;
use log::{debug, info, warn};
use nym_api_requests::nym_nodes::{CachedNodesResponse, SkimmedNode};
@@ -25,7 +24,6 @@ use std::str::FromStr;
use ::serde::{Deserialize, Deserializer, Serialize, Serializer};
pub mod error;
pub mod filter;
pub mod gateway;
pub mod mix;
pub mod random_route_provider;
@@ -465,19 +463,6 @@ impl NymTopology {
Ok(())
}
#[must_use]
pub fn filter_system_version(&self, expected_version: &str) -> Self {
self.filter_node_versions(expected_version)
}
#[must_use]
pub fn filter_node_versions(&self, expected_mix_version: &str) -> Self {
NymTopology {
mixes: self.mixes.filter_by_version(expected_mix_version),
gateways: self.gateways.clone(),
}
}
}
#[cfg(feature = "serializable")]
+1 -8
View File
@@ -1,7 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{filter, NetworkAddress, NodeVersion};
use crate::{NetworkAddress, NodeVersion};
use nym_api_requests::nym_nodes::{NodeRole, SkimmedNode};
use nym_crypto::asymmetric::{encryption, identity};
pub use nym_mixnet_contract_common::LegacyMixLayer;
@@ -89,13 +89,6 @@ impl LegacyNode {
}
}
impl filter::Versioned for LegacyNode {
fn version(&self) -> String {
// TODO: return semver instead
self.version.to_string()
}
}
impl<'a> From<&'a LegacyNode> for SphinxNode {
fn from(node: &'a LegacyNode) -> Self {
let node_address_bytes = NymNodeRoutingAddress::from(node.mix_host)
+2 -2
View File
@@ -68,9 +68,9 @@ pub async fn current_network_topology_async(
let api_client = NymApiClient::new(url);
let mixnodes = api_client
.get_all_basic_active_mixing_assigned_nodes(None)
.get_all_basic_active_mixing_assigned_nodes()
.await?;
let gateways = api_client.get_all_basic_entry_assigned_nodes(None).await?;
let gateways = api_client.get_all_basic_entry_assigned_nodes().await?;
Ok(NymTopology::from_basic(&mixnodes, &gateways).into())
}
+275 -25
View File
@@ -60,8 +60,8 @@
},
"version_score_params": {
"default": {
"penalty": "0.8",
"penalty_scaling": "2"
"penalty": "0.995",
"penalty_scaling": "1.65"
},
"allOf": [
{
@@ -261,7 +261,7 @@
"type": "string"
},
"VersionScoreFormulaParams": {
"description": "Given the formula of version_score = penalty ^ (num_versions_behind ^ penalty_scaling) define the relevant parameters",
"description": "Given the formula of version_score = penalty ^ (versions_behind_factor ^ penalty_scaling) define the relevant parameters",
"type": "object",
"required": [
"penalty",
@@ -1308,12 +1308,6 @@
"ConfigScoreParamsUpdate": {
"type": "object",
"properties": {
"current_nym_node_semver": {
"type": [
"string",
"null"
]
},
"version_score_formula_params": {
"anyOf": [
{
@@ -1938,7 +1932,7 @@
"type": "string"
},
"VersionScoreFormulaParams": {
"description": "Given the formula of version_score = penalty ^ (num_versions_behind ^ penalty_scaling) define the relevant parameters",
"description": "Given the formula of version_score = penalty ^ (versions_behind_factor ^ penalty_scaling) define the relevant parameters",
"type": "object",
"required": [
"penalty",
@@ -2043,6 +2037,54 @@
},
"additionalProperties": false
},
{
"description": "Get the current expected version of a Nym Node.",
"type": "object",
"required": [
"get_current_nym_node_version"
],
"properties": {
"get_current_nym_node_version": {
"type": "object",
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"description": "Get the version history of Nym Node.",
"type": "object",
"required": [
"get_nym_node_version_history"
],
"properties": {
"get_nym_node_version_history": {
"type": "object",
"properties": {
"limit": {
"description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.",
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0.0
},
"start_after": {
"description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.",
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"description": "Gets the current parameters used for reward calculation.",
"type": "object",
@@ -3443,8 +3485,8 @@
},
"version_score_params": {
"default": {
"penalty": "0.8",
"penalty_scaling": "2"
"penalty": "0.995",
"penalty_scaling": "1.65"
},
"allOf": [
{
@@ -3512,7 +3554,7 @@
"additionalProperties": false
},
"VersionScoreFormulaParams": {
"description": "Given the formula of version_score = penalty ^ (num_versions_behind ^ penalty_scaling) define the relevant parameters",
"description": "Given the formula of version_score = penalty ^ (versions_behind_factor ^ penalty_scaling) define the relevant parameters",
"type": "object",
"required": [
"penalty",
@@ -4180,6 +4222,111 @@
}
}
},
"get_current_nym_node_version": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "CurrentNymNodeVersionResponse",
"type": "object",
"properties": {
"version": {
"anyOf": [
{
"$ref": "#/definitions/HistoricalNymNodeVersionEntry"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false,
"definitions": {
"HistoricalNymNodeVersion": {
"type": "object",
"required": [
"difference_since_genesis",
"introduced_at_height",
"semver"
],
"properties": {
"difference_since_genesis": {
"description": "The absolute version difference as compared against the first version introduced into the contract.",
"allOf": [
{
"$ref": "#/definitions/TotalVersionDifference"
}
]
},
"introduced_at_height": {
"description": "Block height of when this version has been added to the contract",
"type": "integer",
"format": "uint64",
"minimum": 0.0
},
"semver": {
"description": "Version of the nym node that is going to be used for determining the version score of a node. note: value stored here is pre-validated `semver::Version`",
"type": "string"
}
},
"additionalProperties": false
},
"HistoricalNymNodeVersionEntry": {
"type": "object",
"required": [
"id",
"version_information"
],
"properties": {
"id": {
"description": "The unique, ordered, id of this particular entry",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"version_information": {
"description": "Data associated with this particular version",
"allOf": [
{
"$ref": "#/definitions/HistoricalNymNodeVersion"
}
]
}
},
"additionalProperties": false
},
"TotalVersionDifference": {
"type": "object",
"required": [
"major",
"minor",
"patch",
"prerelease"
],
"properties": {
"major": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"minor": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"patch": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"prerelease": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false
}
}
},
"get_delegation_details": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "NodeDelegationResponse",
@@ -7119,6 +7266,119 @@
}
}
},
"get_nym_node_version_history": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "NymNodeVersionHistoryResponse",
"type": "object",
"required": [
"history"
],
"properties": {
"history": {
"type": "array",
"items": {
"$ref": "#/definitions/HistoricalNymNodeVersionEntry"
}
},
"start_next_after": {
"description": "Field indicating paging information for the following queries if the caller wishes to get further entries.",
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false,
"definitions": {
"HistoricalNymNodeVersion": {
"type": "object",
"required": [
"difference_since_genesis",
"introduced_at_height",
"semver"
],
"properties": {
"difference_since_genesis": {
"description": "The absolute version difference as compared against the first version introduced into the contract.",
"allOf": [
{
"$ref": "#/definitions/TotalVersionDifference"
}
]
},
"introduced_at_height": {
"description": "Block height of when this version has been added to the contract",
"type": "integer",
"format": "uint64",
"minimum": 0.0
},
"semver": {
"description": "Version of the nym node that is going to be used for determining the version score of a node. note: value stored here is pre-validated `semver::Version`",
"type": "string"
}
},
"additionalProperties": false
},
"HistoricalNymNodeVersionEntry": {
"type": "object",
"required": [
"id",
"version_information"
],
"properties": {
"id": {
"description": "The unique, ordered, id of this particular entry",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"version_information": {
"description": "Data associated with this particular version",
"allOf": [
{
"$ref": "#/definitions/HistoricalNymNodeVersion"
}
]
}
},
"additionalProperties": false
},
"TotalVersionDifference": {
"type": "object",
"required": [
"major",
"minor",
"patch",
"prerelease"
],
"properties": {
"major": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"minor": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"patch": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"prerelease": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false
}
}
},
"get_nym_nodes_detailed_paged": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "PagedNymNodeDetailsResponse",
@@ -10554,15 +10814,10 @@
"ConfigScoreParams": {
"type": "object",
"required": [
"current_nym_node_semver",
"version_score_formula_params",
"version_weights"
],
"properties": {
"current_nym_node_semver": {
"description": "Current version of the nym node that is going to be used for determining the version score of a node. note: value stored here is pre-validated `semver::Version`",
"type": "string"
},
"version_score_formula_params": {
"description": "Defines the parameters of the formula for calculating the version score",
"allOf": [
@@ -10752,7 +11007,7 @@
"type": "string"
},
"VersionScoreFormulaParams": {
"description": "Given the formula of version_score = penalty ^ (num_versions_behind ^ penalty_scaling) define the relevant parameters",
"description": "Given the formula of version_score = penalty ^ (versions_behind_factor ^ penalty_scaling) define the relevant parameters",
"type": "object",
"required": [
"penalty",
@@ -10826,15 +11081,10 @@
"ConfigScoreParams": {
"type": "object",
"required": [
"current_nym_node_semver",
"version_score_formula_params",
"version_weights"
],
"properties": {
"current_nym_node_semver": {
"description": "Current version of the nym node that is going to be used for determining the version score of a node. note: value stored here is pre-validated `semver::Version`",
"type": "string"
},
"version_score_formula_params": {
"description": "Defines the parameters of the formula for calculating the version score",
"allOf": [
@@ -10988,7 +11238,7 @@
"type": "string"
},
"VersionScoreFormulaParams": {
"description": "Given the formula of version_score = penalty ^ (num_versions_behind ^ penalty_scaling) define the relevant parameters",
"description": "Given the formula of version_score = penalty ^ (versions_behind_factor ^ penalty_scaling) define the relevant parameters",
"type": "object",
"required": [
"penalty",
+1 -7
View File
@@ -1027,12 +1027,6 @@
"ConfigScoreParamsUpdate": {
"type": "object",
"properties": {
"current_nym_node_semver": {
"type": [
"string",
"null"
]
},
"version_score_formula_params": {
"anyOf": [
{
@@ -1657,7 +1651,7 @@
"type": "string"
},
"VersionScoreFormulaParams": {
"description": "Given the formula of version_score = penalty ^ (num_versions_behind ^ penalty_scaling) define the relevant parameters",
"description": "Given the formula of version_score = penalty ^ (versions_behind_factor ^ penalty_scaling) define the relevant parameters",
"type": "object",
"required": [
"penalty",
+3 -3
View File
@@ -56,8 +56,8 @@
},
"version_score_params": {
"default": {
"penalty": "0.8",
"penalty_scaling": "2"
"penalty": "0.995",
"penalty_scaling": "1.65"
},
"allOf": [
{
@@ -257,7 +257,7 @@
"type": "string"
},
"VersionScoreFormulaParams": {
"description": "Given the formula of version_score = penalty ^ (num_versions_behind ^ penalty_scaling) define the relevant parameters",
"description": "Given the formula of version_score = penalty ^ (versions_behind_factor ^ penalty_scaling) define the relevant parameters",
"type": "object",
"required": [
"penalty",
+3 -3
View File
@@ -17,8 +17,8 @@
},
"version_score_params": {
"default": {
"penalty": "0.8",
"penalty_scaling": "2"
"penalty": "0.995",
"penalty_scaling": "1.65"
},
"allOf": [
{
@@ -86,7 +86,7 @@
"additionalProperties": false
},
"VersionScoreFormulaParams": {
"description": "Given the formula of version_score = penalty ^ (num_versions_behind ^ penalty_scaling) define the relevant parameters",
"description": "Given the formula of version_score = penalty ^ (versions_behind_factor ^ penalty_scaling) define the relevant parameters",
"type": "object",
"required": [
"penalty",
+48
View File
@@ -85,6 +85,54 @@
},
"additionalProperties": false
},
{
"description": "Get the current expected version of a Nym Node.",
"type": "object",
"required": [
"get_current_nym_node_version"
],
"properties": {
"get_current_nym_node_version": {
"type": "object",
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"description": "Get the version history of Nym Node.",
"type": "object",
"required": [
"get_nym_node_version_history"
],
"properties": {
"get_nym_node_version_history": {
"type": "object",
"properties": {
"limit": {
"description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.",
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0.0
},
"start_after": {
"description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.",
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"description": "Gets the current parameters used for reward calculation.",
"type": "object",
@@ -0,0 +1,105 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "CurrentNymNodeVersionResponse",
"type": "object",
"properties": {
"version": {
"anyOf": [
{
"$ref": "#/definitions/HistoricalNymNodeVersionEntry"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false,
"definitions": {
"HistoricalNymNodeVersion": {
"type": "object",
"required": [
"difference_since_genesis",
"introduced_at_height",
"semver"
],
"properties": {
"difference_since_genesis": {
"description": "The absolute version difference as compared against the first version introduced into the contract.",
"allOf": [
{
"$ref": "#/definitions/TotalVersionDifference"
}
]
},
"introduced_at_height": {
"description": "Block height of when this version has been added to the contract",
"type": "integer",
"format": "uint64",
"minimum": 0.0
},
"semver": {
"description": "Version of the nym node that is going to be used for determining the version score of a node. note: value stored here is pre-validated `semver::Version`",
"type": "string"
}
},
"additionalProperties": false
},
"HistoricalNymNodeVersionEntry": {
"type": "object",
"required": [
"id",
"version_information"
],
"properties": {
"id": {
"description": "The unique, ordered, id of this particular entry",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"version_information": {
"description": "Data associated with this particular version",
"allOf": [
{
"$ref": "#/definitions/HistoricalNymNodeVersion"
}
]
}
},
"additionalProperties": false
},
"TotalVersionDifference": {
"type": "object",
"required": [
"major",
"minor",
"patch",
"prerelease"
],
"properties": {
"major": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"minor": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"patch": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"prerelease": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false
}
}
}
@@ -0,0 +1,113 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "NymNodeVersionHistoryResponse",
"type": "object",
"required": [
"history"
],
"properties": {
"history": {
"type": "array",
"items": {
"$ref": "#/definitions/HistoricalNymNodeVersionEntry"
}
},
"start_next_after": {
"description": "Field indicating paging information for the following queries if the caller wishes to get further entries.",
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false,
"definitions": {
"HistoricalNymNodeVersion": {
"type": "object",
"required": [
"difference_since_genesis",
"introduced_at_height",
"semver"
],
"properties": {
"difference_since_genesis": {
"description": "The absolute version difference as compared against the first version introduced into the contract.",
"allOf": [
{
"$ref": "#/definitions/TotalVersionDifference"
}
]
},
"introduced_at_height": {
"description": "Block height of when this version has been added to the contract",
"type": "integer",
"format": "uint64",
"minimum": 0.0
},
"semver": {
"description": "Version of the nym node that is going to be used for determining the version score of a node. note: value stored here is pre-validated `semver::Version`",
"type": "string"
}
},
"additionalProperties": false
},
"HistoricalNymNodeVersionEntry": {
"type": "object",
"required": [
"id",
"version_information"
],
"properties": {
"id": {
"description": "The unique, ordered, id of this particular entry",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"version_information": {
"description": "Data associated with this particular version",
"allOf": [
{
"$ref": "#/definitions/HistoricalNymNodeVersion"
}
]
}
},
"additionalProperties": false
},
"TotalVersionDifference": {
"type": "object",
"required": [
"major",
"minor",
"patch",
"prerelease"
],
"properties": {
"major": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"minor": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"patch": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"prerelease": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false
}
}
}
@@ -76,15 +76,10 @@
"ConfigScoreParams": {
"type": "object",
"required": [
"current_nym_node_semver",
"version_score_formula_params",
"version_weights"
],
"properties": {
"current_nym_node_semver": {
"description": "Current version of the nym node that is going to be used for determining the version score of a node. note: value stored here is pre-validated `semver::Version`",
"type": "string"
},
"version_score_formula_params": {
"description": "Defines the parameters of the formula for calculating the version score",
"allOf": [
@@ -274,7 +269,7 @@
"type": "string"
},
"VersionScoreFormulaParams": {
"description": "Given the formula of version_score = penalty ^ (num_versions_behind ^ penalty_scaling) define the relevant parameters",
"description": "Given the formula of version_score = penalty ^ (versions_behind_factor ^ penalty_scaling) define the relevant parameters",
"type": "object",
"required": [
"penalty",
@@ -54,15 +54,10 @@
"ConfigScoreParams": {
"type": "object",
"required": [
"current_nym_node_semver",
"version_score_formula_params",
"version_weights"
],
"properties": {
"current_nym_node_semver": {
"description": "Current version of the nym node that is going to be used for determining the version score of a node. note: value stored here is pre-validated `semver::Version`",
"type": "string"
},
"version_score_formula_params": {
"description": "Defines the parameters of the formula for calculating the version score",
"allOf": [
@@ -216,7 +211,7 @@
"type": "string"
},
"VersionScoreFormulaParams": {
"description": "Given the formula of version_score = penalty ^ (num_versions_behind ^ penalty_scaling) define the relevant parameters",
"description": "Given the formula of version_score = penalty ^ (versions_behind_factor ^ penalty_scaling) define the relevant parameters",
"type": "object",
"required": [
"penalty",
+3
View File
@@ -71,6 +71,9 @@ pub const LAST_INTERVAL_EVENT_ID_KEY: &str = "lie";
pub const ADMIN_STORAGE_KEY: &str = "admin";
pub const CONTRACT_STATE_KEY: &str = "state";
pub const VERSION_HISTORY_ID_COUNTER_KEY: &str = "vhid";
pub const VERSION_HISTORY_NAMESPACE: &str = "vh";
pub const NYMNODE_ROLES_ASSIGNMENT_NAMESPACE: &str = "roles";
pub const NYMNODE_REWARDED_SET_METADATA_NAMESPACE: &str = "roles_metadata";
pub const NYMNODE_ACTIVE_ROLE_ASSIGNMENT_KEY: &str = "active_roles";
+20 -5
View File
@@ -48,7 +48,6 @@ fn default_initial_state(
},
config_score_params: ConfigScoreParams {
current_nym_node_semver: msg.current_nym_node_version.clone(),
version_weights: msg.version_score_weights,
version_score_formula_params: msg.version_score_params,
},
@@ -101,7 +100,13 @@ pub fn instantiate(
starting_interval,
rewarding_validator_address,
)?;
mixnet_params_storage::initialise_storage(deps.branch(), state, info.sender)?;
mixnet_params_storage::initialise_storage(
deps.branch(),
&env,
state,
info.sender,
msg.current_nym_node_version,
)?;
RewardingStorage::new().initialise(deps.storage, reward_params)?;
nymnodes_storage::initialise_storage(deps.storage)?;
cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
@@ -138,6 +143,7 @@ pub fn execute(
ExecuteMsg::UpdateCurrentNymNodeSemver { current_version } => {
crate::mixnet_contract_settings::transactions::try_update_current_nym_node_semver(
deps,
env,
info,
current_version,
)
@@ -334,6 +340,16 @@ pub fn query(
QueryMsg::GetState {} => {
to_binary(&crate::mixnet_contract_settings::queries::query_contract_state(deps)?)
}
QueryMsg::GetCurrentNymNodeVersion {} => to_binary(
&crate::mixnet_contract_settings::queries::query_current_nym_node_version(deps)?,
),
QueryMsg::GetNymNodeVersionHistory { limit, start_after } => to_binary(
&crate::mixnet_contract_settings::queries::query_nym_node_version_history_paged(
deps,
start_after,
limit,
)?,
),
QueryMsg::Admin {} => to_binary(&crate::mixnet_contract_settings::queries::query_admin(
deps,
)?),
@@ -587,7 +603,7 @@ pub fn query(
#[entry_point]
pub fn migrate(
mut deps: DepsMut<'_>,
_env: Env,
env: Env,
msg: MigrateMsg,
) -> Result<Response, MixnetContractError> {
set_build_information!(deps.storage)?;
@@ -596,7 +612,7 @@ pub fn migrate(
let skip_state_updates = msg.unsafe_skip_state_updates.unwrap_or(false);
if !skip_state_updates {
crate::queued_migrations::add_config_score_params(deps.branch(), &msg)?;
crate::queued_migrations::add_config_score_params(deps.branch(), env, &msg)?;
}
// due to circular dependency on contract addresses (i.e. mixnet contract requiring vesting contract address
@@ -693,7 +709,6 @@ mod tests {
},
},
config_score_params: ConfigScoreParams {
current_nym_node_semver: "1.1.10".to_string(),
version_weights: Default::default(),
version_score_formula_params: Default::default(),
},
@@ -3,9 +3,14 @@
use super::storage;
use crate::mixnet_contract_settings::storage::ADMIN;
use cosmwasm_std::{Deps, StdResult};
use cosmwasm_std::{Deps, Order, StdResult};
use cw_controllers::AdminResponse;
use mixnet_contract_common::{ContractBuildInformation, ContractState, ContractStateParams};
use cw_storage_plus::Bound;
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::{
ContractBuildInformation, ContractState, ContractStateParams, CurrentNymNodeVersionResponse,
HistoricalNymNodeVersionEntry, NymNodeVersionHistoryResponse,
};
use nym_contracts_common::get_build_information;
pub(crate) fn query_admin(deps: Deps<'_>) -> StdResult<AdminResponse> {
@@ -32,6 +37,36 @@ pub(crate) fn query_contract_version() -> ContractBuildInformation {
get_build_information!()
}
pub(crate) fn query_nym_node_version_history_paged(
deps: Deps<'_>,
start_after: Option<u32>,
limit: Option<u32>,
) -> StdResult<NymNodeVersionHistoryResponse> {
let limit = limit.unwrap_or(100).min(200) as usize;
let start = start_after.map(Bound::exclusive);
let history = storage::NymNodeVersionHistory::new()
.version_history
.range(deps.storage, start, None, Order::Ascending)
.take(limit)
.map(|r| r.map(Into::<HistoricalNymNodeVersionEntry>::into))
.collect::<StdResult<Vec<_>>>()?;
let start_next_after = history.last().map(|entry| entry.id);
Ok(NymNodeVersionHistoryResponse {
history,
start_next_after,
})
}
pub(crate) fn query_current_nym_node_version(
deps: Deps<'_>,
) -> Result<CurrentNymNodeVersionResponse, MixnetContractError> {
let version = storage::NymNodeVersionHistory::new().current_version(deps.storage)?;
Ok(CurrentNymNodeVersionResponse { version })
}
#[cfg(test)]
pub(crate) mod tests {
use super::*;
@@ -59,7 +94,6 @@ pub(crate) mod tests {
interval_operating_cost: Default::default(),
},
config_score_params: ConfigScoreParams {
current_nym_node_semver: "1.1.10".to_string(),
version_weights: Default::default(),
version_score_formula_params: Default::default(),
},
@@ -1,19 +1,108 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::constants::{ADMIN_STORAGE_KEY, CONTRACT_STATE_KEY};
use cosmwasm_std::{Addr, DepsMut, Storage};
use cosmwasm_std::{Coin, StdResult};
use crate::constants::{
ADMIN_STORAGE_KEY, CONTRACT_STATE_KEY, VERSION_HISTORY_ID_COUNTER_KEY,
VERSION_HISTORY_NAMESPACE,
};
use cosmwasm_std::Coin;
use cosmwasm_std::{Addr, DepsMut, Env, Storage};
use cw_controllers::Admin;
use cw_storage_plus::Item;
use cw_storage_plus::{Item, Map};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::{
ContractState, ContractStateParams, OperatingCostRange, ProfitMarginRange,
ContractState, ContractStateParams, HistoricalNymNodeVersion, HistoricalNymNodeVersionEntry,
OperatingCostRange, ProfitMarginRange,
};
use std::str::FromStr;
pub(crate) const CONTRACT_STATE: Item<'_, ContractState> = Item::new(CONTRACT_STATE_KEY);
pub(crate) const ADMIN: Admin = Admin::new(ADMIN_STORAGE_KEY);
pub(crate) struct NymNodeVersionHistory<'a> {
pub(crate) id_counter: Item<'a, u32>,
pub(crate) version_history: Map<'a, u32, HistoricalNymNodeVersion>,
}
impl NymNodeVersionHistory<'_> {
#[allow(clippy::new_without_default)]
pub const fn new() -> Self {
Self {
id_counter: Item::new(VERSION_HISTORY_ID_COUNTER_KEY),
version_history: Map::new(VERSION_HISTORY_NAMESPACE),
}
}
fn next_id(&self, storage: &mut dyn Storage) -> Result<u32, MixnetContractError> {
let next = self.id_counter.may_load(storage)?.unwrap_or_default();
self.id_counter.save(storage, &next)?;
Ok(next)
}
pub fn current_version(
&self,
storage: &dyn Storage,
) -> Result<Option<HistoricalNymNodeVersionEntry>, MixnetContractError> {
let Some(current_id) = self.id_counter.may_load(storage)? else {
return Ok(None);
};
let version_information = self.version_history.load(storage, current_id)?;
Ok(Some(HistoricalNymNodeVersionEntry {
id: current_id,
version_information,
}))
}
pub fn insert_new(
&self,
storage: &mut dyn Storage,
entry: HistoricalNymNodeVersion,
) -> Result<u32, MixnetContractError> {
let next_id = self.next_id(storage)?;
self.version_history.save(storage, next_id, &entry)?;
Ok(next_id)
}
pub fn try_insert_new(
&self,
storage: &mut dyn Storage,
env: &Env,
raw_semver: &str,
) -> Result<u32, MixnetContractError> {
let Ok(new_semver) = semver::Version::from_str(raw_semver) else {
return Err(MixnetContractError::InvalidNymNodeSemver {
provided: raw_semver.to_string(),
});
};
let Some(current) = self.current_version(storage)? else {
// treat this as genesis
let genesis =
HistoricalNymNodeVersion::genesis(raw_semver.to_string(), env.block.height);
return self.insert_new(storage, genesis);
};
let current_semver = current.version_information.semver_unchecked();
if new_semver <= current_semver {
// make sure the new semver is strictly more recent than the current head
return Err(MixnetContractError::NonIncreasingSemver {
provided: raw_semver.to_string(),
current: current.version_information.semver,
});
}
let diff = current
.version_information
.cumulative_difference_since_genesis(&new_semver);
let entry = HistoricalNymNodeVersion {
semver: raw_semver.to_string(),
introduced_at_height: env.block.height,
difference_since_genesis: diff,
};
self.insert_new(storage, entry)
}
}
pub fn rewarding_validator_address(storage: &dyn Storage) -> Result<Addr, MixnetContractError> {
Ok(CONTRACT_STATE
.load(storage)
@@ -71,9 +160,13 @@ pub(crate) fn state_params(
pub(crate) fn initialise_storage(
deps: DepsMut<'_>,
env: &Env,
initial_state: ContractState,
initial_admin: Addr,
) -> StdResult<()> {
initial_nymnode_version: String,
) -> Result<(), MixnetContractError> {
CONTRACT_STATE.save(deps.storage, &initial_state)?;
ADMIN.set(deps, Some(initial_admin))
NymNodeVersionHistory::new().try_insert_new(deps.storage, env, &initial_nymnode_version)?;
ADMIN.set(deps, Some(initial_admin))?;
Ok(())
}
@@ -3,16 +3,15 @@
use super::storage;
use crate::mixnet_contract_settings::storage::ADMIN;
use cosmwasm_std::MessageInfo;
use cosmwasm_std::Response;
use cosmwasm_std::{DepsMut, StdResult};
use cosmwasm_std::{Env, MessageInfo};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::events::{
new_rewarding_validator_address_update_event, new_settings_update_event,
new_update_nym_node_semver_event,
};
use mixnet_contract_common::ContractStateParamsUpdate;
use std::str::FromStr;
pub fn try_update_contract_admin(
mut deps: DepsMut<'_>,
@@ -92,15 +91,6 @@ pub(crate) fn try_update_contract_settings(
// check for config score params updates
if let Some(config_score_update) = update.config_score_params {
// if semver is to be updated - validate the provided value
if let Some(current_nym_node_semver) = config_score_update.current_nym_node_semver {
if semver::Version::from_str(&current_nym_node_semver).is_err() {
return Err(MixnetContractError::InvalidNymNodeSemver {
provided: current_nym_node_semver,
});
}
state.params.config_score_params.current_nym_node_semver = current_nym_node_semver
}
if let Some(version_weights) = config_score_update.version_weights {
state.params.config_score_params.version_weights = version_weights
}
@@ -119,23 +109,19 @@ pub(crate) fn try_update_contract_settings(
pub(crate) fn try_update_current_nym_node_semver(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
current_version: String,
) -> Result<Response, MixnetContractError> {
let mut state = storage::CONTRACT_STATE.load(deps.storage)?;
ADMIN.assert_admin(deps.as_ref(), &info.sender)?;
let response = Response::new().add_event(new_update_nym_node_semver_event(&current_version));
let new_id = storage::NymNodeVersionHistory::new().try_insert_new(
deps.storage,
&env,
&current_version,
)?;
if semver::Version::from_str(&current_version).is_err() {
return Err(MixnetContractError::InvalidNymNodeSemver {
provided: current_version,
});
}
state.params.config_score_params.current_nym_node_semver = current_version;
storage::CONTRACT_STATE.save(deps.storage, &state)?;
Ok(response)
Ok(Response::new().add_event(new_update_nym_node_semver_event(&current_version, new_id)))
}
#[cfg(test)]
+11 -2
View File
@@ -4,7 +4,8 @@
mod config_score_params {
use crate::constants::CONTRACT_STATE_KEY;
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use cosmwasm_std::{Addr, Coin, DepsMut};
use crate::mixnet_contract_settings::storage::NymNodeVersionHistory;
use cosmwasm_std::{Addr, Coin, DepsMut, Env};
use cw_storage_plus::Item;
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::{
@@ -16,6 +17,7 @@ mod config_score_params {
pub(crate) fn add_config_score_params(
deps: DepsMut<'_>,
env: Env,
msg: &MigrateMsg,
) -> Result<(), MixnetContractError> {
if semver::Version::from_str(&msg.current_nym_node_semver).is_err() {
@@ -62,7 +64,6 @@ mod config_score_params {
interval_operating_cost: old_state.params.interval_operating_cost,
},
config_score_params: ConfigScoreParams {
current_nym_node_semver: msg.current_nym_node_semver.to_string(),
version_weights: msg.version_score_weights,
version_score_formula_params: msg.version_score_params,
},
@@ -70,6 +71,14 @@ mod config_score_params {
};
mixnet_params_storage::CONTRACT_STATE.save(deps.storage, &new_state)?;
// initialise the version chain
NymNodeVersionHistory::new().try_insert_new(
deps.storage,
&env,
&msg.current_nym_node_semver,
)?;
Ok(())
}
}
@@ -1 +1 @@
Monday, November 25th 2024, 13:24:04 UTC
Wednesday, November 20th 2024, 15:53:00 UTC
@@ -1,4 +1,5 @@
```sh
2024-11-20T15:53:01.702819Z  INFO nym-api/src/main.rs:40: Starting nym api...
Usage: nym-api [OPTIONS] <COMMAND>
Commands:
@@ -9,9 +10,9 @@ Commands:
Options:
-c, --config-env-file <CONFIG_ENV_FILE>
Path pointing to an env file that configures the Nym API
Path pointing to an env file that configures the Nym API [env: NYMAPI_CONFIG_ENV_FILE_ARG=]
--no-banner
A no-op flag included for consistency with other binaries (and compatibility with nymvisor, oops)
A no-op flag included for consistency with other binaries (and compatibility with nymvisor, oops) [env: NYMAPI_NO_BANNER_ARG=]
-h, --help
Print help
-V, --version
@@ -44,6 +44,8 @@ Options:
Specify whether detailed system crypto hardware information should be exposed. default: true [env: NYMNODE_HTTP_EXPOSE_CRYPTO_HARDWARE=] [possible values: true, false]
--mixnet-bind-address <MIXNET_BIND_ADDRESS>
Address this node will bind to for listening for mixnet packets default: `0.0.0.0:1789` [env: NYMNODE_MIXNET_BIND_ADDRESS=]
--mixnet-announce-port <MIXNET_ANNOUNCE_PORT>
If applicable, custom port announced in the self-described API that other clients and nodes will use. Useful when the node is behind a proxy [env: NYMNODE_MIXNET_ANNOUNCE_PORT=]
--nym-api-urls <NYM_API_URLS>
Addresses to nym APIs from which the node gets the view of the network [env: NYMNODE_NYM_APIS=]
--nyxd-urls <NYXD_URLS>
@@ -52,14 +54,14 @@ Options:
Specifies whether the wireguard service is enabled on this node [env: NYMNODE_WG_ENABLED=] [possible values: true, false]
--wireguard-bind-address <WIREGUARD_BIND_ADDRESS>
Socket address this node will use for binding its wireguard interface. default: `0.0.0.0:51822` [env: NYMNODE_WG_BIND_ADDRESS=]
--wireguard-private-ip <WIREGUARD_PRIVATE_IP>
Private IP address of the wireguard gateway. default: `10.1.0.1` [env: NYMNODE_WG_IP=]
--wireguard-announced-port <WIREGUARD_ANNOUNCED_PORT>
Port announced to external clients wishing to connect to the wireguard interface. Useful in the instances where the node is behind a proxy [env: NYMNODE_WG_ANNOUNCED_PORT=]
--wireguard-private-network-prefix <WIREGUARD_PRIVATE_NETWORK_PREFIX>
The prefix denoting the maximum number of the clients that can be connected via Wireguard. The maximum value for IPv4 is 32 and for IPv6 is 128 [env: NYMNODE_WG_PRIVATE_NETWORK_PREFIX=]
--verloc-bind-address <VERLOC_BIND_ADDRESS>
Socket address this node will use for binding its verloc API. default: `0.0.0.0:1790` [env: NYMNODE_VERLOC_BIND_ADDRESS=]
--verloc-announce-port <VERLOC_ANNOUNCE_PORT>
If applicable, custom port announced in the self-described API that other clients and nodes will use. Useful when the node is behind a proxy [env: NYMNODE_VERLOC_ANNOUNCE_PORT=]
--entry-bind-address <ENTRY_BIND_ADDRESS>
Socket address this node will use for binding its client websocket API. default: `0.0.0.0:9000` [env: NYMNODE_ENTRY_BIND_ADDRESS=]
--announce-ws-port <ANNOUNCE_WS_PORT>
+17 -168
View File
@@ -33,157 +33,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)
- [`nym-node`](nodes/nym-node.mdx) version `1.1.11`
```sh
nym-node
Binary Name: nym-node
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.82.0
rustc Channel: stable
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.
[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.
- **Please re-run [routing configuration steps](https://nymtech.net/docs/operators/nodes/nym-node/configuration#routing-configuration) to update your routing settings.**
- We found out that some operators have a wrong value for wireguard IP. Follow these steps to ensure your value is set to `10.1.0.1` (default on new nodes):
<Steps>
###### 1. Open your node config file:
```sh
nano $HOME/.nym/nym-nodes/<ID>/config/config.toml
# change <ID> for your local nym moniker for example:
# nano $HOME/.nym/nym-nodes/default-nym-node/config/config.toml
```
###### 2. Control or change the value of wireguard private IP
- Scroll down to section starting with `[wireguard]`
- Find line `private_ip` and ensure it's set to value `10.1.0.1`
- The section will look like this:
```toml
[wireguard]
# Specifies whether the wireguard service is enabled on this node.
enabled = true
# Socket address this node will use for binding its wireguard interface.
# default: `0.0.0.0:51822`
bind_address = '0.0.0.0:51822'
# Private IP address of the wireguard gateway.
# default: `10.1.0.1`
private_ip = '10.1.0.1'
```
###### 3. Save, exit and restart the service
- If you used `nano` editor - press `ctrl` + `x` and confirm the changes
- Run these commands to update the service with new values and restart your node process:
```sh
systemctl daemon-reload && service nym-node restart
```
</Steps>
- New manual how to [run `nym-node` as non-root](nodes/nym-node/configuration#running-nym-node-as-a-non-root)
- Since `v2024.13-magura`, operators do not update their node version in the wallet. [Manual upgrading steps](nodes/maintenance/manual-upgrade.mdx) has been updated accordingly.
- CLI tool [`node_api_check.py`](nodes/performance-and-testing/node-api-check.mdx), helping operators to collect all API values about their nodes locally, is not up to date with the API changes introduced with `v2024.13-magura` release version. Please treat it as unstable before we fix it.
#### Error Log
In case you encounter this error:
```
[ERROR] nym-node/src/node/mod.rs:628: the exit gateway subtask has failed with the following message: failed to start authenticator: internal wireguard error no private IP set for peer..
```
You can follow these steps to make a workaround:
<br />
<AccordionTemplate name="Authenticator error fix">
<Steps>
###### 1. Find the error
- In the node logs, locate the ERROR message which says `the exit gateway subtask has failed with the following message: failed to start authenticator: internal wireguard error no private IP set for peer KN5GPvkC+p6G/SM4PD2Z3ObAtRGiDjHPRnQOPpbdUQk=`
- Copy the end part of that peer, later denoted as `<WG_PEER_STRING_END>` (in our example `GiDjHPRnQOPpbdUQk=`) to use later in the sql commands
###### 2. Fix the issue in sqlite3 db
<Callout type="warning" emoji="⚠️">
Be careful when running commands within sqlite database.
</Callout>
- Navigate to the data directory:
```sh
cd $HOME/.nym/nym-nodes/<ID>/data
```
- Enter the database:
```sh
sqlite3 clients.sqlite
```
- Run these commands:
```sh
# Change with your unique <PEER_STRING_END>
select * from wireguard_peer where public_key like "%<WG_PEER_STRING_END>%"
# Make sure that only ONE line is returned and it contains the key
delete from wireguard_peer where public_key like "%<WG_PEER_STRING_END>%";
```
- Confirm that peer has been removed by running this again:
```sh
select * from wireguard_peer where public_key like "%<WG_PEER_STRING_END>%";
```
###### 3. Exit and restart the service
Run `.quit` and:
```sh
systemctl restart nym-node.service
```
</Steps>
</AccordionTemplate>
## `v2024.13-magura`
Magura release represents a bigger milestone in [project Smoosh](archive/faq/smoosh-faq.mdx) development where `nym-node` is one binary able to perform any function in Nym Mixnet. This release is especially crucial for operators, please pay attention to the section [*Operators Updates & Tooling*](#operators-updates--tooling) below.
@@ -221,15 +70,15 @@ cargo Profile: release
- [Switch over the last set of jobs to arc runners](https://github.com/nymtech/nym/pull/4938): Switch over the remaining GH jobs using 16-core runners to self-hosted arc runners. Since we can't currently use Docker on the ubuntu-20.04 runners, remove the matrix notification steps
<AccordionTemplate name={<TestingSteps/>}>
<AccordionTemplate name={<TestingSteps/>}>
Confirm that the deployment workflows work through manual testing
- [x] cd-docs
- [x] publish-sdk-npm
</AccordionTemplate>
- [V2 performance monitoring feature flag](https://github.com/nymtech/nym/pull/4943): Feature flag to use v2 network monitor results in rewarding
- [V2 performance monitoring feature flag](https://github.com/nymtech/nym/pull/4943): Feature flag to use v2 network monitor results in rewarding
- [Add `utoipa` feature to nym-node](https://github.com/nymtech/nym/pull/4945): `cargo build -p nym-node` was failing, since its depending on `QueryParams` having `utoipa` traits derived
- [Add `utoipa` feature to nym-node](https://github.com/nymtech/nym/pull/4945): `cargo build -p nym-node` was failing, since its depending on `QueryParams` having `utoipa` traits derived
- [Ticket type storage](https://github.com/nymtech/nym/pull/4947)
@@ -254,8 +103,8 @@ Confirm that the deployment workflows work through manual testing
- [chore: remove unused rocket code](https://github.com/nymtech/nym/pull/4968)
- [add Dockerfile for nym node](https://github.com/nymtech/nym/pull/4972)
- [`Product Data` Add session type based on ecash ticket received](https://github.com/nymtech/nym/pull/4974): Fire an `EcashTicket` event for the `GatewayStatisticsCollector`, when an Ecash ticket is being accepted. This allows to mark an active session as being a mixnet session or a vpn session. It also changes the format of the related self-described data, to accommodate that new session type.
- [`Product Data` Add session type based on ecash ticket received](https://github.com/nymtech/nym/pull/4974): Fire an `EcashTicket` event for the `GatewayStatisticsCollector`, when an Ecash ticket is being accepted. This allows to mark an active session as being a mixnet session or a vpn session. It also changes the format of the related self-described data, to accommodate that new session type.
- [Top up bandwidth](https://github.com/nymtech/nym/pull/4975)
@@ -298,7 +147,7 @@ Confirm that the deployment workflows work through manual testing
- log gw identity key
- better agent testrun logging
- log responses on server side
- change response code for agents
- change response code for agents
- update sqlx data
- fix agent - probe gw bug
@@ -316,7 +165,7 @@ Confirm that the deployment workflows work through manual testing
- [Allow custom http port to be reset](https://github.com/nymtech/nym/pull/5073)
- [Fix gateway decreasing bandwidth](https://github.com/nymtech/nym/pull/5075): Make sure to update the storage after each decrease with the new values. Also set the storage values to 0 on restart for existing peers, as kernel peers can't have those values set to 0
- [Fix gateway decreasing bandwidth](https://github.com/nymtech/nym/pull/5075): Make sure to update the storage after each decrease with the new values. Also set the storage values to 0 on restart for existing peers, as kernel peers can't have those values set to 0
- [Fix expiration date as today + 7 days](https://github.com/nymtech/nym/pull/5076)
@@ -338,7 +187,7 @@ Confirm that the deployment workflows work through manual testing
- [Add NYM_VPN_API to env files](https://github.com/nymtech/nym/pull/5099)
- [Feature/force refresh node](https://github.com/nymtech/nym/pull/5101): currently if nodes update their role from say mixnode to entry-gateway, it might take quite a while for `nym-api` to pick up the change and thus they might be losing performance. With this change, the node will be force refreshed on its startup
- [Feature/force refresh node](https://github.com/nymtech/nym/pull/5101): currently if nodes update their role from say mixnode to entry-gateway, it might take quite a while for `nym-api` to pick up the change and thus they might be losing performance. With this change, the node will be force refreshed on its startup
- [`nym-credential-proxy-requests`: reqwest use rustls-tls](https://github.com/nymtech/nym/pull/5116)
@@ -356,7 +205,7 @@ Confirm that the deployment workflows work through manual testing
- [Fix critical issues SI84 and SI85 from Cure53](https://github.com/nymtech/nym/pull/4758): This pull request fixes the following issues:
- NYM-01-009 WP5: BLS12-381 EC signature bypasses in Coconut library (Critical)
- NYM-01-014 WP5: Partial signature bypass in offline eCash (Critical)
- [bugfix: correctly paginate through 'search_tx' endpoint](https://github.com/nymtech/nym/pull/4936): when `results.append(&mut res.txs);` was called, `res.txs` was always empty thus it was impossible to return more than page size number of results
- [Fix broken build after merge](https://github.com/nymtech/nym/pull/4937)
@@ -365,7 +214,7 @@ Confirm that the deployment workflows work through manual testing
- dedicated commands to request specific blocks for processing
- decreased websocket failure timeout
- ensuring we do actually have sufficient number of blocks to process rewarding for given epoch
- additional error logging
- additional error logging
- [bugfix: fix expected return type on /v1/gateways endpoint](https://github.com/nymtech/nym/pull/4965)
@@ -375,7 +224,7 @@ Confirm that the deployment workflows work through manual testing
- Faulty aggregation to invalid offline eCash signatures
- Signature forgery of Pointcheval-Sanders schema
- [bugfix: client memory leak](https://github.com/nymtech/nym/pull/4991): This fixes memory leaks in all the clients. however, they were most prominent in `nym-api` during network monitoring due to the sheer amount of packets being pushed
- [bugfix: client memory leak](https://github.com/nymtech/nym/pull/4991): This fixes memory leaks in all the clients. however, they were most prominent in `nym-api` during network monitoring due to the sheer amount of packets being pushed
- [Fix rustfmt in nym-credential-proxy](https://github.com/nymtech/nym/pull/4992)
@@ -430,7 +279,7 @@ Confirm that the deployment workflows work through manual testing
- [bugfix: make sure to assign correct node_id and identity during 'gateway_details' table migration](https://github.com/nymtech/nym/pull/5142)
- [bugifx: assign 'node_id' when converting from 'GatewayDetails' to 'TestNode'](https://github.com/nymtech/nym/pull/5143)
### Operators Updates & Tooling
<Callout type="warning" emoji="⚠️">
@@ -443,7 +292,7 @@ Confirm that the deployment workflows work through manual testing
- **[Operators release & rewards roadmap](tokenomics/mixnet-rewards.mdx#roadmap)**
- **New [Operators landing page](https://nymtech.net/operators)**
- **New [Operators landing pag e](https://nymtech.net/operators)
- [Nym Harbourmaster](https://harbourmaster.nymtech.net) had a new tab `NODE SEARCH` where operators can easily search nodes by identity keys and owner accounts and get all public information listed.
@@ -486,7 +335,7 @@ Confirm that the deployment workflows work through manual testing
- Confirm the transaction
###### 5. Welcome to new episode of `nym-node`!
###### 5. Welcome to new episode of `nym-node`!
</ Steps>
</AccordionTemplate>
@@ -511,13 +360,13 @@ Confirm that the deployment workflows work through manual testing
- Currently in *Native rewarding*, the rewards are split equally across the [rewarded set of nodes](https://validator.nymtech.net/api/v1/epoch/reward_params) (which now = active set and it's size is 240 nodes) for both Mixnet mode and dVPN mode. Every node being assigned 1 / 240 work factor (hence *naive rewarding*).
#### Directory Services v2.1: API & Mixnet Contract Changes
#### Directory Services v2.1: API & Mixnet Contract Changes
Magura release brings [breaking changes on API](https://github.com/nymtech/nym/pull/4903) logic of Nym. New APIs will only communicate with `nym-node` from this release and newer. Also old version of APIs won't be able to communicate with the new version of `nym-node`. We are also moving towards completely removing Nym Explorer API, which now has been only used to report nodes location.
Magura release brings [breaking changes on API](https://github.com/nymtech/nym/pull/4903) logic of Nym. New APIs will only communicate with `nym-node` from this release and newer. Also old version of APIs won't be able to communicate with the new version of `nym-node`. We are also moving towards completely removing Nym Explorer API, which now has been only used to report nodes location.
Any new bonded node will provide only the bare minimum information: host, identity key and optionally custom port of its HTTP api - we highly recommend to set that one up to `8080`. Everything else will be discovered via the self-described API for maximum flexibility. This also includes the sphinx key, meaning if the API is not exposed, the node will be unable to route any traffic. Furthermore, this allows to arbitrary change of `nym-node` from mixnode into a gateway modes (and vice versa) without losing any delegations.
The contract changes also mean any node functionality can get rewards. Rather than just with assigned mixing roles, gateways now also added into the pool. However, to be eligible for gateway rewarding, one must [migrate into a `nym-node`](#wallet-changes) on a smart contract level (or bond a new node).
The contract changes also mean any node functionality can get rewards. Rather than just with assigned mixing roles, gateways now also added into the pool. However, to be eligible for gateway rewarding, one must [migrate into a `nym-node`](#wallet-changes) on a smart contract level (or bond a new node).
##### API High Level Changes
@@ -14,27 +14,30 @@ This page explains how to upgrade [`nym-node`](#nym-node-upgrade) or [`validator
## Nym node Upgrade
Since `v2024.13-magura` (`nym-node v1.1.10`), **operators NO longer update node information in the Mixnet smart contract** (wallet version information), **only upgrade node binary** (on VPS), resulting in `~/.nym/nym-nodes/<ID>/config/config.toml` update.
**Upgrading your node is a straight forward two-step process:**
<Steps>
#### 1. Updating the binary and `~/.nym/nym-nodes/<ID>/config/config.toml` on your VPS
#### 2. Updating the node information in the [mixnet smart contract](https://nymtech.net/docs/nyx/mixnet-contract.html). This is the information that is present on the [mixnet explorer](https://explorer.nymtech.net).
</Steps>
Below are detailed steps how to do it:
<Steps>
###### 1. Upgrade `nym-node` binary
###### 1. Upgrading node binary and information in config file
- Pause your node process.
- If you run your node as `systemd` service (recommended), run: `service nym-node stop`
- Otherwise open the terminal window with your node logs and press once `ctrl + c` and wait for the node to terminate gracefully
- Otherwise open the terminal window with your node logs and press `ctrl + c`
- Replace the existing `nym-node` binary with the newest binary (which you can either [compile yourself](../../binaries/building-nym.mdx) or [download](../../binaries/pre-built-binaries.mdx).
- To verify node version, run `./nym-node --version`
###### 2. Restart the node
- [Re-run with the same values](../nym-node/setup.mdx#initialise--run) as you use to run your `nym-node`. If you want keep changes in your config file, use flag `-w` (`--write-changes`), **This will just update the config file, it will not overwrite existing keys**.
- If you automated your node with `systemd` (recommended), make sure you have all needed flags in `ExecStart` line of the service config file, and run:
- If you automated your node with `systemd` (recommended) run:
```sh
systemctl daemon-reload
service nym-node start
@@ -45,19 +48,25 @@ service nym-node start
journalctl -f -u nym-node.service
```
###### 2. Updating your node information in the smart contract
###### 3. Check if your node is reporting the version correctly
Follow these steps to update the information about your `nym-node` which is publicly available from the [`nym-api`](https://validator.nymtech.net/api/swagger/index.html) and information displayed on the [Mixnet explorer](https://explorer.nymtech.net).
- Open [Nym Harbourbourmaster](https://harbourmaster.nymtech.net), search your node and verify that everything is working as expected and your node shows expected version.
You can either do this graphically via the Desktop Wallet, or the CLI.
<div>
<Tabs items={[
<strong>Desktop Wallet (recommended)</strong>,
<strong>CLI (superusers)</strong>,
]} defaultIndex="0">
<MyTab><DesktopWalletUpdate/></MyTab>
<MyTab><CliUpdate/></MyTab>
</Tabs>
</div>
</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.
[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>
## Validator Upgrade
Upgrading from `v0.31.1` -> `v0.32.0` process is fairly simple. Grab the `v0.32.0` release tarball from the [`nyxd` releases page](https://github.com/nymtech/nyxd/releases), and untar it. Inside are two files:
@@ -83,3 +92,4 @@ The most common reason for your validator being jailed is that it runs out of me
Running the command `df -H` will return the size of the various partitions of your VPS.
If the `/dev/sda` partition is almost full, try pruning some of the `.gz` syslog archives and restart your validator process.
@@ -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
@@ -426,95 +426,6 @@ journalctl -u nym-node.service -f -n 100
Make sure that you get the validation of all connectivity. If there are still any problems, please refer to [troubleshooting section](../../troubleshooting/vps-isp.mdx#incorrect-gateway-network-check).
## Running `nym-node` as a non-root
Some operators prefer to run `nym-node` without root privileges. It's possible but still `nym-node` binary needs higher privileges for network-level operations demanding these permissions. Below is a guide how to go about such setup:
<Callout type="warning" emoji="⚠️">
Copying nodes database and the `.nym/` directories from `/root/.nym` to `/home/<USER>/.nym/` should be treated as experimental, therefore we would advise this section for operators starting new nodes, rather than tweaking an existing one. We will publish a detailed guide for changing permissions of an existing node soon.
</Callout>
<Steps>
###### 1. Setup a new user
- Define a variable `user_name` using your desired user name:
```sh
user_name="<USER>"
```
- Run:
```sh
user_home="/home/$user_name"
if ! id "$user_name" &>/dev/null; then
sudo adduser --home "$user_home" --disabled-login --gecos "" "$user_name"
else
echo "user $user_name already exists"
fi
```
- And follow by:
```sh
sudo usermod -aG sudo "$user_name"
```
- Optional: Add to sudoers group:
```sh
echo "$user_name ALL=(ALL) NOPASSWD:ALL" | sudo tee -a /etc/sudoers.d/$user_name
```
###### 2. Grant needed permissions for network-level operations
While `nym-node` will be set as a user process, it requires higher privileges for network-level operations, set them up with this command:
```sh
sudo setcap 'cap_net_bind_service=+ep cap_net_admin=+ep' nym-node
```
**After replacing or upgrading the binary, you must reapply these permissions each time!**
###### 3. Edit service config file
- Add these new lines to your `/etc/systemd/system/nym-node.service` [service config file](#systemd)
- `After=network.target`
- `Group=<USER>`
- `Type=simple`
- Your service file will then look like this:
```ini
[Unit]
Description=Nym Node
After=network.target
StartLimitInterval=350
StartLimitBurst=10
[Service]
User=<USER>
Group=<USER>
Type=simple
LimitNOFILE=65536
ExecStart=<PATH>/nym-node run <ARGUMENTS> # add all the flags you use to run your node
KillSignal=SIGINT
Restart=on-failure
RestartSec=30
[Install]
WantedBy=multi-user.target
```
###### 4. Reload and restart the service
```sh
systemctl daemon-reload && service nym-node restart
```
- If you want to follow the logs, run:
```sh
journalctl -u nym-node -f
```
</Steps>
## Next Steps
@@ -17,16 +17,15 @@ 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-18T17:02:50.947941194Z
Build Version: 1.1.10
Commit SHA: b49ef643df86f0c670672429812c632fbbaf6cf1
Commit Date: 2024-11-18T17:56:57.000000000+01:00
Commit Branch: HEAD
rustc Version: 1.83.0
rustc Version: 1.82.0
rustc Channel: stable
cargo Profile: release
```
{/* COMMENTING THIS OUT ASS WE HAVE TO FIGURE OUT HOW TO SHOW THE LATEST VERSION FROM MASTER BRANCH
<BuildInfo />
*/}
@@ -8,10 +8,6 @@ import NodeApiCheckQueryHelp from 'components/outputs/command-outputs/node-api-c
# Node API Check
<Callout type="warning" emoji="⚠️">
CLI tool `node_api_check.py`, helping operators to collect all API values about their nodes locally, is not up to date with the API changes introduced with `v2024.13-magura` release version. Please treat it as unstable before we fix it.
</Callout>
<VarInfo />
Operating a `nym-node` is not a *"set and forget"* endeavor, it takes some work. To diagnose node network performance through querying APIs, is a good knowledge to have. These are the main places to look for API endpoints regarding `nym-node`:
@@ -97,9 +93,9 @@ python --version
</Tabs>
</div>
###### 2. Install `node_api_check.py` and make executable
###### 2. Install `node_api_check.py` and make executable
To run the program you neet to have [`node_api_check.py`](https://github.com/nymtech/nym/tree/develop/scripts/node_api_check.py) and [`api_endpoints.json`](https://github.com/nymtech/nym/tree/develop/scripts/api_endpoints.json).
To run the program you neet to have [`node_api_check.py`](https://github.com/nymtech/nym/tree/develop/scripts/node_api_check.py) and [`api_endpoints.json`](https://github.com/nymtech/nym/tree/develop/scripts/api_endpoints.json).
- If you [compiled from source](../../binaries/building-nym.mdx), you already have both of these files. Note that the latest version of this program may be on `develop` branch.
@@ -163,3 +159,4 @@ Another command is `version_count` where at least one `nym-node` version is requ
```sh
./node_api_check version_count 1.1.0 1.1.1 1.1.2 1.1.3 --markdown
```
@@ -232,9 +232,6 @@ username soft nofile 4096
Then reboot your server and restart your node.
## Running `nym-node` as a non-root
Some operators prefer to run `nym-node` without root privileges. It's possible but still `nym-node` binary needs higher privileges for network-level operations demanding these permissions. If you are starting a new `nym-node` and want to run it as a non-root, follow [this guide](../nym-node/configuration#running-nym-node-as-a-non-root) before you proceed with the node setup sections.
## Ports reference tables
@@ -161,7 +161,7 @@ In dVPN (2-hop) mode every node which meets the performance criteria, including
In both cases, the selection algorithm also looks whether the node runs with [Terms & Conditions](nodes/nym-node/setup.mdx#terms--conditions) accepted **AND** if it's not a legacy binary version. In case either of these criterias are not met, the node will have be excluded from the rewarded set selection.
To read more about rewards calculation, please see next page [*Nym Operators Rewards*](tokenomics/mixnet-rewards.md).
To read more about rewards calculation, please see next page [*Nym Operators Rewards*](mixnet-rewards.md).
## Query Validator API
@@ -153,7 +153,7 @@ The entire active set selection probablity:
For a comparison we made an example with 5 nodes, where first number is node performance and second stake saturation (assuming all of them `config_score` = 1 and not 0):
<br />
<AccordionTemplate name="✏️ Example: Reward set selection probability calculation">
<AccordionTemplate name="✏️ Example: Reward set selection">
> node_1 = 1.00 ^ 20 \* 1.0 = 1 <br />
> node_2 = 1.00 ^ 20 \* 0.5 = 0.5 <br />
> node_3 = 0.99 ^ 20 \* 1.0 = 0.818 <br />
+33 -2
View File
@@ -1,6 +1,8 @@
use nym_api_requests::models::NodePerformance;
use nym_api_requests::models::{DescribedNodeType, NodePerformance, NymNodeData};
use nym_contracts_common::Percent;
use nym_mixnet_contract_common::{Addr, Coin, Gateway, LegacyMixLayer, MixNode, NodeId};
use nym_mixnet_contract_common::{
Addr, Coin, Delegation, Gateway, LegacyMixLayer, MixNode, NodeId, NodeRewarding, NymNodeBond,
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@@ -52,3 +54,32 @@ pub struct PrettyDetailedGatewayBond {
pub proxy: Option<Addr>,
pub location: Option<Location>,
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct NymNodeWithDescriptionAndLocation {
pub node_id: NodeId,
pub contract_node_type: Option<DescribedNodeType>,
pub description: Option<NymNodeData>,
pub bond_information: NymNodeBond,
pub rewarding_details: NodeRewarding,
pub location: Option<Location>,
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct NymNodeWithDescriptionAndLocationAndDelegations {
pub node_id: NodeId,
pub contract_node_type: Option<DescribedNodeType>,
pub description: Option<NymNodeData>,
pub bond_information: NymNodeBond,
pub rewarding_details: NodeRewarding,
pub location: Option<Location>,
pub delegations: Option<Vec<Delegation>>,
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct NymVestingAccount {
pub locked: Coin,
pub vested: Coin,
pub vesting: Coin,
pub spendable: Coin,
}
@@ -38,8 +38,14 @@ impl CountryStatisticsDistributionTask {
/// Retrieves the current list of mixnodes from the validators and calculates how many nodes are in each country
async fn calculate_nodes_per_country(&mut self) {
let cache = self.state.inner.mixnodes.get_locations().await;
let nym_nodes = self
.state
.inner
.nymnodes
.get_bonded_nymnodes_locations()
.await;
let three_letter_iso_country_codes: Vec<String> = cache
let mut three_letter_iso_country_codes: Vec<String> = cache
.values()
.flat_map(|i| {
i.location
@@ -48,6 +54,10 @@ impl CountryStatisticsDistributionTask {
})
.collect();
for node in nym_nodes {
three_letter_iso_country_codes.push(node.three_letter_iso_country_code);
}
let mut distribution = CountryNodesDistribution::new();
info!("Calculating country distribution from located mixnodes...");
+50 -6
View File
@@ -2,8 +2,10 @@
// SPDX-License-Identifier: Apache-2.0
use crate::{cache::Cache, location::LocationCacheItem};
use nym_contracts_common::IdentityKey;
use nym_explorer_api_requests::{Location, PrettyDetailedGatewayBond};
use nym_mixnet_contract_common::{GatewayBond, IdentityKey};
use nym_mixnet_contract_common::GatewayBond;
use nym_validator_client::models::GatewayBondAnnotated;
use serde::Serialize;
use std::{sync::Arc, time::SystemTime};
use tokio::sync::RwLock;
@@ -11,7 +13,7 @@ use tokio::sync::RwLock;
use super::location::GatewayLocationCache;
pub(crate) struct GatewayCache {
pub(crate) gateways: Cache<IdentityKey, GatewayBond>,
pub(crate) gateways: Cache<IdentityKey, GatewayBondAnnotated>,
}
#[derive(Clone, Debug, Serialize, JsonSchema)]
@@ -22,6 +24,7 @@ pub(crate) struct GatewaySummary {
#[derive(Clone)]
pub(crate) struct ThreadsafeGatewayCache {
gateways: Arc<RwLock<GatewayCache>>,
legacy_gateway_bonds: Arc<RwLock<GatewayCache>>,
locations: Arc<RwLock<GatewayLocationCache>>,
}
@@ -31,6 +34,9 @@ impl ThreadsafeGatewayCache {
gateways: Arc::new(RwLock::new(GatewayCache {
gateways: Cache::new(),
})),
legacy_gateway_bonds: Arc::new(RwLock::new(GatewayCache {
gateways: Cache::new(),
})),
locations: Arc::new(RwLock::new(GatewayLocationCache::new())),
}
}
@@ -51,7 +57,14 @@ impl ThreadsafeGatewayCache {
}
pub(crate) async fn get_gateways(&self) -> Vec<GatewayBond> {
self.gateways.read().await.gateways.get_all()
self.gateways
.read()
.await
.gateways
.get_all()
.iter()
.map(|g| g.gateway_bond.bond.clone())
.collect()
}
pub(crate) async fn get_detailed_gateways(&self) -> Vec<PrettyDetailedGatewayBond> {
@@ -64,7 +77,22 @@ impl ThreadsafeGatewayCache {
.iter()
.map(|bond| {
let location = location_guard.get(bond.identity());
self.create_detailed_gateway(bond.to_owned(), location)
self.create_detailed_gateway(bond.gateway_bond.bond.to_owned(), location)
})
.collect()
}
pub(crate) async fn get_legacy_detailed_gateways(&self) -> Vec<PrettyDetailedGatewayBond> {
let legacy_gateways = self.legacy_gateway_bonds.read().await;
let location_guard = self.locations.read().await;
legacy_gateways
.gateways
.get_all()
.iter()
.map(|bond| {
let location = location_guard.get(bond.identity());
self.create_detailed_gateway(bond.gateway_bond.bond.to_owned(), location)
})
.collect()
}
@@ -80,6 +108,9 @@ impl ThreadsafeGatewayCache {
gateways: Arc::new(RwLock::new(GatewayCache {
gateways: Cache::new(),
})),
legacy_gateway_bonds: Arc::new(RwLock::new(GatewayCache {
gateways: Cache::new(),
})),
locations: Arc::new(RwLock::new(locations)),
}
}
@@ -106,13 +137,26 @@ impl ThreadsafeGatewayCache {
.insert(identy_key, LocationCacheItem::new_from_location(location));
}
pub(crate) async fn update_cache(&self, gateways: Vec<GatewayBond>) {
pub(crate) async fn update_cache(
&self,
gateways: Vec<GatewayBondAnnotated>,
legacy_gateway_bonds: Vec<GatewayBond>,
) {
let mut guard = self.gateways.write().await;
let mut guard_legacy_gateways = self.legacy_gateway_bonds.write().await;
for gateway in gateways {
guard
.gateways
.set(gateway.gateway.identity_key.clone(), gateway)
.set(gateway.gateway_bond.gateway.identity_key.clone(), gateway)
}
for legacy_gateway in legacy_gateway_bonds {
if let Some(g) = guard.gateways.get(&legacy_gateway.gateway.identity_key) {
guard_legacy_gateways
.gateways
.set(legacy_gateway.gateway.identity_key, g.clone());
}
}
}
}
+2 -2
View File
@@ -10,11 +10,11 @@ use crate::gateways::http::gateways_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::nym_nodes::http::unstable_temp_nymnodes_make_default_routes;
use crate::overview::http::overview_make_default_routes;
use crate::ping::http::ping_make_default_routes;
use crate::service_providers::http::service_providers_make_default_routes;
use crate::state::ExplorerApiStateContext;
use crate::unstable::http::unstable_temp_make_default_routes;
use crate::validators::http::validators_make_default_routes;
mod swagger;
@@ -59,7 +59,7 @@ fn configure_rocket(state: ExplorerApiStateContext) -> Rocket<Build> {
"/ping" => ping_make_default_routes(&openapi_settings),
"/validators" => validators_make_default_routes(&openapi_settings),
"/service-providers" => service_providers_make_default_routes(&openapi_settings),
"/tmp/unstable" => unstable_temp_nymnodes_make_default_routes(&openapi_settings),
"/tmp/unstable" => unstable_temp_make_default_routes(&openapi_settings),
};
building_rocket
+1 -1
View File
@@ -22,12 +22,12 @@ mod http;
mod location;
mod mix_node;
pub(crate) mod mix_nodes;
mod nym_nodes;
mod overview;
mod ping;
pub(crate) mod service_providers;
mod state;
mod tasks;
mod unstable;
mod validators;
const COUNTRY_DATA_REFRESH_INTERVAL: u64 = 60 * 15; // every 15 minutes
+1 -1
View File
@@ -44,7 +44,7 @@ async fn get_mix_node_stats(host: &str, port: u16) -> Result<NodeStats, ReqwestE
.await
}
#[openapi(tag = "mix_nodes")]
#[openapi(tag = "mix_node")]
#[get("/<mix_id>")]
pub(crate) async fn get_by_id(
mix_id: NodeId,
+32 -1
View File
@@ -7,7 +7,7 @@ use crate::location::LocationCacheItem;
use crate::mix_nodes::CACHE_ENTRY_TTL;
use nym_explorer_api_requests::{Location, MixnodeStatus, PrettyDetailedMixNodeBond};
use nym_mixnet_contract_common::rewarding::helpers::truncate_reward;
use nym_mixnet_contract_common::NodeId;
use nym_mixnet_contract_common::{MixNodeBond, NodeId};
use nym_validator_client::models::MixNodeBondAnnotated;
use serde::Serialize;
use std::collections::{HashMap, HashSet};
@@ -80,6 +80,7 @@ impl MixNodesResult {
#[derive(Clone)]
pub(crate) struct ThreadsafeMixNodesCache {
mixnodes: Arc<RwLock<MixNodesResult>>,
legacy_mixnode_bonds: Arc<RwLock<MixNodesResult>>,
locations: Arc<RwLock<MixnodeLocationCache>>,
}
@@ -87,6 +88,7 @@ impl ThreadsafeMixNodesCache {
pub(crate) fn new() -> Self {
ThreadsafeMixNodesCache {
mixnodes: Arc::new(RwLock::new(MixNodesResult::new())),
legacy_mixnode_bonds: Arc::new(RwLock::new(MixNodesResult::new())),
locations: Arc::new(RwLock::new(MixnodeLocationCache::new())),
}
}
@@ -94,6 +96,7 @@ impl ThreadsafeMixNodesCache {
pub(crate) fn new_with_location_cache(locations: MixnodeLocationCache) -> Self {
ThreadsafeMixNodesCache {
mixnodes: Arc::new(RwLock::new(MixNodesResult::new())),
legacy_mixnode_bonds: Arc::new(RwLock::new(MixNodesResult::new())),
locations: Arc::new(RwLock::new(locations)),
}
}
@@ -188,13 +191,35 @@ impl ThreadsafeMixNodesCache {
.collect()
}
pub(crate) async fn get_legacy_detailed_mixnodes(&self) -> Vec<PrettyDetailedMixNodeBond> {
let legacy_mixnodes = self.legacy_mixnode_bonds.read().await;
let location_guard = self.locations.read().await;
legacy_mixnodes
.all_mixnodes
.values()
.map(|bond| {
let location = location_guard.get(&bond.mix_id());
self.create_detailed_mixnode(bond.mix_id(), &legacy_mixnodes, location, bond)
})
.collect()
}
pub(crate) async fn update_cache(
&self,
all_bonds: Vec<MixNodeBondAnnotated>,
rewarded_nodes: HashSet<NodeId>,
active_nodes: HashSet<NodeId>,
legacy_mixnode_bonds: Vec<MixNodeBond>,
) {
let mut guard = self.mixnodes.write().await;
let mut guard_legacy_mixnodes = self.legacy_mixnode_bonds.write().await;
let legacy_mixnode_bond_ids: Vec<&NodeId> = legacy_mixnode_bonds
.iter()
.map(|bond| &bond.mix_id)
.collect();
guard.all_mixnodes = all_bonds
.into_iter()
.map(|bond| (bond.mix_id(), bond))
@@ -202,5 +227,11 @@ impl ThreadsafeMixNodesCache {
guard.rewarded_mixnodes = rewarded_nodes;
guard.active_mixnodes = active_nodes;
guard.valid_until = SystemTime::now() + CACHE_ENTRY_TTL;
guard_legacy_mixnodes.all_mixnodes = guard
.all_mixnodes
.clone()
.into_iter()
.filter(|(node_id, _bond)| legacy_mixnode_bond_ids.iter().any(|i| **i == *node_id))
.collect();
}
}
-26
View File
@@ -1,26 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::state::ExplorerApiStateContext;
use nym_explorer_api_requests::PrettyDetailedGatewayBond;
use okapi::openapi3::OpenApi;
use rocket::serde::json::Json;
use rocket::{Route, State};
use rocket_okapi::settings::OpenApiSettings;
pub fn unstable_temp_nymnodes_make_default_routes(
settings: &OpenApiSettings,
) -> (Vec<Route>, OpenApi) {
openapi_get_routes_spec![settings: all_gateways]
}
#[openapi(tag = "UNSTABLE")]
#[get("/gateways")]
pub(crate) async fn all_gateways(
state: &State<ExplorerApiStateContext>,
) -> Json<Vec<PrettyDetailedGatewayBond>> {
let mut gateways = state.inner.gateways.get_detailed_gateways().await;
gateways.append(&mut state.inner.nymnodes.pretty_gateways().await);
Json(gateways)
}
+34 -1
View File
@@ -1,3 +1,4 @@
use nym_validator_client::models::NymNodeData;
use rocket::serde::json::Json;
use rocket::{Route, State};
use rocket_okapi::okapi::openapi3::OpenApi;
@@ -5,19 +6,51 @@ use rocket_okapi::openapi_get_routes_spec;
use rocket_okapi::settings::OpenApiSettings;
use crate::mix_nodes::http::get_mixnode_summary;
use crate::overview::models::OverviewSummary;
use crate::overview::models::{NymNodeSummary, OverviewSummary, RoleSummary};
use crate::state::ExplorerApiStateContext;
pub fn overview_make_default_routes(settings: &OpenApiSettings) -> (Vec<Route>, OpenApi) {
openapi_get_routes_spec![settings: summary]
}
fn get_nym_nodes_by_role(nodes: &Vec<NymNodeData>) -> RoleSummary {
let mut summary = RoleSummary::default();
for node in nodes {
if node.declared_role.entry {
summary.entry += 1;
}
if node.declared_role.exit_ipr {
summary.exit_ipr += 1;
}
if node.declared_role.exit_nr {
summary.exit_nr += 1;
}
if node.declared_role.mixnode {
summary.mixnode += 1;
}
}
summary
}
#[openapi(tag = "overview")]
#[get("/summary")]
pub(crate) async fn summary(state: &State<ExplorerApiStateContext>) -> Json<OverviewSummary> {
let nym_nodes = state
.inner
.nymnodes
.get_bonded_nymnodes_descriptions()
.await;
let roles = get_nym_nodes_by_role(&nym_nodes);
Json(OverviewSummary {
mixnodes: get_mixnode_summary(state).await,
validators: state.inner.validators.get_validator_summary().await,
gateways: state.inner.gateways.get_gateway_summary().await,
nymnodes: NymNodeSummary {
count: nym_nodes.len(),
roles,
},
})
}
+15
View File
@@ -7,9 +7,24 @@ use crate::gateways::models::GatewaySummary;
use crate::mix_nodes::models::MixNodeSummary;
use crate::validators::models::ValidatorSummary;
#[derive(Clone, Debug, Serialize, JsonSchema, Default)]
pub(crate) struct RoleSummary {
pub mixnode: usize,
pub entry: usize,
pub exit_nr: usize,
pub exit_ipr: usize,
}
#[derive(Clone, Debug, Serialize, JsonSchema, Default)]
pub(crate) struct NymNodeSummary {
pub count: usize,
pub roles: RoleSummary,
}
#[derive(Clone, Debug, Serialize, JsonSchema)]
pub(crate) struct OverviewSummary {
pub mixnodes: MixNodeSummary,
pub gateways: GatewaySummary,
pub validators: ValidatorSummary,
pub nymnodes: NymNodeSummary,
}
+153 -3
View File
@@ -3,12 +3,18 @@ use std::path::Path;
use chrono::{DateTime, Utc};
use log::info;
use nym_mixnet_contract_common::NodeId;
use nym_explorer_api_requests::NymVestingAccount;
use nym_mixnet_contract_common::{Addr, Delegation, NodeId, PendingRewardResponse};
use serde::{Deserialize, Serialize};
use crate::client::ThreadsafeValidatorClient;
use crate::geo_ip::location::ThreadsafeGeoIp;
use nym_mixnet_contract_common::Coin as CosmWasmCoin;
use nym_validator_client::models::MixNodeBondAnnotated;
use nym_validator_client::nyxd::contract_traits::{
MixnetQueryClient, PagedMixnetQueryClient, VestingQueryClient,
};
use nym_validator_client::nyxd::{AccountId, Coin, CosmWasmClient};
use crate::country_statistics::country_nodes_distribution::{
CountryNodesDistribution, ThreadsafeCountryNodesDistribution,
@@ -18,9 +24,9 @@ use crate::gateways::models::ThreadsafeGatewayCache;
use crate::mix_node::models::ThreadsafeMixNodeCache;
use crate::mix_nodes::location::MixnodeLocationCache;
use crate::mix_nodes::models::ThreadsafeMixNodesCache;
use crate::nym_nodes::location::NymNodeLocationCache;
use crate::nym_nodes::models::ThreadSafeNymNodesCache;
use crate::ping::models::ThreadsafePingCache;
use crate::unstable::location::NymNodeLocationCache;
use crate::unstable::models::ThreadSafeNymNodesCache;
use crate::validators::models::ThreadsafeValidatorCache;
// TODO: change to an environment variable with a default value
@@ -45,6 +51,150 @@ impl ExplorerApiState {
pub(crate) async fn get_mix_node(&self, mix_id: NodeId) -> Option<MixNodeBondAnnotated> {
self.mixnodes.get_mixnode(mix_id).await
}
pub(crate) async fn get_delegations_by_node(
&self,
node_id: NodeId,
) -> Result<Vec<Delegation>, rocket::response::status::NotFound<String>> {
match self
.validator_client
.0
.nyxd
.get_all_single_mixnode_delegations(node_id)
.await
{
Ok(res) => Ok(res),
Err(e) => Err(rocket::response::status::NotFound(format!("{}", e))),
}
}
pub(crate) async fn get_balance(
&self,
addr: &AccountId,
) -> Result<Vec<Coin>, rocket::response::status::NotFound<String>> {
match self.validator_client.0.nyxd.get_all_balances(addr).await {
Ok(res) => Ok(res),
Err(e) => Err(rocket::response::status::NotFound(format!("{}", e))),
}
}
pub(crate) async fn get_vesting_balance(
&self,
addr: &AccountId,
) -> Result<Option<NymVestingAccount>, rocket::response::status::NotFound<String>> {
match nym_validator_client::nyxd::contract_traits::VestingQueryClient::get_account(
&self.validator_client.0.nyxd,
addr.as_ref(),
)
.await
{
// 1. is there a vesting account?
Ok(_res) => {
// 2. there is vesting account, get all the coins
let mut locked = CosmWasmCoin::default();
let mut vested = CosmWasmCoin::default();
let mut vesting = CosmWasmCoin::default();
let mut spendable = CosmWasmCoin::default();
// 3. try to get each coin type
if let Ok(coin) = self
.validator_client
.0
.nyxd
.locked_coins(addr.as_ref(), None)
.await
{
locked = coin.into();
}
if let Ok(coin) = self
.validator_client
.0
.nyxd
.vested_coins(addr.as_ref(), None)
.await
{
vested = coin.into();
}
if let Ok(coin) = self
.validator_client
.0
.nyxd
.vesting_coins(addr.as_ref(), None)
.await
{
vesting = coin.into();
}
if let Ok(coin) = self
.validator_client
.0
.nyxd
.spendable_coins(addr.as_ref(), None)
.await
{
spendable = coin.into();
}
// 4.combine into a response
Ok(Some(NymVestingAccount {
locked,
vested,
vesting,
spendable,
}))
}
Err(e) => Err(rocket::response::status::NotFound(format!("{}", e))),
}
}
pub(crate) async fn get_delegations(
&self,
addr: &AccountId,
) -> Result<Vec<Delegation>, rocket::response::status::NotFound<String>> {
match self
.validator_client
.0
.nyxd
.get_all_delegator_delegations(addr)
.await
{
Ok(res) => Ok(res),
Err(e) => Err(rocket::response::status::NotFound(format!("{}", e))),
}
}
pub(crate) async fn get_delegation_rewards(
&self,
addr: &AccountId,
node_id: &NodeId,
proxy: &Option<Addr>,
) -> Result<PendingRewardResponse, rocket::response::status::NotFound<String>> {
match self
.validator_client
.0
.nyxd
.get_pending_delegator_reward(addr, *node_id, proxy.clone().map(|d| d.to_string()))
.await
{
Ok(res) => Ok(res),
Err(e) => Err(rocket::response::status::NotFound(format!("{}", e))),
}
}
pub(crate) async fn get_operator_rewards(
&self,
addr: &AccountId,
) -> Result<PendingRewardResponse, rocket::response::status::NotFound<String>> {
match self
.validator_client
.0
.nyxd
.get_pending_operator_reward(addr)
.await
{
Ok(res) => Ok(res),
Err(e) => Err(rocket::response::status::NotFound(format!("{}", e))),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
+46 -6
View File
@@ -3,9 +3,12 @@
use crate::mix_nodes::CACHE_REFRESH_RATE;
use crate::state::ExplorerApiStateContext;
use nym_mixnet_contract_common::{GatewayBond, NymNodeDetails};
use nym_mixnet_contract_common::{GatewayBond, MixNodeBond, NymNodeDetails};
use nym_task::TaskClient;
use nym_validator_client::models::{MixNodeBondAnnotated, NymNodeDescription};
use nym_validator_client::models::{
GatewayBondAnnotated, MixNodeBondAnnotated, NymNodeDescription,
};
use nym_validator_client::nyxd::contract_traits::PagedMixnetQueryClient;
use nym_validator_client::nyxd::error::NyxdError;
use nym_validator_client::nyxd::{Paging, TendermintRpcClient, ValidatorResponse};
use nym_validator_client::{QueryHttpRpcValidatorClient, ValidatorClientError};
@@ -71,13 +74,15 @@ impl ExplorerApiTasks {
.await
}
async fn retrieve_all_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorClientError> {
async fn retrieve_all_gateways(
&self,
) -> Result<Vec<GatewayBondAnnotated>, ValidatorClientError> {
info!("About to retrieve all gateways...");
self.state
.inner
.validator_client
.0
.get_cached_gateways()
.get_cached_gateways_detailed_unfiltered()
.await
}
@@ -115,7 +120,30 @@ impl ExplorerApiTasks {
.await
}
async fn retrieve_legacy_gateway_bonds(&self) -> Vec<GatewayBond> {
self.state
.inner
.validator_client
.0
.nyxd
.get_all_gateways()
.await
.unwrap_or(vec![])
}
async fn retrieve_legacy_mixnode_bonds(&self) -> Vec<MixNodeBond> {
self.state
.inner
.validator_client
.0
.nyxd
.get_all_mixnode_bonds()
.await
.unwrap_or(vec![])
}
async fn update_mixnode_cache(&self) {
let legacy_mixnode_bonds = self.retrieve_legacy_mixnode_bonds().await;
let all_bonds = self.retrieve_all_mixnodes().await;
let rewarded_nodes = self
.retrieve_rewarded_mixnodes()
@@ -132,7 +160,12 @@ impl ExplorerApiTasks {
self.state
.inner
.mixnodes
.update_cache(all_bonds, rewarded_nodes, active_nodes)
.update_cache(
all_bonds,
rewarded_nodes,
active_nodes,
legacy_mixnode_bonds,
)
.await;
}
@@ -146,8 +179,15 @@ impl ExplorerApiTasks {
}
async fn update_gateways_cache(&self) {
let legacy_gateway_bonds = self.retrieve_legacy_gateway_bonds().await;
match self.retrieve_all_gateways().await {
Ok(response) => self.state.inner.gateways.update_cache(response).await,
Ok(response) => {
self.state
.inner
.gateways
.update_cache(response, legacy_gateway_bonds)
.await
}
Err(err) => {
error!("Failed to get gateways: {err}")
}

Some files were not shown because too many files have changed in this diff Show More