Merge branch 'release/2024.13-magura' into merge/release/2024.13-magura
This commit is contained in:
@@ -55,6 +55,7 @@ jobs:
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
override: true
|
||||
|
||||
- name: Build all binaries
|
||||
uses: actions-rs/cargo@v1
|
||||
|
||||
@@ -14,13 +14,14 @@ jobs:
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
toolchain: 1.77
|
||||
target: wasm32-unknown-unknown
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Install wasm-opt
|
||||
run: cargo install --version 0.114.0 wasm-opt
|
||||
uses: ./.github/actions/install-wasm-opt
|
||||
with:
|
||||
version: '114'
|
||||
|
||||
- name: Build release contracts
|
||||
run: make contracts
|
||||
|
||||
@@ -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
|
||||
+192
@@ -4,6 +4,198 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [2024.13-magura] (2024-11-18)
|
||||
|
||||
- Limit race probability ([#5145])
|
||||
- bugifx: assign 'node_id' when converting from 'GatewayDetails' to 'TestNode' ([#5143])
|
||||
- bugfix: make sure to assign correct node_id and identity during 'gateway_details' table migration ([#5142])
|
||||
- Respond to auth messages with same version ([#5140])
|
||||
- Pain/polyfill deprecated endpoints ([#5131])
|
||||
- change: dont select mixnodes bonded with vested tokens into the rewarded set ([#5129])
|
||||
- nym-credential-proxy-requests: reqwest use rustls-tls ([#5116])
|
||||
- bugfix: preserve as much as possible of the rewarded set during migration ([#5103])
|
||||
- Feature/force refresh node ([#5101])
|
||||
- Add NYM_VPN_API to env files ([#5099])
|
||||
- bugfix: fixed historical uptimes for nodes ([#5097])
|
||||
- Remove old use of 1GB constant ([#5096])
|
||||
- Graceful agent 1.1.5 ([#5093])
|
||||
- Add more translations from v2 to v3 authenticator ([#5091])
|
||||
- Nym node - Fix claim delegator rewards ([#5090])
|
||||
- Make 250 GB/30 days for free ride mode ([#5083])
|
||||
- Don't increase bandwidth two times ([#5081])
|
||||
- Fix expiration date as today + 7 days ([#5076])
|
||||
- Fix gateway decreasing bandwidth ([#5075])
|
||||
- Allow custom http port to be reset ([#5073])
|
||||
- bugfix: additional checks inside credential proxy ([#5072])
|
||||
- chore: deprecated old nym-api client methods and replaced them when possible ([#5069])
|
||||
- NS API with directory v2 (#5058) ([#5068])
|
||||
- bugfix: credential-proxy obtain-async ([#5067])
|
||||
- Allow nym node config updates ([#5066])
|
||||
- bugfix: use corrext axum extractors for ecash route arguments ([#5065])
|
||||
- Merge2/release/2024.13 magura ([#5063])
|
||||
- bugfix/feature: added NymApiClient method to get all skimmed nodes ([#5062])
|
||||
- Merge1/release/2024.13 magura ([#5061])
|
||||
- added hacky routes to return nymnodes alongside legacy nodes ([#5051])
|
||||
- bugfix: mark migrated gateways as rewarded in the previous epoch in case theyre in the rewarded set ([#5049])
|
||||
- bugfix: adjust runtime storage migration ([#5047])
|
||||
- bugfix: supersede 'cb13be27f8f61d9ae74d924e85d2e6787895eb14' by using… ([#5046])
|
||||
- bugfix: restore default http port for nym-api ([#5045])
|
||||
- bugfix: fix ecash handlers routes ([#5043])
|
||||
- bugfix: don't assign exit gateways to standby set ([#5041])
|
||||
- bugfix: make sure nym-nodes are also tested by network monitor ([#5040])
|
||||
- bugfix: use bonded nym-nodes for determining initial network monitor … ([#5039])
|
||||
- bugfix: make gateways insert themselves into [local] topology ([#5038])
|
||||
- Pass poisson flag ([#5037])
|
||||
- bugfix: use human readable roles for annotations ([#5036])
|
||||
- bugfix: use old name for 'epoch_role' in SkimmedNode ([#5034])
|
||||
- bugfix: make sure to use correct highest node id when assigning role ([#5032])
|
||||
- feature: use axum_client_ip for attempting to extract source ip ([#5031])
|
||||
- bugfix: fixed backwards incompatibility for /gateways/described endpoint ([#5030])
|
||||
- bugfix: verifying signed information of legacy nodes ([#5029])
|
||||
- bugfix: introduce 'LegacyPendingMixNodeChanges' that does not contain 'cost_params_change' ([#5028])
|
||||
- bugfix: missing #[serde(default)] for announce port ([#5024])
|
||||
- bugfix: directory v2.1 `get_all_avg_gateway_reliability_in_interval` query ([#5023])
|
||||
- added 'get_all_described_nodes' to NymApiClient and adjusted return t… ([#5016])
|
||||
- Reapply fixes to new branch ([#5014])
|
||||
- Consume only positive bandwidth ([#5013])
|
||||
- feature: adjusted ticket sizes to the agreed amounts ([#5009])
|
||||
- Push private ip before inserting ([#5008])
|
||||
- chore: update itertools in compact ecash ([#4994])
|
||||
- feature: make accepting t&c a hard requirement for rewarded set selection ([#4993])
|
||||
- Fix rustfmt in nym-credential-proxy ([#4992])
|
||||
- bugfix: client memory leak ([#4991])
|
||||
- Eliminate 0 bandwidth race check ([#4988])
|
||||
- [DOCs;/operators]: Release notes for v2024.12 aero ([#4984])
|
||||
- Add topup req constructor ([#4983])
|
||||
- Fix critical issues SI86 and SI87 from Cure53 ([#4982])
|
||||
- Rename nym-vpn-api to nym-credential-proxy ([#4981])
|
||||
- enable global ecash routes even if api is not a signer ([#4980])
|
||||
- resolve beta clippy issues in contracts ([#4978])
|
||||
- Re-enable vested delegation migration ([#4977])
|
||||
- feature: require reporting using nym-node binary for rewarded set selection ([#4976])
|
||||
- Top up bandwidth ([#4975])
|
||||
- [Product Data] Add session type based on ecash ticket received ([#4974])
|
||||
- Bugfix/additional directory fixes ([#4973])
|
||||
- feat: add Dockerfile for nym node ([#4972])
|
||||
- chore: remove unused rocket code ([#4968])
|
||||
- Import nym-vpn-api crates ([#4967])
|
||||
- feature: importer-cli to correctly handle mixnet/vesting import ([#4966])
|
||||
- bugfix: fix expected return type on /v1/gateways endpoint ([#4965])
|
||||
- [Product Data] First step in gateway usage data collection ([#4963])
|
||||
- Bump sqlx to 0.7.4 ([#4959])
|
||||
- Add env feature to clap and make clap parameters available as env variables ([#4957])
|
||||
- Feature/contract state tools ([#4954])
|
||||
- expose authenticator address along other address in node-details ([#4953])
|
||||
- Extract packet processing from mixnode-common ([#4949])
|
||||
- nym-api container ([#4948])
|
||||
- Ticket type storage ([#4947])
|
||||
- Add "utoipa" feature to nym-node ([#4945])
|
||||
- build(deps): bump the patch-updates group across 1 directory with 9 updates ([#4944])
|
||||
- V2 performance monitoring feature flag ([#4943])
|
||||
- Bugfix/rewarder post pruning adjustments ([#4942])
|
||||
- Switch over the last set of jobs to arc runners ([#4938])
|
||||
- Fix broken build after merge ([#4937])
|
||||
- bugfix: correctly paginate through 'search_tx' endpoint ([#4936])
|
||||
- Add more conversions for responses of authenticator messages ([#4929])
|
||||
- Directory Sevices v2.1 ([#4903])
|
||||
- Migrate Legacy Node (Frontend) ([#4826])
|
||||
- Fix critical issues SI84 and SI85 from Cure53 ([#4758])
|
||||
|
||||
[#5145]: https://github.com/nymtech/nym/pull/5145
|
||||
[#5143]: https://github.com/nymtech/nym/pull/5143
|
||||
[#5142]: https://github.com/nymtech/nym/pull/5142
|
||||
[#5140]: https://github.com/nymtech/nym/pull/5140
|
||||
[#5131]: https://github.com/nymtech/nym/pull/5131
|
||||
[#5129]: https://github.com/nymtech/nym/pull/5129
|
||||
[#5116]: https://github.com/nymtech/nym/pull/5116
|
||||
[#5103]: https://github.com/nymtech/nym/pull/5103
|
||||
[#5101]: https://github.com/nymtech/nym/pull/5101
|
||||
[#5099]: https://github.com/nymtech/nym/pull/5099
|
||||
[#5097]: https://github.com/nymtech/nym/pull/5097
|
||||
[#5096]: https://github.com/nymtech/nym/pull/5096
|
||||
[#5093]: https://github.com/nymtech/nym/pull/5093
|
||||
[#5091]: https://github.com/nymtech/nym/pull/5091
|
||||
[#5090]: https://github.com/nymtech/nym/pull/5090
|
||||
[#5083]: https://github.com/nymtech/nym/pull/5083
|
||||
[#5081]: https://github.com/nymtech/nym/pull/5081
|
||||
[#5076]: https://github.com/nymtech/nym/pull/5076
|
||||
[#5075]: https://github.com/nymtech/nym/pull/5075
|
||||
[#5073]: https://github.com/nymtech/nym/pull/5073
|
||||
[#5072]: https://github.com/nymtech/nym/pull/5072
|
||||
[#5069]: https://github.com/nymtech/nym/pull/5069
|
||||
[#5068]: https://github.com/nymtech/nym/pull/5068
|
||||
[#5067]: https://github.com/nymtech/nym/pull/5067
|
||||
[#5066]: https://github.com/nymtech/nym/pull/5066
|
||||
[#5065]: https://github.com/nymtech/nym/pull/5065
|
||||
[#5063]: https://github.com/nymtech/nym/pull/5063
|
||||
[#5062]: https://github.com/nymtech/nym/pull/5062
|
||||
[#5061]: https://github.com/nymtech/nym/pull/5061
|
||||
[#5051]: https://github.com/nymtech/nym/pull/5051
|
||||
[#5049]: https://github.com/nymtech/nym/pull/5049
|
||||
[#5047]: https://github.com/nymtech/nym/pull/5047
|
||||
[#5046]: https://github.com/nymtech/nym/pull/5046
|
||||
[#5045]: https://github.com/nymtech/nym/pull/5045
|
||||
[#5043]: https://github.com/nymtech/nym/pull/5043
|
||||
[#5041]: https://github.com/nymtech/nym/pull/5041
|
||||
[#5040]: https://github.com/nymtech/nym/pull/5040
|
||||
[#5039]: https://github.com/nymtech/nym/pull/5039
|
||||
[#5038]: https://github.com/nymtech/nym/pull/5038
|
||||
[#5037]: https://github.com/nymtech/nym/pull/5037
|
||||
[#5036]: https://github.com/nymtech/nym/pull/5036
|
||||
[#5034]: https://github.com/nymtech/nym/pull/5034
|
||||
[#5032]: https://github.com/nymtech/nym/pull/5032
|
||||
[#5031]: https://github.com/nymtech/nym/pull/5031
|
||||
[#5030]: https://github.com/nymtech/nym/pull/5030
|
||||
[#5029]: https://github.com/nymtech/nym/pull/5029
|
||||
[#5028]: https://github.com/nymtech/nym/pull/5028
|
||||
[#5024]: https://github.com/nymtech/nym/pull/5024
|
||||
[#5023]: https://github.com/nymtech/nym/pull/5023
|
||||
[#5016]: https://github.com/nymtech/nym/pull/5016
|
||||
[#5014]: https://github.com/nymtech/nym/pull/5014
|
||||
[#5013]: https://github.com/nymtech/nym/pull/5013
|
||||
[#5009]: https://github.com/nymtech/nym/pull/5009
|
||||
[#5008]: https://github.com/nymtech/nym/pull/5008
|
||||
[#4994]: https://github.com/nymtech/nym/pull/4994
|
||||
[#4993]: https://github.com/nymtech/nym/pull/4993
|
||||
[#4992]: https://github.com/nymtech/nym/pull/4992
|
||||
[#4991]: https://github.com/nymtech/nym/pull/4991
|
||||
[#4988]: https://github.com/nymtech/nym/pull/4988
|
||||
[#4984]: https://github.com/nymtech/nym/pull/4984
|
||||
[#4983]: https://github.com/nymtech/nym/pull/4983
|
||||
[#4982]: https://github.com/nymtech/nym/pull/4982
|
||||
[#4981]: https://github.com/nymtech/nym/pull/4981
|
||||
[#4980]: https://github.com/nymtech/nym/pull/4980
|
||||
[#4978]: https://github.com/nymtech/nym/pull/4978
|
||||
[#4977]: https://github.com/nymtech/nym/pull/4977
|
||||
[#4976]: https://github.com/nymtech/nym/pull/4976
|
||||
[#4975]: https://github.com/nymtech/nym/pull/4975
|
||||
[#4974]: https://github.com/nymtech/nym/pull/4974
|
||||
[#4973]: https://github.com/nymtech/nym/pull/4973
|
||||
[#4972]: https://github.com/nymtech/nym/pull/4972
|
||||
[#4968]: https://github.com/nymtech/nym/pull/4968
|
||||
[#4967]: https://github.com/nymtech/nym/pull/4967
|
||||
[#4966]: https://github.com/nymtech/nym/pull/4966
|
||||
[#4965]: https://github.com/nymtech/nym/pull/4965
|
||||
[#4963]: https://github.com/nymtech/nym/pull/4963
|
||||
[#4959]: https://github.com/nymtech/nym/pull/4959
|
||||
[#4957]: https://github.com/nymtech/nym/pull/4957
|
||||
[#4954]: https://github.com/nymtech/nym/pull/4954
|
||||
[#4953]: https://github.com/nymtech/nym/pull/4953
|
||||
[#4949]: https://github.com/nymtech/nym/pull/4949
|
||||
[#4948]: https://github.com/nymtech/nym/pull/4948
|
||||
[#4947]: https://github.com/nymtech/nym/pull/4947
|
||||
[#4945]: https://github.com/nymtech/nym/pull/4945
|
||||
[#4944]: https://github.com/nymtech/nym/pull/4944
|
||||
[#4943]: https://github.com/nymtech/nym/pull/4943
|
||||
[#4942]: https://github.com/nymtech/nym/pull/4942
|
||||
[#4938]: https://github.com/nymtech/nym/pull/4938
|
||||
[#4937]: https://github.com/nymtech/nym/pull/4937
|
||||
[#4936]: https://github.com/nymtech/nym/pull/4936
|
||||
[#4929]: https://github.com/nymtech/nym/pull/4929
|
||||
[#4903]: https://github.com/nymtech/nym/pull/4903
|
||||
[#4826]: https://github.com/nymtech/nym/pull/4826
|
||||
[#4758]: https://github.com/nymtech/nym/pull/4758
|
||||
|
||||
## [2024.12-aero] (2024-10-17)
|
||||
|
||||
- nym-node: don't use bloomfilters for double spending checks ([#4960])
|
||||
|
||||
Generated
+11
-10
@@ -2428,7 +2428,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "explorer-api"
|
||||
version = "1.1.41"
|
||||
version = "1.1.42"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap 4.5.20",
|
||||
@@ -4451,7 +4451,7 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
||||
|
||||
[[package]]
|
||||
name = "nym-api"
|
||||
version = "1.1.45"
|
||||
version = "1.1.46"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -4700,7 +4700,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-cli"
|
||||
version = "1.1.43"
|
||||
version = "1.1.44"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
@@ -4781,7 +4781,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-client"
|
||||
version = "1.1.42"
|
||||
version = "1.1.43"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"clap 4.5.20",
|
||||
@@ -5895,7 +5895,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-requester"
|
||||
version = "1.1.43"
|
||||
version = "1.1.44"
|
||||
dependencies = [
|
||||
"addr",
|
||||
"anyhow",
|
||||
@@ -5946,7 +5946,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-node"
|
||||
version = "1.1.9"
|
||||
version = "1.1.10"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bip39",
|
||||
@@ -5973,6 +5973,7 @@ dependencies = [
|
||||
"nym-sphinx-addressing",
|
||||
"nym-task",
|
||||
"nym-types",
|
||||
"nym-validator-client",
|
||||
"nym-wireguard",
|
||||
"nym-wireguard-types",
|
||||
"rand",
|
||||
@@ -6048,7 +6049,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-node-status-agent"
|
||||
version = "0.1.4"
|
||||
version = "0.1.6"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.5.20",
|
||||
@@ -6064,7 +6065,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-node-status-api"
|
||||
version = "0.1.5"
|
||||
version = "0.1.6"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.7",
|
||||
@@ -6293,7 +6294,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.42"
|
||||
version = "1.1.43"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"clap 4.5.20",
|
||||
@@ -6895,7 +6896,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nymvisor"
|
||||
version = "0.1.8"
|
||||
version = "0.1.9"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.42"
|
||||
version = "1.1.43"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.42"
|
||||
version = "1.1.43"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
||||
edition = "2021"
|
||||
|
||||
@@ -22,4 +22,7 @@ pub enum Error {
|
||||
|
||||
#[error("conversion: {0}")]
|
||||
Conversion(String),
|
||||
|
||||
#[error("failed to serialize response packet: {source}")]
|
||||
FailedToSerializeResponsePacket { source: Box<bincode::ErrorKind> },
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod traits;
|
||||
pub mod v1;
|
||||
pub mod v2;
|
||||
pub mod v3;
|
||||
|
||||
@@ -0,0 +1,343 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
use nym_crypto::asymmetric::x25519::PrivateKey;
|
||||
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
|
||||
use crate::{
|
||||
v1, v2, v3,
|
||||
v4::{self, registration::IpPair},
|
||||
Error,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum AuthenticatorVersion {
|
||||
V1,
|
||||
V2,
|
||||
V3,
|
||||
V4,
|
||||
UNKNOWN,
|
||||
}
|
||||
|
||||
impl From<Protocol> for AuthenticatorVersion {
|
||||
fn from(value: Protocol) -> Self {
|
||||
if value.service_provider_type != ServiceProviderType::Authenticator {
|
||||
AuthenticatorVersion::UNKNOWN
|
||||
} else if value.version == v1::VERSION {
|
||||
AuthenticatorVersion::V1
|
||||
} else if value.version == v2::VERSION {
|
||||
AuthenticatorVersion::V2
|
||||
} else if value.version == v3::VERSION {
|
||||
AuthenticatorVersion::V3
|
||||
} else if value.version == v4::VERSION {
|
||||
AuthenticatorVersion::V4
|
||||
} else {
|
||||
AuthenticatorVersion::UNKNOWN
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait InitMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey;
|
||||
}
|
||||
|
||||
impl InitMessage for v1::registration::InitMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.pub_key
|
||||
}
|
||||
}
|
||||
|
||||
impl InitMessage for v2::registration::InitMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.pub_key
|
||||
}
|
||||
}
|
||||
|
||||
impl InitMessage for v3::registration::InitMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.pub_key
|
||||
}
|
||||
}
|
||||
|
||||
impl InitMessage for v4::registration::InitMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.pub_key
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FinalMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey;
|
||||
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error>;
|
||||
fn private_ips(&self) -> IpPair;
|
||||
fn credential(&self) -> Option<CredentialSpendingData>;
|
||||
}
|
||||
|
||||
impl FinalMessage for v1::GatewayClient {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.pub_key
|
||||
}
|
||||
|
||||
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
self.verify(private_key, nonce)
|
||||
}
|
||||
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.private_ip.into()
|
||||
}
|
||||
|
||||
fn credential(&self) -> Option<CredentialSpendingData> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl FinalMessage for v2::registration::FinalMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.gateway_client.pub_key
|
||||
}
|
||||
|
||||
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
self.gateway_client.verify(private_key, nonce)
|
||||
}
|
||||
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.gateway_client.private_ip.into()
|
||||
}
|
||||
|
||||
fn credential(&self) -> Option<CredentialSpendingData> {
|
||||
self.credential.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl FinalMessage for v3::registration::FinalMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.gateway_client.pub_key
|
||||
}
|
||||
|
||||
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
self.gateway_client.verify(private_key, nonce)
|
||||
}
|
||||
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.gateway_client.private_ip.into()
|
||||
}
|
||||
|
||||
fn credential(&self) -> Option<CredentialSpendingData> {
|
||||
self.credential.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl FinalMessage for v4::registration::FinalMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.gateway_client.pub_key
|
||||
}
|
||||
|
||||
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
self.gateway_client.verify(private_key, nonce)
|
||||
}
|
||||
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.gateway_client.private_ips
|
||||
}
|
||||
|
||||
fn credential(&self) -> Option<CredentialSpendingData> {
|
||||
self.credential.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait QueryBandwidthMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey;
|
||||
}
|
||||
|
||||
impl QueryBandwidthMessage for PeerPublicKey {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TopUpMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey;
|
||||
fn credential(&self) -> CredentialSpendingData;
|
||||
}
|
||||
|
||||
impl TopUpMessage for v3::topup::TopUpMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.pub_key
|
||||
}
|
||||
|
||||
fn credential(&self) -> CredentialSpendingData {
|
||||
self.credential.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl TopUpMessage for v4::topup::TopUpMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.pub_key
|
||||
}
|
||||
|
||||
fn credential(&self) -> CredentialSpendingData {
|
||||
self.credential.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub enum AuthenticatorRequest {
|
||||
Initial {
|
||||
msg: Box<dyn InitMessage + Send + Sync + 'static>,
|
||||
protocol: Protocol,
|
||||
reply_to: Recipient,
|
||||
request_id: u64,
|
||||
},
|
||||
Final {
|
||||
msg: Box<dyn FinalMessage + Send + Sync + 'static>,
|
||||
protocol: Protocol,
|
||||
reply_to: Recipient,
|
||||
request_id: u64,
|
||||
},
|
||||
QueryBandwidth {
|
||||
msg: Box<dyn QueryBandwidthMessage + Send + Sync + 'static>,
|
||||
protocol: Protocol,
|
||||
reply_to: Recipient,
|
||||
request_id: u64,
|
||||
},
|
||||
TopUpBandwidth {
|
||||
msg: Box<dyn TopUpMessage + Send + Sync + 'static>,
|
||||
protocol: Protocol,
|
||||
reply_to: Recipient,
|
||||
request_id: u64,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<v1::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
fn from(value: v1::request::AuthenticatorRequest) -> Self {
|
||||
match value.data {
|
||||
v1::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
|
||||
msg: Box::new(init_message),
|
||||
protocol: Protocol {
|
||||
version: value.version,
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
},
|
||||
reply_to: value.reply_to,
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v1::request::AuthenticatorRequestData::Final(gateway_client) => Self::Final {
|
||||
msg: Box::new(gateway_client),
|
||||
protocol: Protocol {
|
||||
version: value.version,
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
},
|
||||
reply_to: value.reply_to,
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v1::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
|
||||
Self::QueryBandwidth {
|
||||
msg: Box::new(peer_public_key),
|
||||
protocol: Protocol {
|
||||
version: value.version,
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
},
|
||||
reply_to: value.reply_to,
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
fn from(value: v2::request::AuthenticatorRequest) -> Self {
|
||||
match value.data {
|
||||
v2::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
|
||||
msg: Box::new(init_message),
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v2::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
|
||||
msg: final_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v2::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
|
||||
Self::QueryBandwidth {
|
||||
msg: Box::new(peer_public_key),
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v3::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
fn from(value: v3::request::AuthenticatorRequest) -> Self {
|
||||
match value.data {
|
||||
v3::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
|
||||
msg: Box::new(init_message),
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v3::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
|
||||
msg: final_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v3::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
|
||||
Self::QueryBandwidth {
|
||||
msg: Box::new(peer_public_key),
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
v3::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => {
|
||||
Self::TopUpBandwidth {
|
||||
msg: top_up_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
fn from(value: v4::request::AuthenticatorRequest) -> Self {
|
||||
match value.data {
|
||||
v4::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
|
||||
msg: Box::new(init_message),
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v4::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
|
||||
msg: final_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v4::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
|
||||
Self::QueryBandwidth {
|
||||
msg: Box::new(peer_public_key),
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
v4::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => {
|
||||
Self::TopUpBandwidth {
|
||||
msg: top_up_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -98,6 +98,16 @@ impl TryFrom<v3::response::AuthenticatorResponse> for v2::response::Authenticato
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::response::AuthenticatorResponse> for v3::response::AuthenticatorResponse {
|
||||
fn from(value: v2::response::AuthenticatorResponse) -> Self {
|
||||
Self {
|
||||
protocol: value.protocol,
|
||||
data: value.data.into(),
|
||||
reply_to: value.reply_to,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<v3::response::AuthenticatorResponseData> for v2::response::AuthenticatorResponseData {
|
||||
type Error = crate::Error;
|
||||
|
||||
@@ -129,6 +139,22 @@ impl TryFrom<v3::response::AuthenticatorResponseData> for v2::response::Authenti
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::response::AuthenticatorResponseData> for v3::response::AuthenticatorResponseData {
|
||||
fn from(value: v2::response::AuthenticatorResponseData) -> Self {
|
||||
match value {
|
||||
v2::response::AuthenticatorResponseData::PendingRegistration(
|
||||
pending_registration_response,
|
||||
) => Self::PendingRegistration(pending_registration_response.into()),
|
||||
v2::response::AuthenticatorResponseData::Registered(registered_response) => {
|
||||
Self::Registered(registered_response.into())
|
||||
}
|
||||
v2::response::AuthenticatorResponseData::RemainingBandwidth(
|
||||
remaining_bandwidth_response,
|
||||
) => Self::RemainingBandwidth(remaining_bandwidth_response.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v3::response::PendingRegistrationResponse> for v2::response::PendingRegistrationResponse {
|
||||
fn from(value: v3::response::PendingRegistrationResponse) -> Self {
|
||||
Self {
|
||||
@@ -139,6 +165,16 @@ impl From<v3::response::PendingRegistrationResponse> for v2::response::PendingRe
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::response::PendingRegistrationResponse> for v3::response::PendingRegistrationResponse {
|
||||
fn from(value: v2::response::PendingRegistrationResponse) -> Self {
|
||||
Self {
|
||||
request_id: value.request_id,
|
||||
reply_to: value.reply_to,
|
||||
reply: value.reply.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v3::response::RegisteredResponse> for v2::response::RegisteredResponse {
|
||||
fn from(value: v3::response::RegisteredResponse) -> Self {
|
||||
Self {
|
||||
@@ -149,6 +185,16 @@ impl From<v3::response::RegisteredResponse> for v2::response::RegisteredResponse
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::response::RegisteredResponse> for v3::response::RegisteredResponse {
|
||||
fn from(value: v2::response::RegisteredResponse) -> Self {
|
||||
Self {
|
||||
request_id: value.request_id,
|
||||
reply_to: value.reply_to,
|
||||
reply: value.reply.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v3::response::RemainingBandwidthResponse> for v2::response::RemainingBandwidthResponse {
|
||||
fn from(value: v3::response::RemainingBandwidthResponse) -> Self {
|
||||
Self {
|
||||
@@ -159,6 +205,16 @@ impl From<v3::response::RemainingBandwidthResponse> for v2::response::RemainingB
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::response::RemainingBandwidthResponse> for v3::response::RemainingBandwidthResponse {
|
||||
fn from(value: v2::response::RemainingBandwidthResponse) -> Self {
|
||||
Self {
|
||||
request_id: value.request_id,
|
||||
reply_to: value.reply_to,
|
||||
reply: value.reply.map(Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v3::registration::RegistrationData> for v2::registration::RegistrationData {
|
||||
fn from(value: v3::registration::RegistrationData) -> Self {
|
||||
Self {
|
||||
@@ -169,6 +225,16 @@ impl From<v3::registration::RegistrationData> for v2::registration::Registration
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::registration::RegistrationData> for v3::registration::RegistrationData {
|
||||
fn from(value: v2::registration::RegistrationData) -> Self {
|
||||
Self {
|
||||
nonce: value.nonce,
|
||||
gateway_data: value.gateway_data.into(),
|
||||
wg_port: value.wg_port,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v3::registration::RegistredData> for v2::registration::RegistredData {
|
||||
fn from(value: v3::registration::RegistredData) -> Self {
|
||||
Self {
|
||||
@@ -179,6 +245,16 @@ impl From<v3::registration::RegistredData> for v2::registration::RegistredData {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::registration::RegistredData> for v3::registration::RegistredData {
|
||||
fn from(value: v2::registration::RegistredData) -> Self {
|
||||
Self {
|
||||
pub_key: value.pub_key,
|
||||
private_ip: value.private_ip,
|
||||
wg_port: value.wg_port,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v3::registration::RemainingBandwidthData> for v2::registration::RemainingBandwidthData {
|
||||
fn from(value: v3::registration::RemainingBandwidthData) -> Self {
|
||||
Self {
|
||||
@@ -186,3 +262,11 @@ impl From<v3::registration::RemainingBandwidthData> for v2::registration::Remain
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::registration::RemainingBandwidthData> for v3::registration::RemainingBandwidthData {
|
||||
fn from(value: v2::registration::RemainingBandwidthData) -> Self {
|
||||
Self {
|
||||
available_bandwidth: value.available_bandwidth,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ pub type HmacSha256 = Hmac<Sha256>;
|
||||
pub type Nonce = u64;
|
||||
pub type Taken = Option<SystemTime>;
|
||||
|
||||
pub const BANDWIDTH_CAP_PER_DAY: u64 = 1024 * 1024 * 1024; // 1 GB
|
||||
pub const BANDWIDTH_CAP_PER_DAY: u64 = 250 * 1024 * 1024 * 1024; // 250 GB
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct InitMessage {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
|
||||
|
||||
use crate::{v3, v4};
|
||||
use crate::{v2, v3, v4};
|
||||
|
||||
impl From<v3::request::AuthenticatorRequest> for v4::request::AuthenticatorRequest {
|
||||
fn from(authenticator_request: v3::request::AuthenticatorRequest) -> Self {
|
||||
@@ -64,6 +64,16 @@ impl From<Box<v3::topup::TopUpMessage>> for Box<v4::topup::TopUpMessage> {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::registration::GatewayClient> for v4::registration::GatewayClient {
|
||||
fn from(gw_client: v2::registration::GatewayClient) -> Self {
|
||||
Self {
|
||||
pub_key: gw_client.pub_key,
|
||||
private_ips: gw_client.private_ip.into(),
|
||||
mac: gw_client.mac.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v3::registration::GatewayClient> for v4::registration::GatewayClient {
|
||||
fn from(gw_client: v3::registration::GatewayClient) -> Self {
|
||||
Self {
|
||||
@@ -84,6 +94,22 @@ impl From<v4::registration::GatewayClient> for v3::registration::GatewayClient {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::registration::GatewayClient> for v2::registration::GatewayClient {
|
||||
fn from(gw_client: v4::registration::GatewayClient) -> Self {
|
||||
Self {
|
||||
pub_key: gw_client.pub_key,
|
||||
private_ip: gw_client.private_ips.ipv4.into(),
|
||||
mac: gw_client.mac.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::registration::ClientMac> for v4::registration::ClientMac {
|
||||
fn from(mac: v2::registration::ClientMac) -> Self {
|
||||
Self::new(mac.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v3::registration::ClientMac> for v4::registration::ClientMac {
|
||||
fn from(mac: v3::registration::ClientMac) -> Self {
|
||||
Self::new(mac.to_vec())
|
||||
@@ -96,6 +122,12 @@ impl From<v4::registration::ClientMac> for v3::registration::ClientMac {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::registration::ClientMac> for v2::registration::ClientMac {
|
||||
fn from(mac: v4::registration::ClientMac) -> Self {
|
||||
Self::new(mac.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<v4::response::AuthenticatorResponse> for v3::response::AuthenticatorResponse {
|
||||
type Error = crate::Error;
|
||||
|
||||
|
||||
@@ -42,6 +42,12 @@ impl IpPair {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(Ipv4Addr, Ipv6Addr)> for IpPair {
|
||||
fn from((ipv4, ipv6): (Ipv4Addr, Ipv6Addr)) -> Self {
|
||||
IpPair { ipv4, ipv6 }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for IpPair {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "({}, {})", self.ipv4, self.ipv6)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub use crate::nym_api::NymApiClientExt;
|
||||
use crate::nyxd::{self, NyxdClient};
|
||||
use crate::signing::direct_wallet::DirectSecp256k1HdWallet;
|
||||
use crate::signing::signer::{NoSigner, OfflineSigner};
|
||||
@@ -32,6 +31,7 @@ use nym_network_defaults::NymNetworkDetails;
|
||||
use time::Date;
|
||||
use url::Url;
|
||||
|
||||
pub use crate::nym_api::NymApiClientExt;
|
||||
pub use nym_mixnet_contract_common::{
|
||||
mixnode::MixNodeDetails, GatewayBond, IdentityKey, IdentityKeyRef, NodeId, NymNodeDetails,
|
||||
};
|
||||
@@ -332,10 +332,10 @@ impl NymApiClient {
|
||||
NymApiClient { nym_api }
|
||||
}
|
||||
|
||||
pub fn new_with_user_agent(api_url: Url, user_agent: UserAgent) -> Self {
|
||||
pub fn new_with_user_agent(api_url: Url, user_agent: impl Into<UserAgent>) -> Self {
|
||||
let nym_api = nym_api::Client::builder::<_, ValidatorClientError>(api_url)
|
||||
.expect("invalid api url")
|
||||
.with_user_agent(user_agent)
|
||||
.with_user_agent(user_agent.into())
|
||||
.build::<ValidatorClientError>()
|
||||
.expect("failed to build nym api client");
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ use nym_api_requests::ecash::models::{
|
||||
use nym_api_requests::ecash::VerificationKeyResponse;
|
||||
use nym_api_requests::models::{
|
||||
AnnotationResponse, ApiHealthResponse, LegacyDescribedMixNode, NodePerformanceResponse,
|
||||
NymNodeDescription,
|
||||
NodeRefreshBody, NymNodeDescription,
|
||||
};
|
||||
use nym_api_requests::nym_nodes::PaginatedCachedNodesResponse;
|
||||
use nym_api_requests::pagination::PaginatedResponse;
|
||||
@@ -695,16 +695,32 @@ pub trait NymApiClientExt: ApiClient {
|
||||
&self,
|
||||
node_id: NodeId,
|
||||
) -> Result<NodePerformanceResponse, NymAPIError> {
|
||||
self.get_json_from(format!("/v1/nym-nodes/performance/{node_id}"))
|
||||
.await
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
"nym-nodes",
|
||||
"performance",
|
||||
&node_id.to_string(),
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_node_annotation(
|
||||
&self,
|
||||
node_id: NodeId,
|
||||
) -> Result<AnnotationResponse, NymAPIError> {
|
||||
self.get_json_from(format!("/v1/nym-nodes/annotation/{node_id}"))
|
||||
.await
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
"nym-nodes",
|
||||
"annotation",
|
||||
&node_id.to_string(),
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[deprecated]
|
||||
@@ -917,6 +933,18 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn force_refresh_describe_cache(
|
||||
&self,
|
||||
request: &NodeRefreshBody,
|
||||
) -> Result<(), NymAPIError> {
|
||||
self.post_json(
|
||||
&[routes::API_VERSION, "nym-nodes", "refresh-described"],
|
||||
NO_PARAMS,
|
||||
request,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn issued_ticketbooks_for(
|
||||
&self,
|
||||
expiration_date: Date,
|
||||
|
||||
@@ -10,10 +10,10 @@ use cosmrs::AccountId;
|
||||
use nym_contracts_common::signing::Nonce;
|
||||
use nym_mixnet_contract_common::gateway::{PreassignedGatewayIdsResponse, PreassignedId};
|
||||
use nym_mixnet_contract_common::nym_node::{
|
||||
EpochAssignmentResponse, NodeDetailsByIdentityResponse, NodeOwnershipResponse,
|
||||
NodeRewardingDetailsResponse, PagedNymNodeBondsResponse, PagedNymNodeDetailsResponse,
|
||||
PagedUnbondedNymNodesResponse, Role, RolesMetadataResponse, StakeSaturationResponse,
|
||||
UnbondedNodeResponse, UnbondedNymNode,
|
||||
EpochAssignmentResponse, NodeDetailsByIdentityResponse, NodeDetailsResponse,
|
||||
NodeOwnershipResponse, NodeRewardingDetailsResponse, PagedNymNodeBondsResponse,
|
||||
PagedNymNodeDetailsResponse, PagedUnbondedNymNodesResponse, Role, RolesMetadataResponse,
|
||||
StakeSaturationResponse, UnbondedNodeResponse, UnbondedNymNode,
|
||||
};
|
||||
use nym_mixnet_contract_common::reward_params::WorkFactor;
|
||||
use nym_mixnet_contract_common::{
|
||||
@@ -311,10 +311,7 @@ pub trait MixnetQueryClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_nymnode_details(
|
||||
&self,
|
||||
node_id: NodeId,
|
||||
) -> Result<NodeOwnershipResponse, NyxdError> {
|
||||
async fn get_nymnode_details(&self, node_id: NodeId) -> Result<NodeDetailsResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetNymNodeDetails { node_id })
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -315,6 +315,7 @@ impl Client {
|
||||
parse_response(res, true).await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
pub async fn get_json_endpoint<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
|
||||
where
|
||||
for<'a> T: Deserialize<'a>,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[repr(u8)]
|
||||
pub enum ServiceProviderType {
|
||||
NetworkRequester = 0,
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::deprecated::DelegationEvent;
|
||||
use crate::error::TypesError;
|
||||
use crate::mixnode::NodeCostParams;
|
||||
use cosmwasm_std::Decimal;
|
||||
use nym_mixnet_contract_common::{Delegation as MixnetContractDelegation, NodeId};
|
||||
use nym_mixnet_contract_common::{Delegation as MixnetContractDelegation, NodeId, NodeRewarding};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -70,6 +70,14 @@ pub struct DelegationWithEverything {
|
||||
pub mixnode_is_unbonding: Option<bool>,
|
||||
}
|
||||
|
||||
pub struct NodeInformation {
|
||||
pub owner: String,
|
||||
pub mix_id: NodeId,
|
||||
pub node_identity: String,
|
||||
pub rewarding_details: NodeRewarding,
|
||||
pub is_unbonding: bool,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
|
||||
@@ -7,8 +7,8 @@ use defguard_wireguard_rs::{
|
||||
WireguardInterfaceApi,
|
||||
};
|
||||
use futures::channel::oneshot;
|
||||
use nym_authenticator_requests::{
|
||||
latest::registration::RemainingBandwidthData, v1::registration::BANDWIDTH_CAP_PER_DAY,
|
||||
use nym_authenticator_requests::latest::registration::{
|
||||
RemainingBandwidthData, BANDWIDTH_CAP_PER_DAY,
|
||||
};
|
||||
use nym_credential_verification::{
|
||||
bandwidth_storage_manager::BandwidthStorageManager, BandwidthFlushingBehaviourConfig,
|
||||
@@ -194,6 +194,10 @@ impl<St: Storage + Clone + 'static> PeerController<St> {
|
||||
);
|
||||
self.bw_storage_managers
|
||||
.insert(peer.public_key.clone(), bandwidth_storage_manager);
|
||||
// try to immediately update the host information, to eliminate races
|
||||
if let Ok(host_information) = self.wg_api.inner.read_interface_data() {
|
||||
*self.host_information.write().await = host_information;
|
||||
}
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = handle.run().await {
|
||||
log::error!("Peer handle shut down ungracefully - {e}");
|
||||
@@ -230,7 +234,7 @@ impl<St: Storage + Clone + 'static> PeerController<St> {
|
||||
// host information not updated yet
|
||||
return Ok(None);
|
||||
};
|
||||
BANDWIDTH_CAP_PER_DAY.saturating_sub((peer.rx_bytes + peer.tx_bytes) as i64)
|
||||
BANDWIDTH_CAP_PER_DAY.saturating_sub(peer.rx_bytes + peer.tx_bytes) as i64
|
||||
};
|
||||
|
||||
Ok(Some(RemainingBandwidthData {
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::peer_controller::PeerControlRequest;
|
||||
use defguard_wireguard_rs::host::Peer;
|
||||
use defguard_wireguard_rs::{host::Host, key::Key};
|
||||
use futures::channel::oneshot;
|
||||
use nym_authenticator_requests::v2::registration::BANDWIDTH_CAP_PER_DAY;
|
||||
use nym_authenticator_requests::latest::registration::BANDWIDTH_CAP_PER_DAY;
|
||||
use nym_credential_verification::bandwidth_storage_manager::BandwidthStorageManager;
|
||||
use nym_gateway_storage::models::WireguardPeer;
|
||||
use nym_gateway_storage::Storage;
|
||||
@@ -18,7 +18,7 @@ use tokio::sync::{mpsc, RwLock};
|
||||
use tokio_stream::{wrappers::IntervalStream, StreamExt};
|
||||
|
||||
pub(crate) type SharedBandwidthStorageManager<St> = Arc<RwLock<BandwidthStorageManager<St>>>;
|
||||
const AUTO_REMOVE_AFTER: Duration = Duration::from_secs(60 * 60 * 24); // 24 hours
|
||||
const AUTO_REMOVE_AFTER: Duration = Duration::from_secs(60 * 60 * 24 * 30); // 30 days
|
||||
|
||||
pub struct PeerHandle<St> {
|
||||
storage: St,
|
||||
@@ -98,7 +98,7 @@ impl<St: Storage + Clone + 'static> PeerHandle<St> {
|
||||
} else {
|
||||
if SystemTime::now().duration_since(self.startup_timestamp)? >= AUTO_REMOVE_AFTER {
|
||||
log::debug!(
|
||||
"Peer {} has been present for 24 hours, removing it",
|
||||
"Peer {} has been present for 30 days, removing it",
|
||||
self.public_key.to_string()
|
||||
);
|
||||
let success = self.remove_peer().await?;
|
||||
|
||||
@@ -104,7 +104,10 @@ pub(crate) fn next_nymnode_id_counter(store: &mut dyn Storage) -> StdResult<Node
|
||||
}
|
||||
|
||||
pub(crate) fn initialise_storage(storage: &mut dyn Storage) -> Result<(), MixnetContractError> {
|
||||
ACTIVE_ROLES_BUCKET.save(storage, &RoleStorageBucket::default())?;
|
||||
let active_bucket = RoleStorageBucket::default();
|
||||
let inactive_bucket = active_bucket.other();
|
||||
|
||||
ACTIVE_ROLES_BUCKET.save(storage, &active_bucket)?;
|
||||
let roles = vec![
|
||||
Role::Layer1,
|
||||
Role::Layer2,
|
||||
@@ -114,24 +117,12 @@ pub(crate) fn initialise_storage(storage: &mut dyn Storage) -> Result<(), Mixnet
|
||||
Role::Standby,
|
||||
];
|
||||
for role in roles {
|
||||
ROLES.save(storage, (RoleStorageBucket::default() as u8, role), &vec![])?;
|
||||
ROLES.save(
|
||||
storage,
|
||||
(RoleStorageBucket::default().other() as u8, role),
|
||||
&vec![],
|
||||
)?
|
||||
ROLES.save(storage, (active_bucket as u8, role), &vec![])?;
|
||||
ROLES.save(storage, (inactive_bucket as u8, role), &vec![])?
|
||||
}
|
||||
|
||||
ROLES_METADATA.save(
|
||||
storage,
|
||||
RoleStorageBucket::default() as u8,
|
||||
&Default::default(),
|
||||
)?;
|
||||
ROLES_METADATA.save(
|
||||
storage,
|
||||
RoleStorageBucket::default().other() as u8,
|
||||
&Default::default(),
|
||||
)?;
|
||||
ROLES_METADATA.save(storage, active_bucket as u8, &Default::default())?;
|
||||
ROLES_METADATA.save(storage, inactive_bucket as u8, &Default::default())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+3
-2
@@ -18,6 +18,7 @@ GROUP_CONTRACT_ADDRESS=n1qg5ega6dykkxc307y25pecuufrjkxkaggkkxh7nad0vhyhtuhw3sa07
|
||||
MULTISIG_CONTRACT_ADDRESS=n1zwv6feuzhy6a9wekh96cd57lsarmqlwxdypdsplw6zhfncqw6ftqx5a364
|
||||
COCONUT_DKG_CONTRACT_ADDRESS=n1aakfpghcanxtc45gpqlx8j3rq0zcpyf49qmhm9mdjrfx036h4z5sy2vfh9
|
||||
|
||||
EXPLORER_API=https://canary-explorer.performance.nymte.ch/api
|
||||
EXPLORER_API=https://canary-explorer.performance.nymte.ch/api/
|
||||
NYXD=https://canary-validator.performance.nymte.ch
|
||||
NYM_API=https://canary-api.performance.nymte.ch/api
|
||||
NYM_API=https://canary-api.performance.nymte.ch/api/
|
||||
NYM_VPN_API=https://nym-vpn-api-git-deploy-canary-nyx-network-staging.vercel.app/api/
|
||||
|
||||
+3
-2
@@ -18,6 +18,7 @@ COCONUT_DKG_CONTRACT_ADDRESS=n1pk8jgr6y4c5k93gz7qf3xc0hvygmp7csk88c2tf8l39tkq683
|
||||
VESTING_CONTRACT_ADDRESS=n1jlzdxnyces4hrhqz68dqk28mrw5jgwtcfq0c2funcwrmw0dx9l9s8nnnvj
|
||||
REWARDING_VALIDATOR_ADDRESS=n1rfvpsynktze6wvn6ldskj8xgwfzzk5v6pnff39
|
||||
|
||||
EXPLORER_API=https://qa-network-explorer.qa.nymte.ch/api
|
||||
EXPLORER_API=https://qa-network-explorer.qa.nymte.ch/api/
|
||||
NYXD=https://qa-validator.qa.nymte.ch
|
||||
NYM_API=https://qa-nym-api.qa.nymte.ch/api
|
||||
NYM_API=https://qa-nym-api.qa.nymte.ch/api/
|
||||
NYM_VPN_API=https://nym-vpn-api-git-deploy-qa-nyx-network-staging.vercel.app/api/
|
||||
|
||||
+4
-3
@@ -19,7 +19,8 @@ COCONUT_DKG_CONTRACT_ADDRESS=n1v3n2ly2dp3a9ng3ff6rh26yfkn0pc5hed7w2shc5u9ca5c865
|
||||
ECASH_CONTRACT_ADDRESS=n1v3vydvs2ued84yv3khqwtgldmgwn0elljsdh08dr5s2j9x4rc5fs9jlwz9
|
||||
|
||||
STATISTICS_SERVICE_DOMAIN_ADDRESS="http://0.0.0.0"
|
||||
EXPLORER_API=https://sandbox-explorer.nymtech.net/api
|
||||
EXPLORER_API=https://sandbox-explorer.nymtech.net/api/
|
||||
NYXD=https://rpc.sandbox.nymtech.net
|
||||
NYXD_WS=wss://rpc.sandbox.nymtech.net/websocket
|
||||
NYM_API=https://sandbox-nym-api1.nymtech.net/api
|
||||
NYXD_WS=wss://rpc.sandbox.nymtech.net/websocket/
|
||||
NYM_API=https://sandbox-nym-api1.nymtech.net/api/
|
||||
NYM_VPN_API=https://nym-vpn-api-git-deploy-sandbox-nyx-network-staging.vercel.app/api/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "explorer-api"
|
||||
version = "1.1.41"
|
||||
version = "1.1.42"
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
|
||||
@@ -102,7 +102,6 @@ export const isLessThan = (a: number, b: number) => a < b;
|
||||
*/
|
||||
|
||||
export const isBalanceEnough = (fee: string, tx: string = '0', balance: string = '0') => {
|
||||
console.log('balance', balance, fee, tx);
|
||||
try {
|
||||
return Big(balance).gte(Big(fee).plus(Big(tx)));
|
||||
} catch (e) {
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
[package]
|
||||
name = "nym-api"
|
||||
license = "GPL-3.0"
|
||||
version = "1.1.45"
|
||||
version = "1.1.46"
|
||||
authors.workspace = true
|
||||
edition = "2021"
|
||||
rust-version.workspace = true
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use crate::helpers::unix_epoch;
|
||||
use crate::helpers::PlaceholderJsonSchemaImpl;
|
||||
use crate::legacy::{
|
||||
LegacyGatewayBondWithId, LegacyMixNodeBondWithLayer, LegacyMixNodeDetailsWithLayer,
|
||||
};
|
||||
@@ -1265,6 +1266,67 @@ pub struct NoiseDetails {
|
||||
pub ip_addresses: Vec<IpAddr>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
|
||||
pub struct NodeRefreshBody {
|
||||
#[serde(with = "bs58_ed25519_pubkey")]
|
||||
#[schemars(with = "String")]
|
||||
pub node_identity: ed25519::PublicKey,
|
||||
|
||||
// a poor man's nonce
|
||||
pub request_timestamp: i64,
|
||||
|
||||
#[schemars(with = "PlaceholderJsonSchemaImpl")]
|
||||
pub signature: ed25519::Signature,
|
||||
}
|
||||
|
||||
impl NodeRefreshBody {
|
||||
pub fn plaintext(node_identity: ed25519::PublicKey, request_timestamp: i64) -> Vec<u8> {
|
||||
node_identity
|
||||
.to_bytes()
|
||||
.into_iter()
|
||||
.chain(request_timestamp.to_be_bytes())
|
||||
.chain(b"describe-cache-refresh-request".iter().copied())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn new(private_key: &ed25519::PrivateKey) -> Self {
|
||||
let node_identity = private_key.public_key();
|
||||
let request_timestamp = OffsetDateTime::now_utc().unix_timestamp();
|
||||
let signature = private_key.sign(Self::plaintext(node_identity, request_timestamp));
|
||||
NodeRefreshBody {
|
||||
node_identity,
|
||||
request_timestamp,
|
||||
signature,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn verify_signature(&self) -> bool {
|
||||
self.node_identity
|
||||
.verify(
|
||||
Self::plaintext(self.node_identity, self.request_timestamp),
|
||||
&self.signature,
|
||||
)
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
pub fn is_stale(&self) -> bool {
|
||||
let Ok(encoded) = OffsetDateTime::from_unix_timestamp(self.request_timestamp) else {
|
||||
return true;
|
||||
};
|
||||
let now = OffsetDateTime::now_utc();
|
||||
|
||||
if encoded > now {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (encoded + Duration::from_secs(30)) < now {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -1264,6 +1264,7 @@ struct TestFixture {
|
||||
impl TestFixture {
|
||||
fn build_app_state(storage: NymApiStorage) -> AppState {
|
||||
AppState {
|
||||
forced_refresh: Default::default(),
|
||||
nym_contract_cache: NymContractCache::new(),
|
||||
node_status_cache: NodeStatusCache::new(),
|
||||
circulating_supply_cache: CirculatingSupplyCache::new("unym".to_owned()),
|
||||
|
||||
@@ -227,6 +227,11 @@ impl EpochAdvancer {
|
||||
continue;
|
||||
};
|
||||
|
||||
if mix.bond_information.proxy.is_some() {
|
||||
debug!("legacy mixnode {node_id} is using vested tokens");
|
||||
continue;
|
||||
}
|
||||
|
||||
let performance = annotation.detailed_performance.to_rewarding_performance();
|
||||
debug!(
|
||||
"legacy mixnode {}: stake: {total_stake}, performance: {performance}",
|
||||
|
||||
@@ -9,9 +9,10 @@ use crate::support::config;
|
||||
use crate::support::config::DEFAULT_NODE_DESCRIBE_BATCH_SIZE;
|
||||
use async_trait::async_trait;
|
||||
use futures::{stream, StreamExt};
|
||||
use nym_api_requests::legacy::{LegacyGatewayBondWithId, LegacyMixNodeDetailsWithLayer};
|
||||
use nym_api_requests::models::{DescribedNodeType, NymNodeData, NymNodeDescription};
|
||||
use nym_config::defaults::DEFAULT_NYM_NODE_HTTP_PORT;
|
||||
use nym_mixnet_contract_common::{LegacyMixLayer, NodeId};
|
||||
use nym_mixnet_contract_common::{LegacyMixLayer, NodeId, NymNodeDetails};
|
||||
use nym_node_requests::api::client::{NymNodeApiClientError, NymNodeApiClientExt};
|
||||
use nym_topology::gateway::GatewayConversionError;
|
||||
use nym_topology::mix::MixnodeConversionError;
|
||||
@@ -151,6 +152,10 @@ pub struct DescribedNodes {
|
||||
}
|
||||
|
||||
impl DescribedNodes {
|
||||
pub fn force_update(&mut self, node: NymNodeDescription) {
|
||||
self.nodes.insert(node.node_id, node);
|
||||
}
|
||||
|
||||
pub fn get_description(&self, node_id: &NodeId) -> Option<&NymNodeData> {
|
||||
self.nodes.get(node_id).map(|n| &n.description)
|
||||
}
|
||||
@@ -292,7 +297,7 @@ async fn try_get_description(
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct RefreshData {
|
||||
pub(crate) struct RefreshData {
|
||||
host: String,
|
||||
node_id: NodeId,
|
||||
node_type: DescribedNodeType,
|
||||
@@ -300,6 +305,39 @@ struct RefreshData {
|
||||
port: Option<u16>,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a LegacyMixNodeDetailsWithLayer> for RefreshData {
|
||||
fn from(node: &'a LegacyMixNodeDetailsWithLayer) -> Self {
|
||||
RefreshData::new(
|
||||
&node.bond_information.mix_node.host,
|
||||
DescribedNodeType::LegacyMixnode,
|
||||
node.mix_id(),
|
||||
Some(node.bond_information.mix_node.http_api_port),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a LegacyGatewayBondWithId> for RefreshData {
|
||||
fn from(node: &'a LegacyGatewayBondWithId) -> Self {
|
||||
RefreshData::new(
|
||||
&node.bond.gateway.host,
|
||||
DescribedNodeType::LegacyGateway,
|
||||
node.node_id,
|
||||
None,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a NymNodeDetails> for RefreshData {
|
||||
fn from(node: &'a NymNodeDetails) -> Self {
|
||||
RefreshData::new(
|
||||
&node.bond_information.node.host,
|
||||
DescribedNodeType::NymNode,
|
||||
node.node_id(),
|
||||
node.bond_information.node.custom_http_port,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl RefreshData {
|
||||
pub fn new(
|
||||
host: impl Into<String>,
|
||||
@@ -315,7 +353,11 @@ impl RefreshData {
|
||||
}
|
||||
}
|
||||
|
||||
async fn try_refresh(self) -> Option<NymNodeDescription> {
|
||||
pub(crate) fn node_id(&self) -> NodeId {
|
||||
self.node_id
|
||||
}
|
||||
|
||||
pub(crate) async fn try_refresh(self) -> Option<NymNodeDescription> {
|
||||
match try_get_description(self).await {
|
||||
Ok(description) => Some(description),
|
||||
Err(err) => {
|
||||
@@ -341,18 +383,13 @@ impl CacheItemProvider for NodeDescriptionProvider {
|
||||
// - legacy gateways (because they might already be running nym-nodes, but haven't updated contract info)
|
||||
// - nym-nodes
|
||||
|
||||
let mut nodes_to_query = Vec::new();
|
||||
let mut nodes_to_query: Vec<RefreshData> = Vec::new();
|
||||
|
||||
match self.contract_cache.all_cached_legacy_mixnodes().await {
|
||||
None => error!("failed to obtain mixnodes information from the cache"),
|
||||
Some(legacy_mixnodes) => {
|
||||
for node in &**legacy_mixnodes {
|
||||
nodes_to_query.push(RefreshData::new(
|
||||
&node.bond_information.mix_node.host,
|
||||
DescribedNodeType::LegacyMixnode,
|
||||
node.mix_id(),
|
||||
Some(node.bond_information.mix_node.http_api_port),
|
||||
))
|
||||
nodes_to_query.push(node.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -361,12 +398,7 @@ impl CacheItemProvider for NodeDescriptionProvider {
|
||||
None => error!("failed to obtain gateways information from the cache"),
|
||||
Some(legacy_gateways) => {
|
||||
for node in &**legacy_gateways {
|
||||
nodes_to_query.push(RefreshData::new(
|
||||
&node.bond.gateway.host,
|
||||
DescribedNodeType::LegacyGateway,
|
||||
node.node_id,
|
||||
None,
|
||||
))
|
||||
nodes_to_query.push(node.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -375,12 +407,7 @@ impl CacheItemProvider for NodeDescriptionProvider {
|
||||
None => error!("failed to obtain nym-nodes information from the cache"),
|
||||
Some(nym_nodes) => {
|
||||
for node in &**nym_nodes {
|
||||
nodes_to_query.push(RefreshData::new(
|
||||
&node.bond_information.node.host,
|
||||
DescribedNodeType::NymNode,
|
||||
node.node_id(),
|
||||
node.bond_information.node.custom_http_port,
|
||||
))
|
||||
nodes_to_query.push(node.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,6 +356,13 @@ impl AxumErrorResponse {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn unauthorised(msg: impl Display) -> Self {
|
||||
Self {
|
||||
message: RequestError::new(msg.to_string()),
|
||||
status: StatusCode::UNAUTHORIZED,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn unprocessable_entity(msg: impl Display) -> Self {
|
||||
Self {
|
||||
message: RequestError::new(msg.to_string()),
|
||||
@@ -376,6 +383,13 @@ impl AxumErrorResponse {
|
||||
status: StatusCode::BAD_REQUEST,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn too_many(msg: impl Display) -> Self {
|
||||
Self {
|
||||
message: RequestError::new(msg.to_string()),
|
||||
status: StatusCode::TOO_MANY_REQUESTS,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UninitialisedCache> for AxumErrorResponse {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::node_status_api::models::{
|
||||
@@ -9,7 +9,7 @@ use crate::storage::NymApiStorage;
|
||||
use nym_task::{TaskClient, TaskManager};
|
||||
use std::time::Duration;
|
||||
use time::{OffsetDateTime, PrimitiveDateTime, Time};
|
||||
use tokio::time::{interval, sleep};
|
||||
use tokio::time::{interval_at, Instant};
|
||||
use tracing::error;
|
||||
use tracing::{info, trace, warn};
|
||||
|
||||
@@ -93,15 +93,8 @@ impl HistoricalUptimeUpdater {
|
||||
"waiting until {update_datetime} to update the historical uptimes for the first time ({} seconds left)", time_left.as_secs()
|
||||
);
|
||||
|
||||
tokio::select! {
|
||||
biased;
|
||||
_ = shutdown.recv() => {
|
||||
trace!("UpdateHandler: Received shutdown");
|
||||
}
|
||||
_ = sleep(time_left) => {}
|
||||
}
|
||||
|
||||
let mut interval = interval(ONE_DAY);
|
||||
let start = Instant::now() + time_left;
|
||||
let mut interval = interval_at(start, ONE_DAY);
|
||||
while !shutdown.is_shutdown() {
|
||||
tokio::select! {
|
||||
biased;
|
||||
@@ -109,6 +102,7 @@ impl HistoricalUptimeUpdater {
|
||||
trace!("UpdateHandler: Received shutdown");
|
||||
}
|
||||
_ = interval.tick() => {
|
||||
info!("updating historical uptimes of nodes");
|
||||
// we don't want to have another select here; uptime update is relatively speedy
|
||||
// and we don't want to exit while we're in the middle of database update
|
||||
if let Err(err) = self.update_uptimes().await {
|
||||
|
||||
+44
@@ -1,6 +1,7 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::node_describe_cache::RefreshData;
|
||||
use crate::nym_contract_cache::cache::data::CachedContractsInfo;
|
||||
use crate::support::caching::Cache;
|
||||
use data::ContractCacheData;
|
||||
@@ -8,6 +9,7 @@ use nym_api_requests::legacy::{
|
||||
LegacyGatewayBondWithId, LegacyMixNodeBondWithLayer, LegacyMixNodeDetailsWithLayer,
|
||||
};
|
||||
use nym_api_requests::models::MixnodeStatus;
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_mixnet_contract_common::{
|
||||
ConfigScoreParams, Interval, NodeId, NymNodeDetails, RewardedSet, RewardingParams,
|
||||
};
|
||||
@@ -364,6 +366,48 @@ impl NymContractCache {
|
||||
self.legacy_mixnode_details(mix_id).await.1
|
||||
}
|
||||
|
||||
pub async fn get_node_refresh_data(
|
||||
&self,
|
||||
node_identity: ed25519::PublicKey,
|
||||
) -> Option<RefreshData> {
|
||||
if !self.initialised() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let inner = self.inner.read().await;
|
||||
|
||||
let encoded_identity = node_identity.to_base58_string();
|
||||
|
||||
// 1. check nymnodes
|
||||
if let Some(nym_node) = inner
|
||||
.nym_nodes
|
||||
.iter()
|
||||
.find(|n| n.bond_information.identity() == encoded_identity)
|
||||
{
|
||||
return Some(nym_node.into());
|
||||
}
|
||||
|
||||
// 2. check legacy mixnodes
|
||||
if let Some(mixnode) = inner
|
||||
.legacy_mixnodes
|
||||
.iter()
|
||||
.find(|n| n.bond_information.identity() == encoded_identity)
|
||||
{
|
||||
return Some(mixnode.into());
|
||||
}
|
||||
|
||||
// 3. check legacy gateways
|
||||
if let Some(gateway) = inner
|
||||
.legacy_gateways
|
||||
.iter()
|
||||
.find(|n| n.identity() == &encoded_identity)
|
||||
{
|
||||
return Some(gateway.into());
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn initialised(&self) -> bool {
|
||||
self.initialised.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
@@ -6,10 +6,12 @@ use crate::node_status_api::helpers::{
|
||||
_get_rewarded_set_legacy_mixnodes_detailed,
|
||||
};
|
||||
use crate::support::http::state::AppState;
|
||||
use crate::support::legacy_helpers::{to_legacy_gateway, to_legacy_mixnode};
|
||||
use axum::extract::State;
|
||||
use axum::{Json, Router};
|
||||
use nym_api_requests::legacy::LegacyMixNodeDetailsWithLayer;
|
||||
use nym_api_requests::models::MixNodeBondAnnotated;
|
||||
use nym_mixnet_contract_common::reward_params::Performance;
|
||||
use nym_mixnet_contract_common::{reward_params::RewardingParams, GatewayBond, Interval, NodeId};
|
||||
use std::collections::HashSet;
|
||||
|
||||
@@ -58,11 +60,47 @@ pub(crate) fn nym_contract_cache_routes() -> Router<AppState> {
|
||||
)]
|
||||
#[deprecated]
|
||||
async fn get_mixnodes(State(state): State<AppState>) -> Json<Vec<LegacyMixNodeDetailsWithLayer>> {
|
||||
state
|
||||
.nym_contract_cache()
|
||||
.legacy_mixnodes_filtered()
|
||||
.await
|
||||
.into()
|
||||
let mut out = state.nym_contract_cache().legacy_mixnodes_filtered().await;
|
||||
|
||||
let Ok(describe_cache) = state.described_nodes_cache.get().await else {
|
||||
return Json(out);
|
||||
};
|
||||
|
||||
let Some(migrated_nymnodes) = state.nym_contract_cache().all_cached_nym_nodes().await else {
|
||||
return Json(out);
|
||||
};
|
||||
|
||||
let Ok(annotations) = state.node_annotations().await else {
|
||||
return Json(out);
|
||||
};
|
||||
|
||||
// safety: valid percentage value
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let p50 = Performance::from_percentage_value(50).unwrap();
|
||||
|
||||
for nym_node in &**migrated_nymnodes {
|
||||
// if we can't get it self-described data, ignore it
|
||||
let Some(description) = describe_cache.get_description(&nym_node.node_id()) else {
|
||||
continue;
|
||||
};
|
||||
// if the node hasn't declared it can be a mixnode, ignore it
|
||||
if !description.declared_role.mixnode {
|
||||
continue;
|
||||
}
|
||||
// if we don't have annotation for this node, ignore it
|
||||
let Some(annotation) = annotations.get(&nym_node.node_id()) else {
|
||||
continue;
|
||||
};
|
||||
// equivalent of legacy mixnode being blacklisted
|
||||
if annotation.last_24h_performance < p50 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let node = to_legacy_mixnode(nym_node, description);
|
||||
out.push(node);
|
||||
}
|
||||
|
||||
Json(out)
|
||||
}
|
||||
|
||||
// DEPRECATED: this endpoint now lives in `node_status_api`. Once all consumers are updated,
|
||||
@@ -97,15 +135,54 @@ async fn get_mixnodes_detailed(State(state): State<AppState>) -> Json<Vec<MixNod
|
||||
)]
|
||||
#[deprecated]
|
||||
async fn get_gateways(State(state): State<AppState>) -> Json<Vec<GatewayBond>> {
|
||||
Json(
|
||||
state
|
||||
.nym_contract_cache()
|
||||
.legacy_gateways_filtered()
|
||||
.await
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect(),
|
||||
)
|
||||
// legacy
|
||||
let mut out: Vec<GatewayBond> = state
|
||||
.nym_contract_cache()
|
||||
.legacy_gateways_filtered()
|
||||
.await
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect();
|
||||
|
||||
let Ok(describe_cache) = state.described_nodes_cache.get().await else {
|
||||
return Json(out);
|
||||
};
|
||||
|
||||
let Some(migrated_nymnodes) = state.nym_contract_cache().all_cached_nym_nodes().await else {
|
||||
return Json(out);
|
||||
};
|
||||
|
||||
let Ok(annotations) = state.node_annotations().await else {
|
||||
return Json(out);
|
||||
};
|
||||
|
||||
// safety: valid percentage value
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let p50 = Performance::from_percentage_value(50).unwrap();
|
||||
|
||||
for nym_node in &**migrated_nymnodes {
|
||||
// if we can't get it self-described data, ignore it
|
||||
let Some(description) = describe_cache.get_description(&nym_node.node_id()) else {
|
||||
continue;
|
||||
};
|
||||
// if the node hasn't declared it can be a gateway, ignore it
|
||||
if !description.declared_role.entry {
|
||||
continue;
|
||||
}
|
||||
// if we don't have annotation for this node, ignore it
|
||||
let Some(annotation) = annotations.get(&nym_node.node_id()) else {
|
||||
continue;
|
||||
};
|
||||
// equivalent of legacy gateway being blacklisted
|
||||
if annotation.last_24h_performance < p50 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let node = to_legacy_gateway(nym_node, description);
|
||||
out.push(node);
|
||||
}
|
||||
|
||||
Json(out)
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
@@ -166,12 +243,59 @@ async fn get_rewarded_set_detailed(
|
||||
)]
|
||||
#[deprecated]
|
||||
async fn get_active_set(State(state): State<AppState>) -> Json<Vec<LegacyMixNodeDetailsWithLayer>> {
|
||||
state
|
||||
let mut out = state
|
||||
.nym_contract_cache()
|
||||
.legacy_v1_active_set_mixnodes()
|
||||
.await
|
||||
.clone()
|
||||
.into()
|
||||
.clone();
|
||||
|
||||
let Some(rewarded_set) = state.nym_contract_cache().rewarded_set().await else {
|
||||
return Json(out);
|
||||
};
|
||||
|
||||
let Ok(describe_cache) = state.described_nodes_cache.get().await else {
|
||||
return Json(out);
|
||||
};
|
||||
|
||||
let Some(migrated_nymnodes) = state.nym_contract_cache().all_cached_nym_nodes().await else {
|
||||
return Json(out);
|
||||
};
|
||||
|
||||
let Ok(annotations) = state.node_annotations().await else {
|
||||
return Json(out);
|
||||
};
|
||||
|
||||
// safety: valid percentage value
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let p50 = Performance::from_percentage_value(50).unwrap();
|
||||
|
||||
for nym_node in &**migrated_nymnodes {
|
||||
// if we can't get it self-described data, ignore it
|
||||
let Some(description) = describe_cache.get_description(&nym_node.node_id()) else {
|
||||
continue;
|
||||
};
|
||||
// if the node hasn't declared it can be a mixnode, ignore it
|
||||
if !description.declared_role.mixnode {
|
||||
continue;
|
||||
}
|
||||
// if we don't have annotation for this node, ignore it
|
||||
let Some(annotation) = annotations.get(&nym_node.node_id()) else {
|
||||
continue;
|
||||
};
|
||||
// equivalent of legacy mixnode being blacklisted
|
||||
if annotation.last_24h_performance < p50 {
|
||||
continue;
|
||||
}
|
||||
// if the node is not in the active set, ignore it
|
||||
if !rewarded_set.is_active_mixnode(&nym_node.node_id()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let node = to_legacy_mixnode(nym_node, description);
|
||||
out.push(node);
|
||||
}
|
||||
|
||||
Json(out)
|
||||
}
|
||||
|
||||
// DEPRECATED: this endpoint now lives in `node_status_api`. Once all consumers are updated,
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::support::http::state::AppState;
|
||||
use crate::support::legacy_helpers::{to_legacy_gateway, to_legacy_mixnode};
|
||||
use axum::extract::State;
|
||||
use axum::{Json, Router};
|
||||
use nym_api_requests::legacy::LegacyMixNodeBondWithLayer;
|
||||
use nym_api_requests::models::{LegacyDescribedGateway, LegacyDescribedMixNode};
|
||||
|
||||
// we want to mark the routes as deprecated in swagger, but still expose them
|
||||
@@ -34,25 +36,44 @@ async fn get_gateways_described(
|
||||
) -> Json<Vec<LegacyDescribedGateway>> {
|
||||
let contract_cache = state.nym_contract_cache();
|
||||
let describe_cache = state.described_nodes_cache();
|
||||
let gateways = contract_cache.legacy_gateways_filtered().await;
|
||||
if gateways.is_empty() {
|
||||
return Json(Vec::new());
|
||||
}
|
||||
|
||||
// legacy
|
||||
let legacy = contract_cache.legacy_gateways_filtered().await;
|
||||
|
||||
// if the self describe cache is unavailable, well, don't attach describe data and only return legacy gateways
|
||||
let Ok(self_descriptions) = describe_cache.get().await else {
|
||||
return Json(gateways.into_iter().map(Into::into).collect());
|
||||
let Ok(describe_cache) = describe_cache.get().await else {
|
||||
return Json(legacy.into_iter().map(Into::into).collect());
|
||||
};
|
||||
|
||||
Json(
|
||||
gateways
|
||||
.into_iter()
|
||||
.map(|bond| LegacyDescribedGateway {
|
||||
self_described: self_descriptions.get_description(&bond.node_id).cloned(),
|
||||
bond: bond.bond,
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
let migrated_nymnodes = state.nym_contract_cache().nym_nodes().await;
|
||||
let mut out = Vec::new();
|
||||
|
||||
for legacy_bond in legacy {
|
||||
out.push(LegacyDescribedGateway {
|
||||
self_described: describe_cache
|
||||
.get_description(&legacy_bond.node_id)
|
||||
.cloned(),
|
||||
bond: legacy_bond.bond,
|
||||
})
|
||||
}
|
||||
|
||||
for nym_node in migrated_nymnodes {
|
||||
// we ALWAYS need description to set legacy fields
|
||||
let Some(description) = describe_cache.get_description(&nym_node.node_id()) else {
|
||||
continue;
|
||||
};
|
||||
// if the node hasn't declared it can be a gateway, ignore it
|
||||
if !description.declared_role.entry {
|
||||
continue;
|
||||
}
|
||||
|
||||
out.push(LegacyDescribedGateway {
|
||||
bond: to_legacy_gateway(&nym_node, description),
|
||||
self_described: Some(description.clone()),
|
||||
})
|
||||
}
|
||||
|
||||
Json(out)
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
@@ -70,28 +91,43 @@ async fn get_mixnodes_described(
|
||||
let contract_cache = state.nym_contract_cache();
|
||||
let describe_cache = state.described_nodes_cache();
|
||||
|
||||
let mixnodes = contract_cache
|
||||
let legacy: Vec<LegacyMixNodeBondWithLayer> = contract_cache
|
||||
.legacy_mixnodes_filtered()
|
||||
.await
|
||||
.into_iter()
|
||||
.map(|m| m.bond_information)
|
||||
.collect::<Vec<_>>();
|
||||
if mixnodes.is_empty() {
|
||||
return Json(Vec::new());
|
||||
}
|
||||
|
||||
// if the self describe cache is unavailable, well, don't attach describe data
|
||||
let Ok(self_descriptions) = describe_cache.get().await else {
|
||||
return Json(mixnodes.into_iter().map(Into::into).collect());
|
||||
// if the self describe cache is unavailable, well, don't attach describe data and only return legacy mixnodes
|
||||
let Ok(describe_cache) = describe_cache.get().await else {
|
||||
return Json(legacy.into_iter().map(Into::into).collect());
|
||||
};
|
||||
|
||||
Json(
|
||||
mixnodes
|
||||
.into_iter()
|
||||
.map(|bond| LegacyDescribedMixNode {
|
||||
self_described: self_descriptions.get_description(&bond.mix_id).cloned(),
|
||||
bond,
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
let migrated_nymnodes = state.nym_contract_cache().nym_nodes().await;
|
||||
let mut out = Vec::new();
|
||||
|
||||
for legacy_bond in legacy {
|
||||
out.push(LegacyDescribedMixNode {
|
||||
self_described: describe_cache.get_description(&legacy_bond.mix_id).cloned(),
|
||||
bond: legacy_bond,
|
||||
})
|
||||
}
|
||||
|
||||
for nym_node in migrated_nymnodes {
|
||||
// we ALWAYS need description to set legacy fields
|
||||
let Some(description) = describe_cache.get_description(&nym_node.node_id()) else {
|
||||
continue;
|
||||
};
|
||||
// if the node hasn't declared it can be a gateway, ignore it
|
||||
if !description.declared_role.mixnode {
|
||||
continue;
|
||||
}
|
||||
|
||||
out.push(LegacyDescribedMixNode {
|
||||
bond: to_legacy_mixnode(&nym_node, description).bond_information,
|
||||
self_described: Some(description.clone()),
|
||||
})
|
||||
}
|
||||
|
||||
Json(out)
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@ use crate::node_status_api::models::{AxumErrorResponse, AxumResult};
|
||||
use crate::support::http::helpers::{NodeIdParam, PaginationRequest};
|
||||
use crate::support::http::state::AppState;
|
||||
use axum::extract::{Path, Query, State};
|
||||
use axum::routing::get;
|
||||
use axum::routing::{get, post};
|
||||
use axum::{Json, Router};
|
||||
use nym_api_requests::models::{
|
||||
AnnotationResponse, NodeDatePerformanceResponse, NodePerformanceResponse, NoiseDetails,
|
||||
NymNodeDescription, PerformanceHistoryResponse, UptimeHistoryResponse,
|
||||
AnnotationResponse, NodeDatePerformanceResponse, NodePerformanceResponse, NodeRefreshBody,
|
||||
NoiseDetails, NymNodeDescription, PerformanceHistoryResponse, UptimeHistoryResponse,
|
||||
};
|
||||
use nym_api_requests::pagination::{PaginatedResponse, Pagination};
|
||||
use nym_contracts_common::NaiveFloat;
|
||||
@@ -17,7 +17,8 @@ use nym_mixnet_contract_common::reward_params::Performance;
|
||||
use nym_mixnet_contract_common::NymNodeDetails;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use time::Date;
|
||||
use std::time::Duration;
|
||||
use time::{Date, OffsetDateTime};
|
||||
use utoipa::{IntoParams, ToSchema};
|
||||
|
||||
pub(crate) mod legacy;
|
||||
@@ -25,6 +26,7 @@ pub(crate) mod unstable;
|
||||
|
||||
pub(crate) fn nym_node_routes() -> Router<AppState> {
|
||||
Router::new()
|
||||
.route("/refresh-described", post(refresh_described))
|
||||
.route("/noise", get(nodes_noise))
|
||||
.route("/bonded", get(get_bonded_nodes))
|
||||
.route("/described", get(get_described_nodes))
|
||||
@@ -42,6 +44,63 @@ pub(crate) fn nym_node_routes() -> Router<AppState> {
|
||||
.route("/uptime-history/:node_id", get(get_node_uptime_history))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
tag = "Nym Nodes",
|
||||
post,
|
||||
request_body = NodeRefreshBody,
|
||||
path = "/refresh-described",
|
||||
context_path = "/v1/nym-nodes",
|
||||
)]
|
||||
async fn refresh_described(
|
||||
State(state): State<AppState>,
|
||||
Json(request_body): Json<NodeRefreshBody>,
|
||||
) -> AxumResult<Json<()>> {
|
||||
let Some(refresh_data) = state
|
||||
.nym_contract_cache()
|
||||
.get_node_refresh_data(request_body.node_identity)
|
||||
.await
|
||||
else {
|
||||
return Err(AxumErrorResponse::not_found(format!(
|
||||
"node with identity {} does not seem to exist",
|
||||
request_body.node_identity
|
||||
)));
|
||||
};
|
||||
|
||||
if !request_body.verify_signature() {
|
||||
return Err(AxumErrorResponse::unauthorised("invalid request signature"));
|
||||
}
|
||||
|
||||
if request_body.is_stale() {
|
||||
return Err(AxumErrorResponse::bad_request("the request is stale"));
|
||||
}
|
||||
|
||||
let node_id = refresh_data.node_id();
|
||||
if let Some(last) = state.forced_refresh.last_refreshed(node_id).await {
|
||||
// max 1 refresh a minute
|
||||
let minute_ago = OffsetDateTime::now_utc() - Duration::from_secs(60);
|
||||
if last > minute_ago {
|
||||
return Err(AxumErrorResponse::too_many(
|
||||
"already refreshed node in the last minute",
|
||||
));
|
||||
}
|
||||
}
|
||||
// to make sure you can't ddos the endpoint while a request is in progress
|
||||
state.forced_refresh.set_last_refreshed(node_id).await;
|
||||
|
||||
if let Some(updated_data) = refresh_data.try_refresh().await {
|
||||
let Ok(mut describe_cache) = state.described_nodes_cache.write().await else {
|
||||
return Err(AxumErrorResponse::service_unavailable());
|
||||
};
|
||||
describe_cache.get_mut().force_update(updated_data)
|
||||
} else {
|
||||
return Err(AxumErrorResponse::unprocessable_entity(
|
||||
"failed to refresh node description",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(Json(()))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
tag = "Nym Nodes",
|
||||
get,
|
||||
|
||||
@@ -6,7 +6,7 @@ use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use thiserror::Error;
|
||||
use time::OffsetDateTime;
|
||||
use tokio::sync::{RwLock, RwLockReadGuard};
|
||||
use tokio::sync::{RwLock, RwLockMappedWriteGuard, RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
#[error("the cache item has not been initialised")]
|
||||
@@ -45,6 +45,13 @@ impl<T> SharedCache<T> {
|
||||
RwLockReadGuard::try_map(guard, |a| a.inner.as_ref()).map_err(|_| UninitialisedCache)
|
||||
}
|
||||
|
||||
pub(crate) async fn write(
|
||||
&self,
|
||||
) -> Result<RwLockMappedWriteGuard<'_, Cache<T>>, UninitialisedCache> {
|
||||
let guard = self.0.write().await;
|
||||
RwLockWriteGuard::try_map(guard, |a| a.inner.as_mut()).map_err(|_| UninitialisedCache)
|
||||
}
|
||||
|
||||
// ignores expiration data
|
||||
#[allow(dead_code)]
|
||||
pub(crate) async fn unchecked_get_inner(
|
||||
@@ -134,6 +141,10 @@ impl<T> Cache<T> {
|
||||
self.as_at = OffsetDateTime::now_utc()
|
||||
}
|
||||
|
||||
pub(crate) fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.value
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn has_expired(&self, ttl: Duration, now: Option<OffsetDateTime>) -> bool {
|
||||
let now = now.unwrap_or(OffsetDateTime::now_utc());
|
||||
|
||||
@@ -188,6 +188,7 @@ async fn start_nym_api_tasks_axum(config: &Config) -> anyhow::Result<ShutdownHan
|
||||
};
|
||||
|
||||
let router = router.with_state(AppState {
|
||||
forced_refresh: Default::default(),
|
||||
nym_contract_cache: nym_contract_cache_state.clone(),
|
||||
node_status_cache: node_status_cache_state.clone(),
|
||||
circulating_supply_cache: circulating_supply_cache.clone(),
|
||||
|
||||
@@ -22,6 +22,7 @@ use utoipauto::utoipauto;
|
||||
models::CirculatingSupplyResponse,
|
||||
models::CoinSchema,
|
||||
nym_mixnet_contract_common::Interval,
|
||||
nym_api_requests::models::NodeRefreshBody,
|
||||
nym_api_requests::models::GatewayStatusReportResponse,
|
||||
nym_api_requests::models::GatewayUptimeHistoryResponse,
|
||||
nym_api_requests::models::GatewayCoreStatusResponse,
|
||||
|
||||
@@ -15,7 +15,9 @@ use nym_api_requests::models::{GatewayBondAnnotated, MixNodeBondAnnotated, NodeA
|
||||
use nym_mixnet_contract_common::NodeId;
|
||||
use nym_task::TaskManager;
|
||||
use std::collections::HashMap;
|
||||
use tokio::sync::RwLockReadGuard;
|
||||
use std::sync::Arc;
|
||||
use time::OffsetDateTime;
|
||||
use tokio::sync::{RwLock, RwLockReadGuard};
|
||||
use tokio::task::JoinHandle;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
@@ -70,6 +72,7 @@ type AxumJoinHandle = JoinHandle<std::io::Result<()>>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct AppState {
|
||||
pub(crate) forced_refresh: ForcedRefresh,
|
||||
pub(crate) nym_contract_cache: NymContractCache,
|
||||
pub(crate) node_status_cache: NodeStatusCache,
|
||||
pub(crate) circulating_supply_cache: CirculatingSupplyCache,
|
||||
@@ -79,6 +82,24 @@ pub(crate) struct AppState {
|
||||
pub(crate) node_info_cache: unstable::NodeInfoCache,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub(crate) struct ForcedRefresh {
|
||||
pub(crate) refreshes: Arc<RwLock<HashMap<NodeId, OffsetDateTime>>>,
|
||||
}
|
||||
|
||||
impl ForcedRefresh {
|
||||
pub(crate) async fn last_refreshed(&self, node_id: NodeId) -> Option<OffsetDateTime> {
|
||||
self.refreshes.read().await.get(&node_id).copied()
|
||||
}
|
||||
|
||||
pub(crate) async fn set_last_refreshed(&self, node_id: NodeId) {
|
||||
self.refreshes
|
||||
.write()
|
||||
.await
|
||||
.insert(node_id, OffsetDateTime::now_utc());
|
||||
}
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
pub(crate) fn nym_contract_cache(&self) -> &NymContractCache {
|
||||
&self.nym_contract_cache
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use nym_api_requests::legacy::{LegacyMixNodeBondWithLayer, LegacyMixNodeDetailsWithLayer};
|
||||
use nym_api_requests::models::NymNodeData;
|
||||
use nym_config::defaults::DEFAULT_NYM_NODE_HTTP_PORT;
|
||||
use nym_crypto::aes::cipher::crypto_common::rand_core::OsRng;
|
||||
use nym_mixnet_contract_common::mixnode::LegacyPendingMixNodeChanges;
|
||||
use nym_mixnet_contract_common::{
|
||||
Gateway, GatewayBond, LegacyMixLayer, MixNode, MixNodeBond, NymNodeDetails,
|
||||
};
|
||||
use rand::prelude::SliceRandom;
|
||||
|
||||
pub(crate) fn to_legacy_mixnode(
|
||||
nym_node: &NymNodeDetails,
|
||||
description: &NymNodeData,
|
||||
) -> LegacyMixNodeDetailsWithLayer {
|
||||
let layer_choices = [
|
||||
LegacyMixLayer::One,
|
||||
LegacyMixLayer::Two,
|
||||
LegacyMixLayer::Three,
|
||||
];
|
||||
let mut rng = OsRng;
|
||||
|
||||
// slap a random layer on it because legacy clients don't understand a concept of layerless mixnodes
|
||||
// SAFETY: the slice is not empty so the unwrap is fine
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let layer = layer_choices.choose(&mut rng).copied().unwrap();
|
||||
|
||||
LegacyMixNodeDetailsWithLayer {
|
||||
bond_information: LegacyMixNodeBondWithLayer {
|
||||
bond: MixNodeBond {
|
||||
mix_id: nym_node.node_id(),
|
||||
owner: nym_node.bond_information.owner.clone(),
|
||||
original_pledge: nym_node.bond_information.original_pledge.clone(),
|
||||
mix_node: MixNode {
|
||||
host: nym_node.bond_information.node.host.clone(),
|
||||
mix_port: description.mix_port(),
|
||||
verloc_port: description.verloc_port(),
|
||||
http_api_port: nym_node
|
||||
.bond_information
|
||||
.node
|
||||
.custom_http_port
|
||||
.unwrap_or(DEFAULT_NYM_NODE_HTTP_PORT),
|
||||
sphinx_key: description.host_information.keys.x25519.to_base58_string(),
|
||||
identity_key: nym_node.bond_information.node.identity_key.clone(),
|
||||
version: description.build_information.build_version.clone(),
|
||||
},
|
||||
proxy: None,
|
||||
bonding_height: nym_node.bond_information.bonding_height,
|
||||
is_unbonding: nym_node.bond_information.is_unbonding,
|
||||
},
|
||||
layer,
|
||||
},
|
||||
rewarding_details: nym_node.rewarding_details.clone(),
|
||||
pending_changes: LegacyPendingMixNodeChanges {
|
||||
pledge_change: nym_node.pending_changes.pledge_change,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn to_legacy_gateway(
|
||||
nym_node: &NymNodeDetails,
|
||||
description: &NymNodeData,
|
||||
) -> GatewayBond {
|
||||
GatewayBond {
|
||||
pledge_amount: nym_node.bond_information.original_pledge.clone(),
|
||||
owner: nym_node.bond_information.owner.clone(),
|
||||
block_height: nym_node.bond_information.bonding_height,
|
||||
gateway: Gateway {
|
||||
host: nym_node.bond_information.node.host.clone(),
|
||||
mix_port: description.mix_port(),
|
||||
clients_port: description.mixnet_websockets.ws_port,
|
||||
location: description
|
||||
.auxiliary_details
|
||||
.location
|
||||
.map(|c| c.to_string())
|
||||
.unwrap_or_default(),
|
||||
sphinx_key: description.host_information.keys.x25519.to_base58_string(),
|
||||
identity_key: nym_node.bond_information.node.identity_key.clone(),
|
||||
version: description.build_information.build_version.clone(),
|
||||
},
|
||||
proxy: None,
|
||||
}
|
||||
}
|
||||
@@ -7,3 +7,5 @@ pub(crate) mod config;
|
||||
pub(crate) mod http;
|
||||
pub(crate) mod nyxd;
|
||||
pub(crate) mod storage;
|
||||
|
||||
pub(crate) mod legacy_helpers;
|
||||
|
||||
@@ -983,7 +983,8 @@ impl StorageManager {
|
||||
since: i64,
|
||||
until: i64,
|
||||
) -> Result<Vec<ActiveGateway>, sqlx::Error> {
|
||||
sqlx::query_as(
|
||||
sqlx::query_as!(
|
||||
ActiveGateway,
|
||||
r#"
|
||||
SELECT DISTINCT identity, node_id as "node_id: NodeId", id
|
||||
FROM gateway_details
|
||||
@@ -993,9 +994,9 @@ impl StorageManager {
|
||||
SELECT 1 FROM gateway_status WHERE timestamp > ? AND timestamp < ?
|
||||
)
|
||||
"#,
|
||||
since,
|
||||
until
|
||||
)
|
||||
.bind(since)
|
||||
.bind(until)
|
||||
.fetch_all(&self.connection_pool)
|
||||
.await
|
||||
}
|
||||
@@ -1328,7 +1329,7 @@ pub(crate) mod v3_migration {
|
||||
identity VARCHAR NOT NULL UNIQUE
|
||||
);
|
||||
|
||||
INSERT INTO gateway_details_temp SELECT * FROM gateway_details;
|
||||
INSERT INTO gateway_details_temp(id, node_id, identity) SELECT id, node_id, identity FROM gateway_details;
|
||||
DROP TABLE gateway_details;
|
||||
ALTER TABLE gateway_details_temp RENAME TO gateway_details;
|
||||
"#,
|
||||
|
||||
@@ -87,7 +87,7 @@ pub struct GatewayDetails {
|
||||
impl From<GatewayDetails> for TestNode {
|
||||
fn from(value: GatewayDetails) -> Self {
|
||||
TestNode {
|
||||
node_id: None,
|
||||
node_id: Some(value.node_id),
|
||||
identity_key: Some(value.identity),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ serde = { workspace = true, features = ["derive"] }
|
||||
serde_json.workspace = true
|
||||
time = { workspace = true, features = ["serde", "formatting", "parsing"] }
|
||||
tsify = { workspace = true, optional = true }
|
||||
reqwest = { workspace = true, features = ["json"] }
|
||||
reqwest = { workspace = true, features = ["json", "rustls-tls"] }
|
||||
wasm-bindgen = { workspace = true, optional = true }
|
||||
|
||||
## openapi:
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-node-status-agent"
|
||||
version = "0.1.4"
|
||||
version = "0.1.6"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
|
||||
@@ -37,4 +37,4 @@ COPY --from=builder /usr/src/nym/target/release/nym-node-status-agent ./
|
||||
COPY --from=builder /usr/src/nym-vpn-client/nym-vpn-core/target/release/nym-gateway-probe ./
|
||||
|
||||
ENV NODE_STATUS_AGENT_PROBE_PATH=/nym/nym-gateway-probe
|
||||
ENTRYPOINT [ "/nym/nym-node-status-agent", "run-probe" ]
|
||||
ENTRYPOINT [ "/nym/nym-node-status-agent", "run-probe" ]
|
||||
@@ -1,3 +1,4 @@
|
||||
use anyhow::bail;
|
||||
use clap::{Parser, Subcommand};
|
||||
use nym_bin_common::bin_info;
|
||||
use nym_common_models::ns_api::TestrunAssignment;
|
||||
@@ -51,11 +52,13 @@ impl Args {
|
||||
let version = probe.version().await;
|
||||
tracing::info!("Probe version:\n{}", version);
|
||||
|
||||
let testrun = request_testrun(&server_address).await?;
|
||||
if let Some(testrun) = request_testrun(&server_address).await? {
|
||||
let log = probe.run_and_get_log(&Some(testrun.gateway_identity_key));
|
||||
|
||||
let log = probe.run_and_get_log(&Some(testrun.gateway_identity_key));
|
||||
|
||||
submit_results(&server_address, testrun.testrun_id, log).await?;
|
||||
submit_results(&server_address, testrun.testrun_id, log).await?;
|
||||
} else {
|
||||
tracing::info!("No testruns available, exiting")
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -64,16 +67,26 @@ impl Args {
|
||||
const URL_BASE: &str = "internal/testruns";
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
async fn request_testrun(server_addr: &str) -> anyhow::Result<TestrunAssignment> {
|
||||
async fn request_testrun(server_addr: &str) -> anyhow::Result<Option<TestrunAssignment>> {
|
||||
let target_url = format!("{}/{}", server_addr, URL_BASE);
|
||||
let client = reqwest::Client::new();
|
||||
let res = client
|
||||
.get(target_url)
|
||||
.send()
|
||||
.await
|
||||
.and_then(|response| response.error_for_status())?;
|
||||
res.json()
|
||||
.await
|
||||
let res = client.get(target_url).send().await?;
|
||||
let status = res.status();
|
||||
let response_text = res.text().await?;
|
||||
|
||||
if status.is_client_error() {
|
||||
bail!("{}: {}", status, response_text);
|
||||
} else if status.is_server_error() {
|
||||
if matches!(status, reqwest::StatusCode::SERVICE_UNAVAILABLE)
|
||||
&& response_text.contains("No testruns available")
|
||||
{
|
||||
return Ok(None);
|
||||
} else {
|
||||
bail!("{}: {}", status, response_text);
|
||||
}
|
||||
}
|
||||
|
||||
serde_json::from_str(&response_text)
|
||||
.map(|testrun| {
|
||||
tracing::info!("Received testrun assignment: {:?}", testrun);
|
||||
testrun
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-node-status-api"
|
||||
version = "0.1.5"
|
||||
version = "0.1.6"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
use anyhow::anyhow;
|
||||
use axum::{response::Redirect, Router};
|
||||
use tokio::net::ToSocketAddrs;
|
||||
use tower_http::{
|
||||
cors::CorsLayer,
|
||||
trace::{DefaultOnResponse, TraceLayer},
|
||||
};
|
||||
use tower_http::{cors::CorsLayer, trace::TraceLayer};
|
||||
use utoipa::OpenApi;
|
||||
use utoipa_swagger_ui::SwaggerUi;
|
||||
|
||||
@@ -61,10 +58,7 @@ impl RouterBuilder {
|
||||
// CORS layer needs to wrap other API layers
|
||||
.layer(setup_cors())
|
||||
// logger should be outermost layer
|
||||
.layer(
|
||||
TraceLayer::new_for_http()
|
||||
.on_response(DefaultOnResponse::new().level(tracing::Level::DEBUG)),
|
||||
)
|
||||
.layer(TraceLayer::new_for_http())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,8 @@ async fn request_testrun(State(state): State<AppState>) -> HttpResult<Json<Testr
|
||||
);
|
||||
Ok(Json(testrun))
|
||||
} else {
|
||||
Err(HttpError::no_available_testruns())
|
||||
tracing::debug!("No testruns available for agent");
|
||||
Err(HttpError::no_testruns_available())
|
||||
}
|
||||
}
|
||||
Err(err) => Err(HttpError::internal_with_logging(err)),
|
||||
|
||||
@@ -27,9 +27,9 @@ impl HttpError {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn no_available_testruns() -> Self {
|
||||
pub(crate) fn no_testruns_available() -> Self {
|
||||
Self {
|
||||
message: serde_json::json!({"message": "No available testruns"}).to_string(),
|
||||
message: serde_json::json!({"message": "No testruns available"}).to_string(),
|
||||
status: axum::http::StatusCode::SERVICE_UNAVAILABLE,
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-node"
|
||||
version = "1.1.9"
|
||||
version = "1.1.10"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
@@ -53,6 +53,7 @@ nym-sphinx-acknowledgements = { path = "../common/nymsphinx/acknowledgements" }
|
||||
nym-sphinx-addressing = { path = "../common/nymsphinx/addressing" }
|
||||
nym-task = { path = "../common/task" }
|
||||
nym-types = { path = "../common/types" }
|
||||
nym-validator-client = { path = "../common/client-libs/validator-client" }
|
||||
nym-wireguard = { path = "../common/wireguard" }
|
||||
nym-wireguard-types = { path = "../common/wireguard-types", default-features = false }
|
||||
|
||||
|
||||
@@ -32,13 +32,18 @@ use nym_node_http_api::{NymNodeHTTPServer, NymNodeRouter};
|
||||
use nym_sphinx_acknowledgements::AckKey;
|
||||
use nym_sphinx_addressing::Recipient;
|
||||
use nym_task::{TaskClient, TaskManager};
|
||||
use nym_validator_client::client::NymApiClientExt;
|
||||
use nym_validator_client::models::NodeRefreshBody;
|
||||
use nym_validator_client::NymApiClient;
|
||||
use nym_wireguard::{peer_controller::PeerControlRequest, WireguardGatewayData};
|
||||
use rand::rngs::OsRng;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::sync::mpsc;
|
||||
use tracing::{debug, error, info, trace};
|
||||
use tokio::time::timeout;
|
||||
use tracing::{debug, error, info, trace, warn};
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
use self::helpers::load_x25519_wireguard_keypair;
|
||||
@@ -750,6 +755,38 @@ impl NymNode {
|
||||
.await?)
|
||||
}
|
||||
|
||||
async fn try_refresh_remote_nym_api_cache(&self) {
|
||||
info!("attempting to request described cache request from nym-api...");
|
||||
if self.config.mixnet.nym_api_urls.is_empty() {
|
||||
warn!("no nym-api urls available");
|
||||
return;
|
||||
}
|
||||
|
||||
for nym_api in &self.config.mixnet.nym_api_urls {
|
||||
info!("trying {nym_api}...");
|
||||
let client = NymApiClient::new_with_user_agent(nym_api.clone(), bin_info_owned!());
|
||||
|
||||
// make new request every time in case previous one takes longer and invalidates the signature
|
||||
let request = NodeRefreshBody::new(self.ed25519_identity_keys.private_key());
|
||||
match timeout(
|
||||
Duration::from_secs(10),
|
||||
client.nym_api.force_refresh_describe_cache(&request),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(Ok(_)) => {
|
||||
info!("managed to refresh own self-described data cache")
|
||||
}
|
||||
Ok(Err(request_failure)) => {
|
||||
warn!("failed to resolve the refresh request: {request_failure}")
|
||||
}
|
||||
Err(_timeout) => {
|
||||
warn!("timed out while attempting to resolve the request. the cache might be stale")
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn run(self) -> Result<(), NymNodeError> {
|
||||
let mut task_manager = TaskManager::default().named("NymNode");
|
||||
let http_server = self
|
||||
@@ -764,6 +801,8 @@ impl NymNode {
|
||||
}
|
||||
});
|
||||
|
||||
self.try_refresh_remote_nym_api_cache().await;
|
||||
|
||||
match self.config.mode {
|
||||
NodeMode::Mixnode => {
|
||||
self.start_mixnode(task_manager.subscribe_named("mixnode"))?;
|
||||
|
||||
@@ -2,6 +2,16 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [2024.13-magura] (2024-11-18)
|
||||
|
||||
- bugfix: [wallet] displaying delegations for native nymnodes ([#5087])
|
||||
- bugfix: wallet backend fixes ([#5070])
|
||||
- Feature/wallet bonding fixes ([#5064])
|
||||
|
||||
[#5087]: https://github.com/nymtech/nym/pull/5087
|
||||
[#5070]: https://github.com/nymtech/nym/pull/5070
|
||||
[#5064]: https://github.com/nymtech/nym/pull/5064
|
||||
|
||||
## [v1.2.13] (2024-05-08)
|
||||
|
||||
- Bug fix: wallet delegations list is empty when RPC node doesn't hold block ([#4565])
|
||||
|
||||
Generated
+1
-1
@@ -3535,7 +3535,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym_wallet"
|
||||
version = "1.2.14"
|
||||
version = "1.2.15"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.13.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nymproject/nym-wallet-app",
|
||||
"version": "1.2.14",
|
||||
"version": "1.2.15",
|
||||
"license": "MIT",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym_wallet"
|
||||
version = "1.2.14"
|
||||
version = "1.2.15"
|
||||
description = "Nym Native Wallet"
|
||||
authors = ["Nym Technologies SA"]
|
||||
license = ""
|
||||
|
||||
@@ -7,7 +7,9 @@ use crate::vesting::delegate::vesting_undelegate_from_mixnode;
|
||||
use nym_mixnet_contract_common::mixnode::MixStakeSaturationResponse;
|
||||
use nym_mixnet_contract_common::NodeId;
|
||||
use nym_types::currency::DecCoin;
|
||||
use nym_types::delegation::{Delegation, DelegationWithEverything, DelegationsSummaryResponse};
|
||||
use nym_types::delegation::{
|
||||
Delegation, DelegationWithEverything, DelegationsSummaryResponse, NodeInformation,
|
||||
};
|
||||
use nym_types::deprecated::{
|
||||
convert_to_delegation_events, DelegationEvent, WrappedDelegationEvent,
|
||||
};
|
||||
@@ -19,6 +21,7 @@ use nym_validator_client::nyxd::contract_traits::{
|
||||
MixnetQueryClient, MixnetSigningClient, NymContractsProvider, PagedMixnetQueryClient,
|
||||
};
|
||||
use nym_validator_client::nyxd::Fee;
|
||||
use nym_validator_client::DirectSigningHttpRpcValidatorClient;
|
||||
use tap::TapFallible;
|
||||
|
||||
#[tauri::command]
|
||||
@@ -141,6 +144,58 @@ pub async fn undelegate_all_from_mixnode(
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_node_information(
|
||||
client: &DirectSigningHttpRpcValidatorClient,
|
||||
node_id: NodeId,
|
||||
error_strings: &mut Vec<String>,
|
||||
) -> Result<Option<NodeInformation>, BackendError> {
|
||||
let native_nymnode = client
|
||||
.nyxd
|
||||
.get_nymnode_details(node_id)
|
||||
.await
|
||||
.inspect_err(|err| {
|
||||
let str_err =
|
||||
format!("Failed to get nymnode details for node_id = {node_id}. Error: {err}");
|
||||
log::error!(" <<< {str_err}",);
|
||||
error_strings.push(str_err);
|
||||
})?;
|
||||
|
||||
if let Some(native) = native_nymnode.details {
|
||||
return Ok(Some(NodeInformation {
|
||||
is_unbonding: native.is_unbonding(),
|
||||
owner: native.bond_information.owner.to_string(),
|
||||
mix_id: node_id,
|
||||
node_identity: native.bond_information.node.identity_key,
|
||||
rewarding_details: native.rewarding_details,
|
||||
}));
|
||||
}
|
||||
|
||||
let legacy_mixnode = client
|
||||
.nyxd
|
||||
.get_mixnode_details(node_id)
|
||||
.await
|
||||
.inspect_err(|err| {
|
||||
let str_err = format!(
|
||||
"Failed to get legacy mixnode details for node_id = {node_id}. Error: {err}",
|
||||
);
|
||||
log::error!(" <<< {}", str_err);
|
||||
error_strings.push(str_err);
|
||||
})?
|
||||
.mixnode_details;
|
||||
|
||||
if let Some(legacy) = legacy_mixnode {
|
||||
return Ok(Some(NodeInformation {
|
||||
is_unbonding: legacy.is_unbonding(),
|
||||
owner: legacy.bond_information.owner.to_string(),
|
||||
mix_id: node_id,
|
||||
node_identity: legacy.bond_information.mix_node.identity_key,
|
||||
rewarding_details: legacy.rewarding_details,
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
// TODO: fix later (yeah...)
|
||||
#[allow(deprecated)]
|
||||
#[tauri::command]
|
||||
@@ -208,21 +263,9 @@ pub async fn get_all_mix_delegations(
|
||||
d.amount
|
||||
);
|
||||
|
||||
let mixnode = client
|
||||
.nyxd
|
||||
.get_mixnode_details(d.mix_id)
|
||||
.await
|
||||
.tap_err(|err| {
|
||||
let str_err = format!(
|
||||
"Failed to get mixnode details for mix_id = {}. Error: {}",
|
||||
d.mix_id, err
|
||||
);
|
||||
log::error!(" <<< {}", str_err);
|
||||
error_strings.push(str_err);
|
||||
})?
|
||||
.mixnode_details;
|
||||
let node_details = get_node_information(client, d.mix_id, &mut error_strings).await?;
|
||||
|
||||
let accumulated_by_operator = mixnode
|
||||
let accumulated_by_operator = node_details
|
||||
.as_ref()
|
||||
.map(|m| {
|
||||
guard.display_coin_from_base_decimal(&base_mix_denom, m.rewarding_details.operator)
|
||||
@@ -238,10 +281,13 @@ pub async fn get_all_mix_delegations(
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let accumulated_by_delegates = mixnode
|
||||
let accumulated_by_delegates = node_details
|
||||
.as_ref()
|
||||
.map(|m| {
|
||||
guard.display_coin_from_base_decimal(&base_mix_denom, m.rewarding_details.delegates)
|
||||
reg.attempt_create_display_coin_from_base_dec_amount(
|
||||
&base_mix_denom,
|
||||
m.rewarding_details.delegates,
|
||||
)
|
||||
})
|
||||
.transpose()
|
||||
.tap_err(|err| {
|
||||
@@ -254,7 +300,7 @@ pub async fn get_all_mix_delegations(
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let cost_params = mixnode
|
||||
let cost_params = node_details
|
||||
.as_ref()
|
||||
.map(|m| {
|
||||
NodeCostParams::from_mixnet_contract_mixnode_cost_params(
|
||||
@@ -335,21 +381,26 @@ pub async fn get_all_mix_delegations(
|
||||
" >>> Get average uptime percentage: mix_iid = {}",
|
||||
d.mix_id
|
||||
);
|
||||
let avg_uptime_percent = client
|
||||
|
||||
let current_performance = client
|
||||
.nym_api
|
||||
.get_mixnode_avg_uptime(d.mix_id)
|
||||
.get_current_node_performance(d.mix_id)
|
||||
.await
|
||||
.tap_err(|err| {
|
||||
.inspect_err(|err| {
|
||||
let str_err = format!(
|
||||
"Failed to get average uptime percentage for mix_id = {}. Error: {}",
|
||||
d.mix_id, err
|
||||
"Failed to get current node performance for node_id = {}. Error: {err}",
|
||||
d.mix_id
|
||||
);
|
||||
log::error!(" <<< {}", str_err);
|
||||
error_strings.push(str_err);
|
||||
})
|
||||
.ok()
|
||||
.map(|r| r.avg_uptime);
|
||||
log::trace!(" <<< {:?}", avg_uptime_percent);
|
||||
.and_then(|r| r.performance);
|
||||
|
||||
// convert to old u8
|
||||
let current_uptime = current_performance.map(|p| (p * 100.) as u8);
|
||||
|
||||
log::trace!(" <<< {:?}", current_uptime);
|
||||
|
||||
log::trace!(
|
||||
" >>> Convert delegated on block height to timestamp: block_height = {}",
|
||||
@@ -377,9 +428,9 @@ pub async fn get_all_mix_delegations(
|
||||
pending_events.len()
|
||||
);
|
||||
|
||||
let mixnode_is_unbonding = mixnode.as_ref().map(|m| m.is_unbonding());
|
||||
let mixnode_is_unbonding = node_details.as_ref().map(|m| m.is_unbonding);
|
||||
log::trace!(
|
||||
" >>> mixnode with mix_id: {} is unbonding: {:?}",
|
||||
" >>> node with mix_id: {} is unbonding: {:?}",
|
||||
d.mix_id,
|
||||
mixnode_is_unbonding
|
||||
);
|
||||
@@ -387,16 +438,14 @@ pub async fn get_all_mix_delegations(
|
||||
with_everything.push(DelegationWithEverything {
|
||||
owner: d.owner,
|
||||
mix_id: d.mix_id,
|
||||
node_identity: mixnode
|
||||
.map(|m| m.bond_information.mix_node.identity_key)
|
||||
.unwrap_or_default(),
|
||||
node_identity: node_details.map(|m| m.node_identity).unwrap_or_default(),
|
||||
amount: d.amount,
|
||||
block_height: d.height,
|
||||
uses_vesting_contract_tokens,
|
||||
delegated_on_iso_datetime,
|
||||
stake_saturation: stake_saturation.uncapped_saturation,
|
||||
accumulated_by_operator,
|
||||
avg_uptime_percent,
|
||||
avg_uptime_percent: current_uptime,
|
||||
accumulated_by_delegates,
|
||||
cost_params,
|
||||
unclaimed_rewards: accumulated_rewards,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"package": {
|
||||
"productName": "nym-wallet",
|
||||
"version": "1.2.14"
|
||||
"version": "1.2.15"
|
||||
},
|
||||
"build": {
|
||||
"distDir": "../dist",
|
||||
|
||||
@@ -113,6 +113,7 @@ export const BondedGateway = ({
|
||||
startIcon={<UpgradeRounded />}
|
||||
variant="contained"
|
||||
disableElevation
|
||||
disabled={!!gateway.proxy}
|
||||
onClick={onShowMigrateToNymNodeModal}
|
||||
>
|
||||
Migrate to Nym Node
|
||||
|
||||
@@ -190,15 +190,21 @@ export const BondedMixnode = ({
|
||||
</Button>
|
||||
</Box>
|
||||
</Tooltip>
|
||||
<Button
|
||||
startIcon={<UpgradeRounded />}
|
||||
variant="contained"
|
||||
disableElevation
|
||||
onClick={onShowMigrateToNymNodeModal}
|
||||
disabled={isUnbonding}
|
||||
<Tooltip
|
||||
title={!!mixnode.proxy && 'You must migrate your vested tokens before you can migrate your mixnode'}
|
||||
>
|
||||
Migrate to Nym Node
|
||||
</Button>
|
||||
<Box>
|
||||
<Button
|
||||
startIcon={<UpgradeRounded />}
|
||||
variant="contained"
|
||||
disableElevation
|
||||
onClick={onShowMigrateToNymNodeModal}
|
||||
disabled={isUnbonding || !!mixnode.proxy}
|
||||
>
|
||||
Migrate to Nym Node
|
||||
</Button>
|
||||
</Box>
|
||||
</Tooltip>
|
||||
</Stack>
|
||||
|
||||
{nextEpoch instanceof Error ? null : (
|
||||
|
||||
@@ -4,11 +4,10 @@ import { ModalListItem } from 'src/components/Modals/ModalListItem';
|
||||
import { SimpleModal } from 'src/components/Modals/SimpleModal';
|
||||
import { ModalFee } from 'src/components/Modals/ModalFee';
|
||||
import { useGetFee } from 'src/hooks/useGetFee';
|
||||
import { simulateClaimOperatorReward, simulateVestingClaimOperatorReward } from 'src/requests';
|
||||
import { AppContext } from 'src/context';
|
||||
import { BalanceWarning } from 'src/components/FeeWarning';
|
||||
import { Box } from '@mui/material';
|
||||
import { TBondedMixnode } from 'src/requests/mixnodeDetails';
|
||||
import { TBondedNymNode } from 'src/requests/nymNodeDetails';
|
||||
|
||||
export const RedeemRewardsModal = ({
|
||||
node,
|
||||
@@ -16,23 +15,18 @@ export const RedeemRewardsModal = ({
|
||||
onError,
|
||||
onClose,
|
||||
}: {
|
||||
node: TBondedMixnode;
|
||||
node: TBondedNymNode;
|
||||
onConfirm: (fee?: FeeDetails) => Promise<void>;
|
||||
onError: (err: string) => void;
|
||||
onClose: () => void;
|
||||
}) => {
|
||||
const { fee, getFee, isFeeLoading, feeError } = useGetFee();
|
||||
const { fee, isFeeLoading, feeError } = useGetFee();
|
||||
const { userBalance } = useContext(AppContext);
|
||||
|
||||
useEffect(() => {
|
||||
if (feeError) onError(feeError);
|
||||
}, [feeError]);
|
||||
|
||||
useEffect(() => {
|
||||
if (node.proxy) getFee(simulateVestingClaimOperatorReward, {});
|
||||
else getFee(simulateClaimOperatorReward, {});
|
||||
}, []);
|
||||
|
||||
const handleOnOK = async () => onConfirm(fee);
|
||||
|
||||
return (
|
||||
|
||||
@@ -217,24 +217,34 @@ export const BondingContextProvider: FCWithChildren = ({ children }): JSX.Elemen
|
||||
|
||||
const migrateVestedMixnode = async () => {
|
||||
setIsLoading(true);
|
||||
const tx = await tauriMigrateVestedMixnode();
|
||||
setIsLoading(false);
|
||||
return tx;
|
||||
try {
|
||||
const tx = await tauriMigrateVestedMixnode();
|
||||
setIsLoading(false);
|
||||
return tx;
|
||||
} catch (e) {
|
||||
Console.error(e);
|
||||
setError(`an error occurred: ${e}`);
|
||||
}
|
||||
};
|
||||
|
||||
const migrateLegacyNode = async () => {
|
||||
setIsLoading(true);
|
||||
let tx: TransactionExecuteResult | undefined;
|
||||
try {
|
||||
let tx: TransactionExecuteResult | undefined;
|
||||
|
||||
if (bondedNode && isMixnode(bondedNode)) {
|
||||
tx = await migrateLegacyMixnodeReq();
|
||||
}
|
||||
if (bondedNode && isGateway(bondedNode)) {
|
||||
tx = await migrateLegacyGatewayReq();
|
||||
if (bondedNode && isMixnode(bondedNode)) {
|
||||
tx = await migrateLegacyMixnodeReq();
|
||||
}
|
||||
if (bondedNode && isGateway(bondedNode)) {
|
||||
tx = await migrateLegacyGatewayReq();
|
||||
}
|
||||
return tx;
|
||||
} catch (e) {
|
||||
Console.error(e);
|
||||
setError(`an error occurred: ${e}`);
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
return tx;
|
||||
};
|
||||
|
||||
const memoizedValue = useMemo(
|
||||
|
||||
@@ -53,7 +53,7 @@ export const Bonding = () => {
|
||||
if (!bondedNode) {
|
||||
return false;
|
||||
}
|
||||
if (isMixnode(bondedNode) && !bondedNode.isUnbonding) {
|
||||
if (isMixnode(bondedNode) && !bondedNode.isUnbonding && !bondedNode.proxy) {
|
||||
return true;
|
||||
}
|
||||
if (isGateway(bondedNode)) {
|
||||
@@ -197,7 +197,14 @@ export const Bonding = () => {
|
||||
};
|
||||
|
||||
if (error) {
|
||||
return <ErrorModal open message="An error occured, please check logs for details" onClose={() => refresh()} />;
|
||||
return (
|
||||
<ErrorModal
|
||||
open
|
||||
title="An error occured, please check logs for details"
|
||||
message={error}
|
||||
onClose={() => refresh()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -292,7 +299,7 @@ export const Bonding = () => {
|
||||
/>
|
||||
)}
|
||||
|
||||
{showModal === 'redeem' && bondedNode && isMixnode(bondedNode) && (
|
||||
{showModal === 'redeem' && bondedNode && isNymNode(bondedNode) && (
|
||||
<RedeemRewardsModal
|
||||
node={bondedNode}
|
||||
onClose={() => setShowModal(undefined)}
|
||||
|
||||
@@ -39,9 +39,9 @@ export const NodeSettings = () => {
|
||||
if (location.state === 'unbond') {
|
||||
setValue('Unbond');
|
||||
}
|
||||
if (location.state === 'test-node') {
|
||||
setValue('Test my node');
|
||||
}
|
||||
// if (location.state === 'test-node') {
|
||||
// setValue('Test my node');
|
||||
// }
|
||||
}, [location]);
|
||||
|
||||
const handleUnbond = async (fee?: FeeDetails) => {
|
||||
@@ -129,11 +129,11 @@ export const NodeSettings = () => {
|
||||
>
|
||||
<Divider />
|
||||
{value === 'General' && bondedNode && <NodeGeneralSettings bondedNode={bondedNode} />}
|
||||
{value === 'Test my node' && <NodeTestPage />}
|
||||
{/* {value === 'Test my node' && <NodeTestPage />} */}
|
||||
{value === 'Unbond' && bondedNode && (
|
||||
<NodeUnbondPage bondedNode={bondedNode} onConfirm={handleUnbond} onError={handleError} />
|
||||
)}
|
||||
{value === 'Playground' && bondedNode && <ApyPlayground bondedNode={bondedNode as TBondedMixnode} />}
|
||||
{/* {value === 'Playground' && bondedNode && <ApyPlayground bondedNode={bondedNode as TBondedMixnode} />} */}
|
||||
{confirmationDetails && confirmationDetails.status === 'success' && (
|
||||
<ConfirmationDetailsModal
|
||||
title={confirmationDetails.title}
|
||||
|
||||
@@ -5,11 +5,15 @@ export const makeNavItems = (bondedNode: TBondedNode) => {
|
||||
const navItems: NavItems[] = ['Unbond'];
|
||||
|
||||
if (isNymNode(bondedNode)) {
|
||||
// add these items to the beginning of the array "General", "Test my node", "Playground"
|
||||
navItems.unshift('General', 'Test my node', 'Playground');
|
||||
// Add these items to the beginning of the array "General", "Test my node", "Playground"
|
||||
// Temporarily removed , 'Test my node due to wasm issues which we need to fix
|
||||
// 'Playground' due to freezing issues
|
||||
navItems.unshift('General');
|
||||
}
|
||||
|
||||
return navItems;
|
||||
};
|
||||
|
||||
export type NavItems = 'General' | 'Unbond' | 'Test my node' | 'Playground';
|
||||
// And these back in once fixed.
|
||||
// 'Playground' | 'Test my node' include in array at a later point
|
||||
export type NavItems = 'General' | 'Unbond' ;
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
NodeConfigUpdate,
|
||||
GatewayConfigUpdate,
|
||||
} from '@nymproject/types';
|
||||
import { TBondGatewayArgs, TBondGatewaySignatureArgs, TNodeConfigUpdateArgs } from '../types';
|
||||
import { TBondGatewayArgs, TBondGatewaySignatureArgs } from '../types';
|
||||
import { invokeWrapper } from './wrapper';
|
||||
|
||||
export const bondGateway = async (args: TBondGatewayArgs) =>
|
||||
|
||||
@@ -6,7 +6,7 @@ export const claimOperatorReward = async (fee?: Fee) =>
|
||||
|
||||
export const claimDelegatorRewards = async (mixId: number, fee?: FeeDetails) =>
|
||||
invokeWrapper<TransactionExecuteResult[]>('claim_locked_and_unlocked_delegator_reward', {
|
||||
mixId,
|
||||
nodeId: mixId,
|
||||
fee: fee?.fee,
|
||||
});
|
||||
|
||||
|
||||
@@ -89,6 +89,9 @@ pub enum AuthenticatorError {
|
||||
#[error("storage should have the requested bandwidht entry")]
|
||||
MissingClientBandwidthEntry,
|
||||
|
||||
#[error("unknown version number")]
|
||||
UnknownVersion,
|
||||
|
||||
#[error("{0}")]
|
||||
PublicKey(#[from] nym_wireguard_types::Error),
|
||||
|
||||
|
||||
@@ -6,18 +6,17 @@ use std::{
|
||||
time::{Duration, SystemTime},
|
||||
};
|
||||
|
||||
use crate::{config::Config, error::*};
|
||||
use crate::{error::AuthenticatorError, peer_manager::PeerManager};
|
||||
use defguard_wireguard_rs::net::IpAddrMask;
|
||||
use defguard_wireguard_rs::{host::Peer, key::Key};
|
||||
use futures::StreamExt;
|
||||
use nym_authenticator_requests::latest::registration::RegistrationData;
|
||||
use nym_authenticator_requests::{
|
||||
latest::{
|
||||
registration::{
|
||||
FinalMessage, GatewayClient, InitMessage, IpPair, PendingRegistrations, PrivateIPs,
|
||||
RegistrationData, RegistredData, RemainingBandwidthData,
|
||||
},
|
||||
request::{AuthenticatorRequest, AuthenticatorRequestData},
|
||||
response::AuthenticatorResponse,
|
||||
latest::registration::{GatewayClient, PendingRegistrations, PrivateIPs},
|
||||
traits::{
|
||||
AuthenticatorRequest, AuthenticatorVersion, FinalMessage, InitMessage,
|
||||
QueryBandwidthMessage, TopUpMessage,
|
||||
},
|
||||
v1, v2, v3, v4, CURRENT_VERSION,
|
||||
};
|
||||
@@ -30,7 +29,7 @@ use nym_crypto::asymmetric::x25519::KeyPair;
|
||||
use nym_gateway_requests::models::CredentialSpendingRequest;
|
||||
use nym_gateway_storage::Storage;
|
||||
use nym_sdk::mixnet::{InputMessage, MixnetMessageSender, Recipient, TransmissionLane};
|
||||
use nym_service_provider_requests_common::ServiceProviderType;
|
||||
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
|
||||
use nym_sphinx::receiver::ReconstructedMessage;
|
||||
use nym_task::TaskHandle;
|
||||
use nym_wireguard::WireguardGatewayData;
|
||||
@@ -39,9 +38,7 @@ use rand::{prelude::IteratorRandom, thread_rng};
|
||||
use tokio::sync::RwLock;
|
||||
use tokio_stream::wrappers::IntervalStream;
|
||||
|
||||
use crate::{config::Config, error::*};
|
||||
|
||||
type AuthenticatorHandleResult = Result<AuthenticatorResponse>;
|
||||
type AuthenticatorHandleResult = Result<(Vec<u8>, Recipient)>;
|
||||
const DEFAULT_REGISTRATION_TIMEOUT_CHECK: Duration = Duration::from_secs(60); // 1 minute
|
||||
|
||||
pub(crate) struct RegistredAndFree {
|
||||
@@ -151,22 +148,87 @@ impl<S: Storage + Clone + 'static> MixnetListener<S> {
|
||||
|
||||
async fn on_initial_request(
|
||||
&mut self,
|
||||
init_message: InitMessage,
|
||||
init_message: Box<dyn InitMessage + Send + Sync + 'static>,
|
||||
protocol: Protocol,
|
||||
request_id: u64,
|
||||
reply_to: Recipient,
|
||||
) -> AuthenticatorHandleResult {
|
||||
let remote_public = init_message.pub_key;
|
||||
let remote_public = init_message.pub_key();
|
||||
let nonce: u64 = fastrand::u64(..);
|
||||
let mut registred_and_free = self.registred_and_free.write().await;
|
||||
if let Some(registration_data) = registred_and_free
|
||||
.registration_in_progres
|
||||
.get(&remote_public)
|
||||
{
|
||||
return Ok(AuthenticatorResponse::new_pending_registration_success(
|
||||
registration_data.clone(),
|
||||
request_id,
|
||||
reply_to,
|
||||
));
|
||||
let gateway_data = registration_data.gateway_data.clone();
|
||||
let bytes = match AuthenticatorVersion::from(protocol) {
|
||||
AuthenticatorVersion::V1 => {
|
||||
v1::response::AuthenticatorResponse::new_pending_registration_success(
|
||||
v1::registration::RegistrationData {
|
||||
nonce: registration_data.nonce,
|
||||
gateway_data: v1::GatewayClient {
|
||||
pub_key: gateway_data.pub_key,
|
||||
private_ip: gateway_data.private_ips.ipv4.into(),
|
||||
mac: v1::ClientMac::new(gateway_data.mac.to_vec()),
|
||||
},
|
||||
wg_port: registration_data.wg_port,
|
||||
},
|
||||
request_id,
|
||||
reply_to,
|
||||
)
|
||||
.to_bytes()
|
||||
.map_err(|err| {
|
||||
AuthenticatorError::FailedToSerializeResponsePacket { source: err }
|
||||
})?
|
||||
}
|
||||
AuthenticatorVersion::V2 => {
|
||||
v2::response::AuthenticatorResponse::new_pending_registration_success(
|
||||
v2::registration::RegistrationData {
|
||||
nonce: registration_data.nonce,
|
||||
gateway_data: registration_data.gateway_data.clone().into(),
|
||||
wg_port: registration_data.wg_port,
|
||||
},
|
||||
request_id,
|
||||
reply_to,
|
||||
)
|
||||
.to_bytes()
|
||||
.map_err(|err| {
|
||||
AuthenticatorError::FailedToSerializeResponsePacket { source: err }
|
||||
})?
|
||||
}
|
||||
AuthenticatorVersion::V3 => {
|
||||
v3::response::AuthenticatorResponse::new_pending_registration_success(
|
||||
v3::registration::RegistrationData {
|
||||
nonce: registration_data.nonce,
|
||||
gateway_data: registration_data.gateway_data.clone().into(),
|
||||
wg_port: registration_data.wg_port,
|
||||
},
|
||||
request_id,
|
||||
reply_to,
|
||||
)
|
||||
.to_bytes()
|
||||
.map_err(|err| {
|
||||
AuthenticatorError::FailedToSerializeResponsePacket { source: err }
|
||||
})?
|
||||
}
|
||||
AuthenticatorVersion::V4 => {
|
||||
v4::response::AuthenticatorResponse::new_pending_registration_success(
|
||||
v4::registration::RegistrationData {
|
||||
nonce: registration_data.nonce,
|
||||
gateway_data: registration_data.gateway_data.clone(),
|
||||
wg_port: registration_data.wg_port,
|
||||
},
|
||||
request_id,
|
||||
reply_to,
|
||||
)
|
||||
.to_bytes()
|
||||
.map_err(|err| {
|
||||
AuthenticatorError::FailedToSerializeResponsePacket { source: err }
|
||||
})?
|
||||
}
|
||||
AuthenticatorVersion::UNKNOWN => return Err(AuthenticatorError::UnknownVersion),
|
||||
};
|
||||
return Ok((bytes, reply_to));
|
||||
}
|
||||
|
||||
let peer = self.peer_manager.query_peer(remote_public).await?;
|
||||
@@ -190,15 +252,62 @@ impl<S: Storage + Clone + 'static> MixnetListener<S> {
|
||||
"there should be one private IP pair in the list".to_string(),
|
||||
));
|
||||
};
|
||||
return Ok(AuthenticatorResponse::new_registered(
|
||||
RegistredData {
|
||||
pub_key: PeerPublicKey::new(self.keypair().public_key().to_bytes().into()),
|
||||
private_ips: IpPair::new(allowed_ipv4, allowed_ipv6),
|
||||
wg_port: self.config.authenticator.announced_port,
|
||||
},
|
||||
reply_to,
|
||||
request_id,
|
||||
));
|
||||
let bytes = match AuthenticatorVersion::from(protocol) {
|
||||
AuthenticatorVersion::V1 => v1::response::AuthenticatorResponse::new_registered(
|
||||
v1::registration::RegistredData {
|
||||
pub_key: PeerPublicKey::new(self.keypair().public_key().to_bytes().into()),
|
||||
private_ip: allowed_ipv4.into(),
|
||||
wg_port: self.config.authenticator.announced_port,
|
||||
},
|
||||
reply_to,
|
||||
request_id,
|
||||
)
|
||||
.to_bytes()
|
||||
.map_err(|err| {
|
||||
AuthenticatorError::FailedToSerializeResponsePacket { source: err }
|
||||
})?,
|
||||
AuthenticatorVersion::V2 => v2::response::AuthenticatorResponse::new_registered(
|
||||
v2::registration::RegistredData {
|
||||
pub_key: PeerPublicKey::new(self.keypair().public_key().to_bytes().into()),
|
||||
private_ip: allowed_ipv4.into(),
|
||||
wg_port: self.config.authenticator.announced_port,
|
||||
},
|
||||
reply_to,
|
||||
request_id,
|
||||
)
|
||||
.to_bytes()
|
||||
.map_err(|err| {
|
||||
AuthenticatorError::FailedToSerializeResponsePacket { source: err }
|
||||
})?,
|
||||
AuthenticatorVersion::V3 => v3::response::AuthenticatorResponse::new_registered(
|
||||
v3::registration::RegistredData {
|
||||
pub_key: PeerPublicKey::new(self.keypair().public_key().to_bytes().into()),
|
||||
private_ip: allowed_ipv4.into(),
|
||||
wg_port: self.config.authenticator.announced_port,
|
||||
},
|
||||
reply_to,
|
||||
request_id,
|
||||
)
|
||||
.to_bytes()
|
||||
.map_err(|err| {
|
||||
AuthenticatorError::FailedToSerializeResponsePacket { source: err }
|
||||
})?,
|
||||
AuthenticatorVersion::V4 => v4::response::AuthenticatorResponse::new_registered(
|
||||
v4::registration::RegistredData {
|
||||
pub_key: PeerPublicKey::new(self.keypair().public_key().to_bytes().into()),
|
||||
private_ips: (allowed_ipv4, allowed_ipv6).into(),
|
||||
wg_port: self.config.authenticator.announced_port,
|
||||
},
|
||||
reply_to,
|
||||
request_id,
|
||||
)
|
||||
.to_bytes()
|
||||
.map_err(|err| {
|
||||
AuthenticatorError::FailedToSerializeResponsePacket { source: err }
|
||||
})?,
|
||||
AuthenticatorVersion::UNKNOWN => return Err(AuthenticatorError::UnknownVersion),
|
||||
};
|
||||
return Ok((bytes, reply_to));
|
||||
}
|
||||
|
||||
let private_ip_ref = registred_and_free
|
||||
@@ -217,57 +326,117 @@ impl<S: Storage + Clone + 'static> MixnetListener<S> {
|
||||
);
|
||||
let registration_data = RegistrationData {
|
||||
nonce,
|
||||
gateway_data,
|
||||
gateway_data: gateway_data.clone(),
|
||||
wg_port: self.config.authenticator.announced_port,
|
||||
};
|
||||
registred_and_free
|
||||
.registration_in_progres
|
||||
.insert(remote_public, registration_data.clone());
|
||||
let bytes = match AuthenticatorVersion::from(protocol) {
|
||||
AuthenticatorVersion::V1 => {
|
||||
v1::response::AuthenticatorResponse::new_pending_registration_success(
|
||||
v1::registration::RegistrationData {
|
||||
nonce: registration_data.nonce,
|
||||
gateway_data: v1::GatewayClient {
|
||||
pub_key: gateway_data.pub_key,
|
||||
private_ip: gateway_data.private_ips.ipv4.into(),
|
||||
mac: v1::ClientMac::new(gateway_data.mac.to_vec()),
|
||||
},
|
||||
wg_port: registration_data.wg_port,
|
||||
},
|
||||
request_id,
|
||||
reply_to,
|
||||
)
|
||||
.to_bytes()
|
||||
.map_err(|err| {
|
||||
AuthenticatorError::FailedToSerializeResponsePacket { source: err }
|
||||
})?
|
||||
}
|
||||
AuthenticatorVersion::V2 => {
|
||||
v2::response::AuthenticatorResponse::new_pending_registration_success(
|
||||
v2::registration::RegistrationData {
|
||||
nonce: registration_data.nonce,
|
||||
gateway_data: registration_data.gateway_data.into(),
|
||||
wg_port: registration_data.wg_port,
|
||||
},
|
||||
request_id,
|
||||
reply_to,
|
||||
)
|
||||
.to_bytes()
|
||||
.map_err(|err| {
|
||||
AuthenticatorError::FailedToSerializeResponsePacket { source: err }
|
||||
})?
|
||||
}
|
||||
AuthenticatorVersion::V3 => {
|
||||
v3::response::AuthenticatorResponse::new_pending_registration_success(
|
||||
v3::registration::RegistrationData {
|
||||
nonce: registration_data.nonce,
|
||||
gateway_data: registration_data.gateway_data.into(),
|
||||
wg_port: registration_data.wg_port,
|
||||
},
|
||||
request_id,
|
||||
reply_to,
|
||||
)
|
||||
.to_bytes()
|
||||
.map_err(|err| {
|
||||
AuthenticatorError::FailedToSerializeResponsePacket { source: err }
|
||||
})?
|
||||
}
|
||||
AuthenticatorVersion::V4 => {
|
||||
v4::response::AuthenticatorResponse::new_pending_registration_success(
|
||||
v4::registration::RegistrationData {
|
||||
nonce: registration_data.nonce,
|
||||
gateway_data: registration_data.gateway_data,
|
||||
wg_port: registration_data.wg_port,
|
||||
},
|
||||
request_id,
|
||||
reply_to,
|
||||
)
|
||||
.to_bytes()
|
||||
.map_err(|err| {
|
||||
AuthenticatorError::FailedToSerializeResponsePacket { source: err }
|
||||
})?
|
||||
}
|
||||
AuthenticatorVersion::UNKNOWN => return Err(AuthenticatorError::UnknownVersion),
|
||||
};
|
||||
|
||||
Ok(AuthenticatorResponse::new_pending_registration_success(
|
||||
registration_data,
|
||||
request_id,
|
||||
reply_to,
|
||||
))
|
||||
Ok((bytes, reply_to))
|
||||
}
|
||||
|
||||
async fn on_final_request(
|
||||
&mut self,
|
||||
final_message: FinalMessage,
|
||||
final_message: Box<dyn FinalMessage + Send + Sync + 'static>,
|
||||
protocol: Protocol,
|
||||
request_id: u64,
|
||||
reply_to: Recipient,
|
||||
) -> AuthenticatorHandleResult {
|
||||
let mut registred_and_free = self.registred_and_free.write().await;
|
||||
let registration_data = registred_and_free
|
||||
.registration_in_progres
|
||||
.get(&final_message.gateway_client.pub_key())
|
||||
.get(&final_message.pub_key())
|
||||
.ok_or(AuthenticatorError::RegistrationNotInProgress)?
|
||||
.clone();
|
||||
|
||||
if final_message
|
||||
.gateway_client
|
||||
.verify(self.keypair().private_key(), registration_data.nonce)
|
||||
.is_err()
|
||||
{
|
||||
return Err(AuthenticatorError::MacVerificationFailure);
|
||||
}
|
||||
|
||||
let mut peer = Peer::new(Key::new(final_message.gateway_client.pub_key.to_bytes()));
|
||||
let mut peer = Peer::new(Key::new(final_message.pub_key().to_bytes()));
|
||||
peer.allowed_ips
|
||||
.push(IpAddrMask::new(final_message.private_ips().ipv4.into(), 32));
|
||||
peer.allowed_ips.push(IpAddrMask::new(
|
||||
final_message.gateway_client.private_ips.ipv4.into(),
|
||||
32,
|
||||
));
|
||||
peer.allowed_ips.push(IpAddrMask::new(
|
||||
final_message.gateway_client.private_ips.ipv6.into(),
|
||||
final_message.private_ips().ipv6.into(),
|
||||
128,
|
||||
));
|
||||
|
||||
// If gateway does ecash verification and client sends a credential, we do the additional
|
||||
// credential verification. Later this will become mandatory.
|
||||
if let (Some(ecash_verifier), Some(credential)) = (
|
||||
self.ecash_verifier.clone(),
|
||||
final_message.credential.clone(),
|
||||
) {
|
||||
if let (Some(ecash_verifier), Some(credential)) =
|
||||
(self.ecash_verifier.clone(), final_message.credential())
|
||||
{
|
||||
let client_id = ecash_verifier
|
||||
.storage()
|
||||
.insert_wireguard_peer(&peer, true)
|
||||
@@ -297,17 +466,56 @@ impl<S: Storage + Clone + 'static> MixnetListener<S> {
|
||||
}
|
||||
registred_and_free
|
||||
.registration_in_progres
|
||||
.remove(&final_message.gateway_client.pub_key());
|
||||
.remove(&final_message.pub_key());
|
||||
|
||||
Ok(AuthenticatorResponse::new_registered(
|
||||
RegistredData {
|
||||
pub_key: registration_data.gateway_data.pub_key,
|
||||
private_ips: registration_data.gateway_data.private_ips,
|
||||
wg_port: registration_data.wg_port,
|
||||
},
|
||||
reply_to,
|
||||
request_id,
|
||||
))
|
||||
let bytes = match AuthenticatorVersion::from(protocol) {
|
||||
AuthenticatorVersion::V1 => v1::response::AuthenticatorResponse::new_registered(
|
||||
v1::registration::RegistredData {
|
||||
pub_key: registration_data.gateway_data.pub_key,
|
||||
private_ip: registration_data.gateway_data.private_ips.ipv4.into(),
|
||||
wg_port: registration_data.wg_port,
|
||||
},
|
||||
reply_to,
|
||||
request_id,
|
||||
)
|
||||
.to_bytes()
|
||||
.map_err(|err| AuthenticatorError::FailedToSerializeResponsePacket { source: err })?,
|
||||
AuthenticatorVersion::V2 => v2::response::AuthenticatorResponse::new_registered(
|
||||
v2::registration::RegistredData {
|
||||
pub_key: registration_data.gateway_data.pub_key,
|
||||
private_ip: registration_data.gateway_data.private_ips.ipv4.into(),
|
||||
wg_port: registration_data.wg_port,
|
||||
},
|
||||
reply_to,
|
||||
request_id,
|
||||
)
|
||||
.to_bytes()
|
||||
.map_err(|err| AuthenticatorError::FailedToSerializeResponsePacket { source: err })?,
|
||||
AuthenticatorVersion::V3 => v3::response::AuthenticatorResponse::new_registered(
|
||||
v3::registration::RegistredData {
|
||||
pub_key: registration_data.gateway_data.pub_key,
|
||||
private_ip: registration_data.gateway_data.private_ips.ipv4.into(),
|
||||
wg_port: registration_data.wg_port,
|
||||
},
|
||||
reply_to,
|
||||
request_id,
|
||||
)
|
||||
.to_bytes()
|
||||
.map_err(|err| AuthenticatorError::FailedToSerializeResponsePacket { source: err })?,
|
||||
AuthenticatorVersion::V4 => v4::response::AuthenticatorResponse::new_registered(
|
||||
v4::registration::RegistredData {
|
||||
pub_key: registration_data.gateway_data.pub_key,
|
||||
private_ips: registration_data.gateway_data.private_ips,
|
||||
wg_port: registration_data.wg_port,
|
||||
},
|
||||
reply_to,
|
||||
request_id,
|
||||
)
|
||||
.to_bytes()
|
||||
.map_err(|err| AuthenticatorError::FailedToSerializeResponsePacket { source: err })?,
|
||||
AuthenticatorVersion::UNKNOWN => return Err(AuthenticatorError::UnknownVersion),
|
||||
};
|
||||
Ok((bytes, reply_to))
|
||||
}
|
||||
|
||||
async fn credential_verification(
|
||||
@@ -343,22 +551,75 @@ impl<S: Storage + Clone + 'static> MixnetListener<S> {
|
||||
|
||||
async fn on_query_bandwidth_request(
|
||||
&mut self,
|
||||
peer_public_key: PeerPublicKey,
|
||||
msg: Box<dyn QueryBandwidthMessage + Send + Sync + 'static>,
|
||||
protocol: Protocol,
|
||||
request_id: u64,
|
||||
reply_to: Recipient,
|
||||
) -> AuthenticatorHandleResult {
|
||||
let bandwidth_data = self.peer_manager.query_bandwidth(peer_public_key).await?;
|
||||
Ok(AuthenticatorResponse::new_remaining_bandwidth(
|
||||
bandwidth_data,
|
||||
reply_to,
|
||||
request_id,
|
||||
))
|
||||
let bandwidth_data = self.peer_manager.query_bandwidth(msg).await?;
|
||||
let bytes = match AuthenticatorVersion::from(protocol) {
|
||||
AuthenticatorVersion::V1 => {
|
||||
v1::response::AuthenticatorResponse::new_remaining_bandwidth(
|
||||
bandwidth_data.map(|data| v1::registration::RemainingBandwidthData {
|
||||
available_bandwidth: data.available_bandwidth as u64,
|
||||
suspended: false,
|
||||
}),
|
||||
reply_to,
|
||||
request_id,
|
||||
)
|
||||
.to_bytes()
|
||||
.map_err(|err| {
|
||||
AuthenticatorError::FailedToSerializeResponsePacket { source: err }
|
||||
})?
|
||||
}
|
||||
AuthenticatorVersion::V2 => {
|
||||
v2::response::AuthenticatorResponse::new_remaining_bandwidth(
|
||||
bandwidth_data.map(|data| v2::registration::RemainingBandwidthData {
|
||||
available_bandwidth: data.available_bandwidth,
|
||||
}),
|
||||
reply_to,
|
||||
request_id,
|
||||
)
|
||||
.to_bytes()
|
||||
.map_err(|err| {
|
||||
AuthenticatorError::FailedToSerializeResponsePacket { source: err }
|
||||
})?
|
||||
}
|
||||
AuthenticatorVersion::V3 => {
|
||||
v3::response::AuthenticatorResponse::new_remaining_bandwidth(
|
||||
bandwidth_data.map(|data| v3::registration::RemainingBandwidthData {
|
||||
available_bandwidth: data.available_bandwidth,
|
||||
}),
|
||||
reply_to,
|
||||
request_id,
|
||||
)
|
||||
.to_bytes()
|
||||
.map_err(|err| {
|
||||
AuthenticatorError::FailedToSerializeResponsePacket { source: err }
|
||||
})?
|
||||
}
|
||||
AuthenticatorVersion::V4 => {
|
||||
v4::response::AuthenticatorResponse::new_remaining_bandwidth(
|
||||
bandwidth_data.map(|data| v4::registration::RemainingBandwidthData {
|
||||
available_bandwidth: data.available_bandwidth,
|
||||
}),
|
||||
reply_to,
|
||||
request_id,
|
||||
)
|
||||
.to_bytes()
|
||||
.map_err(|err| {
|
||||
AuthenticatorError::FailedToSerializeResponsePacket { source: err }
|
||||
})?
|
||||
}
|
||||
AuthenticatorVersion::UNKNOWN => return Err(AuthenticatorError::UnknownVersion),
|
||||
};
|
||||
Ok((bytes, reply_to))
|
||||
}
|
||||
|
||||
async fn on_topup_bandwidth_request(
|
||||
&mut self,
|
||||
peer_public_key: PeerPublicKey,
|
||||
credential: CredentialSpendingData,
|
||||
msg: Box<dyn TopUpMessage + Send + Sync + 'static>,
|
||||
protocol: Protocol,
|
||||
request_id: u64,
|
||||
reply_to: Recipient,
|
||||
) -> AuthenticatorHandleResult {
|
||||
@@ -367,7 +628,7 @@ impl<S: Storage + Clone + 'static> MixnetListener<S> {
|
||||
};
|
||||
let client_id = ecash_verifier
|
||||
.storage()
|
||||
.get_wireguard_peer(&peer_public_key.to_string())
|
||||
.get_wireguard_peer(&msg.pub_key().to_string())
|
||||
.await?
|
||||
.ok_or(AuthenticatorError::MissingClientBandwidthEntry)?
|
||||
.client_id
|
||||
@@ -382,7 +643,7 @@ impl<S: Storage + Clone + 'static> MixnetListener<S> {
|
||||
|
||||
let client_bandwidth = ClientBandwidth::new(bandwidth.into());
|
||||
let mut verifier = CredentialVerifier::new(
|
||||
CredentialSpendingRequest::new(credential),
|
||||
CredentialSpendingRequest::new(msg.credential()),
|
||||
ecash_verifier.clone(),
|
||||
BandwidthStorageManager::new(
|
||||
ecash_verifier.storage().clone(),
|
||||
@@ -394,13 +655,31 @@ impl<S: Storage + Clone + 'static> MixnetListener<S> {
|
||||
);
|
||||
let available_bandwidth = verifier.verify().await?;
|
||||
|
||||
Ok(AuthenticatorResponse::new_topup_bandwidth(
|
||||
RemainingBandwidthData {
|
||||
available_bandwidth,
|
||||
},
|
||||
reply_to,
|
||||
request_id,
|
||||
))
|
||||
let bytes = match AuthenticatorVersion::from(protocol) {
|
||||
AuthenticatorVersion::V4 => v4::response::AuthenticatorResponse::new_topup_bandwidth(
|
||||
v4::registration::RemainingBandwidthData {
|
||||
available_bandwidth,
|
||||
},
|
||||
reply_to,
|
||||
request_id,
|
||||
)
|
||||
.to_bytes()
|
||||
.map_err(|err| AuthenticatorError::FailedToSerializeResponsePacket { source: err })?,
|
||||
AuthenticatorVersion::V3 => v3::response::AuthenticatorResponse::new_topup_bandwidth(
|
||||
v3::registration::RemainingBandwidthData {
|
||||
available_bandwidth,
|
||||
},
|
||||
reply_to,
|
||||
request_id,
|
||||
)
|
||||
.to_bytes()
|
||||
.map_err(|err| AuthenticatorError::FailedToSerializeResponsePacket { source: err })?,
|
||||
AuthenticatorVersion::V1 | AuthenticatorVersion::V2 | AuthenticatorVersion::UNKNOWN => {
|
||||
return Err(AuthenticatorError::UnknownVersion)
|
||||
}
|
||||
};
|
||||
|
||||
Ok((bytes, reply_to))
|
||||
}
|
||||
|
||||
async fn on_reconstructed_message(
|
||||
@@ -412,62 +691,52 @@ impl<S: Storage + Clone + 'static> MixnetListener<S> {
|
||||
reconstructed.sender_tag
|
||||
);
|
||||
|
||||
let request = match deserialize_request(&reconstructed) {
|
||||
Err(AuthenticatorError::InvalidPacketVersion(version)) => {
|
||||
return self.on_version_mismatch(version, &reconstructed);
|
||||
}
|
||||
req => req,
|
||||
}?;
|
||||
let request = deserialize_request(&reconstructed)?;
|
||||
|
||||
match request.data {
|
||||
AuthenticatorRequestData::Initial(init_msg) => {
|
||||
self.on_initial_request(init_msg, request.request_id, request.reply_to)
|
||||
match request {
|
||||
AuthenticatorRequest::Initial {
|
||||
msg,
|
||||
reply_to,
|
||||
request_id,
|
||||
protocol,
|
||||
} => {
|
||||
self.on_initial_request(msg, protocol, request_id, reply_to)
|
||||
.await
|
||||
}
|
||||
AuthenticatorRequestData::Final(final_msg) => {
|
||||
self.on_final_request(*final_msg, request.request_id, request.reply_to)
|
||||
AuthenticatorRequest::Final {
|
||||
msg,
|
||||
reply_to,
|
||||
request_id,
|
||||
protocol,
|
||||
} => {
|
||||
self.on_final_request(msg, protocol, request_id, reply_to)
|
||||
.await
|
||||
}
|
||||
AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
|
||||
self.on_query_bandwidth_request(
|
||||
peer_public_key,
|
||||
request.request_id,
|
||||
request.reply_to,
|
||||
)
|
||||
.await
|
||||
AuthenticatorRequest::QueryBandwidth {
|
||||
msg,
|
||||
reply_to,
|
||||
request_id,
|
||||
protocol,
|
||||
} => {
|
||||
self.on_query_bandwidth_request(msg, protocol, request_id, reply_to)
|
||||
.await
|
||||
}
|
||||
AuthenticatorRequestData::TopUpBandwidth(topup_message) => {
|
||||
self.on_topup_bandwidth_request(
|
||||
topup_message.pub_key,
|
||||
topup_message.credential,
|
||||
request.request_id,
|
||||
request.reply_to,
|
||||
)
|
||||
.await
|
||||
AuthenticatorRequest::TopUpBandwidth {
|
||||
msg,
|
||||
reply_to,
|
||||
request_id,
|
||||
protocol,
|
||||
} => {
|
||||
self.on_topup_bandwidth_request(msg, protocol, request_id, reply_to)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_version_mismatch(
|
||||
&self,
|
||||
version: u8,
|
||||
_reconstructed: &ReconstructedMessage,
|
||||
) -> AuthenticatorHandleResult {
|
||||
// If it's possible to parse, do so and return back a response, otherwise just drop
|
||||
Err(AuthenticatorError::InvalidPacketVersion(version))
|
||||
}
|
||||
|
||||
// When an incoming mixnet message triggers a response that we send back.
|
||||
async fn handle_response(&self, response: AuthenticatorResponse) -> Result<()> {
|
||||
let recipient = response.recipient();
|
||||
|
||||
let response_packet = response.to_bytes().map_err(|err| {
|
||||
log::error!("Failed to serialize response packet");
|
||||
AuthenticatorError::FailedToSerializeResponsePacket { source: err }
|
||||
})?;
|
||||
|
||||
async fn handle_response(&self, response: Vec<u8>, recipient: Recipient) -> Result<()> {
|
||||
let input_message =
|
||||
InputMessage::new_regular(recipient, response_packet, TransmissionLane::General, None);
|
||||
InputMessage::new_regular(recipient, response, TransmissionLane::General, None);
|
||||
self.mixnet_client
|
||||
.send(input_message)
|
||||
.await
|
||||
@@ -491,8 +760,8 @@ impl<S: Storage + Clone + 'static> MixnetListener<S> {
|
||||
msg = self.mixnet_client.next() => {
|
||||
if let Some(msg) = msg {
|
||||
match self.on_reconstructed_message(msg).await {
|
||||
Ok(response) => {
|
||||
if let Err(err) = self.handle_response(response).await {
|
||||
Ok((response, recipient)) => {
|
||||
if let Err(err) = self.handle_response(response, recipient).await {
|
||||
log::error!("Mixnet listener failed to handle response: {err}");
|
||||
}
|
||||
}
|
||||
@@ -524,8 +793,6 @@ fn deserialize_request(reconstructed: &ReconstructedMessage) -> Result<Authentic
|
||||
match request_version {
|
||||
[1, _] => v1::request::AuthenticatorRequest::from_reconstructed_message(reconstructed)
|
||||
.map_err(|err| AuthenticatorError::FailedToDeserializeTaggedPacket { source: err })
|
||||
.map(Into::<v2::request::AuthenticatorRequest>::into)
|
||||
.map(Into::<v3::request::AuthenticatorRequest>::into)
|
||||
.map(Into::into),
|
||||
[2, request_type] => {
|
||||
if request_type == ServiceProviderType::Authenticator as u8 {
|
||||
@@ -556,6 +823,7 @@ fn deserialize_request(reconstructed: &ReconstructedMessage) -> Result<Authentic
|
||||
.map_err(|err| AuthenticatorError::FailedToDeserializeTaggedPacket {
|
||||
source: err,
|
||||
})
|
||||
.map(Into::into)
|
||||
} else {
|
||||
Err(AuthenticatorError::InvalidPacketType(request_type))
|
||||
}
|
||||
|
||||
@@ -4,7 +4,10 @@
|
||||
use crate::error::*;
|
||||
use defguard_wireguard_rs::{host::Peer, key::Key};
|
||||
use futures::channel::oneshot;
|
||||
use nym_authenticator_requests::latest::registration::{GatewayClient, RemainingBandwidthData};
|
||||
use nym_authenticator_requests::{
|
||||
latest::registration::{GatewayClient, RemainingBandwidthData},
|
||||
traits::QueryBandwidthMessage,
|
||||
};
|
||||
use nym_wireguard::{
|
||||
peer_controller::{
|
||||
AddPeerControlResponse, PeerControlRequest, QueryBandwidthControlResponse,
|
||||
@@ -92,9 +95,9 @@ impl PeerManager {
|
||||
|
||||
pub async fn query_bandwidth(
|
||||
&mut self,
|
||||
peer_public_key: PeerPublicKey,
|
||||
msg: Box<dyn QueryBandwidthMessage + Send + Sync + 'static>,
|
||||
) -> Result<Option<RemainingBandwidthData>> {
|
||||
let key = Key::new(peer_public_key.to_bytes());
|
||||
let key = Key::new(msg.pub_key().to_bytes());
|
||||
let (response_tx, response_rx) = oneshot::channel();
|
||||
let msg = PeerControlRequest::QueryBandwidth { key, response_tx };
|
||||
self.wireguard_gateway_data
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
[package]
|
||||
name = "nym-network-requester"
|
||||
license = "GPL-3.0"
|
||||
version = "1.1.43"
|
||||
version = "1.1.44"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version = "1.70"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-cli"
|
||||
version = "1.1.43"
|
||||
version = "1.1.44"
|
||||
authors.workspace = true
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nymvisor"
|
||||
version = "0.1.8"
|
||||
version = "0.1.9"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
|
||||
Reference in New Issue
Block a user