Compare commits

..

53 Commits

Author SHA1 Message Date
dynco-nym e06d442e95 Restore Location fields (#5208)
* Add latitude/longitude fields to Location

* Add regression test

* Bump package version

* Load secret during workflow
2024-12-03 18:35:56 +01:00
dynco-nym fc79f739d4 Fix overflow (#5204) 2024-12-03 10:20:28 +01:00
benedetta davico 6ee8ccbeaa Merge pull request #5199 from nymtech/merge/release/2024.14-crunch
merge crunch into develop
2024-12-02 13:21:04 +01:00
Jędrzej Stuczyński cfebd14655 Merge branch 'release/2024.14-crunch' into merge/release/2024.14-crunch 2024-12-02 11:21:09 +00:00
Simon Wicky 4851614375 NS API - Gateway stats scraping (#5180)
* squashed commit before rebasing

* removed blank lines
2024-12-02 12:15:30 +01:00
benedetta davico ec502f46f0 Merge pull request #5196 from nymtech/release/2024.13-magura-patched-v2
Merging magura drift into crunch
2024-12-02 12:13:12 +01:00
dynco-nym a9e62889c3 Remove explorer dependency (#5190)
* Move monitor code to a struct
- to store state in a struct

* explorer deprecation wip

* Replace explorer with ipinfo calls

* PR feedback

* Fix clippy

* Bump package version

* Remove ipinfo crate due to openssl dep

* Add remaining bandwidth log
2024-11-29 16:45:55 +01:00
import this 074d705448 [DOCs/operators]: Magura-drift - second patch (#5194)
* syntax edits

* new version harsh

* changelog info - ready to review
2024-11-29 13:34:58 +00:00
benedettadavico 4a9a5579c4 update changelog 2024-11-29 14:06:32 +01:00
benedetta davico 96180275f8 Update Cargo.toml 2024-11-29 13:57:57 +01:00
Bogdan-Ștefan Neacşu ab20260a2f Guard storage access with cache (#5193)
* Guard storage access with cache

* Do the sync way less freq

* Change sync behaviour for bandwidth too

* Use bigger delta
2024-11-29 14:56:39 +02:00
Tommy Verrall ec1c564c2b Merge pull request #5150 from nymtech/dependabot/npm_and_yarn/testnet-faucet/cross-spawn-7.0.6
build(deps): bump cross-spawn from 7.0.3 to 7.0.6 in /testnet-faucet
2024-11-29 12:27:29 +00:00
Tommy Verrall bdf97bcbd6 Merge pull request #5151 from nymtech/fix/validator-rewarder-push-docker
fix: validator-rewarder GH job
2024-11-29 12:26:55 +00:00
Jędrzej Stuczyński 889d464e98 improvement: make internal gateway clients use the same topology cache (#5191) 2024-11-29 09:45:12 +00:00
Jędrzej Stuczyński 56206433e6 chore: apply 1.84 linter suggestions (#5192) 2024-11-29 09:20:45 +00:00
Tommy Verrall 8e9d01c47b Merge pull request #5189 from nymtech/fix/network-tunnel-script
Fix/network tunnel script
2024-11-28 15:47:56 +00:00
Tommy Verrall f95f01959c fix multiple forwarding calls
also add more logging around joke section
2024-11-28 12:29:29 +01:00
Tommy Verrall 42de620951 typo 2024-11-28 12:06:03 +01:00
Tommy Verrall af9f7b1c0f formatting 2024-11-28 12:02:45 +01:00
Tommy Verrall 7c1ad7d20c add more output on joke commands
this should help the end users debug their machines further
2024-11-28 12:02:13 +01:00
Tommy Verrall 9ac0595a35 remove duplicate iptable rules 2024-11-28 11:49:29 +01:00
Tommy Verrall c6c138167d Merge pull request #5186 from nymtech/fix/network-tunnel-script
fix for the network tunnel manager script
2024-11-28 09:39:50 +00:00
Tommy Verrall 09633dead1 add the enable ip forwarding method 2024-11-28 10:38:13 +01:00
dynco-nym cd2ad0adbb Update dir in workflow (#5185) 2024-11-27 17:50:55 +01:00
benedetta davico 0b52224917 Update network_tunnel_manager.sh 2024-11-27 17:26:37 +01:00
dynco-nym 96ebe3fc4f Fix overflow (#5184) 2024-11-27 17:07:01 +01:00
Drazen Urch 8e713d43e1 Add monitor_run and testing_route indexes (#5182) 2024-11-27 11:07:39 +01:00
dynco-nym e7f806219c Move NS client to separate package under NS API (#5171)
* Move client code to NS API

* Move client to separate package

* Move things around

* Adjust run scripts

* rustfmt

* Add client to workspace
2024-11-26 15:59:42 +01:00
benedettadavico 35aa7e338d bump binary versions 2024-11-26 15:01:06 +01:00
Jędrzej Stuczyński 2a60b2f057 bugfix: fixed nym-node config migrations (again) (#5179) 2024-11-26 09:41:18 +00:00
import this edd3f9108a [DOCs/operators]: Guide to change wg private address (#5178) 2024-11-26 09:32:09 +00:00
Jędrzej Stuczyński dcde4c8df1 bugfix: use default value for verloc config when deserialising missing values (#5177) 2024-11-25 17:54:32 +00:00
Tommy Verrall fcaa32284b Merge pull request #5175 from nymtech/fix/empty_allowed
Remove peers with no allowed ip from storage
2024-11-25 17:36:33 +00:00
Tommy Verrall 3c56977fb5 Merge pull request #5176 from nymtech/script-update
Script update
2024-11-25 17:35:41 +00:00
Tommy Verrall 5f3bb5db82 remove command features 2024-11-25 17:52:49 +01:00
Tommy Verrall 1b84639c34 re-add the configure icmp command 2024-11-25 17:48:03 +01:00
Tommy Verrall 546a486f9f script overhaul
- improved iptables management: apply_iptables_rules and apply_iptables_rules_wg now automatically remove duplicate rules before reapplying them, ensuring a clean setup without disrupting iptables
- consolidated joke feature: unified the "joke through the mixnet" logic into a generic function, allowing it to work seamlessly across any specified interface
- enhanced tunnel checks: added check_nym_wg_tun alongside check_nymtun_iptables, making it easier to verify the state of both tunnels
- reduced error-prone behavior: simplified workflows to avoid issues caused by running commands multiple times

how to use:
1. download the script and make it executable:
   curl -L -o network_tunnel_manager.sh https://raw.download.github.of.this.file && chmod u+x network_tunnel_manager.sh

2. run the following commands as needed:
   - apply_iptables_rules: apply and clean iptables rules for nymtun0
   - apply_iptables_rules_wg: apply and clean iptables rules for nymwg
   - check_ipv6_ipv4_forwarding: verify if ipv4 and ipv6 forwarding are enabled
   - check_ip_routing: display the current ipv4 and ipv6 routing tables

tldr:
- improved iptables handling to avoid duplicates
- unified functionality for better maintainability
- reduced potential errors when rerunning commands
2024-11-25 17:45:10 +01:00
Bogdan-Ștefan Neacşu fa72f90bfa Remove peers with no allowed ip from storage 2024-11-25 16:44:23 +00:00
Jędrzej Stuczyński 5668e123d9 introduced initial internal commands for nym-cli: ecash key and request generation (#5174)
* introduced initial internal commands for nym-cli: ecash key and request generation

* reduced args logging level
2024-11-25 15:41:49 +00:00
import this 27637ae6b4 [DOCs/operators]: Routine guides update with release changes (#5173)
* finish doc updates - ready for review

* info to warning change

* add non root guide and a new error

* syntax fix

* syntax edit
2024-11-25 14:27:52 +00:00
Jędrzej Stuczyński 12b9aefa99 bugfix: correctly expose ecash-related data on nym-api (#5155)
* fixed signer related endpoints

* fixed aggregation of partial data if the api is not a signer

* fixes to swagger docs for global ecash endpoints

* remove unused axum_macros

* fixed test traits
2024-11-25 08:39:55 +00:00
benedetta davico 0041937ed3 Merge pull request #5170 from nymtech/merge/release/2024.13-magura-patched
merge patched magura into develop
2024-11-25 09:06:32 +01:00
Jędrzej Stuczyński 0e5bd966dd Merge branch 'release/2024.13-magura-patched' into develop 2024-11-22 15:53:59 +00:00
Jędrzej Stuczyński 6acd936368 bugfix: additional improvements to nym-api db performance (#5168)
* added statement logging to nym-api db

* adding additional pool options

* dont blow up upon failing to submit network monitor results

* store in-memory cache of node database ids
2024-11-22 15:52:26 +00:00
benedettadavico 01c7b2819e update changelog 2024-11-22 10:50:59 +01:00
Simon Wicky 042a8a58aa start session collection for exit gateways (#5148) (#5161) 2024-11-22 09:12:01 +01:00
benedettadavico d8ab2a8f15 update version for clients and nym-node 2024-11-22 07:25:54 +01:00
Jędrzej Stuczyński d6d2239685 implement optional cap for number of ack retransmissions (#5158)
* implement optional cap for number of ack retransmissions

* Wasm
2024-11-21 18:57:24 +00:00
Jędrzej Stuczyński 3d704fbbf1 change: make nym-api optionally ignore nodes with illegal ip addresses, like loopback (#5159) 2024-11-21 18:54:14 +00:00
Jędrzej Stuczyński 119c36b0bb added 'deterministic_route_selection' for sphinx packet route selection (#5157)
* added 'deterministic_route_selection' for sphinx packet route selection

* clippy + wasm

* Switch to ChaCha8Rng for deterministic route generation

---------

Co-authored-by: durch <durch@users.noreply.github.com>
2024-11-21 18:21:01 +00:00
Jędrzej Stuczyński 6b5b97199b fix: tracing logger to output to stderr 2024-11-19 17:47:13 +00:00
dependabot[bot] d2e85f2bfe build(deps): bump cross-spawn from 7.0.3 to 7.0.6 in /testnet-faucet
Bumps [cross-spawn](https://github.com/moxystudio/node-cross-spawn) from 7.0.3 to 7.0.6.
- [Changelog](https://github.com/moxystudio/node-cross-spawn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/moxystudio/node-cross-spawn/compare/v7.0.3...v7.0.6)

---
updated-dependencies:
- dependency-name: cross-spawn
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-19 10:32:10 +00:00
Fran Arbanas b28e953a2b fix: validator-rewarder GH job 2024-11-12 17:16:59 +01:00
241 changed files with 3235 additions and 3255 deletions
+1
View File
@@ -34,6 +34,7 @@ jobs:
runs-on: ${{ matrix.os }}
env:
CARGO_TERM_COLOR: always
IPINFO_API_TOKEN: ${{ secrets.IPINFO_API_TOKEN }}
steps:
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler
@@ -8,7 +8,7 @@ on:
description: Which gateway probe git ref to build the image with
env:
WORKING_DIRECTORY: "nym-node-status-agent"
WORKING_DIRECTORY: "nym-node-status-api/nym-node-status-agent"
CONTAINER_NAME: "node-status-agent"
jobs:
@@ -58,4 +58,4 @@ jobs:
- name: BuildAndPushImageOnHarbor
run: |
docker build --build-arg GIT_REF=${{ github.event.inputs.gateway_probe_git_ref }} -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.result }}-${{ steps.cleanup_gateway_probe_ref.outputs.git_ref }}
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
+1 -1
View File
@@ -3,7 +3,7 @@ on:
workflow_dispatch:
env:
WORKING_DIRECTORY: "nym-node-status-api"
WORKING_DIRECTORY: "nym-node-status-api/nym-node-status-api"
CONTAINER_NAME: "node-status-api"
jobs:
@@ -29,7 +29,7 @@ jobs:
uses: mikefarah/yq@v4.44.5
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-credential-proxy/Cargo.toml
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
- name: Remove existing tag if exists
run: |
+10
View File
@@ -4,6 +4,16 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [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
+60 -317
View File
@@ -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",
@@ -4840,7 +4701,7 @@ dependencies = [
[[package]]
name = "nym-cli"
version = "1.1.44"
version = "1.1.45"
dependencies = [
"anyhow",
"base64 0.22.1",
@@ -4891,12 +4752,14 @@ dependencies = [
"nym-coconut-dkg-common",
"nym-config",
"nym-contracts-common",
"nym-credential-proxy-requests",
"nym-credential-storage",
"nym-credential-utils",
"nym-credentials",
"nym-credentials-interface",
"nym-crypto",
"nym-ecash-contract-common",
"nym-ecash-time",
"nym-id",
"nym-mixnet-contract-common",
"nym-multisig-contract-common",
@@ -4921,7 +4784,7 @@ dependencies = [
[[package]]
name = "nym-client"
version = "1.1.43"
version = "1.1.45"
dependencies = [
"bs58",
"clap 4.5.20",
@@ -5146,16 +5009,6 @@ dependencies = [
"nym-multisig-contract-common",
]
[[package]]
name = "nym-common-models"
version = "0.1.0"
dependencies = [
"anyhow",
"bincode",
"nym-crypto",
"serde",
]
[[package]]
name = "nym-compact-ecash"
version = "0.1.0"
@@ -5683,6 +5536,7 @@ version = "0.1.0"
dependencies = [
"nym-credentials-interface",
"nym-sphinx",
"nym-statistics-common",
"sqlx",
"thiserror",
"time",
@@ -6038,7 +5892,7 @@ dependencies = [
[[package]]
name = "nym-network-requester"
version = "1.1.44"
version = "1.1.46"
dependencies = [
"addr",
"anyhow",
@@ -6089,7 +5943,7 @@ dependencies = [
[[package]]
name = "nym-node"
version = "1.1.10"
version = "1.1.12"
dependencies = [
"anyhow",
"bip39",
@@ -6195,30 +6049,23 @@ name = "nym-node-status-agent"
version = "1.0.0-rc.1"
dependencies = [
"anyhow",
"bincode",
"chrono",
"clap 4.5.20",
"nym-bin-common",
"nym-common-models",
"nym-crypto",
"nym-node-status-client",
"rand",
"reqwest 0.12.4",
"serde",
"serde_json",
"tempfile",
"tokio",
"tokio-util",
"tracing",
"tracing-subscriber",
]
[[package]]
name = "nym-node-status-api"
version = "1.0.0-rc.2"
version = "1.0.0-rc.6"
dependencies = [
"anyhow",
"axum 0.7.7",
"bincode",
"chrono",
"clap 4.5.20",
"cosmwasm-std",
@@ -6226,11 +6073,12 @@ dependencies = [
"futures-util",
"moka",
"nym-bin-common",
"nym-common-models",
"nym-crypto",
"nym-explorer-client",
"nym-network-defaults",
"nym-node-requests",
"nym-node-status-client",
"nym-statistics-common",
"nym-task",
"nym-validator-client",
"regex",
@@ -6242,6 +6090,7 @@ dependencies = [
"strum 0.26.3",
"strum_macros 0.26.4",
"thiserror",
"time",
"tokio",
"tokio-util",
"tower-http",
@@ -6253,6 +6102,21 @@ dependencies = [
"utoipauto",
]
[[package]]
name = "nym-node-status-client"
version = "0.1.0"
dependencies = [
"anyhow",
"bincode",
"chrono",
"nym-crypto",
"nym-http-api-client",
"reqwest 0.12.4",
"serde",
"serde_json",
"tracing",
]
[[package]]
name = "nym-node-tester-utils"
version = "0.1.0"
@@ -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",
]
@@ -7179,9 +7023,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.102"
version = "0.9.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2"
checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741"
dependencies = [
"cc",
"libc",
@@ -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"
+6 -16
View File
@@ -61,7 +61,6 @@ members = [
"common/ip-packet-requests",
"common/ledger",
"common/mixnode-common",
"common/models",
"common/network-defaults",
"common/node-tester-utils",
"common/nonexhaustive-delayqueue",
@@ -126,8 +125,9 @@ members = [
"nym-node",
"nym-node/nym-node-http-api",
"nym-node/nym-node-requests",
"nym-node-status-api",
"nym-node-status-agent",
"nym-node-status-api/nym-node-status-agent",
"nym-node-status-api/nym-node-status-api",
"nym-node-status-api/nym-node-status-client",
"nym-outfox",
"nym-validator-rewarder",
"tools/echo-server",
@@ -149,13 +149,12 @@ 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 = [
"clients/native",
"clients/socks5",
"common/models",
"explorer-api",
"gateway",
"mixnode",
@@ -163,9 +162,9 @@ default-members = [
"nym-credential-proxy/nym-credential-proxy",
"nym-data-observatory",
"nym-node",
"nym-node-status-api",
"nym-node-status-api/nym-node-status-agent",
"nym-node-status-api/nym-node-status-api",
"nym-validator-rewarder",
"nym-node-status-api",
"service-providers/authenticator",
"service-providers/ip-packet-router",
"service-providers/network-requester",
@@ -412,15 +411,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 -1
View File
@@ -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"
+1 -1
View File
@@ -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"
+10 -1
View File
@@ -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
@@ -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,
@@ -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 {
@@ -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 {
@@ -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 {
+2
View File
@@ -48,6 +48,7 @@ nym-vesting-contract-common = { path = "../cosmwasm-smart-contracts/vesting-cont
nym-coconut-dkg-common = { path = "../cosmwasm-smart-contracts/coconut-dkg" }
nym-multisig-contract-common = { path = "../cosmwasm-smart-contracts/multisig-contract" }
nym-ecash-contract-common = { path = "../cosmwasm-smart-contracts/ecash-contract" }
nym-ecash-time = { path = "../../common/ecash-time" }
nym-sphinx = { path = "../../common/nymsphinx" }
nym-client-core = { path = "../../common/client-core" }
nym-config = { path = "../../common/config" }
@@ -56,6 +57,7 @@ nym-credentials-interface = { path = "../../common/credentials-interface" }
nym-credential-storage = { path = "../../common/credential-storage" }
nym-credential-utils = { path = "../../common/credential-utils" }
nym-id = { path = "../nym-id" }
nym-credential-proxy-requests = { path = "../../nym-credential-proxy/nym-credential-proxy-requests" }
nym-pemstore = { path = "../../common/pemstore", version = "0.3.0" }
nym-types = { path = "../../common/types" }
@@ -0,0 +1,41 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use log::trace;
use nym_credentials_interface::{generate_keypair_user, generate_keypair_user_from_seed, Base58};
use serde::{Deserialize, Serialize};
use std::io::stdout;
#[derive(Serialize, Deserialize)]
pub struct Bs58EncodedKeys {
pub secret_key: String,
pub public_key: String,
}
#[derive(Debug, Parser)]
pub struct Args {
/// Secret value that's used for deriving underlying ecash keypair
#[clap(long)]
pub(crate) bs58_encoded_client_secret: Option<String>,
}
pub fn generate_ecash_keypair(args: Args) -> anyhow::Result<()> {
trace!("args: {args:?}");
let keypair = if let Some(secret) = args.bs58_encoded_client_secret {
let seed = bs58::decode(&secret).into_vec()?;
generate_keypair_user_from_seed(&seed)
} else {
generate_keypair_user()
};
let encoded = Bs58EncodedKeys {
secret_key: keypair.secret_key().to_bs58(),
public_key: keypair.public_key().to_bs58(),
};
serde_json::to_writer_pretty(stdout(), &encoded)?;
Ok(())
}
+23
View File
@@ -0,0 +1,23 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod generate_keypair;
pub mod withdrawal_request;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct InternalEcash {
#[clap(subcommand)]
pub command: InternalEcashCommands,
}
#[derive(Debug, Subcommand)]
pub enum InternalEcashCommands {
/// Generate a dummy withdrawal request
GenerateWithdrawalRequest(withdrawal_request::Args),
/// Generate dummy ecash keypair
GenerateKeypair(generate_keypair::Args),
}
@@ -0,0 +1,78 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use log::trace;
use nym_credential_proxy_requests::api::v1::ticketbook::models::TicketbookRequest;
use nym_credentials_interface::{
generate_keypair_user, withdrawal_request, Base58, SecretKeyUser, TicketType,
};
use nym_ecash_time::{ecash_default_expiration_date, EcashTime};
use serde::{Deserialize, Serialize};
use std::io::stdout;
use time::macros::format_description;
use time::Date;
use zeroize::Zeroizing;
fn parse_date(raw: &str) -> Result<Date, time::error::Parse> {
let format = format_description!("[year]-[month]-[day]");
Date::parse(raw, &format)
}
#[derive(Serialize, Deserialize)]
pub struct Bs58EncodedOutput {
pub ecash_proxy_request: TicketbookRequest,
pub ecash_secret: String,
/// Needed to later unblind shares
pub ecash_request_info_bs58: String,
}
#[derive(Debug, Parser)]
pub struct Args {
/// Specify which type of ticketbook
#[clap(long, default_value_t = TicketType::V1MixnetEntry)]
pub(crate) ticketbook_type: TicketType,
/// Set expiration date for the ticketbook
#[clap(long, value_parser = parse_date, default_value_t = ecash_default_expiration_date())]
pub(crate) expiration_date: Date,
/// Provide ecash secret key (or generate a fresh one)
#[clap(long)]
pub(crate) ecash_secret_key_bs58: Option<String>,
}
pub async fn generate_withdrawal_request(args: Args) -> anyhow::Result<()> {
trace!("args: {args:?}");
let ecash_keypair = if let Some(secret_key) = args.ecash_secret_key_bs58 {
let secret_key = Zeroizing::new(bs58::decode(Zeroizing::new(secret_key)).into_vec()?);
let sk = SecretKeyUser::from_bytes(&secret_key)?;
sk.into()
} else {
generate_keypair_user()
};
let (withdrawal_request, request_info) = withdrawal_request(
ecash_keypair.secret_key(),
args.expiration_date.ecash_unix_timestamp(),
args.ticketbook_type.encode(),
)?;
let encoded = Bs58EncodedOutput {
ecash_proxy_request: TicketbookRequest {
withdrawal_request: withdrawal_request.into(),
ecash_pubkey: ecash_keypair.public_key(),
expiration_date: args.expiration_date,
ticketbook_type: args.ticketbook_type,
is_freepass_request: false,
},
ecash_secret: ecash_keypair.secret_key().to_bs58(),
ecash_request_info_bs58: request_info.to_bs58(),
};
serde_json::to_writer_pretty(stdout(), &encoded)?;
Ok(())
}
+19
View File
@@ -0,0 +1,19 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod ecash;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct Internal {
#[clap(subcommand)]
pub command: InternalCommands,
}
#[derive(Debug, Subcommand)]
pub enum InternalCommands {
/// Ecash related internal commands
Ecash(ecash::InternalEcash),
}
+1
View File
@@ -3,5 +3,6 @@
pub mod context;
pub mod ecash;
pub mod internal;
pub mod utils;
pub mod validator;
@@ -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 {
@@ -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 {
+1 -2
View File
@@ -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>>>;
+3 -3
View File
@@ -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> {
+1
View File
@@ -22,6 +22,7 @@ tracing = { workspace = true }
nym-sphinx = { path = "../nymsphinx" }
nym-credentials-interface = { path = "../credentials-interface" }
nym-statistics-common = { path = "../statistics" }
[build-dependencies]
+2 -1
View File
@@ -2,8 +2,9 @@
// SPDX-License-Identifier: GPL-3.0-only
use error::StatsStorageError;
use models::{ActiveSession, FinishedSession, SessionType, StoredFinishedSession};
use models::{ActiveSession, FinishedSession, StoredFinishedSession};
use nym_sphinx::DestinationAddressBytes;
use nym_statistics_common::gateways::SessionType;
use sessions::SessionManager;
use sqlx::ConnectOptions;
use std::path::Path;
+1 -36
View File
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-only
use nym_credentials_interface::TicketType;
use nym_statistics_common::gateways::SessionType;
use sqlx::prelude::FromRow;
use time::{Duration, OffsetDateTime};
@@ -25,42 +26,6 @@ pub struct FinishedSession {
pub typ: SessionType,
}
#[derive(PartialEq)]
pub enum SessionType {
Vpn,
Mixnet,
Unknown,
}
impl SessionType {
pub fn to_string(&self) -> &str {
match self {
Self::Vpn => "vpn",
Self::Mixnet => "mixnet",
Self::Unknown => "unknown",
}
}
pub fn from_string(s: &str) -> Self {
match s {
"vpn" => Self::Vpn,
"mixnet" => Self::Mixnet,
_ => Self::Unknown,
}
}
}
impl From<TicketType> for SessionType {
fn from(value: TicketType) -> Self {
match value {
TicketType::V1MixnetEntry => Self::Mixnet,
TicketType::V1MixnetExit => Self::Mixnet,
TicketType::V1WireguardEntry => Self::Vpn,
TicketType::V1WireguardExit => Self::Vpn,
}
}
}
#[derive(FromRow)]
pub(crate) struct StoredActiveSession {
start_time: OffsetDateTime,
-17
View File
@@ -1,17 +0,0 @@
[package]
name = "nym-common-models"
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]
anyhow = { workspace = true }
bincode = { workspace = true }
nym-crypto = { path = "../crypto", features = ["asymmetric", "serde"] }
serde = { workspace = true, features = ["derive"] }
-1
View File
@@ -1 +0,0 @@
pub mod ns_api;
+14 -4
View File
@@ -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
}
}
-45
View File
@@ -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
}
-24
View File
@@ -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 },
}
-46
View File
@@ -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(())
}
-13
View File
@@ -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,6 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub trait State: Clone {
//
}
-80
View File
@@ -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")
}
}
-158
View File
@@ -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");
},
}
}
-46
View File
@@ -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);
}
-154
View File
@@ -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]
@@ -530,6 +530,15 @@ impl From<KeyPairUser> for SecretKeyUser {
}
}
impl From<SecretKeyUser> for KeyPairUser {
fn from(value: SecretKeyUser) -> Self {
KeyPairUser {
public_key: value.public_key(),
secret_key: value,
}
}
}
impl KeyPairUser {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
@@ -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);
+2 -2
View File
@@ -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
}
}
+1 -1
View File
@@ -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]
+1 -1
View File
@@ -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 {
+7 -7
View File
@@ -1,6 +1,13 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
//! Encoding 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 +19,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 {
+28 -10
View File
@@ -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
}
}
/*
-10
View File
@@ -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() {
+36
View File
@@ -87,3 +87,39 @@ pub enum SessionEvent {
client: DestinationAddressBytes,
},
}
#[derive(PartialEq)]
pub enum SessionType {
Vpn,
Mixnet,
Unknown,
}
impl SessionType {
pub fn to_string(&self) -> &str {
match self {
Self::Vpn => "vpn",
Self::Mixnet => "mixnet",
Self::Unknown => "unknown",
}
}
pub fn from_string(s: &str) -> Self {
match s {
"vpn" => Self::Vpn,
"mixnet" => Self::Mixnet,
_ => Self::Unknown,
}
}
}
impl From<TicketType> for SessionType {
fn from(value: TicketType) -> Self {
match value {
TicketType::V1MixnetEntry => Self::Mixnet,
TicketType::V1MixnetExit => Self::Mixnet,
TicketType::V1WireguardEntry => Self::Vpn,
TicketType::V1WireguardExit => Self::Vpn,
}
}
}
+11
View File
@@ -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),
+1
View File
@@ -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" }
+2 -1
View File
@@ -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 {
+19 -5
View File
@@ -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,
+20 -10
View File
@@ -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();
}
}
@@ -1 +1 @@
Wednesday, November 20th 2024, 15:53:00 UTC
Monday, November 25th 2024, 13:24:04 UTC
@@ -1,5 +1,4 @@
```sh
2024-11-20T15:53:01.702819Z  INFO nym-api/src/main.rs:40: Starting nym api...
Usage: nym-api [OPTIONS] <COMMAND>
Commands:
@@ -10,9 +9,9 @@ Commands:
Options:
-c, --config-env-file <CONFIG_ENV_FILE>
Path pointing to an env file that configures the Nym API [env: NYMAPI_CONFIG_ENV_FILE_ARG=]
Path pointing to an env file that configures the Nym API
--no-banner
A no-op flag included for consistency with other binaries (and compatibility with nymvisor, oops) [env: NYMAPI_NO_BANNER_ARG=]
A no-op flag included for consistency with other binaries (and compatibility with nymvisor, oops)
-h, --help
Print help
-V, --version
@@ -44,8 +44,6 @@ Options:
Specify whether detailed system crypto hardware information should be exposed. default: true [env: NYMNODE_HTTP_EXPOSE_CRYPTO_HARDWARE=] [possible values: true, false]
--mixnet-bind-address <MIXNET_BIND_ADDRESS>
Address this node will bind to for listening for mixnet packets default: `0.0.0.0:1789` [env: NYMNODE_MIXNET_BIND_ADDRESS=]
--mixnet-announce-port <MIXNET_ANNOUNCE_PORT>
If applicable, custom port announced in the self-described API that other clients and nodes will use. Useful when the node is behind a proxy [env: NYMNODE_MIXNET_ANNOUNCE_PORT=]
--nym-api-urls <NYM_API_URLS>
Addresses to nym APIs from which the node gets the view of the network [env: NYMNODE_NYM_APIS=]
--nyxd-urls <NYXD_URLS>
@@ -54,14 +52,14 @@ Options:
Specifies whether the wireguard service is enabled on this node [env: NYMNODE_WG_ENABLED=] [possible values: true, false]
--wireguard-bind-address <WIREGUARD_BIND_ADDRESS>
Socket address this node will use for binding its wireguard interface. default: `0.0.0.0:51822` [env: NYMNODE_WG_BIND_ADDRESS=]
--wireguard-private-ip <WIREGUARD_PRIVATE_IP>
Private IP address of the wireguard gateway. default: `10.1.0.1` [env: NYMNODE_WG_IP=]
--wireguard-announced-port <WIREGUARD_ANNOUNCED_PORT>
Port announced to external clients wishing to connect to the wireguard interface. Useful in the instances where the node is behind a proxy [env: NYMNODE_WG_ANNOUNCED_PORT=]
--wireguard-private-network-prefix <WIREGUARD_PRIVATE_NETWORK_PREFIX>
The prefix denoting the maximum number of the clients that can be connected via Wireguard. The maximum value for IPv4 is 32 and for IPv6 is 128 [env: NYMNODE_WG_PRIVATE_NETWORK_PREFIX=]
--verloc-bind-address <VERLOC_BIND_ADDRESS>
Socket address this node will use for binding its verloc API. default: `0.0.0.0:1790` [env: NYMNODE_VERLOC_BIND_ADDRESS=]
--verloc-announce-port <VERLOC_ANNOUNCE_PORT>
If applicable, custom port announced in the self-described API that other clients and nodes will use. Useful when the node is behind a proxy [env: NYMNODE_VERLOC_ANNOUNCE_PORT=]
--entry-bind-address <ENTRY_BIND_ADDRESS>
Socket address this node will use for binding its client websocket API. default: `0.0.0.0:9000` [env: NYMNODE_ENTRY_BIND_ADDRESS=]
--announce-ws-port <ANNOUNCE_WS_PORT>
+168 -17
View File
@@ -33,6 +33,157 @@ This page displays a full list of all the changes during our release cycle from
<VarInfo />
## `magura-drift`
Second patch to `v2024.13-magura` release version.
- [Release binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2024.13-magura-drift)
- [`nym-node`](nodes/nym-node.mdx) version `1.1.12`
```sh
nym-node
Binary Name: nym-node
Build Timestamp: 2024-11-29T13:10:51.813092288Z
Build Version: 1.1.12
Commit SHA: 4a9a5579c40ad956163ea02e01d7b53aef2ac8ef
Commit Date: 2024-11-29T14:06:32.000000000+01:00
Commit Branch: HEAD
rustc Version: 1.83.0
rustc Channel: stable
cargo Profile: release
```
- This patch adds a peer storage manager to fix issues causing external clients to be blocked, ensuring they can successfully connect to different nodes.
## `v2024.13-magura-patched`
- [Release binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2024.13-magura-patched)
- [`nym-node`](nodes/nym-node.mdx) version `1.1.11`
```sh
nym-node
Binary Name: nym-node
Build Timestamp: 2024-11-22T14:30:48.067329245Z
Build Version: 1.1.11
Commit SHA: 01c7b2819ee3d328deccd303b4113ff415d7e276
Commit Date: 2024-11-22T10:50:59.000000000+01:00
Commit Branch: HEAD
rustc Version: 1.82.0
rustc Channel: stable
cargo Profile: release
```
<Callout type="warning" emoji="⚠️">
After changes coming along with `v2024.13-magura` (`nym-node v1.1.10`), Nym Explorer is no longer picking all values correctly. Instead of fixing this outdated explorer, we are working on a new one, coming out soon.
[Nym Harbourmaster](https://harbourmaster.nymtech.net) has cache of 90min, expect your values to be updated with delay. We are aware of some issues with Nym Harbourmaster and working hard to resolve them in the upcoming explorer v2. To check your routing values in real time, you can use [`nym-gateway-probe`](nodes/performance-and-testing/gateway-probe).
</Callout>
### Operators Updates & Tools
- Updated [`network_tunnel_manager.sh`](https://github.com/nymtech/nym/blob/develop/scripts/network_tunnel_manager.sh) (moved to our monorepo) helps operators to configure their IP tables rules for `nymtun` and `wireguard` routing.
- **Please re-run [routing configuration steps](https://nymtech.net/docs/operators/nodes/nym-node/configuration#routing-configuration) to update your routing settings.**
- We found out that some operators have a wrong value for wireguard IP. Follow these steps to ensure your value is set to `10.1.0.1` (default on new nodes):
<Steps>
###### 1. Open your node config file:
```sh
nano $HOME/.nym/nym-nodes/<ID>/config/config.toml
# change <ID> for your local nym moniker for example:
# nano $HOME/.nym/nym-nodes/default-nym-node/config/config.toml
```
###### 2. Control or change the value of wireguard private IP
- Scroll down to section starting with `[wireguard]`
- Find line `private_ip` and ensure it's set to value `10.1.0.1`
- The section will look like this:
```toml
[wireguard]
# Specifies whether the wireguard service is enabled on this node.
enabled = true
# Socket address this node will use for binding its wireguard interface.
# default: `0.0.0.0:51822`
bind_address = '0.0.0.0:51822'
# Private IP address of the wireguard gateway.
# default: `10.1.0.1`
private_ip = '10.1.0.1'
```
###### 3. Save, exit and restart the service
- If you used `nano` editor - press `ctrl` + `x` and confirm the changes
- Run these commands to update the service with new values and restart your node process:
```sh
systemctl daemon-reload && service nym-node restart
```
</Steps>
- New manual how to [run `nym-node` as non-root](nodes/nym-node/configuration#running-nym-node-as-a-non-root)
- Since `v2024.13-magura`, operators do not update their node version in the wallet. [Manual upgrading steps](nodes/maintenance/manual-upgrade.mdx) has been updated accordingly.
- CLI tool [`node_api_check.py`](nodes/performance-and-testing/node-api-check.mdx), helping operators to collect all API values about their nodes locally, is not up to date with the API changes introduced with `v2024.13-magura` release version. Please treat it as unstable before we fix it.
#### Error Log
In case you encounter this error:
```
[ERROR] nym-node/src/node/mod.rs:628: the exit gateway subtask has failed with the following message: failed to start authenticator: internal wireguard error no private IP set for peer..
```
You can follow these steps to make a workaround:
<br />
<AccordionTemplate name="Authenticator error fix">
<Steps>
###### 1. Find the error
- In the node logs, locate the ERROR message which says `the exit gateway subtask has failed with the following message: failed to start authenticator: internal wireguard error no private IP set for peer KN5GPvkC+p6G/SM4PD2Z3ObAtRGiDjHPRnQOPpbdUQk=`
- Copy the end part of that peer, later denoted as `<WG_PEER_STRING_END>` (in our example `GiDjHPRnQOPpbdUQk=`) to use later in the sql commands
###### 2. Fix the issue in sqlite3 db
<Callout type="warning" emoji="⚠️">
Be careful when running commands within sqlite database.
</Callout>
- Navigate to the data directory:
```sh
cd $HOME/.nym/nym-nodes/<ID>/data
```
- Enter the database:
```sh
sqlite3 clients.sqlite
```
- Run these commands:
```sh
# Change with your unique <PEER_STRING_END>
select * from wireguard_peer where public_key like "%<WG_PEER_STRING_END>%"
# Make sure that only ONE line is returned and it contains the key
delete from wireguard_peer where public_key like "%<WG_PEER_STRING_END>%";
```
- Confirm that peer has been removed by running this again:
```sh
select * from wireguard_peer where public_key like "%<WG_PEER_STRING_END>%";
```
###### 3. Exit and restart the service
Run `.quit` and:
```sh
systemctl restart nym-node.service
```
</Steps>
</AccordionTemplate>
## `v2024.13-magura`
Magura release represents a bigger milestone in [project Smoosh](archive/faq/smoosh-faq.mdx) development where `nym-node` is one binary able to perform any function in Nym Mixnet. This release is especially crucial for operators, please pay attention to the section [*Operators Updates & Tooling*](#operators-updates--tooling) below.
@@ -70,15 +221,15 @@ cargo Profile: release
- [Switch over the last set of jobs to arc runners](https://github.com/nymtech/nym/pull/4938): Switch over the remaining GH jobs using 16-core runners to self-hosted arc runners. Since we can't currently use Docker on the ubuntu-20.04 runners, remove the matrix notification steps
<AccordionTemplate name={<TestingSteps/>}>
<AccordionTemplate name={<TestingSteps/>}>
Confirm that the deployment workflows work through manual testing
- [x] cd-docs
- [x] publish-sdk-npm
</AccordionTemplate>
- [V2 performance monitoring feature flag](https://github.com/nymtech/nym/pull/4943): Feature flag to use v2 network monitor results in rewarding
- [V2 performance monitoring feature flag](https://github.com/nymtech/nym/pull/4943): Feature flag to use v2 network monitor results in rewarding
- [Add `utoipa` feature to nym-node](https://github.com/nymtech/nym/pull/4945): `cargo build -p nym-node` was failing, since its depending on `QueryParams` having `utoipa` traits derived
- [Add `utoipa` feature to nym-node](https://github.com/nymtech/nym/pull/4945): `cargo build -p nym-node` was failing, since its depending on `QueryParams` having `utoipa` traits derived
- [Ticket type storage](https://github.com/nymtech/nym/pull/4947)
@@ -103,8 +254,8 @@ Confirm that the deployment workflows work through manual testing
- [chore: remove unused rocket code](https://github.com/nymtech/nym/pull/4968)
- [add Dockerfile for nym node](https://github.com/nymtech/nym/pull/4972)
- [`Product Data` Add session type based on ecash ticket received](https://github.com/nymtech/nym/pull/4974): Fire an `EcashTicket` event for the `GatewayStatisticsCollector`, when an Ecash ticket is being accepted. This allows to mark an active session as being a mixnet session or a vpn session. It also changes the format of the related self-described data, to accommodate that new session type.
- [`Product Data` Add session type based on ecash ticket received](https://github.com/nymtech/nym/pull/4974): Fire an `EcashTicket` event for the `GatewayStatisticsCollector`, when an Ecash ticket is being accepted. This allows to mark an active session as being a mixnet session or a vpn session. It also changes the format of the related self-described data, to accommodate that new session type.
- [Top up bandwidth](https://github.com/nymtech/nym/pull/4975)
@@ -147,7 +298,7 @@ Confirm that the deployment workflows work through manual testing
- log gw identity key
- better agent testrun logging
- log responses on server side
- change response code for agents
- change response code for agents
- update sqlx data
- fix agent - probe gw bug
@@ -165,7 +316,7 @@ Confirm that the deployment workflows work through manual testing
- [Allow custom http port to be reset](https://github.com/nymtech/nym/pull/5073)
- [Fix gateway decreasing bandwidth](https://github.com/nymtech/nym/pull/5075): Make sure to update the storage after each decrease with the new values. Also set the storage values to 0 on restart for existing peers, as kernel peers can't have those values set to 0
- [Fix gateway decreasing bandwidth](https://github.com/nymtech/nym/pull/5075): Make sure to update the storage after each decrease with the new values. Also set the storage values to 0 on restart for existing peers, as kernel peers can't have those values set to 0
- [Fix expiration date as today + 7 days](https://github.com/nymtech/nym/pull/5076)
@@ -187,7 +338,7 @@ Confirm that the deployment workflows work through manual testing
- [Add NYM_VPN_API to env files](https://github.com/nymtech/nym/pull/5099)
- [Feature/force refresh node](https://github.com/nymtech/nym/pull/5101): currently if nodes update their role from say mixnode to entry-gateway, it might take quite a while for `nym-api` to pick up the change and thus they might be losing performance. With this change, the node will be force refreshed on its startup
- [Feature/force refresh node](https://github.com/nymtech/nym/pull/5101): currently if nodes update their role from say mixnode to entry-gateway, it might take quite a while for `nym-api` to pick up the change and thus they might be losing performance. With this change, the node will be force refreshed on its startup
- [`nym-credential-proxy-requests`: reqwest use rustls-tls](https://github.com/nymtech/nym/pull/5116)
@@ -205,7 +356,7 @@ Confirm that the deployment workflows work through manual testing
- [Fix critical issues SI84 and SI85 from Cure53](https://github.com/nymtech/nym/pull/4758): This pull request fixes the following issues:
- NYM-01-009 WP5: BLS12-381 EC signature bypasses in Coconut library (Critical)
- NYM-01-014 WP5: Partial signature bypass in offline eCash (Critical)
- [bugfix: correctly paginate through 'search_tx' endpoint](https://github.com/nymtech/nym/pull/4936): when `results.append(&mut res.txs);` was called, `res.txs` was always empty thus it was impossible to return more than page size number of results
- [Fix broken build after merge](https://github.com/nymtech/nym/pull/4937)
@@ -214,7 +365,7 @@ Confirm that the deployment workflows work through manual testing
- dedicated commands to request specific blocks for processing
- decreased websocket failure timeout
- ensuring we do actually have sufficient number of blocks to process rewarding for given epoch
- additional error logging
- additional error logging
- [bugfix: fix expected return type on /v1/gateways endpoint](https://github.com/nymtech/nym/pull/4965)
@@ -224,7 +375,7 @@ Confirm that the deployment workflows work through manual testing
- Faulty aggregation to invalid offline eCash signatures
- Signature forgery of Pointcheval-Sanders schema
- [bugfix: client memory leak](https://github.com/nymtech/nym/pull/4991): This fixes memory leaks in all the clients. however, they were most prominent in `nym-api` during network monitoring due to the sheer amount of packets being pushed
- [bugfix: client memory leak](https://github.com/nymtech/nym/pull/4991): This fixes memory leaks in all the clients. however, they were most prominent in `nym-api` during network monitoring due to the sheer amount of packets being pushed
- [Fix rustfmt in nym-credential-proxy](https://github.com/nymtech/nym/pull/4992)
@@ -279,7 +430,7 @@ Confirm that the deployment workflows work through manual testing
- [bugfix: make sure to assign correct node_id and identity during 'gateway_details' table migration](https://github.com/nymtech/nym/pull/5142)
- [bugifx: assign 'node_id' when converting from 'GatewayDetails' to 'TestNode'](https://github.com/nymtech/nym/pull/5143)
### Operators Updates & Tooling
<Callout type="warning" emoji="⚠️">
@@ -292,7 +443,7 @@ Confirm that the deployment workflows work through manual testing
- **[Operators release & rewards roadmap](tokenomics/mixnet-rewards.mdx#roadmap)**
- **New [Operators landing pag e](https://nymtech.net/operators)
- **New [Operators landing page](https://nymtech.net/operators)**
- [Nym Harbourmaster](https://harbourmaster.nymtech.net) had a new tab `NODE SEARCH` where operators can easily search nodes by identity keys and owner accounts and get all public information listed.
@@ -335,7 +486,7 @@ Confirm that the deployment workflows work through manual testing
- Confirm the transaction
###### 5. Welcome to new episode of `nym-node`!
###### 5. Welcome to new episode of `nym-node`!
</ Steps>
</AccordionTemplate>
@@ -360,13 +511,13 @@ Confirm that the deployment workflows work through manual testing
- Currently in *Native rewarding*, the rewards are split equally across the [rewarded set of nodes](https://validator.nymtech.net/api/v1/epoch/reward_params) (which now = active set and it's size is 240 nodes) for both Mixnet mode and dVPN mode. Every node being assigned 1 / 240 work factor (hence *naive rewarding*).
#### Directory Services v2.1: API & Mixnet Contract Changes
#### Directory Services v2.1: API & Mixnet Contract Changes
Magura release brings [breaking changes on API](https://github.com/nymtech/nym/pull/4903) logic of Nym. New APIs will only communicate with `nym-node` from this release and newer. Also old version of APIs won't be able to communicate with the new version of `nym-node`. We are also moving towards completely removing Nym Explorer API, which now has been only used to report nodes location.
Magura release brings [breaking changes on API](https://github.com/nymtech/nym/pull/4903) logic of Nym. New APIs will only communicate with `nym-node` from this release and newer. Also old version of APIs won't be able to communicate with the new version of `nym-node`. We are also moving towards completely removing Nym Explorer API, which now has been only used to report nodes location.
Any new bonded node will provide only the bare minimum information: host, identity key and optionally custom port of its HTTP api - we highly recommend to set that one up to `8080`. Everything else will be discovered via the self-described API for maximum flexibility. This also includes the sphinx key, meaning if the API is not exposed, the node will be unable to route any traffic. Furthermore, this allows to arbitrary change of `nym-node` from mixnode into a gateway modes (and vice versa) without losing any delegations.
The contract changes also mean any node functionality can get rewards. Rather than just with assigned mixing roles, gateways now also added into the pool. However, to be eligible for gateway rewarding, one must [migrate into a `nym-node`](#wallet-changes) on a smart contract level (or bond a new node).
The contract changes also mean any node functionality can get rewards. Rather than just with assigned mixing roles, gateways now also added into the pool. However, to be eligible for gateway rewarding, one must [migrate into a `nym-node`](#wallet-changes) on a smart contract level (or bond a new node).
##### API High Level Changes
@@ -14,30 +14,27 @@ This page explains how to upgrade [`nym-node`](#nym-node-upgrade) or [`validator
## Nym node Upgrade
**Upgrading your node is a straight forward two-step process:**
<Steps>
#### 1. Updating the binary and `~/.nym/nym-nodes/<ID>/config/config.toml` on your VPS
#### 2. Updating the node information in the [mixnet smart contract](https://nymtech.net/docs/nyx/mixnet-contract.html). This is the information that is present on the [mixnet explorer](https://explorer.nymtech.net).
</Steps>
Since `v2024.13-magura` (`nym-node v1.1.10`), **operators NO longer update node information in the Mixnet smart contract** (wallet version information), **only upgrade node binary** (on VPS), resulting in `~/.nym/nym-nodes/<ID>/config/config.toml` update.
Below are detailed steps how to do it:
<Steps>
###### 1. Upgrading node binary and information in config file
###### 1. Upgrade `nym-node` binary
- Pause your node process.
- If you run your node as `systemd` service (recommended), run: `service nym-node stop`
- Otherwise open the terminal window with your node logs and press `ctrl + c`
- Otherwise open the terminal window with your node logs and press once `ctrl + c` and wait for the node to terminate gracefully
- Replace the existing `nym-node` binary with the newest binary (which you can either [compile yourself](../../binaries/building-nym.mdx) or [download](../../binaries/pre-built-binaries.mdx).
- To verify node version, run `./nym-node --version`
###### 2. Restart the node
- [Re-run with the same values](../nym-node/setup.mdx#initialise--run) as you use to run your `nym-node`. If you want keep changes in your config file, use flag `-w` (`--write-changes`), **This will just update the config file, it will not overwrite existing keys**.
- If you automated your node with `systemd` (recommended) run:
- If you automated your node with `systemd` (recommended), make sure you have all needed flags in `ExecStart` line of the service config file, and run:
```sh
systemctl daemon-reload
service nym-node start
@@ -48,25 +45,19 @@ service nym-node start
journalctl -f -u nym-node.service
```
###### 2. Updating your node information in the smart contract
Follow these steps to update the information about your `nym-node` which is publicly available from the [`nym-api`](https://validator.nymtech.net/api/swagger/index.html) and information displayed on the [Mixnet explorer](https://explorer.nymtech.net).
###### 3. Check if your node is reporting the version correctly
You can either do this graphically via the Desktop Wallet, or the CLI.
<div>
<Tabs items={[
<strong>Desktop Wallet (recommended)</strong>,
<strong>CLI (superusers)</strong>,
]} defaultIndex="0">
<MyTab><DesktopWalletUpdate/></MyTab>
<MyTab><CliUpdate/></MyTab>
</Tabs>
</div>
- Open [Nym Harbourbourmaster](https://harbourmaster.nymtech.net), search your node and verify that everything is working as expected and your node shows expected version.
</Steps>
<Callout type="warning" emoji="⚠️">
After changes coming along with `v2024.13-magura` (`nym-node v1.1.10`), Nym Explorer is no longer picking all values correctly. Instead of fixing this outdated explorer, we are working on a new one, coming out soon.
[Nym Harbourmaster](https://harbourmaster.nymtech.net) has cache of 90min, expect your values to be updated with delay. We are aware of some issues with Nym Harbourmaster and working hard to resolve them in the upcoming explorer v2. To check your routing values in real time, you can use [`nym-gateway-probe`](../performance-and-testing/gateway-probe).
</Callout>
## Validator Upgrade
Upgrading from `v0.31.1` -> `v0.32.0` process is fairly simple. Grab the `v0.32.0` release tarball from the [`nyxd` releases page](https://github.com/nymtech/nyxd/releases), and untar it. Inside are two files:
@@ -92,4 +83,3 @@ The most common reason for your validator being jailed is that it runs out of me
Running the command `df -H` will return the size of the various partitions of your VPS.
If the `/dev/sda` partition is almost full, try pruning some of the `.gz` syslog archives and restart your validator process.
@@ -120,7 +120,7 @@ From `nym-wallet` version `1.2.15` onward the application allows and prompts ope
###### 2. Verify the binary and extract it if needed
- Download [`hashes.json`]https://github.com/nymtech/nym/releases/download/nym-wallet-v1.2.15/hashes.json
- Download [`hashes.json`](https://github.com/nymtech/nym/releases/download/nym-wallet-v1.2.15/hashes.json)
- Open it with your text editor or print it's content with `cat hashes.json`
- Run `sha256sum <WALLET_BINARY>` for example `sha256sum ./nym-wallet_1.2.15_amd64.AppImage`
- If your have to extract it (like `.tar.gz`) do it
@@ -426,6 +426,95 @@ journalctl -u nym-node.service -f -n 100
Make sure that you get the validation of all connectivity. If there are still any problems, please refer to [troubleshooting section](../../troubleshooting/vps-isp.mdx#incorrect-gateway-network-check).
## Running `nym-node` as a non-root
Some operators prefer to run `nym-node` without root privileges. It's possible but still `nym-node` binary needs higher privileges for network-level operations demanding these permissions. Below is a guide how to go about such setup:
<Callout type="warning" emoji="⚠️">
Copying nodes database and the `.nym/` directories from `/root/.nym` to `/home/<USER>/.nym/` should be treated as experimental, therefore we would advise this section for operators starting new nodes, rather than tweaking an existing one. We will publish a detailed guide for changing permissions of an existing node soon.
</Callout>
<Steps>
###### 1. Setup a new user
- Define a variable `user_name` using your desired user name:
```sh
user_name="<USER>"
```
- Run:
```sh
user_home="/home/$user_name"
if ! id "$user_name" &>/dev/null; then
sudo adduser --home "$user_home" --disabled-login --gecos "" "$user_name"
else
echo "user $user_name already exists"
fi
```
- And follow by:
```sh
sudo usermod -aG sudo "$user_name"
```
- Optional: Add to sudoers group:
```sh
echo "$user_name ALL=(ALL) NOPASSWD:ALL" | sudo tee -a /etc/sudoers.d/$user_name
```
###### 2. Grant needed permissions for network-level operations
While `nym-node` will be set as a user process, it requires higher privileges for network-level operations, set them up with this command:
```sh
sudo setcap 'cap_net_bind_service=+ep cap_net_admin=+ep' nym-node
```
**After replacing or upgrading the binary, you must reapply these permissions each time!**
###### 3. Edit service config file
- Add these new lines to your `/etc/systemd/system/nym-node.service` [service config file](#systemd)
- `After=network.target`
- `Group=<USER>`
- `Type=simple`
- Your service file will then look like this:
```ini
[Unit]
Description=Nym Node
After=network.target
StartLimitInterval=350
StartLimitBurst=10
[Service]
User=<USER>
Group=<USER>
Type=simple
LimitNOFILE=65536
ExecStart=<PATH>/nym-node run <ARGUMENTS> # add all the flags you use to run your node
KillSignal=SIGINT
Restart=on-failure
RestartSec=30
[Install]
WantedBy=multi-user.target
```
###### 4. Reload and restart the service
```sh
systemctl daemon-reload && service nym-node restart
```
- If you want to follow the logs, run:
```sh
journalctl -u nym-node -f
```
</Steps>
## Next Steps
@@ -17,15 +17,16 @@ This documentation page provides a guide on how to set up and run a [NYM NODE](.
```sh
nym-node
Binary Name: nym-node
Build Timestamp: 2024-11-18T17:02:50.947941194Z
Build Version: 1.1.10
Commit SHA: b49ef643df86f0c670672429812c632fbbaf6cf1
Commit Date: 2024-11-18T17:56:57.000000000+01:00
Build Timestamp: 2024-11-29T13:10:51.813092288Z
Build Version: 1.1.12
Commit SHA: 4a9a5579c40ad956163ea02e01d7b53aef2ac8ef
Commit Date: 2024-11-29T14:06:32.000000000+01:00
Commit Branch: HEAD
rustc Version: 1.82.0
rustc Version: 1.83.0
rustc Channel: stable
cargo Profile: release
```
{/* COMMENTING THIS OUT ASS WE HAVE TO FIGURE OUT HOW TO SHOW THE LATEST VERSION FROM MASTER BRANCH
<BuildInfo />
*/}
@@ -8,6 +8,10 @@ import NodeApiCheckQueryHelp from 'components/outputs/command-outputs/node-api-c
# Node API Check
<Callout type="warning" emoji="⚠️">
CLI tool `node_api_check.py`, helping operators to collect all API values about their nodes locally, is not up to date with the API changes introduced with `v2024.13-magura` release version. Please treat it as unstable before we fix it.
</Callout>
<VarInfo />
Operating a `nym-node` is not a *"set and forget"* endeavor, it takes some work. To diagnose node network performance through querying APIs, is a good knowledge to have. These are the main places to look for API endpoints regarding `nym-node`:
@@ -93,9 +97,9 @@ python --version
</Tabs>
</div>
###### 2. Install `node_api_check.py` and make executable
###### 2. Install `node_api_check.py` and make executable
To run the program you neet to have [`node_api_check.py`](https://github.com/nymtech/nym/tree/develop/scripts/node_api_check.py) and [`api_endpoints.json`](https://github.com/nymtech/nym/tree/develop/scripts/api_endpoints.json).
To run the program you neet to have [`node_api_check.py`](https://github.com/nymtech/nym/tree/develop/scripts/node_api_check.py) and [`api_endpoints.json`](https://github.com/nymtech/nym/tree/develop/scripts/api_endpoints.json).
- If you [compiled from source](../../binaries/building-nym.mdx), you already have both of these files. Note that the latest version of this program may be on `develop` branch.
@@ -159,4 +163,3 @@ Another command is `version_count` where at least one `nym-node` version is requ
```sh
./node_api_check version_count 1.1.0 1.1.1 1.1.2 1.1.3 --markdown
```
@@ -232,6 +232,9 @@ username soft nofile 4096
Then reboot your server and restart your node.
## Running `nym-node` as a non-root
Some operators prefer to run `nym-node` without root privileges. It's possible but still `nym-node` binary needs higher privileges for network-level operations demanding these permissions. If you are starting a new `nym-node` and want to run it as a non-root, follow [this guide](../nym-node/configuration#running-nym-node-as-a-non-root) before you proceed with the node setup sections.
## Ports reference tables
@@ -161,7 +161,7 @@ In dVPN (2-hop) mode every node which meets the performance criteria, including
In both cases, the selection algorithm also looks whether the node runs with [Terms & Conditions](nodes/nym-node/setup.mdx#terms--conditions) accepted **AND** if it's not a legacy binary version. In case either of these criterias are not met, the node will have be excluded from the rewarded set selection.
To read more about rewards calculation, please see next page [*Nym Operators Rewards*](mixnet-rewards.md).
To read more about rewards calculation, please see next page [*Nym Operators Rewards*](tokenomics/mixnet-rewards.md).
## Query Validator API
@@ -153,7 +153,7 @@ The entire active set selection probablity:
For a comparison we made an example with 5 nodes, where first number is node performance and second stake saturation (assuming all of them `config_score` = 1 and not 0):
<br />
<AccordionTemplate name="✏️ Example: Reward set selection">
<AccordionTemplate name="✏️ Example: Reward set selection probability calculation">
> node_1 = 1.00 ^ 20 \* 1.0 = 1 <br />
> node_2 = 1.00 ^ 20 \* 0.5 = 0.5 <br />
> node_3 = 0.99 ^ 20 \* 1.0 = 0.818 <br />
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "explorer-api"
version = "1.1.42"
version = "1.1.43"
edition = "2021"
license.workspace = true
+1 -1
View File
@@ -166,7 +166,7 @@ impl GeoIp {
}
}
impl<'a> TryFrom<&City<'a>> for Location {
impl TryFrom<&City<'_>> for Location {
type Error = String;
fn try_from(city: &City) -> Result<Self, Self::Error> {
+1 -1
View File
@@ -65,7 +65,7 @@ impl<'r> FromRequest<'r> for Location {
}
}
impl<'a> OpenApiFromRequest<'a> for Location {
impl OpenApiFromRequest<'_> for Location {
fn from_request_input(
_gen: &mut OpenApiGenerator,
_name: String,
+46 -12
View File
@@ -5,6 +5,8 @@ use async_trait::async_trait;
use nym_sdk::{NymApiTopologyProvider, NymApiTopologyProviderConfig, UserAgent};
use nym_topology::{gateway, NymTopology, TopologyProvider};
use std::sync::Arc;
use std::time::Duration;
use time::OffsetDateTime;
use tokio::sync::Mutex;
use tracing::debug;
use url::Url;
@@ -17,6 +19,7 @@ pub struct GatewayTopologyProvider {
impl GatewayTopologyProvider {
pub fn new(
gateway_node: gateway::LegacyNode,
cache_ttl: Duration,
user_agent: UserAgent,
nym_api_url: Vec<Url>,
) -> GatewayTopologyProvider {
@@ -31,6 +34,9 @@ impl GatewayTopologyProvider {
env!("CARGO_PKG_VERSION").to_string(),
Some(user_agent),
),
cache_ttl,
cached_at: OffsetDateTime::UNIX_EPOCH,
cached: None,
gateway_node,
})),
}
@@ -39,25 +45,53 @@ impl GatewayTopologyProvider {
struct GatewayTopologyProviderInner {
inner: NymApiTopologyProvider,
cache_ttl: Duration,
cached_at: OffsetDateTime,
cached: Option<NymTopology>,
gateway_node: gateway::LegacyNode,
}
impl GatewayTopologyProviderInner {
fn cached_topology(&self) -> Option<NymTopology> {
if let Some(cached_topology) = &self.cached {
if self.cached_at + self.cache_ttl > OffsetDateTime::now_utc() {
return Some(cached_topology.clone());
}
}
None
}
async fn update_cache(&mut self) -> Option<NymTopology> {
let updated_cache = match self.inner.get_new_topology().await {
None => None,
Some(mut base) => {
if !base.gateway_exists(&self.gateway_node.identity_key) {
debug!(
"{} didn't exist in topology. inserting it.",
self.gateway_node.identity_key
);
base.insert_gateway(self.gateway_node.clone());
}
Some(base)
}
};
self.cached_at = OffsetDateTime::now_utc();
self.cached = updated_cache.clone();
updated_cache
}
}
#[async_trait]
impl TopologyProvider for GatewayTopologyProvider {
async fn get_new_topology(&mut self) -> Option<NymTopology> {
let mut guard = self.inner.lock().await;
match guard.inner.get_new_topology().await {
None => None,
Some(mut base) => {
if !base.gateway_exists(&guard.gateway_node.identity_key) {
debug!(
"{} didn't exist in topology. inserting it.",
guard.gateway_node.identity_key
);
base.insert_gateway(guard.gateway_node.clone());
}
Some(base)
}
// check the cache
if let Some(cached) = guard.cached_topology() {
return Some(cached);
}
guard.update_cache().await
}
}
+26 -16
View File
@@ -30,6 +30,7 @@ use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::path::PathBuf;
use std::process;
use std::sync::Arc;
use std::time::Duration;
use tracing::*;
pub(crate) mod client_handling;
@@ -148,8 +149,11 @@ impl<St> Gateway<St> {
}
fn gateway_topology_provider(&self) -> GatewayTopologyProvider {
// TODO: make topology ttl configurable
// (to be done in reeses with the final smooshing)
GatewayTopologyProvider::new(
self.as_topology_node(),
Duration::from_secs(5 * 60),
self.user_agent.clone(),
self.config.gateway.nym_api_urls.clone(),
)
@@ -231,22 +235,28 @@ impl<St> Gateway<St> {
forwarding_channel,
router_tx,
);
let all_peers = self.client_storage.get_all_wireguard_peers().await?;
let used_private_network_ips = all_peers
.iter()
.cloned()
.map(|wireguard_peer| {
defguard_wireguard_rs::host::Peer::try_from(wireguard_peer).map(|mut peer| {
peer.allowed_ips
.pop()
.ok_or(Box::new(GatewayError::InternalWireguardError(format!(
"no private IP set for peer {}",
peer.public_key
))))
.map(|p| p.ip)
})
})
.collect::<Result<Result<Vec<_>, _>, _>>()??;
let mut used_private_network_ips = vec![];
let mut all_peers = vec![];
for wireguard_peer in self
.client_storage
.get_all_wireguard_peers()
.await?
.into_iter()
{
let mut peer = defguard_wireguard_rs::host::Peer::try_from(wireguard_peer.clone())?;
let Some(peer) = peer.allowed_ips.pop() else {
tracing::warn!(
"Peer {} has empty allowed ips. It will be removed",
peer.public_key
);
self.client_storage
.remove_wireguard_peer(&peer.public_key.to_string())
.await?;
continue;
};
used_private_network_ips.push(peer.ip);
all_peers.push(wireguard_peer);
}
if let Some(wireguard_data) = self.wireguard_data.take() {
let (on_start_tx, on_start_rx) = oneshot::channel();
+3 -2
View File
@@ -4,7 +4,7 @@
[package]
name = "nym-api"
license = "GPL-3.0"
version = "1.1.46"
version = "1.1.47"
authors.workspace = true
edition = "2021"
rust-version.workspace = true
@@ -20,6 +20,7 @@ bloomfilter = { workspace = true }
cfg-if = { workspace = true }
clap = { workspace = true, features = ["cargo", "derive", "env"] }
console-subscriber = { workspace = true, optional = true } # validator-api needs to be built with RUSTFLAGS="--cfg tokio_unstable"
dashmap = { workspace = true }
dirs = { workspace = true }
futures = { workspace = true }
itertools = { workspace = true }
@@ -120,7 +121,7 @@ nym-types = { path = "../common/types" }
nym-http-api-common = { path = "../common/http-api-common", features = ["utoipa"] }
nym-serde-helpers = { path = "../common/serde-helpers", features = ["date"] }
nym-ticketbooks-merkle = { path = "../common/ticketbooks-merkle" }
nym-statistics-common = {path ="../common/statistics" }
nym-statistics-common = { path = "../common/statistics" }
[features]
no-reward = []
@@ -0,0 +1,8 @@
/*
* Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
* SPDX-License-Identifier: Apache-2.0
*/
CREATE INDEX IF NOT EXISTS monitor_run_id on monitor_run(id);
CREATE INDEX IF NOT EXISTS monitor_run_timestamp on monitor_run(timestamp);
CREATE INDEX IF NOT EXISTS testing_route_monitor_run_id on testing_route(monitor_run_id);
+1 -1
View File
@@ -64,7 +64,7 @@ pub(crate) mod overengineered_offset_date_time_serde {
])),
];
impl<'de> Visitor<'de> for OffsetDateTimeVisitor {
impl Visitor<'_> for OffsetDateTimeVisitor {
type Value = OffsetDateTime;
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
+11 -21
View File
@@ -6,7 +6,7 @@ use crate::ecash::error::EcashError;
use crate::ecash::state::EcashState;
use crate::node_status_api::models::AxumResult;
use crate::support::http::state::AppState;
use axum::extract::Path;
use axum::extract::{Query, State};
use axum::{Json, Router};
use nym_api_requests::ecash::models::{
AggregatedCoinIndicesSignatureResponse, AggregatedExpirationDateSignatureResponse,
@@ -21,28 +21,19 @@ use tracing::trace;
use utoipa::IntoParams;
/// routes with globally aggregated keys, signatures, etc.
pub(crate) fn aggregation_routes(ecash_state: Arc<EcashState>) -> Router<AppState> {
pub(crate) fn aggregation_routes() -> Router<AppState> {
Router::new()
.route(
"/master-verification-key",
axum::routing::get({
let ecash_state = Arc::clone(&ecash_state);
|epoch_id| master_verification_key(epoch_id, ecash_state)
}),
axum::routing::get(master_verification_key),
)
.route(
"/aggregated-expiration-date-signatures",
axum::routing::get({
let ecash_state = Arc::clone(&ecash_state);
|expiration_date| expiration_date_signatures(expiration_date, ecash_state)
}),
axum::routing::get(expiration_date_signatures),
)
.route(
"/aggregated-coin-indices-signatures",
axum::routing::get({
let ecash_state = Arc::clone(&ecash_state);
|epoch_id| coin_indices_signatures(epoch_id, ecash_state)
}),
axum::routing::get(coin_indices_signatures),
)
}
@@ -58,8 +49,8 @@ pub(crate) fn aggregation_routes(ecash_state: Arc<EcashState>) -> Router<AppStat
)
)]
async fn master_verification_key(
Path(EpochIdParam { epoch_id }): Path<EpochIdParam>,
state: Arc<EcashState>,
State(state): State<Arc<EcashState>>,
Query(EpochIdParam { epoch_id }): Query<EpochIdParam>,
) -> AxumResult<Json<VerificationKeyResponse>> {
trace!("aggregated_verification_key request");
@@ -72,7 +63,6 @@ async fn master_verification_key(
}
#[derive(Deserialize, IntoParams)]
#[into_params(parameter_in = Path)]
struct ExpirationDateParam {
expiration_date: Option<String>,
}
@@ -89,8 +79,8 @@ struct ExpirationDateParam {
)
)]
async fn expiration_date_signatures(
Path(ExpirationDateParam { expiration_date }): Path<ExpirationDateParam>,
state: Arc<EcashState>,
State(state): State<Arc<EcashState>>,
Query(ExpirationDateParam { expiration_date }): Query<ExpirationDateParam>,
) -> AxumResult<Json<AggregatedExpirationDateSignatureResponse>> {
trace!("aggregated_expiration_date_signatures request");
@@ -126,8 +116,8 @@ async fn expiration_date_signatures(
)
)]
async fn coin_indices_signatures(
Path(EpochIdParam { epoch_id }): Path<EpochIdParam>,
state: Arc<EcashState>,
Query(EpochIdParam { epoch_id }): Query<EpochIdParam>,
State(state): State<Arc<EcashState>>,
) -> AxumResult<Json<AggregatedCoinIndicesSignatureResponse>> {
trace!("aggregated_coin_indices_signatures request");
+5 -7
View File
@@ -5,15 +5,13 @@ use crate::ecash::api_routes::aggregation::aggregation_routes;
use crate::ecash::api_routes::issued::issued_routes;
use crate::ecash::api_routes::partial_signing::partial_signing_routes;
use crate::ecash::api_routes::spending::spending_routes;
use crate::ecash::state::EcashState;
use crate::support::http::state::AppState;
use axum::Router;
use std::sync::Arc;
pub(crate) fn ecash_routes(ecash_state: Arc<EcashState>) -> Router<AppState> {
pub(crate) fn ecash_routes() -> Router<AppState> {
Router::new()
.merge(aggregation_routes(Arc::clone(&ecash_state)))
.merge(issued_routes(Arc::clone(&ecash_state)))
.merge(partial_signing_routes(Arc::clone(&ecash_state)))
.merge(spending_routes(Arc::clone(&ecash_state)))
.merge(aggregation_routes())
.merge(issued_routes())
.merge(partial_signing_routes())
.merge(spending_routes())
}
-1
View File
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: GPL-3.0-only
#[derive(serde::Deserialize, utoipa::IntoParams)]
#[into_params(parameter_in = Path)]
pub(super) struct EpochIdParam {
pub(super) epoch_id: Option<u64>,
}
+6 -12
View File
@@ -4,7 +4,7 @@
use crate::ecash::state::EcashState;
use crate::node_status_api::models::AxumResult;
use crate::support::http::state::AppState;
use axum::extract::Path;
use axum::extract::{Path, State};
use axum::{Json, Router};
use nym_api_requests::ecash::models::{
IssuedTicketbooksChallengeRequest, IssuedTicketbooksChallengeResponse,
@@ -17,21 +17,15 @@ use time::Date;
use tracing::trace;
use utoipa::{IntoParams, ToSchema};
pub(crate) fn issued_routes(ecash_state: Arc<EcashState>) -> Router<AppState> {
pub(crate) fn issued_routes() -> Router<AppState> {
Router::new()
.route(
"/issued-ticketbooks-for/:expiration_date",
axum::routing::get({
let ecash_state = Arc::clone(&ecash_state);
|expiration_date| issued_ticketbooks_for(expiration_date, ecash_state)
}),
axum::routing::get(issued_ticketbooks_for),
)
.route(
"/issued-ticketbooks-challenge",
axum::routing::post({
let ecash_state = Arc::clone(&ecash_state);
|body| issued_ticketbooks_challenge(body, ecash_state)
}),
axum::routing::post(issued_ticketbooks_challenge),
)
}
@@ -58,8 +52,8 @@ pub(crate) struct ExpirationDatePathParam {
)
)]
async fn issued_ticketbooks_for(
State(state): State<Arc<EcashState>>,
Path(ExpirationDatePathParam { expiration_date }): Path<ExpirationDatePathParam>,
state: Arc<EcashState>,
) -> AxumResult<Json<IssuedTicketbooksForResponse>> {
state.ensure_signer().await?;
@@ -83,8 +77,8 @@ async fn issued_ticketbooks_for(
)
)]
async fn issued_ticketbooks_challenge(
State(state): State<Arc<EcashState>>,
Json(challenge): Json<IssuedTicketbooksChallengeRequest>,
state: Arc<EcashState>,
) -> AxumResult<Json<IssuedTicketbooksChallengeResponse>> {
trace!("replying to ticketbooks challenge: {:?}", challenge);
state.ensure_signer().await?;
@@ -7,7 +7,7 @@ use crate::ecash::helpers::blind_sign;
use crate::ecash::state::EcashState;
use crate::node_status_api::models::AxumResult;
use crate::support::http::state::AppState;
use axum::extract::Query;
use axum::extract::{Query, State};
use axum::{Json, Router};
use nym_api_requests::ecash::{
BlindSignRequestBody, BlindedSignatureResponse, PartialCoinIndicesSignatureResponse,
@@ -22,28 +22,16 @@ use time::Date;
use tracing::{debug, trace};
use utoipa::IntoParams;
pub(crate) fn partial_signing_routes(ecash_state: Arc<EcashState>) -> Router<AppState> {
pub(crate) fn partial_signing_routes() -> Router<AppState> {
Router::new()
.route(
"/blind-sign",
axum::routing::post({
let ecash_state = Arc::clone(&ecash_state);
|body| post_blind_sign(body, ecash_state)
}),
)
.route("/blind-sign", axum::routing::post(post_blind_sign))
.route(
"/partial-expiration-date-signatures",
axum::routing::get({
let ecash_state = Arc::clone(&ecash_state);
|expiration_date| partial_expiration_date_signatures(expiration_date, ecash_state)
}),
axum::routing::get(partial_expiration_date_signatures),
)
.route(
"/partial-coin-indices-signatures",
axum::routing::get({
let ecash_state = Arc::clone(&ecash_state);
|epoch_id| partial_coin_indices_signatures(epoch_id, ecash_state)
}),
axum::routing::get(partial_coin_indices_signatures),
)
}
@@ -59,8 +47,8 @@ pub(crate) fn partial_signing_routes(ecash_state: Arc<EcashState>) -> Router<App
)
)]
async fn post_blind_sign(
State(state): State<Arc<EcashState>>,
Json(blind_sign_request_body): Json<BlindSignRequestBody>,
state: Arc<EcashState>,
) -> AxumResult<Json<BlindedSignatureResponse>> {
state.ensure_signer().await?;
@@ -134,8 +122,8 @@ struct ExpirationDateParam {
)
)]
async fn partial_expiration_date_signatures(
State(state): State<Arc<EcashState>>,
Query(ExpirationDateParam { expiration_date }): Query<ExpirationDateParam>,
state: Arc<EcashState>,
) -> AxumResult<Json<PartialExpirationDateSignatureResponse>> {
state.ensure_signer().await?;
@@ -172,8 +160,8 @@ async fn partial_expiration_date_signatures(
)
)]
async fn partial_coin_indices_signatures(
State(state): State<Arc<EcashState>>,
Query(EpochIdParam { epoch_id }): Query<EpochIdParam>,
state: Arc<EcashState>,
) -> AxumResult<Json<PartialCoinIndicesSignatureResponse>> {
state.ensure_signer().await?;
+8 -21
View File
@@ -5,6 +5,7 @@ use crate::ecash::error::EcashError;
use crate::ecash::state::EcashState;
use crate::node_status_api::models::{AxumErrorResponse, AxumResult};
use crate::support::http::state::AppState;
use axum::extract::State;
use axum::{Json, Router};
use nym_api_requests::constants::MIN_BATCH_REDEMPTION_DELAY;
use nym_api_requests::ecash::models::{
@@ -21,28 +22,16 @@ use time::{OffsetDateTime, Time};
use tracing::{error, warn};
#[allow(deprecated)]
pub(crate) fn spending_routes(ecash_state: Arc<EcashState>) -> Router<AppState> {
pub(crate) fn spending_routes() -> Router<AppState> {
Router::new()
.route(
"/verify-ecash-ticket",
axum::routing::post({
let ecash_state = Arc::clone(&ecash_state);
|body| verify_ticket(body, ecash_state)
}),
)
.route("/verify-ecash-ticket", axum::routing::post(verify_ticket))
.route(
"/batch-redeem-ecash-tickets",
axum::routing::post({
let ecash_state = Arc::clone(&ecash_state);
|body| batch_redeem_tickets(body, ecash_state)
}),
axum::routing::post(batch_redeem_tickets),
)
.route(
"/double-spending-filter-v1",
axum::routing::get({
let ecash_state = Arc::clone(&ecash_state);
|| double_spending_filter_v1(ecash_state)
}),
axum::routing::get(double_spending_filter_v1),
)
}
@@ -67,9 +56,9 @@ fn reject_ticket(
)
)]
async fn verify_ticket(
State(state): State<Arc<EcashState>>,
// TODO in the future: make it send binary data rather than json
Json(verify_ticket_body): Json<VerifyEcashTicketBody>,
state: Arc<EcashState>,
) -> AxumResult<Json<EcashTicketVerificationResponse>> {
state.ensure_signer().await?;
@@ -170,9 +159,9 @@ async fn verify_ticket(
)
)]
async fn batch_redeem_tickets(
State(state): State<Arc<EcashState>>,
// TODO in the future: make it send binary data rather than json
Json(batch_redeem_credentials_body): Json<BatchRedeemTicketsBody>,
state: Arc<EcashState>,
) -> AxumResult<Json<EcashBatchTicketRedemptionResponse>> {
state.ensure_signer().await?;
@@ -244,8 +233,6 @@ async fn batch_redeem_tickets(
)
)]
#[deprecated]
async fn double_spending_filter_v1(
_state: Arc<EcashState>,
) -> AxumResult<Json<SpentCredentialsResponse>> {
async fn double_spending_filter_v1() -> AxumResult<Json<SpentCredentialsResponse>> {
AxumResult::Err(AxumErrorResponse::internal_msg("permanently restricted"))
}
+1 -1
View File
@@ -27,7 +27,7 @@ use nym_validator_client::EcashApiClient;
#[async_trait]
pub trait Client {
async fn address(&self) -> AccountId;
async fn address(&self) -> Result<AccountId>;
async fn dkg_contract_address(&self) -> Result<AccountId>;
+4 -4
View File
@@ -35,7 +35,7 @@ impl DkgClient {
}
}
pub(crate) async fn get_address(&self) -> AccountId {
pub(crate) async fn get_address(&self) -> Result<AccountId, EcashError> {
self.inner.address().await
}
@@ -53,7 +53,7 @@ impl DkgClient {
pub(crate) async fn group_member(&self) -> Result<MemberResponse, EcashError> {
self.inner
.group_member(self.get_address().await.to_string())
.group_member(self.get_address().await?.to_string())
.await
}
@@ -126,7 +126,7 @@ impl DkgClient {
&self,
epoch_id: EpochId,
) -> Result<Option<ContractVKShare>, EcashError> {
let address = self.inner.address().await;
let address = self.inner.address().await?;
self.get_verification_key_share(epoch_id, address).await
}
@@ -138,7 +138,7 @@ impl DkgClient {
}
pub(crate) async fn get_vote(&self, proposal_id: u64) -> Result<VoteResponse, EcashError> {
let address = self.get_address().await.to_string();
let address = self.get_address().await?.to_string();
self.inner.get_vote(proposal_id, address).await
}

Some files were not shown because too many files have changed in this diff Show More