Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9fdf5273e3 | |||
| 8b1490cb99 | |||
| 5ab164d229 | |||
| 26538c5884 | |||
| c202e2d598 | |||
| a7874add88 | |||
| 3d84be22e2 | |||
| 25766dc0ec | |||
| 676e93a372 | |||
| 5a6770e5e2 | |||
| 529e8d49ee | |||
| a94c035c0a | |||
| 24480418f0 | |||
| a46245ffe3 | |||
| 7c1c13e139 | |||
| 836a93cd96 | |||
| b47a742dd0 | |||
| 6e14882246 | |||
| a7466a0e02 | |||
| 78f45012db | |||
| f6a2f62ea9 | |||
| 3efeededc5 | |||
| c482350ec6 | |||
| f7a7a8072f | |||
| acd068e5ab | |||
| 8d5a41a790 | |||
| caa17d933c | |||
| 039b05cf7e | |||
| 37b10b59aa | |||
| a9ede22bbd | |||
| b656003306 | |||
| b4f51baf94 | |||
| a3f3d83c1b | |||
| 84d7004cb2 | |||
| be063a36eb | |||
| 0a712b9fce | |||
| 88d6fb4e22 | |||
| 04c2045d94 |
@@ -30,7 +30,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ arc-ubuntu-20.04, custom-runner-mac-m1 ]
|
os: [ arc-ubuntu-20.04, custom-windows-11, custom-runner-mac-m1 ]
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
|
|||||||
+3
-1
@@ -51,4 +51,6 @@ ppa-private-key.b64
|
|||||||
ppa-private-key.asc
|
ppa-private-key.asc
|
||||||
nym-network-monitor/topology.json
|
nym-network-monitor/topology.json
|
||||||
nym-network-monitor/__pycache__
|
nym-network-monitor/__pycache__
|
||||||
nym-network-monitor/*.key
|
nym-network-monitor/*.key
|
||||||
|
nym-network-monitor/.envrc
|
||||||
|
nym-network-monitor/.envrc
|
||||||
|
|||||||
@@ -4,6 +4,91 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [2025.1-reeses] (2025-01-15)
|
||||||
|
|
||||||
|
- Feture/legacy alert ([#5346])
|
||||||
|
- chore: readjusted --mode behaviour to fix the regression ([#5331])
|
||||||
|
- chore: apply 1.84 linter suggestions ([#5330])
|
||||||
|
- bugfix: make sure refresh data key matches bond info ([#5329])
|
||||||
|
- reduce log severity for number of packets being delayed ([#5321])
|
||||||
|
- feat: warn users if node is run in exit mode only ([#5320])
|
||||||
|
- Bugfix/contract version assignment ([#5318])
|
||||||
|
- fixed client session histogram buckets ([#5316])
|
||||||
|
- amend 250gb limit ([#5313])
|
||||||
|
- feature: expand nym-node prometheus metrics ([#5298])
|
||||||
|
- Cherry picked #5286 ([#5287])
|
||||||
|
- Add close to credential storage ([#5283])
|
||||||
|
- feature: wireguard metrics ([#5278])
|
||||||
|
- Add PATCH support to nym-http-api-client ([#5260])
|
||||||
|
- chore: removed legacy socks5 listener ([#5259])
|
||||||
|
- bugfix: make sure to apply gateway score filtering when choosing initial node ([#5256])
|
||||||
|
- Update TS bindings ([#5255])
|
||||||
|
- Add conversion unit tests for auth msg ([#5251])
|
||||||
|
- Add control messages to GatewayTransciver ([#5247])
|
||||||
|
- Remove unneeded async function annotation ([#5246])
|
||||||
|
- bugfix: make sure to update timestamp of last batch verification to prevent double redemption ([#5239])
|
||||||
|
- Add FromStr impl for UserAgent ([#5236])
|
||||||
|
- Extend swagger docs ([#5235])
|
||||||
|
- TicketType derive Hash and Eq ([#5233])
|
||||||
|
- Add fd callback to client core ([#5230])
|
||||||
|
- Extend raw ws fd for gateway client ([#5218])
|
||||||
|
- Shipping raw metrics to PG ([#5216])
|
||||||
|
- Change sqlite journal mode to WAL ([#5213])
|
||||||
|
- Derive serialize for UserAgent ([#5210])
|
||||||
|
- Restore Location fields ([#5208])
|
||||||
|
- better date serialization ([#5207])
|
||||||
|
- Fix overflow ([#5204])
|
||||||
|
- feature: hopefully final steps of the smoosh™️ ([#5201])
|
||||||
|
- Fix overflow ([#5184])
|
||||||
|
- NS API - Gateway stats scraping ([#5180])
|
||||||
|
- introduced initial internal commands for nym-cli: ecash key and request generation ([#5174])
|
||||||
|
- Move NS client to separate package under NS API ([#5171])
|
||||||
|
- build(deps): bump micromatch from 4.0.4 to 4.0.8 in /testnet-faucet ([#4813])
|
||||||
|
|
||||||
|
[#5346]: https://github.com/nymtech/nym/pull/5346
|
||||||
|
[#5331]: https://github.com/nymtech/nym/pull/5331
|
||||||
|
[#5330]: https://github.com/nymtech/nym/pull/5330
|
||||||
|
[#5329]: https://github.com/nymtech/nym/pull/5329
|
||||||
|
[#5321]: https://github.com/nymtech/nym/pull/5321
|
||||||
|
[#5320]: https://github.com/nymtech/nym/pull/5320
|
||||||
|
[#5318]: https://github.com/nymtech/nym/pull/5318
|
||||||
|
[#5316]: https://github.com/nymtech/nym/pull/5316
|
||||||
|
[#5313]: https://github.com/nymtech/nym/pull/5313
|
||||||
|
[#5298]: https://github.com/nymtech/nym/pull/5298
|
||||||
|
[#5287]: https://github.com/nymtech/nym/pull/5287
|
||||||
|
[#5283]: https://github.com/nymtech/nym/pull/5283
|
||||||
|
[#5278]: https://github.com/nymtech/nym/pull/5278
|
||||||
|
[#5260]: https://github.com/nymtech/nym/pull/5260
|
||||||
|
[#5259]: https://github.com/nymtech/nym/pull/5259
|
||||||
|
[#5256]: https://github.com/nymtech/nym/pull/5256
|
||||||
|
[#5255]: https://github.com/nymtech/nym/pull/5255
|
||||||
|
[#5251]: https://github.com/nymtech/nym/pull/5251
|
||||||
|
[#5247]: https://github.com/nymtech/nym/pull/5247
|
||||||
|
[#5246]: https://github.com/nymtech/nym/pull/5246
|
||||||
|
[#5239]: https://github.com/nymtech/nym/pull/5239
|
||||||
|
[#5236]: https://github.com/nymtech/nym/pull/5236
|
||||||
|
[#5235]: https://github.com/nymtech/nym/pull/5235
|
||||||
|
[#5233]: https://github.com/nymtech/nym/pull/5233
|
||||||
|
[#5230]: https://github.com/nymtech/nym/pull/5230
|
||||||
|
[#5218]: https://github.com/nymtech/nym/pull/5218
|
||||||
|
[#5216]: https://github.com/nymtech/nym/pull/5216
|
||||||
|
[#5213]: https://github.com/nymtech/nym/pull/5213
|
||||||
|
[#5210]: https://github.com/nymtech/nym/pull/5210
|
||||||
|
[#5208]: https://github.com/nymtech/nym/pull/5208
|
||||||
|
[#5207]: https://github.com/nymtech/nym/pull/5207
|
||||||
|
[#5204]: https://github.com/nymtech/nym/pull/5204
|
||||||
|
[#5201]: https://github.com/nymtech/nym/pull/5201
|
||||||
|
[#5184]: https://github.com/nymtech/nym/pull/5184
|
||||||
|
[#5180]: https://github.com/nymtech/nym/pull/5180
|
||||||
|
[#5174]: https://github.com/nymtech/nym/pull/5174
|
||||||
|
[#5171]: https://github.com/nymtech/nym/pull/5171
|
||||||
|
[#4813]: https://github.com/nymtech/nym/pull/4813
|
||||||
|
|
||||||
|
## [2024.14-crunch-patched] (2024-12-17)
|
||||||
|
|
||||||
|
- Fixes an issue to allow previously registred clients to connect to latest nym-nodes
|
||||||
|
- Fixes compatibility issues between nym-nodes and older clients
|
||||||
|
|
||||||
## [2024.14-crunch] (2024-12-11)
|
## [2024.14-crunch] (2024-12-11)
|
||||||
|
|
||||||
- Merge/release/2024.14-crunch ([#5242])
|
- Merge/release/2024.14-crunch ([#5242])
|
||||||
|
|||||||
Generated
+122
-35
@@ -2416,7 +2416,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "explorer-api"
|
name = "explorer-api"
|
||||||
version = "1.1.43"
|
version = "1.1.44"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap 4.5.20",
|
"clap 4.5.20",
|
||||||
@@ -2476,10 +2476,16 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fancy_constructor"
|
name = "fallible-iterator"
|
||||||
version = "1.2.2"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f71f317e4af73b2f8f608fac190c52eac4b1879d2145df1db2fe48881ca69435"
|
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fancy_constructor"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "07b19d0e43eae2bfbafe4931b5e79c73fb1a849ca15cd41a761a7b8587f9a1a2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"macroific",
|
"macroific",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@@ -2840,15 +2846,15 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gloo-net"
|
name = "gloo-net"
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173"
|
checksum = "c06f627b1a58ca3d42b45d6104bf1e1a03799df472df00988b6ba21accc10580"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"gloo-utils 0.2.0",
|
"gloo-utils 0.2.0",
|
||||||
"http 0.2.12",
|
"http 1.1.0",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"pin-project",
|
"pin-project",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -3426,7 +3432,8 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "indexed_db_futures"
|
name = "indexed_db_futures"
|
||||||
version = "0.4.2"
|
version = "0.4.2"
|
||||||
source = "git+https://github.com/TiemenSch/rust-indexed-db?branch=update-uuid#9745d015707008b0c410115d787014a6d1af2efb"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0704b71f13f81b5933d791abf2de26b33c40935143985220299a357721166706"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accessory",
|
"accessory",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
@@ -3664,10 +3671,11 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.72"
|
version = "0.3.76"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9"
|
checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4387,7 +4395,7 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-api"
|
name = "nym-api"
|
||||||
version = "1.1.47"
|
version = "1.1.48"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -4636,7 +4644,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-cli"
|
name = "nym-cli"
|
||||||
version = "1.1.45"
|
version = "1.1.46"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
@@ -4719,7 +4727,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-client"
|
name = "nym-client"
|
||||||
version = "1.1.45"
|
version = "1.1.46"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bs58",
|
"bs58",
|
||||||
"clap 4.5.20",
|
"clap 4.5.20",
|
||||||
@@ -5673,18 +5681,20 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"dashmap",
|
"dashmap",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
|
||||||
"prometheus",
|
"prometheus",
|
||||||
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-mixnet-client"
|
name = "nym-mixnet-client"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"dashmap",
|
||||||
"futures",
|
"futures",
|
||||||
"nym-sphinx",
|
"nym-sphinx",
|
||||||
"nym-task",
|
"nym-task",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-stream",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
@@ -5782,6 +5792,7 @@ dependencies = [
|
|||||||
"nym-bin-common",
|
"nym-bin-common",
|
||||||
"nym-client-core",
|
"nym-client-core",
|
||||||
"nym-crypto",
|
"nym-crypto",
|
||||||
|
"nym-gateway-requests",
|
||||||
"nym-network-defaults",
|
"nym-network-defaults",
|
||||||
"nym-sdk",
|
"nym-sdk",
|
||||||
"nym-sphinx",
|
"nym-sphinx",
|
||||||
@@ -5795,6 +5806,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-postgres",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"utoipa",
|
"utoipa",
|
||||||
"utoipa-swagger-ui",
|
"utoipa-swagger-ui",
|
||||||
@@ -5802,7 +5814,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-network-requester"
|
name = "nym-network-requester"
|
||||||
version = "1.1.46"
|
version = "1.1.47"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"addr",
|
"addr",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@@ -5853,7 +5865,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-node"
|
name = "nym-node"
|
||||||
version = "1.2.0"
|
version = "1.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -6225,7 +6237,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-socks5-client"
|
name = "nym-socks5-client"
|
||||||
version = "1.1.45"
|
version = "1.1.46"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bs58",
|
"bs58",
|
||||||
"clap 4.5.20",
|
"clap 4.5.20",
|
||||||
@@ -6799,6 +6811,7 @@ dependencies = [
|
|||||||
"nym-crypto",
|
"nym-crypto",
|
||||||
"nym-gateway-storage",
|
"nym-gateway-storage",
|
||||||
"nym-network-defaults",
|
"nym-network-defaults",
|
||||||
|
"nym-node-metrics",
|
||||||
"nym-task",
|
"nym-task",
|
||||||
"nym-wireguard-types",
|
"nym-wireguard-types",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
@@ -6826,7 +6839,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nymvisor"
|
name = "nymvisor"
|
||||||
version = "0.1.10"
|
version = "0.1.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -7250,6 +7263,24 @@ dependencies = [
|
|||||||
"indexmap 2.2.6",
|
"indexmap 2.2.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf"
|
||||||
|
version = "0.11.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
||||||
|
dependencies = [
|
||||||
|
"phf_shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf_shared"
|
||||||
|
version = "0.11.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
|
||||||
|
dependencies = [
|
||||||
|
"siphasher 0.3.11",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project"
|
name = "pin-project"
|
||||||
version = "1.1.6"
|
version = "1.1.6"
|
||||||
@@ -7388,6 +7419,35 @@ version = "1.6.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
|
checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "postgres-protocol"
|
||||||
|
version = "0.6.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "acda0ebdebc28befa84bee35e651e4c5f09073d668c7aed4cf7e23c3cda84b23"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.22.1",
|
||||||
|
"byteorder",
|
||||||
|
"bytes",
|
||||||
|
"fallible-iterator",
|
||||||
|
"hmac",
|
||||||
|
"md-5",
|
||||||
|
"memchr",
|
||||||
|
"rand",
|
||||||
|
"sha2 0.10.8",
|
||||||
|
"stringprep",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "postgres-types"
|
||||||
|
version = "0.2.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f66ea23a2d0e5734297357705193335e0a957696f34bed2f2faefacb2fec336f"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"fallible-iterator",
|
||||||
|
"postgres-protocol",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "powerfmt"
|
name = "powerfmt"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@@ -9722,6 +9782,32 @@ dependencies = [
|
|||||||
"syn 2.0.90",
|
"syn 2.0.90",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-postgres"
|
||||||
|
version = "0.7.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b5d3742945bc7d7f210693b0c58ae542c6fd47b17adbbda0885f3dcb34a6bdb"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"byteorder",
|
||||||
|
"bytes",
|
||||||
|
"fallible-iterator",
|
||||||
|
"futures-channel",
|
||||||
|
"futures-util",
|
||||||
|
"log",
|
||||||
|
"parking_lot",
|
||||||
|
"percent-encoding",
|
||||||
|
"phf",
|
||||||
|
"pin-project-lite",
|
||||||
|
"postgres-protocol",
|
||||||
|
"postgres-types",
|
||||||
|
"rand",
|
||||||
|
"socket2",
|
||||||
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
|
"whoami",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-rustls"
|
name = "tokio-rustls"
|
||||||
version = "0.24.1"
|
version = "0.24.1"
|
||||||
@@ -10678,9 +10764,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.95"
|
version = "0.2.99"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e"
|
checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@@ -10689,13 +10775,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-backend"
|
name = "wasm-bindgen-backend"
|
||||||
version = "0.2.95"
|
version = "0.2.99"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358"
|
checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"log",
|
"log",
|
||||||
"once_cell",
|
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.90",
|
"syn 2.0.90",
|
||||||
@@ -10704,21 +10789,22 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-futures"
|
name = "wasm-bindgen-futures"
|
||||||
version = "0.4.45"
|
version = "0.4.49"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b"
|
checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
|
"once_cell",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro"
|
name = "wasm-bindgen-macro"
|
||||||
version = "0.2.95"
|
version = "0.2.99"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56"
|
checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"wasm-bindgen-macro-support",
|
"wasm-bindgen-macro-support",
|
||||||
@@ -10726,9 +10812,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro-support"
|
name = "wasm-bindgen-macro-support"
|
||||||
version = "0.2.95"
|
version = "0.2.99"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
|
checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -10739,9 +10825,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-shared"
|
name = "wasm-bindgen-shared"
|
||||||
version = "0.2.95"
|
version = "0.2.99"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d"
|
checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-test"
|
name = "wasm-bindgen-test"
|
||||||
@@ -10849,9 +10935,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasmtimer"
|
name = "wasmtimer"
|
||||||
version = "0.2.0"
|
version = "0.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5f656cd8858a5164932d8a90f936700860976ec21eb00e0fe2aa8cab13f6b4cf"
|
checksum = "0048ad49a55b9deb3953841fa1fc5858f0efbcb7a18868c899a360269fac1b23"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures",
|
"futures",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
@@ -10863,9 +10949,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "web-sys"
|
name = "web-sys"
|
||||||
version = "0.3.72"
|
version = "0.3.76"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112"
|
checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
@@ -10912,6 +10998,7 @@ checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"redox_syscall 0.5.1",
|
"redox_syscall 0.5.1",
|
||||||
"wasite",
|
"wasite",
|
||||||
|
"web-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
+8
-10
@@ -394,19 +394,17 @@ prost = { version = "0.12", default-features = false }
|
|||||||
|
|
||||||
# wasm-related dependencies
|
# wasm-related dependencies
|
||||||
gloo-utils = "0.2.0"
|
gloo-utils = "0.2.0"
|
||||||
gloo-net = "0.5.0"
|
gloo-net = "0.6.0"
|
||||||
|
|
||||||
# use a separate branch due to feature unification failures
|
# TODO: migrate to 0.6+
|
||||||
# this is blocked until the upstream removes outdates `wasm_bindgen` feature usage
|
indexed_db_futures = "0.4.2"
|
||||||
# indexed_db_futures = "0.4.1"
|
js-sys = "0.3.76"
|
||||||
indexed_db_futures = { git = "https://github.com/TiemenSch/rust-indexed-db", branch = "update-uuid" }
|
|
||||||
js-sys = "0.3.70"
|
|
||||||
serde-wasm-bindgen = "0.6.5"
|
serde-wasm-bindgen = "0.6.5"
|
||||||
tsify = "0.4.5"
|
tsify = "0.4.5"
|
||||||
wasm-bindgen = "0.2.95"
|
wasm-bindgen = "0.2.99"
|
||||||
wasm-bindgen-futures = "0.4.45"
|
wasm-bindgen-futures = "0.4.49"
|
||||||
wasmtimer = "0.2.0"
|
wasmtimer = "0.4.1"
|
||||||
web-sys = "0.3.72"
|
web-sys = "0.3.76"
|
||||||
|
|
||||||
# Profile settings for individual crates
|
# Profile settings for individual crates
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nym-client"
|
name = "nym-client"
|
||||||
version = "1.1.45"
|
version = "1.1.46"
|
||||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||||
description = "Implementation of the Nym Client"
|
description = "Implementation of the Nym Client"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nym-socks5-client"
|
name = "nym-socks5-client"
|
||||||
version = "1.1.45"
|
version = "1.1.46"
|
||||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||||
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ pub type HmacSha256 = Hmac<Sha256>;
|
|||||||
pub type Nonce = u64;
|
pub type Nonce = u64;
|
||||||
pub type Taken = Option<SystemTime>;
|
pub type Taken = Option<SystemTime>;
|
||||||
|
|
||||||
pub const BANDWIDTH_CAP_PER_DAY: u64 = 1024 * 1024 * 1024; // 1 GB
|
pub const BANDWIDTH_CAP_PER_DAY: u64 = 250 * 1024 * 1024 * 1024; // 250 GB
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
pub struct IpPair {
|
pub struct IpPair {
|
||||||
|
|||||||
@@ -8,7 +8,10 @@ use crate::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
use log::{debug, error};
|
use log::{debug, error};
|
||||||
use sqlx::ConnectOptions;
|
use sqlx::{
|
||||||
|
sqlite::{SqliteAutoVacuum, SqliteSynchronous},
|
||||||
|
ConnectOptions,
|
||||||
|
};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -30,6 +33,9 @@ impl StorageManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let opts = sqlx::sqlite::SqliteConnectOptions::new()
|
let opts = sqlx::sqlite::SqliteConnectOptions::new()
|
||||||
|
.journal_mode(sqlx::sqlite::SqliteJournalMode::Wal)
|
||||||
|
.synchronous(SqliteSynchronous::Normal)
|
||||||
|
.auto_vacuum(SqliteAutoVacuum::Incremental)
|
||||||
.filename(database_path)
|
.filename(database_path)
|
||||||
.create_if_missing(true)
|
.create_if_missing(true)
|
||||||
.disable_statement_logging();
|
.disable_statement_logging();
|
||||||
@@ -110,7 +116,7 @@ impl StorageManager {
|
|||||||
) -> Result<(), sqlx::Error> {
|
) -> Result<(), sqlx::Error> {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO registered_gateway(gateway_id_bs58, registration_timestamp, gateway_type)
|
INSERT INTO registered_gateway(gateway_id_bs58, registration_timestamp, gateway_type)
|
||||||
VALUES (?, ?, ?)
|
VALUES (?, ?, ?)
|
||||||
"#,
|
"#,
|
||||||
registered_gateway.gateway_id_bs58,
|
registered_gateway.gateway_id_bs58,
|
||||||
@@ -224,7 +230,7 @@ impl StorageManager {
|
|||||||
) -> Result<(), sqlx::Error> {
|
) -> Result<(), sqlx::Error> {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO custom_gateway_details(gateway_id_bs58, data)
|
INSERT INTO custom_gateway_details(gateway_id_bs58, data)
|
||||||
VALUES (?, ?)
|
VALUES (?, ?)
|
||||||
"#,
|
"#,
|
||||||
custom.gateway_id_bs58,
|
custom.gateway_id_bs58,
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ use crate::init::{
|
|||||||
setup_gateway,
|
setup_gateway,
|
||||||
types::{GatewaySetup, InitialisationResult},
|
types::{GatewaySetup, InitialisationResult},
|
||||||
};
|
};
|
||||||
use crate::{config, spawn_future};
|
use crate::{config, spawn_future, ForgetMe};
|
||||||
use futures::channel::mpsc;
|
use futures::channel::mpsc;
|
||||||
use log::*;
|
use log::*;
|
||||||
use nym_bandwidth_controller::BandwidthController;
|
use nym_bandwidth_controller::BandwidthController;
|
||||||
@@ -188,6 +188,11 @@ pub struct BaseClientBuilder<'a, C, S: MixnetClientStorage> {
|
|||||||
user_agent: Option<UserAgent>,
|
user_agent: Option<UserAgent>,
|
||||||
|
|
||||||
setup_method: GatewaySetup,
|
setup_method: GatewaySetup,
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
|
||||||
|
|
||||||
|
forget_me: ForgetMe,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C, S> BaseClientBuilder<'a, C, S>
|
impl<'a, C, S> BaseClientBuilder<'a, C, S>
|
||||||
@@ -210,9 +215,18 @@ where
|
|||||||
shutdown: None,
|
shutdown: None,
|
||||||
user_agent: None,
|
user_agent: None,
|
||||||
setup_method: GatewaySetup::MustLoad { gateway_id: None },
|
setup_method: GatewaySetup::MustLoad { gateway_id: None },
|
||||||
|
#[cfg(unix)]
|
||||||
|
connection_fd_callback: None,
|
||||||
|
forget_me: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_forget_me(mut self, forget_me: &ForgetMe) -> Self {
|
||||||
|
self.forget_me = forget_me.clone();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn with_gateway_setup(mut self, setup: GatewaySetup) -> Self {
|
pub fn with_gateway_setup(mut self, setup: GatewaySetup) -> Self {
|
||||||
self.setup_method = setup;
|
self.setup_method = setup;
|
||||||
@@ -261,6 +275,15 @@ where
|
|||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
pub fn with_connection_fd_callback(
|
||||||
|
mut self,
|
||||||
|
callback: Arc<dyn Fn(RawFd) + Send + Sync>,
|
||||||
|
) -> Self {
|
||||||
|
self.connection_fd_callback = Some(callback);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
// note: do **NOT** make this method public as its only valid usage is from within `start_base`
|
// note: do **NOT** make this method public as its only valid usage is from within `start_base`
|
||||||
// because it relies on the crypto keys being already loaded
|
// because it relies on the crypto keys being already loaded
|
||||||
fn mix_address(details: &InitialisationResult) -> Recipient {
|
fn mix_address(details: &InitialisationResult) -> Recipient {
|
||||||
@@ -352,6 +375,7 @@ where
|
|||||||
controller.start_with_shutdown(shutdown)
|
controller.start_with_shutdown(shutdown)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
async fn start_gateway_client(
|
async fn start_gateway_client(
|
||||||
config: &Config,
|
config: &Config,
|
||||||
initialisation_result: InitialisationResult,
|
initialisation_result: InitialisationResult,
|
||||||
@@ -359,6 +383,7 @@ where
|
|||||||
details_store: &S::GatewaysDetailsStore,
|
details_store: &S::GatewaysDetailsStore,
|
||||||
packet_router: PacketRouter,
|
packet_router: PacketRouter,
|
||||||
stats_reporter: ClientStatsSender,
|
stats_reporter: ClientStatsSender,
|
||||||
|
#[cfg(unix)] connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
|
||||||
shutdown: TaskClient,
|
shutdown: TaskClient,
|
||||||
) -> Result<GatewayClient<C, S::CredentialStore>, ClientCoreError>
|
) -> Result<GatewayClient<C, S::CredentialStore>, ClientCoreError>
|
||||||
where
|
where
|
||||||
@@ -401,6 +426,8 @@ where
|
|||||||
packet_router,
|
packet_router,
|
||||||
bandwidth_controller,
|
bandwidth_controller,
|
||||||
stats_reporter,
|
stats_reporter,
|
||||||
|
#[cfg(unix)]
|
||||||
|
connection_fd_callback,
|
||||||
shutdown,
|
shutdown,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
@@ -462,6 +489,7 @@ where
|
|||||||
details_store: &S::GatewaysDetailsStore,
|
details_store: &S::GatewaysDetailsStore,
|
||||||
packet_router: PacketRouter,
|
packet_router: PacketRouter,
|
||||||
stats_reporter: ClientStatsSender,
|
stats_reporter: ClientStatsSender,
|
||||||
|
#[cfg(unix)] connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
|
||||||
mut shutdown: TaskClient,
|
mut shutdown: TaskClient,
|
||||||
) -> Result<Box<dyn GatewayTransceiver + Send>, ClientCoreError>
|
) -> Result<Box<dyn GatewayTransceiver + Send>, ClientCoreError>
|
||||||
where
|
where
|
||||||
@@ -493,6 +521,8 @@ where
|
|||||||
details_store,
|
details_store,
|
||||||
packet_router,
|
packet_router,
|
||||||
stats_reporter,
|
stats_reporter,
|
||||||
|
#[cfg(unix)]
|
||||||
|
connection_fd_callback,
|
||||||
shutdown,
|
shutdown,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
@@ -615,9 +645,11 @@ where
|
|||||||
fn start_mix_traffic_controller(
|
fn start_mix_traffic_controller(
|
||||||
gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
|
gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
|
||||||
shutdown: TaskClient,
|
shutdown: TaskClient,
|
||||||
|
forget_me: ForgetMe,
|
||||||
) -> BatchMixMessageSender {
|
) -> BatchMixMessageSender {
|
||||||
info!("Starting mix traffic controller...");
|
info!("Starting mix traffic controller...");
|
||||||
let (mix_traffic_controller, mix_tx) = MixTrafficController::new(gateway_transceiver);
|
let (mix_traffic_controller, mix_tx) =
|
||||||
|
MixTrafficController::new(gateway_transceiver, forget_me);
|
||||||
mix_traffic_controller.start_with_shutdown(shutdown);
|
mix_traffic_controller.start_with_shutdown(shutdown);
|
||||||
mix_tx
|
mix_tx
|
||||||
}
|
}
|
||||||
@@ -772,6 +804,8 @@ where
|
|||||||
&details_store,
|
&details_store,
|
||||||
gateway_packet_router,
|
gateway_packet_router,
|
||||||
stats_reporter.clone(),
|
stats_reporter.clone(),
|
||||||
|
#[cfg(unix)]
|
||||||
|
self.connection_fd_callback,
|
||||||
shutdown.fork("gateway_transceiver"),
|
shutdown.fork("gateway_transceiver"),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
@@ -797,9 +831,11 @@ where
|
|||||||
// that are to be sent to the mixnet. They are used by cover traffic stream and real
|
// that are to be sent to the mixnet. They are used by cover traffic stream and real
|
||||||
// traffic stream.
|
// traffic stream.
|
||||||
// The MixTrafficController then sends the actual traffic
|
// The MixTrafficController then sends the actual traffic
|
||||||
|
|
||||||
let message_sender = Self::start_mix_traffic_controller(
|
let message_sender = Self::start_mix_traffic_controller(
|
||||||
gateway_transceiver,
|
gateway_transceiver,
|
||||||
shutdown.fork("mix_traffic_controller"),
|
shutdown.fork("mix_traffic_controller"),
|
||||||
|
self.forget_me,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Channels that the websocket listener can use to signal downstream to the real traffic
|
// Channels that the websocket listener can use to signal downstream to the real traffic
|
||||||
|
|||||||
@@ -2,8 +2,9 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::client::mix_traffic::transceiver::GatewayTransceiver;
|
use crate::client::mix_traffic::transceiver::GatewayTransceiver;
|
||||||
use crate::spawn_future;
|
use crate::{spawn_future, ForgetMe};
|
||||||
use log::*;
|
use log::*;
|
||||||
|
use nym_gateway_requests::ClientRequest;
|
||||||
use nym_sphinx::forwarding::packet::MixPacket;
|
use nym_sphinx::forwarding::packet::MixPacket;
|
||||||
|
|
||||||
pub type BatchMixMessageSender = tokio::sync::mpsc::Sender<Vec<MixPacket>>;
|
pub type BatchMixMessageSender = tokio::sync::mpsc::Sender<Vec<MixPacket>>;
|
||||||
@@ -26,10 +27,14 @@ pub struct MixTrafficController {
|
|||||||
// TODO: this is temporary work-around.
|
// TODO: this is temporary work-around.
|
||||||
// in long run `gateway_client` will be moved away from `MixTrafficController` anyway.
|
// in long run `gateway_client` will be moved away from `MixTrafficController` anyway.
|
||||||
consecutive_gateway_failure_count: usize,
|
consecutive_gateway_failure_count: usize,
|
||||||
|
forget_me: ForgetMe,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MixTrafficController {
|
impl MixTrafficController {
|
||||||
pub fn new<T>(gateway_transceiver: T) -> (MixTrafficController, BatchMixMessageSender)
|
pub fn new<T>(
|
||||||
|
gateway_transceiver: T,
|
||||||
|
forget_me: ForgetMe,
|
||||||
|
) -> (MixTrafficController, BatchMixMessageSender)
|
||||||
where
|
where
|
||||||
T: GatewayTransceiver + Send + 'static,
|
T: GatewayTransceiver + Send + 'static,
|
||||||
{
|
{
|
||||||
@@ -40,6 +45,7 @@ impl MixTrafficController {
|
|||||||
gateway_transceiver: Box::new(gateway_transceiver),
|
gateway_transceiver: Box::new(gateway_transceiver),
|
||||||
mix_rx: message_receiver,
|
mix_rx: message_receiver,
|
||||||
consecutive_gateway_failure_count: 0,
|
consecutive_gateway_failure_count: 0,
|
||||||
|
forget_me,
|
||||||
},
|
},
|
||||||
message_sender,
|
message_sender,
|
||||||
)
|
)
|
||||||
@@ -47,6 +53,7 @@ impl MixTrafficController {
|
|||||||
|
|
||||||
pub fn new_dynamic(
|
pub fn new_dynamic(
|
||||||
gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
|
gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
|
||||||
|
forget_me: ForgetMe,
|
||||||
) -> (MixTrafficController, BatchMixMessageSender) {
|
) -> (MixTrafficController, BatchMixMessageSender) {
|
||||||
let (message_sender, message_receiver) =
|
let (message_sender, message_receiver) =
|
||||||
tokio::sync::mpsc::channel(MIX_MESSAGE_RECEIVER_BUFFER_SIZE);
|
tokio::sync::mpsc::channel(MIX_MESSAGE_RECEIVER_BUFFER_SIZE);
|
||||||
@@ -55,6 +62,7 @@ impl MixTrafficController {
|
|||||||
gateway_transceiver,
|
gateway_transceiver,
|
||||||
mix_rx: message_receiver,
|
mix_rx: message_receiver,
|
||||||
consecutive_gateway_failure_count: 0,
|
consecutive_gateway_failure_count: 0,
|
||||||
|
forget_me,
|
||||||
},
|
},
|
||||||
message_sender,
|
message_sender,
|
||||||
)
|
)
|
||||||
@@ -111,7 +119,27 @@ impl MixTrafficController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
shutdown.recv_timeout().await;
|
shutdown.recv_timeout().await;
|
||||||
|
|
||||||
|
if self.forget_me.any() {
|
||||||
|
log::info!("Sending forget me request to the gateway");
|
||||||
|
match self
|
||||||
|
.gateway_transceiver
|
||||||
|
.send_client_request(ClientRequest::ForgetMe {
|
||||||
|
client: self.forget_me.client(),
|
||||||
|
stats: self.forget_me.stats(),
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => {
|
||||||
|
log::info!("Successfully sent forget me request to the gateway");
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
log::error!("Failed to send forget me request to the gateway: {err}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
log::debug!("MixTrafficController: Exiting");
|
log::debug!("MixTrafficController: Exiting");
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ use async_trait::async_trait;
|
|||||||
use log::{debug, error};
|
use log::{debug, error};
|
||||||
use nym_credential_storage::storage::Storage as CredentialStorage;
|
use nym_credential_storage::storage::Storage as CredentialStorage;
|
||||||
use nym_crypto::asymmetric::identity;
|
use nym_crypto::asymmetric::identity;
|
||||||
|
use nym_gateway_client::error::GatewayClientError;
|
||||||
use nym_gateway_client::GatewayClient;
|
use nym_gateway_client::GatewayClient;
|
||||||
pub use nym_gateway_client::{GatewayPacketRouter, PacketRouter};
|
pub use nym_gateway_client::{GatewayPacketRouter, PacketRouter};
|
||||||
|
use nym_gateway_requests::ClientRequest;
|
||||||
use nym_sphinx::forwarding::packet::MixPacket;
|
use nym_sphinx::forwarding::packet::MixPacket;
|
||||||
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
|
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
@@ -26,9 +28,14 @@ fn erase_err<E: std::error::Error + Send + Sync + 'static>(err: E) -> ErasedGate
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// This combines combines the functionalities of being able to send and receive mix packets.
|
/// This combines combines the functionalities of being able to send and receive mix packets.
|
||||||
|
#[async_trait]
|
||||||
pub trait GatewayTransceiver: GatewaySender + GatewayReceiver {
|
pub trait GatewayTransceiver: GatewaySender + GatewayReceiver {
|
||||||
fn gateway_identity(&self) -> identity::PublicKey;
|
fn gateway_identity(&self) -> identity::PublicKey;
|
||||||
fn ws_fd(&self) -> Option<RawFd>;
|
fn ws_fd(&self) -> Option<RawFd>;
|
||||||
|
async fn send_client_request(
|
||||||
|
&mut self,
|
||||||
|
message: ClientRequest,
|
||||||
|
) -> Result<(), GatewayClientError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This trait defines the functionality of sending `MixPacket` into the mixnet,
|
/// This trait defines the functionality of sending `MixPacket` into the mixnet,
|
||||||
@@ -65,6 +72,7 @@ pub trait GatewayReceiver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// to allow for dynamic dispatch
|
// to allow for dynamic dispatch
|
||||||
|
#[async_trait]
|
||||||
impl<G: GatewayTransceiver + ?Sized + Send> GatewayTransceiver for Box<G> {
|
impl<G: GatewayTransceiver + ?Sized + Send> GatewayTransceiver for Box<G> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn gateway_identity(&self) -> identity::PublicKey {
|
fn gateway_identity(&self) -> identity::PublicKey {
|
||||||
@@ -73,6 +81,13 @@ impl<G: GatewayTransceiver + ?Sized + Send> GatewayTransceiver for Box<G> {
|
|||||||
fn ws_fd(&self) -> Option<RawFd> {
|
fn ws_fd(&self) -> Option<RawFd> {
|
||||||
(**self).ws_fd()
|
(**self).ws_fd()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn send_client_request(
|
||||||
|
&mut self,
|
||||||
|
message: ClientRequest,
|
||||||
|
) -> Result<(), GatewayClientError> {
|
||||||
|
(**self).send_client_request(message).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||||
@@ -91,7 +106,6 @@ impl<G: GatewaySender + ?Sized + Send> GatewaySender for Box<G> {
|
|||||||
(**self).batch_send_mix_packets(packets).await
|
(**self).batch_send_mix_packets(packets).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<G: GatewayReceiver + ?Sized> GatewayReceiver for Box<G> {
|
impl<G: GatewayReceiver + ?Sized> GatewayReceiver for Box<G> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_packet_router(&mut self, packet_router: PacketRouter) -> Result<(), ErasedGatewayError> {
|
fn set_packet_router(&mut self, packet_router: PacketRouter) -> Result<(), ErasedGatewayError> {
|
||||||
@@ -111,6 +125,7 @@ impl<C, St> RemoteGateway<C, St> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
impl<C, St> GatewayTransceiver for RemoteGateway<C, St>
|
impl<C, St> GatewayTransceiver for RemoteGateway<C, St>
|
||||||
where
|
where
|
||||||
C: DkgQueryClient + Send + Sync,
|
C: DkgQueryClient + Send + Sync,
|
||||||
@@ -123,6 +138,20 @@ where
|
|||||||
fn ws_fd(&self) -> Option<RawFd> {
|
fn ws_fd(&self) -> Option<RawFd> {
|
||||||
self.gateway_client.ws_fd()
|
self.gateway_client.ws_fd()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn send_client_request(
|
||||||
|
&mut self,
|
||||||
|
message: ClientRequest,
|
||||||
|
) -> Result<(), GatewayClientError> {
|
||||||
|
if let Some(shared_key) = self.gateway_client.shared_key() {
|
||||||
|
self.gateway_client
|
||||||
|
.send_websocket_message(message.encrypt(&*shared_key)?)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(GatewayClientError::ConnectionInInvalidState)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||||
@@ -195,6 +224,7 @@ impl LocalGateway {
|
|||||||
mod nonwasm_sealed {
|
mod nonwasm_sealed {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
impl GatewayTransceiver for LocalGateway {
|
impl GatewayTransceiver for LocalGateway {
|
||||||
fn gateway_identity(&self) -> identity::PublicKey {
|
fn gateway_identity(&self) -> identity::PublicKey {
|
||||||
self.local_identity
|
self.local_identity
|
||||||
@@ -202,6 +232,13 @@ mod nonwasm_sealed {
|
|||||||
fn ws_fd(&self) -> Option<RawFd> {
|
fn ws_fd(&self) -> Option<RawFd> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn send_client_request(
|
||||||
|
&mut self,
|
||||||
|
_message: ClientRequest,
|
||||||
|
) -> Result<(), GatewayClientError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@@ -269,6 +306,7 @@ impl GatewaySender for MockGateway {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
impl GatewayTransceiver for MockGateway {
|
impl GatewayTransceiver for MockGateway {
|
||||||
fn gateway_identity(&self) -> identity::PublicKey {
|
fn gateway_identity(&self) -> identity::PublicKey {
|
||||||
self.dummy_identity
|
self.dummy_identity
|
||||||
@@ -276,4 +314,11 @@ impl GatewayTransceiver for MockGateway {
|
|||||||
fn ws_fd(&self) -> Option<RawFd> {
|
fn ws_fd(&self) -> Option<RawFd> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn send_client_request(
|
||||||
|
&mut self,
|
||||||
|
_message: ClientRequest,
|
||||||
|
) -> Result<(), GatewayClientError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ where
|
|||||||
Ok(GatewayWithLatency::new(gateway, avg))
|
Ok(GatewayWithLatency::new(gateway, avg))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn choose_gateway_by_latency<'a, R: Rng, G: ConnectableGateway + Clone>(
|
pub async fn choose_gateway_by_latency<R: Rng, G: ConnectableGateway + Clone>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
gateways: &[G],
|
gateways: &[G],
|
||||||
must_use_tls: bool,
|
must_use_tls: bool,
|
||||||
|
|||||||
@@ -34,3 +34,48 @@ where
|
|||||||
{
|
{
|
||||||
tokio::spawn(future);
|
tokio::spawn(future);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug)]
|
||||||
|
pub struct ForgetMe {
|
||||||
|
client: bool,
|
||||||
|
stats: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ForgetMe {
|
||||||
|
pub fn new_all() -> Self {
|
||||||
|
Self {
|
||||||
|
client: true,
|
||||||
|
stats: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_client() -> Self {
|
||||||
|
Self {
|
||||||
|
client: true,
|
||||||
|
stats: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_stats() -> Self {
|
||||||
|
Self {
|
||||||
|
client: false,
|
||||||
|
stats: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(client: bool, stats: bool) -> Self {
|
||||||
|
Self { client, stats }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn any(&self) -> bool {
|
||||||
|
self.client || self.stats
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn client(&self) -> bool {
|
||||||
|
self.client
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stats(&self) -> bool {
|
||||||
|
self.stats
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,7 +9,10 @@ use crate::backend::fs_backend::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
use sqlx::ConnectOptions;
|
use sqlx::{
|
||||||
|
sqlite::{SqliteAutoVacuum, SqliteSynchronous},
|
||||||
|
ConnectOptions,
|
||||||
|
};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -31,6 +34,9 @@ impl StorageManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let opts = sqlx::sqlite::SqliteConnectOptions::new()
|
let opts = sqlx::sqlite::SqliteConnectOptions::new()
|
||||||
|
.journal_mode(sqlx::sqlite::SqliteJournalMode::Wal)
|
||||||
|
.synchronous(SqliteSynchronous::Normal)
|
||||||
|
.auto_vacuum(SqliteAutoVacuum::Incremental)
|
||||||
.filename(database_path)
|
.filename(database_path)
|
||||||
.create_if_missing(fresh)
|
.create_if_missing(fresh)
|
||||||
.disable_statement_logging();
|
.disable_statement_logging();
|
||||||
|
|||||||
@@ -101,6 +101,10 @@ pub struct GatewayClient<C, St = EphemeralCredentialStorage> {
|
|||||||
// currently unused (but populated)
|
// currently unused (but populated)
|
||||||
negotiated_protocol: Option<u8>,
|
negotiated_protocol: Option<u8>,
|
||||||
|
|
||||||
|
// Callback on the fd as soon as the connection has been established
|
||||||
|
#[cfg(unix)]
|
||||||
|
connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
|
||||||
|
|
||||||
/// Listen to shutdown messages and send notifications back to the task manager
|
/// Listen to shutdown messages and send notifications back to the task manager
|
||||||
task_client: TaskClient,
|
task_client: TaskClient,
|
||||||
}
|
}
|
||||||
@@ -116,6 +120,7 @@ impl<C, St> GatewayClient<C, St> {
|
|||||||
packet_router: PacketRouter,
|
packet_router: PacketRouter,
|
||||||
bandwidth_controller: Option<BandwidthController<C, St>>,
|
bandwidth_controller: Option<BandwidthController<C, St>>,
|
||||||
stats_reporter: ClientStatsSender,
|
stats_reporter: ClientStatsSender,
|
||||||
|
#[cfg(unix)] connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
|
||||||
task_client: TaskClient,
|
task_client: TaskClient,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
GatewayClient {
|
GatewayClient {
|
||||||
@@ -131,6 +136,8 @@ impl<C, St> GatewayClient<C, St> {
|
|||||||
bandwidth_controller,
|
bandwidth_controller,
|
||||||
stats_reporter,
|
stats_reporter,
|
||||||
negotiated_protocol: None,
|
negotiated_protocol: None,
|
||||||
|
#[cfg(unix)]
|
||||||
|
connection_fd_callback,
|
||||||
task_client,
|
task_client,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -205,6 +212,12 @@ impl<C, St> GatewayClient<C, St> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
self.connection = SocketState::Available(Box::new(ws_stream));
|
self.connection = SocketState::Available(Box::new(ws_stream));
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
if let (Some(callback), Some(fd)) = (self.connection_fd_callback.as_ref(), self.ws_fd()) {
|
||||||
|
callback.as_ref()(fd);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,7 +324,7 @@ impl<C, St> GatewayClient<C, St> {
|
|||||||
|
|
||||||
// If we want to send a message (with response), we need to have a full control over the socket,
|
// If we want to send a message (with response), we need to have a full control over the socket,
|
||||||
// as we need to be able to write the request and read the subsequent response
|
// as we need to be able to write the request and read the subsequent response
|
||||||
async fn send_websocket_message(
|
pub async fn send_websocket_message(
|
||||||
&mut self,
|
&mut self,
|
||||||
msg: impl Into<Message>,
|
msg: impl Into<Message>,
|
||||||
) -> Result<ServerResponse, GatewayClientError> {
|
) -> Result<ServerResponse, GatewayClientError> {
|
||||||
@@ -1034,6 +1047,8 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
|
|||||||
bandwidth_controller: None,
|
bandwidth_controller: None,
|
||||||
stats_reporter: ClientStatsSender::new(None),
|
stats_reporter: ClientStatsSender::new(None),
|
||||||
negotiated_protocol: None,
|
negotiated_protocol: None,
|
||||||
|
#[cfg(unix)]
|
||||||
|
connection_fd_callback: None,
|
||||||
task_client,
|
task_client,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1064,6 +1079,8 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
|
|||||||
bandwidth_controller,
|
bandwidth_controller,
|
||||||
stats_reporter,
|
stats_reporter,
|
||||||
negotiated_protocol: self.negotiated_protocol,
|
negotiated_protocol: self.negotiated_protocol,
|
||||||
|
#[cfg(unix)]
|
||||||
|
connection_fd_callback: self.connection_fd_callback,
|
||||||
task_client,
|
task_client,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,12 @@ license.workspace = true
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
dashmap = { workspace = true }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
tokio = { workspace = true, features = ["time"] }
|
tokio = { workspace = true, features = ["time", "sync"] }
|
||||||
tokio-util = { workspace = true, features = ["codec"], optional = true }
|
tokio-util = { workspace = true, features = ["codec"], optional = true }
|
||||||
|
tokio-stream = { workspace = true }
|
||||||
|
|
||||||
# internal
|
# internal
|
||||||
nym-sphinx = { path = "../../nymsphinx" }
|
nym-sphinx = { path = "../../nymsphinx" }
|
||||||
|
|||||||
@@ -1,21 +1,24 @@
|
|||||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use futures::channel::mpsc;
|
use dashmap::DashMap;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use nym_sphinx::addressing::nodes::NymNodeRoutingAddress;
|
use nym_sphinx::addressing::nodes::NymNodeRoutingAddress;
|
||||||
use nym_sphinx::framing::codec::NymCodec;
|
use nym_sphinx::framing::codec::NymCodec;
|
||||||
use nym_sphinx::framing::packet::FramedNymPacket;
|
use nym_sphinx::framing::packet::FramedNymPacket;
|
||||||
use nym_sphinx::params::PacketType;
|
use nym_sphinx::params::PacketType;
|
||||||
use nym_sphinx::NymPacket;
|
use nym_sphinx::NymPacket;
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::sync::atomic::{AtomicU32, Ordering};
|
use std::ops::Deref;
|
||||||
|
use std::sync::atomic::{AtomicU32, AtomicUsize, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
|
use tokio::sync::mpsc;
|
||||||
|
use tokio::sync::mpsc::error::TrySendError;
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
|
use tokio_stream::wrappers::ReceiverStream;
|
||||||
use tokio_util::codec::Framed;
|
use tokio_util::codec::Framed;
|
||||||
use tracing::*;
|
use tracing::*;
|
||||||
|
|
||||||
@@ -55,11 +58,37 @@ pub trait SendWithoutResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
conn_new: HashMap<NymNodeRoutingAddress, ConnectionSender>,
|
active_connections: ActiveConnections,
|
||||||
|
connections_count: Arc<AtomicUsize>,
|
||||||
config: Config,
|
config: Config,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ConnectionSender {
|
#[derive(Default, Clone)]
|
||||||
|
pub struct ActiveConnections {
|
||||||
|
inner: Arc<DashMap<NymNodeRoutingAddress, ConnectionSender>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveConnections {
|
||||||
|
pub fn pending_packets(&self) -> usize {
|
||||||
|
self.inner
|
||||||
|
.iter()
|
||||||
|
.map(|sender| {
|
||||||
|
let max_capacity = sender.channel.max_capacity();
|
||||||
|
let capacity = sender.channel.capacity();
|
||||||
|
max_capacity - capacity
|
||||||
|
})
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for ActiveConnections {
|
||||||
|
type Target = DashMap<NymNodeRoutingAddress, ConnectionSender>;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ConnectionSender {
|
||||||
channel: mpsc::Sender<FramedNymPacket>,
|
channel: mpsc::Sender<FramedNymPacket>,
|
||||||
current_reconnection_attempt: Arc<AtomicU32>,
|
current_reconnection_attempt: Arc<AtomicU32>,
|
||||||
}
|
}
|
||||||
@@ -73,46 +102,53 @@ impl ConnectionSender {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Client {
|
struct ManagedConnection {
|
||||||
pub fn new(config: Config) -> Client {
|
address: SocketAddr,
|
||||||
Client {
|
message_receiver: ReceiverStream<FramedNymPacket>,
|
||||||
conn_new: HashMap::new(),
|
connection_timeout: Duration,
|
||||||
config,
|
current_reconnection: Arc<AtomicU32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ManagedConnection {
|
||||||
|
fn new(
|
||||||
|
address: SocketAddr,
|
||||||
|
message_receiver: mpsc::Receiver<FramedNymPacket>,
|
||||||
|
connection_timeout: Duration,
|
||||||
|
current_reconnection: Arc<AtomicU32>,
|
||||||
|
) -> Self {
|
||||||
|
ManagedConnection {
|
||||||
|
address,
|
||||||
|
message_receiver: ReceiverStream::new(message_receiver),
|
||||||
|
connection_timeout,
|
||||||
|
current_reconnection,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn manage_connection(
|
async fn run(self) {
|
||||||
address: SocketAddr,
|
let address = self.address;
|
||||||
receiver: mpsc::Receiver<FramedNymPacket>,
|
|
||||||
connection_timeout: Duration,
|
|
||||||
current_reconnection: &AtomicU32,
|
|
||||||
) {
|
|
||||||
let connection_fut = TcpStream::connect(address);
|
let connection_fut = TcpStream::connect(address);
|
||||||
|
|
||||||
let conn = match tokio::time::timeout(connection_timeout, connection_fut).await {
|
let conn = match tokio::time::timeout(self.connection_timeout, connection_fut).await {
|
||||||
Ok(stream_res) => match stream_res {
|
Ok(stream_res) => match stream_res {
|
||||||
Ok(stream) => {
|
Ok(stream) => {
|
||||||
debug!("Managed to establish connection to {}", address);
|
debug!("Managed to establish connection to {}", self.address);
|
||||||
// if we managed to connect, reset the reconnection count (whatever it might have been)
|
// if we managed to connect, reset the reconnection count (whatever it might have been)
|
||||||
current_reconnection.store(0, Ordering::Release);
|
self.current_reconnection.store(0, Ordering::Release);
|
||||||
Framed::new(stream, NymCodec)
|
Framed::new(stream, NymCodec)
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
debug!(
|
debug!("failed to establish connection to {address} (err: {err})",);
|
||||||
"failed to establish connection to {} (err: {})",
|
|
||||||
address, err
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
debug!(
|
debug!(
|
||||||
"failed to connect to {} within {:?}",
|
"failed to connect to {address} within {:?}",
|
||||||
address, connection_timeout
|
self.connection_timeout
|
||||||
);
|
);
|
||||||
|
|
||||||
// we failed to connect - increase reconnection attempt
|
// we failed to connect - increase reconnection attempt
|
||||||
current_reconnection.fetch_add(1, Ordering::SeqCst);
|
self.current_reconnection.fetch_add(1, Ordering::SeqCst);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -120,15 +156,28 @@ impl Client {
|
|||||||
// Take whatever the receiver channel produces and put it on the connection.
|
// Take whatever the receiver channel produces and put it on the connection.
|
||||||
// We could have as well used conn.send_all(receiver.map(Ok)), but considering we don't care
|
// We could have as well used conn.send_all(receiver.map(Ok)), but considering we don't care
|
||||||
// about neither receiver nor the connection, it doesn't matter which one gets consumed
|
// about neither receiver nor the connection, it doesn't matter which one gets consumed
|
||||||
if let Err(err) = receiver.map(Ok).forward(conn).await {
|
if let Err(err) = self.message_receiver.map(Ok).forward(conn).await {
|
||||||
warn!("Failed to forward packets to {} - {err}", address);
|
warn!("Failed to forward packets to {address}: {err}");
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"connection manager to {} is finished. Either the connection failed or mixnet client got dropped",
|
"connection manager to {address} is finished. Either the connection failed or mixnet client got dropped",
|
||||||
address
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Client {
|
||||||
|
pub fn new(config: Config, connections_count: Arc<AtomicUsize>) -> Client {
|
||||||
|
Client {
|
||||||
|
active_connections: Default::default(),
|
||||||
|
connections_count,
|
||||||
|
config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn active_connections(&self) -> ActiveConnections {
|
||||||
|
self.active_connections.clone()
|
||||||
|
}
|
||||||
|
|
||||||
/// If we're trying to reconnect, determine how long we should wait.
|
/// If we're trying to reconnect, determine how long we should wait.
|
||||||
fn determine_backoff(&self, current_attempt: u32) -> Option<Duration> {
|
fn determine_backoff(&self, current_attempt: u32) -> Option<Duration> {
|
||||||
@@ -148,7 +197,7 @@ impl Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn make_connection(&mut self, address: NymNodeRoutingAddress, pending_packet: FramedNymPacket) {
|
fn make_connection(&mut self, address: NymNodeRoutingAddress, pending_packet: FramedNymPacket) {
|
||||||
let (mut sender, receiver) = mpsc::channel(self.config.maximum_connection_buffer_size);
|
let (sender, receiver) = mpsc::channel(self.config.maximum_connection_buffer_size);
|
||||||
|
|
||||||
// this CAN'T fail because we just created the channel which has a non-zero capacity
|
// this CAN'T fail because we just created the channel which has a non-zero capacity
|
||||||
if self.config.maximum_connection_buffer_size > 0 {
|
if self.config.maximum_connection_buffer_size > 0 {
|
||||||
@@ -156,15 +205,16 @@ impl Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if we already tried to connect to `address` before, grab the current attempt count
|
// if we already tried to connect to `address` before, grab the current attempt count
|
||||||
let current_reconnection_attempt = if let Some(existing) = self.conn_new.get_mut(&address) {
|
let current_reconnection_attempt =
|
||||||
existing.channel = sender;
|
if let Some(mut existing) = self.active_connections.get_mut(&address) {
|
||||||
Arc::clone(&existing.current_reconnection_attempt)
|
existing.channel = sender;
|
||||||
} else {
|
Arc::clone(&existing.current_reconnection_attempt)
|
||||||
let new_entry = ConnectionSender::new(sender);
|
} else {
|
||||||
let current_attempt = Arc::clone(&new_entry.current_reconnection_attempt);
|
let new_entry = ConnectionSender::new(sender);
|
||||||
self.conn_new.insert(address, new_entry);
|
let current_attempt = Arc::clone(&new_entry.current_reconnection_attempt);
|
||||||
current_attempt
|
self.active_connections.insert(address, new_entry);
|
||||||
};
|
current_attempt
|
||||||
|
};
|
||||||
|
|
||||||
// load the actual value.
|
// load the actual value.
|
||||||
let reconnection_attempt = current_reconnection_attempt.load(Ordering::Acquire);
|
let reconnection_attempt = current_reconnection_attempt.load(Ordering::Acquire);
|
||||||
@@ -173,6 +223,7 @@ impl Client {
|
|||||||
// copy the value before moving into another task
|
// copy the value before moving into another task
|
||||||
let initial_connection_timeout = self.config.initial_connection_timeout;
|
let initial_connection_timeout = self.config.initial_connection_timeout;
|
||||||
|
|
||||||
|
let connections_count = self.connections_count.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
// before executing the manager, wait for what was specified, if anything
|
// before executing the manager, wait for what was specified, if anything
|
||||||
if let Some(backoff) = backoff {
|
if let Some(backoff) = backoff {
|
||||||
@@ -180,13 +231,16 @@ impl Client {
|
|||||||
sleep(backoff).await;
|
sleep(backoff).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::manage_connection(
|
connections_count.fetch_add(1, Ordering::SeqCst);
|
||||||
|
ManagedConnection::new(
|
||||||
address.into(),
|
address.into(),
|
||||||
receiver,
|
receiver,
|
||||||
initial_connection_timeout,
|
initial_connection_timeout,
|
||||||
¤t_reconnection_attempt,
|
current_reconnection_attempt,
|
||||||
)
|
)
|
||||||
.await
|
.run()
|
||||||
|
.await;
|
||||||
|
connections_count.fetch_sub(1, Ordering::SeqCst);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -201,49 +255,47 @@ impl SendWithoutResponse for Client {
|
|||||||
trace!("Sending packet to {address:?}");
|
trace!("Sending packet to {address:?}");
|
||||||
let framed_packet = FramedNymPacket::new(packet, packet_type);
|
let framed_packet = FramedNymPacket::new(packet, packet_type);
|
||||||
|
|
||||||
if let Some(sender) = self.conn_new.get_mut(&address) {
|
let Some(sender) = self.active_connections.get_mut(&address) else {
|
||||||
if let Err(err) = sender.channel.try_send(framed_packet) {
|
|
||||||
if err.is_full() {
|
|
||||||
debug!("Connection to {} seems to not be able to handle all the traffic - dropping the current packet", address);
|
|
||||||
// it's not a 'big' error, but we did not manage to send the packet
|
|
||||||
// if the queue is full, we can't really do anything but to drop the packet
|
|
||||||
Err(io::Error::new(
|
|
||||||
io::ErrorKind::WouldBlock,
|
|
||||||
"connection queue is full",
|
|
||||||
))
|
|
||||||
} else if err.is_disconnected() {
|
|
||||||
debug!(
|
|
||||||
"Connection to {} seems to be dead. attempting to re-establish it...",
|
|
||||||
address
|
|
||||||
);
|
|
||||||
// it's not a 'big' error, but we did not manage to send the packet, but queue
|
|
||||||
// it up to send it as soon as the connection is re-established
|
|
||||||
self.make_connection(address, err.into_inner());
|
|
||||||
Err(io::Error::new(
|
|
||||||
io::ErrorKind::ConnectionAborted,
|
|
||||||
"reconnection attempt is in progress",
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
// this can't really happen, but let's safe-guard against it in case something changes in futures library
|
|
||||||
Err(io::Error::new(
|
|
||||||
io::ErrorKind::Other,
|
|
||||||
"unknown connection buffer error",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// there was never a connection to begin with
|
// there was never a connection to begin with
|
||||||
debug!("establishing initial connection to {}", address);
|
debug!("establishing initial connection to {}", address);
|
||||||
// it's not a 'big' error, but we did not manage to send the packet, but queue the packet
|
// it's not a 'big' error, but we did not manage to send the packet, but queue the packet
|
||||||
// for sending for as soon as the connection is created
|
// for sending for as soon as the connection is created
|
||||||
self.make_connection(address, framed_packet);
|
self.make_connection(address, framed_packet);
|
||||||
Err(io::Error::new(
|
return Err(io::Error::new(
|
||||||
io::ErrorKind::NotConnected,
|
io::ErrorKind::NotConnected,
|
||||||
"connection is in progress",
|
"connection is in progress",
|
||||||
))
|
));
|
||||||
}
|
};
|
||||||
|
|
||||||
|
let sending_res = sender.channel.try_send(framed_packet);
|
||||||
|
drop(sender);
|
||||||
|
|
||||||
|
sending_res.map_err(|err| {
|
||||||
|
match err {
|
||||||
|
TrySendError::Full(_) => {
|
||||||
|
debug!("Connection to {address} seems to not be able to handle all the traffic - dropping the current packet");
|
||||||
|
// it's not a 'big' error, but we did not manage to send the packet
|
||||||
|
// if the queue is full, we can't really do anything but to drop the packet
|
||||||
|
io::Error::new(
|
||||||
|
io::ErrorKind::WouldBlock,
|
||||||
|
"connection queue is full",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
TrySendError::Closed(dropped) => {
|
||||||
|
debug!(
|
||||||
|
"Connection to {address} seems to be dead. attempting to re-establish it...",
|
||||||
|
);
|
||||||
|
|
||||||
|
// it's not a 'big' error, but we did not manage to send the packet, but queue
|
||||||
|
// it up to send it as soon as the connection is re-established
|
||||||
|
self.make_connection(address, dropped);
|
||||||
|
io::Error::new(
|
||||||
|
io::ErrorKind::ConnectionAborted,
|
||||||
|
"reconnection attempt is in progress",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,12 +304,15 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
fn dummy_client() -> Client {
|
fn dummy_client() -> Client {
|
||||||
Client::new(Config {
|
Client::new(
|
||||||
initial_reconnection_backoff: Duration::from_millis(10_000),
|
Config {
|
||||||
maximum_reconnection_backoff: Duration::from_millis(300_000),
|
initial_reconnection_backoff: Duration::from_millis(10_000),
|
||||||
initial_connection_timeout: Duration::from_millis(1_500),
|
maximum_reconnection_backoff: Duration::from_millis(300_000),
|
||||||
maximum_connection_buffer_size: 128,
|
initial_connection_timeout: Duration::from_millis(1_500),
|
||||||
})
|
maximum_connection_buffer_size: 128,
|
||||||
|
},
|
||||||
|
Default::default(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -863,11 +863,4 @@ pub enum QueryMsg {
|
|||||||
pub struct MigrateMsg {
|
pub struct MigrateMsg {
|
||||||
pub unsafe_skip_state_updates: Option<bool>,
|
pub unsafe_skip_state_updates: Option<bool>,
|
||||||
pub vesting_contract_address: Option<String>,
|
pub vesting_contract_address: Option<String>,
|
||||||
pub current_nym_node_semver: String,
|
|
||||||
|
|
||||||
#[serde(default)]
|
|
||||||
pub version_score_weights: OutdatedVersionWeights,
|
|
||||||
|
|
||||||
#[serde(default)]
|
|
||||||
pub version_score_params: VersionScoreFormulaParams,
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,11 @@ impl SqliteEcashTicketbookManager {
|
|||||||
SqliteEcashTicketbookManager { connection_pool }
|
SqliteEcashTicketbookManager { connection_pool }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Closes the connection pool.
|
||||||
|
pub async fn close(&self) {
|
||||||
|
self.connection_pool.close().await
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) async fn cleanup_expired(&self, deadline: Date) -> Result<(), sqlx::Error> {
|
pub(crate) async fn cleanup_expired(&self, deadline: Date) -> Result<(), sqlx::Error> {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"DELETE FROM ecash_ticketbook WHERE expiration_date <= ?",
|
"DELETE FROM ecash_ticketbook WHERE expiration_date <= ?",
|
||||||
|
|||||||
@@ -43,6 +43,10 @@ impl Debug for EphemeralStorage {
|
|||||||
impl Storage for EphemeralStorage {
|
impl Storage for EphemeralStorage {
|
||||||
type StorageError = StorageError;
|
type StorageError = StorageError;
|
||||||
|
|
||||||
|
async fn close(&self) {
|
||||||
|
// nothing to do here
|
||||||
|
}
|
||||||
|
|
||||||
async fn cleanup_expired(&self) -> Result<(), Self::StorageError> {
|
async fn cleanup_expired(&self) -> Result<(), Self::StorageError> {
|
||||||
self.storage_manager.cleanup_expired().await;
|
self.storage_manager.cleanup_expired().await;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -33,7 +33,10 @@ use nym_credentials::{
|
|||||||
IssuanceTicketBook, IssuedTicketBook,
|
IssuanceTicketBook, IssuedTicketBook,
|
||||||
};
|
};
|
||||||
use nym_ecash_time::{ecash_today, Date, EcashTime};
|
use nym_ecash_time::{ecash_today, Date, EcashTime};
|
||||||
use sqlx::ConnectOptions;
|
use sqlx::{
|
||||||
|
sqlite::{SqliteAutoVacuum, SqliteSynchronous},
|
||||||
|
ConnectOptions,
|
||||||
|
};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
@@ -56,6 +59,9 @@ impl PersistentStorage {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let opts = sqlx::sqlite::SqliteConnectOptions::new()
|
let opts = sqlx::sqlite::SqliteConnectOptions::new()
|
||||||
|
.journal_mode(sqlx::sqlite::SqliteJournalMode::Wal)
|
||||||
|
.synchronous(SqliteSynchronous::Normal)
|
||||||
|
.auto_vacuum(SqliteAutoVacuum::Incremental)
|
||||||
.filename(database_path)
|
.filename(database_path)
|
||||||
.create_if_missing(true)
|
.create_if_missing(true)
|
||||||
.disable_statement_logging();
|
.disable_statement_logging();
|
||||||
@@ -83,6 +89,10 @@ impl PersistentStorage {
|
|||||||
impl Storage for PersistentStorage {
|
impl Storage for PersistentStorage {
|
||||||
type StorageError = StorageError;
|
type StorageError = StorageError;
|
||||||
|
|
||||||
|
async fn close(&self) {
|
||||||
|
self.storage_manager.close().await
|
||||||
|
}
|
||||||
|
|
||||||
/// remove all expired ticketbooks and expiration date signatures
|
/// remove all expired ticketbooks and expiration date signatures
|
||||||
async fn cleanup_expired(&self) -> Result<(), Self::StorageError> {
|
async fn cleanup_expired(&self) -> Result<(), Self::StorageError> {
|
||||||
let ecash_yesterday = ecash_today().date().previous_day().unwrap();
|
let ecash_yesterday = ecash_today().date().previous_day().unwrap();
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ use std::error::Error;
|
|||||||
pub trait Storage: Send + Sync {
|
pub trait Storage: Send + Sync {
|
||||||
type StorageError: Error;
|
type StorageError: Error;
|
||||||
|
|
||||||
|
async fn close(&self);
|
||||||
|
|
||||||
/// remove all expired ticketbooks and expiration date signatures
|
/// remove all expired ticketbooks and expiration date signatures
|
||||||
async fn cleanup_expired(&self) -> Result<(), Self::StorageError>;
|
async fn cleanup_expired(&self) -> Result<(), Self::StorageError>;
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use futures::{Sink, Stream};
|
|||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
use tungstenite::Message as WsMessage;
|
use tungstenite::Message as WsMessage;
|
||||||
|
|
||||||
impl<'a, S, R> State<'a, S, R> {
|
impl<S, R> State<'_, S, R> {
|
||||||
async fn client_handshake_inner(&mut self) -> Result<(), HandshakeError>
|
async fn client_handshake_inner(&mut self) -> Result<(), HandshakeError>
|
||||||
where
|
where
|
||||||
S: Stream<Item = WsItem> + Sink<WsMessage> + Unpin,
|
S: Stream<Item = WsItem> + Sink<WsMessage> + Unpin,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use crate::registration::handshake::{error::HandshakeError, WsItem};
|
|||||||
use futures::{Sink, Stream};
|
use futures::{Sink, Stream};
|
||||||
use tungstenite::Message as WsMessage;
|
use tungstenite::Message as WsMessage;
|
||||||
|
|
||||||
impl<'a, S, R> State<'a, S, R> {
|
impl<S, R> State<'_, S, R> {
|
||||||
async fn gateway_handshake_inner(
|
async fn gateway_handshake_inner(
|
||||||
&mut self,
|
&mut self,
|
||||||
raw_init_message: Vec<u8>,
|
raw_init_message: Vec<u8>,
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ pub enum ClientRequest {
|
|||||||
hkdf_salt: Vec<u8>,
|
hkdf_salt: Vec<u8>,
|
||||||
derived_key_digest: Vec<u8>,
|
derived_key_digest: Vec<u8>,
|
||||||
},
|
},
|
||||||
|
ForgetMe {
|
||||||
|
client: bool,
|
||||||
|
stats: bool,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientRequest {
|
impl ClientRequest {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use tungstenite::Message;
|
|||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum SensitiveServerResponse {
|
pub enum SensitiveServerResponse {
|
||||||
KeyUpgradeAck {},
|
KeyUpgradeAck {},
|
||||||
|
ForgetMeAck {},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SensitiveServerResponse {
|
impl SensitiveServerResponse {
|
||||||
|
|||||||
@@ -6,7 +6,10 @@ use models::StoredFinishedSession;
|
|||||||
use nym_node_metrics::entry::{ActiveSession, FinishedSession, SessionType};
|
use nym_node_metrics::entry::{ActiveSession, FinishedSession, SessionType};
|
||||||
use nym_sphinx::DestinationAddressBytes;
|
use nym_sphinx::DestinationAddressBytes;
|
||||||
use sessions::SessionManager;
|
use sessions::SessionManager;
|
||||||
use sqlx::ConnectOptions;
|
use sqlx::{
|
||||||
|
sqlite::{SqliteAutoVacuum, SqliteSynchronous},
|
||||||
|
ConnectOptions,
|
||||||
|
};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use time::Date;
|
use time::Date;
|
||||||
use tracing::{debug, error};
|
use tracing::{debug, error};
|
||||||
@@ -36,6 +39,9 @@ impl PersistentStatsStorage {
|
|||||||
// TODO: we can inject here more stuff based on our gateway global config
|
// TODO: we can inject here more stuff based on our gateway global config
|
||||||
// struct. Maybe different pool size or timeout intervals?
|
// struct. Maybe different pool size or timeout intervals?
|
||||||
let opts = sqlx::sqlite::SqliteConnectOptions::new()
|
let opts = sqlx::sqlite::SqliteConnectOptions::new()
|
||||||
|
.journal_mode(sqlx::sqlite::SqliteJournalMode::Wal)
|
||||||
|
.synchronous(SqliteSynchronous::Normal)
|
||||||
|
.auto_vacuum(SqliteAutoVacuum::Incremental)
|
||||||
.filename(database_path)
|
.filename(database_path)
|
||||||
.create_if_missing(true)
|
.create_if_missing(true)
|
||||||
.disable_statement_logging();
|
.disable_statement_logging();
|
||||||
@@ -116,6 +122,16 @@ impl PersistentStatsStorage {
|
|||||||
.await?)
|
.await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn delete_unique_user(
|
||||||
|
&self,
|
||||||
|
client_address: DestinationAddressBytes,
|
||||||
|
) -> Result<(), StatsStorageError> {
|
||||||
|
Ok(self
|
||||||
|
.session_manager
|
||||||
|
.delete_unique_user(client_address.as_base58_string())
|
||||||
|
.await?)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn insert_active_session(
|
pub async fn insert_active_session(
|
||||||
&self,
|
&self,
|
||||||
client_address: DestinationAddressBytes,
|
client_address: DestinationAddressBytes,
|
||||||
|
|||||||
@@ -71,6 +71,16 @@ impl SessionManager {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn delete_unique_user(&self, client_address_b58: String) -> Result<()> {
|
||||||
|
sqlx::query!(
|
||||||
|
"DELETE FROM sessions_unique_users WHERE client_address = ?",
|
||||||
|
client_address_b58
|
||||||
|
)
|
||||||
|
.execute(&self.connection_pool)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_unique_users(&self, date: Date) -> Result<Vec<String>> {
|
pub(crate) async fn get_unique_users(&self, date: Date) -> Result<Vec<String>> {
|
||||||
sqlx::query_scalar!(
|
sqlx::query_scalar!(
|
||||||
"SELECT client_address as count FROM sessions_unique_users WHERE day = ?",
|
"SELECT client_address as count FROM sessions_unique_users WHERE day = ?",
|
||||||
|
|||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"db_name": "SQLite",
|
||||||
|
"query": "DELETE FROM message_store WHERE client_address_bs58 = ?",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 1
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "3ea5542b21a41b14276a8fd6b870c61aa0ddd30fee2565803b88c6086bd2a734"
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"db_name": "SQLite",
|
||||||
|
"query": "DELETE FROM available_bandwidth WHERE client_id = ?",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 1
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "a3cc707995b8215fa77738cd1a55f9e8d251a3e764104d2a54153895dee1a118"
|
||||||
|
}
|
||||||
@@ -49,6 +49,16 @@ impl BandwidthManager {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn remove_client(&self, client_id: i64) -> Result<(), sqlx::Error> {
|
||||||
|
sqlx::query!(
|
||||||
|
"DELETE FROM available_bandwidth WHERE client_id = ?",
|
||||||
|
client_id
|
||||||
|
)
|
||||||
|
.execute(&self.connection_pool)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Set the expiration date of the particular client to the provided date.
|
/// Set the expiration date of the particular client to the provided date.
|
||||||
pub(crate) async fn set_expiration(
|
pub(crate) async fn set_expiration(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -133,4 +133,17 @@ impl InboxManager {
|
|||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn remove_messages_for_client(
|
||||||
|
&self,
|
||||||
|
client_address_bs58: &str,
|
||||||
|
) -> Result<(), sqlx::Error> {
|
||||||
|
sqlx::query!(
|
||||||
|
"DELETE FROM message_store WHERE client_address_bs58 = ?",
|
||||||
|
client_address_bs58
|
||||||
|
)
|
||||||
|
.execute(&self.connection_pool)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,10 @@ use nym_credentials_interface::ClientTicket;
|
|||||||
use nym_gateway_requests::shared_key::SharedGatewayKey;
|
use nym_gateway_requests::shared_key::SharedGatewayKey;
|
||||||
use nym_sphinx::DestinationAddressBytes;
|
use nym_sphinx::DestinationAddressBytes;
|
||||||
use shared_keys::SharedKeysManager;
|
use shared_keys::SharedKeysManager;
|
||||||
use sqlx::ConnectOptions;
|
use sqlx::{
|
||||||
|
sqlite::{SqliteAutoVacuum, SqliteSynchronous},
|
||||||
|
ConnectOptions,
|
||||||
|
};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use tickets::TicketStorageManager;
|
use tickets::TicketStorageManager;
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
@@ -41,6 +44,33 @@ pub struct GatewayStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl GatewayStorage {
|
impl GatewayStorage {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) fn client_manager(&self) -> &ClientManager {
|
||||||
|
&self.client_manager
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn shared_key_manager(&self) -> &SharedKeysManager {
|
||||||
|
&self.shared_key_manager
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn inbox_manager(&self) -> &InboxManager {
|
||||||
|
&self.inbox_manager
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn bandwidth_manager(&self) -> &BandwidthManager {
|
||||||
|
&self.bandwidth_manager
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) fn ticket_manager(&self) -> &TicketStorageManager {
|
||||||
|
&self.ticket_manager
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) fn wireguard_peer_manager(&self) -> &wireguard_peers::WgPeerManager {
|
||||||
|
&self.wireguard_peer_manager
|
||||||
|
}
|
||||||
|
|
||||||
/// Initialises `PersistentStorage` using the provided path.
|
/// Initialises `PersistentStorage` using the provided path.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@@ -59,6 +89,9 @@ impl GatewayStorage {
|
|||||||
// TODO: we can inject here more stuff based on our gateway global config
|
// TODO: we can inject here more stuff based on our gateway global config
|
||||||
// struct. Maybe different pool size or timeout intervals?
|
// struct. Maybe different pool size or timeout intervals?
|
||||||
let opts = sqlx::sqlite::SqliteConnectOptions::new()
|
let opts = sqlx::sqlite::SqliteConnectOptions::new()
|
||||||
|
.journal_mode(sqlx::sqlite::SqliteJournalMode::Wal)
|
||||||
|
.synchronous(SqliteSynchronous::Normal)
|
||||||
|
.auto_vacuum(SqliteAutoVacuum::Incremental)
|
||||||
.filename(database_path)
|
.filename(database_path)
|
||||||
.create_if_missing(true)
|
.create_if_missing(true)
|
||||||
.disable_statement_logging();
|
.disable_statement_logging();
|
||||||
@@ -101,6 +134,21 @@ impl GatewayStorage {
|
|||||||
.await?)
|
.await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn handle_forget_me(
|
||||||
|
&self,
|
||||||
|
client_address: DestinationAddressBytes,
|
||||||
|
) -> Result<(), GatewayStorageError> {
|
||||||
|
let client_id = self.get_mixnet_client_id(client_address).await?;
|
||||||
|
self.inbox_manager()
|
||||||
|
.remove_messages_for_client(&client_address.as_base58_string())
|
||||||
|
.await?;
|
||||||
|
self.bandwidth_manager().remove_client(client_id).await?;
|
||||||
|
self.shared_key_manager()
|
||||||
|
.remove_shared_keys(&client_address.as_base58_string())
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn insert_shared_keys(
|
pub async fn insert_shared_keys(
|
||||||
&self,
|
&self,
|
||||||
client_address: DestinationAddressBytes,
|
client_address: DestinationAddressBytes,
|
||||||
|
|||||||
@@ -324,6 +324,56 @@ impl Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create_patch_request<B, K, V>(
|
||||||
|
&self,
|
||||||
|
path: PathSegments<'_>,
|
||||||
|
params: Params<'_, K, V>,
|
||||||
|
json_body: &B,
|
||||||
|
) -> RequestBuilder
|
||||||
|
where
|
||||||
|
B: Serialize + ?Sized,
|
||||||
|
K: AsRef<str>,
|
||||||
|
V: AsRef<str>,
|
||||||
|
{
|
||||||
|
let url = sanitize_url(&self.base_url, path, params);
|
||||||
|
self.reqwest_client.patch(url).json(json_body)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_patch_request<B, K, V, E>(
|
||||||
|
&self,
|
||||||
|
path: PathSegments<'_>,
|
||||||
|
params: Params<'_, K, V>,
|
||||||
|
json_body: &B,
|
||||||
|
) -> Result<Response, HttpClientError<E>>
|
||||||
|
where
|
||||||
|
B: Serialize + ?Sized,
|
||||||
|
K: AsRef<str>,
|
||||||
|
V: AsRef<str>,
|
||||||
|
E: Display,
|
||||||
|
{
|
||||||
|
let url = sanitize_url(&self.base_url, path, params);
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
{
|
||||||
|
Ok(wasmtimer::tokio::timeout(
|
||||||
|
self.request_timeout,
|
||||||
|
self.reqwest_client.patch(url).json(json_body).send(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_timeout| HttpClientError::RequestTimeout)??)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
{
|
||||||
|
Ok(self
|
||||||
|
.reqwest_client
|
||||||
|
.patch(url)
|
||||||
|
.json(json_body)
|
||||||
|
.send()
|
||||||
|
.await?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[instrument(level = "debug", skip_all)]
|
#[instrument(level = "debug", skip_all)]
|
||||||
pub async fn get_json<T, K, V, E>(
|
pub async fn get_json<T, K, V, E>(
|
||||||
&self,
|
&self,
|
||||||
@@ -372,6 +422,23 @@ impl Client {
|
|||||||
parse_response(res, false).await
|
parse_response(res, false).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn patch_json<B, T, K, V, E>(
|
||||||
|
&self,
|
||||||
|
path: PathSegments<'_>,
|
||||||
|
params: Params<'_, K, V>,
|
||||||
|
json_body: &B,
|
||||||
|
) -> Result<T, HttpClientError<E>>
|
||||||
|
where
|
||||||
|
B: Serialize + ?Sized,
|
||||||
|
for<'a> T: Deserialize<'a>,
|
||||||
|
K: AsRef<str>,
|
||||||
|
V: AsRef<str>,
|
||||||
|
E: Display + DeserializeOwned,
|
||||||
|
{
|
||||||
|
let res = self.send_patch_request(path, params, json_body).await?;
|
||||||
|
parse_response(res, true).await
|
||||||
|
}
|
||||||
|
|
||||||
#[instrument(level = "debug", skip_all)]
|
#[instrument(level = "debug", skip_all)]
|
||||||
pub async fn get_json_endpoint<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
|
pub async fn get_json_endpoint<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
|
||||||
where
|
where
|
||||||
@@ -466,6 +533,42 @@ impl Client {
|
|||||||
|
|
||||||
parse_response(res, false).await
|
parse_response(res, false).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn patch_json_endpoint<B, T, S, E>(
|
||||||
|
&self,
|
||||||
|
endpoint: S,
|
||||||
|
json_body: &B,
|
||||||
|
) -> Result<T, HttpClientError<E>>
|
||||||
|
where
|
||||||
|
B: Serialize + ?Sized,
|
||||||
|
for<'a> T: Deserialize<'a>,
|
||||||
|
E: Display + DeserializeOwned,
|
||||||
|
S: AsRef<str>,
|
||||||
|
{
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
let res = {
|
||||||
|
wasmtimer::tokio::timeout(
|
||||||
|
self.request_timeout,
|
||||||
|
self.reqwest_client
|
||||||
|
.patch(self.base_url.join(endpoint.as_ref())?)
|
||||||
|
.json(json_body)
|
||||||
|
.send(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_timeout| HttpClientError::RequestTimeout)??
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
let res = {
|
||||||
|
self.reqwest_client
|
||||||
|
.patch(self.base_url.join(endpoint.as_ref())?)
|
||||||
|
.json(json_body)
|
||||||
|
.send()
|
||||||
|
.await?
|
||||||
|
};
|
||||||
|
|
||||||
|
parse_response(res, true).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// define those methods on the trait for nicer extensions (and not having to type the thing twice)
|
// define those methods on the trait for nicer extensions (and not having to type the thing twice)
|
||||||
|
|||||||
@@ -57,3 +57,14 @@ pub mod wireguard {
|
|||||||
pub const WG_TUN_DEVICE_IP_ADDRESS_V6: Ipv6Addr = Ipv6Addr::new(0xfc01, 0, 0, 0, 0, 0, 0, 0x1); // fc01::1
|
pub const WG_TUN_DEVICE_IP_ADDRESS_V6: Ipv6Addr = Ipv6Addr::new(0xfc01, 0, 0, 0, 0, 0, 0, 0x1); // fc01::1
|
||||||
pub const WG_TUN_DEVICE_NETMASK_V6: u8 = 112;
|
pub const WG_TUN_DEVICE_NETMASK_V6: u8 = 112;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod mixnet_vpn {
|
||||||
|
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||||
|
|
||||||
|
// The interface used to route traffic
|
||||||
|
pub const NYM_TUN_BASE_NAME: &str = "nymtun";
|
||||||
|
pub const NYM_TUN_DEVICE_ADDRESS_V4: Ipv4Addr = Ipv4Addr::new(10, 0, 0, 1);
|
||||||
|
pub const NYM_TUN_DEVICE_NETMASK_V4: Ipv4Addr = Ipv4Addr::new(255, 255, 0, 0);
|
||||||
|
pub const NYM_TUN_DEVICE_ADDRESS_V6: Ipv6Addr = Ipv6Addr::new(0xfc00, 0, 0, 0, 0, 0, 0, 0x1); // fc00::1
|
||||||
|
pub const NYM_TUN_DEVICE_NETMASK_V6: &str = "112";
|
||||||
|
}
|
||||||
|
|||||||
@@ -65,6 +65,14 @@ impl<T> NonExhaustiveDelayQueue<T> {
|
|||||||
pub fn remove(&mut self, key: &QueueKey) -> Expired<T> {
|
pub fn remove(&mut self, key: &QueueKey) -> Expired<T> {
|
||||||
self.inner.remove(key)
|
self.inner.remove(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.inner.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.inner.is_empty()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Default for NonExhaustiveDelayQueue<T> {
|
impl<T> Default for NonExhaustiveDelayQueue<T> {
|
||||||
|
|||||||
@@ -12,6 +12,6 @@ license.workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
prometheus = { workspace = true }
|
prometheus = { workspace = true }
|
||||||
log = { workspace = true }
|
tracing = { workspace = true }
|
||||||
dashmap = { workspace = true }
|
dashmap = { workspace = true }
|
||||||
lazy_static = { workspace = true }
|
lazy_static = { workspace = true }
|
||||||
|
|||||||
+390
-81
@@ -1,14 +1,18 @@
|
|||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
pub use log::error;
|
|
||||||
use log::{debug, warn};
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
pub use std::time::Instant;
|
use tracing::{debug, error, warn};
|
||||||
|
|
||||||
use prometheus::{core::Collector, Encoder as _, IntCounter, IntGauge, Registry, TextEncoder};
|
use prometheus::{
|
||||||
|
core::Collector, Encoder as _, Gauge, Histogram, HistogramOpts, IntCounter, IntGauge, Registry,
|
||||||
|
TextEncoder,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub use prometheus::HistogramTimer;
|
||||||
|
pub use std::time::Instant;
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! prepend_package_name {
|
macro_rules! prepend_package_name {
|
||||||
($name: literal) => {
|
($name: tt) => {
|
||||||
&format!(
|
&format!(
|
||||||
"{}_{}",
|
"{}_{}",
|
||||||
std::module_path!()
|
std::module_path!()
|
||||||
@@ -23,15 +27,29 @@ macro_rules! prepend_package_name {
|
|||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! inc_by {
|
macro_rules! inc_by {
|
||||||
|
($name:literal, $x:expr, $help: expr) => {
|
||||||
|
$crate::REGISTRY.maybe_register_and_inc_by(
|
||||||
|
$crate::prepend_package_name!($name),
|
||||||
|
$x as i64,
|
||||||
|
$help,
|
||||||
|
);
|
||||||
|
};
|
||||||
($name:literal, $x:expr) => {
|
($name:literal, $x:expr) => {
|
||||||
$crate::REGISTRY.inc_by($crate::prepend_package_name!($name), $x as i64);
|
$crate::REGISTRY.maybe_register_and_inc_by(
|
||||||
|
$crate::prepend_package_name!($name),
|
||||||
|
$x as i64,
|
||||||
|
None,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! inc {
|
macro_rules! inc {
|
||||||
|
($name:literal, $help: expr) => {
|
||||||
|
$crate::REGISTRY.maybe_register_and_inc($crate::prepend_package_name!($name), $help);
|
||||||
|
};
|
||||||
($name:literal) => {
|
($name:literal) => {
|
||||||
$crate::REGISTRY.inc($crate::prepend_package_name!($name));
|
$crate::REGISTRY.maybe_register_and_inc($crate::prepend_package_name!($name), None);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,6 +60,71 @@ macro_rules! metrics {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! set_metric {
|
||||||
|
($name:literal, $x:expr, $help: expr) => {
|
||||||
|
$crate::REGISTRY.maybe_register_and_set(
|
||||||
|
$crate::prepend_package_name!($name),
|
||||||
|
$x as i64,
|
||||||
|
$help,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
($name:literal, $x:expr) => {
|
||||||
|
$crate::REGISTRY.maybe_register_and_set(
|
||||||
|
$crate::prepend_package_name!($name),
|
||||||
|
$x as i64,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! set_metric_float {
|
||||||
|
($name:literal, $x:expr, $help: expr) => {
|
||||||
|
$crate::REGISTRY.maybe_register_and_set_float(
|
||||||
|
$crate::prepend_package_name!($name),
|
||||||
|
$x as f64,
|
||||||
|
$help,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
($name:literal, $x:expr) => {
|
||||||
|
$crate::REGISTRY.maybe_register_and_set_float(
|
||||||
|
$crate::prepend_package_name!($name),
|
||||||
|
$x as f64,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! add_histogram_obs {
|
||||||
|
($name:expr, $x:expr, $b:expr, $help:expr) => {
|
||||||
|
$crate::REGISTRY.maybe_register_and_add_to_histogram(
|
||||||
|
$crate::prepend_package_name!($name),
|
||||||
|
$x as f64,
|
||||||
|
Some($b),
|
||||||
|
$help,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
($name:expr, $x:expr, $b:expr) => {
|
||||||
|
$crate::REGISTRY.maybe_register_and_add_to_histogram(
|
||||||
|
$crate::prepend_package_name!($name),
|
||||||
|
$x as f64,
|
||||||
|
Some($b),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
($name:expr, $x:expr) => {
|
||||||
|
$crate::REGISTRY.maybe_register_and_add_to_histogram(
|
||||||
|
$crate::prepend_package_name!($name),
|
||||||
|
$x as f64,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! nanos {
|
macro_rules! nanos {
|
||||||
( $name:literal, $x:expr ) => {{
|
( $name:literal, $x:expr ) => {{
|
||||||
@@ -50,7 +133,7 @@ macro_rules! nanos {
|
|||||||
let r = $x;
|
let r = $x;
|
||||||
let duration = start.elapsed().as_nanos() as i64;
|
let duration = start.elapsed().as_nanos() as i64;
|
||||||
let name = $crate::prepend_package_name!($name);
|
let name = $crate::prepend_package_name!($name);
|
||||||
$crate::REGISTRY.inc_by(&format!("{}_nanos", $name), duration);
|
$crate::REGISTRY.maybe_register_and_inc_by(&format!("{}_nanos", $name), duration, None);
|
||||||
r
|
r
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
@@ -59,15 +142,100 @@ lazy_static::lazy_static! {
|
|||||||
pub static ref REGISTRY: MetricsController = MetricsController::default();
|
pub static ref REGISTRY: MetricsController = MetricsController::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn metrics_registry() -> &'static MetricsController {
|
||||||
|
®ISTRY
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct MetricsController {
|
pub struct MetricsController {
|
||||||
registry: Registry,
|
registry: Registry,
|
||||||
registry_index: DashMap<String, Metric>,
|
registry_index: DashMap<String, Metric>,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Metric {
|
pub enum Metric {
|
||||||
C(Box<IntCounter>),
|
IntCounter(Box<IntCounter>),
|
||||||
G(Box<IntGauge>),
|
IntGauge(Box<IntGauge>),
|
||||||
|
FloatGauge(Box<Gauge>),
|
||||||
|
Histogram(Box<Histogram>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Metric {
|
||||||
|
pub fn new_int_counter(name: &str, help: &str) -> Option<Self> {
|
||||||
|
match IntCounter::new(sanitize_metric_name(name), help) {
|
||||||
|
Ok(c) => Some(c.into()),
|
||||||
|
Err(err) => {
|
||||||
|
error!("Failed to create counter {name:?}: {err}");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_int_gauge(name: &str, help: &str) -> Option<Self> {
|
||||||
|
match IntGauge::new(sanitize_metric_name(name), help) {
|
||||||
|
Ok(g) => Some(g.into()),
|
||||||
|
Err(err) => {
|
||||||
|
error!("Failed to create gauge {name:?}: {err}");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_float_gauge(name: &str, help: &str) -> Option<Self> {
|
||||||
|
match Gauge::new(sanitize_metric_name(name), help) {
|
||||||
|
Ok(g) => Some(g.into()),
|
||||||
|
Err(err) => {
|
||||||
|
error!("Failed to create gauge {name:?}: {err}");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_histogram(name: &str, help: &str, buckets: Option<&[f64]>) -> Option<Self> {
|
||||||
|
let mut opts = HistogramOpts::new(sanitize_metric_name(name), help);
|
||||||
|
if let Some(buckets) = buckets {
|
||||||
|
opts = opts.buckets(buckets.to_vec())
|
||||||
|
}
|
||||||
|
match Histogram::with_opts(opts) {
|
||||||
|
Ok(h) => Some(Metric::Histogram(Box::new(h))),
|
||||||
|
Err(err) => {
|
||||||
|
error!("failed to create histogram {name:?}: {err}");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_collector(&self) -> Box<dyn Collector> {
|
||||||
|
match self {
|
||||||
|
Metric::IntCounter(c) => c.clone(),
|
||||||
|
Metric::IntGauge(g) => g.clone(),
|
||||||
|
Metric::FloatGauge(g) => g.clone(),
|
||||||
|
Metric::Histogram(h) => h.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<IntCounter> for Metric {
|
||||||
|
fn from(v: IntCounter) -> Self {
|
||||||
|
Metric::IntCounter(Box::new(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<IntGauge> for Metric {
|
||||||
|
fn from(v: IntGauge) -> Self {
|
||||||
|
Metric::IntGauge(Box::new(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Gauge> for Metric {
|
||||||
|
fn from(v: Gauge) -> Self {
|
||||||
|
Metric::FloatGauge(Box::new(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Histogram> for Metric {
|
||||||
|
fn from(v: Histogram) -> Self {
|
||||||
|
Metric::Histogram(Box::new(v))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fq_name(c: &dyn Collector) -> String {
|
fn fq_name(c: &dyn Collector) -> String {
|
||||||
@@ -81,34 +249,92 @@ impl Metric {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn fq_name(&self) -> String {
|
fn fq_name(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
Metric::C(c) => fq_name(c.as_ref()),
|
Metric::IntCounter(c) => fq_name(c.as_ref()),
|
||||||
Metric::G(g) => fq_name(g.as_ref()),
|
Metric::IntGauge(g) => fq_name(g.as_ref()),
|
||||||
|
Metric::FloatGauge(g) => fq_name(g.as_ref()),
|
||||||
|
Metric::Histogram(h) => fq_name(h.as_ref()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn inc(&self) {
|
fn inc(&self) {
|
||||||
match self {
|
match self {
|
||||||
Metric::C(c) => c.inc(),
|
Metric::IntCounter(c) => c.inc(),
|
||||||
Metric::G(g) => g.inc(),
|
Metric::IntGauge(g) => g.inc(),
|
||||||
|
Metric::FloatGauge(g) => g.inc(),
|
||||||
|
Metric::Histogram(_) => {
|
||||||
|
warn!("invalid operation: attempted to call increment on a histogram")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn inc_by(&self, value: i64) {
|
fn inc_by(&self, value: i64) {
|
||||||
match self {
|
match self {
|
||||||
Metric::C(c) => c.inc_by(value as u64),
|
Metric::IntCounter(c) => c.inc_by(value as u64),
|
||||||
Metric::G(g) => g.add(value),
|
Metric::IntGauge(g) => g.add(value),
|
||||||
|
Metric::FloatGauge(g) => {
|
||||||
|
warn!("attempted to increment a float gauge ('{}') by an integer - this is most likely a bug", self.fq_name());
|
||||||
|
g.add(value as f64)
|
||||||
|
}
|
||||||
|
Metric::Histogram(_) => {
|
||||||
|
warn!("invalid operation: attempted to call increment on a histogram")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn set(&self, value: i64) {
|
fn set(&self, value: i64) {
|
||||||
match self {
|
match self {
|
||||||
Metric::C(_c) => {
|
Metric::IntCounter(_c) => {
|
||||||
warn!("Cannot set value for counter {:?}", self.fq_name());
|
warn!("Cannot set value for counter {:?}", self.fq_name());
|
||||||
}
|
}
|
||||||
Metric::G(g) => g.set(value),
|
Metric::IntGauge(g) => g.set(value),
|
||||||
|
Metric::FloatGauge(g) => {
|
||||||
|
warn!("attempted to set a float gauge ('{}') to an integer value - this is most likely a bug", self.fq_name());
|
||||||
|
g.set(value as f64)
|
||||||
|
}
|
||||||
|
Metric::Histogram(_) => {
|
||||||
|
warn!("invalid operation: attempted to call set on a histogram")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn set_float(&self, value: f64) {
|
||||||
|
match self {
|
||||||
|
Metric::IntCounter(_c) => {
|
||||||
|
warn!("Cannot set value for counter {:?}", self.fq_name());
|
||||||
|
}
|
||||||
|
Metric::IntGauge(g) => {
|
||||||
|
warn!("attempted to set a integer gauge ('{}') to a float value - this is most likely a bug", self.fq_name());
|
||||||
|
g.set(value as i64)
|
||||||
|
}
|
||||||
|
Metric::FloatGauge(g) => g.set(value),
|
||||||
|
Metric::Histogram(_) => {
|
||||||
|
warn!("invalid operation: attempted to call increment on a histogram")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn add_histogram_observation(&self, value: f64) {
|
||||||
|
match self {
|
||||||
|
Metric::Histogram(h) => {
|
||||||
|
h.observe(value);
|
||||||
|
}
|
||||||
|
_ => warn!("attempted to add histogram observation on a non-histogram metric"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn start_timer(&self) -> Option<HistogramTimer> {
|
||||||
|
match self {
|
||||||
|
Metric::Histogram(h) => Some(h.start_timer()),
|
||||||
|
_ => {
|
||||||
|
warn!("attempted to start histogram observation on a non-histogram metric");
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,93 +371,165 @@ impl MetricsController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set(&self, name: &str, value: i64) {
|
pub fn register_int_gauge<'a>(&self, name: &str, help: impl Into<Option<&'a str>>) {
|
||||||
|
let Some(metric) = Metric::new_int_gauge(name, help.into().unwrap_or(name)) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
self.register_metric(metric);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register_float_gauge<'a>(&self, name: &str, help: impl Into<Option<&'a str>>) {
|
||||||
|
let Some(metric) = Metric::new_float_gauge(name, help.into().unwrap_or(name)) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
self.register_metric(metric);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register_int_counter<'a>(&self, name: &str, help: impl Into<Option<&'a str>>) {
|
||||||
|
let Some(metric) = Metric::new_int_counter(name, help.into().unwrap_or(name)) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
self.register_metric(metric);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register_histogram<'a>(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
help: impl Into<Option<&'a str>>,
|
||||||
|
buckets: Option<&[f64]>,
|
||||||
|
) {
|
||||||
|
let Some(metric) = Metric::new_histogram(name, help.into().unwrap_or(name), buckets) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
self.register_metric(metric);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(&self, name: &str, value: i64) -> bool {
|
||||||
if let Some(metric) = self.registry_index.get(name) {
|
if let Some(metric) = self.registry_index.get(name) {
|
||||||
metric.set(value);
|
metric.set(value);
|
||||||
|
true
|
||||||
} else {
|
} else {
|
||||||
let gauge = match IntGauge::new(sanitize_metric_name(name), name) {
|
false
|
||||||
Ok(g) => g,
|
|
||||||
Err(e) => {
|
|
||||||
debug!("Failed to create gauge {:?}:\n{}", name, e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.register_gauge(Box::new(gauge));
|
|
||||||
self.set(name, value)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inc(&self, name: &str) {
|
pub fn set_float(&self, name: &str, value: f64) -> bool {
|
||||||
|
if let Some(metric) = self.registry_index.get(name) {
|
||||||
|
metric.set_float(value);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_to_histogram(&self, name: &str, value: f64) -> bool {
|
||||||
|
if let Some(metric) = self.registry_index.get(name) {
|
||||||
|
metric.add_histogram_observation(value);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start_timer(&self, name: &str) -> Option<HistogramTimer> {
|
||||||
|
self.registry_index
|
||||||
|
.get(name)
|
||||||
|
.and_then(|metric| metric.start_timer())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inc(&self, name: &str) -> bool {
|
||||||
if let Some(metric) = self.registry_index.get(name) {
|
if let Some(metric) = self.registry_index.get(name) {
|
||||||
metric.inc();
|
metric.inc();
|
||||||
|
true
|
||||||
} else {
|
} else {
|
||||||
let counter = match IntCounter::new(sanitize_metric_name(name), name) {
|
false
|
||||||
Ok(c) => c,
|
|
||||||
Err(e) => {
|
|
||||||
debug!("Failed to create counter {:?}:\n{}", name, e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.register_counter(Box::new(counter));
|
|
||||||
self.inc(name)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inc_by(&self, name: &str, value: i64) {
|
pub fn inc_by(&self, name: &str, value: i64) -> bool {
|
||||||
if let Some(metric) = self.registry_index.get(name) {
|
if let Some(metric) = self.registry_index.get(name) {
|
||||||
metric.inc_by(value);
|
metric.inc_by(value);
|
||||||
|
true
|
||||||
} else {
|
} else {
|
||||||
let counter = match IntCounter::new(sanitize_metric_name(name), name) {
|
false
|
||||||
Ok(c) => c,
|
|
||||||
Err(e) => {
|
|
||||||
debug!("Failed to create counter {:?}:\n{}", name, e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.register_counter(Box::new(counter));
|
|
||||||
self.inc_by(name, value)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn register_gauge(&self, metric: Box<IntGauge>) {
|
pub fn maybe_register_and_set<'a>(
|
||||||
let fq_name = metric
|
&self,
|
||||||
.desc()
|
name: &str,
|
||||||
.first()
|
value: i64,
|
||||||
.map(|d| d.fq_name.clone())
|
help: impl Into<Option<&'a str>>,
|
||||||
.unwrap_or_default();
|
) {
|
||||||
|
if !self.set(name, value) {
|
||||||
|
let help = help.into();
|
||||||
|
self.register_int_gauge(name, help);
|
||||||
|
self.set(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn maybe_register_and_set_float<'a>(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
value: f64,
|
||||||
|
help: impl Into<Option<&'a str>>,
|
||||||
|
) {
|
||||||
|
if !self.set_float(name, value) {
|
||||||
|
let help = help.into();
|
||||||
|
self.register_float_gauge(name, help);
|
||||||
|
self.set_float(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn maybe_register_and_add_to_histogram<'a>(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
value: f64,
|
||||||
|
buckets: Option<&[f64]>,
|
||||||
|
help: impl Into<Option<&'a str>>,
|
||||||
|
) {
|
||||||
|
if !self.add_to_histogram(name, value) {
|
||||||
|
let help = help.into();
|
||||||
|
self.register_histogram(name, help, buckets);
|
||||||
|
self.add_to_histogram(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn maybe_register_and_inc<'a>(&self, name: &str, help: impl Into<Option<&'a str>>) {
|
||||||
|
if !self.inc(name) {
|
||||||
|
let help = help.into();
|
||||||
|
self.register_int_counter(name, help);
|
||||||
|
self.inc(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn maybe_register_and_inc_by<'a>(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
value: i64,
|
||||||
|
help: impl Into<Option<&'a str>>,
|
||||||
|
) {
|
||||||
|
if !self.inc_by(name, value) {
|
||||||
|
let help = help.into();
|
||||||
|
self.register_int_counter(name, help);
|
||||||
|
self.inc_by(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register_metric(&self, metric: impl Into<Metric>) {
|
||||||
|
let m = metric.into();
|
||||||
|
let fq_name = m.fq_name();
|
||||||
|
|
||||||
if self.registry_index.contains_key(&fq_name) {
|
if self.registry_index.contains_key(&fq_name) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.registry.register(metric.clone()) {
|
match self.registry.register(m.as_collector()) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
self.registry_index
|
self.registry_index.insert(fq_name, m);
|
||||||
.insert(fq_name, Metric::G(metric.clone()));
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(err) => {
|
||||||
debug!("Failed to register {:?}:\n{}", fq_name, e)
|
debug!("Failed to register '{fq_name}': {err}")
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn register_counter(&self, metric: Box<IntCounter>) {
|
|
||||||
let fq_name = metric
|
|
||||||
.desc()
|
|
||||||
.first()
|
|
||||||
.map(|d| d.fq_name.clone())
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
if self.registry_index.contains_key(&fq_name) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
match self.registry.register(metric.clone()) {
|
|
||||||
Ok(_) => {
|
|
||||||
self.registry_index
|
|
||||||
.insert(fq_name, Metric::C(metric.clone()));
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
debug!("Failed to register {:?}:\n{}", fq_name, e)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -275,4 +573,15 @@ mod tests {
|
|||||||
"packets_sent_34_242_65_133:1789"
|
"packets_sent_34_242_65_133:1789"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn prepend_package_name() {
|
||||||
|
let literal = prepend_package_name!("foo");
|
||||||
|
assert_eq!(literal, "nym_metrics_foo");
|
||||||
|
|
||||||
|
let bar = "bar";
|
||||||
|
let format = format!("foomp_{}", bar);
|
||||||
|
let formatted = prepend_package_name!(format);
|
||||||
|
assert_eq!(formatted, "nym_metrics_foomp_bar");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,10 +115,7 @@ pub fn aggregate_signatures(
|
|||||||
let params = ecash_group_parameters();
|
let params = ecash_group_parameters();
|
||||||
// aggregate the signature
|
// aggregate the signature
|
||||||
|
|
||||||
let signature = match Aggregatable::aggregate(signatures, indices) {
|
let signature = Aggregatable::aggregate(signatures, indices)?;
|
||||||
Ok(res) => res,
|
|
||||||
Err(err) => return Err(err),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Ensure the aggregated signature is not an infinity point
|
// Ensure the aggregated signature is not an infinity point
|
||||||
if bool::from(signature.is_at_infinity()) {
|
if bool::from(signature.is_at_infinity()) {
|
||||||
|
|||||||
@@ -13,7 +13,11 @@ use crate::{
|
|||||||
models::{CommitSignature, Validator},
|
models::{CommitSignature, Validator},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use sqlx::{types::time::OffsetDateTime, ConnectOptions, Sqlite, Transaction};
|
use sqlx::{
|
||||||
|
sqlite::{SqliteAutoVacuum, SqliteSynchronous},
|
||||||
|
types::time::OffsetDateTime,
|
||||||
|
ConnectOptions, Sqlite, Transaction,
|
||||||
|
};
|
||||||
use std::{fmt::Debug, path::Path};
|
use std::{fmt::Debug, path::Path};
|
||||||
use tendermint::{
|
use tendermint::{
|
||||||
block::{Commit, CommitSig},
|
block::{Commit, CommitSig},
|
||||||
@@ -51,6 +55,9 @@ impl ScraperStorage {
|
|||||||
#[instrument]
|
#[instrument]
|
||||||
pub async fn init<P: AsRef<Path> + Debug>(database_path: P) -> Result<Self, ScraperError> {
|
pub async fn init<P: AsRef<Path> + Debug>(database_path: P) -> Result<Self, ScraperError> {
|
||||||
let opts = sqlx::sqlite::SqliteConnectOptions::new()
|
let opts = sqlx::sqlite::SqliteConnectOptions::new()
|
||||||
|
.journal_mode(sqlx::sqlite::SqliteJournalMode::Wal)
|
||||||
|
.synchronous(SqliteSynchronous::Normal)
|
||||||
|
.auto_vacuum(SqliteAutoVacuum::Incremental)
|
||||||
.filename(database_path)
|
.filename(database_path)
|
||||||
.create_if_missing(true)
|
.create_if_missing(true)
|
||||||
.disable_statement_logging();
|
.disable_statement_logging();
|
||||||
|
|||||||
@@ -428,7 +428,7 @@ impl PacketStatisticsControl {
|
|||||||
while self
|
while self
|
||||||
.history
|
.history
|
||||||
.front()
|
.front()
|
||||||
.map_or(false, |&(t, _)| t < recording_window)
|
.is_some_and(|&(t, _)| t < recording_window)
|
||||||
{
|
{
|
||||||
self.history.pop_front();
|
self.history.pop_front();
|
||||||
}
|
}
|
||||||
@@ -462,7 +462,7 @@ impl PacketStatisticsControl {
|
|||||||
while self
|
while self
|
||||||
.rates
|
.rates
|
||||||
.front()
|
.front()
|
||||||
.map_or(false, |&(t, _)| t < recording_window)
|
.is_some_and(|&(t, _)| t < recording_window)
|
||||||
{
|
{
|
||||||
self.rates.pop_front();
|
self.rates.pop_front();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,10 @@ pub enum GatewaySessionEvent {
|
|||||||
/// Address of the remote client opening the connection
|
/// Address of the remote client opening the connection
|
||||||
client: DestinationAddressBytes,
|
client: DestinationAddressBytes,
|
||||||
},
|
},
|
||||||
|
SessionDelete {
|
||||||
|
/// Address of the remote client opening the connection
|
||||||
|
client: DestinationAddressBytes,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GatewaySessionEvent {
|
impl GatewaySessionEvent {
|
||||||
@@ -87,4 +91,8 @@ impl GatewaySessionEvent {
|
|||||||
client,
|
client,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_session_delete(client: DestinationAddressBytes) -> GatewaySessionEvent {
|
||||||
|
GatewaySessionEvent::SessionDelete { client }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,3 +36,4 @@ nym-gateway-storage = { path = "../gateway-storage" }
|
|||||||
nym-network-defaults = { path = "../network-defaults" }
|
nym-network-defaults = { path = "../network-defaults" }
|
||||||
nym-task = { path = "../task" }
|
nym-task = { path = "../task" }
|
||||||
nym-wireguard-types = { path = "../wireguard-types" }
|
nym-wireguard-types = { path = "../wireguard-types" }
|
||||||
|
nym-node-metrics = { path = "../../nym-node/nym-node-metrics" }
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ pub struct WireguardData {
|
|||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub async fn start_wireguard(
|
pub async fn start_wireguard(
|
||||||
storage: nym_gateway_storage::GatewayStorage,
|
storage: nym_gateway_storage::GatewayStorage,
|
||||||
|
metrics: nym_node_metrics::NymNodeMetrics,
|
||||||
all_peers: Vec<nym_gateway_storage::models::WireguardPeer>,
|
all_peers: Vec<nym_gateway_storage::models::WireguardPeer>,
|
||||||
task_client: nym_task::TaskClient,
|
task_client: nym_task::TaskClient,
|
||||||
wireguard_data: WireguardData,
|
wireguard_data: WireguardData,
|
||||||
@@ -175,6 +176,7 @@ pub async fn start_wireguard(
|
|||||||
let wg_api = std::sync::Arc::new(WgApiWrapper::new(wg_api));
|
let wg_api = std::sync::Arc::new(WgApiWrapper::new(wg_api));
|
||||||
let mut controller = PeerController::new(
|
let mut controller = PeerController::new(
|
||||||
storage,
|
storage,
|
||||||
|
metrics,
|
||||||
wg_api.clone(),
|
wg_api.clone(),
|
||||||
host,
|
host,
|
||||||
peer_bandwidth_managers,
|
peer_bandwidth_managers,
|
||||||
|
|||||||
@@ -16,7 +16,9 @@ use nym_credential_verification::{
|
|||||||
ClientBandwidth,
|
ClientBandwidth,
|
||||||
};
|
};
|
||||||
use nym_gateway_storage::GatewayStorage;
|
use nym_gateway_storage::GatewayStorage;
|
||||||
|
use nym_node_metrics::NymNodeMetrics;
|
||||||
use nym_wireguard_types::DEFAULT_PEER_TIMEOUT_CHECK;
|
use nym_wireguard_types::DEFAULT_PEER_TIMEOUT_CHECK;
|
||||||
|
use std::time::{Duration, SystemTime};
|
||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{collections::HashMap, sync::Arc};
|
||||||
use tokio::sync::{mpsc, RwLock};
|
use tokio::sync::{mpsc, RwLock};
|
||||||
use tokio_stream::{wrappers::IntervalStream, StreamExt};
|
use tokio_stream::{wrappers::IntervalStream, StreamExt};
|
||||||
@@ -65,6 +67,11 @@ pub struct QueryBandwidthControlResponse {
|
|||||||
|
|
||||||
pub struct PeerController {
|
pub struct PeerController {
|
||||||
storage: GatewayStorage,
|
storage: GatewayStorage,
|
||||||
|
|
||||||
|
// we have "all" metrics of a node, but they're behind a single Arc pointer,
|
||||||
|
// so the overhead is minimal
|
||||||
|
metrics: NymNodeMetrics,
|
||||||
|
|
||||||
// used to receive commands from individual handles too
|
// used to receive commands from individual handles too
|
||||||
request_tx: mpsc::Sender<PeerControlRequest>,
|
request_tx: mpsc::Sender<PeerControlRequest>,
|
||||||
request_rx: mpsc::Receiver<PeerControlRequest>,
|
request_rx: mpsc::Receiver<PeerControlRequest>,
|
||||||
@@ -76,8 +83,10 @@ pub struct PeerController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PeerController {
|
impl PeerController {
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
storage: GatewayStorage,
|
storage: GatewayStorage,
|
||||||
|
metrics: NymNodeMetrics,
|
||||||
wg_api: Arc<WgApiWrapper>,
|
wg_api: Arc<WgApiWrapper>,
|
||||||
initial_host_information: Host,
|
initial_host_information: Host,
|
||||||
bw_storage_managers: HashMap<Key, (Option<SharedBandwidthStorageManager>, Peer)>,
|
bw_storage_managers: HashMap<Key, (Option<SharedBandwidthStorageManager>, Peer)>,
|
||||||
@@ -123,6 +132,7 @@ impl PeerController {
|
|||||||
request_rx,
|
request_rx,
|
||||||
timeout_check_interval,
|
timeout_check_interval,
|
||||||
task_client,
|
task_client,
|
||||||
|
metrics,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,6 +267,55 @@ impl PeerController {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn update_metrics(&self, new_host: &Host) {
|
||||||
|
let now = SystemTime::now();
|
||||||
|
const ACTIVITY_THRESHOLD: Duration = Duration::from_secs(60);
|
||||||
|
|
||||||
|
let old_host = self.host_information.read().await;
|
||||||
|
|
||||||
|
let total_peers = new_host.peers.len();
|
||||||
|
let mut active_peers = 0;
|
||||||
|
let mut new_rx = 0;
|
||||||
|
let mut new_tx = 0;
|
||||||
|
|
||||||
|
for (peer_key, peer) in new_host.peers.iter() {
|
||||||
|
// only consider pre-existing peers,
|
||||||
|
// so that the value would always be increasing
|
||||||
|
if let Some(prior) = old_host.peers.get(peer_key) {
|
||||||
|
let delta_rx = peer.rx_bytes.saturating_sub(prior.rx_bytes);
|
||||||
|
let delta_tx = prior.tx_bytes.saturating_sub(prior.tx_bytes);
|
||||||
|
|
||||||
|
new_rx += delta_rx;
|
||||||
|
new_tx += delta_tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if a peer hasn't performed a handshake in last minute,
|
||||||
|
// I think it's reasonable to assume it's no longer active
|
||||||
|
let Some(last_handshake) = peer.last_handshake else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Ok(elapsed) = now.duration_since(last_handshake) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if elapsed < ACTIVITY_THRESHOLD {
|
||||||
|
active_peers += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.metrics.wireguard.update(
|
||||||
|
// if the conversion fails it means we're running not running on a 64bit system
|
||||||
|
// and that's a reason enough for this failure.
|
||||||
|
new_rx.try_into().expect(
|
||||||
|
"failed to convert bytes from u64 to usize - are you running on non 64bit system?",
|
||||||
|
),
|
||||||
|
new_tx.try_into().expect(
|
||||||
|
"failed to convert bytes from u64 to usize - are you running on non 64bit system?",
|
||||||
|
),
|
||||||
|
total_peers,
|
||||||
|
active_peers,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn run(&mut self) {
|
pub async fn run(&mut self) {
|
||||||
info!("started wireguard peer controller");
|
info!("started wireguard peer controller");
|
||||||
loop {
|
loop {
|
||||||
@@ -266,6 +325,8 @@ impl PeerController {
|
|||||||
log::error!("Can't read wireguard kernel data");
|
log::error!("Can't read wireguard kernel data");
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
self.update_metrics(&host).await;
|
||||||
|
|
||||||
*self.host_information.write().await = host;
|
*self.host_information.write().await = host;
|
||||||
}
|
}
|
||||||
_ = self.task_client.recv() => {
|
_ = self.task_client.recv() => {
|
||||||
|
|||||||
@@ -464,10 +464,7 @@ pub fn instantiate_contracts(
|
|||||||
mixnet_contract_address.clone(),
|
mixnet_contract_address.clone(),
|
||||||
&nym_mixnet_contract_common::MigrateMsg {
|
&nym_mixnet_contract_common::MigrateMsg {
|
||||||
vesting_contract_address: Some(vesting_contract_address.to_string()),
|
vesting_contract_address: Some(vesting_contract_address.to_string()),
|
||||||
current_nym_node_semver: "1.1.10".to_string(),
|
|
||||||
version_score_weights: Default::default(),
|
|
||||||
unsafe_skip_state_updates: Some(true),
|
unsafe_skip_state_updates: Some(true),
|
||||||
version_score_params: Default::default(),
|
|
||||||
},
|
},
|
||||||
mixnet_code_id,
|
mixnet_code_id,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3470,43 +3470,13 @@
|
|||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
"title": "MigrateMsg",
|
"title": "MigrateMsg",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
|
||||||
"current_nym_node_semver"
|
|
||||||
],
|
|
||||||
"properties": {
|
"properties": {
|
||||||
"current_nym_node_semver": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"unsafe_skip_state_updates": {
|
"unsafe_skip_state_updates": {
|
||||||
"type": [
|
"type": [
|
||||||
"boolean",
|
"boolean",
|
||||||
"null"
|
"null"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"version_score_params": {
|
|
||||||
"default": {
|
|
||||||
"penalty": "0.995",
|
|
||||||
"penalty_scaling": "1.65"
|
|
||||||
},
|
|
||||||
"allOf": [
|
|
||||||
{
|
|
||||||
"$ref": "#/definitions/VersionScoreFormulaParams"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"version_score_weights": {
|
|
||||||
"default": {
|
|
||||||
"major": 100,
|
|
||||||
"minor": 10,
|
|
||||||
"patch": 1,
|
|
||||||
"prerelease": 1
|
|
||||||
},
|
|
||||||
"allOf": [
|
|
||||||
{
|
|
||||||
"$ref": "#/definitions/OutdatedVersionWeights"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"vesting_contract_address": {
|
"vesting_contract_address": {
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
@@ -3514,63 +3484,7 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false
|
||||||
"definitions": {
|
|
||||||
"Decimal": {
|
|
||||||
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"OutdatedVersionWeights": {
|
|
||||||
"description": "Defines weights for calculating numbers of versions behind the current release.",
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"major",
|
|
||||||
"minor",
|
|
||||||
"patch",
|
|
||||||
"prerelease"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"major": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "uint32",
|
|
||||||
"minimum": 0.0
|
|
||||||
},
|
|
||||||
"minor": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "uint32",
|
|
||||||
"minimum": 0.0
|
|
||||||
},
|
|
||||||
"patch": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "uint32",
|
|
||||||
"minimum": 0.0
|
|
||||||
},
|
|
||||||
"prerelease": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "uint32",
|
|
||||||
"minimum": 0.0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"VersionScoreFormulaParams": {
|
|
||||||
"description": "Given the formula of version_score = penalty ^ (versions_behind_factor ^ penalty_scaling) define the relevant parameters",
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"penalty",
|
|
||||||
"penalty_scaling"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"penalty": {
|
|
||||||
"$ref": "#/definitions/Decimal"
|
|
||||||
},
|
|
||||||
"penalty_scaling": {
|
|
||||||
"$ref": "#/definitions/Decimal"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"sudo": null,
|
"sudo": null,
|
||||||
"responses": {
|
"responses": {
|
||||||
|
|||||||
@@ -2,43 +2,13 @@
|
|||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
"title": "MigrateMsg",
|
"title": "MigrateMsg",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
|
||||||
"current_nym_node_semver"
|
|
||||||
],
|
|
||||||
"properties": {
|
"properties": {
|
||||||
"current_nym_node_semver": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"unsafe_skip_state_updates": {
|
"unsafe_skip_state_updates": {
|
||||||
"type": [
|
"type": [
|
||||||
"boolean",
|
"boolean",
|
||||||
"null"
|
"null"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"version_score_params": {
|
|
||||||
"default": {
|
|
||||||
"penalty": "0.995",
|
|
||||||
"penalty_scaling": "1.65"
|
|
||||||
},
|
|
||||||
"allOf": [
|
|
||||||
{
|
|
||||||
"$ref": "#/definitions/VersionScoreFormulaParams"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"version_score_weights": {
|
|
||||||
"default": {
|
|
||||||
"major": 100,
|
|
||||||
"minor": 10,
|
|
||||||
"patch": 1,
|
|
||||||
"prerelease": 1
|
|
||||||
},
|
|
||||||
"allOf": [
|
|
||||||
{
|
|
||||||
"$ref": "#/definitions/OutdatedVersionWeights"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"vesting_contract_address": {
|
"vesting_contract_address": {
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
@@ -46,61 +16,5 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false
|
||||||
"definitions": {
|
|
||||||
"Decimal": {
|
|
||||||
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"OutdatedVersionWeights": {
|
|
||||||
"description": "Defines weights for calculating numbers of versions behind the current release.",
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"major",
|
|
||||||
"minor",
|
|
||||||
"patch",
|
|
||||||
"prerelease"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"major": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "uint32",
|
|
||||||
"minimum": 0.0
|
|
||||||
},
|
|
||||||
"minor": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "uint32",
|
|
||||||
"minimum": 0.0
|
|
||||||
},
|
|
||||||
"patch": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "uint32",
|
|
||||||
"minimum": 0.0
|
|
||||||
},
|
|
||||||
"prerelease": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "uint32",
|
|
||||||
"minimum": 0.0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"VersionScoreFormulaParams": {
|
|
||||||
"description": "Given the formula of version_score = penalty ^ (versions_behind_factor ^ penalty_scaling) define the relevant parameters",
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"penalty",
|
|
||||||
"penalty_scaling"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"penalty": {
|
|
||||||
"$ref": "#/definitions/Decimal"
|
|
||||||
},
|
|
||||||
"penalty_scaling": {
|
|
||||||
"$ref": "#/definitions/Decimal"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -603,7 +603,7 @@ pub fn query(
|
|||||||
#[entry_point]
|
#[entry_point]
|
||||||
pub fn migrate(
|
pub fn migrate(
|
||||||
mut deps: DepsMut<'_>,
|
mut deps: DepsMut<'_>,
|
||||||
env: Env,
|
_env: Env,
|
||||||
msg: MigrateMsg,
|
msg: MigrateMsg,
|
||||||
) -> Result<Response, MixnetContractError> {
|
) -> Result<Response, MixnetContractError> {
|
||||||
set_build_information!(deps.storage)?;
|
set_build_information!(deps.storage)?;
|
||||||
@@ -612,7 +612,7 @@ pub fn migrate(
|
|||||||
let skip_state_updates = msg.unsafe_skip_state_updates.unwrap_or(false);
|
let skip_state_updates = msg.unsafe_skip_state_updates.unwrap_or(false);
|
||||||
|
|
||||||
if !skip_state_updates {
|
if !skip_state_updates {
|
||||||
crate::queued_migrations::add_config_score_params(deps.branch(), env, &msg)?;
|
crate::queued_migrations::restore_node_version_history(deps.branch())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// due to circular dependency on contract addresses (i.e. mixnet contract requiring vesting contract address
|
// due to circular dependency on contract addresses (i.e. mixnet contract requiring vesting contract address
|
||||||
|
|||||||
@@ -34,9 +34,13 @@ impl NymNodeVersionHistory<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn next_id(&self, storage: &mut dyn Storage) -> Result<u32, MixnetContractError> {
|
fn next_id(&self, storage: &mut dyn Storage) -> Result<u32, MixnetContractError> {
|
||||||
let next = self.id_counter.may_load(storage)?.unwrap_or_default();
|
let id = self
|
||||||
self.id_counter.save(storage, &next)?;
|
.id_counter
|
||||||
Ok(next)
|
.may_load(storage)?
|
||||||
|
.map(|current| current + 1)
|
||||||
|
.unwrap_or_default();
|
||||||
|
self.id_counter.save(storage, &id)?;
|
||||||
|
Ok(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_version(
|
pub fn current_version(
|
||||||
@@ -56,10 +60,10 @@ impl NymNodeVersionHistory<'_> {
|
|||||||
pub fn insert_new(
|
pub fn insert_new(
|
||||||
&self,
|
&self,
|
||||||
storage: &mut dyn Storage,
|
storage: &mut dyn Storage,
|
||||||
entry: HistoricalNymNodeVersion,
|
entry: &HistoricalNymNodeVersion,
|
||||||
) -> Result<u32, MixnetContractError> {
|
) -> Result<u32, MixnetContractError> {
|
||||||
let next_id = self.next_id(storage)?;
|
let next_id = self.next_id(storage)?;
|
||||||
self.version_history.save(storage, next_id, &entry)?;
|
self.version_history.save(storage, next_id, entry)?;
|
||||||
Ok(next_id)
|
Ok(next_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +83,7 @@ impl NymNodeVersionHistory<'_> {
|
|||||||
// treat this as genesis
|
// treat this as genesis
|
||||||
let genesis =
|
let genesis =
|
||||||
HistoricalNymNodeVersion::genesis(raw_semver.to_string(), env.block.height);
|
HistoricalNymNodeVersion::genesis(raw_semver.to_string(), env.block.height);
|
||||||
return self.insert_new(storage, genesis);
|
return self.insert_new(storage, &genesis);
|
||||||
};
|
};
|
||||||
|
|
||||||
let current_semver = current.version_information.semver_unchecked();
|
let current_semver = current.version_information.semver_unchecked();
|
||||||
@@ -99,7 +103,7 @@ impl NymNodeVersionHistory<'_> {
|
|||||||
introduced_at_height: env.block.height,
|
introduced_at_height: env.block.height,
|
||||||
difference_since_genesis: diff,
|
difference_since_genesis: diff,
|
||||||
};
|
};
|
||||||
self.insert_new(storage, entry)
|
self.insert_new(storage, &entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,3 +174,218 @@ pub(crate) fn initialise_storage(
|
|||||||
ADMIN.set(deps, Some(initial_admin))?;
|
ADMIN.set(deps, Some(initial_admin))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod nym_node_version_history {
|
||||||
|
use super::*;
|
||||||
|
use crate::support::tests::test_helpers::TestSetup;
|
||||||
|
use cosmwasm_std::testing::{mock_dependencies, mock_env};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn getting_current() -> anyhow::Result<()> {
|
||||||
|
// empty storage
|
||||||
|
let deps = mock_dependencies();
|
||||||
|
let storage = NymNodeVersionHistory::new();
|
||||||
|
assert!(storage.current_version(&deps.storage)?.is_none());
|
||||||
|
|
||||||
|
let mut test = TestSetup::new();
|
||||||
|
|
||||||
|
let zeroth = storage.current_version(test.storage())?.unwrap();
|
||||||
|
let manual_zeroth = storage.version_history.load(test.storage(), 0)?;
|
||||||
|
assert_eq!(zeroth.version_information, manual_zeroth);
|
||||||
|
|
||||||
|
// manually update the counter to make sure data is still read correctly
|
||||||
|
let dummy = HistoricalNymNodeVersion {
|
||||||
|
semver: "1.2.3".to_string(),
|
||||||
|
introduced_at_height: 1234,
|
||||||
|
difference_since_genesis: Default::default(),
|
||||||
|
};
|
||||||
|
storage.id_counter.save(test.storage_mut(), &123)?;
|
||||||
|
storage
|
||||||
|
.version_history
|
||||||
|
.save(test.storage_mut(), 123, &dummy)?;
|
||||||
|
|
||||||
|
let updated = storage.current_version(test.storage())?.unwrap();
|
||||||
|
assert_eq!(updated.version_information, dummy);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn inserting_new_entry() -> anyhow::Result<()> {
|
||||||
|
let mut test = TestSetup::new();
|
||||||
|
let storage = NymNodeVersionHistory::new();
|
||||||
|
|
||||||
|
let first = HistoricalNymNodeVersion {
|
||||||
|
semver: "1.1.1".to_string(),
|
||||||
|
introduced_at_height: 12,
|
||||||
|
difference_since_genesis: Default::default(),
|
||||||
|
};
|
||||||
|
let second = HistoricalNymNodeVersion {
|
||||||
|
semver: "1.1.2".to_string(),
|
||||||
|
introduced_at_height: 123,
|
||||||
|
difference_since_genesis: Default::default(),
|
||||||
|
};
|
||||||
|
let third = HistoricalNymNodeVersion {
|
||||||
|
semver: "1.1.3".to_string(),
|
||||||
|
introduced_at_height: 1234,
|
||||||
|
difference_since_genesis: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(storage.id_counter.load(test.storage())?, 0);
|
||||||
|
|
||||||
|
// id is correctly incremented for each case and no entry is overwritten
|
||||||
|
storage.insert_new(test.storage_mut(), &first)?;
|
||||||
|
assert_eq!(storage.id_counter.load(test.storage())?, 1);
|
||||||
|
|
||||||
|
storage.insert_new(test.storage_mut(), &second)?;
|
||||||
|
assert_eq!(storage.id_counter.load(test.storage())?, 2);
|
||||||
|
|
||||||
|
storage.insert_new(test.storage_mut(), &third)?;
|
||||||
|
assert_eq!(storage.id_counter.load(test.storage())?, 3);
|
||||||
|
|
||||||
|
assert_eq!(storage.version_history.load(test.storage(), 1)?, first);
|
||||||
|
assert_eq!(storage.version_history.load(test.storage(), 2)?, second);
|
||||||
|
assert_eq!(storage.version_history.load(test.storage(), 3)?, third);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn inserting_initial_semver() -> anyhow::Result<()> {
|
||||||
|
// empty storage
|
||||||
|
let mut deps = mock_dependencies();
|
||||||
|
let env = mock_env();
|
||||||
|
let storage = NymNodeVersionHistory::new();
|
||||||
|
|
||||||
|
assert!(storage
|
||||||
|
.id_counter
|
||||||
|
.may_load(deps.as_mut().storage)?
|
||||||
|
.is_none());
|
||||||
|
|
||||||
|
storage.try_insert_new(deps.as_mut().storage, &env, "1.1.1")?;
|
||||||
|
assert_eq!(storage.id_counter.load(deps.as_mut().storage)?, 0);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
storage
|
||||||
|
.version_history
|
||||||
|
.load(deps.as_ref().storage, 0)?
|
||||||
|
.semver,
|
||||||
|
"1.1.1"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
storage
|
||||||
|
.current_version(deps.as_ref().storage)?
|
||||||
|
.unwrap()
|
||||||
|
.version_information
|
||||||
|
.semver,
|
||||||
|
"1.1.1"
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn inserting_second_semver() -> anyhow::Result<()> {
|
||||||
|
let mut test = TestSetup::new();
|
||||||
|
let env = test.env();
|
||||||
|
let storage = NymNodeVersionHistory::new();
|
||||||
|
|
||||||
|
// lower version
|
||||||
|
assert!(storage
|
||||||
|
.try_insert_new(test.storage_mut(), &env, "1.1.9")
|
||||||
|
.is_err());
|
||||||
|
assert!(storage
|
||||||
|
.try_insert_new(test.storage_mut(), &env, "1.0.1")
|
||||||
|
.is_err());
|
||||||
|
|
||||||
|
// malformed
|
||||||
|
assert!(storage
|
||||||
|
.try_insert_new(test.storage_mut(), &env, "1.0")
|
||||||
|
.is_err());
|
||||||
|
assert!(storage
|
||||||
|
.try_insert_new(test.storage_mut(), &env, "1.0bad")
|
||||||
|
.is_err());
|
||||||
|
assert!(storage
|
||||||
|
.try_insert_new(test.storage_mut(), &env, "foomp")
|
||||||
|
.is_err());
|
||||||
|
|
||||||
|
// patch
|
||||||
|
let mut test = TestSetup::new();
|
||||||
|
storage.try_insert_new(test.storage_mut(), &env, "1.1.11")?;
|
||||||
|
let current = storage
|
||||||
|
.current_version(test.storage_mut())?
|
||||||
|
.unwrap()
|
||||||
|
.version_information;
|
||||||
|
assert_eq!(current.semver, "1.1.11");
|
||||||
|
assert_eq!(current.difference_since_genesis.major, 0);
|
||||||
|
assert_eq!(current.difference_since_genesis.minor, 0);
|
||||||
|
assert_eq!(current.difference_since_genesis.patch, 1);
|
||||||
|
assert_eq!(current.difference_since_genesis.prerelease, 0);
|
||||||
|
|
||||||
|
// minor
|
||||||
|
let mut test = TestSetup::new();
|
||||||
|
storage.try_insert_new(test.storage_mut(), &env, "1.2.0")?;
|
||||||
|
let current = storage
|
||||||
|
.current_version(test.storage_mut())?
|
||||||
|
.unwrap()
|
||||||
|
.version_information;
|
||||||
|
assert_eq!(current.semver, "1.2.0");
|
||||||
|
assert_eq!(current.difference_since_genesis.major, 0);
|
||||||
|
assert_eq!(current.difference_since_genesis.minor, 1);
|
||||||
|
assert_eq!(current.difference_since_genesis.patch, 0);
|
||||||
|
assert_eq!(current.difference_since_genesis.prerelease, 0);
|
||||||
|
|
||||||
|
// minor alt.
|
||||||
|
let mut test = TestSetup::new();
|
||||||
|
storage.try_insert_new(test.storage_mut(), &env, "1.2.3")?;
|
||||||
|
let current = storage
|
||||||
|
.current_version(test.storage_mut())?
|
||||||
|
.unwrap()
|
||||||
|
.version_information;
|
||||||
|
assert_eq!(current.semver, "1.2.3");
|
||||||
|
assert_eq!(current.difference_since_genesis.major, 0);
|
||||||
|
assert_eq!(current.difference_since_genesis.minor, 1);
|
||||||
|
assert_eq!(current.difference_since_genesis.patch, 0);
|
||||||
|
assert_eq!(current.difference_since_genesis.prerelease, 0);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn inserting_subsequent_semver() -> anyhow::Result<()> {
|
||||||
|
let mut test = TestSetup::new();
|
||||||
|
let env = test.env();
|
||||||
|
let storage = NymNodeVersionHistory::new();
|
||||||
|
|
||||||
|
storage.try_insert_new(test.storage_mut(), &env, "1.2.0")?;
|
||||||
|
storage.try_insert_new(test.storage_mut(), &env, "1.2.1")?;
|
||||||
|
storage.try_insert_new(test.storage_mut(), &env, "1.2.3")?;
|
||||||
|
let current = storage
|
||||||
|
.current_version(test.storage_mut())?
|
||||||
|
.unwrap()
|
||||||
|
.version_information;
|
||||||
|
assert_eq!(current.semver, "1.2.3");
|
||||||
|
assert_eq!(current.difference_since_genesis.major, 0);
|
||||||
|
assert_eq!(current.difference_since_genesis.minor, 1);
|
||||||
|
assert_eq!(current.difference_since_genesis.patch, 3);
|
||||||
|
assert_eq!(current.difference_since_genesis.prerelease, 0);
|
||||||
|
|
||||||
|
storage.try_insert_new(test.storage_mut(), &env, "1.3.0")?;
|
||||||
|
let current = storage
|
||||||
|
.current_version(test.storage_mut())?
|
||||||
|
.unwrap()
|
||||||
|
.version_information;
|
||||||
|
assert_eq!(current.semver, "1.3.0");
|
||||||
|
assert_eq!(current.difference_since_genesis.major, 0);
|
||||||
|
assert_eq!(current.difference_since_genesis.minor, 2);
|
||||||
|
assert_eq!(current.difference_since_genesis.patch, 3);
|
||||||
|
assert_eq!(current.difference_since_genesis.prerelease, 0);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -238,4 +238,446 @@ pub mod tests {
|
|||||||
// let res = try_update_contract_settings(deps.as_mut(), info, new_params);
|
// let res = try_update_contract_settings(deps.as_mut(), info, new_params);
|
||||||
// assert_eq!(Err(MixnetContractError::ZeroActiveSet), res);
|
// assert_eq!(Err(MixnetContractError::ZeroActiveSet), res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod updating_current_nym_node_semver {
|
||||||
|
use super::*;
|
||||||
|
use crate::mixnet_contract_settings::queries::query_current_nym_node_version;
|
||||||
|
use crate::support::tests::test_helpers::TestSetup;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn is_restricted_to_the_admin() -> anyhow::Result<()> {
|
||||||
|
let mut test = TestSetup::new();
|
||||||
|
|
||||||
|
let not_admin = mock_info("not-admin", &[]);
|
||||||
|
let admin = mock_info(test.admin().as_ref(), &[]);
|
||||||
|
|
||||||
|
let env = test.env();
|
||||||
|
let res = try_update_current_nym_node_semver(
|
||||||
|
test.deps_mut(),
|
||||||
|
env,
|
||||||
|
not_admin,
|
||||||
|
"1.2.1".to_string(),
|
||||||
|
);
|
||||||
|
assert!(res.is_err());
|
||||||
|
|
||||||
|
let env = test.env();
|
||||||
|
let res = try_update_current_nym_node_semver(
|
||||||
|
test.deps_mut(),
|
||||||
|
env,
|
||||||
|
admin,
|
||||||
|
"1.2.1".to_string(),
|
||||||
|
);
|
||||||
|
assert!(res.is_ok());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn updates_current_semver_value() -> anyhow::Result<()> {
|
||||||
|
let mut test = TestSetup::new();
|
||||||
|
|
||||||
|
let res = query_current_nym_node_version(test.deps())?;
|
||||||
|
|
||||||
|
let initial = res.version.unwrap().version_information.semver;
|
||||||
|
// sanity check to make sure our contract init hasn't changed
|
||||||
|
assert_eq!(initial, "1.1.10");
|
||||||
|
|
||||||
|
let update = "1.2.0".to_string();
|
||||||
|
|
||||||
|
let env = test.env();
|
||||||
|
let sender = test.admin_sender();
|
||||||
|
try_update_current_nym_node_semver(test.deps_mut(), env, sender, update.clone())?;
|
||||||
|
|
||||||
|
let updated = query_current_nym_node_version(test.deps())?;
|
||||||
|
let version = updated.version.unwrap().version_information.semver;
|
||||||
|
assert_eq!(version, update);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod semver_chain_updates {
|
||||||
|
use super::*;
|
||||||
|
use crate::mixnet_contract_settings::queries::query_nym_node_version_history_paged;
|
||||||
|
use mixnet_contract_common::{
|
||||||
|
HistoricalNymNodeVersion, HistoricalNymNodeVersionEntry, TotalVersionDifference,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn test_setup_with_initial_checks() -> anyhow::Result<TestSetup> {
|
||||||
|
let test = TestSetup::new();
|
||||||
|
|
||||||
|
let res = query_current_nym_node_version(test.deps())?;
|
||||||
|
let initial = res.version.unwrap().version_information.semver;
|
||||||
|
|
||||||
|
// sanity check to make sure our contract init hasn't changed
|
||||||
|
assert_eq!(initial, "1.1.10");
|
||||||
|
|
||||||
|
let history = query_nym_node_version_history_paged(test.deps(), None, None)?;
|
||||||
|
assert_eq!(history.history.len(), 1);
|
||||||
|
|
||||||
|
Ok(test)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn single_patch() -> anyhow::Result<()> {
|
||||||
|
let mut test = test_setup_with_initial_checks()?;
|
||||||
|
let initial = query_current_nym_node_version(test.deps())?
|
||||||
|
.version
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let env = test.env();
|
||||||
|
let sender = test.admin_sender();
|
||||||
|
try_update_current_nym_node_semver(
|
||||||
|
test.deps_mut(),
|
||||||
|
env.clone(),
|
||||||
|
sender,
|
||||||
|
"1.1.11".to_string(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let history =
|
||||||
|
query_nym_node_version_history_paged(test.deps(), None, None)?.history;
|
||||||
|
assert_eq!(history.len(), 2);
|
||||||
|
assert_eq!(history[0], initial);
|
||||||
|
assert_eq!(
|
||||||
|
history[1],
|
||||||
|
HistoricalNymNodeVersionEntry {
|
||||||
|
id: 1,
|
||||||
|
version_information: HistoricalNymNodeVersion {
|
||||||
|
semver: "1.1.11".to_string(),
|
||||||
|
introduced_at_height: env.block.height,
|
||||||
|
difference_since_genesis: TotalVersionDifference {
|
||||||
|
major: 0,
|
||||||
|
minor: 0,
|
||||||
|
patch: 1,
|
||||||
|
prerelease: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn single_minor() -> anyhow::Result<()> {
|
||||||
|
let mut test = test_setup_with_initial_checks()?;
|
||||||
|
let initial = query_current_nym_node_version(test.deps())?
|
||||||
|
.version
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let env = test.env();
|
||||||
|
let sender = test.admin_sender();
|
||||||
|
try_update_current_nym_node_semver(
|
||||||
|
test.deps_mut(),
|
||||||
|
env.clone(),
|
||||||
|
sender,
|
||||||
|
"1.2.0".to_string(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let history =
|
||||||
|
query_nym_node_version_history_paged(test.deps(), None, None)?.history;
|
||||||
|
assert_eq!(history.len(), 2);
|
||||||
|
assert_eq!(history[0], initial);
|
||||||
|
assert_eq!(
|
||||||
|
history[1],
|
||||||
|
HistoricalNymNodeVersionEntry {
|
||||||
|
id: 1,
|
||||||
|
version_information: HistoricalNymNodeVersion {
|
||||||
|
semver: "1.2.0".to_string(),
|
||||||
|
introduced_at_height: env.block.height,
|
||||||
|
difference_since_genesis: TotalVersionDifference {
|
||||||
|
major: 0,
|
||||||
|
minor: 1,
|
||||||
|
patch: 0,
|
||||||
|
prerelease: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiple_patches() -> anyhow::Result<()> {
|
||||||
|
let mut test = test_setup_with_initial_checks()?;
|
||||||
|
let initial = query_current_nym_node_version(test.deps())?
|
||||||
|
.version
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut env = test.env();
|
||||||
|
let sender = test.admin_sender();
|
||||||
|
try_update_current_nym_node_semver(
|
||||||
|
test.deps_mut(),
|
||||||
|
env.clone(),
|
||||||
|
sender.clone(),
|
||||||
|
"1.1.11".to_string(),
|
||||||
|
)?;
|
||||||
|
env.block.height += 1;
|
||||||
|
try_update_current_nym_node_semver(
|
||||||
|
test.deps_mut(),
|
||||||
|
env.clone(),
|
||||||
|
sender.clone(),
|
||||||
|
"1.1.12".to_string(),
|
||||||
|
)?;
|
||||||
|
env.block.height += 1;
|
||||||
|
try_update_current_nym_node_semver(
|
||||||
|
test.deps_mut(),
|
||||||
|
env.clone(),
|
||||||
|
sender,
|
||||||
|
"1.1.13".to_string(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let history =
|
||||||
|
query_nym_node_version_history_paged(test.deps(), None, None)?.history;
|
||||||
|
assert_eq!(history.len(), 4);
|
||||||
|
assert_eq!(history[0], initial);
|
||||||
|
assert_eq!(
|
||||||
|
history[1],
|
||||||
|
HistoricalNymNodeVersionEntry {
|
||||||
|
id: 1,
|
||||||
|
version_information: HistoricalNymNodeVersion {
|
||||||
|
semver: "1.1.11".to_string(),
|
||||||
|
introduced_at_height: env.block.height - 2,
|
||||||
|
difference_since_genesis: TotalVersionDifference {
|
||||||
|
major: 0,
|
||||||
|
minor: 0,
|
||||||
|
patch: 1,
|
||||||
|
prerelease: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
history[2],
|
||||||
|
HistoricalNymNodeVersionEntry {
|
||||||
|
id: 2,
|
||||||
|
version_information: HistoricalNymNodeVersion {
|
||||||
|
semver: "1.1.12".to_string(),
|
||||||
|
introduced_at_height: env.block.height - 1,
|
||||||
|
difference_since_genesis: TotalVersionDifference {
|
||||||
|
major: 0,
|
||||||
|
minor: 0,
|
||||||
|
patch: 2,
|
||||||
|
prerelease: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
history[3],
|
||||||
|
HistoricalNymNodeVersionEntry {
|
||||||
|
id: 3,
|
||||||
|
version_information: HistoricalNymNodeVersion {
|
||||||
|
semver: "1.1.13".to_string(),
|
||||||
|
introduced_at_height: env.block.height,
|
||||||
|
difference_since_genesis: TotalVersionDifference {
|
||||||
|
major: 0,
|
||||||
|
minor: 0,
|
||||||
|
patch: 3,
|
||||||
|
prerelease: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiple_minors() -> anyhow::Result<()> {
|
||||||
|
let mut test = test_setup_with_initial_checks()?;
|
||||||
|
let initial = query_current_nym_node_version(test.deps())?
|
||||||
|
.version
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut env = test.env();
|
||||||
|
let sender = test.admin_sender();
|
||||||
|
try_update_current_nym_node_semver(
|
||||||
|
test.deps_mut(),
|
||||||
|
env.clone(),
|
||||||
|
sender.clone(),
|
||||||
|
"1.2.0".to_string(),
|
||||||
|
)?;
|
||||||
|
env.block.height += 1;
|
||||||
|
try_update_current_nym_node_semver(
|
||||||
|
test.deps_mut(),
|
||||||
|
env.clone(),
|
||||||
|
sender.clone(),
|
||||||
|
"1.3.0".to_string(),
|
||||||
|
)?;
|
||||||
|
env.block.height += 1;
|
||||||
|
try_update_current_nym_node_semver(
|
||||||
|
test.deps_mut(),
|
||||||
|
env.clone(),
|
||||||
|
sender,
|
||||||
|
"1.4.0".to_string(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let history =
|
||||||
|
query_nym_node_version_history_paged(test.deps(), None, None)?.history;
|
||||||
|
assert_eq!(history.len(), 4);
|
||||||
|
assert_eq!(history[0], initial);
|
||||||
|
assert_eq!(
|
||||||
|
history[1],
|
||||||
|
HistoricalNymNodeVersionEntry {
|
||||||
|
id: 1,
|
||||||
|
version_information: HistoricalNymNodeVersion {
|
||||||
|
semver: "1.2.0".to_string(),
|
||||||
|
introduced_at_height: env.block.height - 2,
|
||||||
|
difference_since_genesis: TotalVersionDifference {
|
||||||
|
major: 0,
|
||||||
|
minor: 1,
|
||||||
|
patch: 0,
|
||||||
|
prerelease: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
history[2],
|
||||||
|
HistoricalNymNodeVersionEntry {
|
||||||
|
id: 2,
|
||||||
|
version_information: HistoricalNymNodeVersion {
|
||||||
|
semver: "1.3.0".to_string(),
|
||||||
|
introduced_at_height: env.block.height - 1,
|
||||||
|
difference_since_genesis: TotalVersionDifference {
|
||||||
|
major: 0,
|
||||||
|
minor: 2,
|
||||||
|
patch: 0,
|
||||||
|
prerelease: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
history[3],
|
||||||
|
HistoricalNymNodeVersionEntry {
|
||||||
|
id: 3,
|
||||||
|
version_information: HistoricalNymNodeVersion {
|
||||||
|
semver: "1.4.0".to_string(),
|
||||||
|
introduced_at_height: env.block.height,
|
||||||
|
difference_since_genesis: TotalVersionDifference {
|
||||||
|
major: 0,
|
||||||
|
minor: 3,
|
||||||
|
patch: 0,
|
||||||
|
prerelease: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mixed_multiple_updates() -> anyhow::Result<()> {
|
||||||
|
let mut test = test_setup_with_initial_checks()?;
|
||||||
|
let initial = query_current_nym_node_version(test.deps())?
|
||||||
|
.version
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut env = test.env();
|
||||||
|
let sender = test.admin_sender();
|
||||||
|
try_update_current_nym_node_semver(
|
||||||
|
test.deps_mut(),
|
||||||
|
env.clone(),
|
||||||
|
sender.clone(),
|
||||||
|
"1.2.0".to_string(),
|
||||||
|
)?;
|
||||||
|
env.block.height += 1;
|
||||||
|
try_update_current_nym_node_semver(
|
||||||
|
test.deps_mut(),
|
||||||
|
env.clone(),
|
||||||
|
sender.clone(),
|
||||||
|
"1.2.1".to_string(),
|
||||||
|
)?;
|
||||||
|
env.block.height += 1;
|
||||||
|
try_update_current_nym_node_semver(
|
||||||
|
test.deps_mut(),
|
||||||
|
env.clone(),
|
||||||
|
sender.clone(),
|
||||||
|
"1.2.3".to_string(),
|
||||||
|
)?;
|
||||||
|
env.block.height += 1;
|
||||||
|
try_update_current_nym_node_semver(
|
||||||
|
test.deps_mut(),
|
||||||
|
env.clone(),
|
||||||
|
sender,
|
||||||
|
"1.3.0".to_string(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let history =
|
||||||
|
query_nym_node_version_history_paged(test.deps(), None, None)?.history;
|
||||||
|
assert_eq!(history.len(), 5);
|
||||||
|
assert_eq!(history[0], initial);
|
||||||
|
assert_eq!(
|
||||||
|
history[1],
|
||||||
|
HistoricalNymNodeVersionEntry {
|
||||||
|
id: 1,
|
||||||
|
version_information: HistoricalNymNodeVersion {
|
||||||
|
semver: "1.2.0".to_string(),
|
||||||
|
introduced_at_height: env.block.height - 3,
|
||||||
|
difference_since_genesis: TotalVersionDifference {
|
||||||
|
major: 0,
|
||||||
|
minor: 1,
|
||||||
|
patch: 0,
|
||||||
|
prerelease: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
history[2],
|
||||||
|
HistoricalNymNodeVersionEntry {
|
||||||
|
id: 2,
|
||||||
|
version_information: HistoricalNymNodeVersion {
|
||||||
|
semver: "1.2.1".to_string(),
|
||||||
|
introduced_at_height: env.block.height - 2,
|
||||||
|
difference_since_genesis: TotalVersionDifference {
|
||||||
|
major: 0,
|
||||||
|
minor: 1,
|
||||||
|
patch: 1,
|
||||||
|
prerelease: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
history[3],
|
||||||
|
HistoricalNymNodeVersionEntry {
|
||||||
|
id: 3,
|
||||||
|
version_information: HistoricalNymNodeVersion {
|
||||||
|
semver: "1.2.3".to_string(),
|
||||||
|
introduced_at_height: env.block.height - 1,
|
||||||
|
difference_since_genesis: TotalVersionDifference {
|
||||||
|
major: 0,
|
||||||
|
minor: 1,
|
||||||
|
patch: 3,
|
||||||
|
prerelease: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
history[4],
|
||||||
|
HistoricalNymNodeVersionEntry {
|
||||||
|
id: 4,
|
||||||
|
version_information: HistoricalNymNodeVersion {
|
||||||
|
semver: "1.3.0".to_string(),
|
||||||
|
introduced_at_height: env.block.height,
|
||||||
|
difference_since_genesis: TotalVersionDifference {
|
||||||
|
major: 0,
|
||||||
|
minor: 2,
|
||||||
|
patch: 3,
|
||||||
|
prerelease: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,86 +1,153 @@
|
|||||||
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
mod config_score_params {
|
mod node_version_history {
|
||||||
use crate::constants::CONTRACT_STATE_KEY;
|
|
||||||
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
|
|
||||||
use crate::mixnet_contract_settings::storage::NymNodeVersionHistory;
|
use crate::mixnet_contract_settings::storage::NymNodeVersionHistory;
|
||||||
use cosmwasm_std::{Addr, Coin, DepsMut, Env};
|
use cosmwasm_std::DepsMut;
|
||||||
use cw_storage_plus::Item;
|
|
||||||
use mixnet_contract_common::error::MixnetContractError;
|
use mixnet_contract_common::error::MixnetContractError;
|
||||||
use mixnet_contract_common::{
|
use mixnet_contract_common::{HistoricalNymNodeVersion, TotalVersionDifference};
|
||||||
ConfigScoreParams, ContractState, ContractStateParams, DelegationsParams, MigrateMsg,
|
|
||||||
OperatingCostRange, OperatorsParams, ProfitMarginRange,
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
pub(crate) fn add_config_score_params(
|
pub(crate) fn restore_node_version_history(
|
||||||
deps: DepsMut<'_>,
|
deps: DepsMut<'_>,
|
||||||
env: Env,
|
|
||||||
msg: &MigrateMsg,
|
|
||||||
) -> Result<(), MixnetContractError> {
|
) -> Result<(), MixnetContractError> {
|
||||||
if semver::Version::from_str(&msg.current_nym_node_semver).is_err() {
|
// sanity check:
|
||||||
return Err(MixnetContractError::InvalidNymNodeSemver {
|
let storage = NymNodeVersionHistory::new();
|
||||||
provided: msg.current_nym_node_semver.to_string(),
|
let Some(current) = storage.current_version(deps.storage)? else {
|
||||||
|
return Err(MixnetContractError::FailedMigration {
|
||||||
|
comment: "no node version history set".to_string(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
if current.version_information.semver != "1.2.1"
|
||||||
|
|| current.version_information.introduced_at_height != 15902170
|
||||||
|
{
|
||||||
|
return Err(MixnetContractError::FailedMigration {
|
||||||
|
comment: format!("unexpected current node version history. got: {current:?}"),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
let lost = HistoricalNymNodeVersion {
|
||||||
#[derive(Serialize, Deserialize)]
|
semver: "1.1.12".to_string(),
|
||||||
pub struct OldContractState {
|
introduced_at_height: 15779133,
|
||||||
pub owner: Option<Addr>,
|
difference_since_genesis: TotalVersionDifference::default(),
|
||||||
pub rewarding_validator_address: Addr,
|
|
||||||
pub vesting_contract_address: Addr,
|
|
||||||
pub rewarding_denom: String,
|
|
||||||
pub params: OldContractStateParams,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct OldContractStateParams {
|
|
||||||
pub minimum_delegation: Option<Coin>,
|
|
||||||
pub minimum_pledge: Coin,
|
|
||||||
#[serde(default)]
|
|
||||||
pub profit_margin: ProfitMarginRange,
|
|
||||||
#[serde(default)]
|
|
||||||
pub interval_operating_cost: OperatingCostRange,
|
|
||||||
}
|
|
||||||
|
|
||||||
const OLD_CONTRACT_STATE: Item<'_, OldContractState> = Item::new(CONTRACT_STATE_KEY);
|
|
||||||
let old_state = OLD_CONTRACT_STATE.load(deps.storage)?;
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
let new_state = ContractState {
|
|
||||||
owner: old_state.owner,
|
|
||||||
rewarding_validator_address: old_state.rewarding_validator_address,
|
|
||||||
vesting_contract_address: old_state.vesting_contract_address,
|
|
||||||
rewarding_denom: old_state.rewarding_denom,
|
|
||||||
params: ContractStateParams {
|
|
||||||
delegations_params: DelegationsParams {
|
|
||||||
minimum_delegation: old_state.params.minimum_delegation,
|
|
||||||
},
|
|
||||||
operators_params: OperatorsParams {
|
|
||||||
minimum_pledge: old_state.params.minimum_pledge,
|
|
||||||
profit_margin: old_state.params.profit_margin,
|
|
||||||
interval_operating_cost: old_state.params.interval_operating_cost,
|
|
||||||
},
|
|
||||||
config_score_params: ConfigScoreParams {
|
|
||||||
version_weights: msg.version_score_weights,
|
|
||||||
version_score_formula_params: msg.version_score_params,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
mixnet_params_storage::CONTRACT_STATE.save(deps.storage, &new_state)?;
|
#[allow(clippy::unwrap_used)]
|
||||||
|
// SAFETY: this information was already stored in the contract, so it must be a valid semver
|
||||||
|
let difference_since_genesis = lost.cumulative_difference_since_genesis(
|
||||||
|
¤t.version_information.semver.parse().unwrap(),
|
||||||
|
);
|
||||||
|
let updated_current = HistoricalNymNodeVersion {
|
||||||
|
semver: current.version_information.semver,
|
||||||
|
introduced_at_height: current.version_information.introduced_at_height,
|
||||||
|
difference_since_genesis,
|
||||||
|
};
|
||||||
|
|
||||||
// initialise the version chain
|
// restore overwritten entry for 1.1.12
|
||||||
NymNodeVersionHistory::new().try_insert_new(
|
storage.version_history.save(deps.storage, 0, &lost)?;
|
||||||
deps.storage,
|
|
||||||
&env,
|
// re-insert 1.2.1 as the current
|
||||||
&msg.current_nym_node_semver,
|
storage
|
||||||
)?;
|
.version_history
|
||||||
|
.save(deps.storage, 1, &updated_current)?;
|
||||||
|
storage.id_counter.save(deps.storage, &1)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) use config_score_params::add_config_score_params;
|
pub(crate) use node_version_history::restore_node_version_history;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::mixnet_contract_settings::queries::query_nym_node_version_history_paged;
|
||||||
|
use crate::mixnet_contract_settings::storage::NymNodeVersionHistory;
|
||||||
|
use cosmwasm_std::testing::{mock_dependencies, mock_env};
|
||||||
|
use mixnet_contract_common::{
|
||||||
|
HistoricalNymNodeVersion, HistoricalNymNodeVersionEntry, TotalVersionDifference,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fixing_history_storage() -> anyhow::Result<()> {
|
||||||
|
// current state on mainnet:
|
||||||
|
let mut deps = mock_dependencies();
|
||||||
|
let storage = NymNodeVersionHistory::new();
|
||||||
|
|
||||||
|
storage.id_counter.save(deps.as_mut().storage, &0)?;
|
||||||
|
storage.version_history.save(
|
||||||
|
deps.as_mut().storage,
|
||||||
|
0,
|
||||||
|
&HistoricalNymNodeVersion {
|
||||||
|
semver: "1.2.1".to_string(),
|
||||||
|
introduced_at_height: 15902170,
|
||||||
|
difference_since_genesis: Default::default(),
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// run migration
|
||||||
|
restore_node_version_history(deps.as_mut())?;
|
||||||
|
|
||||||
|
let current = storage.current_version(deps.as_ref().storage)?.unwrap();
|
||||||
|
assert_eq!(current.version_information.semver, "1.2.1");
|
||||||
|
assert_eq!(current.version_information.introduced_at_height, 15902170);
|
||||||
|
assert_eq!(
|
||||||
|
current.version_information.difference_since_genesis,
|
||||||
|
TotalVersionDifference {
|
||||||
|
major: 0,
|
||||||
|
minor: 1,
|
||||||
|
patch: 0,
|
||||||
|
prerelease: 0,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let history = query_nym_node_version_history_paged(deps.as_ref(), None, None)?.history;
|
||||||
|
assert_eq!(history.len(), 2);
|
||||||
|
assert_eq!(
|
||||||
|
history,
|
||||||
|
vec![
|
||||||
|
HistoricalNymNodeVersionEntry {
|
||||||
|
id: 0,
|
||||||
|
version_information: HistoricalNymNodeVersion {
|
||||||
|
semver: "1.1.12".to_string(),
|
||||||
|
introduced_at_height: 15779133,
|
||||||
|
difference_since_genesis: Default::default(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
HistoricalNymNodeVersionEntry {
|
||||||
|
id: 1,
|
||||||
|
version_information: HistoricalNymNodeVersion {
|
||||||
|
semver: "1.2.1".to_string(),
|
||||||
|
introduced_at_height: 15902170,
|
||||||
|
difference_since_genesis: TotalVersionDifference {
|
||||||
|
major: 0,
|
||||||
|
minor: 1,
|
||||||
|
patch: 0,
|
||||||
|
prerelease: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
let counter = storage.id_counter.load(deps.as_ref().storage)?;
|
||||||
|
assert_eq!(counter, 1);
|
||||||
|
|
||||||
|
// make sure adding another version doesn't mess anything up
|
||||||
|
storage.try_insert_new(deps.as_mut().storage, &mock_env(), "1.3.0")?;
|
||||||
|
|
||||||
|
let current = storage.current_version(deps.as_ref().storage)?.unwrap();
|
||||||
|
assert_eq!(current.version_information.semver, "1.3.0");
|
||||||
|
assert_eq!(
|
||||||
|
current.version_information.difference_since_genesis,
|
||||||
|
TotalVersionDifference {
|
||||||
|
major: 0,
|
||||||
|
minor: 2,
|
||||||
|
patch: 0,
|
||||||
|
prerelease: 0,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
let counter = storage.id_counter.load(deps.as_ref().storage)?;
|
||||||
|
assert_eq!(counter, 2);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ pub mod test_helpers {
|
|||||||
};
|
};
|
||||||
use crate::interval::{pending_events, storage as interval_storage};
|
use crate::interval::{pending_events, storage as interval_storage};
|
||||||
use crate::mixnet_contract_settings::storage::{
|
use crate::mixnet_contract_settings::storage::{
|
||||||
self as mixnet_params_storage, minimum_node_pledge,
|
self as mixnet_params_storage, minimum_node_pledge, ADMIN,
|
||||||
};
|
};
|
||||||
use crate::mixnet_contract_settings::storage::{rewarding_denom, rewarding_validator_address};
|
use crate::mixnet_contract_settings::storage::{rewarding_denom, rewarding_validator_address};
|
||||||
use crate::mixnodes::helpers::get_mixnode_details_by_id;
|
use crate::mixnodes::helpers::get_mixnode_details_by_id;
|
||||||
@@ -318,6 +318,10 @@ pub mod test_helpers {
|
|||||||
compare_decimals(mix_info.delegates, subtotal, Some(epsilon))
|
compare_decimals(mix_info.delegates, subtotal, Some(epsilon))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn admin(&self) -> Addr {
|
||||||
|
ADMIN.get(self.deps()).unwrap().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn random_address(&mut self) -> String {
|
pub fn random_address(&mut self) -> String {
|
||||||
format!("n1foomp{}", self.rng.next_u64())
|
format!("n1foomp{}", self.rng.next_u64())
|
||||||
}
|
}
|
||||||
@@ -330,6 +334,14 @@ pub mod test_helpers {
|
|||||||
self.deps.as_mut()
|
self.deps.as_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn storage(&self) -> &dyn Storage {
|
||||||
|
self.deps().storage
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn storage_mut(&mut self) -> &mut dyn Storage {
|
||||||
|
self.deps_mut().storage
|
||||||
|
}
|
||||||
|
|
||||||
pub fn env(&self) -> Env {
|
pub fn env(&self) -> Env {
|
||||||
self.env.clone()
|
self.env.clone()
|
||||||
}
|
}
|
||||||
@@ -470,6 +482,10 @@ pub mod test_helpers {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn admin_sender(&self) -> MessageInfo {
|
||||||
|
mock_info(self.admin().as_ref(), &[])
|
||||||
|
}
|
||||||
|
|
||||||
pub fn owner(&self) -> MessageInfo {
|
pub fn owner(&self) -> MessageInfo {
|
||||||
self.owner.clone()
|
self.owner.clone()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
---
|
|
||||||
description: Interactive APIs generated from the OpenAPI specs of various API endpoints offered by bits of Nym infrastructure run both by Nym and community operators for both Mainnet and the Sandbox testnet.
|
|
||||||
---
|
|
||||||
|
|
||||||
# Introduction
|
# Introduction
|
||||||
|
|
||||||
This site contains interactive APIs generated from the OpenAPI specs of various API endpoints offered by bits of Nym infrastructure run both by Nym and community operators for both Mainnet and the Sandbox testnet.
|
This site contains interactive APIs generated from the OpenAPI specs of various API endpoints offered by bits of Nym infrastructure run both by Nym and community operators for both Mainnet and the Sandbox testnet.
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
---
|
|
||||||
description: Nym's developer documentation covering core concepts of integrating with the Mixnet, interacting with the Nyx blockchain, an overview of the avaliable tools, and our SDK docs.
|
|
||||||
---
|
|
||||||
|
|
||||||
# Introduction
|
# Introduction
|
||||||
Nym's developer documentation covering core concepts of integrating with the Mixnet, interacting with the Nyx blockchain, an overview of the avaliable tools, and our SDK docs.
|
Nym's developer documentation covering core concepts of integrating with the Mixnet, interacting with the Nyx blockchain, an overview of the avaliable tools, and our SDK docs.
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
---
|
|
||||||
description: Nym's network documentation covering network architecture, node types, tokenomics, and cryptography.
|
|
||||||
---
|
|
||||||
|
|
||||||
# Introduction
|
# Introduction
|
||||||
Nym's network documentation covering network architecture, node types, tokenomics, and crypto systems.
|
Nym's network documentation covering network architecture, node types, tokenomics, and crypto systems.
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
---
|
|
||||||
description: Nym's Operators guide containing information and setup guides for the various components of Nym network and Nyx blockchain validators.
|
|
||||||
---
|
|
||||||
|
|
||||||
# Introduction
|
# Introduction
|
||||||
|
|
||||||
This is **Nym's Operators guide**, containing information and setup guides for the various components of Nym network and Nyx blockchain validators.
|
This is **Nym's Operators guide**, containing information and setup guides for the various components of Nym network and Nyx blockchain validators.
|
||||||
|
|||||||
@@ -222,7 +222,7 @@ Good performance is much more essential than [total stake](../tokenomics.mdx#sta
|
|||||||
For a comparison we made an example with 5 nodes, where first number is node performance and second stake saturation (assuming all of them `config_score` = 1 for simplification):
|
For a comparison we made an example with 5 nodes, where first number is node performance and second stake saturation (assuming all of them `config_score` = 1 for simplification):
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
<AccordionTemplate name="✏️ Example: Performance ^ 20 \* total_stake calculation">
|
<AccordionTemplate name="✏️ Example: Performance ^ 20 * total_stake calculation">
|
||||||
> node_1 = 1.00 ^ 20 \* 1.0 = 1 <br />
|
> node_1 = 1.00 ^ 20 \* 1.0 = 1 <br />
|
||||||
> node_2 = 1.00 ^ 20 \* 0.5 = 0.5 <br />
|
> node_2 = 1.00 ^ 20 \* 0.5 = 0.5 <br />
|
||||||
> node_3 = 0.99 ^ 20 \* 1.0 = 0.818 <br />
|
> node_3 = 0.99 ^ 20 \* 1.0 = 0.818 <br />
|
||||||
|
|||||||
@@ -11,10 +11,23 @@ const config: DocsThemeConfig = {
|
|||||||
const url = process.env.NEXT_PUBLIC_SITE_URL
|
const url = process.env.NEXT_PUBLIC_SITE_URL
|
||||||
const image = url + '/nym_logo.jpg'
|
const image = url + '/nym_logo.jpg'
|
||||||
|
|
||||||
|
// Define descriptions for different "books"
|
||||||
|
const bookDescriptions: Record<string, string> = {
|
||||||
|
'/developers': "Nym's developer documentation covering core concepts of integrating with the Mixnet, interacting with the Nyx blockchain, an overview of the avaliable tools, and our SDK docs.",
|
||||||
|
'/network': "Nym's network documentation covering network architecture, node types, tokenomics, and cryptography.",
|
||||||
|
'/operators': "Nym's Operators guide containing information and setup guides for the various components of Nym network and Nyx blockchain validators.",
|
||||||
|
'/apis': "Interactive APIs generated from the OpenAPI specs of various API endpoints offered by bits of Nym infrastructure run both by Nym and community operators for both Mainnet and the Sandbox testnet."
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultDescription = 'Nym is a privacy platform. It provides strong network-level privacy against sophisticated end-to-end attackers, and anonymous access control using blinded, re-randomizable, decentralized credentials.'
|
||||||
|
|
||||||
|
const topLevel = '/' + route.split('/')[1]
|
||||||
const description =
|
const description =
|
||||||
config.frontMatter.description ||
|
config.frontMatter.description ||
|
||||||
'Nym is a privacy platform. It provides strong network-level privacy against sophisticated end-to-end attackers, and anonymous access control using blinded, re-randomizable, decentralized credentials.'
|
bookDescriptions[topLevel] ||
|
||||||
const title = config.title + (route === '/' ? '' : ' - Nym docs')
|
defaultDescription
|
||||||
|
|
||||||
|
const title = config.title + (route === '/' ? '' : ' - Nym docs')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "explorer-api"
|
name = "explorer-api"
|
||||||
version = "1.1.43"
|
version = "1.1.44"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|
||||||
|
|||||||
@@ -120,9 +120,7 @@ impl ThreadsafeGatewayCache {
|
|||||||
.read()
|
.read()
|
||||||
.await
|
.await
|
||||||
.get(&identity_key)
|
.get(&identity_key)
|
||||||
.map_or(false, |cache_item| {
|
.is_some_and(|cache_item| cache_item.valid_until > SystemTime::now())
|
||||||
cache_item.valid_until > SystemTime::now()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_locations(&self) -> GatewayLocationCache {
|
pub(crate) async fn get_locations(&self) -> GatewayLocationCache {
|
||||||
|
|||||||
@@ -106,9 +106,7 @@ impl ThreadsafeMixNodesCache {
|
|||||||
.read()
|
.read()
|
||||||
.await
|
.await
|
||||||
.get(&mix_id)
|
.get(&mix_id)
|
||||||
.map_or(false, |cache_item| {
|
.is_some_and(|cache_item| cache_item.valid_until > SystemTime::now())
|
||||||
cache_item.valid_until > SystemTime::now()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_locations(&self) -> MixnodeLocationCache {
|
pub(crate) async fn get_locations(&self) -> MixnodeLocationCache {
|
||||||
|
|||||||
@@ -60,9 +60,7 @@ impl ThreadSafeNymNodesCache {
|
|||||||
.read()
|
.read()
|
||||||
.await
|
.await
|
||||||
.get(&node_id)
|
.get(&node_id)
|
||||||
.map_or(false, |cache_item| {
|
.is_some_and(|cache_item| cache_item.valid_until > SystemTime::now())
|
||||||
cache_item.valid_until > SystemTime::now()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_bonded_nymnodes(
|
pub(crate) async fn get_bonded_nymnodes(
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import { NYM_WEBSITE } from '@/app/api/constants'
|
|||||||
import { useMainContext } from '@/app/context/main'
|
import { useMainContext } from '@/app/context/main'
|
||||||
import { MobileDrawerClose } from '@/app/icons/MobileDrawerClose'
|
import { MobileDrawerClose } from '@/app/icons/MobileDrawerClose'
|
||||||
import { NavOptionType, originalNavOptions } from '@/app/context/nav'
|
import { NavOptionType, originalNavOptions } from '@/app/context/nav'
|
||||||
|
import { ReleaseAlert } from '@/app/components/ReleaseAlert'
|
||||||
import { DarkLightSwitchDesktop } from '@/app/components/Switch'
|
import { DarkLightSwitchDesktop } from '@/app/components/Switch'
|
||||||
import { Footer } from '@/app/components/Footer'
|
import { Footer } from '@/app/components/Footer'
|
||||||
import { ConnectKeplrWallet } from '@/app/components/Wallet/ConnectKeplrWallet'
|
import { ConnectKeplrWallet } from '@/app/components/Wallet/ConnectKeplrWallet'
|
||||||
@@ -369,6 +370,7 @@ export const Nav: FCWithChildren = ({ children }) => {
|
|||||||
style={{ width: `calc(100% - ${drawerWidth}px` }}
|
style={{ width: `calc(100% - ${drawerWidth}px` }}
|
||||||
sx={{ py: 5, px: 6, mt: 7 }}
|
sx={{ py: 5, px: 6, mt: 7 }}
|
||||||
>
|
>
|
||||||
|
<ReleaseAlert />
|
||||||
{children}
|
{children}
|
||||||
<Footer />
|
<Footer />
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import { ExpandableButton } from './DesktopNav'
|
|||||||
import { ConnectKeplrWallet } from '../Wallet/ConnectKeplrWallet'
|
import { ConnectKeplrWallet } from '../Wallet/ConnectKeplrWallet'
|
||||||
import { NetworkTitle } from '../NetworkTitle'
|
import { NetworkTitle } from '../NetworkTitle'
|
||||||
import { originalNavOptions } from '@/app/context/nav'
|
import { originalNavOptions } from '@/app/context/nav'
|
||||||
|
import { ReleaseAlert } from '@/app/components/ReleaseAlert'
|
||||||
import {SearchToolbar} from "@/app/components/Nav/Search";
|
import {SearchToolbar} from "@/app/components/Nav/Search";
|
||||||
|
|
||||||
export const MobileNav: FCWithChildren = ({ children }) => {
|
export const MobileNav: FCWithChildren = ({ children }) => {
|
||||||
@@ -137,6 +138,7 @@ export const MobileNav: FCWithChildren = ({ children }) => {
|
|||||||
</Drawer>
|
</Drawer>
|
||||||
|
|
||||||
<Box sx={{ width: '100%', p: 4, mt: 7 }}>
|
<Box sx={{ width: '100%', p: 4, mt: 7 }}>
|
||||||
|
<ReleaseAlert />
|
||||||
{children}
|
{children}
|
||||||
<Footer />
|
<Footer />
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { Alert, Box, Typography } from '@mui/material';
|
||||||
|
|
||||||
|
export const ReleaseAlert = () => (
|
||||||
|
<Alert severity="warning" sx={{ mb: 3, fontSize: 'medium', width: '100%' }}>
|
||||||
|
<Box>
|
||||||
|
<Typography>You are now viewing the legacy Nym mixnet explorer. Explorer 2.0 is coming soon.</Typography>
|
||||||
|
</Box>
|
||||||
|
</Alert>
|
||||||
|
);
|
||||||
@@ -163,4 +163,11 @@ impl ActiveClientsStore {
|
|||||||
pub(crate) fn size(&self) -> usize {
|
pub(crate) fn size(&self) -> usize {
|
||||||
self.inner.len()
|
self.inner.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn pending_packets(&self) -> usize {
|
||||||
|
self.inner
|
||||||
|
.iter()
|
||||||
|
.map(|client| client.get_sender_ref().len())
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use nym_crypto::asymmetric::identity;
|
|||||||
use nym_gateway_storage::GatewayStorage;
|
use nym_gateway_storage::GatewayStorage;
|
||||||
use nym_mixnet_client::forwarder::MixForwardingSender;
|
use nym_mixnet_client::forwarder::MixForwardingSender;
|
||||||
use nym_node_metrics::events::MetricEventsSender;
|
use nym_node_metrics::events::MetricEventsSender;
|
||||||
|
use nym_node_metrics::NymNodeMetrics;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
// I can see this being possible expanded with say storage or client store
|
// I can see this being possible expanded with say storage or client store
|
||||||
@@ -17,7 +18,14 @@ pub(crate) struct CommonHandlerState {
|
|||||||
pub(crate) local_identity: Arc<identity::KeyPair>,
|
pub(crate) local_identity: Arc<identity::KeyPair>,
|
||||||
pub(crate) only_coconut_credentials: bool,
|
pub(crate) only_coconut_credentials: bool,
|
||||||
pub(crate) bandwidth_cfg: BandwidthFlushingBehaviourConfig,
|
pub(crate) bandwidth_cfg: BandwidthFlushingBehaviourConfig,
|
||||||
|
pub(crate) metrics: NymNodeMetrics,
|
||||||
pub(crate) metrics_sender: MetricEventsSender,
|
pub(crate) metrics_sender: MetricEventsSender,
|
||||||
pub(crate) outbound_mix_sender: MixForwardingSender,
|
pub(crate) outbound_mix_sender: MixForwardingSender,
|
||||||
pub(crate) active_clients_store: ActiveClientsStore,
|
pub(crate) active_clients_store: ActiveClientsStore,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CommonHandlerState {
|
||||||
|
pub(crate) fn storage(&self) -> &GatewayStorage {
|
||||||
|
&self.storage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -157,6 +157,10 @@ impl<R, S> Drop for AuthenticatedHandler<R, S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<R, S> AuthenticatedHandler<R, S> {
|
impl<R, S> AuthenticatedHandler<R, S> {
|
||||||
|
pub(crate) fn inner(&self) -> &FreshHandler<R, S> {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
|
|
||||||
/// Upgrades `FreshHandler` into the Authenticated variant implying the client is now authenticated
|
/// Upgrades `FreshHandler` into the Authenticated variant implying the client is now authenticated
|
||||||
/// and thus allowed to perform more actions with the gateway, such as redeeming bandwidth or
|
/// and thus allowed to perform more actions with the gateway, such as redeeming bandwidth or
|
||||||
/// sending sphinx packets.
|
/// sending sphinx packets.
|
||||||
@@ -327,6 +331,24 @@ impl<R, S> AuthenticatedHandler<R, S> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn handle_forget_me(
|
||||||
|
&mut self,
|
||||||
|
client: bool,
|
||||||
|
stats: bool,
|
||||||
|
) -> Result<ServerResponse, RequestHandlingError> {
|
||||||
|
if client {
|
||||||
|
self.inner()
|
||||||
|
.shared_state()
|
||||||
|
.storage()
|
||||||
|
.handle_forget_me(self.client.address)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
if stats {
|
||||||
|
self.send_metrics(GatewaySessionEvent::new_session_delete(self.client.address));
|
||||||
|
}
|
||||||
|
Ok(SensitiveServerResponse::ForgetMeAck {}.encrypt(&self.client.shared_keys)?)
|
||||||
|
}
|
||||||
|
|
||||||
async fn handle_key_upgrade(
|
async fn handle_key_upgrade(
|
||||||
&mut self,
|
&mut self,
|
||||||
hkdf_salt: Vec<u8>,
|
hkdf_salt: Vec<u8>,
|
||||||
@@ -370,6 +392,7 @@ impl<R, S> AuthenticatedHandler<R, S> {
|
|||||||
hkdf_salt,
|
hkdf_salt,
|
||||||
derived_key_digest,
|
derived_key_digest,
|
||||||
} => self.handle_key_upgrade(hkdf_salt, derived_key_digest).await,
|
} => self.handle_key_upgrade(hkdf_salt, derived_key_digest).await,
|
||||||
|
ClientRequest::ForgetMe { client, stats } => self.handle_forget_me(client, stats).await,
|
||||||
_ => Err(RequestHandlingError::UnknownEncryptedTextRequest),
|
_ => Err(RequestHandlingError::UnknownEncryptedTextRequest),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,6 +114,10 @@ pub(crate) struct FreshHandler<R, S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<R, S> FreshHandler<R, S> {
|
impl<R, S> FreshHandler<R, S> {
|
||||||
|
pub(crate) fn shared_state(&self) -> &CommonHandlerState {
|
||||||
|
&self.shared_state
|
||||||
|
}
|
||||||
|
|
||||||
// for time being we assume handle is always constructed from raw socket.
|
// for time being we assume handle is always constructed from raw socket.
|
||||||
// if we decide we want to change it, that's not too difficult
|
// if we decide we want to change it, that's not too difficult
|
||||||
// also at this point I'm not entirely sure how to deal with this warning without
|
// also at this point I'm not entirely sure how to deal with this warning without
|
||||||
|
|||||||
@@ -61,7 +61,13 @@ impl Listener {
|
|||||||
remote_addr,
|
remote_addr,
|
||||||
shutdown,
|
shutdown,
|
||||||
);
|
);
|
||||||
tokio::spawn(handle.start_handling());
|
tokio::spawn(async move {
|
||||||
|
// TODO: refactor it similarly to the mixnet listener on the nym-node
|
||||||
|
let metrics_ref = handle.shared_state.metrics.clone();
|
||||||
|
metrics_ref.network.new_ingress_websocket_client();
|
||||||
|
handle.start_handling().await;
|
||||||
|
metrics_ref.network.disconnected_ingress_websocket_client();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
Err(err) => warn!("failed to get client: {err}"),
|
Err(err) => warn!("failed to get client: {err}"),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ mod internal_service_providers;
|
|||||||
pub use client_handling::active_clients::ActiveClientsStore;
|
pub use client_handling::active_clients::ActiveClientsStore;
|
||||||
pub use nym_gateway_stats_storage::PersistentStatsStorage;
|
pub use nym_gateway_stats_storage::PersistentStatsStorage;
|
||||||
pub use nym_gateway_storage::{error::GatewayStorageError, GatewayStorage};
|
pub use nym_gateway_storage::{error::GatewayStorageError, GatewayStorage};
|
||||||
|
use nym_node_metrics::NymNodeMetrics;
|
||||||
pub use nym_sdk::{NymApiTopologyProvider, NymApiTopologyProviderConfig, UserAgent};
|
pub use nym_sdk::{NymApiTopologyProvider, NymApiTopologyProviderConfig, UserAgent};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -81,6 +82,8 @@ pub struct GatewayTasksBuilder {
|
|||||||
|
|
||||||
metrics_sender: MetricEventsSender,
|
metrics_sender: MetricEventsSender,
|
||||||
|
|
||||||
|
metrics: NymNodeMetrics,
|
||||||
|
|
||||||
mnemonic: Arc<Zeroizing<bip39::Mnemonic>>,
|
mnemonic: Arc<Zeroizing<bip39::Mnemonic>>,
|
||||||
|
|
||||||
shutdown: TaskClient,
|
shutdown: TaskClient,
|
||||||
@@ -102,12 +105,14 @@ impl Drop for GatewayTasksBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl GatewayTasksBuilder {
|
impl GatewayTasksBuilder {
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
config: Config,
|
config: Config,
|
||||||
identity: Arc<ed25519::KeyPair>,
|
identity: Arc<ed25519::KeyPair>,
|
||||||
storage: GatewayStorage,
|
storage: GatewayStorage,
|
||||||
mix_packet_sender: MixForwardingSender,
|
mix_packet_sender: MixForwardingSender,
|
||||||
metrics_sender: MetricEventsSender,
|
metrics_sender: MetricEventsSender,
|
||||||
|
metrics: NymNodeMetrics,
|
||||||
mnemonic: Arc<Zeroizing<bip39::Mnemonic>>,
|
mnemonic: Arc<Zeroizing<bip39::Mnemonic>>,
|
||||||
shutdown: TaskClient,
|
shutdown: TaskClient,
|
||||||
) -> GatewayTasksBuilder {
|
) -> GatewayTasksBuilder {
|
||||||
@@ -121,6 +126,7 @@ impl GatewayTasksBuilder {
|
|||||||
storage,
|
storage,
|
||||||
mix_packet_sender,
|
mix_packet_sender,
|
||||||
metrics_sender,
|
metrics_sender,
|
||||||
|
metrics,
|
||||||
mnemonic,
|
mnemonic,
|
||||||
shutdown,
|
shutdown,
|
||||||
ecash_manager: None,
|
ecash_manager: None,
|
||||||
@@ -246,6 +252,7 @@ impl GatewayTasksBuilder {
|
|||||||
local_identity: Arc::clone(&self.identity_keypair),
|
local_identity: Arc::clone(&self.identity_keypair),
|
||||||
only_coconut_credentials: self.config.gateway.enforce_zk_nyms,
|
only_coconut_credentials: self.config.gateway.enforce_zk_nyms,
|
||||||
bandwidth_cfg: (&self.config).into(),
|
bandwidth_cfg: (&self.config).into(),
|
||||||
|
metrics: self.metrics.clone(),
|
||||||
metrics_sender: self.metrics_sender.clone(),
|
metrics_sender: self.metrics_sender.clone(),
|
||||||
outbound_mix_sender: self.mix_packet_sender.clone(),
|
outbound_mix_sender: self.mix_packet_sender.clone(),
|
||||||
active_clients_store: active_clients_store.clone(),
|
active_clients_store: active_clients_store.clone(),
|
||||||
@@ -443,6 +450,7 @@ impl GatewayTasksBuilder {
|
|||||||
pub async fn try_start_wireguard(
|
pub async fn try_start_wireguard(
|
||||||
&mut self,
|
&mut self,
|
||||||
) -> Result<Arc<nym_wireguard::WgApiWrapper>, Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<Arc<nym_wireguard::WgApiWrapper>, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let _ = self.metrics.clone();
|
||||||
unimplemented!("wireguard is not supported on this platform")
|
unimplemented!("wireguard is not supported on this platform")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -460,6 +468,7 @@ impl GatewayTasksBuilder {
|
|||||||
|
|
||||||
let wg_handle = nym_wireguard::start_wireguard(
|
let wg_handle = nym_wireguard::start_wireguard(
|
||||||
self.storage.clone(),
|
self.storage.clone(),
|
||||||
|
self.metrics.clone(),
|
||||||
all_peers,
|
all_peers,
|
||||||
self.shutdown.fork("wireguard"),
|
self.shutdown.fork("wireguard"),
|
||||||
wireguard_data,
|
wireguard_data,
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nym-api"
|
name = "nym-api"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
version = "1.1.47"
|
version = "1.1.48"
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
|
|||||||
@@ -183,6 +183,8 @@ impl PacketSender {
|
|||||||
gateway_packet_router,
|
gateway_packet_router,
|
||||||
Some(fresh_gateway_client_data.bandwidth_controller.clone()),
|
Some(fresh_gateway_client_data.bandwidth_controller.clone()),
|
||||||
nym_statistics_common::clients::ClientStatsSender::new(None),
|
nym_statistics_common::clients::ClientStatsSender::new(None),
|
||||||
|
#[cfg(unix)]
|
||||||
|
None,
|
||||||
task_client,
|
task_client,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use futures::{stream, StreamExt};
|
|||||||
use nym_api_requests::legacy::{LegacyGatewayBondWithId, LegacyMixNodeDetailsWithLayer};
|
use nym_api_requests::legacy::{LegacyGatewayBondWithId, LegacyMixNodeDetailsWithLayer};
|
||||||
use nym_api_requests::models::{DescribedNodeType, NymNodeData, NymNodeDescription};
|
use nym_api_requests::models::{DescribedNodeType, NymNodeData, NymNodeDescription};
|
||||||
use nym_config::defaults::DEFAULT_NYM_NODE_HTTP_PORT;
|
use nym_config::defaults::DEFAULT_NYM_NODE_HTTP_PORT;
|
||||||
|
use nym_crypto::asymmetric::ed25519;
|
||||||
use nym_mixnet_contract_common::{LegacyMixLayer, NodeId, NymNodeDetails};
|
use nym_mixnet_contract_common::{LegacyMixLayer, NodeId, NymNodeDetails};
|
||||||
use nym_node_requests::api::client::{NymNodeApiClientError, NymNodeApiClientExt};
|
use nym_node_requests::api::client::{NymNodeApiClientError, NymNodeApiClientExt};
|
||||||
use nym_topology::gateway::GatewayConversionError;
|
use nym_topology::gateway::GatewayConversionError;
|
||||||
@@ -58,6 +59,13 @@ pub enum NodeDescribeCacheError {
|
|||||||
#[error("could not verify signed host information for node {node_id}")]
|
#[error("could not verify signed host information for node {node_id}")]
|
||||||
MissignedHostInformation { node_id: NodeId },
|
MissignedHostInformation { node_id: NodeId },
|
||||||
|
|
||||||
|
#[error("identity of node {node_id} does not match. expected {expected} but got {got}")]
|
||||||
|
MismatchedIdentity {
|
||||||
|
node_id: NodeId,
|
||||||
|
expected: String,
|
||||||
|
got: String,
|
||||||
|
},
|
||||||
|
|
||||||
#[error("node {node_id} is announcing an illegal ip address")]
|
#[error("node {node_id} is announcing an illegal ip address")]
|
||||||
IllegalIpAddress { node_id: NodeId },
|
IllegalIpAddress { node_id: NodeId },
|
||||||
}
|
}
|
||||||
@@ -289,6 +297,15 @@ async fn try_get_description(
|
|||||||
|
|
||||||
let host_info = client.get_host_information().await.map_err(map_query_err)?;
|
let host_info = client.get_host_information().await.map_err(map_query_err)?;
|
||||||
|
|
||||||
|
// check if the identity key matches the information provided during bonding
|
||||||
|
if data.expected_identity != host_info.keys.ed25519_identity {
|
||||||
|
return Err(NodeDescribeCacheError::MismatchedIdentity {
|
||||||
|
node_id: data.node_id,
|
||||||
|
expected: data.expected_identity.to_base58_string(),
|
||||||
|
got: host_info.keys.ed25519_identity.to_base58_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if !host_info.verify_host_information() {
|
if !host_info.verify_host_information() {
|
||||||
return Err(NodeDescribeCacheError::MissignedHostInformation {
|
return Err(NodeDescribeCacheError::MissignedHostInformation {
|
||||||
node_id: data.node_id,
|
node_id: data.node_id,
|
||||||
@@ -315,47 +332,58 @@ async fn try_get_description(
|
|||||||
pub(crate) struct RefreshData {
|
pub(crate) struct RefreshData {
|
||||||
host: String,
|
host: String,
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
|
expected_identity: ed25519::PublicKey,
|
||||||
node_type: DescribedNodeType,
|
node_type: DescribedNodeType,
|
||||||
|
|
||||||
port: Option<u16>,
|
port: Option<u16>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a LegacyMixNodeDetailsWithLayer> for RefreshData {
|
impl<'a> TryFrom<&'a LegacyMixNodeDetailsWithLayer> for RefreshData {
|
||||||
fn from(node: &'a LegacyMixNodeDetailsWithLayer) -> Self {
|
type Error = ed25519::Ed25519RecoveryError;
|
||||||
RefreshData::new(
|
|
||||||
|
fn try_from(node: &'a LegacyMixNodeDetailsWithLayer) -> Result<Self, Self::Error> {
|
||||||
|
Ok(RefreshData::new(
|
||||||
&node.bond_information.mix_node.host,
|
&node.bond_information.mix_node.host,
|
||||||
|
node.bond_information.identity().parse()?,
|
||||||
DescribedNodeType::LegacyMixnode,
|
DescribedNodeType::LegacyMixnode,
|
||||||
node.mix_id(),
|
node.mix_id(),
|
||||||
Some(node.bond_information.mix_node.http_api_port),
|
Some(node.bond_information.mix_node.http_api_port),
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a LegacyGatewayBondWithId> for RefreshData {
|
impl<'a> TryFrom<&'a LegacyGatewayBondWithId> for RefreshData {
|
||||||
fn from(node: &'a LegacyGatewayBondWithId) -> Self {
|
type Error = ed25519::Ed25519RecoveryError;
|
||||||
RefreshData::new(
|
|
||||||
|
fn try_from(node: &'a LegacyGatewayBondWithId) -> Result<Self, Self::Error> {
|
||||||
|
Ok(RefreshData::new(
|
||||||
&node.bond.gateway.host,
|
&node.bond.gateway.host,
|
||||||
|
node.bond.identity().parse()?,
|
||||||
DescribedNodeType::LegacyGateway,
|
DescribedNodeType::LegacyGateway,
|
||||||
node.node_id,
|
node.node_id,
|
||||||
None,
|
None,
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a NymNodeDetails> for RefreshData {
|
impl<'a> TryFrom<&'a NymNodeDetails> for RefreshData {
|
||||||
fn from(node: &'a NymNodeDetails) -> Self {
|
type Error = ed25519::Ed25519RecoveryError;
|
||||||
RefreshData::new(
|
|
||||||
|
fn try_from(node: &'a NymNodeDetails) -> Result<Self, Self::Error> {
|
||||||
|
Ok(RefreshData::new(
|
||||||
&node.bond_information.node.host,
|
&node.bond_information.node.host,
|
||||||
|
node.bond_information.identity().parse()?,
|
||||||
DescribedNodeType::NymNode,
|
DescribedNodeType::NymNode,
|
||||||
node.node_id(),
|
node.node_id(),
|
||||||
node.bond_information.node.custom_http_port,
|
node.bond_information.node.custom_http_port,
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RefreshData {
|
impl RefreshData {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
host: impl Into<String>,
|
host: impl Into<String>,
|
||||||
|
expected_identity: ed25519::PublicKey,
|
||||||
node_type: DescribedNodeType,
|
node_type: DescribedNodeType,
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
port: Option<u16>,
|
port: Option<u16>,
|
||||||
@@ -363,6 +391,7 @@ impl RefreshData {
|
|||||||
RefreshData {
|
RefreshData {
|
||||||
host: host.into(),
|
host: host.into(),
|
||||||
node_id,
|
node_id,
|
||||||
|
expected_identity,
|
||||||
node_type,
|
node_type,
|
||||||
port,
|
port,
|
||||||
}
|
}
|
||||||
@@ -404,7 +433,9 @@ impl CacheItemProvider for NodeDescriptionProvider {
|
|||||||
None => error!("failed to obtain mixnodes information from the cache"),
|
None => error!("failed to obtain mixnodes information from the cache"),
|
||||||
Some(legacy_mixnodes) => {
|
Some(legacy_mixnodes) => {
|
||||||
for node in &**legacy_mixnodes {
|
for node in &**legacy_mixnodes {
|
||||||
nodes_to_query.push(node.into())
|
if let Ok(data) = node.try_into() {
|
||||||
|
nodes_to_query.push(data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -413,7 +444,9 @@ impl CacheItemProvider for NodeDescriptionProvider {
|
|||||||
None => error!("failed to obtain gateways information from the cache"),
|
None => error!("failed to obtain gateways information from the cache"),
|
||||||
Some(legacy_gateways) => {
|
Some(legacy_gateways) => {
|
||||||
for node in &**legacy_gateways {
|
for node in &**legacy_gateways {
|
||||||
nodes_to_query.push(node.into())
|
if let Ok(data) = node.try_into() {
|
||||||
|
nodes_to_query.push(data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -422,7 +455,9 @@ impl CacheItemProvider for NodeDescriptionProvider {
|
|||||||
None => error!("failed to obtain nym-nodes information from the cache"),
|
None => error!("failed to obtain nym-nodes information from the cache"),
|
||||||
Some(nym_nodes) => {
|
Some(nym_nodes) => {
|
||||||
for node in &**nym_nodes {
|
for node in &**nym_nodes {
|
||||||
nodes_to_query.push(node.into())
|
if let Ok(data) = node.try_into() {
|
||||||
|
nodes_to_query.push(data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -385,7 +385,7 @@ impl NymContractCache {
|
|||||||
.iter()
|
.iter()
|
||||||
.find(|n| n.bond_information.identity() == encoded_identity)
|
.find(|n| n.bond_information.identity() == encoded_identity)
|
||||||
{
|
{
|
||||||
return Some(nym_node.into());
|
return nym_node.try_into().ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. check legacy mixnodes
|
// 2. check legacy mixnodes
|
||||||
@@ -394,7 +394,7 @@ impl NymContractCache {
|
|||||||
.iter()
|
.iter()
|
||||||
.find(|n| n.bond_information.identity() == encoded_identity)
|
.find(|n| n.bond_information.identity() == encoded_identity)
|
||||||
{
|
{
|
||||||
return Some(mixnode.into());
|
return mixnode.try_into().ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. check legacy gateways
|
// 3. check legacy gateways
|
||||||
@@ -403,7 +403,7 @@ impl NymContractCache {
|
|||||||
.iter()
|
.iter()
|
||||||
.find(|n| n.identity() == &encoded_identity)
|
.find(|n| n.identity() == &encoded_identity)
|
||||||
{
|
{
|
||||||
return Some(gateway.into());
|
return gateway.try_into().ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ use crate::support::storage::models::{
|
|||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use nym_mixnet_contract_common::NodeId;
|
use nym_mixnet_contract_common::NodeId;
|
||||||
use nym_types::monitoring::NodeResult;
|
use nym_types::monitoring::NodeResult;
|
||||||
|
use sqlx::sqlite::{SqliteAutoVacuum, SqliteSynchronous};
|
||||||
use sqlx::ConnectOptions;
|
use sqlx::ConnectOptions;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -67,6 +68,9 @@ impl NymApiStorage {
|
|||||||
// TODO: we can inject here more stuff based on our nym-api global config
|
// TODO: we can inject here more stuff based on our nym-api global config
|
||||||
// struct. Maybe different pool size or timeout intervals?
|
// struct. Maybe different pool size or timeout intervals?
|
||||||
let connect_opts = sqlx::sqlite::SqliteConnectOptions::new()
|
let connect_opts = sqlx::sqlite::SqliteConnectOptions::new()
|
||||||
|
.journal_mode(sqlx::sqlite::SqliteJournalMode::Wal)
|
||||||
|
.synchronous(SqliteSynchronous::Normal)
|
||||||
|
.auto_vacuum(SqliteAutoVacuum::Incremental)
|
||||||
.filename(database_path)
|
.filename(database_path)
|
||||||
.create_if_missing(true)
|
.create_if_missing(true)
|
||||||
.log_statements(LevelFilter::Trace)
|
.log_statements(LevelFilter::Trace)
|
||||||
|
|||||||
@@ -1,8 +1,22 @@
|
|||||||
// Copyright 2024 Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2024 Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
use nym_bin_common::bin_info;
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
use tracing::{debug, info, warn};
|
use tokio_util::sync::CancellationToken;
|
||||||
|
use tracing::{debug, error, info, warn};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
cli::Cli,
|
||||||
|
deposit_maker::DepositMaker,
|
||||||
|
error::VpnApiError,
|
||||||
|
http::{
|
||||||
|
state::{ApiState, ChainClient},
|
||||||
|
HttpServer,
|
||||||
|
},
|
||||||
|
storage::VpnApiStorage,
|
||||||
|
tasks::StoragePruner,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct LockTimer {
|
pub struct LockTimer {
|
||||||
created: OffsetDateTime,
|
created: OffsetDateTime,
|
||||||
@@ -40,3 +54,88 @@ impl Default for LockTimer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn wait_for_signal() {
|
||||||
|
use tokio::signal::unix::{signal, SignalKind};
|
||||||
|
|
||||||
|
// if we fail to setup the signals, we should just blow up
|
||||||
|
#[allow(clippy::expect_used)]
|
||||||
|
let mut sigterm = signal(SignalKind::terminate()).expect("Failed to setup SIGTERM channel");
|
||||||
|
#[allow(clippy::expect_used)]
|
||||||
|
let mut sigquit = signal(SignalKind::quit()).expect("Failed to setup SIGQUIT channel");
|
||||||
|
|
||||||
|
tokio::select! {
|
||||||
|
_ = tokio::signal::ctrl_c() => {
|
||||||
|
info!("Received SIGINT");
|
||||||
|
},
|
||||||
|
_ = sigterm.recv() => {
|
||||||
|
info!("Received SIGTERM");
|
||||||
|
}
|
||||||
|
_ = sigquit.recv() => {
|
||||||
|
info!("Received SIGQUIT");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_sha_short() -> &'static str {
|
||||||
|
let bin_info = bin_info!();
|
||||||
|
if bin_info.commit_sha.len() < 7 {
|
||||||
|
panic!("unavailable build commit sha")
|
||||||
|
}
|
||||||
|
|
||||||
|
if bin_info.commit_sha == "VERGEN_IDEMPOTENT_OUTPUT" {
|
||||||
|
error!("the binary hasn't been built correctly. it doesn't have a commit sha information");
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
&bin_info.commit_sha[..7]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn run_api(cli: Cli) -> Result<(), VpnApiError> {
|
||||||
|
// create the tasks
|
||||||
|
let bind_address = cli.bind_address();
|
||||||
|
|
||||||
|
let storage = VpnApiStorage::init(cli.persistent_storage_path()).await?;
|
||||||
|
let mnemonic = cli.mnemonic;
|
||||||
|
let auth_token = cli.http_auth_token;
|
||||||
|
let webhook_cfg = cli.webhook;
|
||||||
|
let chain_client = ChainClient::new(mnemonic)?;
|
||||||
|
let cancellation_token = CancellationToken::new();
|
||||||
|
|
||||||
|
let deposit_maker = DepositMaker::new(
|
||||||
|
build_sha_short(),
|
||||||
|
chain_client.clone(),
|
||||||
|
cli.max_concurrent_deposits,
|
||||||
|
cancellation_token.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let deposit_request_sender = deposit_maker.deposit_request_sender();
|
||||||
|
let api_state = ApiState::new(
|
||||||
|
storage.clone(),
|
||||||
|
webhook_cfg,
|
||||||
|
chain_client,
|
||||||
|
deposit_request_sender,
|
||||||
|
cancellation_token.clone(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let http_server = HttpServer::new(
|
||||||
|
bind_address,
|
||||||
|
api_state.clone(),
|
||||||
|
auth_token,
|
||||||
|
cancellation_token.clone(),
|
||||||
|
);
|
||||||
|
let storage_pruner = StoragePruner::new(cancellation_token, storage);
|
||||||
|
|
||||||
|
// spawn all the tasks
|
||||||
|
api_state.try_spawn(http_server.run_forever());
|
||||||
|
api_state.try_spawn(storage_pruner.run_forever());
|
||||||
|
api_state.try_spawn(deposit_maker.run_forever());
|
||||||
|
|
||||||
|
// wait for cancel signal (SIGINT, SIGTERM or SIGQUIT)
|
||||||
|
wait_for_signal().await;
|
||||||
|
|
||||||
|
// cancel all the tasks and wait for all task to terminate
|
||||||
|
api_state.cancel_and_wait().await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
@@ -710,7 +710,7 @@ pub(crate) struct ChainWritePermit<'a> {
|
|||||||
inner: RwLockWriteGuard<'a, DirectSigningHttpRpcNyxdClient>,
|
inner: RwLockWriteGuard<'a, DirectSigningHttpRpcNyxdClient>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ChainWritePermit<'a> {
|
impl ChainWritePermit<'_> {
|
||||||
pub(crate) async fn make_deposits(
|
pub(crate) async fn make_deposits(
|
||||||
self,
|
self,
|
||||||
short_sha: &'static str,
|
short_sha: &'static str,
|
||||||
|
|||||||
@@ -6,117 +6,30 @@
|
|||||||
#![warn(clippy::todo)]
|
#![warn(clippy::todo)]
|
||||||
#![warn(clippy::dbg_macro)]
|
#![warn(clippy::dbg_macro)]
|
||||||
|
|
||||||
use crate::cli::Cli;
|
cfg_if::cfg_if! {
|
||||||
use crate::deposit_maker::DepositMaker;
|
if #[cfg(unix)] {
|
||||||
use crate::error::VpnApiError;
|
use crate::cli::Cli;
|
||||||
use crate::http::state::{ApiState, ChainClient};
|
use clap::Parser;
|
||||||
use crate::http::HttpServer;
|
use nym_bin_common::bin_info_owned;
|
||||||
use crate::storage::VpnApiStorage;
|
use nym_bin_common::logging::setup_tracing_logger;
|
||||||
use crate::tasks::StoragePruner;
|
use nym_network_defaults::setup_env;
|
||||||
use clap::Parser;
|
use tracing::{info, trace};
|
||||||
use nym_bin_common::logging::setup_tracing_logger;
|
|
||||||
use nym_bin_common::{bin_info, bin_info_owned};
|
|
||||||
use nym_network_defaults::setup_env;
|
|
||||||
use tokio_util::sync::CancellationToken;
|
|
||||||
use tracing::{error, info, trace};
|
|
||||||
|
|
||||||
pub mod cli;
|
pub mod cli;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod credentials;
|
pub mod credentials;
|
||||||
mod deposit_maker;
|
mod deposit_maker;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod helpers;
|
pub mod helpers;
|
||||||
pub mod http;
|
pub mod http;
|
||||||
pub mod nym_api_helpers;
|
pub mod nym_api_helpers;
|
||||||
pub mod storage;
|
pub mod storage;
|
||||||
pub mod tasks;
|
pub mod tasks;
|
||||||
mod webhook;
|
mod webhook;
|
||||||
|
|
||||||
pub async fn wait_for_signal() {
|
|
||||||
use tokio::signal::unix::{signal, SignalKind};
|
|
||||||
|
|
||||||
// if we fail to setup the signals, we should just blow up
|
|
||||||
#[allow(clippy::expect_used)]
|
|
||||||
let mut sigterm = signal(SignalKind::terminate()).expect("Failed to setup SIGTERM channel");
|
|
||||||
#[allow(clippy::expect_used)]
|
|
||||||
let mut sigquit = signal(SignalKind::quit()).expect("Failed to setup SIGQUIT channel");
|
|
||||||
|
|
||||||
tokio::select! {
|
|
||||||
_ = tokio::signal::ctrl_c() => {
|
|
||||||
info!("Received SIGINT");
|
|
||||||
},
|
|
||||||
_ = sigterm.recv() => {
|
|
||||||
info!("Received SIGTERM");
|
|
||||||
}
|
|
||||||
_ = sigquit.recv() => {
|
|
||||||
info!("Received SIGQUIT");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_sha_short() -> &'static str {
|
#[cfg(unix)]
|
||||||
let bin_info = bin_info!();
|
|
||||||
if bin_info.commit_sha.len() < 7 {
|
|
||||||
panic!("unavailable build commit sha")
|
|
||||||
}
|
|
||||||
|
|
||||||
if bin_info.commit_sha == "VERGEN_IDEMPOTENT_OUTPUT" {
|
|
||||||
error!("the binary hasn't been built correctly. it doesn't have a commit sha information");
|
|
||||||
return "unknown";
|
|
||||||
}
|
|
||||||
|
|
||||||
&bin_info.commit_sha[..7]
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run_api(cli: Cli) -> Result<(), VpnApiError> {
|
|
||||||
// create the tasks
|
|
||||||
let bind_address = cli.bind_address();
|
|
||||||
|
|
||||||
let storage = VpnApiStorage::init(cli.persistent_storage_path()).await?;
|
|
||||||
let mnemonic = cli.mnemonic;
|
|
||||||
let auth_token = cli.http_auth_token;
|
|
||||||
let webhook_cfg = cli.webhook;
|
|
||||||
let chain_client = ChainClient::new(mnemonic)?;
|
|
||||||
let cancellation_token = CancellationToken::new();
|
|
||||||
|
|
||||||
let deposit_maker = DepositMaker::new(
|
|
||||||
build_sha_short(),
|
|
||||||
chain_client.clone(),
|
|
||||||
cli.max_concurrent_deposits,
|
|
||||||
cancellation_token.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let deposit_request_sender = deposit_maker.deposit_request_sender();
|
|
||||||
let api_state = ApiState::new(
|
|
||||||
storage.clone(),
|
|
||||||
webhook_cfg,
|
|
||||||
chain_client,
|
|
||||||
deposit_request_sender,
|
|
||||||
cancellation_token.clone(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
let http_server = HttpServer::new(
|
|
||||||
bind_address,
|
|
||||||
api_state.clone(),
|
|
||||||
auth_token,
|
|
||||||
cancellation_token.clone(),
|
|
||||||
);
|
|
||||||
let storage_pruner = StoragePruner::new(cancellation_token, storage);
|
|
||||||
|
|
||||||
// spawn all the tasks
|
|
||||||
api_state.try_spawn(http_server.run_forever());
|
|
||||||
api_state.try_spawn(storage_pruner.run_forever());
|
|
||||||
api_state.try_spawn(deposit_maker.run_forever());
|
|
||||||
|
|
||||||
// wait for cancel signal (SIGINT, SIGTERM or SIGQUIT)
|
|
||||||
wait_for_signal().await;
|
|
||||||
|
|
||||||
// cancel all the tasks and wait for all task to terminate
|
|
||||||
api_state.cancel_and_wait().await;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
// std::env::set_var(
|
// std::env::set_var(
|
||||||
@@ -134,6 +47,13 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
let bin_info = bin_info_owned!();
|
let bin_info = bin_info_owned!();
|
||||||
info!("using the following version: {bin_info}");
|
info!("using the following version: {bin_info}");
|
||||||
|
|
||||||
run_api(cli).await?;
|
helpers::run_api(cli).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> anyhow::Result<()> {
|
||||||
|
eprintln!("This tool is only supported on Unix systems");
|
||||||
|
std::process::exit(1)
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use nym_validator_client::ecash::BlindedSignatureResponse;
|
|||||||
use nym_validator_client::nym_api::EpochId;
|
use nym_validator_client::nym_api::EpochId;
|
||||||
use nym_validator_client::nyxd::contract_traits::ecash_query_client::DepositId;
|
use nym_validator_client::nyxd::contract_traits::ecash_query_client::DepositId;
|
||||||
use nym_validator_client::nyxd::Coin;
|
use nym_validator_client::nyxd::Coin;
|
||||||
|
use sqlx::sqlite::{SqliteAutoVacuum, SqliteSynchronous};
|
||||||
use sqlx::ConnectOptions;
|
use sqlx::ConnectOptions;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
@@ -40,6 +41,9 @@ impl VpnApiStorage {
|
|||||||
debug!("Attempting to connect to database");
|
debug!("Attempting to connect to database");
|
||||||
|
|
||||||
let opts = sqlx::sqlite::SqliteConnectOptions::new()
|
let opts = sqlx::sqlite::SqliteConnectOptions::new()
|
||||||
|
.journal_mode(sqlx::sqlite::SqliteJournalMode::Wal)
|
||||||
|
.synchronous(SqliteSynchronous::Normal)
|
||||||
|
.auto_vacuum(SqliteAutoVacuum::Incremental)
|
||||||
.filename(database_path)
|
.filename(database_path)
|
||||||
.create_if_missing(true)
|
.create_if_missing(true)
|
||||||
.log_statements(LevelFilter::Trace)
|
.log_statements(LevelFilter::Trace)
|
||||||
|
|||||||
@@ -27,12 +27,14 @@ tokio = { workspace = true, features = ["macros", "time"] }
|
|||||||
tokio-util = { workspace = true }
|
tokio-util = { workspace = true }
|
||||||
utoipa = { workspace = true, features = ["axum_extras"] }
|
utoipa = { workspace = true, features = ["axum_extras"] }
|
||||||
utoipa-swagger-ui = { workspace = true, features = ["axum"] }
|
utoipa-swagger-ui = { workspace = true, features = ["axum"] }
|
||||||
|
tokio-postgres = "0.7"
|
||||||
|
|
||||||
# internal
|
# internal
|
||||||
nym-bin-common = { path = "../common/bin-common" }
|
nym-bin-common = { path = "../common/bin-common" }
|
||||||
nym-client-core = { path = "../common/client-core" }
|
nym-client-core = { path = "../common/client-core" }
|
||||||
nym-crypto = { path = "../common/crypto" }
|
nym-crypto = { path = "../common/crypto" }
|
||||||
nym-network-defaults = { path = "../common/network-defaults" }
|
nym-network-defaults = { path = "../common/network-defaults" }
|
||||||
|
nym-gateway-requests = { path = "../common/gateway-requests" }
|
||||||
nym-sdk = { path = "../sdk/rust/nym-sdk" }
|
nym-sdk = { path = "../sdk/rust/nym-sdk" }
|
||||||
nym-sphinx = { path = "../common/nymsphinx" }
|
nym-sphinx = { path = "../common/nymsphinx" }
|
||||||
nym-topology = { path = "../common/topology" }
|
nym-topology = { path = "../common/topology" }
|
||||||
|
|||||||
@@ -9,13 +9,14 @@ network=${NYM_NETWORK:-mainnet}
|
|||||||
timeout=${LOCUST_TIMEOUT:-600}
|
timeout=${LOCUST_TIMEOUT:-600}
|
||||||
users=${LOCUST_USERS:-10}
|
users=${LOCUST_USERS:-10}
|
||||||
processes=${LOCUST_PROCESSES:-4}
|
processes=${LOCUST_PROCESSES:-4}
|
||||||
|
_database_url=${DATABASE_URL}
|
||||||
|
|
||||||
RUST_LOG=info nym-network-monitor --env envs/"${network}".env --private-key "${_private_key}" &
|
RUST_LOG=info nym-network-monitor --env envs/"${network}".env --private-key "${_private_key}" --database-url "${_database_url}" &
|
||||||
nnm_pid=$!
|
nnm_pid=$!
|
||||||
|
|
||||||
sleep 10
|
sleep 10
|
||||||
|
|
||||||
python -m locust -H http://${NYM_NETWORK_MONITOR_HOST}:${NYM_NETWORK_MONITOR_PORT} --processes "${processes}" --autostart --autoquit 60 -u "${users}" -t "${timeout}"s &
|
python -m locust -H http://"${NYM_NETWORK_MONITOR_HOST}":"${NYM_NETWORK_MONITOR_PORT}" --processes "${processes}" --autostart --autoquit 60 -u "${users}" -t "${timeout}"s &
|
||||||
locust_pid=$!
|
locust_pid=$!
|
||||||
|
|
||||||
wait $locust_pid
|
wait $locust_pid
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
use std::collections::{HashMap, HashSet};
|
use std::{
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use futures::{stream::FuturesUnordered, StreamExt};
|
use futures::{pin_mut, stream::FuturesUnordered, StreamExt};
|
||||||
use log::{debug, info};
|
use log::{debug, info};
|
||||||
use nym_sphinx::chunking::{monitoring, SentFragment};
|
use nym_sphinx::chunking::{monitoring, SentFragment};
|
||||||
use nym_topology::{gateway, mix, NymTopology};
|
use nym_topology::{gateway, mix, NymTopology};
|
||||||
@@ -10,6 +13,7 @@ use nym_validator_client::nym_api::routes::{API_VERSION, STATUS, SUBMIT_GATEWAY,
|
|||||||
use rand::SeedableRng;
|
use rand::SeedableRng;
|
||||||
use rand_chacha::ChaCha8Rng;
|
use rand_chacha::ChaCha8Rng;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tokio_postgres::{binary_copy::BinaryCopyInWriter, types::Type, Client};
|
||||||
use utoipa::ToSchema;
|
use utoipa::ToSchema;
|
||||||
|
|
||||||
use crate::{NYM_API_URL, PRIVATE_KEY, TOPOLOGY};
|
use crate::{NYM_API_URL, PRIVATE_KEY, TOPOLOGY};
|
||||||
@@ -23,20 +27,20 @@ struct HydratedRoute {
|
|||||||
struct GatewayStats(u32, u32);
|
struct GatewayStats(u32, u32);
|
||||||
|
|
||||||
impl GatewayStats {
|
impl GatewayStats {
|
||||||
fn new(sent: u32, recv: u32) -> Self {
|
fn new(success: u32, failure: u32) -> Self {
|
||||||
GatewayStats(sent, recv)
|
GatewayStats(success, failure)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn success(&self) -> u32 {
|
fn success(&self) -> u32 {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn failed(&self) -> u32 {
|
fn failure(&self) -> u32 {
|
||||||
self.1
|
self.1
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reliability(&self) -> f64 {
|
fn reliability(&self) -> f64 {
|
||||||
self.success() as f64 / (self.success() + self.failed()) as f64
|
self.success() as f64 / (self.success() + self.failure()) as f64
|
||||||
}
|
}
|
||||||
|
|
||||||
fn incr_success(&mut self) {
|
fn incr_success(&mut self) {
|
||||||
@@ -321,48 +325,125 @@ pub async fn monitor_mixnode_results() -> anyhow::Result<Vec<NodeResult>> {
|
|||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn submit_metrics() -> anyhow::Result<()> {
|
async fn submit_node_stats_to_db(client: Arc<Client>) -> anyhow::Result<()> {
|
||||||
let node_stats = monitor_mixnode_results().await?;
|
let client = Arc::clone(&client);
|
||||||
let gateway_stats = monitor_gateway_results().await?;
|
let node_stats = all_node_stats().await?;
|
||||||
|
|
||||||
info!("Submitting metrics to {}", *NYM_API_URL);
|
let sink = client
|
||||||
let client = reqwest::Client::new();
|
.copy_in("COPY node_stats (node_id, identity, reliability, complete_routes, incomplete_routes) FROM STDIN BINARY")
|
||||||
|
.await?;
|
||||||
|
|
||||||
let node_submit_url = format!("{}/{API_VERSION}/{STATUS}/{SUBMIT_NODE}", &*NYM_API_URL);
|
let writer = BinaryCopyInWriter::new(
|
||||||
let gateway_submit_url = format!("{}/{API_VERSION}/{STATUS}/{SUBMIT_GATEWAY}", &*NYM_API_URL);
|
sink,
|
||||||
|
&[Type::INT4, Type::TEXT, Type::FLOAT8, Type::INT8, Type::INT8],
|
||||||
|
);
|
||||||
|
pin_mut!(writer);
|
||||||
|
|
||||||
info!("Submitting {} mixnode measurements", node_stats.len());
|
for stat in node_stats {
|
||||||
|
writer
|
||||||
|
.as_mut()
|
||||||
|
.write(&[
|
||||||
|
&(stat.mix_id as i32),
|
||||||
|
&stat.identity,
|
||||||
|
&stat.reliability,
|
||||||
|
&(stat.complete_routes as i64),
|
||||||
|
&(stat.incomplete_routes as i64),
|
||||||
|
])
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
node_stats
|
writer.finish().await?;
|
||||||
.chunks(10)
|
|
||||||
.map(|chunk| {
|
|
||||||
let monitor_message =
|
|
||||||
MonitorMessage::new(chunk.to_vec(), PRIVATE_KEY.get().expect("We've set this!"));
|
|
||||||
client.post(&node_submit_url).json(&monitor_message).send()
|
|
||||||
})
|
|
||||||
.collect::<FuturesUnordered<_>>()
|
|
||||||
.collect::<Vec<Result<_, _>>>()
|
|
||||||
.await
|
|
||||||
.into_iter()
|
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
|
||||||
|
|
||||||
info!("Submitting {} gateway measurements", gateway_stats.len());
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
gateway_stats
|
async fn submit_gateway_stats_to_db(client: Arc<Client>) -> anyhow::Result<()> {
|
||||||
.chunks(10)
|
let client = Arc::clone(&client);
|
||||||
.map(|chunk| {
|
let network_account = NetworkAccount::finalize()?;
|
||||||
let monitor_message =
|
let gateway_stats = network_account.gateway_stats;
|
||||||
MonitorMessage::new(chunk.to_vec(), PRIVATE_KEY.get().expect("We've set this!"));
|
|
||||||
client
|
let sink = client
|
||||||
.post(&gateway_submit_url)
|
.copy_in("COPY gateway_stats (identity, reliability, success, failure) FROM STDIN BINARY")
|
||||||
.json(&monitor_message)
|
.await?;
|
||||||
.send()
|
|
||||||
})
|
let writer = BinaryCopyInWriter::new(sink, &[Type::TEXT, Type::FLOAT8, Type::INT8, Type::INT8]);
|
||||||
.collect::<FuturesUnordered<_>>()
|
pin_mut!(writer);
|
||||||
.collect::<Vec<Result<_, _>>>()
|
|
||||||
.await
|
for (key, stats) in gateway_stats {
|
||||||
.into_iter()
|
writer
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.as_mut()
|
||||||
|
.write(&[
|
||||||
|
&key,
|
||||||
|
&stats.reliability(),
|
||||||
|
&(stats.success() as i64),
|
||||||
|
&(stats.failure() as i64),
|
||||||
|
])
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.finish().await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn submit_metrics_to_db(client: Arc<Client>) -> anyhow::Result<()> {
|
||||||
|
let client = Arc::clone(&client);
|
||||||
|
let client2 = Arc::clone(&client);
|
||||||
|
submit_node_stats_to_db(client).await?;
|
||||||
|
submit_gateway_stats_to_db(client2).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn submit_metrics(client: Option<Arc<Client>>) -> anyhow::Result<()> {
|
||||||
|
if let Some(client) = client {
|
||||||
|
submit_metrics_to_db(client).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(private_key) = PRIVATE_KEY.get() {
|
||||||
|
let node_stats = monitor_mixnode_results().await?;
|
||||||
|
let gateway_stats = monitor_gateway_results().await?;
|
||||||
|
|
||||||
|
info!("Submitting metrics to {}", *NYM_API_URL);
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
|
||||||
|
let node_submit_url = format!("{}/{API_VERSION}/{STATUS}/{SUBMIT_NODE}", &*NYM_API_URL);
|
||||||
|
let gateway_submit_url =
|
||||||
|
format!("{}/{API_VERSION}/{STATUS}/{SUBMIT_GATEWAY}", &*NYM_API_URL);
|
||||||
|
|
||||||
|
info!("Submitting {} mixnode measurements", node_stats.len());
|
||||||
|
|
||||||
|
node_stats
|
||||||
|
.chunks(10)
|
||||||
|
.map(|chunk| {
|
||||||
|
let monitor_message = MonitorMessage::new(chunk.to_vec(), private_key);
|
||||||
|
client.post(&node_submit_url).json(&monitor_message).send()
|
||||||
|
})
|
||||||
|
.collect::<FuturesUnordered<_>>()
|
||||||
|
.collect::<Vec<Result<_, _>>>()
|
||||||
|
.await
|
||||||
|
.into_iter()
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
info!("Submitting {} gateway measurements", gateway_stats.len());
|
||||||
|
|
||||||
|
gateway_stats
|
||||||
|
.chunks(10)
|
||||||
|
.map(|chunk| {
|
||||||
|
let monitor_message = MonitorMessage::new(
|
||||||
|
chunk.to_vec(),
|
||||||
|
PRIVATE_KEY.get().expect("We've set this!"),
|
||||||
|
);
|
||||||
|
client
|
||||||
|
.post(&gateway_submit_url)
|
||||||
|
.json(&monitor_message)
|
||||||
|
.send()
|
||||||
|
})
|
||||||
|
.collect::<FuturesUnordered<_>>()
|
||||||
|
.collect::<Vec<Result<_, _>>>()
|
||||||
|
.await
|
||||||
|
.into_iter()
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
}
|
||||||
|
|
||||||
NetworkAccount::empty_buffers();
|
NetworkAccount::empty_buffers();
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ use crate::http::HttpServer;
|
|||||||
use accounting::submit_metrics;
|
use accounting::submit_metrics;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use log::{info, warn};
|
use log::{error, info, warn};
|
||||||
|
use nym_client_core::ForgetMe;
|
||||||
use nym_crypto::asymmetric::ed25519::PrivateKey;
|
use nym_crypto::asymmetric::ed25519::PrivateKey;
|
||||||
use nym_network_defaults::setup_env;
|
use nym_network_defaults::setup_env;
|
||||||
use nym_network_defaults::var_names::NYM_API;
|
use nym_network_defaults::var_names::NYM_API;
|
||||||
@@ -21,6 +22,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
use tokio::sync::OnceCell;
|
use tokio::sync::OnceCell;
|
||||||
use tokio::{signal::ctrl_c, sync::RwLock};
|
use tokio::{signal::ctrl_c, sync::RwLock};
|
||||||
|
use tokio_postgres::NoTls;
|
||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
|
|
||||||
static NYM_API_URL: LazyLock<String> = LazyLock::new(|| {
|
static NYM_API_URL: LazyLock<String> = LazyLock::new(|| {
|
||||||
@@ -56,7 +58,11 @@ async fn make_clients(
|
|||||||
loop {
|
loop {
|
||||||
if Arc::strong_count(&dropped_client) == 1 {
|
if Arc::strong_count(&dropped_client) == 1 {
|
||||||
if let Some(client) = Arc::into_inner(dropped_client) {
|
if let Some(client) = Arc::into_inner(dropped_client) {
|
||||||
client.into_inner().disconnect().await;
|
// let forget_me = ClientRequest::ForgetMe {
|
||||||
|
// also_from_stats: true,
|
||||||
|
// };
|
||||||
|
let client_handle = client.into_inner();
|
||||||
|
client_handle.disconnect().await;
|
||||||
} else {
|
} else {
|
||||||
warn!("Failed to drop client, client had more then one strong ref")
|
warn!("Failed to drop client, client had more then one strong ref")
|
||||||
}
|
}
|
||||||
@@ -89,6 +95,7 @@ async fn make_client(topology: NymTopology) -> Result<MixnetClient> {
|
|||||||
.network_details(net)
|
.network_details(net)
|
||||||
.custom_topology_provider(topology_provider)
|
.custom_topology_provider(topology_provider)
|
||||||
.debug_config(mixnet_debug_config(0))
|
.debug_config(mixnet_debug_config(0))
|
||||||
|
.with_forget_me(ForgetMe::new_all())
|
||||||
// .enable_credentials_mode()
|
// .enable_credentials_mode()
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
@@ -130,7 +137,10 @@ struct Args {
|
|||||||
generate_key_pair: bool,
|
generate_key_pair: bool,
|
||||||
|
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
private_key: String,
|
private_key: Option<String>,
|
||||||
|
|
||||||
|
#[arg(long, env = "DATABASE_URL")]
|
||||||
|
database_url: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_key_pair() -> Result<()> {
|
fn generate_key_pair() -> Result<()> {
|
||||||
@@ -168,8 +178,10 @@ async fn main() -> Result<()> {
|
|||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let pk = PrivateKey::from_base58_string(&args.private_key)?;
|
if let Some(private_key) = args.private_key {
|
||||||
PRIVATE_KEY.set(pk).ok();
|
let pk = PrivateKey::from_base58_string(&private_key)?;
|
||||||
|
PRIVATE_KEY.set(pk).ok();
|
||||||
|
}
|
||||||
|
|
||||||
TOPOLOGY
|
TOPOLOGY
|
||||||
.set(if let Some(topology_file) = args.topology {
|
.set(if let Some(topology_file) = args.topology {
|
||||||
@@ -197,16 +209,31 @@ async fn main() -> Result<()> {
|
|||||||
|
|
||||||
info!("Waiting for message (ctrl-c to exit)");
|
info!("Waiting for message (ctrl-c to exit)");
|
||||||
|
|
||||||
|
let client = if let Some(database_url) = args.database_url {
|
||||||
|
let (client, connection) = tokio_postgres::connect(&database_url, NoTls).await?;
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
if let Err(e) = connection.await {
|
||||||
|
error!("Postgres connection error: {}", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(Arc::new(client))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
let client = client.as_ref().map(Arc::clone);
|
||||||
match tokio::time::timeout(Duration::from_secs(600), ctrl_c()).await {
|
match tokio::time::timeout(Duration::from_secs(600), ctrl_c()).await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
info!("Received kill signal, shutting down, submitting final batch of metrics");
|
info!("Received kill signal, shutting down, submitting final batch of metrics");
|
||||||
submit_metrics().await?;
|
submit_metrics(client).await?;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
info!("Submitting metrics, cleaning metric buffers");
|
info!("Submitting metrics, cleaning metric buffers");
|
||||||
submit_metrics().await?;
|
submit_metrics(client).await?;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
set -eu
|
set -eu
|
||||||
export ENVIRONMENT=${ENVIRONMENT:-"sandbox"}
|
export ENVIRONMENT=${ENVIRONMENT:-"sandbox"}
|
||||||
|
|
||||||
probe_git_ref="nym-vpn-core-v1.0.0-rc.14"
|
probe_git_ref="nym-vpn-core-v1.1.0"
|
||||||
|
|
||||||
crate_root=$(dirname $(realpath "$0"))
|
crate_root=$(dirname $(realpath "$0"))
|
||||||
monorepo_root=$(realpath "${crate_root}/../..")
|
monorepo_root=$(realpath "${crate_root}/../..")
|
||||||
@@ -54,7 +54,7 @@ function swarm() {
|
|||||||
echo "All agents completed"
|
echo "All agents completed"
|
||||||
}
|
}
|
||||||
|
|
||||||
copy_gw_probe
|
# copy_gw_probe
|
||||||
build_agent
|
build_agent
|
||||||
|
|
||||||
swarm $workers
|
swarm $workers
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use sqlx::{Connection, SqliteConnection};
|
use sqlx::{Connection, SqliteConnection};
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
use std::fs::Permissions;
|
use std::fs::Permissions;
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
use std::os::unix::fs::PermissionsExt;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
use tokio::{fs::File, io::AsyncWriteExt};
|
use tokio::{fs::File, io::AsyncWriteExt};
|
||||||
|
|
||||||
@@ -39,7 +41,10 @@ async fn write_db_path_to_file(out_dir: &str, db_filename: &str) -> anyhow::Resu
|
|||||||
file.write_all(format!("sqlite3 {}/{}", out_dir, db_filename).as_bytes())
|
file.write_all(format!("sqlite3 {}/{}", out_dir, db_filename).as_bytes())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
file.set_permissions(Permissions::from_mode(0o755))
|
file.set_permissions(Permissions::from_mode(0o755))
|
||||||
.await
|
.await
|
||||||
.map_err(From::from)
|
.map_err(anyhow::Error::from)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
user_rust_log_preference=$RUST_LOG
|
||||||
export NYM_API_CLIENT_TIMEOUT=60
|
export NYM_API_CLIENT_TIMEOUT=60
|
||||||
export EXPLORER_CLIENT_TIMEOUT=60
|
export EXPLORER_CLIENT_TIMEOUT=60
|
||||||
export NODE_STATUS_API_TESTRUN_REFRESH_INTERVAL=120
|
export NODE_STATUS_API_TESTRUN_REFRESH_INTERVAL=120
|
||||||
@@ -20,7 +21,8 @@ function run_bare() {
|
|||||||
set -a
|
set -a
|
||||||
source "${monorepo_root}/envs/${ENVIRONMENT}.env"
|
source "${monorepo_root}/envs/${ENVIRONMENT}.env"
|
||||||
set +a
|
set +a
|
||||||
export RUST_LOG=debug
|
export RUST_LOG=${user_rust_log_preference:-debug}
|
||||||
|
echo "RUST_LOG=${RUST_LOG}"
|
||||||
|
|
||||||
# --conection-url is provided in build.rs
|
# --conection-url is provided in build.rs
|
||||||
cargo run --package nym-node-status-api
|
cargo run --package nym-node-status-api
|
||||||
|
|||||||
@@ -154,31 +154,39 @@ mod api_regression {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use std::{env::var, sync::LazyLock};
|
use std::{env::var, sync::LazyLock};
|
||||||
|
|
||||||
static IPINFO_TOKEN: LazyLock<String> = LazyLock::new(|| var("IPINFO_API_TOKEN").unwrap());
|
static IPINFO_TOKEN: LazyLock<Option<String>> = LazyLock::new(|| var("IPINFO_API_TOKEN").ok());
|
||||||
|
static CI: LazyLock<Option<String>> = LazyLock::new(|| var("CI").ok());
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn should_parse_response() {
|
async fn should_parse_response() {
|
||||||
let client = IpInfoClient::new(&(*IPINFO_TOKEN));
|
if CI.is_none() {
|
||||||
let my_ip = reqwest::get("https://api.ipify.org")
|
return;
|
||||||
.await
|
}
|
||||||
.expect("Couldn't get own IP")
|
if let Some(token) = &*IPINFO_TOKEN {
|
||||||
.text()
|
let client = IpInfoClient::new(token);
|
||||||
.await
|
let my_ip = reqwest::get("https://api.ipify.org")
|
||||||
.unwrap();
|
.await
|
||||||
|
.expect("Couldn't get own IP")
|
||||||
|
.text()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let location_result = client.locate_ip(my_ip).await;
|
let location_result = client.locate_ip(my_ip).await;
|
||||||
assert!(location_result.is_ok(), "Did ipinfo response change?");
|
assert!(location_result.is_ok(), "Did ipinfo response change?");
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
client.check_remaining_bandwidth().await.is_ok(),
|
client.check_remaining_bandwidth().await.is_ok(),
|
||||||
"Failed to check remaining bandwidth?"
|
"Failed to check remaining bandwidth?"
|
||||||
);
|
);
|
||||||
|
|
||||||
// when serialized, these fields should be present because they're exposed over API
|
// when serialized, these fields should be present because they're exposed over API
|
||||||
let location_result = location_result.unwrap();
|
let location_result = location_result.unwrap();
|
||||||
let json = serde_json::to_value(&location_result).unwrap();
|
let json = serde_json::to_value(&location_result).unwrap();
|
||||||
assert!(json.get("two_letter_iso_country_code").is_some());
|
assert!(json.get("two_letter_iso_country_code").is_some());
|
||||||
assert!(json.get("latitude").is_some());
|
assert!(json.get("latitude").is_some());
|
||||||
assert!(json.get("longitude").is_some());
|
assert!(json.get("longitude").is_some());
|
||||||
|
} else {
|
||||||
|
panic!("IPINFO_API_TOKEN not set");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "nym-node"
|
name = "nym-node"
|
||||||
version = "1.2.0"
|
version = "1.3.1"
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
homepage.workspace = true
|
homepage.workspace = true
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
#![warn(clippy::expect_used)]
|
||||||
|
#![warn(clippy::unwrap_used)]
|
||||||
|
#![warn(clippy::todo)]
|
||||||
|
#![warn(clippy::dbg_macro)]
|
||||||
|
|
||||||
use crate::entry::EntryStats;
|
use crate::entry::EntryStats;
|
||||||
use crate::mixnet::MixingStats;
|
use crate::mixnet::MixingStats;
|
||||||
use crate::network::NetworkStats;
|
use crate::network::NetworkStats;
|
||||||
|
use crate::process::NodeStats;
|
||||||
|
use crate::wireguard::WireguardStats;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -11,6 +18,9 @@ pub mod entry;
|
|||||||
pub mod events;
|
pub mod events;
|
||||||
pub mod mixnet;
|
pub mod mixnet;
|
||||||
pub mod network;
|
pub mod network;
|
||||||
|
pub mod process;
|
||||||
|
pub mod prometheus_wrapper;
|
||||||
|
pub mod wireguard;
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct NymNodeMetrics {
|
pub struct NymNodeMetrics {
|
||||||
@@ -34,6 +44,8 @@ impl Deref for NymNodeMetrics {
|
|||||||
pub struct NymNodeMetricsInner {
|
pub struct NymNodeMetricsInner {
|
||||||
pub mixnet: MixingStats,
|
pub mixnet: MixingStats,
|
||||||
pub entry: EntryStats,
|
pub entry: EntryStats,
|
||||||
|
pub wireguard: WireguardStats,
|
||||||
|
|
||||||
pub network: NetworkStats,
|
pub network: NetworkStats,
|
||||||
|
pub process: NodeStats,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,13 +131,6 @@ impl MixingStats {
|
|||||||
.or_default()
|
.or_default()
|
||||||
.dropped += 1;
|
.dropped += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn egress_dropped_final_hop_packet(&self) {
|
|
||||||
todo!()
|
|
||||||
// self.egress
|
|
||||||
// .final_hop_packets_dropped
|
|
||||||
// .fetch_add(1, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default, PartialEq)]
|
#[derive(Clone, Copy, Default, PartialEq)]
|
||||||
@@ -148,6 +141,8 @@ pub struct EgressRecipientStats {
|
|||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct EgressMixingStats {
|
pub struct EgressMixingStats {
|
||||||
|
disk_persisted_packets: AtomicUsize,
|
||||||
|
|
||||||
// this includes ACKS!
|
// this includes ACKS!
|
||||||
forward_hop_packets_sent: AtomicUsize,
|
forward_hop_packets_sent: AtomicUsize,
|
||||||
|
|
||||||
@@ -159,6 +154,14 @@ pub struct EgressMixingStats {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl EgressMixingStats {
|
impl EgressMixingStats {
|
||||||
|
pub fn add_disk_persisted_packet(&self) {
|
||||||
|
self.disk_persisted_packets.fetch_add(1, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disk_persisted_packets(&self) -> usize {
|
||||||
|
self.disk_persisted_packets.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn forward_hop_packets_sent(&self) -> usize {
|
pub fn forward_hop_packets_sent(&self) -> usize {
|
||||||
self.forward_hop_packets_sent.load(Ordering::Relaxed)
|
self.forward_hop_packets_sent.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,19 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct NetworkStats {
|
pub struct NetworkStats {
|
||||||
// for now just experiment with basic data, we could always extend it
|
// for now just experiment with basic data, we could always extend it
|
||||||
active_ingress_mixnet_connections: AtomicUsize,
|
active_ingress_mixnet_connections: AtomicUsize,
|
||||||
|
|
||||||
|
active_ingress_websocket_connections: AtomicUsize,
|
||||||
|
|
||||||
|
// the reason for additional `Arc` on this one is that the handler wasn't
|
||||||
|
// designed with metrics in mind and this single counter has been woven through
|
||||||
|
// the call stack
|
||||||
|
active_egress_mixnet_connections: Arc<AtomicUsize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NetworkStats {
|
impl NetworkStats {
|
||||||
@@ -20,8 +28,32 @@ impl NetworkStats {
|
|||||||
.fetch_sub(1, Ordering::Relaxed);
|
.fetch_sub(1, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_ingress_websocket_client(&self) {
|
||||||
|
self.active_ingress_websocket_connections
|
||||||
|
.fetch_add(1, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disconnected_ingress_websocket_client(&self) {
|
||||||
|
self.active_ingress_websocket_connections
|
||||||
|
.fetch_sub(1, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn active_ingress_mixnet_connections_count(&self) -> usize {
|
pub fn active_ingress_mixnet_connections_count(&self) -> usize {
|
||||||
self.active_ingress_mixnet_connections
|
self.active_ingress_mixnet_connections
|
||||||
.load(Ordering::Relaxed)
|
.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn active_ingress_websocket_connections_count(&self) -> usize {
|
||||||
|
self.active_ingress_websocket_connections
|
||||||
|
.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn active_egress_mixnet_connections_counter(&self) -> Arc<AtomicUsize> {
|
||||||
|
self.active_egress_mixnet_connections.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn active_egress_mixnet_connections_count(&self) -> usize {
|
||||||
|
self.active_egress_mixnet_connections
|
||||||
|
.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct NodeStats {
|
||||||
|
pub final_hop_packets_pending_delivery: AtomicUsize,
|
||||||
|
|
||||||
|
pub forward_hop_packets_pending_delivery: AtomicUsize,
|
||||||
|
|
||||||
|
pub forward_hop_packets_being_delayed: AtomicUsize,
|
||||||
|
|
||||||
|
// packets that haven't yet been delayed and are waiting for their chance
|
||||||
|
pub packet_forwarder_queue_size: AtomicUsize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NodeStats {
|
||||||
|
pub fn update_final_hop_packets_pending_delivery(&self, current: usize) {
|
||||||
|
self.final_hop_packets_pending_delivery
|
||||||
|
.store(current, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn final_hop_packets_pending_delivery_count(&self) -> usize {
|
||||||
|
self.final_hop_packets_pending_delivery
|
||||||
|
.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_forward_hop_packets_pending_delivery(&self, current: usize) {
|
||||||
|
self.forward_hop_packets_pending_delivery
|
||||||
|
.store(current, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn forward_hop_packets_pending_delivery_count(&self) -> usize {
|
||||||
|
self.forward_hop_packets_pending_delivery
|
||||||
|
.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_forward_hop_packets_being_delayed(&self, current: usize) {
|
||||||
|
self.forward_hop_packets_being_delayed
|
||||||
|
.store(current, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn forward_hop_packets_being_delayed_count(&self) -> usize {
|
||||||
|
self.forward_hop_packets_being_delayed
|
||||||
|
.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_packet_forwarder_queue_size(&self, current: usize) {
|
||||||
|
self.packet_forwarder_queue_size
|
||||||
|
.store(current, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn packet_forwarder_queue_size(&self) -> usize {
|
||||||
|
self.packet_forwarder_queue_size.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,390 @@
|
|||||||
|
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
use nym_metrics::{metrics_registry, HistogramTimer, Metric};
|
||||||
|
use std::sync::LazyLock;
|
||||||
|
use strum::{Display, EnumCount, EnumIter, EnumProperty, IntoEnumIterator};
|
||||||
|
|
||||||
|
pub static PROMETHEUS_METRICS: LazyLock<NymNodePrometheusMetrics> =
|
||||||
|
LazyLock::new(NymNodePrometheusMetrics::initialise);
|
||||||
|
|
||||||
|
const CLIENT_SESSION_DURATION_BUCKETS: &[f64] = &[
|
||||||
|
// sub 3s (implicitly)
|
||||||
|
3., // 3s - 15s
|
||||||
|
15., // 15s - 70s
|
||||||
|
70., // 70s - 2min
|
||||||
|
120., // 2 min - 5 min
|
||||||
|
300., // 5min - 15min
|
||||||
|
900., // 15min - 1h
|
||||||
|
3600., // 1h - 12h
|
||||||
|
43200., // 12h - 23.5h
|
||||||
|
84600., // 23.5h - 24.5h
|
||||||
|
88200., // 24.5h - 72h
|
||||||
|
259200., // 72h+ (implicitly)
|
||||||
|
];
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, EnumIter, Display, EnumProperty, EnumCount, Eq, Hash, PartialEq)]
|
||||||
|
#[strum(serialize_all = "snake_case", prefix = "nym_node_")]
|
||||||
|
pub enum PrometheusMetric {
|
||||||
|
// # MIXNET
|
||||||
|
// ## INGRESS
|
||||||
|
#[strum(props(help = "The number of ingress forward hop sphinx packets received"))]
|
||||||
|
MixnetIngressForwardPacketsReceived,
|
||||||
|
|
||||||
|
#[strum(props(help = "The number of ingress final hop sphinx packets received"))]
|
||||||
|
MixnetIngressFinalHopPacketsReceived,
|
||||||
|
|
||||||
|
#[strum(props(help = "The number of ingress malformed sphinx packets received"))]
|
||||||
|
MixnetIngressMalformedPacketsReceived,
|
||||||
|
|
||||||
|
#[strum(props(
|
||||||
|
help = "The number of ingress forward sphinx packets that specified excessive delay received"
|
||||||
|
))]
|
||||||
|
MixnetIngressExcessiveDelayPacketsReceived,
|
||||||
|
|
||||||
|
#[strum(props(help = "The number of ingress forward hop sphinx packets dropped"))]
|
||||||
|
MixnetIngressForwardPacketsDropped,
|
||||||
|
|
||||||
|
#[strum(props(help = "The number of ingress final hop sphinx packets dropped"))]
|
||||||
|
MixnetIngressFinalHopPacketsDropped,
|
||||||
|
|
||||||
|
#[strum(props(help = "The current rate of receiving ingress forward hop sphinx packets"))]
|
||||||
|
MixnetIngressForwardPacketsReceivedRate,
|
||||||
|
|
||||||
|
#[strum(props(help = "The current rate of receiving ingress final hop sphinx packets"))]
|
||||||
|
MixnetIngressFinalHopPacketsReceivedRate,
|
||||||
|
|
||||||
|
#[strum(props(help = "The current rate of receiving ingress malformed sphinx packets"))]
|
||||||
|
MixnetIngressMalformedPacketsReceivedRate,
|
||||||
|
|
||||||
|
#[strum(props(
|
||||||
|
help = "The current rate of receiving ingress sphinx packets that specified excessive delay"
|
||||||
|
))]
|
||||||
|
MixnetIngressExcessiveDelayPacketsReceivedRate,
|
||||||
|
|
||||||
|
#[strum(props(help = "The current rate of dropping ingress forward hop sphinx packets"))]
|
||||||
|
MixnetIngressForwardPacketsDroppedRate,
|
||||||
|
|
||||||
|
#[strum(props(help = "The current rate of dropping ingress final hop sphinx packets"))]
|
||||||
|
MixnetIngressFinalHopPacketsDroppedRate,
|
||||||
|
|
||||||
|
// ## EGRESS
|
||||||
|
#[strum(props(
|
||||||
|
help = "The number of unwrapped final hop packets stored on disk for offline clients"
|
||||||
|
))]
|
||||||
|
MixnetEgressStoredOnDiskFinalHopPackets,
|
||||||
|
|
||||||
|
#[strum(props(help = "The number of egress forward hop sphinx packets sent/forwarded"))]
|
||||||
|
MixnetEgressForwardPacketsSent,
|
||||||
|
|
||||||
|
#[strum(props(
|
||||||
|
help = "The number of egress forward hop sphinx packets sent/forwarded (acks only)"
|
||||||
|
))]
|
||||||
|
MixnetEgressAckSent,
|
||||||
|
|
||||||
|
#[strum(props(help = "The number of egress forward hop sphinx packets dropped"))]
|
||||||
|
MixnetEgressForwardPacketsDropped,
|
||||||
|
|
||||||
|
#[strum(props(
|
||||||
|
help = "The current rate of sending/forwarding egress forward hop sphinx packets"
|
||||||
|
))]
|
||||||
|
MixnetEgressForwardPacketsSendRate,
|
||||||
|
|
||||||
|
#[strum(props(
|
||||||
|
help = "The current rate of sending/forwarding egress forward hop sphinx packets (acks only)"
|
||||||
|
))]
|
||||||
|
MixnetEgressAckSendRate,
|
||||||
|
|
||||||
|
#[strum(props(help = "The current rate of dropping egress forward hop sphinx packets"))]
|
||||||
|
MixnetEgressForwardPacketsDroppedRate,
|
||||||
|
|
||||||
|
// # ENTRY
|
||||||
|
#[strum(props(help = "The number of unique users"))]
|
||||||
|
EntryClientUniqueUsers,
|
||||||
|
|
||||||
|
#[strum(props(help = "The number of client sessions started"))]
|
||||||
|
EntryClientSessionsStarted,
|
||||||
|
|
||||||
|
#[strum(props(help = "The number of client sessions finished"))]
|
||||||
|
EntryClientSessionsFinished,
|
||||||
|
|
||||||
|
#[strum(to_string = "entry_client_sessions_durations_{typ}")]
|
||||||
|
#[strum(props(help = "The distribution of client sessions duration of the specified type"))]
|
||||||
|
EntryClientSessionsDurations { typ: String },
|
||||||
|
|
||||||
|
// # WIREGUARD
|
||||||
|
#[strum(props(help = "The amount of bytes transmitted via wireguard"))]
|
||||||
|
WireguardBytesTx,
|
||||||
|
|
||||||
|
#[strum(props(help = "The amount of bytes received via wireguard"))]
|
||||||
|
WireguardBytesRx,
|
||||||
|
|
||||||
|
#[strum(props(help = "The current number of all registered wireguard peers"))]
|
||||||
|
WireguardTotalPeers,
|
||||||
|
|
||||||
|
#[strum(props(help = "The current number of active wireguard peers"))]
|
||||||
|
WireguardActivePeers,
|
||||||
|
|
||||||
|
#[strum(props(help = "The current sending rate of wireguard"))]
|
||||||
|
WireguardBytesTxRate,
|
||||||
|
|
||||||
|
#[strum(props(help = "The current receiving rate of wireguard"))]
|
||||||
|
WireguardBytesRxRate,
|
||||||
|
|
||||||
|
// # NETWORK
|
||||||
|
#[strum(props(help = "The number of active ingress mixnet connections"))]
|
||||||
|
NetworkActiveIngressMixnetConnections,
|
||||||
|
|
||||||
|
#[strum(props(help = "The number of active ingress websocket connections"))]
|
||||||
|
NetworkActiveIngressWebSocketConnections,
|
||||||
|
|
||||||
|
#[strum(props(help = "The number of active egress mixnet connections"))]
|
||||||
|
NetworkActiveEgressMixnetConnections,
|
||||||
|
|
||||||
|
// # PROCESS
|
||||||
|
#[strum(props(help = "The current number of packets being delayed"))]
|
||||||
|
ProcessForwardHopPacketsBeingDelayed,
|
||||||
|
|
||||||
|
#[strum(props(
|
||||||
|
help = "The current number of packets waiting in the queue to get delayed and sent into the mixnet"
|
||||||
|
))]
|
||||||
|
ProcessPacketForwarderQueueSize,
|
||||||
|
|
||||||
|
#[strum(props(
|
||||||
|
help = "The latency distribution of attempting to retrieve network topology (from nym-api)"
|
||||||
|
))]
|
||||||
|
ProcessTopologyQueryResolutionLatency,
|
||||||
|
|
||||||
|
#[strum(props(
|
||||||
|
help = "The current number of final hop packets stuck in channels waiting to get delivered to appropriate websocket connections"
|
||||||
|
))]
|
||||||
|
ProcessFinalHopPacketsPendingDelivery,
|
||||||
|
|
||||||
|
#[strum(props(
|
||||||
|
help = "The current number of forward hop packets stuck in channels waiting to get delivered to appropriate TCP connections"
|
||||||
|
))]
|
||||||
|
ProcessForwardHopPacketsPendingDelivery,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrometheusMetric {
|
||||||
|
fn name(&self) -> String {
|
||||||
|
self.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn help(&self) -> &'static str {
|
||||||
|
// SAFETY: every variant has a `help` prop defined (and there's a unit test is checking for that)
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
self.get_str("help").unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_complex(&self) -> bool {
|
||||||
|
matches!(self, PrometheusMetric::EntryClientSessionsDurations { .. })
|
||||||
|
// match self {
|
||||||
|
// PrometheusMetric::EntryClientSessionsDurations { .. } => true,
|
||||||
|
// _ => false,
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_registrable_metric(&self) -> Option<Metric> {
|
||||||
|
let name = self.name();
|
||||||
|
let help = self.help();
|
||||||
|
|
||||||
|
match self {
|
||||||
|
PrometheusMetric::MixnetIngressForwardPacketsReceived => {
|
||||||
|
Metric::new_int_gauge(&name, help)
|
||||||
|
}
|
||||||
|
PrometheusMetric::MixnetIngressFinalHopPacketsReceived => {
|
||||||
|
Metric::new_int_gauge(&name, help)
|
||||||
|
}
|
||||||
|
PrometheusMetric::MixnetIngressMalformedPacketsReceived => {
|
||||||
|
Metric::new_int_gauge(&name, help)
|
||||||
|
}
|
||||||
|
PrometheusMetric::MixnetIngressExcessiveDelayPacketsReceived => {
|
||||||
|
Metric::new_int_gauge(&name, help)
|
||||||
|
}
|
||||||
|
PrometheusMetric::MixnetIngressForwardPacketsDropped => {
|
||||||
|
Metric::new_int_gauge(&name, help)
|
||||||
|
}
|
||||||
|
PrometheusMetric::MixnetIngressFinalHopPacketsDropped => {
|
||||||
|
Metric::new_int_gauge(&name, help)
|
||||||
|
}
|
||||||
|
PrometheusMetric::MixnetIngressForwardPacketsReceivedRate => {
|
||||||
|
Metric::new_float_gauge(&name, help)
|
||||||
|
}
|
||||||
|
PrometheusMetric::MixnetIngressFinalHopPacketsReceivedRate => {
|
||||||
|
Metric::new_float_gauge(&name, help)
|
||||||
|
}
|
||||||
|
PrometheusMetric::MixnetIngressMalformedPacketsReceivedRate => {
|
||||||
|
Metric::new_float_gauge(&name, help)
|
||||||
|
}
|
||||||
|
PrometheusMetric::MixnetIngressExcessiveDelayPacketsReceivedRate => {
|
||||||
|
Metric::new_float_gauge(&name, help)
|
||||||
|
}
|
||||||
|
PrometheusMetric::MixnetIngressForwardPacketsDroppedRate => {
|
||||||
|
Metric::new_float_gauge(&name, help)
|
||||||
|
}
|
||||||
|
PrometheusMetric::MixnetIngressFinalHopPacketsDroppedRate => {
|
||||||
|
Metric::new_float_gauge(&name, help)
|
||||||
|
}
|
||||||
|
PrometheusMetric::MixnetEgressStoredOnDiskFinalHopPackets => {
|
||||||
|
Metric::new_int_gauge(&name, help)
|
||||||
|
}
|
||||||
|
PrometheusMetric::MixnetEgressForwardPacketsSent => Metric::new_int_gauge(&name, help),
|
||||||
|
PrometheusMetric::MixnetEgressAckSent => Metric::new_int_gauge(&name, help),
|
||||||
|
PrometheusMetric::MixnetEgressForwardPacketsDropped => {
|
||||||
|
Metric::new_int_gauge(&name, help)
|
||||||
|
}
|
||||||
|
PrometheusMetric::MixnetEgressForwardPacketsSendRate => {
|
||||||
|
Metric::new_float_gauge(&name, help)
|
||||||
|
}
|
||||||
|
PrometheusMetric::MixnetEgressAckSendRate => Metric::new_float_gauge(&name, help),
|
||||||
|
PrometheusMetric::MixnetEgressForwardPacketsDroppedRate => {
|
||||||
|
Metric::new_float_gauge(&name, help)
|
||||||
|
}
|
||||||
|
PrometheusMetric::EntryClientUniqueUsers => Metric::new_int_gauge(&name, help),
|
||||||
|
PrometheusMetric::EntryClientSessionsStarted => Metric::new_int_gauge(&name, help),
|
||||||
|
PrometheusMetric::EntryClientSessionsFinished => Metric::new_int_gauge(&name, help),
|
||||||
|
PrometheusMetric::EntryClientSessionsDurations { .. } => {
|
||||||
|
Metric::new_histogram(&name, help, Some(CLIENT_SESSION_DURATION_BUCKETS))
|
||||||
|
}
|
||||||
|
PrometheusMetric::WireguardBytesTx => Metric::new_int_gauge(&name, help),
|
||||||
|
PrometheusMetric::WireguardBytesRx => Metric::new_int_gauge(&name, help),
|
||||||
|
PrometheusMetric::WireguardTotalPeers => Metric::new_int_gauge(&name, help),
|
||||||
|
PrometheusMetric::WireguardActivePeers => Metric::new_int_gauge(&name, help),
|
||||||
|
PrometheusMetric::WireguardBytesTxRate => Metric::new_float_gauge(&name, help),
|
||||||
|
PrometheusMetric::WireguardBytesRxRate => Metric::new_float_gauge(&name, help),
|
||||||
|
PrometheusMetric::NetworkActiveIngressMixnetConnections => {
|
||||||
|
Metric::new_int_gauge(&name, help)
|
||||||
|
}
|
||||||
|
PrometheusMetric::NetworkActiveIngressWebSocketConnections => {
|
||||||
|
Metric::new_int_gauge(&name, help)
|
||||||
|
}
|
||||||
|
PrometheusMetric::NetworkActiveEgressMixnetConnections => {
|
||||||
|
Metric::new_int_gauge(&name, help)
|
||||||
|
}
|
||||||
|
PrometheusMetric::ProcessForwardHopPacketsBeingDelayed => {
|
||||||
|
Metric::new_int_gauge(&name, help)
|
||||||
|
}
|
||||||
|
PrometheusMetric::ProcessPacketForwarderQueueSize => Metric::new_int_gauge(&name, help),
|
||||||
|
PrometheusMetric::ProcessTopologyQueryResolutionLatency => {
|
||||||
|
Metric::new_histogram(&name, help, None)
|
||||||
|
}
|
||||||
|
PrometheusMetric::ProcessFinalHopPacketsPendingDelivery => {
|
||||||
|
Metric::new_int_gauge(&name, help)
|
||||||
|
}
|
||||||
|
PrometheusMetric::ProcessForwardHopPacketsPendingDelivery => {
|
||||||
|
Metric::new_int_gauge(&name, help)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set(&self, value: i64) {
|
||||||
|
metrics_registry().set(&self.name(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_float(&self, value: f64) {
|
||||||
|
metrics_registry().set_float(&self.name(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inc(&self) {
|
||||||
|
metrics_registry().inc(&self.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inc_by(&self, value: i64) {
|
||||||
|
metrics_registry().inc_by(&self.name(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn observe_histogram(&self, value: f64) {
|
||||||
|
let reg = metrics_registry();
|
||||||
|
if !reg.add_to_histogram(&self.name(), value) {
|
||||||
|
if let Some(registrable) = self.to_registrable_metric() {
|
||||||
|
reg.register_metric(registrable);
|
||||||
|
reg.add_to_histogram(&self.name(), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_timer(&self) -> Option<HistogramTimer> {
|
||||||
|
metrics_registry().start_timer(&self.name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub struct NymNodePrometheusMetrics {}
|
||||||
|
|
||||||
|
impl NymNodePrometheusMetrics {
|
||||||
|
// initialise all fields on startup with default values so that they'd be immediately available for query
|
||||||
|
pub(crate) fn initialise() -> Self {
|
||||||
|
let registry = metrics_registry();
|
||||||
|
|
||||||
|
// we can't initialise complex metrics as their names will only be fully known at runtime
|
||||||
|
for kind in PrometheusMetric::iter() {
|
||||||
|
if !kind.is_complex() {
|
||||||
|
if let Some(metric) = kind.to_registrable_metric() {
|
||||||
|
registry.register_metric(metric);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NymNodePrometheusMetrics {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(&self, metric: PrometheusMetric, value: i64) {
|
||||||
|
metric.set(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_float(&self, metric: PrometheusMetric, value: f64) {
|
||||||
|
metric.set_float(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inc(&self, metric: PrometheusMetric) {
|
||||||
|
metric.inc()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inc_by(&self, metric: PrometheusMetric, value: i64) {
|
||||||
|
metric.inc_by(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn observe_histogram(&self, metric: PrometheusMetric, value: f64) {
|
||||||
|
metric.observe_histogram(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start_timer(&self, metric: PrometheusMetric) -> Option<HistogramTimer> {
|
||||||
|
metric.start_timer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn prometheus_metrics() {
|
||||||
|
// a sanity check for anyone adding new metrics. if this test fails,
|
||||||
|
// make sure any methods on `PrometheusMetric` enum don't need updating
|
||||||
|
// or require custom Display impl
|
||||||
|
assert_eq!(37, PrometheusMetric::COUNT)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn every_variant_has_help_property() {
|
||||||
|
for variant in PrometheusMetric::iter() {
|
||||||
|
assert!(variant.get_str("help").is_some())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn prometheus_metrics_names() {
|
||||||
|
// make sure nothing changed in our serialisation
|
||||||
|
let simple = PrometheusMetric::MixnetIngressForwardPacketsReceived.to_string();
|
||||||
|
assert_eq!("nym_node_mixnet_ingress_forward_packets_received", simple);
|
||||||
|
|
||||||
|
let parameterised =
|
||||||
|
PrometheusMetric::EntryClientSessionsDurations { typ: "vpn".into() }.to_string();
|
||||||
|
assert_eq!(
|
||||||
|
"nym_node_entry_client_sessions_durations_vpn",
|
||||||
|
parameterised
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct WireguardStats {
|
||||||
|
bytes_rx: AtomicUsize,
|
||||||
|
bytes_tx: AtomicUsize,
|
||||||
|
|
||||||
|
total_peers: AtomicUsize,
|
||||||
|
active_peers: AtomicUsize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WireguardStats {
|
||||||
|
pub fn bytes_rx(&self) -> usize {
|
||||||
|
self.bytes_rx.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bytes_tx(&self) -> usize {
|
||||||
|
self.bytes_tx.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn total_peers(&self) -> usize {
|
||||||
|
self.total_peers.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn active_peers(&self) -> usize {
|
||||||
|
self.active_peers.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(
|
||||||
|
&self,
|
||||||
|
new_bytes_rx: usize,
|
||||||
|
new_bytes_tx: usize,
|
||||||
|
total_peers: usize,
|
||||||
|
active_peers: usize,
|
||||||
|
) {
|
||||||
|
self.bytes_rx.fetch_add(new_bytes_rx, Ordering::Relaxed);
|
||||||
|
self.bytes_tx.fetch_add(new_bytes_tx, Ordering::Relaxed);
|
||||||
|
self.total_peers.store(total_peers, Ordering::Relaxed);
|
||||||
|
self.active_peers.store(active_peers, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,19 @@
|
|||||||
pub use mixing::*;
|
pub use mixing::*;
|
||||||
pub use session::*;
|
pub use session::*;
|
||||||
pub use verloc::*;
|
pub use verloc::*;
|
||||||
|
pub use wireguard::*;
|
||||||
|
|
||||||
|
pub mod wireguard {
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
|
||||||
|
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
|
||||||
|
pub struct WireguardStats {
|
||||||
|
pub bytes_tx: usize,
|
||||||
|
|
||||||
|
pub bytes_rx: usize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub mod packets {
|
pub mod packets {
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|||||||
@@ -66,12 +66,18 @@ pub mod routes {
|
|||||||
|
|
||||||
pub const LEGACY_MIXING: &str = "/mixing";
|
pub const LEGACY_MIXING: &str = "/mixing";
|
||||||
pub const PACKETS_STATS: &str = "/packets-stats";
|
pub const PACKETS_STATS: &str = "/packets-stats";
|
||||||
|
pub const WIREGUARD_STATS: &str = "/wireguard-stats";
|
||||||
pub const SESSIONS: &str = "/sessions";
|
pub const SESSIONS: &str = "/sessions";
|
||||||
pub const VERLOC: &str = "/verloc";
|
pub const VERLOC: &str = "/verloc";
|
||||||
pub const PROMETHEUS: &str = "/prometheus";
|
pub const PROMETHEUS: &str = "/prometheus";
|
||||||
|
|
||||||
absolute_route!(legacy_mixing_absolute, metrics_absolute(), LEGACY_MIXING);
|
absolute_route!(legacy_mixing_absolute, metrics_absolute(), LEGACY_MIXING);
|
||||||
absolute_route!(packets_stats_absolute, metrics_absolute(), PACKETS_STATS);
|
absolute_route!(packets_stats_absolute, metrics_absolute(), PACKETS_STATS);
|
||||||
|
absolute_route!(
|
||||||
|
wireguard_stats_absolute,
|
||||||
|
metrics_absolute(),
|
||||||
|
WIREGUARD_STATS
|
||||||
|
);
|
||||||
absolute_route!(sessions_absolute, metrics_absolute(), SESSIONS);
|
absolute_route!(sessions_absolute, metrics_absolute(), SESSIONS);
|
||||||
absolute_route!(verloc_absolute, metrics_absolute(), VERLOC);
|
absolute_route!(verloc_absolute, metrics_absolute(), VERLOC);
|
||||||
absolute_route!(prometheus_absolute, metrics_absolute(), PROMETHEUS);
|
absolute_route!(prometheus_absolute, metrics_absolute(), PROMETHEUS);
|
||||||
|
|||||||
@@ -83,6 +83,10 @@ pub(crate) async fn execute(mut args: Args) -> Result<(), NymNodeError> {
|
|||||||
warn!("this node is going to run without mixnode or gateway support! consider providing `mode` value");
|
warn!("this node is going to run without mixnode or gateway support! consider providing `mode` value");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.modes.standalone_exit() {
|
||||||
|
warn!("this node is going to run in EXIT gateway mode only - it will not be able to accept client traffic and thus will NOT be eligible for any rewards. consider running it alongside `entry` (or `full-gateway`) mode")
|
||||||
|
}
|
||||||
|
|
||||||
if config.host.public_ips.is_empty() {
|
if config.host.public_ips.is_empty() {
|
||||||
return Err(NymNodeError::NoPublicIps);
|
return Err(NymNodeError::NoPublicIps);
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user