Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f0a4350e83 | |||
| d5c2a01a34 | |||
| b1c58b36fe | |||
| dfbcc781db | |||
| 5026960169 | |||
| 7c2710b61a | |||
| bf9fc2d537 | |||
| 4182af9199 | |||
| 408d803344 | |||
| c2a5d6c035 | |||
| 1136901daf | |||
| 593a1da0ff | |||
| 9c17b7c269 | |||
| df398dbe05 | |||
| effd03b2f5 | |||
| e00db6adb9 | |||
| fd207d4699 | |||
| b9126dfc0e | |||
| 7bbe153b8f | |||
| 36e1e73ed2 | |||
| 6e23322ac4 | |||
| 729eedc960 | |||
| 025cbf5231 | |||
| 3db3959a74 | |||
| 3ba83795d4 | |||
| 39b01d10bd | |||
| 6060ce5fb8 | |||
| 8fbad9cad8 | |||
| 650865e59a | |||
| 08e580ec8b | |||
| ad86ec9315 | |||
| 53ab4c8ec9 |
@@ -102,6 +102,18 @@ jobs:
|
||||
nym-wallet/target/release/bundle/dmg/*.dmg
|
||||
nym-wallet/target/release/bundle/macos/*.app.tar.gz*
|
||||
|
||||
- name: Deploy artifacts to CI www
|
||||
continue-on-error: true
|
||||
uses: easingthemes/ssh-deploy@main
|
||||
env:
|
||||
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
|
||||
ARGS: "-avzr"
|
||||
SOURCE: "nym-wallet/target/release/bundle/macos/nym-wallet.app.tar.gz"
|
||||
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
|
||||
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
|
||||
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/builds/${{ github.ref_name }}/nym-wallet
|
||||
EXCLUDE: "/dist/, /node_modules/"
|
||||
|
||||
push-release-data:
|
||||
if: ${{ (startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
|
||||
uses: ./.github/workflows/release-calculate-hash.yml
|
||||
|
||||
@@ -77,6 +77,18 @@ jobs:
|
||||
nym-wallet/target/release/bundle/appimage/*.AppImage
|
||||
nym-wallet/target/release/bundle/appimage/*.AppImage.tar.gz*
|
||||
|
||||
- name: Deploy artifacts to CI www
|
||||
continue-on-error: true
|
||||
uses: easingthemes/ssh-deploy@main
|
||||
env:
|
||||
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
|
||||
ARGS: "-avzr"
|
||||
SOURCE: "nym-wallet/target/release/bundle/appimage/nym-wallet*.AppImage.tar.gz"
|
||||
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
|
||||
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
|
||||
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/builds/${{ github.ref_name }}/nym-wallet
|
||||
EXCLUDE: "/dist/, /node_modules/"
|
||||
|
||||
push-release-data:
|
||||
if: ${{ (startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
|
||||
uses: ./.github/workflows/release-calculate-hash.yml
|
||||
|
||||
@@ -97,6 +97,18 @@ jobs:
|
||||
nym-wallet/target/release/bundle/msi/*.msi
|
||||
nym-wallet/target/release/bundle/msi/*.msi.zip*
|
||||
|
||||
- name: Deploy artifacts to CI www
|
||||
continue-on-error: true
|
||||
uses: easingthemes/ssh-deploy@main
|
||||
env:
|
||||
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
|
||||
ARGS: "-avzr"
|
||||
SOURCE: "nym-wallet/target/release/bundle/msi/nym-wallet_1.*.msi"
|
||||
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
|
||||
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
|
||||
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/builds/${{ github.ref_name }}/nym-wallet
|
||||
EXCLUDE: "/dist/, /node_modules/"
|
||||
|
||||
push-release-data:
|
||||
if: ${{ (startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
|
||||
uses: ./.github/workflows/release-calculate-hash.yml
|
||||
|
||||
@@ -4,6 +4,16 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [2024.4-nutella] (2024-05-08)
|
||||
|
||||
- [fix] apply disable_poisson_rate from internal NR/IPR cfgs ([#4579])
|
||||
- updating sign commands to include nym-node ([#4578])
|
||||
- changed nym-node redirects from 308 'Permanent Redirect' to 303: 'See Other' ([#4572])
|
||||
|
||||
[#4579]: https://github.com/nymtech/nym/pull/4579
|
||||
[#4578]: https://github.com/nymtech/nym/pull/4578
|
||||
[#4572]: https://github.com/nymtech/nym/pull/4572
|
||||
|
||||
## [2024.3-eclipse] (2024-04-22)
|
||||
|
||||
- Initial release of the first iteration of the Nym Node
|
||||
|
||||
Generated
+3
-3
@@ -5684,7 +5684,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-gateway"
|
||||
version = "1.1.35"
|
||||
version = "1.1.36"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -6098,7 +6098,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-requester"
|
||||
version = "1.1.34"
|
||||
version = "1.1.35"
|
||||
dependencies = [
|
||||
"addr",
|
||||
"anyhow",
|
||||
@@ -6167,7 +6167,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-node"
|
||||
version = "1.1.0"
|
||||
version = "1.1.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bip39",
|
||||
|
||||
@@ -8,3 +8,12 @@ use thiserror::Error;
|
||||
pub struct ConfigUpgradeFailure {
|
||||
pub current_version: String,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum InvalidTrafficModeFailure {
|
||||
#[error("attempted to set medium toggle traffic mode with fast mode flag")]
|
||||
MediumToggleWithFastMode,
|
||||
|
||||
#[error("attempted to set medium toggle traffic mode with no cover flag")]
|
||||
MediumToggleWithNoCover,
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ const DEFAULT_MAXIMUM_REPLY_SURB_AGE: Duration = Duration::from_secs(12 * 60 * 6
|
||||
// 24 hours
|
||||
const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(24 * 60 * 60);
|
||||
|
||||
use crate::error::InvalidTrafficModeFailure;
|
||||
pub use nym_country_group::CountryGroup;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
|
||||
@@ -127,6 +128,56 @@ impl Config {
|
||||
self
|
||||
}
|
||||
|
||||
// TODO: this should be refactored properly
|
||||
// as of 12.09.23 the below is true (not sure how this comment will rot in the future)
|
||||
// medium_toggle:
|
||||
// - sets secondary packet size to 16kb
|
||||
// - disables poisson distribution of the main traffic stream
|
||||
// - sets the cover traffic stream to 1 packet / 5s (on average)
|
||||
// - disables per hop delay
|
||||
//
|
||||
// fastmode (to be renamed to `fast-poisson`):
|
||||
// - sets average per hop delay to 10ms
|
||||
// - sets the cover traffic stream to 1 packet / 2000s (on average); for all intents and purposes it disables the stream
|
||||
// - sets the poisson distribution of the main traffic stream to 4ms, i.e. 250 packets / s on average
|
||||
//
|
||||
// no_cover:
|
||||
// - disables poisson distribution of the main traffic stream
|
||||
// - disables the secondary cover traffic stream
|
||||
#[doc(hidden)]
|
||||
pub fn try_apply_traffic_modes(
|
||||
&mut self,
|
||||
disable_poisson_process: bool,
|
||||
medium_toggle: bool,
|
||||
fast_mode: bool,
|
||||
no_cover: bool,
|
||||
) -> Result<(), InvalidTrafficModeFailure> {
|
||||
if disable_poisson_process {
|
||||
self.set_no_poisson_process()
|
||||
}
|
||||
|
||||
if medium_toggle {
|
||||
if fast_mode {
|
||||
return Err(InvalidTrafficModeFailure::MediumToggleWithFastMode);
|
||||
}
|
||||
if no_cover {
|
||||
return Err(InvalidTrafficModeFailure::MediumToggleWithNoCover);
|
||||
}
|
||||
|
||||
self.set_experimental_medium_toggle();
|
||||
}
|
||||
|
||||
if fast_mode {
|
||||
self.set_high_default_traffic_volume()
|
||||
}
|
||||
|
||||
if no_cover {
|
||||
self.set_no_cover_traffic();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_high_default_traffic_volume(&mut self) {
|
||||
self.debug.traffic.average_packet_delay = Duration::from_millis(10);
|
||||
// basically don't really send cover messages
|
||||
@@ -136,6 +187,15 @@ impl Config {
|
||||
self.debug.traffic.message_sending_average_delay = Duration::from_millis(4);
|
||||
}
|
||||
|
||||
/// Enable medium mixnet traffic, for experiments only.
|
||||
/// This includes things like disabling cover traffic, no per hop delays, etc.
|
||||
#[doc(hidden)]
|
||||
pub fn set_experimental_medium_toggle(&mut self) {
|
||||
self.set_no_cover_traffic_with_keepalive();
|
||||
self.set_no_per_hop_delays();
|
||||
self.debug.traffic.secondary_packet_size = Some(PacketSize::ExtendedPacket16);
|
||||
}
|
||||
|
||||
pub fn with_disabled_poisson_process(mut self, disabled: bool) -> Self {
|
||||
if disabled {
|
||||
self.set_no_poisson_process()
|
||||
|
||||
@@ -442,7 +442,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
}
|
||||
|
||||
debug_assert!(self.connection.is_available());
|
||||
log::trace!("Registering gateway");
|
||||
log::debug!("Registering gateway");
|
||||
|
||||
// it's fine to instantiate it here as it's only used once (during authentication or registration)
|
||||
// and putting it into the GatewayClient struct would be a hassle
|
||||
@@ -494,6 +494,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
if !self.connection.is_established() {
|
||||
return Err(GatewayClientError::ConnectionNotEstablished);
|
||||
}
|
||||
log::debug!("Authenticating with gateway");
|
||||
|
||||
// it's fine to instantiate it here as it's only used once (during authentication or registration)
|
||||
// and putting it into the GatewayClient struct would be a hassle
|
||||
@@ -529,6 +530,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
self.authenticated = status;
|
||||
self.bandwidth_remaining = bandwidth_remaining;
|
||||
self.negotiated_protocol = protocol_version;
|
||||
log::debug!("authenticated: {status}, bandwidth remaining: {bandwidth_remaining}");
|
||||
Ok(())
|
||||
}
|
||||
ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)),
|
||||
@@ -541,10 +543,11 @@ impl<C, St> GatewayClient<C, St> {
|
||||
&mut self,
|
||||
) -> Result<Arc<SharedKeys>, GatewayClientError> {
|
||||
if self.authenticated {
|
||||
debug!("Already authenticated");
|
||||
return if let Some(shared_key) = &self.shared_key {
|
||||
Ok(Arc::clone(shared_key))
|
||||
} else {
|
||||
Err(GatewayClientError::AuthenticationFailure)
|
||||
Err(GatewayClientError::AuthenticationFailureWithPreexistingSharedKey)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,9 @@ pub enum GatewayClientError {
|
||||
#[error("Authentication failure")]
|
||||
AuthenticationFailure,
|
||||
|
||||
#[error("Authentication failure with preexisting shared key")]
|
||||
AuthenticationFailureWithPreexistingSharedKey,
|
||||
|
||||
#[error("Timed out")]
|
||||
Timeout,
|
||||
|
||||
|
||||
@@ -159,7 +159,7 @@ impl TunDevice {
|
||||
"add",
|
||||
&format!("{}/{}", ipv6, netmaskv6),
|
||||
"dev",
|
||||
&tun.name(),
|
||||
(tun.name()),
|
||||
])
|
||||
.output()?;
|
||||
Ok(tun)
|
||||
|
||||
@@ -50,7 +50,7 @@ pub struct DelegationWithEverything {
|
||||
pub accumulated_by_delegates: Option<DecCoin>,
|
||||
pub accumulated_by_operator: Option<DecCoin>,
|
||||
pub block_height: u64,
|
||||
pub delegated_on_iso_datetime: String,
|
||||
pub delegated_on_iso_datetime: Option<String>,
|
||||
pub cost_params: Option<MixNodeCostParams>,
|
||||
pub avg_uptime_percent: Option<u8>,
|
||||
|
||||
@@ -60,6 +60,8 @@ pub struct DelegationWithEverything {
|
||||
pub uses_vesting_contract_tokens: bool,
|
||||
pub unclaimed_rewards: Option<DecCoin>,
|
||||
|
||||
pub errors: Option<String>,
|
||||
|
||||
// DEPRECATED, IF POSSIBLE TRY TO DISCONTINUE USE OF IT!
|
||||
pub pending_events: Vec<DelegationEvent>,
|
||||
pub mixnode_is_unbonding: Option<bool>,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# Summary
|
||||
|
||||
- [Introduction](introduction.md)
|
||||
- [Changelog](changelog.md)
|
||||
|
||||
# Binaries
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# Changelog
|
||||
|
||||
This page displays a full list of all the changes during our release cycle from [`v2024.3-eclipse`](https://github.com/nymtech/nym/blob/nym-binaries-v2024.3-eclipse/CHANGELOG.md) onwards. Operators can find here the newest updates together with links to relevant documentation. The list is sorted so that the newest changes appear first.
|
||||
|
||||
## `v2024.4-nutella`
|
||||
|
||||
- [Merged PRs](https://github.com/nymtech/nym/milestone/59?closed=1)
|
||||
- [`nym-node`](nodes/nym-node.md) version `1.1.1`
|
||||
- This release also contains: `nym-gateway` and `nym-network-requester` binaries
|
||||
- core improvements on nym-node configuration
|
||||
- Nym wallet changes:
|
||||
- Adding `nym-node` command to bonding screens
|
||||
- Fixed the delegation issues with fixing RPC
|
||||
- [Network configuration](nodes/configuration.md#connectivity-test-and-configuration) section updates, in particular for `--mode mixnode` operators
|
||||
- [VPS IPv6 troubleshooting](troubleshooting/vps-isp.md#ipv6-troubleshooting) updates
|
||||
|
||||
## `v2024.3-eclipse`
|
||||
|
||||
- Release [Changelog.md](https://github.com/nymtech/nym/blob/nym-binaries-v2024.3-eclipse/CHANGELOG.md)
|
||||
- [`nym-node`](nodes/nym-node.md) initial release
|
||||
- New tool for monitoring Gateways performance [harbourmaster.nymtech.net](https://harbourmaster.nymtech.net)
|
||||
- New versioning `1.1.0+nymnode` mainly for internal migration testing, not essential for operational use. We aim to correct this in a future release to ensure mixnodes feature correctly in the main API
|
||||
- New [VPS specs & configuration](nodes/vps-setup.md) page
|
||||
- New [configuration page](nodes/configuration.md) with [connectivity setup guide](nodes/configuration.md#connectivity-test-and-configuration) - a new requirement for `exit-gateway`
|
||||
- API endpoints redirection: Nym-mixnode and nym-gateway endpoints will eventually be deprecated; due to this, their endpoints will be redirected to new routes once the `nym-node` has been migrated and is running
|
||||
|
||||
**API endpoints redirection**
|
||||
|
||||
| Previous endpoint | New endpoint |
|
||||
| --- | --- |
|
||||
| `http://<IP>:8000/stats` | `http://<IP>:8000/api/v1/metrics/mixing` |
|
||||
| `http://<IP>:8000/hardware` | `http://<IP>:8000/api/v1/system-info` |
|
||||
| `http://<IP>:8000/description` | `http://<IP>:8000/api/v1/description` |
|
||||
@@ -161,15 +161,49 @@ This lets your operating system know it's ok to reload the service configuration
|
||||
|
||||
## Connectivity Test and Configuration
|
||||
|
||||
```admonish info
|
||||
**This chapter is relevant only for operators running an `exit-gateway` mode.** If this is not your case, please proceed to [bonding](bonding.md).
|
||||
```
|
||||
|
||||
During our ongoing testing events [Fast and Furious](https://nymtech.net/events/fast-and-furious) we found out, that after introducing IP Packet Router (IPR) and [Nym exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt) on embedded Network Requester (NR) by default, only a fragment of Gateways routes correctly through IPv4 and IPv6. We built a useful monitor to check out your Gateway (`nym-node --mode exit-gateway`) at [harbourmaster.nymtech.net](https://harbourmaster.nymtech.net/).
|
||||
|
||||
IPv6 routing is not only a case for gateways. Imagine a rare occassion when you run a `mixnode` without IPv6 enabled and a client will sent IPv6 packets through the Mixnet through such route:
|
||||
```ascii
|
||||
[client] -> [entry-gateway] -> [mixnode layer 1] -> [your mixnode] -> [IPv6 mixnode layer3] -> [exit-gateway]
|
||||
```
|
||||
In this (unusual) case your `mixnode` will not be able to route the packets. The node will drop the packets and its performance would go down. For that reason it's befetial to have IPv6 enabled when running a `mixnode` functionality.
|
||||
|
||||
### Quick IPv6 Check
|
||||
|
||||
```admonish caution
|
||||
Make sure to keep your IPv4 address enabled while setting up IPv6, as the majority of routing goes through that one!
|
||||
```
|
||||
|
||||
You can always check IPv6 address and connectivity by using some of these methods:
|
||||
|
||||
```sh
|
||||
# locally listed IPv6 addresses
|
||||
ip -6 addr
|
||||
|
||||
# globally reachable IPv6 addresses
|
||||
ip -6 addr show scope global
|
||||
|
||||
# with DNS
|
||||
dig -6 TXT +short o-o.myaddr.l.google.com @ns1.google.com
|
||||
dig -t aaaa +short myip.opendns.com @resolver1.opendns.com
|
||||
|
||||
# https check
|
||||
curl -6 https://ifconfig.co
|
||||
curl -6 https://ipv6.icanhazip.com
|
||||
|
||||
# using telnet
|
||||
telnet -6 ipv6.telnetmyip.com
|
||||
```
|
||||
|
||||
### IPv6 Configuration
|
||||
|
||||
While we're working on Rust implementation to have these settings as a part of the binary build, we wrote a script to solve these connectivity requirements in the meantime we wrote a script [`network_tunnel_manager.sh`](https://gist.github.com/tommyv1987/ccf6ca00ffb3d7e13192edda61bb2a77) to support the operators to configure their servers and address all the connectivity requirements.
|
||||
|
||||
Networking configuration across different ISPs and various operation systems does not have a generic solution. If the provided configuration setup doesn't solve your problem check out [IPv6 troubleshooting](../troubleshooting/vps-isp.md#ipv6-troubleshooting) page. Be aware that you may have to do more research and customised adjustments.
|
||||
|
||||
#### Mode: `exit-gateway`
|
||||
|
||||
The `nymtun0` interface is dynamically managed by the `exit-gateway` service. When the service is stopped, `nymtun0` disappears, and when started, `nymtun0` is recreated.
|
||||
|
||||
The script should be used in a context where `nym-node --mode exit-gateway` is running to fully utilise its capabilities, particularly for fetching IPv6 addresses or applying network rules that depend on the `nymtun0` interface.
|
||||
@@ -203,7 +237,7 @@ alongside diagnostics for verifying system settings and network connectivity.
|
||||
```
|
||||
~~~
|
||||
|
||||
- To run the script next time, just enter `./network_tunnel_manager.`
|
||||
- To run the script next time, just enter `./network_tunnel_manager <ARG>`
|
||||
|
||||
2. Make sure your `nym-node --mode exit-gateway` service is up running
|
||||
|
||||
@@ -265,11 +299,11 @@ operation fetch_ipv6_address_nym_tun completed successfully.
|
||||
sudo ./network_tunnel_manager.sh apply_iptables_rules
|
||||
```
|
||||
|
||||
- The process may prompt you if you want to save current IPv4 rules, choose yes.
|
||||
- The process may prompt you if you want to save current IPv4 and IPv6 rules, choose yes.
|
||||
|
||||

|
||||
|
||||
- and check them again like in point 3
|
||||
- check IPv6 again like in point 3
|
||||
|
||||
6. At this point your node needs to be [bonded](bonding.md) to the API for `nymtun0` to interact with the network. After bonding please follow up with the remaining streps below to ensure that your Exit Gateway is routing properly.
|
||||
|
||||
@@ -298,6 +332,75 @@ sudo ./network_tunnel_manager.sh joke_through_the_mixnet
|
||||
|
||||
Make sure that you get the validation of IPv4 and IPv6 connectivity. If there are still any problems, please refer to [troubleshooting section](../troubleshooting/vps-isp.md#incorrect-gateway-network-check).
|
||||
|
||||
#### Mode: `mixnode`
|
||||
|
||||
```admonish caution title=""
|
||||
Most of the time the packets sent through the Mixnet are IPv4 based. The IPv6 packets are still pretty rare and therefore it's not mandatory from operational point of view. If you preparing to run a `nym-node` with all modes enabled once this option is implemented, then the IPv6 setup on your VPS is required.
|
||||
```
|
||||
|
||||
1. Download `network_tunnel_manager.sh`, make executable and run:
|
||||
|
||||
```sh
|
||||
curl -o network_tunnel_manager.sh -L https://gist.githubusercontent.com/tommyv1987/ccf6ca00ffb3d7e13192edda61bb2a77/raw/9d785d6ee3aa2970553633eccbd89a827f49fab5/network_tunnel_manager.sh && chmod +x network_tunnel_manager.sh && ./network_tunnel_manager.sh
|
||||
```
|
||||
|
||||
Here is a quick command explanation, for more details on the `network_tunnel_manager.sh` script, refer to the [overview](https://gist.github.com/tommyv1987/ccf6ca00ffb3d7e13192edda61bb2a77) under the code block. Mind that for `mixnode` VPS setup we will use only a few of the commands.
|
||||
|
||||
~~~admonish example collapsible=true title="A summarized usage of `network_tunnel_manager.sh`"
|
||||
```sh
|
||||
summary:
|
||||
This is a comprehensive script for configuring network packet forwarding and iptables rules,
|
||||
aimed at ensuring smooth operation of a tunnel interface.
|
||||
It includes functionality for both setup and tear-down of nymtun network configurations,
|
||||
alongside diagnostics for verifying system settings and network connectivity.
|
||||
|
||||
* fetch_ipv6_address_nym_tun - Fetches the IPv6 address assigned to the 'nymtun0'.
|
||||
* fetch_and_display_ipv6 - Displays the IPv6 address on the default network device.
|
||||
* apply_iptables_rules - Applies necessary IPv4 and IPv6 iptables rules.
|
||||
* remove_iptables_rules - Removes applied IPv4 and IPv6 iptables rules.
|
||||
* check_ipv6_ipv4_forwarding - Checks if IPv4 and IPv6 forwarding are enabled.
|
||||
* check_nymtun_iptables - Check nymtun0 device
|
||||
* perform_ipv4_ipv6_pings - Perform ipv4 and ipv6 pings to google
|
||||
* check_ip6_ipv4_routing - Check ipv6 and ipv4 routing
|
||||
* joke_through_the_mixnet - Run a joke through the mixnet via ipv4 and ipv6
|
||||
|
||||
```
|
||||
~~~
|
||||
|
||||
- To run the script next time, just enter `./network_tunnel_manager <ARG>`
|
||||
|
||||
2. Display IPv6:
|
||||
```sh
|
||||
sudo ./network_tunnel_manager.sh fetch_and_display_ipv6
|
||||
```
|
||||
- if you have a `global ipv6` address this is good
|
||||
|
||||
~~~admonish example collapsible=true title="Correct `./network_tunnel_manager.sh fetch_and_display_ipv6` output:"
|
||||
```sh
|
||||
iptables-persistent is already installed.
|
||||
Using IPv6 address: 2001:db8:a160::1/112 #the address will be different for you
|
||||
operation fetch_ipv6_address_nym_tun completed successfully.
|
||||
```
|
||||
~~~
|
||||
|
||||
3. Apply the rules:
|
||||
```sh
|
||||
sudo ./network_tunnel_manager.sh apply_iptables_rules
|
||||
```
|
||||
|
||||
- The process may prompt you if you want to save current IPv4 and IPv6 rules, choose yes.
|
||||
|
||||

|
||||
|
||||
- check IPv6 again like in point 2
|
||||
|
||||
4. Check connectivity
|
||||
```sh
|
||||
telnet -6 ipv6.telnetmyip.com
|
||||
```
|
||||
|
||||
Make sure that you get the validation of IPv4 and IPv6 connectivity. If there are still any problems, please refer to [troubleshooting section](../troubleshooting/vps-isp.md#incorrect-gateway-network-check).
|
||||
|
||||
## Next Steps
|
||||
|
||||
There are a few more good suggestions for `nym-node` VPS configuration, especially to be considered for `exit-gateway` functionality, like Web Secure Socket or Reversed Proxy setup. Visit [Proxy configuration](proxy-configuration.md) page to see the guides.
|
||||
|
||||
@@ -205,33 +205,79 @@ This lets your operating system know it's ok to reload the service configuration
|
||||
|
||||
## Moving a node
|
||||
|
||||
In case of a need to move a node from one machine to another and avoiding to lose the delegation, here are few steps how to do it.
|
||||
|
||||
The following examples transfers a Mix Node (in case of other nodes, change the `mixnodes` in the command for the `<NODE>` of your desire.
|
||||
|
||||
* Pause your node process.
|
||||
In case of a need to move a Nym Node from one machine to another and avoiding to lose the delegation, here are few steps how to do it.
|
||||
|
||||
Assuming both machines are remote VPS.
|
||||
|
||||
* Make sure your `~/.ssh/<YOUR_KEY>.pub` is in both of the machines `~/.ssh/authorized_keys` file
|
||||
* Make sure your `~/.ssh/<YOUR_KEY>.pub` is in both of the servers `~/.ssh/authorized_keys` file
|
||||
* Create a `nym-nodes` folder in the target VPS. SSH in from your terminal and run:
|
||||
|
||||
```sh
|
||||
# in case none of the nym configs was created previously
|
||||
mkdir ~/.nym
|
||||
|
||||
#in case no nym Mix Node was initialized previously
|
||||
#in case no nym Nym Node was initialized previously
|
||||
mkdir ~/.nym/nym-nodes
|
||||
```
|
||||
* Move the node data (keys) and config file to the new machine by opening your **local terminal** (as that one's ssh key is authorized in both of the machines) and running:
|
||||
* Move the node data (keys) and config file to the new machine by opening your **local terminal** (as that one's ssh key is authorized in both of the VPS) and running:
|
||||
```sh
|
||||
scp -r -3 <SOURCE_USER_NAME>@<SOURCE_HOST_ADDRESS>:~/.nym/nym-nodes <TARGET_USER_NAME>@<TARGET_HOST_ADDRESS>:~/.nym/nym-nodes/
|
||||
```
|
||||
* Re-initialise (`run` command) the node to generate a config with the new listening address.
|
||||
|
||||
**On new/target machine**
|
||||
|
||||
* Edit `~/.nym/nym-nodes/<ID>/config/config.toml` config with the new listening address IP.
|
||||
* Setup the [systemd](#systemd) automation, reload the daemon and run the service, or just simply run the node if you don't use automation
|
||||
* Change the node smart contract info via the wallet interface. Otherwise the keys will point to the old IP address in the smart contract, and the node will not be able to be connected, and it will fail up-time checks.
|
||||
* Re-run the node from the new location.
|
||||
|
||||
## Rename node local ID
|
||||
|
||||
Local node ID (not the identity key) is a name chosen by operators which defines where the nodes configuration data will be stored, where the ID determines the path to `~/.nym/nym-nodes/<ID>/`. This ID is never shared on the network.
|
||||
|
||||
Since migrating to [`nym-node`](nym-node.md), specifying an with `--ID <ID>` when starting a new node is no longer necessary. Nodes without a specified ID will be asigned the default ID `default-nym-node`. This streamlines node management, particularly for operators handling multiple nodes via ansible and other automation scripts, as all data is stored at `~/.nym/nym-nodes/default-nym-node`.
|
||||
|
||||
If you already operate a `nym-node` and wish to change the local ID to `default-nym-node` or anything else, follow the steps below to do so.
|
||||
|
||||
```admonish note
|
||||
In the example we use `default-nym-node` as a target `<ID>`, if you prefer to use another name, edit the syntax in the commands accordingly.
|
||||
```
|
||||
|
||||
1. Copy the configuration directory to the new one
|
||||
```sh
|
||||
cp -r ~/.nym/nym-nodes/<SOURCE_ID> ~/.nym/nym-nodes/default-nym-node/
|
||||
```
|
||||
|
||||
2. Rename all `<SOURCE_ID>` occurences in `config.toml` to `default-nym-node`
|
||||
|
||||
```sh
|
||||
# check occurences of the <SOURCE_ID>
|
||||
grep -r "<SOURCE_ID>" ~/.nym/nym-nodes/default-nym-node/*
|
||||
```
|
||||
```admonish bug title="Caution!"
|
||||
If your node `<SOURCE_ID>` is too generic (like `gateway` etc) and it occurs elsewhere than just a custom value, **do not use `sed` command but rewrite the values manually using a text editor!**
|
||||
```
|
||||
|
||||
```sh
|
||||
# rename it by using sed command
|
||||
sed -i -e "s/<SOURCE_ID>/default-nym-node/g" ~/.nym/nym-nodes/default-nym-node/config/config.toml
|
||||
|
||||
# or manually by opening config.toml and rewriting each occurence of <SOURCE_ID>
|
||||
nano ~/.nym/nym-nodes/default-nym-node/config/config.toml
|
||||
```
|
||||
|
||||
3. Validate by rechecking the config file content
|
||||
```sh
|
||||
# either re-run
|
||||
grep -r "<SOURCE_ID>" ~/.nym/nym-nodes/default-nym-node/*
|
||||
|
||||
# or by reading the config file
|
||||
less ~/.nym/nym-nodes/default-nym-node/config/config.toml
|
||||
```
|
||||
- Pay extra attention to the `hostname` line. In case its value was somehow correlated with the `<SOURCE_ID>` string you may need to correct it back
|
||||
|
||||
4. Reload your [systemd service daemon](#systemd) and restart the service, or if automation isn't your thing, simply reboot the node
|
||||
|
||||
5. If you double-checked that everything works fine, you can consider removing your old config directory
|
||||
|
||||
## Ports
|
||||
All `<NODE>`-specific port configuration can be found in `$HOME/.nym/<NODE>/<YOUR_ID>/config/config.toml`. If you do edit any port configs, remember to restart your client and node processes.
|
||||
|
||||
@@ -13,7 +13,7 @@ This documentation page provides a guide on how to set up and run a [NYM NODE](n
|
||||
```
|
||||
|
||||
```admonish note
|
||||
If you are a `nym-mixnode` or `nym-gateway` operator and you are not familiar wwith the binary changes called *Project Smoosh*, you can read the archived [Smoosh FAQ](../archive/smoosh-faq.md) page.
|
||||
If you are a `nym-mixnode` or `nym-gateway` operator and you are not familiar with the binary changes called *Project Smoosh*, you can read the archived [Smoosh FAQ](../archive/faq/smoosh-faq.md) page.
|
||||
```
|
||||
|
||||
## Summary
|
||||
@@ -278,9 +278,11 @@ Run the node with custom `--id` without initialising:
|
||||
### Migrate
|
||||
|
||||
```admonish caution
|
||||
Migration is a must for all deprecated nodes (`nym-mixnode`, `nym-gateway`). For backward compatibility we created an [archive section](../archive/setup-guides.md) with all the guides for individual binaries. However, the binaries from version 1.1.35 (`nym-gateway`) and 1.1.37 (`nym-mixnode`) onwards will no longer have `init` command.
|
||||
Migration is a must for all deprecated nodes (`nym-mixnode`, `nym-gateway`). For backward compatibility we created an [archive section](../archive/nodes/setup-guides.md) with all the guides for individual binaries. However, the binaries from version 1.1.35 (`nym-gateway`) and 1.1.37 (`nym-mixnode`) onwards will no longer have `init` command.
|
||||
```
|
||||
|
||||
Operators who are about to migrate their nodes need to configure their [VPS](vps-setup.md) and setup `nym-node` which can be downloaded as a [pre-built binary](../binaries/pre-built-binaries.md) or compiled from [source](../binaries/building-nym.md).
|
||||
|
||||
To migrate a `nym-mixnode` or a `nym-gateway` to `nym-node` is fairly simple, use the `migrate` command with `--config-file` flag pointing to the original `config.toml` file, with a conditional argument defining which type of node this configuration belongs to. Examples are below.
|
||||
|
||||
Make sure to use `--deny-init` flag to prevent initialisation of a new node.
|
||||
|
||||
@@ -58,6 +58,22 @@ To install a full node from scratch, refer to the [validator setup guide](valida
|
||||
|
||||
Before node or validator setup, the VPS needs to be configured and tested, to verify your connectivity and make sure that your provider wasn't dishonest with the offered services.
|
||||
|
||||
### Install Dependencies
|
||||
|
||||
SSH to your server as `root` or become one running `sudo -i` or `su`. If you prefer to administrate your VPS from a user environment, supply the commands with prefix `sudo`.
|
||||
|
||||
Start with setting up the essential tools on your server.
|
||||
```sh
|
||||
# get your system up to date
|
||||
apt update -y && apt --fix-broken install
|
||||
|
||||
# install dependencies
|
||||
apt -y install ca-certificates jq curl wget ufw jq tmux pkg-config build-essential libssl-dev git
|
||||
|
||||
# double check ufw is installed correctly
|
||||
apt install ufw --fix-missing
|
||||
```
|
||||
|
||||
### Configure your Firewall
|
||||
|
||||
For a `nym-node` or Nyx validator to recieve traffic, you need to open ports on the server. The following commands will allow you to set up a firewall using `ufw`.
|
||||
@@ -68,33 +84,33 @@ For a `nym-node` or Nyx validator to recieve traffic, you need to open ports on
|
||||
ufw version
|
||||
|
||||
# if it is not installed, install with
|
||||
sudo apt install ufw -y
|
||||
apt install ufw -y
|
||||
|
||||
# enable ufw
|
||||
sudo ufw enable
|
||||
ufw enable
|
||||
|
||||
# check the status of the firewall
|
||||
sudo ufw status
|
||||
ufw status
|
||||
```
|
||||
|
||||
2. Open all needed ports to have your firewall working correctly:
|
||||
```sh
|
||||
# for nym-node
|
||||
sudo ufw allow 1789,1790,8000,9000,9001,22/tcp
|
||||
ufw allow 1789,1790,8000,9000,9001,22/tcp
|
||||
|
||||
# in case of planning to setup a WSS (for Gateway functionality)
|
||||
sudo ufw allow 9001/tcp
|
||||
ufw allow 9001/tcp
|
||||
|
||||
# in case of reverse proxy for the swagger page (for Gateway optionality)
|
||||
sudo ufw allow 8080,80,443
|
||||
ufw allow 8080,80,443
|
||||
|
||||
# for validator
|
||||
sudo ufw allow 1317,26656,26660,22,80,443/tcp
|
||||
ufw allow 1317,26656,26660,22,80,443/tcp
|
||||
```
|
||||
|
||||
3. Check the status of the firewall:
|
||||
```sh
|
||||
sudo ufw status
|
||||
ufw status
|
||||
```
|
||||
|
||||
For more information about your node's port configuration, check the [port reference table](#ports-reference-table) below.
|
||||
|
||||
@@ -191,14 +191,38 @@ If you are still unable to see your node on the dashboard, or your node is decla
|
||||
- The firewall on your host machine is not configured properly. Checkout the [instructions](../nodes/vps-setup.md#configure-your-firewall).
|
||||
- You provided incorrect information when bonding your node.
|
||||
- You are running your node from a VPS without IPv6 support.
|
||||
<!-- You did not use the `--announce-host` flag while running the Mix Node from your local machine behind NAT. -->
|
||||
- You did not configure your router firewall while running the node from your local machine behind NAT, or you are lacking IPv6 support
|
||||
- Your Mix Node is not running at all, it has either exited / panicked or you closed the session without making the node persistent. Check out the [instructions](../nodes/configuration.md#automating-your-node-with-tmux-and-systemd).
|
||||
|
||||
```admonish caution
|
||||
```admonish caution title=""
|
||||
Your Nym Node **must speak both IPv4 and IPv6** in order to cooperate with other nodes and route traffic. This is a common reason behind many errors we are seeing among node operators, so check with your provider that your VPS is able to do this!
|
||||
```
|
||||
|
||||
#### Check IPv6 Connectivity
|
||||
|
||||
You can always check IPv6 address and connectivity by using some of these methods:
|
||||
|
||||
```sh
|
||||
# locally listed IPv6 addresses
|
||||
ip -6 addr
|
||||
|
||||
# globally reachable IPv6 addresses
|
||||
ip -6 addr show scope global
|
||||
|
||||
# with DNS
|
||||
dig -6 TXT +short o-o.myaddr.l.google.com @ns1.google.com
|
||||
dig -t aaaa +short myip.opendns.com @resolver1.opendns.com
|
||||
|
||||
# https check
|
||||
curl -6 https://ifconfig.co
|
||||
curl -6 https://ipv6.icanhazip.com
|
||||
|
||||
# using telnet
|
||||
telnet -6 ipv6.telnetmyip.com
|
||||
```
|
||||
If your connection doesn't work make sure to follow [VPS IPv6 setup](../nodes/configuration.md#connectivity-test-and-configuration). If there is more troubleshooting needed, check out [VPS IPv6 troubleshooting](vps-isp.md#ipv6-troubleshooting) page.
|
||||
|
||||
|
||||
#### Incorrect bonding information
|
||||
|
||||
Check that you have provided the correct information when bonding your Nym Node in the web wallet interface. When in doubt, un-bond and then re-bond your node!
|
||||
@@ -211,33 +235,6 @@ On certain cloud providers such as AWS and Google Cloud, you need to do some add
|
||||
|
||||
If the difference between the two is unclear, contact the help desk of your VPS provider.
|
||||
|
||||
#### No IPv6 connectivity
|
||||
|
||||
Make sure that your VPS has IPv6 connectivity available with whatever provider you are using.
|
||||
|
||||
To get all ip addresses of your host, try following commands:
|
||||
|
||||
```
|
||||
hostname -i
|
||||
```
|
||||
|
||||
Will return your **local ip** address.
|
||||
|
||||
```
|
||||
hostname -I
|
||||
```
|
||||
|
||||
Will return all of the ip addresses of your host. This output should look something like this:
|
||||
|
||||
```
|
||||
bob@nym:~$ hostname -I
|
||||
88.36.11.23 172.18.0.1 2a01:28:ca:102::1:641
|
||||
```
|
||||
|
||||
- The first **ipv4** is the public ip you need to use for the `--announce-host` flag.
|
||||
- The second **ipv4** is the local ip you need to use for the `--host` flag.
|
||||
- The 3rd output should confirm if your machine has ipv6 available.
|
||||
|
||||
### Running on a local machine behind NAT with no fixed IP address
|
||||
|
||||
Your ISP has to be IPv6 ready if you want to run a Nym Node on your local machine. Sadly, in 2020, most of them are not and you won't get an IPv6 address by default from your ISP. Usually it is an extra paid service or they simply don't offer it.
|
||||
@@ -274,7 +271,7 @@ thread 'tokio-runtime-worker' panicked at 'Failed to create TCP listener: Os { c
|
||||
```
|
||||
|
||||
Then you need to `--announce-host <PUBLIC_IP>` and `--host <LOCAL_IP>` on startup. This issue is addressed [above](#missing-`announce-host`-flag)
|
||||
|
||||
<!-- NEEDS TO BE REWORKED AND ADD WARNING TO NOT CHANGE OTHER PORTS FOR API
|
||||
### Can I use a port other than 1789?
|
||||
|
||||
Yes! Here is what you will need to do:
|
||||
@@ -301,7 +298,7 @@ nano ~/.nym/nym-nodes/alice-node/config/config.toml
|
||||
You will need to edit two parts of the file. `announce_address` and `listening_address` in the `config.toml` file. Simply replace `:1789` (the default port) with `:1337` (your new port) after your IP address.
|
||||
|
||||
Finally, restart your node. You should see if the Mix Node is using the port you have changed in the config.toml file right after you run the node.
|
||||
|
||||
-->
|
||||
### What is `verloc` and do I have to configure my Nym Node to implement it?
|
||||
|
||||
`verloc` is short for _verifiable location_. Mix Nodes and Gateways now measure speed-of-light distances to each other, in an attempt to verify how far apart they are. In later releases, this will allow us to algorithmically verify node locations in a non-fake-able and trustworthy manner.
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
# Troubleshooting VPS Setup
|
||||
|
||||
## Incorrect Gateway Network Check
|
||||
```admonish info
|
||||
To monitor the connectivity of your Exit Gateway, use results of probe testing displayed in [harbourmaster.nymtech.net](https://harbourmaster.nymtech.net).
|
||||
```
|
||||
|
||||
If you followed all the steps listed in [Connectivity Test and Configuration](../nodes/vps-setup.md#connectivity-test-and-configuration) chapter of VPS Setup and you still have a problem with a correct connectivity for page in
|
||||
## IPv6 troubleshooting
|
||||
|
||||
### Incorrect Gateway Network Check
|
||||
|
||||
Nym operators community is working on a Nym version of tors [good bad ISP table](https://community.torproject.org/relay/community-resources/good-bad-isps/). There is no one solution fits all when it comes to connectivity setup. The operation of `nym-node` will vary depending on your ISP and chosen system/distribution. While few machines will work out of the box, most will work after uisng our connectivity configuration guide, some need more adjustments.
|
||||
|
||||
Begin with the steps listed in [*Connectivity Test and Configuration*](../nodes/vps-setup.md#connectivity-test-and-configuration) chapter of VPS Setup page. If you still have a problem with the IPv6 connectivity try:
|
||||
|
||||
1. Tor community created a helpful [table of ISPs](https://community.torproject.org/relay/community-resources/good-bad-isps/). Make sure your one is listed there as a *"good ISP"*. If not, consider migrating!
|
||||
2. Checkout your VPS dashboard and make sure your IPv6-public enabled.
|
||||
@@ -10,8 +18,15 @@ If you followed all the steps listed in [Connectivity Test and Configuration](..
|
||||
|
||||

|
||||
|
||||
4. Search or ask your ISP for additional documentation related to IPv6 routing and ask them to provide you with `IPv6 IP address` and `IPv6 IP gateway address`
|
||||
- For example Digital Ocean setup isn't the most straight forward, but it's [well documented](https://docs.digitalocean.com/products/networking/ipv6/how-to/enable/#on-existing-droplets) and it works.
|
||||
|
||||
## Virtual IPs and hosting via Google & AWS
|
||||
5. Search for guides regarding your particular system and distribution. For Debian based distributions using systemd, some generic guides such as [this one](https://cloudzy.com/blog/configure-ipv6-on-ubuntu/) work as well.
|
||||
|
||||
|
||||
## Other VPS troubleshooting
|
||||
|
||||
### Virtual IPs and hosting via Google & AWS
|
||||
|
||||
For true internet decentralization we encourage operators to use diverse VPS providers instead of the largest companies offering such services. If for some reasons you have already running AWS or Google and want to setup a `<NODE>` there, please read the following.
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ license.workspace = true
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.82"
|
||||
chrono = { version = "0.4.31", features = ["serde"] }
|
||||
clap = { workspace = true, features = ["cargo", "derive"] }
|
||||
dotenvy = { workspace = true }
|
||||
|
||||
@@ -9,12 +9,9 @@ use crate::mix_node::models::{
|
||||
EconomicDynamicsStats, NodeDescription, NodeStats, SummedDelegations,
|
||||
};
|
||||
use crate::state::ExplorerApiStateContext;
|
||||
use crate::mix_node::models::{NewModelDescription, OldModelDescription};
|
||||
use anyhow::{Context, Result};
|
||||
|
||||
use nym_explorer_api_requests::PrettyDetailedMixNodeBond;
|
||||
|
||||
use nym_mixnet_contract_common::{Delegation, MixId};
|
||||
use reqwest::Error as ReqwestError;
|
||||
use rocket::response::status::NotFound;
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::{Route, State};
|
||||
@@ -33,67 +30,18 @@ pub fn mix_node_make_default_routes(settings: &OpenApiSettings) -> (Vec<Route>,
|
||||
]
|
||||
}
|
||||
|
||||
async fn get_mix_node_description(host: &str, port: u16) -> Result<NodeDescription> {
|
||||
let first_url = format!("http://{host}:{port}/description");
|
||||
let first_response = reqwest::get(&first_url).await.context(format!(
|
||||
"Failed to fetch description from nym-mixnode /description url: {}",
|
||||
first_url
|
||||
))?;
|
||||
|
||||
match first_response
|
||||
.error_for_status()
|
||||
.context("Nym-mixnodes /description url returned error status")?
|
||||
.json::<OldModelDescription>()
|
||||
async fn get_mix_node_description(host: &str, port: u16) -> Result<NodeDescription, ReqwestError> {
|
||||
reqwest::get(format!("http://{host}:{port}/description"))
|
||||
.await?
|
||||
.json::<NodeDescription>()
|
||||
.await
|
||||
{
|
||||
Ok(description) => return Ok(description.into()),
|
||||
Err(e) => log::warn!("Failed to parse old model description: {}", e),
|
||||
}
|
||||
|
||||
let second_url = format!("http://{host}:{port}/api/v1/description");
|
||||
let second_response = reqwest::get(&second_url).await.context(format!(
|
||||
"Failed to fetch description from nym-node /api/v1/description url: {}",
|
||||
second_url
|
||||
))?;
|
||||
|
||||
let description = second_response
|
||||
.error_for_status()
|
||||
.context("Nym-node /api/v1/description url returned error status")?
|
||||
.json::<NewModelDescription>()
|
||||
.await
|
||||
.context("Failed to parse JSON from nym-node /api/v1/description url")?;
|
||||
|
||||
Ok(description.into())
|
||||
}
|
||||
|
||||
pub async fn get_mix_node_stats(host: &str, port: u16) -> Result<NodeStats> {
|
||||
let primary_url = format!("http://{host}:{port}/stats");
|
||||
let secondary_url = format!("http://{host}:{port}/api/v1/metrics/mixing");
|
||||
|
||||
let primary_response = reqwest::get(&primary_url)
|
||||
.await
|
||||
.context("Failed to fetch from primary nym-mixnode /stats url")?;
|
||||
let primary_stats = primary_response
|
||||
.error_for_status()
|
||||
.context("Nym-mixnode url returned error status")?
|
||||
async fn get_mix_node_stats(host: &str, port: u16) -> Result<NodeStats, ReqwestError> {
|
||||
reqwest::get(format!("http://{host}:{port}/stats"))
|
||||
.await?
|
||||
.json::<NodeStats>()
|
||||
.await
|
||||
.context("Failed to parse JSON from primary nym-mixnode /stats url");
|
||||
|
||||
if let Ok(stats) = primary_stats {
|
||||
return Ok(stats);
|
||||
}
|
||||
|
||||
let secondary_response = reqwest::get(&secondary_url)
|
||||
.await
|
||||
.context("Failed to fetch from nym-node /api/v1/metrics/mixing url")?;
|
||||
let stats = secondary_response
|
||||
.error_for_status()
|
||||
.context("Nym-node /api/v1/metrics/mixing returned error status")?
|
||||
.json::<NodeStats>()
|
||||
.await
|
||||
.context("Failed to parse JSON from nym-node /api/v1/metrics/mixing")?;
|
||||
Ok(stats)
|
||||
}
|
||||
|
||||
#[openapi(tag = "mix_nodes")]
|
||||
|
||||
@@ -92,86 +92,31 @@ impl ThreadsafeMixNodeCache {
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
pub(crate) struct NodeDescription {
|
||||
pub(crate) name: Option<String>,
|
||||
pub(crate) description: Option<String>,
|
||||
pub(crate) link: Option<String>,
|
||||
pub(crate) location: Option<String>,
|
||||
pub(crate) moniker: Option<String>,
|
||||
pub(crate) website: Option<String>,
|
||||
pub(crate) security_contact: Option<String>,
|
||||
pub(crate) details: Option<String>,
|
||||
pub(crate) name: String,
|
||||
pub(crate) description: String,
|
||||
pub(crate) link: String,
|
||||
pub(crate) location: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct OldModelDescription {
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub link: String,
|
||||
pub location: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct NewModelDescription {
|
||||
pub moniker: String,
|
||||
pub website: String,
|
||||
pub security_contact: String,
|
||||
pub details: String,
|
||||
}
|
||||
|
||||
impl From<OldModelDescription> for NodeDescription {
|
||||
fn from(old: OldModelDescription) -> Self {
|
||||
NodeDescription {
|
||||
name: Some(old.name),
|
||||
description: Some(old.description),
|
||||
link: Some(old.link),
|
||||
location: Some(old.location),
|
||||
moniker: None,
|
||||
website: None,
|
||||
security_contact: None,
|
||||
details: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NewModelDescription> for NodeDescription {
|
||||
fn from(new: NewModelDescription) -> Self {
|
||||
NodeDescription {
|
||||
name: None,
|
||||
description: Some(new.details),
|
||||
link: Some(new.website),
|
||||
location: None,
|
||||
moniker: Some(new.moniker),
|
||||
website: None,
|
||||
security_contact: Some(new.security_contact),
|
||||
details: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone, Deserialize, JsonSchema, Debug)]
|
||||
#[derive(Serialize, Clone, Deserialize, JsonSchema)]
|
||||
pub(crate) struct NodeStats {
|
||||
#[serde(
|
||||
serialize_with = "humantime_serde::serialize",
|
||||
deserialize_with = "humantime_serde::deserialize"
|
||||
)]
|
||||
update_time: SystemTime,
|
||||
|
||||
#[serde(
|
||||
serialize_with = "humantime_serde::serialize",
|
||||
deserialize_with = "humantime_serde::deserialize"
|
||||
)]
|
||||
previous_update_time: SystemTime,
|
||||
|
||||
#[serde(alias = "received_since_startup")]
|
||||
packets_received_since_startup: u64,
|
||||
#[serde(alias = "sent_since_startup")]
|
||||
packets_sent_since_startup: u64,
|
||||
#[serde(alias = "dropped_since_startup")]
|
||||
packets_explicitly_dropped_since_startup: u64,
|
||||
#[serde(alias = "received_since_last_update")]
|
||||
packets_received_since_last_update: u64,
|
||||
#[serde(alias = "sent_since_last_update")]
|
||||
packets_sent_since_last_update: u64,
|
||||
#[serde(alias = "dropped_since_last_update")]
|
||||
packets_explicitly_dropped_since_last_update: u64,
|
||||
}
|
||||
|
||||
|
||||
@@ -11,15 +11,12 @@ interface MixNodeDetailProps {
|
||||
mixnodeDescription: MixNodeDescriptionResponse;
|
||||
}
|
||||
|
||||
export const MixNodeDetailSection: React.FC<MixNodeDetailProps> = ({ mixNodeRow, mixnodeDescription }) => {
|
||||
export const MixNodeDetailSection: FCWithChildren<MixNodeDetailProps> = ({ mixNodeRow, mixnodeDescription }) => {
|
||||
const theme = useTheme();
|
||||
const palette = [theme.palette.text.primary];
|
||||
const isMobile = useIsMobile();
|
||||
const statusText = React.useMemo(() => getMixNodeStatusText(mixNodeRow.status), [mixNodeRow.status]);
|
||||
|
||||
const title = mixnodeDescription.name || mixnodeDescription.moniker || "Unknown Node";
|
||||
const description = mixnodeDescription.description || mixnodeDescription.details || "No description available.";
|
||||
const link = mixnodeDescription.link || mixnodeDescription.website || '#';
|
||||
|
||||
return (
|
||||
<Grid container>
|
||||
<Grid item xs={12} md={6}>
|
||||
@@ -38,11 +35,11 @@ export const MixNodeDetailSection: React.FC<MixNodeDetailProps> = ({ mixNodeRow,
|
||||
placeItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Identicon size={43} string={mixNodeRow.identity_key} />
|
||||
<Identicon size={43} string={mixNodeRow.identity_key} palette={palette} />
|
||||
</Box>
|
||||
<Box ml={isMobile ? 0 : 2} mt={isMobile ? 2 : 0}>
|
||||
<Typography fontSize={21}>{title}</Typography>
|
||||
<Typography>{description.slice(0, 1000)}</Typography>
|
||||
<Typography fontSize={21}>{mixnodeDescription.name}</Typography>
|
||||
<Typography>{(mixnodeDescription.description || '').slice(0, 1000)}</Typography>
|
||||
<Button
|
||||
component="a"
|
||||
variant="text"
|
||||
@@ -52,7 +49,7 @@ export const MixNodeDetailSection: React.FC<MixNodeDetailProps> = ({ mixNodeRow,
|
||||
fontWeight: 600,
|
||||
padding: 0,
|
||||
}}
|
||||
href={link}
|
||||
href={mixnodeDescription.link}
|
||||
target="_blank"
|
||||
>
|
||||
<Typography
|
||||
@@ -62,7 +59,7 @@ export const MixNodeDetailSection: React.FC<MixNodeDetailProps> = ({ mixNodeRow,
|
||||
overflow="hidden"
|
||||
maxWidth="250px"
|
||||
>
|
||||
Visit Node
|
||||
{mixnodeDescription.link}
|
||||
</Typography>
|
||||
</Button>
|
||||
</Box>
|
||||
@@ -95,4 +92,4 @@ export const MixNodeDetailSection: React.FC<MixNodeDetailProps> = ({ mixNodeRow,
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -169,14 +169,10 @@ export interface GatewayReportResponse {
|
||||
export type GatewayHistoryResponse = StatsResponse;
|
||||
|
||||
export interface MixNodeDescriptionResponse {
|
||||
name?: string;
|
||||
description?: string;
|
||||
link?: string;
|
||||
location?: string;
|
||||
moniker?: string;
|
||||
website?: string;
|
||||
security_contact?: string;
|
||||
details?: string;
|
||||
name: string;
|
||||
description: string;
|
||||
link: string;
|
||||
location: string;
|
||||
}
|
||||
|
||||
export type MixNodeStatsResponse = StatsResponse;
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
[package]
|
||||
name = "nym-gateway"
|
||||
license = "GPL-3.0"
|
||||
version = "1.1.35"
|
||||
version = "1.1.36"
|
||||
authors = [
|
||||
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
||||
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
||||
|
||||
+10
-36
@@ -6,7 +6,6 @@ use crate::node::helpers::load_keypair;
|
||||
use crate::GatewayError;
|
||||
use nym_config::OptionalSet;
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
use nym_ip_packet_router::config::BaseClientConfig;
|
||||
use nym_pemstore::traits::PemStorableKey;
|
||||
use nym_pemstore::KeyPairPath;
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
@@ -43,48 +42,23 @@ pub struct OverrideIpPacketRouterConfig {
|
||||
// TODO
|
||||
}
|
||||
|
||||
// NOTE: make sure this is in sync with service-providers/network-requester/src/cli/mod.rs::override_config
|
||||
pub fn override_network_requester_config(
|
||||
mut cfg: nym_network_requester::Config,
|
||||
opts: Option<OverrideNetworkRequesterConfig>,
|
||||
) -> nym_network_requester::Config {
|
||||
let Some(opts) = opts else { return cfg };
|
||||
|
||||
// as of 12.09.23 the below is true (not sure how this comment will rot in the future)
|
||||
// medium_toggle:
|
||||
// - sets secondary packet size to 16kb
|
||||
// - disables poisson distribution of the main traffic stream
|
||||
// - sets the cover traffic stream to 1 packet / 5s (on average)
|
||||
// - disables per hop delay
|
||||
//
|
||||
// fastmode (to be renamed to `fast-poisson`):
|
||||
// - sets average per hop delay to 10ms
|
||||
// - sets the cover traffic stream to 1 packet / 2000s (on average); for all intents and purposes it disables the stream
|
||||
// - sets the poisson distribution of the main traffic stream to 4ms, i.e. 250 packets / s on average
|
||||
//
|
||||
// no_cover:
|
||||
// - disables poisson distribution of the main traffic stream
|
||||
// - disables the secondary cover traffic stream
|
||||
// in the old code we had calls to `assert` thus panicking
|
||||
cfg.base
|
||||
.try_apply_traffic_modes(
|
||||
cfg.network_requester.disable_poisson_rate,
|
||||
opts.medium_toggle,
|
||||
opts.fastmode,
|
||||
opts.no_cover,
|
||||
)
|
||||
.expect("failed to apply traffic modes");
|
||||
|
||||
// disable poisson rate in the BASE client if the NR option is enabled
|
||||
if cfg.network_requester.disable_poisson_rate {
|
||||
cfg.set_no_poisson_process();
|
||||
}
|
||||
|
||||
// those should be enforced by `clap` when parsing the arguments
|
||||
if opts.medium_toggle {
|
||||
assert!(!opts.fastmode);
|
||||
assert!(!opts.no_cover);
|
||||
|
||||
cfg.set_medium_toggle();
|
||||
}
|
||||
|
||||
cfg.with_base(
|
||||
BaseClientConfig::with_high_default_traffic_volume,
|
||||
opts.fastmode,
|
||||
)
|
||||
.with_base(BaseClientConfig::with_disabled_cover_traffic, opts.no_cover)
|
||||
.with_optional(
|
||||
cfg.with_optional(
|
||||
nym_network_requester::Config::with_open_proxy,
|
||||
opts.open_proxy,
|
||||
)
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-node"
|
||||
version = "1.1.0"
|
||||
version = "1.1.1"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
|
||||
@@ -161,28 +161,24 @@ impl NymNodeRouter {
|
||||
// redirection for old legacy mixnode routes
|
||||
.route(
|
||||
"/hardware",
|
||||
get(|| async { Redirect::permanent(&routes::api::v1::system_info_absolute()) }),
|
||||
get(|| async { Redirect::to(&routes::api::v1::system_info_absolute()) }),
|
||||
)
|
||||
.route(
|
||||
"/description",
|
||||
get(|| async { Redirect::permanent(&routes::api::v1::description_absolute()) }),
|
||||
get(|| async { Redirect::to(&routes::api::v1::description_absolute()) }),
|
||||
)
|
||||
.route(
|
||||
"/stats",
|
||||
get(|| async {
|
||||
Redirect::permanent(&routes::api::v1::metrics::mixing_absolute())
|
||||
}),
|
||||
get(|| async { Redirect::to(&routes::api::v1::metrics::mixing_absolute()) }),
|
||||
)
|
||||
.route(
|
||||
"/verloc",
|
||||
get(|| async {
|
||||
Redirect::permanent(&routes::api::v1::metrics::verloc_absolute())
|
||||
}),
|
||||
get(|| async { Redirect::to(&routes::api::v1::metrics::verloc_absolute()) }),
|
||||
)
|
||||
.route(
|
||||
"/metrics",
|
||||
get(|| async {
|
||||
Redirect::permanent(&routes::api::v1::metrics::prometheus_absolute())
|
||||
Redirect::to(&routes::api::v1::metrics::prometheus_absolute())
|
||||
}),
|
||||
)
|
||||
.nest(routes::LANDING_PAGE, landing_page::routes(config.landing))
|
||||
|
||||
@@ -154,7 +154,7 @@ pub fn ephemeral_exit_gateway_config(
|
||||
config: Config,
|
||||
mnemonic: &bip39::Mnemonic,
|
||||
) -> Result<EphemeralConfig, ExitGatewayError> {
|
||||
let nr_opts = LocalNetworkRequesterOpts {
|
||||
let mut nr_opts = LocalNetworkRequesterOpts {
|
||||
config: nym_network_requester::Config {
|
||||
base: nym_client_core_config_types::Config {
|
||||
client: base_client_config(&config),
|
||||
@@ -186,7 +186,20 @@ pub fn ephemeral_exit_gateway_config(
|
||||
custom_mixnet_path: None,
|
||||
};
|
||||
|
||||
let ipr_opts = LocalIpPacketRouterOpts {
|
||||
// SAFETY: this function can only fail if fastmode or nocover is set alongside medium_toggle which is not the case here
|
||||
#[allow(clippy::unwrap_used)]
|
||||
nr_opts
|
||||
.config
|
||||
.base
|
||||
.try_apply_traffic_modes(
|
||||
nr_opts.config.network_requester.disable_poisson_rate,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut ipr_opts = LocalIpPacketRouterOpts {
|
||||
config: nym_ip_packet_router::Config {
|
||||
base: nym_client_core_config_types::Config {
|
||||
client: base_client_config(&config),
|
||||
@@ -216,6 +229,10 @@ pub fn ephemeral_exit_gateway_config(
|
||||
custom_mixnet_path: None,
|
||||
};
|
||||
|
||||
if ipr_opts.config.ip_packet_router.disable_poisson_rate {
|
||||
ipr_opts.config.base.set_no_poisson_process()
|
||||
}
|
||||
|
||||
let pub_id_path = config
|
||||
.storage_paths
|
||||
.keys
|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v1.2.13] (2024-05-08)
|
||||
|
||||
- Bug fix: wallet delegations list is empty when RPC node doesn't hold block ([#4565])
|
||||
- updating sign commands to include nym-node([#4578])
|
||||
|
||||
[#4578]: https://github.com/nymtech/nym/pull/4578
|
||||
[#4565]: https://github.com/nymtech/nym/pull/4565
|
||||
|
||||
## [v1.2.10] (2023-10-31)
|
||||
|
||||
- Add loading model on initial load of delegations ([#4039])
|
||||
|
||||
Generated
+3
-1
@@ -3005,6 +3005,7 @@ dependencies = [
|
||||
"schemars",
|
||||
"serde",
|
||||
"tendermint",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3442,7 +3443,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym_wallet"
|
||||
version = "1.2.12"
|
||||
version = "1.2.13"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.13.1",
|
||||
@@ -3476,6 +3477,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"strum 0.23.0",
|
||||
"tap",
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tauri-codegen",
|
||||
|
||||
@@ -37,6 +37,7 @@ serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
serde_repr = "0.1"
|
||||
strum = { version = "0.23", features = ["derive"] }
|
||||
tap = "1"
|
||||
tauri = { version = "=1.2.3", features = ["clipboard-all", "shell-open", "updater", "window-maximize", "window-print"] }
|
||||
#tendermint-rpc = "0.23.0"
|
||||
time = { version = "0.3.17", features = ["local-offset"] }
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
use crate::error::BackendError;
|
||||
use crate::state::WalletState;
|
||||
use crate::vesting::delegate::vesting_undelegate_from_mixnode;
|
||||
use nym_mixnet_contract_common::mixnode::StakeSaturationResponse;
|
||||
use nym_mixnet_contract_common::MixId;
|
||||
use nym_types::currency::DecCoin;
|
||||
use nym_types::delegation::{Delegation, DelegationWithEverything, DelegationsSummaryResponse};
|
||||
@@ -18,6 +19,7 @@ use nym_validator_client::nyxd::contract_traits::{
|
||||
MixnetQueryClient, MixnetSigningClient, NymContractsProvider, PagedMixnetQueryClient,
|
||||
};
|
||||
use nym_validator_client::nyxd::Fee;
|
||||
use tap::TapFallible;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_pending_delegation_events(
|
||||
@@ -165,10 +167,21 @@ pub async fn get_all_mix_delegations(
|
||||
.expect("vesting contract address is not available");
|
||||
|
||||
log::info!(" >>> Get delegations");
|
||||
let delegations = client.nyxd.get_all_delegator_delegations(&address).await?;
|
||||
let delegations = client
|
||||
.nyxd
|
||||
.get_all_delegator_delegations(&address)
|
||||
.await
|
||||
.tap_err(|err| {
|
||||
log::error!(" <<< Failed to get delegations. Error: {}", err);
|
||||
})?;
|
||||
log::info!(" <<< {} delegations", delegations.len());
|
||||
|
||||
let pending_events_for_account = get_pending_delegation_events(state.clone()).await?;
|
||||
let pending_events_for_account =
|
||||
get_pending_delegation_events(state.clone())
|
||||
.await
|
||||
.tap_err(|err| {
|
||||
log::error!(" <<< Failed to get pending delegations. Error: {}", err);
|
||||
})?;
|
||||
|
||||
log::info!(
|
||||
" <<< {} pending delegation events for account",
|
||||
@@ -178,7 +191,16 @@ pub async fn get_all_mix_delegations(
|
||||
let mut with_everything: Vec<DelegationWithEverything> = Vec::with_capacity(delegations.len());
|
||||
|
||||
for delegation in delegations {
|
||||
let d = Delegation::from_mixnet_contract(delegation, reg)?;
|
||||
let mut error_strings: Vec<String> = vec![];
|
||||
|
||||
let d = Delegation::from_mixnet_contract(delegation.clone(), reg).tap_err(|err| {
|
||||
log::error!(
|
||||
" <<< Failed to get delegation for mix id {} from contract. Error: {}",
|
||||
delegation.mix_id,
|
||||
err
|
||||
);
|
||||
})?;
|
||||
|
||||
let uses_vesting_contract_tokens = d
|
||||
.proxy
|
||||
.as_ref()
|
||||
@@ -194,7 +216,15 @@ pub async fn get_all_mix_delegations(
|
||||
let mixnode = client
|
||||
.nyxd
|
||||
.get_mixnode_details(d.mix_id)
|
||||
.await?
|
||||
.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 accumulated_by_operator = mixnode
|
||||
@@ -202,14 +232,32 @@ pub async fn get_all_mix_delegations(
|
||||
.map(|m| {
|
||||
guard.display_coin_from_base_decimal(&base_mix_denom, m.rewarding_details.operator)
|
||||
})
|
||||
.transpose()?;
|
||||
.transpose()
|
||||
.tap_err(|err| {
|
||||
let str_err = format!(
|
||||
"Failed to get operator rewards as a display coin for mix_id = {}. Error: {}",
|
||||
d.mix_id, err
|
||||
);
|
||||
log::error!(" <<< {}", str_err);
|
||||
error_strings.push(str_err);
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let accumulated_by_delegates = mixnode
|
||||
.as_ref()
|
||||
.map(|m| {
|
||||
guard.display_coin_from_base_decimal(&base_mix_denom, m.rewarding_details.delegates)
|
||||
})
|
||||
.transpose()?;
|
||||
.transpose()
|
||||
.tap_err(|err| {
|
||||
let str_err = format!(
|
||||
"Failed to get delegator rewards as a display coin for mix_id = {}. Error: {}",
|
||||
d.mix_id, err
|
||||
);
|
||||
log::error!(" <<< {}", str_err);
|
||||
error_strings.push(str_err);
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let cost_params = mixnode
|
||||
.as_ref()
|
||||
@@ -219,19 +267,48 @@ pub async fn get_all_mix_delegations(
|
||||
reg,
|
||||
)
|
||||
})
|
||||
.transpose()?;
|
||||
.transpose()
|
||||
.tap_err(|err| {
|
||||
let str_err = format!(
|
||||
"Failed to mixnode cost params for mix_id = {}. Error: {}",
|
||||
d.mix_id, err
|
||||
);
|
||||
log::error!(" <<< {}", str_err);
|
||||
error_strings.push(str_err);
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
log::trace!(" >>> Get accumulated rewards: address = {}", address);
|
||||
let pending_reward = client
|
||||
.nyxd
|
||||
.get_pending_delegator_reward(&address, d.mix_id, d.proxy.clone())
|
||||
.await?;
|
||||
.await
|
||||
.tap_err(|err| {
|
||||
let str_err = format!(
|
||||
"Failed to get accumulated rewards for mix_id = {}. Error: {}",
|
||||
d.mix_id, err
|
||||
);
|
||||
log::error!(" <<< {}", str_err);
|
||||
error_strings.push(str_err);
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let accumulated_rewards = match &pending_reward.amount_earned {
|
||||
Some(reward) => {
|
||||
let amount = guard.attempt_convert_to_display_dec_coin(reward.clone().into())?;
|
||||
log::trace!(" <<< rewards = {:?}, amount = {}", pending_reward, amount);
|
||||
Some(amount)
|
||||
let amount = guard
|
||||
.attempt_convert_to_display_dec_coin(reward.clone().into())
|
||||
.tap_err(|err| {
|
||||
let str_err = format!("Failed to get convert reward to a display coin for mix_id = {}. Error: {}", d.mix_id, err);
|
||||
log::error!(" <<< {}", str_err);
|
||||
error_strings.push(str_err);
|
||||
})
|
||||
.ok();
|
||||
log::trace!(
|
||||
" <<< rewards = {:?}, amount = {:?}",
|
||||
pending_reward,
|
||||
amount
|
||||
);
|
||||
amount
|
||||
}
|
||||
None => {
|
||||
log::trace!(" <<< no rewards waiting");
|
||||
@@ -240,7 +317,23 @@ pub async fn get_all_mix_delegations(
|
||||
};
|
||||
|
||||
log::trace!(" >>> Get stake saturation: mix_id = {}", d.mix_id);
|
||||
let stake_saturation = client.nyxd.get_mixnode_stake_saturation(d.mix_id).await?;
|
||||
let stake_saturation = client
|
||||
.nyxd
|
||||
.get_mixnode_stake_saturation(d.mix_id)
|
||||
.await
|
||||
.tap_err(|err| {
|
||||
let str_err = format!(
|
||||
"Failed to get stake saturation for mix_id = {}. Error: {}",
|
||||
d.mix_id, err
|
||||
);
|
||||
log::error!(" <<< {}", str_err);
|
||||
error_strings.push(str_err);
|
||||
})
|
||||
.unwrap_or(StakeSaturationResponse {
|
||||
mix_id: d.mix_id,
|
||||
uncapped_saturation: None,
|
||||
current_saturation: None,
|
||||
});
|
||||
log::trace!(" <<< {:?}", stake_saturation);
|
||||
|
||||
log::trace!(
|
||||
@@ -251,6 +344,14 @@ pub async fn get_all_mix_delegations(
|
||||
.nym_api
|
||||
.get_mixnode_avg_uptime(d.mix_id)
|
||||
.await
|
||||
.tap_err(|err| {
|
||||
let str_err = format!(
|
||||
"Failed to get average uptime percentage for mix_id = {}. Error: {}",
|
||||
d.mix_id, err
|
||||
);
|
||||
log::error!(" <<< {}", str_err);
|
||||
error_strings.push(str_err);
|
||||
})
|
||||
.ok()
|
||||
.map(|r| r.avg_uptime);
|
||||
log::trace!(" <<< {:?}", avg_uptime_percent);
|
||||
@@ -262,8 +363,13 @@ pub async fn get_all_mix_delegations(
|
||||
let timestamp = client
|
||||
.nyxd
|
||||
.get_block_timestamp(Some(d.height as u32))
|
||||
.await?;
|
||||
let delegated_on_iso_datetime = timestamp.to_rfc3339();
|
||||
.await
|
||||
.tap_err(|err| {
|
||||
let str_err = format!("Failed to get block timestamp for height = {} for delegation to mix_id = {}. Error: {}", d.height, d.mix_id, err);
|
||||
log::error!(" <<< {}", str_err);
|
||||
error_strings.push(str_err);
|
||||
}).ok();
|
||||
let delegated_on_iso_datetime = timestamp.map(|ts| ts.to_rfc3339());
|
||||
log::trace!(
|
||||
" <<< timestamp = {:?}, delegated_on_iso_datetime = {:?}",
|
||||
timestamp,
|
||||
@@ -301,6 +407,11 @@ pub async fn get_all_mix_delegations(
|
||||
unclaimed_rewards: accumulated_rewards,
|
||||
pending_events,
|
||||
mixnode_is_unbonding,
|
||||
errors: if error_strings.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(error_strings.join("\n"))
|
||||
},
|
||||
})
|
||||
}
|
||||
log::trace!("<<< {:?}", with_everything);
|
||||
|
||||
@@ -60,10 +60,16 @@ const GatewaySignatureForm = ({
|
||||
return (
|
||||
<Stack gap={3} mb={3}>
|
||||
<Typography variant="body1">
|
||||
Copy below message and sign it with your gateway using the following command
|
||||
Copy the message below and sign it:
|
||||
<br />
|
||||
If you are using a nym-gateway:
|
||||
<br />
|
||||
<code>nym-gateway sign --id <your-node-id> --contract-msg <payload-generated-by-the-wallet></code>
|
||||
<br />
|
||||
If you are using a nym-node:
|
||||
<br />
|
||||
<code>nym-node sign --id <your-node-id> --contract-msg <payload-generated-by-the-wallet></code>
|
||||
<br />
|
||||
Then paste the signature in the next field.
|
||||
</Typography>
|
||||
<TextField id="outlined-multiline-static" multiline rows={7} value={message} fullWidth disabled />
|
||||
|
||||
@@ -61,10 +61,16 @@ const MixnodeSignatureForm = ({
|
||||
return (
|
||||
<Stack gap={3} mb={3}>
|
||||
<Typography variant="body1">
|
||||
Copy below message and sign it with your mix node using the following command
|
||||
Copy the message below and sign it:
|
||||
<br />
|
||||
If you are using a nym-mixnode:
|
||||
<br />
|
||||
<code>nym-mixnode sign --id <your-node-id> --contract-msg <payload-generated-by-the-wallet></code>
|
||||
<br />
|
||||
If you are using a nym-node:
|
||||
<br />
|
||||
<code>nym-node sign --id <your-node-id> --contract-msg <payload-generated-by-the-wallet></code>
|
||||
<br />
|
||||
Then paste the signature in the next field.
|
||||
</Typography>
|
||||
<TextField id="outlined-multiline-static" multiline rows={7} value={message} fullWidth disabled />
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from 'react';
|
||||
import { Chip, IconButton, TableCell, TableRow, Tooltip, Typography } from '@mui/material';
|
||||
import { Box, Chip, IconButton, TableCell, TableRow, Tooltip, Typography } from '@mui/material';
|
||||
import { Link } from '@nymproject/react/link/Link';
|
||||
import { decimalToPercentage, DelegationWithEverything } from '@nymproject/types';
|
||||
import { LockOutlined } from '@mui/icons-material';
|
||||
import { isDelegation } from 'src/context/delegations';
|
||||
import { LockOutlined, WarningAmberOutlined } from '@mui/icons-material';
|
||||
import { isDelegation, useDelegationContext } from 'src/context/delegations';
|
||||
import { toPercentIntegerString } from 'src/utils';
|
||||
import { format } from 'date-fns';
|
||||
import { Undelegate } from 'src/svg-icons';
|
||||
@@ -29,6 +29,8 @@ export const DelegationItem = ({
|
||||
nodeIsUnbonded: boolean;
|
||||
onItemActionClick?: (item: DelegationWithEverything, action: DelegationListItemActions) => void;
|
||||
}) => {
|
||||
const { setDelegationItemErrors } = useDelegationContext();
|
||||
|
||||
const operatingCost = isDelegation(item) && item.cost_params?.interval_operating_cost;
|
||||
|
||||
const tooltipText = () => {
|
||||
@@ -45,13 +47,26 @@ export const DelegationItem = ({
|
||||
{nodeIsUnbonded ? (
|
||||
'-'
|
||||
) : (
|
||||
<Link
|
||||
target="_blank"
|
||||
href={`${explorerUrl}/network-components/mixnode/${item.mix_id}`}
|
||||
text={`${item.node_identity.slice(0, 6)}...${item.node_identity.slice(-6)}`}
|
||||
color="text.primary"
|
||||
noIcon
|
||||
/>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
{item.errors && (
|
||||
<Tooltip title="Open to view a list of errors that occurred">
|
||||
<IconButton
|
||||
sx={{ mr: 1 }}
|
||||
size="small"
|
||||
onClick={() => setDelegationItemErrors({ nodeId: item.node_identity, errors: item.errors! })}
|
||||
>
|
||||
<WarningAmberOutlined color="warning" fontSize="small" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
<Link
|
||||
target="_blank"
|
||||
href={`${explorerUrl}/network-components/mixnode/${item.mix_id}`}
|
||||
text={`${item.node_identity.slice(0, 6)}...${item.node_identity.slice(-6)}`}
|
||||
color="text.primary"
|
||||
noIcon
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell sx={{ color: 'inherit' }}>
|
||||
@@ -70,7 +85,7 @@ export const DelegationItem = ({
|
||||
</TableCell>
|
||||
<TableCell sx={{ color: 'inherit' }}>{getStakeSaturation(item)}</TableCell>
|
||||
<TableCell sx={{ color: 'inherit' }}>
|
||||
{format(new Date(item.delegated_on_iso_datetime), 'dd/MM/yyyy')}
|
||||
{item.delegated_on_iso_datetime && format(new Date(item.delegated_on_iso_datetime), 'dd/MM/yyyy')}
|
||||
</TableCell>
|
||||
<TableCell sx={{ color: 'inherit' }}>
|
||||
<Typography style={{ textTransform: 'uppercase', fontSize: 'inherit' }}>
|
||||
|
||||
@@ -34,6 +34,7 @@ export const items: DelegationWithEverything[] = [
|
||||
uses_vesting_contract_tokens: false,
|
||||
pending_events: [],
|
||||
mixnode_is_unbonding: true,
|
||||
errors: null,
|
||||
},
|
||||
{
|
||||
mix_id: 2,
|
||||
@@ -57,6 +58,7 @@ export const items: DelegationWithEverything[] = [
|
||||
uses_vesting_contract_tokens: true,
|
||||
pending_events: [],
|
||||
mixnode_is_unbonding: true,
|
||||
errors: null,
|
||||
},
|
||||
{
|
||||
mix_id: 3,
|
||||
@@ -80,6 +82,7 @@ export const items: DelegationWithEverything[] = [
|
||||
uses_vesting_contract_tokens: true,
|
||||
pending_events: [],
|
||||
mixnode_is_unbonding: true,
|
||||
errors: null,
|
||||
},
|
||||
{
|
||||
mix_id: 4,
|
||||
@@ -103,6 +106,7 @@ export const items: DelegationWithEverything[] = [
|
||||
uses_vesting_contract_tokens: true,
|
||||
pending_events: [],
|
||||
mixnode_is_unbonding: true,
|
||||
errors: null,
|
||||
},
|
||||
{
|
||||
mix_id: 5,
|
||||
@@ -126,6 +130,7 @@ export const items: DelegationWithEverything[] = [
|
||||
uses_vesting_contract_tokens: true,
|
||||
pending_events: [],
|
||||
mixnode_is_unbonding: true,
|
||||
errors: null,
|
||||
},
|
||||
{
|
||||
mix_id: 6,
|
||||
@@ -149,6 +154,7 @@ export const items: DelegationWithEverything[] = [
|
||||
uses_vesting_contract_tokens: true,
|
||||
pending_events: [],
|
||||
mixnode_is_unbonding: true,
|
||||
errors: null,
|
||||
},
|
||||
{
|
||||
mix_id: 7,
|
||||
@@ -172,6 +178,7 @@ export const items: DelegationWithEverything[] = [
|
||||
uses_vesting_contract_tokens: false,
|
||||
pending_events: [],
|
||||
mixnode_is_unbonding: true,
|
||||
errors: null,
|
||||
},
|
||||
{
|
||||
mix_id: 8,
|
||||
@@ -195,6 +202,7 @@ export const items: DelegationWithEverything[] = [
|
||||
uses_vesting_contract_tokens: true,
|
||||
pending_events: [],
|
||||
mixnode_is_unbonding: true,
|
||||
errors: null,
|
||||
},
|
||||
{
|
||||
mix_id: 9,
|
||||
@@ -218,6 +226,7 @@ export const items: DelegationWithEverything[] = [
|
||||
uses_vesting_contract_tokens: true,
|
||||
pending_events: [],
|
||||
mixnode_is_unbonding: true,
|
||||
errors: null,
|
||||
},
|
||||
{
|
||||
mix_id: 10,
|
||||
@@ -241,6 +250,7 @@ export const items: DelegationWithEverything[] = [
|
||||
uses_vesting_contract_tokens: true,
|
||||
pending_events: [],
|
||||
mixnode_is_unbonding: true,
|
||||
errors: null,
|
||||
},
|
||||
{
|
||||
mix_id: 11,
|
||||
@@ -264,6 +274,7 @@ export const items: DelegationWithEverything[] = [
|
||||
uses_vesting_contract_tokens: true,
|
||||
pending_events: [],
|
||||
mixnode_is_unbonding: true,
|
||||
errors: null,
|
||||
},
|
||||
{
|
||||
mix_id: 12,
|
||||
@@ -287,6 +298,7 @@ export const items: DelegationWithEverything[] = [
|
||||
uses_vesting_contract_tokens: true,
|
||||
pending_events: [],
|
||||
mixnode_is_unbonding: true,
|
||||
errors: null,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -8,7 +8,8 @@ import { DelegationListItemActions } from './DelegationActions';
|
||||
import { DelegationItem } from './DelegationItem';
|
||||
import { PendingDelegationItem } from './PendingDelegationItem';
|
||||
import { LoadingModal } from '../Modals/LoadingModal';
|
||||
import { isDelegation, isPendingDelegation, TDelegations } from '../../context/delegations';
|
||||
import { isDelegation, isPendingDelegation, TDelegations, useDelegationContext } from '../../context/delegations';
|
||||
import { ErrorModal } from '../Modals/ErrorModal';
|
||||
|
||||
export type Order = 'asc' | 'desc';
|
||||
type AdditionalTypes = { profit_margin_percent: number; operating_cost: number };
|
||||
@@ -94,6 +95,8 @@ export const DelegationList: FCWithChildren<{
|
||||
const [order, setOrder] = React.useState<Order>('asc');
|
||||
const [orderBy, setOrderBy] = React.useState<SortingKeys>('delegated_on_iso_datetime');
|
||||
|
||||
const { delegationItemErrors, setDelegationItemErrors } = useDelegationContext();
|
||||
|
||||
const handleRequestSort = (_: React.MouseEvent<unknown>, property: any) => {
|
||||
const isAsc = orderBy === property && order === 'asc';
|
||||
setOrder(isAsc ? 'desc' : 'asc');
|
||||
@@ -105,6 +108,12 @@ export const DelegationList: FCWithChildren<{
|
||||
return (
|
||||
<TableContainer>
|
||||
{isLoading && <LoadingModal text="Please wait. Refreshing..." />}
|
||||
<ErrorModal
|
||||
open={Boolean(delegationItemErrors)}
|
||||
title={`Delegation errors for Node ID ${delegationItemErrors?.nodeId || 'unknown'}`}
|
||||
message={delegationItemErrors?.errors || 'oops'}
|
||||
onClose={() => setDelegationItemErrors(undefined)}
|
||||
/>
|
||||
<Table sx={{ width: '100%' }}>
|
||||
<EnhancedTableHead order={order} orderBy={orderBy} onRequestSort={handleRequestSort} />
|
||||
<TableBody>
|
||||
|
||||
@@ -20,6 +20,7 @@ import { decCoinToDisplay } from 'src/utils';
|
||||
import { Console } from 'src/utils/console';
|
||||
|
||||
export type TDelegationContext = {
|
||||
delegationItemErrors?: { nodeId: string; errors: string };
|
||||
isLoading: boolean;
|
||||
delegations?: TDelegations;
|
||||
pendingDelegations?: WrappedDelegationEvent[];
|
||||
@@ -34,6 +35,7 @@ export type TDelegationContext = {
|
||||
) => Promise<TransactionExecuteResult>;
|
||||
undelegate: (mix_id: number, fee?: Fee) => Promise<TransactionExecuteResult>;
|
||||
undelegateVesting: (mix_id: number) => Promise<TransactionExecuteResult>;
|
||||
setDelegationItemErrors: (data: { nodeId: string; errors: string } | undefined) => void;
|
||||
};
|
||||
|
||||
export type TDelegationTransaction = {
|
||||
@@ -61,6 +63,7 @@ export const DelegationContext = createContext<TDelegationContext>({
|
||||
undelegateVesting: () => {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
setDelegationItemErrors: () => undefined,
|
||||
});
|
||||
|
||||
export const DelegationContextProvider: FC<{
|
||||
@@ -69,6 +72,7 @@ export const DelegationContextProvider: FC<{
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
}> = ({ network, children }) => {
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [delegationItemErrors, setDelegationItemErrors] = useState<{ nodeId: string; errors: string }>();
|
||||
const [delegations, setDelegations] = useState<undefined | TDelegations>();
|
||||
const [totalDelegations, setTotalDelegations] = useState<undefined | string>();
|
||||
const [totalRewards, setTotalRewards] = useState<undefined | string>();
|
||||
@@ -130,6 +134,7 @@ export const DelegationContextProvider: FC<{
|
||||
|
||||
const memoizedValue = useMemo(
|
||||
() => ({
|
||||
delegationItemErrors,
|
||||
isLoading,
|
||||
delegations,
|
||||
pendingDelegations,
|
||||
@@ -137,11 +142,20 @@ export const DelegationContextProvider: FC<{
|
||||
totalRewards,
|
||||
totalDelegationsAndRewards,
|
||||
refresh,
|
||||
setDelegationItemErrors,
|
||||
addDelegation,
|
||||
undelegate: undelegateFromMixnode,
|
||||
undelegateVesting: vestingUndelegateFromMixnode,
|
||||
}),
|
||||
[isLoading, delegations, pendingDelegations, totalDelegations, totalRewards, totalDelegationsAndRewards],
|
||||
[
|
||||
isLoading,
|
||||
delegations,
|
||||
delegationItemErrors,
|
||||
pendingDelegations,
|
||||
totalDelegations,
|
||||
totalRewards,
|
||||
totalDelegationsAndRewards,
|
||||
],
|
||||
);
|
||||
|
||||
return <DelegationContext.Provider value={memoizedValue}>{children}</DelegationContext.Provider>;
|
||||
|
||||
@@ -37,6 +37,7 @@ let mockDelegations: DelegationWithEverything[] = [
|
||||
uses_vesting_contract_tokens: false,
|
||||
pending_events: [],
|
||||
mixnode_is_unbonding: false,
|
||||
errors: null,
|
||||
},
|
||||
{
|
||||
mix_id: 5678,
|
||||
@@ -60,6 +61,7 @@ let mockDelegations: DelegationWithEverything[] = [
|
||||
uses_vesting_contract_tokens: true,
|
||||
pending_events: [],
|
||||
mixnode_is_unbonding: false,
|
||||
errors: null,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -69,6 +71,7 @@ export const MockDelegationContextProvider: FCWithChildren = ({ children }) => {
|
||||
const [error, setError] = useState<string>();
|
||||
const [delegations, setDelegations] = useState<undefined | DelegationWithEverything[]>();
|
||||
const [totalDelegations, setTotalDelegations] = useState<undefined | string>();
|
||||
const [delegationItemErrors, setDelegationItemErrors] = useState<{ nodeId: string; errors: string }>();
|
||||
|
||||
const triggerStateUpdate = () => setTrigger(new Date());
|
||||
|
||||
@@ -230,6 +233,8 @@ export const MockDelegationContextProvider: FCWithChildren = ({ children }) => {
|
||||
|
||||
const memoizedValue = useMemo(
|
||||
() => ({
|
||||
delegationItemErrors,
|
||||
setDelegationItemErrors,
|
||||
isLoading,
|
||||
error,
|
||||
delegations,
|
||||
|
||||
@@ -101,7 +101,7 @@ export const Delegation: FC<{ isStorybook?: boolean }> = ({ isStorybook }) => {
|
||||
|
||||
// Refresh the rewards and delegations periodically when page is mounted
|
||||
useEffect(() => {
|
||||
const timer = setInterval(refreshWithIntervalUpdate, 1 * 60 * 1000); // every 1 minute
|
||||
const timer = setInterval(refreshWithIntervalUpdate, 5 * 60 * 1000); // every 5 minutes
|
||||
return () => clearInterval(timer);
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
[package]
|
||||
name = "nym-network-requester"
|
||||
license = "GPL-3.0"
|
||||
version = "1.1.34"
|
||||
version = "1.1.35"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version = "1.70"
|
||||
|
||||
@@ -112,41 +112,18 @@ pub(crate) struct OverrideConfig {
|
||||
|
||||
// NOTE: make sure this is in sync with `gateway/src/helpers.rs::override_network_requester_config`
|
||||
pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Config {
|
||||
// as of 12.09.23 the below is true (not sure how this comment will rot in the future)
|
||||
// medium_toggle:
|
||||
// - sets secondary packet size to 16kb
|
||||
// - disables poisson distribution of the main traffic stream
|
||||
// - sets the cover traffic stream to 1 packet / 5s (on average)
|
||||
// - disables per hop delay
|
||||
//
|
||||
// fastmode (to be renamed to `fast-poisson`):
|
||||
// - sets average per hop delay to 10ms
|
||||
// - sets the cover traffic stream to 1 packet / 2000s (on average); for all intents and purposes it disables the stream
|
||||
// - sets the poisson distribution of the main traffic stream to 4ms, i.e. 250 packets / s on average
|
||||
//
|
||||
// no_cover:
|
||||
// - disables poisson distribution of the main traffic stream
|
||||
// - disables the secondary cover traffic stream
|
||||
|
||||
// disable poisson rate in the BASE client if the NR option is enabled
|
||||
if config.network_requester.disable_poisson_rate {
|
||||
config.set_no_poisson_process();
|
||||
}
|
||||
|
||||
// those should be enforced by `clap` when parsing the arguments
|
||||
if args.medium_toggle {
|
||||
assert!(!args.fastmode);
|
||||
assert!(!args.no_cover);
|
||||
|
||||
config.set_medium_toggle();
|
||||
}
|
||||
// in the old code we had calls to `assert` thus panicking
|
||||
config
|
||||
.base
|
||||
.try_apply_traffic_modes(
|
||||
config.network_requester.disable_poisson_rate,
|
||||
args.medium_toggle,
|
||||
args.fastmode,
|
||||
args.no_cover,
|
||||
)
|
||||
.expect("failed to apply traffic modes");
|
||||
|
||||
config
|
||||
.with_base(
|
||||
BaseClientConfig::with_high_default_traffic_volume,
|
||||
args.fastmode,
|
||||
)
|
||||
.with_base(BaseClientConfig::with_disabled_cover_traffic, args.no_cover)
|
||||
.with_optional_base_custom_env(
|
||||
BaseClientConfig::with_custom_nym_apis,
|
||||
args.nym_apis,
|
||||
|
||||
@@ -12,7 +12,6 @@ use nym_config::{
|
||||
};
|
||||
use nym_network_defaults::mainnet;
|
||||
use nym_service_providers_common::DEFAULT_SERVICE_PROVIDERS_DIR;
|
||||
use nym_sphinx::params::PacketSize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
@@ -149,20 +148,6 @@ impl Config {
|
||||
self.base.validate()
|
||||
}
|
||||
|
||||
/// Enable medium mixnet traffic, for experiments only.
|
||||
/// This includes things like disabling cover traffic, no per hop delays, etc.
|
||||
#[doc(hidden)]
|
||||
pub fn set_medium_toggle(&mut self) {
|
||||
self.base.set_no_cover_traffic_with_keepalive();
|
||||
self.base.set_no_per_hop_delays();
|
||||
self.base.debug.traffic.secondary_packet_size = Some(PacketSize::ExtendedPacket16);
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn set_no_poisson_process(&mut self) {
|
||||
self.base.set_no_poisson_process()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_open_proxy(mut self, open_proxy: bool) -> Self {
|
||||
self.network_requester.open_proxy = open_proxy;
|
||||
@@ -182,6 +167,7 @@ impl Config {
|
||||
}
|
||||
|
||||
// poor man's 'builder' method
|
||||
#[allow(unused)]
|
||||
pub fn with_base<F, T>(mut self, f: F, val: T) -> Self
|
||||
where
|
||||
F: Fn(BaseClientConfig, T) -> BaseClientConfig,
|
||||
|
||||
@@ -11,7 +11,7 @@ export interface DelegationWithEverything {
|
||||
accumulated_by_delegates: DecCoin | null;
|
||||
accumulated_by_operator: DecCoin | null;
|
||||
block_height: bigint;
|
||||
delegated_on_iso_datetime: string;
|
||||
delegated_on_iso_datetime: string | null;
|
||||
cost_params: MixNodeCostParams | null;
|
||||
avg_uptime_percent: number | null;
|
||||
stake_saturation: string | null;
|
||||
@@ -19,4 +19,5 @@ export interface DelegationWithEverything {
|
||||
unclaimed_rewards: DecCoin | null;
|
||||
pending_events: Array<DelegationEvent>;
|
||||
mixnode_is_unbonding: boolean | null;
|
||||
errors: string | null;
|
||||
}
|
||||
|
||||
@@ -10735,9 +10735,9 @@ ee-first@1.1.1:
|
||||
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
|
||||
|
||||
ejs@^3.1.7:
|
||||
version "3.1.9"
|
||||
resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.9.tgz#03c9e8777fe12686a9effcef22303ca3d8eeb361"
|
||||
integrity sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==
|
||||
version "3.1.10"
|
||||
resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b"
|
||||
integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==
|
||||
dependencies:
|
||||
jake "^10.8.5"
|
||||
|
||||
@@ -20299,7 +20299,16 @@ string-to-color@^2.2.2:
|
||||
lodash.words "^4.2.0"
|
||||
rgb-hex "^3.0.0"
|
||||
|
||||
"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
|
||||
"string-width-cjs@npm:string-width@^4.2.0":
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
dependencies:
|
||||
emoji-regex "^8.0.0"
|
||||
is-fullwidth-code-point "^3.0.0"
|
||||
strip-ansi "^6.0.1"
|
||||
|
||||
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
@@ -20399,7 +20408,7 @@ stringify-entities@^4.0.0:
|
||||
character-entities-html4 "^2.0.0"
|
||||
character-entities-legacy "^3.0.0"
|
||||
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
@@ -20413,6 +20422,13 @@ strip-ansi@^3.0.1:
|
||||
dependencies:
|
||||
ansi-regex "^2.0.0"
|
||||
|
||||
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
dependencies:
|
||||
ansi-regex "^5.0.1"
|
||||
|
||||
strip-ansi@^7.0.1:
|
||||
version "7.1.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
|
||||
@@ -22436,7 +22452,7 @@ workerpool@6.2.1:
|
||||
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343"
|
||||
integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==
|
||||
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
@@ -22454,6 +22470,15 @@ wrap-ansi@^6.0.1:
|
||||
string-width "^4.1.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
wrap-ansi@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
dependencies:
|
||||
ansi-styles "^4.0.0"
|
||||
string-width "^4.1.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
wrap-ansi@^8.1.0:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
|
||||
|
||||
Reference in New Issue
Block a user