Compare commits
101 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1ecc2a8dda | |||
| f9659ef42c | |||
| c74494a21d | |||
| 54f6c98c22 | |||
| 846484bbb4 | |||
| fb3f5501ba | |||
| e8a607f520 | |||
| f5f6df9eaf | |||
| c647ab5605 | |||
| 416c21a42e | |||
| fd5a95fa4d | |||
| c61df79182 | |||
| 08559a7660 | |||
| 6dce55a99b | |||
| bc0b89b31c | |||
| 67c32faa11 | |||
| aa0d15ee67 | |||
| 4f0974fcf1 | |||
| bd2174641e | |||
| 59b62fabc9 | |||
| e6f4bae895 | |||
| d71742af32 | |||
| 3b7c07e249 | |||
| 3b429dde69 | |||
| 3a29c296da | |||
| 8544c54f8f | |||
| 9f9639950b | |||
| 111a0b20b6 | |||
| 67b300d0b8 | |||
| 88c4e0ce6c | |||
| f6800aff0a | |||
| 0c7c927ca2 | |||
| a69c8b1660 | |||
| f3ea310a46 | |||
| 92f9ff035d | |||
| 5a817e1df1 | |||
| a07a24db00 | |||
| a0cb812eff | |||
| 923c1fa184 | |||
| 35ea7e4926 | |||
| d1cb9afaf0 | |||
| 79d4b4b2e3 | |||
| 8460b33946 | |||
| ae6539e07c | |||
| 18cebdfedc | |||
| c448ec823a | |||
| a266137278 | |||
| 6f4dfd1dab | |||
| 57719787db | |||
| 29a57bf172 | |||
| db813b6e3e | |||
| 1be5ba310a | |||
| 41ff3f7824 | |||
| c9d4d62446 | |||
| e839a0d80e | |||
| cd61f930bf | |||
| 0674f31227 | |||
| 3e4f563dce | |||
| edcf2b1204 | |||
| b07fb18113 | |||
| 017dea4afd | |||
| 5a9ce13beb | |||
| 514cf25c68 | |||
| 49ee0636e4 | |||
| bb971ce99c | |||
| 54de369c1e | |||
| 6d6ce284df | |||
| 56ad1c6c8e | |||
| 10b4a288c8 | |||
| bbbb9486ce | |||
| 16e86e1a07 | |||
| ca0c9898f0 | |||
| 8b73d4e615 | |||
| 6a9a767ab4 | |||
| e03a9fa16f | |||
| a0fbd57d5b | |||
| 9856198356 | |||
| 5c33846e57 | |||
| cfa7635ae1 | |||
| 5d45544c27 | |||
| aa6a79cb3e | |||
| b3a940770a | |||
| e980f76a81 | |||
| 9b38fef28f | |||
| 43910ca635 | |||
| d3ccd7575a | |||
| 422f889df7 | |||
| c9e96edc35 | |||
| 7768317046 | |||
| 0ebbb1a540 | |||
| 827c13b69e | |||
| 18ff09608c | |||
| 8cc996bc0d | |||
| 83a598907f | |||
| bb06a1b7a8 | |||
| e783a5fced | |||
| 8a24b45b5d | |||
| 10e4eba727 | |||
| 8ebf482f36 | |||
| 6940ca427e | |||
| 24f877fda5 |
Generated
+3
-3
@@ -133,9 +133,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
[[package]]
|
||||
name = "ammonia"
|
||||
version = "4.1.2"
|
||||
version = "4.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17e913097e1a2124b46746c980134e8c954bc17a6a59bb3fde96f088d126dde6"
|
||||
checksum = "d6b346764dd0814805de8abf899fe03065bcee69bb1a4771c785817e39f3978f"
|
||||
dependencies = [
|
||||
"cssparser",
|
||||
"html5ever",
|
||||
@@ -6546,7 +6546,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-node-status-api"
|
||||
version = "4.0.11-rc1"
|
||||
version = "4.0.10"
|
||||
dependencies = [
|
||||
"ammonia",
|
||||
"anyhow",
|
||||
|
||||
@@ -1 +1 @@
|
||||
Thursday, October 30th 2025, 13:00:59 UTC
|
||||
Tuesday, October 14th 2025, 11:34:14 UTC
|
||||
|
||||
@@ -11,7 +11,7 @@ options:
|
||||
--no_routing_history Display node stats without routing history
|
||||
--no_verloc_metrics Display node stats without verloc metrics
|
||||
-m, --markdown Display results in markdown format
|
||||
-o [OUTPUT], --output [OUTPUT]
|
||||
-o, --output [OUTPUT]
|
||||
Save results to file (in current dir or supply with
|
||||
path without filename)
|
||||
```
|
||||
|
||||
@@ -18,23 +18,23 @@
|
||||
| [Hostslick](https://hostslick.com) | Netherlands, Germany | Yes, on by default | Yes | Good amount of bandwidth for the price. Make sure you open the ticket if you want to run Exit node | 07/2024 |
|
||||
| [Incognet](https://incognet.io) | Netherlands and USA | Yes, on by default | Yes | They allow Tor exit nodes but you must adhere to their rules https://incognet.io/tor-exits | 07/2024 |
|
||||
| [Incognet](https://incognet.io/kansas-city-dedicated-servers) | USA, Netherlands | Yes | nan | nan | 07/2025 |
|
||||
| [Ionos](https://www.ionos.com/servers/amd-servers) | USA, DE, UK, ESP, FR | nan | No | nan | 07/2025 |
|
||||
| [Ionos](https://www.ionos.com/servers/amd-servers) | US, DE, UK, ESP, FR | nan | No | nan | 07/2025 |
|
||||
| [IsHosting](https://ishosting.com/en) | Brazil, Netherlands | Yes, based on ticket | Yes | Expensive | 05/2024 |
|
||||
| [Leaseweb](https://www.leaseweb.com/en/configure/vc/product/entityKey/DEDSER02_NEW_ORDER_BUSINESS_R740XD-24SFF-6134) | USA, NL, DE, UK, CA, SG, JP, AUS, HK | nan | No | KYC mandatory | 07/2025 |
|
||||
| [Leaseweb](https://www.leaseweb.com/en/configure/vc/product/entityKey/DEDSER02_NEW_ORDER_BUSINESS_R740XD-24SFF-6134) | US, NL, DE, UK, CA, SG, JP, AUS, HK | nan | No | KYC mandatory | 07/2025 |
|
||||
| [Linode](https://linode.com) | USA, Canada, Japan, India, Indonesia, Sweden, Netherlands, Germany, Brazil, France, UK, Australia, Italy | Yes out of the box | No, only through [BitLAunch](https://bitlaunch.io) | IPv6 sometimes need to be re-added in Networking tab, no reboot needed | 05/2024 |
|
||||
| [LiteServer](https://liteserver.nl) | Netherlands | Yes, on by default | Yes | Very reliable Dutch provider. They do allow Relay nodes but for Exit nodes you need to contact them. Always check T&C https://liteserver.nl/legal | 07/2024 |
|
||||
| [Lowendbox](https://lowendbox.com/category/dedicated-servers) | | | | Just an aggregator with good offers | 07/2025 |
|
||||
| [M247](https://m247.com/eu/services/host/dedicated-servers/) | UK, Austria, Br, Sw, Jp, Poland, Fr, USA, Netherlands | Yes | No | nan | 07/2025 |
|
||||
| [Mebilcom](https://www.melbicom.net/dedicatedserver/) | NL, USA, DE, UAE, NG, ESP, IN, IT, FR, LT, SG, BG, LV, PL | nan | No | nan | 07/2025 |
|
||||
| [Mebilcom](https://www.melbicom.net/dedicatedserver/) | NL, US, DE, UAE, NG, ESP, IN, IT, FR, LT, SG, BG, LV, PL | nan | No | nan | 07/2025 |
|
||||
| [Mevspace](https://mevspace.com) | Poland | Yes, on by default | Yes | Flexible Polish providers with 3 DCs in Poland. They do allow Tor Exit nodes but you may need a dedicated server for this. Make sure you open a ticket to check. As of today's date, they have 48h for 1 EUR tariff | 07/2024 |
|
||||
| [Misaka](https://www.misaka.io/) | South Africa | Yes, native support | No | Very Expensive | 05/2024 |
|
||||
| [NiceVPS](https://nicevps.net/) | Netherlands | Yes | nan | nan | 07/2025 |
|
||||
| [Njalla](https://nja.la) | Sweden | Yes | Yes | Privacy vandguards! The biggest VPS 45 is 3 cores only, but it works better than many “larger” servers on the market. | 05/2024 |
|
||||
| [OVH](https://us.ovhcloud.com/bare-metal/rise/rise-3/) | USA, DE, FR, UK, PL, CA | | No | Not all locations always available | 07/2025 |
|
||||
| [Oneprovider](https://oneprovider.com/en/dedicated-servers/ipv6) | PL, FR, NL, UA, USA, BG, RO, DK, ESP, NO, CZ, RS, IE, IT, UK, HU, CH, SK, AT, BE, BA, HK, JP, SG, LU, AU, SWE, UAE, BR, CR, MX, GR, CL, MA, AR | Yes | No | nan | 07/2025 |
|
||||
| [Oneprovider](https://oneprovider.com/en/dedicated-servers/ipv6) | PL, FR, NL, UA, US, BG, RO, DK, ESP, NO, CZ, RS, IE, IT, UK, HU, CH, SK, AT, BE, BA, HK, JP, SG, LU, AU, SWE, UAE, BR, CR, MX, GR, CL, MA, AR | Yes | No | nan | 07/2025 |
|
||||
| [PrivateLayer](https://privatelayer.com) | Swiss | Yes | Yes | Slow customer response | 07/2025 |
|
||||
| [Privex](https://www.privex.io/tor-exit-policy/) | USA, Germany, Sweden | Yes | Yes | nan | 07/2025 |
|
||||
| [Psychz](https://www.psychz.net) | USA, UK, Brazil, Japan, Russia, South Africa and many more | Yes | nan | nan | 07/2025 |
|
||||
| [Psychz](https://www.psychz.net) | US, UK, Brazil, Japan, Russia, South Africa and many more | Yes | nan | nan | 07/2025 |
|
||||
| [RDP](https://rdp.sh) | Netherlands, USA, Poland | Yes, on by default | Yes | German provider. Exit nodes are allowed, policy is here https://rdp.sh/docs/faq/tor ports 25,465,587 must be closed. Make sure you open a ticket before running an exit node. | 07/2024 |
|
||||
| [Servermania](https://www.servermania.com/dedicated-servers-hosting.htm) | USA, Canada | nan | No | nan | 07/2025 |
|
||||
| [Svea](https://svea.net/vps) | Sweden | Yes | nan | nan | 07/2025 |
|
||||
|
||||
@@ -21,11 +21,11 @@
|
||||
[Lowendbox](https://lowendbox.com/category/dedicated-servers), , , ,Just an aggregator with good offers,07/2025
|
||||
[Thundervm](https://thundervm.com/en/hosting/dedicated-server),"USA, UK, France, Italy, Switzerland, Netherlands",,Yes, ,07/2025
|
||||
[OVH](https://us.ovhcloud.com/bare-metal/rise/rise-3/),"USA, DE, FR, UK, PL, CA", ,No,Not all locations always available,07/2025
|
||||
[Mebilcom](https://www.melbicom.net/dedicatedserver/),"NL, USA, DE, UAE, NG, ESP, IN, IT, FR, LT, SG, BG, LV, PL",,No,,07/2025
|
||||
[Mebilcom](https://www.melbicom.net/dedicatedserver/),"NL, US, DE, UAE, NG, ESP, IN, IT, FR, LT, SG, BG, LV, PL",,No,,07/2025
|
||||
[Servermania](https://www.servermania.com/dedicated-servers-hosting.htm),"USA, Canada",,No,,07/2025
|
||||
[Oneprovider](https://oneprovider.com/en/dedicated-servers/ipv6),"PL, FR, NL, UA, USA, BG, RO, DK, ESP, NO, CZ, RS, IE, IT, UK, HU, CH, SK, AT, BE, BA, HK, JP, SG, LU, AU, SWE, UAE, BR, CR, MX, GR, CL, MA, AR",Yes,No,,07/2025
|
||||
[Ionos](https://www.ionos.com/servers/amd-servers),"USA, DE, UK, ESP, FR",,No,,07/2025
|
||||
[Leaseweb](https://www.leaseweb.com/en/configure/vc/product/entityKey/DEDSER02_NEW_ORDER_BUSINESS_R740XD-24SFF-6134),"USA, NL, DE, UK, CA, SG, JP, AUS, HK",,No,KYC mandatory,07/2025
|
||||
[Oneprovider](https://oneprovider.com/en/dedicated-servers/ipv6),"PL, FR, NL, UA, US, BG, RO, DK, ESP, NO, CZ, RS, IE, IT, UK, HU, CH, SK, AT, BE, BA, HK, JP, SG, LU, AU, SWE, UAE, BR, CR, MX, GR, CL, MA, AR",Yes,No,,07/2025
|
||||
[Ionos](https://www.ionos.com/servers/amd-servers),"US, DE, UK, ESP, FR",,No,,07/2025
|
||||
[Leaseweb](https://www.leaseweb.com/en/configure/vc/product/entityKey/DEDSER02_NEW_ORDER_BUSINESS_R740XD-24SFF-6134),"US, NL, DE, UK, CA, SG, JP, AUS, HK",,No,KYC mandatory,07/2025
|
||||
[M247](https://m247.com/eu/services/host/dedicated-servers/),"UK, Austria, Br, Sw, Jp, Poland, Fr, USA, Netherlands",Yes,No,,07/2025
|
||||
[Hostroyale](https://hostroyale.com/hosting/dedicated-server/),Various countries with different pricing,, Yes,,07/2025
|
||||
[DataPacket](https://www.datapacket.com/pricing),"NL, GR, SK, BE, RO, HU, DK, IE, DE, UA, PT, GB, ES, FR, IT, NO, CZ, BG, SE, AT, PL, HR, CH, USA, CO, AR, PE, MX, CL, TR, ZA, NG, IL, HK, AU, SG, JP",Yes,,,07/2025
|
||||
@@ -35,7 +35,7 @@
|
||||
[Colocall](https://www.colocall.net/),Ukraine,Yes,,,07/2025
|
||||
[Incognet](https://incognet.io/kansas-city-dedicated-servers),"USA, Netherlands",Yes,,,07/2025
|
||||
[FranTech](https://my.frantech.ca),USA,Yes,,,07/2025
|
||||
[Psychz](https://www.psychz.net),"USA, UK, Brazil, Japan, Russia, South Africa and many more",Yes,,,07/2025
|
||||
[Psychz](https://www.psychz.net),"US, UK, Brazil, Japan, Russia, South Africa and many more",Yes,,,07/2025
|
||||
[Fsit](https://www.fsit.com/server/vps-vserver-kvm),Swiss,Yes,Yes,,07/2025
|
||||
[NiceVPS](https://nicevps.net/),Netherlands,Yes,,,07/2025
|
||||
[Dataclub](https://www.dataclub.eu/),"Latvia, Sweden, Netherlands",Yes,,,07/2027
|
||||
|
||||
|
@@ -49,90 +49,6 @@ This page displays a full list of all the changes during our release cycle from
|
||||
|
||||
<VarInfo />
|
||||
|
||||
## `v2025.19-kase`
|
||||
|
||||
- [Release Binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2025.19-kase)
|
||||
- [`nym-node`](nodes/nym-node.mdx) version `1.20.0`
|
||||
|
||||
```sh
|
||||
nym-node
|
||||
Binary Name: nym-node
|
||||
Build Timestamp: 2025-10-30T12:43:37.933354749Z
|
||||
Build Version: 1.20.0
|
||||
Commit SHA: 75a6d3426bd18dca600ad1cfa39b0a3c4f319c69
|
||||
Commit Date: 2025-10-30T11:59:32.000000000+01:00
|
||||
Commit Branch: HEAD
|
||||
rustc Version: 1.88.0
|
||||
rustc Channel: stable
|
||||
cargo Profile: release
|
||||
```
|
||||
|
||||
### Operators Updates & Tools
|
||||
|
||||
<Callout type="info" emoji="ℹ️">
|
||||
**When this platform release becomes latest, we would like to ask operators ruuning any Gateway mode of `nym-node`, to use new version of [QUIC brige deployment tool](https://github.com/nymtech/nym/blob/develop/scripts/nym-node-setup/quic_bridge_deployment.sh)and install QUIC `nym-bridge` on their server, following [these steps](#quic-transport-bridge-deployment).**
|
||||
</Callout>
|
||||
|
||||
Alongside this platform release we are happy to introduce several improvements and new tools for node operators.
|
||||
|
||||
- [Updated version of QUIC brige deployment tool](https://github.com/nymtech/nym/blob/develop/scripts/nym-node-setup/quic_bridge_deployment.sh), **if you run a `nym-node` in any Gateway mode, please install QUIC on your server, following [these steps](#quic-transport-bridge-deployment)**
|
||||
|
||||
- [New **Nym Node Status Dashboard**](https://node-status.nym.com)
|
||||
|
||||
- [New **Harbourmaster** aka ***Nym Node Status Observatory***](https://harbourmaster.nymtech.net)
|
||||
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
- [Propagate cancel token to mixnet client](https://github.com/nymtech/nym/pull/6105): Ensures cancellation token propagation to mixnet client
|
||||
|
||||
- [[DOCs/operators] QUIC deployment script & docs](https://github.com/nymtech/nym/pull/6098): Script and documentation for QUIC deployment, referencing `nym-bridges` repository
|
||||
|
||||
- [Move gateway probe to monorepo (Rust edition 2024)](https://github.com/nymtech/nym/pull/6094): Moves `nym-gateway-probe` and related packages into monorepo, updates to Rust 2024 edition
|
||||
|
||||
- [Expose reference to Mnemonic from `DirectSecp256k1HdWallet`](https://github.com/nymtech/nym/pull/6083): Adds safer accessors for mnemonic references and deprecates unsafe cloning
|
||||
|
||||
### Bugfix
|
||||
|
||||
- [Cherry pick - request #6143 from nymtech/bugfix/mix-tx-closed-v2](https://github.com/nymtech/nym/pull/6153): Add circuit breaker
|
||||
<AccordionTemplate name={<TestingSteps/>}>
|
||||
**Summary:**
|
||||
- Network-requester started successfully
|
||||
- SOCKS5 client started successfully
|
||||
- Traffic was proxied through the mixnet
|
||||
- Shutdown was clean
|
||||
- No 'channel closed (outside of shutdown!)' errors
|
||||
</AccordionTemplate>
|
||||
|
||||
- [`nym-credential-proxy` query params parsing regression](https://github.com/nymtech/nym/pull/6121): Fix query deserialization issue with `serde_urlencoded` breaking compatibility with VPN API
|
||||
|
||||
- [Revert some dep updates introduced in #6043](https://github.com/nymtech/nym/pull/6120): Revert dependency updates that broke ANSI escape characters within tracing output
|
||||
|
||||
- [Skip IPv6 metadata endpoint request](https://github.com/nymtech/nym/pull/6118): Skip querying IPv4-only metadata endpoints during IPv6 probing tests
|
||||
|
||||
- [Revert "Propagate cancel token to mixnet client"](https://github.com/nymtech/nym/pull/6115): Reverts earlier change due to premature mixnet exit issues
|
||||
|
||||
- [Retrieve and update ticketbook in the same query](https://github.com/nymtech/nym/pull/6101): Fix concurrency issue with multiple agents retrieving ticketbooks simultaneously
|
||||
|
||||
- [Include network name in default gateway probe config path](https://github.com/nymtech/nym/pull/6100): Prevents reuse of credentials across different networks
|
||||
|
||||
- [Incompatibility fixes](https://github.com/nymtech/nym/pull/6099): Fixes several incompatibilities, including initialization and build mismatches
|
||||
|
||||
- [Testnet manager `02sql` migration](https://github.com/nymtech/nym/pull/6096): Fix invalid FK constraint blocking SQL migration
|
||||
|
||||
- [Use custom topology provider for list of init gateways](https://github.com/nymtech/nym/pull/6092): Fixes SDK bug where clients ignored custom topology provider on registration
|
||||
|
||||
- [Fix `WASM` client + build commands](https://github.com/nymtech/nym/pull/6043): Fixes WASM client hang and runtime time-related issues; improves internal dev testing stability
|
||||
|
||||
### Refactors & Maintenance
|
||||
|
||||
- [Update to no longer use 1mb files](https://github.com/nymtech/nym/pull/6117)
|
||||
|
||||
- [Restore pending DKG contract state migration](https://github.com/nymtech/nym/pull/6116)
|
||||
|
||||
- [Update `dirs` to `6.0`](https://github.com/nymtech/nym/pull/6109): Minor dependency update, safe for compatibility
|
||||
|
||||
## `v2025.18-jarlsberg`
|
||||
|
||||
- [Release Binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2025.18-jarlsberg)
|
||||
|
||||
@@ -21,13 +21,13 @@ This documentation page provides a guide on how to set up and run a [NYM NODE](.
|
||||
```sh
|
||||
nym-node
|
||||
Binary Name: nym-node
|
||||
Build Timestamp: 2025-10-30T12:43:37.933354749Z
|
||||
Build Version: 1.20.0
|
||||
Commit SHA: 75a6d3426bd18dca600ad1cfa39b0a3c4f319c69
|
||||
Commit Date: 2025-10-30T11:59:32.000000000+01:00
|
||||
Commit Branch: HEAD
|
||||
rustc Version: 1.88.0
|
||||
rustc Channel: stable
|
||||
Build Timestamp: 2025-10-15T09:04:32.043934599Z
|
||||
Build Version: 1.19.0
|
||||
Commit SHA: 2235a6e1477bea7368ee5443a298f544deb63504
|
||||
Commit Date: 2025-10-15T10:22:16.000000000+02:00
|
||||
Commit Branch: master
|
||||
rustc Version: 1.92.0-nightly
|
||||
rustc Channel: nightly
|
||||
cargo Profile: release
|
||||
```
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ impl AuthClientMixnetListener {
|
||||
}
|
||||
}
|
||||
|
||||
async fn run(mut self) -> Self {
|
||||
async fn run(mut self) {
|
||||
let mixnet_cancel_token = self.mixnet_client.cancellation_token();
|
||||
self.shutdown_token.run_until_cancelled(async {
|
||||
loop {
|
||||
@@ -95,12 +95,8 @@ impl AuthClientMixnetListener {
|
||||
tracing::debug!("AuthClientMixnetListener is shutting down");
|
||||
}).await;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
// Disconnects the mixnet client and effectively drop itself, since it doesn't work without one, and reconnecting isn't supported
|
||||
pub async fn disconnect_mixnet_client(self) {
|
||||
if !self.mixnet_client.cancellation_token().is_cancelled() {
|
||||
tracing::debug!("AuthClientMixnetListener: Disconnect mixnet client");
|
||||
if !mixnet_cancel_token.is_cancelled() {
|
||||
self.mixnet_client.disconnect().await;
|
||||
}
|
||||
}
|
||||
@@ -128,7 +124,7 @@ pub struct AuthClientMixnetListenerHandle {
|
||||
message_sender: MixnetMessageInputSender,
|
||||
cancellation_token: CancellationToken,
|
||||
mixnet_cancellation_token: CancellationToken,
|
||||
handle: JoinHandle<AuthClientMixnetListener>,
|
||||
handle: JoinHandle<()>,
|
||||
}
|
||||
|
||||
impl AuthClientMixnetListenerHandle {
|
||||
@@ -148,13 +144,8 @@ impl AuthClientMixnetListenerHandle {
|
||||
// If shutdown was externally called, that call is a no-op
|
||||
// If we're only stopping this, it is very much needed
|
||||
self.cancellation_token.cancel();
|
||||
match self.handle.await {
|
||||
Ok(auth_client_mixnet_listener) => {
|
||||
auth_client_mixnet_listener.disconnect_mixnet_client().await;
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Error waiting for auth clients mixnet listener to stop: {e}");
|
||||
}
|
||||
if let Err(e) = self.handle.await {
|
||||
tracing::error!("Error waiting for auth clients mixnet listener to stop: {e}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -765,7 +765,7 @@ async fn connect_exit(
|
||||
);
|
||||
// The IPR supports cancellation, but it's unused in the gateway probe
|
||||
let cancel_token = CancellationToken::new();
|
||||
let mut ipr_client = IprClientConnect::new(mixnet_client, cancel_token).await;
|
||||
let mut ipr_client = IprClientConnect::new(mixnet_client, cancel_token);
|
||||
|
||||
let maybe_ip_pair = ipr_client.connect(exit_router_address).await;
|
||||
let mixnet_client = ipr_client.into_mixnet_client();
|
||||
|
||||
@@ -43,7 +43,7 @@ pub struct IprClientConnect {
|
||||
}
|
||||
|
||||
impl IprClientConnect {
|
||||
pub async fn new(mixnet_client: MixnetClient, cancel_token: CancellationToken) -> Self {
|
||||
pub fn new(mixnet_client: MixnetClient, cancel_token: CancellationToken) -> Self {
|
||||
Self {
|
||||
mixnet_client,
|
||||
connected: ConnectionState::Disconnected,
|
||||
|
||||
+2
-8
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT\n node_id,\n ed25519_identity_pubkey,\n total_stake,\n ip_addresses as \"ip_addresses!: serde_json::Value\",\n mix_port,\n x25519_sphinx_pubkey,\n node_role as \"node_role: serde_json::Value\",\n supported_roles as \"supported_roles: serde_json::Value\",\n entry as \"entry: serde_json::Value\",\n performance,\n self_described as \"self_described: serde_json::Value\",\n bond_info as \"bond_info: serde_json::Value\",\n http_api_port\n FROM\n nym_nodes\n ORDER BY\n node_id\n ",
|
||||
"query": "SELECT\n node_id,\n ed25519_identity_pubkey,\n total_stake,\n ip_addresses as \"ip_addresses!: serde_json::Value\",\n mix_port,\n x25519_sphinx_pubkey,\n node_role as \"node_role: serde_json::Value\",\n supported_roles as \"supported_roles: serde_json::Value\",\n entry as \"entry: serde_json::Value\",\n performance,\n self_described as \"self_described: serde_json::Value\",\n bond_info as \"bond_info: serde_json::Value\"\n FROM\n nym_nodes\n WHERE\n self_described IS NOT NULL\n AND\n bond_info IS NOT NULL\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -62,11 +62,6 @@
|
||||
"ordinal": 11,
|
||||
"name": "bond_info: serde_json::Value",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "http_api_port",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -84,9 +79,8 @@
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "3ddc12cc4e1796b787a50c40560d2bd71d1cfe5f5265e6f161b3122d1317a421"
|
||||
"hash": "283f49a65c7d70bf271702ff6a5c7ad6e68c81932d295ff18ed198c54706a57c"
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO nym_nodes\n (node_id, ed25519_identity_pubkey,\n total_stake,\n ip_addresses, mix_port,\n x25519_sphinx_pubkey, node_role,\n supported_roles, entry,\n self_described,\n bond_info,\n performance, last_updated_utc\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)\n ON CONFLICT(node_id) DO UPDATE SET\n ed25519_identity_pubkey=excluded.ed25519_identity_pubkey,\n ip_addresses=excluded.ip_addresses,\n mix_port=excluded.mix_port,\n x25519_sphinx_pubkey=excluded.x25519_sphinx_pubkey,\n node_role=excluded.node_role,\n supported_roles=excluded.supported_roles,\n entry=excluded.entry,\n self_described=excluded.self_described,\n bond_info=excluded.bond_info,\n performance=excluded.performance,\n last_updated_utc=excluded.last_updated_utc\n ;",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int4",
|
||||
"Varchar",
|
||||
"Int8",
|
||||
"Jsonb",
|
||||
"Int4",
|
||||
"Varchar",
|
||||
"Jsonb",
|
||||
"Jsonb",
|
||||
"Jsonb",
|
||||
"Jsonb",
|
||||
"Jsonb",
|
||||
"Varchar",
|
||||
"Int4"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "b010fb91828f7e4f0b72bdfe3b58b2abb437cccdb6ebd2e1087cc822ed737b0e"
|
||||
}
|
||||
+2
-8
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT\n node_id,\n ed25519_identity_pubkey,\n total_stake,\n ip_addresses as \"ip_addresses!: serde_json::Value\",\n mix_port,\n x25519_sphinx_pubkey,\n node_role as \"node_role: serde_json::Value\",\n supported_roles as \"supported_roles: serde_json::Value\",\n entry as \"entry: serde_json::Value\",\n performance,\n self_described as \"self_described: serde_json::Value\",\n bond_info as \"bond_info: serde_json::Value\",\n http_api_port\n FROM\n nym_nodes\n WHERE\n self_described IS NOT NULL\n AND\n bond_info IS NOT NULL\n ",
|
||||
"query": "SELECT\n node_id,\n ed25519_identity_pubkey,\n total_stake,\n ip_addresses as \"ip_addresses!: serde_json::Value\",\n mix_port,\n x25519_sphinx_pubkey,\n node_role as \"node_role: serde_json::Value\",\n supported_roles as \"supported_roles: serde_json::Value\",\n entry as \"entry: serde_json::Value\",\n performance,\n self_described as \"self_described: serde_json::Value\",\n bond_info as \"bond_info: serde_json::Value\"\n FROM\n nym_nodes\n ORDER BY\n node_id\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -62,11 +62,6 @@
|
||||
"ordinal": 11,
|
||||
"name": "bond_info: serde_json::Value",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "http_api_port",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -84,9 +79,8 @@
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "0b51df277ed66c6553f66af9b135342dee177abc1c92e4a89147de3c22d3d1a5"
|
||||
"hash": "c48d04fc3de59dd484f0a63d40336ced54e08785f77e9ef85f3157d004ec85dc"
|
||||
}
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO nym_nodes\n (node_id, ed25519_identity_pubkey,\n total_stake,\n ip_addresses, mix_port,\n x25519_sphinx_pubkey, node_role,\n supported_roles, entry,\n self_described,\n bond_info,\n performance, last_updated_utc, http_api_port\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)\n ON CONFLICT(node_id) DO UPDATE SET\n ed25519_identity_pubkey=excluded.ed25519_identity_pubkey,\n ip_addresses=excluded.ip_addresses,\n mix_port=excluded.mix_port,\n x25519_sphinx_pubkey=excluded.x25519_sphinx_pubkey,\n node_role=excluded.node_role,\n supported_roles=excluded.supported_roles,\n entry=excluded.entry,\n self_described=excluded.self_described,\n bond_info=excluded.bond_info,\n performance=excluded.performance,\n last_updated_utc=excluded.last_updated_utc,\n http_api_port=excluded.http_api_port\n ;",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int4",
|
||||
"Varchar",
|
||||
"Int8",
|
||||
"Jsonb",
|
||||
"Int4",
|
||||
"Varchar",
|
||||
"Jsonb",
|
||||
"Jsonb",
|
||||
"Jsonb",
|
||||
"Jsonb",
|
||||
"Jsonb",
|
||||
"Varchar",
|
||||
"Int4",
|
||||
"Int4"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "dde9aff827c34086077927bbe33fa3d5c939e7122ba7c88b78a353f00b271ec2"
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-node-status-api"
|
||||
version = "4.0.11-rc1"
|
||||
version = "4.0.10"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
ALTER TABLE nym_nodes
|
||||
ADD COLUMN IF NOT EXISTS http_api_port INTEGER;
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::ticketbook_manager::TicketbookManagerConfig;
|
||||
use clap::{Parser, Subcommand};
|
||||
use clap::Parser;
|
||||
use nym_bin_common::bin_info;
|
||||
use nym_credential_proxy_lib::shared_state::ecash_state::TicketType;
|
||||
use reqwest::Url;
|
||||
@@ -105,19 +105,6 @@ pub(crate) struct Cli {
|
||||
|
||||
#[clap(flatten)]
|
||||
pub(crate) ticketbook: TicketbookArgs,
|
||||
|
||||
#[command(subcommand)]
|
||||
pub(crate) command: Option<Commands>,
|
||||
}
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
pub(crate) enum Commands {
|
||||
/// Scrape a single node and output detailed debug logs
|
||||
ScrapeNode {
|
||||
/// The id of the node to scrape
|
||||
#[arg(long)]
|
||||
node_id: i64,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
|
||||
@@ -381,7 +381,7 @@ impl ScrapeNodeKind {
|
||||
pub(crate) struct ScraperNodeInfo {
|
||||
pub node_kind: ScrapeNodeKind,
|
||||
pub hosts: Vec<String>,
|
||||
pub http_api_port: Option<u16>,
|
||||
pub http_api_port: i64,
|
||||
}
|
||||
|
||||
impl ScraperNodeInfo {
|
||||
@@ -395,21 +395,8 @@ impl ScraperNodeInfo {
|
||||
format!("http://{}", host),
|
||||
]);
|
||||
|
||||
if let Some(custom_http_api_port) = self.http_api_port {
|
||||
urls = Vec::new();
|
||||
for host in &self.hosts {
|
||||
urls.append(&mut vec![format!(
|
||||
"http://{}:{}",
|
||||
host, custom_http_api_port
|
||||
)]);
|
||||
}
|
||||
|
||||
// do not fall back to default ports, if the operator sets a custom http api port
|
||||
// in their bond, use it and error out if it's not available
|
||||
// this will correctly handle cases where some operators run multiple nodes
|
||||
// on a single IP address and assign different custom http port apis at bond time
|
||||
|
||||
// urls.insert(0, format!("http://{}:{}", host, custom_http_api_port));
|
||||
if self.http_api_port != DEFAULT_NYM_NODE_HTTP_PORT as i64 {
|
||||
urls.insert(0, format!("http://{}:{}", host, self.http_api_port));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -436,7 +423,6 @@ pub(crate) struct NymNodeDto {
|
||||
pub performance: String,
|
||||
pub self_described: Option<serde_json::Value>,
|
||||
pub bond_info: Option<serde_json::Value>,
|
||||
pub http_api_port: Option<i32>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)] // it's not dead code but clippy doesn't detect usage in sqlx macros
|
||||
@@ -454,7 +440,6 @@ pub(crate) struct NymNodeInsertRecord {
|
||||
pub entry: Option<serde_json::Value>,
|
||||
pub self_described: Option<serde_json::Value>,
|
||||
pub bond_info: Option<serde_json::Value>,
|
||||
pub http_api_port: Option<i32>,
|
||||
pub last_updated_utc: i64,
|
||||
}
|
||||
|
||||
@@ -471,12 +456,6 @@ impl NymNodeInsertRecord {
|
||||
.map(|info| decimal_to_i64(info.total_stake()))
|
||||
.unwrap_or(0);
|
||||
let entry = serialize_opt_to_value!(skimmed_node.entry)?;
|
||||
let http_api_port = bond_info.and_then(|bond| {
|
||||
bond.bond_information
|
||||
.node
|
||||
.custom_http_port
|
||||
.map(|port| port as i32)
|
||||
});
|
||||
let bond_info = serialize_opt_to_value!(bond_info)?;
|
||||
let self_described = serialize_opt_to_value!(self_described)?;
|
||||
|
||||
@@ -493,7 +472,6 @@ impl NymNodeInsertRecord {
|
||||
entry,
|
||||
self_described,
|
||||
bond_info,
|
||||
http_api_port,
|
||||
last_updated_utc: now,
|
||||
};
|
||||
|
||||
|
||||
@@ -35,8 +35,7 @@ pub(crate) async fn get_all_nym_nodes(pool: &DbPool) -> anyhow::Result<Vec<NymNo
|
||||
entry as "entry: serde_json::Value",
|
||||
performance,
|
||||
self_described as "self_described: serde_json::Value",
|
||||
bond_info as "bond_info: serde_json::Value",
|
||||
http_api_port
|
||||
bond_info as "bond_info: serde_json::Value"
|
||||
FROM
|
||||
nym_nodes
|
||||
ORDER BY
|
||||
@@ -73,8 +72,7 @@ pub(crate) async fn get_described_bonded_nym_nodes(
|
||||
entry as "entry: serde_json::Value",
|
||||
performance,
|
||||
self_described as "self_described: serde_json::Value",
|
||||
bond_info as "bond_info: serde_json::Value",
|
||||
http_api_port
|
||||
bond_info as "bond_info: serde_json::Value"
|
||||
FROM
|
||||
nym_nodes
|
||||
WHERE
|
||||
@@ -117,9 +115,9 @@ pub(crate) async fn update_nym_nodes(
|
||||
supported_roles, entry,
|
||||
self_described,
|
||||
bond_info,
|
||||
performance, last_updated_utc, http_api_port
|
||||
performance, last_updated_utc
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
|
||||
ON CONFLICT(node_id) DO UPDATE SET
|
||||
ed25519_identity_pubkey=excluded.ed25519_identity_pubkey,
|
||||
ip_addresses=excluded.ip_addresses,
|
||||
@@ -131,8 +129,7 @@ pub(crate) async fn update_nym_nodes(
|
||||
self_described=excluded.self_described,
|
||||
bond_info=excluded.bond_info,
|
||||
performance=excluded.performance,
|
||||
last_updated_utc=excluded.last_updated_utc,
|
||||
http_api_port=excluded.http_api_port
|
||||
last_updated_utc=excluded.last_updated_utc
|
||||
;",
|
||||
record.node_id,
|
||||
record.ed25519_identity_pubkey,
|
||||
@@ -147,7 +144,6 @@ pub(crate) async fn update_nym_nodes(
|
||||
record.bond_info,
|
||||
record.performance,
|
||||
record.last_updated_utc as i32,
|
||||
record.http_api_port,
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await
|
||||
|
||||
@@ -21,11 +21,10 @@ pub(crate) async fn get_nodes_for_scraping(pool: &DbPool) -> Result<Vec<ScraperN
|
||||
let skimmed_nodes = queries::get_described_bonded_nym_nodes(pool)
|
||||
.await
|
||||
.map(|nodes_dto| {
|
||||
nodes_dto.into_iter().filter_map(|node_dto| {
|
||||
let node_id = node_dto.node_id;
|
||||
let http_api_port = node_dto.http_api_port;
|
||||
match SkimmedNode::try_from(node_dto) {
|
||||
Ok(node) => Some((node, http_api_port)),
|
||||
nodes_dto.into_iter().filter_map(|node| {
|
||||
let node_id = node.node_id;
|
||||
match SkimmedNode::try_from(node) {
|
||||
Ok(node) => Some(node),
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to decode node_id={}: {}", node_id, e);
|
||||
None
|
||||
@@ -34,7 +33,7 @@ pub(crate) async fn get_nodes_for_scraping(pool: &DbPool) -> Result<Vec<ScraperN
|
||||
})
|
||||
})?;
|
||||
|
||||
skimmed_nodes.for_each(|(node, http_api_port)| {
|
||||
skimmed_nodes.for_each(|node| {
|
||||
// TODO: relies on polyfilling: Nym nodes table might contain legacy mixnodes
|
||||
// as well. Categorize them here.
|
||||
let node_kind = if gateway_keys.contains(&node.ed25519_identity_pubkey.to_base58_string()) {
|
||||
@@ -55,7 +54,7 @@ pub(crate) async fn get_nodes_for_scraping(pool: &DbPool) -> Result<Vec<ScraperN
|
||||
.into_iter()
|
||||
.map(|ip| ip.to_string())
|
||||
.collect::<Vec<_>>(),
|
||||
http_api_port: http_api_port.map(|port| port as u16),
|
||||
http_api_port: node.mix_port.into(),
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
@@ -138,7 +138,6 @@ mod db_tests {
|
||||
performance: "1.0".to_string(),
|
||||
self_described: None,
|
||||
bond_info: None,
|
||||
http_api_port: None,
|
||||
};
|
||||
|
||||
let skimmed_node: nym_validator_client::nym_api::SkimmedNode =
|
||||
@@ -363,42 +362,22 @@ fn test_scraper_node_info_contact_addresses() {
|
||||
let node_info = ScraperNodeInfo {
|
||||
node_kind: ScrapeNodeKind::MixingNymNode { node_id: 123 },
|
||||
hosts: vec!["1.1.1.1".to_string(), "example.com".to_string()],
|
||||
http_api_port: None,
|
||||
http_api_port: 8080,
|
||||
};
|
||||
|
||||
let addresses = node_info.contact_addresses();
|
||||
|
||||
// Should generate multiple URLs for each host
|
||||
// When no custom port is specified only default ports should be used
|
||||
// Custom port (8080) should be inserted at the beginning
|
||||
assert!(addresses.contains(&"http://1.1.1.1:8080".to_string()));
|
||||
assert!(addresses.contains(&"http://example.com:8080".to_string()));
|
||||
assert!(addresses.contains(&"http://1.1.1.1:8000".to_string()));
|
||||
assert!(addresses.contains(&"https://1.1.1.1".to_string()));
|
||||
assert!(addresses.contains(&"http://1.1.1.1".to_string()));
|
||||
assert!(addresses.contains(&"http://example.com:8000".to_string()));
|
||||
// Check that URLs follow the expected pattern
|
||||
assert!(addresses.len() >= 8); // At least 4 URLs per host
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scraper_node_info_contact_addresses_with_custom_http_api_port() {
|
||||
use crate::db::models::{ScrapeNodeKind, ScraperNodeInfo};
|
||||
|
||||
let node_info = ScraperNodeInfo {
|
||||
node_kind: ScrapeNodeKind::MixingNymNode { node_id: 123 },
|
||||
hosts: vec!["1.1.1.1".to_string(), "example.com".to_string()],
|
||||
http_api_port: Some(4444),
|
||||
};
|
||||
|
||||
let addresses = node_info.contact_addresses();
|
||||
|
||||
// Should generate multiple URLs for each host
|
||||
// Custom port (4444) should be the only port in the list
|
||||
assert!(addresses.contains(&"http://1.1.1.1:4444".to_string()));
|
||||
assert!(addresses.contains(&"http://example.com:4444".to_string()));
|
||||
// Check that URLs follow the expected pattern
|
||||
assert!(addresses.len() >= 2); // At least 4 URLs per host
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scrape_node_kind_node_id() {
|
||||
use crate::db::models::ScrapeNodeKind;
|
||||
@@ -435,7 +414,6 @@ fn test_nym_node_dto_with_invalid_keys() {
|
||||
performance: "1.0".to_string(),
|
||||
self_described: None,
|
||||
bond_info: None,
|
||||
http_api_port: None,
|
||||
};
|
||||
|
||||
let result: Result<nym_validator_client::nym_api::SkimmedNode, _> = nym_node_dto.try_into();
|
||||
@@ -473,7 +451,6 @@ fn test_nym_node_dto_with_invalid_performance() {
|
||||
performance: "invalid_percent".to_string(),
|
||||
self_described: None,
|
||||
bond_info: None,
|
||||
http_api_port: None,
|
||||
};
|
||||
|
||||
let result: Result<nym_validator_client::nym_api::SkimmedNode, _> = nym_node_dto.try_into();
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
use crate::cli::Commands;
|
||||
use crate::monitor::DelegationsCache;
|
||||
use crate::node_scraper::helpers::scrape_and_store_description_by_node_id;
|
||||
use crate::ticketbook_manager::TicketbookManager;
|
||||
use crate::ticketbook_manager::state::TicketbookManagerState;
|
||||
use clap::Parser;
|
||||
@@ -42,49 +40,11 @@ async fn main() -> anyhow::Result<()> {
|
||||
tracing::info!("Registered {} agent keys", agent_key_list.len());
|
||||
|
||||
let connection_url = args.database_url.clone();
|
||||
if std::env::var("SHOW_CONFIG").ok().is_some() {
|
||||
tracing::debug!("Using config:\n{:#?}", args);
|
||||
}
|
||||
tracing::debug!("Using config:\n{:#?}", args);
|
||||
|
||||
let storage = db::Storage::init(connection_url, args.sqlx_busy_timeout_s).await?;
|
||||
let db_pool = storage.pool_owned();
|
||||
|
||||
// node geocache is shared between node monitor and HTTP server
|
||||
let geocache = moka::future::Cache::builder()
|
||||
.time_to_live(args.geodata_ttl)
|
||||
.build();
|
||||
let delegations_cache = DelegationsCache::new();
|
||||
|
||||
let client_config = nym_validator_client::nyxd::Config::try_from_nym_network_details(
|
||||
&nym_network_defaults::NymNetworkDetails::new_from_env(),
|
||||
)?;
|
||||
let nyxd_client = NyxdClient::connect(client_config.clone(), args.nyxd_addr.as_str())
|
||||
.map_err(|err| anyhow::anyhow!("Couldn't connect: {}", err))?;
|
||||
|
||||
match args.command {
|
||||
Some(Commands::ScrapeNode { node_id }) => {
|
||||
if std::env::var("RUN_ONCE_INIT_NODES").ok().is_some() {
|
||||
let geocache_clone = geocache.clone();
|
||||
let delegations_cache_clone = Arc::clone(&delegations_cache);
|
||||
monitor::run_once(
|
||||
db_pool.clone(),
|
||||
args.nym_api_client_timeout,
|
||||
nyxd_client,
|
||||
args.ipinfo_api_token,
|
||||
geocache_clone,
|
||||
delegations_cache_clone,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
tracing::info!("Scraping node with id {node_id}...");
|
||||
scrape_and_store_description_by_node_id(&db_pool, node_id).await?;
|
||||
return Ok(());
|
||||
}
|
||||
None => {
|
||||
// default behaviour
|
||||
}
|
||||
}
|
||||
|
||||
// Start the node scraper
|
||||
let scraper = node_scraper::DescriptionScraper::new(storage.pool_owned());
|
||||
shutdown_manager.spawn_with_shutdown(async move {
|
||||
@@ -98,9 +58,20 @@ async fn main() -> anyhow::Result<()> {
|
||||
scraper.start().await;
|
||||
});
|
||||
|
||||
// node geocache is shared between node monitor and HTTP server
|
||||
let geocache = moka::future::Cache::builder()
|
||||
.time_to_live(args.geodata_ttl)
|
||||
.build();
|
||||
let delegations_cache = DelegationsCache::new();
|
||||
|
||||
// Start the monitor
|
||||
let geocache_clone = geocache.clone();
|
||||
let delegations_cache_clone = Arc::clone(&delegations_cache);
|
||||
let client_config = nym_validator_client::nyxd::Config::try_from_nym_network_details(
|
||||
&nym_network_defaults::NymNetworkDetails::new_from_env(),
|
||||
)?;
|
||||
let nyxd_client = NyxdClient::connect(client_config.clone(), args.nyxd_addr.as_str())
|
||||
.map_err(|err| anyhow::anyhow!("Couldn't connect: {}", err))?;
|
||||
|
||||
shutdown_manager.spawn_with_shutdown(async move {
|
||||
monitor::run_in_background(
|
||||
|
||||
@@ -68,7 +68,7 @@ pub(crate) async fn run_in_background(
|
||||
loop {
|
||||
tracing::info!("Refreshing node info...");
|
||||
|
||||
if let Err(e) = monitor.run(false).await {
|
||||
if let Err(e) = monitor.run().await {
|
||||
tracing::error!(
|
||||
"Monitor run failed: {e}, retrying in {}s...",
|
||||
MONITOR_FAILURE_RETRY_DELAY.as_secs()
|
||||
@@ -84,33 +84,8 @@ pub(crate) async fn run_in_background(
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", name = "data_monitor", skip_all)]
|
||||
pub(crate) async fn run_once(
|
||||
db_pool: DbPool,
|
||||
nym_api_client_timeout: Duration,
|
||||
nyxd_client: nym_validator_client::QueryHttpRpcNyxdClient,
|
||||
ipinfo_api_token: String,
|
||||
geocache: NodeGeoCache,
|
||||
node_delegations: Arc<RwLock<DelegationsCache>>,
|
||||
) -> anyhow::Result<()> {
|
||||
let ipinfo = IpInfoClient::new(ipinfo_api_token.clone());
|
||||
|
||||
let mut monitor = Monitor {
|
||||
db_pool,
|
||||
network_details: nym_network_defaults::NymNetworkDetails::new_from_env(),
|
||||
nym_api_client_timeout,
|
||||
nyxd_client,
|
||||
ipinfo,
|
||||
geocache,
|
||||
node_delegations,
|
||||
};
|
||||
|
||||
tracing::info!("Refreshing node info...");
|
||||
monitor.run(true).await
|
||||
}
|
||||
|
||||
impl Monitor {
|
||||
async fn run(&mut self, exit_early: bool) -> anyhow::Result<()> {
|
||||
async fn run(&mut self) -> anyhow::Result<()> {
|
||||
self.check_ipinfo_bandwidth().await;
|
||||
|
||||
let default_api_url = self
|
||||
@@ -178,11 +153,6 @@ impl Monitor {
|
||||
tracing::debug!("{} nym nodes written to DB!", inserted);
|
||||
})?;
|
||||
|
||||
// stop here if running once
|
||||
if exit_early {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// refresh geodata for all nodes
|
||||
for node_description in described_nodes.values() {
|
||||
self.location_cached(node_description).await;
|
||||
|
||||
@@ -118,17 +118,6 @@ pub fn sanitize_description(
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn scrape_and_store_description_by_node_id(pool: &DbPool, node_id: i64) -> Result<()> {
|
||||
let nodes = crate::db::queries::get_nodes_for_scraping(pool).await?;
|
||||
match nodes.iter().find(|n| *n.node_kind.node_id() == node_id) {
|
||||
Some(node) => Ok(scrape_and_store_description(pool, node.clone()).await?),
|
||||
None => {
|
||||
error!("Could not find node with id {node_id}");
|
||||
Err(anyhow!("Could not find node with id {node_id}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn scrape_and_store_description(pool: &DbPool, node: ScraperNodeInfo) -> Result<()> {
|
||||
let client = build_client()?;
|
||||
let urls = node.contact_addresses();
|
||||
@@ -163,13 +152,7 @@ pub async fn scrape_and_store_description(pool: &DbPool, node: ScraperNodeInfo)
|
||||
anyhow::anyhow!("Failed to fetch description from any URL: {}", err_msg)
|
||||
})?;
|
||||
|
||||
let sanitized_description = sanitize_description(description.clone(), *node.node_id());
|
||||
|
||||
trace!("tried_url_list = {tried_url_list:?}");
|
||||
trace!("ndoe_id = {}", node.node_id());
|
||||
trace!("description = {:?}", description);
|
||||
trace!("sanitized_description = {:?}", sanitized_description);
|
||||
|
||||
let sanitized_description = sanitize_description(description, *node.node_id());
|
||||
insert_scraped_node_description(pool, &node.node_kind, &sanitized_description).await?;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -9,6 +9,7 @@ use nym_credentials_interface::TicketType;
|
||||
use nym_ip_packet_client::IprClientConnect;
|
||||
use nym_registration_common::AssignedAddresses;
|
||||
use nym_sdk::mixnet::{EventReceiver, MixnetClient, Recipient};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::config::RegistrationClientConfig;
|
||||
|
||||
@@ -34,23 +35,49 @@ pub struct RegistrationClient {
|
||||
event_rx: EventReceiver,
|
||||
}
|
||||
|
||||
// Bundle of an actual error and the underlying mixnet client so it can be shutdown correctly if needed
|
||||
struct RegistrationError {
|
||||
mixnet_client: Option<MixnetClient>,
|
||||
source: crate::RegistrationClientError,
|
||||
}
|
||||
|
||||
impl RegistrationClient {
|
||||
async fn register_mix_exit(self) -> Result<RegistrationResult, RegistrationClientError> {
|
||||
async fn register_mix_exit(self) -> Result<RegistrationResult, RegistrationError> {
|
||||
let entry_mixnet_gateway_ip = self.config.entry.node.ip_address;
|
||||
|
||||
let exit_mixnet_gateway_ip = self.config.exit.node.ip_address;
|
||||
|
||||
let ipr_address = self.config.exit.node.ipr_address.ok_or(
|
||||
RegistrationClientError::NoIpPacketRouterAddress {
|
||||
node_id: self.config.exit.node.identity.to_base58_string(),
|
||||
},
|
||||
)?;
|
||||
let Some(ipr_address) = self.config.exit.node.ipr_address else {
|
||||
return Err(RegistrationError {
|
||||
mixnet_client: Some(self.mixnet_client),
|
||||
source: RegistrationClientError::NoIpPacketRouterAddress {
|
||||
node_id: self.config.exit.node.identity.to_base58_string(),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
let mut ipr_client =
|
||||
IprClientConnect::new(self.mixnet_client, self.cancel_token.clone()).await;
|
||||
let interface_addresses = ipr_client
|
||||
.connect(ipr_address)
|
||||
IprClientConnect::new(self.mixnet_client, self.cancel_token.child_token());
|
||||
|
||||
let interface_addresses = match self
|
||||
.cancel_token
|
||||
.run_until_cancelled(ipr_client.connect(ipr_address))
|
||||
.await
|
||||
.map_err(RegistrationClientError::ConnectToIpPacketRouter)?;
|
||||
{
|
||||
Some(Ok(addr)) => addr,
|
||||
Some(Err(e)) => {
|
||||
return Err(RegistrationError {
|
||||
mixnet_client: Some(ipr_client.into_mixnet_client()),
|
||||
source: RegistrationClientError::ConnectToIpPacketRouter(e),
|
||||
});
|
||||
}
|
||||
None => {
|
||||
return Err(RegistrationError {
|
||||
mixnet_client: Some(ipr_client.into_mixnet_client()),
|
||||
source: RegistrationClientError::Cancelled,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Ok(RegistrationResult::Mixnet(Box::new(
|
||||
MixnetRegistrationResult {
|
||||
@@ -67,18 +94,24 @@ impl RegistrationClient {
|
||||
)))
|
||||
}
|
||||
|
||||
async fn register_wg(self) -> Result<RegistrationResult, RegistrationClientError> {
|
||||
let entry_auth_address = self.config.entry.node.authenticator_address.ok_or(
|
||||
RegistrationClientError::AuthenticationNotPossible {
|
||||
node_id: self.config.entry.node.identity.to_base58_string(),
|
||||
},
|
||||
)?;
|
||||
async fn register_wg(self) -> Result<RegistrationResult, RegistrationError> {
|
||||
let Some(entry_auth_address) = self.config.entry.node.authenticator_address else {
|
||||
return Err(RegistrationError {
|
||||
mixnet_client: Some(self.mixnet_client),
|
||||
source: RegistrationClientError::AuthenticationNotPossible {
|
||||
node_id: self.config.entry.node.identity.to_base58_string(),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
let exit_auth_address = self.config.exit.node.authenticator_address.ok_or(
|
||||
RegistrationClientError::AuthenticationNotPossible {
|
||||
node_id: self.config.exit.node.identity.to_base58_string(),
|
||||
},
|
||||
)?;
|
||||
let Some(exit_auth_address) = self.config.exit.node.authenticator_address else {
|
||||
return Err(RegistrationError {
|
||||
mixnet_client: Some(self.mixnet_client),
|
||||
source: RegistrationClientError::AuthenticationNotPossible {
|
||||
node_id: self.config.exit.node.identity.to_base58_string(),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
let entry_version = self.config.entry.node.version;
|
||||
tracing::debug!("Entry gateway version: {entry_version}");
|
||||
@@ -87,8 +120,10 @@ impl RegistrationClient {
|
||||
|
||||
// Start the auth client mixnet listener, which will listen for incoming messages from the
|
||||
// mixnet and rebroadcast them to the auth clients.
|
||||
// From this point on, we don't need to care about the mixnet client anymore
|
||||
let mixnet_listener =
|
||||
AuthClientMixnetListener::new(self.mixnet_client, self.cancel_token.clone()).start();
|
||||
AuthClientMixnetListener::new(self.mixnet_client, self.cancel_token.child_token())
|
||||
.start();
|
||||
|
||||
let mut entry_auth_client = AuthenticatorClient::new(
|
||||
mixnet_listener.subscribe(),
|
||||
@@ -115,24 +150,33 @@ impl RegistrationClient {
|
||||
let exit_fut = exit_auth_client
|
||||
.register_wireguard(&*self.bandwidth_controller, TicketType::V1WireguardExit);
|
||||
|
||||
let (entry, exit) = Box::pin(async { tokio::join!(entry_fut, exit_fut) }).await;
|
||||
let (entry, exit) = Box::pin(
|
||||
self.cancel_token
|
||||
.run_until_cancelled(async { tokio::join!(entry_fut, exit_fut) }),
|
||||
)
|
||||
.await
|
||||
.ok_or(RegistrationError {
|
||||
mixnet_client: None,
|
||||
source: RegistrationClientError::Cancelled,
|
||||
})?;
|
||||
|
||||
let entry =
|
||||
entry.map_err(
|
||||
|source| RegistrationClientError::EntryGatewayRegisterWireguard {
|
||||
gateway_id: self.config.entry.node.identity.to_base58_string(),
|
||||
authenticator_address: Box::new(entry_auth_address),
|
||||
source: Box::new(source),
|
||||
},
|
||||
)?;
|
||||
let exit =
|
||||
exit.map_err(
|
||||
|source| RegistrationClientError::ExitGatewayRegisterWireguard {
|
||||
gateway_id: self.config.exit.node.identity.to_base58_string(),
|
||||
authenticator_address: Box::new(exit_auth_address),
|
||||
source: Box::new(source),
|
||||
},
|
||||
)?;
|
||||
let entry = entry.map_err(|source| RegistrationError {
|
||||
mixnet_client: None,
|
||||
source: RegistrationClientError::EntryGatewayRegisterWireguard {
|
||||
gateway_id: self.config.entry.node.identity.to_base58_string(),
|
||||
authenticator_address: Box::new(entry_auth_address),
|
||||
source: Box::new(source),
|
||||
},
|
||||
})?;
|
||||
|
||||
let exit = exit.map_err(|source| RegistrationError {
|
||||
mixnet_client: None,
|
||||
source: RegistrationClientError::EntryGatewayRegisterWireguard {
|
||||
gateway_id: self.config.exit.node.identity.to_base58_string(),
|
||||
authenticator_address: Box::new(exit_auth_address),
|
||||
source: Box::new(source),
|
||||
},
|
||||
})?;
|
||||
|
||||
Ok(RegistrationResult::Wireguard(Box::new(
|
||||
WireguardRegistrationResult {
|
||||
@@ -147,16 +191,23 @@ impl RegistrationClient {
|
||||
}
|
||||
|
||||
pub async fn register(self) -> Result<RegistrationResult, RegistrationClientError> {
|
||||
self.cancel_token
|
||||
.clone()
|
||||
.run_until_cancelled(async {
|
||||
if self.config.two_hops {
|
||||
self.register_wg().await
|
||||
} else {
|
||||
self.register_mix_exit().await
|
||||
let registration_result = if self.config.two_hops {
|
||||
self.register_wg().await
|
||||
} else {
|
||||
self.register_mix_exit().await
|
||||
};
|
||||
|
||||
// If we failed to register, and we were the owner of the mixnet client, shut it down
|
||||
match registration_result {
|
||||
Ok(result) => Ok(result),
|
||||
Err(error) => {
|
||||
debug!("Registration failed");
|
||||
if let Some(mixnet_client) = error.mixnet_client {
|
||||
debug!("Shutting down mixnet client");
|
||||
mixnet_client.disconnect().await;
|
||||
}
|
||||
})
|
||||
.await
|
||||
.ok_or(RegistrationClientError::Cancelled)?
|
||||
Err(error.source)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user