Compare commits
95 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 22d6eaf9b8 | |||
| a6b0b6f0f6 | |||
| a4bad2e697 | |||
| a19ee8f2aa | |||
| abfc68108a | |||
| ed90e358fb | |||
| c7d0e26946 | |||
| 8d65c25986 | |||
| a143d5f4f6 | |||
| c041d11673 | |||
| 82e82943aa | |||
| 19ffe217f1 | |||
| 079bfa52e7 | |||
| be9a2c26e7 | |||
| d6f3eb6411 | |||
| 144f3bed9c | |||
| c1174e64d4 | |||
| 312ecbe4dc | |||
| d2afa587e4 | |||
| 224c4c1870 | |||
| 3f8abdb74f | |||
| 0f6ec8610e | |||
| 3baac1292d | |||
| c3b8c4b2f7 | |||
| 271b9e545c | |||
| 9641f01670 | |||
| a7bb3e8d91 | |||
| dc88650d6d | |||
| 79ce611d21 | |||
| 960e817b8f | |||
| 8b03e66ba7 | |||
| 6a35581299 | |||
| ce124a29a7 | |||
| f62d8813e0 | |||
| a9cf016af2 | |||
| a8403b585b | |||
| e9a7b48da0 | |||
| 66792f57ed | |||
| f8d863249e | |||
| 7d59a2477a | |||
| eca88b0fa4 | |||
| b80a4c8614 | |||
| ec5d342e3a | |||
| 6565655861 | |||
| 5aba886f14 | |||
| 3ee73d541e | |||
| 4588a3036e | |||
| 6194ac07b8 | |||
| a7fcfef5a3 | |||
| fa927b82d8 | |||
| f724478763 | |||
| 040f4f2500 | |||
| 63002e784a | |||
| 4a0b683b70 | |||
| 9e84b1f0c1 | |||
| bf031ad6de | |||
| 933769401c | |||
| ddd85704bb | |||
| 17860c809f | |||
| 2d00fcd934 | |||
| c2c3df98cb | |||
| f429092e21 | |||
| d7ef68d8d1 | |||
| 1a334b575d | |||
| 2126736aff | |||
| a69aa23609 | |||
| 8a2d98e3ce | |||
| 9c4243914e | |||
| 143ede268d | |||
| 81bddb5f6d | |||
| 247ebb7c43 | |||
| 01c052e9a4 | |||
| 3880971e57 | |||
| 6bd31b9521 | |||
| 430c33eb04 | |||
| d45d1eb313 | |||
| 3cb3ebd79b | |||
| b42e5b063e | |||
| f6b30d0db6 | |||
| c33e4c0836 | |||
| be92ccf0da | |||
| 35bf49c48c | |||
| 7335a3dad4 | |||
| 698883c03f | |||
| 8ddef08c72 | |||
| 0d8b3abc6f | |||
| aa2f336904 | |||
| eacaf84430 | |||
| c284b1e8b1 | |||
| 7785d085cf | |||
| bb5b2eafcf | |||
| 09ea406c02 | |||
| 681c054890 | |||
| f623bbd57c | |||
| d4d576f363 |
@@ -1 +1,2 @@
|
||||
nym-validator-rewarder/.sqlx/** diff=nodiff
|
||||
nym-node-status-api/nym-node-status-api/.sqlx/** diff=nodiff
|
||||
|
||||
@@ -100,7 +100,6 @@ jobs:
|
||||
cp target/release/nymvisor $OUTPUT_DIR
|
||||
cp target/release/nym-node $OUTPUT_DIR
|
||||
cp target/release/nym-cli $OUTPUT_DIR
|
||||
cp target/release/explorer-api $OUTPUT_DIR
|
||||
if [ ${{ github.event_name == 'workflow_dispatch' && inputs.enable_deb == true }} = true ]; then
|
||||
cp target/debian/*.deb $OUTPUT_DIR
|
||||
fi
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
name: ci-check-ns-api-version
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "nym-node-status-api/**"
|
||||
|
||||
env:
|
||||
WORKING_DIRECTORY: "nym-node-status-api/nym-node-status-api"
|
||||
|
||||
jobs:
|
||||
check-if-tag-exists:
|
||||
runs-on: arc-ubuntu-22.04-dind
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Get version from cargo.toml
|
||||
uses: mikefarah/yq@v4.45.1
|
||||
id: get_version
|
||||
with:
|
||||
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
|
||||
|
||||
- name: Check if git tag exists
|
||||
run: |
|
||||
TAG=${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}
|
||||
if [[ -z "$TAG" ]]; then
|
||||
echo "Tag is empty"
|
||||
exit 1
|
||||
fi
|
||||
git ls-remote --tags origin | awk '{print $2}'
|
||||
if git ls-remote --tags origin | awk '{print $2}' | grep -q "refs/tags/$TAG$" ; then
|
||||
echo "Tag '$TAG' ALREADY EXISTS on the remote"
|
||||
exit 1
|
||||
else
|
||||
echo "Tag '$TAG' does not exist on the remote"
|
||||
fi
|
||||
- name: Check if harbor tag exists
|
||||
run: |
|
||||
TAG=${{ steps.get_version.outputs.result }}
|
||||
registry=https://harbor.nymte.ch
|
||||
repo_name=nym/node-status-api
|
||||
if [[ -z $TAG ]]; then
|
||||
echo "Tag is empty"
|
||||
exit 1
|
||||
fi
|
||||
curl -su ${{ secrets.HARBOR_ROBOT_USERNAME }}:${{ secrets.HARBOR_ROBOT_SECRET }} "$registry/v2/$repo_name/tags/list" | jq
|
||||
exists=$(curl -su ${{ secrets.HARBOR_ROBOT_USERNAME }}:${{ secrets.HARBOR_ROBOT_SECRET }} "$registry/v2/$repo_name/tags/list" | jq --arg tag $TAG '.tags | contains([$tag])' )
|
||||
if [[ $exists = "true" ]]; then
|
||||
echo "Version '$TAG' defined in Cargo.toml ALREADY EXISTS as tag in harbor repo"
|
||||
exit 1
|
||||
elif [[ $exists = "false" ]]; then
|
||||
echo "Version '$TAG' doesn't exist on the remote"
|
||||
else
|
||||
echo "Unknown output '$exists'"
|
||||
exit 1
|
||||
fi
|
||||
@@ -4,6 +4,126 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [2025.5-chokito] (2025-03-18)
|
||||
|
||||
- build(deps): bump braces from 3.0.2 to 3.0.3 in /sdk/typescript/packages/nodejs-client ([#5611])
|
||||
- build(deps-dev): bump webpack-dev-middleware from 5.3.3 to 5.3.4 in /wasm/client/internal-dev ([#5610])
|
||||
- Export lane queue lengths in sdk ([#5609])
|
||||
- Chore/more payment watcher debug endpoints ([#5608])
|
||||
- build(deps): bump @babel/helpers from 7.24.4 to 7.26.10 ([#5606])
|
||||
- Chore/update bls12 381 fork ([#5605])
|
||||
- chore: change auth v2 timestamp skew and allow values from the future ([#5604])
|
||||
- Chore/payment watcher debug endpoints ([#5601])
|
||||
- Allow resetting all SURB sender tags ([#5600])
|
||||
- introduce internal tool for checking signer status ([#5598])
|
||||
- build(deps-dev): bump webpack from 5.77.0 to 5.98.0 in /wasm/mix-fetch/internal-dev ([#5597])
|
||||
- build(deps): bump body-parser and express in /wasm/mix-fetch/internal-dev ([#5596])
|
||||
- build(deps): bump serve-static and express in /wasm/mix-fetch/internal-dev ([#5594])
|
||||
- build(deps-dev): bump ws from 8.13.0 to 8.18.1 in /wasm/mix-fetch/internal-dev ([#5593])
|
||||
- build(deps): bump cookie and express in /wasm/client/internal-dev ([#5592])
|
||||
- build(deps): bump cookie and express in /wasm/mix-fetch/internal-dev ([#5591])
|
||||
- build(deps): bump braces from 3.0.2 to 3.0.3 in /wasm/zknym-lib/internal-dev ([#5590])
|
||||
- build(deps): bump webpack-dev-middleware from 5.3.3 to 5.3.4 in /wasm/zknym-lib/internal-dev ([#5589])
|
||||
- build(deps): bump tempfile from 3.17.1 to 3.18.0 ([#5588])
|
||||
- build(deps): bump tokio from 1.43.0 to 1.44.0 ([#5587])
|
||||
- build(deps): bump the patch-updates group with 8 updates ([#5585])
|
||||
- build(deps): bump ring from 0.17.9 to 0.17.13 ([#5583])
|
||||
- delete double memo field in send modal ([#5578])
|
||||
- Server Side internal DoT/DoH opt out ([#5577])
|
||||
- Rust SDK SURB example: change hardcoded file to tempdir ([#5576])
|
||||
- Add /v3/nym-nodes ([#5569])
|
||||
- chore: start sending v2 sphinx packets ([#5554])
|
||||
- build(deps): bump the patch-updates group across 1 directory with 14 updates ([#5549])
|
||||
- build(deps): bump uuid from 1.13.2 to 1.15.1 ([#5542])
|
||||
- build(deps): bump rs_merkle from 1.4.2 to 1.5.0 ([#5541])
|
||||
- feature: v2 authentication request ([#5537])
|
||||
- Set RUSTUP_PERMIT_COPY_RENAME ([#5533])
|
||||
- feature: disallow routing mix packets to nodes not present in the topology ([#5526])
|
||||
- Make "Memo" visible per default on send NYM ([#5524])
|
||||
- feat: make sure any terminated task kills the watcher and write run info to db ([#5517])
|
||||
- Another total_stake SQL fix ([#5516])
|
||||
- Fix total_stake on SQL update ([#5514])
|
||||
- build(deps): bump flate2 from 1.0.35 to 1.1.0 ([#5510])
|
||||
- build(deps): bump itertools from 0.13.0 to 0.14.0 ([#5509])
|
||||
- build(deps): bump the patch-updates group with 2 updates ([#5505])
|
||||
- Treat gateways as Nym Nodes ([#5504])
|
||||
- Update version in Cargo.toml ([#5503])
|
||||
- feat: use ct_eq for checking bearer token ([#5501])
|
||||
- Add extra args for the probe ([#5499])
|
||||
- Fix stats bug & remove HM caching ([#5495])
|
||||
- fix: Cargo.lock for contracts ([#5489])
|
||||
- Display error messages if IPv4 or IPv6 address not found on nymtun0 ([#5465])
|
||||
|
||||
[#5611]: https://github.com/nymtech/nym/pull/5611
|
||||
[#5610]: https://github.com/nymtech/nym/pull/5610
|
||||
[#5609]: https://github.com/nymtech/nym/pull/5609
|
||||
[#5608]: https://github.com/nymtech/nym/pull/5608
|
||||
[#5606]: https://github.com/nymtech/nym/pull/5606
|
||||
[#5605]: https://github.com/nymtech/nym/pull/5605
|
||||
[#5604]: https://github.com/nymtech/nym/pull/5604
|
||||
[#5601]: https://github.com/nymtech/nym/pull/5601
|
||||
[#5600]: https://github.com/nymtech/nym/pull/5600
|
||||
[#5598]: https://github.com/nymtech/nym/pull/5598
|
||||
[#5597]: https://github.com/nymtech/nym/pull/5597
|
||||
[#5596]: https://github.com/nymtech/nym/pull/5596
|
||||
[#5594]: https://github.com/nymtech/nym/pull/5594
|
||||
[#5593]: https://github.com/nymtech/nym/pull/5593
|
||||
[#5592]: https://github.com/nymtech/nym/pull/5592
|
||||
[#5591]: https://github.com/nymtech/nym/pull/5591
|
||||
[#5590]: https://github.com/nymtech/nym/pull/5590
|
||||
[#5589]: https://github.com/nymtech/nym/pull/5589
|
||||
[#5588]: https://github.com/nymtech/nym/pull/5588
|
||||
[#5587]: https://github.com/nymtech/nym/pull/5587
|
||||
[#5585]: https://github.com/nymtech/nym/pull/5585
|
||||
[#5583]: https://github.com/nymtech/nym/pull/5583
|
||||
[#5578]: https://github.com/nymtech/nym/pull/5578
|
||||
[#5577]: https://github.com/nymtech/nym/pull/5577
|
||||
[#5576]: https://github.com/nymtech/nym/pull/5576
|
||||
[#5569]: https://github.com/nymtech/nym/pull/5569
|
||||
[#5554]: https://github.com/nymtech/nym/pull/5554
|
||||
[#5549]: https://github.com/nymtech/nym/pull/5549
|
||||
[#5542]: https://github.com/nymtech/nym/pull/5542
|
||||
[#5541]: https://github.com/nymtech/nym/pull/5541
|
||||
[#5537]: https://github.com/nymtech/nym/pull/5537
|
||||
[#5533]: https://github.com/nymtech/nym/pull/5533
|
||||
[#5526]: https://github.com/nymtech/nym/pull/5526
|
||||
[#5524]: https://github.com/nymtech/nym/pull/5524
|
||||
[#5517]: https://github.com/nymtech/nym/pull/5517
|
||||
[#5516]: https://github.com/nymtech/nym/pull/5516
|
||||
[#5514]: https://github.com/nymtech/nym/pull/5514
|
||||
[#5510]: https://github.com/nymtech/nym/pull/5510
|
||||
[#5509]: https://github.com/nymtech/nym/pull/5509
|
||||
[#5505]: https://github.com/nymtech/nym/pull/5505
|
||||
[#5504]: https://github.com/nymtech/nym/pull/5504
|
||||
[#5503]: https://github.com/nymtech/nym/pull/5503
|
||||
[#5501]: https://github.com/nymtech/nym/pull/5501
|
||||
[#5499]: https://github.com/nymtech/nym/pull/5499
|
||||
[#5495]: https://github.com/nymtech/nym/pull/5495
|
||||
[#5489]: https://github.com/nymtech/nym/pull/5489
|
||||
[#5465]: https://github.com/nymtech/nym/pull/5465
|
||||
|
||||
## [2025.4-dorina-patched] (2025-03-06)
|
||||
|
||||
- use legacy crypto for constructing SURB headers ([#5579])
|
||||
- bugfix: make sure to correctly decode response content when putting it into error message ([#5571])
|
||||
- Tweak surb management to be more conservative ([#5570])
|
||||
- Deserialize v5 authenticator requests ([#5568])
|
||||
- chore: additional logs when attempting to load ecash keys ([#5567])
|
||||
- add full response body to error message upon decoding failure ([#5566])
|
||||
- hotfix: ensure we bail on merkle leaves insertion upon missing data ([#5565])
|
||||
- feature: v2 authentication request (#5537) ([#5563])
|
||||
- Create authenticator v5 request/response types ([#5561])
|
||||
|
||||
[#5579]: https://github.com/nymtech/nym/pull/5579
|
||||
[#5571]: https://github.com/nymtech/nym/pull/5571
|
||||
[#5570]: https://github.com/nymtech/nym/pull/5570
|
||||
[#5568]: https://github.com/nymtech/nym/pull/5568
|
||||
[#5567]: https://github.com/nymtech/nym/pull/5567
|
||||
[#5566]: https://github.com/nymtech/nym/pull/5566
|
||||
[#5565]: https://github.com/nymtech/nym/pull/5565
|
||||
[#5563]: https://github.com/nymtech/nym/pull/5563
|
||||
[#5561]: https://github.com/nymtech/nym/pull/5561
|
||||
|
||||
## [2025.4-dorina] (2025-03-04)
|
||||
|
||||
- fixed sphinx version metrics registration ([#5546])
|
||||
@@ -138,7 +258,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- Downgrade harmless log message from info to debug ([#5403])
|
||||
- Redirect from mixnode page to nodes page ([#5397])
|
||||
- chore :update version of chain watcher and validator rewarder ([#5394])
|
||||
- bugfix: correctly handle ingore epoch roles flag ([#5390])
|
||||
- bugfix: correctly handle ignore epoch roles flag ([#5390])
|
||||
- bugfix: terminate mixnet socket listener on shutdown ([#5389])
|
||||
- feat: make client ignore dual mode nodes by default ([#5388])
|
||||
- Handle ecash network errors differently ([#5378])
|
||||
@@ -159,7 +279,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- Use expect in geodata test to give error message on failure ([#5314])
|
||||
- feature: periodically remove stale gateway messages ([#5312])
|
||||
- build(deps): bump the patch-updates group across 1 directory with 35 updates ([#5310])
|
||||
- Add dependabot assignes for the root cargo ecosystem ([#5297])
|
||||
- Add dependabot assigns for the root cargo ecosystem ([#5297])
|
||||
- Move tun constants to network defaults ([#5286])
|
||||
- Include IPINFO_API_TOKEN in nightly CI ([#5285])
|
||||
- Nyx Chain Watcher ([#5274])
|
||||
@@ -212,7 +332,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
## [2025.1-reeses] (2025-01-15)
|
||||
|
||||
- Feture/legacy alert ([#5346])
|
||||
- Feature, Future/legacy alert ([#5346])
|
||||
- chore: readjusted --mode behaviour to fix the regression ([#5331])
|
||||
- chore: apply 1.84 linter suggestions ([#5330])
|
||||
- bugfix: make sure refresh data key matches bond info ([#5329])
|
||||
@@ -292,7 +412,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
## [2024.14-crunch-patched] (2024-12-17)
|
||||
|
||||
- Fixes an issue to allow previously registred clients to connect to latest nym-nodes
|
||||
- Fixes an issue to allow previously registered clients to connect to latest nym-nodes
|
||||
- Fixes compatibility issues between nym-nodes and older clients
|
||||
|
||||
## [2024.14-crunch] (2024-12-11)
|
||||
@@ -300,7 +420,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- Merge/release/2024.14-crunch ([#5242])
|
||||
- bugfix: added explicit openapi servers to account for route prefixes ([#5237])
|
||||
- Further config score adjustments ([#5225])
|
||||
- feature: remve any filtering on node semver ([#5224])
|
||||
- feature: remove any filtering on node semver ([#5224])
|
||||
- Backport #5218 ([#5220])
|
||||
- Derive serialize for UserAgent (#5210) ([#5217])
|
||||
- dont consider legacy nodes for rewarded set selection ([#5215])
|
||||
@@ -479,7 +599,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- bugfix/feature: added NymApiClient method to get all skimmed nodes ([#5062])
|
||||
- Merge1/release/2024.13 magura ([#5061])
|
||||
- added hacky routes to return nymnodes alongside legacy nodes ([#5051])
|
||||
- bugfix: mark migrated gateways as rewarded in the previous epoch in case theyre in the rewarded set ([#5049])
|
||||
- bugfix: mark migrated gateways as rewarded in the previous epoch in case they're, their, there in the rewarded set ([#5049])
|
||||
- bugfix: adjust runtime storage migration ([#5047])
|
||||
- bugfix: supersede 'cb13be27f8f61d9ae74d924e85d2e6787895eb14' by using… ([#5046])
|
||||
- bugfix: restore default http port for nym-api ([#5045])
|
||||
@@ -540,7 +660,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- Fix broken build after merge ([#4937])
|
||||
- bugfix: correctly paginate through 'search_tx' endpoint ([#4936])
|
||||
- Add more conversions for responses of authenticator messages ([#4929])
|
||||
- Directory Sevices v2.1 ([#4903])
|
||||
- Directory Services, Devices v2.1 ([#4903])
|
||||
- Migrate Legacy Node (Frontend) ([#4826])
|
||||
- Fix critical issues SI84 and SI85 from Cure53 ([#4758])
|
||||
|
||||
@@ -924,7 +1044,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- Remove stale peers ([#4640])
|
||||
- Add generic wg private network routing ([#4636])
|
||||
- Feature/new node endpoints ([#4635])
|
||||
- standarised ContractBuildInformation and added it to all contracts ([#4631])
|
||||
- standardised ContractBuildInformation and added it to all contracts ([#4631])
|
||||
- validate nym-node public ips on startup ([#4630])
|
||||
- Bump defguard wg ([#4625])
|
||||
- Fix cargo warnings ([#4624])
|
||||
@@ -1545,7 +1665,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- clean-up nym-api startup arguments/flags to use clap 3 and its macro-derived arguments ([#2772])
|
||||
- renamed all references to validator_api to nym_api
|
||||
- renamed all references to nymd to nyxd ([#2696])
|
||||
- all-binaries: standarised argument names (note: old names should still be accepted) ([#2762]
|
||||
- all-binaries: standardised argument names (note: old names should still be accepted) ([#2762]
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -2050,7 +2170,7 @@ The release also include some additional work for distributed key generation in
|
||||
- Explorer UI tests missing data-testid [\#903](https://github.com/nymtech/nym/pull/903) ([tommyv1987](https://github.com/tommyv1987))
|
||||
- Fix up Nym-Wallet README.md [\#899](https://github.com/nymtech/nym/pull/899) ([tommyv1987](https://github.com/tommyv1987))
|
||||
- Feature/batch delegator rewarding [\#898](https://github.com/nymtech/nym/pull/898) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Bug mapp nodemap [\#897](https://github.com/nymtech/nym/pull/897) ([Aid19801](https://github.com/Aid19801))
|
||||
- Bug map nodemap [\#897](https://github.com/nymtech/nym/pull/897) ([Aid19801](https://github.com/Aid19801))
|
||||
- Bug fix/macos keyboard shortcuts [\#896](https://github.com/nymtech/nym/pull/896) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Add a Mobile Nav to the Network Explorer [\#895](https://github.com/nymtech/nym/pull/895) ([Aid19801](https://github.com/Aid19801))
|
||||
- Only use ts-rs in tests [\#894](https://github.com/nymtech/nym/pull/894) ([durch](https://github.com/durch))
|
||||
|
||||
@@ -98,9 +98,9 @@ members = [
|
||||
"common/wireguard",
|
||||
"common/wireguard-types",
|
||||
"documentation/autodoc",
|
||||
"explorer-api",
|
||||
"explorer-api/explorer-api-requests",
|
||||
"explorer-api/explorer-client",
|
||||
# "explorer-api",
|
||||
# "explorer-api/explorer-api-requests",
|
||||
# "explorer-api/explorer-client",
|
||||
"gateway",
|
||||
"integrations/bity",
|
||||
"nym-api",
|
||||
@@ -137,7 +137,7 @@ members = [
|
||||
"tools/internal/testnet-manager",
|
||||
"tools/internal/testnet-manager",
|
||||
"tools/internal/testnet-manager/dkg-bypass-contract",
|
||||
"tools/internal/testnet-manager/dkg-bypass-contract",
|
||||
"tools/internal/testnet-manager/dkg-bypass-contract", "tools/internal/validator-status-check",
|
||||
"tools/nym-cli",
|
||||
"tools/nym-id-cli",
|
||||
"tools/nym-nr-query",
|
||||
@@ -153,7 +153,6 @@ members = [
|
||||
default-members = [
|
||||
"clients/native",
|
||||
"clients/socks5",
|
||||
"explorer-api",
|
||||
"nym-api",
|
||||
"nym-credential-proxy/nym-credential-proxy",
|
||||
"nym-node",
|
||||
@@ -194,7 +193,7 @@ ammonia = "4"
|
||||
anyhow = "1.0.97"
|
||||
arc-swap = "1.7.1"
|
||||
argon2 = "0.5.0"
|
||||
async-trait = "0.1.87"
|
||||
async-trait = "0.1.88"
|
||||
axum = "0.7.5"
|
||||
axum-client-ip = "0.6.1"
|
||||
axum-extra = "0.9.4"
|
||||
@@ -209,15 +208,15 @@ blake3 = "1.6.1"
|
||||
bloomfilter = "1.0.14"
|
||||
bs58 = "0.5.1"
|
||||
bytecodec = "0.4.15"
|
||||
bytes = "1.7.2"
|
||||
bytes = "1.10.1"
|
||||
cargo_metadata = "0.18.1"
|
||||
celes = "2.5.0"
|
||||
celes = "2.6.0"
|
||||
cfg-if = "1.0.0"
|
||||
chacha20 = "0.9.0"
|
||||
chacha20poly1305 = "0.10.1"
|
||||
chrono = "0.4.40"
|
||||
cipher = "0.4.3"
|
||||
clap = "4.5.31"
|
||||
clap = "4.5.32"
|
||||
clap_complete = "4.5"
|
||||
clap_complete_fig = "4.5"
|
||||
colored = "2.2"
|
||||
@@ -241,7 +240,8 @@ doc-comment = "0.3"
|
||||
dotenvy = "0.15.6"
|
||||
ecdsa = "0.16"
|
||||
ed25519-dalek = "2.1"
|
||||
env_logger = "0.11.6"
|
||||
encoding_rs = "0.8.35"
|
||||
env_logger = "0.11.7"
|
||||
envy = "0.4"
|
||||
etherparse = "0.13.0"
|
||||
eyre = "0.6.9"
|
||||
@@ -263,7 +263,7 @@ http = "1"
|
||||
http-body-util = "0.1"
|
||||
httpcodec = "0.2.3"
|
||||
human-repr = "1.1.0"
|
||||
humantime = "2.1.0"
|
||||
humantime = "2.2.0"
|
||||
humantime-serde = "1.1.1"
|
||||
hyper = "1.6.0"
|
||||
hyper-util = "0.1"
|
||||
@@ -284,7 +284,7 @@ moka = { version = "0.12", features = ["future"] }
|
||||
nix = "0.27.1"
|
||||
notify = "5.1.0"
|
||||
okapi = "0.7.0"
|
||||
once_cell = "1.20.3"
|
||||
once_cell = "1.21.1"
|
||||
opentelemetry = "0.19.0"
|
||||
opentelemetry-jaeger = "0.18.0"
|
||||
parking_lot = "0.12.3"
|
||||
@@ -307,12 +307,12 @@ reqwest = { version = "0.12.4", default-features = false }
|
||||
rocket = "0.5.0"
|
||||
rocket_cors = "0.6.0"
|
||||
rocket_okapi = "0.8.0"
|
||||
rs_merkle = "1.4.2"
|
||||
rs_merkle = "1.5.0"
|
||||
safer-ffi = "0.1.13"
|
||||
schemars = "0.8.22"
|
||||
semver = "1.0.25"
|
||||
serde = "1.0.217"
|
||||
serde_bytes = "0.11.16"
|
||||
semver = "1.0.26"
|
||||
serde = "1.0.219"
|
||||
serde_bytes = "0.11.17"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0.140"
|
||||
serde_json_path = "0.7.2"
|
||||
@@ -321,8 +321,8 @@ serde_with = "3.9.0"
|
||||
serde_yaml = "0.9.25"
|
||||
sha2 = "0.10.8"
|
||||
si-scale = "0.2.3"
|
||||
sphinx-packet = "0.3.1"
|
||||
sqlx = "0.7.4"
|
||||
sphinx-packet = "=0.3.2"
|
||||
sqlx = "0.8.3"
|
||||
strum = "0.26"
|
||||
strum_macros = "0.26"
|
||||
subtle-encoding = "0.5"
|
||||
@@ -330,16 +330,16 @@ syn = "1"
|
||||
sysinfo = "0.33.0"
|
||||
tap = "1.0.1"
|
||||
tar = "0.4.44"
|
||||
tempfile = "3.15"
|
||||
tempfile = "3.19"
|
||||
thiserror = "2.0"
|
||||
time = "0.3.37"
|
||||
tokio = "1.43"
|
||||
time = "0.3.39"
|
||||
tokio = "1.44"
|
||||
tokio-postgres = "0.7"
|
||||
tokio-stream = "0.1.17"
|
||||
tokio-test = "0.4.4"
|
||||
tokio-tun = "0.11.5"
|
||||
tokio-tungstenite = { version = "0.20.1" }
|
||||
tokio-util = "0.7.13"
|
||||
tokio-util = "0.7.14"
|
||||
toml = "0.8.20"
|
||||
tower = "0.5.2"
|
||||
tower-http = "0.5.2"
|
||||
@@ -361,7 +361,7 @@ vergen = { version = "=8.3.1", default-features = false }
|
||||
walkdir = "2"
|
||||
wasm-bindgen-test = "0.3.49"
|
||||
x25519-dalek = "2.0.0"
|
||||
zeroize = "1.6.0"
|
||||
zeroize = "1.8.1"
|
||||
|
||||
prometheus = { version = "0.13.0" }
|
||||
|
||||
@@ -369,9 +369,9 @@ prometheus = { version = "0.13.0" }
|
||||
# unfortunately until https://github.com/zkcrypto/bls12_381/issues/10 is resolved, we have to rely on the fork
|
||||
# as we need to be able to serialize Gt so that we could create the lookup table for baby-step-giant-step algorithm
|
||||
# plus to make our live easier we need serde support from https://github.com/zkcrypto/bls12_381/pull/125
|
||||
bls12_381 = { git = "https://github.com/jstuczyn/bls12_381", default-features = false, branch = "temp/experimental-serdect" }
|
||||
bls12_381 = { git = "https://github.com/jstuczyn/bls12_381", default-features = false, branch = "temp/experimental-serdect-updated" }
|
||||
group = { version = "0.13.0", default-features = false }
|
||||
ff = { version = "0.13.0", default-features = false }
|
||||
ff = { version = "0.13.1", default-features = false }
|
||||
subtle = "2.5.0"
|
||||
|
||||
# cosmwasm-related
|
||||
@@ -402,7 +402,7 @@ prost = { version = "0.13", default-features = false }
|
||||
gloo-utils = "0.2.0"
|
||||
gloo-net = "0.6.0"
|
||||
|
||||
indexed_db_futures = "0.6.0"
|
||||
indexed_db_futures = "0.6.1"
|
||||
js-sys = "0.3.76"
|
||||
serde-wasm-bindgen = "0.6.5"
|
||||
tsify = "0.4.5"
|
||||
@@ -446,4 +446,4 @@ dbg_macro = "deny"
|
||||
exit = "deny"
|
||||
panic = "deny"
|
||||
unimplemented = "deny"
|
||||
unreachable = "deny"
|
||||
unreachable = "deny"
|
||||
|
||||
@@ -67,3 +67,13 @@ As a general approach, licensing is as follows this pattern:
|
||||
- documentation is Apache 2.0 or CC0-1.0
|
||||
|
||||
Nym Node Operators and Validators Terms and Conditions can be found [here](https://nym.com/operators-validators-terms).
|
||||
|
||||
## Getting Started
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
```
|
||||
|
||||
```bash
|
||||
yarn build
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.49"
|
||||
version = "1.1.51"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.49"
|
||||
version = "1.1.51"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
||||
edition = "2021"
|
||||
|
||||
@@ -6,14 +6,15 @@ pub mod v1;
|
||||
pub mod v2;
|
||||
pub mod v3;
|
||||
pub mod v4;
|
||||
pub mod v5;
|
||||
|
||||
mod error;
|
||||
mod util;
|
||||
|
||||
pub use error::Error;
|
||||
pub use v4 as latest;
|
||||
pub use v5 as latest;
|
||||
|
||||
pub const CURRENT_VERSION: u8 = 4;
|
||||
pub const CURRENT_VERSION: u8 = 5;
|
||||
|
||||
fn make_bincode_serializer() -> impl bincode::Options {
|
||||
use bincode::Options;
|
||||
|
||||
@@ -8,8 +8,8 @@ use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
|
||||
use crate::{
|
||||
v1, v2, v3,
|
||||
v4::{self, registration::IpPair},
|
||||
v1, v2, v3, v4,
|
||||
v5::{self, registration::IpPair},
|
||||
Error,
|
||||
};
|
||||
|
||||
@@ -19,6 +19,7 @@ pub enum AuthenticatorVersion {
|
||||
V2,
|
||||
V3,
|
||||
V4,
|
||||
V5,
|
||||
UNKNOWN,
|
||||
}
|
||||
|
||||
@@ -34,6 +35,8 @@ impl From<Protocol> for AuthenticatorVersion {
|
||||
AuthenticatorVersion::V3
|
||||
} else if value.version == v4::VERSION {
|
||||
AuthenticatorVersion::V4
|
||||
} else if value.version == v5::VERSION {
|
||||
AuthenticatorVersion::V5
|
||||
} else {
|
||||
AuthenticatorVersion::UNKNOWN
|
||||
}
|
||||
@@ -68,6 +71,12 @@ impl InitMessage for v4::registration::InitMessage {
|
||||
}
|
||||
}
|
||||
|
||||
impl InitMessage for v5::registration::InitMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.pub_key
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FinalMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey;
|
||||
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error>;
|
||||
@@ -138,6 +147,24 @@ impl FinalMessage for v4::registration::FinalMessage {
|
||||
self.gateway_client.verify(private_key, nonce)
|
||||
}
|
||||
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.gateway_client.private_ips.into()
|
||||
}
|
||||
|
||||
fn credential(&self) -> Option<CredentialSpendingData> {
|
||||
self.credential.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl FinalMessage for v5::registration::FinalMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.gateway_client.pub_key
|
||||
}
|
||||
|
||||
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
self.gateway_client.verify(private_key, nonce)
|
||||
}
|
||||
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.gateway_client.private_ips
|
||||
}
|
||||
@@ -182,29 +209,39 @@ impl TopUpMessage for v4::topup::TopUpMessage {
|
||||
}
|
||||
}
|
||||
|
||||
impl TopUpMessage for v5::topup::TopUpMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.pub_key
|
||||
}
|
||||
|
||||
fn credential(&self) -> CredentialSpendingData {
|
||||
self.credential.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub enum AuthenticatorRequest {
|
||||
Initial {
|
||||
msg: Box<dyn InitMessage + Send + Sync + 'static>,
|
||||
protocol: Protocol,
|
||||
reply_to: Recipient,
|
||||
reply_to: Option<Recipient>,
|
||||
request_id: u64,
|
||||
},
|
||||
Final {
|
||||
msg: Box<dyn FinalMessage + Send + Sync + 'static>,
|
||||
protocol: Protocol,
|
||||
reply_to: Recipient,
|
||||
reply_to: Option<Recipient>,
|
||||
request_id: u64,
|
||||
},
|
||||
QueryBandwidth {
|
||||
msg: Box<dyn QueryBandwidthMessage + Send + Sync + 'static>,
|
||||
protocol: Protocol,
|
||||
reply_to: Recipient,
|
||||
reply_to: Option<Recipient>,
|
||||
request_id: u64,
|
||||
},
|
||||
TopUpBandwidth {
|
||||
msg: Box<dyn TopUpMessage + Send + Sync + 'static>,
|
||||
protocol: Protocol,
|
||||
reply_to: Recipient,
|
||||
reply_to: Option<Recipient>,
|
||||
request_id: u64,
|
||||
},
|
||||
}
|
||||
@@ -218,7 +255,7 @@ impl From<v1::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
version: value.version,
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
},
|
||||
reply_to: value.reply_to,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v1::request::AuthenticatorRequestData::Final(gateway_client) => Self::Final {
|
||||
@@ -227,7 +264,7 @@ impl From<v1::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
version: value.version,
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
},
|
||||
reply_to: value.reply_to,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v1::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
|
||||
@@ -237,7 +274,7 @@ impl From<v1::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
version: value.version,
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
},
|
||||
reply_to: value.reply_to,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
@@ -251,20 +288,20 @@ impl From<v2::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
v2::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
|
||||
msg: Box::new(init_message),
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v2::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
|
||||
msg: final_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v2::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
|
||||
Self::QueryBandwidth {
|
||||
msg: Box::new(peer_public_key),
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
@@ -278,20 +315,20 @@ impl From<v3::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
v3::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
|
||||
msg: Box::new(init_message),
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v3::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
|
||||
msg: final_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v3::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
|
||||
Self::QueryBandwidth {
|
||||
msg: Box::new(peer_public_key),
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
@@ -299,7 +336,7 @@ impl From<v3::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
Self::TopUpBandwidth {
|
||||
msg: top_up_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
@@ -313,20 +350,20 @@ impl From<v4::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
v4::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
|
||||
msg: Box::new(init_message),
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v4::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
|
||||
msg: final_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v4::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
|
||||
Self::QueryBandwidth {
|
||||
msg: Box::new(peer_public_key),
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
@@ -334,7 +371,42 @@ impl From<v4::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
Self::TopUpBandwidth {
|
||||
msg: top_up_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: value.reply_to,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
fn from(value: v5::request::AuthenticatorRequest) -> Self {
|
||||
match value.data {
|
||||
v5::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
|
||||
msg: Box::new(init_message),
|
||||
protocol: value.protocol,
|
||||
reply_to: None,
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v5::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
|
||||
msg: final_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: None,
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v5::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
|
||||
Self::QueryBandwidth {
|
||||
msg: Box::new(peer_public_key),
|
||||
protocol: value.protocol,
|
||||
reply_to: None,
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
v5::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => {
|
||||
Self::TopUpBandwidth {
|
||||
msg: top_up_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: None,
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,478 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
|
||||
|
||||
use crate::{v4, v5};
|
||||
|
||||
impl From<v4::request::AuthenticatorRequest> for v5::request::AuthenticatorRequest {
|
||||
fn from(authenticator_request: v4::request::AuthenticatorRequest) -> Self {
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
version: 5,
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
},
|
||||
data: authenticator_request.data.into(),
|
||||
request_id: authenticator_request.request_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::request::AuthenticatorRequestData> for v5::request::AuthenticatorRequestData {
|
||||
fn from(authenticator_request_data: v4::request::AuthenticatorRequestData) -> Self {
|
||||
match authenticator_request_data {
|
||||
v4::request::AuthenticatorRequestData::Initial(init_msg) => {
|
||||
v5::request::AuthenticatorRequestData::Initial(init_msg.into())
|
||||
}
|
||||
v4::request::AuthenticatorRequestData::Final(final_msg) => {
|
||||
v5::request::AuthenticatorRequestData::Final(Box::new((*final_msg).into()))
|
||||
}
|
||||
v4::request::AuthenticatorRequestData::QueryBandwidth(pub_key) => {
|
||||
v5::request::AuthenticatorRequestData::QueryBandwidth(pub_key)
|
||||
}
|
||||
v4::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => {
|
||||
v5::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::registration::InitMessage> for v5::registration::InitMessage {
|
||||
fn from(init_msg: v4::registration::InitMessage) -> Self {
|
||||
Self {
|
||||
pub_key: init_msg.pub_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::registration::FinalMessage> for v5::registration::FinalMessage {
|
||||
fn from(final_msg: v4::registration::FinalMessage) -> Self {
|
||||
Self {
|
||||
gateway_client: final_msg.gateway_client.into(),
|
||||
credential: final_msg.credential,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::registration::GatewayClient> for v5::registration::GatewayClient {
|
||||
fn from(gateway_client: v4::registration::GatewayClient) -> Self {
|
||||
Self {
|
||||
pub_key: gateway_client.pub_key,
|
||||
private_ips: gateway_client.private_ips.into(),
|
||||
mac: gateway_client.mac.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::registration::GatewayClient> for v4::registration::GatewayClient {
|
||||
fn from(gateway_client: v5::registration::GatewayClient) -> Self {
|
||||
Self {
|
||||
pub_key: gateway_client.pub_key,
|
||||
private_ips: gateway_client.private_ips.into(),
|
||||
mac: gateway_client.mac.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::registration::ClientMac> for v5::registration::ClientMac {
|
||||
fn from(client_mac: v4::registration::ClientMac) -> Self {
|
||||
Self::new((*client_mac).clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::registration::ClientMac> for v4::registration::ClientMac {
|
||||
fn from(client_mac: v5::registration::ClientMac) -> Self {
|
||||
Self::new((*client_mac).clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Box<v4::topup::TopUpMessage>> for Box<v5::topup::TopUpMessage> {
|
||||
fn from(top_up_message: Box<v4::topup::TopUpMessage>) -> Self {
|
||||
Box::new(v5::topup::TopUpMessage {
|
||||
pub_key: top_up_message.pub_key,
|
||||
credential: top_up_message.credential,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::response::AuthenticatorResponse> for v5::response::AuthenticatorResponse {
|
||||
fn from(value: v4::response::AuthenticatorResponse) -> Self {
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
version: 5,
|
||||
service_provider_type: value.protocol.service_provider_type,
|
||||
},
|
||||
data: value.data.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::response::AuthenticatorResponseData> for v5::response::AuthenticatorResponseData {
|
||||
fn from(authenticator_response_data: v4::response::AuthenticatorResponseData) -> Self {
|
||||
match authenticator_response_data {
|
||||
v4::response::AuthenticatorResponseData::PendingRegistration(pending_response) => {
|
||||
v5::response::AuthenticatorResponseData::PendingRegistration(
|
||||
pending_response.into(),
|
||||
)
|
||||
}
|
||||
v4::response::AuthenticatorResponseData::Registered(registered_response) => {
|
||||
v5::response::AuthenticatorResponseData::Registered(registered_response.into())
|
||||
}
|
||||
v4::response::AuthenticatorResponseData::RemainingBandwidth(
|
||||
remaining_bandwidth_response,
|
||||
) => v5::response::AuthenticatorResponseData::RemainingBandwidth(
|
||||
remaining_bandwidth_response.into(),
|
||||
),
|
||||
v4::response::AuthenticatorResponseData::TopUpBandwidth(top_up_response) => {
|
||||
v5::response::AuthenticatorResponseData::TopUpBandwidth(top_up_response.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::response::RegisteredResponse> for v5::response::RegisteredResponse {
|
||||
fn from(value: v4::response::RegisteredResponse) -> Self {
|
||||
Self {
|
||||
request_id: value.request_id,
|
||||
reply: value.reply.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::response::PendingRegistrationResponse> for v5::response::PendingRegistrationResponse {
|
||||
fn from(value: v4::response::PendingRegistrationResponse) -> Self {
|
||||
Self {
|
||||
request_id: value.request_id,
|
||||
reply: value.reply.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::registration::RegistrationData> for v5::registration::RegistrationData {
|
||||
fn from(value: v4::registration::RegistrationData) -> Self {
|
||||
Self {
|
||||
nonce: value.nonce,
|
||||
gateway_data: value.gateway_data.into(),
|
||||
wg_port: value.wg_port,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::registration::RegistrationData> for v4::registration::RegistrationData {
|
||||
fn from(value: v5::registration::RegistrationData) -> Self {
|
||||
Self {
|
||||
nonce: value.nonce,
|
||||
gateway_data: value.gateway_data.into(),
|
||||
wg_port: value.wg_port,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::response::RemainingBandwidthResponse> for v5::response::RemainingBandwidthResponse {
|
||||
fn from(value: v4::response::RemainingBandwidthResponse) -> Self {
|
||||
Self {
|
||||
request_id: value.request_id,
|
||||
reply: value.reply.map(Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::response::TopUpBandwidthResponse> for v5::response::TopUpBandwidthResponse {
|
||||
fn from(value: v4::response::TopUpBandwidthResponse) -> Self {
|
||||
Self {
|
||||
request_id: value.request_id,
|
||||
reply: value.reply.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::registration::RegistredData> for v5::registration::RegistredData {
|
||||
fn from(value: v4::registration::RegistredData) -> Self {
|
||||
Self {
|
||||
pub_key: value.pub_key,
|
||||
private_ips: value.private_ips.into(),
|
||||
wg_port: value.wg_port,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::registration::RemainingBandwidthData> for v5::registration::RemainingBandwidthData {
|
||||
fn from(value: v4::registration::RemainingBandwidthData) -> Self {
|
||||
Self {
|
||||
available_bandwidth: value.available_bandwidth,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::registration::IpPair> for v5::registration::IpPair {
|
||||
fn from(value: v4::registration::IpPair) -> Self {
|
||||
Self {
|
||||
ipv4: value.ipv4,
|
||||
ipv6: value.ipv6,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::registration::IpPair> for v4::registration::IpPair {
|
||||
fn from(value: v5::registration::IpPair) -> Self {
|
||||
Self {
|
||||
ipv4: value.ipv4,
|
||||
ipv6: value.ipv6,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{
|
||||
net::{Ipv4Addr, Ipv6Addr},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
use nym_crypto::asymmetric::encryption::PrivateKey;
|
||||
use nym_sphinx::addressing::Recipient;
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
use x25519_dalek::PublicKey;
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
util::tests::{CREDENTIAL_BYTES, RECIPIENT},
|
||||
v4,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn upgrade_initial_req() {
|
||||
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let (msg, _) = v4::request::AuthenticatorRequest::new_initial_request(
|
||||
v4::registration::InitMessage::new(pub_key),
|
||||
reply_to,
|
||||
);
|
||||
let upgraded_msg = v5::request::AuthenticatorRequest::from(msg);
|
||||
|
||||
assert_eq!(
|
||||
upgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 5,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v5::request::AuthenticatorRequestData::Initial(v5::registration::InitMessage {
|
||||
pub_key
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_final_req() {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let local_secret = PrivateKey::new(&mut rng);
|
||||
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
|
||||
let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap();
|
||||
let ipv6 = Ipv6Addr::from_str("fc01::a0a").unwrap();
|
||||
let ips = v4::registration::IpPair::new(ipv4, ipv6);
|
||||
let nonce = 42;
|
||||
let gateway_client = v4::registration::GatewayClient::new(
|
||||
&local_secret,
|
||||
(&remote_secret).into(),
|
||||
ips,
|
||||
nonce,
|
||||
);
|
||||
let credential = Some(CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap());
|
||||
let final_message = v4::registration::FinalMessage {
|
||||
gateway_client: gateway_client.clone(),
|
||||
credential: credential.clone(),
|
||||
};
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let (msg, _) =
|
||||
v4::request::AuthenticatorRequest::new_final_request(final_message, reply_to);
|
||||
let upgraded_msg = v5::request::AuthenticatorRequest::from(msg);
|
||||
|
||||
assert_eq!(
|
||||
upgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 5,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v5::request::AuthenticatorRequestData::Final(Box::new(
|
||||
v5::registration::FinalMessage {
|
||||
gateway_client: v5::registration::GatewayClient::new(
|
||||
&local_secret,
|
||||
(&remote_secret).into(),
|
||||
v5::registration::IpPair::new(ipv4, ipv6),
|
||||
nonce
|
||||
),
|
||||
credential
|
||||
}
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_query_req() {
|
||||
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let (msg, _) = v4::request::AuthenticatorRequest::new_query_request(pub_key, reply_to);
|
||||
let upgraded_msg = v5::request::AuthenticatorRequest::from(msg);
|
||||
|
||||
assert_eq!(
|
||||
upgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 5,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v5::request::AuthenticatorRequestData::QueryBandwidth(pub_key)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_pending_reg_resp() {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let local_secret = PrivateKey::new(&mut rng);
|
||||
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
|
||||
let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap();
|
||||
let ipv6 = Ipv6Addr::from_str("fc01::a0a").unwrap();
|
||||
let ips = v4::registration::IpPair::new(ipv4, ipv6);
|
||||
let nonce = 42;
|
||||
let wg_port = 51822;
|
||||
let gateway_data = v4::registration::GatewayClient::new(
|
||||
&local_secret,
|
||||
(&remote_secret).into(),
|
||||
ips,
|
||||
nonce,
|
||||
);
|
||||
let registration_data = v4::registration::RegistrationData {
|
||||
nonce,
|
||||
gateway_data,
|
||||
wg_port,
|
||||
};
|
||||
let request_id = 123;
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let msg = v4::response::AuthenticatorResponse::new_pending_registration_success(
|
||||
registration_data,
|
||||
request_id,
|
||||
reply_to,
|
||||
);
|
||||
let upgraded_msg = v5::response::AuthenticatorResponse::from(msg);
|
||||
|
||||
assert_eq!(
|
||||
upgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 5,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v5::response::AuthenticatorResponseData::PendingRegistration(
|
||||
v5::response::PendingRegistrationResponse {
|
||||
request_id,
|
||||
reply: v5::registration::RegistrationData {
|
||||
nonce,
|
||||
gateway_data: v5::registration::GatewayClient::new(
|
||||
&local_secret,
|
||||
(&remote_secret).into(),
|
||||
v5::registration::IpPair::new(ipv4, ipv6),
|
||||
nonce
|
||||
),
|
||||
wg_port
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_registered_resp() {
|
||||
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
|
||||
let ipv4 = Ipv4Addr::from_str("10.1.10.10").unwrap();
|
||||
let ipv6 = Ipv6Addr::from_str("fc01::a0a").unwrap();
|
||||
let private_ips = v4::registration::IpPair::new(ipv4, ipv6);
|
||||
let wg_port = 51822;
|
||||
let registred_data = v4::registration::RegistredData {
|
||||
pub_key,
|
||||
private_ips,
|
||||
wg_port,
|
||||
};
|
||||
let request_id = 123;
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let msg = v4::response::AuthenticatorResponse::new_registered(
|
||||
registred_data,
|
||||
reply_to,
|
||||
request_id,
|
||||
);
|
||||
let upgraded_msg = v5::response::AuthenticatorResponse::from(msg);
|
||||
|
||||
assert_eq!(
|
||||
upgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 5,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v5::response::AuthenticatorResponseData::Registered(v5::response::RegisteredResponse {
|
||||
request_id,
|
||||
reply: v5::registration::RegistredData {
|
||||
wg_port,
|
||||
pub_key,
|
||||
private_ips: v5::registration::IpPair::new(ipv4, ipv6)
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_remaining_bandwidth_resp() {
|
||||
let available_bandwidth = 42;
|
||||
let remaining_bandwidth_data = Some(v4::registration::RemainingBandwidthData {
|
||||
available_bandwidth,
|
||||
});
|
||||
let request_id = 123;
|
||||
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
|
||||
|
||||
let msg = v4::response::AuthenticatorResponse::new_remaining_bandwidth(
|
||||
remaining_bandwidth_data,
|
||||
reply_to,
|
||||
request_id,
|
||||
);
|
||||
let upgraded_msg = v5::response::AuthenticatorResponse::from(msg);
|
||||
|
||||
assert_eq!(
|
||||
upgraded_msg.protocol,
|
||||
Protocol {
|
||||
version: 5,
|
||||
service_provider_type: ServiceProviderType::Authenticator
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v5::response::AuthenticatorResponseData::RemainingBandwidth(
|
||||
v5::response::RemainingBandwidthResponse {
|
||||
request_id,
|
||||
reply: Some(v5::registration::RemainingBandwidthData {
|
||||
available_bandwidth,
|
||||
})
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod conversion;
|
||||
pub mod registration;
|
||||
pub mod request;
|
||||
pub mod response;
|
||||
pub mod topup;
|
||||
|
||||
pub const VERSION: u8 = 5;
|
||||
@@ -0,0 +1,287 @@
|
||||
// -2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::Error;
|
||||
use base64::{engine::general_purpose, Engine};
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
use nym_network_defaults::constants::{WG_TUN_DEVICE_IP_ADDRESS_V4, WG_TUN_DEVICE_IP_ADDRESS_V6};
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
use std::time::SystemTime;
|
||||
use std::{fmt, ops::Deref, str::FromStr};
|
||||
|
||||
#[cfg(feature = "verify")]
|
||||
use hmac::{Hmac, Mac};
|
||||
#[cfg(feature = "verify")]
|
||||
use nym_crypto::asymmetric::encryption::PrivateKey;
|
||||
#[cfg(feature = "verify")]
|
||||
use sha2::Sha256;
|
||||
|
||||
pub type PendingRegistrations = HashMap<PeerPublicKey, RegistrationData>;
|
||||
pub type PrivateIPs = HashMap<IpPair, Taken>;
|
||||
|
||||
#[cfg(feature = "verify")]
|
||||
pub type HmacSha256 = Hmac<Sha256>;
|
||||
|
||||
pub type Nonce = u64;
|
||||
pub type Taken = Option<SystemTime>;
|
||||
|
||||
pub const BANDWIDTH_CAP_PER_DAY: u64 = 250 * 1024 * 1024 * 1024; // 250 GB
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct IpPair {
|
||||
pub ipv4: Ipv4Addr,
|
||||
pub ipv6: Ipv6Addr,
|
||||
}
|
||||
|
||||
impl IpPair {
|
||||
pub fn new(ipv4: Ipv4Addr, ipv6: Ipv6Addr) -> Self {
|
||||
IpPair { ipv4, ipv6 }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(Ipv4Addr, Ipv6Addr)> for IpPair {
|
||||
fn from((ipv4, ipv6): (Ipv4Addr, Ipv6Addr)) -> Self {
|
||||
IpPair { ipv4, ipv6 }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for IpPair {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "({}, {})", self.ipv4, self.ipv6)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IpAddr> for IpPair {
|
||||
fn from(value: IpAddr) -> Self {
|
||||
let (before_last_byte, last_byte) = match value {
|
||||
std::net::IpAddr::V4(ipv4_addr) => (ipv4_addr.octets()[2], ipv4_addr.octets()[3]),
|
||||
std::net::IpAddr::V6(ipv6_addr) => (ipv6_addr.octets()[14], ipv6_addr.octets()[15]),
|
||||
};
|
||||
let last_bytes = ((before_last_byte as u16) << 8) | last_byte as u16;
|
||||
let ipv4 = Ipv4Addr::new(
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V4.octets()[0],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V4.octets()[1],
|
||||
before_last_byte,
|
||||
last_byte,
|
||||
);
|
||||
let ipv6 = Ipv6Addr::new(
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[0],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[1],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[2],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[3],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[4],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[5],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[6],
|
||||
last_bytes,
|
||||
);
|
||||
IpPair::new(ipv4, ipv6)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct InitMessage {
|
||||
/// Base64 encoded x25519 public key
|
||||
pub pub_key: PeerPublicKey,
|
||||
}
|
||||
|
||||
impl InitMessage {
|
||||
pub fn new(pub_key: PeerPublicKey) -> Self {
|
||||
InitMessage { pub_key }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct FinalMessage {
|
||||
/// Gateway client data
|
||||
pub gateway_client: GatewayClient,
|
||||
|
||||
/// Ecash credential
|
||||
pub credential: Option<CredentialSpendingData>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct RegistrationData {
|
||||
pub nonce: u64,
|
||||
pub gateway_data: GatewayClient,
|
||||
pub wg_port: u16,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct RegistredData {
|
||||
pub pub_key: PeerPublicKey,
|
||||
pub private_ips: IpPair,
|
||||
pub wg_port: u16,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct RemainingBandwidthData {
|
||||
pub available_bandwidth: i64,
|
||||
}
|
||||
|
||||
/// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret.
|
||||
/// Gateway/Nym node can then verify pub_key payload using the same process
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct GatewayClient {
|
||||
/// Base64 encoded x25519 public key
|
||||
pub pub_key: PeerPublicKey,
|
||||
|
||||
/// Assigned private IPs (v4 and v6)
|
||||
pub private_ips: IpPair,
|
||||
|
||||
/// Sha256 hmac on the data (alongside the prior nonce)
|
||||
pub mac: ClientMac,
|
||||
}
|
||||
|
||||
impl GatewayClient {
|
||||
#[cfg(feature = "verify")]
|
||||
pub fn new(
|
||||
local_secret: &PrivateKey,
|
||||
remote_public: x25519_dalek::PublicKey,
|
||||
private_ips: IpPair,
|
||||
nonce: u64,
|
||||
) -> Self {
|
||||
// convert from 1.0 x25519-dalek private key into 2.0 x25519-dalek
|
||||
#[allow(clippy::expect_used)]
|
||||
let static_secret = x25519_dalek::StaticSecret::from(local_secret.to_bytes());
|
||||
let local_public: x25519_dalek::PublicKey = (&static_secret).into();
|
||||
|
||||
let dh = static_secret.diffie_hellman(&remote_public);
|
||||
|
||||
// TODO: change that to use our nym_crypto::hmac module instead
|
||||
#[allow(clippy::expect_used)]
|
||||
let mut mac = HmacSha256::new_from_slice(dh.as_bytes())
|
||||
.expect("x25519 shared secret is always 32 bytes long");
|
||||
|
||||
mac.update(local_public.as_bytes());
|
||||
mac.update(private_ips.to_string().as_bytes());
|
||||
mac.update(&nonce.to_le_bytes());
|
||||
|
||||
GatewayClient {
|
||||
pub_key: PeerPublicKey::new(local_public),
|
||||
private_ips,
|
||||
mac: ClientMac(mac.finalize().into_bytes().to_vec()),
|
||||
}
|
||||
}
|
||||
|
||||
// Reusable secret should be gateways Wireguard PK
|
||||
// Client should perform this step when generating its payload, using its own WG PK
|
||||
#[cfg(feature = "verify")]
|
||||
pub fn verify(&self, gateway_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
// convert from 1.0 x25519-dalek private key into 2.0 x25519-dalek
|
||||
#[allow(clippy::expect_used)]
|
||||
let static_secret = x25519_dalek::StaticSecret::from(gateway_key.to_bytes());
|
||||
|
||||
let dh = static_secret.diffie_hellman(&self.pub_key);
|
||||
|
||||
// TODO: change that to use our nym_crypto::hmac module instead
|
||||
#[allow(clippy::expect_used)]
|
||||
let mut mac = HmacSha256::new_from_slice(dh.as_bytes())
|
||||
.expect("x25519 shared secret is always 32 bytes long");
|
||||
|
||||
mac.update(self.pub_key.as_bytes());
|
||||
mac.update(self.private_ips.to_string().as_bytes());
|
||||
mac.update(&nonce.to_le_bytes());
|
||||
|
||||
mac.verify_slice(&self.mac)
|
||||
.map_err(|source| Error::FailedClientMacVerification {
|
||||
client: self.pub_key.to_string(),
|
||||
source,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn pub_key(&self) -> PeerPublicKey {
|
||||
self.pub_key
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: change the inner type into generic array of size HmacSha256::OutputSize
|
||||
// TODO2: rely on our internal crypto/hmac
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ClientMac(Vec<u8>);
|
||||
|
||||
impl fmt::Display for ClientMac {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", general_purpose::STANDARD.encode(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientMac {
|
||||
#[allow(dead_code)]
|
||||
pub fn new(mac: Vec<u8>) -> Self {
|
||||
ClientMac(mac)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ClientMac {
|
||||
type Target = Vec<u8>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for ClientMac {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mac_bytes: Vec<u8> =
|
||||
general_purpose::STANDARD
|
||||
.decode(s)
|
||||
.map_err(|source| Error::MalformedClientMac {
|
||||
mac: s.to_string(),
|
||||
source,
|
||||
})?;
|
||||
|
||||
Ok(ClientMac(mac_bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for ClientMac {
|
||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let encoded_key = general_purpose::STANDARD.encode(self.0.clone());
|
||||
serializer.serialize_str(&encoded_key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for ClientMac {
|
||||
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
let encoded_key = String::deserialize(deserializer)?;
|
||||
ClientMac::from_str(&encoded_key).map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use nym_crypto::asymmetric::encryption;
|
||||
|
||||
#[test]
|
||||
fn create_ip_pair() {
|
||||
let ipv4: IpAddr = Ipv4Addr::from_str("10.1.10.50").unwrap().into();
|
||||
let ipv6: IpAddr = Ipv6Addr::from_str("fc01::0a32").unwrap().into();
|
||||
|
||||
assert_eq!(IpPair::from(ipv4), IpPair::from(ipv6));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "verify")]
|
||||
fn client_request_roundtrip() {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let gateway_key_pair = encryption::KeyPair::new(&mut rng);
|
||||
let client_key_pair = encryption::KeyPair::new(&mut rng);
|
||||
|
||||
let nonce = 1234567890;
|
||||
|
||||
let client = GatewayClient::new(
|
||||
client_key_pair.private_key(),
|
||||
x25519_dalek::PublicKey::from(gateway_key_pair.public_key().to_bytes()),
|
||||
IpPair::new("10.0.0.42".parse().unwrap(), "fc00::42".parse().unwrap()),
|
||||
nonce,
|
||||
);
|
||||
assert!(client.verify(gateway_key_pair.private_key(), nonce).is_ok())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::{
|
||||
registration::{FinalMessage, InitMessage},
|
||||
topup::TopUpMessage,
|
||||
};
|
||||
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::make_bincode_serializer;
|
||||
|
||||
use super::VERSION;
|
||||
|
||||
fn generate_random() -> u64 {
|
||||
use rand::RngCore;
|
||||
let mut rng = rand::rngs::OsRng;
|
||||
rng.next_u64()
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct AuthenticatorRequest {
|
||||
pub protocol: Protocol,
|
||||
pub data: AuthenticatorRequestData,
|
||||
pub request_id: u64,
|
||||
}
|
||||
|
||||
impl AuthenticatorRequest {
|
||||
pub fn from_reconstructed_message(
|
||||
message: &nym_sphinx::receiver::ReconstructedMessage,
|
||||
) -> Result<Self, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().deserialize(&message.message)
|
||||
}
|
||||
|
||||
pub fn new_initial_request(init_message: InitMessage) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
version: VERSION,
|
||||
},
|
||||
data: AuthenticatorRequestData::Initial(init_message),
|
||||
request_id,
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_final_request(final_message: FinalMessage) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
version: VERSION,
|
||||
},
|
||||
data: AuthenticatorRequestData::Final(Box::new(final_message)),
|
||||
request_id,
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_query_request(peer_public_key: PeerPublicKey) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
version: VERSION,
|
||||
},
|
||||
data: AuthenticatorRequestData::QueryBandwidth(peer_public_key),
|
||||
request_id,
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_topup_request(top_up_message: TopUpMessage) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
version: VERSION,
|
||||
},
|
||||
data: AuthenticatorRequestData::TopUpBandwidth(Box::new(top_up_message)),
|
||||
request_id,
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().serialize(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub enum AuthenticatorRequestData {
|
||||
Initial(InitMessage),
|
||||
Final(Box<FinalMessage>),
|
||||
QueryBandwidth(PeerPublicKey),
|
||||
TopUpBandwidth(Box<TopUpMessage>),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
fn check_first_bytes_protocol() {
|
||||
let version = 5;
|
||||
let data = AuthenticatorRequest {
|
||||
protocol: Protocol {
|
||||
version,
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
},
|
||||
data: AuthenticatorRequestData::Initial(InitMessage::new(
|
||||
PeerPublicKey::from_str("yvNUDpT5l7W/xDhiu6HkqTHDQwbs/B3J5UrLmORl1EQ=").unwrap(),
|
||||
)),
|
||||
request_id: 1,
|
||||
};
|
||||
let bytes = *data.to_bytes().unwrap().first_chunk::<2>().unwrap();
|
||||
assert_eq!(bytes, [version, ServiceProviderType::Authenticator as u8]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::registration::{RegistrationData, RegistredData, RemainingBandwidthData};
|
||||
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::make_bincode_serializer;
|
||||
|
||||
use super::VERSION;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct AuthenticatorResponse {
|
||||
pub protocol: Protocol,
|
||||
pub data: AuthenticatorResponseData,
|
||||
}
|
||||
|
||||
impl AuthenticatorResponse {
|
||||
pub fn new_pending_registration_success(
|
||||
registration_data: RegistrationData,
|
||||
request_id: u64,
|
||||
) -> Self {
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
version: VERSION,
|
||||
},
|
||||
data: AuthenticatorResponseData::PendingRegistration(PendingRegistrationResponse {
|
||||
reply: registration_data,
|
||||
request_id,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_registered(registred_data: RegistredData, request_id: u64) -> Self {
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
version: VERSION,
|
||||
},
|
||||
data: AuthenticatorResponseData::Registered(RegisteredResponse {
|
||||
reply: registred_data,
|
||||
request_id,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_remaining_bandwidth(
|
||||
remaining_bandwidth_data: Option<RemainingBandwidthData>,
|
||||
request_id: u64,
|
||||
) -> Self {
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
version: VERSION,
|
||||
},
|
||||
data: AuthenticatorResponseData::RemainingBandwidth(RemainingBandwidthResponse {
|
||||
reply: remaining_bandwidth_data,
|
||||
request_id,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_topup_bandwidth(
|
||||
remaining_bandwidth_data: RemainingBandwidthData,
|
||||
request_id: u64,
|
||||
) -> Self {
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
version: VERSION,
|
||||
},
|
||||
data: AuthenticatorResponseData::TopUpBandwidth(TopUpBandwidthResponse {
|
||||
reply: remaining_bandwidth_data,
|
||||
request_id,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().serialize(self)
|
||||
}
|
||||
|
||||
pub fn from_reconstructed_message(
|
||||
message: &nym_sphinx::receiver::ReconstructedMessage,
|
||||
) -> Result<Self, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().deserialize(&message.message)
|
||||
}
|
||||
|
||||
pub fn id(&self) -> Option<u64> {
|
||||
match &self.data {
|
||||
AuthenticatorResponseData::PendingRegistration(response) => Some(response.request_id),
|
||||
AuthenticatorResponseData::Registered(response) => Some(response.request_id),
|
||||
AuthenticatorResponseData::RemainingBandwidth(response) => Some(response.request_id),
|
||||
AuthenticatorResponseData::TopUpBandwidth(response) => Some(response.request_id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub enum AuthenticatorResponseData {
|
||||
PendingRegistration(PendingRegistrationResponse),
|
||||
Registered(RegisteredResponse),
|
||||
RemainingBandwidth(RemainingBandwidthResponse),
|
||||
TopUpBandwidth(TopUpBandwidthResponse),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct PendingRegistrationResponse {
|
||||
pub request_id: u64,
|
||||
pub reply: RegistrationData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct RegisteredResponse {
|
||||
pub request_id: u64,
|
||||
pub reply: RegistredData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct RemainingBandwidthResponse {
|
||||
pub request_id: u64,
|
||||
pub reply: Option<RemainingBandwidthData>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct TopUpBandwidthResponse {
|
||||
pub request_id: u64,
|
||||
pub reply: RemainingBandwidthData,
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct TopUpMessage {
|
||||
/// Base64 encoded x25519 public key
|
||||
pub pub_key: PeerPublicKey,
|
||||
|
||||
/// Ecash credential
|
||||
pub credential: CredentialSpendingData,
|
||||
}
|
||||
@@ -50,7 +50,7 @@ const DEFAULT_MINIMUM_REPLY_SURB_THRESHOLD_BUFFER: usize = 0;
|
||||
// define how much to request at once
|
||||
// clients/client-core/src/client/replies/reply_controller.rs
|
||||
const DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 10;
|
||||
const DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 100;
|
||||
const DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 50;
|
||||
|
||||
const DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE: u32 = 500;
|
||||
|
||||
@@ -658,6 +658,9 @@ pub struct ReplySurbs {
|
||||
/// Specifies the number of mixnet hops the packet should go through. If not specified, then
|
||||
/// the default value is used.
|
||||
pub surb_mix_hops: Option<u8>,
|
||||
|
||||
/// Specifies if we should reset all the sender tags on startup
|
||||
pub fresh_sender_tags: bool,
|
||||
}
|
||||
|
||||
impl Default for ReplySurbs {
|
||||
@@ -675,6 +678,7 @@ impl Default for ReplySurbs {
|
||||
maximum_reply_surb_age: DEFAULT_MAXIMUM_REPLY_SURB_AGE,
|
||||
maximum_reply_key_age: DEFAULT_MAXIMUM_REPLY_KEY_AGE,
|
||||
surb_mix_hops: None,
|
||||
fresh_sender_tags: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ impl StorageManager {
|
||||
sqlx::query!("SELECT EXISTS (SELECT 1 FROM registered_gateway WHERE gateway_id_bs58 = ?) AS 'exists'", gateway_id)
|
||||
.fetch_one(&self.connection_pool)
|
||||
.await
|
||||
.map(|result| result.exists == Some(1))
|
||||
.map(|result| result.exists == 1)
|
||||
}
|
||||
|
||||
pub(crate) async fn maybe_get_registered_gateway(
|
||||
|
||||
@@ -88,7 +88,7 @@ pub async fn setup_fs_reply_surb_backend<P: AsRef<Path>>(
|
||||
let db_path = db_path.as_ref();
|
||||
if db_path.exists() {
|
||||
info!("loading existing surb database");
|
||||
match fs_backend::Backend::try_load(db_path).await {
|
||||
match fs_backend::Backend::try_load(db_path, surb_config.fresh_sender_tags).await {
|
||||
Ok(backend) => Ok(backend),
|
||||
Err(err) => {
|
||||
error!("failed to setup persistent storage backend for our reply needs: {err}. We're going to create a fresh database instead. This behaviour might change in the future");
|
||||
|
||||
@@ -33,10 +33,12 @@ pub enum PreparationError {
|
||||
#[error(transparent)]
|
||||
NymTopologyError(#[from] NymTopologyError),
|
||||
|
||||
#[error("The received message cannot be sent using a single reply surb. It ended up getting split into {fragments} fragments.")]
|
||||
#[error("message too long for a single SURB, splitting into {fragments} fragments.")]
|
||||
MessageTooLongForSingleSurb { fragments: usize },
|
||||
|
||||
#[error("Not enough reply SURBs to send the message. We have {available} available and require at least {required}.")]
|
||||
#[error(
|
||||
"not enough reply SURBs to send the message, available: {available} required: {required}."
|
||||
)]
|
||||
NotEnoughSurbs { available: usize, required: usize },
|
||||
}
|
||||
|
||||
|
||||
@@ -746,7 +746,7 @@ where
|
||||
.request_additional_reply_surbs(target, request_size)
|
||||
.await
|
||||
{
|
||||
warn!("failed to request additional surbs... - {err}")
|
||||
info!("{err}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::{
|
||||
CombinedReplyStorage, ReceivedReplySurbsMap, ReplyStorageBackend, SentReplyKeys, UsedSenderTags,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use log::{error, info, warn};
|
||||
use log::{debug, error, info, warn};
|
||||
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
@@ -52,7 +52,10 @@ impl Backend {
|
||||
Ok(backend)
|
||||
}
|
||||
|
||||
pub async fn try_load<P: AsRef<Path>>(database_path: P) -> Result<Self, StorageError> {
|
||||
pub async fn try_load<P: AsRef<Path>>(
|
||||
database_path: P,
|
||||
fresh_sender_tags: bool,
|
||||
) -> Result<Self, StorageError> {
|
||||
let owned_path: PathBuf = database_path.as_ref().into();
|
||||
if owned_path.file_name().is_none() {
|
||||
return Err(StorageError::DatabasePathWithoutFilename {
|
||||
@@ -118,6 +121,9 @@ impl Backend {
|
||||
if days > 2 {
|
||||
info!("it's been over {days} days and {hours} hours since we last used our data store. our used sender tags are already outdated - we're going to purge them now.");
|
||||
manager.delete_all_tags().await?;
|
||||
} else if fresh_sender_tags {
|
||||
debug!("starting with fresh sender tags");
|
||||
manager.delete_all_tags().await?;
|
||||
}
|
||||
|
||||
Ok(Backend {
|
||||
|
||||
@@ -83,6 +83,12 @@ impl TryFrom<ContractVKShare> for EcashApiClient {
|
||||
|
||||
let url_address = Url::parse(&share.announce_address)?;
|
||||
|
||||
// The NymApiClient constructed here uses the default (hickory DoT/DoH) resolver because
|
||||
// this EcashApiClient is used by both client and non-client applications.
|
||||
//
|
||||
// In non-client applications this resolver can cause warning logs about H2 connection
|
||||
// failure. This indicates that the long lived https connection was closed by the remote
|
||||
// peer and the resolver will have to reconnect. It should not impact actual functionality
|
||||
Ok(EcashApiClient {
|
||||
api_client: NymApiClient::new(url_address),
|
||||
verification_key: VerificationKeyAuth::try_from_bs58(&share.share)?,
|
||||
|
||||
@@ -12,8 +12,9 @@ use nym_api_requests::ecash::models::{
|
||||
};
|
||||
use nym_api_requests::ecash::VerificationKeyResponse;
|
||||
use nym_api_requests::models::{
|
||||
AnnotationResponse, ApiHealthResponse, LegacyDescribedMixNode, NodePerformanceResponse,
|
||||
NodeRefreshBody, NymNodeDescription, PerformanceHistoryResponse, RewardedSetResponse,
|
||||
AnnotationResponse, ApiHealthResponse, BinaryBuildInformationOwned, ChainStatusResponse,
|
||||
LegacyDescribedMixNode, NodePerformanceResponse, NodeRefreshBody, NymNodeDescription,
|
||||
PerformanceHistoryResponse, RewardedSetResponse,
|
||||
};
|
||||
use nym_api_requests::nym_nodes::{
|
||||
NodesByAddressesRequestBody, NodesByAddressesResponse, PaginatedCachedNodesResponse,
|
||||
@@ -69,6 +70,19 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn build_information(&self) -> Result<BinaryBuildInformationOwned, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::API_STATUS_ROUTES,
|
||||
routes::BUILD_INFORMATION,
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[deprecated]
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
|
||||
@@ -1043,6 +1057,15 @@ pub trait NymApiClientExt: ApiClient {
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_chain_status(&self) -> Result<ChainStatusResponse, NymAPIError> {
|
||||
self.get_json(
|
||||
&[routes::API_VERSION, routes::NETWORK, routes::CHAIN_STATUS],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
|
||||
@@ -49,6 +49,8 @@ pub mod nym_nodes {
|
||||
pub const STATUS_ROUTES: &str = "status";
|
||||
pub const API_STATUS_ROUTES: &str = "api-status";
|
||||
pub const HEALTH: &str = "health";
|
||||
pub const BUILD_INFORMATION: &str = "build-information";
|
||||
|
||||
pub const MIXNODE: &str = "mixnode";
|
||||
pub const GATEWAY: &str = "gateway";
|
||||
pub const NYM_NODES: &str = "nym-nodes";
|
||||
@@ -70,4 +72,5 @@ pub const SUBMIT_NODE: &str = "submit-node-monitoring-results";
|
||||
pub const SERVICE_PROVIDERS: &str = "services";
|
||||
|
||||
pub const DETAILS: &str = "details";
|
||||
pub const CHAIN_STATUS: &str = "chain-status";
|
||||
pub const NETWORK: &str = "network";
|
||||
|
||||
@@ -62,6 +62,7 @@ pub use cw3;
|
||||
pub use cw4;
|
||||
pub use cw_controllers;
|
||||
pub use fee::{gas_price::GasPrice, GasAdjustable, GasAdjustment};
|
||||
pub use prost::Name;
|
||||
pub use tendermint_rpc::endpoint::block::Response as BlockResponse;
|
||||
pub use tendermint_rpc::{
|
||||
endpoint::{tx::Response as TxResponse, validators::Response as ValidatorResponse},
|
||||
|
||||
@@ -21,7 +21,7 @@ lazy_static = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
rand_chacha = { workspace = true }
|
||||
rand_core = { workspace = true }
|
||||
sha2 = "0.9"
|
||||
sha2 = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_derive = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
@@ -54,12 +54,12 @@ pub(crate) fn hash_to_scalar<M: AsRef<[u8]>>(msg: M, domain: &[u8]) -> Scalar {
|
||||
pub(crate) fn hash_to_scalars<M: AsRef<[u8]>>(msg: M, domain: &[u8], n: usize) -> Vec<Scalar> {
|
||||
let mut output = vec![Scalar::zero(); n];
|
||||
|
||||
Scalar::hash_to_field::<ExpandMsgXmd<Sha256>>(msg.as_ref(), domain, &mut output);
|
||||
Scalar::hash_to_field::<ExpandMsgXmd<Sha256>, _>([msg], domain, &mut output);
|
||||
output
|
||||
}
|
||||
|
||||
pub(crate) fn hash_g2<M: AsRef<[u8]>>(msg: M, domain: &[u8]) -> G2Projective {
|
||||
<G2Projective as HashToCurve<ExpandMsgXmd<Sha256>>>::hash_to_curve(msg, domain)
|
||||
<G2Projective as HashToCurve<ExpandMsgXmd<Sha256>>>::hash_to_curve([msg], domain)
|
||||
}
|
||||
|
||||
pub(crate) fn combine_scalar_chunks(chunks: &[Scalar]) -> Scalar {
|
||||
@@ -112,3 +112,97 @@ pub(crate) fn deserialize_g2(b: &[u8]) -> Option<G2Projective> {
|
||||
G2Projective::from_bytes(&encoding).into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use bls12_381::G2Affine;
|
||||
|
||||
#[test]
|
||||
fn test_hash_to_scalar() {
|
||||
let msg1 = "foo";
|
||||
let expected1 = Scalar::from_bytes(&[
|
||||
253, 57, 224, 227, 175, 195, 226, 82, 46, 175, 33, 126, 171, 239, 255, 92, 108, 168, 6,
|
||||
79, 90, 11, 235, 236, 221, 10, 85, 133, 42, 81, 95, 30,
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
let msg2 = "bar";
|
||||
let expected2 = Scalar::from_bytes(&[
|
||||
48, 83, 69, 52, 42, 18, 135, 244, 211, 190, 160, 196, 118, 154, 24, 126, 0, 125, 72,
|
||||
201, 170, 225, 123, 201, 52, 120, 171, 132, 235, 182, 20, 26,
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
let msg3 = [
|
||||
33, 135, 76, 234, 71, 35, 247, 216, 39, 242, 42, 88, 152, 29, 74, 135, 9, 29, 216, 123,
|
||||
250, 87, 108, 29, 245, 126, 109, 102, 84, 71, 158, 224, 145, 243, 49, 121, 244, 27,
|
||||
115, 121, 25, 66, 216, 67, 97, 101, 140, 160, 77, 239, 114, 215, 152, 48, 15, 231, 101,
|
||||
60, 42, 92, 128, 131, 161, 43,
|
||||
];
|
||||
let expected3 = Scalar::from_bytes(&[
|
||||
128, 189, 8, 43, 186, 55, 52, 61, 171, 196, 159, 177, 162, 100, 27, 143, 85, 83, 218,
|
||||
171, 91, 220, 155, 25, 7, 38, 2, 36, 4, 93, 136, 4,
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
hash_to_scalar(msg1, b"NYMECASH-V01-CS02-with-expander-SHA256"),
|
||||
expected1
|
||||
);
|
||||
assert_eq!(
|
||||
hash_to_scalar(msg2, b"NYMECASH-V01-CS02-with-expander-SHA256"),
|
||||
expected2
|
||||
);
|
||||
assert_eq!(
|
||||
hash_to_scalar(msg3, b"NYMECASH-V01-CS02-with-expander-SHA256"),
|
||||
expected3
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hash_g2() {
|
||||
let msg1 = "foo";
|
||||
let expected1 = G2Affine::from_compressed(&[
|
||||
175, 187, 62, 7, 29, 17, 42, 93, 28, 93, 234, 253, 101, 166, 158, 187, 153, 82, 93, 18,
|
||||
11, 233, 36, 107, 51, 117, 30, 127, 32, 254, 210, 77, 133, 12, 253, 255, 84, 128, 36,
|
||||
214, 234, 103, 50, 21, 26, 78, 112, 49, 20, 69, 19, 109, 7, 78, 33, 227, 196, 180, 168,
|
||||
219, 73, 251, 192, 221, 41, 138, 160, 131, 191, 186, 156, 117, 179, 179, 191, 235, 171,
|
||||
26, 219, 148, 170, 179, 11, 38, 137, 14, 95, 115, 171, 186, 163, 82, 158, 6, 239, 88,
|
||||
])
|
||||
.unwrap()
|
||||
.into();
|
||||
|
||||
let msg2 = "bar";
|
||||
let expected2 = G2Affine::from_compressed(&[
|
||||
183, 25, 90, 187, 34, 184, 30, 182, 215, 242, 158, 83, 116, 34, 210, 96, 188, 79, 83,
|
||||
255, 100, 122, 90, 188, 196, 93, 164, 253, 20, 106, 205, 33, 48, 140, 60, 149, 66, 246,
|
||||
121, 244, 146, 66, 170, 60, 113, 95, 102, 237, 25, 231, 8, 42, 121, 124, 180, 140, 34,
|
||||
104, 173, 251, 89, 189, 28, 196, 49, 66, 101, 38, 68, 44, 40, 235, 21, 35, 204, 123,
|
||||
218, 238, 216, 92, 134, 217, 212, 246, 176, 77, 187, 0, 245, 134, 132, 73, 31, 44, 137,
|
||||
197,
|
||||
])
|
||||
.unwrap()
|
||||
.into();
|
||||
let msg3 = [
|
||||
33, 135, 76, 234, 71, 35, 247, 216, 39, 242, 42, 88, 152, 29, 74, 135, 9, 29, 216, 123,
|
||||
250, 87, 108, 29, 245, 126, 109, 102, 84, 71, 158, 224, 145, 243, 49, 121, 244, 27,
|
||||
115, 121, 25, 66, 216, 67, 97, 101, 140, 160, 77, 239, 114, 215, 152, 48, 15, 231, 101,
|
||||
60, 42, 92, 128, 131, 161, 43,
|
||||
];
|
||||
let expected3 = G2Affine::from_compressed(&[
|
||||
151, 185, 8, 123, 223, 150, 192, 192, 115, 10, 3, 129, 49, 179, 31, 108, 0, 17, 46,
|
||||
231, 184, 164, 247, 228, 22, 142, 87, 70, 120, 111, 154, 15, 245, 110, 32, 84, 53, 117,
|
||||
239, 93, 89, 119, 32, 17, 39, 250, 198, 137, 6, 95, 137, 202, 54, 244, 238, 190, 11,
|
||||
217, 237, 95, 72, 59, 140, 56, 3, 42, 61, 195, 192, 101, 46, 204, 207, 75, 70, 176,
|
||||
207, 48, 24, 195, 248, 234, 178, 168, 54, 109, 19, 189, 51, 52, 120, 69, 248, 226, 102,
|
||||
91,
|
||||
])
|
||||
.unwrap()
|
||||
.into();
|
||||
|
||||
assert_eq!(hash_g2(msg1, b"DUMMY_TEST_DOMAIN"), expected1);
|
||||
assert_eq!(hash_g2(msg2, b"DUMMY_TEST_DOMAIN"), expected2);
|
||||
assert_eq!(hash_g2(msg3, b"DUMMY_TEST_DOMAIN"), expected3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use nym_sphinx::params::packet_sizes::PacketSize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::string::FromUtf8Error;
|
||||
use thiserror::Error;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
// specific errors (that should not be nested!!) for clients to match on
|
||||
#[derive(Debug, Copy, Clone, Error, Serialize, Deserialize)]
|
||||
@@ -112,15 +113,15 @@ pub enum AuthenticationFailure {
|
||||
#[error("failed to verify request signature")]
|
||||
InvalidSignature(#[from] SignatureError),
|
||||
|
||||
#[error("provided request timestamp is in the future")]
|
||||
RequestTimestampInFuture,
|
||||
|
||||
#[error("the client is not registered")]
|
||||
NotRegistered,
|
||||
|
||||
#[error("the provided request is too stale to process")]
|
||||
StaleRequest,
|
||||
#[error("the provided request timestamp is excessively skewed. got {received} whilst the server time is {server}")]
|
||||
ExcessiveTimestampSkew {
|
||||
received: OffsetDateTime,
|
||||
server: OffsetDateTime,
|
||||
},
|
||||
|
||||
#[error("the provided request timestamp is smaller or equal to a one previously used")]
|
||||
#[error("the provided request timestamp is smaller or equal to one previously used")]
|
||||
RequestReuse,
|
||||
}
|
||||
|
||||
@@ -38,13 +38,22 @@ impl AuthenticateRequest {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn verify_timestamp(&self, max_request_age: Duration) -> Result<(), AuthenticationFailure> {
|
||||
pub fn verify_timestamp(
|
||||
&self,
|
||||
max_request_timestamp_skew: Duration,
|
||||
) -> Result<(), AuthenticationFailure> {
|
||||
let now = OffsetDateTime::now_utc();
|
||||
if self.content.request_timestamp() + max_request_age < now {
|
||||
return Err(AuthenticationFailure::StaleRequest);
|
||||
if self.content.request_timestamp() < now - max_request_timestamp_skew {
|
||||
return Err(AuthenticationFailure::ExcessiveTimestampSkew {
|
||||
received: self.content.request_timestamp(),
|
||||
server: now,
|
||||
});
|
||||
}
|
||||
if self.content.request_timestamp() > now {
|
||||
return Err(AuthenticationFailure::RequestTimestampInFuture);
|
||||
if self.content.request_timestamp() - max_request_timestamp_skew > now {
|
||||
return Err(AuthenticationFailure::ExcessiveTimestampSkew {
|
||||
received: self.content.request_timestamp(),
|
||||
server: now,
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ impl PersistentStatsStorage {
|
||||
pub async fn get_started_sessions_count(
|
||||
&self,
|
||||
start_date: Date,
|
||||
) -> Result<i32, StatsStorageError> {
|
||||
) -> Result<i64, StatsStorageError> {
|
||||
Ok(self
|
||||
.session_manager
|
||||
.get_started_sessions_count(start_date)
|
||||
|
||||
@@ -148,7 +148,7 @@ impl SessionManager {
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn get_started_sessions_count(&self, start_date: Date) -> Result<i32> {
|
||||
pub(crate) async fn get_started_sessions_count(&self, start_date: Date) -> Result<i64> {
|
||||
Ok(sqlx::query!(
|
||||
"SELECT COUNT(*) as count FROM sessions_active WHERE date(start_time) = ?",
|
||||
start_date
|
||||
|
||||
@@ -17,7 +17,7 @@ sqlx = { workspace = true, features = [
|
||||
"macros",
|
||||
"migrate",
|
||||
"time",
|
||||
"chrono"
|
||||
# "chrono"
|
||||
] }
|
||||
time = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
@@ -92,7 +92,7 @@ impl TicketStorageManager {
|
||||
)
|
||||
.fetch_one(&self.connection_pool)
|
||||
.await
|
||||
.map(|result| result.exists == Some(1))
|
||||
.map(|result| result.exists == 1)
|
||||
}
|
||||
|
||||
pub(crate) async fn remove_binary_ticket_data(
|
||||
|
||||
@@ -21,6 +21,12 @@ serde_json = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
|
||||
# used for decoding text responses (they were already implicitly included)
|
||||
bytes = { workspace = true }
|
||||
encoding_rs = { workspace = true }
|
||||
mime = { workspace = true }
|
||||
|
||||
|
||||
nym-bin-common = { path = "../bin-common" }
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies]
|
||||
@@ -32,4 +38,4 @@ workspace = true
|
||||
features = ["tokio"]
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { workspace = true, features=["rt", "macros"] }
|
||||
tokio = { workspace = true, features = ["rt", "macros"] }
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//! DNS resolver configuration for internal lookups.
|
||||
//!
|
||||
//! The resolver itself is the set combination of the google, cloudflare, and quad9 endpoints
|
||||
@@ -9,6 +12,19 @@
|
||||
//!
|
||||
//! Requires the `dns-over-https-rustls`, `webpki-roots` feature for the
|
||||
//! `hickory-resolver` crate
|
||||
//!
|
||||
//!
|
||||
//! Note: The hickory DoH resolver can cause warning logs about H2 connection failure. This
|
||||
//! indicates that the long lived https connection was closed by the remote peer and the resolver
|
||||
//! will have to reconnect. It should not impact actual functionality.
|
||||
//!
|
||||
//! code ref: https://github.com/hickory-dns/hickory-dns/blob/06a8b1ce9bd9322d8e6accf857d30257e1274427/crates/proto/src/h2/h2_client_stream.rs#L534
|
||||
//!
|
||||
//! example log:
|
||||
//!
|
||||
//! ```txt
|
||||
//! WARN /home/ubuntu/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hickory-proto-0.24.3/src/h2/h2_client_stream.rs:493: h2 connection failed: unexpected end of file
|
||||
//! ```
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use crate::ClientBuilder;
|
||||
@@ -33,6 +49,13 @@ impl ClientBuilder {
|
||||
/// Override the DNS resolver implementation used by the underlying http client.
|
||||
pub fn dns_resolver<R: Resolve + 'static>(mut self, resolver: Arc<R>) -> Self {
|
||||
self.reqwest_client_builder = self.reqwest_client_builder.dns_resolver(resolver);
|
||||
self.use_secure_dns = false;
|
||||
self
|
||||
}
|
||||
|
||||
/// Override the DNS resolver implementation used by the underlying http client.
|
||||
pub fn no_hickory_dns(mut self) -> Self {
|
||||
self.use_secure_dns = false;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,13 +147,13 @@ use thiserror::Error;
|
||||
use tracing::{instrument, warn};
|
||||
use url::Url;
|
||||
|
||||
use http::HeaderMap;
|
||||
pub use reqwest::IntoUrl;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use std::net::SocketAddr;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use std::sync::Arc;
|
||||
|
||||
pub use reqwest::IntoUrl;
|
||||
|
||||
mod user_agent;
|
||||
pub use user_agent::UserAgent;
|
||||
|
||||
@@ -210,6 +210,12 @@ pub enum HttpClientError<E: Display = String> {
|
||||
#[error("failed to resolve request. status: '{status}', additional error message: {error}")]
|
||||
EndpointFailure { status: StatusCode, error: E },
|
||||
|
||||
#[error("failed to decode response body: {source} from {content}")]
|
||||
ResponseDecodeFailure {
|
||||
source: serde_json::Error,
|
||||
content: String,
|
||||
},
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[error("the request has timed out")]
|
||||
RequestTimeout,
|
||||
@@ -222,6 +228,8 @@ pub struct ClientBuilder {
|
||||
timeout: Option<Duration>,
|
||||
custom_user_agent: bool,
|
||||
reqwest_client_builder: reqwest::ClientBuilder,
|
||||
#[allow(dead_code)] // not dead code, just unused in wasm
|
||||
use_secure_dns: bool,
|
||||
}
|
||||
|
||||
impl ClientBuilder {
|
||||
@@ -233,37 +241,46 @@ impl ClientBuilder {
|
||||
U: IntoUrl,
|
||||
E: Display,
|
||||
{
|
||||
// a naive check: if the provided URL does not start with http(s), add that scheme
|
||||
let str_url = url.as_str();
|
||||
|
||||
// a naive check: if the provided URL does not start with http(s), add that scheme
|
||||
if !str_url.starts_with("http") {
|
||||
let alt = format!("http://{str_url}");
|
||||
warn!("the provided url ('{str_url}') does not contain scheme information. Changing it to '{alt}' ...");
|
||||
// TODO: or should we maybe default to https?
|
||||
Self::new(alt)
|
||||
} else {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let reqwest_client_builder = reqwest::ClientBuilder::new();
|
||||
Ok(Self::new_with_url(url.into_url()?))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let reqwest_client_builder = {
|
||||
let r = reqwest::ClientBuilder::new()
|
||||
.dns_resolver(Arc::new(HickoryDnsResolver::default()));
|
||||
/// Constructs a new http `ClientBuilder` from a valid url.
|
||||
pub fn new_with_url(url: Url) -> Self {
|
||||
if !url.scheme().starts_with("http") {
|
||||
warn!("the provided url ('{url}') does not use HTTP / HTTPS scheme");
|
||||
}
|
||||
|
||||
// Note this is extra as the `gzip` feature for `reqwest` crate should be enabled which
|
||||
// `"Enable[s] auto gzip decompression by checking the Content-Encoding response header."`
|
||||
//
|
||||
// I am going to leave it here anyways so that gzip decompression is attempted even if
|
||||
// that feature is removed.
|
||||
r.gzip(true)
|
||||
};
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let reqwest_client_builder = reqwest::ClientBuilder::new();
|
||||
|
||||
Ok(ClientBuilder {
|
||||
url: url.into_url()?,
|
||||
timeout: None,
|
||||
custom_user_agent: false,
|
||||
reqwest_client_builder,
|
||||
})
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let reqwest_client_builder = {
|
||||
let r = reqwest::ClientBuilder::new();
|
||||
|
||||
// Note this is extra as the `gzip` feature for `reqwest` crate should be enabled which
|
||||
// `"Enable[s] auto gzip decompression by checking the Content-Encoding response header."`
|
||||
//
|
||||
// I am going to leave it here anyways so that gzip decompression is attempted even if
|
||||
// that feature is removed.
|
||||
r.gzip(true)
|
||||
};
|
||||
|
||||
ClientBuilder {
|
||||
url,
|
||||
timeout: None,
|
||||
custom_user_agent: false,
|
||||
reqwest_client_builder,
|
||||
use_secure_dns: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,10 +336,18 @@ impl ClientBuilder {
|
||||
let mut builder = self
|
||||
.reqwest_client_builder
|
||||
.timeout(self.timeout.unwrap_or(DEFAULT_TIMEOUT));
|
||||
|
||||
// if no custom user agent was set, use a default
|
||||
if !self.custom_user_agent {
|
||||
builder =
|
||||
builder.user_agent(format!("nym-http-api-client/{}", env!("CARGO_PKG_VERSION")))
|
||||
}
|
||||
|
||||
// unless explicitly disabled use the DoT/DoH enabled resolver
|
||||
if self.use_secure_dns {
|
||||
builder = builder.dns_resolver(Arc::new(HickoryDnsResolver::default()));
|
||||
}
|
||||
|
||||
builder.build()?
|
||||
};
|
||||
|
||||
@@ -349,6 +374,9 @@ pub struct Client {
|
||||
impl Client {
|
||||
/// Create a new http `Client`
|
||||
// no timeout until https://github.com/seanmonstar/reqwest/issues/1135 is fixed
|
||||
//
|
||||
// In order to prevent interference in API requests at the DNS phase we default to a resolver
|
||||
// that uses DoT and DoH.
|
||||
pub fn new(base_url: Url, timeout: Option<Duration>) -> Self {
|
||||
Self::new_url::<_, String>(base_url, timeout).expect(
|
||||
"we provided valid url and we were unwrapping previous construction errors anyway",
|
||||
@@ -849,6 +877,26 @@ fn sanitize_url<K: AsRef<str>, V: AsRef<str>>(
|
||||
url
|
||||
}
|
||||
|
||||
fn decode_as_text(bytes: &bytes::Bytes, headers: HeaderMap) -> String {
|
||||
use encoding_rs::{Encoding, UTF_8};
|
||||
use mime::Mime;
|
||||
|
||||
let content_type = headers
|
||||
.get(http::header::CONTENT_TYPE)
|
||||
.and_then(|value| value.to_str().ok())
|
||||
.and_then(|value| value.parse::<Mime>().ok());
|
||||
|
||||
let encoding_name = content_type
|
||||
.as_ref()
|
||||
.and_then(|mime| mime.get_param("charset").map(|charset| charset.as_str()))
|
||||
.unwrap_or("utf-8");
|
||||
|
||||
let encoding = Encoding::for_label(encoding_name.as_bytes()).unwrap_or(UTF_8);
|
||||
|
||||
let (text, _, _) = encoding.decode(bytes);
|
||||
text.into_owned()
|
||||
}
|
||||
|
||||
/// Attempt to parse a json object from an HTTP response
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
pub async fn parse_response<T, E>(res: Response, allow_empty: bool) -> Result<T, HttpClientError<E>>
|
||||
@@ -864,21 +912,23 @@ where
|
||||
return Err(HttpClientError::EmptyResponse { status });
|
||||
}
|
||||
}
|
||||
let headers = res.headers().clone();
|
||||
tracing::trace!("headers: {:?}", headers);
|
||||
|
||||
if res.status().is_success() {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
let text = res.text().await.inspect_err(|err| {
|
||||
tracing::error!("Couldn't even get response text: {err}");
|
||||
})?;
|
||||
tracing::trace!("Result:\n{:#?}", text);
|
||||
|
||||
serde_json::from_str(&text)
|
||||
.map_err(|err| HttpClientError::GenericRequestFailure(err.to_string()))
|
||||
// internally reqwest is first retrieving bytes and then performing parsing via serde_json
|
||||
// (and similarly does the same thing for text())
|
||||
let full = res.bytes().await?;
|
||||
match serde_json::from_slice(&full) {
|
||||
Ok(data) => Ok(data),
|
||||
Err(err) => {
|
||||
let content = decode_as_text(&full, headers);
|
||||
Err(HttpClientError::ResponseDecodeFailure {
|
||||
source: err,
|
||||
content,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
Ok(res.json().await?)
|
||||
} else if res.status() == StatusCode::NOT_FOUND {
|
||||
Err(HttpClientError::NotFound)
|
||||
} else {
|
||||
|
||||
@@ -15,10 +15,10 @@ bls12_381 = { workspace = true, features = ["alloc", "pairings", "experimental",
|
||||
bincode.workspace = true
|
||||
cfg-if.workspace = true
|
||||
itertools = { workspace = true }
|
||||
digest = "0.9"
|
||||
digest = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
sha2 = "0.9"
|
||||
sha2 = { workspace = true }
|
||||
bs58 = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
rayon = { workspace = true, optional = true }
|
||||
|
||||
@@ -113,17 +113,13 @@ const G1_HASH_DOMAIN: &[u8] = b"NYMECASH-V01-CS02-with-BLS12381G1_XMD:SHA-256_SS
|
||||
const SCALAR_HASH_DOMAIN: &[u8] = b"NYMECASH-V01-CS02-with-expander-SHA256";
|
||||
|
||||
pub fn hash_g1<M: AsRef<[u8]>>(msg: M) -> G1Projective {
|
||||
<G1Projective as HashToCurve<ExpandMsgXmd<sha2::Sha256>>>::hash_to_curve(msg, G1_HASH_DOMAIN)
|
||||
<G1Projective as HashToCurve<ExpandMsgXmd<sha2::Sha256>>>::hash_to_curve([msg], G1_HASH_DOMAIN)
|
||||
}
|
||||
|
||||
pub fn hash_to_scalar<M: AsRef<[u8]>>(msg: M) -> Scalar {
|
||||
let mut output = vec![Scalar::zero()];
|
||||
|
||||
Scalar::hash_to_field::<ExpandMsgXmd<sha2::Sha256>>(
|
||||
msg.as_ref(),
|
||||
SCALAR_HASH_DOMAIN,
|
||||
&mut output,
|
||||
);
|
||||
Scalar::hash_to_field::<ExpandMsgXmd<sha2::Sha256>, _>([msg], SCALAR_HASH_DOMAIN, &mut output);
|
||||
output[0]
|
||||
}
|
||||
|
||||
@@ -401,4 +397,75 @@ mod tests {
|
||||
assert_eq!(hash_to_scalar(msg2), hash_to_scalar(msg2));
|
||||
assert_ne!(hash_to_scalar(msg1), hash_to_scalar(msg2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hash_to_scalar() {
|
||||
let msg1 = "foo";
|
||||
let expected1 = Scalar::from_bytes(&[
|
||||
253, 57, 224, 227, 175, 195, 226, 82, 46, 175, 33, 126, 171, 239, 255, 92, 108, 168, 6,
|
||||
79, 90, 11, 235, 236, 221, 10, 85, 133, 42, 81, 95, 30,
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
let msg2 = "bar";
|
||||
let expected2 = Scalar::from_bytes(&[
|
||||
48, 83, 69, 52, 42, 18, 135, 244, 211, 190, 160, 196, 118, 154, 24, 126, 0, 125, 72,
|
||||
201, 170, 225, 123, 201, 52, 120, 171, 132, 235, 182, 20, 26,
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
let msg3 = [
|
||||
33, 135, 76, 234, 71, 35, 247, 216, 39, 242, 42, 88, 152, 29, 74, 135, 9, 29, 216, 123,
|
||||
250, 87, 108, 29, 245, 126, 109, 102, 84, 71, 158, 224, 145, 243, 49, 121, 244, 27,
|
||||
115, 121, 25, 66, 216, 67, 97, 101, 140, 160, 77, 239, 114, 215, 152, 48, 15, 231, 101,
|
||||
60, 42, 92, 128, 131, 161, 43,
|
||||
];
|
||||
let expected3 = Scalar::from_bytes(&[
|
||||
128, 189, 8, 43, 186, 55, 52, 61, 171, 196, 159, 177, 162, 100, 27, 143, 85, 83, 218,
|
||||
171, 91, 220, 155, 25, 7, 38, 2, 36, 4, 93, 136, 4,
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(hash_to_scalar(msg1), expected1);
|
||||
assert_eq!(hash_to_scalar(msg2), expected2);
|
||||
assert_eq!(hash_to_scalar(msg3), expected3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hash_to_g1() {
|
||||
let msg1 = "foo";
|
||||
let expected1 = G1Affine::from_compressed(&[
|
||||
161, 109, 186, 0, 192, 221, 83, 87, 71, 31, 120, 201, 185, 35, 62, 239, 46, 120, 117,
|
||||
150, 191, 227, 128, 161, 78, 201, 207, 167, 86, 181, 229, 115, 2, 6, 178, 16, 251, 118,
|
||||
219, 115, 184, 96, 2, 10, 31, 63, 150, 70,
|
||||
])
|
||||
.unwrap()
|
||||
.into();
|
||||
|
||||
let msg2 = "bar";
|
||||
let expected2 = G1Affine::from_compressed(&[
|
||||
135, 102, 204, 42, 221, 49, 209, 192, 250, 87, 59, 255, 197, 93, 37, 113, 38, 2, 154,
|
||||
233, 68, 234, 206, 182, 121, 212, 166, 210, 74, 155, 190, 33, 203, 237, 176, 60, 249,
|
||||
241, 53, 170, 18, 168, 49, 35, 1, 151, 205, 174,
|
||||
])
|
||||
.unwrap()
|
||||
.into();
|
||||
let msg3 = [
|
||||
33, 135, 76, 234, 71, 35, 247, 216, 39, 242, 42, 88, 152, 29, 74, 135, 9, 29, 216, 123,
|
||||
250, 87, 108, 29, 245, 126, 109, 102, 84, 71, 158, 224, 145, 243, 49, 121, 244, 27,
|
||||
115, 121, 25, 66, 216, 67, 97, 101, 140, 160, 77, 239, 114, 215, 152, 48, 15, 231, 101,
|
||||
60, 42, 92, 128, 131, 161, 43,
|
||||
];
|
||||
let expected3 = G1Affine::from_compressed(&[
|
||||
184, 200, 211, 115, 47, 45, 39, 185, 105, 9, 222, 247, 132, 241, 121, 130, 238, 224,
|
||||
155, 109, 105, 201, 137, 154, 132, 149, 214, 233, 136, 69, 77, 132, 174, 30, 46, 123,
|
||||
20, 92, 219, 18, 45, 29, 208, 127, 158, 145, 130, 41,
|
||||
])
|
||||
.unwrap()
|
||||
.into();
|
||||
|
||||
assert_eq!(hash_g1(msg1), expected1);
|
||||
assert_eq!(hash_g1(msg2), expected2);
|
||||
assert_eq!(hash_g1(msg3), expected3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ pub use sphinx_packet::{
|
||||
route::{Destination, DestinationAddressBytes, Node, NodeAddressBytes, SURBIdentifier},
|
||||
surb::{SURBMaterial, SURB},
|
||||
version::Version,
|
||||
version::UPDATED_LEGACY_VERSION,
|
||||
Error as SphinxError, ProcessedPacket, ProcessedPacketData,
|
||||
};
|
||||
|
||||
@@ -91,12 +90,8 @@ impl NymPacket {
|
||||
destination: &Destination,
|
||||
delays: &[Delay],
|
||||
) -> Result<NymPacket, NymPacketError> {
|
||||
// FIXME:
|
||||
// for now explicitly use the legacy version until sufficient number of nodes
|
||||
// understand both variants
|
||||
Ok(NymPacket::Sphinx(
|
||||
SphinxPacketBuilder::new()
|
||||
.with_version(UPDATED_LEGACY_VERSION)
|
||||
.with_payload_size(size)
|
||||
.build_packet(message, route, destination, delays)?,
|
||||
))
|
||||
|
||||
@@ -182,9 +182,11 @@ impl BlockProcessor {
|
||||
// the ones concerned with individual messages
|
||||
for (index, msg) in block_tx.tx.body.messages.iter().enumerate() {
|
||||
for msg_module in &mut self.msg_modules {
|
||||
msg_module
|
||||
.handle_msg(index, msg, &block_tx, &mut tx)
|
||||
.await?
|
||||
if msg.type_url == msg_module.type_url() {
|
||||
msg_module
|
||||
.handle_msg(index, msg, &block_tx, &mut tx)
|
||||
.await?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +83,15 @@ pub enum ScraperError {
|
||||
source: cosmrs::ErrorReport,
|
||||
},
|
||||
|
||||
#[error("could not parse msg in tx {hash} at index {index} into {type_url}: {source}")]
|
||||
MsgParseFailure {
|
||||
hash: Hash,
|
||||
index: usize,
|
||||
type_url: String,
|
||||
#[source]
|
||||
source: cosmrs::ErrorReport,
|
||||
},
|
||||
|
||||
#[error("received an invalid chain subscription event of kind {kind} while we were waiting for new block data (query: '{query}')")]
|
||||
InvalidSubscriptionEvent { query: String, kind: String },
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ use cosmrs::Any;
|
||||
|
||||
#[async_trait]
|
||||
pub trait MsgModule {
|
||||
fn type_url(&self) -> String;
|
||||
|
||||
async fn handle_msg(
|
||||
&mut self,
|
||||
index: usize,
|
||||
|
||||
@@ -111,7 +111,7 @@ impl StorageManager {
|
||||
consensus_address: &str,
|
||||
start_height: i64,
|
||||
end_height: i64,
|
||||
) -> Result<i32, sqlx::Error> {
|
||||
) -> Result<i64, sqlx::Error> {
|
||||
trace!("get_signed_between");
|
||||
let start = Instant::now();
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ impl ScraperStorage {
|
||||
consensus_address: &str,
|
||||
start_height: i64,
|
||||
end_height: i64,
|
||||
) -> Result<i32, ScraperError> {
|
||||
) -> Result<i64, ScraperError> {
|
||||
Ok(self
|
||||
.manager
|
||||
.get_signed_between(consensus_address, start_height, end_height)
|
||||
@@ -182,7 +182,7 @@ impl ScraperStorage {
|
||||
consensus_address: &str,
|
||||
start_time: OffsetDateTime,
|
||||
end_time: OffsetDateTime,
|
||||
) -> Result<i32, ScraperError> {
|
||||
) -> Result<i64, ScraperError> {
|
||||
let Some(block_start) = self.get_first_block_height_after(start_time).await? else {
|
||||
return Ok(0);
|
||||
};
|
||||
|
||||
@@ -9,3 +9,4 @@ repository = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
pem = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
@@ -6,6 +6,7 @@ use pem::Pem;
|
||||
use std::fs::File;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use tracing::debug;
|
||||
|
||||
pub mod traits;
|
||||
|
||||
@@ -46,6 +47,10 @@ where
|
||||
T: PemStorableKey,
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
debug!(
|
||||
"attempting to load key with the following pem type: {}",
|
||||
T::pem_type()
|
||||
);
|
||||
let key_pem = read_pem_file(path)?;
|
||||
|
||||
if T::pem_type() != key_pem.tag {
|
||||
|
||||
@@ -103,4 +103,8 @@ impl LaneQueueLengthsInner {
|
||||
{
|
||||
self.map.entry(*lane).and_modify(f);
|
||||
}
|
||||
|
||||
pub fn total(&self) -> usize {
|
||||
self.map.values().sum()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -494,6 +494,9 @@ pub struct ReplySurbsWasm {
|
||||
/// Defines how many mix nodes the reply surb should go through.
|
||||
/// If not set, the default value is going to be used.
|
||||
pub surb_mix_hops: Option<u8>,
|
||||
|
||||
/// Specifies if we should reset all the sender tags on startup
|
||||
pub fresh_sender_tags: bool,
|
||||
}
|
||||
|
||||
impl Default for ReplySurbsWasm {
|
||||
@@ -525,6 +528,7 @@ impl From<ReplySurbsWasm> for ConfigReplySurbs {
|
||||
reply_surbs.maximum_reply_key_age_ms as u64,
|
||||
),
|
||||
surb_mix_hops: reply_surbs.surb_mix_hops,
|
||||
fresh_sender_tags: reply_surbs.fresh_sender_tags,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -548,6 +552,7 @@ impl From<ConfigReplySurbs> for ReplySurbsWasm {
|
||||
maximum_reply_surb_age_ms: reply_surbs.maximum_reply_surb_age.as_millis() as u32,
|
||||
maximum_reply_key_age_ms: reply_surbs.maximum_reply_key_age.as_millis() as u32,
|
||||
surb_mix_hops: reply_surbs.surb_mix_hops,
|
||||
fresh_sender_tags: reply_surbs.fresh_sender_tags,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -378,6 +378,9 @@ pub struct ReplySurbsWasmOverride {
|
||||
|
||||
#[tsify(optional)]
|
||||
pub surb_mix_hops: Option<u8>,
|
||||
|
||||
/// Specifies if we should reset all the sender tags on startup
|
||||
pub fresh_sender_tags: bool,
|
||||
}
|
||||
|
||||
impl From<ReplySurbsWasmOverride> for ReplySurbsWasm {
|
||||
@@ -416,6 +419,7 @@ impl From<ReplySurbsWasmOverride> for ReplySurbsWasm {
|
||||
.maximum_reply_key_age_ms
|
||||
.unwrap_or(def.maximum_reply_key_age_ms),
|
||||
surb_mix_hops: value.surb_mix_hops,
|
||||
fresh_sender_tags: value.fresh_sender_tags,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1185,6 +1185,7 @@ name = "nym-pemstore"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"pem",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1251,6 +1252,12 @@ dependencies = [
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||
|
||||
[[package]]
|
||||
name = "pkcs8"
|
||||
version = "0.9.0"
|
||||
@@ -1522,18 +1529,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.25"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03"
|
||||
checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.217"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
|
||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
@@ -1558,9 +1565,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.217"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1777,9 +1784,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.37"
|
||||
version = "0.3.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
|
||||
checksum = "dad298b01a40a23aac4580b67e3dbedb7cc8402f3592d7f49469de2ea4aecdd8"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
@@ -1794,15 +1801,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
||||
checksum = "765c97a5b985b7c11d7bc27fa927dc4fe6af3a6dfb021d28deb60d3bf51e76ef"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.19"
|
||||
version = "0.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
|
||||
checksum = "e8093bc3e81c3bc5f7879de09619d06c9a5a5e45ca44dfeeb7225bae38005c5c"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
@@ -1840,6 +1847,37 @@ dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
||||
dependencies = [
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.18.0"
|
||||
|
||||
@@ -7,4 +7,6 @@ package-lock.json
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
.env
|
||||
.env
|
||||
|
||||
scratch.md
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import explorerLogo from "../public/images/smiley.png";
|
||||
|
||||
export const Explorer = () => {
|
||||
return (
|
||||
<Link
|
||||
href={"https://nym.com/explorer"}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
src={explorerLogo}
|
||||
style = {{
|
||||
marginRight: "0.6rem"
|
||||
}}
|
||||
alt={"Network Explorer"}
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
@@ -5,7 +5,7 @@ const links = [
|
||||
["Matrix", "https://matrix.to/#/#dev:nymtech.chat"],
|
||||
["GitHub", "https://nymtech.net/go/github/nym"],
|
||||
["Nym Wallet", "https://nymtech.net/download/wallet"],
|
||||
["Nym Explorer", "https://explorer.nymtech.net/"],
|
||||
["Nym Explorer", "https://nym.com/explorer/"],
|
||||
["Nym Blog", "https://nymtech.medium.com/"],
|
||||
["Twitter", "https://nymtech.net/go/x"],
|
||||
["Telegram", "https://nymtech.net/go/telegram"],
|
||||
|
||||
@@ -6,10 +6,6 @@ import { useTheme } from "@mui/material/styles";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
|
||||
// import networkDocs from "../public/images/landing/network-docs.png";
|
||||
// import developerDocs from "../public/images/landing/developer-docs.png";
|
||||
// import sdkDocs from "../public/images/landing/sdk-docs.png";
|
||||
// import operatorGuide from "../public/images/landing/operator-guide.png";
|
||||
import networkDocs from "../public/images/landing/Vector1.png";
|
||||
import developerDocs from "../public/images/landing/Vector2.png";
|
||||
import sdkDocs from "../public/images/landing/Vector3.png";
|
||||
@@ -41,13 +37,6 @@ export const LandingPage = () => {
|
||||
href: "/developers",
|
||||
icon: sdkDocs,
|
||||
},
|
||||
// {
|
||||
// text: "SDKs",
|
||||
// description: "Rust and Typescript SDK docs",
|
||||
|
||||
// href: "/developers/rust",
|
||||
// icon: sdkDocs,
|
||||
// },
|
||||
{
|
||||
text: "APIs",
|
||||
description: "Interactive API specs for Nym infrastructure",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Box } from "@mui/material";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import matrixLogo from "../public/images/matrix-logo.png";
|
||||
|
||||
export const Matrix = () => {
|
||||
return (
|
||||
<Link
|
||||
@@ -9,7 +9,14 @@ export const Matrix = () => {
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image src={matrixLogo} alt={"Matrix Logo"} width={20} height={24} />
|
||||
<Image src={matrixLogo}
|
||||
style = {{
|
||||
marginRight: "0.6rem"
|
||||
}}
|
||||
alt={"Matrix Logo"}
|
||||
width={20}
|
||||
height={24}
|
||||
/>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
- Run synchronization against [pool.ntp.org](https://www.ntppool.org/en/):
|
||||
```bash
|
||||
ntpdate -q pool.ntp.org
|
||||
```
|
||||
- Enable `ntp` service, start and review the status:
|
||||
```bash
|
||||
systemctl enable --now ntp
|
||||
service ntp start
|
||||
service ntp status
|
||||
```
|
||||
@@ -1 +1 @@
|
||||
807_251_217
|
||||
808_623_916
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.64%
|
||||
0.68%
|
||||
|
||||
@@ -1 +1 @@
|
||||
44.811
|
||||
42.157
|
||||
|
||||
@@ -1 +1 @@
|
||||
1_025_628
|
||||
1_028_488
|
||||
|
||||
@@ -1 +1 @@
|
||||
403_625_608
|
||||
404_311_958
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
| **Item** | **Description** | **Amount in NYM** |
|
||||
|:-------------------|:------------------------------------------------------|--------------------:|
|
||||
| Total Supply | Maximum amount of NYM token in existence | 1_000_000_000 |
|
||||
| Mixmining Reserve | Tokens releasing for operators rewards | 192_748_782 |
|
||||
| Mixmining Reserve | Tokens releasing for operators rewards | 191_376_083 |
|
||||
| Vesting Tokens | Tokens locked outside of cicrulation for future claim | 0 |
|
||||
| Circulating Supply | Amount of unlocked tokens | 807_251_217 |
|
||||
| Stake Saturation | Optimal size of node self-bond + delegation | 1_025_628 |
|
||||
| Circulating Supply | Amount of unlocked tokens | 808_623_916 |
|
||||
| Stake Saturation | Optimal size of node self-bond + delegation | 1_028_488 |
|
||||
|
||||
@@ -1 +1 @@
|
||||
Wednesday, February 26th 2025, 16:02:47 UTC
|
||||
Tuesday, March 11th 2025, 11:04:18 UTC
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
{
|
||||
"mainnet":"Mainnet Endpoints",
|
||||
"sandbox":"Sandbox Endpoints"
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
# Explorer API
|
||||
|
||||
The Explorer API is the backend for the [Mixnet Explorer](https://explorer.nymtech.net/).
|
||||
The Explorer API is the backend for the [Mixnet Explorer](https://nym.com/explorer).
|
||||
|
||||
**This will soon be deprecated in favour of the [Node Status API](ns-api.mdx).**
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
{
|
||||
"mainnet":"Mainnet Endpoints",
|
||||
"sandbox":"Sandbox Endpoints"
|
||||
}
|
||||
@@ -1,11 +1,8 @@
|
||||
import { Callout } from 'nextra/components'
|
||||
|
||||
# Node Status API
|
||||
|
||||
The Node Status API contains information about the network, its topology, and the routing scores of all nodes within it. It offers broadly similar information to the experimental [Habourmaster frontend](https://harbourmaster.nymtech.net/) but is stable where the Harbourmaster is subject to sudden changes as we modify and experiment with how we scrape and present data about the Mixnet infrastructure.
|
||||
The Node Status API serves information about individual `nym-nodes` in the Mixnet, such as which role they are operating in, statistics about them, services such as Network Requesters, as well as summaries of the state of the Mixnet.
|
||||
|
||||
<Callout type="info">
|
||||
People building applications or dashboards which requires information about nodes, their uptime, and their delegations should use this instead of Habourmaster.
|
||||
We recommend that developers building applications such as explorers or analytics interfaces about the Mixnet run their own instance of the API, in order to promote a robust network of downstream services, and spread the load of API calls amongst as many endpoints as possible.
|
||||
</Callout>
|
||||
|
||||
The code for this service can be found [in our monorepo](https://github.com/nymtech/nym/tree/develop/nym-node-status-api). In the future we will encourage developers to run their own instance of this API in order to distribute endpoints and query load.
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"ns-api-run-deploy":"Run Instance",
|
||||
"mainnet":"Mainnet Endpoints",
|
||||
"sandbox":"Sandbox Endpoints"
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
import { RedocStandalone } from 'redoc';
|
||||
import { Callout } from 'nextra/components'
|
||||
|
||||
The information below is generated with [Redoc](https://redocly.com/docs/redoc) consuming the OpenAPI spec found at [https://mainnet-node-status-api.nymtech.cc/api-docs/openapi.json](https://mainnet-node-status-api.nymtech.cc/api-docs/openapi.json) which is also used to generate the Swagger docs deployed at [https://mainnet-node-status-api.nymtech.cc/swagger/](https://mainnet-node-status-api.nymtech.cc/swagger/).
|
||||
|
||||
<br /><br />
|
||||
|
||||
|
||||
<RedocStandalone
|
||||
specUrl="https://mainnet-node-status-api.nymtech.cc/api-docs/openapi.json"
|
||||
options={{
|
||||
|
||||
@@ -0,0 +1,336 @@
|
||||
import { Callout } from 'nextra/components'
|
||||
import { AccordionTemplate } from 'components/accordion-template.tsx'
|
||||
|
||||
# NS API: Deployment Guide
|
||||
|
||||
## Components
|
||||
The Node Status API is made up of 3 components:
|
||||
- `nym-node-status-api`
|
||||
- `nym-node-status-client`
|
||||
- `nym-node-status-agent`
|
||||
|
||||
The API stores its data in a local SQLite database. It periodically gets most of its data from a [NymAPI](./nym-api) instance, and for instances also running with the Gateway Probe, uses the stored topology to conduct performance probe tests.
|
||||
|
||||
## Gateway Probe
|
||||
You can run the `nym-node-status-api` alone, but if you want to run probes for `nym-node`s running as Gateways, you have to also run the `nym-node-status-agent` (which consumes the `nym-node-status-client` lib). The probe will run a set of tests per Gateway node, and return the sort of results seen [here](https://harbourmaster.nymtech.net/gateway/23A7CSaBSA2L67PWuFTPXUnYrCdyVcB7ATYsjUsfdftb).
|
||||
|
||||
## UI
|
||||
The API exposes a [Swagger](https://swagger.io/docs/) documentation UI by default on port `8000` (see an example [here](https://mainnet-node-status-api.nymtech.cc/swagger/)).
|
||||
|
||||
Currently we are not shipping a custom UI component for the Node Status API. The [Harbourmaster](https://harbourmaster.nymtech.net/) frontend consumes this API, amongst other things, but this is an internal repo we use to experiment with new APIs and data on, so it is not public yet.
|
||||
|
||||
<Callout type="info">
|
||||
We invite developers to roll their own UI for their Node Status API instance.
|
||||
</Callout>
|
||||
|
||||
## Docker Images
|
||||
We will ship Docker images for both the `agent` and `api` in the future. There are Docker images for both in root of each corresponding crate ([`agent`](https://github.com/nymtech/nym/blob/09ea406c02e9a3beebc062f525e4ea1b4222dcbb/nym-node-status-api/nym-node-status-agent/Dockerfile), [`api`](https://github.com/nymtech/nym/blob/develop/nym-node-status-api/nym-node-status-api/Dockerfile)) which are used internally, which could be a starting point for developers to dockerize their instance for the moment.
|
||||
|
||||
## Build
|
||||
### Prerequisites
|
||||
- Rust
|
||||
- SQLite
|
||||
- Get an `ipinfo` key following instructions [here](https://github.com/ipinfo/rust?tab=readme-ov-file#getting-started).
|
||||
|
||||
### Compilation
|
||||
```shell
|
||||
cargo build --release --package nym-node-status-api --package nym-node-status-agent --package nym-node-status-client
|
||||
```
|
||||
|
||||
## Run
|
||||
Since the Node Status API depends on both flags and environmental variables, it might be easier to run the binary via a script like the one below - this this script essentially just `source`-s the defined `.env` file after exporting certain binary-specific variables, and then runs the binary. You can find the `.env` files [here](https://github.com/nymtech/nym/tree/master/envs).
|
||||
|
||||
<Callout type="info">
|
||||
All CLI flags are configurable as environmental variables and vice versa, so take the following scripts / setups as guides that you can change however best suits your setup. You can see all definitions [here](https://github.com/nymtech/nym/blob/develop/nym-node-status-api/nym-node-status-api/src/cli/mod.rs#L14).
|
||||
</Callout>
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
export ENVIRONMENT=${ENVIRONMENT:-"mainnet"} # see nym/envs/ for all possible environments
|
||||
export NYM_API_CLIENT_TIMEOUT=60
|
||||
export NODE_STATUS_API_TESTRUN_REFRESH_INTERVAL=120
|
||||
export IPINFO_API_TOKEN=<YOUR_IPINFO_API_KEY>
|
||||
|
||||
monorepo_root=<PATH/TO/NYM/>
|
||||
set -a
|
||||
source "${monorepo_root}/envs/${ENVIRONMENT}.env"
|
||||
echo ${monorepo_root}/envs/${ENVIRONMENT}.env
|
||||
set +a
|
||||
export RUST_LOG=${RUST_LOG:-debug} # debug is useful to check everything is working initially, but quite verbose
|
||||
|
||||
echo "Verifying environment variables were properly sourced:"
|
||||
echo "RUST_LOG=${RUST_LOG}"
|
||||
echo "BECH32_PREFIX=${BECH32_PREFIX}"
|
||||
echo "NETWORK_NAME=${NETWORK_NAME}"
|
||||
|
||||
<PATH/TO/>nym-node-status-api -- --ipinfo-api-token $IPINFO_API_TOKEN
|
||||
```
|
||||
|
||||
### Functionality Without Gateway Probe
|
||||
Data will be restricted to information that doesn't involve Probe results; `routing` and `config` scores will be `0` and `last_probe` results `null`, as you can see in this snipped output of the `gateways` endpoint:
|
||||
|
||||
<AccordionTemplate name="Output">
|
||||
```shell
|
||||
{
|
||||
"gateway_identity_key": "23A7CSaBSA2L67PWuFTPXUnYrCdyVcB7ATYsjUsfdftb",
|
||||
"bonded": true,
|
||||
"performance": 99,
|
||||
"self_described": {
|
||||
"authenticator": {
|
||||
"address": "6Gdtw13Fa46AvkqkHELZZCMKWASDodoJeK9APRNpjjdj.7ji8DDkpjA2AdgwK7wbZm8yi4xZGogGJeypBQt4hAw3P@23A7CSaBSA2L67PWuFTPXUnYrCdyVcB7ATYsjUsfdftb"
|
||||
},
|
||||
"auxiliary_details": {
|
||||
"accepted_operator_terms_and_conditions": true,
|
||||
"announce_ports": {
|
||||
"mix_port": null,
|
||||
"verloc_port": null
|
||||
},
|
||||
"location": null
|
||||
},
|
||||
"build_information": {
|
||||
"binary_name": "nym-node",
|
||||
"build_timestamp": "2025-02-13T11:49:34.670488195Z",
|
||||
"build_version": "1.5.0",
|
||||
"cargo_profile": "release",
|
||||
"cargo_triple": "x86_64-unknown-linux-gnu",
|
||||
"commit_branch": "HEAD",
|
||||
"commit_sha": "a3e19b4563843055b305ea9a397eb1ad84b5c378",
|
||||
"commit_timestamp": "2025-02-10T18:14:47.000000000+01:00",
|
||||
"rustc_channel": "stable",
|
||||
"rustc_version": "1.84.1"
|
||||
},
|
||||
"declared_role": {
|
||||
"entry": true,
|
||||
"exit_ipr": true,
|
||||
"exit_nr": true,
|
||||
"mixnode": false
|
||||
},
|
||||
"host_information": {
|
||||
"hostname": "bwng1.bwnym.xyz",
|
||||
"ip_address": [
|
||||
"95.164.2.86"
|
||||
],
|
||||
"keys": {
|
||||
"ed25519": "23A7CSaBSA2L67PWuFTPXUnYrCdyVcB7ATYsjUsfdftb",
|
||||
"x25519": "H6pFjqtdSVxkxEQ3wFnuSoobDAUqHx1bYMVJzPZdRByn",
|
||||
"x25519_noise": null
|
||||
}
|
||||
},
|
||||
"ip_packet_router": {
|
||||
"address": "7ms2D2uYiTuhX6MKeVL5rz5usgehEoxAAovwYm9nJyBF.3siMjk3wTU7ykaXLNi9c7LpX8yonYKPCA4BQoMwhsfTV@23A7CSaBSA2L67PWuFTPXUnYrCdyVcB7ATYsjUsfdftb"
|
||||
},
|
||||
"last_polled": "2025-03-03 09:48:03.635274187 +00:00:00",
|
||||
"mixnet_websockets": {
|
||||
"ws_port": 9000,
|
||||
"wss_port": 9001
|
||||
},
|
||||
"network_requester": {
|
||||
"address": "HuNL1pFprNSKW6jdqppibXP5KNKCNJxDh7ivpYcoULN9.C62NahRTUf6kqpNtDVHXoVriQr6yyaU5LtxdgpbsGrtA@23A7CSaBSA2L67PWuFTPXUnYrCdyVcB7ATYsjUsfdftb",
|
||||
"uses_exit_policy": true
|
||||
},
|
||||
"wireguard": {
|
||||
"port": 51822,
|
||||
"public_key": "6o8x9GitFjcrkjrJnivWaQCPnxXykQPYLneNr2FEB8Vq"
|
||||
}
|
||||
},
|
||||
"explorer_pretty_bond": {
|
||||
"identity_key": "23A7CSaBSA2L67PWuFTPXUnYrCdyVcB7ATYsjUsfdftb",
|
||||
"location": {
|
||||
"latitude": 52.5083,
|
||||
"longitude": 5.475,
|
||||
"two_letter_iso_country_code": "NL"
|
||||
},
|
||||
"owner": "n1cp5gq0apat6c7qmenqp5zjprn2vwvc7jl29j8r",
|
||||
"pledge_amount": {
|
||||
"amount": "100000000",
|
||||
"denom": "unym"
|
||||
}
|
||||
},
|
||||
"description": {
|
||||
"moniker": "bwn_g1",
|
||||
"website": "https://bwnym.xyz",
|
||||
"security_contact": "bwnym@proton.me",
|
||||
"details": "This gateway is part of the NYM project, which is dedicated to create outstanding privacy software that is legally compliant without sacrificing integrity or having any backdoors."
|
||||
},
|
||||
"last_probe_result": null,
|
||||
"last_probe_log": null,
|
||||
"last_testrun_utc": null,
|
||||
"last_updated_utc": "2025-03-03T10:45:48+00:00",
|
||||
"routing_score": 0.0,
|
||||
"config_score": 0
|
||||
},
|
||||
```
|
||||
</AccordionTemplate>
|
||||
|
||||
If you have already run the API before, make sure to add the following to your script `--database-url "sqlite://node-status-api.sqlite?mode=rwc` so it will continue using the same DB.
|
||||
|
||||
### Functionality with Gateway Probe
|
||||
If you want to enable Gateway node probes and have the NS API store that data, you need to periodically run the `nym-node-status-agent`, authenticated with your `nym-node-status-api` instance. Authentication is to make sure that only the `-agent` you are operating (or others you trust) are submitting data to your API instance.
|
||||
|
||||
#### Compile Gateway Probe
|
||||
The `nym-node-status-agent` is a thin wrapper around the Gateway Probe binary which currently is in the NymVPN repo. `git checkout` to the most recent [release](https://github.com/nymtech/nym-vpn-client/releases), and compile the probe by following the [readme instructions](https://github.com/nymtech/nym-vpn-client/tree/develop/nym-vpn-core/crates/nym-gateway-probe). You will point the `-agent` at this binary when doing Probe testruns.
|
||||
|
||||
#### Generate Keypair
|
||||
```shell
|
||||
<PATH/TO/>nym-node-status-agent generate-keypair --path <PATH/TO/KEY/FILE/TO/GENERATE>/<KEY_NAME>
|
||||
# e.g.
|
||||
# nym-node-status-agent generate-keypair --path ~/.ssh/ns-agent-key
|
||||
```
|
||||
|
||||
You will then want to export the generated `public-key` so its accessible to the `nym-node-status-api` however you are setting your environmental variables, as `NODE_STATUS_API_AGENT_KEY_LIST`:
|
||||
|
||||
```bash
|
||||
export NODE_STATUS_API_AGENT_KEY_LIST=<YOUR_KEY> # e.g. "H4z8kx5Kkf5JNQHfxaE1MwRndjDCD1C7HsVhHTFfBZ4J"
|
||||
```
|
||||
|
||||
In this situation, you are probably only using one key. However, it is possible to set multiple keys as a comma seperated list, in case you wish to whitelist multiple `-agent`s to be able to submit to a single `-api` instance.
|
||||
|
||||
#### Run the Node Status Agent
|
||||
```shell
|
||||
<PATH/TO/>nym-node-status-agent run-probe --server-address http://127.0.0.1 --server-port 8000 --ns-api-auth-key "<NS_AGENT_PRIVATE_KEY>" --probe-path <PATH/TO/> nym-gateway-probe
|
||||
```
|
||||
|
||||
You will see a lot of output like so:
|
||||
|
||||
<AccordionTemplate name="Output">
|
||||
```shell
|
||||
listen_port=48586
|
||||
public_key=3f95caf771b8a63b9bdae6c186f7aba2eae93483f1c82c0243b5e00c85b4ec26
|
||||
preshared_key=0000000000000000000000000000000000000000000000000000000000000000
|
||||
protocol_version=1
|
||||
endpoint=185.186.78.251:51822
|
||||
last_handshake_time_sec=0
|
||||
last_handshake_time_nsec=0
|
||||
tx_bytes=0
|
||||
rx_bytes=0
|
||||
persistent_keepalive_interval=0
|
||||
allowed_ip=0.0.0.0/0
|
||||
2025/03/03 18:58:28 Pinging nymtech.net seq=0
|
||||
2025/03/03 18:58:29 Ping latency: 44.503483ms
|
||||
2025/03/03 18:58:29 Pinging nymtech.net seq=1
|
||||
2025/03/03 18:58:29 Ping latency: 42.852414ms
|
||||
2025/03/03 18:58:29 Pinging nymtech.net seq=2
|
||||
2025/03/03 18:58:29 Ping latency: 43.627256ms
|
||||
2025/03/03 18:58:29 Pinging nymtech.net seq=3
|
||||
2025/03/03 18:58:29 Ping latency: 43.638839ms
|
||||
2025/03/03 18:58:29 Pinging nymtech.net seq=4
|
||||
2025/03/03 18:58:29 Ping latency: 43.345357ms
|
||||
2025/03/03 18:58:29 Pinging 1.1.1.1 seq=0
|
||||
2025/03/03 18:58:29 Ping latency: 46.327233ms
|
||||
2025/03/03 18:58:34 Pinging 1.1.1.1 seq=1
|
||||
2025/03/03 18:58:34 Ping latency: 46.273726ms
|
||||
2025/03/03 18:58:39 Pinging 1.1.1.1 seq=2
|
||||
2025/03/03 18:58:39 Ping latency: 46.542774ms
|
||||
2025/03/03 18:58:44 Pinging 1.1.1.1 seq=3
|
||||
2025/03/03 18:58:44 Ping latency: 45.663545ms
|
||||
2025/03/03 18:58:49 Pinging 1.1.1.1 seq=4
|
||||
2025/03/03 18:58:49 Ping latency: 43.803063ms
|
||||
2025/03/03 18:58:56 Downloaded file content length: 1.00 MB
|
||||
2025/03/03 18:58:56 Download duration: 1.308072386s
|
||||
2025/03/03 18:58:56 private_key=1083749e43f4f8fb008f3f7deef9107ef86a68969670ddbb9f07bf85f94fb564
|
||||
listen_port=39129
|
||||
public_key=3f95caf771b8a63b9bdae6c186f7aba2eae93483f1c82c0243b5e00c85b4ec26
|
||||
preshared_key=0000000000000000000000000000000000000000000000000000000000000000
|
||||
protocol_version=1
|
||||
endpoint=185.186.78.251:51822
|
||||
last_handshake_time_sec=0
|
||||
last_handshake_time_nsec=0
|
||||
tx_bytes=0
|
||||
rx_bytes=0
|
||||
persistent_keepalive_interval=0
|
||||
allowed_ip=::/0
|
||||
2025/03/03 18:58:56 Pinging ipv6.google.com seq=0
|
||||
2025/03/03 18:58:56 Ping latency: 42.839528ms
|
||||
2025/03/03 18:58:56 Pinging ipv6.google.com seq=1
|
||||
2025/03/03 18:58:56 Ping latency: 54.844651ms
|
||||
2025/03/03 18:58:56 Pinging ipv6.google.com seq=2
|
||||
2025/03/03 18:58:56 Ping latency: 51.23104ms
|
||||
2025/03/03 18:58:56 Pinging ipv6.google.com seq=3
|
||||
2025/03/03 18:58:56 Ping latency: 43.320409ms
|
||||
2025/03/03 18:58:56 Pinging ipv6.google.com seq=4
|
||||
2025/03/03 18:58:56 Ping latency: 63.517358ms
|
||||
2025/03/03 18:58:56 Pinging 2001:4860:4860::8888 seq=0
|
||||
2025/03/03 18:58:56 Ping latency: 54.682534ms
|
||||
2025/03/03 18:59:01 Pinging 2001:4860:4860::8888 seq=1
|
||||
2025/03/03 18:59:01 Ping latency: 55.56235ms
|
||||
2025/03/03 18:59:06 Pinging 2001:4860:4860::8888 seq=2
|
||||
2025/03/03 18:59:06 Ping latency: 55.970418ms
|
||||
2025/03/03 18:59:11 Pinging 2001:4860:4860::8888 seq=3
|
||||
2025/03/03 18:59:14 Failed to send ping: i/o timeout
|
||||
2025/03/03 18:59:19 Pinging 2001:4860:4860::8888 seq=4
|
||||
2025/03/03 18:59:22 Failed to send ping: i/o timeout
|
||||
2025/03/03 18:59:27 Pinging 2606:4700:4700::1111 seq=0
|
||||
2025/03/03 18:59:27 Ping latency: 45.072616ms
|
||||
2025/03/03 18:59:32 Pinging 2606:4700:4700::1111 seq=1
|
||||
2025/03/03 18:59:32 Ping latency: 44.357306ms
|
||||
2025/03/03 18:59:37 Pinging 2606:4700:4700::1111 seq=2
|
||||
2025/03/03 18:59:37 Ping latency: 44.013562ms
|
||||
2025/03/03 18:59:42 Pinging 2606:4700:4700::1111 seq=3
|
||||
2025/03/03 18:59:42 Ping latency: 46.94342ms
|
||||
2025/03/03 18:59:47 Pinging 2606:4700:4700::1111 seq=4
|
||||
2025/03/03 18:59:48 Ping latency: 43.372288ms
|
||||
2025/03/03 18:59:53 Pinging 2620:fe::fe seq=0
|
||||
2025/03/03 18:59:53 Ping latency: 42.164952ms
|
||||
2025/03/03 18:59:58 Pinging 2620:fe::fe seq=1
|
||||
2025/03/03 18:59:58 Ping latency: 42.295812ms
|
||||
2025/03/03 19:00:03 Pinging 2620:fe::fe seq=2
|
||||
2025/03/03 19:00:03 Ping latency: 43.117534ms
|
||||
2025/03/03 19:00:08 Pinging 2620:fe::fe seq=3
|
||||
2025/03/03 19:00:08 Ping latency: 44.26068ms
|
||||
2025/03/03 19:00:13 Pinging 2620:fe::fe seq=4
|
||||
2025/03/03 19:00:13 Ping latency: 45.29956ms
|
||||
2025/03/03 19:00:21 Downloaded file content length: 10.00 MB
|
||||
2025/03/03 19:00:21 Download duration: 3.39529252s
|
||||
```
|
||||
</AccordionTemplate>
|
||||
|
||||
Whilst you can run the `-agent` directly, it might be easier to run multiple instances in parallel with a script like so:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
set -eu
|
||||
export ENVIRONMENT=${ENVIRONMENT:-"mainnet"}
|
||||
|
||||
probe_git_ref="nym-vpn-core-v1.4.0" # check for the most recent release
|
||||
monorepo_root=<PATH/TO/NYM/>
|
||||
|
||||
set -a
|
||||
source "${monorepo_root}/envs/${ENVIRONMENT}.env"
|
||||
set +a
|
||||
|
||||
export RUST_LOG="info"
|
||||
export NODE_STATUS_AGENT_SERVER_ADDRESS="http://127.0.0.1"
|
||||
export NODE_STATUS_AGENT_SERVER_PORT="8000"
|
||||
export NODE_STATUS_AGENT_AUTH_KEY=<NS_AGENT_PRIVATE_KEY>
|
||||
export NODE_STATUS_AGENT_PROBE_EXTRA_ARGS="netstack-download-timeout-sec=30,netstack-num-ping=2,netstack-send-timeout-sec=1,netstack-recv-timeout-sec=1"
|
||||
|
||||
workers=${1:-1}
|
||||
echo "Running $workers workers in parallel"
|
||||
|
||||
function swarm() {
|
||||
local workers=$1
|
||||
|
||||
for ((i = 1; i <= workers; i++)); do
|
||||
${monorepo_root}/target/release/nym-node-status-agent run-probe --probe-path ~/<PATH/TO>/nym-vpn-client/nym-vpn-core/target/debug/nym-gateway-probe &
|
||||
done
|
||||
|
||||
wait
|
||||
|
||||
echo "All agents completed"
|
||||
}
|
||||
|
||||
swarm $workers
|
||||
```
|
||||
|
||||
And run specifying the number of workers with `./<SCRIPT_NAME>.sh <NUMBER_OF_WORKERS>`.
|
||||
|
||||
<Callout type="info">
|
||||
When running the probe, use logging level `RUST_LOG=info`. The Node Status API relies on that granularity for parsing probe results, and the logs grow incessantly if a lower level (e.g. `DEBUG`) is used.
|
||||
</Callout>
|
||||
|
||||
### Ports
|
||||
By default the API listens on `8000`, so you will need to configure this post to be reachable on your remote server. You can modify this with the `--http_port` flag.
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"mainnet":"Mainnet Endpoints"
|
||||
}
|
||||
@@ -30,7 +30,7 @@ The `--id` in the example above is a local identifier so that you can name your
|
||||
|
||||
The `--use-reply-surbs` field denotes whether you wish to send [SURBs](../../network/concepts/anonymous-replies) along with your request. It defaults to `false`, we are explicitly setting it as `true`. It defaults to `false` for compatibility with versions of the pre-smoosh `nym-network-requester` binary which will soon be deprecated.
|
||||
|
||||
The `--provider` field needs to be filled with the Nym address of an Exit Gateway that can make network requests on your behalf. You can select one from the [mixnet explorer](https://explorer.nymtech.net/network-components/gateways) by copying its `Client ID` and using this as the value of the `--provider` flag. Alternatively, you could use [Harbourmaster](https://harbourmaster.nymtech.net/).
|
||||
The `--provider` field needs to be filled with the Nym address of an Exit Gateway that can make network requests on your behalf. You can select one from the [mixnet explorer](https://nym.com/explorer) by copying its `Client ID` and using this as the value of the `--provider` flag. Alternatively, you could use [Harbourmaster](https://harbourmaster.nymtech.net/).
|
||||
|
||||
## Choosing a Gateway
|
||||
By default - as in the example above - your client will choose a random gateway to connect to.
|
||||
@@ -42,6 +42,6 @@ However, there are several options for choosing a gateway, if you do not want on
|
||||
* send few ping messages to all of them, and measure response times.
|
||||
* create a weighted distribution to randomly choose one, favouring ones with lower latency.
|
||||
|
||||
You can select one from the [mixnet explorer](https://explorer.nymtech.net/network-components/gateways) by copying its `Client ID` and using this as the value of the `--provider` flag. Alternatively, you could use [Harbourmaster](https://harbourmaster.nymtech.net/).
|
||||
You can select one from the [mixnet explorer](https://nym.com/explorer) by copying its `Client ID` and using this as the value of the `--provider` flag. Alternatively, you could use [Harbourmaster](https://harbourmaster.nymtech.net/).
|
||||
|
||||
> Note this doesn't mean that your client will pick the closest gateway to you, but it will be far more likely to connect to gateway with a 20ms ping rather than 200ms
|
||||
|
||||
@@ -31,7 +31,7 @@ Initialising a new client instance can be done with the following command:
|
||||
|
||||
The `--id` in the example above is a local identifier so that you can name your clients; it is **never** transmitted over the network.
|
||||
|
||||
There is an optional `--gateway` flag that you can use if you want to use a specific gateway. The supplied argument is the `Identity Key` of the gateway you wish to use, which can be found on the [mixnet explorer](https://explorer.nymtech.net/network-components/gateways). Alternatively, you could use [Harbourmaster](https://harbourmaster.nymtech.net/)
|
||||
There is an optional `--gateway` flag that you can use if you want to use a specific gateway. The supplied argument is the `Identity Key` of the gateway you wish to use, which can be found on the [mixnet explorer](https://nym.com/explorer). Alternatively, you could use [Harbourmaster](https://harbourmaster.nymtech.net/)
|
||||
|
||||
Not passing this argument will randomly select a gateway for your client.
|
||||
|
||||
|
||||
@@ -19,4 +19,4 @@ To illustrate this: `DpB3cHAchJiNBQi5FrZx2csXb1mrHkpYh9Wzf8Rjsuko.ANNWrvHqMYuert
|
||||
|
||||
- `DpB3cHAchJiNBQi5FrZx2csXb1mrHkpYh9Wzf8Rjsuko`: is the client's identity key;
|
||||
- `ANNWrvHqMYuertHGHUrZdBntQhpzfbWekB39qez9U2Vx`: is the client's Diffie Hellman key;
|
||||
- `2BuMSfMW3zpeAjKXyKLhmY4QW1DXurrtSPEJ6CjX3SEh`: is the gateway's identity, which is what you'll need to check the state of the gateway in the [Nym Explorer](https://explorer.nymtech.net/network-components/gateways).
|
||||
- `2BuMSfMW3zpeAjKXyKLhmY4QW1DXurrtSPEJ6CjX3SEh`: is the gateway's identity, which is what you'll need to check the state of the gateway in the [Nym Explorer](https://nym.com/explorer).
|
||||
|
||||
@@ -47,6 +47,218 @@ This page displays a full list of all the changes during our release cycle from
|
||||
|
||||
<VarInfo />
|
||||
|
||||
## `v2025.4-dorina-patched`
|
||||
|
||||
Patched version of `dorina` with a few fixes and tweaks to the release. We would like to ask `nym-node` operators to upgrade to this version as quickly as possible to implement the fixes across the network and improve general quality before NymVPN launch.
|
||||
|
||||
- [Release Binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2025.4-dorina-patched)
|
||||
- [`nym-node`](nodes/nym-node.mdx) version `1.6.2`
|
||||
|
||||
```shell
|
||||
nym-node
|
||||
Binary Name: nym-node
|
||||
Build Timestamp: 2025-03-06T20:32:36.922212778Z
|
||||
Build Version: 1.6.2
|
||||
Commit SHA: 247ebb7c4339de0a298a7fcb2574122a8306c3b8
|
||||
Commit Date: 2025-03-06T21:26:16.000000000+01:00
|
||||
Commit Branch: HEAD
|
||||
rustc Version: 1.85.0
|
||||
rustc Channel: stable
|
||||
cargo Profile: release
|
||||
```
|
||||
|
||||
- Use legacy crypto for constructing SURB headers ([#5579])
|
||||
- Bugfix: make sure to correctly decode response content when putting it into error message ([#5571])
|
||||
- Tweak surb management to be more conservative ([#5570])
|
||||
- Deserialize v5 authenticator requests ([#5568])
|
||||
- Chore: additional logs when attempting to load ecash keys ([#5567])
|
||||
- Add full response body to error message upon decoding failure ([#5566])
|
||||
- Hotfix: ensure we bail on merkle leaves insertion upon missing data ([#5565])
|
||||
- Feature: v2 authentication request (#5537) ([#5563])
|
||||
- Create authenticator v5 request/response types ([#5561])
|
||||
|
||||
[#5579]: https://github.com/nymtech/nym/pull/5579
|
||||
[#5571]: https://github.com/nymtech/nym/pull/5571
|
||||
[#5570]: https://github.com/nymtech/nym/pull/5570
|
||||
[#5568]: https://github.com/nymtech/nym/pull/5568
|
||||
[#5567]: https://github.com/nymtech/nym/pull/5567
|
||||
[#5566]: https://github.com/nymtech/nym/pull/5566
|
||||
[#5565]: https://github.com/nymtech/nym/pull/5565
|
||||
[#5563]: https://github.com/nymtech/nym/pull/5563
|
||||
[#5561]: https://github.com/nymtech/nym/pull/5561
|
||||
|
||||
|
||||
## `v2025.4-dorina`
|
||||
- [Release Binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2025.4-dorina)
|
||||
- [`nym-node`](nodes/nym-node.mdx) version `1.6.0`
|
||||
```shell
|
||||
Binary Name: nym-node
|
||||
Build Timestamp: 2025-03-04T09:03:11.322601809Z
|
||||
Build Version: 1.6.0
|
||||
Commit SHA: 7060fa6dad58f17543f5086c73b1854ad1ceae60
|
||||
Commit Date: 2025-03-03T17:24:10.000000000Z
|
||||
Commit Branch: release/2025.4-dorina
|
||||
rustc Version: 1.86.0-nightly
|
||||
rustc Channel: nightly
|
||||
cargo Profile: release
|
||||
```
|
||||
|
||||
### Operators Updates & Tools
|
||||
|
||||
- New advanced [guide to virtualise a dedicated server](nodes/preliminary-steps/vps-setup/advanced), providing steps for experienced operators and aspiring sys-admins who seek for higher optimisation and better efficiency of their work orchestrating multiple nodes.
|
||||
- New Service Grant program is being implemented by operators setting up new ~150 Exit Gateways following the updated [specs for `nym-node`](nodes#minimum-requirements)
|
||||
|
||||
### Features
|
||||
- [Feature/chain status api](https://github.com/nymtech/nym/pull/5539):
|
||||
this PR introduces `/v1/network/chain-status` endpoint on nym api to give basic information about the current block as seen by this API (with some caching) and updates `/health` endpoint to give stall information.
|
||||
|
||||
- [Add SURBs soft threshold](https://github.com/nymtech/nym/pull/5535): the IPR should try to keep a buffer of available SURBs to reduce latency.
|
||||
|
||||
- [Simplify IPR v8](https://github.com/nymtech/nym/pull/5532): purge stuff from IPR v8 to simplify; use protocol field in v8.
|
||||
|
||||
- [Shared instance for DNS AsyncResolver](https://github.com/nymtech/nym/pull/5523):
|
||||
make the `HickoryDnsResolver` use a shared instance by default to limit fd use. Now the multiple `nym_http_api_client::Client`s that get built should take advantage of a shared connection pool for DNS lookups. If for some reason a client does need an independent AsyncResolver this can still be done with the added `thread_resolver` function.
|
||||
|
||||
- [cherry-pick 17d3ff2d775f61aee381d90a304ed416c08f33fc onto dorina](https://github.com/nymtech/nym/pull/5519)
|
||||
|
||||
- [cherry-pick 6e5d0dac1b75413c5f09122b0d953f8ec6ef48df onto dorina](https://github.com/nymtech/nym/pull/5518)
|
||||
|
||||
- [feat: add config option for maximum number of client connections](https://github.com/nymtech/nym/pull/5513)
|
||||
|
||||
- [IPR request types v8](https://github.com/nymtech/nym/pull/5498): Bump IPR request/response types to v8
|
||||
|
||||
- [Support static routes for HTTP requests](https://github.com/nymtech/nym/pull/5487)
|
||||
|
||||
- [added missing import to doctest](https://github.com/nymtech/nym/pull/5480)
|
||||
|
||||
- [adjusted TestSetup::new_complex to ensure bonded node's existence](https://github.com/nymtech/nym/pull/5478)
|
||||
|
||||
- [Trigger contracts CI on main workspace Cargo changes](https://github.com/nymtech/nym/pull/5477): since the contracts workspace depends on the common code in the main workspace, and since the contracts are critical to not have regressions in, trigger contracts CI on any changes to the workspace Cargo.toml and lock files.
|
||||
|
||||
- [Run cargo autoinherit](https://github.com/nymtech/nym/pull/5460): run `cargo autoinherit` to move a bunch of dependencies to the workspace level. `Cargo.lock` remains untouched.
|
||||
|
||||
- [Disable debug in wasm and wallet workflows too](https://github.com/nymtech/nym/pull/5459)
|
||||
|
||||
- [Fix clippy::precedence](https://github.com/nymtech/nym/pull/5457): fix clippy warnings for the Rust beta toolchain.
|
||||
|
||||
- [Provide Interval context with node descriptor endpoints](https://github.com/nymtech/nym/pull/5456): add current interval context information to existing enpoints using `build_skimmed_nodes_response` under the hood. This allows clients checking for a refresh to send the `epoch_uid` as a query parameter when fetching updates so the server can tell it that there have been no changes, instead of sending duplicate data over and over. The changes in this PR should be backwards compatible and never interfere with existing clients. The additions to the `NodeParams` are optional, so there is no error if a client does not send them. Old clients will not send `epoch_uid` by accident so they cannot accidentally clear their set of known nodes. In the response the status field is optional so if it is missing (e.g. if a new client talks to an old server) the connection still works. For old clients speaking to new servers, the json parsing will simply ignore extra information not included in the objects json spec.
|
||||
|
||||
<AccordionTemplate name={<TestingSteps/>}>
|
||||
|
||||
Made a request to the `/api/v1/unstable/nym-nodes/semi-skimmed` endpoint:
|
||||
- without entering an epoch_id
|
||||
- with entering the current epoch_id
|
||||
- with entering an old epoch_id
|
||||
- with entering a future epoch_id
|
||||
All results returned the same data, as expected
|
||||
|
||||
</AccordionTemplate>
|
||||
|
||||
- [Feature/add gbp currency](https://github.com/nymtech/nym/pull/5453)
|
||||
|
||||
- [Add helper to extract a list of sqlite files with journal files wal/shm](https://github.com/nymtech/nym/pull/5452)
|
||||
|
||||
- [Add a middleware layer to the nym api allowing for data compression](https://github.com/nymtech/nym/pull/5451): after Testing in a minimal example, this does work as expected. Routes still default to plain encoding, however if a client indicates support for a compressed encoding using the `Accept-Encoding` header then the served response will be compressed with the appropriate `Content-Encoding` header. ([Proof-of-Concept](https://gist.github.com/jmwample/c15a983e804fc338fee3d1b037d216b0))
|
||||
|
||||
<AccordionTemplate name={<TestingSteps/>}>
|
||||
Sent requests with and without Accept-Encoding: gzip to observe response behaviour
|
||||
Verified if responses included content-encoding: gzip when compression was requested
|
||||
Checked response file output to confirm actual compression occurred
|
||||
</AccordionTemplate>
|
||||
|
||||
- [Condense core API functionalities and enable gzip decompression for reqwest payloads](https://github.com/nymtech/nym/pull/5450): this PR is intended to take out variables from our usage of HTTP requests in the `nym-http-api-client`. To do this the PR: (1) adds the `ApiClientCore` trait with the minimal feature required to send a request, (2) turns all request sending into trait extension automatically implemented for any type that implements `ApiClientCore`. This has the benefit of consistent expected behavior for all clients using `nym-http-api-client`, including features added going forward. FOR EXAMPLE this pr adds a default header `"accept-encoding: gzip;q=1.0, *;q=0.5"` which indicates that compression is preferred whenever available. Other features to keep in mind here are things like configurable retries, domain fallbacks, etc. Note: the `Apiclient` interface could be simplified, but that would require refactoring our downstream usages of the API. For now this isn't necessary as `ApiClient` is implemented automatically so it costs nothing to have it this way. It just allows divergent usage in downstream crates.
|
||||
|
||||
<AccordionTemplate name={<TestingSteps/>}>
|
||||
Measured nym-api response times before and after updating the API client using curl
|
||||
Checked if the client automatically decompressed Gzip responses
|
||||
</AccordionTemplate>
|
||||
|
||||
- [Seedable clients](https://github.com/nymtech/nym/pull/5440): Adds `DerivationMaterial` and accompanying methods to builders. `DerivationMaterial` encapsulates parameters for deterministic key derivation using HKDF (SHA-512). Use the `derive_secret()` method to generate a 32-byte secret. To prepare for a new derivation, call the `next()` method which increments the index. **It is the caller's responsibility to track and persist the derivation index if keys need to be rederived.**
|
||||
|
||||
<AccordionTemplate name="Example">
|
||||
|
||||
```rust
|
||||
let master_key = [0u8; 32]; // your secret master key
|
||||
let salt = "unique-salt-value".to_string();
|
||||
let material = DerivationMaterial::new(master_key, 0, salt.as_bytes());
|
||||
|
||||
// Derive a secret
|
||||
let secret = material.derive_secret().expect("Failed to derive secret");
|
||||
|
||||
// Prepare for the next derivation
|
||||
let next_material = material.next();
|
||||
```
|
||||
|
||||
</AccordionTemplate>
|
||||
|
||||
- [Remove all recv_with_delay and add shutdown condition to loops in client-core](https://github.com/nymtech/nym/pull/5435): inside client-core we want to prepare the ground for moving a behaviour close to what we have in the vpn client. Remove all the recv_with_delay since we want to just stop. Add shutdown condition to all select loops to guard against the shutdown listener being polled inside the select blocks. Remove unwraps when sending on unbounded channels in case the receiver exits before the sender. Move `TaskClient` to be a member field so make it easier to wrap log errors in a shutdown check. Update all fork names to use underscore consistently, since the task separator is hyphen
|
||||
|
||||
<AccordionTemplate name={<TestingSteps/>}>
|
||||
Validated that all binaries including `nym-node`, `nym-client`, `nym-network-requester`, and `nym-socks5-client` are behaving well without indicating the presence of any unexpected errors or crashes
|
||||
</AccordionTemplate>
|
||||
|
||||
- [Disable the test for checking the remaining bandwidth in nym-node-status-api](https://github.com/nymtech/nym/pull/5425): this check fails almost every time on CI, possibly due to rate limiting? It's not good to disable the check, but it's blocking CI as it stands now. Given that we have the check above for locating the ip, we at least have a little coverage.
|
||||
|
||||
- [Dz nym node stats](https://github.com/nymtech/nym/pull/5418): removed obsolete fields from stats (blacklisted mixnodes, blacklisted gateways, bonded mixnodes, bonded gateways). Introduced nym-node scraping, beside just mixnodes. `/mixnodes/stats` now returns data for nym-nodes as well, which results in much more accurate "packets mixed" stats.
|
||||
|
||||
- [Nymnode entrypoint docker](https://github.com/nymtech/nym/pull/5300)
|
||||
|
||||
### Bugfix
|
||||
- [bugfix: dont query for ecash apis unless necessary when spending ticketbooks](https://github.com/nymtech/nym/pull/5508)
|
||||
|
||||
- [bugfix: bound check when recovering a reply SURB](https://github.com/nymtech/nym/pull/5502)
|
||||
|
||||
- [fix: update fx average rate calcs to ignore 0 values](https://github.com/nymtech/nym/pull/5454)
|
||||
|
||||
### Chore
|
||||
- [chore: workspace global panic preventing lints](https://github.com/nymtech/nym/pull/5512)
|
||||
|
||||
- [chore: removed all old coconut code](https://github.com/nymtech/nym/pull/5500)
|
||||
|
||||
- [build(deps): bump the patch-updates group across 1 directory with 3 updates](https://github.com/nymtech/nym/pull/5482): updates `clap` from 4.5.28 to 4.5.30, updates `clap` from 4.5.28 to 4.5.30, updates `prost` from 0.13.4 to 0.13.5.
|
||||
|
||||
- [build(deps): bump http from 1.1.0 to 1.2.0](https://github.com/nymtech/nym/pull/5472)
|
||||
|
||||
- [build(deps): bump utoipa-swagger-ui from 8.0.3 to 8.1.0](https://github.com/nymtech/nym/pull/5471)
|
||||
|
||||
- [build(deps): bump colored from 2.1.0 to 2.2.0](https://github.com/nymtech/nym/pull/5470)
|
||||
|
||||
- [build(deps): bump celes from 2.4.0 to 2.5.0](https://github.com/nymtech/nym/pull/5469)
|
||||
|
||||
- [build(deps): bump the patch-updates group with 2 updates](https://github.com/nymtech/nym/pull/5467): updates `clap` from 4.5.28 to 4.5.29, updates `prost` from 0.13.4 to 0.13.5.
|
||||
|
||||
- [build(deps): bump elliptic from 6.5.4 to 6.6.1 in /docker/typescript_client/upload_contract](https://github.com/nymtech/nym/pull/5463): bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.4 to 6.6.1.
|
||||
|
||||
- [build(deps): bump uniffi_build from 0.25.3 to 0.29.0](https://github.com/nymtech/nym/pull/5448)
|
||||
|
||||
- [Upgrade tower to 0.5.2](https://github.com/nymtech/nym/pull/5446)
|
||||
|
||||
- [build(deps): bump hickory-proto from 0.24.2 to 0.24.3 in /nym-wallet](https://github.com/nymtech/nym/pull/5445)
|
||||
|
||||
- [build(deps): bump hickory-proto from 0.24.2 to 0.24.3](https://github.com/nymtech/nym/pull/5444)
|
||||
|
||||
- [build(deps): bump the patch-updates group across 1 directory with 10 updates](https://github.com/nymtech/nym/pull/5439):
|
||||
Bumps the patch-updates group with 10 updates in the directory:
|
||||
|
||||
| Package | From | To |
|
||||
| --- | --- | --- |
|
||||
| [async-trait](https://github.com/dtolnay/async-trait) | `0.1.85` | `0.1.86` |
|
||||
| [clap](https://github.com/clap-rs/clap) | `4.5.27` | `4.5.28` |
|
||||
| [comfy-table](https://github.com/nukesor/comfy-table) | `7.1.3` | `7.1.4` |
|
||||
| [hickory-resolver](https://github.com/hickory-dns/hickory-dns) | `0.24.2` | `0.24.3` |
|
||||
| [once_cell](https://github.com/matklad/once_cell) | `1.20.2` | `1.20.3` |
|
||||
| [pin-project](https://github.com/taiki-e/pin-project) | `1.1.8` | `1.1.9` |
|
||||
| [serde_json_path](https://github.com/hiltontj/serde_json_path) | `0.7.1` | `0.7.2` |
|
||||
| [toml](https://github.com/toml-rs/toml) | `0.8.19` | `0.8.20` |
|
||||
| [cosmrs](https://github.com/cosmos/cosmos-rust) | `0.21.0` | `0.21.1` |
|
||||
| [tokio-postgres](https://github.com/sfackler/rust-postgres) | `0.7.12` | `0.7.13` |
|
||||
|
||||
- [build(deps): bump openssl from 0.10.56 to 0.10.70 in /nym-wallet](https://github.com/nymtech/nym/pull/5422)
|
||||
|
||||
- [build(deps): bump hyper from 1.4.1 to 1.6.0](https://github.com/nymtech/nym/pull/5416)
|
||||
|
||||
- [build(deps): bump publicsuffix from 2.2.3 to 2.3.0](https://github.com/nymtech/nym/pull/5367)
|
||||
|
||||
## `v2025.3-ruta`
|
||||
|
||||
- [Release Binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2025.3-ruta)
|
||||
@@ -97,7 +309,7 @@ As we announced in [`hu` release notes](#service-grant-program-v2), we are opene
|
||||
|
||||
##### Locations
|
||||
|
||||
These locations and slots are approximate and constantly change based on new operators submissions. Please keep in mind that we are going to chose people with respect to empty slots in the given location at the time of their submission. In case of competing submissions in the same location we will take the one with better specs vs price ratio.
|
||||
These locations and slots are approximate and constantly change based on new operators submissions. Please keep in mind that we are going to chose people with respect to empty slots in the given location at the time of their submission. In case of competing submissions in the same location we will take the one with better specs vs price ratio.
|
||||
|
||||
| LOCATION | SLOTS |
|
||||
| :-- | --: |
|
||||
@@ -151,7 +363,7 @@ These locations and slots are approximate and constantly change based on new ope
|
||||
|
||||
- [Send shutdown instead of panic when reaching max fail](https://github.com/nymtech/nym/pull/5398): Remove a panic and an unwrap inside `client-core` that is hit occasionally in the vpn client. This is a change that can have wide ranging impact since it changes the task handling and it's inside `client-core`, which is used in many components and services, so preventing regressions is important.
|
||||
|
||||
- [Relocate a validator api function](https://github.com/nymtech/nym/pull/5401): Adds a function to the `nym-validator-client` crate that hits the network details endpoint and returns an object parsed from `json`. This was floating loose in the `nym-vpn-client` repo.
|
||||
- [Relocate a validator api function](https://github.com/nymtech/nym/pull/5401): Adds a function to the `nym-validator-client` crate that hits the network details endpoint and returns an object parsed from `json`. This was floating loose in the `nym-vpn-client` repo.
|
||||
|
||||
- [Bump the patch-updates group across 1 directory with 9 updates](https://github.com/nymtech/nym/pull/5406)
|
||||
|
||||
@@ -199,7 +411,7 @@ From `nym-node v1.3.0` operators can technically choose multiple functionalities
|
||||
|
||||
- Updated maintenance guides to [backup](nodes/maintenance#backup-a-node), [restore](nodes/maintenance#restoring-a-node) and [move](nodes/maintenance#moving-a-node) a node, containing a new and important commands to backup and restore `clients.sqlite` database.
|
||||
|
||||
- [New explanation of `nym-node` functionalities](nodes/nym-node/setup#functionality-mode) describing how Gateways get selected in Mixnet mode and Wireguard mode.
|
||||
- [New explanation of `nym-node` functionalities](nodes/nym-node/setup#functionality-mode) describing how Gateways get selected in Mixnet mode and Wireguard mode.
|
||||
|
||||
<Callout type="warning">
|
||||
Wireguard nodes route data directly to the open internet. Therefore it exposes IP of operators server (VPS) to abuse complains. Before you decide to run a node with active wireguard routing, please read our [Community Counsel pages](community-counsel/exit-gateway) containing more information and some legal content.
|
||||
@@ -223,7 +435,7 @@ We have been notified that a handful of nodes have been taken down by abuse repo
|
||||
- Join [Community legal counsel](https://nym.com/docs/operators/community-counsel) - our collective knowledge hub. Add your findings by opening a [Pull Request](https://nym.com/docs/operators/add-content)
|
||||
|
||||
- While we are working on a new list of more friendly providers, consider to move away from these provides as soon as possible:
|
||||
|
||||
|
||||
- Servinga / VPS2day (AS39378)
|
||||
- Frantech / Ponynet / BuyVM (AS53667)
|
||||
- OVH SAS / OVHcloud (AS16276)
|
||||
@@ -233,7 +445,7 @@ We have been notified that a handful of nodes have been taken down by abuse repo
|
||||
- Psychz Networks (AS40676)
|
||||
- 1337 Services GmbH / RDP.sh (AS210558)
|
||||
|
||||
- Backup your nodes to have access to `.nym` directory locally. Follow [node](nodes/maintenance#backup-a-node) and [proxy configuration](nodes/maintenance#backup-proxy-configuration) backup guides to be able to [restore your node](nodes/maintenance#restoring-a-node) later on on another machine, without losing your delegation.
|
||||
- Backup your nodes to have access to `.nym` directory locally. Follow [node](nodes/maintenance#backup-a-node) and [proxy configuration](nodes/maintenance#backup-proxy-configuration) backup guides to be able to [restore your node](nodes/maintenance#restoring-a-node) later on on another machine, without losing your delegation.
|
||||
|
||||
- We would like to ask operators who use reverse proxy and a domain (required for Gateways) to start using a common convention starting with `nym-exit` for their nodes URL. The entire address should have this new format:
|
||||
```
|
||||
@@ -258,7 +470,7 @@ nym-exit.mysquad.org
|
||||
|
||||
**The `NYM-EXIT` part in the beginning is what's important.**
|
||||
|
||||
- When registering a domain, check [Top Level Domain (TLD)](https://www.techopedia.com/definition/1348/top-level-domain-tld) terms and conditions. For example `.icu` is a no go. Having a wrong TLD may lead to your domain being taken away from you when facing a DMCA report.
|
||||
- When registering a domain, check [Top Level Domain (TLD)](https://www.techopedia.com/definition/1348/top-level-domain-tld) terms and conditions. For example `.icu` is a no go. Having a wrong TLD may lead to your domain being taken away from you when facing a DMCA report.
|
||||
|
||||
- Write a message to your provider and introduce your intention to run a Nym Node on their service
|
||||
<AccordionTemplate name="Email template: Introduce yourself to your VPS provider">
|
||||
@@ -322,7 +534,7 @@ Undelegated due to high saturation:
|
||||
|
||||
#### Service Grant Program v2
|
||||
|
||||
Aside from delegating on top of nodes, Nym runs a Service Grant Program (SGP) to support Exit Gateway operators before they will be rewarded by collecting [zk-nym tickets](../network/cryptography/zk-nym) from users subscription. Operators included in SGP are long term active community members with the highest requirements on the technical setup and upgrading pace. We are about to start a second iteration of SGP very soon (SGPv2). The final slots and locations are yet to be concluded. Priority to participate in SGPv2 will be given to the current operators in SGP. Based on the number of slots, we will then determine how many more operators can sign up.
|
||||
Aside from delegating on top of nodes, Nym runs a Service Grant Program (SGP) to support Exit Gateway operators before they will be rewarded by collecting [zk-nym tickets](../network/cryptography/zk-nym) from users subscription. Operators included in SGP are long term active community members with the highest requirements on the technical setup and upgrading pace. We are about to start a second iteration of SGP very soon (SGPv2). The final slots and locations are yet to be concluded. Priority to participate in SGPv2 will be given to the current operators in SGP. Based on the number of slots, we will then determine how many more operators can sign up.
|
||||
|
||||
##### Rules of SGPv2
|
||||
|
||||
@@ -330,7 +542,7 @@ Aside from delegating on top of nodes, Nym runs a Service Grant Program (SGP) to
|
||||
**We will share more info soon in the channels. The rules are not set in stone and could potentially be altered or updated in the future! Do *not* purchase new servers neither migrate your nodes just yet.**
|
||||
</Callout>
|
||||
|
||||
As we finalising last details of *"Project Smoosh"*, where one binary - `nym-node` - can run as an `entry-gateway`, `mixnode` or `exit-gateway` in Mixnet mode as well as `entry-gateway` or `exit-gateway` in Wireguard mode, we plan to step up the game. SGPv2 grants will be higher if operators can meet new requirements.
|
||||
As we finalising last details of *"Project Smoosh"*, where one binary - `nym-node` - can run as an `entry-gateway`, `mixnode` or `exit-gateway` in Mixnet mode as well as `entry-gateway` or `exit-gateway` in Wireguard mode, we plan to step up the game. SGPv2 grants will be higher if operators can meet new requirements.
|
||||
|
||||
**Minimum Specs & Requirements**
|
||||
|
||||
@@ -402,7 +614,7 @@ These are minimum requirements to become a part of SGPv2. We aim to have nodes o
|
||||
|
||||
- [build(deps): bump criterion from `0.4.0` to `0.5.1`](https://github.com/nymtech/nym/pull/4911): Bumps [criterion](https://github.com/bheisler/criterion.rs) from `0.4.0` to `0.5.1`.
|
||||
|
||||
- [NS API: add mixnet scraper](https://github.com/nymtech/nym/pull/5200)
|
||||
- [NS API: add mixnet scraper](https://github.com/nymtech/nym/pull/5200)
|
||||
|
||||
- [build(deps): bump http from `1.1.0` to `1.2.0`](https://github.com/nymtech/nym/pull/5228): Bumps [http](https://github.com/hyperium/http) from `1.1.0` to `1.2.0`.
|
||||
|
||||
@@ -410,41 +622,41 @@ These are minimum requirements to become a part of SGPv2. We aim to have nodes o
|
||||
|
||||
- [Add windows to CI builds](https://github.com/nymtech/nym/pull/5269)
|
||||
|
||||
- [nym topology revamp](https://github.com/nymtech/nym/pull/5271): This PR changes the internals of the `NymTopology` to blur the lines between explicit mixnodes and gateways so that what used to be considered a "mixnode" could be a valid egress point of the network. `NymTopology` is no longer divided into `BTreeMap<MixLayer, Vec<mix::Node>>` and `Vec<gateway::Node>`. instead there's information about the current rewarded set (to support future VRF work) and a simple map of `HashMap<NodeId, RoutingNode>`. The new features are mostly controlled via 2 new flags/config values:
|
||||
- [nym topology revamp](https://github.com/nymtech/nym/pull/5271): This PR changes the internals of the `NymTopology` to blur the lines between explicit mixnodes and gateways so that what used to be considered a "mixnode" could be a valid egress point of the network. `NymTopology` is no longer divided into `BTreeMap<MixLayer, Vec<mix::Node>>` and `Vec<gateway::Node>`. instead there's information about the current rewarded set (to support future VRF work) and a simple map of `HashMap<NodeId, RoutingNode>`. The new features are mostly controlled via 2 new flags/config values:
|
||||
- `use_extended_topology` that tells the client to retrieve **all** network nodes rather than the ones that got assigned "active" mixnode role (or support being a gateway)
|
||||
- `ignore_egress_epoch_role` that tells the client it's fine to construct egress packets to nodes that are **not** assigned entry or exit gateway role
|
||||
|
||||
|
||||
- [Nyx Chain Watcher](https://github.com/nymtech/nym/pull/5274)
|
||||
|
||||
- [Include `IPINFO_API_TOKEN` in nightly CI](https://github.com/nymtech/nym/pull/5285)
|
||||
|
||||
- [Move tun constants to network defaults](https://github.com/nymtech/nym/pull/5286):
|
||||
- [Move tun constants to network defaults](https://github.com/nymtech/nym/pull/5286):
|
||||
<AccordionTemplate name={<TestingSteps/>}>
|
||||
1. **Regression Testing**:
|
||||
- Verified no issues arose when running tests for the affected files.
|
||||
- Tested TUN behaviour with new nym-nodes in the hu branch.
|
||||
|
||||
**Results**:
|
||||
- Verified no issues arose when running tests for the affected files.
|
||||
- Tested TUN behaviour with new nym-nodes in the hu branch.
|
||||
|
||||
- **No bugs detected**.
|
||||
- Tunnels are functioning as expected, with traffic routing and IP generation working seamlessly.
|
||||
</AccordionTemplate>
|
||||
**Results**:
|
||||
|
||||
- **No bugs detected**.
|
||||
- Tunnels are functioning as expected, with traffic routing and IP generation working seamlessly.
|
||||
</AccordionTemplate>
|
||||
|
||||
- [Add dependabot assignes for the root cargo ecosystem](https://github.com/nymtech/nym/pull/5297)
|
||||
|
||||
- [build(deps): bump the patch-updates group across 1 directory with 35 updates](https://github.com/nymtech/nym/pull/5310): Bumps the `patch-updates` group with 33 updates
|
||||
|
||||
- [Periodically remove stale gateway messages](https://github.com/nymtech/nym/pull/5312): This PR introduces a simple task that removes gateway messages that haven't been retrieved in (by default) 24h.
|
||||
- [Periodically remove stale gateway messages](https://github.com/nymtech/nym/pull/5312): This PR introduces a simple task that removes gateway messages that haven't been retrieved in (by default) 24h.
|
||||
<AccordionTemplate name={<TestingSteps/>}>
|
||||
**Automation Script for Data Cleanup Validation**
|
||||
|
||||
Test Objective: Validate that the stale message cleanup mechanism in the database correctly removes records older than the configured threshold (24 hours).
|
||||
|
||||
|
||||
Test Setup:
|
||||
1. Environment:
|
||||
* SQLite database
|
||||
* Bash script: used to insert data.
|
||||
|
||||
|
||||
Steps Performed:
|
||||
1. Ran the insert_data.sh script to populate the database with test data:
|
||||
* Recent records inserted successfully.
|
||||
@@ -452,8 +664,8 @@ Steps Performed:
|
||||
3. Confirmed that all 20 records (10 recent + 10 stale) were present.
|
||||
4. Allowed the system to run for 24 hours to trigger the cleanup mechanism.
|
||||
5. Queried the database again after 24 hours: sqlite3 gateway_storage.db "SELECT * FROM message_store;"
|
||||
6.
|
||||
|
||||
6.
|
||||
|
||||
Expected Result:
|
||||
* All stale records (older than 24 hours) should be removed.
|
||||
* Recent records should remain in the database.
|
||||
@@ -464,18 +676,18 @@ Actual Result:
|
||||
|
||||
- [Use expect in geodata test to give error message on failure](https://github.com/nymtech/nym/pull/5314): Keep hitting this error on CI, from what I think is network hickup. But it's hard to tell form the log since the error is swallowed. Explicitly unwrap the result so we get a more detailed error output.
|
||||
<AccordionTemplate name={<TestingSteps/>}>
|
||||
**Quick Code Review**
|
||||
|
||||
**Summary**
|
||||
**Quick Code Review**
|
||||
|
||||
**Summary**
|
||||
1. **CI Workflow**: Adjusted paths to optimise build triggers, avoiding unnecessary CI runs while ensuring coverage for key directories
|
||||
2. **Geolocation Test**: Improved error handling by replacing assertions with `.expect` for clearer debugging in API regression tests
|
||||
|
||||
**Conclusion**
|
||||
|
||||
**Conclusion**
|
||||
|
||||
Regression testing confirms everything works as intended. **Approved**.
|
||||
</AccordionTemplate>
|
||||
|
||||
- [`CancellationToken`-based shutdowns](https://github.com/nymtech/nym/pull/5325): This PR introduces scaffolding for using `CancellationToken` and `TaskTracker` for our graceful shutdowns rather than the existing `TaskClient` and `TaskManager`.
|
||||
- [`CancellationToken`-based shutdowns](https://github.com/nymtech/nym/pull/5325): This PR introduces scaffolding for using `CancellationToken` and `TaskTracker` for our graceful shutdowns rather than the existing `TaskClient` and `TaskManager`.
|
||||
|
||||
- [Introduce `/load` endpoint for self-reported quantised Nym Node load](https://github.com/nymtech/nym/pull/5326): This PR introduces a new `/load` endpoint on a `NymNode` to return its current load. It returns the following data:
|
||||
```rust
|
||||
@@ -505,7 +717,7 @@ The actual values for`NodeLoad` are determined as follows:
|
||||
- Thus we calculate two additional auxiliary `Load` values, for memory usage and swap usage, i.e.: `used_memory / total_memory` and `used_swap / total_swap` respectively.
|
||||
- Then we check whether either of the `MemoryLoad` or `SwapLoad` is bigger than the current base `Load` of the machine we have determined, if so, it's increased by one tier / bucket. For example, say the current machine load is `Load::Low`, but the memory usage is at 90% (`Load::VeryHigh`). that would result in the reported `Load` being bumped up to `Load::Medium` instead. The same logic applies with swap load, **however, only if the total swap > 1GB**. this is to prevent weird edge cases where the machine has hardly any swap.
|
||||
- Finally, the `.total` `Load` uses the same "tier bumping" behaviour using the `.total` and `.network` loads, i.e. `if network > machine`, then `total = machine + 1`. for example if `machine` `Load` is `Load::Low`, but `network` `Load` is `Load::Medium`, then the `total` `Load` is set to `Load::Medium` instead.
|
||||
</AccordionTemplate>
|
||||
</AccordionTemplate>
|
||||
|
||||
- [Bump the `patch-updates` group with 8 updates](https://github.com/nymtech/nym/pull/5336)
|
||||
|
||||
@@ -517,29 +729,29 @@ The actual values for`NodeLoad` are determined as follows:
|
||||
|
||||
- [Bump mikefarah/yq from `4.44.6` to `4.45.1`](https://github.com/nymtech/nym/pull/5342)
|
||||
|
||||
- [Update `indexed_db_futures`](https://github.com/nymtech/nym/pull/5347): Updates the `indexed_db_futures` dependency to stop relying on the fork.
|
||||
- [Update `indexed_db_futures`](https://github.com/nymtech/nym/pull/5347): Updates the `indexed_db_futures` dependency to stop relying on the fork.
|
||||
|
||||
- [Refresh wasm sdk](https://github.com/nymtech/nym/pull/5353): This PR refreshes the wasm clients to make them usable in the current network
|
||||
|
||||
- [Client gateway selection](https://github.com/nymtech/nym/pull/5358): Changed how gateway is selected.
|
||||
- For init the target gateway can't support mixing
|
||||
- For egress, unless `ignore_epoch_roles` is specified, the gateway can't be currently assigned to a mixing layer. But it can be standby or inactive
|
||||
|
||||
|
||||
- [Exposed `NymApiClient` method for obtaining node performance history](https://github.com/nymtech/nym/pull/5360)
|
||||
|
||||
- [Bind to `[::]` on `nym-node` for both IP versions](https://github.com/nymtech/nym/pull/5361)
|
||||
<AccordionTemplate name={<TestingSteps/>}>
|
||||
**IPv4 Configuration and Migration Testing**
|
||||
|
||||
|
||||
Testing Steps:
|
||||
- Initiated and ran a nym-node with version 1.3.1 on an IPv4-only machine, then updated it to the new 'hu', 1.4.0 version.
|
||||
- Initiated and ran a new nym-node with 'hu', 1.4.0 always on a machine with only IPv4.
|
||||
- Initiated and ran a new nym-node with 'hu', 1.4.0 on a machine with both IPv4 and IPv6 for regression testing.
|
||||
|
||||
|
||||
Results:
|
||||
- No functional issues during version updates.
|
||||
- Logged message on all versions: "no registered client for destination: ff02::2"
|
||||
|
||||
|
||||
Status: Pass
|
||||
</ AccordionTemplate>
|
||||
|
||||
@@ -554,10 +766,10 @@ Status: Pass
|
||||
- Ensured a client is able to select a node which aside from a gateway, can also act as a mixnode
|
||||
- Verified 'ignore_epoch_roles' is the default mode
|
||||
- _note; this is most likely not going to be a permanent solution_
|
||||
|
||||
|
||||
**Results:**
|
||||
- Clients and topology are behaving correctly
|
||||
|
||||
- Clients and topology are behaving correctly
|
||||
|
||||
Status: Pass
|
||||
</ AccordionTemplate>
|
||||
|
||||
@@ -612,7 +824,7 @@ We are developing a design where operators can enable multiple modes, and let th
|
||||
- Moved top level `authenticator` section to `service_providers` so that it'd live alongside NR and IPR
|
||||
- Added general `debug` section
|
||||
- Added `metrics` section
|
||||
- All documentation migrated to a new URL [nym.com/docs](https://nym.com/docs) alongside the rebranding of Nym organisation.
|
||||
- All documentation migrated to a new URL [nym.com/docs](https://nym.com/docs) alongside the rebranding of Nym organisation.
|
||||
- Updated [network architecture diagrams](https://nym.com/docs/network/architecture)
|
||||
- New blow-by-blow mixnet [traffic flow](https://nym.com/docs/network/architecture) section
|
||||
- [Winter Nym Squad League started](https://forum.nym.com/t/nym-squad-league-farewell-fall-welcome-winter/977)
|
||||
@@ -648,11 +860,11 @@ We are developing a design where operators can enable multiple modes, and let th
|
||||
- Everything in `mixnode` directory has been removed because there was nothing really left there. The mixing socket listener was unified in `nym-node` and similarly `verloc` was also moved there
|
||||
- `gateway` directory was similarly reduced in size. Now it also creates appropriate tasks as opposed to the whole gateway process. eventually it might also be further stripped, but today is not that day.
|
||||
- Removed the generic parameter on the `GatewayStorage` to simplify all the generics down the stack. it wasn't used anyway
|
||||
|
||||
|
||||
CLI:
|
||||
- Added `--modes` argument to specify all node modes with a single command (or `env` variable). for example: `--modes="mixnode,entry"`. Can't be used alongside `--modes`
|
||||
- Extended `--mode` argument to allow specifying it multiple times, for example: `--mode mixnode --mode entry`. can't be used alongside `--mode`
|
||||
|
||||
|
||||
Config changes:
|
||||
- Replaced `mode` with `modes` to allow setting the node to run with say, `entry` + `mixnode` roles simultaneously
|
||||
- Added `maximum_forward_packet_delay` to `mixnet.debug` section
|
||||
@@ -677,17 +889,17 @@ Config changes:
|
||||
- [Remove unneeded async function annotation](https://github.com/nymtech/nym/pull/5246)
|
||||
- [Add control messages to `GatewayTransciver`](https://github.com/nymtech/nym/pull/5247)
|
||||
<AccordionTemplate name={<TestingSteps/>}>
|
||||
**Review and Testing: Forget Me Implementation**
|
||||
**Review and Testing: Forget Me Implementation**
|
||||
|
||||
- Validated the encryption and delivery of `ForgetMe` control messages to the gateway
|
||||
|
||||
**Testing: MixTrafficController Integration**
|
||||
- Validated the encryption and delivery of `ForgetMe` control messages to the gateway
|
||||
|
||||
- Verified that the `MixTrafficController` invokes `ForgetMe` logic correctly during shutdown
|
||||
- Tested behaviour for gateway transceiver failures while sending control messages
|
||||
|
||||
**Testing: Gateway Storage Updates**
|
||||
- Confirmed successful deletion of client data (e.g., inbox messages, bandwidth allocations) from persistent storage
|
||||
**Testing: MixTrafficController Integration**
|
||||
|
||||
- Verified that the `MixTrafficController` invokes `ForgetMe` logic correctly during shutdown
|
||||
- Tested behaviour for gateway transceiver failures while sending control messages
|
||||
|
||||
**Testing: Gateway Storage Updates**
|
||||
- Confirmed successful deletion of client data (e.g., inbox messages, bandwidth allocations) from persistent storage
|
||||
</AccordionTemplate>
|
||||
|
||||
- [Add conversion unit tests for auth msg](https://github.com/nymtech/nym/pull/5251)
|
||||
@@ -704,13 +916,13 @@ Config changes:
|
||||
<AccordionTemplate name={<TestingSteps/>}>
|
||||
1. **Review File: `common/credential-storage/src/backends/sqlite.rs`**
|
||||
- Verified addition of `close` method for the SQLite backend
|
||||
|
||||
|
||||
2. **Review File: `common/credential-storage/src/ephemeral_storage.rs`**
|
||||
- Confirmed addition of `close` method for ephemeral storage with no action required
|
||||
|
||||
|
||||
3. **Review File: `common/credential-storage/src/persistent_storage/mod.rs`**
|
||||
- Ensured `close` method integration for persistent storage
|
||||
|
||||
|
||||
4. **Review File: `common/credential-storage/src/storage.rs`**
|
||||
- Verified updates to the `Storage` trait to include `close` and `cleanup_expired` methods
|
||||
</AccordionTemplate>
|
||||
@@ -719,15 +931,15 @@ Config changes:
|
||||
<AccordionTemplate name={<TestingSteps/>}>
|
||||
1. **Review File: `common/network-defaults/src/constants.rs`**
|
||||
- Confirmed updated `mixnet_vpn` constants were added.
|
||||
|
||||
|
||||
2. **Review File: `service-providers/ip-packet-router/src/constants.rs`**
|
||||
- Checked replacement of legacy `TUN_*` constants with new `mixnet_vpn` constants.
|
||||
- Validated alignment of routing traffic configurations.
|
||||
|
||||
|
||||
3. **Review File: `service-providers/ip-packet-router/src/ip_packet_router.rs`**
|
||||
- Ensured new `nym_network_defaults::constants::mixnet_vpn` constants replaced old references.
|
||||
- Verified `TunDeviceConfig` consistency.
|
||||
|
||||
|
||||
4. **Review File: `service-providers/ip-packet-router/src/util/generate_new_ip.rs`**
|
||||
- Confirmed substitution of `TUN_DEVICE_*` constants with `NYM_TUN_DEVICE_*` constants.
|
||||
- Tested functionality for generating random IPs within subnet.
|
||||
@@ -743,54 +955,54 @@ Config changes:
|
||||
- `nym_node_mixnet_ingress_excessive_delay_packets`
|
||||
- `nym_node_mixnet_ingress_forward_hop_packets_dropped`
|
||||
- `nym_node_mixnet_ingress_final_hop_packets_dropped`
|
||||
|
||||
|
||||
- `nym_node_mixnet_ingress_forward_hop_packets_received_rate`
|
||||
- `nym_node_mixnet_ingress_final_hop_packets_received_rate`
|
||||
- `nym_node_mixnet_ingress_malformed_packets_received_rate`
|
||||
- `nym_node_mixnet_ingress_excessive_delay_packets_rate`
|
||||
- `nym_node_mixnet_ingress_final_hop_packets_received_rate`
|
||||
- `nym_node_mixnet_ingress_malformed_packets_received_rate`
|
||||
- `nym_node_mixnet_ingress_excessive_delay_packets_rate`
|
||||
- `nym_node_mixnet_ingress_forward_hop_packets_dropped_rate`
|
||||
- `nym_node_mixnet_ingress_final_hop_packets_dropped_rate`
|
||||
|
||||
|
||||
- egress:
|
||||
- `nym_node_mixnet_egress_stored_on_disk_final_hop_packets`
|
||||
- `nym_node_mixnet_egress_forward_hop_packets_sent`
|
||||
- `nym_node_mixnet_egress_ack_packets_sent`
|
||||
- `nym_node_mixnet_egress_forward_hop_packets_dropped`
|
||||
|
||||
|
||||
- `nym_node_mixnet_egress_forward_hop_packets_sent_rate`
|
||||
- `nym_node_mixnet_egress_ack_packets_sent_rate`
|
||||
- `nym_node_mixnet_egress_forward_hop_packets_dropped_rate`
|
||||
|
||||
|
||||
- client sessions
|
||||
- `nym_node_entry_client_sessions_unique_users`
|
||||
- `nym_node_entry_client_sessions_sessions_started`
|
||||
- `nym_node_entry_client_sessions_finished_sessions`
|
||||
- `nym_node_entry_client_sessions_durations_{TYP}` (histogram), for example `nym_node_entry_client_sessions_durations_vpn`
|
||||
|
||||
|
||||
- wireguard:
|
||||
- `nym_node_wireguard_bytes_rx`
|
||||
- `nym_node_wireguard_bytes_tx`
|
||||
- `nym_node_wireguard_bytes_total_peers`
|
||||
- `nym_node_wireguard_bytes_active_peers`
|
||||
|
||||
|
||||
- `nym_node_wireguard_bytes_rx_rate`
|
||||
- `nym_node_wireguard_bytes_tx_rate`
|
||||
|
||||
|
||||
|
||||
|
||||
- network
|
||||
- `nym_node_network_active_ingress_mixnet_connections`
|
||||
- `nym_node_network_active_ingress_web_socket_connections`
|
||||
- `nym_node_network_active_egress_mixnet_connections`
|
||||
|
||||
|
||||
- process
|
||||
- `nym_node_process_forward_hop_packets_being_delayed`
|
||||
- `nym_node_process_packet_forwarder_queue_size`
|
||||
- `nym_node_process_topology_query_resolution_latency` (histogram)
|
||||
- `nym_node_process_final_hop_packets_pending_delivery`
|
||||
- `nym_node_process_forward_hop_packets_pending_delivery`
|
||||
- `nym_node_process_forward_hop_packets_pending_delivery`
|
||||
</AccordionTemplate>
|
||||
|
||||
- [Amend 250gb limit](https://github.com/nymtech/nym/pull/5313): Change bandwidth cap to 250gb
|
||||
- [Amend 250gb limit](https://github.com/nymtech/nym/pull/5313): Change bandwidth cap to 250gb
|
||||
- [Warn users if node is run in exit mode only](https://github.com/nymtech/nym/pull/5320): Throws a warning if node is run in "exit" mode only as by default, this will **NOT** enable entry capabilities, i.e. opening the websocket. thus making it ineligible for rewarded set selection (and rewards)
|
||||
- [Reduce log severity for number of packets being delayed](https://github.com/nymtech/nym/pull/5321)
|
||||
- [Apply 1.84 linter suggestions](https://github.com/nymtech/nym/pull/5330)
|
||||
@@ -806,7 +1018,7 @@ Config changes:
|
||||
- [Make sure to apply gateway score filtering when choosing initial node](https://github.com/nymtech/nym/pull/5256)
|
||||
- [Fixed client session histogram buckets](https://github.com/nymtech/nym/pull/5316)
|
||||
- [Contract version assignment](https://github.com/nymtech/nym/pull/5318): This PR fixes updates to current nym-node version as well as introduces migration to fix the existing state of the mainnet contract
|
||||
- [Make sure refresh data key matches bond info](https://github.com/nymtech/nym/pull/5329): This is to forbid operators from reusing the same underlying identity key for multiple nodes by overwriting the describe data. the reported key has to always match what the node has bonded with (and the contract enforces uniqueness)
|
||||
- [Make sure refresh data key matches bond info](https://github.com/nymtech/nym/pull/5329): This is to forbid operators from reusing the same underlying identity key for multiple nodes by overwriting the describe data. the reported key has to always match what the node has bonded with (and the contract enforces uniqueness)
|
||||
|
||||
## Archived Changelog
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ Yes, there are..
|
||||
|
||||
**Built by Nym**
|
||||
|
||||
* [Nym Explorer](https://explorer.nymtech.net/)
|
||||
* [Nym Explorer](https://nym.com/explorer)
|
||||
* [Sandbox testnet](https://sandbox-explorer.nymtech.net/)
|
||||
* [Nym Harbourmaster](https://harbourmaster.nymtech.net)
|
||||
|
||||
|
||||
@@ -128,10 +128,15 @@ mkdir -pv <PATH_TO_TARGET_DIRECTORY>
|
||||
|
||||
###### 2. Backup `clients.sqlite` database
|
||||
|
||||
|
||||
- Install `sqlite3`
|
||||
```sh
|
||||
apt install sqlite3
|
||||
```
|
||||
- **Stop your node**
|
||||
```sh
|
||||
service nym-node stop
|
||||
```
|
||||
- Open sqlite CLI shell inside `clients.sqlite` database
|
||||
```sh
|
||||
sqlite3 ~/.nym/nym-nodes/default-nym-node/data/clients.sqlite
|
||||
@@ -151,10 +156,14 @@ sqlite3 ~/.nym/nym-nodes/default-nym-node/data/clients.sqlite
|
||||
scp -r <SOURCE_USER_NAME>@<SOURCE_HOST_ADDRESS>:~/.nym/nym-nodes/<ID> <PATH_TO_TARGET_DIRECTORY>
|
||||
```
|
||||
|
||||
###### 4. Verify the success of the backup
|
||||
###### 4. Verify the success of the backup & start your node
|
||||
|
||||
The `scp` command should print logs, an operator can see directly whether it was successful or if it encountered any error. However, double check that all your needed configuration is in the backup target directory.
|
||||
|
||||
- Start your node
|
||||
```sh
|
||||
service nym-node start && journalctl -u nym-node -f
|
||||
```
|
||||
</Steps>
|
||||
|
||||
Now you have everything needed to restore your `nym-node` on another server. If you are in a need of doing so, follow the steps in [*Restoring a node*](#restoring-a-node) chapter below.
|
||||
@@ -360,7 +369,7 @@ mkdir ~/.nym/nym-nodes
|
||||
```
|
||||
###### 2. Backup `clients.sqlite` database
|
||||
|
||||
- Stop your node
|
||||
- **Stop your node**
|
||||
```sh
|
||||
service nym-node stop
|
||||
```
|
||||
|
||||
@@ -259,7 +259,7 @@ That can be done only on an IP+destination port basis, however. Common P2P ports
|
||||
|
||||
<p>
|
||||
You also have the option of blocking this IP address and others on the Nym network if you so desire.
|
||||
The Nym project provides a <a href="https://explorer.nymtech.net/network-components/gateways">
|
||||
The Nym project provides a <a href="https://nym.com/explorer">
|
||||
web service</a> to fetch a list of all IP addresses of Nym Gateway Exit nodes that allow exiting to a
|
||||
specified IP:port combination. Please be considerate when using these options.</p>
|
||||
|
||||
|
||||
@@ -20,12 +20,12 @@ This documentation page provides a guide on how to set up and run a [NYM NODE](.
|
||||
```sh
|
||||
nym-node
|
||||
Binary Name: nym-node
|
||||
Build Timestamp: 2025-02-13T11:49:34.670488195Z
|
||||
Build Version: 1.5.0
|
||||
Commit SHA: a3e19b4563843055b305ea9a397eb1ad84b5c378
|
||||
Commit Date: 2025-02-10T18:14:47.000000000+01:00
|
||||
Build Timestamp: 2025-03-06T20:32:36.922212778Z
|
||||
Build Version: 1.6.2
|
||||
Commit SHA: 247ebb7c4339de0a298a7fcb2574122a8306c3b8
|
||||
Commit Date: 2025-03-06T21:26:16.000000000+01:00
|
||||
Commit Branch: HEAD
|
||||
rustc Version: 1.84.1
|
||||
rustc Version: 1.85.0
|
||||
rustc Channel: stable
|
||||
cargo Profile: release
|
||||
```
|
||||
@@ -44,7 +44,7 @@ From `nym-node v1.3.0` operators can choose multiple functionalities for their `
|
||||
|
||||
### Mixnet Routing
|
||||
|
||||
***Mixnet mode (5-hop) is the full anonymising option of NymVPN. Read more about the Mixnet architecture [here](http://localhost:3000/docs/network/architecture)***
|
||||
***Mixnet mode (5-hop) is the full anonymising option of NymVPN. Read more about the Mixnet architecture [here](../../../network/architecture)***
|
||||
|
||||
Nym Node has three functionalities in the Mixnet: `entry-gateway`, `mixnode` and `exit-gateway`. These are selected with a flag `--mode <MODE>` alongside `nym-node` command `run` .
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Nym Node operators running Gateway functionality are already familiar with the monitoring tool [Harbourmaster.nymtech.net](https://harbourmaster.nymtech.net). Under the hood of Nym Harbourmaster runs iterations of `nym-gateway-probe` doing various checks and displaying the results on the interface. Operators don't have to rely on the probe ran by Nym and wait for the data to refresh. With `nym-gateway-probe` everyone can check any Gateway's networking status from their own computer at any time. In one command the client queries data from:
|
||||
|
||||
- [`nym-api`](https://validator.nymtech.net/api/v1/gateways)
|
||||
- [`explorer-api`](https://explorer.nymtech.net/api/v1/gateways)
|
||||
- [`explorer-api`](https://mainnet-node-status-api.nymtech.cc/swagger/#/Gateways)
|
||||
- [`harbour-master`](https://harbourmaster.nymtech.net/)
|
||||
|
||||
|
||||
@@ -88,4 +88,3 @@ For any `nym-node --mode exit-gateway` the aim is to have this outcome:
|
||||
**If your Gateway is blacklisted, the probe will not work.**
|
||||
|
||||
If you don't provide a `--gateway` flag it will pick a random one to test.
|
||||
|
||||
|
||||
@@ -3,8 +3,9 @@ import { VarInfo } from 'components/variable-info.tsx';
|
||||
import { Steps } from 'nextra/components';import { Tabs } from 'nextra/components';
|
||||
import { MyTab } from 'components/generic-tabs.tsx';
|
||||
import PortsNymNode from 'components/operators/snippets/ports-nym-node.mdx';
|
||||
import PortsValidator from 'components/operators/snippets/ports-validator.mdx'
|
||||
import NymNodeSpecs from 'components/operators/snippets/nym-node-specs.mdx'
|
||||
import PortsValidator from 'components/operators/snippets/ports-validator.mdx';
|
||||
import NymNodeSpecs from 'components/operators/snippets/nym-node-specs.mdx';
|
||||
import NTPSync from 'components/operators/snippets/ntp-time-sync.mdx'
|
||||
|
||||
# VPS Setup & Configuration
|
||||
|
||||
@@ -42,14 +43,18 @@ apt update -y && apt --fix-broken install
|
||||
```
|
||||
- Install dependencies
|
||||
```sh
|
||||
apt -y install ca-certificates jq curl wget ufw jq tmux pkg-config build-essential libssl-dev git
|
||||
apt -y install ca-certificates jq curl wget ufw jq tmux pkg-config build-essential libssl-dev git ntp ntpdate
|
||||
```
|
||||
- Double check ufw is installed correctly
|
||||
```sh
|
||||
apt install ufw --fix-missing
|
||||
```
|
||||
|
||||
###### 2. Configure your firewall using Uncomplicated Firewall (UFW)
|
||||
###### 2. Synchronize time of your server
|
||||
|
||||
<NTPSync />
|
||||
|
||||
###### 3. Configure your firewall using Uncomplicated Firewall (UFW)
|
||||
|
||||
For a `nym-node` or Nyx validator to recieve traffic, you need to open ports on the server. The following commands will allow you to set up a firewall using `ufw`.
|
||||
|
||||
@@ -70,7 +75,7 @@ ufw enable
|
||||
ufw status
|
||||
```
|
||||
|
||||
###### 3. Open all needed ports to have your firewall for `nym-node` working correctly
|
||||
###### 4. Open all needed ports to have your firewall for `nym-node` working correctly
|
||||
|
||||
<div>
|
||||
<Tabs items={[
|
||||
|
||||
@@ -360,7 +360,7 @@ $1 \ NYM = 1 \_ 000 \_ 000 \ uNYM$
|
||||
|
||||
The Reward Estimation API endpoint allows Mix Node operators to estimate the rewards they could earn for running a Nym Mix Node with a specific `MIX_ID`.
|
||||
|
||||
> The `<MIX_ID>` can be found in the "Mix ID" column of the [Network Explorer](https://explorer.nymtech.net/network-components/mixnodes/active).
|
||||
> The `<MIX_ID>` can be found in the "Mix ID" column of the [Network Explorer](https://nym.com/explorer).
|
||||
|
||||
The endpoint is a particularly common for Mix Node operators as it can provide an estimate of potential earnings based on factors such as the amount of traffic routed through the Mix Node, the quality of the Mix Node's performance, and the overall demand for Mix Nodes in the network. This information can be useful for Mix Node operators in deciding whether or not to run a Mix Node and in optimizing its operations for maximum profitability.
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ The directory structure for each node will be roughly as follows:
|
||||
```
|
||||
|
||||
<Callout>
|
||||
If you `cat` the `public_sphinx.pem` key, the output will be different from the public key you will see on Nym [dashboard](https://explorer.nymtech.net/). The reason for this is that `.pem` files are encoded in **base64**, however on the web they are in **base58**. Don't be confused if your keys look different. They are the same keys, just with different encoding.
|
||||
If you `cat` the `public_sphinx.pem` key, the output will be different from the public key you will see on Nym [dashboard](https://nym.com/explorer). The reason for this is that `.pem` files are encoded in **base64**, however on the web they are in **base58**. Don't be confused if your keys look different. They are the same keys, just with different encoding.
|
||||
</Callout>
|
||||
|
||||
### Accidentally killing your node process on exiting session
|
||||
@@ -101,7 +101,7 @@ You don't have to do any additional configuration for your node to implement thi
|
||||
### How can I tell my node is up and running and mixing traffic?
|
||||
|
||||
First of all check the 'Mixnodes' section of either of the Nym Network Explorers:
|
||||
* [Mainnet](https://explorer.nymtech.net/)
|
||||
* [Mainnet](https://nym.com/explorer)
|
||||
* [Sandbox testnet](https://sandbox-explorer.nymtech.net/)
|
||||
|
||||
You can also check [Nym Harbourmaster](https://harbourmaster.nymtech.net) which now also includes mixnode mode.
|
||||
|
||||
|
After Width: | Height: | Size: 136 KiB |
@@ -1,7 +1,8 @@
|
||||
import React from "react";
|
||||
import { DocsThemeConfig, useConfig } from "nextra-theme-docs";
|
||||
import { Footer } from "./components/footer";
|
||||
// import { Footer } from "./components/footer";
|
||||
import { Matrix } from "./components/matrix-link";
|
||||
import { Explorer } from "./components/explorer-link";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
const config: DocsThemeConfig = {
|
||||
@@ -66,9 +67,9 @@ const config: DocsThemeConfig = {
|
||||
},
|
||||
docsRepositoryBase:
|
||||
"https://github.com/nymtech/nym/tree/develop/documentation/docs/",
|
||||
footer: {
|
||||
text: Footer,
|
||||
},
|
||||
// footer: {
|
||||
// text: Footer,
|
||||
// },
|
||||
darkMode: true,
|
||||
nextThemes: {
|
||||
defaultTheme: "dark",
|
||||
@@ -77,8 +78,14 @@ const config: DocsThemeConfig = {
|
||||
defaultMenuCollapseLevel: 1,
|
||||
autoCollapse: true,
|
||||
},
|
||||
|
||||
navbar: {
|
||||
extraContent: <Matrix />,
|
||||
extraContent: (
|
||||
<>
|
||||
<Explorer />
|
||||
<Matrix />
|
||||
</>
|
||||
),
|
||||
},
|
||||
toc: {
|
||||
float: false,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "explorer-api"
|
||||
version = "1.1.47"
|
||||
version = "1.1.48"
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": ["next/core-web-vitals", "next/typescript"]
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/versions
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# env files (can opt-in for committing if needed)
|
||||
.env*
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
@@ -0,0 +1,36 @@
|
||||
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
# or
|
||||
bun dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
|
||||
"vcs": {
|
||||
"enabled": false,
|
||||
"clientKind": "git",
|
||||
"useIgnoreFile": false
|
||||
},
|
||||
"files": {
|
||||
"ignoreUnknown": false,
|
||||
"ignore": ["node_modules", ".next", "public"]
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
"indentStyle": "space"
|
||||
},
|
||||
"organizeImports": {
|
||||
"enabled": true
|
||||
},
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
"recommended": true,
|
||||
"suspicious": {
|
||||
"noExplicitAny": "warn"
|
||||
}
|
||||
}
|
||||
},
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"quoteStyle": "double"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
reactStrictMode: true,
|
||||
|
||||
basePath: "/explorer",
|
||||
assetPrefix: "/explorer",
|
||||
trailingSlash: false,
|
||||
|
||||
async redirects() {
|
||||
return [
|
||||
// Change the basePath to /explorer
|
||||
{
|
||||
source: "/",
|
||||
destination: "/explorer",
|
||||
basePath: false,
|
||||
permanent: true,
|
||||
},
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"name": "@nymproject/explorer-v2",
|
||||
"version": "0.1.0",
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"build:prod": "yarn --cwd .. build && next build",
|
||||
"start": "next start",
|
||||
"lint": "biome check --fix"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@chain-registry/types": "^0.50.36",
|
||||
"@cosmos-kit/keplr-extension": "^2.14.0",
|
||||
"@cosmos-kit/react": "^2.20.1",
|
||||
"@emotion/cache": "^11.13.5",
|
||||
"@emotion/react": "^11.13.5",
|
||||
"@emotion/styled": "^11.13.5",
|
||||
"@interchain-ui/react": "^1.26.1",
|
||||
"@mui/icons-material": "^5.16.11",
|
||||
"@mui/material": "^6.1.10",
|
||||
"@mui/material-nextjs": "^6.1.9",
|
||||
"@mui/x-date-pickers": "^7.23.2",
|
||||
"@nivo/line": "^0.88.0",
|
||||
"@nymproject/contract-clients": "^1.4.1",
|
||||
"@nymproject/react": "1.0.0",
|
||||
"@tanstack/react-query": "^5.64.2",
|
||||
"@tanstack/react-query-devtools": "^5.64.2",
|
||||
"@tanstack/react-query-next-experimental": "^5.66.0",
|
||||
"@tanstack/react-table": "^8.20.6",
|
||||
"@types/qs": "^6.9.18",
|
||||
"@uidotdev/usehooks": "^2.4.1",
|
||||
"chain-registry": "^1.69.64",
|
||||
"cldr-compact-number": "^0.4.0",
|
||||
"date-fns": "^4.1.0",
|
||||
"i18next": "^24.2.2",
|
||||
"i18next-resources-to-backend": "^1.2.1",
|
||||
"isomorphic-dompurify": "^2.21.0",
|
||||
"material-react-table": "^3.0.3",
|
||||
"next": "^15.2.0",
|
||||
"openapi-fetch": "^0.13.4",
|
||||
"qrcode.react": "^4.1.0",
|
||||
"qs": "^6.14.0",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"react-i18next": "^15.4.0",
|
||||
"react-markdown": "^9.0.3",
|
||||
"react-random-avatars": "^1.3.1",
|
||||
"react-world-flags": "^1.6.0",
|
||||
"zod": "^3.24.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "15.0.3",
|
||||
"lefthook": "^1.8.5",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 391 B |
@@ -0,0 +1 @@
|
||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,61 @@
|
||||
<svg
|
||||
width="33"
|
||||
height="32"
|
||||
viewBox="0 0 33 32"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect
|
||||
width="6.4"
|
||||
height="6.38019"
|
||||
transform="matrix(1 1.74846e-07 1.74846e-07 -1 7.06641 25.5204)"
|
||||
fill="#242B2D"
|
||||
style="
|
||||
fill: #242b2d;
|
||||
fill: color(display-p3 0.1412 0.1686 0.1765);
|
||||
fill-opacity: 1;
|
||||
"
|
||||
/>
|
||||
<rect
|
||||
width="6.4"
|
||||
height="6.38019"
|
||||
transform="matrix(1 1.74846e-07 1.74846e-07 -1 0.666504 31.9006)"
|
||||
fill="#242B2D"
|
||||
style="
|
||||
fill: #242b2d;
|
||||
fill: color(display-p3 0.1412 0.1686 0.1765);
|
||||
fill-opacity: 1;
|
||||
"
|
||||
/>
|
||||
<rect
|
||||
width="6.4"
|
||||
height="6.38019"
|
||||
transform="matrix(1 1.74846e-07 1.74846e-07 -1 13.4663 19.1406)"
|
||||
fill="#242B2D"
|
||||
style="
|
||||
fill: #242b2d;
|
||||
fill: color(display-p3 0.1412 0.1686 0.1765);
|
||||
fill-opacity: 1;
|
||||
"
|
||||
/>
|
||||
<rect
|
||||
width="6.4"
|
||||
height="6.38019"
|
||||
transform="matrix(1 1.74846e-07 1.74846e-07 -1 19.8667 12.761)"
|
||||
fill="#242B2D"
|
||||
style="
|
||||
fill: #242b2d;
|
||||
fill: color(display-p3 0.1412 0.1686 0.1765);
|
||||
fill-opacity: 1;
|
||||
"
|
||||
/>
|
||||
<path
|
||||
d="M32.6663 32L26.2663 32L26.2663 6.38018L0.708989 6.38017L0.70899 -1.52588e-05L24.9863 -1.1014e-05C29.2278 -1.02724e-05 32.6663 3.43845 32.6663 7.68L32.6663 32Z"
|
||||
fill="#242B2D"
|
||||
style="
|
||||
fill: #242b2d;
|
||||
fill: color(display-p3 0.1412 0.1686 0.1765);
|
||||
fill-opacity: 1;
|
||||
"
|
||||
/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,7 @@
|
||||
<svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect y="15" width="3" height="3" transform="rotate(-90 0 15)" fill="currentColor" />
|
||||
<rect x="3" y="12" width="3" height="3" transform="rotate(-90 3 12)" fill="currentColor" />
|
||||
<rect x="6" y="9" width="3" height="3" transform="rotate(-90 6 9)" fill="currentColor" />
|
||||
<rect x="9" y="6" width="3" height="3" transform="rotate(-90 9 6)" fill="currentColor"/>
|
||||
<path d="M0 2.19345e-05V3.00002H11.9997V15H14.9997V3.00002C14.9997 1.34317 13.6565 2.19345e-05 11.9997 2.19345e-05H0Z" fill="currentColor" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 579 B |
@@ -0,0 +1,31 @@
|
||||
<svg width="361" height="361" viewBox="0 0 361 361" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_236_24563)">
|
||||
<path d="M180.789 120.41V150.41H210.789V180.41H240.789V180.41C240.789 147.273 213.926 120.41 180.789 120.41V120.41Z" fill="#14E76F" style="fill:#14E76F;fill:color(display-p3 0.0784 0.9059 0.4353);fill-opacity:1;"/>
|
||||
<path d="M180.789 240.487L180.789 210.487L150.789 210.487L150.789 180.487L120.789 180.487V180.487C120.789 213.624 147.652 240.487 180.789 240.487V240.487Z" fill="#14E76F" style="fill:#14E76F;fill:color(display-p3 0.0784 0.9059 0.4353);fill-opacity:1;"/>
|
||||
<path d="M120.789 180.41L150.789 180.41L150.789 150.41L180.789 150.41L180.789 120.41V120.41C147.652 120.41 120.789 147.273 120.789 180.41V180.41Z" fill="#14E76F" style="fill:#14E76F;fill:color(display-p3 0.0784 0.9059 0.4353);fill-opacity:1;"/>
|
||||
<path d="M240.789 180.487L210.789 180.487L210.789 210.487L180.789 210.487L180.789 240.487V240.487C213.926 240.487 240.789 213.624 240.789 180.487V180.487Z" fill="#14E76F" style="fill:#14E76F;fill:color(display-p3 0.0784 0.9059 0.4353);fill-opacity:1;"/>
|
||||
<path d="M330.773 120.41L330.773 150.41V150.41C347.342 150.41 360.773 136.979 360.773 120.41V120.41L330.773 120.41Z" fill="#14E76F" style="fill:#14E76F;fill:color(display-p3 0.0784 0.9059 0.4353);fill-opacity:1;"/>
|
||||
<path d="M270.789 60.4102L270.789 90.4102L300.789 90.4102L300.789 60.4102L270.789 60.4102Z" fill="#14E76F" style="fill:#14E76F;fill:color(display-p3 0.0784 0.9059 0.4353);fill-opacity:1;"/>
|
||||
<path d="M240.789 30.4102L240.789 60.4102L270.789 60.4102L270.789 30.4102L240.789 30.4102Z" fill="#14E76F" style="fill:#14E76F;fill:color(display-p3 0.0784 0.9059 0.4353);fill-opacity:1;"/>
|
||||
<path d="M60.7891 30.4102L60.7891 60.4102L90.7891 60.4102L90.7891 30.4102L60.7891 30.4102Z" fill="#14E76F" style="fill:#14E76F;fill:color(display-p3 0.0784 0.9059 0.4353);fill-opacity:1;"/>
|
||||
<path d="M30.7734 60.4102L30.7734 90.4102L60.7734 90.4102L60.7734 60.4102L30.7734 60.4102Z" fill="#14E76F" style="fill:#14E76F;fill:color(display-p3 0.0784 0.9059 0.4353);fill-opacity:1;"/>
|
||||
<path d="M90.7891 0.410156L90.7891 30.4102L240.789 30.4102L240.789 0.410181L90.7891 0.410156Z" fill="#14E76F" style="fill:#14E76F;fill:color(display-p3 0.0784 0.9059 0.4353);fill-opacity:1;"/>
|
||||
<rect width="30" height="150.466" transform="matrix(-1.62921e-07 1 1 1.62921e-07 180.789 120.41)" fill="#14E76F" style="fill:#14E76F;fill:color(display-p3 0.0784 0.9059 0.4353);fill-opacity:1;"/>
|
||||
<rect width="120" height="30" transform="matrix(-1.62921e-07 1 1 1.62921e-07 330.773 0.410156)" fill="#14E76F" style="fill:#14E76F;fill:color(display-p3 0.0784 0.9059 0.4353);fill-opacity:1;"/>
|
||||
<path d="M301.023 90.4102L301.023 120.41L331.023 120.41L331.023 90.4102L301.023 90.4102Z" fill="#14E76F" style="fill:#14E76F;fill:color(display-p3 0.0784 0.9059 0.4353);fill-opacity:1;"/>
|
||||
<path d="M30.7734 240.41L30.7734 210.41V210.41C14.2049 210.41 0.77344 223.842 0.773438 240.41V240.41L30.7734 240.41Z" fill="#14E76F" style="fill:#14E76F;fill:color(display-p3 0.0784 0.9059 0.4353);fill-opacity:1;"/>
|
||||
<path d="M90.7734 300.41L90.7734 270.41L60.7734 270.41L60.7734 300.41L90.7734 300.41Z" fill="#14E76F" style="fill:#14E76F;fill:color(display-p3 0.0784 0.9059 0.4353);fill-opacity:1;"/>
|
||||
<path d="M120.773 330.41L120.773 300.41L90.7734 300.41L90.7734 330.41L120.773 330.41Z" fill="#14E76F" style="fill:#14E76F;fill:color(display-p3 0.0784 0.9059 0.4353);fill-opacity:1;"/>
|
||||
<path d="M300.773 330.41L300.773 300.41L270.773 300.41L270.773 330.41L300.773 330.41Z" fill="#14E76F" style="fill:#14E76F;fill:color(display-p3 0.0784 0.9059 0.4353);fill-opacity:1;"/>
|
||||
<path d="M330.773 300.41L330.773 270.41L300.773 270.41L300.773 300.41L330.773 300.41Z" fill="#14E76F" style="fill:#14E76F;fill:color(display-p3 0.0784 0.9059 0.4353);fill-opacity:1;"/>
|
||||
<path d="M270.773 360.41L270.773 330.41L120.773 330.41L120.773 360.41L270.773 360.41Z" fill="#14E76F" style="fill:#14E76F;fill:color(display-p3 0.0784 0.9059 0.4353);fill-opacity:1;"/>
|
||||
<rect width="30" height="150.481" transform="matrix(1.62921e-07 -1 -1 -1.62921e-07 180.789 240.41)" fill="#14E76F" style="fill:#14E76F;fill:color(display-p3 0.0784 0.9059 0.4353);fill-opacity:1;"/>
|
||||
<rect width="120" height="30" transform="matrix(1.62921e-07 -1 -1 -1.62921e-07 30.7734 360.41)" fill="#14E76F" style="fill:#14E76F;fill:color(display-p3 0.0784 0.9059 0.4353);fill-opacity:1;"/>
|
||||
<path d="M60.5391 270.41L60.5391 240.41L30.5391 240.41L30.5391 270.41L60.5391 270.41Z" fill="#14E76F" style="fill:#14E76F;fill:color(display-p3 0.0784 0.9059 0.4353);fill-opacity:1;"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_236_24563">
|
||||
<rect width="360" height="360" fill="white" style="fill:white;fill-opacity:1;" transform="translate(0.773438 0.410156)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.7 KiB |
@@ -0,0 +1,13 @@
|
||||
<svg
|
||||
width="15"
|
||||
height="15"
|
||||
viewBox="0 0 15 15"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M3 3L-4.47035e-08 3L-8.31509e-07 6L3 6L3 3Z" fill="currentColor" />
|
||||
<path d="M6 6L3 6L3 9L6 9L6 6Z" fill="currentColor" />
|
||||
<path d="M9 9L6 9L6 12L9 12L9 9Z" fill="currentColor" />
|
||||
<path d="M12 6L9 6L9 9L12 9L12 6Z" fill="currentColor" />
|
||||
<path d="M15 3L12 3L12 6L15 6L15 3Z" fill="currentColor" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 431 B |