Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dc72c1eadd | |||
| acb7be7ea2 | |||
| ab0320733e | |||
| 01ebf3677d | |||
| e680e8dc49 | |||
| 242bc93807 | |||
| 94c6cdc7b2 | |||
| fce322c789 | |||
| ac5baab693 | |||
| 23da0f4d8e | |||
| 25e3b4cd83 | |||
| 8e4d72a565 | |||
| ad84a6d85d | |||
| 34c5f23684 | |||
| 000f2f1c29 | |||
| 317f7fffa9 | |||
| 4396def133 | |||
| a56a318a7f | |||
| 4d08047c57 | |||
| cb13be27f8 | |||
| fa392169c1 | |||
| 3167fb34e6 | |||
| 9ca6301e1c | |||
| e16a73338e | |||
| bfa3825d70 | |||
| d626e7689f | |||
| 9234474565 | |||
| 29f8386b50 | |||
| 0edb9631a6 | |||
| 4b0153f5f2 | |||
| c09a17b66d | |||
| d18ddcdc11 | |||
| d2df542280 | |||
| 6fafd8c03a | |||
| 38e66f6ddf | |||
| b9fbe0b8f3 | |||
| daafb5cae4 |
@@ -1,45 +0,0 @@
|
||||
name: ci-nym-credential-proxy
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'common/**'
|
||||
- 'nym-credential-proxy/**'
|
||||
- '.github/workspace/ci-nym-credential-proxy.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: arc-ubuntu-22.04
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
MANIFEST_PATH: "--manifest-path nym-credential-proxy/Cargo.toml"
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: ${{ env.MANIFEST_PATH }} --all -- --check
|
||||
|
||||
- name: Build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: ${{ env.MANIFEST_PATH }} --workspace --all-targets
|
||||
|
||||
- name: Clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: ${{ env.MANIFEST_PATH }} --workspace --all-targets -- -D warnings
|
||||
Generated
+403
-8
@@ -493,6 +493,17 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-client-ip"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9eefda7e2b27e1bda4d6fa8a06b50803b8793769045918bc37ad062d48a6efac"
|
||||
dependencies = [
|
||||
"axum 0.7.7",
|
||||
"forwarded-header-value",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-core"
|
||||
version = "0.3.4"
|
||||
@@ -2429,7 +2440,7 @@ dependencies = [
|
||||
"maxminddb",
|
||||
"nym-bin-common 0.6.0",
|
||||
"nym-contracts-common 0.5.0",
|
||||
"nym-explorer-api-requests",
|
||||
"nym-explorer-api-requests 0.1.0",
|
||||
"nym-mixnet-contract-common 0.6.0",
|
||||
"nym-network-defaults 0.1.0",
|
||||
"nym-task",
|
||||
@@ -2638,6 +2649,16 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e"
|
||||
|
||||
[[package]]
|
||||
name = "forwarded-header-value"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8835f84f38484cc86f110a805655697908257fb9a7af005234060891557198e9"
|
||||
dependencies = [
|
||||
"nonempty",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fs-err"
|
||||
version = "2.11.0"
|
||||
@@ -4275,6 +4296,12 @@ dependencies = [
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nonempty"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7"
|
||||
|
||||
[[package]]
|
||||
name = "notify"
|
||||
version = "5.2.0"
|
||||
@@ -4559,6 +4586,34 @@ dependencies = [
|
||||
"utoipa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-api-requests"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/nymtech/nym?branch=release/2024.13-magura#317f7fffa986dff856e5b980b15872620bc052f0"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"cosmrs 0.17.0-pre",
|
||||
"cosmwasm-std",
|
||||
"ecdsa",
|
||||
"getset",
|
||||
"nym-compact-ecash 0.1.0 (git+https://github.com/nymtech/nym?branch=release/2024.13-magura)",
|
||||
"nym-credentials-interface 0.1.0 (git+https://github.com/nymtech/nym?branch=release/2024.13-magura)",
|
||||
"nym-crypto 0.4.0 (git+https://github.com/nymtech/nym?branch=release/2024.13-magura)",
|
||||
"nym-ecash-time 0.1.0 (git+https://github.com/nymtech/nym?branch=release/2024.13-magura)",
|
||||
"nym-mixnet-contract-common 0.6.0 (git+https://github.com/nymtech/nym?branch=release/2024.13-magura)",
|
||||
"nym-network-defaults 0.1.0 (git+https://github.com/nymtech/nym?branch=release/2024.13-magura)",
|
||||
"nym-node-requests 0.1.0 (git+https://github.com/nymtech/nym?branch=release/2024.13-magura)",
|
||||
"nym-serde-helpers 0.1.0 (git+https://github.com/nymtech/nym?branch=release/2024.13-magura)",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.10.8",
|
||||
"tendermint 0.37.0",
|
||||
"thiserror",
|
||||
"time",
|
||||
"utoipa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-async-file-watcher"
|
||||
version = "0.1.0"
|
||||
@@ -4689,6 +4744,21 @@ dependencies = [
|
||||
"vergen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-bin-common"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/nymtech/nym?branch=release/2024.13-magura#317f7fffa986dff856e5b980b15872620bc052f0"
|
||||
dependencies = [
|
||||
"const-str",
|
||||
"log",
|
||||
"pretty_env_logger",
|
||||
"schemars",
|
||||
"semver 1.0.23",
|
||||
"serde",
|
||||
"utoipa",
|
||||
"vergen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-bity-integration"
|
||||
version = "0.1.0"
|
||||
@@ -4849,7 +4919,7 @@ dependencies = [
|
||||
"nym-credentials-interface 0.1.0",
|
||||
"nym-crypto 0.4.0",
|
||||
"nym-ecash-time 0.1.0",
|
||||
"nym-explorer-client",
|
||||
"nym-explorer-client 0.1.0",
|
||||
"nym-gateway-client",
|
||||
"nym-gateway-requests",
|
||||
"nym-id",
|
||||
@@ -5088,6 +5158,29 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-compact-ecash"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/nymtech/nym?branch=release/2024.13-magura#317f7fffa986dff856e5b980b15872620bc052f0"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"bls12_381",
|
||||
"bs58",
|
||||
"cfg-if",
|
||||
"digest 0.9.0",
|
||||
"ff",
|
||||
"group",
|
||||
"itertools 0.13.0",
|
||||
"nym-network-defaults 0.1.0 (git+https://github.com/nymtech/nym?branch=release/2024.13-magura)",
|
||||
"nym-pemstore 0.3.0 (git+https://github.com/nymtech/nym?branch=release/2024.13-magura)",
|
||||
"rand",
|
||||
"serde",
|
||||
"sha2 0.9.9",
|
||||
"subtle 2.5.0",
|
||||
"thiserror",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-config"
|
||||
version = "0.1.0"
|
||||
@@ -5115,6 +5208,20 @@ dependencies = [
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-config"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/nymtech/nym?branch=release/2024.13-magura#317f7fffa986dff856e5b980b15872620bc052f0"
|
||||
dependencies = [
|
||||
"dirs",
|
||||
"handlebars",
|
||||
"log",
|
||||
"nym-network-defaults 0.1.0 (git+https://github.com/nymtech/nym?branch=release/2024.13-magura)",
|
||||
"serde",
|
||||
"toml 0.8.14",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-contracts-common"
|
||||
version = "0.5.0"
|
||||
@@ -5145,6 +5252,21 @@ dependencies = [
|
||||
"vergen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-contracts-common"
|
||||
version = "0.5.0"
|
||||
source = "git+https://github.com/nymtech/nym?branch=release/2024.13-magura#317f7fffa986dff856e5b980b15872620bc052f0"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"vergen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-country-group"
|
||||
version = "0.1.0"
|
||||
@@ -5167,6 +5289,75 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-credential-proxy"
|
||||
version = "0.1.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"axum 0.7.7",
|
||||
"bip39",
|
||||
"bs58",
|
||||
"cfg-if",
|
||||
"clap 4.5.20",
|
||||
"colored",
|
||||
"dotenvy",
|
||||
"futures",
|
||||
"humantime 2.1.0",
|
||||
"nym-bin-common 0.6.0",
|
||||
"nym-compact-ecash 0.1.0",
|
||||
"nym-config 0.1.0",
|
||||
"nym-credential-proxy-requests",
|
||||
"nym-credentials",
|
||||
"nym-credentials-interface 0.1.0",
|
||||
"nym-crypto 0.4.0",
|
||||
"nym-http-api-common",
|
||||
"nym-network-defaults 0.1.0",
|
||||
"nym-validator-client 0.1.0",
|
||||
"rand",
|
||||
"reqwest 0.12.4",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"strum 0.26.3",
|
||||
"strum_macros 0.26.4",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"time",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tower 0.4.13",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
"url",
|
||||
"utoipa",
|
||||
"utoipa-swagger-ui",
|
||||
"uuid",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-credential-proxy-requests"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"nym-credentials",
|
||||
"nym-credentials-interface 0.1.0",
|
||||
"nym-http-api-client 0.1.0",
|
||||
"nym-http-api-common",
|
||||
"nym-serde-helpers 0.1.0",
|
||||
"reqwest 0.12.4",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"time",
|
||||
"tsify",
|
||||
"utoipa",
|
||||
"uuid",
|
||||
"wasm-bindgen",
|
||||
"wasmtimer",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-credential-storage"
|
||||
version = "0.1.0"
|
||||
@@ -5281,6 +5472,22 @@ dependencies = [
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-credentials-interface"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/nymtech/nym?branch=release/2024.13-magura#317f7fffa986dff856e5b980b15872620bc052f0"
|
||||
dependencies = [
|
||||
"bls12_381",
|
||||
"nym-compact-ecash 0.1.0 (git+https://github.com/nymtech/nym?branch=release/2024.13-magura)",
|
||||
"nym-ecash-time 0.1.0 (git+https://github.com/nymtech/nym?branch=release/2024.13-magura)",
|
||||
"nym-network-defaults 0.1.0 (git+https://github.com/nymtech/nym?branch=release/2024.13-magura)",
|
||||
"rand",
|
||||
"serde",
|
||||
"strum 0.26.3",
|
||||
"thiserror",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-crypto"
|
||||
version = "0.4.0"
|
||||
@@ -5326,6 +5533,23 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-crypto"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/nymtech/nym?branch=release/2024.13-magura#317f7fffa986dff856e5b980b15872620bc052f0"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"ed25519-dalek",
|
||||
"nym-pemstore 0.3.0 (git+https://github.com/nymtech/nym?branch=release/2024.13-magura)",
|
||||
"nym-sphinx-types 0.2.0 (git+https://github.com/nymtech/nym?branch=release/2024.13-magura)",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
"subtle-encoding",
|
||||
"thiserror",
|
||||
"x25519-dalek",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-data-observatory"
|
||||
version = "0.1.0"
|
||||
@@ -5427,6 +5651,14 @@ dependencies = [
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-ecash-time"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/nymtech/nym?branch=release/2024.13-magura#317f7fffa986dff856e5b980b15872620bc052f0"
|
||||
dependencies = [
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-execute"
|
||||
version = "0.1.0"
|
||||
@@ -5459,6 +5691,18 @@ dependencies = [
|
||||
"utoipa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-exit-policy"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/nymtech/nym?branch=release/2024.13-magura#317f7fffa986dff856e5b980b15872620bc052f0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
"utoipa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-explorer-api-requests"
|
||||
version = "0.1.0"
|
||||
@@ -5471,11 +5715,23 @@ dependencies = [
|
||||
"ts-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-explorer-api-requests"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/nymtech/nym?branch=release/2024.13-magura#317f7fffa986dff856e5b980b15872620bc052f0"
|
||||
dependencies = [
|
||||
"nym-api-requests 0.1.0 (git+https://github.com/nymtech/nym?branch=release/2024.13-magura)",
|
||||
"nym-contracts-common 0.5.0 (git+https://github.com/nymtech/nym?branch=release/2024.13-magura)",
|
||||
"nym-mixnet-contract-common 0.6.0 (git+https://github.com/nymtech/nym?branch=release/2024.13-magura)",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-explorer-client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"nym-explorer-api-requests",
|
||||
"nym-explorer-api-requests 0.1.0",
|
||||
"reqwest 0.12.4",
|
||||
"serde",
|
||||
"thiserror",
|
||||
@@ -5484,6 +5740,19 @@ dependencies = [
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-explorer-client"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/nymtech/nym?branch=release/2024.13-magura#317f7fffa986dff856e5b980b15872620bc052f0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"nym-explorer-api-requests 0.1.0 (git+https://github.com/nymtech/nym?branch=release/2024.13-magura)",
|
||||
"reqwest 0.12.4",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-ffi-shared"
|
||||
version = "0.2.0"
|
||||
@@ -5534,9 +5803,11 @@ dependencies = [
|
||||
"nym-network-requester",
|
||||
"nym-node-http-api",
|
||||
"nym-pemstore 0.3.0",
|
||||
"nym-sdk",
|
||||
"nym-sphinx",
|
||||
"nym-statistics-common",
|
||||
"nym-task",
|
||||
"nym-topology",
|
||||
"nym-types",
|
||||
"nym-validator-client 0.1.0",
|
||||
"nym-wireguard",
|
||||
@@ -5729,6 +6000,7 @@ name = "nym-http-api-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"axum 0.7.7",
|
||||
"axum-client-ip",
|
||||
"bytes",
|
||||
"colored",
|
||||
"mime",
|
||||
@@ -5910,6 +6182,27 @@ dependencies = [
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-mixnet-contract-common"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/nymtech/nym?branch=release/2024.13-magura#317f7fffa986dff856e5b980b15872620bc052f0"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-controllers",
|
||||
"cw-storage-plus",
|
||||
"humantime-serde",
|
||||
"log",
|
||||
"nym-contracts-common 0.5.0 (git+https://github.com/nymtech/nym?branch=release/2024.13-magura)",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde-json-wasm",
|
||||
"serde_repr",
|
||||
"thiserror",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-mixnode"
|
||||
version = "1.1.37"
|
||||
@@ -6041,6 +6334,19 @@ dependencies = [
|
||||
"utoipa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-defaults"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/nymtech/nym?branch=release/2024.13-magura#317f7fffa986dff856e5b980b15872620bc052f0"
|
||||
dependencies = [
|
||||
"dotenvy",
|
||||
"log",
|
||||
"schemars",
|
||||
"serde",
|
||||
"url",
|
||||
"utoipa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-monitor"
|
||||
version = "0.1.0"
|
||||
@@ -6247,8 +6553,29 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-node-status-agent"
|
||||
name = "nym-node-requests"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/nymtech/nym?branch=release/2024.13-magura#317f7fffa986dff856e5b980b15872620bc052f0"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"celes",
|
||||
"humantime 2.1.0",
|
||||
"humantime-serde",
|
||||
"nym-bin-common 0.6.0 (git+https://github.com/nymtech/nym?branch=release/2024.13-magura)",
|
||||
"nym-crypto 0.4.0 (git+https://github.com/nymtech/nym?branch=release/2024.13-magura)",
|
||||
"nym-exit-policy 0.1.0 (git+https://github.com/nymtech/nym?branch=release/2024.13-magura)",
|
||||
"nym-wireguard-types 0.1.0 (git+https://github.com/nymtech/nym?branch=release/2024.13-magura)",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"time",
|
||||
"utoipa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-node-status-agent"
|
||||
version = "0.1.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.5.20",
|
||||
@@ -6264,7 +6591,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-node-status-api"
|
||||
version = "0.1.0"
|
||||
version = "0.1.4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.7",
|
||||
@@ -6276,7 +6603,7 @@ dependencies = [
|
||||
"moka",
|
||||
"nym-bin-common 0.6.0",
|
||||
"nym-common-models",
|
||||
"nym-explorer-client",
|
||||
"nym-explorer-client 0.1.0 (git+https://github.com/nymtech/nym?branch=release/2024.13-magura)",
|
||||
"nym-network-defaults 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)",
|
||||
"nym-node-requests 0.1.0 (git+https://github.com/nymtech/nym?branch=pre-dir-v2-fork)",
|
||||
"nym-task",
|
||||
@@ -6409,6 +6736,14 @@ dependencies = [
|
||||
"pem",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-pemstore"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/nymtech/nym?branch=release/2024.13-magura#317f7fffa986dff856e5b980b15872620bc052f0"
|
||||
dependencies = [
|
||||
"pem",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-sdk"
|
||||
version = "0.1.0"
|
||||
@@ -6485,6 +6820,17 @@ dependencies = [
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-serde-helpers"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/nymtech/nym?branch=release/2024.13-magura#317f7fffa986dff856e5b980b15872620bc052f0"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bs58",
|
||||
"serde",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-service-provider-requests-common"
|
||||
version = "0.1.0"
|
||||
@@ -6798,6 +7144,15 @@ dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-sphinx-types"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/nymtech/nym?branch=release/2024.13-magura#317f7fffa986dff856e5b980b15872620bc052f0"
|
||||
dependencies = [
|
||||
"sphinx-packet",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-statistics-common"
|
||||
version = "0.1.0"
|
||||
@@ -7066,6 +7421,31 @@ dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-vpn-api-lib-wasm"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"getrandom",
|
||||
"js-sys",
|
||||
"nym-bin-common 0.6.0",
|
||||
"nym-compact-ecash 0.1.0",
|
||||
"nym-credential-proxy-requests",
|
||||
"nym-credentials",
|
||||
"nym-credentials-interface 0.1.0",
|
||||
"nym-crypto 0.4.0",
|
||||
"nym-ecash-time 0.1.0",
|
||||
"serde",
|
||||
"serde-wasm-bindgen 0.6.5",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"time",
|
||||
"tsify",
|
||||
"wasm-bindgen",
|
||||
"wasm-utils",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-wallet-types"
|
||||
version = "1.0.0"
|
||||
@@ -7139,6 +7519,20 @@ dependencies = [
|
||||
"x25519-dalek",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-wireguard-types"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/nymtech/nym?branch=release/2024.13-magura#317f7fffa986dff856e5b980b15872620bc052f0"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"log",
|
||||
"nym-config 0.1.0 (git+https://github.com/nymtech/nym?branch=release/2024.13-magura)",
|
||||
"nym-network-defaults 0.1.0 (git+https://github.com/nymtech/nym?branch=release/2024.13-magura)",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"x25519-dalek",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nymvisor"
|
||||
version = "0.1.8"
|
||||
@@ -7216,9 +7610,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.19.0"
|
||||
version = "1.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "oneshot-uniffi"
|
||||
@@ -8694,6 +9088,7 @@ dependencies = [
|
||||
"schemars_derive",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
+21
-20
@@ -19,33 +19,33 @@ members = [
|
||||
"clients/native",
|
||||
"clients/native/websocket-requests",
|
||||
"clients/socks5",
|
||||
"common/authenticator-requests",
|
||||
"common/async-file-watcher",
|
||||
"common/authenticator-requests",
|
||||
"common/bandwidth-controller",
|
||||
"common/bin-common",
|
||||
"common/client-core",
|
||||
"common/client-core/config-types",
|
||||
"common/client-core/surb-storage",
|
||||
"common/client-core/gateways-storage",
|
||||
"common/client-core/surb-storage",
|
||||
"common/client-libs/gateway-client",
|
||||
"common/client-libs/mixnet-client",
|
||||
"common/client-libs/validator-client",
|
||||
"common/commands",
|
||||
"common/config",
|
||||
"common/cosmwasm-smart-contracts/coconut-bandwidth-contract",
|
||||
"common/cosmwasm-smart-contracts/ecash-contract",
|
||||
"common/cosmwasm-smart-contracts/coconut-dkg",
|
||||
"common/cosmwasm-smart-contracts/contracts-common",
|
||||
"common/cosmwasm-smart-contracts/ecash-contract",
|
||||
"common/cosmwasm-smart-contracts/group-contract",
|
||||
"common/cosmwasm-smart-contracts/mixnet-contract",
|
||||
"common/cosmwasm-smart-contracts/multisig-contract",
|
||||
"common/cosmwasm-smart-contracts/vesting-contract",
|
||||
"common/country-group",
|
||||
"common/credential-storage",
|
||||
"common/credentials",
|
||||
"common/credential-utils",
|
||||
"common/credentials-interface",
|
||||
"common/credential-verification",
|
||||
"common/credentials",
|
||||
"common/credentials-interface",
|
||||
"common/crypto",
|
||||
"common/dkg",
|
||||
"common/ecash-double-spending",
|
||||
@@ -65,10 +65,10 @@ members = [
|
||||
"common/network-defaults",
|
||||
"common/node-tester-utils",
|
||||
"common/nonexhaustive-delayqueue",
|
||||
"common/nymcoconut",
|
||||
"common/nym_offline_compact_ecash",
|
||||
"common/nym-id",
|
||||
"common/nym-metrics",
|
||||
"common/nym_offline_compact_ecash",
|
||||
"common/nymcoconut",
|
||||
"common/nymsphinx",
|
||||
"common/nymsphinx/acknowledgements",
|
||||
"common/nymsphinx/addressing",
|
||||
@@ -104,20 +104,23 @@ members = [
|
||||
"gateway",
|
||||
"integrations/bity",
|
||||
"mixnode",
|
||||
"sdk/ffi/cpp",
|
||||
"sdk/ffi/go",
|
||||
"sdk/ffi/shared",
|
||||
"sdk/lib/socks5-listener",
|
||||
"sdk/rust/nym-sdk",
|
||||
"sdk/ffi/shared",
|
||||
"sdk/ffi/go",
|
||||
"sdk/ffi/cpp",
|
||||
"service-providers/authenticator",
|
||||
"service-providers/common",
|
||||
"service-providers/ip-packet-router",
|
||||
"service-providers/network-requester",
|
||||
"nym-network-monitor",
|
||||
"nym-api",
|
||||
"nym-browser-extension/storage",
|
||||
"nym-api/nym-api-requests",
|
||||
"nym-browser-extension/storage",
|
||||
"nym-credential-proxy/nym-credential-proxy",
|
||||
"nym-credential-proxy/nym-credential-proxy-requests",
|
||||
"nym-credential-proxy/vpn-api-lib-wasm",
|
||||
"nym-data-observatory",
|
||||
"nym-network-monitor",
|
||||
"nym-node",
|
||||
"nym-node/nym-node-http-api",
|
||||
"nym-node/nym-node-requests",
|
||||
@@ -140,11 +143,11 @@ members = [
|
||||
"wasm/mix-fetch",
|
||||
"wasm/node-tester",
|
||||
"wasm/zknym-lib",
|
||||
"tools/internal/testnet-manager",
|
||||
"tools/internal/testnet-manager/dkg-bypass-contract",
|
||||
"tools/echo-server",
|
||||
"tools/internal/contract-state-importer/importer-cli",
|
||||
"tools/internal/contract-state-importer/importer-contract",
|
||||
"tools/internal/testnet-manager",
|
||||
"tools/internal/testnet-manager/dkg-bypass-contract",
|
||||
]
|
||||
|
||||
default-members = [
|
||||
@@ -155,6 +158,7 @@ default-members = [
|
||||
"gateway",
|
||||
"mixnode",
|
||||
"nym-api",
|
||||
"nym-credential-proxy/nym-credential-proxy",
|
||||
"nym-data-observatory",
|
||||
"nym-node",
|
||||
"nym-node-status-api",
|
||||
@@ -193,16 +197,14 @@ aead = "0.5.2"
|
||||
anyhow = "1.0.90"
|
||||
argon2 = "0.5.0"
|
||||
async-trait = "0.1.83"
|
||||
axum-client-ip = "0.6.1"
|
||||
axum = "0.7.5"
|
||||
axum-extra = "0.9.4"
|
||||
base64 = "0.22.1"
|
||||
bincode = "1.3.3"
|
||||
bip39 = { version = "2.0.0", features = ["zeroize"] }
|
||||
|
||||
# can we unify those?
|
||||
bit-vec = "0.7.0"
|
||||
bit-vec = "0.7.0" # can we unify those?
|
||||
bitvec = "1.0.0"
|
||||
|
||||
blake3 = "1.5.4"
|
||||
bloomfilter = "1.0.14"
|
||||
bs58 = "0.5.1"
|
||||
@@ -279,7 +281,7 @@ moka = { version = "0.12", features = ["future"] }
|
||||
nix = "0.27.1"
|
||||
notify = "5.1.0"
|
||||
okapi = "0.7.0"
|
||||
once_cell = "1.7.2"
|
||||
once_cell = "1.20.2"
|
||||
opentelemetry = "0.19.0"
|
||||
opentelemetry-jaeger = "0.18.0"
|
||||
parking_lot = "0.12.3"
|
||||
@@ -407,7 +409,6 @@ wasm-bindgen-futures = "0.4.45"
|
||||
wasmtimer = "0.2.0"
|
||||
web-sys = "0.3.72"
|
||||
|
||||
|
||||
# Profile settings for individual crates
|
||||
|
||||
# Compile-time verified queries do quite a bit of work at compile time. Incremental
|
||||
|
||||
@@ -6,7 +6,6 @@ pub(crate) use accessor::{TopologyAccessor, TopologyReadPermit};
|
||||
use futures::StreamExt;
|
||||
use log::*;
|
||||
use nym_sphinx::addressing::nodes::NodeIdentity;
|
||||
use nym_topology::provider_trait::TopologyProvider;
|
||||
use nym_topology::NymTopologyError;
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -18,7 +17,11 @@ use wasmtimer::tokio::sleep;
|
||||
|
||||
mod accessor;
|
||||
pub mod geo_aware_provider;
|
||||
pub(crate) mod nym_api_provider;
|
||||
pub mod nym_api_provider;
|
||||
|
||||
pub use geo_aware_provider::GeoAwareTopologyProvider;
|
||||
pub use nym_api_provider::{Config as NymApiTopologyProviderConfig, NymApiTopologyProvider};
|
||||
pub use nym_topology::provider_trait::TopologyProvider;
|
||||
|
||||
// TODO: move it to config later
|
||||
const MAX_FAILURE_COUNT: usize = 10;
|
||||
|
||||
@@ -14,9 +14,10 @@ use url::Url;
|
||||
pub const DEFAULT_MIN_MIXNODE_PERFORMANCE: u8 = 50;
|
||||
pub const DEFAULT_MIN_GATEWAY_PERFORMANCE: u8 = 50;
|
||||
|
||||
pub(crate) struct Config {
|
||||
pub(crate) min_mixnode_performance: u8,
|
||||
pub(crate) min_gateway_performance: u8,
|
||||
#[derive(Debug)]
|
||||
pub struct Config {
|
||||
pub min_mixnode_performance: u8,
|
||||
pub min_gateway_performance: u8,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
@@ -29,7 +30,7 @@ impl Default for Config {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct NymApiTopologyProvider {
|
||||
pub struct NymApiTopologyProvider {
|
||||
config: Config,
|
||||
|
||||
validator_client: nym_validator_client::client::NymApiClient,
|
||||
@@ -40,7 +41,7 @@ pub(crate) struct NymApiTopologyProvider {
|
||||
}
|
||||
|
||||
impl NymApiTopologyProvider {
|
||||
pub(crate) fn new(
|
||||
pub fn new(
|
||||
config: Config,
|
||||
mut nym_api_urls: Vec<Url>,
|
||||
client_version: String,
|
||||
|
||||
@@ -19,7 +19,7 @@ use nym_api_requests::ecash::{
|
||||
};
|
||||
use nym_api_requests::models::{
|
||||
GatewayCoreStatusResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse,
|
||||
RewardEstimationResponse, StakeSaturationResponse,
|
||||
NymNodeDescription, RewardEstimationResponse, StakeSaturationResponse,
|
||||
};
|
||||
use nym_api_requests::models::{LegacyDescribedGateway, MixNodeBondAnnotated};
|
||||
use nym_api_requests::nym_nodes::SkimmedNode;
|
||||
@@ -30,10 +30,10 @@ use time::Date;
|
||||
use url::Url;
|
||||
|
||||
pub use crate::nym_api::NymApiClientExt;
|
||||
use nym_mixnet_contract_common::NymNodeDetails;
|
||||
pub use nym_mixnet_contract_common::{
|
||||
mixnode::MixNodeDetails, GatewayBond, IdentityKey, IdentityKeyRef, NodeId,
|
||||
};
|
||||
|
||||
// re-export the type to not break existing imports
|
||||
pub use crate::coconut::EcashApiClient;
|
||||
|
||||
@@ -106,7 +106,9 @@ impl Config {
|
||||
|
||||
pub struct Client<C, S = NoSigner> {
|
||||
// ideally they would have been read-only, but unfortunately rust doesn't have such features
|
||||
// #[deprecated(note = "please use `nym_api_client` instead")]
|
||||
pub nym_api: nym_api::Client,
|
||||
// pub nym_api_client: NymApiClient,
|
||||
pub nyxd: NyxdClient<C, S>,
|
||||
}
|
||||
|
||||
@@ -243,6 +245,50 @@ impl<C, S> Client<C, S> {
|
||||
Ok(self.nym_api.get_gateways().await?)
|
||||
}
|
||||
|
||||
// TODO: combine with NymApiClient...
|
||||
pub async fn get_all_cached_described_nodes(
|
||||
&self,
|
||||
) -> Result<Vec<NymNodeDescription>, ValidatorClientError> {
|
||||
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
|
||||
let mut page = 0;
|
||||
let mut descriptions = Vec::new();
|
||||
|
||||
loop {
|
||||
let mut res = self.nym_api.get_nodes_described(Some(page), None).await?;
|
||||
|
||||
descriptions.append(&mut res.data);
|
||||
if descriptions.len() < res.pagination.total {
|
||||
page += 1
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(descriptions)
|
||||
}
|
||||
|
||||
// TODO: combine with NymApiClient...
|
||||
pub async fn get_all_cached_bonded_nym_nodes(
|
||||
&self,
|
||||
) -> Result<Vec<NymNodeDetails>, ValidatorClientError> {
|
||||
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
|
||||
let mut page = 0;
|
||||
let mut bonds = Vec::new();
|
||||
|
||||
loop {
|
||||
let mut res = self.nym_api.get_nym_nodes(Some(page), None).await?;
|
||||
|
||||
bonds.append(&mut res.data);
|
||||
if bonds.len() < res.pagination.total {
|
||||
page += 1
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(bonds)
|
||||
}
|
||||
|
||||
pub async fn blind_sign(
|
||||
&self,
|
||||
request_body: &BlindSignRequestBody,
|
||||
@@ -327,7 +373,7 @@ impl NymApiClient {
|
||||
loop {
|
||||
let mut res = self
|
||||
.nym_api
|
||||
.get_all_basic_entry_assigned_nodes(
|
||||
.get_basic_entry_assigned_nodes(
|
||||
semver_compatibility.clone(),
|
||||
false,
|
||||
Some(page),
|
||||
@@ -404,6 +450,48 @@ impl NymApiClient {
|
||||
Ok(self.nym_api.get_gateways_described().await?)
|
||||
}
|
||||
|
||||
pub async fn get_all_described_nodes(
|
||||
&self,
|
||||
) -> Result<Vec<NymNodeDescription>, ValidatorClientError> {
|
||||
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
|
||||
let mut page = 0;
|
||||
let mut descriptions = Vec::new();
|
||||
|
||||
loop {
|
||||
let mut res = self.nym_api.get_nodes_described(Some(page), None).await?;
|
||||
|
||||
descriptions.append(&mut res.data);
|
||||
if descriptions.len() < res.pagination.total {
|
||||
page += 1
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(descriptions)
|
||||
}
|
||||
|
||||
pub async fn get_all_bonded_nym_nodes(
|
||||
&self,
|
||||
) -> Result<Vec<NymNodeDetails>, ValidatorClientError> {
|
||||
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
|
||||
let mut page = 0;
|
||||
let mut bonds = Vec::new();
|
||||
|
||||
loop {
|
||||
let mut res = self.nym_api.get_nym_nodes(Some(page), None).await?;
|
||||
|
||||
bonds.append(&mut res.data);
|
||||
if bonds.len() < res.pagination.total {
|
||||
page += 1
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(bonds)
|
||||
}
|
||||
|
||||
pub async fn get_gateway_core_status_count(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
|
||||
@@ -11,9 +11,10 @@ use nym_api_requests::ecash::models::{
|
||||
};
|
||||
use nym_api_requests::ecash::VerificationKeyResponse;
|
||||
use nym_api_requests::models::{
|
||||
AnnotationResponse, LegacyDescribedMixNode, NodePerformanceResponse,
|
||||
AnnotationResponse, LegacyDescribedMixNode, NodePerformanceResponse, NymNodeDescription,
|
||||
};
|
||||
use nym_api_requests::nym_nodes::PaginatedCachedNodesResponse;
|
||||
use nym_api_requests::pagination::PaginatedResponse;
|
||||
pub use nym_api_requests::{
|
||||
ecash::{
|
||||
models::{
|
||||
@@ -38,7 +39,7 @@ use nym_contracts_common::IdentityKey;
|
||||
pub use nym_http_api_client::Client;
|
||||
use nym_http_api_client::{ApiClient, NO_PARAMS};
|
||||
use nym_mixnet_contract_common::mixnode::MixNodeDetails;
|
||||
use nym_mixnet_contract_common::{GatewayBond, IdentityKeyRef, NodeId};
|
||||
use nym_mixnet_contract_common::{GatewayBond, IdentityKeyRef, NodeId, NymNodeDetails};
|
||||
use time::format_description::BorrowedFormatItem;
|
||||
use time::Date;
|
||||
use tracing::instrument;
|
||||
@@ -127,6 +128,44 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_nodes_described(
|
||||
&self,
|
||||
page: Option<u32>,
|
||||
per_page: Option<u32>,
|
||||
) -> Result<PaginatedResponse<NymNodeDescription>, NymAPIError> {
|
||||
let mut params = Vec::new();
|
||||
|
||||
if let Some(page) = page {
|
||||
params.push(("page", page.to_string()))
|
||||
}
|
||||
|
||||
if let Some(per_page) = per_page {
|
||||
params.push(("per_page", per_page.to_string()))
|
||||
}
|
||||
|
||||
self.get_json(&[routes::API_VERSION, "nym-nodes", "described"], ¶ms)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_nym_nodes(
|
||||
&self,
|
||||
page: Option<u32>,
|
||||
per_page: Option<u32>,
|
||||
) -> Result<PaginatedResponse<NymNodeDetails>, NymAPIError> {
|
||||
let mut params = Vec::new();
|
||||
|
||||
if let Some(page) = page {
|
||||
params.push(("page", page.to_string()))
|
||||
}
|
||||
|
||||
if let Some(per_page) = per_page {
|
||||
params.push(("per_page", per_page.to_string()))
|
||||
}
|
||||
|
||||
self.get_json(&[routes::API_VERSION, "nym-nodes", "bonded"], ¶ms)
|
||||
.await
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
async fn get_basic_mixnodes(
|
||||
&self,
|
||||
@@ -178,7 +217,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
/// retrieve basic information for nodes are capable of operating as an entry gateway
|
||||
/// this includes legacy gateways and nym-nodes
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_all_basic_entry_assigned_nodes(
|
||||
async fn get_basic_entry_assigned_nodes(
|
||||
&self,
|
||||
semver_compatibility: Option<String>,
|
||||
no_legacy: bool,
|
||||
|
||||
@@ -17,6 +17,7 @@ use crate::{
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use cosmwasm_std::{Addr, Coin, Decimal, StdResult, Uint128};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||
|
||||
/// Full details associated with given mixnode.
|
||||
@@ -647,14 +648,39 @@ impl From<LegacyMixLayer> for u8 {
|
||||
export_to = "ts-packages/types/src/types/rust/PendingMixnodeChanges.ts"
|
||||
)
|
||||
)]
|
||||
#[cw_serde]
|
||||
#[derive(Default, Copy)]
|
||||
// note: we had to remove `#[cw_serde]` as it enforces `#[serde(deny_unknown_fields)]` which we do not want
|
||||
// with the addition of .cost_params_change field
|
||||
#[derive(
|
||||
::cosmwasm_schema::serde::Serialize,
|
||||
::cosmwasm_schema::serde::Deserialize,
|
||||
::std::clone::Clone,
|
||||
::std::fmt::Debug,
|
||||
::std::cmp::PartialEq,
|
||||
::cosmwasm_schema::schemars::JsonSchema,
|
||||
Default,
|
||||
Copy,
|
||||
)]
|
||||
#[schemars(crate = "::cosmwasm_schema::schemars")]
|
||||
pub struct PendingMixNodeChanges {
|
||||
pub pledge_change: Option<EpochEventId>,
|
||||
|
||||
#[serde(default)]
|
||||
pub cost_params_change: Option<IntervalEventId>,
|
||||
}
|
||||
|
||||
#[derive(Default, Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct LegacyPendingMixNodeChanges {
|
||||
pub pledge_change: Option<EpochEventId>,
|
||||
}
|
||||
|
||||
impl From<PendingMixNodeChanges> for LegacyPendingMixNodeChanges {
|
||||
fn from(value: PendingMixNodeChanges) -> Self {
|
||||
LegacyPendingMixNodeChanges {
|
||||
pledge_change: value.pledge_change,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PendingMixNodeChanges {
|
||||
pub fn new_empty() -> PendingMixNodeChanges {
|
||||
PendingMixNodeChanges {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_pemstore::traits::{PemStorableKey, PemStorableKeyPair};
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::fmt::{self, Debug, Display, Formatter};
|
||||
use std::str::FromStr;
|
||||
use thiserror::Error;
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
@@ -112,12 +112,18 @@ impl PemStorableKeyPair for KeyPair {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
|
||||
#[derive(PartialEq, Eq, Hash, Copy, Clone)]
|
||||
pub struct PublicKey(x25519_dalek::PublicKey);
|
||||
|
||||
impl Display for PublicKey {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.to_base58_string())
|
||||
Display::fmt(&self.to_base58_string(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for PublicKey {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
Debug::fmt(&self.to_base58_string(), f)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,8 +31,16 @@ pub mod option_bs58_x25519_pubkey {
|
||||
pub fn deserialize<'de, D: Deserializer<'de>>(
|
||||
deserializer: D,
|
||||
) -> Result<Option<PublicKey>, D::Error> {
|
||||
let s = Option::<String>::deserialize(deserializer)?;
|
||||
s.map(|s| PublicKey::from_base58_string(&s).map_err(serde::de::Error::custom))
|
||||
.transpose()
|
||||
match Option::<String>::deserialize(deserializer)? {
|
||||
None => Ok(None),
|
||||
Some(s) => {
|
||||
if s.is_empty() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Some(PublicKey::from_base58_string(&s).map_err(serde::de::Error::custom))
|
||||
.transpose()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ pub use ed25519_dalek::SignatureError;
|
||||
use ed25519_dalek::{Signer, SigningKey};
|
||||
pub use ed25519_dalek::{Verifier, PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, SIGNATURE_LENGTH};
|
||||
use nym_pemstore::traits::{PemStorableKey, PemStorableKeyPair};
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::fmt::{self, Debug, Display, Formatter};
|
||||
use std::str::FromStr;
|
||||
use thiserror::Error;
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
@@ -119,12 +119,18 @@ impl PemStorableKeyPair for KeyPair {
|
||||
}
|
||||
|
||||
/// ed25519 EdDSA Public Key
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub struct PublicKey(ed25519_dalek::VerifyingKey);
|
||||
|
||||
impl Display for PublicKey {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.to_base58_string())
|
||||
Display::fmt(&self.to_base58_string(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for PublicKey {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
Debug::fmt(&self.to_base58_string(), f)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,9 @@ pub enum HttpClientError<E: Display = String> {
|
||||
source: reqwest::Error,
|
||||
},
|
||||
|
||||
#[error("failed to deserialise received response: {source}")]
|
||||
ResponseDeserialisationFailure { source: serde_json::Error },
|
||||
|
||||
#[error("provided url is malformed: {source}")]
|
||||
MalformedUrl {
|
||||
#[from]
|
||||
@@ -531,19 +534,17 @@ where
|
||||
}
|
||||
|
||||
if res.status().is_success() {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
let text = res.text().await.inspect_err(|err| {
|
||||
tracing::error!("Couldn't even get response text: {err}");
|
||||
})?;
|
||||
tracing::trace!("Result:\n{:#?}", text);
|
||||
|
||||
serde_json::from_str(&text)
|
||||
.map_err(|err| HttpClientError::GenericRequestFailure(err.to_string()))
|
||||
let text = res.text().await?;
|
||||
match serde_json::from_str(&text) {
|
||||
Ok(res) => Ok(res),
|
||||
Err(source) => {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
tracing::trace!("Result:\n{:#?}", text);
|
||||
}
|
||||
Err(HttpClientError::ResponseDeserialisationFailure { source })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
Ok(res.json().await?)
|
||||
} else if res.status() == StatusCode::NOT_FOUND {
|
||||
Err(HttpClientError::NotFound)
|
||||
} else {
|
||||
|
||||
@@ -11,6 +11,7 @@ license.workspace = true
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
axum-client-ip.workspace = true
|
||||
axum.workspace = true
|
||||
bytes = { workspace = true }
|
||||
colored.workspace = true
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use axum::extract::{ConnectInfo, Request};
|
||||
use axum::extract::Request;
|
||||
use axum::http::header::{HOST, USER_AGENT};
|
||||
use axum::http::HeaderValue;
|
||||
use axum::middleware::Next;
|
||||
use axum::response::IntoResponse;
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use colored::Colorize;
|
||||
use std::net::SocketAddr;
|
||||
use std::time::Instant;
|
||||
use tracing::info;
|
||||
|
||||
pub async fn logger(
|
||||
ConnectInfo(addr): ConnectInfo<SocketAddr>,
|
||||
InsecureClientIp(addr): InsecureClientIp,
|
||||
request: Request,
|
||||
next: Next,
|
||||
) -> impl IntoResponse {
|
||||
|
||||
@@ -2,7 +2,6 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct TestrunAssignment {
|
||||
/// has nothing to do with GW identity key. This is PK from `gateways` table
|
||||
pub testrun_id: i64,
|
||||
pub gateway_pk_id: i64,
|
||||
pub gateway_identity_key: String,
|
||||
}
|
||||
|
||||
@@ -42,32 +42,8 @@ impl PendingSync {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BlockProcessorConfig {
|
||||
pub pruning_options: PruningOptions,
|
||||
pub store_precommits: bool,
|
||||
}
|
||||
|
||||
impl Default for BlockProcessorConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
pruning_options: PruningOptions::nothing(),
|
||||
store_precommits: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockProcessorConfig {
|
||||
pub fn new(pruning_options: PruningOptions, store_precommits: bool) -> Self {
|
||||
Self {
|
||||
pruning_options,
|
||||
store_precommits,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BlockProcessor {
|
||||
config: BlockProcessorConfig,
|
||||
pruning_options: PruningOptions,
|
||||
cancel: CancellationToken,
|
||||
synced: Arc<Notify>,
|
||||
last_processed_height: u32,
|
||||
@@ -89,10 +65,9 @@ pub struct BlockProcessor {
|
||||
msg_modules: Vec<Box<dyn MsgModule + Send>>,
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
impl BlockProcessor {
|
||||
pub async fn new(
|
||||
config: BlockProcessorConfig,
|
||||
pruning_options: PruningOptions,
|
||||
cancel: CancellationToken,
|
||||
synced: Arc<Notify>,
|
||||
incoming: UnboundedReceiver<BlockToProcess>,
|
||||
@@ -107,7 +82,7 @@ impl BlockProcessor {
|
||||
let last_pruned_height = last_pruned.try_into().unwrap_or_default();
|
||||
|
||||
Ok(BlockProcessor {
|
||||
config,
|
||||
pruning_options,
|
||||
cancel,
|
||||
synced,
|
||||
last_processed_height,
|
||||
@@ -126,7 +101,7 @@ impl BlockProcessor {
|
||||
}
|
||||
|
||||
pub fn with_pruning(mut self, pruning_options: PruningOptions) -> Self {
|
||||
self.config.pruning_options = pruning_options;
|
||||
self.pruning_options = pruning_options;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -153,7 +128,7 @@ impl BlockProcessor {
|
||||
// we won't end up with a corrupted storage.
|
||||
let mut tx = self.storage.begin_processing_tx().await?;
|
||||
|
||||
persist_block(&full_info, &mut tx, self.config.store_precommits).await?;
|
||||
persist_block(&full_info, &mut tx).await?;
|
||||
|
||||
// let the modules do whatever they want
|
||||
// the ones wanting the full block:
|
||||
@@ -266,7 +241,7 @@ impl BlockProcessor {
|
||||
|
||||
#[instrument(skip(self))]
|
||||
async fn prune_storage(&mut self) -> Result<(), ScraperError> {
|
||||
let keep_recent = self.config.pruning_options.strategy_keep_recent();
|
||||
let keep_recent = self.pruning_options.strategy_keep_recent();
|
||||
let last_to_keep = self.last_processed_height - keep_recent;
|
||||
|
||||
info!(
|
||||
@@ -307,12 +282,12 @@ impl BlockProcessor {
|
||||
async fn maybe_prune_storage(&mut self) -> Result<(), ScraperError> {
|
||||
debug!("checking for storage pruning");
|
||||
|
||||
if self.config.pruning_options.strategy.is_nothing() {
|
||||
if self.pruning_options.strategy.is_nothing() {
|
||||
trace!("the current pruning strategy is 'nothing'");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let interval = self.config.pruning_options.strategy_interval();
|
||||
let interval = self.pruning_options.strategy_interval();
|
||||
if self.last_pruned_height + interval <= self.last_processed_height {
|
||||
self.prune_storage().await?;
|
||||
}
|
||||
@@ -396,7 +371,7 @@ impl BlockProcessor {
|
||||
if latest_block > self.last_processed_height && self.last_processed_height != 0 {
|
||||
// in case we were offline for a while,
|
||||
// make sure we don't request blocks we'd have to prune anyway
|
||||
let keep_recent = self.config.pruning_options.strategy_keep_recent();
|
||||
let keep_recent = self.pruning_options.strategy_keep_recent();
|
||||
let last_to_keep = latest_block - keep_recent;
|
||||
self.last_processed_height = max(self.last_processed_height, last_to_keep);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::block_processor::types::BlockToProcess;
|
||||
use crate::block_processor::{BlockProcessor, BlockProcessorConfig};
|
||||
use crate::block_processor::BlockProcessor;
|
||||
use crate::block_requester::{BlockRequest, BlockRequester};
|
||||
use crate::error::ScraperError;
|
||||
use crate::modules::{BlockModule, MsgModule, TxModule};
|
||||
@@ -34,8 +34,6 @@ pub struct Config {
|
||||
pub database_path: PathBuf,
|
||||
|
||||
pub pruning_options: PruningOptions,
|
||||
|
||||
pub store_precommits: bool,
|
||||
}
|
||||
|
||||
pub struct NyxdScraperBuilder {
|
||||
@@ -62,14 +60,8 @@ impl NyxdScraperBuilder {
|
||||
req_rx,
|
||||
processing_tx.clone(),
|
||||
);
|
||||
|
||||
let block_processor_config = BlockProcessorConfig::new(
|
||||
scraper.config.pruning_options,
|
||||
scraper.config.store_precommits,
|
||||
);
|
||||
|
||||
let mut block_processor = BlockProcessor::new(
|
||||
block_processor_config,
|
||||
scraper.config.pruning_options,
|
||||
scraper.cancel_token.clone(),
|
||||
scraper.startup_sync.clone(),
|
||||
processing_rx,
|
||||
@@ -283,11 +275,8 @@ impl NyxdScraper {
|
||||
req_tx: Sender<BlockRequest>,
|
||||
processing_rx: UnboundedReceiver<BlockToProcess>,
|
||||
) -> Result<BlockProcessor, ScraperError> {
|
||||
let block_processor_config =
|
||||
BlockProcessorConfig::new(self.config.pruning_options, self.config.store_precommits);
|
||||
|
||||
BlockProcessor::new(
|
||||
block_processor_config,
|
||||
self.config.pruning_options,
|
||||
self.cancel_token.clone(),
|
||||
self.startup_sync.clone(),
|
||||
processing_rx,
|
||||
|
||||
@@ -212,7 +212,6 @@ impl ScraperStorage {
|
||||
pub async fn persist_block(
|
||||
block: &FullBlockInformation,
|
||||
tx: &mut StorageTransaction,
|
||||
store_precommits: bool,
|
||||
) -> Result<(), ScraperError> {
|
||||
let total_gas = crate::helpers::tx_gas_sum(&block.transactions);
|
||||
|
||||
@@ -225,12 +224,11 @@ pub async fn persist_block(
|
||||
// persist block data
|
||||
persist_block_data(&block.block, total_gas, tx).await?;
|
||||
|
||||
if store_precommits {
|
||||
if let Some(commit) = &block.block.last_commit {
|
||||
persist_commits(commit, &block.validators, tx).await?;
|
||||
} else {
|
||||
warn!("no commits for block {}", block.block.header.height)
|
||||
}
|
||||
// persist commits
|
||||
if let Some(commit) = &block.block.last_commit {
|
||||
persist_commits(commit, &block.validators, tx).await?;
|
||||
} else {
|
||||
warn!("no commits for block {}", block.block.header.height)
|
||||
}
|
||||
|
||||
// persist txs
|
||||
|
||||
@@ -286,6 +286,10 @@ impl NymTopology {
|
||||
self.get_gateway(gateway_identity).is_some()
|
||||
}
|
||||
|
||||
pub fn insert_gateway(&mut self, gateway: gateway::LegacyNode) {
|
||||
self.gateways.push(gateway)
|
||||
}
|
||||
|
||||
pub fn set_gateways(&mut self, gateways: Vec<gateway::LegacyNode>) {
|
||||
self.gateways = gateways
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ impl<'a> TryFrom<&'a SkimmedNode> for LegacyNode {
|
||||
});
|
||||
}
|
||||
|
||||
let layer = match value.epoch_role {
|
||||
let layer = match value.role {
|
||||
NodeRole::Mixnode { layer } => layer
|
||||
.try_into()
|
||||
.map_err(|_| MixnodeConversionError::InvalidLayer)?,
|
||||
|
||||
@@ -158,10 +158,13 @@ impl<St: Storage + Clone + 'static> PeerController<St> {
|
||||
.ok_or(Error::MissingClientBandwidthEntry)?
|
||||
.client_id
|
||||
{
|
||||
storage.create_bandwidth_entry(client_id).await?;
|
||||
let bandwidth = storage
|
||||
.get_available_bandwidth(client_id)
|
||||
.await?
|
||||
.ok_or(Error::MissingClientBandwidthEntry)?;
|
||||
Ok(Some(BandwidthStorageManager::new(
|
||||
storage,
|
||||
ClientBandwidth::new(Default::default()),
|
||||
ClientBandwidth::new(bandwidth.into()),
|
||||
client_id,
|
||||
BandwidthFlushingBehaviourConfig::default(),
|
||||
true,
|
||||
|
||||
@@ -84,12 +84,13 @@ impl<St: Storage + Clone + 'static> PeerHandle<St> {
|
||||
.ok_or(Error::InconsistentConsumedBytes)?
|
||||
.try_into()
|
||||
.map_err(|_| Error::InconsistentConsumedBytes)?;
|
||||
if bandwidth_manager
|
||||
.write()
|
||||
.await
|
||||
.try_use_bandwidth(spent_bandwidth)
|
||||
.await
|
||||
.is_err()
|
||||
if spent_bandwidth > 0
|
||||
&& bandwidth_manager
|
||||
.write()
|
||||
.await
|
||||
.try_use_bandwidth(spent_bandwidth)
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
let success = self.remove_peer().await?;
|
||||
return Ok(!success);
|
||||
|
||||
Generated
+15
-15
@@ -433,7 +433,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.59",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1483,9 +1483,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.81"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
|
||||
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -1660,7 +1660,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde_derive_internals",
|
||||
"syn 2.0.59",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1702,9 +1702,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.210"
|
||||
version = "1.0.214"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
|
||||
checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
@@ -1729,13 +1729,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.210"
|
||||
version = "1.0.214"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
|
||||
checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.59",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1746,7 +1746,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.59",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1769,7 +1769,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.59",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1928,9 +1928,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.59"
|
||||
version = "2.0.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a6531ffc7b071655e4ce2e04bd464c4830bb585a61cabb96cf808f05172615a"
|
||||
checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1954,7 +1954,7 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.59",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2120,5 +2120,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.59",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
@@ -3689,8 +3689,7 @@
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"Percent": {
|
||||
"description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)",
|
||||
@@ -5239,8 +5238,7 @@
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"Percent": {
|
||||
"description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)",
|
||||
@@ -5575,8 +5573,7 @@
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"Percent": {
|
||||
"description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)",
|
||||
@@ -7595,8 +7592,7 @@
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"Percent": {
|
||||
"description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)",
|
||||
|
||||
@@ -315,8 +315,7 @@
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"Percent": {
|
||||
"description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)",
|
||||
|
||||
@@ -323,8 +323,7 @@
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"Percent": {
|
||||
"description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)",
|
||||
|
||||
@@ -317,8 +317,7 @@
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"Percent": {
|
||||
"description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)",
|
||||
|
||||
@@ -319,8 +319,7 @@
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"Percent": {
|
||||
"description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)",
|
||||
|
||||
@@ -52,8 +52,8 @@ pub(crate) fn save_assignment(
|
||||
|
||||
// update metadata
|
||||
let mut metadata = ROLES_METADATA.load(storage, inactive)?;
|
||||
let last = assignment.nodes.last().copied().unwrap_or_default();
|
||||
metadata.set_highest_id(last, assignment.role);
|
||||
let highest_id = assignment.nodes.iter().max().copied().unwrap_or_default();
|
||||
metadata.set_highest_id(highest_id, assignment.role);
|
||||
metadata.set_role_count(assignment.role, assignment.nodes.len() as u32);
|
||||
if assignment.is_final_assignment() {
|
||||
metadata.fully_assigned = true
|
||||
@@ -140,6 +140,7 @@ pub(crate) fn initialise_storage(storage: &mut dyn Storage) -> Result<(), Mixnet
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::support::tests::test_helpers;
|
||||
use crate::support::tests::test_helpers::TestSetup;
|
||||
|
||||
#[test]
|
||||
fn next_id() {
|
||||
@@ -149,4 +150,33 @@ mod tests {
|
||||
assert_eq!(i, next_nymnode_id_counter(deps.as_mut().storage).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assigning_role_uses_highest_id_even_if_not_sorted() {
|
||||
let mut test = TestSetup::new();
|
||||
let deps = test.deps_mut();
|
||||
|
||||
let sorted = RoleAssignment {
|
||||
role: Role::EntryGateway,
|
||||
nodes: vec![1, 2, 3],
|
||||
};
|
||||
|
||||
let unsorted = RoleAssignment {
|
||||
role: Role::Layer1,
|
||||
nodes: vec![8, 5, 4],
|
||||
};
|
||||
|
||||
save_assignment(deps.storage, sorted).unwrap();
|
||||
save_assignment(deps.storage, unsorted).unwrap();
|
||||
|
||||
let storage = deps.as_ref().storage;
|
||||
|
||||
let active_bucket = ACTIVE_ROLES_BUCKET.load(storage).unwrap();
|
||||
let inactive = active_bucket.other() as u8;
|
||||
let metadata = ROLES_METADATA.load(storage, inactive).unwrap();
|
||||
|
||||
assert_eq!(metadata.entry_gateway_metadata.highest_id, 3);
|
||||
assert_eq!(metadata.layer1_metadata.highest_id, 8);
|
||||
assert_eq!(metadata.highest_rewarded_id(), 8)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ pub use nym_explorer_api_requests::{
|
||||
|
||||
// Paths
|
||||
const API_VERSION: &str = "v1";
|
||||
const TMP: &str = "tmp";
|
||||
const UNSTABLE: &str = "unstable";
|
||||
const MIXNODES: &str = "mix-nodes";
|
||||
const GATEWAYS: &str = "gateways";
|
||||
|
||||
@@ -96,6 +98,13 @@ impl ExplorerClient {
|
||||
pub async fn get_gateways(&self) -> Result<Vec<PrettyDetailedGatewayBond>, ExplorerApiError> {
|
||||
self.query_explorer_api(&[API_VERSION, GATEWAYS]).await
|
||||
}
|
||||
|
||||
pub async fn unstable_get_gateways(
|
||||
&self,
|
||||
) -> Result<Vec<PrettyDetailedGatewayBond>, ExplorerApiError> {
|
||||
self.query_explorer_api(&[API_VERSION, TMP, UNSTABLE, GATEWAYS])
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
fn combine_url(mut base_url: Url, paths: &[&str]) -> Result<Url, ExplorerApiError> {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
use crate::state::ExplorerApiStateContext;
|
||||
use log::{info, warn};
|
||||
use nym_explorer_api_requests::Location;
|
||||
use nym_network_defaults::DEFAULT_NYM_NODE_HTTP_PORT;
|
||||
use nym_task::TaskClient;
|
||||
|
||||
pub(crate) struct GeoLocateTask {
|
||||
@@ -25,6 +26,7 @@ impl GeoLocateTask {
|
||||
_ = interval_timer.tick() => {
|
||||
self.locate_mix_nodes().await;
|
||||
self.locate_gateways().await;
|
||||
self.locate_nym_nodes().await;
|
||||
}
|
||||
_ = self.shutdown.recv() => {
|
||||
trace!("Listener: Received shutdown");
|
||||
@@ -109,6 +111,83 @@ impl GeoLocateTask {
|
||||
trace!("All mix nodes located");
|
||||
}
|
||||
|
||||
async fn locate_nym_nodes(&mut self) {
|
||||
// I'm unwrapping to the default value to get rid of an extra indentation level from the `if let Some(...) = ...`
|
||||
// If the value is None, we'll unwrap to an empty hashmap and the `values()` loop won't do any work anyway
|
||||
let nym_nodes = self.state.inner.nymnodes.get_bonded_nymnodes().await;
|
||||
|
||||
let geo_ip = self.state.inner.geo_ip.0.clone();
|
||||
|
||||
for (i, cache_item) in nym_nodes.values().enumerate() {
|
||||
if self
|
||||
.state
|
||||
.inner
|
||||
.nymnodes
|
||||
.is_location_valid(cache_item.node_id())
|
||||
.await
|
||||
{
|
||||
// when the cached location is valid, don't locate and continue to next mix node
|
||||
continue;
|
||||
}
|
||||
|
||||
let bonded_host = &cache_item.bond_information.node.host;
|
||||
|
||||
match geo_ip.query(
|
||||
bonded_host,
|
||||
Some(
|
||||
cache_item
|
||||
.bond_information
|
||||
.node
|
||||
.custom_http_port
|
||||
.unwrap_or(DEFAULT_NYM_NODE_HTTP_PORT),
|
||||
),
|
||||
) {
|
||||
Ok(opt) => match opt {
|
||||
Some(location) => {
|
||||
let location: Location = location.into();
|
||||
|
||||
trace!(
|
||||
"{} mix nodes already located. host {} is located in {:#?}",
|
||||
i,
|
||||
bonded_host,
|
||||
location.three_letter_iso_country_code,
|
||||
);
|
||||
|
||||
if i > 0 && (i % 100) == 0 {
|
||||
info!("Located {} nym-nodes...", i + 1,);
|
||||
}
|
||||
|
||||
self.state
|
||||
.inner
|
||||
.nymnodes
|
||||
.set_location(cache_item.node_id(), Some(location))
|
||||
.await;
|
||||
|
||||
// one node has been located, so return out of the loop
|
||||
return;
|
||||
}
|
||||
None => {
|
||||
warn!("❌ Location for {bonded_host} not found.");
|
||||
self.state
|
||||
.inner
|
||||
.nymnodes
|
||||
.set_location(cache_item.node_id(), None)
|
||||
.await;
|
||||
}
|
||||
},
|
||||
Err(_e) => {
|
||||
// warn!(
|
||||
// "❌ Oh no! Location for {} failed. Error: {:#?}",
|
||||
// cache_item.mix_node().host,
|
||||
// e
|
||||
// );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
trace!("All nym-nodes nodes located");
|
||||
}
|
||||
|
||||
async fn locate_gateways(&mut self) {
|
||||
let gateways = self.state.inner.gateways.get_gateways().await;
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ use crate::gateways::http::gateways_make_default_routes;
|
||||
use crate::http::swagger::get_docs;
|
||||
use crate::mix_node::http::mix_node_make_default_routes;
|
||||
use crate::mix_nodes::http::mix_nodes_make_default_routes;
|
||||
use crate::nym_nodes::http::unstable_temp_nymnodes_make_default_routes;
|
||||
use crate::overview::http::overview_make_default_routes;
|
||||
use crate::ping::http::ping_make_default_routes;
|
||||
use crate::service_providers::http::service_providers_make_default_routes;
|
||||
@@ -58,6 +59,7 @@ fn configure_rocket(state: ExplorerApiStateContext) -> Rocket<Build> {
|
||||
"/ping" => ping_make_default_routes(&openapi_settings),
|
||||
"/validators" => validators_make_default_routes(&openapi_settings),
|
||||
"/service-providers" => service_providers_make_default_routes(&openapi_settings),
|
||||
"/tmp/unstable" => unstable_temp_nymnodes_make_default_routes(&openapi_settings),
|
||||
};
|
||||
|
||||
building_rocket
|
||||
|
||||
@@ -22,6 +22,7 @@ mod http;
|
||||
mod location;
|
||||
mod mix_node;
|
||||
pub(crate) mod mix_nodes;
|
||||
mod nym_nodes;
|
||||
mod overview;
|
||||
mod ping;
|
||||
pub(crate) mod service_providers;
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::state::ExplorerApiStateContext;
|
||||
use nym_explorer_api_requests::PrettyDetailedGatewayBond;
|
||||
use okapi::openapi3::OpenApi;
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::{Route, State};
|
||||
use rocket_okapi::settings::OpenApiSettings;
|
||||
|
||||
pub fn unstable_temp_nymnodes_make_default_routes(
|
||||
settings: &OpenApiSettings,
|
||||
) -> (Vec<Route>, OpenApi) {
|
||||
openapi_get_routes_spec![settings: all_gateways]
|
||||
}
|
||||
|
||||
#[openapi(tag = "UNSTABLE")]
|
||||
#[get("/gateways")]
|
||||
pub(crate) async fn all_gateways(
|
||||
state: &State<ExplorerApiStateContext>,
|
||||
) -> Json<Vec<PrettyDetailedGatewayBond>> {
|
||||
let mut gateways = state.inner.gateways.get_detailed_gateways().await;
|
||||
gateways.append(&mut state.inner.nymnodes.pretty_gateways().await);
|
||||
|
||||
Json(gateways)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_mixnet_contract_common::NodeId;
|
||||
|
||||
use crate::location::LocationCache;
|
||||
|
||||
pub(crate) type NymNodeLocationCache = LocationCache<NodeId>;
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
pub(crate) mod http;
|
||||
pub(crate) mod location;
|
||||
pub(crate) mod models;
|
||||
|
||||
pub(crate) const CACHE_ENTRY_TTL: Duration = Duration::from_secs(1200);
|
||||
@@ -0,0 +1,154 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::location::{LocationCache, LocationCacheItem};
|
||||
use crate::nym_nodes::location::NymNodeLocationCache;
|
||||
use crate::nym_nodes::CACHE_ENTRY_TTL;
|
||||
use nym_explorer_api_requests::{Location, PrettyDetailedGatewayBond};
|
||||
use nym_mixnet_contract_common::{Gateway, NodeId, NymNodeDetails};
|
||||
use nym_validator_client::models::NymNodeDescription;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, SystemTime};
|
||||
use tokio::sync::{RwLock, RwLockReadGuard};
|
||||
|
||||
pub(crate) struct NymNodesCache {
|
||||
pub(crate) valid_until: SystemTime,
|
||||
pub(crate) bonded_nym_nodes: HashMap<NodeId, NymNodeDetails>,
|
||||
pub(crate) described_nodes: HashMap<NodeId, NymNodeDescription>,
|
||||
}
|
||||
|
||||
impl NymNodesCache {
|
||||
fn new() -> Self {
|
||||
NymNodesCache {
|
||||
valid_until: SystemTime::now() - Duration::from_secs(60), // in the past
|
||||
bonded_nym_nodes: Default::default(),
|
||||
described_nodes: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
// fn is_valid(&self) -> bool {
|
||||
// self.valid_until >= SystemTime::now()
|
||||
// }
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct ThreadSafeNymNodesCache {
|
||||
nymnodes: Arc<RwLock<NymNodesCache>>,
|
||||
locations: Arc<RwLock<LocationCache<NodeId>>>,
|
||||
}
|
||||
|
||||
impl ThreadSafeNymNodesCache {
|
||||
pub(crate) fn new() -> Self {
|
||||
ThreadSafeNymNodesCache {
|
||||
nymnodes: Arc::new(RwLock::new(NymNodesCache::new())),
|
||||
locations: Arc::new(RwLock::new(NymNodeLocationCache::new())),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_with_location_cache(locations: NymNodeLocationCache) -> Self {
|
||||
ThreadSafeNymNodesCache {
|
||||
nymnodes: Arc::new(RwLock::new(NymNodesCache::new())),
|
||||
locations: Arc::new(RwLock::new(locations)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn is_location_valid(&self, node_id: NodeId) -> bool {
|
||||
self.locations
|
||||
.read()
|
||||
.await
|
||||
.get(&node_id)
|
||||
.map_or(false, |cache_item| {
|
||||
cache_item.valid_until > SystemTime::now()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) async fn get_bonded_nymnodes(
|
||||
&self,
|
||||
) -> RwLockReadGuard<HashMap<NodeId, NymNodeDetails>> {
|
||||
let guard = self.nymnodes.read().await;
|
||||
RwLockReadGuard::map(guard, |n| &n.bonded_nym_nodes)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_locations(&self) -> NymNodeLocationCache {
|
||||
self.locations.read().await.clone()
|
||||
}
|
||||
|
||||
pub(crate) async fn set_location(&self, node_id: NodeId, location: Option<Location>) {
|
||||
// cache the location for this mix node so that it can be used when the mix node list is refreshed
|
||||
self.locations
|
||||
.write()
|
||||
.await
|
||||
.insert(node_id, LocationCacheItem::new_from_location(location));
|
||||
}
|
||||
|
||||
pub(crate) async fn update_cache(
|
||||
&self,
|
||||
all_bonds: Vec<NymNodeDetails>,
|
||||
descriptions: Vec<NymNodeDescription>,
|
||||
) {
|
||||
let mut guard = self.nymnodes.write().await;
|
||||
guard.bonded_nym_nodes = all_bonds
|
||||
.into_iter()
|
||||
.map(|details| (details.node_id(), details))
|
||||
.collect();
|
||||
guard.described_nodes = descriptions
|
||||
.into_iter()
|
||||
.map(|description| (description.node_id, description))
|
||||
.collect();
|
||||
|
||||
guard.valid_until = SystemTime::now() + CACHE_ENTRY_TTL;
|
||||
}
|
||||
|
||||
pub(crate) async fn pretty_gateways(&self) -> Vec<PrettyDetailedGatewayBond> {
|
||||
let nodes_guard = self.nymnodes.read().await;
|
||||
let location_guard = self.locations.read().await;
|
||||
|
||||
let mut pretty_gateways = vec![];
|
||||
|
||||
for (node_id, native_nymnode) in &nodes_guard.bonded_nym_nodes {
|
||||
let Some(description) = nodes_guard.described_nodes.get(node_id) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if description.description.declared_role.entry {
|
||||
let location = location_guard.get(node_id);
|
||||
let bond = &native_nymnode.bond_information;
|
||||
|
||||
pretty_gateways.push(PrettyDetailedGatewayBond {
|
||||
pledge_amount: bond.original_pledge.clone(),
|
||||
owner: bond.owner.clone(),
|
||||
block_height: bond.bonding_height,
|
||||
gateway: Gateway {
|
||||
host: bond.node.host.clone(),
|
||||
mix_port: description.description.mix_port(),
|
||||
clients_port: description.description.mixnet_websockets.ws_port,
|
||||
location: description
|
||||
.description
|
||||
.auxiliary_details
|
||||
.location
|
||||
.as_ref()
|
||||
.map(|l| l.to_string())
|
||||
.unwrap_or_default(),
|
||||
sphinx_key: description
|
||||
.description
|
||||
.host_information
|
||||
.keys
|
||||
.x25519
|
||||
.to_base58_string(),
|
||||
identity_key: bond.node.identity_key.clone(),
|
||||
version: description
|
||||
.description
|
||||
.build_information
|
||||
.build_version
|
||||
.clone(),
|
||||
},
|
||||
proxy: None,
|
||||
location: location.and_then(|l| l.location.clone()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pretty_gateways
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,8 @@ use crate::gateways::models::ThreadsafeGatewayCache;
|
||||
use crate::mix_node::models::ThreadsafeMixNodeCache;
|
||||
use crate::mix_nodes::location::MixnodeLocationCache;
|
||||
use crate::mix_nodes::models::ThreadsafeMixNodesCache;
|
||||
use crate::nym_nodes::location::NymNodeLocationCache;
|
||||
use crate::nym_nodes::models::ThreadSafeNymNodesCache;
|
||||
use crate::ping::models::ThreadsafePingCache;
|
||||
use crate::validators::models::ThreadsafeValidatorCache;
|
||||
|
||||
@@ -30,6 +32,7 @@ pub struct ExplorerApiState {
|
||||
pub(crate) gateways: ThreadsafeGatewayCache,
|
||||
pub(crate) mixnode: ThreadsafeMixNodeCache,
|
||||
pub(crate) mixnodes: ThreadsafeMixNodesCache,
|
||||
pub(crate) nymnodes: ThreadSafeNymNodesCache,
|
||||
pub(crate) ping: ThreadsafePingCache,
|
||||
pub(crate) validators: ThreadsafeValidatorCache,
|
||||
pub(crate) geo_ip: ThreadsafeGeoIp,
|
||||
@@ -49,6 +52,7 @@ pub struct ExplorerApiStateOnDisk {
|
||||
pub(crate) country_node_distribution: CountryNodesDistribution,
|
||||
pub(crate) mixnode_location_cache: MixnodeLocationCache,
|
||||
pub(crate) gateway_location_cache: GatewayLocationCache,
|
||||
pub(crate) nymnode_location_cache: NymNodeLocationCache,
|
||||
pub(crate) as_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
@@ -85,6 +89,9 @@ impl ExplorerApiStateContext {
|
||||
mixnodes: ThreadsafeMixNodesCache::new_with_location_cache(
|
||||
state.mixnode_location_cache,
|
||||
),
|
||||
nymnodes: ThreadSafeNymNodesCache::new_with_location_cache(
|
||||
state.nymnode_location_cache,
|
||||
),
|
||||
ping: ThreadsafePingCache::new(),
|
||||
validators: ThreadsafeValidatorCache::new(),
|
||||
validator_client: ThreadsafeValidatorClient::new(),
|
||||
@@ -101,6 +108,7 @@ impl ExplorerApiStateContext {
|
||||
gateways: ThreadsafeGatewayCache::new(),
|
||||
mixnode: ThreadsafeMixNodeCache::new(),
|
||||
mixnodes: ThreadsafeMixNodesCache::new(),
|
||||
nymnodes: ThreadSafeNymNodesCache::new(),
|
||||
ping: ThreadsafePingCache::new(),
|
||||
validators: ThreadsafeValidatorCache::new(),
|
||||
validator_client: ThreadsafeValidatorClient::new(),
|
||||
@@ -117,6 +125,7 @@ impl ExplorerApiStateContext {
|
||||
country_node_distribution: self.inner.country_node_distribution.get_all().await,
|
||||
mixnode_location_cache: self.inner.mixnodes.get_locations().await,
|
||||
gateway_location_cache: self.inner.gateways.get_locations().await,
|
||||
nymnode_location_cache: self.inner.nymnodes.get_locations().await,
|
||||
as_at: Utc::now(),
|
||||
};
|
||||
serde_json::to_writer(file, &state).expect("error writing state to disk");
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_mixnet_contract_common::GatewayBond;
|
||||
use crate::mix_nodes::CACHE_REFRESH_RATE;
|
||||
use crate::state::ExplorerApiStateContext;
|
||||
use nym_mixnet_contract_common::{GatewayBond, NymNodeDetails};
|
||||
use nym_task::TaskClient;
|
||||
use nym_validator_client::models::MixNodeBondAnnotated;
|
||||
use nym_validator_client::models::{MixNodeBondAnnotated, NymNodeDescription};
|
||||
use nym_validator_client::nyxd::error::NyxdError;
|
||||
use nym_validator_client::nyxd::{Paging, TendermintRpcClient, ValidatorResponse};
|
||||
use nym_validator_client::{QueryHttpRpcValidatorClient, ValidatorClientError};
|
||||
use std::future::Future;
|
||||
|
||||
use crate::mix_nodes::CACHE_REFRESH_RATE;
|
||||
use crate::state::ExplorerApiStateContext;
|
||||
use tokio::time::MissedTickBehavior;
|
||||
|
||||
pub(crate) struct ExplorerApiTasks {
|
||||
state: ExplorerApiStateContext,
|
||||
@@ -39,6 +39,28 @@ impl ExplorerApiTasks {
|
||||
bonds
|
||||
}
|
||||
|
||||
async fn retrieve_bonded_nymnodes(&self) -> Result<Vec<NymNodeDetails>, ValidatorClientError> {
|
||||
info!("About to retrieve all nymnode bonds...");
|
||||
self.state
|
||||
.inner
|
||||
.validator_client
|
||||
.0
|
||||
.get_all_cached_bonded_nym_nodes()
|
||||
.await
|
||||
}
|
||||
|
||||
async fn retrieve_node_descriptions(
|
||||
&self,
|
||||
) -> Result<Vec<NymNodeDescription>, ValidatorClientError> {
|
||||
info!("About to retrieve node descriptions...");
|
||||
self.state
|
||||
.inner
|
||||
.validator_client
|
||||
.0
|
||||
.get_all_cached_described_nodes()
|
||||
.await
|
||||
}
|
||||
|
||||
async fn retrieve_all_mixnodes(&self) -> Vec<MixNodeBondAnnotated> {
|
||||
info!("About to retrieve all mixnode bonds...");
|
||||
self.retrieve_mixnodes(
|
||||
@@ -130,10 +152,33 @@ impl ExplorerApiTasks {
|
||||
}
|
||||
}
|
||||
|
||||
async fn update_nymnodes_cache(&self) {
|
||||
let nym_node_bonds = self.retrieve_bonded_nymnodes().await.unwrap_or_else(|err| {
|
||||
error!("failed to retrieve nym node bonds: {err}");
|
||||
Vec::new()
|
||||
});
|
||||
|
||||
let all_descriptions = self
|
||||
.retrieve_node_descriptions()
|
||||
.await
|
||||
.unwrap_or_else(|err| {
|
||||
error!("failed to retrieve node descriptions: {err}");
|
||||
Vec::new()
|
||||
});
|
||||
|
||||
self.state
|
||||
.inner
|
||||
.nymnodes
|
||||
.update_cache(nym_node_bonds, all_descriptions)
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) fn start(mut self) {
|
||||
info!("Spawning mix nodes task runner...");
|
||||
tokio::spawn(async move {
|
||||
let mut interval_timer = tokio::time::interval(CACHE_REFRESH_RATE);
|
||||
interval_timer.set_missed_tick_behavior(MissedTickBehavior::Skip);
|
||||
|
||||
while !self.shutdown.is_shutdown() {
|
||||
tokio::select! {
|
||||
_ = interval_timer.tick() => {
|
||||
@@ -147,6 +192,10 @@ impl ExplorerApiTasks {
|
||||
|
||||
info!("Updating mix node cache...");
|
||||
self.update_mixnode_cache().await;
|
||||
|
||||
info!("Updating nymnode cache...");
|
||||
self.update_nymnodes_cache().await;
|
||||
info!("Done");
|
||||
}
|
||||
_ = self.shutdown.recv() => {
|
||||
trace!("Listener: Received shutdown");
|
||||
|
||||
@@ -77,9 +77,11 @@ nym-network-defaults = { path = "../common/network-defaults" }
|
||||
nym-network-requester = { path = "../service-providers/network-requester" }
|
||||
nym-node-http-api = { path = "../nym-node/nym-node-http-api" }
|
||||
nym-pemstore = { path = "../common/pemstore" }
|
||||
nym-sdk = { path = "../sdk/rust/nym-sdk" }
|
||||
nym-sphinx = { path = "../common/nymsphinx" }
|
||||
nym-statistics-common = { path = "../common/statistics" }
|
||||
nym-task = { path = "../common/task" }
|
||||
nym-topology = { path = "../common/topology" }
|
||||
nym-types = { path = "../common/types" }
|
||||
nym-validator-client = { path = "../common/client-libs/validator-client" }
|
||||
nym-ip-packet-router = { path = "../service-providers/ip-packet-router" }
|
||||
|
||||
@@ -3,14 +3,19 @@
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::error::GatewayError;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use nym_crypto::asymmetric::encryption;
|
||||
use nym_gateway_stats_storage::PersistentStatsStorage;
|
||||
use nym_gateway_storage::PersistentStorage;
|
||||
use nym_pemstore::traits::PemStorableKeyPair;
|
||||
use nym_pemstore::KeyPairPath;
|
||||
|
||||
use nym_sdk::{NymApiTopologyProvider, NymApiTopologyProviderConfig, UserAgent};
|
||||
use nym_topology::{gateway, NymTopology, TopologyProvider};
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
use tracing::debug;
|
||||
use url::Url;
|
||||
|
||||
pub async fn load_network_requester_config<P: AsRef<Path>>(
|
||||
id: &str,
|
||||
@@ -102,3 +107,56 @@ pub(crate) fn load_sphinx_keys(config: &Config) -> Result<encryption::KeyPair, G
|
||||
);
|
||||
load_keypair(sphinx_paths, "gateway sphinx")
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GatewayTopologyProvider {
|
||||
inner: Arc<Mutex<GatewayTopologyProviderInner>>,
|
||||
}
|
||||
|
||||
impl GatewayTopologyProvider {
|
||||
pub fn new(
|
||||
gateway_node: gateway::LegacyNode,
|
||||
user_agent: UserAgent,
|
||||
nym_api_url: Vec<Url>,
|
||||
) -> GatewayTopologyProvider {
|
||||
GatewayTopologyProvider {
|
||||
inner: Arc::new(Mutex::new(GatewayTopologyProviderInner {
|
||||
inner: NymApiTopologyProvider::new(
|
||||
NymApiTopologyProviderConfig {
|
||||
min_mixnode_performance: 50,
|
||||
min_gateway_performance: 0,
|
||||
},
|
||||
nym_api_url,
|
||||
env!("CARGO_PKG_VERSION").to_string(),
|
||||
Some(user_agent),
|
||||
),
|
||||
gateway_node,
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct GatewayTopologyProviderInner {
|
||||
inner: NymApiTopologyProvider,
|
||||
gateway_node: gateway::LegacyNode,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl TopologyProvider for GatewayTopologyProvider {
|
||||
async fn get_new_topology(&mut self) -> Option<NymTopology> {
|
||||
let mut guard = self.inner.lock().await;
|
||||
match guard.inner.get_new_topology().await {
|
||||
None => None,
|
||||
Some(mut base) => {
|
||||
if !base.gateway_exists(&guard.gateway_node.identity_key) {
|
||||
debug!(
|
||||
"{} didn't exist in topology. inserting it.",
|
||||
guard.gateway_node.identity_key
|
||||
);
|
||||
base.insert_gateway(guard.gateway_node.clone());
|
||||
}
|
||||
Some(base)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+50
-1
@@ -14,9 +14,11 @@ use crate::node::client_handling::embedded_clients::{LocalEmbeddedClientHandle,
|
||||
use crate::node::client_handling::websocket;
|
||||
use crate::node::helpers::{
|
||||
initialise_main_storage, initialise_stats_storage, load_network_requester_config,
|
||||
GatewayTopologyProvider,
|
||||
};
|
||||
use crate::node::mixnet_handling::receiver::connection_handler::ConnectionHandler;
|
||||
use futures::channel::{mpsc, oneshot};
|
||||
use nym_bin_common::bin_info;
|
||||
use nym_credential_verification::ecash::{
|
||||
credential_sender::CredentialHandlerConfig, EcashManager,
|
||||
};
|
||||
@@ -27,13 +29,15 @@ use nym_network_requester::{LocalGateway, NRServiceProviderBuilder, RequestFilte
|
||||
use nym_node_http_api::state::metrics::SharedSessionStats;
|
||||
use nym_statistics_common::events::{self, StatsEventSender};
|
||||
use nym_task::{TaskClient, TaskHandle, TaskManager};
|
||||
use nym_topology::NetworkAddress;
|
||||
use nym_types::gateway::GatewayNodeDetailsResponse;
|
||||
use nym_validator_client::client::NodeId;
|
||||
use nym_validator_client::nyxd::{Coin, CosmWasmClient};
|
||||
use nym_validator_client::{nyxd, DirectSigningHttpRpcNyxdClient};
|
||||
use rand::seq::SliceRandom;
|
||||
use rand::thread_rng;
|
||||
use statistics::GatewayStatisticsCollector;
|
||||
use std::net::SocketAddr;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use tracing::*;
|
||||
@@ -236,6 +240,39 @@ impl<St> Gateway<St> {
|
||||
crate::helpers::node_details(&self.config).await
|
||||
}
|
||||
|
||||
fn gateway_topology_provider(&self) -> GatewayTopologyProvider {
|
||||
GatewayTopologyProvider::new(
|
||||
self.as_topology_node(),
|
||||
bin_info!().into(),
|
||||
self.config.gateway.nym_api_urls.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
fn as_topology_node(&self) -> nym_topology::gateway::LegacyNode {
|
||||
let ip = self
|
||||
.config
|
||||
.host
|
||||
.public_ips
|
||||
.first()
|
||||
.copied()
|
||||
.unwrap_or(IpAddr::V4(Ipv4Addr::LOCALHOST));
|
||||
let mix_host = SocketAddr::new(ip, self.config.gateway.mix_port);
|
||||
|
||||
nym_topology::gateway::LegacyNode {
|
||||
// those fields are irrelevant for the purposes of routing so it's fine if they're inaccurate.
|
||||
// the only thing that matters is the identity key (and maybe version)
|
||||
node_id: NodeId::MAX,
|
||||
mix_host,
|
||||
host: NetworkAddress::IpAddr(ip),
|
||||
clients_ws_port: self.config.gateway.clients_port,
|
||||
clients_wss_port: self.config.gateway.clients_wss_port,
|
||||
sphinx_key: *self.sphinx_keypair.public_key(),
|
||||
|
||||
identity_key: *self.identity_keypair.public_key(),
|
||||
version: env!("CARGO_PKG_VERSION").into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn start_mix_socket_listener(
|
||||
&self,
|
||||
ack_sender: MixForwardingSender,
|
||||
@@ -268,6 +305,7 @@ impl<St> Gateway<St> {
|
||||
async fn start_authenticator(
|
||||
&mut self,
|
||||
forwarding_channel: MixForwardingSender,
|
||||
topology_provider: GatewayTopologyProvider,
|
||||
shutdown: TaskClient,
|
||||
ecash_verifier: Arc<EcashManager<St>>,
|
||||
) -> Result<StartedAuthenticator, Box<dyn std::error::Error + Send + Sync>>
|
||||
@@ -315,6 +353,7 @@ impl<St> Gateway<St> {
|
||||
.with_shutdown(shutdown.fork("authenticator"))
|
||||
.with_wait_for_gateway(true)
|
||||
.with_minimum_gateway_performance(0)
|
||||
.with_custom_topology_provider(Box::new(topology_provider))
|
||||
.with_on_start(on_start_tx);
|
||||
|
||||
if let Some(custom_mixnet) = &opts.custom_mixnet_path {
|
||||
@@ -363,6 +402,7 @@ impl<St> Gateway<St> {
|
||||
async fn start_authenticator(
|
||||
&self,
|
||||
_forwarding_channel: MixForwardingSender,
|
||||
_topology_provider: GatewayTopologyProvider,
|
||||
_shutdown: TaskClient,
|
||||
_ecash_verifier: Arc<EcashManager<St>>,
|
||||
) -> Result<StartedAuthenticator, Box<dyn std::error::Error + Send + Sync>> {
|
||||
@@ -435,6 +475,7 @@ impl<St> Gateway<St> {
|
||||
async fn start_network_requester(
|
||||
&self,
|
||||
forwarding_channel: MixForwardingSender,
|
||||
topology_provider: GatewayTopologyProvider,
|
||||
shutdown: TaskClient,
|
||||
) -> Result<StartedNetworkRequester, GatewayError> {
|
||||
info!("Starting network requester...");
|
||||
@@ -462,6 +503,7 @@ impl<St> Gateway<St> {
|
||||
.with_custom_gateway_transceiver(Box::new(transceiver))
|
||||
.with_wait_for_gateway(true)
|
||||
.with_minimum_gateway_performance(0)
|
||||
.with_custom_topology_provider(Box::new(topology_provider))
|
||||
.with_on_start(on_start_tx);
|
||||
|
||||
if let Some(custom_mixnet) = &nr_opts.custom_mixnet_path {
|
||||
@@ -499,6 +541,7 @@ impl<St> Gateway<St> {
|
||||
async fn start_ip_packet_router(
|
||||
&self,
|
||||
forwarding_channel: MixForwardingSender,
|
||||
topology_provider: GatewayTopologyProvider,
|
||||
shutdown: TaskClient,
|
||||
) -> Result<LocalEmbeddedClientHandle, GatewayError> {
|
||||
info!("Starting IP packet provider...");
|
||||
@@ -527,6 +570,7 @@ impl<St> Gateway<St> {
|
||||
.with_custom_gateway_transceiver(Box::new(transceiver))
|
||||
.with_wait_for_gateway(true)
|
||||
.with_minimum_gateway_performance(0)
|
||||
.with_custom_topology_provider(Box::new(topology_provider))
|
||||
.with_on_start(on_start_tx);
|
||||
|
||||
if let Some(custom_mixnet) = &ip_opts.custom_mixnet_path {
|
||||
@@ -643,6 +687,8 @@ impl<St> Gateway<St> {
|
||||
shutdown.fork("statistics::GatewayStatisticsCollector"),
|
||||
);
|
||||
|
||||
let topology_provider = self.gateway_topology_provider();
|
||||
|
||||
let handler_config = CredentialHandlerConfig {
|
||||
revocation_bandwidth_penalty: self
|
||||
.config
|
||||
@@ -691,6 +737,7 @@ impl<St> Gateway<St> {
|
||||
let embedded_nr = self
|
||||
.start_network_requester(
|
||||
mix_forwarding_channel.clone(),
|
||||
topology_provider.clone(),
|
||||
shutdown.fork("NetworkRequester"),
|
||||
)
|
||||
.await?;
|
||||
@@ -706,6 +753,7 @@ impl<St> Gateway<St> {
|
||||
let embedded_ip_sp = self
|
||||
.start_ip_packet_router(
|
||||
mix_forwarding_channel.clone(),
|
||||
topology_provider.clone(),
|
||||
shutdown.fork("ip_service_provider"),
|
||||
)
|
||||
.await?;
|
||||
@@ -718,6 +766,7 @@ impl<St> Gateway<St> {
|
||||
let embedded_auth = self
|
||||
.start_authenticator(
|
||||
mix_forwarding_channel,
|
||||
topology_provider,
|
||||
shutdown.fork("authenticator"),
|
||||
ecash_verifier,
|
||||
)
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use cosmwasm_std::Decimal;
|
||||
use nym_mixnet_contract_common::mixnode::PendingMixNodeChanges;
|
||||
use nym_mixnet_contract_common::{
|
||||
GatewayBond, LegacyMixLayer, MixNodeBond, MixNodeDetails, NodeId, NodeRewarding,
|
||||
};
|
||||
use nym_mixnet_contract_common::mixnode::LegacyPendingMixNodeChanges;
|
||||
use nym_mixnet_contract_common::{GatewayBond, LegacyMixLayer, MixNodeBond, NodeId, NodeRewarding};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::ops::Deref;
|
||||
@@ -64,7 +62,7 @@ pub struct LegacyMixNodeDetailsWithLayer {
|
||||
|
||||
/// Adjustments to the mixnode that are ought to happen during future epoch transitions.
|
||||
#[serde(default)]
|
||||
pub pending_changes: PendingMixNodeChanges,
|
||||
pub pending_changes: LegacyPendingMixNodeChanges,
|
||||
}
|
||||
|
||||
impl LegacyMixNodeDetailsWithLayer {
|
||||
@@ -80,13 +78,3 @@ impl LegacyMixNodeDetailsWithLayer {
|
||||
self.bond_information.is_unbonding
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LegacyMixNodeDetailsWithLayer> for MixNodeDetails {
|
||||
fn from(value: LegacyMixNodeDetailsWithLayer) -> Self {
|
||||
MixNodeDetails {
|
||||
bond_information: value.bond_information.into(),
|
||||
rewarding_details: value.rewarding_details,
|
||||
pending_changes: value.pending_changes,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ use nym_crypto::asymmetric::x25519::{
|
||||
use nym_mixnet_contract_common::nym_node::Role;
|
||||
use nym_mixnet_contract_common::reward_params::{Performance, RewardingParams};
|
||||
use nym_mixnet_contract_common::rewarding::RewardEstimate;
|
||||
use nym_mixnet_contract_common::{IdentityKey, Interval, MixNode, NodeId, Percent};
|
||||
use nym_mixnet_contract_common::{GatewayBond, IdentityKey, Interval, MixNode, NodeId, Percent};
|
||||
use nym_network_defaults::{DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT};
|
||||
use nym_node_requests::api::v1::authenticator::models::Authenticator;
|
||||
use nym_node_requests::api::v1::gateway::models::Wireguard;
|
||||
@@ -138,6 +138,48 @@ pub struct NodePerformance {
|
||||
pub last_24h: Performance,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, Hash, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "generate-ts",
|
||||
ts(export, export_to = "ts-packages/types/src/types/rust/DisplayRole.ts")
|
||||
)]
|
||||
pub enum DisplayRole {
|
||||
EntryGateway,
|
||||
Layer1,
|
||||
Layer2,
|
||||
Layer3,
|
||||
ExitGateway,
|
||||
Standby,
|
||||
}
|
||||
|
||||
impl From<Role> for DisplayRole {
|
||||
fn from(role: Role) -> Self {
|
||||
match role {
|
||||
Role::EntryGateway => DisplayRole::EntryGateway,
|
||||
Role::Layer1 => DisplayRole::Layer1,
|
||||
Role::Layer2 => DisplayRole::Layer2,
|
||||
Role::Layer3 => DisplayRole::Layer3,
|
||||
Role::ExitGateway => DisplayRole::ExitGateway,
|
||||
Role::Standby => DisplayRole::Standby,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DisplayRole> for Role {
|
||||
fn from(role: DisplayRole) -> Self {
|
||||
match role {
|
||||
DisplayRole::EntryGateway => Role::EntryGateway,
|
||||
DisplayRole::Layer1 => Role::Layer1,
|
||||
DisplayRole::Layer2 => Role::Layer2,
|
||||
DisplayRole::Layer3 => Role::Layer3,
|
||||
DisplayRole::ExitGateway => Role::ExitGateway,
|
||||
DisplayRole::Standby => Role::Standby,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// imo for now there's no point in exposing more than that,
|
||||
// nym-api shouldn't be calculating apy or stake saturation for you.
|
||||
// it should just return its own metrics (performance) and then you can do with it as you wish
|
||||
@@ -153,7 +195,7 @@ pub struct NodePerformance {
|
||||
pub struct NodeAnnotation {
|
||||
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
||||
pub last_24h_performance: Performance,
|
||||
pub current_role: Option<Role>,
|
||||
pub current_role: Option<DisplayRole>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
@@ -286,7 +328,7 @@ impl MixNodeBondAnnotated {
|
||||
.sphinx_key
|
||||
.parse()
|
||||
.map_err(|_| MalformedNodeBond::InvalidX25519Key)?,
|
||||
epoch_role: role,
|
||||
role,
|
||||
supported_roles: DeclaredRoles {
|
||||
mixnode: true,
|
||||
entry: false,
|
||||
@@ -345,7 +387,7 @@ impl GatewayBondAnnotated {
|
||||
.sphinx_key
|
||||
.parse()
|
||||
.map_err(|_| MalformedNodeBond::InvalidX25519Key)?,
|
||||
epoch_role: role,
|
||||
role,
|
||||
supported_roles: DeclaredRoles {
|
||||
mixnode: false,
|
||||
entry: true,
|
||||
@@ -827,7 +869,7 @@ impl NymNodeDescription {
|
||||
// we can't use the declared roles, we have to take whatever was provided in the contract.
|
||||
// why? say this node COULD operate as an exit, but it might be the case the contract decided
|
||||
// to assign it an ENTRY role only. we have to use that one instead.
|
||||
epoch_role: role,
|
||||
role,
|
||||
supported_roles: self.description.declared_role,
|
||||
entry,
|
||||
performance,
|
||||
@@ -935,14 +977,14 @@ impl NymNodeData {
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
|
||||
pub struct LegacyDescribedGateway {
|
||||
pub bond: LegacyGatewayBondWithId,
|
||||
pub bond: GatewayBond,
|
||||
pub self_described: Option<NymNodeData>,
|
||||
}
|
||||
|
||||
impl From<LegacyGatewayBondWithId> for LegacyDescribedGateway {
|
||||
fn from(bond: LegacyGatewayBondWithId) -> Self {
|
||||
LegacyDescribedGateway {
|
||||
bond,
|
||||
bond: bond.bond,
|
||||
self_described: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,8 +141,8 @@ pub struct SkimmedNode {
|
||||
#[schemars(with = "String")]
|
||||
pub x25519_sphinx_pubkey: x25519::PublicKey,
|
||||
|
||||
#[serde(alias = "role")]
|
||||
pub epoch_role: NodeRole,
|
||||
#[serde(alias = "epoch_role")]
|
||||
pub role: NodeRole,
|
||||
|
||||
// needed for the purposes of sending appropriate test packets
|
||||
#[serde(default)]
|
||||
@@ -157,7 +157,7 @@ pub struct SkimmedNode {
|
||||
|
||||
impl SkimmedNode {
|
||||
pub fn get_mix_layer(&self) -> Option<u8> {
|
||||
match self.epoch_role {
|
||||
match self.role {
|
||||
NodeRole::Mixnode { layer } => Some(layer),
|
||||
_ => None,
|
||||
}
|
||||
|
||||
@@ -24,21 +24,21 @@ use utoipa::IntoParams;
|
||||
pub(crate) fn aggregation_routes(ecash_state: Arc<EcashState>) -> Router<AppState> {
|
||||
Router::new()
|
||||
.route(
|
||||
"/master-verification-key:epoch_id",
|
||||
"/master-verification-key",
|
||||
axum::routing::get({
|
||||
let ecash_state = Arc::clone(&ecash_state);
|
||||
|epoch_id| master_verification_key(epoch_id, ecash_state)
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/aggregated-expiration-date-signatures:expiration_date",
|
||||
"/aggregated-expiration-date-signatures",
|
||||
axum::routing::get({
|
||||
let ecash_state = Arc::clone(&ecash_state);
|
||||
|expiration_date| expiration_date_signatures(expiration_date, ecash_state)
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/aggregated-coin-indices-signatures:epoch_id",
|
||||
"/aggregated-coin-indices-signatures",
|
||||
axum::routing::get({
|
||||
let ecash_state = Arc::clone(&ecash_state);
|
||||
|epoch_id| coin_indices_signatures(epoch_id, ecash_state)
|
||||
@@ -52,7 +52,7 @@ pub(crate) fn aggregation_routes(ecash_state: Arc<EcashState>) -> Router<AppStat
|
||||
params(
|
||||
EpochIdParam
|
||||
),
|
||||
path = "/v1/ecash/master-verification-key/{epoch_id}",
|
||||
path = "/v1/ecash/master-verification-key",
|
||||
responses(
|
||||
(status = 200, body = VerificationKeyResponse)
|
||||
)
|
||||
@@ -83,7 +83,7 @@ struct ExpirationDateParam {
|
||||
params(
|
||||
ExpirationDateParam
|
||||
),
|
||||
path = "/v1/ecash/aggregated-expiration-date-signatures/{epoch_id}",
|
||||
path = "/v1/ecash/aggregated-expiration-date-signatures",
|
||||
responses(
|
||||
(status = 200, body = AggregatedExpirationDateSignatureResponse)
|
||||
)
|
||||
@@ -120,7 +120,7 @@ async fn expiration_date_signatures(
|
||||
params(
|
||||
EpochIdParam
|
||||
),
|
||||
path = "/v1/ecash/aggregated-coin-indices-signatures/{epoch_id}",
|
||||
path = "/v1/ecash/aggregated-coin-indices-signatures",
|
||||
responses(
|
||||
(status = 200, body = AggregatedCoinIndicesSignatureResponse)
|
||||
)
|
||||
|
||||
@@ -32,14 +32,14 @@ pub(crate) fn partial_signing_routes(ecash_state: Arc<EcashState>) -> Router<App
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/partial-expiration-date-signatures:expiration_date",
|
||||
"/partial-expiration-date-signatures",
|
||||
axum::routing::get({
|
||||
let ecash_state = Arc::clone(&ecash_state);
|
||||
|expiration_date| partial_expiration_date_signatures(expiration_date, ecash_state)
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/partial-coin-indices-signatures:epoch_id",
|
||||
"/partial-coin-indices-signatures",
|
||||
axum::routing::get({
|
||||
let ecash_state = Arc::clone(&ecash_state);
|
||||
|epoch_id| partial_coin_indices_signatures(epoch_id, ecash_state)
|
||||
@@ -127,7 +127,7 @@ struct ExpirationDateParam {
|
||||
params(
|
||||
ExpirationDateParam
|
||||
),
|
||||
path = "/v1/ecash/partial-expiration-date-signatures/{expiration_date}",
|
||||
path = "/v1/ecash/partial-expiration-date-signatures",
|
||||
responses(
|
||||
(status = 200, body = PartialExpirationDateSignatureResponse),
|
||||
(status = 400, body = ErrorResponse, description = "this nym-api is not an ecash signer in the current epoch"),
|
||||
@@ -165,7 +165,7 @@ async fn partial_expiration_date_signatures(
|
||||
params(
|
||||
EpochIdParam
|
||||
),
|
||||
path = "/v1/ecash/partial-coin-indices-signatures/{epoch_id}",
|
||||
path = "/v1/ecash/partial-coin-indices-signatures",
|
||||
responses(
|
||||
(status = 200, body = PartialExpirationDateSignatureResponse),
|
||||
(status = 400, body = ErrorResponse, description = "this nym-api is not an ecash signer in the current epoch"),
|
||||
|
||||
@@ -20,6 +20,7 @@ use time::macros::time;
|
||||
use time::{OffsetDateTime, Time};
|
||||
use tracing::{error, warn};
|
||||
|
||||
#[allow(deprecated)]
|
||||
pub(crate) fn spending_routes(ecash_state: Arc<EcashState>) -> Router<AppState> {
|
||||
Router::new()
|
||||
.route(
|
||||
@@ -242,6 +243,7 @@ async fn batch_redeem_tickets(
|
||||
(status = 500, body = ErrorResponse, description = "bloomfilters got disabled"),
|
||||
)
|
||||
)]
|
||||
#[deprecated]
|
||||
async fn double_spending_filter_v1(
|
||||
_state: Arc<EcashState>,
|
||||
) -> AxumResult<Json<SpentCredentialsResponse>> {
|
||||
|
||||
@@ -169,7 +169,7 @@ impl EpochAdvancer {
|
||||
let standby_eligible = all_choices
|
||||
.iter()
|
||||
.filter(|node| {
|
||||
exit_gateways.contains(&node.0.node_id)
|
||||
!exit_gateways.contains(&node.0.node_id)
|
||||
&& !entry_gateways.contains(&node.0.node_id)
|
||||
&& !mixnodes.contains(&node.0.node_id)
|
||||
})
|
||||
@@ -228,14 +228,24 @@ impl EpochAdvancer {
|
||||
)
|
||||
}
|
||||
|
||||
Ok(RewardedSet {
|
||||
let mut rewarded_set = RewardedSet {
|
||||
entry_gateways: entry_gateways.into_iter().collect(),
|
||||
exit_gateways: exit_gateways.into_iter().collect(),
|
||||
layer1,
|
||||
layer2,
|
||||
layer3,
|
||||
standby,
|
||||
})
|
||||
};
|
||||
|
||||
// make sure to sort the rewarded set values
|
||||
rewarded_set.entry_gateways.sort();
|
||||
rewarded_set.exit_gateways.sort();
|
||||
rewarded_set.layer1.sort();
|
||||
rewarded_set.layer2.sort();
|
||||
rewarded_set.layer3.sort();
|
||||
rewarded_set.standby.sort();
|
||||
|
||||
Ok(rewarded_set)
|
||||
}
|
||||
|
||||
async fn attach_performance_to_eligible_nodes(
|
||||
|
||||
@@ -274,8 +274,8 @@ impl<R: MessageReceiver + Send> Monitor<R> {
|
||||
info!("Received {}/{} packets", total_received, total_sent);
|
||||
|
||||
let summary = self.summary_producer.produce_summary(
|
||||
prepared_packets.tested_mixnodes,
|
||||
prepared_packets.tested_gateways,
|
||||
prepared_packets.mixnodes_under_test,
|
||||
prepared_packets.gateways_under_test,
|
||||
received,
|
||||
prepared_packets.invalid_mixnodes,
|
||||
prepared_packets.invalid_gateways,
|
||||
|
||||
@@ -25,7 +25,7 @@ use std::collections::{HashMap, HashSet};
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tracing::{error, info, trace};
|
||||
use tracing::{debug, error, info, trace};
|
||||
|
||||
const DEFAULT_AVERAGE_PACKET_DELAY: Duration = Duration::from_millis(200);
|
||||
const DEFAULT_AVERAGE_ACK_DELAY: Duration = Duration::from_millis(200);
|
||||
@@ -59,10 +59,10 @@ pub(crate) struct PreparedPackets {
|
||||
pub(super) packets: Vec<GatewayPackets>,
|
||||
|
||||
/// Vector containing list of public keys and owners of all nodes mixnodes being tested.
|
||||
pub(super) tested_mixnodes: Vec<TestableNode>,
|
||||
pub(super) mixnodes_under_test: Vec<TestableNode>,
|
||||
|
||||
/// Vector containing list of public keys and owners of all gateways being tested.
|
||||
pub(super) tested_gateways: Vec<TestableNode>,
|
||||
pub(super) gateways_under_test: Vec<TestableNode>,
|
||||
|
||||
/// All mixnodes that failed to get parsed correctly or were not version compatible.
|
||||
/// They will be marked to the validator as being down for the test.
|
||||
@@ -151,34 +151,38 @@ impl PacketPreparer {
|
||||
self.contract_cache.wait_for_initial_values().await;
|
||||
self.described_cache.naive_wait_for_initial_values().await;
|
||||
|
||||
let described_nodes = self
|
||||
.described_cache
|
||||
.get()
|
||||
.await
|
||||
.expect("the self-describe cache should have been initialised!");
|
||||
|
||||
// now wait for at least `minimum_full_routes` mixnodes per layer and `minimum_full_routes` gateway to be online
|
||||
info!("Waiting for minimal topology to be online");
|
||||
let initialisation_backoff = Duration::from_secs(30);
|
||||
loop {
|
||||
let gateways = self.contract_cache.legacy_gateways_all().await;
|
||||
let mixnodes = self.contract_cache.legacy_mixnodes_all_basic().await;
|
||||
let nym_nodes = self.contract_cache.nym_nodes().await;
|
||||
|
||||
if gateways.len() < minimum_full_routes {
|
||||
self.topology_wait_backoff(initialisation_backoff).await;
|
||||
continue;
|
||||
}
|
||||
let mut gateways_count = gateways.len();
|
||||
let mut mixnodes_count = mixnodes.len();
|
||||
|
||||
let mut layer1_count = 0;
|
||||
let mut layer2_count = 0;
|
||||
let mut layer3_count = 0;
|
||||
|
||||
for mix in mixnodes {
|
||||
match mix.layer {
|
||||
LegacyMixLayer::One => layer1_count += 1,
|
||||
LegacyMixLayer::Two => layer2_count += 1,
|
||||
LegacyMixLayer::Three => layer3_count += 1,
|
||||
for nym_node in nym_nodes {
|
||||
if let Some(described) = described_nodes.get_description(&nym_node.node_id()) {
|
||||
if described.declared_role.mixnode {
|
||||
mixnodes_count += 1;
|
||||
} else if described.declared_role.entry {
|
||||
gateways_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if layer1_count >= minimum_full_routes
|
||||
&& layer2_count >= minimum_full_routes
|
||||
&& layer3_count >= minimum_full_routes
|
||||
{
|
||||
debug!(
|
||||
"we have {mixnodes_count} possible mixnodes and {gateways_count} possible gateways"
|
||||
);
|
||||
|
||||
if gateways_count >= minimum_full_routes && mixnodes_count * 3 >= minimum_full_routes {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -529,30 +533,41 @@ impl PacketPreparer {
|
||||
let mixing_nym_nodes = descriptions.mixing_nym_nodes();
|
||||
let gateway_capable_nym_nodes = descriptions.entry_capable_nym_nodes();
|
||||
|
||||
let (mixnodes, invalid_mixnodes) = self.filter_outdated_and_malformed_mixnodes(mixnodes);
|
||||
let (gateways, invalid_gateways) = self.filter_outdated_and_malformed_gateways(gateways);
|
||||
let (mut mixnodes_to_test_details, invalid_mixnodes) =
|
||||
self.filter_outdated_and_malformed_mixnodes(mixnodes);
|
||||
let (mut gateways_to_test_details, invalid_gateways) =
|
||||
self.filter_outdated_and_malformed_gateways(gateways);
|
||||
|
||||
let mut tested_mixnodes = mixnodes.iter().map(|node| node.into()).collect::<Vec<_>>();
|
||||
let mut tested_gateways = gateways.iter().map(|node| node.into()).collect::<Vec<_>>();
|
||||
// summary of nodes that got tested
|
||||
let mut mixnodes_under_test = mixnodes_to_test_details
|
||||
.iter()
|
||||
.map(|node| node.into())
|
||||
.collect::<Vec<_>>();
|
||||
let mut gateways_under_test = gateways_to_test_details
|
||||
.iter()
|
||||
.map(|node| node.into())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// try to add nym-nodes into the fold
|
||||
if let Some(rewarded_set) = rewarded_set {
|
||||
let mut rng = thread_rng();
|
||||
for mix in mixing_nym_nodes {
|
||||
if let Some(parsed) = self.nym_node_to_legacy_mix(&mut rng, &rewarded_set, mix) {
|
||||
tested_mixnodes.push(TestableNode::from(&parsed));
|
||||
mixnodes_under_test.push(TestableNode::from(&parsed));
|
||||
mixnodes_to_test_details.push(parsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for gateway in gateway_capable_nym_nodes {
|
||||
if let Some(parsed) = self.nym_node_to_legacy_gateway(gateway) {
|
||||
tested_gateways.push((&parsed, gateway.node_id).into())
|
||||
gateways_under_test.push((&parsed, gateway.node_id).into());
|
||||
gateways_to_test_details.push((parsed, gateway.node_id));
|
||||
}
|
||||
}
|
||||
|
||||
let packets_to_create = (test_routes.len() * self.per_node_test_packets)
|
||||
* (tested_mixnodes.len() + tested_gateways.len());
|
||||
* (mixnodes_under_test.len() + gateways_under_test.len());
|
||||
info!("Need to create {} mix packets", packets_to_create);
|
||||
|
||||
let mut all_gateway_packets = HashMap::new();
|
||||
@@ -574,7 +589,7 @@ impl PacketPreparer {
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let mixnode_test_packets = mix_tester
|
||||
.mixnodes_test_packets(
|
||||
&mixnodes,
|
||||
&mixnodes_to_test_details,
|
||||
route_ext,
|
||||
self.per_node_test_packets as u32,
|
||||
None,
|
||||
@@ -588,7 +603,7 @@ impl PacketPreparer {
|
||||
gateway_packets.push_packets(mix_packets);
|
||||
|
||||
// and generate test packets for gateways (note the variable recipient)
|
||||
for (gateway, node_id) in &gateways {
|
||||
for (gateway, node_id) in &gateways_to_test_details {
|
||||
let recipient = self.create_packet_sender(gateway);
|
||||
let gateway_identity = gateway.identity_key;
|
||||
let gateway_address = gateway.clients_address();
|
||||
@@ -624,8 +639,8 @@ impl PacketPreparer {
|
||||
|
||||
PreparedPackets {
|
||||
packets,
|
||||
tested_mixnodes,
|
||||
tested_gateways,
|
||||
mixnodes_under_test,
|
||||
gateways_under_test,
|
||||
invalid_mixnodes,
|
||||
invalid_gateways,
|
||||
}
|
||||
|
||||
@@ -145,6 +145,7 @@ impl NodeDescriptionTopologyExt for NymNodeDescription {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DescribedNodes {
|
||||
nodes: HashMap<NodeId, NymNodeDescription>,
|
||||
}
|
||||
@@ -290,6 +291,7 @@ async fn try_get_description(
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct RefreshData {
|
||||
host: String,
|
||||
node_id: NodeId,
|
||||
|
||||
@@ -210,6 +210,7 @@ impl ResolvedNodeDescribedInfo {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct UnwrappedResolvedNodeDescribedInfo {
|
||||
pub(crate) build_info: BinaryBuildInformationOwned,
|
||||
pub(crate) roles: DeclaredRoles,
|
||||
|
||||
+3
-3
@@ -209,7 +209,7 @@ pub(crate) async fn produce_node_annotations(
|
||||
legacy_mix.mix_id(),
|
||||
NodeAnnotation {
|
||||
last_24h_performance: perf,
|
||||
current_role: rewarded_set.role(legacy_mix.mix_id()),
|
||||
current_role: rewarded_set.role(legacy_mix.mix_id()).map(|r| r.into()),
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -229,7 +229,7 @@ pub(crate) async fn produce_node_annotations(
|
||||
legacy_gateway.node_id,
|
||||
NodeAnnotation {
|
||||
last_24h_performance: perf,
|
||||
current_role: rewarded_set.role(legacy_gateway.node_id),
|
||||
current_role: rewarded_set.role(legacy_gateway.node_id).map(|r| r.into()),
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -249,7 +249,7 @@ pub(crate) async fn produce_node_annotations(
|
||||
nym_node.node_id(),
|
||||
NodeAnnotation {
|
||||
last_24h_performance: perf,
|
||||
current_role: rewarded_set.role(nym_node.node_id()),
|
||||
current_role: rewarded_set.role(nym_node.node_id()).map(|r| r.into()),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ use nym_serde_helpers::date::DATE_FORMAT;
|
||||
use reqwest::StatusCode;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::Error;
|
||||
use std::fmt::Display;
|
||||
use thiserror::Error;
|
||||
use time::{Date, OffsetDateTime};
|
||||
@@ -438,7 +439,7 @@ pub enum NymApiStorageError {
|
||||
|
||||
// I don't think we want to expose errors to the user about what really happened
|
||||
#[error("experienced internal database error")]
|
||||
InternalDatabaseError(#[from] sqlx::Error),
|
||||
InternalDatabaseError(sqlx::Error),
|
||||
|
||||
// the same is true here (also note that the message is subtly different so we would be able to distinguish them)
|
||||
#[error("experienced internal storage error")]
|
||||
@@ -449,6 +450,14 @@ pub enum NymApiStorageError {
|
||||
StartupMigrationFailure(#[from] sqlx::migrate::MigrateError),
|
||||
}
|
||||
|
||||
impl From<sqlx::Error> for NymApiStorageError {
|
||||
fn from(err: Error) -> Self {
|
||||
// those should realistically never be happening so an `error!` is warranted
|
||||
error!("storage failure: {err}");
|
||||
NymApiStorageError::InternalDatabaseError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl NymApiStorageError {
|
||||
pub fn database_inconsistency<S: Into<String>>(reason: S) -> NymApiStorageError {
|
||||
NymApiStorageError::DatabaseInconsistency {
|
||||
|
||||
+1
-1
@@ -173,7 +173,7 @@ impl NymContractCacheRefresher {
|
||||
layer,
|
||||
},
|
||||
rewarding_details: detail.rewarding_details,
|
||||
pending_changes: detail.pending_changes,
|
||||
pending_changes: detail.pending_changes.into(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ async fn get_gateways_described(
|
||||
.into_iter()
|
||||
.map(|bond| LegacyDescribedGateway {
|
||||
self_described: self_descriptions.get_description(&bond.node_id).cloned(),
|
||||
bond,
|
||||
bond: bond.bond,
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
|
||||
@@ -9,7 +9,7 @@ use axum::routing::get;
|
||||
use axum::{Json, Router};
|
||||
use nym_api_requests::models::{
|
||||
AnnotationResponse, NodeDatePerformanceResponse, NodePerformanceResponse, NoiseDetails,
|
||||
NymNodeData, PerformanceHistoryResponse, UptimeHistoryResponse,
|
||||
NymNodeDescription, PerformanceHistoryResponse, UptimeHistoryResponse,
|
||||
};
|
||||
use nym_api_requests::pagination::{PaginatedResponse, Pagination};
|
||||
use nym_contracts_common::NaiveFloat;
|
||||
@@ -125,32 +125,27 @@ async fn get_bonded_nodes(
|
||||
path = "/described",
|
||||
context_path = "/v1/nym-nodes",
|
||||
responses(
|
||||
(status = 200, body = PaginatedResponse<NymNodeData>)
|
||||
(status = 200, body = PaginatedResponse<NymNodeDescription>)
|
||||
),
|
||||
params(PaginationRequest)
|
||||
)]
|
||||
async fn get_described_nodes(
|
||||
State(state): State<AppState>,
|
||||
Query(pagination): Query<PaginationRequest>,
|
||||
) -> AxumResult<Json<PaginatedResponse<NymNodeData>>> {
|
||||
) -> AxumResult<Json<PaginatedResponse<NymNodeDescription>>> {
|
||||
// TODO: implement it
|
||||
let _ = pagination;
|
||||
|
||||
let cache = state.described_nodes_cache.get().await?;
|
||||
let descriptions = cache.all_nodes();
|
||||
|
||||
let data = descriptions
|
||||
.map(|n| &n.description)
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
let descriptions = cache.all_nodes().cloned().collect::<Vec<_>>();
|
||||
|
||||
Ok(Json(PaginatedResponse {
|
||||
pagination: Pagination {
|
||||
total: data.len(),
|
||||
total: descriptions.len(),
|
||||
page: 0,
|
||||
size: data.len(),
|
||||
size: descriptions.len(),
|
||||
},
|
||||
data,
|
||||
data: descriptions,
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
@@ -234,9 +234,9 @@ impl Config {
|
||||
fn default_http_socket_addr() -> SocketAddr {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(debug_assertions)] {
|
||||
SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8080)
|
||||
SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8000)
|
||||
} else {
|
||||
SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 8080)
|
||||
SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 8000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,9 +119,8 @@ impl StorageManager {
|
||||
start_ts_secs: i64,
|
||||
end_ts_secs: i64,
|
||||
) -> Result<Vec<AvgGatewayReliability>, sqlx::Error> {
|
||||
// we can't use `query_as!` macro because we don't apply all required table changes during sqlx migrations.
|
||||
// some (like v3 directory) happens at runtime
|
||||
let result = sqlx::query_as(
|
||||
let result = sqlx::query_as!(
|
||||
AvgGatewayReliability,
|
||||
r#"
|
||||
SELECT
|
||||
d.node_id as "node_id: NodeId",
|
||||
@@ -135,9 +134,9 @@ impl StorageManager {
|
||||
timestamp <= ?
|
||||
GROUP BY 1
|
||||
"#,
|
||||
start_ts_secs,
|
||||
end_ts_secs
|
||||
)
|
||||
.bind(start_ts_secs)
|
||||
.bind(end_ts_secs)
|
||||
.fetch_all(&self.connection_pool)
|
||||
.await?;
|
||||
Ok(result)
|
||||
|
||||
@@ -30,10 +30,6 @@ pub async fn migrate_v3_database(
|
||||
let contract_gateways = nyxd_client.get_gateways().await?;
|
||||
let nym_nodes = nyxd_client.get_nymnodes().await?;
|
||||
|
||||
if preassigned_ids.len() != contract_gateways.len() {
|
||||
bail!("CONTRACT DATA CORRUPTION: THE NUMBER OF PREASSIGNED GATEWAY IDS IS DIFFERENT THAN THE NUMBER OF GATEWAYS")
|
||||
}
|
||||
|
||||
// assign node_id to every gateway
|
||||
let all_known = storage.get_all_known_gateways().await?;
|
||||
for gateway in all_known {
|
||||
|
||||
Generated
-5388
File diff suppressed because it is too large
Load Diff
@@ -1,62 +0,0 @@
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
opt-level = "s"
|
||||
overflow-checks = true
|
||||
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
|
||||
[workspace]
|
||||
|
||||
resolver = "2"
|
||||
members = [
|
||||
"nym-credential-proxy",
|
||||
"nym-credential-proxy-requests",
|
||||
"vpn-api-lib-wasm"
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
authors = ["Nym Technologies SA"]
|
||||
repository = "https://github.com/nymtech/nym"
|
||||
homepage = "https://nymtech.net"
|
||||
documentation = "https://nymtech.net"
|
||||
edition = "2021"
|
||||
license = "GPL-3.0"
|
||||
|
||||
[workspace.dependencies]
|
||||
async-trait = "0.1.80"
|
||||
axum = "0.7.5"
|
||||
anyhow = "1.0.71"
|
||||
bip39 = "2.0.0"
|
||||
bs58 = "0.5.1"
|
||||
colored = "2.1.0"
|
||||
cfg-if = "1.0.0"
|
||||
clap = "4.5.4"
|
||||
dotenv = "0.15.0"
|
||||
futures = "0.3.30"
|
||||
humantime = "2.1.0"
|
||||
thiserror = "1.0.59"
|
||||
rand = "0.8.5"
|
||||
reqwest = "0.12.4"
|
||||
schemars = "0.8.17"
|
||||
strum = "0.26.3"
|
||||
strum_macros = "0.26.4"
|
||||
serde = "1.0.200"
|
||||
serde_json = "1.0.117"
|
||||
sqlx = "0.7.4"
|
||||
tempfile = "3.12.0"
|
||||
time = "0.3.36"
|
||||
tracing = "0.1.40"
|
||||
tsify = "0.4.5"
|
||||
tokio = "1.37.0"
|
||||
tokio-util = "0.7.10"
|
||||
tower = "0.5.0"
|
||||
tower-http = "0.5.2"
|
||||
uuid = "1.8.0"
|
||||
url = "2.5.2"
|
||||
utoipa = "4.2.0"
|
||||
utoipa-swagger-ui = "7.0.1"
|
||||
zeroize = "1.6.0"
|
||||
|
||||
wasm-bindgen = "0.2.93"
|
||||
wasmtimer = "0.2.0"
|
||||
@@ -27,7 +27,7 @@ utoipa = { workspace = true, optional = true }
|
||||
nym-credentials = { path = "../../common/credentials" }
|
||||
nym-credentials-interface = { path = "../../common/credentials-interface" }
|
||||
nym-http-api-common = { path = "../../common/http-api-common", optional = true }
|
||||
nym-http-api-client = { path = "../../common/http-api-client" }
|
||||
nym-http-api-client = { path = "../../common/http-api-client" }
|
||||
nym-serde-helpers = { path = "../../common/serde-helpers", features = ["bs58"] }
|
||||
|
||||
[target."cfg(target_arch = \"wasm32\")".dependencies.wasmtimer]
|
||||
|
||||
@@ -19,7 +19,7 @@ bs58.workspace = true
|
||||
cfg-if = { workspace = true }
|
||||
colored.workspace = true
|
||||
clap = { workspace = true, features = ["derive", "env"] }
|
||||
dotenv.workspace = true
|
||||
dotenvy.workspace = true
|
||||
futures.workspace = true
|
||||
humantime.workspace = true
|
||||
rand.workspace = true
|
||||
|
||||
@@ -40,7 +40,7 @@ impl SqliteStorageManager {
|
||||
sqlx::query_as!(
|
||||
MinimalWalletShare,
|
||||
r#"
|
||||
SELECT t1.node_id, t1.blinded_signature, t1.epoch_id, t1.expiration_date
|
||||
SELECT t1.node_id, t1.blinded_signature, t1.epoch_id, t1.expiration_date as "expiration_date!: Date"
|
||||
FROM partial_blinded_wallet as t1
|
||||
JOIN ticketbook_deposit as t2
|
||||
on t1.corresponding_deposit = t2.deposit_id
|
||||
@@ -79,10 +79,11 @@ impl SqliteStorageManager {
|
||||
device_id: &str,
|
||||
credential_id: &str,
|
||||
) -> Result<Vec<MinimalWalletShare>, sqlx::Error> {
|
||||
// https://docs.rs/sqlx/latest/sqlx/macro.query.html#force-a-differentcustom-type
|
||||
sqlx::query_as!(
|
||||
MinimalWalletShare,
|
||||
r#"
|
||||
SELECT t1.node_id, t1.blinded_signature, t1.epoch_id, t1.expiration_date
|
||||
SELECT t1.node_id, t1.blinded_signature, t1.epoch_id, t1.expiration_date as "expiration_date!: Date"
|
||||
FROM partial_blinded_wallet as t1
|
||||
JOIN ticketbook_deposit as t2
|
||||
on t1.corresponding_deposit = t2.deposit_id
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-node-status-agent"
|
||||
version = "0.1.0"
|
||||
version = "0.1.2"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
|
||||
@@ -97,6 +97,7 @@ async fn submit_results(
|
||||
) -> anyhow::Result<()> {
|
||||
let target_url = format!("{}/{}/{}", server_addr, URL_BASE, testrun_id);
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
let res = client
|
||||
.post(target_url)
|
||||
.body(probe_outcome)
|
||||
|
||||
@@ -40,6 +40,12 @@ impl GwProbe {
|
||||
match command.spawn() {
|
||||
Ok(child) => {
|
||||
if let Ok(output) = child.wait_with_output() {
|
||||
if !output.status.success() {
|
||||
let out = String::from_utf8_lossy(&output.stdout);
|
||||
let err = String::from_utf8_lossy(&output.stderr);
|
||||
tracing::error!("Probe exited with {:?}:\n{}\n{}", output.status, out, err);
|
||||
}
|
||||
|
||||
return String::from_utf8(output.stdout)
|
||||
.unwrap_or("Unable to get log from test run".to_string());
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-node-status-api"
|
||||
version = "0.1.0"
|
||||
version = "0.1.4"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
@@ -23,7 +23,8 @@ futures-util = { workspace = true }
|
||||
moka = { workspace = true, features = ["future"] }
|
||||
nym-bin-common = { path = "../common/bin-common", features = ["models"]}
|
||||
nym-common-models = { path = "../common/models" }
|
||||
nym-explorer-client = { path = "../explorer-api/explorer-client" }
|
||||
# nym-explorer-client = { path = "../explorer-api/explorer-client" }
|
||||
nym-explorer-client = { git = "https://github.com/nymtech/nym", branch = "release/2024.13-magura" }
|
||||
# TODO dz: before Nym API client breaking changes. Update to latest develop once new Nym API is live
|
||||
nym-network-defaults = { git = "https://github.com/nymtech/nym", branch = "pre-dir-v2-fork" }
|
||||
nym-validator-client = { git = "https://github.com/nymtech/nym", branch = "pre-dir-v2-fork" }
|
||||
|
||||
@@ -25,7 +25,6 @@ async fn main() -> Result<()> {
|
||||
// not a valid windows path... but hey, it works...
|
||||
println!("cargo::rustc-env=DATABASE_URL=sqlite:///{}", &database_path);
|
||||
|
||||
rerun_if_changed();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -33,11 +32,6 @@ fn read_env_var(var: &str) -> Result<String> {
|
||||
std::env::var(var).map_err(|_| anyhow!("You need to set {} env var", var))
|
||||
}
|
||||
|
||||
fn rerun_if_changed() {
|
||||
println!("cargo::rerun-if-changed=migrations");
|
||||
println!("cargo::rerun-if-changed=src/db/queries");
|
||||
}
|
||||
|
||||
/// use `./enter_db.sh` to inspect DB
|
||||
async fn write_db_path_to_file(out_dir: &str, db_filename: &str) -> anyhow::Result<()> {
|
||||
let mut file = File::create("enter_db.sh").await?;
|
||||
|
||||
@@ -7,7 +7,7 @@ export RUST_LOG=${RUST_LOG:-debug}
|
||||
export NYM_API_CLIENT_TIMEOUT=60
|
||||
export EXPLORER_CLIENT_TIMEOUT=60
|
||||
|
||||
export ENVIRONMENT="mainnet.env"
|
||||
export ENVIRONMENT="qa.env"
|
||||
|
||||
function run_bare() {
|
||||
# export necessary env vars
|
||||
|
||||
@@ -103,7 +103,7 @@ CREATE TABLE
|
||||
CREATE TABLE testruns
|
||||
(
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
gateway_id INTEGER,
|
||||
gateway_id INTEGER NOT NULL,
|
||||
status INTEGER NOT NULL, -- 0=pending, 1=in-progress, 2=complete
|
||||
timestamp_utc INTEGER NOT NULL,
|
||||
ip_address VARCHAR NOT NULL,
|
||||
|
||||
@@ -300,6 +300,7 @@ pub(crate) mod gateway {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)] // not dead code, this is SQL data model
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TestRunDto {
|
||||
pub id: i64,
|
||||
@@ -315,7 +316,7 @@ pub struct TestRunDto {
|
||||
pub(crate) enum TestRunStatus {
|
||||
Complete = 2,
|
||||
InProgress = 1,
|
||||
Pending = 0,
|
||||
Queued = 0,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
||||
@@ -7,8 +7,28 @@ use crate::{
|
||||
};
|
||||
use futures_util::TryStreamExt;
|
||||
use nym_validator_client::models::DescribedGateway;
|
||||
use sqlx::{pool::PoolConnection, Sqlite};
|
||||
use tracing::error;
|
||||
|
||||
pub(crate) async fn select_gateway_identity(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
gateway_pk: i64,
|
||||
) -> anyhow::Result<String> {
|
||||
let record = sqlx::query!(
|
||||
r#"SELECT
|
||||
gateway_identity_key
|
||||
FROM
|
||||
gateways
|
||||
WHERE
|
||||
id = ?"#,
|
||||
gateway_pk
|
||||
)
|
||||
.fetch_one(conn.as_mut())
|
||||
.await?;
|
||||
|
||||
Ok(record.gateway_identity_key)
|
||||
}
|
||||
|
||||
pub(crate) async fn insert_gateways(
|
||||
pool: &DbPool,
|
||||
gateways: Vec<GatewayRecord>,
|
||||
|
||||
@@ -5,7 +5,7 @@ mod summary;
|
||||
pub(crate) mod testruns;
|
||||
|
||||
pub(crate) use gateways::{
|
||||
ensure_gateways_still_bonded, get_all_gateways, insert_gateways,
|
||||
ensure_gateways_still_bonded, get_all_gateways, insert_gateways, select_gateway_identity,
|
||||
write_blacklisted_gateways_to_db,
|
||||
};
|
||||
pub(crate) use misc::insert_summaries;
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
use crate::db::DbPool;
|
||||
use crate::http::models::TestrunAssignment;
|
||||
use crate::{
|
||||
db::models::{TestRunDto, TestRunStatus},
|
||||
testruns::now_utc,
|
||||
};
|
||||
use anyhow::Context;
|
||||
use chrono::Duration;
|
||||
use sqlx::{pool::PoolConnection, Sqlite};
|
||||
|
||||
pub(crate) async fn get_testrun_by_id(
|
||||
pub(crate) async fn get_in_progress_testrun_by_id(
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
testrun_id: i64,
|
||||
) -> anyhow::Result<TestRunDto> {
|
||||
@@ -20,22 +22,58 @@ pub(crate) async fn get_testrun_by_id(
|
||||
ip_address as "ip_address!",
|
||||
log as "log!"
|
||||
FROM testruns
|
||||
WHERE id = ?
|
||||
WHERE
|
||||
id = ?
|
||||
AND
|
||||
status = ?
|
||||
ORDER BY timestamp_utc"#,
|
||||
testrun_id
|
||||
testrun_id,
|
||||
TestRunStatus::InProgress as i64,
|
||||
)
|
||||
.fetch_one(conn.as_mut())
|
||||
.await
|
||||
.context(format!("Couldn't retrieve testrun {testrun_id}"))
|
||||
}
|
||||
|
||||
pub(crate) async fn update_testruns_older_than(db: &DbPool, age: Duration) -> anyhow::Result<u64> {
|
||||
let mut conn = db.acquire().await?;
|
||||
let previous_run = now_utc() - age;
|
||||
let cutoff_timestamp = previous_run.timestamp();
|
||||
|
||||
let res = sqlx::query!(
|
||||
r#"UPDATE
|
||||
testruns
|
||||
SET
|
||||
status = ?
|
||||
WHERE
|
||||
status = ?
|
||||
AND
|
||||
timestamp_utc < ?
|
||||
"#,
|
||||
TestRunStatus::Queued as i64,
|
||||
TestRunStatus::InProgress as i64,
|
||||
cutoff_timestamp
|
||||
)
|
||||
.execute(conn.as_mut())
|
||||
.await?;
|
||||
|
||||
let stale_testruns = res.rows_affected();
|
||||
if stale_testruns > 0 {
|
||||
tracing::debug!(
|
||||
"Refreshed {} stale testruns, scheduled before {} but not yet finished",
|
||||
stale_testruns,
|
||||
previous_run
|
||||
);
|
||||
}
|
||||
|
||||
Ok(stale_testruns)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_oldest_testrun_and_make_it_pending(
|
||||
// TODO dz accept mut reference, repeat in all similar functions
|
||||
conn: PoolConnection<Sqlite>,
|
||||
conn: &mut PoolConnection<Sqlite>,
|
||||
) -> anyhow::Result<Option<TestrunAssignment>> {
|
||||
let mut conn = conn;
|
||||
let assignment = sqlx::query_as!(
|
||||
TestrunAssignment,
|
||||
// find & mark as "In progress" in the same transaction to avoid race conditions
|
||||
let returning = sqlx::query!(
|
||||
r#"UPDATE testruns
|
||||
SET status = ?
|
||||
WHERE rowid =
|
||||
@@ -47,16 +85,36 @@ pub(crate) async fn get_oldest_testrun_and_make_it_pending(
|
||||
LIMIT 1
|
||||
)
|
||||
RETURNING
|
||||
id as "testrun_id!",
|
||||
gateway_id as "gateway_pk_id!"
|
||||
id as "id!",
|
||||
gateway_id
|
||||
"#,
|
||||
TestRunStatus::InProgress as i64,
|
||||
TestRunStatus::Pending as i64,
|
||||
TestRunStatus::Queued as i64,
|
||||
)
|
||||
.fetch_optional(&mut *conn)
|
||||
.fetch_optional(conn.as_mut())
|
||||
.await?;
|
||||
|
||||
Ok(assignment)
|
||||
if let Some(testrun) = returning {
|
||||
let gw_identity = sqlx::query!(
|
||||
r#"
|
||||
SELECT
|
||||
id,
|
||||
gateway_identity_key
|
||||
FROM gateways
|
||||
WHERE id = ?
|
||||
LIMIT 1"#,
|
||||
testrun.gateway_id
|
||||
)
|
||||
.fetch_one(conn.as_mut())
|
||||
.await?;
|
||||
|
||||
return Ok(Some(TestrunAssignment {
|
||||
testrun_id: testrun.id,
|
||||
gateway_identity_key: gw_identity.gateway_identity_key,
|
||||
}));
|
||||
} else {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn update_testrun_status(
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
use anyhow::anyhow;
|
||||
use axum::{response::Redirect, Router};
|
||||
use tokio::net::ToSocketAddrs;
|
||||
use tower_http::{cors::CorsLayer, trace::TraceLayer};
|
||||
use tower_http::{
|
||||
cors::CorsLayer,
|
||||
trace::{DefaultOnResponse, TraceLayer},
|
||||
};
|
||||
use utoipa::OpenApi;
|
||||
use utoipa_swagger_ui::SwaggerUi;
|
||||
|
||||
@@ -58,7 +61,10 @@ impl RouterBuilder {
|
||||
// CORS layer needs to wrap other API layers
|
||||
.layer(setup_cors())
|
||||
// logger should be outermost layer
|
||||
.layer(TraceLayer::new_for_http())
|
||||
.layer(
|
||||
TraceLayer::new_for_http()
|
||||
.on_response(DefaultOnResponse::new().level(tracing::Level::DEBUG)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use axum::extract::DefaultBodyLimit;
|
||||
use axum::Json;
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
@@ -23,31 +24,32 @@ pub(crate) fn routes() -> Router<AppState> {
|
||||
Router::new()
|
||||
.route("/", axum::routing::get(request_testrun))
|
||||
.route("/:testrun_id", axum::routing::post(submit_testrun))
|
||||
.layer(DefaultBodyLimit::max(1024 * 1024 * 5))
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
async fn request_testrun(State(state): State<AppState>) -> HttpResult<Json<TestrunAssignment>> {
|
||||
// TODO dz log agent's key
|
||||
// TODO dz log agent's network probe version
|
||||
tracing::debug!("Agent X requested testrun");
|
||||
tracing::debug!("Agent requested testrun");
|
||||
|
||||
let db = state.db_pool();
|
||||
let conn = db
|
||||
let mut conn = db
|
||||
.acquire()
|
||||
.await
|
||||
.map_err(HttpError::internal_with_logging)?;
|
||||
|
||||
return match db::queries::testruns::get_oldest_testrun_and_make_it_pending(conn).await {
|
||||
return match db::queries::testruns::get_oldest_testrun_and_make_it_pending(&mut conn).await {
|
||||
Ok(res) => {
|
||||
if let Some(testrun) = res {
|
||||
// TODO dz consider adding a column to testruns table with agent's public key
|
||||
tracing::debug!(
|
||||
"🏃 Assigned testrun row_id {} to agent X",
|
||||
&testrun.testrun_id
|
||||
"🏃 Assigned testrun row_id {} gateway {} to agent",
|
||||
&testrun.testrun_id,
|
||||
testrun.gateway_identity_key
|
||||
);
|
||||
Ok(Json(testrun))
|
||||
} else {
|
||||
Err(HttpError::not_found("No testruns available"))
|
||||
Err(HttpError::no_available_testruns())
|
||||
}
|
||||
}
|
||||
Err(err) => Err(HttpError::internal_with_logging(err)),
|
||||
@@ -61,25 +63,32 @@ async fn submit_testrun(
|
||||
State(state): State<AppState>,
|
||||
body: String,
|
||||
) -> HttpResult<StatusCode> {
|
||||
tracing::debug!(
|
||||
"Agent submitted testrun {}. Total length: {}",
|
||||
testrun_id,
|
||||
body.len(),
|
||||
);
|
||||
// TODO dz store testrun results
|
||||
|
||||
let db = state.db_pool();
|
||||
let mut conn = db
|
||||
.acquire()
|
||||
.await
|
||||
.map_err(HttpError::internal_with_logging)?;
|
||||
|
||||
let testrun = queries::testruns::get_testrun_by_id(&mut conn, testrun_id)
|
||||
let testrun = queries::testruns::get_in_progress_testrun_by_id(&mut conn, testrun_id)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::error!("{e}");
|
||||
HttpError::not_found(testrun_id)
|
||||
})?;
|
||||
|
||||
let gw_identity = db::queries::select_gateway_identity(&mut conn, testrun.gateway_id)
|
||||
.await
|
||||
.map_err(|_| {
|
||||
// should never happen:
|
||||
HttpError::internal_with_logging("No gateway found for testrun")
|
||||
})?;
|
||||
tracing::debug!(
|
||||
"Agent submitted testrun {} for gateway {} ({} bytes)",
|
||||
testrun_id,
|
||||
gw_identity,
|
||||
body.len(),
|
||||
);
|
||||
|
||||
// TODO dz this should be part of a single transaction: commit after everything is done
|
||||
queries::testruns::update_testrun_status(&mut conn, testrun_id, TestRunStatus::Complete)
|
||||
.await
|
||||
@@ -99,7 +108,7 @@ async fn submit_testrun(
|
||||
tracing::info!(
|
||||
"✅ Testrun row_id {} for gateway {} complete",
|
||||
testrun.id,
|
||||
testrun.gateway_id
|
||||
gw_identity
|
||||
);
|
||||
|
||||
Ok(StatusCode::CREATED)
|
||||
|
||||
@@ -8,9 +8,9 @@ pub(crate) struct HttpError {
|
||||
}
|
||||
|
||||
impl HttpError {
|
||||
pub(crate) fn invalid_input(message: String) -> Self {
|
||||
pub(crate) fn invalid_input(msg: impl Display) -> Self {
|
||||
Self {
|
||||
message,
|
||||
message: serde_json::json!({"message": msg.to_string()}).to_string(),
|
||||
status: axum::http::StatusCode::BAD_REQUEST,
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,12 @@ impl HttpError {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn no_available_testruns() -> Self {
|
||||
Self {
|
||||
message: serde_json::json!({"message": "No available testruns"}).to_string(),
|
||||
status: axum::http::StatusCode::SERVICE_UNAVAILABLE,
|
||||
}
|
||||
}
|
||||
pub(crate) fn not_found(msg: impl Display) -> Self {
|
||||
Self {
|
||||
message: serde_json::json!({"message": msg.to_string()}).to_string(),
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use crate::db::models::TestRunDto;
|
||||
use nym_node_requests::api::v1::node::models::NodeDescription;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
@@ -75,12 +74,3 @@ pub(crate) struct SummaryHistory {
|
||||
pub value_json: serde_json::Value,
|
||||
pub timestamp_utc: String,
|
||||
}
|
||||
|
||||
impl From<TestRunDto> for TestrunAssignment {
|
||||
fn from(value: TestRunDto) -> Self {
|
||||
Self {
|
||||
gateway_pk_id: value.gateway_id,
|
||||
testrun_id: value.id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ async fn run(
|
||||
let explorer_client =
|
||||
ExplorerClient::new_with_timeout(default_explorer_url, explorer_client_timeout)?;
|
||||
let explorer_gateways = explorer_client
|
||||
.get_gateways()
|
||||
.unstable_get_gateways()
|
||||
.await
|
||||
.log_error("get_gateways")?;
|
||||
|
||||
|
||||
@@ -11,10 +11,12 @@ pub(crate) use queue::now_utc;
|
||||
pub(crate) async fn spawn(pool: DbPool, refresh_interval: Duration) {
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
tracing::info!("Spawning testruns...");
|
||||
if let Err(e) = refresh_stale_testruns(&pool, refresh_interval).await {
|
||||
tracing::error!("{e}");
|
||||
}
|
||||
|
||||
if let Err(e) = run(&pool).await {
|
||||
tracing::error!("Cron job failed: {}", e);
|
||||
tracing::error!("Assigning testruns failed: {}", e);
|
||||
}
|
||||
tracing::debug!("Sleeping for {}s...", refresh_interval.as_secs());
|
||||
tokio::time::sleep(refresh_interval).await;
|
||||
@@ -24,9 +26,9 @@ pub(crate) async fn spawn(pool: DbPool, refresh_interval: Duration) {
|
||||
|
||||
// TODO dz make number of max agents configurable
|
||||
|
||||
// TODO dz periodically clean up stale pending testruns
|
||||
#[instrument(level = "debug", name = "testrun_queue", skip_all)]
|
||||
async fn run(pool: &DbPool) -> anyhow::Result<()> {
|
||||
tracing::info!("Spawning testruns...");
|
||||
if pool.is_closed() {
|
||||
tracing::debug!("DB pool closed, returning early");
|
||||
return Ok(());
|
||||
@@ -74,3 +76,11 @@ async fn run(pool: &DbPool) -> anyhow::Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
async fn refresh_stale_testruns(pool: &DbPool, refresh_interval: Duration) -> anyhow::Result<()> {
|
||||
let chrono_duration = chrono::Duration::from_std(refresh_interval)?;
|
||||
crate::db::queries::testruns::update_testruns_older_than(pool, chrono_duration).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ pub(crate) async fn try_queue_testrun(
|
||||
//
|
||||
// save test run
|
||||
//
|
||||
let status = TestRunStatus::Pending as u32;
|
||||
let status = TestRunStatus::Queued as u32;
|
||||
let log = format!(
|
||||
"Test for {identity_key} requested at {} UTC\n\n",
|
||||
timestamp_pretty
|
||||
@@ -103,7 +103,7 @@ pub(crate) async fn try_queue_testrun(
|
||||
Ok(TestRun {
|
||||
id: id as u32,
|
||||
identity_key,
|
||||
status: format!("{}", TestRunStatus::Pending),
|
||||
status: format!("{}", TestRunStatus::Queued),
|
||||
log,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::api::v1::node::models::{HostInformation, LegacyHostInformation};
|
||||
use crate::api::v1::node::models::{
|
||||
HostInformation, LegacyHostInformation, LegacyHostInformationV2,
|
||||
};
|
||||
use crate::error::Error;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use schemars::JsonSchema;
|
||||
@@ -61,9 +63,18 @@ impl SignedHostInformation {
|
||||
return true;
|
||||
}
|
||||
|
||||
// attempt to verify legacy signature
|
||||
// attempt to verify legacy signatures
|
||||
let legacy_v2 = SignedData {
|
||||
data: LegacyHostInformationV2::from(self.data.clone()),
|
||||
signature: self.signature.clone(),
|
||||
};
|
||||
|
||||
if legacy_v2.verify(&self.keys.ed25519_identity) {
|
||||
return true;
|
||||
}
|
||||
|
||||
SignedData {
|
||||
data: LegacyHostInformation::from(self.data.clone()),
|
||||
data: LegacyHostInformation::from(legacy_v2.data),
|
||||
signature: self.signature.clone(),
|
||||
}
|
||||
.verify(&self.keys.ed25519_identity)
|
||||
@@ -133,6 +144,81 @@ mod tests {
|
||||
assert!(signed_info.verify_host_information());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dummy_legacy_v2_signed_host_verification() {
|
||||
let mut rng = rand_chacha::ChaCha20Rng::from_seed([0u8; 32]);
|
||||
let ed22519 = ed25519::KeyPair::new(&mut rng);
|
||||
let x25519_sphinx = x25519::KeyPair::new(&mut rng);
|
||||
let x25519_noise = x25519::KeyPair::new(&mut rng);
|
||||
|
||||
let legacy_info_no_noise = crate::api::v1::node::models::LegacyHostInformationV2 {
|
||||
ip_address: vec!["1.1.1.1".parse().unwrap()],
|
||||
hostname: Some("foomp.com".to_string()),
|
||||
keys: crate::api::v1::node::models::LegacyHostKeysV2 {
|
||||
ed25519_identity: ed22519.public_key().to_base58_string(),
|
||||
x25519_sphinx: x25519_sphinx.public_key().to_base58_string(),
|
||||
x25519_noise: "".to_string(),
|
||||
},
|
||||
};
|
||||
|
||||
let legacy_info_noise = crate::api::v1::node::models::LegacyHostInformationV2 {
|
||||
ip_address: vec!["1.1.1.1".parse().unwrap()],
|
||||
hostname: Some("foomp.com".to_string()),
|
||||
keys: crate::api::v1::node::models::LegacyHostKeysV2 {
|
||||
ed25519_identity: ed22519.public_key().to_base58_string(),
|
||||
x25519_sphinx: x25519_sphinx.public_key().to_base58_string(),
|
||||
x25519_noise: x25519_noise.public_key().to_base58_string(),
|
||||
},
|
||||
};
|
||||
|
||||
let host_info_no_noise = crate::api::v1::node::models::HostInformation {
|
||||
ip_address: legacy_info_no_noise.ip_address.clone(),
|
||||
hostname: legacy_info_no_noise.hostname.clone(),
|
||||
keys: crate::api::v1::node::models::HostKeys {
|
||||
ed25519_identity: legacy_info_no_noise.keys.ed25519_identity.parse().unwrap(),
|
||||
x25519_sphinx: legacy_info_no_noise.keys.x25519_sphinx.parse().unwrap(),
|
||||
x25519_noise: None,
|
||||
},
|
||||
};
|
||||
|
||||
let host_info_noise = crate::api::v1::node::models::HostInformation {
|
||||
ip_address: legacy_info_noise.ip_address.clone(),
|
||||
hostname: legacy_info_noise.hostname.clone(),
|
||||
keys: crate::api::v1::node::models::HostKeys {
|
||||
ed25519_identity: legacy_info_noise.keys.ed25519_identity.parse().unwrap(),
|
||||
x25519_sphinx: legacy_info_noise.keys.x25519_sphinx.parse().unwrap(),
|
||||
x25519_noise: Some(legacy_info_noise.keys.x25519_noise.parse().unwrap()),
|
||||
},
|
||||
};
|
||||
|
||||
// signature on legacy data
|
||||
let signature_no_noise = SignedData::new(legacy_info_no_noise, ed22519.private_key())
|
||||
.unwrap()
|
||||
.signature;
|
||||
|
||||
let signature_noise = SignedData::new(legacy_info_noise, ed22519.private_key())
|
||||
.unwrap()
|
||||
.signature;
|
||||
|
||||
// signed blob with the 'current' structure
|
||||
let current_struct_no_noise = SignedData {
|
||||
data: host_info_no_noise,
|
||||
signature: signature_no_noise,
|
||||
};
|
||||
|
||||
let current_struct_noise = SignedData {
|
||||
data: host_info_noise,
|
||||
signature: signature_noise,
|
||||
};
|
||||
|
||||
assert!(!current_struct_no_noise.verify(ed22519.public_key()));
|
||||
assert!(current_struct_no_noise.verify_host_information());
|
||||
|
||||
// if noise key is present, the signature is actually valid
|
||||
assert!(current_struct_noise.verify(ed22519.public_key()));
|
||||
assert!(current_struct_noise.verify_host_information())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dummy_legacy_signed_host_verification() {
|
||||
let mut rng = rand_chacha::ChaCha20Rng::from_seed([0u8; 32]);
|
||||
|
||||
@@ -59,6 +59,13 @@ pub struct HostInformation {
|
||||
pub keys: HostKeys,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct LegacyHostInformationV2 {
|
||||
pub ip_address: Vec<IpAddr>,
|
||||
pub hostname: Option<String>,
|
||||
pub keys: LegacyHostKeysV2,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct LegacyHostInformation {
|
||||
pub ip_address: Vec<IpAddr>,
|
||||
@@ -66,8 +73,18 @@ pub struct LegacyHostInformation {
|
||||
pub keys: LegacyHostKeys,
|
||||
}
|
||||
|
||||
impl From<HostInformation> for LegacyHostInformation {
|
||||
impl From<HostInformation> for LegacyHostInformationV2 {
|
||||
fn from(value: HostInformation) -> Self {
|
||||
LegacyHostInformationV2 {
|
||||
ip_address: value.ip_address,
|
||||
hostname: value.hostname,
|
||||
keys: value.keys.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LegacyHostInformationV2> for LegacyHostInformation {
|
||||
fn from(value: LegacyHostInformationV2) -> Self {
|
||||
LegacyHostInformation {
|
||||
ip_address: value.ip_address,
|
||||
hostname: value.hostname,
|
||||
@@ -99,13 +116,11 @@ pub struct HostKeys {
|
||||
pub x25519_noise: Option<x25519::PublicKey>,
|
||||
}
|
||||
|
||||
impl From<HostKeys> for LegacyHostKeys {
|
||||
fn from(value: HostKeys) -> Self {
|
||||
LegacyHostKeys {
|
||||
ed25519: value.ed25519_identity.to_base58_string(),
|
||||
x25519: value.x25519_sphinx.to_base58_string(),
|
||||
}
|
||||
}
|
||||
#[derive(Serialize)]
|
||||
pub struct LegacyHostKeysV2 {
|
||||
pub ed25519_identity: String,
|
||||
pub x25519_sphinx: String,
|
||||
pub x25519_noise: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
@@ -114,6 +129,28 @@ pub struct LegacyHostKeys {
|
||||
pub x25519: String,
|
||||
}
|
||||
|
||||
impl From<HostKeys> for LegacyHostKeysV2 {
|
||||
fn from(value: HostKeys) -> Self {
|
||||
LegacyHostKeysV2 {
|
||||
ed25519_identity: value.ed25519_identity.to_base58_string(),
|
||||
x25519_sphinx: value.x25519_sphinx.to_base58_string(),
|
||||
x25519_noise: value
|
||||
.x25519_noise
|
||||
.map(|k| k.to_base58_string())
|
||||
.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LegacyHostKeysV2> for LegacyHostKeys {
|
||||
fn from(value: LegacyHostKeysV2) -> Self {
|
||||
LegacyHostKeys {
|
||||
ed25519: value.ed25519_identity,
|
||||
x25519: value.x25519_sphinx,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
|
||||
pub struct HostSystem {
|
||||
|
||||
@@ -154,7 +154,7 @@ pub fn ephemeral_entry_gateway_config(
|
||||
config: Config,
|
||||
mnemonic: &bip39::Mnemonic,
|
||||
) -> Result<EphemeralConfig, EntryGatewayError> {
|
||||
let auth_opts = LocalAuthenticatorOpts {
|
||||
let mut auth_opts = LocalAuthenticatorOpts {
|
||||
config: nym_authenticator::Config {
|
||||
base: nym_client_core_config_types::Config {
|
||||
client: base_client_config(&config),
|
||||
@@ -173,6 +173,10 @@ pub fn ephemeral_entry_gateway_config(
|
||||
custom_mixnet_path: None,
|
||||
};
|
||||
|
||||
if config.authenticator.debug.disable_poisson_rate {
|
||||
auth_opts.config.base.set_no_poisson_process();
|
||||
}
|
||||
|
||||
let wg_opts = LocalWireguardOpts {
|
||||
config: super::Wireguard {
|
||||
enabled: config.wireguard.enabled,
|
||||
|
||||
@@ -243,7 +243,7 @@ pub fn ephemeral_exit_gateway_config(
|
||||
ipr_opts.config.base.set_no_poisson_process()
|
||||
}
|
||||
|
||||
let auth_opts = LocalAuthenticatorOpts {
|
||||
let mut auth_opts = LocalAuthenticatorOpts {
|
||||
config: nym_authenticator::Config {
|
||||
base: nym_client_core_config_types::Config {
|
||||
client: base_client_config(&config),
|
||||
@@ -262,6 +262,10 @@ pub fn ephemeral_exit_gateway_config(
|
||||
custom_mixnet_path: None,
|
||||
};
|
||||
|
||||
if config.authenticator.debug.disable_poisson_rate {
|
||||
auth_opts.config.base.set_no_poisson_process();
|
||||
}
|
||||
|
||||
let pub_id_path = config
|
||||
.storage_paths
|
||||
.keys
|
||||
|
||||
@@ -46,6 +46,7 @@ pub struct Verloc {
|
||||
/// will use.
|
||||
/// Useful when the node is behind a proxy.
|
||||
#[serde(deserialize_with = "de_maybe_port")]
|
||||
#[serde(default)]
|
||||
pub announce_port: Option<u16>,
|
||||
|
||||
#[serde(default)]
|
||||
|
||||
@@ -421,6 +421,7 @@ pub struct Mixnet {
|
||||
/// will use.
|
||||
/// Useful when the node is behind a proxy.
|
||||
#[serde(deserialize_with = "de_maybe_port")]
|
||||
#[serde(default)]
|
||||
pub announce_port: Option<u16>,
|
||||
|
||||
/// Addresses to nym APIs from which the node gets the view of the network.
|
||||
|
||||
@@ -107,7 +107,6 @@ impl Config {
|
||||
nyxd_scraper: NyxdScraper {
|
||||
websocket_url,
|
||||
pruning: Default::default(),
|
||||
store_precommits: true,
|
||||
},
|
||||
base: Base {
|
||||
upstream_nyxd: nyxd_url,
|
||||
@@ -123,7 +122,6 @@ impl Config {
|
||||
rpc_url: self.base.upstream_nyxd.clone(),
|
||||
database_path: self.storage_paths.nyxd_scraper.clone(),
|
||||
pruning_options: self.nyxd_scraper.pruning,
|
||||
store_precommits: self.nyxd_scraper.store_precommits,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,14 +249,7 @@ pub struct NyxdScraper {
|
||||
// if the value is missing, use `nothing` pruning as this was the past behaviour
|
||||
#[serde(default = "PruningOptions::nothing")]
|
||||
pub pruning: PruningOptions,
|
||||
|
||||
/// Specifies whether to store pre-commits within the database.
|
||||
#[serde(default = "default_store_precommits")]
|
||||
pub store_precommits: bool,
|
||||
}
|
||||
|
||||
fn default_store_precommits() -> bool {
|
||||
true
|
||||
// TODO: debug with everything that's currently hardcoded in the scraper
|
||||
}
|
||||
|
||||
impl NyxdScraper {
|
||||
|
||||
@@ -4,13 +4,12 @@
|
||||
use crate::api_client;
|
||||
use crate::error::BackendError;
|
||||
use crate::state::WalletState;
|
||||
use nym_mixnet_contract_common::nym_node::Role;
|
||||
use nym_mixnet_contract_common::{
|
||||
reward_params::Performance, Coin, IdentityKeyRef, NodeId, Percent,
|
||||
};
|
||||
use nym_validator_client::client::NymApiClientExt;
|
||||
use nym_validator_client::models::{
|
||||
AnnotationResponse, ComputeRewardEstParam, GatewayCoreStatusResponse,
|
||||
AnnotationResponse, ComputeRewardEstParam, DisplayRole, GatewayCoreStatusResponse,
|
||||
GatewayStatusReportResponse, InclusionProbabilityResponse, MixnodeCoreStatusResponse,
|
||||
MixnodeStatusResponse, RewardEstimationResponse, StakeSaturationResponse,
|
||||
};
|
||||
@@ -110,7 +109,7 @@ pub async fn mixnode_inclusion_probability(
|
||||
pub async fn get_nymnode_role(
|
||||
node_id: NodeId,
|
||||
state: tauri::State<'_, WalletState>,
|
||||
) -> Result<Option<Role>, BackendError> {
|
||||
) -> Result<Option<DisplayRole>, BackendError> {
|
||||
let annotation = get_nymnode_annotation(node_id, state).await?;
|
||||
Ok(annotation.annotation.and_then(|n| n.current_role))
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ export type TGatewayReport = {
|
||||
most_recent: number;
|
||||
};
|
||||
|
||||
export type TNodeRole = 'entry' | 'exit' | 'layer1' | 'layer2' | 'layer3' | 'standby';
|
||||
export type TNodeRole = 'entryGateway' | 'exitGateway' | 'layer1' | 'layer2' | 'layer3' | 'standby';
|
||||
|
||||
export type MixnodeSaturationResponse = {
|
||||
saturation: string;
|
||||
|
||||
@@ -10,7 +10,13 @@ pub mod mixnet;
|
||||
pub mod tcp_proxy;
|
||||
|
||||
pub use error::{Error, Result};
|
||||
pub use nym_client_core::client::mix_traffic::transceiver::*;
|
||||
pub use nym_client_core::client::{
|
||||
mix_traffic::transceiver::*,
|
||||
topology_control::{
|
||||
GeoAwareTopologyProvider, NymApiTopologyProvider, NymApiTopologyProviderConfig,
|
||||
TopologyProvider,
|
||||
},
|
||||
};
|
||||
pub use nym_network_defaults::{
|
||||
ChainDetails, DenomDetails, DenomDetailsOwned, NymContracts, NymNetworkDetails,
|
||||
ValidatorDetails,
|
||||
|
||||
@@ -128,7 +128,9 @@ impl<S: Storage + Clone + 'static> Authenticator<S> {
|
||||
// Connect to the mixnet
|
||||
let mixnet_client = crate::mixnet_client::create_mixnet_client(
|
||||
&self.config.base,
|
||||
task_handle.get_handle().named("nym_sdk::MixnetClient"),
|
||||
task_handle
|
||||
.get_handle()
|
||||
.named("nym_sdk::MixnetClient[AUTH]"),
|
||||
self.custom_gateway_transceiver,
|
||||
self.custom_topology_provider,
|
||||
self.wait_for_gateway,
|
||||
|
||||
@@ -297,6 +297,10 @@ impl<S: Storage + Clone + 'static> MixnetListener<S> {
|
||||
credential: CredentialSpendingData,
|
||||
client_id: i64,
|
||||
) -> Result<i64> {
|
||||
ecash_verifier
|
||||
.storage()
|
||||
.create_bandwidth_entry(client_id)
|
||||
.await?;
|
||||
let bandwidth = ecash_verifier
|
||||
.storage()
|
||||
.get_available_bandwidth(client_id)
|
||||
|
||||
@@ -133,7 +133,7 @@ impl IpPacketRouter {
|
||||
// Connect to the mixnet
|
||||
let mixnet_client = crate::mixnet_client::create_mixnet_client(
|
||||
&self.config.base,
|
||||
task_handle.get_handle().named("nym_sdk::MixnetClient"),
|
||||
task_handle.get_handle().named("nym_sdk::MixnetClient[IPR]"),
|
||||
self.custom_gateway_transceiver,
|
||||
self.custom_topology_provider,
|
||||
self.wait_for_gateway,
|
||||
|
||||
@@ -239,7 +239,7 @@ impl NRServiceProviderBuilder {
|
||||
// Connect to the mixnet
|
||||
let mixnet_client = create_mixnet_client(
|
||||
&self.config.base,
|
||||
shutdown.get_handle().named("nym_sdk::MixnetClient"),
|
||||
shutdown.get_handle().named("nym_sdk::MixnetClient[NR]"),
|
||||
self.custom_gateway_transceiver,
|
||||
self.custom_topology_provider,
|
||||
self.wait_for_gateway,
|
||||
|
||||
Reference in New Issue
Block a user