Compare commits

..

1 Commits

Author SHA1 Message Date
Tommy Verrall 37f0b02cee Sign based upon github ref 2025-04-22 11:36:13 +02:00
406 changed files with 5962 additions and 10081 deletions
+11 -1
View File
@@ -14,6 +14,7 @@
# contracts
/contracts/mixnet @durch @jstuczyn
/contracts/vesting @durch @jstuczyn
/contracts/service-provider-directory @octol
# crypto code
/common/crypto/ @jstuczyn
@@ -21,5 +22,14 @@
/common/dkg/ @jstuczyn
/common/nymsphinx/ @jstuczyn
# rust sdk
/sdk/rust/ @octol
# nym-connect (rust)
/nym-connect/desktop/src-tauri/ @octol
# nym-wallet (rust)
/nym-wallet/src-tauri/ @octol
# documentation
/documentation @mfahampshire
/documentation @mfahampshire
+2
View File
@@ -31,3 +31,5 @@ updates:
update-types:
- "patch"
open-pull-requests-limit: 10
assignees:
- "octol"
-1
View File
@@ -13,7 +13,6 @@ on:
- 'nym-network-monitor/**'
- 'nym-node/**'
- 'nym-node-status-api/**'
- 'nym-statistics-api/**'
- 'nym-outfox/**'
- 'nym-validator-rewarder/**'
- 'nyx-chain-watcher/**'
@@ -16,7 +16,7 @@ jobs:
uses: actions/checkout@v4
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.4
uses: mikefarah/yq@v4.45.1
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
+1 -3
View File
@@ -20,7 +20,6 @@ jobs:
runs-on: ubuntu-22.04
env:
CARGO_TERM_COLOR: always
RUSTUP_PERMIT_COPY_RENAME: 1
steps:
- uses: actions/checkout@v4
@@ -28,8 +27,7 @@ jobs:
uses: actions-rs/toolchain@v1
with:
profile: minimal
# pinned due to issues building contracts
toolchain: 1.86.0
toolchain: stable
target: wasm32-unknown-unknown
override: true
components: rustfmt, clippy
+3 -1
View File
@@ -52,7 +52,7 @@ jobs:
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: 1.86.0
toolchain: stable
override: true
- name: Build all binaries
@@ -66,6 +66,7 @@ jobs:
with:
name: my-artifact
path: |
target/release/explorer-api
target/release/nym-client
target/release/nym-socks5-client
target/release/nym-api
@@ -81,6 +82,7 @@ jobs:
if: github.event_name == 'release'
with:
files: |
target/release/explorer-api
target/release/nym-client
target/release/nym-socks5-client
target/release/nym-api
+12 -8
View File
@@ -44,21 +44,24 @@ jobs:
- name: Download EV CodeSignTool from ssl.com
working-directory: nym-wallet/src-tauri
if: ${{ inputs.sign }}
if: ${{ inputs.sign || startsWith(github.ref, 'refs/tags/nym-wallet-') }}
shell: bash
run: |
curl -L0 https://www.ssl.com/download/codesigntool-for-linux-and-macos/ -o codesigntool.zip
unzip codesigntool.zip
chmod +x ./CodeSignTool.sh
- name: Get EV certificate credential id
working-directory: nym-wallet/src-tauri
if: ${{ inputs.sign }}
if: ${{ inputs.sign || startsWith(github.ref, 'refs/tags/nym-wallet-') }}
id: get_credential_ids
shell: bash
run: |
echo "SSL_COM_CREDENTIAL_ID=$(./CodeSignTool.sh get_credential_ids -username=${{ secrets.SSL_COM_USERNAME }} -password=${{ secrets.SSL_COM_PASSWORD }} | sed -n '1!p' | sed 's/- //')" >> "$GITHUB_OUTPUT"
- name: Add custom sign command to tauri.conf.json
working-directory: nym-wallet/src-tauri
if: ${{ inputs.sign }}
if: ${{ inputs.sign || startsWith(github.ref, 'refs/tags/nym-wallet-') }}
shell: bash
run: |
yq eval --inplace '.bundle.windows +=
@@ -79,6 +82,7 @@ jobs:
]
}
}' tauri.conf.json
- name: Install project dependencies
shell: bash
run: cd .. && yarn --network-timeout 100000
@@ -93,10 +97,10 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
SSL_COM_USERNAME: ${{ inputs.sign && secrets.SSL_COM_USERNAME }}
SSL_COM_PASSWORD: ${{ inputs.sign && secrets.SSL_COM_PASSWORD }}
SSL_COM_CREDENTIAL_ID: ${{ inputs.sign && steps.get_credential_ids.outputs.SSL_COM_CREDENTIAL_ID }}
SSL_COM_TOTP_SECRET: ${{ inputs.sign && secrets.SSL_COM_TOTP_SECRET }}
SSL_COM_USERNAME: ${{ (inputs.sign || startsWith(github.ref, 'refs/tags/nym-wallet-')) && secrets.SSL_COM_USERNAME }}
SSL_COM_PASSWORD: ${{ (inputs.sign || startsWith(github.ref, 'refs/tags/nym-wallet-')) && secrets.SSL_COM_PASSWORD }}
SSL_COM_CREDENTIAL_ID: ${{ (inputs.sign || startsWith(github.ref, 'refs/tags/nym-wallet-')) && steps.get_credential_ids.outputs.SSL_COM_CREDENTIAL_ID }}
SSL_COM_TOTP_SECRET: ${{ (inputs.sign || startsWith(github.ref, 'refs/tags/nym-wallet-')) && secrets.SSL_COM_TOTP_SECRET }}
run: |
echo "Starting build process..."
yarn build
@@ -140,7 +144,7 @@ jobs:
- id: create-release
name: Upload to release based on tag name
uses: softprops/action-gh-release@v2
if: github.event_name == 'release'
if: ${{ startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release' }}
with:
files: |
nym-wallet/${{ env.BUNDLE_PATH }}/msi/*.msi
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.4
uses: mikefarah/yq@v4.45.1
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-credential-proxy/Cargo.toml
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.4
uses: mikefarah/yq@v4.45.1
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.4
uses: mikefarah/yq@v4.45.1
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-network-monitor/Cargo.toml
@@ -31,7 +31,7 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.4
uses: mikefarah/yq@v4.45.1
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.4
uses: mikefarah/yq@v4.45.1
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
-51
View File
@@ -1,51 +0,0 @@
name: Build and upload Nym APU container to harbor.nymte.ch
on:
workflow_dispatch:
env:
WORKING_DIRECTORY: "."
CONTAINER_NAME: "nym-api"
jobs:
build-container:
runs-on: arc-ubuntu-22.04-dind
steps:
- name: Login to Harbor
uses: docker/login-action@v3
with:
registry: harbor.nymte.ch
username: ${{ secrets.HARBOR_ROBOT_USERNAME }}
password: ${{ secrets.HARBOR_ROBOT_SECRET }}
- name: Checkout repo
uses: actions/checkout@v4
- name: Configure git identity
run: |
git config --global user.email "lawrence@nymtech.net"
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.4
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-api/Cargo.toml
- name: Remove existing tag if exists
run: |
echo "Checking if tag ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }} exists..."
if git rev-parse ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }} >/dev/null 2>&1; then
echo "Tag ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }} already exists"
git push --delete origin ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }}
git tag -d ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }}
fi
- name: Create tag
run: |
git tag -a ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }} -m "Version ${{ steps.get_version.outputs.result }}"
git push origin ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }}
- name: BuildAndPushImageOnHarbor
run: |
docker build -f nym-api.dockerfile ${{ env.WORKING_DIRECTORY }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.result }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:latest
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.4
uses: mikefarah/yq@v4.45.1
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.4
uses: mikefarah/yq@v4.45.1
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.4
uses: mikefarah/yq@v4.45.1
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
-3
View File
@@ -59,6 +59,3 @@ nym-api/redocly/formatted-openapi.json
*.sqlite
.build
**/settings.sql
**/enter_db.sh
-124
View File
@@ -4,130 +4,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [2025.11-cheddar] (2025-06-10)
- No autoremoval of peers ([#5831])
- Set cached storage counters to 0 ([#5812])
- hack: temporarily use next.config.js instead of next.config.ts ([#5805])
- chore: resolve 1.87 clippy warnings ([#5802])
- Nym Statistics API ([#5800])
- QoL: RequestPath trait for http-api-client ([#5788])
- Fix contains ticketbook function that always returned true ([#5787])
- swap a decode into a fromrow to please future postgres feature ([#5785])
- Make address cache configurable ([#5784])
- Track wireguard credential retries ([#5783])
[#5831]: https://github.com/nymtech/nym/pull/5831
[#5812]: https://github.com/nymtech/nym/pull/5812
[#5805]: https://github.com/nymtech/nym/pull/5805
[#5802]: https://github.com/nymtech/nym/pull/5802
[#5800]: https://github.com/nymtech/nym/pull/5800
[#5788]: https://github.com/nymtech/nym/pull/5788
[#5787]: https://github.com/nymtech/nym/pull/5787
[#5785]: https://github.com/nymtech/nym/pull/5785
[#5784]: https://github.com/nymtech/nym/pull/5784
[#5783]: https://github.com/nymtech/nym/pull/5783
## [2025.10-brie] (2025-05-27)
- Backport PR 5779 ([#5801])
- Expanded Accept Encoding for `reqwest` ([#5779])
- Teach HttpClientError how to report its status code and timeout ([#5770])
- Skip refreshing the topology on startup as we already have an initial set ([#5768])
- Fetch the topology from the nym-api concurrently ([#5767])
- feat: use bincode by default in NymApiClient + remove feature-lock ([#5761])
- Instrument create_request ([#5760])
- Add node_bonded field to delegations ([#5759])
- build(deps): bump mikefarah/yq from 4.45.1 to 4.45.4 ([#5758])
- Raw route submissions ([#5756])
- feat: expires header for `/active` nym-api responses ([#5755])
- Decrease default average packet delay to 15 ms ([#5754])
- build(deps): bump the patch-updates group across 1 directory with 12 updates ([#5753])
- Remove pretty_env_logger and switch remaining crates to use tracing ([#5749])
- Update pretty_env_logger to latest to not depend on unmaintained crate atty ([#5748])
- Upgrade prometheus crate to fix security warning ([#5747])
- Downgrade deranged crate to 0.4.0 ([#5746])
- feat: nym-api bincode + yaml support ([#5745])
- fix parallel feature in ecash crate with send + sync ([#5744])
- Remove old test directory - Update validator docker ([#5743])
- [Feature] `RememberMe` is the new don't `ForgetMe` ([#5742])
- build(deps): bump ammonia from 4.0.0 to 4.1.0 ([#5739])
- build(deps): bump base-x from 3.0.9 to 3.0.11 in /testnet-faucet ([#5737])
- build(deps): bump http-proxy-middleware from 2.0.8 to 2.0.9 ([#5730])
[#5801]: https://github.com/nymtech/nym/pull/5801
[#5779]: https://github.com/nymtech/nym/pull/5779
[#5770]: https://github.com/nymtech/nym/pull/5770
[#5768]: https://github.com/nymtech/nym/pull/5768
[#5767]: https://github.com/nymtech/nym/pull/5767
[#5761]: https://github.com/nymtech/nym/pull/5761
[#5760]: https://github.com/nymtech/nym/pull/5760
[#5759]: https://github.com/nymtech/nym/pull/5759
[#5758]: https://github.com/nymtech/nym/pull/5758
[#5756]: https://github.com/nymtech/nym/pull/5756
[#5755]: https://github.com/nymtech/nym/pull/5755
[#5754]: https://github.com/nymtech/nym/pull/5754
[#5753]: https://github.com/nymtech/nym/pull/5753
[#5749]: https://github.com/nymtech/nym/pull/5749
[#5748]: https://github.com/nymtech/nym/pull/5748
[#5747]: https://github.com/nymtech/nym/pull/5747
[#5746]: https://github.com/nymtech/nym/pull/5746
[#5745]: https://github.com/nymtech/nym/pull/5745
[#5744]: https://github.com/nymtech/nym/pull/5744
[#5743]: https://github.com/nymtech/nym/pull/5743
[#5742]: https://github.com/nymtech/nym/pull/5742
[#5739]: https://github.com/nymtech/nym/pull/5739
[#5737]: https://github.com/nymtech/nym/pull/5737
[#5730]: https://github.com/nymtech/nym/pull/5730
## [2025.9-appenzeller] (2025-05-13)
- build(deps): bump clap from 4.5.36 to 4.5.37 in the patch-updates group ([#5722])
- build(deps): bump golang.org/x/net from 0.36.0 to 0.38.0 in /wasm/mix-fetch/go-mix-conn ([#5720])
- build(deps-dev): bump http-proxy-middleware from 2.0.6 to 2.0.9 in /wasm/client/internal-dev ([#5719])
- Add /account/{address} ([#5673])
- Add contains ticketbook data db query ([#5670])
[#5722]: https://github.com/nymtech/nym/pull/5722
[#5720]: https://github.com/nymtech/nym/pull/5720
[#5719]: https://github.com/nymtech/nym/pull/5719
[#5673]: https://github.com/nymtech/nym/pull/5673
[#5670]: https://github.com/nymtech/nym/pull/5670
## [2025.8-tourist] (2025-04-29)
- add reserved byte to reply surb serialisation ([#5731])
- Remove inactive peers ([#5721])
- Update Hickory DNS "0.24.4" to "0.25" ([#5709])
- build(deps): bump the patch-updates group across 1 directory with 7 updates ([#5708])
- Peer handle should die more gracefully ([#5704])
- build(deps): bump crossbeam-channel from 0.5.14 to 0.5.15 ([#5702])
- build(deps): bump actions/checkout from 3 to 4 ([#5700])
- Feature/updated sphinx payload keys ([#5698])
- Bump the nym-vpn deb metapackage to 1.0 ([#5697])
- Make mix hops optional for Mixnet Client ([#5696])
- build(deps): bump tokio from 1.44.1 to 1.44.2 ([#5693])
- Feature/replay protection ([#5682])
- Adding fresh nym-api tests and workflow ([#5659])
- build(deps): bump next from 14.2.21 to 14.2.25 ([#5655])
- build(deps): bump pnpm/action-setup from 4.0.0 to 4.1.0 ([#5436])
[#5731]: https://github.com/nymtech/nym/pull/5731
[#5721]: https://github.com/nymtech/nym/pull/5721
[#5709]: https://github.com/nymtech/nym/pull/5709
[#5708]: https://github.com/nymtech/nym/pull/5708
[#5704]: https://github.com/nymtech/nym/pull/5704
[#5702]: https://github.com/nymtech/nym/pull/5702
[#5700]: https://github.com/nymtech/nym/pull/5700
[#5698]: https://github.com/nymtech/nym/pull/5698
[#5697]: https://github.com/nymtech/nym/pull/5697
[#5696]: https://github.com/nymtech/nym/pull/5696
[#5693]: https://github.com/nymtech/nym/pull/5693
[#5682]: https://github.com/nymtech/nym/pull/5682
[#5659]: https://github.com/nymtech/nym/pull/5659
[#5655]: https://github.com/nymtech/nym/pull/5655
[#5436]: https://github.com/nymtech/nym/pull/5436
## [2025.7-tex] (2025-04-14)
- Expand /v3/nym-nodes with geodata ([#5686])
Generated
+241 -460
View File
File diff suppressed because it is too large Load Diff
+18 -15
View File
@@ -112,7 +112,6 @@ members = [
"nym-node/nym-node-metrics",
"nym-node/nym-node-requests",
"nym-outfox",
"nym-statistics-api",
"nym-validator-rewarder",
"nyx-chain-watcher",
"sdk/ffi/cpp",
@@ -123,7 +122,7 @@ members = [
"service-providers/common",
"service-providers/ip-packet-router",
"service-providers/network-requester",
"sqlx-pool-guard",
"tools/echo-server",
"tools/echo-server",
"tools/internal/contract-state-importer/importer-cli",
"tools/internal/contract-state-importer/importer-contract",
@@ -154,7 +153,6 @@ default-members = [
"nym-node",
"nym-node-status-api/nym-node-status-agent",
"nym-node-status-api/nym-node-status-api",
"nym-statistics-api",
"nym-validator-rewarder",
"nyx-chain-watcher",
"service-providers/authenticator",
@@ -163,7 +161,12 @@ default-members = [
"tools/nymvisor",
]
exclude = ["explorer", "contracts", "nym-wallet", "cpu-cycles"]
exclude = [
"explorer",
"contracts",
"nym-wallet",
"cpu-cycles",
]
[workspace.package]
authors = ["Nym Technologies SA"]
@@ -206,9 +209,9 @@ celes = "2.6.0"
cfg-if = "1.0.0"
chacha20 = "0.9.0"
chacha20poly1305 = "0.10.1"
chrono = "0.4.41"
chrono = "0.4.40"
cipher = "0.4.3"
clap = "4.5.38"
clap = "4.5.36"
clap_complete = "4.5"
clap_complete_fig = "4.5"
colored = "2.2"
@@ -283,8 +286,8 @@ pem = "0.8"
petgraph = "0.6.5"
pin-project = "1.1"
pin-project-lite = "0.2.16"
pretty_env_logger = "0.4.0"
publicsuffix = "2.3.0"
proc_pidinfo = "0.1.3"
quote = "1"
rand = "0.8.5"
rand_chacha = "0.3"
@@ -307,7 +310,7 @@ serde_json_path = "0.7.2"
serde_repr = "0.1"
serde_with = "3.9.0"
serde_yaml = "0.9.25"
sha2 = "0.10.9"
sha2 = "0.10.8"
si-scale = "0.2.3"
sphinx-packet = "=0.6.0"
sqlx = "0.7.4"
@@ -327,8 +330,8 @@ tokio-stream = "0.1.17"
tokio-test = "0.4.4"
tokio-tun = "0.11.5"
tokio-tungstenite = { version = "0.20.1" }
tokio-util = "0.7.15"
toml = "0.8.22"
tokio-util = "0.7.14"
toml = "0.8.20"
tower = "0.5.2"
tower-http = "0.5.2"
tracing = "0.1.41"
@@ -339,7 +342,7 @@ tracing-tree = "0.2.2"
tracing-indicatif = "0.3.9"
ts-rs = "10.1.0"
tungstenite = { version = "0.20.1", default-features = false }
uniffi = "0.29.2"
uniffi = "0.29.1"
uniffi_build = "0.29.0"
url = "2.5"
utoipa = "5.2"
@@ -352,7 +355,7 @@ wasm-bindgen-test = "0.3.49"
x25519-dalek = "2.0.0"
zeroize = "1.7.0"
prometheus = { version = "0.14.0" }
prometheus = { version = "0.13.0" }
# coconut/DKG related
# unfortunately until https://github.com/zkcrypto/bls12_381/issues/10 is resolved, we have to rely on the fork
@@ -382,15 +385,15 @@ bip32 = { version = "0.5.3", default-features = false }
cosmrs = { version = "0.21.1" }
tendermint = "0.40.4"
tendermint-rpc = "0.40.4"
tendermint = "0.40.3"
tendermint-rpc = "0.40.3"
prost = { version = "0.13", default-features = false }
# wasm-related dependencies
gloo-utils = "0.2.0"
gloo-net = "0.6.0"
indexed_db_futures = "0.6.4"
indexed_db_futures = "0.6.1"
js-sys = "0.3.76"
serde-wasm-bindgen = "0.6.5"
tsify = "0.4.5"
+1 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "1.1.57"
version = "1.1.53"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
@@ -46,7 +46,6 @@ nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
nym-bin-common = { path = "../../common/bin-common", features = [
"output_format",
"clap",
"basic_tracing",
] }
nym-client-core = { path = "../../common/client-core", features = [
"fs-credentials-storage",
-1
View File
@@ -25,7 +25,6 @@ pub mod old_config_v1_1_13;
pub mod old_config_v1_1_20;
pub mod old_config_v1_1_20_2;
pub mod old_config_v1_1_33;
pub mod old_config_v1_1_54;
mod persistence;
mod template;
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::client::config::persistence::ClientPaths;
use crate::client::config::{default_config_filepath, Socket, SocketType};
use crate::client::config::{default_config_filepath, Config, Socket, SocketType};
use crate::error::ClientError;
use nym_bin_common::logging::LoggingSettings;
use nym_client_core::config::disk_persistence::old_v1_1_33::CommonClientPathsV1_1_33;
@@ -14,8 +14,6 @@ use std::io;
use std::net::{IpAddr, Ipv4Addr};
use std::path::Path;
use super::old_config_v1_1_54::ConfigV1_1_54;
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone)]
pub struct ClientPathsV1_1_33 {
#[serde(flatten)]
@@ -35,21 +33,6 @@ pub struct ConfigV1_1_33 {
pub logging: LoggingSettings,
}
impl TryFrom<ConfigV1_1_33> for ConfigV1_1_54 {
type Error = ClientError;
fn try_from(value: ConfigV1_1_33) -> Result<Self, Self::Error> {
Ok(ConfigV1_1_54 {
base: value.base.into(),
socket: value.socket.into(),
storage_paths: ClientPaths {
common_paths: value.storage_paths.common_paths.upgrade_default()?,
},
logging: value.logging,
})
}
}
impl ConfigV1_1_33 {
pub fn read_from_toml_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
read_config_from_toml_file(path)
@@ -58,6 +41,17 @@ impl ConfigV1_1_33 {
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
Self::read_from_toml_file(default_config_filepath(id))
}
pub fn try_upgrade(self) -> Result<Config, ClientError> {
Ok(Config {
base: self.base.into(),
socket: self.socket.into(),
storage_paths: ClientPaths {
common_paths: self.storage_paths.common_paths.upgrade_default()?,
},
logging: self.logging,
})
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone, Copy)]
@@ -1,41 +0,0 @@
use std::{io, path::Path};
use nym_bin_common::logging::LoggingSettings;
use nym_client_core::config::old_config_v1_1_54::ConfigV1_1_54 as BaseConfigV1_1_54;
use nym_config::read_config_from_toml_file;
use serde::{Deserialize, Serialize};
use crate::error::ClientError;
use super::{default_config_filepath, persistence::ClientPaths, Config, Socket};
#[derive(Debug, Deserialize, PartialEq, Serialize, Clone)]
pub struct ConfigV1_1_54 {
#[serde(flatten)]
pub base: BaseConfigV1_1_54,
pub socket: Socket,
pub storage_paths: ClientPaths,
pub logging: LoggingSettings,
}
impl ConfigV1_1_54 {
pub fn read_from_toml_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
read_config_from_toml_file(path)
}
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
Self::read_from_toml_file(default_config_filepath(id))
}
pub fn try_upgrade(self) -> Result<Config, ClientError> {
Ok(Config {
base: self.base.into(),
socket: self.socket,
storage_paths: self.storage_paths,
logging: self.logging,
})
}
}
@@ -92,6 +92,10 @@ host = '{{ socket.host }}'
[debug]
[debug.traffic]
average_packet_delay = '{{ debug.traffic.average_packet_delay }}'
message_sending_average_delay = '{{ debug.traffic.message_sending_average_delay }}'
[debug.acknowledgements]
average_ack_delay = '{{ debug.acknowledgements.average_ack_delay }}'
+4 -28
View File
@@ -5,7 +5,6 @@ use crate::client::config::old_config_v1_1_13::OldConfigV1_1_13;
use crate::client::config::old_config_v1_1_20::ConfigV1_1_20;
use crate::client::config::old_config_v1_1_20_2::ConfigV1_1_20_2;
use crate::client::config::old_config_v1_1_33::ConfigV1_1_33;
use crate::client::config::old_config_v1_1_54::ConfigV1_1_54;
use crate::client::config::{BaseClientConfig, Config};
use crate::commands::ecash::Ecash;
use crate::error::ClientError;
@@ -178,8 +177,7 @@ async fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, ClientError> {
let updated_step2: ConfigV1_1_20_2 = updated_step1.into();
let (updated_step3, gateway_config) = updated_step2.upgrade()?;
let old_paths = updated_step3.storage_paths.clone();
let updated_step4: ConfigV1_1_54 = updated_step3.try_into()?;
let updated = updated_step4.try_upgrade()?;
let updated = updated_step3.try_upgrade()?;
v1_1_33::migrate_gateway_details(
&old_paths.common_paths,
@@ -207,8 +205,7 @@ async fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, ClientError> {
let updated_step1: ConfigV1_1_20_2 = old_config.into();
let (updated_step2, gateway_config) = updated_step1.upgrade()?;
let old_paths = updated_step2.storage_paths.clone();
let updated_step3: ConfigV1_1_54 = updated_step2.try_into()?;
let updated = updated_step3.try_upgrade()?;
let updated = updated_step2.try_upgrade()?;
v1_1_33::migrate_gateway_details(
&old_paths.common_paths,
@@ -232,8 +229,7 @@ async fn try_upgrade_v1_1_20_2_config(id: &str) -> Result<bool, ClientError> {
let (updated_step1, gateway_config) = old_config.upgrade()?;
let old_paths = updated_step1.storage_paths.clone();
let updated_step2: ConfigV1_1_54 = updated_step1.try_into()?;
let updated = updated_step2.try_upgrade()?;
let updated = updated_step1.try_upgrade()?;
v1_1_33::migrate_gateway_details(
&old_paths.common_paths,
@@ -256,8 +252,7 @@ async fn try_upgrade_v1_1_33_config(id: &str) -> Result<bool, ClientError> {
info!("It is going to get updated to the current specification.");
let old_paths = old_config.storage_paths.clone();
let updated_step1: ConfigV1_1_54 = old_config.try_into()?;
let updated = updated_step1.try_upgrade()?;
let updated = old_config.try_upgrade()?;
v1_1_33::migrate_gateway_details(
&old_paths.common_paths,
@@ -270,22 +265,6 @@ async fn try_upgrade_v1_1_33_config(id: &str) -> Result<bool, ClientError> {
Ok(true)
}
async fn try_upgrade_v1_1_54_config(id: &str) -> Result<bool, ClientError> {
// explicitly load it as v1.1.54 (which is incompatible with the current one, i.e. +1.1.55)
let Ok(old_config) = ConfigV1_1_54::read_from_default_path(id) else {
// if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(false);
};
info!("It seems the client is using <= v1.1.54 config template.");
info!("It is going to get updated to the current specification.");
let updated = old_config.try_upgrade()?;
updated.save_to_default_location()?;
Ok(true)
}
async fn try_upgrade_config(id: &str) -> Result<(), ClientError> {
if try_upgrade_v1_1_13_config(id).await? {
return Ok(());
@@ -299,9 +278,6 @@ async fn try_upgrade_config(id: &str) -> Result<(), ClientError> {
if try_upgrade_v1_1_33_config(id).await? {
return Ok(());
}
if try_upgrade_v1_1_54_config(id).await? {
return Ok(());
}
Ok(())
}
+2 -2
View File
@@ -4,7 +4,7 @@
use std::error::Error;
use clap::{crate_name, crate_version, Parser};
use nym_bin_common::logging::{maybe_print_banner, setup_tracing_logger};
use nym_bin_common::logging::{maybe_print_banner, setup_logging};
use nym_network_defaults::setup_env;
pub mod client;
@@ -20,7 +20,7 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
if !args.no_banner {
maybe_print_banner(crate_name!(), crate_version!());
}
setup_tracing_logger();
setup_logging();
if let Err(err) = commands::execute(args).await {
log::error!("{err}");
+1 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.57"
version = "1.1.53"
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"
@@ -27,7 +27,6 @@ zeroize = { workspace = true }
nym-bin-common = { path = "../../common/bin-common", features = [
"output_format",
"clap",
"basic_tracing",
] }
nym-client-core = { path = "../../common/client-core", features = [
"fs-credentials-storage",
+10 -36
View File
@@ -7,7 +7,6 @@ use crate::config::old_config_v1_1_20::ConfigV1_1_20;
use crate::config::old_config_v1_1_20_2::ConfigV1_1_20_2;
use crate::config::old_config_v1_1_30::ConfigV1_1_30;
use crate::config::old_config_v1_1_33::ConfigV1_1_33;
use crate::config::old_config_v1_1_54::ConfigV1_1_54;
use crate::config::{BaseClientConfig, Config};
use crate::error::Socks5ClientError;
use clap::CommandFactory;
@@ -205,16 +204,15 @@ async fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, Socks5ClientError>
let old_paths = updated_step3.storage_paths.clone();
let updated_step4: ConfigV1_1_33 = updated_step3.into();
let updated_step5: ConfigV1_1_54 = updated_step4.try_into()?;
let updated = updated_step4.try_upgrade()?;
v1_1_33::migrate_gateway_details(
&old_paths.common_paths,
&updated_step5.storage_paths.common_paths,
&updated.storage_paths.common_paths,
Some(gateway_config),
)
.await?;
let updated = updated_step5.try_upgrade()?;
updated.save_to_default_location()?;
Ok(true)
}
@@ -236,16 +234,15 @@ async fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, Socks5ClientError>
let old_paths = updated_step2.storage_paths.clone();
let updated_step3: ConfigV1_1_33 = updated_step2.into();
let updated_step4: ConfigV1_1_54 = updated_step3.try_into()?;
let updated = updated_step3.try_upgrade()?;
v1_1_33::migrate_gateway_details(
&old_paths.common_paths,
&updated_step4.storage_paths.common_paths,
&updated.storage_paths.common_paths,
Some(gateway_config),
)
.await?;
let updated = updated_step4.try_upgrade()?;
updated.save_to_default_location()?;
Ok(true)
}
@@ -264,17 +261,15 @@ async fn try_upgrade_v1_1_20_2_config(id: &str) -> Result<bool, Socks5ClientErro
let old_paths = updated_step1.storage_paths.clone();
let updated_step2: ConfigV1_1_33 = updated_step1.into();
let updated_step3: ConfigV1_1_54 = updated_step2.try_into()?;
let updated = updated_step2.try_upgrade()?;
v1_1_33::migrate_gateway_details(
&old_paths.common_paths,
&updated_step3.storage_paths.common_paths,
&updated.storage_paths.common_paths,
Some(gateway_config),
)
.await?;
let updated = updated_step3.try_upgrade()?;
updated.save_to_default_location()?;
Ok(true)
}
@@ -292,16 +287,15 @@ async fn try_upgrade_v1_1_30_config(id: &str) -> Result<bool, Socks5ClientError>
let old_paths = old_config.storage_paths.clone();
let updated_step1: ConfigV1_1_33 = old_config.into();
let updated_step2: ConfigV1_1_54 = updated_step1.try_into()?;
let updated = updated_step1.try_upgrade()?;
v1_1_33::migrate_gateway_details(
&old_paths.common_paths,
&updated_step2.storage_paths.common_paths,
&updated.storage_paths.common_paths,
None,
)
.await?;
let updated = updated_step2.try_upgrade()?;
updated.save_to_default_location()?;
Ok(true)
}
@@ -318,32 +312,15 @@ async fn try_upgrade_v1_1_33_config(id: &str) -> Result<bool, Socks5ClientError>
let old_paths = old_config.storage_paths.clone();
let updated_step1: ConfigV1_1_54 = old_config.try_into()?;
let updated = old_config.try_upgrade()?;
v1_1_33::migrate_gateway_details(
&old_paths.common_paths,
&updated_step1.storage_paths.common_paths,
&updated.storage_paths.common_paths,
None,
)
.await?;
let updated = updated_step1.try_upgrade()?;
updated.save_to_default_location()?;
Ok(true)
}
async fn try_upgrade_v1_1_54_config(id: &str) -> Result<bool, Socks5ClientError> {
// explicitly load it as v1.1.54 (which is incompatible with the current one, i.e. +1.1.55)
let Ok(old_config) = ConfigV1_1_54::read_from_default_path(id) else {
// if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(false);
};
info!("It seems the client is using <= v1.1.54 config template.");
info!("It is going to get updated to the current specification.");
let updated = old_config.try_upgrade()?;
updated.save_to_default_location()?;
Ok(true)
}
@@ -364,9 +341,6 @@ async fn try_upgrade_config(id: &str) -> Result<(), Socks5ClientError> {
if try_upgrade_v1_1_33_config(id).await? {
return Ok(());
}
if try_upgrade_v1_1_54_config(id).await? {
return Ok(());
}
Ok(())
}
-1
View File
@@ -25,7 +25,6 @@ pub mod old_config_v1_1_20;
pub mod old_config_v1_1_20_2;
pub mod old_config_v1_1_30;
pub mod old_config_v1_1_33;
pub mod old_config_v1_1_54;
mod persistence;
mod template;
+11 -17
View File
@@ -1,7 +1,7 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::{default_config_filepath, SocksClientPaths};
use crate::config::{default_config_filepath, Config, SocksClientPaths};
use crate::error::Socks5ClientError;
use nym_bin_common::logging::LoggingSettings;
use nym_client_core::config::disk_persistence::old_v1_1_33::CommonClientPathsV1_1_33;
@@ -11,8 +11,6 @@ use serde::{Deserialize, Serialize};
use std::io;
use std::path::Path;
use super::old_config_v1_1_54::ConfigV1_1_54;
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct SocksClientPathsV1_1_33 {
#[serde(flatten)]
@@ -30,20 +28,6 @@ pub struct ConfigV1_1_33 {
pub logging: LoggingSettings,
}
impl TryFrom<ConfigV1_1_33> for ConfigV1_1_54 {
type Error = Socks5ClientError;
fn try_from(value: ConfigV1_1_33) -> Result<Self, Self::Error> {
Ok(ConfigV1_1_54 {
core: value.core.into(),
storage_paths: SocksClientPaths {
common_paths: value.storage_paths.common_paths.upgrade_default()?,
},
logging: value.logging,
})
}
}
impl ConfigV1_1_33 {
pub fn read_from_toml_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
read_config_from_toml_file(path)
@@ -52,4 +36,14 @@ impl ConfigV1_1_33 {
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
Self::read_from_toml_file(default_config_filepath(id))
}
pub fn try_upgrade(self) -> Result<Config, Socks5ClientError> {
Ok(Config {
core: self.core.into(),
storage_paths: SocksClientPaths {
common_paths: self.storage_paths.common_paths.upgrade_default()?,
},
logging: self.logging,
})
}
}
@@ -1,39 +0,0 @@
use std::{io, path::Path};
use nym_bin_common::logging::LoggingSettings;
use nym_config::read_config_from_toml_file;
use nym_socks5_client_core::config::old_config_v1_1_54::ConfigV1_1_54 as CoreConfigV1_1_54;
use serde::{Deserialize, Serialize};
use crate::config::Config;
use crate::error::Socks5ClientError;
use super::{default_config_filepath, SocksClientPaths};
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_54 {
pub core: CoreConfigV1_1_54,
pub storage_paths: SocksClientPaths,
pub logging: LoggingSettings,
}
impl ConfigV1_1_54 {
pub fn read_from_toml_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
read_config_from_toml_file(path)
}
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
Self::read_from_toml_file(default_config_filepath(id))
}
pub fn try_upgrade(self) -> Result<Config, Socks5ClientError> {
Ok(Config {
core: self.core.into(),
storage_paths: self.storage_paths,
logging: self.logging,
})
}
}
+4
View File
@@ -98,6 +98,10 @@ send_anonymously = {{ core.socks5.send_anonymously }}
[core.debug]
[core.debug.traffic]
average_packet_delay = '{{ core.debug.traffic.average_packet_delay }}'
message_sending_average_delay = '{{ core.debug.traffic.message_sending_average_delay }}'
[core.debug.acknowledgements]
average_ack_delay = '{{ core.debug.acknowledgements.average_ack_delay }}'
+2 -2
View File
@@ -4,7 +4,7 @@
use std::error::Error;
use clap::{crate_name, crate_version, Parser};
use nym_bin_common::logging::{maybe_print_banner, setup_tracing_logger};
use nym_bin_common::logging::{maybe_print_banner, setup_logging};
use nym_network_defaults::setup_env;
mod commands;
@@ -19,7 +19,7 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
if !args.no_banner {
maybe_print_banner(crate_name!(), crate_version!());
}
setup_tracing_logger();
setup_logging();
if let Err(err) = commands::execute(args).await {
log::error!("{err}");
+1
View File
@@ -13,6 +13,7 @@ clap_complete = { workspace = true, optional = true }
clap_complete_fig = { workspace = true, optional = true }
const-str = { workspace = true }
log = { workspace = true }
pretty_env_logger = { workspace = true }
schemars = { workspace = true, features = ["preserve_order"], optional = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true, optional = true }
+23
View File
@@ -21,6 +21,29 @@ pub struct LoggingSettings {
// well, we need to implement something here at some point...
}
// I'd argue we should start transitioning from `log` to `tracing`
pub fn setup_logging() {
let mut log_builder = pretty_env_logger::formatted_timed_builder();
if let Ok(s) = ::std::env::var("RUST_LOG") {
log_builder.parse_filters(&s);
} else {
// default to 'Info'
log_builder.filter(None, log::LevelFilter::Info);
}
log_builder
.filter_module("hyper", log::LevelFilter::Warn)
.filter_module("tokio_reactor", log::LevelFilter::Warn)
.filter_module("reqwest", log::LevelFilter::Warn)
.filter_module("mio", log::LevelFilter::Warn)
.filter_module("want", log::LevelFilter::Warn)
.filter_module("tungstenite", log::LevelFilter::Warn)
.filter_module("tokio_tungstenite", log::LevelFilter::Warn)
.filter_module("handlebars", log::LevelFilter::Warn)
.filter_module("sled", log::LevelFilter::Warn)
.init();
}
// don't call init so that we could attach additional layers
#[cfg(feature = "basic_tracing")]
pub fn build_tracing_logger() -> impl tracing_subscriber::layer::SubscriberExt {
@@ -19,7 +19,6 @@ nym-pemstore = { path = "../../pemstore", optional = true }
# those are pulling so many deps T.T
nym-sphinx-params = { path = "../../nymsphinx/params" }
nym-sphinx-addressing = { path = "../../nymsphinx/addressing" }
nym-statistics-common = { path = "../../statistics" }
[features]
+3 -60
View File
@@ -5,7 +5,6 @@ use nym_config::defaults::NymNetworkDetails;
use nym_config::serde_helpers::{de_maybe_stringified, ser_maybe_stringified};
use nym_sphinx_addressing::Recipient;
use nym_sphinx_params::{PacketSize, PacketType};
use nym_statistics_common::types::SessionType;
use serde::{Deserialize, Serialize};
use std::time::Duration;
use url::Url;
@@ -23,7 +22,7 @@ const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5;
const DEFAULT_ACK_WAIT_ADDITION: Duration = Duration::from_millis(1_500);
const DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(200);
const DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(20);
const DEFAULT_AVERAGE_PACKET_DELAY: Duration = Duration::from_millis(15);
const DEFAULT_AVERAGE_PACKET_DELAY: Duration = Duration::from_millis(50);
const DEFAULT_TOPOLOGY_REFRESH_RATE: Duration = Duration::from_secs(5 * 60); // every 5min
const DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT: Duration = Duration::from_millis(5_000);
@@ -376,12 +375,14 @@ pub struct Traffic {
/// sent packet is going to be delayed at any given mix node.
/// So for a packet going through three mix nodes, on average, it will take three times this value
/// until the packet reaches its destination.
#[serde(with = "humantime_serde")]
pub average_packet_delay: Duration,
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take another 'real traffic stream' message to be sent.
/// If no real packets are available and cover traffic is enabled,
/// a loop cover message is sent instead in order to preserve the rate.
#[serde(with = "humantime_serde")]
pub message_sending_average_delay: Duration,
/// Controls whether the main packet stream constantly produces packets according to the predefined
@@ -717,9 +718,6 @@ pub struct DebugConfig {
/// Defines all configuration options related to the forget me flag.
pub forget_me: ForgetMe,
/// Defines all configuration options related to the remember me flag.
pub remember_me: RememberMe,
}
impl DebugConfig {
@@ -743,7 +741,6 @@ impl Default for DebugConfig {
reply_surbs: Default::default(),
stats_reporting: Default::default(),
forget_me: Default::default(),
remember_me: Default::default(),
}
}
}
@@ -809,57 +806,3 @@ impl ForgetMe {
}
}
}
#[derive(Clone, Default, Debug, Deserialize, PartialEq, Serialize, Copy)]
pub struct RememberMe {
/// Signal that this client should be accounted for in the stats
stats: bool,
/// Type of the session to remember, if it should be remembered
session_type: SessionType,
}
impl RememberMe {
pub fn new_vpn() -> Self {
Self {
stats: true,
session_type: SessionType::Vpn,
}
}
pub fn new_mixnet() -> Self {
Self {
stats: true,
session_type: SessionType::Mixnet,
}
}
pub fn new_native() -> Self {
Self {
stats: true,
session_type: SessionType::Native,
}
}
pub fn new(stats: bool, session_type: SessionType) -> Self {
Self {
stats,
session_type,
}
}
pub fn new_none() -> Self {
Self {
stats: false,
session_type: SessionType::Unknown,
}
}
pub fn session_type(&self) -> SessionType {
self.session_type
}
pub fn stats(&self) -> bool {
self.stats
}
}
@@ -6,7 +6,6 @@ pub mod v2;
pub mod v3;
pub mod v4;
pub mod v5;
pub mod v6;
// aliases for backwards compatibility
pub use v1 as old_config_v1_1_13;
@@ -14,4 +13,3 @@ pub use v2 as old_config_v1_1_20;
pub use v3 as old_config_v1_1_20_2;
pub use v4 as old_config_v1_1_30;
pub use v5 as old_config_v1_1_33;
pub use v6 as old_config_v1_1_54;
+14 -12
View File
@@ -1,14 +1,16 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{
Acknowledgements, Client, Config, CoverTraffic, DebugConfig, GatewayConnection, ReplySurbs,
Topology, Traffic,
};
use nym_sphinx_addressing::Recipient;
use nym_sphinx_params::{PacketSize, PacketType};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use url::Url;
use super::v6::*;
// 'DEBUG'
const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5;
@@ -85,18 +87,18 @@ pub struct ConfigV5 {
pub debug: DebugConfigV5,
}
impl From<ConfigV5> for ConfigV6 {
impl From<ConfigV5> for Config {
fn from(value: ConfigV5) -> Self {
ConfigV6 {
client: ClientV6 {
Config {
client: Client {
version: value.client.version,
id: value.client.id,
disabled_credentials_mode: value.client.disabled_credentials_mode,
nyxd_urls: value.client.nyxd_urls,
nym_api_urls: value.client.nym_api_urls,
},
debug: DebugConfigV6 {
traffic: TrafficV6 {
debug: DebugConfig {
traffic: Traffic {
average_packet_delay: value.debug.traffic.average_packet_delay,
message_sending_average_delay: value
.debug
@@ -111,7 +113,7 @@ impl From<ConfigV5> for ConfigV6 {
packet_type: value.debug.traffic.packet_type,
..Default::default()
},
cover_traffic: CoverTrafficV6 {
cover_traffic: CoverTraffic {
loop_cover_traffic_average_delay: value
.debug
.cover_traffic
@@ -125,18 +127,18 @@ impl From<ConfigV5> for ConfigV6 {
.cover_traffic
.disable_loop_cover_traffic_stream,
},
gateway_connection: GatewayConnectionV6 {
gateway_connection: GatewayConnection {
gateway_response_timeout: value
.debug
.gateway_connection
.gateway_response_timeout,
},
acknowledgements: AcknowledgementsV6 {
acknowledgements: Acknowledgements {
average_ack_delay: value.debug.acknowledgements.average_ack_delay,
ack_wait_multiplier: value.debug.acknowledgements.ack_wait_multiplier,
ack_wait_addition: value.debug.acknowledgements.ack_wait_addition,
},
topology: TopologyV6 {
topology: Topology {
topology_refresh_rate: value.debug.topology.topology_refresh_rate,
topology_resolution_timeout: value.debug.topology.topology_resolution_timeout,
disable_refreshing: value.debug.topology.disable_refreshing,
@@ -146,7 +148,7 @@ impl From<ConfigV5> for ConfigV6 {
.max_startup_gateway_waiting_period,
..Default::default()
},
reply_surbs: ReplySurbsV6 {
reply_surbs: ReplySurbs {
minimum_reply_surb_storage_threshold: value
.debug
.reply_surbs
@@ -1,623 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{
Acknowledgements, Client, Config, CoverTraffic, DebugConfig, ForgetMe, GatewayConnection,
RememberMe, ReplySurbs, StatsReporting, Topology, Traffic,
};
use nym_config::serde_helpers::{de_maybe_stringified, ser_maybe_stringified};
use nym_sphinx_addressing::Recipient;
use nym_sphinx_params::{PacketSize, PacketType};
use nym_statistics_common::types::SessionType;
use serde::{Deserialize, Serialize};
use std::time::Duration;
use url::Url;
// 'DEBUG'
const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5;
const DEFAULT_ACK_WAIT_ADDITION: Duration = Duration::from_millis(1_500);
const DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(200);
const DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(20);
const DEFAULT_AVERAGE_PACKET_DELAY: Duration = Duration::from_millis(15);
const DEFAULT_TOPOLOGY_REFRESH_RATE: Duration = Duration::from_secs(5 * 60); // every 5min
const DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT: Duration = Duration::from_millis(5_000);
// the same values as our current (10.06.24) blacklist
const DEFAULT_MIN_MIXNODE_PERFORMANCE: u8 = 50;
const DEFAULT_MIN_GATEWAY_PERFORMANCE: u8 = 50;
const DEFAULT_MAX_STARTUP_GATEWAY_WAITING_PERIOD: Duration = Duration::from_secs(70 * 60); // 70min -> full epoch (1h) + a bit of overhead
// Set this to a high value for now, so that we don't risk sporadic timeouts that might cause
// bought bandwidth tokens to not have time to be spent; Once we remove the gateway from the
// bandwidth bridging protocol, we can come back to a smaller timeout value
const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: Duration = Duration::from_secs(5 * 60);
const DEFAULT_COVER_TRAFFIC_PRIMARY_SIZE_RATIO: f64 = 0.70;
// reply-surbs related:
// define when to request
// clients/client-core/src/client/replies/reply_storage/surb_storage.rs
const DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD: usize = 10;
const DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD: usize = 200;
const DEFAULT_MINIMUM_REPLY_SURB_THRESHOLD_BUFFER: usize = 0;
// define how much to request at once
// clients/client-core/src/client/replies/reply_controller.rs
const DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 10;
const DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 50;
const DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE: u32 = 500;
const DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD: Duration = Duration::from_secs(10);
const DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD: Duration = Duration::from_secs(5 * 60);
// 12 hours
const DEFAULT_MAXIMUM_REPLY_SURB_AGE: Duration = Duration::from_secs(12 * 60 * 60);
// 24 hours
const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(24 * 60 * 60);
// stats reporting related
/// Time interval between reporting statistics to the given provider if it exists
const STATS_REPORT_INTERVAL_SECS: Duration = Duration::from_secs(300);
// aliases for backwards compatibility
pub type ConfigV1_1_54 = ConfigV6;
pub type ClientV1_1_54 = ClientV6;
pub type DebugConfigV1_1_54 = DebugConfigV6;
pub type TrafficV1_1_54 = TrafficV6;
pub type CoverTrafficV1_1_54 = CoverTrafficV6;
pub type GatewayConnectionV1_1_54 = GatewayConnectionV6;
pub type AcknowledgementsV1_1_54 = AcknowledgementsV6;
pub type TopologyV1_1_54 = TopologyV6;
pub type ReplySurbsV1_1_54 = ReplySurbsV6;
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV6 {
pub client: ClientV6,
#[serde(default)]
pub debug: DebugConfigV6,
}
impl From<ConfigV6> for Config {
fn from(value: ConfigV6) -> Self {
Config {
client: Client {
version: value.client.version,
id: value.client.id,
disabled_credentials_mode: value.client.disabled_credentials_mode,
nyxd_urls: value.client.nyxd_urls,
nym_api_urls: value.client.nym_api_urls,
},
debug: DebugConfig {
traffic: Traffic {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
message_sending_average_delay: value
.debug
.traffic
.message_sending_average_delay,
disable_main_poisson_packet_distribution: value
.debug
.traffic
.disable_main_poisson_packet_distribution,
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,
deterministic_route_selection: value
.debug
.traffic
.deterministic_route_selection,
maximum_number_of_retransmissions: value
.debug
.traffic
.maximum_number_of_retransmissions,
use_legacy_sphinx_format: value.debug.traffic.use_legacy_sphinx_format,
disable_mix_hops: value.debug.traffic.disable_mix_hops,
},
cover_traffic: CoverTraffic {
loop_cover_traffic_average_delay: value
.debug
.cover_traffic
.loop_cover_traffic_average_delay,
cover_traffic_primary_size_ratio: value
.debug
.cover_traffic
.cover_traffic_primary_size_ratio,
disable_loop_cover_traffic_stream: value
.debug
.cover_traffic
.disable_loop_cover_traffic_stream,
},
gateway_connection: GatewayConnection {
gateway_response_timeout: value
.debug
.gateway_connection
.gateway_response_timeout,
},
acknowledgements: Acknowledgements {
average_ack_delay: value.debug.acknowledgements.average_ack_delay,
ack_wait_multiplier: value.debug.acknowledgements.ack_wait_multiplier,
ack_wait_addition: value.debug.acknowledgements.ack_wait_addition,
},
topology: Topology {
topology_refresh_rate: value.debug.topology.topology_refresh_rate,
topology_resolution_timeout: value.debug.topology.topology_resolution_timeout,
disable_refreshing: value.debug.topology.disable_refreshing,
max_startup_gateway_waiting_period: value
.debug
.topology
.max_startup_gateway_waiting_period,
minimum_mixnode_performance: value.debug.topology.minimum_mixnode_performance,
minimum_gateway_performance: value.debug.topology.minimum_gateway_performance,
use_extended_topology: value.debug.topology.use_extended_topology,
ignore_egress_epoch_role: value.debug.topology.ignore_egress_epoch_role,
ignore_ingress_epoch_role: value.debug.topology.ignore_ingress_epoch_role,
},
reply_surbs: ReplySurbs {
minimum_reply_surb_storage_threshold: value
.debug
.reply_surbs
.minimum_reply_surb_storage_threshold,
maximum_reply_surb_storage_threshold: value
.debug
.reply_surbs
.maximum_reply_surb_storage_threshold,
minimum_reply_surb_request_size: value
.debug
.reply_surbs
.minimum_reply_surb_request_size,
maximum_reply_surb_request_size: value
.debug
.reply_surbs
.maximum_reply_surb_request_size,
maximum_allowed_reply_surb_request_size: value
.debug
.reply_surbs
.maximum_allowed_reply_surb_request_size,
maximum_reply_surb_rerequest_waiting_period: value
.debug
.reply_surbs
.maximum_reply_surb_rerequest_waiting_period,
maximum_reply_surb_drop_waiting_period: value
.debug
.reply_surbs
.maximum_reply_surb_drop_waiting_period,
maximum_reply_surb_age: value.debug.reply_surbs.maximum_reply_surb_age,
maximum_reply_key_age: value.debug.reply_surbs.maximum_reply_key_age,
surb_mix_hops: value.debug.reply_surbs.surb_mix_hops,
minimum_reply_surb_threshold_buffer: value
.debug
.reply_surbs
.minimum_reply_surb_threshold_buffer,
fresh_sender_tags: value.debug.reply_surbs.fresh_sender_tags,
},
stats_reporting: StatsReporting {
enabled: value.debug.stats_reporting.enabled,
provider_address: value.debug.stats_reporting.provider_address,
reporting_interval: value.debug.stats_reporting.reporting_interval,
},
forget_me: ForgetMe {
client: value.debug.forget_me.client,
stats: value.debug.forget_me.stats,
},
remember_me: RememberMe {
stats: value.debug.remember_me.stats,
session_type: value.debug.remember_me.session_type.into(),
},
},
}
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
// note: the deny_unknown_fields is VITAL here to allow upgrades from v1.1.20_2
#[serde(deny_unknown_fields)]
pub struct ClientV6 {
/// Version of the client for which this configuration was created.
pub version: String,
/// ID specifies the human readable ID of this particular client.
pub id: String,
/// Indicates whether this client is running in a disabled credentials mode, thus attempting
/// to claim bandwidth without presenting bandwidth credentials.
// TODO: this should be moved to `debug.gateway_connection`
#[serde(default)]
pub disabled_credentials_mode: bool,
/// Addresses to nyxd validators via which the client can communicate with the chain.
#[serde(alias = "validator_urls")]
pub nyxd_urls: Vec<Url>,
/// Addresses to APIs running on validator from which the client gets the view of the network.
#[serde(alias = "validator_api_urls")]
pub nym_api_urls: Vec<Url>,
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct TrafficV6 {
/// The parameter of Poisson distribution determining how long, on average,
/// sent packet is going to be delayed at any given mix node.
/// So for a packet going through three mix nodes, on average, it will take three times this value
/// until the packet reaches its destination.
#[serde(with = "humantime_serde")]
pub average_packet_delay: Duration,
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take another 'real traffic stream' message to be sent.
/// If no real packets are available and cover traffic is enabled,
/// a loop cover message is sent instead in order to preserve the rate.
#[serde(with = "humantime_serde")]
pub message_sending_average_delay: Duration,
/// Controls whether the main packet stream constantly produces packets according to the predefined
/// 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 unless you understand the consequences of that change.
pub secondary_packet_size: Option<PacketSize>,
/// Specify whether any constructed sphinx packets should use the legacy format,
/// where the payload keys are explicitly attached rather than using the seeds
/// this affects any forward packets, acks and reply surbs
/// this flag should remain disabled until sufficient number of nodes on the network has upgraded
/// and support updated format.
/// in the case of reply surbs, the recipient must also understand the new encoding
pub use_legacy_sphinx_format: bool,
pub packet_type: PacketType,
/// Indicates whether to mix hops or not. If mix hops are enabled, traffic
/// will be routed as usual, to the entry gateway, through three mix nodes, egressing
/// through the exit gateway. If mix hops are disabled, traffic will be routed directly
/// from the entry gateway to the exit gateway, bypassing the mix nodes.
pub disable_mix_hops: bool,
}
impl Default for TrafficV6 {
fn default() -> Self {
TrafficV6 {
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,
// we should use the legacy format until sufficient number of nodes understand the
// improved encoding
use_legacy_sphinx_format: true,
disable_mix_hops: false,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct CoverTrafficV6 {
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take for another loop cover traffic message to be sent.
#[serde(with = "humantime_serde")]
pub loop_cover_traffic_average_delay: Duration,
/// Specifies the ratio of `primary_packet_size` to `secondary_packet_size` used in cover traffic.
/// Only applicable if `secondary_packet_size` is enabled.
pub cover_traffic_primary_size_ratio: f64,
/// Controls whether the dedicated loop cover traffic stream should be enabled.
/// (and sending packets, on average, every [Self::loop_cover_traffic_average_delay])
pub disable_loop_cover_traffic_stream: bool,
}
impl Default for CoverTrafficV6 {
fn default() -> Self {
CoverTrafficV6 {
loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
cover_traffic_primary_size_ratio: DEFAULT_COVER_TRAFFIC_PRIMARY_SIZE_RATIO,
disable_loop_cover_traffic_stream: false,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct GatewayConnectionV6 {
/// How long we're willing to wait for a response to a message sent to the gateway,
/// before giving up on it.
#[serde(with = "humantime_serde")]
pub gateway_response_timeout: Duration,
}
impl Default for GatewayConnectionV6 {
fn default() -> Self {
GatewayConnectionV6 {
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct AcknowledgementsV6 {
/// The parameter of Poisson distribution determining how long, on average,
/// sent acknowledgement is going to be delayed at any given mix node.
/// So for an ack going through three mix nodes, on average, it will take three times this value
/// until the packet reaches its destination.
#[serde(with = "humantime_serde")]
pub average_ack_delay: Duration,
/// Value multiplied with the expected round trip time of an acknowledgement packet before
/// it is assumed it was lost and retransmission of the data packet happens.
/// In an ideal network with 0 latency, this value would have been 1.
pub ack_wait_multiplier: f64,
/// Value added to the expected round trip time of an acknowledgement packet before
/// it is assumed it was lost and retransmission of the data packet happens.
/// In an ideal network with 0 latency, this value would have been 0.
#[serde(with = "humantime_serde")]
pub ack_wait_addition: Duration,
}
impl Default for AcknowledgementsV6 {
fn default() -> Self {
AcknowledgementsV6 {
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct TopologyV6 {
/// The uniform delay every which clients are querying the directory server
/// to try to obtain a compatible network topology to send sphinx packets through.
#[serde(with = "humantime_serde")]
pub topology_refresh_rate: Duration,
/// During topology refresh, test packets are sent through every single possible network
/// path. This timeout determines waiting period until it is decided that the packet
/// did not reach its destination.
#[serde(with = "humantime_serde")]
pub topology_resolution_timeout: Duration,
/// Specifies whether the client should not refresh the network topology after obtaining
/// the first valid instance.
/// Supersedes `topology_refresh_rate_ms`.
pub disable_refreshing: bool,
/// Defines how long the client is going to wait on startup for its gateway to come online,
/// before abandoning the procedure.
#[serde(with = "humantime_serde")]
pub max_startup_gateway_waiting_period: Duration,
/// Specifies a minimum performance of a mixnode that is used on route construction.
/// This setting is only applicable when `NymApi` topology is used.
pub minimum_mixnode_performance: u8,
/// Specifies a minimum performance of a gateway that is used on route construction.
/// This setting is only applicable when `NymApi` topology is used.
pub minimum_gateway_performance: u8,
/// Specifies whether this client should attempt to retrieve all available network nodes
/// as opposed to just active mixnodes/gateways.
pub use_extended_topology: bool,
/// Specifies whether this client should ignore the current epoch role of the target egress node
/// when constructing the final hop packets.
pub ignore_egress_epoch_role: bool,
/// Specifies whether this client should ignore the current epoch role of the ingress node
/// when attempting to establish new connection
pub ignore_ingress_epoch_role: bool,
}
impl Default for TopologyV6 {
fn default() -> Self {
TopologyV6 {
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
disable_refreshing: false,
max_startup_gateway_waiting_period: DEFAULT_MAX_STARTUP_GATEWAY_WAITING_PERIOD,
minimum_mixnode_performance: DEFAULT_MIN_MIXNODE_PERFORMANCE,
minimum_gateway_performance: DEFAULT_MIN_GATEWAY_PERFORMANCE,
use_extended_topology: false,
ignore_egress_epoch_role: true,
ignore_ingress_epoch_role: true,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct ReplySurbsV6 {
/// Defines the minimum number of reply surbs the client wants to keep in its storage at all times.
/// It can only allow to go below that value if its to request additional reply surbs.
pub minimum_reply_surb_storage_threshold: usize,
/// Defines the maximum number of reply surbs the client wants to keep in its storage at any times.
pub maximum_reply_surb_storage_threshold: usize,
/// Defines the soft threshold ontop of the minimum reply surb storage threshold for when the client
/// should proactively request additional reply surbs.
pub minimum_reply_surb_threshold_buffer: usize,
/// Defines the minimum number of reply surbs the client would request.
pub minimum_reply_surb_request_size: u32,
/// Defines the maximum number of reply surbs the client would request.
pub maximum_reply_surb_request_size: u32,
/// Defines the maximum number of reply surbs a remote party is allowed to request from this client at once.
pub maximum_allowed_reply_surb_request_size: u32,
/// Defines maximum amount of time the client is going to wait for reply surbs before explicitly asking
/// for more even though in theory they wouldn't need to.
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_rerequest_waiting_period: Duration,
/// Defines maximum amount of time the client is going to wait for reply surbs before
/// deciding it's never going to get them and would drop all pending messages
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_drop_waiting_period: Duration,
/// Defines maximum amount of time given reply surb is going to be valid for.
/// This is going to be superseded by key rotation once implemented.
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_age: Duration,
/// Defines maximum amount of time given reply key is going to be valid for.
/// This is going to be superseded by key rotation once implemented.
#[serde(with = "humantime_serde")]
pub maximum_reply_key_age: Duration,
/// Specifies the number of mixnet hops the packet should go through. If not specified, then
/// the default value is used.
pub surb_mix_hops: Option<u8>,
/// Specifies if we should reset all the sender tags on startup
pub fresh_sender_tags: bool,
}
impl Default for ReplySurbsV6 {
fn default() -> Self {
ReplySurbsV6 {
minimum_reply_surb_storage_threshold: DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
maximum_reply_surb_storage_threshold: DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD,
minimum_reply_surb_threshold_buffer: DEFAULT_MINIMUM_REPLY_SURB_THRESHOLD_BUFFER,
minimum_reply_surb_request_size: DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE,
maximum_reply_surb_request_size: DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE,
maximum_allowed_reply_surb_request_size: DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE,
maximum_reply_surb_rerequest_waiting_period:
DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD,
maximum_reply_surb_drop_waiting_period: DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD,
maximum_reply_surb_age: DEFAULT_MAXIMUM_REPLY_SURB_AGE,
maximum_reply_key_age: DEFAULT_MAXIMUM_REPLY_KEY_AGE,
surb_mix_hops: None,
fresh_sender_tags: false,
}
}
}
#[derive(Debug, Default, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct DebugConfigV6 {
/// Defines all configuration options related to traffic streams.
pub traffic: TrafficV6,
/// Defines all configuration options related to cover traffic stream(s).
pub cover_traffic: CoverTrafficV6,
/// Defines all configuration options related to the gateway connection.
pub gateway_connection: GatewayConnectionV6,
/// Defines all configuration options related to acknowledgements, such as delays or wait timeouts.
pub acknowledgements: AcknowledgementsV6,
/// Defines all configuration options related topology, such as refresh rates or timeouts.
pub topology: TopologyV6,
/// Defines all configuration options related to reply SURBs.
pub reply_surbs: ReplySurbsV6,
/// Defines all configuration options related to stats reporting.
pub stats_reporting: StatsReportingV6,
/// Defines all configuration options related to the forget me flag.
pub forget_me: ForgetMeV6,
/// Defines all configuration options related to the remember me flag.
pub remember_me: RememberMeV6,
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct StatsReportingV6 {
/// Is stats reporting enabled
pub enabled: bool,
/// Address of the stats collector. If this is none, no reporting will happen, regardless of `enabled`
#[serde(
serialize_with = "ser_maybe_stringified",
deserialize_with = "de_maybe_stringified"
)]
pub provider_address: Option<Recipient>,
/// With what frequence will statistics be sent
#[serde(with = "humantime_serde")]
pub reporting_interval: Duration,
}
impl Default for StatsReportingV6 {
fn default() -> Self {
StatsReportingV6 {
enabled: true,
provider_address: None,
reporting_interval: STATS_REPORT_INTERVAL_SECS,
}
}
}
#[derive(Clone, Default, Debug, Deserialize, PartialEq, Serialize, Copy)]
pub struct ForgetMeV6 {
client: bool,
stats: bool,
}
#[derive(Clone, Default, Debug, Deserialize, PartialEq, Serialize, Copy)]
pub struct RememberMeV6 {
/// Signal that this client should be accounted for in the stats
stats: bool,
/// Type of the session to remember, if it should be remembered
session_type: SessionTypeV6,
}
#[derive(PartialEq, Copy, Clone, Serialize, Deserialize, Default, Debug)]
pub enum SessionTypeV6 {
Vpn,
Mixnet,
Wasm,
Native,
Socks5,
#[default]
Unknown,
}
impl From<SessionTypeV6> for SessionType {
fn from(value: SessionTypeV6) -> Self {
match value {
SessionTypeV6::Vpn => Self::Vpn,
SessionTypeV6::Mixnet => Self::Mixnet,
SessionTypeV6::Wasm => Self::Wasm,
SessionTypeV6::Native => Self::Native,
SessionTypeV6::Socks5 => Self::Socks5,
SessionTypeV6::Unknown => Self::Unknown,
}
}
}
@@ -2,7 +2,8 @@
// SPDX-License-Identifier: Apache-2.0
use crate::BadGateway;
use std::{io, path::PathBuf};
use std::io;
use std::path::PathBuf;
use thiserror::Error;
#[derive(Debug, Error)]
@@ -18,6 +19,7 @@ pub enum StorageError {
#[error("failed to perform sqlx migration: {source}")]
MigrationError {
#[source]
#[from]
source: sqlx::migrate::MigrateError,
},
@@ -30,6 +32,7 @@ pub enum StorageError {
#[error("failed to run the SQL query: {source}")]
QueryError {
#[source]
#[from]
source: sqlx::error::Error,
},
@@ -36,7 +36,7 @@ use crate::{config, spawn_future};
use futures::channel::mpsc;
use log::*;
use nym_bandwidth_controller::BandwidthController;
use nym_client_core_config_types::{ForgetMe, RememberMe};
use nym_client_core_config_types::ForgetMe;
use nym_client_core_gateways_storage::{GatewayDetails, GatewaysDetailsStore};
use nym_credential_storage::storage::Storage as CredentialStorage;
use nym_crypto::asymmetric::{ed25519, x25519};
@@ -238,12 +238,6 @@ where
self
}
#[must_use]
pub fn with_remember_me(mut self, remember_me: &RememberMe) -> Self {
self.config.debug.remember_me = *remember_me;
self
}
#[must_use]
pub fn with_gateway_setup(mut self, setup: GatewaySetup) -> Self {
self.setup_method = setup;
@@ -456,7 +450,7 @@ where
log::error!("Could not authenticate and start up the gateway connection - {err}");
ClientCoreError::GatewayClientError {
gateway_id: details.gateway_id.to_base58_string(),
source: Box::new(err),
source: err,
}
};
@@ -936,7 +930,6 @@ where
task_handle: shutdown,
client_request_sender,
forget_me: self.config.debug.forget_me,
remember_me: self.config.debug.remember_me,
})
}
}
@@ -951,5 +944,4 @@ pub struct BaseClient {
pub client_request_sender: ClientRequestSender,
pub task_handle: TaskHandle,
pub forget_me: ForgetMe,
pub remember_me: RememberMe,
}
@@ -1,18 +1,20 @@
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{
client::replies::reply_storage::{fs_backend, CombinedReplyStorage, ReplyStorageBackend},
config,
config::Config,
error::ClientCoreError,
use crate::client::replies::reply_storage::{
fs_backend, CombinedReplyStorage, ReplyStorageBackend,
};
use crate::config;
use crate::config::Config;
use crate::error::ClientCoreError;
use log::{error, info, trace};
use nym_bandwidth_controller::BandwidthController;
use nym_client_core_gateways_storage::OnDiskGatewaysDetails;
use nym_credential_storage::storage::Storage as CredentialStorage;
use nym_validator_client::{nyxd, QueryHttpRpcNyxdClient};
use std::{io, path::Path};
use nym_validator_client::nyxd;
use nym_validator_client::QueryHttpRpcNyxdClient;
use std::path::Path;
use std::{fs, io};
use time::OffsetDateTime;
use url::Url;
@@ -20,11 +22,11 @@ async fn setup_fresh_backend<P: AsRef<Path>>(
db_path: P,
surb_config: &config::ReplySurbs,
) -> Result<fs_backend::Backend, ClientCoreError> {
info!("Creating fresh surb database");
info!("creating fresh surb database");
let mut storage_backend = match fs_backend::Backend::init(db_path).await {
Ok(backend) => backend,
Err(err) => {
error!("setup_fresh_backend: Failed to setup persistent storage backend for our reply needs: {err}");
error!("failed to setup persistent storage backend for our reply needs: {err}");
return Err(ClientCoreError::SurbStorageError {
source: Box::new(err),
});
@@ -38,15 +40,14 @@ async fn setup_fresh_backend<P: AsRef<Path>>(
surb_config.minimum_reply_surb_storage_threshold,
surb_config.maximum_reply_surb_storage_threshold,
);
match storage_backend.init_fresh(&mem_store).await {
Ok(()) => Ok(storage_backend),
Err(err) => {
storage_backend.shutdown().await;
Err(ClientCoreError::SurbStorageError {
source: Box::new(err),
})
}
}
storage_backend
.init_fresh(&mem_store)
.await
.map_err(|err| ClientCoreError::SurbStorageError {
source: Box::new(err),
})?;
Ok(storage_backend)
}
// fn setup_inactive_backend(surb_config: &config::ReplySurbs) -> fs_backend::Backend {
@@ -57,11 +58,12 @@ async fn setup_fresh_backend<P: AsRef<Path>>(
// )
// }
async fn archive_corrupted_database<P: AsRef<Path>>(db_path: P) -> io::Result<()> {
fn archive_corrupted_database<P: AsRef<Path>>(db_path: P) -> io::Result<()> {
let db_path = db_path.as_ref();
debug_assert!(db_path.exists());
let now = OffsetDateTime::now_utc().unix_timestamp();
let suffix = format!("_{now}.corrupted");
let new_extension =
@@ -70,15 +72,11 @@ async fn archive_corrupted_database<P: AsRef<Path>>(db_path: P) -> io::Result<()
} else {
suffix
};
let renamed = db_path.with_extension(new_extension);
tokio::fs::rename(db_path, &renamed).await.inspect_err(|_| {
error!(
"Failed to rename corrupt database file: {} to {}",
db_path.display(),
renamed.display()
);
})
let mut renamed = db_path.to_owned();
renamed.set_extension(new_extension);
fs::rename(db_path, renamed)
}
pub async fn setup_fs_reply_surb_backend<P: AsRef<Path>>(
@@ -89,12 +87,13 @@ pub async fn setup_fs_reply_surb_backend<P: AsRef<Path>>(
// the existing one
let db_path = db_path.as_ref();
if db_path.exists() {
info!("Loading existing surb database");
info!("loading existing surb database");
match fs_backend::Backend::try_load(db_path, surb_config.fresh_sender_tags).await {
Ok(backend) => Ok(backend),
Err(err) => {
error!("setup_fs_reply_surb_backend: Failed to setup persistent storage backend for our reply needs: {err}. We're going to create a fresh database instead. This behaviour might change in the future");
archive_corrupted_database(db_path).await?;
error!("failed to setup persistent storage backend for our reply needs: {err}. We're going to create a fresh database instead. This behaviour might change in the future");
archive_corrupted_database(db_path)?;
setup_fresh_backend(db_path, surb_config).await
}
}
@@ -157,12 +157,6 @@ impl TopologyRefresher {
let mut interval =
gloo_timers::future::IntervalStream::new(self.refresh_rate.as_millis() as u32);
// We already have an initial topology, so no need to refresh it immediately.
// My understanding is that js setInterval does not fire immediately, so it's not
// needed there.
#[cfg(not(target_arch = "wasm32"))]
interval.next().await;
while !self.task_client.is_shutdown() {
tokio::select! {
_ = interval.next() => {
@@ -70,10 +70,6 @@ impl NymApiTopologyProvider {
}
}
pub fn disable_bincode(&mut self) {
self.validator_client.use_bincode = false;
}
fn use_next_nym_api(&mut self) {
if self.nym_api_urls.len() == 1 {
warn!("There's only a single nym API available - it won't be possible to use a different one");
@@ -86,13 +82,20 @@ impl NymApiTopologyProvider {
}
async fn get_current_compatible_topology(&mut self) -> Option<NymTopology> {
let rewarded_set_fut = self.validator_client.get_current_rewarded_set();
let rewarded_set = self
.validator_client
.get_current_rewarded_set()
.await
.inspect_err(|err| error!("failed to get current rewarded set: {err}"))
.ok()?;
let topology = if self.config.use_extended_topology {
let all_nodes_fut = self.validator_client.get_all_basic_nodes();
let mut topology = NymTopology::new_empty(rewarded_set);
// Join rewarded_set_fut and all_nodes_fut concurrently
let (rewarded_set, all_nodes) = futures::try_join!(rewarded_set_fut, all_nodes_fut)
if self.config.use_extended_topology {
let all_nodes = self
.validator_client
.get_all_basic_nodes()
.await
.inspect_err(|err| error!("failed to get network nodes: {err}"))
.ok()?;
@@ -100,28 +103,26 @@ impl NymApiTopologyProvider {
"there are {} nodes on the network (before filtering)",
all_nodes.len()
);
let mut topology = NymTopology::new_empty(rewarded_set);
topology.add_additional_nodes(all_nodes.iter().filter(|n| {
n.performance.round_to_integer() >= self.config.min_node_performance()
}));
topology
} else {
// if we're not using extended topology, we're only getting active set mixnodes and gateways
let mixnodes_fut = self
let mixnodes = self
.validator_client
.get_all_basic_active_mixing_assigned_nodes();
.get_all_basic_active_mixing_assigned_nodes()
.await
.inspect_err(|err| error!("failed to get network mixnodes: {err}"))
.ok()?;
// TODO: we really should be getting ACTIVE gateways only
let gateways_fut = self.validator_client.get_all_basic_entry_assigned_nodes();
let (rewarded_set, mixnodes, gateways) =
futures::try_join!(rewarded_set_fut, mixnodes_fut, gateways_fut)
.inspect_err(|err| {
error!("failed to get network nodes: {err}");
})
.ok()?;
let gateways = self
.validator_client
.get_all_basic_entry_assigned_nodes()
.await
.inspect_err(|err| error!("failed to get network gateways: {err}"))
.ok()?;
debug!(
"there are {} mixnodes and {} gateways in total (before performance filtering)",
@@ -129,15 +130,12 @@ impl NymApiTopologyProvider {
gateways.len()
);
let mut topology = NymTopology::new_empty(rewarded_set);
topology.add_additional_nodes(mixnodes.iter().filter(|m| {
m.performance.round_to_integer() >= self.config.min_mixnode_performance
}));
topology.add_additional_nodes(gateways.iter().filter(|m| {
m.performance.round_to_integer() >= self.config.min_gateway_performance
}));
topology
};
if !topology.is_minimally_routable() {
+1 -1
View File
@@ -4,6 +4,6 @@
pub use nym_client_core_config_types::disk_persistence;
pub use nym_client_core_config_types::old::{
old_config_v1_1_13, old_config_v1_1_20, old_config_v1_1_20_2, old_config_v1_1_30,
old_config_v1_1_33, old_config_v1_1_54,
old_config_v1_1_33,
};
pub use nym_client_core_config_types::*;
+5 -10
View File
@@ -18,7 +18,7 @@ pub enum ClientCoreError {
#[error("gateway client error ({gateway_id}): {source}")]
GatewayClientError {
gateway_id: String,
source: Box<GatewayClientError>,
source: GatewayClientError,
},
#[error("custom gateway client error: {source}")]
@@ -88,7 +88,10 @@ pub enum ClientCoreError {
},
#[error("failed to establish connection to gateway: {source}")]
GatewayConnectionFailure { source: Box<tungstenite::Error> },
GatewayConnectionFailure {
#[from]
source: tungstenite::Error,
},
#[cfg(target_arch = "wasm32")]
#[error("failed to establish gateway connection (wasm)")]
@@ -224,14 +227,6 @@ pub enum ClientCoreError {
HkdfDerivationError {},
}
impl From<tungstenite::Error> for ClientCoreError {
fn from(err: tungstenite::Error) -> ClientCoreError {
ClientCoreError::GatewayConnectionFailure {
source: Box::new(err),
}
}
}
/// Set of messages that the client can send to listeners via the task manager
#[derive(Debug)]
pub enum ClientCoreStatusMessage {
+2 -2
View File
@@ -329,7 +329,7 @@ pub(super) async fn register_with_gateway(
log::warn!("Failed to establish connection with gateway!");
ClientCoreError::GatewayClientError {
gateway_id: gateway_id.to_base58_string(),
source: Box::new(err),
source: err,
}
})?;
let auth_response = gateway_client
@@ -339,7 +339,7 @@ pub(super) async fn register_with_gateway(
log::warn!("Failed to register with the gateway {gateway_id}: {err}");
ClientCoreError::GatewayClientError {
gateway_id: gateway_id.to_base58_string(),
source: Box::new(err),
source: err,
}
})?;
+1 -1
View File
@@ -232,7 +232,7 @@ where
} => {
log::debug!("GatewaySetup::ReuseConnection");
Ok(reuse_gateway_connection(
*authenticated_ephemeral_client,
authenticated_ephemeral_client,
*gateway_details,
managed_keys,
))
+2 -2
View File
@@ -218,7 +218,7 @@ pub enum GatewaySetup {
ReuseConnection {
/// The authenticated ephemeral client that was created during `init`
authenticated_ephemeral_client: Box<InitGatewayClient>,
authenticated_ephemeral_client: InitGatewayClient,
// Details of this pre-initialised client (i.e. gateway and keys)
gateway_details: Box<GatewayRegistration>,
@@ -261,7 +261,7 @@ impl GatewaySetup {
pub fn try_reuse_connection(init_res: InitialisationResult) -> Result<Self, ClientCoreError> {
if let Some(authenticated_ephemeral_client) = init_res.authenticated_ephemeral_client {
Ok(GatewaySetup::ReuseConnection {
authenticated_ephemeral_client: Box::new(authenticated_ephemeral_client),
authenticated_ephemeral_client,
gateway_details: Box::new(init_res.gateway_registration),
client_keys: init_res.client_keys,
})
+1 -12
View File
@@ -17,26 +17,15 @@ nym-crypto = { path = "../../crypto", optional = true, default-features = false
nym-sphinx = { path = "../../nymsphinx" }
nym-task = { path = "../../task" }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio]
workspace = true
features = ["fs"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
workspace = true
features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]
optional = true
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx-pool-guard]
path = "../../../sqlx-pool-guard"
[build-dependencies]
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
sqlx = { workspace = true, features = [
"runtime-tokio-rustls",
"sqlite",
"macros",
"migrate",
] }
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
[features]
fs-surb-storage = ["sqlx", "nym-crypto", "nym-crypto/hashing"]
@@ -1,7 +1,8 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::{io, path::PathBuf};
use std::io;
use std::path::PathBuf;
use thiserror::Error;
#[derive(Debug, Error)]
@@ -29,6 +30,7 @@ pub enum StorageError {
#[error("failed to perform sqlx migration: {source}")]
MigrationError {
#[source]
#[from]
source: sqlx::migrate::MigrateError,
},
@@ -41,6 +43,7 @@ pub enum StorageError {
#[error("failed to run the SQL query: {source}")]
QueryError {
#[source]
#[from]
source: sqlx::error::Error,
},
@@ -15,11 +15,9 @@ use sqlx::{
};
use std::path::Path;
use sqlx_pool_guard::SqlitePoolGuard;
#[derive(Debug, Clone)]
pub struct StorageManager {
connection_pool: SqlitePoolGuard,
pub connection_pool: sqlx::SqlitePool,
}
// all SQL goes here
@@ -39,7 +37,7 @@ impl StorageManager {
.journal_mode(sqlx::sqlite::SqliteJournalMode::Wal)
.synchronous(SqliteSynchronous::Normal)
.auto_vacuum(SqliteAutoVacuum::Incremental)
.filename(&database_path)
.filename(database_path)
.create_if_missing(fresh)
.disable_statement_logging();
@@ -51,15 +49,11 @@ impl StorageManager {
}
};
let connection_pool =
SqlitePoolGuard::new(database_path.as_ref().to_path_buf(), connection_pool);
if let Err(err) = sqlx::migrate!("./fs_surbs_migrations")
.run(&*connection_pool)
.run(&connection_pool)
.await
{
error!("Failed to initialize SQLx database: {err}");
connection_pool.close().await;
return Err(err.into());
}
@@ -67,43 +61,38 @@ impl StorageManager {
Ok(StorageManager { connection_pool })
}
/// Close connection pool waiting for all connections to be closed.
pub async fn close_pool(&self) {
self.connection_pool.close().await;
}
#[allow(dead_code)]
pub async fn status_table_exists(&self) -> Result<bool, sqlx::Error> {
sqlx::query!("SELECT name FROM sqlite_master WHERE type='table' AND name='status'")
.fetch_optional(&*self.connection_pool)
.fetch_optional(&self.connection_pool)
.await
.map(|r| r.is_some())
}
pub async fn create_status_table(&self) -> Result<(), sqlx::Error> {
sqlx::query!("INSERT INTO status(flush_in_progress, previous_flush_timestamp, client_in_use) VALUES (0, 0, 1)")
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?;
Ok(())
}
pub async fn get_flush_status(&self) -> Result<bool, sqlx::Error> {
sqlx::query!("SELECT flush_in_progress FROM status;")
.fetch_one(&*self.connection_pool)
.fetch_one(&self.connection_pool)
.await
.map(|r| r.flush_in_progress > 0)
}
pub async fn set_previous_flush_timestamp(&self, timestamp: i64) -> Result<(), sqlx::Error> {
sqlx::query!("UPDATE status SET previous_flush_timestamp = ?", timestamp)
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?;
Ok(())
}
pub async fn get_previous_flush_timestamp(&self) -> Result<i64, sqlx::Error> {
sqlx::query!("SELECT previous_flush_timestamp FROM status;")
.fetch_one(&*self.connection_pool)
.fetch_one(&self.connection_pool)
.await
.map(|r| r.previous_flush_timestamp)
}
@@ -111,14 +100,14 @@ impl StorageManager {
pub async fn set_flush_status(&self, in_progress: bool) -> Result<(), sqlx::Error> {
let in_progress_int = i64::from(in_progress);
sqlx::query!("UPDATE status SET flush_in_progress = ?", in_progress_int)
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?;
Ok(())
}
pub async fn get_client_in_use_status(&self) -> Result<bool, sqlx::Error> {
sqlx::query!("SELECT client_in_use FROM status;")
.fetch_one(&*self.connection_pool)
.fetch_one(&self.connection_pool)
.await
.map(|r| r.client_in_use > 0)
}
@@ -126,21 +115,21 @@ impl StorageManager {
pub async fn set_client_in_use_status(&self, in_use: bool) -> Result<(), sqlx::Error> {
let in_use_int = i64::from(in_use);
sqlx::query!("UPDATE status SET client_in_use = ?", in_use_int)
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?;
Ok(())
}
pub async fn delete_all_tags(&self) -> Result<(), sqlx::Error> {
sqlx::query!("DELETE FROM sender_tag;")
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?;
Ok(())
}
pub async fn get_tags(&self) -> Result<Vec<StoredSenderTag>, sqlx::Error> {
sqlx::query_as!(StoredSenderTag, "SELECT * FROM sender_tag;",)
.fetch_all(&*self.connection_pool)
.fetch_all(&self.connection_pool)
.await
}
@@ -152,21 +141,21 @@ impl StorageManager {
stored_tag.recipient,
stored_tag.tag
)
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?;
Ok(())
}
pub async fn delete_all_reply_keys(&self) -> Result<(), sqlx::Error> {
sqlx::query!("DELETE FROM reply_key;")
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?;
Ok(())
}
pub async fn get_reply_keys(&self) -> Result<Vec<StoredReplyKey>, sqlx::Error> {
sqlx::query_as!(StoredReplyKey, "SELECT * FROM reply_key;",)
.fetch_all(&*self.connection_pool)
.fetch_all(&self.connection_pool)
.await
}
@@ -182,14 +171,14 @@ impl StorageManager {
stored_reply_key.reply_key,
stored_reply_key.sent_at_timestamp
)
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?;
Ok(())
}
pub async fn get_surb_senders(&self) -> Result<Vec<StoredSurbSender>, sqlx::Error> {
sqlx::query_as!(StoredSurbSender, "SELECT * FROM reply_surb_sender;",)
.fetch_all(&*self.connection_pool)
.fetch_all(&self.connection_pool)
.await
}
@@ -204,7 +193,7 @@ impl StorageManager {
stored_surb_sender.tag,
stored_surb_sender.last_sent_timestamp
)
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?
.last_insert_rowid();
Ok(id)
@@ -219,17 +208,17 @@ impl StorageManager {
"SELECT * FROM reply_surb WHERE reply_surb_sender_id = ?",
sender_id
)
.fetch_all(&*self.connection_pool)
.fetch_all(&self.connection_pool)
.await
}
pub async fn delete_all_reply_surb_data(&self) -> Result<(), sqlx::Error> {
sqlx::query!("DELETE FROM reply_surb;")
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?;
sqlx::query!("DELETE FROM reply_surb_sender;")
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?;
Ok(())
@@ -246,7 +235,7 @@ impl StorageManager {
stored_reply_surb.reply_surb_sender_id,
stored_reply_surb.reply_surb
)
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?;
Ok(())
}
@@ -260,7 +249,7 @@ impl StorageManager {
SELECT min_reply_surb_threshold as "min_reply_surb_threshold: u32", max_reply_surb_threshold as "max_reply_surb_threshold: u32" FROM reply_surb_storage_metadata;
"#,
)
.fetch_one(&*self.connection_pool)
.fetch_one(&self.connection_pool)
.await
}
@@ -274,7 +263,7 @@ impl StorageManager {
"#,
metadata.min_reply_surb_threshold,
metadata.max_reply_surb_threshold,
).execute(&*self.connection_pool).await?;
).execute(&self.connection_pool).await?;
Ok(())
}
}
@@ -1,21 +1,18 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::backend::fs_backend::manager::StorageManager;
use crate::backend::fs_backend::models::{
ReplySurbStorageMetadata, StoredReplyKey, StoredReplySurb, StoredSenderTag, StoredSurbSender,
};
use crate::surb_storage::ReceivedReplySurbs;
use crate::{
backend::fs_backend::{
manager::StorageManager,
models::{
ReplySurbStorageMetadata, StoredReplyKey, StoredReplySurb, StoredSenderTag,
StoredSurbSender,
},
},
surb_storage::ReceivedReplySurbs,
CombinedReplyStorage, ReceivedReplySurbsMap, ReplyStorageBackend, SentReplyKeys,
UsedSenderTags,
CombinedReplyStorage, ReceivedReplySurbsMap, ReplyStorageBackend, SentReplyKeys, UsedSenderTags,
};
use async_trait::async_trait;
use log::{debug, error, info, warn};
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
use std::fs;
use std::path::{Path, PathBuf};
use time::OffsetDateTime;
@@ -44,17 +41,15 @@ impl Backend {
}
let manager = StorageManager::init(database_path, true).await?;
match manager.create_status_table().await {
Ok(()) => Ok(Backend {
temporary_old_path: None,
database_path: owned_path,
manager,
}),
Err(err) => {
manager.close_pool().await;
Err(err.into())
}
}
manager.create_status_table().await?;
let backend = Backend {
temporary_old_path: None,
database_path: owned_path,
manager,
};
Ok(backend)
}
pub async fn try_load<P: AsRef<Path>>(
@@ -69,28 +64,7 @@ impl Backend {
}
let manager = StorageManager::init(database_path, false).await?;
match Self::try_load_inner(&manager, fresh_sender_tags).await {
Ok(()) => Ok(Backend {
temporary_old_path: None,
database_path: owned_path,
manager,
}),
Err(e) => {
manager.close_pool().await;
Err(e)
}
}
}
/// Gracefully close sqlite connection pool and drop backend.
pub async fn shutdown(self) {
self.manager.close_pool().await
}
async fn try_load_inner(
manager: &StorageManager,
fresh_sender_tags: bool,
) -> Result<(), StorageError> {
// the database flush wasn't fully finished and thus the data is in inconsistent state
// (we don't really know what's properly saved or what's not)
if manager.get_flush_status().await? {
@@ -152,11 +126,20 @@ impl Backend {
manager.delete_all_tags().await?;
}
Ok(())
Ok(Backend {
temporary_old_path: None,
database_path: owned_path,
// manager: StorageManagerState::Storage(manager),
manager,
})
}
async fn close_pool(&mut self) {
self.manager.connection_pool.close().await;
}
async fn rotate(&mut self) -> Result<(), StorageError> {
self.manager.close_pool().await;
self.close_pool().await;
let new_extension = if let Some(existing_extension) =
self.database_path.extension().and_then(|ext| ext.to_str())
@@ -169,8 +152,7 @@ impl Backend {
let mut temp_old = self.database_path.clone();
temp_old.set_extension(new_extension);
tokio::fs::rename(&self.database_path, &temp_old)
.await
fs::rename(&self.database_path, &temp_old)
.map_err(|err| StorageError::DatabaseRenameError { source: err })?;
self.manager = StorageManager::init(&self.database_path, true).await?;
self.manager.create_status_table().await?;
@@ -179,10 +161,9 @@ impl Backend {
Ok(())
}
async fn remove_old(&mut self) -> Result<(), StorageError> {
fn remove_old(&mut self) -> Result<(), StorageError> {
if let Some(old_path) = self.temporary_old_path.take() {
tokio::fs::remove_file(old_path)
.await
fs::remove_file(old_path)
.map_err(|err| StorageError::DatabaseOldFileRemoveError { source: err })
} else {
warn!("the old database file doesn't seem to exist!");
@@ -354,7 +335,7 @@ impl ReplyStorageBackend for Backend {
self.dump_reply_surb_storage_metadata(surbs_ref).await?;
self.dump_reply_surbs(surbs_ref).await?;
self.remove_old().await?;
self.remove_old()?;
self.end_storage_flush().await
}
@@ -56,7 +56,7 @@ pub(crate) async fn connect_async(
}
.map_err(|err| GatewayClientError::NetworkConnectionFailed {
address: endpoint.to_owned(),
source: Box::new(tungstenite::Error::from(err)),
source: err.into(),
})?;
#[cfg(unix)]
@@ -72,7 +72,7 @@ pub(crate) async fn connect_async(
Err(err) => {
stream = Err(GatewayClientError::NetworkConnectionFailed {
address: endpoint.to_owned(),
source: Box::new(tungstenite::Error::from(err)),
source: err.into(),
});
continue;
}
@@ -83,6 +83,6 @@ pub(crate) async fn connect_async(
.await
.map_err(|error| GatewayClientError::NetworkConnectionFailed {
address: endpoint.to_owned(),
source: Box::new(error),
source: error,
})
}
+3 -12
View File
@@ -25,7 +25,7 @@ pub enum GatewayClientError {
RequestError(#[from] GatewayRequestsError),
#[error("There was a network error: {0}")]
NetworkError(Box<WsError>),
NetworkError(#[from] WsError),
#[error("failed to upgrade our shared key - the gateway sent malformed response")]
FatalKeyUpgradeFailure,
@@ -41,10 +41,7 @@ pub enum GatewayClientError {
NetworkErrorWasm(#[from] JsError),
#[error("connection failed: {address}: {source}")]
NetworkConnectionFailed {
address: String,
source: Box<WsError>,
},
NetworkConnectionFailed { address: String, source: WsError },
#[error("no socket address for endpoint: {address}")]
NoEndpointForConnection { address: String },
@@ -130,16 +127,10 @@ pub enum GatewayClientError {
ShutdownInProgress,
}
impl From<WsError> for GatewayClientError {
fn from(error: WsError) -> Self {
GatewayClientError::NetworkError(Box::new(error))
}
}
impl GatewayClientError {
pub fn is_closed_connection(&self) -> bool {
match self {
GatewayClientError::NetworkError(ws_err) => match ws_err.as_ref() {
GatewayClientError::NetworkError(ws_err) => match ws_err {
WsError::AlreadyClosed | WsError::ConnectionClosed => true,
WsError::Io(io_err) => matches!(
io_err.kind(),
+2 -2
View File
@@ -28,7 +28,7 @@ pub(crate) fn cleanup_socket_message(
msg: Option<Result<Message, WsError>>,
) -> Result<Message, GatewayClientError> {
match msg {
Some(msg) => msg.map_err(GatewayClientError::from),
Some(msg) => msg.map_err(GatewayClientError::NetworkError),
None => Err(GatewayClientError::ConnectionAbruptlyClosed),
}
}
@@ -39,7 +39,7 @@ pub(crate) fn cleanup_socket_messages(
match msgs {
Some(msgs) => msgs
.into_iter()
.map(|msg| msg.map_err(GatewayClientError::from))
.map(|msg| msg.map_err(GatewayClientError::NetworkError))
.collect(),
None => Err(GatewayClientError::ConnectionAbruptlyClosed),
}
@@ -10,7 +10,7 @@ use futures::channel::oneshot;
use futures::stream::{SplitSink, SplitStream};
use futures::{SinkExt, StreamExt};
use nym_gateway_requests::shared_key::SharedGatewayKey;
use nym_gateway_requests::{SensitiveServerResponse, ServerResponse, SimpleGatewayRequestsError};
use nym_gateway_requests::{ServerResponse, SimpleGatewayRequestsError};
use nym_task::TaskClient;
use si_scale::helpers::bibytes2;
use std::os::raw::c_int as RawFd;
@@ -188,34 +188,6 @@ impl PartiallyDelegatedRouter {
}
}
}
ServerResponse::EncryptedResponse { ciphertext, nonce } => {
match SensitiveServerResponse::decrypt(
&ciphertext,
&nonce,
self.shared_key.as_ref(),
) {
Ok(response) => match response {
SensitiveServerResponse::ForgetMeAck {} => {
info!("received forget me acknowledgement");
}
SensitiveServerResponse::RememberMeAck {} => {
info!("received remember me acknowledgement");
}
SensitiveServerResponse::KeyUpgradeAck {} => {
warn!(
"received illegal key upgrade acknowledgement in an authenticated client"
);
}
_ => {
warn!("received unknown SensitiveServerResponse");
}
},
Err(e) => {
error!("failed to handle encrypted response: {e}");
}
}
Ok(())
}
other => {
let name = other.name();
warn!("received illegal message of type '{name}' in an authenticated client");
@@ -345,47 +345,25 @@ impl<C, S> Client<C, S> {
#[derive(Clone)]
pub struct NymApiClient {
pub use_bincode: bool,
pub nym_api: nym_api::Client,
// TODO: perhaps if we really need it at some (currently I don't see any reasons for it)
// we could re-implement the communication with the REST API on port 1317
}
impl From<nym_api::Client> for NymApiClient {
fn from(nym_api: nym_api::Client) -> Self {
NymApiClient {
use_bincode: false,
nym_api,
}
}
}
// we have to allow the use of deprecated method here as they're calling the deprecated trait methods
#[allow(deprecated)]
impl NymApiClient {
pub fn new(api_url: Url) -> Self {
let nym_api = nym_api::Client::new(api_url, None);
NymApiClient {
use_bincode: true,
nym_api,
}
NymApiClient { nym_api }
}
#[cfg(not(target_arch = "wasm32"))]
pub fn new_with_timeout(api_url: Url, timeout: std::time::Duration) -> Self {
let nym_api = nym_api::Client::new(api_url, Some(timeout));
NymApiClient {
use_bincode: true,
nym_api,
}
}
#[must_use]
pub fn with_bincode(mut self, use_bincode: bool) -> Self {
self.use_bincode = use_bincode;
self
NymApiClient { nym_api }
}
pub fn new_with_user_agent(api_url: Url, user_agent: impl Into<UserAgent>) -> Self {
@@ -395,10 +373,7 @@ impl NymApiClient {
.build::<ValidatorClientError>()
.expect("failed to build nym api client");
NymApiClient {
use_bincode: false,
nym_api,
}
NymApiClient { nym_api }
}
pub fn api_url(&self) -> &Url {
@@ -435,7 +410,7 @@ impl NymApiClient {
loop {
let mut res = self
.nym_api
.get_basic_entry_assigned_nodes(false, Some(page), None, self.use_bincode)
.get_basic_entry_assigned_nodes(false, Some(page), None)
.await?;
nodes.append(&mut res.nodes.data);
@@ -461,7 +436,7 @@ impl NymApiClient {
loop {
let mut res = self
.nym_api
.get_basic_active_mixing_assigned_nodes(false, Some(page), None, self.use_bincode)
.get_basic_active_mixing_assigned_nodes(false, Some(page), None)
.await?;
nodes.append(&mut res.nodes.data);
@@ -487,7 +462,7 @@ impl NymApiClient {
loop {
let mut res = self
.nym_api
.get_basic_mixing_capable_nodes(false, Some(page), None, self.use_bincode)
.get_basic_mixing_capable_nodes(false, Some(page), None)
.await?;
nodes.append(&mut res.nodes.data);
@@ -510,7 +485,7 @@ impl NymApiClient {
loop {
let mut res = self
.nym_api
.get_basic_nodes(false, Some(page), None, self.use_bincode)
.get_basic_nodes(false, Some(page), None)
.await?;
nodes.append(&mut res.nodes.data);
@@ -318,7 +318,6 @@ pub trait NymApiClientExt: ApiClient {
no_legacy: bool,
page: Option<u32>,
per_page: Option<u32>,
use_bincode: bool,
) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> {
let mut params = Vec::new();
@@ -334,11 +333,7 @@ pub trait NymApiClientExt: ApiClient {
params.push(("per_page", per_page.to_string()))
}
if use_bincode {
params.push(("output", "bincode".to_string()))
}
self.get_response(
self.get_json(
&[
routes::API_VERSION,
"unstable",
@@ -360,7 +355,6 @@ pub trait NymApiClientExt: ApiClient {
no_legacy: bool,
page: Option<u32>,
per_page: Option<u32>,
use_bincode: bool,
) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> {
let mut params = Vec::new();
@@ -376,11 +370,7 @@ pub trait NymApiClientExt: ApiClient {
params.push(("per_page", per_page.to_string()))
}
if use_bincode {
params.push(("output", "bincode".to_string()))
}
self.get_response(
self.get_json(
&[
routes::API_VERSION,
"unstable",
@@ -402,7 +392,6 @@ pub trait NymApiClientExt: ApiClient {
no_legacy: bool,
page: Option<u32>,
per_page: Option<u32>,
use_bincode: bool,
) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> {
let mut params = Vec::new();
@@ -418,11 +407,7 @@ pub trait NymApiClientExt: ApiClient {
params.push(("per_page", per_page.to_string()))
}
if use_bincode {
params.push(("output", "bincode".to_string()))
}
self.get_response(
self.get_json(
&[
routes::API_VERSION,
"unstable",
@@ -442,7 +427,6 @@ pub trait NymApiClientExt: ApiClient {
no_legacy: bool,
page: Option<u32>,
per_page: Option<u32>,
use_bincode: bool,
) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> {
let mut params = Vec::new();
@@ -458,11 +442,7 @@ pub trait NymApiClientExt: ApiClient {
params.push(("per_page", per_page.to_string()))
}
if use_bincode {
params.push(("output", "bincode".to_string()))
}
self.get_response(
self.get_json(
&[
routes::API_VERSION,
"unstable",
+2 -1
View File
@@ -48,7 +48,8 @@ pub mod nym_config {
log::trace!("Loading from file: {:#?}", filepath.as_ref().to_owned());
let config_contents = fs::read_to_string(filepath)?;
toml::from_str(&config_contents).map_err(io::Error::other)
toml::from_str(&config_contents)
.map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err))
}
}
}
+2 -1
View File
@@ -151,7 +151,8 @@ where
let content = fs::read_to_string(path)?;
// TODO: should we be preserving original error type instead?
deserialize_config_from_toml_str(&content).map_err(io::Error::other)
deserialize_config_from_toml_str(&content)
.map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err))
}
//
+2 -9
View File
@@ -20,8 +20,6 @@ nym-credentials = { path = "../credentials" }
nym-compact-ecash = { path = "../nym_offline_compact_ecash" }
nym-ecash-time = { path = "../ecash-time" }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx-pool-guard]
path = "../../sqlx-pool-guard"
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
workspace = true
@@ -33,13 +31,8 @@ features = ["rt-multi-thread", "net", "signal", "fs"]
[build-dependencies]
sqlx = { workspace = true, features = [
"runtime-tokio-rustls",
"sqlite",
"macros",
"migrate",
] }
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
[features]
persistent-storage = ["bincode", "serde"]
persistent-storage = ["bincode", "serde"]
@@ -7,11 +7,10 @@ use crate::models::{
};
use nym_ecash_time::Date;
use sqlx::{Executor, Sqlite, Transaction};
use sqlx_pool_guard::SqlitePoolGuard;
#[derive(Clone)]
pub struct SqliteEcashTicketbookManager {
connection_pool: SqlitePoolGuard,
connection_pool: sqlx::SqlitePool,
}
impl SqliteEcashTicketbookManager {
@@ -20,7 +19,7 @@ impl SqliteEcashTicketbookManager {
/// # Arguments
///
/// * `connection_pool`: database connection pool to use.
pub fn new(connection_pool: SqlitePoolGuard) -> Self {
pub fn new(connection_pool: sqlx::SqlitePool) -> Self {
SqliteEcashTicketbookManager { connection_pool }
}
@@ -34,7 +33,7 @@ impl SqliteEcashTicketbookManager {
"DELETE FROM ecash_ticketbook WHERE expiration_date <= ?",
deadline
)
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?;
Ok(())
}
@@ -61,7 +60,7 @@ impl SqliteEcashTicketbookManager {
data,
expiration_date,
)
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?;
Ok(())
@@ -91,7 +90,7 @@ impl SqliteEcashTicketbookManager {
epoch_id,
total_tickets,
used_tickets,
).execute(&*self.connection_pool).await?;
).execute(&self.connection_pool).await?;
Ok(())
}
@@ -99,14 +98,15 @@ impl SqliteEcashTicketbookManager {
pub(crate) async fn contains_ticketbook_data(&self, data: &[u8]) -> Result<bool, sqlx::Error> {
let exists = sqlx::query(
r#"
SELECT 1
FROM ecash_ticketbook
WHERE ticketbook_data = ?
SELECT EXISTS (
SELECT 1
FROM ecash_ticketbook
WHERE ticketbook_data = ?
)
"#,
)
.bind(data)
.fetch_optional(&*self.connection_pool)
.fetch_optional(&self.connection_pool)
.await?
.is_some();
@@ -122,7 +122,7 @@ impl SqliteEcashTicketbookManager {
FROM ecash_ticketbook
"#,
)
.fetch_all(&*self.connection_pool)
.fetch_all(&self.connection_pool)
.await
}
@@ -144,7 +144,7 @@ impl SqliteEcashTicketbookManager {
ticketbook_id,
expected_current_total_spent
)
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?
.rows_affected();
Ok(affected > 0)
@@ -154,7 +154,7 @@ impl SqliteEcashTicketbookManager {
&self,
) -> Result<Vec<StoredPendingTicketbook>, sqlx::Error> {
sqlx::query_as("SELECT * FROM pending_issuance")
.fetch_all(&*self.connection_pool)
.fetch_all(&self.connection_pool)
.await
}
@@ -166,7 +166,7 @@ impl SqliteEcashTicketbookManager {
"DELETE FROM pending_issuance WHERE deposit_id = ?",
pending_id
)
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?;
Ok(())
}
@@ -183,7 +183,7 @@ impl SqliteEcashTicketbookManager {
"#,
epoch_id
)
.fetch_optional(&*self.connection_pool)
.fetch_optional(&self.connection_pool)
.await
}
@@ -209,7 +209,7 @@ impl SqliteEcashTicketbookManager {
serialisation_revision,
epoch_id
)
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?;
Ok(())
}
@@ -226,7 +226,7 @@ impl SqliteEcashTicketbookManager {
"#,
epoch_id
)
.fetch_optional(&*self.connection_pool)
.fetch_optional(&self.connection_pool)
.await
}
@@ -252,7 +252,7 @@ impl SqliteEcashTicketbookManager {
serialisation_revision,
epoch_id,
)
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?;
Ok(())
}
@@ -270,7 +270,7 @@ impl SqliteEcashTicketbookManager {
"#,
expiration_date
)
.fetch_optional(&*self.connection_pool)
.fetch_optional(&self.connection_pool)
.await
}
@@ -299,7 +299,7 @@ impl SqliteEcashTicketbookManager {
serialisation_revision,
expiration_date
)
.execute(&*self.connection_pool)
.execute(&self.connection_pool)
.await?;
Ok(())
}
@@ -37,7 +37,6 @@ use sqlx::{
sqlite::{SqliteAutoVacuum, SqliteSynchronous},
ConnectOptions,
};
use sqlx_pool_guard::SqlitePoolGuard;
use std::path::Path;
use zeroize::Zeroizing;
@@ -63,7 +62,7 @@ impl PersistentStorage {
.journal_mode(sqlx::sqlite::SqliteJournalMode::Wal)
.synchronous(SqliteSynchronous::Normal)
.auto_vacuum(SqliteAutoVacuum::Incremental)
.filename(&database_path)
.filename(database_path)
.create_if_missing(true)
.disable_statement_logging();
@@ -75,17 +74,13 @@ impl PersistentStorage {
}
};
let connection_pool =
SqlitePoolGuard::new(database_path.as_ref().to_path_buf(), connection_pool);
if let Err(err) = sqlx::migrate!("./migrations").run(&*connection_pool).await {
if let Err(err) = sqlx::migrate!("./migrations").run(&connection_pool).await {
error!("Failed to perform migration on the SQLx database: {err}");
connection_pool.close().await;
return Err(err.into());
}
Ok(PersistentStorage {
storage_manager: SqliteEcashTicketbookManager::new(connection_pool),
storage_manager: SqliteEcashTicketbookManager::new(connection_pool.clone()),
})
}
}
-1
View File
@@ -28,7 +28,6 @@ nym-crypto = { path = "../crypto", features = ["aead", "hashing"] }
nym-pemstore = { path = "../pemstore" }
nym-sphinx = { path = "../nymsphinx" }
nym-serde-helpers = { path = "../serde-helpers", features = ["base64"] }
nym-statistics-common = { path = "../statistics" }
nym-task = { path = "../task" }
nym-credentials = { path = "../credentials" }
@@ -11,7 +11,6 @@ use crate::{
use nym_credentials_interface::CredentialSpendingData;
use nym_crypto::asymmetric::ed25519;
use nym_sphinx::DestinationAddressBytes;
use nym_statistics_common::types::SessionType;
use serde::{Deserialize, Serialize};
use std::str::FromStr;
use tungstenite::Message;
@@ -30,9 +29,6 @@ pub enum ClientRequest {
client: bool,
stats: bool,
},
RememberMe {
session_type: SessionType,
},
}
impl ClientRequest {
@@ -12,7 +12,6 @@ use tungstenite::Message;
pub enum SensitiveServerResponse {
KeyUpgradeAck {},
ForgetMeAck {},
RememberMeAck {},
}
impl SensitiveServerResponse {
+1
View File
@@ -22,6 +22,7 @@ thiserror = { workspace = true }
tracing = { workspace = true }
nym-sphinx = { path = "../nymsphinx" }
nym-credentials-interface = { path = "../credentials-interface" }
nym-node-metrics = { path = "../../nym-node/nym-node-metrics" }
nym-statistics-common = { path = "../statistics" }
@@ -1,7 +0,0 @@
/*
* Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
* SPDX-License-Identifier: GPL-3.0-only
*/
ALTER TABLE sessions_active
ADD COLUMN remember INTEGER NOT NULL default 0;
+1 -12
View File
@@ -3,9 +3,8 @@
use error::StatsStorageError;
use models::StoredFinishedSession;
use nym_node_metrics::entry::{ActiveSession, FinishedSession};
use nym_node_metrics::entry::{ActiveSession, FinishedSession, SessionType};
use nym_sphinx::DestinationAddressBytes;
use nym_statistics_common::types::SessionType;
use sessions::SessionManager;
use sqlx::{
sqlite::{SqliteAutoVacuum, SqliteSynchronous},
@@ -148,16 +147,6 @@ impl PersistentStatsStorage {
.await?)
}
pub async fn remember_active_session(
&self,
client_address: DestinationAddressBytes,
) -> Result<(), StatsStorageError> {
Ok(self
.session_manager
.remember_active_session(client_address.as_base58_string())
.await?)
}
pub async fn update_active_session_type(
&self,
client_address: DestinationAddressBytes,
+18 -4
View File
@@ -1,11 +1,12 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use nym_node_metrics::entry::{ActiveSession, FinishedSession};
use nym_statistics_common::types::SessionType;
use nym_node_metrics::entry::{ActiveSession, FinishedSession, SessionType};
use sqlx::prelude::FromRow;
use time::OffsetDateTime;
pub use nym_credentials_interface::TicketType;
#[derive(FromRow)]
pub struct StoredFinishedSession {
duration_ms: i64,
@@ -21,11 +22,25 @@ impl From<StoredFinishedSession> for FinishedSession {
}
}
pub trait ToSessionType {
fn to_session_type(&self) -> SessionType;
}
impl ToSessionType for TicketType {
fn to_session_type(&self) -> SessionType {
match self {
TicketType::V1MixnetEntry => SessionType::Mixnet,
TicketType::V1MixnetExit => SessionType::Mixnet,
TicketType::V1WireguardEntry => SessionType::Vpn,
TicketType::V1WireguardExit => SessionType::Vpn,
}
}
}
#[derive(FromRow)]
pub(crate) struct StoredActiveSession {
start_time: OffsetDateTime,
typ: String,
remember: u8,
}
impl From<StoredActiveSession> for ActiveSession {
@@ -33,7 +48,6 @@ impl From<StoredActiveSession> for ActiveSession {
ActiveSession {
start: value.start_time,
typ: SessionType::from_string(&value.typ),
remember: value.remember != 0,
}
}
}
+6 -18
View File
@@ -107,7 +107,7 @@ impl SessionManager {
typ: String,
) -> Result<()> {
sqlx::query!(
"INSERT INTO sessions_active (client_address, start_time, typ, remember) VALUES (?, ?, ?, 0)",
"INSERT INTO sessions_active (client_address, start_time, typ) VALUES (?, ?, ?)",
client_address_b58,
start_time,
typ
@@ -117,16 +117,6 @@ impl SessionManager {
Ok(())
}
pub(crate) async fn remember_active_session(&self, client_address_b58: String) -> Result<()> {
sqlx::query!(
"UPDATE sessions_active SET remember = 1 WHERE client_address = ?",
client_address_b58,
)
.execute(&self.connection_pool)
.await?;
Ok(())
}
pub(crate) async fn update_active_session_type(
&self,
client_address_b58: String,
@@ -146,16 +136,14 @@ impl SessionManager {
&self,
client_address_b58: String,
) -> Result<Option<StoredActiveSession>> {
sqlx::query_as(
"SELECT start_time, typ, remember FROM sessions_active WHERE client_address = ?",
)
.bind(client_address_b58)
.fetch_optional(&self.connection_pool)
.await
sqlx::query_as("SELECT start_time, typ FROM sessions_active WHERE client_address = ?")
.bind(client_address_b58)
.fetch_optional(&self.connection_pool)
.await
}
pub(crate) async fn get_all_active_sessions(&self) -> Result<Vec<StoredActiveSession>> {
sqlx::query_as("SELECT start_time, typ, remember FROM sessions_active")
sqlx::query_as("SELECT start_time, typ FROM sessions_active")
.fetch_all(&self.connection_pool)
.await
}
+2 -4
View File
@@ -12,8 +12,7 @@ license.workspace = true
[dependencies]
async-trait = { workspace = true }
bincode = { workspace = true }
reqwest = { workspace = true, features = ["json", "gzip", "deflate", "brotli", "zstd"] }
reqwest = { workspace = true, features = ["json", "gzip"] }
http.workspace = true
url = { workspace = true }
once_cell = { workspace = true }
@@ -27,7 +26,7 @@ bytes = { workspace = true }
encoding_rs = { workspace = true }
mime = { workspace = true }
nym-http-api-common = { path = "../http-api-common", default-features = false }
nym-bin-common = { path = "../bin-common" }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies]
@@ -40,4 +39,3 @@ features = ["tokio"]
[dev-dependencies]
tokio = { workspace = true, features = ["rt", "macros"] }
+81 -194
View File
@@ -136,24 +136,19 @@
//! ```
#![warn(missing_docs)]
pub use reqwest::{IntoUrl, StatusCode};
use crate::path::RequestPath;
use async_trait::async_trait;
use bytes::Bytes;
use http::header::CONTENT_TYPE;
use http::HeaderMap;
use mime::Mime;
use reqwest::header::HeaderValue;
use reqwest::{RequestBuilder, Response};
use reqwest::{RequestBuilder, Response, StatusCode};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use std::fmt::Display;
use std::time::Duration;
use thiserror::Error;
use tracing::{debug, instrument, warn};
use tracing::{instrument, warn};
use url::Url;
use http::HeaderMap;
pub use reqwest::IntoUrl;
#[cfg(not(target_arch = "wasm32"))]
use std::net::SocketAddr;
#[cfg(not(target_arch = "wasm32"))]
@@ -164,8 +159,6 @@ pub use user_agent::UserAgent;
#[cfg(not(target_arch = "wasm32"))]
mod dns;
mod path;
#[cfg(not(target_arch = "wasm32"))]
pub use dns::{HickoryDnsError, HickoryDnsResolver};
@@ -217,36 +210,17 @@ pub enum HttpClientError<E: Display = String> {
#[error("failed to resolve request. status: '{status}', additional error message: {error}")]
EndpointFailure { status: StatusCode, error: E },
#[error("failed to decode response body: {message} from {content}")]
ResponseDecodeFailure { message: String, content: String },
#[error("failed to decode response body: {source} from {content}")]
ResponseDecodeFailure {
source: serde_json::Error,
content: String,
},
#[cfg(target_arch = "wasm32")]
#[error("the request has timed out")]
RequestTimeout,
}
impl HttpClientError {
/// Returns true if the error is a timeout.
pub fn is_timeout(&self) -> bool {
match self {
HttpClientError::ReqwestClientError { source } => source.is_timeout(),
#[cfg(target_arch = "wasm32")]
HttpClientError::RequestTimeout => true,
_ => false,
}
}
/// Returns the HTTP status code if available.
pub fn status_code(&self) -> Option<StatusCode> {
match self {
HttpClientError::RequestFailure { status } => Some(*status),
HttpClientError::EmptyResponse { status } => Some(*status),
HttpClientError::EndpointFailure { status, .. } => Some(*status),
_ => None,
}
}
}
/// A `ClientBuilder` can be used to create a [`Client`] with custom configuration applied consistently
/// and state tracked across subsequent requests.
pub struct ClientBuilder {
@@ -291,18 +265,14 @@ impl ClientBuilder {
#[cfg(not(target_arch = "wasm32"))]
let reqwest_client_builder = {
// Note: I believe the manual enable calls for the compression methods are extra
// as the various compression features for `reqwest` crate should be enabled
// just by including the feature which:
// `"Enable[s] auto decompression by checking the Content-Encoding response header."`
let r = reqwest::ClientBuilder::new();
// Note this is extra as the `gzip` feature for `reqwest` crate should be enabled which
// `"Enable[s] auto gzip decompression by checking the Content-Encoding response header."`
//
// I am going to leave these here anyways so that removing a decompression method
// from the features list will throw an error if it is not also removed here.
reqwest::ClientBuilder::new()
.gzip(true)
.deflate(true)
.brotli(true)
.zstd(true)
// I am going to leave it here anyways so that gzip decompression is attempted even if
// that feature is removed.
r.gzip(true)
};
ClientBuilder {
@@ -457,15 +427,14 @@ impl Client {
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait ApiClientCore {
/// Create an HTTP request using the host configured in this client.
fn create_request<P, B, K, V>(
fn create_request<B, K, V>(
&self,
method: reqwest::Method,
path: P,
path: PathSegments<'_>,
params: Params<'_, K, V>,
json_body: Option<&B>,
) -> RequestBuilder
where
P: RequestPath,
B: Serialize + ?Sized,
K: AsRef<str>,
V: AsRef<str>;
@@ -516,7 +485,7 @@ pub trait ApiClientCore {
};
let params: Vec<(String, String)> = standin_url.query_pairs().into_owned().collect();
self.create_request(method, path.as_slice(), &params, json_body)
self.create_request(method, &path, &params, json_body)
}
/// Send a created HTTP request.
@@ -529,15 +498,14 @@ pub trait ApiClientCore {
E: Display;
/// Create and send a created HTTP request.
async fn send_request<P, B, K, V, E>(
async fn send_request<B, K, V, E>(
&self,
method: reqwest::Method,
path: P,
path: PathSegments<'_>,
params: Params<'_, K, V>,
json_body: Option<&B>,
) -> Result<Response, HttpClientError<E>>
where
P: RequestPath + Send + Sync,
B: Serialize + ?Sized + Sync,
K: AsRef<str> + Sync,
V: AsRef<str> + Sync,
@@ -551,16 +519,14 @@ pub trait ApiClientCore {
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl ApiClientCore for Client {
#[instrument(level = "debug", skip_all, fields(path=?path))]
fn create_request<P, B, K, V>(
fn create_request<B, K, V>(
&self,
method: reqwest::Method,
path: P,
path: PathSegments<'_>,
params: Params<'_, K, V>,
json_body: Option<&B>,
) -> RequestBuilder
where
P: RequestPath,
B: Serialize + ?Sized,
K: AsRef<str>,
V: AsRef<str>,
@@ -569,6 +535,11 @@ impl ApiClientCore for Client {
let mut request = self.reqwest_client.request(method.clone(), url);
// Indicate that compressed responses are preferred, but if not supported other encodings are fine.
// TODO: Down the road we can be more selective about adding this, but it's inclusion here guarantees
// that we use compression when available.
request = request.header(reqwest::header::ACCEPT_ENCODING, "gzip;q=1.0, *;q=0.5");
if let Some(body) = json_body {
request = request.json(body);
}
@@ -603,9 +574,12 @@ impl ApiClientCore for Client {
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait ApiClient: ApiClientCore {
/// Create an HTTP GET Request with the provided path and parameters
fn create_get_request<P, K, V>(&self, path: P, params: Params<'_, K, V>) -> RequestBuilder
fn create_get_request<K, V>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> RequestBuilder
where
P: RequestPath,
K: AsRef<str>,
V: AsRef<str>,
{
@@ -613,14 +587,13 @@ pub trait ApiClient: ApiClientCore {
}
/// Create an HTTP POST Request with the provided path, parameters, and json body
fn create_post_request<P, B, K, V>(
fn create_post_request<B, K, V>(
&self,
path: P,
path: PathSegments<'_>,
params: Params<'_, K, V>,
json_body: &B,
) -> RequestBuilder
where
P: RequestPath,
B: Serialize + ?Sized,
K: AsRef<str>,
V: AsRef<str>,
@@ -629,9 +602,12 @@ pub trait ApiClient: ApiClientCore {
}
/// Create an HTTP DELETE Request with the provided path and parameters
fn create_delete_request<P, K, V>(&self, path: P, params: Params<'_, K, V>) -> RequestBuilder
fn create_delete_request<K, V>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> RequestBuilder
where
P: RequestPath,
K: AsRef<str>,
V: AsRef<str>,
{
@@ -639,14 +615,13 @@ pub trait ApiClient: ApiClientCore {
}
/// Create an HTTP PATCH Request with the provided path, parameters, and json body
fn create_patch_request<P, B, K, V>(
fn create_patch_request<B, K, V>(
&self,
path: P,
path: PathSegments<'_>,
params: Params<'_, K, V>,
json_body: &B,
) -> RequestBuilder
where
P: RequestPath,
B: Serialize + ?Sized,
K: AsRef<str>,
V: AsRef<str>,
@@ -656,13 +631,12 @@ pub trait ApiClient: ApiClientCore {
/// Create and send an HTTP GET Request with the provided path and parameters
#[instrument(level = "debug", skip_all, fields(path=?path))]
async fn send_get_request<P, K, V, E>(
async fn send_get_request<K, V, E>(
&self,
path: P,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Result<Response, HttpClientError<E>>
where
P: RequestPath + Send + Sync,
K: AsRef<str> + Sync,
V: AsRef<str> + Sync,
E: Display,
@@ -672,14 +646,13 @@ pub trait ApiClient: ApiClientCore {
}
/// Create and send an HTTP POST Request with the provided path, parameters, and json data
async fn send_post_request<P, B, K, V, E>(
async fn send_post_request<B, K, V, E>(
&self,
path: P,
path: PathSegments<'_>,
params: Params<'_, K, V>,
json_body: &B,
) -> Result<Response, HttpClientError<E>>
where
P: RequestPath + Send + Sync,
B: Serialize + ?Sized + Sync,
K: AsRef<str> + Sync,
V: AsRef<str> + Sync,
@@ -690,13 +663,12 @@ pub trait ApiClient: ApiClientCore {
}
/// Create and send an HTTP DELETE Request with the provided path and parameters
async fn send_delete_request<P, K, V, E>(
async fn send_delete_request<K, V, E>(
&self,
path: P,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Result<Response, HttpClientError<E>>
where
P: RequestPath + Send + Sync,
K: AsRef<str> + Sync,
V: AsRef<str> + Sync,
E: Display,
@@ -706,14 +678,13 @@ pub trait ApiClient: ApiClientCore {
}
/// Create and send an HTTP PATCH Request with the provided path, parameters, and json data
async fn send_patch_request<P, B, K, V, E>(
async fn send_patch_request<B, K, V, E>(
&self,
path: P,
path: PathSegments<'_>,
params: Params<'_, K, V>,
json_body: &B,
) -> Result<Response, HttpClientError<E>>
where
P: RequestPath + Send + Sync,
B: Serialize + ?Sized + Sync,
K: AsRef<str> + Sync,
V: AsRef<str> + Sync,
@@ -726,33 +697,13 @@ pub trait ApiClient: ApiClientCore {
/// 'get' json data from the segment-defined path, e.g. `["api", "v1", "mixnodes"]`, with tuple
/// defined key-value parameters, e.g. `[("since", "12345")]`. Attempt to parse the response
/// into the provided type `T`.
#[instrument(level = "debug", skip_all, fields(path=?path))]
// TODO: deprecate in favour of get_response that works based on mime type in the response
async fn get_json<P, T, K, V, E>(
#[instrument(level = "debug", skip_all)]
async fn get_json<T, K, V, E>(
&self,
path: P,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Result<T, HttpClientError<E>>
where
P: RequestPath + Send + Sync,
for<'a> T: Deserialize<'a>,
K: AsRef<str> + Sync,
V: AsRef<str> + Sync,
E: Display + DeserializeOwned,
{
self.get_response(path, params).await
}
/// 'get' data from the segment-defined path, e.g. `["api", "v1", "mixnodes"]`, with tuple
/// defined key-value parameters, e.g. `[("since", "12345")]`. Attempt to parse the response
/// into the provided type `T` based on the content type header
async fn get_response<P, T, K, V, E>(
&self,
path: P,
params: Params<'_, K, V>,
) -> Result<T, HttpClientError<E>>
where
P: RequestPath + Send + Sync,
for<'a> T: Deserialize<'a>,
K: AsRef<str> + Sync,
V: AsRef<str> + Sync,
@@ -767,14 +718,13 @@ pub trait ApiClient: ApiClientCore {
/// 'post' json data to the segment-defined path, e.g. `["api", "v1", "mixnodes"]`, with tuple
/// defined key-value parameters, e.g. `[("since", "12345")]`. Attempt to parse the response
/// into the provided type `T`.
async fn post_json<P, B, T, K, V, E>(
async fn post_json<B, T, K, V, E>(
&self,
path: P,
path: PathSegments<'_>,
params: Params<'_, K, V>,
json_body: &B,
) -> Result<T, HttpClientError<E>>
where
P: RequestPath + Send + Sync,
B: Serialize + ?Sized + Sync,
for<'a> T: Deserialize<'a>,
K: AsRef<str> + Sync,
@@ -790,13 +740,12 @@ pub trait ApiClient: ApiClientCore {
/// 'delete' json data from the segment-defined path, e.g. `["api", "v1", "mixnodes"]`, with
/// tuple defined key-value parameters, e.g. `[("since", "12345")]`. Attempt to parse the
/// response into the provided type `T`.
async fn delete_json<P, T, K, V, E>(
async fn delete_json<T, K, V, E>(
&self,
path: P,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Result<T, HttpClientError<E>>
where
P: RequestPath + Send + Sync,
for<'a> T: Deserialize<'a>,
K: AsRef<str> + Sync,
V: AsRef<str> + Sync,
@@ -811,14 +760,13 @@ pub trait ApiClient: ApiClientCore {
/// 'patch' json data at the segment-defined path, e.g. `["api", "v1", "mixnodes"]`, with tuple
/// defined key-value parameters, e.g. `[("since", "12345")]`. Attempt to parse the response
/// into the provided type `T`.
async fn patch_json<P, B, T, K, V, E>(
async fn patch_json<B, T, K, V, E>(
&self,
path: P,
path: PathSegments<'_>,
params: Params<'_, K, V>,
json_body: &B,
) -> Result<T, HttpClientError<E>>
where
P: RequestPath + Send + Sync,
B: Serialize + ?Sized + Sync,
for<'a> T: Deserialize<'a>,
K: AsRef<str> + Sync,
@@ -901,7 +849,7 @@ impl<C> ApiClient for C where C: ApiClientCore + Sync {}
/// utility function that should solve the double slash problem in API urls forever.
fn sanitize_url<K: AsRef<str>, V: AsRef<str>>(
base: &Url,
request_path: impl RequestPath,
segments: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Url {
let mut url = base.clone();
@@ -911,7 +859,10 @@ fn sanitize_url<K: AsRef<str>, V: AsRef<str>>(
path_segments.pop_if_empty();
for segment in request_path.to_sanitized_segments() {
for segment in segments {
let segment = segment.strip_prefix('/').unwrap_or(segment);
let segment = segment.strip_suffix('/').unwrap_or(segment);
path_segments.push(segment);
}
@@ -926,10 +877,14 @@ fn sanitize_url<K: AsRef<str>, V: AsRef<str>>(
url
}
fn decode_as_text(bytes: &bytes::Bytes, headers: &HeaderMap) -> String {
fn decode_as_text(bytes: &bytes::Bytes, headers: HeaderMap) -> String {
use encoding_rs::{Encoding, UTF_8};
use mime::Mime;
let content_type = try_get_mime_type(headers);
let content_type = headers
.get(http::header::CONTENT_TYPE)
.and_then(|value| value.to_str().ok())
.and_then(|value| value.parse::<Mime>().ok());
let encoding_name = content_type
.as_ref()
@@ -942,7 +897,7 @@ fn decode_as_text(bytes: &bytes::Bytes, headers: &HeaderMap) -> String {
text.into_owned()
}
/// Attempt to parse a response object from an HTTP response
/// Attempt to parse a json object from an HTTP response
#[instrument(level = "debug", skip_all)]
pub async fn parse_response<T, E>(res: Response, allow_empty: bool) -> Result<T, HttpClientError<E>>
where
@@ -964,7 +919,16 @@ where
// internally reqwest is first retrieving bytes and then performing parsing via serde_json
// (and similarly does the same thing for text())
let full = res.bytes().await?;
decode_raw_response(&headers, full)
match serde_json::from_slice(&full) {
Ok(data) => Ok(data),
Err(err) => {
let content = decode_as_text(&full, headers);
Err(HttpClientError::ResponseDecodeFailure {
source: err,
content,
})
}
}
} else if res.status() == StatusCode::NOT_FOUND {
Err(HttpClientError::NotFound)
} else {
@@ -983,71 +947,6 @@ where
}
}
fn decode_as_json<T, E>(headers: &HeaderMap, content: Bytes) -> Result<T, HttpClientError<E>>
where
T: DeserializeOwned,
E: DeserializeOwned + Display,
{
match serde_json::from_slice(&content) {
Ok(data) => Ok(data),
Err(err) => {
let content = decode_as_text(&content, headers);
Err(HttpClientError::ResponseDecodeFailure {
message: err.to_string(),
content,
})
}
}
}
fn decode_as_bincode<T, E>(headers: &HeaderMap, content: Bytes) -> Result<T, HttpClientError<E>>
where
T: DeserializeOwned,
E: DeserializeOwned + Display,
{
use bincode::Options;
let opts = nym_http_api_common::make_bincode_serializer();
match opts.deserialize(&content) {
Ok(data) => Ok(data),
Err(err) => {
let content = decode_as_text(&content, headers);
Err(HttpClientError::ResponseDecodeFailure {
message: err.to_string(),
content,
})
}
}
}
fn decode_raw_response<T, E>(headers: &HeaderMap, content: Bytes) -> Result<T, HttpClientError<E>>
where
T: DeserializeOwned,
E: DeserializeOwned + Display,
{
// if content type header is missing, fallback to our old default, json
let mime = try_get_mime_type(headers).unwrap_or(mime::APPLICATION_JSON);
debug!("attempting to parse response as {mime}");
// unfortunately we can't use stronger typing for subtype as "bincode" is not a defined mime type
match (mime.type_(), mime.subtype().as_str()) {
(mime::APPLICATION, "json") => decode_as_json(headers, content),
(mime::APPLICATION, "bincode") => decode_as_bincode(headers, content),
(_, _) => {
debug!("unrecognised mime type {mime}. falling back to json decoding...");
decode_as_json(headers, content)
}
}
}
fn try_get_mime_type(headers: &HeaderMap) -> Option<Mime> {
headers
.get(CONTENT_TYPE)
.and_then(|value| value.to_str().ok())
.and_then(|value| value.parse::<Mime>().ok())
}
#[cfg(test)]
mod tests {
use super::*;
@@ -1056,18 +955,6 @@ mod tests {
fn sanitizing_urls() {
let base_url: Url = "http://foomp.com".parse().unwrap();
// works with a full string
assert_eq!(
"http://foomp.com/foo/bar",
sanitize_url(&base_url, "/foo//bar/", NO_PARAMS).as_str()
);
// (and leading slash doesn't matter)
assert_eq!(
"http://foomp.com/foo/bar",
sanitize_url(&base_url, "foo//bar/", NO_PARAMS).as_str()
);
// works with 1 segment
assert_eq!(
"http://foomp.com/foo",
-59
View File
@@ -1,59 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::fmt::Debug;
/// Collection of URL Path Segments
pub type PathSegments<'a> = &'a [&'a str];
fn sanitize_fragment(segment: &str) -> &str {
segment.trim_matches(|c: char| c.is_whitespace() || c == '/')
}
pub trait RequestPath: Debug {
fn to_sanitized_segments(&self) -> Vec<&str>;
}
macro_rules! impl_stringified_sanitized_segments {
($frag_iter:expr) => {{
let mut path_segments = Vec::new();
for segment in $frag_iter {
if !segment.is_empty() {
path_segments.push(sanitize_fragment(segment));
}
}
path_segments
}};
}
impl RequestPath for PathSegments<'_> {
fn to_sanitized_segments(&self) -> Vec<&str> {
impl_stringified_sanitized_segments!(self.iter())
}
}
impl<const N: usize> RequestPath for &[&str; N] {
fn to_sanitized_segments(&self) -> Vec<&str> {
impl_stringified_sanitized_segments!(self.iter())
}
}
impl RequestPath for &str {
fn to_sanitized_segments(&self) -> Vec<&str> {
impl_stringified_sanitized_segments!(self.split('/'))
}
}
impl RequestPath for String {
fn to_sanitized_segments(&self) -> Vec<&str> {
impl_stringified_sanitized_segments!(self.split('/'))
}
}
impl RequestPath for &String {
fn to_sanitized_segments(&self) -> Vec<&str> {
impl_stringified_sanitized_segments!(self.split('/'))
}
}
+11 -35
View File
@@ -11,44 +11,20 @@ license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
axum = { workspace = true, optional = true }
axum-client-ip = { workspace = true, optional = true }
bincode = { workspace = true }
bytes = { workspace = true, optional = true }
colored = { workspace = true, optional = true }
futures = { workspace = true, optional = true }
mime = { workspace = true, optional = true }
axum-client-ip.workspace = true
axum.workspace = true
bytes = { workspace = true }
colored.workspace = true
futures = { workspace = true }
mime = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_yaml = { workspace = true, optional = true }
subtle = { workspace = true, optional = true }
time = { workspace = true, optional = true, features = ["macros"] }
tower = { workspace = true, optional = true }
serde_json.workspace = true
serde_yaml = { workspace = true }
subtle.workspace = true
tower = { workspace = true }
tracing.workspace = true
utoipa = { workspace = true, optional = true }
zeroize = { workspace = true, optional = true }
zeroize = { workspace = true }
[features]
default = []
output = [
"axum",
"bytes",
"mime",
"serde_yaml",
"time",
"time/formatting"
]
middleware = [
"axum",
"axum-client-ip",
"colored",
"futures",
"subtle",
"tower",
"zeroize"
]
utoipa = ["dep:utoipa"]
[lints]
workspace = true
+87 -15
View File
@@ -1,20 +1,92 @@
// Copyright 2023-2025 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#[cfg(feature = "middleware")]
use axum::http::{header, HeaderValue, StatusCode};
use axum::response::{IntoResponse, Response};
use axum::Json;
use bytes::{BufMut, BytesMut};
use serde::{Deserialize, Serialize};
pub mod middleware;
#[cfg(feature = "output")]
pub mod response;
// don't break existing imports
#[cfg(feature = "output")]
pub use response::*;
// be explicit about those values because bincode uses different defaults in different places
pub fn make_bincode_serializer() -> impl ::bincode::Options {
use ::bincode::Options;
::bincode::DefaultOptions::new()
.with_little_endian()
.with_varint_encoding()
#[derive(Debug, Clone)]
pub enum FormattedResponse<T> {
Json(Json<T>),
Yaml(Yaml<T>),
}
impl<T> IntoResponse for FormattedResponse<T>
where
T: Serialize,
{
fn into_response(self) -> Response {
match self {
FormattedResponse::Json(json_response) => json_response.into_response(),
FormattedResponse::Yaml(yaml_response) => yaml_response.into_response(),
}
}
}
#[derive(Default, Debug, Serialize, Deserialize, Copy, Clone)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[serde(rename_all = "lowercase")]
pub enum Output {
#[default]
Json,
Yaml,
}
#[derive(Default, Debug, Serialize, Deserialize, Copy, Clone)]
#[cfg_attr(feature = "utoipa", derive(utoipa::IntoParams, utoipa::ToSchema))]
#[serde(default)]
pub struct OutputParams {
pub output: Option<Output>,
}
impl Output {
pub fn to_response<T: Serialize>(self, data: T) -> FormattedResponse<T> {
match self {
Output::Json => FormattedResponse::Json(Json(data)),
Output::Yaml => FormattedResponse::Yaml(Yaml(data)),
}
}
}
#[derive(Debug, Clone, Copy, Default)]
#[must_use]
pub struct Yaml<T>(pub T);
impl<T> From<T> for Yaml<T> {
fn from(inner: T) -> Self {
Self(inner)
}
}
impl<T> IntoResponse for Yaml<T>
where
T: Serialize,
{
// replicates axum's Json
fn into_response(self) -> Response {
let mut buf = BytesMut::with_capacity(128).writer();
match serde_yaml::to_writer(&mut buf, &self.0) {
Ok(()) => (
[(
header::CONTENT_TYPE,
HeaderValue::from_static("application/yaml"),
)],
buf.into_inner().freeze(),
)
.into_response(),
Err(err) => (
StatusCode::INTERNAL_SERVER_ERROR,
[(
header::CONTENT_TYPE,
HeaderValue::from_static(mime::TEXT_PLAIN_UTF_8.as_ref()),
)],
err.to_string(),
)
.into_response(),
}
}
}
@@ -9,35 +9,13 @@ use axum::response::IntoResponse;
use axum_client_ip::InsecureClientIp;
use colored::Colorize;
use std::time::Instant;
use tracing::{debug, info};
enum LogLevel {
Debug,
Info,
}
pub async fn log_request_info(
insecure_client_ip: InsecureClientIp,
request: Request,
next: Next,
) -> impl IntoResponse {
log_request(insecure_client_ip, request, next, LogLevel::Info).await
}
pub async fn log_request_debug(
insecure_client_ip: InsecureClientIp,
request: Request,
next: Next,
) -> impl IntoResponse {
log_request(insecure_client_ip, request, next, LogLevel::Debug).await
}
use tracing::info;
/// Simple logger for requests
async fn log_request(
pub async fn logger(
InsecureClientIp(addr): InsecureClientIp,
request: Request,
next: Next,
level: LogLevel,
) -> impl IntoResponse {
// TODO dz use `OriginalUri` extractor to get full URI even for nested
// routers if routes aren't logged correctly in handlers
@@ -80,14 +58,7 @@ async fn log_request(
let agent_str = "agent".bold();
match level {
LogLevel::Debug => debug!(
"[{addr} -> {host}] {method} '{uri}': {print_status} {time_taken} {agent_str}: {agent}"
),
LogLevel::Info => info!(
"[{addr} -> {host}] {method} '{uri}': {print_status} {time_taken} {agent_str}: {agent}"
),
}
info!("[{addr} -> {host}] {method} '{uri}': {print_status} {time_taken} {agent_str}: {agent}");
res
}
@@ -1,49 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::response::{error_response, ResponseWrapper};
use axum::http::header::IntoHeaderName;
use axum::http::{header, HeaderValue};
use axum::response::{IntoResponse, Response};
use bytes::{BufMut, BytesMut};
use serde::Serialize;
#[derive(Debug, Clone, Default)]
#[must_use]
pub struct Bincode<T>(pub(crate) ResponseWrapper<T>);
impl<T> From<T> for Bincode<T> {
fn from(response: T) -> Self {
Bincode(ResponseWrapper::new(response).with_header(
header::CONTENT_TYPE,
HeaderValue::from_static("application/bincode"),
))
}
}
impl<T> Bincode<T> {
pub(crate) fn with_header(
mut self,
name: impl IntoHeaderName,
value: impl Into<HeaderValue>,
) -> Self {
self.0.headers.insert(name, value.into());
self
}
}
impl<T> IntoResponse for Bincode<T>
where
T: Serialize,
{
// replicates axum's Json
fn into_response(self) -> Response {
use bincode::Options;
let mut buf = BytesMut::with_capacity(128).writer();
match crate::make_bincode_serializer().serialize_into(&mut buf, &self.0.data) {
Ok(()) => (self.0.headers, buf.into_inner().freeze()).into_response(),
Err(err) => error_response(err),
}
}
}
@@ -1,49 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::response::{error_response, ResponseWrapper};
use axum::http::header::IntoHeaderName;
use axum::http::{header, HeaderValue};
use axum::response::{IntoResponse, Response};
use bytes::{BufMut, BytesMut};
use serde::Serialize;
use utoipa::gen::serde_json;
// don't use axum's Json directly as we need to be able to define custom headers
#[derive(Debug, Clone, Default)]
#[must_use]
pub struct Json<T>(pub(crate) ResponseWrapper<T>);
impl<T> From<T> for Json<T> {
fn from(response: T) -> Self {
Json(ResponseWrapper::new(response).with_header(
header::CONTENT_TYPE,
HeaderValue::from_static(mime::APPLICATION_JSON.as_ref()),
))
}
}
impl<T> Json<T> {
pub(crate) fn with_header(
mut self,
name: impl IntoHeaderName,
value: impl Into<HeaderValue>,
) -> Self {
self.0.headers.insert(name, value.into());
self
}
}
impl<T> IntoResponse for Json<T>
where
T: Serialize,
{
fn into_response(self) -> Response {
let mut buf = BytesMut::with_capacity(128).writer();
match serde_json::to_writer(&mut buf, &self.0.data) {
Ok(()) => (self.0.headers, buf.into_inner().freeze()).into_response(),
Err(err) => error_response(err),
}
}
}
-190
View File
@@ -1,190 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use axum::http::header::IntoHeaderName;
use axum::http::{header, HeaderMap, HeaderValue, StatusCode};
use axum::response::{IntoResponse, Response};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use time::format_description::BorrowedFormatItem;
use time::macros::{format_description, offset};
use time::OffsetDateTime;
pub mod bincode;
pub mod json;
pub mod yaml;
pub use json::Json;
pub use yaml::Yaml;
pub use bincode::Bincode;
#[derive(Debug, Clone, Default)]
pub(crate) struct ResponseWrapper<T> {
data: T,
headers: HeaderMap,
}
impl<T> ResponseWrapper<T> {
pub(crate) fn new(response: T) -> ResponseWrapper<T> {
ResponseWrapper {
data: response,
headers: Default::default(),
}
}
#[must_use]
pub(crate) fn with_header(
mut self,
name: impl IntoHeaderName,
value: impl Into<HeaderValue>,
) -> Self {
self.headers.insert(name, value.into());
self
}
}
#[derive(Debug, Clone)]
pub enum FormattedResponse<T> {
Json(Json<T>),
Yaml(Yaml<T>),
Bincode(Bincode<T>),
}
impl<T> FormattedResponse<T> {
pub fn into_inner(self) -> T {
match self {
FormattedResponse::Json(inner) => inner.0.data,
FormattedResponse::Yaml(inner) => inner.0.data,
FormattedResponse::Bincode(inner) => inner.0.data,
}
}
#[must_use]
pub fn with_header(
self,
name: impl IntoHeaderName,
value: impl Into<HeaderValue>,
) -> FormattedResponse<T> {
match self {
FormattedResponse::Json(inner) => {
FormattedResponse::Json(inner.with_header(name, value))
}
FormattedResponse::Yaml(inner) => {
FormattedResponse::Yaml(inner.with_header(name, value))
}
FormattedResponse::Bincode(inner) => {
FormattedResponse::Bincode(inner.with_header(name, value))
}
}
}
/// Set the `expires` header on the response to the provided expiration.
/// Internally it will perform conversions to make sure the value is set in GMT offset,
/// e.g. `Expires: Wed, 21 Oct 2015 07:28:00 GMT`
#[must_use]
pub fn with_expires_header(self, expiration: OffsetDateTime) -> FormattedResponse<T> {
// as per RFC-7234 (section 5.3) EXPIRES header has to use value formatted
// as defined in RFC-7231 (section 7.1.1.1)
// (preferred format (IMF-fixdate) uses RFC-5322 (section 3.3)
let formatted = format_rfc5352(expiration);
// SAFETY: our formatted datetime doesn't contain forbidden characters
#[allow(clippy::unwrap_used)]
self.with_header(header::EXPIRES, HeaderValue::try_from(formatted).unwrap())
}
/// Work similarly to `with_expires_header`, but rather than setting explicit expiration value,
/// it adds the provided time delta to the current time instead.
#[must_use]
pub fn with_expires_header_delta(self, expires_in: Duration) -> FormattedResponse<T> {
self.with_expires_header(OffsetDateTime::now_utc() + expires_in)
}
}
impl<T> IntoResponse for FormattedResponse<T>
where
T: Serialize,
{
fn into_response(self) -> Response {
match self {
FormattedResponse::Json(json_response) => json_response.into_response(),
FormattedResponse::Yaml(yaml_response) => yaml_response.into_response(),
FormattedResponse::Bincode(bincode_response) => bincode_response.into_response(),
}
}
}
#[derive(Default, Debug, Serialize, Deserialize, Copy, Clone)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[serde(rename_all = "lowercase")]
pub enum Output {
#[default]
Json,
Yaml,
Bincode,
}
#[derive(Default, Debug, Serialize, Deserialize, Copy, Clone)]
#[cfg_attr(feature = "utoipa", derive(utoipa::IntoParams, utoipa::ToSchema))]
#[serde(default)]
pub struct OutputParams {
pub output: Option<Output>,
}
impl Output {
pub fn to_response<T: Serialize>(self, data: T) -> FormattedResponse<T> {
match self {
Output::Json => FormattedResponse::Json(Json::from(data)),
Output::Yaml => FormattedResponse::Yaml(Yaml::from(data)),
Output::Bincode => FormattedResponse::Bincode(Bincode::from(data)),
}
}
}
pub(crate) fn error_response<E: ToString>(err: E) -> Response {
(
StatusCode::INTERNAL_SERVER_ERROR,
[(
header::CONTENT_TYPE,
HeaderValue::from_static(mime::TEXT_PLAIN_UTF_8.as_ref()),
)],
err.to_string(),
)
.into_response()
}
// SAFETY: this hardcoded datetime formatter is valid
#[allow(clippy::unwrap_used)]
fn format_rfc5352(datetime: OffsetDateTime) -> String {
// the time must be using GMT (UTC) offset
let normalised = datetime.to_offset(offset!(UTC));
normalised.format(&rfc5322()).unwrap()
}
// NOTE: this function is purposely not made public as it cannot guarantee caller
// has correctly ensured their date is using correct GMT offset
fn rfc5322() -> &'static [BorrowedFormatItem<'static>] {
// D, d M Y H:i:s T
format_description!(
"[weekday repr:short], [day] [month repr:short] [year] [hour]:[minute]:[second] GMT"
)
}
#[cfg(test)]
mod tests {
use crate::response::format_rfc5352;
use time::macros::datetime;
#[test]
fn rfc5322_formatting() {
let utc_date = datetime!(2021-08-23 12:13:14 UTC);
let non_utc_date = datetime!(2021-08-23 12:13:14 -1);
assert_eq!("Mon, 23 Aug 2021 12:13:14 GMT", format_rfc5352(utc_date));
assert_eq!(
"Mon, 23 Aug 2021 13:13:14 GMT",
format_rfc5352(non_utc_date)
);
}
}
@@ -1,47 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::response::{error_response, ResponseWrapper};
use axum::http::header::IntoHeaderName;
use axum::http::{header, HeaderValue};
use axum::response::{IntoResponse, Response};
use bytes::{BufMut, BytesMut};
use serde::Serialize;
#[derive(Debug, Clone, Default)]
#[must_use]
pub struct Yaml<T>(pub(crate) ResponseWrapper<T>);
impl<T> From<T> for Yaml<T> {
fn from(response: T) -> Self {
Yaml(ResponseWrapper::new(response).with_header(
header::CONTENT_TYPE,
HeaderValue::from_static("application/yaml"),
))
}
}
impl<T> Yaml<T> {
pub(crate) fn with_header(
mut self,
name: impl IntoHeaderName,
value: impl Into<HeaderValue>,
) -> Self {
self.0.headers.insert(name, value.into());
self
}
}
impl<T> IntoResponse for Yaml<T>
where
T: Serialize,
{
// replicates axum's Json
fn into_response(self) -> Response {
let mut buf = BytesMut::with_capacity(128).writer();
match serde_yaml::to_writer(&mut buf, &self.0.data) {
Ok(()) => (self.0.headers, buf.into_inner().freeze()).into_response(),
Err(err) => error_response(err),
}
}
}
@@ -179,7 +179,7 @@ fn _aggregate_indices_signatures<B>(
validate_shares: bool,
) -> Result<Vec<CoinIndexSignature>>
where
B: Borrow<PartialCoinIndexSignature> + Send + Sync,
B: Borrow<PartialCoinIndexSignature>,
{
// Check if all indices are unique
if signatures_shares
@@ -271,7 +271,7 @@ pub fn aggregate_indices_signatures<B>(
signatures_shares: &[CoinIndexSignatureShare<B>],
) -> Result<Vec<CoinIndexSignature>>
where
B: Borrow<PartialCoinIndexSignature> + Send + Sync,
B: Borrow<PartialCoinIndexSignature>,
{
_aggregate_indices_signatures(params, vk, signatures_shares, true)
}
@@ -38,7 +38,7 @@ impl From<AnnotatedExpirationDateSignature> for ExpirationDateSignature {
pub struct ExpirationDateSignatureShare<B = PartialExpirationDateSignature>
where
B: Borrow<PartialExpirationDateSignature> + Send + Sync,
B: Borrow<PartialExpirationDateSignature>,
{
pub index: SignerIndex,
pub key: VerificationKeyAuth,
@@ -205,7 +205,7 @@ fn _aggregate_expiration_signatures<B>(
validate_shares: bool,
) -> Result<Vec<ExpirationDateSignature>>
where
B: Borrow<ExpirationDateSignature> + Send + Sync,
B: Borrow<ExpirationDateSignature>,
{
// Check if all indices are unique
if signatures_shares
@@ -304,7 +304,7 @@ pub fn aggregate_expiration_signatures<B>(
signatures_shares: &[ExpirationDateSignatureShare<B>],
) -> Result<Vec<ExpirationDateSignature>>
where
B: Borrow<PartialExpirationDateSignature> + Send + Sync,
B: Borrow<PartialExpirationDateSignature>,
{
_aggregate_expiration_signatures(vk, expiration_date, signatures_shares, true)
}
@@ -9,6 +9,7 @@ use nym_sphinx_addressing::nodes::{
};
use nym_sphinx_params::packet_sizes::PacketSize;
use nym_sphinx_params::{PacketType, ReplySurbKeyDigestAlgorithm};
use nym_sphinx_types::constants::PAYLOAD_KEY_SEED_SIZE;
use nym_sphinx_types::{
NymPacket, SURBMaterial, SphinxError, HEADER_SIZE, NODE_ADDRESS_LENGTH, SURB,
X25519_WITH_EXPLICIT_PAYLOAD_KEYS_VERSION,
@@ -126,6 +127,12 @@ impl ReplySurb {
})
}
/// Returns the expected number of bytes the [`ReplySURB`] will take after serialization using the new encoding format.
/// Useful for deserialization from a bytes stream.
pub fn v2_serialised_len(num_hops: u8) -> usize {
Self::BASE_OVERHEAD + num_hops as usize * PAYLOAD_KEY_SEED_SIZE
}
pub fn encryption_key(&self) -> &SurbEncryptionKey {
&self.encryption_key
}
@@ -32,28 +32,24 @@ fn v2_reply_surbs_serialised_len(surbs: &[ReplySurb]) -> usize {
}
}
// when serialising surbs are always prepended with:
// - u16-encoded count,
// - u8-encoded number of hops
// - u8 reserved value
4 + num_surbs * v2_reply_surb_serialised_len(num_hops)
// when serialising surbs are always prepended with u16-encoded count an u8-encoded number of hops
3 + num_surbs * v2_reply_surb_serialised_len(num_hops)
}
// NUM_SURBS (u16) || HOPS (u8) || RESERVED (u8) || SURB_DATA
// NUM_SURBS (u16) || HOPS (u8) || SURB_DATA
fn recover_reply_surbs_v2(
bytes: &[u8],
) -> Result<(Vec<ReplySurb>, usize), InvalidReplyRequestError> {
if bytes.len() < 4 {
if bytes.len() < 2 {
return Err(InvalidReplyRequestError::RequestTooShortToDeserialize);
}
// we're not attaching more than 65k surbs...
let num_surbs = u16::from_be_bytes([bytes[0], bytes[1]]);
let num_hops = bytes[2];
let _reserved = bytes[3];
let mut consumed = 4;
let mut consumed = 3;
let surb_size = v2_reply_surb_serialised_len(num_hops);
let surb_size = ReplySurb::v2_serialised_len(num_hops);
if bytes[consumed..].len() < num_surbs as usize * surb_size {
return Err(InvalidReplyRequestError::RequestTooShortToDeserialize);
}
@@ -73,13 +69,11 @@ fn recover_reply_surbs_v2(
fn reply_surbs_bytes_v2(reply_surbs: &[ReplySurb]) -> impl Iterator<Item = u8> + use<'_> {
let num_surbs = reply_surbs.len() as u16;
let num_hops = reply_surbs_hops(reply_surbs);
let reserved = 0;
num_surbs
.to_be_bytes()
.into_iter()
.chain(once(num_hops))
.chain(once(reserved))
.chain(reply_surbs.iter().flat_map(|surb| surb.to_bytes()))
}
+8 -8
View File
@@ -26,54 +26,54 @@ pub enum ScraperError {
WebSocketConnectionFailure {
url: String,
#[source]
source: Box<tendermint_rpc::Error>,
source: tendermint_rpc::Error,
},
#[error("failed to establish rpc connection to {url}: {source}")]
HttpConnectionFailure {
url: String,
#[source]
source: Box<tendermint_rpc::Error>,
source: tendermint_rpc::Error,
},
#[error("failed to create chain subscription: {source}")]
ChainSubscriptionFailure {
#[source]
source: Box<tendermint_rpc::Error>,
source: tendermint_rpc::Error,
},
#[error("could not obtain basic block information at height: {height}: {source}")]
BlockQueryFailure {
height: u32,
#[source]
source: Box<tendermint_rpc::Error>,
source: tendermint_rpc::Error,
},
#[error("could not obtain block results information at height: {height}: {source}")]
BlockResultsQueryFailure {
height: u32,
#[source]
source: Box<tendermint_rpc::Error>,
source: tendermint_rpc::Error,
},
#[error("could not obtain validators information at height: {height}: {source}")]
ValidatorsQueryFailure {
height: u32,
#[source]
source: Box<tendermint_rpc::Error>,
source: tendermint_rpc::Error,
},
#[error("could not obtain tx results for tx: {hash}: {source}")]
TxResultsQueryFailure {
hash: Hash,
#[source]
source: Box<tendermint_rpc::Error>,
source: tendermint_rpc::Error,
},
#[error("could not obtain current abci info: {source}")]
AbciInfoQueryFailure {
#[source]
source: Box<tendermint_rpc::Error>,
source: tendermint_rpc::Error,
},
#[error("could not parse tx {hash}: {source}")]
+18 -30
View File
@@ -29,7 +29,7 @@ impl RpcClient {
let http_client = HttpClient::new(url.as_str()).map_err(|source| {
ScraperError::HttpConnectionFailure {
url: url.to_string(),
source: Box::new(source),
source,
}
})?;
@@ -90,10 +90,7 @@ impl RpcClient {
self.inner
.block(height)
.await
.map_err(|source| ScraperError::BlockQueryFailure {
height,
source: Box::new(source),
})
.map_err(|source| ScraperError::BlockQueryFailure { height, source })
}
#[instrument(skip(self), err(Display))]
@@ -103,37 +100,31 @@ impl RpcClient {
) -> Result<block_results::Response, ScraperError> {
debug!("getting block results");
self.inner.block_results(height).await.map_err(|source| {
ScraperError::BlockResultsQueryFailure {
height,
source: Box::new(source),
}
})
self.inner
.block_results(height)
.await
.map_err(|source| ScraperError::BlockResultsQueryFailure { height, source })
}
pub(crate) async fn current_block_height(&self) -> Result<u64, ScraperError> {
debug!("getting current block height");
let info =
self.inner
.abci_info()
.await
.map_err(|source| ScraperError::AbciInfoQueryFailure {
source: Box::new(source),
})?;
let info = self
.inner
.abci_info()
.await
.map_err(|source| ScraperError::AbciInfoQueryFailure { source })?;
Ok(info.last_block_height.value())
}
pub(crate) async fn earliest_available_block_height(&self) -> Result<u64, ScraperError> {
debug!("getting earliest available block height");
let status =
self.inner
.status()
.await
.map_err(|source| ScraperError::AbciInfoQueryFailure {
source: Box::new(source),
})?;
let status = self
.inner
.status()
.await
.map_err(|source| ScraperError::AbciInfoQueryFailure { source })?;
Ok(status.sync_info.earliest_block_height.value())
}
@@ -176,7 +167,7 @@ impl RpcClient {
.await
.map_err(|source| ScraperError::TxResultsQueryFailure {
hash: tx_hash,
source: Box::new(source),
source,
})
}
@@ -190,9 +181,6 @@ impl RpcClient {
self.inner
.validators(height, Paging::All)
.await
.map_err(|source| ScraperError::ValidatorsQueryFailure {
height,
source: Box::new(source),
})
.map_err(|source| ScraperError::ValidatorsQueryFailure { height, source })
}
}
@@ -42,7 +42,7 @@ impl ChainSubscriber {
let websocket_url = websocket_endpoint.as_str().try_into().map_err(|source| {
ScraperError::WebSocketConnectionFailure {
url: websocket_endpoint.to_string(),
source: Box::new(source),
source,
}
})?;
@@ -52,7 +52,7 @@ impl ChainSubscriber {
.await
.map_err(|source| ScraperError::WebSocketConnectionFailure {
url: websocket_endpoint.to_string(),
source: Box::new(source),
source,
})?;
Ok(ChainSubscriber {
@@ -83,7 +83,7 @@ impl ChainSubscriber {
.await
.map_err(|source| ScraperError::WebSocketConnectionFailure {
url: self.websocket_endpoint.to_string(),
source: Box::new(source),
source,
})?;
self.websocket_client = client;
self.websocket_driver = Some(driver);
@@ -121,9 +121,7 @@ impl ChainSubscriber {
.websocket_client
.subscribe(EventType::NewBlock.into())
.await
.map_err(|source| ScraperError::ChainSubscriptionFailure {
source: Box::new(source),
})?;
.map_err(|source| ScraperError::ChainSubscriptionFailure { source })?;
let mut failures = 0;
+9 -6
View File
@@ -54,11 +54,14 @@ where
let key_pem = read_pem_file(path)?;
if T::pem_type() != key_pem.tag {
return Err(io::Error::other(format!(
"unexpected key pem tag. Got '{}', expected: '{}'",
key_pem.tag,
T::pem_type()
)));
return Err(io::Error::new(
io::ErrorKind::Other,
format!(
"unexpected key pem tag. Got '{}', expected: '{}'",
key_pem.tag,
T::pem_type()
),
));
}
let key = match T::from_bytes(&key_pem.contents) {
@@ -81,7 +84,7 @@ fn read_pem_file<P: AsRef<Path>>(filepath: P) -> io::Result<Pem> {
let mut pem_bytes = File::open(filepath)?;
let mut buf = Vec::new();
pem_bytes.read_to_end(&mut buf)?;
pem::parse(&buf).map_err(io::Error::other)
pem::parse(&buf).map_err(|e| io::Error::new(io::ErrorKind::Other, e))
}
fn write_pem_file<P: AsRef<Path>>(filepath: P, data: Vec<u8>, tag: &str) -> io::Result<()> {
@@ -13,7 +13,6 @@ use std::str::FromStr;
pub mod old_config_v1_1_20_2;
pub mod old_config_v1_1_30;
pub mod old_config_v1_1_33;
pub mod old_config_v1_1_54;
pub use nym_service_providers_common::interface::ProviderInterfaceVersion;
pub use nym_socks5_requests::Socks5ProtocolVersion;
@@ -1,8 +1,7 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::old_config_v1_1_54::ConfigV1_1_54;
use super::{Socks5, Socks5Debug};
use super::{Config, Socks5, Socks5Debug};
pub use nym_client_core::config::old_config_v1_1_33::ConfigV1_1_33 as BaseClientConfigV1_1_33;
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
@@ -24,9 +23,9 @@ pub struct ConfigV1_1_33 {
pub socks5: Socks5V1_1_33,
}
impl From<ConfigV1_1_33> for ConfigV1_1_54 {
impl From<ConfigV1_1_33> for Config {
fn from(value: ConfigV1_1_33) -> Self {
ConfigV1_1_54 {
Config {
base: value.base.into(),
socks5: value.socks5.into(),
}
@@ -1,23 +0,0 @@
use super::Config;
pub use nym_client_core::config::old_config_v1_1_54::ConfigV1_1_54 as BaseClientConfigV1_1_54;
use serde::{Deserialize, Serialize};
use super::Socks5;
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_54 {
#[serde(flatten)]
pub base: BaseClientConfigV1_1_54,
pub socks5: Socks5,
}
impl From<ConfigV1_1_54> for Config {
fn from(value: ConfigV1_1_54) -> Self {
Config {
base: value.base.into(),
socks5: value.socks5,
}
}
}
-7
View File
@@ -20,7 +20,6 @@ thiserror = { workspace = true }
time = { workspace = true }
tokio = { workspace = true }
si-scale = { workspace = true }
strum = { workspace = true }
nym-crypto = { path = "../crypto" }
nym-sphinx = { path = "../nymsphinx" }
@@ -28,11 +27,5 @@ nym-credentials-interface = { path = "../credentials-interface" }
nym-metrics = { path = "../nym-metrics" }
nym-task = { path = "../task" }
utoipa = { workspace = true, optional = true }
[target."cfg(target_arch = \"wasm32\")".dependencies.wasmtimer]
workspace = true
[features]
default = []
openapi = ["dep:utoipa"]
+1 -1
View File
@@ -1,7 +1,7 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::report::client::{ClientStatsReport, OsInformation};
use crate::report::{ClientStatsReport, OsInformation};
use nym_task::TaskClient;
use time::{OffsetDateTime, Time};
+19 -9
View File
@@ -1,11 +1,10 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_credentials_interface::TicketType;
use nym_sphinx::DestinationAddressBytes;
use time::OffsetDateTime;
use crate::types::SessionType;
/// Channel for receiving incoming Stats events
pub type GatewayStatsReceiver = tokio::sync::mpsc::UnboundedReceiver<GatewayStatsEvent>;
@@ -52,9 +51,15 @@ pub enum GatewaySessionEvent {
/// Address of the remote client opening the connection
client: DestinationAddressBytes,
},
/// An active session should be given a type and remembered
SessionRemember {
session_type: SessionType,
/// A new ecash ticket has been added / requested
EcashTicket {
/// Type of ecash ticket that has been created as part of the session
ticket_type: TicketType,
/// Address of the remote client opening the connection
client: DestinationAddressBytes,
},
SessionDelete {
/// Address of the remote client opening the connection
client: DestinationAddressBytes,
},
}
@@ -76,13 +81,18 @@ impl GatewaySessionEvent {
}
}
pub fn new_session_remember(
session_type: SessionType,
/// A new ecash ticket has been added / requested
pub fn new_ecash_ticket(
client: DestinationAddressBytes,
ticket_type: TicketType,
) -> GatewaySessionEvent {
GatewaySessionEvent::SessionRemember {
session_type,
GatewaySessionEvent::EcashTicket {
ticket_type,
client,
}
}
pub fn new_session_delete(client: DestinationAddressBytes) -> GatewaySessionEvent {
GatewaySessionEvent::SessionDelete { client }
}
}
-7
View File
@@ -22,20 +22,13 @@ pub mod error;
pub mod gateways;
/// Statistics reporting abstractions and implementations.
pub mod report;
/// Statistics related types.
pub mod types;
const CLIENT_ID_PREFIX: &str = "client_stats_id";
const VPN_CLIENT_ID_PREFIX: &str = "vpnclient_stats_id";
pub fn generate_client_stats_id(id_key: ed25519::PublicKey) -> String {
generate_stats_id(CLIENT_ID_PREFIX, id_key.to_base58_string())
}
pub fn generate_vpn_client_stats_id<M: AsRef<[u8]>>(seed: M) -> String {
generate_stats_id(VPN_CLIENT_ID_PREFIX, seed)
}
fn generate_stats_id<M: AsRef<[u8]>>(prefix: &str, id_seed: M) -> String {
let mut hasher = sha2::Sha256::new();
hasher.update(prefix);
@@ -6,7 +6,7 @@ use crate::clients::{
nym_api_statistics::NymApiStats, packet_statistics::PacketStatistics,
};
use crate::error::StatsError;
use super::error::StatsError;
use serde::{Deserialize, Serialize};
use sysinfo::System;

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