Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a491e6a71a | |||
| 96f99bb9e4 | |||
| 10933ff8f1 | |||
| 5454b36022 | |||
| 675e5a0305 | |||
| 210cc5286e | |||
| d07e293cb5 | |||
| 4b055a9bf0 | |||
| 80d1a24164 | |||
| b481da9c55 | |||
| 585d752c83 | |||
| 6eddc913f4 | |||
| 15c3012199 | |||
| 78bf413e6a | |||
| 29ea4623c8 | |||
| 60c21a8d1d | |||
| feefde9022 | |||
| 645be5fa22 | |||
| ac56717b23 | |||
| ec502f46f0 | |||
| 4a9a5579c4 | |||
| 96180275f8 | |||
| ab20260a2f | |||
| 889d464e98 | |||
| 56206433e6 | |||
| 8e713d43e1 | |||
| 35aa7e338d | |||
| 2a60b2f057 | |||
| dcde4c8df1 | |||
| fcaa32284b | |||
| fa72f90bfa | |||
| 12b9aefa99 | |||
| 0041937ed3 | |||
| 0e5bd966dd | |||
| 6acd936368 | |||
| 01c7b2819e | |||
| 042a8a58aa | |||
| d8ab2a8f15 | |||
| d6d2239685 | |||
| 3d704fbbf1 | |||
| 119c36b0bb | |||
| 6b5b97199b |
+152
@@ -4,6 +4,158 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [2024.14-crunch] (2024-12-11)
|
||||
|
||||
- Merge/release/2024.14-crunch ([#5242])
|
||||
- bugfix: added explicit openapi servers to account for route prefixes ([#5237])
|
||||
- Further config score adjustments ([#5225])
|
||||
- feature: remve any filtering on node semver ([#5224])
|
||||
- Backport #5218 ([#5220])
|
||||
- Derive serialize for UserAgent (#5210) ([#5217])
|
||||
- dont consider legacy nodes for rewarded set selection ([#5215])
|
||||
- introduce UNSTABLE endpoints for returning network monitor run details ([#5214])
|
||||
- Nmv2 add debug config ([#5212])
|
||||
- nym-api NMv1 adjustments ([#5209])
|
||||
- adjusted config score penalty calculation ([#5206])
|
||||
- Fix backwards compat mac generation ([#5202])
|
||||
- merge crunch into develop ([#5199])
|
||||
- Update Security disclosure email, public key and policy ([#5195])
|
||||
- Guard storage access with cache ([#5193])
|
||||
- chore: apply 1.84 linter suggestions ([#5192])
|
||||
- improvement: make internal gateway clients use the same topology cache ([#5191])
|
||||
- Bugfix/credential proxy sequencing ([#5187])
|
||||
- Add monitor_run and testing_route indexes ([#5182])
|
||||
- Add indexes to monitor run and testing route ([#5181])
|
||||
- bugfix: fixed nym-node config migrations (again) ([#5179])
|
||||
- bugfix: use default value for verloc config when deserialising missing values ([#5177])
|
||||
- Remove peers with no allowed ip from storage ([#5175])
|
||||
- Move two minor jobs to free tier github hosted runners ([#5169])
|
||||
- Add support for DELETE to nym-http-api-client ([#5166])
|
||||
- Fix env var name ([#5165])
|
||||
- Add strum::EnumIter for TicketType ([#5164])
|
||||
- Add export_to_env to NymNetworkDetails ([#5162])
|
||||
- bugfix: correctly expose ecash-related data on nym-api ([#5155])
|
||||
- fix: validator-rewarder GH job ([#5151])
|
||||
- build(deps): bump cross-spawn from 7.0.3 to 7.0.6 in /testnet-faucet ([#5150])
|
||||
- build(deps): bump mikefarah/yq from 4.44.3 to 4.44.5 ([#5149])
|
||||
- start session collection for exit gateways ([#5148])
|
||||
- add version to clientStatsReport ([#5147])
|
||||
- update serde_json_path due to compilation issue ([#5144])
|
||||
- chore: remove standalone legacy mixnode/gateway binaries ([#5135])
|
||||
- [Product Data] Set up country reporting from vpn-client ([#5134])
|
||||
- removed ci-nym-api-tests.yml which was running outdated (and broken) tests ([#5133])
|
||||
- CI: reduce jobs running on cluster ([#5132])
|
||||
- [DOCS/operators]: Release changes v2024.13-magura & Tokenomics pages v1.0 ([#5128])
|
||||
- NS Agent auth with NS API ([#5127])
|
||||
- [Product Data] Config deserialization bug fix ([#5126])
|
||||
- bugfix: don't send empty BankMsg in ecash contract ([#5121])
|
||||
- [Product data] Data consumption with ecash ticket ([#5120])
|
||||
- feat: add GH workflow for nym-validator-rewarder ([#5119])
|
||||
- feat: add Dockerfile and add env vars for clap arguments ([#5118])
|
||||
- feature: config score ([#5117])
|
||||
- [Product Data] Add stats reporting configuration in client config ([#5115])
|
||||
- Correct IPv6 address generation ([#5113])
|
||||
- feature: rewarding for ticketbook issuance ([#5112])
|
||||
- Add granular log on nym-node ([#5111])
|
||||
- Send mixnet packet stats using task client ([#5109])
|
||||
- Expose time range ([#5108])
|
||||
- [Product Data] Client-side stats collection ([#5107])
|
||||
- chore: ecash contract migration to remove unused 'redemption_gateway_share' ([#5104])
|
||||
- [Product Data] Better unique user count on gateways ([#5084])
|
||||
- feat: add nym node GH workflow ([#5080])
|
||||
- IPv6 support for wireguard ([#5059])
|
||||
- Node Status API ([#5050])
|
||||
- Authenticator CLI client mode ([#5044])
|
||||
- Integrate nym-credential-proxy into workspace ([#5027])
|
||||
- [Product Data] Introduce data persistence on gateways ([#5022])
|
||||
- Bump the patch-updates group across 1 directory with 10 updates ([#5011])
|
||||
- build(deps): bump once_cell from 1.19.0 to 1.20.2 ([#4952])
|
||||
- Create TaskStatusEvent trait instead of piggybacking on Error ([#4919])
|
||||
- build(deps): bump lazy_static from 1.4.0 to 1.5.0 ([#4913])
|
||||
- Sync code with .env in build.rs ([#4876])
|
||||
- build(deps): bump axios from 1.6.0 to 1.7.5 in /nym-api/tests ([#4790])
|
||||
- Bump elliptic from 6.5.4 to 6.5.7 in /testnet-faucet ([#4768])
|
||||
|
||||
[#5242]: https://github.com/nymtech/nym/pull/5242
|
||||
[#5237]: https://github.com/nymtech/nym/pull/5237
|
||||
[#5225]: https://github.com/nymtech/nym/pull/5225
|
||||
[#5224]: https://github.com/nymtech/nym/pull/5224
|
||||
[#5220]: https://github.com/nymtech/nym/pull/5220
|
||||
[#5217]: https://github.com/nymtech/nym/pull/5217
|
||||
[#5215]: https://github.com/nymtech/nym/pull/5215
|
||||
[#5214]: https://github.com/nymtech/nym/pull/5214
|
||||
[#5212]: https://github.com/nymtech/nym/pull/5212
|
||||
[#5209]: https://github.com/nymtech/nym/pull/5209
|
||||
[#5206]: https://github.com/nymtech/nym/pull/5206
|
||||
[#5202]: https://github.com/nymtech/nym/pull/5202
|
||||
[#5199]: https://github.com/nymtech/nym/pull/5199
|
||||
[#5195]: https://github.com/nymtech/nym/pull/5195
|
||||
[#5193]: https://github.com/nymtech/nym/pull/5193
|
||||
[#5192]: https://github.com/nymtech/nym/pull/5192
|
||||
[#5191]: https://github.com/nymtech/nym/pull/5191
|
||||
[#5187]: https://github.com/nymtech/nym/pull/5187
|
||||
[#5182]: https://github.com/nymtech/nym/pull/5182
|
||||
[#5181]: https://github.com/nymtech/nym/pull/5181
|
||||
[#5179]: https://github.com/nymtech/nym/pull/5179
|
||||
[#5177]: https://github.com/nymtech/nym/pull/5177
|
||||
[#5175]: https://github.com/nymtech/nym/pull/5175
|
||||
[#5169]: https://github.com/nymtech/nym/pull/5169
|
||||
[#5166]: https://github.com/nymtech/nym/pull/5166
|
||||
[#5165]: https://github.com/nymtech/nym/pull/5165
|
||||
[#5164]: https://github.com/nymtech/nym/pull/5164
|
||||
[#5162]: https://github.com/nymtech/nym/pull/5162
|
||||
[#5155]: https://github.com/nymtech/nym/pull/5155
|
||||
[#5151]: https://github.com/nymtech/nym/pull/5151
|
||||
[#5150]: https://github.com/nymtech/nym/pull/5150
|
||||
[#5149]: https://github.com/nymtech/nym/pull/5149
|
||||
[#5148]: https://github.com/nymtech/nym/pull/5148
|
||||
[#5147]: https://github.com/nymtech/nym/pull/5147
|
||||
[#5144]: https://github.com/nymtech/nym/pull/5144
|
||||
[#5135]: https://github.com/nymtech/nym/pull/5135
|
||||
[#5134]: https://github.com/nymtech/nym/pull/5134
|
||||
[#5133]: https://github.com/nymtech/nym/pull/5133
|
||||
[#5132]: https://github.com/nymtech/nym/pull/5132
|
||||
[#5128]: https://github.com/nymtech/nym/pull/5128
|
||||
[#5127]: https://github.com/nymtech/nym/pull/5127
|
||||
[#5126]: https://github.com/nymtech/nym/pull/5126
|
||||
[#5121]: https://github.com/nymtech/nym/pull/5121
|
||||
[#5120]: https://github.com/nymtech/nym/pull/5120
|
||||
[#5119]: https://github.com/nymtech/nym/pull/5119
|
||||
[#5118]: https://github.com/nymtech/nym/pull/5118
|
||||
[#5117]: https://github.com/nymtech/nym/pull/5117
|
||||
[#5115]: https://github.com/nymtech/nym/pull/5115
|
||||
[#5113]: https://github.com/nymtech/nym/pull/5113
|
||||
[#5112]: https://github.com/nymtech/nym/pull/5112
|
||||
[#5111]: https://github.com/nymtech/nym/pull/5111
|
||||
[#5109]: https://github.com/nymtech/nym/pull/5109
|
||||
[#5108]: https://github.com/nymtech/nym/pull/5108
|
||||
[#5107]: https://github.com/nymtech/nym/pull/5107
|
||||
[#5104]: https://github.com/nymtech/nym/pull/5104
|
||||
[#5084]: https://github.com/nymtech/nym/pull/5084
|
||||
[#5080]: https://github.com/nymtech/nym/pull/5080
|
||||
[#5059]: https://github.com/nymtech/nym/pull/5059
|
||||
[#5050]: https://github.com/nymtech/nym/pull/5050
|
||||
[#5044]: https://github.com/nymtech/nym/pull/5044
|
||||
[#5027]: https://github.com/nymtech/nym/pull/5027
|
||||
[#5022]: https://github.com/nymtech/nym/pull/5022
|
||||
[#5011]: https://github.com/nymtech/nym/pull/5011
|
||||
[#4952]: https://github.com/nymtech/nym/pull/4952
|
||||
[#4919]: https://github.com/nymtech/nym/pull/4919
|
||||
[#4913]: https://github.com/nymtech/nym/pull/4913
|
||||
[#4876]: https://github.com/nymtech/nym/pull/4876
|
||||
[#4790]: https://github.com/nymtech/nym/pull/4790
|
||||
[#4768]: https://github.com/nymtech/nym/pull/4768
|
||||
|
||||
## [2024.13-magura-drift] (2024-11-29)
|
||||
|
||||
- Optimised syncing bandwidth information to storage
|
||||
|
||||
## [2024.13-magura-patched] (2024-11-22)
|
||||
|
||||
- [experimental] allow clients to change between deterministic route selection based on packet headers and a pseudorandom distribution
|
||||
- Introduced a configurable limit on retransmission frequency of packets if ACKs are not received
|
||||
- Filtered out invalid IP addresses on nym-api
|
||||
|
||||
## [2024.13-magura] (2024-11-18)
|
||||
|
||||
- Limit race probability ([#5145])
|
||||
|
||||
Generated
+40
-297
@@ -26,9 +26,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
|
||||
checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
@@ -607,9 +607,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.71"
|
||||
version = "0.3.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d"
|
||||
checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
@@ -665,16 +665,6 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "better-panic"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fa9e1d11a268684cbd90ed36370d7577afb6c62d912ddff5c15fc34343e5036"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"console",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "binascii"
|
||||
version = "0.1.4"
|
||||
@@ -953,12 +943,6 @@ dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cassowary"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
version = "0.3.0"
|
||||
@@ -971,15 +955,6 @@ version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6"
|
||||
|
||||
[[package]]
|
||||
name = "castaway"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5"
|
||||
dependencies = [
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.1.18"
|
||||
@@ -1183,33 +1158,6 @@ version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70"
|
||||
|
||||
[[package]]
|
||||
name = "color-eyre"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"color-spantrace",
|
||||
"eyre",
|
||||
"indenter",
|
||||
"once_cell",
|
||||
"owo-colors",
|
||||
"tracing-error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "color-spantrace"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"owo-colors",
|
||||
"tracing-core",
|
||||
"tracing-error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.1"
|
||||
@@ -1245,22 +1193,7 @@ dependencies = [
|
||||
"crossterm 0.27.0",
|
||||
"strum 0.26.3",
|
||||
"strum_macros 0.26.4",
|
||||
"unicode-width 0.1.13",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "compact_str"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6050c3a16ddab2e412160b31f2c871015704239bca62f72f6e5f0be631d3f644"
|
||||
dependencies = [
|
||||
"castaway 0.2.3",
|
||||
"cfg-if",
|
||||
"itoa",
|
||||
"rustversion",
|
||||
"ryu",
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1281,7 +1214,7 @@ dependencies = [
|
||||
"encode_unicode",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"unicode-width 0.1.13",
|
||||
"unicode-width",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
@@ -1701,23 +1634,6 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.28.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"crossterm_winapi",
|
||||
"futures-core",
|
||||
"mio 1.0.1",
|
||||
"parking_lot",
|
||||
"rustix",
|
||||
"signal-hook",
|
||||
"signal-hook-mio",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm_winapi"
|
||||
version = "0.9.1"
|
||||
@@ -1999,12 +1915,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.20.10"
|
||||
version = "0.20.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
|
||||
checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1"
|
||||
dependencies = [
|
||||
"darling_core 0.20.10",
|
||||
"darling_macro 0.20.10",
|
||||
"darling_core 0.20.9",
|
||||
"darling_macro 0.20.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2023,9 +1939,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.20.10"
|
||||
version = "0.20.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
|
||||
checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
@@ -2048,11 +1964,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.20.10"
|
||||
version = "0.20.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
|
||||
checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178"
|
||||
dependencies = [
|
||||
"darling_core 0.20.10",
|
||||
"darling_core 0.20.9",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
@@ -2512,7 +2428,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "explorer-api"
|
||||
version = "1.1.42"
|
||||
version = "1.1.43"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap 4.5.20",
|
||||
@@ -2718,12 +2634,6 @@ version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.1"
|
||||
@@ -2888,15 +2798,6 @@ dependencies = [
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fxhash"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gcc"
|
||||
version = "0.3.55"
|
||||
@@ -2974,9 +2875,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.28.1"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
||||
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
@@ -3127,17 +3028,6 @@ dependencies = [
|
||||
"allocator-api2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashlink"
|
||||
version = "0.8.4"
|
||||
@@ -3622,15 +3512,9 @@ dependencies = [
|
||||
"instant",
|
||||
"number_prefix",
|
||||
"portable-atomic",
|
||||
"unicode-width 0.1.13",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indoc"
|
||||
version = "2.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
|
||||
|
||||
[[package]]
|
||||
name = "inlinable_string"
|
||||
version = "0.1.15"
|
||||
@@ -3680,21 +3564,7 @@ dependencies = [
|
||||
"newline-converter",
|
||||
"thiserror",
|
||||
"unicode-segmentation",
|
||||
"unicode-width 0.1.13",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instability"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b829f37dead9dc39df40c2d3376c179fdfd2ac771f53f55d3c30dc096a3c0c6e"
|
||||
dependencies = [
|
||||
"darling 0.20.10",
|
||||
"indoc",
|
||||
"pretty_assertions",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3772,7 +3642,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "334e04b4d781f436dc315cb1e7515bd96826426345d498149e4bde36b67f8ee9"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"castaway 0.1.2",
|
||||
"castaway",
|
||||
"crossbeam-utils",
|
||||
"curl",
|
||||
"curl-sys",
|
||||
@@ -4044,15 +3914,6 @@ dependencies = [
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lru"
|
||||
version = "0.12.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38"
|
||||
dependencies = [
|
||||
"hashbrown 0.15.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "macro_rules_attribute"
|
||||
version = "0.1.3"
|
||||
@@ -4238,7 +4099,6 @@ checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4"
|
||||
dependencies = [
|
||||
"hermit-abi 0.3.9",
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
@@ -4591,7 +4451,7 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
||||
|
||||
[[package]]
|
||||
name = "nym-api"
|
||||
version = "1.1.46"
|
||||
version = "1.1.47"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -4610,6 +4470,7 @@ dependencies = [
|
||||
"cw2",
|
||||
"cw3",
|
||||
"cw4",
|
||||
"dashmap",
|
||||
"dirs",
|
||||
"futures",
|
||||
"getset",
|
||||
@@ -4813,7 +4674,6 @@ dependencies = [
|
||||
"opentelemetry-jaeger",
|
||||
"pretty_env_logger",
|
||||
"schemars",
|
||||
"semver 1.0.23",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tracing-opentelemetry",
|
||||
@@ -4840,7 +4700,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-cli"
|
||||
version = "1.1.44"
|
||||
version = "1.1.45"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
@@ -4921,7 +4781,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-client"
|
||||
version = "1.1.43"
|
||||
version = "1.1.45"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"clap 4.5.20",
|
||||
@@ -5232,7 +5092,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-credential-proxy"
|
||||
version = "0.1.3"
|
||||
version = "0.1.6"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -5252,6 +5112,7 @@ dependencies = [
|
||||
"nym-credentials",
|
||||
"nym-credentials-interface",
|
||||
"nym-crypto",
|
||||
"nym-ecash-contract-common",
|
||||
"nym-http-api-common",
|
||||
"nym-network-defaults",
|
||||
"nym-validator-client",
|
||||
@@ -5759,12 +5620,15 @@ dependencies = [
|
||||
"axum-client-ip",
|
||||
"bytes",
|
||||
"colored",
|
||||
"futures",
|
||||
"mime",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"tower 0.4.13",
|
||||
"tracing",
|
||||
"utoipa",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6038,7 +5902,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-requester"
|
||||
version = "1.1.44"
|
||||
version = "1.1.46"
|
||||
dependencies = [
|
||||
"addr",
|
||||
"anyhow",
|
||||
@@ -6089,7 +5953,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-node"
|
||||
version = "1.1.10"
|
||||
version = "1.2.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bip39",
|
||||
@@ -6445,7 +6309,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.43"
|
||||
version = "1.1.45"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"clap 4.5.20",
|
||||
@@ -6814,27 +6678,6 @@ dependencies = [
|
||||
"wasm-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-tui-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"better-panic",
|
||||
"color-eyre",
|
||||
"crossterm 0.28.1",
|
||||
"humantime-serde",
|
||||
"ratatui",
|
||||
"serde",
|
||||
"strip-ansi-escapes",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"tui-logger",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-tun"
|
||||
version = "0.1.0"
|
||||
@@ -7046,6 +6889,7 @@ dependencies = [
|
||||
"nym-task",
|
||||
"nym-wireguard-types",
|
||||
"thiserror",
|
||||
"time",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"x25519-dalek",
|
||||
@@ -7068,7 +6912,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nymvisor"
|
||||
version = "0.1.9"
|
||||
version = "0.1.10"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -7122,9 +6966,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.32.2"
|
||||
version = "0.36.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
|
||||
checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -7306,12 +7150,6 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||
|
||||
[[package]]
|
||||
name = "owo-colors"
|
||||
version = "3.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
|
||||
|
||||
[[package]]
|
||||
name = "pairing"
|
||||
version = "0.23.0"
|
||||
@@ -7971,28 +7809,6 @@ dependencies = [
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ratatui"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"cassowary",
|
||||
"compact_str",
|
||||
"crossterm 0.28.1",
|
||||
"indoc",
|
||||
"instability",
|
||||
"itertools 0.13.0",
|
||||
"lru",
|
||||
"paste",
|
||||
"serde",
|
||||
"strum 0.26.3",
|
||||
"unicode-segmentation",
|
||||
"unicode-truncate",
|
||||
"unicode-width 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "raw-cpuid"
|
||||
version = "11.2.0"
|
||||
@@ -8987,7 +8803,7 @@ version = "3.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350"
|
||||
dependencies = [
|
||||
"darling 0.20.10",
|
||||
"darling 0.20.9",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
@@ -9100,13 +8916,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-mio"
|
||||
version = "0.2.4"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
|
||||
checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mio 0.8.11",
|
||||
"mio 1.0.1",
|
||||
"signal-hook",
|
||||
]
|
||||
|
||||
@@ -9537,15 +9352,6 @@ dependencies = [
|
||||
"unicode-properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strip-ansi-escapes"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55ff8ef943b384c414f54aefa961dd2bd853add74ec75e7ac74cf91dba62bcfa"
|
||||
dependencies = [
|
||||
"vte",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
@@ -10373,16 +10179,6 @@ dependencies = [
|
||||
"valuable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-error"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e"
|
||||
dependencies = [
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-futures"
|
||||
version = "0.2.5"
|
||||
@@ -10564,22 +10360,6 @@ dependencies = [
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tui-logger"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bdf8f5ebd2c83a5176c69b150ea7f2a855ec8dc2a2774e7f198d1b50feda5745"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"fxhash",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"parking_lot",
|
||||
"ratatui",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.20.1"
|
||||
@@ -10705,29 +10485,12 @@ version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-truncate"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf"
|
||||
dependencies = [
|
||||
"itertools 0.13.0",
|
||||
"unicode-segmentation",
|
||||
"unicode-width 0.1.13",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.4"
|
||||
@@ -11059,26 +10822,6 @@ version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "vte"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
"vte_generate_state_changes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vte_generate_state_changes"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e369bee1b05d510a7b4ed645f5faa90619e05437111783ea5848f28d97d3c2e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "waker-fn"
|
||||
version = "1.2.0"
|
||||
|
||||
+1
-10
@@ -149,7 +149,7 @@ members = [
|
||||
"tools/internal/contract-state-importer/importer-cli",
|
||||
"tools/internal/contract-state-importer/importer-contract",
|
||||
"tools/internal/testnet-manager",
|
||||
"tools/internal/testnet-manager/dkg-bypass-contract", "common/nym-tui-common",
|
||||
"tools/internal/testnet-manager/dkg-bypass-contract",
|
||||
]
|
||||
|
||||
default-members = [
|
||||
@@ -412,15 +412,6 @@ wasm-bindgen-futures = "0.4.45"
|
||||
wasmtimer = "0.2.0"
|
||||
web-sys = "0.3.72"
|
||||
|
||||
# tui related
|
||||
better-panic = "0.3.0"
|
||||
color-eyre = "0.6.3"
|
||||
crossterm = "0.28.1"
|
||||
strip-ansi-escapes = "0.2.0"
|
||||
ratatui = "0.29.0"
|
||||
tui-logger = "0.14.0"
|
||||
|
||||
|
||||
# Profile settings for individual crates
|
||||
|
||||
# Compile-time verified queries do quite a bit of work at compile time. Incremental
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.43"
|
||||
version = "1.1.45"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
|
||||
@@ -3,13 +3,10 @@
|
||||
|
||||
use crate::commands::try_load_current_config;
|
||||
use crate::{
|
||||
client::{config::Config, SocketClient},
|
||||
client::SocketClient,
|
||||
commands::{override_config, OverrideConfig},
|
||||
error::ClientError,
|
||||
};
|
||||
use clap::Args;
|
||||
use log::*;
|
||||
use nym_bin_common::version_checker::is_minor_version_compatible;
|
||||
use nym_client_core::cli_helpers::client_run::CommonClientRunArgs;
|
||||
use std::error::Error;
|
||||
use std::net::IpAddr;
|
||||
@@ -48,36 +45,12 @@ impl From<Run> for OverrideConfig {
|
||||
}
|
||||
}
|
||||
|
||||
// this only checks compatibility between config the binary. It does not take into consideration
|
||||
// network version. It might do so in the future.
|
||||
fn version_check(cfg: &Config) -> bool {
|
||||
let binary_version = env!("CARGO_PKG_VERSION");
|
||||
let config_version = &cfg.base.client.version;
|
||||
if binary_version == config_version {
|
||||
true
|
||||
} else {
|
||||
warn!("The native-client binary has different version than what is specified in config file! {} and {}", binary_version, config_version);
|
||||
if is_minor_version_compatible(binary_version, config_version) {
|
||||
info!("but they are still semver compatible. However, consider running the `upgrade` command");
|
||||
true
|
||||
} else {
|
||||
error!("and they are semver incompatible! - please run the `upgrade` command before attempting `run` again");
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn execute(args: Run) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
eprintln!("Starting client {}...", args.common_args.id);
|
||||
|
||||
let mut config = try_load_current_config(&args.common_args.id).await?;
|
||||
config = override_config(config, OverrideConfig::from(args.clone()));
|
||||
|
||||
if !version_check(&config) {
|
||||
error!("failed the local version check");
|
||||
return Err(Box::new(ClientError::FailedLocalVersionCheck));
|
||||
}
|
||||
|
||||
SocketClient::new(config, args.common_args.custom_mixnet)
|
||||
.run_socket_forever()
|
||||
.await
|
||||
|
||||
@@ -17,9 +17,6 @@ pub enum ClientError {
|
||||
#[error("Failed to validate the loaded config")]
|
||||
ConfigValidationFailure,
|
||||
|
||||
#[error("Failed local version check, client and config mismatch")]
|
||||
FailedLocalVersionCheck,
|
||||
|
||||
#[error("Attempted to start the client in invalid socket mode")]
|
||||
InvalidSocketMode,
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.43"
|
||||
version = "1.1.45"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
||||
edition = "2021"
|
||||
|
||||
@@ -2,14 +2,8 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::commands::try_load_current_config;
|
||||
use crate::config::Config;
|
||||
use crate::{
|
||||
commands::{override_config, OverrideConfig},
|
||||
error::Socks5ClientError,
|
||||
};
|
||||
use crate::commands::{override_config, OverrideConfig};
|
||||
use clap::Args;
|
||||
use log::*;
|
||||
use nym_bin_common::version_checker::is_minor_version_compatible;
|
||||
use nym_client_core::cli_helpers::client_run::CommonClientRunArgs;
|
||||
use nym_client_core::client::base_client::storage::OnDiskPersistent;
|
||||
use nym_client_core::client::topology_control::geo_aware_provider::CountryGroup;
|
||||
@@ -82,38 +76,12 @@ fn validate_country_group(s: &str) -> Result<CountryGroup, String> {
|
||||
}
|
||||
}
|
||||
|
||||
// this only checks compatibility between config the binary. It does not take into consideration
|
||||
// network version. It might do so in the future.
|
||||
fn version_check(cfg: &Config) -> bool {
|
||||
let binary_version = env!("CARGO_PKG_VERSION");
|
||||
let config_version = &cfg.core.base.client.version;
|
||||
if binary_version == config_version {
|
||||
true
|
||||
} else {
|
||||
warn!(
|
||||
"The socks5-client binary has different version than what is specified in config file! {binary_version} and {config_version}",
|
||||
);
|
||||
if is_minor_version_compatible(binary_version, config_version) {
|
||||
info!("but they are still semver compatible. However, consider running the `upgrade` command");
|
||||
true
|
||||
} else {
|
||||
error!("and they are semver incompatible! - please run the `upgrade` command before attempting `run` again");
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn execute(args: Run) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
eprintln!("Starting client {}...", args.common_args.id);
|
||||
|
||||
let mut config = try_load_current_config(&args.common_args.id).await?;
|
||||
config = override_config(config, OverrideConfig::from(args.clone()));
|
||||
|
||||
if !version_check(&config) {
|
||||
error!("failed the local version check");
|
||||
return Err(Box::new(Socks5ClientError::FailedLocalVersionCheck));
|
||||
}
|
||||
|
||||
let storage =
|
||||
OnDiskPersistent::from_paths(config.storage_paths.common_paths, &config.core.base.debug)
|
||||
.await?;
|
||||
|
||||
@@ -14,9 +14,6 @@ pub enum Socks5ClientError {
|
||||
#[error("Failed to validate the loaded config")]
|
||||
ConfigValidationFailure,
|
||||
|
||||
#[error("Failed local version check, client and config mismatch")]
|
||||
FailedLocalVersionCheck,
|
||||
|
||||
#[error("Fail to bind address")]
|
||||
FailToBindAddress,
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ use nym_validator_client::coconut::all_ecash_api_clients;
|
||||
use nym_validator_client::nym_api::EpochId;
|
||||
use nym_validator_client::nyxd::contract_traits::EcashSigningClient;
|
||||
use nym_validator_client::nyxd::contract_traits::{DkgQueryClient, EcashQueryClient};
|
||||
use nym_validator_client::nyxd::cosmwasm_client::ToSingletonContractData;
|
||||
use nym_validator_client::nyxd::cosmwasm_client::ContractResponseData;
|
||||
use nym_validator_client::EcashApiClient;
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ const-str = { workspace = true }
|
||||
log = { workspace = true }
|
||||
pretty_env_logger = { workspace = true }
|
||||
schemars = { workspace = true, features = ["preserve_order"], optional = true }
|
||||
semver.workspace = true
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true, optional = true }
|
||||
|
||||
@@ -44,5 +43,5 @@ tracing = [
|
||||
"tracing-opentelemetry",
|
||||
"opentelemetry",
|
||||
]
|
||||
clap = [ "dep:clap", "dep:clap_complete", "dep:clap_complete_fig" ]
|
||||
clap = ["dep:clap", "dep:clap_complete", "dep:clap_complete_fig"]
|
||||
models = []
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
pub mod build_information;
|
||||
pub mod logging;
|
||||
pub mod version_checker;
|
||||
|
||||
#[cfg(feature = "clap")]
|
||||
pub mod completions;
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub use semver::Version;
|
||||
|
||||
/// Checks if the version is minor version compatible.
|
||||
///
|
||||
/// Checks whether given `version` is compatible with a given semantic version requirement `req`
|
||||
/// according to major-minor semver rules. The semantic version requirement can be passed as a full,
|
||||
/// concrete version number, because that's what we'll have in our Cargo.toml files (e.g. 0.3.2).
|
||||
/// The patch number in the requirement gets dropped and replaced with a wildcard (0.3.*) as all
|
||||
/// minor versions should be compatible with each other.
|
||||
pub fn is_minor_version_compatible(version: &str, req: &str) -> bool {
|
||||
let expected_version = match Version::parse(version) {
|
||||
Ok(v) => v,
|
||||
Err(_) => return false,
|
||||
};
|
||||
let req_version = match Version::parse(req) {
|
||||
Ok(v) => v,
|
||||
Err(_) => return false,
|
||||
};
|
||||
|
||||
expected_version.major == req_version.major && expected_version.minor == req_version.minor
|
||||
}
|
||||
|
||||
pub fn parse_version(raw_version: &str) -> Result<Version, semver::Error> {
|
||||
Version::parse(raw_version)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn version_0_3_0_is_compatible_with_requirement_0_3_x() {
|
||||
assert!(is_minor_version_compatible("0.3.0", "0.3.2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn version_0_3_1_is_compatible_with_minimum_requirement_0_3_x() {
|
||||
assert!(is_minor_version_compatible("0.3.1", "0.3.2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn version_0_3_2_is_compatible_with_minimum_requirement_0_3_x() {
|
||||
assert!(is_minor_version_compatible("0.3.2", "0.3.0"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn version_0_2_0_is_not_compatible_with_requirement_0_3_x() {
|
||||
assert!(!is_minor_version_compatible("0.2.0", "0.3.2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn version_0_4_0_is_not_compatible_with_requirement_0_3_x() {
|
||||
assert!(!is_minor_version_compatible("0.4.0", "0.3.2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn version_1_3_2_is_not_compatible_with_requirement_0_3_x() {
|
||||
assert!(!is_minor_version_compatible("1.3.2", "0.3.2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn version_0_4_0_rc_1_is_compatible_with_version_0_4_0_rc_1() {
|
||||
assert!(is_minor_version_compatible("0.4.0-rc.1", "0.4.0-rc.1"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn returns_false_on_foo_version() {
|
||||
assert!(!is_minor_version_compatible("foo", "0.3.2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn returns_false_on_bar_version() {
|
||||
assert!(!is_minor_version_compatible("0.3.2", "bar"));
|
||||
}
|
||||
}
|
||||
@@ -393,13 +393,20 @@ pub struct Traffic {
|
||||
/// poisson distribution.
|
||||
pub disable_main_poisson_packet_distribution: bool,
|
||||
|
||||
/// Specify whether route selection should be determined by the packet header.
|
||||
pub deterministic_route_selection: bool,
|
||||
|
||||
/// Specify how many times particular packet can be retransmitted
|
||||
/// None - no limit
|
||||
pub maximum_number_of_retransmissions: Option<u32>,
|
||||
|
||||
/// Specifies the packet size used for sent messages.
|
||||
/// Do not override it unless you understand the consequences of that change.
|
||||
pub primary_packet_size: PacketSize,
|
||||
|
||||
/// Specifies the optional auxiliary packet size for optimizing message streams.
|
||||
/// Note that its use decreases overall anonymity.
|
||||
/// Do not set it it unless you understand the consequences of that change.
|
||||
/// Do not set it unless you understand the consequences of that change.
|
||||
pub secondary_packet_size: Option<PacketSize>,
|
||||
|
||||
pub packet_type: PacketType,
|
||||
@@ -424,6 +431,8 @@ impl Default for Traffic {
|
||||
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
|
||||
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
|
||||
disable_main_poisson_packet_distribution: false,
|
||||
deterministic_route_selection: false,
|
||||
maximum_number_of_retransmissions: None,
|
||||
primary_packet_size: PacketSize::RegularPacket,
|
||||
secondary_packet_size: None,
|
||||
packet_type: PacketType::Mix,
|
||||
|
||||
@@ -111,6 +111,7 @@ impl From<ConfigV5> for Config {
|
||||
primary_packet_size: value.debug.traffic.primary_packet_size,
|
||||
secondary_packet_size: value.debug.traffic.secondary_packet_size,
|
||||
packet_type: value.debug.traffic.packet_type,
|
||||
..Default::default()
|
||||
},
|
||||
cover_traffic: CoverTraffic {
|
||||
loop_cover_traffic_average_delay: value
|
||||
|
||||
@@ -514,15 +514,10 @@ where
|
||||
min_gateway_performance: config_topology.minimum_gateway_performance,
|
||||
},
|
||||
nym_api_urls,
|
||||
env!("CARGO_PKG_VERSION").to_string(),
|
||||
user_agent,
|
||||
)),
|
||||
config::TopologyStructure::GeoAware(group_by) => {
|
||||
Box::new(GeoAwareTopologyProvider::new(
|
||||
nym_api_urls,
|
||||
env!("CARGO_PKG_VERSION").to_string(),
|
||||
group_by,
|
||||
))
|
||||
Box::new(GeoAwareTopologyProvider::new(nym_api_urls, group_by))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
+12
-9
@@ -30,7 +30,8 @@ pub(crate) enum Action {
|
||||
InsertPending(Vec<PendingAcknowledgement>),
|
||||
|
||||
/// Removes given `PendingAcknowledgement` from the 'shared' state. Also cancels the retransmission timer.
|
||||
/// Initiated by `AcknowledgementListener`
|
||||
/// Initiated by `AcknowledgementListener` upon receiving the acknowledgement. Also by `RetransmissionRequestListener`
|
||||
/// upon deciding to abandon the data.
|
||||
RemovePending(FragmentIdentifier),
|
||||
|
||||
/// Starts the retransmission timer on given `PendingAcknowledgement` with the `Duration` based on
|
||||
@@ -41,7 +42,7 @@ pub(crate) enum Action {
|
||||
|
||||
/// Updates the expected delay of given `PendingAcknowledgement` with the new provided `SphinxDelay`.
|
||||
/// Initiated by `RetransmissionRequestListener`
|
||||
UpdateDelay(FragmentIdentifier, SphinxDelay),
|
||||
UpdatePendingAck(FragmentIdentifier, SphinxDelay),
|
||||
}
|
||||
|
||||
impl Action {
|
||||
@@ -57,8 +58,8 @@ impl Action {
|
||||
Action::StartTimer(frag_id)
|
||||
}
|
||||
|
||||
pub(crate) fn new_update_delay(frag_id: FragmentIdentifier, delay: SphinxDelay) -> Self {
|
||||
Action::UpdateDelay(frag_id, delay)
|
||||
pub(crate) fn new_update_pending_ack(frag_id: FragmentIdentifier, delay: SphinxDelay) -> Self {
|
||||
Action::UpdatePendingAck(frag_id, delay)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +136,7 @@ impl ActionController {
|
||||
}
|
||||
|
||||
fn handle_start_timer(&mut self, frag_id: FragmentIdentifier) {
|
||||
trace!("{} is starting its timer", frag_id);
|
||||
trace!("{frag_id} is starting its timer");
|
||||
|
||||
if let Some((pending_ack_data, queue_key)) = self.pending_acks_data.get_mut(&frag_id) {
|
||||
// the fact that this branch is now POSSIBLE is a sign of a need to refactor this whole
|
||||
@@ -193,7 +194,7 @@ impl ActionController {
|
||||
|
||||
// initiated basically as a first step of retransmission. At first data has its delay updated
|
||||
// (as new sphinx packet was created with new expected delivery time)
|
||||
fn handle_update_delay(&mut self, frag_id: FragmentIdentifier, delay: SphinxDelay) {
|
||||
fn handle_update_pending_ack(&mut self, frag_id: FragmentIdentifier, delay: SphinxDelay) {
|
||||
trace!("{} is updating its delay", frag_id);
|
||||
// TODO: is it possible to solve this without either locking or temporarily removing the value?
|
||||
if let Some((pending_ack_data, queue_key)) = self.pending_acks_data.remove(&frag_id) {
|
||||
@@ -202,7 +203,7 @@ impl ActionController {
|
||||
// reference to this Arc. HOWEVER, before the Action was pushed onto the queue, the reference
|
||||
// was dropped hence this unwrap is safe.
|
||||
let mut inner_data = Arc::try_unwrap(pending_ack_data).unwrap();
|
||||
inner_data.update_delay(delay);
|
||||
inner_data.update_retransmitted(delay);
|
||||
|
||||
self.pending_acks_data
|
||||
.insert(frag_id, (Arc::new(inner_data), queue_key));
|
||||
@@ -225,7 +226,7 @@ impl ActionController {
|
||||
// about it. Perhaps just reschedule it at later point?
|
||||
let frag_id = expired_ack.into_inner();
|
||||
|
||||
trace!("{} has expired", frag_id);
|
||||
trace!("{frag_id} has expired");
|
||||
|
||||
if let Some((pending_ack_data, queue_key)) = self.pending_acks_data.get_mut(&frag_id) {
|
||||
if queue_key.is_none() {
|
||||
@@ -258,7 +259,9 @@ impl ActionController {
|
||||
Action::InsertPending(pending_acks) => self.handle_insert(pending_acks),
|
||||
Action::RemovePending(frag_id) => self.handle_remove(frag_id),
|
||||
Action::StartTimer(frag_id) => self.handle_start_timer(frag_id),
|
||||
Action::UpdateDelay(frag_id, delay) => self.handle_update_delay(frag_id, delay),
|
||||
Action::UpdatePendingAck(frag_id, delay) => {
|
||||
self.handle_update_pending_ack(frag_id, delay)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ pub(crate) struct PendingAcknowledgement {
|
||||
delay: SphinxDelay,
|
||||
destination: PacketDestination,
|
||||
mix_hops: Option<u8>,
|
||||
retransmissions: u32,
|
||||
}
|
||||
|
||||
impl PendingAcknowledgement {
|
||||
@@ -86,6 +87,7 @@ impl PendingAcknowledgement {
|
||||
delay,
|
||||
destination: PacketDestination::KnownRecipient(recipient.into()),
|
||||
mix_hops,
|
||||
retransmissions: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,6 +107,7 @@ impl PendingAcknowledgement {
|
||||
// Messages sent using SURBs are using the number of mix hops set by the recipient when
|
||||
// they provided the SURBs, so it doesn't make sense to include it here.
|
||||
mix_hops: None,
|
||||
retransmissions: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,8 +119,9 @@ impl PendingAcknowledgement {
|
||||
self.message_chunk.clone()
|
||||
}
|
||||
|
||||
fn update_delay(&mut self, new_delay: SphinxDelay) {
|
||||
fn update_retransmitted(&mut self, new_delay: SphinxDelay) {
|
||||
self.delay = new_delay;
|
||||
self.retransmissions += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,6 +167,9 @@ impl AcknowledgementControllerConnectors {
|
||||
|
||||
/// Configurable parameters of the `AcknowledgementController`
|
||||
pub(super) struct Config {
|
||||
/// Specify how many times particular packet can be retransmitted
|
||||
maximum_retransmissions: Option<u32>,
|
||||
|
||||
/// Given ack timeout in the form a * BASE_DELAY + b, it specifies the additive part `b`
|
||||
ack_wait_addition: Duration,
|
||||
|
||||
@@ -174,8 +181,13 @@ pub(super) struct Config {
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub(super) fn new(ack_wait_addition: Duration, ack_wait_multiplier: f64) -> Self {
|
||||
pub(super) fn new(
|
||||
maximum_retransmissions: Option<u32>,
|
||||
ack_wait_addition: Duration,
|
||||
ack_wait_multiplier: f64,
|
||||
) -> Self {
|
||||
Config {
|
||||
maximum_retransmissions,
|
||||
ack_wait_addition,
|
||||
ack_wait_multiplier,
|
||||
packet_size: Default::default(),
|
||||
@@ -238,6 +250,7 @@ where
|
||||
|
||||
// will listen for any ack timeouts and trigger retransmission
|
||||
let retransmission_request_listener = RetransmissionRequestListener::new(
|
||||
config.maximum_retransmissions,
|
||||
connectors.ack_action_sender.clone(),
|
||||
message_handler,
|
||||
retransmission_rx,
|
||||
|
||||
+16
-3
@@ -20,6 +20,7 @@ use std::sync::{Arc, Weak};
|
||||
|
||||
// responsible for packet retransmission upon fired timer
|
||||
pub(super) struct RetransmissionRequestListener<R> {
|
||||
maximum_retransmissions: Option<u32>,
|
||||
action_sender: AckActionSender,
|
||||
message_handler: MessageHandler<R>,
|
||||
request_receiver: RetransmissionRequestReceiver,
|
||||
@@ -31,12 +32,14 @@ where
|
||||
R: CryptoRng + Rng,
|
||||
{
|
||||
pub(super) fn new(
|
||||
maximum_retransmissions: Option<u32>,
|
||||
action_sender: AckActionSender,
|
||||
message_handler: MessageHandler<R>,
|
||||
request_receiver: RetransmissionRequestReceiver,
|
||||
reply_controller_sender: ReplyControllerSender,
|
||||
) -> Self {
|
||||
RetransmissionRequestListener {
|
||||
maximum_retransmissions,
|
||||
action_sender,
|
||||
message_handler,
|
||||
request_receiver,
|
||||
@@ -77,6 +80,18 @@ where
|
||||
}
|
||||
};
|
||||
|
||||
let frag_id = timed_out_ack.message_chunk.fragment_identifier();
|
||||
|
||||
if let Some(limit) = self.maximum_retransmissions {
|
||||
if timed_out_ack.retransmissions >= limit {
|
||||
warn!("reached maximum number of allowed retransmissions for the packet");
|
||||
self.action_sender
|
||||
.unbounded_send(Action::new_remove(frag_id))
|
||||
.unwrap();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let maybe_prepared_fragment = match &timed_out_ack.destination {
|
||||
PacketDestination::Anonymous {
|
||||
recipient_tag,
|
||||
@@ -101,8 +116,6 @@ where
|
||||
}
|
||||
};
|
||||
|
||||
let frag_id = timed_out_ack.message_chunk.fragment_identifier();
|
||||
|
||||
let prepared_fragment = match maybe_prepared_fragment {
|
||||
Ok(prepared_fragment) => prepared_fragment,
|
||||
Err(err) => {
|
||||
@@ -136,7 +149,7 @@ where
|
||||
// with the additional poisson delay.
|
||||
// And since Actions are executed in order `UpdateTimer` will HAVE TO be executed before `StartTimer`
|
||||
self.action_sender
|
||||
.unbounded_send(Action::new_update_delay(frag_id, new_delay))
|
||||
.unbounded_send(Action::new_update_pending_ack(frag_id, new_delay))
|
||||
.unwrap();
|
||||
|
||||
// send to `OutQueueControl` to eventually send to the mix network
|
||||
|
||||
@@ -91,6 +91,9 @@ pub(crate) struct Config {
|
||||
/// and surb-based are going to be sent.
|
||||
sender_address: Recipient,
|
||||
|
||||
/// Specify whether route selection should be determined by the packet header.
|
||||
deterministic_route_selection: bool,
|
||||
|
||||
/// Average delay a data packet is going to get delay at a single mixnode.
|
||||
average_packet_delay: Duration,
|
||||
|
||||
@@ -114,10 +117,12 @@ impl Config {
|
||||
sender_address: Recipient,
|
||||
average_packet_delay: Duration,
|
||||
average_ack_delay: Duration,
|
||||
deterministic_route_selection: bool,
|
||||
) -> Self {
|
||||
Config {
|
||||
ack_key,
|
||||
sender_address,
|
||||
deterministic_route_selection,
|
||||
average_packet_delay,
|
||||
average_ack_delay,
|
||||
num_mix_hops: DEFAULT_NUM_MIX_HOPS,
|
||||
@@ -176,6 +181,7 @@ where
|
||||
{
|
||||
let message_preparer = MessagePreparer::new(
|
||||
rng,
|
||||
config.deterministic_route_selection,
|
||||
config.sender_address,
|
||||
config.average_packet_delay,
|
||||
config.average_ack_delay,
|
||||
@@ -634,7 +640,7 @@ where
|
||||
|
||||
pub(crate) fn update_ack_delay(&self, id: FragmentIdentifier, new_delay: Delay) {
|
||||
self.action_sender
|
||||
.unbounded_send(Action::UpdateDelay(id, new_delay))
|
||||
.unbounded_send(Action::UpdatePendingAck(id, new_delay))
|
||||
.expect("action control task has died")
|
||||
}
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ pub struct Config {
|
||||
impl<'a> From<&'a Config> for acknowledgement_control::Config {
|
||||
fn from(cfg: &'a Config) -> Self {
|
||||
acknowledgement_control::Config::new(
|
||||
cfg.traffic.maximum_number_of_retransmissions,
|
||||
cfg.acks.ack_wait_addition,
|
||||
cfg.acks.ack_wait_multiplier,
|
||||
)
|
||||
@@ -97,6 +98,7 @@ impl<'a> From<&'a Config> for message_handler::Config {
|
||||
cfg.self_recipient,
|
||||
cfg.traffic.average_packet_delay,
|
||||
cfg.acks.average_ack_delay,
|
||||
cfg.traffic.deterministic_route_selection,
|
||||
)
|
||||
.with_custom_primary_packet_size(cfg.traffic.primary_packet_size)
|
||||
.with_custom_secondary_packet_size(cfg.traffic.secondary_packet_size)
|
||||
|
||||
@@ -38,7 +38,7 @@ pub struct TopologyReadPermit<'a> {
|
||||
permit: RwLockReadGuard<'a, Option<NymTopology>>,
|
||||
}
|
||||
|
||||
impl<'a> Deref for TopologyReadPermit<'a> {
|
||||
impl Deref for TopologyReadPermit<'_> {
|
||||
type Target = Option<NymTopology>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
|
||||
@@ -85,15 +85,10 @@ fn check_layer_integrity(topology: NymTopology) -> Result<(), ()> {
|
||||
pub struct GeoAwareTopologyProvider {
|
||||
validator_client: nym_validator_client::client::NymApiClient,
|
||||
filter_on: GroupBy,
|
||||
client_version: String,
|
||||
}
|
||||
|
||||
impl GeoAwareTopologyProvider {
|
||||
pub fn new(
|
||||
mut nym_api_urls: Vec<Url>,
|
||||
client_version: String,
|
||||
filter_on: GroupBy,
|
||||
) -> GeoAwareTopologyProvider {
|
||||
pub fn new(mut nym_api_urls: Vec<Url>, filter_on: GroupBy) -> GeoAwareTopologyProvider {
|
||||
log::info!(
|
||||
"Creating geo-aware topology provider with filter on {}",
|
||||
filter_on
|
||||
@@ -105,14 +100,13 @@ impl GeoAwareTopologyProvider {
|
||||
nym_api_urls[0].clone(),
|
||||
),
|
||||
filter_on,
|
||||
client_version,
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_topology(&self) -> Option<NymTopology> {
|
||||
let mixnodes = match self
|
||||
.validator_client
|
||||
.get_all_basic_active_mixing_assigned_nodes(Some(self.client_version.clone()))
|
||||
.get_all_basic_active_mixing_assigned_nodes()
|
||||
.await
|
||||
{
|
||||
Err(err) => {
|
||||
@@ -124,7 +118,7 @@ impl GeoAwareTopologyProvider {
|
||||
|
||||
let gateways = match self
|
||||
.validator_client
|
||||
.get_all_basic_entry_assigned_nodes(Some(self.client_version.clone()))
|
||||
.get_all_basic_entry_assigned_nodes()
|
||||
.await
|
||||
{
|
||||
Err(err) => {
|
||||
|
||||
@@ -35,18 +35,11 @@ pub struct NymApiTopologyProvider {
|
||||
|
||||
validator_client: nym_validator_client::client::NymApiClient,
|
||||
nym_api_urls: Vec<Url>,
|
||||
|
||||
client_version: String,
|
||||
currently_used_api: usize,
|
||||
}
|
||||
|
||||
impl NymApiTopologyProvider {
|
||||
pub fn new(
|
||||
config: Config,
|
||||
mut nym_api_urls: Vec<Url>,
|
||||
client_version: String,
|
||||
user_agent: Option<UserAgent>,
|
||||
) -> Self {
|
||||
pub fn new(config: Config, mut nym_api_urls: Vec<Url>, user_agent: Option<UserAgent>) -> Self {
|
||||
nym_api_urls.shuffle(&mut thread_rng());
|
||||
|
||||
let validator_client = if let Some(user_agent) = user_agent {
|
||||
@@ -62,7 +55,6 @@ impl NymApiTopologyProvider {
|
||||
config,
|
||||
validator_client,
|
||||
nym_api_urls,
|
||||
client_version,
|
||||
currently_used_api: 0,
|
||||
}
|
||||
}
|
||||
@@ -99,7 +91,7 @@ impl NymApiTopologyProvider {
|
||||
async fn get_current_compatible_topology(&mut self) -> Option<NymTopology> {
|
||||
let mixnodes = match self
|
||||
.validator_client
|
||||
.get_all_basic_active_mixing_assigned_nodes(Some(self.client_version.clone()))
|
||||
.get_all_basic_active_mixing_assigned_nodes()
|
||||
.await
|
||||
{
|
||||
Err(err) => {
|
||||
@@ -111,7 +103,7 @@ impl NymApiTopologyProvider {
|
||||
|
||||
let gateways = match self
|
||||
.validator_client
|
||||
.get_all_basic_entry_assigned_nodes(Some(self.client_version.clone()))
|
||||
.get_all_basic_entry_assigned_nodes()
|
||||
.await
|
||||
{
|
||||
Err(err) => {
|
||||
|
||||
@@ -94,7 +94,7 @@ pub async fn current_gateways<R: Rng>(
|
||||
|
||||
log::debug!("Fetching list of gateways from: {nym_api}");
|
||||
|
||||
let gateways = client.get_all_basic_entry_assigned_nodes(None).await?;
|
||||
let gateways = client.get_all_basic_entry_assigned_nodes().await?;
|
||||
log::debug!("Found {} gateways", gateways.len());
|
||||
log::trace!("Gateways: {:#?}", gateways);
|
||||
|
||||
@@ -121,9 +121,7 @@ pub async fn current_mixnodes<R: Rng>(
|
||||
|
||||
log::trace!("Fetching list of mixnodes from: {nym_api}");
|
||||
|
||||
let mixnodes = client
|
||||
.get_all_basic_active_mixing_assigned_nodes(None)
|
||||
.await?;
|
||||
let mixnodes = client.get_all_basic_active_mixing_assigned_nodes().await?;
|
||||
let valid_mixnodes = mixnodes
|
||||
.iter()
|
||||
.filter_map(|mixnode| mixnode.try_into().ok())
|
||||
|
||||
@@ -87,8 +87,10 @@ impl ClientBandwidth {
|
||||
|
||||
if remaining < 0 {
|
||||
tracing::warn!("OUT OF BANDWIDTH. remaining: {remaining_bi2}");
|
||||
} else {
|
||||
} else if remaining < 1_000_000 {
|
||||
tracing::info!("remaining bandwidth: {remaining_bi2}");
|
||||
} else {
|
||||
tracing::debug!("remaining bandwidth: {remaining_bi2}");
|
||||
}
|
||||
|
||||
self.inner
|
||||
|
||||
@@ -139,6 +139,10 @@ impl<C, St> GatewayClient<C, St> {
|
||||
self.gateway_identity
|
||||
}
|
||||
|
||||
pub fn shared_key(&self) -> Option<Arc<SharedGatewayKey>> {
|
||||
self.shared_key.clone()
|
||||
}
|
||||
|
||||
pub fn ws_fd(&self) -> Option<RawFd> {
|
||||
match &self.connection {
|
||||
SocketState::Available(conn) => ws_fd(conn.as_ref()),
|
||||
@@ -408,7 +412,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
}
|
||||
|
||||
Some(_) => {
|
||||
info!("the gateway is using exactly the same (or older) protocol version as we are. We're good to continue!");
|
||||
debug!("the gateway is using exactly the same (or older) protocol version as we are. We're good to continue!");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -992,24 +996,6 @@ impl<C, St> GatewayClient<C, St> {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[deprecated(note = "this method does not deal with upgraded keys for legacy clients")]
|
||||
pub async fn authenticate_and_start(
|
||||
&mut self,
|
||||
) -> Result<AuthenticationResponse, GatewayClientError>
|
||||
where
|
||||
C: DkgQueryClient + Send + Sync,
|
||||
St: CredentialStorage,
|
||||
<St as CredentialStorage>::StorageError: Send + Sync + 'static,
|
||||
{
|
||||
let shared_key = self.perform_initial_authentication().await?;
|
||||
self.claim_initial_bandwidth().await?;
|
||||
|
||||
// this call is NON-blocking
|
||||
self.start_listening_for_mixnet_messages()?;
|
||||
|
||||
Ok(shared_key)
|
||||
}
|
||||
}
|
||||
|
||||
// type alias for an ease of use
|
||||
|
||||
@@ -46,7 +46,8 @@ pub(crate) fn ws_fd(_conn: &WsConn) -> Option<RawFd> {
|
||||
#[cfg(unix)]
|
||||
match _conn.get_ref() {
|
||||
MaybeTlsStream::Plain(stream) => Some(stream.as_raw_fd()),
|
||||
&_ => None,
|
||||
MaybeTlsStream::Rustls(tls_stream) => Some(tls_stream.as_raw_fd()),
|
||||
_ => None,
|
||||
}
|
||||
#[cfg(not(unix))]
|
||||
None
|
||||
@@ -110,6 +111,11 @@ impl PartiallyDelegatedRouter {
|
||||
}
|
||||
};
|
||||
|
||||
if self.stream_return.is_canceled() {
|
||||
// nothing to do, receiver has been dropped
|
||||
return;
|
||||
}
|
||||
|
||||
let return_res = match ret {
|
||||
Err(err) => self.stream_return.send(Err(err)),
|
||||
Ok(_) => {
|
||||
|
||||
@@ -19,8 +19,8 @@ use nym_api_requests::ecash::{
|
||||
PartialExpirationDateSignatureResponse, VerificationKeyResponse,
|
||||
};
|
||||
use nym_api_requests::models::{
|
||||
ApiHealthResponse, GatewayCoreStatusResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse,
|
||||
NymNodeDescription, RewardEstimationResponse, StakeSaturationResponse,
|
||||
ApiHealthResponse, GatewayBondAnnotated, GatewayCoreStatusResponse, MixnodeCoreStatusResponse,
|
||||
MixnodeStatusResponse, NymNodeDescription, RewardEstimationResponse, StakeSaturationResponse,
|
||||
};
|
||||
use nym_api_requests::models::{LegacyDescribedGateway, MixNodeBondAnnotated};
|
||||
use nym_api_requests::nym_nodes::SkimmedNode;
|
||||
@@ -257,6 +257,13 @@ impl<C, S> Client<C, S> {
|
||||
Ok(self.nym_api.get_gateways().await?)
|
||||
}
|
||||
|
||||
#[deprecated]
|
||||
pub async fn get_cached_gateways_detailed_unfiltered(
|
||||
&self,
|
||||
) -> Result<Vec<GatewayBondAnnotated>, ValidatorClientError> {
|
||||
Ok(self.nym_api.get_gateways_detailed_unfiltered().await?)
|
||||
}
|
||||
|
||||
// TODO: combine with NymApiClient...
|
||||
pub async fn get_all_cached_described_nodes(
|
||||
&self,
|
||||
@@ -351,34 +358,19 @@ impl NymApiClient {
|
||||
}
|
||||
|
||||
#[deprecated(note = "use get_all_basic_active_mixing_assigned_nodes instead")]
|
||||
pub async fn get_basic_mixnodes(
|
||||
&self,
|
||||
semver_compatibility: Option<String>,
|
||||
) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
|
||||
Ok(self
|
||||
.nym_api
|
||||
.get_basic_mixnodes(semver_compatibility)
|
||||
.await?
|
||||
.nodes)
|
||||
pub async fn get_basic_mixnodes(&self) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
|
||||
Ok(self.nym_api.get_basic_mixnodes().await?.nodes)
|
||||
}
|
||||
|
||||
#[deprecated(note = "use get_all_basic_entry_assigned_nodes instead")]
|
||||
pub async fn get_basic_gateways(
|
||||
&self,
|
||||
semver_compatibility: Option<String>,
|
||||
) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
|
||||
Ok(self
|
||||
.nym_api
|
||||
.get_basic_gateways(semver_compatibility)
|
||||
.await?
|
||||
.nodes)
|
||||
pub async fn get_basic_gateways(&self) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
|
||||
Ok(self.nym_api.get_basic_gateways().await?.nodes)
|
||||
}
|
||||
|
||||
/// retrieve basic information for nodes are capable of operating as an entry gateway
|
||||
/// this includes legacy gateways and nym-nodes
|
||||
pub async fn get_all_basic_entry_assigned_nodes(
|
||||
&self,
|
||||
semver_compatibility: Option<String>,
|
||||
) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
|
||||
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
|
||||
let mut page = 0;
|
||||
@@ -387,12 +379,7 @@ impl NymApiClient {
|
||||
loop {
|
||||
let mut res = self
|
||||
.nym_api
|
||||
.get_basic_entry_assigned_nodes(
|
||||
semver_compatibility.clone(),
|
||||
false,
|
||||
Some(page),
|
||||
None,
|
||||
)
|
||||
.get_basic_entry_assigned_nodes(false, Some(page), None)
|
||||
.await?;
|
||||
|
||||
nodes.append(&mut res.nodes.data);
|
||||
@@ -410,7 +397,6 @@ impl NymApiClient {
|
||||
/// this includes legacy mixnodes and nym-nodes
|
||||
pub async fn get_all_basic_active_mixing_assigned_nodes(
|
||||
&self,
|
||||
semver_compatibility: Option<String>,
|
||||
) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
|
||||
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
|
||||
let mut page = 0;
|
||||
@@ -419,12 +405,7 @@ impl NymApiClient {
|
||||
loop {
|
||||
let mut res = self
|
||||
.nym_api
|
||||
.get_basic_active_mixing_assigned_nodes(
|
||||
semver_compatibility.clone(),
|
||||
false,
|
||||
Some(page),
|
||||
None,
|
||||
)
|
||||
.get_basic_active_mixing_assigned_nodes(false, Some(page), None)
|
||||
.await?;
|
||||
|
||||
nodes.append(&mut res.nodes.data);
|
||||
@@ -442,7 +423,6 @@ impl NymApiClient {
|
||||
/// this includes legacy mixnodes and nym-nodes
|
||||
pub async fn get_all_basic_mixing_capable_nodes(
|
||||
&self,
|
||||
semver_compatibility: Option<String>,
|
||||
) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
|
||||
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
|
||||
let mut page = 0;
|
||||
@@ -451,12 +431,7 @@ impl NymApiClient {
|
||||
loop {
|
||||
let mut res = self
|
||||
.nym_api
|
||||
.get_basic_mixing_capable_nodes(
|
||||
semver_compatibility.clone(),
|
||||
false,
|
||||
Some(page),
|
||||
None,
|
||||
)
|
||||
.get_basic_mixing_capable_nodes(false, Some(page), None)
|
||||
.await?;
|
||||
|
||||
nodes.append(&mut res.nodes.data);
|
||||
@@ -471,10 +446,7 @@ impl NymApiClient {
|
||||
}
|
||||
|
||||
/// retrieve basic information for all bonded nodes on the network
|
||||
pub async fn get_all_basic_nodes(
|
||||
&self,
|
||||
semver_compatibility: Option<String>,
|
||||
) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
|
||||
pub async fn get_all_basic_nodes(&self) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
|
||||
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
|
||||
let mut page = 0;
|
||||
let mut nodes = Vec::new();
|
||||
@@ -482,7 +454,7 @@ impl NymApiClient {
|
||||
loop {
|
||||
let mut res = self
|
||||
.nym_api
|
||||
.get_basic_nodes(semver_compatibility.clone(), false, Some(page), None)
|
||||
.get_basic_nodes(false, Some(page), None)
|
||||
.await?;
|
||||
|
||||
nodes.append(&mut res.nodes.data);
|
||||
|
||||
@@ -102,6 +102,23 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[deprecated]
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_gateways_detailed_unfiltered(
|
||||
&self,
|
||||
) -> Result<Vec<GatewayBondAnnotated>, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS,
|
||||
routes::GATEWAYS,
|
||||
routes::DETAILED_UNFILTERED,
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[deprecated]
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_mixnodes_detailed_unfiltered(
|
||||
@@ -188,16 +205,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
|
||||
#[deprecated]
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
async fn get_basic_mixnodes(
|
||||
&self,
|
||||
semver_compatibility: Option<String>,
|
||||
) -> Result<CachedNodesResponse<SkimmedNode>, NymAPIError> {
|
||||
let params = if let Some(semver_compatibility) = &semver_compatibility {
|
||||
vec![("semver_compatibility", semver_compatibility.as_str())]
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
async fn get_basic_mixnodes(&self) -> Result<CachedNodesResponse<SkimmedNode>, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
@@ -206,23 +214,14 @@ pub trait NymApiClientExt: ApiClient {
|
||||
"mixnodes",
|
||||
"skimmed",
|
||||
],
|
||||
¶ms,
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[deprecated]
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_basic_gateways(
|
||||
&self,
|
||||
semver_compatibility: Option<String>,
|
||||
) -> Result<CachedNodesResponse<SkimmedNode>, NymAPIError> {
|
||||
let params = if let Some(semver_compatibility) = &semver_compatibility {
|
||||
vec![("semver_compatibility", semver_compatibility.as_str())]
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
async fn get_basic_gateways(&self) -> Result<CachedNodesResponse<SkimmedNode>, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
@@ -231,7 +230,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
"gateways",
|
||||
"skimmed",
|
||||
],
|
||||
¶ms,
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -241,17 +240,12 @@ pub trait NymApiClientExt: ApiClient {
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_basic_entry_assigned_nodes(
|
||||
&self,
|
||||
semver_compatibility: Option<String>,
|
||||
no_legacy: bool,
|
||||
page: Option<u32>,
|
||||
per_page: Option<u32>,
|
||||
) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> {
|
||||
let mut params = Vec::new();
|
||||
|
||||
if let Some(arg) = &semver_compatibility {
|
||||
params.push(("semver_compatibility", arg.clone()))
|
||||
}
|
||||
|
||||
if no_legacy {
|
||||
params.push(("no_legacy", "true".to_string()))
|
||||
}
|
||||
@@ -283,17 +277,12 @@ pub trait NymApiClientExt: ApiClient {
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_basic_active_mixing_assigned_nodes(
|
||||
&self,
|
||||
semver_compatibility: Option<String>,
|
||||
no_legacy: bool,
|
||||
page: Option<u32>,
|
||||
per_page: Option<u32>,
|
||||
) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> {
|
||||
let mut params = Vec::new();
|
||||
|
||||
if let Some(arg) = &semver_compatibility {
|
||||
params.push(("semver_compatibility", arg.clone()))
|
||||
}
|
||||
|
||||
if no_legacy {
|
||||
params.push(("no_legacy", "true".to_string()))
|
||||
}
|
||||
@@ -325,17 +314,12 @@ pub trait NymApiClientExt: ApiClient {
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_basic_mixing_capable_nodes(
|
||||
&self,
|
||||
semver_compatibility: Option<String>,
|
||||
no_legacy: bool,
|
||||
page: Option<u32>,
|
||||
per_page: Option<u32>,
|
||||
) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> {
|
||||
let mut params = Vec::new();
|
||||
|
||||
if let Some(arg) = &semver_compatibility {
|
||||
params.push(("semver_compatibility", arg.clone()))
|
||||
}
|
||||
|
||||
if no_legacy {
|
||||
params.push(("no_legacy", "true".to_string()))
|
||||
}
|
||||
@@ -365,17 +349,12 @@ pub trait NymApiClientExt: ApiClient {
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_basic_nodes(
|
||||
&self,
|
||||
semver_compatibility: Option<String>,
|
||||
no_legacy: bool,
|
||||
page: Option<u32>,
|
||||
per_page: Option<u32>,
|
||||
) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> {
|
||||
let mut params = Vec::new();
|
||||
|
||||
if let Some(arg) = &semver_compatibility {
|
||||
params.push(("semver_compatibility", arg.clone()))
|
||||
}
|
||||
|
||||
if no_legacy {
|
||||
params.push(("no_legacy", "true".to_string()))
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ impl Div<GasPrice> for Coin {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Div<GasPrice> for &'a Coin {
|
||||
impl Div<GasPrice> for &Coin {
|
||||
type Output = Gas;
|
||||
|
||||
fn div(self, rhs: GasPrice) -> Self::Output {
|
||||
|
||||
+32
-4
@@ -26,10 +26,11 @@ use nym_mixnet_contract_common::{
|
||||
reward_params::{Performance, RewardingParams},
|
||||
rewarding::{EstimatedCurrentEpochRewardResponse, PendingRewardResponse},
|
||||
ContractBuildInformation, ContractState, ContractStateParams, CurrentIntervalResponse,
|
||||
Delegation, EpochEventId, EpochStatus, GatewayBond, GatewayBondResponse,
|
||||
GatewayOwnershipResponse, IdentityKey, IdentityKeyRef, IntervalEventId, MixNodeBond,
|
||||
MixNodeDetails, MixOwnershipResponse, MixnodeDetailsByIdentityResponse, MixnodeDetailsResponse,
|
||||
NodeId, NumberOfPendingEventsResponse, NymNodeBond, NymNodeDetails,
|
||||
CurrentNymNodeVersionResponse, Delegation, EpochEventId, EpochStatus, GatewayBond,
|
||||
GatewayBondResponse, GatewayOwnershipResponse, HistoricalNymNodeVersionEntry, IdentityKey,
|
||||
IdentityKeyRef, IntervalEventId, MixNodeBond, MixNodeDetails, MixOwnershipResponse,
|
||||
MixnodeDetailsByIdentityResponse, MixnodeDetailsResponse, NodeId,
|
||||
NumberOfPendingEventsResponse, NymNodeBond, NymNodeDetails, NymNodeVersionHistoryResponse,
|
||||
PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse, PagedGatewayResponse,
|
||||
PagedMixnodeBondsResponse, PagedNodeDelegationsResponse, PendingEpochEvent,
|
||||
PendingEpochEventResponse, PendingEpochEventsResponse, PendingIntervalEvent,
|
||||
@@ -71,6 +72,22 @@ pub trait MixnetQueryClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_nym_node_version_history_paged(
|
||||
&self,
|
||||
start_after: Option<u32>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<NymNodeVersionHistoryResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetNymNodeVersionHistory { limit, start_after })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_current_nym_node_version(
|
||||
&self,
|
||||
) -> Result<CurrentNymNodeVersionResponse, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetCurrentNymNodeVersion {})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_mixnet_contract_state(&self) -> Result<ContractState, NyxdError> {
|
||||
self.query_mixnet_contract(MixnetQueryMsg::GetState {})
|
||||
.await
|
||||
@@ -638,6 +655,12 @@ pub trait PagedMixnetQueryClient: MixnetQueryClient {
|
||||
) -> Result<Vec<PendingIntervalEvent>, NyxdError> {
|
||||
collect_paged!(self, get_pending_interval_events_paged, events)
|
||||
}
|
||||
|
||||
async fn get_full_nym_node_version_history(
|
||||
&self,
|
||||
) -> Result<Vec<HistoricalNymNodeVersionEntry>, NyxdError> {
|
||||
collect_paged!(self, get_nym_node_version_history_paged, history)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -724,6 +747,7 @@ where
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::nyxd::contract_traits::tests::IgnoreValue;
|
||||
use nym_mixnet_contract_common::QueryMsg;
|
||||
|
||||
// it's enough that this compiles and clippy is happy about it
|
||||
#[allow(dead_code)]
|
||||
@@ -924,6 +948,10 @@ mod tests {
|
||||
MixnetQueryMsg::GetRewardedSetMetadata {} => {
|
||||
client.get_rewarded_set_metadata().ignore()
|
||||
}
|
||||
QueryMsg::GetCurrentNymNodeVersion {} => client.get_current_nym_node_version().ignore(),
|
||||
QueryMsg::GetNymNodeVersionHistory { limit, start_after } => client
|
||||
.get_nym_node_version_history_paged(start_after, limit)
|
||||
.ignore(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,44 @@ use tracing::error;
|
||||
|
||||
pub use cosmrs::abci::MsgResponse;
|
||||
|
||||
pub fn parse_singleton_u32_from_contract_response(b: Vec<u8>) -> Result<u32, NyxdError> {
|
||||
if b.len() != 4 {
|
||||
return Err(NyxdError::MalformedResponseData {
|
||||
got: b.len(),
|
||||
expected: 4,
|
||||
});
|
||||
}
|
||||
Ok(u32::from_be_bytes([b[0], b[1], b[2], b[3]]))
|
||||
}
|
||||
|
||||
pub fn parse_singleton_u64_from_contract_response(b: Vec<u8>) -> Result<u64, NyxdError> {
|
||||
if b.len() != 8 {
|
||||
return Err(NyxdError::MalformedResponseData {
|
||||
got: b.len(),
|
||||
expected: 8,
|
||||
});
|
||||
}
|
||||
Ok(u64::from_be_bytes([
|
||||
b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
|
||||
]))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ParsedContractResponse {
|
||||
pub message_index: usize,
|
||||
pub response: Vec<u8>,
|
||||
}
|
||||
|
||||
impl ParsedContractResponse {
|
||||
pub fn parse_singleton_u32_contract_data(self) -> Result<u32, NyxdError> {
|
||||
parse_singleton_u32_from_contract_response(self.response)
|
||||
}
|
||||
|
||||
pub fn parse_singleton_u64_contract_data(self) -> Result<u64, NyxdError> {
|
||||
parse_singleton_u64_from_contract_response(self.response)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_msg_responses(data: Bytes) -> Vec<MsgResponse> {
|
||||
// it seems that currently, on wasmd 0.43 + tendermint-rs 0.37 + cosmrs 0.17.0-pre
|
||||
// the data is left in undecoded base64 form, but I'd imagine this might change so if the decoding fails,
|
||||
@@ -34,35 +72,25 @@ pub fn parse_msg_responses(data: Bytes) -> Vec<MsgResponse> {
|
||||
}
|
||||
|
||||
// requires there's a single response message
|
||||
pub trait ToSingletonContractData: Sized {
|
||||
pub trait ContractResponseData: Sized {
|
||||
fn parse_singleton_u32_contract_data(&self) -> Result<u32, NyxdError> {
|
||||
let b = self.to_singleton_contract_data()?;
|
||||
if b.len() != 4 {
|
||||
return Err(NyxdError::MalformedResponseData {
|
||||
got: b.len(),
|
||||
expected: 4,
|
||||
});
|
||||
}
|
||||
Ok(u32::from_be_bytes([b[0], b[1], b[2], b[3]]))
|
||||
parse_singleton_u32_from_contract_response(b)
|
||||
}
|
||||
|
||||
fn parse_singleton_u64_contract_data(&self) -> Result<u64, NyxdError> {
|
||||
let b = self.to_singleton_contract_data()?;
|
||||
if b.len() != 8 {
|
||||
return Err(NyxdError::MalformedResponseData {
|
||||
got: b.len(),
|
||||
expected: 8,
|
||||
});
|
||||
}
|
||||
Ok(u64::from_be_bytes([
|
||||
b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
|
||||
]))
|
||||
parse_singleton_u64_from_contract_response(b)
|
||||
}
|
||||
|
||||
fn to_singleton_contract_data(&self) -> Result<Vec<u8>, NyxdError>;
|
||||
|
||||
fn to_unchecked_contract_data(&self) -> Result<Vec<Vec<u8>>, NyxdError>;
|
||||
|
||||
fn to_contract_data(&self) -> Result<Vec<ParsedContractResponse>, NyxdError>;
|
||||
}
|
||||
|
||||
impl ToSingletonContractData for ExecuteResult {
|
||||
impl ContractResponseData for ExecuteResult {
|
||||
fn to_singleton_contract_data(&self) -> Result<Vec<u8>, NyxdError> {
|
||||
if self.msg_responses.len() != 1 {
|
||||
return Err(NyxdError::UnexpectedNumberOfMsgResponses {
|
||||
@@ -72,6 +100,30 @@ impl ToSingletonContractData for ExecuteResult {
|
||||
|
||||
self.msg_responses[0].to_contract_response_data()
|
||||
}
|
||||
|
||||
fn to_unchecked_contract_data(&self) -> Result<Vec<Vec<u8>>, NyxdError> {
|
||||
self.msg_responses
|
||||
.iter()
|
||||
.map(ToContractResponseData::to_contract_response_data)
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn to_contract_data(&self) -> Result<Vec<ParsedContractResponse>, NyxdError> {
|
||||
let mut response = Vec::new();
|
||||
|
||||
for (message_index, msg) in self.msg_responses.iter().enumerate() {
|
||||
// unfortunately `Name` trait has not been derived for `MsgExecuteContractResponse`,
|
||||
// so we have to make an explicit string comparison instead
|
||||
if msg.type_url == "/cosmwasm.wasm.v1.MsgExecuteContractResponse" {
|
||||
response.push(ParsedContractResponse {
|
||||
message_index,
|
||||
response: msg.to_contract_response_data()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ToContractResponseData: Sized {
|
||||
|
||||
@@ -23,7 +23,7 @@ use tendermint_rpc::endpoint::*;
|
||||
use tendermint_rpc::query::Query;
|
||||
use tendermint_rpc::{Error as TendermintRpcError, Order, Paging, SimpleRequest};
|
||||
|
||||
pub use helpers::{ToContractResponseData, ToSingletonContractData};
|
||||
pub use helpers::{ContractResponseData, ToContractResponseData};
|
||||
|
||||
#[cfg(feature = "http-client")]
|
||||
use crate::http_client;
|
||||
|
||||
@@ -22,7 +22,7 @@ pub struct GasPrice {
|
||||
pub denom: String,
|
||||
}
|
||||
|
||||
impl<'a> Mul<Gas> for &'a GasPrice {
|
||||
impl Mul<Gas> for &GasPrice {
|
||||
type Output = Coin;
|
||||
|
||||
fn mul(self, gas_limit: Gas) -> Self::Output {
|
||||
|
||||
@@ -0,0 +1,673 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use cosmwasm_std::Decimal;
|
||||
use std::cmp::Ordering;
|
||||
use std::ops::{Add, Sub};
|
||||
|
||||
#[cw_serde]
|
||||
pub struct HistoricalNymNodeVersion {
|
||||
/// Version of the nym node that is going to be used for determining the version score of a node.
|
||||
/// note: value stored here is pre-validated `semver::Version`
|
||||
pub semver: String,
|
||||
|
||||
/// Block height of when this version has been added to the contract
|
||||
pub introduced_at_height: u64,
|
||||
|
||||
/// The absolute version difference as compared against the first version introduced into the contract.
|
||||
pub difference_since_genesis: TotalVersionDifference,
|
||||
}
|
||||
|
||||
impl HistoricalNymNodeVersion {
|
||||
pub fn genesis(semver: String, height: u64) -> HistoricalNymNodeVersion {
|
||||
HistoricalNymNodeVersion {
|
||||
semver,
|
||||
introduced_at_height: height,
|
||||
difference_since_genesis: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: the value stored in the contract is always valid
|
||||
// if you manually construct that struct with invalid value, it's on you.
|
||||
#[allow(clippy::unwrap_used)]
|
||||
pub fn semver_unchecked(&self) -> semver::Version {
|
||||
self.semver.parse().unwrap()
|
||||
}
|
||||
|
||||
/// Return [`TotalVersionDifference`] for a new release version that is going to be pushed right after this one
|
||||
/// this function cannot be called against 2 arbitrary versions
|
||||
#[inline]
|
||||
pub fn cumulative_difference_since_genesis(
|
||||
&self,
|
||||
new_version: &semver::Version,
|
||||
) -> TotalVersionDifference {
|
||||
let self_semver = self.semver_unchecked();
|
||||
let mut new_absolute = self.difference_since_genesis;
|
||||
if new_version.major > self_semver.major {
|
||||
new_absolute.major += (new_version.major - self_semver.major) as u32
|
||||
} else if new_version.minor > self_semver.minor {
|
||||
new_absolute.minor += (new_version.minor - self_semver.minor) as u32
|
||||
} else if new_version.patch > self_semver.patch {
|
||||
new_absolute.patch += (new_version.patch - self_semver.patch) as u32
|
||||
} else if new_version.pre != self_semver.pre {
|
||||
new_absolute.prerelease += 1
|
||||
}
|
||||
new_absolute
|
||||
}
|
||||
|
||||
pub fn relative_difference(&self, other: &Self) -> TotalVersionDifference {
|
||||
if self.difference_since_genesis > other.difference_since_genesis {
|
||||
self.difference_since_genesis - other.difference_since_genesis
|
||||
} else {
|
||||
other.difference_since_genesis - self.difference_since_genesis
|
||||
}
|
||||
}
|
||||
|
||||
pub fn difference_against_legacy(
|
||||
&self,
|
||||
legacy_version: &semver::Version,
|
||||
) -> TotalVersionDifference {
|
||||
let current = self.semver_unchecked();
|
||||
let major_diff = (current.major as i64 - legacy_version.major as i64).unsigned_abs() as u32;
|
||||
let minor_diff = (current.minor as i64 - legacy_version.minor as i64).unsigned_abs() as u32;
|
||||
let patch_diff = (current.patch as i64 - legacy_version.patch as i64).unsigned_abs() as u32;
|
||||
let prerelease_diff = if current.pre == legacy_version.pre {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
};
|
||||
|
||||
let mut diff = TotalVersionDifference::default();
|
||||
// if there's a major increase, ignore minor and patch and treat it as 0
|
||||
if major_diff != 0 {
|
||||
diff.major += major_diff;
|
||||
return diff;
|
||||
}
|
||||
|
||||
// if there's a minor increase, ignore patch and treat is as 0
|
||||
if minor_diff != 0 {
|
||||
diff.minor += minor_diff;
|
||||
return diff;
|
||||
}
|
||||
|
||||
diff.patch = patch_diff;
|
||||
diff.prerelease = prerelease_diff;
|
||||
diff
|
||||
}
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
#[derive(Default, Copy, PartialOrd, Ord, Eq)]
|
||||
pub struct TotalVersionDifference {
|
||||
pub major: u32,
|
||||
pub minor: u32,
|
||||
pub patch: u32,
|
||||
pub prerelease: u32,
|
||||
}
|
||||
|
||||
impl Add for TotalVersionDifference {
|
||||
type Output = TotalVersionDifference;
|
||||
fn add(self, rhs: TotalVersionDifference) -> Self::Output {
|
||||
TotalVersionDifference {
|
||||
major: self.major.add(rhs.major),
|
||||
minor: self.minor.add(rhs.minor),
|
||||
patch: self.patch.add(rhs.patch),
|
||||
prerelease: self.prerelease.add(rhs.prerelease),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for TotalVersionDifference {
|
||||
type Output = TotalVersionDifference;
|
||||
fn sub(self, rhs: TotalVersionDifference) -> Self::Output {
|
||||
TotalVersionDifference {
|
||||
major: self.major.saturating_sub(rhs.major),
|
||||
minor: self.minor.saturating_sub(rhs.minor),
|
||||
patch: self.patch.saturating_sub(rhs.patch),
|
||||
prerelease: self.prerelease.saturating_sub(rhs.prerelease),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct HistoricalNymNodeVersionEntry {
|
||||
/// The unique, ordered, id of this particular entry
|
||||
pub id: u32,
|
||||
|
||||
/// Data associated with this particular version
|
||||
pub version_information: HistoricalNymNodeVersion,
|
||||
}
|
||||
|
||||
impl From<(u32, HistoricalNymNodeVersion)> for HistoricalNymNodeVersionEntry {
|
||||
fn from((id, version_information): (u32, HistoricalNymNodeVersion)) -> Self {
|
||||
HistoricalNymNodeVersionEntry {
|
||||
id,
|
||||
version_information,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for HistoricalNymNodeVersionEntry {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
// we only care about id for the purposes of ordering as they should have unique data
|
||||
self.id.partial_cmp(&other.id)
|
||||
}
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct NymNodeVersionHistoryResponse {
|
||||
pub history: Vec<HistoricalNymNodeVersionEntry>,
|
||||
|
||||
/// Field indicating paging information for the following queries if the caller wishes to get further entries.
|
||||
pub start_next_after: Option<u32>,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct CurrentNymNodeVersionResponse {
|
||||
pub version: Option<HistoricalNymNodeVersionEntry>,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct ConfigScoreParams {
|
||||
/// Defines weights for calculating numbers of versions behind the current release.
|
||||
pub version_weights: OutdatedVersionWeights,
|
||||
|
||||
/// Defines the parameters of the formula for calculating the version score
|
||||
pub version_score_formula_params: VersionScoreFormulaParams,
|
||||
}
|
||||
|
||||
/// Defines weights for calculating numbers of versions behind the current release.
|
||||
#[cw_serde]
|
||||
#[derive(Copy)]
|
||||
pub struct OutdatedVersionWeights {
|
||||
pub major: u32,
|
||||
pub minor: u32,
|
||||
pub patch: u32,
|
||||
pub prerelease: u32,
|
||||
}
|
||||
|
||||
fn is_one_semver_difference(this: &semver::Version, other: &semver::Version) -> bool {
|
||||
let major_diff = (this.major as i64 - other.major as i64).unsigned_abs() as u32;
|
||||
let minor_diff = (this.minor as i64 - other.minor as i64).unsigned_abs() as u32;
|
||||
let patch_diff = (this.patch as i64 - other.patch as i64).unsigned_abs() as u32;
|
||||
let prerelease_diff = if this.pre == other.pre { 0 } else { 1 };
|
||||
|
||||
if major_diff == 1 {
|
||||
return true;
|
||||
}
|
||||
|
||||
if major_diff == 0 && minor_diff == 1 {
|
||||
return true;
|
||||
}
|
||||
|
||||
if major_diff == 0 && minor_diff == 0 && patch_diff == 1 {
|
||||
return true;
|
||||
}
|
||||
|
||||
prerelease_diff == 1
|
||||
}
|
||||
|
||||
impl OutdatedVersionWeights {
|
||||
pub fn difference_to_versions_behind_factor(&self, diff: TotalVersionDifference) -> u32 {
|
||||
diff.major * self.major
|
||||
+ diff.minor * self.minor
|
||||
+ diff.patch * self.patch
|
||||
+ diff.prerelease * self.prerelease
|
||||
}
|
||||
|
||||
// INVARIANT: release chain is sorted
|
||||
// do NOT call this method directly from inside the contract. it's too inefficient
|
||||
// it relies on some external caching.
|
||||
pub fn versions_behind_factor(
|
||||
&self,
|
||||
node_version: &semver::Version,
|
||||
release_chain: &[HistoricalNymNodeVersionEntry],
|
||||
) -> u32 {
|
||||
let Some(latest) = release_chain.last() else {
|
||||
return 0;
|
||||
};
|
||||
|
||||
let latest_semver = latest.version_information.semver_unchecked();
|
||||
|
||||
// if you're more recent than the latest, you get the benefit of the doubt, the release might have not yet been commited to the chain
|
||||
// but only if you're only a single semver ahead, otherwise you get penalty equivalent of being major version behind for cheating
|
||||
if node_version > &latest_semver {
|
||||
return if is_one_semver_difference(node_version, &latest_semver) {
|
||||
0
|
||||
} else {
|
||||
self.major
|
||||
};
|
||||
}
|
||||
|
||||
// find your position in the release chain, if we fail, we assume that the node comes from before the changes were introduced
|
||||
// in which case we simply calculate the absolute difference between the genesis entry and add up the total difference
|
||||
let version_diff = match release_chain
|
||||
.iter()
|
||||
.rfind(|h| &h.version_information.semver_unchecked() <= node_version)
|
||||
{
|
||||
Some(h) => {
|
||||
// first chain entry that is smaller (or equal) to the provided node version
|
||||
// now, calculate the difference to the genesis version and ultimately against the current head
|
||||
let diff_since_genesis = if h.version_information.semver == node_version.to_string()
|
||||
{
|
||||
h.version_information.difference_since_genesis
|
||||
} else {
|
||||
h.version_information
|
||||
.cumulative_difference_since_genesis(node_version)
|
||||
};
|
||||
latest.version_information.difference_since_genesis - diff_since_genesis
|
||||
}
|
||||
None => {
|
||||
// SAFETY: since we managed to get 'last' entry, it means the release chain is not empty,
|
||||
// so we must be able to obtain the first entry
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let genesis = release_chain.first().unwrap();
|
||||
|
||||
let difference_from_genesis = genesis
|
||||
.version_information
|
||||
.difference_against_legacy(node_version);
|
||||
difference_from_genesis + latest.version_information.difference_since_genesis
|
||||
}
|
||||
};
|
||||
|
||||
self.difference_to_versions_behind_factor(version_diff)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for OutdatedVersionWeights {
|
||||
fn default() -> Self {
|
||||
OutdatedVersionWeights {
|
||||
major: 100,
|
||||
minor: 10,
|
||||
patch: 1,
|
||||
prerelease: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Given the formula of version_score = penalty ^ (versions_behind_factor ^ penalty_scaling)
|
||||
/// define the relevant parameters
|
||||
#[cw_serde]
|
||||
#[derive(Copy)]
|
||||
pub struct VersionScoreFormulaParams {
|
||||
pub penalty: Decimal,
|
||||
pub penalty_scaling: Decimal,
|
||||
}
|
||||
|
||||
impl Default for VersionScoreFormulaParams {
|
||||
fn default() -> Self {
|
||||
#[allow(clippy::unwrap_used)]
|
||||
VersionScoreFormulaParams {
|
||||
penalty: "0.995".parse().unwrap(),
|
||||
penalty_scaling: "1.65".parse().unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::ops::Deref;
|
||||
|
||||
// simple wrapper for tests
|
||||
struct ReleaseChain {
|
||||
inner: Vec<HistoricalNymNodeVersionEntry>,
|
||||
}
|
||||
|
||||
impl Deref for ReleaseChain {
|
||||
type Target = [HistoricalNymNodeVersionEntry];
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.inner.deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl ReleaseChain {
|
||||
fn new(initial: &str) -> Self {
|
||||
ReleaseChain {
|
||||
inner: vec![HistoricalNymNodeVersionEntry {
|
||||
id: 0,
|
||||
version_information: HistoricalNymNodeVersion {
|
||||
semver: initial.to_string(),
|
||||
introduced_at_height: 123,
|
||||
difference_since_genesis: TotalVersionDifference::default(),
|
||||
},
|
||||
}],
|
||||
}
|
||||
}
|
||||
|
||||
fn with_release(mut self, raw: &str) -> Self {
|
||||
self.push_new(raw);
|
||||
self
|
||||
}
|
||||
|
||||
fn push_new(&mut self, raw: &str) {
|
||||
let latest = self.inner.last().unwrap();
|
||||
let new_version: semver::Version = raw.parse().unwrap();
|
||||
|
||||
let new_absolute = latest
|
||||
.version_information
|
||||
.cumulative_difference_since_genesis(&new_version);
|
||||
|
||||
self.inner.push(HistoricalNymNodeVersionEntry {
|
||||
id: latest.id + 1,
|
||||
version_information: HistoricalNymNodeVersion {
|
||||
semver: new_version.to_string(),
|
||||
introduced_at_height: latest.version_information.introduced_at_height + 1,
|
||||
difference_since_genesis: new_absolute,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn versions_behind_factor() {
|
||||
// helper to compact the parsing
|
||||
fn s(raw: &str) -> semver::Version {
|
||||
raw.parse().unwrap()
|
||||
}
|
||||
|
||||
let weights = OutdatedVersionWeights::default();
|
||||
|
||||
// no releases:
|
||||
let res = weights.versions_behind_factor(&s("1.1.13"), &[]);
|
||||
assert_eq!(0, res);
|
||||
|
||||
// ###############################
|
||||
// single released version (1.1.13)
|
||||
// ###############################
|
||||
let mut release_chain = ReleaseChain::new("1.1.13");
|
||||
|
||||
// "legacy" versions
|
||||
let res = weights.versions_behind_factor(&s("1.0.12"), &release_chain);
|
||||
assert_eq!(10, res);
|
||||
let res = weights.versions_behind_factor(&s("1.0.4"), &release_chain);
|
||||
assert_eq!(10, res);
|
||||
let res = weights.versions_behind_factor(&s("1.0.1"), &release_chain);
|
||||
assert_eq!(10, res);
|
||||
let res = weights.versions_behind_factor(&s("0.1.12"), &release_chain);
|
||||
assert_eq!(100, res);
|
||||
|
||||
let res = weights.versions_behind_factor(&s("1.1.12"), &release_chain);
|
||||
assert_eq!(1, res);
|
||||
let res = weights.versions_behind_factor(&s("1.1.11"), &release_chain);
|
||||
assert_eq!(2, res);
|
||||
let res = weights.versions_behind_factor(&s("1.1.9"), &release_chain);
|
||||
assert_eq!(4, res);
|
||||
|
||||
// current version
|
||||
let res = weights.versions_behind_factor(&s("1.1.13"), &release_chain);
|
||||
assert_eq!(0, res);
|
||||
|
||||
// "ahead" versions
|
||||
let res = weights.versions_behind_factor(&s("1.1.14"), &release_chain);
|
||||
assert_eq!(0, res);
|
||||
let res = weights.versions_behind_factor(&s("1.2.0"), &release_chain);
|
||||
assert_eq!(0, res);
|
||||
let res = weights.versions_behind_factor(&s("2.0.0"), &release_chain);
|
||||
assert_eq!(0, res);
|
||||
|
||||
// cheating ahead:
|
||||
let res = weights.versions_behind_factor(&s("1.1.15"), &release_chain);
|
||||
assert_eq!(100, res);
|
||||
let res = weights.versions_behind_factor(&s("1.3.0"), &release_chain);
|
||||
assert_eq!(100, res);
|
||||
let res = weights.versions_behind_factor(&s("3.0.0"), &release_chain);
|
||||
assert_eq!(100, res);
|
||||
|
||||
// ###############################
|
||||
// small patch release chain (1.1.13 => 1.1.14 => 1.1.15 => 1.1.16)
|
||||
// ###############################
|
||||
release_chain.push_new("1.1.14");
|
||||
release_chain.push_new("1.1.15");
|
||||
release_chain.push_new("1.1.16");
|
||||
|
||||
// "legacy" versions
|
||||
let res = weights.versions_behind_factor(&s("1.0.12"), &release_chain);
|
||||
assert_eq!(13, res);
|
||||
let res = weights.versions_behind_factor(&s("1.0.4"), &release_chain);
|
||||
assert_eq!(13, res);
|
||||
let res = weights.versions_behind_factor(&s("1.0.1"), &release_chain);
|
||||
assert_eq!(13, res);
|
||||
let res = weights.versions_behind_factor(&s("0.1.12"), &release_chain);
|
||||
assert_eq!(103, res);
|
||||
|
||||
let res = weights.versions_behind_factor(&s("1.1.12"), &release_chain);
|
||||
assert_eq!(4, res);
|
||||
let res = weights.versions_behind_factor(&s("1.1.11"), &release_chain);
|
||||
assert_eq!(5, res);
|
||||
let res = weights.versions_behind_factor(&s("1.1.9"), &release_chain);
|
||||
assert_eq!(7, res);
|
||||
|
||||
// current version
|
||||
let res = weights.versions_behind_factor(&s("1.1.16"), &release_chain);
|
||||
assert_eq!(0, res);
|
||||
|
||||
// present in the chain
|
||||
let res = weights.versions_behind_factor(&s("1.1.15"), &release_chain);
|
||||
assert_eq!(1, res);
|
||||
let res = weights.versions_behind_factor(&s("1.1.14"), &release_chain);
|
||||
assert_eq!(2, res);
|
||||
let res = weights.versions_behind_factor(&s("1.1.13"), &release_chain);
|
||||
assert_eq!(3, res);
|
||||
|
||||
// "ahead" versions
|
||||
let res = weights.versions_behind_factor(&s("1.1.17"), &release_chain);
|
||||
assert_eq!(0, res);
|
||||
let res = weights.versions_behind_factor(&s("1.2.0"), &release_chain);
|
||||
assert_eq!(0, res);
|
||||
let res = weights.versions_behind_factor(&s("2.0.0"), &release_chain);
|
||||
assert_eq!(0, res);
|
||||
|
||||
// cheating ahead:
|
||||
let res = weights.versions_behind_factor(&s("1.1.18"), &release_chain);
|
||||
assert_eq!(100, res);
|
||||
let res = weights.versions_behind_factor(&s("1.3.0"), &release_chain);
|
||||
assert_eq!(100, res);
|
||||
let res = weights.versions_behind_factor(&s("3.0.0"), &release_chain);
|
||||
assert_eq!(100, res);
|
||||
|
||||
// ###############################
|
||||
// small minor release chain (1.2.0 => 1.3.0 => 1.4.0)
|
||||
// ###############################
|
||||
let release_chain = ReleaseChain::new("1.2.0")
|
||||
.with_release("1.3.0")
|
||||
.with_release("1.4.0");
|
||||
|
||||
// "legacy" versions
|
||||
let res = weights.versions_behind_factor(&s("1.0.12"), &release_chain);
|
||||
assert_eq!(40, res);
|
||||
let res = weights.versions_behind_factor(&s("1.0.4"), &release_chain);
|
||||
assert_eq!(40, res);
|
||||
let res = weights.versions_behind_factor(&s("1.0.1"), &release_chain);
|
||||
assert_eq!(40, res);
|
||||
let res = weights.versions_behind_factor(&s("0.1.12"), &release_chain);
|
||||
assert_eq!(120, res);
|
||||
|
||||
let res = weights.versions_behind_factor(&s("1.1.12"), &release_chain);
|
||||
assert_eq!(30, res);
|
||||
let res = weights.versions_behind_factor(&s("1.1.11"), &release_chain);
|
||||
assert_eq!(30, res);
|
||||
let res = weights.versions_behind_factor(&s("1.1.9"), &release_chain);
|
||||
assert_eq!(30, res);
|
||||
|
||||
// current version
|
||||
let res = weights.versions_behind_factor(&s("1.4.0"), &release_chain);
|
||||
assert_eq!(0, res);
|
||||
|
||||
// present in the chain
|
||||
let res = weights.versions_behind_factor(&s("1.2.0"), &release_chain);
|
||||
assert_eq!(20, res);
|
||||
let res = weights.versions_behind_factor(&s("1.3.0"), &release_chain);
|
||||
assert_eq!(10, res);
|
||||
|
||||
// weird in between
|
||||
let res = weights.versions_behind_factor(&s("1.2.1"), &release_chain);
|
||||
assert_eq!(20, res);
|
||||
let res = weights.versions_behind_factor(&s("1.3.3"), &release_chain);
|
||||
assert_eq!(10, res);
|
||||
|
||||
// "ahead" versions
|
||||
let res = weights.versions_behind_factor(&s("1.4.1"), &release_chain);
|
||||
assert_eq!(0, res);
|
||||
let res = weights.versions_behind_factor(&s("1.5.0"), &release_chain);
|
||||
assert_eq!(0, res);
|
||||
let res = weights.versions_behind_factor(&s("2.0.0"), &release_chain);
|
||||
assert_eq!(0, res);
|
||||
|
||||
// cheating ahead:
|
||||
let res = weights.versions_behind_factor(&s("1.4.2"), &release_chain);
|
||||
assert_eq!(100, res);
|
||||
let res = weights.versions_behind_factor(&s("1.6.0"), &release_chain);
|
||||
assert_eq!(100, res);
|
||||
let res = weights.versions_behind_factor(&s("3.0.0"), &release_chain);
|
||||
assert_eq!(100, res);
|
||||
|
||||
// ###############################
|
||||
// mixed release chain (1.1.13 => 1.2.0 => 1.2.1 => 1.3.0 => 1.3.1 => 1.3.2 => 1.4.0)
|
||||
// ###############################
|
||||
let release_chain = ReleaseChain::new("1.1.13")
|
||||
.with_release("1.2.0")
|
||||
.with_release("1.2.1")
|
||||
.with_release("1.3.0")
|
||||
.with_release("1.3.1-importantpre")
|
||||
.with_release("1.3.1")
|
||||
.with_release("1.3.2")
|
||||
.with_release("1.4.0");
|
||||
|
||||
// "legacy" versions
|
||||
let res = weights.versions_behind_factor(&s("1.0.12"), &release_chain);
|
||||
assert_eq!(44, res);
|
||||
let res = weights.versions_behind_factor(&s("1.0.4"), &release_chain);
|
||||
assert_eq!(44, res);
|
||||
let res = weights.versions_behind_factor(&s("1.0.1"), &release_chain);
|
||||
assert_eq!(44, res);
|
||||
let res = weights.versions_behind_factor(&s("0.1.12"), &release_chain);
|
||||
assert_eq!(134, res);
|
||||
|
||||
let res = weights.versions_behind_factor(&s("1.1.12"), &release_chain);
|
||||
assert_eq!(35, res);
|
||||
let res = weights.versions_behind_factor(&s("1.1.11"), &release_chain);
|
||||
assert_eq!(36, res);
|
||||
let res = weights.versions_behind_factor(&s("1.1.9"), &release_chain);
|
||||
assert_eq!(38, res);
|
||||
|
||||
// current version
|
||||
let res = weights.versions_behind_factor(&s("1.4.0"), &release_chain);
|
||||
assert_eq!(0, res);
|
||||
|
||||
// present in the chain
|
||||
let res = weights.versions_behind_factor(&s("1.1.13"), &release_chain);
|
||||
assert_eq!(34, res);
|
||||
let res = weights.versions_behind_factor(&s("1.2.0"), &release_chain);
|
||||
assert_eq!(24, res);
|
||||
let res = weights.versions_behind_factor(&s("1.2.1"), &release_chain);
|
||||
assert_eq!(23, res);
|
||||
let res = weights.versions_behind_factor(&s("1.3.0"), &release_chain);
|
||||
assert_eq!(13, res);
|
||||
let res = weights.versions_behind_factor(&s("1.3.1-importantpre"), &release_chain);
|
||||
assert_eq!(12, res);
|
||||
let res = weights.versions_behind_factor(&s("1.3.1"), &release_chain);
|
||||
assert_eq!(11, res);
|
||||
let res = weights.versions_behind_factor(&s("1.3.2"), &release_chain);
|
||||
assert_eq!(10, res);
|
||||
|
||||
// weird in between
|
||||
let res = weights.versions_behind_factor(&s("1.2.3"), &release_chain);
|
||||
assert_eq!(21, res);
|
||||
let res = weights.versions_behind_factor(&s("1.3.69"), &release_chain);
|
||||
assert_eq!(10, res);
|
||||
|
||||
// "ahead" versions
|
||||
let res = weights.versions_behind_factor(&s("1.4.1"), &release_chain);
|
||||
assert_eq!(0, res);
|
||||
let res = weights.versions_behind_factor(&s("1.5.0"), &release_chain);
|
||||
assert_eq!(0, res);
|
||||
let res = weights.versions_behind_factor(&s("2.0.0"), &release_chain);
|
||||
assert_eq!(0, res);
|
||||
|
||||
// cheating ahead:
|
||||
let res = weights.versions_behind_factor(&s("1.4.2"), &release_chain);
|
||||
assert_eq!(100, res);
|
||||
let res = weights.versions_behind_factor(&s("1.6.0"), &release_chain);
|
||||
assert_eq!(100, res);
|
||||
let res = weights.versions_behind_factor(&s("3.0.0"), &release_chain);
|
||||
assert_eq!(100, res);
|
||||
|
||||
// ###############################
|
||||
// skipped patch chain (1.1.13 => 1.2.0 => 1.2.1 => 1.2.4 => [1.3.0])
|
||||
// ###############################
|
||||
let mut release_chain = ReleaseChain::new("1.1.13")
|
||||
.with_release("1.2.0")
|
||||
.with_release("1.2.1")
|
||||
.with_release("1.2.4");
|
||||
|
||||
// current
|
||||
let res = weights.versions_behind_factor(&s("1.2.4"), &release_chain);
|
||||
assert_eq!(0, res);
|
||||
|
||||
// on 'skipped' version
|
||||
let res = weights.versions_behind_factor(&s("1.2.2"), &release_chain);
|
||||
assert_eq!(2, res);
|
||||
|
||||
// on version before the skip
|
||||
let res = weights.versions_behind_factor(&s("1.2.1"), &release_chain);
|
||||
assert_eq!(3, res);
|
||||
|
||||
release_chain.push_new("1.3.0");
|
||||
// current
|
||||
let res = weights.versions_behind_factor(&s("1.3.0"), &release_chain);
|
||||
assert_eq!(0, res);
|
||||
|
||||
// on 'skipped' version
|
||||
let res = weights.versions_behind_factor(&s("1.2.2"), &release_chain);
|
||||
assert_eq!(12, res);
|
||||
|
||||
// on version before the skip
|
||||
let res = weights.versions_behind_factor(&s("1.2.1"), &release_chain);
|
||||
assert_eq!(13, res);
|
||||
|
||||
// ###############################
|
||||
// skipped minor chain (1.1.13 => 1.2.0 => 1.2.1 => 1.4.0 => [1.5.0])
|
||||
// ###############################
|
||||
let mut release_chain = ReleaseChain::new("1.1.13")
|
||||
.with_release("1.2.0")
|
||||
.with_release("1.2.1")
|
||||
.with_release("1.4.0");
|
||||
|
||||
// current
|
||||
let res = weights.versions_behind_factor(&s("1.4.0"), &release_chain);
|
||||
assert_eq!(0, res);
|
||||
|
||||
// on 'skipped' version
|
||||
let res = weights.versions_behind_factor(&s("1.3.0"), &release_chain);
|
||||
assert_eq!(10, res);
|
||||
|
||||
// on version before the skip
|
||||
let res = weights.versions_behind_factor(&s("1.2.1"), &release_chain);
|
||||
assert_eq!(20, res);
|
||||
|
||||
let res = weights.versions_behind_factor(&s("1.2.0"), &release_chain);
|
||||
assert_eq!(21, res);
|
||||
|
||||
release_chain.push_new("1.5.0");
|
||||
|
||||
// current
|
||||
let res = weights.versions_behind_factor(&s("1.5.0"), &release_chain);
|
||||
assert_eq!(0, res);
|
||||
|
||||
let res = weights.versions_behind_factor(&s("1.4.0"), &release_chain);
|
||||
assert_eq!(10, res);
|
||||
|
||||
// on 'skipped' version
|
||||
let res = weights.versions_behind_factor(&s("1.3.0"), &release_chain);
|
||||
assert_eq!(20, res);
|
||||
|
||||
// on version before the skip
|
||||
let res = weights.versions_behind_factor(&s("1.2.1"), &release_chain);
|
||||
assert_eq!(30, res);
|
||||
|
||||
let res = weights.versions_behind_factor(&s("1.2.0"), &release_chain);
|
||||
assert_eq!(31, res);
|
||||
}
|
||||
}
|
||||
@@ -275,6 +275,9 @@ pub enum MixnetContractError {
|
||||
|
||||
#[error("the provided nym-node version is not a valid semver. got: {provided}")]
|
||||
InvalidNymNodeSemver { provided: String },
|
||||
|
||||
#[error("the provided nym-node version is not greater than the current one. got: {provided}. current: {current}")]
|
||||
NonIncreasingSemver { provided: String, current: String },
|
||||
}
|
||||
|
||||
impl MixnetContractError {
|
||||
|
||||
@@ -141,6 +141,7 @@ pub const NEW_INTERVAL_OPERATING_COST_RANGE_KEY: &str = "new_interval_operating_
|
||||
pub const NEW_VERSION_WEIGHTS_RANGE_KEY: &str = "new_version_weights_range";
|
||||
pub const NEW_VERSION_SCORE_FORMULA_PARAMS_KEY: &str = "new_version_score_formula_params";
|
||||
pub const NYM_NODE_CURRENT_SEMVER_KEY: &str = "new_current_semver";
|
||||
pub const NYM_NODE_CURRENT_SEMVER_ID_KEY: &str = "new_current_semver_id";
|
||||
|
||||
pub const OLD_REWARDING_VALIDATOR_ADDRESS_KEY: &str = "old_rewarding_validator_address";
|
||||
pub const NEW_REWARDING_VALIDATOR_ADDRESS_KEY: &str = "new_rewarding_validator_address";
|
||||
@@ -481,12 +482,6 @@ pub fn new_settings_update_event(update: &ContractStateParamsUpdate) -> Event {
|
||||
|
||||
// check for config score params updates
|
||||
if let Some(config_score_update) = &update.config_score_params {
|
||||
if let Some(current_nym_node_semver) = &config_score_update.current_nym_node_semver {
|
||||
event.attributes.push(attr(
|
||||
NYM_NODE_CURRENT_SEMVER_KEY,
|
||||
current_nym_node_semver.to_string(),
|
||||
))
|
||||
}
|
||||
if let Some(version_weights) = &config_score_update.version_weights {
|
||||
event.attributes.push(attr(
|
||||
NEW_VERSION_WEIGHTS_RANGE_KEY,
|
||||
@@ -506,9 +501,10 @@ pub fn new_settings_update_event(update: &ContractStateParamsUpdate) -> Event {
|
||||
event
|
||||
}
|
||||
|
||||
pub fn new_update_nym_node_semver_event(new_version: &str) -> Event {
|
||||
pub fn new_update_nym_node_semver_event(new_version: &str, new_id: u32) -> Event {
|
||||
Event::new(MixnetEventType::NymNodeSemverUpdate)
|
||||
.add_attribute(NYM_NODE_CURRENT_SEMVER_KEY, new_version)
|
||||
.add_attribute(NYM_NODE_CURRENT_SEMVER_ID_KEY, new_id.to_string())
|
||||
}
|
||||
|
||||
pub fn new_not_found_node_operator_rewarding_event(interval: Interval, node_id: NodeId) -> Event {
|
||||
|
||||
@@ -32,7 +32,7 @@ pub(crate) mod string_rfc3339_offset_date_time {
|
||||
|
||||
struct Rfc3339OffsetDateTimeVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for Rfc3339OffsetDateTimeVisitor {
|
||||
impl Visitor<'_> for Rfc3339OffsetDateTimeVisitor {
|
||||
type Value = OffsetDateTime;
|
||||
|
||||
fn expecting(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#![warn(clippy::unwrap_used)]
|
||||
#![warn(clippy::todo)]
|
||||
|
||||
mod config_score;
|
||||
pub mod constants;
|
||||
pub mod delegation;
|
||||
pub mod error;
|
||||
@@ -21,6 +22,7 @@ pub mod rewarding;
|
||||
pub mod signing_types;
|
||||
pub mod types;
|
||||
|
||||
pub use config_score::*;
|
||||
pub use constants::*;
|
||||
pub use contracts_common::types::*;
|
||||
pub use cosmwasm_std::{Addr, Coin, Decimal, Fraction};
|
||||
|
||||
@@ -25,6 +25,7 @@ use std::time::Duration;
|
||||
|
||||
#[cfg(feature = "schema")]
|
||||
use crate::{
|
||||
config_score::{CurrentNymNodeVersionResponse, NymNodeVersionHistoryResponse},
|
||||
delegation::{
|
||||
NodeDelegationResponse, PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse,
|
||||
PagedNodeDelegationsResponse,
|
||||
@@ -423,6 +424,20 @@ pub enum QueryMsg {
|
||||
#[cfg_attr(feature = "schema", returns(ContractState))]
|
||||
GetState {},
|
||||
|
||||
/// Get the current expected version of a Nym Node.
|
||||
#[cfg_attr(feature = "schema", returns(CurrentNymNodeVersionResponse))]
|
||||
GetCurrentNymNodeVersion {},
|
||||
|
||||
/// Get the version history of Nym Node.
|
||||
#[cfg_attr(feature = "schema", returns(NymNodeVersionHistoryResponse))]
|
||||
GetNymNodeVersionHistory {
|
||||
/// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.
|
||||
limit: Option<u32>,
|
||||
|
||||
/// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.
|
||||
start_after: Option<u32>,
|
||||
},
|
||||
|
||||
/// Gets the current parameters used for reward calculation.
|
||||
#[cfg_attr(feature = "schema", returns(RewardingParams))]
|
||||
GetRewardingParams {},
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config_score::{ConfigScoreParams, OutdatedVersionWeights, VersionScoreFormulaParams};
|
||||
use crate::nym_node::Role;
|
||||
use contracts_common::Percent;
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use cosmwasm_std::Coin;
|
||||
use cosmwasm_std::{Addr, Uint128};
|
||||
use cosmwasm_std::{Coin, Decimal};
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
// type aliases for better reasoning about available data
|
||||
@@ -221,96 +222,14 @@ impl OperatorsParamsUpdate {
|
||||
}
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct ConfigScoreParams {
|
||||
/// Current version of the nym node that is going to be used for determining the version score of a node.
|
||||
/// note: value stored here is pre-validated `semver::Version`
|
||||
pub current_nym_node_semver: String,
|
||||
|
||||
/// Defines weights for calculating numbers of versions behind the current release.
|
||||
pub version_weights: OutdatedVersionWeights,
|
||||
|
||||
/// Defines the parameters of the formula for calculating the version score
|
||||
pub version_score_formula_params: VersionScoreFormulaParams,
|
||||
}
|
||||
|
||||
impl ConfigScoreParams {
|
||||
// SAFETY: the value stored in the contract is always valid
|
||||
#[allow(clippy::unwrap_used)]
|
||||
pub fn unchecked_nym_node_version(&self) -> semver::Version {
|
||||
self.current_nym_node_semver.parse().unwrap()
|
||||
}
|
||||
|
||||
pub fn versions_behind(&self, node_semver: &semver::Version) -> u32 {
|
||||
let expected = self.unchecked_nym_node_version();
|
||||
|
||||
let major_diff = (node_semver.major as i64 - expected.major as i64).unsigned_abs() as u32;
|
||||
let minor_diff = (node_semver.minor as i64 - expected.minor as i64).unsigned_abs() as u32;
|
||||
let patch_diff = (node_semver.patch as i64 - expected.patch as i64).unsigned_abs() as u32;
|
||||
let prerelease_diff = if node_semver.pre == expected.pre {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
};
|
||||
|
||||
major_diff * self.version_weights.major
|
||||
+ minor_diff * self.version_weights.minor
|
||||
+ patch_diff * self.version_weights.patch
|
||||
+ prerelease_diff * self.version_weights.prerelease
|
||||
}
|
||||
}
|
||||
|
||||
/// Defines weights for calculating numbers of versions behind the current release.
|
||||
#[cw_serde]
|
||||
#[derive(Copy)]
|
||||
pub struct OutdatedVersionWeights {
|
||||
pub major: u32,
|
||||
pub minor: u32,
|
||||
pub patch: u32,
|
||||
pub prerelease: u32,
|
||||
}
|
||||
|
||||
impl Default for OutdatedVersionWeights {
|
||||
fn default() -> Self {
|
||||
OutdatedVersionWeights {
|
||||
major: 100,
|
||||
minor: 10,
|
||||
patch: 1,
|
||||
prerelease: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Given the formula of version_score = penalty ^ (num_versions_behind ^ penalty_scaling)
|
||||
/// define the relevant parameters
|
||||
#[cw_serde]
|
||||
#[derive(Copy)]
|
||||
pub struct VersionScoreFormulaParams {
|
||||
pub penalty: Decimal,
|
||||
pub penalty_scaling: Decimal,
|
||||
}
|
||||
|
||||
impl Default for VersionScoreFormulaParams {
|
||||
fn default() -> Self {
|
||||
#[allow(clippy::unwrap_used)]
|
||||
VersionScoreFormulaParams {
|
||||
penalty: "0.8".parse().unwrap(),
|
||||
penalty_scaling: "2.0".parse().unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct ConfigScoreParamsUpdate {
|
||||
pub current_nym_node_semver: Option<String>,
|
||||
pub version_weights: Option<OutdatedVersionWeights>,
|
||||
pub version_score_formula_params: Option<VersionScoreFormulaParams>,
|
||||
}
|
||||
|
||||
impl ConfigScoreParamsUpdate {
|
||||
pub fn contains_updates(&self) -> bool {
|
||||
self.current_nym_node_semver.is_some()
|
||||
|| self.version_weights.is_some()
|
||||
|| self.version_score_formula_params.is_some()
|
||||
self.version_weights.is_some() || self.version_score_formula_params.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ impl<S: Storage + Clone + 'static> BandwidthStorageManager<S> {
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip_all)]
|
||||
async fn sync_storage_bandwidth(&mut self) -> Result<()> {
|
||||
pub async fn sync_storage_bandwidth(&mut self) -> Result<()> {
|
||||
trace!("syncing client bandwidth with the underlying storage");
|
||||
let updated = self
|
||||
.storage
|
||||
|
||||
@@ -8,8 +8,8 @@ use std::time::Duration;
|
||||
use time::OffsetDateTime;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
const DEFAULT_CLIENT_BANDWIDTH_MAX_FLUSHING_RATE: Duration = Duration::from_millis(5);
|
||||
const DEFAULT_CLIENT_BANDWIDTH_MAX_DELTA_FLUSHING_AMOUNT: i64 = 512 * 1024; // 512kB
|
||||
const DEFAULT_CLIENT_BANDWIDTH_MAX_FLUSHING_RATE: Duration = Duration::from_secs(5 * 60); // 5 minutes
|
||||
const DEFAULT_CLIENT_BANDWIDTH_MAX_DELTA_FLUSHING_AMOUNT: i64 = 5 * 1024 * 1024; // 5MB
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct BandwidthFlushingBehaviourConfig {
|
||||
|
||||
@@ -18,7 +18,7 @@ use nym_validator_client::nym_api::EpochId;
|
||||
use nym_validator_client::nyxd::contract_traits::{
|
||||
EcashSigningClient, MultisigQueryClient, MultisigSigningClient, PagedMultisigQueryClient,
|
||||
};
|
||||
use nym_validator_client::nyxd::cosmwasm_client::ToSingletonContractData;
|
||||
use nym_validator_client::nyxd::cosmwasm_client::ContractResponseData;
|
||||
use nym_validator_client::nyxd::cw3::Status;
|
||||
use nym_validator_client::nyxd::AccountId;
|
||||
use nym_validator_client::EcashApiClient;
|
||||
|
||||
@@ -6,6 +6,7 @@ use ed25519_dalek::{Signer, SigningKey};
|
||||
pub use ed25519_dalek::{Verifier, PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, SIGNATURE_LENGTH};
|
||||
use nym_pemstore::traits::{PemStorableKey, PemStorableKeyPair};
|
||||
use std::fmt::{self, Debug, Display, Formatter};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::str::FromStr;
|
||||
use thiserror::Error;
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
@@ -122,6 +123,14 @@ impl PemStorableKeyPair for KeyPair {
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub struct PublicKey(ed25519_dalek::VerifyingKey);
|
||||
|
||||
impl Hash for PublicKey {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
// each public key has unique bytes representation which can be used
|
||||
// for the hash implementation
|
||||
self.to_bytes().hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for PublicKey {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
Display::fmt(&self.to_base58_string(), f)
|
||||
|
||||
@@ -26,9 +26,8 @@ const PARALLEL_RUNS: usize = 32;
|
||||
/// `lambda` ($\lambda$) in the DKG paper
|
||||
const SECURITY_PARAMETER: usize = 256;
|
||||
|
||||
// note: ceiling in integer division can be achieved via q = (x + y - 1) / y;
|
||||
/// ceil(SECURITY_PARAMETER / PARALLEL_RUNS) in the paper
|
||||
const NUM_CHALLENGE_BITS: usize = (SECURITY_PARAMETER + PARALLEL_RUNS - 1) / PARALLEL_RUNS;
|
||||
const NUM_CHALLENGE_BITS: usize = SECURITY_PARAMETER.div_ceil(PARALLEL_RUNS);
|
||||
|
||||
// type alias for ease of use
|
||||
type FirstChallenge = Vec<Vec<Vec<u64>>>;
|
||||
|
||||
@@ -196,7 +196,7 @@ impl<'b> Add<&'b Polynomial> for Polynomial {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Add<Polynomial> for &'a Polynomial {
|
||||
impl Add<Polynomial> for &Polynomial {
|
||||
type Output = Polynomial;
|
||||
|
||||
fn add(self, rhs: Polynomial) -> Polynomial {
|
||||
@@ -212,10 +212,10 @@ impl Add<Polynomial> for Polynomial {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Add<&'b Polynomial> for &'a Polynomial {
|
||||
impl<'a> Add<&'a Polynomial> for &Polynomial {
|
||||
type Output = Polynomial;
|
||||
|
||||
fn add(self, rhs: &'b Polynomial) -> Self::Output {
|
||||
fn add(self, rhs: &'a Polynomial) -> Self::Output {
|
||||
let len = self.coefficients.len();
|
||||
let rhs_len = rhs.coefficients.len();
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ pub struct GatewayHandshake<'a> {
|
||||
handshake_future: BoxFuture<'a, Result<SharedGatewayKey, HandshakeError>>,
|
||||
}
|
||||
|
||||
impl<'a> Future for GatewayHandshake<'a> {
|
||||
impl Future for GatewayHandshake<'_> {
|
||||
type Output = Result<SharedGatewayKey, HandshakeError>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
|
||||
@@ -5,8 +5,9 @@ use std::fmt;
|
||||
|
||||
use http::HeaderValue;
|
||||
use nym_bin_common::build_information::{BinaryBuildInformation, BinaryBuildInformationOwned};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct UserAgent {
|
||||
pub application: String,
|
||||
pub version: String,
|
||||
|
||||
@@ -15,12 +15,15 @@ axum-client-ip.workspace = true
|
||||
axum.workspace = true
|
||||
bytes = { workspace = true }
|
||||
colored.workspace = true
|
||||
futures = { workspace = true }
|
||||
mime = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json.workspace = true
|
||||
serde_yaml = { workspace = true }
|
||||
tower = { workspace = true }
|
||||
tracing.workspace = true
|
||||
utoipa = { workspace = true, optional = true }
|
||||
zeroize = { workspace = true }
|
||||
|
||||
[features]
|
||||
utoipa = ["dep:utoipa"]
|
||||
|
||||
@@ -7,7 +7,7 @@ use axum::Json;
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub mod logging;
|
||||
pub mod middleware;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use axum::http::{header, HeaderValue, StatusCode};
|
||||
use axum::response::IntoResponse;
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use axum::extract::Request;
|
||||
use axum::http::header::{HOST, USER_AGENT};
|
||||
@@ -11,6 +11,7 @@ use colored::Colorize;
|
||||
use std::time::Instant;
|
||||
use tracing::info;
|
||||
|
||||
/// Simple logger for requests
|
||||
pub async fn logger(
|
||||
InsecureClientIp(addr): InsecureClientIp,
|
||||
request: Request,
|
||||
+2
-3
@@ -1,6 +1,5 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub trait State: Clone {
|
||||
//
|
||||
}
|
||||
pub mod bearer_auth;
|
||||
pub mod logging;
|
||||
@@ -6,7 +6,6 @@ use crate::verloc::sender::{PacketSender, TestedNode};
|
||||
use futures::stream::FuturesUnordered;
|
||||
use futures::StreamExt;
|
||||
use log::*;
|
||||
use nym_bin_common::version_checker::{self, parse_version};
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_network_defaults::mainnet::NYM_API;
|
||||
use nym_node_http_api::state::metrics::{SharedVerlocStats, VerlocNodeResult};
|
||||
@@ -30,9 +29,6 @@ pub(crate) mod measurement;
|
||||
pub(crate) mod packet;
|
||||
pub(crate) mod sender;
|
||||
|
||||
// TODO: MUST BE UPDATED BEFORE ACTUAL RELEASE!!
|
||||
pub const MINIMUM_NODE_VERSION: &str = "0.10.1";
|
||||
|
||||
// by default all of those are overwritten by config data from mixnodes directly
|
||||
const DEFAULT_VERLOC_PORT: u16 = 1790;
|
||||
const DEFAULT_PACKETS_PER_NODE: usize = 100;
|
||||
@@ -45,9 +41,6 @@ const DEFAULT_RETRY_TIMEOUT: Duration = Duration::from_secs(60 * 30);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Config {
|
||||
/// Minimum semver version of a node (gateway or mixnode) that is capable of replying to echo packets.
|
||||
minimum_compatible_node_version: version_checker::Version,
|
||||
|
||||
/// Socket address of this node on which it will be listening for the measurement packets.
|
||||
listening_address: SocketAddr,
|
||||
|
||||
@@ -91,11 +84,6 @@ impl ConfigBuilder {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn minimum_compatible_node_version(mut self, version: version_checker::Version) -> Self {
|
||||
self.0.minimum_compatible_node_version = version;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn listening_address(mut self, listening_address: SocketAddr) -> Self {
|
||||
self.0.listening_address = listening_address;
|
||||
self
|
||||
@@ -154,7 +142,6 @@ impl ConfigBuilder {
|
||||
impl Default for ConfigBuilder {
|
||||
fn default() -> Self {
|
||||
ConfigBuilder(Config {
|
||||
minimum_compatible_node_version: parse_version(MINIMUM_NODE_VERSION).unwrap(),
|
||||
listening_address: format!("[::]:{DEFAULT_VERLOC_PORT}").parse().unwrap(),
|
||||
packets_per_node: DEFAULT_PACKETS_PER_NODE,
|
||||
packet_timeout: DEFAULT_PACKET_TIMEOUT,
|
||||
|
||||
@@ -29,6 +29,9 @@ pub struct NodeTester<R> {
|
||||
|
||||
packet_size: PacketSize,
|
||||
|
||||
/// Specify whether route selection should be determined by the packet header.
|
||||
deterministic_route_selection: bool,
|
||||
|
||||
/// Average delay a data packet is going to get delay at a single mixnode.
|
||||
average_packet_delay: Duration,
|
||||
|
||||
@@ -48,11 +51,13 @@ impl<R> NodeTester<R>
|
||||
where
|
||||
R: Rng + CryptoRng,
|
||||
{
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
rng: R,
|
||||
base_topology: NymTopology,
|
||||
self_address: Option<Recipient>,
|
||||
packet_size: PacketSize,
|
||||
deterministic_route_selection: bool,
|
||||
average_packet_delay: Duration,
|
||||
average_ack_delay: Duration,
|
||||
ack_key: Arc<AckKey>,
|
||||
@@ -62,6 +67,7 @@ where
|
||||
base_topology,
|
||||
self_address,
|
||||
packet_size,
|
||||
deterministic_route_selection,
|
||||
average_packet_delay,
|
||||
average_ack_delay,
|
||||
num_mix_hops: DEFAULT_NUM_MIX_HOPS,
|
||||
@@ -289,10 +295,18 @@ where
|
||||
impl<R: CryptoRng + Rng> FragmentPreparer for NodeTester<R> {
|
||||
type Rng = R;
|
||||
|
||||
fn deterministic_route_selection(&self) -> bool {
|
||||
self.deterministic_route_selection
|
||||
}
|
||||
|
||||
fn rng(&mut self) -> &mut Self::Rng {
|
||||
&mut self.rng
|
||||
}
|
||||
|
||||
fn nonce(&self) -> i32 {
|
||||
1
|
||||
}
|
||||
|
||||
fn num_mix_hops(&self) -> u8 {
|
||||
self.num_mix_hops
|
||||
}
|
||||
@@ -304,8 +318,4 @@ impl<R: CryptoRng + Rng> FragmentPreparer for NodeTester<R> {
|
||||
fn average_ack_delay(&self) -> Duration {
|
||||
self.average_ack_delay
|
||||
}
|
||||
|
||||
fn nonce(&self) -> i32 {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
[package]
|
||||
name = "nym-tui-common"
|
||||
version = "0.1.0"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
humantime-serde = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
tokio = { workspace = true, features = ["sync", "rt-multi-thread", "signal", "macros"] }
|
||||
tokio-stream = { workspace = true }
|
||||
tokio-util = { workspace = true, features = ["rt"] }
|
||||
tracing = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
# tui
|
||||
crossterm = { workspace = true, features = ["event-stream"] }
|
||||
ratatui = { workspace = true, features = ["serde", "macros", "crossterm"] }
|
||||
tui-logger = { workspace = true, optional = true, features = ["tracing-support"], default-features = false }
|
||||
|
||||
# panic handlers
|
||||
better-panic = { workspace = true }
|
||||
color-eyre = { workspace = true }
|
||||
strip-ansi-escapes = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
tracing-subscriber = { workspace = true, features = ["env-filter"] }
|
||||
tracing = { workspace = true, features = ["log"] }
|
||||
|
||||
[[example]]
|
||||
name = "hello_world"
|
||||
required-features = ["logger"]
|
||||
|
||||
|
||||
|
||||
|
||||
[features]
|
||||
logger = ["tui-logger"]
|
||||
@@ -1,175 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use async_trait::async_trait;
|
||||
use color_eyre::eyre;
|
||||
use crossterm::event::{KeyCode, KeyEvent};
|
||||
use nym_tui_common::tui::config::keybindings::{KeyBinding, LoggerKeybindings};
|
||||
use nym_tui_common::{
|
||||
run_tui, Action, ActionDispatcher, ActionSender, AppAction, Component, DebugHistory, Logger,
|
||||
LoggerProps, State,
|
||||
};
|
||||
use ratatui::layout::{Layout, Rect};
|
||||
use ratatui::prelude::Constraint;
|
||||
use ratatui::widgets::Paragraph;
|
||||
use ratatui::Frame;
|
||||
use tracing::log::LevelFilter;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
// useful pattern for tabs, etc:
|
||||
/*
|
||||
fn get_active_page_component_mut(&mut self) -> &mut dyn Component {
|
||||
match self.props.active_tab {
|
||||
ActiveTab::Tab1 => &mut self.tab1,
|
||||
ActiveTab::Tab2 => &mut self.tab2,
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
struct Props {
|
||||
custom_quit: KeyBinding,
|
||||
}
|
||||
|
||||
impl From<&HelloStore> for Props {
|
||||
fn from(store: &HelloStore) -> Self {
|
||||
Props {
|
||||
custom_quit: store.config.custom_quit,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HelloRootApp {
|
||||
props: Props,
|
||||
|
||||
action_sender: ActionSender<HelloActions>,
|
||||
logger: Logger<HelloStore, HelloActions>,
|
||||
debug_history: DebugHistory<HelloStore, HelloActions>,
|
||||
}
|
||||
|
||||
impl Component for HelloRootApp {
|
||||
type State = HelloStore;
|
||||
type Actions = HelloActions;
|
||||
|
||||
fn new(state: &HelloStore, action_sender: ActionSender<HelloActions>) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
HelloRootApp {
|
||||
props: Props::from(state),
|
||||
action_sender: action_sender.clone(),
|
||||
logger: Logger::new(state, action_sender.clone()),
|
||||
debug_history: DebugHistory::new(state, action_sender),
|
||||
}
|
||||
}
|
||||
|
||||
fn update(self, state: &Self::State) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
HelloRootApp {
|
||||
logger: self.logger.update(state),
|
||||
debug_history: self.debug_history.update(state),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
fn tick(&mut self) -> bool {
|
||||
let logger_tick = self.logger.tick();
|
||||
let debug_history_tick = self.debug_history.tick();
|
||||
|
||||
logger_tick || debug_history_tick
|
||||
}
|
||||
|
||||
fn handle_key(&mut self, key: KeyEvent) -> eyre::Result<()> {
|
||||
let maybe_binding = KeyBinding::from(key);
|
||||
if maybe_binding == self.props.custom_quit {
|
||||
self.action_sender.send(Action::Quit);
|
||||
}
|
||||
|
||||
self.logger.handle_key(key)?;
|
||||
self.debug_history.handle_key(key)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn view(&mut self, frame: &mut Frame, rect: Rect) {
|
||||
let [logs, hello_rect] =
|
||||
Layout::vertical([Constraint::Percentage(50), Constraint::Percentage(50)]).areas(rect);
|
||||
|
||||
self.logger.view(frame, logs);
|
||||
|
||||
frame.render_widget(Paragraph::new("Hello world!").centered(), hello_rect);
|
||||
|
||||
self.debug_history.view(frame, rect);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum HelloActions {}
|
||||
|
||||
impl AppAction for HelloActions {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct HelloConfig {
|
||||
pub custom_quit: KeyBinding,
|
||||
pub logger_keybindings: LoggerKeybindings,
|
||||
}
|
||||
|
||||
impl Default for HelloConfig {
|
||||
fn default() -> Self {
|
||||
HelloConfig {
|
||||
custom_quit: KeyBinding::new(KeyCode::Char('x')),
|
||||
logger_keybindings: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct HelloStore {
|
||||
config: HelloConfig,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a HelloStore> for LoggerProps {
|
||||
fn from(store: &'a HelloStore) -> LoggerProps {
|
||||
LoggerProps {
|
||||
keybindings: store.config.logger_keybindings,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl State for HelloStore {}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct HelloDispatcher {}
|
||||
|
||||
#[async_trait]
|
||||
impl ActionDispatcher for HelloDispatcher {
|
||||
type Store = HelloStore;
|
||||
type Actions = HelloActions;
|
||||
|
||||
async fn handle_app_action(
|
||||
&mut self,
|
||||
action: Self::Actions,
|
||||
store: &mut Self::Store,
|
||||
) -> eyre::Result<()> {
|
||||
let _ = action;
|
||||
let _ = store;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> eyre::Result<()> {
|
||||
use tracing_subscriber::layer::SubscriberExt;
|
||||
use tracing_subscriber::util::SubscriberInitExt;
|
||||
use tracing_subscriber::Layer;
|
||||
|
||||
let filter: EnvFilter = "trace,mio=warn".parse()?;
|
||||
|
||||
tracing_subscriber::registry()
|
||||
.with(tui_logger::tracing_subscriber_layer().with_filter(filter))
|
||||
.init();
|
||||
tui_logger::init_logger(LevelFilter::Trace)?;
|
||||
|
||||
run_tui::<HelloRootApp, _>(Default::default(), HelloDispatcher {}, Default::default()).await
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::char::ParseCharError;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum NymTuiError {
|
||||
#[error("failed to abort tui processing task within specified duration")]
|
||||
TuiTaskAbortFailure,
|
||||
|
||||
#[error("{str} could not be parsed into a character: {source}")]
|
||||
InvalidCharacter {
|
||||
str: String,
|
||||
#[source]
|
||||
source: ParseCharError,
|
||||
},
|
||||
|
||||
#[error("could not process an unknown keybinding: '{value}'")]
|
||||
UnknownKeyBinding { value: String },
|
||||
|
||||
#[error("could not process an unknown key modifier: '{value}'")]
|
||||
UnknownKeyModifier { value: String },
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#![warn(clippy::expect_used)]
|
||||
#![warn(clippy::unwrap_used)]
|
||||
#![warn(clippy::todo)]
|
||||
#![warn(clippy::dbg_macro)]
|
||||
#![warn(clippy::panic)]
|
||||
|
||||
use crate::tui::manager::TuiManager;
|
||||
use color_eyre::eyre;
|
||||
|
||||
pub mod error;
|
||||
pub mod tui;
|
||||
|
||||
pub use crate::tui::config::TuiConfig;
|
||||
pub use tui::action::{Action, AppAction};
|
||||
pub use tui::dispatcher::store::State;
|
||||
pub use tui::dispatcher::{ActionDispatcher, ActionSender};
|
||||
pub use tui::initialize_panic_handler;
|
||||
pub use tui::ui::components::Component;
|
||||
|
||||
// components:
|
||||
pub use tui::ui::components::common::DebugHistory;
|
||||
#[cfg(feature = "logger")]
|
||||
pub use tui::ui::components::common::{Logger, LoggerProps};
|
||||
|
||||
pub async fn run_tui<C, D>(
|
||||
config: TuiConfig,
|
||||
action_dispatcher: D,
|
||||
initial_state: D::Store,
|
||||
) -> eyre::Result<()>
|
||||
where
|
||||
C: Component + Send + Sync + 'static,
|
||||
C::State: Send + Sync + 'static,
|
||||
C::Actions: Send + Sync + Clone + 'static,
|
||||
D: ActionDispatcher<Store = C::State, Actions = C::Actions> + Send + Sync + 'static,
|
||||
{
|
||||
initialize_panic_handler()?;
|
||||
|
||||
TuiManager::<C>::build_new(config, action_dispatcher, initial_state)?
|
||||
.wait_for_exit_or_signal()
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Action<T: AppAction> {
|
||||
Quit,
|
||||
|
||||
AppDefined(T),
|
||||
}
|
||||
|
||||
pub trait AppAction: Debug {}
|
||||
@@ -1,271 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::str::FromStr;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use crate::error::NymTuiError;
|
||||
#[cfg(feature = "logger")]
|
||||
use tui_logger::TuiWidgetEvent;
|
||||
|
||||
static KEY_MODIFIERS: LazyLock<HashMap<&'static str, KeyModifiers>> = LazyLock::new(|| {
|
||||
let mut m = HashMap::new();
|
||||
m.insert("shift", KeyModifiers::SHIFT);
|
||||
m.insert("ctrl", KeyModifiers::CONTROL);
|
||||
m.insert("alt", KeyModifiers::ALT);
|
||||
m.insert("super", KeyModifiers::SUPER);
|
||||
m.insert("hyper", KeyModifiers::HYPER);
|
||||
m.insert("meta", KeyModifiers::META);
|
||||
m
|
||||
});
|
||||
|
||||
static SPECIAL_KEYS: LazyLock<HashMap<&'static str, KeyCode>> = LazyLock::new(|| {
|
||||
let mut m = HashMap::new();
|
||||
m.insert("backspace", KeyCode::Backspace);
|
||||
m.insert("enter", KeyCode::Enter);
|
||||
m.insert("left", KeyCode::Left);
|
||||
m.insert("right", KeyCode::Right);
|
||||
m.insert("up", KeyCode::Up);
|
||||
m.insert("down", KeyCode::Down);
|
||||
m.insert("home", KeyCode::Home);
|
||||
m.insert("end", KeyCode::End);
|
||||
m.insert("pageup", KeyCode::PageUp);
|
||||
m.insert("pagedown", KeyCode::PageDown);
|
||||
m.insert("tab", KeyCode::Tab);
|
||||
m.insert("backtab", KeyCode::BackTab);
|
||||
m.insert("delete", KeyCode::Delete);
|
||||
m.insert("insert", KeyCode::Insert);
|
||||
m.insert("null", KeyCode::Null);
|
||||
m.insert("esc", KeyCode::Esc);
|
||||
m.insert("space", KeyCode::Char(' '));
|
||||
m.insert("f1", KeyCode::F(1));
|
||||
m.insert("f2", KeyCode::F(2));
|
||||
m.insert("f3", KeyCode::F(3));
|
||||
m.insert("f4", KeyCode::F(4));
|
||||
m.insert("f5", KeyCode::F(5));
|
||||
m.insert("f6", KeyCode::F(6));
|
||||
m.insert("f7", KeyCode::F(7));
|
||||
m.insert("f8", KeyCode::F(8));
|
||||
m.insert("f9", KeyCode::F(9));
|
||||
m.insert("f10", KeyCode::F(10));
|
||||
m.insert("f11", KeyCode::F(11));
|
||||
m.insert("f12", KeyCode::F(12));
|
||||
m
|
||||
});
|
||||
|
||||
#[cfg(feature = "logger")]
|
||||
#[derive(Serialize, Deserialize, Clone, Copy, Debug)]
|
||||
#[serde(rename_all = "kebab-case", deny_unknown_fields, default)]
|
||||
pub struct LoggerKeybindings {
|
||||
// TODO: give them better names
|
||||
tui_logger_space_key: KeyBinding,
|
||||
tui_logger_up_key: KeyBinding,
|
||||
tui_logger_down_key: KeyBinding,
|
||||
tui_logger_left_key: KeyBinding,
|
||||
tui_logger_right_key: KeyBinding,
|
||||
tui_logger_plus_key: KeyBinding,
|
||||
tui_logger_minus_key: KeyBinding,
|
||||
tui_logger_hide_key: KeyBinding,
|
||||
tui_logger_focus_key: KeyBinding,
|
||||
tui_logger_prev_page_key: KeyBinding,
|
||||
tui_logger_next_page_key: KeyBinding,
|
||||
tui_logger_escape_key: KeyBinding,
|
||||
}
|
||||
|
||||
#[cfg(feature = "logger")]
|
||||
impl Default for LoggerKeybindings {
|
||||
fn default() -> Self {
|
||||
LoggerKeybindings {
|
||||
tui_logger_space_key: KeyBinding::new(KeyCode::Char(' ')),
|
||||
tui_logger_up_key: KeyBinding::new(KeyCode::Up),
|
||||
tui_logger_down_key: KeyBinding::new(KeyCode::Down),
|
||||
tui_logger_left_key: KeyBinding::new(KeyCode::Left),
|
||||
tui_logger_right_key: KeyBinding::new(KeyCode::Right),
|
||||
tui_logger_plus_key: KeyBinding::new(KeyCode::Char('+')),
|
||||
tui_logger_minus_key: KeyBinding::new(KeyCode::Char('-')),
|
||||
tui_logger_hide_key: KeyBinding::new(KeyCode::Char('h')),
|
||||
tui_logger_focus_key: KeyBinding::new(KeyCode::Char('f')),
|
||||
tui_logger_prev_page_key: KeyBinding::new(KeyCode::PageUp),
|
||||
tui_logger_next_page_key: KeyBinding::new(KeyCode::PageDown),
|
||||
tui_logger_escape_key: KeyBinding::new(KeyCode::Esc),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "logger")]
|
||||
impl LoggerKeybindings {
|
||||
pub fn tui_logger_event(&self, key: KeyBinding) -> Option<TuiWidgetEvent> {
|
||||
if key == self.tui_logger_space_key {
|
||||
Some(TuiWidgetEvent::SpaceKey)
|
||||
} else if key == self.tui_logger_up_key {
|
||||
Some(TuiWidgetEvent::UpKey)
|
||||
} else if key == self.tui_logger_down_key {
|
||||
Some(TuiWidgetEvent::DownKey)
|
||||
} else if key == self.tui_logger_left_key {
|
||||
Some(TuiWidgetEvent::LeftKey)
|
||||
} else if key == self.tui_logger_right_key {
|
||||
Some(TuiWidgetEvent::RightKey)
|
||||
} else if key == self.tui_logger_plus_key {
|
||||
Some(TuiWidgetEvent::PlusKey)
|
||||
} else if key == self.tui_logger_minus_key {
|
||||
Some(TuiWidgetEvent::MinusKey)
|
||||
} else if key == self.tui_logger_hide_key {
|
||||
Some(TuiWidgetEvent::HideKey)
|
||||
} else if key == self.tui_logger_focus_key {
|
||||
Some(TuiWidgetEvent::FocusKey)
|
||||
} else if key == self.tui_logger_prev_page_key {
|
||||
Some(TuiWidgetEvent::PrevPageKey)
|
||||
} else if key == self.tui_logger_next_page_key {
|
||||
Some(TuiWidgetEvent::NextPageKey)
|
||||
} else if key == self.tui_logger_escape_key {
|
||||
Some(TuiWidgetEvent::EscapeKey)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
#[serde(try_from = "String")]
|
||||
#[serde(into = "String")]
|
||||
pub struct KeyBinding {
|
||||
pub code: KeyCode,
|
||||
pub modifier: KeyModifiers,
|
||||
}
|
||||
|
||||
impl KeyBinding {
|
||||
pub fn new(code: KeyCode) -> Self {
|
||||
KeyBinding {
|
||||
code,
|
||||
modifier: KeyModifiers::NONE,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_modifier(code: KeyCode, modifier: KeyModifiers) -> Self {
|
||||
KeyBinding { code, modifier }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<KeyEvent> for KeyBinding {
|
||||
fn from(value: KeyEvent) -> Self {
|
||||
KeyBinding {
|
||||
code: value.code,
|
||||
modifier: value.modifiers,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for KeyBinding {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
if self.modifier.contains(KeyModifiers::SHIFT) {
|
||||
write!(f, "shift+")?;
|
||||
}
|
||||
if self.modifier.contains(KeyModifiers::CONTROL) {
|
||||
write!(f, "ctrl+")?;
|
||||
}
|
||||
if self.modifier.contains(KeyModifiers::ALT) {
|
||||
write!(f, "alt+")?;
|
||||
}
|
||||
if self.modifier.contains(KeyModifiers::SUPER) {
|
||||
write!(f, "super+")?;
|
||||
}
|
||||
if self.modifier.contains(KeyModifiers::HYPER) {
|
||||
write!(f, "hyper+")?;
|
||||
}
|
||||
if self.modifier.contains(KeyModifiers::META) {
|
||||
write!(f, "meta+")?;
|
||||
}
|
||||
match self.code {
|
||||
KeyCode::Backspace => write!(f, "backspace"),
|
||||
KeyCode::Enter => write!(f, "enter"),
|
||||
KeyCode::Left => write!(f, "left"),
|
||||
KeyCode::Right => write!(f, "right"),
|
||||
KeyCode::Up => write!(f, "up"),
|
||||
KeyCode::Down => write!(f, "down"),
|
||||
KeyCode::Home => write!(f, "home"),
|
||||
KeyCode::End => write!(f, "end"),
|
||||
KeyCode::PageUp => write!(f, "pageup"),
|
||||
KeyCode::PageDown => write!(f, "pagedown"),
|
||||
KeyCode::Tab => write!(f, "tab"),
|
||||
KeyCode::BackTab => write!(f, "backtab"),
|
||||
KeyCode::Delete => write!(f, "delete"),
|
||||
KeyCode::Insert => write!(f, "insert"),
|
||||
KeyCode::Char(c) => write!(f, "{c}"),
|
||||
KeyCode::Null => write!(f, "null"),
|
||||
KeyCode::Esc => write!(f, "esc"),
|
||||
_ => write!(f, "unknown"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<KeyBinding> for String {
|
||||
fn from(value: KeyBinding) -> Self {
|
||||
value.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for KeyBinding {
|
||||
type Error = NymTuiError;
|
||||
|
||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||
value.parse()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for KeyBinding {
|
||||
type Err = NymTuiError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s.rsplit_once('+') {
|
||||
Some((modifiers, value)) => Ok(Self {
|
||||
code: parse_keycode(value)?,
|
||||
modifier: parse_modifiers(modifiers)?,
|
||||
}),
|
||||
None => Ok(Self {
|
||||
code: parse_keycode(s)?,
|
||||
modifier: KeyModifiers::NONE,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_keycode(value: &str) -> Result<KeyCode, NymTuiError> {
|
||||
Ok(if value.len() == 1 {
|
||||
KeyCode::Char(
|
||||
char::from_str(value)
|
||||
.map_err(|source| NymTuiError::InvalidCharacter {
|
||||
str: value.to_string(),
|
||||
source,
|
||||
})?
|
||||
.to_ascii_lowercase(),
|
||||
)
|
||||
} else {
|
||||
SPECIAL_KEYS
|
||||
.get(value)
|
||||
.cloned()
|
||||
.ok_or_else(|| NymTuiError::UnknownKeyBinding {
|
||||
value: value.to_string(),
|
||||
})?
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_modifiers(modifiers: &str) -> Result<KeyModifiers, NymTuiError> {
|
||||
modifiers
|
||||
.split('+')
|
||||
.try_fold(KeyModifiers::NONE, |modifiers, token| {
|
||||
KEY_MODIFIERS
|
||||
.get(token)
|
||||
.map(|modifier| modifiers | *modifier)
|
||||
.ok_or_else(|| NymTuiError::UnknownKeyModifier {
|
||||
value: token.to_string(),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn key_event_to_string(key_event: &KeyEvent) -> String {
|
||||
KeyBinding::from(*key_event).to_string()
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
|
||||
pub mod keybindings;
|
||||
|
||||
pub const DEFAULT_TICK_RATE: Duration = Duration::from_millis(200);
|
||||
|
||||
const DEFAULT_SHUTDOWN_GRACE: Duration = Duration::from_millis(500);
|
||||
const DEFAULT_CANCEL_GRACE: Duration = Duration::from_millis(500);
|
||||
const DEFAULT_ABORT_GRACE: Duration = Duration::from_millis(200);
|
||||
|
||||
#[derive(Default, Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct TuiConfig {
|
||||
pub tui: Tui,
|
||||
|
||||
// #[serde(default)]
|
||||
// pub key_bindings: KeyBindings,
|
||||
#[serde(default)]
|
||||
pub debug: Debug,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
|
||||
pub struct Debug {
|
||||
pub debug_mode_enabled: bool,
|
||||
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub shutdown_grace: Duration,
|
||||
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub cancel_grace: Duration,
|
||||
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub abort_grace: Duration,
|
||||
}
|
||||
|
||||
impl Default for Debug {
|
||||
fn default() -> Self {
|
||||
Debug {
|
||||
debug_mode_enabled: true,
|
||||
shutdown_grace: DEFAULT_SHUTDOWN_GRACE,
|
||||
cancel_grace: DEFAULT_CANCEL_GRACE,
|
||||
abort_grace: DEFAULT_ABORT_GRACE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize, Deserialize, Clone, Copy)]
|
||||
pub struct Tui {
|
||||
#[serde(default, flatten)]
|
||||
pub debug: TuiDebug,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
|
||||
pub struct TuiDebug {
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub tick_rate: Duration,
|
||||
}
|
||||
|
||||
impl Default for TuiDebug {
|
||||
fn default() -> Self {
|
||||
TuiDebug {
|
||||
tick_rate: DEFAULT_TICK_RATE,
|
||||
// frame_rate: DEFAULT_FRAME_RATE,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::tui::action::Action;
|
||||
use crate::tui::dispatcher::store::State;
|
||||
use crate::AppAction;
|
||||
use async_trait::async_trait;
|
||||
use color_eyre::eyre;
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
use tokio_stream::wrappers::UnboundedReceiverStream;
|
||||
use tokio_stream::StreamExt;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::{debug, error, info, warn};
|
||||
|
||||
pub mod store;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ActionSender<T: AppAction>(UnboundedSender<Action<T>>);
|
||||
|
||||
impl<T> From<UnboundedSender<Action<T>>> for ActionSender<T>
|
||||
where
|
||||
T: AppAction,
|
||||
{
|
||||
fn from(value: UnboundedSender<Action<T>>) -> Self {
|
||||
ActionSender(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ActionSender<T>
|
||||
where
|
||||
T: AppAction,
|
||||
{
|
||||
pub fn send(&self, action: impl Into<Action<T>>) {
|
||||
if let Err(unsent) = self.0.send(action.into()) {
|
||||
error!("failed to send {:?} action to the dispatcher", unsent.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type ActionReceiver<T> = UnboundedReceiverStream<Action<T>>;
|
||||
|
||||
pub type StateUpdateSender<S> = UnboundedSender<S>;
|
||||
pub type StateUpdateReceiver<S> = UnboundedReceiverStream<S>;
|
||||
|
||||
#[async_trait]
|
||||
pub trait ActionDispatcher {
|
||||
type Store: State;
|
||||
type Actions: AppAction;
|
||||
|
||||
async fn handle_app_action(
|
||||
&mut self,
|
||||
action: Self::Actions,
|
||||
store: &mut Self::Store,
|
||||
) -> eyre::Result<()>;
|
||||
}
|
||||
|
||||
pub struct DispatcherLoop<D: ActionDispatcher> {
|
||||
dispatcher: D,
|
||||
store: D::Store,
|
||||
|
||||
// to be used with async actions
|
||||
#[allow(dead_code)]
|
||||
action_sender: ActionSender<D::Actions>,
|
||||
action_receiver: ActionReceiver<D::Actions>,
|
||||
state_update_sender: StateUpdateSender<D::Store>,
|
||||
cancellation_token: CancellationToken,
|
||||
}
|
||||
|
||||
impl<D> DispatcherLoop<D>
|
||||
where
|
||||
D: ActionDispatcher + Send + Sync + 'static,
|
||||
D::Store: Send + Sync + 'static,
|
||||
{
|
||||
pub(crate) fn new(
|
||||
dispatcher: D,
|
||||
store: D::Store,
|
||||
action_sender: impl Into<ActionSender<D::Actions>>,
|
||||
action_receiver: impl Into<ActionReceiver<D::Actions>>,
|
||||
state_update_sender: StateUpdateSender<D::Store>,
|
||||
cancellation_token: CancellationToken,
|
||||
) -> DispatcherLoop<D> {
|
||||
DispatcherLoop {
|
||||
dispatcher,
|
||||
store,
|
||||
action_sender: action_sender.into(),
|
||||
action_receiver: action_receiver.into(),
|
||||
state_update_sender,
|
||||
cancellation_token,
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_action(&mut self, action: Option<Action<D::Actions>>) -> eyre::Result<()> {
|
||||
let Some(action) = action else {
|
||||
warn!("the dispatcher channel has closed! we're probably already in shutdown!");
|
||||
// but if we're not, make sure to kick it off...
|
||||
self.cancellation_token.cancel();
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
match action {
|
||||
Action::Quit => {
|
||||
debug!("attempting to handle the QUIT action");
|
||||
self.cancellation_token.cancel();
|
||||
// no need to send any state updates here
|
||||
return Ok(());
|
||||
}
|
||||
Action::AppDefined(action) => {
|
||||
debug!("attempting to handle the following action: {:?}", action);
|
||||
self.dispatcher
|
||||
.handle_app_action(action, &mut self.store)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
self.state_update_sender.send(self.store.clone())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn run(&mut self) -> eyre::Result<()> {
|
||||
info!("starting the dispatcher loop");
|
||||
|
||||
// issue initial state
|
||||
self.state_update_sender.send(self.store.clone())?;
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
biased;
|
||||
_ = self.cancellation_token.cancelled() => {
|
||||
info!("received cancellation token");
|
||||
break;
|
||||
}
|
||||
maybe_action = self.action_receiver.next() => {
|
||||
self.handle_action(maybe_action).await?
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use color_eyre::eyre;
|
||||
use crossterm::event::EventStream;
|
||||
use ratatui::backend::CrosstermBackend as Backend;
|
||||
use ratatui::crossterm::{
|
||||
self, cursor,
|
||||
terminal::{EnterAlternateScreen, LeaveAlternateScreen},
|
||||
};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use tokio_stream::Stream;
|
||||
|
||||
pub struct TuiHandle {
|
||||
pub terminal: ratatui::Terminal<Backend<std::io::Stderr>>,
|
||||
pub crossterm_events: EventStream,
|
||||
}
|
||||
|
||||
impl TuiHandle {
|
||||
pub fn new() -> Result<TuiHandle, eyre::Error> {
|
||||
let terminal = ratatui::Terminal::new(Backend::new(std::io::stderr()))?;
|
||||
let crossterm_events = EventStream::new();
|
||||
|
||||
Ok(TuiHandle {
|
||||
terminal,
|
||||
crossterm_events,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn enter(&self) -> Result<(), eyre::Error> {
|
||||
crossterm::terminal::enable_raw_mode()?;
|
||||
crossterm::execute!(std::io::stderr(), EnterAlternateScreen, cursor::Hide)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn exit(&mut self) -> eyre::Result<()> {
|
||||
if crossterm::terminal::is_raw_mode_enabled()? {
|
||||
self.terminal.flush()?;
|
||||
crossterm::execute!(std::io::stderr(), LeaveAlternateScreen, cursor::Show)?;
|
||||
crossterm::terminal::disable_raw_mode()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream for TuiHandle {
|
||||
type Item = <EventStream as Stream>::Item;
|
||||
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
Pin::new(&mut self.crossterm_events).poll_next(cx)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.crossterm_events.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for TuiHandle {
|
||||
type Target = ratatui::Terminal<Backend<std::io::Stderr>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.terminal
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for TuiHandle {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.terminal
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TuiHandle {
|
||||
fn drop(&mut self) {
|
||||
// well. at this point we can't do much, we'll just go straight into the panic handler
|
||||
#[allow(clippy::expect_used)]
|
||||
self.exit().expect("failed to teardown the terminal")
|
||||
}
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::tui::action::Action;
|
||||
use crate::tui::config::TuiConfig;
|
||||
use crate::tui::dispatcher::{ActionDispatcher, ActionSender, DispatcherLoop};
|
||||
use crate::tui::ui::UiEventLoop;
|
||||
use crate::Component;
|
||||
use color_eyre::eyre;
|
||||
use std::time::Duration;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::task::JoinHandle;
|
||||
use tokio::time::timeout;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tokio_util::task::TaskTracker;
|
||||
use tracing::{error, info};
|
||||
|
||||
pub struct TuiManager<C: Component> {
|
||||
shutdown_grace: Duration,
|
||||
cancel_grace: Duration,
|
||||
abort_grace: Duration,
|
||||
|
||||
task_tracker: TaskTracker,
|
||||
cancellation_token: CancellationToken,
|
||||
|
||||
dispatcher_handle: JoinHandle<eyre::Result<()>>,
|
||||
ui_event_loop_handle: JoinHandle<eyre::Result<()>>,
|
||||
|
||||
action_sender: ActionSender<C::Actions>,
|
||||
}
|
||||
|
||||
impl<C> TuiManager<C>
|
||||
where
|
||||
C: Component + Send + Sync + 'static,
|
||||
C::State: Send + Sync + 'static,
|
||||
C::Actions: Send + Sync + Clone + 'static,
|
||||
{
|
||||
pub(crate) fn build_new<D>(
|
||||
config: TuiConfig,
|
||||
action_dispatcher: D,
|
||||
initial_state: D::Store,
|
||||
) -> eyre::Result<TuiManager<C>>
|
||||
where
|
||||
D: ActionDispatcher<Store = C::State, Actions = C::Actions> + Send + Sync + 'static,
|
||||
{
|
||||
let task_tracker = TaskTracker::new();
|
||||
let cancellation_token = CancellationToken::new();
|
||||
|
||||
let (action_tx, action_rx) = mpsc::unbounded_channel();
|
||||
let (state_tx, state_rx) = mpsc::unbounded_channel();
|
||||
|
||||
let mut dispatcher_loop = DispatcherLoop::new(
|
||||
action_dispatcher,
|
||||
initial_state,
|
||||
action_tx.clone(),
|
||||
action_rx,
|
||||
state_tx,
|
||||
cancellation_token.clone(),
|
||||
);
|
||||
let mut ui_event_loop = UiEventLoop::<C>::new(
|
||||
config.tui.debug.tick_rate,
|
||||
cancellation_token.clone(),
|
||||
state_rx,
|
||||
action_tx.clone(),
|
||||
)?;
|
||||
|
||||
let dispatcher_handle = task_tracker.spawn(async move { dispatcher_loop.run().await });
|
||||
let ui_event_loop_handle = task_tracker.spawn(async move { ui_event_loop.run().await });
|
||||
|
||||
task_tracker.close();
|
||||
|
||||
Ok(TuiManager {
|
||||
shutdown_grace: config.debug.shutdown_grace,
|
||||
cancel_grace: config.debug.cancel_grace,
|
||||
abort_grace: config.debug.abort_grace,
|
||||
task_tracker,
|
||||
cancellation_token,
|
||||
dispatcher_handle,
|
||||
ui_event_loop_handle,
|
||||
action_sender: action_tx.into(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn graceful_shutdown(&self) {
|
||||
// 1. try to send quit action to handle it the most gracefully
|
||||
self.action_sender.send(Action::Quit);
|
||||
if timeout(self.shutdown_grace, self.task_tracker.wait())
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
return;
|
||||
}
|
||||
error!("timed out while waiting for graceful shutdown");
|
||||
|
||||
// 2. if that doesn't work, issue cancellation token
|
||||
self.cancellation_token.cancel();
|
||||
if timeout(self.cancel_grace, self.task_tracker.wait())
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
return;
|
||||
}
|
||||
error!("timed out while attempting to resolve cancellation token shutdown");
|
||||
|
||||
// 3. finally go with nuclear option and just abort the tasks
|
||||
self.dispatcher_handle.abort();
|
||||
self.ui_event_loop_handle.abort();
|
||||
|
||||
if timeout(self.abort_grace, self.task_tracker.wait())
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
error!("somehow we still failed to shutdown our tasks! we might end up in a dirty state... oh well")
|
||||
}
|
||||
|
||||
pub(crate) async fn wait_for_exit_or_signal(&self) {
|
||||
tokio::select! {
|
||||
_ = self.task_tracker.wait() => {
|
||||
// user decided to quit with 'normal' action
|
||||
}
|
||||
_ = wait_for_signal() => {
|
||||
self.graceful_shutdown().await
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[allow(clippy::expect_used)]
|
||||
pub async fn wait_for_signal() {
|
||||
use tokio::signal::unix::{signal, SignalKind};
|
||||
let mut sigterm = signal(SignalKind::terminate()).expect("failed to setup SIGTERM channel");
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
pub async fn wait_for_signal() {
|
||||
tokio::select! {
|
||||
_ = tokio::signal::ctrl_c() => {
|
||||
info!("Received SIGINT");
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::tui::handle::TuiHandle;
|
||||
use color_eyre::eyre;
|
||||
use tracing::error;
|
||||
|
||||
pub mod action;
|
||||
pub mod config;
|
||||
pub(crate) mod dispatcher;
|
||||
pub mod handle;
|
||||
pub(crate) mod manager;
|
||||
pub(crate) mod ui;
|
||||
|
||||
pub fn initialize_panic_handler() -> eyre::Result<()> {
|
||||
let (panic_hook, eyre_hook) = color_eyre::config::HookBuilder::default()
|
||||
.display_location_section(true)
|
||||
.display_env_section(true)
|
||||
.into_hooks();
|
||||
eyre_hook.install()?;
|
||||
std::panic::set_hook(Box::new(move |panic_info| {
|
||||
if let Err(r) = TuiHandle::new().and_then(|mut h| h.exit()) {
|
||||
error!("Unable to exit Terminal: {:?}", r);
|
||||
}
|
||||
|
||||
let msg = format!("{}", panic_hook.panic_report(panic_info));
|
||||
#[cfg(not(debug_assertions))]
|
||||
{
|
||||
eprintln!("{}", msg); // prints color-eyre stack trace to stderr
|
||||
}
|
||||
error!("error: {}", strip_ansi_escapes::strip_str(msg));
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
// Better Panic stacktrace that is only enabled when debugging.
|
||||
better_panic::Settings::auto()
|
||||
.most_recent_first(false)
|
||||
.lineno_suffix(true)
|
||||
.verbosity(better_panic::Verbosity::Full)
|
||||
.create_panic_handler()(panic_info);
|
||||
}
|
||||
|
||||
std::process::exit(1);
|
||||
}));
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::tui::config::keybindings::key_event_to_string;
|
||||
use crate::tui::dispatcher::ActionSender;
|
||||
use crate::{AppAction, Component, State};
|
||||
use crossterm::event::KeyEvent;
|
||||
use ratatui::layout::{Alignment, Rect};
|
||||
use ratatui::prelude::{Line, Style, Stylize};
|
||||
use ratatui::widgets::Block;
|
||||
use ratatui::Frame;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub struct DebugHistory<S, A> {
|
||||
last_tick_key_events: Vec<KeyEvent>,
|
||||
|
||||
phantom_state: PhantomData<S>,
|
||||
phantom_action: PhantomData<A>,
|
||||
}
|
||||
|
||||
impl<S, A> Component for DebugHistory<S, A>
|
||||
where
|
||||
S: State,
|
||||
A: AppAction,
|
||||
{
|
||||
type State = S;
|
||||
type Actions = A;
|
||||
|
||||
fn new(_state: &Self::State, _action_sender: ActionSender<Self::Actions>) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
DebugHistory {
|
||||
last_tick_key_events: vec![],
|
||||
phantom_state: PhantomData,
|
||||
phantom_action: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn tick(&mut self) -> bool {
|
||||
let was_empty = self.last_tick_key_events.is_empty();
|
||||
self.last_tick_key_events.drain(..);
|
||||
|
||||
!was_empty
|
||||
}
|
||||
|
||||
fn handle_key(&mut self, key: KeyEvent) -> color_eyre::Result<()> {
|
||||
self.last_tick_key_events.push(key);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn view(&mut self, frame: &mut Frame, rect: Rect) {
|
||||
frame.render_widget(
|
||||
Block::default()
|
||||
.title_top(
|
||||
Line::from(format!(
|
||||
"{:?}",
|
||||
&self
|
||||
.last_tick_key_events
|
||||
.iter()
|
||||
.map(key_event_to_string)
|
||||
.collect::<Vec<_>>()
|
||||
))
|
||||
.alignment(Alignment::Right),
|
||||
)
|
||||
.title_style(Style::default().bold()),
|
||||
rect,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::tui::config::keybindings::LoggerKeybindings;
|
||||
use crate::tui::dispatcher::store::State;
|
||||
use crate::tui::dispatcher::ActionSender;
|
||||
use crate::tui::ui::components::Component;
|
||||
use crate::AppAction;
|
||||
use crossterm::event::KeyEvent;
|
||||
use ratatui::layout::Rect;
|
||||
use ratatui::prelude::*;
|
||||
use ratatui::widgets::Block;
|
||||
use ratatui::Frame;
|
||||
use std::marker::PhantomData;
|
||||
use tui_logger::{TuiLoggerLevelOutput, TuiLoggerSmartWidget, TuiWidgetState};
|
||||
|
||||
pub struct Props {
|
||||
pub keybindings: LoggerKeybindings,
|
||||
}
|
||||
|
||||
pub struct Logger<S, A> {
|
||||
props: Props,
|
||||
widget_state: TuiWidgetState,
|
||||
|
||||
phantom_state: PhantomData<S>,
|
||||
phantom_action: PhantomData<A>,
|
||||
}
|
||||
|
||||
impl<S, A> Component for Logger<S, A>
|
||||
where
|
||||
S: State,
|
||||
for<'a> Props: From<&'a S>,
|
||||
A: AppAction,
|
||||
{
|
||||
type State = S;
|
||||
type Actions = A;
|
||||
|
||||
fn new(state: &Self::State, _action_sender: ActionSender<Self::Actions>) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Logger {
|
||||
props: Props::from(state),
|
||||
widget_state: TuiWidgetState::new(),
|
||||
phantom_state: PhantomData,
|
||||
phantom_action: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn tick(&mut self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn handle_key(&mut self, key: KeyEvent) -> color_eyre::Result<()> {
|
||||
if let Some(tui_event) = self.props.keybindings.tui_logger_event(key.into()) {
|
||||
self.widget_state.transition(tui_event)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn view(&mut self, frame: &mut Frame, rect: Rect) {
|
||||
let border = Block::bordered();
|
||||
let inner_area = border.inner(rect);
|
||||
frame.render_widget(border, rect);
|
||||
|
||||
let tui_sm = TuiLoggerSmartWidget::default()
|
||||
.style_error(Style::default().fg(Color::Red))
|
||||
.style_warn(Style::default().fg(Color::Yellow))
|
||||
.style_info(Style::default().fg(Color::Green))
|
||||
.style_debug(Style::default().fg(Color::Cyan))
|
||||
.style_trace(Style::default().fg(Color::Magenta))
|
||||
.output_separator(':')
|
||||
.output_timestamp(Some("%F %H:%M:%S%.3f".to_string()))
|
||||
.output_level(Some(TuiLoggerLevelOutput::Long))
|
||||
.output_target(true)
|
||||
.output_file(true)
|
||||
.output_line(true)
|
||||
.state(&self.widget_state);
|
||||
frame.render_widget(tui_sm, inner_area);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod debug_history;
|
||||
|
||||
#[cfg(feature = "logger")]
|
||||
pub mod logger;
|
||||
|
||||
pub use debug_history::DebugHistory;
|
||||
|
||||
#[cfg(feature = "logger")]
|
||||
pub use logger::{Logger, Props as LoggerProps};
|
||||
@@ -1,45 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::tui::dispatcher::ActionSender;
|
||||
use crate::{AppAction, State};
|
||||
use color_eyre::eyre;
|
||||
use crossterm::event::KeyEvent;
|
||||
use ratatui::prelude::Rect;
|
||||
use ratatui::Frame;
|
||||
|
||||
pub mod common;
|
||||
|
||||
// pub trait Props<'a>: From<&'a Self::State> {
|
||||
// type State: State;
|
||||
// }
|
||||
|
||||
pub trait Component {
|
||||
type State: State;
|
||||
type Actions: AppAction;
|
||||
|
||||
fn new(state: &Self::State, action_sender: ActionSender<Self::Actions>) -> Self
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
fn update(self, state: &Self::State) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let _ = state;
|
||||
self
|
||||
}
|
||||
|
||||
// returns boolean indicating whether a rerender is needed
|
||||
// fn tick(&mut self) -> bool;
|
||||
fn tick(&mut self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn handle_key(&mut self, key: KeyEvent) -> eyre::Result<()> {
|
||||
let _ = key;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn view(&mut self, frame: &mut Frame, rect: Rect);
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::tui::dispatcher::{ActionSender, StateUpdateReceiver};
|
||||
use crate::tui::handle::TuiHandle;
|
||||
use crate::tui::ui::components::Component;
|
||||
use color_eyre::eyre;
|
||||
use color_eyre::eyre::eyre;
|
||||
use crossterm::event::Event;
|
||||
use humantime_serde::re::humantime;
|
||||
use std::io;
|
||||
use std::marker::PhantomData;
|
||||
use std::time::Duration;
|
||||
use tokio::time::{timeout, Instant};
|
||||
use tokio_stream::StreamExt;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::{info, trace, warn};
|
||||
|
||||
pub mod components;
|
||||
|
||||
pub struct UiEventLoop<C: Component> {
|
||||
tick_rate: Duration,
|
||||
cancellation_token: CancellationToken,
|
||||
state_receiver: StateUpdateReceiver<C::State>,
|
||||
|
||||
// only to be used to construct root 'App' instance
|
||||
action_sender: ActionSender<C::Actions>,
|
||||
tui_handle: TuiHandle,
|
||||
|
||||
root_component: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<C> UiEventLoop<C>
|
||||
where
|
||||
C: Component,
|
||||
{
|
||||
pub fn new(
|
||||
tick_rate: Duration,
|
||||
cancellation_token: CancellationToken,
|
||||
state_receiver: impl Into<StateUpdateReceiver<C::State>>,
|
||||
action_sender: impl Into<ActionSender<C::Actions>>,
|
||||
) -> eyre::Result<Self> {
|
||||
Ok(UiEventLoop {
|
||||
tick_rate,
|
||||
cancellation_token,
|
||||
state_receiver: state_receiver.into(),
|
||||
action_sender: action_sender.into(),
|
||||
tui_handle: TuiHandle::new()?,
|
||||
root_component: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
async fn handle_ui_event(
|
||||
&mut self,
|
||||
event: Option<io::Result<Event>>,
|
||||
root_app: &mut C,
|
||||
) -> eyre::Result<()> {
|
||||
let Some(event) = event else {
|
||||
warn!("the crossterm event channel has closed! we're probably already in shutdown!");
|
||||
// but if we're not, make sure to kick it off...
|
||||
self.cancellation_token.cancel();
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
match event? {
|
||||
Event::FocusGained => {}
|
||||
Event::FocusLost => {}
|
||||
Event::Key(key_event) => root_app.handle_key(key_event)?,
|
||||
Event::Mouse(_) => {}
|
||||
Event::Paste(_) => {}
|
||||
Event::Resize(_, _) => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_updated_state(&mut self, state: Option<C::State>, root_app: C) -> C
|
||||
where
|
||||
C: Component,
|
||||
{
|
||||
let Some(updated_state) = state else {
|
||||
warn!("the state update channel has closed! we're probably already in shutdown!");
|
||||
// but if we're not, make sure to kick it off...
|
||||
self.cancellation_token.cancel();
|
||||
return root_app;
|
||||
};
|
||||
|
||||
root_app.update(&updated_state)
|
||||
}
|
||||
|
||||
pub async fn run(&mut self) -> eyre::Result<()>
|
||||
where
|
||||
// this clone shouldn't really be needed...
|
||||
C::Actions: Clone,
|
||||
{
|
||||
info!("starting the ui loop");
|
||||
|
||||
// wait for initial state...
|
||||
let initial_state = timeout(Duration::from_secs(1), self.state_receiver.next())
|
||||
.await?
|
||||
.ok_or_else(|| eyre!("did not receive initial state!"))?;
|
||||
|
||||
let mut root_app = C::new(&initial_state, self.action_sender.clone());
|
||||
|
||||
let mut tick_rate = self.tick_rate;
|
||||
let mut tick_interval = tokio::time::interval(tick_rate);
|
||||
self.tui_handle.enter()?;
|
||||
|
||||
let mut draw = true;
|
||||
|
||||
loop {
|
||||
if draw {
|
||||
let draw_start = Instant::now();
|
||||
|
||||
trace!("redrawing the UI");
|
||||
self.tui_handle
|
||||
.draw(|frame| root_app.view(frame, frame.area()))?;
|
||||
|
||||
let taken = humantime::format_duration(draw_start.elapsed()).to_string();
|
||||
trace!(time_taken = taken, "UI drawing");
|
||||
}
|
||||
|
||||
tokio::select! {
|
||||
biased;
|
||||
_ = self.cancellation_token.cancelled() => {
|
||||
info!("received cancellation token");
|
||||
break;
|
||||
}
|
||||
maybe_ui_event = self.tui_handle.next() => {
|
||||
self.handle_ui_event(maybe_ui_event, &mut root_app).await?;
|
||||
draw = true;
|
||||
}
|
||||
state_update = self.state_receiver.next() => {
|
||||
root_app = self.handle_updated_state(state_update, root_app).await;
|
||||
// the tick rate has changed
|
||||
if self.tick_rate != tick_rate {
|
||||
tick_rate = self.tick_rate;
|
||||
tick_interval = tokio::time::interval(tick_rate);
|
||||
}
|
||||
draw = true;
|
||||
}
|
||||
_ = tick_interval.tick() => {
|
||||
let tick_start = Instant::now();
|
||||
draw = root_app.tick();
|
||||
|
||||
let taken = humantime::format_duration(tick_start.elapsed()).to_string();
|
||||
trace!(time_taken = taken, will_redraw = draw, "app tick");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -324,18 +324,6 @@ pub fn unchecked_aggregate_indices_signatures(
|
||||
_aggregate_indices_signatures(params, vk, signatures_shares, false)
|
||||
}
|
||||
|
||||
/// Generates parameters for the scheme setup.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `total_coins` - it is the number of coins in a freshly generated wallet. It is the public parameter of the scheme.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `Parameters` struct containing group parameters, public key, the number of signatures (`total_coins`),
|
||||
/// and a map of signatures for each index `l`.
|
||||
///
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -264,7 +264,7 @@ impl<'b> Add<&'b VerificationKeyAuth> for VerificationKeyAuth {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Mul<Scalar> for &'a VerificationKeyAuth {
|
||||
impl Mul<Scalar> for &VerificationKeyAuth {
|
||||
type Output = VerificationKeyAuth;
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -984,7 +984,7 @@ pub struct SerialNumberRef<'a> {
|
||||
pub(crate) inner: &'a [G1Projective],
|
||||
}
|
||||
|
||||
impl<'a> SerialNumberRef<'a> {
|
||||
impl SerialNumberRef<'_> {
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
let ss_len = self.inner.len();
|
||||
let mut bytes: Vec<u8> = Vec::with_capacity(ss_len * 48);
|
||||
|
||||
@@ -206,10 +206,10 @@ impl Deref for PublicKey {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Mul<&'b Scalar> for &'a PublicKey {
|
||||
impl<'a> Mul<&'a Scalar> for &PublicKey {
|
||||
type Output = G1Projective;
|
||||
|
||||
fn mul(self, rhs: &'b Scalar) -> Self::Output {
|
||||
fn mul(self, rhs: &'a Scalar) -> Self::Output {
|
||||
self.0 * rhs
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,7 +305,7 @@ impl<'b> Add<&'b VerificationKey> for VerificationKey {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Mul<Scalar> for &'a VerificationKey {
|
||||
impl Mul<Scalar> for &VerificationKey {
|
||||
type Output = VerificationKey;
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -64,7 +64,7 @@ impl<'de> Deserialize<'de> for Recipient {
|
||||
{
|
||||
struct RecipientVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for RecipientVisitor {
|
||||
impl Visitor<'_> for RecipientVisitor {
|
||||
type Value = Recipient;
|
||||
|
||||
fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//! Encodoing and decoding node routing information.
|
||||
//!
|
||||
//! This module is responsible for encoding and decoding node routing information, so that
|
||||
//! they could be later put into an appropriate field in a sphinx header.
|
||||
//! Currently, that routing information is an IP address, but in principle it can be anything
|
||||
//! for as long as it's going to fit in the field.
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_sphinx_types::{NodeAddressBytes, NODE_ADDRESS_LENGTH};
|
||||
|
||||
@@ -12,13 +18,6 @@ use thiserror::Error;
|
||||
pub type NodeIdentity = identity::PublicKey;
|
||||
pub const NODE_IDENTITY_SIZE: usize = identity::PUBLIC_KEY_LENGTH;
|
||||
|
||||
/// Encodoing and decoding node routing information.
|
||||
///
|
||||
/// This module is responsible for encoding and decoding node routing information, so that
|
||||
/// they could be later put into an appropriate field in a sphinx header.
|
||||
/// Currently, that routing information is an IP address, but in principle it can be anything
|
||||
/// for as long as it's going to fit in the field.
|
||||
|
||||
/// MAX_UNPADDED_LEN represents maximum length an unpadded address could have.
|
||||
/// In this case it's an ipv6 socket address (with version prefix)
|
||||
pub const MAX_NODE_ADDRESS_UNPADDED_LEN: usize = 19;
|
||||
|
||||
@@ -56,7 +56,7 @@ impl<'de> Deserialize<'de> for ReplySurb {
|
||||
{
|
||||
struct ReplySurbVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for ReplySurbVisitor {
|
||||
impl Visitor<'_> for ReplySurbVisitor {
|
||||
type Value = ReplySurb;
|
||||
|
||||
fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
|
||||
|
||||
@@ -18,7 +18,7 @@ use nym_sphinx_params::{PacketType, ReplySurbKeyDigestAlgorithm, DEFAULT_NUM_MIX
|
||||
use nym_sphinx_types::{Delay, NymPacket};
|
||||
use nym_topology::{NymTopology, NymTopologyError};
|
||||
use rand::{CryptoRng, Rng, SeedableRng};
|
||||
use rand_chacha::ChaCha20Rng;
|
||||
use rand_chacha::ChaCha8Rng;
|
||||
|
||||
use nym_sphinx_chunking::monitoring;
|
||||
use std::time::Duration;
|
||||
@@ -51,6 +51,7 @@ impl From<PreparedFragment> for MixPacket {
|
||||
pub trait FragmentPreparer {
|
||||
type Rng: CryptoRng + Rng;
|
||||
|
||||
fn deterministic_route_selection(&self) -> bool;
|
||||
fn rng(&mut self) -> &mut Self::Rng;
|
||||
fn nonce(&self) -> i32;
|
||||
fn num_mix_hops(&self) -> u8;
|
||||
@@ -201,9 +202,7 @@ pub trait FragmentPreparer {
|
||||
// could perform diffie-hellman with its own keys followed by a kdf to re-derive
|
||||
// the packet encryption key
|
||||
|
||||
let seed = fragment.seed().wrapping_mul(self.nonce());
|
||||
let mut rng = ChaCha20Rng::seed_from_u64(seed as u64);
|
||||
|
||||
let fragment_header = fragment.header();
|
||||
let destination = packet_recipient.gateway();
|
||||
let hops = mix_hops.unwrap_or(self.num_mix_hops());
|
||||
monitoring::fragment_sent(&fragment, self.nonce(), *destination, hops);
|
||||
@@ -241,8 +240,18 @@ pub trait FragmentPreparer {
|
||||
};
|
||||
|
||||
// generate pseudorandom route for the packet
|
||||
log::trace!("Preparing chunk for sending with {} mix hops", hops);
|
||||
let route = topology.random_route_to_gateway(&mut rng, hops, destination)?;
|
||||
log::trace!("Preparing chunk for sending with {hops} mix hops");
|
||||
let route = if self.deterministic_route_selection() {
|
||||
log::trace!("using deterministic route selection");
|
||||
let seed = fragment_header.seed().wrapping_mul(self.nonce());
|
||||
let mut rng = ChaCha8Rng::seed_from_u64(seed as u64);
|
||||
topology.random_route_to_gateway(&mut rng, hops, destination)?
|
||||
} else {
|
||||
log::trace!("using pseudorandom route selection");
|
||||
let mut rng = self.rng();
|
||||
topology.random_route_to_gateway(&mut rng, hops, destination)?
|
||||
};
|
||||
|
||||
let destination = packet_recipient.as_sphinx_destination();
|
||||
|
||||
// including set of delays
|
||||
@@ -313,6 +322,9 @@ pub struct MessagePreparer<R> {
|
||||
/// Instance of a cryptographically secure random number generator.
|
||||
rng: R,
|
||||
|
||||
/// Specify whether route selection should be determined by the packet header.
|
||||
deterministic_route_selection: bool,
|
||||
|
||||
/// Address of this client which also represent an address to which all acknowledgements
|
||||
/// and surb-based are going to be sent.
|
||||
sender_address: Recipient,
|
||||
@@ -336,6 +348,7 @@ where
|
||||
{
|
||||
pub fn new(
|
||||
rng: R,
|
||||
deterministic_route_selection: bool,
|
||||
sender_address: Recipient,
|
||||
average_packet_delay: Duration,
|
||||
average_ack_delay: Duration,
|
||||
@@ -344,6 +357,7 @@ where
|
||||
let nonce = rng.gen();
|
||||
MessagePreparer {
|
||||
rng,
|
||||
deterministic_route_selection,
|
||||
sender_address,
|
||||
average_packet_delay,
|
||||
average_ack_delay,
|
||||
@@ -457,10 +471,18 @@ where
|
||||
impl<R: CryptoRng + Rng> FragmentPreparer for MessagePreparer<R> {
|
||||
type Rng = R;
|
||||
|
||||
fn deterministic_route_selection(&self) -> bool {
|
||||
self.deterministic_route_selection
|
||||
}
|
||||
|
||||
fn rng(&mut self) -> &mut Self::Rng {
|
||||
&mut self.rng
|
||||
}
|
||||
|
||||
fn nonce(&self) -> i32 {
|
||||
self.nonce
|
||||
}
|
||||
|
||||
fn num_mix_hops(&self) -> u8 {
|
||||
self.num_mix_hops
|
||||
}
|
||||
@@ -472,10 +494,6 @@ impl<R: CryptoRng + Rng> FragmentPreparer for MessagePreparer<R> {
|
||||
fn average_ack_delay(&self) -> Duration {
|
||||
self.average_ack_delay
|
||||
}
|
||||
|
||||
fn nonce(&self) -> i32 {
|
||||
self.nonce
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -253,25 +253,15 @@ impl Socks5RequestContent {
|
||||
/// Deserialize the request type, connection id, destination address and port,
|
||||
/// and the request body from bytes.
|
||||
///
|
||||
// TODO: this was already inaccurate
|
||||
// /// Serialized bytes looks like this:
|
||||
// ///
|
||||
// /// --------------------------------------------------------------------------------------
|
||||
// /// request_flag | connection_id | address_length | remote_address_bytes | request_data |
|
||||
// /// 1 | 8 | 2 | address_length | ... |
|
||||
// /// --------------------------------------------------------------------------------------
|
||||
///
|
||||
/// The request_flag tells us whether this is a new connection request (`new_connect`),
|
||||
/// an already-established connection we should send up (`new_send`), or
|
||||
/// a request to close an established connection (`new_close`).
|
||||
|
||||
// connect:
|
||||
// RequestFlag::Connect || CONN_ID || ADDR_LEN || ADDR || <RETURN_ADDR>
|
||||
//
|
||||
// send:
|
||||
// RequestFlag::Send || CONN_ID || LOCAL_CLOSED || DATA
|
||||
// where DATA: SEQ || TRUE_DATA
|
||||
|
||||
pub fn try_from_bytes(b: &[u8]) -> Result<Socks5RequestContent, RequestDeserializationError> {
|
||||
// each request needs to at least contain flag and ConnectionId
|
||||
if b.is_empty() {
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_bin_common::version_checker;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::hash::Hash;
|
||||
|
||||
pub trait Versioned: Clone {
|
||||
fn version(&self) -> String;
|
||||
}
|
||||
|
||||
pub trait VersionFilterable<T> {
|
||||
#[must_use]
|
||||
fn filter_by_version(&self, expected_version: &str) -> Self;
|
||||
}
|
||||
|
||||
impl<T> VersionFilterable<T> for Vec<T>
|
||||
where
|
||||
T: Versioned,
|
||||
{
|
||||
fn filter_by_version(&self, expected_version: &str) -> Self {
|
||||
self.iter()
|
||||
.filter(|node| {
|
||||
version_checker::is_minor_version_compatible(&node.version(), expected_version)
|
||||
})
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, K, V> VersionFilterable<T> for HashMap<K, V>
|
||||
where
|
||||
K: Eq + Hash + Clone,
|
||||
V: VersionFilterable<T>,
|
||||
T: Versioned,
|
||||
{
|
||||
fn filter_by_version(&self, expected_version: &str) -> Self {
|
||||
self.iter()
|
||||
.map(|(k, v)| (k.clone(), v.filter_by_version(expected_version)))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, K, V> VersionFilterable<T> for BTreeMap<K, V>
|
||||
where
|
||||
K: Eq + Ord + Clone,
|
||||
V: VersionFilterable<T>,
|
||||
T: Versioned,
|
||||
{
|
||||
fn filter_by_version(&self, expected_version: &str) -> Self {
|
||||
self.iter()
|
||||
.map(|(k, v)| (k.clone(), v.filter_by_version(expected_version)))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{filter, NetworkAddress, NodeVersion};
|
||||
use crate::{NetworkAddress, NodeVersion};
|
||||
use nym_api_requests::nym_nodes::SkimmedNode;
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
use nym_mixnet_contract_common::NodeId;
|
||||
@@ -126,13 +126,6 @@ impl fmt::Display for LegacyNode {
|
||||
}
|
||||
}
|
||||
|
||||
impl filter::Versioned for LegacyNode {
|
||||
fn version(&self) -> String {
|
||||
// TODO: return semver instead
|
||||
self.version.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a LegacyNode> for SphinxNode {
|
||||
fn from(node: &'a LegacyNode) -> Self {
|
||||
let node_address_bytes = NymNodeRoutingAddress::from(node.mix_host)
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#![allow(unknown_lints)]
|
||||
// clippy::to_string_trait_impl is not on stable as of 1.77
|
||||
|
||||
use crate::filter::VersionFilterable;
|
||||
pub use error::NymTopologyError;
|
||||
use log::{debug, info, warn};
|
||||
use nym_api_requests::nym_nodes::{CachedNodesResponse, SkimmedNode};
|
||||
@@ -25,7 +24,6 @@ use std::str::FromStr;
|
||||
use ::serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
pub mod error;
|
||||
pub mod filter;
|
||||
pub mod gateway;
|
||||
pub mod mix;
|
||||
pub mod random_route_provider;
|
||||
@@ -465,19 +463,6 @@ impl NymTopology {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn filter_system_version(&self, expected_version: &str) -> Self {
|
||||
self.filter_node_versions(expected_version)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn filter_node_versions(&self, expected_mix_version: &str) -> Self {
|
||||
NymTopology {
|
||||
mixes: self.mixes.filter_by_version(expected_mix_version),
|
||||
gateways: self.gateways.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serializable")]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{filter, NetworkAddress, NodeVersion};
|
||||
use crate::{NetworkAddress, NodeVersion};
|
||||
use nym_api_requests::nym_nodes::{NodeRole, SkimmedNode};
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
pub use nym_mixnet_contract_common::LegacyMixLayer;
|
||||
@@ -89,13 +89,6 @@ impl LegacyNode {
|
||||
}
|
||||
}
|
||||
|
||||
impl filter::Versioned for LegacyNode {
|
||||
fn version(&self) -> String {
|
||||
// TODO: return semver instead
|
||||
self.version.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a LegacyNode> for SphinxNode {
|
||||
fn from(node: &'a LegacyNode) -> Self {
|
||||
let node_address_bytes = NymNodeRoutingAddress::from(node.mix_host)
|
||||
|
||||
@@ -162,6 +162,13 @@ pub struct TrafficWasm {
|
||||
/// a loop cover message is sent instead in order to preserve the rate.
|
||||
pub message_sending_average_delay_ms: u32,
|
||||
|
||||
/// Specify how many times particular packet can be retransmitted
|
||||
/// None - no limit
|
||||
pub maximum_number_of_retransmissions: Option<u32>,
|
||||
|
||||
/// Specify whether route selection should be determined by the packet header.
|
||||
pub deterministic_route_selection: bool,
|
||||
|
||||
/// Controls whether the main packet stream constantly produces packets according to the predefined
|
||||
/// poisson distribution.
|
||||
pub disable_main_poisson_packet_distribution: bool,
|
||||
@@ -196,6 +203,8 @@ impl From<TrafficWasm> for ConfigTraffic {
|
||||
message_sending_average_delay: Duration::from_millis(
|
||||
traffic.message_sending_average_delay_ms as u64,
|
||||
),
|
||||
deterministic_route_selection: traffic.deterministic_route_selection,
|
||||
maximum_number_of_retransmissions: traffic.maximum_number_of_retransmissions,
|
||||
disable_main_poisson_packet_distribution: traffic
|
||||
.disable_main_poisson_packet_distribution,
|
||||
primary_packet_size: PacketSize::RegularPacket,
|
||||
@@ -211,6 +220,8 @@ impl From<ConfigTraffic> for TrafficWasm {
|
||||
average_packet_delay_ms: traffic.average_packet_delay.as_millis() as u32,
|
||||
message_sending_average_delay_ms: traffic.message_sending_average_delay.as_millis()
|
||||
as u32,
|
||||
deterministic_route_selection: traffic.deterministic_route_selection,
|
||||
maximum_number_of_retransmissions: traffic.maximum_number_of_retransmissions,
|
||||
disable_main_poisson_packet_distribution: traffic
|
||||
.disable_main_poisson_packet_distribution,
|
||||
use_extended_packet_size: traffic.secondary_packet_size.is_some(),
|
||||
|
||||
@@ -88,6 +88,14 @@ pub struct TrafficWasmOverride {
|
||||
#[tsify(optional)]
|
||||
pub message_sending_average_delay_ms: Option<u32>,
|
||||
|
||||
/// Specify how many times particular packet can be retransmitted
|
||||
#[tsify(optional)]
|
||||
pub maximum_number_of_retransmissions: Option<u32>,
|
||||
|
||||
/// Specify whether route selection should be determined by the packet header.
|
||||
#[tsify(optional)]
|
||||
pub deterministic_route_selection: Option<bool>,
|
||||
|
||||
/// Controls whether the main packet stream constantly produces packets according to the predefined
|
||||
/// poisson distribution.
|
||||
#[tsify(optional)]
|
||||
@@ -113,6 +121,10 @@ impl From<TrafficWasmOverride> for TrafficWasm {
|
||||
message_sending_average_delay_ms: value
|
||||
.message_sending_average_delay_ms
|
||||
.unwrap_or(def.message_sending_average_delay_ms),
|
||||
maximum_number_of_retransmissions: value.maximum_number_of_retransmissions,
|
||||
deterministic_route_selection: value
|
||||
.deterministic_route_selection
|
||||
.unwrap_or(def.deterministic_route_selection),
|
||||
disable_main_poisson_packet_distribution: value
|
||||
.disable_main_poisson_packet_distribution
|
||||
.unwrap_or(def.disable_main_poisson_packet_distribution),
|
||||
|
||||
@@ -68,9 +68,9 @@ pub async fn current_network_topology_async(
|
||||
|
||||
let api_client = NymApiClient::new(url);
|
||||
let mixnodes = api_client
|
||||
.get_all_basic_active_mixing_assigned_nodes(None)
|
||||
.get_all_basic_active_mixing_assigned_nodes()
|
||||
.await?;
|
||||
let gateways = api_client.get_all_basic_entry_assigned_nodes(None).await?;
|
||||
let gateways = api_client.get_all_basic_entry_assigned_nodes().await?;
|
||||
|
||||
Ok(NymTopology::from_basic(&mixnodes, &gateways).into())
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ log.workspace = true
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "net", "io-util"] }
|
||||
tokio-stream = { workspace = true }
|
||||
time = { workspace = true }
|
||||
|
||||
nym-authenticator-requests = { path = "../authenticator-requests" }
|
||||
nym-credential-verification = { path = "../credential-verification" }
|
||||
|
||||
@@ -20,6 +20,7 @@ use tokio::sync::mpsc::{self, Receiver, Sender};
|
||||
pub(crate) mod error;
|
||||
pub mod peer_controller;
|
||||
pub mod peer_handle;
|
||||
pub mod peer_storage_manager;
|
||||
|
||||
pub struct WgApiWrapper {
|
||||
inner: WGApi,
|
||||
@@ -118,7 +119,7 @@ pub async fn start_wireguard<St: nym_gateway_storage::Storage + Clone + 'static>
|
||||
storage
|
||||
.insert_wireguard_peer(peer, bandwidth_manager.is_some())
|
||||
.await?;
|
||||
peer_bandwidth_managers.insert(peer.public_key.clone(), bandwidth_manager);
|
||||
peer_bandwidth_managers.insert(peer.public_key.clone(), (bandwidth_manager, peer.clone()));
|
||||
}
|
||||
wg_api.create_interface()?;
|
||||
let interface_config = InterfaceConfiguration {
|
||||
|
||||
@@ -20,9 +20,9 @@ use std::{collections::HashMap, sync::Arc};
|
||||
use tokio::sync::{mpsc, RwLock};
|
||||
use tokio_stream::{wrappers::IntervalStream, StreamExt};
|
||||
|
||||
use crate::peer_handle::PeerHandle;
|
||||
use crate::WgApiWrapper;
|
||||
use crate::{error::Error, peer_handle::SharedBandwidthStorageManager};
|
||||
use crate::{peer_handle::PeerHandle, peer_storage_manager::PeerStorageManager};
|
||||
|
||||
pub enum PeerControlRequest {
|
||||
AddPeer {
|
||||
@@ -79,7 +79,7 @@ impl<St: Storage + Clone + 'static> PeerController<St> {
|
||||
storage: St,
|
||||
wg_api: Arc<WgApiWrapper>,
|
||||
initial_host_information: Host,
|
||||
bw_storage_managers: HashMap<Key, Option<SharedBandwidthStorageManager<St>>>,
|
||||
bw_storage_managers: HashMap<Key, (Option<SharedBandwidthStorageManager<St>>, Peer)>,
|
||||
request_tx: mpsc::Sender<PeerControlRequest>,
|
||||
request_rx: mpsc::Receiver<PeerControlRequest>,
|
||||
task_client: nym_task::TaskClient,
|
||||
@@ -88,11 +88,16 @@ impl<St: Storage + Clone + 'static> PeerController<St> {
|
||||
tokio::time::interval(DEFAULT_PEER_TIMEOUT_CHECK),
|
||||
);
|
||||
let host_information = Arc::new(RwLock::new(initial_host_information));
|
||||
for (public_key, bandwidth_storage_manager) in bw_storage_managers.iter() {
|
||||
let mut handle = PeerHandle::new(
|
||||
for (public_key, (bandwidth_storage_manager, peer)) in bw_storage_managers.iter() {
|
||||
let peer_storage_manager = PeerStorageManager::new(
|
||||
storage.clone(),
|
||||
peer.clone(),
|
||||
bandwidth_storage_manager.is_some(),
|
||||
);
|
||||
let mut handle = PeerHandle::new(
|
||||
public_key.clone(),
|
||||
host_information.clone(),
|
||||
peer_storage_manager,
|
||||
bandwidth_storage_manager.clone(),
|
||||
request_tx.clone(),
|
||||
&task_client,
|
||||
@@ -103,6 +108,10 @@ impl<St: Storage + Clone + 'static> PeerController<St> {
|
||||
}
|
||||
});
|
||||
}
|
||||
let bw_storage_managers = bw_storage_managers
|
||||
.into_iter()
|
||||
.map(|(k, (m, _))| (k, m))
|
||||
.collect();
|
||||
|
||||
PeerController {
|
||||
storage,
|
||||
@@ -184,10 +193,15 @@ impl<St: Storage + Clone + 'static> PeerController<St> {
|
||||
Self::generate_bandwidth_manager(self.storage.clone(), &peer.public_key)
|
||||
.await?
|
||||
.map(|bw_m| Arc::new(RwLock::new(bw_m)));
|
||||
let mut handle = PeerHandle::new(
|
||||
let peer_storage_manager = PeerStorageManager::new(
|
||||
self.storage.clone(),
|
||||
peer.clone(),
|
||||
bandwidth_storage_manager.is_some(),
|
||||
);
|
||||
let mut handle = PeerHandle::new(
|
||||
peer.public_key.clone(),
|
||||
self.host_information.clone(),
|
||||
peer_storage_manager,
|
||||
bandwidth_storage_manager.clone(),
|
||||
self.request_tx.clone(),
|
||||
&self.task_client,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::peer_controller::PeerControlRequest;
|
||||
use crate::peer_storage_manager::PeerStorageManager;
|
||||
use defguard_wireguard_rs::host::Peer;
|
||||
use defguard_wireguard_rs::{host::Host, key::Key};
|
||||
use futures::channel::oneshot;
|
||||
@@ -21,9 +22,9 @@ pub(crate) type SharedBandwidthStorageManager<St> = Arc<RwLock<BandwidthStorageM
|
||||
const AUTO_REMOVE_AFTER: Duration = Duration::from_secs(60 * 60 * 24 * 30); // 30 days
|
||||
|
||||
pub struct PeerHandle<St> {
|
||||
storage: St,
|
||||
public_key: Key,
|
||||
host_information: Arc<RwLock<Host>>,
|
||||
peer_storage_manager: PeerStorageManager<St>,
|
||||
bandwidth_storage_manager: Option<SharedBandwidthStorageManager<St>>,
|
||||
request_tx: mpsc::Sender<PeerControlRequest>,
|
||||
timeout_check_interval: IntervalStream,
|
||||
@@ -33,9 +34,9 @@ pub struct PeerHandle<St> {
|
||||
|
||||
impl<St: Storage + Clone + 'static> PeerHandle<St> {
|
||||
pub fn new(
|
||||
storage: St,
|
||||
public_key: Key,
|
||||
host_information: Arc<RwLock<Host>>,
|
||||
peer_storage_manager: PeerStorageManager<St>,
|
||||
bandwidth_storage_manager: Option<SharedBandwidthStorageManager<St>>,
|
||||
request_tx: mpsc::Sender<PeerControlRequest>,
|
||||
task_client: &TaskClient,
|
||||
@@ -46,9 +47,9 @@ impl<St: Storage + Clone + 'static> PeerHandle<St> {
|
||||
let mut task_client = task_client.fork(format!("peer-{public_key}"));
|
||||
task_client.disarm();
|
||||
PeerHandle {
|
||||
storage,
|
||||
public_key,
|
||||
host_information,
|
||||
peer_storage_manager,
|
||||
bandwidth_storage_manager,
|
||||
request_tx,
|
||||
timeout_check_interval,
|
||||
@@ -84,16 +85,19 @@ impl<St: Storage + Clone + 'static> PeerHandle<St> {
|
||||
.ok_or(Error::InconsistentConsumedBytes)?
|
||||
.try_into()
|
||||
.map_err(|_| Error::InconsistentConsumedBytes)?;
|
||||
if spent_bandwidth > 0
|
||||
&& bandwidth_manager
|
||||
if spent_bandwidth > 0 {
|
||||
self.peer_storage_manager.update_trx(kernel_peer);
|
||||
if bandwidth_manager
|
||||
.write()
|
||||
.await
|
||||
.try_use_bandwidth(spent_bandwidth)
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
let success = self.remove_peer().await?;
|
||||
return Ok(!success);
|
||||
{
|
||||
let success = self.remove_peer().await?;
|
||||
self.peer_storage_manager.remove_peer();
|
||||
return Ok(!success);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if SystemTime::now().duration_since(self.startup_timestamp)? >= AUTO_REMOVE_AFTER {
|
||||
@@ -132,7 +136,7 @@ impl<St: Storage + Clone + 'static> PeerHandle<St> {
|
||||
// the host information hasn't beed updated yet
|
||||
continue;
|
||||
};
|
||||
let Some(storage_peer) = self.storage.get_wireguard_peer(&self.public_key.to_string()).await? else {
|
||||
let Some(storage_peer) = self.peer_storage_manager.get_peer() else {
|
||||
log::debug!("Peer {:?} not in storage anymore, shutting down handle", self.public_key);
|
||||
return Ok(());
|
||||
};
|
||||
@@ -141,12 +145,18 @@ impl<St: Storage + Clone + 'static> PeerHandle<St> {
|
||||
return Ok(());
|
||||
} else {
|
||||
// Update storage values
|
||||
self.storage.insert_wireguard_peer(&kernel_peer, self.bandwidth_storage_manager.is_some()).await?;
|
||||
self.peer_storage_manager.sync_storage_peer().await?;
|
||||
}
|
||||
}
|
||||
|
||||
_ = self.task_client.recv() => {
|
||||
log::trace!("PeerHandle: Received shutdown");
|
||||
if let Some(bandwidth_manager) = &self.bandwidth_storage_manager {
|
||||
if let Err(e) = bandwidth_manager.write().await.sync_storage_bandwidth().await {
|
||||
log::error!("Storage sync failed - {e}, unaccounted bandwidth might have been consumed");
|
||||
}
|
||||
}
|
||||
log::trace!("PeerHandle: Finished shutdown");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::Error;
|
||||
use defguard_wireguard_rs::host::Peer;
|
||||
use nym_gateway_storage::models::WireguardPeer;
|
||||
use nym_gateway_storage::Storage;
|
||||
use std::time::Duration;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
const DEFAULT_PEER_MAX_FLUSHING_RATE: Duration = Duration::from_secs(60 * 60 * 24); // 24h
|
||||
const DEFAULT_PEER_MAX_DELTA_FLUSHING_AMOUNT: u64 = 512 * 1024 * 1024; // 512MB
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct PeerFlushingBehaviourConfig {
|
||||
/// Defines maximum delay between peer information being flushed to the persistent storage.
|
||||
pub peer_max_flushing_rate: Duration,
|
||||
|
||||
/// Defines a maximum change in peer before it gets flushed to the persistent storage.
|
||||
pub peer_max_delta_flushing_amount: u64,
|
||||
}
|
||||
|
||||
impl Default for PeerFlushingBehaviourConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
peer_max_flushing_rate: DEFAULT_PEER_MAX_FLUSHING_RATE,
|
||||
peer_max_delta_flushing_amount: DEFAULT_PEER_MAX_DELTA_FLUSHING_AMOUNT,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PeerStorageManager<S> {
|
||||
pub(crate) storage: S,
|
||||
pub(crate) peer_information: Option<PeerInformation>,
|
||||
pub(crate) cfg: PeerFlushingBehaviourConfig,
|
||||
pub(crate) with_client_id: bool,
|
||||
}
|
||||
|
||||
impl<S: Storage + Clone + 'static> PeerStorageManager<S> {
|
||||
pub(crate) fn new(storage: S, peer: Peer, with_client_id: bool) -> Self {
|
||||
let peer_information = Some(PeerInformation::new(peer));
|
||||
Self {
|
||||
storage,
|
||||
peer_information,
|
||||
cfg: PeerFlushingBehaviourConfig::default(),
|
||||
with_client_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_peer(&self) -> Option<WireguardPeer> {
|
||||
self.peer_information
|
||||
.as_ref()
|
||||
.map(|p| p.peer.clone().into())
|
||||
}
|
||||
|
||||
pub(crate) fn remove_peer(&mut self) {
|
||||
self.peer_information = None;
|
||||
}
|
||||
|
||||
pub(crate) fn update_trx(&mut self, kernel_peer: &Peer) {
|
||||
if let Some(peer_information) = self.peer_information.as_mut() {
|
||||
peer_information.update_trx_bytes(kernel_peer.tx_bytes, kernel_peer.rx_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn sync_storage_peer(&mut self) -> Result<(), Error> {
|
||||
let Some(peer_information) = self.peer_information.as_mut() else {
|
||||
return Ok(());
|
||||
};
|
||||
if !peer_information.should_sync(self.cfg) {
|
||||
return Ok(());
|
||||
}
|
||||
if self
|
||||
.storage
|
||||
.get_wireguard_peer(&peer_information.peer().public_key.to_string())
|
||||
.await?
|
||||
.is_none()
|
||||
{
|
||||
self.peer_information = None;
|
||||
return Ok(());
|
||||
}
|
||||
self.storage
|
||||
.insert_wireguard_peer(peer_information.peer(), self.with_client_id)
|
||||
.await?;
|
||||
|
||||
peer_information.resync_peer_with_storage();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct PeerInformation {
|
||||
pub(crate) peer: Peer,
|
||||
pub(crate) last_synced: OffsetDateTime,
|
||||
|
||||
pub(crate) bytes_delta_since_sync: u64,
|
||||
}
|
||||
|
||||
impl PeerInformation {
|
||||
pub fn new(peer: Peer) -> PeerInformation {
|
||||
PeerInformation {
|
||||
peer,
|
||||
last_synced: OffsetDateTime::now_utc(),
|
||||
bytes_delta_since_sync: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn should_sync(&self, cfg: PeerFlushingBehaviourConfig) -> bool {
|
||||
if self.bytes_delta_since_sync >= cfg.peer_max_delta_flushing_amount {
|
||||
return true;
|
||||
}
|
||||
|
||||
if self.last_synced + cfg.peer_max_flushing_rate < OffsetDateTime::now_utc()
|
||||
&& self.bytes_delta_since_sync != 0
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub(crate) fn peer(&self) -> &Peer {
|
||||
&self.peer
|
||||
}
|
||||
|
||||
pub(crate) fn update_trx_bytes(&mut self, tx_bytes: u64, rx_bytes: u64) {
|
||||
self.bytes_delta_since_sync += tx_bytes.saturating_sub(self.peer.tx_bytes)
|
||||
+ rx_bytes.saturating_sub(self.peer.rx_bytes);
|
||||
self.peer.tx_bytes = tx_bytes;
|
||||
self.peer.rx_bytes = rx_bytes;
|
||||
}
|
||||
|
||||
pub(crate) fn resync_peer_with_storage(&mut self) {
|
||||
self.bytes_delta_since_sync = 0;
|
||||
self.last_synced = OffsetDateTime::now_utc();
|
||||
}
|
||||
}
|
||||
@@ -60,8 +60,8 @@
|
||||
},
|
||||
"version_score_params": {
|
||||
"default": {
|
||||
"penalty": "0.8",
|
||||
"penalty_scaling": "2"
|
||||
"penalty": "0.995",
|
||||
"penalty_scaling": "1.65"
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
@@ -261,7 +261,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"VersionScoreFormulaParams": {
|
||||
"description": "Given the formula of version_score = penalty ^ (num_versions_behind ^ penalty_scaling) define the relevant parameters",
|
||||
"description": "Given the formula of version_score = penalty ^ (versions_behind_factor ^ penalty_scaling) define the relevant parameters",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"penalty",
|
||||
@@ -1308,12 +1308,6 @@
|
||||
"ConfigScoreParamsUpdate": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"current_nym_node_semver": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"version_score_formula_params": {
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -1938,7 +1932,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"VersionScoreFormulaParams": {
|
||||
"description": "Given the formula of version_score = penalty ^ (num_versions_behind ^ penalty_scaling) define the relevant parameters",
|
||||
"description": "Given the formula of version_score = penalty ^ (versions_behind_factor ^ penalty_scaling) define the relevant parameters",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"penalty",
|
||||
@@ -2043,6 +2037,54 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "Get the current expected version of a Nym Node.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_current_nym_node_version"
|
||||
],
|
||||
"properties": {
|
||||
"get_current_nym_node_version": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "Get the version history of Nym Node.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_nym_node_version_history"
|
||||
],
|
||||
"properties": {
|
||||
"get_nym_node_version_history": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"limit": {
|
||||
"description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"start_after": {
|
||||
"description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "Gets the current parameters used for reward calculation.",
|
||||
"type": "object",
|
||||
@@ -3443,8 +3485,8 @@
|
||||
},
|
||||
"version_score_params": {
|
||||
"default": {
|
||||
"penalty": "0.8",
|
||||
"penalty_scaling": "2"
|
||||
"penalty": "0.995",
|
||||
"penalty_scaling": "1.65"
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
@@ -3512,7 +3554,7 @@
|
||||
"additionalProperties": false
|
||||
},
|
||||
"VersionScoreFormulaParams": {
|
||||
"description": "Given the formula of version_score = penalty ^ (num_versions_behind ^ penalty_scaling) define the relevant parameters",
|
||||
"description": "Given the formula of version_score = penalty ^ (versions_behind_factor ^ penalty_scaling) define the relevant parameters",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"penalty",
|
||||
@@ -4180,6 +4222,111 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_current_nym_node_version": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "CurrentNymNodeVersionResponse",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"version": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/HistoricalNymNodeVersionEntry"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"HistoricalNymNodeVersion": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"difference_since_genesis",
|
||||
"introduced_at_height",
|
||||
"semver"
|
||||
],
|
||||
"properties": {
|
||||
"difference_since_genesis": {
|
||||
"description": "The absolute version difference as compared against the first version introduced into the contract.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TotalVersionDifference"
|
||||
}
|
||||
]
|
||||
},
|
||||
"introduced_at_height": {
|
||||
"description": "Block height of when this version has been added to the contract",
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"semver": {
|
||||
"description": "Version of the nym node that is going to be used for determining the version score of a node. note: value stored here is pre-validated `semver::Version`",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"HistoricalNymNodeVersionEntry": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"version_information"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "The unique, ordered, id of this particular entry",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"version_information": {
|
||||
"description": "Data associated with this particular version",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/HistoricalNymNodeVersion"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"TotalVersionDifference": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"major",
|
||||
"minor",
|
||||
"patch",
|
||||
"prerelease"
|
||||
],
|
||||
"properties": {
|
||||
"major": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"minor": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"patch": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"prerelease": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_delegation_details": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "NodeDelegationResponse",
|
||||
@@ -7119,6 +7266,119 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_nym_node_version_history": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "NymNodeVersionHistoryResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"history"
|
||||
],
|
||||
"properties": {
|
||||
"history": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/HistoricalNymNodeVersionEntry"
|
||||
}
|
||||
},
|
||||
"start_next_after": {
|
||||
"description": "Field indicating paging information for the following queries if the caller wishes to get further entries.",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"HistoricalNymNodeVersion": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"difference_since_genesis",
|
||||
"introduced_at_height",
|
||||
"semver"
|
||||
],
|
||||
"properties": {
|
||||
"difference_since_genesis": {
|
||||
"description": "The absolute version difference as compared against the first version introduced into the contract.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TotalVersionDifference"
|
||||
}
|
||||
]
|
||||
},
|
||||
"introduced_at_height": {
|
||||
"description": "Block height of when this version has been added to the contract",
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"semver": {
|
||||
"description": "Version of the nym node that is going to be used for determining the version score of a node. note: value stored here is pre-validated `semver::Version`",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"HistoricalNymNodeVersionEntry": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"version_information"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "The unique, ordered, id of this particular entry",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"version_information": {
|
||||
"description": "Data associated with this particular version",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/HistoricalNymNodeVersion"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"TotalVersionDifference": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"major",
|
||||
"minor",
|
||||
"patch",
|
||||
"prerelease"
|
||||
],
|
||||
"properties": {
|
||||
"major": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"minor": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"patch": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"prerelease": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_nym_nodes_detailed_paged": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "PagedNymNodeDetailsResponse",
|
||||
@@ -10554,15 +10814,10 @@
|
||||
"ConfigScoreParams": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"current_nym_node_semver",
|
||||
"version_score_formula_params",
|
||||
"version_weights"
|
||||
],
|
||||
"properties": {
|
||||
"current_nym_node_semver": {
|
||||
"description": "Current version of the nym node that is going to be used for determining the version score of a node. note: value stored here is pre-validated `semver::Version`",
|
||||
"type": "string"
|
||||
},
|
||||
"version_score_formula_params": {
|
||||
"description": "Defines the parameters of the formula for calculating the version score",
|
||||
"allOf": [
|
||||
@@ -10752,7 +11007,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"VersionScoreFormulaParams": {
|
||||
"description": "Given the formula of version_score = penalty ^ (num_versions_behind ^ penalty_scaling) define the relevant parameters",
|
||||
"description": "Given the formula of version_score = penalty ^ (versions_behind_factor ^ penalty_scaling) define the relevant parameters",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"penalty",
|
||||
@@ -10826,15 +11081,10 @@
|
||||
"ConfigScoreParams": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"current_nym_node_semver",
|
||||
"version_score_formula_params",
|
||||
"version_weights"
|
||||
],
|
||||
"properties": {
|
||||
"current_nym_node_semver": {
|
||||
"description": "Current version of the nym node that is going to be used for determining the version score of a node. note: value stored here is pre-validated `semver::Version`",
|
||||
"type": "string"
|
||||
},
|
||||
"version_score_formula_params": {
|
||||
"description": "Defines the parameters of the formula for calculating the version score",
|
||||
"allOf": [
|
||||
@@ -10988,7 +11238,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"VersionScoreFormulaParams": {
|
||||
"description": "Given the formula of version_score = penalty ^ (num_versions_behind ^ penalty_scaling) define the relevant parameters",
|
||||
"description": "Given the formula of version_score = penalty ^ (versions_behind_factor ^ penalty_scaling) define the relevant parameters",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"penalty",
|
||||
|
||||
@@ -1027,12 +1027,6 @@
|
||||
"ConfigScoreParamsUpdate": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"current_nym_node_semver": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"version_score_formula_params": {
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -1657,7 +1651,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"VersionScoreFormulaParams": {
|
||||
"description": "Given the formula of version_score = penalty ^ (num_versions_behind ^ penalty_scaling) define the relevant parameters",
|
||||
"description": "Given the formula of version_score = penalty ^ (versions_behind_factor ^ penalty_scaling) define the relevant parameters",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"penalty",
|
||||
|
||||
@@ -56,8 +56,8 @@
|
||||
},
|
||||
"version_score_params": {
|
||||
"default": {
|
||||
"penalty": "0.8",
|
||||
"penalty_scaling": "2"
|
||||
"penalty": "0.995",
|
||||
"penalty_scaling": "1.65"
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
@@ -257,7 +257,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"VersionScoreFormulaParams": {
|
||||
"description": "Given the formula of version_score = penalty ^ (num_versions_behind ^ penalty_scaling) define the relevant parameters",
|
||||
"description": "Given the formula of version_score = penalty ^ (versions_behind_factor ^ penalty_scaling) define the relevant parameters",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"penalty",
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
},
|
||||
"version_score_params": {
|
||||
"default": {
|
||||
"penalty": "0.8",
|
||||
"penalty_scaling": "2"
|
||||
"penalty": "0.995",
|
||||
"penalty_scaling": "1.65"
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
@@ -86,7 +86,7 @@
|
||||
"additionalProperties": false
|
||||
},
|
||||
"VersionScoreFormulaParams": {
|
||||
"description": "Given the formula of version_score = penalty ^ (num_versions_behind ^ penalty_scaling) define the relevant parameters",
|
||||
"description": "Given the formula of version_score = penalty ^ (versions_behind_factor ^ penalty_scaling) define the relevant parameters",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"penalty",
|
||||
|
||||
@@ -85,6 +85,54 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "Get the current expected version of a Nym Node.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_current_nym_node_version"
|
||||
],
|
||||
"properties": {
|
||||
"get_current_nym_node_version": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "Get the version history of Nym Node.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_nym_node_version_history"
|
||||
],
|
||||
"properties": {
|
||||
"get_nym_node_version_history": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"limit": {
|
||||
"description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"start_after": {
|
||||
"description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "Gets the current parameters used for reward calculation.",
|
||||
"type": "object",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user