Compare commits
154 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b7e3687757 | |||
| b9b969b7d3 | |||
| 47303e5b3b | |||
| 6b38ffd4f3 | |||
| 169c313404 | |||
| ccf430ea62 | |||
| cf13b79e93 | |||
| 134a0196f8 | |||
| 6c45c9f0b0 | |||
| b5afae0916 | |||
| 988eca857f | |||
| 3c05db2874 | |||
| a8e268f84a | |||
| ac22533ecd | |||
| bdc0b875a4 | |||
| d7b67c1408 | |||
| 606e29ebb0 | |||
| 21e3c1538d | |||
| 0fc7cc657d | |||
| 23a7f01c05 | |||
| 3a21cfa1ab | |||
| 1d2e6d916c | |||
| 4c2bf3642e | |||
| 70e2e32385 | |||
| 68a192daa3 | |||
| d6aacae14e | |||
| 6f00023d09 | |||
| 982ec56874 | |||
| 5dcc1ed6dc | |||
| d62bc0a10b | |||
| d1fb926a2a | |||
| dea69acd49 | |||
| ada2d2247a | |||
| 0159d7c27a | |||
| 882003c08c | |||
| b71a491872 | |||
| 8f48ae08c4 | |||
| 31b9623407 | |||
| 6d90ffdd2c | |||
| 28997c7f97 | |||
| 9550934d1f | |||
| a6c586a33b | |||
| 7c85c1a271 | |||
| 92c8d1b73f | |||
| 554e9ca490 | |||
| ff91d4619e | |||
| 9d01474277 | |||
| 8d10552d7c | |||
| 04fd197f5a | |||
| 4eadaf8292 | |||
| 32e39ebc6b | |||
| 117eb83a0b | |||
| c964c137f4 | |||
| 35b43d5b20 | |||
| bf88b34898 | |||
| 93140a1aa7 | |||
| f594bfc9ab | |||
| 4327e2945a | |||
| 6e6675f7bf | |||
| 8670693952 | |||
| a7f7ebfbae | |||
| 57c38ef222 | |||
| 1aec8be85e | |||
| 4b474dd8ff | |||
| 8e05386a0b | |||
| 13cfa55e6c | |||
| 18e628acde | |||
| b163dba2d4 | |||
| e67b2b020a | |||
| 9b627dd70f | |||
| 9a0b769425 | |||
| 8e14f5f884 | |||
| 1b64cb42b0 | |||
| 03c4895f2b | |||
| dcfb092758 | |||
| 9305ad5364 | |||
| ea5aef6c2f | |||
| 61a4433cd9 | |||
| 5c89d36140 | |||
| 5ab164d229 | |||
| 26538c5884 | |||
| a0daabab03 | |||
| b0a5b60945 | |||
| adb248dbcc | |||
| fffec65cab | |||
| bb24004d46 | |||
| c487eff7ca | |||
| 5fa21c9aae | |||
| fd18aae0d6 | |||
| c202e2d598 | |||
| 62d23cff9f | |||
| e454d71b78 | |||
| a7874add88 | |||
| 0a47d5dcf8 | |||
| 3d84be22e2 | |||
| 6ccbb30491 | |||
| 91c205f83a | |||
| 4a704e992a | |||
| 6c88c7df42 | |||
| 2a748fc968 | |||
| 25766dc0ec | |||
| 07544d939e | |||
| 102cd1033c | |||
| 676e93a372 | |||
| 5a6770e5e2 | |||
| 529e8d49ee | |||
| 01c7ea72dd | |||
| dfd1df5706 | |||
| 11d6ee2fdb | |||
| d704c428fc | |||
| bca070c1bd | |||
| a94c035c0a | |||
| 24480418f0 | |||
| 226c040a13 | |||
| a46245ffe3 | |||
| 7c1c13e139 | |||
| 836a93cd96 | |||
| 3d2914b3e5 | |||
| 9b02de3e75 | |||
| b47a742dd0 | |||
| 6e14882246 | |||
| f3d8aba82c | |||
| aa83501ed0 | |||
| a7466a0e02 | |||
| 78f45012db | |||
| f6a2f62ea9 | |||
| 3efeededc5 | |||
| c482350ec6 | |||
| 72a4a26c40 | |||
| 5d9b5a0d70 | |||
| c070e4bfee | |||
| 4d3d60b78e | |||
| 5f06414a12 | |||
| 656838811a | |||
| 7b8458630a | |||
| cf2ab08b4d | |||
| 41fb17a31b | |||
| 7d5e3ef7d3 | |||
| 4f283f565c | |||
| f7a7a8072f | |||
| acd068e5ab | |||
| 8d5a41a790 | |||
| caa17d933c | |||
| 039b05cf7e | |||
| 37b10b59aa | |||
| a9ede22bbd | |||
| b656003306 | |||
| b628a5f814 | |||
| 62045d76b3 | |||
| f8317f5a03 | |||
| c3ec970a37 | |||
| 5a573bc278 | |||
| 3d200db722 | |||
| e4139713cb |
@@ -0,0 +1 @@
|
||||
nym-validator-rewarder/.sqlx/** diff=nodiff
|
||||
@@ -31,3 +31,5 @@ updates:
|
||||
update-types:
|
||||
- "patch"
|
||||
open-pull-requests-limit: 10
|
||||
assignees:
|
||||
- "octol"
|
||||
|
||||
@@ -79,7 +79,6 @@ jobs:
|
||||
target/release/nym-socks5-client
|
||||
target/release/nym-api
|
||||
target/release/nym-network-requester
|
||||
target/release/nym-data-observatory
|
||||
target/release/nym-cli
|
||||
target/release/nymvisor
|
||||
target/release/nym-node
|
||||
@@ -97,7 +96,6 @@ jobs:
|
||||
cp target/release/nym-socks5-client $OUTPUT_DIR
|
||||
cp target/release/nym-api $OUTPUT_DIR
|
||||
cp target/release/nym-network-requester $OUTPUT_DIR
|
||||
cp target/release/nym-data-observatory $OUTPUT_DIR
|
||||
cp target/release/nymvisor $OUTPUT_DIR
|
||||
cp target/release/nym-node $OUTPUT_DIR
|
||||
cp target/release/nym-cli $OUTPUT_DIR
|
||||
|
||||
@@ -8,16 +8,18 @@ on:
|
||||
- 'explorer-api/**'
|
||||
- 'gateway/**'
|
||||
- 'integrations/**'
|
||||
- 'mixnode/**'
|
||||
- 'sdk/rust/**'
|
||||
- 'sdk/lib/**'
|
||||
- 'service-providers/**'
|
||||
- 'nym-network-monitor/**'
|
||||
- 'nym-api/**'
|
||||
- 'nym-credential-proxy/**'
|
||||
- 'nym-network-monitor/**'
|
||||
- 'nym-node/**'
|
||||
- 'nym-node-status-api/**'
|
||||
- 'nym-outfox/**'
|
||||
- 'nym-data-observatory/**'
|
||||
- 'nym-validator-rewarder/**'
|
||||
- 'nyx-chain-watcher/**'
|
||||
- 'sdk/ffi/**'
|
||||
- 'sdk/rust/**'
|
||||
- 'service-providers/**'
|
||||
- 'nym-browser-extension/storage/**'
|
||||
- 'tools/**'
|
||||
- 'wasm/**'
|
||||
- 'Cargo.toml'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
name: ci-sdk-wasm
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'wasm/**'
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
git config --global user.name "Lawrence Stalder"
|
||||
|
||||
- name: Get version from cargo.toml
|
||||
uses: mikefarah/yq@v4.44.6
|
||||
uses: mikefarah/yq@v4.45.1
|
||||
id: get_version
|
||||
with:
|
||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-credential-proxy/Cargo.toml
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
git config --global user.name "Lawrence Stalder"
|
||||
|
||||
- name: Get version from cargo.toml
|
||||
uses: mikefarah/yq@v4.44.6
|
||||
uses: mikefarah/yq@v4.45.1
|
||||
id: get_version
|
||||
with:
|
||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
git config --global user.name "Lawrence Stalder"
|
||||
|
||||
- name: Get version from cargo.toml
|
||||
uses: mikefarah/yq@v4.44.6
|
||||
uses: mikefarah/yq@v4.45.1
|
||||
id: get_version
|
||||
with:
|
||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-network-monitor/Cargo.toml
|
||||
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
git config --global user.name "Lawrence Stalder"
|
||||
|
||||
- name: Get version from cargo.toml
|
||||
uses: mikefarah/yq@v4.44.6
|
||||
uses: mikefarah/yq@v4.45.1
|
||||
id: get_version
|
||||
with:
|
||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
git config --global user.name "Lawrence Stalder"
|
||||
|
||||
- name: Get version from cargo.toml
|
||||
uses: mikefarah/yq@v4.44.6
|
||||
uses: mikefarah/yq@v4.45.1
|
||||
id: get_version
|
||||
with:
|
||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
git config --global user.name "Lawrence Stalder"
|
||||
|
||||
- name: Get version from cargo.toml
|
||||
uses: mikefarah/yq@v4.44.6
|
||||
uses: mikefarah/yq@v4.45.1
|
||||
id: get_version
|
||||
with:
|
||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
git config --global user.name "Lawrence Stalder"
|
||||
|
||||
- name: Get version from cargo.toml
|
||||
uses: mikefarah/yq@v4.44.6
|
||||
uses: mikefarah/yq@v4.45.1
|
||||
id: get_version
|
||||
with:
|
||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
git config --global user.name "Lawrence Stalder"
|
||||
|
||||
- name: Get version from cargo.toml
|
||||
uses: mikefarah/yq@v4.44.6
|
||||
uses: mikefarah/yq@v4.45.1
|
||||
id: get_version
|
||||
with:
|
||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
||||
|
||||
+2
-1
@@ -54,7 +54,8 @@ nym-network-monitor/__pycache__
|
||||
nym-network-monitor/*.key
|
||||
nym-network-monitor/.envrc
|
||||
nym-network-monitor/.envrc
|
||||
nym-api/redocly/formatted-openapi.json
|
||||
|
||||
|
||||
*.sqlite
|
||||
.build
|
||||
.build
|
||||
|
||||
+165
@@ -4,6 +4,171 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [2025.2-hu] (2025-02-04)
|
||||
|
||||
- Feature/remove double spending bloomfilter ([#5417])
|
||||
- HU - Downgrade harmless log message from info to debug ([#5405])
|
||||
- lower default ticket verification quorum to 0.7 ([#5404])
|
||||
- Downgrade harmless log message from info to debug ([#5403])
|
||||
- Redirect from mixnode page to nodes page ([#5397])
|
||||
- chore :update version of chain watcher and validator rewarder ([#5394])
|
||||
- bugfix: correctly handle ingore epoch roles flag ([#5390])
|
||||
- bugfix: terminate mixnet socket listener on shutdown ([#5389])
|
||||
- feat: make client ignore dual mode nodes by default ([#5388])
|
||||
- Handle ecash network errors differently ([#5378])
|
||||
- Remove empty ephemeral keys ([#5376])
|
||||
- fixed sql migration for adding default message timestamp ([#5374])
|
||||
- Bind to [::] on nym-node for both IP versions ([#5361])
|
||||
- exposed NymApiClient method for obtaining node performance history ([#5360])
|
||||
- Client gateway selection ([#5358])
|
||||
- chore: refresh wasm sdk ([#5353])
|
||||
- chore: update indexed_db_futures ([#5347])
|
||||
- build(deps): bump mikefarah/yq from 4.44.6 to 4.45.1 ([#5342])
|
||||
- updated cosmrs and tendermint-rpc to their most recent versions ([#5339])
|
||||
- build(deps): bump ts-rs from 10.0.0 to 10.1.0 ([#5338])
|
||||
- build(deps): bump tempfile from 3.14.0 to 3.15.0 ([#5337])
|
||||
- build(deps): bump the patch-updates group with 8 updates ([#5336])
|
||||
- feature: introduce /load endpoint for self-reported quantised NymNode load ([#5326])
|
||||
- feature: `CancellationToken`-based shutdowns ([#5325])
|
||||
- Use expect in geodata test to give error message on failure ([#5314])
|
||||
- feature: periodically remove stale gateway messages ([#5312])
|
||||
- build(deps): bump the patch-updates group across 1 directory with 35 updates ([#5310])
|
||||
- Add dependabot assignes for the root cargo ecosystem ([#5297])
|
||||
- Move tun constants to network defaults ([#5286])
|
||||
- Include IPINFO_API_TOKEN in nightly CI ([#5285])
|
||||
- Nyx Chain Watcher ([#5274])
|
||||
- bugfix: remove unnecessary arguments for nym-api swagger endpoints ([#5272])
|
||||
- feature: nym topology revamp ([#5271])
|
||||
- Add windows to CI builds ([#5269])
|
||||
- http-api-client: deduplicate code ([#5267])
|
||||
- build(deps): bump http from 1.1.0 to 1.2.0 ([#5228])
|
||||
- NS API: add mixnet scraper ([#5200])
|
||||
- build(deps): bump criterion from 0.4.0 to 0.5.1 ([#4911])
|
||||
|
||||
[#5417]: https://github.com/nymtech/nym/pull/5417
|
||||
[#5405]: https://github.com/nymtech/nym/pull/5405
|
||||
[#5404]: https://github.com/nymtech/nym/pull/5404
|
||||
[#5403]: https://github.com/nymtech/nym/pull/5403
|
||||
[#5397]: https://github.com/nymtech/nym/pull/5397
|
||||
[#5394]: https://github.com/nymtech/nym/pull/5394
|
||||
[#5390]: https://github.com/nymtech/nym/pull/5390
|
||||
[#5389]: https://github.com/nymtech/nym/pull/5389
|
||||
[#5388]: https://github.com/nymtech/nym/pull/5388
|
||||
[#5378]: https://github.com/nymtech/nym/pull/5378
|
||||
[#5376]: https://github.com/nymtech/nym/pull/5376
|
||||
[#5374]: https://github.com/nymtech/nym/pull/5374
|
||||
[#5361]: https://github.com/nymtech/nym/pull/5361
|
||||
[#5360]: https://github.com/nymtech/nym/pull/5360
|
||||
[#5358]: https://github.com/nymtech/nym/pull/5358
|
||||
[#5353]: https://github.com/nymtech/nym/pull/5353
|
||||
[#5347]: https://github.com/nymtech/nym/pull/5347
|
||||
[#5342]: https://github.com/nymtech/nym/pull/5342
|
||||
[#5339]: https://github.com/nymtech/nym/pull/5339
|
||||
[#5338]: https://github.com/nymtech/nym/pull/5338
|
||||
[#5337]: https://github.com/nymtech/nym/pull/5337
|
||||
[#5336]: https://github.com/nymtech/nym/pull/5336
|
||||
[#5326]: https://github.com/nymtech/nym/pull/5326
|
||||
[#5325]: https://github.com/nymtech/nym/pull/5325
|
||||
[#5314]: https://github.com/nymtech/nym/pull/5314
|
||||
[#5312]: https://github.com/nymtech/nym/pull/5312
|
||||
[#5310]: https://github.com/nymtech/nym/pull/5310
|
||||
[#5297]: https://github.com/nymtech/nym/pull/5297
|
||||
[#5286]: https://github.com/nymtech/nym/pull/5286
|
||||
[#5285]: https://github.com/nymtech/nym/pull/5285
|
||||
[#5274]: https://github.com/nymtech/nym/pull/5274
|
||||
[#5272]: https://github.com/nymtech/nym/pull/5272
|
||||
[#5271]: https://github.com/nymtech/nym/pull/5271
|
||||
[#5269]: https://github.com/nymtech/nym/pull/5269
|
||||
[#5267]: https://github.com/nymtech/nym/pull/5267
|
||||
[#5228]: https://github.com/nymtech/nym/pull/5228
|
||||
[#5200]: https://github.com/nymtech/nym/pull/5200
|
||||
[#4911]: https://github.com/nymtech/nym/pull/4911
|
||||
|
||||
## [2025.1-reeses] (2025-01-15)
|
||||
|
||||
- Feture/legacy alert ([#5346])
|
||||
- chore: readjusted --mode behaviour to fix the regression ([#5331])
|
||||
- chore: apply 1.84 linter suggestions ([#5330])
|
||||
- bugfix: make sure refresh data key matches bond info ([#5329])
|
||||
- reduce log severity for number of packets being delayed ([#5321])
|
||||
- feat: warn users if node is run in exit mode only ([#5320])
|
||||
- Bugfix/contract version assignment ([#5318])
|
||||
- fixed client session histogram buckets ([#5316])
|
||||
- amend 250gb limit ([#5313])
|
||||
- feature: expand nym-node prometheus metrics ([#5298])
|
||||
- Cherry picked #5286 ([#5287])
|
||||
- Add close to credential storage ([#5283])
|
||||
- feature: wireguard metrics ([#5278])
|
||||
- Add PATCH support to nym-http-api-client ([#5260])
|
||||
- chore: removed legacy socks5 listener ([#5259])
|
||||
- bugfix: make sure to apply gateway score filtering when choosing initial node ([#5256])
|
||||
- Update TS bindings ([#5255])
|
||||
- Add conversion unit tests for auth msg ([#5251])
|
||||
- Add control messages to GatewayTransciver ([#5247])
|
||||
- Remove unneeded async function annotation ([#5246])
|
||||
- bugfix: make sure to update timestamp of last batch verification to prevent double redemption ([#5239])
|
||||
- Add FromStr impl for UserAgent ([#5236])
|
||||
- Extend swagger docs ([#5235])
|
||||
- TicketType derive Hash and Eq ([#5233])
|
||||
- Add fd callback to client core ([#5230])
|
||||
- Extend raw ws fd for gateway client ([#5218])
|
||||
- Shipping raw metrics to PG ([#5216])
|
||||
- Change sqlite journal mode to WAL ([#5213])
|
||||
- Derive serialize for UserAgent ([#5210])
|
||||
- Restore Location fields ([#5208])
|
||||
- better date serialization ([#5207])
|
||||
- Fix overflow ([#5204])
|
||||
- feature: hopefully final steps of the smoosh™️ ([#5201])
|
||||
- Fix overflow ([#5184])
|
||||
- NS API - Gateway stats scraping ([#5180])
|
||||
- introduced initial internal commands for nym-cli: ecash key and request generation ([#5174])
|
||||
- Move NS client to separate package under NS API ([#5171])
|
||||
- build(deps): bump micromatch from 4.0.4 to 4.0.8 in /testnet-faucet ([#4813])
|
||||
|
||||
[#5346]: https://github.com/nymtech/nym/pull/5346
|
||||
[#5331]: https://github.com/nymtech/nym/pull/5331
|
||||
[#5330]: https://github.com/nymtech/nym/pull/5330
|
||||
[#5329]: https://github.com/nymtech/nym/pull/5329
|
||||
[#5321]: https://github.com/nymtech/nym/pull/5321
|
||||
[#5320]: https://github.com/nymtech/nym/pull/5320
|
||||
[#5318]: https://github.com/nymtech/nym/pull/5318
|
||||
[#5316]: https://github.com/nymtech/nym/pull/5316
|
||||
[#5313]: https://github.com/nymtech/nym/pull/5313
|
||||
[#5298]: https://github.com/nymtech/nym/pull/5298
|
||||
[#5287]: https://github.com/nymtech/nym/pull/5287
|
||||
[#5283]: https://github.com/nymtech/nym/pull/5283
|
||||
[#5278]: https://github.com/nymtech/nym/pull/5278
|
||||
[#5260]: https://github.com/nymtech/nym/pull/5260
|
||||
[#5259]: https://github.com/nymtech/nym/pull/5259
|
||||
[#5256]: https://github.com/nymtech/nym/pull/5256
|
||||
[#5255]: https://github.com/nymtech/nym/pull/5255
|
||||
[#5251]: https://github.com/nymtech/nym/pull/5251
|
||||
[#5247]: https://github.com/nymtech/nym/pull/5247
|
||||
[#5246]: https://github.com/nymtech/nym/pull/5246
|
||||
[#5239]: https://github.com/nymtech/nym/pull/5239
|
||||
[#5236]: https://github.com/nymtech/nym/pull/5236
|
||||
[#5235]: https://github.com/nymtech/nym/pull/5235
|
||||
[#5233]: https://github.com/nymtech/nym/pull/5233
|
||||
[#5230]: https://github.com/nymtech/nym/pull/5230
|
||||
[#5218]: https://github.com/nymtech/nym/pull/5218
|
||||
[#5216]: https://github.com/nymtech/nym/pull/5216
|
||||
[#5213]: https://github.com/nymtech/nym/pull/5213
|
||||
[#5210]: https://github.com/nymtech/nym/pull/5210
|
||||
[#5208]: https://github.com/nymtech/nym/pull/5208
|
||||
[#5207]: https://github.com/nymtech/nym/pull/5207
|
||||
[#5204]: https://github.com/nymtech/nym/pull/5204
|
||||
[#5201]: https://github.com/nymtech/nym/pull/5201
|
||||
[#5184]: https://github.com/nymtech/nym/pull/5184
|
||||
[#5180]: https://github.com/nymtech/nym/pull/5180
|
||||
[#5174]: https://github.com/nymtech/nym/pull/5174
|
||||
[#5171]: https://github.com/nymtech/nym/pull/5171
|
||||
[#4813]: https://github.com/nymtech/nym/pull/4813
|
||||
|
||||
## [2024.14-crunch-patched] (2024-12-17)
|
||||
|
||||
- Fixes an issue to allow previously registred clients to connect to latest nym-nodes
|
||||
- Fixes compatibility issues between nym-nodes and older clients
|
||||
|
||||
## [2024.14-crunch] (2024-12-11)
|
||||
|
||||
- Merge/release/2024.14-crunch ([#5242])
|
||||
|
||||
Generated
+1569
-818
File diff suppressed because it is too large
Load Diff
+75
-80
@@ -48,13 +48,12 @@ members = [
|
||||
"common/credentials-interface",
|
||||
"common/crypto",
|
||||
"common/dkg",
|
||||
"common/ecash-double-spending",
|
||||
"common/ecash-time",
|
||||
"common/execute",
|
||||
"common/exit-policy",
|
||||
"common/gateway-requests",
|
||||
"common/gateway-storage",
|
||||
"common/gateway-stats-storage",
|
||||
"common/gateway-storage",
|
||||
"common/http-api-client",
|
||||
"common/http-api-common",
|
||||
"common/inclusion-probability",
|
||||
@@ -93,6 +92,7 @@ members = [
|
||||
"common/topology",
|
||||
"common/tun",
|
||||
"common/types",
|
||||
"common/verloc",
|
||||
"common/wasm/client-core",
|
||||
"common/wasm/storage",
|
||||
"common/wasm/utils",
|
||||
@@ -104,6 +104,22 @@ members = [
|
||||
"explorer-api/explorer-client",
|
||||
"gateway",
|
||||
"integrations/bity",
|
||||
"nym-api",
|
||||
"nym-api/nym-api-requests",
|
||||
"nym-browser-extension/storage",
|
||||
"nym-credential-proxy/nym-credential-proxy",
|
||||
"nym-credential-proxy/nym-credential-proxy-requests",
|
||||
"nym-credential-proxy/vpn-api-lib-wasm",
|
||||
"nym-network-monitor",
|
||||
"nym-node",
|
||||
"nym-node-status-api/nym-node-status-agent",
|
||||
"nym-node-status-api/nym-node-status-api",
|
||||
"nym-node-status-api/nym-node-status-client",
|
||||
"nym-node/nym-node-metrics",
|
||||
"nym-node/nym-node-requests",
|
||||
"nym-outfox",
|
||||
"nym-validator-rewarder",
|
||||
"nyx-chain-watcher",
|
||||
"sdk/ffi/cpp",
|
||||
"sdk/ffi/go",
|
||||
"sdk/ffi/shared",
|
||||
@@ -112,26 +128,16 @@ members = [
|
||||
"service-providers/common",
|
||||
"service-providers/ip-packet-router",
|
||||
"service-providers/network-requester",
|
||||
"nym-api",
|
||||
"nym-api/nym-api-requests",
|
||||
"nym-browser-extension/storage",
|
||||
"nym-credential-proxy/nym-credential-proxy",
|
||||
"nym-credential-proxy/nym-credential-proxy-requests",
|
||||
"nym-credential-proxy/vpn-api-lib-wasm",
|
||||
"nym-network-monitor",
|
||||
"nyx-chain-watcher",
|
||||
"nym-node",
|
||||
"nym-node/nym-node-requests",
|
||||
"nym-node/nym-node-metrics",
|
||||
"nym-node-status-api/nym-node-status-agent",
|
||||
"nym-node-status-api/nym-node-status-api",
|
||||
"nym-node-status-api/nym-node-status-client",
|
||||
"nym-outfox",
|
||||
"nym-validator-rewarder",
|
||||
"tools/echo-server",
|
||||
"tools/internal/ssl-inject",
|
||||
"tools/echo-server",
|
||||
"tools/internal/contract-state-importer/importer-cli",
|
||||
"tools/internal/contract-state-importer/importer-contract",
|
||||
"tools/internal/mixnet-connectivity-check",
|
||||
# "tools/internal/sdk-version-bump",
|
||||
"tools/internal/ssl-inject",
|
||||
"tools/internal/testnet-manager",
|
||||
"tools/internal/testnet-manager",
|
||||
"tools/internal/testnet-manager/dkg-bypass-contract",
|
||||
"tools/internal/testnet-manager/dkg-bypass-contract",
|
||||
"tools/nym-cli",
|
||||
"tools/nym-id-cli",
|
||||
@@ -143,13 +149,6 @@ members = [
|
||||
"wasm/mix-fetch",
|
||||
"wasm/node-tester",
|
||||
"wasm/zknym-lib",
|
||||
"tools/echo-server",
|
||||
"tools/internal/contract-state-importer/importer-cli",
|
||||
"tools/internal/contract-state-importer/importer-contract",
|
||||
"tools/internal/testnet-manager",
|
||||
"tools/internal/testnet-manager/dkg-bypass-contract",
|
||||
"common/verloc",
|
||||
"tools/internal/mixnet-connectivity-check",
|
||||
]
|
||||
|
||||
default-members = [
|
||||
@@ -173,7 +172,6 @@ exclude = [
|
||||
"explorer",
|
||||
"contracts",
|
||||
"nym-wallet",
|
||||
"nym-vpn/ui/src-tauri",
|
||||
"cpu-cycles",
|
||||
]
|
||||
|
||||
@@ -193,9 +191,10 @@ aes = "0.8.1"
|
||||
aes-gcm = "0.10.1"
|
||||
aes-gcm-siv = "0.11.1"
|
||||
aead = "0.5.2"
|
||||
anyhow = "1.0.90"
|
||||
anyhow = "1.0.95"
|
||||
arc-swap = "1.7.1"
|
||||
argon2 = "0.5.0"
|
||||
async-trait = "0.1.83"
|
||||
async-trait = "0.1.86"
|
||||
axum-client-ip = "0.6.1"
|
||||
axum = "0.7.5"
|
||||
axum-extra = "0.9.4"
|
||||
@@ -204,7 +203,7 @@ bincode = "1.3.3"
|
||||
bip39 = { version = "2.0.0", features = ["zeroize"] }
|
||||
bit-vec = "0.7.0" # can we unify those?
|
||||
bitvec = "1.0.0"
|
||||
blake3 = "1.5.4"
|
||||
blake3 = "1.5.5"
|
||||
bloomfilter = "1.0.14"
|
||||
bs58 = "0.5.1"
|
||||
bytecodec = "0.4.15"
|
||||
@@ -214,20 +213,20 @@ celes = "2.4.0"
|
||||
cfg-if = "1.0.0"
|
||||
chacha20 = "0.9.0"
|
||||
chacha20poly1305 = "0.10.1"
|
||||
chrono = "0.4.31"
|
||||
chrono = "0.4.39"
|
||||
cipher = "0.4.3"
|
||||
clap = "4.5.20"
|
||||
clap = "4.5.28"
|
||||
clap_complete = "4.5"
|
||||
clap_complete_fig = "4.5"
|
||||
colored = "2.0"
|
||||
comfy-table = "7.1.1"
|
||||
console = "0.15.8"
|
||||
comfy-table = "7.1.4"
|
||||
console = "0.15.10"
|
||||
console-subscriber = "0.1.1"
|
||||
console_error_panic_hook = "0.1"
|
||||
const-str = "0.5.6"
|
||||
const_format = "0.2.33"
|
||||
criterion = "0.4"
|
||||
csv = "1.3.0"
|
||||
const_format = "0.2.34"
|
||||
criterion = "0.5"
|
||||
csv = "1.3.1"
|
||||
ctr = "0.9.1"
|
||||
cupid = "0.6.1"
|
||||
curve25519-dalek = "4.1"
|
||||
@@ -244,16 +243,17 @@ etherparse = "0.13.0"
|
||||
envy = "0.4"
|
||||
eyre = "0.6.9"
|
||||
fastrand = "2.1.1"
|
||||
flate2 = "1.0.34"
|
||||
futures = "0.3.28"
|
||||
flate2 = "1.0.35"
|
||||
futures = "0.3.31"
|
||||
futures-util = "0.3"
|
||||
generic-array = "0.14.7"
|
||||
getrandom = "0.2.10"
|
||||
getset = "0.1.3"
|
||||
getset = "0.1.4"
|
||||
handlebars = "3.5.5"
|
||||
headers = "0.4.0"
|
||||
hex = "0.4.3"
|
||||
hex-literal = "0.3.3"
|
||||
hickory-resolver = "0.24.3"
|
||||
hkdf = "0.12.3"
|
||||
hmac = "0.12.1"
|
||||
http = "1"
|
||||
@@ -264,7 +264,7 @@ humantime-serde = "1.1.1"
|
||||
human-repr = "1.1.0"
|
||||
hyper = "1.4.1"
|
||||
hyper-util = "0.1"
|
||||
indicatif = "0.17.8"
|
||||
indicatif = "0.17.11"
|
||||
inquire = "0.6.2"
|
||||
ip_network = "0.4.1"
|
||||
ipnetwork = "0.20"
|
||||
@@ -282,14 +282,14 @@ moka = { version = "0.12", features = ["future"] }
|
||||
nix = "0.27.1"
|
||||
notify = "5.1.0"
|
||||
okapi = "0.7.0"
|
||||
once_cell = "1.20.2"
|
||||
once_cell = "1.20.3"
|
||||
opentelemetry = "0.19.0"
|
||||
opentelemetry-jaeger = "0.18.0"
|
||||
parking_lot = "0.12.3"
|
||||
pem = "0.8"
|
||||
petgraph = "0.6.5"
|
||||
pin-project = "1.1"
|
||||
pin-project-lite = "0.2.14"
|
||||
pin-project-lite = "0.2.16"
|
||||
pretty_env_logger = "0.4.0"
|
||||
publicsuffix = "2.2.3"
|
||||
quote = "1"
|
||||
@@ -307,12 +307,12 @@ rocket_cors = "0.6.0"
|
||||
rocket_okapi = "0.8.0"
|
||||
safer-ffi = "0.1.13"
|
||||
schemars = "0.8.21"
|
||||
semver = "1.0.23"
|
||||
serde = "1.0.211"
|
||||
semver = "1.0.25"
|
||||
serde = "1.0.217"
|
||||
serde_bytes = "0.11.15"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0.132"
|
||||
serde_json_path = "0.7.1"
|
||||
serde_json = "1.0.138"
|
||||
serde_json_path = "0.7.2"
|
||||
serde_repr = "0.1"
|
||||
serde_with = "3.9.0"
|
||||
serde_yaml = "0.9.25"
|
||||
@@ -324,36 +324,36 @@ strum = "0.26"
|
||||
strum_macros = "0.26"
|
||||
subtle-encoding = "0.5"
|
||||
syn = "1"
|
||||
sysinfo = "0.30.13"
|
||||
sysinfo = "0.33.0"
|
||||
tap = "1.0.1"
|
||||
tar = "0.4.42"
|
||||
tempfile = "3.14"
|
||||
thiserror = "1.0.64"
|
||||
time = "0.3.30"
|
||||
tokio = "1.39"
|
||||
tokio-stream = "0.1.16"
|
||||
tar = "0.4.43"
|
||||
tempfile = "3.15"
|
||||
thiserror = "2.0"
|
||||
time = "0.3.37"
|
||||
tokio = "1.43"
|
||||
tokio-stream = "0.1.17"
|
||||
tokio-test = "0.4.4"
|
||||
tokio-tun = "0.11.5"
|
||||
tokio-tungstenite = { version = "0.20.1" }
|
||||
tokio-util = "0.7.12"
|
||||
toml = "0.8.14"
|
||||
tokio-util = "0.7.13"
|
||||
toml = "0.8.20"
|
||||
tower = "0.4.13"
|
||||
tower-http = "0.5.2"
|
||||
tracing = "0.1.37"
|
||||
tracing = "0.1.41"
|
||||
tracing-opentelemetry = "0.19.0"
|
||||
tracing-subscriber = "0.3.16"
|
||||
tracing-subscriber = "0.3.19"
|
||||
tracing-tree = "0.2.2"
|
||||
tracing-log = "0.2"
|
||||
ts-rs = "10.0.0"
|
||||
ts-rs = "10.1.0"
|
||||
tungstenite = { version = "0.20.1", default-features = false }
|
||||
url = "2.5"
|
||||
utoipa = "4.2"
|
||||
utoipa-swagger-ui = "7.1"
|
||||
utoipauto = "0.1"
|
||||
utoipa = "5.2"
|
||||
utoipa-swagger-ui = "8.0"
|
||||
utoipauto = "0.2"
|
||||
uuid = "*"
|
||||
vergen = { version = "=8.3.1", default-features = false }
|
||||
walkdir = "2"
|
||||
wasm-bindgen-test = "0.3.43"
|
||||
wasm-bindgen-test = "0.3.49"
|
||||
x25519-dalek = "2.0.0"
|
||||
zeroize = "1.6.0"
|
||||
|
||||
@@ -384,31 +384,26 @@ cw4 = { version = "=1.1.2" }
|
||||
cw-controllers = { version = "=1.1.0" }
|
||||
|
||||
# cosmrs-related
|
||||
bip32 = { version = "0.5.2", default-features = false }
|
||||
bip32 = { version = "0.5.3", default-features = false }
|
||||
|
||||
# temporarily using a fork again (yay.) because we need staking and slashing support (which are already on main but not released)
|
||||
# plus response message parsing (which is, as of the time of writing this message, waiting to get merged)
|
||||
#cosmrs = { path = "../cosmos-rust-fork/cosmos-rust/cosmrs" }
|
||||
cosmrs = { git = "https://github.com/cosmos/cosmos-rust", rev = "4b1332e6d8258ac845cef71589c8d362a669675a" } # unfortuntely we need a fork by yours truly to get the staking support
|
||||
tendermint = "0.37.0" # same version as used by cosmrs
|
||||
tendermint-rpc = "0.37.0" # same version as used by cosmrs
|
||||
prost = { version = "0.12", default-features = false }
|
||||
|
||||
cosmrs = { version = "0.21.1" }
|
||||
tendermint = "0.40.0"
|
||||
tendermint-rpc = "0.40.0"
|
||||
prost = { version = "0.13", default-features = false }
|
||||
|
||||
# wasm-related dependencies
|
||||
gloo-utils = "0.2.0"
|
||||
gloo-net = "0.5.0"
|
||||
gloo-net = "0.6.0"
|
||||
|
||||
# use a separate branch due to feature unification failures
|
||||
# this is blocked until the upstream removes outdates `wasm_bindgen` feature usage
|
||||
# indexed_db_futures = "0.4.1"
|
||||
indexed_db_futures = { git = "https://github.com/TiemenSch/rust-indexed-db", branch = "update-uuid" }
|
||||
js-sys = "0.3.70"
|
||||
indexed_db_futures = "0.6.0"
|
||||
js-sys = "0.3.76"
|
||||
serde-wasm-bindgen = "0.6.5"
|
||||
tsify = "0.4.5"
|
||||
wasm-bindgen = "0.2.99"
|
||||
wasm-bindgen-futures = "0.4.45"
|
||||
wasmtimer = "0.2.0"
|
||||
web-sys = "0.3.72"
|
||||
wasm-bindgen-futures = "0.4.49"
|
||||
wasmtimer = "0.4.1"
|
||||
web-sys = "0.3.76"
|
||||
|
||||
# Profile settings for individual crates
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
@@ -13,8 +13,8 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
|
||||
* `nym-client` - an executable which you can build into your own applications. Use it for interacting with Nym nodes.
|
||||
* `nym-socks5-client` - a Socks5 proxy you can run on your machine and use with existing applications.
|
||||
* `nym-explorer` - a (projected) block explorer and (existing) mixnet viewer.
|
||||
* `nym-wallet` - a desktop wallet implemented using the [Tauri](https://tauri.studio/en/docs/about/intro) framework.
|
||||
* `nym-cli` - a tool for interacting with the network from the CLI.
|
||||
* `nym-wallet` - a desktop wallet implemented using the [Tauri](https://tauri.app)) framework.
|
||||
* `nym-cli` - a tool for interacting with the network from the CLI.
|
||||
<!-- coming soon
|
||||
* `nym-network-monitor` - sends packets through the full system to check that they are working as expected, and stores node uptime histories as the basis of a rewards system ("mixmining" or "proof-of-mixing").
|
||||
-->
|
||||
@@ -42,10 +42,10 @@ client ───► Gateway ──┘ mix │ mix ┌─►mix ───►
|
||||
|
||||
References for developers:
|
||||
|
||||
* [Dev Docs](https://nymtech.net/docs/developers)
|
||||
* [SDKs](https://nymtech.net/docs/developers/rust)
|
||||
* [Network Docs](https://nymtech.net/docs/network)
|
||||
* [Release Cycle - git flow](https://nymtech.net/docs/operators/release-cycle)
|
||||
* [Dev Docs](https://nym.com/docs/developers)
|
||||
* [SDKs](https://nym.com/docs/developers/rust)
|
||||
* [Network Docs](https://nym.com/docs/network)
|
||||
* [Release Cycle - git flow](https://nym.com/docs/operators/release-cycle)
|
||||
|
||||
### Developer chat
|
||||
|
||||
@@ -66,4 +66,4 @@ As a general approach, licensing is as follows this pattern:
|
||||
- libraries and components are Apache 2.0 or MIT
|
||||
- documentation is Apache 2.0 or CC0-1.0
|
||||
|
||||
Nym Node Operators and Validators Temrs and Conditions can be found [here](https://nymtech.net/terms-and-conditions/operators/v1.0.0).
|
||||
Nym Node Operators and Validators Terms and Conditions can be found [here](https://nym.com/operators-validators-terms).
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.45"
|
||||
version = "1.1.47"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
|
||||
@@ -56,7 +56,7 @@ pub fn default_data_directory<P: AsRef<Path>>(id: P) -> PathBuf {
|
||||
.join(DEFAULT_DATA_DIR)
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize, Clone)]
|
||||
pub struct Config {
|
||||
#[serde(flatten)]
|
||||
pub base: BaseClientConfig,
|
||||
@@ -94,6 +94,10 @@ impl CliClientConfig for Config {
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn base(&self) -> BaseClientConfig {
|
||||
self.base.clone()
|
||||
}
|
||||
|
||||
pub fn new<S: AsRef<str>>(id: S) -> Self {
|
||||
Config {
|
||||
base: BaseClientConfig::new(id.as_ref(), env!("CARGO_PKG_VERSION")),
|
||||
@@ -209,7 +213,7 @@ impl SocketType {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct Socket {
|
||||
pub socket_type: SocketType,
|
||||
|
||||
@@ -107,5 +107,8 @@ enabled = {{ debug.stats_reporting.enabled }}
|
||||
provider_address = '{{ debug.stats_reporting.provider_address }}'
|
||||
reporting_interval = '{{ debug.stats_reporting.reporting_interval }}'
|
||||
|
||||
[debug.forget_me]
|
||||
client = {{ debug.forget_me.client }}
|
||||
stats = {{ debug.forget_me.stats }}
|
||||
|
||||
"#;
|
||||
|
||||
@@ -20,7 +20,7 @@ pub use nym_sphinx::addressing::clients::Recipient;
|
||||
|
||||
pub mod config;
|
||||
|
||||
type NativeClientBuilder<'a> = BaseClientBuilder<'a, QueryHttpRpcNyxdClient, OnDiskPersistent>;
|
||||
type NativeClientBuilder = BaseClientBuilder<QueryHttpRpcNyxdClient, OnDiskPersistent>;
|
||||
|
||||
pub struct SocketClient {
|
||||
/// Client configuration options, including, among other things, packet sending rates,
|
||||
@@ -32,6 +32,10 @@ pub struct SocketClient {
|
||||
}
|
||||
|
||||
impl SocketClient {
|
||||
pub fn config(&self) -> Config {
|
||||
self.config.clone()
|
||||
}
|
||||
|
||||
pub fn new(config: Config, custom_mixnet: Option<PathBuf>) -> Self {
|
||||
SocketClient {
|
||||
config,
|
||||
@@ -108,8 +112,9 @@ impl SocketClient {
|
||||
let storage = self.initialise_storage().await?;
|
||||
let user_agent = nym_bin_common::bin_info!().into();
|
||||
|
||||
let mut base_client = BaseClientBuilder::new(&self.config.base, storage, dkg_query_client)
|
||||
.with_user_agent(user_agent);
|
||||
let mut base_client =
|
||||
BaseClientBuilder::new(self.config().base(), storage, dkg_query_client)
|
||||
.with_user_agent(user_agent);
|
||||
|
||||
if let Some(custom_mixnet) = &self.custom_mixnet {
|
||||
base_client = base_client.with_stored_topology(custom_mixnet)?;
|
||||
|
||||
@@ -82,6 +82,7 @@ impl From<Init> for OverrideConfig {
|
||||
nyxd_urls: init_config.common_args.nyxd_urls,
|
||||
enabled_credentials_mode: init_config.common_args.enabled_credentials_mode,
|
||||
stats_reporting_address: init_config.common_args.stats_reporting_address,
|
||||
forget_me: init_config.common_args.forget_me.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ use nym_bin_common::completions::{fig_generate, ArgShell};
|
||||
use nym_client::client::Recipient;
|
||||
use nym_client_core::cli_helpers::CliClient;
|
||||
use nym_client_core::client::base_client::storage::migration_helpers::v1_1_33;
|
||||
use nym_client_core::config::ForgetMe;
|
||||
use nym_config::OptionalSet;
|
||||
use std::error::Error;
|
||||
use std::net::IpAddr;
|
||||
@@ -106,6 +107,7 @@ pub(crate) struct OverrideConfig {
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
stats_reporting_address: Option<Recipient>,
|
||||
forget_me: ForgetMe,
|
||||
}
|
||||
|
||||
pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
@@ -133,6 +135,7 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
|
||||
args.fastmode,
|
||||
)
|
||||
.with_base(BaseClientConfig::with_disabled_cover_traffic, args.no_cover)
|
||||
.with_base(BaseClientConfig::with_forget_me, args.forget_me)
|
||||
.with_optional(Config::with_port, args.port)
|
||||
.with_optional(Config::with_host, args.host)
|
||||
.with_optional_custom_env_ext(
|
||||
|
||||
@@ -41,6 +41,7 @@ impl From<Run> for OverrideConfig {
|
||||
nyxd_urls: run_config.common_args.nyxd_urls,
|
||||
enabled_credentials_mode: run_config.common_args.enabled_credentials_mode,
|
||||
stats_reporting_address: run_config.common_args.stats_reporting_address,
|
||||
forget_me: run_config.common_args.forget_me.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.45"
|
||||
version = "1.1.47"
|
||||
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"
|
||||
|
||||
@@ -93,6 +93,7 @@ impl From<Init> for OverrideConfig {
|
||||
enabled_credentials_mode: init_config.common_args.enabled_credentials_mode,
|
||||
outfox: false,
|
||||
stats_reporting_address: init_config.common_args.stats_reporting_address,
|
||||
forget_me: init_config.common_args.forget_me.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ use nym_bin_common::completions::{fig_generate, ArgShell};
|
||||
use nym_client_core::cli_helpers::CliClient;
|
||||
use nym_client_core::client::base_client::storage::migration_helpers::v1_1_33;
|
||||
use nym_client_core::client::topology_control::geo_aware_provider::CountryGroup;
|
||||
use nym_client_core::config::{GroupBy, TopologyStructure};
|
||||
use nym_client_core::config::{ForgetMe, GroupBy, TopologyStructure};
|
||||
use nym_config::OptionalSet;
|
||||
use nym_sphinx::addressing::Recipient;
|
||||
use nym_sphinx::params::{PacketSize, PacketType};
|
||||
@@ -113,6 +113,7 @@ pub(crate) struct OverrideConfig {
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
outfox: bool,
|
||||
stats_reporting_address: Option<Recipient>,
|
||||
forget_me: ForgetMe,
|
||||
}
|
||||
|
||||
pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
@@ -179,6 +180,7 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
|
||||
BaseClientConfig::with_topology_structure,
|
||||
topology_structure,
|
||||
)
|
||||
.with_base(BaseClientConfig::with_forget_me, args.forget_me)
|
||||
.with_optional(Config::with_anonymous_replies, args.use_anonymous_replies)
|
||||
.with_optional(Config::with_port, args.port)
|
||||
.with_optional(Config::with_ip, args.ip)
|
||||
|
||||
@@ -65,6 +65,7 @@ impl From<Run> for OverrideConfig {
|
||||
enabled_credentials_mode: run_config.common_args.enabled_credentials_mode,
|
||||
outfox: run_config.outfox,
|
||||
stats_reporting_address: run_config.common_args.stats_reporting_address,
|
||||
forget_me: run_config.common_args.forget_me.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,4 +113,8 @@ enabled = {{ core.debug.stats_reporting.enabled }}
|
||||
provider_address = '{{ core.debug.stats_reporting.provider_address }}'
|
||||
reporting_interval = '{{ core.debug.stats_reporting.reporting_interval }}'
|
||||
|
||||
[core.debug.forget_me]
|
||||
client = {{ core.debug.forget_me.client }}
|
||||
stats = {{ core.debug.forget_me.stats }}
|
||||
|
||||
"#;
|
||||
|
||||
@@ -28,7 +28,7 @@ pub type HmacSha256 = Hmac<Sha256>;
|
||||
pub type Nonce = u64;
|
||||
pub type Taken = Option<SystemTime>;
|
||||
|
||||
pub const BANDWIDTH_CAP_PER_DAY: u64 = 1024 * 1024 * 1024; // 1 GB
|
||||
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 {
|
||||
|
||||
@@ -40,6 +40,7 @@ nym-crypto = { path = "../crypto" }
|
||||
nym-explorer-client = { path = "../../explorer-api/explorer-client" }
|
||||
nym-gateway-client = { path = "../client-libs/gateway-client" }
|
||||
nym-gateway-requests = { path = "../gateway-requests" }
|
||||
nym-http-api-client = { path = "../http-api-client" }
|
||||
nym-metrics = { path = "../nym-metrics" }
|
||||
nym-nonexhaustive-delayqueue = { path = "../nonexhaustive-delayqueue" }
|
||||
nym-sphinx = { path = "../nymsphinx" }
|
||||
|
||||
@@ -145,6 +145,11 @@ impl Config {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_forget_me(mut self, forget_me: ForgetMe) -> Self {
|
||||
self.debug.forget_me = forget_me;
|
||||
self
|
||||
}
|
||||
|
||||
// TODO: this should be refactored properly
|
||||
// as of 12.09.23 the below is true (not sure how this comment will rot in the future)
|
||||
// medium_toggle:
|
||||
@@ -517,7 +522,7 @@ impl Default for Acknowledgements {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
#[serde(default)]
|
||||
pub struct Topology {
|
||||
/// The uniform delay every which clients are querying the directory server
|
||||
/// to try to obtain a compatible network topology to send sphinx packets through.
|
||||
@@ -553,12 +558,15 @@ pub struct Topology {
|
||||
|
||||
/// Specifies whether this client should attempt to retrieve all available network nodes
|
||||
/// as opposed to just active mixnodes/gateways.
|
||||
/// Useless without `ignore_epoch_roles = true`
|
||||
pub use_extended_topology: bool,
|
||||
|
||||
/// Specifies whether this client should ignore the current epoch role of the target egress node
|
||||
/// when constructing the final hop packets.
|
||||
pub ignore_egress_epoch_role: bool,
|
||||
|
||||
/// Specifies whether this client should ignore the current epoch role of the ingress node
|
||||
/// when attempting to establish new connection
|
||||
pub ignore_ingress_epoch_role: bool,
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
@@ -596,7 +604,9 @@ impl Default for Topology {
|
||||
minimum_mixnode_performance: DEFAULT_MIN_MIXNODE_PERFORMANCE,
|
||||
minimum_gateway_performance: DEFAULT_MIN_GATEWAY_PERFORMANCE,
|
||||
use_extended_topology: false,
|
||||
ignore_egress_epoch_role: false,
|
||||
|
||||
ignore_egress_epoch_role: true,
|
||||
ignore_ingress_epoch_role: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -714,6 +724,9 @@ pub struct DebugConfig {
|
||||
|
||||
/// Defines all configuration options related to stats reporting.
|
||||
pub stats_reporting: StatsReporting,
|
||||
|
||||
/// Defines all configuration options related to the forget me flag.
|
||||
pub forget_me: ForgetMe,
|
||||
}
|
||||
|
||||
impl DebugConfig {
|
||||
@@ -736,6 +749,69 @@ impl Default for DebugConfig {
|
||||
topology: Default::default(),
|
||||
reply_surbs: Default::default(),
|
||||
stats_reporting: Default::default(),
|
||||
forget_me: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug, Deserialize, PartialEq, Serialize, Copy)]
|
||||
pub struct ForgetMe {
|
||||
client: bool,
|
||||
stats: bool,
|
||||
}
|
||||
|
||||
impl From<bool> for ForgetMe {
|
||||
fn from(value: bool) -> Self {
|
||||
if value {
|
||||
Self::new_all()
|
||||
} else {
|
||||
Self::new_none()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ForgetMe {
|
||||
pub fn new_all() -> Self {
|
||||
Self {
|
||||
client: true,
|
||||
stats: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_client() -> Self {
|
||||
Self {
|
||||
client: true,
|
||||
stats: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_stats() -> Self {
|
||||
Self {
|
||||
client: false,
|
||||
stats: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(client: bool, stats: bool) -> Self {
|
||||
Self { client, stats }
|
||||
}
|
||||
|
||||
pub fn any(&self) -> bool {
|
||||
self.client || self.stats
|
||||
}
|
||||
|
||||
pub fn client(&self) -> bool {
|
||||
self.client
|
||||
}
|
||||
|
||||
pub fn stats(&self) -> bool {
|
||||
self.stats
|
||||
}
|
||||
|
||||
pub fn new_none() -> Self {
|
||||
Self {
|
||||
client: false,
|
||||
stats: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ impl From<ConfigV5> for Config {
|
||||
maximum_reply_key_age: value.debug.reply_surbs.maximum_reply_key_age,
|
||||
surb_mix_hops: value.debug.reply_surbs.surb_mix_hops,
|
||||
},
|
||||
stats_reporting: Default::default(),
|
||||
..Default::default()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ pub mod error;
|
||||
mod manager;
|
||||
mod models;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct OnDiskGatewaysDetails {
|
||||
manager: StorageManager,
|
||||
}
|
||||
|
||||
@@ -20,12 +20,12 @@ pub enum InMemStorageError {
|
||||
MalformedGateway(#[from] BadGateway),
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct InMemGatewaysDetails {
|
||||
inner: Arc<RwLock<InMemStorageInner>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct InMemStorageInner {
|
||||
active_gateway: Option<String>,
|
||||
gateways: HashMap<String, GatewayRegistration>,
|
||||
|
||||
@@ -115,11 +115,12 @@ where
|
||||
hardcoded_topology.entry_capable_nodes().cloned().collect()
|
||||
} else {
|
||||
let mut rng = rand::thread_rng();
|
||||
crate::init::helpers::current_gateways(
|
||||
crate::init::helpers::gateways_for_init(
|
||||
&mut rng,
|
||||
&core.client.nym_api_urls,
|
||||
user_agent,
|
||||
core.debug.topology.minimum_gateway_performance,
|
||||
core.debug.topology.ignore_ingress_epoch_role,
|
||||
)
|
||||
.await?
|
||||
};
|
||||
|
||||
@@ -93,6 +93,10 @@ pub struct CommonClientInitArgs {
|
||||
/// Sets the address to report statistics
|
||||
#[cfg_attr(feature = "cli", clap(long, hide = true))]
|
||||
pub stats_reporting_address: Option<Recipient>,
|
||||
|
||||
/// Sets the forget me flag
|
||||
#[cfg_attr(feature = "cli", clap(long, hide = true, default_value_t = false))]
|
||||
pub forget_me: bool,
|
||||
}
|
||||
|
||||
pub struct InitResultsWithConfig<T> {
|
||||
@@ -170,11 +174,12 @@ where
|
||||
hardcoded_topology.entry_capable_nodes().cloned().collect()
|
||||
} else {
|
||||
let mut rng = rand::thread_rng();
|
||||
crate::init::helpers::current_gateways(
|
||||
crate::init::helpers::gateways_for_init(
|
||||
&mut rng,
|
||||
&core.client.nym_api_urls,
|
||||
user_agent,
|
||||
core.debug.topology.minimum_gateway_performance,
|
||||
core.debug.topology.ignore_ingress_epoch_role,
|
||||
)
|
||||
.await?
|
||||
};
|
||||
|
||||
@@ -61,4 +61,8 @@ pub struct CommonClientRunArgs {
|
||||
/// Sets the address to report statistics
|
||||
#[cfg_attr(feature = "cli", clap(long, hide = true))]
|
||||
pub stats_reporting_address: Option<Recipient>,
|
||||
|
||||
/// Sets the forget me flag
|
||||
#[cfg_attr(feature = "cli", clap(long, hide = true, default_value_t = false))]
|
||||
pub forget_me: bool,
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::mix_traffic::ClientRequestSender;
|
||||
use super::received_buffer::ReceivedBufferMessage;
|
||||
use super::statistics_control::StatisticsControl;
|
||||
use crate::client::base_client::storage::helpers::store_client_keys;
|
||||
@@ -31,10 +32,11 @@ use crate::init::{
|
||||
setup_gateway,
|
||||
types::{GatewaySetup, InitialisationResult},
|
||||
};
|
||||
use crate::{config, spawn_future, ForgetMe};
|
||||
use crate::{config, spawn_future};
|
||||
use futures::channel::mpsc;
|
||||
use log::*;
|
||||
use nym_bandwidth_controller::BandwidthController;
|
||||
use nym_client_core_config_types::ForgetMe;
|
||||
use nym_client_core_gateways_storage::{GatewayDetails, GatewaysDetailsStore};
|
||||
use nym_credential_storage::storage::Storage as CredentialStorage;
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
@@ -175,8 +177,8 @@ impl From<bool> for CredentialsToggle {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BaseClientBuilder<'a, C, S: MixnetClientStorage> {
|
||||
config: &'a Config,
|
||||
pub struct BaseClientBuilder<C, S: MixnetClientStorage> {
|
||||
config: Config,
|
||||
client_store: S,
|
||||
dkg_query_client: Option<C>,
|
||||
|
||||
@@ -190,20 +192,18 @@ pub struct BaseClientBuilder<'a, C, S: MixnetClientStorage> {
|
||||
|
||||
#[cfg(unix)]
|
||||
connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
|
||||
|
||||
forget_me: ForgetMe,
|
||||
}
|
||||
|
||||
impl<'a, C, S> BaseClientBuilder<'a, C, S>
|
||||
impl<C, S> BaseClientBuilder<C, S>
|
||||
where
|
||||
S: MixnetClientStorage + 'static,
|
||||
C: DkgQueryClient + Send + Sync + 'static,
|
||||
{
|
||||
pub fn new(
|
||||
base_config: &'a Config,
|
||||
base_config: Config,
|
||||
client_store: S,
|
||||
dkg_query_client: Option<C>,
|
||||
) -> BaseClientBuilder<'a, C, S> {
|
||||
) -> BaseClientBuilder<C, S> {
|
||||
BaseClientBuilder {
|
||||
config: base_config,
|
||||
client_store,
|
||||
@@ -216,13 +216,12 @@ where
|
||||
setup_method: GatewaySetup::MustLoad { gateway_id: None },
|
||||
#[cfg(unix)]
|
||||
connection_fd_callback: None,
|
||||
forget_me: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_forget_me(mut self, forget_me: &ForgetMe) -> Self {
|
||||
self.forget_me = forget_me.clone();
|
||||
self.config.debug.forget_me = *forget_me;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -472,6 +471,7 @@ where
|
||||
.claim_initial_bandwidth()
|
||||
.await
|
||||
.map_err(gateway_failure)?;
|
||||
|
||||
gateway_client
|
||||
.start_listening_for_mixnet_messages()
|
||||
.map_err(gateway_failure)?;
|
||||
@@ -644,13 +644,12 @@ where
|
||||
fn start_mix_traffic_controller(
|
||||
gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
|
||||
shutdown: TaskClient,
|
||||
forget_me: ForgetMe,
|
||||
) -> BatchMixMessageSender {
|
||||
) -> (BatchMixMessageSender, ClientRequestSender) {
|
||||
info!("Starting mix traffic controller...");
|
||||
let (mix_traffic_controller, mix_tx) =
|
||||
MixTrafficController::new(gateway_transceiver, forget_me);
|
||||
let (mix_traffic_controller, mix_tx, client_tx) =
|
||||
MixTrafficController::new(gateway_transceiver);
|
||||
mix_traffic_controller.start_with_shutdown(shutdown);
|
||||
mix_tx
|
||||
(mix_tx, client_tx)
|
||||
}
|
||||
|
||||
// TODO: rename it as it implies the data is persistent whilst one can use InMemBackend
|
||||
@@ -772,7 +771,7 @@ where
|
||||
);
|
||||
|
||||
let stats_reporter = Self::start_statistics_control(
|
||||
self.config,
|
||||
&self.config,
|
||||
self.user_agent.clone(),
|
||||
generate_client_stats_id(*self_address.identity()),
|
||||
input_sender.clone(),
|
||||
@@ -798,7 +797,7 @@ where
|
||||
|
||||
let gateway_transceiver = Self::setup_gateway_transceiver(
|
||||
self.custom_gateway_transceiver,
|
||||
self.config,
|
||||
&self.config,
|
||||
init_res,
|
||||
bandwidth_controller,
|
||||
&details_store,
|
||||
@@ -832,10 +831,9 @@ where
|
||||
// traffic stream.
|
||||
// The MixTrafficController then sends the actual traffic
|
||||
|
||||
let message_sender = Self::start_mix_traffic_controller(
|
||||
let (message_sender, client_request_sender) = Self::start_mix_traffic_controller(
|
||||
gateway_transceiver,
|
||||
shutdown.fork("mix_traffic_controller"),
|
||||
self.forget_me,
|
||||
);
|
||||
|
||||
// Channels that the websocket listener can use to signal downstream to the real traffic
|
||||
@@ -910,6 +908,8 @@ where
|
||||
},
|
||||
stats_reporter,
|
||||
task_handle: shutdown,
|
||||
client_request_sender,
|
||||
forget_me: self.config.debug.forget_me,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -921,6 +921,7 @@ pub struct BaseClient {
|
||||
pub client_output: ClientOutputStatus,
|
||||
pub client_state: ClientState,
|
||||
pub stats_reporter: ClientStatsSender,
|
||||
|
||||
pub client_request_sender: ClientRequestSender,
|
||||
pub task_handle: TaskHandle,
|
||||
pub forget_me: ForgetMe,
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
// TODO: combine those more closely. Perhaps into a single underlying store.
|
||||
// Like for persistent, on-disk, storage, what's the point of having 3 different databases?
|
||||
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
use crate::client::key_manager::persistence::{InMemEphemeralKeys, KeyStore};
|
||||
use crate::client::replies::reply_storage;
|
||||
use crate::client::replies::reply_storage::ReplyStorageBackend;
|
||||
@@ -63,7 +65,7 @@ pub trait MixnetClientStorage {
|
||||
fn gateway_details_store(&self) -> &Self::GatewaysDetailsStore;
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Clone)]
|
||||
pub struct Ephemeral {
|
||||
key_store: InMemEphemeralKeys,
|
||||
reply_store: reply_storage::Empty,
|
||||
@@ -71,9 +73,14 @@ pub struct Ephemeral {
|
||||
gateway_details_store: InMemGatewaysDetails,
|
||||
}
|
||||
|
||||
impl Ephemeral {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
impl Default for Ephemeral {
|
||||
fn default() -> Self {
|
||||
Ephemeral {
|
||||
key_store: InMemEphemeralKeys::new(&mut OsRng),
|
||||
reply_store: Default::default(),
|
||||
credential_store: Default::default(),
|
||||
gateway_details_store: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,6 +121,7 @@ impl MixnetClientStorage for Ephemeral {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[cfg(all(
|
||||
not(target_arch = "wasm32"),
|
||||
feature = "fs-surb-storage",
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
|
||||
use crate::client::key_manager::ClientKeys;
|
||||
use async_trait::async_trait;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use std::error::Error;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
@@ -64,6 +66,7 @@ pub enum OnDiskKeysError {
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub struct OnDiskKeys {
|
||||
paths: ClientKeysPaths,
|
||||
@@ -193,9 +196,20 @@ impl KeyStore for OnDiskKeys {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Clone)]
|
||||
pub struct InMemEphemeralKeys {
|
||||
keys: Mutex<Option<ClientKeys>>,
|
||||
keys: Arc<Mutex<ClientKeys>>,
|
||||
}
|
||||
|
||||
impl InMemEphemeralKeys {
|
||||
pub fn new<R>(rng: &mut R) -> Self
|
||||
where
|
||||
R: RngCore + CryptoRng,
|
||||
{
|
||||
InMemEphemeralKeys {
|
||||
keys: Arc::new(Mutex::new(ClientKeys::generate_new(rng))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
@@ -208,11 +222,11 @@ impl KeyStore for InMemEphemeralKeys {
|
||||
type StorageError = EphemeralKeysError;
|
||||
|
||||
async fn load_keys(&self) -> Result<ClientKeys, Self::StorageError> {
|
||||
self.keys.lock().await.clone().ok_or(EphemeralKeysError)
|
||||
Ok(self.keys.lock().await.clone())
|
||||
}
|
||||
|
||||
async fn store_keys(&self, keys: &ClientKeys) -> Result<(), Self::StorageError> {
|
||||
*self.keys.lock().await = Some(keys.clone());
|
||||
*self.keys.lock().await = keys.clone();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,17 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::mix_traffic::transceiver::GatewayTransceiver;
|
||||
use crate::{spawn_future, ForgetMe};
|
||||
use crate::error::ClientCoreError;
|
||||
use crate::spawn_future;
|
||||
use log::*;
|
||||
use nym_gateway_requests::ClientRequest;
|
||||
use nym_sphinx::forwarding::packet::MixPacket;
|
||||
use transceiver::ErasedGatewayError;
|
||||
|
||||
pub type BatchMixMessageSender = tokio::sync::mpsc::Sender<Vec<MixPacket>>;
|
||||
pub type BatchMixMessageReceiver = tokio::sync::mpsc::Receiver<Vec<MixPacket>>;
|
||||
pub type ClientRequestReceiver = tokio::sync::mpsc::Receiver<ClientRequest>;
|
||||
pub type ClientRequestSender = tokio::sync::mpsc::Sender<ClientRequest>;
|
||||
|
||||
pub mod transceiver;
|
||||
|
||||
@@ -23,52 +27,67 @@ pub struct MixTrafficController {
|
||||
gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
|
||||
|
||||
mix_rx: BatchMixMessageReceiver,
|
||||
client_rx: ClientRequestReceiver,
|
||||
|
||||
// TODO: this is temporary work-around.
|
||||
// in long run `gateway_client` will be moved away from `MixTrafficController` anyway.
|
||||
consecutive_gateway_failure_count: usize,
|
||||
forget_me: ForgetMe,
|
||||
}
|
||||
|
||||
impl MixTrafficController {
|
||||
pub fn new<T>(
|
||||
gateway_transceiver: T,
|
||||
forget_me: ForgetMe,
|
||||
) -> (MixTrafficController, BatchMixMessageSender)
|
||||
) -> (
|
||||
MixTrafficController,
|
||||
BatchMixMessageSender,
|
||||
ClientRequestSender,
|
||||
)
|
||||
where
|
||||
T: GatewayTransceiver + Send + 'static,
|
||||
{
|
||||
let (message_sender, message_receiver) =
|
||||
tokio::sync::mpsc::channel(MIX_MESSAGE_RECEIVER_BUFFER_SIZE);
|
||||
|
||||
let (client_sender, client_receiver) = tokio::sync::mpsc::channel(1);
|
||||
|
||||
(
|
||||
MixTrafficController {
|
||||
gateway_transceiver: Box::new(gateway_transceiver),
|
||||
mix_rx: message_receiver,
|
||||
client_rx: client_receiver,
|
||||
consecutive_gateway_failure_count: 0,
|
||||
forget_me,
|
||||
},
|
||||
message_sender,
|
||||
client_sender,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_dynamic(
|
||||
gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
|
||||
forget_me: ForgetMe,
|
||||
) -> (MixTrafficController, BatchMixMessageSender) {
|
||||
) -> (
|
||||
MixTrafficController,
|
||||
BatchMixMessageSender,
|
||||
ClientRequestSender,
|
||||
) {
|
||||
let (message_sender, message_receiver) =
|
||||
tokio::sync::mpsc::channel(MIX_MESSAGE_RECEIVER_BUFFER_SIZE);
|
||||
let (client_sender, client_receiver) = tokio::sync::mpsc::channel(1);
|
||||
(
|
||||
MixTrafficController {
|
||||
gateway_transceiver,
|
||||
mix_rx: message_receiver,
|
||||
client_rx: client_receiver,
|
||||
consecutive_gateway_failure_count: 0,
|
||||
forget_me,
|
||||
},
|
||||
message_sender,
|
||||
client_sender,
|
||||
)
|
||||
}
|
||||
|
||||
async fn on_messages(&mut self, mut mix_packets: Vec<MixPacket>) {
|
||||
async fn on_messages(
|
||||
&mut self,
|
||||
mut mix_packets: Vec<MixPacket>,
|
||||
) -> Result<(), ErasedGatewayError> {
|
||||
debug_assert!(!mix_packets.is_empty());
|
||||
|
||||
let result = if mix_packets.len() == 1 {
|
||||
@@ -80,21 +99,14 @@ impl MixTrafficController {
|
||||
.await
|
||||
};
|
||||
|
||||
match result {
|
||||
Err(err) => {
|
||||
error!("Failed to send sphinx packet(s) to the gateway: {err}");
|
||||
self.consecutive_gateway_failure_count += 1;
|
||||
if self.consecutive_gateway_failure_count == MAX_FAILURE_COUNT {
|
||||
// todo: in the future this should initiate a 'graceful' shutdown or try
|
||||
// to reconnect?
|
||||
panic!("failed to send sphinx packet to the gateway {MAX_FAILURE_COUNT} times in a row - assuming the gateway is dead. Can't do anything about it yet :(")
|
||||
}
|
||||
}
|
||||
Ok(_) => {
|
||||
trace!("We *might* have managed to forward sphinx packet(s) to the gateway!");
|
||||
self.consecutive_gateway_failure_count = 0;
|
||||
}
|
||||
if result.is_err() {
|
||||
self.consecutive_gateway_failure_count += 1;
|
||||
} else {
|
||||
trace!("We *might* have managed to forward sphinx packet(s) to the gateway!");
|
||||
self.consecutive_gateway_failure_count = 0;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn start_with_shutdown(mut self, mut shutdown: nym_task::TaskClient) {
|
||||
@@ -105,13 +117,35 @@ impl MixTrafficController {
|
||||
tokio::select! {
|
||||
mix_packets = self.mix_rx.recv() => match mix_packets {
|
||||
Some(mix_packets) => {
|
||||
self.on_messages(mix_packets).await;
|
||||
if let Err(err) = self.on_messages(mix_packets).await {
|
||||
error!("Failed to send sphinx packet(s) to the gateway: {err}");
|
||||
if self.consecutive_gateway_failure_count == MAX_FAILURE_COUNT {
|
||||
// Disconnect from the gateway. If we should try to re-connect
|
||||
// is handled at a higher layer.
|
||||
error!("Failed to send sphinx packet to the gateway {MAX_FAILURE_COUNT} times in a row - assuming the gateway is dead");
|
||||
// Do we need to handle the embedded mixnet client case
|
||||
// separately?
|
||||
shutdown.send_we_stopped(Box::new(ClientCoreError::GatewayFailedToForwardMessages));
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {
|
||||
log::trace!("MixTrafficController: Stopping since channel closed");
|
||||
break;
|
||||
}
|
||||
},
|
||||
client_request = self.client_rx.recv() => match client_request {
|
||||
Some(client_request) => {
|
||||
match self.gateway_transceiver.send_client_request(client_request).await {
|
||||
Ok(_) => (),
|
||||
Err(e) => error!("Failed to send client request: {}", e),
|
||||
};
|
||||
},
|
||||
None => {
|
||||
log::trace!("MixTrafficController, client request channel closed");
|
||||
}
|
||||
},
|
||||
_ = shutdown.recv_with_delay() => {
|
||||
log::trace!("MixTrafficController: Received shutdown");
|
||||
break;
|
||||
@@ -120,25 +154,6 @@ impl MixTrafficController {
|
||||
}
|
||||
shutdown.recv_timeout().await;
|
||||
|
||||
if self.forget_me.any() {
|
||||
log::info!("Sending forget me request to the gateway");
|
||||
match self
|
||||
.gateway_transceiver
|
||||
.send_client_request(ClientRequest::ForgetMe {
|
||||
client: self.forget_me.client(),
|
||||
stats: self.forget_me.stats(),
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
log::info!("Successfully sent forget me request to the gateway");
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!("Failed to send forget me request to the gateway: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log::debug!("MixTrafficController: Exiting");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -86,7 +86,9 @@ impl<G: GatewayTransceiver + ?Sized + Send> GatewayTransceiver for Box<G> {
|
||||
&mut self,
|
||||
message: ClientRequest,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
(**self).send_client_request(message).await
|
||||
let _ = (**self).send_client_request(message.clone()).await?;
|
||||
log::debug!("Sent client request: {:?}", message);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,14 +145,7 @@ where
|
||||
&mut self,
|
||||
message: ClientRequest,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
if let Some(shared_key) = self.gateway_client.shared_key() {
|
||||
self.gateway_client
|
||||
.send_websocket_message(message.encrypt(&*shared_key)?)
|
||||
.await?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(GatewayClientError::ConnectionInInvalidState)
|
||||
}
|
||||
self.gateway_client.send_client_request(message).await
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -626,9 +626,14 @@ where
|
||||
messages: Vec<RealMessage>,
|
||||
transmission_lane: TransmissionLane,
|
||||
) {
|
||||
self.real_message_sender
|
||||
if let Err(err) = self
|
||||
.real_message_sender
|
||||
.send((messages, transmission_lane))
|
||||
.await
|
||||
.expect("real message receiver task (OutQueueControl) has died");
|
||||
{
|
||||
error!(
|
||||
"Failed to forward messages to the real message sender (OutQueueControl): {err}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,12 @@ use self::{
|
||||
acknowledgement_control::AcknowledgementController, real_traffic_stream::OutQueueControl,
|
||||
};
|
||||
use crate::client::real_messages_control::message_handler::MessageHandler;
|
||||
use crate::client::replies::reply_controller;
|
||||
use crate::client::replies::reply_controller::{
|
||||
ReplyController, ReplyControllerReceiver, ReplyControllerSender,
|
||||
};
|
||||
use crate::client::replies::reply_storage::CombinedReplyStorage;
|
||||
use crate::config;
|
||||
use crate::{
|
||||
client::{
|
||||
inbound_messages::InputMessageReceiver, mix_traffic::BatchMixMessageSender,
|
||||
@@ -27,16 +29,13 @@ use nym_gateway_client::AcknowledgementReceiver;
|
||||
use nym_sphinx::acknowledgements::AckKey;
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_sphinx::params::PacketType;
|
||||
use nym_statistics_common::clients::ClientStatsSender;
|
||||
use nym_task::connections::{ConnectionCommandReceiver, LaneQueueLengths};
|
||||
use rand::{rngs::OsRng, CryptoRng, Rng};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::client::replies::reply_controller;
|
||||
use crate::config;
|
||||
pub(crate) use acknowledgement_control::{AckActionSender, Action};
|
||||
|
||||
use nym_statistics_common::clients::ClientStatsSender;
|
||||
|
||||
pub(crate) mod acknowledgement_control;
|
||||
pub(crate) mod message_handler;
|
||||
pub(crate) mod real_traffic_stream;
|
||||
|
||||
@@ -545,7 +545,7 @@ where
|
||||
loop {
|
||||
tokio::select! {
|
||||
biased;
|
||||
_ = shutdown.recv_with_delay() => {
|
||||
_ = shutdown.recv() => {
|
||||
log::trace!("OutQueueControl: Received shutdown");
|
||||
break;
|
||||
}
|
||||
|
||||
+4
-1
@@ -70,7 +70,10 @@ impl SendingDelayController {
|
||||
lower_bound,
|
||||
multiplier_elevated_counter: 0,
|
||||
time_when_logged_about_elevated_multiplier: now
|
||||
- Duration::from_secs(INTERVAL_BETWEEN_WARNING_ABOUT_ELEVATED_MULTIPLIER_SECS),
|
||||
.checked_sub(Duration::from_secs(
|
||||
INTERVAL_BETWEEN_WARNING_ABOUT_ELEVATED_MULTIPLIER_SECS,
|
||||
))
|
||||
.unwrap_or(now),
|
||||
time_when_changed: now,
|
||||
time_when_backpressure_detected: now,
|
||||
}
|
||||
|
||||
@@ -16,14 +16,14 @@
|
||||
#![warn(clippy::todo)]
|
||||
#![warn(clippy::dbg_macro)]
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use futures::StreamExt;
|
||||
use nym_client_core_config_types::StatsReporting;
|
||||
use nym_sphinx::addressing::Recipient;
|
||||
use nym_statistics_common::clients::{
|
||||
ClientStatsController, ClientStatsReceiver, ClientStatsSender,
|
||||
};
|
||||
use nym_task::connections::TransmissionLane;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::{
|
||||
client::inbound_messages::{InputMessage, InputMessageSender},
|
||||
@@ -94,13 +94,40 @@ impl StatisticsControl {
|
||||
async fn run_with_shutdown(&mut self, mut task_client: nym_task::TaskClient) {
|
||||
log::debug!("Started StatisticsControl with graceful shutdown support");
|
||||
|
||||
let mut stats_report_interval =
|
||||
tokio::time::interval(self.reporting_config.reporting_interval);
|
||||
let mut local_report_interval = tokio::time::interval(LOCAL_REPORT_INTERVAL);
|
||||
let mut snapshot_interval = tokio::time::interval(SNAPSHOT_INTERVAL);
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let mut stats_report_interval = tokio_stream::wrappers::IntervalStream::new(
|
||||
tokio::time::interval(self.reporting_config.reporting_interval),
|
||||
);
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let mut local_report_interval = tokio_stream::wrappers::IntervalStream::new(
|
||||
tokio::time::interval(LOCAL_REPORT_INTERVAL),
|
||||
);
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let mut snapshot_interval =
|
||||
tokio_stream::wrappers::IntervalStream::new(tokio::time::interval(SNAPSHOT_INTERVAL));
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let mut stats_report_interval = gloo_timers::future::IntervalStream::new(
|
||||
self.reporting_config.reporting_interval.as_millis() as u32,
|
||||
);
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let mut local_report_interval =
|
||||
gloo_timers::future::IntervalStream::new(LOCAL_REPORT_INTERVAL.as_millis() as u32);
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let mut snapshot_interval =
|
||||
gloo_timers::future::IntervalStream::new(SNAPSHOT_INTERVAL.as_millis() as u32);
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
biased;
|
||||
_ = task_client.recv() => {
|
||||
log::trace!("StatisticsControl: Received shutdown");
|
||||
break;
|
||||
},
|
||||
stats_event = self.stats_rx.recv() => match stats_event {
|
||||
Some(stats_event) => self.stats.handle_event(stats_event),
|
||||
None => {
|
||||
@@ -108,25 +135,24 @@ impl StatisticsControl {
|
||||
break;
|
||||
}
|
||||
},
|
||||
_ = snapshot_interval.tick() => {
|
||||
_ = snapshot_interval.next() => {
|
||||
self.stats.snapshot();
|
||||
}
|
||||
_ = stats_report_interval.tick(), if self.reporting_config.enabled && self.reporting_config.provider_address.is_some() => {
|
||||
// SAFTEY : this branch executes only if reporting is not none, so unwrapp is fine
|
||||
#[allow(clippy::unwrap_used)]
|
||||
self.report_stats(self.reporting_config.provider_address.unwrap()).await;
|
||||
_ = stats_report_interval.next() => {
|
||||
let Some(recipient) = self.reporting_config.provider_address else {
|
||||
continue
|
||||
};
|
||||
|
||||
if self.reporting_config.enabled {
|
||||
self.report_stats(recipient).await;
|
||||
}
|
||||
}
|
||||
|
||||
_ = local_report_interval.tick() => {
|
||||
_ = local_report_interval.next() => {
|
||||
self.stats.local_report(&mut task_client);
|
||||
}
|
||||
_ = task_client.recv_with_delay() => {
|
||||
log::trace!("StatisticsControl: Received shutdown");
|
||||
break;
|
||||
},
|
||||
}
|
||||
}
|
||||
task_client.recv_timeout().await;
|
||||
log::debug!("StatisticsControl: Exiting");
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,13 @@ pub enum ClientCoreError {
|
||||
#[error("no gateway with id: {0}")]
|
||||
NoGatewayWithId(String),
|
||||
|
||||
#[error("Invalid URL: {0}")]
|
||||
InvalidUrl(String),
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[error("resolution failed: {0}")]
|
||||
ResolutionFailed(#[from] nym_http_api_client::HickoryDnsError),
|
||||
|
||||
#[error("no gateways on network")]
|
||||
NoGatewaysOnNetwork,
|
||||
|
||||
@@ -96,6 +103,9 @@ pub enum ClientCoreError {
|
||||
#[error("timed out while trying to establish gateway connection")]
|
||||
GatewayConnectionTimeout,
|
||||
|
||||
#[error("failed to forward mix messages to gateway")]
|
||||
GatewayFailedToForwardMessages,
|
||||
|
||||
#[error("no ping measurements for the gateway ({identity}) performed")]
|
||||
NoGatewayMeasurements { identity: String },
|
||||
|
||||
|
||||
@@ -15,6 +15,9 @@ use std::{sync::Arc, time::Duration};
|
||||
use tungstenite::Message;
|
||||
use url::Url;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use crate::init::websockets::connect_async;
|
||||
|
||||
use nym_topology::NodeId;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio::net::TcpStream;
|
||||
@@ -23,8 +26,6 @@ use tokio::time::sleep;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio::time::Instant;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio_tungstenite::connect_async;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_utils::websocket::JSWebsocket;
|
||||
@@ -86,11 +87,12 @@ impl<'a, G: ConnectableGateway> GatewayWithLatency<'a, G> {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn current_gateways<R: Rng>(
|
||||
pub async fn gateways_for_init<R: Rng>(
|
||||
rng: &mut R,
|
||||
nym_apis: &[Url],
|
||||
user_agent: Option<UserAgent>,
|
||||
minimum_performance: u8,
|
||||
ignore_epoch_roles: bool,
|
||||
) -> Result<Vec<RoutingNode>, ClientCoreError> {
|
||||
let nym_api = nym_apis
|
||||
.choose(rng)
|
||||
@@ -108,8 +110,11 @@ pub async fn current_gateways<R: Rng>(
|
||||
|
||||
log::trace!("Gateways: {:#?}", gateways);
|
||||
|
||||
// filter out gateways below minimum performance and ones that could operate as a mixnode
|
||||
// (we don't want instability)
|
||||
let valid_gateways = gateways
|
||||
.iter()
|
||||
.filter(|g| ignore_epoch_roles || !g.supported_roles.mixnode)
|
||||
.filter(|g| g.performance.round_to_integer() >= minimum_performance)
|
||||
.filter_map(|gateway| gateway.try_into().ok())
|
||||
.collect::<Vec<_>>();
|
||||
@@ -128,7 +133,7 @@ pub async fn current_gateways<R: Rng>(
|
||||
async fn connect(endpoint: &str) -> Result<WsConn, ClientCoreError> {
|
||||
match tokio::time::timeout(CONN_TIMEOUT, connect_async(endpoint)).await {
|
||||
Err(_elapsed) => Err(ClientCoreError::GatewayConnectionTimeout),
|
||||
Ok(Err(conn_failure)) => Err(conn_failure.into()),
|
||||
Ok(Err(conn_failure)) => Err(conn_failure),
|
||||
Ok(Ok((stream, _))) => Ok(stream),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ use serde::Serialize;
|
||||
|
||||
pub mod helpers;
|
||||
pub mod types;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(crate) mod websockets;
|
||||
|
||||
// helpers for error wrapping
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ use nym_topology::node::RoutingNode;
|
||||
use nym_validator_client::client::IdentityKey;
|
||||
use nym_validator_client::nyxd::AccountId;
|
||||
use serde::Serialize;
|
||||
use std::fmt::Display;
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::sync::Arc;
|
||||
use time::OffsetDateTime;
|
||||
use url::Url;
|
||||
@@ -221,6 +221,34 @@ pub enum GatewaySetup {
|
||||
},
|
||||
}
|
||||
|
||||
impl Debug for GatewaySetup {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
GatewaySetup::MustLoad { gateway_id } => f
|
||||
.debug_struct("GatewaySetup::MustLoad")
|
||||
.field("gateway_id", gateway_id)
|
||||
.finish(),
|
||||
GatewaySetup::New {
|
||||
specification,
|
||||
available_gateways,
|
||||
} => f
|
||||
.debug_struct("GatewaySetup::New")
|
||||
.field("specification", specification)
|
||||
.field("available_gateways", available_gateways)
|
||||
.field("gateways", specification)
|
||||
.finish(),
|
||||
GatewaySetup::ReuseConnection {
|
||||
gateway_details, ..
|
||||
} => f
|
||||
.debug_struct("GatewaySetup::ReuseConnection")
|
||||
.field("authenticated_ephemeral_client", &"***")
|
||||
.field("gateway_details", gateway_details)
|
||||
.field("client_keys", &"***")
|
||||
.finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GatewaySetup {
|
||||
pub fn try_reuse_connection(init_res: InitialisationResult) -> Result<Self, ClientCoreError> {
|
||||
if let Some(authenticated_ephemeral_client) = init_res.authenticated_ephemeral_client {
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
use crate::error::ClientCoreError;
|
||||
|
||||
use nym_http_api_client::HickoryDnsResolver;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
|
||||
use tungstenite::handshake::client::Response;
|
||||
use url::{Host, Url};
|
||||
|
||||
use std::net::SocketAddr;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(crate) async fn connect_async(
|
||||
endpoint: &str,
|
||||
) -> Result<(WebSocketStream<MaybeTlsStream<TcpStream>>, Response), ClientCoreError> {
|
||||
let resolver = HickoryDnsResolver::default();
|
||||
let uri = Url::parse(endpoint).map_err(|_| ClientCoreError::InvalidUrl(endpoint.to_owned()))?;
|
||||
let port: u16 = uri.port_or_known_default().unwrap_or(443);
|
||||
|
||||
let host = uri
|
||||
.host()
|
||||
.ok_or(ClientCoreError::InvalidUrl(endpoint.to_owned()))?;
|
||||
|
||||
// Get address for tcp connection, if a domain is provided use our preferred resolver rather than
|
||||
// the default std resolve
|
||||
let sock_addrs: Vec<SocketAddr> = match host {
|
||||
Host::Ipv4(addr) => vec![SocketAddr::new(addr.into(), port)],
|
||||
Host::Ipv6(addr) => vec![SocketAddr::new(addr.into(), port)],
|
||||
Host::Domain(domain) => {
|
||||
// Do a DNS lookup for the domain using our custom DNS resolver
|
||||
resolver
|
||||
.resolve_str(domain)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|a| SocketAddr::new(a, port))
|
||||
.collect()
|
||||
}
|
||||
};
|
||||
|
||||
let stream = TcpStream::connect(&sock_addrs[..]).await?;
|
||||
|
||||
tokio_tungstenite::client_async_tls(endpoint, stream)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
@@ -33,48 +33,3 @@ where
|
||||
{
|
||||
tokio::spawn(future);
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub struct ForgetMe {
|
||||
client: bool,
|
||||
stats: bool,
|
||||
}
|
||||
|
||||
impl ForgetMe {
|
||||
pub fn new_all() -> Self {
|
||||
Self {
|
||||
client: true,
|
||||
stats: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_client() -> Self {
|
||||
Self {
|
||||
client: true,
|
||||
stats: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_stats() -> Self {
|
||||
Self {
|
||||
client: false,
|
||||
stats: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(client: bool, stats: bool) -> Self {
|
||||
Self { client, stats }
|
||||
}
|
||||
|
||||
pub fn any(&self) -> bool {
|
||||
self.client || self.stats
|
||||
}
|
||||
|
||||
pub fn client(&self) -> bool {
|
||||
self.client
|
||||
}
|
||||
|
||||
pub fn stats(&self) -> bool {
|
||||
self.stats
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ mod error;
|
||||
mod manager;
|
||||
mod models;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Backend {
|
||||
temporary_old_path: Option<PathBuf>,
|
||||
database_path: PathBuf,
|
||||
|
||||
@@ -19,7 +19,7 @@ pub mod fs_backend;
|
||||
#[error("no information provided")]
|
||||
pub struct UndefinedError;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Empty {
|
||||
// we need to keep 'basic' metadata here to "load" the CombinedReplyStorage
|
||||
pub min_surb_threshold: usize,
|
||||
|
||||
@@ -27,6 +27,7 @@ nym-credential-storage = { path = "../../credential-storage" }
|
||||
nym-credentials-interface = { path = "../../credentials-interface" }
|
||||
nym-crypto = { path = "../../crypto" }
|
||||
nym-gateway-requests = { path = "../../gateway-requests" }
|
||||
nym-http-api-client = { path = "../../http-api-client" }
|
||||
nym-network-defaults = { path = "../../network-defaults" }
|
||||
nym-sphinx = { path = "../../nymsphinx" }
|
||||
nym-statistics-common = { path = "../../statistics" }
|
||||
|
||||
@@ -40,8 +40,6 @@ use url::Url;
|
||||
use std::os::fd::RawFd;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio::time::sleep;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio_tungstenite::connect_async;
|
||||
|
||||
#[cfg(not(unix))]
|
||||
use std::os::raw::c_int as RawFd;
|
||||
@@ -53,6 +51,11 @@ use zeroize::Zeroizing;
|
||||
|
||||
pub mod config;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(crate) mod websockets;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use websockets::connect_async;
|
||||
|
||||
pub struct GatewayConfig {
|
||||
pub gateway_identity: identity::PublicKey,
|
||||
|
||||
@@ -201,15 +204,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
"Attemting to establish connection to gateway at: {}",
|
||||
self.gateway_address
|
||||
);
|
||||
let ws_stream = match connect_async(&self.gateway_address).await {
|
||||
Ok((ws_stream, _)) => ws_stream,
|
||||
Err(error) => {
|
||||
return Err(GatewayClientError::NetworkConnectionFailed {
|
||||
address: self.gateway_address.clone(),
|
||||
source: error,
|
||||
})
|
||||
}
|
||||
};
|
||||
let (ws_stream, _) = connect_async(&self.gateway_address).await?;
|
||||
|
||||
self.connection = SocketState::Available(Box::new(ws_stream));
|
||||
|
||||
@@ -271,6 +266,19 @@ impl<C, St> GatewayClient<C, St> {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send_client_request(
|
||||
&mut self,
|
||||
message: ClientRequest,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
if let Some(shared_key) = self.shared_key() {
|
||||
let encrypted = message.encrypt(&*shared_key)?;
|
||||
Box::pin(self.send_websocket_message(encrypted)).await?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(GatewayClientError::ConnectionInInvalidState)
|
||||
}
|
||||
}
|
||||
|
||||
async fn read_control_response(&mut self) -> Result<ServerResponse, GatewayClientError> {
|
||||
// we use the fact that all request responses are Message::Text and only pushed
|
||||
// sphinx packets are Message::Binary
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
use crate::error::GatewayClientError;
|
||||
|
||||
use nym_http_api_client::HickoryDnsResolver;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
|
||||
use tungstenite::handshake::client::Response;
|
||||
use url::{Host, Url};
|
||||
|
||||
use std::net::SocketAddr;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(crate) async fn connect_async(
|
||||
endpoint: &str,
|
||||
) -> Result<(WebSocketStream<MaybeTlsStream<TcpStream>>, Response), GatewayClientError> {
|
||||
let resolver = HickoryDnsResolver::default();
|
||||
let uri =
|
||||
Url::parse(endpoint).map_err(|_| GatewayClientError::InvalidUrl(endpoint.to_owned()))?;
|
||||
let port: u16 = uri.port_or_known_default().unwrap_or(443);
|
||||
|
||||
let host = uri
|
||||
.host()
|
||||
.ok_or(GatewayClientError::InvalidUrl(endpoint.to_owned()))?;
|
||||
|
||||
// Get address for tcp connection, if a domain is provided use our preferred resolver rather than
|
||||
// the default std resolve
|
||||
let sock_addrs: Vec<SocketAddr> = match host {
|
||||
Host::Ipv4(addr) => vec![SocketAddr::new(addr.into(), port)],
|
||||
Host::Ipv6(addr) => vec![SocketAddr::new(addr.into(), port)],
|
||||
Host::Domain(domain) => {
|
||||
// Do a DNS lookup for the domain using our custom DNS resolver
|
||||
resolver
|
||||
.resolve_str(domain)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|a| SocketAddr::new(a, port))
|
||||
.collect()
|
||||
}
|
||||
};
|
||||
|
||||
let stream = TcpStream::connect(&sock_addrs[..]).await.map_err(|error| {
|
||||
GatewayClientError::NetworkConnectionFailed {
|
||||
address: endpoint.to_owned(),
|
||||
source: error.into(),
|
||||
}
|
||||
})?;
|
||||
|
||||
tokio_tungstenite::client_async_tls(endpoint, stream)
|
||||
.await
|
||||
.map_err(|error| GatewayClientError::NetworkConnectionFailed {
|
||||
address: endpoint.to_owned(),
|
||||
source: error,
|
||||
})
|
||||
}
|
||||
@@ -44,7 +44,11 @@ pub enum GatewayClientError {
|
||||
NetworkConnectionFailed { address: String, source: WsError },
|
||||
|
||||
#[error("Invalid URL: {0}")]
|
||||
InvalidURL(String),
|
||||
InvalidUrl(String),
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[error("resolution failed: {0}")]
|
||||
ResolutionFailed(#[from] nym_http_api_client::HickoryDnsError),
|
||||
|
||||
#[error("No shared key was provided or obtained")]
|
||||
NoSharedKeyAvailable,
|
||||
|
||||
@@ -8,10 +8,12 @@ license.workspace = true
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
dashmap = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
tokio = { workspace = true, features = ["time"] }
|
||||
tokio = { workspace = true, features = ["time", "sync"] }
|
||||
tokio-util = { workspace = true, features = ["codec"], optional = true }
|
||||
tokio-stream = { workspace = true }
|
||||
|
||||
# internal
|
||||
nym-sphinx = { path = "../../nymsphinx" }
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use futures::channel::mpsc;
|
||||
use dashmap::DashMap;
|
||||
use futures::StreamExt;
|
||||
use nym_sphinx::addressing::nodes::NymNodeRoutingAddress;
|
||||
use nym_sphinx::framing::codec::NymCodec;
|
||||
use nym_sphinx::framing::packet::FramedNymPacket;
|
||||
use nym_sphinx::params::PacketType;
|
||||
use nym_sphinx::NymPacket;
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::ops::Deref;
|
||||
use std::sync::atomic::{AtomicU32, AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::sync::mpsc::error::TrySendError;
|
||||
use tokio::time::sleep;
|
||||
use tokio_stream::wrappers::ReceiverStream;
|
||||
use tokio_util::codec::Framed;
|
||||
use tracing::*;
|
||||
|
||||
@@ -55,11 +58,37 @@ pub trait SendWithoutResponse {
|
||||
}
|
||||
|
||||
pub struct Client {
|
||||
conn_new: HashMap<NymNodeRoutingAddress, ConnectionSender>,
|
||||
active_connections: ActiveConnections,
|
||||
connections_count: Arc<AtomicUsize>,
|
||||
config: Config,
|
||||
}
|
||||
|
||||
struct ConnectionSender {
|
||||
#[derive(Default, Clone)]
|
||||
pub struct ActiveConnections {
|
||||
inner: Arc<DashMap<NymNodeRoutingAddress, ConnectionSender>>,
|
||||
}
|
||||
|
||||
impl ActiveConnections {
|
||||
pub fn pending_packets(&self) -> usize {
|
||||
self.inner
|
||||
.iter()
|
||||
.map(|sender| {
|
||||
let max_capacity = sender.channel.max_capacity();
|
||||
let capacity = sender.channel.capacity();
|
||||
max_capacity - capacity
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ActiveConnections {
|
||||
type Target = DashMap<NymNodeRoutingAddress, ConnectionSender>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ConnectionSender {
|
||||
channel: mpsc::Sender<FramedNymPacket>,
|
||||
current_reconnection_attempt: Arc<AtomicU32>,
|
||||
}
|
||||
@@ -73,46 +102,53 @@ impl ConnectionSender {
|
||||
}
|
||||
}
|
||||
|
||||
impl Client {
|
||||
pub fn new(config: Config) -> Client {
|
||||
Client {
|
||||
conn_new: HashMap::new(),
|
||||
config,
|
||||
struct ManagedConnection {
|
||||
address: SocketAddr,
|
||||
message_receiver: ReceiverStream<FramedNymPacket>,
|
||||
connection_timeout: Duration,
|
||||
current_reconnection: Arc<AtomicU32>,
|
||||
}
|
||||
|
||||
impl ManagedConnection {
|
||||
fn new(
|
||||
address: SocketAddr,
|
||||
message_receiver: mpsc::Receiver<FramedNymPacket>,
|
||||
connection_timeout: Duration,
|
||||
current_reconnection: Arc<AtomicU32>,
|
||||
) -> Self {
|
||||
ManagedConnection {
|
||||
address,
|
||||
message_receiver: ReceiverStream::new(message_receiver),
|
||||
connection_timeout,
|
||||
current_reconnection,
|
||||
}
|
||||
}
|
||||
|
||||
async fn manage_connection(
|
||||
address: SocketAddr,
|
||||
receiver: mpsc::Receiver<FramedNymPacket>,
|
||||
connection_timeout: Duration,
|
||||
current_reconnection: &AtomicU32,
|
||||
) {
|
||||
async fn run(self) {
|
||||
let address = self.address;
|
||||
let connection_fut = TcpStream::connect(address);
|
||||
|
||||
let conn = match tokio::time::timeout(connection_timeout, connection_fut).await {
|
||||
let conn = match tokio::time::timeout(self.connection_timeout, connection_fut).await {
|
||||
Ok(stream_res) => match stream_res {
|
||||
Ok(stream) => {
|
||||
debug!("Managed to establish connection to {}", address);
|
||||
debug!("Managed to establish connection to {}", self.address);
|
||||
// if we managed to connect, reset the reconnection count (whatever it might have been)
|
||||
current_reconnection.store(0, Ordering::Release);
|
||||
self.current_reconnection.store(0, Ordering::Release);
|
||||
Framed::new(stream, NymCodec)
|
||||
}
|
||||
Err(err) => {
|
||||
debug!(
|
||||
"failed to establish connection to {} (err: {})",
|
||||
address, err
|
||||
);
|
||||
debug!("failed to establish connection to {address} (err: {err})",);
|
||||
return;
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
debug!(
|
||||
"failed to connect to {} within {:?}",
|
||||
address, connection_timeout
|
||||
"failed to connect to {address} within {:?}",
|
||||
self.connection_timeout
|
||||
);
|
||||
|
||||
// we failed to connect - increase reconnection attempt
|
||||
current_reconnection.fetch_add(1, Ordering::SeqCst);
|
||||
self.current_reconnection.fetch_add(1, Ordering::SeqCst);
|
||||
return;
|
||||
}
|
||||
};
|
||||
@@ -120,15 +156,28 @@ impl Client {
|
||||
// Take whatever the receiver channel produces and put it on the connection.
|
||||
// We could have as well used conn.send_all(receiver.map(Ok)), but considering we don't care
|
||||
// about neither receiver nor the connection, it doesn't matter which one gets consumed
|
||||
if let Err(err) = receiver.map(Ok).forward(conn).await {
|
||||
warn!("Failed to forward packets to {} - {err}", address);
|
||||
if let Err(err) = self.message_receiver.map(Ok).forward(conn).await {
|
||||
warn!("Failed to forward packets to {address}: {err}");
|
||||
}
|
||||
|
||||
debug!(
|
||||
"connection manager to {} is finished. Either the connection failed or mixnet client got dropped",
|
||||
address
|
||||
"connection manager to {address} is finished. Either the connection failed or mixnet client got dropped",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl Client {
|
||||
pub fn new(config: Config, connections_count: Arc<AtomicUsize>) -> Client {
|
||||
Client {
|
||||
active_connections: Default::default(),
|
||||
connections_count,
|
||||
config,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn active_connections(&self) -> ActiveConnections {
|
||||
self.active_connections.clone()
|
||||
}
|
||||
|
||||
/// If we're trying to reconnect, determine how long we should wait.
|
||||
fn determine_backoff(&self, current_attempt: u32) -> Option<Duration> {
|
||||
@@ -148,7 +197,7 @@ impl Client {
|
||||
}
|
||||
|
||||
fn make_connection(&mut self, address: NymNodeRoutingAddress, pending_packet: FramedNymPacket) {
|
||||
let (mut sender, receiver) = mpsc::channel(self.config.maximum_connection_buffer_size);
|
||||
let (sender, receiver) = mpsc::channel(self.config.maximum_connection_buffer_size);
|
||||
|
||||
// this CAN'T fail because we just created the channel which has a non-zero capacity
|
||||
if self.config.maximum_connection_buffer_size > 0 {
|
||||
@@ -156,15 +205,16 @@ impl Client {
|
||||
}
|
||||
|
||||
// if we already tried to connect to `address` before, grab the current attempt count
|
||||
let current_reconnection_attempt = if let Some(existing) = self.conn_new.get_mut(&address) {
|
||||
existing.channel = sender;
|
||||
Arc::clone(&existing.current_reconnection_attempt)
|
||||
} else {
|
||||
let new_entry = ConnectionSender::new(sender);
|
||||
let current_attempt = Arc::clone(&new_entry.current_reconnection_attempt);
|
||||
self.conn_new.insert(address, new_entry);
|
||||
current_attempt
|
||||
};
|
||||
let current_reconnection_attempt =
|
||||
if let Some(mut existing) = self.active_connections.get_mut(&address) {
|
||||
existing.channel = sender;
|
||||
Arc::clone(&existing.current_reconnection_attempt)
|
||||
} else {
|
||||
let new_entry = ConnectionSender::new(sender);
|
||||
let current_attempt = Arc::clone(&new_entry.current_reconnection_attempt);
|
||||
self.active_connections.insert(address, new_entry);
|
||||
current_attempt
|
||||
};
|
||||
|
||||
// load the actual value.
|
||||
let reconnection_attempt = current_reconnection_attempt.load(Ordering::Acquire);
|
||||
@@ -173,6 +223,7 @@ impl Client {
|
||||
// copy the value before moving into another task
|
||||
let initial_connection_timeout = self.config.initial_connection_timeout;
|
||||
|
||||
let connections_count = self.connections_count.clone();
|
||||
tokio::spawn(async move {
|
||||
// before executing the manager, wait for what was specified, if anything
|
||||
if let Some(backoff) = backoff {
|
||||
@@ -180,13 +231,16 @@ impl Client {
|
||||
sleep(backoff).await;
|
||||
}
|
||||
|
||||
Self::manage_connection(
|
||||
connections_count.fetch_add(1, Ordering::SeqCst);
|
||||
ManagedConnection::new(
|
||||
address.into(),
|
||||
receiver,
|
||||
initial_connection_timeout,
|
||||
¤t_reconnection_attempt,
|
||||
current_reconnection_attempt,
|
||||
)
|
||||
.await
|
||||
.run()
|
||||
.await;
|
||||
connections_count.fetch_sub(1, Ordering::SeqCst);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -201,49 +255,47 @@ impl SendWithoutResponse for Client {
|
||||
trace!("Sending packet to {address:?}");
|
||||
let framed_packet = FramedNymPacket::new(packet, packet_type);
|
||||
|
||||
if let Some(sender) = self.conn_new.get_mut(&address) {
|
||||
if let Err(err) = sender.channel.try_send(framed_packet) {
|
||||
if err.is_full() {
|
||||
debug!("Connection to {} seems to not be able to handle all the traffic - dropping the current packet", address);
|
||||
// it's not a 'big' error, but we did not manage to send the packet
|
||||
// if the queue is full, we can't really do anything but to drop the packet
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::WouldBlock,
|
||||
"connection queue is full",
|
||||
))
|
||||
} else if err.is_disconnected() {
|
||||
debug!(
|
||||
"Connection to {} seems to be dead. attempting to re-establish it...",
|
||||
address
|
||||
);
|
||||
// it's not a 'big' error, but we did not manage to send the packet, but queue
|
||||
// it up to send it as soon as the connection is re-established
|
||||
self.make_connection(address, err.into_inner());
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::ConnectionAborted,
|
||||
"reconnection attempt is in progress",
|
||||
))
|
||||
} else {
|
||||
// this can't really happen, but let's safe-guard against it in case something changes in futures library
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"unknown connection buffer error",
|
||||
))
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
let Some(sender) = self.active_connections.get_mut(&address) else {
|
||||
// there was never a connection to begin with
|
||||
debug!("establishing initial connection to {}", address);
|
||||
// it's not a 'big' error, but we did not manage to send the packet, but queue the packet
|
||||
// for sending for as soon as the connection is created
|
||||
self.make_connection(address, framed_packet);
|
||||
Err(io::Error::new(
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::NotConnected,
|
||||
"connection is in progress",
|
||||
))
|
||||
}
|
||||
));
|
||||
};
|
||||
|
||||
let sending_res = sender.channel.try_send(framed_packet);
|
||||
drop(sender);
|
||||
|
||||
sending_res.map_err(|err| {
|
||||
match err {
|
||||
TrySendError::Full(_) => {
|
||||
debug!("Connection to {address} seems to not be able to handle all the traffic - dropping the current packet");
|
||||
// it's not a 'big' error, but we did not manage to send the packet
|
||||
// if the queue is full, we can't really do anything but to drop the packet
|
||||
io::Error::new(
|
||||
io::ErrorKind::WouldBlock,
|
||||
"connection queue is full",
|
||||
)
|
||||
}
|
||||
TrySendError::Closed(dropped) => {
|
||||
debug!(
|
||||
"Connection to {address} seems to be dead. attempting to re-establish it...",
|
||||
);
|
||||
|
||||
// it's not a 'big' error, but we did not manage to send the packet, but queue
|
||||
// it up to send it as soon as the connection is re-established
|
||||
self.make_connection(address, dropped);
|
||||
io::Error::new(
|
||||
io::ErrorKind::ConnectionAborted,
|
||||
"reconnection attempt is in progress",
|
||||
)
|
||||
}
|
||||
}
|
||||
} )
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,12 +304,15 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
fn dummy_client() -> Client {
|
||||
Client::new(Config {
|
||||
initial_reconnection_backoff: Duration::from_millis(10_000),
|
||||
maximum_reconnection_backoff: Duration::from_millis(300_000),
|
||||
initial_connection_timeout: Duration::from_millis(1_500),
|
||||
maximum_connection_buffer_size: 128,
|
||||
})
|
||||
Client::new(
|
||||
Config {
|
||||
initial_reconnection_backoff: Duration::from_millis(10_000),
|
||||
maximum_reconnection_backoff: Duration::from_millis(300_000),
|
||||
initial_connection_timeout: Duration::from_millis(1_500),
|
||||
maximum_connection_buffer_size: 128,
|
||||
},
|
||||
Default::default(),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -11,16 +11,16 @@ use crate::{
|
||||
use nym_api_requests::ecash::models::{
|
||||
AggregatedCoinIndicesSignatureResponse, AggregatedExpirationDateSignatureResponse,
|
||||
BatchRedeemTicketsBody, EcashBatchTicketRedemptionResponse, EcashTicketVerificationResponse,
|
||||
IssuedTicketbooksChallengeResponse, IssuedTicketbooksForResponse, SpentCredentialsResponse,
|
||||
VerifyEcashTicketBody,
|
||||
IssuedTicketbooksChallengeResponse, IssuedTicketbooksForResponse, VerifyEcashTicketBody,
|
||||
};
|
||||
use nym_api_requests::ecash::{
|
||||
BlindSignRequestBody, BlindedSignatureResponse, PartialCoinIndicesSignatureResponse,
|
||||
PartialExpirationDateSignatureResponse, VerificationKeyResponse,
|
||||
};
|
||||
use nym_api_requests::models::{
|
||||
ApiHealthResponse, GatewayBondAnnotated, GatewayCoreStatusResponse, MixnodeCoreStatusResponse,
|
||||
MixnodeStatusResponse, NymNodeDescription, RewardEstimationResponse, StakeSaturationResponse,
|
||||
ApiHealthResponse, GatewayBondAnnotated, GatewayCoreStatusResponse,
|
||||
HistoricalPerformanceResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse,
|
||||
NymNodeDescription, RewardEstimationResponse, StakeSaturationResponse,
|
||||
};
|
||||
use nym_api_requests::models::{LegacyDescribedGateway, MixNodeBondAnnotated};
|
||||
use nym_api_requests::nym_nodes::SkimmedNode;
|
||||
@@ -264,6 +264,31 @@ impl<C, S> Client<C, S> {
|
||||
Ok(self.nym_api.get_gateways_detailed_unfiltered().await?)
|
||||
}
|
||||
|
||||
pub async fn get_full_node_performance_history(
|
||||
&self,
|
||||
node_id: NodeId,
|
||||
) -> Result<Vec<HistoricalPerformanceResponse>, ValidatorClientError> {
|
||||
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
|
||||
let mut page = 0;
|
||||
let mut history = Vec::new();
|
||||
|
||||
loop {
|
||||
let mut res = self
|
||||
.nym_api
|
||||
.get_node_performance_history(node_id, Some(page), None)
|
||||
.await?;
|
||||
|
||||
history.append(&mut res.history.data);
|
||||
if history.len() < res.history.pagination.total {
|
||||
page += 1
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(history)
|
||||
}
|
||||
|
||||
// TODO: combine with NymApiClient...
|
||||
pub async fn get_all_cached_described_nodes(
|
||||
&self,
|
||||
@@ -621,13 +646,6 @@ impl NymApiClient {
|
||||
.await?)
|
||||
}
|
||||
|
||||
#[deprecated]
|
||||
pub async fn spent_credentials_filter(
|
||||
&self,
|
||||
) -> Result<SpentCredentialsResponse, ValidatorClientError> {
|
||||
Ok(self.nym_api.double_spending_filter_v1().await?)
|
||||
}
|
||||
|
||||
pub async fn partial_expiration_date_signatures(
|
||||
&self,
|
||||
expiration_date: Option<Date>,
|
||||
|
||||
@@ -65,6 +65,12 @@ pub enum EcashApiError {
|
||||
#[from]
|
||||
source: cosmrs::ErrorReport,
|
||||
},
|
||||
|
||||
#[error("nym api error")]
|
||||
NymApi {
|
||||
#[from]
|
||||
source: crate::ValidatorClientError,
|
||||
},
|
||||
}
|
||||
|
||||
impl TryFrom<ContractVKShare> for EcashApiClient {
|
||||
|
||||
@@ -13,7 +13,7 @@ use nym_api_requests::ecash::models::{
|
||||
use nym_api_requests::ecash::VerificationKeyResponse;
|
||||
use nym_api_requests::models::{
|
||||
AnnotationResponse, ApiHealthResponse, LegacyDescribedMixNode, NodePerformanceResponse,
|
||||
NodeRefreshBody, NymNodeDescription, RewardedSetResponse,
|
||||
NodeRefreshBody, NymNodeDescription, PerformanceHistoryResponse, RewardedSetResponse,
|
||||
};
|
||||
use nym_api_requests::nym_nodes::PaginatedCachedNodesResponse;
|
||||
use nym_api_requests::pagination::PaginatedResponse;
|
||||
@@ -31,6 +31,7 @@ pub use nym_api_requests::{
|
||||
StakeSaturationResponse, UptimeResponse,
|
||||
},
|
||||
nym_nodes::{CachedNodesResponse, SkimmedNode},
|
||||
NymNetworkDetailsResponse,
|
||||
};
|
||||
pub use nym_coconut_dkg_common::types::EpochId;
|
||||
use nym_contracts_common::IdentityKey;
|
||||
@@ -163,6 +164,35 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
async fn get_node_performance_history(
|
||||
&self,
|
||||
node_id: NodeId,
|
||||
page: Option<u32>,
|
||||
per_page: Option<u32>,
|
||||
) -> Result<PerformanceHistoryResponse, NymAPIError> {
|
||||
let mut params = Vec::new();
|
||||
|
||||
if let Some(page) = page {
|
||||
params.push(("page", page.to_string()))
|
||||
}
|
||||
|
||||
if let Some(per_page) = per_page {
|
||||
params.push(("per_page", per_page.to_string()))
|
||||
}
|
||||
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::NYM_NODES_ROUTES,
|
||||
routes::NYM_NODES_PERFORMANCE_HISTORY,
|
||||
&*node_id.to_string(),
|
||||
],
|
||||
¶ms,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
async fn get_nodes_described(
|
||||
&self,
|
||||
@@ -179,8 +209,15 @@ pub trait NymApiClientExt: ApiClient {
|
||||
params.push(("per_page", per_page.to_string()))
|
||||
}
|
||||
|
||||
self.get_json(&[routes::API_VERSION, "nym-nodes", "described"], ¶ms)
|
||||
.await
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::NYM_NODES_ROUTES,
|
||||
routes::NYM_NODES_DESCRIBED,
|
||||
],
|
||||
¶ms,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
@@ -199,8 +236,15 @@ pub trait NymApiClientExt: ApiClient {
|
||||
params.push(("per_page", per_page.to_string()))
|
||||
}
|
||||
|
||||
self.get_json(&[routes::API_VERSION, "nym-nodes", "bonded"], ¶ms)
|
||||
.await
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::NYM_NODES_ROUTES,
|
||||
routes::NYM_NODES_BONDED,
|
||||
],
|
||||
¶ms,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[deprecated]
|
||||
@@ -210,7 +254,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
"unstable",
|
||||
"nym-nodes",
|
||||
routes::NYM_NODES_ROUTES,
|
||||
"mixnodes",
|
||||
"skimmed",
|
||||
],
|
||||
@@ -226,7 +270,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
"unstable",
|
||||
"nym-nodes",
|
||||
routes::NYM_NODES_ROUTES,
|
||||
"gateways",
|
||||
"skimmed",
|
||||
],
|
||||
@@ -238,7 +282,11 @@ pub trait NymApiClientExt: ApiClient {
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_rewarded_set(&self) -> Result<RewardedSetResponse, NymAPIError> {
|
||||
self.get_json(
|
||||
&[routes::API_VERSION, "nym-nodes", "rewarded-set"],
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::NYM_NODES_ROUTES,
|
||||
routes::NYM_NODES_REWARDED_SET,
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
@@ -271,7 +319,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
"unstable",
|
||||
"nym-nodes",
|
||||
routes::NYM_NODES_ROUTES,
|
||||
"skimmed",
|
||||
"entry-gateways",
|
||||
"all",
|
||||
@@ -308,7 +356,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
"unstable",
|
||||
"nym-nodes",
|
||||
routes::NYM_NODES_ROUTES,
|
||||
"skimmed",
|
||||
"mixnodes",
|
||||
"active",
|
||||
@@ -345,7 +393,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
"unstable",
|
||||
"nym-nodes",
|
||||
routes::NYM_NODES_ROUTES,
|
||||
"skimmed",
|
||||
"mixnodes",
|
||||
"all",
|
||||
@@ -377,7 +425,12 @@ pub trait NymApiClientExt: ApiClient {
|
||||
}
|
||||
|
||||
self.get_json(
|
||||
&[routes::API_VERSION, "unstable", "nym-nodes", "skimmed"],
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
"unstable",
|
||||
routes::NYM_NODES_ROUTES,
|
||||
"skimmed",
|
||||
],
|
||||
¶ms,
|
||||
)
|
||||
.await
|
||||
@@ -686,8 +739,8 @@ pub trait NymApiClientExt: ApiClient {
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
"nym-nodes",
|
||||
"performance",
|
||||
routes::NYM_NODES_ROUTES,
|
||||
routes::NYM_NODES_PERFORMANCE,
|
||||
&node_id.to_string(),
|
||||
],
|
||||
NO_PARAMS,
|
||||
@@ -702,8 +755,8 @@ pub trait NymApiClientExt: ApiClient {
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
"nym-nodes",
|
||||
"annotation",
|
||||
routes::NYM_NODES_ROUTES,
|
||||
routes::NYM_NODES_ANNOTATION,
|
||||
&node_id.to_string(),
|
||||
],
|
||||
NO_PARAMS,
|
||||
@@ -797,20 +850,6 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[deprecated]
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn double_spending_filter_v1(&self) -> Result<SpentCredentialsResponse, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::ECASH_ROUTES,
|
||||
routes::DOUBLE_SPENDING_FILTER_V1,
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn partial_expiration_date_signatures(
|
||||
&self,
|
||||
@@ -927,7 +966,11 @@ pub trait NymApiClientExt: ApiClient {
|
||||
request: &NodeRefreshBody,
|
||||
) -> Result<(), NymAPIError> {
|
||||
self.post_json(
|
||||
&[routes::API_VERSION, "nym-nodes", "refresh-described"],
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::NYM_NODES_ROUTES,
|
||||
routes::NYM_NODES_REFRESH_DESCRIBED,
|
||||
],
|
||||
NO_PARAMS,
|
||||
request,
|
||||
)
|
||||
@@ -971,6 +1014,15 @@ pub trait NymApiClientExt: ApiClient {
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_network_details(&self) -> Result<NymNetworkDetailsResponse, NymAPIError> {
|
||||
self.get_json(
|
||||
&[routes::API_VERSION, routes::NETWORK, routes::DETAILS],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
|
||||
@@ -13,8 +13,6 @@ pub const DETAILED: &str = "detailed";
|
||||
pub const DETAILED_UNFILTERED: &str = "detailed-unfiltered";
|
||||
pub const ACTIVE: &str = "active";
|
||||
pub const REWARDED: &str = "rewarded";
|
||||
pub const DOUBLE_SPENDING_FILTER_V1: &str = "double-spending-filter-v1";
|
||||
|
||||
pub const ECASH_ROUTES: &str = "ecash";
|
||||
|
||||
pub use ecash::*;
|
||||
@@ -34,6 +32,19 @@ pub mod ecash {
|
||||
pub const EPOCH_ID_PARAM: &str = "epoch_id";
|
||||
}
|
||||
|
||||
pub const NYM_NODES_ROUTES: &str = "nym-nodes";
|
||||
|
||||
pub use nym_nodes::*;
|
||||
pub mod nym_nodes {
|
||||
pub const NYM_NODES_PERFORMANCE_HISTORY: &str = "performance-history";
|
||||
pub const NYM_NODES_PERFORMANCE: &str = "performance";
|
||||
pub const NYM_NODES_ANNOTATION: &str = "annotation";
|
||||
pub const NYM_NODES_DESCRIBED: &str = "described";
|
||||
pub const NYM_NODES_BONDED: &str = "bonded";
|
||||
pub const NYM_NODES_REWARDED_SET: &str = "rewarded-set";
|
||||
pub const NYM_NODES_REFRESH_DESCRIBED: &str = "refresh-described";
|
||||
}
|
||||
|
||||
pub const STATUS_ROUTES: &str = "status";
|
||||
pub const API_STATUS_ROUTES: &str = "api-status";
|
||||
pub const HEALTH: &str = "health";
|
||||
@@ -54,6 +65,8 @@ pub const STAKE_SATURATION: &str = "stake-saturation";
|
||||
pub const INCLUSION_CHANCE: &str = "inclusion-probability";
|
||||
pub const SUBMIT_GATEWAY: &str = "submit-gateway-monitoring-results";
|
||||
pub const SUBMIT_NODE: &str = "submit-node-monitoring-results";
|
||||
pub const PERFORMANCE: &str = "performance";
|
||||
|
||||
pub const SERVICE_PROVIDERS: &str = "services";
|
||||
|
||||
pub const DETAILS: &str = "details";
|
||||
pub const NETWORK: &str = "network";
|
||||
|
||||
+31
@@ -153,13 +153,20 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
||||
let req = QueryAllBalancesRequest {
|
||||
address: address.to_string(),
|
||||
pagination,
|
||||
resolve_denom: false,
|
||||
};
|
||||
|
||||
let mut res = self
|
||||
.make_abci_query::<_, QueryAllBalancesResponse>(path.clone(), req)
|
||||
.await?;
|
||||
|
||||
let early_break = res.balances.is_empty();
|
||||
raw_balances.append(&mut res.balances);
|
||||
|
||||
if early_break {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(next_key) = next_page_key(res.pagination) {
|
||||
pagination = Some(create_pagination(next_key))
|
||||
} else {
|
||||
@@ -187,7 +194,13 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
||||
.make_abci_query::<_, QueryTotalSupplyResponse>(path.clone(), req)
|
||||
.await?;
|
||||
|
||||
let early_break = res.supply.is_empty();
|
||||
supply.append(&mut res.supply);
|
||||
|
||||
if early_break {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(next_key) = next_page_key(res.pagination) {
|
||||
pagination = Some(create_pagination(next_key))
|
||||
} else {
|
||||
@@ -328,7 +341,13 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
||||
.make_abci_query::<_, QueryCodesResponse>(path.clone(), req)
|
||||
.await?;
|
||||
|
||||
let early_break = res.code_infos.is_empty();
|
||||
raw_codes.append(&mut res.code_infos);
|
||||
|
||||
if early_break {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(next_key) = next_page_key(res.pagination) {
|
||||
pagination = Some(create_pagination(next_key))
|
||||
} else {
|
||||
@@ -373,7 +392,13 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
||||
.make_abci_query::<_, QueryContractsByCodeResponse>(path.clone(), req)
|
||||
.await?;
|
||||
|
||||
let early_break = res.contracts.is_empty();
|
||||
raw_contracts.append(&mut res.contracts);
|
||||
|
||||
if early_break {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(next_key) = next_page_key(res.pagination) {
|
||||
pagination = Some(create_pagination(next_key))
|
||||
} else {
|
||||
@@ -429,7 +454,13 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
||||
.make_abci_query::<_, QueryContractHistoryResponse>(path.clone(), req)
|
||||
.await?;
|
||||
|
||||
let early_break = res.entries.is_empty();
|
||||
raw_entries.append(&mut res.entries);
|
||||
|
||||
if early_break {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(next_key) = next_page_key(res.pagination) {
|
||||
pagination = Some(create_pagination(next_key))
|
||||
} else {
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
use crate::rpc::TendermintRpcClient;
|
||||
use async_trait::async_trait;
|
||||
use base64::Engine;
|
||||
use cosmrs::tendermint;
|
||||
use cosmrs::tendermint::{block::Height, evidence::Evidence, Hash};
|
||||
use reqwest::header::HeaderMap;
|
||||
use reqwest::{header, RequestBuilder};
|
||||
use tendermint_rpc::dialect::{v0_34, v0_37, v0_38, LatestDialect};
|
||||
use tendermint_rpc::{
|
||||
client::CompatMode,
|
||||
dialect::{self, Dialect},
|
||||
@@ -21,8 +23,21 @@ macro_rules! perform_with_compat {
|
||||
($self:expr, $request:expr) => {{
|
||||
let request = $request;
|
||||
match $self.compat {
|
||||
CompatMode::V0_37 => $self.perform_v0_37(request).await,
|
||||
CompatMode::V0_34 => $self.perform_v0_34(request).await,
|
||||
CompatMode::V0_38 => {
|
||||
$self
|
||||
.perform_request_with_dialect(request, dialect::v0_38::Dialect)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_37 => {
|
||||
$self
|
||||
.perform_request_with_dialect(request, dialect::v0_37::Dialect)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_34 => {
|
||||
$self
|
||||
.perform_request_with_dialect(request, dialect::v0_34::Dialect)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
@@ -70,7 +85,11 @@ impl ReqwestRpcClient {
|
||||
.headers(headers)
|
||||
}
|
||||
|
||||
async fn perform_request<R, S>(&self, request: R) -> Result<R::Output, Error>
|
||||
async fn perform_request_with_dialect<R, S>(
|
||||
&self,
|
||||
request: R,
|
||||
_dialect: S,
|
||||
) -> Result<R::Output, Error>
|
||||
where
|
||||
R: SimpleRequest<S>,
|
||||
S: Dialect,
|
||||
@@ -81,26 +100,25 @@ impl ReqwestRpcClient {
|
||||
.send()
|
||||
.await
|
||||
.map_err(TendermintRpcErrorMap::into_rpc_err)?;
|
||||
let response_status = response.status();
|
||||
let bytes = response
|
||||
.bytes()
|
||||
.await
|
||||
.map_err(TendermintRpcErrorMap::into_rpc_err)?;
|
||||
|
||||
// Successful JSON-RPC requests are expected to return a 200 OK HTTP status.
|
||||
// Otherwise, this means that the HTTP request failed as a whole,
|
||||
// as opposed to the JSON-RPC request returning an error,
|
||||
// and we cannot expect the response body to be a valid JSON-RPC response.
|
||||
if response_status != reqwest::StatusCode::OK {
|
||||
// hehe, that's so nasty but we have to somehow convert between different versions of the same lib
|
||||
return Err(Error::http_request_failed(
|
||||
response_status.as_u16().try_into().unwrap(),
|
||||
));
|
||||
}
|
||||
|
||||
R::Response::from_string(bytes).map(Into::into)
|
||||
}
|
||||
|
||||
async fn perform_v0_34<R>(&self, request: R) -> Result<R::Output, Error>
|
||||
where
|
||||
R: SimpleRequest<dialect::v0_34::Dialect>,
|
||||
{
|
||||
self.perform_request(request).await
|
||||
}
|
||||
|
||||
async fn perform_v0_37<R>(&self, request: R) -> Result<R::Output, Error>
|
||||
where
|
||||
R: SimpleRequest<dialect::v0_37::Dialect>,
|
||||
{
|
||||
self.perform_request(request).await
|
||||
}
|
||||
}
|
||||
|
||||
trait TendermintRpcErrorMap {
|
||||
@@ -120,18 +138,50 @@ impl TendermintRpcClient for ReqwestRpcClient {
|
||||
where
|
||||
R: SimpleRequest,
|
||||
{
|
||||
self.perform_request(request).await
|
||||
self.perform_request_with_dialect(request, LatestDialect)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn block_results<H>(&self, height: H) -> Result<block_results::Response, Error>
|
||||
async fn block<H>(&self, height: H) -> Result<endpoint::block::Response, Error>
|
||||
where
|
||||
H: Into<Height> + Send,
|
||||
{
|
||||
perform_with_compat!(self, block_results::Request::new(height.into()))
|
||||
perform_with_compat!(self, endpoint::block::Request::new(height.into()))
|
||||
}
|
||||
|
||||
async fn latest_block_results(&self) -> Result<block_results::Response, Error> {
|
||||
perform_with_compat!(self, block_results::Request::default())
|
||||
async fn block_by_hash(
|
||||
&self,
|
||||
hash: tendermint::Hash,
|
||||
) -> Result<endpoint::block_by_hash::Response, Error> {
|
||||
perform_with_compat!(self, endpoint::block_by_hash::Request::new(hash))
|
||||
}
|
||||
|
||||
async fn latest_block(&self) -> Result<endpoint::block::Response, Error> {
|
||||
perform_with_compat!(self, endpoint::block::Request::default())
|
||||
}
|
||||
|
||||
async fn block_results<H>(&self, height: H) -> Result<endpoint::block_results::Response, Error>
|
||||
where
|
||||
H: Into<Height> + Send,
|
||||
{
|
||||
perform_with_compat!(self, endpoint::block_results::Request::new(height.into()))
|
||||
}
|
||||
|
||||
async fn latest_block_results(&self) -> Result<endpoint::block_results::Response, Error> {
|
||||
perform_with_compat!(self, endpoint::block_results::Request::default())
|
||||
}
|
||||
|
||||
async fn block_search(
|
||||
&self,
|
||||
query: Query,
|
||||
page: u32,
|
||||
per_page: u8,
|
||||
order: Order,
|
||||
) -> Result<endpoint::block_search::Response, Error> {
|
||||
perform_with_compat!(
|
||||
self,
|
||||
endpoint::block_search::Request::new(query, page, per_page, order)
|
||||
)
|
||||
}
|
||||
|
||||
async fn header<H>(&self, height: H) -> Result<endpoint::header::Response, Error>
|
||||
@@ -140,11 +190,26 @@ impl TendermintRpcClient for ReqwestRpcClient {
|
||||
{
|
||||
let height = height.into();
|
||||
match self.compat {
|
||||
CompatMode::V0_37 => self.perform(endpoint::header::Request::new(height)).await,
|
||||
CompatMode::V0_38 => {
|
||||
self.perform_request_with_dialect(
|
||||
endpoint::header::Request::new(height),
|
||||
v0_38::Dialect,
|
||||
)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_37 => {
|
||||
self.perform_request_with_dialect(
|
||||
endpoint::header::Request::new(height),
|
||||
v0_37::Dialect,
|
||||
)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_34 => {
|
||||
// Back-fill with a request to /block endpoint and
|
||||
// taking just the header from the response.
|
||||
let resp = self.perform_v0_34(block::Request::new(height)).await?;
|
||||
let resp = self
|
||||
.perform_request_with_dialect(block::Request::new(height), v0_34::Dialect)
|
||||
.await?;
|
||||
Ok(resp.into())
|
||||
}
|
||||
}
|
||||
@@ -152,12 +217,25 @@ impl TendermintRpcClient for ReqwestRpcClient {
|
||||
|
||||
async fn header_by_hash(&self, hash: Hash) -> Result<header_by_hash::Response, Error> {
|
||||
match self.compat {
|
||||
CompatMode::V0_37 => self.perform(header_by_hash::Request::new(hash)).await,
|
||||
CompatMode::V0_38 => {
|
||||
self.perform_request_with_dialect(
|
||||
header_by_hash::Request::new(hash),
|
||||
v0_38::Dialect,
|
||||
)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_37 => {
|
||||
self.perform_request_with_dialect(
|
||||
header_by_hash::Request::new(hash),
|
||||
v0_37::Dialect,
|
||||
)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_34 => {
|
||||
// Back-fill with a request to /block_by_hash endpoint and
|
||||
// taking just the header from the response.
|
||||
let resp = self
|
||||
.perform_v0_34(block_by_hash::Request::new(hash))
|
||||
.perform_request_with_dialect(block_by_hash::Request::new(hash), v0_34::Dialect)
|
||||
.await?;
|
||||
Ok(resp.into())
|
||||
}
|
||||
@@ -167,8 +245,18 @@ impl TendermintRpcClient for ReqwestRpcClient {
|
||||
/// `/broadcast_evidence`: broadcast an evidence.
|
||||
async fn broadcast_evidence(&self, e: Evidence) -> Result<evidence::Response, Error> {
|
||||
match self.compat {
|
||||
CompatMode::V0_37 => self.perform(evidence::Request::new(e)).await,
|
||||
CompatMode::V0_34 => self.perform_v0_34(evidence::Request::new(e)).await,
|
||||
CompatMode::V0_38 => {
|
||||
self.perform_request_with_dialect(evidence::Request::new(e), v0_38::Dialect)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_37 => {
|
||||
self.perform_request_with_dialect(evidence::Request::new(e), v0_37::Dialect)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_34 => {
|
||||
self.perform_request_with_dialect(evidence::Request::new(e), v0_34::Dialect)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ cosmwasm-std = { workspace = true }
|
||||
cosmwasm-schema = { workspace = true }
|
||||
cw-storage-plus = { workspace = true }
|
||||
schemars = { workspace = true }
|
||||
utoipa = { workspace = true, optional = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
@@ -23,4 +24,5 @@ serde_json = { workspace = true }
|
||||
vergen = { workspace = true, features = ["build", "git", "gitcl", "rustc", "cargo"] }
|
||||
|
||||
[features]
|
||||
naive_float = []
|
||||
naive_float = []
|
||||
utoipa = ["dep:utoipa"]
|
||||
|
||||
@@ -221,6 +221,7 @@ fn default_unknown() -> String {
|
||||
// TODO: there's no reason this couldn't be used for proper binaries, but in that case
|
||||
// perhaps the struct should get renamed and moved to a "more" common crate
|
||||
#[cw_serde]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct ContractBuildInformation {
|
||||
/// Provides the name of the binary, i.e. the content of `CARGO_PKG_NAME` environmental variable.
|
||||
#[serde(default = "default_unknown")]
|
||||
|
||||
@@ -42,9 +42,11 @@ pub struct Gateway {
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct GatewayBond {
|
||||
/// Original amount pledged by the operator of this node.
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = crate::CoinSchema))]
|
||||
pub pledge_amount: Coin,
|
||||
|
||||
/// Address of the owner of this gateway.
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||
pub owner: Addr,
|
||||
|
||||
/// Block height at which this gateway has been bonded.
|
||||
@@ -55,6 +57,7 @@ pub struct GatewayBond {
|
||||
|
||||
/// Entity who bonded this gateway on behalf of the owner.
|
||||
/// If exists, it's most likely the address of the vesting contract.
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||
pub proxy: Option<Addr>,
|
||||
}
|
||||
|
||||
|
||||
@@ -82,20 +82,25 @@ impl MixNodeDetails {
|
||||
|
||||
// currently this struct is shared between mixnodes and nymnodes
|
||||
#[cw_serde]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct NodeRewarding {
|
||||
/// Information provided by the operator that influence the cost function.
|
||||
pub cost_params: NodeCostParams,
|
||||
|
||||
/// Total pledge and compounded reward earned by the node operator.
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||
pub operator: Decimal,
|
||||
|
||||
/// Total delegation and compounded reward earned by all node delegators.
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||
pub delegates: Decimal,
|
||||
|
||||
/// Cumulative reward earned by the "unit delegation" since the block 0.
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||
pub total_unit_reward: Decimal,
|
||||
|
||||
/// Value of the theoretical "unit delegation" that has delegated to this node at block 0.
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||
pub unit_delegation: Decimal,
|
||||
|
||||
/// Marks the epoch when this node was last rewarded so that we wouldn't accidentally attempt
|
||||
@@ -492,14 +497,17 @@ impl NodeRewarding {
|
||||
::cosmwasm_schema::schemars::JsonSchema,
|
||||
)]
|
||||
#[schemars(crate = "::cosmwasm_schema::schemars")]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct MixNodeBond {
|
||||
/// Unique id assigned to the bonded mixnode.
|
||||
pub mix_id: NodeId,
|
||||
|
||||
/// Address of the owner of this mixnode.
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||
pub owner: Addr,
|
||||
|
||||
/// Original amount pledged by the operator of this node.
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = crate::CoinSchema))]
|
||||
pub original_pledge: Coin,
|
||||
|
||||
// REMOVED (but might be needed due to legacy things, idk yet)
|
||||
@@ -510,6 +518,7 @@ pub struct MixNodeBond {
|
||||
|
||||
/// Entity who bonded this mixnode on behalf of the owner.
|
||||
/// If exists, it's most likely the address of the vesting contract.
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = Option<String>))]
|
||||
pub proxy: Option<Addr>,
|
||||
|
||||
/// Block height at which this mixnode has been bonded.
|
||||
@@ -545,6 +554,7 @@ impl MixNodeBond {
|
||||
feature = "generate-ts",
|
||||
ts(export, export_to = "ts-packages/types/src/types/rust/Mixnode.ts")
|
||||
)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct MixNode {
|
||||
/// Network address of this mixnode, for example 1.1.1.1 or foo.mixnode.com
|
||||
pub host: String,
|
||||
@@ -571,11 +581,14 @@ pub struct MixNode {
|
||||
/// The cost parameters, or the cost function, defined for the particular mixnode that influences
|
||||
/// how the rewards should be split between the node operator and its delegators.
|
||||
#[cw_serde]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct NodeCostParams {
|
||||
/// The profit margin of the associated node, i.e. the desired percent of the reward to be distributed to the operator.
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||
pub profit_margin_percent: Percent,
|
||||
|
||||
/// Operating cost of the associated node per the entire interval.
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = crate::CoinSchema))]
|
||||
pub interval_operating_cost: Coin,
|
||||
}
|
||||
|
||||
@@ -680,7 +693,9 @@ pub struct PendingMixNodeChanges {
|
||||
}
|
||||
|
||||
#[derive(Default, Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct LegacyPendingMixNodeChanges {
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = Option<u32>))]
|
||||
pub pledge_change: Option<EpochEventId>,
|
||||
}
|
||||
|
||||
|
||||
@@ -863,11 +863,4 @@ pub enum QueryMsg {
|
||||
pub struct MigrateMsg {
|
||||
pub unsafe_skip_state_updates: Option<bool>,
|
||||
pub vesting_contract_address: Option<String>,
|
||||
pub current_nym_node_semver: String,
|
||||
|
||||
#[serde(default)]
|
||||
pub version_score_weights: OutdatedVersionWeights,
|
||||
|
||||
#[serde(default)]
|
||||
pub version_score_params: VersionScoreFormulaParams,
|
||||
}
|
||||
|
||||
@@ -113,6 +113,10 @@ impl Role {
|
||||
pub fn is_standby(&self) -> bool {
|
||||
matches!(self, Role::Standby)
|
||||
}
|
||||
|
||||
pub fn is_mixnode(&self) -> bool {
|
||||
matches!(self, Role::Layer1 | Role::Layer2 | Role::Layer3)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Role {
|
||||
@@ -231,6 +235,7 @@ pub struct RoleMetadata {
|
||||
|
||||
/// Full details associated with given node.
|
||||
#[cw_serde]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct NymNodeDetails {
|
||||
/// Basic bond information of this node, such as owner address, original pledge, etc.
|
||||
pub bond_information: NymNodeBond,
|
||||
@@ -288,14 +293,19 @@ impl NymNodeDetails {
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct NymNodeBond {
|
||||
/// Unique id assigned to the bonded node.
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = u32))]
|
||||
pub node_id: NodeId,
|
||||
|
||||
/// Address of the owner of this nym-node.
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||
pub owner: Addr,
|
||||
|
||||
/// Original amount pledged by the operator of this node.
|
||||
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = crate::CoinSchema))]
|
||||
pub original_pledge: Coin,
|
||||
|
||||
/// Block height at which this nym-node has been bonded.
|
||||
@@ -348,6 +358,7 @@ impl NymNodeBond {
|
||||
feature = "generate-ts",
|
||||
ts(export, export_to = "ts-packages/types/src/types/rust/NymNode.ts")
|
||||
)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct NymNode {
|
||||
/// Network address of this nym-node, for example 1.1.1.1 or foo.mixnode.com
|
||||
/// that is used to discover other capabilities of this node.
|
||||
@@ -358,6 +369,7 @@ pub struct NymNode {
|
||||
pub custom_http_port: Option<u16>,
|
||||
|
||||
/// Base58-encoded ed25519 EdDSA public key.
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||
pub identity_key: IdentityKey,
|
||||
// TODO: I don't think we want to include sphinx keys here,
|
||||
// given we want to rotate them and keeping that in sync with contract will be a PITA
|
||||
@@ -435,8 +447,11 @@ pub struct NodeConfigUpdate {
|
||||
export_to = "ts-packages/types/src/types/rust/PendingNodeChanges.ts"
|
||||
)
|
||||
)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct PendingNodeChanges {
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = Option<u32>))]
|
||||
pub pledge_change: Option<EpochEventId>,
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = Option<u32>))]
|
||||
pub cost_params_change: Option<IntervalEventId>,
|
||||
}
|
||||
|
||||
|
||||
@@ -21,31 +21,37 @@ pub type WorkFactor = Decimal;
|
||||
)]
|
||||
#[cw_serde]
|
||||
#[derive(Copy)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct IntervalRewardParams {
|
||||
/// Current value of the rewarding pool.
|
||||
/// It is expected to be constant throughout the interval.
|
||||
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||
pub reward_pool: Decimal,
|
||||
|
||||
/// Current value of the staking supply.
|
||||
/// It is expected to be constant throughout the interval.
|
||||
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||
pub staking_supply: Decimal,
|
||||
|
||||
/// Defines the percentage of stake needed to reach saturation for all of the nodes in the rewarded set.
|
||||
/// Also known as `beta`.
|
||||
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||
pub staking_supply_scale_factor: Percent,
|
||||
|
||||
// computed values
|
||||
/// Current value of the computed reward budget per epoch, per node.
|
||||
/// It is expected to be constant throughout the interval.
|
||||
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||
pub epoch_reward_budget: Decimal,
|
||||
|
||||
/// Current value of the stake saturation point.
|
||||
/// It is expected to be constant throughout the interval.
|
||||
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||
pub stake_saturation_point: Decimal,
|
||||
|
||||
// constants(-ish)
|
||||
@@ -54,6 +60,7 @@ pub struct IntervalRewardParams {
|
||||
/// It is not really expected to be changing very often.
|
||||
/// As a matter of fact, unless there's a very specific reason, it should remain constant.
|
||||
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||
pub sybil_resistance: Percent,
|
||||
|
||||
// default: 10
|
||||
@@ -61,6 +68,7 @@ pub struct IntervalRewardParams {
|
||||
/// It is not really expected to be changing very often.
|
||||
/// As a matter of fact, unless there's a very specific reason, it should remain constant.
|
||||
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||
pub active_set_work_factor: Decimal,
|
||||
|
||||
// default: 2%
|
||||
@@ -70,6 +78,7 @@ pub struct IntervalRewardParams {
|
||||
/// It is not really expected to be changing very often.
|
||||
/// As a matter of fact, unless there's a very specific reason, it should remain constant.
|
||||
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||
pub interval_pool_emission: Percent,
|
||||
}
|
||||
|
||||
@@ -90,6 +99,7 @@ impl IntervalRewardParams {
|
||||
)]
|
||||
#[cw_serde]
|
||||
#[derive(Copy)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct RewardingParams {
|
||||
/// Parameters that should remain unchanged throughout an interval.
|
||||
pub interval: IntervalRewardParams,
|
||||
@@ -254,6 +264,7 @@ impl RewardingParams {
|
||||
)]
|
||||
#[cw_serde]
|
||||
#[derive(Copy)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct RewardedSetParams {
|
||||
/// The expected number of nodes assigned entry gateway role (i.e. [`Role::EntryGateway`])
|
||||
pub entry_gateways: u32,
|
||||
|
||||
@@ -17,10 +17,12 @@ pub mod simulator;
|
||||
)]
|
||||
#[cw_serde]
|
||||
#[derive(Copy, Default)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct RewardEstimate {
|
||||
/// The amount of **decimal** coins that are going to get distributed to the node,
|
||||
/// i.e. the operator and all its delegators.
|
||||
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||
pub total_node_reward: Decimal,
|
||||
|
||||
// note that operator reward includes the operating_cost,
|
||||
@@ -28,14 +30,17 @@ pub struct RewardEstimate {
|
||||
// in that case the operator reward would still be `1nym` as opposed to 0
|
||||
/// The share of the reward that is going to get distributed to the node operator.
|
||||
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||
pub operator: Decimal,
|
||||
|
||||
/// The share of the reward that is going to get distributed among the node delegators.
|
||||
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||
pub delegates: Decimal,
|
||||
|
||||
/// The operating cost of this node. Note: it's already included in the operator reward.
|
||||
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||
pub operating_cost: Decimal,
|
||||
}
|
||||
|
||||
|
||||
@@ -175,6 +175,14 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "utoipa")]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[cfg_attr(feature = "utoipa", schema(title = "Coin"))]
|
||||
pub struct CoinSchema {
|
||||
pub denom: String,
|
||||
pub amount: String,
|
||||
}
|
||||
|
||||
/// The current state of the mixnet contract.
|
||||
#[cw_serde]
|
||||
pub struct ContractState {
|
||||
|
||||
@@ -8,81 +8,81 @@ use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug, PartialEq)]
|
||||
pub enum VestingContractError {
|
||||
#[error("VESTING ({}): {0}", line!())]
|
||||
#[error("VESTING ({l}): {0}", l = line!())]
|
||||
Std(#[from] StdError),
|
||||
|
||||
#[error("VESTING: {0}")]
|
||||
OverflowError(#[from] OverflowError),
|
||||
|
||||
#[error("VESTING ({}): Account does not exist - {0}", line!())]
|
||||
#[error("VESTING ({l}): Account does not exist - {0}", l = line!())]
|
||||
NoAccountForAddress(String),
|
||||
|
||||
#[error("VESTING ({}): Only admin can perform this action, {0} is not admin", line!())]
|
||||
#[error("VESTING ({l}): Only admin can perform this action, {0} is not admin", l = line!())]
|
||||
NotAdmin(String),
|
||||
|
||||
#[error("VESTING ({}): Balance not found for existing account ({0}), this is a bug", line!())]
|
||||
#[error("VESTING ({l}): Balance not found for existing account ({0}), this is a bug", l = line!())]
|
||||
NoBalanceForAddress(String),
|
||||
|
||||
#[error("VESTING ({}): Insufficient balance for address {0} -> {1}", line!())]
|
||||
#[error("VESTING ({l}): Insufficient balance for address {0} -> {1}", l = line!())]
|
||||
InsufficientBalance(String, u128),
|
||||
|
||||
#[error("VESTING ({}): Insufficient spendable balance for address {0} -> {1}", line!())]
|
||||
#[error("VESTING ({l}): Insufficient spendable balance for address {0} -> {1}", l = line!())]
|
||||
InsufficientSpendable(String, u128),
|
||||
|
||||
#[error(
|
||||
"VESTING ({}):Only delegation owner can perform delegation actions, {0} is not the delegation owner"
|
||||
, line!())]
|
||||
"VESTING ({l}):Only delegation owner can perform delegation actions, {0} is not the delegation owner"
|
||||
, l = line!())]
|
||||
NotDelegate(String),
|
||||
|
||||
#[error("VESTING ({}): Total vesting amount is inprobably low -> {0}, this is likely an error", line!())]
|
||||
#[error("VESTING ({l}): Total vesting amount is inprobably low -> {0}, this is likely an error", l = line!())]
|
||||
ImprobableVestingAmount(u128),
|
||||
|
||||
#[error("VESTING ({}): Address {0} has already bonded a node", line!())]
|
||||
#[error("VESTING ({l}): Address {0} has already bonded a node", l = line!())]
|
||||
AlreadyBonded(String),
|
||||
|
||||
#[error("VESTING ({}): Received empty funds vector", line!())]
|
||||
#[error("VESTING ({l}): Received empty funds vector", l = line!())]
|
||||
EmptyFunds,
|
||||
|
||||
#[error("VESTING ({}): Received wrong denom: {0}, expected {1}", line!())]
|
||||
#[error("VESTING ({l}): Received wrong denom: {0}, expected {1}", l = line!())]
|
||||
WrongDenom(String, String),
|
||||
|
||||
#[error("VESTING ({}): Received multiple denoms, expected 1", line!())]
|
||||
#[error("VESTING ({l}): Received multiple denoms, expected 1", l = line!())]
|
||||
MultipleDenoms,
|
||||
|
||||
#[error("VESTING ({}): No delegations found for account {0}, mix_identity {1}", line!())]
|
||||
#[error("VESTING ({l}): No delegations found for account {0}, mix_identity {1}", l = line!())]
|
||||
NoSuchDelegation(Addr, NodeId),
|
||||
|
||||
#[error("VESTING ({}): Only mixnet contract can perform this operation, got {0}", line!())]
|
||||
#[error("VESTING ({l}): Only mixnet contract can perform this operation, got {0}", l = line!())]
|
||||
NotMixnetContract(Addr),
|
||||
|
||||
#[error("VESTING ({}): Calculation underflowed", line!())]
|
||||
#[error("VESTING ({l}): Calculation underflowed", l = line!())]
|
||||
Underflow,
|
||||
|
||||
#[error("VESTING ({}): No bond found for account {0}", line!())]
|
||||
#[error("VESTING ({l}): No bond found for account {0}", l = line!())]
|
||||
NoBondFound(String),
|
||||
|
||||
#[error("VESTING: Attempted to reduce mixnode bond pledge below zero! The current pledge is {current} and we attempted to reduce it by {decrease_by}.")]
|
||||
InvalidBondPledgeReduction { current: Coin, decrease_by: Coin },
|
||||
|
||||
#[error("VESTING ({}): Action can only be executed by account owner -> {0}", line!())]
|
||||
#[error("VESTING ({l}): Action can only be executed by account owner -> {0}", l = line!())]
|
||||
NotOwner(String),
|
||||
|
||||
#[error("VESTING ({}): Invalid address: {0}", line!())]
|
||||
#[error("VESTING ({l}): Invalid address: {0}", l = line!())]
|
||||
InvalidAddress(String),
|
||||
|
||||
#[error("VESTING ({}): Account already exists: {0}", line!())]
|
||||
#[error("VESTING ({l}): Account already exists: {0}", l = line!())]
|
||||
AccountAlreadyExists(String),
|
||||
|
||||
#[error("VESTING ({}): Staking account already exists: {0}", line!())]
|
||||
#[error("VESTING ({l}): Staking account already exists: {0}", l = line!())]
|
||||
StakingAccountAlreadyExists(String),
|
||||
|
||||
#[error("VESTING ({}): Too few coins sent for vesting account creation, sent {sent}, need at least {need}", line!())]
|
||||
#[error("VESTING ({l}): Too few coins sent for vesting account creation, sent {sent}, need at least {need}", l = line!())]
|
||||
MinVestingFunds { sent: u128, need: u128 },
|
||||
|
||||
#[error("VESTING ({}): Maximum amount of locked coins has already been pledged: {current}, cap is {cap}", line!())]
|
||||
#[error("VESTING ({l}): Maximum amount of locked coins has already been pledged: {current}, cap is {cap}", l = line!())]
|
||||
LockedPledgeCapReached { current: Uint128, cap: Uint128 },
|
||||
|
||||
#[error("VESTING: ({}: Account owned by {owner} has unpopulated vesting periods!", line!())]
|
||||
#[error("VESTING: ({l}: Account owned by {owner} has unpopulated vesting periods!", l = line!())]
|
||||
UnpopulatedVestingPeriods { owner: Addr },
|
||||
|
||||
#[error("VESTING: Vesting account associated with {0} already exists, only addresses with not existing vesting accounts can be added as staking addresses")]
|
||||
|
||||
@@ -19,7 +19,7 @@ use std::error::Error;
|
||||
// `SELECT total_tickets, used_tickets FROM ecash_ticketbook WHERE expiration_date >= ?`, today_date
|
||||
// then for each calculate the diff total_tickets - used_tickets and multiply the result by the size of the ticket
|
||||
#[async_trait]
|
||||
pub trait Storage: Send + Sync {
|
||||
pub trait Storage: Clone + Send + Sync {
|
||||
type StorageError: Error;
|
||||
|
||||
async fn close(&self);
|
||||
|
||||
@@ -26,7 +26,6 @@ nym-api-requests = { path = "../../nym-api/nym-api-requests" }
|
||||
nym-credentials = { path = "../credentials" }
|
||||
nym-credentials-interface = { path = "../credentials-interface" }
|
||||
nym-ecash-contract-common = { path = "../cosmwasm-smart-contracts/ecash-contract" }
|
||||
nym-ecash-double-spending = { path = "../ecash-double-spending" }
|
||||
nym-gateway-requests = { path = "../gateway-requests" }
|
||||
nym-gateway-storage = { path = "../gateway-storage" }
|
||||
nym-task = { path = "../task" }
|
||||
|
||||
@@ -13,6 +13,7 @@ use nym_api_requests::constants::MIN_BATCH_REDEMPTION_DELAY;
|
||||
use nym_api_requests::ecash::models::{BatchRedeemTicketsBody, VerifyEcashTicketBody};
|
||||
use nym_credentials_interface::Bandwidth;
|
||||
use nym_credentials_interface::{ClientTicket, TicketType};
|
||||
use nym_validator_client::coconut::EcashApiError;
|
||||
use nym_validator_client::nym_api::EpochId;
|
||||
use nym_validator_client::nyxd::contract_traits::{
|
||||
EcashSigningClient, MultisigQueryClient, MultisigSigningClient, PagedMultisigQueryClient,
|
||||
@@ -352,7 +353,9 @@ impl CredentialHandler {
|
||||
}
|
||||
Err(err) => {
|
||||
error!("failed to send ticket {ticket_id} for verification to ecash signer '{client}': {err}. if we don't reach quorum, we'll retry later");
|
||||
Ok(false)
|
||||
Err(EcashTicketError::ApiFailure(EcashApiError::NymApi {
|
||||
source: err,
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ serde = { workspace = true, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
strum = { workspace = true, features = ["derive"] }
|
||||
time = { workspace = true, features = ["serde"] }
|
||||
utoipa = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
|
||||
nym-compact-ecash = { path = "../nym_offline_compact_ecash" }
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
[package]
|
||||
name = "nym-ecash-double-spending"
|
||||
version = "0.1.0"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bit-vec = { workspace = true }
|
||||
bloomfilter = { workspace = true }
|
||||
nym-network-defaults = { path = "../network-defaults" }
|
||||
@@ -1,136 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use bit_vec::BitVec;
|
||||
use bloomfilter::Bloom;
|
||||
use nym_network_defaults::{BloomfilterParameters, ECASH_DS_BLOOMFILTER_PARAMS};
|
||||
|
||||
pub struct DoubleSpendingFilter {
|
||||
params: BloomfilterParameters,
|
||||
inner: Bloom<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl Default for DoubleSpendingFilter {
|
||||
fn default() -> Self {
|
||||
DoubleSpendingFilter::new_empty_ecash()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bloom_from_params<T>(params: &BloomfilterParameters, bitvec: BitVec) -> Bloom<Vec<T>> {
|
||||
assert_eq!(params.bitmap_size, bitvec.len() as u64);
|
||||
|
||||
Bloom::from_bit_vec(
|
||||
bitvec,
|
||||
params.bitmap_size,
|
||||
params.num_hashes,
|
||||
params.sip_keys,
|
||||
)
|
||||
}
|
||||
|
||||
impl DoubleSpendingFilter {
|
||||
pub fn new_empty(params: BloomfilterParameters) -> Self {
|
||||
let bitvec = BitVec::from_elem(params.bitmap_size as usize, false);
|
||||
DoubleSpendingFilter {
|
||||
inner: bloom_from_params(¶ms, bitvec),
|
||||
params,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn params(&self) -> BloomfilterParameters {
|
||||
self.params
|
||||
}
|
||||
|
||||
pub fn rebuild(&self) -> DoubleSpendingFilterBuilder {
|
||||
DoubleSpendingFilterBuilder::new(self.params)
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.inner.clear()
|
||||
}
|
||||
|
||||
pub fn new_empty_ecash() -> Self {
|
||||
DoubleSpendingFilter::new_empty(ECASH_DS_BLOOMFILTER_PARAMS)
|
||||
}
|
||||
|
||||
pub fn builder(params: BloomfilterParameters) -> DoubleSpendingFilterBuilder {
|
||||
DoubleSpendingFilterBuilder::new(params)
|
||||
}
|
||||
|
||||
pub fn from_bytes(params: BloomfilterParameters, bitmap: &[u8]) -> Self {
|
||||
DoubleSpendingFilter {
|
||||
inner: bloom_from_params(¶ms, BitVec::from_bytes(bitmap)),
|
||||
params,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn replace_bitvec(&mut self, new: BitVec) {
|
||||
self.inner = bloom_from_params(&self.params, new)
|
||||
}
|
||||
|
||||
pub fn dump_bitmap(&self) -> Vec<u8> {
|
||||
self.inner.bitmap()
|
||||
}
|
||||
|
||||
pub fn set(&mut self, b: &Vec<u8>) {
|
||||
self.inner.set(b);
|
||||
}
|
||||
|
||||
pub fn check(&self, b: &Vec<u8>) -> bool {
|
||||
self.inner.check(b)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DoubleSpendingFilterBuilder {
|
||||
params: BloomfilterParameters,
|
||||
bit_vec_builder: Option<BitVecBuilder>,
|
||||
}
|
||||
|
||||
impl DoubleSpendingFilterBuilder {
|
||||
pub fn new(params: BloomfilterParameters) -> Self {
|
||||
DoubleSpendingFilterBuilder {
|
||||
params,
|
||||
bit_vec_builder: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_bytes(&mut self, b: &[u8]) -> bool {
|
||||
match &mut self.bit_vec_builder {
|
||||
None => {
|
||||
self.bit_vec_builder = Some(BitVecBuilder::new(b));
|
||||
true
|
||||
}
|
||||
Some(builder) => builder.add_bytes(b),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build(self) -> DoubleSpendingFilter {
|
||||
match self.bit_vec_builder {
|
||||
None => DoubleSpendingFilter::new_empty(self.params),
|
||||
Some(builder) => DoubleSpendingFilter {
|
||||
inner: bloom_from_params(&self.params, builder.finish()),
|
||||
params: self.params,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BitVecBuilder(BitVec);
|
||||
|
||||
impl BitVecBuilder {
|
||||
pub fn new(initial_bitmap: &[u8]) -> Self {
|
||||
BitVecBuilder(BitVec::from_bytes(initial_bitmap))
|
||||
}
|
||||
|
||||
pub fn add_bytes(&mut self, b: &[u8]) -> bool {
|
||||
let add = BitVec::from_bytes(b);
|
||||
if self.0.len() != add.len() {
|
||||
return false;
|
||||
}
|
||||
self.0.or(&add);
|
||||
true
|
||||
}
|
||||
|
||||
pub fn finish(self) -> BitVec {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
@@ -86,7 +86,6 @@ impl Display for AddressPolicyAction {
|
||||
/// ```
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
|
||||
#[cfg_attr(feature = "openapi", aliases(ExitPolicy))]
|
||||
pub struct AddressPolicy {
|
||||
/// A list of rules to apply to find out whether an address is
|
||||
/// contained by this policy.
|
||||
@@ -727,10 +726,10 @@ mod test {
|
||||
let policy = AddressPolicy::parse_from_torrc(
|
||||
r#"
|
||||
ExitPolicy reject 1.2.3.4/32:*
|
||||
ExitPolicy reject 1.2.3.5:*
|
||||
ExitPolicy reject 1.2.3.5:*
|
||||
ExitPolicy reject 1.2.3.6/16:*
|
||||
ExitPolicy reject 1.2.3.6/16:123-456
|
||||
ExitPolicy accept *:53
|
||||
ExitPolicy reject 1.2.3.6/16:123-456
|
||||
ExitPolicy accept *:53
|
||||
ExitPolicy accept6 *6:119
|
||||
ExitPolicy accept *4:120
|
||||
ExitPolicy reject6 [FC00::]/7:*
|
||||
|
||||
@@ -59,12 +59,12 @@ pub enum GatewayRequestsError {
|
||||
source: NymNodeRoutingAddressError,
|
||||
},
|
||||
|
||||
#[error("received request had invalid size. (actual: {0}, but expected one of: {} (ACK), {} (REGULAR), {}, {}, {} (EXTENDED))",
|
||||
PacketSize::AckPacket.size(),
|
||||
PacketSize::RegularPacket.size(),
|
||||
PacketSize::ExtendedPacket8.size(),
|
||||
PacketSize::ExtendedPacket16.size(),
|
||||
PacketSize::ExtendedPacket32.size())
|
||||
#[error("received request had invalid size. (actual: {0}, but expected one of: {a} (ACK), {r} (REGULAR), {e8}, {e16}, {e32} (EXTENDED))",
|
||||
a = PacketSize::AckPacket.size(),
|
||||
r = PacketSize::RegularPacket.size(),
|
||||
e8 = PacketSize::ExtendedPacket8.size(),
|
||||
e16 = PacketSize::ExtendedPacket16.size(),
|
||||
e32 = PacketSize::ExtendedPacket32.size())
|
||||
]
|
||||
RequestOfInvalidSize(usize),
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ use std::str::FromStr;
|
||||
use tungstenite::Message;
|
||||
|
||||
// wrapper for all encrypted requests for ease of use
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[non_exhaustive]
|
||||
pub enum ClientRequest {
|
||||
UpgradeKey {
|
||||
|
||||
@@ -11,7 +11,6 @@ license.workspace = true
|
||||
[dependencies]
|
||||
bincode = { workspace = true }
|
||||
defguard_wireguard_rs = { workspace = true }
|
||||
log = { workspace = true }
|
||||
sqlx = { workspace = true, features = [
|
||||
"runtime-tokio-rustls",
|
||||
"sqlite",
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
|
||||
|
||||
ALTER TABLE message_store
|
||||
RENAME TO message_store_old;
|
||||
|
||||
-- add new column with message timestamp.
|
||||
-- note: we can't simply alter existing table to add it since the default value is non-constant
|
||||
CREATE TABLE message_store
|
||||
(
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
client_address_bs58 TEXT NOT NULL,
|
||||
content BLOB NOT NULL,
|
||||
timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
INSERT INTO message_store(id, client_address_bs58, content)
|
||||
SELECT id, client_address_bs58, content
|
||||
FROM message_store_old;
|
||||
DROP TABLE message_store_old;
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::models::StoredMessage;
|
||||
use time::OffsetDateTime;
|
||||
use tracing::debug;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct InboxManager {
|
||||
pub struct InboxManager {
|
||||
connection_pool: sqlx::SqlitePool,
|
||||
/// Maximum number of messages that can be obtained from the database per operation.
|
||||
/// It is used to prevent out of memory errors in the case of client receiving a lot of data while
|
||||
@@ -71,44 +73,22 @@ impl InboxManager {
|
||||
// get 1 additional message to check whether there will be more to grab
|
||||
// next time
|
||||
let limit = self.retrieval_limit + 1;
|
||||
let mut res = if let Some(start_after) = start_after {
|
||||
sqlx::query_as!(
|
||||
StoredMessage,
|
||||
r#"
|
||||
SELECT
|
||||
id as "id!",
|
||||
client_address_bs58 as "client_address_bs58!",
|
||||
content as "content!"
|
||||
FROM message_store
|
||||
WHERE client_address_bs58 = ? AND id > ?
|
||||
ORDER BY id ASC
|
||||
LIMIT ?;
|
||||
"#,
|
||||
client_address_bs58,
|
||||
start_after,
|
||||
limit
|
||||
)
|
||||
.fetch_all(&self.connection_pool)
|
||||
.await?
|
||||
} else {
|
||||
sqlx::query_as!(
|
||||
StoredMessage,
|
||||
r#"
|
||||
SELECT
|
||||
id as "id!",
|
||||
client_address_bs58 as "client_address_bs58!",
|
||||
content as "content!"
|
||||
FROM message_store
|
||||
WHERE client_address_bs58 = ?
|
||||
ORDER BY id ASC
|
||||
LIMIT ?;
|
||||
"#,
|
||||
client_address_bs58,
|
||||
limit
|
||||
)
|
||||
.fetch_all(&self.connection_pool)
|
||||
.await?
|
||||
};
|
||||
let start_after = start_after.unwrap_or(-1);
|
||||
|
||||
let mut res: Vec<StoredMessage> = sqlx::query_as(
|
||||
r#"
|
||||
SELECT id, client_address_bs58, content, timestamp
|
||||
FROM message_store
|
||||
WHERE client_address_bs58 = ? AND id > ?
|
||||
ORDER BY id ASC
|
||||
LIMIT ?;
|
||||
"#,
|
||||
)
|
||||
.bind(client_address_bs58)
|
||||
.bind(start_after)
|
||||
.bind(limit)
|
||||
.fetch_all(&self.connection_pool)
|
||||
.await?;
|
||||
|
||||
if res.len() > self.retrieval_limit as usize {
|
||||
res.truncate(self.retrieval_limit as usize);
|
||||
@@ -146,4 +126,13 @@ impl InboxManager {
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn remove_stale(&self, cutoff: OffsetDateTime) -> Result<(), sqlx::Error> {
|
||||
let affected = sqlx::query!("DELETE FROM message_store WHERE timestamp < ?", cutoff)
|
||||
.execute(&self.connection_pool)
|
||||
.await?
|
||||
.rows_affected();
|
||||
debug!("Removed {affected} stale messages");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
use bandwidth::BandwidthManager;
|
||||
use clients::{ClientManager, ClientType};
|
||||
use inboxes::InboxManager;
|
||||
use models::{
|
||||
Client, PersistedBandwidth, PersistedSharedKeys, RedemptionProposal, StoredMessage,
|
||||
VerifiedTicket, WireguardPeer,
|
||||
@@ -31,6 +30,7 @@ mod tickets;
|
||||
mod wireguard_peers;
|
||||
|
||||
pub use error::GatewayStorageError;
|
||||
pub use inboxes::InboxManager;
|
||||
|
||||
// note that clone here is fine as upon cloning the same underlying pool will be used
|
||||
#[derive(Clone)]
|
||||
@@ -53,7 +53,7 @@ impl GatewayStorage {
|
||||
&self.shared_key_manager
|
||||
}
|
||||
|
||||
pub(crate) fn inbox_manager(&self) -> &InboxManager {
|
||||
pub fn inbox_manager(&self) -> &InboxManager {
|
||||
&self.inbox_manager
|
||||
}
|
||||
|
||||
|
||||
@@ -48,11 +48,13 @@ impl TryFrom<PersistedSharedKeys> for SharedGatewayKey {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromRow)]
|
||||
pub struct StoredMessage {
|
||||
pub id: i64,
|
||||
#[allow(dead_code)]
|
||||
pub client_address_bs58: String,
|
||||
pub content: Vec<u8>,
|
||||
pub timestamp: OffsetDateTime,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, FromRow)]
|
||||
|
||||
@@ -15,6 +15,7 @@ async-trait = { workspace = true }
|
||||
reqwest = { workspace = true, features = ["json"] }
|
||||
http.workspace = true
|
||||
url = { workspace = true }
|
||||
once_cell = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
@@ -22,7 +23,13 @@ tracing = { workspace = true }
|
||||
|
||||
nym-bin-common = { path = "../bin-common" }
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies]
|
||||
hickory-resolver = { workspace = true, features = ["dns-over-https-rustls", "webpki-roots"] }
|
||||
|
||||
# for request timeout until https://github.com/seanmonstar/reqwest/issues/1135 is fixed
|
||||
[target."cfg(target_arch = \"wasm32\")".dependencies.wasmtimer]
|
||||
workspace = true
|
||||
features = ["tokio"]
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { workspace = true, features=["rt", "macros"] }
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
//! DNS resolver configuration for internal lookups.
|
||||
//!
|
||||
//! The resolver itself is the set combination of the google, cloudflare, and quad9 endpoints
|
||||
//! supporting DoH and DoT.
|
||||
//!
|
||||
//! This resolver implements a fallback mechanism where, should the DNS-over-TLS resolution fail, a
|
||||
//! followup resolution will be done using the hosts configured default (e.g. `/etc/resolve.conf` on
|
||||
//! linux).
|
||||
//!
|
||||
//! Requires the `dns-over-https-rustls`, `webpki-roots` feature for the
|
||||
//! `hickory-resolver` crate
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use crate::ClientBuilder;
|
||||
|
||||
use std::{net::SocketAddr, sync::Arc};
|
||||
|
||||
use hickory_resolver::lookup_ip::LookupIp;
|
||||
use hickory_resolver::{
|
||||
config::{LookupIpStrategy, NameServerConfigGroup, ResolverConfig, ResolverOpts},
|
||||
error::ResolveError,
|
||||
lookup_ip::LookupIpIntoIter,
|
||||
TokioAsyncResolver,
|
||||
};
|
||||
use once_cell::sync::OnceCell;
|
||||
use reqwest::dns::{Addrs, Name, Resolve, Resolving};
|
||||
use tracing::warn;
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
struct SocketAddrs {
|
||||
iter: LookupIpIntoIter,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("hickory-dns resolver error: {hickory_error}")]
|
||||
/// Error occurring while resolving a hostname into an IP address.
|
||||
pub struct HickoryDnsError {
|
||||
#[from]
|
||||
hickory_error: ResolveError,
|
||||
}
|
||||
|
||||
/// Wrapper around an `AsyncResolver`, which implements the `Resolve` trait.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct HickoryDnsResolver {
|
||||
/// Since we might not have been called in the context of a
|
||||
/// Tokio Runtime in initialization, so we must delay the actual
|
||||
/// construction of the resolver.
|
||||
state: Arc<OnceCell<TokioAsyncResolver>>,
|
||||
fallback: Arc<OnceCell<TokioAsyncResolver>>,
|
||||
}
|
||||
|
||||
impl Resolve for HickoryDnsResolver {
|
||||
fn resolve(&self, name: Name) -> Resolving {
|
||||
let resolver = self.state.clone();
|
||||
let fallback = self.fallback.clone();
|
||||
Box::pin(async move {
|
||||
let resolver = resolver.get_or_try_init(new_resolver)?;
|
||||
|
||||
// try the primary DNS resolver that we set up (DoH or DoT or whatever)
|
||||
let lookup = match resolver.lookup_ip(name.as_str()).await {
|
||||
Ok(res) => res,
|
||||
Err(e) => {
|
||||
// on failure use the fall back system configured DNS resolver
|
||||
warn!("primary DNS failed w/ error {e}: using system fallback");
|
||||
let resolver = fallback.get_or_try_init(new_resolver_system)?;
|
||||
resolver.lookup_ip(name.as_str()).await?
|
||||
}
|
||||
};
|
||||
|
||||
let addrs: Addrs = Box::new(SocketAddrs {
|
||||
iter: lookup.into_iter(),
|
||||
});
|
||||
Ok(addrs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for SocketAddrs {
|
||||
type Item = SocketAddr;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next().map(|ip_addr| SocketAddr::new(ip_addr, 0))
|
||||
}
|
||||
}
|
||||
|
||||
impl HickoryDnsResolver {
|
||||
/// Attempt to resolve a domain name to a set of ['IpAddr']s
|
||||
pub async fn resolve_str(&self, name: &str) -> Result<LookupIp, HickoryDnsError> {
|
||||
let resolver = self.state.get_or_try_init(new_resolver)?;
|
||||
|
||||
// try the primary DNS resolver that we set up (DoH or DoT or whatever)
|
||||
let lookup = match resolver.lookup_ip(name).await {
|
||||
Ok(res) => res,
|
||||
Err(e) => {
|
||||
// on failure use the fall back system configured DNS resolver
|
||||
warn!("primary DNS failed w/ error {e}: using system fallback");
|
||||
let resolver = self.fallback.get_or_try_init(new_resolver_system)?;
|
||||
resolver.lookup_ip(name).await?
|
||||
}
|
||||
};
|
||||
|
||||
Ok(lookup)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new resolver with a custom DoT based configuration. The options are overridden to look
|
||||
/// up for both IPv4 and IPv6 addresses to work with "happy eyeballs" algorithm.
|
||||
fn new_resolver() -> Result<TokioAsyncResolver, HickoryDnsError> {
|
||||
let mut name_servers = NameServerConfigGroup::google_tls();
|
||||
name_servers.merge(NameServerConfigGroup::google_https());
|
||||
// name_servers.merge(NameServerConfigGroup::google_h3());
|
||||
name_servers.merge(NameServerConfigGroup::quad9_tls());
|
||||
name_servers.merge(NameServerConfigGroup::quad9_https());
|
||||
name_servers.merge(NameServerConfigGroup::cloudflare_tls());
|
||||
name_servers.merge(NameServerConfigGroup::cloudflare_https());
|
||||
|
||||
let config = ResolverConfig::from_parts(None, Vec::new(), name_servers);
|
||||
|
||||
let mut opts = ResolverOpts::default();
|
||||
opts.ip_strategy = LookupIpStrategy::Ipv4AndIpv6;
|
||||
// Would like to enable this when 0.25 stabilizes
|
||||
// opts.server_ordering_strategy = ServerOrderingStrategy::RoundRobin;
|
||||
|
||||
Ok(TokioAsyncResolver::tokio(config, opts))
|
||||
}
|
||||
|
||||
/// Create a new resolver with the default configuration, which reads from the system DNS config
|
||||
/// (i.e. `/etc/resolve.conf` in unix). The options are overridden to look up for both IPv4 and IPv6
|
||||
/// addresses to work with "happy eyeballs" algorithm.
|
||||
fn new_resolver_system() -> Result<TokioAsyncResolver, HickoryDnsError> {
|
||||
let (config, mut opts) = hickory_resolver::system_conf::read_system_conf()?;
|
||||
opts.ip_strategy = LookupIpStrategy::Ipv4AndIpv6;
|
||||
Ok(TokioAsyncResolver::tokio(config, opts))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn reqwest_hickory_doh() {
|
||||
let resolver = HickoryDnsResolver::default();
|
||||
let client = reqwest::ClientBuilder::new()
|
||||
.dns_resolver(resolver.into())
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let resp = client
|
||||
.get("http://ifconfig.me:80")
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.bytes()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(!resp.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn dns_lookup() -> Result<(), HickoryDnsError> {
|
||||
let resolver = HickoryDnsResolver::default();
|
||||
|
||||
let domain = "ifconfig.me";
|
||||
let addrs = resolver.resolve_str(domain).await?;
|
||||
|
||||
assert!(addrs.into_iter().next().is_some());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -6,17 +6,23 @@ use reqwest::header::HeaderValue;
|
||||
use reqwest::{RequestBuilder, Response, StatusCode};
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Display;
|
||||
use std::time::Duration;
|
||||
use thiserror::Error;
|
||||
use tracing::{instrument, warn};
|
||||
use url::Url;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use std::sync::Arc;
|
||||
use std::{fmt::Display, time::Duration};
|
||||
|
||||
pub use reqwest::IntoUrl;
|
||||
|
||||
mod user_agent;
|
||||
pub use user_agent::UserAgent;
|
||||
|
||||
mod user_agent;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
mod dns;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub use dns::{HickoryDnsError, HickoryDnsResolver};
|
||||
|
||||
// The timeout is relatively high as we are often making requests over the mixnet, where latency is
|
||||
// high and chatty protocols take a while to complete.
|
||||
@@ -86,11 +92,18 @@ impl ClientBuilder {
|
||||
// TODO: or should we maybe default to https?
|
||||
Self::new(alt)
|
||||
} else {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let reqwest_client_builder = reqwest::ClientBuilder::new();
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let reqwest_client_builder =
|
||||
reqwest::ClientBuilder::new().dns_resolver(Arc::new(HickoryDnsResolver::default()));
|
||||
|
||||
Ok(ClientBuilder {
|
||||
url: url.into_url()?,
|
||||
timeout: None,
|
||||
custom_user_agent: false,
|
||||
reqwest_client_builder: reqwest::ClientBuilder::new(),
|
||||
reqwest_client_builder,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -755,7 +768,7 @@ where
|
||||
E: DeserializeOwned + Display,
|
||||
{
|
||||
let status = res.status();
|
||||
tracing::debug!("Status: {} (success: {})", &status, status.is_success());
|
||||
tracing::trace!("Status: {} (success: {})", &status, status.is_success());
|
||||
|
||||
if !allow_empty {
|
||||
if let Some(0) = res.content_length() {
|
||||
|
||||
@@ -10,7 +10,6 @@ use serde::{Deserialize, Serialize};
|
||||
pub mod middleware;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub enum FormattedResponse<T> {
|
||||
Json(Json<T>),
|
||||
Yaml(Yaml<T>),
|
||||
|
||||
@@ -65,6 +65,14 @@ impl<T> NonExhaustiveDelayQueue<T> {
|
||||
pub fn remove(&mut self, key: &QueueKey) -> Expired<T> {
|
||||
self.inner.remove(key)
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.inner.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.inner.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for NonExhaustiveDelayQueue<T> {
|
||||
|
||||
@@ -12,6 +12,6 @@ license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
prometheus = { workspace = true }
|
||||
log = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
dashmap = { workspace = true }
|
||||
lazy_static = { workspace = true }
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user