Compare commits
110 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1ef33ce797 | |||
| ff91d4619e | |||
| 9d01474277 | |||
| 8d10552d7c | |||
| 04fd197f5a | |||
| 4eadaf8292 | |||
| 32e39ebc6b | |||
| 117eb83a0b | |||
| c964c137f4 | |||
| 35b43d5b20 | |||
| bf88b34898 | |||
| 93140a1aa7 | |||
| f594bfc9ab | |||
| 4327e2945a | |||
| 8670693952 | |||
| 57c38ef222 | |||
| 8e05386a0b | |||
| 13cfa55e6c | |||
| 18e628acde | |||
| e67b2b020a | |||
| 9b627dd70f | |||
| 9a0b769425 | |||
| 8e14f5f884 | |||
| 1b64cb42b0 | |||
| 03c4895f2b | |||
| dcfb092758 | |||
| 9305ad5364 | |||
| ea5aef6c2f | |||
| 61a4433cd9 | |||
| 5c89d36140 | |||
| adb248dbcc | |||
| fffec65cab | |||
| bb24004d46 | |||
| c487eff7ca | |||
| 5fa21c9aae | |||
| fd18aae0d6 | |||
| 62d23cff9f | |||
| e454d71b78 | |||
| 0a47d5dcf8 | |||
| 6ccbb30491 | |||
| 91c205f83a | |||
| 4a704e992a | |||
| 6c88c7df42 | |||
| 2a748fc968 | |||
| 07544d939e | |||
| 102cd1033c | |||
| 01c7ea72dd | |||
| dfd1df5706 | |||
| 11d6ee2fdb | |||
| d704c428fc | |||
| bca070c1bd | |||
| 226c040a13 | |||
| 3d2914b3e5 | |||
| 9b02de3e75 | |||
| f3d8aba82c | |||
| 72a4a26c40 | |||
| 5d9b5a0d70 | |||
| c070e4bfee | |||
| 4d3d60b78e | |||
| 41fb17a31b | |||
| 7d5e3ef7d3 | |||
| 4f283f565c | |||
| 2fab3f11b6 | |||
| d0722e5f63 | |||
| 64373548e4 | |||
| bad85abff3 | |||
| 6e66cc2467 | |||
| c805aa79a4 | |||
| f5ca1ee20a | |||
| 4f07343efd | |||
| 94ab78606a | |||
| 7b92e471c8 | |||
| a507ffe371 | |||
| c02e93004f | |||
| 1113e0c599 | |||
| 06c7394861 | |||
| e20bea9d32 | |||
| eeea32fdca | |||
| b06349efd0 | |||
| 98a4cb4ae8 | |||
| be185824b4 | |||
| 60e8e53f3b | |||
| 1890367bfc | |||
| 2b26a88d6c | |||
| a6f4f017c7 | |||
| d8a6ca48c1 | |||
| 541d46e899 | |||
| 39f525e88e | |||
| 156e892baa | |||
| 5b6ae39dab | |||
| df004f834f | |||
| 235165171b | |||
| 572875058d | |||
| cf6f437187 | |||
| 6010de978d | |||
| d951ea9548 | |||
| 868d7439ec | |||
| a884aee1e9 | |||
| 80f965a104 | |||
| c99a240ed4 | |||
| 67976b1b30 | |||
| a2322d6cdf | |||
| ae346bb75b | |||
| 53c28af847 | |||
| 3521f36374 | |||
| 3695332036 | |||
| d03302c391 | |||
| cd86110b2c | |||
| ad0c135d4c | |||
| 61e872f033 |
@@ -0,0 +1 @@
|
|||||||
|
nym-validator-rewarder/.sqlx/** diff=nodiff
|
||||||
@@ -31,3 +31,5 @@ updates:
|
|||||||
update-types:
|
update-types:
|
||||||
- "patch"
|
- "patch"
|
||||||
open-pull-requests-limit: 10
|
open-pull-requests-limit: 10
|
||||||
|
assignees:
|
||||||
|
- "octol"
|
||||||
|
|||||||
@@ -79,7 +79,6 @@ jobs:
|
|||||||
target/release/nym-socks5-client
|
target/release/nym-socks5-client
|
||||||
target/release/nym-api
|
target/release/nym-api
|
||||||
target/release/nym-network-requester
|
target/release/nym-network-requester
|
||||||
target/release/nym-data-observatory
|
|
||||||
target/release/nym-cli
|
target/release/nym-cli
|
||||||
target/release/nymvisor
|
target/release/nymvisor
|
||||||
target/release/nym-node
|
target/release/nym-node
|
||||||
@@ -97,7 +96,6 @@ jobs:
|
|||||||
cp target/release/nym-socks5-client $OUTPUT_DIR
|
cp target/release/nym-socks5-client $OUTPUT_DIR
|
||||||
cp target/release/nym-api $OUTPUT_DIR
|
cp target/release/nym-api $OUTPUT_DIR
|
||||||
cp target/release/nym-network-requester $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/nymvisor $OUTPUT_DIR
|
||||||
cp target/release/nym-node $OUTPUT_DIR
|
cp target/release/nym-node $OUTPUT_DIR
|
||||||
cp target/release/nym-cli $OUTPUT_DIR
|
cp target/release/nym-cli $OUTPUT_DIR
|
||||||
|
|||||||
@@ -8,16 +8,18 @@ on:
|
|||||||
- 'explorer-api/**'
|
- 'explorer-api/**'
|
||||||
- 'gateway/**'
|
- 'gateway/**'
|
||||||
- 'integrations/**'
|
- 'integrations/**'
|
||||||
- 'mixnode/**'
|
|
||||||
- 'sdk/rust/**'
|
|
||||||
- 'sdk/lib/**'
|
|
||||||
- 'service-providers/**'
|
|
||||||
- 'nym-network-monitor/**'
|
|
||||||
- 'nym-api/**'
|
- 'nym-api/**'
|
||||||
|
- 'nym-credential-proxy/**'
|
||||||
|
- 'nym-network-monitor/**'
|
||||||
- 'nym-node/**'
|
- 'nym-node/**'
|
||||||
|
- 'nym-node-status-api/**'
|
||||||
- 'nym-outfox/**'
|
- 'nym-outfox/**'
|
||||||
- 'nym-data-observatory/**'
|
|
||||||
- 'nym-validator-rewarder/**'
|
- 'nym-validator-rewarder/**'
|
||||||
|
- 'nyx-chain-watcher/**'
|
||||||
|
- 'sdk/ffi/**'
|
||||||
|
- 'sdk/rust/**'
|
||||||
|
- 'service-providers/**'
|
||||||
|
- 'nym-browser-extension/storage/**'
|
||||||
- 'tools/**'
|
- 'tools/**'
|
||||||
- 'wasm/**'
|
- 'wasm/**'
|
||||||
- 'Cargo.toml'
|
- 'Cargo.toml'
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
name: ci-sdk-wasm
|
name: ci-sdk-wasm
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- 'wasm/**'
|
- 'wasm/**'
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ jobs:
|
|||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
|
IPINFO_API_TOKEN: ${{ secrets.IPINFO_API_TOKEN }}
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
steps:
|
steps:
|
||||||
- name: Check out repository code
|
- name: Check out repository code
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
git config --global user.name "Lawrence Stalder"
|
git config --global user.name "Lawrence Stalder"
|
||||||
|
|
||||||
- name: Get version from cargo.toml
|
- name: Get version from cargo.toml
|
||||||
uses: mikefarah/yq@v4.44.6
|
uses: mikefarah/yq@v4.45.1
|
||||||
id: get_version
|
id: get_version
|
||||||
with:
|
with:
|
||||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-credential-proxy/Cargo.toml
|
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"
|
git config --global user.name "Lawrence Stalder"
|
||||||
|
|
||||||
- name: Get version from cargo.toml
|
- name: Get version from cargo.toml
|
||||||
uses: mikefarah/yq@v4.44.6
|
uses: mikefarah/yq@v4.45.1
|
||||||
id: get_version
|
id: get_version
|
||||||
with:
|
with:
|
||||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
git config --global user.name "Lawrence Stalder"
|
git config --global user.name "Lawrence Stalder"
|
||||||
|
|
||||||
- name: Get version from cargo.toml
|
- name: Get version from cargo.toml
|
||||||
uses: mikefarah/yq@v4.44.6
|
uses: mikefarah/yq@v4.45.1
|
||||||
id: get_version
|
id: get_version
|
||||||
with:
|
with:
|
||||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-network-monitor/Cargo.toml
|
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"
|
git config --global user.name "Lawrence Stalder"
|
||||||
|
|
||||||
- name: Get version from cargo.toml
|
- name: Get version from cargo.toml
|
||||||
uses: mikefarah/yq@v4.44.6
|
uses: mikefarah/yq@v4.45.1
|
||||||
id: get_version
|
id: get_version
|
||||||
with:
|
with:
|
||||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
git config --global user.name "Lawrence Stalder"
|
git config --global user.name "Lawrence Stalder"
|
||||||
|
|
||||||
- name: Get version from cargo.toml
|
- name: Get version from cargo.toml
|
||||||
uses: mikefarah/yq@v4.44.6
|
uses: mikefarah/yq@v4.45.1
|
||||||
id: get_version
|
id: get_version
|
||||||
with:
|
with:
|
||||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
git config --global user.name "Lawrence Stalder"
|
git config --global user.name "Lawrence Stalder"
|
||||||
|
|
||||||
- name: Get version from cargo.toml
|
- name: Get version from cargo.toml
|
||||||
uses: mikefarah/yq@v4.44.6
|
uses: mikefarah/yq@v4.45.1
|
||||||
id: get_version
|
id: get_version
|
||||||
with:
|
with:
|
||||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
git config --global user.name "Lawrence Stalder"
|
git config --global user.name "Lawrence Stalder"
|
||||||
|
|
||||||
- name: Get version from cargo.toml
|
- name: Get version from cargo.toml
|
||||||
uses: mikefarah/yq@v4.44.6
|
uses: mikefarah/yq@v4.45.1
|
||||||
id: get_version
|
id: get_version
|
||||||
with:
|
with:
|
||||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
git config --global user.name "Lawrence Stalder"
|
git config --global user.name "Lawrence Stalder"
|
||||||
|
|
||||||
- name: Get version from cargo.toml
|
- name: Get version from cargo.toml
|
||||||
uses: mikefarah/yq@v4.44.6
|
uses: mikefarah/yq@v4.45.1
|
||||||
id: get_version
|
id: get_version
|
||||||
with:
|
with:
|
||||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
||||||
|
|||||||
@@ -54,3 +54,8 @@ nym-network-monitor/__pycache__
|
|||||||
nym-network-monitor/*.key
|
nym-network-monitor/*.key
|
||||||
nym-network-monitor/.envrc
|
nym-network-monitor/.envrc
|
||||||
nym-network-monitor/.envrc
|
nym-network-monitor/.envrc
|
||||||
|
nym-api/redocly/formatted-openapi.json
|
||||||
|
|
||||||
|
|
||||||
|
*.sqlite
|
||||||
|
.build
|
||||||
|
|||||||
Generated
+1337
-655
File diff suppressed because it is too large
Load Diff
+65
-65
@@ -53,8 +53,8 @@ members = [
|
|||||||
"common/execute",
|
"common/execute",
|
||||||
"common/exit-policy",
|
"common/exit-policy",
|
||||||
"common/gateway-requests",
|
"common/gateway-requests",
|
||||||
"common/gateway-storage",
|
|
||||||
"common/gateway-stats-storage",
|
"common/gateway-stats-storage",
|
||||||
|
"common/gateway-storage",
|
||||||
"common/http-api-client",
|
"common/http-api-client",
|
||||||
"common/http-api-common",
|
"common/http-api-common",
|
||||||
"common/inclusion-probability",
|
"common/inclusion-probability",
|
||||||
@@ -93,6 +93,7 @@ members = [
|
|||||||
"common/topology",
|
"common/topology",
|
||||||
"common/tun",
|
"common/tun",
|
||||||
"common/types",
|
"common/types",
|
||||||
|
"common/verloc",
|
||||||
"common/wasm/client-core",
|
"common/wasm/client-core",
|
||||||
"common/wasm/storage",
|
"common/wasm/storage",
|
||||||
"common/wasm/utils",
|
"common/wasm/utils",
|
||||||
@@ -104,6 +105,22 @@ members = [
|
|||||||
"explorer-api/explorer-client",
|
"explorer-api/explorer-client",
|
||||||
"gateway",
|
"gateway",
|
||||||
"integrations/bity",
|
"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/cpp",
|
||||||
"sdk/ffi/go",
|
"sdk/ffi/go",
|
||||||
"sdk/ffi/shared",
|
"sdk/ffi/shared",
|
||||||
@@ -112,26 +129,16 @@ members = [
|
|||||||
"service-providers/common",
|
"service-providers/common",
|
||||||
"service-providers/ip-packet-router",
|
"service-providers/ip-packet-router",
|
||||||
"service-providers/network-requester",
|
"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-data-observatory",
|
|
||||||
"nym-network-monitor",
|
|
||||||
"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/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/sdk-version-bump",
|
||||||
|
"tools/internal/ssl-inject",
|
||||||
"tools/internal/testnet-manager",
|
"tools/internal/testnet-manager",
|
||||||
|
"tools/internal/testnet-manager",
|
||||||
|
"tools/internal/testnet-manager/dkg-bypass-contract",
|
||||||
"tools/internal/testnet-manager/dkg-bypass-contract",
|
"tools/internal/testnet-manager/dkg-bypass-contract",
|
||||||
"tools/nym-cli",
|
"tools/nym-cli",
|
||||||
"tools/nym-id-cli",
|
"tools/nym-id-cli",
|
||||||
@@ -143,11 +150,6 @@ members = [
|
|||||||
"wasm/mix-fetch",
|
"wasm/mix-fetch",
|
||||||
"wasm/node-tester",
|
"wasm/node-tester",
|
||||||
"wasm/zknym-lib",
|
"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 = [
|
default-members = [
|
||||||
@@ -156,11 +158,11 @@ default-members = [
|
|||||||
"explorer-api",
|
"explorer-api",
|
||||||
"nym-api",
|
"nym-api",
|
||||||
"nym-credential-proxy/nym-credential-proxy",
|
"nym-credential-proxy/nym-credential-proxy",
|
||||||
"nym-data-observatory",
|
|
||||||
"nym-node",
|
"nym-node",
|
||||||
"nym-node-status-api/nym-node-status-agent",
|
"nym-node-status-api/nym-node-status-agent",
|
||||||
"nym-node-status-api/nym-node-status-api",
|
"nym-node-status-api/nym-node-status-api",
|
||||||
"nym-validator-rewarder",
|
"nym-validator-rewarder",
|
||||||
|
"nyx-chain-watcher",
|
||||||
"service-providers/authenticator",
|
"service-providers/authenticator",
|
||||||
"service-providers/ip-packet-router",
|
"service-providers/ip-packet-router",
|
||||||
"service-providers/network-requester",
|
"service-providers/network-requester",
|
||||||
@@ -171,7 +173,6 @@ exclude = [
|
|||||||
"explorer",
|
"explorer",
|
||||||
"contracts",
|
"contracts",
|
||||||
"nym-wallet",
|
"nym-wallet",
|
||||||
"nym-vpn/ui/src-tauri",
|
|
||||||
"cpu-cycles",
|
"cpu-cycles",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -191,9 +192,10 @@ aes = "0.8.1"
|
|||||||
aes-gcm = "0.10.1"
|
aes-gcm = "0.10.1"
|
||||||
aes-gcm-siv = "0.11.1"
|
aes-gcm-siv = "0.11.1"
|
||||||
aead = "0.5.2"
|
aead = "0.5.2"
|
||||||
anyhow = "1.0.90"
|
anyhow = "1.0.95"
|
||||||
|
arc-swap = "1.7.1"
|
||||||
argon2 = "0.5.0"
|
argon2 = "0.5.0"
|
||||||
async-trait = "0.1.83"
|
async-trait = "0.1.85"
|
||||||
axum-client-ip = "0.6.1"
|
axum-client-ip = "0.6.1"
|
||||||
axum = "0.7.5"
|
axum = "0.7.5"
|
||||||
axum-extra = "0.9.4"
|
axum-extra = "0.9.4"
|
||||||
@@ -202,7 +204,7 @@ bincode = "1.3.3"
|
|||||||
bip39 = { version = "2.0.0", features = ["zeroize"] }
|
bip39 = { version = "2.0.0", features = ["zeroize"] }
|
||||||
bit-vec = "0.7.0" # can we unify those?
|
bit-vec = "0.7.0" # can we unify those?
|
||||||
bitvec = "1.0.0"
|
bitvec = "1.0.0"
|
||||||
blake3 = "1.5.4"
|
blake3 = "1.5.5"
|
||||||
bloomfilter = "1.0.14"
|
bloomfilter = "1.0.14"
|
||||||
bs58 = "0.5.1"
|
bs58 = "0.5.1"
|
||||||
bytecodec = "0.4.15"
|
bytecodec = "0.4.15"
|
||||||
@@ -212,20 +214,20 @@ celes = "2.4.0"
|
|||||||
cfg-if = "1.0.0"
|
cfg-if = "1.0.0"
|
||||||
chacha20 = "0.9.0"
|
chacha20 = "0.9.0"
|
||||||
chacha20poly1305 = "0.10.1"
|
chacha20poly1305 = "0.10.1"
|
||||||
chrono = "0.4.31"
|
chrono = "0.4.39"
|
||||||
cipher = "0.4.3"
|
cipher = "0.4.3"
|
||||||
clap = "4.5.20"
|
clap = "4.5.26"
|
||||||
clap_complete = "4.5"
|
clap_complete = "4.5"
|
||||||
clap_complete_fig = "4.5"
|
clap_complete_fig = "4.5"
|
||||||
colored = "2.0"
|
colored = "2.0"
|
||||||
comfy-table = "7.1.1"
|
comfy-table = "7.1.3"
|
||||||
console = "0.15.8"
|
console = "0.15.10"
|
||||||
console-subscriber = "0.1.1"
|
console-subscriber = "0.1.1"
|
||||||
console_error_panic_hook = "0.1"
|
console_error_panic_hook = "0.1"
|
||||||
const-str = "0.5.6"
|
const-str = "0.5.6"
|
||||||
const_format = "0.2.33"
|
const_format = "0.2.34"
|
||||||
criterion = "0.4"
|
criterion = "0.5"
|
||||||
csv = "1.3.0"
|
csv = "1.3.1"
|
||||||
ctr = "0.9.1"
|
ctr = "0.9.1"
|
||||||
cupid = "0.6.1"
|
cupid = "0.6.1"
|
||||||
curve25519-dalek = "4.1"
|
curve25519-dalek = "4.1"
|
||||||
@@ -242,8 +244,8 @@ etherparse = "0.13.0"
|
|||||||
envy = "0.4"
|
envy = "0.4"
|
||||||
eyre = "0.6.9"
|
eyre = "0.6.9"
|
||||||
fastrand = "2.1.1"
|
fastrand = "2.1.1"
|
||||||
flate2 = "1.0.34"
|
flate2 = "1.0.35"
|
||||||
futures = "0.3.28"
|
futures = "0.3.31"
|
||||||
futures-util = "0.3"
|
futures-util = "0.3"
|
||||||
generic-array = "0.14.7"
|
generic-array = "0.14.7"
|
||||||
getrandom = "0.2.10"
|
getrandom = "0.2.10"
|
||||||
@@ -252,6 +254,7 @@ handlebars = "3.5.5"
|
|||||||
headers = "0.4.0"
|
headers = "0.4.0"
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
hex-literal = "0.3.3"
|
hex-literal = "0.3.3"
|
||||||
|
hickory-resolver = "0.24.2"
|
||||||
hkdf = "0.12.3"
|
hkdf = "0.12.3"
|
||||||
hmac = "0.12.1"
|
hmac = "0.12.1"
|
||||||
http = "1"
|
http = "1"
|
||||||
@@ -262,7 +265,7 @@ humantime-serde = "1.1.1"
|
|||||||
human-repr = "1.1.0"
|
human-repr = "1.1.0"
|
||||||
hyper = "1.4.1"
|
hyper = "1.4.1"
|
||||||
hyper-util = "0.1"
|
hyper-util = "0.1"
|
||||||
indicatif = "0.17.8"
|
indicatif = "0.17.9"
|
||||||
inquire = "0.6.2"
|
inquire = "0.6.2"
|
||||||
ip_network = "0.4.1"
|
ip_network = "0.4.1"
|
||||||
ipnetwork = "0.20"
|
ipnetwork = "0.20"
|
||||||
@@ -287,7 +290,7 @@ parking_lot = "0.12.3"
|
|||||||
pem = "0.8"
|
pem = "0.8"
|
||||||
petgraph = "0.6.5"
|
petgraph = "0.6.5"
|
||||||
pin-project = "1.1"
|
pin-project = "1.1"
|
||||||
pin-project-lite = "0.2.14"
|
pin-project-lite = "0.2.16"
|
||||||
pretty_env_logger = "0.4.0"
|
pretty_env_logger = "0.4.0"
|
||||||
publicsuffix = "2.2.3"
|
publicsuffix = "2.2.3"
|
||||||
quote = "1"
|
quote = "1"
|
||||||
@@ -305,11 +308,11 @@ rocket_cors = "0.6.0"
|
|||||||
rocket_okapi = "0.8.0"
|
rocket_okapi = "0.8.0"
|
||||||
safer-ffi = "0.1.13"
|
safer-ffi = "0.1.13"
|
||||||
schemars = "0.8.21"
|
schemars = "0.8.21"
|
||||||
semver = "1.0.23"
|
semver = "1.0.24"
|
||||||
serde = "1.0.211"
|
serde = "1.0.217"
|
||||||
serde_bytes = "0.11.15"
|
serde_bytes = "0.11.15"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
serde_json = "1.0.132"
|
serde_json = "1.0.135"
|
||||||
serde_json_path = "0.7.1"
|
serde_json_path = "0.7.1"
|
||||||
serde_repr = "0.1"
|
serde_repr = "0.1"
|
||||||
serde_with = "3.9.0"
|
serde_with = "3.9.0"
|
||||||
@@ -322,36 +325,36 @@ strum = "0.26"
|
|||||||
strum_macros = "0.26"
|
strum_macros = "0.26"
|
||||||
subtle-encoding = "0.5"
|
subtle-encoding = "0.5"
|
||||||
syn = "1"
|
syn = "1"
|
||||||
sysinfo = "0.30.13"
|
sysinfo = "0.33.0"
|
||||||
tap = "1.0.1"
|
tap = "1.0.1"
|
||||||
tar = "0.4.42"
|
tar = "0.4.43"
|
||||||
tempfile = "3.14"
|
tempfile = "3.15"
|
||||||
thiserror = "1.0.64"
|
thiserror = "1.0.64"
|
||||||
time = "0.3.30"
|
time = "0.3.37"
|
||||||
tokio = "1.39"
|
tokio = "1.39"
|
||||||
tokio-stream = "0.1.16"
|
tokio-stream = "0.1.17"
|
||||||
tokio-test = "0.4.4"
|
tokio-test = "0.4.4"
|
||||||
tokio-tun = "0.11.5"
|
tokio-tun = "0.11.5"
|
||||||
tokio-tungstenite = { version = "0.20.1" }
|
tokio-tungstenite = { version = "0.20.1" }
|
||||||
tokio-util = "0.7.12"
|
tokio-util = "0.7.13"
|
||||||
toml = "0.8.14"
|
toml = "0.8.19"
|
||||||
tower = "0.4.13"
|
tower = "0.4.13"
|
||||||
tower-http = "0.5.2"
|
tower-http = "0.5.2"
|
||||||
tracing = "0.1.37"
|
tracing = "0.1.41"
|
||||||
tracing-opentelemetry = "0.19.0"
|
tracing-opentelemetry = "0.19.0"
|
||||||
tracing-subscriber = "0.3.16"
|
tracing-subscriber = "0.3.19"
|
||||||
tracing-tree = "0.2.2"
|
tracing-tree = "0.2.2"
|
||||||
tracing-log = "0.2"
|
tracing-log = "0.2"
|
||||||
ts-rs = "10.0.0"
|
ts-rs = "10.1.0"
|
||||||
tungstenite = { version = "0.20.1", default-features = false }
|
tungstenite = { version = "0.20.1", default-features = false }
|
||||||
url = "2.5"
|
url = "2.5"
|
||||||
utoipa = "4.2"
|
utoipa = "5.2"
|
||||||
utoipa-swagger-ui = "7.1"
|
utoipa-swagger-ui = "8.0"
|
||||||
utoipauto = "0.1"
|
utoipauto = "0.2"
|
||||||
uuid = "*"
|
uuid = "*"
|
||||||
vergen = { version = "=8.3.1", default-features = false }
|
vergen = { version = "=8.3.1", default-features = false }
|
||||||
walkdir = "2"
|
walkdir = "2"
|
||||||
wasm-bindgen-test = "0.3.43"
|
wasm-bindgen-test = "0.3.49"
|
||||||
x25519-dalek = "2.0.0"
|
x25519-dalek = "2.0.0"
|
||||||
zeroize = "1.6.0"
|
zeroize = "1.6.0"
|
||||||
|
|
||||||
@@ -384,20 +387,17 @@ cw-controllers = { version = "=1.1.0" }
|
|||||||
# cosmrs-related
|
# cosmrs-related
|
||||||
bip32 = { version = "0.5.2", default-features = false }
|
bip32 = { version = "0.5.2", 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 = { version = "0.21.0" }
|
||||||
#cosmrs = { path = "../cosmos-rust-fork/cosmos-rust/cosmrs" }
|
tendermint = "0.40.0"
|
||||||
cosmrs = { git = "https://github.com/cosmos/cosmos-rust", rev = "4b1332e6d8258ac845cef71589c8d362a669675a" } # unfortuntely we need a fork by yours truly to get the staking support
|
tendermint-rpc = "0.40.0"
|
||||||
tendermint = "0.37.0" # same version as used by cosmrs
|
prost = { version = "0.13", default-features = false }
|
||||||
tendermint-rpc = "0.37.0" # same version as used by cosmrs
|
|
||||||
prost = { version = "0.12", default-features = false }
|
|
||||||
|
|
||||||
# wasm-related dependencies
|
# wasm-related dependencies
|
||||||
gloo-utils = "0.2.0"
|
gloo-utils = "0.2.0"
|
||||||
gloo-net = "0.6.0"
|
gloo-net = "0.6.0"
|
||||||
|
|
||||||
# TODO: migrate to 0.6+
|
indexed_db_futures = "0.6.0"
|
||||||
indexed_db_futures = "0.4.2"
|
|
||||||
js-sys = "0.3.76"
|
js-sys = "0.3.76"
|
||||||
serde-wasm-bindgen = "0.6.5"
|
serde-wasm-bindgen = "0.6.5"
|
||||||
tsify = "0.4.5"
|
tsify = "0.4.5"
|
||||||
|
|||||||
@@ -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-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-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-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-wallet` - a desktop wallet implemented using the [Tauri](https://tauri.app)) framework.
|
||||||
* `nym-cli` - a tool for interacting with the network from the CLI.
|
* `nym-cli` - a tool for interacting with the network from the CLI.
|
||||||
<!-- coming soon
|
<!-- 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").
|
* `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:
|
References for developers:
|
||||||
|
|
||||||
* [Dev Docs](https://nymtech.net/docs/developers)
|
* [Dev Docs](https://nym.com/docs/developers)
|
||||||
* [SDKs](https://nymtech.net/docs/developers/rust)
|
* [SDKs](https://nym.com/docs/developers/rust)
|
||||||
* [Network Docs](https://nymtech.net/docs/network)
|
* [Network Docs](https://nym.com/docs/network)
|
||||||
* [Release Cycle - git flow](https://nymtech.net/docs/operators/release-cycle)
|
* [Release Cycle - git flow](https://nym.com/docs/operators/release-cycle)
|
||||||
|
|
||||||
### Developer chat
|
### 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
|
- libraries and components are Apache 2.0 or MIT
|
||||||
- documentation is Apache 2.0 or CC0-1.0
|
- 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).
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ name = "nym-client-core"
|
|||||||
version = "1.1.15"
|
version = "1.1.15"
|
||||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.70"
|
rust-version = "1.76"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
@@ -45,7 +45,7 @@ nym-nonexhaustive-delayqueue = { path = "../nonexhaustive-delayqueue" }
|
|||||||
nym-sphinx = { path = "../nymsphinx" }
|
nym-sphinx = { path = "../nymsphinx" }
|
||||||
nym-statistics-common = { path = "../statistics" }
|
nym-statistics-common = { path = "../statistics" }
|
||||||
nym-pemstore = { path = "../pemstore" }
|
nym-pemstore = { path = "../pemstore" }
|
||||||
nym-topology = { path = "../topology", features = ["serializable"] }
|
nym-topology = { path = "../topology", features = ["persistence"] }
|
||||||
nym-mixnet-client = { path = "../client-libs/mixnet-client", default-features = false }
|
nym-mixnet-client = { path = "../client-libs/mixnet-client", default-features = false }
|
||||||
nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
|
nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
|
||||||
nym-task = { path = "../task" }
|
nym-task = { path = "../task" }
|
||||||
|
|||||||
@@ -550,6 +550,14 @@ pub struct Topology {
|
|||||||
/// Specifies a minimum performance of a gateway that is used on route construction.
|
/// Specifies a minimum performance of a gateway that is used on route construction.
|
||||||
/// This setting is only applicable when `NymApi` topology is used.
|
/// This setting is only applicable when `NymApi` topology is used.
|
||||||
pub minimum_gateway_performance: u8,
|
pub minimum_gateway_performance: u8,
|
||||||
|
|
||||||
|
/// Specifies whether this client should attempt to retrieve all available network nodes
|
||||||
|
/// as opposed to just active mixnodes/gateways.
|
||||||
|
pub use_extended_topology: bool,
|
||||||
|
|
||||||
|
/// Specifies whether this client should ignore the current epoch role of the target egress node
|
||||||
|
/// when constructing the final hop packets.
|
||||||
|
pub ignore_egress_epoch_role: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::large_enum_variant)]
|
#[allow(clippy::large_enum_variant)]
|
||||||
@@ -586,6 +594,8 @@ impl Default for Topology {
|
|||||||
topology_structure: TopologyStructure::default(),
|
topology_structure: TopologyStructure::default(),
|
||||||
minimum_mixnode_performance: DEFAULT_MIN_MIXNODE_PERFORMANCE,
|
minimum_mixnode_performance: DEFAULT_MIN_MIXNODE_PERFORMANCE,
|
||||||
minimum_gateway_performance: DEFAULT_MIN_GATEWAY_PERFORMANCE,
|
minimum_gateway_performance: DEFAULT_MIN_GATEWAY_PERFORMANCE,
|
||||||
|
use_extended_topology: false,
|
||||||
|
ignore_egress_epoch_role: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ pub mod error;
|
|||||||
mod manager;
|
mod manager;
|
||||||
mod models;
|
mod models;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct OnDiskGatewaysDetails {
|
pub struct OnDiskGatewaysDetails {
|
||||||
manager: StorageManager,
|
manager: StorageManager,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,12 +20,12 @@ pub enum InMemStorageError {
|
|||||||
MalformedGateway(#[from] BadGateway),
|
MalformedGateway(#[from] BadGateway),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct InMemGatewaysDetails {
|
pub struct InMemGatewaysDetails {
|
||||||
inner: Arc<RwLock<InMemStorageInner>>,
|
inner: Arc<RwLock<InMemStorageInner>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct InMemStorageInner {
|
struct InMemStorageInner {
|
||||||
active_gateway: Option<String>,
|
active_gateway: Option<String>,
|
||||||
gateways: HashMap<String, GatewayRegistration>,
|
gateways: HashMap<String, GatewayRegistration>,
|
||||||
|
|||||||
@@ -112,10 +112,10 @@ where
|
|||||||
source,
|
source,
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
hardcoded_topology.get_gateways()
|
hardcoded_topology.entry_capable_nodes().cloned().collect()
|
||||||
} else {
|
} else {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
crate::init::helpers::current_gateways(
|
crate::init::helpers::gateways_for_init(
|
||||||
&mut rng,
|
&mut rng,
|
||||||
&core.client.nym_api_urls,
|
&core.client.nym_api_urls,
|
||||||
user_agent,
|
user_agent,
|
||||||
@@ -128,7 +128,7 @@ where
|
|||||||
// make sure the list of available gateways doesn't overlap the list of known gateways
|
// make sure the list of available gateways doesn't overlap the list of known gateways
|
||||||
let available_gateways = available_gateways
|
let available_gateways = available_gateways
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|g| !registered_gateways.contains(g.identity()))
|
.filter(|g| !registered_gateways.contains(&g.identity()))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
if available_gateways.is_empty() {
|
if available_gateways.is_empty() {
|
||||||
|
|||||||
@@ -167,10 +167,10 @@ where
|
|||||||
source,
|
source,
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
hardcoded_topology.get_gateways()
|
hardcoded_topology.entry_capable_nodes().cloned().collect()
|
||||||
} else {
|
} else {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
crate::init::helpers::current_gateways(
|
crate::init::helpers::gateways_for_init(
|
||||||
&mut rng,
|
&mut rng,
|
||||||
&core.client.nym_api_urls,
|
&core.client.nym_api_urls,
|
||||||
user_agent,
|
user_agent,
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
use super::received_buffer::ReceivedBufferMessage;
|
use super::received_buffer::ReceivedBufferMessage;
|
||||||
use super::statistics_control::StatisticsControl;
|
use super::statistics_control::StatisticsControl;
|
||||||
use super::topology_control::geo_aware_provider::GeoAwareTopologyProvider;
|
|
||||||
use crate::client::base_client::storage::helpers::store_client_keys;
|
use crate::client::base_client::storage::helpers::store_client_keys;
|
||||||
use crate::client::base_client::storage::MixnetClientStorage;
|
use crate::client::base_client::storage::MixnetClientStorage;
|
||||||
use crate::client::cover_traffic_stream::LoopCoverTrafficStream;
|
use crate::client::cover_traffic_stream::LoopCoverTrafficStream;
|
||||||
@@ -24,7 +23,7 @@ use crate::client::replies::reply_storage::{
|
|||||||
};
|
};
|
||||||
use crate::client::topology_control::nym_api_provider::NymApiTopologyProvider;
|
use crate::client::topology_control::nym_api_provider::NymApiTopologyProvider;
|
||||||
use crate::client::topology_control::{
|
use crate::client::topology_control::{
|
||||||
nym_api_provider, TopologyAccessor, TopologyRefresher, TopologyRefresherConfig,
|
TopologyAccessor, TopologyRefresher, TopologyRefresherConfig,
|
||||||
};
|
};
|
||||||
use crate::config::{Config, DebugConfig};
|
use crate::config::{Config, DebugConfig};
|
||||||
use crate::error::ClientCoreError;
|
use crate::error::ClientCoreError;
|
||||||
@@ -464,8 +463,8 @@ where
|
|||||||
details_store
|
details_store
|
||||||
.upgrade_stored_remote_gateway_key(gateway_client.gateway_identity(), &updated_key)
|
.upgrade_stored_remote_gateway_key(gateway_client.gateway_identity(), &updated_key)
|
||||||
.await.map_err(|err| {
|
.await.map_err(|err| {
|
||||||
error!("failed to store upgraded gateway key! this connection might be forever broken now: {err}");
|
error!("failed to store upgraded gateway key! this connection might be forever broken now: {err}");
|
||||||
ClientCoreError::GatewaysDetailsStoreError { source: Box::new(err) }
|
ClientCoreError::GatewaysDetailsStoreError { source: Box::new(err) }
|
||||||
})?
|
})?
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -473,6 +472,7 @@ where
|
|||||||
.claim_initial_bandwidth()
|
.claim_initial_bandwidth()
|
||||||
.await
|
.await
|
||||||
.map_err(gateway_failure)?;
|
.map_err(gateway_failure)?;
|
||||||
|
|
||||||
gateway_client
|
gateway_client
|
||||||
.start_listening_for_mixnet_messages()
|
.start_listening_for_mixnet_messages()
|
||||||
.map_err(gateway_failure)?;
|
.map_err(gateway_failure)?;
|
||||||
@@ -539,15 +539,15 @@ where
|
|||||||
// if no custom provider was ... provided ..., create one using nym-api
|
// if no custom provider was ... provided ..., create one using nym-api
|
||||||
custom_provider.unwrap_or_else(|| match config_topology.topology_structure {
|
custom_provider.unwrap_or_else(|| match config_topology.topology_structure {
|
||||||
config::TopologyStructure::NymApi => Box::new(NymApiTopologyProvider::new(
|
config::TopologyStructure::NymApi => Box::new(NymApiTopologyProvider::new(
|
||||||
nym_api_provider::Config {
|
config_topology,
|
||||||
min_mixnode_performance: config_topology.minimum_mixnode_performance,
|
|
||||||
min_gateway_performance: config_topology.minimum_gateway_performance,
|
|
||||||
},
|
|
||||||
nym_api_urls,
|
nym_api_urls,
|
||||||
user_agent,
|
user_agent,
|
||||||
)),
|
)),
|
||||||
config::TopologyStructure::GeoAware(group_by) => {
|
config::TopologyStructure::GeoAware(group_by) => {
|
||||||
Box::new(GeoAwareTopologyProvider::new(nym_api_urls, group_by))
|
warn!("using deprecated 'GeoAware' topology provider - this option will be removed very soon");
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
|
Box::new(crate::client::topology_control::GeoAwareTopologyProvider::new(nym_api_urls, group_by))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -558,7 +558,7 @@ where
|
|||||||
topology_provider: Box<dyn TopologyProvider + Send + Sync>,
|
topology_provider: Box<dyn TopologyProvider + Send + Sync>,
|
||||||
topology_config: config::Topology,
|
topology_config: config::Topology,
|
||||||
topology_accessor: TopologyAccessor,
|
topology_accessor: TopologyAccessor,
|
||||||
local_gateway: &NodeIdentity,
|
local_gateway: NodeIdentity,
|
||||||
wait_for_gateway: bool,
|
wait_for_gateway: bool,
|
||||||
mut shutdown: TaskClient,
|
mut shutdown: TaskClient,
|
||||||
) -> Result<(), ClientCoreError> {
|
) -> Result<(), ClientCoreError> {
|
||||||
@@ -590,7 +590,7 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Err(err) = topology_refresher
|
if let Err(err) = topology_refresher
|
||||||
.ensure_contains_gateway(local_gateway)
|
.ensure_contains_routable_egress(local_gateway)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
if let Some(waiting_timeout) = gateway_wait_timeout {
|
if let Some(waiting_timeout) = gateway_wait_timeout {
|
||||||
@@ -740,7 +740,8 @@ where
|
|||||||
|
|
||||||
// channels responsible for controlling ack messages
|
// channels responsible for controlling ack messages
|
||||||
let (ack_sender, ack_receiver) = mpsc::unbounded();
|
let (ack_sender, ack_receiver) = mpsc::unbounded();
|
||||||
let shared_topology_accessor = TopologyAccessor::new();
|
let shared_topology_accessor =
|
||||||
|
TopologyAccessor::new(self.config.debug.topology.ignore_egress_epoch_role);
|
||||||
|
|
||||||
// Shutdown notifier for signalling tasks to stop
|
// Shutdown notifier for signalling tasks to stop
|
||||||
let shutdown = self
|
let shutdown = self
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ pub trait MixnetClientStorage {
|
|||||||
fn gateway_details_store(&self) -> &Self::GatewaysDetailsStore;
|
fn gateway_details_store(&self) -> &Self::GatewaysDetailsStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct Ephemeral {
|
pub struct Ephemeral {
|
||||||
key_store: InMemEphemeralKeys,
|
key_store: InMemEphemeralKeys,
|
||||||
reply_store: reply_storage::Empty,
|
reply_store: reply_storage::Empty,
|
||||||
@@ -114,6 +114,7 @@ impl MixnetClientStorage for Ephemeral {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
#[cfg(all(
|
#[cfg(all(
|
||||||
not(target_arch = "wasm32"),
|
not(target_arch = "wasm32"),
|
||||||
feature = "fs-surb-storage",
|
feature = "fs-surb-storage",
|
||||||
|
|||||||
@@ -163,6 +163,7 @@ impl LoopCoverTrafficStream<OsRng> {
|
|||||||
// poisson delay, but is it really a problem?
|
// poisson delay, but is it really a problem?
|
||||||
let topology_permit = self.topology_access.get_read_permit().await;
|
let topology_permit = self.topology_access.get_read_permit().await;
|
||||||
// the ack is sent back to ourselves (and then ignored)
|
// the ack is sent back to ourselves (and then ignored)
|
||||||
|
|
||||||
let topology_ref = match topology_permit.try_get_valid_topology_ref(
|
let topology_ref = match topology_permit.try_get_valid_topology_ref(
|
||||||
&self.our_full_destination,
|
&self.our_full_destination,
|
||||||
Some(&self.our_full_destination),
|
Some(&self.our_full_destination),
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ pub enum InputMessage {
|
|||||||
recipient: Recipient,
|
recipient: Recipient,
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
lane: TransmissionLane,
|
lane: TransmissionLane,
|
||||||
mix_hops: Option<u8>,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Creates a message used for a duplex anonymous communication where the recipient
|
/// Creates a message used for a duplex anonymous communication where the recipient
|
||||||
@@ -44,7 +43,6 @@ pub enum InputMessage {
|
|||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
reply_surbs: u32,
|
reply_surbs: u32,
|
||||||
lane: TransmissionLane,
|
lane: TransmissionLane,
|
||||||
mix_hops: Option<u8>,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Attempt to use our internally received and stored `ReplySurb` to send the message back
|
/// Attempt to use our internally received and stored `ReplySurb` to send the message back
|
||||||
@@ -94,29 +92,6 @@ impl InputMessage {
|
|||||||
recipient,
|
recipient,
|
||||||
data,
|
data,
|
||||||
lane,
|
lane,
|
||||||
mix_hops: None,
|
|
||||||
};
|
|
||||||
if let Some(packet_type) = packet_type {
|
|
||||||
InputMessage::new_wrapper(message, packet_type)
|
|
||||||
} else {
|
|
||||||
message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IMHO `new_regular` should take `mix_hops: Option<u8>` as an argument instead of creating
|
|
||||||
// this function, but that would potentially break backwards compatibility with the current API
|
|
||||||
pub fn new_regular_with_custom_hops(
|
|
||||||
recipient: Recipient,
|
|
||||||
data: Vec<u8>,
|
|
||||||
lane: TransmissionLane,
|
|
||||||
packet_type: Option<PacketType>,
|
|
||||||
mix_hops: Option<u8>,
|
|
||||||
) -> Self {
|
|
||||||
let message = InputMessage::Regular {
|
|
||||||
recipient,
|
|
||||||
data,
|
|
||||||
lane,
|
|
||||||
mix_hops,
|
|
||||||
};
|
};
|
||||||
if let Some(packet_type) = packet_type {
|
if let Some(packet_type) = packet_type {
|
||||||
InputMessage::new_wrapper(message, packet_type)
|
InputMessage::new_wrapper(message, packet_type)
|
||||||
@@ -137,7 +112,6 @@ impl InputMessage {
|
|||||||
data,
|
data,
|
||||||
reply_surbs,
|
reply_surbs,
|
||||||
lane,
|
lane,
|
||||||
mix_hops: None,
|
|
||||||
};
|
};
|
||||||
if let Some(packet_type) = packet_type {
|
if let Some(packet_type) = packet_type {
|
||||||
InputMessage::new_wrapper(message, packet_type)
|
InputMessage::new_wrapper(message, packet_type)
|
||||||
@@ -154,14 +128,12 @@ impl InputMessage {
|
|||||||
reply_surbs: u32,
|
reply_surbs: u32,
|
||||||
lane: TransmissionLane,
|
lane: TransmissionLane,
|
||||||
packet_type: Option<PacketType>,
|
packet_type: Option<PacketType>,
|
||||||
mix_hops: Option<u8>,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let message = InputMessage::Anonymous {
|
let message = InputMessage::Anonymous {
|
||||||
recipient,
|
recipient,
|
||||||
data,
|
data,
|
||||||
reply_surbs,
|
reply_surbs,
|
||||||
lane,
|
lane,
|
||||||
mix_hops,
|
|
||||||
};
|
};
|
||||||
if let Some(packet_type) = packet_type {
|
if let Some(packet_type) = packet_type {
|
||||||
InputMessage::new_wrapper(message, packet_type)
|
InputMessage::new_wrapper(message, packet_type)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
use crate::client::key_manager::ClientKeys;
|
use crate::client::key_manager::ClientKeys;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
use std::sync::Arc;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
@@ -64,6 +65,7 @@ pub enum OnDiskKeysError {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub struct OnDiskKeys {
|
pub struct OnDiskKeys {
|
||||||
paths: ClientKeysPaths,
|
paths: ClientKeysPaths,
|
||||||
@@ -193,9 +195,9 @@ impl KeyStore for OnDiskKeys {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct InMemEphemeralKeys {
|
pub struct InMemEphemeralKeys {
|
||||||
keys: Mutex<Option<ClientKeys>>,
|
keys: Arc<Mutex<Option<ClientKeys>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
|||||||
+8
-35
@@ -73,11 +73,10 @@ where
|
|||||||
content: Vec<u8>,
|
content: Vec<u8>,
|
||||||
lane: TransmissionLane,
|
lane: TransmissionLane,
|
||||||
packet_type: PacketType,
|
packet_type: PacketType,
|
||||||
mix_hops: Option<u8>,
|
|
||||||
) {
|
) {
|
||||||
if let Err(err) = self
|
if let Err(err) = self
|
||||||
.message_handler
|
.message_handler
|
||||||
.try_send_plain_message(recipient, content, lane, packet_type, mix_hops)
|
.try_send_plain_message(recipient, content, lane, packet_type)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
warn!("failed to send a plain message - {err}")
|
warn!("failed to send a plain message - {err}")
|
||||||
@@ -91,18 +90,10 @@ where
|
|||||||
reply_surbs: u32,
|
reply_surbs: u32,
|
||||||
lane: TransmissionLane,
|
lane: TransmissionLane,
|
||||||
packet_type: PacketType,
|
packet_type: PacketType,
|
||||||
mix_hops: Option<u8>,
|
|
||||||
) {
|
) {
|
||||||
if let Err(err) = self
|
if let Err(err) = self
|
||||||
.message_handler
|
.message_handler
|
||||||
.try_send_message_with_reply_surbs(
|
.try_send_message_with_reply_surbs(recipient, content, reply_surbs, lane, packet_type)
|
||||||
recipient,
|
|
||||||
content,
|
|
||||||
reply_surbs,
|
|
||||||
lane,
|
|
||||||
packet_type,
|
|
||||||
mix_hops,
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
warn!("failed to send a repliable message - {err}")
|
warn!("failed to send a repliable message - {err}")
|
||||||
@@ -115,9 +106,8 @@ where
|
|||||||
recipient,
|
recipient,
|
||||||
data,
|
data,
|
||||||
lane,
|
lane,
|
||||||
mix_hops,
|
|
||||||
} => {
|
} => {
|
||||||
self.handle_plain_message(recipient, data, lane, PacketType::Mix, mix_hops)
|
self.handle_plain_message(recipient, data, lane, PacketType::Mix)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
InputMessage::Anonymous {
|
InputMessage::Anonymous {
|
||||||
@@ -125,17 +115,9 @@ where
|
|||||||
data,
|
data,
|
||||||
reply_surbs,
|
reply_surbs,
|
||||||
lane,
|
lane,
|
||||||
mix_hops,
|
|
||||||
} => {
|
} => {
|
||||||
self.handle_repliable_message(
|
self.handle_repliable_message(recipient, data, reply_surbs, lane, PacketType::Mix)
|
||||||
recipient,
|
.await
|
||||||
data,
|
|
||||||
reply_surbs,
|
|
||||||
lane,
|
|
||||||
PacketType::Mix,
|
|
||||||
mix_hops,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
InputMessage::Reply {
|
InputMessage::Reply {
|
||||||
recipient_tag,
|
recipient_tag,
|
||||||
@@ -153,9 +135,8 @@ where
|
|||||||
recipient,
|
recipient,
|
||||||
data,
|
data,
|
||||||
lane,
|
lane,
|
||||||
mix_hops,
|
|
||||||
} => {
|
} => {
|
||||||
self.handle_plain_message(recipient, data, lane, packet_type, mix_hops)
|
self.handle_plain_message(recipient, data, lane, packet_type)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
InputMessage::Anonymous {
|
InputMessage::Anonymous {
|
||||||
@@ -163,17 +144,9 @@ where
|
|||||||
data,
|
data,
|
||||||
reply_surbs,
|
reply_surbs,
|
||||||
lane,
|
lane,
|
||||||
mix_hops,
|
|
||||||
} => {
|
} => {
|
||||||
self.handle_repliable_message(
|
self.handle_repliable_message(recipient, data, reply_surbs, lane, packet_type)
|
||||||
recipient,
|
.await
|
||||||
data,
|
|
||||||
reply_surbs,
|
|
||||||
lane,
|
|
||||||
packet_type,
|
|
||||||
mix_hops,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
InputMessage::Reply {
|
InputMessage::Reply {
|
||||||
recipient_tag,
|
recipient_tag,
|
||||||
|
|||||||
@@ -70,7 +70,6 @@ pub(crate) struct PendingAcknowledgement {
|
|||||||
message_chunk: Fragment,
|
message_chunk: Fragment,
|
||||||
delay: SphinxDelay,
|
delay: SphinxDelay,
|
||||||
destination: PacketDestination,
|
destination: PacketDestination,
|
||||||
mix_hops: Option<u8>,
|
|
||||||
retransmissions: u32,
|
retransmissions: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,13 +79,11 @@ impl PendingAcknowledgement {
|
|||||||
message_chunk: Fragment,
|
message_chunk: Fragment,
|
||||||
delay: SphinxDelay,
|
delay: SphinxDelay,
|
||||||
recipient: Recipient,
|
recipient: Recipient,
|
||||||
mix_hops: Option<u8>,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
PendingAcknowledgement {
|
PendingAcknowledgement {
|
||||||
message_chunk,
|
message_chunk,
|
||||||
delay,
|
delay,
|
||||||
destination: PacketDestination::KnownRecipient(recipient.into()),
|
destination: PacketDestination::KnownRecipient(recipient.into()),
|
||||||
mix_hops,
|
|
||||||
retransmissions: 0,
|
retransmissions: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -104,9 +101,6 @@ impl PendingAcknowledgement {
|
|||||||
recipient_tag,
|
recipient_tag,
|
||||||
extra_surb_request,
|
extra_surb_request,
|
||||||
},
|
},
|
||||||
// Messages sent using SURBs are using the number of mix hops set by the recipient when
|
|
||||||
// they provided the SURBs, so it doesn't make sense to include it here.
|
|
||||||
mix_hops: None,
|
|
||||||
retransmissions: 0,
|
retransmissions: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-8
@@ -52,18 +52,12 @@ where
|
|||||||
packet_recipient: Recipient,
|
packet_recipient: Recipient,
|
||||||
chunk_data: Fragment,
|
chunk_data: Fragment,
|
||||||
packet_type: PacketType,
|
packet_type: PacketType,
|
||||||
mix_hops: Option<u8>,
|
|
||||||
) -> Result<PreparedFragment, PreparationError> {
|
) -> Result<PreparedFragment, PreparationError> {
|
||||||
debug!("retransmitting normal packet...");
|
debug!("retransmitting normal packet...");
|
||||||
|
|
||||||
// TODO: Figure out retransmission packet type signaling
|
// TODO: Figure out retransmission packet type signaling
|
||||||
self.message_handler
|
self.message_handler
|
||||||
.try_prepare_single_chunk_for_sending(
|
.try_prepare_single_chunk_for_sending(packet_recipient, chunk_data, packet_type)
|
||||||
packet_recipient,
|
|
||||||
chunk_data,
|
|
||||||
packet_type,
|
|
||||||
mix_hops,
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,7 +104,6 @@ where
|
|||||||
**recipient,
|
**recipient,
|
||||||
timed_out_ack.message_chunk.clone(),
|
timed_out_ack.message_chunk.clone(),
|
||||||
packet_type,
|
packet_type,
|
||||||
timed_out_ack.mix_hops,
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,11 +15,11 @@ use nym_sphinx::anonymous_replies::requests::{AnonymousSenderTag, RepliableMessa
|
|||||||
use nym_sphinx::anonymous_replies::{ReplySurb, SurbEncryptionKey};
|
use nym_sphinx::anonymous_replies::{ReplySurb, SurbEncryptionKey};
|
||||||
use nym_sphinx::chunking::fragment::{Fragment, FragmentIdentifier};
|
use nym_sphinx::chunking::fragment::{Fragment, FragmentIdentifier};
|
||||||
use nym_sphinx::message::NymMessage;
|
use nym_sphinx::message::NymMessage;
|
||||||
use nym_sphinx::params::{PacketSize, PacketType, DEFAULT_NUM_MIX_HOPS};
|
use nym_sphinx::params::{PacketSize, PacketType};
|
||||||
use nym_sphinx::preparer::{MessagePreparer, PreparedFragment};
|
use nym_sphinx::preparer::{MessagePreparer, PreparedFragment};
|
||||||
use nym_sphinx::Delay;
|
use nym_sphinx::Delay;
|
||||||
use nym_task::connections::TransmissionLane;
|
use nym_task::connections::TransmissionLane;
|
||||||
use nym_topology::{NymTopology, NymTopologyError};
|
use nym_topology::{NymRouteProvider, NymTopologyError};
|
||||||
use rand::{CryptoRng, Rng};
|
use rand::{CryptoRng, Rng};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -100,10 +100,6 @@ pub(crate) struct Config {
|
|||||||
/// Average delay an acknowledgement packet is going to get delay at a single mixnode.
|
/// Average delay an acknowledgement packet is going to get delay at a single mixnode.
|
||||||
average_ack_delay: Duration,
|
average_ack_delay: Duration,
|
||||||
|
|
||||||
/// Number of mix hops each packet ('real' message, ack, reply) is expected to take.
|
|
||||||
/// Note that it does not include gateway hops.
|
|
||||||
num_mix_hops: u8,
|
|
||||||
|
|
||||||
/// Primary predefined packet size used for the encapsulated messages.
|
/// Primary predefined packet size used for the encapsulated messages.
|
||||||
primary_packet_size: PacketSize,
|
primary_packet_size: PacketSize,
|
||||||
|
|
||||||
@@ -125,19 +121,11 @@ impl Config {
|
|||||||
deterministic_route_selection,
|
deterministic_route_selection,
|
||||||
average_packet_delay,
|
average_packet_delay,
|
||||||
average_ack_delay,
|
average_ack_delay,
|
||||||
num_mix_hops: DEFAULT_NUM_MIX_HOPS,
|
|
||||||
primary_packet_size: PacketSize::default(),
|
primary_packet_size: PacketSize::default(),
|
||||||
secondary_packet_size: None,
|
secondary_packet_size: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allows setting non-default number of expected mix hops in the network.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn with_mix_hops(mut self, hops: u8) -> Self {
|
|
||||||
self.num_mix_hops = hops;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Allows setting non-default size of the sphinx packets sent out.
|
/// Allows setting non-default size of the sphinx packets sent out.
|
||||||
pub fn with_custom_primary_packet_size(mut self, packet_size: PacketSize) -> Self {
|
pub fn with_custom_primary_packet_size(mut self, packet_size: PacketSize) -> Self {
|
||||||
self.primary_packet_size = packet_size;
|
self.primary_packet_size = packet_size;
|
||||||
@@ -185,9 +173,7 @@ where
|
|||||||
config.sender_address,
|
config.sender_address,
|
||||||
config.average_packet_delay,
|
config.average_packet_delay,
|
||||||
config.average_ack_delay,
|
config.average_ack_delay,
|
||||||
)
|
);
|
||||||
.with_mix_hops(config.num_mix_hops);
|
|
||||||
|
|
||||||
MessageHandler {
|
MessageHandler {
|
||||||
config,
|
config,
|
||||||
rng,
|
rng,
|
||||||
@@ -216,7 +202,7 @@ where
|
|||||||
fn get_topology<'a>(
|
fn get_topology<'a>(
|
||||||
&self,
|
&self,
|
||||||
permit: &'a TopologyReadPermit<'a>,
|
permit: &'a TopologyReadPermit<'a>,
|
||||||
) -> Result<&'a NymTopology, PreparationError> {
|
) -> Result<&'a NymRouteProvider, PreparationError> {
|
||||||
match permit.try_get_valid_topology_ref(&self.config.sender_address, None) {
|
match permit.try_get_valid_topology_ref(&self.config.sender_address, None) {
|
||||||
Ok(topology_ref) => Ok(topology_ref),
|
Ok(topology_ref) => Ok(topology_ref),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@@ -233,9 +219,8 @@ where
|
|||||||
return self.config.primary_packet_size;
|
return self.config.primary_packet_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
let primary_count =
|
let primary_count = msg.required_packets(self.config.primary_packet_size);
|
||||||
msg.required_packets(self.config.primary_packet_size, self.config.num_mix_hops);
|
let secondary_count = msg.required_packets(secondary_packet);
|
||||||
let secondary_count = msg.required_packets(secondary_packet, self.config.num_mix_hops);
|
|
||||||
|
|
||||||
trace!("This message would require: {primary_count} primary packets or {secondary_count} secondary packets...");
|
trace!("This message would require: {primary_count} primary packets or {secondary_count} secondary packets...");
|
||||||
// if there would be no benefit in using the secondary packet - use the primary (duh)
|
// if there would be no benefit in using the secondary packet - use the primary (duh)
|
||||||
@@ -424,10 +409,9 @@ where
|
|||||||
message: Vec<u8>,
|
message: Vec<u8>,
|
||||||
lane: TransmissionLane,
|
lane: TransmissionLane,
|
||||||
packet_type: PacketType,
|
packet_type: PacketType,
|
||||||
mix_hops: Option<u8>,
|
|
||||||
) -> Result<(), PreparationError> {
|
) -> Result<(), PreparationError> {
|
||||||
let message = NymMessage::new_plain(message);
|
let message = NymMessage::new_plain(message);
|
||||||
self.try_split_and_send_non_reply_message(message, recipient, lane, packet_type, mix_hops)
|
self.try_split_and_send_non_reply_message(message, recipient, lane, packet_type)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -437,7 +421,6 @@ where
|
|||||||
recipient: Recipient,
|
recipient: Recipient,
|
||||||
lane: TransmissionLane,
|
lane: TransmissionLane,
|
||||||
packet_type: PacketType,
|
packet_type: PacketType,
|
||||||
mix_hops: Option<u8>,
|
|
||||||
) -> Result<(), PreparationError> {
|
) -> Result<(), PreparationError> {
|
||||||
debug!("Sending non-reply message with packet type {packet_type}");
|
debug!("Sending non-reply message with packet type {packet_type}");
|
||||||
// TODO: I really dislike existence of this assertion, it implies code has to be re-organised
|
// TODO: I really dislike existence of this assertion, it implies code has to be re-organised
|
||||||
@@ -470,7 +453,6 @@ where
|
|||||||
&self.config.ack_key,
|
&self.config.ack_key,
|
||||||
&recipient,
|
&recipient,
|
||||||
packet_type,
|
packet_type,
|
||||||
mix_hops,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let real_message = RealMessage::new(
|
let real_message = RealMessage::new(
|
||||||
@@ -478,8 +460,7 @@ where
|
|||||||
Some(fragment.fragment_identifier()),
|
Some(fragment.fragment_identifier()),
|
||||||
);
|
);
|
||||||
let delay = prepared_fragment.total_delay;
|
let delay = prepared_fragment.total_delay;
|
||||||
let pending_ack =
|
let pending_ack = PendingAcknowledgement::new_known(fragment, delay, recipient);
|
||||||
PendingAcknowledgement::new_known(fragment, delay, recipient, mix_hops);
|
|
||||||
|
|
||||||
real_messages.push(real_message);
|
real_messages.push(real_message);
|
||||||
pending_acks.push(pending_ack);
|
pending_acks.push(pending_ack);
|
||||||
@@ -496,7 +477,6 @@ where
|
|||||||
recipient: Recipient,
|
recipient: Recipient,
|
||||||
amount: u32,
|
amount: u32,
|
||||||
packet_type: PacketType,
|
packet_type: PacketType,
|
||||||
mix_hops: Option<u8>,
|
|
||||||
) -> Result<(), PreparationError> {
|
) -> Result<(), PreparationError> {
|
||||||
debug!("Sending additional reply SURBs with packet type {packet_type}");
|
debug!("Sending additional reply SURBs with packet type {packet_type}");
|
||||||
let sender_tag = self.get_or_create_sender_tag(&recipient);
|
let sender_tag = self.get_or_create_sender_tag(&recipient);
|
||||||
@@ -513,7 +493,6 @@ where
|
|||||||
recipient,
|
recipient,
|
||||||
TransmissionLane::AdditionalReplySurbs,
|
TransmissionLane::AdditionalReplySurbs,
|
||||||
packet_type,
|
packet_type,
|
||||||
mix_hops,
|
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@@ -530,7 +509,6 @@ where
|
|||||||
num_reply_surbs: u32,
|
num_reply_surbs: u32,
|
||||||
lane: TransmissionLane,
|
lane: TransmissionLane,
|
||||||
packet_type: PacketType,
|
packet_type: PacketType,
|
||||||
mix_hops: Option<u8>,
|
|
||||||
) -> Result<(), SurbWrappedPreparationError> {
|
) -> Result<(), SurbWrappedPreparationError> {
|
||||||
debug!("Sending message with reply SURBs with packet type {packet_type}");
|
debug!("Sending message with reply SURBs with packet type {packet_type}");
|
||||||
let sender_tag = self.get_or_create_sender_tag(&recipient);
|
let sender_tag = self.get_or_create_sender_tag(&recipient);
|
||||||
@@ -541,7 +519,7 @@ where
|
|||||||
let message =
|
let message =
|
||||||
NymMessage::new_repliable(RepliableMessage::new_data(message, sender_tag, reply_surbs));
|
NymMessage::new_repliable(RepliableMessage::new_data(message, sender_tag, reply_surbs));
|
||||||
|
|
||||||
self.try_split_and_send_non_reply_message(message, recipient, lane, packet_type, mix_hops)
|
self.try_split_and_send_non_reply_message(message, recipient, lane, packet_type)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
log::trace!("storing {} reply keys", reply_keys.len());
|
log::trace!("storing {} reply keys", reply_keys.len());
|
||||||
@@ -555,23 +533,18 @@ where
|
|||||||
recipient: Recipient,
|
recipient: Recipient,
|
||||||
chunk: Fragment,
|
chunk: Fragment,
|
||||||
packet_type: PacketType,
|
packet_type: PacketType,
|
||||||
mix_hops: Option<u8>,
|
|
||||||
) -> Result<PreparedFragment, PreparationError> {
|
) -> Result<PreparedFragment, PreparationError> {
|
||||||
debug!("Sending single chunk with packet type {packet_type}");
|
debug!("Sending single chunk with packet type {packet_type}");
|
||||||
let topology_permit = self.topology_access.get_read_permit().await;
|
let topology_permit = self.topology_access.get_read_permit().await;
|
||||||
let topology = self.get_topology(&topology_permit)?;
|
let topology = self.get_topology(&topology_permit)?;
|
||||||
|
|
||||||
let prepared_fragment = self
|
let prepared_fragment = self.message_preparer.prepare_chunk_for_sending(
|
||||||
.message_preparer
|
chunk,
|
||||||
.prepare_chunk_for_sending(
|
topology,
|
||||||
chunk,
|
&self.config.ack_key,
|
||||||
topology,
|
&recipient,
|
||||||
&self.config.ack_key,
|
packet_type,
|
||||||
&recipient,
|
)?;
|
||||||
packet_type,
|
|
||||||
mix_hops,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Ok(prepared_fragment)
|
Ok(prepared_fragment)
|
||||||
}
|
}
|
||||||
@@ -624,16 +597,13 @@ where
|
|||||||
Err(err) => return Err(err.return_surbs(vec![reply_surb])),
|
Err(err) => return Err(err.return_surbs(vec![reply_surb])),
|
||||||
};
|
};
|
||||||
|
|
||||||
let prepared_fragment = self
|
let prepared_fragment = self.message_preparer.prepare_reply_chunk_for_sending(
|
||||||
.message_preparer
|
chunk,
|
||||||
.prepare_reply_chunk_for_sending(
|
topology,
|
||||||
chunk,
|
&self.config.ack_key,
|
||||||
topology,
|
reply_surb,
|
||||||
&self.config.ack_key,
|
PacketType::Mix,
|
||||||
reply_surb,
|
)?;
|
||||||
PacketType::Mix,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Ok(prepared_fragment)
|
Ok(prepared_fragment)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,12 @@ use self::{
|
|||||||
acknowledgement_control::AcknowledgementController, real_traffic_stream::OutQueueControl,
|
acknowledgement_control::AcknowledgementController, real_traffic_stream::OutQueueControl,
|
||||||
};
|
};
|
||||||
use crate::client::real_messages_control::message_handler::MessageHandler;
|
use crate::client::real_messages_control::message_handler::MessageHandler;
|
||||||
|
use crate::client::replies::reply_controller;
|
||||||
use crate::client::replies::reply_controller::{
|
use crate::client::replies::reply_controller::{
|
||||||
ReplyController, ReplyControllerReceiver, ReplyControllerSender,
|
ReplyController, ReplyControllerReceiver, ReplyControllerSender,
|
||||||
};
|
};
|
||||||
use crate::client::replies::reply_storage::CombinedReplyStorage;
|
use crate::client::replies::reply_storage::CombinedReplyStorage;
|
||||||
|
use crate::config;
|
||||||
use crate::{
|
use crate::{
|
||||||
client::{
|
client::{
|
||||||
inbound_messages::InputMessageReceiver, mix_traffic::BatchMixMessageSender,
|
inbound_messages::InputMessageReceiver, mix_traffic::BatchMixMessageSender,
|
||||||
@@ -27,16 +29,13 @@ use nym_gateway_client::AcknowledgementReceiver;
|
|||||||
use nym_sphinx::acknowledgements::AckKey;
|
use nym_sphinx::acknowledgements::AckKey;
|
||||||
use nym_sphinx::addressing::clients::Recipient;
|
use nym_sphinx::addressing::clients::Recipient;
|
||||||
use nym_sphinx::params::PacketType;
|
use nym_sphinx::params::PacketType;
|
||||||
|
use nym_statistics_common::clients::ClientStatsSender;
|
||||||
use nym_task::connections::{ConnectionCommandReceiver, LaneQueueLengths};
|
use nym_task::connections::{ConnectionCommandReceiver, LaneQueueLengths};
|
||||||
use rand::{rngs::OsRng, CryptoRng, Rng};
|
use rand::{rngs::OsRng, CryptoRng, Rng};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::client::replies::reply_controller;
|
|
||||||
use crate::config;
|
|
||||||
pub(crate) use acknowledgement_control::{AckActionSender, Action};
|
pub(crate) use acknowledgement_control::{AckActionSender, Action};
|
||||||
|
|
||||||
use nym_statistics_common::clients::ClientStatsSender;
|
|
||||||
|
|
||||||
pub(crate) mod acknowledgement_control;
|
pub(crate) mod acknowledgement_control;
|
||||||
pub(crate) mod message_handler;
|
pub(crate) mod message_handler;
|
||||||
pub(crate) mod real_traffic_stream;
|
pub(crate) mod real_traffic_stream;
|
||||||
|
|||||||
@@ -230,6 +230,7 @@ where
|
|||||||
// poisson delay, but is it really a problem?
|
// poisson delay, but is it really a problem?
|
||||||
let topology_permit = self.topology_access.get_read_permit().await;
|
let topology_permit = self.topology_access.get_read_permit().await;
|
||||||
// the ack is sent back to ourselves (and then ignored)
|
// the ack is sent back to ourselves (and then ignored)
|
||||||
|
|
||||||
let topology_ref = match topology_permit.try_get_valid_topology_ref(
|
let topology_ref = match topology_permit.try_get_valid_topology_ref(
|
||||||
&self.config.our_full_destination,
|
&self.config.our_full_destination,
|
||||||
Some(&self.config.our_full_destination),
|
Some(&self.config.our_full_destination),
|
||||||
|
|||||||
+4
-1
@@ -70,7 +70,10 @@ impl SendingDelayController {
|
|||||||
lower_bound,
|
lower_bound,
|
||||||
multiplier_elevated_counter: 0,
|
multiplier_elevated_counter: 0,
|
||||||
time_when_logged_about_elevated_multiplier: now
|
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_changed: now,
|
||||||
time_when_backpressure_detected: now,
|
time_when_backpressure_detected: now,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -516,7 +516,6 @@ where
|
|||||||
recipient,
|
recipient,
|
||||||
to_send,
|
to_send,
|
||||||
nym_sphinx::params::PacketType::Mix,
|
nym_sphinx::params::PacketType::Mix,
|
||||||
self.config.reply_surbs.surb_mix_hops,
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,14 +16,14 @@
|
|||||||
#![warn(clippy::todo)]
|
#![warn(clippy::todo)]
|
||||||
#![warn(clippy::dbg_macro)]
|
#![warn(clippy::dbg_macro)]
|
||||||
|
|
||||||
use std::time::Duration;
|
use futures::StreamExt;
|
||||||
|
|
||||||
use nym_client_core_config_types::StatsReporting;
|
use nym_client_core_config_types::StatsReporting;
|
||||||
use nym_sphinx::addressing::Recipient;
|
use nym_sphinx::addressing::Recipient;
|
||||||
use nym_statistics_common::clients::{
|
use nym_statistics_common::clients::{
|
||||||
ClientStatsController, ClientStatsReceiver, ClientStatsSender,
|
ClientStatsController, ClientStatsReceiver, ClientStatsSender,
|
||||||
};
|
};
|
||||||
use nym_task::connections::TransmissionLane;
|
use nym_task::connections::TransmissionLane;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
client::inbound_messages::{InputMessage, InputMessageSender},
|
client::inbound_messages::{InputMessage, InputMessageSender},
|
||||||
@@ -94,10 +94,32 @@ impl StatisticsControl {
|
|||||||
async fn run_with_shutdown(&mut self, mut task_client: nym_task::TaskClient) {
|
async fn run_with_shutdown(&mut self, mut task_client: nym_task::TaskClient) {
|
||||||
log::debug!("Started StatisticsControl with graceful shutdown support");
|
log::debug!("Started StatisticsControl with graceful shutdown support");
|
||||||
|
|
||||||
let mut stats_report_interval =
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
tokio::time::interval(self.reporting_config.reporting_interval);
|
let mut stats_report_interval = tokio_stream::wrappers::IntervalStream::new(
|
||||||
let mut local_report_interval = tokio::time::interval(LOCAL_REPORT_INTERVAL);
|
tokio::time::interval(self.reporting_config.reporting_interval),
|
||||||
let mut snapshot_interval = tokio::time::interval(SNAPSHOT_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 {
|
loop {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
@@ -108,16 +130,20 @@ impl StatisticsControl {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ = snapshot_interval.tick() => {
|
_ = snapshot_interval.next() => {
|
||||||
self.stats.snapshot();
|
self.stats.snapshot();
|
||||||
}
|
}
|
||||||
_ = stats_report_interval.tick(), if self.reporting_config.enabled && self.reporting_config.provider_address.is_some() => {
|
_ = stats_report_interval.next() => {
|
||||||
// SAFTEY : this branch executes only if reporting is not none, so unwrapp is fine
|
let Some(recipient) = self.reporting_config.provider_address else {
|
||||||
#[allow(clippy::unwrap_used)]
|
continue
|
||||||
self.report_stats(self.reporting_config.provider_address.unwrap()).await;
|
};
|
||||||
|
|
||||||
|
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);
|
self.stats.local_report(&mut task_client);
|
||||||
}
|
}
|
||||||
_ = task_client.recv_with_delay() => {
|
_ = task_client.recv_with_delay() => {
|
||||||
|
|||||||
@@ -2,8 +2,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use nym_sphinx::addressing::clients::Recipient;
|
use nym_sphinx::addressing::clients::Recipient;
|
||||||
use nym_sphinx::params::DEFAULT_NUM_MIX_HOPS;
|
use nym_topology::{NymRouteProvider, NymTopology, NymTopologyError};
|
||||||
use nym_topology::{NymTopology, NymTopologyError};
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -17,29 +16,36 @@ pub struct TopologyAccessorInner {
|
|||||||
// few seconds, while reads are needed every single packet generated.
|
// few seconds, while reads are needed every single packet generated.
|
||||||
// However, proper benchmarks will be needed to determine if `RwLock` is indeed a better
|
// However, proper benchmarks will be needed to determine if `RwLock` is indeed a better
|
||||||
// approach than a `Mutex`
|
// approach than a `Mutex`
|
||||||
topology: RwLock<Option<NymTopology>>,
|
topology: RwLock<NymRouteProvider>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TopologyAccessorInner {
|
impl TopologyAccessorInner {
|
||||||
fn new() -> Self {
|
fn new(initial: NymRouteProvider) -> Self {
|
||||||
TopologyAccessorInner {
|
TopologyAccessorInner {
|
||||||
controlled_manually: AtomicBool::new(false),
|
controlled_manually: AtomicBool::new(false),
|
||||||
released_manual_control: Notify::new(),
|
released_manual_control: Notify::new(),
|
||||||
topology: RwLock::new(None),
|
topology: RwLock::new(initial),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn update(&self, new: Option<NymTopology>) {
|
async fn update(&self, new: Option<NymTopology>) {
|
||||||
*self.topology.write().await = new;
|
let mut guard = self.topology.write().await;
|
||||||
|
|
||||||
|
match new {
|
||||||
|
Some(updated) => {
|
||||||
|
guard.update(updated);
|
||||||
|
}
|
||||||
|
None => guard.clear_topology(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TopologyReadPermit<'a> {
|
pub struct TopologyReadPermit<'a> {
|
||||||
permit: RwLockReadGuard<'a, Option<NymTopology>>,
|
permit: RwLockReadGuard<'a, NymRouteProvider>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for TopologyReadPermit<'_> {
|
impl Deref for TopologyReadPermit<'_> {
|
||||||
type Target = Option<NymTopology>;
|
type Target = NymRouteProvider;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.permit
|
&self.permit
|
||||||
@@ -53,43 +59,31 @@ impl<'a> TopologyReadPermit<'a> {
|
|||||||
&'a self,
|
&'a self,
|
||||||
ack_recipient: &Recipient,
|
ack_recipient: &Recipient,
|
||||||
packet_recipient: Option<&Recipient>,
|
packet_recipient: Option<&Recipient>,
|
||||||
) -> Result<&'a NymTopology, NymTopologyError> {
|
) -> Result<&'a NymRouteProvider, NymTopologyError> {
|
||||||
|
let route_provider = self.permit.deref();
|
||||||
|
let topology = &route_provider.topology;
|
||||||
|
|
||||||
// 1. Have we managed to get anything from the refresher, i.e. have the nym-api queries gone through?
|
// 1. Have we managed to get anything from the refresher, i.e. have the nym-api queries gone through?
|
||||||
let topology = self
|
topology.ensure_not_empty()?;
|
||||||
.permit
|
|
||||||
.as_ref()
|
|
||||||
.ok_or(NymTopologyError::EmptyNetworkTopology)?;
|
|
||||||
|
|
||||||
// 2. does it have any mixnode at all?
|
// 2. does the topology have a node on each mixing layer?
|
||||||
// 3. does it have any gateways at all?
|
topology.ensure_minimally_routable()?;
|
||||||
// 4. does it have a mixnode on each layer?
|
|
||||||
topology.ensure_can_construct_path_through(DEFAULT_NUM_MIX_HOPS)?;
|
|
||||||
|
|
||||||
// 5. does it contain OUR gateway (so that we could create an ack packet)?
|
// 3. does it contain OUR gateway (so that we could create an ack packet)?
|
||||||
if !topology.gateway_exists(ack_recipient.gateway()) {
|
let _ = route_provider.egress_by_identity(ack_recipient.gateway())?;
|
||||||
return Err(NymTopologyError::NonExistentGatewayError {
|
|
||||||
identity_key: ack_recipient.gateway().to_base58_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. for our target recipient, does it contain THEIR gateway (so that we could create
|
// 4. for our target recipient, does it contain THEIR gateway (so that we send anything over?)
|
||||||
if let Some(recipient) = packet_recipient {
|
if let Some(recipient) = packet_recipient {
|
||||||
if !topology.gateway_exists(recipient.gateway()) {
|
let _ = route_provider.egress_by_identity(recipient.gateway())?;
|
||||||
return Err(NymTopologyError::NonExistentGatewayError {
|
|
||||||
identity_key: recipient.gateway().to_base58_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(topology)
|
Ok(route_provider)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<RwLockReadGuard<'a, Option<NymTopology>>> for TopologyReadPermit<'a> {
|
impl<'a> From<RwLockReadGuard<'a, NymRouteProvider>> for TopologyReadPermit<'a> {
|
||||||
fn from(read_permit: RwLockReadGuard<'a, Option<NymTopology>>) -> Self {
|
fn from(permit: RwLockReadGuard<'a, NymRouteProvider>) -> Self {
|
||||||
TopologyReadPermit {
|
TopologyReadPermit { permit }
|
||||||
permit: read_permit,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,9 +93,11 @@ pub struct TopologyAccessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TopologyAccessor {
|
impl TopologyAccessor {
|
||||||
pub fn new() -> Self {
|
pub fn new(ignore_egress_epoch_roles: bool) -> Self {
|
||||||
TopologyAccessor {
|
TopologyAccessor {
|
||||||
inner: Arc::new(TopologyAccessorInner::new()),
|
inner: Arc::new(TopologyAccessorInner::new(NymRouteProvider::new_empty(
|
||||||
|
ignore_egress_epoch_roles,
|
||||||
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,8 +117,21 @@ impl TopologyAccessor {
|
|||||||
self.inner.released_manual_control.notified().await
|
self.inner.released_manual_control.notified().await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated(note = "use .current_route_provider instead")]
|
||||||
pub async fn current_topology(&self) -> Option<NymTopology> {
|
pub async fn current_topology(&self) -> Option<NymTopology> {
|
||||||
self.inner.topology.read().await.clone()
|
self.current_route_provider()
|
||||||
|
.await
|
||||||
|
.as_ref()
|
||||||
|
.map(|p| p.topology.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn current_route_provider(&self) -> Option<RwLockReadGuard<NymRouteProvider>> {
|
||||||
|
let provider = self.inner.topology.read().await;
|
||||||
|
if provider.topology.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(provider)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn manually_change_topology(&self, new_topology: NymTopology) {
|
pub async fn manually_change_topology(&self, new_topology: NymTopology) {
|
||||||
@@ -140,15 +149,11 @@ impl TopologyAccessor {
|
|||||||
// only used by the client at startup to get a slightly more reasonable error message
|
// only used by the client at startup to get a slightly more reasonable error message
|
||||||
// (currently displays as unused because health checker is disabled due to required changes)
|
// (currently displays as unused because health checker is disabled due to required changes)
|
||||||
pub async fn ensure_is_routable(&self) -> Result<(), NymTopologyError> {
|
pub async fn ensure_is_routable(&self) -> Result<(), NymTopologyError> {
|
||||||
match self.inner.topology.read().await.deref() {
|
self.inner
|
||||||
None => Err(NymTopologyError::EmptyNetworkTopology),
|
.topology
|
||||||
Some(ref topology) => topology.ensure_can_construct_path_through(DEFAULT_NUM_MIX_HOPS),
|
.read()
|
||||||
}
|
.await
|
||||||
}
|
.topology
|
||||||
}
|
.ensure_minimally_routable()
|
||||||
|
|
||||||
impl Default for TopologyAccessor {
|
|
||||||
fn default() -> Self {
|
|
||||||
TopologyAccessor::new()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ use log::{debug, error};
|
|||||||
use nym_explorer_client::{ExplorerClient, PrettyDetailedMixNodeBond};
|
use nym_explorer_client::{ExplorerClient, PrettyDetailedMixNodeBond};
|
||||||
use nym_network_defaults::var_names::EXPLORER_API;
|
use nym_network_defaults::var_names::EXPLORER_API;
|
||||||
use nym_topology::{
|
use nym_topology::{
|
||||||
nym_topology_from_basic_info,
|
|
||||||
provider_trait::{async_trait, TopologyProvider},
|
provider_trait::{async_trait, TopologyProvider},
|
||||||
NymTopology,
|
NymTopology,
|
||||||
};
|
};
|
||||||
@@ -15,8 +14,6 @@ use url::Url;
|
|||||||
|
|
||||||
pub use nym_country_group::CountryGroup;
|
pub use nym_country_group::CountryGroup;
|
||||||
|
|
||||||
const MIN_NODES_PER_LAYER: usize = 1;
|
|
||||||
|
|
||||||
fn create_explorer_client() -> Option<ExplorerClient> {
|
fn create_explorer_client() -> Option<ExplorerClient> {
|
||||||
let Ok(explorer_api_url) = std::env::var(EXPLORER_API) else {
|
let Ok(explorer_api_url) = std::env::var(EXPLORER_API) else {
|
||||||
error!("Missing EXPLORER_API");
|
error!("Missing EXPLORER_API");
|
||||||
@@ -63,30 +60,20 @@ fn log_mixnode_distribution(mixnodes: &HashMap<CountryGroup, Vec<NodeId>>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn check_layer_integrity(topology: NymTopology) -> Result<(), ()> {
|
fn check_layer_integrity(topology: NymTopology) -> Result<(), ()> {
|
||||||
let mixes = topology.mixes();
|
if topology.ensure_minimally_routable().is_err() {
|
||||||
if mixes.keys().len() < 3 {
|
|
||||||
error!("Layer is missing in topology!");
|
error!("Layer is missing in topology!");
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
for (layer, mixnodes) in mixes {
|
|
||||||
debug!("Layer {:?} has {} mixnodes", layer, mixnodes.len());
|
|
||||||
if mixnodes.len() < MIN_NODES_PER_LAYER {
|
|
||||||
error!(
|
|
||||||
"There are only {} mixnodes in layer {:?}",
|
|
||||||
mixnodes.len(),
|
|
||||||
layer
|
|
||||||
);
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated(note = "use NymApiTopologyProvider instead as explorer API will soon be removed")]
|
||||||
pub struct GeoAwareTopologyProvider {
|
pub struct GeoAwareTopologyProvider {
|
||||||
validator_client: nym_validator_client::client::NymApiClient,
|
validator_client: nym_validator_client::client::NymApiClient,
|
||||||
filter_on: GroupBy,
|
filter_on: GroupBy,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
impl GeoAwareTopologyProvider {
|
impl GeoAwareTopologyProvider {
|
||||||
pub fn new(mut nym_api_urls: Vec<Url>, filter_on: GroupBy) -> GeoAwareTopologyProvider {
|
pub fn new(mut nym_api_urls: Vec<Url>, filter_on: GroupBy) -> GeoAwareTopologyProvider {
|
||||||
log::info!(
|
log::info!(
|
||||||
@@ -104,6 +91,15 @@ impl GeoAwareTopologyProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn get_topology(&self) -> Option<NymTopology> {
|
async fn get_topology(&self) -> Option<NymTopology> {
|
||||||
|
let rewarded_set = self
|
||||||
|
.validator_client
|
||||||
|
.get_current_rewarded_set()
|
||||||
|
.await
|
||||||
|
.inspect_err(|err| error!("failed to get current rewarded set: {err}"))
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
let mut topology = NymTopology::new_empty(rewarded_set);
|
||||||
|
|
||||||
let mixnodes = match self
|
let mixnodes = match self
|
||||||
.validator_client
|
.validator_client
|
||||||
.get_all_basic_active_mixing_assigned_nodes()
|
.get_all_basic_active_mixing_assigned_nodes()
|
||||||
@@ -187,7 +183,8 @@ impl GeoAwareTopologyProvider {
|
|||||||
.filter(|m| filtered_mixnode_ids.contains(&m.node_id))
|
.filter(|m| filtered_mixnode_ids.contains(&m.node_id))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let topology = nym_topology_from_basic_info(&mixnodes, &gateways);
|
topology.add_skimmed_nodes(&mixnodes);
|
||||||
|
topology.add_skimmed_nodes(&gateways);
|
||||||
|
|
||||||
// TODO: return real error type
|
// TODO: return real error type
|
||||||
check_layer_integrity(topology.clone()).ok()?;
|
check_layer_integrity(topology.clone()).ok()?;
|
||||||
@@ -196,6 +193,7 @@ impl GeoAwareTopologyProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl TopologyProvider for GeoAwareTopologyProvider {
|
impl TopologyProvider for GeoAwareTopologyProvider {
|
||||||
@@ -205,6 +203,7 @@ impl TopologyProvider for GeoAwareTopologyProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
impl TopologyProvider for GeoAwareTopologyProvider {
|
impl TopologyProvider for GeoAwareTopologyProvider {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ mod accessor;
|
|||||||
pub mod geo_aware_provider;
|
pub mod geo_aware_provider;
|
||||||
pub mod nym_api_provider;
|
pub mod nym_api_provider;
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
pub use geo_aware_provider::GeoAwareTopologyProvider;
|
pub use geo_aware_provider::GeoAwareTopologyProvider;
|
||||||
pub use nym_api_provider::{Config as NymApiTopologyProviderConfig, NymApiTopologyProvider};
|
pub use nym_api_provider::{Config as NymApiTopologyProviderConfig, NymApiTopologyProvider};
|
||||||
pub use nym_topology::provider_trait::TopologyProvider;
|
pub use nym_topology::provider_trait::TopologyProvider;
|
||||||
@@ -27,7 +28,7 @@ pub use nym_topology::provider_trait::TopologyProvider;
|
|||||||
const MAX_FAILURE_COUNT: usize = 10;
|
const MAX_FAILURE_COUNT: usize = 10;
|
||||||
|
|
||||||
pub struct TopologyRefresherConfig {
|
pub struct TopologyRefresherConfig {
|
||||||
refresh_rate: Duration,
|
pub refresh_rate: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TopologyRefresherConfig {
|
impl TopologyRefresherConfig {
|
||||||
@@ -96,28 +97,24 @@ impl TopologyRefresher {
|
|||||||
self.topology_accessor.ensure_is_routable().await
|
self.topology_accessor.ensure_is_routable().await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn ensure_contains_gateway(
|
pub async fn ensure_contains_routable_egress(
|
||||||
&self,
|
&self,
|
||||||
gateway: &NodeIdentity,
|
egress: NodeIdentity,
|
||||||
) -> Result<(), NymTopologyError> {
|
) -> Result<(), NymTopologyError> {
|
||||||
let topology = self
|
let topology = self
|
||||||
.topology_accessor
|
.topology_accessor
|
||||||
.current_topology()
|
.current_route_provider()
|
||||||
.await
|
.await
|
||||||
.ok_or(NymTopologyError::EmptyNetworkTopology)?;
|
.ok_or(NymTopologyError::EmptyNetworkTopology)?;
|
||||||
|
|
||||||
if !topology.gateway_exists(gateway) {
|
let _ = topology.egress_by_identity(egress)?;
|
||||||
return Err(NymTopologyError::NonExistentGatewayError {
|
|
||||||
identity_key: gateway.to_base58_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn wait_for_gateway(
|
pub async fn wait_for_gateway(
|
||||||
&mut self,
|
&mut self,
|
||||||
gateway: &NodeIdentity,
|
gateway: NodeIdentity,
|
||||||
timeout_duration: Duration,
|
timeout_duration: Duration,
|
||||||
) -> Result<(), NymTopologyError> {
|
) -> Result<(), NymTopologyError> {
|
||||||
info!(
|
info!(
|
||||||
@@ -135,7 +132,7 @@ impl TopologyRefresher {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
_ = self.try_refresh() => {
|
_ = self.try_refresh() => {
|
||||||
if self.ensure_contains_gateway(gateway).await.is_ok() {
|
if self.ensure_contains_routable_egress(gateway).await.is_ok() {
|
||||||
return Ok(())
|
return Ok(())
|
||||||
}
|
}
|
||||||
info!("gateway '{gateway}' is still not online...");
|
info!("gateway '{gateway}' is still not online...");
|
||||||
|
|||||||
@@ -4,32 +4,39 @@
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use log::{debug, error, warn};
|
use log::{debug, error, warn};
|
||||||
use nym_topology::provider_trait::TopologyProvider;
|
use nym_topology::provider_trait::TopologyProvider;
|
||||||
use nym_topology::{NymTopology, NymTopologyError};
|
use nym_topology::NymTopology;
|
||||||
use nym_validator_client::UserAgent;
|
use nym_validator_client::UserAgent;
|
||||||
use rand::prelude::SliceRandom;
|
use rand::prelude::SliceRandom;
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
|
use std::cmp::min;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
// the same values as our current (10.06.24) blacklist
|
|
||||||
pub const DEFAULT_MIN_MIXNODE_PERFORMANCE: u8 = 50;
|
|
||||||
pub const DEFAULT_MIN_GATEWAY_PERFORMANCE: u8 = 50;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub min_mixnode_performance: u8,
|
pub min_mixnode_performance: u8,
|
||||||
pub min_gateway_performance: u8,
|
pub min_gateway_performance: u8,
|
||||||
|
pub use_extended_topology: bool,
|
||||||
|
pub ignore_egress_epoch_role: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl From<nym_client_core_config_types::Topology> for Config {
|
||||||
fn default() -> Self {
|
fn from(value: nym_client_core_config_types::Topology) -> Self {
|
||||||
// old values that decided on blacklist membership
|
|
||||||
Config {
|
Config {
|
||||||
min_mixnode_performance: DEFAULT_MIN_MIXNODE_PERFORMANCE,
|
min_mixnode_performance: value.minimum_mixnode_performance,
|
||||||
min_gateway_performance: DEFAULT_MIN_GATEWAY_PERFORMANCE,
|
min_gateway_performance: value.minimum_gateway_performance,
|
||||||
|
use_extended_topology: value.use_extended_topology,
|
||||||
|
ignore_egress_epoch_role: value.ignore_egress_epoch_role,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
// if we're using 'extended' topology, filter the nodes based on the lowest set performance
|
||||||
|
fn min_node_performance(&self) -> u8 {
|
||||||
|
min(self.min_mixnode_performance, self.min_gateway_performance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct NymApiTopologyProvider {
|
pub struct NymApiTopologyProvider {
|
||||||
config: Config,
|
config: Config,
|
||||||
|
|
||||||
@@ -39,7 +46,11 @@ pub struct NymApiTopologyProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NymApiTopologyProvider {
|
impl NymApiTopologyProvider {
|
||||||
pub fn new(config: Config, mut nym_api_urls: Vec<Url>, user_agent: Option<UserAgent>) -> Self {
|
pub fn new(
|
||||||
|
config: impl Into<Config>,
|
||||||
|
mut nym_api_urls: Vec<Url>,
|
||||||
|
user_agent: Option<UserAgent>,
|
||||||
|
) -> Self {
|
||||||
nym_api_urls.shuffle(&mut thread_rng());
|
nym_api_urls.shuffle(&mut thread_rng());
|
||||||
|
|
||||||
let validator_client = if let Some(user_agent) = user_agent {
|
let validator_client = if let Some(user_agent) = user_agent {
|
||||||
@@ -52,7 +63,7 @@ impl NymApiTopologyProvider {
|
|||||||
};
|
};
|
||||||
|
|
||||||
NymApiTopologyProvider {
|
NymApiTopologyProvider {
|
||||||
config,
|
config: config.into(),
|
||||||
validator_client,
|
validator_client,
|
||||||
nym_api_urls,
|
nym_api_urls,
|
||||||
currently_used_api: 0,
|
currently_used_api: 0,
|
||||||
@@ -70,70 +81,69 @@ impl NymApiTopologyProvider {
|
|||||||
.change_nym_api(self.nym_api_urls[self.currently_used_api].clone())
|
.change_nym_api(self.nym_api_urls[self.currently_used_api].clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verifies whether nodes a reasonably distributed among all mix layers.
|
|
||||||
///
|
|
||||||
/// In ideal world we would have 33% nodes on layer 1, 33% on layer 2 and 33% on layer 3.
|
|
||||||
/// However, this is a rather unrealistic expectation, instead we check whether there exists
|
|
||||||
/// a layer with more than 66% of nodes or with fewer than 15% and if so, we trigger a failure.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `topology`: active topology constructed from validator api data
|
|
||||||
fn check_layer_distribution(
|
|
||||||
&self,
|
|
||||||
active_topology: &NymTopology,
|
|
||||||
) -> Result<(), NymTopologyError> {
|
|
||||||
let lower_threshold = 0.15;
|
|
||||||
let upper_threshold = 0.66;
|
|
||||||
active_topology.ensure_even_layer_distribution(lower_threshold, upper_threshold)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_current_compatible_topology(&mut self) -> Option<NymTopology> {
|
async fn get_current_compatible_topology(&mut self) -> Option<NymTopology> {
|
||||||
let mixnodes = match self
|
let rewarded_set = self
|
||||||
.validator_client
|
.validator_client
|
||||||
.get_all_basic_active_mixing_assigned_nodes()
|
.get_current_rewarded_set()
|
||||||
.await
|
.await
|
||||||
{
|
.inspect_err(|err| error!("failed to get current rewarded set: {err}"))
|
||||||
Err(err) => {
|
.ok()?;
|
||||||
error!("failed to get network mixnodes - {err}");
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Ok(mixes) => mixes,
|
|
||||||
};
|
|
||||||
|
|
||||||
let gateways = match self
|
let mut topology = NymTopology::new_empty(rewarded_set);
|
||||||
.validator_client
|
|
||||||
.get_all_basic_entry_assigned_nodes()
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Err(err) => {
|
|
||||||
error!("failed to get network gateways - {err}");
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Ok(gateways) => gateways,
|
|
||||||
};
|
|
||||||
|
|
||||||
debug!(
|
if self.config.use_extended_topology {
|
||||||
"there are {} mixnodes and {} gateways in total (before performance filtering)",
|
let all_nodes = self
|
||||||
mixnodes.len(),
|
.validator_client
|
||||||
gateways.len()
|
.get_all_basic_nodes()
|
||||||
);
|
.await
|
||||||
|
.inspect_err(|err| error!("failed to get network nodes: {err}"))
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
let topology = NymTopology::from_unordered(
|
debug!(
|
||||||
mixnodes.iter().filter(|m| {
|
"there are {} nodes on the network (before filtering)",
|
||||||
m.performance.round_to_integer() >= self.config.min_mixnode_performance
|
all_nodes.len()
|
||||||
}),
|
);
|
||||||
gateways.iter().filter(|g| {
|
topology.add_additional_nodes(all_nodes.iter().filter(|n| {
|
||||||
g.performance.round_to_integer() >= self.config.min_gateway_performance
|
n.performance.round_to_integer() >= self.config.min_node_performance()
|
||||||
}),
|
}));
|
||||||
);
|
|
||||||
if let Err(err) = self.check_layer_distribution(&topology) {
|
|
||||||
warn!("The current filtered active topology has extremely skewed layer distribution. It cannot be used: {err}");
|
|
||||||
self.use_next_nym_api();
|
|
||||||
None
|
|
||||||
} else {
|
} else {
|
||||||
Some(topology)
|
// if we're not using extended topology, we're only getting active set mixnodes and gateways
|
||||||
|
|
||||||
|
let mixnodes = self
|
||||||
|
.validator_client
|
||||||
|
.get_all_basic_active_mixing_assigned_nodes()
|
||||||
|
.await
|
||||||
|
.inspect_err(|err| error!("failed to get network mixnodes: {err}"))
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
// TODO: we really should be getting ACTIVE gateways only
|
||||||
|
let gateways = self
|
||||||
|
.validator_client
|
||||||
|
.get_all_basic_entry_assigned_nodes()
|
||||||
|
.await
|
||||||
|
.inspect_err(|err| error!("failed to get network gateways: {err}"))
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
debug!(
|
||||||
|
"there are {} mixnodes and {} gateways in total (before performance filtering)",
|
||||||
|
mixnodes.len(),
|
||||||
|
gateways.len()
|
||||||
|
);
|
||||||
|
|
||||||
|
topology.add_additional_nodes(mixnodes.iter().filter(|m| {
|
||||||
|
m.performance.round_to_integer() >= self.config.min_mixnode_performance
|
||||||
|
}));
|
||||||
|
topology.add_additional_nodes(gateways.iter().filter(|m| {
|
||||||
|
m.performance.round_to_integer() >= self.config.min_gateway_performance
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
if !topology.is_minimally_routable() {
|
||||||
|
error!("the current filtered active topology can't be used to construct any packets");
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some(topology)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +152,11 @@ impl NymApiTopologyProvider {
|
|||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl TopologyProvider for NymApiTopologyProvider {
|
impl TopologyProvider for NymApiTopologyProvider {
|
||||||
async fn get_new_topology(&mut self) -> Option<NymTopology> {
|
async fn get_new_topology(&mut self) -> Option<NymTopology> {
|
||||||
self.get_current_compatible_topology().await
|
let Some(topology) = self.get_current_compatible_topology().await else {
|
||||||
|
self.use_next_nym_api();
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
Some(topology)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,6 +164,10 @@ impl TopologyProvider for NymApiTopologyProvider {
|
|||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
impl TopologyProvider for NymApiTopologyProvider {
|
impl TopologyProvider for NymApiTopologyProvider {
|
||||||
async fn get_new_topology(&mut self) -> Option<NymTopology> {
|
async fn get_new_topology(&mut self) -> Option<NymTopology> {
|
||||||
self.get_current_compatible_topology().await
|
let Some(topology) = self.get_current_compatible_topology().await else {
|
||||||
|
self.use_next_nym_api();
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
Some(topology)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
use crate::client::mix_traffic::transceiver::ErasedGatewayError;
|
use crate::client::mix_traffic::transceiver::ErasedGatewayError;
|
||||||
use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
|
use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
|
||||||
use nym_gateway_client::error::GatewayClientError;
|
use nym_gateway_client::error::GatewayClientError;
|
||||||
use nym_topology::gateway::GatewayConversionError;
|
use nym_topology::node::RoutingNodeError;
|
||||||
use nym_topology::NymTopologyError;
|
use nym_topology::{NodeId, NymTopologyError};
|
||||||
use nym_validator_client::ValidatorClientError;
|
use nym_validator_client::ValidatorClientError;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
@@ -74,10 +74,10 @@ pub enum ClientCoreError {
|
|||||||
#[error("the gateway id is invalid - {0}")]
|
#[error("the gateway id is invalid - {0}")]
|
||||||
UnableToCreatePublicKeyFromGatewayId(Ed25519RecoveryError),
|
UnableToCreatePublicKeyFromGatewayId(Ed25519RecoveryError),
|
||||||
|
|
||||||
#[error("The gateway is malformed: {source}")]
|
#[error("the node is malformed: {source}")]
|
||||||
MalformedGateway {
|
MalformedGateway {
|
||||||
#[from]
|
#[from]
|
||||||
source: GatewayConversionError,
|
source: Box<RoutingNodeError>,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error("failed to establish connection to gateway: {source}")]
|
#[error("failed to establish connection to gateway: {source}")]
|
||||||
@@ -159,6 +159,9 @@ pub enum ClientCoreError {
|
|||||||
#[error("the specified gateway '{gateway}' does not support the wss protocol")]
|
#[error("the specified gateway '{gateway}' does not support the wss protocol")]
|
||||||
UnsupportedWssProtocol { gateway: String },
|
UnsupportedWssProtocol { gateway: String },
|
||||||
|
|
||||||
|
#[error("node {id} ({identity}) does not support mixnet entry mode")]
|
||||||
|
UnsupportedEntry { id: NodeId, identity: String },
|
||||||
|
|
||||||
#[error(
|
#[error(
|
||||||
"failed to load custom topology using path '{}'. detailed message: {source}", file_path.display()
|
"failed to load custom topology using path '{}'. detailed message: {source}", file_path.display()
|
||||||
)]
|
)]
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use futures::{SinkExt, StreamExt};
|
|||||||
use log::{debug, info, trace, warn};
|
use log::{debug, info, trace, warn};
|
||||||
use nym_crypto::asymmetric::identity;
|
use nym_crypto::asymmetric::identity;
|
||||||
use nym_gateway_client::GatewayClient;
|
use nym_gateway_client::GatewayClient;
|
||||||
use nym_topology::gateway;
|
use nym_topology::node::RoutingNode;
|
||||||
use nym_validator_client::client::IdentityKeyRef;
|
use nym_validator_client::client::IdentityKeyRef;
|
||||||
use nym_validator_client::UserAgent;
|
use nym_validator_client::UserAgent;
|
||||||
use rand::{seq::SliceRandom, Rng};
|
use rand::{seq::SliceRandom, Rng};
|
||||||
@@ -15,6 +15,7 @@ use std::{sync::Arc, time::Duration};
|
|||||||
use tungstenite::Message;
|
use tungstenite::Message;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
use nym_topology::NodeId;
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
@@ -25,7 +26,6 @@ use tokio::time::Instant;
|
|||||||
use tokio_tungstenite::connect_async;
|
use tokio_tungstenite::connect_async;
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
|
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
use wasm_utils::websocket::JSWebsocket;
|
use wasm_utils::websocket::JSWebsocket;
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
@@ -48,22 +48,30 @@ const PING_TIMEOUT: Duration = Duration::from_millis(1000);
|
|||||||
|
|
||||||
// The abstraction that some of these helpers use
|
// The abstraction that some of these helpers use
|
||||||
pub trait ConnectableGateway {
|
pub trait ConnectableGateway {
|
||||||
fn identity(&self) -> &identity::PublicKey;
|
fn node_id(&self) -> NodeId;
|
||||||
fn clients_address(&self) -> String;
|
fn identity(&self) -> identity::PublicKey;
|
||||||
|
fn clients_address(&self, prefer_ipv6: bool) -> Option<String>;
|
||||||
fn is_wss(&self) -> bool;
|
fn is_wss(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConnectableGateway for gateway::LegacyNode {
|
impl ConnectableGateway for RoutingNode {
|
||||||
fn identity(&self) -> &identity::PublicKey {
|
fn node_id(&self) -> NodeId {
|
||||||
self.identity()
|
self.node_id
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clients_address(&self) -> String {
|
fn identity(&self) -> identity::PublicKey {
|
||||||
self.clients_address()
|
self.identity_key
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clients_address(&self, prefer_ipv6: bool) -> Option<String> {
|
||||||
|
self.ws_entry_address(prefer_ipv6)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_wss(&self) -> bool {
|
fn is_wss(&self) -> bool {
|
||||||
self.clients_wss_port.is_some()
|
self.entry
|
||||||
|
.as_ref()
|
||||||
|
.map(|e| e.clients_wss_port.is_some())
|
||||||
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,12 +86,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,
|
rng: &mut R,
|
||||||
nym_apis: &[Url],
|
nym_apis: &[Url],
|
||||||
user_agent: Option<UserAgent>,
|
user_agent: Option<UserAgent>,
|
||||||
minimum_performance: u8,
|
minimum_performance: u8,
|
||||||
) -> Result<Vec<gateway::LegacyNode>, ClientCoreError> {
|
) -> Result<Vec<RoutingNode>, ClientCoreError> {
|
||||||
let nym_api = nym_apis
|
let nym_api = nym_apis
|
||||||
.choose(rng)
|
.choose(rng)
|
||||||
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
|
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
|
||||||
@@ -100,11 +108,14 @@ pub async fn current_gateways<R: Rng>(
|
|||||||
|
|
||||||
log::trace!("Gateways: {:#?}", gateways);
|
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
|
let valid_gateways = gateways
|
||||||
.iter()
|
.iter()
|
||||||
|
.filter(|g| !g.supported_roles.mixnode)
|
||||||
.filter(|g| g.performance.round_to_integer() >= minimum_performance)
|
.filter(|g| g.performance.round_to_integer() >= minimum_performance)
|
||||||
.filter_map(|gateway| gateway.try_into().ok())
|
.filter_map(|gateway| gateway.try_into().ok())
|
||||||
.collect::<Vec<gateway::LegacyNode>>();
|
.collect::<Vec<_>>();
|
||||||
log::debug!("After checking validity: {}", valid_gateways.len());
|
log::debug!("After checking validity: {}", valid_gateways.len());
|
||||||
log::trace!("Valid gateways: {:#?}", valid_gateways);
|
log::trace!("Valid gateways: {:#?}", valid_gateways);
|
||||||
|
|
||||||
@@ -134,7 +145,12 @@ async fn measure_latency<G>(gateway: &G) -> Result<GatewayWithLatency<G>, Client
|
|||||||
where
|
where
|
||||||
G: ConnectableGateway,
|
G: ConnectableGateway,
|
||||||
{
|
{
|
||||||
let addr = gateway.clients_address();
|
let Some(addr) = gateway.clients_address(false) else {
|
||||||
|
return Err(ClientCoreError::UnsupportedEntry {
|
||||||
|
id: gateway.node_id(),
|
||||||
|
identity: gateway.identity().to_string(),
|
||||||
|
});
|
||||||
|
};
|
||||||
trace!(
|
trace!(
|
||||||
"establishing connection to {} ({addr})...",
|
"establishing connection to {} ({addr})...",
|
||||||
gateway.identity(),
|
gateway.identity(),
|
||||||
@@ -205,7 +221,7 @@ pub async fn choose_gateway_by_latency<R: Rng, G: ConnectableGateway + Clone>(
|
|||||||
let gateways_with_latency = Arc::new(tokio::sync::Mutex::new(Vec::new()));
|
let gateways_with_latency = Arc::new(tokio::sync::Mutex::new(Vec::new()));
|
||||||
futures::stream::iter(gateways)
|
futures::stream::iter(gateways)
|
||||||
.for_each_concurrent(CONCURRENT_GATEWAYS_MEASURED, |gateway| async {
|
.for_each_concurrent(CONCURRENT_GATEWAYS_MEASURED, |gateway| async {
|
||||||
let id = *gateway.identity();
|
let id = gateway.identity();
|
||||||
trace!("measuring latency to {id}...");
|
trace!("measuring latency to {id}...");
|
||||||
match measure_latency(gateway).await {
|
match measure_latency(gateway).await {
|
||||||
Ok(with_latency) => {
|
Ok(with_latency) => {
|
||||||
@@ -252,9 +268,9 @@ fn filter_by_tls<G: ConnectableGateway>(
|
|||||||
|
|
||||||
pub(super) fn uniformly_random_gateway<R: Rng>(
|
pub(super) fn uniformly_random_gateway<R: Rng>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
gateways: &[gateway::LegacyNode],
|
gateways: &[RoutingNode],
|
||||||
must_use_tls: bool,
|
must_use_tls: bool,
|
||||||
) -> Result<gateway::LegacyNode, ClientCoreError> {
|
) -> Result<RoutingNode, ClientCoreError> {
|
||||||
filter_by_tls(gateways, must_use_tls)?
|
filter_by_tls(gateways, must_use_tls)?
|
||||||
.choose(rng)
|
.choose(rng)
|
||||||
.ok_or(ClientCoreError::NoGatewaysOnNetwork)
|
.ok_or(ClientCoreError::NoGatewaysOnNetwork)
|
||||||
@@ -263,9 +279,9 @@ pub(super) fn uniformly_random_gateway<R: Rng>(
|
|||||||
|
|
||||||
pub(super) fn get_specified_gateway(
|
pub(super) fn get_specified_gateway(
|
||||||
gateway_identity: IdentityKeyRef,
|
gateway_identity: IdentityKeyRef,
|
||||||
gateways: &[gateway::LegacyNode],
|
gateways: &[RoutingNode],
|
||||||
must_use_tls: bool,
|
must_use_tls: bool,
|
||||||
) -> Result<gateway::LegacyNode, ClientCoreError> {
|
) -> Result<RoutingNode, ClientCoreError> {
|
||||||
log::debug!("Requesting specified gateway: {}", gateway_identity);
|
log::debug!("Requesting specified gateway: {}", gateway_identity);
|
||||||
let user_gateway = identity::PublicKey::from_base58_string(gateway_identity)
|
let user_gateway = identity::PublicKey::from_base58_string(gateway_identity)
|
||||||
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
|
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
|
||||||
@@ -275,7 +291,14 @@ pub(super) fn get_specified_gateway(
|
|||||||
.find(|gateway| gateway.identity_key == user_gateway)
|
.find(|gateway| gateway.identity_key == user_gateway)
|
||||||
.ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_identity.to_string()))?;
|
.ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_identity.to_string()))?;
|
||||||
|
|
||||||
if must_use_tls && gateway.clients_wss_port.is_none() {
|
let Some(entry_details) = gateway.entry.as_ref() else {
|
||||||
|
return Err(ClientCoreError::UnsupportedEntry {
|
||||||
|
id: gateway.node_id,
|
||||||
|
identity: gateway.identity().to_string(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if must_use_tls && entry_details.clients_wss_port.is_none() {
|
||||||
return Err(ClientCoreError::UnsupportedWssProtocol {
|
return Err(ClientCoreError::UnsupportedWssProtocol {
|
||||||
gateway: gateway_identity.to_string(),
|
gateway: gateway_identity.to_string(),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ use crate::init::types::{
|
|||||||
use nym_client_core_gateways_storage::GatewaysDetailsStore;
|
use nym_client_core_gateways_storage::GatewaysDetailsStore;
|
||||||
use nym_client_core_gateways_storage::{GatewayDetails, GatewayRegistration};
|
use nym_client_core_gateways_storage::{GatewayDetails, GatewayRegistration};
|
||||||
use nym_gateway_client::client::InitGatewayClient;
|
use nym_gateway_client::client::InitGatewayClient;
|
||||||
use nym_topology::gateway;
|
use nym_topology::node::RoutingNode;
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
@@ -50,7 +50,7 @@ async fn setup_new_gateway<K, D>(
|
|||||||
key_store: &K,
|
key_store: &K,
|
||||||
details_store: &D,
|
details_store: &D,
|
||||||
selection_specification: GatewaySelectionSpecification,
|
selection_specification: GatewaySelectionSpecification,
|
||||||
available_gateways: Vec<gateway::LegacyNode>,
|
available_gateways: Vec<RoutingNode>,
|
||||||
) -> Result<InitialisationResult, ClientCoreError>
|
) -> Result<InitialisationResult, ClientCoreError>
|
||||||
where
|
where
|
||||||
K: KeyStore,
|
K: KeyStore,
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ use nym_crypto::asymmetric::identity;
|
|||||||
use nym_gateway_client::client::InitGatewayClient;
|
use nym_gateway_client::client::InitGatewayClient;
|
||||||
use nym_gateway_requests::shared_key::SharedGatewayKey;
|
use nym_gateway_requests::shared_key::SharedGatewayKey;
|
||||||
use nym_sphinx::addressing::clients::Recipient;
|
use nym_sphinx::addressing::clients::Recipient;
|
||||||
use nym_topology::gateway;
|
use nym_topology::node::RoutingNode;
|
||||||
use nym_validator_client::client::IdentityKey;
|
use nym_validator_client::client::IdentityKey;
|
||||||
use nym_validator_client::nyxd::AccountId;
|
use nym_validator_client::nyxd::AccountId;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::fmt::Display;
|
use std::fmt::{Debug, Display};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
@@ -38,16 +38,23 @@ pub enum SelectedGateway {
|
|||||||
|
|
||||||
impl SelectedGateway {
|
impl SelectedGateway {
|
||||||
pub fn from_topology_node(
|
pub fn from_topology_node(
|
||||||
node: gateway::LegacyNode,
|
node: RoutingNode,
|
||||||
must_use_tls: bool,
|
must_use_tls: bool,
|
||||||
) -> Result<Self, ClientCoreError> {
|
) -> Result<Self, ClientCoreError> {
|
||||||
|
// for now, let's use 'old' behaviour, if you want to change it, you can pass it up the enum stack yourself : )
|
||||||
|
let prefer_ipv6 = false;
|
||||||
|
|
||||||
let gateway_listener = if must_use_tls {
|
let gateway_listener = if must_use_tls {
|
||||||
node.clients_address_tls()
|
node.ws_entry_address_tls()
|
||||||
.ok_or(ClientCoreError::UnsupportedWssProtocol {
|
.ok_or(ClientCoreError::UnsupportedWssProtocol {
|
||||||
gateway: node.identity_key.to_base58_string(),
|
gateway: node.identity_key.to_base58_string(),
|
||||||
})?
|
})?
|
||||||
} else {
|
} else {
|
||||||
node.clients_address()
|
node.ws_entry_address(prefer_ipv6)
|
||||||
|
.ok_or(ClientCoreError::UnsupportedEntry {
|
||||||
|
id: node.node_id,
|
||||||
|
identity: node.identity_key.to_base58_string(),
|
||||||
|
})?
|
||||||
};
|
};
|
||||||
|
|
||||||
let gateway_listener =
|
let gateway_listener =
|
||||||
@@ -200,7 +207,7 @@ pub enum GatewaySetup {
|
|||||||
specification: GatewaySelectionSpecification,
|
specification: GatewaySelectionSpecification,
|
||||||
|
|
||||||
// TODO: seems to be a bit inefficient to pass them by value
|
// TODO: seems to be a bit inefficient to pass them by value
|
||||||
available_gateways: Vec<gateway::LegacyNode>,
|
available_gateways: Vec<RoutingNode>,
|
||||||
},
|
},
|
||||||
|
|
||||||
ReuseConnection {
|
ReuseConnection {
|
||||||
@@ -214,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 {
|
impl GatewaySetup {
|
||||||
pub fn try_reuse_connection(init_res: InitialisationResult) -> Result<Self, ClientCoreError> {
|
pub fn try_reuse_connection(init_res: InitialisationResult) -> Result<Self, ClientCoreError> {
|
||||||
if let Some(authenticated_ephemeral_client) = init_res.authenticated_ephemeral_client {
|
if let Some(authenticated_ephemeral_client) = init_res.authenticated_ephemeral_client {
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ pub mod error;
|
|||||||
pub mod init;
|
pub mod init;
|
||||||
|
|
||||||
pub use nym_topology::{
|
pub use nym_topology::{
|
||||||
HardcodedTopologyProvider, NymTopology, NymTopologyError, SerializableNymTopology,
|
HardcodedTopologyProvider, NymRouteProvider, NymTopology, NymTopologyError, TopologyProvider,
|
||||||
SerializableTopologyError, TopologyProvider,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ mod error;
|
|||||||
mod manager;
|
mod manager;
|
||||||
mod models;
|
mod models;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Backend {
|
pub struct Backend {
|
||||||
temporary_old_path: Option<PathBuf>,
|
temporary_old_path: Option<PathBuf>,
|
||||||
database_path: PathBuf,
|
database_path: PathBuf,
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ pub mod fs_backend;
|
|||||||
#[error("no information provided")]
|
#[error("no information provided")]
|
||||||
pub struct UndefinedError;
|
pub struct UndefinedError;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Empty {
|
pub struct Empty {
|
||||||
// we need to keep 'basic' metadata here to "load" the CombinedReplyStorage
|
// we need to keep 'basic' metadata here to "load" the CombinedReplyStorage
|
||||||
pub min_surb_threshold: usize,
|
pub min_surb_threshold: usize,
|
||||||
|
|||||||
@@ -19,8 +19,9 @@ use nym_api_requests::ecash::{
|
|||||||
PartialExpirationDateSignatureResponse, VerificationKeyResponse,
|
PartialExpirationDateSignatureResponse, VerificationKeyResponse,
|
||||||
};
|
};
|
||||||
use nym_api_requests::models::{
|
use nym_api_requests::models::{
|
||||||
ApiHealthResponse, GatewayBondAnnotated, GatewayCoreStatusResponse, MixnodeCoreStatusResponse,
|
ApiHealthResponse, GatewayBondAnnotated, GatewayCoreStatusResponse,
|
||||||
MixnodeStatusResponse, NymNodeDescription, RewardEstimationResponse, StakeSaturationResponse,
|
HistoricalPerformanceResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse,
|
||||||
|
NymNodeDescription, RewardEstimationResponse, StakeSaturationResponse,
|
||||||
};
|
};
|
||||||
use nym_api_requests::models::{LegacyDescribedGateway, MixNodeBondAnnotated};
|
use nym_api_requests::models::{LegacyDescribedGateway, MixNodeBondAnnotated};
|
||||||
use nym_api_requests::nym_nodes::SkimmedNode;
|
use nym_api_requests::nym_nodes::SkimmedNode;
|
||||||
@@ -32,10 +33,10 @@ use time::Date;
|
|||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
pub use crate::nym_api::NymApiClientExt;
|
pub use crate::nym_api::NymApiClientExt;
|
||||||
|
use nym_mixnet_contract_common::EpochRewardedSet;
|
||||||
pub use nym_mixnet_contract_common::{
|
pub use nym_mixnet_contract_common::{
|
||||||
mixnode::MixNodeDetails, GatewayBond, IdentityKey, IdentityKeyRef, NodeId, NymNodeDetails,
|
mixnode::MixNodeDetails, GatewayBond, IdentityKey, IdentityKeyRef, NodeId, NymNodeDetails,
|
||||||
};
|
};
|
||||||
|
|
||||||
// re-export the type to not break existing imports
|
// re-export the type to not break existing imports
|
||||||
pub use crate::coconut::EcashApiClient;
|
pub use crate::coconut::EcashApiClient;
|
||||||
|
|
||||||
@@ -264,6 +265,31 @@ impl<C, S> Client<C, S> {
|
|||||||
Ok(self.nym_api.get_gateways_detailed_unfiltered().await?)
|
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...
|
// TODO: combine with NymApiClient...
|
||||||
pub async fn get_all_cached_described_nodes(
|
pub async fn get_all_cached_described_nodes(
|
||||||
&self,
|
&self,
|
||||||
@@ -367,6 +393,10 @@ impl NymApiClient {
|
|||||||
Ok(self.nym_api.get_basic_gateways().await?.nodes)
|
Ok(self.nym_api.get_basic_gateways().await?.nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_current_rewarded_set(&self) -> Result<EpochRewardedSet, ValidatorClientError> {
|
||||||
|
Ok(self.nym_api.get_rewarded_set().await?.into())
|
||||||
|
}
|
||||||
|
|
||||||
/// retrieve basic information for nodes are capable of operating as an entry gateway
|
/// retrieve basic information for nodes are capable of operating as an entry gateway
|
||||||
/// this includes legacy gateways and nym-nodes
|
/// this includes legacy gateways and nym-nodes
|
||||||
pub async fn get_all_basic_entry_assigned_nodes(
|
pub async fn get_all_basic_entry_assigned_nodes(
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use nym_api_requests::ecash::models::{
|
|||||||
use nym_api_requests::ecash::VerificationKeyResponse;
|
use nym_api_requests::ecash::VerificationKeyResponse;
|
||||||
use nym_api_requests::models::{
|
use nym_api_requests::models::{
|
||||||
AnnotationResponse, ApiHealthResponse, LegacyDescribedMixNode, NodePerformanceResponse,
|
AnnotationResponse, ApiHealthResponse, LegacyDescribedMixNode, NodePerformanceResponse,
|
||||||
NodeRefreshBody, NymNodeDescription,
|
NodeRefreshBody, NymNodeDescription, PerformanceHistoryResponse, RewardedSetResponse,
|
||||||
};
|
};
|
||||||
use nym_api_requests::nym_nodes::PaginatedCachedNodesResponse;
|
use nym_api_requests::nym_nodes::PaginatedCachedNodesResponse;
|
||||||
use nym_api_requests::pagination::PaginatedResponse;
|
use nym_api_requests::pagination::PaginatedResponse;
|
||||||
@@ -163,6 +163,35 @@ pub trait NymApiClientExt: ApiClient {
|
|||||||
.await
|
.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)]
|
#[tracing::instrument(level = "debug", skip_all)]
|
||||||
async fn get_nodes_described(
|
async fn get_nodes_described(
|
||||||
&self,
|
&self,
|
||||||
@@ -179,8 +208,15 @@ pub trait NymApiClientExt: ApiClient {
|
|||||||
params.push(("per_page", per_page.to_string()))
|
params.push(("per_page", per_page.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
self.get_json(&[routes::API_VERSION, "nym-nodes", "described"], ¶ms)
|
self.get_json(
|
||||||
.await
|
&[
|
||||||
|
routes::API_VERSION,
|
||||||
|
routes::NYM_NODES_ROUTES,
|
||||||
|
routes::NYM_NODES_DESCRIBED,
|
||||||
|
],
|
||||||
|
¶ms,
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip_all)]
|
#[tracing::instrument(level = "debug", skip_all)]
|
||||||
@@ -199,8 +235,15 @@ pub trait NymApiClientExt: ApiClient {
|
|||||||
params.push(("per_page", per_page.to_string()))
|
params.push(("per_page", per_page.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
self.get_json(&[routes::API_VERSION, "nym-nodes", "bonded"], ¶ms)
|
self.get_json(
|
||||||
.await
|
&[
|
||||||
|
routes::API_VERSION,
|
||||||
|
routes::NYM_NODES_ROUTES,
|
||||||
|
routes::NYM_NODES_BONDED,
|
||||||
|
],
|
||||||
|
¶ms,
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
@@ -210,7 +253,7 @@ pub trait NymApiClientExt: ApiClient {
|
|||||||
&[
|
&[
|
||||||
routes::API_VERSION,
|
routes::API_VERSION,
|
||||||
"unstable",
|
"unstable",
|
||||||
"nym-nodes",
|
routes::NYM_NODES_ROUTES,
|
||||||
"mixnodes",
|
"mixnodes",
|
||||||
"skimmed",
|
"skimmed",
|
||||||
],
|
],
|
||||||
@@ -226,7 +269,7 @@ pub trait NymApiClientExt: ApiClient {
|
|||||||
&[
|
&[
|
||||||
routes::API_VERSION,
|
routes::API_VERSION,
|
||||||
"unstable",
|
"unstable",
|
||||||
"nym-nodes",
|
routes::NYM_NODES_ROUTES,
|
||||||
"gateways",
|
"gateways",
|
||||||
"skimmed",
|
"skimmed",
|
||||||
],
|
],
|
||||||
@@ -235,6 +278,19 @@ pub trait NymApiClientExt: ApiClient {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip(self))]
|
||||||
|
async fn get_rewarded_set(&self) -> Result<RewardedSetResponse, NymAPIError> {
|
||||||
|
self.get_json(
|
||||||
|
&[
|
||||||
|
routes::API_VERSION,
|
||||||
|
routes::NYM_NODES_ROUTES,
|
||||||
|
routes::NYM_NODES_REWARDED_SET,
|
||||||
|
],
|
||||||
|
NO_PARAMS,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
/// retrieve basic information for nodes are capable of operating as an entry gateway
|
/// retrieve basic information for nodes are capable of operating as an entry gateway
|
||||||
/// this includes legacy gateways and nym-nodes
|
/// this includes legacy gateways and nym-nodes
|
||||||
#[instrument(level = "debug", skip(self))]
|
#[instrument(level = "debug", skip(self))]
|
||||||
@@ -262,7 +318,7 @@ pub trait NymApiClientExt: ApiClient {
|
|||||||
&[
|
&[
|
||||||
routes::API_VERSION,
|
routes::API_VERSION,
|
||||||
"unstable",
|
"unstable",
|
||||||
"nym-nodes",
|
routes::NYM_NODES_ROUTES,
|
||||||
"skimmed",
|
"skimmed",
|
||||||
"entry-gateways",
|
"entry-gateways",
|
||||||
"all",
|
"all",
|
||||||
@@ -299,7 +355,7 @@ pub trait NymApiClientExt: ApiClient {
|
|||||||
&[
|
&[
|
||||||
routes::API_VERSION,
|
routes::API_VERSION,
|
||||||
"unstable",
|
"unstable",
|
||||||
"nym-nodes",
|
routes::NYM_NODES_ROUTES,
|
||||||
"skimmed",
|
"skimmed",
|
||||||
"mixnodes",
|
"mixnodes",
|
||||||
"active",
|
"active",
|
||||||
@@ -336,7 +392,7 @@ pub trait NymApiClientExt: ApiClient {
|
|||||||
&[
|
&[
|
||||||
routes::API_VERSION,
|
routes::API_VERSION,
|
||||||
"unstable",
|
"unstable",
|
||||||
"nym-nodes",
|
routes::NYM_NODES_ROUTES,
|
||||||
"skimmed",
|
"skimmed",
|
||||||
"mixnodes",
|
"mixnodes",
|
||||||
"all",
|
"all",
|
||||||
@@ -368,7 +424,12 @@ pub trait NymApiClientExt: ApiClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.get_json(
|
self.get_json(
|
||||||
&[routes::API_VERSION, "unstable", "nym-nodes", "skimmed"],
|
&[
|
||||||
|
routes::API_VERSION,
|
||||||
|
"unstable",
|
||||||
|
routes::NYM_NODES_ROUTES,
|
||||||
|
"skimmed",
|
||||||
|
],
|
||||||
¶ms,
|
¶ms,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@@ -677,8 +738,8 @@ pub trait NymApiClientExt: ApiClient {
|
|||||||
self.get_json(
|
self.get_json(
|
||||||
&[
|
&[
|
||||||
routes::API_VERSION,
|
routes::API_VERSION,
|
||||||
"nym-nodes",
|
routes::NYM_NODES_ROUTES,
|
||||||
"performance",
|
routes::NYM_NODES_PERFORMANCE,
|
||||||
&node_id.to_string(),
|
&node_id.to_string(),
|
||||||
],
|
],
|
||||||
NO_PARAMS,
|
NO_PARAMS,
|
||||||
@@ -693,8 +754,8 @@ pub trait NymApiClientExt: ApiClient {
|
|||||||
self.get_json(
|
self.get_json(
|
||||||
&[
|
&[
|
||||||
routes::API_VERSION,
|
routes::API_VERSION,
|
||||||
"nym-nodes",
|
routes::NYM_NODES_ROUTES,
|
||||||
"annotation",
|
routes::NYM_NODES_ANNOTATION,
|
||||||
&node_id.to_string(),
|
&node_id.to_string(),
|
||||||
],
|
],
|
||||||
NO_PARAMS,
|
NO_PARAMS,
|
||||||
@@ -912,18 +973,24 @@ pub trait NymApiClientExt: ApiClient {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip(self))]
|
||||||
async fn force_refresh_describe_cache(
|
async fn force_refresh_describe_cache(
|
||||||
&self,
|
&self,
|
||||||
request: &NodeRefreshBody,
|
request: &NodeRefreshBody,
|
||||||
) -> Result<(), NymAPIError> {
|
) -> Result<(), NymAPIError> {
|
||||||
self.post_json(
|
self.post_json(
|
||||||
&[routes::API_VERSION, "nym-nodes", "refresh-described"],
|
&[
|
||||||
|
routes::API_VERSION,
|
||||||
|
routes::NYM_NODES_ROUTES,
|
||||||
|
routes::NYM_NODES_REFRESH_DESCRIBED,
|
||||||
|
],
|
||||||
NO_PARAMS,
|
NO_PARAMS,
|
||||||
request,
|
request,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip(self))]
|
||||||
async fn issued_ticketbooks_for(
|
async fn issued_ticketbooks_for(
|
||||||
&self,
|
&self,
|
||||||
expiration_date: Date,
|
expiration_date: Date,
|
||||||
@@ -940,6 +1007,7 @@ pub trait NymApiClientExt: ApiClient {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip(self))]
|
||||||
async fn issued_ticketbooks_challenge(
|
async fn issued_ticketbooks_challenge(
|
||||||
&self,
|
&self,
|
||||||
expiration_date: Date,
|
expiration_date: Date,
|
||||||
|
|||||||
@@ -34,6 +34,19 @@ pub mod ecash {
|
|||||||
pub const EPOCH_ID_PARAM: &str = "epoch_id";
|
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 STATUS_ROUTES: &str = "status";
|
||||||
pub const API_STATUS_ROUTES: &str = "api-status";
|
pub const API_STATUS_ROUTES: &str = "api-status";
|
||||||
pub const HEALTH: &str = "health";
|
pub const HEALTH: &str = "health";
|
||||||
|
|||||||
+15
-12
@@ -26,10 +26,10 @@ use nym_mixnet_contract_common::{
|
|||||||
reward_params::{Performance, RewardingParams},
|
reward_params::{Performance, RewardingParams},
|
||||||
rewarding::{EstimatedCurrentEpochRewardResponse, PendingRewardResponse},
|
rewarding::{EstimatedCurrentEpochRewardResponse, PendingRewardResponse},
|
||||||
ContractBuildInformation, ContractState, ContractStateParams, CurrentIntervalResponse,
|
ContractBuildInformation, ContractState, ContractStateParams, CurrentIntervalResponse,
|
||||||
CurrentNymNodeVersionResponse, Delegation, EpochEventId, EpochStatus, GatewayBond,
|
CurrentNymNodeVersionResponse, Delegation, EpochEventId, EpochRewardedSet, EpochStatus,
|
||||||
GatewayBondResponse, GatewayOwnershipResponse, HistoricalNymNodeVersionEntry, IdentityKey,
|
GatewayBond, GatewayBondResponse, GatewayOwnershipResponse, HistoricalNymNodeVersionEntry,
|
||||||
IdentityKeyRef, IntervalEventId, MixNodeBond, MixNodeDetails, MixOwnershipResponse,
|
IdentityKey, IdentityKeyRef, IntervalEventId, MixNodeBond, MixNodeDetails,
|
||||||
MixnodeDetailsByIdentityResponse, MixnodeDetailsResponse, NodeId,
|
MixOwnershipResponse, MixnodeDetailsByIdentityResponse, MixnodeDetailsResponse, NodeId,
|
||||||
NumberOfPendingEventsResponse, NymNodeBond, NymNodeDetails, NymNodeVersionHistoryResponse,
|
NumberOfPendingEventsResponse, NymNodeBond, NymNodeDetails, NymNodeVersionHistoryResponse,
|
||||||
PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse, PagedGatewayResponse,
|
PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse, PagedGatewayResponse,
|
||||||
PagedMixnodeBondsResponse, PagedNodeDelegationsResponse, PendingEpochEvent,
|
PagedMixnodeBondsResponse, PagedNodeDelegationsResponse, PendingEpochEvent,
|
||||||
@@ -670,7 +670,7 @@ impl<T> PagedMixnetQueryClient for T where T: MixnetQueryClient {}
|
|||||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||||
pub trait MixnetQueryClientExt: MixnetQueryClient {
|
pub trait MixnetQueryClientExt: MixnetQueryClient {
|
||||||
async fn get_rewarded_set(&self) -> Result<RewardedSet, NyxdError> {
|
async fn get_rewarded_set(&self) -> Result<EpochRewardedSet, NyxdError> {
|
||||||
let error_response = |message| Err(NyxdError::extension_query_failure("mixnet", message));
|
let error_response = |message| Err(NyxdError::extension_query_failure("mixnet", message));
|
||||||
|
|
||||||
let metadata = self.get_rewarded_set_metadata().await?;
|
let metadata = self.get_rewarded_set_metadata().await?;
|
||||||
@@ -711,13 +711,16 @@ pub trait MixnetQueryClientExt: MixnetQueryClient {
|
|||||||
return error_response("the nodes assigned for 'standby' returned unexpected epoch_id");
|
return error_response("the nodes assigned for 'standby' returned unexpected epoch_id");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(RewardedSet {
|
Ok(EpochRewardedSet {
|
||||||
entry_gateways: entry.nodes,
|
epoch_id: expected_epoch_id,
|
||||||
exit_gateways: exit.nodes,
|
assignment: RewardedSet {
|
||||||
layer1: layer1.nodes,
|
entry_gateways: entry.nodes,
|
||||||
layer2: layer2.nodes,
|
exit_gateways: exit.nodes,
|
||||||
layer3: layer3.nodes,
|
layer1: layer1.nodes,
|
||||||
standby: standby.nodes,
|
layer2: layer2.nodes,
|
||||||
|
layer3: layer3.nodes,
|
||||||
|
standby: standby.nodes,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+31
@@ -153,13 +153,20 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
|||||||
let req = QueryAllBalancesRequest {
|
let req = QueryAllBalancesRequest {
|
||||||
address: address.to_string(),
|
address: address.to_string(),
|
||||||
pagination,
|
pagination,
|
||||||
|
resolve_denom: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut res = self
|
let mut res = self
|
||||||
.make_abci_query::<_, QueryAllBalancesResponse>(path.clone(), req)
|
.make_abci_query::<_, QueryAllBalancesResponse>(path.clone(), req)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
let early_break = res.balances.is_empty();
|
||||||
raw_balances.append(&mut res.balances);
|
raw_balances.append(&mut res.balances);
|
||||||
|
|
||||||
|
if early_break {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(next_key) = next_page_key(res.pagination) {
|
if let Some(next_key) = next_page_key(res.pagination) {
|
||||||
pagination = Some(create_pagination(next_key))
|
pagination = Some(create_pagination(next_key))
|
||||||
} else {
|
} else {
|
||||||
@@ -187,7 +194,13 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
|||||||
.make_abci_query::<_, QueryTotalSupplyResponse>(path.clone(), req)
|
.make_abci_query::<_, QueryTotalSupplyResponse>(path.clone(), req)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
let early_break = res.supply.is_empty();
|
||||||
supply.append(&mut res.supply);
|
supply.append(&mut res.supply);
|
||||||
|
|
||||||
|
if early_break {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(next_key) = next_page_key(res.pagination) {
|
if let Some(next_key) = next_page_key(res.pagination) {
|
||||||
pagination = Some(create_pagination(next_key))
|
pagination = Some(create_pagination(next_key))
|
||||||
} else {
|
} else {
|
||||||
@@ -328,7 +341,13 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
|||||||
.make_abci_query::<_, QueryCodesResponse>(path.clone(), req)
|
.make_abci_query::<_, QueryCodesResponse>(path.clone(), req)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
let early_break = res.code_infos.is_empty();
|
||||||
raw_codes.append(&mut res.code_infos);
|
raw_codes.append(&mut res.code_infos);
|
||||||
|
|
||||||
|
if early_break {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(next_key) = next_page_key(res.pagination) {
|
if let Some(next_key) = next_page_key(res.pagination) {
|
||||||
pagination = Some(create_pagination(next_key))
|
pagination = Some(create_pagination(next_key))
|
||||||
} else {
|
} else {
|
||||||
@@ -373,7 +392,13 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
|||||||
.make_abci_query::<_, QueryContractsByCodeResponse>(path.clone(), req)
|
.make_abci_query::<_, QueryContractsByCodeResponse>(path.clone(), req)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
let early_break = res.contracts.is_empty();
|
||||||
raw_contracts.append(&mut res.contracts);
|
raw_contracts.append(&mut res.contracts);
|
||||||
|
|
||||||
|
if early_break {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(next_key) = next_page_key(res.pagination) {
|
if let Some(next_key) = next_page_key(res.pagination) {
|
||||||
pagination = Some(create_pagination(next_key))
|
pagination = Some(create_pagination(next_key))
|
||||||
} else {
|
} else {
|
||||||
@@ -429,7 +454,13 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
|||||||
.make_abci_query::<_, QueryContractHistoryResponse>(path.clone(), req)
|
.make_abci_query::<_, QueryContractHistoryResponse>(path.clone(), req)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
let early_break = res.entries.is_empty();
|
||||||
raw_entries.append(&mut res.entries);
|
raw_entries.append(&mut res.entries);
|
||||||
|
|
||||||
|
if early_break {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(next_key) = next_page_key(res.pagination) {
|
if let Some(next_key) = next_page_key(res.pagination) {
|
||||||
pagination = Some(create_pagination(next_key))
|
pagination = Some(create_pagination(next_key))
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -4,9 +4,11 @@
|
|||||||
use crate::rpc::TendermintRpcClient;
|
use crate::rpc::TendermintRpcClient;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
|
use cosmrs::tendermint;
|
||||||
use cosmrs::tendermint::{block::Height, evidence::Evidence, Hash};
|
use cosmrs::tendermint::{block::Height, evidence::Evidence, Hash};
|
||||||
use reqwest::header::HeaderMap;
|
use reqwest::header::HeaderMap;
|
||||||
use reqwest::{header, RequestBuilder};
|
use reqwest::{header, RequestBuilder};
|
||||||
|
use tendermint_rpc::dialect::{v0_34, v0_37, v0_38, LatestDialect};
|
||||||
use tendermint_rpc::{
|
use tendermint_rpc::{
|
||||||
client::CompatMode,
|
client::CompatMode,
|
||||||
dialect::{self, Dialect},
|
dialect::{self, Dialect},
|
||||||
@@ -21,8 +23,21 @@ macro_rules! perform_with_compat {
|
|||||||
($self:expr, $request:expr) => {{
|
($self:expr, $request:expr) => {{
|
||||||
let request = $request;
|
let request = $request;
|
||||||
match $self.compat {
|
match $self.compat {
|
||||||
CompatMode::V0_37 => $self.perform_v0_37(request).await,
|
CompatMode::V0_38 => {
|
||||||
CompatMode::V0_34 => $self.perform_v0_34(request).await,
|
$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)
|
.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
|
where
|
||||||
R: SimpleRequest<S>,
|
R: SimpleRequest<S>,
|
||||||
S: Dialect,
|
S: Dialect,
|
||||||
@@ -81,26 +100,25 @@ impl ReqwestRpcClient {
|
|||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
.map_err(TendermintRpcErrorMap::into_rpc_err)?;
|
.map_err(TendermintRpcErrorMap::into_rpc_err)?;
|
||||||
|
let response_status = response.status();
|
||||||
let bytes = response
|
let bytes = response
|
||||||
.bytes()
|
.bytes()
|
||||||
.await
|
.await
|
||||||
.map_err(TendermintRpcErrorMap::into_rpc_err)?;
|
.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)
|
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 {
|
trait TendermintRpcErrorMap {
|
||||||
@@ -120,18 +138,50 @@ impl TendermintRpcClient for ReqwestRpcClient {
|
|||||||
where
|
where
|
||||||
R: SimpleRequest,
|
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
|
where
|
||||||
H: Into<Height> + Send,
|
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> {
|
async fn block_by_hash(
|
||||||
perform_with_compat!(self, block_results::Request::default())
|
&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>
|
async fn header<H>(&self, height: H) -> Result<endpoint::header::Response, Error>
|
||||||
@@ -140,11 +190,26 @@ impl TendermintRpcClient for ReqwestRpcClient {
|
|||||||
{
|
{
|
||||||
let height = height.into();
|
let height = height.into();
|
||||||
match self.compat {
|
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 => {
|
CompatMode::V0_34 => {
|
||||||
// Back-fill with a request to /block endpoint and
|
// Back-fill with a request to /block endpoint and
|
||||||
// taking just the header from the response.
|
// 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())
|
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> {
|
async fn header_by_hash(&self, hash: Hash) -> Result<header_by_hash::Response, Error> {
|
||||||
match self.compat {
|
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 => {
|
CompatMode::V0_34 => {
|
||||||
// Back-fill with a request to /block_by_hash endpoint and
|
// Back-fill with a request to /block_by_hash endpoint and
|
||||||
// taking just the header from the response.
|
// taking just the header from the response.
|
||||||
let resp = self
|
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?;
|
.await?;
|
||||||
Ok(resp.into())
|
Ok(resp.into())
|
||||||
}
|
}
|
||||||
@@ -167,8 +245,18 @@ impl TendermintRpcClient for ReqwestRpcClient {
|
|||||||
/// `/broadcast_evidence`: broadcast an evidence.
|
/// `/broadcast_evidence`: broadcast an evidence.
|
||||||
async fn broadcast_evidence(&self, e: Evidence) -> Result<evidence::Response, Error> {
|
async fn broadcast_evidence(&self, e: Evidence) -> Result<evidence::Response, Error> {
|
||||||
match self.compat {
|
match self.compat {
|
||||||
CompatMode::V0_37 => self.perform(evidence::Request::new(e)).await,
|
CompatMode::V0_38 => {
|
||||||
CompatMode::V0_34 => self.perform_v0_34(evidence::Request::new(e)).await,
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ dirs = { workspace = true, optional = true }
|
|||||||
handlebars = { workspace = true }
|
handlebars = { workspace = true }
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
toml = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
|
toml = { workspace = true, features = ["display"] }
|
||||||
url = { workspace = true }
|
url = { workspace = true }
|
||||||
|
|
||||||
nym-network-defaults = { path = "../network-defaults", features = ["utoipa"] }
|
nym-network-defaults = { path = "../network-defaults", features = ["utoipa"] }
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
use std::io;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum NymConfigTomlError {
|
||||||
|
#[error(transparent)]
|
||||||
|
FileIoFailure(#[from] io::Error),
|
||||||
|
#[error(transparent)]
|
||||||
|
TomlSerializeFailure(#[from] toml::ser::Error),
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ pub use helpers::{parse_urls, OptionalSet};
|
|||||||
pub use toml::de::Error as TomlDeError;
|
pub use toml::de::Error as TomlDeError;
|
||||||
|
|
||||||
pub mod defaults;
|
pub mod defaults;
|
||||||
|
pub mod error;
|
||||||
pub mod helpers;
|
pub mod helpers;
|
||||||
pub mod legacy_helpers;
|
pub mod legacy_helpers;
|
||||||
pub mod serde_helpers;
|
pub mod serde_helpers;
|
||||||
@@ -95,6 +96,42 @@ where
|
|||||||
config.format_to_writer(file)
|
config.format_to_writer(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn save_unformatted_config_to_file<C, P>(
|
||||||
|
config: &C,
|
||||||
|
path: P,
|
||||||
|
) -> Result<(), error::NymConfigTomlError>
|
||||||
|
where
|
||||||
|
C: Serialize + ?Sized,
|
||||||
|
P: AsRef<Path>,
|
||||||
|
{
|
||||||
|
let path = path.as_ref();
|
||||||
|
log::info!("saving config file to {}", path.display());
|
||||||
|
|
||||||
|
if let Some(parent) = path.parent() {
|
||||||
|
create_dir_all(parent)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut file = File::create(path)?;
|
||||||
|
|
||||||
|
// TODO: check for whether any of our configs store anything sensitive
|
||||||
|
// and change that to 0o644 instead
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
{
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
|
||||||
|
let mut perms = fs::metadata(path)?.permissions();
|
||||||
|
perms.set_mode(0o600);
|
||||||
|
fs::set_permissions(path, perms)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// let serde format the TOML in whatever ugly way it chooses
|
||||||
|
// TODO: in https://docs.rs/toml/latest/toml/fn.to_string_pretty.html it recommends using
|
||||||
|
// https://docs.rs/toml_edit/latest/toml_edit/struct.DocumentMut.html to preserve formatting
|
||||||
|
let toml_string = toml::to_string_pretty(config)?;
|
||||||
|
|
||||||
|
Ok(file.write_all(toml_string.as_bytes())?)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn deserialize_config_from_toml_str<C>(raw: &str) -> Result<C, TomlDeError>
|
pub fn deserialize_config_from_toml_str<C>(raw: &str) -> Result<C, TomlDeError>
|
||||||
where
|
where
|
||||||
C: DeserializeOwned,
|
C: DeserializeOwned,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ cosmwasm-std = { workspace = true }
|
|||||||
cosmwasm-schema = { workspace = true }
|
cosmwasm-schema = { workspace = true }
|
||||||
cw-storage-plus = { workspace = true }
|
cw-storage-plus = { workspace = true }
|
||||||
schemars = { workspace = true }
|
schemars = { workspace = true }
|
||||||
|
utoipa = { workspace = true, optional = true }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
|
|
||||||
@@ -23,4 +24,5 @@ serde_json = { workspace = true }
|
|||||||
vergen = { workspace = true, features = ["build", "git", "gitcl", "rustc", "cargo"] }
|
vergen = { workspace = true, features = ["build", "git", "gitcl", "rustc", "cargo"] }
|
||||||
|
|
||||||
[features]
|
[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
|
// 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
|
// perhaps the struct should get renamed and moved to a "more" common crate
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
|
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||||
pub struct ContractBuildInformation {
|
pub struct ContractBuildInformation {
|
||||||
/// Provides the name of the binary, i.e. the content of `CARGO_PKG_NAME` environmental variable.
|
/// Provides the name of the binary, i.e. the content of `CARGO_PKG_NAME` environmental variable.
|
||||||
#[serde(default = "default_unknown")]
|
#[serde(default = "default_unknown")]
|
||||||
|
|||||||
@@ -42,9 +42,11 @@ pub struct Gateway {
|
|||||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||||
pub struct GatewayBond {
|
pub struct GatewayBond {
|
||||||
/// Original amount pledged by the operator of this node.
|
/// Original amount pledged by the operator of this node.
|
||||||
|
#[cfg_attr(feature = "utoipa", schema(value_type = crate::CoinSchema))]
|
||||||
pub pledge_amount: Coin,
|
pub pledge_amount: Coin,
|
||||||
|
|
||||||
/// Address of the owner of this gateway.
|
/// Address of the owner of this gateway.
|
||||||
|
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||||
pub owner: Addr,
|
pub owner: Addr,
|
||||||
|
|
||||||
/// Block height at which this gateway has been bonded.
|
/// 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.
|
/// Entity who bonded this gateway on behalf of the owner.
|
||||||
/// If exists, it's most likely the address of the vesting contract.
|
/// If exists, it's most likely the address of the vesting contract.
|
||||||
|
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||||
pub proxy: Option<Addr>,
|
pub proxy: Option<Addr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
use crate::constants::{TOKEN_SUPPLY, UNIT_DELEGATION_BASE};
|
use crate::constants::{TOKEN_SUPPLY, UNIT_DELEGATION_BASE};
|
||||||
use crate::error::MixnetContractError;
|
use crate::error::MixnetContractError;
|
||||||
use crate::helpers::IntoBaseDecimal;
|
use crate::helpers::IntoBaseDecimal;
|
||||||
|
use crate::nym_node::Role;
|
||||||
use crate::reward_params::{NodeRewardingParameters, RewardingParams};
|
use crate::reward_params::{NodeRewardingParameters, RewardingParams};
|
||||||
use crate::rewarding::helpers::truncate_reward;
|
use crate::rewarding::helpers::truncate_reward;
|
||||||
use crate::rewarding::RewardDistribution;
|
use crate::rewarding::RewardDistribution;
|
||||||
@@ -81,20 +82,25 @@ impl MixNodeDetails {
|
|||||||
|
|
||||||
// currently this struct is shared between mixnodes and nymnodes
|
// currently this struct is shared between mixnodes and nymnodes
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
|
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||||
pub struct NodeRewarding {
|
pub struct NodeRewarding {
|
||||||
/// Information provided by the operator that influence the cost function.
|
/// Information provided by the operator that influence the cost function.
|
||||||
pub cost_params: NodeCostParams,
|
pub cost_params: NodeCostParams,
|
||||||
|
|
||||||
/// Total pledge and compounded reward earned by the node operator.
|
/// Total pledge and compounded reward earned by the node operator.
|
||||||
|
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||||
pub operator: Decimal,
|
pub operator: Decimal,
|
||||||
|
|
||||||
/// Total delegation and compounded reward earned by all node delegators.
|
/// Total delegation and compounded reward earned by all node delegators.
|
||||||
|
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||||
pub delegates: Decimal,
|
pub delegates: Decimal,
|
||||||
|
|
||||||
/// Cumulative reward earned by the "unit delegation" since the block 0.
|
/// Cumulative reward earned by the "unit delegation" since the block 0.
|
||||||
|
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||||
pub total_unit_reward: Decimal,
|
pub total_unit_reward: Decimal,
|
||||||
|
|
||||||
/// Value of the theoretical "unit delegation" that has delegated to this node at block 0.
|
/// 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,
|
pub unit_delegation: Decimal,
|
||||||
|
|
||||||
/// Marks the epoch when this node was last rewarded so that we wouldn't accidentally attempt
|
/// Marks the epoch when this node was last rewarded so that we wouldn't accidentally attempt
|
||||||
@@ -491,14 +497,17 @@ impl NodeRewarding {
|
|||||||
::cosmwasm_schema::schemars::JsonSchema,
|
::cosmwasm_schema::schemars::JsonSchema,
|
||||||
)]
|
)]
|
||||||
#[schemars(crate = "::cosmwasm_schema::schemars")]
|
#[schemars(crate = "::cosmwasm_schema::schemars")]
|
||||||
|
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||||
pub struct MixNodeBond {
|
pub struct MixNodeBond {
|
||||||
/// Unique id assigned to the bonded mixnode.
|
/// Unique id assigned to the bonded mixnode.
|
||||||
pub mix_id: NodeId,
|
pub mix_id: NodeId,
|
||||||
|
|
||||||
/// Address of the owner of this mixnode.
|
/// Address of the owner of this mixnode.
|
||||||
|
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||||
pub owner: Addr,
|
pub owner: Addr,
|
||||||
|
|
||||||
/// Original amount pledged by the operator of this node.
|
/// Original amount pledged by the operator of this node.
|
||||||
|
#[cfg_attr(feature = "utoipa", schema(value_type = crate::CoinSchema))]
|
||||||
pub original_pledge: Coin,
|
pub original_pledge: Coin,
|
||||||
|
|
||||||
// REMOVED (but might be needed due to legacy things, idk yet)
|
// REMOVED (but might be needed due to legacy things, idk yet)
|
||||||
@@ -509,6 +518,7 @@ pub struct MixNodeBond {
|
|||||||
|
|
||||||
/// Entity who bonded this mixnode on behalf of the owner.
|
/// Entity who bonded this mixnode on behalf of the owner.
|
||||||
/// If exists, it's most likely the address of the vesting contract.
|
/// 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>,
|
pub proxy: Option<Addr>,
|
||||||
|
|
||||||
/// Block height at which this mixnode has been bonded.
|
/// Block height at which this mixnode has been bonded.
|
||||||
@@ -544,6 +554,7 @@ impl MixNodeBond {
|
|||||||
feature = "generate-ts",
|
feature = "generate-ts",
|
||||||
ts(export, export_to = "ts-packages/types/src/types/rust/Mixnode.ts")
|
ts(export, export_to = "ts-packages/types/src/types/rust/Mixnode.ts")
|
||||||
)]
|
)]
|
||||||
|
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||||
pub struct MixNode {
|
pub struct MixNode {
|
||||||
/// Network address of this mixnode, for example 1.1.1.1 or foo.mixnode.com
|
/// Network address of this mixnode, for example 1.1.1.1 or foo.mixnode.com
|
||||||
pub host: String,
|
pub host: String,
|
||||||
@@ -570,11 +581,14 @@ pub struct MixNode {
|
|||||||
/// The cost parameters, or the cost function, defined for the particular mixnode that influences
|
/// 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.
|
/// how the rewards should be split between the node operator and its delegators.
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
|
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||||
pub struct NodeCostParams {
|
pub struct NodeCostParams {
|
||||||
/// The profit margin of the associated node, i.e. the desired percent of the reward to be distributed to the operator.
|
/// 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,
|
pub profit_margin_percent: Percent,
|
||||||
|
|
||||||
/// Operating cost of the associated node per the entire interval.
|
/// Operating cost of the associated node per the entire interval.
|
||||||
|
#[cfg_attr(feature = "utoipa", schema(value_type = crate::CoinSchema))]
|
||||||
pub interval_operating_cost: Coin,
|
pub interval_operating_cost: Coin,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -611,6 +625,16 @@ pub enum LegacyMixLayer {
|
|||||||
Three = 3,
|
Three = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<LegacyMixLayer> for Role {
|
||||||
|
fn from(layer: LegacyMixLayer) -> Self {
|
||||||
|
match layer {
|
||||||
|
LegacyMixLayer::One => Role::Layer1,
|
||||||
|
LegacyMixLayer::Two => Role::Layer2,
|
||||||
|
LegacyMixLayer::Three => Role::Layer3,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<LegacyMixLayer> for String {
|
impl From<LegacyMixLayer> for String {
|
||||||
fn from(layer: LegacyMixLayer) -> Self {
|
fn from(layer: LegacyMixLayer) -> Self {
|
||||||
(layer as u8).to_string()
|
(layer as u8).to_string()
|
||||||
@@ -669,7 +693,9 @@ pub struct PendingMixNodeChanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
#[derive(Default, Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||||
pub struct LegacyPendingMixNodeChanges {
|
pub struct LegacyPendingMixNodeChanges {
|
||||||
|
#[cfg_attr(feature = "utoipa", schema(value_type = Option<u32>))]
|
||||||
pub pledge_change: Option<EpochEventId>,
|
pub pledge_change: Option<EpochEventId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -113,6 +113,10 @@ impl Role {
|
|||||||
pub fn is_standby(&self) -> bool {
|
pub fn is_standby(&self) -> bool {
|
||||||
matches!(self, Role::Standby)
|
matches!(self, Role::Standby)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_mixnode(&self) -> bool {
|
||||||
|
matches!(self, Role::Layer1 | Role::Layer2 | Role::Layer3)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Role {
|
impl Display for Role {
|
||||||
@@ -231,6 +235,7 @@ pub struct RoleMetadata {
|
|||||||
|
|
||||||
/// Full details associated with given node.
|
/// Full details associated with given node.
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
|
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||||
pub struct NymNodeDetails {
|
pub struct NymNodeDetails {
|
||||||
/// Basic bond information of this node, such as owner address, original pledge, etc.
|
/// Basic bond information of this node, such as owner address, original pledge, etc.
|
||||||
pub bond_information: NymNodeBond,
|
pub bond_information: NymNodeBond,
|
||||||
@@ -288,14 +293,19 @@ impl NymNodeDetails {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
|
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||||
pub struct NymNodeBond {
|
pub struct NymNodeBond {
|
||||||
/// Unique id assigned to the bonded node.
|
/// Unique id assigned to the bonded node.
|
||||||
|
#[cfg_attr(feature = "utoipa", schema(value_type = u32))]
|
||||||
pub node_id: NodeId,
|
pub node_id: NodeId,
|
||||||
|
|
||||||
/// Address of the owner of this nym-node.
|
/// Address of the owner of this nym-node.
|
||||||
|
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||||
pub owner: Addr,
|
pub owner: Addr,
|
||||||
|
|
||||||
/// Original amount pledged by the operator of this node.
|
/// Original amount pledged by the operator of this node.
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "utoipa", schema(value_type = crate::CoinSchema))]
|
||||||
pub original_pledge: Coin,
|
pub original_pledge: Coin,
|
||||||
|
|
||||||
/// Block height at which this nym-node has been bonded.
|
/// Block height at which this nym-node has been bonded.
|
||||||
@@ -348,6 +358,7 @@ impl NymNodeBond {
|
|||||||
feature = "generate-ts",
|
feature = "generate-ts",
|
||||||
ts(export, export_to = "ts-packages/types/src/types/rust/NymNode.ts")
|
ts(export, export_to = "ts-packages/types/src/types/rust/NymNode.ts")
|
||||||
)]
|
)]
|
||||||
|
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||||
pub struct NymNode {
|
pub struct NymNode {
|
||||||
/// Network address of this nym-node, for example 1.1.1.1 or foo.mixnode.com
|
/// 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.
|
/// that is used to discover other capabilities of this node.
|
||||||
@@ -358,6 +369,7 @@ pub struct NymNode {
|
|||||||
pub custom_http_port: Option<u16>,
|
pub custom_http_port: Option<u16>,
|
||||||
|
|
||||||
/// Base58-encoded ed25519 EdDSA public key.
|
/// Base58-encoded ed25519 EdDSA public key.
|
||||||
|
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||||
pub identity_key: IdentityKey,
|
pub identity_key: IdentityKey,
|
||||||
// TODO: I don't think we want to include sphinx keys here,
|
// 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
|
// 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"
|
export_to = "ts-packages/types/src/types/rust/PendingNodeChanges.ts"
|
||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
|
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||||
pub struct PendingNodeChanges {
|
pub struct PendingNodeChanges {
|
||||||
|
#[cfg_attr(feature = "utoipa", schema(value_type = Option<u32>))]
|
||||||
pub pledge_change: Option<EpochEventId>,
|
pub pledge_change: Option<EpochEventId>,
|
||||||
|
#[cfg_attr(feature = "utoipa", schema(value_type = Option<u32>))]
|
||||||
pub cost_params_change: Option<IntervalEventId>,
|
pub cost_params_change: Option<IntervalEventId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,31 +21,37 @@ pub type WorkFactor = Decimal;
|
|||||||
)]
|
)]
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
#[derive(Copy)]
|
#[derive(Copy)]
|
||||||
|
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||||
pub struct IntervalRewardParams {
|
pub struct IntervalRewardParams {
|
||||||
/// Current value of the rewarding pool.
|
/// Current value of the rewarding pool.
|
||||||
/// It is expected to be constant throughout the interval.
|
/// It is expected to be constant throughout the interval.
|
||||||
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
||||||
|
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||||
pub reward_pool: Decimal,
|
pub reward_pool: Decimal,
|
||||||
|
|
||||||
/// Current value of the staking supply.
|
/// Current value of the staking supply.
|
||||||
/// It is expected to be constant throughout the interval.
|
/// It is expected to be constant throughout the interval.
|
||||||
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
||||||
|
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||||
pub staking_supply: Decimal,
|
pub staking_supply: Decimal,
|
||||||
|
|
||||||
/// Defines the percentage of stake needed to reach saturation for all of the nodes in the rewarded set.
|
/// Defines the percentage of stake needed to reach saturation for all of the nodes in the rewarded set.
|
||||||
/// Also known as `beta`.
|
/// Also known as `beta`.
|
||||||
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
||||||
|
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||||
pub staking_supply_scale_factor: Percent,
|
pub staking_supply_scale_factor: Percent,
|
||||||
|
|
||||||
// computed values
|
// computed values
|
||||||
/// Current value of the computed reward budget per epoch, per node.
|
/// Current value of the computed reward budget per epoch, per node.
|
||||||
/// It is expected to be constant throughout the interval.
|
/// It is expected to be constant throughout the interval.
|
||||||
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
||||||
|
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||||
pub epoch_reward_budget: Decimal,
|
pub epoch_reward_budget: Decimal,
|
||||||
|
|
||||||
/// Current value of the stake saturation point.
|
/// Current value of the stake saturation point.
|
||||||
/// It is expected to be constant throughout the interval.
|
/// It is expected to be constant throughout the interval.
|
||||||
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
||||||
|
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||||
pub stake_saturation_point: Decimal,
|
pub stake_saturation_point: Decimal,
|
||||||
|
|
||||||
// constants(-ish)
|
// constants(-ish)
|
||||||
@@ -54,6 +60,7 @@ pub struct IntervalRewardParams {
|
|||||||
/// It is not really expected to be changing very often.
|
/// 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.
|
/// 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 = "generate-ts", ts(type = "string"))]
|
||||||
|
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||||
pub sybil_resistance: Percent,
|
pub sybil_resistance: Percent,
|
||||||
|
|
||||||
// default: 10
|
// default: 10
|
||||||
@@ -61,6 +68,7 @@ pub struct IntervalRewardParams {
|
|||||||
/// It is not really expected to be changing very often.
|
/// 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.
|
/// 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 = "generate-ts", ts(type = "string"))]
|
||||||
|
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||||
pub active_set_work_factor: Decimal,
|
pub active_set_work_factor: Decimal,
|
||||||
|
|
||||||
// default: 2%
|
// default: 2%
|
||||||
@@ -70,6 +78,7 @@ pub struct IntervalRewardParams {
|
|||||||
/// It is not really expected to be changing very often.
|
/// 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.
|
/// 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 = "generate-ts", ts(type = "string"))]
|
||||||
|
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||||
pub interval_pool_emission: Percent,
|
pub interval_pool_emission: Percent,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,6 +99,7 @@ impl IntervalRewardParams {
|
|||||||
)]
|
)]
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
#[derive(Copy)]
|
#[derive(Copy)]
|
||||||
|
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||||
pub struct RewardingParams {
|
pub struct RewardingParams {
|
||||||
/// Parameters that should remain unchanged throughout an interval.
|
/// Parameters that should remain unchanged throughout an interval.
|
||||||
pub interval: IntervalRewardParams,
|
pub interval: IntervalRewardParams,
|
||||||
@@ -254,6 +264,7 @@ impl RewardingParams {
|
|||||||
)]
|
)]
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
#[derive(Copy)]
|
#[derive(Copy)]
|
||||||
|
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||||
pub struct RewardedSetParams {
|
pub struct RewardedSetParams {
|
||||||
/// The expected number of nodes assigned entry gateway role (i.e. [`Role::EntryGateway`])
|
/// The expected number of nodes assigned entry gateway role (i.e. [`Role::EntryGateway`])
|
||||||
pub entry_gateways: u32,
|
pub entry_gateways: u32,
|
||||||
|
|||||||
@@ -17,10 +17,12 @@ pub mod simulator;
|
|||||||
)]
|
)]
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
#[derive(Copy, Default)]
|
#[derive(Copy, Default)]
|
||||||
|
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||||
pub struct RewardEstimate {
|
pub struct RewardEstimate {
|
||||||
/// The amount of **decimal** coins that are going to get distributed to the node,
|
/// The amount of **decimal** coins that are going to get distributed to the node,
|
||||||
/// i.e. the operator and all its delegators.
|
/// i.e. the operator and all its delegators.
|
||||||
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
#[cfg_attr(feature = "generate-ts", ts(type = "string"))]
|
||||||
|
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||||
pub total_node_reward: Decimal,
|
pub total_node_reward: Decimal,
|
||||||
|
|
||||||
// note that operator reward includes the operating_cost,
|
// 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
|
// 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.
|
/// 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 = "generate-ts", ts(type = "string"))]
|
||||||
|
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||||
pub operator: Decimal,
|
pub operator: Decimal,
|
||||||
|
|
||||||
/// The share of the reward that is going to get distributed among the node delegators.
|
/// 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 = "generate-ts", ts(type = "string"))]
|
||||||
|
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||||
pub delegates: Decimal,
|
pub delegates: Decimal,
|
||||||
|
|
||||||
/// The operating cost of this node. Note: it's already included in the operator reward.
|
/// 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 = "generate-ts", ts(type = "string"))]
|
||||||
|
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||||
pub operating_cost: Decimal,
|
pub operating_cost: Decimal,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
use crate::config_score::{ConfigScoreParams, OutdatedVersionWeights, VersionScoreFormulaParams};
|
use crate::config_score::{ConfigScoreParams, OutdatedVersionWeights, VersionScoreFormulaParams};
|
||||||
use crate::nym_node::Role;
|
use crate::nym_node::Role;
|
||||||
|
use crate::EpochId;
|
||||||
use contracts_common::Percent;
|
use contracts_common::Percent;
|
||||||
use cosmwasm_schema::cw_serde;
|
use cosmwasm_schema::cw_serde;
|
||||||
use cosmwasm_std::Coin;
|
use cosmwasm_std::Coin;
|
||||||
@@ -32,6 +33,23 @@ impl RoleAssignment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cw_serde]
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct EpochRewardedSet {
|
||||||
|
pub epoch_id: EpochId,
|
||||||
|
|
||||||
|
pub assignment: RewardedSet,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(EpochId, RewardedSet)> for EpochRewardedSet {
|
||||||
|
fn from((epoch_id, assignment): (EpochId, RewardedSet)) -> Self {
|
||||||
|
EpochRewardedSet {
|
||||||
|
epoch_id,
|
||||||
|
assignment,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct RewardedSet {
|
pub struct RewardedSet {
|
||||||
@@ -69,6 +87,29 @@ impl RewardedSet {
|
|||||||
pub fn rewarded_set_size(&self) -> usize {
|
pub fn rewarded_set_size(&self) -> usize {
|
||||||
self.active_set_size() + self.standby.len()
|
self.active_set_size() + self.standby.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_role(&self, node_id: NodeId) -> Option<Role> {
|
||||||
|
// given each role has ~100 entries in them, doing linear lookup with vec should be fine
|
||||||
|
if self.entry_gateways.contains(&node_id) {
|
||||||
|
return Some(Role::EntryGateway);
|
||||||
|
}
|
||||||
|
if self.exit_gateways.contains(&node_id) {
|
||||||
|
return Some(Role::ExitGateway);
|
||||||
|
}
|
||||||
|
if self.layer1.contains(&node_id) {
|
||||||
|
return Some(Role::Layer1);
|
||||||
|
}
|
||||||
|
if self.layer2.contains(&node_id) {
|
||||||
|
return Some(Role::Layer2);
|
||||||
|
}
|
||||||
|
if self.layer3.contains(&node_id) {
|
||||||
|
return Some(Role::Layer3);
|
||||||
|
}
|
||||||
|
if self.standby.contains(&node_id) {
|
||||||
|
return Some(Role::Standby);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
@@ -134,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.
|
/// The current state of the mixnet contract.
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
pub struct ContractState {
|
pub struct ContractState {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ use std::error::Error;
|
|||||||
// `SELECT total_tickets, used_tickets FROM ecash_ticketbook WHERE expiration_date >= ?`, today_date
|
// `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
|
// then for each calculate the diff total_tickets - used_tickets and multiply the result by the size of the ticket
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait Storage: Send + Sync {
|
pub trait Storage: Clone + Send + Sync {
|
||||||
type StorageError: Error;
|
type StorageError: Error;
|
||||||
|
|
||||||
async fn close(&self);
|
async fn close(&self);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ serde = { workspace = true, features = ["derive"] }
|
|||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
strum = { workspace = true, features = ["derive"] }
|
strum = { workspace = true, features = ["derive"] }
|
||||||
time = { workspace = true, features = ["serde"] }
|
time = { workspace = true, features = ["serde"] }
|
||||||
|
utoipa = { workspace = true }
|
||||||
rand = { workspace = true }
|
rand = { workspace = true }
|
||||||
|
|
||||||
nym-compact-ecash = { path = "../nym_offline_compact_ecash" }
|
nym-compact-ecash = { path = "../nym_offline_compact_ecash" }
|
||||||
|
|||||||
@@ -86,7 +86,6 @@ impl Display for AddressPolicyAction {
|
|||||||
/// ```
|
/// ```
|
||||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
|
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
|
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
|
||||||
#[cfg_attr(feature = "openapi", aliases(ExitPolicy))]
|
|
||||||
pub struct AddressPolicy {
|
pub struct AddressPolicy {
|
||||||
/// A list of rules to apply to find out whether an address is
|
/// A list of rules to apply to find out whether an address is
|
||||||
/// contained by this policy.
|
/// contained by this policy.
|
||||||
@@ -727,10 +726,10 @@ mod test {
|
|||||||
let policy = AddressPolicy::parse_from_torrc(
|
let policy = AddressPolicy::parse_from_torrc(
|
||||||
r#"
|
r#"
|
||||||
ExitPolicy reject 1.2.3.4/32:*
|
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:*
|
||||||
ExitPolicy reject 1.2.3.6/16:123-456
|
ExitPolicy reject 1.2.3.6/16:123-456
|
||||||
ExitPolicy accept *:53
|
ExitPolicy accept *:53
|
||||||
ExitPolicy accept6 *6:119
|
ExitPolicy accept6 *6:119
|
||||||
ExitPolicy accept *4:120
|
ExitPolicy accept *4:120
|
||||||
ExitPolicy reject6 [FC00::]/7:*
|
ExitPolicy reject6 [FC00::]/7:*
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ license.workspace = true
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
bincode = { workspace = true }
|
bincode = { workspace = true }
|
||||||
defguard_wireguard_rs = { workspace = true }
|
defguard_wireguard_rs = { workspace = true }
|
||||||
log = { workspace = true }
|
|
||||||
sqlx = { workspace = true, features = [
|
sqlx = { workspace = true, features = [
|
||||||
"runtime-tokio-rustls",
|
"runtime-tokio-rustls",
|
||||||
"sqlite",
|
"sqlite",
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
ALTER TABLE message_store
|
||||||
|
ADD COLUMN timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
||||||
@@ -2,9 +2,11 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
use crate::models::StoredMessage;
|
use crate::models::StoredMessage;
|
||||||
|
use time::OffsetDateTime;
|
||||||
|
use tracing::debug;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct InboxManager {
|
pub struct InboxManager {
|
||||||
connection_pool: sqlx::SqlitePool,
|
connection_pool: sqlx::SqlitePool,
|
||||||
/// Maximum number of messages that can be obtained from the database per operation.
|
/// 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
|
/// 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
|
// get 1 additional message to check whether there will be more to grab
|
||||||
// next time
|
// next time
|
||||||
let limit = self.retrieval_limit + 1;
|
let limit = self.retrieval_limit + 1;
|
||||||
let mut res = if let Some(start_after) = start_after {
|
let start_after = start_after.unwrap_or(-1);
|
||||||
sqlx::query_as!(
|
|
||||||
StoredMessage,
|
let mut res: Vec<StoredMessage> = sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
SELECT
|
SELECT id, client_address_bs58, content, timestamp
|
||||||
id as "id!",
|
FROM message_store
|
||||||
client_address_bs58 as "client_address_bs58!",
|
WHERE client_address_bs58 = ? AND id > ?
|
||||||
content as "content!"
|
ORDER BY id ASC
|
||||||
FROM message_store
|
LIMIT ?;
|
||||||
WHERE client_address_bs58 = ? AND id > ?
|
"#,
|
||||||
ORDER BY id ASC
|
)
|
||||||
LIMIT ?;
|
.bind(client_address_bs58)
|
||||||
"#,
|
.bind(start_after)
|
||||||
client_address_bs58,
|
.bind(limit)
|
||||||
start_after,
|
.fetch_all(&self.connection_pool)
|
||||||
limit
|
.await?;
|
||||||
)
|
|
||||||
.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?
|
|
||||||
};
|
|
||||||
|
|
||||||
if res.len() > self.retrieval_limit as usize {
|
if res.len() > self.retrieval_limit as usize {
|
||||||
res.truncate(self.retrieval_limit as usize);
|
res.truncate(self.retrieval_limit as usize);
|
||||||
@@ -146,4 +126,13 @@ impl InboxManager {
|
|||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
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 bandwidth::BandwidthManager;
|
||||||
use clients::{ClientManager, ClientType};
|
use clients::{ClientManager, ClientType};
|
||||||
use inboxes::InboxManager;
|
|
||||||
use models::{
|
use models::{
|
||||||
Client, PersistedBandwidth, PersistedSharedKeys, RedemptionProposal, StoredMessage,
|
Client, PersistedBandwidth, PersistedSharedKeys, RedemptionProposal, StoredMessage,
|
||||||
VerifiedTicket, WireguardPeer,
|
VerifiedTicket, WireguardPeer,
|
||||||
@@ -31,6 +30,7 @@ mod tickets;
|
|||||||
mod wireguard_peers;
|
mod wireguard_peers;
|
||||||
|
|
||||||
pub use error::GatewayStorageError;
|
pub use error::GatewayStorageError;
|
||||||
|
pub use inboxes::InboxManager;
|
||||||
|
|
||||||
// note that clone here is fine as upon cloning the same underlying pool will be used
|
// note that clone here is fine as upon cloning the same underlying pool will be used
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -53,7 +53,7 @@ impl GatewayStorage {
|
|||||||
&self.shared_key_manager
|
&self.shared_key_manager
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn inbox_manager(&self) -> &InboxManager {
|
pub fn inbox_manager(&self) -> &InboxManager {
|
||||||
&self.inbox_manager
|
&self.inbox_manager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,11 +48,13 @@ impl TryFrom<PersistedSharedKeys> for SharedGatewayKey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(FromRow)]
|
||||||
pub struct StoredMessage {
|
pub struct StoredMessage {
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub client_address_bs58: String,
|
pub client_address_bs58: String,
|
||||||
pub content: Vec<u8>,
|
pub content: Vec<u8>,
|
||||||
|
pub timestamp: OffsetDateTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, FromRow)]
|
#[derive(Debug, Clone, FromRow)]
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ async-trait = { workspace = true }
|
|||||||
reqwest = { workspace = true, features = ["json"] }
|
reqwest = { workspace = true, features = ["json"] }
|
||||||
http.workspace = true
|
http.workspace = true
|
||||||
url = { workspace = true }
|
url = { workspace = true }
|
||||||
|
once_cell = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
@@ -22,7 +23,13 @@ tracing = { workspace = true }
|
|||||||
|
|
||||||
nym-bin-common = { path = "../bin-common" }
|
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
|
# for request timeout until https://github.com/seanmonstar/reqwest/issues/1135 is fixed
|
||||||
[target."cfg(target_arch = \"wasm32\")".dependencies.wasmtimer]
|
[target."cfg(target_arch = \"wasm32\")".dependencies.wasmtimer]
|
||||||
workspace = true
|
workspace = true
|
||||||
features = ["tokio"]
|
features = ["tokio"]
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tokio = { workspace = true, features=["rt", "macros"] }
|
||||||
|
|||||||
@@ -0,0 +1,177 @@
|
|||||||
|
//! 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}")]
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
+212
-199
@@ -6,17 +6,23 @@ use reqwest::header::HeaderValue;
|
|||||||
use reqwest::{RequestBuilder, Response, StatusCode};
|
use reqwest::{RequestBuilder, Response, StatusCode};
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::Display;
|
|
||||||
use std::time::Duration;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tracing::{instrument, warn};
|
use tracing::{instrument, warn};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::{fmt::Display, time::Duration};
|
||||||
|
|
||||||
pub use reqwest::IntoUrl;
|
pub use reqwest::IntoUrl;
|
||||||
|
|
||||||
|
mod user_agent;
|
||||||
pub use user_agent::UserAgent;
|
pub use user_agent::UserAgent;
|
||||||
|
|
||||||
mod user_agent;
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
mod dns;
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
pub use dns::HickoryDnsResolver;
|
||||||
|
|
||||||
// The timeout is relatively high as we are often making requests over the mixnet, where latency is
|
// 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.
|
// high and chatty protocols take a while to complete.
|
||||||
@@ -86,11 +92,18 @@ impl ClientBuilder {
|
|||||||
// TODO: or should we maybe default to https?
|
// TODO: or should we maybe default to https?
|
||||||
Self::new(alt)
|
Self::new(alt)
|
||||||
} else {
|
} 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 {
|
Ok(ClientBuilder {
|
||||||
url: url.into_url()?,
|
url: url.into_url()?,
|
||||||
timeout: None,
|
timeout: None,
|
||||||
custom_user_agent: false,
|
custom_user_agent: false,
|
||||||
reqwest_client_builder: reqwest::ClientBuilder::new(),
|
reqwest_client_builder,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,6 +205,28 @@ impl Client {
|
|||||||
&self.base_url
|
&self.base_url
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create_request<B, K, V>(
|
||||||
|
&self,
|
||||||
|
method: reqwest::Method,
|
||||||
|
path: PathSegments<'_>,
|
||||||
|
params: Params<'_, K, V>,
|
||||||
|
json_body: Option<&B>,
|
||||||
|
) -> RequestBuilder
|
||||||
|
where
|
||||||
|
B: Serialize + ?Sized,
|
||||||
|
K: AsRef<str>,
|
||||||
|
V: AsRef<str>,
|
||||||
|
{
|
||||||
|
let url = sanitize_url(&self.base_url, path, params);
|
||||||
|
let mut request = self.reqwest_client.request(method.clone(), url);
|
||||||
|
|
||||||
|
if let Some(body) = json_body {
|
||||||
|
request = request.json(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
request
|
||||||
|
}
|
||||||
|
|
||||||
pub fn create_get_request<K, V>(
|
pub fn create_get_request<K, V>(
|
||||||
&self,
|
&self,
|
||||||
path: PathSegments<'_>,
|
path: PathSegments<'_>,
|
||||||
@@ -205,38 +240,6 @@ impl Client {
|
|||||||
self.reqwest_client.get(url)
|
self.reqwest_client.get(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "debug", skip_all, fields(path=?path))]
|
|
||||||
async fn send_get_request<K, V, E>(
|
|
||||||
&self,
|
|
||||||
path: PathSegments<'_>,
|
|
||||||
params: Params<'_, K, V>,
|
|
||||||
) -> Result<Response, HttpClientError<E>>
|
|
||||||
where
|
|
||||||
K: AsRef<str>,
|
|
||||||
V: AsRef<str>,
|
|
||||||
E: Display,
|
|
||||||
{
|
|
||||||
tracing::trace!("Sending GET request");
|
|
||||||
let url = sanitize_url(&self.base_url, path, params);
|
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
{
|
|
||||||
Ok(
|
|
||||||
wasmtimer::tokio::timeout(
|
|
||||||
self.request_timeout,
|
|
||||||
self.reqwest_client.get(url).send(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map_err(|_timeout| HttpClientError::RequestTimeout)??,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
{
|
|
||||||
Ok(self.reqwest_client.get(url).send().await?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_post_request<B, K, V>(
|
pub fn create_post_request<B, K, V>(
|
||||||
&self,
|
&self,
|
||||||
path: PathSegments<'_>,
|
path: PathSegments<'_>,
|
||||||
@@ -252,36 +255,6 @@ impl Client {
|
|||||||
self.reqwest_client.post(url).json(json_body)
|
self.reqwest_client.post(url).json(json_body)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_post_request<B, K, V, E>(
|
|
||||||
&self,
|
|
||||||
path: PathSegments<'_>,
|
|
||||||
params: Params<'_, K, V>,
|
|
||||||
json_body: &B,
|
|
||||||
) -> Result<Response, HttpClientError<E>>
|
|
||||||
where
|
|
||||||
B: Serialize + ?Sized,
|
|
||||||
K: AsRef<str>,
|
|
||||||
V: AsRef<str>,
|
|
||||||
E: Display,
|
|
||||||
{
|
|
||||||
let url = sanitize_url(&self.base_url, path, params);
|
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
{
|
|
||||||
Ok(wasmtimer::tokio::timeout(
|
|
||||||
self.request_timeout,
|
|
||||||
self.reqwest_client.post(url).json(json_body).send(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map_err(|_timeout| HttpClientError::RequestTimeout)??)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
{
|
|
||||||
Ok(self.reqwest_client.post(url).json(json_body).send().await?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_delete_request<K, V>(
|
pub fn create_delete_request<K, V>(
|
||||||
&self,
|
&self,
|
||||||
path: PathSegments<'_>,
|
path: PathSegments<'_>,
|
||||||
@@ -295,35 +268,6 @@ impl Client {
|
|||||||
self.reqwest_client.delete(url)
|
self.reqwest_client.delete(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_delete_request<K, V, E>(
|
|
||||||
&self,
|
|
||||||
path: PathSegments<'_>,
|
|
||||||
params: Params<'_, K, V>,
|
|
||||||
) -> Result<Response, HttpClientError<E>>
|
|
||||||
where
|
|
||||||
K: AsRef<str>,
|
|
||||||
V: AsRef<str>,
|
|
||||||
E: Display,
|
|
||||||
{
|
|
||||||
tracing::trace!("Sending DELETE request");
|
|
||||||
let url = sanitize_url(&self.base_url, path, params);
|
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
{
|
|
||||||
Ok(wasmtimer::tokio::timeout(
|
|
||||||
self.request_timeout,
|
|
||||||
self.reqwest_client.delete(url).send(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map_err(|_timeout| HttpClientError::RequestTimeout)??)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
{
|
|
||||||
Ok(self.reqwest_client.delete(url).send().await?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_patch_request<B, K, V>(
|
pub fn create_patch_request<B, K, V>(
|
||||||
&self,
|
&self,
|
||||||
path: PathSegments<'_>,
|
path: PathSegments<'_>,
|
||||||
@@ -339,6 +283,87 @@ impl Client {
|
|||||||
self.reqwest_client.patch(url).json(json_body)
|
self.reqwest_client.patch(url).json(json_body)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn send_request<B, K, V, E>(
|
||||||
|
&self,
|
||||||
|
method: reqwest::Method,
|
||||||
|
path: PathSegments<'_>,
|
||||||
|
params: Params<'_, K, V>,
|
||||||
|
json_body: Option<&B>,
|
||||||
|
) -> Result<Response, HttpClientError<E>>
|
||||||
|
where
|
||||||
|
B: Serialize + ?Sized,
|
||||||
|
K: AsRef<str>,
|
||||||
|
V: AsRef<str>,
|
||||||
|
E: Display,
|
||||||
|
{
|
||||||
|
let url = sanitize_url(&self.base_url, path, params);
|
||||||
|
|
||||||
|
let mut request = self.reqwest_client.request(method.clone(), url);
|
||||||
|
|
||||||
|
if let Some(body) = json_body {
|
||||||
|
request = request.json(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
{
|
||||||
|
Ok(
|
||||||
|
wasmtimer::tokio::timeout(self.request_timeout, request.send())
|
||||||
|
.await
|
||||||
|
.map_err(|_timeout| HttpClientError::RequestTimeout)??,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
{
|
||||||
|
Ok(request.send().await?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip_all, fields(path=?path))]
|
||||||
|
async fn send_get_request<K, V, E>(
|
||||||
|
&self,
|
||||||
|
path: PathSegments<'_>,
|
||||||
|
params: Params<'_, K, V>,
|
||||||
|
) -> Result<Response, HttpClientError<E>>
|
||||||
|
where
|
||||||
|
K: AsRef<str>,
|
||||||
|
V: AsRef<str>,
|
||||||
|
E: Display,
|
||||||
|
{
|
||||||
|
self.send_request(reqwest::Method::GET, path, params, None::<&()>)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn send_post_request<B, K, V, E>(
|
||||||
|
&self,
|
||||||
|
path: PathSegments<'_>,
|
||||||
|
params: Params<'_, K, V>,
|
||||||
|
json_body: &B,
|
||||||
|
) -> Result<Response, HttpClientError<E>>
|
||||||
|
where
|
||||||
|
B: Serialize + ?Sized,
|
||||||
|
K: AsRef<str>,
|
||||||
|
V: AsRef<str>,
|
||||||
|
E: Display,
|
||||||
|
{
|
||||||
|
self.send_request(reqwest::Method::POST, path, params, Some(json_body))
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_delete_request<K, V, E>(
|
||||||
|
&self,
|
||||||
|
path: PathSegments<'_>,
|
||||||
|
params: Params<'_, K, V>,
|
||||||
|
) -> Result<Response, HttpClientError<E>>
|
||||||
|
where
|
||||||
|
K: AsRef<str>,
|
||||||
|
V: AsRef<str>,
|
||||||
|
E: Display,
|
||||||
|
{
|
||||||
|
self.send_request(reqwest::Method::DELETE, path, params, None::<&()>)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn send_patch_request<B, K, V, E>(
|
pub async fn send_patch_request<B, K, V, E>(
|
||||||
&self,
|
&self,
|
||||||
path: PathSegments<'_>,
|
path: PathSegments<'_>,
|
||||||
@@ -351,27 +376,8 @@ impl Client {
|
|||||||
V: AsRef<str>,
|
V: AsRef<str>,
|
||||||
E: Display,
|
E: Display,
|
||||||
{
|
{
|
||||||
let url = sanitize_url(&self.base_url, path, params);
|
self.send_request(reqwest::Method::PATCH, path, params, Some(json_body))
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
{
|
|
||||||
Ok(wasmtimer::tokio::timeout(
|
|
||||||
self.request_timeout,
|
|
||||||
self.reqwest_client.patch(url).json(json_body).send(),
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
.map_err(|_timeout| HttpClientError::RequestTimeout)??)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
{
|
|
||||||
Ok(self
|
|
||||||
.reqwest_client
|
|
||||||
.patch(url)
|
|
||||||
.json(json_body)
|
|
||||||
.send()
|
|
||||||
.await?)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "debug", skip_all)]
|
#[instrument(level = "debug", skip_all)]
|
||||||
@@ -439,6 +445,39 @@ impl Client {
|
|||||||
parse_response(res, true).await
|
parse_response(res, true).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn call_json_endpoint<B, T, S, E>(
|
||||||
|
&self,
|
||||||
|
method: reqwest::Method,
|
||||||
|
endpoint: S,
|
||||||
|
json_body: Option<&B>,
|
||||||
|
) -> Result<T, HttpClientError<E>>
|
||||||
|
where
|
||||||
|
B: Serialize + ?Sized,
|
||||||
|
for<'a> T: Deserialize<'a>,
|
||||||
|
E: Display + DeserializeOwned,
|
||||||
|
S: AsRef<str>,
|
||||||
|
{
|
||||||
|
let mut request = self
|
||||||
|
.reqwest_client
|
||||||
|
.request(method.clone(), self.base_url.join(endpoint.as_ref())?);
|
||||||
|
|
||||||
|
if let Some(body) = json_body {
|
||||||
|
request = request.json(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
let res = {
|
||||||
|
wasmtimer::tokio::timeout(self.request_timeout, request.send())
|
||||||
|
.await
|
||||||
|
.map_err(|_timeout| HttpClientError::RequestTimeout)??
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
let res = { request.send().await? };
|
||||||
|
|
||||||
|
parse_response(res, false).await
|
||||||
|
}
|
||||||
|
|
||||||
#[instrument(level = "debug", skip_all)]
|
#[instrument(level = "debug", skip_all)]
|
||||||
pub async fn get_json_endpoint<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
|
pub async fn get_json_endpoint<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
|
||||||
where
|
where
|
||||||
@@ -446,27 +485,8 @@ impl Client {
|
|||||||
E: Display + DeserializeOwned,
|
E: Display + DeserializeOwned,
|
||||||
S: AsRef<str>,
|
S: AsRef<str>,
|
||||||
{
|
{
|
||||||
#[cfg(target_arch = "wasm32")]
|
self.call_json_endpoint(reqwest::Method::GET, endpoint, None::<&()>)
|
||||||
let res = {
|
|
||||||
wasmtimer::tokio::timeout(
|
|
||||||
self.request_timeout,
|
|
||||||
self.reqwest_client
|
|
||||||
.get(self.base_url.join(endpoint.as_ref())?)
|
|
||||||
.send(),
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
.map_err(|_timeout| HttpClientError::RequestTimeout)??
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
let res = {
|
|
||||||
self.reqwest_client
|
|
||||||
.get(self.base_url.join(endpoint.as_ref())?)
|
|
||||||
.send()
|
|
||||||
.await?
|
|
||||||
};
|
|
||||||
|
|
||||||
parse_response(res, false).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn post_json_endpoint<B, T, S, E>(
|
pub async fn post_json_endpoint<B, T, S, E>(
|
||||||
@@ -480,29 +500,8 @@ impl Client {
|
|||||||
E: Display + DeserializeOwned,
|
E: Display + DeserializeOwned,
|
||||||
S: AsRef<str>,
|
S: AsRef<str>,
|
||||||
{
|
{
|
||||||
#[cfg(target_arch = "wasm32")]
|
self.call_json_endpoint(reqwest::Method::POST, endpoint, Some(json_body))
|
||||||
let res = {
|
|
||||||
wasmtimer::tokio::timeout(
|
|
||||||
self.request_timeout,
|
|
||||||
self.reqwest_client
|
|
||||||
.post(self.base_url.join(endpoint.as_ref())?)
|
|
||||||
.json(json_body)
|
|
||||||
.send(),
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
.map_err(|_timeout| HttpClientError::RequestTimeout)??
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
let res = {
|
|
||||||
self.reqwest_client
|
|
||||||
.post(self.base_url.join(endpoint.as_ref())?)
|
|
||||||
.json(json_body)
|
|
||||||
.send()
|
|
||||||
.await?
|
|
||||||
};
|
|
||||||
|
|
||||||
parse_response(res, true).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_json_endpoint<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
|
pub async fn delete_json_endpoint<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
|
||||||
@@ -511,27 +510,8 @@ impl Client {
|
|||||||
E: Display + DeserializeOwned,
|
E: Display + DeserializeOwned,
|
||||||
S: AsRef<str>,
|
S: AsRef<str>,
|
||||||
{
|
{
|
||||||
#[cfg(target_arch = "wasm32")]
|
self.call_json_endpoint(reqwest::Method::DELETE, endpoint, None::<&()>)
|
||||||
let res = {
|
|
||||||
wasmtimer::tokio::timeout(
|
|
||||||
self.request_timeout,
|
|
||||||
self.reqwest_client
|
|
||||||
.delete(self.base_url.join(endpoint.as_ref())?)
|
|
||||||
.send(),
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
.map_err(|_timeout| HttpClientError::RequestTimeout)??
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
let res = {
|
|
||||||
self.reqwest_client
|
|
||||||
.delete(self.base_url.join(endpoint.as_ref())?)
|
|
||||||
.send()
|
|
||||||
.await?
|
|
||||||
};
|
|
||||||
|
|
||||||
parse_response(res, false).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn patch_json_endpoint<B, T, S, E>(
|
pub async fn patch_json_endpoint<B, T, S, E>(
|
||||||
@@ -545,29 +525,8 @@ impl Client {
|
|||||||
E: Display + DeserializeOwned,
|
E: Display + DeserializeOwned,
|
||||||
S: AsRef<str>,
|
S: AsRef<str>,
|
||||||
{
|
{
|
||||||
#[cfg(target_arch = "wasm32")]
|
self.call_json_endpoint(reqwest::Method::PATCH, endpoint, Some(json_body))
|
||||||
let res = {
|
|
||||||
wasmtimer::tokio::timeout(
|
|
||||||
self.request_timeout,
|
|
||||||
self.reqwest_client
|
|
||||||
.patch(self.base_url.join(endpoint.as_ref())?)
|
|
||||||
.json(json_body)
|
|
||||||
.send(),
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
.map_err(|_timeout| HttpClientError::RequestTimeout)??
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
let res = {
|
|
||||||
self.reqwest_client
|
|
||||||
.patch(self.base_url.join(endpoint.as_ref())?)
|
|
||||||
.json(json_body)
|
|
||||||
.send()
|
|
||||||
.await?
|
|
||||||
};
|
|
||||||
|
|
||||||
parse_response(res, true).await
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -612,6 +571,19 @@ pub trait ApiClient {
|
|||||||
V: AsRef<str> + Sync,
|
V: AsRef<str> + Sync,
|
||||||
E: Display + DeserializeOwned;
|
E: Display + DeserializeOwned;
|
||||||
|
|
||||||
|
async fn patch_json<B, T, K, V, E>(
|
||||||
|
&self,
|
||||||
|
path: PathSegments<'_>,
|
||||||
|
params: Params<'_, K, V>,
|
||||||
|
json_body: &B,
|
||||||
|
) -> Result<T, HttpClientError<E>>
|
||||||
|
where
|
||||||
|
B: Serialize + ?Sized + Sync,
|
||||||
|
for<'a> T: Deserialize<'a>,
|
||||||
|
K: AsRef<str> + Sync,
|
||||||
|
V: AsRef<str> + Sync,
|
||||||
|
E: Display + DeserializeOwned;
|
||||||
|
|
||||||
/// `get` json data from the provided absolute endpoint, i.e. for example `"/api/v1/mixnodes?since=12345"`
|
/// `get` json data from the provided absolute endpoint, i.e. for example `"/api/v1/mixnodes?since=12345"`
|
||||||
async fn get_json_from<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
|
async fn get_json_from<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
|
||||||
where
|
where
|
||||||
@@ -635,6 +607,17 @@ pub trait ApiClient {
|
|||||||
for<'a> T: Deserialize<'a>,
|
for<'a> T: Deserialize<'a>,
|
||||||
E: Display + DeserializeOwned,
|
E: Display + DeserializeOwned,
|
||||||
S: AsRef<str> + Sync + Send;
|
S: AsRef<str> + Sync + Send;
|
||||||
|
|
||||||
|
async fn patch_json_data_at<B, T, S, E>(
|
||||||
|
&self,
|
||||||
|
endpoint: S,
|
||||||
|
json_body: &B,
|
||||||
|
) -> Result<T, HttpClientError<E>>
|
||||||
|
where
|
||||||
|
B: Serialize + ?Sized + Sync,
|
||||||
|
for<'a> T: Deserialize<'a>,
|
||||||
|
E: Display + DeserializeOwned,
|
||||||
|
S: AsRef<str> + Sync + Send;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||||
@@ -684,6 +667,22 @@ impl ApiClient for Client {
|
|||||||
self.delete_json(path, params).await
|
self.delete_json(path, params).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn patch_json<B, T, K, V, E>(
|
||||||
|
&self,
|
||||||
|
path: PathSegments<'_>,
|
||||||
|
params: Params<'_, K, V>,
|
||||||
|
json_body: &B,
|
||||||
|
) -> Result<T, HttpClientError<E>>
|
||||||
|
where
|
||||||
|
B: Serialize + ?Sized + Sync,
|
||||||
|
for<'a> T: Deserialize<'a>,
|
||||||
|
K: AsRef<str> + Sync,
|
||||||
|
V: AsRef<str> + Sync,
|
||||||
|
E: Display + DeserializeOwned,
|
||||||
|
{
|
||||||
|
self.patch_json(path, params, json_body).await
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_json_from<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
|
async fn get_json_from<T, S, E>(&self, endpoint: S) -> Result<T, HttpClientError<E>>
|
||||||
where
|
where
|
||||||
for<'a> T: Deserialize<'a>,
|
for<'a> T: Deserialize<'a>,
|
||||||
@@ -715,6 +714,20 @@ impl ApiClient for Client {
|
|||||||
{
|
{
|
||||||
self.delete_json_endpoint(endpoint).await
|
self.delete_json_endpoint(endpoint).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn patch_json_data_at<B, T, S, E>(
|
||||||
|
&self,
|
||||||
|
endpoint: S,
|
||||||
|
json_body: &B,
|
||||||
|
) -> Result<T, HttpClientError<E>>
|
||||||
|
where
|
||||||
|
B: Serialize + ?Sized + Sync,
|
||||||
|
for<'a> T: Deserialize<'a>,
|
||||||
|
E: Display + DeserializeOwned,
|
||||||
|
S: AsRef<str> + Sync + Send,
|
||||||
|
{
|
||||||
|
self.patch_json_endpoint(endpoint, json_body).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// utility function that should solve the double slash problem in API urls forever.
|
// utility function that should solve the double slash problem in API urls forever.
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ use serde::{Deserialize, Serialize};
|
|||||||
pub mod middleware;
|
pub mod middleware;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
|
||||||
pub enum FormattedResponse<T> {
|
pub enum FormattedResponse<T> {
|
||||||
Json(Json<T>),
|
Json(Json<T>),
|
||||||
Yaml(Yaml<T>),
|
Yaml(Yaml<T>),
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ impl From<v6::request::StaticConnectRequest> for v7::request::StaticConnectReque
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
impl From<v6::request::DynamicConnectRequest> for v7::request::DynamicConnectRequest {
|
impl From<v6::request::DynamicConnectRequest> for v7::request::DynamicConnectRequest {
|
||||||
fn from(dynamic_connect_request: v6::request::DynamicConnectRequest) -> Self {
|
fn from(dynamic_connect_request: v6::request::DynamicConnectRequest) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ impl IpPacketRequest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
pub fn new_dynamic_connect_request(
|
pub fn new_dynamic_connect_request(
|
||||||
reply_to: Recipient,
|
reply_to: Recipient,
|
||||||
reply_to_hops: Option<u8>,
|
reply_to_hops: Option<u8>,
|
||||||
@@ -285,6 +286,9 @@ pub struct DynamicConnectRequest {
|
|||||||
|
|
||||||
// The number of mix node hops that responses should take, in addition to the entry and exit
|
// The number of mix node hops that responses should take, in addition to the entry and exit
|
||||||
// node. Zero means only client -> entry -> exit -> client.
|
// node. Zero means only client -> entry -> exit -> client.
|
||||||
|
#[deprecated(
|
||||||
|
note = "clients can no longer control number of hops to use. this field is scheduled for removal in V8"
|
||||||
|
)]
|
||||||
pub reply_to_hops: Option<u8>,
|
pub reply_to_hops: Option<u8>,
|
||||||
|
|
||||||
// The average delay at each mix node, in milliseconds. Currently this is not supported by the
|
// The average delay at each mix node, in milliseconds. Currently this is not supported by the
|
||||||
|
|||||||
@@ -2,10 +2,9 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::error::NetworkTestingError;
|
use crate::error::NetworkTestingError;
|
||||||
use crate::node::TestableNode;
|
use crate::node::{NodeType, TestableNode};
|
||||||
use crate::NodeId;
|
|
||||||
use nym_sphinx::message::NymMessage;
|
use nym_sphinx::message::NymMessage;
|
||||||
use nym_topology::{gateway, mix};
|
use nym_topology::node::RoutingNode;
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
@@ -26,73 +25,76 @@ pub struct TestMessage<T = Empty> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T> TestMessage<T> {
|
impl<T> TestMessage<T> {
|
||||||
pub fn new<N: Into<TestableNode>>(node: N, msg_id: u32, total_msgs: u32, ext: T) -> Self {
|
pub fn new(tested_node: TestableNode, msg_id: u32, total_msgs: u32, ext: T) -> Self {
|
||||||
TestMessage {
|
TestMessage {
|
||||||
tested_node: node.into(),
|
tested_node,
|
||||||
msg_id,
|
msg_id,
|
||||||
total_msgs,
|
total_msgs,
|
||||||
ext,
|
ext,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_mix(node: &mix::LegacyNode, msg_id: u32, total_msgs: u32, ext: T) -> Self {
|
pub fn new_mix(node: &RoutingNode, msg_id: u32, total_msgs: u32, ext: T) -> Self {
|
||||||
Self::new(node, msg_id, total_msgs, ext)
|
Self::new(
|
||||||
|
TestableNode::new_routing(node, NodeType::Mixnode),
|
||||||
|
msg_id,
|
||||||
|
total_msgs,
|
||||||
|
ext,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn new_gateway(node: &gateway::Node, msg_id: u32, total_msgs: u32, ext: T) -> Self {
|
pub fn new_gateway(node: &RoutingNode, msg_id: u32, total_msgs: u32, ext: T) -> Self {
|
||||||
// Self::new(node, msg_id, total_msgs, ext)
|
Self::new(
|
||||||
// }
|
TestableNode::new_routing(node, NodeType::Gateway),
|
||||||
|
msg_id,
|
||||||
pub fn new_serialized<N>(
|
total_msgs,
|
||||||
node: N,
|
ext,
|
||||||
msg_id: u32,
|
)
|
||||||
total_msgs: u32,
|
|
||||||
ext: T,
|
|
||||||
) -> Result<Vec<u8>, NetworkTestingError>
|
|
||||||
where
|
|
||||||
N: Into<TestableNode>,
|
|
||||||
T: Serialize,
|
|
||||||
{
|
|
||||||
Self::new(node, msg_id, total_msgs, ext).as_bytes()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_plaintexts<N>(
|
pub fn new_plaintexts(
|
||||||
node: &N,
|
node: TestableNode,
|
||||||
total_msgs: u32,
|
total_msgs: u32,
|
||||||
ext: T,
|
ext: T,
|
||||||
) -> Result<Vec<Vec<u8>>, NetworkTestingError>
|
) -> Result<Vec<Vec<u8>>, NetworkTestingError>
|
||||||
where
|
where
|
||||||
for<'a> &'a N: Into<TestableNode>,
|
|
||||||
T: Serialize + Clone,
|
T: Serialize + Clone,
|
||||||
{
|
{
|
||||||
let mut msgs = Vec::with_capacity(total_msgs as usize);
|
let mut msgs = Vec::with_capacity(total_msgs as usize);
|
||||||
for msg_id in 1..=total_msgs {
|
for msg_id in 1..=total_msgs {
|
||||||
msgs.push(Self::new(node, msg_id, total_msgs, ext.clone()).as_bytes()?)
|
msgs.push(Self::new(node.clone(), msg_id, total_msgs, ext.clone()).as_bytes()?)
|
||||||
}
|
}
|
||||||
Ok(msgs)
|
Ok(msgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mix_plaintexts(
|
pub fn mix_plaintexts(
|
||||||
node: &mix::LegacyNode,
|
node: &RoutingNode,
|
||||||
total_msgs: u32,
|
total_msgs: u32,
|
||||||
ext: T,
|
ext: T,
|
||||||
) -> Result<Vec<Vec<u8>>, NetworkTestingError>
|
) -> Result<Vec<Vec<u8>>, NetworkTestingError>
|
||||||
where
|
where
|
||||||
T: Serialize + Clone,
|
T: Serialize + Clone,
|
||||||
{
|
{
|
||||||
Self::new_plaintexts(node, total_msgs, ext)
|
Self::new_plaintexts(
|
||||||
|
TestableNode::new_routing(node, NodeType::Mixnode),
|
||||||
|
total_msgs,
|
||||||
|
ext,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn legacy_gateway_plaintexts(
|
pub fn legacy_gateway_plaintexts(
|
||||||
node: &gateway::LegacyNode,
|
node: &RoutingNode,
|
||||||
node_id: NodeId,
|
|
||||||
total_msgs: u32,
|
total_msgs: u32,
|
||||||
ext: T,
|
ext: T,
|
||||||
) -> Result<Vec<Vec<u8>>, NetworkTestingError>
|
) -> Result<Vec<Vec<u8>>, NetworkTestingError>
|
||||||
where
|
where
|
||||||
T: Serialize + Clone,
|
T: Serialize + Clone,
|
||||||
{
|
{
|
||||||
Self::new_plaintexts(&(node, node_id), total_msgs, ext)
|
Self::new_plaintexts(
|
||||||
|
TestableNode::new_routing(node, NodeType::Gateway),
|
||||||
|
total_msgs,
|
||||||
|
ext,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_json_string(&self) -> Result<String, NetworkTestingError>
|
pub fn as_json_string(&self) -> Result<String, NetworkTestingError>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::NodeId;
|
use crate::NodeId;
|
||||||
use nym_topology::{gateway, mix};
|
use nym_topology::node::RoutingNode;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
@@ -24,6 +24,14 @@ impl TestableNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_routing(routing_node: &RoutingNode, typ: NodeType) -> Self {
|
||||||
|
TestableNode::new(
|
||||||
|
routing_node.identity_key.to_base58_string(),
|
||||||
|
typ,
|
||||||
|
routing_node.node_id,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new_mixnode(encoded_identity: String, node_id: NodeId) -> Self {
|
pub fn new_mixnode(encoded_identity: String, node_id: NodeId) -> Self {
|
||||||
TestableNode::new(encoded_identity, NodeType::Mixnode, node_id)
|
TestableNode::new(encoded_identity, NodeType::Mixnode, node_id)
|
||||||
}
|
}
|
||||||
@@ -37,38 +45,6 @@ impl TestableNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a mix::LegacyNode> for TestableNode {
|
|
||||||
fn from(value: &'a mix::LegacyNode) -> Self {
|
|
||||||
TestableNode {
|
|
||||||
encoded_identity: value.identity_key.to_base58_string(),
|
|
||||||
typ: NodeType::Mixnode,
|
|
||||||
node_id: value.mix_id,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<(&'a gateway::LegacyNode, NodeId)> for TestableNode {
|
|
||||||
fn from((gateway, node_id): (&'a gateway::LegacyNode, NodeId)) -> Self {
|
|
||||||
(&(gateway, node_id)).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a (gateway::LegacyNode, NodeId)> for TestableNode {
|
|
||||||
fn from((gateway, node_id): &'a (gateway::LegacyNode, NodeId)) -> Self {
|
|
||||||
(gateway, *node_id).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b> From<&'a (&'b gateway::LegacyNode, NodeId)> for TestableNode {
|
|
||||||
fn from((gateway, node_id): &'a (&'b gateway::LegacyNode, NodeId)) -> Self {
|
|
||||||
TestableNode {
|
|
||||||
encoded_identity: gateway.identity_key.to_base58_string(),
|
|
||||||
typ: NodeType::Gateway,
|
|
||||||
node_id: *node_id,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for TestableNode {
|
impl Display for TestableNode {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
|
|||||||
@@ -2,21 +2,22 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::error::NetworkTestingError;
|
use crate::error::NetworkTestingError;
|
||||||
use crate::Empty;
|
|
||||||
use crate::NodeId;
|
|
||||||
use crate::TestMessage;
|
use crate::TestMessage;
|
||||||
use nym_sphinx::acknowledgements::AckKey;
|
use nym_sphinx::acknowledgements::AckKey;
|
||||||
use nym_sphinx::addressing::clients::Recipient;
|
use nym_sphinx::addressing::clients::Recipient;
|
||||||
use nym_sphinx::message::NymMessage;
|
use nym_sphinx::message::NymMessage;
|
||||||
use nym_sphinx::params::{PacketSize, DEFAULT_NUM_MIX_HOPS};
|
use nym_sphinx::params::PacketSize;
|
||||||
use nym_sphinx::preparer::{FragmentPreparer, PreparedFragment};
|
use nym_sphinx::preparer::{FragmentPreparer, PreparedFragment};
|
||||||
use nym_sphinx_params::PacketType;
|
use nym_sphinx_params::PacketType;
|
||||||
use nym_topology::{gateway, mix, NymTopology};
|
use nym_topology::node::RoutingNode;
|
||||||
|
use nym_topology::{NymRouteProvider, NymTopology, Role};
|
||||||
use rand::{CryptoRng, Rng};
|
use rand::{CryptoRng, Rng};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
pub use nym_topology::node::LegacyMixLayer;
|
||||||
|
|
||||||
pub struct NodeTester<R> {
|
pub struct NodeTester<R> {
|
||||||
rng: R,
|
rng: R,
|
||||||
|
|
||||||
@@ -38,10 +39,6 @@ pub struct NodeTester<R> {
|
|||||||
/// Average delay an acknowledgement packet is going to get delay at a single mixnode.
|
/// Average delay an acknowledgement packet is going to get delay at a single mixnode.
|
||||||
average_ack_delay: Duration,
|
average_ack_delay: Duration,
|
||||||
|
|
||||||
/// Number of mix hops each packet ('real' message, ack, reply) is expected to take.
|
|
||||||
/// Note that it does not include gateway hops.
|
|
||||||
num_mix_hops: u8,
|
|
||||||
|
|
||||||
// while acks are going to be ignored they still need to be constructed
|
// while acks are going to be ignored they still need to be constructed
|
||||||
// so that the gateway would be able to correctly process and forward the message
|
// so that the gateway would be able to correctly process and forward the message
|
||||||
ack_key: Arc<AckKey>,
|
ack_key: Arc<AckKey>,
|
||||||
@@ -70,41 +67,27 @@ where
|
|||||||
deterministic_route_selection,
|
deterministic_route_selection,
|
||||||
average_packet_delay,
|
average_packet_delay,
|
||||||
average_ack_delay,
|
average_ack_delay,
|
||||||
num_mix_hops: DEFAULT_NUM_MIX_HOPS,
|
|
||||||
ack_key,
|
ack_key,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allows setting non-default number of expected mix hops in the network.
|
pub fn testable_mix_topology(&self, layer: LegacyMixLayer, node: &RoutingNode) -> NymTopology {
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn with_mix_hops(mut self, hops: u8) -> Self {
|
|
||||||
self.num_mix_hops = hops;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn testable_mix_topology(&self, node: &mix::LegacyNode) -> NymTopology {
|
|
||||||
let mut topology = self.base_topology.clone();
|
let mut topology = self.base_topology.clone();
|
||||||
topology.set_mixes_in_layer(node.layer as u8, vec![node.clone()]);
|
topology.set_testable_node(layer.into(), node.clone());
|
||||||
topology
|
topology
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn testable_gateway_topology(&self, gateway: &gateway::LegacyNode) -> NymTopology {
|
pub fn testable_gateway_topology(&self, node: &RoutingNode) -> NymTopology {
|
||||||
let mut topology = self.base_topology.clone();
|
let mut topology = self.base_topology.clone();
|
||||||
topology.set_gateways(vec![gateway.clone()]);
|
topology.set_testable_node(Role::EntryGateway, node.clone());
|
||||||
|
topology.set_testable_node(Role::ExitGateway, node.clone());
|
||||||
topology
|
topology
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn simple_mixnode_test_packets(
|
|
||||||
&mut self,
|
|
||||||
mix: &mix::LegacyNode,
|
|
||||||
test_packets: u32,
|
|
||||||
) -> Result<Vec<PreparedFragment>, NetworkTestingError> {
|
|
||||||
self.mixnode_test_packets(mix, Empty, test_packets, None)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mixnode_test_packets<T>(
|
pub fn mixnode_test_packets<T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
mix: &mix::LegacyNode,
|
mix: &RoutingNode,
|
||||||
|
legacy_mix_layer: LegacyMixLayer,
|
||||||
msg_ext: T,
|
msg_ext: T,
|
||||||
test_packets: u32,
|
test_packets: u32,
|
||||||
custom_recipient: Option<Recipient>,
|
custom_recipient: Option<Recipient>,
|
||||||
@@ -112,7 +95,9 @@ where
|
|||||||
where
|
where
|
||||||
T: Serialize + Clone,
|
T: Serialize + Clone,
|
||||||
{
|
{
|
||||||
let ephemeral_topology = self.testable_mix_topology(mix);
|
let ephemeral_topology =
|
||||||
|
NymRouteProvider::from(self.testable_mix_topology(legacy_mix_layer, mix))
|
||||||
|
.with_ignore_egress_epoch_roles(true);
|
||||||
|
|
||||||
let mut packets = Vec::with_capacity(test_packets as usize);
|
let mut packets = Vec::with_capacity(test_packets as usize);
|
||||||
for plaintext in TestMessage::mix_plaintexts(mix, test_packets, msg_ext)? {
|
for plaintext in TestMessage::mix_plaintexts(mix, test_packets, msg_ext)? {
|
||||||
@@ -128,7 +113,7 @@ where
|
|||||||
|
|
||||||
pub fn mixnodes_test_packets<T>(
|
pub fn mixnodes_test_packets<T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
nodes: &[mix::LegacyNode],
|
nodes: &[(LegacyMixLayer, RoutingNode)],
|
||||||
msg_ext: T,
|
msg_ext: T,
|
||||||
test_packets: u32,
|
test_packets: u32,
|
||||||
custom_recipient: Option<Recipient>,
|
custom_recipient: Option<Recipient>,
|
||||||
@@ -137,9 +122,10 @@ where
|
|||||||
T: Serialize + Clone,
|
T: Serialize + Clone,
|
||||||
{
|
{
|
||||||
let mut packets = Vec::new();
|
let mut packets = Vec::new();
|
||||||
for node in nodes {
|
for (layer, node) in nodes {
|
||||||
packets.append(&mut self.mixnode_test_packets(
|
packets.append(&mut self.mixnode_test_packets(
|
||||||
node,
|
node,
|
||||||
|
*layer,
|
||||||
msg_ext.clone(),
|
msg_ext.clone(),
|
||||||
test_packets,
|
test_packets,
|
||||||
custom_recipient,
|
custom_recipient,
|
||||||
@@ -149,26 +135,10 @@ where
|
|||||||
Ok(packets)
|
Ok(packets)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn existing_mixnode_test_packets<T>(
|
|
||||||
&mut self,
|
|
||||||
mix_id: NodeId,
|
|
||||||
msg_ext: T,
|
|
||||||
test_packets: u32,
|
|
||||||
custom_recipient: Option<Recipient>,
|
|
||||||
) -> Result<Vec<PreparedFragment>, NetworkTestingError>
|
|
||||||
where
|
|
||||||
T: Serialize + Clone,
|
|
||||||
{
|
|
||||||
let Some(node) = self.base_topology.find_mix(mix_id) else {
|
|
||||||
return Err(NetworkTestingError::NonExistentMixnode { mix_id });
|
|
||||||
};
|
|
||||||
|
|
||||||
self.mixnode_test_packets(&node.clone(), msg_ext, test_packets, custom_recipient)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn existing_identity_mixnode_test_packets<T>(
|
pub fn existing_identity_mixnode_test_packets<T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
encoded_mix_identity: String,
|
encoded_mix_identity: String,
|
||||||
|
layer: LegacyMixLayer,
|
||||||
msg_ext: T,
|
msg_ext: T,
|
||||||
test_packets: u32,
|
test_packets: u32,
|
||||||
custom_recipient: Option<Recipient>,
|
custom_recipient: Option<Recipient>,
|
||||||
@@ -176,22 +146,30 @@ where
|
|||||||
where
|
where
|
||||||
T: Serialize + Clone,
|
T: Serialize + Clone,
|
||||||
{
|
{
|
||||||
let Some(node) = self
|
let Ok(identity) = encoded_mix_identity.parse() else {
|
||||||
.base_topology
|
|
||||||
.find_mix_by_identity(&encoded_mix_identity)
|
|
||||||
else {
|
|
||||||
return Err(NetworkTestingError::NonExistentMixnodeIdentity {
|
return Err(NetworkTestingError::NonExistentMixnodeIdentity {
|
||||||
mix_identity: encoded_mix_identity,
|
mix_identity: encoded_mix_identity,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
self.mixnode_test_packets(&node.clone(), msg_ext, test_packets, custom_recipient)
|
let Some(node) = self.base_topology.find_node_by_identity(identity) else {
|
||||||
|
return Err(NetworkTestingError::NonExistentMixnodeIdentity {
|
||||||
|
mix_identity: encoded_mix_identity,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
self.mixnode_test_packets(
|
||||||
|
&node.clone(),
|
||||||
|
layer,
|
||||||
|
msg_ext,
|
||||||
|
test_packets,
|
||||||
|
custom_recipient,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn legacy_gateway_test_packets<T>(
|
pub fn legacy_gateway_test_packets<T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
gateway: &gateway::LegacyNode,
|
gateway: &RoutingNode,
|
||||||
node_id: NodeId,
|
|
||||||
msg_ext: T,
|
msg_ext: T,
|
||||||
test_packets: u32,
|
test_packets: u32,
|
||||||
custom_recipient: Option<Recipient>,
|
custom_recipient: Option<Recipient>,
|
||||||
@@ -199,12 +177,11 @@ where
|
|||||||
where
|
where
|
||||||
T: Serialize + Clone,
|
T: Serialize + Clone,
|
||||||
{
|
{
|
||||||
let ephemeral_topology = self.testable_gateway_topology(gateway);
|
let ephemeral_topology = NymRouteProvider::from(self.testable_gateway_topology(gateway))
|
||||||
|
.with_ignore_egress_epoch_roles(true);
|
||||||
|
|
||||||
let mut packets = Vec::with_capacity(test_packets as usize);
|
let mut packets = Vec::with_capacity(test_packets as usize);
|
||||||
for plaintext in
|
for plaintext in TestMessage::legacy_gateway_plaintexts(gateway, test_packets, msg_ext)? {
|
||||||
TestMessage::legacy_gateway_plaintexts(gateway, node_id, test_packets, msg_ext)?
|
|
||||||
{
|
|
||||||
packets.push(self.wrap_plaintext_data(
|
packets.push(self.wrap_plaintext_data(
|
||||||
plaintext,
|
plaintext,
|
||||||
&ephemeral_topology,
|
&ephemeral_topology,
|
||||||
@@ -215,36 +192,10 @@ where
|
|||||||
Ok(packets)
|
Ok(packets)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn existing_gateway_test_packets<T>(
|
|
||||||
&mut self,
|
|
||||||
node_id: NodeId,
|
|
||||||
encoded_gateway_identity: String,
|
|
||||||
msg_ext: T,
|
|
||||||
test_packets: u32,
|
|
||||||
custom_recipient: Option<Recipient>,
|
|
||||||
) -> Result<Vec<PreparedFragment>, NetworkTestingError>
|
|
||||||
where
|
|
||||||
T: Serialize + Clone,
|
|
||||||
{
|
|
||||||
let Some(node) = self.base_topology.find_gateway(&encoded_gateway_identity) else {
|
|
||||||
return Err(NetworkTestingError::NonExistentGateway {
|
|
||||||
gateway_identity: encoded_gateway_identity,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
self.legacy_gateway_test_packets(
|
|
||||||
&node.clone(),
|
|
||||||
node_id,
|
|
||||||
msg_ext,
|
|
||||||
test_packets,
|
|
||||||
custom_recipient,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn wrap_plaintext_data(
|
pub fn wrap_plaintext_data(
|
||||||
&mut self,
|
&mut self,
|
||||||
plaintext: Vec<u8>,
|
plaintext: Vec<u8>,
|
||||||
topology: &NymTopology,
|
topology: &NymRouteProvider,
|
||||||
custom_recipient: Option<Recipient>,
|
custom_recipient: Option<Recipient>,
|
||||||
) -> Result<PreparedFragment, NetworkTestingError> {
|
) -> Result<PreparedFragment, NetworkTestingError> {
|
||||||
let message = NymMessage::new_plain(plaintext);
|
let message = NymMessage::new_plain(plaintext);
|
||||||
@@ -274,14 +225,13 @@ where
|
|||||||
&address,
|
&address,
|
||||||
&address,
|
&address,
|
||||||
PacketType::Mix,
|
PacketType::Mix,
|
||||||
None,
|
|
||||||
)?)
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_test_packet<T>(
|
pub fn create_test_packet<T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
message: &TestMessage<T>,
|
message: &TestMessage<T>,
|
||||||
topology: &NymTopology,
|
topology: &NymRouteProvider,
|
||||||
custom_recipient: Option<Recipient>,
|
custom_recipient: Option<Recipient>,
|
||||||
) -> Result<PreparedFragment, NetworkTestingError>
|
) -> Result<PreparedFragment, NetworkTestingError>
|
||||||
where
|
where
|
||||||
@@ -307,10 +257,6 @@ impl<R: CryptoRng + Rng> FragmentPreparer for NodeTester<R> {
|
|||||||
1
|
1
|
||||||
}
|
}
|
||||||
|
|
||||||
fn num_mix_hops(&self) -> u8 {
|
|
||||||
self.num_mix_hops
|
|
||||||
}
|
|
||||||
|
|
||||||
fn average_packet_delay(&self) -> Duration {
|
fn average_packet_delay(&self) -> Duration {
|
||||||
self.average_packet_delay
|
self.average_packet_delay
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,4 +63,4 @@ par_signing = ["rayon"]
|
|||||||
# but given it's not done very frequently, it shouldn't be too much of a problem
|
# but given it's not done very frequently, it shouldn't be too much of a problem
|
||||||
# furthermore, we can't and shouldn't dedicate the entire nym-api CPU just for verification,
|
# furthermore, we can't and shouldn't dedicate the entire nym-api CPU just for verification,
|
||||||
# but this feature might potentially be desirable for clients.
|
# but this feature might potentially be desirable for clients.
|
||||||
par_verify = ["rayon"]
|
par_verify = ["rayon"]
|
||||||
|
|||||||
@@ -4,10 +4,10 @@
|
|||||||
use crate::ecash_group_parameters;
|
use crate::ecash_group_parameters;
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::helpers::{g1_tuple_to_bytes, recover_g1_tuple};
|
use crate::helpers::{g1_tuple_to_bytes, recover_g1_tuple};
|
||||||
use bls12_381::{G1Projective, Scalar};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use subtle::Choice;
|
use subtle::Choice;
|
||||||
|
|
||||||
|
pub use bls12_381::{G1Projective, G2Projective, Scalar};
|
||||||
pub type SignerIndex = u64;
|
pub type SignerIndex = u64;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ pub mod common_types;
|
|||||||
pub mod constants;
|
pub mod constants;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
mod helpers;
|
mod helpers;
|
||||||
mod proofs;
|
pub mod proofs;
|
||||||
pub mod scheme;
|
pub mod scheme;
|
||||||
pub mod tests;
|
pub mod tests;
|
||||||
mod traits;
|
mod traits;
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ use nym_sphinx_addressing::nodes::{
|
|||||||
NymNodeRoutingAddress, NymNodeRoutingAddressError, MAX_NODE_ADDRESS_UNPADDED_LEN,
|
NymNodeRoutingAddress, NymNodeRoutingAddressError, MAX_NODE_ADDRESS_UNPADDED_LEN,
|
||||||
};
|
};
|
||||||
use nym_sphinx_params::packet_sizes::PacketSize;
|
use nym_sphinx_params::packet_sizes::PacketSize;
|
||||||
use nym_sphinx_params::{PacketType, DEFAULT_NUM_MIX_HOPS};
|
use nym_sphinx_params::PacketType;
|
||||||
use nym_sphinx_types::delays::Delay;
|
use nym_sphinx_types::delays::Delay;
|
||||||
use nym_sphinx_types::{NymPacket, NymPacketError, MIN_PACKET_SIZE};
|
use nym_sphinx_types::{NymPacket, NymPacketError, MIN_PACKET_SIZE};
|
||||||
use nym_topology::{NymTopology, NymTopologyError};
|
use nym_topology::{NymRouteProvider, NymTopologyError};
|
||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
|
|
||||||
use std::time;
|
use std::time;
|
||||||
@@ -43,14 +43,13 @@ impl SurbAck {
|
|||||||
ack_key: &AckKey,
|
ack_key: &AckKey,
|
||||||
marshaled_fragment_id: [u8; 5],
|
marshaled_fragment_id: [u8; 5],
|
||||||
average_delay: time::Duration,
|
average_delay: time::Duration,
|
||||||
topology: &NymTopology,
|
topology: &NymRouteProvider,
|
||||||
packet_type: PacketType,
|
packet_type: PacketType,
|
||||||
) -> Result<Self, NymTopologyError>
|
) -> Result<Self, NymTopologyError>
|
||||||
where
|
where
|
||||||
R: RngCore + CryptoRng,
|
R: RngCore + CryptoRng,
|
||||||
{
|
{
|
||||||
let route =
|
let route = topology.random_route_to_egress(rng, recipient.gateway())?;
|
||||||
topology.random_route_to_gateway(rng, DEFAULT_NUM_MIX_HOPS, recipient.gateway())?;
|
|
||||||
let delays = nym_sphinx_routing::generate_hop_delays(average_delay, route.len());
|
let delays = nym_sphinx_routing::generate_hop_delays(average_delay, route.len());
|
||||||
let destination = recipient.as_sphinx_destination();
|
let destination = recipient.as_sphinx_destination();
|
||||||
|
|
||||||
|
|||||||
@@ -131,8 +131,8 @@ impl Recipient {
|
|||||||
&self.client_encryption_key
|
&self.client_encryption_key
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gateway(&self) -> &NodeIdentity {
|
pub fn gateway(&self) -> NodeIdentity {
|
||||||
&self.gateway
|
self.gateway
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_bytes(self) -> RecipientBytes {
|
pub fn to_bytes(self) -> RecipientBytes {
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ use nym_crypto::{generic_array::typenum::Unsigned, Digest};
|
|||||||
use nym_sphinx_addressing::clients::Recipient;
|
use nym_sphinx_addressing::clients::Recipient;
|
||||||
use nym_sphinx_addressing::nodes::{NymNodeRoutingAddress, MAX_NODE_ADDRESS_UNPADDED_LEN};
|
use nym_sphinx_addressing::nodes::{NymNodeRoutingAddress, MAX_NODE_ADDRESS_UNPADDED_LEN};
|
||||||
use nym_sphinx_params::packet_sizes::PacketSize;
|
use nym_sphinx_params::packet_sizes::PacketSize;
|
||||||
use nym_sphinx_params::{PacketType, ReplySurbKeyDigestAlgorithm, DEFAULT_NUM_MIX_HOPS};
|
use nym_sphinx_params::{PacketType, ReplySurbKeyDigestAlgorithm};
|
||||||
use nym_sphinx_types::{NymPacket, SURBMaterial, SphinxError, SURB};
|
use nym_sphinx_types::{NymPacket, SURBMaterial, SphinxError, SURB};
|
||||||
use nym_topology::{NymTopology, NymTopologyError};
|
use nym_topology::{NymRouteProvider, NymTopologyError};
|
||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
use serde::de::{Error as SerdeError, Visitor};
|
use serde::de::{Error as SerdeError, Visitor};
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
@@ -89,13 +89,12 @@ impl ReplySurb {
|
|||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
recipient: &Recipient,
|
recipient: &Recipient,
|
||||||
average_delay: time::Duration,
|
average_delay: time::Duration,
|
||||||
topology: &NymTopology,
|
topology: &NymRouteProvider,
|
||||||
) -> Result<Self, NymTopologyError>
|
) -> Result<Self, NymTopologyError>
|
||||||
where
|
where
|
||||||
R: RngCore + CryptoRng,
|
R: RngCore + CryptoRng,
|
||||||
{
|
{
|
||||||
let route =
|
let route = topology.random_route_to_egress(rng, recipient.gateway())?;
|
||||||
topology.random_route_to_gateway(rng, DEFAULT_NUM_MIX_HOPS, recipient.gateway())?;
|
|
||||||
let delays = nym_sphinx_routing::generate_hop_delays(average_delay, route.len());
|
let delays = nym_sphinx_routing::generate_hop_delays(average_delay, route.len());
|
||||||
let destination = recipient.as_sphinx_destination();
|
let destination = recipient.as_sphinx_destination();
|
||||||
|
|
||||||
@@ -110,15 +109,12 @@ impl ReplySurb {
|
|||||||
|
|
||||||
/// Returns the expected number of bytes the [`ReplySURB`] will take after serialization.
|
/// Returns the expected number of bytes the [`ReplySURB`] will take after serialization.
|
||||||
/// Useful for deserialization from a bytes stream.
|
/// Useful for deserialization from a bytes stream.
|
||||||
pub fn serialized_len(mix_hops: u8) -> usize {
|
pub fn serialized_len() -> usize {
|
||||||
use nym_sphinx_types::{HEADER_SIZE, NODE_ADDRESS_LENGTH, PAYLOAD_KEY_SIZE};
|
use nym_sphinx_types::{HEADER_SIZE, NODE_ADDRESS_LENGTH, PAYLOAD_KEY_SIZE};
|
||||||
|
|
||||||
// the SURB itself consists of SURB_header, first hop address and set of payload keys
|
// the SURB itself consists of SURB_header, first hop address and set of payload keys
|
||||||
// (note extra 1 for the gateway)
|
// for each hop (3x mix + egress)
|
||||||
SurbEncryptionKeySize::USIZE
|
SurbEncryptionKeySize::USIZE + HEADER_SIZE + NODE_ADDRESS_LENGTH + 4 * PAYLOAD_KEY_SIZE
|
||||||
+ HEADER_SIZE
|
|
||||||
+ NODE_ADDRESS_LENGTH
|
|
||||||
+ (1 + mix_hops as usize) * PAYLOAD_KEY_SIZE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encryption_key(&self) -> &SurbEncryptionKey {
|
pub fn encryption_key(&self) -> &SurbEncryptionKey {
|
||||||
|
|||||||
@@ -169,10 +169,7 @@ impl RepliableMessage {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_from_bytes(
|
pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, InvalidReplyRequestError> {
|
||||||
bytes: &[u8],
|
|
||||||
num_mix_hops: u8,
|
|
||||||
) -> Result<Self, InvalidReplyRequestError> {
|
|
||||||
if bytes.len() < SENDER_TAG_SIZE + 1 {
|
if bytes.len() < SENDER_TAG_SIZE + 1 {
|
||||||
return Err(InvalidReplyRequestError::RequestTooShortToDeserialize);
|
return Err(InvalidReplyRequestError::RequestTooShortToDeserialize);
|
||||||
}
|
}
|
||||||
@@ -180,11 +177,8 @@ impl RepliableMessage {
|
|||||||
AnonymousSenderTag::from_bytes(bytes[..SENDER_TAG_SIZE].try_into().unwrap());
|
AnonymousSenderTag::from_bytes(bytes[..SENDER_TAG_SIZE].try_into().unwrap());
|
||||||
let content_tag = RepliableMessageContentTag::try_from(bytes[SENDER_TAG_SIZE])?;
|
let content_tag = RepliableMessageContentTag::try_from(bytes[SENDER_TAG_SIZE])?;
|
||||||
|
|
||||||
let content = RepliableMessageContent::try_from_bytes(
|
let content =
|
||||||
&bytes[SENDER_TAG_SIZE + 1..],
|
RepliableMessageContent::try_from_bytes(&bytes[SENDER_TAG_SIZE + 1..], content_tag)?;
|
||||||
num_mix_hops,
|
|
||||||
content_tag,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(RepliableMessage {
|
Ok(RepliableMessage {
|
||||||
sender_tag,
|
sender_tag,
|
||||||
@@ -192,23 +186,20 @@ impl RepliableMessage {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serialized_size(&self, num_mix_hops: u8) -> usize {
|
pub fn serialized_size(&self) -> usize {
|
||||||
let content_type_size = 1;
|
let content_type_size = 1;
|
||||||
SENDER_TAG_SIZE + content_type_size + self.content.serialized_size(num_mix_hops)
|
SENDER_TAG_SIZE + content_type_size + self.content.serialized_size()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// this recovery code is shared between all variants containing reply surbs
|
// this recovery code is shared between all variants containing reply surbs
|
||||||
fn recover_reply_surbs(
|
fn recover_reply_surbs(bytes: &[u8]) -> Result<(Vec<ReplySurb>, usize), InvalidReplyRequestError> {
|
||||||
bytes: &[u8],
|
|
||||||
num_mix_hops: u8,
|
|
||||||
) -> Result<(Vec<ReplySurb>, usize), InvalidReplyRequestError> {
|
|
||||||
let mut consumed = mem::size_of::<u32>();
|
let mut consumed = mem::size_of::<u32>();
|
||||||
if bytes.len() < consumed {
|
if bytes.len() < consumed {
|
||||||
return Err(InvalidReplyRequestError::RequestTooShortToDeserialize);
|
return Err(InvalidReplyRequestError::RequestTooShortToDeserialize);
|
||||||
}
|
}
|
||||||
let num_surbs = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
|
let num_surbs = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
|
||||||
let surb_size = ReplySurb::serialized_len(num_mix_hops);
|
let surb_size = ReplySurb::serialized_len();
|
||||||
if bytes[consumed..].len() < num_surbs as usize * surb_size {
|
if bytes[consumed..].len() < num_surbs as usize * surb_size {
|
||||||
return Err(InvalidReplyRequestError::RequestTooShortToDeserialize);
|
return Err(InvalidReplyRequestError::RequestTooShortToDeserialize);
|
||||||
}
|
}
|
||||||
@@ -307,14 +298,13 @@ impl RepliableMessageContent {
|
|||||||
|
|
||||||
fn try_from_bytes(
|
fn try_from_bytes(
|
||||||
bytes: &[u8],
|
bytes: &[u8],
|
||||||
num_mix_hops: u8,
|
|
||||||
tag: RepliableMessageContentTag,
|
tag: RepliableMessageContentTag,
|
||||||
) -> Result<Self, InvalidReplyRequestError> {
|
) -> Result<Self, InvalidReplyRequestError> {
|
||||||
if bytes.is_empty() {
|
if bytes.is_empty() {
|
||||||
return Err(InvalidReplyRequestError::RequestTooShortToDeserialize);
|
return Err(InvalidReplyRequestError::RequestTooShortToDeserialize);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (reply_surbs, n) = recover_reply_surbs(bytes, num_mix_hops)?;
|
let (reply_surbs, n) = recover_reply_surbs(bytes)?;
|
||||||
|
|
||||||
match tag {
|
match tag {
|
||||||
RepliableMessageContentTag::Data => Ok(RepliableMessageContent::Data {
|
RepliableMessageContentTag::Data => Ok(RepliableMessageContent::Data {
|
||||||
@@ -340,7 +330,7 @@ impl RepliableMessageContent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialized_size(&self, num_mix_hops: u8) -> usize {
|
fn serialized_size(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
RepliableMessageContent::Data {
|
RepliableMessageContent::Data {
|
||||||
message,
|
message,
|
||||||
@@ -348,19 +338,18 @@ impl RepliableMessageContent {
|
|||||||
} => {
|
} => {
|
||||||
let num_reply_surbs_tag = mem::size_of::<u32>();
|
let num_reply_surbs_tag = mem::size_of::<u32>();
|
||||||
num_reply_surbs_tag
|
num_reply_surbs_tag
|
||||||
+ reply_surbs.len() * ReplySurb::serialized_len(num_mix_hops)
|
+ reply_surbs.len() * ReplySurb::serialized_len()
|
||||||
+ message.len()
|
+ message.len()
|
||||||
}
|
}
|
||||||
RepliableMessageContent::AdditionalSurbs { reply_surbs } => {
|
RepliableMessageContent::AdditionalSurbs { reply_surbs } => {
|
||||||
let num_reply_surbs_tag = mem::size_of::<u32>();
|
let num_reply_surbs_tag = mem::size_of::<u32>();
|
||||||
num_reply_surbs_tag + reply_surbs.len() * ReplySurb::serialized_len(num_mix_hops)
|
num_reply_surbs_tag + reply_surbs.len() * ReplySurb::serialized_len()
|
||||||
}
|
}
|
||||||
RepliableMessageContent::Heartbeat {
|
RepliableMessageContent::Heartbeat {
|
||||||
additional_reply_surbs,
|
additional_reply_surbs,
|
||||||
} => {
|
} => {
|
||||||
let num_reply_surbs_tag = mem::size_of::<u32>();
|
let num_reply_surbs_tag = mem::size_of::<u32>();
|
||||||
num_reply_surbs_tag
|
num_reply_surbs_tag + additional_reply_surbs.len() * ReplySurb::serialized_len()
|
||||||
+ additional_reply_surbs.len() * ReplySurb::serialized_len(num_mix_hops)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -578,11 +567,11 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn reply_surb(rng: &mut ChaCha20Rng, num_mix_hops: u8) -> ReplySurb {
|
pub(super) fn reply_surb(rng: &mut ChaCha20Rng) -> ReplySurb {
|
||||||
// due to gateway
|
// due to gateway
|
||||||
let num_hops = num_mix_hops + 1;
|
const HOPS: u8 = 4;
|
||||||
let route = (0..num_hops).map(|_| node(rng)).collect();
|
let route = (0..HOPS).map(|_| node(rng)).collect();
|
||||||
let delays = (0..num_hops)
|
let delays = (0..HOPS)
|
||||||
.map(|_| Delay::new_from_nanos(rng.next_u64()))
|
.map(|_| Delay::new_from_nanos(rng.next_u64()))
|
||||||
.collect();
|
.collect();
|
||||||
let mut destination_bytes = [0u8; 32];
|
let mut destination_bytes = [0u8; 32];
|
||||||
@@ -605,47 +594,40 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn reply_surbs(
|
pub(super) fn reply_surbs(rng: &mut ChaCha20Rng, n: usize) -> Vec<ReplySurb> {
|
||||||
rng: &mut ChaCha20Rng,
|
|
||||||
num_mix_hops: u8,
|
|
||||||
n: usize,
|
|
||||||
) -> Vec<ReplySurb> {
|
|
||||||
let mut surbs = Vec::with_capacity(n);
|
let mut surbs = Vec::with_capacity(n);
|
||||||
for _ in 0..n {
|
for _ in 0..n {
|
||||||
surbs.push(reply_surb(rng, num_mix_hops))
|
surbs.push(reply_surb(rng))
|
||||||
}
|
}
|
||||||
surbs
|
surbs
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn repliable_content_data(
|
pub(super) fn repliable_content_data(
|
||||||
rng: &mut ChaCha20Rng,
|
rng: &mut ChaCha20Rng,
|
||||||
num_mix_hops: u8,
|
|
||||||
msg_len: usize,
|
msg_len: usize,
|
||||||
surbs: usize,
|
surbs: usize,
|
||||||
) -> RepliableMessageContent {
|
) -> RepliableMessageContent {
|
||||||
RepliableMessageContent::Data {
|
RepliableMessageContent::Data {
|
||||||
message: random_vec_u8(rng, msg_len),
|
message: random_vec_u8(rng, msg_len),
|
||||||
reply_surbs: reply_surbs(rng, num_mix_hops, surbs),
|
reply_surbs: reply_surbs(rng, surbs),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn repliable_content_surbs(
|
pub(super) fn repliable_content_surbs(
|
||||||
rng: &mut ChaCha20Rng,
|
rng: &mut ChaCha20Rng,
|
||||||
num_mix_hops: u8,
|
|
||||||
surbs: usize,
|
surbs: usize,
|
||||||
) -> RepliableMessageContent {
|
) -> RepliableMessageContent {
|
||||||
RepliableMessageContent::AdditionalSurbs {
|
RepliableMessageContent::AdditionalSurbs {
|
||||||
reply_surbs: reply_surbs(rng, num_mix_hops, surbs),
|
reply_surbs: reply_surbs(rng, surbs),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn repliable_content_heartbeat(
|
pub(super) fn repliable_content_heartbeat(
|
||||||
rng: &mut ChaCha20Rng,
|
rng: &mut ChaCha20Rng,
|
||||||
num_mix_hops: u8,
|
|
||||||
surbs: usize,
|
surbs: usize,
|
||||||
) -> RepliableMessageContent {
|
) -> RepliableMessageContent {
|
||||||
RepliableMessageContent::Heartbeat {
|
RepliableMessageContent::Heartbeat {
|
||||||
additional_reply_surbs: reply_surbs(rng, num_mix_hops, surbs),
|
additional_reply_surbs: reply_surbs(rng, surbs),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -676,70 +658,54 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn serialized_size_matches_actual_serialization() {
|
fn serialized_size_matches_actual_serialization() {
|
||||||
let mut rng = fixtures::test_rng();
|
let mut rng = fixtures::test_rng();
|
||||||
let num_mix_hops = 3;
|
|
||||||
|
|
||||||
let data1 = RepliableMessage {
|
let data1 = RepliableMessage {
|
||||||
sender_tag: fixtures::sender_tag(&mut rng),
|
sender_tag: fixtures::sender_tag(&mut rng),
|
||||||
content: fixtures::repliable_content_data(&mut rng, num_mix_hops, 10000, 0),
|
content: fixtures::repliable_content_data(&mut rng, 10000, 0),
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(data1.serialized_size(), data1.into_bytes().len());
|
||||||
data1.serialized_size(num_mix_hops),
|
|
||||||
data1.into_bytes().len()
|
|
||||||
);
|
|
||||||
|
|
||||||
let data2 = RepliableMessage {
|
let data2 = RepliableMessage {
|
||||||
sender_tag: fixtures::sender_tag(&mut rng),
|
sender_tag: fixtures::sender_tag(&mut rng),
|
||||||
content: fixtures::repliable_content_data(&mut rng, num_mix_hops, 10, 100),
|
content: fixtures::repliable_content_data(&mut rng, 10, 100),
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(data2.serialized_size(), data2.into_bytes().len());
|
||||||
data2.serialized_size(num_mix_hops),
|
|
||||||
data2.into_bytes().len()
|
|
||||||
);
|
|
||||||
|
|
||||||
let data3 = RepliableMessage {
|
let data3 = RepliableMessage {
|
||||||
sender_tag: fixtures::sender_tag(&mut rng),
|
sender_tag: fixtures::sender_tag(&mut rng),
|
||||||
content: fixtures::repliable_content_data(&mut rng, num_mix_hops, 100000, 1000),
|
content: fixtures::repliable_content_data(&mut rng, 100000, 1000),
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(data3.serialized_size(), data3.into_bytes().len());
|
||||||
data3.serialized_size(num_mix_hops),
|
|
||||||
data3.into_bytes().len()
|
|
||||||
);
|
|
||||||
|
|
||||||
let additional_surbs1 = RepliableMessage {
|
let additional_surbs1 = RepliableMessage {
|
||||||
sender_tag: fixtures::sender_tag(&mut rng),
|
sender_tag: fixtures::sender_tag(&mut rng),
|
||||||
content: fixtures::repliable_content_surbs(&mut rng, num_mix_hops, 1),
|
content: fixtures::repliable_content_surbs(&mut rng, 1),
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
additional_surbs1.serialized_size(num_mix_hops),
|
additional_surbs1.serialized_size(),
|
||||||
additional_surbs1.into_bytes().len()
|
additional_surbs1.into_bytes().len()
|
||||||
);
|
);
|
||||||
|
|
||||||
let additional_surbs2 = RepliableMessage {
|
let additional_surbs2 = RepliableMessage {
|
||||||
sender_tag: fixtures::sender_tag(&mut rng),
|
sender_tag: fixtures::sender_tag(&mut rng),
|
||||||
content: fixtures::repliable_content_surbs(&mut rng, num_mix_hops, 1000),
|
content: fixtures::repliable_content_surbs(&mut rng, 1000),
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
additional_surbs2.serialized_size(num_mix_hops),
|
additional_surbs2.serialized_size(),
|
||||||
additional_surbs2.into_bytes().len()
|
additional_surbs2.into_bytes().len()
|
||||||
);
|
);
|
||||||
|
|
||||||
let heartbeat1 = RepliableMessage {
|
let heartbeat1 = RepliableMessage {
|
||||||
sender_tag: fixtures::sender_tag(&mut rng),
|
sender_tag: fixtures::sender_tag(&mut rng),
|
||||||
content: fixtures::repliable_content_heartbeat(&mut rng, num_mix_hops, 1),
|
content: fixtures::repliable_content_heartbeat(&mut rng, 1),
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(heartbeat1.serialized_size(), heartbeat1.into_bytes().len());
|
||||||
heartbeat1.serialized_size(num_mix_hops),
|
|
||||||
heartbeat1.into_bytes().len()
|
|
||||||
);
|
|
||||||
|
|
||||||
let heartbeat2 = RepliableMessage {
|
let heartbeat2 = RepliableMessage {
|
||||||
sender_tag: fixtures::sender_tag(&mut rng),
|
sender_tag: fixtures::sender_tag(&mut rng),
|
||||||
content: fixtures::repliable_content_heartbeat(&mut rng, num_mix_hops, 1000),
|
content: fixtures::repliable_content_heartbeat(&mut rng, 1000),
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(heartbeat2.serialized_size(), heartbeat2.into_bytes().len());
|
||||||
heartbeat2.serialized_size(num_mix_hops),
|
|
||||||
heartbeat2.into_bytes().len()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -750,49 +716,33 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn serialized_size_matches_actual_serialization() {
|
fn serialized_size_matches_actual_serialization() {
|
||||||
let mut rng = fixtures::test_rng();
|
let mut rng = fixtures::test_rng();
|
||||||
let num_mix_hops = 3;
|
|
||||||
|
|
||||||
let data1 = fixtures::repliable_content_data(&mut rng, num_mix_hops, 10000, 0);
|
let data1 = fixtures::repliable_content_data(&mut rng, 10000, 0);
|
||||||
assert_eq!(
|
assert_eq!(data1.serialized_size(), data1.into_bytes().len());
|
||||||
data1.serialized_size(num_mix_hops),
|
|
||||||
data1.into_bytes().len()
|
|
||||||
);
|
|
||||||
|
|
||||||
let data2 = fixtures::repliable_content_data(&mut rng, num_mix_hops, 10, 100);
|
let data2 = fixtures::repliable_content_data(&mut rng, 10, 100);
|
||||||
assert_eq!(
|
assert_eq!(data2.serialized_size(), data2.into_bytes().len());
|
||||||
data2.serialized_size(num_mix_hops),
|
|
||||||
data2.into_bytes().len()
|
|
||||||
);
|
|
||||||
|
|
||||||
let data3 = fixtures::repliable_content_data(&mut rng, num_mix_hops, 100000, 1000);
|
let data3 = fixtures::repliable_content_data(&mut rng, 100000, 1000);
|
||||||
assert_eq!(
|
assert_eq!(data3.serialized_size(), data3.into_bytes().len());
|
||||||
data3.serialized_size(num_mix_hops),
|
|
||||||
data3.into_bytes().len()
|
|
||||||
);
|
|
||||||
|
|
||||||
let additional_surbs1 = fixtures::repliable_content_surbs(&mut rng, num_mix_hops, 1);
|
let additional_surbs1 = fixtures::repliable_content_surbs(&mut rng, 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
additional_surbs1.serialized_size(num_mix_hops),
|
additional_surbs1.serialized_size(),
|
||||||
additional_surbs1.into_bytes().len()
|
additional_surbs1.into_bytes().len()
|
||||||
);
|
);
|
||||||
|
|
||||||
let additional_surbs2 = fixtures::repliable_content_surbs(&mut rng, num_mix_hops, 1000);
|
let additional_surbs2 = fixtures::repliable_content_surbs(&mut rng, 1000);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
additional_surbs2.serialized_size(num_mix_hops),
|
additional_surbs2.serialized_size(),
|
||||||
additional_surbs2.into_bytes().len()
|
additional_surbs2.into_bytes().len()
|
||||||
);
|
);
|
||||||
|
|
||||||
let heartbeat1 = fixtures::repliable_content_heartbeat(&mut rng, num_mix_hops, 1);
|
let heartbeat1 = fixtures::repliable_content_heartbeat(&mut rng, 1);
|
||||||
assert_eq!(
|
assert_eq!(heartbeat1.serialized_size(), heartbeat1.into_bytes().len());
|
||||||
heartbeat1.serialized_size(num_mix_hops),
|
|
||||||
heartbeat1.into_bytes().len()
|
|
||||||
);
|
|
||||||
|
|
||||||
let heartbeat2 = fixtures::repliable_content_heartbeat(&mut rng, num_mix_hops, 1000);
|
let heartbeat2 = fixtures::repliable_content_heartbeat(&mut rng, 1000);
|
||||||
assert_eq!(
|
assert_eq!(heartbeat2.serialized_size(), heartbeat2.into_bytes().len());
|
||||||
heartbeat2.serialized_size(num_mix_hops),
|
|
||||||
heartbeat2.into_bytes().len()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -69,11 +69,11 @@ pub mod monitoring {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fragment_sent(fragment: &Fragment, client_nonce: i32, destination: PublicKey, hops: u8) {
|
pub fn fragment_sent(fragment: &Fragment, client_nonce: i32, destination: PublicKey) {
|
||||||
if enabled() {
|
if enabled() {
|
||||||
let id = fragment.fragment_identifier().set_id();
|
let id = fragment.fragment_identifier().set_id();
|
||||||
let mut entry = FRAGMENTS_SENT.entry(id).or_default();
|
let mut entry = FRAGMENTS_SENT.entry(id).or_default();
|
||||||
let s = SentFragment::new(fragment.header(), now!(), client_nonce, destination, hops);
|
let s = SentFragment::new(fragment.header(), now!(), client_nonce, destination);
|
||||||
entry.push(s);
|
entry.push(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,16 +82,11 @@ pub mod monitoring {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct FragmentMixParams {
|
pub struct FragmentMixParams {
|
||||||
destination: PublicKey,
|
destination: PublicKey,
|
||||||
hops: u8,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FragmentMixParams {
|
impl FragmentMixParams {
|
||||||
pub fn destination(&self) -> &PublicKey {
|
pub fn destination(&self) -> PublicKey {
|
||||||
&self.destination
|
self.destination
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hops(&self) -> u8 {
|
|
||||||
self.hops
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,14 +100,8 @@ pub struct SentFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SentFragment {
|
impl SentFragment {
|
||||||
fn new(
|
fn new(header: FragmentHeader, at: u64, client_nonce: i32, destination: PublicKey) -> Self {
|
||||||
header: FragmentHeader,
|
let mixnet_params = FragmentMixParams { destination };
|
||||||
at: u64,
|
|
||||||
client_nonce: i32,
|
|
||||||
destination: PublicKey,
|
|
||||||
hops: u8,
|
|
||||||
) -> Self {
|
|
||||||
let mixnet_params = FragmentMixParams { destination, hops };
|
|
||||||
SentFragment {
|
SentFragment {
|
||||||
header,
|
header,
|
||||||
at,
|
at,
|
||||||
|
|||||||
@@ -10,11 +10,9 @@ use nym_sphinx_addressing::nodes::NymNodeRoutingAddress;
|
|||||||
use nym_sphinx_chunking::fragment::COVER_FRAG_ID;
|
use nym_sphinx_chunking::fragment::COVER_FRAG_ID;
|
||||||
use nym_sphinx_forwarding::packet::MixPacket;
|
use nym_sphinx_forwarding::packet::MixPacket;
|
||||||
use nym_sphinx_params::packet_sizes::PacketSize;
|
use nym_sphinx_params::packet_sizes::PacketSize;
|
||||||
use nym_sphinx_params::{
|
use nym_sphinx_params::{PacketEncryptionAlgorithm, PacketHkdfAlgorithm, PacketType};
|
||||||
PacketEncryptionAlgorithm, PacketHkdfAlgorithm, PacketType, DEFAULT_NUM_MIX_HOPS,
|
|
||||||
};
|
|
||||||
use nym_sphinx_types::NymPacket;
|
use nym_sphinx_types::NymPacket;
|
||||||
use nym_topology::{NymTopology, NymTopologyError};
|
use nym_topology::{NymRouteProvider, NymTopologyError};
|
||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
|
|
||||||
use std::time;
|
use std::time;
|
||||||
@@ -36,7 +34,7 @@ pub enum CoverMessageError {
|
|||||||
|
|
||||||
pub fn generate_loop_cover_surb_ack<R>(
|
pub fn generate_loop_cover_surb_ack<R>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
topology: &NymTopology,
|
topology: &NymRouteProvider,
|
||||||
ack_key: &AckKey,
|
ack_key: &AckKey,
|
||||||
full_address: &Recipient,
|
full_address: &Recipient,
|
||||||
average_ack_delay: time::Duration,
|
average_ack_delay: time::Duration,
|
||||||
@@ -59,7 +57,7 @@ where
|
|||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn generate_loop_cover_packet<R>(
|
pub fn generate_loop_cover_packet<R>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
topology: &NymTopology,
|
topology: &NymRouteProvider,
|
||||||
ack_key: &AckKey,
|
ack_key: &AckKey,
|
||||||
full_address: &Recipient,
|
full_address: &Recipient,
|
||||||
average_ack_delay: time::Duration,
|
average_ack_delay: time::Duration,
|
||||||
@@ -118,8 +116,7 @@ where
|
|||||||
.chain(cover_content)
|
.chain(cover_content)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let route =
|
let route = topology.random_route_to_egress(rng, full_address.gateway())?;
|
||||||
topology.random_route_to_gateway(rng, DEFAULT_NUM_MIX_HOPS, full_address.gateway())?;
|
|
||||||
let delays = nym_sphinx_routing::generate_hop_delays(average_packet_delay, route.len());
|
let delays = nym_sphinx_routing::generate_hop_delays(average_packet_delay, route.len());
|
||||||
let destination = full_address.as_sphinx_destination();
|
let destination = full_address.as_sphinx_destination();
|
||||||
|
|
||||||
|
|||||||
@@ -16,10 +16,6 @@ pub mod packet_sizes;
|
|||||||
pub mod packet_types;
|
pub mod packet_types;
|
||||||
pub mod packet_version;
|
pub mod packet_version;
|
||||||
|
|
||||||
// If somebody can provide an argument why it might be reasonable to have more than 255 mix hops,
|
|
||||||
// I will change this to [`usize`]
|
|
||||||
pub const DEFAULT_NUM_MIX_HOPS: u8 = 3;
|
|
||||||
|
|
||||||
// TODO: not entirely sure how to feel about those being defined here, ideally it'd be where [`Fragment`]
|
// TODO: not entirely sure how to feel about those being defined here, ideally it'd be where [`Fragment`]
|
||||||
// is defined, but that'd introduce circular dependencies as the acknowledgements crate also needs
|
// is defined, but that'd introduce circular dependencies as the acknowledgements crate also needs
|
||||||
// access to that
|
// access to that
|
||||||
|
|||||||
@@ -1,19 +1,10 @@
|
|||||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use nym_sphinx_types::{delays, Delay};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use nym_sphinx_addressing::clients::Recipient;
|
|
||||||
use nym_sphinx_types::{delays, Delay, Node};
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
pub trait SphinxRouteMaker {
|
|
||||||
type Error;
|
|
||||||
|
|
||||||
fn sphinx_route(&mut self, hops: u8, destination: &Recipient)
|
|
||||||
-> Result<Vec<Node>, Self::Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Error, Clone, Copy)]
|
#[derive(Debug, Error, Clone, Copy)]
|
||||||
#[error("the route vector contains {available} nodes while {requested} hops are required")]
|
#[error("the route vector contains {available} nodes while {requested} hops are required")]
|
||||||
pub struct InvalidNumberOfHops {
|
pub struct InvalidNumberOfHops {
|
||||||
@@ -21,29 +12,6 @@ pub struct InvalidNumberOfHops {
|
|||||||
requested: u8,
|
requested: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
// if one wants to provide a hardcoded route, they can
|
|
||||||
impl SphinxRouteMaker for Vec<Node> {
|
|
||||||
type Error = InvalidNumberOfHops;
|
|
||||||
|
|
||||||
fn sphinx_route(
|
|
||||||
&mut self,
|
|
||||||
hops: u8,
|
|
||||||
_destination: &Recipient,
|
|
||||||
) -> Result<Vec<Node>, InvalidNumberOfHops> {
|
|
||||||
// it's the responsibility of the caller to ensure the hardcoded route has correct number of hops
|
|
||||||
// and that it's final hop include the recipient's gateway.
|
|
||||||
|
|
||||||
if self.len() != hops as usize {
|
|
||||||
Err(InvalidNumberOfHops {
|
|
||||||
available: self.len(),
|
|
||||||
requested: hops,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Ok(self.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn generate_hop_delays(average_packet_delay: Duration, num_hops: usize) -> Vec<Delay> {
|
pub fn generate_hop_delays(average_packet_delay: Duration, num_hops: usize) -> Vec<Delay> {
|
||||||
if average_packet_delay.is_zero() {
|
if average_packet_delay.is_zero() {
|
||||||
vec![nym_sphinx_types::Delay::new_from_millis(0); num_hops]
|
vec![nym_sphinx_types::Delay::new_from_millis(0); num_hops]
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ impl NymMessage {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_from_bytes(bytes: &[u8], num_mix_hops: u8) -> Result<Self, NymMessageError> {
|
fn try_from_bytes(bytes: &[u8]) -> Result<Self, NymMessageError> {
|
||||||
if bytes.is_empty() {
|
if bytes.is_empty() {
|
||||||
return Err(NymMessageError::EmptyMessage);
|
return Err(NymMessageError::EmptyMessage);
|
||||||
}
|
}
|
||||||
@@ -158,7 +158,7 @@ impl NymMessage {
|
|||||||
match typ_tag {
|
match typ_tag {
|
||||||
NymMessageType::Plain => Ok(NymMessage::Plain(bytes[1..].to_vec())),
|
NymMessageType::Plain => Ok(NymMessage::Plain(bytes[1..].to_vec())),
|
||||||
NymMessageType::Repliable => Ok(NymMessage::Repliable(
|
NymMessageType::Repliable => Ok(NymMessage::Repliable(
|
||||||
RepliableMessage::try_from_bytes(&bytes[1..], num_mix_hops)?,
|
RepliableMessage::try_from_bytes(&bytes[1..])?,
|
||||||
)),
|
)),
|
||||||
NymMessageType::Reply => Ok(NymMessage::Reply(ReplyMessage::try_from_bytes(
|
NymMessageType::Reply => Ok(NymMessage::Reply(ReplyMessage::try_from_bytes(
|
||||||
&bytes[1..],
|
&bytes[1..],
|
||||||
@@ -166,10 +166,10 @@ impl NymMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialized_size(&self, num_mix_hops: u8) -> usize {
|
fn serialized_size(&self) -> usize {
|
||||||
let inner_size = match self {
|
let inner_size = match self {
|
||||||
NymMessage::Plain(msg) => msg.len(),
|
NymMessage::Plain(msg) => msg.len(),
|
||||||
NymMessage::Repliable(msg) => msg.serialized_size(num_mix_hops),
|
NymMessage::Repliable(msg) => msg.serialized_size(),
|
||||||
NymMessage::Reply(msg) => msg.serialized_size(),
|
NymMessage::Reply(msg) => msg.serialized_size(),
|
||||||
};
|
};
|
||||||
let message_type_size = 1;
|
let message_type_size = 1;
|
||||||
@@ -207,9 +207,9 @@ impl NymMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Determines the number of required packets of the provided size for the split message.
|
/// Determines the number of required packets of the provided size for the split message.
|
||||||
pub fn required_packets(&self, packet_size: PacketSize, num_mix_hops: u8) -> usize {
|
pub fn required_packets(&self, packet_size: PacketSize) -> usize {
|
||||||
let plaintext_per_packet = self.true_available_plaintext_per_packet(packet_size);
|
let plaintext_per_packet = self.true_available_plaintext_per_packet(packet_size);
|
||||||
let serialized_len = self.serialized_size(num_mix_hops);
|
let serialized_len = self.serialized_size();
|
||||||
|
|
||||||
let (num_fragments, _) =
|
let (num_fragments, _) =
|
||||||
chunking::number_of_required_fragments(serialized_len, plaintext_per_packet);
|
chunking::number_of_required_fragments(serialized_len, plaintext_per_packet);
|
||||||
@@ -279,11 +279,11 @@ impl PaddedMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// reverse of NymMessage::pad_to_full_packet_lengths
|
// reverse of NymMessage::pad_to_full_packet_lengths
|
||||||
pub fn remove_padding(self, num_mix_hops: u8) -> Result<NymMessage, NymMessageError> {
|
pub fn remove_padding(self) -> Result<NymMessage, NymMessageError> {
|
||||||
// we are looking for first occurrence of 1 in the tail and we get its index
|
// we are looking for first occurrence of 1 in the tail and we get its index
|
||||||
if let Some(padding_end) = self.0.iter().rposition(|b| *b == 1) {
|
if let Some(padding_end) = self.0.iter().rposition(|b| *b == 1) {
|
||||||
// and now we only take bytes until that point (but not including it)
|
// and now we only take bytes until that point (but not including it)
|
||||||
NymMessage::try_from_bytes(&self.0[..padding_end], num_mix_hops)
|
NymMessage::try_from_bytes(&self.0[..padding_end])
|
||||||
} else {
|
} else {
|
||||||
Err(NymMessageError::InvalidMessagePadding)
|
Err(NymMessageError::InvalidMessagePadding)
|
||||||
}
|
}
|
||||||
@@ -304,7 +304,7 @@ mod tests {
|
|||||||
fn serialized_size_matches_actual_serialization() {
|
fn serialized_size_matches_actual_serialization() {
|
||||||
// plain
|
// plain
|
||||||
let plain = NymMessage::new_plain(vec![1, 2, 3, 4, 5]);
|
let plain = NymMessage::new_plain(vec![1, 2, 3, 4, 5]);
|
||||||
assert_eq!(plain.serialized_size(3), plain.into_bytes().len());
|
assert_eq!(plain.serialized_size(), plain.into_bytes().len());
|
||||||
|
|
||||||
// a single variant for each repliable and reply is enough as they are more thoroughly tested
|
// a single variant for each repliable and reply is enough as they are more thoroughly tested
|
||||||
// internally
|
// internally
|
||||||
@@ -313,9 +313,9 @@ mod tests {
|
|||||||
[42u8; 16].into(),
|
[42u8; 16].into(),
|
||||||
vec![],
|
vec![],
|
||||||
));
|
));
|
||||||
assert_eq!(repliable.serialized_size(3), repliable.into_bytes().len());
|
assert_eq!(repliable.serialized_size(), repliable.into_bytes().len());
|
||||||
|
|
||||||
let reply = NymMessage::new_reply(ReplyMessage::new_data_message(vec![1, 2, 3, 4, 5]));
|
let reply = NymMessage::new_reply(ReplyMessage::new_data_message(vec![1, 2, 3, 4, 5]));
|
||||||
assert_eq!(reply.serialized_size(3), reply.into_bytes().len());
|
assert_eq!(reply.serialized_size(), reply.into_bytes().len());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ use nym_sphinx_anonymous_replies::reply_surb::ReplySurb;
|
|||||||
use nym_sphinx_chunking::fragment::{Fragment, FragmentIdentifier};
|
use nym_sphinx_chunking::fragment::{Fragment, FragmentIdentifier};
|
||||||
use nym_sphinx_forwarding::packet::MixPacket;
|
use nym_sphinx_forwarding::packet::MixPacket;
|
||||||
use nym_sphinx_params::packet_sizes::PacketSize;
|
use nym_sphinx_params::packet_sizes::PacketSize;
|
||||||
use nym_sphinx_params::{PacketType, ReplySurbKeyDigestAlgorithm, DEFAULT_NUM_MIX_HOPS};
|
use nym_sphinx_params::{PacketType, ReplySurbKeyDigestAlgorithm};
|
||||||
use nym_sphinx_types::{Delay, NymPacket};
|
use nym_sphinx_types::{Delay, NymPacket};
|
||||||
use nym_topology::{NymTopology, NymTopologyError};
|
use nym_topology::{NymRouteProvider, NymTopologyError};
|
||||||
use rand::{CryptoRng, Rng, SeedableRng};
|
use rand::{CryptoRng, Rng, SeedableRng};
|
||||||
use rand_chacha::ChaCha8Rng;
|
use rand_chacha::ChaCha8Rng;
|
||||||
|
|
||||||
@@ -54,14 +54,13 @@ pub trait FragmentPreparer {
|
|||||||
fn deterministic_route_selection(&self) -> bool;
|
fn deterministic_route_selection(&self) -> bool;
|
||||||
fn rng(&mut self) -> &mut Self::Rng;
|
fn rng(&mut self) -> &mut Self::Rng;
|
||||||
fn nonce(&self) -> i32;
|
fn nonce(&self) -> i32;
|
||||||
fn num_mix_hops(&self) -> u8;
|
|
||||||
fn average_packet_delay(&self) -> Duration;
|
fn average_packet_delay(&self) -> Duration;
|
||||||
fn average_ack_delay(&self) -> Duration;
|
fn average_ack_delay(&self) -> Duration;
|
||||||
|
|
||||||
fn generate_reply_surbs(
|
fn generate_reply_surbs(
|
||||||
&mut self,
|
&mut self,
|
||||||
amount: usize,
|
amount: usize,
|
||||||
topology: &NymTopology,
|
topology: &NymRouteProvider,
|
||||||
reply_recipient: &Recipient,
|
reply_recipient: &Recipient,
|
||||||
) -> Result<Vec<ReplySurb>, NymTopologyError> {
|
) -> Result<Vec<ReplySurb>, NymTopologyError> {
|
||||||
let mut reply_surbs = Vec::with_capacity(amount);
|
let mut reply_surbs = Vec::with_capacity(amount);
|
||||||
@@ -79,7 +78,7 @@ pub trait FragmentPreparer {
|
|||||||
&mut self,
|
&mut self,
|
||||||
recipient: &Recipient,
|
recipient: &Recipient,
|
||||||
fragment_id: FragmentIdentifier,
|
fragment_id: FragmentIdentifier,
|
||||||
topology: &NymTopology,
|
topology: &NymRouteProvider,
|
||||||
ack_key: &AckKey,
|
ack_key: &AckKey,
|
||||||
packet_type: PacketType,
|
packet_type: PacketType,
|
||||||
) -> Result<SurbAck, NymTopologyError> {
|
) -> Result<SurbAck, NymTopologyError> {
|
||||||
@@ -109,7 +108,7 @@ pub trait FragmentPreparer {
|
|||||||
fn prepare_reply_chunk_for_sending(
|
fn prepare_reply_chunk_for_sending(
|
||||||
&mut self,
|
&mut self,
|
||||||
fragment: Fragment,
|
fragment: Fragment,
|
||||||
topology: &NymTopology,
|
topology: &NymRouteProvider,
|
||||||
ack_key: &AckKey,
|
ack_key: &AckKey,
|
||||||
reply_surb: ReplySurb,
|
reply_surb: ReplySurb,
|
||||||
packet_sender: &Recipient,
|
packet_sender: &Recipient,
|
||||||
@@ -130,9 +129,8 @@ pub trait FragmentPreparer {
|
|||||||
.expect("the message has been incorrectly fragmented");
|
.expect("the message has been incorrectly fragmented");
|
||||||
|
|
||||||
// this is not going to be accurate by any means. but that's the best estimation we can do
|
// this is not going to be accurate by any means. but that's the best estimation we can do
|
||||||
let expected_forward_delay = Delay::new_from_millis(
|
let expected_forward_delay =
|
||||||
(self.average_packet_delay().as_millis() * self.num_mix_hops() as u128) as u64,
|
Delay::new_from_millis((self.average_packet_delay().as_millis() * 3) as u64);
|
||||||
);
|
|
||||||
|
|
||||||
let fragment_identifier = fragment.fragment_identifier();
|
let fragment_identifier = fragment.fragment_identifier();
|
||||||
|
|
||||||
@@ -190,12 +188,11 @@ pub trait FragmentPreparer {
|
|||||||
fn prepare_chunk_for_sending(
|
fn prepare_chunk_for_sending(
|
||||||
&mut self,
|
&mut self,
|
||||||
fragment: Fragment,
|
fragment: Fragment,
|
||||||
topology: &NymTopology,
|
topology: &NymRouteProvider,
|
||||||
ack_key: &AckKey,
|
ack_key: &AckKey,
|
||||||
packet_sender: &Recipient,
|
packet_sender: &Recipient,
|
||||||
packet_recipient: &Recipient,
|
packet_recipient: &Recipient,
|
||||||
packet_type: PacketType,
|
packet_type: PacketType,
|
||||||
mix_hops: Option<u8>,
|
|
||||||
) -> Result<PreparedFragment, NymTopologyError> {
|
) -> Result<PreparedFragment, NymTopologyError> {
|
||||||
debug!("Preparing chunk for sending");
|
debug!("Preparing chunk for sending");
|
||||||
// each plain or repliable packet (i.e. not a reply) attaches an ephemeral public key so that the recipient
|
// each plain or repliable packet (i.e. not a reply) attaches an ephemeral public key so that the recipient
|
||||||
@@ -204,8 +201,7 @@ pub trait FragmentPreparer {
|
|||||||
|
|
||||||
let fragment_header = fragment.header();
|
let fragment_header = fragment.header();
|
||||||
let destination = packet_recipient.gateway();
|
let destination = packet_recipient.gateway();
|
||||||
let hops = mix_hops.unwrap_or(self.num_mix_hops());
|
monitoring::fragment_sent(&fragment, self.nonce(), destination);
|
||||||
monitoring::fragment_sent(&fragment, self.nonce(), *destination, hops);
|
|
||||||
|
|
||||||
let non_reply_overhead = encryption::PUBLIC_KEY_SIZE;
|
let non_reply_overhead = encryption::PUBLIC_KEY_SIZE;
|
||||||
let expected_plaintext = match packet_type {
|
let expected_plaintext = match packet_type {
|
||||||
@@ -240,16 +236,16 @@ pub trait FragmentPreparer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// generate pseudorandom route for the packet
|
// generate pseudorandom route for the packet
|
||||||
log::trace!("Preparing chunk for sending with {hops} mix hops");
|
log::trace!("Preparing chunk for sending");
|
||||||
let route = if self.deterministic_route_selection() {
|
let route = if self.deterministic_route_selection() {
|
||||||
log::trace!("using deterministic route selection");
|
log::trace!("using deterministic route selection");
|
||||||
let seed = fragment_header.seed().wrapping_mul(self.nonce());
|
let seed = fragment_header.seed().wrapping_mul(self.nonce());
|
||||||
let mut rng = ChaCha8Rng::seed_from_u64(seed as u64);
|
let mut rng = ChaCha8Rng::seed_from_u64(seed as u64);
|
||||||
topology.random_route_to_gateway(&mut rng, hops, destination)?
|
topology.random_route_to_egress(&mut rng, destination)?
|
||||||
} else {
|
} else {
|
||||||
log::trace!("using pseudorandom route selection");
|
log::trace!("using pseudorandom route selection");
|
||||||
let mut rng = self.rng();
|
let mut rng = self.rng();
|
||||||
topology.random_route_to_gateway(&mut rng, hops, destination)?
|
topology.random_route_to_egress(&mut rng, destination)?
|
||||||
};
|
};
|
||||||
|
|
||||||
let destination = packet_recipient.as_sphinx_destination();
|
let destination = packet_recipient.as_sphinx_destination();
|
||||||
@@ -335,10 +331,6 @@ pub struct MessagePreparer<R> {
|
|||||||
/// Average delay an acknowledgement packet is going to get delay at a single mixnode.
|
/// Average delay an acknowledgement packet is going to get delay at a single mixnode.
|
||||||
average_ack_delay: Duration,
|
average_ack_delay: Duration,
|
||||||
|
|
||||||
/// Number of mix hops each packet ('real' message, ack, reply) is expected to take.
|
|
||||||
/// Note that it does not include gateway hops.
|
|
||||||
num_mix_hops: u8,
|
|
||||||
|
|
||||||
nonce: i32,
|
nonce: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,17 +353,10 @@ where
|
|||||||
sender_address,
|
sender_address,
|
||||||
average_packet_delay,
|
average_packet_delay,
|
||||||
average_ack_delay,
|
average_ack_delay,
|
||||||
num_mix_hops: DEFAULT_NUM_MIX_HOPS,
|
|
||||||
nonce,
|
nonce,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allows setting non-default number of expected mix hops in the network.
|
|
||||||
pub fn with_mix_hops(mut self, hops: u8) -> Self {
|
|
||||||
self.num_mix_hops = hops;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Overwrites existing sender address with the provided value.
|
/// Overwrites existing sender address with the provided value.
|
||||||
pub fn set_sender_address(&mut self, sender_address: Recipient) {
|
pub fn set_sender_address(&mut self, sender_address: Recipient) {
|
||||||
self.sender_address = sender_address;
|
self.sender_address = sender_address;
|
||||||
@@ -380,7 +365,7 @@ where
|
|||||||
pub fn generate_reply_surbs(
|
pub fn generate_reply_surbs(
|
||||||
&mut self,
|
&mut self,
|
||||||
amount: usize,
|
amount: usize,
|
||||||
topology: &NymTopology,
|
topology: &NymRouteProvider,
|
||||||
) -> Result<Vec<ReplySurb>, NymTopologyError> {
|
) -> Result<Vec<ReplySurb>, NymTopologyError> {
|
||||||
let mut reply_surbs = Vec::with_capacity(amount);
|
let mut reply_surbs = Vec::with_capacity(amount);
|
||||||
for _ in 0..amount {
|
for _ in 0..amount {
|
||||||
@@ -399,7 +384,7 @@ where
|
|||||||
pub fn prepare_reply_chunk_for_sending(
|
pub fn prepare_reply_chunk_for_sending(
|
||||||
&mut self,
|
&mut self,
|
||||||
fragment: Fragment,
|
fragment: Fragment,
|
||||||
topology: &NymTopology,
|
topology: &NymRouteProvider,
|
||||||
ack_key: &AckKey,
|
ack_key: &AckKey,
|
||||||
reply_surb: ReplySurb,
|
reply_surb: ReplySurb,
|
||||||
packet_type: PacketType,
|
packet_type: PacketType,
|
||||||
@@ -420,11 +405,10 @@ where
|
|||||||
pub fn prepare_chunk_for_sending(
|
pub fn prepare_chunk_for_sending(
|
||||||
&mut self,
|
&mut self,
|
||||||
fragment: Fragment,
|
fragment: Fragment,
|
||||||
topology: &NymTopology,
|
topology: &NymRouteProvider,
|
||||||
ack_key: &AckKey,
|
ack_key: &AckKey,
|
||||||
packet_recipient: &Recipient,
|
packet_recipient: &Recipient,
|
||||||
packet_type: PacketType,
|
packet_type: PacketType,
|
||||||
mix_hops: Option<u8>,
|
|
||||||
) -> Result<PreparedFragment, NymTopologyError> {
|
) -> Result<PreparedFragment, NymTopologyError> {
|
||||||
let sender = self.sender_address;
|
let sender = self.sender_address;
|
||||||
|
|
||||||
@@ -436,7 +420,6 @@ where
|
|||||||
&sender,
|
&sender,
|
||||||
packet_recipient,
|
packet_recipient,
|
||||||
packet_type,
|
packet_type,
|
||||||
mix_hops,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -444,7 +427,7 @@ where
|
|||||||
pub fn generate_surb_ack(
|
pub fn generate_surb_ack(
|
||||||
&mut self,
|
&mut self,
|
||||||
fragment_id: FragmentIdentifier,
|
fragment_id: FragmentIdentifier,
|
||||||
topology: &NymTopology,
|
topology: &NymRouteProvider,
|
||||||
ack_key: &AckKey,
|
ack_key: &AckKey,
|
||||||
packet_type: PacketType,
|
packet_type: PacketType,
|
||||||
) -> Result<SurbAck, NymTopologyError> {
|
) -> Result<SurbAck, NymTopologyError> {
|
||||||
@@ -483,10 +466,6 @@ impl<R: CryptoRng + Rng> FragmentPreparer for MessagePreparer<R> {
|
|||||||
self.nonce
|
self.nonce
|
||||||
}
|
}
|
||||||
|
|
||||||
fn num_mix_hops(&self) -> u8 {
|
|
||||||
self.num_mix_hops
|
|
||||||
}
|
|
||||||
|
|
||||||
fn average_packet_delay(&self) -> Duration {
|
fn average_packet_delay(&self) -> Duration {
|
||||||
self.average_packet_delay
|
self.average_packet_delay
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ use nym_sphinx_chunking::reconstruction::MessageReconstructor;
|
|||||||
use nym_sphinx_chunking::ChunkingError;
|
use nym_sphinx_chunking::ChunkingError;
|
||||||
use nym_sphinx_params::{
|
use nym_sphinx_params::{
|
||||||
PacketEncryptionAlgorithm, PacketHkdfAlgorithm, ReplySurbEncryptionAlgorithm,
|
PacketEncryptionAlgorithm, PacketHkdfAlgorithm, ReplySurbEncryptionAlgorithm,
|
||||||
DEFAULT_NUM_MIX_HOPS,
|
|
||||||
};
|
};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
@@ -79,7 +78,6 @@ pub enum MessageRecoveryError {
|
|||||||
pub trait MessageReceiver {
|
pub trait MessageReceiver {
|
||||||
fn new() -> Self;
|
fn new() -> Self;
|
||||||
fn reconstructor(&mut self) -> &mut MessageReconstructor;
|
fn reconstructor(&mut self) -> &mut MessageReconstructor;
|
||||||
fn num_mix_hops(&self) -> u8;
|
|
||||||
|
|
||||||
fn decrypt_raw_message<C>(
|
fn decrypt_raw_message<C>(
|
||||||
&self,
|
&self,
|
||||||
@@ -143,7 +141,7 @@ pub trait MessageReceiver {
|
|||||||
fragment: Fragment,
|
fragment: Fragment,
|
||||||
) -> Result<Option<(NymMessage, Vec<i32>)>, MessageRecoveryError> {
|
) -> Result<Option<(NymMessage, Vec<i32>)>, MessageRecoveryError> {
|
||||||
if let Some((message, used_sets)) = self.reconstructor().insert_new_fragment(fragment) {
|
if let Some((message, used_sets)) = self.reconstructor().insert_new_fragment(fragment) {
|
||||||
match PaddedMessage::new_reconstructed(message).remove_padding(self.num_mix_hops()) {
|
match PaddedMessage::new_reconstructed(message).remove_padding() {
|
||||||
Ok(message) => Ok(Some((message, used_sets))),
|
Ok(message) => Ok(Some((message, used_sets))),
|
||||||
Err(err) => Err(MessageRecoveryError::MalformedReconstructedMessage {
|
Err(err) => Err(MessageRecoveryError::MalformedReconstructedMessage {
|
||||||
source: err,
|
source: err,
|
||||||
@@ -156,28 +154,11 @@ pub trait MessageReceiver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Default)]
|
||||||
pub struct SphinxMessageReceiver {
|
pub struct SphinxMessageReceiver {
|
||||||
/// High level public structure used to buffer all received data [`Fragment`]s and eventually
|
/// High level public structure used to buffer all received data [`Fragment`]s and eventually
|
||||||
/// returning original messages that they encapsulate.
|
/// returning original messages that they encapsulate.
|
||||||
reconstructor: MessageReconstructor,
|
reconstructor: MessageReconstructor,
|
||||||
|
|
||||||
/// Number of mix hops each packet ('real' message, ack, reply) is expected to take.
|
|
||||||
/// Note that it does not include gateway hops.
|
|
||||||
num_mix_hops: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SphinxMessageReceiver {
|
|
||||||
/// Allows setting non-default number of expected mix hops in the network.
|
|
||||||
// IMPORTANT NOTE: this is among others used to deserialize SURBs. Meaning that this is a
|
|
||||||
// global setting and currently always set to the default value. The implication is that it is
|
|
||||||
// not currently possible to have different number of hops for different SURB messages. So,
|
|
||||||
// don't try to use <3 mix hops for SURBs until this is refactored.
|
|
||||||
#[must_use]
|
|
||||||
pub fn with_mix_hops(mut self, hops: u8) -> Self {
|
|
||||||
self.num_mix_hops = hops;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MessageReceiver for SphinxMessageReceiver {
|
impl MessageReceiver for SphinxMessageReceiver {
|
||||||
@@ -201,112 +182,4 @@ impl MessageReceiver for SphinxMessageReceiver {
|
|||||||
fn reconstructor(&mut self) -> &mut MessageReconstructor {
|
fn reconstructor(&mut self) -> &mut MessageReconstructor {
|
||||||
&mut self.reconstructor
|
&mut self.reconstructor
|
||||||
}
|
}
|
||||||
|
|
||||||
fn num_mix_hops(&self) -> u8 {
|
|
||||||
self.num_mix_hops
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for SphinxMessageReceiver {
|
|
||||||
fn default() -> Self {
|
|
||||||
SphinxMessageReceiver {
|
|
||||||
reconstructor: Default::default(),
|
|
||||||
num_mix_hops: DEFAULT_NUM_MIX_HOPS,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod message_receiver {
|
|
||||||
use super::*;
|
|
||||||
use nym_crypto::asymmetric::identity;
|
|
||||||
use nym_mixnet_contract_common::LegacyMixLayer;
|
|
||||||
use nym_topology::{gateway, mix, NymTopology};
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
|
|
||||||
// TODO: is it somehow maybe possible to move it to `topology` and have if conditionally
|
|
||||||
// available to other modules?
|
|
||||||
/// Returns a hardcoded, valid instance of [`NymTopology`] that is to be used in
|
|
||||||
/// tests requiring instance of topology.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn topology_fixture() -> NymTopology {
|
|
||||||
let mut mixes = BTreeMap::new();
|
|
||||||
mixes.insert(
|
|
||||||
1,
|
|
||||||
vec![mix::LegacyNode {
|
|
||||||
mix_id: 123,
|
|
||||||
host: "10.20.30.40".parse().unwrap(),
|
|
||||||
mix_host: "10.20.30.40:1789".parse().unwrap(),
|
|
||||||
identity_key: identity::PublicKey::from_base58_string(
|
|
||||||
"3ebjp1Fb9hdcS1AR6AZihgeJiMHkB5jjJUsvqNnfQwU7",
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
sphinx_key: encryption::PublicKey::from_base58_string(
|
|
||||||
"B3GzG62aXAZNg14RoMCp3BhELNBrySLr2JqrwyfYFzRc",
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
layer: LegacyMixLayer::One,
|
|
||||||
version: "0.8.0-dev".into(),
|
|
||||||
}],
|
|
||||||
);
|
|
||||||
|
|
||||||
mixes.insert(
|
|
||||||
2,
|
|
||||||
vec![mix::LegacyNode {
|
|
||||||
mix_id: 234,
|
|
||||||
host: "11.21.31.41".parse().unwrap(),
|
|
||||||
mix_host: "11.21.31.41:1789".parse().unwrap(),
|
|
||||||
identity_key: identity::PublicKey::from_base58_string(
|
|
||||||
"D6YaMzLSY7mANtSQRKXsmMZpqgqiVkeiagKM4V4oFPFr",
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
sphinx_key: encryption::PublicKey::from_base58_string(
|
|
||||||
"5Z1VqYwM2xeKxd8H7fJpGWasNiDFijYBAee7MErkZ5QT",
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
layer: LegacyMixLayer::Two,
|
|
||||||
version: "0.8.0-dev".into(),
|
|
||||||
}],
|
|
||||||
);
|
|
||||||
|
|
||||||
mixes.insert(
|
|
||||||
3,
|
|
||||||
vec![mix::LegacyNode {
|
|
||||||
mix_id: 456,
|
|
||||||
host: "12.22.32.42".parse().unwrap(),
|
|
||||||
mix_host: "12.22.32.42:1789".parse().unwrap(),
|
|
||||||
identity_key: identity::PublicKey::from_base58_string(
|
|
||||||
"GkWDysw4AjESv1KiAiVn7JzzCMJeksxNSXVfr1PpX8wD",
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
sphinx_key: encryption::PublicKey::from_base58_string(
|
|
||||||
"9EyjhCggr2QEA2nakR88YHmXgpy92DWxoe2draDRkYof",
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
layer: LegacyMixLayer::Three,
|
|
||||||
version: "0.8.0-dev".into(),
|
|
||||||
}],
|
|
||||||
);
|
|
||||||
|
|
||||||
NymTopology::new(
|
|
||||||
// currently coco_nodes don't really exist so this is still to be determined
|
|
||||||
mixes,
|
|
||||||
vec![gateway::LegacyNode {
|
|
||||||
node_id: 789,
|
|
||||||
host: "1.2.3.4".parse().unwrap(),
|
|
||||||
mix_host: "1.2.3.4:1789".parse().unwrap(),
|
|
||||||
clients_ws_port: 9000,
|
|
||||||
clients_wss_port: None,
|
|
||||||
identity_key: identity::PublicKey::from_base58_string(
|
|
||||||
"FioFa8nMmPpQnYi7JyojoTuwGLeyNS8BF4ChPr29zUML",
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
sphinx_key: encryption::PublicKey::from_base58_string(
|
|
||||||
"EB42xvMFMD5rUCstE2CDazgQQJ22zLv8SPm1Luxni44c",
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
version: "0.8.0-dev".into(),
|
|
||||||
}],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,8 +42,43 @@ impl PendingSync {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct BlockProcessorConfig {
|
||||||
|
pub pruning_options: PruningOptions,
|
||||||
|
pub store_precommits: bool,
|
||||||
|
pub explicit_starting_block_height: Option<u32>,
|
||||||
|
pub use_best_effort_start_height: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for BlockProcessorConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
pruning_options: PruningOptions::nothing(),
|
||||||
|
store_precommits: true,
|
||||||
|
explicit_starting_block_height: None,
|
||||||
|
use_best_effort_start_height: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockProcessorConfig {
|
||||||
|
pub fn new(
|
||||||
|
pruning_options: PruningOptions,
|
||||||
|
store_precommits: bool,
|
||||||
|
explicit_starting_block_height: Option<u32>,
|
||||||
|
use_best_effort_start_height: bool,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
pruning_options,
|
||||||
|
store_precommits,
|
||||||
|
explicit_starting_block_height,
|
||||||
|
use_best_effort_start_height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct BlockProcessor {
|
pub struct BlockProcessor {
|
||||||
pruning_options: PruningOptions,
|
config: BlockProcessorConfig,
|
||||||
cancel: CancellationToken,
|
cancel: CancellationToken,
|
||||||
synced: Arc<Notify>,
|
synced: Arc<Notify>,
|
||||||
last_processed_height: u32,
|
last_processed_height: u32,
|
||||||
@@ -65,9 +100,10 @@ pub struct BlockProcessor {
|
|||||||
msg_modules: Vec<Box<dyn MsgModule + Send>>,
|
msg_modules: Vec<Box<dyn MsgModule + Send>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
impl BlockProcessor {
|
impl BlockProcessor {
|
||||||
pub async fn new(
|
pub async fn new(
|
||||||
pruning_options: PruningOptions,
|
config: BlockProcessorConfig,
|
||||||
cancel: CancellationToken,
|
cancel: CancellationToken,
|
||||||
synced: Arc<Notify>,
|
synced: Arc<Notify>,
|
||||||
incoming: UnboundedReceiver<BlockToProcess>,
|
incoming: UnboundedReceiver<BlockToProcess>,
|
||||||
@@ -81,8 +117,10 @@ impl BlockProcessor {
|
|||||||
let last_pruned = storage.get_pruned_height().await?;
|
let last_pruned = storage.get_pruned_height().await?;
|
||||||
let last_pruned_height = last_pruned.try_into().unwrap_or_default();
|
let last_pruned_height = last_pruned.try_into().unwrap_or_default();
|
||||||
|
|
||||||
|
debug!(last_processed_height = %last_processed_height, pruned_height = %last_pruned_height, "setting up block processor...");
|
||||||
|
|
||||||
Ok(BlockProcessor {
|
Ok(BlockProcessor {
|
||||||
pruning_options,
|
config,
|
||||||
cancel,
|
cancel,
|
||||||
synced,
|
synced,
|
||||||
last_processed_height,
|
last_processed_height,
|
||||||
@@ -101,7 +139,7 @@ impl BlockProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_pruning(mut self, pruning_options: PruningOptions) -> Self {
|
pub fn with_pruning(mut self, pruning_options: PruningOptions) -> Self {
|
||||||
self.pruning_options = pruning_options;
|
self.config.pruning_options = pruning_options;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,7 +166,7 @@ impl BlockProcessor {
|
|||||||
// we won't end up with a corrupted storage.
|
// we won't end up with a corrupted storage.
|
||||||
let mut tx = self.storage.begin_processing_tx().await?;
|
let mut tx = self.storage.begin_processing_tx().await?;
|
||||||
|
|
||||||
persist_block(&full_info, &mut tx).await?;
|
persist_block(&full_info, &mut tx, self.config.store_precommits).await?;
|
||||||
|
|
||||||
// let the modules do whatever they want
|
// let the modules do whatever they want
|
||||||
// the ones wanting the full block:
|
// the ones wanting the full block:
|
||||||
@@ -241,7 +279,7 @@ impl BlockProcessor {
|
|||||||
|
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
async fn prune_storage(&mut self) -> Result<(), ScraperError> {
|
async fn prune_storage(&mut self) -> Result<(), ScraperError> {
|
||||||
let keep_recent = self.pruning_options.strategy_keep_recent();
|
let keep_recent = self.config.pruning_options.strategy_keep_recent();
|
||||||
let last_to_keep = self.last_processed_height - keep_recent;
|
let last_to_keep = self.last_processed_height - keep_recent;
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
@@ -282,12 +320,12 @@ impl BlockProcessor {
|
|||||||
async fn maybe_prune_storage(&mut self) -> Result<(), ScraperError> {
|
async fn maybe_prune_storage(&mut self) -> Result<(), ScraperError> {
|
||||||
debug!("checking for storage pruning");
|
debug!("checking for storage pruning");
|
||||||
|
|
||||||
if self.pruning_options.strategy.is_nothing() {
|
if self.config.pruning_options.strategy.is_nothing() {
|
||||||
trace!("the current pruning strategy is 'nothing'");
|
trace!("the current pruning strategy is 'nothing'");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let interval = self.pruning_options.strategy_interval();
|
let interval = self.config.pruning_options.strategy_interval();
|
||||||
if self.last_pruned_height + interval <= self.last_processed_height {
|
if self.last_pruned_height + interval <= self.last_processed_height {
|
||||||
self.prune_storage().await?;
|
self.prune_storage().await?;
|
||||||
}
|
}
|
||||||
@@ -363,20 +401,69 @@ impl BlockProcessor {
|
|||||||
// but we need it to help the compiler figure out the future is `Send`
|
// but we need it to help the compiler figure out the future is `Send`
|
||||||
async fn startup_resync(&mut self) -> Result<(), ScraperError> {
|
async fn startup_resync(&mut self) -> Result<(), ScraperError> {
|
||||||
assert!(self.pending_sync.is_empty());
|
assert!(self.pending_sync.is_empty());
|
||||||
|
info!("attempting to run startup resync...");
|
||||||
|
|
||||||
self.maybe_prune_storage().await?;
|
self.maybe_prune_storage().await?;
|
||||||
|
|
||||||
let latest_block = self.rpc_client.current_block_height().await? as u32;
|
let latest_block = self.rpc_client.current_block_height().await? as u32;
|
||||||
|
info!("obtained latest block height: {latest_block}");
|
||||||
|
|
||||||
if latest_block > self.last_processed_height && self.last_processed_height != 0 {
|
if latest_block > self.last_processed_height && self.last_processed_height != 0 {
|
||||||
|
info!("we have already processed some blocks in the past - attempting to resume...");
|
||||||
// in case we were offline for a while,
|
// in case we were offline for a while,
|
||||||
// make sure we don't request blocks we'd have to prune anyway
|
// make sure we don't request blocks we'd have to prune anyway
|
||||||
let keep_recent = self.pruning_options.strategy_keep_recent();
|
let keep_recent = self.config.pruning_options.strategy_keep_recent();
|
||||||
let last_to_keep = latest_block - keep_recent;
|
let last_to_keep = latest_block - keep_recent;
|
||||||
self.last_processed_height = max(self.last_processed_height, last_to_keep);
|
|
||||||
|
if !self.config.pruning_options.strategy.is_nothing() {
|
||||||
|
self.last_processed_height = max(self.last_processed_height, last_to_keep);
|
||||||
|
}
|
||||||
|
|
||||||
let request_range = self.last_processed_height + 1..latest_block + 1;
|
let request_range = self.last_processed_height + 1..latest_block + 1;
|
||||||
info!("we need to request {request_range:?} to resync");
|
info!(
|
||||||
|
keep_recent = %keep_recent,
|
||||||
|
last_to_keep = %last_to_keep,
|
||||||
|
last_processed_height = %self.last_processed_height,
|
||||||
|
"we need to request {request_range:?} to resync"
|
||||||
|
);
|
||||||
|
self.request_missing_blocks(request_range).await?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is the first time starting up
|
||||||
|
if self.last_processed_height == 0 {
|
||||||
|
info!("this is the first time starting up");
|
||||||
|
let Some(starting_height) = self.config.explicit_starting_block_height else {
|
||||||
|
info!("no starting block height set - will use the default behaviour");
|
||||||
|
// nothing to do
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
info!("attempting to start the scraper from block {starting_height}");
|
||||||
|
let earliest_available =
|
||||||
|
self.rpc_client.earliest_available_block_height().await? as u32;
|
||||||
|
info!("earliest available block height: {earliest_available}");
|
||||||
|
|
||||||
|
if earliest_available > starting_height && self.config.use_best_effort_start_height {
|
||||||
|
error!("the earliest available block is higher than the desired starting height");
|
||||||
|
return Err(ScraperError::BlocksUnavailable {
|
||||||
|
height: starting_height,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let starting_height = if earliest_available > starting_height {
|
||||||
|
// add few additional blocks to account for all the startup waiting
|
||||||
|
// because the node might have pruned few blocks since
|
||||||
|
earliest_available + 10
|
||||||
|
} else {
|
||||||
|
starting_height
|
||||||
|
};
|
||||||
|
|
||||||
|
let request_range = starting_height..latest_block + 1;
|
||||||
|
|
||||||
|
info!("going to start the scraper from block {starting_height}");
|
||||||
|
info!("we need to request {request_range:?} before properly starting up");
|
||||||
|
|
||||||
self.request_missing_blocks(request_range).await?;
|
self.request_missing_blocks(request_range).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -384,7 +471,7 @@ impl BlockProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn run(&mut self) {
|
pub(crate) async fn run(&mut self) {
|
||||||
info!("starting processing loop");
|
info!("starting block processor processing loop");
|
||||||
|
|
||||||
// sure, we could be more efficient and reset it on every processed block,
|
// sure, we could be more efficient and reset it on every processed block,
|
||||||
// but the overhead is so minimal that it doesn't matter
|
// but the overhead is so minimal that it doesn't matter
|
||||||
|
|||||||
@@ -84,13 +84,7 @@ impl TryFrom<Event> for BlockToProcess {
|
|||||||
|
|
||||||
// TODO: we're losing `result_begin_block` and `result_end_block` here but maybe that's fine?
|
// TODO: we're losing `result_begin_block` and `result_end_block` here but maybe that's fine?
|
||||||
let maybe_block = match event.data {
|
let maybe_block = match event.data {
|
||||||
// we don't care about `NewBlock` until CometBFT 0.38, i.e. until we upgrade to wasmd 0.50
|
EventData::NewBlock { block, .. } => block,
|
||||||
EventData::NewBlock { .. } => {
|
|
||||||
return Err(ScraperError::InvalidSubscriptionEvent {
|
|
||||||
query,
|
|
||||||
kind: "NewBlock".to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
EventData::LegacyNewBlock { block, .. } => block,
|
EventData::LegacyNewBlock { block, .. } => block,
|
||||||
EventData::Tx { .. } => {
|
EventData::Tx { .. } => {
|
||||||
return Err(ScraperError::InvalidSubscriptionEvent {
|
return Err(ScraperError::InvalidSubscriptionEvent {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user