Compare commits
102 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f4bd48263d | |||
| 5c40052d39 | |||
| 7b858dfd69 | |||
| a4bd547023 | |||
| db03ec31b1 | |||
| 9b285735b8 | |||
| 691280797a | |||
| f84de25302 | |||
| e554f1e0ad | |||
| 62a4a2ed70 | |||
| caad74c73d | |||
| 917993d8fb | |||
| 1451db39e6 | |||
| f13a2a6c06 | |||
| ce39fb6675 | |||
| 02a926b74a | |||
| 54ba710ea0 | |||
| 2653d12e55 | |||
| f94d6d51cf | |||
| a0116f9aec | |||
| aaa8ee9d53 | |||
| ab0f6af4b9 | |||
| 7669d0933f | |||
| 50433fe265 | |||
| 42aade29eb | |||
| 9f26759b8d | |||
| 9e642c6354 | |||
| cca19f36c2 | |||
| 17894880e0 | |||
| ef6fc82c39 | |||
| 0c83ae2408 | |||
| 92490731e7 | |||
| 0ce93e366e | |||
| 0d031875f6 | |||
| e6103e4c43 | |||
| 7140ba4ea9 | |||
| 62962509eb | |||
| d285b70030 | |||
| 534a5068d3 | |||
| e32c042c8d | |||
| dd6a45f251 | |||
| 6ee1f16ce8 | |||
| 924d7d1ccc | |||
| 395c134186 | |||
| 55e485ebce | |||
| cfce6dedff | |||
| 5a08a4cdd2 | |||
| 42ffb7d36e | |||
| e024d68fac | |||
| e66a069d5f | |||
| 3531901a17 | |||
| a399a75b03 | |||
| 0de14718cb | |||
| cd476ef6a2 | |||
| ad56645fc5 | |||
| 7ceaf9a40e | |||
| 2209e8ac04 | |||
| 6f2a3d9033 | |||
| 0530967807 | |||
| 9db748e8dd | |||
| 82ed88e26e | |||
| 594174827d | |||
| f6f364c551 | |||
| f648349e82 | |||
| 4fb78c3737 | |||
| f208855bc8 | |||
| 60426b8c45 | |||
| 9792a8829b | |||
| 5b23429415 | |||
| 89de989ad1 | |||
| 97068b2aac | |||
| 0e3e5c27f3 | |||
| 01e3c8206b | |||
| ef20b8c7d1 | |||
| 61af16784b | |||
| caf21076c9 | |||
| 1672135308 | |||
| c07ef0253d | |||
| cc799b69d3 | |||
| dd4bbc0708 | |||
| 7b77091fb1 | |||
| 6581ebf235 | |||
| 82ace6d27b | |||
| e362207583 | |||
| 68caecff35 | |||
| 2fae4414d2 | |||
| 6eca09b904 | |||
| 7ab821cb11 | |||
| 9904f6b17c | |||
| 5e0eeeddd6 | |||
| b6df383584 | |||
| 3f00e2c317 | |||
| 3cdda8fdfd | |||
| 33f47ef36e | |||
| 180802feb8 | |||
| 4077717d3a | |||
| bc3df31518 | |||
| 61d6acace8 | |||
| abb4e3f988 | |||
| c5488337da | |||
| f06eefe184 | |||
| e86fa8fc7f |
@@ -15,10 +15,8 @@ jobs:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Install Dependencies (Linux)
|
||||
run: sudo apt-get update && sudo apt-get install -y build-essential curl wget libssl-dev libudev-dev squashfs-tools protobuf-compiler git python3 && sudo apt-get update --fix-missing
|
||||
- name: Install pip3
|
||||
run: sudo apt install -y python3-pip
|
||||
- name: Install Python3 modules
|
||||
run: sudo pip3 install pandas tabulate
|
||||
run: sudo apt install -y python3-pandas python3-tabulate
|
||||
- name: Install rsync
|
||||
run: sudo apt-get install -y rsync
|
||||
- uses: rlespinasse/github-slug-action@v3.x
|
||||
@@ -41,6 +39,8 @@ jobs:
|
||||
|
||||
- name: Install project dependencies
|
||||
run: pnpm i
|
||||
- name: Generate llms-full.txt
|
||||
run: pnpm run generate:llms
|
||||
- name: Build project
|
||||
run: pnpm run build
|
||||
- name: Generate sitemap
|
||||
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [ubuntu-22.04]
|
||||
platform: [arc-ubuntu-22.04]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
env:
|
||||
|
||||
@@ -90,7 +90,7 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: --workspace --all-targets --exclude nym-gateway-probe --exclude nym-node-status-api -- -D warnings
|
||||
args: --workspace --all-targets --exclude nym-gateway-probe --exclude nym-node-status-api --exclude nym-node-status-agent --exclude nym-node-status-client -- -D warnings
|
||||
|
||||
- name: Clippy (non-macos)
|
||||
if: contains(matrix.os, 'linux') || contains(matrix.os, 'windows')
|
||||
@@ -104,14 +104,15 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace --exclude nym-gateway-probe --exclude nym-node-status-api --exclude nym-node-status-agent --exclude nym-node-status-client
|
||||
|
||||
# only build on linux because of wg FFI bindings of its dependency (network probe)
|
||||
- name: Build nym-node-status-api (linux only)
|
||||
# Build Go FFI-dependent crates separately (requires Go, only available on Linux CI)
|
||||
- name: Build nym-node-status-api and nym-node-status-agent (linux only)
|
||||
if: runner.os == 'Linux'
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: -p nym-node-status-api
|
||||
args: -p nym-node-status-api -p nym-node-status-agent
|
||||
|
||||
- name: Build all examples
|
||||
if: contains(matrix.os, 'linux')
|
||||
|
||||
@@ -15,6 +15,9 @@ env:
|
||||
jobs:
|
||||
publish-dry-run:
|
||||
runs-on: arc-linux-latest
|
||||
timeout-minutes: 35
|
||||
env:
|
||||
RUSTUP_PERMIT_COPY_RENAME: 1
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v6
|
||||
@@ -59,20 +62,60 @@ jobs:
|
||||
- name: Bump versions (local only)
|
||||
run: |
|
||||
cargo workspaces version custom ${{ inputs.version }} \
|
||||
--allow-branch ${{ github.ref_name }} \
|
||||
--no-git-commit \
|
||||
--yes
|
||||
|
||||
- name: Preflight publish checks
|
||||
run: |
|
||||
python3 tools/internal/check_publish_preflight.py
|
||||
|
||||
# Dry run may show cascading dependency errors because packages aren't
|
||||
# actually uploaded - these are expected and ignored. We check for real
|
||||
# errors like packaging failures, missing metadata, or invalid Cargo.toml.
|
||||
- name: Publish (dry run)
|
||||
run: |
|
||||
output=$(cargo workspaces publish --dry-run --allow-dirty 2>&1) || true
|
||||
echo "$output"
|
||||
set +e
|
||||
publish_status=1
|
||||
max_attempts=2
|
||||
attempt=1
|
||||
rm -f /tmp/publish-dry-run.log
|
||||
|
||||
# Check for real errors (not cascading dependency errors)
|
||||
# Cascading errors mention "crates.io index", real errors mention "Cargo.toml"
|
||||
echo "$output" | grep -i "Cargo.toml" && exit 1 || true
|
||||
while [ "$attempt" -le "$max_attempts" ]; do
|
||||
echo "Dry-run publish attempt ${attempt}/${max_attempts}"
|
||||
cargo workspaces publish --dry-run --allow-dirty 2>&1 | tee /tmp/publish-dry-run.log
|
||||
publish_status=${PIPESTATUS[0]}
|
||||
|
||||
if [ "$publish_status" -eq 0 ]; then
|
||||
break
|
||||
fi
|
||||
|
||||
# Retry once for interruption/runner issues.
|
||||
if [ "$attempt" -lt "$max_attempts" ] && \
|
||||
{ [ "$publish_status" -eq 130 ] || [ "$publish_status" -eq 137 ]; }; then
|
||||
echo "Publish dry-run interrupted (exit ${publish_status}), retrying in 10s..."
|
||||
sleep 10
|
||||
attempt=$((attempt + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
break
|
||||
done
|
||||
set -e
|
||||
|
||||
if grep -Eiq \
|
||||
"failed to verify manifest|failed to parse manifest|invalid Cargo.toml|error: package .* has no (description|license|repository)" \
|
||||
/tmp/publish-dry-run.log; then
|
||||
echo "Detected real packaging/manifest errors"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# In dry-run mode, non-zero publish status is expected due to
|
||||
# dependency-cascade failures against crates.io index.
|
||||
if [ "$publish_status" -ne 0 ]; then
|
||||
echo "Dry-run publish returned non-zero (${publish_status}) but no real manifest blockers were detected."
|
||||
fi
|
||||
|
||||
echo "Only expected dry-run dependency cascade errors detected (if any)."
|
||||
|
||||
# Show the list of packages published
|
||||
- name: Show package versions
|
||||
|
||||
@@ -17,6 +17,8 @@ on:
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: arc-linux-latest
|
||||
env:
|
||||
RUSTUP_PERMIT_COPY_RENAME: 1
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v6
|
||||
|
||||
@@ -17,6 +17,8 @@ on:
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: arc-linux-latest
|
||||
env:
|
||||
RUSTUP_PERMIT_COPY_RENAME: 1
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v6
|
||||
|
||||
@@ -15,6 +15,8 @@ env:
|
||||
jobs:
|
||||
version-bump:
|
||||
runs-on: arc-linux-latest
|
||||
env:
|
||||
RUSTUP_PERMIT_COPY_RENAME: 1
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
|
||||
@@ -6,6 +6,8 @@ on:
|
||||
branches-ignore: [master]
|
||||
paths:
|
||||
- "documentation/docs/**"
|
||||
- "sdk/typescript/packages/sdk/src/**"
|
||||
- "sdk/typescript/packages/mix-fetch/src/**"
|
||||
- ".github/workflows/ci-docs.yml"
|
||||
|
||||
jobs:
|
||||
@@ -20,10 +22,8 @@ jobs:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Install Dependencies (Linux)
|
||||
run: sudo apt-get update && sudo apt-get install -y build-essential curl wget libssl-dev libudev-dev squashfs-tools protobuf-compiler git python3 && sudo apt-get update --fix-missing
|
||||
- name: Install pip3
|
||||
run: sudo apt install -y python3-pip
|
||||
- name: Install Python3 modules
|
||||
run: sudo pip3 install pandas tabulate
|
||||
run: sudo apt install -y python3-pandas python3-tabulate
|
||||
- name: Install rsync
|
||||
run: sudo apt-get install -y rsync
|
||||
- uses: rlespinasse/github-slug-action@v3.x
|
||||
@@ -44,8 +44,27 @@ jobs:
|
||||
command: build
|
||||
args: --workspace --release
|
||||
|
||||
- name: Check if TypeScript SDK source changed
|
||||
id: check-ts-sdk
|
||||
run: |
|
||||
if git diff --name-only ${{ github.event.before }} ${{ github.sha }} | grep -qE '^sdk/typescript/packages/(sdk|mix-fetch)/src/'; then
|
||||
echo "changed=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "changed=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
working-directory: ${{ github.workspace }}
|
||||
|
||||
- name: Regenerate TypeDoc API reference
|
||||
if: steps.check-ts-sdk.outputs.changed == 'true'
|
||||
run: |
|
||||
npm install -g typedoc@0.25.13 typedoc-plugin-markdown@4.0.3
|
||||
cd ${{ github.workspace }}/sdk/typescript/packages/sdk && typedoc --skipErrorChecking
|
||||
cd ${{ github.workspace }}/sdk/typescript/packages/mix-fetch && typedoc --skipErrorChecking
|
||||
|
||||
- name: Install project dependencies
|
||||
run: pnpm i
|
||||
- name: Generate llms-full.txt
|
||||
run: pnpm run generate:llms
|
||||
- name: Build project
|
||||
run: pnpm run build
|
||||
- name: Generate sitemap
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
- os: arc-ubuntu-22.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
|
||||
@@ -25,6 +25,10 @@ jobs:
|
||||
- name: Install cargo-workspaces
|
||||
run: cargo install cargo-workspaces
|
||||
|
||||
- name: Preflight publish checks
|
||||
run: |
|
||||
python3 tools/internal/check_publish_preflight.py
|
||||
|
||||
- name: Publish remaining crates
|
||||
env:
|
||||
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
||||
|
||||
@@ -4,6 +4,56 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [2026.9-venaco] (2026-05-06)
|
||||
|
||||
- Fix for v9 IPR ([#6710])
|
||||
- Only init SHARED_CLIENT if requested ([#6708])
|
||||
- Fixes to crates and CI ([#6686])
|
||||
- Return ipv6 addresses as well ([#6684])
|
||||
- Fix invalid ticket spend ([#6683])
|
||||
- Block non-public IPR/NR checks ([#6670])
|
||||
|
||||
[#6710]: https://github.com/nymtech/nym/pull/6710
|
||||
[#6708]: https://github.com/nymtech/nym/pull/6708
|
||||
[#6686]: https://github.com/nymtech/nym/pull/6686
|
||||
[#6684]: https://github.com/nymtech/nym/pull/6684
|
||||
[#6683]: https://github.com/nymtech/nym/pull/6683
|
||||
[#6670]: https://github.com/nymtech/nym/pull/6670
|
||||
|
||||
## [2026.8-urda] (2026-04-20)
|
||||
|
||||
- Include all gateways in the returned list ([#6649])
|
||||
- Optimize GW probe in NS agent ([#6636])
|
||||
- Max/sdk docrs ([#6566])
|
||||
- Max/sdk stream wrapper ([#6320])
|
||||
|
||||
[#6649]: https://github.com/nymtech/nym/pull/6649
|
||||
[#6636]: https://github.com/nymtech/nym/pull/6636
|
||||
[#6566]: https://github.com/nymtech/nym/pull/6566
|
||||
[#6320]: https://github.com/nymtech/nym/pull/6320
|
||||
|
||||
## [2026.7-tola] (2026-04-07)
|
||||
|
||||
- Simon/ecash contract serde fix ([#6634])
|
||||
- Update Fallback IP for Nym API ([#6622])
|
||||
- Nym Node spam logging ([#6621])
|
||||
- feat: multiple deposit prices ([#6608])
|
||||
- move format_debug_bytes in common crate ([#6580])
|
||||
- bugfix: make sure client keys are generated before requesting credentials ([#6579])
|
||||
- Fix socks5 GW probe regression ([#6576])
|
||||
- Max/lp stream framing ([#6573])
|
||||
- HTTP domain rotation conditions ([#6570])
|
||||
|
||||
[#6634]: https://github.com/nymtech/nym/pull/6634
|
||||
[#6622]: https://github.com/nymtech/nym/pull/6622
|
||||
[#6621]: https://github.com/nymtech/nym/pull/6621
|
||||
[#6608]: https://github.com/nymtech/nym/pull/6608
|
||||
[#6580]: https://github.com/nymtech/nym/pull/6580
|
||||
[#6579]: https://github.com/nymtech/nym/pull/6579
|
||||
[#6576]: https://github.com/nymtech/nym/pull/6576
|
||||
[#6573]: https://github.com/nymtech/nym/pull/6573
|
||||
[#6570]: https://github.com/nymtech/nym/pull/6570
|
||||
|
||||
## [2026.6-stilton] (2026-03-25)
|
||||
|
||||
- lp fixes ([#6601])
|
||||
|
||||
Generated
+219
-64
@@ -1691,7 +1691,8 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
[[package]]
|
||||
name = "core-models"
|
||||
version = "0.0.5"
|
||||
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "657f625ff361906f779745d08375ae3cc9fef87a35fba5f22874cf773010daf4"
|
||||
dependencies = [
|
||||
"hax-lib",
|
||||
"pastey 0.2.1",
|
||||
@@ -2350,6 +2351,47 @@ dependencies = [
|
||||
"x25519-dalek",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "defmt"
|
||||
version = "0.3.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0963443817029b2024136fc4dd07a5107eb8f977eaf18fcd1fdeb11306b64ad"
|
||||
dependencies = [
|
||||
"defmt 1.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "defmt"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"defmt-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "defmt-macros"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e"
|
||||
dependencies = [
|
||||
"defmt-parser",
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "defmt-parser"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e"
|
||||
dependencies = [
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "delegate-display"
|
||||
version = "3.0.0"
|
||||
@@ -2534,7 +2576,7 @@ dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3398,6 +3440,15 @@ dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hash32"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
@@ -3523,6 +3574,16 @@ dependencies = [
|
||||
"http 1.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heapless"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
|
||||
dependencies = [
|
||||
"hash32",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
@@ -3589,7 +3650,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"rand 0.9.2",
|
||||
"ring",
|
||||
"rustls 0.23.29",
|
||||
"rustls 0.23.37",
|
||||
"thiserror 2.0.12",
|
||||
"tinyvec",
|
||||
"tokio",
|
||||
@@ -3614,7 +3675,7 @@ dependencies = [
|
||||
"parking_lot",
|
||||
"rand 0.9.2",
|
||||
"resolv-conf",
|
||||
"rustls 0.23.29",
|
||||
"rustls 0.23.37",
|
||||
"smallvec",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
@@ -3871,7 +3932,7 @@ dependencies = [
|
||||
"http 1.3.1",
|
||||
"hyper 1.6.0",
|
||||
"hyper-util",
|
||||
"rustls 0.23.29",
|
||||
"rustls 0.23.37",
|
||||
"rustls-native-certs 0.8.3",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
@@ -4611,7 +4672,8 @@ checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
|
||||
[[package]]
|
||||
name = "libcrux-aesgcm"
|
||||
version = "0.0.7"
|
||||
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99f2a019dab4097585a7d4f5b9deebe46cd1e628b16a5bc4cb0ce35e1da334e6"
|
||||
dependencies = [
|
||||
"libcrux-intrinsics",
|
||||
"libcrux-platform",
|
||||
@@ -4621,8 +4683,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-chacha20poly1305"
|
||||
version = "0.0.6"
|
||||
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
|
||||
version = "0.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc08d044676af21343b32b988411fa98dbb5cf65a03c9df478ced221bbdfdb1b"
|
||||
dependencies = [
|
||||
"libcrux-hacl-rs",
|
||||
"libcrux-macros",
|
||||
@@ -4634,7 +4697,8 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "libcrux-curve25519"
|
||||
version = "0.0.6"
|
||||
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb1e5fd8476a6ed609d24ef42aee5ab6f99f7c65d054f92412da9f499e423299"
|
||||
dependencies = [
|
||||
"libcrux-hacl-rs",
|
||||
"libcrux-macros",
|
||||
@@ -4645,7 +4709,8 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "libcrux-ecdh"
|
||||
version = "0.0.6"
|
||||
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b65f73ce79337c762eb38bbac91e4c9b9e60cf318e8501b812750c640814d45e"
|
||||
dependencies = [
|
||||
"libcrux-curve25519",
|
||||
"libcrux-p256",
|
||||
@@ -4655,8 +4720,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-ed25519"
|
||||
version = "0.0.6"
|
||||
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
|
||||
version = "0.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "835919315b7042fe9e03b6458efe0db94bf2aa7b873934dbee5b5463a8124b43"
|
||||
dependencies = [
|
||||
"libcrux-hacl-rs",
|
||||
"libcrux-macros",
|
||||
@@ -4668,7 +4734,8 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "libcrux-hacl-rs"
|
||||
version = "0.0.4"
|
||||
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2637dc87d158e1f1b550fd9b226443e84153fded4de69028d897b534d16d22e6"
|
||||
dependencies = [
|
||||
"libcrux-macros",
|
||||
]
|
||||
@@ -4676,7 +4743,8 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "libcrux-hkdf"
|
||||
version = "0.0.6"
|
||||
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c1a89ca0c89be3a268a921e47105fb7873badf7267f5e3ebf4ea46baedd73ef"
|
||||
dependencies = [
|
||||
"libcrux-hacl-rs",
|
||||
"libcrux-hmac",
|
||||
@@ -4686,7 +4754,8 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "libcrux-hmac"
|
||||
version = "0.0.6"
|
||||
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a7a242707d65960770bd7e14e4f18a92bdf0b967777dd404887db8d087a643b"
|
||||
dependencies = [
|
||||
"libcrux-hacl-rs",
|
||||
"libcrux-macros",
|
||||
@@ -4696,7 +4765,8 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "libcrux-intrinsics"
|
||||
version = "0.0.6"
|
||||
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1b5db005ff8001e026b73a6842ee81bbef8ec5ff0e1915a67ae65fd2a9fafa5"
|
||||
dependencies = [
|
||||
"core-models",
|
||||
"hax-lib",
|
||||
@@ -4704,8 +4774,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-kem"
|
||||
version = "0.0.6"
|
||||
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
|
||||
version = "0.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12631592f491d22fd1a176d32b2c6edfb673998fd3987e9d95f8fa79ad2a737b"
|
||||
dependencies = [
|
||||
"libcrux-curve25519",
|
||||
"libcrux-ecdh",
|
||||
@@ -4720,7 +4791,8 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "libcrux-macros"
|
||||
version = "0.0.3"
|
||||
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffd6aa2dcd5be681662001b81d493f1569c6d49a32361f470b0c955465cd0338"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.106",
|
||||
@@ -4728,8 +4800,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-ml-dsa"
|
||||
version = "0.0.7"
|
||||
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
|
||||
version = "0.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a72929ed421cc3bf16a946b3e7d2a58d215b0b5c2a12be26b53629f081bf49b2"
|
||||
dependencies = [
|
||||
"core-models",
|
||||
"hax-lib",
|
||||
@@ -4742,8 +4815,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-ml-kem"
|
||||
version = "0.0.7"
|
||||
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
|
||||
version = "0.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a14ab3e477de9df6ee1273a114018ff62c4996ca9220070c4e5cb1743f94a67d"
|
||||
dependencies = [
|
||||
"hax-lib",
|
||||
"libcrux-intrinsics",
|
||||
@@ -4758,7 +4832,8 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "libcrux-p256"
|
||||
version = "0.0.6"
|
||||
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4778ba25cb08bb8a96bd100e19ed9aecf78337198fd176036e21042b2dd99bc"
|
||||
dependencies = [
|
||||
"libcrux-hacl-rs",
|
||||
"libcrux-macros",
|
||||
@@ -4770,15 +4845,17 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "libcrux-platform"
|
||||
version = "0.0.3"
|
||||
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d9e21d7ed31a92ac539bd69a8c970b183ee883872d2d19ce27036e24cb8ecc4"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-poly1305"
|
||||
version = "0.0.4"
|
||||
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
|
||||
version = "0.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02491808ee5b9db8cb65fad64ae0be812db64beef179d945c00c7787dc7dfcf9"
|
||||
dependencies = [
|
||||
"libcrux-hacl-rs",
|
||||
"libcrux-macros",
|
||||
@@ -4786,8 +4863,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-psq"
|
||||
version = "0.0.7"
|
||||
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
|
||||
version = "0.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "779ade7aa5e1b4b400c716b313cbf69070988dd005f92e961c2da4c3c42fbea4"
|
||||
dependencies = [
|
||||
"classic-mceliece-rust",
|
||||
"libcrux-aesgcm",
|
||||
@@ -4809,7 +4887,8 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "libcrux-secrets"
|
||||
version = "0.0.5"
|
||||
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ce650f3041b44ba40d4263852347d007cd2cd9d1cc856a6f6c8b2e10c3fd40b"
|
||||
dependencies = [
|
||||
"hax-lib",
|
||||
]
|
||||
@@ -4817,7 +4896,8 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "libcrux-sha2"
|
||||
version = "0.0.6"
|
||||
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9d253473f259fc74a280c43f29c464f7e374abdf28b4942234dc707f529d4b7"
|
||||
dependencies = [
|
||||
"libcrux-hacl-rs",
|
||||
"libcrux-macros",
|
||||
@@ -4826,8 +4906,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-sha3"
|
||||
version = "0.0.7"
|
||||
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
|
||||
version = "0.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1ae0b7d0e1cc4793a609fd0ff2ca3b3a3fabae523770c619a3d4bc86417b0d7"
|
||||
dependencies = [
|
||||
"hax-lib",
|
||||
"libcrux-intrinsics",
|
||||
@@ -4838,7 +4919,8 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "libcrux-traits"
|
||||
version = "0.0.6"
|
||||
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "812e4fa89f3f5e34b47f928b22b1b78395a0d4ec23b1f583db635f128159d65f"
|
||||
dependencies = [
|
||||
"libcrux-secrets",
|
||||
"rand 0.9.2",
|
||||
@@ -5047,6 +5129,12 @@ dependencies = [
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "managed"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d"
|
||||
|
||||
[[package]]
|
||||
name = "maplit"
|
||||
version = "1.0.2"
|
||||
@@ -5448,7 +5536,7 @@ version = "0.50.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||
dependencies = [
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5547,7 +5635,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-api"
|
||||
version = "1.1.76"
|
||||
version = "1.1.79"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -5792,7 +5880,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-cli"
|
||||
version = "1.1.73"
|
||||
version = "1.1.76"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
@@ -5875,7 +5963,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-client"
|
||||
version = "1.1.73"
|
||||
version = "1.1.76"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"clap",
|
||||
@@ -6687,6 +6775,7 @@ version = "1.20.4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
"bs58",
|
||||
"bytes",
|
||||
"clap",
|
||||
"futures",
|
||||
@@ -6711,7 +6800,6 @@ dependencies = [
|
||||
"nym-lp",
|
||||
"nym-network-defaults",
|
||||
"nym-node-requests",
|
||||
"nym-node-status-client",
|
||||
"nym-registration-client",
|
||||
"nym-registration-common",
|
||||
"nym-sdk",
|
||||
@@ -6849,6 +6937,7 @@ dependencies = [
|
||||
"nym-network-defaults",
|
||||
"once_cell",
|
||||
"reqwest 0.13.1",
|
||||
"rustls 0.23.37",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_plain",
|
||||
@@ -6939,6 +7028,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"futures",
|
||||
"nym-ip-packet-requests",
|
||||
"nym-lp",
|
||||
"nym-sdk",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
@@ -6956,12 +7046,15 @@ dependencies = [
|
||||
"nym-crypto",
|
||||
"nym-service-provider-requests-common",
|
||||
"nym-sphinx",
|
||||
"pnet_packet",
|
||||
"rand 0.8.5",
|
||||
"semver 1.0.27",
|
||||
"serde",
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6984,7 +7077,7 @@ dependencies = [
|
||||
"nym-exit-policy",
|
||||
"nym-id",
|
||||
"nym-ip-packet-requests",
|
||||
"nym-kcp",
|
||||
"nym-lp",
|
||||
"nym-network-defaults",
|
||||
"nym-network-requester",
|
||||
"nym-sdk",
|
||||
@@ -7086,6 +7179,7 @@ dependencies = [
|
||||
"criterion",
|
||||
"libcrux-psq",
|
||||
"num_enum",
|
||||
"nym-common",
|
||||
"nym-crypto",
|
||||
"nym-kkt",
|
||||
"nym-kkt-ciphersuite",
|
||||
@@ -7279,7 +7373,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-requester"
|
||||
version = "1.1.74"
|
||||
version = "1.1.77"
|
||||
dependencies = [
|
||||
"addr",
|
||||
"anyhow",
|
||||
@@ -7329,7 +7423,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-node"
|
||||
version = "1.28.0"
|
||||
version = "1.31.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
@@ -7466,14 +7560,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-node-status-agent"
|
||||
version = "1.1.3"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"futures",
|
||||
"nym-bin-common",
|
||||
"nym-crypto",
|
||||
"nym-gateway-probe",
|
||||
"nym-node-status-client",
|
||||
"nym-sdk",
|
||||
"rand 0.8.5",
|
||||
"regex",
|
||||
"serde_json",
|
||||
@@ -7485,7 +7581,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-node-status-api"
|
||||
version = "4.3.0"
|
||||
version = "4.6.1"
|
||||
dependencies = [
|
||||
"ammonia",
|
||||
"anyhow",
|
||||
@@ -7550,9 +7646,9 @@ version = "0.3.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"bs58",
|
||||
"nym-credentials",
|
||||
"nym-crypto",
|
||||
"nym-gateway-probe",
|
||||
"reqwest 0.13.1",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -7790,6 +7886,8 @@ dependencies = [
|
||||
"nym-crypto",
|
||||
"nym-gateway-requests",
|
||||
"nym-http-api-client",
|
||||
"nym-ip-packet-requests",
|
||||
"nym-lp",
|
||||
"nym-network-defaults",
|
||||
"nym-ordered-buffer",
|
||||
"nym-service-providers-common",
|
||||
@@ -7802,8 +7900,10 @@ dependencies = [
|
||||
"nym-topology",
|
||||
"nym-validator-client",
|
||||
"parking_lot",
|
||||
"pnet_packet",
|
||||
"rand 0.8.5",
|
||||
"reqwest 0.13.1",
|
||||
"semver 1.0.27",
|
||||
"serde",
|
||||
"tap",
|
||||
"tempfile",
|
||||
@@ -7875,7 +7975,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.73"
|
||||
version = "1.1.76"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"clap",
|
||||
@@ -8143,7 +8243,7 @@ dependencies = [
|
||||
"tempfile",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"tracing-test",
|
||||
"windows 0.61.3",
|
||||
]
|
||||
|
||||
@@ -8673,7 +8773,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nymvisor"
|
||||
version = "0.1.38"
|
||||
version = "0.1.41"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -9652,7 +9752,7 @@ dependencies = [
|
||||
"quinn-proto",
|
||||
"quinn-udp",
|
||||
"rustc-hash",
|
||||
"rustls 0.23.29",
|
||||
"rustls 0.23.37",
|
||||
"socket2 0.5.10",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
@@ -9673,7 +9773,7 @@ dependencies = [
|
||||
"rand 0.9.2",
|
||||
"ring",
|
||||
"rustc-hash",
|
||||
"rustls 0.23.29",
|
||||
"rustls 0.23.37",
|
||||
"rustls-pki-types",
|
||||
"slab",
|
||||
"thiserror 2.0.12",
|
||||
@@ -9940,7 +10040,7 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"quinn",
|
||||
"rustls 0.23.29",
|
||||
"rustls 0.23.37",
|
||||
"rustls-native-certs 0.8.3",
|
||||
"rustls-pki-types",
|
||||
"serde",
|
||||
@@ -9981,7 +10081,7 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"quinn",
|
||||
"rustls 0.23.29",
|
||||
"rustls 0.23.37",
|
||||
"rustls-pki-types",
|
||||
"rustls-platform-verifier",
|
||||
"serde",
|
||||
@@ -10241,16 +10341,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.29"
|
||||
version = "0.23.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2491382039b29b9b11ff08b76ff6c97cf287671dbb74f0be44bda389fffe9bd1"
|
||||
checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4"
|
||||
dependencies = [
|
||||
"aws-lc-rs",
|
||||
"log",
|
||||
"once_cell",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki 0.103.4",
|
||||
"rustls-webpki 0.103.9",
|
||||
"subtle 2.6.1",
|
||||
"zeroize",
|
||||
]
|
||||
@@ -10331,14 +10431,14 @@ dependencies = [
|
||||
"jni",
|
||||
"log",
|
||||
"once_cell",
|
||||
"rustls 0.23.29",
|
||||
"rustls 0.23.37",
|
||||
"rustls-native-certs 0.8.3",
|
||||
"rustls-platform-verifier-android",
|
||||
"rustls-webpki 0.103.4",
|
||||
"rustls-webpki 0.103.9",
|
||||
"security-framework 3.6.0",
|
||||
"security-framework-sys",
|
||||
"webpki-root-certs",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10370,9 +10470,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.103.4"
|
||||
version = "0.103.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc"
|
||||
checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53"
|
||||
dependencies = [
|
||||
"aws-lc-rs",
|
||||
"ring",
|
||||
@@ -11063,6 +11163,47 @@ dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smolmix"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"hickory-proto",
|
||||
"hickory-resolver",
|
||||
"http-body-util",
|
||||
"hyper 1.6.0",
|
||||
"hyper-util",
|
||||
"nym-bin-common",
|
||||
"nym-ip-packet-requests",
|
||||
"nym-sdk",
|
||||
"reqwest 0.13.1",
|
||||
"rustls 0.23.37",
|
||||
"smoltcp",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
"tokio-rustls 0.26.2",
|
||||
"tokio-smoltcp",
|
||||
"tokio-tungstenite",
|
||||
"tracing",
|
||||
"webpki-roots 0.26.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smoltcp"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dad095989c1533c1c266d9b1e8d70a1329dd3723c3edac6d03bbd67e7bf6f4bb"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"byteorder",
|
||||
"cfg-if",
|
||||
"defmt 0.3.100",
|
||||
"heapless",
|
||||
"libc",
|
||||
"log",
|
||||
"managed",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "snafu"
|
||||
version = "0.7.5"
|
||||
@@ -11203,7 +11344,7 @@ dependencies = [
|
||||
"memchr",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"rustls 0.23.29",
|
||||
"rustls 0.23.37",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.10.9",
|
||||
@@ -12025,10 +12166,24 @@ version = "0.26.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b"
|
||||
dependencies = [
|
||||
"rustls 0.23.29",
|
||||
"rustls 0.23.37",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-smoltcp"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5f5d53da1c3095663a8900d86c2abb0ffe02d3f6aa86527b066148fcb33e65e"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"smoltcp",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-stream"
|
||||
version = "0.1.17"
|
||||
@@ -12843,7 +12998,7 @@ dependencies = [
|
||||
"serde",
|
||||
"tempfile",
|
||||
"textwrap",
|
||||
"toml 0.8.23",
|
||||
"toml 0.9.12+spec-1.1.0",
|
||||
"uniffi_internal_macros 0.31.0",
|
||||
"uniffi_meta 0.31.0",
|
||||
"uniffi_pipeline 0.31.0",
|
||||
@@ -12952,7 +13107,7 @@ dependencies = [
|
||||
"quote",
|
||||
"serde",
|
||||
"syn 2.0.106",
|
||||
"toml 0.8.23",
|
||||
"toml 0.9.12+spec-1.1.0",
|
||||
"uniffi_meta 0.31.0",
|
||||
]
|
||||
|
||||
|
||||
+16
-9
@@ -147,6 +147,7 @@ members = [
|
||||
"sdk/ffi/go",
|
||||
"sdk/ffi/shared",
|
||||
"sdk/rust/nym-sdk",
|
||||
"smolmix/core",
|
||||
"service-providers/common",
|
||||
"service-providers/ip-packet-router",
|
||||
"service-providers/network-requester",
|
||||
@@ -184,7 +185,6 @@ default-members = [
|
||||
"nym-api",
|
||||
"nym-credential-proxy/nym-credential-proxy",
|
||||
"nym-node",
|
||||
"nym-node-status-api/nym-node-status-agent",
|
||||
"nym-statistics-api",
|
||||
"nym-validator-rewarder",
|
||||
"nyx-chain-watcher",
|
||||
@@ -280,6 +280,7 @@ getrandom03 = { package = "getrandom", version = "=0.3.3" }
|
||||
glob = "0.3"
|
||||
handlebars = "3.5.5"
|
||||
hex = "0.4.3"
|
||||
hickory-proto = "0.25.2"
|
||||
hickory-resolver = "0.25.2"
|
||||
hkdf = "0.12.3"
|
||||
hmac = "0.12.1"
|
||||
@@ -334,6 +335,7 @@ rayon = "1.5.1"
|
||||
regex = "1.10.6"
|
||||
reqwest = { version = "0.13.1", default-features = false }
|
||||
rs_merkle = "1.5.0"
|
||||
rustls = { version = "0.23.37", default-features = false }
|
||||
schemars = "0.8.22"
|
||||
semver = "1.0.26"
|
||||
serde = "1.0.219"
|
||||
@@ -347,6 +349,8 @@ serde_yaml = "0.9.25"
|
||||
serde_plain = "1.0.2"
|
||||
sha2 = "0.10.3"
|
||||
si-scale = "0.2.3"
|
||||
smolmix = { version = "0.0.1", path = "smolmix/core" }
|
||||
smoltcp = "0.12"
|
||||
snow = "0.9.6"
|
||||
sphinx-packet = "=0.6.0"
|
||||
sqlx = "0.8.6"
|
||||
@@ -367,6 +371,8 @@ tokio-postgres = "0.7"
|
||||
tokio-stream = "0.1.17"
|
||||
tokio-test = "0.4.4"
|
||||
tokio-tun = "0.11.5"
|
||||
tokio-rustls = "0.26"
|
||||
tokio-smoltcp = "0.5"
|
||||
tokio-tungstenite = { version = "0.20.1" }
|
||||
tokio-util = "0.7.15"
|
||||
toml = "0.8.22"
|
||||
@@ -398,14 +404,14 @@ prometheus = { version = "0.14.0" }
|
||||
|
||||
|
||||
# libcrux
|
||||
libcrux-kem = { git = "https://github.com/cryspen/libcrux", rev = "b17f8687b67cdcfc10b55aeecc998bbbca28f775" }
|
||||
libcrux-ecdh = { git = "https://github.com/cryspen/libcrux", rev = "b17f8687b67cdcfc10b55aeecc998bbbca28f775" }
|
||||
libcrux-curve25519 = { git = "https://github.com/cryspen/libcrux", rev = "b17f8687b67cdcfc10b55aeecc998bbbca28f775" }
|
||||
libcrux-chacha20poly1305 = { git = "https://github.com/cryspen/libcrux", rev = "b17f8687b67cdcfc10b55aeecc998bbbca28f775" }
|
||||
libcrux-psq = { git = "https://github.com/cryspen/libcrux", rev = "b17f8687b67cdcfc10b55aeecc998bbbca28f775" }
|
||||
libcrux-ml-kem = { git = "https://github.com/cryspen/libcrux", rev = "b17f8687b67cdcfc10b55aeecc998bbbca28f775" }
|
||||
libcrux-sha3 = { git = "https://github.com/cryspen/libcrux", rev = "b17f8687b67cdcfc10b55aeecc998bbbca28f775" }
|
||||
libcrux-traits = { git = "https://github.com/cryspen/libcrux", rev = "b17f8687b67cdcfc10b55aeecc998bbbca28f775" }
|
||||
libcrux-kem = "0.0.7"
|
||||
libcrux-ecdh = "0.0.6"
|
||||
libcrux-curve25519 = "0.0.6"
|
||||
libcrux-chacha20poly1305 = "0.0.7"
|
||||
libcrux-psq = "0.0.8"
|
||||
libcrux-ml-kem = "0.0.8"
|
||||
libcrux-sha3 = "0.0.8"
|
||||
libcrux-traits = "0.0.8"
|
||||
|
||||
# Workspace dep definitions required by crates.io publication - we need a workspace version since `cargo workspaces` doesn't work with path imports from crate manifests
|
||||
nym-api-requests = { version = "1.20.4", path = "nym-api/nym-api-requests" }
|
||||
@@ -559,6 +565,7 @@ wasm-bindgen = "0.2.99"
|
||||
wasm-bindgen-futures = "0.4.49"
|
||||
wasm-bindgen-test = "0.3.49"
|
||||
wasmtimer = "0.4.1"
|
||||
webpki-roots = "0.26"
|
||||
web-sys = "0.3.76"
|
||||
|
||||
# for local development:
|
||||
|
||||
@@ -60,7 +60,8 @@ packages:
|
||||
## SYSTEM MAINTENANCE PLAYBOOK KNOBS
|
||||
###############################################################################
|
||||
|
||||
# nym_version: "v2025.21-mozzarella"
|
||||
# To use particular version instead of Latest, provide in such form:
|
||||
# nym_version: "nym-binaries-v2026.7-tola"
|
||||
|
||||
## NOTE:
|
||||
## if you want to pin Nym to a specific version instead of using the
|
||||
@@ -117,4 +118,4 @@ packages:
|
||||
|
||||
# enable_writeback_tuning: true
|
||||
# writeback_dirty_writeback_centisecs: 1500
|
||||
# writeback_dirty_expire_centisecs: 6000
|
||||
# writeback_dirty_expire_centisecs: 6000
|
||||
|
||||
@@ -1,11 +1,30 @@
|
||||
---
|
||||
- name: Configure tunnel manager
|
||||
- name: Ensure nym binaries directory exists
|
||||
file:
|
||||
path: /root/nym-binaries
|
||||
state: directory
|
||||
mode: "0755"
|
||||
tags:
|
||||
- tunnel
|
||||
- network_tunnel_manager
|
||||
become: true
|
||||
command:
|
||||
cmd: "/root/nym-binaries/network-tunnel-manager.sh {{ item }}"
|
||||
- ntm
|
||||
|
||||
- name: Download network tunnel manager
|
||||
get_url:
|
||||
url: "{{ tunnel_manager_url }}"
|
||||
dest: /root/nym-binaries/network-tunnel-manager.sh
|
||||
mode: "0755"
|
||||
force: yes
|
||||
tags:
|
||||
- tunnel
|
||||
- network_tunnel_manager
|
||||
- ntm
|
||||
|
||||
- name: Run network tunnel manager
|
||||
command: "/root/nym-binaries/network-tunnel-manager.sh {{ item }}"
|
||||
loop:
|
||||
- complete_networking_configuration
|
||||
register: tunnel_mgr
|
||||
failed_when: false
|
||||
tags:
|
||||
- tunnel
|
||||
- network_tunnel_manager
|
||||
- ntm
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.73"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
version = "1.1.76"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.85"
|
||||
license.workspace = true
|
||||
rust-version = "1.85"
|
||||
publish = false
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
[package]
|
||||
name = "nym-client-websocket-requests"
|
||||
description = "Request and response definitions for Nym client websocket connections"
|
||||
version.workspace = true
|
||||
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
description = "Request and response definitions for Nym client websocket connections"
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.73"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
||||
version = "1.1.76"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.85"
|
||||
license.workspace = true
|
||||
rust-version = "1.85"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
[package]
|
||||
name = "nym-async-file-watcher"
|
||||
description = "Simple file watcher that sends a notification whenever there was any change in the watched file"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
description = "Simple file watcher that sends a notification whenever there was any change in the watched file"
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
[package]
|
||||
name = "nym-authenticator-requests"
|
||||
description = "Crate defining requests and responses for the Nym authenticator client"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
description = "Crate defining requests and responses for the Nym authenticator client"
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
[dependencies]
|
||||
base64 = { workspace = true }
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
[package]
|
||||
name = "nym-bandwidth-controller"
|
||||
description = "Crate for controlling the use of zknym credentials to ensure constant bandwidth availability for NymVPN app"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
description = "Crate for controlling the use of zknym credentials to ensure constant bandwidth availability for NymVPN app"
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ where
|
||||
let signing_key = ed25519::PrivateKey::new(&mut rng);
|
||||
let expiration = expiration.unwrap_or_else(ecash_default_expiration_date);
|
||||
|
||||
let deposit_amount = client.get_required_deposit_amount().await?;
|
||||
let deposit_amount = client.get_default_deposit_amount().await?;
|
||||
info!("we'll need to deposit {deposit_amount} to obtain the ticketbook");
|
||||
let result = client
|
||||
.make_ticketbook_deposit(
|
||||
|
||||
@@ -25,6 +25,8 @@ pub trait BandwidthTicketProvider: Send + Sync {
|
||||
) -> Result<PreparedCredential, BandwidthControllerError>;
|
||||
|
||||
async fn get_upgrade_mode_token(&self) -> Result<Option<String>, BandwidthControllerError>;
|
||||
|
||||
async fn close(&self) {}
|
||||
}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
@@ -56,6 +58,10 @@ where
|
||||
.map_err(|_| BandwidthControllerError::MalformedUpgradeModeToken)?;
|
||||
Ok(Some(token))
|
||||
}
|
||||
|
||||
async fn close(&self) {
|
||||
self.storage.close().await;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
@@ -75,4 +81,8 @@ impl<T: BandwidthTicketProvider + ?Sized + Send> BandwidthTicketProvider for Box
|
||||
async fn get_upgrade_mode_token(&self) -> Result<Option<String>, BandwidthControllerError> {
|
||||
(**self).get_upgrade_mode_token().await
|
||||
}
|
||||
|
||||
async fn close(&self) {
|
||||
(**self).close().await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
[package]
|
||||
name = "nym-bin-common"
|
||||
version.workspace = true
|
||||
description = "Common code for nym binaries"
|
||||
edition = { workspace = true }
|
||||
version.workspace = true
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
license = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
[dependencies]
|
||||
clap = { workspace = true, features = ["derive"], optional = true }
|
||||
@@ -38,6 +43,7 @@ default = []
|
||||
openapi = ["utoipa"]
|
||||
output_format = ["serde_json", "dep:clap"]
|
||||
bin_info_schema = ["schemars"]
|
||||
ip_check = []
|
||||
basic_tracing = ["dep:tracing", "dep:tracing-subscriber"]
|
||||
otel-otlp = [
|
||||
"basic_tracing",
|
||||
|
||||
+13
-18
@@ -1,29 +1,12 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
|
||||
// use `ip` feature without nightly
|
||||
// issue: https://github.com/rust-lang/rust/issues/27709
|
||||
pub(crate) const fn is_global_ip(ip: &IpAddr) -> bool {
|
||||
pub const fn is_global_ip(ip: &IpAddr) -> bool {
|
||||
match ip {
|
||||
IpAddr::V4(addr) => is_global_ipv4(addr),
|
||||
IpAddr::V6(addr) => is_global_ipv6(addr),
|
||||
}
|
||||
}
|
||||
|
||||
const fn is_shared_ipv4(ip: &Ipv4Addr) -> bool {
|
||||
ip.octets()[0] == 100 && (ip.octets()[1] & 0b1100_0000 == 0b0100_0000)
|
||||
}
|
||||
|
||||
const fn is_benchmarking_ipv4(ip: &Ipv4Addr) -> bool {
|
||||
ip.octets()[0] == 198 && (ip.octets()[1] & 0xfe) == 18
|
||||
}
|
||||
|
||||
const fn is_reserved_ipv4(ip: &Ipv4Addr) -> bool {
|
||||
ip.octets()[0] & 240 == 240 && !ip.is_broadcast()
|
||||
}
|
||||
|
||||
const fn is_global_ipv4(ip: &Ipv4Addr) -> bool {
|
||||
!(ip.octets()[0] == 0 // "This network"
|
||||
|| ip.is_private()
|
||||
@@ -42,6 +25,18 @@ const fn is_global_ipv4(ip: &Ipv4Addr) -> bool {
|
||||
|| ip.is_broadcast())
|
||||
}
|
||||
|
||||
const fn is_shared_ipv4(ip: &Ipv4Addr) -> bool {
|
||||
ip.octets()[0] == 100 && (ip.octets()[1] & 0b1100_0000 == 0b0100_0000)
|
||||
}
|
||||
|
||||
const fn is_benchmarking_ipv4(ip: &Ipv4Addr) -> bool {
|
||||
ip.octets()[0] == 198 && (ip.octets()[1] & 0xfe) == 18
|
||||
}
|
||||
|
||||
const fn is_reserved_ipv4(ip: &Ipv4Addr) -> bool {
|
||||
ip.octets()[0] & 240 == 240 && !ip.is_broadcast()
|
||||
}
|
||||
|
||||
const fn is_documentation_ipv6(ip: &Ipv6Addr) -> bool {
|
||||
(ip.segments()[0] == 0x2001) && (ip.segments()[1] == 0xdb8)
|
||||
}
|
||||
@@ -9,3 +9,6 @@ pub mod completions;
|
||||
|
||||
#[cfg(feature = "output_format")]
|
||||
pub mod output_format;
|
||||
|
||||
#[cfg(feature = "ip_check")]
|
||||
pub mod ip_check;
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
[package]
|
||||
name = "nym-client-core"
|
||||
description = "Crate containing core client functionality and configs, used by all other Nym client implentations"
|
||||
version.workspace = true
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
edition = "2024"
|
||||
rust-version = "1.85"
|
||||
license.workspace = true
|
||||
description = "Crate containing core client functionality and configs, used by all other Nym client implentations"
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version = "1.85"
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
[package]
|
||||
name = "nym-client-core-config-types"
|
||||
description = "Low level configs and constants used by Nym clients and nodes"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
description = "Low level configs and constants used by Nym clients and nodes"
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
[package]
|
||||
name = "nym-client-core-gateways-storage"
|
||||
description = "Functionality for Nym clients to store and retrive Gateway connections"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
description = "Functionality for Nym clients to store and retrive Gateway connections"
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -1023,6 +1023,16 @@ where
|
||||
let encryption_keys = init_res.client_keys.encryption_keypair();
|
||||
let identity_keys = init_res.client_keys.identity_keypair();
|
||||
|
||||
let credential_store_for_close = credential_store.clone();
|
||||
let close_credential_token = shutdown_tracker.clone_shutdown_token();
|
||||
shutdown_tracker.try_spawn_named(
|
||||
async move {
|
||||
close_credential_token.cancelled().await;
|
||||
credential_store_for_close.close().await;
|
||||
},
|
||||
"CredentialStorage::close_on_shutdown",
|
||||
);
|
||||
|
||||
// the components are started in very specific order. Unless you know what you are doing,
|
||||
// do not change that.
|
||||
let bandwidth_controller = self
|
||||
|
||||
@@ -11,11 +11,17 @@ use nym_bandwidth_controller::BandwidthController;
|
||||
use nym_client_core_gateways_storage::OnDiskGatewaysDetails;
|
||||
use nym_credential_storage::storage::Storage as CredentialStorage;
|
||||
use nym_validator_client::{QueryHttpRpcNyxdClient, nyxd};
|
||||
use std::{io, path::Path};
|
||||
use std::{io, path::Path, time::Duration};
|
||||
use time::OffsetDateTime;
|
||||
use tracing::{error, info, trace};
|
||||
use url::Url;
|
||||
|
||||
/// Maximum rename retry attempts when the database file is temporarily locked.
|
||||
const ARCHIVE_MAX_RETRY_ATTEMPTS: u8 = 15;
|
||||
|
||||
/// Delay between archive rename retry attempts.
|
||||
const ARCHIVE_RETRY_DELAY: Duration = Duration::from_millis(200);
|
||||
|
||||
async fn setup_fresh_backend<P: AsRef<Path>>(
|
||||
db_path: P,
|
||||
surb_config: &config::ReplySurbs,
|
||||
@@ -74,13 +80,58 @@ async fn archive_corrupted_database<P: AsRef<Path>>(db_path: P) -> io::Result<()
|
||||
};
|
||||
let renamed = db_path.with_extension(new_extension);
|
||||
|
||||
tokio::fs::rename(db_path, &renamed).await.inspect_err(|_| {
|
||||
error!(
|
||||
"Failed to rename corrupt database file: {} to {}",
|
||||
db_path.display(),
|
||||
renamed.display()
|
||||
);
|
||||
})
|
||||
// On Windows, sqlx may release its OS file handles asynchronously after
|
||||
// pool.close() returns, briefly keeping the file locked
|
||||
// (ERROR_SHARING_VIOLATION, os error 32). Retry with a short delay to
|
||||
// give the OS time to flush the remaining handles.
|
||||
for attempt in 0..ARCHIVE_MAX_RETRY_ATTEMPTS {
|
||||
match tokio::fs::rename(db_path, &renamed).await {
|
||||
Ok(()) => return Ok(()),
|
||||
Err(e) if is_file_locked_error(&e) && (attempt + 1) < ARCHIVE_MAX_RETRY_ATTEMPTS => {
|
||||
trace!(
|
||||
"Database file is temporarily locked, retrying archive \
|
||||
(attempt {}/{}): {e}",
|
||||
attempt + 1,
|
||||
ARCHIVE_MAX_RETRY_ATTEMPTS
|
||||
);
|
||||
tokio::time::sleep(ARCHIVE_RETRY_DELAY).await;
|
||||
}
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Failed to rename corrupt database file: {} to {}",
|
||||
db_path.display(),
|
||||
renamed.display()
|
||||
);
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reached only when every attempt was blocked by a file lock.
|
||||
error!(
|
||||
"Failed to rename corrupt database file after {} attempts: {} to {}",
|
||||
ARCHIVE_MAX_RETRY_ATTEMPTS,
|
||||
db_path.display(),
|
||||
renamed.display()
|
||||
);
|
||||
Err(io::Error::other(
|
||||
"corrupt database archive blocked by persistent file lock",
|
||||
))
|
||||
}
|
||||
|
||||
/// Returns `true` when the IO error indicates a temporary file lock held by another handle
|
||||
/// within the same process. Only meaningful on Windows; always `false` elsewhere.
|
||||
fn is_file_locked_error(e: &io::Error) -> bool {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
// ERROR_SHARING_VIOLATION = 32, ERROR_LOCK_VIOLATION = 33
|
||||
matches!(e.raw_os_error(), Some(32) | Some(33))
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
let _ = e;
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn setup_fs_reply_surb_backend<P: AsRef<Path>>(
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
[package]
|
||||
name = "nym-client-core-surb-storage"
|
||||
description = "Functionality for Nym clients to generate and use Single Use Reply Blocks (SURBs)"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
description = "Functionality for Nym clients to generate and use Single Use Reply Blocks (SURBs)"
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -337,6 +337,8 @@ impl ReplyStorageBackend for Backend {
|
||||
}
|
||||
|
||||
async fn stop_storage_session(self) -> Result<(), Self::StorageError> {
|
||||
self.stop_client_use().await
|
||||
let result = self.stop_client_use().await;
|
||||
self.shutdown().await;
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ where
|
||||
debug!("Started PersistentReplyStorage");
|
||||
if let Err(err) = self.backend.start_storage_session().await {
|
||||
error!("failed to start the storage session - {err}");
|
||||
self.backend.stop_storage_session().await.ok();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -55,10 +56,11 @@ where
|
||||
|
||||
info!("PersistentReplyStorage is flushing all reply-related data to underlying storage");
|
||||
if let Err(err) = self.backend.flush_surb_storage(&mem_state).await {
|
||||
error!("failed to flush our reply-related data to the persistent storage: {err}")
|
||||
} else {
|
||||
info!("Data flush is complete")
|
||||
error!("failed to flush our reply-related data to the persistent storage: {err}");
|
||||
self.backend.stop_storage_session().await.ok();
|
||||
return;
|
||||
}
|
||||
info!("Data flush is complete");
|
||||
|
||||
if let Err(err) = self.backend.stop_storage_session().await {
|
||||
error!("failed to properly stop the storage session - {err}. We might not be able to smoothly restore it")
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
[package]
|
||||
name = "nym-gateway-client"
|
||||
description = "Functions and types for Nym client <> Gateway connections"
|
||||
version.workspace = true
|
||||
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
description = "Functions and types for Nym client <> Gateway connections"
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
[package]
|
||||
name = "nym-mixnet-client"
|
||||
description = "Client for Mix Node <> Mix Node & Mix Node <> Gateway communication"
|
||||
version.workspace = true
|
||||
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
description = "Client for Mix Node <> Mix Node & Mix Node <> Gateway communication"
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -342,7 +342,7 @@ impl SendWithoutResponse for Client {
|
||||
sending_res.map_err(|err| {
|
||||
match err {
|
||||
TrySendError::Full(_) => {
|
||||
warn!(
|
||||
trace!(
|
||||
event = "mixclient.try_send",
|
||||
peer = %address,
|
||||
result = "full_dropped",
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
[package]
|
||||
name = "nym-validator-client"
|
||||
description = "Client for interacting with Nyx Cosmos SDK blockchain"
|
||||
version.workspace = true
|
||||
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.85"
|
||||
license.workspace = true
|
||||
description = "Client for interacting with Nyx Cosmos SDK blockchain"
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version = "1.85"
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ use crate::nyxd::CosmWasmClient;
|
||||
use async_trait::async_trait;
|
||||
use cosmwasm_std::Coin;
|
||||
use nym_ecash_contract_common::deposit::LatestDepositResponse;
|
||||
use nym_ecash_contract_common::deposit_statistics::DepositsStatistics;
|
||||
use nym_ecash_contract_common::msg::QueryMsg as EcashQueryMsg;
|
||||
use serde::Deserialize;
|
||||
|
||||
@@ -17,6 +18,9 @@ pub use nym_ecash_contract_common::blacklist::{
|
||||
pub use nym_ecash_contract_common::deposit::{
|
||||
Deposit, DepositData, DepositId, DepositResponse, PagedDepositsResponse,
|
||||
};
|
||||
pub use nym_ecash_contract_common::reduced_deposit::{
|
||||
WhitelistedAccount, WhitelistedAccountsResponse,
|
||||
};
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
@@ -42,8 +46,18 @@ pub trait EcashQueryClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_required_deposit_amount(&self) -> Result<Coin, NyxdError> {
|
||||
self.query_ecash_contract(EcashQueryMsg::GetRequiredDepositAmount {})
|
||||
async fn get_default_deposit_amount(&self) -> Result<Coin, NyxdError> {
|
||||
self.query_ecash_contract(EcashQueryMsg::GetDefaultDepositAmount {})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_reduced_deposit_amount(&self, address: String) -> Result<Option<Coin>, NyxdError> {
|
||||
self.query_ecash_contract(EcashQueryMsg::GetReducedDepositAmount { address })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_all_whitelisted_accounts(&self) -> Result<WhitelistedAccountsResponse, NyxdError> {
|
||||
self.query_ecash_contract(EcashQueryMsg::GetAllWhitelistedAccounts {})
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -65,6 +79,11 @@ pub trait EcashQueryClient {
|
||||
self.query_ecash_contract(EcashQueryMsg::GetDepositsPaged { start_after, limit })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_deposits_statistics(&self) -> Result<DepositsStatistics, NyxdError> {
|
||||
self.query_ecash_contract(EcashQueryMsg::GetDepositsStatistics {})
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
@@ -122,10 +141,17 @@ mod tests {
|
||||
EcashQueryMsg::GetDepositsPaged { limit, start_after } => {
|
||||
client.get_deposits_paged(start_after, limit).ignore()
|
||||
}
|
||||
EcashQueryMsg::GetRequiredDepositAmount {} => {
|
||||
client.get_required_deposit_amount().ignore()
|
||||
EcashQueryMsg::GetDefaultDepositAmount {} => {
|
||||
client.get_default_deposit_amount().ignore()
|
||||
}
|
||||
EcashQueryMsg::GetReducedDepositAmount { address } => {
|
||||
client.get_reduced_deposit_amount(address).ignore()
|
||||
}
|
||||
EcashQueryMsg::GetAllWhitelistedAccounts {} => {
|
||||
client.get_all_whitelisted_accounts().ignore()
|
||||
}
|
||||
EcashQueryMsg::GetLatestDeposit {} => client.get_latest_deposit().ignore(),
|
||||
EcashQueryMsg::GetDepositsStatistics {} => client.get_deposits_statistics().ignore(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+42
-2
@@ -62,13 +62,47 @@ pub trait EcashSigningClient {
|
||||
new_deposit: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
let req = EcashExecuteMsg::UpdateDepositValue {
|
||||
let req = EcashExecuteMsg::UpdateDefaultDepositValue {
|
||||
new_deposit: new_deposit.into(),
|
||||
};
|
||||
self.execute_ecash_contract(fee, req, "Ecash::UpdateDepositValue".to_string(), vec![])
|
||||
.await
|
||||
}
|
||||
|
||||
async fn set_reduced_deposit_price(
|
||||
&self,
|
||||
address: String,
|
||||
deposit: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
let req = EcashExecuteMsg::SetReducedDepositPrice {
|
||||
address,
|
||||
deposit: deposit.into(),
|
||||
};
|
||||
self.execute_ecash_contract(
|
||||
fee,
|
||||
req,
|
||||
"Ecash::SetReducedDepositPrice".to_string(),
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn remove_reduced_deposit_price(
|
||||
&self,
|
||||
address: String,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
let req = EcashExecuteMsg::RemoveReducedDepositPrice { address };
|
||||
self.execute_ecash_contract(
|
||||
fee,
|
||||
req,
|
||||
"Ecash::RemoveReducedDepositPrice".to_string(),
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn propose_for_blacklist(
|
||||
&self,
|
||||
public_key: String,
|
||||
@@ -141,9 +175,15 @@ mod tests {
|
||||
.ignore(),
|
||||
ExecuteMsg::RedeemTickets { .. } => unimplemented!(), // no redeem tickets method for the client
|
||||
ExecuteMsg::UpdateAdmin { admin } => client.update_admin(admin, None).ignore(),
|
||||
ExecuteMsg::UpdateDepositValue { new_deposit } => client
|
||||
ExecuteMsg::UpdateDefaultDepositValue { new_deposit } => client
|
||||
.update_deposit_value(new_deposit.into(), None)
|
||||
.ignore(),
|
||||
ExecuteMsg::SetReducedDepositPrice { address, deposit } => client
|
||||
.set_reduced_deposit_price(address, deposit.into(), None)
|
||||
.ignore(),
|
||||
ExecuteMsg::RemoveReducedDepositPrice { address } => {
|
||||
client.remove_reduced_deposit_price(address, None).ignore()
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
[package]
|
||||
name = "nym-cli-commands"
|
||||
description = "Common commands crate used by the nym-cli tool for interacting with the Nyx Cosmos SDK blockchain and Mixnet endpoints"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
description = "Common commands crate used by the nym-cli tool for interacting with the Nyx Cosmos SDK blockchain and Mixnet endpoints"
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
[package]
|
||||
name = "nym-config"
|
||||
description = "Config related helpers and functions"
|
||||
version.workspace = true
|
||||
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
description = "Config related helpers and functions"
|
||||
documentation.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
[package]
|
||||
name = "nym-coconut-dkg-common"
|
||||
description = "Common crate for Nym's DKG cosmwasm contract"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
description = "Common crate for Nym's DKG cosmwasm contract"
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version = "1.85"
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
[package]
|
||||
name = "nym-contracts-common-testing"
|
||||
description = "Common crate for cosmwasm contract tests"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
rust-version = "1.85"
|
||||
readme.workspace = true
|
||||
description = "Common crate for cosmwasm contract tests"
|
||||
publish = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
[package]
|
||||
name = "nym-contracts-common"
|
||||
version.workspace = true
|
||||
description = "Common library for Nym cosmwasm contracts"
|
||||
edition = { workspace = true }
|
||||
version.workspace = true
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
license = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version = "1.85"
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
[dependencies]
|
||||
bs58 = { workspace = true }
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name = "easy-addr"
|
||||
version.workspace = true
|
||||
edition = "2021"
|
||||
publish = false
|
||||
license.workspace = true
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
[package]
|
||||
name = "nym-ecash-contract-common"
|
||||
description = "Common crate for Nym's ecash/zknym cosmwasm contract"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
description = "Common crate for Nym's ecash/zknym cosmwasm contract"
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version = "1.85"
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -6,6 +6,14 @@ use cosmwasm_std::Coin;
|
||||
|
||||
#[cw_serde]
|
||||
pub struct PoolCounters {
|
||||
/// Represents the total amount of funds deposited into the contract.
|
||||
pub total_deposited: Coin,
|
||||
|
||||
/// Represents the total amount of funds redeemed from the contract that got transferred into the holding account.
|
||||
pub total_redeemed: Coin,
|
||||
|
||||
/// Represents the total amount of tickets requested to be redeemed from the contract and get moved into the holding account,
|
||||
/// after that functionality got disabled.
|
||||
#[serde(default)]
|
||||
pub tickets_requested_and_not_redeemed: u64,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use cosmwasm_std::Coin;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Aggregate statistics about all deposits made through the ecash contract.
|
||||
#[cw_serde]
|
||||
pub struct DepositsStatistics {
|
||||
/// Total number of deposits ever made (at any price tier),
|
||||
/// derived from the deposit id counter.
|
||||
pub total_deposits_made: u32,
|
||||
|
||||
/// Total value of all deposits ever made (at any price tier),
|
||||
/// sourced from `PoolCounters::total_deposited`.
|
||||
pub total_deposited: Coin,
|
||||
|
||||
/// Number of deposits made at the default (non-reduced) price.
|
||||
pub total_deposits_made_with_default_price: u32,
|
||||
|
||||
/// Total value deposited at the default price.
|
||||
pub total_deposited_with_default_price: Coin,
|
||||
|
||||
/// Number of deposits made at any custom (reduced) price, summed across all whitelisted accounts.
|
||||
pub total_deposits_made_with_custom_price: u32,
|
||||
|
||||
/// Total value deposited at custom prices, summed across all whitelisted accounts.
|
||||
pub total_deposited_with_custom_price: Coin,
|
||||
|
||||
/// Per-account breakdown of deposit counts for whitelisted addresses.
|
||||
// note: we use String for addressing due to serialisation incompatibility
|
||||
pub deposits_made_with_custom_price: HashMap<String, u32>,
|
||||
|
||||
/// Per-account breakdown of deposited amounts for whitelisted addresses.
|
||||
// note: we use String for addressing due to serialisation incompatibility
|
||||
pub deposited_with_custom_price: HashMap<String, Coin>,
|
||||
}
|
||||
@@ -65,4 +65,26 @@ pub enum EcashContractError {
|
||||
|
||||
#[error("the account blacklisting hasn't been fully implemented yet")]
|
||||
UnimplementedBlacklisting,
|
||||
|
||||
#[error("reduced deposit must use the same denom as the default deposit (expected '{expected}', got '{got}')")]
|
||||
InvalidReducedDepositDenom { expected: String, got: String },
|
||||
|
||||
#[error(
|
||||
"reduced deposit amount ({reduced}) must be strictly less than the default ({default})"
|
||||
)]
|
||||
ReducedDepositNotReduced {
|
||||
reduced: cosmwasm_std::Uint128,
|
||||
default: cosmwasm_std::Uint128,
|
||||
},
|
||||
|
||||
#[error("address '{address}' does not have a custom reduced deposit price set")]
|
||||
NoReducedDepositPrice { address: String },
|
||||
|
||||
#[error(
|
||||
"deposit amount ({amount}) must be at least the ticket book size ({ticket_book_size})"
|
||||
)]
|
||||
DepositBelowTicketBookSize {
|
||||
amount: cosmwasm_std::Uint128,
|
||||
ticket_book_size: u64,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
pub mod blacklist;
|
||||
pub mod counters;
|
||||
pub mod deposit;
|
||||
pub mod deposit_statistics;
|
||||
pub mod error;
|
||||
pub mod event_attributes;
|
||||
pub mod events;
|
||||
pub mod msg;
|
||||
pub mod redeem_credential;
|
||||
pub mod reduced_deposit;
|
||||
|
||||
pub use error::EcashContractError;
|
||||
|
||||
@@ -9,6 +9,10 @@ use crate::blacklist::{BlacklistedAccountResponse, PagedBlacklistedAccountRespon
|
||||
#[cfg(feature = "schema")]
|
||||
use crate::deposit::{DepositResponse, LatestDepositResponse, PagedDepositsResponse};
|
||||
#[cfg(feature = "schema")]
|
||||
use crate::deposit_statistics::DepositsStatistics;
|
||||
#[cfg(feature = "schema")]
|
||||
use crate::reduced_deposit::WhitelistedAccountsResponse;
|
||||
#[cfg(feature = "schema")]
|
||||
use cosmwasm_schema::QueryResponses;
|
||||
|
||||
#[cw_serde]
|
||||
@@ -42,10 +46,25 @@ pub enum ExecuteMsg {
|
||||
admin: String,
|
||||
},
|
||||
|
||||
UpdateDepositValue {
|
||||
#[serde(alias = "update_deposit_value")]
|
||||
UpdateDefaultDepositValue {
|
||||
new_deposit: Coin,
|
||||
},
|
||||
|
||||
/// Set (or overwrite) a reduced deposit price for a specific address.
|
||||
/// Only callable by the contract admin.
|
||||
SetReducedDepositPrice {
|
||||
address: String,
|
||||
deposit: Coin,
|
||||
},
|
||||
|
||||
/// Remove the reduced deposit price for a specific address, reverting them to
|
||||
/// the default price. Returns an error if the address has no custom price set.
|
||||
/// Only callable by the contract admin.
|
||||
RemoveReducedDepositPrice {
|
||||
address: String,
|
||||
},
|
||||
|
||||
// TODO: properly implement
|
||||
ProposeToBlacklist {
|
||||
public_key: String,
|
||||
@@ -68,7 +87,15 @@ pub enum QueryMsg {
|
||||
},
|
||||
|
||||
#[cfg_attr(feature = "schema", returns(Coin))]
|
||||
GetRequiredDepositAmount {},
|
||||
#[serde(alias = "get_required_deposit_amount")]
|
||||
#[serde(alias = "GetRequiredDepositAmount")]
|
||||
GetDefaultDepositAmount {},
|
||||
|
||||
#[cfg_attr(feature = "schema", returns(Option<Coin>))]
|
||||
GetReducedDepositAmount { address: String },
|
||||
|
||||
#[cfg_attr(feature = "schema", returns(WhitelistedAccountsResponse))]
|
||||
GetAllWhitelistedAccounts {},
|
||||
|
||||
#[cfg_attr(feature = "schema", returns(DepositResponse))]
|
||||
GetDeposit { deposit_id: u32 },
|
||||
@@ -81,7 +108,22 @@ pub enum QueryMsg {
|
||||
limit: Option<u32>,
|
||||
start_after: Option<u32>,
|
||||
},
|
||||
|
||||
#[cfg_attr(feature = "schema", returns(DepositsStatistics))]
|
||||
GetDepositsStatistics {},
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct MigrateMsg {}
|
||||
pub struct MigrateMsg {
|
||||
/// Initial set of whitelisted accounts with their reduced deposit prices.
|
||||
/// Each entry is validated and stored during migration.
|
||||
pub initial_whitelist: Vec<WhitelistedDeposit>,
|
||||
}
|
||||
|
||||
/// An address and its reduced deposit price, used when seeding the whitelist
|
||||
/// via migration.
|
||||
#[cw_serde]
|
||||
pub struct WhitelistedDeposit {
|
||||
pub address: String,
|
||||
pub deposit: Coin,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use cosmwasm_std::{Addr, Coin};
|
||||
|
||||
#[cw_serde]
|
||||
pub struct WhitelistedAccount {
|
||||
pub address: Addr,
|
||||
pub deposit: Coin,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct WhitelistedAccountsResponse {
|
||||
pub whitelisted_accounts: Vec<WhitelistedAccount>,
|
||||
}
|
||||
@@ -1,12 +1,16 @@
|
||||
[package]
|
||||
name = "nym-group-contract-common"
|
||||
description = "Common crate for Nym's group cosmwasm contract"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
description = "Common crate for Nym's group cosmwasm contract"
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version = "1.85"
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
[dependencies]
|
||||
cosmwasm-schema = { workspace = true }
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
[package]
|
||||
name = "nym-mixnet-contract-common"
|
||||
version.workspace = true
|
||||
description = "Common library for the Nym mixnet contract"
|
||||
rust-version = "1.85"
|
||||
edition = { workspace = true }
|
||||
version.workspace = true
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
license = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version = "1.85"
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
[dependencies]
|
||||
bs58 = { workspace = true }
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
[package]
|
||||
name = "nym-multisig-contract-common"
|
||||
description = "Common code for the Nym multisig CosmWasm smart contract"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
description = "Common code for the Nym multisig CosmWasm smart contract"
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version = "1.85"
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
[dependencies]
|
||||
cosmwasm-schema = { workspace = true }
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
[package]
|
||||
name = "nym-performance-contract-common"
|
||||
description = "Common crate for Nym's group performance contract"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
rust-version = "1.85"
|
||||
readme.workspace = true
|
||||
description = "Common crate for Nym's group performance contract"
|
||||
publish = true
|
||||
|
||||
[dependencies]
|
||||
thiserror = { workspace = true }
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
[package]
|
||||
name = "nym-pool-contract-common"
|
||||
version.workspace = true
|
||||
description = "Common library for the Nym Pool contract"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
rust-version = "1.85"
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
[dependencies]
|
||||
thiserror = { workspace = true }
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
[package]
|
||||
name = "nym-vesting-contract-common"
|
||||
version.workspace = true
|
||||
description = "Common library for the Nym vesting contract"
|
||||
edition = { workspace = true }
|
||||
version.workspace = true
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
license = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version = "1.85"
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
[dependencies]
|
||||
cosmwasm-std = { workspace = true }
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
[package]
|
||||
name = "nym-credential-proxy-lib"
|
||||
description = "Build script and core functionality of the Nym Credential Proxy"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
description = "Build script and core functionality of the Nym Credential Proxy"
|
||||
publish = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::helpers::LockTimer;
|
||||
use nym_ecash_contract_common::msg::ExecuteMsg;
|
||||
use nym_validator_client::nyxd::contract_traits::NymContractsProvider;
|
||||
use nym_validator_client::nyxd::cosmwasm_client::types::ExecuteResult;
|
||||
use nym_validator_client::nyxd::{Coin, Config, CosmWasmClient, NyxdClient};
|
||||
use nym_validator_client::nyxd::{AccountId, Coin, Config, CosmWasmClient, NyxdClient};
|
||||
use nym_validator_client::{DirectSigningHttpRpcNyxdClient, nyxd};
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
@@ -50,6 +50,10 @@ impl ChainClient {
|
||||
Ok(ChainClient(Arc::new(RwLock::new(client))))
|
||||
}
|
||||
|
||||
pub async fn address(&self) -> AccountId {
|
||||
self.0.read().await.address()
|
||||
}
|
||||
|
||||
pub async fn query_chain(&self) -> ChainReadPermit<'_> {
|
||||
let _acquire_timer = LockTimer::new("acquire chain query permit");
|
||||
self.0.read().await
|
||||
|
||||
@@ -8,6 +8,7 @@ use nym_validator_client::nyxd::contract_traits::EcashQueryClient;
|
||||
use std::sync::Arc;
|
||||
use time::OffsetDateTime;
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::{info, warn};
|
||||
|
||||
pub struct CachedDeposit {
|
||||
valid_until: OffsetDateTime,
|
||||
@@ -56,13 +57,29 @@ impl RequiredDepositCache {
|
||||
|
||||
// update cache
|
||||
drop(read_guard);
|
||||
|
||||
let address = chain_client.address().await;
|
||||
info!("checking deposit required by {address}");
|
||||
let mut write_guard = self.inner.write().await;
|
||||
let deposit_amount = chain_client
|
||||
.query_chain()
|
||||
.await
|
||||
.get_required_deposit_amount()
|
||||
|
||||
let read_permit = chain_client.query_chain().await;
|
||||
let reduced = read_permit
|
||||
.get_reduced_deposit_amount(address.to_string())
|
||||
.await?;
|
||||
|
||||
let deposit_amount = match reduced {
|
||||
Some(reduced) => {
|
||||
info!("we're permitted to use reduced price");
|
||||
reduced
|
||||
}
|
||||
None => {
|
||||
warn!(
|
||||
"using default deposit value {address} is not whitelisted for price reduction"
|
||||
);
|
||||
read_permit.get_default_deposit_amount().await?
|
||||
}
|
||||
};
|
||||
|
||||
let nym_coin: Coin = deposit_amount.into();
|
||||
|
||||
write_guard.update(nym_coin.clone());
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
[package]
|
||||
name = "nym-credential-storage"
|
||||
description = "Crate for handling and storing spent and unspent zknym ticketbooks"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
description = "Crate for handling and storing spent and unspent zknym ticketbooks"
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
[package]
|
||||
name = "nym-credential-utils"
|
||||
description = "Utils crate for dealing with zknym credentials"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
description = "Utils crate for dealing with zknym credentials"
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
[package]
|
||||
name = "nym-credential-verification"
|
||||
description = "Store and verify zknym credentials"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
description = "Store and verify zknym credentials"
|
||||
publish = true
|
||||
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
|
||||
@@ -3,25 +3,19 @@
|
||||
|
||||
use crate::Error;
|
||||
use crate::ecash::error::EcashTicketError;
|
||||
use crate::ecash::helpers::for_each_api_concurrent;
|
||||
use crate::ecash::state::SharedState;
|
||||
use cosmwasm_std::Fraction;
|
||||
use cw_utils::ThresholdResponse;
|
||||
use futures::channel::mpsc::UnboundedReceiver;
|
||||
use futures::{Stream, StreamExt};
|
||||
use nym_api_requests::constants::MIN_BATCH_REDEMPTION_DELAY;
|
||||
use nym_api_requests::ecash::models::{BatchRedeemTicketsBody, VerifyEcashTicketBody};
|
||||
use nym_api_requests::ecash::models::VerifyEcashTicketBody;
|
||||
use nym_credentials_interface::Bandwidth;
|
||||
use nym_credentials_interface::{ClientTicket, TicketType};
|
||||
use nym_validator_client::EcashApiClient;
|
||||
use nym_validator_client::coconut::EcashApiError;
|
||||
use nym_validator_client::nym_api::{EpochId, NymApiClientExt};
|
||||
use nym_validator_client::nym_api::NymApiClientExt;
|
||||
use nym_validator_client::nyxd::AccountId;
|
||||
use nym_validator_client::nyxd::contract_traits::{
|
||||
EcashSigningClient, MultisigQueryClient, MultisigSigningClient, PagedMultisigQueryClient,
|
||||
};
|
||||
use nym_validator_client::nyxd::cosmwasm_client::ContractResponseData;
|
||||
use nym_validator_client::nyxd::cw3::Status;
|
||||
use nym_validator_client::nyxd::contract_traits::MultisigQueryClient;
|
||||
use si_scale::helpers::bibytes2;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::ops::Deref;
|
||||
@@ -31,22 +25,6 @@ use tokio::sync::{Mutex, RwLockReadGuard};
|
||||
use tokio::time::{Duration, Instant, interval_at};
|
||||
use tracing::{debug, error, info, instrument, trace, warn};
|
||||
|
||||
enum ProposalResult {
|
||||
Executed,
|
||||
Rejected,
|
||||
Pending,
|
||||
}
|
||||
|
||||
impl ProposalResult {
|
||||
fn is_pending(&self) -> bool {
|
||||
matches!(self, ProposalResult::Pending)
|
||||
}
|
||||
|
||||
fn is_rejected(&self) -> bool {
|
||||
matches!(self, ProposalResult::Rejected)
|
||||
}
|
||||
}
|
||||
|
||||
struct PendingVerification {
|
||||
ticket: ClientTicket,
|
||||
|
||||
@@ -68,43 +46,6 @@ impl PendingVerification {
|
||||
}
|
||||
}
|
||||
|
||||
struct PendingRedemptionVote {
|
||||
proposal_id: u64,
|
||||
digest: Vec<u8>,
|
||||
included_serial_numbers: Vec<Vec<u8>>,
|
||||
epoch_id: EpochId,
|
||||
|
||||
// vec of node ids of apis that haven't sent a valid response
|
||||
pending: Vec<u64>,
|
||||
}
|
||||
|
||||
impl PendingRedemptionVote {
|
||||
fn new(
|
||||
proposal_id: u64,
|
||||
digest: Vec<u8>,
|
||||
included_serial_numbers: Vec<Vec<u8>>,
|
||||
epoch_id: EpochId,
|
||||
pending: Vec<u64>,
|
||||
) -> Self {
|
||||
PendingRedemptionVote {
|
||||
proposal_id,
|
||||
digest,
|
||||
included_serial_numbers,
|
||||
epoch_id,
|
||||
pending,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_request_body(&self, gateway_cosmos_addr: AccountId) -> BatchRedeemTicketsBody {
|
||||
BatchRedeemTicketsBody::new(
|
||||
self.digest.clone(),
|
||||
self.proposal_id,
|
||||
self.included_serial_numbers.clone(),
|
||||
gateway_cosmos_addr,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CredentialHandlerConfig {
|
||||
/// Specifies the multiplier for revoking a malformed/double-spent ticket
|
||||
/// (if it has to go all the way to the nym-api for verification)
|
||||
@@ -132,7 +73,6 @@ pub struct CredentialHandler {
|
||||
ticket_receiver: UnboundedReceiver<ClientTicket>,
|
||||
shared_state: SharedState,
|
||||
pending_tickets: Vec<PendingVerification>,
|
||||
pending_redemptions: Vec<PendingRedemptionVote>,
|
||||
}
|
||||
|
||||
impl CredentialHandler {
|
||||
@@ -184,75 +124,6 @@ impl CredentialHandler {
|
||||
Ok(pending)
|
||||
}
|
||||
|
||||
async fn rebuild_pending_votes(
|
||||
shared_state: &SharedState,
|
||||
) -> Result<Vec<PendingRedemptionVote>, EcashTicketError> {
|
||||
// 1. get all tickets that were not fully verified
|
||||
let unverified = shared_state.storage.get_all_unresolved_proposals().await?;
|
||||
let mut pending = Vec::with_capacity(unverified.len());
|
||||
|
||||
let epoch_id = shared_state.current_epoch_id().await?;
|
||||
let apis = shared_state
|
||||
.api_clients(epoch_id)
|
||||
.await?
|
||||
.iter()
|
||||
.map(|s| (s.cosmos_address.to_string(), s.node_id))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for proposal_id in unverified {
|
||||
// get all of the votes
|
||||
let votes = shared_state
|
||||
.start_query()
|
||||
.await
|
||||
.get_all_votes(proposal_id as u64)
|
||||
.await
|
||||
.map_err(EcashTicketError::chain_query_failure)?
|
||||
.into_iter()
|
||||
.map(|v| v.voter)
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
let mut missing_votes = Vec::new();
|
||||
|
||||
// see who hasn't voted
|
||||
for (api_address, api_id) in &apis {
|
||||
// for each signer, check if they have actually voted; if not, that's the missing guy
|
||||
if !votes.contains(api_address) {
|
||||
missing_votes.push(*api_id)
|
||||
}
|
||||
}
|
||||
|
||||
// attempt to rebuild SN and digest from the proposal info + storage data
|
||||
let proposal_info = shared_state
|
||||
.start_query()
|
||||
.await
|
||||
.query_proposal(proposal_id as u64)
|
||||
.await
|
||||
.map_err(EcashTicketError::chain_query_failure)?;
|
||||
|
||||
let tickets = shared_state
|
||||
.storage
|
||||
.get_all_proposed_tickets_with_sn(proposal_id as u32)
|
||||
.await?;
|
||||
let digest =
|
||||
BatchRedeemTicketsBody::make_digest(tickets.iter().map(|t| &t.serial_number));
|
||||
let encoded_digest = bs58::encode(&digest).into_string();
|
||||
if encoded_digest != proposal_info.description {
|
||||
error!("the lost proposal {proposal_id} does not have a matching digest!");
|
||||
continue;
|
||||
}
|
||||
|
||||
pending.push(PendingRedemptionVote {
|
||||
proposal_id: proposal_id as u64,
|
||||
digest,
|
||||
included_serial_numbers: tickets.into_iter().map(|t| t.serial_number).collect(),
|
||||
epoch_id,
|
||||
pending: missing_votes,
|
||||
})
|
||||
}
|
||||
|
||||
Ok(pending)
|
||||
}
|
||||
|
||||
pub(crate) async fn new(
|
||||
config: CredentialHandlerConfig,
|
||||
ticket_receiver: UnboundedReceiver<ClientTicket>,
|
||||
@@ -276,51 +147,15 @@ impl CredentialHandler {
|
||||
// on startup read pending credentials and api responses from the storage
|
||||
let pending_tickets = Self::rebuild_pending_tickets(&shared_state).await?;
|
||||
|
||||
// on startup read pending proposals from the storage
|
||||
// then reconstruct the votes by querying the multisig contract for votes on those proposals
|
||||
// digest from the description and count from the message
|
||||
let pending_redemptions = Self::rebuild_pending_votes(&shared_state).await?;
|
||||
|
||||
Ok(CredentialHandler {
|
||||
config,
|
||||
multisig_threshold,
|
||||
ticket_receiver,
|
||||
shared_state,
|
||||
pending_tickets,
|
||||
pending_redemptions,
|
||||
})
|
||||
}
|
||||
|
||||
// the argument is temporary as we'll be reading from the storage
|
||||
async fn create_redemption_proposal(
|
||||
&self,
|
||||
commitment: &[u8],
|
||||
number_of_tickets: u16,
|
||||
) -> Result<u64, EcashTicketError> {
|
||||
let res = self
|
||||
.shared_state
|
||||
.start_tx()
|
||||
.await
|
||||
.request_ticket_redemption(
|
||||
bs58::encode(commitment).into_string(),
|
||||
number_of_tickets,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.map_err(|source| EcashTicketError::RedemptionProposalCreationFailure { source })?;
|
||||
|
||||
// that one is quite tricky because proposal exists on chain, but we didn't get the id...
|
||||
// but it should be quite impossible to ever reach this unless we make breaking changes
|
||||
let proposal_id = res
|
||||
.parse_singleton_u64_contract_data()
|
||||
.inspect_err(|err| error!("reached seemingly impossible error! could not recover the redemption proposal id: {err}"))
|
||||
.map_err(|source| EcashTicketError::ProposalIdParsingFailure { source })?;
|
||||
|
||||
info!("created redemption proposal {proposal_id} to redeem {number_of_tickets} tickets");
|
||||
|
||||
Ok(proposal_id)
|
||||
}
|
||||
|
||||
/// Attempt to send ticket verification request to the provided ecash verifier.
|
||||
async fn verify_ticket(
|
||||
&self,
|
||||
@@ -522,42 +357,7 @@ impl CredentialHandler {
|
||||
async fn resolve_pending(&mut self) -> Result<(), EcashTicketError> {
|
||||
let mut still_failing = Vec::new();
|
||||
|
||||
// 1. attempt to resolve all pending proposals
|
||||
while let Some(mut pending) = self.pending_redemptions.pop() {
|
||||
match self.try_resolve_pending_proposal(&mut pending, None).await {
|
||||
Ok(resolution) => {
|
||||
if resolution.is_pending() {
|
||||
warn!(
|
||||
"still failed to reach quorum for proposal {}. apis: {:?} haven't responded. we'll retry later",
|
||||
pending.proposal_id, pending.pending
|
||||
);
|
||||
still_failing.push(pending);
|
||||
} else {
|
||||
self.shared_state
|
||||
.storage
|
||||
.clear_post_proposal_data(
|
||||
pending.proposal_id as u32,
|
||||
OffsetDateTime::now_utc(),
|
||||
resolution.is_rejected(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
error!(
|
||||
"experienced internal error when attempting to resolve pending proposal: {err}"
|
||||
);
|
||||
// make sure to update internal state to not lose any data
|
||||
self.pending_redemptions.push(pending);
|
||||
self.pending_redemptions.append(&mut still_failing);
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut still_failing = Vec::new();
|
||||
|
||||
// 2. attempt to verify the remaining tickets
|
||||
// 1. attempt to verify the remaining tickets
|
||||
while let Some(mut pending) = self.pending_tickets.pop() {
|
||||
// possible optimisation: if there's a lot of pending tickets, pre-emptively grab locks for api_clients
|
||||
match self
|
||||
@@ -595,362 +395,14 @@ impl CredentialHandler {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Attempt to send batch redemption request to the provided ecash verifier.
|
||||
async fn redeem_tickets(
|
||||
&self,
|
||||
proposal_id: u64,
|
||||
request: &BatchRedeemTicketsBody,
|
||||
client: &EcashApiClient,
|
||||
) -> Result<bool, EcashTicketError> {
|
||||
match client.api_client.batch_redeem_ecash_tickets(request).await {
|
||||
Ok(res) => {
|
||||
let accepted = if res.proposal_accepted {
|
||||
trace!("{client} has accepted proposal {proposal_id}");
|
||||
true
|
||||
} else {
|
||||
warn!("{client} has rejected proposal {proposal_id}");
|
||||
false
|
||||
};
|
||||
|
||||
Ok(accepted)
|
||||
}
|
||||
Err(err) => {
|
||||
error!(
|
||||
"failed to send proposal {proposal_id} for redemption vote to ecash signer '{client}': {err}. if we don't reach quorum, we'll retry later"
|
||||
);
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn try_execute_proposal(&self, proposal_id: u64) -> Result<(), EcashTicketError> {
|
||||
self.shared_state
|
||||
.start_tx()
|
||||
.await
|
||||
.execute_proposal(proposal_id, None)
|
||||
.await
|
||||
.map_err(
|
||||
|source| EcashTicketError::RedemptionProposalExecutionFailure {
|
||||
proposal_id,
|
||||
source,
|
||||
},
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_proposal_status(&self, proposal_id: u64) -> Result<Status, EcashTicketError> {
|
||||
Ok(self
|
||||
.shared_state
|
||||
.start_query()
|
||||
.await
|
||||
.query_proposal(proposal_id)
|
||||
.await
|
||||
.map_err(EcashTicketError::chain_query_failure)?
|
||||
.status)
|
||||
}
|
||||
|
||||
async fn try_finalize_proposal(
|
||||
&self,
|
||||
proposal_id: u64,
|
||||
) -> Result<ProposalResult, EcashTicketError> {
|
||||
match self.get_proposal_status(proposal_id).await? {
|
||||
Status::Pending => {
|
||||
// the voting hasn't even begun!
|
||||
error!("impossible case! the proposal {proposal_id} is still pending");
|
||||
Ok(ProposalResult::Pending)
|
||||
}
|
||||
Status::Open => {
|
||||
debug!("proposal {proposal_id} is still open and needs more votes");
|
||||
Ok(ProposalResult::Pending)
|
||||
}
|
||||
Status::Rejected => {
|
||||
warn!("proposal {proposal_id} has been rejected");
|
||||
Ok(ProposalResult::Rejected)
|
||||
}
|
||||
Status::Passed => {
|
||||
info!(
|
||||
"proposal {proposal_id} has already been passed - we just need to execute it"
|
||||
);
|
||||
self.try_execute_proposal(proposal_id).await?;
|
||||
info!("executed proposal {proposal_id}");
|
||||
Ok(ProposalResult::Executed)
|
||||
}
|
||||
Status::Executed => {
|
||||
info!("proposal {proposal_id} has already been executed - nothing to do!");
|
||||
Ok(ProposalResult::Executed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn try_resolve_pending_proposal(
|
||||
&self,
|
||||
pending: &mut PendingRedemptionVote,
|
||||
api_clients: Option<RwLockReadGuard<'_, Vec<EcashApiClient>>>,
|
||||
) -> Result<ProposalResult, EcashTicketError> {
|
||||
let proposal_id = pending.proposal_id;
|
||||
|
||||
info!(
|
||||
"attempting to resolve pending redemption proposal {proposal_id} to redeem {} tickets",
|
||||
pending.included_serial_numbers.len()
|
||||
);
|
||||
|
||||
// check if the proposal still needs more votes from the apis
|
||||
let result = self.try_finalize_proposal(proposal_id).await?;
|
||||
if !result.is_pending() {
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
let api_clients = match api_clients {
|
||||
Some(clients) => clients,
|
||||
None => self.shared_state.api_clients(pending.epoch_id).await?,
|
||||
};
|
||||
|
||||
let redemption_request = pending.to_request_body(self.shared_state.address.clone());
|
||||
|
||||
// TODO: optimisation: tell other apis they can purge our tickets even if they haven't voted
|
||||
|
||||
let total = api_clients.len();
|
||||
let api_failures = Mutex::new(Vec::new());
|
||||
let rejected = AtomicUsize::new(0);
|
||||
|
||||
for_each_api_concurrent(&api_clients, &pending.pending, |ecash_client| async {
|
||||
// errors are only returned on hard, storage, failures
|
||||
match self
|
||||
.redeem_tickets(pending.proposal_id, &redemption_request, ecash_client)
|
||||
.await
|
||||
{
|
||||
Err(err) => {
|
||||
error!("internal failure. could not proceed with ticket redemption: {err}");
|
||||
api_failures.lock().await.push(ecash_client.node_id);
|
||||
}
|
||||
Ok(false) => {
|
||||
rejected.fetch_add(1, Ordering::SeqCst);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
let api_failures = api_failures.into_inner();
|
||||
let num_failures = api_failures.len();
|
||||
pending.pending = api_failures;
|
||||
|
||||
let rejected = rejected.into_inner();
|
||||
let rejected_ratio = rejected as f32 / total as f32;
|
||||
let rejected_perc = rejected_ratio * 100.;
|
||||
if rejected_ratio >= (1. - self.multisig_threshold) {
|
||||
error!(
|
||||
"{rejected_perc:.2}% of signers rejected proposal {proposal_id}. we won't be able to execute it"
|
||||
);
|
||||
// no need to query the chain as with so many rejections it's impossible it has passed.
|
||||
return Ok(ProposalResult::Rejected);
|
||||
}
|
||||
|
||||
let accepted_ratio = (total - rejected - num_failures) as f32 / total as f32;
|
||||
let accepted_perc = accepted_ratio * 100.;
|
||||
match accepted_ratio {
|
||||
n if n < self.multisig_threshold => {
|
||||
error!(
|
||||
"less than 2/3 of signers ({accepted_perc:.2}%) accepted proposal {proposal_id}. we're not yet be able to execute it to get funds out"
|
||||
);
|
||||
return Ok(ProposalResult::Pending);
|
||||
}
|
||||
n if n < self.config.minimum_api_quorum => {
|
||||
warn!(
|
||||
"the system seems to be a bit unstable: less than 80%, but more than 67% of signers ({accepted_perc:.2}%) accepted proposal {proposal_id}"
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
trace!("{accepted_perc:.2}% of signers accepted proposal {proposal_id}");
|
||||
}
|
||||
}
|
||||
|
||||
// attempt to execute the proposal if it reached the required threshold
|
||||
self.try_finalize_proposal(proposal_id).await
|
||||
}
|
||||
|
||||
async fn maybe_redeem_tickets(&mut self) -> Result<(), EcashTicketError> {
|
||||
if !self.pending_tickets.is_empty() {
|
||||
return Err(EcashTicketError::PendingTickets);
|
||||
}
|
||||
|
||||
let latest_stored = self.shared_state.storage.latest_proposal().await?;
|
||||
|
||||
// check if we have already created the proposal but crashed before persisting it in the db
|
||||
//
|
||||
// if we have some persisted proposals in storage, try to see if there's anything more recent on chain
|
||||
// (i.e. the missing proposal)
|
||||
// if not (i.e. this would have been our first) check the latest page of proposals.
|
||||
// while this is not ideal, realistically speaking we probably crashed few minutes ago
|
||||
// and worst case scenario we'll just recreate the proposal instead
|
||||
//
|
||||
// LIMITATION: if MULTIPLE proposals got created in between, well. though luck.
|
||||
let latest_on_chain = if let Some(latest_stored) = &latest_stored {
|
||||
// those are sorted in ASCENDING way
|
||||
self.shared_state
|
||||
.proposals_since(latest_stored.proposal_id as u64)
|
||||
.await?
|
||||
.pop()
|
||||
} else {
|
||||
// but those are DESCENDING
|
||||
self.shared_state
|
||||
.last_proposal_page()
|
||||
.await?
|
||||
.first()
|
||||
.cloned()
|
||||
};
|
||||
|
||||
let now = OffsetDateTime::now_utc();
|
||||
|
||||
let prior_proposal = match (&latest_stored, latest_on_chain) {
|
||||
(None, None) => {
|
||||
// we haven't created any proposals before
|
||||
trace!("this could be our first redemption proposal");
|
||||
None
|
||||
}
|
||||
(Some(stored), None) => {
|
||||
if stored.created_at + MIN_BATCH_REDEMPTION_DELAY > now {
|
||||
trace!("too soon to create new redemption proposal");
|
||||
return Ok(());
|
||||
}
|
||||
None
|
||||
}
|
||||
(_, Some(on_chain)) => {
|
||||
warn!(
|
||||
"we seem to have crashed after creating proposal, but before persisting it onto disk!"
|
||||
);
|
||||
|
||||
Some(on_chain)
|
||||
}
|
||||
};
|
||||
|
||||
// technically we could have been just caching all of those serial numbers as we verify tickets,
|
||||
// but given how infrequently we call this, there's no point in wasting this memory
|
||||
let verified_tickets = self
|
||||
.shared_state
|
||||
.storage
|
||||
.get_all_verified_tickets_with_sn()
|
||||
.await?;
|
||||
|
||||
// TODO: somehow simplify that nasty nested if
|
||||
if verified_tickets.len() < self.config.minimum_redemption_tickets {
|
||||
// bypass the number of tickets check if we're about to lose our rewards due to expiration
|
||||
if let Some(latest_stored) = latest_stored {
|
||||
if latest_stored.created_at + self.config.maximum_time_between_redemption < now {
|
||||
{}
|
||||
} else {
|
||||
debug!(
|
||||
"we only have {} verified tickets. there's no point in creating a redemption request yet. (we need at least {} (configurable))",
|
||||
verified_tickets.len(),
|
||||
self.config.minimum_redemption_tickets
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
} else {
|
||||
// first proposal
|
||||
debug!(
|
||||
"we only have {} verified tickets. there's no point in creating a redemption request yet. (we need at least {} (configurable))",
|
||||
verified_tickets.len(),
|
||||
self.config.minimum_redemption_tickets
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
// this should have been ensured when querying
|
||||
assert!(verified_tickets.len() <= u16::MAX as usize);
|
||||
|
||||
let digest =
|
||||
BatchRedeemTicketsBody::make_digest(verified_tickets.iter().map(|t| &t.serial_number));
|
||||
let encoded_digest = bs58::encode(&digest).into_string();
|
||||
|
||||
let prior_proposal_id = if let Some(prior_proposal) = prior_proposal {
|
||||
if prior_proposal.description == encoded_digest {
|
||||
info!("we have already created proposal for those tickets");
|
||||
Some(prior_proposal.id)
|
||||
} else {
|
||||
warn!(
|
||||
"our missed proposal seem to have been for different tickets - abandoning it"
|
||||
);
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// if the proposal has already existed on chain, do use it. otherwise create a new one
|
||||
let proposal_id = if let Some(prior) = prior_proposal_id {
|
||||
prior
|
||||
} else {
|
||||
self.create_redemption_proposal(&digest, verified_tickets.len() as u16)
|
||||
.await?
|
||||
};
|
||||
|
||||
if proposal_id > u32::MAX as u64 {
|
||||
// realistically will we ever reach it? no.
|
||||
panic!(
|
||||
"we have created more than {} proposals. we can't handle that.",
|
||||
u32::MAX
|
||||
)
|
||||
}
|
||||
|
||||
self.shared_state
|
||||
.storage
|
||||
.insert_redemption_proposal(
|
||||
&verified_tickets,
|
||||
proposal_id as u32,
|
||||
OffsetDateTime::now_utc(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let current_epoch = self.shared_state.current_epoch_id().await?;
|
||||
let api_clients = self.shared_state.api_clients(current_epoch).await?;
|
||||
let ids = api_clients.iter().map(|c| c.node_id).collect();
|
||||
let mut pending = PendingRedemptionVote::new(
|
||||
proposal_id,
|
||||
digest,
|
||||
verified_tickets
|
||||
.into_iter()
|
||||
.map(|t| t.serial_number)
|
||||
.collect(),
|
||||
current_epoch,
|
||||
ids,
|
||||
);
|
||||
|
||||
let resolution = self
|
||||
.try_resolve_pending_proposal(&mut pending, Some(api_clients))
|
||||
.await?;
|
||||
if resolution.is_pending() {
|
||||
warn!(
|
||||
"failed to reach quorum for proposal {proposal_id}. apis: {:?} haven't responded. we'll retry later",
|
||||
pending.pending
|
||||
);
|
||||
self.pending_redemptions.push(pending);
|
||||
} else {
|
||||
self.shared_state
|
||||
.storage
|
||||
.clear_post_proposal_data(
|
||||
proposal_id as u32,
|
||||
OffsetDateTime::now_utc(),
|
||||
resolution.is_rejected(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn periodic_operations(&mut self) -> Result<(), EcashTicketError> {
|
||||
trace!(
|
||||
"attempting to resolve all pending operations -> tickets that are waiting for verification and possibly redemption"
|
||||
"attempting to resolve all pending operations -> tickets that are waiting for verification"
|
||||
);
|
||||
|
||||
// 1. retry all operations that have failed in the past: verification requests and pending redemption
|
||||
// retry the pending verification requests that have failed before
|
||||
self.resolve_pending().await?;
|
||||
|
||||
// 2. if applicable, attempt to redeem all newly verified tickets
|
||||
self.maybe_redeem_tickets().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ use std::future::Future;
|
||||
use std::ops::Deref;
|
||||
use tokio::sync::RwLockReadGuard;
|
||||
|
||||
pub(crate) fn apis_stream<'a>(
|
||||
pub fn apis_stream<'a>(
|
||||
// if needed we could make this argument more generic to accept either locks or iterators, etc.
|
||||
all_clients: &'a RwLockReadGuard<'a, Vec<EcashApiClient>>,
|
||||
filter_by_id: &'a [u64],
|
||||
@@ -22,7 +22,7 @@ pub(crate) fn apis_stream<'a>(
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) async fn for_each_api_concurrent<'a, F, Fut>(
|
||||
pub async fn for_each_api_concurrent<'a, F, Fut>(
|
||||
all_clients: &'a RwLockReadGuard<'a, Vec<EcashApiClient>>,
|
||||
filter_by_id: &'a [u64],
|
||||
f: F,
|
||||
|
||||
@@ -20,7 +20,7 @@ use tracing::error;
|
||||
|
||||
pub mod credential_sender;
|
||||
pub mod error;
|
||||
mod helpers;
|
||||
pub mod helpers;
|
||||
mod state;
|
||||
pub mod traits;
|
||||
|
||||
|
||||
@@ -3,17 +3,12 @@
|
||||
|
||||
use crate::Error;
|
||||
use crate::ecash::error::EcashTicketError;
|
||||
use cosmwasm_std::{CosmosMsg, WasmMsg, from_json};
|
||||
use nym_credentials_interface::VerificationKeyAuth;
|
||||
use nym_ecash_contract_common::msg::ExecuteMsg;
|
||||
use nym_gateway_storage::traits::BandwidthGatewayStorage;
|
||||
use nym_validator_client::coconut::all_ecash_api_clients;
|
||||
use nym_validator_client::nym_api::EpochId;
|
||||
use nym_validator_client::nyxd::AccountId;
|
||||
use nym_validator_client::nyxd::contract_traits::{
|
||||
DkgQueryClient, MultisigQueryClient, NymContractsProvider,
|
||||
};
|
||||
use nym_validator_client::nyxd::cw3::ProposalResponse;
|
||||
use nym_validator_client::nyxd::contract_traits::{DkgQueryClient, NymContractsProvider};
|
||||
use nym_validator_client::{DirectSigningHttpRpcNyxdClient, EcashApiClient};
|
||||
use std::collections::BTreeMap;
|
||||
use std::ops::Deref;
|
||||
@@ -77,53 +72,6 @@ impl SharedState {
|
||||
Ok(this)
|
||||
}
|
||||
|
||||
fn created_redemption_proposal(&self, proposal: &ProposalResponse) -> bool {
|
||||
let Some(msg) = proposal.msgs.first() else {
|
||||
return false;
|
||||
};
|
||||
let CosmosMsg::Wasm(WasmMsg::Execute { msg, .. }) = msg else {
|
||||
return false;
|
||||
};
|
||||
let Ok(ExecuteMsg::RedeemTickets { gw, .. }) = from_json(msg) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
gw == self.address.as_ref()
|
||||
}
|
||||
|
||||
/// retrieve all redemption proposals made by this gateway since, but excluding, the provided id
|
||||
pub(crate) async fn proposals_since(
|
||||
&self,
|
||||
proposal_id: u64,
|
||||
) -> Result<Vec<ProposalResponse>, EcashTicketError> {
|
||||
Ok(self
|
||||
.start_query()
|
||||
.await
|
||||
.list_proposals(Some(proposal_id), None)
|
||||
.await
|
||||
.map_err(EcashTicketError::chain_query_failure)?
|
||||
.proposals
|
||||
.into_iter()
|
||||
.filter(|p| self.created_redemption_proposal(p))
|
||||
.collect())
|
||||
}
|
||||
|
||||
/// retrieve all redemption proposals made by this gateway that are available on the last page of the query
|
||||
pub(crate) async fn last_proposal_page(
|
||||
&self,
|
||||
) -> Result<Vec<ProposalResponse>, EcashTicketError> {
|
||||
Ok(self
|
||||
.start_query()
|
||||
.await
|
||||
.reverse_proposals(None, None)
|
||||
.await
|
||||
.map_err(EcashTicketError::chain_query_failure)?
|
||||
.proposals
|
||||
.into_iter()
|
||||
.filter(|p| self.created_redemption_proposal(p))
|
||||
.collect())
|
||||
}
|
||||
|
||||
async fn set_epoch_data(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
@@ -240,24 +188,6 @@ impl SharedState {
|
||||
data.get(&epoch_id).map(|d| &d.master_key).unwrap()
|
||||
}))
|
||||
}
|
||||
|
||||
pub(crate) async fn start_tx(&self) -> RwLockWriteGuard<'_, DirectSigningHttpRpcNyxdClient> {
|
||||
self.nyxd_client.write().await
|
||||
}
|
||||
|
||||
pub(crate) async fn start_query(&self) -> RwLockReadGuard<'_, DirectSigningHttpRpcNyxdClient> {
|
||||
self.nyxd_client.read().await
|
||||
}
|
||||
|
||||
pub(crate) async fn current_epoch_id(&self) -> Result<EpochId, EcashTicketError> {
|
||||
Ok(self
|
||||
.start_query()
|
||||
.await
|
||||
.get_current_epoch()
|
||||
.await
|
||||
.map_err(EcashTicketError::chain_query_failure)?
|
||||
.epoch_id)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct EpochState {
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
[package]
|
||||
name = "nym-credentials-interface"
|
||||
description = "Interface for Nym's compact eacash / zknym credential scheme"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
description = "Interface for Nym's compact eacash / zknym credential scheme"
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
[package]
|
||||
name = "nym-credentials"
|
||||
description = "Crate for using Nym's zknym credentials"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
description = "Crate for using Nym's zknym credentials"
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
[package]
|
||||
name = "nym-crypto"
|
||||
version.workspace = true
|
||||
description = "Crypto library for the nym mixnet"
|
||||
edition = { workspace = true }
|
||||
version.workspace = true
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
license = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
[dependencies]
|
||||
aes-gcm-siv = { workspace = true, optional = true }
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
[package]
|
||||
name = "nym-dkg"
|
||||
version.workspace = true
|
||||
edition = "2021"
|
||||
resolver = "2"
|
||||
license.workspace = true
|
||||
description = "Nym's Distributed Key Generation functionality"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
resolver = "2"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
[package]
|
||||
name = "nym-ecash-signer-check-types"
|
||||
description = "Crate containing types for the `ecash-signer-check` crate used to check if zknym signers are up and running properly"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
description = "Crate containing types for the `ecash-signer-check` crate used to check if zknym signers are up and running properly"
|
||||
publish = true
|
||||
|
||||
[dependencies]
|
||||
semver = { workspace = true }
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
[package]
|
||||
name = "nym-ecash-signer-check"
|
||||
description = "Functions to interact with zknym signers, checking their status and health"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
description = "Functions to interact with zknym signers, checking their status and health"
|
||||
publish = true
|
||||
|
||||
[dependencies]
|
||||
futures = { workspace = true }
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
[package]
|
||||
name = "nym-ecash-time"
|
||||
description = "Time-related helper functions for Nym's zknym scheme"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
description = "Time-related helper functions for Nym's zknym scheme"
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
[package]
|
||||
name = "nym-exit-policy"
|
||||
description = "Get and set the Nym Exit Policy, used by Exit Gateways"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
description = "Get and set the Nym Exit Policy, used by Exit Gateways"
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -3,14 +3,17 @@
|
||||
|
||||
[package]
|
||||
name = "nym-gateway-requests"
|
||||
description = "Request and response definitions for Nym Gateway <> client communication"
|
||||
version.workspace = true
|
||||
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
description = "Request and response definitions for Nym Gateway <> client communication"
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
[package]
|
||||
name = "nym-gateway-stats-storage"
|
||||
description = "Functionality Nym Gateway statistics storage"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
description = "Functionality Nym Gateway statistics storage"
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
[dependencies]
|
||||
sqlx = { workspace = true, features = [
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
[package]
|
||||
name = "nym-gateway-storage"
|
||||
description = "Crate handling db setup and use for Nym Gateways, used for credentials, packets, connections"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
description = "Crate handling db setup and use for Nym Gateways, used for credentials, packets, connections"
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
[package]
|
||||
name = "nym-http-api-client-macro"
|
||||
description = "Proc-macros for configuring HTTP clients globally via the `inventory` crate"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
description = "Proc-macros for configuring HTTP clients globally via the `inventory` crate"
|
||||
publish = true
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
[package]
|
||||
name = "nym-http-api-client"
|
||||
description = "Nym's HTTP API client, examples, and tests"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
description = "Nym's HTTP API client, examples, and tests"
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -34,6 +37,7 @@ tracing = { workspace = true }
|
||||
itertools = { workspace = true }
|
||||
inventory = { workspace = true }
|
||||
tokio = { workspace = true, features = ["rt", "macros", "time"] }
|
||||
rustls = { workspace=true }
|
||||
# used for decoding text responses (they were already implicitly included)
|
||||
bytes = { workspace = true }
|
||||
encoding_rs = { workspace = true }
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::collections::HashMap;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
|
||||
pub const NYM_API_DOMAIN: &str = "validator.nymtech.net";
|
||||
pub const NYM_API_IPS: &[IpAddr] = &[IpAddr::V4(Ipv4Addr::new(212, 71, 233, 232))];
|
||||
pub const NYM_API_IPS: &[IpAddr] = &[IpAddr::V4(Ipv4Addr::new(92, 39, 63, 14))];
|
||||
|
||||
pub const NYM_VPN_API_DOMAIN: &str = "nymvpn.com";
|
||||
pub const NYM_VPN_API_IPS: &[IpAddr] = &[IpAddr::V4(Ipv4Addr::new(76, 76, 21, 21))];
|
||||
|
||||
@@ -161,6 +161,8 @@ use reqwest::{RequestBuilder, Response};
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Display;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use std::io::ErrorKind;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::time::Duration;
|
||||
use thiserror::Error;
|
||||
@@ -1150,7 +1152,10 @@ impl ApiClientCore for Client {
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let response: Result<Response, HttpClientError> = {
|
||||
let client = self.reqwest_client.as_ref().unwrap_or(&*SHARED_CLIENT);
|
||||
let client = self
|
||||
.reqwest_client
|
||||
.as_ref()
|
||||
.unwrap_or_else(|| &*SHARED_CLIENT);
|
||||
Ok(
|
||||
wasmtimer::tokio::timeout(self.request_timeout, client.execute(req))
|
||||
.await
|
||||
@@ -1160,21 +1165,29 @@ impl ApiClientCore for Client {
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let response = {
|
||||
let client = self.reqwest_client.as_ref().unwrap_or(&*SHARED_CLIENT);
|
||||
let client = self
|
||||
.reqwest_client
|
||||
.as_ref()
|
||||
.unwrap_or_else(|| &*SHARED_CLIENT);
|
||||
client.execute(req).await
|
||||
};
|
||||
|
||||
match response {
|
||||
Ok(resp) => return Ok(resp),
|
||||
Ok(resp) => {
|
||||
// Check if the response includes a rate limit error from the vercel API
|
||||
if is_http_rate_limit_err(&resp) {
|
||||
warn!("encountered vercel rate limit error for {}", url.as_str());
|
||||
// if we have multiple urls, update to the next
|
||||
self.maybe_rotate_hosts(Some(url.clone()));
|
||||
}
|
||||
|
||||
return Ok(resp);
|
||||
}
|
||||
Err(err) => {
|
||||
// only if there was a network issue should we consider updating the host info
|
||||
//
|
||||
// note: for now this includes DNS resolution failure, I am not sure how I would go about
|
||||
// segregating that based on the interface provided by request for errors.
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let is_network_err = err.is_timeout();
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let is_network_err = err.is_timeout() || err.is_connect();
|
||||
let is_network_err = might_be_network_interference(&err);
|
||||
|
||||
if is_network_err {
|
||||
// if we have multiple urls, update to the next
|
||||
@@ -1222,6 +1235,90 @@ impl ApiClientCore for Client {
|
||||
}
|
||||
}
|
||||
|
||||
const VERCEL_CHALLENGE_HEADER: &str = "x-vercel-mitigated";
|
||||
const VERCEL_CHALLENGE_VALUE: &[u8] = b"challenge";
|
||||
|
||||
/// Check for Rate Limit challenge response from the vercel API
|
||||
pub(crate) fn is_http_rate_limit_err(resp: &Response) -> bool {
|
||||
let status = resp.status() == StatusCode::FORBIDDEN;
|
||||
let header = resp
|
||||
.headers()
|
||||
.get(VERCEL_CHALLENGE_HEADER)
|
||||
.is_some_and(|v| v.as_bytes() == VERCEL_CHALLENGE_VALUE);
|
||||
let content_type = resp
|
||||
.headers()
|
||||
.get(CONTENT_TYPE)
|
||||
.and_then(|value| value.to_str().ok())
|
||||
.and_then(|value| value.parse::<Mime>().ok())
|
||||
.is_some_and(|mime_type| {
|
||||
mime_type.type_() == mime::TEXT && mime_type.subtype() == mime::HTML
|
||||
});
|
||||
|
||||
status && header && content_type
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
const MAX_ERR_SOURCE_ITERATIONS: usize = 4;
|
||||
|
||||
/// This functions attempts to check the error returned by reqwest to see if rotating host
|
||||
/// information (for clients with multiple hosts defined) could be helpful. This looks for
|
||||
/// situations where the error could plausibly be caused by a network adversary, or where rotating
|
||||
/// to an equivalent hostname might help.
|
||||
///
|
||||
/// For example --> NetworkUnreachable will not be helped by rotating domains, but ConnectionReset
|
||||
/// might be caused by a network adversary blocking by SNI which could possibly benefit from
|
||||
/// rotating domains.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(crate) fn might_be_network_interference(err: &reqwest::Error) -> bool {
|
||||
if err.is_timeout() {
|
||||
return true;
|
||||
}
|
||||
|
||||
if !(err.is_connect() || err.is_request()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The io::Error source is several layers deep, for clarity this is done as a loop
|
||||
// * reqwest::Error -> hyper_util::Error
|
||||
// * hyper_util::Error -> hyper_util::ClientError
|
||||
// * hyper_util::ClientError -> io::Error
|
||||
let mut inner = err.source();
|
||||
for _ in 0..MAX_ERR_SOURCE_ITERATIONS {
|
||||
if let Some(e) = inner {
|
||||
if let Some(io_err) = e.downcast_ref::<std::io::Error>() {
|
||||
// try downcast to io::Error from <dyn std::error:Error>
|
||||
match io_err.kind() {
|
||||
// device not connected to the internet
|
||||
ErrorKind::NetworkUnreachable | ErrorKind::NetworkDown => return false,
|
||||
// connection errors can indicate connection interference
|
||||
ErrorKind::ConnectionReset
|
||||
| ErrorKind::HostUnreachable
|
||||
| ErrorKind::ConnectionRefused => return true,
|
||||
// TLS errors get wrapped in custom io::Errors
|
||||
ErrorKind::Other | ErrorKind::InvalidData => {
|
||||
// io::Error get_ref works while source doesn't here -_-
|
||||
// if you don't like it take it up with the rust devs https://users.rust-lang.org/t/question-about-implementation-of-std-source/121117
|
||||
inner = io_err.get_ref().map(|e| e as &dyn std::error::Error);
|
||||
}
|
||||
_ => return false,
|
||||
}
|
||||
} else if let Some(_tls_err) = e.downcast_ref::<rustls::Error>() {
|
||||
// try downcast to TLS error
|
||||
return true;
|
||||
} else if let Some(resolve_err) = e.downcast_ref::<hickory_resolver::ResolveError>() {
|
||||
// try downcast to DNS error
|
||||
return resolve_err.is_nx_domain();
|
||||
} else {
|
||||
inner = e.source();
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Common usage functionality for the http client.
|
||||
///
|
||||
/// These functions allow for cleaner downstream usage free of type parameters and unneeded imports.
|
||||
@@ -1631,6 +1728,13 @@ where
|
||||
decode_raw_response(&headers, full)
|
||||
} else if res.status() == StatusCode::NOT_FOUND {
|
||||
Err(HttpClientError::NotFound { url: Box::new(url) })
|
||||
} else if is_http_rate_limit_err(&res) {
|
||||
Err(HttpClientError::EndpointFailure {
|
||||
url: Box::new(url),
|
||||
status,
|
||||
headers: Box::new(headers),
|
||||
error: String::from("received vercel rate limit challenge response"),
|
||||
})
|
||||
} else {
|
||||
let Ok(plaintext) = res.text().await else {
|
||||
return Err(HttpClientError::RequestFailure {
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
[package]
|
||||
name = "nym-http-api-common"
|
||||
description = "Common crate for Nym-related HTTP API interaction"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
description = "Common crate for Nym-related HTTP API interaction"
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
[package]
|
||||
name = "nym-inclusion-probability"
|
||||
version.workspace = true
|
||||
description = "Nym active set probability simulator"
|
||||
edition.workspace = true
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
[dependencies]
|
||||
log = { workspace = true }
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
[package]
|
||||
name = "nym-ip-packet-requests"
|
||||
description = "Codec, signing functionality, and different version definitions for IP packet request and responses"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
description = "Codec, signing functionality, and different version definitions for IP packet request and responses"
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
|
||||
[features]
|
||||
test-utils = ["pnet_packet"]
|
||||
|
||||
[dependencies]
|
||||
pnet_packet = { workspace = true, optional = true }
|
||||
bincode = { workspace = true }
|
||||
bytes = { workspace = true }
|
||||
nym-bin-common = { workspace = true }
|
||||
@@ -18,8 +25,10 @@ nym-crypto = { workspace = true }
|
||||
nym-service-provider-requests-common = { workspace = true }
|
||||
nym-sphinx = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
semver = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
time = { workspace = true }
|
||||
tokio = { workspace = true, features = ["time"] }
|
||||
tokio-util = { workspace = true, features = ["codec"] }
|
||||
tracing = { workspace = true }
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Extracted from sdk/rust/nym-sdk/examples/ipr_tunnel.rs
|
||||
|
||||
//! ICMP/ICMPv6 packet construction and reply detection helpers for testing
|
||||
//! IPR connectivity. Gated behind the `test-utils` feature.
|
||||
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
|
||||
use pnet_packet::Packet;
|
||||
use pnet_packet::icmp::echo_reply::EchoReplyPacket;
|
||||
use pnet_packet::icmp::echo_request::MutableEchoRequestPacket;
|
||||
use pnet_packet::icmp::{IcmpPacket, IcmpTypes};
|
||||
use pnet_packet::icmpv6::Icmpv6Types;
|
||||
use pnet_packet::ipv4::{Ipv4Flags, MutableIpv4Packet};
|
||||
use pnet_packet::ipv6::MutableIpv6Packet;
|
||||
|
||||
/// Build a complete IPv4 ICMP echo request packet.
|
||||
pub fn build_icmp_ping(src: Ipv4Addr, dst: Ipv4Addr, seq: u16) -> Option<Vec<u8>> {
|
||||
let mut echo = MutableEchoRequestPacket::owned(vec![0u8; 64])?;
|
||||
echo.set_icmp_type(IcmpTypes::EchoRequest);
|
||||
echo.set_icmp_code(pnet_packet::icmp::IcmpCode::new(0));
|
||||
echo.set_sequence_number(seq);
|
||||
let cksum = pnet_packet::icmp::checksum(&IcmpPacket::new(echo.packet())?);
|
||||
echo.set_checksum(cksum);
|
||||
|
||||
let total_len = 20 + echo.packet().len();
|
||||
let mut ip = MutableIpv4Packet::owned(vec![0u8; total_len])?;
|
||||
ip.set_version(4);
|
||||
ip.set_header_length(5);
|
||||
ip.set_total_length(total_len as u16);
|
||||
ip.set_ttl(64);
|
||||
ip.set_next_level_protocol(pnet_packet::ip::IpNextHeaderProtocols::Icmp);
|
||||
ip.set_source(src);
|
||||
ip.set_destination(dst);
|
||||
ip.set_flags(Ipv4Flags::DontFragment);
|
||||
ip.set_payload(echo.packet());
|
||||
|
||||
let mut buf = ip.consume_to_immutable().packet().to_vec();
|
||||
let cksum = ipv4_checksum(&buf);
|
||||
buf[10] = (cksum >> 8) as u8;
|
||||
buf[11] = cksum as u8;
|
||||
Some(buf)
|
||||
}
|
||||
|
||||
/// Build a complete IPv6 ICMPv6 echo request packet.
|
||||
pub fn build_icmpv6_ping(src: Ipv6Addr, dst: Ipv6Addr, seq: u16) -> Option<Vec<u8>> {
|
||||
let mut echo =
|
||||
pnet_packet::icmpv6::echo_request::MutableEchoRequestPacket::owned(vec![0u8; 64])?;
|
||||
echo.set_icmpv6_type(Icmpv6Types::EchoRequest);
|
||||
echo.set_icmpv6_code(pnet_packet::icmpv6::Icmpv6Code::new(0));
|
||||
echo.set_sequence_number(seq);
|
||||
let cksum = pnet_packet::icmpv6::checksum(
|
||||
&pnet_packet::icmpv6::Icmpv6Packet::new(echo.packet())?,
|
||||
&src,
|
||||
&dst,
|
||||
);
|
||||
echo.set_checksum(cksum);
|
||||
|
||||
let payload_len = echo.packet().len();
|
||||
let mut ip = MutableIpv6Packet::owned(vec![0u8; 40 + payload_len])?;
|
||||
ip.set_version(6);
|
||||
ip.set_payload_length(payload_len as u16);
|
||||
ip.set_next_header(pnet_packet::ip::IpNextHeaderProtocols::Icmpv6);
|
||||
ip.set_hop_limit(64);
|
||||
ip.set_source(src);
|
||||
ip.set_destination(dst);
|
||||
ip.set_payload(echo.packet());
|
||||
|
||||
Some(ip.consume_to_immutable().packet().to_vec())
|
||||
}
|
||||
|
||||
/// Check if a raw packet is an IPv4 ICMP echo reply destined to `expected_dst`.
|
||||
pub fn is_echo_reply_v4(data: &[u8], expected_dst: Ipv4Addr) -> bool {
|
||||
let Some(ip) = pnet_packet::ipv4::Ipv4Packet::new(data) else {
|
||||
return false;
|
||||
};
|
||||
if ip.get_destination() != expected_dst {
|
||||
return false;
|
||||
}
|
||||
if ip.get_next_level_protocol() != pnet_packet::ip::IpNextHeaderProtocols::Icmp {
|
||||
return false;
|
||||
}
|
||||
let Some(reply) = EchoReplyPacket::new(ip.payload()) else {
|
||||
return false;
|
||||
};
|
||||
reply.get_icmp_type() == IcmpTypes::EchoReply
|
||||
}
|
||||
|
||||
/// Check if a raw packet is an IPv6 ICMPv6 echo reply destined to `expected_dst`.
|
||||
pub fn is_echo_reply_v6(data: &[u8], expected_dst: Ipv6Addr) -> bool {
|
||||
let Some(ip) = pnet_packet::ipv6::Ipv6Packet::new(data) else {
|
||||
return false;
|
||||
};
|
||||
if ip.get_destination() != expected_dst {
|
||||
return false;
|
||||
}
|
||||
if ip.get_next_header() != pnet_packet::ip::IpNextHeaderProtocols::Icmpv6 {
|
||||
return false;
|
||||
}
|
||||
let Some(reply) = pnet_packet::icmpv6::echo_reply::EchoReplyPacket::new(ip.payload()) else {
|
||||
return false;
|
||||
};
|
||||
reply.get_icmpv6_type() == Icmpv6Types::EchoReply
|
||||
}
|
||||
|
||||
fn ipv4_checksum(header: &[u8]) -> u16 {
|
||||
let mut sum = 0u32;
|
||||
for i in (0..20).step_by(2) {
|
||||
sum += ((header[i] as u32) << 8) | header[i + 1] as u32;
|
||||
}
|
||||
while (sum >> 16) > 0 {
|
||||
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||
}
|
||||
!sum as u16
|
||||
}
|
||||
@@ -3,10 +3,22 @@ use std::fmt::{Display, Formatter};
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
|
||||
pub mod codec;
|
||||
#[cfg(feature = "test-utils")]
|
||||
pub mod icmp_utils;
|
||||
pub mod response_helpers;
|
||||
pub mod sign;
|
||||
pub mod v6;
|
||||
pub mod v7;
|
||||
pub mod v8;
|
||||
pub mod v9;
|
||||
|
||||
/// Highest IPR protocol version that is allowed to be sent as a **non-stream** mixnet payload
|
||||
/// (i.e. not wrapped in `LpFrameKind::SphinxStream`).
|
||||
pub const MAX_NON_STREAM_VERSION: u8 = v8::VERSION;
|
||||
|
||||
/// First IPR protocol version that **requires** the SphinxStream (LP) transport for non-stream
|
||||
/// mixnet sends, matching the node-side enforcement in `ip-packet-router`.
|
||||
pub const SPHINX_STREAM_VERSION_THRESHOLD: u8 = v9::VERSION;
|
||||
|
||||
// version 3: initial version
|
||||
// version 4: IPv6 support
|
||||
@@ -14,6 +26,8 @@ pub mod v8;
|
||||
// version 6: Increase the available IPs
|
||||
// version 7: Add signature support (for the future)
|
||||
// version 8: Anonymous sends
|
||||
// version 9: LP-framed transport (SphinxStream)
|
||||
// response_helpers: shared IPR response parsing (nym-ip-packet-client + nym-sdk)
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct IpPair {
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use tokio_util::codec::Decoder;
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
use crate::{
|
||||
IpPair,
|
||||
codec::MultiIpPacketCodec,
|
||||
v8::response::{
|
||||
ConnectResponseReply, ControlResponse, InfoLevel, IpPacketResponse, IpPacketResponseData,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum IprResponseError {
|
||||
#[error("no version byte in message")]
|
||||
NoVersionByte,
|
||||
|
||||
#[error("version mismatch: received v{received}, expected v{expected}")]
|
||||
VersionMismatch { expected: u8, received: u8 },
|
||||
|
||||
#[error("expected control response, got {0:?}")]
|
||||
UnexpectedResponse(IpPacketResponseData),
|
||||
|
||||
#[error("connect denied: {0:?}")]
|
||||
ConnectDenied(crate::v8::response::ConnectFailureReason),
|
||||
}
|
||||
|
||||
pub enum MixnetMessageOutcome {
|
||||
IpPackets(Vec<Bytes>),
|
||||
Disconnect,
|
||||
}
|
||||
|
||||
// Extracted from:
|
||||
// nym-ip-packet-client/src/helpers.rs — check_ipr_message_version()
|
||||
// sdk/rust/nym-sdk/src/ip_packet_client/listener.rs — check_ipr_message_version()
|
||||
/// Check that the first byte of an IPR message matches the expected protocol version.
|
||||
pub fn check_ipr_message_version(data: &[u8], expected: u8) -> Result<(), IprResponseError> {
|
||||
let version = data.first().ok_or(IprResponseError::NoVersionByte)?;
|
||||
if *version != expected {
|
||||
return Err(IprResponseError::VersionMismatch {
|
||||
expected,
|
||||
received: *version,
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Extracted from:
|
||||
// nym-ip-packet-client/src/connect.rs — handle_connect_response() + handle_ip_packet_router_response()
|
||||
// sdk/rust/nym-sdk/src/ip_packet_client/discovery.rs — parse_connect_response()
|
||||
/// Parse an IPR connect response, returning allocated IPs on success.
|
||||
pub fn parse_connect_response(response: IpPacketResponse) -> Result<IpPair, IprResponseError> {
|
||||
let control_response = match response.data {
|
||||
IpPacketResponseData::Control(c) => c,
|
||||
other => return Err(IprResponseError::UnexpectedResponse(other)),
|
||||
};
|
||||
|
||||
match *control_response {
|
||||
ControlResponse::Connect(connect_resp) => match connect_resp.reply {
|
||||
ConnectResponseReply::Success(success) => Ok(success.ips),
|
||||
ConnectResponseReply::Failure(reason) => Err(IprResponseError::ConnectDenied(reason)),
|
||||
},
|
||||
_ => Err(IprResponseError::UnexpectedResponse(
|
||||
IpPacketResponseData::Control(control_response),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
// Extracted from:
|
||||
// nym-ip-packet-client/src/listener.rs — IprListener::handle_reconstructed_message()
|
||||
// sdk/rust/nym-sdk/src/ip_packet_client/listener.rs — handle_ipr_response()
|
||||
/// Parse raw IPR response bytes into an outcome.
|
||||
///
|
||||
/// Logs non-fatal conditions (unknown control messages, deserialization
|
||||
/// failures) and returns `None` for them.
|
||||
pub fn handle_ipr_response(data: &[u8]) -> Option<MixnetMessageOutcome> {
|
||||
match IpPacketResponse::from_bytes(data) {
|
||||
Ok(response) => match response.data {
|
||||
IpPacketResponseData::Data(data_response) => {
|
||||
let mut codec = MultiIpPacketCodec::new();
|
||||
let mut buf = BytesMut::from(data_response.ip_packet.as_ref());
|
||||
let mut packets = Vec::new();
|
||||
loop {
|
||||
match codec.decode(&mut buf) {
|
||||
Ok(Some(packet)) => packets.push(packet.into_bytes()),
|
||||
Ok(None) => break,
|
||||
Err(e) => {
|
||||
warn!("Failed to decode bundled IP packet: {e}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(MixnetMessageOutcome::IpPackets(packets))
|
||||
}
|
||||
IpPacketResponseData::Control(control_response) => match *control_response {
|
||||
ControlResponse::Connect(_) => {
|
||||
info!("Received connect response when already connected - ignoring");
|
||||
None
|
||||
}
|
||||
ControlResponse::Disconnect(_) | ControlResponse::UnrequestedDisconnect(_) => {
|
||||
info!("Received disconnect from IPR");
|
||||
Some(MixnetMessageOutcome::Disconnect)
|
||||
}
|
||||
ControlResponse::Pong(_) => {
|
||||
info!("Received pong response");
|
||||
None
|
||||
}
|
||||
ControlResponse::Health(_) => {
|
||||
info!("Received health response");
|
||||
None
|
||||
}
|
||||
ControlResponse::Info(info_resp) => {
|
||||
let msg = format!(
|
||||
"Received info response from the mixnet: {}",
|
||||
info_resp.reply
|
||||
);
|
||||
match info_resp.level {
|
||||
InfoLevel::Info => info!("{msg}"),
|
||||
InfoLevel::Warn => warn!("{msg}"),
|
||||
InfoLevel::Error => error!("{msg}"),
|
||||
}
|
||||
None
|
||||
}
|
||||
},
|
||||
},
|
||||
Err(err) => {
|
||||
warn!("Failed to deserialize IPR response: {err}");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -179,11 +179,15 @@ impl IpPacketResponse {
|
||||
make_bincode_serializer().serialize(self)
|
||||
}
|
||||
|
||||
pub fn from_bytes(data: &[u8]) -> Result<Self, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().deserialize(data)
|
||||
}
|
||||
|
||||
pub fn from_reconstructed_message(
|
||||
message: &nym_sphinx::receiver::ReconstructedMessage,
|
||||
) -> Result<Self, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().deserialize(&message.message)
|
||||
Self::from_bytes(&message.message)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
pub const VERSION: u8 = 9;
|
||||
|
||||
/// Minimum nym-node release version that supports v9 (LP Stream framing).
|
||||
/// Nodes running older versions will not understand LP-wrapped packets.
|
||||
pub const MIN_RELEASE_VERSION: semver::Version = semver::Version::new(1, 30, 0);
|
||||
|
||||
// v9 uses the same wire format as v8. The version bump indicates
|
||||
// the message was sent with LP framing (SphinxStream).
|
||||
//
|
||||
// Types are re-exported for deserialization/matching. Use the wrapper
|
||||
// constructors below to create correctly-versioned packets — never
|
||||
// manually set `protocol.version` or `response.version`.
|
||||
pub use super::v8::{request, response};
|
||||
|
||||
/// Create a v9 connect request (version byte set to 9).
|
||||
pub fn new_connect_request(buffer_timeout: Option<u64>) -> (request::IpPacketRequest, u64) {
|
||||
let (mut req, id) = request::IpPacketRequest::new_connect_request(buffer_timeout);
|
||||
req.protocol.version = VERSION;
|
||||
(req, id)
|
||||
}
|
||||
|
||||
/// Create a v9 data request (version byte set to 9).
|
||||
pub fn new_data_request(data: bytes::Bytes) -> request::IpPacketRequest {
|
||||
let mut req = request::IpPacketRequest::new_data_request(data);
|
||||
req.protocol.version = VERSION;
|
||||
req
|
||||
}
|
||||
|
||||
/// Create a v9 IP packet response (version byte set to 9).
|
||||
pub fn new_ip_packet_response(ip_packet: bytes::Bytes) -> response::IpPacketResponse {
|
||||
let mut resp = response::IpPacketResponse::new_ip_packet(ip_packet);
|
||||
resp.version = VERSION;
|
||||
resp
|
||||
}
|
||||
@@ -1,13 +1,16 @@
|
||||
[package]
|
||||
name = "nym-mixnode-common"
|
||||
description = "Common crate for Nym Mix Nodes"
|
||||
version.workspace = true
|
||||
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
description = "Common crate for Nym Mix Nodes"
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
[package]
|
||||
name = "nym-network-defaults"
|
||||
version.workspace = true
|
||||
description = "Nym network defaults"
|
||||
edition.workspace = true
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
# Exclude build.rs from published crate - it's only used for dev-time sync
|
||||
# of env files and requires workspace context
|
||||
exclude = ["build.rs"]
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
[package]
|
||||
name = "nym-node-tester-utils"
|
||||
description = "Utils for the Nym Node Tester"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
description = "Utils for the Nym Node Tester"
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
[package]
|
||||
name = "nym-nonexhaustive-delayqueue"
|
||||
description = "A copy of tokio-util delay_queue with `Sleep` and `Instant` being replaced with`wasm_timer` equivalents"
|
||||
version.workspace = true
|
||||
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
description = "A copy of tokio-util delay_queue with `Sleep` and `Instant` being replaced with`wasm_timer` equivalents"
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
[package]
|
||||
name = "nym-cache"
|
||||
description = "Helper functions around a RwLock for writing to local cache of items"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
description = "Helper functions around a RwLock for writing to local cache of items"
|
||||
publish = true
|
||||
|
||||
[dependencies]
|
||||
tokio = { workspace = true, features = ["sync"] }
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
[package]
|
||||
name = "nym-common"
|
||||
description = "Runtime diagnostics for high frequency logging, debugging and error handling utilities"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
description = "Runtime diagnostics for high frequency logging, debugging and error handling utilities"
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
publish = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::fmt::{self, Write};
|
||||
|
||||
pub fn format_debug_bytes(bytes: &[u8]) -> Result<String, fmt::Error> {
|
||||
@@ -2,6 +2,7 @@
|
||||
// Copyright 2024 Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
pub mod debug;
|
||||
mod error;
|
||||
pub mod flood;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user