Compare commits

..

5 Commits

Author SHA1 Message Date
Jon Häggblad d21cbc9746 Update names 2022-11-19 11:06:57 +01:00
Jon Häggblad d4d739e267 WIP 2022-11-19 10:50:06 +01:00
Jon Häggblad 433214a9d8 Fix clippy 2022-11-19 10:41:23 +01:00
Jon Häggblad fb74d243d5 client-connections: rename to LaneQueueLenghts plural 2022-11-19 10:30:49 +01:00
Jon Häggblad ee54efe54b client-core: publish lane queue lengths 2022-11-19 10:20:08 +01:00
289 changed files with 4197 additions and 8703 deletions
+7 -9
View File
@@ -29,12 +29,6 @@ jobs:
override: true
components: rustfmt, clippy
- name: Check formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
- name: Build all binaries
uses: actions-rs/cargo@v1
with:
@@ -54,6 +48,12 @@ jobs:
command: test
args: --workspace --all-features -- --ignored
- name: Check formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
- uses: actions-rs/clippy-check@v1
name: Clippy checks
with:
@@ -66,8 +66,6 @@ jobs:
command: clippy
args: --workspace -- -D warnings
# COCONUT stuff
- name: Build all binaries with coconut enabled
uses: actions-rs/cargo@v1
with:
@@ -84,4 +82,4 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: clippy
args: --all-targets --features=coconut -- -D warnings
args: --features=coconut -- -D warnings
+5 -18
View File
@@ -1,21 +1,16 @@
name: Build release of Nym smart contracts
on:
workflow_dispatch:
release:
types: [created]
defaults:
run:
working-directory: contracts
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Check the release tag starts with `nym-contracts-`
if: startsWith(github.ref, 'refs/tags/nym-contracts-') == false && github.event_name != 'workflow_dispatch'
uses: actions/github-script@v3
with:
script: |
core.setFailed('Release tag did not start with nym-contracts-...')
- name: Install Rust stable
uses: actions-rs/toolchain@v1
@@ -26,7 +21,7 @@ jobs:
components: rustfmt, clippy
- name: Build release contracts
run: make wasm
run: RUSTFLAGS='-C link-arg=-s' cargo build --release --target wasm32-unknown-unknown
- name: Upload Mixnet Contract Artifact
uses: actions/upload-artifact@v3
@@ -41,11 +36,3 @@ jobs:
name: vesting_contract.wasm
path: contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm
retention-days: 5
- name: Upload to release based on tag name
uses: softprops/action-gh-release@v1
if: github.event_name == 'release'
with:
files: |
contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm
contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm
+8 -11
View File
@@ -1,34 +1,31 @@
name: Nightly builds on latest release
on:
schedule:
- cron: '14 2 * * *'
on: workflow_dispatch
jobs:
matrix_prep:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
# creates the matrix strategy from nightly_build_matrix_includes.json
# creates the matrix strategy from nightly_build_release_matrix.json
- uses: actions/checkout@v3
- id: set-matrix
uses: JoshuaTheMiller/conditional-build-matrix@main
with:
inputFile: '.github/workflows/nightly_build_matrix_includes.json'
inputFile: '.github/workflows/nightly_build_release_matrix.json'
filter: '[?runOnEvent==`${{ github.event_name }}` || runOnEvent==`always`]'
get_release:
runs-on: ubuntu-latest
needs: matrix_prep
outputs:
output1: ${{ steps.step2.outputs.latest_release }}
output1: ${{ steps.step2.outputs.lastest_release }}
steps:
- name: Check out repository code
uses: actions/checkout@v3
- name: Fetch all branches
run: git fetch --all
- name: Set output variable to latest release branch
id: step2
run: echo "latest_release=$(git branch -r | grep -E 'release/v[0-9]+\.[0-9]+\.[0-9]+' | tail -n 1 | sed 's/ origin\///')" >> $GITHUB_OUTPUT
run: echo "latest_release=$(git branch -r | grep -E 'release/v[0-9]+\.[0-9]+\.[0-9]+' | tail -n 1 | sed 's/origin\///')" >> $GITHUB_OUTPUT
build:
needs: [get_release,matrix_prep]
strategy:
@@ -174,7 +171,7 @@ jobs:
args: --manifest-path nym-wallet/Cargo.toml --workspace --all-targets -- -D warnings
notification:
needs: [build,get_release]
needs: build
runs-on: ubuntu-latest
steps:
- name: Collect jobs status
@@ -192,7 +189,7 @@ jobs:
NYM_PROJECT_NAME: "Nym nightly build on latest release"
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "https://github.com/nymtech/nym/tree/${{needs.get_release.outputs.output1}}"
GIT_BRANCH: "${GITHUB_REF##*/}"
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
@@ -1,203 +0,0 @@
name: Nightly builds on second latest release
on:
schedule:
- cron: '24 2 * * *'
jobs:
matrix_prep:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
# creates the matrix strategy from nightly_build_matrix_includes.json
- uses: actions/checkout@v3
- id: set-matrix
uses: JoshuaTheMiller/conditional-build-matrix@main
with:
inputFile: '.github/workflows/nightly_build_matrix_includes.json'
filter: '[?runOnEvent==`${{ github.event_name }}` || runOnEvent==`always`]'
get_release:
runs-on: ubuntu-latest
needs: matrix_prep
outputs:
output1: ${{ steps.step2.outputs.latest_release }}
steps:
- name: Check out repository code
uses: actions/checkout@v3
- name: Fetch all branches
run: git fetch --all
- name: Set output variable to latest release branch
id: step2
run: echo "latest_release=$(git branch -r | grep -E 'release/v[0-9]+\.[0-9]+\.[0-9]+' | tail -n 2 | head -n 1 | sed 's/ origin\///')" >> $GITHUB_OUTPUT
build:
needs: [get_release,matrix_prep]
strategy:
matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}}
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.rust == 'nightly' || matrix.rust == 'beta' || matrix.rust == 'stable' }}
steps:
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools
if: matrix.os == 'ubuntu-latest'
- name: Check out latest release branch
uses: actions/checkout@v3
with:
ref: ${{needs.get_release.outputs.output1}}
- name: Install rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
override: true
components: rustfmt, clippy
- name: Build all binaries
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace
- name: Reclaim some disk space (because Windows is being annoying)
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: clean
- name: Run all tests
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace
- name: Reclaim some disk space (because Windows is being annoying)
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: clean
- name: Run expensive tests
if: github.ref == 'refs/heads/develop' || github.event.pull_request.base.ref == 'develop' || github.event.pull_request.base.ref == 'master'
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace --all-features -- --ignored
- name: Check formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
- name: Reclaim some disk space (because Windows is being annoying)
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: clean
- uses: actions-rs/clippy-check@v1
name: Clippy checks
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --all-features
- name: Run clippy
uses: actions-rs/cargo@v1
if: ${{ matrix.rust != 'nightly' }}
with:
command: clippy
args: --workspace --all-targets -- -D warnings
- name: Reclaim some disk space
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-latest' }}
with:
command: clean
# COCONUT stuff
- name: Build all binaries with coconut enabled
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace --features=coconut
- name: Reclaim some disk space (because Windows is being annoying)
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: clean
- name: Run all tests with coconut enabled
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace --features=coconut
- name: Reclaim some disk space (because Windows is being annoying)
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: clean
- name: Run clippy with coconut enabled
uses: actions-rs/cargo@v1
if: ${{ matrix.rust != 'nightly' }}
with:
command: clippy
args: --workspace --all-targets --features=coconut -- -D warnings
# nym-wallet (the rust part)
- name: Build nym-wallet rust code
uses: actions-rs/cargo@v1
with:
command: build
args: --manifest-path nym-wallet/Cargo.toml --workspace
- name: Run nym-wallet tests
uses: actions-rs/cargo@v1
with:
command: test
args: --manifest-path nym-wallet/Cargo.toml --workspace
- name: Check nym-wallet formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --manifest-path nym-wallet/Cargo.toml --all -- --check
- name: Run clippy for nym-wallet
uses: actions-rs/cargo@v1
if: ${{ matrix.rust != 'nightly' }}
with:
command: clippy
args: --manifest-path nym-wallet/Cargo.toml --workspace --all-targets -- -D warnings
notification:
needs: [build,get_release]
runs-on: ubuntu-latest
steps:
- name: Collect jobs status
uses: technote-space/workflow-conclusion-action@v2
- name: Check out repository code
uses: actions/checkout@v3
- name: Keybase - Node Install
if: env.WORKFLOW_CONCLUSION == 'failure'
run: npm install
working-directory: .github/workflows/support-files
- name: Keybase - Send Notification
if: env.WORKFLOW_CONCLUSION == 'failure'
env:
NYM_NOTIFICATION_KIND: nightly
NYM_PROJECT_NAME: "Nym nightly build on latest release"
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "https://github.com/nymtech/nym/tree/${{needs.get_release.outputs.output1}}"
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
KEYBASE_NYM_CHANNEL: "ci-nightly-release"
IS_SUCCESS: "${{ env.WORKFLOW_CONCLUSION == 'success' }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
@@ -0,0 +1,50 @@
[
{
"os":"ubuntu-latest",
"rust":"stable",
"runOnEvent":"workflow_dispatch"
},
{
"os":"windows-latest",
"rust":"stable",
"runOnEvent":"workflow_dispatch"
},
{
"os":"macos-latest",
"rust":"stable",
"runOnEvent":"workflow_dispatch"
},
{
"os":"ubuntu-latest",
"rust":"beta",
"runOnEvent":"workflow_dispatch"
},
{
"os":"windows-latest",
"rust":"beta",
"runOnEvent":"workflow_dispatch"
},
{
"os":"macos-latest",
"rust":"beta",
"runOnEvent":"workflow_dispatch"
},
{
"os":"ubuntu-latest",
"rust":"nightly",
"runOnEvent":"workflow_dispatch"
},
{
"os":"windows-latest",
"rust":"nightly",
"runOnEvent":"workflow_dispatch"
},
{
"os":"macos-latest",
"rust":"nightly",
"runOnEvent":"workflow_dispatch"
}
]
+16 -16
View File
@@ -41,19 +41,19 @@ jobs:
- name: Keybase - Node Install
run: npm install
working-directory: .github/workflows/support-files
- name: Keybase - Send Notification
env:
NYM_NOTIFICATION_KIND: nym-connect
NYM_PROJECT_NAME: "nym-connect"
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
NYM_CI_WWW_LOCATION: "nym-connect-${{ env.GITHUB_REF_SLUG }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "${GITHUB_REF##*/}"
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
KEYBASE_NYM_CHANNEL: "ci-nym-connect"
IS_SUCCESS: "${{ job.status == 'success' }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
# - name: Keybase - Send Notification
# env:
# NYM_NOTIFICATION_KIND: nym-connect
# NYM_PROJECT_NAME: "nym-connect"
# NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
# NYM_CI_WWW_LOCATION: "nym-connect-${{ env.GITHUB_REF_SLUG }}"
# GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
# GIT_BRANCH: "${GITHUB_REF##*/}"
# KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
# KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
# KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
# KEYBASE_NYM_CHANNEL: "ci-nym-connect"
# IS_SUCCESS: "${{ job.status == 'success' }}"
# uses: docker://keybaseio/client:stable-node
# with:
# args: .github/workflows/support-files/notifications/entry_point.sh
-28
View File
@@ -6,42 +6,14 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
### Added
- socks5-client/network-requester: add support for socks4a protocol
## [v1.1.1](https://github.com/nymtech/nym/tree/v1.1.1) (2022-11-29)
### Added
- binaries: add `-c` shortform for `--config-env-file`
- websocket-requests: add server response signalling current packet queue length in the client
- contracts: DKG contract that handles coconut key generation ([#1678][#1708][#1747])
- validator-api: generate coconut keys interactively, using DKG and multisig contracts ([#1678][#1708][#1747])
### Changed
- clients: add concept of transmission lanes to better handle multiple data streams ([#1720])
- clients,validator-api: take coconut signers from the chain instead of specifying them via CLI ([#1747])
- multisig contract: add DKG contract to the list of addresses that can create proposals ([#1747])
- socks5-client: wait closing inbound connection until data is sent, and throttle incoming data in general ([#1783])
- nym-cli: improve error reporting/handling and changed `vesting-schedule` queries to use query client instead of signing client
### Fixed
- gateway-client: fix decrypting stored messages on reconnect ([#1786])
### Fixed
- gateway-client: fix decrypting stored messages on reconnect ([#1786])
- socks5-client: fix shutting down all tasks if anyone of them panics or errors out ([#1805])
[#1678]: https://github.com/nymtech/nym/pull/1678
[#1708]: https://github.com/nymtech/nym/pull/1708
[#1720]: https://github.com/nymtech/nym/pull/1720
[#1747]: https://github.com/nymtech/nym/pull/1747
[#1783]: https://github.com/nymtech/nym/pull/1783
[#1786]: https://github.com/nymtech/nym/pull/1786
[#1805]: https://github.com/nymtech/nym/pull/1805
## [v1.1.0](https://github.com/nymtech/nym/tree/v1.1.0) (2022-11-09)
Generated
+25 -92
View File
@@ -586,7 +586,7 @@ dependencies = [
[[package]]
name = "client-core"
version = "1.1.1"
version = "1.1.0"
dependencies = [
"client-connections",
"config",
@@ -609,7 +609,6 @@ dependencies = [
"tempfile",
"thiserror",
"tokio",
"tokio-stream",
"topology",
"url",
"validator-client",
@@ -637,18 +636,6 @@ dependencies = [
"serde",
]
[[package]]
name = "coconut-dkg-common"
version = "0.1.0"
dependencies = [
"contracts-common",
"cosmwasm-std",
"cw-utils",
"multisig-contract-common",
"schemars",
"serde",
]
[[package]]
name = "coconut-interface"
version = "0.1.0"
@@ -758,9 +745,7 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
name = "contracts-common"
version = "0.1.0"
dependencies = [
"bs58",
"cosmwasm-std",
"dkg",
"schemars",
"serde",
"serde_json",
@@ -966,6 +951,7 @@ dependencies = [
"crypto",
"rand 0.7.3",
"thiserror",
"url",
"validator-api-requests",
"validator-client",
]
@@ -1244,9 +1230,9 @@ dependencies = [
[[package]]
name = "cw-utils"
version = "0.13.4"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dbaecb78c8e8abfd6b4258c7f4fbeb5c49a5e45ee4d910d3240ee8e1d714e1b"
checksum = "babd2c090f39d07ce5bf2556962305e795daa048ce20a93709eb591476e4a29e"
dependencies = [
"cosmwasm-std",
"schemars",
@@ -1254,23 +1240,11 @@ dependencies = [
"thiserror",
]
[[package]]
name = "cw2"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04cf4639517490dd36b333bbd6c4fbd92e325fd0acf4683b41753bc5eb63bfc1"
dependencies = [
"cosmwasm-std",
"cw-storage-plus",
"schemars",
"serde",
]
[[package]]
name = "cw3"
version = "0.13.4"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe19462a7f644ba60c19d3443cb90d00c50d9b6b3b0a3a7fca93df8261af979b"
checksum = "f871854338a54c7bb094d16ffe17212b93b146d9659dbce4c9402a9b77e240ef"
dependencies = [
"cosmwasm-std",
"cw-utils",
@@ -1278,27 +1252,11 @@ dependencies = [
"serde",
]
[[package]]
name = "cw3-fixed-multisig"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df54aa54c13f405ec4ab36b6217538bc957d439eee58f89312db05a79caf6706"
dependencies = [
"cosmwasm-std",
"cw-storage-plus",
"cw-utils",
"cw2",
"cw3",
"schemars",
"serde",
"thiserror",
]
[[package]]
name = "cw4"
version = "0.13.4"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0acc3549d5ce11c6901b3a676f2e2628684722197054d97cd0101ea174ed5cbd"
checksum = "c4476d6a7c13c46ed9ff260bd0e1cf648dc37b13f483822e1ff2a431f0f6ee52"
dependencies = [
"cosmwasm-std",
"cw-storage-plus",
@@ -1453,7 +1411,6 @@ dependencies = [
"ff 0.11.0",
"group 0.11.0",
"lazy_static",
"pemstore",
"rand 0.8.5",
"rand_chacha 0.3.1",
"rand_core 0.6.3",
@@ -1505,9 +1462,9 @@ dependencies = [
[[package]]
name = "ed25519"
version = "1.5.2"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e9c280362032ea4203659fc489832d0204ef09f247a0506f170dafcac08c369"
checksum = "3d5c4b5e5959dc2c2b89918d8e2cc40fcdd623cef026ed09d2f0ee05199dc8e4"
dependencies = [
"serde",
"signature",
@@ -1634,7 +1591,7 @@ dependencies = [
[[package]]
name = "explorer-api"
version = "1.1.1"
version = "1.1.0"
dependencies = [
"chrono",
"clap 3.2.8",
@@ -2982,7 +2939,6 @@ dependencies = [
"cosmwasm-std",
"cw-utils",
"cw3",
"cw3-fixed-multisig",
"cw4",
"schemars",
"serde",
@@ -3123,7 +3079,7 @@ dependencies = [
[[package]]
name = "nym-cli"
version = "1.1.1"
version = "1.1.0"
dependencies = [
"anyhow",
"base64",
@@ -3140,7 +3096,6 @@ dependencies = [
"pretty_env_logger",
"serde",
"serde_json",
"tap",
"tokio",
"validator-client",
]
@@ -3166,7 +3121,6 @@ dependencies = [
"rand 0.6.5",
"serde",
"serde_json",
"tap",
"thiserror",
"time 0.3.14",
"toml",
@@ -3177,7 +3131,7 @@ dependencies = [
[[package]]
name = "nym-client"
version = "1.1.1"
version = "1.1.0"
dependencies = [
"clap 3.2.8",
"client-connections",
@@ -3202,7 +3156,6 @@ dependencies = [
"serde",
"serde_json",
"sled",
"tap",
"task",
"thiserror",
"tokio",
@@ -3217,7 +3170,7 @@ dependencies = [
[[package]]
name = "nym-gateway"
version = "1.1.1"
version = "1.1.0"
dependencies = [
"anyhow",
"async-trait",
@@ -3264,7 +3217,7 @@ dependencies = [
[[package]]
name = "nym-mixnode"
version = "1.1.1"
version = "1.1.0"
dependencies = [
"anyhow",
"bs58",
@@ -3306,7 +3259,7 @@ dependencies = [
[[package]]
name = "nym-network-requester"
version = "1.1.1"
version = "1.1.0"
dependencies = [
"async-trait",
"clap 3.2.8",
@@ -3352,18 +3305,9 @@ dependencies = [
"tokio",
]
[[package]]
name = "nym-sdk"
version = "0.1.0"
dependencies = [
"client-core",
"rand 0.7.3",
"tokio",
]
[[package]]
name = "nym-socks5-client"
version = "1.1.1"
version = "1.1.0"
dependencies = [
"clap 3.2.8",
"client-connections",
@@ -3391,7 +3335,6 @@ dependencies = [
"serde",
"snafu 0.6.10",
"socks5-requests",
"tap",
"task",
"thiserror",
"tokio",
@@ -3430,15 +3373,13 @@ dependencies = [
[[package]]
name = "nym-validator-api"
version = "1.1.1"
version = "1.1.0"
dependencies = [
"anyhow",
"async-trait",
"bs58",
"cfg-if 1.0.0",
"clap 3.2.8",
"coconut-bandwidth-contract-common",
"coconut-dkg-common",
"coconut-interface",
"config",
"console-subscriber",
@@ -3450,7 +3391,6 @@ dependencies = [
"cw-utils",
"cw3",
"dirs",
"dkg",
"dotenv",
"futures",
"gateway-client",
@@ -3464,7 +3404,6 @@ dependencies = [
"nymcoconut",
"nymsphinx",
"okapi",
"pemstore",
"pin-project",
"pretty_env_logger",
"rand 0.7.3",
@@ -3517,19 +3456,16 @@ name = "nymcoconut"
version = "0.5.0"
dependencies = [
"bincode",
"bls12_381 0.6.0",
"bls12_381 0.5.0",
"bs58",
"criterion",
"digest 0.9.0",
"dkg",
"doc-comment",
"ff 0.11.0",
"ff 0.10.1",
"getrandom 0.2.6",
"group 0.11.0",
"group 0.10.0",
"itertools",
"pemstore",
"rand 0.8.5",
"rand_chacha 0.3.1",
"serde",
"serde_derive",
"sha2 0.9.9",
@@ -5705,9 +5641,7 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
name = "task"
version = "0.1.0"
dependencies = [
"futures",
"log",
"thiserror",
"tokio",
]
@@ -5847,18 +5781,18 @@ checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
[[package]]
name = "thiserror"
version = "1.0.37"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
checksum = "c53f98874615aea268107765aa1ed8f6116782501d18e53d08b471733bea6c85"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.37"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
checksum = "f8b463991b4eab2d801e724172285ec4195c650e8ec79b149e6c2a8e6dd3f783"
dependencies = [
"proc-macro2",
"quote",
@@ -6486,7 +6420,6 @@ dependencies = [
"base64",
"bip39",
"coconut-bandwidth-contract-common",
"coconut-dkg-common",
"coconut-interface",
"colored",
"config",
-2
View File
@@ -31,7 +31,6 @@ members = [
"common/commands",
"common/config",
"common/cosmwasm-smart-contracts/coconut-bandwidth-contract",
"common/cosmwasm-smart-contracts/coconut-dkg",
"common/cosmwasm-smart-contracts/contracts-common",
"common/cosmwasm-smart-contracts/mixnet-contract",
"common/cosmwasm-smart-contracts/multisig-contract",
@@ -72,7 +71,6 @@ members = [
"gateway/gateway-requests",
"integrations/bity",
"mixnode",
"sdk/rust/nym-sdk",
"service-providers/network-requester",
"service-providers/network-statistics",
"validator-api",
+4 -8
View File
@@ -1,6 +1,6 @@
[package]
name = "client-core"
version = "1.1.1"
version = "1.1.0"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021"
@@ -17,7 +17,6 @@ sled = { version = "0.34", optional = true }
tap = "1.0.1"
thiserror = "1.0.34"
url = { version ="2.2", features = ["serde"] }
tokio = { version = "1.21.2", features = ["time", "macros"]}
# internal
config = { path = "../../common/config" }
@@ -31,11 +30,8 @@ nymsphinx = { path = "../../common/nymsphinx" }
pemstore = { path = "../../common/pemstore" }
topology = { path = "../../common/topology" }
validator-client = { path = "../../common/client-libs/validator-client", default-features = false }
task = { path = "../../common/task" }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream]
version = "0.1.9"
features = ["time"]
tokio = { version = "1.21.2", features = ["time", "macros"]}
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen-futures]
version = "0.4"
@@ -51,8 +47,8 @@ rev = "b9d1a54ad514c2f230a026afe0dde341e98cd7b6"
version = "0.2.4"
features = ["futures"]
#[target."cfg(not(target_arch = \"wasm32\"))".dependencies.task]
#path = "../../common/task"
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.task]
path = "../../common/task"
[dev-dependencies]
tempfile = "3.1.0"
@@ -1,441 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::cover_traffic_stream::LoopCoverTrafficStream;
use crate::client::inbound_messages::{InputMessage, InputMessageReceiver, InputMessageSender};
use crate::client::key_manager::KeyManager;
use crate::client::mix_traffic::{BatchMixMessageSender, MixTrafficController};
use crate::client::real_messages_control;
use crate::client::real_messages_control::RealMessagesController;
use crate::client::received_buffer::{
ReceivedBufferRequestReceiver, ReceivedBufferRequestSender, ReceivedMessagesBufferController,
};
use crate::client::topology_control::{
TopologyAccessor, TopologyRefresher, TopologyRefresherConfig,
};
use crate::config::{Config, DebugConfig, GatewayEndpointConfig};
use crate::error::ClientCoreError;
use client_connections::{ConnectionCommandReceiver, ConnectionCommandSender, LaneQueueLengths};
use crypto::asymmetric::identity;
use futures::channel::mpsc;
use gateway_client::bandwidth::BandwidthController;
use gateway_client::{
AcknowledgementReceiver, AcknowledgementSender, GatewayClient, MixnetMessageReceiver,
MixnetMessageSender,
};
use log::info;
use nymsphinx::addressing::clients::Recipient;
use nymsphinx::addressing::nodes::NodeIdentity;
#[cfg(feature = "reply-surb")]
use std::path::PathBuf;
#[cfg(feature = "reply-surb")]
use tap::TapFallible;
use task::{ShutdownListener, ShutdownNotifier};
use url::Url;
// it's fine to do this disgusting compilation flag business here as this problem
// is going to go away in 1.2.0
#[cfg(feature = "reply-surb")]
use crate::client::reply_key_storage::ReplyKeyStorage;
pub struct ClientInput {
pub shared_lane_queue_lengths: LaneQueueLengths,
pub connection_command_sender: ConnectionCommandSender,
pub input_sender: InputMessageSender,
}
pub struct ClientOutput {
pub received_buffer_request_sender: ReceivedBufferRequestSender,
}
pub enum ClientInputStatus {
AwaitingProducer { client_input: ClientInput },
Connected,
}
impl ClientInputStatus {
pub fn register_producer(&mut self) -> ClientInput {
match std::mem::replace(self, ClientInputStatus::Connected) {
ClientInputStatus::AwaitingProducer { client_input } => client_input,
ClientInputStatus::Connected => panic!("producer was already registered before"),
}
}
}
pub enum ClientOutputStatus {
AwaitingConsumer { client_output: ClientOutput },
Connected,
}
impl ClientOutputStatus {
pub fn register_consumer(&mut self) -> ClientOutput {
match std::mem::replace(self, ClientOutputStatus::Connected) {
ClientOutputStatus::AwaitingConsumer { client_output } => client_output,
ClientOutputStatus::Connected => panic!("consumer was already registered before"),
}
}
}
pub struct BaseClientBuilder<'a> {
// due to wasm limitations I had to split it like this : (
gateway_config: &'a GatewayEndpointConfig,
debug_config: &'a DebugConfig,
disabled_credentials: bool,
validator_api_endpoints: Vec<Url>,
#[cfg(feature = "reply-surb")]
reply_surb_keys_store_path: PathBuf,
bandwidth_controller: Option<BandwidthController>,
key_manager: KeyManager,
}
impl<'a> BaseClientBuilder<'a> {
pub fn new_from_base_config<T>(
base_config: &'a Config<T>,
key_manager: KeyManager,
bandwidth_controller: Option<BandwidthController>,
) -> BaseClientBuilder<'a> {
BaseClientBuilder {
gateway_config: base_config.get_gateway_endpoint_config(),
debug_config: base_config.get_debug_config(),
disabled_credentials: base_config.get_disabled_credentials_mode(),
validator_api_endpoints: base_config.get_validator_api_endpoints(),
bandwidth_controller,
key_manager,
#[cfg(feature = "reply-surb")]
reply_surb_keys_store_path: base_config.get_reply_encryption_key_store_path(),
}
}
pub fn new(
gateway_config: &'a GatewayEndpointConfig,
debug_config: &'a DebugConfig,
key_manager: KeyManager,
bandwidth_controller: Option<BandwidthController>,
disabled_credentials: bool,
validator_api_endpoints: Vec<Url>,
#[cfg(feature = "reply-surb")] reply_surb_keys_store_path: PathBuf,
) -> BaseClientBuilder<'a> {
BaseClientBuilder {
gateway_config,
debug_config,
disabled_credentials,
validator_api_endpoints,
bandwidth_controller,
key_manager,
#[cfg(feature = "reply-surb")]
reply_surb_keys_store_path,
}
}
pub fn as_mix_recipient(&self) -> Recipient {
Recipient::new(
*self.key_manager.identity_keypair().public_key(),
*self.key_manager.encryption_keypair().public_key(),
// TODO: below only works under assumption that gateway address == gateway id
// (which currently is true)
NodeIdentity::from_base58_string(&self.gateway_config.gateway_id).unwrap(),
)
}
// future constantly pumping loop cover traffic at some specified average rate
// the pumped traffic goes to the MixTrafficController
fn start_cover_traffic_stream(
&self,
topology_accessor: TopologyAccessor,
mix_tx: BatchMixMessageSender,
shutdown: ShutdownListener,
) {
info!("Starting loop cover traffic stream...");
let mut stream = LoopCoverTrafficStream::new(
self.key_manager.ack_key(),
self.debug_config.average_ack_delay,
self.debug_config.average_packet_delay,
self.debug_config.loop_cover_traffic_average_delay,
mix_tx,
self.as_mix_recipient(),
topology_accessor,
);
if let Some(size) = self.debug_config.use_extended_packet_size {
log::debug!("Setting extended packet size: {:?}", size);
stream.set_custom_packet_size(size.into());
}
stream.start_with_shutdown(shutdown);
}
#[allow(clippy::too_many_arguments)]
fn start_real_traffic_controller(
&self,
topology_accessor: TopologyAccessor,
ack_receiver: AcknowledgementReceiver,
input_receiver: InputMessageReceiver,
mix_sender: BatchMixMessageSender,
lane_queue_lengths: LaneQueueLengths,
client_connection_rx: ConnectionCommandReceiver,
shutdown: ShutdownListener,
#[cfg(feature = "reply-surb")] reply_key_storage: ReplyKeyStorage,
) {
let mut controller_config = real_messages_control::Config::new(
self.key_manager.ack_key(),
self.debug_config.ack_wait_multiplier,
self.debug_config.ack_wait_addition,
self.debug_config.average_ack_delay,
self.debug_config.message_sending_average_delay,
self.debug_config.average_packet_delay,
self.debug_config.disable_main_poisson_packet_distribution,
self.as_mix_recipient(),
);
if let Some(size) = self.debug_config.use_extended_packet_size {
log::debug!("Setting extended packet size: {:?}", size);
controller_config.set_custom_packet_size(size.into());
}
info!("Starting real traffic stream...");
RealMessagesController::new(
controller_config,
ack_receiver,
input_receiver,
mix_sender,
topology_accessor,
lane_queue_lengths,
client_connection_rx,
#[cfg(feature = "reply-surb")]
reply_key_storage,
)
.start_with_shutdown(shutdown);
}
// buffer controlling all messages fetched from provider
// required so that other components would be able to use them (say the websocket)
fn start_received_messages_buffer_controller(
&self,
query_receiver: ReceivedBufferRequestReceiver,
mixnet_receiver: MixnetMessageReceiver,
shutdown: ShutdownListener,
#[cfg(feature = "reply-surb")] reply_key_storage: ReplyKeyStorage,
) {
info!("Starting received messages buffer controller...");
ReceivedMessagesBufferController::new(
self.key_manager.encryption_keypair(),
query_receiver,
mixnet_receiver,
#[cfg(feature = "reply-surb")]
reply_key_storage,
)
.start_with_shutdown(shutdown)
}
async fn start_gateway_client(
&mut self,
mixnet_message_sender: MixnetMessageSender,
ack_sender: AcknowledgementSender,
shutdown: ShutdownListener,
) -> GatewayClient {
let gateway_id = self.gateway_config.gateway_id.clone();
if gateway_id.is_empty() {
panic!("The identity of the gateway is unknown - did you run `nym-client` init?")
}
let gateway_owner = self.gateway_config.gateway_owner.clone();
if gateway_owner.is_empty() {
panic!("The owner of the gateway is unknown - did you run `nym-client` init?")
}
let gateway_address = self.gateway_config.gateway_listener.clone();
if gateway_address.is_empty() {
panic!("The address of the gateway is unknown - did you run `nym-client` init?")
}
let gateway_identity = identity::PublicKey::from_base58_string(gateway_id)
.expect("provided gateway id is invalid!");
// disgusting wasm workaround since there's no key persistence there (nor `client init`)
let shared_key = if self.key_manager.gateway_key_set() {
Some(self.key_manager.gateway_shared_key())
} else {
None
};
let mut gateway_client = GatewayClient::new(
gateway_address,
self.key_manager.identity_keypair(),
gateway_identity,
gateway_owner,
shared_key,
mixnet_message_sender,
ack_sender,
self.debug_config.gateway_response_timeout,
self.bandwidth_controller.take(),
Some(shutdown),
);
gateway_client.set_disabled_credentials_mode(self.disabled_credentials);
gateway_client
.authenticate_and_start()
.await
.expect("could not authenticate and start up the gateway connection");
gateway_client
}
// future responsible for periodically polling directory server and updating
// the current global view of topology
async fn start_topology_refresher(
&mut self,
topology_accessor: TopologyAccessor,
shutdown: ShutdownListener,
) -> Result<(), ClientCoreError> {
let topology_refresher_config = TopologyRefresherConfig::new(
self.validator_api_endpoints.clone(),
self.debug_config.topology_refresh_rate,
env!("CARGO_PKG_VERSION").to_string(),
);
let mut topology_refresher =
TopologyRefresher::new(topology_refresher_config, topology_accessor);
// before returning, block entire runtime to refresh the current network view so that any
// components depending on topology would see a non-empty view
info!("Obtaining initial network topology");
topology_refresher.refresh().await;
// TODO: a slightly more graceful termination here
if !topology_refresher.is_topology_routable().await {
log::error!(
"The current network topology seem to be insufficient to route any packets through \
- check if enough nodes and a gateway are online"
);
return Err(ClientCoreError::InsufficientNetworkTopology);
}
info!("Starting topology refresher...");
topology_refresher.start_with_shutdown(shutdown);
Ok(())
}
// controller for sending sphinx packets to mixnet (either real traffic or cover traffic)
// TODO: if we want to send control messages to gateway_client, this CAN'T take the ownership
// over it. Perhaps GatewayClient needs to be thread-shareable or have some channel for
// requests?
fn start_mix_traffic_controller(
gateway_client: GatewayClient,
shutdown: ShutdownListener,
) -> BatchMixMessageSender {
info!("Starting mix traffic controller...");
let (mix_traffic_controller, mix_tx) = MixTrafficController::new(gateway_client);
mix_traffic_controller.start_with_shutdown(shutdown);
mix_tx
}
pub async fn start_base(mut self) -> Result<BaseClient, ClientCoreError> {
info!("Starting nym client");
// channels for inter-component communication
// TODO: make the channels be internally created by the relevant components
// rather than creating them here, so say for example the buffer controller would create the request channels
// and would allow anyone to clone the sender channel
// unwrapped_sphinx_sender is the transmitter of mixnet messages received from the gateway
// unwrapped_sphinx_receiver is the receiver for said messages - used by ReceivedMessagesBuffer
let (mixnet_messages_sender, mixnet_messages_receiver) = mpsc::unbounded();
// used for announcing connection or disconnection of a channel for pushing re-assembled messages to
let (received_buffer_request_sender, received_buffer_request_receiver) = mpsc::unbounded();
// channels responsible for controlling real messages
let (input_sender, input_receiver) = tokio::sync::mpsc::channel::<InputMessage>(1);
// channels responsible for controlling ack messages
let (ack_sender, ack_receiver) = mpsc::unbounded();
let shared_topology_accessor = TopologyAccessor::new();
#[cfg(feature = "reply-surb")]
let reply_key_storage =
ReplyKeyStorage::load(&self.reply_surb_keys_store_path).tap_err(|err| {
log::error!("Failed to load reply key storage - is it perhaps already in use?");
log::error!("{:?}", err);
})?;
// Shutdown notifier for signalling tasks to stop
let shutdown = ShutdownNotifier::default();
// the components are started in very specific order. Unless you know what you are doing,
// do not change that.
self.start_topology_refresher(shared_topology_accessor.clone(), shutdown.subscribe())
.await?;
self.start_received_messages_buffer_controller(
received_buffer_request_receiver,
mixnet_messages_receiver,
shutdown.subscribe(),
#[cfg(feature = "reply-surb")]
reply_key_storage.clone(),
);
let gateway_client = self
.start_gateway_client(mixnet_messages_sender, ack_sender, shutdown.subscribe())
.await;
// The sphinx_message_sender is the transmitter for any component generating sphinx packets
// that are to be sent to the mixnet. They are used by cover traffic stream and real
// traffic stream.
// The MixTrafficController then sends the actual traffic
let sphinx_message_sender =
Self::start_mix_traffic_controller(gateway_client, shutdown.subscribe());
// Channels that the websocket listener can use to signal downstream to the real traffic
// controller that connections are closed.
let (client_connection_tx, client_connection_rx) = mpsc::unbounded();
// Shared queue length data. Published by the `OutQueueController` in the client, and used
// primarily to throttle incoming connections (e.g socks5 for attached network-requesters)
let shared_lane_queue_lengths = LaneQueueLengths::new();
self.start_real_traffic_controller(
shared_topology_accessor.clone(),
ack_receiver,
input_receiver,
sphinx_message_sender.clone(),
shared_lane_queue_lengths.clone(),
client_connection_rx,
shutdown.subscribe(),
#[cfg(feature = "reply-surb")]
reply_key_storage,
);
if !self.debug_config.disable_loop_cover_traffic_stream {
self.start_cover_traffic_stream(
shared_topology_accessor,
sphinx_message_sender,
shutdown.subscribe(),
);
}
info!("Client startup finished!");
info!("The address of this client is: {}", self.as_mix_recipient());
Ok(BaseClient {
client_input: ClientInputStatus::AwaitingProducer {
client_input: ClientInput {
shared_lane_queue_lengths,
connection_command_sender: client_connection_tx,
input_sender,
},
},
client_output: ClientOutputStatus::AwaitingConsumer {
client_output: ClientOutput {
received_buffer_request_sender,
},
},
shutdown_notifier: shutdown,
})
}
}
pub struct BaseClient {
pub client_input: ClientInputStatus,
pub client_output: ClientOutputStatus,
pub shutdown_notifier: ShutdownNotifier,
}
@@ -143,16 +143,6 @@ impl LoopCoverTrafficStream<OsRng> {
self.packet_size = packet_size;
}
fn set_next_delay(&mut self, amount: Duration) {
#[cfg(not(target_arch = "wasm32"))]
let next_delay = Box::pin(time::sleep(amount));
#[cfg(target_arch = "wasm32")]
let next_delay = Box::pin(wasm_timer::Delay::new(amount));
self.next_delay = next_delay;
}
async fn on_new_message(&mut self) {
trace!("next cover message!");
@@ -212,11 +202,12 @@ impl LoopCoverTrafficStream<OsRng> {
tokio::task::yield_now().await;
}
#[cfg(not(target_arch = "wasm32"))]
pub fn start_with_shutdown(mut self, mut shutdown: task::ShutdownListener) {
// we should set initial delay only when we actually start the stream
let sampled =
sample_poisson_duration(&mut self.rng, self.average_cover_message_sending_delay);
self.set_next_delay(sampled);
self.next_delay = Box::pin(time::sleep(sampled));
spawn_future(async move {
debug!("Started LoopCoverTrafficStream with graceful shutdown support");
@@ -237,16 +228,17 @@ impl LoopCoverTrafficStream<OsRng> {
}
}
}
shutdown.recv_timeout().await;
assert!(shutdown.is_shutdown_poll());
log::debug!("LoopCoverTrafficStream: Exiting");
})
}
#[cfg(target_arch = "wasm32")]
pub fn start(mut self) {
// we should set initial delay only when we actually start the stream
let sampled =
sample_poisson_duration(&mut self.rng, self.average_cover_message_sending_delay);
self.set_next_delay(sampled);
self.next_delay = Box::pin(wasm_timer::Delay::new(sampled));
spawn_future(async move {
debug!("Started LoopCoverTrafficStream without graceful shutdown support");
@@ -1,9 +1,10 @@
use client_connections::TransmissionLane;
use futures::channel::mpsc;
use nymsphinx::addressing::clients::Recipient;
use nymsphinx::anonymous_replies::ReplySurb;
pub type InputMessageSender = tokio::sync::mpsc::Sender<InputMessage>;
pub type InputMessageReceiver = tokio::sync::mpsc::Receiver<InputMessage>;
pub type InputMessageSender = mpsc::UnboundedSender<InputMessage>;
pub type InputMessageReceiver = mpsc::UnboundedReceiver<InputMessage>;
#[derive(Debug)]
pub enum InputMessage {
@@ -149,10 +149,6 @@ impl KeyManager {
)
}
pub fn gateway_key_set(&self) -> bool {
self.gateway_shared_key.is_some()
}
/// Gets an atomically reference counted pointer to [`AckKey`].
pub fn ack_key(&self) -> Arc<AckKey> {
Arc::clone(&self.ack_key)
@@ -67,6 +67,7 @@ impl MixTrafficController {
}
}
#[cfg(not(target_arch = "wasm32"))]
pub fn start_with_shutdown(mut self, mut shutdown: task::ShutdownListener) {
spawn_future(async move {
debug!("Started MixTrafficController with graceful shutdown support");
@@ -87,11 +88,12 @@ impl MixTrafficController {
}
}
}
shutdown.recv_timeout().await;
assert!(shutdown.is_shutdown_poll());
log::debug!("MixTrafficController: Exiting");
})
}
#[cfg(target_arch = "wasm32")]
pub fn start(mut self) {
spawn_future(async move {
debug!("Started MixTrafficController without graceful shutdown support");
+8 -3
View File
@@ -1,7 +1,5 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::sync::atomic::AtomicBool;
pub mod base_client;
pub mod cover_traffic_stream;
pub mod inbound_messages;
pub mod key_manager;
@@ -11,3 +9,10 @@ pub mod received_buffer;
#[cfg(feature = "reply-surb")]
pub mod reply_key_storage;
pub mod topology_control;
// This is *NOT* used to signal shutdown.
// It's critical that we don't have any tasks finishing early, this is an additional safety check
// that tasks exiting are doing so because shutdown has been signalled, and no other reason.
// In particular for tasks that rely on their associated channel being closed to signal shutdown,
// and don't have access to a shutdown listener channel.
pub static SHUTDOWN_HAS_BEEN_SIGNALLED: AtomicBool = AtomicBool::new(false);
@@ -70,6 +70,7 @@ impl AcknowledgementListener {
}
}
#[cfg(not(target_arch = "wasm32"))]
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
debug!("Started AcknowledgementListener with graceful shutdown support");
@@ -87,12 +88,11 @@ impl AcknowledgementListener {
}
}
}
shutdown.recv_timeout().await;
assert!(shutdown.is_shutdown_poll());
log::debug!("AcknowledgementListener: Exiting");
}
// todo: think whether this is still required
#[allow(dead_code)]
#[cfg(target_arch = "wasm32")]
pub(super) async fn run(&mut self) {
debug!("Started AcknowledgementListener without graceful shutdown support");
@@ -245,6 +245,7 @@ impl ActionController {
}
}
#[cfg(not(target_arch = "wasm32"))]
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
debug!("Started ActionController with graceful shutdown support");
@@ -271,15 +272,11 @@ impl ActionController {
}
}
}
#[cfg(not(target_arch = "wasm32"))]
tokio::time::timeout(Duration::from_secs(5), shutdown.recv())
.await
.expect("Task stopped without shutdown called");
assert!(shutdown.is_shutdown_poll());
log::debug!("ActionController: Exiting");
}
// todo: think whether this is still required
#[allow(dead_code)]
#[cfg(target_arch = "wasm32")]
pub(super) async fn run(&mut self) {
debug!("Started ActionController without graceful shutdown support");
@@ -9,6 +9,7 @@ use crate::client::{
topology_control::TopologyAccessor,
};
use client_connections::TransmissionLane;
use futures::StreamExt;
use log::*;
use nymsphinx::anonymous_replies::ReplySurb;
use nymsphinx::preparer::MessagePreparer;
@@ -188,18 +189,18 @@ where
if let Some(real_messages) = real_messages {
// tells real message sender (with the poisson timer) to send this to the mix network
self.real_message_sender
.send((real_messages, lane))
.await
.expect("BatchRealMessageReceiver has stopped receiving!");
.unbounded_send((real_messages, lane))
.unwrap();
}
}
#[cfg(not(target_arch = "wasm32"))]
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
debug!("Started InputMessageListener with graceful shutdown support");
while !shutdown.is_shutdown() {
tokio::select! {
input_msg = self.input_receiver.recv() => match input_msg {
input_msg = self.input_receiver.next() => match input_msg {
Some(input_msg) => {
self.on_input_message(input_msg).await;
},
@@ -213,15 +214,14 @@ where
}
}
}
shutdown.recv_timeout().await;
assert!(shutdown.is_shutdown_poll());
log::debug!("InputMessageListener: Exiting");
}
// todo: think whether this is still required
#[allow(dead_code)]
#[cfg(target_arch = "wasm32")]
pub(super) async fn run(&mut self) {
debug!("Started InputMessageListener without graceful shutdown support");
while let Some(input_msg) = self.input_receiver.recv().await {
while let Some(input_msg) = self.input_receiver.next().await {
self.on_input_message(input_msg).await;
}
}
@@ -234,6 +234,7 @@ where
}
}
#[cfg(not(target_arch = "wasm32"))]
pub(super) fn start_with_shutdown(self, shutdown: task::ShutdownListener) {
let mut acknowledgement_listener = self.acknowledgement_listener;
let mut input_message_listener = self.input_message_listener;
@@ -279,8 +280,7 @@ where
});
}
// todo: think whether this is still required
#[allow(dead_code)]
#[cfg(target_arch = "wasm32")]
pub(super) fn start(self) {
let mut acknowledgement_listener = self.acknowledgement_listener;
let mut input_message_listener = self.input_message_listener;
@@ -117,14 +117,14 @@ where
// send to `OutQueueControl` to eventually send to the mix network
self.real_message_sender
.send((
.unbounded_send((
vec![RealMessage::new(prepared_fragment.mix_packet, frag_id)],
TransmissionLane::Retransmission,
))
.await
.expect("BatchRealMessageReceiver has stopped receiving!");
.unwrap();
}
#[cfg(not(target_arch = "wasm32"))]
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
debug!("Started RetransmissionRequestListener with graceful shutdown support");
@@ -142,12 +142,11 @@ where
}
}
}
shutdown.recv_timeout().await;
assert!(shutdown.is_shutdown_poll());
log::debug!("RetransmissionRequestListener: Exiting");
}
// todo: think whether this is still required
#[allow(dead_code)]
#[cfg(target_arch = "wasm32")]
pub(super) async fn run(&mut self) {
debug!("Started RetransmissionRequestListener without graceful shutdown support");
@@ -42,6 +42,7 @@ impl SentNotificationListener {
.unwrap();
}
#[cfg(not(target_arch = "wasm32"))]
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
debug!("Started SentNotificationListener with graceful shutdown support");
@@ -65,8 +66,7 @@ impl SentNotificationListener {
log::debug!("SentNotificationListener: Exiting");
}
// todo: think whether this is still required
#[allow(dead_code)]
#[cfg(target_arch = "wasm32")]
pub(super) async fn run(&mut self) {
debug!("Started SentNotificationListener without graceful shutdown support");
@@ -16,7 +16,7 @@ use crate::{
},
spawn_future,
};
use client_connections::{ConnectionCommandReceiver, LaneQueueLengths};
use client_connections::{ClosedConnectionReceiver, LaneQueueLengths};
use futures::channel::mpsc;
use gateway_client::AcknowledgementReceiver;
use log::*;
@@ -113,13 +113,13 @@ impl RealMessagesController<OsRng> {
input_receiver: InputMessageReceiver,
mix_sender: BatchMixMessageSender,
topology_access: TopologyAccessor,
lane_queue_lengths: LaneQueueLengths,
client_connection_rx: ConnectionCommandReceiver,
#[cfg(feature = "reply-surb")] reply_key_storage: ReplyKeyStorage,
lane_queue_lengths: LaneQueueLengths,
closed_connection_rx: ClosedConnectionReceiver,
) -> Self {
let rng = OsRng;
let (real_message_sender, real_message_receiver) = tokio::sync::mpsc::channel(1);
let (real_message_sender, real_message_receiver) = mpsc::unbounded();
let (sent_notifier_tx, sent_notifier_rx) = mpsc::unbounded();
let ack_controller_connectors = AcknowledgementControllerConnectors::new(
@@ -166,7 +166,7 @@ impl RealMessagesController<OsRng> {
config.self_recipient,
topology_access,
lane_queue_lengths,
client_connection_rx,
closed_connection_rx,
);
RealMessagesController {
@@ -175,6 +175,7 @@ impl RealMessagesController<OsRng> {
}
}
#[cfg(not(target_arch = "wasm32"))]
pub fn start_with_shutdown(self, shutdown: task::ShutdownListener) {
let mut out_queue_control = self.out_queue_control;
let ack_control = self.ack_control;
@@ -5,8 +5,9 @@ use crate::client::mix_traffic::BatchMixMessageSender;
use crate::client::real_messages_control::acknowledgement_control::SentPacketNotificationSender;
use crate::client::topology_control::TopologyAccessor;
use client_connections::{
ConnectionCommand, ConnectionCommandReceiver, ConnectionId, LaneQueueLengths, TransmissionLane,
ClosedConnectionReceiver, ConnectionId, LaneQueueLengths, TransmissionLane,
};
use futures::channel::mpsc;
use futures::task::{Context, Poll};
use futures::{Future, Stream, StreamExt};
use log::*;
@@ -134,13 +135,12 @@ where
/// Incoming channel for being notified of closed connections, so that we can close lanes
/// corresponding to connections. To avoid sending traffic unnecessary
client_connection_rx: ConnectionCommandReceiver,
closed_connection_rx: ClosedConnectionReceiver,
/// Report queue lengths so that upstream can backoff sending data, and keep connections open.
lane_queue_lengths: LaneQueueLengths,
}
#[derive(Debug)]
pub(crate) struct RealMessage {
mix_packet: MixPacket,
fragment_id: FragmentIdentifier,
@@ -158,8 +158,8 @@ impl RealMessage {
// messages are already prepared, etc. the real point of it is to forward it to mix_traffic
// after sufficient delay
pub(crate) type BatchRealMessageSender =
tokio::sync::mpsc::Sender<(Vec<RealMessage>, TransmissionLane)>;
type BatchRealMessageReceiver = tokio::sync::mpsc::Receiver<(Vec<RealMessage>, TransmissionLane)>;
mpsc::UnboundedSender<(Vec<RealMessage>, TransmissionLane)>;
type BatchRealMessageReceiver = mpsc::UnboundedReceiver<(Vec<RealMessage>, TransmissionLane)>;
pub(crate) enum StreamMessage {
Cover,
@@ -183,7 +183,7 @@ where
our_full_destination: Recipient,
topology_access: TopologyAccessor,
lane_queue_lengths: LaneQueueLengths,
client_connection_rx: ConnectionCommandReceiver,
closed_connection_rx: ClosedConnectionReceiver,
) -> Self {
OutQueueControl {
config,
@@ -197,11 +197,15 @@ where
rng,
topology_access,
transmission_buffer: Default::default(),
client_connection_rx,
closed_connection_rx,
lane_queue_lengths,
}
}
//pub fn get_lane_queue_length(&self) -> &LaneQueueLength {
// &self.lane_queue_length
//}
fn sent_notify(&self, frag_id: FragmentIdentifier) {
// well technically the message was not sent just yet, but now it's up to internal
// queues and client load rather than the required delay. So realistically we can treat
@@ -254,7 +258,7 @@ where
};
if let Err(err) = self.mix_tx.send(vec![next_message]).await {
log::error!("Failed to send: {}", err);
log::error!("Failed to send - channel closed: {}", err);
}
// notify ack controller about sending our message only after we actually managed to push it
@@ -263,7 +267,7 @@ where
self.sent_notify(fragment_id);
}
// In addition to closing connections on receiving messages throught client_connection_rx,
// In addition to closing connections on receiving messages throught closed_connection_rx,
// also close connections when sufficiently stale.
self.transmission_buffer.prune_stale_connections();
@@ -279,9 +283,9 @@ where
}
fn on_close_connection(&mut self, connection_id: ConnectionId) {
log::debug!("Removing lane for connection: {connection_id}");
self.transmission_buffer
.remove(&TransmissionLane::ConnectionId(connection_id));
//log::debug!("Removing lane for connection: {connection_id}");
//self.transmission_buffer
//.remove(&TransmissionLane::ConnectionId(connection_id));
}
fn current_average_message_sending_delay(&self) -> Duration {
@@ -336,11 +340,8 @@ where
// Start by checking if we have any incoming messages about closed connections
// NOTE: this feels a bit iffy, the `OutQueueControl` is getting ripe for a rewrite to
// something simpler.
if let Poll::Ready(Some(id)) = Pin::new(&mut self.client_connection_rx).poll_next(cx) {
match id {
ConnectionCommand::Close(id) => self.on_close_connection(id),
ConnectionCommand::ActiveConnections(_) => panic!(),
}
if let Poll::Ready(Some(id)) = Pin::new(&mut self.closed_connection_rx).poll_next(cx) {
self.on_close_connection(id);
}
if let Some(ref mut next_delay) = &mut self.next_delay {
@@ -371,7 +372,7 @@ where
// in `Vec`, this ensures that on average we will fetch messages faster than we can
// send, which is a condition for being able to multiplex sphinx packets from multiple
// data streams.
match Pin::new(&mut self.real_receiver).poll_recv(cx) {
match Pin::new(&mut self.real_receiver).poll_next(cx) {
// in the case our real message channel stream was closed, we should also indicate we are closed
// (and whoever is using the stream should panic)
Poll::Ready(None) => Poll::Ready(None),
@@ -415,14 +416,11 @@ where
fn poll_immediate(&mut self, cx: &mut Context<'_>) -> Poll<Option<StreamMessage>> {
// Start by checking if we have any incoming messages about closed connections
if let Poll::Ready(Some(id)) = Pin::new(&mut self.client_connection_rx).poll_next(cx) {
match id {
ConnectionCommand::Close(id) => self.on_close_connection(id),
ConnectionCommand::ActiveConnections(_) => panic!(),
}
if let Poll::Ready(Some(id)) = Pin::new(&mut self.closed_connection_rx).poll_next(cx) {
self.on_close_connection(id);
}
match Pin::new(&mut self.real_receiver).poll_recv(cx) {
match Pin::new(&mut self.real_receiver).poll_next(cx) {
// in the case our real message channel stream was closed, we should also indicate we are closed
// (and whoever is using the stream should panic)
Poll::Ready(None) => Poll::Ready(None),
@@ -465,91 +463,48 @@ where
let lanes = self.transmission_buffer.num_lanes();
let mult = self.sending_delay_controller.current_multiplier();
let delay = self.current_average_message_sending_delay().as_millis();
let status_str = if self.config.disable_poisson_packet_distribution {
format!(
if self.config.disable_poisson_packet_distribution {
log::info!(
"Status: {lanes} lanes, backlog: {:.2} kiB ({packets}), no delay",
backlog
)
);
} else {
format!(
log::info!(
"Status: {lanes} lanes, backlog: {:.2} kiB ({packets}), avg delay: {}ms ({mult})",
backlog, delay
)
};
if packets > 1000 {
log::warn!("{status_str}");
} else if packets > 0 {
log::info!("{status_str}");
} else {
log::debug!("{status_str}");
}
}
#[cfg(not(target_arch = "wasm32"))]
fn log_status_infrequent(&self) {
if self.sending_delay_controller.current_multiplier() > 1 {
log::warn!(
"Unable to send packets fast enough - sending delay multiplier set to: {}",
self.sending_delay_controller.current_multiplier()
backlog,
delay
);
}
}
#[cfg(not(target_arch = "wasm32"))]
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
debug!("Started OutQueueControl with graceful shutdown support");
#[cfg(not(target_arch = "wasm32"))]
{
let mut status_timer = tokio::time::interval(Duration::from_secs(5));
let mut infrequent_status_timer = tokio::time::interval(Duration::from_secs(60));
let mut status_timer = tokio::time::interval(Duration::from_secs(1));
while !shutdown.is_shutdown() {
tokio::select! {
biased;
_ = shutdown.recv() => {
log::trace!("OutQueueControl: Received shutdown");
}
_ = status_timer.tick() => {
self.log_status();
}
_ = infrequent_status_timer.tick() => {
self.log_status_infrequent();
}
next_message = self.next() => if let Some(next_message) = next_message {
self.on_message(next_message).await;
} else {
log::trace!("OutQueueControl: Stopping since channel closed");
break;
}
while !shutdown.is_shutdown() {
tokio::select! {
biased;
_ = shutdown.recv() => {
log::trace!("OutQueueControl: Received shutdown");
}
}
tokio::time::timeout(Duration::from_secs(5), shutdown.recv())
.await
.expect("Task stopped without shutdown called");
}
#[cfg(target_arch = "wasm32")]
{
while !shutdown.is_shutdown() {
tokio::select! {
biased;
_ = shutdown.recv() => {
log::trace!("OutQueueControl: Received shutdown");
}
next_message = self.next() => if let Some(next_message) = next_message {
self.on_message(next_message).await;
} else {
log::trace!("OutQueueControl: Stopping since channel closed");
break;
}
_ = status_timer.tick() => {
self.log_status();
}
next_message = self.next() => if let Some(next_message) = next_message {
self.on_message(next_message).await;
} else {
log::trace!("OutQueueControl: Stopping since channel closed");
break;
}
}
}
assert!(shutdown.is_shutdown_poll());
log::debug!("OutQueueControl: Exiting");
}
// todo: think whether this is still required
#[allow(dead_code)]
#[cfg(target_arch = "wasm32")]
pub(super) async fn run(&mut self) {
debug!("Started OutQueueControl without graceful shutdown support");
@@ -82,7 +82,7 @@ impl SendingDelayController {
self.current_multiplier =
(self.current_multiplier + 1).clamp(self.lower_bound, self.upper_bound);
self.time_when_changed = get_time_now();
log::warn!(
log::debug!(
"Increasing sending delay multiplier to: {}",
self.current_multiplier
);
@@ -201,6 +201,7 @@ impl LaneBufferEntry {
> Duration::from_secs(MSG_CONSIDERED_STALE_AFTER_SECS)
}
#[cfg(not(target_arch = "wasm32"))]
fn len(&self) -> usize {
self.real_messages.len()
}
@@ -320,6 +320,7 @@ impl RequestReceiver {
}
}
#[cfg(not(target_arch = "wasm32"))]
async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
debug!("Started RequestReceiver with graceful shutdown support");
while !shutdown.is_shutdown() {
@@ -339,12 +340,11 @@ impl RequestReceiver {
},
}
}
shutdown.recv_timeout().await;
assert!(shutdown.is_shutdown_poll());
log::debug!("RequestReceiver: Exiting");
}
// todo: think whether this is still required
#[allow(dead_code)]
#[cfg(target_arch = "wasm32")]
async fn run(&mut self) {
debug!("Started RequestReceiver without graceful shutdown support");
@@ -370,6 +370,7 @@ impl FragmentedMessageReceiver {
}
}
#[cfg(not(target_arch = "wasm32"))]
async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
debug!("Started FragmentedMessageReceiver with graceful shutdown support");
while !shutdown.is_shutdown() {
@@ -388,12 +389,11 @@ impl FragmentedMessageReceiver {
}
}
}
shutdown.recv_timeout().await;
assert!(shutdown.is_shutdown_poll());
log::debug!("FragmentedMessageReceiver: Exiting");
}
// todo: think whether this is still required
#[allow(dead_code)]
#[cfg(target_arch = "wasm32")]
async fn run(&mut self) {
debug!("Started FragmentedMessageReceiver without graceful shutdown support");
@@ -430,6 +430,7 @@ impl ReceivedMessagesBufferController {
}
}
#[cfg(not(target_arch = "wasm32"))]
pub fn start_with_shutdown(self, shutdown: task::ShutdownListener) {
let mut fragmented_message_receiver = self.fragmented_message_receiver;
let mut request_receiver = self.request_receiver;
@@ -8,13 +8,10 @@ use nymsphinx::anonymous_replies::{
};
use std::path::Path;
#[derive(Debug, thiserror::Error)]
#[derive(Debug)]
pub enum ReplyKeyStorageError {
#[error("DB Read Error: {0}")]
DbReadError(sled::Error),
#[error("DB Write Error: {0}")]
DbWriteError(sled::Error),
#[error("DB Open Error: {0}")]
DbOpenError(sled::Error),
}
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
use crate::spawn_future;
use futures::StreamExt;
use log::*;
use nymsphinx::addressing::clients::Recipient;
use nymsphinx::params::DEFAULT_NUM_MIX_HOPS;
@@ -139,7 +138,7 @@ impl TopologyRefresherConfig {
}
pub struct TopologyRefresher {
validator_client: validator_client::client::ApiClient,
validator_client: validator_client::ApiClient,
client_version: String,
validator_api_urls: Vec<Url>,
@@ -155,9 +154,7 @@ impl TopologyRefresher {
cfg.validator_api_urls.shuffle(&mut thread_rng());
TopologyRefresher {
validator_client: validator_client::client::ApiClient::new(
cfg.validator_api_urls[0].clone(),
),
validator_client: validator_client::ApiClient::new(cfg.validator_api_urls[0].clone()),
client_version: cfg.client_version,
validator_api_urls: cfg.validator_api_urls,
topology_accessor,
@@ -297,22 +294,14 @@ impl TopologyRefresher {
self.topology_accessor.is_routable().await
}
#[cfg(not(target_arch = "wasm32"))]
pub fn start_with_shutdown(mut self, mut shutdown: task::ShutdownListener) {
spawn_future(async move {
debug!("Started TopologyRefresher with graceful shutdown support");
#[cfg(not(target_arch = "wasm32"))]
let mut interval = tokio_stream::wrappers::IntervalStream::new(tokio::time::interval(
self.refresh_rate,
));
#[cfg(target_arch = "wasm32")]
let mut interval =
gloo_timers::future::IntervalStream::new(self.refresh_rate.as_millis() as u32);
while !shutdown.is_shutdown() {
tokio::select! {
_ = interval.next() => {
_ = tokio::time::sleep(self.refresh_rate) => {
self.refresh().await;
},
_ = shutdown.recv() => {
@@ -320,23 +309,19 @@ impl TopologyRefresher {
},
}
}
shutdown.recv_timeout().await;
assert!(shutdown.is_shutdown_poll());
log::debug!("TopologyRefresher: Exiting");
})
}
#[cfg(target_arch = "wasm32")]
pub fn start(mut self) {
spawn_future(async move {
#[cfg(not(target_arch = "wasm32"))]
let mut interval = tokio_stream::wrappers::IntervalStream::new(tokio::time::interval(
self.refresh_rate,
));
use futures::StreamExt;
#[cfg(target_arch = "wasm32")]
spawn_future(async move {
let mut interval =
gloo_timers::future::IntervalStream::new(self.refresh_rate.as_millis() as u32);
while (interval.next().await).is_some() {
while let Some(_) = interval.next().await {
self.refresh().await;
}
})
+20 -47
View File
@@ -34,7 +34,7 @@ pub fn missing_string_value() -> String {
MISSING_VALUE.to_string()
}
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Config<T> {
client: Client<T>,
@@ -42,23 +42,17 @@ pub struct Config<T> {
#[serde(default)]
logging: Logging,
#[serde(default)]
debug: DebugConfig,
debug: Debug,
}
impl<T> Config<T> {
pub fn new<S: Into<String>>(id: S) -> Self
where
T: NymConfig,
{
impl<T: NymConfig> Config<T> {
pub fn new<S: Into<String>>(id: S) -> Self {
let mut cfg = Config::default();
cfg.with_id(id);
cfg
}
pub fn with_id<S: Into<String>>(&mut self, id: S)
where
T: NymConfig,
{
pub fn with_id<S: Into<String>>(&mut self, id: S) {
let id = id.into();
// identity key setting
@@ -123,7 +117,7 @@ impl<T> Config<T> {
self.client.disabled_credentials_mode = disabled_credentials_mode;
}
pub fn with_gateway_endpoint(&mut self, gateway_endpoint: GatewayEndpointConfig) {
pub fn with_gateway_endpoint(&mut self, gateway_endpoint: GatewayEndpoint) {
self.client.gateway_endpoint = gateway_endpoint;
}
@@ -131,10 +125,6 @@ impl<T> Config<T> {
self.client.gateway_endpoint.gateway_id = id.into();
}
pub fn set_custom_validators(&mut self, validator_urls: Vec<Url>) {
self.client.validator_urls = validator_urls;
}
pub fn set_custom_validator_apis(&mut self, validator_api_urls: Vec<Url>) {
self.client.validator_api_urls = validator_api_urls;
}
@@ -189,10 +179,6 @@ impl<T> Config<T> {
self.client.ack_key_file.clone()
}
pub fn get_validator_endpoints(&self) -> Vec<Url> {
self.client.validator_urls.clone()
}
pub fn get_validator_api_endpoints(&self) -> Vec<Url> {
self.client.validator_api_urls.clone()
}
@@ -209,11 +195,7 @@ impl<T> Config<T> {
self.client.gateway_endpoint.gateway_listener.clone()
}
pub fn get_gateway_endpoint_config(&self) -> &GatewayEndpointConfig {
&self.client.gateway_endpoint
}
pub fn get_gateway_endpoint(&self) -> &GatewayEndpointConfig {
pub fn get_gateway_endpoint(&self) -> &GatewayEndpoint {
&self.client.gateway_endpoint
}
@@ -222,10 +204,6 @@ impl<T> Config<T> {
}
// Debug getters
pub fn get_debug_config(&self) -> &DebugConfig {
&self.debug
}
pub fn get_average_packet_delay(&self) -> Duration {
self.debug.average_packet_delay
}
@@ -271,7 +249,7 @@ impl<T> Config<T> {
}
pub fn get_use_extended_packet_size(&self) -> Option<ExtendedPacketSize> {
self.debug.use_extended_packet_size
self.debug.use_extended_packet_size.clone()
}
pub fn get_version(&self) -> &str {
@@ -291,7 +269,7 @@ impl<T: NymConfig> Default for Config<T> {
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen(getter_with_clone))]
pub struct GatewayEndpointConfig {
pub struct GatewayEndpoint {
/// gateway_id specifies ID of the gateway to which the client should send messages.
/// If initially omitted, a random gateway will be chosen from the available topology.
pub gateway_id: String,
@@ -303,10 +281,10 @@ pub struct GatewayEndpointConfig {
pub gateway_listener: String,
}
impl From<topology::gateway::Node> for GatewayEndpointConfig {
fn from(node: topology::gateway::Node) -> GatewayEndpointConfig {
impl From<topology::gateway::Node> for GatewayEndpoint {
fn from(node: topology::gateway::Node) -> GatewayEndpoint {
let gateway_listener = node.clients_address();
GatewayEndpointConfig {
GatewayEndpoint {
gateway_id: node.identity_key.to_base58_string(),
gateway_owner: node.owner,
gateway_listener,
@@ -314,7 +292,7 @@ impl From<topology::gateway::Node> for GatewayEndpointConfig {
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
pub struct Client<T> {
/// Version of the client for which this configuration was created.
#[serde(default = "missing_string_value")]
@@ -328,10 +306,6 @@ pub struct Client<T> {
#[serde(default)]
disabled_credentials_mode: bool,
/// Addresses to nymd validators via which the client can communicate with the chain.
#[serde(default)]
validator_urls: Vec<Url>,
/// Addresses to APIs running on validator from which the client gets the view of the network.
validator_api_urls: Vec<Url>,
@@ -360,7 +334,7 @@ pub struct Client<T> {
reply_encryption_key_store_path: PathBuf,
/// Information regarding how the client should send data to gateway.
gateway_endpoint: GatewayEndpointConfig,
gateway_endpoint: GatewayEndpoint,
/// Path to the database containing bandwidth credentials of this client.
database_path: PathBuf,
@@ -380,7 +354,6 @@ impl<T: NymConfig> Default for Client<T> {
version: env!("CARGO_PKG_VERSION").to_string(),
id: "".to_string(),
disabled_credentials_mode: true,
validator_urls: vec![],
validator_api_urls: vec![],
private_identity_key_file: Default::default(),
public_identity_key_file: Default::default(),
@@ -430,13 +403,13 @@ impl<T: NymConfig> Client<T> {
}
}
#[derive(Debug, Clone, Default, Deserialize, PartialEq, Eq, Serialize)]
#[derive(Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Logging {}
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct DebugConfig {
pub struct Debug {
/// The parameter of Poisson distribution determining how long, on average,
/// sent packet is going to be delayed at any given mix node.
/// So for a packet going through three mix nodes, on average, it will take three times this value
@@ -502,7 +475,7 @@ pub struct DebugConfig {
pub use_extended_packet_size: Option<ExtendedPacketSize>,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum ExtendedPacketSize {
Extended8,
@@ -510,9 +483,9 @@ pub enum ExtendedPacketSize {
Extended32,
}
impl Default for DebugConfig {
impl Default for Debug {
fn default() -> Self {
DebugConfig {
Debug {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
-9
View File
@@ -1,8 +1,6 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#[cfg(feature = "reply-surb")]
use crate::client::reply_key_storage::ReplyKeyStorageError;
use crypto::asymmetric::identity::Ed25519RecoveryError;
use gateway_client::error::GatewayClientError;
use validator_client::ValidatorClientError;
@@ -18,10 +16,6 @@ pub enum ClientCoreError {
#[error("Validator client error: {0}")]
ValidatorClientError(#[from] ValidatorClientError),
#[cfg(feature = "reply-surb")]
#[error("Reply key storage error: {0}")]
ReplyKeyStorageError(#[from] ReplyKeyStorageError),
#[error("No gateway with id: {0}")]
NoGatewayWithId(String),
#[error("No gateways on network")]
@@ -32,7 +26,4 @@ pub enum ClientCoreError {
CouldNotLoadExistingGatewayConfiguration(std::io::Error),
#[error("The current network topology seem to be insufficient to route any packets through")]
InsufficientNetworkTopology,
#[error("Unexpected exit")]
UnexpectedExit,
}
+2 -1
View File
@@ -31,7 +31,7 @@ pub async fn query_gateway_details(
let validator_api = validator_servers
.choose(&mut thread_rng())
.ok_or(ClientCoreError::ListOfValidatorApisIsEmpty)?;
let validator_client = validator_client::client::ApiClient::new(validator_api.clone());
let validator_client = validator_client::ApiClient::new(validator_api.clone());
log::trace!("Fetching list of gateways from: {}", validator_api);
let gateways = validator_client.get_cached_gateways().await?;
@@ -90,6 +90,7 @@ async fn register_with_gateway(
gateway.owner.clone(),
our_identity.clone(),
timeout,
#[cfg(not(target_arch = "wasm32"))]
None,
);
gateway_client
+7 -14
View File
@@ -14,9 +14,8 @@ use credential_storage::PersistentStorage;
use credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES};
use credentials::coconut::utils::obtain_aggregate_signature;
use crypto::asymmetric::{encryption, identity};
use network_defaults::{NymNetworkDetails, VOUCHER_INFO};
use network_defaults::VOUCHER_INFO;
use validator_client::nymd::tx::Hash;
use validator_client::{CoconutApiClient, Config};
use crate::client::Client;
use crate::error::{CredentialClientError, Result};
@@ -108,9 +107,10 @@ pub(crate) struct GetCredential {
/// The hash of a successful deposit transaction
#[clap(long)]
tx_hash: String,
/// The nymd URL that should be used
/// The URLs to the validator-api endpoints the are run as coconut signer authorities, separated
/// by comma (,)
#[clap(long)]
nymd_url: String,
signer_authorities: String,
/// If we want to get the signature without attaching a blind sign request; it is expected that
/// there is already a signature stored on the signer
#[clap(long, parse(from_flag))]
@@ -124,10 +124,7 @@ impl Execute for GetCredential {
.get::<State>(&self.tx_hash)
.ok_or(CredentialClientError::NoDeposit)?;
let network_details = NymNetworkDetails::new_from_env();
let config = Config::try_from_nym_network_details(&network_details)?;
let client = validator_client::Client::new_query(config)?;
let coconut_api_clients = CoconutApiClient::all_coconut_api_clients(&client).await?;
let urls = config::parse_validators(&self.signer_authorities);
let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap();
let bandwidth_credential_attributes = if self.__no_request {
@@ -181,12 +178,8 @@ impl Execute for GetCredential {
)?);
db.set(&self.tx_hash, &state).unwrap();
let signature = obtain_aggregate_signature(
&params,
&bandwidth_credential_attributes,
&coconut_api_clients,
)
.await?;
let signature =
obtain_aggregate_signature(&params, &bandwidth_credential_attributes, &urls).await?;
shared_storage
.insert_coconut_credential(
state.amount.to_string(),
-4
View File
@@ -8,7 +8,6 @@ use credentials::error::Error as CredentialError;
use crypto::asymmetric::encryption::KeyRecoveryError;
use crypto::asymmetric::identity::Ed25519RecoveryError;
use validator_client::nymd::error::NymdError;
use validator_client::ValidatorClientError;
pub type Result<T> = std::result::Result<T, CredentialClientError>;
@@ -17,9 +16,6 @@ pub enum CredentialClientError {
#[error("Nymd error: {0}")]
Nymd(#[from] NymdError),
#[error("Validator client error: {0}")]
ValidatorClientError(#[from] ValidatorClientError),
#[error("Credential error: {0}")]
Credential(#[from] CredentialError),
+2 -3
View File
@@ -1,10 +1,10 @@
[package]
name = "nym-client"
version = "1.1.1"
version = "1.1.0"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
rust-version = "1.65"
rust-version = "1.56"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -51,7 +51,6 @@ topology = { path = "../../common/topology" }
validator-client = { path = "../../common/client-libs/validator-client", features = ["nymd-client"] }
version-checker = { path = "../../common/version-checker" }
websocket-requests = { path = "websocket-requests" }
tap = "1.0.1"
[features]
coconut = ["coconut-interface", "credentials", "credentials/coconut", "gateway-requests/coconut", "gateway-client/coconut", "client-core/coconut"]
@@ -43,7 +43,7 @@ async fn send_file_with_reply() {
recipient,
message: read_data,
with_reply_surb: true,
connection_id: Some(0),
connection_id: 0,
};
println!("sending content of 'dummy_file' over the mix network...");
@@ -92,7 +92,7 @@ async fn send_file_without_reply() {
recipient,
message: read_data,
with_reply_surb: false,
connection_id: Some(0),
connection_id: 0,
};
println!("sending content of 'dummy_file' over the mix network...");
-4
View File
@@ -27,10 +27,6 @@ impl SocketType {
_ => SocketType::None,
}
}
pub fn is_websocket(&self) -> bool {
matches!(self, SocketType::WebSocket)
}
}
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
@@ -23,13 +23,6 @@ id = '{{ client.id }}'
# to claim bandwidth without presenting bandwidth credentials.
disabled_credentials_mode = {{ client.disabled_credentials_mode }}
# Addresses to nymd validators via which the client can communicate with the chain.
validator_urls = [
{{#each client.validator_urls }}
'{{this}}',
{{/each}}
]
# Addresses to APIs running on validator from which the client gets the view of the network.
validator_api_urls = [
{{#each client.validator_api_urls }}
+407 -155
View File
@@ -1,100 +1,355 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::config::Config;
use crate::error::ClientError;
use crate::websocket;
use client_connections::TransmissionLane;
use client_core::client::base_client::{BaseClientBuilder, ClientInput, ClientOutput};
use client_core::client::inbound_messages::InputMessage;
use client_connections::{
ClosedConnectionReceiver, ClosedConnectionSender, LaneQueueLengths, TransmissionLane,
};
use client_core::client::cover_traffic_stream::LoopCoverTrafficStream;
use client_core::client::inbound_messages::{
InputMessage, InputMessageReceiver, InputMessageSender,
};
use client_core::client::key_manager::KeyManager;
use client_core::client::received_buffer::{ReceivedBufferMessage, ReconstructedMessagesReceiver};
use client_core::client::mix_traffic::{BatchMixMessageSender, MixTrafficController};
use client_core::client::real_messages_control;
use client_core::client::real_messages_control::RealMessagesController;
use client_core::client::received_buffer::{
ReceivedBufferMessage, ReceivedBufferRequestReceiver, ReceivedBufferRequestSender,
ReceivedMessagesBufferController, ReconstructedMessagesReceiver,
};
use client_core::client::reply_key_storage::ReplyKeyStorage;
use client_core::client::topology_control::{
TopologyAccessor, TopologyRefresher, TopologyRefresherConfig,
};
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
use client_core::error::ClientCoreError;
use crypto::asymmetric::identity;
use futures::channel::mpsc;
use gateway_client::bandwidth::BandwidthController;
use gateway_client::{
AcknowledgementReceiver, AcknowledgementSender, GatewayClient, MixnetMessageReceiver,
MixnetMessageSender,
};
use log::*;
use nymsphinx::addressing::clients::Recipient;
use nymsphinx::addressing::nodes::NodeIdentity;
use nymsphinx::anonymous_replies::ReplySurb;
use nymsphinx::receiver::ReconstructedMessage;
use task::{wait_for_signal, ShutdownNotifier};
use task::{wait_for_signal, ShutdownListener, ShutdownNotifier};
use crate::client::config::{Config, SocketType};
use crate::error::ClientError;
use crate::websocket;
pub(crate) mod config;
pub struct SocketClient {
pub struct NymClient {
/// Client configuration options, including, among other things, packet sending rates,
/// key filepaths, etc.
config: Config,
/// KeyManager object containing smart pointers to all relevant keys used by the client.
key_manager: KeyManager,
/// Channel used for transforming 'raw' messages into sphinx packets and sending them
/// through the mix network.
/// It is only available if the client started with the websocket listener disabled.
input_tx: Option<InputMessageSender>,
/// Channel used for obtaining reconstructed messages received from the mix network.
/// It is only available if the client started with the websocket listener disabled.
receive_tx: Option<ReconstructedMessagesReceiver>,
}
impl SocketClient {
impl NymClient {
pub fn new(config: Config) -> Self {
let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base());
let key_manager = KeyManager::load_keys(&pathfinder).expect("failed to load stored keys");
SocketClient {
NymClient {
config,
key_manager,
input_tx: None,
receive_tx: None,
}
}
async fn create_bandwidth_controller(config: &Config) -> BandwidthController {
pub fn as_mix_recipient(&self) -> Recipient {
Recipient::new(
*self.key_manager.identity_keypair().public_key(),
*self.key_manager.encryption_keypair().public_key(),
// TODO: below only works under assumption that gateway address == gateway id
// (which currently is true)
NodeIdentity::from_base58_string(self.config.get_base().get_gateway_id()).unwrap(),
)
}
// future constantly pumping loop cover traffic at some specified average rate
// the pumped traffic goes to the MixTrafficController
fn start_cover_traffic_stream(
&self,
topology_accessor: TopologyAccessor,
mix_tx: BatchMixMessageSender,
shutdown: ShutdownListener,
) {
info!("Starting loop cover traffic stream...");
let mut stream = LoopCoverTrafficStream::new(
self.key_manager.ack_key(),
self.config.get_base().get_average_ack_delay(),
self.config.get_base().get_average_packet_delay(),
self.config
.get_base()
.get_loop_cover_traffic_average_delay(),
mix_tx,
self.as_mix_recipient(),
topology_accessor,
);
if let Some(size) = self.config.get_base().get_use_extended_packet_size() {
log::debug!("Setting extended packet size: {:?}", size);
stream.set_custom_packet_size(size.into());
}
stream.start_with_shutdown(shutdown);
}
#[allow(clippy::too_many_arguments)]
fn start_real_traffic_controller(
&self,
topology_accessor: TopologyAccessor,
reply_key_storage: ReplyKeyStorage,
ack_receiver: AcknowledgementReceiver,
input_receiver: InputMessageReceiver,
mix_sender: BatchMixMessageSender,
lane_queue_lengths: LaneQueueLengths,
closed_connection_rx: ClosedConnectionReceiver,
shutdown: ShutdownListener,
) {
let mut controller_config = real_messages_control::Config::new(
self.key_manager.ack_key(),
self.config.get_base().get_ack_wait_multiplier(),
self.config.get_base().get_ack_wait_addition(),
self.config.get_base().get_average_ack_delay(),
self.config.get_base().get_message_sending_average_delay(),
self.config.get_base().get_average_packet_delay(),
self.config
.get_base()
.get_disabled_main_poisson_packet_distribution(),
self.as_mix_recipient(),
);
if let Some(size) = self.config.get_base().get_use_extended_packet_size() {
log::debug!("Setting extended packet size: {:?}", size);
controller_config.set_custom_packet_size(size.into());
}
info!("Starting real traffic stream...");
RealMessagesController::new(
controller_config,
ack_receiver,
input_receiver,
mix_sender,
topology_accessor,
reply_key_storage,
lane_queue_lengths,
closed_connection_rx,
)
.start_with_shutdown(shutdown);
}
// buffer controlling all messages fetched from provider
// required so that other components would be able to use them (say the websocket)
fn start_received_messages_buffer_controller(
&self,
query_receiver: ReceivedBufferRequestReceiver,
mixnet_receiver: MixnetMessageReceiver,
reply_key_storage: ReplyKeyStorage,
shutdown: ShutdownListener,
) {
info!("Starting received messages buffer controller...");
ReceivedMessagesBufferController::new(
self.key_manager.encryption_keypair(),
query_receiver,
mixnet_receiver,
reply_key_storage,
)
.start_with_shutdown(shutdown)
}
async fn start_gateway_client(
&mut self,
mixnet_message_sender: MixnetMessageSender,
ack_sender: AcknowledgementSender,
shutdown: ShutdownListener,
) -> GatewayClient {
let gateway_id = self.config.get_base().get_gateway_id();
if gateway_id.is_empty() {
panic!("The identity of the gateway is unknown - did you run `nym-client` init?")
}
let gateway_owner = self.config.get_base().get_gateway_owner();
if gateway_owner.is_empty() {
panic!("The owner of the gateway is unknown - did you run `nym-client` init?")
}
let gateway_address = self.config.get_base().get_gateway_listener();
if gateway_address.is_empty() {
panic!("The address of the gateway is unknown - did you run `nym-client` init?")
}
let gateway_identity = identity::PublicKey::from_base58_string(gateway_id)
.expect("provided gateway id is invalid!");
#[cfg(feature = "coconut")]
let bandwidth_controller = {
let details = network_defaults::NymNetworkDetails::new_from_env();
let client_config = validator_client::Config::try_from_nym_network_details(&details)
.expect("failed to construct validator client config");
let client = validator_client::Client::new_query(client_config)
.expect("Could not construct query client");
let coconut_api_clients =
validator_client::CoconutApiClient::all_coconut_api_clients(&client)
.await
.expect("Could not query api clients");
BandwidthController::new(
credential_storage::initialise_storage(config.get_base().get_database_path()).await,
coconut_api_clients,
)
};
let bandwidth_controller = BandwidthController::new(
credential_storage::initialise_storage(self.config.get_base().get_database_path())
.await,
self.config.get_base().get_validator_api_endpoints(),
);
#[cfg(not(feature = "coconut"))]
let bandwidth_controller = BandwidthController::new(
credential_storage::initialise_storage(config.get_base().get_database_path()).await,
credential_storage::initialise_storage(self.config.get_base().get_database_path())
.await,
)
.expect("Could not create bandwidth controller");
bandwidth_controller
let mut gateway_client = GatewayClient::new(
gateway_address,
self.key_manager.identity_keypair(),
gateway_identity,
gateway_owner,
Some(self.key_manager.gateway_shared_key()),
mixnet_message_sender,
ack_sender,
self.config.get_base().get_gateway_response_timeout(),
Some(bandwidth_controller),
Some(shutdown),
);
gateway_client
.set_disabled_credentials_mode(self.config.get_base().get_disabled_credentials_mode());
gateway_client
.authenticate_and_start()
.await
.expect("could not authenticate and start up the gateway connection");
gateway_client
}
// future responsible for periodically polling directory server and updating
// the current global view of topology
async fn start_topology_refresher(
&mut self,
topology_accessor: TopologyAccessor,
shutdown: ShutdownListener,
) -> Result<(), ClientError> {
let topology_refresher_config = TopologyRefresherConfig::new(
self.config.get_base().get_validator_api_endpoints(),
self.config.get_base().get_topology_refresh_rate(),
env!("CARGO_PKG_VERSION").to_string(),
);
let mut topology_refresher =
TopologyRefresher::new(topology_refresher_config, topology_accessor);
// before returning, block entire runtime to refresh the current network view so that any
// components depending on topology would see a non-empty view
info!("Obtaining initial network topology");
topology_refresher.refresh().await;
// TODO: a slightly more graceful termination here
if !topology_refresher.is_topology_routable().await {
log::error!(
"The current network topology seem to be insufficient to route any packets through \
- check if enough nodes and a gateway are online"
);
return Err(ClientCoreError::InsufficientNetworkTopology.into());
}
info!("Starting topology refresher...");
topology_refresher.start_with_shutdown(shutdown);
Ok(())
}
// controller for sending sphinx packets to mixnet (either real traffic or cover traffic)
// TODO: if we want to send control messages to gateway_client, this CAN'T take the ownership
// over it. Perhaps GatewayClient needs to be thread-shareable or have some channel for
// requests?
fn start_mix_traffic_controller(
gateway_client: GatewayClient,
shutdown: ShutdownListener,
) -> BatchMixMessageSender {
info!("Starting mix traffic controller...");
let (mix_traffic_controller, mix_tx) = MixTrafficController::new(gateway_client);
mix_traffic_controller.start_with_shutdown(shutdown);
mix_tx
}
fn start_websocket_listener(
config: &Config,
client_input: ClientInput,
client_output: ClientOutput,
self_address: Recipient,
&self,
buffer_requester: ReceivedBufferRequestSender,
msg_input: InputMessageSender,
lane_queue_lengths: LaneQueueLengths,
closed_connection_tx: ClosedConnectionSender,
) {
info!("Starting websocket listener...");
let ClientInput {
shared_lane_queue_lengths,
connection_command_sender,
input_sender,
} = client_input;
let received_buffer_request_sender = client_output.received_buffer_request_sender;
let websocket_handler = websocket::Handler::new(
input_sender,
connection_command_sender,
received_buffer_request_sender,
self_address,
shared_lane_queue_lengths,
msg_input,
closed_connection_tx,
buffer_requester,
&self.as_mix_recipient(),
lane_queue_lengths,
);
websocket::Listener::new(config.get_listening_port()).start(websocket_handler);
websocket::Listener::new(self.config.get_listening_port()).start(websocket_handler);
}
/// blocking version of `start_socket` method. Will run forever (or until SIGINT is sent)
pub async fn run_socket_forever(self) -> Result<(), ClientError> {
let shutdown = self.start_socket().await?;
/// EXPERIMENTAL DIRECT RUST API
/// It's untested and there are absolutely no guarantees about it (but seems to have worked
/// well enough in local tests)
pub fn send_message(&mut self, recipient: Recipient, message: Vec<u8>, with_reply_surb: bool) {
let lane = TransmissionLane::General;
let input_msg = InputMessage::new_fresh(recipient, message, with_reply_surb, lane);
self.input_tx
.as_ref()
.expect("start method was not called before!")
.unbounded_send(input_msg)
.unwrap();
}
/// EXPERIMENTAL DIRECT RUST API
/// It's untested and there are absolutely no guarantees about it (but seems to have worked
/// well enough in local tests)
pub fn send_reply(&mut self, reply_surb: ReplySurb, message: Vec<u8>) {
let input_msg = InputMessage::new_reply(reply_surb, message);
self.input_tx
.as_ref()
.expect("start method was not called before!")
.unbounded_send(input_msg)
.unwrap();
}
/// EXPERIMENTAL DIRECT RUST API
/// It's untested and there are absolutely no guarantees about it (but seems to have worked
/// well enough in local tests)
/// Note: it waits for the first occurrence of messages being sent to ourselves. If you expect multiple
/// messages, you might have to call this function repeatedly.
// TODO: I guess this should really return something that `impl Stream<Item=ReconstructedMessage>`
pub async fn wait_for_messages(&mut self) -> Vec<ReconstructedMessage> {
use futures::StreamExt;
self.receive_tx
.as_mut()
.expect("start method was not called before!")
.next()
.await
.expect("buffer controller seems to have somehow died!")
}
/// blocking version of `start` method. Will run forever (or until SIGINT is sent)
pub async fn run_forever(&mut self) -> Result<(), ClientError> {
let shutdown = self.start().await?;
wait_for_signal().await;
println!(
@@ -114,117 +369,114 @@ impl SocketClient {
Ok(())
}
pub async fn start_socket(self) -> Result<ShutdownNotifier, ClientError> {
if !self.config.get_socket_type().is_websocket() {
return Err(ClientError::InvalidSocketMode);
}
pub async fn start(&mut self) -> Result<ShutdownNotifier, ClientError> {
info!("Starting nym client");
// channels for inter-component communication
// TODO: make the channels be internally created by the relevant components
// rather than creating them here, so say for example the buffer controller would create the request channels
// and would allow anyone to clone the sender channel
let base_builder = BaseClientBuilder::new_from_base_config(
self.config.get_base(),
self.key_manager,
Some(Self::create_bandwidth_controller(&self.config).await),
// unwrapped_sphinx_sender is the transmitter of mixnet messages received from the gateway
// unwrapped_sphinx_receiver is the receiver for said messages - used by ReceivedMessagesBuffer
let (mixnet_messages_sender, mixnet_messages_receiver) = mpsc::unbounded();
// used for announcing connection or disconnection of a channel for pushing re-assembled messages to
let (received_buffer_request_sender, received_buffer_request_receiver) = mpsc::unbounded();
// channels responsible for controlling real messages
let (input_sender, input_receiver) = mpsc::unbounded::<InputMessage>();
// channels responsible for controlling ack messages
let (ack_sender, ack_receiver) = mpsc::unbounded();
let shared_topology_accessor = TopologyAccessor::new();
let reply_key_storage =
ReplyKeyStorage::load(self.config.get_base().get_reply_encryption_key_store_path())
.expect("Failed to load reply key storage!");
// Shutdown notifier for signalling tasks to stop
let shutdown = ShutdownNotifier::default();
// the components are started in very specific order. Unless you know what you are doing,
// do not change that.
self.start_topology_refresher(shared_topology_accessor.clone(), shutdown.subscribe())
.await?;
self.start_received_messages_buffer_controller(
received_buffer_request_receiver,
mixnet_messages_receiver,
reply_key_storage.clone(),
shutdown.subscribe(),
);
let self_address = base_builder.as_mix_recipient();
let mut started_client = base_builder.start_base().await?;
let client_input = started_client.client_input.register_producer();
let client_output = started_client.client_output.register_consumer();
let gateway_client = self
.start_gateway_client(mixnet_messages_sender, ack_sender, shutdown.subscribe())
.await;
Self::start_websocket_listener(&self.config, client_input, client_output, self_address);
// The sphinx_message_sender is the transmitter for any component generating sphinx packets
// that are to be sent to the mixnet. They are used by cover traffic stream and real
// traffic stream.
// The MixTrafficController then sends the actual traffic
let sphinx_message_sender =
Self::start_mix_traffic_controller(gateway_client, shutdown.subscribe());
// Channels that the websocket listener can use to signal downstream to the real traffic
// controller that connections are closed.
let (closed_connection_tx, closed_connection_rx) = mpsc::unbounded();
// Shared queue length data. Published by the `OutQueueController` in the client, and used
// primarily to throttle incoming connections (e.g socks5 for attached network-requesters)
let shared_lane_queue_lengths = LaneQueueLengths::new();
self.start_real_traffic_controller(
shared_topology_accessor.clone(),
reply_key_storage,
ack_receiver,
input_receiver,
sphinx_message_sender.clone(),
shared_lane_queue_lengths.clone(),
closed_connection_rx,
shutdown.subscribe(),
);
if !self
.config
.get_base()
.get_disabled_loop_cover_traffic_stream()
{
self.start_cover_traffic_stream(
shared_topology_accessor,
sphinx_message_sender,
shutdown.subscribe(),
);
}
match self.config.get_socket_type() {
SocketType::WebSocket => self.start_websocket_listener(
received_buffer_request_sender,
input_sender,
shared_lane_queue_lengths,
closed_connection_tx,
),
SocketType::None => {
// if we did not start the socket, it means we're running (supposedly) in the native mode
// and hence we should announce 'ourselves' to the buffer
let (reconstructed_sender, reconstructed_receiver) = mpsc::unbounded();
// tell the buffer to start sending stuff to us
received_buffer_request_sender
.unbounded_send(ReceivedBufferMessage::ReceiverAnnounce(
reconstructed_sender,
))
.expect("the buffer request failed!");
self.receive_tx = Some(reconstructed_receiver);
self.input_tx = Some(input_sender);
}
}
info!("Client startup finished!");
info!("The address of this client is: {}", self_address);
info!("The address of this client is: {}", self.as_mix_recipient());
Ok(started_client.shutdown_notifier)
}
pub async fn start_direct(self) -> Result<DirectClient, ClientError> {
if self.config.get_socket_type().is_websocket() {
return Err(ClientError::InvalidSocketMode);
}
let base_client = BaseClientBuilder::new_from_base_config(
self.config.get_base(),
self.key_manager,
Some(Self::create_bandwidth_controller(&self.config).await),
);
let mut started_client = base_client.start_base().await?;
let client_input = started_client.client_input.register_producer();
let client_output = started_client.client_output.register_consumer();
// register our receiver
let (reconstructed_sender, reconstructed_receiver) = mpsc::unbounded();
// tell the buffer to start sending stuff to us
client_output
.received_buffer_request_sender
.unbounded_send(ReceivedBufferMessage::ReceiverAnnounce(
reconstructed_sender,
))
.expect("the buffer request failed!");
Ok(DirectClient {
client_input,
reconstructed_receiver,
_shutdown_notifier: started_client.shutdown_notifier,
})
}
}
pub struct DirectClient {
client_input: ClientInput,
reconstructed_receiver: ReconstructedMessagesReceiver,
// we need to keep reference to this guy otherwise things will start dropping
_shutdown_notifier: ShutdownNotifier,
}
impl DirectClient {
/// EXPERIMENTAL DIRECT RUST API
/// It's untested and there are absolutely no guarantees about it (but seems to have worked
/// well enough in local tests)
pub async fn send_message(
&mut self,
recipient: Recipient,
message: Vec<u8>,
with_reply_surb: bool,
) {
let lane = TransmissionLane::General;
let input_msg = InputMessage::new_fresh(recipient, message, with_reply_surb, lane);
self.client_input
.input_sender
.send(input_msg)
.await
.expect("InputMessageReceiver has stopped receiving!");
}
/// EXPERIMENTAL DIRECT RUST API
/// It's untested and there are absolutely no guarantees about it (but seems to have worked
/// well enough in local tests)
pub async fn send_reply(&mut self, reply_surb: ReplySurb, message: Vec<u8>) {
let input_msg = InputMessage::new_reply(reply_surb, message);
self.client_input
.input_sender
.send(input_msg)
.await
.expect("InputMessageReceiver has stopped receiving!");
}
/// EXPERIMENTAL DIRECT RUST API
/// It's untested and there are absolutely no guarantees about it (but seems to have worked
/// well enough in local tests)
/// Note: it waits for the first occurrence of messages being sent to ourselves. If you expect multiple
/// messages, you might have to call this function repeatedly.
// TODO: I guess this should really return something that `impl Stream<Item=ReconstructedMessage>`
pub async fn wait_for_messages(&mut self) -> Vec<ReconstructedMessage> {
use futures::StreamExt;
self.reconstructed_receiver
.next()
.await
.expect("buffer controller seems to have somehow died!")
Ok(shutdown)
}
}
+5 -10
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use clap::Args;
use client_core::{config::GatewayEndpointConfig, error::ClientCoreError};
use client_core::{config::GatewayEndpoint, error::ClientCoreError};
use config::NymConfig;
use crate::{
@@ -25,13 +25,9 @@ pub(crate) struct Init {
#[clap(long)]
force_register_gateway: bool,
/// Comma separated list of rest endpoints of the nymd validators
/// Comma separated list of rest endpoints of the validators
#[clap(long)]
nymd_validators: Option<String>,
/// Comma separated list of rest endpoints of the API validators
#[clap(long)]
api_validators: Option<String>,
validators: Option<String>,
/// Whether to not start the websocket
#[clap(long)]
@@ -56,8 +52,7 @@ pub(crate) struct Init {
impl From<Init> for OverrideConfig {
fn from(init_config: Init) -> Self {
OverrideConfig {
nymd_validators: init_config.nymd_validators,
api_validators: init_config.api_validators,
validators: init_config.validators,
disable_socket: init_config.disable_socket,
port: init_config.port,
fastmode: init_config.fastmode,
@@ -132,7 +127,7 @@ async fn setup_gateway(
register: bool,
user_chosen_gateway_id: Option<&str>,
config: &Config,
) -> Result<GatewayEndpointConfig, ClientCoreError> {
) -> Result<GatewayEndpoint, ClientCoreError> {
if register {
// Get the gateway details by querying the validator-api. Either pick one at random or use
// the chosen one if it's among the available ones.
+2 -14
View File
@@ -75,8 +75,7 @@ pub(crate) enum Commands {
// Configuration that can be overridden.
pub(crate) struct OverrideConfig {
nymd_validators: Option<String>,
api_validators: Option<String>,
validators: Option<String>,
disable_socket: bool,
port: Option<u16>,
fastmode: bool,
@@ -99,18 +98,7 @@ pub(crate) async fn execute(args: &Cli) -> Result<(), ClientError> {
}
pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Config {
if let Some(raw_validators) = args.nymd_validators {
config
.get_base_mut()
.set_custom_validators(config::parse_validators(&raw_validators));
} else if std::env::var(network_defaults::var_names::CONFIGURED).is_ok() {
let raw_validators = std::env::var(network_defaults::var_names::NYMD_VALIDATOR)
.expect("nymd validator not set");
config
.get_base_mut()
.set_custom_validators(config::parse_validators(&raw_validators));
}
if let Some(raw_validators) = args.api_validators {
if let Some(raw_validators) = args.validators {
config
.get_base_mut()
.set_custom_validator_apis(config::parse_validators(&raw_validators));
+5 -10
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::{
client::{config::Config, SocketClient},
client::{config::Config, NymClient},
commands::{override_config, OverrideConfig},
error::ClientError,
};
@@ -18,13 +18,9 @@ pub(crate) struct Run {
#[clap(long)]
id: String,
/// Comma separated list of rest endpoints of the nymd validators
/// Comma separated list of rest endpoints of the validators
#[clap(long)]
nymd_validators: Option<String>,
/// Comma separated list of rest endpoints of the API validators
#[clap(long)]
api_validators: Option<String>,
validators: Option<String>,
/// Id of the gateway we want to connect to. If overridden, it is user's responsibility to
/// ensure prior registration happened
@@ -49,8 +45,7 @@ pub(crate) struct Run {
impl From<Run> for OverrideConfig {
fn from(run_config: Run) -> Self {
OverrideConfig {
nymd_validators: run_config.nymd_validators,
api_validators: run_config.api_validators,
validators: run_config.validators,
disable_socket: run_config.disable_socket,
port: run_config.port,
fastmode: false,
@@ -98,5 +93,5 @@ pub(crate) async fn execute(args: &Run) -> Result<(), ClientError> {
return Err(ClientError::FailedLocalVersionCheck);
}
SocketClient::new(config).run_socket_forever().await
NymClient::new(config).run_forever().await
}
+11 -3
View File
@@ -1,7 +1,18 @@
use client_core::error::ClientCoreError;
use crypto::asymmetric::identity::Ed25519RecoveryError;
use gateway_client::error::GatewayClientError;
use validator_client::ValidatorClientError;
#[derive(thiserror::Error, Debug)]
pub enum ClientError {
#[error("I/O error: {0}")]
IoError(#[from] std::io::Error),
#[error("Gateway client error: {0}")]
GatewayClientError(#[from] GatewayClientError),
#[error("Ed25519 error: {0}")]
Ed25519RecoveryError(#[from] Ed25519RecoveryError),
#[error("Validator client error: {0}")]
ValidatorClientError(#[from] ValidatorClientError),
#[error("client-core error: {0}")]
ClientCoreError(#[from] ClientCoreError),
@@ -9,7 +20,4 @@ pub enum ClientError {
FailedToLoadConfig(String),
#[error("Failed local version check, client and config mismatch")]
FailedLocalVersionCheck,
#[error("Attempted to start the client in invalid socket mode")]
InvalidSocketMode,
}
+35 -88
View File
@@ -1,9 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use client_connections::{
ConnectionCommand, ConnectionCommandSender, LaneQueueLengths, TransmissionLane,
};
use client_connections::{ClosedConnectionSender, LaneQueueLengths, TransmissionLane};
use client_core::client::{
inbound_messages::{InputMessage, InputMessageSender},
received_buffer::{
@@ -37,7 +35,7 @@ impl Default for ReceivedResponseType {
pub(crate) struct Handler {
msg_input: InputMessageSender,
client_connection_tx: ConnectionCommandSender,
closed_connection_tx: ClosedConnectionSender,
buffer_requester: ReceivedBufferRequestSender,
self_full_address: Recipient,
socket: Option<WebSocketStream<TcpStream>>,
@@ -50,7 +48,7 @@ impl Clone for Handler {
fn clone(&self) -> Self {
Handler {
msg_input: self.msg_input.clone(),
client_connection_tx: self.client_connection_tx.clone(),
closed_connection_tx: self.closed_connection_tx.clone(),
buffer_requester: self.buffer_requester.clone(),
self_full_address: self.self_full_address,
socket: None,
@@ -71,85 +69,51 @@ impl Drop for Handler {
impl Handler {
pub(crate) fn new(
msg_input: InputMessageSender,
client_connection_tx: ConnectionCommandSender,
closed_connection_tx: ClosedConnectionSender,
buffer_requester: ReceivedBufferRequestSender,
self_full_address: Recipient,
self_full_address: &Recipient,
lane_queue_lengths: LaneQueueLengths,
) -> Self {
Handler {
msg_input,
client_connection_tx,
closed_connection_tx,
buffer_requester,
self_full_address,
self_full_address: *self_full_address,
socket: None,
received_response_type: Default::default(),
lane_queue_lengths,
}
}
async fn handle_send(
fn handle_send(
&mut self,
recipient: &Recipient,
message: Vec<u8>,
with_reply_surb: bool,
connection_id: Option<u64>,
connection_id: u64,
) -> Option<ServerResponse> {
// We map the absence of a connection id as going into the general lane.
let lane = connection_id.map_or(TransmissionLane::General, |id| {
TransmissionLane::ConnectionId(id)
});
// the ack control is now responsible for chunking, etc.
let lane = TransmissionLane::ConnectionId(connection_id);
let input_msg = InputMessage::new_fresh(*recipient, message, with_reply_surb, lane);
self.msg_input
.send(input_msg)
.await
.expect("InputMessageReceiver has stopped receiving!");
self.msg_input.unbounded_send(input_msg).unwrap();
// Only reply back with a `LaneQueueLength` if the sender providided a connection id
let connection_id = match lane {
TransmissionLane::General
| TransmissionLane::Reply
| TransmissionLane::Retransmission
| TransmissionLane::Control => return None,
TransmissionLane::ConnectionId(id) => id,
};
// on receiving a send, we reply back the current lane queue length for that connection id.
// Note that this does _NOT_ take into account the packets that have been received but not
// yet reach `OutQueueControl`, so it might be a tad low.
let Ok(lane_queue_lengths) = self.lane_queue_lengths.lock() else {
log::warn!(
"Failed to get the lane queue length lock, \
not responding back with the current queue length"
);
return None;
};
let queue_length = lane_queue_lengths.get(&lane).unwrap_or(0);
Some(ServerResponse::LaneQueueLength(connection_id, queue_length))
// WIP(JON)
let queue_lengh = self
.lane_queue_lengths
.lock()
.unwrap()
.get(&lane)
.unwrap_or(0);
Some(ServerResponse::LaneQueueLength(connection_id, queue_lengh))
}
async fn handle_reply(
&mut self,
reply_surb: ReplySurb,
message: Vec<u8>,
) -> Option<ServerResponse> {
fn handle_reply(&mut self, reply_surb: ReplySurb, message: Vec<u8>) -> Option<ServerResponse> {
if message.len() > ReplySurb::max_msg_len(Default::default()) {
return Some(
ServerResponse::new_error(
format!(
"too long message to put inside a reply SURB. Received: {} bytes and maximum is {} bytes",
message.len(), ReplySurb::max_msg_len(Default::default()))
)
);
return Some(ServerResponse::new_error(format!("too long message to put inside a reply SURB. Received: {} bytes and maximum is {} bytes", message.len(), ReplySurb::max_msg_len(Default::default()))));
}
let input_msg = InputMessage::new_reply(reply_surb, message);
self.msg_input
.send(input_msg)
.await
.expect("InputMessageReceiver has stopped receiving!");
self.msg_input.unbounded_send(input_msg).unwrap();
None
}
@@ -159,47 +123,30 @@ impl Handler {
}
fn handle_closed_connection(&self, connection_id: u64) -> Option<ServerResponse> {
self.client_connection_tx
.unbounded_send(ConnectionCommand::Close(connection_id))
self.closed_connection_tx
.unbounded_send(connection_id)
.unwrap();
None
}
fn handle_get_lane_queue_length(&self, connection_id: u64) -> Option<ServerResponse> {
let Ok(lane_queue_lengths) = self.lane_queue_lengths.lock() else {
log::warn!(
"Failed to get the lane queue length lock, not responding back with the current queue length"
);
return None;
};
let lane = TransmissionLane::ConnectionId(connection_id);
let queue_length = lane_queue_lengths.get(&lane).unwrap_or(0);
Some(ServerResponse::LaneQueueLength(connection_id, queue_length))
}
async fn handle_request(&mut self, request: ClientRequest) -> Option<ServerResponse> {
fn handle_request(&mut self, request: ClientRequest) -> Option<ServerResponse> {
match request {
ClientRequest::Send {
recipient,
message,
with_reply_surb,
connection_id,
} => {
self.handle_send(&recipient, message, with_reply_surb, connection_id)
.await
}
} => self.handle_send(&recipient, message, with_reply_surb, connection_id),
ClientRequest::Reply {
message,
reply_surb,
} => self.handle_reply(reply_surb, message).await,
} => self.handle_reply(reply_surb, message),
ClientRequest::SelfAddress => Some(self.handle_self_address()),
ClientRequest::ClosedConnection(id) => self.handle_closed_connection(id),
ClientRequest::GetLaneQueueLength(id) => self.handle_get_lane_queue_length(id),
}
}
async fn handle_text_message(&mut self, msg: String) -> Option<WsMessage> {
fn handle_text_message(&mut self, msg: String) -> Option<WsMessage> {
debug!("Handling text message request");
trace!("Content: {:?}", msg);
@@ -208,13 +155,13 @@ impl Handler {
let response = match client_request {
Err(err) => Some(ServerResponse::Error(err)),
Ok(req) => self.handle_request(req).await,
Ok(req) => self.handle_request(req),
};
response.map(|resp| WsMessage::text(resp.into_text()))
}
async fn handle_binary_message(&mut self, msg: &[u8]) -> Option<WsMessage> {
fn handle_binary_message(&mut self, msg: &[u8]) -> Option<WsMessage> {
debug!("Handling binary message request");
self.received_response_type = ReceivedResponseType::Binary;
@@ -222,19 +169,19 @@ impl Handler {
let response = match client_request {
Err(err) => Some(ServerResponse::Error(err)),
Ok(req) => self.handle_request(req).await,
Ok(req) => self.handle_request(req),
};
response.map(|resp| WsMessage::Binary(resp.into_binary()))
}
async fn handle_ws_request(&mut self, raw_request: WsMessage) -> Option<WsMessage> {
fn handle_ws_request(&mut self, raw_request: WsMessage) -> Option<WsMessage> {
// apparently tungstenite auto-handles ping/pong/close messages so for now let's ignore
// them and let's test that claim. If that's not the case, just copy code from
// old version of this file.
match raw_request {
WsMessage::Text(text_message) => self.handle_text_message(text_message).await,
WsMessage::Binary(binary_message) => self.handle_binary_message(&binary_message).await,
WsMessage::Text(text_message) => self.handle_text_message(text_message),
WsMessage::Binary(binary_message) => self.handle_binary_message(&binary_message),
_ => None,
}
}
@@ -296,7 +243,7 @@ impl Handler {
break;
}
if let Some(response) = self.handle_ws_request(socket_msg).await {
if let Some(response) = self.handle_ws_request(socket_msg) {
if let Err(err) = self.send_websocket_response(response).await {
warn!(
"Failed to send message over websocket: {}. Assuming the connection is dead.",
@@ -23,9 +23,6 @@ pub const SELF_ADDRESS_REQUEST_TAG: u8 = 0x02;
/// Value tag representing [`ClosedConnection`] variant of the [`ClientRequest`]
pub const CLOSED_CONNECTION_REQUEST_TAG: u8 = 0x03;
/// Value tag representing [`GetLaneQueueLength`] variant of the [`ClientRequest`]
pub const GET_LANE_QUEUE_LENGHT_TAG: u8 = 0x04;
#[allow(non_snake_case)]
#[derive(Debug)]
pub enum ClientRequest {
@@ -34,7 +31,7 @@ pub enum ClientRequest {
message: Vec<u8>,
// Perhaps we could change it to a number to indicate how many reply_SURBs we want to include?
with_reply_surb: bool,
connection_id: Option<u64>,
connection_id: u64,
},
Reply {
message: Vec<u8>,
@@ -42,7 +39,6 @@ pub enum ClientRequest {
},
SelfAddress,
ClosedConnection(u64),
GetLaneQueueLength(u64),
}
// we could have been parsing it directly TryFrom<WsMessage>, but we want to retain
@@ -53,10 +49,10 @@ impl ClientRequest {
recipient: Recipient,
data: Vec<u8>,
with_reply_surb: bool,
connection_id: Option<u64>,
connection_id: u64,
) -> Vec<u8> {
let data_len_bytes = (data.len() as u64).to_be_bytes();
let conn_id_bytes = connection_id.unwrap_or(0).to_be_bytes();
let conn_id_bytes = connection_id.to_be_bytes();
std::iter::once(SEND_REQUEST_TAG)
.chain(std::iter::once(with_reply_surb as u8))
.chain(recipient.to_bytes().iter().cloned()) // will not be length prefixed because the length is constant
@@ -106,11 +102,6 @@ impl ClientRequest {
connection_id_bytes
.copy_from_slice(&b[2 + Recipient::LEN..2 + Recipient::LEN + size_of::<u64>()]);
let connection_id = u64::from_be_bytes(connection_id_bytes);
let connection_id = if connection_id == 0 {
None
} else {
Some(connection_id)
};
let data_len_bytes =
&b[2 + Recipient::LEN + size_of::<u64>()..2 + Recipient::LEN + 2 * size_of::<u64>()];
@@ -239,14 +230,7 @@ impl ClientRequest {
}
// CLOSED_CONNECTION_REQUEST_TAG
fn deserialize_closed_connection(b: &[u8]) -> Result<Self, error::Error> {
if b.len() != 1 + size_of::<u64>() {
return Err(error::Error::new(
ErrorKind::MalformedRequest,
"the received closed connection has invalid length".to_string(),
));
}
fn deserialize_closed_connection(b: &[u8]) -> Self {
// this MUST match because it was called by 'deserialize'
debug_assert_eq!(b[0], CLOSED_CONNECTION_REQUEST_TAG);
@@ -254,34 +238,7 @@ impl ClientRequest {
connection_id_bytes.copy_from_slice(&b[1..=size_of::<u64>()]);
let connection_id = u64::from_be_bytes(connection_id_bytes);
Ok(ClientRequest::ClosedConnection(connection_id))
}
// GET_LANE_QUEUE_LENGHT_TAG
fn serialize_get_lane_queue_lengths(connection_id: u64) -> Vec<u8> {
let conn_id_bytes = connection_id.to_be_bytes();
std::iter::once(GET_LANE_QUEUE_LENGHT_TAG)
.chain(conn_id_bytes.iter().copied())
.collect()
}
// GET_LANE_QUEUE_LENGHT_TAG
fn deserialize_get_lane_queue_length(b: &[u8]) -> Result<Self, error::Error> {
if b.len() != 1 + size_of::<u64>() {
return Err(error::Error::new(
ErrorKind::MalformedRequest,
"the received get lane queue length has invalid length".to_string(),
));
}
// this MUST match because it was called by 'deserialize'
debug_assert_eq!(b[0], GET_LANE_QUEUE_LENGHT_TAG);
let mut connection_id_bytes = [0u8; size_of::<u64>()];
connection_id_bytes.copy_from_slice(&b[1..=size_of::<u64>()]);
let connection_id = u64::from_be_bytes(connection_id_bytes);
Ok(ClientRequest::GetLaneQueueLength(connection_id))
ClientRequest::ClosedConnection(connection_id)
}
pub fn serialize(self) -> Vec<u8> {
@@ -301,8 +258,6 @@ impl ClientRequest {
ClientRequest::SelfAddress => Self::serialize_self_address(),
ClientRequest::ClosedConnection(id) => Self::serialize_closed_connection(id),
ClientRequest::GetLaneQueueLength(id) => Self::serialize_get_lane_queue_lengths(id),
}
}
@@ -332,8 +287,7 @@ impl ClientRequest {
SEND_REQUEST_TAG => Self::deserialize_send(b),
REPLY_REQUEST_TAG => Self::deserialize_reply(b),
SELF_ADDRESS_REQUEST_TAG => Ok(Self::deserialize_self_address(b)),
CLOSED_CONNECTION_REQUEST_TAG => Self::deserialize_closed_connection(b),
GET_LANE_QUEUE_LENGHT_TAG => Self::deserialize_get_lane_queue_length(b),
CLOSED_CONNECTION_REQUEST_TAG => Ok(Self::deserialize_closed_connection(b)),
n => Err(error::Error::new(
ErrorKind::UnknownRequest,
format!("type {n}"),
@@ -369,7 +323,7 @@ mod tests {
recipient,
message: b"foomp".to_vec(),
with_reply_surb: false,
connection_id: Some(42),
connection_id: 42,
};
let bytes = send_request_no_surb.serialize();
@@ -384,7 +338,7 @@ mod tests {
assert_eq!(recipient.to_string(), recipient_string);
assert_eq!(message, b"foomp".to_vec());
assert!(!with_reply_surb);
assert_eq!(connection_id, Some(42))
assert_eq!(connection_id, 42)
}
_ => unreachable!(),
}
@@ -393,7 +347,7 @@ mod tests {
recipient,
message: b"foomp".to_vec(),
with_reply_surb: true,
connection_id: None,
connection_id: 213,
};
let bytes = send_request_surb.serialize();
@@ -408,7 +362,7 @@ mod tests {
assert_eq!(recipient.to_string(), recipient_string);
assert_eq!(message, b"foomp".to_vec());
assert!(with_reply_surb);
assert_eq!(connection_id, None)
assert_eq!(connection_id, 213)
}
_ => unreachable!(),
}
@@ -20,7 +20,7 @@ pub(super) enum ClientRequestText {
message: String,
recipient: String,
with_reply_surb: bool,
connection_id: Option<u64>,
connection_id: u64,
},
SelfAddress,
#[serde(rename_all = "camelCase")]
+1 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.1"
version = "1.1.0"
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"
@@ -46,7 +46,6 @@ task = { path = "../../common/task" }
topology = { path = "../../common/topology" }
validator-client = { path = "../../common/client-libs/validator-client", features = ["nymd-client"] }
version-checker = { path = "../../common/version-checker" }
tap = "1.0.1"
[features]
coconut = ["coconut-interface", "credentials", "gateway-requests/coconut", "gateway-client/coconut", "credentials/coconut", "client-core/coconut"]
@@ -23,13 +23,6 @@ id = '{{ client.id }}'
# to claim bandwidth without presenting bandwidth credentials.
disabled_credentials_mode = {{ client.disabled_credentials_mode }}
# Addresses to nymd validators via which the client can communicate with the chain.
validator_urls = [
{{#each client.validator_urls }}
'{{this}}',
{{/each}}
]
# Addresses to APIs running on validator from which the client gets the view of the network.
validator_api_urls = [
{{#each client.validator_api_urls }}
+338 -98
View File
@@ -1,22 +1,43 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::sync::atomic::Ordering;
use crate::client::config::Config;
use crate::error::Socks5ClientError;
use crate::socks::{
authentication::{AuthenticationMethods, Authenticator, User},
server::SphinxSocksServer,
};
use client_core::client::base_client::{BaseClientBuilder, ClientInput, ClientOutput};
use client_connections::{ClosedConnectionReceiver, ClosedConnectionSender, LaneQueueLengths};
use client_core::client::cover_traffic_stream::LoopCoverTrafficStream;
use client_core::client::inbound_messages::{
InputMessage, InputMessageReceiver, InputMessageSender,
};
use client_core::client::key_manager::KeyManager;
use client_core::client::mix_traffic::{BatchMixMessageSender, MixTrafficController};
use client_core::client::real_messages_control::RealMessagesController;
use client_core::client::received_buffer::{
ReceivedBufferRequestReceiver, ReceivedBufferRequestSender, ReceivedMessagesBufferController,
};
use client_core::client::reply_key_storage::ReplyKeyStorage;
use client_core::client::topology_control::{
TopologyAccessor, TopologyRefresher, TopologyRefresherConfig,
};
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
use client_core::error::ClientCoreError;
use crypto::asymmetric::identity;
use futures::channel::mpsc;
use futures::StreamExt;
use gateway_client::bandwidth::BandwidthController;
use gateway_client::{
AcknowledgementReceiver, AcknowledgementSender, GatewayClient, MixnetMessageReceiver,
MixnetMessageSender,
};
use log::*;
use nymsphinx::addressing::clients::Recipient;
use std::error::Error;
use task::{wait_for_signal_and_error, ShutdownListener, ShutdownNotifier};
use nymsphinx::addressing::nodes::NodeIdentity;
use task::{wait_for_signal, ShutdownListener, ShutdownNotifier};
pub mod config;
@@ -50,112 +71,267 @@ impl NymClient {
}
}
async fn create_bandwidth_controller(config: &Config) -> BandwidthController {
pub fn as_mix_recipient(&self) -> Recipient {
Recipient::new(
*self.key_manager.identity_keypair().public_key(),
*self.key_manager.encryption_keypair().public_key(),
// TODO: below only works under assumption that gateway address == gateway id
// (which currently is true)
NodeIdentity::from_base58_string(self.config.get_base().get_gateway_id()).unwrap(),
)
}
// future constantly pumping loop cover traffic at some specified average rate
// the pumped traffic goes to the MixTrafficController
fn start_cover_traffic_stream(
&self,
topology_accessor: TopologyAccessor,
mix_tx: BatchMixMessageSender,
shutdown: ShutdownListener,
) {
info!("Starting loop cover traffic stream...");
let mut stream = LoopCoverTrafficStream::new(
self.key_manager.ack_key(),
self.config.get_base().get_average_ack_delay(),
self.config.get_base().get_average_packet_delay(),
self.config
.get_base()
.get_loop_cover_traffic_average_delay(),
mix_tx,
self.as_mix_recipient(),
topology_accessor,
);
if let Some(size) = self.config.get_base().get_use_extended_packet_size() {
log::debug!("Setting extended packet size: {:?}", size);
stream.set_custom_packet_size(size.into());
}
stream.start_with_shutdown(shutdown);
}
#[allow(clippy::too_many_arguments)]
fn start_real_traffic_controller(
&self,
topology_accessor: TopologyAccessor,
reply_key_storage: ReplyKeyStorage,
ack_receiver: AcknowledgementReceiver,
input_receiver: InputMessageReceiver,
mix_sender: BatchMixMessageSender,
closed_connection_rx: ClosedConnectionReceiver,
lane_queue_lengths: LaneQueueLengths,
shutdown: ShutdownListener,
) {
let mut controller_config = client_core::client::real_messages_control::Config::new(
self.key_manager.ack_key(),
self.config.get_base().get_ack_wait_multiplier(),
self.config.get_base().get_ack_wait_addition(),
self.config.get_base().get_average_ack_delay(),
self.config.get_base().get_message_sending_average_delay(),
self.config.get_base().get_average_packet_delay(),
self.config
.get_base()
.get_disabled_main_poisson_packet_distribution(),
self.as_mix_recipient(),
);
if let Some(size) = self.config.get_base().get_use_extended_packet_size() {
log::debug!("Setting extended packet size: {:?}", size);
controller_config.set_custom_packet_size(size.into());
}
info!("Starting real traffic stream...");
RealMessagesController::new(
controller_config,
ack_receiver,
input_receiver,
mix_sender,
topology_accessor,
reply_key_storage,
lane_queue_lengths,
closed_connection_rx,
)
.start_with_shutdown(shutdown);
}
// buffer controlling all messages fetched from provider
// required so that other components would be able to use them (say the websocket)
fn start_received_messages_buffer_controller(
&self,
query_receiver: ReceivedBufferRequestReceiver,
mixnet_receiver: MixnetMessageReceiver,
reply_key_storage: ReplyKeyStorage,
shutdown: ShutdownListener,
) {
info!("Starting received messages buffer controller...");
ReceivedMessagesBufferController::new(
self.key_manager.encryption_keypair(),
query_receiver,
mixnet_receiver,
reply_key_storage,
)
.start_with_shutdown(shutdown);
}
async fn start_gateway_client(
&mut self,
mixnet_message_sender: MixnetMessageSender,
ack_sender: AcknowledgementSender,
shutdown: ShutdownListener,
) -> GatewayClient {
let gateway_id = self.config.get_base().get_gateway_id();
if gateway_id.is_empty() {
panic!("The identity of the gateway is unknown - did you run `nym-client` init?")
}
let gateway_owner = self.config.get_base().get_gateway_owner();
if gateway_owner.is_empty() {
panic!("The owner of the gateway is unknown - did you run `nym-client` init?")
}
let gateway_address = self.config.get_base().get_gateway_listener();
if gateway_address.is_empty() {
panic!("The address of the gateway is unknown - did you run `nym-client` init?")
}
let gateway_identity = identity::PublicKey::from_base58_string(gateway_id)
.expect("provided gateway id is invalid!");
#[cfg(feature = "coconut")]
let bandwidth_controller = {
let details = network_defaults::NymNetworkDetails::new_from_env();
let client_config = validator_client::Config::try_from_nym_network_details(&details)
.expect("failed to construct validator client config");
let client = validator_client::Client::new_query(client_config)
.expect("Could not construct query client");
let coconut_api_clients =
validator_client::CoconutApiClient::all_coconut_api_clients(&client)
.await
.expect("Could not query api clients");
BandwidthController::new(
credential_storage::initialise_storage(config.get_base().get_database_path()).await,
coconut_api_clients,
)
};
let bandwidth_controller = BandwidthController::new(
credential_storage::initialise_storage(self.config.get_base().get_database_path())
.await,
self.config.get_base().get_validator_api_endpoints(),
);
#[cfg(not(feature = "coconut"))]
let bandwidth_controller = BandwidthController::new(
credential_storage::initialise_storage(config.get_base().get_database_path()).await,
credential_storage::initialise_storage(self.config.get_base().get_database_path())
.await,
)
.expect("Could not create bandwidth controller");
bandwidth_controller
let mut gateway_client = GatewayClient::new(
gateway_address,
self.key_manager.identity_keypair(),
gateway_identity,
gateway_owner,
Some(self.key_manager.gateway_shared_key()),
mixnet_message_sender,
ack_sender,
self.config.get_base().get_gateway_response_timeout(),
Some(bandwidth_controller),
Some(shutdown),
);
gateway_client
.set_disabled_credentials_mode(self.config.get_base().get_disabled_credentials_mode());
gateway_client
.authenticate_and_start()
.await
.expect("could not authenticate and start up the gateway connection");
gateway_client
}
// future responsible for periodically polling directory server and updating
// the current global view of topology
async fn start_topology_refresher(
&mut self,
topology_accessor: TopologyAccessor,
shutdown: ShutdownListener,
) -> Result<(), Socks5ClientError> {
let topology_refresher_config = TopologyRefresherConfig::new(
self.config.get_base().get_validator_api_endpoints(),
self.config.get_base().get_topology_refresh_rate(),
env!("CARGO_PKG_VERSION").to_string(),
);
let mut topology_refresher =
TopologyRefresher::new(topology_refresher_config, topology_accessor);
// before returning, block entire runtime to refresh the current network view so that any
// components depending on topology would see a non-empty view
info!("Obtaining initial network topology");
topology_refresher.refresh().await;
// TODO: a slightly more graceful termination here
if !topology_refresher.is_topology_routable().await {
log::error!(
"The current network topology seem to be insufficient to route any packets through \
- check if enough nodes and a gateway are online"
);
return Err(ClientCoreError::InsufficientNetworkTopology.into());
}
info!("Starting topology refresher...");
topology_refresher.start_with_shutdown(shutdown);
Ok(())
}
// controller for sending sphinx packets to mixnet (either real traffic or cover traffic)
// TODO: if we want to send control messages to gateway_client, this CAN'T take the ownership
// over it. Perhaps GatewayClient needs to be thread-shareable or have some channel for
// requests?
fn start_mix_traffic_controller(
gateway_client: GatewayClient,
shutdown: ShutdownListener,
) -> BatchMixMessageSender {
info!("Starting mix traffic controller...");
let (mix_traffic_controller, mix_tx) = MixTrafficController::new(gateway_client);
mix_traffic_controller.start_with_shutdown(shutdown);
mix_tx
}
fn start_socks5_listener(
config: &Config,
client_input: ClientInput,
client_output: ClientOutput,
self_address: Recipient,
mut shutdown: ShutdownListener,
&self,
buffer_requester: ReceivedBufferRequestSender,
msg_input: InputMessageSender,
closed_connection_tx: ClosedConnectionSender,
lane_queue_lengths: LaneQueueLengths,
shutdown: ShutdownListener,
) {
info!("Starting socks5 listener...");
let auth_methods = vec![AuthenticationMethods::NoAuth as u8];
let allowed_users: Vec<User> = Vec::new();
let ClientInput {
shared_lane_queue_lengths,
connection_command_sender,
input_sender,
} = client_input;
let received_buffer_request_sender = client_output.received_buffer_request_sender;
let authenticator = Authenticator::new(auth_methods, allowed_users);
let mut sphinx_socks = SphinxSocksServer::new(
config.get_listening_port(),
self.config.get_listening_port(),
authenticator,
config.get_provider_mix_address(),
self_address,
shared_lane_queue_lengths,
shutdown.clone(),
self.config.get_provider_mix_address(),
self.as_mix_recipient(),
lane_queue_lengths,
shutdown,
);
tokio::spawn(async move {
// Ideally we should have a fully fledged task manager to check for errors in all
// tasks.
// However, pragmatically, we start out by at least reporting errors for some of the
// tasks that interact with the outside world and can fail in normal operation, such as
// network issues.
// TODO: replace this by a generic solution, such as a task manager that stores all
// JoinHandles of all spawned tasks.
if let Err(res) = sphinx_socks
.serve(
input_sender,
received_buffer_request_sender,
connection_command_sender,
)
sphinx_socks
.serve(msg_input, buffer_requester, closed_connection_tx)
.await
{
shutdown.send_we_stopped(Box::new(res));
}
});
}
/// blocking version of `start` method. Will run forever (or until SIGINT is sent)
pub async fn run_forever(self) -> Result<(), Box<dyn Error + Send>> {
let mut shutdown = self
.start()
.await
.map_err(|err| Box::new(err) as Box<dyn Error + Send>)?;
let res = wait_for_signal_and_error(&mut shutdown).await;
pub async fn run_forever(&mut self) -> Result<(), Socks5ClientError> {
let mut shutdown = self.start().await?;
wait_for_signal().await;
log::info!("Sending shutdown");
client_core::client::SHUTDOWN_HAS_BEEN_SIGNALLED.store(true, Ordering::Relaxed);
shutdown.signal_shutdown().ok();
log::info!("Waiting for tasks to finish... (Press ctrl-c to force)");
shutdown.wait_for_shutdown().await;
log::info!("Stopping nym-socks5-client");
res
Ok(())
}
// Variant of `run_forever` that listends for remote control messages
pub async fn run_and_listen(
self,
&mut self,
mut receiver: Socks5ControlMessageReceiver,
) -> Result<(), Box<dyn Error + Send>> {
// Start the main task
let mut shutdown = self
.start()
.await
.map_err(|err| Box::new(err) as Box<dyn Error + Send>)?;
let res = tokio::select! {
biased;
) -> Result<(), Socks5ClientError> {
let mut shutdown = self.start().await?;
tokio::select! {
message = receiver.next() => {
log::debug!("Received message: {:?}", message);
match message {
@@ -166,51 +342,115 @@ impl NymClient {
log::info!("Channel closed, stopping");
}
}
Ok(())
}
Some(msg) = shutdown.wait_for_error() => {
log::info!("Task error: {:?}", msg);
Err(msg)
}
_ = tokio::signal::ctrl_c() => {
log::info!("Received SIGINT");
Ok(())
},
};
}
log::info!("Sending shutdown");
client_core::client::SHUTDOWN_HAS_BEEN_SIGNALLED.store(true, Ordering::Relaxed);
shutdown.signal_shutdown().ok();
log::info!("Waiting for tasks to finish... (Press ctrl-c to force)");
shutdown.wait_for_shutdown().await;
log::info!("Stopping nym-socks5-client");
res
Ok(())
}
pub async fn start(self) -> Result<ShutdownNotifier, Socks5ClientError> {
let base_builder = BaseClientBuilder::new_from_base_config(
self.config.get_base(),
self.key_manager,
Some(Self::create_bandwidth_controller(&self.config).await),
pub async fn start(&mut self) -> Result<ShutdownNotifier, Socks5ClientError> {
info!("Starting nym client");
// channels for inter-component communication
// TODO: make the channels be internally created by the relevant components
// rather than creating them here, so say for example the buffer controller would create the request channels
// and would allow anyone to clone the sender channel
// unwrapped_sphinx_sender is the transmitter of mixnet messages received from the gateway
// unwrapped_sphinx_receiver is the receiver for said messages - used by ReceivedMessagesBuffer
let (mixnet_messages_sender, mixnet_messages_receiver) = mpsc::unbounded();
// used for announcing connection or disconnection of a channel for pushing re-assembled messages to
let (received_buffer_request_sender, received_buffer_request_receiver) = mpsc::unbounded();
// channels responsible for controlling real messages
let (input_sender, input_receiver) = mpsc::unbounded::<InputMessage>();
// channels responsible for controlling ack messages
let (ack_sender, ack_receiver) = mpsc::unbounded();
let shared_topology_accessor = TopologyAccessor::new();
let reply_key_storage =
ReplyKeyStorage::load(self.config.get_base().get_reply_encryption_key_store_path())
.expect("Failed to load reply key storage!");
// Shutdown notifier for signalling tasks to stop
let shutdown = ShutdownNotifier::default();
// the components are started in very specific order. Unless you know what you are doing,
// do not change that.
self.start_topology_refresher(shared_topology_accessor.clone(), shutdown.subscribe())
.await?;
self.start_received_messages_buffer_controller(
received_buffer_request_receiver,
mixnet_messages_receiver,
reply_key_storage.clone(),
shutdown.subscribe(),
);
let self_address = base_builder.as_mix_recipient();
let mut started_client = base_builder.start_base().await?;
let client_input = started_client.client_input.register_producer();
let client_output = started_client.client_output.register_consumer();
let gateway_client = self
.start_gateway_client(mixnet_messages_sender, ack_sender, shutdown.subscribe())
.await;
Self::start_socks5_listener(
&self.config,
client_input,
client_output,
self_address,
started_client.shutdown_notifier.subscribe(),
// The sphinx_message_sender is the transmitter for any component generating sphinx packets
// that are to be sent to the mixnet. They are used by cover traffic stream and real
// traffic stream.
// The MixTrafficController then sends the actual traffic
let sphinx_message_sender =
Self::start_mix_traffic_controller(gateway_client, shutdown.subscribe());
// Channel for announcing closed (socks5) connections by the controller.
// This will be forwarded to `OutQueueControl`
let (closed_connection_tx, closed_connection_rx) = mpsc::unbounded();
// Shared queue length data. Published by the `OutQueueController` in the client, and used
// primarily to throttle incoming connections
let shared_lane_queue_lengths = LaneQueueLengths::new();
self.start_real_traffic_controller(
shared_topology_accessor.clone(),
reply_key_storage,
ack_receiver,
input_receiver,
sphinx_message_sender.clone(),
closed_connection_rx,
shared_lane_queue_lengths.clone(),
shutdown.subscribe(),
);
if !self
.config
.get_base()
.get_disabled_loop_cover_traffic_stream()
{
self.start_cover_traffic_stream(
shared_topology_accessor,
sphinx_message_sender,
shutdown.subscribe(),
);
}
self.start_socks5_listener(
received_buffer_request_sender,
input_sender,
closed_connection_tx,
shared_lane_queue_lengths,
shutdown.subscribe(),
);
info!("Client startup finished!");
info!("The address of this client is: {}", self_address);
info!("The address of this client is: {}", self.as_mix_recipient());
Ok(started_client.shutdown_notifier)
Ok(shutdown)
}
}
+5 -10
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use clap::Args;
use client_core::{config::GatewayEndpointConfig, error::ClientCoreError};
use client_core::{config::GatewayEndpoint, error::ClientCoreError};
use config::NymConfig;
use crate::{
@@ -29,13 +29,9 @@ pub(crate) struct Init {
#[clap(long)]
force_register_gateway: bool,
/// Comma separated list of rest endpoints of the nymd validators
/// Comma separated list of rest endpoints of the validators
#[clap(long)]
nymd_validators: Option<String>,
/// Comma separated list of rest endpoints of the API validators
#[clap(long)]
api_validators: Option<String>,
validators: Option<String>,
/// Port for the socket to listen on in all subsequent runs
#[clap(short, long)]
@@ -56,8 +52,7 @@ pub(crate) struct Init {
impl From<Init> for OverrideConfig {
fn from(init_config: Init) -> Self {
OverrideConfig {
nymd_validators: init_config.nymd_validators,
api_validators: init_config.api_validators,
validators: init_config.validators,
port: init_config.port,
fastmode: init_config.fastmode,
#[cfg(feature = "coconut")]
@@ -131,7 +126,7 @@ async fn setup_gateway(
register: bool,
user_chosen_gateway_id: Option<&str>,
config: &Config,
) -> Result<GatewayEndpointConfig, ClientCoreError> {
) -> Result<GatewayEndpoint, ClientCoreError> {
if register {
// Get the gateway details by querying the validator-api. Either pick one at random or use
// the chosen one if it's among the available ones.
+4 -15
View File
@@ -1,9 +1,8 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::error::Error;
use crate::client::config::Config;
use crate::error::Socks5ClientError;
use clap::CommandFactory;
use clap::{Parser, Subcommand};
use completions::{fig_generate, ArgShell};
@@ -79,8 +78,7 @@ pub(crate) enum Commands {
// Configuration that can be overridden.
pub(crate) struct OverrideConfig {
nymd_validators: Option<String>,
api_validators: Option<String>,
validators: Option<String>,
port: Option<u16>,
fastmode: bool,
@@ -88,7 +86,7 @@ pub(crate) struct OverrideConfig {
enabled_credentials_mode: bool,
}
pub(crate) async fn execute(args: &Cli) -> Result<(), Box<dyn Error + Send>> {
pub(crate) async fn execute(args: &Cli) -> Result<(), Socks5ClientError> {
let bin_name = "nym-socks5-client";
match &args.command {
@@ -102,16 +100,7 @@ pub(crate) async fn execute(args: &Cli) -> Result<(), Box<dyn Error + Send>> {
}
pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Config {
if let Some(raw_validators) = args.nymd_validators {
config
.get_base_mut()
.set_custom_validators(parse_validators(&raw_validators));
} else if let Ok(raw_validators) = std::env::var(network_defaults::var_names::NYMD_VALIDATOR) {
config
.get_base_mut()
.set_custom_validators(parse_validators(&raw_validators));
}
if let Some(raw_validators) = args.api_validators {
if let Some(raw_validators) = args.validators {
config
.get_base_mut()
.set_custom_validator_apis(parse_validators(&raw_validators));
+6 -13
View File
@@ -31,13 +31,9 @@ pub(crate) struct Run {
#[clap(long)]
gateway: Option<String>,
/// Comma separated list of rest endpoints of the nymd validators
/// Comma separated list of rest endpoints of the validators
#[clap(long)]
nymd_validators: Option<String>,
/// Comma separated list of rest endpoints of the API validators
#[clap(long)]
api_validators: Option<String>,
validators: Option<String>,
/// Port for the socket to listen on
#[clap(short, long)]
@@ -53,8 +49,7 @@ pub(crate) struct Run {
impl From<Run> for OverrideConfig {
fn from(run_config: Run) -> Self {
OverrideConfig {
nymd_validators: run_config.nymd_validators,
api_validators: run_config.api_validators,
validators: run_config.validators,
port: run_config.port,
fastmode: false,
@@ -86,16 +81,14 @@ fn version_check(cfg: &Config) -> bool {
}
}
pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn std::error::Error + Send>> {
pub(crate) async fn execute(args: &Run) -> Result<(), Socks5ClientError> {
let id = &args.id;
let mut config = match Config::load_from_file(Some(id)) {
Ok(cfg) => cfg,
Err(err) => {
error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {})", id, err);
return Err(Box::new(Socks5ClientError::FailedToLoadConfig(
id.to_string(),
)));
return Err(Socks5ClientError::FailedToLoadConfig(id.to_string()));
}
};
@@ -104,7 +97,7 @@ pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn std::error::Error
if !version_check(&config) {
error!("failed the local version check");
return Err(Box::new(Socks5ClientError::FailedLocalVersionCheck));
return Err(Socks5ClientError::FailedLocalVersionCheck);
}
NymClient::new(config).run_forever().await
+9 -7
View File
@@ -1,21 +1,23 @@
use client_core::error::ClientCoreError;
use crate::socks::types::SocksProxyError;
use crypto::asymmetric::identity::Ed25519RecoveryError;
use gateway_client::error::GatewayClientError;
use validator_client::ValidatorClientError;
#[derive(thiserror::Error, Debug)]
pub enum Socks5ClientError {
#[error("I/O error: {0}")]
IoError(#[from] std::io::Error),
#[error("Gateway client error: {0}")]
GatewayClientError(#[from] GatewayClientError),
#[error("Ed25519 error: {0}")]
Ed25519RecoveryError(#[from] Ed25519RecoveryError),
#[error("Validator client error: {0}")]
ValidatorClientError(#[from] ValidatorClientError),
#[error("client-core error: {0}")]
ClientCoreError(#[from] ClientCoreError),
#[error("SOCKS proxy error")]
SocksProxyError(SocksProxyError),
#[error("Failed to load config for: {0}")]
FailedToLoadConfig(String),
#[error("Failed local version check, client and config mismatch")]
FailedLocalVersionCheck,
#[error("Fail to bind address")]
FailToBindAddress,
}
+2 -3
View File
@@ -1,9 +1,8 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::error::Error;
use clap::{crate_version, Parser};
use error::Socks5ClientError;
use logging::setup_logging;
use network_defaults::setup_env;
@@ -13,7 +12,7 @@ pub mod error;
pub mod socks;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error + Send>> {
async fn main() -> Result<(), Socks5ClientError> {
setup_logging();
println!("{}", banner());
+77 -110
View File
@@ -2,8 +2,8 @@
use super::authentication::{AuthenticationMethods, Authenticator, User};
use super::request::{SocksCommand, SocksRequest};
use super::types::{ResponseCodeV4, ResponseCodeV5, SocksProxyError};
use super::{SocksVersion, RESERVED, SOCKS4_VERSION, SOCKS5_VERSION};
use super::types::{ResponseCode, SocksProxyError};
use super::{RESERVED, SOCKS_VERSION};
use client_connections::{LaneQueueLengths, TransmissionLane};
use client_core::client::inbound_messages::{InputMessage, InputMessageSender};
use futures::channel::mpsc;
@@ -20,6 +20,7 @@ use socks5_requests::{ConnectionId, Message, RemoteAddress, Request};
use std::io;
use std::net::SocketAddr;
use std::pin::Pin;
use std::sync::Arc;
use task::ShutdownListener;
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, ReadBuf};
use tokio::{self, net::TcpStream};
@@ -129,13 +130,13 @@ impl AsyncWrite for StreamState {
/// A client connecting to the Socks proxy server, because
/// it wants to make a Nym-protected outbound request. Typically, this is
/// something like e.g. a wallet app running on your laptop connecting to
/// `SphinxSocksServer`.
/// SphinxSocksServer.
pub(crate) struct SocksClient {
controller_sender: ControllerSender,
stream: StreamState,
auth_nmethods: u8,
authenticator: Authenticator,
socks_version: Option<SocksVersion>,
socks_version: u8,
input_sender: InputMessageSender,
connection_id: ConnectionId,
service_provider: Recipient,
@@ -143,6 +144,7 @@ pub(crate) struct SocksClient {
started_proxy: bool,
lane_queue_lengths: LaneQueueLengths,
shutdown_listener: ShutdownListener,
active_connections: Arc<std::sync::Mutex<u64>>,
}
impl Drop for SocksClient {
@@ -158,35 +160,39 @@ impl Drop for SocksClient {
}
impl SocksClient {
#[allow(clippy::too_many_arguments)]
/// Create a new SOCKClient
pub fn new(
stream: TcpStream,
authenticator: Authenticator,
input_sender: InputMessageSender,
service_provider: &Recipient,
service_provider: Recipient,
controller_sender: ControllerSender,
self_address: &Recipient,
self_address: Recipient,
lane_queue_lengths: LaneQueueLengths,
mut shutdown_listener: ShutdownListener,
shutdown_listener: ShutdownListener,
active_connections: Arc<std::sync::Mutex<u64>>,
) -> Self {
// If this task fails and exits, we don't want to send shutdown signal
shutdown_listener.mark_as_success();
let connection_id = Self::generate_random();
let u_active_connections = {
let g = active_connections.lock().unwrap();
*g
};
error!("client active_connections: {}", u_active_connections);
SocksClient {
controller_sender,
connection_id,
stream: StreamState::Available(stream),
auth_nmethods: 0,
socks_version: None,
socks_version: 0,
authenticator,
input_sender,
service_provider: *service_provider,
self_address: *self_address,
service_provider,
self_address,
started_proxy: false,
lane_queue_lengths,
shutdown_listener,
active_connections,
}
}
@@ -195,49 +201,16 @@ impl SocksClient {
rng.next_u64()
}
pub async fn send_error(&mut self, err: SocksProxyError) -> Result<(), SocksProxyError> {
let error_text = format!("{}", err);
let Some(ref version) = self.socks_version else {
log::error!("Trying to send error without knowing the version");
return Ok(());
};
match version {
SocksVersion::V4 => {
let response = ResponseCodeV4::RequestRejected;
self.send_error_v4(response).await
}
SocksVersion::V5 => {
let response = if error_text.contains("Host") {
ResponseCodeV5::HostUnreachable
} else if error_text.contains("Network") {
ResponseCodeV5::NetworkUnreachable
} else if error_text.contains("ttl") {
ResponseCodeV5::TtlExpired
} else {
ResponseCodeV5::Failure
};
self.send_error_v5(response).await
}
}
}
// Send an error back to the client
pub async fn send_error_v4(&mut self, r: ResponseCodeV4) -> Result<(), SocksProxyError> {
self.stream.write_all(&[SOCKS4_VERSION, r as u8]).await?;
pub async fn error(&mut self, r: ResponseCode) -> Result<(), SocksProxyError> {
self.stream.write_all(&[5, r as u8]).await?;
Ok(())
}
pub async fn send_error_v5(&mut self, r: ResponseCodeV5) -> Result<(), SocksProxyError> {
self.stream.write_all(&[SOCKS5_VERSION, r as u8]).await?;
Ok(())
}
/// Shutdown the `TcpStream` to the client and end the session
/// Shutdown the TcpStream to the client and end the session
pub async fn shutdown(&mut self) -> Result<(), SocksProxyError> {
info!("client is shutting down its TCP stream");
self.stream.shutdown().await?;
self.shutdown_listener.mark_as_success();
Ok(())
}
@@ -245,30 +218,30 @@ impl SocksClient {
/// is in use and that the client is authenticated, then runs the request.
pub async fn run(&mut self) -> Result<(), SocksProxyError> {
debug!("New connection from: {}", self.stream.peer_addr()?.ip());
//dbg!(&self.stream.peer_addr());
let mut header = [0u8; 2];
// Read a byte from the stream and determine the version being requested
let mut header = [0u8];
self.stream.read_exact(&mut header).await?;
self.socks_version = match SocksVersion::try_from(header[0]) {
Ok(version) => Some(version),
Err(_err) => {
warn!("Init: Unsupported version: SOCKS{}", header[0]);
return self.shutdown().await;
}
};
self.socks_version = header[0];
self.auth_nmethods = header[1];
if self.socks_version == Some(SocksVersion::V5) {
let mut auth = [0u8];
self.stream.read_exact(&mut auth).await?;
self.auth_nmethods = auth[0];
self.authenticate_socks5().await?;
// Handle SOCKS4 requests
if header[0] != SOCKS_VERSION {
warn!("Init: Unsupported version: SOCKS{}", self.socks_version);
self.shutdown().await
}
// Valid SOCKS5
else {
// Authenticate w/ client
self.authenticate().await?;
// Handle requests
self.handle_request().await
}
self.handle_request().await
}
async fn send_connect_to_mixnet(&mut self, remote_address: RemoteAddress) {
fn send_connect_to_mixnet(&mut self, remote_address: RemoteAddress) {
let req = Request::new_connect(self.connection_id, remote_address, self.self_address);
let msg = Message::Request(req);
@@ -278,26 +251,17 @@ impl SocksClient {
false,
TransmissionLane::ConnectionId(self.connection_id),
);
self.input_sender
.send(input_message)
.await
.expect("InputMessageReceiver has stopped receiving!");
self.input_sender.unbounded_send(input_message).unwrap();
}
async fn run_proxy(&mut self, conn_receiver: ConnectionReceiver, remote_proxy_target: String) {
self.send_connect_to_mixnet(remote_proxy_target.clone())
.await;
self.send_connect_to_mixnet(remote_proxy_target.clone());
let stream = self.stream.run_proxy();
let peer_addr = match stream.peer_addr() {
Ok(peer_addr) => peer_addr,
Err(err) => {
log::error!("Unable to extract the remote peer address: {err}");
return;
}
};
let local_stream_remote = peer_addr.to_string();
let local_stream_remote = stream
.peer_addr()
.expect("failed to extract peer address")
.to_string();
let connection_id = self.connection_id;
let input_sender = self.input_sender.clone();
@@ -309,7 +273,7 @@ impl SocksClient {
conn_receiver,
input_sender,
connection_id,
Some(self.lane_queue_lengths.clone()),
self.lane_queue_lengths.clone(),
self.shutdown_listener.clone(),
)
.run(move |conn_id, read_data, socket_closed| {
@@ -327,18 +291,23 @@ impl SocksClient {
/// Handles a client request.
async fn handle_request(&mut self) -> Result<(), SocksProxyError> {
debug!("Handling CONNECT Command");
let version = self
.socks_version
.as_ref()
.expect("Must read version before parsing request");
let request = match version {
SocksVersion::V4 => SocksRequest::from_stream_socks4(&mut self.stream).await?,
SocksVersion::V5 => SocksRequest::from_stream_socks5(&mut self.stream).await?,
let active_connections = {
let g = self.active_connections.lock().unwrap();
*g
};
error!("active_connections: {}", active_connections);
let remote_address = request.address_string();
let request = SocksRequest::from_stream(&mut self.stream).await?;
let remote_address = request.to_string();
if active_connections > 50 {
log::warn!(
"Refusing SOCKS5: too many connections: {}",
active_connections
);
self.refuse_connection_socks5().await;
return Ok(());
}
// setup for receiving from the mixnet
let (mix_sender, mix_receiver) = mpsc::unbounded();
@@ -347,10 +316,7 @@ impl SocksClient {
// Use the Proxy to connect to the specified addr/port
SocksCommand::Connect => {
trace!("Connecting to: {:?}", remote_address.clone());
match version {
SocksVersion::V4 => self.acknowledge_socks4().await,
SocksVersion::V5 => self.acknowledge_socks5().await,
}
self.acknowledge_socks5().await;
self.started_proxy = true;
self.controller_sender
@@ -381,8 +347,8 @@ impl SocksClient {
async fn acknowledge_socks5(&mut self) {
self.stream
.write_all(&[
SOCKS5_VERSION,
ResponseCodeV5::Success as u8,
SOCKS_VERSION,
ResponseCode::Success as u8,
RESERVED,
1,
127,
@@ -396,18 +362,19 @@ impl SocksClient {
.unwrap();
}
/// Writes a Socks4 header back to the requesting client's TCP stream,
async fn acknowledge_socks4(&mut self) {
async fn refuse_connection_socks5(&mut self) {
self.stream
.write_all(&[
0, //SOCKS4_VERSION,
ResponseCodeV4::Granted as u8,
0,
0,
SOCKS_VERSION,
ResponseCode::RuleFailure as u8,
RESERVED,
1,
127,
0,
0,
1,
0,
0,
])
.await
.unwrap();
@@ -419,7 +386,7 @@ impl SocksClient {
/// to see if the resulting user is allowed.
///
/// A lot of this could probably be put into the `SocksRequest::from_stream()`
/// constructor, and/or cleaned up with `tokio::codec`. It's mostly just
/// constructor, and/or cleaned up with tokio::codec. It's mostly just
/// read-a-byte-or-two. The bytes being extracted look like this:
///
/// +----+------+----------+------+------------+
@@ -431,7 +398,7 @@ impl SocksClient {
/// Pulling out the stream code into its own home, and moving the if/else logic
/// into the Authenticator (where it'll be more easily testable)
/// would be a good next step.
async fn authenticate_socks5(&mut self) -> Result<(), SocksProxyError> {
async fn authenticate(&mut self) -> Result<(), SocksProxyError> {
debug!("Authenticating w/ {}", self.stream.peer_addr()?.ip());
// Get valid auth methods
let methods = self.get_available_methods().await?;
@@ -440,7 +407,7 @@ impl SocksClient {
let mut response = [0u8; 2];
// Set the version in the response
response[0] = SOCKS5_VERSION;
response[0] = SOCKS_VERSION;
if methods.contains(&(AuthenticationMethods::UserPass as u8)) {
// Set the default auth method (NO AUTH)
response[1] = AuthenticationMethods::UserPass as u8;
@@ -476,11 +443,11 @@ impl SocksClient {
// Authenticate passwords
if self.authenticator.is_allowed(&user) {
debug!("Access Granted. User: {}", user.username);
let response = [1, ResponseCodeV5::Success as u8];
let response = [1, ResponseCode::Success as u8];
self.stream.write_all(&response).await?;
} else {
debug!("Access Denied. User: {}", user.username);
let response = [1, ResponseCodeV5::Failure as u8];
let response = [1, ResponseCode::Failure as u8];
self.stream.write_all(&response).await?;
// Shutdown
@@ -499,7 +466,7 @@ impl SocksClient {
response[1] = AuthenticationMethods::NoMethods as u8;
self.stream.write_all(&response).await?;
self.shutdown().await?;
Err(ResponseCodeV5::Failure.into())
Err(ResponseCode::Failure.into())
}
}
+3 -15
View File
@@ -1,5 +1,3 @@
use std::time::Duration;
use futures::channel::mpsc;
use futures::StreamExt;
use log::*;
@@ -20,16 +18,9 @@ pub(crate) struct MixnetResponseListener {
impl Drop for MixnetResponseListener {
fn drop(&mut self) {
if let Err(err) = self
.buffer_requester
self.buffer_requester
.unbounded_send(ReceivedBufferMessage::ReceiverDisconnect)
{
if self.shutdown.is_shutdown_poll() {
log::debug!("The buffer request failed: {}", err);
} else {
log::error!("The buffer request failed: {}", err);
}
}
.expect("the buffer request failed!")
}
}
@@ -105,10 +96,7 @@ impl MixnetResponseListener {
}
}
}
#[cfg(not(target_arch = "wasm32"))]
tokio::time::timeout(Duration::from_secs(5), self.shutdown.recv())
.await
.expect("Task stopped without shutdown called");
assert!(self.shutdown.is_shutdown_poll());
log::debug!("MixnetResponseListener: Exiting");
}
}
+1 -26
View File
@@ -1,9 +1,5 @@
#![forbid(unsafe_code)]
use std::convert::TryFrom;
use self::types::SocksProxyError;
pub mod authentication;
mod client;
pub(crate) mod mixnet_responses;
@@ -13,27 +9,6 @@ pub mod types;
pub mod utils;
/// Version of socks
const SOCKS4_VERSION: u8 = 0x04;
const SOCKS5_VERSION: u8 = 0x05;
const SOCKS_VERSION: u8 = 0x05;
const RESERVED: u8 = 0x00;
#[derive(Clone, PartialEq, Eq)]
pub enum SocksVersion {
V4 = 0x04,
V5 = 0x05,
}
pub struct InvalidSocksVersion;
impl TryFrom<u8> for SocksVersion {
type Error = SocksProxyError;
fn try_from(version: u8) -> Result<Self, Self::Error> {
match version {
SOCKS4_VERSION => Ok(Self::V4),
SOCKS5_VERSION => Ok(Self::V5),
_ => Err(SocksProxyError::UnsupportedProxyVersion(version)),
}
}
}
+49 -109
View File
@@ -1,7 +1,5 @@
use crate::socks::SOCKS4_VERSION;
use super::types::{AddrType, ResponseCodeV5, SocksProxyError};
use super::{utils as socks_utils, SOCKS5_VERSION};
use super::types::{AddrType, ResponseCode, SocksProxyError};
use super::{utils as socks_utils, SOCKS_VERSION};
use log::*;
use std::fmt::{self, Display};
use tokio::io::{AsyncRead, AsyncReadExt};
@@ -17,114 +15,80 @@ pub(crate) struct SocksRequest {
}
impl SocksRequest {
/// Parse a SOCKS4 request from a `TcpStream`
/// From documents at:
/// - SOCKS4: https://www.openssh.com/txt/socks4.protocol
/// - SOCKS4a: https://www.openssh.com/txt/socks4a.protocol
pub async fn from_stream_socks4<R>(stream: &mut R) -> Result<Self, SocksProxyError>
/// Parse a SOCKS5 request from a TcpStream
pub async fn from_stream<R>(stream: &mut R) -> Result<Self, SocksProxyError>
where
R: AsyncRead + Unpin,
{
log::trace!("read from stream socks4");
let mut packet = [0u8; 3];
stream.read_exact(&mut packet).await?;
// CD (command)
let Some(command) = SocksCommand::from(packet[0] as usize) else {
log::warn!("Invalid Command");
return Err(ResponseCodeV5::CommandNotSupported.into());
};
// DSTPORT
let mut port = [0u8; 2];
port.copy_from_slice(&packet[1..]);
let port = merge_u8_into_u16(port[0], port[1]);
// DSTIP
let mut ip = [0u8; 4];
stream.read_exact(&mut ip).await?;
// USERID
let _userid = read_until_zero(stream).await;
// SOCKS4a extension
// https://www.openssh.com/txt/socks4a.protocol
// If the IP is 0.0.0.x with x nonzero, read the domain name
let (addr, addr_type) = if ip[..3] == [0, 0, 0] && ip[3] != 0 {
(read_until_zero(stream).await?, AddrType::Domain)
} else {
(ip.to_vec(), AddrType::V4)
};
// Return parsed request
Ok(SocksRequest {
version: SOCKS4_VERSION,
command,
addr_type,
addr,
port,
})
}
/// Parse a SOCKS5 request from a `TcpStream`
/// From: https://www.rfc-editor.org/rfc/rfc1928
pub async fn from_stream_socks5<R>(stream: &mut R) -> Result<Self, SocksProxyError>
where
R: AsyncRead + Unpin,
{
log::info!("read from stream socks5");
let mut packet = [0u8; 4];
// Read a byte from the stream and determine the version being requested
stream.read_exact(&mut packet).await?;
// VER
if packet[0] != SOCKS5_VERSION {
warn!("Unsupported version: SOCKS{}", packet[0]);
if packet[0] != SOCKS_VERSION {
warn!("from_stream Unsupported version: SOCKS{}", packet[0]);
return Err(SocksProxyError::UnsupportedProxyVersion(packet[0]));
}
// CMD
let Some(command) = SocksCommand::from(packet[1] as usize) else {
warn!("Invalid Command");
return Err(ResponseCodeV5::CommandNotSupported.into());
};
// Get command
let mut command: SocksCommand = SocksCommand::Connect;
match SocksCommand::from(packet[1] as usize) {
Some(com) => {
command = com;
Ok(())
}
None => {
warn!("Invalid Command");
Err(ResponseCode::CommandNotSupported)
}
}?;
// RSV
// packet[2] is reserved
// DST.address
// ATYP
let Some(addr_type) = AddrType::from(packet[3] as usize) else {
error!("No Addr");
return Err(ResponseCodeV5::AddrTypeNotSupported.into())
};
let mut addr_type: AddrType = AddrType::V6;
match AddrType::from(packet[3] as usize) {
Some(addr) => {
addr_type = addr;
Ok(())
}
None => {
error!("No Addr");
Err(ResponseCode::AddrTypeNotSupported)
}
}?;
// DST.ADDR
let addr = match addr_type {
trace!("Getting Addr");
// Get Addr from addr_type and stream
let addr: Result<Vec<u8>, SocksProxyError> = match addr_type {
AddrType::Domain => {
let mut domain_length = [0u8];
let mut domain_length = [0u8; 1];
stream.read_exact(&mut domain_length).await?;
let mut domain = vec![0u8; domain_length[0] as usize];
stream.read_exact(&mut domain).await?;
domain
Ok(domain)
}
AddrType::V4 => {
let mut addr = [0u8; 4];
stream.read_exact(&mut addr).await?;
addr.to_vec()
Ok(addr.to_vec())
}
AddrType::V6 => {
let mut addr = [0u8; 16];
stream.read_exact(&mut addr).await?;
addr.to_vec()
Ok(addr.to_vec())
}
};
// DST.PORT
let addr = addr?;
// read DST.port
let mut port = [0u8; 2];
stream.read_exact(&mut port).await?;
let port = merge_u8_into_u16(port[0], port[1]);
// Merge two u8s into u16
let port = (u16::from(port[0]) << 8) | u16::from(port[1]);
// Return parsed request
Ok(SocksRequest {
version: packet[0],
command,
@@ -133,18 +97,14 @@ impl SocksRequest {
port,
})
}
/// Print out the address and port to a String.
/// This might return domain:port, ipv6:port, or ipv4:port.
pub fn address_string(&self) -> String {
let address = socks_utils::pretty_print_addr(&self.addr_type, &self.addr);
format!("{}:{}", address, self.port)
}
}
impl Display for SocksRequest {
/// Print out the address and port to a String.
/// This might return domain:port, ipv6:port, or ipv4:port.
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.address_string())
let address = socks_utils::pretty_print_addr(&self.addr_type, &self.addr);
write!(f, "{}:{}", address, self.port)
}
}
@@ -167,23 +127,3 @@ impl SocksCommand {
}
}
}
fn merge_u8_into_u16(a: u8, b: u8) -> u16 {
(u16::from(a) << 8) | u16::from(b)
}
async fn read_until_zero<R>(stream: &mut R) -> Result<Vec<u8>, SocksProxyError>
where
R: AsyncRead + Unpin,
{
let mut result = Vec::new();
let mut char = [0u8];
loop {
stream.read_exact(&mut char).await?;
if char[0] == 0 {
break;
}
result.push(char[0]);
}
Ok(result)
}
+54 -25
View File
@@ -1,17 +1,18 @@
use crate::error::Socks5ClientError;
use super::authentication::Authenticator;
use super::client::SocksClient;
use super::{
authentication::Authenticator, client::SocksClient, mixnet_responses::MixnetResponseListener,
mixnet_responses::MixnetResponseListener,
types::{ResponseCode, SocksProxyError},
};
use client_connections::{ConnectionCommandSender, LaneQueueLengths};
use client_connections::{ClosedConnectionSender, LaneQueueLengths};
use client_core::client::{
inbound_messages::InputMessageSender, received_buffer::ReceivedBufferRequestSender,
};
use log::*;
use nymsphinx::addressing::clients::Recipient;
use proxy_helpers::connection_controller::{BroadcastActiveConnections, Controller};
use proxy_helpers::connection_controller::Controller;
use std::net::SocketAddr;
use tap::TapFallible;
use std::sync::Arc;
use task::ShutdownListener;
use tokio::net::TcpListener;
@@ -55,19 +56,14 @@ impl SphinxSocksServer {
&mut self,
input_sender: InputMessageSender,
buffer_requester: ReceivedBufferRequestSender,
client_connection_tx: ConnectionCommandSender,
) -> Result<(), Socks5ClientError> {
let listener = TcpListener::bind(self.listening_address)
.await
.tap_err(|err| log::error!("Failed to bind to address: {err}"))?;
closed_connection_tx: ClosedConnectionSender,
) -> Result<(), SocksProxyError> {
let listener = TcpListener::bind(self.listening_address).await.unwrap();
info!("Serving Connections...");
// controller for managing all active connections
let (mut active_streams_controller, controller_sender) = Controller::new(
client_connection_tx,
BroadcastActiveConnections::Off,
self.shutdown.clone(),
);
let (mut active_streams_controller, controller_sender) =
Controller::new(closed_connection_tx, self.shutdown.clone());
tokio::spawn(async move {
active_streams_controller.run().await;
});
@@ -82,29 +78,62 @@ impl SphinxSocksServer {
mixnet_response_listener.run().await;
});
let active_connections = Arc::new(std::sync::Mutex::new(0));
loop {
tokio::select! {
Ok((stream, _remote)) = listener.accept() => {
// TODO Optimize this
{
let mut g = active_connections.lock().unwrap();
*g += 1;
}
let mut client = SocksClient::new(
stream,
self.authenticator.clone(),
input_sender.clone(),
&self.service_provider,
self.service_provider,
controller_sender.clone(),
&self.self_address,
self.self_address,
self.lane_queue_lengths.clone(),
self.shutdown.clone(),
active_connections.clone(),
);
let active_connections = active_connections.clone();
tokio::spawn(async move {
if let Err(err) = client.run().await {
error!("Error! {}", err);
if client.send_error(err).await.is_err() {
warn!("Failed to send error code");
};
if client.shutdown().await.is_err() {
warn!("Failed to shutdown TcpStream");
{
match client.run().await {
Ok(_) => {}
Err(error) => {
error!("Error! {}", error);
let error_text = format!("{}", error);
let response: ResponseCode;
if error_text.contains("Host") {
response = ResponseCode::HostUnreachable;
} else if error_text.contains("Network") {
response = ResponseCode::NetworkUnreachable;
} else if error_text.contains("ttl") {
response = ResponseCode::TtlExpired
} else {
response = ResponseCode::Failure
}
if client.error(response).await.is_err() {
warn!("Failed to send error code");
};
if client.shutdown().await.is_err() {
warn!("Failed to shutdown TcpStream");
};
}
};
// client gets dropped here
}
{
let mut g = active_connections.lock().unwrap();
*g -= 1;
}
});
},
+3 -13
View File
@@ -1,17 +1,7 @@
use snafu::Snafu;
/// SOCKS4 Response codes
#[allow(dead_code)]
pub(crate) enum ResponseCodeV4 {
Granted = 0x5a,
RequestRejected = 0x5b,
CannotConnectToIdent = 0x5c,
DifferentUserId = 0x5d,
}
/// Possible SOCKS5 Response Codes
#[derive(Debug, Snafu)]
pub(crate) enum ResponseCodeV5 {
/// Possible SOCKS5 Response Codes
pub(crate) enum ResponseCode {
Success = 0x00,
#[snafu(display("SOCKS5 Server Failure"))]
Failure = 0x01,
@@ -58,7 +48,7 @@ where
}
/// DST.addr variant types
#[derive(Debug, PartialEq)]
#[derive(PartialEq)]
pub(crate) enum AddrType {
V4 = 0x01,
Domain = 0x03,
+4 -5
View File
@@ -19,14 +19,13 @@ coconut = ["coconut-interface", "credentials", "gateway-client/coconut"]
[dependencies]
futures = "0.3"
js-sys = "0.3"
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
serde = { version = "1.0", features = ["derive"] }
serde-wasm-bindgen = "0.4"
tokio = { version = "1.21.2", features = ["sync"] }
url = "2.2"
wasm-bindgen = { version = "=0.2.83", features = ["serde-serialize"] }
wasm-bindgen-futures = "0.4"
js-sys = "0.3"
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
url = "2.2"
# internal
client-core = { path = "../client-core", default-features = false, features = ["wasm"] }
@@ -39,7 +38,7 @@ topology = { path = "../../common/topology" }
gateway-client = { path = "../../common/client-libs/gateway-client", default-features = false, features = ["wasm", "coconut"] }
validator-client = { path = "../../common/client-libs/validator-client", default-features = false }
wasm-utils = { path = "../../common/wasm-utils" }
task = { path = "../../common/task" }
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
+1 -1
View File
@@ -24,7 +24,7 @@
},
"../pkg": {
"name": "@nymproject/nym-client-wasm",
"version": "1.1.0",
"version": "1.0.0",
"license": "Apache-2.0"
},
"node_modules/@discoveryjs/json-ext": {
+7
View File
@@ -61,9 +61,16 @@ async function main() {
// sets up better stack traces in case of in-rust panics
set_panic_hook();
console.error("the current mainnet is not compatible with v2! - either use the pre-merge branch or explicitly set the client to use one of V2 QA networks")
return
// validator server we will use to get topology from
// MAINNET (V1):
const validator = 'https://validator.nymtech.net/api'; //"http://localhost:8081";
const preferredGateway = 'E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM';
// QA (V2):
// const validator = 'https://qa-validator-api.nymtech.net/api'; //"http://localhost:8081";
// const preferredGateway = 'CgQrYP8etksSBf4nALNqp93SHPpgFwEUyTsjBNNLj5WM';
const gatewayEndpoint = await get_gateway(validator, preferredGateway);
gatewayEndpoint.gateway_listener = "wss://gateway1.nymtech.net:443"; // this is needed if we want it to work on the web. However this gateway is a v1 gateway, we will need to change for v2 once we get there
+3 -6
View File
@@ -4,15 +4,12 @@
// due to expansion of #[wasm_bindgen] macro on `Debug` Config struct
#![allow(clippy::drop_non_drop)]
use client_core::config::{DebugConfig as ConfigDebug, ExtendedPacketSize, GatewayEndpointConfig};
use serde::{Deserialize, Serialize};
use client_core::config::{Debug as ConfigDebug, ExtendedPacketSize, GatewayEndpoint};
use std::time::Duration;
use url::Url;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Config {
/// ID specifies the human readable ID of this particular client.
pub(crate) id: String,
@@ -22,7 +19,7 @@ pub struct Config {
pub(crate) disabled_credentials_mode: bool,
/// Information regarding how the client should send data to gateway.
pub(crate) gateway_endpoint: GatewayEndpointConfig,
pub(crate) gateway_endpoint: GatewayEndpoint,
pub(crate) debug: ConfigDebug,
}
@@ -33,7 +30,7 @@ impl Config {
pub fn new(
id: String,
validator_server: String,
gateway_endpoint: GatewayEndpointConfig,
gateway_endpoint: GatewayEndpoint,
debug: Option<Debug>,
) -> Self {
Config {
+305 -91
View File
@@ -2,44 +2,51 @@
// SPDX-License-Identifier: Apache-2.0
use self::config::Config;
use crate::client::response_pusher::ResponsePusher;
use client_connections::TransmissionLane;
use client_core::client::base_client::{BaseClientBuilder, ClientInput, ClientOutput};
use client_core::client::{inbound_messages::InputMessage, key_manager::KeyManager};
use client_connections::{ClosedConnectionReceiver, TransmissionLane};
use client_core::client::{
cover_traffic_stream::LoopCoverTrafficStream,
inbound_messages::{InputMessage, InputMessageReceiver, InputMessageSender},
key_manager::KeyManager,
mix_traffic::{BatchMixMessageSender, MixTrafficController},
real_messages_control::{self, RealMessagesController},
received_buffer::{
ReceivedBufferMessage, ReceivedBufferRequestReceiver, ReceivedBufferRequestSender,
ReceivedMessagesBufferController,
},
topology_control::{TopologyAccessor, TopologyRefresher, TopologyRefresherConfig},
};
use crypto::asymmetric::identity;
use futures::channel::mpsc;
use futures::StreamExt;
use gateway_client::{
AcknowledgementReceiver, AcknowledgementSender, GatewayClient, MixnetMessageReceiver,
MixnetMessageSender,
};
use nymsphinx::addressing::clients::Recipient;
use rand::rngs::OsRng;
use task::ShutdownNotifier;
use wasm_bindgen::prelude::*;
use wasm_utils::{console_error, console_log};
use wasm_bindgen_futures::spawn_local;
use wasm_utils::console_log;
pub mod config;
mod response_pusher;
#[wasm_bindgen]
pub struct NymClient {
config: Config,
/// KeyManager object containing smart pointers to all relevant keys used by the client.
// due to disgusting workaround I had to wrap the key_manager in an Option
// so that the interface wouldn't change (i.e. both `start` and `new` would still return a `NymClient`)
key_manager: Option<KeyManager>,
self_address: Option<String>,
key_manager: KeyManager,
// TODO: this should be stored somewhere persistently
// received_keys: HashSet<SURBEncryptionKey>,
/// Channel used for transforming 'raw' messages into sphinx packets and sending them
/// through the mix network.
client_input: Option<ClientInput>,
input_tx: Option<InputMessageSender>,
// callbacks
on_message: Option<js_sys::Function>,
on_binary_message: Option<js_sys::Function>,
on_gateway_connect: Option<js_sys::Function>,
// even though we don't use graceful shutdowns, other components rely on existence of this struct
// and if it's dropped, everything will start going offline
_shutdown: Option<ShutdownNotifier>,
}
#[wasm_bindgen]
@@ -48,19 +55,14 @@ impl NymClient {
pub fn new(config: Config) -> Self {
Self {
config,
key_manager: Some(Self::setup_key_manager()),
key_manager: Self::setup_key_manager(),
on_message: None,
on_binary_message: None,
on_gateway_connect: None,
client_input: None,
self_address: None,
_shutdown: None,
input_tx: None,
}
}
// TODO: once we make keys persistent, we'll require some kind of `init` method to generate
// a prior shared keypair between the client and the gateway
// perhaps this should be public?
fn setup_key_manager() -> KeyManager {
let mut rng = OsRng;
@@ -82,26 +84,291 @@ impl NymClient {
}
fn as_mix_recipient(&self) -> Recipient {
// another disgusting (and hopefully temporary) workaround
let key_manager_ref = self
.key_manager
.as_ref()
.expect("attempting to call 'as_mix_recipient' after 'start'");
Recipient::new(
*key_manager_ref.identity_keypair().public_key(),
*key_manager_ref.encryption_keypair().public_key(),
*self.key_manager.identity_keypair().public_key(),
*self.key_manager.encryption_keypair().public_key(),
identity::PublicKey::from_base58_string(&self.config.gateway_endpoint.gateway_id)
.expect("no gateway has been selected"),
)
}
pub fn self_address(&self) -> String {
if let Some(address) = &self.self_address {
address.clone()
} else {
self.as_mix_recipient().to_string()
self.as_mix_recipient().to_string()
}
// future constantly pumping loop cover traffic at some specified average rate
// the pumped traffic goes to the MixTrafficController
fn start_cover_traffic_stream(
&self,
topology_accessor: TopologyAccessor,
mix_tx: BatchMixMessageSender,
) {
console_log!("Starting loop cover traffic stream...");
let mut stream = LoopCoverTrafficStream::new(
self.key_manager.ack_key(),
self.config.debug.average_ack_delay,
self.config.debug.average_packet_delay,
self.config.debug.loop_cover_traffic_average_delay,
mix_tx,
self.as_mix_recipient(),
topology_accessor,
);
if let Some(size) = &self.config.debug.use_extended_packet_size {
stream.set_custom_packet_size(size.clone().into());
}
stream.start();
}
fn start_real_traffic_controller(
&self,
topology_accessor: TopologyAccessor,
ack_receiver: AcknowledgementReceiver,
input_receiver: InputMessageReceiver,
mix_sender: BatchMixMessageSender,
closed_connection_rx: ClosedConnectionReceiver,
) {
let mut controller_config = real_messages_control::Config::new(
self.key_manager.ack_key(),
self.config.debug.ack_wait_multiplier,
self.config.debug.ack_wait_addition,
self.config.debug.average_ack_delay,
self.config.debug.message_sending_average_delay,
self.config.debug.average_packet_delay,
self.config.debug.disable_main_poisson_packet_distribution,
self.as_mix_recipient(),
);
if let Some(size) = &self.config.debug.use_extended_packet_size {
controller_config.set_custom_packet_size(size.clone().into());
}
console_log!("Starting real traffic stream...");
RealMessagesController::new(
controller_config,
ack_receiver,
input_receiver,
mix_sender,
topology_accessor,
closed_connection_rx,
)
.start();
}
// buffer controlling all messages fetched from provider
// required so that other components would be able to use them (say the websocket)
fn start_received_messages_buffer_controller(
&self,
query_receiver: ReceivedBufferRequestReceiver,
mixnet_receiver: MixnetMessageReceiver,
) {
console_log!("Starting received messages buffer controller...");
ReceivedMessagesBufferController::new(
self.key_manager.encryption_keypair(),
query_receiver,
mixnet_receiver,
)
.start()
}
async fn start_gateway_client(
&mut self,
mixnet_message_sender: MixnetMessageSender,
ack_sender: AcknowledgementSender,
) -> GatewayClient {
let gateway_id = self.config.gateway_endpoint.gateway_id.clone();
if gateway_id.is_empty() {
panic!("The identity of the gateway is unknown - did you run `get_gateway()`?")
}
let gateway_owner = self.config.gateway_endpoint.gateway_owner.clone();
if gateway_owner.is_empty() {
panic!("The owner of the gateway is unknown - did you run `get_gateway()`?")
}
let gateway_address = self.config.gateway_endpoint.gateway_listener.clone();
if gateway_address.is_empty() {
panic!("The address of the gateway is unknown - did you run `get_gateway()`?")
}
let gateway_identity = identity::PublicKey::from_base58_string(gateway_id)
.expect("provided gateway id is invalid!");
let mut gateway_client = GatewayClient::new(
gateway_address,
self.key_manager.identity_keypair(),
gateway_identity,
gateway_owner,
None,
mixnet_message_sender,
ack_sender,
self.config.debug.gateway_response_timeout,
None,
);
gateway_client.set_disabled_credentials_mode(self.config.disabled_credentials_mode);
let shared_keys = gateway_client
.authenticate_and_start()
.await
.expect("could not authenticate and start up the gateway connection");
self.key_manager.insert_gateway_shared_key(shared_keys);
match self.on_gateway_connect.as_ref() {
Some(callback) => {
callback
.call0(&JsValue::null())
.expect("on connect callback failed!");
}
None => console_log!("Gateway connection established - no callback specified"),
};
gateway_client
}
// future responsible for periodically polling directory server and updating
// the current global view of topology
async fn start_topology_refresher(&mut self, topology_accessor: TopologyAccessor) {
let topology_refresher_config = TopologyRefresherConfig::new(
vec![self.config.validator_api_url.clone()],
self.config.debug.topology_refresh_rate,
env!("CARGO_PKG_VERSION").to_string(),
);
let mut topology_refresher =
TopologyRefresher::new(topology_refresher_config, topology_accessor);
// before returning, block entire runtime to refresh the current network view so that any
// components depending on topology would see a non-empty view
console_log!("Obtaining initial network topology");
topology_refresher.refresh().await;
// TODO: a slightly more graceful termination here
if !topology_refresher.is_topology_routable().await {
panic!(
"The current network topology seem to be insufficient to route any packets through\
- check if enough nodes and a gateway are online"
);
}
console_log!("Starting topology refresher...");
// TODO: re-enable
topology_refresher.start();
}
// controller for sending sphinx packets to mixnet (either real traffic or cover traffic)
// TODO: if we want to send control messages to gateway_client, this CAN'T take the ownership
// over it. Perhaps GatewayClient needs to be thread-shareable or have some channel for
// requests?
fn start_mix_traffic_controller(gateway_client: GatewayClient) -> BatchMixMessageSender {
console_log!("Starting mix traffic controller...");
let (mix_traffic_controller, mix_tx) = MixTrafficController::new(gateway_client);
mix_traffic_controller.start();
mix_tx
}
// TODO: this procedure is extremely overcomplicated, because it's based off native client's behaviour
// which doesn't fully apply in this case
fn start_reconstructed_pusher(
&mut self,
received_buffer_request_sender: ReceivedBufferRequestSender,
) {
let on_message = self.on_message.take();
let on_binary_message = self.on_binary_message.take();
spawn_local(async move {
let (reconstructed_sender, mut reconstructed_receiver) = mpsc::unbounded();
// tell the buffer to start sending stuff to us
received_buffer_request_sender
.unbounded_send(ReceivedBufferMessage::ReceiverAnnounce(
reconstructed_sender,
))
.expect("the buffer request failed!");
let this = JsValue::null();
while let Some(reconstructed) = reconstructed_receiver.next().await {
for msg in reconstructed {
if let Some(ref callback_binary) = on_binary_message {
let arg1 = serde_wasm_bindgen::to_value(&msg.message).unwrap();
callback_binary
.call1(&this, &arg1)
.expect("on binary message failed!");
}
if let Some(ref callback) = on_message {
if msg.reply_surb.is_some() {
console_log!("the received message contained a reply-surb that we do not know how to handle (yet)")
}
let stringified = String::from_utf8_lossy(&msg.message).into_owned();
let arg1 = serde_wasm_bindgen::to_value(&stringified).unwrap();
callback.call1(&this, &arg1).expect("on message failed!");
}
}
}
});
}
pub async fn start(mut self) -> NymClient {
console_log!("Starting wasm client '{}'", self.config.id);
// channels for inter-component communication
// TODO: make the channels be internally created by the relevant components
// rather than creating them here, so say for example the buffer controller would create the request channels
// and would allow anyone to clone the sender channel
// unwrapped_sphinx_sender is the transmitter of mixnet messages received from the gateway
// unwrapped_sphinx_receiver is the receiver for said messages - used by ReceivedMessagesBuffer
let (mixnet_messages_sender, mixnet_messages_receiver) = mpsc::unbounded();
// used for announcing connection or disconnection of a channel for pushing re-assembled messages to
let (received_buffer_request_sender, received_buffer_request_receiver) = mpsc::unbounded();
// channels responsible for controlling real messages
let (input_sender, input_receiver) = mpsc::unbounded::<InputMessage>();
// channels responsible for controlling ack messages
let (ack_sender, ack_receiver) = mpsc::unbounded();
let shared_topology_accessor = TopologyAccessor::new();
// Channel that the real traffix controller can listed to for closing connections.
// Currently unused in the wasm client.
let (_closed_connection_tx, closed_connection_rx) = mpsc::unbounded();
// the components are started in very specific order. Unless you know what you are doing,
// do not change that.
self.start_topology_refresher(shared_topology_accessor.clone())
.await;
self.start_received_messages_buffer_controller(
received_buffer_request_receiver,
mixnet_messages_receiver,
);
let gateway_client = self
.start_gateway_client(mixnet_messages_sender, ack_sender)
.await;
// The sphinx_message_sender is the transmitter for any component generating sphinx packets
// that are to be sent to the mixnet. They are used by cover traffic stream and real
// traffic stream.
// The MixTrafficController then sends the actual traffic
let sphinx_message_sender = Self::start_mix_traffic_controller(gateway_client);
self.start_real_traffic_controller(
shared_topology_accessor.clone(),
ack_receiver,
input_receiver,
sphinx_message_sender.clone(),
closed_connection_rx,
);
if !self.config.debug.disable_loop_cover_traffic_stream {
self.start_cover_traffic_stream(shared_topology_accessor, sphinx_message_sender);
}
self.start_reconstructed_pusher(received_buffer_request_sender);
self.input_tx = Some(input_sender);
self
}
// Right now it's impossible to have async exported functions to take `&mut self` rather than mut self
@@ -121,64 +388,11 @@ impl NymClient {
let input_msg = InputMessage::new_fresh(recipient, message, false, lane);
self.client_input
self.input_tx
.as_ref()
.expect("start method was not called before!")
.input_sender
.send(input_msg)
.await
.expect("InputMessageReceiver has stopped receiving!");
self
}
fn start_reconstructed_pusher(
client_output: ClientOutput,
on_message: Option<js_sys::Function>,
on_binary_message: Option<js_sys::Function>,
) {
ResponsePusher::new(client_output, on_message, on_binary_message).start()
}
pub async fn start(mut self) -> NymClient {
console_log!("Starting the wasm client");
let base_builder = BaseClientBuilder::new(
&self.config.gateway_endpoint,
&self.config.debug,
self.key_manager.take().unwrap(),
None,
true,
vec![self.config.validator_api_url.clone()],
);
self.self_address = Some(base_builder.as_mix_recipient().to_string());
let mut started_client = match base_builder.start_base().await {
Ok(base_client) => base_client,
Err(err) => {
console_error!("failed to start base client components - {}", err);
// proper error handling is left here as an exercise for the reader (hi Mark : ))
panic!("failed to start base client components - {err}")
}
};
match self.on_gateway_connect.as_ref() {
Some(callback) => {
callback
.call0(&JsValue::null())
.expect("on connect callback failed!");
}
None => console_log!("Gateway connection established - no callback specified"),
};
// those should be moved to a completely different struct, but I don't want to break compatibility for now
let client_input = started_client.client_input.register_producer();
let client_output = started_client.client_output.register_consumer();
let on_message = self.on_message.take();
let on_binary_message = self.on_binary_message.take();
Self::start_reconstructed_pusher(client_output, on_message, on_binary_message);
self.client_input = Some(client_input);
self._shutdown = Some(started_client.shutdown_notifier);
.unbounded_send(input_msg)
.unwrap();
self
}
@@ -1,71 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use client_core::client::base_client::ClientOutput;
use client_core::client::received_buffer::{ReceivedBufferMessage, ReconstructedMessagesReceiver};
use futures::channel::mpsc;
use futures::StreamExt;
use wasm_bindgen::JsValue;
use wasm_bindgen_futures::spawn_local;
use wasm_utils::console_log;
pub(crate) struct ResponsePusher {
reconstructed_receiver: ReconstructedMessagesReceiver,
on_message: Option<js_sys::Function>,
on_binary_message: Option<js_sys::Function>,
}
impl ResponsePusher {
pub(crate) fn new(
client_output: ClientOutput,
on_message: Option<js_sys::Function>,
on_binary_message: Option<js_sys::Function>,
) -> Self {
if on_message.is_none() && on_binary_message.is_none() {
// exercise for the reader : )
panic!("neither 'on_message' nor 'on_binary_message' was set!")
}
// register our output
let (reconstructed_sender, reconstructed_receiver) = mpsc::unbounded();
// tell the buffer to start sending stuff to us
client_output
.received_buffer_request_sender
.unbounded_send(ReceivedBufferMessage::ReceiverAnnounce(
reconstructed_sender,
))
.expect("the buffer request failed!");
ResponsePusher {
reconstructed_receiver,
on_message,
on_binary_message,
}
}
pub(crate) fn start(mut self) {
spawn_local(async move {
let this = JsValue::null();
while let Some(reconstructed) = self.reconstructed_receiver.next().await {
for msg in reconstructed {
if let Some(ref callback_binary) = self.on_binary_message {
let arg1 = serde_wasm_bindgen::to_value(&msg.message).unwrap();
callback_binary
.call1(&this, &arg1)
.expect("on binary message failed!");
}
if let Some(ref callback) = self.on_message {
if msg.reply_surb.is_some() {
console_log!("the received message contained a reply-surb that we do not know how to handle (yet)")
}
let stringified = String::from_utf8_lossy(&msg.message).into_owned();
let arg1 = serde_wasm_bindgen::to_value(&stringified).unwrap();
callback.call1(&this, &arg1).expect("on message failed!");
}
}
}
})
}
}
+6 -6
View File
@@ -1,15 +1,15 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use client_core::config::GatewayEndpointConfig;
use client_core::config::GatewayEndpoint;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub async fn get_gateway(api_server: String, preferred: Option<String>) -> GatewayEndpointConfig {
let validator_client = validator_client::client::ApiClient::new(api_server.parse().unwrap());
pub async fn get_gateway(api_server: String, preferred: Option<String>) -> GatewayEndpoint {
let validator_client = validator_client::ApiClient::new(api_server.parse().unwrap());
let gateways = match validator_client.get_cached_gateways().await {
Err(err) => panic!("failed to obtain list of all gateways - {err}"),
Err(err) => panic!("failed to obtain list of all gateways - {}", err),
Ok(gateways) => gateways,
};
@@ -18,7 +18,7 @@ pub async fn get_gateway(api_server: String, preferred: Option<String>) -> Gatew
.iter()
.find(|g| g.gateway.identity_key == preferred)
{
return GatewayEndpointConfig {
return GatewayEndpoint {
gateway_id: details.gateway.identity_key.clone(),
gateway_owner: details.owner.to_string(),
gateway_listener: format!(
@@ -33,7 +33,7 @@ pub async fn get_gateway(api_server: String, preferred: Option<String>) -> Gatew
.first()
.expect("current topology holds no gateways");
GatewayEndpointConfig {
GatewayEndpoint {
gateway_id: details.gateway.identity_key.clone(),
gateway_owner: details.owner.to_string(),
gateway_listener: format!(
+5 -15
View File
@@ -16,21 +16,11 @@ pub enum TransmissionLane {
ConnectionId(ConnectionId),
}
/// Used by the connection controller to report current state for client connections.
pub type ConnectionCommandSender = mpsc::UnboundedSender<ConnectionCommand>;
pub type ConnectionCommandReceiver = mpsc::UnboundedReceiver<ConnectionCommand>;
pub enum ConnectionCommand {
// Announce that at a connection was closed. E.g the `OutQueueControl` uses this to discard
// transmission lanes.
Close(ConnectionId),
// In the network requester for example, we usually want to broadcast active connections
// regularly, so we know what connections we need to request lane queue lengths for from the
// client.
// In the socks5-client, this is not needed since have direct access to the lane queue lengths.
ActiveConnections(Vec<ConnectionId>),
}
/// Announce connections that are closed, for whoever is interested.
/// One usecase is that the network-requester and socks5-client wants to know about this, so that
/// they can forward this to the `OutQueueControl` (via `ClientRequest` for the network-requester)
pub type ClosedConnectionSender = mpsc::UnboundedSender<ConnectionId>;
pub type ClosedConnectionReceiver = mpsc::UnboundedReceiver<ConnectionId>;
// The `OutQueueControl` publishes the backlog per lane, primarily so that upstream can slow down
// if needed.
+3 -1
View File
@@ -26,7 +26,6 @@ network-defaults = { path = "../../network-defaults" }
nymsphinx = { path = "../../nymsphinx" }
pemstore = { path = "../../pemstore" }
validator-client = { path = "../validator-client", optional = true }
task = { path = "../../task" }
[dependencies.tungstenite]
@@ -48,6 +47,9 @@ version = "0.14"
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.credential-storage]
path = "../../credential-storage"
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.task]
path = "../../task"
# wasm-only dependencies
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen]
version = "0.2"
@@ -17,8 +17,6 @@ use credential_storage::error::StorageError;
#[cfg(feature = "coconut")]
use std::str::FromStr;
#[cfg(feature = "coconut")]
use validator_client::client::CoconutApiClient;
#[cfg(feature = "coconut")]
use {
coconut_interface::Base58,
credentials::coconut::{
@@ -26,19 +24,12 @@ use {
},
};
// TODO: make it nicer for wasm (I don't want to touch it for this experiment)
#[cfg(target_arch = "wasm32")]
use crate::wasm_storage::PersistentStorage;
#[cfg(not(target_arch = "wasm32"))]
use credential_storage::PersistentStorage;
#[derive(Clone)]
pub struct BandwidthController<St: Storage = PersistentStorage> {
pub struct BandwidthController<St: Storage> {
#[allow(dead_code)]
storage: St,
#[cfg(feature = "coconut")]
coconut_api_clients: Vec<CoconutApiClient>,
validator_endpoints: Vec<url::Url>,
}
impl<St> BandwidthController<St>
@@ -46,10 +37,10 @@ where
St: Storage + Clone + 'static,
{
#[cfg(feature = "coconut")]
pub fn new(storage: St, coconut_api_clients: Vec<CoconutApiClient>) -> Self {
pub fn new(storage: St, validator_endpoints: Vec<url::Url>) -> Self {
BandwidthController {
storage,
coconut_api_clients,
validator_endpoints,
}
}
@@ -62,7 +53,7 @@ where
pub async fn prepare_coconut_credential(
&self,
) -> Result<coconut_interface::Credential, GatewayClientError> {
let verification_key = obtain_aggregate_verification_key(&self.coconut_api_clients).await?;
let verification_key = obtain_aggregate_verification_key(&self.validator_endpoints).await?;
let bandwidth_credential = self.storage.get_next_coconut_credential().await?;
let voucher_value = u64::from_str(&bandwidth_credential.voucher_value)
.map_err(|_| StorageError::InconsistentData)?;
+42 -73
View File
@@ -2,19 +2,25 @@
// SPDX-License-Identifier: Apache-2.0
use crate::bandwidth::BandwidthController;
use crate::cleanup_socket_message;
use crate::error::GatewayClientError;
use crate::packet_router::PacketRouter;
pub use crate::packet_router::{
AcknowledgementReceiver, AcknowledgementSender, MixnetMessageReceiver, MixnetMessageSender,
};
use crate::socket_state::{PartiallyDelegated, SocketState};
use crate::{cleanup_socket_message, try_decrypt_binary_message};
#[cfg(target_arch = "wasm32")]
use crate::wasm_storage::PersistentStorage;
#[cfg(feature = "coconut")]
use coconut_interface::Credential;
#[cfg(not(target_arch = "wasm32"))]
use credential_storage::PersistentStorage;
use crypto::asymmetric::identity;
use futures::{FutureExt, SinkExt, StreamExt};
use gateway_requests::authentication::encrypted_address::EncryptedAddressBytes;
use gateway_requests::iv::IV;
use gateway_requests::registration::handshake::{client_handshake, SharedKeys};
use gateway_requests::{BinaryRequest, ClientControlRequest, ServerResponse, PROTOCOL_VERSION};
use gateway_requests::{BinaryRequest, ClientControlRequest, ServerResponse};
use log::*;
use network_defaults::{REMAINING_BANDWIDTH_THRESHOLD, TOKENS_TO_BURN};
use nymsphinx::forwarding::packet::MixPacket;
@@ -22,19 +28,13 @@ use rand::rngs::OsRng;
use std::convert::TryFrom;
use std::sync::Arc;
use std::time::Duration;
#[cfg(not(target_arch = "wasm32"))]
use task::ShutdownListener;
use tungstenite::protocol::Message;
#[cfg(feature = "coconut")]
use coconut_interface::Credential;
#[cfg(not(target_arch = "wasm32"))]
use credential_storage::PersistentStorage;
#[cfg(not(target_arch = "wasm32"))]
use tokio_tungstenite::connect_async;
#[cfg(target_arch = "wasm32")]
use crate::wasm_storage::PersistentStorage;
#[cfg(target_arch = "wasm32")]
use wasm_timer;
#[cfg(target_arch = "wasm32")]
@@ -66,9 +66,8 @@ pub struct GatewayClient {
/// Delay between each subsequent reconnection attempt.
reconnection_backoff: Duration,
#[cfg(not(target_arch = "wasm32"))]
/// Listen to shutdown messages.
// TODO: fix this
#[cfg_attr(target_arch = "wasm32", allow(dead_code))]
shutdown: Option<ShutdownListener>,
}
@@ -85,7 +84,7 @@ impl GatewayClient {
ack_sender: AcknowledgementSender,
response_timeout_duration: Duration,
bandwidth_controller: Option<BandwidthController<PersistentStorage>>,
shutdown: Option<ShutdownListener>,
#[cfg(not(target_arch = "wasm32"))] shutdown: Option<ShutdownListener>,
) -> Self {
GatewayClient {
authenticated: false,
@@ -97,12 +96,18 @@ impl GatewayClient {
local_identity,
shared_key,
connection: SocketState::NotConnected,
packet_router: PacketRouter::new(ack_sender, mixnet_message_sender, shutdown.clone()),
packet_router: PacketRouter::new(
ack_sender,
mixnet_message_sender,
#[cfg(not(target_arch = "wasm32"))]
shutdown.clone(),
),
response_timeout_duration,
bandwidth_controller,
should_reconnect_on_failure: true,
reconnection_attempts: DEFAULT_RECONNECTION_ATTEMPTS,
reconnection_backoff: DEFAULT_RECONNECTION_BACKOFF,
#[cfg(not(target_arch = "wasm32"))]
shutdown,
}
}
@@ -130,7 +135,7 @@ impl GatewayClient {
gateway_owner: String,
local_identity: Arc<identity::KeyPair>,
response_timeout_duration: Duration,
shutdown: Option<ShutdownListener>,
#[cfg(not(target_arch = "wasm32"))] shutdown: Option<ShutdownListener>,
) -> Self {
use futures::channel::mpsc;
@@ -138,7 +143,12 @@ impl GatewayClient {
// perfectly fine here, because it's not meant to be used
let (ack_tx, _) = mpsc::unbounded();
let (mix_tx, _) = mpsc::unbounded();
let packet_router = PacketRouter::new(ack_tx, mix_tx, shutdown.clone());
let packet_router = PacketRouter::new(
ack_tx,
mix_tx,
#[cfg(not(target_arch = "wasm32"))]
shutdown.clone(),
);
GatewayClient {
authenticated: false,
@@ -156,6 +166,7 @@ impl GatewayClient {
should_reconnect_on_failure: false,
reconnection_attempts: DEFAULT_RECONNECTION_ATTEMPTS,
reconnection_backoff: DEFAULT_RECONNECTION_BACKOFF,
#[cfg(not(target_arch = "wasm32"))]
shutdown,
}
}
@@ -295,8 +306,6 @@ impl GatewayClient {
let m_shutdown = self.shutdown.clone();
async {
if let Some(mut s) = m_shutdown {
// TODO: fix this by marking as success _after_ the select
s.mark_as_success();
s.recv().await
} else {
std::future::pending::<()>().await
@@ -327,15 +336,7 @@ impl GatewayClient {
};
match ws_msg {
Message::Binary(bin_msg) => {
// if we have established the shared key already, attempt to use it for decryption
// otherwise there's not much we can do apart from just routing what we have on hand
if let Some(shared_keys) = &self.shared_key {
if let Some(plaintext) = try_decrypt_binary_message(bin_msg, shared_keys) {
if let Err(err) = self.packet_router.route_received(vec![plaintext]) {
log::warn!("Route received failed: {:?}", err);
}
}
} else if let Err(err) = self.packet_router.route_received(vec![bin_msg]) {
if let Err(err) = self.packet_router.route_received(vec![bin_msg]) {
log::warn!("Route received failed: {:?}", err);
}
}
@@ -391,10 +392,13 @@ impl GatewayClient {
.batch_send_without_response(messages)
.await
{
error!("failed to batch send messages - {err}...");
error!("failed to batch send messages - {}...", err);
// we must ensure we do not leave the task still active
if let Err(err) = self.recover_socket_connection().await {
error!("... and the delegated stream has also errored out - {err}")
error!(
"... and the delegated stream has also errored out - {}",
err
)
}
Err(err)
} else {
@@ -414,10 +418,13 @@ impl GatewayClient {
SocketState::Available(ref mut conn) => Ok(conn.send(msg).await?),
SocketState::PartiallyDelegated(ref mut partially_delegated) => {
if let Err(err) = partially_delegated.send_without_response(msg).await {
error!("failed to send message without response - {err}...");
error!("failed to send message without response - {}...", err);
// we must ensure we do not leave the task still active
if let Err(err) = self.recover_socket_connection().await {
error!("... and the delegated stream has also errored out - {err}")
error!(
"... and the delegated stream has also errored out - {}",
err
)
}
Err(err)
} else {
@@ -429,33 +436,6 @@ impl GatewayClient {
}
}
fn check_gateway_protocol(
&self,
gateway_protocol: Option<u8>,
) -> Result<(), GatewayClientError> {
// right now there are no failure cases here, but this might change in the future
match gateway_protocol {
None => {
warn!("the gateway we're connected to has not specified its protocol version. It's probably running version < 1.1.X, but that's still fine for now. It will become a hard error in 1.2.0");
// note: in 1.2.0 we will have to return a hard error here
Ok(())
}
Some(v) if v != PROTOCOL_VERSION => {
let err = GatewayClientError::IncompatibleProtocol {
gateway: Some(v),
current: PROTOCOL_VERSION,
};
error!("{err}");
Err(err)
}
Some(_) => {
info!("the gateway is using exactly the same protocol version as we are. We're good to continue!");
Ok(())
}
}
}
async fn register(&mut self) -> Result<(), GatewayClientError> {
if !self.connection.is_established() {
return Err(GatewayClientError::ConnectionNotEstablished);
@@ -478,20 +458,11 @@ impl GatewayClient {
.map_err(GatewayClientError::RegistrationFailure),
_ => unreachable!(),
}?;
let (authentication_status, gateway_protocol) = match self.read_control_response().await? {
ServerResponse::Register {
protocol_version,
status,
} => (status, protocol_version),
ServerResponse::Error { message } => {
return Err(GatewayClientError::GatewayError(message))
}
_ => return Err(GatewayClientError::UnexpectedResponse),
};
self.check_gateway_protocol(gateway_protocol)?;
self.authenticated = authentication_status;
self.authenticated = match self.read_control_response().await? {
ServerResponse::Register { status } => Ok(status),
ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)),
_ => Err(GatewayClientError::UnexpectedResponse),
}?;
if self.authenticated {
self.shared_key = Some(Arc::new(shared_key));
}
@@ -530,11 +501,9 @@ impl GatewayClient {
match self.send_websocket_message(msg).await? {
ServerResponse::Authenticate {
protocol_version,
status,
bandwidth_remaining,
} => {
self.check_gateway_protocol(protocol_version)?;
self.authenticated = status;
self.bandwidth_remaining = bandwidth_remaining;
Ok(())
@@ -85,9 +85,6 @@ pub enum GatewayClientError {
#[error("Failed to send mixnet message")]
MixnetMsgSenderFailedToSend,
#[error("Attempted to negotiate connection with gateway using incompatible protocol version. Ours is {current} and the gateway reports {gateway:?}")]
IncompatibleProtocol { gateway: Option<u8>, current: u8 },
}
impl GatewayClientError {
@@ -3,9 +3,6 @@
use crate::error::GatewayClientError;
pub use client::GatewayClient;
use gateway_requests::registration::handshake::SharedKeys;
use gateway_requests::BinaryResponse;
use log::warn;
pub use packet_router::{
AcknowledgementReceiver, AcknowledgementSender, MixnetMessageReceiver, MixnetMessageSender,
};
@@ -40,21 +37,3 @@ pub(crate) fn cleanup_socket_messages(
None => Err(GatewayClientError::ConnectionAbruptlyClosed),
}
}
pub(crate) fn try_decrypt_binary_message(
bin_msg: Vec<u8>,
shared_keys: &SharedKeys,
) -> Option<Vec<u8>> {
match BinaryResponse::try_from_encrypted_tagged_bytes(bin_msg, shared_keys) {
Ok(bin_response) => match bin_response {
BinaryResponse::PushedMixMessage(plaintext) => Some(plaintext),
},
Err(err) => {
warn!(
"message received from the gateway was malformed! - {:?}",
err
);
None
}
}
}
@@ -4,13 +4,15 @@
// JS: I personally don't like this name very much, but could not think of anything better.
// I will gladly take any suggestions on how to rename this.
use crate::error::GatewayClientError;
use futures::channel::mpsc;
use log::*;
use nymsphinx::addressing::nodes::MAX_NODE_ADDRESS_UNPADDED_LEN;
use nymsphinx::params::packet_sizes::PacketSize;
#[cfg(not(target_arch = "wasm32"))]
use task::ShutdownListener;
use crate::error::GatewayClientError;
pub type MixnetMessageSender = mpsc::UnboundedSender<Vec<Vec<u8>>>;
pub type MixnetMessageReceiver = mpsc::UnboundedReceiver<Vec<Vec<u8>>>;
@@ -21,6 +23,7 @@ pub type AcknowledgementReceiver = mpsc::UnboundedReceiver<Vec<Vec<u8>>>;
pub struct PacketRouter {
ack_sender: AcknowledgementSender,
mixnet_message_sender: MixnetMessageSender,
#[cfg(not(target_arch = "wasm32"))]
shutdown: Option<ShutdownListener>,
}
@@ -28,11 +31,12 @@ impl PacketRouter {
pub fn new(
ack_sender: AcknowledgementSender,
mixnet_message_sender: MixnetMessageSender,
shutdown: Option<ShutdownListener>,
#[cfg(not(target_arch = "wasm32"))] shutdown: Option<ShutdownListener>,
) -> Self {
PacketRouter {
ack_sender,
mixnet_message_sender,
#[cfg(not(target_arch = "wasm32"))]
shutdown,
}
}
@@ -82,6 +86,7 @@ impl PacketRouter {
if !received_messages.is_empty() {
trace!("routing 'real'");
if let Err(err) = self.mixnet_message_sender.unbounded_send(received_messages) {
#[cfg(not(target_arch = "wasm32"))]
if let Some(shutdown) = &mut self.shutdown {
if shutdown.is_shutdown_poll() {
// This should ideally not happen, but it's ok
@@ -1,13 +1,14 @@
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::cleanup_socket_messages;
use crate::error::GatewayClientError;
use crate::packet_router::PacketRouter;
use crate::{cleanup_socket_messages, try_decrypt_binary_message};
use futures::channel::oneshot;
use futures::stream::{SplitSink, SplitStream};
use futures::{SinkExt, StreamExt};
use gateway_requests::registration::handshake::SharedKeys;
use gateway_requests::BinaryResponse;
use log::*;
use std::sync::Arc;
#[cfg(not(target_arch = "wasm32"))]
@@ -49,9 +50,21 @@ impl PartiallyDelegated {
match ws_msg {
Message::Binary(bin_msg) => {
// this function decrypts the request and checks the MAC
if let Some(plaintext) = try_decrypt_binary_message(bin_msg, shared_key) {
plaintexts.push(plaintext)
}
let plaintext = match BinaryResponse::try_from_encrypted_tagged_bytes(
bin_msg, shared_key,
) {
Ok(bin_response) => match bin_response {
BinaryResponse::PushedMixMessage(plaintext) => plaintext,
},
Err(err) => {
warn!(
"message received from the gateway was malformed! - {:?}",
err
);
continue;
}
};
plaintexts.push(plaintext)
}
// I think that in the future we should perhaps have some sequence number system, i.e.
// so each request/response pair can be easily identified, so that if messages are
@@ -10,11 +10,10 @@ rust-version = "1.56"
[dependencies]
base64 = "0.13"
colored = "2.0"
coconut-dkg-common = { path = "../../cosmwasm-smart-contracts/coconut-dkg" }
contracts-common = { path = "../../cosmwasm-smart-contracts/contracts-common" }
cw3 = "0.13.1"
mixnet-contract-common = { path= "../../cosmwasm-smart-contracts/mixnet-contract" }
vesting-contract-common = { path= "../../cosmwasm-smart-contracts/vesting-contract" }
contracts-common = { path = "../../cosmwasm-smart-contracts/contracts-common" }
coconut-bandwidth-contract-common = { path= "../../cosmwasm-smart-contracts/coconut-bandwidth-contract" }
multisig-contract-common = { path = "../../cosmwasm-smart-contracts/multisig-contract" }
vesting-contract = { path = "../../../contracts/vesting" }
@@ -38,7 +37,6 @@ async-trait = { version = "0.1.51", optional = true }
bip39 = { version = "1", features = ["rand"], optional = true }
config = { path = "../../config", optional = true }
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support", features = ["rpc", "bip32", "cosmwasm"], optional = true}
cw3 = { version = "0.13.4", optional = true }
prost = { version = "0.10", default-features = false, optional = true }
flate2 = { version = "1.0.20", optional = true }
sha2 = { version = "0.9.5", optional = true }
@@ -55,7 +53,6 @@ nymd-client = [
"bip39",
"config",
"cosmrs",
"cw3",
"prost",
"flate2",
"sha2",
+18 -202
View File
@@ -2,21 +2,13 @@
// SPDX-License-Identifier: Apache-2.0
use crate::{validator_api, ValidatorClientError};
use coconut_dkg_common::types::NodeIndex;
#[cfg(feature = "nymd-client")]
use coconut_dkg_common::{
dealer::ContractDealing, types::DealerDetails, verification_key::ContractVKShare,
};
#[cfg(feature = "nymd-client")]
use coconut_interface::Base58;
use coconut_interface::VerificationKey;
use mixnet_contract_common::mixnode::MixNodeDetails;
use mixnet_contract_common::MixId;
use mixnet_contract_common::{GatewayBond, IdentityKeyRef};
#[cfg(feature = "nymd-client")]
use std::str::FromStr;
use url::Url;
use validator_api_requests::coconut::{
BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse,
BlindSignRequestBody, BlindedSignatureResponse, CosmosAddressResponse, VerificationKeyResponse,
VerifyCredentialBody, VerifyCredentialResponse,
};
use validator_api_requests::models::{
GatewayCoreStatusResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse,
@@ -24,12 +16,10 @@ use validator_api_requests::models::{
};
#[cfg(feature = "nymd-client")]
use crate::nymd::traits::{DkgQueryClient, MixnetQueryClient, MultisigQueryClient};
use crate::nymd::traits::MixnetQueryClient;
#[cfg(feature = "nymd-client")]
use crate::nymd::{self, CosmWasmClient, NymdClient, QueryNymdClient, SigningNymdClient};
#[cfg(feature = "nymd-client")]
use cw3::ProposalResponse;
#[cfg(feature = "nymd-client")]
use mixnet_contract_common::{
mixnode::MixNodeBond,
pending_events::{PendingEpochEvent, PendingIntervalEvent},
@@ -37,7 +27,6 @@ use mixnet_contract_common::{
};
#[cfg(feature = "nymd-client")]
use network_defaults::NymNetworkDetails;
use url::Url;
#[cfg(feature = "nymd-client")]
use validator_api_requests::models::MixNodeBondAnnotated;
@@ -54,9 +43,6 @@ pub struct Config {
gateway_page_limit: Option<u32>,
mixnode_delegations_page_limit: Option<u32>,
rewarded_set_page_limit: Option<u32>,
dealers_page_limit: Option<u32>,
verification_key_page_limit: Option<u32>,
proposals_page_limit: Option<u32>,
}
#[cfg(feature = "nymd-client")]
@@ -86,9 +72,6 @@ impl Config {
gateway_page_limit: None,
mixnode_delegations_page_limit: None,
rewarded_set_page_limit: None,
dealers_page_limit: None,
verification_key_page_limit: None,
proposals_page_limit: None,
})
}
@@ -136,9 +119,6 @@ pub struct Client<C> {
gateway_page_limit: Option<u32>,
mixnode_delegations_page_limit: Option<u32>,
rewarded_set_page_limit: Option<u32>,
dealers_page_limit: Option<u32>,
verification_key_page_limit: Option<u32>,
proposals_page_limit: Option<u32>,
// ideally they would have been read-only, but unfortunately rust doesn't have such features
pub validator_api: validator_api::Client,
@@ -165,9 +145,6 @@ impl Client<SigningNymdClient> {
gateway_page_limit: config.gateway_page_limit,
mixnode_delegations_page_limit: config.mixnode_delegations_page_limit,
rewarded_set_page_limit: config.rewarded_set_page_limit,
dealers_page_limit: config.dealers_page_limit,
verification_key_page_limit: config.verification_key_page_limit,
proposals_page_limit: config.proposals_page_limit,
validator_api: validator_api_client,
nymd: nymd_client,
})
@@ -201,9 +178,6 @@ impl Client<QueryNymdClient> {
gateway_page_limit: config.gateway_page_limit,
mixnode_delegations_page_limit: config.mixnode_delegations_page_limit,
rewarded_set_page_limit: config.rewarded_set_page_limit,
dealers_page_limit: config.dealers_page_limit,
verification_key_page_limit: config.verification_key_page_limit,
proposals_page_limit: config.proposals_page_limit,
validator_api: validator_api_client,
nymd: nymd_client,
})
@@ -545,135 +519,6 @@ impl<C> Client<C> {
Ok(events)
}
pub async fn get_all_nymd_current_dealers(
&self,
) -> Result<Vec<DealerDetails>, ValidatorClientError>
where
C: CosmWasmClient + Sync + Send,
{
let mut dealers = Vec::new();
let mut start_after = None;
loop {
let mut paged_response = self
.nymd
.get_current_dealers_paged(start_after.take(), self.dealers_page_limit)
.await?;
dealers.append(&mut paged_response.dealers);
if let Some(start_after_res) = paged_response.start_next_after {
start_after = Some(start_after_res.into_string())
} else {
break;
}
}
Ok(dealers)
}
pub async fn get_all_nymd_past_dealers(
&self,
) -> Result<Vec<DealerDetails>, ValidatorClientError>
where
C: CosmWasmClient + Sync + Send,
{
let mut dealers = Vec::new();
let mut start_after = None;
loop {
let mut paged_response = self
.nymd
.get_past_dealers_paged(start_after.take(), self.dealers_page_limit)
.await?;
dealers.append(&mut paged_response.dealers);
if let Some(start_after_res) = paged_response.start_next_after {
start_after = Some(start_after_res.into_string())
} else {
break;
}
}
Ok(dealers)
}
pub async fn get_all_nymd_epoch_dealings(
&self,
idx: usize,
) -> Result<Vec<ContractDealing>, ValidatorClientError>
where
C: CosmWasmClient + Sync + Send,
{
let mut dealings = Vec::new();
let mut start_after = None;
loop {
let mut paged_response = self
.nymd
.get_dealings_paged(idx, start_after.take(), self.dealers_page_limit)
.await?;
dealings.append(&mut paged_response.dealings);
if let Some(start_after_res) = paged_response.start_next_after {
start_after = Some(start_after_res.into_string())
} else {
break;
}
}
Ok(dealings)
}
pub async fn get_all_nymd_verification_key_shares(
&self,
) -> Result<Vec<ContractVKShare>, ValidatorClientError>
where
C: CosmWasmClient + Sync + Send,
{
let mut shares = Vec::new();
let mut start_after = None;
loop {
let mut paged_response = self
.nymd
.get_vk_shares_paged(start_after.take(), self.verification_key_page_limit)
.await?;
shares.append(&mut paged_response.shares);
if let Some(start_after_res) = paged_response.start_next_after {
start_after = Some(start_after_res.into_string())
} else {
break;
}
}
Ok(shares)
}
pub async fn get_all_nymd_proposals(
&self,
) -> Result<Vec<ProposalResponse>, ValidatorClientError>
where
C: CosmWasmClient + Sync + Send,
{
let mut proposals = Vec::new();
let mut start_after = None;
loop {
let mut paged_response = self
.nymd
.list_proposals(start_after.take(), self.proposals_page_limit)
.await?;
let last_id = paged_response.proposals.last().map(|prop| prop.id);
proposals.append(&mut paged_response.proposals);
if let Some(start_after_res) = last_id {
start_after = Some(start_after_res)
} else {
break;
}
}
Ok(proposals)
}
}
// validator-api wrappers
@@ -727,53 +572,14 @@ impl<C> Client<C> {
) -> Result<BlindedSignatureResponse, ValidatorClientError> {
Ok(self.validator_api.blind_sign(request_body).await?)
}
}
#[derive(Clone)]
pub struct CoconutApiClient {
pub api_client: ApiClient,
pub verification_key: VerificationKey,
pub node_id: NodeIndex,
#[cfg(feature = "nymd-client")]
pub cosmos_address: cosmrs::AccountId,
}
#[cfg(feature = "nymd-client")]
impl CoconutApiClient {
pub async fn all_coconut_api_clients<C>(
nymd_client: &Client<C>,
) -> Result<Vec<Self>, ValidatorClientError>
where
C: CosmWasmClient + Sync + Send,
{
Ok(nymd_client
.get_all_nymd_verification_key_shares()
.await?
.into_iter()
.filter_map(Self::try_from)
.collect())
}
fn try_from(share: ContractVKShare) -> Option<Self> {
if share.verified {
if let Ok(url_address) = Url::parse(&share.announce_address) {
if let Ok(verification_key) = VerificationKey::try_from_bs58(&share.share) {
if let Ok(cosmos_address) = cosmrs::AccountId::from_str(share.owner.as_str()) {
return Some(CoconutApiClient {
api_client: ApiClient::new(url_address),
verification_key,
node_id: share.node_index,
cosmos_address,
});
}
}
}
}
None
pub async fn get_coconut_verification_key(
&self,
) -> Result<VerificationKeyResponse, ValidatorClientError> {
Ok(self.validator_api.get_coconut_verification_key().await?)
}
}
#[derive(Clone)]
pub struct ApiClient {
pub validator_api: validator_api::Client,
// TODO: perhaps if we really need it at some (currently I don't see any reasons for it)
@@ -879,6 +685,16 @@ impl ApiClient {
.await?)
}
pub async fn get_coconut_verification_key(
&self,
) -> Result<VerificationKeyResponse, ValidatorClientError> {
Ok(self.validator_api.get_coconut_verification_key().await?)
}
pub async fn get_cosmos_address(&self) -> Result<CosmosAddressResponse, ValidatorClientError> {
Ok(self.validator_api.get_cosmos_address().await?)
}
pub async fn verify_bandwidth_credential(
&self,
request_body: &VerifyCredentialBody,
@@ -9,8 +9,7 @@ mod error;
pub mod nymd;
pub mod validator_api;
#[cfg(feature = "nymd-client")]
pub use crate::client::{ApiClient, CoconutApiClient};
pub use crate::client::ApiClient;
pub use crate::error::ValidatorClientError;
pub use validator_api_requests::*;
@@ -6,9 +6,6 @@ use cosmrs::tendermint::abci;
use itertools::Itertools;
use serde::{Deserialize, Serialize};
pub use coconut_bandwidth_contract_common::event_attributes::*;
pub use coconut_dkg_common::event_attributes::*;
// it seems that currently validators just emit stringified events (which are also returned as part of deliverTx response)
// as theirs logs
#[derive(Debug, Serialize, Deserialize)]
@@ -65,7 +65,6 @@ pub struct Config {
pub(crate) bandwidth_claim_contract_address: Option<AccountId>,
pub(crate) coconut_bandwidth_contract_address: Option<AccountId>,
pub(crate) multisig_contract_address: Option<AccountId>,
pub(crate) coconut_dkg_contract_address: Option<AccountId>,
// TODO: add this in later commits
// pub(crate) gas_price: GasPrice,
}
@@ -119,10 +118,6 @@ impl Config {
details.contracts.multisig_contract_address.as_ref(),
prefix,
)?,
coconut_dkg_contract_address: Self::parse_optional_account(
details.contracts.coconut_dkg_contract_address.as_ref(),
prefix,
)?,
})
}
}
@@ -280,14 +275,6 @@ impl<C> NymdClient<C> {
self.config.multisig_contract_address.as_ref().unwrap()
}
// TODO: this should get changed into Result<&AccountId, NymdError> (or Option<&AccountId> in future commits
// note: what unwrap is doing here is just moving a failure that would have normally
// occurred in `connect` when attempting to parse an empty address,
// so it's not introducing new source of failure (just moves it)
pub fn coconut_dkg_contract_address(&self) -> &AccountId {
self.config.coconut_dkg_contract_address.as_ref().unwrap()
}
pub fn set_simulated_gas_multiplier(&mut self, multiplier: f32) {
self.simulated_gas_multiplier = multiplier;
}
@@ -1,126 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::nymd::error::NymdError;
use crate::nymd::{CosmWasmClient, NymdClient};
use async_trait::async_trait;
use coconut_dkg_common::dealer::{
DealerDetailsResponse, PagedDealerResponse, PagedDealingsResponse,
};
use coconut_dkg_common::msg::QueryMsg as DkgQueryMsg;
use coconut_dkg_common::types::EpochState;
use coconut_dkg_common::verification_key::PagedVKSharesResponse;
use cosmrs::AccountId;
#[async_trait]
pub trait DkgQueryClient {
async fn get_current_epoch_state(&self) -> Result<EpochState, NymdError>;
async fn get_dealer_details(
&self,
address: &AccountId,
) -> Result<DealerDetailsResponse, NymdError>;
async fn get_current_dealers_paged(
&self,
start_after: Option<String>,
page_limit: Option<u32>,
) -> Result<PagedDealerResponse, NymdError>;
async fn get_past_dealers_paged(
&self,
start_after: Option<String>,
page_limit: Option<u32>,
) -> Result<PagedDealerResponse, NymdError>;
async fn get_dealings_paged(
&self,
idx: usize,
start_after: Option<String>,
page_limit: Option<u32>,
) -> Result<PagedDealingsResponse, NymdError>;
async fn get_vk_shares_paged(
&self,
start_after: Option<String>,
page_limit: Option<u32>,
) -> Result<PagedVKSharesResponse, NymdError>;
}
#[async_trait]
impl<C> DkgQueryClient for NymdClient<C>
where
C: CosmWasmClient + Send + Sync,
{
async fn get_current_epoch_state(&self) -> Result<EpochState, NymdError> {
let request = DkgQueryMsg::GetCurrentEpochState {};
self.client
.query_contract_smart(self.coconut_dkg_contract_address(), &request)
.await
}
async fn get_dealer_details(
&self,
address: &AccountId,
) -> Result<DealerDetailsResponse, NymdError> {
let request = DkgQueryMsg::GetDealerDetails {
dealer_address: address.to_string(),
};
self.client
.query_contract_smart(self.coconut_dkg_contract_address(), &request)
.await
}
async fn get_current_dealers_paged(
&self,
start_after: Option<String>,
page_limit: Option<u32>,
) -> Result<PagedDealerResponse, NymdError> {
let request = DkgQueryMsg::GetCurrentDealers {
start_after,
limit: page_limit,
};
self.client
.query_contract_smart(self.coconut_dkg_contract_address(), &request)
.await
}
async fn get_past_dealers_paged(
&self,
start_after: Option<String>,
page_limit: Option<u32>,
) -> Result<PagedDealerResponse, NymdError> {
let request = DkgQueryMsg::GetPastDealers {
start_after,
limit: page_limit,
};
self.client
.query_contract_smart(self.coconut_dkg_contract_address(), &request)
.await
}
async fn get_dealings_paged(
&self,
idx: usize,
start_after: Option<String>,
page_limit: Option<u32>,
) -> Result<PagedDealingsResponse, NymdError> {
let request = DkgQueryMsg::GetDealing {
idx: idx as u64,
limit: page_limit,
start_after,
};
self.client
.query_contract_smart(self.coconut_dkg_contract_address(), &request)
.await
}
async fn get_vk_shares_paged(
&self,
start_after: Option<String>,
page_limit: Option<u32>,
) -> Result<PagedVKSharesResponse, NymdError> {
let request = DkgQueryMsg::GetVerificationKeys {
limit: page_limit,
start_after,
};
self.client
.query_contract_smart(self.coconut_dkg_contract_address(), &request)
.await
}
}
@@ -1,100 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::nymd::cosmwasm_client::types::ExecuteResult;
use crate::nymd::error::NymdError;
use crate::nymd::{Fee, NymdClient, SigningCosmWasmClient};
use async_trait::async_trait;
use coconut_dkg_common::msg::ExecuteMsg as DkgExecuteMsg;
use coconut_dkg_common::types::EncodedBTEPublicKeyWithProof;
use coconut_dkg_common::verification_key::VerificationKeyShare;
use contracts_common::dealings::ContractSafeBytes;
#[async_trait]
pub trait DkgSigningClient {
async fn register_dealer(
&self,
bte_key: EncodedBTEPublicKeyWithProof,
announce_address: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError>;
async fn submit_dealing_bytes(
&self,
commitment: ContractSafeBytes,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError>;
async fn submit_verification_key_share(
&self,
share: VerificationKeyShare,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError>;
}
#[async_trait]
impl<C> DkgSigningClient for NymdClient<C>
where
C: SigningCosmWasmClient + Send + Sync,
{
async fn register_dealer(
&self,
bte_key: EncodedBTEPublicKeyWithProof,
announce_address: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError> {
let req = DkgExecuteMsg::RegisterDealer {
bte_key_with_proof: bte_key,
announce_address,
};
self.client
.execute(
self.address(),
self.coconut_dkg_contract_address(),
&req,
fee.unwrap_or_default(),
format!("registering {} as a dealer", self.address()),
vec![],
)
.await
}
async fn submit_dealing_bytes(
&self,
dealing_bytes: ContractSafeBytes,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError> {
let req = DkgExecuteMsg::CommitDealing { dealing_bytes };
self.client
.execute(
self.address(),
self.coconut_dkg_contract_address(),
&req,
fee.unwrap_or_default(),
"dealing commitment",
vec![],
)
.await
}
async fn submit_verification_key_share(
&self,
share: VerificationKeyShare,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError> {
let req = DkgExecuteMsg::CommitVerificationKeyShare { share };
self.client
.execute(
self.address(),
self.coconut_dkg_contract_address(),
&req,
fee.unwrap_or_default(),
"verification key share commitment",
vec![],
)
.await
}
}
@@ -180,35 +180,6 @@ pub trait MixnetSigningClient {
.await
}
async fn pledge_more(
&self,
additional_pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError> {
self.execute_mixnet_contract(
fee,
MixnetExecuteMsg::PledgeMore {},
vec![additional_pledge],
)
.await
}
async fn pledge_more_on_behalf(
&self,
owner: AccountId,
additional_pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError> {
self.execute_mixnet_contract(
fee,
MixnetExecuteMsg::PledgeMoreOnBehalf {
owner: owner.to_string(),
},
vec![additional_pledge],
)
.await
}
async fn unbond_mixnode(&self, fee: Option<Fee>) -> Result<ExecuteResult, NymdError> {
self.execute_mixnet_contract(fee, MixnetExecuteMsg::UnbondMixnode {}, vec![])
.await
@@ -3,8 +3,6 @@
mod coconut_bandwidth_query_client;
mod coconut_bandwidth_signing_client;
mod dkg_query_client;
mod dkg_signing_client;
mod mixnet_query_client;
mod mixnet_signing_client;
mod multisig_query_client;
@@ -14,8 +12,6 @@ mod vesting_signing_client;
pub use coconut_bandwidth_query_client::CoconutBandwidthQueryClient;
pub use coconut_bandwidth_signing_client::CoconutBandwidthSigningClient;
pub use dkg_query_client::DkgQueryClient;
pub use dkg_signing_client::DkgSigningClient;
pub use mixnet_query_client::MixnetQueryClient;
pub use mixnet_signing_client::MixnetSigningClient;
pub use multisig_query_client::MultisigQueryClient;
@@ -4,19 +4,13 @@
use crate::nymd::error::NymdError;
use crate::nymd::{CosmWasmClient, NymdClient};
use cw3::{ProposalListResponse, ProposalResponse};
use multisig_contract_common::msg::QueryMsg;
use multisig_contract_common::msg::{ProposalResponse, QueryMsg};
use async_trait::async_trait;
#[async_trait]
pub trait MultisigQueryClient {
async fn get_proposal(&self, proposal_id: u64) -> Result<ProposalResponse, NymdError>;
async fn list_proposals(
&self,
start_after: Option<u64>,
limit: Option<u32>,
) -> Result<ProposalListResponse, NymdError>;
}
#[async_trait]
@@ -27,15 +21,4 @@ impl<C: CosmWasmClient + Sync + Send> MultisigQueryClient for NymdClient<C> {
.query_contract_smart(self.multisig_contract_address(), &request)
.await
}
async fn list_proposals(
&self,
start_after: Option<u64>,
limit: Option<u32>,
) -> Result<ProposalListResponse, NymdError> {
let request = QueryMsg::ListProposals { start_after, limit };
self.client
.query_contract_smart(self.multisig_contract_address(), &request)
.await
}
}
@@ -7,11 +7,11 @@ use crate::nymd::error::NymdError;
use crate::nymd::{Fee, NymdClient};
use coconut_bandwidth_contract_common::msg::ExecuteMsg as CoconutBandwidthExecuteMsg;
use cw3::Vote;
use multisig_contract_common::msg::ExecuteMsg;
use async_trait::async_trait;
use cosmwasm_std::{to_binary, Coin, CosmosMsg, WasmMsg};
use cw3::Vote;
#[async_trait]
pub trait MultisigSigningClient {
@@ -64,21 +64,6 @@ pub trait VestingSigningClient {
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError>;
async fn vesting_pledge_more(
&self,
additional_pledge: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NymdError> {
self.execute_vesting_contract(
fee,
VestingExecuteMsg::PledgeMore {
amount: additional_pledge.into(),
},
vec![],
)
.await
}
async fn vesting_unbond_mixnode(&self, fee: Option<Fee>) -> Result<ExecuteResult, NymdError>;
async fn vesting_track_unbond_mixnode(
@@ -9,7 +9,8 @@ use reqwest::Response;
use serde::{Deserialize, Serialize};
use url::Url;
use validator_api_requests::coconut::{
BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse,
BlindSignRequestBody, BlindedSignatureResponse, CosmosAddressResponse, VerificationKeyResponse,
VerifyCredentialBody, VerifyCredentialResponse,
};
use validator_api_requests::models::{
GatewayCoreStatusResponse, GatewayStatusReportResponse, GatewayUptimeHistoryResponse,
@@ -26,7 +27,6 @@ type Params<'a, K, V> = &'a [(K, V)];
const NO_PARAMS: Params<'_, &'_ str, &'_ str> = &[];
#[derive(Clone)]
pub struct Client {
url: Url,
reqwest_client: reqwest::Client,
@@ -443,6 +443,34 @@ impl Client {
.await
}
pub async fn get_coconut_verification_key(
&self,
) -> Result<VerificationKeyResponse, ValidatorAPIError> {
self.query_validator_api(
&[
routes::API_VERSION,
routes::COCONUT_ROUTES,
routes::BANDWIDTH,
routes::COCONUT_VERIFICATION_KEY,
],
NO_PARAMS,
)
.await
}
pub async fn get_cosmos_address(&self) -> Result<CosmosAddressResponse, ValidatorAPIError> {
self.query_validator_api(
&[
routes::API_VERSION,
routes::COCONUT_ROUTES,
routes::BANDWIDTH,
routes::COCONUT_COSMOS_ADDRESS,
],
NO_PARAMS,
)
.await
}
pub async fn verify_bandwidth_credential(
&self,
request_body: &VerifyCredentialBody,
@@ -15,6 +15,8 @@ pub const BANDWIDTH: &str = "bandwidth";
pub const COCONUT_BLIND_SIGN: &str = "blind-sign";
pub const COCONUT_PARTIAL_BANDWIDTH_CREDENTIAL: &str = "partial-bandwidth-credential";
pub const COCONUT_VERIFICATION_KEY: &str = "verification-key";
pub const COCONUT_COSMOS_ADDRESS: &str = "cosmos-address";
pub const COCONUT_VERIFY_BANDWIDTH_CREDENTIAL: &str = "verify-bandwidth-credential";
pub const STATUS_ROUTES: &str = "status";
-1
View File
@@ -22,7 +22,6 @@ thiserror = "1"
time = { version = "0.3.6", features = ["parsing", "formatting"] }
toml = "0.5.6"
url = "2.2"
tap = "1"
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" }
cosmwasm-std = { version = "1.0.0" }
-6
View File
@@ -15,10 +15,4 @@ pub enum ContextError {
// TODO: improve this to return known errors
#[error("failed to create client - {0}")]
NymdError(String),
#[error("{0}")]
NymdErrorPassthrough(#[from] validator_client::nymd::error::NymdError),
#[error("{0}")]
ValidatorClientError(#[from] validator_client::ValidatorClientError),
}
+4 -5
View File
@@ -6,7 +6,6 @@ use network_defaults::{
var_names::{API_VALIDATOR, MIXNET_CONTRACT_ADDRESS, NYMD_VALIDATOR, VESTING_CONTRACT_ADDRESS},
NymNetworkDetails,
};
use tap::prelude::*;
use validator_client::nymd::{self, AccountId, NymdClient, QueryNymdClient, SigningNymdClient};
pub use validator_client::validator_api::Client as ValidatorApiClient;
@@ -59,7 +58,7 @@ pub fn create_signing_client(
network_details: &NymNetworkDetails,
) -> Result<SigningClient, ContextError> {
let client_config = nymd::Config::try_from_nym_network_details(network_details)
.tap_err(|e| log::error!("Failed to get client config - {:?}", e))?;
.expect("failed to construct valid validator client config with the provided network");
// get mnemonic
let mnemonic = match std::env::var("MNEMONIC") {
@@ -88,7 +87,7 @@ pub fn create_query_client(
network_details: &NymNetworkDetails,
) -> Result<QueryClient, ContextError> {
let client_config = nymd::Config::try_from_nym_network_details(network_details)
.tap_err(|e| log::error!("Failed to get client config - {:?}", e))?;
.expect("failed to construct valid validator client config with the provided network");
let nymd_url = network_details
.endpoints
@@ -108,7 +107,7 @@ pub fn create_signing_client_with_validator_api(
network_details: &NymNetworkDetails,
) -> Result<SigningClientWithValidatorAPI, ContextError> {
let client_config = validator_client::Config::try_from_nym_network_details(network_details)
.tap_err(|e| log::error!("Failed to get client config - {:?}", e))?;
.expect("failed to construct valid validator client config with the provided network");
// get mnemonic
let mnemonic = match std::env::var("MNEMONIC") {
@@ -130,7 +129,7 @@ pub fn create_query_client_with_validator_api(
network_details: &NymNetworkDetails,
) -> Result<QueryClientWithValidatorAPI, ContextError> {
let client_config = validator_client::Config::try_from_nym_network_details(network_details)
.tap_err(|e| log::error!("Failed to get client config - {:?}", e))?;
.expect("failed to construct valid validator client config with the provided network");
match validator_client::client::Client::new_query(client_config) {
Ok(client) => Ok(client),
@@ -3,11 +3,11 @@
use clap::Parser;
use cosmrs::AccountId;
use log::{error, info};
use log::info;
use validator_client::nymd::{Coin, VestingQueryClient};
use crate::context::QueryClient;
use crate::context::SigningClient;
use crate::utils::show_error;
use crate::utils::{pretty_coin, pretty_cosmwasm_coin};
@@ -18,16 +18,8 @@ pub struct Args {
pub address: Option<AccountId>,
}
pub async fn balance(args: Args, client: QueryClient, address_from_mnemonic: Option<AccountId>) {
if args.address.is_none() && address_from_mnemonic.is_none() {
error!("Please specify an account address or a mnemonic to get the balance for");
return;
}
let account_id = args
.address
.unwrap_or_else(|| address_from_mnemonic.expect("please provide a mnemonic"));
pub async fn balance(args: Args, client: SigningClient) {
let account_id = args.address.unwrap_or_else(|| client.address().clone());
let vesting_address = account_id.to_string();
let denom = client.current_chain_details().mix_denom.base.as_str();
@@ -4,11 +4,11 @@
use clap::Parser;
use cosmrs::AccountId;
use cosmwasm_std::Coin as CosmWasmCoin;
use log::{error, info};
use log::info;
use validator_client::nymd::{Coin, VestingQueryClient};
use crate::context::QueryClient;
use crate::context::SigningClient;
use crate::utils::show_error;
use crate::utils::{pretty_coin, pretty_cosmwasm_coin};
@@ -19,18 +19,8 @@ pub struct Args {
pub address: Option<AccountId>,
}
pub async fn query(args: Args, client: QueryClient, address_from_mnemonic: Option<AccountId>) {
if args.address.is_none() && address_from_mnemonic.is_none() {
error!("Please specify an account address or a mnemonic to get the balance for");
return;
}
let account_id = args
.address
.unwrap_or_else(|| address_from_mnemonic.expect("please provide a mnemonic"));
info!("Checking account {} for a vesting schedule...", account_id);
pub async fn query(args: Args, client: SigningClient) {
let account_id = args.address.unwrap_or_else(|| client.address().clone());
let vesting_address = account_id.to_string();
let denom = client.current_chain_details().mix_denom.base.as_str();
@@ -1,4 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub const BANDWIDTH_PROPOSAL_ID: &str = "proposal_id";
@@ -1,5 +1,4 @@
pub mod deposit;
pub mod event_attributes;
pub mod events;
pub mod msg;
pub mod spend_credential;
@@ -1,15 +0,0 @@
[package]
name = "coconut-dkg-common"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cosmwasm-std = "1.0.0"
cw-utils = "0.13.4"
schemars = "0.8"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
contracts-common = { path = "../contracts-common" }
multisig-contract-common = { path = "../multisig-contract" }
@@ -1,102 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::types::{ContractSafeBytes, EncodedBTEPublicKeyWithProof, NodeIndex};
use cosmwasm_std::Addr;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
#[serde(rename_all = "snake_case")]
pub struct DealerDetails {
pub address: Addr,
pub bte_public_key_with_proof: EncodedBTEPublicKeyWithProof,
pub announce_address: String,
pub assigned_index: NodeIndex,
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum DealerType {
Current,
Past,
Unknown,
}
impl DealerType {
pub fn is_current(&self) -> bool {
matches!(&self, DealerType::Current)
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub struct DealerDetailsResponse {
pub details: Option<DealerDetails>,
pub dealer_type: DealerType,
}
impl DealerDetailsResponse {
pub fn new(details: Option<DealerDetails>, dealer_type: DealerType) -> Self {
DealerDetailsResponse {
details,
dealer_type,
}
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub struct PagedDealerResponse {
pub dealers: Vec<DealerDetails>,
pub per_page: usize,
pub start_next_after: Option<Addr>,
}
impl PagedDealerResponse {
pub fn new(
dealers: Vec<DealerDetails>,
per_page: usize,
start_next_after: Option<Addr>,
) -> Self {
PagedDealerResponse {
dealers,
per_page,
start_next_after,
}
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub struct ContractDealing {
pub dealing: ContractSafeBytes,
pub dealer: Addr,
}
impl ContractDealing {
pub fn new(dealing: ContractSafeBytes, dealer: Addr) -> Self {
ContractDealing { dealing, dealer }
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub struct PagedDealingsResponse {
pub dealings: Vec<ContractDealing>,
pub per_page: usize,
pub start_next_after: Option<Addr>,
}
impl PagedDealingsResponse {
pub fn new(
dealings: Vec<ContractDealing>,
per_page: usize,
start_next_after: Option<Addr>,
) -> Self {
PagedDealingsResponse {
dealings,
per_page,
start_next_after,
}
}
}
@@ -1,5 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub const NODE_INDEX: &str = "node_index";
pub const DKG_PROPOSAL_ID: &str = "proposal_id";
@@ -1,5 +0,0 @@
pub mod dealer;
pub mod event_attributes;
pub mod msg;
pub mod types;
pub mod verification_key;

Some files were not shown because too many files have changed in this diff Show More