Compare commits
71 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b83fdd34af | |||
| 356cd2eeac | |||
| 0f6ec8610e | |||
| c3b8c4b2f7 | |||
| 271b9e545c | |||
| 9641f01670 | |||
| a7bb3e8d91 | |||
| 79ce611d21 | |||
| 960e817b8f | |||
| 8b03e66ba7 | |||
| 6a35581299 | |||
| ce124a29a7 | |||
| f62d8813e0 | |||
| a9cf016af2 | |||
| a8403b585b | |||
| e9a7b48da0 | |||
| 66792f57ed | |||
| f8d863249e | |||
| 7d59a2477a | |||
| eca88b0fa4 | |||
| b80a4c8614 | |||
| ec5d342e3a | |||
| 6565655861 | |||
| 5aba886f14 | |||
| 3ee73d541e | |||
| 4588a3036e | |||
| 6194ac07b8 | |||
| a7fcfef5a3 | |||
| fa927b82d8 | |||
| f724478763 | |||
| 040f4f2500 | |||
| 63002e784a | |||
| 4a0b683b70 | |||
| 9e84b1f0c1 | |||
| bf031ad6de | |||
| 933769401c | |||
| ddd85704bb | |||
| 17860c809f | |||
| 2d00fcd934 | |||
| c2c3df98cb | |||
| f429092e21 | |||
| d7ef68d8d1 | |||
| 1a334b575d | |||
| 2126736aff | |||
| a69aa23609 | |||
| 8a2d98e3ce | |||
| 9c4243914e | |||
| 143ede268d | |||
| 81bddb5f6d | |||
| 247ebb7c43 | |||
| 01c052e9a4 | |||
| 3880971e57 | |||
| 6bd31b9521 | |||
| 430c33eb04 | |||
| d45d1eb313 | |||
| 3cb3ebd79b | |||
| b42e5b063e | |||
| f6b30d0db6 | |||
| c33e4c0836 | |||
| be92ccf0da | |||
| 35bf49c48c | |||
| 7335a3dad4 | |||
| 698883c03f | |||
| 8ddef08c72 | |||
| 0d8b3abc6f | |||
| aa2f336904 | |||
| eacaf84430 | |||
| c284b1e8b1 | |||
| 7785d085cf | |||
| bb5b2eafcf | |||
| 09ea406c02 |
@@ -100,7 +100,6 @@ jobs:
|
||||
cp target/release/nymvisor $OUTPUT_DIR
|
||||
cp target/release/nym-node $OUTPUT_DIR
|
||||
cp target/release/nym-cli $OUTPUT_DIR
|
||||
cp target/release/explorer-api $OUTPUT_DIR
|
||||
if [ ${{ github.event_name == 'workflow_dispatch' && inputs.enable_deb == true }} = true ]; then
|
||||
cp target/debian/*.deb $OUTPUT_DIR
|
||||
fi
|
||||
|
||||
@@ -4,6 +4,28 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [2025.4-dorina-patched] (2025-03-06)
|
||||
|
||||
- use legacy crypto for constructing SURB headers ([#5579])
|
||||
- bugfix: make sure to correctly decode response content when putting it into error message ([#5571])
|
||||
- Tweak surb management to be more conservative ([#5570])
|
||||
- Deserialize v5 authenticator requests ([#5568])
|
||||
- chore: additional logs when attempting to load ecash keys ([#5567])
|
||||
- add full response body to error message upon decoding failure ([#5566])
|
||||
- hotfix: ensure we bail on merkle leaves insertion upon missing data ([#5565])
|
||||
- feature: v2 authentication request (#5537) ([#5563])
|
||||
- Create authenticator v5 request/response types ([#5561])
|
||||
|
||||
[#5579]: https://github.com/nymtech/nym/pull/5579
|
||||
[#5571]: https://github.com/nymtech/nym/pull/5571
|
||||
[#5570]: https://github.com/nymtech/nym/pull/5570
|
||||
[#5568]: https://github.com/nymtech/nym/pull/5568
|
||||
[#5567]: https://github.com/nymtech/nym/pull/5567
|
||||
[#5566]: https://github.com/nymtech/nym/pull/5566
|
||||
[#5565]: https://github.com/nymtech/nym/pull/5565
|
||||
[#5563]: https://github.com/nymtech/nym/pull/5563
|
||||
[#5561]: https://github.com/nymtech/nym/pull/5561
|
||||
|
||||
## [2025.4-dorina] (2025-03-04)
|
||||
|
||||
- fixed sphinx version metrics registration ([#5546])
|
||||
|
||||
Generated
+104
-62
@@ -816,9 +816,9 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "bls12_381"
|
||||
version = "0.8.0"
|
||||
source = "git+https://github.com/jstuczyn/bls12_381?branch=temp/experimental-serdect#22cd0a16b674af1629110a2dc8b6cf6c73ea4cd9"
|
||||
source = "git+https://github.com/jstuczyn/bls12_381?branch=temp/experimental-serdect-updated#9bf520059cb28323fc51469cae86868ef4fa6fbd"
|
||||
dependencies = [
|
||||
"digest 0.9.0",
|
||||
"digest 0.10.7",
|
||||
"ff",
|
||||
"group",
|
||||
"pairing",
|
||||
@@ -902,9 +902,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.10.0"
|
||||
version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9"
|
||||
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -941,7 +941,7 @@ checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a"
|
||||
dependencies = [
|
||||
"camino",
|
||||
"cargo-platform",
|
||||
"semver 1.0.25",
|
||||
"semver 1.0.26",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 1.0.69",
|
||||
@@ -955,7 +955,7 @@ checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037"
|
||||
dependencies = [
|
||||
"camino",
|
||||
"cargo-platform",
|
||||
"semver 1.0.25",
|
||||
"semver 1.0.26",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 1.0.69",
|
||||
@@ -1587,7 +1587,7 @@ dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"crossterm_winapi",
|
||||
"parking_lot",
|
||||
"rustix",
|
||||
"rustix 0.38.44",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
@@ -1799,7 +1799,7 @@ dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw2",
|
||||
"schemars",
|
||||
"semver 1.0.25",
|
||||
"semver 1.0.26",
|
||||
"serde",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
@@ -1814,7 +1814,7 @@ dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
"schemars",
|
||||
"semver 1.0.25",
|
||||
"semver 1.0.26",
|
||||
"serde",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
@@ -2433,7 +2433,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "explorer-api"
|
||||
version = "1.1.47"
|
||||
version = "1.1.48"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
@@ -2527,9 +2527,9 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "ff"
|
||||
version = "0.13.0"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449"
|
||||
checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393"
|
||||
dependencies = [
|
||||
"rand_core 0.6.4",
|
||||
"subtle 2.6.1",
|
||||
@@ -4144,6 +4144,12 @@ version = "0.4.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db9c683daf087dc577b7506e9695b3d556a9f3849903fa28186283afd6809e9"
|
||||
|
||||
[[package]]
|
||||
name = "lioness"
|
||||
version = "0.1.2"
|
||||
@@ -4780,7 +4786,7 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
||||
|
||||
[[package]]
|
||||
name = "nym-api"
|
||||
version = "1.1.51"
|
||||
version = "1.1.54"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -4841,10 +4847,10 @@ dependencies = [
|
||||
"rand_chacha 0.3.1",
|
||||
"reqwest 0.12.4",
|
||||
"schemars",
|
||||
"semver 1.0.25",
|
||||
"semver 1.0.26",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.9.9",
|
||||
"sha2 0.10.8",
|
||||
"sqlx",
|
||||
"tempfile",
|
||||
"tendermint 0.40.1",
|
||||
@@ -4890,6 +4896,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"sha2 0.10.8",
|
||||
"tendermint 0.40.1",
|
||||
"tendermint-rpc",
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
"ts-rs",
|
||||
@@ -5029,7 +5036,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-cli"
|
||||
version = "1.1.49"
|
||||
version = "1.1.51"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
@@ -5112,7 +5119,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-client"
|
||||
version = "1.1.49"
|
||||
version = "1.1.51"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"clap",
|
||||
@@ -5324,7 +5331,7 @@ dependencies = [
|
||||
"bs58",
|
||||
"cfg-if",
|
||||
"criterion",
|
||||
"digest 0.9.0",
|
||||
"digest 0.10.7",
|
||||
"ff",
|
||||
"group",
|
||||
"itertools 0.14.0",
|
||||
@@ -5333,7 +5340,7 @@ dependencies = [
|
||||
"rand 0.8.5",
|
||||
"rayon",
|
||||
"serde",
|
||||
"sha2 0.9.9",
|
||||
"sha2 0.10.8",
|
||||
"subtle 2.6.1",
|
||||
"thiserror 2.0.12",
|
||||
"zeroize",
|
||||
@@ -5606,7 +5613,7 @@ dependencies = [
|
||||
"rand_core 0.6.4",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"sha2 0.9.9",
|
||||
"sha2 0.10.8",
|
||||
"thiserror 2.0.12",
|
||||
"zeroize",
|
||||
]
|
||||
@@ -5874,8 +5881,11 @@ name = "nym-http-api-client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"hickory-resolver",
|
||||
"http 1.2.0",
|
||||
"mime",
|
||||
"nym-bin-common",
|
||||
"once_cell",
|
||||
"reqwest 0.12.4",
|
||||
@@ -6052,7 +6062,7 @@ dependencies = [
|
||||
"nym-contracts-common",
|
||||
"rand_chacha 0.3.1",
|
||||
"schemars",
|
||||
"semver 1.0.25",
|
||||
"semver 1.0.26",
|
||||
"serde",
|
||||
"serde-json-wasm",
|
||||
"serde_repr",
|
||||
@@ -6153,7 +6163,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-requester"
|
||||
version = "1.1.50"
|
||||
version = "1.1.52"
|
||||
dependencies = [
|
||||
"addr",
|
||||
"anyhow",
|
||||
@@ -6204,7 +6214,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-node"
|
||||
version = "1.6.0"
|
||||
version = "1.7.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
@@ -6231,6 +6241,7 @@ dependencies = [
|
||||
"nym-crypto",
|
||||
"nym-gateway",
|
||||
"nym-gateway-stats-storage",
|
||||
"nym-http-api-client",
|
||||
"nym-http-api-common",
|
||||
"nym-ip-packet-router",
|
||||
"nym-metrics",
|
||||
@@ -6253,7 +6264,7 @@ dependencies = [
|
||||
"nym-wireguard",
|
||||
"nym-wireguard-types",
|
||||
"rand 0.8.5",
|
||||
"semver 1.0.25",
|
||||
"semver 1.0.26",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"si-scale",
|
||||
@@ -6345,6 +6356,7 @@ dependencies = [
|
||||
"nym-contracts-common",
|
||||
"nym-crypto",
|
||||
"nym-explorer-client",
|
||||
"nym-http-api-client",
|
||||
"nym-network-defaults",
|
||||
"nym-node-metrics",
|
||||
"nym-node-requests",
|
||||
@@ -6489,6 +6501,7 @@ name = "nym-pemstore"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"pem",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6536,6 +6549,7 @@ dependencies = [
|
||||
"reqwest 0.12.4",
|
||||
"serde",
|
||||
"tap",
|
||||
"tempfile",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
@@ -6587,7 +6601,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.49"
|
||||
version = "1.1.51"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"clap",
|
||||
@@ -7192,7 +7206,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nymvisor"
|
||||
version = "0.1.14"
|
||||
version = "0.1.16"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -7222,7 +7236,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nyx-chain-watcher"
|
||||
version = "0.1.11"
|
||||
version = "0.1.14"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -7232,15 +7246,12 @@ dependencies = [
|
||||
"nym-bin-common",
|
||||
"nym-config",
|
||||
"nym-network-defaults",
|
||||
"nym-node-requests",
|
||||
"nym-task",
|
||||
"nym-validator-client",
|
||||
"nyxd-scraper",
|
||||
"reqwest 0.12.4",
|
||||
"rocket",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
@@ -8405,9 +8416,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.9"
|
||||
version = "0.17.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e75ec5e92c4d8aede845126adc388046234541629e76029599ed35a003c7ed24"
|
||||
checksum = "70ac5d832aa16abd7d1def883a8545280c20a60f523a370aa3a9617c2b8550ee"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
@@ -8594,9 +8605,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rs_merkle"
|
||||
version = "1.4.2"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b241d2e59b74ef9e98d94c78c47623d04c8392abaf82014dfd372a16041128f"
|
||||
checksum = "bb09b49230ba22e8c676e7b75dfe2887dea8121f18b530ae0ba519ce442d2b21"
|
||||
dependencies = [
|
||||
"sha2 0.10.8",
|
||||
]
|
||||
@@ -8698,7 +8709,7 @@ version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||
dependencies = [
|
||||
"semver 1.0.25",
|
||||
"semver 1.0.26",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8710,7 +8721,20 @@ dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"linux-raw-sys 0.4.15",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dade4812df5c384711475be5fcd8c162555352945401aed22a35bffeab61f657"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.9.2",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
@@ -8986,9 +9010,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.25"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03"
|
||||
checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -9001,9 +9025,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.218"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60"
|
||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
@@ -9041,18 +9065,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_bytes"
|
||||
version = "0.11.16"
|
||||
version = "0.11.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "364fec0df39c49a083c9a8a18a23a6bcfd9af130fe9fe321d18520a0d113e09e"
|
||||
checksum = "8437fd221bde2d4ca316d61b90e337e9e702b3820b87d63caa9ba6c02bd06d96"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.218"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b"
|
||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -9155,9 +9179,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_repr"
|
||||
version = "0.1.19"
|
||||
version = "0.1.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
|
||||
checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -9428,9 +9452,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sphinx-packet"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "535f2c430778bf59c22249fcc1ed6d384129eb2f0f694706015d636c688f9ac6"
|
||||
checksum = "c23047e0cf36ff6904603f499fd13153425cdf5ba47bfbaedbc999da0bd92f4e"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"arrayref",
|
||||
@@ -9949,15 +9973,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.17.1"
|
||||
version = "3.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230"
|
||||
checksum = "2c317e0a526ee6120d8dabad239c8dadca62b24b6f168914bbbc8e2fb1f0e567"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand 2.3.0",
|
||||
"getrandom 0.3.1",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"rustix 1.0.1",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
@@ -10085,7 +10109,7 @@ dependencies = [
|
||||
"pin-project",
|
||||
"rand 0.8.5",
|
||||
"reqwest 0.11.27",
|
||||
"semver 1.0.25",
|
||||
"semver 1.0.26",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
"serde_json",
|
||||
@@ -10245,9 +10269,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.37"
|
||||
version = "0.3.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
|
||||
checksum = "dad298b01a40a23aac4580b67e3dbedb7cc8402f3592d7f49469de2ea4aecdd8"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
@@ -10263,15 +10287,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
||||
checksum = "765c97a5b985b7c11d7bc27fa927dc4fe6af3a6dfb021d28deb60d3bf51e76ef"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.19"
|
||||
version = "0.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
|
||||
checksum = "e8093bc3e81c3bc5f7879de09619d06c9a5a5e45ca44dfeeb7225bae38005c5c"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
@@ -10314,9 +10338,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.43.0"
|
||||
version = "1.44.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e"
|
||||
checksum = "9975ea0f48b5aa3972bf2d888c238182458437cc2a19374b81b25cdf1023fb3a"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
@@ -11241,6 +11265,24 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "validator-status-check"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"comfy-table",
|
||||
"nym-bin-common",
|
||||
"nym-network-defaults",
|
||||
"nym-validator-client",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"strum 0.26.3",
|
||||
"time",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.1"
|
||||
@@ -12041,8 +12083,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e105d177a3871454f754b33bb0ee637ecaaac997446375fd3e5d43a2ed00c909"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"rustix",
|
||||
"linux-raw-sys 0.4.15",
|
||||
"rustix 0.38.44",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
+14
-13
@@ -137,7 +137,7 @@ members = [
|
||||
"tools/internal/testnet-manager",
|
||||
"tools/internal/testnet-manager",
|
||||
"tools/internal/testnet-manager/dkg-bypass-contract",
|
||||
"tools/internal/testnet-manager/dkg-bypass-contract",
|
||||
"tools/internal/testnet-manager/dkg-bypass-contract", "tools/internal/validator-status-check",
|
||||
"tools/nym-cli",
|
||||
"tools/nym-id-cli",
|
||||
"tools/nym-nr-query",
|
||||
@@ -209,7 +209,7 @@ blake3 = "1.6.1"
|
||||
bloomfilter = "1.0.14"
|
||||
bs58 = "0.5.1"
|
||||
bytecodec = "0.4.15"
|
||||
bytes = "1.7.2"
|
||||
bytes = "1.10.1"
|
||||
cargo_metadata = "0.18.1"
|
||||
celes = "2.5.0"
|
||||
cfg-if = "1.0.0"
|
||||
@@ -241,6 +241,7 @@ doc-comment = "0.3"
|
||||
dotenvy = "0.15.6"
|
||||
ecdsa = "0.16"
|
||||
ed25519-dalek = "2.1"
|
||||
encoding_rs = "0.8.35"
|
||||
env_logger = "0.11.6"
|
||||
envy = "0.4"
|
||||
etherparse = "0.13.0"
|
||||
@@ -307,12 +308,12 @@ reqwest = { version = "0.12.4", default-features = false }
|
||||
rocket = "0.5.0"
|
||||
rocket_cors = "0.6.0"
|
||||
rocket_okapi = "0.8.0"
|
||||
rs_merkle = "1.4.2"
|
||||
rs_merkle = "1.5.0"
|
||||
safer-ffi = "0.1.13"
|
||||
schemars = "0.8.22"
|
||||
semver = "1.0.25"
|
||||
serde = "1.0.217"
|
||||
serde_bytes = "0.11.16"
|
||||
semver = "1.0.26"
|
||||
serde = "1.0.219"
|
||||
serde_bytes = "0.11.17"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0.140"
|
||||
serde_json_path = "0.7.2"
|
||||
@@ -321,7 +322,7 @@ serde_with = "3.9.0"
|
||||
serde_yaml = "0.9.25"
|
||||
sha2 = "0.10.8"
|
||||
si-scale = "0.2.3"
|
||||
sphinx-packet = "0.3.1"
|
||||
sphinx-packet = "=0.3.2"
|
||||
sqlx = "0.7.4"
|
||||
strum = "0.26"
|
||||
strum_macros = "0.26"
|
||||
@@ -330,10 +331,10 @@ syn = "1"
|
||||
sysinfo = "0.33.0"
|
||||
tap = "1.0.1"
|
||||
tar = "0.4.44"
|
||||
tempfile = "3.15"
|
||||
tempfile = "3.18"
|
||||
thiserror = "2.0"
|
||||
time = "0.3.37"
|
||||
tokio = "1.43"
|
||||
time = "0.3.39"
|
||||
tokio = "1.44"
|
||||
tokio-postgres = "0.7"
|
||||
tokio-stream = "0.1.17"
|
||||
tokio-test = "0.4.4"
|
||||
@@ -369,9 +370,9 @@ prometheus = { version = "0.13.0" }
|
||||
# unfortunately until https://github.com/zkcrypto/bls12_381/issues/10 is resolved, we have to rely on the fork
|
||||
# as we need to be able to serialize Gt so that we could create the lookup table for baby-step-giant-step algorithm
|
||||
# plus to make our live easier we need serde support from https://github.com/zkcrypto/bls12_381/pull/125
|
||||
bls12_381 = { git = "https://github.com/jstuczyn/bls12_381", default-features = false, branch = "temp/experimental-serdect" }
|
||||
bls12_381 = { git = "https://github.com/jstuczyn/bls12_381", default-features = false, branch = "temp/experimental-serdect-updated" }
|
||||
group = { version = "0.13.0", default-features = false }
|
||||
ff = { version = "0.13.0", default-features = false }
|
||||
ff = { version = "0.13.1", default-features = false }
|
||||
subtle = "2.5.0"
|
||||
|
||||
# cosmwasm-related
|
||||
@@ -446,4 +447,4 @@ dbg_macro = "deny"
|
||||
exit = "deny"
|
||||
panic = "deny"
|
||||
unimplemented = "deny"
|
||||
unreachable = "deny"
|
||||
unreachable = "deny"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.49"
|
||||
version = "1.1.51"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.49"
|
||||
version = "1.1.51"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
||||
edition = "2021"
|
||||
|
||||
@@ -6,14 +6,15 @@ pub mod v1;
|
||||
pub mod v2;
|
||||
pub mod v3;
|
||||
pub mod v4;
|
||||
pub mod v5;
|
||||
|
||||
mod error;
|
||||
mod util;
|
||||
|
||||
pub use error::Error;
|
||||
pub use v4 as latest;
|
||||
pub use v5 as latest;
|
||||
|
||||
pub const CURRENT_VERSION: u8 = 4;
|
||||
pub const CURRENT_VERSION: u8 = 5;
|
||||
|
||||
fn make_bincode_serializer() -> impl bincode::Options {
|
||||
use bincode::Options;
|
||||
|
||||
@@ -8,8 +8,8 @@ use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
|
||||
use crate::{
|
||||
v1, v2, v3,
|
||||
v4::{self, registration::IpPair},
|
||||
v1, v2, v3, v4,
|
||||
v5::{self, registration::IpPair},
|
||||
Error,
|
||||
};
|
||||
|
||||
@@ -19,6 +19,7 @@ pub enum AuthenticatorVersion {
|
||||
V2,
|
||||
V3,
|
||||
V4,
|
||||
V5,
|
||||
UNKNOWN,
|
||||
}
|
||||
|
||||
@@ -34,6 +35,8 @@ impl From<Protocol> for AuthenticatorVersion {
|
||||
AuthenticatorVersion::V3
|
||||
} else if value.version == v4::VERSION {
|
||||
AuthenticatorVersion::V4
|
||||
} else if value.version == v5::VERSION {
|
||||
AuthenticatorVersion::V5
|
||||
} else {
|
||||
AuthenticatorVersion::UNKNOWN
|
||||
}
|
||||
@@ -68,6 +71,12 @@ impl InitMessage for v4::registration::InitMessage {
|
||||
}
|
||||
}
|
||||
|
||||
impl InitMessage for v5::registration::InitMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.pub_key
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FinalMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey;
|
||||
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error>;
|
||||
@@ -138,6 +147,24 @@ impl FinalMessage for v4::registration::FinalMessage {
|
||||
self.gateway_client.verify(private_key, nonce)
|
||||
}
|
||||
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.gateway_client.private_ips.into()
|
||||
}
|
||||
|
||||
fn credential(&self) -> Option<CredentialSpendingData> {
|
||||
self.credential.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl FinalMessage for v5::registration::FinalMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.gateway_client.pub_key
|
||||
}
|
||||
|
||||
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
self.gateway_client.verify(private_key, nonce)
|
||||
}
|
||||
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.gateway_client.private_ips
|
||||
}
|
||||
@@ -182,29 +209,39 @@ impl TopUpMessage for v4::topup::TopUpMessage {
|
||||
}
|
||||
}
|
||||
|
||||
impl TopUpMessage for v5::topup::TopUpMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.pub_key
|
||||
}
|
||||
|
||||
fn credential(&self) -> CredentialSpendingData {
|
||||
self.credential.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub enum AuthenticatorRequest {
|
||||
Initial {
|
||||
msg: Box<dyn InitMessage + Send + Sync + 'static>,
|
||||
protocol: Protocol,
|
||||
reply_to: Recipient,
|
||||
reply_to: Option<Recipient>,
|
||||
request_id: u64,
|
||||
},
|
||||
Final {
|
||||
msg: Box<dyn FinalMessage + Send + Sync + 'static>,
|
||||
protocol: Protocol,
|
||||
reply_to: Recipient,
|
||||
reply_to: Option<Recipient>,
|
||||
request_id: u64,
|
||||
},
|
||||
QueryBandwidth {
|
||||
msg: Box<dyn QueryBandwidthMessage + Send + Sync + 'static>,
|
||||
protocol: Protocol,
|
||||
reply_to: Recipient,
|
||||
reply_to: Option<Recipient>,
|
||||
request_id: u64,
|
||||
},
|
||||
TopUpBandwidth {
|
||||
msg: Box<dyn TopUpMessage + Send + Sync + 'static>,
|
||||
protocol: Protocol,
|
||||
reply_to: Recipient,
|
||||
reply_to: Option<Recipient>,
|
||||
request_id: u64,
|
||||
},
|
||||
}
|
||||
@@ -218,7 +255,7 @@ impl From<v1::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
version: value.version,
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
},
|
||||
reply_to: value.reply_to,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v1::request::AuthenticatorRequestData::Final(gateway_client) => Self::Final {
|
||||
@@ -227,7 +264,7 @@ impl From<v1::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
version: value.version,
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
},
|
||||
reply_to: value.reply_to,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v1::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
|
||||
@@ -237,7 +274,7 @@ impl From<v1::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
version: value.version,
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
},
|
||||
reply_to: value.reply_to,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
@@ -251,20 +288,20 @@ impl From<v2::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
v2::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
|
||||
msg: Box::new(init_message),
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v2::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
|
||||
msg: final_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v2::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
|
||||
Self::QueryBandwidth {
|
||||
msg: Box::new(peer_public_key),
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
@@ -278,20 +315,20 @@ impl From<v3::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
v3::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
|
||||
msg: Box::new(init_message),
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v3::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
|
||||
msg: final_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v3::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
|
||||
Self::QueryBandwidth {
|
||||
msg: Box::new(peer_public_key),
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
@@ -299,7 +336,7 @@ impl From<v3::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
Self::TopUpBandwidth {
|
||||
msg: top_up_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
@@ -313,20 +350,20 @@ impl From<v4::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
v4::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
|
||||
msg: Box::new(init_message),
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v4::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
|
||||
msg: final_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v4::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
|
||||
Self::QueryBandwidth {
|
||||
msg: Box::new(peer_public_key),
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
@@ -334,7 +371,42 @@ impl From<v4::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
Self::TopUpBandwidth {
|
||||
msg: top_up_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
fn from(value: v5::request::AuthenticatorRequest) -> Self {
|
||||
match value.data {
|
||||
v5::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
|
||||
msg: Box::new(init_message),
|
||||
protocol: value.protocol,
|
||||
reply_to: None,
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v5::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
|
||||
msg: final_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: None,
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v5::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
|
||||
Self::QueryBandwidth {
|
||||
msg: Box::new(peer_public_key),
|
||||
protocol: value.protocol,
|
||||
reply_to: None,
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
v5::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => {
|
||||
Self::TopUpBandwidth {
|
||||
msg: top_up_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: None,
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,478 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
|
||||
|
||||
use crate::{v4, v5};
|
||||
|
||||
impl From<v4::request::AuthenticatorRequest> for v5::request::AuthenticatorRequest {
|
||||
fn from(authenticator_request: v4::request::AuthenticatorRequest) -> Self {
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
version: 5,
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
},
|
||||
data: authenticator_request.data.into(),
|
||||
request_id: authenticator_request.request_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::request::AuthenticatorRequestData> for v5::request::AuthenticatorRequestData {
|
||||
fn from(authenticator_request_data: v4::request::AuthenticatorRequestData) -> Self {
|
||||
match authenticator_request_data {
|
||||
v4::request::AuthenticatorRequestData::Initial(init_msg) => {
|
||||
v5::request::AuthenticatorRequestData::Initial(init_msg.into())
|
||||
}
|
||||
v4::request::AuthenticatorRequestData::Final(final_msg) => {
|
||||
v5::request::AuthenticatorRequestData::Final(Box::new((*final_msg).into()))
|
||||
}
|
||||
v4::request::AuthenticatorRequestData::QueryBandwidth(pub_key) => {
|
||||
v5::request::AuthenticatorRequestData::QueryBandwidth(pub_key)
|
||||
}
|
||||
v4::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => {
|
||||
v5::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::registration::InitMessage> for v5::registration::InitMessage {
|
||||
fn from(init_msg: v4::registration::InitMessage) -> Self {
|
||||
Self {
|
||||
pub_key: init_msg.pub_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::registration::FinalMessage> for v5::registration::FinalMessage {
|
||||
fn from(final_msg: v4::registration::FinalMessage) -> Self {
|
||||
Self {
|
||||
gateway_client: final_msg.gateway_client.into(),
|
||||
credential: final_msg.credential,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::registration::GatewayClient> for v5::registration::GatewayClient {
|
||||
fn from(gateway_client: v4::registration::GatewayClient) -> Self {
|
||||
Self {
|
||||
pub_key: gateway_client.pub_key,
|
||||
private_ips: gateway_client.private_ips.into(),
|
||||
mac: gateway_client.mac.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::registration::GatewayClient> for v4::registration::GatewayClient {
|
||||
fn from(gateway_client: v5::registration::GatewayClient) -> Self {
|
||||
Self {
|
||||
pub_key: gateway_client.pub_key,
|
||||
private_ips: gateway_client.private_ips.into(),
|
||||
mac: gateway_client.mac.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::registration::ClientMac> for v5::registration::ClientMac {
|
||||
fn from(client_mac: v4::registration::ClientMac) -> Self {
|
||||
Self::new((*client_mac).clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::registration::ClientMac> for v4::registration::ClientMac {
|
||||
fn from(client_mac: v5::registration::ClientMac) -> Self {
|
||||
Self::new((*client_mac).clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Box<v4::topup::TopUpMessage>> for Box<v5::topup::TopUpMessage> {
|
||||
fn from(top_up_message: Box<v4::topup::TopUpMessage>) -> Self {
|
||||
Box::new(v5::topup::TopUpMessage {
|
||||
pub_key: top_up_message.pub_key,
|
||||
credential: top_up_message.credential,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::response::AuthenticatorResponse> for v5::response::AuthenticatorResponse {
|
||||
fn from(value: v4::response::AuthenticatorResponse) -> Self {
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
version: 5,
|
||||
service_provider_type: value.protocol.service_provider_type,
|
||||
},
|
||||
data: value.data.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::response::AuthenticatorResponseData> for v5::response::AuthenticatorResponseData {
|
||||
fn from(authenticator_response_data: v4::response::AuthenticatorResponseData) -> Self {
|
||||
match authenticator_response_data {
|
||||
v4::response::AuthenticatorResponseData::PendingRegistration(pending_response) => {
|
||||
v5::response::AuthenticatorResponseData::PendingRegistration(
|
||||
pending_response.into(),
|
||||
)
|
||||
}
|
||||
v4::response::AuthenticatorResponseData::Registered(registered_response) => {
|
||||
v5::response::AuthenticatorResponseData::Registered(registered_response.into())
|
||||
}
|
||||
v4::response::AuthenticatorResponseData::RemainingBandwidth(
|
||||
remaining_bandwidth_response,
|
||||
) => v5::response::AuthenticatorResponseData::RemainingBandwidth(
|
||||
remaining_bandwidth_response.into(),
|
||||
),
|
||||
v4::response::AuthenticatorResponseData::TopUpBandwidth(top_up_response) => {
|
||||
v5::response::AuthenticatorResponseData::TopUpBandwidth(top_up_response.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::response::RegisteredResponse> for v5::response::RegisteredResponse {
|
||||
fn from(value: v4::response::RegisteredResponse) -> Self {
|
||||
Self {
|
||||
request_id: value.request_id,
|
||||
reply: value.reply.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::response::PendingRegistrationResponse> for v5::response::PendingRegistrationResponse {
|
||||
fn from(value: v4::response::PendingRegistrationResponse) -> Self {
|
||||
Self {
|
||||
request_id: value.request_id,
|
||||
reply: value.reply.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::registration::RegistrationData> for v5::registration::RegistrationData {
|
||||
fn from(value: v4::registration::RegistrationData) -> Self {
|
||||
Self {
|
||||
nonce: value.nonce,
|
||||
gateway_data: value.gateway_data.into(),
|
||||
wg_port: value.wg_port,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::registration::RegistrationData> for v4::registration::RegistrationData {
|
||||
fn from(value: v5::registration::RegistrationData) -> Self {
|
||||
Self {
|
||||
nonce: value.nonce,
|
||||
gateway_data: value.gateway_data.into(),
|
||||
wg_port: value.wg_port,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::response::RemainingBandwidthResponse> for v5::response::RemainingBandwidthResponse {
|
||||
fn from(value: v4::response::RemainingBandwidthResponse) -> Self {
|
||||
Self {
|
||||
request_id: value.request_id,
|
||||
reply: value.reply.map(Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::response::TopUpBandwidthResponse> for v5::response::TopUpBandwidthResponse {
|
||||
fn from(value: v4::response::TopUpBandwidthResponse) -> Self {
|
||||
Self {
|
||||
request_id: value.request_id,
|
||||
reply: value.reply.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::registration::RegistredData> for v5::registration::RegistredData {
|
||||
fn from(value: v4::registration::RegistredData) -> Self {
|
||||
Self {
|
||||
pub_key: value.pub_key,
|
||||
private_ips: value.private_ips.into(),
|
||||
wg_port: value.wg_port,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::registration::RemainingBandwidthData> for v5::registration::RemainingBandwidthData {
|
||||
fn from(value: v4::registration::RemainingBandwidthData) -> Self {
|
||||
Self {
|
||||
available_bandwidth: value.available_bandwidth,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::registration::IpPair> for v5::registration::IpPair {
|
||||
fn from(value: v4::registration::IpPair) -> Self {
|
||||
Self {
|
||||
ipv4: value.ipv4,
|
||||
ipv6: value.ipv6,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::registration::IpPair> for v4::registration::IpPair {
|
||||
fn from(value: v5::registration::IpPair) -> Self {
|
||||
Self {
|
||||
ipv4: value.ipv4,
|
||||
ipv6: value.ipv6,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{
|
||||
net::{Ipv4Addr, Ipv6Addr},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
use nym_crypto::asymmetric::encryption::PrivateKey;
|
||||
use nym_sphinx::addressing::Recipient;
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
use x25519_dalek::PublicKey;
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
util::tests::{CREDENTIAL_BYTES, RECIPIENT},
|
||||
v4,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn upgrade_initial_req() {
|
||||
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let (msg, _) = v4::request::AuthenticatorRequest::new_initial_request(
|
||||
v4::registration::InitMessage::new(pub_key),
|
||||
reply_to,
|
||||
);
|
||||
let upgraded_msg = v5::request::AuthenticatorRequest::from(msg);
|
||||
|
||||
assert_eq!(
|
||||
upgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 5,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v5::request::AuthenticatorRequestData::Initial(v5::registration::InitMessage {
|
||||
pub_key
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_final_req() {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let local_secret = PrivateKey::new(&mut rng);
|
||||
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
|
||||
let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap();
|
||||
let ipv6 = Ipv6Addr::from_str("fc01::a0a").unwrap();
|
||||
let ips = v4::registration::IpPair::new(ipv4, ipv6);
|
||||
let nonce = 42;
|
||||
let gateway_client = v4::registration::GatewayClient::new(
|
||||
&local_secret,
|
||||
(&remote_secret).into(),
|
||||
ips,
|
||||
nonce,
|
||||
);
|
||||
let credential = Some(CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap());
|
||||
let final_message = v4::registration::FinalMessage {
|
||||
gateway_client: gateway_client.clone(),
|
||||
credential: credential.clone(),
|
||||
};
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let (msg, _) =
|
||||
v4::request::AuthenticatorRequest::new_final_request(final_message, reply_to);
|
||||
let upgraded_msg = v5::request::AuthenticatorRequest::from(msg);
|
||||
|
||||
assert_eq!(
|
||||
upgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 5,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v5::request::AuthenticatorRequestData::Final(Box::new(
|
||||
v5::registration::FinalMessage {
|
||||
gateway_client: v5::registration::GatewayClient::new(
|
||||
&local_secret,
|
||||
(&remote_secret).into(),
|
||||
v5::registration::IpPair::new(ipv4, ipv6),
|
||||
nonce
|
||||
),
|
||||
credential
|
||||
}
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_query_req() {
|
||||
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let (msg, _) = v4::request::AuthenticatorRequest::new_query_request(pub_key, reply_to);
|
||||
let upgraded_msg = v5::request::AuthenticatorRequest::from(msg);
|
||||
|
||||
assert_eq!(
|
||||
upgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 5,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v5::request::AuthenticatorRequestData::QueryBandwidth(pub_key)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_pending_reg_resp() {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let local_secret = PrivateKey::new(&mut rng);
|
||||
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
|
||||
let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap();
|
||||
let ipv6 = Ipv6Addr::from_str("fc01::a0a").unwrap();
|
||||
let ips = v4::registration::IpPair::new(ipv4, ipv6);
|
||||
let nonce = 42;
|
||||
let wg_port = 51822;
|
||||
let gateway_data = v4::registration::GatewayClient::new(
|
||||
&local_secret,
|
||||
(&remote_secret).into(),
|
||||
ips,
|
||||
nonce,
|
||||
);
|
||||
let registration_data = v4::registration::RegistrationData {
|
||||
nonce,
|
||||
gateway_data,
|
||||
wg_port,
|
||||
};
|
||||
let request_id = 123;
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let msg = v4::response::AuthenticatorResponse::new_pending_registration_success(
|
||||
registration_data,
|
||||
request_id,
|
||||
reply_to,
|
||||
);
|
||||
let upgraded_msg = v5::response::AuthenticatorResponse::from(msg);
|
||||
|
||||
assert_eq!(
|
||||
upgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 5,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v5::response::AuthenticatorResponseData::PendingRegistration(
|
||||
v5::response::PendingRegistrationResponse {
|
||||
request_id,
|
||||
reply: v5::registration::RegistrationData {
|
||||
nonce,
|
||||
gateway_data: v5::registration::GatewayClient::new(
|
||||
&local_secret,
|
||||
(&remote_secret).into(),
|
||||
v5::registration::IpPair::new(ipv4, ipv6),
|
||||
nonce
|
||||
),
|
||||
wg_port
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_registered_resp() {
|
||||
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
|
||||
let ipv4 = Ipv4Addr::from_str("10.1.10.10").unwrap();
|
||||
let ipv6 = Ipv6Addr::from_str("fc01::a0a").unwrap();
|
||||
let private_ips = v4::registration::IpPair::new(ipv4, ipv6);
|
||||
let wg_port = 51822;
|
||||
let registred_data = v4::registration::RegistredData {
|
||||
pub_key,
|
||||
private_ips,
|
||||
wg_port,
|
||||
};
|
||||
let request_id = 123;
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let msg = v4::response::AuthenticatorResponse::new_registered(
|
||||
registred_data,
|
||||
reply_to,
|
||||
request_id,
|
||||
);
|
||||
let upgraded_msg = v5::response::AuthenticatorResponse::from(msg);
|
||||
|
||||
assert_eq!(
|
||||
upgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 5,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v5::response::AuthenticatorResponseData::Registered(v5::response::RegisteredResponse {
|
||||
request_id,
|
||||
reply: v5::registration::RegistredData {
|
||||
wg_port,
|
||||
pub_key,
|
||||
private_ips: v5::registration::IpPair::new(ipv4, ipv6)
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_remaining_bandwidth_resp() {
|
||||
let available_bandwidth = 42;
|
||||
let remaining_bandwidth_data = Some(v4::registration::RemainingBandwidthData {
|
||||
available_bandwidth,
|
||||
});
|
||||
let request_id = 123;
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let msg = v4::response::AuthenticatorResponse::new_remaining_bandwidth(
|
||||
remaining_bandwidth_data,
|
||||
reply_to,
|
||||
request_id,
|
||||
);
|
||||
let upgraded_msg = v5::response::AuthenticatorResponse::from(msg);
|
||||
|
||||
assert_eq!(
|
||||
upgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 5,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v5::response::AuthenticatorResponseData::RemainingBandwidth(
|
||||
v5::response::RemainingBandwidthResponse {
|
||||
request_id,
|
||||
reply: Some(v5::registration::RemainingBandwidthData {
|
||||
available_bandwidth,
|
||||
})
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod conversion;
|
||||
pub mod registration;
|
||||
pub mod request;
|
||||
pub mod response;
|
||||
pub mod topup;
|
||||
|
||||
pub const VERSION: u8 = 5;
|
||||
@@ -0,0 +1,287 @@
|
||||
// -2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::Error;
|
||||
use base64::{engine::general_purpose, Engine};
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
use nym_network_defaults::constants::{WG_TUN_DEVICE_IP_ADDRESS_V4, WG_TUN_DEVICE_IP_ADDRESS_V6};
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
use std::time::SystemTime;
|
||||
use std::{fmt, ops::Deref, str::FromStr};
|
||||
|
||||
#[cfg(feature = "verify")]
|
||||
use hmac::{Hmac, Mac};
|
||||
#[cfg(feature = "verify")]
|
||||
use nym_crypto::asymmetric::encryption::PrivateKey;
|
||||
#[cfg(feature = "verify")]
|
||||
use sha2::Sha256;
|
||||
|
||||
pub type PendingRegistrations = HashMap<PeerPublicKey, RegistrationData>;
|
||||
pub type PrivateIPs = HashMap<IpPair, Taken>;
|
||||
|
||||
#[cfg(feature = "verify")]
|
||||
pub type HmacSha256 = Hmac<Sha256>;
|
||||
|
||||
pub type Nonce = u64;
|
||||
pub type Taken = Option<SystemTime>;
|
||||
|
||||
pub const BANDWIDTH_CAP_PER_DAY: u64 = 250 * 1024 * 1024 * 1024; // 250 GB
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct IpPair {
|
||||
pub ipv4: Ipv4Addr,
|
||||
pub ipv6: Ipv6Addr,
|
||||
}
|
||||
|
||||
impl IpPair {
|
||||
pub fn new(ipv4: Ipv4Addr, ipv6: Ipv6Addr) -> Self {
|
||||
IpPair { ipv4, ipv6 }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(Ipv4Addr, Ipv6Addr)> for IpPair {
|
||||
fn from((ipv4, ipv6): (Ipv4Addr, Ipv6Addr)) -> Self {
|
||||
IpPair { ipv4, ipv6 }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for IpPair {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "({}, {})", self.ipv4, self.ipv6)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IpAddr> for IpPair {
|
||||
fn from(value: IpAddr) -> Self {
|
||||
let (before_last_byte, last_byte) = match value {
|
||||
std::net::IpAddr::V4(ipv4_addr) => (ipv4_addr.octets()[2], ipv4_addr.octets()[3]),
|
||||
std::net::IpAddr::V6(ipv6_addr) => (ipv6_addr.octets()[14], ipv6_addr.octets()[15]),
|
||||
};
|
||||
let last_bytes = ((before_last_byte as u16) << 8) | last_byte as u16;
|
||||
let ipv4 = Ipv4Addr::new(
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V4.octets()[0],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V4.octets()[1],
|
||||
before_last_byte,
|
||||
last_byte,
|
||||
);
|
||||
let ipv6 = Ipv6Addr::new(
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[0],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[1],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[2],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[3],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[4],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[5],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[6],
|
||||
last_bytes,
|
||||
);
|
||||
IpPair::new(ipv4, ipv6)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct InitMessage {
|
||||
/// Base64 encoded x25519 public key
|
||||
pub pub_key: PeerPublicKey,
|
||||
}
|
||||
|
||||
impl InitMessage {
|
||||
pub fn new(pub_key: PeerPublicKey) -> Self {
|
||||
InitMessage { pub_key }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct FinalMessage {
|
||||
/// Gateway client data
|
||||
pub gateway_client: GatewayClient,
|
||||
|
||||
/// Ecash credential
|
||||
pub credential: Option<CredentialSpendingData>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct RegistrationData {
|
||||
pub nonce: u64,
|
||||
pub gateway_data: GatewayClient,
|
||||
pub wg_port: u16,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct RegistredData {
|
||||
pub pub_key: PeerPublicKey,
|
||||
pub private_ips: IpPair,
|
||||
pub wg_port: u16,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct RemainingBandwidthData {
|
||||
pub available_bandwidth: i64,
|
||||
}
|
||||
|
||||
/// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret.
|
||||
/// Gateway/Nym node can then verify pub_key payload using the same process
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct GatewayClient {
|
||||
/// Base64 encoded x25519 public key
|
||||
pub pub_key: PeerPublicKey,
|
||||
|
||||
/// Assigned private IPs (v4 and v6)
|
||||
pub private_ips: IpPair,
|
||||
|
||||
/// Sha256 hmac on the data (alongside the prior nonce)
|
||||
pub mac: ClientMac,
|
||||
}
|
||||
|
||||
impl GatewayClient {
|
||||
#[cfg(feature = "verify")]
|
||||
pub fn new(
|
||||
local_secret: &PrivateKey,
|
||||
remote_public: x25519_dalek::PublicKey,
|
||||
private_ips: IpPair,
|
||||
nonce: u64,
|
||||
) -> Self {
|
||||
// convert from 1.0 x25519-dalek private key into 2.0 x25519-dalek
|
||||
#[allow(clippy::expect_used)]
|
||||
let static_secret = x25519_dalek::StaticSecret::from(local_secret.to_bytes());
|
||||
let local_public: x25519_dalek::PublicKey = (&static_secret).into();
|
||||
|
||||
let dh = static_secret.diffie_hellman(&remote_public);
|
||||
|
||||
// TODO: change that to use our nym_crypto::hmac module instead
|
||||
#[allow(clippy::expect_used)]
|
||||
let mut mac = HmacSha256::new_from_slice(dh.as_bytes())
|
||||
.expect("x25519 shared secret is always 32 bytes long");
|
||||
|
||||
mac.update(local_public.as_bytes());
|
||||
mac.update(private_ips.to_string().as_bytes());
|
||||
mac.update(&nonce.to_le_bytes());
|
||||
|
||||
GatewayClient {
|
||||
pub_key: PeerPublicKey::new(local_public),
|
||||
private_ips,
|
||||
mac: ClientMac(mac.finalize().into_bytes().to_vec()),
|
||||
}
|
||||
}
|
||||
|
||||
// Reusable secret should be gateways Wireguard PK
|
||||
// Client should perform this step when generating its payload, using its own WG PK
|
||||
#[cfg(feature = "verify")]
|
||||
pub fn verify(&self, gateway_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
// convert from 1.0 x25519-dalek private key into 2.0 x25519-dalek
|
||||
#[allow(clippy::expect_used)]
|
||||
let static_secret = x25519_dalek::StaticSecret::from(gateway_key.to_bytes());
|
||||
|
||||
let dh = static_secret.diffie_hellman(&self.pub_key);
|
||||
|
||||
// TODO: change that to use our nym_crypto::hmac module instead
|
||||
#[allow(clippy::expect_used)]
|
||||
let mut mac = HmacSha256::new_from_slice(dh.as_bytes())
|
||||
.expect("x25519 shared secret is always 32 bytes long");
|
||||
|
||||
mac.update(self.pub_key.as_bytes());
|
||||
mac.update(self.private_ips.to_string().as_bytes());
|
||||
mac.update(&nonce.to_le_bytes());
|
||||
|
||||
mac.verify_slice(&self.mac)
|
||||
.map_err(|source| Error::FailedClientMacVerification {
|
||||
client: self.pub_key.to_string(),
|
||||
source,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn pub_key(&self) -> PeerPublicKey {
|
||||
self.pub_key
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: change the inner type into generic array of size HmacSha256::OutputSize
|
||||
// TODO2: rely on our internal crypto/hmac
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ClientMac(Vec<u8>);
|
||||
|
||||
impl fmt::Display for ClientMac {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", general_purpose::STANDARD.encode(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientMac {
|
||||
#[allow(dead_code)]
|
||||
pub fn new(mac: Vec<u8>) -> Self {
|
||||
ClientMac(mac)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ClientMac {
|
||||
type Target = Vec<u8>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for ClientMac {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mac_bytes: Vec<u8> =
|
||||
general_purpose::STANDARD
|
||||
.decode(s)
|
||||
.map_err(|source| Error::MalformedClientMac {
|
||||
mac: s.to_string(),
|
||||
source,
|
||||
})?;
|
||||
|
||||
Ok(ClientMac(mac_bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for ClientMac {
|
||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let encoded_key = general_purpose::STANDARD.encode(self.0.clone());
|
||||
serializer.serialize_str(&encoded_key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for ClientMac {
|
||||
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
let encoded_key = String::deserialize(deserializer)?;
|
||||
ClientMac::from_str(&encoded_key).map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use nym_crypto::asymmetric::encryption;
|
||||
|
||||
#[test]
|
||||
fn create_ip_pair() {
|
||||
let ipv4: IpAddr = Ipv4Addr::from_str("10.1.10.50").unwrap().into();
|
||||
let ipv6: IpAddr = Ipv6Addr::from_str("fc01::0a32").unwrap().into();
|
||||
|
||||
assert_eq!(IpPair::from(ipv4), IpPair::from(ipv6));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "verify")]
|
||||
fn client_request_roundtrip() {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let gateway_key_pair = encryption::KeyPair::new(&mut rng);
|
||||
let client_key_pair = encryption::KeyPair::new(&mut rng);
|
||||
|
||||
let nonce = 1234567890;
|
||||
|
||||
let client = GatewayClient::new(
|
||||
client_key_pair.private_key(),
|
||||
x25519_dalek::PublicKey::from(gateway_key_pair.public_key().to_bytes()),
|
||||
IpPair::new("10.0.0.42".parse().unwrap(), "fc00::42".parse().unwrap()),
|
||||
nonce,
|
||||
);
|
||||
assert!(client.verify(gateway_key_pair.private_key(), nonce).is_ok())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::{
|
||||
registration::{FinalMessage, InitMessage},
|
||||
topup::TopUpMessage,
|
||||
};
|
||||
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::make_bincode_serializer;
|
||||
|
||||
use super::VERSION;
|
||||
|
||||
fn generate_random() -> u64 {
|
||||
use rand::RngCore;
|
||||
let mut rng = rand::rngs::OsRng;
|
||||
rng.next_u64()
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct AuthenticatorRequest {
|
||||
pub protocol: Protocol,
|
||||
pub data: AuthenticatorRequestData,
|
||||
pub request_id: u64,
|
||||
}
|
||||
|
||||
impl AuthenticatorRequest {
|
||||
pub fn from_reconstructed_message(
|
||||
message: &nym_sphinx::receiver::ReconstructedMessage,
|
||||
) -> Result<Self, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().deserialize(&message.message)
|
||||
}
|
||||
|
||||
pub fn new_initial_request(init_message: InitMessage) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
version: VERSION,
|
||||
},
|
||||
data: AuthenticatorRequestData::Initial(init_message),
|
||||
request_id,
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_final_request(final_message: FinalMessage) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
version: VERSION,
|
||||
},
|
||||
data: AuthenticatorRequestData::Final(Box::new(final_message)),
|
||||
request_id,
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_query_request(peer_public_key: PeerPublicKey) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
version: VERSION,
|
||||
},
|
||||
data: AuthenticatorRequestData::QueryBandwidth(peer_public_key),
|
||||
request_id,
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_topup_request(top_up_message: TopUpMessage) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
version: VERSION,
|
||||
},
|
||||
data: AuthenticatorRequestData::TopUpBandwidth(Box::new(top_up_message)),
|
||||
request_id,
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().serialize(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub enum AuthenticatorRequestData {
|
||||
Initial(InitMessage),
|
||||
Final(Box<FinalMessage>),
|
||||
QueryBandwidth(PeerPublicKey),
|
||||
TopUpBandwidth(Box<TopUpMessage>),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
fn check_first_bytes_protocol() {
|
||||
let version = 5;
|
||||
let data = AuthenticatorRequest {
|
||||
protocol: Protocol {
|
||||
version,
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
},
|
||||
data: AuthenticatorRequestData::Initial(InitMessage::new(
|
||||
PeerPublicKey::from_str("yvNUDpT5l7W/xDhiu6HkqTHDQwbs/B3J5UrLmORl1EQ=").unwrap(),
|
||||
)),
|
||||
request_id: 1,
|
||||
};
|
||||
let bytes = *data.to_bytes().unwrap().first_chunk::<2>().unwrap();
|
||||
assert_eq!(bytes, [version, ServiceProviderType::Authenticator as u8]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::registration::{RegistrationData, RegistredData, RemainingBandwidthData};
|
||||
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::make_bincode_serializer;
|
||||
|
||||
use super::VERSION;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct AuthenticatorResponse {
|
||||
pub protocol: Protocol,
|
||||
pub data: AuthenticatorResponseData,
|
||||
}
|
||||
|
||||
impl AuthenticatorResponse {
|
||||
pub fn new_pending_registration_success(
|
||||
registration_data: RegistrationData,
|
||||
request_id: u64,
|
||||
) -> Self {
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
version: VERSION,
|
||||
},
|
||||
data: AuthenticatorResponseData::PendingRegistration(PendingRegistrationResponse {
|
||||
reply: registration_data,
|
||||
request_id,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_registered(registred_data: RegistredData, request_id: u64) -> Self {
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
version: VERSION,
|
||||
},
|
||||
data: AuthenticatorResponseData::Registered(RegisteredResponse {
|
||||
reply: registred_data,
|
||||
request_id,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_remaining_bandwidth(
|
||||
remaining_bandwidth_data: Option<RemainingBandwidthData>,
|
||||
request_id: u64,
|
||||
) -> Self {
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
version: VERSION,
|
||||
},
|
||||
data: AuthenticatorResponseData::RemainingBandwidth(RemainingBandwidthResponse {
|
||||
reply: remaining_bandwidth_data,
|
||||
request_id,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_topup_bandwidth(
|
||||
remaining_bandwidth_data: RemainingBandwidthData,
|
||||
request_id: u64,
|
||||
) -> Self {
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
version: VERSION,
|
||||
},
|
||||
data: AuthenticatorResponseData::TopUpBandwidth(TopUpBandwidthResponse {
|
||||
reply: remaining_bandwidth_data,
|
||||
request_id,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().serialize(self)
|
||||
}
|
||||
|
||||
pub fn from_reconstructed_message(
|
||||
message: &nym_sphinx::receiver::ReconstructedMessage,
|
||||
) -> Result<Self, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().deserialize(&message.message)
|
||||
}
|
||||
|
||||
pub fn id(&self) -> Option<u64> {
|
||||
match &self.data {
|
||||
AuthenticatorResponseData::PendingRegistration(response) => Some(response.request_id),
|
||||
AuthenticatorResponseData::Registered(response) => Some(response.request_id),
|
||||
AuthenticatorResponseData::RemainingBandwidth(response) => Some(response.request_id),
|
||||
AuthenticatorResponseData::TopUpBandwidth(response) => Some(response.request_id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub enum AuthenticatorResponseData {
|
||||
PendingRegistration(PendingRegistrationResponse),
|
||||
Registered(RegisteredResponse),
|
||||
RemainingBandwidth(RemainingBandwidthResponse),
|
||||
TopUpBandwidth(TopUpBandwidthResponse),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct PendingRegistrationResponse {
|
||||
pub request_id: u64,
|
||||
pub reply: RegistrationData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct RegisteredResponse {
|
||||
pub request_id: u64,
|
||||
pub reply: RegistredData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct RemainingBandwidthResponse {
|
||||
pub request_id: u64,
|
||||
pub reply: Option<RemainingBandwidthData>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct TopUpBandwidthResponse {
|
||||
pub request_id: u64,
|
||||
pub reply: RemainingBandwidthData,
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct TopUpMessage {
|
||||
/// Base64 encoded x25519 public key
|
||||
pub pub_key: PeerPublicKey,
|
||||
|
||||
/// Ecash credential
|
||||
pub credential: CredentialSpendingData,
|
||||
}
|
||||
@@ -50,7 +50,7 @@ const DEFAULT_MINIMUM_REPLY_SURB_THRESHOLD_BUFFER: usize = 0;
|
||||
// define how much to request at once
|
||||
// clients/client-core/src/client/replies/reply_controller.rs
|
||||
const DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 10;
|
||||
const DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 100;
|
||||
const DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 50;
|
||||
|
||||
const DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE: u32 = 500;
|
||||
|
||||
@@ -658,6 +658,9 @@ pub struct ReplySurbs {
|
||||
/// Specifies the number of mixnet hops the packet should go through. If not specified, then
|
||||
/// the default value is used.
|
||||
pub surb_mix_hops: Option<u8>,
|
||||
|
||||
/// Specifies if we should reset all the sender tags on startup
|
||||
pub fresh_sender_tags: bool,
|
||||
}
|
||||
|
||||
impl Default for ReplySurbs {
|
||||
@@ -675,6 +678,7 @@ impl Default for ReplySurbs {
|
||||
maximum_reply_surb_age: DEFAULT_MAXIMUM_REPLY_SURB_AGE,
|
||||
maximum_reply_key_age: DEFAULT_MAXIMUM_REPLY_KEY_AGE,
|
||||
surb_mix_hops: None,
|
||||
fresh_sender_tags: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,6 +139,8 @@ where
|
||||
let gateway_setup = GatewaySetup::New {
|
||||
specification: selection_spec,
|
||||
available_gateways,
|
||||
#[cfg(unix)]
|
||||
connection_fd_callback: None,
|
||||
};
|
||||
|
||||
let init_details =
|
||||
|
||||
@@ -187,6 +187,8 @@ where
|
||||
let gateway_setup = GatewaySetup::New {
|
||||
specification: selection_spec,
|
||||
available_gateways,
|
||||
#[cfg(unix)]
|
||||
connection_fd_callback: None,
|
||||
};
|
||||
|
||||
let init_details =
|
||||
|
||||
@@ -88,7 +88,7 @@ pub async fn setup_fs_reply_surb_backend<P: AsRef<Path>>(
|
||||
let db_path = db_path.as_ref();
|
||||
if db_path.exists() {
|
||||
info!("loading existing surb database");
|
||||
match fs_backend::Backend::try_load(db_path).await {
|
||||
match fs_backend::Backend::try_load(db_path, surb_config.fresh_sender_tags).await {
|
||||
Ok(backend) => Ok(backend),
|
||||
Err(err) => {
|
||||
error!("failed to setup persistent storage backend for our reply needs: {err}. We're going to create a fresh database instead. This behaviour might change in the future");
|
||||
|
||||
@@ -33,10 +33,12 @@ pub enum PreparationError {
|
||||
#[error(transparent)]
|
||||
NymTopologyError(#[from] NymTopologyError),
|
||||
|
||||
#[error("The received message cannot be sent using a single reply surb. It ended up getting split into {fragments} fragments.")]
|
||||
#[error("message too long for a single SURB, splitting into {fragments} fragments.")]
|
||||
MessageTooLongForSingleSurb { fragments: usize },
|
||||
|
||||
#[error("Not enough reply SURBs to send the message. We have {available} available and require at least {required}.")]
|
||||
#[error(
|
||||
"not enough reply SURBs to send the message, available: {available} required: {required}."
|
||||
)]
|
||||
NotEnoughSurbs { available: usize, required: usize },
|
||||
}
|
||||
|
||||
|
||||
@@ -746,7 +746,7 @@ where
|
||||
.request_additional_reply_surbs(target, request_size)
|
||||
.await
|
||||
{
|
||||
warn!("failed to request additional surbs... - {err}")
|
||||
info!("{err}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ use nym_topology::node::RoutingNode;
|
||||
use nym_validator_client::client::IdentityKeyRef;
|
||||
use nym_validator_client::UserAgent;
|
||||
use rand::{seq::SliceRandom, Rng};
|
||||
#[cfg(unix)]
|
||||
use std::os::fd::RawFd;
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use tungstenite::Message;
|
||||
use url::Url;
|
||||
@@ -313,9 +315,15 @@ pub(super) async fn register_with_gateway(
|
||||
gateway_id: identity::PublicKey,
|
||||
gateway_listener: Url,
|
||||
our_identity: Arc<identity::KeyPair>,
|
||||
#[cfg(unix)] connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
|
||||
) -> Result<RegistrationResult, ClientCoreError> {
|
||||
let mut gateway_client =
|
||||
GatewayClient::new_init(gateway_listener, gateway_id, our_identity.clone());
|
||||
let mut gateway_client = GatewayClient::new_init(
|
||||
gateway_listener,
|
||||
gateway_id,
|
||||
our_identity.clone(),
|
||||
#[cfg(unix)]
|
||||
connection_fd_callback,
|
||||
);
|
||||
|
||||
gateway_client.establish_connection().await.map_err(|err| {
|
||||
log::warn!("Failed to establish connection with gateway!");
|
||||
|
||||
@@ -23,6 +23,8 @@ use nym_topology::node::RoutingNode;
|
||||
use rand::rngs::OsRng;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use serde::Serialize;
|
||||
#[cfg(unix)]
|
||||
use std::{os::fd::RawFd, sync::Arc};
|
||||
|
||||
pub mod helpers;
|
||||
pub mod types;
|
||||
@@ -53,6 +55,7 @@ async fn setup_new_gateway<K, D>(
|
||||
details_store: &D,
|
||||
selection_specification: GatewaySelectionSpecification,
|
||||
available_gateways: Vec<RoutingNode>,
|
||||
#[cfg(unix)] connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
|
||||
) -> Result<InitialisationResult, ClientCoreError>
|
||||
where
|
||||
K: KeyStore,
|
||||
@@ -108,9 +111,14 @@ where
|
||||
// if we're using a 'normal' gateway setup, do register
|
||||
let our_identity = client_keys.identity_keypair();
|
||||
|
||||
let registration =
|
||||
helpers::register_with_gateway(gateway_id, gateway_listener.clone(), our_identity)
|
||||
.await?;
|
||||
let registration = helpers::register_with_gateway(
|
||||
gateway_id,
|
||||
gateway_listener.clone(),
|
||||
our_identity,
|
||||
#[cfg(unix)]
|
||||
connection_fd_callback,
|
||||
)
|
||||
.await?;
|
||||
(
|
||||
GatewayDetails::new_remote(
|
||||
gateway_id,
|
||||
@@ -203,9 +211,19 @@ where
|
||||
GatewaySetup::New {
|
||||
specification,
|
||||
available_gateways,
|
||||
#[cfg(unix)]
|
||||
connection_fd_callback,
|
||||
} => {
|
||||
log::debug!("GatewaySetup::New with spec: {specification:?}");
|
||||
setup_new_gateway(key_store, details_store, specification, available_gateways).await
|
||||
setup_new_gateway(
|
||||
key_store,
|
||||
details_store,
|
||||
specification,
|
||||
available_gateways,
|
||||
#[cfg(unix)]
|
||||
connection_fd_callback,
|
||||
)
|
||||
.await
|
||||
}
|
||||
GatewaySetup::ReuseConnection {
|
||||
authenticated_ephemeral_client,
|
||||
|
||||
@@ -18,6 +18,8 @@ use nym_validator_client::client::IdentityKey;
|
||||
use nym_validator_client::nyxd::AccountId;
|
||||
use serde::Serialize;
|
||||
use std::fmt::{Debug, Display};
|
||||
#[cfg(unix)]
|
||||
use std::os::fd::RawFd;
|
||||
use std::sync::Arc;
|
||||
use time::OffsetDateTime;
|
||||
use url::Url;
|
||||
@@ -208,6 +210,10 @@ pub enum GatewaySetup {
|
||||
|
||||
// TODO: seems to be a bit inefficient to pass them by value
|
||||
available_gateways: Vec<RoutingNode>,
|
||||
|
||||
/// Callback useful for allowing initial connection to gateway
|
||||
#[cfg(unix)]
|
||||
connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
|
||||
},
|
||||
|
||||
ReuseConnection {
|
||||
@@ -231,6 +237,8 @@ impl Debug for GatewaySetup {
|
||||
GatewaySetup::New {
|
||||
specification,
|
||||
available_gateways,
|
||||
#[cfg(unix)]
|
||||
connection_fd_callback: _,
|
||||
} => f
|
||||
.debug_struct("GatewaySetup::New")
|
||||
.field("specification", specification)
|
||||
@@ -270,6 +278,8 @@ impl GatewaySetup {
|
||||
additional_data: None,
|
||||
},
|
||||
available_gateways: vec![],
|
||||
#[cfg(unix)]
|
||||
connection_fd_callback: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::{
|
||||
CombinedReplyStorage, ReceivedReplySurbsMap, ReplyStorageBackend, SentReplyKeys, UsedSenderTags,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use log::{error, info, warn};
|
||||
use log::{debug, error, info, warn};
|
||||
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
@@ -52,7 +52,10 @@ impl Backend {
|
||||
Ok(backend)
|
||||
}
|
||||
|
||||
pub async fn try_load<P: AsRef<Path>>(database_path: P) -> Result<Self, StorageError> {
|
||||
pub async fn try_load<P: AsRef<Path>>(
|
||||
database_path: P,
|
||||
fresh_sender_tags: bool,
|
||||
) -> Result<Self, StorageError> {
|
||||
let owned_path: PathBuf = database_path.as_ref().into();
|
||||
if owned_path.file_name().is_none() {
|
||||
return Err(StorageError::DatabasePathWithoutFilename {
|
||||
@@ -118,6 +121,9 @@ impl Backend {
|
||||
if days > 2 {
|
||||
info!("it's been over {days} days and {hours} hours since we last used our data store. our used sender tags are already outdated - we're going to purge them now.");
|
||||
manager.delete_all_tags().await?;
|
||||
} else if fresh_sender_tags {
|
||||
debug!("starting with fresh sender tags");
|
||||
manager.delete_all_tags().await?;
|
||||
}
|
||||
|
||||
Ok(Backend {
|
||||
|
||||
@@ -1065,6 +1065,7 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
|
||||
gateway_listener: Url,
|
||||
gateway_identity: identity::PublicKey,
|
||||
local_identity: Arc<identity::KeyPair>,
|
||||
#[cfg(unix)] connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
|
||||
) -> Self {
|
||||
log::trace!("Initialising gateway client");
|
||||
use futures::channel::mpsc;
|
||||
@@ -1090,7 +1091,7 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
|
||||
stats_reporter: ClientStatsSender::new(None, task_client.clone()),
|
||||
negotiated_protocol: None,
|
||||
#[cfg(unix)]
|
||||
connection_fd_callback: None,
|
||||
connection_fd_callback,
|
||||
task_client,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,8 @@ pub(crate) async fn connect_async(
|
||||
// Do a DNS lookup for the domain using our custom DNS resolver
|
||||
resolver
|
||||
.resolve_str(domain)
|
||||
.await?
|
||||
.await
|
||||
.inspect_err(|err| tracing::error!("Resolve error {err}"))?
|
||||
.into_iter()
|
||||
.map(|a| SocketAddr::new(a, port))
|
||||
.collect()
|
||||
@@ -49,20 +50,27 @@ pub(crate) async fn connect_async(
|
||||
address: endpoint.to_owned(),
|
||||
});
|
||||
for sock_addr in sock_addrs {
|
||||
tracing::info!("Trying with {sock_addr}");
|
||||
let socket = if sock_addr.is_ipv4() {
|
||||
TcpSocket::new_v4()
|
||||
} else {
|
||||
TcpSocket::new_v6()
|
||||
}
|
||||
.map_err(|err| GatewayClientError::NetworkConnectionFailed {
|
||||
address: endpoint.to_owned(),
|
||||
source: err.into(),
|
||||
.map_err(|err| {
|
||||
tracing::error!("Couldn't create the socket");
|
||||
GatewayClientError::NetworkConnectionFailed {
|
||||
address: endpoint.to_owned(),
|
||||
source: err.into(),
|
||||
}
|
||||
})?;
|
||||
|
||||
tracing::info!("Preparing to call callback");
|
||||
#[cfg(unix)]
|
||||
if let Some(callback) = connection_fd_callback.as_ref() {
|
||||
tracing::info!("Calling callback");
|
||||
callback.as_ref()(socket.as_raw_fd());
|
||||
}
|
||||
tracing::info!("Preparing to connect");
|
||||
|
||||
match socket.connect(sock_addr).await {
|
||||
Ok(s) => {
|
||||
|
||||
@@ -83,6 +83,12 @@ impl TryFrom<ContractVKShare> for EcashApiClient {
|
||||
|
||||
let url_address = Url::parse(&share.announce_address)?;
|
||||
|
||||
// The NymApiClient constructed here uses the default (hickory DoT/DoH) resolver because
|
||||
// this EcashApiClient is used by both client and non-client applications.
|
||||
//
|
||||
// In non-client applications this resolver can cause warning logs about H2 connection
|
||||
// failure. This indicates that the long lived https connection was closed by the remote
|
||||
// peer and the resolver will have to reconnect. It should not impact actual functionality
|
||||
Ok(EcashApiClient {
|
||||
api_client: NymApiClient::new(url_address),
|
||||
verification_key: VerificationKeyAuth::try_from_bs58(&share.share)?,
|
||||
|
||||
@@ -12,8 +12,9 @@ use nym_api_requests::ecash::models::{
|
||||
};
|
||||
use nym_api_requests::ecash::VerificationKeyResponse;
|
||||
use nym_api_requests::models::{
|
||||
AnnotationResponse, ApiHealthResponse, LegacyDescribedMixNode, NodePerformanceResponse,
|
||||
NodeRefreshBody, NymNodeDescription, PerformanceHistoryResponse, RewardedSetResponse,
|
||||
AnnotationResponse, ApiHealthResponse, BinaryBuildInformationOwned, ChainStatusResponse,
|
||||
LegacyDescribedMixNode, NodePerformanceResponse, NodeRefreshBody, NymNodeDescription,
|
||||
PerformanceHistoryResponse, RewardedSetResponse,
|
||||
};
|
||||
use nym_api_requests::nym_nodes::{
|
||||
NodesByAddressesRequestBody, NodesByAddressesResponse, PaginatedCachedNodesResponse,
|
||||
@@ -69,6 +70,19 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn build_information(&self) -> Result<BinaryBuildInformationOwned, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::API_STATUS_ROUTES,
|
||||
routes::BUILD_INFORMATION,
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[deprecated]
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
|
||||
@@ -1043,6 +1057,15 @@ pub trait NymApiClientExt: ApiClient {
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_chain_status(&self) -> Result<ChainStatusResponse, NymAPIError> {
|
||||
self.get_json(
|
||||
&[routes::API_VERSION, routes::NETWORK, routes::CHAIN_STATUS],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
|
||||
@@ -49,6 +49,8 @@ pub mod nym_nodes {
|
||||
pub const STATUS_ROUTES: &str = "status";
|
||||
pub const API_STATUS_ROUTES: &str = "api-status";
|
||||
pub const HEALTH: &str = "health";
|
||||
pub const BUILD_INFORMATION: &str = "build-information";
|
||||
|
||||
pub const MIXNODE: &str = "mixnode";
|
||||
pub const GATEWAY: &str = "gateway";
|
||||
pub const NYM_NODES: &str = "nym-nodes";
|
||||
@@ -70,4 +72,5 @@ pub const SUBMIT_NODE: &str = "submit-node-monitoring-results";
|
||||
pub const SERVICE_PROVIDERS: &str = "services";
|
||||
|
||||
pub const DETAILS: &str = "details";
|
||||
pub const CHAIN_STATUS: &str = "chain-status";
|
||||
pub const NETWORK: &str = "network";
|
||||
|
||||
@@ -62,6 +62,7 @@ pub use cw3;
|
||||
pub use cw4;
|
||||
pub use cw_controllers;
|
||||
pub use fee::{gas_price::GasPrice, GasAdjustable, GasAdjustment};
|
||||
pub use prost::Name;
|
||||
pub use tendermint_rpc::endpoint::block::Response as BlockResponse;
|
||||
pub use tendermint_rpc::{
|
||||
endpoint::{tx::Response as TxResponse, validators::Response as ValidatorResponse},
|
||||
|
||||
@@ -21,7 +21,7 @@ lazy_static = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
rand_chacha = { workspace = true }
|
||||
rand_core = { workspace = true }
|
||||
sha2 = "0.9"
|
||||
sha2 = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_derive = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
+96
-2
@@ -54,12 +54,12 @@ pub(crate) fn hash_to_scalar<M: AsRef<[u8]>>(msg: M, domain: &[u8]) -> Scalar {
|
||||
pub(crate) fn hash_to_scalars<M: AsRef<[u8]>>(msg: M, domain: &[u8], n: usize) -> Vec<Scalar> {
|
||||
let mut output = vec![Scalar::zero(); n];
|
||||
|
||||
Scalar::hash_to_field::<ExpandMsgXmd<Sha256>>(msg.as_ref(), domain, &mut output);
|
||||
Scalar::hash_to_field::<ExpandMsgXmd<Sha256>, _>([msg], domain, &mut output);
|
||||
output
|
||||
}
|
||||
|
||||
pub(crate) fn hash_g2<M: AsRef<[u8]>>(msg: M, domain: &[u8]) -> G2Projective {
|
||||
<G2Projective as HashToCurve<ExpandMsgXmd<Sha256>>>::hash_to_curve(msg, domain)
|
||||
<G2Projective as HashToCurve<ExpandMsgXmd<Sha256>>>::hash_to_curve([msg], domain)
|
||||
}
|
||||
|
||||
pub(crate) fn combine_scalar_chunks(chunks: &[Scalar]) -> Scalar {
|
||||
@@ -112,3 +112,97 @@ pub(crate) fn deserialize_g2(b: &[u8]) -> Option<G2Projective> {
|
||||
G2Projective::from_bytes(&encoding).into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use bls12_381::G2Affine;
|
||||
|
||||
#[test]
|
||||
fn test_hash_to_scalar() {
|
||||
let msg1 = "foo";
|
||||
let expected1 = Scalar::from_bytes(&[
|
||||
253, 57, 224, 227, 175, 195, 226, 82, 46, 175, 33, 126, 171, 239, 255, 92, 108, 168, 6,
|
||||
79, 90, 11, 235, 236, 221, 10, 85, 133, 42, 81, 95, 30,
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
let msg2 = "bar";
|
||||
let expected2 = Scalar::from_bytes(&[
|
||||
48, 83, 69, 52, 42, 18, 135, 244, 211, 190, 160, 196, 118, 154, 24, 126, 0, 125, 72,
|
||||
201, 170, 225, 123, 201, 52, 120, 171, 132, 235, 182, 20, 26,
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
let msg3 = [
|
||||
33, 135, 76, 234, 71, 35, 247, 216, 39, 242, 42, 88, 152, 29, 74, 135, 9, 29, 216, 123,
|
||||
250, 87, 108, 29, 245, 126, 109, 102, 84, 71, 158, 224, 145, 243, 49, 121, 244, 27,
|
||||
115, 121, 25, 66, 216, 67, 97, 101, 140, 160, 77, 239, 114, 215, 152, 48, 15, 231, 101,
|
||||
60, 42, 92, 128, 131, 161, 43,
|
||||
];
|
||||
let expected3 = Scalar::from_bytes(&[
|
||||
128, 189, 8, 43, 186, 55, 52, 61, 171, 196, 159, 177, 162, 100, 27, 143, 85, 83, 218,
|
||||
171, 91, 220, 155, 25, 7, 38, 2, 36, 4, 93, 136, 4,
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
hash_to_scalar(msg1, b"NYMECASH-V01-CS02-with-expander-SHA256"),
|
||||
expected1
|
||||
);
|
||||
assert_eq!(
|
||||
hash_to_scalar(msg2, b"NYMECASH-V01-CS02-with-expander-SHA256"),
|
||||
expected2
|
||||
);
|
||||
assert_eq!(
|
||||
hash_to_scalar(msg3, b"NYMECASH-V01-CS02-with-expander-SHA256"),
|
||||
expected3
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hash_g2() {
|
||||
let msg1 = "foo";
|
||||
let expected1 = G2Affine::from_compressed(&[
|
||||
175, 187, 62, 7, 29, 17, 42, 93, 28, 93, 234, 253, 101, 166, 158, 187, 153, 82, 93, 18,
|
||||
11, 233, 36, 107, 51, 117, 30, 127, 32, 254, 210, 77, 133, 12, 253, 255, 84, 128, 36,
|
||||
214, 234, 103, 50, 21, 26, 78, 112, 49, 20, 69, 19, 109, 7, 78, 33, 227, 196, 180, 168,
|
||||
219, 73, 251, 192, 221, 41, 138, 160, 131, 191, 186, 156, 117, 179, 179, 191, 235, 171,
|
||||
26, 219, 148, 170, 179, 11, 38, 137, 14, 95, 115, 171, 186, 163, 82, 158, 6, 239, 88,
|
||||
])
|
||||
.unwrap()
|
||||
.into();
|
||||
|
||||
let msg2 = "bar";
|
||||
let expected2 = G2Affine::from_compressed(&[
|
||||
183, 25, 90, 187, 34, 184, 30, 182, 215, 242, 158, 83, 116, 34, 210, 96, 188, 79, 83,
|
||||
255, 100, 122, 90, 188, 196, 93, 164, 253, 20, 106, 205, 33, 48, 140, 60, 149, 66, 246,
|
||||
121, 244, 146, 66, 170, 60, 113, 95, 102, 237, 25, 231, 8, 42, 121, 124, 180, 140, 34,
|
||||
104, 173, 251, 89, 189, 28, 196, 49, 66, 101, 38, 68, 44, 40, 235, 21, 35, 204, 123,
|
||||
218, 238, 216, 92, 134, 217, 212, 246, 176, 77, 187, 0, 245, 134, 132, 73, 31, 44, 137,
|
||||
197,
|
||||
])
|
||||
.unwrap()
|
||||
.into();
|
||||
let msg3 = [
|
||||
33, 135, 76, 234, 71, 35, 247, 216, 39, 242, 42, 88, 152, 29, 74, 135, 9, 29, 216, 123,
|
||||
250, 87, 108, 29, 245, 126, 109, 102, 84, 71, 158, 224, 145, 243, 49, 121, 244, 27,
|
||||
115, 121, 25, 66, 216, 67, 97, 101, 140, 160, 77, 239, 114, 215, 152, 48, 15, 231, 101,
|
||||
60, 42, 92, 128, 131, 161, 43,
|
||||
];
|
||||
let expected3 = G2Affine::from_compressed(&[
|
||||
151, 185, 8, 123, 223, 150, 192, 192, 115, 10, 3, 129, 49, 179, 31, 108, 0, 17, 46,
|
||||
231, 184, 164, 247, 228, 22, 142, 87, 70, 120, 111, 154, 15, 245, 110, 32, 84, 53, 117,
|
||||
239, 93, 89, 119, 32, 17, 39, 250, 198, 137, 6, 95, 137, 202, 54, 244, 238, 190, 11,
|
||||
217, 237, 95, 72, 59, 140, 56, 3, 42, 61, 195, 192, 101, 46, 204, 207, 75, 70, 176,
|
||||
207, 48, 24, 195, 248, 234, 178, 168, 54, 109, 19, 189, 51, 52, 120, 69, 248, 226, 102,
|
||||
91,
|
||||
])
|
||||
.unwrap()
|
||||
.into();
|
||||
|
||||
assert_eq!(hash_g2(msg1, b"DUMMY_TEST_DOMAIN"), expected1);
|
||||
assert_eq!(hash_g2(msg2, b"DUMMY_TEST_DOMAIN"), expected2);
|
||||
assert_eq!(hash_g2(msg3, b"DUMMY_TEST_DOMAIN"), expected3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use nym_sphinx::params::packet_sizes::PacketSize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::string::FromUtf8Error;
|
||||
use thiserror::Error;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
// specific errors (that should not be nested!!) for clients to match on
|
||||
#[derive(Debug, Copy, Clone, Error, Serialize, Deserialize)]
|
||||
@@ -112,15 +113,15 @@ pub enum AuthenticationFailure {
|
||||
#[error("failed to verify request signature")]
|
||||
InvalidSignature(#[from] SignatureError),
|
||||
|
||||
#[error("provided request timestamp is in the future")]
|
||||
RequestTimestampInFuture,
|
||||
|
||||
#[error("the client is not registered")]
|
||||
NotRegistered,
|
||||
|
||||
#[error("the provided request is too stale to process")]
|
||||
StaleRequest,
|
||||
#[error("the provided request timestamp is excessively skewed. got {received} whilst the server time is {server}")]
|
||||
ExcessiveTimestampSkew {
|
||||
received: OffsetDateTime,
|
||||
server: OffsetDateTime,
|
||||
},
|
||||
|
||||
#[error("the provided request timestamp is smaller or equal to a one previously used")]
|
||||
#[error("the provided request timestamp is smaller or equal to one previously used")]
|
||||
RequestReuse,
|
||||
}
|
||||
|
||||
@@ -38,13 +38,22 @@ impl AuthenticateRequest {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn verify_timestamp(&self, max_request_age: Duration) -> Result<(), AuthenticationFailure> {
|
||||
pub fn verify_timestamp(
|
||||
&self,
|
||||
max_request_timestamp_skew: Duration,
|
||||
) -> Result<(), AuthenticationFailure> {
|
||||
let now = OffsetDateTime::now_utc();
|
||||
if self.content.request_timestamp() + max_request_age < now {
|
||||
return Err(AuthenticationFailure::StaleRequest);
|
||||
if self.content.request_timestamp() < now - max_request_timestamp_skew {
|
||||
return Err(AuthenticationFailure::ExcessiveTimestampSkew {
|
||||
received: self.content.request_timestamp(),
|
||||
server: now,
|
||||
});
|
||||
}
|
||||
if self.content.request_timestamp() > now {
|
||||
return Err(AuthenticationFailure::RequestTimestampInFuture);
|
||||
if self.content.request_timestamp() - max_request_timestamp_skew > now {
|
||||
return Err(AuthenticationFailure::ExcessiveTimestampSkew {
|
||||
received: self.content.request_timestamp(),
|
||||
server: now,
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -21,6 +21,12 @@ serde_json = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
|
||||
# used for decoding text responses (they were already implicitly included)
|
||||
bytes = { workspace = true }
|
||||
encoding_rs = { workspace = true }
|
||||
mime = { workspace = true }
|
||||
|
||||
|
||||
nym-bin-common = { path = "../bin-common" }
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies]
|
||||
@@ -32,4 +38,4 @@ workspace = true
|
||||
features = ["tokio"]
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { workspace = true, features=["rt", "macros"] }
|
||||
tokio = { workspace = true, features = ["rt", "macros"] }
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//! DNS resolver configuration for internal lookups.
|
||||
//!
|
||||
//! The resolver itself is the set combination of the google, cloudflare, and quad9 endpoints
|
||||
@@ -9,6 +12,19 @@
|
||||
//!
|
||||
//! Requires the `dns-over-https-rustls`, `webpki-roots` feature for the
|
||||
//! `hickory-resolver` crate
|
||||
//!
|
||||
//!
|
||||
//! Note: The hickory DoH resolver can cause warning logs about H2 connection failure. This
|
||||
//! indicates that the long lived https connection was closed by the remote peer and the resolver
|
||||
//! will have to reconnect. It should not impact actual functionality.
|
||||
//!
|
||||
//! code ref: https://github.com/hickory-dns/hickory-dns/blob/06a8b1ce9bd9322d8e6accf857d30257e1274427/crates/proto/src/h2/h2_client_stream.rs#L534
|
||||
//!
|
||||
//! example log:
|
||||
//!
|
||||
//! ```txt
|
||||
//! WARN /home/ubuntu/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hickory-proto-0.24.3/src/h2/h2_client_stream.rs:493: h2 connection failed: unexpected end of file
|
||||
//! ```
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use crate::ClientBuilder;
|
||||
@@ -33,6 +49,13 @@ impl ClientBuilder {
|
||||
/// Override the DNS resolver implementation used by the underlying http client.
|
||||
pub fn dns_resolver<R: Resolve + 'static>(mut self, resolver: Arc<R>) -> Self {
|
||||
self.reqwest_client_builder = self.reqwest_client_builder.dns_resolver(resolver);
|
||||
self.use_secure_dns = false;
|
||||
self
|
||||
}
|
||||
|
||||
/// Override the DNS resolver implementation used by the underlying http client.
|
||||
pub fn no_hickory_dns(mut self) -> Self {
|
||||
self.use_secure_dns = false;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,13 +147,13 @@ use thiserror::Error;
|
||||
use tracing::{instrument, warn};
|
||||
use url::Url;
|
||||
|
||||
use http::HeaderMap;
|
||||
pub use reqwest::IntoUrl;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use std::net::SocketAddr;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use std::sync::Arc;
|
||||
|
||||
pub use reqwest::IntoUrl;
|
||||
|
||||
mod user_agent;
|
||||
pub use user_agent::UserAgent;
|
||||
|
||||
@@ -210,6 +210,12 @@ pub enum HttpClientError<E: Display = String> {
|
||||
#[error("failed to resolve request. status: '{status}', additional error message: {error}")]
|
||||
EndpointFailure { status: StatusCode, error: E },
|
||||
|
||||
#[error("failed to decode response body: {source} from {content}")]
|
||||
ResponseDecodeFailure {
|
||||
source: serde_json::Error,
|
||||
content: String,
|
||||
},
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[error("the request has timed out")]
|
||||
RequestTimeout,
|
||||
@@ -222,6 +228,8 @@ pub struct ClientBuilder {
|
||||
timeout: Option<Duration>,
|
||||
custom_user_agent: bool,
|
||||
reqwest_client_builder: reqwest::ClientBuilder,
|
||||
#[allow(dead_code)] // not dead code, just unused in wasm
|
||||
use_secure_dns: bool,
|
||||
}
|
||||
|
||||
impl ClientBuilder {
|
||||
@@ -233,37 +241,46 @@ impl ClientBuilder {
|
||||
U: IntoUrl,
|
||||
E: Display,
|
||||
{
|
||||
// a naive check: if the provided URL does not start with http(s), add that scheme
|
||||
let str_url = url.as_str();
|
||||
|
||||
// a naive check: if the provided URL does not start with http(s), add that scheme
|
||||
if !str_url.starts_with("http") {
|
||||
let alt = format!("http://{str_url}");
|
||||
warn!("the provided url ('{str_url}') does not contain scheme information. Changing it to '{alt}' ...");
|
||||
// TODO: or should we maybe default to https?
|
||||
Self::new(alt)
|
||||
} else {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let reqwest_client_builder = reqwest::ClientBuilder::new();
|
||||
Ok(Self::new_with_url(url.into_url()?))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let reqwest_client_builder = {
|
||||
let r = reqwest::ClientBuilder::new()
|
||||
.dns_resolver(Arc::new(HickoryDnsResolver::default()));
|
||||
/// Constructs a new http `ClientBuilder` from a valid url.
|
||||
pub fn new_with_url(url: Url) -> Self {
|
||||
if !url.scheme().starts_with("http") {
|
||||
warn!("the provided url ('{url}') does not use HTTP / HTTPS scheme");
|
||||
}
|
||||
|
||||
// Note this is extra as the `gzip` feature for `reqwest` crate should be enabled which
|
||||
// `"Enable[s] auto gzip decompression by checking the Content-Encoding response header."`
|
||||
//
|
||||
// I am going to leave it here anyways so that gzip decompression is attempted even if
|
||||
// that feature is removed.
|
||||
r.gzip(true)
|
||||
};
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let reqwest_client_builder = reqwest::ClientBuilder::new();
|
||||
|
||||
Ok(ClientBuilder {
|
||||
url: url.into_url()?,
|
||||
timeout: None,
|
||||
custom_user_agent: false,
|
||||
reqwest_client_builder,
|
||||
})
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let reqwest_client_builder = {
|
||||
let r = reqwest::ClientBuilder::new();
|
||||
|
||||
// Note this is extra as the `gzip` feature for `reqwest` crate should be enabled which
|
||||
// `"Enable[s] auto gzip decompression by checking the Content-Encoding response header."`
|
||||
//
|
||||
// I am going to leave it here anyways so that gzip decompression is attempted even if
|
||||
// that feature is removed.
|
||||
r.gzip(true)
|
||||
};
|
||||
|
||||
ClientBuilder {
|
||||
url,
|
||||
timeout: None,
|
||||
custom_user_agent: false,
|
||||
reqwest_client_builder,
|
||||
use_secure_dns: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,10 +336,18 @@ impl ClientBuilder {
|
||||
let mut builder = self
|
||||
.reqwest_client_builder
|
||||
.timeout(self.timeout.unwrap_or(DEFAULT_TIMEOUT));
|
||||
|
||||
// if no custom user agent was set, use a default
|
||||
if !self.custom_user_agent {
|
||||
builder =
|
||||
builder.user_agent(format!("nym-http-api-client/{}", env!("CARGO_PKG_VERSION")))
|
||||
}
|
||||
|
||||
// unless explicitly disabled use the DoT/DoH enabled resolver
|
||||
if self.use_secure_dns {
|
||||
builder = builder.dns_resolver(Arc::new(HickoryDnsResolver::default()));
|
||||
}
|
||||
|
||||
builder.build()?
|
||||
};
|
||||
|
||||
@@ -349,6 +374,9 @@ pub struct Client {
|
||||
impl Client {
|
||||
/// Create a new http `Client`
|
||||
// no timeout until https://github.com/seanmonstar/reqwest/issues/1135 is fixed
|
||||
//
|
||||
// In order to prevent interference in API requests at the DNS phase we default to a resolver
|
||||
// that uses DoT and DoH.
|
||||
pub fn new(base_url: Url, timeout: Option<Duration>) -> Self {
|
||||
Self::new_url::<_, String>(base_url, timeout).expect(
|
||||
"we provided valid url and we were unwrapping previous construction errors anyway",
|
||||
@@ -849,6 +877,26 @@ fn sanitize_url<K: AsRef<str>, V: AsRef<str>>(
|
||||
url
|
||||
}
|
||||
|
||||
fn decode_as_text(bytes: &bytes::Bytes, headers: HeaderMap) -> String {
|
||||
use encoding_rs::{Encoding, UTF_8};
|
||||
use mime::Mime;
|
||||
|
||||
let content_type = headers
|
||||
.get(http::header::CONTENT_TYPE)
|
||||
.and_then(|value| value.to_str().ok())
|
||||
.and_then(|value| value.parse::<Mime>().ok());
|
||||
|
||||
let encoding_name = content_type
|
||||
.as_ref()
|
||||
.and_then(|mime| mime.get_param("charset").map(|charset| charset.as_str()))
|
||||
.unwrap_or("utf-8");
|
||||
|
||||
let encoding = Encoding::for_label(encoding_name.as_bytes()).unwrap_or(UTF_8);
|
||||
|
||||
let (text, _, _) = encoding.decode(bytes);
|
||||
text.into_owned()
|
||||
}
|
||||
|
||||
/// Attempt to parse a json object from an HTTP response
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
pub async fn parse_response<T, E>(res: Response, allow_empty: bool) -> Result<T, HttpClientError<E>>
|
||||
@@ -864,21 +912,23 @@ where
|
||||
return Err(HttpClientError::EmptyResponse { status });
|
||||
}
|
||||
}
|
||||
let headers = res.headers().clone();
|
||||
tracing::trace!("headers: {:?}", headers);
|
||||
|
||||
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()))
|
||||
// internally reqwest is first retrieving bytes and then performing parsing via serde_json
|
||||
// (and similarly does the same thing for text())
|
||||
let full = res.bytes().await?;
|
||||
match serde_json::from_slice(&full) {
|
||||
Ok(data) => Ok(data),
|
||||
Err(err) => {
|
||||
let content = decode_as_text(&full, headers);
|
||||
Err(HttpClientError::ResponseDecodeFailure {
|
||||
source: err,
|
||||
content,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
Ok(res.json().await?)
|
||||
} else if res.status() == StatusCode::NOT_FOUND {
|
||||
Err(HttpClientError::NotFound)
|
||||
} else {
|
||||
|
||||
@@ -15,10 +15,10 @@ bls12_381 = { workspace = true, features = ["alloc", "pairings", "experimental",
|
||||
bincode.workspace = true
|
||||
cfg-if.workspace = true
|
||||
itertools = { workspace = true }
|
||||
digest = "0.9"
|
||||
digest = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
sha2 = "0.9"
|
||||
sha2 = { workspace = true }
|
||||
bs58 = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
rayon = { workspace = true, optional = true }
|
||||
|
||||
@@ -113,17 +113,13 @@ const G1_HASH_DOMAIN: &[u8] = b"NYMECASH-V01-CS02-with-BLS12381G1_XMD:SHA-256_SS
|
||||
const SCALAR_HASH_DOMAIN: &[u8] = b"NYMECASH-V01-CS02-with-expander-SHA256";
|
||||
|
||||
pub fn hash_g1<M: AsRef<[u8]>>(msg: M) -> G1Projective {
|
||||
<G1Projective as HashToCurve<ExpandMsgXmd<sha2::Sha256>>>::hash_to_curve(msg, G1_HASH_DOMAIN)
|
||||
<G1Projective as HashToCurve<ExpandMsgXmd<sha2::Sha256>>>::hash_to_curve([msg], G1_HASH_DOMAIN)
|
||||
}
|
||||
|
||||
pub fn hash_to_scalar<M: AsRef<[u8]>>(msg: M) -> Scalar {
|
||||
let mut output = vec![Scalar::zero()];
|
||||
|
||||
Scalar::hash_to_field::<ExpandMsgXmd<sha2::Sha256>>(
|
||||
msg.as_ref(),
|
||||
SCALAR_HASH_DOMAIN,
|
||||
&mut output,
|
||||
);
|
||||
Scalar::hash_to_field::<ExpandMsgXmd<sha2::Sha256>, _>([msg], SCALAR_HASH_DOMAIN, &mut output);
|
||||
output[0]
|
||||
}
|
||||
|
||||
@@ -401,4 +397,75 @@ mod tests {
|
||||
assert_eq!(hash_to_scalar(msg2), hash_to_scalar(msg2));
|
||||
assert_ne!(hash_to_scalar(msg1), hash_to_scalar(msg2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hash_to_scalar() {
|
||||
let msg1 = "foo";
|
||||
let expected1 = Scalar::from_bytes(&[
|
||||
253, 57, 224, 227, 175, 195, 226, 82, 46, 175, 33, 126, 171, 239, 255, 92, 108, 168, 6,
|
||||
79, 90, 11, 235, 236, 221, 10, 85, 133, 42, 81, 95, 30,
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
let msg2 = "bar";
|
||||
let expected2 = Scalar::from_bytes(&[
|
||||
48, 83, 69, 52, 42, 18, 135, 244, 211, 190, 160, 196, 118, 154, 24, 126, 0, 125, 72,
|
||||
201, 170, 225, 123, 201, 52, 120, 171, 132, 235, 182, 20, 26,
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
let msg3 = [
|
||||
33, 135, 76, 234, 71, 35, 247, 216, 39, 242, 42, 88, 152, 29, 74, 135, 9, 29, 216, 123,
|
||||
250, 87, 108, 29, 245, 126, 109, 102, 84, 71, 158, 224, 145, 243, 49, 121, 244, 27,
|
||||
115, 121, 25, 66, 216, 67, 97, 101, 140, 160, 77, 239, 114, 215, 152, 48, 15, 231, 101,
|
||||
60, 42, 92, 128, 131, 161, 43,
|
||||
];
|
||||
let expected3 = Scalar::from_bytes(&[
|
||||
128, 189, 8, 43, 186, 55, 52, 61, 171, 196, 159, 177, 162, 100, 27, 143, 85, 83, 218,
|
||||
171, 91, 220, 155, 25, 7, 38, 2, 36, 4, 93, 136, 4,
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(hash_to_scalar(msg1), expected1);
|
||||
assert_eq!(hash_to_scalar(msg2), expected2);
|
||||
assert_eq!(hash_to_scalar(msg3), expected3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hash_to_g1() {
|
||||
let msg1 = "foo";
|
||||
let expected1 = G1Affine::from_compressed(&[
|
||||
161, 109, 186, 0, 192, 221, 83, 87, 71, 31, 120, 201, 185, 35, 62, 239, 46, 120, 117,
|
||||
150, 191, 227, 128, 161, 78, 201, 207, 167, 86, 181, 229, 115, 2, 6, 178, 16, 251, 118,
|
||||
219, 115, 184, 96, 2, 10, 31, 63, 150, 70,
|
||||
])
|
||||
.unwrap()
|
||||
.into();
|
||||
|
||||
let msg2 = "bar";
|
||||
let expected2 = G1Affine::from_compressed(&[
|
||||
135, 102, 204, 42, 221, 49, 209, 192, 250, 87, 59, 255, 197, 93, 37, 113, 38, 2, 154,
|
||||
233, 68, 234, 206, 182, 121, 212, 166, 210, 74, 155, 190, 33, 203, 237, 176, 60, 249,
|
||||
241, 53, 170, 18, 168, 49, 35, 1, 151, 205, 174,
|
||||
])
|
||||
.unwrap()
|
||||
.into();
|
||||
let msg3 = [
|
||||
33, 135, 76, 234, 71, 35, 247, 216, 39, 242, 42, 88, 152, 29, 74, 135, 9, 29, 216, 123,
|
||||
250, 87, 108, 29, 245, 126, 109, 102, 84, 71, 158, 224, 145, 243, 49, 121, 244, 27,
|
||||
115, 121, 25, 66, 216, 67, 97, 101, 140, 160, 77, 239, 114, 215, 152, 48, 15, 231, 101,
|
||||
60, 42, 92, 128, 131, 161, 43,
|
||||
];
|
||||
let expected3 = G1Affine::from_compressed(&[
|
||||
184, 200, 211, 115, 47, 45, 39, 185, 105, 9, 222, 247, 132, 241, 121, 130, 238, 224,
|
||||
155, 109, 105, 201, 137, 154, 132, 149, 214, 233, 136, 69, 77, 132, 174, 30, 46, 123,
|
||||
20, 92, 219, 18, 45, 29, 208, 127, 158, 145, 130, 41,
|
||||
])
|
||||
.unwrap()
|
||||
.into();
|
||||
|
||||
assert_eq!(hash_g1(msg1), expected1);
|
||||
assert_eq!(hash_g1(msg2), expected2);
|
||||
assert_eq!(hash_g1(msg3), expected3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ pub use sphinx_packet::{
|
||||
route::{Destination, DestinationAddressBytes, Node, NodeAddressBytes, SURBIdentifier},
|
||||
surb::{SURBMaterial, SURB},
|
||||
version::Version,
|
||||
version::UPDATED_LEGACY_VERSION,
|
||||
Error as SphinxError, ProcessedPacket, ProcessedPacketData,
|
||||
};
|
||||
|
||||
@@ -91,12 +90,8 @@ impl NymPacket {
|
||||
destination: &Destination,
|
||||
delays: &[Delay],
|
||||
) -> Result<NymPacket, NymPacketError> {
|
||||
// FIXME:
|
||||
// for now explicitly use the legacy version until sufficient number of nodes
|
||||
// understand both variants
|
||||
Ok(NymPacket::Sphinx(
|
||||
SphinxPacketBuilder::new()
|
||||
.with_version(UPDATED_LEGACY_VERSION)
|
||||
.with_payload_size(size)
|
||||
.build_packet(message, route, destination, delays)?,
|
||||
))
|
||||
|
||||
@@ -182,9 +182,11 @@ impl BlockProcessor {
|
||||
// the ones concerned with individual messages
|
||||
for (index, msg) in block_tx.tx.body.messages.iter().enumerate() {
|
||||
for msg_module in &mut self.msg_modules {
|
||||
msg_module
|
||||
.handle_msg(index, msg, &block_tx, &mut tx)
|
||||
.await?
|
||||
if msg.type_url == msg_module.type_url() {
|
||||
msg_module
|
||||
.handle_msg(index, msg, &block_tx, &mut tx)
|
||||
.await?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +83,15 @@ pub enum ScraperError {
|
||||
source: cosmrs::ErrorReport,
|
||||
},
|
||||
|
||||
#[error("could not parse msg in tx {hash} at index {index} into {type_url}: {source}")]
|
||||
MsgParseFailure {
|
||||
hash: Hash,
|
||||
index: usize,
|
||||
type_url: String,
|
||||
#[source]
|
||||
source: cosmrs::ErrorReport,
|
||||
},
|
||||
|
||||
#[error("received an invalid chain subscription event of kind {kind} while we were waiting for new block data (query: '{query}')")]
|
||||
InvalidSubscriptionEvent { query: String, kind: String },
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ use cosmrs::Any;
|
||||
|
||||
#[async_trait]
|
||||
pub trait MsgModule {
|
||||
fn type_url(&self) -> String;
|
||||
|
||||
async fn handle_msg(
|
||||
&mut self,
|
||||
index: usize,
|
||||
|
||||
@@ -9,3 +9,4 @@ repository = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
pem = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
@@ -6,6 +6,7 @@ use pem::Pem;
|
||||
use std::fs::File;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use tracing::debug;
|
||||
|
||||
pub mod traits;
|
||||
|
||||
@@ -46,6 +47,10 @@ where
|
||||
T: PemStorableKey,
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
debug!(
|
||||
"attempting to load key with the following pem type: {}",
|
||||
T::pem_type()
|
||||
);
|
||||
let key_pem = read_pem_file(path)?;
|
||||
|
||||
if T::pem_type() != key_pem.tag {
|
||||
|
||||
@@ -103,4 +103,8 @@ impl LaneQueueLengthsInner {
|
||||
{
|
||||
self.map.entry(*lane).and_modify(f);
|
||||
}
|
||||
|
||||
pub fn total(&self) -> usize {
|
||||
self.map.values().sum()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -494,6 +494,9 @@ pub struct ReplySurbsWasm {
|
||||
/// Defines how many mix nodes the reply surb should go through.
|
||||
/// If not set, the default value is going to be used.
|
||||
pub surb_mix_hops: Option<u8>,
|
||||
|
||||
/// Specifies if we should reset all the sender tags on startup
|
||||
pub fresh_sender_tags: bool,
|
||||
}
|
||||
|
||||
impl Default for ReplySurbsWasm {
|
||||
@@ -525,6 +528,7 @@ impl From<ReplySurbsWasm> for ConfigReplySurbs {
|
||||
reply_surbs.maximum_reply_key_age_ms as u64,
|
||||
),
|
||||
surb_mix_hops: reply_surbs.surb_mix_hops,
|
||||
fresh_sender_tags: reply_surbs.fresh_sender_tags,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -548,6 +552,7 @@ impl From<ConfigReplySurbs> for ReplySurbsWasm {
|
||||
maximum_reply_surb_age_ms: reply_surbs.maximum_reply_surb_age.as_millis() as u32,
|
||||
maximum_reply_key_age_ms: reply_surbs.maximum_reply_key_age.as_millis() as u32,
|
||||
surb_mix_hops: reply_surbs.surb_mix_hops,
|
||||
fresh_sender_tags: reply_surbs.fresh_sender_tags,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -378,6 +378,9 @@ pub struct ReplySurbsWasmOverride {
|
||||
|
||||
#[tsify(optional)]
|
||||
pub surb_mix_hops: Option<u8>,
|
||||
|
||||
/// Specifies if we should reset all the sender tags on startup
|
||||
pub fresh_sender_tags: bool,
|
||||
}
|
||||
|
||||
impl From<ReplySurbsWasmOverride> for ReplySurbsWasm {
|
||||
@@ -416,6 +419,7 @@ impl From<ReplySurbsWasmOverride> for ReplySurbsWasm {
|
||||
.maximum_reply_key_age_ms
|
||||
.unwrap_or(def.maximum_reply_key_age_ms),
|
||||
surb_mix_hops: value.surb_mix_hops,
|
||||
fresh_sender_tags: value.fresh_sender_tags,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+50
-12
@@ -1185,6 +1185,7 @@ name = "nym-pemstore"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"pem",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1251,6 +1252,12 @@ dependencies = [
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||
|
||||
[[package]]
|
||||
name = "pkcs8"
|
||||
version = "0.9.0"
|
||||
@@ -1522,18 +1529,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.25"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03"
|
||||
checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.217"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
|
||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
@@ -1558,9 +1565,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.217"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1777,9 +1784,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.37"
|
||||
version = "0.3.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
|
||||
checksum = "dad298b01a40a23aac4580b67e3dbedb7cc8402f3592d7f49469de2ea4aecdd8"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
@@ -1794,15 +1801,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
||||
checksum = "765c97a5b985b7c11d7bc27fa927dc4fe6af3a6dfb021d28deb60d3bf51e76ef"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.19"
|
||||
version = "0.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
|
||||
checksum = "e8093bc3e81c3bc5f7879de09619d06c9a5a5e45ca44dfeeb7225bae38005c5c"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
@@ -1840,6 +1847,37 @@ dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
||||
dependencies = [
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.18.0"
|
||||
|
||||
@@ -7,4 +7,6 @@ package-lock.json
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
.env
|
||||
.env
|
||||
|
||||
scratch.md
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
- Run synchronization against [pool.ntp.org](https://www.ntppool.org/en/):
|
||||
```bash
|
||||
ntpdate -q pool.ntp.org
|
||||
```
|
||||
- Enable `ntp` service, start and review the status:
|
||||
```bash
|
||||
systemctl enable --now ntp
|
||||
service ntp start
|
||||
service ntp status
|
||||
```
|
||||
+1
-1
@@ -1 +1 @@
|
||||
807_251_217
|
||||
808_623_916
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
0.64%
|
||||
0.68%
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
44.811
|
||||
42.157
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
1_025_628
|
||||
1_028_488
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
403_625_608
|
||||
404_311_958
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
| **Item** | **Description** | **Amount in NYM** |
|
||||
|:-------------------|:------------------------------------------------------|--------------------:|
|
||||
| Total Supply | Maximum amount of NYM token in existence | 1_000_000_000 |
|
||||
| Mixmining Reserve | Tokens releasing for operators rewards | 192_748_782 |
|
||||
| Mixmining Reserve | Tokens releasing for operators rewards | 191_376_083 |
|
||||
| Vesting Tokens | Tokens locked outside of cicrulation for future claim | 0 |
|
||||
| Circulating Supply | Amount of unlocked tokens | 807_251_217 |
|
||||
| Stake Saturation | Optimal size of node self-bond + delegation | 1_025_628 |
|
||||
| Circulating Supply | Amount of unlocked tokens | 808_623_916 |
|
||||
| Stake Saturation | Optimal size of node self-bond + delegation | 1_028_488 |
|
||||
|
||||
@@ -1 +1 @@
|
||||
Wednesday, February 26th 2025, 16:02:47 UTC
|
||||
Tuesday, March 11th 2025, 11:04:18 UTC
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
{
|
||||
"mainnet":"Mainnet Endpoints",
|
||||
"sandbox":"Sandbox Endpoints"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
{
|
||||
"mainnet":"Mainnet Endpoints",
|
||||
"sandbox":"Sandbox Endpoints"
|
||||
}
|
||||
@@ -1,11 +1,8 @@
|
||||
import { Callout } from 'nextra/components'
|
||||
|
||||
# Node Status API
|
||||
|
||||
The Node Status API contains information about the network, its topology, and the routing scores of all nodes within it. It offers broadly similar information to the experimental [Habourmaster frontend](https://harbourmaster.nymtech.net/) but is stable where the Harbourmaster is subject to sudden changes as we modify and experiment with how we scrape and present data about the Mixnet infrastructure.
|
||||
The Node Status API serves information about individual `nym-nodes` in the Mixnet, such as which role they are operating in, statistics about them, services such as Network Requesters, as well as summaries of the state of the Mixnet.
|
||||
|
||||
<Callout type="info">
|
||||
People building applications or dashboards which requires information about nodes, their uptime, and their delegations should use this instead of Habourmaster.
|
||||
We recommend that developers building applications such as explorers or analytics interfaces about the Mixnet run their own instance of the API, in order to promote a robust network of downstream services, and spread the load of API calls amongst as many endpoints as possible.
|
||||
</Callout>
|
||||
|
||||
The code for this service can be found [in our monorepo](https://github.com/nymtech/nym/tree/develop/nym-node-status-api). In the future we will encourage developers to run their own instance of this API in order to distribute endpoints and query load.
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"ns-api-run-deploy":"Run Instance",
|
||||
"mainnet":"Mainnet Endpoints",
|
||||
"sandbox":"Sandbox Endpoints"
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
import { RedocStandalone } from 'redoc';
|
||||
import { Callout } from 'nextra/components'
|
||||
|
||||
The information below is generated with [Redoc](https://redocly.com/docs/redoc) consuming the OpenAPI spec found at [https://mainnet-node-status-api.nymtech.cc/api-docs/openapi.json](https://mainnet-node-status-api.nymtech.cc/api-docs/openapi.json) which is also used to generate the Swagger docs deployed at [https://mainnet-node-status-api.nymtech.cc/swagger/](https://mainnet-node-status-api.nymtech.cc/swagger/).
|
||||
|
||||
<br /><br />
|
||||
|
||||
|
||||
<RedocStandalone
|
||||
specUrl="https://mainnet-node-status-api.nymtech.cc/api-docs/openapi.json"
|
||||
options={{
|
||||
|
||||
@@ -0,0 +1,336 @@
|
||||
import { Callout } from 'nextra/components'
|
||||
import { AccordionTemplate } from 'components/accordion-template.tsx'
|
||||
|
||||
# NS API: Deployment Guide
|
||||
|
||||
## Components
|
||||
The Node Status API is made up of 3 components:
|
||||
- `nym-node-status-api`
|
||||
- `nym-node-status-client`
|
||||
- `nym-node-status-agent`
|
||||
|
||||
The API stores its data in a local SQLite database. It periodically gets most of its data from a [NymAPI](./nym-api) instance, and for instances also running with the Gateway Probe, uses the stored topology to conduct performance probe tests.
|
||||
|
||||
## Gateway Probe
|
||||
You can run the `nym-node-status-api` alone, but if you want to run probes for `nym-node`s running as Gateways, you have to also run the `nym-node-status-agent` (which consumes the `nym-node-status-client` lib). The probe will run a set of tests per Gateway node, and return the sort of results seen [here](https://harbourmaster.nymtech.net/gateway/23A7CSaBSA2L67PWuFTPXUnYrCdyVcB7ATYsjUsfdftb).
|
||||
|
||||
## UI
|
||||
The API exposes a [Swagger](https://swagger.io/docs/) documentation UI by default on port `8000` (see an example [here](https://mainnet-node-status-api.nymtech.cc/swagger/)).
|
||||
|
||||
Currently we are not shipping a custom UI component for the Node Status API. The [Harbourmaster](https://harbourmaster.nymtech.net/) frontend consumes this API, amongst other things, but this is an internal repo we use to experiment with new APIs and data on, so it is not public yet.
|
||||
|
||||
<Callout type="info">
|
||||
We invite developers to roll their own UI for their Node Status API instance.
|
||||
</Callout>
|
||||
|
||||
## Docker Images
|
||||
We will ship Docker images for both the `agent` and `api` in the future. There are Docker images for both in root of each corresponding crate ([`agent`](https://github.com/nymtech/nym/blob/09ea406c02e9a3beebc062f525e4ea1b4222dcbb/nym-node-status-api/nym-node-status-agent/Dockerfile), [`api`](https://github.com/nymtech/nym/blob/develop/nym-node-status-api/nym-node-status-api/Dockerfile)) which are used internally, which could be a starting point for developers to dockerize their instance for the moment.
|
||||
|
||||
## Build
|
||||
### Prerequisites
|
||||
- Rust
|
||||
- SQLite
|
||||
- Get an `ipinfo` key following instructions [here](https://github.com/ipinfo/rust?tab=readme-ov-file#getting-started).
|
||||
|
||||
### Compilation
|
||||
```shell
|
||||
cargo build --release --package nym-node-status-api --package nym-node-status-agent --package nym-node-status-client
|
||||
```
|
||||
|
||||
## Run
|
||||
Since the Node Status API depends on both flags and environmental variables, it might be easier to run the binary via a script like the one below - this this script essentially just `source`-s the defined `.env` file after exporting certain binary-specific variables, and then runs the binary. You can find the `.env` files [here](https://github.com/nymtech/nym/tree/master/envs).
|
||||
|
||||
<Callout type="info">
|
||||
All CLI flags are configurable as environmental variables and vice versa, so take the following scripts / setups as guides that you can change however best suits your setup. You can see all definitions [here](https://github.com/nymtech/nym/blob/develop/nym-node-status-api/nym-node-status-api/src/cli/mod.rs#L14).
|
||||
</Callout>
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
export ENVIRONMENT=${ENVIRONMENT:-"mainnet"} # see nym/envs/ for all possible environments
|
||||
export NYM_API_CLIENT_TIMEOUT=60
|
||||
export NODE_STATUS_API_TESTRUN_REFRESH_INTERVAL=120
|
||||
export IPINFO_API_TOKEN=<YOUR_IPINFO_API_KEY>
|
||||
|
||||
monorepo_root=<PATH/TO/NYM/>
|
||||
set -a
|
||||
source "${monorepo_root}/envs/${ENVIRONMENT}.env"
|
||||
echo ${monorepo_root}/envs/${ENVIRONMENT}.env
|
||||
set +a
|
||||
export RUST_LOG=${RUST_LOG:-debug} # debug is useful to check everything is working initially, but quite verbose
|
||||
|
||||
echo "Verifying environment variables were properly sourced:"
|
||||
echo "RUST_LOG=${RUST_LOG}"
|
||||
echo "BECH32_PREFIX=${BECH32_PREFIX}"
|
||||
echo "NETWORK_NAME=${NETWORK_NAME}"
|
||||
|
||||
<PATH/TO/>nym-node-status-api -- --ipinfo-api-token $IPINFO_API_TOKEN
|
||||
```
|
||||
|
||||
### Functionality Without Gateway Probe
|
||||
Data will be restricted to information that doesn't involve Probe results; `routing` and `config` scores will be `0` and `last_probe` results `null`, as you can see in this snipped output of the `gateways` endpoint:
|
||||
|
||||
<AccordionTemplate name="Output">
|
||||
```shell
|
||||
{
|
||||
"gateway_identity_key": "23A7CSaBSA2L67PWuFTPXUnYrCdyVcB7ATYsjUsfdftb",
|
||||
"bonded": true,
|
||||
"performance": 99,
|
||||
"self_described": {
|
||||
"authenticator": {
|
||||
"address": "6Gdtw13Fa46AvkqkHELZZCMKWASDodoJeK9APRNpjjdj.7ji8DDkpjA2AdgwK7wbZm8yi4xZGogGJeypBQt4hAw3P@23A7CSaBSA2L67PWuFTPXUnYrCdyVcB7ATYsjUsfdftb"
|
||||
},
|
||||
"auxiliary_details": {
|
||||
"accepted_operator_terms_and_conditions": true,
|
||||
"announce_ports": {
|
||||
"mix_port": null,
|
||||
"verloc_port": null
|
||||
},
|
||||
"location": null
|
||||
},
|
||||
"build_information": {
|
||||
"binary_name": "nym-node",
|
||||
"build_timestamp": "2025-02-13T11:49:34.670488195Z",
|
||||
"build_version": "1.5.0",
|
||||
"cargo_profile": "release",
|
||||
"cargo_triple": "x86_64-unknown-linux-gnu",
|
||||
"commit_branch": "HEAD",
|
||||
"commit_sha": "a3e19b4563843055b305ea9a397eb1ad84b5c378",
|
||||
"commit_timestamp": "2025-02-10T18:14:47.000000000+01:00",
|
||||
"rustc_channel": "stable",
|
||||
"rustc_version": "1.84.1"
|
||||
},
|
||||
"declared_role": {
|
||||
"entry": true,
|
||||
"exit_ipr": true,
|
||||
"exit_nr": true,
|
||||
"mixnode": false
|
||||
},
|
||||
"host_information": {
|
||||
"hostname": "bwng1.bwnym.xyz",
|
||||
"ip_address": [
|
||||
"95.164.2.86"
|
||||
],
|
||||
"keys": {
|
||||
"ed25519": "23A7CSaBSA2L67PWuFTPXUnYrCdyVcB7ATYsjUsfdftb",
|
||||
"x25519": "H6pFjqtdSVxkxEQ3wFnuSoobDAUqHx1bYMVJzPZdRByn",
|
||||
"x25519_noise": null
|
||||
}
|
||||
},
|
||||
"ip_packet_router": {
|
||||
"address": "7ms2D2uYiTuhX6MKeVL5rz5usgehEoxAAovwYm9nJyBF.3siMjk3wTU7ykaXLNi9c7LpX8yonYKPCA4BQoMwhsfTV@23A7CSaBSA2L67PWuFTPXUnYrCdyVcB7ATYsjUsfdftb"
|
||||
},
|
||||
"last_polled": "2025-03-03 09:48:03.635274187 +00:00:00",
|
||||
"mixnet_websockets": {
|
||||
"ws_port": 9000,
|
||||
"wss_port": 9001
|
||||
},
|
||||
"network_requester": {
|
||||
"address": "HuNL1pFprNSKW6jdqppibXP5KNKCNJxDh7ivpYcoULN9.C62NahRTUf6kqpNtDVHXoVriQr6yyaU5LtxdgpbsGrtA@23A7CSaBSA2L67PWuFTPXUnYrCdyVcB7ATYsjUsfdftb",
|
||||
"uses_exit_policy": true
|
||||
},
|
||||
"wireguard": {
|
||||
"port": 51822,
|
||||
"public_key": "6o8x9GitFjcrkjrJnivWaQCPnxXykQPYLneNr2FEB8Vq"
|
||||
}
|
||||
},
|
||||
"explorer_pretty_bond": {
|
||||
"identity_key": "23A7CSaBSA2L67PWuFTPXUnYrCdyVcB7ATYsjUsfdftb",
|
||||
"location": {
|
||||
"latitude": 52.5083,
|
||||
"longitude": 5.475,
|
||||
"two_letter_iso_country_code": "NL"
|
||||
},
|
||||
"owner": "n1cp5gq0apat6c7qmenqp5zjprn2vwvc7jl29j8r",
|
||||
"pledge_amount": {
|
||||
"amount": "100000000",
|
||||
"denom": "unym"
|
||||
}
|
||||
},
|
||||
"description": {
|
||||
"moniker": "bwn_g1",
|
||||
"website": "https://bwnym.xyz",
|
||||
"security_contact": "bwnym@proton.me",
|
||||
"details": "This gateway is part of the NYM project, which is dedicated to create outstanding privacy software that is legally compliant without sacrificing integrity or having any backdoors."
|
||||
},
|
||||
"last_probe_result": null,
|
||||
"last_probe_log": null,
|
||||
"last_testrun_utc": null,
|
||||
"last_updated_utc": "2025-03-03T10:45:48+00:00",
|
||||
"routing_score": 0.0,
|
||||
"config_score": 0
|
||||
},
|
||||
```
|
||||
</AccordionTemplate>
|
||||
|
||||
If you have already run the API before, make sure to add the following to your script `--database-url "sqlite://node-status-api.sqlite?mode=rwc` so it will continue using the same DB.
|
||||
|
||||
### Functionality with Gateway Probe
|
||||
If you want to enable Gateway node probes and have the NS API store that data, you need to periodically run the `nym-node-status-agent`, authenticated with your `nym-node-status-api` instance. Authentication is to make sure that only the `-agent` you are operating (or others you trust) are submitting data to your API instance.
|
||||
|
||||
#### Compile Gateway Probe
|
||||
The `nym-node-status-agent` is a thin wrapper around the Gateway Probe binary which currently is in the NymVPN repo. `git checkout` to the most recent [release](https://github.com/nymtech/nym-vpn-client/releases), and compile the probe by following the [readme instructions](https://github.com/nymtech/nym-vpn-client/tree/develop/nym-vpn-core/crates/nym-gateway-probe). You will point the `-agent` at this binary when doing Probe testruns.
|
||||
|
||||
#### Generate Keypair
|
||||
```shell
|
||||
<PATH/TO/>nym-node-status-agent generate-keypair --path <PATH/TO/KEY/FILE/TO/GENERATE>/<KEY_NAME>
|
||||
# e.g.
|
||||
# nym-node-status-agent generate-keypair --path ~/.ssh/ns-agent-key
|
||||
```
|
||||
|
||||
You will then want to export the generated `public-key` so its accessible to the `nym-node-status-api` however you are setting your environmental variables, as `NODE_STATUS_API_AGENT_KEY_LIST`:
|
||||
|
||||
```bash
|
||||
export NODE_STATUS_API_AGENT_KEY_LIST=<YOUR_KEY> # e.g. "H4z8kx5Kkf5JNQHfxaE1MwRndjDCD1C7HsVhHTFfBZ4J"
|
||||
```
|
||||
|
||||
In this situation, you are probably only using one key. However, it is possible to set multiple keys as a comma seperated list, in case you wish to whitelist multiple `-agent`s to be able to submit to a single `-api` instance.
|
||||
|
||||
#### Run the Node Status Agent
|
||||
```shell
|
||||
<PATH/TO/>nym-node-status-agent run-probe --server-address http://127.0.0.1 --server-port 8000 --ns-api-auth-key "<NS_AGENT_PRIVATE_KEY>" --probe-path <PATH/TO/> nym-gateway-probe
|
||||
```
|
||||
|
||||
You will see a lot of output like so:
|
||||
|
||||
<AccordionTemplate name="Output">
|
||||
```shell
|
||||
listen_port=48586
|
||||
public_key=3f95caf771b8a63b9bdae6c186f7aba2eae93483f1c82c0243b5e00c85b4ec26
|
||||
preshared_key=0000000000000000000000000000000000000000000000000000000000000000
|
||||
protocol_version=1
|
||||
endpoint=185.186.78.251:51822
|
||||
last_handshake_time_sec=0
|
||||
last_handshake_time_nsec=0
|
||||
tx_bytes=0
|
||||
rx_bytes=0
|
||||
persistent_keepalive_interval=0
|
||||
allowed_ip=0.0.0.0/0
|
||||
2025/03/03 18:58:28 Pinging nymtech.net seq=0
|
||||
2025/03/03 18:58:29 Ping latency: 44.503483ms
|
||||
2025/03/03 18:58:29 Pinging nymtech.net seq=1
|
||||
2025/03/03 18:58:29 Ping latency: 42.852414ms
|
||||
2025/03/03 18:58:29 Pinging nymtech.net seq=2
|
||||
2025/03/03 18:58:29 Ping latency: 43.627256ms
|
||||
2025/03/03 18:58:29 Pinging nymtech.net seq=3
|
||||
2025/03/03 18:58:29 Ping latency: 43.638839ms
|
||||
2025/03/03 18:58:29 Pinging nymtech.net seq=4
|
||||
2025/03/03 18:58:29 Ping latency: 43.345357ms
|
||||
2025/03/03 18:58:29 Pinging 1.1.1.1 seq=0
|
||||
2025/03/03 18:58:29 Ping latency: 46.327233ms
|
||||
2025/03/03 18:58:34 Pinging 1.1.1.1 seq=1
|
||||
2025/03/03 18:58:34 Ping latency: 46.273726ms
|
||||
2025/03/03 18:58:39 Pinging 1.1.1.1 seq=2
|
||||
2025/03/03 18:58:39 Ping latency: 46.542774ms
|
||||
2025/03/03 18:58:44 Pinging 1.1.1.1 seq=3
|
||||
2025/03/03 18:58:44 Ping latency: 45.663545ms
|
||||
2025/03/03 18:58:49 Pinging 1.1.1.1 seq=4
|
||||
2025/03/03 18:58:49 Ping latency: 43.803063ms
|
||||
2025/03/03 18:58:56 Downloaded file content length: 1.00 MB
|
||||
2025/03/03 18:58:56 Download duration: 1.308072386s
|
||||
2025/03/03 18:58:56 private_key=1083749e43f4f8fb008f3f7deef9107ef86a68969670ddbb9f07bf85f94fb564
|
||||
listen_port=39129
|
||||
public_key=3f95caf771b8a63b9bdae6c186f7aba2eae93483f1c82c0243b5e00c85b4ec26
|
||||
preshared_key=0000000000000000000000000000000000000000000000000000000000000000
|
||||
protocol_version=1
|
||||
endpoint=185.186.78.251:51822
|
||||
last_handshake_time_sec=0
|
||||
last_handshake_time_nsec=0
|
||||
tx_bytes=0
|
||||
rx_bytes=0
|
||||
persistent_keepalive_interval=0
|
||||
allowed_ip=::/0
|
||||
2025/03/03 18:58:56 Pinging ipv6.google.com seq=0
|
||||
2025/03/03 18:58:56 Ping latency: 42.839528ms
|
||||
2025/03/03 18:58:56 Pinging ipv6.google.com seq=1
|
||||
2025/03/03 18:58:56 Ping latency: 54.844651ms
|
||||
2025/03/03 18:58:56 Pinging ipv6.google.com seq=2
|
||||
2025/03/03 18:58:56 Ping latency: 51.23104ms
|
||||
2025/03/03 18:58:56 Pinging ipv6.google.com seq=3
|
||||
2025/03/03 18:58:56 Ping latency: 43.320409ms
|
||||
2025/03/03 18:58:56 Pinging ipv6.google.com seq=4
|
||||
2025/03/03 18:58:56 Ping latency: 63.517358ms
|
||||
2025/03/03 18:58:56 Pinging 2001:4860:4860::8888 seq=0
|
||||
2025/03/03 18:58:56 Ping latency: 54.682534ms
|
||||
2025/03/03 18:59:01 Pinging 2001:4860:4860::8888 seq=1
|
||||
2025/03/03 18:59:01 Ping latency: 55.56235ms
|
||||
2025/03/03 18:59:06 Pinging 2001:4860:4860::8888 seq=2
|
||||
2025/03/03 18:59:06 Ping latency: 55.970418ms
|
||||
2025/03/03 18:59:11 Pinging 2001:4860:4860::8888 seq=3
|
||||
2025/03/03 18:59:14 Failed to send ping: i/o timeout
|
||||
2025/03/03 18:59:19 Pinging 2001:4860:4860::8888 seq=4
|
||||
2025/03/03 18:59:22 Failed to send ping: i/o timeout
|
||||
2025/03/03 18:59:27 Pinging 2606:4700:4700::1111 seq=0
|
||||
2025/03/03 18:59:27 Ping latency: 45.072616ms
|
||||
2025/03/03 18:59:32 Pinging 2606:4700:4700::1111 seq=1
|
||||
2025/03/03 18:59:32 Ping latency: 44.357306ms
|
||||
2025/03/03 18:59:37 Pinging 2606:4700:4700::1111 seq=2
|
||||
2025/03/03 18:59:37 Ping latency: 44.013562ms
|
||||
2025/03/03 18:59:42 Pinging 2606:4700:4700::1111 seq=3
|
||||
2025/03/03 18:59:42 Ping latency: 46.94342ms
|
||||
2025/03/03 18:59:47 Pinging 2606:4700:4700::1111 seq=4
|
||||
2025/03/03 18:59:48 Ping latency: 43.372288ms
|
||||
2025/03/03 18:59:53 Pinging 2620:fe::fe seq=0
|
||||
2025/03/03 18:59:53 Ping latency: 42.164952ms
|
||||
2025/03/03 18:59:58 Pinging 2620:fe::fe seq=1
|
||||
2025/03/03 18:59:58 Ping latency: 42.295812ms
|
||||
2025/03/03 19:00:03 Pinging 2620:fe::fe seq=2
|
||||
2025/03/03 19:00:03 Ping latency: 43.117534ms
|
||||
2025/03/03 19:00:08 Pinging 2620:fe::fe seq=3
|
||||
2025/03/03 19:00:08 Ping latency: 44.26068ms
|
||||
2025/03/03 19:00:13 Pinging 2620:fe::fe seq=4
|
||||
2025/03/03 19:00:13 Ping latency: 45.29956ms
|
||||
2025/03/03 19:00:21 Downloaded file content length: 10.00 MB
|
||||
2025/03/03 19:00:21 Download duration: 3.39529252s
|
||||
```
|
||||
</AccordionTemplate>
|
||||
|
||||
Whilst you can run the `-agent` directly, it might be easier to run multiple instances in parallel with a script like so:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
set -eu
|
||||
export ENVIRONMENT=${ENVIRONMENT:-"mainnet"}
|
||||
|
||||
probe_git_ref="nym-vpn-core-v1.4.0" # check for the most recent release
|
||||
monorepo_root=<PATH/TO/NYM/>
|
||||
|
||||
set -a
|
||||
source "${monorepo_root}/envs/${ENVIRONMENT}.env"
|
||||
set +a
|
||||
|
||||
export RUST_LOG="info"
|
||||
export NODE_STATUS_AGENT_SERVER_ADDRESS="http://127.0.0.1"
|
||||
export NODE_STATUS_AGENT_SERVER_PORT="8000"
|
||||
export NODE_STATUS_AGENT_AUTH_KEY=<NS_AGENT_PRIVATE_KEY>
|
||||
export NODE_STATUS_AGENT_PROBE_EXTRA_ARGS="netstack-download-timeout-sec=30,netstack-num-ping=2,netstack-send-timeout-sec=1,netstack-recv-timeout-sec=1"
|
||||
|
||||
workers=${1:-1}
|
||||
echo "Running $workers workers in parallel"
|
||||
|
||||
function swarm() {
|
||||
local workers=$1
|
||||
|
||||
for ((i = 1; i <= workers; i++)); do
|
||||
${monorepo_root}/target/release/nym-node-status-agent run-probe --probe-path ~/<PATH/TO>/nym-vpn-client/nym-vpn-core/target/debug/nym-gateway-probe &
|
||||
done
|
||||
|
||||
wait
|
||||
|
||||
echo "All agents completed"
|
||||
}
|
||||
|
||||
swarm $workers
|
||||
```
|
||||
|
||||
And run specifying the number of workers with `./<SCRIPT_NAME>.sh <NUMBER_OF_WORKERS>`.
|
||||
|
||||
<Callout type="info">
|
||||
When running the probe, use logging level `RUST_LOG=info`. The Node Status API relies on that granularity for parsing probe results, and the logs grow incessantly if a lower level (e.g. `DEBUG`) is used.
|
||||
</Callout>
|
||||
|
||||
### Ports
|
||||
By default the API listens on `8000`, so you will need to configure this post to be reachable on your remote server. You can modify this with the `--http_port` flag.
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"mainnet":"Mainnet Endpoints"
|
||||
}
|
||||
@@ -47,6 +47,218 @@ This page displays a full list of all the changes during our release cycle from
|
||||
|
||||
<VarInfo />
|
||||
|
||||
## `v2025.4-dorina-patched`
|
||||
|
||||
Patched version of `dorina` with a few fixes and tweaks to the release. We would like to ask `nym-node` operators to upgrade to this version as quickly as possible to implement the fixes across the network and improve general quality before NymVPN launch.
|
||||
|
||||
- [Release Binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2025.4-dorina-patched)
|
||||
- [`nym-node`](nodes/nym-node.mdx) version `1.6.2`
|
||||
|
||||
```shell
|
||||
nym-node
|
||||
Binary Name: nym-node
|
||||
Build Timestamp: 2025-03-06T20:32:36.922212778Z
|
||||
Build Version: 1.6.2
|
||||
Commit SHA: 247ebb7c4339de0a298a7fcb2574122a8306c3b8
|
||||
Commit Date: 2025-03-06T21:26:16.000000000+01:00
|
||||
Commit Branch: HEAD
|
||||
rustc Version: 1.85.0
|
||||
rustc Channel: stable
|
||||
cargo Profile: release
|
||||
```
|
||||
|
||||
- Use legacy crypto for constructing SURB headers ([#5579])
|
||||
- Bugfix: make sure to correctly decode response content when putting it into error message ([#5571])
|
||||
- Tweak surb management to be more conservative ([#5570])
|
||||
- Deserialize v5 authenticator requests ([#5568])
|
||||
- Chore: additional logs when attempting to load ecash keys ([#5567])
|
||||
- Add full response body to error message upon decoding failure ([#5566])
|
||||
- Hotfix: ensure we bail on merkle leaves insertion upon missing data ([#5565])
|
||||
- Feature: v2 authentication request (#5537) ([#5563])
|
||||
- Create authenticator v5 request/response types ([#5561])
|
||||
|
||||
[#5579]: https://github.com/nymtech/nym/pull/5579
|
||||
[#5571]: https://github.com/nymtech/nym/pull/5571
|
||||
[#5570]: https://github.com/nymtech/nym/pull/5570
|
||||
[#5568]: https://github.com/nymtech/nym/pull/5568
|
||||
[#5567]: https://github.com/nymtech/nym/pull/5567
|
||||
[#5566]: https://github.com/nymtech/nym/pull/5566
|
||||
[#5565]: https://github.com/nymtech/nym/pull/5565
|
||||
[#5563]: https://github.com/nymtech/nym/pull/5563
|
||||
[#5561]: https://github.com/nymtech/nym/pull/5561
|
||||
|
||||
|
||||
## `v2025.4-dorina`
|
||||
- [Release Binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2025.4-dorina)
|
||||
- [`nym-node`](nodes/nym-node.mdx) version `1.6.0`
|
||||
```shell
|
||||
Binary Name: nym-node
|
||||
Build Timestamp: 2025-03-04T09:03:11.322601809Z
|
||||
Build Version: 1.6.0
|
||||
Commit SHA: 7060fa6dad58f17543f5086c73b1854ad1ceae60
|
||||
Commit Date: 2025-03-03T17:24:10.000000000Z
|
||||
Commit Branch: release/2025.4-dorina
|
||||
rustc Version: 1.86.0-nightly
|
||||
rustc Channel: nightly
|
||||
cargo Profile: release
|
||||
```
|
||||
|
||||
### Operators Updates & Tools
|
||||
|
||||
- New advanced [guide to virtualise a dedicated server](nodes/preliminary-steps/vps-setup/advanced), providing steps for experienced operators and aspiring sys-admins who seek for higher optimisation and better efficiency of their work orchestrating multiple nodes.
|
||||
- New Service Grant program is being implemented by operators setting up new ~150 Exit Gateways following the updated [specs for `nym-node`](nodes#minimum-requirements)
|
||||
|
||||
### Features
|
||||
- [Feature/chain status api](https://github.com/nymtech/nym/pull/5539):
|
||||
this PR introduces `/v1/network/chain-status` endpoint on nym api to give basic information about the current block as seen by this API (with some caching) and updates `/health` endpoint to give stall information.
|
||||
|
||||
- [Add SURBs soft threshold](https://github.com/nymtech/nym/pull/5535): the IPR should try to keep a buffer of available SURBs to reduce latency.
|
||||
|
||||
- [Simplify IPR v8](https://github.com/nymtech/nym/pull/5532): purge stuff from IPR v8 to simplify; use protocol field in v8.
|
||||
|
||||
- [Shared instance for DNS AsyncResolver](https://github.com/nymtech/nym/pull/5523):
|
||||
make the `HickoryDnsResolver` use a shared instance by default to limit fd use. Now the multiple `nym_http_api_client::Client`s that get built should take advantage of a shared connection pool for DNS lookups. If for some reason a client does need an independent AsyncResolver this can still be done with the added `thread_resolver` function.
|
||||
|
||||
- [cherry-pick 17d3ff2d775f61aee381d90a304ed416c08f33fc onto dorina](https://github.com/nymtech/nym/pull/5519)
|
||||
|
||||
- [cherry-pick 6e5d0dac1b75413c5f09122b0d953f8ec6ef48df onto dorina](https://github.com/nymtech/nym/pull/5518)
|
||||
|
||||
- [feat: add config option for maximum number of client connections](https://github.com/nymtech/nym/pull/5513)
|
||||
|
||||
- [IPR request types v8](https://github.com/nymtech/nym/pull/5498): Bump IPR request/response types to v8
|
||||
|
||||
- [Support static routes for HTTP requests](https://github.com/nymtech/nym/pull/5487)
|
||||
|
||||
- [added missing import to doctest](https://github.com/nymtech/nym/pull/5480)
|
||||
|
||||
- [adjusted TestSetup::new_complex to ensure bonded node's existence](https://github.com/nymtech/nym/pull/5478)
|
||||
|
||||
- [Trigger contracts CI on main workspace Cargo changes](https://github.com/nymtech/nym/pull/5477): since the contracts workspace depends on the common code in the main workspace, and since the contracts are critical to not have regressions in, trigger contracts CI on any changes to the workspace Cargo.toml and lock files.
|
||||
|
||||
- [Run cargo autoinherit](https://github.com/nymtech/nym/pull/5460): run `cargo autoinherit` to move a bunch of dependencies to the workspace level. `Cargo.lock` remains untouched.
|
||||
|
||||
- [Disable debug in wasm and wallet workflows too](https://github.com/nymtech/nym/pull/5459)
|
||||
|
||||
- [Fix clippy::precedence](https://github.com/nymtech/nym/pull/5457): fix clippy warnings for the Rust beta toolchain.
|
||||
|
||||
- [Provide Interval context with node descriptor endpoints](https://github.com/nymtech/nym/pull/5456): add current interval context information to existing enpoints using `build_skimmed_nodes_response` under the hood. This allows clients checking for a refresh to send the `epoch_uid` as a query parameter when fetching updates so the server can tell it that there have been no changes, instead of sending duplicate data over and over. The changes in this PR should be backwards compatible and never interfere with existing clients. The additions to the `NodeParams` are optional, so there is no error if a client does not send them. Old clients will not send `epoch_uid` by accident so they cannot accidentally clear their set of known nodes. In the response the status field is optional so if it is missing (e.g. if a new client talks to an old server) the connection still works. For old clients speaking to new servers, the json parsing will simply ignore extra information not included in the objects json spec.
|
||||
|
||||
<AccordionTemplate name={<TestingSteps/>}>
|
||||
|
||||
Made a request to the `/api/v1/unstable/nym-nodes/semi-skimmed` endpoint:
|
||||
- without entering an epoch_id
|
||||
- with entering the current epoch_id
|
||||
- with entering an old epoch_id
|
||||
- with entering a future epoch_id
|
||||
All results returned the same data, as expected
|
||||
|
||||
</AccordionTemplate>
|
||||
|
||||
- [Feature/add gbp currency](https://github.com/nymtech/nym/pull/5453)
|
||||
|
||||
- [Add helper to extract a list of sqlite files with journal files wal/shm](https://github.com/nymtech/nym/pull/5452)
|
||||
|
||||
- [Add a middleware layer to the nym api allowing for data compression](https://github.com/nymtech/nym/pull/5451): after Testing in a minimal example, this does work as expected. Routes still default to plain encoding, however if a client indicates support for a compressed encoding using the `Accept-Encoding` header then the served response will be compressed with the appropriate `Content-Encoding` header. ([Proof-of-Concept](https://gist.github.com/jmwample/c15a983e804fc338fee3d1b037d216b0))
|
||||
|
||||
<AccordionTemplate name={<TestingSteps/>}>
|
||||
Sent requests with and without Accept-Encoding: gzip to observe response behaviour
|
||||
Verified if responses included content-encoding: gzip when compression was requested
|
||||
Checked response file output to confirm actual compression occurred
|
||||
</AccordionTemplate>
|
||||
|
||||
- [Condense core API functionalities and enable gzip decompression for reqwest payloads](https://github.com/nymtech/nym/pull/5450): this PR is intended to take out variables from our usage of HTTP requests in the `nym-http-api-client`. To do this the PR: (1) adds the `ApiClientCore` trait with the minimal feature required to send a request, (2) turns all request sending into trait extension automatically implemented for any type that implements `ApiClientCore`. This has the benefit of consistent expected behavior for all clients using `nym-http-api-client`, including features added going forward. FOR EXAMPLE this pr adds a default header `"accept-encoding: gzip;q=1.0, *;q=0.5"` which indicates that compression is preferred whenever available. Other features to keep in mind here are things like configurable retries, domain fallbacks, etc. Note: the `Apiclient` interface could be simplified, but that would require refactoring our downstream usages of the API. For now this isn't necessary as `ApiClient` is implemented automatically so it costs nothing to have it this way. It just allows divergent usage in downstream crates.
|
||||
|
||||
<AccordionTemplate name={<TestingSteps/>}>
|
||||
Measured nym-api response times before and after updating the API client using curl
|
||||
Checked if the client automatically decompressed Gzip responses
|
||||
</AccordionTemplate>
|
||||
|
||||
- [Seedable clients](https://github.com/nymtech/nym/pull/5440): Adds `DerivationMaterial` and accompanying methods to builders. `DerivationMaterial` encapsulates parameters for deterministic key derivation using HKDF (SHA-512). Use the `derive_secret()` method to generate a 32-byte secret. To prepare for a new derivation, call the `next()` method which increments the index. **It is the caller's responsibility to track and persist the derivation index if keys need to be rederived.**
|
||||
|
||||
<AccordionTemplate name="Example">
|
||||
|
||||
```rust
|
||||
let master_key = [0u8; 32]; // your secret master key
|
||||
let salt = "unique-salt-value".to_string();
|
||||
let material = DerivationMaterial::new(master_key, 0, salt.as_bytes());
|
||||
|
||||
// Derive a secret
|
||||
let secret = material.derive_secret().expect("Failed to derive secret");
|
||||
|
||||
// Prepare for the next derivation
|
||||
let next_material = material.next();
|
||||
```
|
||||
|
||||
</AccordionTemplate>
|
||||
|
||||
- [Remove all recv_with_delay and add shutdown condition to loops in client-core](https://github.com/nymtech/nym/pull/5435): inside client-core we want to prepare the ground for moving a behaviour close to what we have in the vpn client. Remove all the recv_with_delay since we want to just stop. Add shutdown condition to all select loops to guard against the shutdown listener being polled inside the select blocks. Remove unwraps when sending on unbounded channels in case the receiver exits before the sender. Move `TaskClient` to be a member field so make it easier to wrap log errors in a shutdown check. Update all fork names to use underscore consistently, since the task separator is hyphen
|
||||
|
||||
<AccordionTemplate name={<TestingSteps/>}>
|
||||
Validated that all binaries including `nym-node`, `nym-client`, `nym-network-requester`, and `nym-socks5-client` are behaving well without indicating the presence of any unexpected errors or crashes
|
||||
</AccordionTemplate>
|
||||
|
||||
- [Disable the test for checking the remaining bandwidth in nym-node-status-api](https://github.com/nymtech/nym/pull/5425): this check fails almost every time on CI, possibly due to rate limiting? It's not good to disable the check, but it's blocking CI as it stands now. Given that we have the check above for locating the ip, we at least have a little coverage.
|
||||
|
||||
- [Dz nym node stats](https://github.com/nymtech/nym/pull/5418): removed obsolete fields from stats (blacklisted mixnodes, blacklisted gateways, bonded mixnodes, bonded gateways). Introduced nym-node scraping, beside just mixnodes. `/mixnodes/stats` now returns data for nym-nodes as well, which results in much more accurate "packets mixed" stats.
|
||||
|
||||
- [Nymnode entrypoint docker](https://github.com/nymtech/nym/pull/5300)
|
||||
|
||||
### Bugfix
|
||||
- [bugfix: dont query for ecash apis unless necessary when spending ticketbooks](https://github.com/nymtech/nym/pull/5508)
|
||||
|
||||
- [bugfix: bound check when recovering a reply SURB](https://github.com/nymtech/nym/pull/5502)
|
||||
|
||||
- [fix: update fx average rate calcs to ignore 0 values](https://github.com/nymtech/nym/pull/5454)
|
||||
|
||||
### Chore
|
||||
- [chore: workspace global panic preventing lints](https://github.com/nymtech/nym/pull/5512)
|
||||
|
||||
- [chore: removed all old coconut code](https://github.com/nymtech/nym/pull/5500)
|
||||
|
||||
- [build(deps): bump the patch-updates group across 1 directory with 3 updates](https://github.com/nymtech/nym/pull/5482): updates `clap` from 4.5.28 to 4.5.30, updates `clap` from 4.5.28 to 4.5.30, updates `prost` from 0.13.4 to 0.13.5.
|
||||
|
||||
- [build(deps): bump http from 1.1.0 to 1.2.0](https://github.com/nymtech/nym/pull/5472)
|
||||
|
||||
- [build(deps): bump utoipa-swagger-ui from 8.0.3 to 8.1.0](https://github.com/nymtech/nym/pull/5471)
|
||||
|
||||
- [build(deps): bump colored from 2.1.0 to 2.2.0](https://github.com/nymtech/nym/pull/5470)
|
||||
|
||||
- [build(deps): bump celes from 2.4.0 to 2.5.0](https://github.com/nymtech/nym/pull/5469)
|
||||
|
||||
- [build(deps): bump the patch-updates group with 2 updates](https://github.com/nymtech/nym/pull/5467): updates `clap` from 4.5.28 to 4.5.29, updates `prost` from 0.13.4 to 0.13.5.
|
||||
|
||||
- [build(deps): bump elliptic from 6.5.4 to 6.6.1 in /docker/typescript_client/upload_contract](https://github.com/nymtech/nym/pull/5463): bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.4 to 6.6.1.
|
||||
|
||||
- [build(deps): bump uniffi_build from 0.25.3 to 0.29.0](https://github.com/nymtech/nym/pull/5448)
|
||||
|
||||
- [Upgrade tower to 0.5.2](https://github.com/nymtech/nym/pull/5446)
|
||||
|
||||
- [build(deps): bump hickory-proto from 0.24.2 to 0.24.3 in /nym-wallet](https://github.com/nymtech/nym/pull/5445)
|
||||
|
||||
- [build(deps): bump hickory-proto from 0.24.2 to 0.24.3](https://github.com/nymtech/nym/pull/5444)
|
||||
|
||||
- [build(deps): bump the patch-updates group across 1 directory with 10 updates](https://github.com/nymtech/nym/pull/5439):
|
||||
Bumps the patch-updates group with 10 updates in the directory:
|
||||
|
||||
| Package | From | To |
|
||||
| --- | --- | --- |
|
||||
| [async-trait](https://github.com/dtolnay/async-trait) | `0.1.85` | `0.1.86` |
|
||||
| [clap](https://github.com/clap-rs/clap) | `4.5.27` | `4.5.28` |
|
||||
| [comfy-table](https://github.com/nukesor/comfy-table) | `7.1.3` | `7.1.4` |
|
||||
| [hickory-resolver](https://github.com/hickory-dns/hickory-dns) | `0.24.2` | `0.24.3` |
|
||||
| [once_cell](https://github.com/matklad/once_cell) | `1.20.2` | `1.20.3` |
|
||||
| [pin-project](https://github.com/taiki-e/pin-project) | `1.1.8` | `1.1.9` |
|
||||
| [serde_json_path](https://github.com/hiltontj/serde_json_path) | `0.7.1` | `0.7.2` |
|
||||
| [toml](https://github.com/toml-rs/toml) | `0.8.19` | `0.8.20` |
|
||||
| [cosmrs](https://github.com/cosmos/cosmos-rust) | `0.21.0` | `0.21.1` |
|
||||
| [tokio-postgres](https://github.com/sfackler/rust-postgres) | `0.7.12` | `0.7.13` |
|
||||
|
||||
- [build(deps): bump openssl from 0.10.56 to 0.10.70 in /nym-wallet](https://github.com/nymtech/nym/pull/5422)
|
||||
|
||||
- [build(deps): bump hyper from 1.4.1 to 1.6.0](https://github.com/nymtech/nym/pull/5416)
|
||||
|
||||
- [build(deps): bump publicsuffix from 2.2.3 to 2.3.0](https://github.com/nymtech/nym/pull/5367)
|
||||
|
||||
## `v2025.3-ruta`
|
||||
|
||||
- [Release Binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2025.3-ruta)
|
||||
@@ -97,7 +309,7 @@ As we announced in [`hu` release notes](#service-grant-program-v2), we are opene
|
||||
|
||||
##### Locations
|
||||
|
||||
These locations and slots are approximate and constantly change based on new operators submissions. Please keep in mind that we are going to chose people with respect to empty slots in the given location at the time of their submission. In case of competing submissions in the same location we will take the one with better specs vs price ratio.
|
||||
These locations and slots are approximate and constantly change based on new operators submissions. Please keep in mind that we are going to chose people with respect to empty slots in the given location at the time of their submission. In case of competing submissions in the same location we will take the one with better specs vs price ratio.
|
||||
|
||||
| LOCATION | SLOTS |
|
||||
| :-- | --: |
|
||||
@@ -151,7 +363,7 @@ These locations and slots are approximate and constantly change based on new ope
|
||||
|
||||
- [Send shutdown instead of panic when reaching max fail](https://github.com/nymtech/nym/pull/5398): Remove a panic and an unwrap inside `client-core` that is hit occasionally in the vpn client. This is a change that can have wide ranging impact since it changes the task handling and it's inside `client-core`, which is used in many components and services, so preventing regressions is important.
|
||||
|
||||
- [Relocate a validator api function](https://github.com/nymtech/nym/pull/5401): Adds a function to the `nym-validator-client` crate that hits the network details endpoint and returns an object parsed from `json`. This was floating loose in the `nym-vpn-client` repo.
|
||||
- [Relocate a validator api function](https://github.com/nymtech/nym/pull/5401): Adds a function to the `nym-validator-client` crate that hits the network details endpoint and returns an object parsed from `json`. This was floating loose in the `nym-vpn-client` repo.
|
||||
|
||||
- [Bump the patch-updates group across 1 directory with 9 updates](https://github.com/nymtech/nym/pull/5406)
|
||||
|
||||
@@ -199,7 +411,7 @@ From `nym-node v1.3.0` operators can technically choose multiple functionalities
|
||||
|
||||
- Updated maintenance guides to [backup](nodes/maintenance#backup-a-node), [restore](nodes/maintenance#restoring-a-node) and [move](nodes/maintenance#moving-a-node) a node, containing a new and important commands to backup and restore `clients.sqlite` database.
|
||||
|
||||
- [New explanation of `nym-node` functionalities](nodes/nym-node/setup#functionality-mode) describing how Gateways get selected in Mixnet mode and Wireguard mode.
|
||||
- [New explanation of `nym-node` functionalities](nodes/nym-node/setup#functionality-mode) describing how Gateways get selected in Mixnet mode and Wireguard mode.
|
||||
|
||||
<Callout type="warning">
|
||||
Wireguard nodes route data directly to the open internet. Therefore it exposes IP of operators server (VPS) to abuse complains. Before you decide to run a node with active wireguard routing, please read our [Community Counsel pages](community-counsel/exit-gateway) containing more information and some legal content.
|
||||
@@ -223,7 +435,7 @@ We have been notified that a handful of nodes have been taken down by abuse repo
|
||||
- Join [Community legal counsel](https://nym.com/docs/operators/community-counsel) - our collective knowledge hub. Add your findings by opening a [Pull Request](https://nym.com/docs/operators/add-content)
|
||||
|
||||
- While we are working on a new list of more friendly providers, consider to move away from these provides as soon as possible:
|
||||
|
||||
|
||||
- Servinga / VPS2day (AS39378)
|
||||
- Frantech / Ponynet / BuyVM (AS53667)
|
||||
- OVH SAS / OVHcloud (AS16276)
|
||||
@@ -233,7 +445,7 @@ We have been notified that a handful of nodes have been taken down by abuse repo
|
||||
- Psychz Networks (AS40676)
|
||||
- 1337 Services GmbH / RDP.sh (AS210558)
|
||||
|
||||
- Backup your nodes to have access to `.nym` directory locally. Follow [node](nodes/maintenance#backup-a-node) and [proxy configuration](nodes/maintenance#backup-proxy-configuration) backup guides to be able to [restore your node](nodes/maintenance#restoring-a-node) later on on another machine, without losing your delegation.
|
||||
- Backup your nodes to have access to `.nym` directory locally. Follow [node](nodes/maintenance#backup-a-node) and [proxy configuration](nodes/maintenance#backup-proxy-configuration) backup guides to be able to [restore your node](nodes/maintenance#restoring-a-node) later on on another machine, without losing your delegation.
|
||||
|
||||
- We would like to ask operators who use reverse proxy and a domain (required for Gateways) to start using a common convention starting with `nym-exit` for their nodes URL. The entire address should have this new format:
|
||||
```
|
||||
@@ -258,7 +470,7 @@ nym-exit.mysquad.org
|
||||
|
||||
**The `NYM-EXIT` part in the beginning is what's important.**
|
||||
|
||||
- When registering a domain, check [Top Level Domain (TLD)](https://www.techopedia.com/definition/1348/top-level-domain-tld) terms and conditions. For example `.icu` is a no go. Having a wrong TLD may lead to your domain being taken away from you when facing a DMCA report.
|
||||
- When registering a domain, check [Top Level Domain (TLD)](https://www.techopedia.com/definition/1348/top-level-domain-tld) terms and conditions. For example `.icu` is a no go. Having a wrong TLD may lead to your domain being taken away from you when facing a DMCA report.
|
||||
|
||||
- Write a message to your provider and introduce your intention to run a Nym Node on their service
|
||||
<AccordionTemplate name="Email template: Introduce yourself to your VPS provider">
|
||||
@@ -322,7 +534,7 @@ Undelegated due to high saturation:
|
||||
|
||||
#### Service Grant Program v2
|
||||
|
||||
Aside from delegating on top of nodes, Nym runs a Service Grant Program (SGP) to support Exit Gateway operators before they will be rewarded by collecting [zk-nym tickets](../network/cryptography/zk-nym) from users subscription. Operators included in SGP are long term active community members with the highest requirements on the technical setup and upgrading pace. We are about to start a second iteration of SGP very soon (SGPv2). The final slots and locations are yet to be concluded. Priority to participate in SGPv2 will be given to the current operators in SGP. Based on the number of slots, we will then determine how many more operators can sign up.
|
||||
Aside from delegating on top of nodes, Nym runs a Service Grant Program (SGP) to support Exit Gateway operators before they will be rewarded by collecting [zk-nym tickets](../network/cryptography/zk-nym) from users subscription. Operators included in SGP are long term active community members with the highest requirements on the technical setup and upgrading pace. We are about to start a second iteration of SGP very soon (SGPv2). The final slots and locations are yet to be concluded. Priority to participate in SGPv2 will be given to the current operators in SGP. Based on the number of slots, we will then determine how many more operators can sign up.
|
||||
|
||||
##### Rules of SGPv2
|
||||
|
||||
@@ -330,7 +542,7 @@ Aside from delegating on top of nodes, Nym runs a Service Grant Program (SGP) to
|
||||
**We will share more info soon in the channels. The rules are not set in stone and could potentially be altered or updated in the future! Do *not* purchase new servers neither migrate your nodes just yet.**
|
||||
</Callout>
|
||||
|
||||
As we finalising last details of *"Project Smoosh"*, where one binary - `nym-node` - can run as an `entry-gateway`, `mixnode` or `exit-gateway` in Mixnet mode as well as `entry-gateway` or `exit-gateway` in Wireguard mode, we plan to step up the game. SGPv2 grants will be higher if operators can meet new requirements.
|
||||
As we finalising last details of *"Project Smoosh"*, where one binary - `nym-node` - can run as an `entry-gateway`, `mixnode` or `exit-gateway` in Mixnet mode as well as `entry-gateway` or `exit-gateway` in Wireguard mode, we plan to step up the game. SGPv2 grants will be higher if operators can meet new requirements.
|
||||
|
||||
**Minimum Specs & Requirements**
|
||||
|
||||
@@ -402,7 +614,7 @@ These are minimum requirements to become a part of SGPv2. We aim to have nodes o
|
||||
|
||||
- [build(deps): bump criterion from `0.4.0` to `0.5.1`](https://github.com/nymtech/nym/pull/4911): Bumps [criterion](https://github.com/bheisler/criterion.rs) from `0.4.0` to `0.5.1`.
|
||||
|
||||
- [NS API: add mixnet scraper](https://github.com/nymtech/nym/pull/5200)
|
||||
- [NS API: add mixnet scraper](https://github.com/nymtech/nym/pull/5200)
|
||||
|
||||
- [build(deps): bump http from `1.1.0` to `1.2.0`](https://github.com/nymtech/nym/pull/5228): Bumps [http](https://github.com/hyperium/http) from `1.1.0` to `1.2.0`.
|
||||
|
||||
@@ -410,41 +622,41 @@ These are minimum requirements to become a part of SGPv2. We aim to have nodes o
|
||||
|
||||
- [Add windows to CI builds](https://github.com/nymtech/nym/pull/5269)
|
||||
|
||||
- [nym topology revamp](https://github.com/nymtech/nym/pull/5271): This PR changes the internals of the `NymTopology` to blur the lines between explicit mixnodes and gateways so that what used to be considered a "mixnode" could be a valid egress point of the network. `NymTopology` is no longer divided into `BTreeMap<MixLayer, Vec<mix::Node>>` and `Vec<gateway::Node>`. instead there's information about the current rewarded set (to support future VRF work) and a simple map of `HashMap<NodeId, RoutingNode>`. The new features are mostly controlled via 2 new flags/config values:
|
||||
- [nym topology revamp](https://github.com/nymtech/nym/pull/5271): This PR changes the internals of the `NymTopology` to blur the lines between explicit mixnodes and gateways so that what used to be considered a "mixnode" could be a valid egress point of the network. `NymTopology` is no longer divided into `BTreeMap<MixLayer, Vec<mix::Node>>` and `Vec<gateway::Node>`. instead there's information about the current rewarded set (to support future VRF work) and a simple map of `HashMap<NodeId, RoutingNode>`. The new features are mostly controlled via 2 new flags/config values:
|
||||
- `use_extended_topology` that tells the client to retrieve **all** network nodes rather than the ones that got assigned "active" mixnode role (or support being a gateway)
|
||||
- `ignore_egress_epoch_role` that tells the client it's fine to construct egress packets to nodes that are **not** assigned entry or exit gateway role
|
||||
|
||||
|
||||
- [Nyx Chain Watcher](https://github.com/nymtech/nym/pull/5274)
|
||||
|
||||
- [Include `IPINFO_API_TOKEN` in nightly CI](https://github.com/nymtech/nym/pull/5285)
|
||||
|
||||
- [Move tun constants to network defaults](https://github.com/nymtech/nym/pull/5286):
|
||||
- [Move tun constants to network defaults](https://github.com/nymtech/nym/pull/5286):
|
||||
<AccordionTemplate name={<TestingSteps/>}>
|
||||
1. **Regression Testing**:
|
||||
- Verified no issues arose when running tests for the affected files.
|
||||
- Tested TUN behaviour with new nym-nodes in the hu branch.
|
||||
|
||||
**Results**:
|
||||
- Verified no issues arose when running tests for the affected files.
|
||||
- Tested TUN behaviour with new nym-nodes in the hu branch.
|
||||
|
||||
- **No bugs detected**.
|
||||
- Tunnels are functioning as expected, with traffic routing and IP generation working seamlessly.
|
||||
</AccordionTemplate>
|
||||
**Results**:
|
||||
|
||||
- **No bugs detected**.
|
||||
- Tunnels are functioning as expected, with traffic routing and IP generation working seamlessly.
|
||||
</AccordionTemplate>
|
||||
|
||||
- [Add dependabot assignes for the root cargo ecosystem](https://github.com/nymtech/nym/pull/5297)
|
||||
|
||||
- [build(deps): bump the patch-updates group across 1 directory with 35 updates](https://github.com/nymtech/nym/pull/5310): Bumps the `patch-updates` group with 33 updates
|
||||
|
||||
- [Periodically remove stale gateway messages](https://github.com/nymtech/nym/pull/5312): This PR introduces a simple task that removes gateway messages that haven't been retrieved in (by default) 24h.
|
||||
- [Periodically remove stale gateway messages](https://github.com/nymtech/nym/pull/5312): This PR introduces a simple task that removes gateway messages that haven't been retrieved in (by default) 24h.
|
||||
<AccordionTemplate name={<TestingSteps/>}>
|
||||
**Automation Script for Data Cleanup Validation**
|
||||
|
||||
Test Objective: Validate that the stale message cleanup mechanism in the database correctly removes records older than the configured threshold (24 hours).
|
||||
|
||||
|
||||
Test Setup:
|
||||
1. Environment:
|
||||
* SQLite database
|
||||
* Bash script: used to insert data.
|
||||
|
||||
|
||||
Steps Performed:
|
||||
1. Ran the insert_data.sh script to populate the database with test data:
|
||||
* Recent records inserted successfully.
|
||||
@@ -452,8 +664,8 @@ Steps Performed:
|
||||
3. Confirmed that all 20 records (10 recent + 10 stale) were present.
|
||||
4. Allowed the system to run for 24 hours to trigger the cleanup mechanism.
|
||||
5. Queried the database again after 24 hours: sqlite3 gateway_storage.db "SELECT * FROM message_store;"
|
||||
6.
|
||||
|
||||
6.
|
||||
|
||||
Expected Result:
|
||||
* All stale records (older than 24 hours) should be removed.
|
||||
* Recent records should remain in the database.
|
||||
@@ -464,18 +676,18 @@ Actual Result:
|
||||
|
||||
- [Use expect in geodata test to give error message on failure](https://github.com/nymtech/nym/pull/5314): Keep hitting this error on CI, from what I think is network hickup. But it's hard to tell form the log since the error is swallowed. Explicitly unwrap the result so we get a more detailed error output.
|
||||
<AccordionTemplate name={<TestingSteps/>}>
|
||||
**Quick Code Review**
|
||||
|
||||
**Summary**
|
||||
**Quick Code Review**
|
||||
|
||||
**Summary**
|
||||
1. **CI Workflow**: Adjusted paths to optimise build triggers, avoiding unnecessary CI runs while ensuring coverage for key directories
|
||||
2. **Geolocation Test**: Improved error handling by replacing assertions with `.expect` for clearer debugging in API regression tests
|
||||
|
||||
**Conclusion**
|
||||
|
||||
**Conclusion**
|
||||
|
||||
Regression testing confirms everything works as intended. **Approved**.
|
||||
</AccordionTemplate>
|
||||
|
||||
- [`CancellationToken`-based shutdowns](https://github.com/nymtech/nym/pull/5325): This PR introduces scaffolding for using `CancellationToken` and `TaskTracker` for our graceful shutdowns rather than the existing `TaskClient` and `TaskManager`.
|
||||
- [`CancellationToken`-based shutdowns](https://github.com/nymtech/nym/pull/5325): This PR introduces scaffolding for using `CancellationToken` and `TaskTracker` for our graceful shutdowns rather than the existing `TaskClient` and `TaskManager`.
|
||||
|
||||
- [Introduce `/load` endpoint for self-reported quantised Nym Node load](https://github.com/nymtech/nym/pull/5326): This PR introduces a new `/load` endpoint on a `NymNode` to return its current load. It returns the following data:
|
||||
```rust
|
||||
@@ -505,7 +717,7 @@ The actual values for`NodeLoad` are determined as follows:
|
||||
- Thus we calculate two additional auxiliary `Load` values, for memory usage and swap usage, i.e.: `used_memory / total_memory` and `used_swap / total_swap` respectively.
|
||||
- Then we check whether either of the `MemoryLoad` or `SwapLoad` is bigger than the current base `Load` of the machine we have determined, if so, it's increased by one tier / bucket. For example, say the current machine load is `Load::Low`, but the memory usage is at 90% (`Load::VeryHigh`). that would result in the reported `Load` being bumped up to `Load::Medium` instead. The same logic applies with swap load, **however, only if the total swap > 1GB**. this is to prevent weird edge cases where the machine has hardly any swap.
|
||||
- Finally, the `.total` `Load` uses the same "tier bumping" behaviour using the `.total` and `.network` loads, i.e. `if network > machine`, then `total = machine + 1`. for example if `machine` `Load` is `Load::Low`, but `network` `Load` is `Load::Medium`, then the `total` `Load` is set to `Load::Medium` instead.
|
||||
</AccordionTemplate>
|
||||
</AccordionTemplate>
|
||||
|
||||
- [Bump the `patch-updates` group with 8 updates](https://github.com/nymtech/nym/pull/5336)
|
||||
|
||||
@@ -517,29 +729,29 @@ The actual values for`NodeLoad` are determined as follows:
|
||||
|
||||
- [Bump mikefarah/yq from `4.44.6` to `4.45.1`](https://github.com/nymtech/nym/pull/5342)
|
||||
|
||||
- [Update `indexed_db_futures`](https://github.com/nymtech/nym/pull/5347): Updates the `indexed_db_futures` dependency to stop relying on the fork.
|
||||
- [Update `indexed_db_futures`](https://github.com/nymtech/nym/pull/5347): Updates the `indexed_db_futures` dependency to stop relying on the fork.
|
||||
|
||||
- [Refresh wasm sdk](https://github.com/nymtech/nym/pull/5353): This PR refreshes the wasm clients to make them usable in the current network
|
||||
|
||||
- [Client gateway selection](https://github.com/nymtech/nym/pull/5358): Changed how gateway is selected.
|
||||
- For init the target gateway can't support mixing
|
||||
- For egress, unless `ignore_epoch_roles` is specified, the gateway can't be currently assigned to a mixing layer. But it can be standby or inactive
|
||||
|
||||
|
||||
- [Exposed `NymApiClient` method for obtaining node performance history](https://github.com/nymtech/nym/pull/5360)
|
||||
|
||||
- [Bind to `[::]` on `nym-node` for both IP versions](https://github.com/nymtech/nym/pull/5361)
|
||||
<AccordionTemplate name={<TestingSteps/>}>
|
||||
**IPv4 Configuration and Migration Testing**
|
||||
|
||||
|
||||
Testing Steps:
|
||||
- Initiated and ran a nym-node with version 1.3.1 on an IPv4-only machine, then updated it to the new 'hu', 1.4.0 version.
|
||||
- Initiated and ran a new nym-node with 'hu', 1.4.0 always on a machine with only IPv4.
|
||||
- Initiated and ran a new nym-node with 'hu', 1.4.0 on a machine with both IPv4 and IPv6 for regression testing.
|
||||
|
||||
|
||||
Results:
|
||||
- No functional issues during version updates.
|
||||
- Logged message on all versions: "no registered client for destination: ff02::2"
|
||||
|
||||
|
||||
Status: Pass
|
||||
</ AccordionTemplate>
|
||||
|
||||
@@ -554,10 +766,10 @@ Status: Pass
|
||||
- Ensured a client is able to select a node which aside from a gateway, can also act as a mixnode
|
||||
- Verified 'ignore_epoch_roles' is the default mode
|
||||
- _note; this is most likely not going to be a permanent solution_
|
||||
|
||||
|
||||
**Results:**
|
||||
- Clients and topology are behaving correctly
|
||||
|
||||
- Clients and topology are behaving correctly
|
||||
|
||||
Status: Pass
|
||||
</ AccordionTemplate>
|
||||
|
||||
@@ -612,7 +824,7 @@ We are developing a design where operators can enable multiple modes, and let th
|
||||
- Moved top level `authenticator` section to `service_providers` so that it'd live alongside NR and IPR
|
||||
- Added general `debug` section
|
||||
- Added `metrics` section
|
||||
- All documentation migrated to a new URL [nym.com/docs](https://nym.com/docs) alongside the rebranding of Nym organisation.
|
||||
- All documentation migrated to a new URL [nym.com/docs](https://nym.com/docs) alongside the rebranding of Nym organisation.
|
||||
- Updated [network architecture diagrams](https://nym.com/docs/network/architecture)
|
||||
- New blow-by-blow mixnet [traffic flow](https://nym.com/docs/network/architecture) section
|
||||
- [Winter Nym Squad League started](https://forum.nym.com/t/nym-squad-league-farewell-fall-welcome-winter/977)
|
||||
@@ -648,11 +860,11 @@ We are developing a design where operators can enable multiple modes, and let th
|
||||
- Everything in `mixnode` directory has been removed because there was nothing really left there. The mixing socket listener was unified in `nym-node` and similarly `verloc` was also moved there
|
||||
- `gateway` directory was similarly reduced in size. Now it also creates appropriate tasks as opposed to the whole gateway process. eventually it might also be further stripped, but today is not that day.
|
||||
- Removed the generic parameter on the `GatewayStorage` to simplify all the generics down the stack. it wasn't used anyway
|
||||
|
||||
|
||||
CLI:
|
||||
- Added `--modes` argument to specify all node modes with a single command (or `env` variable). for example: `--modes="mixnode,entry"`. Can't be used alongside `--modes`
|
||||
- Extended `--mode` argument to allow specifying it multiple times, for example: `--mode mixnode --mode entry`. can't be used alongside `--mode`
|
||||
|
||||
|
||||
Config changes:
|
||||
- Replaced `mode` with `modes` to allow setting the node to run with say, `entry` + `mixnode` roles simultaneously
|
||||
- Added `maximum_forward_packet_delay` to `mixnet.debug` section
|
||||
@@ -677,17 +889,17 @@ Config changes:
|
||||
- [Remove unneeded async function annotation](https://github.com/nymtech/nym/pull/5246)
|
||||
- [Add control messages to `GatewayTransciver`](https://github.com/nymtech/nym/pull/5247)
|
||||
<AccordionTemplate name={<TestingSteps/>}>
|
||||
**Review and Testing: Forget Me Implementation**
|
||||
**Review and Testing: Forget Me Implementation**
|
||||
|
||||
- Validated the encryption and delivery of `ForgetMe` control messages to the gateway
|
||||
|
||||
**Testing: MixTrafficController Integration**
|
||||
- Validated the encryption and delivery of `ForgetMe` control messages to the gateway
|
||||
|
||||
- Verified that the `MixTrafficController` invokes `ForgetMe` logic correctly during shutdown
|
||||
- Tested behaviour for gateway transceiver failures while sending control messages
|
||||
|
||||
**Testing: Gateway Storage Updates**
|
||||
- Confirmed successful deletion of client data (e.g., inbox messages, bandwidth allocations) from persistent storage
|
||||
**Testing: MixTrafficController Integration**
|
||||
|
||||
- Verified that the `MixTrafficController` invokes `ForgetMe` logic correctly during shutdown
|
||||
- Tested behaviour for gateway transceiver failures while sending control messages
|
||||
|
||||
**Testing: Gateway Storage Updates**
|
||||
- Confirmed successful deletion of client data (e.g., inbox messages, bandwidth allocations) from persistent storage
|
||||
</AccordionTemplate>
|
||||
|
||||
- [Add conversion unit tests for auth msg](https://github.com/nymtech/nym/pull/5251)
|
||||
@@ -704,13 +916,13 @@ Config changes:
|
||||
<AccordionTemplate name={<TestingSteps/>}>
|
||||
1. **Review File: `common/credential-storage/src/backends/sqlite.rs`**
|
||||
- Verified addition of `close` method for the SQLite backend
|
||||
|
||||
|
||||
2. **Review File: `common/credential-storage/src/ephemeral_storage.rs`**
|
||||
- Confirmed addition of `close` method for ephemeral storage with no action required
|
||||
|
||||
|
||||
3. **Review File: `common/credential-storage/src/persistent_storage/mod.rs`**
|
||||
- Ensured `close` method integration for persistent storage
|
||||
|
||||
|
||||
4. **Review File: `common/credential-storage/src/storage.rs`**
|
||||
- Verified updates to the `Storage` trait to include `close` and `cleanup_expired` methods
|
||||
</AccordionTemplate>
|
||||
@@ -719,15 +931,15 @@ Config changes:
|
||||
<AccordionTemplate name={<TestingSteps/>}>
|
||||
1. **Review File: `common/network-defaults/src/constants.rs`**
|
||||
- Confirmed updated `mixnet_vpn` constants were added.
|
||||
|
||||
|
||||
2. **Review File: `service-providers/ip-packet-router/src/constants.rs`**
|
||||
- Checked replacement of legacy `TUN_*` constants with new `mixnet_vpn` constants.
|
||||
- Validated alignment of routing traffic configurations.
|
||||
|
||||
|
||||
3. **Review File: `service-providers/ip-packet-router/src/ip_packet_router.rs`**
|
||||
- Ensured new `nym_network_defaults::constants::mixnet_vpn` constants replaced old references.
|
||||
- Verified `TunDeviceConfig` consistency.
|
||||
|
||||
|
||||
4. **Review File: `service-providers/ip-packet-router/src/util/generate_new_ip.rs`**
|
||||
- Confirmed substitution of `TUN_DEVICE_*` constants with `NYM_TUN_DEVICE_*` constants.
|
||||
- Tested functionality for generating random IPs within subnet.
|
||||
@@ -743,54 +955,54 @@ Config changes:
|
||||
- `nym_node_mixnet_ingress_excessive_delay_packets`
|
||||
- `nym_node_mixnet_ingress_forward_hop_packets_dropped`
|
||||
- `nym_node_mixnet_ingress_final_hop_packets_dropped`
|
||||
|
||||
|
||||
- `nym_node_mixnet_ingress_forward_hop_packets_received_rate`
|
||||
- `nym_node_mixnet_ingress_final_hop_packets_received_rate`
|
||||
- `nym_node_mixnet_ingress_malformed_packets_received_rate`
|
||||
- `nym_node_mixnet_ingress_excessive_delay_packets_rate`
|
||||
- `nym_node_mixnet_ingress_final_hop_packets_received_rate`
|
||||
- `nym_node_mixnet_ingress_malformed_packets_received_rate`
|
||||
- `nym_node_mixnet_ingress_excessive_delay_packets_rate`
|
||||
- `nym_node_mixnet_ingress_forward_hop_packets_dropped_rate`
|
||||
- `nym_node_mixnet_ingress_final_hop_packets_dropped_rate`
|
||||
|
||||
|
||||
- egress:
|
||||
- `nym_node_mixnet_egress_stored_on_disk_final_hop_packets`
|
||||
- `nym_node_mixnet_egress_forward_hop_packets_sent`
|
||||
- `nym_node_mixnet_egress_ack_packets_sent`
|
||||
- `nym_node_mixnet_egress_forward_hop_packets_dropped`
|
||||
|
||||
|
||||
- `nym_node_mixnet_egress_forward_hop_packets_sent_rate`
|
||||
- `nym_node_mixnet_egress_ack_packets_sent_rate`
|
||||
- `nym_node_mixnet_egress_forward_hop_packets_dropped_rate`
|
||||
|
||||
|
||||
- client sessions
|
||||
- `nym_node_entry_client_sessions_unique_users`
|
||||
- `nym_node_entry_client_sessions_sessions_started`
|
||||
- `nym_node_entry_client_sessions_finished_sessions`
|
||||
- `nym_node_entry_client_sessions_durations_{TYP}` (histogram), for example `nym_node_entry_client_sessions_durations_vpn`
|
||||
|
||||
|
||||
- wireguard:
|
||||
- `nym_node_wireguard_bytes_rx`
|
||||
- `nym_node_wireguard_bytes_tx`
|
||||
- `nym_node_wireguard_bytes_total_peers`
|
||||
- `nym_node_wireguard_bytes_active_peers`
|
||||
|
||||
|
||||
- `nym_node_wireguard_bytes_rx_rate`
|
||||
- `nym_node_wireguard_bytes_tx_rate`
|
||||
|
||||
|
||||
|
||||
|
||||
- network
|
||||
- `nym_node_network_active_ingress_mixnet_connections`
|
||||
- `nym_node_network_active_ingress_web_socket_connections`
|
||||
- `nym_node_network_active_egress_mixnet_connections`
|
||||
|
||||
|
||||
- process
|
||||
- `nym_node_process_forward_hop_packets_being_delayed`
|
||||
- `nym_node_process_packet_forwarder_queue_size`
|
||||
- `nym_node_process_topology_query_resolution_latency` (histogram)
|
||||
- `nym_node_process_final_hop_packets_pending_delivery`
|
||||
- `nym_node_process_forward_hop_packets_pending_delivery`
|
||||
- `nym_node_process_forward_hop_packets_pending_delivery`
|
||||
</AccordionTemplate>
|
||||
|
||||
- [Amend 250gb limit](https://github.com/nymtech/nym/pull/5313): Change bandwidth cap to 250gb
|
||||
- [Amend 250gb limit](https://github.com/nymtech/nym/pull/5313): Change bandwidth cap to 250gb
|
||||
- [Warn users if node is run in exit mode only](https://github.com/nymtech/nym/pull/5320): Throws a warning if node is run in "exit" mode only as by default, this will **NOT** enable entry capabilities, i.e. opening the websocket. thus making it ineligible for rewarded set selection (and rewards)
|
||||
- [Reduce log severity for number of packets being delayed](https://github.com/nymtech/nym/pull/5321)
|
||||
- [Apply 1.84 linter suggestions](https://github.com/nymtech/nym/pull/5330)
|
||||
@@ -806,7 +1018,7 @@ Config changes:
|
||||
- [Make sure to apply gateway score filtering when choosing initial node](https://github.com/nymtech/nym/pull/5256)
|
||||
- [Fixed client session histogram buckets](https://github.com/nymtech/nym/pull/5316)
|
||||
- [Contract version assignment](https://github.com/nymtech/nym/pull/5318): This PR fixes updates to current nym-node version as well as introduces migration to fix the existing state of the mainnet contract
|
||||
- [Make sure refresh data key matches bond info](https://github.com/nymtech/nym/pull/5329): This is to forbid operators from reusing the same underlying identity key for multiple nodes by overwriting the describe data. the reported key has to always match what the node has bonded with (and the contract enforces uniqueness)
|
||||
- [Make sure refresh data key matches bond info](https://github.com/nymtech/nym/pull/5329): This is to forbid operators from reusing the same underlying identity key for multiple nodes by overwriting the describe data. the reported key has to always match what the node has bonded with (and the contract enforces uniqueness)
|
||||
|
||||
## Archived Changelog
|
||||
|
||||
|
||||
@@ -128,10 +128,15 @@ mkdir -pv <PATH_TO_TARGET_DIRECTORY>
|
||||
|
||||
###### 2. Backup `clients.sqlite` database
|
||||
|
||||
|
||||
- Install `sqlite3`
|
||||
```sh
|
||||
apt install sqlite3
|
||||
```
|
||||
- **Stop your node**
|
||||
```sh
|
||||
service nym-node stop
|
||||
```
|
||||
- Open sqlite CLI shell inside `clients.sqlite` database
|
||||
```sh
|
||||
sqlite3 ~/.nym/nym-nodes/default-nym-node/data/clients.sqlite
|
||||
@@ -151,10 +156,14 @@ sqlite3 ~/.nym/nym-nodes/default-nym-node/data/clients.sqlite
|
||||
scp -r <SOURCE_USER_NAME>@<SOURCE_HOST_ADDRESS>:~/.nym/nym-nodes/<ID> <PATH_TO_TARGET_DIRECTORY>
|
||||
```
|
||||
|
||||
###### 4. Verify the success of the backup
|
||||
###### 4. Verify the success of the backup & start your node
|
||||
|
||||
The `scp` command should print logs, an operator can see directly whether it was successful or if it encountered any error. However, double check that all your needed configuration is in the backup target directory.
|
||||
|
||||
- Start your node
|
||||
```sh
|
||||
service nym-node start && journalctl -u nym-node -f
|
||||
```
|
||||
</Steps>
|
||||
|
||||
Now you have everything needed to restore your `nym-node` on another server. If you are in a need of doing so, follow the steps in [*Restoring a node*](#restoring-a-node) chapter below.
|
||||
@@ -360,7 +369,7 @@ mkdir ~/.nym/nym-nodes
|
||||
```
|
||||
###### 2. Backup `clients.sqlite` database
|
||||
|
||||
- Stop your node
|
||||
- **Stop your node**
|
||||
```sh
|
||||
service nym-node stop
|
||||
```
|
||||
|
||||
@@ -20,12 +20,12 @@ This documentation page provides a guide on how to set up and run a [NYM NODE](.
|
||||
```sh
|
||||
nym-node
|
||||
Binary Name: nym-node
|
||||
Build Timestamp: 2025-02-13T11:49:34.670488195Z
|
||||
Build Version: 1.5.0
|
||||
Commit SHA: a3e19b4563843055b305ea9a397eb1ad84b5c378
|
||||
Commit Date: 2025-02-10T18:14:47.000000000+01:00
|
||||
Build Timestamp: 2025-03-06T20:32:36.922212778Z
|
||||
Build Version: 1.6.2
|
||||
Commit SHA: 247ebb7c4339de0a298a7fcb2574122a8306c3b8
|
||||
Commit Date: 2025-03-06T21:26:16.000000000+01:00
|
||||
Commit Branch: HEAD
|
||||
rustc Version: 1.84.1
|
||||
rustc Version: 1.85.0
|
||||
rustc Channel: stable
|
||||
cargo Profile: release
|
||||
```
|
||||
|
||||
@@ -3,8 +3,9 @@ import { VarInfo } from 'components/variable-info.tsx';
|
||||
import { Steps } from 'nextra/components';import { Tabs } from 'nextra/components';
|
||||
import { MyTab } from 'components/generic-tabs.tsx';
|
||||
import PortsNymNode from 'components/operators/snippets/ports-nym-node.mdx';
|
||||
import PortsValidator from 'components/operators/snippets/ports-validator.mdx'
|
||||
import NymNodeSpecs from 'components/operators/snippets/nym-node-specs.mdx'
|
||||
import PortsValidator from 'components/operators/snippets/ports-validator.mdx';
|
||||
import NymNodeSpecs from 'components/operators/snippets/nym-node-specs.mdx';
|
||||
import NTPSync from 'components/operators/snippets/ntp-time-sync.mdx'
|
||||
|
||||
# VPS Setup & Configuration
|
||||
|
||||
@@ -42,14 +43,18 @@ apt update -y && apt --fix-broken install
|
||||
```
|
||||
- Install dependencies
|
||||
```sh
|
||||
apt -y install ca-certificates jq curl wget ufw jq tmux pkg-config build-essential libssl-dev git
|
||||
apt -y install ca-certificates jq curl wget ufw jq tmux pkg-config build-essential libssl-dev git ntp ntpdate
|
||||
```
|
||||
- Double check ufw is installed correctly
|
||||
```sh
|
||||
apt install ufw --fix-missing
|
||||
```
|
||||
|
||||
###### 2. Configure your firewall using Uncomplicated Firewall (UFW)
|
||||
###### 2. Synchronize time of your server
|
||||
|
||||
<NTPSync />
|
||||
|
||||
###### 3. Configure your firewall using Uncomplicated Firewall (UFW)
|
||||
|
||||
For a `nym-node` or Nyx validator to recieve traffic, you need to open ports on the server. The following commands will allow you to set up a firewall using `ufw`.
|
||||
|
||||
@@ -70,7 +75,7 @@ ufw enable
|
||||
ufw status
|
||||
```
|
||||
|
||||
###### 3. Open all needed ports to have your firewall for `nym-node` working correctly
|
||||
###### 4. Open all needed ports to have your firewall for `nym-node` working correctly
|
||||
|
||||
<div>
|
||||
<Tabs items={[
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "explorer-api"
|
||||
version = "1.1.47"
|
||||
version = "1.1.48"
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
|
||||
@@ -102,8 +102,8 @@ pub struct Debug {
|
||||
|
||||
pub zk_nym_tickets: ZkNymTicketHandlerDebug,
|
||||
|
||||
/// Defines the maximum age of a signed authentication request before it's deemed too stale to process.
|
||||
pub maximum_auth_request_age: Duration,
|
||||
/// Defines the timestamp skew of a signed authentication request before it's deemed too excessive to process.
|
||||
pub max_request_timestamp_skew: Duration,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
||||
@@ -14,12 +14,11 @@ use std::time::Duration;
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct Config {
|
||||
pub(crate) enforce_zk_nym: bool,
|
||||
pub(crate) max_auth_request_age: Duration,
|
||||
pub(crate) max_request_timestamp_skew: Duration,
|
||||
|
||||
pub(crate) bandwidth: BandwidthFlushingBehaviourConfig,
|
||||
}
|
||||
|
||||
// I can see this being possible expanded with say storage or client store
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct CommonHandlerState {
|
||||
pub(crate) cfg: Config,
|
||||
|
||||
@@ -131,9 +131,6 @@ impl<R, S> FreshHandler<R, S> {
|
||||
|
||||
// for time being we assume handle is always constructed from raw socket.
|
||||
// if we decide we want to change it, that's not too difficult
|
||||
// also at this point I'm not entirely sure how to deal with this warning without
|
||||
// some considerable refactoring
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn new(
|
||||
rng: R,
|
||||
conn: S,
|
||||
@@ -426,6 +423,11 @@ impl<R, S> FreshHandler<R, S> {
|
||||
return Ok(2);
|
||||
}
|
||||
|
||||
// a v4 gateway will understand v3 requests (aes256gcm-siv)
|
||||
if client_protocol_version == 3 {
|
||||
return Ok(3);
|
||||
}
|
||||
|
||||
// we can't handle clients with higher protocol than ours
|
||||
// (perhaps we could try to negotiate downgrade on our end? sounds like a nice future improvement)
|
||||
if client_protocol_version <= CURRENT_PROTOCOL_VERSION {
|
||||
@@ -636,7 +638,7 @@ impl<R, S> FreshHandler<R, S> {
|
||||
|
||||
// do cheap checks first
|
||||
// is the provided timestamp relatively recent (and not in the future?)
|
||||
request.verify_timestamp(self.shared_state.cfg.max_auth_request_age)?;
|
||||
request.verify_timestamp(self.shared_state.cfg.max_request_timestamp_skew)?;
|
||||
|
||||
// does the message signature verify?
|
||||
request.verify_signature()?;
|
||||
|
||||
@@ -6,9 +6,8 @@ use crate::node::client_handling::websocket::connection_handler::FreshHandler;
|
||||
use nym_task::TaskClient;
|
||||
use rand::rngs::OsRng;
|
||||
use std::net::SocketAddr;
|
||||
use std::process;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::{io, process};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::task::JoinHandle;
|
||||
use tracing::*;
|
||||
|
||||
@@ -34,6 +33,76 @@ impl Listener {
|
||||
}
|
||||
}
|
||||
|
||||
fn active_connections(&self) -> usize {
|
||||
self.shared_state
|
||||
.metrics
|
||||
.network
|
||||
.active_ingress_websocket_connections_count()
|
||||
}
|
||||
|
||||
fn prepare_connection_handler(
|
||||
&self,
|
||||
socket: TcpStream,
|
||||
remote_address: SocketAddr,
|
||||
) -> FreshHandler<OsRng, TcpStream> {
|
||||
let shutdown = self
|
||||
.shutdown
|
||||
.fork(format!("websocket_handler_{remote_address}"));
|
||||
FreshHandler::new(
|
||||
OsRng,
|
||||
socket,
|
||||
self.shared_state.clone(),
|
||||
remote_address,
|
||||
shutdown,
|
||||
)
|
||||
}
|
||||
|
||||
fn try_handle_accepted_connection(&self, accepted: io::Result<(TcpStream, SocketAddr)>) {
|
||||
match accepted {
|
||||
Ok((socket, remote_address)) => {
|
||||
trace!("received a socket connection from {remote_address}");
|
||||
|
||||
let active = self.active_connections();
|
||||
|
||||
// 1. check if we're within the connection limit
|
||||
if active >= self.maximum_open_connections {
|
||||
warn!(
|
||||
"connection limit exceeded ({}). can't accept request from {remote_address}",
|
||||
self.maximum_open_connections
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
debug!("there are currently {active} connected clients on the gateway websocket");
|
||||
|
||||
// 2. prepare shared data for the new connection handler
|
||||
let handle = self.prepare_connection_handler(socket, remote_address);
|
||||
|
||||
// 3. increment the connection counter.
|
||||
// make sure to do it before spawning the task,
|
||||
// as another connection might get accepted before the task is scheduled
|
||||
// for execution
|
||||
self.shared_state
|
||||
.metrics
|
||||
.network
|
||||
.new_ingress_websocket_client();
|
||||
|
||||
// 4. spawn the task handling the client connection
|
||||
tokio::spawn(async move {
|
||||
// TODO: refactor it similarly to the mixnet listener on the nym-node
|
||||
let metrics_ref = handle.shared_state.metrics.clone();
|
||||
|
||||
// 4.1. handle all client requests until connection gets terminated
|
||||
handle.start_handling().await;
|
||||
|
||||
// 4.2. decrement the connection counter
|
||||
metrics_ref.network.disconnected_ingress_websocket_client();
|
||||
});
|
||||
}
|
||||
Err(err) => warn!("failed to accept client connection: {err}"),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: change the signature to pub(crate) async fn run(&self, handler: Handler)
|
||||
|
||||
pub(crate) async fn run(&mut self) {
|
||||
@@ -46,8 +115,6 @@ impl Listener {
|
||||
}
|
||||
};
|
||||
|
||||
let open_connections = Arc::new(AtomicUsize::new(0));
|
||||
|
||||
while !self.shutdown.is_shutdown() {
|
||||
tokio::select! {
|
||||
biased;
|
||||
@@ -55,38 +122,7 @@ impl Listener {
|
||||
trace!("client_handling::Listener: received shutdown");
|
||||
}
|
||||
connection = tcp_listener.accept() => {
|
||||
match connection {
|
||||
Ok((socket, remote_addr)) => {
|
||||
let shutdown = self.shutdown.fork(format!("websocket_handler_{remote_addr}"));
|
||||
trace!("received a socket connection from {remote_addr}");
|
||||
|
||||
if open_connections.fetch_add(1, Ordering::SeqCst) >= self.maximum_open_connections {
|
||||
warn!("connection limit exceeded ({}). can't accept request from {remote_addr}", self.maximum_open_connections);
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: I think we *REALLY* need a mechanism for having a maximum number of connected
|
||||
// clients or spawned tokio tasks -> perhaps a worker system?
|
||||
let handle = FreshHandler::new(
|
||||
OsRng,
|
||||
socket,
|
||||
self.shared_state.clone(),
|
||||
remote_addr,
|
||||
shutdown,
|
||||
);
|
||||
let open_connections = open_connections.clone();
|
||||
tokio::spawn(async move {
|
||||
// TODO: refactor it similarly to the mixnet listener on the nym-node
|
||||
let metrics_ref = handle.shared_state.metrics.clone();
|
||||
metrics_ref.network.new_ingress_websocket_client();
|
||||
open_connections.fetch_add(1, Ordering::SeqCst);
|
||||
handle.start_handling().await;
|
||||
metrics_ref.network.disconnected_ingress_websocket_client();
|
||||
open_connections.fetch_sub(1, Ordering::SeqCst);
|
||||
});
|
||||
}
|
||||
Err(err) => warn!("failed to get client: {err}"),
|
||||
}
|
||||
self.try_handle_accepted_connection(connection)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -251,7 +251,7 @@ impl GatewayTasksBuilder {
|
||||
let shared_state = websocket::CommonHandlerState {
|
||||
cfg: websocket::Config {
|
||||
enforce_zk_nym: self.config.gateway.enforce_zk_nyms,
|
||||
max_auth_request_age: self.config.debug.maximum_auth_request_age,
|
||||
max_request_timestamp_skew: self.config.debug.max_request_timestamp_skew,
|
||||
bandwidth: (&self.config).into(),
|
||||
},
|
||||
ecash_verifier: self.ecash_manager().await?,
|
||||
|
||||
+3
-3
@@ -4,7 +4,7 @@
|
||||
[package]
|
||||
name = "nym-api"
|
||||
license = "GPL-3.0"
|
||||
version = "1.1.51"
|
||||
version = "1.1.54"
|
||||
authors.workspace = true
|
||||
edition = "2021"
|
||||
rust-version.workspace = true
|
||||
@@ -141,7 +141,7 @@ tempfile = { workspace = true }
|
||||
cw3 = { workspace = true }
|
||||
cw-utils = { workspace = true }
|
||||
rand_chacha = { workspace = true }
|
||||
sha2 = "0.9"
|
||||
sha2 = { workspace = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
workspace = true
|
||||
|
||||
@@ -17,6 +17,7 @@ humantime-serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
sha2.workspace = true
|
||||
tendermint = { workspace = true }
|
||||
tendermint-rpc = { workspace = true }
|
||||
thiserror.workspace = true
|
||||
time = { workspace = true, features = ["serde", "parsing", "formatting"] }
|
||||
ts-rs = { workspace = true, optional = true }
|
||||
|
||||
@@ -27,9 +27,7 @@ use nym_network_defaults::{DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_
|
||||
use nym_node_requests::api::v1::authenticator::models::Authenticator;
|
||||
use nym_node_requests::api::v1::gateway::models::Wireguard;
|
||||
use nym_node_requests::api::v1::ip_packet_router::models::IpPacketRouter;
|
||||
use nym_node_requests::api::v1::node::models::{
|
||||
AuxiliaryDetails, BinaryBuildInformationOwned, NodeRoles,
|
||||
};
|
||||
use nym_node_requests::api::v1::node::models::{AuxiliaryDetails, NodeRoles};
|
||||
use schemars::gen::SchemaGenerator;
|
||||
use schemars::schema::{InstanceType, Schema, SchemaObject};
|
||||
use schemars::JsonSchema;
|
||||
@@ -43,6 +41,8 @@ use thiserror::Error;
|
||||
use time::{Date, OffsetDateTime};
|
||||
use utoipa::{IntoParams, ToResponse, ToSchema};
|
||||
|
||||
pub use nym_node_requests::api::v1::node::models::BinaryBuildInformationOwned;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
pub struct RequestError {
|
||||
message: String,
|
||||
@@ -1215,6 +1215,7 @@ impl From<Wireguard> for WireguardDetails {
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
|
||||
pub struct ApiHealthResponse {
|
||||
pub status: ApiStatus,
|
||||
#[serde(default)]
|
||||
pub chain_status: ChainStatus,
|
||||
pub uptime: u64,
|
||||
}
|
||||
@@ -1225,10 +1226,11 @@ pub enum ApiStatus {
|
||||
Up,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Default, schemars::JsonSchema, ToSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ChainStatus {
|
||||
Synced,
|
||||
#[default]
|
||||
Unknown,
|
||||
Stalled {
|
||||
#[serde(
|
||||
@@ -1428,7 +1430,297 @@ impl From<nym_mixnet_contract_common::EpochRewardedSet> for RewardedSetResponse
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
pub struct ChainStatusResponse {
|
||||
pub connected_nyxd: String,
|
||||
pub status: DetailedChainStatus,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
pub struct DetailedChainStatus {
|
||||
pub abci: crate::models::tendermint_types::AbciInfo,
|
||||
pub latest_block: BlockInfo,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
pub struct BlockInfo {
|
||||
pub block_id: BlockId,
|
||||
pub block: FullBlockInfo,
|
||||
// if necessary we might put block data here later too
|
||||
}
|
||||
|
||||
impl From<tendermint_rpc::endpoint::block::Response> for BlockInfo {
|
||||
fn from(value: tendermint_rpc::endpoint::block::Response) -> Self {
|
||||
BlockInfo {
|
||||
block_id: value.block_id.into(),
|
||||
block: FullBlockInfo {
|
||||
header: value.block.header.into(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
pub struct FullBlockInfo {
|
||||
pub header: BlockHeader,
|
||||
}
|
||||
|
||||
// copy tendermint types definitions whilst deriving schema types on them and dropping unwanted fields
|
||||
pub mod tendermint_types {
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tendermint::abci::response::Info;
|
||||
use tendermint::block::header::Version;
|
||||
use tendermint::{block, Hash};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
pub struct AbciInfo {
|
||||
/// Some arbitrary information.
|
||||
pub data: String,
|
||||
|
||||
/// The application software semantic version.
|
||||
pub version: String,
|
||||
|
||||
/// The application protocol version.
|
||||
pub app_version: u64,
|
||||
|
||||
/// The latest block for which the app has called [`Commit`].
|
||||
pub last_block_height: u64,
|
||||
|
||||
/// The latest result of [`Commit`].
|
||||
pub last_block_app_hash: String,
|
||||
}
|
||||
|
||||
impl From<Info> for AbciInfo {
|
||||
fn from(value: Info) -> Self {
|
||||
AbciInfo {
|
||||
data: value.data,
|
||||
version: value.version,
|
||||
app_version: value.app_version,
|
||||
last_block_height: value.last_block_height.value(),
|
||||
last_block_app_hash: value.last_block_app_hash.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `Version` contains the protocol version for the blockchain and the
|
||||
/// application.
|
||||
///
|
||||
/// <https://github.com/tendermint/spec/blob/d46cd7f573a2c6a2399fcab2cde981330aa63f37/spec/core/data_structures.md#version>
|
||||
#[derive(
|
||||
Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema, ToSchema,
|
||||
)]
|
||||
pub struct HeaderVersion {
|
||||
/// Block version
|
||||
pub block: u64,
|
||||
|
||||
/// App version
|
||||
pub app: u64,
|
||||
}
|
||||
|
||||
impl From<tendermint::block::header::Version> for HeaderVersion {
|
||||
fn from(value: Version) -> Self {
|
||||
HeaderVersion {
|
||||
block: value.block,
|
||||
app: value.app,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Block identifiers which contain two distinct Merkle roots of the block,
|
||||
/// as well as the number of parts in the block.
|
||||
///
|
||||
/// <https://github.com/tendermint/spec/blob/d46cd7f573a2c6a2399fcab2cde981330aa63f37/spec/core/data_structures.md#blockid>
|
||||
///
|
||||
/// Default implementation is an empty Id as defined by the Go implementation in
|
||||
/// <https://github.com/tendermint/tendermint/blob/1635d1339c73ae6a82e062cd2dc7191b029efa14/types/block.go#L1204>.
|
||||
///
|
||||
/// If the Hash is empty in BlockId, the BlockId should be empty (encoded to None).
|
||||
/// This is implemented outside of this struct. Use the Default trait to check for an empty BlockId.
|
||||
/// See: <https://github.com/informalsystems/tendermint-rs/issues/663>
|
||||
#[derive(
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Copy,
|
||||
Clone,
|
||||
Debug,
|
||||
Default,
|
||||
Hash,
|
||||
Eq,
|
||||
PartialEq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
JsonSchema,
|
||||
ToSchema,
|
||||
)]
|
||||
pub struct BlockId {
|
||||
/// The block's main hash is the Merkle root of all the fields in the
|
||||
/// block header.
|
||||
#[schemars(with = "String")]
|
||||
#[schema(value_type = String)]
|
||||
pub hash: Hash,
|
||||
|
||||
/// Parts header (if available) is used for secure gossipping of the block
|
||||
/// during consensus. It is the Merkle root of the complete serialized block
|
||||
/// cut into parts.
|
||||
///
|
||||
/// PartSet is used to split a byteslice of data into parts (pieces) for
|
||||
/// transmission. By splitting data into smaller parts and computing a
|
||||
/// Merkle root hash on the list, you can verify that a part is
|
||||
/// legitimately part of the complete data, and the part can be forwarded
|
||||
/// to other peers before all the parts are known. In short, it's a fast
|
||||
/// way to propagate a large file over a gossip network.
|
||||
///
|
||||
/// <https://github.com/tendermint/tendermint/wiki/Block-Structure#partset>
|
||||
///
|
||||
/// PartSetHeader in protobuf is defined as never nil using the gogoproto
|
||||
/// annotations. This does not translate to Rust, but we can indicate this
|
||||
/// in the domain type.
|
||||
pub part_set_header: PartSetHeader,
|
||||
}
|
||||
|
||||
impl From<block::Id> for BlockId {
|
||||
fn from(value: block::Id) -> Self {
|
||||
BlockId {
|
||||
hash: value.hash,
|
||||
part_set_header: value.part_set_header.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Block parts header
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
Default,
|
||||
Hash,
|
||||
Eq,
|
||||
PartialEq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Deserialize,
|
||||
Serialize,
|
||||
JsonSchema,
|
||||
ToSchema,
|
||||
)]
|
||||
#[non_exhaustive]
|
||||
pub struct PartSetHeader {
|
||||
/// Number of parts in this block
|
||||
pub total: u32,
|
||||
|
||||
/// Hash of the parts set header,
|
||||
#[schemars(with = "String")]
|
||||
#[schema(value_type = String)]
|
||||
pub hash: Hash,
|
||||
}
|
||||
|
||||
impl From<tendermint::block::parts::Header> for PartSetHeader {
|
||||
fn from(value: block::parts::Header) -> Self {
|
||||
PartSetHeader {
|
||||
total: value.total,
|
||||
hash: value.hash,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Block `Header` values contain metadata about the block and about the
|
||||
/// consensus, as well as commitments to the data in the current block, the
|
||||
/// previous block, and the results returned by the application.
|
||||
///
|
||||
/// <https://github.com/tendermint/spec/blob/d46cd7f573a2c6a2399fcab2cde981330aa63f37/spec/core/data_structures.md#header>
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
pub struct BlockHeader {
|
||||
/// Header version
|
||||
pub version: HeaderVersion,
|
||||
|
||||
/// Chain ID
|
||||
pub chain_id: String,
|
||||
|
||||
/// Current block height
|
||||
pub height: u64,
|
||||
|
||||
/// Current timestamp
|
||||
#[schemars(with = "String")]
|
||||
#[schema(value_type = String)]
|
||||
pub time: tendermint::Time,
|
||||
|
||||
/// Previous block info
|
||||
pub last_block_id: Option<BlockId>,
|
||||
|
||||
/// Commit from validators from the last block
|
||||
#[schemars(with = "Option<String>")]
|
||||
#[schema(value_type = Option<String>)]
|
||||
pub last_commit_hash: Option<Hash>,
|
||||
|
||||
/// Merkle root of transaction hashes
|
||||
#[schemars(with = "Option<String>")]
|
||||
#[schema(value_type = Option<String>)]
|
||||
pub data_hash: Option<Hash>,
|
||||
|
||||
/// Validators for the current block
|
||||
#[schemars(with = "String")]
|
||||
#[schema(value_type = String)]
|
||||
pub validators_hash: Hash,
|
||||
|
||||
/// Validators for the next block
|
||||
#[schemars(with = "String")]
|
||||
#[schema(value_type = String)]
|
||||
pub next_validators_hash: Hash,
|
||||
|
||||
/// Consensus params for the current block
|
||||
#[schemars(with = "String")]
|
||||
#[schema(value_type = String)]
|
||||
pub consensus_hash: Hash,
|
||||
|
||||
/// State after txs from the previous block
|
||||
#[schemars(with = "String")]
|
||||
#[schema(value_type = String)]
|
||||
pub app_hash: Hash,
|
||||
|
||||
/// Root hash of all results from the txs from the previous block
|
||||
#[schemars(with = "Option<String>")]
|
||||
#[schema(value_type = Option<String>)]
|
||||
pub last_results_hash: Option<Hash>,
|
||||
|
||||
/// Hash of evidence included in the block
|
||||
#[schemars(with = "Option<String>")]
|
||||
#[schema(value_type = Option<String>)]
|
||||
pub evidence_hash: Option<Hash>,
|
||||
|
||||
/// Original proposer of the block
|
||||
#[serde(with = "nym_serde_helpers::hex")]
|
||||
#[schemars(with = "String")]
|
||||
#[schema(value_type = String)]
|
||||
pub proposer_address: Vec<u8>,
|
||||
}
|
||||
|
||||
impl From<block::Header> for BlockHeader {
|
||||
fn from(value: block::Header) -> Self {
|
||||
BlockHeader {
|
||||
version: value.version.into(),
|
||||
chain_id: value.chain_id.to_string(),
|
||||
height: value.height.value(),
|
||||
time: value.time,
|
||||
last_block_id: value.last_block_id.map(Into::into),
|
||||
last_commit_hash: value.last_commit_hash,
|
||||
data_hash: value.data_hash,
|
||||
validators_hash: value.validators_hash,
|
||||
next_validators_hash: value.next_validators_hash,
|
||||
consensus_hash: value.consensus_hash,
|
||||
app_hash: Hash::try_from(value.app_hash.as_bytes().to_vec()).unwrap_or_default(),
|
||||
last_results_hash: value.last_results_hash,
|
||||
evidence_hash: value.evidence_hash,
|
||||
proposer_address: value.proposer_address.as_bytes().to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use crate::models::tendermint_types::{BlockHeader, BlockId};
|
||||
pub use config_score::*;
|
||||
|
||||
pub mod config_score {
|
||||
use nym_contracts_common::NaiveFloat;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -10,7 +10,7 @@ use nym_dkg::bte::keys::KeyPair as DkgKeyPair;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use std::path::Path;
|
||||
use thiserror::__private::AsDisplay;
|
||||
use tracing::warn;
|
||||
use tracing::{debug, warn};
|
||||
|
||||
pub(crate) fn init_bte_keypair<R: RngCore + CryptoRng>(
|
||||
rng: &mut R,
|
||||
@@ -39,17 +39,20 @@ pub(crate) fn load_bte_keypair(config: &config::EcashSigner) -> anyhow::Result<D
|
||||
pub(crate) fn load_ecash_keypair_if_exists(
|
||||
config: &config::EcashSigner,
|
||||
) -> anyhow::Result<Option<KeyPairWithEpoch>> {
|
||||
let storage_path = &config.storage_paths.ecash_key_path;
|
||||
debug!(
|
||||
"attempting to ecash keypair from {}",
|
||||
storage_path.display()
|
||||
);
|
||||
if !config.storage_paths.ecash_key_path.exists() {
|
||||
debug!("the provided filepath doesn't exist - the key won't be loaded");
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
if let Ok(ecash_key) =
|
||||
let ecash_key =
|
||||
nym_pemstore::load_key::<KeyPairWithEpoch, _>(&config.storage_paths.ecash_key_path)
|
||||
{
|
||||
return Ok(Some(ecash_key));
|
||||
}
|
||||
|
||||
bail!("ecash key load failure")
|
||||
.context("failed to load ecash key")?;
|
||||
Ok(Some(ecash_key))
|
||||
}
|
||||
|
||||
// the keys can be considered valid if they were generated for the current dkg epoch
|
||||
|
||||
@@ -33,14 +33,17 @@ impl DailyMerkleTree {
|
||||
.into_iter()
|
||||
.map(|l| (l.merkle_index, l))
|
||||
.collect();
|
||||
let total_leaves = leaves.len();
|
||||
|
||||
let mut sorted_leaves = Vec::new();
|
||||
for i in 0..leaves.len() {
|
||||
if let Some(next_leaf) = leaves.remove(&i) {
|
||||
sorted_leaves.push(next_leaf);
|
||||
} else {
|
||||
let lost = leaves.len() - i + 1;
|
||||
error!("failed to produce consistent merkle tree. there was no leaf with index {i}. at least {lost} leaves got lost")
|
||||
let lost = total_leaves - i + 1;
|
||||
error!("failed to produce consistent merkle tree. there was no leaf with index {i}. at least {lost} leaves got lost");
|
||||
// we have to drop all data above that height because we can't rebuild the full tree
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ impl InternalCounters {
|
||||
|
||||
// just hash the current counter
|
||||
self.tx_hash_counter += 1;
|
||||
Hash::Sha256(sha2::Sha256::digest(&self.tx_hash_counter.to_be_bytes()).into())
|
||||
Hash::Sha256(sha2::Sha256::digest(self.tx_hash_counter.to_be_bytes()).into())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::network::models::{ChainStatusResponse, ContractInformation, NetworkDetails};
|
||||
use crate::network::models::{ContractInformation, NetworkDetails};
|
||||
use crate::node_status_api::models::AxumResult;
|
||||
use crate::support::http::state::AppState;
|
||||
use axum::extract::State;
|
||||
use axum::{extract, Json, Router};
|
||||
use nym_api_requests::models::ChainStatusResponse;
|
||||
use nym_contracts_common::ContractBuildInformation;
|
||||
use std::collections::HashMap;
|
||||
use tower_http::compression::CompressionLayer;
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::network::models::tendermint_types::{AbciInfo, BlockHeader, BlockId};
|
||||
use nym_config::defaults::NymNetworkDetails;
|
||||
use nym_validator_client::nyxd::BlockResponse;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
@@ -29,292 +27,3 @@ pub struct ContractInformation<T> {
|
||||
pub(crate) address: Option<String>,
|
||||
pub(crate) details: Option<T>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
pub struct ChainStatusResponse {
|
||||
pub connected_nyxd: String,
|
||||
pub status: ChainStatus,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
pub struct ChainStatus {
|
||||
pub abci: AbciInfo,
|
||||
pub latest_block: BlockInfo,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
pub struct BlockInfo {
|
||||
pub block_id: BlockId,
|
||||
pub block: FullBlockInfo,
|
||||
// if necessary we might put block data here later too
|
||||
}
|
||||
|
||||
impl From<BlockResponse> for BlockInfo {
|
||||
fn from(value: BlockResponse) -> Self {
|
||||
BlockInfo {
|
||||
block_id: value.block_id.into(),
|
||||
block: FullBlockInfo {
|
||||
header: value.block.header.into(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
pub struct FullBlockInfo {
|
||||
pub header: BlockHeader,
|
||||
}
|
||||
|
||||
// copy tendermint types definitions whilst deriving schema types on them and dropping unwanted fields
|
||||
pub mod tendermint_types {
|
||||
use nym_validator_client::nyxd::Hash;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tendermint::abci::response::Info;
|
||||
use tendermint::block;
|
||||
use tendermint::block::header::Version;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
pub struct AbciInfo {
|
||||
/// Some arbitrary information.
|
||||
pub data: String,
|
||||
|
||||
/// The application software semantic version.
|
||||
pub version: String,
|
||||
|
||||
/// The application protocol version.
|
||||
pub app_version: u64,
|
||||
|
||||
/// The latest block for which the app has called [`Commit`].
|
||||
pub last_block_height: u64,
|
||||
|
||||
/// The latest result of [`Commit`].
|
||||
pub last_block_app_hash: String,
|
||||
}
|
||||
|
||||
impl From<Info> for AbciInfo {
|
||||
fn from(value: Info) -> Self {
|
||||
AbciInfo {
|
||||
data: value.data,
|
||||
version: value.version,
|
||||
app_version: value.app_version,
|
||||
last_block_height: value.last_block_height.value(),
|
||||
last_block_app_hash: value.last_block_app_hash.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `Version` contains the protocol version for the blockchain and the
|
||||
/// application.
|
||||
///
|
||||
/// <https://github.com/tendermint/spec/blob/d46cd7f573a2c6a2399fcab2cde981330aa63f37/spec/core/data_structures.md#version>
|
||||
#[derive(
|
||||
Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema, ToSchema,
|
||||
)]
|
||||
pub struct HeaderVersion {
|
||||
/// Block version
|
||||
pub block: u64,
|
||||
|
||||
/// App version
|
||||
pub app: u64,
|
||||
}
|
||||
|
||||
impl From<tendermint::block::header::Version> for HeaderVersion {
|
||||
fn from(value: Version) -> Self {
|
||||
HeaderVersion {
|
||||
block: value.block,
|
||||
app: value.app,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Block identifiers which contain two distinct Merkle roots of the block,
|
||||
/// as well as the number of parts in the block.
|
||||
///
|
||||
/// <https://github.com/tendermint/spec/blob/d46cd7f573a2c6a2399fcab2cde981330aa63f37/spec/core/data_structures.md#blockid>
|
||||
///
|
||||
/// Default implementation is an empty Id as defined by the Go implementation in
|
||||
/// <https://github.com/tendermint/tendermint/blob/1635d1339c73ae6a82e062cd2dc7191b029efa14/types/block.go#L1204>.
|
||||
///
|
||||
/// If the Hash is empty in BlockId, the BlockId should be empty (encoded to None).
|
||||
/// This is implemented outside of this struct. Use the Default trait to check for an empty BlockId.
|
||||
/// See: <https://github.com/informalsystems/tendermint-rs/issues/663>
|
||||
#[derive(
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Copy,
|
||||
Clone,
|
||||
Debug,
|
||||
Default,
|
||||
Hash,
|
||||
Eq,
|
||||
PartialEq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
JsonSchema,
|
||||
ToSchema,
|
||||
)]
|
||||
pub struct BlockId {
|
||||
/// The block's main hash is the Merkle root of all the fields in the
|
||||
/// block header.
|
||||
#[schemars(with = "String")]
|
||||
#[schema(value_type = String)]
|
||||
pub hash: Hash,
|
||||
|
||||
/// Parts header (if available) is used for secure gossipping of the block
|
||||
/// during consensus. It is the Merkle root of the complete serialized block
|
||||
/// cut into parts.
|
||||
///
|
||||
/// PartSet is used to split a byteslice of data into parts (pieces) for
|
||||
/// transmission. By splitting data into smaller parts and computing a
|
||||
/// Merkle root hash on the list, you can verify that a part is
|
||||
/// legitimately part of the complete data, and the part can be forwarded
|
||||
/// to other peers before all the parts are known. In short, it's a fast
|
||||
/// way to propagate a large file over a gossip network.
|
||||
///
|
||||
/// <https://github.com/tendermint/tendermint/wiki/Block-Structure#partset>
|
||||
///
|
||||
/// PartSetHeader in protobuf is defined as never nil using the gogoproto
|
||||
/// annotations. This does not translate to Rust, but we can indicate this
|
||||
/// in the domain type.
|
||||
pub part_set_header: PartSetHeader,
|
||||
}
|
||||
|
||||
impl From<block::Id> for BlockId {
|
||||
fn from(value: block::Id) -> Self {
|
||||
BlockId {
|
||||
hash: value.hash,
|
||||
part_set_header: value.part_set_header.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Block parts header
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
Default,
|
||||
Hash,
|
||||
Eq,
|
||||
PartialEq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Deserialize,
|
||||
Serialize,
|
||||
JsonSchema,
|
||||
ToSchema,
|
||||
)]
|
||||
#[non_exhaustive]
|
||||
pub struct PartSetHeader {
|
||||
/// Number of parts in this block
|
||||
pub total: u32,
|
||||
|
||||
/// Hash of the parts set header,
|
||||
#[schemars(with = "String")]
|
||||
#[schema(value_type = String)]
|
||||
pub hash: Hash,
|
||||
}
|
||||
|
||||
impl From<tendermint::block::parts::Header> for PartSetHeader {
|
||||
fn from(value: block::parts::Header) -> Self {
|
||||
PartSetHeader {
|
||||
total: value.total,
|
||||
hash: value.hash,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Block `Header` values contain metadata about the block and about the
|
||||
/// consensus, as well as commitments to the data in the current block, the
|
||||
/// previous block, and the results returned by the application.
|
||||
///
|
||||
/// <https://github.com/tendermint/spec/blob/d46cd7f573a2c6a2399fcab2cde981330aa63f37/spec/core/data_structures.md#header>
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
pub struct BlockHeader {
|
||||
/// Header version
|
||||
pub version: HeaderVersion,
|
||||
|
||||
/// Chain ID
|
||||
pub chain_id: String,
|
||||
|
||||
/// Current block height
|
||||
pub height: u64,
|
||||
|
||||
/// Current timestamp
|
||||
#[schemars(with = "String")]
|
||||
#[schema(value_type = String)]
|
||||
pub time: tendermint::Time,
|
||||
|
||||
/// Previous block info
|
||||
pub last_block_id: Option<BlockId>,
|
||||
|
||||
/// Commit from validators from the last block
|
||||
#[schemars(with = "Option<String>")]
|
||||
#[schema(value_type = Option<String>)]
|
||||
pub last_commit_hash: Option<Hash>,
|
||||
|
||||
/// Merkle root of transaction hashes
|
||||
#[schemars(with = "Option<String>")]
|
||||
#[schema(value_type = Option<String>)]
|
||||
pub data_hash: Option<Hash>,
|
||||
|
||||
/// Validators for the current block
|
||||
#[schemars(with = "String")]
|
||||
#[schema(value_type = String)]
|
||||
pub validators_hash: Hash,
|
||||
|
||||
/// Validators for the next block
|
||||
#[schemars(with = "String")]
|
||||
#[schema(value_type = String)]
|
||||
pub next_validators_hash: Hash,
|
||||
|
||||
/// Consensus params for the current block
|
||||
#[schemars(with = "String")]
|
||||
#[schema(value_type = String)]
|
||||
pub consensus_hash: Hash,
|
||||
|
||||
/// State after txs from the previous block
|
||||
#[schemars(with = "String")]
|
||||
#[schema(value_type = String)]
|
||||
pub app_hash: Hash,
|
||||
|
||||
/// Root hash of all results from the txs from the previous block
|
||||
#[schemars(with = "Option<String>")]
|
||||
#[schema(value_type = Option<String>)]
|
||||
pub last_results_hash: Option<Hash>,
|
||||
|
||||
/// Hash of evidence included in the block
|
||||
#[schemars(with = "Option<String>")]
|
||||
#[schema(value_type = Option<String>)]
|
||||
pub evidence_hash: Option<Hash>,
|
||||
|
||||
/// Original proposer of the block
|
||||
#[serde(with = "nym_serde_helpers::hex")]
|
||||
#[schemars(with = "String")]
|
||||
#[schema(value_type = String)]
|
||||
pub proposer_address: Vec<u8>,
|
||||
}
|
||||
|
||||
impl From<block::Header> for BlockHeader {
|
||||
fn from(value: block::Header) -> Self {
|
||||
BlockHeader {
|
||||
version: value.version.into(),
|
||||
chain_id: value.chain_id.to_string(),
|
||||
height: value.height.value(),
|
||||
time: value.time,
|
||||
last_block_id: value.last_block_id.map(Into::into),
|
||||
last_commit_hash: value.last_commit_hash,
|
||||
data_hash: value.data_hash,
|
||||
validators_hash: value.validators_hash,
|
||||
next_validators_hash: value.next_validators_hash,
|
||||
consensus_hash: value.consensus_hash,
|
||||
app_hash: Hash::try_from(value.app_hash.as_bytes().to_vec()).unwrap_or_default(),
|
||||
last_results_hash: value.last_results_hash,
|
||||
evidence_hash: value.evidence_hash,
|
||||
proposer_address: value.proposer_address.as_bytes().to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,6 +191,7 @@ async fn try_get_client(
|
||||
// if provided host was malformed, no point in continuing
|
||||
let client = match nym_node_requests::api::Client::builder(address).and_then(|b| {
|
||||
b.with_timeout(Duration::from_secs(5))
|
||||
.no_hickory_dns()
|
||||
.with_user_agent("nym-api-describe-cache")
|
||||
.build()
|
||||
}) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use crate::circulating_supply_api::cache::CirculatingSupplyCache;
|
||||
use crate::ecash::state::EcashState;
|
||||
use crate::network::models::{ChainStatus, NetworkDetails};
|
||||
use crate::network::models::NetworkDetails;
|
||||
use crate::node_describe_cache::DescribedNodes;
|
||||
use crate::node_status_api::handlers::unstable;
|
||||
use crate::node_status_api::models::AxumErrorResponse;
|
||||
@@ -15,7 +15,9 @@ use crate::support::caching::Cache;
|
||||
use crate::support::nyxd::Client;
|
||||
use crate::support::storage;
|
||||
use axum::extract::FromRef;
|
||||
use nym_api_requests::models::{GatewayBondAnnotated, MixNodeBondAnnotated, NodeAnnotation};
|
||||
use nym_api_requests::models::{
|
||||
DetailedChainStatus, GatewayBondAnnotated, MixNodeBondAnnotated, NodeAnnotation,
|
||||
};
|
||||
use nym_mixnet_contract_common::NodeId;
|
||||
use nym_task::TaskManager;
|
||||
use nym_topology::CachedEpochRewardedSet;
|
||||
@@ -151,7 +153,7 @@ impl ChainStatusCache {
|
||||
|
||||
struct ChainStatusCacheInner {
|
||||
last_refreshed_at: OffsetDateTime,
|
||||
cache_value: ChainStatus,
|
||||
cache_value: DetailedChainStatus,
|
||||
}
|
||||
|
||||
impl ChainStatusCacheInner {
|
||||
@@ -167,7 +169,7 @@ impl ChainStatusCache {
|
||||
pub(crate) async fn get_or_refresh(
|
||||
&self,
|
||||
client: &Client,
|
||||
) -> Result<ChainStatus, AxumErrorResponse> {
|
||||
) -> Result<DetailedChainStatus, AxumErrorResponse> {
|
||||
if let Some(cached) = self.check_cache().await {
|
||||
return Ok(cached);
|
||||
}
|
||||
@@ -175,7 +177,7 @@ impl ChainStatusCache {
|
||||
self.refresh(client).await
|
||||
}
|
||||
|
||||
async fn check_cache(&self) -> Option<ChainStatus> {
|
||||
async fn check_cache(&self) -> Option<DetailedChainStatus> {
|
||||
let guard = self.inner.read().await;
|
||||
let inner = guard.as_ref()?;
|
||||
if inner.is_valid(self.cache_ttl) {
|
||||
@@ -184,7 +186,7 @@ impl ChainStatusCache {
|
||||
None
|
||||
}
|
||||
|
||||
async fn refresh(&self, client: &Client) -> Result<ChainStatus, AxumErrorResponse> {
|
||||
async fn refresh(&self, client: &Client) -> Result<DetailedChainStatus, AxumErrorResponse> {
|
||||
// 1. attempt to get write lock permit
|
||||
let mut guard = self.inner.write().await;
|
||||
|
||||
@@ -201,7 +203,7 @@ impl ChainStatusCache {
|
||||
.block_info(abci.last_block_height.value() as u32)
|
||||
.await?;
|
||||
|
||||
let status = ChainStatus {
|
||||
let status = DetailedChainStatus {
|
||||
abci: abci.into(),
|
||||
latest_block: block.into(),
|
||||
};
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Nym Node Status API
|
||||
The Node Status API serves information about individual `nym-nodes` in the Mixnet, such as which role they are operating in, statistics about them, services such as Network Requesters, as well as summaries of the state of the Mixnet.
|
||||
|
||||
We recommend that developers building applications such as explorers or analytics interfaces about the Mixnet run their own instance of the API, in order to promote a robust network of downstream services, and spread the load of API calls amongst as many endpoints as possible.
|
||||
|
||||
You can find build and operation instructions in the [docs](https://nym.com/docs/apis/ns-api).
|
||||
@@ -10,7 +10,8 @@ pub(crate) fn generate_key_pair(path: impl AsRef<Path>) -> anyhow::Result<()> {
|
||||
let mut private_key_file = File::create(priv_key_path)?;
|
||||
private_key_file.write_all(keypair.private_key().to_base58_string().as_bytes())?;
|
||||
|
||||
let pub_key_path = priv_key_path.with_extension("public");
|
||||
let pub_key_path = priv_key_path.with_file_name("public-key");
|
||||
|
||||
let mut public_key_file = File::create(&pub_key_path)?;
|
||||
public_key_file.write_all(keypair.public_key().to_base58_string().as_bytes())?;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use tracing::error;
|
||||
use tracing::{debug, error, info};
|
||||
|
||||
pub(crate) struct GwProbe {
|
||||
path: String,
|
||||
@@ -10,21 +10,62 @@ impl GwProbe {
|
||||
}
|
||||
|
||||
pub(crate) async fn version(&self) -> String {
|
||||
debug!("Attempting to execute binary at: {}", &self.path);
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
match tokio::fs::metadata(&self.path).await {
|
||||
Ok(metadata) => {
|
||||
let perms = metadata.permissions();
|
||||
let mode = perms.mode();
|
||||
if mode & 0o111 == 0 {
|
||||
error!(
|
||||
"Binary is not executable: {} (mode: {:o})",
|
||||
&self.path, mode
|
||||
);
|
||||
return "Binary is not executable".to_string();
|
||||
}
|
||||
debug!("Binary exists with permissions: {:o}", mode);
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to stat binary at {}: {}", &self.path, e);
|
||||
return format!("Failed to access binary: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut command = tokio::process::Command::new(&self.path);
|
||||
command.stdout(std::process::Stdio::piped());
|
||||
command.arg("--version");
|
||||
|
||||
info!("Executing command: {:?} --version", &self.path);
|
||||
|
||||
match command.spawn() {
|
||||
Ok(child) => {
|
||||
if let Ok(output) = child.wait_with_output().await {
|
||||
return String::from_utf8(output.stdout)
|
||||
.unwrap_or("Unable to get log from test run".to_string());
|
||||
Ok(child) => match child.wait_with_output().await {
|
||||
Ok(output) => {
|
||||
if output.status.success() {
|
||||
String::from_utf8(output.stdout)
|
||||
.unwrap_or_else(|_| "Unable to parse version output".to_string())
|
||||
} else {
|
||||
let stderr = String::from_utf8(output.stderr)
|
||||
.unwrap_or_else(|_| "Unable to parse error output".to_string());
|
||||
error!(
|
||||
"Command failed with exit code {}: {}",
|
||||
output.status.code().unwrap_or(-1),
|
||||
stderr
|
||||
);
|
||||
format!("Command failed: {}", stderr)
|
||||
}
|
||||
}
|
||||
"Unable to get probe version".to_string()
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to get command output: {}", e);
|
||||
format!("Failed to get command output: {}", e)
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
error!("Failed to get probe version: {}", e);
|
||||
"Failed to get probe version".to_string()
|
||||
error!("Failed to spawn process: {}", e);
|
||||
format!("Failed to spawn process: {}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ nym-bin-common = { path = "../../common/bin-common", features = ["models"] }
|
||||
nym-node-status-client = { path = "../nym-node-status-client" }
|
||||
nym-crypto = { path = "../../common/crypto", features = ["asymmetric", "serde"] }
|
||||
nym-explorer-client = { path = "../../explorer-api/explorer-client" }
|
||||
nym-http-api-client = { path = "../../common/http-api-client" }
|
||||
nym-network-defaults = { path = "../../common/network-defaults" }
|
||||
nym-serde-helpers = { path = "../../common/serde-helpers"}
|
||||
nym-statistics-common = { path = "../../common/statistics" }
|
||||
|
||||
@@ -97,8 +97,12 @@ impl Monitor {
|
||||
.clone()
|
||||
.expect("rust sdk mainnet default missing api_url");
|
||||
|
||||
let api_client =
|
||||
NymApiClient::new_with_timeout(default_api_url, self.nym_api_client_timeout);
|
||||
let nym_api = nym_http_api_client::ClientBuilder::new_with_url(default_api_url)
|
||||
.no_hickory_dns()
|
||||
.with_timeout(self.nym_api_client_timeout)
|
||||
.build::<&str>()?;
|
||||
|
||||
let api_client = NymApiClient { nym_api };
|
||||
|
||||
let described_nodes = api_client
|
||||
.get_all_described_nodes()
|
||||
@@ -296,9 +300,14 @@ impl Monitor {
|
||||
Some(location) => return location,
|
||||
None => {
|
||||
for ip in node.description.host_information.ip_address.iter() {
|
||||
if let Ok(location) = self.ipinfo.locate_ip(ip.to_string()).await {
|
||||
self.geocache.insert(node_id, location.clone()).await;
|
||||
return location;
|
||||
match self.ipinfo.locate_ip(ip.to_string()).await {
|
||||
Ok(location) => {
|
||||
self.geocache.insert(node_id, location.clone()).await;
|
||||
return location;
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::warn!("Couldn't locate IP {} due to: {}", ip, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
// if no data could be retrieved
|
||||
|
||||
@@ -57,7 +57,12 @@ async fn run(
|
||||
.clone()
|
||||
.expect("rust sdk mainnet default missing api_url");
|
||||
|
||||
let api_client = NymApiClient::new_with_timeout(default_api_url, nym_api_client_timeout);
|
||||
let nym_api = nym_http_api_client::ClientBuilder::new_with_url(default_api_url)
|
||||
.no_hickory_dns()
|
||||
.with_timeout(nym_api_client_timeout)
|
||||
.build::<&str>()?;
|
||||
|
||||
let api_client = NymApiClient { nym_api };
|
||||
|
||||
//SW TBC what nodes exactly need to be scraped, the skimmed node endpoint seems to return more nodes
|
||||
let bonded_nodes = api_client.get_all_bonded_nym_nodes().await?;
|
||||
@@ -170,6 +175,7 @@ impl MetricsScrapingData {
|
||||
let client = match nym_node_requests::api::Client::builder(address).and_then(|b| {
|
||||
b.with_timeout(Duration::from_secs(5))
|
||||
.with_user_agent("node-status-api-metrics-scraper")
|
||||
.no_hickory_dns()
|
||||
.build()
|
||||
}) {
|
||||
Ok(client) => client,
|
||||
|
||||
+3
-3
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-node"
|
||||
version = "1.6.0"
|
||||
version = "1.7.0"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
@@ -71,7 +71,7 @@ nym-verloc = { path = "../common/verloc" }
|
||||
nym-metrics = { path = "../common/nym-metrics" }
|
||||
nym-gateway-stats-storage = { path = "../common/gateway-stats-storage" }
|
||||
nym-topology = { path = "../common/topology" }
|
||||
|
||||
nym-http-api-client = { path = "../common/http-api-client" }
|
||||
|
||||
# http server
|
||||
# useful for `#[axum_macros::debug_handler]`
|
||||
@@ -101,4 +101,4 @@ cargo_metadata = { workspace = true }
|
||||
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
workspace = true
|
||||
|
||||
@@ -45,7 +45,7 @@ impl NetworkStats {
|
||||
|
||||
pub fn active_ingress_websocket_connections_count(&self) -> usize {
|
||||
self.active_ingress_websocket_connections
|
||||
.load(Ordering::Relaxed)
|
||||
.load(Ordering::SeqCst)
|
||||
}
|
||||
|
||||
pub fn active_egress_mixnet_connections_counter(&self) -> Arc<AtomicUsize> {
|
||||
|
||||
@@ -54,8 +54,9 @@ pub struct Debug {
|
||||
/// of the services providers
|
||||
pub minimum_mix_performance: u8,
|
||||
|
||||
/// Defines the maximum age of a signed authentication request before it's deemed too stale to process.
|
||||
pub maximum_auth_request_age: Duration,
|
||||
/// Defines the timestamp skew of a signed authentication request before it's deemed too excessive to process.
|
||||
#[serde(alias = "maximum_auth_request_age")]
|
||||
pub max_request_timestamp_skew: Duration,
|
||||
|
||||
pub stale_messages: StaleMessageDebug,
|
||||
|
||||
@@ -67,8 +68,8 @@ pub struct Debug {
|
||||
impl Debug {
|
||||
pub const DEFAULT_MESSAGE_RETRIEVAL_LIMIT: i64 = 100;
|
||||
pub const DEFAULT_MINIMUM_MIX_PERFORMANCE: u8 = 50;
|
||||
pub const DEFAULT_MAXIMUM_AUTH_REQUEST_AGE: Duration = Duration::from_secs(30);
|
||||
const DEFAULT_MAXIMUM_OPEN_CONNECTIONS: usize = 8192;
|
||||
pub const DEFAULT_MAXIMUM_AUTH_REQUEST_TIMESTAMP_SKEW: Duration = Duration::from_secs(120);
|
||||
pub const DEFAULT_MAXIMUM_OPEN_CONNECTIONS: usize = 8192;
|
||||
}
|
||||
|
||||
impl Default for Debug {
|
||||
@@ -76,7 +77,7 @@ impl Default for Debug {
|
||||
Debug {
|
||||
message_retrieval_limit: Self::DEFAULT_MESSAGE_RETRIEVAL_LIMIT,
|
||||
maximum_open_connections: Self::DEFAULT_MAXIMUM_OPEN_CONNECTIONS,
|
||||
maximum_auth_request_age: Self::DEFAULT_MAXIMUM_AUTH_REQUEST_AGE,
|
||||
max_request_timestamp_skew: Self::DEFAULT_MAXIMUM_AUTH_REQUEST_TIMESTAMP_SKEW,
|
||||
minimum_mix_performance: Self::DEFAULT_MINIMUM_MIX_PERFORMANCE,
|
||||
stale_messages: Default::default(),
|
||||
client_bandwidth: Default::default(),
|
||||
|
||||
@@ -60,7 +60,7 @@ fn ephemeral_gateway_config(config: &Config) -> nym_gateway::config::Config {
|
||||
.zk_nym_tickets
|
||||
.maximum_time_between_redemption,
|
||||
},
|
||||
maximum_auth_request_age: config.gateway_tasks.debug.maximum_auth_request_age,
|
||||
max_request_timestamp_skew: config.gateway_tasks.debug.max_request_timestamp_skew,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
use crate::node::http::error::NymNodeHttpError;
|
||||
use crate::wireguard::error::WireguardError;
|
||||
use nym_http_api_client::HttpClientError;
|
||||
use nym_ip_packet_router::error::ClientCoreError;
|
||||
use nym_validator_client::ValidatorClientError;
|
||||
use std::io;
|
||||
@@ -209,3 +210,9 @@ pub enum ServiceProvidersError {
|
||||
#[error(transparent)]
|
||||
ExternalClientCore(#[from] ClientCoreError),
|
||||
}
|
||||
|
||||
impl From<HttpClientError> for NymNodeError {
|
||||
fn from(value: HttpClientError) -> Self {
|
||||
Self::HttpFailure(NymNodeHttpError::ClientError { source: value })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use nym_http_api_client::HttpClientError;
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
use thiserror::Error;
|
||||
@@ -24,4 +25,10 @@ pub enum NymNodeHttpError {
|
||||
#[from]
|
||||
source: nym_crypto::asymmetric::encryption::KeyRecoveryError,
|
||||
},
|
||||
|
||||
#[error("error building or using HTTP client: {source}")]
|
||||
ClientError {
|
||||
#[from]
|
||||
source: HttpClientError,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -814,9 +814,23 @@ impl NymNode {
|
||||
return;
|
||||
}
|
||||
|
||||
for nym_api in &self.config.mixnet.nym_api_urls {
|
||||
info!("trying {nym_api}...");
|
||||
let client = NymApiClient::new_with_user_agent(nym_api.clone(), self.user_agent());
|
||||
for nym_api_url in &self.config.mixnet.nym_api_urls {
|
||||
info!("trying {nym_api_url}...");
|
||||
|
||||
let nym_api =
|
||||
match nym_http_api_client::ClientBuilder::new_with_url(nym_api_url.clone())
|
||||
.no_hickory_dns()
|
||||
.with_user_agent(self.user_agent())
|
||||
.build::<&str>()
|
||||
{
|
||||
Ok(b) => b,
|
||||
Err(e) => {
|
||||
warn!("failed to build http client for \"{nym_api_url}\": {e}",);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let client = NymApiClient { nym_api };
|
||||
|
||||
// make new request every time in case previous one takes longer and invalidates the signature
|
||||
let request = NodeRefreshBody::new(self.ed25519_identity_keys.private_key());
|
||||
|
||||
@@ -9,6 +9,7 @@ use nym_node_metrics::prometheus_wrapper::{PrometheusMetric, PROMETHEUS_METRICS}
|
||||
use nym_task::ShutdownToken;
|
||||
use nym_topology::node::RoutingNode;
|
||||
use nym_topology::{EpochRewardedSet, NymTopology, Role, TopologyProvider};
|
||||
use nym_validator_client::nym_api::NymApiClientExt;
|
||||
use nym_validator_client::nym_nodes::{NodesByAddressesResponse, SkimmedNode};
|
||||
use nym_validator_client::{NymApiClient, ValidatorClientError};
|
||||
use std::collections::HashSet;
|
||||
@@ -167,6 +168,7 @@ impl NodesQuerier {
|
||||
) -> Result<NodesByAddressesResponse, ValidatorClientError> {
|
||||
let res = self
|
||||
.client
|
||||
.nym_api
|
||||
.nodes_by_addresses(ips)
|
||||
.await
|
||||
.inspect_err(|err| error!("failed to obtain node information: {err}"));
|
||||
@@ -174,7 +176,7 @@ impl NodesQuerier {
|
||||
if res.is_err() {
|
||||
self.use_next_nym_api()
|
||||
}
|
||||
res
|
||||
Ok(res?)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,9 +265,14 @@ impl NetworkRefresher {
|
||||
pending_check_interval: Duration,
|
||||
shutdown_token: ShutdownToken,
|
||||
) -> Result<Self, NymNodeError> {
|
||||
let nym_api = nym_http_api_client::Client::builder(nym_api_urls[0].clone())?
|
||||
.no_hickory_dns()
|
||||
.with_user_agent(user_agent)
|
||||
.build()?;
|
||||
|
||||
let mut this = NetworkRefresher {
|
||||
querier: NodesQuerier {
|
||||
client: NymApiClient::new_with_user_agent(nym_api_urls[0].clone(), user_agent),
|
||||
client: NymApiClient { nym_api },
|
||||
nym_api_urls,
|
||||
currently_used_api: 0,
|
||||
},
|
||||
|
||||
Generated
+49
-44
@@ -385,15 +385,15 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "bls12_381"
|
||||
version = "0.8.0"
|
||||
source = "git+https://github.com/jstuczyn/bls12_381?branch=temp/experimental-serdect#22cd0a16b674af1629110a2dc8b6cf6c73ea4cd9"
|
||||
source = "git+https://github.com/jstuczyn/bls12_381?branch=temp/experimental-serdect-updated#9bf520059cb28323fc51469cae86868ef4fa6fbd"
|
||||
dependencies = [
|
||||
"digest 0.9.0",
|
||||
"digest 0.10.7",
|
||||
"ff",
|
||||
"group",
|
||||
"pairing",
|
||||
"rand_core 0.6.4",
|
||||
"serde",
|
||||
"serdect 0.3.0-pre.0",
|
||||
"serdect 0.3.0",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
@@ -465,9 +465,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.5.0"
|
||||
version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
|
||||
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -540,11 +540,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.82"
|
||||
version = "1.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01"
|
||||
checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1487,9 +1487,9 @@ checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.32"
|
||||
version = "0.8.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394"
|
||||
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
@@ -1583,9 +1583,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ff"
|
||||
version = "0.13.0"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449"
|
||||
checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393"
|
||||
dependencies = [
|
||||
"rand_core 0.6.4",
|
||||
"subtle",
|
||||
@@ -3293,6 +3293,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"sha2 0.10.8",
|
||||
"tendermint 0.40.1",
|
||||
"tendermint-rpc",
|
||||
"thiserror 2.0.11",
|
||||
"time",
|
||||
"utoipa",
|
||||
@@ -3341,7 +3342,7 @@ dependencies = [
|
||||
"bls12_381",
|
||||
"bs58",
|
||||
"cfg-if",
|
||||
"digest 0.9.0",
|
||||
"digest 0.10.7",
|
||||
"ff",
|
||||
"group",
|
||||
"itertools 0.14.0",
|
||||
@@ -3349,7 +3350,7 @@ dependencies = [
|
||||
"nym-pemstore",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"sha2 0.9.9",
|
||||
"sha2 0.10.8",
|
||||
"subtle",
|
||||
"thiserror 2.0.11",
|
||||
"zeroize",
|
||||
@@ -3463,8 +3464,11 @@ name = "nym-http-api-client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"hickory-resolver",
|
||||
"http 1.1.0",
|
||||
"mime",
|
||||
"nym-bin-common",
|
||||
"once_cell",
|
||||
"reqwest 0.12.4",
|
||||
@@ -3554,6 +3558,7 @@ name = "nym-pemstore"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"pem",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4756,16 +4761,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.3"
|
||||
version = "0.17.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9babe80d5c16becf6594aa32ad2be8fe08498e7ae60b77de8df700e67f191d7e"
|
||||
checksum = "70ac5d832aa16abd7d1def883a8545280c20a60f523a370aa3a9617c2b8550ee"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"getrandom 0.2.10",
|
||||
"libc",
|
||||
"spin",
|
||||
"untrusted",
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4779,9 +4784,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rs_merkle"
|
||||
version = "1.4.2"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b241d2e59b74ef9e98d94c78c47623d04c8392abaf82014dfd372a16041128f"
|
||||
checksum = "bb09b49230ba22e8c676e7b75dfe2887dea8121f18b530ae0ba519ce442d2b21"
|
||||
dependencies = [
|
||||
"sha2 0.10.8",
|
||||
]
|
||||
@@ -5059,18 +5064,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.25"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03"
|
||||
checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.217"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
|
||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
@@ -5086,18 +5091,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_bytes"
|
||||
version = "0.11.16"
|
||||
version = "0.11.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "364fec0df39c49a083c9a8a18a23a6bcfd9af130fe9fe321d18520a0d113e09e"
|
||||
checksum = "8437fd221bde2d4ca316d61b90e337e9e702b3820b87d63caa9ba6c02bd06d96"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.217"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -5193,9 +5198,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serdect"
|
||||
version = "0.3.0-pre.0"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "791ef964bfaba6be28a5c3f0c56836e17cb711ac009ca1074b9c735a3ebf240a"
|
||||
checksum = "f42f67da2385b51a5f9652db9c93d78aeaf7610bf5ec366080b6de810604af53"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"serde",
|
||||
@@ -5266,6 +5271,12 @@ dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.1"
|
||||
@@ -5360,12 +5371,6 @@ dependencies = [
|
||||
"system-deps 5.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
version = "0.7.2"
|
||||
@@ -6052,9 +6057,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.37"
|
||||
version = "0.3.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
|
||||
checksum = "dad298b01a40a23aac4580b67e3dbedb7cc8402f3592d7f49469de2ea4aecdd8"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa 1.0.9",
|
||||
@@ -6069,15 +6074,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
||||
checksum = "765c97a5b985b7c11d7bc27fa927dc4fe6af3a6dfb021d28deb60d3bf51e76ef"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.19"
|
||||
version = "0.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
|
||||
checksum = "e8093bc3e81c3bc5f7879de09619d06c9a5a5e45ca44dfeeb7225bae38005c5c"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
@@ -6110,9 +6115,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.43.0"
|
||||
version = "1.44.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e"
|
||||
checksum = "9975ea0f48b5aa3972bf2d888c238182458437cc2a19374b81b25cdf1023fb3a"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user