Compare commits
253 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fb86ce9eea | |||
| d03f769b14 | |||
| 86e9463c42 | |||
| 5376c2a4ba | |||
| cbeac10383 | |||
| 63a4bdf5a6 | |||
| 0e0a62938d | |||
| f1d0bd0bf4 | |||
| 404473c128 | |||
| a980d6f804 | |||
| c6a5e08188 | |||
| 886e2ed5e7 | |||
| aa545ee6c6 | |||
| 62741889bc | |||
| 64c963e36e | |||
| d6f87c40ed | |||
| 39562e653a | |||
| e548d6f1f8 | |||
| 48def795d9 | |||
| 80017d258d | |||
| 6d6d9d4359 | |||
| c708a7cc12 | |||
| ea35a37d4c | |||
| e40d25a97b | |||
| cf903aa2e5 | |||
| 0a2e0d6a8f | |||
| 8eb3dbd191 | |||
| a0661fecb2 | |||
| ea68d42886 | |||
| d4298c61a0 | |||
| 65ed611c24 | |||
| d713b926f8 | |||
| f305901a18 | |||
| 082a8ad8ee | |||
| 031092815b | |||
| 6b96e474f7 | |||
| f4fd08f64e | |||
| 78247b973b | |||
| 6b52132501 | |||
| d2f33180e2 | |||
| dc71f6e94d | |||
| cc7161c113 | |||
| bbb46ebd90 | |||
| bc3fd236d8 | |||
| ea95288940 | |||
| b182ed6925 | |||
| 017c9d2504 | |||
| 50c7d717c0 | |||
| b473aeb3be | |||
| ac10e03aec | |||
| fe2e1c29a2 | |||
| 2eee5195cc | |||
| 3bd4343a39 | |||
| 74feb065f9 | |||
| 65b819c649 | |||
| bd12305a68 | |||
| 4854e929ed | |||
| 5709c45a50 | |||
| a7f1242961 | |||
| 8b14321c4a | |||
| 5f88517e1d | |||
| dddc6eae57 | |||
| 8beb33fe92 | |||
| c7d8f3af97 | |||
| 1e84f87bf5 | |||
| bf5b8fab85 | |||
| 70ae45b6c9 | |||
| b0960091c1 | |||
| b97a12186f | |||
| 36496a519a | |||
| 87fad25ac3 | |||
| df3d478caa | |||
| 751e3ccd27 | |||
| 74a4546d72 | |||
| 2587d00b9e | |||
| cf25c331c0 | |||
| a24c7e4783 | |||
| 9ea725bf83 | |||
| fe78d4faf0 | |||
| 0df063f9f6 | |||
| ad8fdbdddf | |||
| 8d3aea969e | |||
| 078ca0b0d1 | |||
| 273dc41559 | |||
| 61bc74148f | |||
| 5b14eecf82 | |||
| 2746cabecc | |||
| 666cbcf2cc | |||
| 30110aff65 | |||
| 1954c49ac2 | |||
| 70328ba114 | |||
| c43b2dc117 | |||
| 4a8a9096dd | |||
| 96ab4325e3 | |||
| 11e2ba33e7 | |||
| 95db26c35b | |||
| aed96b2d44 | |||
| 326d5fcec8 | |||
| 88d813b9c1 | |||
| e181a1cfb1 | |||
| afae6fc9a5 | |||
| 23c13a409a | |||
| 29091aab8e | |||
| 1d522143a2 | |||
| 775ce0f95d | |||
| b019786c5a | |||
| 9b9c01fb8f | |||
| 6c857b5daf | |||
| 5ea084d286 | |||
| fdbe3a1f6a | |||
| 5f4926dd49 | |||
| 1f132a4eaa | |||
| 097d2d51cc | |||
| a2078d997b | |||
| 558d4b899d | |||
| 4fcb98e839 | |||
| 702bb202a3 | |||
| 71ff2c04a0 | |||
| cb30384eaf | |||
| e911c2fbc0 | |||
| bb005a39f5 | |||
| ea72c37083 | |||
| a24dd8b9bc | |||
| b006c01397 | |||
| f7f2a51458 | |||
| 408396c900 | |||
| a97c913bb1 | |||
| dba5a9caef | |||
| 3e39573feb | |||
| ac312e9109 | |||
| 8a2a7dc0ce | |||
| 5b15ed6f15 | |||
| 9684b7ffbd | |||
| 57703af642 | |||
| 9dd4c5d871 | |||
| 4813cf6c18 | |||
| 29f48efe49 | |||
| 83b76c6b37 | |||
| 4120234155 | |||
| 42b444ddf8 | |||
| acd832d8e5 | |||
| 75a726ebbe | |||
| b9fbab6024 | |||
| 06ed8716a1 | |||
| beddd3dcee | |||
| bdc285dbbb | |||
| 4d02dfb899 | |||
| 80d6cb5c12 | |||
| 7422ab69ba | |||
| 60c8185bea | |||
| 9d3c7c0be8 | |||
| d6048fae52 | |||
| ddb7b0e872 | |||
| 4995dde705 | |||
| 63bfe4246f | |||
| 4654b360e0 | |||
| 7c5c19986a | |||
| 46edca0bd4 | |||
| b03a1f922d | |||
| 9616c90433 | |||
| ea49f0a265 | |||
| 2470c8b9b5 | |||
| 7d001965ec | |||
| 11b1089d83 | |||
| 52699d7598 | |||
| 87443dd624 | |||
| a02a1b0385 | |||
| e0f2fa6705 | |||
| 7949b07213 | |||
| bd20fd0b1f | |||
| 57d3d6fd0f | |||
| 06eff652dd | |||
| 246decac4a | |||
| a14ae298ae | |||
| 43188051d3 | |||
| 8aa15fa467 | |||
| f77b037ef7 | |||
| 75dbc5d790 | |||
| e6bcd706ff | |||
| 9a077a0928 | |||
| 11fd42e187 | |||
| 6e499e5996 | |||
| f98cc73a1f | |||
| db9bf4d3fa | |||
| b30628529d | |||
| 3be615f74f | |||
| a8ccd2ec17 | |||
| 2d71caf50a | |||
| 4d08d62fc2 | |||
| 40e5595d65 | |||
| 6780d58a98 | |||
| ccbf06f179 | |||
| e6ecf71cc3 | |||
| c66312ee96 | |||
| 808802aeb4 | |||
| fb38f24e5c | |||
| 65f148a5ad | |||
| 2bcdc5d11e | |||
| 5ecb03ffe9 | |||
| 8505989dad | |||
| ed5e865db7 | |||
| ea3194e0c3 | |||
| 190bff4ffe | |||
| c509555d15 | |||
| f0d66fdd88 | |||
| 14847d01b7 | |||
| f4711902bd | |||
| 32ad93c57e | |||
| c4a68dbbe6 | |||
| 1140503eba | |||
| d92d6877a4 | |||
| b9fed9f455 | |||
| 5e45f7d3a5 | |||
| 1133acd8bd | |||
| 08e6f3c4b7 | |||
| 0ed546b739 | |||
| b4f2233d2b | |||
| d8369eb4c9 | |||
| db3d379219 | |||
| a746738d48 | |||
| c67f0fb7f8 | |||
| 7c12a3422c | |||
| f047209baf | |||
| cc5a69d4f1 | |||
| 776d27a899 | |||
| 829dbb1695 | |||
| 52156e0c38 | |||
| 901275fd63 | |||
| a0a73421d0 | |||
| 70fcb8c046 | |||
| 80c81fa3d7 | |||
| 27e6539e98 | |||
| a2d10d9956 | |||
| 142a2bb26b | |||
| 677d8c7fce | |||
| 3563ad67b2 | |||
| c11a4c23fa | |||
| ce676c2bb5 | |||
| a096f9d54e | |||
| d9b6823106 | |||
| 89bcb5649b | |||
| 53444cf55a | |||
| f2e460a96b | |||
| b5b7b7255b | |||
| 894a46688a | |||
| 5d3550a569 | |||
| b43dab4f83 | |||
| 1bdb58571d | |||
| a74a99d81d | |||
| c09d3af92f | |||
| 76773c58d7 | |||
| fd90175e87 | |||
| 0eb859467e |
+1
-1
@@ -14,7 +14,7 @@ GEOIPUPDATE_LICENSE_KEY=xxx
|
||||
# List of space-separated database edition IDs. Edition IDs may
|
||||
# consist of letters, digits, and dashes. For example, GeoIP2-City
|
||||
# would download the GeoIP2 City database (GeoIP2-City).
|
||||
GEOIPUPDATE_EDITION_IDS=GeoLite2-Country
|
||||
GEOIPUPDATE_EDITION_IDS=GeoLite2-City
|
||||
# The number of hours between geoipupdate runs. If this is not set
|
||||
# or is set to 0, geoipupdate will run once and exit.
|
||||
GEOIPUPDATE_FREQUENCY=72
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
path: .github/workflows/support-files/notifications/deny.message
|
||||
notification:
|
||||
needs: cargo-deny
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: custom-runner-linux
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v2
|
||||
@@ -35,6 +35,10 @@ jobs:
|
||||
with:
|
||||
name: report
|
||||
path: .github/workflows/support-files/notifications
|
||||
- name: install npm
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Keybase - Node Install
|
||||
run: npm install
|
||||
working-directory: .github/workflows/support-files
|
||||
@@ -47,6 +51,11 @@ jobs:
|
||||
KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
|
||||
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
|
||||
KEYBASE_NYM_CHANNEL: "security"
|
||||
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
|
||||
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
|
||||
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
|
||||
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
|
||||
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
|
||||
@@ -58,6 +58,11 @@ jobs:
|
||||
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
|
||||
KEYBASE_NYM_CHANNEL: "ci-ts-packages"
|
||||
IS_SUCCESS: "${{ job.status == 'success' }}"
|
||||
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
|
||||
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
|
||||
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
|
||||
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
|
||||
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
|
||||
@@ -11,12 +11,13 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: [ self-hosted, custom-linux ]
|
||||
# Enable sccache via environment variable
|
||||
# Enable sccache via environment variable
|
||||
env:
|
||||
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
|
||||
steps:
|
||||
- name: Install Dependencies (Linux)
|
||||
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools
|
||||
continue-on-error: true
|
||||
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v2
|
||||
@@ -41,6 +42,12 @@ jobs:
|
||||
command: build
|
||||
args: --workspace
|
||||
|
||||
- name: Build all examples
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace --examples
|
||||
|
||||
- name: Run all tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
@@ -56,6 +63,7 @@ jobs:
|
||||
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
name: Clippy checks
|
||||
continue-on-error: true
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --workspace --all-features
|
||||
@@ -64,7 +72,7 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: --workspace -- -D warnings
|
||||
args: --workspace --all-targets --all-features -- -D warnings
|
||||
|
||||
# COCONUT stuff
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ jobs:
|
||||
steps:
|
||||
- name: Install Dependencies (Linux)
|
||||
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools libayatana-appindicator3-dev
|
||||
continue-on-error: true
|
||||
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v2
|
||||
@@ -45,6 +46,7 @@ jobs:
|
||||
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
name: Clippy checks
|
||||
continue-on-error: true
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --manifest-path nym-connect/Cargo.toml --workspace --all-features
|
||||
|
||||
@@ -17,6 +17,7 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install rsync
|
||||
run: sudo apt-get install rsync
|
||||
continue-on-error: true
|
||||
- uses: rlespinasse/github-slug-action@v3.x
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
@@ -73,6 +74,11 @@ jobs:
|
||||
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
|
||||
KEYBASE_NYM_CHANNEL: "ci-network-explorer"
|
||||
IS_SUCCESS: "${{ job.status == 'success' }}"
|
||||
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
|
||||
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
|
||||
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
|
||||
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
|
||||
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
|
||||
@@ -38,6 +38,12 @@ 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:
|
||||
@@ -50,6 +56,18 @@ jobs:
|
||||
with:
|
||||
command: clean
|
||||
|
||||
- name: Build all examples
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace --examples
|
||||
|
||||
- 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:
|
||||
@@ -69,12 +87,6 @@ jobs:
|
||||
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' }}
|
||||
@@ -83,6 +95,7 @@ jobs:
|
||||
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
name: Clippy checks
|
||||
continue-on-error: true
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --all-features
|
||||
@@ -92,7 +105,7 @@ jobs:
|
||||
if: ${{ matrix.rust != 'nightly' }}
|
||||
with:
|
||||
command: clippy
|
||||
args: --workspace --all-targets -- -D warnings
|
||||
args: --workspace --all-targets --all-features -- -D warnings
|
||||
|
||||
- name: Reclaim some disk space
|
||||
uses: actions-rs/cargo@v1
|
||||
@@ -160,12 +173,17 @@ jobs:
|
||||
|
||||
notification:
|
||||
needs: build
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: custom-runner-linux
|
||||
steps:
|
||||
- name: Collect jobs status
|
||||
uses: technote-space/workflow-conclusion-action@v2
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v2
|
||||
- name: install npm
|
||||
uses: actions/setup-node@v3
|
||||
if: env.WORKFLOW_CONCLUSION == 'failure'
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Keybase - Node Install
|
||||
if: env.WORKFLOW_CONCLUSION == 'failure'
|
||||
run: npm install
|
||||
@@ -183,6 +201,11 @@ jobs:
|
||||
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
|
||||
KEYBASE_NYM_CHANNEL: "ci-nightly"
|
||||
IS_SUCCESS: "${{ env.WORKFLOW_CONCLUSION == 'success' }}"
|
||||
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
|
||||
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
|
||||
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
|
||||
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
|
||||
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
|
||||
@@ -98,6 +98,7 @@ jobs:
|
||||
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
name: Clippy checks
|
||||
continue-on-error: true
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --all-features
|
||||
@@ -175,12 +176,17 @@ jobs:
|
||||
|
||||
notification:
|
||||
needs: [build,get_release]
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: custom-runner-linux
|
||||
steps:
|
||||
- name: Collect jobs status
|
||||
uses: technote-space/workflow-conclusion-action@v2
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v3
|
||||
- name: install npm
|
||||
uses: actions/setup-node@v3
|
||||
if: env.WORKFLOW_CONCLUSION == 'failure'
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Keybase - Node Install
|
||||
if: env.WORKFLOW_CONCLUSION == 'failure'
|
||||
run: npm install
|
||||
@@ -198,6 +204,11 @@ jobs:
|
||||
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
|
||||
KEYBASE_NYM_CHANNEL: "ci-nightly-release"
|
||||
IS_SUCCESS: "${{ env.WORKFLOW_CONCLUSION == 'success' }}"
|
||||
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
|
||||
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
|
||||
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
|
||||
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
|
||||
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
|
||||
@@ -98,6 +98,7 @@ jobs:
|
||||
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
name: Clippy checks
|
||||
continue-on-error: true
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --all-features
|
||||
@@ -175,12 +176,17 @@ jobs:
|
||||
|
||||
notification:
|
||||
needs: [build,get_release]
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: custom-runner-linux
|
||||
steps:
|
||||
- name: Collect jobs status
|
||||
uses: technote-space/workflow-conclusion-action@v2
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v3
|
||||
- name: install npm
|
||||
uses: actions/setup-node@v3
|
||||
if: env.WORKFLOW_CONCLUSION == 'failure'
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Keybase - Node Install
|
||||
if: env.WORKFLOW_CONCLUSION == 'failure'
|
||||
run: npm install
|
||||
@@ -198,6 +204,11 @@ jobs:
|
||||
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
|
||||
KEYBASE_NYM_CHANNEL: "ci-nightly-release"
|
||||
IS_SUCCESS: "${{ env.WORKFLOW_CONCLUSION == 'success' }}"
|
||||
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
|
||||
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
|
||||
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
|
||||
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
|
||||
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
|
||||
@@ -16,6 +16,7 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install rsync
|
||||
run: sudo apt-get install rsync
|
||||
continue-on-error: true
|
||||
- uses: rlespinasse/github-slug-action@v3.x
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
@@ -54,6 +55,11 @@ jobs:
|
||||
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
|
||||
KEYBASE_NYM_CHANNEL: "ci-nym-connect"
|
||||
IS_SUCCESS: "${{ job.status == 'success' }}"
|
||||
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
|
||||
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
|
||||
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
|
||||
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
|
||||
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
|
||||
@@ -60,7 +60,7 @@ jobs:
|
||||
target/release/nym-gateway
|
||||
target/release/nym-mixnode
|
||||
target/release/nym-socks5-client
|
||||
target/release/nym-validator-api
|
||||
target/release/nym-api
|
||||
target/release/nym-network-requester
|
||||
target/release/nym-network-statistics
|
||||
target/release/nym-cli
|
||||
@@ -75,7 +75,7 @@ jobs:
|
||||
target/release/nym-gateway
|
||||
target/release/nym-mixnode
|
||||
target/release/nym-socks5-client
|
||||
target/release/nym-validator-api
|
||||
target/release/nym-api
|
||||
target/release/nym-network-requester
|
||||
target/release/nym-network-statistics
|
||||
target/release/nym-cli
|
||||
|
||||
@@ -12,6 +12,7 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install rsync
|
||||
run: sudo apt-get install rsync
|
||||
continue-on-error: true
|
||||
- uses: rlespinasse/github-slug-action@v3.x
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
@@ -50,6 +51,11 @@ jobs:
|
||||
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
|
||||
KEYBASE_NYM_CHANNEL: "ci-nym-wallet"
|
||||
IS_SUCCESS: "${{ job.status == 'success' }}"
|
||||
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
|
||||
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
|
||||
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
|
||||
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
|
||||
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
|
||||
@@ -2,6 +2,13 @@ KEYBASE_NYM_CHANNEL=
|
||||
KEYBASE_NYMBOT_USERNAME=
|
||||
KEYBASE_NYMBOT_PAPERKEY=
|
||||
|
||||
MATRIX_SERVER=
|
||||
MATRIX_ROOM=
|
||||
MATRIX_ROOM_OF_SHAME=
|
||||
MATRIX_USER_ID=
|
||||
MATRIX_TOKEN=
|
||||
MATRIX_DEVICE_ID=
|
||||
|
||||
NYM_NOTIFICATION_KIND=nightly
|
||||
NYM_PROJECT_NAME=Nightly Build
|
||||
|
||||
|
||||
@@ -2,4 +2,6 @@ node_modules
|
||||
.idea
|
||||
|
||||
# don't commit the lock file to avoid cross-platform issues
|
||||
package-lock.json
|
||||
package-lock.json
|
||||
|
||||
scratch
|
||||
@@ -1,6 +1,7 @@
|
||||
require('dotenv').config();
|
||||
|
||||
const Bot = require('keybase-bot');
|
||||
const { sendMatrixMessage } = require('./send_message_to_matrix');
|
||||
|
||||
let context = {
|
||||
kinds: ['nym-wallet', 'ts-packages', 'network-explorer', 'nightly', 'nym-connect','security'],
|
||||
@@ -38,6 +39,28 @@ function validateContext() {
|
||||
'Paperkey is not defined. Please set env var KEYBASE_NYMBOT_PAPERKEY',
|
||||
);
|
||||
}
|
||||
if (context.env.MATRIX_ROOM) {
|
||||
if (!context.env.MATRIX_SERVER) {
|
||||
throw new Error(
|
||||
'Matrix server is not defined. Please set env var MATRIX_SERVER',
|
||||
);
|
||||
}
|
||||
if (!context.env.MATRIX_USER_ID) {
|
||||
throw new Error(
|
||||
'Matrix user id is not defined. Please set env var MATRIX_USER_ID',
|
||||
);
|
||||
}
|
||||
if (!context.env.MATRIX_TOKEN) {
|
||||
throw new Error(
|
||||
'Matrix token is not defined. Please set env var MATRIX_TOKEN',
|
||||
);
|
||||
}
|
||||
if (!context.env.MATRIX_DEVICE_ID) {
|
||||
throw new Error(
|
||||
'Matrix device id is not defined. Please set env var MATRIX_DEVICE_ID',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,6 +170,13 @@ async function main() {
|
||||
console.log('-----------------------------------------');
|
||||
}
|
||||
await sendKeybaseMessage(messageBody);
|
||||
if(context.env.MATRIX_ROOM) {
|
||||
await sendMatrixMessage(context, messageBody, context.env.MATRIX_ROOM)
|
||||
}
|
||||
if(context.env.MATRIX_ROOM_OF_SHAME && context.env.IS_SUCCESS !== 'true') {
|
||||
// when a job fails
|
||||
await sendMatrixMessage(context, messageBody, context.env.MATRIX_ROOM_OF_SHAME)
|
||||
}
|
||||
}
|
||||
|
||||
// call main function and let NodeJS handle the promise
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
const sdk = require('matrix-js-sdk');
|
||||
global.Olm = require('olm');
|
||||
const { LocalStorage } = require('node-localstorage');
|
||||
const localStorage = new LocalStorage('./scratch');
|
||||
const {
|
||||
LocalStorageCryptoStore,
|
||||
} = require('matrix-js-sdk/lib/crypto/store/localStorage-crypto-store');
|
||||
|
||||
const vfile = require('to-vfile');
|
||||
const unified = require('unified');
|
||||
const remarkParse = require('remark-parse');
|
||||
const remarkHtml = require('remark-html');
|
||||
const emoji = require('remark-emoji');
|
||||
|
||||
// hide all matrix client output
|
||||
console.error = (error) => console.log('❌ error: ', error);
|
||||
process.stderr.write = () => {};
|
||||
process.stdout.write = () => {};
|
||||
|
||||
|
||||
function createClient(context, room, message) {
|
||||
const server = context.env.MATRIX_SERVER;
|
||||
const token = context.env.MATRIX_TOKEN;
|
||||
const deviceId = context.env.MATRIX_DEVICE_ID;
|
||||
const userId = context.env.MATRIX_USER_ID;
|
||||
|
||||
const client = sdk.createClient({
|
||||
baseUrl: server,
|
||||
accessToken: token,
|
||||
userId,
|
||||
deviceId,
|
||||
sessionStore: new sdk.WebStorageSessionStore(localStorage),
|
||||
cryptoStore: new LocalStorageCryptoStore(localStorage),
|
||||
});
|
||||
|
||||
client.on('sync', async function(state, prevState, res) {
|
||||
if (state !== 'PREPARED') return;
|
||||
client.setGlobalErrorOnUnknownDevices(false);
|
||||
try {
|
||||
await client.joinRoom(room);
|
||||
await client.sendEvent(
|
||||
room,
|
||||
'm.room.message',
|
||||
{
|
||||
msgtype: 'm.text',
|
||||
format: 'org.matrix.custom.html',
|
||||
body: message,
|
||||
formatted_body: message,
|
||||
},
|
||||
'',
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Job failed: ' + error.message);
|
||||
}
|
||||
client.stopClient();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
async function markdownToHtml(messageAsMarkdown) {
|
||||
const file = await unified()
|
||||
.use(emoji)
|
||||
.use(remarkParse)
|
||||
.use(remarkHtml)
|
||||
.process(await vfile({ path: 'test.md', contents: messageAsMarkdown}));
|
||||
return String(file);
|
||||
}
|
||||
|
||||
async function sendMatrixMessage(contextArg, messageAsMarkdown, roomId) {
|
||||
const messageAsHtml = await markdownToHtml(messageAsMarkdown);
|
||||
const client = createClient(contextArg, roomId, messageAsHtml);
|
||||
await client.initCrypto();
|
||||
await client.startClient({ initialSyncLimit: 1 });
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
sendMatrixMessage,
|
||||
};
|
||||
@@ -11,7 +11,15 @@
|
||||
"dotenv": "^16.0.0",
|
||||
"handlebars": "^4.7.7",
|
||||
"keybase-bot": "^3.6.1",
|
||||
"octokit": "^1.7.1"
|
||||
"matrix-js-sdk": "^9.3.0",
|
||||
"node-localstorage": "^2.1.6",
|
||||
"octokit": "^1.7.1",
|
||||
"olm": "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz",
|
||||
"remark-emoji": "^2.2.0",
|
||||
"remark-html": "^13.0.2",
|
||||
"remark-parse": "^9.0.0",
|
||||
"to-vfile": "^6.1.0",
|
||||
"unified": "^9.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "2.3.2"
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
name: CI for linting Typescript
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'ts-packages/**'
|
||||
- 'sdk/typescript/**'
|
||||
- nym-connect
|
||||
- nym-wallet
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: custom-runner-linux
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install rsync
|
||||
run: sudo apt-get install rsync
|
||||
- uses: rlespinasse/github-slug-action@v3.x
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Setup yarn
|
||||
run: npm install -g yarn
|
||||
- name: Lint
|
||||
run: yarn && yarn lint
|
||||
- name: Keybase - Node Install
|
||||
run: npm install
|
||||
working-directory: .github/workflows/support-files
|
||||
- name: Keybase - Send Notification
|
||||
env:
|
||||
NYM_NOTIFICATION_KIND: ts-packages
|
||||
NYM_PROJECT_NAME: "ts-packages"
|
||||
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
|
||||
NYM_CI_WWW_LOCATION: "ts-${{ 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-ts-packages"
|
||||
IS_SUCCESS: "${{ job.status == 'success' }}"
|
||||
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
|
||||
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
|
||||
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
|
||||
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
|
||||
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
@@ -16,6 +16,7 @@ jobs:
|
||||
steps:
|
||||
- name: Install Dependencies (Linux)
|
||||
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools
|
||||
continue-on-error: true
|
||||
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v2
|
||||
@@ -48,6 +49,7 @@ jobs:
|
||||
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
name: Clippy checks
|
||||
continue-on-error: true
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --manifest-path nym-wallet/Cargo.toml --workspace --all-features
|
||||
|
||||
@@ -29,11 +29,6 @@ jobs:
|
||||
command: build
|
||||
args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown --features=coconut
|
||||
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --manifest-path clients/webassembly/Cargo.toml
|
||||
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
|
||||
+4
-1
@@ -38,4 +38,7 @@ validator-config
|
||||
validator-api-config.toml
|
||||
dist
|
||||
storybook-static
|
||||
envs/qwerty.env
|
||||
envs/qwerty.env
|
||||
Cargo.lock
|
||||
nym-connect/Cargo.lock
|
||||
.parcel-cache
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
{
|
||||
"mainnet":[{
|
||||
"nymd_url":"https://rpc.nyx.nodes.guru/",
|
||||
"api_url":"https://api.nyx.nodes.guru/"
|
||||
}]
|
||||
}
|
||||
"mainnet": [
|
||||
{
|
||||
"nyxd_url": "https://rpc.nyx.nodes.guru/",
|
||||
"api_url": "https://api.nyx.nodes.guru/"
|
||||
}
|
||||
]
|
||||
}
|
||||
+94
-42
@@ -6,24 +6,82 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
### Added
|
||||
|
||||
- socks5: send status message for service ready, and network-requester error response
|
||||
- dkg rerun from scratch and dkg-specific epochs ([#2839])
|
||||
- nym-sdk: add support for surb storage ([#2870])
|
||||
- nym-sdk: enable reply-SURBs by default ([#2874])
|
||||
|
||||
[#2839]: https://github.com/nymtech/nym/pull/2839
|
||||
[#2870]: https://github.com/nymtech/nym/pull/2870
|
||||
[#2874]: https://github.com/nymtech/nym/pull/2874
|
||||
|
||||
|
||||
## [v1.1.6] (2023-01-17)
|
||||
|
||||
### Added
|
||||
|
||||
- nym-sdk: added initial version of a Rust client sdk
|
||||
- nym-api: added `/circulating-supply` endpoint ([#2814])
|
||||
- nym-api: add endpoint listing detailed gateway info by @octol in https://github.com/nymtech/nym/pull/2833
|
||||
|
||||
### Changed
|
||||
|
||||
- all-binaries: improved error logging ([#2686])
|
||||
- native client: bring shutdown logic up to the same level as socks5-client
|
||||
- nym-api, coconut-dkg contract: automatic, time-based dkg epoch state advancement ([#2670])
|
||||
- streamline override_config functions -> there's a lot of duplicate if statements everywhere ([#2774])
|
||||
- clean-up nym-api startup arguments/flags to use clap 3 and its macro-derived arguments ([#2772])
|
||||
- renamed all references to validator_api to nym_api
|
||||
- renamed all references to nymd to nyxd ([#2696])
|
||||
- all-binaries: standarised argument names (note: old names should still be accepted) ([#2762]
|
||||
|
||||
[#2686]: https://github.com/nymtech/nym/pull/2686
|
||||
[#2670]: https://github.com/nymtech/nym/pull/2670
|
||||
### Fixed
|
||||
|
||||
- nym-api: should now correctly use `rewarding.enabled` config flag ([#2753])
|
||||
|
||||
[#2696]: https://github.com/nymtech/nym/pull/2696
|
||||
[#2753]: https://github.com/nymtech/nym/pull/2753
|
||||
[#2762]: https://github.com/nymtech/nym/pull/2762
|
||||
[#2814]: https://github.com/nymtech/nym/pull/2814
|
||||
[#2772]: https://github.com/nymtech/nym/pull/2772
|
||||
[#2774]: https://github.com/nymtech/nym/pull/2774
|
||||
|
||||
## [v1.1.5] (2023-01-10)
|
||||
|
||||
### Added
|
||||
|
||||
- socks5: send status message for service ready, and network-requester error response in https://github.com/nymtech/nym/pull/2715
|
||||
|
||||
### Changed
|
||||
|
||||
- all-binaries: improved error logging in https://github.com/nymtech/nym/pull/2686
|
||||
- native client: bring shutdown logic up to the same level as socks5-client in https://github.com/nymtech/nym/pull/2695
|
||||
- nym-api, coconut-dkg contract: automatic, time-based dkg epoch state advancement in https://github.com/nymtech/nym/pull/2670
|
||||
- DKG resharing unit test by @neacsu in https://github.com/nymtech/nym/pull/2668
|
||||
- Renaming validator-api to nym-api by @futurechimp in https://github.com/nymtech/nym/pull/1863
|
||||
- Modify wasm specific make targets by @neacsu in https://github.com/nymtech/nym/pull/2693
|
||||
- client: create websocket handler builder by @octol in https://github.com/nymtech/nym/pull/2700
|
||||
- Outfox and Lion by @durch in https://github.com/nymtech/nym/pull/2730
|
||||
- Feature/multi surb transmission lanes by @jstuczyn in https://github.com/nymtech/nym/pull/2723
|
||||
|
||||
## [v1.1.4] (2022-12-20)
|
||||
|
||||
This release adds multiple Single Use Reply Blocks (SURBs) to allow arbitrarily-sized anonymized replies.
|
||||
At the moment this is turned off by default, but available for use by application developers.
|
||||
We will need to wait for network-requesters to upgrade to this new release, after which multi-SURB anonymization will become the default setting for the SOCKS proxy clients.
|
||||
|
||||
The release also include some additional work for distributed key generation in the Coconut signing authority nodes.
|
||||
|
||||
### Changed
|
||||
|
||||
- Feature/dkg contract threshold by @neacsu in https://github.com/nymtech/nym/pull/1885
|
||||
- Multi-surbs by @jstuczyn in https://github.com/nymtech/nym/pull/2667
|
||||
- Fix multi-surb backwards compatibility in pre 1.1.4 client config files by @jstuczyn in https://github.com/nymtech/nym/pull/2703
|
||||
- fix: ignore corrupted surb storage and instead create fresh one by @jstuczyn in https://github.com/nymtech/nym/pull/2711
|
||||
- socks5: rework waiting in inbound.rs by @octol in https://github.com/nymtech/nym/pull/1880
|
||||
|
||||
## [v1.1.3] (2022-12-13)
|
||||
|
||||
### Changed
|
||||
|
||||
- validator-api: can recover from shutdown during DKG process ([#1872])
|
||||
- clients: deduplicate gateway inititialization, part of work towards a rust-sdk
|
||||
- clients: deduplicate gateway initialization, part of work towards a rust-sdk
|
||||
- clients: keep all transmission lanes going at all times by making priority probabilistic
|
||||
- clients: ability to use multi-reply SURBs to send arbitrarily long messages fully anonymously whilst requesting additional reply blocks whenever they're about to run out ([#1796], [#1801], [#1804], [#1835], [#1858], [#1883]))
|
||||
|
||||
@@ -31,7 +89,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
- network-requester: fix bug where websocket connection disconnect resulted in success error code
|
||||
- clients: fix a few panics handling the gateway-client
|
||||
- mixnode, gateway, validator-api: Use mainnet values as defaults for URLs and mixnet contract ([#1884])
|
||||
- mixnode, gateway, validator-api: Use mainnet values as defaults for URLs and mixnet contract ([#1884])
|
||||
- socks5: fixed bug where connections sometimes where closed too early
|
||||
- clients: improve message logging when received message fails to get reconstructed ([#1803])
|
||||
|
||||
@@ -51,7 +109,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
- gateway: Renamed flag from `enabled/disabled_credentials_mode` to `only-coconut-credentials`
|
||||
- "Family" feature for node families + layers
|
||||
- Initial coconut functionality including credentials and distributed key generation
|
||||
- Initial coconut functionality including credentials and distributed key generation
|
||||
|
||||
## [v1.1.1](https://github.com/nymtech/nym/tree/v1.1.1) (2022-11-29)
|
||||
|
||||
@@ -87,7 +145,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
[#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)
|
||||
|
||||
### Added
|
||||
@@ -99,7 +156,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- native-client/socks5-client/wasm-client: `use_extended_packet_size` Debug config option to make the client use 'ExtendedPacketSize' for its traffic (32kB as opposed to 2kB in 1.0.2) ([#1671])
|
||||
- network-requester: added additional Blockstream Green wallet endpoint to `example.allowed.list` ([#1611])
|
||||
- validator-api: add `interval_operating_cost` and `profit_margin_percent` to compute reward estimation endpoint
|
||||
- validator-client: added `query_contract_smart` and `query_contract_raw` on `NymdClient` ([#1558])
|
||||
- validator-client: added `query_contract_smart` and `query_contract_raw` on `NyxdClient` ([#1558])
|
||||
- wasm-client: uses updated wasm-compatible `client-core` so that it's now capable of packet retransmission, cover traffic and poisson delay (among other things!) ([#1673])
|
||||
|
||||
### Fixed
|
||||
@@ -114,7 +171,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- gateway-client: will attempt to read now as many as 8 websocket messages at once, assuming they're already available on the socket ([#1669])
|
||||
- moved `Percent` struct to `contracts-common`, change affects explorer-api
|
||||
- socks5 client: graceful shutdown should fix error on disconnect in nym-connect ([#1591])
|
||||
- validator-api: changed error serialization on `inclusion_probability`, `stake-saturation` and `reward-estimation` endpoints to provide more accurate information ([#1681])
|
||||
- validator-api: changed error serialization on `inclusion_probability`, `stake-saturation` and `reward-estimation` endpoints to provide more accurate information ([#1681])
|
||||
- validator-client: made `fee` argument optional for `execute` and `execute_multiple` ([#1541])
|
||||
- wasm-client: fixed build errors on MacOS and changed example JS code to use mainnet ([#1585])
|
||||
- validator-api: changes to internal SQL schema due to the mixnet contract revamp ([#1472])
|
||||
@@ -142,7 +199,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
[#1724]: https://github.com/nymtech/nym/pull/1724
|
||||
[#1725]: https://github.com/nymtech/nym/pull/1725
|
||||
|
||||
|
||||
## [nym-binaries-1.0.2](https://github.com/nymtech/nym/tree/nym-binaries-1.0.2)
|
||||
|
||||
### Added
|
||||
@@ -195,9 +251,8 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- All binaries and cosmwasm blobs are configured at runtime now; binaries are configured using environment variables or .env files and contracts keep the configuration parameters in storage ([#1463])
|
||||
- gateway, network-statistics: include gateway id in the sent statistical data ([#1478])
|
||||
- network explorer: tweak how active set probability is shown ([#1503])
|
||||
- validator-api: rewarder set update fails without panicking on possible nymd queries ([#1520])
|
||||
- network-requester, socks5 client (nym-connect): send and receive respectively a message error to be displayed about filter check failure ([#1576])
|
||||
|
||||
- validator-api: rewarder set update fails without panicking on possible nyxd queries ([#1520])
|
||||
- network-requester, socks5 client (nym-connect): send and receive respectively a message error to be displayed about filter check failure ([#1576])
|
||||
|
||||
[#1249]: https://github.com/nymtech/nym/pull/1249
|
||||
[#1256]: https://github.com/nymtech/nym/pull/1256
|
||||
@@ -291,9 +346,9 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- Bump minimist from 1.2.5 to 1.2.6 in /clients/tauri-client [\#1163](https://github.com/nymtech/nym/pull/1163) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Bump minimist from 1.2.5 to 1.2.6 in /clients/webassembly/js-example [\#1162](https://github.com/nymtech/nym/pull/1162) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Bump minimist from 1.2.5 to 1.2.6 in /clients/native/examples/js-examples/websocket [\#1160](https://github.com/nymtech/nym/pull/1160) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Bump minimist from 1.2.5 to 1.2.6 in /docker/typescript\_client/upload\_contract [\#1159](https://github.com/nymtech/nym/pull/1159) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Bump minimist from 1.2.5 to 1.2.6 in /docker/typescript_client/upload_contract [\#1159](https://github.com/nymtech/nym/pull/1159) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Feature/vesting full [\#1158](https://github.com/nymtech/nym/pull/1158) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- get\_current\_epoch tauri [\#1156](https://github.com/nymtech/nym/pull/1156) ([durch](https://github.com/durch))
|
||||
- get_current_epoch tauri [\#1156](https://github.com/nymtech/nym/pull/1156) ([durch](https://github.com/durch))
|
||||
- Cleanup [\#1155](https://github.com/nymtech/nym/pull/1155) ([durch](https://github.com/durch))
|
||||
- Feature flag reward payments [\#1154](https://github.com/nymtech/nym/pull/1154) ([durch](https://github.com/durch))
|
||||
- Add Query endpoints for calculating rewards [\#1152](https://github.com/nymtech/nym/pull/1152) ([durch](https://github.com/durch))
|
||||
@@ -302,7 +357,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- wallet: use Urls rather than Strings for validator urls [\#1148](https://github.com/nymtech/nym/pull/1148) ([octol](https://github.com/octol))
|
||||
- Change accumulated reward to Option, migrate delegations [\#1147](https://github.com/nymtech/nym/pull/1147) ([durch](https://github.com/durch))
|
||||
- wallet: fetch validators url remotely if available [\#1146](https://github.com/nymtech/nym/pull/1146) ([octol](https://github.com/octol))
|
||||
- Fix delegated\_free calculation [\#1145](https://github.com/nymtech/nym/pull/1145) ([durch](https://github.com/durch))
|
||||
- Fix delegated_free calculation [\#1145](https://github.com/nymtech/nym/pull/1145) ([durch](https://github.com/durch))
|
||||
- Update Nym wallet dependencies to use `ts-packages` [\#1144](https://github.com/nymtech/nym/pull/1144) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- wallet: try validators one by one if available [\#1143](https://github.com/nymtech/nym/pull/1143) ([octol](https://github.com/octol))
|
||||
- Update Network Explorer Packages and add mix node identity key copy [\#1142](https://github.com/nymtech/nym/pull/1142) ([mmsinclair](https://github.com/mmsinclair))
|
||||
@@ -342,14 +397,13 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- feature/pedersen-commitments [\#1048](https://github.com/nymtech/nym/pull/1048) ([danielementary](https://github.com/danielementary))
|
||||
- Feature/reuse init owner [\#970](https://github.com/nymtech/nym/pull/970) ([neacsu](https://github.com/neacsu))
|
||||
|
||||
|
||||
## [v0.12.1](https://github.com/nymtech/nym/tree/v0.12.1) (2021-12-23)
|
||||
|
||||
[Full Changelog](https://github.com/nymtech/nym/compare/v0.12.0...v0.12.1)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Add version check to binaries [\#967](https://github.com/nymtech/nym/issues/967)
|
||||
- Add version check to binaries [\#967](https://github.com/nymtech/nym/issues/967)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
@@ -379,7 +433,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- Bugfix/remove mixnode bonding overwrite [\#917](https://github.com/nymtech/nym/pull/917) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Fixes crash condition in validator API when calculating last day uptime [\#909](https://github.com/nymtech/nym/pull/909) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Bugfix/monitor initial values wait [\#907](https://github.com/nymtech/nym/pull/907) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Bug fix: Network Explorer: Add freegeoip API key and split out tasks for country distributions [\#806](https://github.com/nymtech/nym/pull/806) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- Bug fix: Network Explorer: Add freegeoip API key and split out tasks for country distributions [\#806](https://github.com/nymtech/nym/pull/806) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- Explorer API: port test now split out address resolution and add units tests [\#755](https://github.com/nymtech/nym/pull/755) ([mmsinclair](https://github.com/mmsinclair))
|
||||
|
||||
**Closed issues:**
|
||||
@@ -394,7 +448,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- help!!! [\#712](https://github.com/nymtech/nym/issues/712)
|
||||
- UX feature request: show all delegated nodes in wallet [\#711](https://github.com/nymtech/nym/issues/711)
|
||||
- UX feature request: add current balance on wallet pages [\#710](https://github.com/nymtech/nym/issues/710)
|
||||
- got sign issue from bot [\#709](https://github.com/nymtech/nym/issues/709)
|
||||
- got sign issue from bot [\#709](https://github.com/nymtech/nym/issues/709)
|
||||
- As a wallet user, I would like to be able to log out of the wallet [\#706](https://github.com/nymtech/nym/issues/706)
|
||||
- As a wallet user, I would like to have a "receive" page where I can see my own wallet address [\#705](https://github.com/nymtech/nym/issues/705)
|
||||
- Update native client/socks client/mixnode/gateway `upgrade` command [\#689](https://github.com/nymtech/nym/issues/689)
|
||||
@@ -404,7 +458,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- nym-socks5-client crash after opening Keybase team "Browse all channels" [\#494](https://github.com/nymtech/nym/issues/494)
|
||||
- Mixed Content problem [\#400](https://github.com/nymtech/nym/issues/400)
|
||||
- Gateway disk quota [\#137](https://github.com/nymtech/nym/issues/137)
|
||||
- Simplify message encapsulation with regards to topology [\#127](https://github.com/nymtech/nym/issues/127)
|
||||
- Simplify message encapsulation with regards to topology [\#127](https://github.com/nymtech/nym/issues/127)
|
||||
- Create constants for cli argument names [\#115](https://github.com/nymtech/nym/issues/115)
|
||||
- Using Blake3 as a hash function [\#103](https://github.com/nymtech/nym/issues/103)
|
||||
- Validator should decide which layer a node is in [\#86](https://github.com/nymtech/nym/issues/86)
|
||||
@@ -460,10 +514,10 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- Feature/pre cosmrs updates [\#935](https://github.com/nymtech/nym/pull/935) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Feature/client on behalf [\#934](https://github.com/nymtech/nym/pull/934) ([neacsu](https://github.com/neacsu))
|
||||
- Webpack wallet prod configuration [\#933](https://github.com/nymtech/nym/pull/933) ([tommyv1987](https://github.com/tommyv1987))
|
||||
- Adding tx\_hash to wallet response [\#932](https://github.com/nymtech/nym/pull/932) ([futurechimp](https://github.com/futurechimp))
|
||||
- Adding tx_hash to wallet response [\#932](https://github.com/nymtech/nym/pull/932) ([futurechimp](https://github.com/futurechimp))
|
||||
- Release/1.0.0 pre1 [\#931](https://github.com/nymtech/nym/pull/931) ([durch](https://github.com/durch))
|
||||
- Feature/identity verification [\#930](https://github.com/nymtech/nym/pull/930) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Move cleaned up smart contracts to main code repo [\#929](https://github.com/nymtech/nym/pull/929) ([mfahampshire](https://github.com/mfahampshire))
|
||||
- Move cleaned up smart contracts to main code repo [\#929](https://github.com/nymtech/nym/pull/929) ([mfahampshire](https://github.com/mfahampshire))
|
||||
- Feature/mixnet contract further adjustments [\#928](https://github.com/nymtech/nym/pull/928) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- typo copy change for nodemap [\#926](https://github.com/nymtech/nym/pull/926) ([Aid19801](https://github.com/Aid19801))
|
||||
- Feature/UI enhancements for Desktop Wallet [\#925](https://github.com/nymtech/nym/pull/925) ([fmtabbara](https://github.com/fmtabbara))
|
||||
@@ -476,7 +530,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- Feature/faucet page react [\#911](https://github.com/nymtech/nym/pull/911) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Feature/mixnet contract refactor [\#910](https://github.com/nymtech/nym/pull/910) ([futurechimp](https://github.com/futurechimp))
|
||||
- Update README.md [\#905](https://github.com/nymtech/nym/pull/905) ([tommyv1987](https://github.com/tommyv1987))
|
||||
- BUG: Bond cell denom [\#904](https://github.com/nymtech/nym/pull/904) ([Aid19801](https://github.com/Aid19801))
|
||||
- BUG: Bond cell denom [\#904](https://github.com/nymtech/nym/pull/904) ([Aid19801](https://github.com/Aid19801))
|
||||
- Explorer UI tests missing data-testid [\#903](https://github.com/nymtech/nym/pull/903) ([tommyv1987](https://github.com/tommyv1987))
|
||||
- Fix up Nym-Wallet README.md [\#899](https://github.com/nymtech/nym/pull/899) ([tommyv1987](https://github.com/tommyv1987))
|
||||
- Feature/batch delegator rewarding [\#898](https://github.com/nymtech/nym/pull/898) ([jstuczyn](https://github.com/jstuczyn))
|
||||
@@ -494,7 +548,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- Reverted gateway registration handshake to its 0.11.0 version [\#882](https://github.com/nymtech/nym/pull/882) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Network Explorer [\#881](https://github.com/nymtech/nym/pull/881) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- Feature/rewarding interval updates [\#880](https://github.com/nymtech/nym/pull/880) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Put client\_address and id in the correct order [\#875](https://github.com/nymtech/nym/pull/875) ([neacsu](https://github.com/neacsu))
|
||||
- Put client_address and id in the correct order [\#875](https://github.com/nymtech/nym/pull/875) ([neacsu](https://github.com/neacsu))
|
||||
- remove gateway selection on delegation and undelegation pages [\#873](https://github.com/nymtech/nym/pull/873) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Set MSRV on all binaries to 1.56 [\#872](https://github.com/nymtech/nym/pull/872) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- add native window items \(copy/paste\) via tauri [\#871](https://github.com/nymtech/nym/pull/871) ([fmtabbara](https://github.com/fmtabbara))
|
||||
@@ -510,7 +564,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- Overflow checks in release [\#846](https://github.com/nymtech/nym/pull/846) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- fix delegate success overflow [\#842](https://github.com/nymtech/nym/pull/842) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Feature NYM wallet webdriverio test [\#841](https://github.com/nymtech/nym/pull/841) ([tommyv1987](https://github.com/tommyv1987))
|
||||
- Update nym\_wallet.yml [\#840](https://github.com/nymtech/nym/pull/840) ([tommyv1987](https://github.com/tommyv1987))
|
||||
- Update nym_wallet.yml [\#840](https://github.com/nymtech/nym/pull/840) ([tommyv1987](https://github.com/tommyv1987))
|
||||
- Feature/vouchers [\#837](https://github.com/nymtech/nym/pull/837) ([aniampio](https://github.com/aniampio))
|
||||
- Apply readable ids to elements on Nym Wallet [\#836](https://github.com/nymtech/nym/pull/836) ([tommyv1987](https://github.com/tommyv1987))
|
||||
- Feature/removal of monitor good nodes [\#833](https://github.com/nymtech/nym/pull/833) ([jstuczyn](https://github.com/jstuczyn))
|
||||
@@ -534,8 +588,8 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- Created getters for AccountData [\#787](https://github.com/nymtech/nym/pull/787) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Feature/migrate hidden delegations [\#786](https://github.com/nymtech/nym/pull/786) ([neacsu](https://github.com/neacsu))
|
||||
- Feature/persistent gateway storage [\#784](https://github.com/nymtech/nym/pull/784) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Replaced unwrap\_or\_else with unwrap\_or\_default [\#780](https://github.com/nymtech/nym/pull/780) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Add block\_height method to Delegation [\#778](https://github.com/nymtech/nym/pull/778) ([durch](https://github.com/durch))
|
||||
- Replaced unwrap_or_else with unwrap_or_default [\#780](https://github.com/nymtech/nym/pull/780) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Add block_height method to Delegation [\#778](https://github.com/nymtech/nym/pull/778) ([durch](https://github.com/durch))
|
||||
- Make fee helpers public [\#777](https://github.com/nymtech/nym/pull/777) ([durch](https://github.com/durch))
|
||||
- re-enable bonding [\#776](https://github.com/nymtech/nym/pull/776) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Explorer-api: add API resource to show the delegations for each mix node [\#774](https://github.com/nymtech/nym/pull/774) ([mmsinclair](https://github.com/mmsinclair))
|
||||
@@ -544,14 +598,14 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- Adding deps for building the Tauri wallet under Ubuntu [\#770](https://github.com/nymtech/nym/pull/770) ([futurechimp](https://github.com/futurechimp))
|
||||
- remove alert [\#767](https://github.com/nymtech/nym/pull/767) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Feature/consumable bandwidth [\#766](https://github.com/nymtech/nym/pull/766) ([neacsu](https://github.com/neacsu))
|
||||
- Update coconut-rs and use hash\_to\_scalar from there [\#765](https://github.com/nymtech/nym/pull/765) ([neacsu](https://github.com/neacsu))
|
||||
- Update coconut-rs and use hash_to_scalar from there [\#765](https://github.com/nymtech/nym/pull/765) ([neacsu](https://github.com/neacsu))
|
||||
- Feature/active sets [\#764](https://github.com/nymtech/nym/pull/764) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- add app alert banner [\#762](https://github.com/nymtech/nym/pull/762) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Updated cosmos-sdk [\#761](https://github.com/nymtech/nym/pull/761) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Feature/bond blockstamp [\#760](https://github.com/nymtech/nym/pull/760) ([neacsu](https://github.com/neacsu))
|
||||
- Feature/revert migration code [\#759](https://github.com/nymtech/nym/pull/759) ([neacsu](https://github.com/neacsu))
|
||||
- Bump next from 11.1.0 to 11.1.1 in /wallet-web [\#758](https://github.com/nymtech/nym/pull/758) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Add block\_height in the Delegation structure as well [\#757](https://github.com/nymtech/nym/pull/757) ([neacsu](https://github.com/neacsu))
|
||||
- Add block_height in the Delegation structure as well [\#757](https://github.com/nymtech/nym/pull/757) ([neacsu](https://github.com/neacsu))
|
||||
- Feature/add blockstamp [\#756](https://github.com/nymtech/nym/pull/756) ([neacsu](https://github.com/neacsu))
|
||||
- NetworkMonitorBuilder - starting the monitor after rocket has launched [\#754](https://github.com/nymtech/nym/pull/754) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Enabled validators api argument [\#753](https://github.com/nymtech/nym/pull/753) ([jstuczyn](https://github.com/jstuczyn))
|
||||
@@ -563,21 +617,21 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- Feature/more reliable uptime calculation [\#747](https://github.com/nymtech/nym/pull/747) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Update template toml key [\#746](https://github.com/nymtech/nym/pull/746) ([neacsu](https://github.com/neacsu))
|
||||
- Feature/cred after handshake [\#745](https://github.com/nymtech/nym/pull/745) ([neacsu](https://github.com/neacsu))
|
||||
- Reinstate the POST method blind\_sign [\#744](https://github.com/nymtech/nym/pull/744) ([neacsu](https://github.com/neacsu))
|
||||
- Reinstate the POST method blind_sign [\#744](https://github.com/nymtech/nym/pull/744) ([neacsu](https://github.com/neacsu))
|
||||
- explorer-api: add pending field to port check response [\#742](https://github.com/nymtech/nym/pull/742) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- Feature/use delegation rates [\#741](https://github.com/nymtech/nym/pull/741) ([neacsu](https://github.com/neacsu))
|
||||
- Feature/copy to clipboard [\#740](https://github.com/nymtech/nym/pull/740) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Feature/update wallet with stake rates [\#739](https://github.com/nymtech/nym/pull/739) ([neacsu](https://github.com/neacsu))
|
||||
- Add stake reward rates and bump version of client [\#738](https://github.com/nymtech/nym/pull/738) ([neacsu](https://github.com/neacsu))
|
||||
- Bump next from 10.1.3 to 11.1.0 in /wallet-web [\#737](https://github.com/nymtech/nym/pull/737) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- Feature/nymd client integration [\#736](https://github.com/nymtech/nym/pull/736) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Feature/nyxd client integration [\#736](https://github.com/nymtech/nym/pull/736) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Bug/fix parking lot on wasm [\#735](https://github.com/nymtech/nym/pull/735) ([neacsu](https://github.com/neacsu))
|
||||
- Explorer API: add new HTTP resource to decorate mix nodes with geoip locations [\#734](https://github.com/nymtech/nym/pull/734) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- Feature/completing nymd client api [\#732](https://github.com/nymtech/nym/pull/732) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Feature/completing nyxd client api [\#732](https://github.com/nymtech/nym/pull/732) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Explorer API - add port check and node description/stats proxy [\#731](https://github.com/nymtech/nym/pull/731) ([mmsinclair](https://github.com/mmsinclair))
|
||||
- Feature/nymd client fee handling [\#730](https://github.com/nymtech/nym/pull/730) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Feature/nyxd client fee handling [\#730](https://github.com/nymtech/nym/pull/730) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Update DelegationCheck.tsx [\#725](https://github.com/nymtech/nym/pull/725) ([jessgess](https://github.com/jessgess))
|
||||
- Rust nymd/cosmwasm client [\#724](https://github.com/nymtech/nym/pull/724) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Rust nyxd/cosmwasm client [\#724](https://github.com/nymtech/nym/pull/724) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Removed wasm feature bypassing cyclic dependencies [\#723](https://github.com/nymtech/nym/pull/723) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Updated used sphinx dependency to the most recent revision [\#722](https://github.com/nymtech/nym/pull/722) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- update state management and validation [\#721](https://github.com/nymtech/nym/pull/721) ([fmtabbara](https://github.com/fmtabbara))
|
||||
@@ -596,10 +650,8 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- Bond and delegation alerts [\#698](https://github.com/nymtech/nym/pull/698) ([fmtabbara](https://github.com/fmtabbara))
|
||||
- Bugfix/network monitor version check [\#697](https://github.com/nymtech/nym/pull/697) ([jstuczyn](https://github.com/jstuczyn))
|
||||
- Feature/other containers [\#692](https://github.com/nymtech/nym/pull/692) ([neacsu](https://github.com/neacsu))
|
||||
- Using validator API instead of nymd [\#690](https://github.com/nymtech/nym/pull/690) ([futurechimp](https://github.com/futurechimp))
|
||||
- Using validator API instead of nyxd [\#690](https://github.com/nymtech/nym/pull/690) ([futurechimp](https://github.com/futurechimp))
|
||||
- Hang coconut issuance off the validator-api [\#679](https://github.com/nymtech/nym/pull/679) ([durch](https://github.com/durch))
|
||||
- Update hmac and blake3 [\#673](https://github.com/nymtech/nym/pull/673) ([durch](https://github.com/durch))
|
||||
|
||||
|
||||
|
||||
\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
|
||||
\* _This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)_
|
||||
|
||||
Generated
+1291
-864
File diff suppressed because it is too large
Load Diff
+13
@@ -30,9 +30,11 @@ members = [
|
||||
"common/coconut-interface",
|
||||
"common/commands",
|
||||
"common/config",
|
||||
"common/build-information",
|
||||
"common/cosmwasm-smart-contracts/coconut-bandwidth-contract",
|
||||
"common/cosmwasm-smart-contracts/coconut-dkg",
|
||||
"common/cosmwasm-smart-contracts/contracts-common",
|
||||
"common/cosmwasm-smart-contracts/group-contract",
|
||||
"common/cosmwasm-smart-contracts/mixnet-contract",
|
||||
"common/cosmwasm-smart-contracts/multisig-contract",
|
||||
"common/cosmwasm-smart-contracts/vesting-contract",
|
||||
@@ -72,10 +74,12 @@ members = [
|
||||
"gateway/gateway-requests",
|
||||
"integrations/bity",
|
||||
"mixnode",
|
||||
"sdk/rust/nym-sdk",
|
||||
"service-providers/network-requester",
|
||||
"service-providers/network-statistics",
|
||||
"nym-api",
|
||||
"nym-api/nym-api-requests",
|
||||
"nym-outfox",
|
||||
"tools/nym-cli",
|
||||
"tools/ts-rs-cli"
|
||||
]
|
||||
@@ -92,3 +96,12 @@ default-members = [
|
||||
]
|
||||
|
||||
exclude = ["explorer", "contracts", "clients/webassembly", "nym-wallet", "nym-connect"]
|
||||
|
||||
[workspace.package]
|
||||
authors = ["Nym Technologies SA"]
|
||||
repository = "https://github.com/nymtech/nym"
|
||||
homepage = "https://nymtech.net"
|
||||
edition = "2021"
|
||||
|
||||
[workspace.dependencies]
|
||||
log = "0.4"
|
||||
|
||||
@@ -4,7 +4,7 @@ no-clippy: build cargo-test wasm fmt
|
||||
happy: fmt clippy-happy test
|
||||
clippy-all: clippy-main clippy-coconut clippy-all-contracts clippy-all-wallet clippy-all-connect clippy-all-wasm-client
|
||||
clippy-happy: clippy-happy-main clippy-happy-contracts clippy-happy-wallet clippy-happy-connect
|
||||
cargo-test: test-main test-contracts test-wallet test-connect test-coconut test-wasm-client
|
||||
cargo-test: test-main test-contracts test-wallet test-connect test-coconut
|
||||
cargo-test-expensive: test-main-expensive test-contracts-expensive test-wallet-expensive test-connect-expensive test-coconut-expensive
|
||||
build: build-contracts build-wallet build-main build-connect build-wasm-client
|
||||
fmt: fmt-main fmt-contracts fmt-wallet fmt-connect fmt-wasm-client
|
||||
@@ -68,9 +68,6 @@ test-wallet:
|
||||
test-wallet-expensive:
|
||||
cargo test --manifest-path nym-wallet/Cargo.toml --all-features -- --ignored
|
||||
|
||||
test-wasm-client:
|
||||
cargo test --workspace --manifest-path clients/webassembly/Cargo.toml --all-features
|
||||
|
||||
test-connect:
|
||||
cargo test --manifest-path nym-connect/Cargo.toml --all-features
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
[package]
|
||||
name = "client-core"
|
||||
version = "1.1.3"
|
||||
version = "1.1.6"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.66"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -12,14 +13,14 @@ dirs = "4.0"
|
||||
dashmap = "5.4.0"
|
||||
futures = "0.3"
|
||||
humantime-serde = "1.0"
|
||||
log = "0.4"
|
||||
log = { workspace = true }
|
||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0.89"
|
||||
tap = "1.0.1"
|
||||
thiserror = "1.0.34"
|
||||
url = { version ="2.2", features = ["serde"] }
|
||||
tokio = { version = "1.21.2", features = ["macros"]}
|
||||
tokio = { version = "1.24.1", features = ["macros"]}
|
||||
time = "0.3.17"
|
||||
|
||||
# internal
|
||||
@@ -37,11 +38,11 @@ validator-client = { path = "../../common/client-libs/validator-client", default
|
||||
task = { path = "../../common/task" }
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream]
|
||||
version = "0.1.9"
|
||||
version = "0.1.11"
|
||||
features = ["time"]
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio]
|
||||
version = "1.21.2"
|
||||
version = "1.24.1"
|
||||
features = ["time"]
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
|
||||
@@ -74,7 +75,7 @@ features = ["wasm-bindgen"]
|
||||
tempfile = "3.1.0"
|
||||
|
||||
[build-dependencies]
|
||||
tokio = { version = "1.21.2", features = ["rt-multi-thread", "macros"] }
|
||||
tokio = { version = "1.24.1", features = ["rt-multi-thread", "macros"] }
|
||||
sqlx = { version = "0.6.2", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
|
||||
|
||||
[features]
|
||||
|
||||
@@ -9,9 +9,9 @@ async fn main() {
|
||||
use std::env;
|
||||
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
let database_path = format!("{}/fs-surbs-example.sqlite", out_dir);
|
||||
let database_path = format!("{out_dir}/fs-surbs-example.sqlite");
|
||||
|
||||
let mut conn = SqliteConnection::connect(&format!("sqlite://{}?mode=rwc", database_path))
|
||||
let mut conn = SqliteConnection::connect(&format!("sqlite://{database_path}?mode=rwc"))
|
||||
.await
|
||||
.expect("Failed to create SQLx database connection");
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@ use client_connections::{ConnectionCommandReceiver, ConnectionCommandSender, Lan
|
||||
use crypto::asymmetric::{encryption, identity};
|
||||
use futures::channel::mpsc;
|
||||
use gateway_client::bandwidth::BandwidthController;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use gateway_client::wasm_mockups::CosmWasmClient;
|
||||
use gateway_client::{
|
||||
AcknowledgementReceiver, AcknowledgementSender, GatewayClient, MixnetMessageReceiver,
|
||||
MixnetMessageSender,
|
||||
@@ -33,11 +35,16 @@ use log::{debug, info};
|
||||
use nymsphinx::acknowledgements::AckKey;
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
use nymsphinx::addressing::nodes::NodeIdentity;
|
||||
use nymsphinx::receiver::ReconstructedMessage;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tap::TapFallible;
|
||||
use task::{TaskClient, TaskManager};
|
||||
use url::Url;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use validator_client::nyxd::CosmWasmClient;
|
||||
|
||||
use super::received_buffer::ReceivedBufferMessage;
|
||||
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
||||
pub mod non_wasm_helpers;
|
||||
@@ -48,10 +55,30 @@ pub struct ClientInput {
|
||||
}
|
||||
|
||||
pub struct ClientOutput {
|
||||
pub shared_lane_queue_lengths: LaneQueueLengths,
|
||||
pub received_buffer_request_sender: ReceivedBufferRequestSender,
|
||||
}
|
||||
|
||||
impl ClientOutput {
|
||||
pub fn register_receiver(
|
||||
&mut self,
|
||||
) -> Result<mpsc::UnboundedReceiver<Vec<ReconstructedMessage>>, ClientCoreError> {
|
||||
let (reconstructed_sender, reconstructed_receiver) = mpsc::unbounded();
|
||||
|
||||
self.received_buffer_request_sender
|
||||
.unbounded_send(ReceivedBufferMessage::ReceiverAnnounce(
|
||||
reconstructed_sender,
|
||||
))
|
||||
.map_err(|_| ClientCoreError::FailedToRegisterReceiver)?;
|
||||
|
||||
Ok(reconstructed_receiver)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ClientState {
|
||||
pub shared_lane_queue_lengths: LaneQueueLengths,
|
||||
pub reply_controller_sender: ReplyControllerSender,
|
||||
}
|
||||
|
||||
pub enum ClientInputStatus {
|
||||
AwaitingProducer { client_input: ClientInput },
|
||||
Connected,
|
||||
@@ -80,7 +107,33 @@ impl ClientOutputStatus {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BaseClientBuilder<'a, B> {
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum CredentialsToggle {
|
||||
Enabled,
|
||||
Disabled,
|
||||
}
|
||||
|
||||
impl CredentialsToggle {
|
||||
pub fn is_enabled(&self) -> bool {
|
||||
self == &CredentialsToggle::Enabled
|
||||
}
|
||||
|
||||
pub fn is_disabled(&self) -> bool {
|
||||
self == &CredentialsToggle::Disabled
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for CredentialsToggle {
|
||||
fn from(value: bool) -> Self {
|
||||
if value {
|
||||
CredentialsToggle::Enabled
|
||||
} else {
|
||||
CredentialsToggle::Disabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BaseClientBuilder<'a, B, C: Clone> {
|
||||
// due to wasm limitations I had to split it like this : (
|
||||
gateway_config: &'a GatewayEndpointConfig,
|
||||
debug_config: &'a DebugConfig,
|
||||
@@ -88,20 +141,21 @@ pub struct BaseClientBuilder<'a, B> {
|
||||
nym_api_endpoints: Vec<Url>,
|
||||
reply_storage_backend: B,
|
||||
|
||||
bandwidth_controller: Option<BandwidthController>,
|
||||
bandwidth_controller: Option<BandwidthController<C>>,
|
||||
key_manager: KeyManager,
|
||||
}
|
||||
|
||||
impl<'a, B> BaseClientBuilder<'a, B>
|
||||
impl<'a, B, C> BaseClientBuilder<'a, B, C>
|
||||
where
|
||||
B: ReplyStorageBackend + Send + Sync + 'static,
|
||||
C: CosmWasmClient + Sync + Send + Clone + 'static,
|
||||
{
|
||||
pub fn new_from_base_config<T>(
|
||||
base_config: &'a Config<T>,
|
||||
key_manager: KeyManager,
|
||||
bandwidth_controller: Option<BandwidthController>,
|
||||
bandwidth_controller: Option<BandwidthController<C>>,
|
||||
reply_storage_backend: B,
|
||||
) -> BaseClientBuilder<'a, B> {
|
||||
) -> BaseClientBuilder<'a, B, C> {
|
||||
BaseClientBuilder {
|
||||
gateway_config: base_config.get_gateway_endpoint_config(),
|
||||
debug_config: base_config.get_debug_config(),
|
||||
@@ -117,15 +171,15 @@ where
|
||||
gateway_config: &'a GatewayEndpointConfig,
|
||||
debug_config: &'a DebugConfig,
|
||||
key_manager: KeyManager,
|
||||
bandwidth_controller: Option<BandwidthController>,
|
||||
bandwidth_controller: Option<BandwidthController<C>>,
|
||||
reply_storage_backend: B,
|
||||
disabled_credentials: bool,
|
||||
credentials_toggle: CredentialsToggle,
|
||||
nym_api_endpoints: Vec<Url>,
|
||||
) -> BaseClientBuilder<'a, B> {
|
||||
) -> BaseClientBuilder<'a, B, C> {
|
||||
BaseClientBuilder {
|
||||
gateway_config,
|
||||
debug_config,
|
||||
disabled_credentials,
|
||||
disabled_credentials: credentials_toggle.is_disabled(),
|
||||
nym_api_endpoints,
|
||||
reply_storage_backend,
|
||||
bandwidth_controller,
|
||||
@@ -230,7 +284,7 @@ where
|
||||
mixnet_message_sender: MixnetMessageSender,
|
||||
ack_sender: AcknowledgementSender,
|
||||
shutdown: TaskClient,
|
||||
) -> Result<GatewayClient, ClientCoreError<B>> {
|
||||
) -> Result<GatewayClient<C>, ClientCoreError> {
|
||||
let gateway_id = self.gateway_config.gateway_id.clone();
|
||||
if gateway_id.is_empty() {
|
||||
return Err(ClientCoreError::GatewayIdUnknown);
|
||||
@@ -248,7 +302,7 @@ where
|
||||
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
|
||||
|
||||
// disgusting wasm workaround since there's no key persistence there (nor `client init`)
|
||||
let shared_key = if self.key_manager.gateway_key_set() {
|
||||
let shared_key = if self.key_manager.is_gateway_key_set() {
|
||||
Some(self.key_manager.gateway_shared_key())
|
||||
} else {
|
||||
None
|
||||
@@ -285,7 +339,7 @@ where
|
||||
refresh_rate: Duration,
|
||||
topology_accessor: TopologyAccessor,
|
||||
shutdown: TaskClient,
|
||||
) -> Result<(), ClientCoreError<B>> {
|
||||
) -> Result<(), ClientCoreError> {
|
||||
let topology_refresher_config = TopologyRefresherConfig::new(
|
||||
nym_api_urls,
|
||||
refresh_rate,
|
||||
@@ -316,7 +370,7 @@ where
|
||||
// over it. Perhaps GatewayClient needs to be thread-shareable or have some channel for
|
||||
// requests?
|
||||
fn start_mix_traffic_controller(
|
||||
gateway_client: GatewayClient,
|
||||
gateway_client: GatewayClient<C>,
|
||||
shutdown: TaskClient,
|
||||
) -> BatchMixMessageSender {
|
||||
info!("Starting mix traffic controller...");
|
||||
@@ -328,24 +382,42 @@ where
|
||||
async fn setup_persistent_reply_storage(
|
||||
backend: B,
|
||||
shutdown: TaskClient,
|
||||
) -> Result<CombinedReplyStorage, ClientCoreError<B>> {
|
||||
let persistent_storage = PersistentReplyStorage::new(backend);
|
||||
let mem_store = persistent_storage
|
||||
.load_state_from_backend()
|
||||
.await
|
||||
.map_err(|err| ClientCoreError::SurbStorageError { source: err })?;
|
||||
|
||||
let store_clone = mem_store.clone();
|
||||
spawn_future(async move {
|
||||
persistent_storage
|
||||
.flush_on_shutdown(store_clone, shutdown)
|
||||
) -> Result<CombinedReplyStorage, ClientCoreError>
|
||||
where
|
||||
<B as ReplyStorageBackend>::StorageError: Sync + Send,
|
||||
{
|
||||
if backend.is_active() {
|
||||
log::trace!("Setup persistent reply storage");
|
||||
let persistent_storage = PersistentReplyStorage::new(backend);
|
||||
let mem_store = persistent_storage
|
||||
.load_state_from_backend()
|
||||
.await
|
||||
});
|
||||
.map_err(|err| ClientCoreError::SurbStorageError {
|
||||
source: Box::new(err),
|
||||
})?;
|
||||
|
||||
Ok(mem_store)
|
||||
let store_clone = mem_store.clone();
|
||||
spawn_future(async move {
|
||||
persistent_storage
|
||||
.flush_on_shutdown(store_clone, shutdown)
|
||||
.await
|
||||
});
|
||||
|
||||
Ok(mem_store)
|
||||
} else {
|
||||
log::trace!("Setup inactive reply storage");
|
||||
Ok(backend
|
||||
.get_inactive_storage()
|
||||
.map_err(|err| ClientCoreError::SurbStorageError {
|
||||
source: Box::new(err),
|
||||
})?)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn start_base(mut self) -> Result<BaseClient, ClientCoreError<B>> {
|
||||
pub async fn start_base(mut self) -> Result<BaseClient, ClientCoreError>
|
||||
where
|
||||
<B as ReplyStorageBackend>::StorageError: Sync + Send,
|
||||
{
|
||||
info!("Starting nym client");
|
||||
// channels for inter-component communication
|
||||
// TODO: make the channels be internally created by the relevant components
|
||||
@@ -371,7 +443,7 @@ where
|
||||
|
||||
// channels responsible for dealing with reply-related fun
|
||||
let (reply_controller_sender, reply_controller_receiver) =
|
||||
reply_controller::new_control_channels();
|
||||
reply_controller::requests::new_control_channels();
|
||||
|
||||
let self_address = self.as_mix_recipient();
|
||||
|
||||
@@ -437,7 +509,7 @@ where
|
||||
input_receiver,
|
||||
sphinx_message_sender.clone(),
|
||||
reply_storage,
|
||||
reply_controller_sender,
|
||||
reply_controller_sender.clone(),
|
||||
reply_controller_receiver,
|
||||
shared_lane_queue_lengths.clone(),
|
||||
client_connection_rx,
|
||||
@@ -467,10 +539,13 @@ where
|
||||
},
|
||||
client_output: ClientOutputStatus::AwaitingConsumer {
|
||||
client_output: ClientOutput {
|
||||
shared_lane_queue_lengths,
|
||||
received_buffer_request_sender,
|
||||
},
|
||||
},
|
||||
client_state: ClientState {
|
||||
shared_lane_queue_lengths,
|
||||
reply_controller_sender,
|
||||
},
|
||||
task_manager,
|
||||
})
|
||||
}
|
||||
@@ -479,6 +554,7 @@ where
|
||||
pub struct BaseClient {
|
||||
pub client_input: ClientInputStatus,
|
||||
pub client_output: ClientOutputStatus,
|
||||
pub client_state: ClientState,
|
||||
|
||||
pub task_manager: TaskManager,
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::replies::reply_storage::{
|
||||
fs_backend, CombinedReplyStorage, ReplyStorageBackend,
|
||||
self, fs_backend, CombinedReplyStorage, ReplyStorageBackend,
|
||||
};
|
||||
use crate::config::DebugConfig;
|
||||
use crate::error::ClientCoreError;
|
||||
@@ -14,13 +14,15 @@ use time::OffsetDateTime;
|
||||
async fn setup_fresh_backend<P: AsRef<Path>>(
|
||||
db_path: P,
|
||||
debug_config: &DebugConfig,
|
||||
) -> Result<fs_backend::Backend, ClientCoreError<fs_backend::Backend>> {
|
||||
) -> Result<fs_backend::Backend, ClientCoreError> {
|
||||
info!("creating fresh surb database");
|
||||
let mut storage_backend = match fs_backend::Backend::init(db_path).await {
|
||||
Ok(backend) => backend,
|
||||
Err(err) => {
|
||||
error!("failed to setup persistent storage backend for our reply needs: {err}");
|
||||
return Err(ClientCoreError::SurbStorageError { source: err });
|
||||
return Err(ClientCoreError::SurbStorageError {
|
||||
source: Box::new(err),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -34,11 +36,21 @@ async fn setup_fresh_backend<P: AsRef<Path>>(
|
||||
storage_backend
|
||||
.init_fresh(&mem_store)
|
||||
.await
|
||||
.map_err(|err| ClientCoreError::SurbStorageError { source: err })?;
|
||||
.map_err(|err| ClientCoreError::SurbStorageError {
|
||||
source: Box::new(err),
|
||||
})?;
|
||||
|
||||
Ok(storage_backend)
|
||||
}
|
||||
|
||||
fn setup_inactive_backend(debug_config: &DebugConfig) -> fs_backend::Backend {
|
||||
info!("creating inactive surb database");
|
||||
fs_backend::Backend::new_inactive(
|
||||
debug_config.minimum_reply_surb_storage_threshold,
|
||||
debug_config.maximum_reply_surb_storage_threshold,
|
||||
)
|
||||
}
|
||||
|
||||
fn archive_corrupted_database<P: AsRef<Path>>(db_path: P) -> io::Result<()> {
|
||||
let db_path = db_path.as_ref();
|
||||
debug_assert!(db_path.exists());
|
||||
@@ -49,7 +61,7 @@ fn archive_corrupted_database<P: AsRef<Path>>(db_path: P) -> io::Result<()> {
|
||||
|
||||
let new_extension =
|
||||
if let Some(existing_extension) = db_path.extension().and_then(|ext| ext.to_str()) {
|
||||
format!("{existing_extension}.{}", suffix)
|
||||
format!("{existing_extension}.{suffix}")
|
||||
} else {
|
||||
suffix
|
||||
};
|
||||
@@ -61,23 +73,35 @@ fn archive_corrupted_database<P: AsRef<Path>>(db_path: P) -> io::Result<()> {
|
||||
}
|
||||
|
||||
pub async fn setup_fs_reply_surb_backend<P: AsRef<Path>>(
|
||||
db_path: P,
|
||||
db_path: Option<P>,
|
||||
debug_config: &DebugConfig,
|
||||
) -> Result<fs_backend::Backend, ClientCoreError<fs_backend::Backend>> {
|
||||
// if the database file doesnt exist, initialise fresh storage, otherwise attempt to load the existing one
|
||||
let db_path = db_path.as_ref();
|
||||
if db_path.exists() {
|
||||
info!("loading existing surb database");
|
||||
match fs_backend::Backend::try_load(db_path).await {
|
||||
Ok(backend) => Ok(backend),
|
||||
Err(err) => {
|
||||
error!("failed to setup persistent storage backend for our reply needs: {err}. We're going to create a fresh database instead. This behaviour might change in the future");
|
||||
) -> Result<fs_backend::Backend, ClientCoreError> {
|
||||
if let Some(db_path) = db_path {
|
||||
// if the database file doesnt exist, initialise fresh storage, otherwise attempt to load
|
||||
// the existing one
|
||||
let db_path = db_path.as_ref();
|
||||
if db_path.exists() {
|
||||
info!("loading existing surb database");
|
||||
match fs_backend::Backend::try_load(db_path).await {
|
||||
Ok(backend) => Ok(backend),
|
||||
Err(err) => {
|
||||
error!("failed to setup persistent storage backend for our reply needs: {err}. We're going to create a fresh database instead. This behaviour might change in the future");
|
||||
|
||||
archive_corrupted_database(db_path)?;
|
||||
setup_fresh_backend(db_path, debug_config).await
|
||||
archive_corrupted_database(db_path)?;
|
||||
setup_fresh_backend(db_path, debug_config).await
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setup_fresh_backend(db_path, debug_config).await
|
||||
}
|
||||
} else {
|
||||
setup_fresh_backend(db_path, debug_config).await
|
||||
Ok(setup_inactive_backend(debug_config))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_empty_reply_surb_backend(debug_config: &DebugConfig) -> reply_storage::Empty {
|
||||
reply_storage::Empty {
|
||||
min_surb_threshold: debug_config.minimum_reply_surb_storage_threshold,
|
||||
max_surb_threshold: debug_config.maximum_reply_surb_storage_threshold,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
mod non_wasm;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
mod wasm;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub use non_wasm::*;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub use wasm::*;
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub use tokio::time::*;
|
||||
pub type IntervalStream = tokio_stream::wrappers::IntervalStream;
|
||||
|
||||
pub(crate) fn get_time_now() -> Instant {
|
||||
Instant::now()
|
||||
}
|
||||
|
||||
pub(crate) fn new_interval_stream(polling_rate: Duration) -> IntervalStream {
|
||||
tokio_stream::wrappers::IntervalStream::new(tokio::time::interval(polling_rate))
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::time::Duration;
|
||||
use wasm_timer;
|
||||
|
||||
pub use wasm_timer::*;
|
||||
pub type IntervalStream = gloo_timers::future::IntervalStream;
|
||||
|
||||
pub(crate) fn get_time_now() -> Instant {
|
||||
wasm_timer::Instant::now()
|
||||
}
|
||||
|
||||
pub(crate) fn new_interval_stream(polling_rate: Duration) -> IntervalStream {
|
||||
gloo_timers::future::IntervalStream::new(polling_rate.as_millis() as u32)
|
||||
}
|
||||
@@ -17,6 +17,7 @@ use std::sync::Arc;
|
||||
// use the old key after new one was issued.
|
||||
|
||||
// Remember that Arc<T> has Deref implementation for T
|
||||
#[derive(Clone)]
|
||||
pub struct KeyManager {
|
||||
/// identity key associated with the client instance.
|
||||
identity_keypair: Arc<identity::KeyPair>,
|
||||
@@ -41,9 +42,6 @@ pub struct KeyManager {
|
||||
*/
|
||||
|
||||
impl KeyManager {
|
||||
// this is actually **NOT** dead code
|
||||
// I have absolutely no idea why the compiler insists it's unused. The call happens during client::init::execute
|
||||
#[allow(dead_code)]
|
||||
/// Creates new instance of a [`KeyManager`]
|
||||
pub fn new<R>(rng: &mut R) -> Self
|
||||
where
|
||||
@@ -57,16 +55,22 @@ impl KeyManager {
|
||||
}
|
||||
}
|
||||
|
||||
// this is actually **NOT** dead code
|
||||
// I have absolutely no idea why the compiler insists it's unused. The call happens during client::init::execute
|
||||
#[allow(dead_code)]
|
||||
/// After shared key with the gateway is derived, puts its ownership to this instance of a [`KeyManager`].
|
||||
pub fn insert_gateway_shared_key(&mut self, gateway_shared_key: Arc<SharedKeys>) {
|
||||
self.gateway_shared_key = Some(gateway_shared_key)
|
||||
pub fn from_keys(
|
||||
id_keypair: identity::KeyPair,
|
||||
enc_keypair: encryption::KeyPair,
|
||||
gateway_shared_key: SharedKeys,
|
||||
ack_key: AckKey,
|
||||
) -> Self {
|
||||
Self {
|
||||
identity_keypair: Arc::new(id_keypair),
|
||||
encryption_keypair: Arc::new(enc_keypair),
|
||||
gateway_shared_key: Some(Arc::new(gateway_shared_key)),
|
||||
ack_key: Arc::new(ack_key),
|
||||
}
|
||||
}
|
||||
|
||||
/// Loads previously stored keys from the disk.
|
||||
pub fn load_keys(client_pathfinder: &ClientKeyPathfinder) -> io::Result<Self> {
|
||||
/// Loads previously stored client keys from the disk.
|
||||
fn load_client_keys(client_pathfinder: &ClientKeyPathfinder) -> io::Result<Self> {
|
||||
let identity_keypair: identity::KeyPair =
|
||||
pemstore::load_keypair(&pemstore::KeyPairPath::new(
|
||||
client_pathfinder.private_identity_key().to_owned(),
|
||||
@@ -78,24 +82,51 @@ impl KeyManager {
|
||||
client_pathfinder.public_encryption_key().to_owned(),
|
||||
))?;
|
||||
|
||||
let gateway_shared_key: SharedKeys =
|
||||
pemstore::load_key(client_pathfinder.gateway_shared_key())?;
|
||||
|
||||
let ack_key: AckKey = pemstore::load_key(client_pathfinder.ack_key())?;
|
||||
|
||||
// TODO: ack key is never stored so it is generated now. But perhaps it should be stored
|
||||
// after all for consistency sake?
|
||||
Ok(KeyManager {
|
||||
identity_keypair: Arc::new(identity_keypair),
|
||||
encryption_keypair: Arc::new(encryption_keypair),
|
||||
gateway_shared_key: Some(Arc::new(gateway_shared_key)),
|
||||
gateway_shared_key: None,
|
||||
ack_key: Arc::new(ack_key),
|
||||
})
|
||||
}
|
||||
|
||||
// this is actually **NOT** dead code
|
||||
// I have absolutely no idea why the compiler insists it's unused. The call happens during client::init::execute
|
||||
#[allow(dead_code)]
|
||||
/// Loads previously stored keys from the disk. Fails if not all, including the shared gateway
|
||||
/// key, is available.
|
||||
pub fn load_keys(client_pathfinder: &ClientKeyPathfinder) -> io::Result<Self> {
|
||||
let mut key_manager = Self::load_client_keys(client_pathfinder)?;
|
||||
|
||||
let gateway_shared_key: SharedKeys =
|
||||
pemstore::load_key(client_pathfinder.gateway_shared_key())?;
|
||||
|
||||
key_manager.gateway_shared_key = Some(Arc::new(gateway_shared_key));
|
||||
|
||||
Ok(key_manager)
|
||||
}
|
||||
|
||||
/// Loads previously stored keys from the disk. Fails if client keys are not availabe, but the
|
||||
/// shared gateway key is optional.
|
||||
pub fn load_keys_but_gateway_is_optional(
|
||||
client_pathfinder: &ClientKeyPathfinder,
|
||||
) -> io::Result<Self> {
|
||||
let mut key_manager = Self::load_client_keys(client_pathfinder)?;
|
||||
|
||||
let gateway_shared_key: Result<SharedKeys, io::Error> =
|
||||
pemstore::load_key(client_pathfinder.gateway_shared_key());
|
||||
|
||||
// It's ok if the gateway key was not found
|
||||
let gateway_shared_key = match gateway_shared_key {
|
||||
Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
|
||||
Err(err) => Err(err),
|
||||
Ok(key) => Ok(Some(key)),
|
||||
}?;
|
||||
|
||||
key_manager.gateway_shared_key = gateway_shared_key.map(Arc::new);
|
||||
|
||||
Ok(key_manager)
|
||||
}
|
||||
|
||||
/// Stores all available keys on the disk.
|
||||
// While perhaps there is no much point in storing the `AckKey` on the disk,
|
||||
// it is done so for the consistency sake so that you wouldn't require an rng instance
|
||||
@@ -119,7 +150,7 @@ impl KeyManager {
|
||||
pemstore::store_key(self.ack_key.as_ref(), client_pathfinder.ack_key())?;
|
||||
|
||||
match self.gateway_shared_key.as_ref() {
|
||||
None => warn!("No gateway shared key available to store!"),
|
||||
None => debug!("No gateway shared key available to store!"),
|
||||
Some(gate_key) => {
|
||||
pemstore::store_key(gate_key.as_ref(), client_pathfinder.gateway_shared_key())?
|
||||
}
|
||||
@@ -128,16 +159,57 @@ impl KeyManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn store_gateway_key(&self, client_pathfinder: &ClientKeyPathfinder) -> io::Result<()> {
|
||||
match self.gateway_shared_key.as_ref() {
|
||||
None => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"trying to store a non-existing key",
|
||||
))
|
||||
}
|
||||
Some(gate_key) => {
|
||||
pemstore::store_key(gate_key.as_ref(), client_pathfinder.gateway_shared_key())?
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Overwrite the existing identity keypair
|
||||
pub fn set_identity_keypair(&mut self, id_keypair: identity::KeyPair) {
|
||||
self.identity_keypair = Arc::new(id_keypair);
|
||||
}
|
||||
|
||||
/// Gets an atomically reference counted pointer to [`identity::KeyPair`].
|
||||
pub fn identity_keypair(&self) -> Arc<identity::KeyPair> {
|
||||
Arc::clone(&self.identity_keypair)
|
||||
}
|
||||
|
||||
/// Overwrite the existing encryption keypair
|
||||
pub fn set_encryption_keypair(&mut self, enc_keypair: encryption::KeyPair) {
|
||||
self.encryption_keypair = Arc::new(enc_keypair);
|
||||
}
|
||||
|
||||
/// Gets an atomically reference counted pointer to [`encryption::KeyPair`].
|
||||
pub fn encryption_keypair(&self) -> Arc<encryption::KeyPair> {
|
||||
Arc::clone(&self.encryption_keypair)
|
||||
}
|
||||
|
||||
/// Overwrite the existing ack key
|
||||
pub fn set_ack_key(&mut self, ack_key: AckKey) {
|
||||
self.ack_key = Arc::new(ack_key);
|
||||
}
|
||||
|
||||
/// Gets an atomically reference counted pointer to [`AckKey`].
|
||||
pub fn ack_key(&self) -> Arc<AckKey> {
|
||||
Arc::clone(&self.ack_key)
|
||||
}
|
||||
|
||||
/// After shared key with the gateway is derived, puts its ownership to this instance of a [`KeyManager`].
|
||||
pub fn insert_gateway_shared_key(&mut self, gateway_shared_key: Arc<SharedKeys>) {
|
||||
self.gateway_shared_key = Some(gateway_shared_key)
|
||||
}
|
||||
|
||||
/// Gets an atomically reference counted pointer to [`SharedKey`].
|
||||
// since this function is not fully public, it is not expected to be used externally and
|
||||
// hence it's up to us to ensure it's called in correct context
|
||||
@@ -149,12 +221,7 @@ impl KeyManager {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn gateway_key_set(&self) -> bool {
|
||||
pub fn is_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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,13 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::spawn_future;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use gateway_client::wasm_mockups::CosmWasmClient;
|
||||
use gateway_client::GatewayClient;
|
||||
use log::*;
|
||||
use nymsphinx::forwarding::packet::MixPacket;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use validator_client::nyxd::CosmWasmClient;
|
||||
|
||||
pub type BatchMixMessageSender = tokio::sync::mpsc::Sender<Vec<MixPacket>>;
|
||||
pub type BatchMixMessageReceiver = tokio::sync::mpsc::Receiver<Vec<MixPacket>>;
|
||||
@@ -13,10 +17,10 @@ pub type BatchMixMessageReceiver = tokio::sync::mpsc::Receiver<Vec<MixPacket>>;
|
||||
pub const MIX_MESSAGE_RECEIVER_BUFFER_SIZE: usize = 32;
|
||||
const MAX_FAILURE_COUNT: usize = 100;
|
||||
|
||||
pub struct MixTrafficController {
|
||||
pub struct MixTrafficController<C: Clone> {
|
||||
// TODO: most likely to be replaced by some higher level construct as
|
||||
// later on gateway_client will need to be accessible by other entities
|
||||
gateway_client: GatewayClient,
|
||||
gateway_client: GatewayClient<C>,
|
||||
mix_rx: BatchMixMessageReceiver,
|
||||
|
||||
// TODO: this is temporary work-around.
|
||||
@@ -24,8 +28,13 @@ pub struct MixTrafficController {
|
||||
consecutive_gateway_failure_count: usize,
|
||||
}
|
||||
|
||||
impl MixTrafficController {
|
||||
pub fn new(gateway_client: GatewayClient) -> (MixTrafficController, BatchMixMessageSender) {
|
||||
impl<C> MixTrafficController<C>
|
||||
where
|
||||
C: CosmWasmClient + Sync + Send + Clone + 'static,
|
||||
{
|
||||
pub fn new(
|
||||
gateway_client: GatewayClient<C>,
|
||||
) -> (MixTrafficController<C>, BatchMixMessageSender) {
|
||||
let (sphinx_message_sender, sphinx_message_receiver) =
|
||||
tokio::sync::mpsc::channel(MIX_MESSAGE_RECEIVER_BUFFER_SIZE);
|
||||
(
|
||||
@@ -57,7 +66,7 @@ impl MixTrafficController {
|
||||
if self.consecutive_gateway_failure_count == MAX_FAILURE_COUNT {
|
||||
// todo: in the future this should initiate a 'graceful' shutdown or try
|
||||
// to reconnect?
|
||||
panic!("failed to send sphinx packet to the gateway {} times in a row - assuming the gateway is dead. Can't do anything about it yet :(", MAX_FAILURE_COUNT)
|
||||
panic!("failed to send sphinx packet to the gateway {MAX_FAILURE_COUNT} times in a row - assuming the gateway is dead. Can't do anything about it yet :(")
|
||||
}
|
||||
}
|
||||
Ok(_) => {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
pub mod base_client;
|
||||
pub mod cover_traffic_stream;
|
||||
pub(crate) mod helpers;
|
||||
pub mod inbound_messages;
|
||||
pub mod key_manager;
|
||||
pub mod mix_traffic;
|
||||
@@ -10,3 +11,4 @@ pub mod real_messages_control;
|
||||
pub mod received_buffer;
|
||||
pub mod replies;
|
||||
pub mod topology_control;
|
||||
pub(crate) mod transmission_buffer;
|
||||
|
||||
+16
-8
@@ -213,7 +213,11 @@ impl ActionController {
|
||||
}
|
||||
|
||||
// note: when the entry expires it's automatically removed from pending_acks_timers
|
||||
fn handle_expired_ack_timer(&mut self, expired_ack: Expired<FragmentIdentifier>) {
|
||||
fn handle_expired_ack_timer(
|
||||
&mut self,
|
||||
expired_ack: Expired<FragmentIdentifier>,
|
||||
task_client: &mut task::TaskClient,
|
||||
) {
|
||||
// I'm honestly not sure how to handle it, because getting it means other things in our
|
||||
// system are already misbehaving. If we ever see this panic, then I guess we should worry
|
||||
// about it. Perhaps just reschedule it at later point?
|
||||
@@ -231,9 +235,16 @@ impl ActionController {
|
||||
// downgrading an arc and then upgrading vs cloning is difference of 30ns vs 15ns
|
||||
// so it's literally a NO difference while it might prevent us from unnecessarily
|
||||
// resending data (in maybe 1 in 1 million cases, but it's something)
|
||||
self.retransmission_sender
|
||||
if self
|
||||
.retransmission_sender
|
||||
.unbounded_send(Arc::downgrade(pending_ack_data))
|
||||
.unwrap()
|
||||
.is_err()
|
||||
{
|
||||
assert!(
|
||||
task_client.is_shutdown_poll(),
|
||||
"Failed to send pending ack for retransmission"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// this shouldn't cause any issues but shouldn't have happened to begin with!
|
||||
error!("An already removed pending ack has expired")
|
||||
@@ -264,7 +275,7 @@ impl ActionController {
|
||||
}
|
||||
},
|
||||
expired_ack = self.pending_acks_timers.next() => match expired_ack {
|
||||
Some(expired_ack) => self.handle_expired_ack_timer(expired_ack),
|
||||
Some(expired_ack) => self.handle_expired_ack_timer(expired_ack, &mut shutdown),
|
||||
None => {
|
||||
log::trace!("ActionController: Stopping since ack channel closed");
|
||||
break;
|
||||
@@ -275,10 +286,7 @@ impl ActionController {
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
tokio::time::timeout(Duration::from_secs(5), shutdown.recv())
|
||||
.await
|
||||
.expect("Task stopped without shutdown called");
|
||||
shutdown.recv_timeout().await;
|
||||
log::debug!("ActionController: Exiting");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ use nymsphinx::params::{PacketSize, DEFAULT_NUM_MIX_HOPS};
|
||||
use nymsphinx::preparer::{MessagePreparer, PreparedFragment};
|
||||
use nymsphinx::Delay;
|
||||
use rand::{CryptoRng, Rng};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use thiserror::Error;
|
||||
@@ -278,7 +279,7 @@ where
|
||||
reply_surb: ReplySurb,
|
||||
amount: u32,
|
||||
) -> Result<(), SurbWrappedPreparationError> {
|
||||
debug!("requesting {amount} reply SURBs from {from:?}");
|
||||
debug!("requesting {amount} reply SURBs from {from}");
|
||||
|
||||
let surbs_request =
|
||||
ReplyMessage::new_surb_request_message(self.config.sender_address, amount);
|
||||
@@ -294,19 +295,11 @@ where
|
||||
)))
|
||||
}
|
||||
|
||||
// the only difference between this method and `try_send_reply_chunks` is that
|
||||
// here we are not creating acks as acks are already in memory waiting to get cleared.
|
||||
// we are only updating their existing delays
|
||||
pub(crate) async fn try_send_retransmission_reply_chunks(
|
||||
pub(crate) async fn send_retransmission_reply_chunks(
|
||||
&mut self,
|
||||
fragments: Vec<Fragment>,
|
||||
reply_surbs: Vec<ReplySurb>,
|
||||
prepared_fragments: Vec<PreparedFragment>,
|
||||
lane: TransmissionLane,
|
||||
) -> Result<(), SurbWrappedPreparationError> {
|
||||
let prepared_fragments = self
|
||||
.prepare_reply_chunks_for_sending(fragments.clone(), reply_surbs)
|
||||
.await?;
|
||||
|
||||
) {
|
||||
let mut real_messages = Vec::with_capacity(prepared_fragments.len());
|
||||
|
||||
for prepared in prepared_fragments {
|
||||
@@ -315,33 +308,58 @@ where
|
||||
}
|
||||
|
||||
self.forward_messages(real_messages, lane).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn try_send_reply_chunks(
|
||||
pub(crate) async fn try_send_reply_chunks_on_lane(
|
||||
&mut self,
|
||||
target: AnonymousSenderTag,
|
||||
fragments: Vec<Fragment>,
|
||||
reply_surbs: Vec<ReplySurb>,
|
||||
lane: TransmissionLane,
|
||||
) -> Result<(), SurbWrappedPreparationError> {
|
||||
// TODO: technically this is performing an unnecessary cloning, but in the grand scheme of things
|
||||
// is it really that bad?
|
||||
self.try_send_reply_chunks(
|
||||
target,
|
||||
fragments.into_iter().map(|f| (lane, f)).collect(),
|
||||
reply_surbs,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn try_send_reply_chunks(
|
||||
&mut self,
|
||||
target: AnonymousSenderTag,
|
||||
fragments: Vec<(TransmissionLane, Fragment)>,
|
||||
reply_surbs: Vec<ReplySurb>,
|
||||
) -> Result<(), SurbWrappedPreparationError> {
|
||||
let prepared_fragments = self
|
||||
.prepare_reply_chunks_for_sending(fragments.clone(), reply_surbs)
|
||||
.prepare_reply_chunks_for_sending(
|
||||
fragments.iter().map(|(_, f)| f.clone()).collect(),
|
||||
reply_surbs,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut pending_acks = Vec::with_capacity(fragments.len());
|
||||
let mut real_messages = Vec::with_capacity(fragments.len());
|
||||
let mut to_forward: HashMap<_, Vec<_>> = HashMap::new();
|
||||
|
||||
for (raw, prepared) in fragments.into_iter().zip(prepared_fragments.into_iter()) {
|
||||
let lane = raw.0;
|
||||
let fragment = raw.1;
|
||||
|
||||
let real_message = RealMessage::new(prepared.mix_packet, prepared.fragment_identifier);
|
||||
let delay = prepared.total_delay;
|
||||
let pending_ack = PendingAcknowledgement::new_anonymous(raw, delay, target, false);
|
||||
let pending_ack = PendingAcknowledgement::new_anonymous(fragment, delay, target, false);
|
||||
|
||||
real_messages.push(real_message);
|
||||
let entry = to_forward.entry(lane).or_default();
|
||||
entry.push(real_message);
|
||||
pending_acks.push(pending_ack);
|
||||
}
|
||||
|
||||
self.forward_messages(real_messages, lane).await;
|
||||
for (lane, real_messages) in to_forward {
|
||||
self.forward_messages(real_messages, lane).await;
|
||||
}
|
||||
|
||||
self.insert_pending_acks(pending_acks);
|
||||
Ok(())
|
||||
}
|
||||
@@ -467,7 +485,7 @@ where
|
||||
Ok(prepared_fragment)
|
||||
}
|
||||
|
||||
async fn prepare_reply_chunks_for_sending(
|
||||
pub(crate) async fn prepare_reply_chunks_for_sending(
|
||||
&mut self,
|
||||
fragments: Vec<Fragment>,
|
||||
reply_surbs: Vec<ReplySurb>,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use self::sending_delay_controller::SendingDelayController;
|
||||
use crate::client::mix_traffic::BatchMixMessageSender;
|
||||
use crate::client::real_messages_control::acknowledgement_control::SentPacketNotificationSender;
|
||||
use crate::client::topology_control::TopologyAccessor;
|
||||
use crate::client::transmission_buffer::TransmissionBuffer;
|
||||
use client_connections::{
|
||||
ConnectionCommand, ConnectionCommandReceiver, ConnectionId, LaneQueueLengths, TransmissionLane,
|
||||
};
|
||||
@@ -29,22 +31,7 @@ use tokio::time;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_timer;
|
||||
|
||||
use self::{
|
||||
sending_delay_controller::SendingDelayController, transmission_buffer::TransmissionBuffer,
|
||||
};
|
||||
|
||||
mod sending_delay_controller;
|
||||
mod transmission_buffer;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn get_time_now() -> time::Instant {
|
||||
time::Instant::now()
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn get_time_now() -> wasm_timer::Instant {
|
||||
wasm_timer::Instant::now()
|
||||
}
|
||||
|
||||
/// Configurable parameters of the `OutQueueControl`
|
||||
pub(crate) struct Config {
|
||||
@@ -135,7 +122,7 @@ where
|
||||
|
||||
/// Buffer containing all incoming real messages keyed by transmission lane, that we will send
|
||||
/// out to the mixnet.
|
||||
transmission_buffer: TransmissionBuffer,
|
||||
transmission_buffer: TransmissionBuffer<RealMessage>,
|
||||
|
||||
/// Incoming channel for being notified of closed connections, so that we can close lanes
|
||||
/// corresponding to connections. To avoid sending traffic unnecessary
|
||||
@@ -162,6 +149,10 @@ impl From<PreparedFragment> for RealMessage {
|
||||
}
|
||||
|
||||
impl RealMessage {
|
||||
pub(crate) fn packet_size(&self) -> usize {
|
||||
self.mix_packet.sphinx_packet().len()
|
||||
}
|
||||
|
||||
pub(crate) fn new(mix_packet: MixPacket, fragment_id: FragmentIdentifier) -> Self {
|
||||
RealMessage {
|
||||
mix_packet,
|
||||
@@ -207,7 +198,7 @@ where
|
||||
real_receiver,
|
||||
rng,
|
||||
topology_access,
|
||||
transmission_buffer: Default::default(),
|
||||
transmission_buffer: TransmissionBuffer::new(),
|
||||
client_connection_rx,
|
||||
lane_queue_lengths,
|
||||
}
|
||||
@@ -328,7 +319,9 @@ where
|
||||
|
||||
fn pop_next_message(&mut self) -> Option<RealMessage> {
|
||||
// Pop the next message from the transmission buffer
|
||||
let (lane, real_next) = self.transmission_buffer.pop_next_message_at_random()?;
|
||||
let (lane, real_next) = self
|
||||
.transmission_buffer
|
||||
.pop_next_message_at_random(&mut self.rng)?;
|
||||
|
||||
// Update the published queue length
|
||||
let lane_length = self.transmission_buffer.lane_length(&lane);
|
||||
@@ -478,14 +471,10 @@ where
|
||||
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!(
|
||||
"Status: {lanes} lanes, backlog: {:.2} kiB ({packets}), no delay",
|
||||
backlog
|
||||
)
|
||||
format!("Status: {lanes} lanes, backlog: {backlog:.2} kiB ({packets}), no delay")
|
||||
} else {
|
||||
format!(
|
||||
"Status: {lanes} lanes, backlog: {:.2} kiB ({packets}), avg delay: {}ms ({mult})",
|
||||
backlog, delay
|
||||
"Status: {lanes} lanes, backlog: {backlog:.2} kiB ({packets}), avg delay: {delay}ms ({mult})"
|
||||
)
|
||||
};
|
||||
if packets > 1000 {
|
||||
@@ -542,9 +531,7 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
tokio::time::timeout(Duration::from_secs(5), shutdown.recv())
|
||||
.await
|
||||
.expect("Task stopped without shutdown called");
|
||||
shutdown.recv_timeout().await;
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
|
||||
+3
-16
@@ -1,14 +1,9 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::get_time_now;
|
||||
use crate::client::helpers::{get_time_now, Instant};
|
||||
use std::time::Duration;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio::time;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_timer;
|
||||
|
||||
// The minimum time between increasing the average delay between packets. If we hit the ceiling in
|
||||
// the available buffer space we want to take somewhat swift action, but we still need to give a
|
||||
// short time to give the channel a chance reduce pressure.
|
||||
@@ -39,19 +34,11 @@ pub(crate) struct SendingDelayController {
|
||||
lower_bound: u32,
|
||||
|
||||
/// To make sure we don't change the multiplier to fast, we limit a change to some duration
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
time_when_changed: time::Instant,
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
time_when_changed: wasm_timer::Instant,
|
||||
time_when_changed: Instant,
|
||||
|
||||
/// If we have a long enough time without any backpressure detected we try reducing the sending
|
||||
/// delay multiplier
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
time_when_backpressure_detected: time::Instant,
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
time_when_backpressure_detected: wasm_timer::Instant,
|
||||
time_when_backpressure_detected: Instant,
|
||||
}
|
||||
|
||||
impl Default for SendingDelayController {
|
||||
|
||||
+155
-218
@@ -4,8 +4,8 @@
|
||||
use crate::client::real_messages_control::acknowledgement_control::PendingAcknowledgement;
|
||||
use crate::client::real_messages_control::message_handler::{MessageHandler, PreparationError};
|
||||
use crate::client::replies::reply_storage::CombinedReplyStorage;
|
||||
use client_connections::TransmissionLane;
|
||||
use futures::channel::mpsc;
|
||||
use client_connections::{ConnectionId, TransmissionLane};
|
||||
use futures::channel::oneshot;
|
||||
use futures::StreamExt;
|
||||
use log::{debug, error, info, trace, warn};
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
@@ -15,116 +15,16 @@ use nymsphinx::chunking::fragment::{Fragment, FragmentIdentifier};
|
||||
use rand::{CryptoRng, Rng};
|
||||
use std::cmp::{max, min};
|
||||
use std::collections::btree_map::Entry;
|
||||
use std::collections::{BTreeMap, HashMap, VecDeque};
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::time::Duration;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
type IntervalStream = tokio_stream::wrappers::IntervalStream;
|
||||
use crate::client::helpers::new_interval_stream;
|
||||
use crate::client::transmission_buffer::TransmissionBuffer;
|
||||
pub(crate) use requests::{ReplyControllerMessage, ReplyControllerReceiver, ReplyControllerSender};
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
type IntervalStream = gloo_timers::future::IntervalStream;
|
||||
|
||||
pub(crate) fn new_control_channels() -> (ReplyControllerSender, ReplyControllerReceiver) {
|
||||
let (tx, rx) = mpsc::unbounded();
|
||||
(tx.into(), rx)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct ReplyControllerSender(mpsc::UnboundedSender<ReplyControllerMessage>);
|
||||
|
||||
impl From<mpsc::UnboundedSender<ReplyControllerMessage>> for ReplyControllerSender {
|
||||
fn from(inner: mpsc::UnboundedSender<ReplyControllerMessage>) -> Self {
|
||||
ReplyControllerSender(inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl ReplyControllerSender {
|
||||
pub(crate) fn send_retransmission_data(
|
||||
&self,
|
||||
recipient: AnonymousSenderTag,
|
||||
timed_out_ack: Weak<PendingAcknowledgement>,
|
||||
extra_surb_request: bool,
|
||||
) {
|
||||
self.0
|
||||
.unbounded_send(ReplyControllerMessage::RetransmitReply {
|
||||
recipient,
|
||||
timed_out_ack,
|
||||
extra_surb_request,
|
||||
})
|
||||
.expect("ReplyControllerReceiver has died!")
|
||||
}
|
||||
|
||||
pub(crate) fn send_reply(
|
||||
&self,
|
||||
recipient: AnonymousSenderTag,
|
||||
message: Vec<u8>,
|
||||
lane: TransmissionLane,
|
||||
) {
|
||||
self.0
|
||||
.unbounded_send(ReplyControllerMessage::SendReply {
|
||||
recipient,
|
||||
message,
|
||||
lane,
|
||||
})
|
||||
.expect("ReplyControllerReceiver has died!")
|
||||
}
|
||||
|
||||
pub(crate) fn send_additional_surbs(
|
||||
&self,
|
||||
sender_tag: AnonymousSenderTag,
|
||||
reply_surbs: Vec<ReplySurb>,
|
||||
from_surb_request: bool,
|
||||
) {
|
||||
self.0
|
||||
.unbounded_send(ReplyControllerMessage::AdditionalSurbs {
|
||||
sender_tag,
|
||||
reply_surbs,
|
||||
from_surb_request,
|
||||
})
|
||||
.expect("ReplyControllerReceiver has died!")
|
||||
}
|
||||
|
||||
pub(crate) fn send_additional_surbs_request(&self, recipient: Recipient, amount: u32) {
|
||||
self.0
|
||||
.unbounded_send(ReplyControllerMessage::AdditionalSurbsRequest {
|
||||
recipient: Box::new(recipient),
|
||||
amount,
|
||||
})
|
||||
.expect("ReplyControllerReceiver has died!")
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) type ReplyControllerReceiver = mpsc::UnboundedReceiver<ReplyControllerMessage>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum ReplyControllerMessage {
|
||||
RetransmitReply {
|
||||
recipient: AnonymousSenderTag,
|
||||
timed_out_ack: Weak<PendingAcknowledgement>,
|
||||
extra_surb_request: bool,
|
||||
},
|
||||
|
||||
SendReply {
|
||||
recipient: AnonymousSenderTag,
|
||||
message: Vec<u8>,
|
||||
lane: TransmissionLane,
|
||||
},
|
||||
|
||||
AdditionalSurbs {
|
||||
sender_tag: AnonymousSenderTag,
|
||||
reply_surbs: Vec<ReplySurb>,
|
||||
from_surb_request: bool,
|
||||
},
|
||||
|
||||
// Should this also be handled in here? it's technically a completely different side of the pipe
|
||||
// let's see how it works when combined, might split it before creating PR
|
||||
AdditionalSurbsRequest {
|
||||
recipient: Box<Recipient>,
|
||||
amount: u32,
|
||||
},
|
||||
}
|
||||
pub mod requests;
|
||||
|
||||
pub struct Config {
|
||||
min_surb_request_size: u32,
|
||||
@@ -172,7 +72,7 @@ pub struct ReplyController<R> {
|
||||
// of surbs required to send the message through
|
||||
// expected_reliability: f32,
|
||||
request_receiver: ReplyControllerReceiver,
|
||||
pending_replies: HashMap<AnonymousSenderTag, VecDeque<Fragment>>,
|
||||
pending_replies: HashMap<AnonymousSenderTag, TransmissionBuffer<Fragment>>,
|
||||
|
||||
/// Retransmission packets that have already timed out and are waiting for additional reply SURBs
|
||||
/// so that they could be sent back to the network. Once we receive more SURBs, we should send them ASAP.
|
||||
@@ -204,17 +104,28 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Inserts the pending replies into the BACK of the queue fn insert_pending_replies<V: Into<VecDeque<Fragment>>>(
|
||||
fn insert_pending_replies<V: Into<VecDeque<Fragment>>>(
|
||||
fn insert_pending_replies<I: IntoIterator<Item = Fragment>>(
|
||||
&mut self,
|
||||
recipient: &AnonymousSenderTag,
|
||||
fragments: V,
|
||||
fragments: I,
|
||||
lane: TransmissionLane,
|
||||
) {
|
||||
if let Some(existing) = self.pending_replies.get_mut(recipient) {
|
||||
existing.append(&mut fragments.into())
|
||||
} else {
|
||||
self.pending_replies.insert(*recipient, fragments.into());
|
||||
}
|
||||
self.pending_replies
|
||||
.entry(*recipient)
|
||||
.or_insert_with(TransmissionBuffer::new)
|
||||
.store(&lane, fragments)
|
||||
}
|
||||
|
||||
fn re_insert_pending_replies(
|
||||
&mut self,
|
||||
recipient: &AnonymousSenderTag,
|
||||
fragments: Vec<(TransmissionLane, Fragment)>,
|
||||
) {
|
||||
// the buffer should ALWAYS exist at this point, if it doesn't, it's a bug...
|
||||
self.pending_replies
|
||||
.entry(*recipient)
|
||||
.or_insert_with(TransmissionBuffer::new)
|
||||
.store_multiple(fragments)
|
||||
}
|
||||
|
||||
fn re_insert_pending_retransmission(
|
||||
@@ -244,7 +155,7 @@ where
|
||||
let pending_queue_size = self
|
||||
.pending_replies
|
||||
.get(target)
|
||||
.map(|pending_queue| pending_queue.len())
|
||||
.map(|pending_queue| pending_queue.total_size())
|
||||
.unwrap_or_default();
|
||||
|
||||
let retransmission_queue = self
|
||||
@@ -299,43 +210,62 @@ where
|
||||
}
|
||||
|
||||
trace!("handling reply to {:?}", recipient_tag);
|
||||
let fragments = self.message_handler.split_reply_message(data);
|
||||
let mut fragments = self.message_handler.split_reply_message(data);
|
||||
let total_size = fragments.len();
|
||||
trace!("This reply requires {:?} SURBs", total_size);
|
||||
|
||||
let required_surbs = fragments.len();
|
||||
trace!("This reply requires {:?} SURBs", required_surbs);
|
||||
|
||||
// TODO: edge case:
|
||||
// we're making a lot of requests and have to request a lot of surbs
|
||||
// (but at some point we run out of surbs for surb requests)
|
||||
|
||||
let (surbs, _surbs_left) = self
|
||||
let available_surbs = self
|
||||
.full_reply_storage
|
||||
.surbs_storage_ref()
|
||||
.get_reply_surbs(&recipient_tag, required_surbs);
|
||||
.available_surbs(&recipient_tag);
|
||||
let min_surbs_threshold = self
|
||||
.full_reply_storage
|
||||
.surbs_storage_ref()
|
||||
.min_surb_threshold();
|
||||
|
||||
if let Some(reply_surbs) = surbs {
|
||||
if let Err(err) = self
|
||||
.message_handler
|
||||
.try_send_reply_chunks(recipient_tag, fragments, reply_surbs, lane)
|
||||
.await
|
||||
{
|
||||
let err = err.return_unused_surbs(
|
||||
self.full_reply_storage.surbs_storage_ref(),
|
||||
&recipient_tag,
|
||||
);
|
||||
warn!("failed to send reply to {:?} - {err}", recipient_tag);
|
||||
|
||||
// TODO: should we buffer that data to try again?
|
||||
}
|
||||
let max_to_send = if available_surbs > min_surbs_threshold {
|
||||
min(fragments.len(), available_surbs - min_surbs_threshold)
|
||||
} else {
|
||||
// we don't have enough surbs for this reply
|
||||
self.insert_pending_replies(&recipient_tag, fragments);
|
||||
0
|
||||
};
|
||||
|
||||
if self.should_request_more_surbs(&recipient_tag) {
|
||||
self.request_reply_surbs_for_queue_clearing(recipient_tag)
|
||||
.await;
|
||||
if max_to_send > 0 {
|
||||
let (surbs, _surbs_left) = self
|
||||
.full_reply_storage
|
||||
.surbs_storage_ref()
|
||||
.get_reply_surbs(&recipient_tag, max_to_send);
|
||||
|
||||
if let Some(reply_surbs) = surbs {
|
||||
let to_send = fragments.drain(..max_to_send).collect::<Vec<_>>();
|
||||
if let Err(err) = self
|
||||
.message_handler
|
||||
.try_send_reply_chunks_on_lane(
|
||||
recipient_tag,
|
||||
to_send.clone(),
|
||||
reply_surbs,
|
||||
lane,
|
||||
)
|
||||
.await
|
||||
{
|
||||
let err = err.return_unused_surbs(
|
||||
self.full_reply_storage.surbs_storage_ref(),
|
||||
&recipient_tag,
|
||||
);
|
||||
warn!("failed to send reply to {recipient_tag}: {err}");
|
||||
self.insert_pending_replies(&recipient_tag, to_send, lane);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if there's leftover data we didn't send because we didn't have enough (or any) surbs - buffer it
|
||||
if !fragments.is_empty() {
|
||||
self.insert_pending_replies(&recipient_tag, fragments, lane);
|
||||
}
|
||||
|
||||
if self.should_request_more_surbs(&recipient_tag) {
|
||||
self.request_reply_surbs_for_queue_clearing(recipient_tag)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn request_additional_reply_surbs(
|
||||
@@ -398,35 +328,18 @@ where
|
||||
};
|
||||
|
||||
let mut to_take = Vec::new();
|
||||
let mut to_remove = Vec::new();
|
||||
|
||||
// TODO: once rust 1.66.0 is stabilised on 15.12.22, just change it to
|
||||
// `.pop_front()` to directly take ownership
|
||||
for (k, data) in pending.iter() {
|
||||
let upgraded = match data.upgrade() {
|
||||
Some(upgraded) => upgraded,
|
||||
None => {
|
||||
// we got the ack while the data was waiting in the queue
|
||||
to_remove.push(*k);
|
||||
continue;
|
||||
while to_take.len() < max_to_clear {
|
||||
if let Some((_, data)) = pending.pop_first() {
|
||||
// no need to do anything if we failed to upgrade the reference,
|
||||
// it means we got the ack while the data was waiting in the queue
|
||||
if let Some(upgraded) = data.upgrade() {
|
||||
to_take.push(upgraded)
|
||||
}
|
||||
};
|
||||
|
||||
to_take.push(upgraded);
|
||||
|
||||
// we have taken as many entries as we could have
|
||||
if to_take.len() >= max_to_clear {
|
||||
} else {
|
||||
// our map is empty!
|
||||
break;
|
||||
}
|
||||
// TODO: use if upgraded.is_extra_surb_request() to bypass the limit
|
||||
}
|
||||
|
||||
for ack in &to_take {
|
||||
pending.remove(&ack.inner_fragment_identifier());
|
||||
}
|
||||
|
||||
for id in to_remove {
|
||||
pending.remove(&id);
|
||||
}
|
||||
|
||||
if to_take.is_empty() {
|
||||
@@ -447,46 +360,47 @@ where
|
||||
|
||||
let to_send_vec = to_take.iter().map(|ack| ack.fragment_data()).collect();
|
||||
|
||||
if let Err(err) = self
|
||||
let prepared_fragments = match self
|
||||
.message_handler
|
||||
.try_send_retransmission_reply_chunks(
|
||||
to_send_vec,
|
||||
surbs_for_reply,
|
||||
TransmissionLane::Retransmission,
|
||||
)
|
||||
.prepare_reply_chunks_for_sending(to_send_vec, surbs_for_reply)
|
||||
.await
|
||||
{
|
||||
let err = err.return_unused_surbs(self.full_reply_storage.surbs_storage_ref(), &target);
|
||||
self.re_insert_pending_retransmission(&target, to_take);
|
||||
Ok(prepared) => prepared,
|
||||
Err(err) => {
|
||||
let err =
|
||||
err.return_unused_surbs(self.full_reply_storage.surbs_storage_ref(), &target);
|
||||
self.re_insert_pending_retransmission(&target, to_take);
|
||||
|
||||
warn!(
|
||||
"failed to clear pending retransmission queue for {:?} - {err}",
|
||||
target
|
||||
);
|
||||
}
|
||||
warn!(
|
||||
"failed to clear pending retransmission queue for {:?} - {err}",
|
||||
target
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// we can't fail at this point, so drop all references to acks so that timer updates wouldn't blow up
|
||||
drop(to_take);
|
||||
|
||||
self.message_handler
|
||||
.send_retransmission_reply_chunks(prepared_fragments, TransmissionLane::Retransmission)
|
||||
.await;
|
||||
}
|
||||
|
||||
fn pop_at_most_pending_replies(
|
||||
&mut self,
|
||||
from: &AnonymousSenderTag,
|
||||
amount: usize,
|
||||
) -> Option<VecDeque<Fragment>> {
|
||||
) -> Option<Vec<(TransmissionLane, Fragment)>> {
|
||||
// if possible, pop all pending replies, if not, pop only entries for which we'd have a reply surb
|
||||
let total = self.pending_replies.get(from)?.len();
|
||||
let total = self.pending_replies.get(from)?.total_size();
|
||||
trace!("pending queue has {total} elements");
|
||||
if total == 0 {
|
||||
return None;
|
||||
}
|
||||
if total < amount {
|
||||
self.pending_replies.remove(from)
|
||||
} else {
|
||||
Some(
|
||||
self.pending_replies
|
||||
.get_mut(from)?
|
||||
.drain(..amount)
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
self.pending_replies
|
||||
.get_mut(from)?
|
||||
.pop_at_most_n_next_messages_at_random(amount)
|
||||
}
|
||||
|
||||
async fn try_clear_pending_queue(&mut self, target: AnonymousSenderTag) {
|
||||
@@ -510,9 +424,9 @@ where
|
||||
|
||||
// we're guaranteed to not get more entries than we have reply surbs for
|
||||
if let Some(to_send) = self.pop_at_most_pending_replies(&target, max_to_clear) {
|
||||
let to_send_vec = to_send.iter().cloned().collect::<Vec<_>>();
|
||||
let to_send_clone = to_send.clone();
|
||||
|
||||
if to_send_vec.is_empty() {
|
||||
if to_send_clone.is_empty() {
|
||||
panic!(
|
||||
"please let the devs know if you ever see this message (reply_controller.rs)"
|
||||
);
|
||||
@@ -521,27 +435,22 @@ where
|
||||
let (surbs_for_reply, _) = self
|
||||
.full_reply_storage
|
||||
.surbs_storage_ref()
|
||||
.get_reply_surbs(&target, to_send_vec.len());
|
||||
.get_reply_surbs(&target, to_send_clone.len());
|
||||
|
||||
let Some(surbs_for_reply) = surbs_for_reply else {
|
||||
error!("somehow different task has stolen our reply surbs! - this should have been impossible");
|
||||
self.insert_pending_replies(&target, to_send);
|
||||
self.re_insert_pending_replies(&target, to_send);
|
||||
return;
|
||||
};
|
||||
|
||||
if let Err(err) = self
|
||||
.message_handler
|
||||
.try_send_reply_chunks(
|
||||
target,
|
||||
to_send_vec,
|
||||
surbs_for_reply,
|
||||
TransmissionLane::General,
|
||||
)
|
||||
.try_send_reply_chunks(target, to_send_clone, surbs_for_reply)
|
||||
.await
|
||||
{
|
||||
let err =
|
||||
err.return_unused_surbs(self.full_reply_storage.surbs_storage_ref(), &target);
|
||||
self.insert_pending_replies(&target, to_send);
|
||||
self.re_insert_pending_replies(&target, to_send);
|
||||
warn!("failed to clear pending queue for {:?} - {err}", target);
|
||||
}
|
||||
} else {
|
||||
@@ -712,6 +621,30 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// to be honest this doesn't make a lot of sense in the context of `connection_id`,
|
||||
// it should really be asked per tag
|
||||
fn handle_lane_queue_length(
|
||||
&self,
|
||||
connection_id: ConnectionId,
|
||||
response_channel: oneshot::Sender<usize>,
|
||||
) {
|
||||
// TODO: if we ever have duplicate ids for different senders, it means our rng is super weak
|
||||
// thus I don't think we have to worry about it?
|
||||
let lane = TransmissionLane::ConnectionId(connection_id);
|
||||
for buf in self.pending_replies.values() {
|
||||
if let Some(length) = buf.lane_length(&lane) {
|
||||
if response_channel.send(length).is_err() {
|
||||
error!("the requester for lane queue length has dropped the response channel!")
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
// make sure that if we didn't find that lane, we reply with 0
|
||||
if response_channel.send(0).is_err() {
|
||||
error!("the requester for lane queue length has dropped the response channel!")
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_request(&mut self, request: ReplyControllerMessage) {
|
||||
match request {
|
||||
ReplyControllerMessage::RetransmitReply {
|
||||
@@ -735,19 +668,26 @@ where
|
||||
self.handle_received_surbs(sender_tag, reply_surbs, from_surb_request)
|
||||
.await
|
||||
}
|
||||
ReplyControllerMessage::LaneQueueLength {
|
||||
connection_id,
|
||||
response_channel,
|
||||
} => self.handle_lane_queue_length(connection_id, response_channel),
|
||||
ReplyControllerMessage::AdditionalSurbsRequest { recipient, amount } => {
|
||||
self.handle_surb_request(*recipient, amount).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: modify this method to more accurately determine the amount of surbs it needs to request
|
||||
// it should take into consideration the average latency, sending rate and queue size.
|
||||
// it should request as many surbs as it takes to saturate its sending rate before next batch arrives
|
||||
async fn request_reply_surbs_for_queue_clearing(&mut self, target: AnonymousSenderTag) {
|
||||
trace!("requesting surbs for queues clearing");
|
||||
|
||||
let pending_queue_size = self
|
||||
.pending_replies
|
||||
.get(&target)
|
||||
.map(|pending_queue| pending_queue.len())
|
||||
.map(|pending_queue| pending_queue.total_size())
|
||||
.unwrap_or_default();
|
||||
|
||||
let retransmission_queue = self
|
||||
@@ -787,7 +727,7 @@ where
|
||||
}
|
||||
|
||||
let Some(last_received) = self.full_reply_storage.surbs_storage_ref().surbs_last_received_at(pending_reply_target) else {
|
||||
error!("we have {} pending replies for {pending_reply_target}, but we somehow never received any reply surbs from them!", vals.len());
|
||||
error!("we have {} pending replies for {pending_reply_target}, but we somehow never received any reply surbs from them!", vals.total_size());
|
||||
to_remove.push(*pending_reply_target);
|
||||
continue;
|
||||
};
|
||||
@@ -883,23 +823,20 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn create_interval_stream(polling_rate: Duration) -> IntervalStream {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
return tokio_stream::wrappers::IntervalStream::new(tokio::time::interval(polling_rate));
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
return gloo_timers::future::IntervalStream::new(polling_rate.as_millis() as u32);
|
||||
}
|
||||
// #[cfg(not(target_arch = "wasm32"))]
|
||||
// async fn log_status(&self) {
|
||||
// todo!()
|
||||
// }
|
||||
|
||||
pub(crate) async fn run_with_shutdown(&mut self, mut shutdown: task::TaskClient) {
|
||||
debug!("Started ReplyController with graceful shutdown support");
|
||||
|
||||
let polling_rate = Duration::from_secs(5);
|
||||
let mut stale_inspection = Self::create_interval_stream(polling_rate);
|
||||
let mut stale_inspection = new_interval_stream(polling_rate);
|
||||
|
||||
// this is in the order of hours/days so we don't have to poll it that often
|
||||
let polling_rate = Duration::from_secs(self.config.max_reply_surb_age.as_secs() / 10);
|
||||
let mut invalidation_inspection = Self::create_interval_stream(polling_rate);
|
||||
let mut invalidation_inspection = new_interval_stream(polling_rate);
|
||||
|
||||
while !shutdown.is_shutdown() {
|
||||
tokio::select! {
|
||||
@@ -0,0 +1,154 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::real_messages_control::acknowledgement_control::PendingAcknowledgement;
|
||||
use client_connections::{ConnectionId, TransmissionLane};
|
||||
use futures::channel::{mpsc, oneshot};
|
||||
use log::error;
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
use nymsphinx::anonymous_replies::requests::AnonymousSenderTag;
|
||||
use nymsphinx::anonymous_replies::ReplySurb;
|
||||
use std::sync::Weak;
|
||||
|
||||
pub(crate) fn new_control_channels() -> (ReplyControllerSender, ReplyControllerReceiver) {
|
||||
let (tx, rx) = mpsc::unbounded();
|
||||
(tx.into(), rx)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ReplyControllerSender(mpsc::UnboundedSender<ReplyControllerMessage>);
|
||||
|
||||
impl From<mpsc::UnboundedSender<ReplyControllerMessage>> for ReplyControllerSender {
|
||||
fn from(inner: mpsc::UnboundedSender<ReplyControllerMessage>) -> Self {
|
||||
ReplyControllerSender(inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl ReplyControllerSender {
|
||||
pub(crate) fn send_retransmission_data(
|
||||
&self,
|
||||
recipient: AnonymousSenderTag,
|
||||
timed_out_ack: Weak<PendingAcknowledgement>,
|
||||
extra_surb_request: bool,
|
||||
) {
|
||||
self.0
|
||||
.unbounded_send(ReplyControllerMessage::RetransmitReply {
|
||||
recipient,
|
||||
timed_out_ack,
|
||||
extra_surb_request,
|
||||
})
|
||||
.expect("ReplyControllerReceiver has died!")
|
||||
}
|
||||
|
||||
pub(crate) fn send_reply(
|
||||
&self,
|
||||
recipient: AnonymousSenderTag,
|
||||
message: Vec<u8>,
|
||||
lane: TransmissionLane,
|
||||
) {
|
||||
self.0
|
||||
.unbounded_send(ReplyControllerMessage::SendReply {
|
||||
recipient,
|
||||
message,
|
||||
lane,
|
||||
})
|
||||
.expect("ReplyControllerReceiver has died!")
|
||||
}
|
||||
|
||||
pub(crate) fn send_additional_surbs(
|
||||
&self,
|
||||
sender_tag: AnonymousSenderTag,
|
||||
reply_surbs: Vec<ReplySurb>,
|
||||
from_surb_request: bool,
|
||||
) {
|
||||
self.0
|
||||
.unbounded_send(ReplyControllerMessage::AdditionalSurbs {
|
||||
sender_tag,
|
||||
reply_surbs,
|
||||
from_surb_request,
|
||||
})
|
||||
.expect("ReplyControllerReceiver has died!")
|
||||
}
|
||||
|
||||
pub(crate) fn send_additional_surbs_request(&self, recipient: Recipient, amount: u32) {
|
||||
self.0
|
||||
.unbounded_send(ReplyControllerMessage::AdditionalSurbsRequest {
|
||||
recipient: Box::new(recipient),
|
||||
amount,
|
||||
})
|
||||
.expect("ReplyControllerReceiver has died!")
|
||||
}
|
||||
|
||||
pub async fn get_lane_queue_length(&self, connection_id: ConnectionId) -> usize {
|
||||
let (response_tx, response_rx) = oneshot::channel();
|
||||
self.0
|
||||
.unbounded_send(ReplyControllerMessage::LaneQueueLength {
|
||||
connection_id,
|
||||
response_channel: response_tx,
|
||||
})
|
||||
.expect("ReplyControllerReceiver has died!");
|
||||
|
||||
match response_rx.await {
|
||||
Ok(length) => length,
|
||||
Err(_) => {
|
||||
error!("The reply controller has dropped our response channel!");
|
||||
// TODO: should we panic here instead? this message implies something weird and unrecoverable has happened
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ReplyQueueLengths {
|
||||
reply_controller_sender: ReplyControllerSender,
|
||||
}
|
||||
|
||||
impl ReplyQueueLengths {
|
||||
pub fn new(reply_controller_sender: ReplyControllerSender) -> Self {
|
||||
Self {
|
||||
reply_controller_sender,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_lane_queue_length(&self, connection_id: ConnectionId) -> usize {
|
||||
self.reply_controller_sender
|
||||
.get_lane_queue_length(connection_id)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) type ReplyControllerReceiver = mpsc::UnboundedReceiver<ReplyControllerMessage>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum ReplyControllerMessage {
|
||||
RetransmitReply {
|
||||
recipient: AnonymousSenderTag,
|
||||
timed_out_ack: Weak<PendingAcknowledgement>,
|
||||
extra_surb_request: bool,
|
||||
},
|
||||
|
||||
SendReply {
|
||||
recipient: AnonymousSenderTag,
|
||||
message: Vec<u8>,
|
||||
lane: TransmissionLane,
|
||||
},
|
||||
|
||||
AdditionalSurbs {
|
||||
sender_tag: AnonymousSenderTag,
|
||||
reply_surbs: Vec<ReplySurb>,
|
||||
from_surb_request: bool,
|
||||
},
|
||||
|
||||
// this one doesn't belong here either...
|
||||
LaneQueueLength {
|
||||
connection_id: ConnectionId,
|
||||
response_channel: oneshot::Sender<usize>,
|
||||
},
|
||||
|
||||
// Should this also be handled in here? it's technically a completely different side of the pipe
|
||||
// let's see how it works when combined, might split it before creating PR
|
||||
AdditionalSurbsRequest {
|
||||
recipient: Box<Recipient>,
|
||||
amount: u32,
|
||||
},
|
||||
}
|
||||
@@ -7,6 +7,7 @@ use async_trait::async_trait;
|
||||
|
||||
// well, right now we don't have the browser storage : (
|
||||
// so we keep everything in memory
|
||||
#[derive(Debug)]
|
||||
pub struct Backend {
|
||||
empty: Empty,
|
||||
}
|
||||
@@ -40,4 +41,8 @@ impl ReplyStorageBackend for Backend {
|
||||
async fn load_surb_storage(&self) -> Result<CombinedReplyStorage, Self::StorageError> {
|
||||
self.empty.load_surb_storage().await
|
||||
}
|
||||
|
||||
fn get_inactive_storage(&self) -> Result<CombinedReplyStorage, Self::StorageError> {
|
||||
self.empty.get_inactive_storage()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,12 @@ pub enum StorageError {
|
||||
#[error("the provided database path doesn't have a filename defined")]
|
||||
DatabasePathWithoutFilename { provided_path: PathBuf },
|
||||
|
||||
#[error("unable to create the directory for the database")]
|
||||
DatabasePathUnableToCreateParentDirectory {
|
||||
provided_path: PathBuf,
|
||||
source: io::Error,
|
||||
},
|
||||
|
||||
#[error("failed to rename our databse file - {source}")]
|
||||
DatabaseRenameError {
|
||||
#[source]
|
||||
|
||||
@@ -20,6 +20,16 @@ impl StorageManager {
|
||||
database_path: P,
|
||||
fresh: bool,
|
||||
) -> Result<Self, StorageError> {
|
||||
// ensure the whole directory structure exists
|
||||
if let Some(parent_dir) = database_path.as_ref().parent() {
|
||||
std::fs::create_dir_all(parent_dir).map_err(|source| {
|
||||
StorageError::DatabasePathUnableToCreateParentDirectory {
|
||||
provided_path: database_path.as_ref().to_path_buf(),
|
||||
source,
|
||||
}
|
||||
})?;
|
||||
}
|
||||
|
||||
let mut opts = sqlx::sqlite::SqliteConnectOptions::new()
|
||||
.filename(database_path)
|
||||
.create_if_missing(fresh);
|
||||
|
||||
@@ -22,11 +22,49 @@ mod error;
|
||||
mod manager;
|
||||
mod models;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum StorageManagerState {
|
||||
Storage(StorageManager),
|
||||
Inactive(InactiveMetadata),
|
||||
}
|
||||
|
||||
// When the storage backaed is initialized as inactive, it will still contain metadata parameters
|
||||
// that will be needed when the in-mem storage is fetched for use.
|
||||
#[derive(Debug)]
|
||||
struct InactiveMetadata {
|
||||
pub minimum_reply_surb_storage_threshold: usize,
|
||||
pub maximum_reply_surb_storage_threshold: usize,
|
||||
}
|
||||
|
||||
impl StorageManagerState {
|
||||
fn get(&self) -> &StorageManager {
|
||||
match self {
|
||||
StorageManagerState::Storage(manager) => manager,
|
||||
StorageManagerState::Inactive(_) => {
|
||||
panic!("tried to get storage of an inactive backend")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_mut(&mut self) -> &mut StorageManager {
|
||||
match self {
|
||||
StorageManagerState::Storage(manager) => manager,
|
||||
StorageManagerState::Inactive(_) => {
|
||||
panic!("tried to get storage of an inactive backend")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_active(&self) -> bool {
|
||||
matches!(self, StorageManagerState::Storage(_))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Backend {
|
||||
temporary_old_path: Option<PathBuf>,
|
||||
database_path: PathBuf,
|
||||
manager: StorageManager,
|
||||
manager: StorageManagerState,
|
||||
}
|
||||
|
||||
impl Backend {
|
||||
@@ -40,17 +78,32 @@ impl Backend {
|
||||
});
|
||||
}
|
||||
|
||||
let manager = StorageManager::init(database_path, true).await?;
|
||||
manager.create_status_table().await?;
|
||||
|
||||
let backend = Backend {
|
||||
temporary_old_path: None,
|
||||
database_path: owned_path,
|
||||
manager: StorageManager::init(database_path, true).await?,
|
||||
manager: StorageManagerState::Storage(manager),
|
||||
};
|
||||
|
||||
backend.manager.create_status_table().await?;
|
||||
|
||||
Ok(backend)
|
||||
}
|
||||
|
||||
pub fn new_inactive(
|
||||
minimum_reply_surb_storage_threshold: usize,
|
||||
maximum_reply_surb_storage_threshold: usize,
|
||||
) -> Self {
|
||||
Backend {
|
||||
temporary_old_path: None,
|
||||
database_path: PathBuf::new(),
|
||||
manager: StorageManagerState::Inactive(InactiveMetadata {
|
||||
minimum_reply_surb_storage_threshold,
|
||||
maximum_reply_surb_storage_threshold,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn try_load<P: AsRef<Path>>(database_path: P) -> Result<Self, StorageError> {
|
||||
let owned_path: PathBuf = database_path.as_ref().into();
|
||||
if owned_path.file_name().is_none() {
|
||||
@@ -119,12 +172,12 @@ impl Backend {
|
||||
Ok(Backend {
|
||||
temporary_old_path: None,
|
||||
database_path: owned_path,
|
||||
manager,
|
||||
manager: StorageManagerState::Storage(manager),
|
||||
})
|
||||
}
|
||||
|
||||
async fn close_pool(&mut self) {
|
||||
self.manager.connection_pool.close().await;
|
||||
self.manager.get_mut().connection_pool.close().await;
|
||||
}
|
||||
|
||||
async fn rotate(&mut self) -> Result<(), StorageError> {
|
||||
@@ -143,8 +196,9 @@ impl Backend {
|
||||
|
||||
fs::rename(&self.database_path, &temp_old)
|
||||
.map_err(|err| StorageError::DatabaseRenameError { source: err })?;
|
||||
self.manager = StorageManager::init(&self.database_path, true).await?;
|
||||
self.manager.create_status_table().await?;
|
||||
self.manager =
|
||||
StorageManagerState::Storage(StorageManager::init(&self.database_path, true).await?);
|
||||
self.manager.get_mut().create_status_table().await?;
|
||||
|
||||
self.temporary_old_path = Some(temp_old);
|
||||
Ok(())
|
||||
@@ -161,26 +215,27 @@ impl Backend {
|
||||
}
|
||||
|
||||
async fn start_storage_flush(&self) -> Result<(), StorageError> {
|
||||
Ok(self.manager.set_flush_status(true).await?)
|
||||
Ok(self.manager.get().set_flush_status(true).await?)
|
||||
}
|
||||
|
||||
async fn end_storage_flush(&self) -> Result<(), StorageError> {
|
||||
self.manager
|
||||
.get()
|
||||
.set_previous_flush_timestamp(OffsetDateTime::now_utc().unix_timestamp())
|
||||
.await?;
|
||||
Ok(self.manager.set_flush_status(false).await?)
|
||||
Ok(self.manager.get().set_flush_status(false).await?)
|
||||
}
|
||||
|
||||
async fn start_client_use(&self) -> Result<(), StorageError> {
|
||||
Ok(self.manager.set_client_in_use_status(true).await?)
|
||||
Ok(self.manager.get().set_client_in_use_status(true).await?)
|
||||
}
|
||||
|
||||
async fn stop_client_use(&self) -> Result<(), StorageError> {
|
||||
Ok(self.manager.set_client_in_use_status(false).await?)
|
||||
Ok(self.manager.get().set_client_in_use_status(false).await?)
|
||||
}
|
||||
|
||||
async fn get_stored_tags(&self) -> Result<UsedSenderTags, StorageError> {
|
||||
let stored = self.manager.get_tags().await?;
|
||||
let stored = self.manager.get().get_tags().await?;
|
||||
|
||||
// stop at the first instance of corruption. if even a single entry is malformed,
|
||||
// something weird has happened and we can't trust the rest of the data
|
||||
@@ -196,6 +251,7 @@ impl Backend {
|
||||
for map_ref in tags.as_raw_iter() {
|
||||
let (recipient, tag) = map_ref.pair();
|
||||
self.manager
|
||||
.get()
|
||||
.insert_tag(StoredSenderTag::new(*recipient, *tag))
|
||||
.await?;
|
||||
}
|
||||
@@ -203,7 +259,7 @@ impl Backend {
|
||||
}
|
||||
|
||||
async fn get_stored_reply_keys(&self) -> Result<SentReplyKeys, StorageError> {
|
||||
let stored = self.manager.get_reply_keys().await?;
|
||||
let stored = self.manager.get().get_reply_keys().await?;
|
||||
|
||||
// stop at the first instance of corruption. if even a single entry is malformed,
|
||||
// something weird has happened and we can't trust the rest of the data
|
||||
@@ -219,6 +275,7 @@ impl Backend {
|
||||
for map_ref in reply_keys.as_raw_iter() {
|
||||
let (digest, key) = map_ref.pair();
|
||||
self.manager
|
||||
.get()
|
||||
.insert_reply_key(StoredReplyKey::new(*digest, *key))
|
||||
.await?;
|
||||
}
|
||||
@@ -226,7 +283,7 @@ impl Backend {
|
||||
}
|
||||
|
||||
async fn get_stored_reply_surbs(&self) -> Result<ReceivedReplySurbsMap, StorageError> {
|
||||
let surb_senders = self.manager.get_surb_senders().await?;
|
||||
let surb_senders = self.manager.get().get_surb_senders().await?;
|
||||
|
||||
let metadata = self.get_reply_surb_storage_metadata().await?;
|
||||
let mut received_surbs = Vec::with_capacity(surb_senders.len());
|
||||
@@ -236,6 +293,7 @@ impl Backend {
|
||||
sender.try_into()?;
|
||||
let stored_surbs = self
|
||||
.manager
|
||||
.get()
|
||||
.get_reply_surbs(sender_id)
|
||||
.await?
|
||||
.into_iter()
|
||||
@@ -263,6 +321,7 @@ impl Backend {
|
||||
let (tag, received_surbs) = map_ref.pair();
|
||||
let sender_id = self
|
||||
.manager
|
||||
.get()
|
||||
.insert_surb_sender(StoredSurbSender::new(
|
||||
*tag,
|
||||
received_surbs.surbs_last_received_at(),
|
||||
@@ -271,6 +330,7 @@ impl Backend {
|
||||
|
||||
for reply_surb in received_surbs.surbs_ref() {
|
||||
self.manager
|
||||
.get()
|
||||
.insert_reply_surb(StoredReplySurb::new(sender_id, reply_surb))
|
||||
.await?
|
||||
}
|
||||
@@ -282,6 +342,7 @@ impl Backend {
|
||||
&self,
|
||||
) -> Result<ReplySurbStorageMetadata, StorageError> {
|
||||
self.manager
|
||||
.get()
|
||||
.get_reply_surb_storage_metadata()
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
@@ -292,6 +353,7 @@ impl Backend {
|
||||
reply_surbs: &ReceivedReplySurbsMap,
|
||||
) -> Result<(), StorageError> {
|
||||
self.manager
|
||||
.get()
|
||||
.insert_reply_surb_storage_metadata(ReplySurbStorageMetadata::new(
|
||||
reply_surbs.min_surb_threshold(),
|
||||
reply_surbs.max_surb_threshold(),
|
||||
@@ -305,6 +367,10 @@ impl Backend {
|
||||
impl ReplyStorageBackend for Backend {
|
||||
type StorageError = error::StorageError;
|
||||
|
||||
fn is_active(&self) -> bool {
|
||||
self.manager.is_active()
|
||||
}
|
||||
|
||||
async fn start_storage_session(&self) -> Result<(), Self::StorageError> {
|
||||
self.start_client_use().await
|
||||
}
|
||||
@@ -342,6 +408,18 @@ impl ReplyStorageBackend for Backend {
|
||||
Ok(CombinedReplyStorage::load(reply_keys, reply_surbs, tags))
|
||||
}
|
||||
|
||||
fn get_inactive_storage(&self) -> Result<CombinedReplyStorage, Self::StorageError> {
|
||||
match self.manager {
|
||||
StorageManagerState::Storage(_) => {
|
||||
panic!("tried to get inactive storage from an active storage backend")
|
||||
}
|
||||
StorageManagerState::Inactive(ref state) => Ok(CombinedReplyStorage::new(
|
||||
state.minimum_reply_surb_storage_threshold,
|
||||
state.maximum_reply_surb_storage_threshold,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
async fn stop_storage_session(self) -> Result<(), Self::StorageError> {
|
||||
self.stop_client_use().await
|
||||
}
|
||||
|
||||
@@ -44,8 +44,7 @@ impl TryFrom<StoredSenderTag> for (RecipientBytes, AnonymousSenderTag) {
|
||||
let Ok(sender_tag_bytes) = value.tag.try_into() else {
|
||||
return Err(StorageError::CorruptedData {
|
||||
details: format!(
|
||||
"the retrieved sender tag has length of {tag_len} while {} was expected",
|
||||
SENDER_TAG_SIZE
|
||||
"the retrieved sender tag has length of {tag_len} while {SENDER_TAG_SIZE} was expected",
|
||||
),
|
||||
});
|
||||
};
|
||||
@@ -132,8 +131,7 @@ impl TryFrom<StoredSurbSender> for (AnonymousSenderTag, i64) {
|
||||
let Ok(sender_tag_bytes) = value.tag.try_into() else {
|
||||
return Err(StorageError::CorruptedData {
|
||||
details: format!(
|
||||
"the retrieved sender tag has length of {tag_len} while {} was expected",
|
||||
SENDER_TAG_SIZE
|
||||
"the retrieved sender tag has length of {tag_len} while {SENDER_TAG_SIZE} was expected",
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -19,10 +19,11 @@ pub mod fs_backend;
|
||||
#[error("no information provided")]
|
||||
pub struct UndefinedError;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Empty {
|
||||
// we need to keep 'basic' metadata here to "load" the CombinedReplyStorage
|
||||
min_surb_threshold: usize,
|
||||
max_surb_threshold: usize,
|
||||
pub min_surb_threshold: usize,
|
||||
pub max_surb_threshold: usize,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -49,12 +50,23 @@ impl ReplyStorageBackend for Empty {
|
||||
self.max_surb_threshold,
|
||||
))
|
||||
}
|
||||
|
||||
fn get_inactive_storage(&self) -> Result<CombinedReplyStorage, Self::StorageError> {
|
||||
Ok(CombinedReplyStorage::new(
|
||||
self.min_surb_threshold,
|
||||
self.max_surb_threshold,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait ReplyStorageBackend: Sized {
|
||||
type StorageError: Error + 'static;
|
||||
|
||||
fn is_active(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
async fn start_storage_session(&self) -> Result<(), Self::StorageError> {
|
||||
Ok(())
|
||||
}
|
||||
@@ -72,6 +84,11 @@ pub trait ReplyStorageBackend: Sized {
|
||||
|
||||
async fn load_surb_storage(&self) -> Result<CombinedReplyStorage, Self::StorageError>;
|
||||
|
||||
/// In the case the storage backend is initialized in an inactive state (persisting data is
|
||||
/// disabled), we might still need to fetch the (in-mem) storage and the parameters it was
|
||||
/// created with.
|
||||
fn get_inactive_storage(&self) -> Result<CombinedReplyStorage, Self::StorageError>;
|
||||
|
||||
async fn stop_storage_session(self) -> Result<(), Self::StorageError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ impl TopologyRefresherConfig {
|
||||
}
|
||||
|
||||
pub struct TopologyRefresher {
|
||||
validator_client: validator_client::client::ApiClient,
|
||||
validator_client: validator_client::client::NymApiClient,
|
||||
client_version: String,
|
||||
|
||||
nym_api_urls: Vec<Url>,
|
||||
@@ -168,7 +168,9 @@ impl TopologyRefresher {
|
||||
cfg.nym_api_urls.shuffle(&mut thread_rng());
|
||||
|
||||
TopologyRefresher {
|
||||
validator_client: validator_client::client::ApiClient::new(cfg.nym_api_urls[0].clone()),
|
||||
validator_client: validator_client::client::NymApiClient::new(
|
||||
cfg.nym_api_urls[0].clone(),
|
||||
),
|
||||
client_version: cfg.client_version,
|
||||
nym_api_urls: cfg.nym_api_urls,
|
||||
topology_accessor,
|
||||
|
||||
+118
-48
@@ -1,38 +1,57 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::helpers::{get_time_now, Instant};
|
||||
use crate::client::real_messages_control::real_traffic_stream::RealMessage;
|
||||
use client_connections::TransmissionLane;
|
||||
use nymsphinx::chunking::fragment::Fragment;
|
||||
use rand::{seq::SliceRandom, Rng};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet, VecDeque},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio::time;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_timer;
|
||||
|
||||
use super::{get_time_now, RealMessage};
|
||||
|
||||
// The number of lanes included in the oldest set. Used when we need to prioritize traffic.
|
||||
const OLDEST_LANE_SET_SIZE: usize = 4;
|
||||
// As a way of prune connections we also check for timeouts.
|
||||
const MSG_CONSIDERED_STALE_AFTER_SECS: u64 = 10 * 60;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct TransmissionBuffer {
|
||||
buffer: HashMap<TransmissionLane, LaneBufferEntry>,
|
||||
pub(crate) trait SizedData {
|
||||
fn data_size(&self) -> usize;
|
||||
}
|
||||
|
||||
impl TransmissionBuffer {
|
||||
impl SizedData for RealMessage {
|
||||
fn data_size(&self) -> usize {
|
||||
self.packet_size()
|
||||
}
|
||||
}
|
||||
|
||||
impl SizedData for Fragment {
|
||||
fn data_size(&self) -> usize {
|
||||
// note that raw `Fragment` is smaller than sphinx packet payload
|
||||
// as it doesn't include surb-ack or the [shared] key materials
|
||||
self.payload_size()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct TransmissionBuffer<T> {
|
||||
buffer: HashMap<TransmissionLane, LaneBufferEntry<T>>,
|
||||
}
|
||||
|
||||
impl<T> TransmissionBuffer<T> {
|
||||
pub(crate) fn new() -> Self {
|
||||
TransmissionBuffer {
|
||||
buffer: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn is_empty(&self) -> bool {
|
||||
self.buffer.is_empty()
|
||||
}
|
||||
|
||||
pub(crate) fn remove(&mut self, lane: &TransmissionLane) -> Option<LaneBufferEntry> {
|
||||
pub(crate) fn remove(&mut self, lane: &TransmissionLane) -> Option<LaneBufferEntry<T>> {
|
||||
self.buffer.remove(lane)
|
||||
}
|
||||
|
||||
@@ -57,20 +76,22 @@ impl TransmissionBuffer {
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(crate) fn total_size(&self) -> usize {
|
||||
self.buffer.values().map(LaneBufferEntry::len).sum()
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(crate) fn total_size_in_bytes(&self) -> usize {
|
||||
pub(crate) fn total_size_in_bytes(&self) -> usize
|
||||
where
|
||||
T: SizedData,
|
||||
{
|
||||
self.buffer
|
||||
.values()
|
||||
.map(|lane_buffer_entry| {
|
||||
lane_buffer_entry
|
||||
.real_messages
|
||||
.items
|
||||
.iter()
|
||||
.map(|real_message| real_message.mix_packet.sphinx_packet().len())
|
||||
.map(|item| item.data_size())
|
||||
.sum::<usize>()
|
||||
})
|
||||
.sum()
|
||||
@@ -92,42 +113,51 @@ impl TransmissionBuffer {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn store(&mut self, lane: &TransmissionLane, real_messages: Vec<RealMessage>) {
|
||||
pub(crate) fn store<I: IntoIterator<Item = T>>(&mut self, lane: &TransmissionLane, items: I) {
|
||||
if let Some(lane_buffer_entry) = self.buffer.get_mut(lane) {
|
||||
lane_buffer_entry.append(real_messages);
|
||||
lane_buffer_entry.extend(items);
|
||||
} else {
|
||||
self.buffer
|
||||
.insert(*lane, LaneBufferEntry::new(real_messages));
|
||||
.insert(*lane, LaneBufferEntry::new(items.into_iter().collect()));
|
||||
}
|
||||
}
|
||||
|
||||
fn pick_random_lane(&self) -> Option<&TransmissionLane> {
|
||||
let lanes: Vec<&TransmissionLane> = self.buffer.keys().collect();
|
||||
lanes.choose(&mut rand::thread_rng()).copied()
|
||||
pub(crate) fn store_multiple(&mut self, items: Vec<(TransmissionLane, T)>) {
|
||||
for (lane, item) in items {
|
||||
self.buffer
|
||||
.entry(lane)
|
||||
.or_insert_with(LaneBufferEntry::new_empty)
|
||||
.push_item(item)
|
||||
}
|
||||
}
|
||||
|
||||
fn pick_random_small_lane(&self) -> Option<&TransmissionLane> {
|
||||
fn pick_random_lane<R: Rng + ?Sized>(&self, rng: &mut R) -> Option<&TransmissionLane> {
|
||||
let lanes: Vec<&TransmissionLane> = self.buffer.keys().collect();
|
||||
lanes.choose(rng).copied()
|
||||
}
|
||||
|
||||
fn pick_random_small_lane<R: Rng + ?Sized>(&self, rng: &mut R) -> Option<&TransmissionLane> {
|
||||
let lanes: Vec<&TransmissionLane> = self
|
||||
.buffer
|
||||
.iter()
|
||||
.filter(|(_, v)| v.is_small())
|
||||
.map(|(k, _)| k)
|
||||
.collect();
|
||||
lanes.choose(&mut rand::thread_rng()).copied()
|
||||
lanes.choose(rng).copied()
|
||||
}
|
||||
|
||||
// 2/3 chance to pick from the old lanes
|
||||
fn pick_random_old_lane(&self) -> Option<TransmissionLane> {
|
||||
fn pick_random_old_lane<R: Rng + ?Sized>(&self, rng: &mut R) -> Option<TransmissionLane> {
|
||||
let rand = &mut rand::thread_rng();
|
||||
if rand.gen_ratio(2, 3) {
|
||||
let lanes = self.get_oldest_set();
|
||||
lanes.choose(rand).copied()
|
||||
} else {
|
||||
self.pick_random_lane().copied()
|
||||
self.pick_random_lane(rng).copied()
|
||||
}
|
||||
}
|
||||
|
||||
fn pop_front_from_lane(&mut self, lane: &TransmissionLane) -> Option<RealMessage> {
|
||||
fn pop_front_from_lane(&mut self, lane: &TransmissionLane) -> Option<T> {
|
||||
let real_msgs_queued = self.buffer.get_mut(lane)?;
|
||||
let real_next = real_msgs_queued.pop_front()?;
|
||||
real_msgs_queued.messages_transmitted += 1;
|
||||
@@ -137,19 +167,48 @@ impl TransmissionBuffer {
|
||||
Some(real_next)
|
||||
}
|
||||
|
||||
pub(crate) fn pop_next_message_at_random(&mut self) -> Option<(TransmissionLane, RealMessage)> {
|
||||
pub(crate) fn pop_at_most_n_next_messages_at_random(
|
||||
&mut self,
|
||||
n: usize,
|
||||
) -> Option<Vec<(TransmissionLane, T)>> {
|
||||
// let start = Instant::now();
|
||||
|
||||
if self.buffer.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let rng = &mut rand::thread_rng();
|
||||
let mut items = Vec::with_capacity(n);
|
||||
|
||||
while items.len() < n {
|
||||
let Some(next) = self.pop_next_message_at_random(rng) else {
|
||||
break
|
||||
};
|
||||
items.push(next)
|
||||
}
|
||||
|
||||
// todo!("time time taken");
|
||||
|
||||
Some(items)
|
||||
}
|
||||
|
||||
pub(crate) fn pop_next_message_at_random<R: Rng + ?Sized>(
|
||||
&mut self,
|
||||
// turns out the caller always have access to some rng, so no point in instantiating new one
|
||||
rng: &mut R,
|
||||
) -> Option<(TransmissionLane, T)> {
|
||||
if self.buffer.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Very basic heuristic where we prioritize according to small lanes first, the older lanes
|
||||
// to try to finish lanes when possible, then the rest.
|
||||
let lane = if let Some(small_lane) = self.pick_random_small_lane() {
|
||||
let lane = if let Some(small_lane) = self.pick_random_small_lane(rng) {
|
||||
*small_lane
|
||||
} else if let Some(old_lane) = self.pick_random_old_lane() {
|
||||
} else if let Some(old_lane) = self.pick_random_old_lane(rng) {
|
||||
old_lane
|
||||
} else {
|
||||
*self.pick_random_lane()?
|
||||
*self.pick_random_lane(rng)?
|
||||
};
|
||||
|
||||
let msg = self.pop_front_from_lane(&lane)?;
|
||||
@@ -171,35 +230,46 @@ impl TransmissionBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct LaneBufferEntry {
|
||||
pub real_messages: VecDeque<RealMessage>,
|
||||
pub(crate) struct LaneBufferEntry<T> {
|
||||
pub items: VecDeque<T>,
|
||||
pub messages_transmitted: usize,
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub time_for_last_activity: time::Instant,
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub time_for_last_activity: wasm_timer::Instant,
|
||||
pub time_for_last_activity: Instant,
|
||||
}
|
||||
|
||||
impl LaneBufferEntry {
|
||||
fn new(real_messages: Vec<RealMessage>) -> Self {
|
||||
impl<T> LaneBufferEntry<T> {
|
||||
fn new_empty() -> Self {
|
||||
LaneBufferEntry {
|
||||
real_messages: real_messages.into(),
|
||||
items: VecDeque::new(),
|
||||
messages_transmitted: 0,
|
||||
time_for_last_activity: get_time_now(),
|
||||
}
|
||||
}
|
||||
|
||||
fn append(&mut self, real_messages: Vec<RealMessage>) {
|
||||
self.real_messages.append(&mut real_messages.into());
|
||||
fn new(items: VecDeque<T>) -> Self {
|
||||
LaneBufferEntry {
|
||||
items,
|
||||
messages_transmitted: 0,
|
||||
time_for_last_activity: get_time_now(),
|
||||
}
|
||||
}
|
||||
|
||||
fn push_item(&mut self, item: T) {
|
||||
self.items.push_back(item);
|
||||
// I'm not updating time here on purpose. This method is called just after `new_empty`,
|
||||
// where the time is already set. Furthermore, this method is called there multiple times at once
|
||||
}
|
||||
|
||||
fn extend<I: IntoIterator<Item = T>>(&mut self, items: I) {
|
||||
self.items.extend(items);
|
||||
self.time_for_last_activity = get_time_now();
|
||||
}
|
||||
|
||||
fn pop_front(&mut self) -> Option<RealMessage> {
|
||||
self.real_messages.pop_front()
|
||||
fn pop_front(&mut self) -> Option<T> {
|
||||
self.items.pop_front()
|
||||
}
|
||||
|
||||
fn is_small(&self) -> bool {
|
||||
self.real_messages.len() < 100
|
||||
self.items.len() < 100
|
||||
}
|
||||
|
||||
fn is_stale(&self) -> bool {
|
||||
@@ -208,10 +278,10 @@ impl LaneBufferEntry {
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.real_messages.len()
|
||||
self.items.len()
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.real_messages.is_empty()
|
||||
self.items.is_empty()
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use config::{NymConfig, DB_FILE_NAME};
|
||||
use config::defaults::NymNetworkDetails;
|
||||
use config::{NymConfig, OptionalSet, DB_FILE_NAME};
|
||||
use nymsphinx::params::PacketSize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::marker::PhantomData;
|
||||
@@ -70,12 +71,15 @@ pub struct Config<T> {
|
||||
#[serde(default)]
|
||||
debug: DebugConfig,
|
||||
}
|
||||
|
||||
impl<T> ClientCoreConfigTrait for Config<T> {
|
||||
fn get_gateway_endpoint(&self) -> &GatewayEndpointConfig {
|
||||
&self.client.gateway_endpoint
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> OptionalSet for Config<T> where T: NymConfig {}
|
||||
|
||||
impl<T> Config<T> {
|
||||
pub fn new<S: Into<String>>(id: S) -> Self
|
||||
where
|
||||
@@ -84,6 +88,7 @@ impl<T> Config<T> {
|
||||
Config::default().with_id(id)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_id<S: Into<String>>(mut self, id: S) -> Self
|
||||
where
|
||||
T: NymConfig,
|
||||
@@ -160,8 +165,9 @@ impl<T> Config<T> {
|
||||
changes_made
|
||||
}
|
||||
|
||||
pub fn with_disabled_credentials(&mut self, disabled_credentials_mode: bool) {
|
||||
pub fn with_disabled_credentials(mut self, disabled_credentials_mode: bool) -> Self {
|
||||
self.client.disabled_credentials_mode = disabled_credentials_mode;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_gateway_endpoint(&mut self, gateway_endpoint: GatewayEndpointConfig) {
|
||||
@@ -172,14 +178,31 @@ 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 with_custom_nyxd(mut self, urls: Vec<Url>) -> Self {
|
||||
self.client.nyxd_urls = urls;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_custom_nyxd(&mut self, nyxd_urls: Vec<Url>) {
|
||||
self.client.nyxd_urls = nyxd_urls;
|
||||
}
|
||||
|
||||
pub fn with_custom_nym_apis(mut self, nym_api_urls: Vec<Url>) -> Self {
|
||||
self.client.nym_api_urls = nym_api_urls;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_custom_nym_apis(&mut self, nym_api_urls: Vec<Url>) {
|
||||
self.client.nym_api_urls = nym_api_urls;
|
||||
}
|
||||
|
||||
pub fn with_high_default_traffic_volume(mut self, enabled: bool) -> Self {
|
||||
if enabled {
|
||||
self.set_high_default_traffic_volume();
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_high_default_traffic_volume(&mut self) {
|
||||
self.debug.average_packet_delay = Duration::from_millis(10);
|
||||
// basically don't really send cover messages
|
||||
@@ -188,6 +211,13 @@ impl<T> Config<T> {
|
||||
self.debug.message_sending_average_delay = Duration::from_millis(4);
|
||||
}
|
||||
|
||||
pub fn with_disabled_cover_traffic(mut self, disabled: bool) -> Self {
|
||||
if disabled {
|
||||
self.set_no_cover_traffic()
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_no_cover_traffic(&mut self) {
|
||||
self.debug.disable_loop_cover_traffic_stream = true;
|
||||
self.debug.disable_main_poisson_packet_distribution = true;
|
||||
@@ -234,7 +264,7 @@ impl<T> Config<T> {
|
||||
}
|
||||
|
||||
pub fn get_validator_endpoints(&self) -> Vec<Url> {
|
||||
self.client.validator_urls.clone()
|
||||
self.client.nyxd_urls.clone()
|
||||
}
|
||||
|
||||
pub fn get_nym_api_endpoints(&self) -> Vec<Url> {
|
||||
@@ -420,9 +450,9 @@ 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 nyxd validators via which the client can communicate with the chain.
|
||||
#[serde(alias = "validator_urls")]
|
||||
nyxd_urls: Vec<Url>,
|
||||
|
||||
/// Addresses to APIs running on validator from which the client gets the view of the network.
|
||||
#[serde(alias = "validator_api_urls")]
|
||||
@@ -471,13 +501,29 @@ pub struct Client<T> {
|
||||
|
||||
impl<T: NymConfig> Default for Client<T> {
|
||||
fn default() -> Self {
|
||||
let network = NymNetworkDetails::new_mainnet();
|
||||
let nyxd_urls = network
|
||||
.endpoints
|
||||
.iter()
|
||||
.map(|validator| validator.nyxd_url())
|
||||
.collect();
|
||||
let nym_api_urls = network
|
||||
.endpoints
|
||||
.iter()
|
||||
.filter_map(|validator| validator.api_url())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if nym_api_urls.is_empty() {
|
||||
panic!("we do not have any default nym-api urls available!")
|
||||
}
|
||||
|
||||
// there must be explicit checks for whether id is not empty later
|
||||
Client {
|
||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
id: "".to_string(),
|
||||
disabled_credentials_mode: true,
|
||||
validator_urls: vec![],
|
||||
nym_api_urls: vec![],
|
||||
nyxd_urls,
|
||||
nym_api_urls,
|
||||
private_identity_key_file: Default::default(),
|
||||
public_identity_key_file: Default::default(),
|
||||
private_encryption_key_file: Default::default(),
|
||||
|
||||
@@ -7,12 +7,12 @@ use std::path::{Path, PathBuf};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ClientKeyPathfinder {
|
||||
identity_private_key: PathBuf,
|
||||
identity_public_key: PathBuf,
|
||||
encryption_private_key: PathBuf,
|
||||
encryption_public_key: PathBuf,
|
||||
gateway_shared_key: PathBuf,
|
||||
ack_key: PathBuf,
|
||||
pub identity_private_key: PathBuf,
|
||||
pub identity_public_key: PathBuf,
|
||||
pub encryption_private_key: PathBuf,
|
||||
pub encryption_public_key: PathBuf,
|
||||
pub gateway_shared_key: PathBuf,
|
||||
pub ack_key: PathBuf,
|
||||
}
|
||||
|
||||
impl ClientKeyPathfinder {
|
||||
@@ -22,8 +22,8 @@ impl ClientKeyPathfinder {
|
||||
ClientKeyPathfinder {
|
||||
identity_private_key: config_dir.join("private_identity.pem"),
|
||||
identity_public_key: config_dir.join("public_identity.pem"),
|
||||
encryption_private_key: config_dir.join("public_encryption.pem"),
|
||||
encryption_public_key: config_dir.join("private_encryption.pem"),
|
||||
encryption_private_key: config_dir.join("private_encryption.pem"),
|
||||
encryption_public_key: config_dir.join("public_encryption.pem"),
|
||||
gateway_shared_key: config_dir.join("gateway_shared.pem"),
|
||||
ack_key: config_dir.join("ack_key.pem"),
|
||||
}
|
||||
@@ -40,6 +40,28 @@ impl ClientKeyPathfinder {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn any_file_exists(&self) -> bool {
|
||||
matches!(self.identity_public_key.try_exists(), Ok(true))
|
||||
|| matches!(self.identity_private_key.try_exists(), Ok(true))
|
||||
|| matches!(self.encryption_public_key.try_exists(), Ok(true))
|
||||
|| matches!(self.encryption_private_key.try_exists(), Ok(true))
|
||||
|| matches!(self.gateway_shared_key.try_exists(), Ok(true))
|
||||
|| matches!(self.ack_key.try_exists(), Ok(true))
|
||||
}
|
||||
|
||||
pub fn any_file_exists_and_return(&self) -> Option<PathBuf> {
|
||||
file_exists(&self.identity_public_key)
|
||||
.or_else(|| file_exists(&self.identity_private_key))
|
||||
.or_else(|| file_exists(&self.encryption_public_key))
|
||||
.or_else(|| file_exists(&self.encryption_private_key))
|
||||
.or_else(|| file_exists(&self.gateway_shared_key))
|
||||
.or_else(|| file_exists(&self.ack_key))
|
||||
}
|
||||
|
||||
pub fn gateway_key_file_exists(&self) -> bool {
|
||||
matches!(self.gateway_shared_key.try_exists(), Ok(true))
|
||||
}
|
||||
|
||||
pub fn private_identity_key(&self) -> &Path {
|
||||
&self.identity_private_key
|
||||
}
|
||||
@@ -64,3 +86,10 @@ impl ClientKeyPathfinder {
|
||||
&self.ack_key
|
||||
}
|
||||
}
|
||||
|
||||
fn file_exists(path: &Path) -> Option<PathBuf> {
|
||||
if matches!(path.try_exists(), Ok(true)) {
|
||||
return Some(path.to_path_buf());
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::replies::reply_storage::ReplyStorageBackend;
|
||||
use crypto::asymmetric::identity::Ed25519RecoveryError;
|
||||
use gateway_client::error::GatewayClientError;
|
||||
use topology::NymTopologyError;
|
||||
use validator_client::ValidatorClientError;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum ClientCoreError<B: ReplyStorageBackend> {
|
||||
pub enum ClientCoreError {
|
||||
#[error("I/O error: {0}")]
|
||||
IoError(#[from] std::io::Error),
|
||||
|
||||
@@ -40,7 +39,9 @@ pub enum ClientCoreError<B: ReplyStorageBackend> {
|
||||
InsufficientNetworkTopology(#[from] NymTopologyError),
|
||||
|
||||
#[error("experienced a failure with our reply surb persistent storage: {source}")]
|
||||
SurbStorageError { source: B::StorageError },
|
||||
SurbStorageError {
|
||||
source: Box<dyn std::error::Error + Send + Sync>,
|
||||
},
|
||||
|
||||
#[error("The gateway id is invalid - {0}")]
|
||||
UnableToCreatePublicKeyFromGatewayId(Ed25519RecoveryError),
|
||||
@@ -54,6 +55,9 @@ pub enum ClientCoreError<B: ReplyStorageBackend> {
|
||||
#[error("The address of the gateway is unknown - did you run init?")]
|
||||
GatwayAddressUnknown,
|
||||
|
||||
#[error("failed to register receiver for reconstructed mixnet messages")]
|
||||
FailedToRegisterReceiver,
|
||||
|
||||
#[error("Unexpected exit")]
|
||||
UnexpectedExit,
|
||||
}
|
||||
@@ -61,8 +65,10 @@ pub enum ClientCoreError<B: ReplyStorageBackend> {
|
||||
/// Set of messages that the client can send to listeners via the task manager
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum ClientCoreStatusMessage {
|
||||
// NOTE: The nym-connect frontend listens for these strings, so don't change them until we have a more robust mechanism in place
|
||||
#[error("The connected gateway is slow, or the connection to it is slow")]
|
||||
GatewayIsSlow,
|
||||
// NOTE: The nym-connect frontend listens for these strings, so don't change them until we have a more robust mechanism in place
|
||||
#[error("The connected gateway is very slow, or the connection to it is very slow")]
|
||||
GatewayIsVerySlow,
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::replies::reply_storage::ReplyStorageBackend;
|
||||
use crate::{
|
||||
client::key_manager::KeyManager,
|
||||
config::{persistence::key_pathfinder::ClientKeyPathfinder, Config},
|
||||
@@ -9,25 +8,26 @@ use crate::{
|
||||
};
|
||||
use config::NymConfig;
|
||||
use crypto::asymmetric::identity;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use gateway_client::wasm_mockups::SigningNyxdClient;
|
||||
use gateway_client::GatewayClient;
|
||||
use gateway_requests::registration::handshake::SharedKeys;
|
||||
use rand::{rngs::OsRng, seq::SliceRandom, thread_rng};
|
||||
use rand::{seq::SliceRandom, thread_rng};
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use tap::TapFallible;
|
||||
use topology::{filter::VersionFilterable, gateway};
|
||||
use url::Url;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use validator_client::nyxd::SigningNyxdClient;
|
||||
|
||||
pub(super) async fn query_gateway_details<B>(
|
||||
pub(super) async fn query_gateway_details(
|
||||
validator_servers: Vec<Url>,
|
||||
chosen_gateway_id: Option<String>,
|
||||
) -> Result<gateway::Node, ClientCoreError<B>>
|
||||
where
|
||||
B: ReplyStorageBackend,
|
||||
{
|
||||
chosen_gateway_id: Option<identity::PublicKey>,
|
||||
) -> Result<gateway::Node, ClientCoreError> {
|
||||
let nym_api = validator_servers
|
||||
.choose(&mut thread_rng())
|
||||
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
|
||||
let validator_client = validator_client::client::ApiClient::new(nym_api.clone());
|
||||
let validator_client = validator_client::client::NymApiClient::new(nym_api.clone());
|
||||
|
||||
log::trace!("Fetching list of gateways from: {}", nym_api);
|
||||
let gateways = validator_client.get_cached_gateways().await?;
|
||||
@@ -44,7 +44,7 @@ where
|
||||
if let Some(gateway_id) = chosen_gateway_id {
|
||||
filtered_gateways
|
||||
.iter()
|
||||
.find(|gateway| gateway.identity_key.to_base58_string() == gateway_id)
|
||||
.find(|gateway| gateway.identity_key == gateway_id)
|
||||
.ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_id.to_string()))
|
||||
.cloned()
|
||||
} else {
|
||||
@@ -55,15 +55,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
async fn register_with_gateway<B>(
|
||||
pub(super) async fn register_with_gateway(
|
||||
gateway: &gateway::Node,
|
||||
our_identity: Arc<identity::KeyPair>,
|
||||
) -> Result<Arc<SharedKeys>, ClientCoreError<B>>
|
||||
where
|
||||
B: ReplyStorageBackend,
|
||||
{
|
||||
) -> Result<Arc<SharedKeys>, ClientCoreError> {
|
||||
let timeout = Duration::from_millis(1500);
|
||||
let mut gateway_client = GatewayClient::new_init(
|
||||
let mut gateway_client: GatewayClient<SigningNyxdClient> = GatewayClient::new_init(
|
||||
gateway.clients_address(),
|
||||
gateway.identity_key,
|
||||
gateway.owner.clone(),
|
||||
@@ -81,21 +78,13 @@ where
|
||||
Ok(shared_keys)
|
||||
}
|
||||
|
||||
pub(super) async fn register_with_gateway_and_store_keys<T, B>(
|
||||
gateway_details: gateway::Node,
|
||||
pub(super) fn store_keys<T>(
|
||||
key_manager: &KeyManager,
|
||||
config: &Config<T>,
|
||||
) -> Result<(), ClientCoreError<B>>
|
||||
) -> Result<(), ClientCoreError>
|
||||
where
|
||||
T: NymConfig,
|
||||
B: ReplyStorageBackend,
|
||||
{
|
||||
let mut rng = OsRng;
|
||||
let mut key_manager = KeyManager::new(&mut rng);
|
||||
|
||||
let shared_keys =
|
||||
register_with_gateway(&gateway_details, key_manager.identity_keypair()).await?;
|
||||
key_manager.insert_gateway_shared_key(shared_keys);
|
||||
|
||||
let pathfinder = ClientKeyPathfinder::new_from_config(config);
|
||||
Ok(key_manager
|
||||
.store_keys(&pathfinder)
|
||||
|
||||
@@ -6,24 +6,26 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use nymsphinx::addressing::{clients::Recipient, nodes::NodeIdentity};
|
||||
use rand::rngs::OsRng;
|
||||
use serde::Serialize;
|
||||
use tap::TapFallible;
|
||||
|
||||
use config::NymConfig;
|
||||
use crypto::asymmetric::{encryption, identity};
|
||||
use url::Url;
|
||||
|
||||
use crate::client::replies::reply_storage::ReplyStorageBackend;
|
||||
use crate::client::key_manager::KeyManager;
|
||||
use crate::{
|
||||
config::{
|
||||
persistence::key_pathfinder::ClientKeyPathfinder, ClientCoreConfigTrait, Config,
|
||||
GatewayEndpointConfig,
|
||||
},
|
||||
error::ClientCoreError,
|
||||
init::helpers::{query_gateway_details, register_with_gateway_and_store_keys},
|
||||
};
|
||||
|
||||
mod helpers;
|
||||
|
||||
/// Struct describing the results of the client initialization procedure.
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct InitResults {
|
||||
version: String,
|
||||
@@ -61,80 +63,92 @@ impl Display for InitResults {
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience function for setting up the gateway for a client. Depending on the arguments given
|
||||
/// it will do the sensible thing.
|
||||
pub async fn setup_gateway<B, C, T>(
|
||||
/// Create a new set of client keys.
|
||||
pub fn new_client_keys() -> KeyManager {
|
||||
let mut rng = OsRng;
|
||||
KeyManager::new(&mut rng)
|
||||
}
|
||||
|
||||
/// Authenticate and register with a gateway.
|
||||
/// Either pick one at random by querying the available gateways from the nym-api, or use the
|
||||
/// chosen one if it's among the available ones.
|
||||
/// The shared key is added to the supplied `KeyManager` and the endpoint details are returned.
|
||||
pub async fn register_with_gateway(
|
||||
key_manager: &mut KeyManager,
|
||||
nym_api_endpoints: Vec<Url>,
|
||||
chosen_gateway_id: Option<identity::PublicKey>,
|
||||
) -> Result<GatewayEndpointConfig, ClientCoreError> {
|
||||
// Get the gateway details of the gateway we will use
|
||||
let gateway = helpers::query_gateway_details(nym_api_endpoints, chosen_gateway_id).await?;
|
||||
log::debug!("Querying gateway gives: {}", gateway);
|
||||
|
||||
let our_identity = key_manager.identity_keypair();
|
||||
|
||||
// Establish connection, authenticate and generate keys for talking with the gateway
|
||||
let shared_keys = helpers::register_with_gateway(&gateway, our_identity).await?;
|
||||
key_manager.insert_gateway_shared_key(shared_keys);
|
||||
|
||||
Ok(gateway.into())
|
||||
}
|
||||
|
||||
/// Convenience function for setting up the gateway for a client given a `Config`. Depending on the
|
||||
/// arguments given it will do the sensible thing. Either it will
|
||||
///
|
||||
/// a. Reuse existing gateway configuration from storage.
|
||||
/// b. Create a new gateway configuration but keep existing keys. This assumes that the caller
|
||||
/// knows what they are doing and that the keys match the requested gateway.
|
||||
/// c. Create a new gateway configuration with a newly registered gateway and keys.
|
||||
pub async fn setup_gateway_from_config<C, T>(
|
||||
register_gateway: bool,
|
||||
user_chosen_gateway_id: Option<String>,
|
||||
user_chosen_gateway_id: Option<identity::PublicKey>,
|
||||
config: &Config<T>,
|
||||
) -> Result<GatewayEndpointConfig, ClientCoreError<B>>
|
||||
) -> Result<GatewayEndpointConfig, ClientCoreError>
|
||||
where
|
||||
B: ReplyStorageBackend,
|
||||
C: NymConfig + ClientCoreConfigTrait,
|
||||
T: NymConfig,
|
||||
{
|
||||
let id = config.get_id();
|
||||
if register_gateway {
|
||||
register_with_gateway(user_chosen_gateway_id, config).await
|
||||
} else if let Some(user_chosen_gateway_id) = user_chosen_gateway_id {
|
||||
config_gateway_with_existing_keys(user_chosen_gateway_id, config).await
|
||||
} else {
|
||||
reuse_existing_gateway_config::<B, C>(&id)
|
||||
|
||||
// If we are not going to register gateway, and an explicitly chosed gateway is not passed in,
|
||||
// load the existing configuration file
|
||||
if !register_gateway && user_chosen_gateway_id.is_none() {
|
||||
println!("Not registering gateway, will reuse existing config and keys");
|
||||
return load_existing_gateway_config::<C>(&id);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
/// Saves keys to disk, specified by the paths in `config`.
|
||||
pub async fn register_with_gateway<B, T>(
|
||||
user_chosen_gateway_id: Option<String>,
|
||||
config: &Config<T>,
|
||||
) -> Result<GatewayEndpointConfig, ClientCoreError<B>>
|
||||
where
|
||||
B: ReplyStorageBackend,
|
||||
T: NymConfig,
|
||||
{
|
||||
println!("Configuring gateway");
|
||||
// Else, we preceed by querying the nym-api
|
||||
let gateway =
|
||||
query_gateway_details(config.get_nym_api_endpoints(), user_chosen_gateway_id).await?;
|
||||
helpers::query_gateway_details(config.get_nym_api_endpoints(), user_chosen_gateway_id)
|
||||
.await?;
|
||||
log::debug!("Querying gateway gives: {}", gateway);
|
||||
|
||||
// Registering with gateway by setting up and writing shared keys to disk
|
||||
log::trace!("Registering gateway");
|
||||
register_with_gateway_and_store_keys(gateway.clone(), config).await?;
|
||||
println!("Saved all generated keys");
|
||||
// If we are not registering, just return this and assume the caller has the keys already and
|
||||
// wants to keep the,
|
||||
if !register_gateway && user_chosen_gateway_id.is_some() {
|
||||
println!("Using gateway provided by user, keeping existing keys");
|
||||
return Ok(gateway.into());
|
||||
}
|
||||
|
||||
Ok(gateway.into())
|
||||
}
|
||||
// Create new keys and derive our identity
|
||||
let mut key_manager = new_client_keys();
|
||||
let our_identity = key_manager.identity_keypair();
|
||||
|
||||
/// Set the gateway using the usual procedue of querying the validator-api, but don't register or
|
||||
/// create any keys.
|
||||
/// This assumes that the user knows what they are doing, and that the existing keys are valid for
|
||||
/// the gateway being used
|
||||
pub async fn config_gateway_with_existing_keys<B, T>(
|
||||
user_chosen_gateway_id: String,
|
||||
config: &Config<T>,
|
||||
) -> Result<GatewayEndpointConfig, ClientCoreError<B>>
|
||||
where
|
||||
B: ReplyStorageBackend,
|
||||
T: NymConfig,
|
||||
{
|
||||
println!("Using gateway provided by user, keeping existing keys");
|
||||
let gateway =
|
||||
query_gateway_details(config.get_nym_api_endpoints(), Some(user_chosen_gateway_id)).await?;
|
||||
log::debug!("Querying gateway gives: {}", gateway);
|
||||
// Establish connection, authenticate and generate keys for talking with the gateway
|
||||
println!("Registering with new gateway");
|
||||
let shared_keys = helpers::register_with_gateway(&gateway, our_identity).await?;
|
||||
key_manager.insert_gateway_shared_key(shared_keys);
|
||||
|
||||
// Write all keys to storage and just return the gateway endpoint config. It is assumed that we
|
||||
// will load keys from storage when actually connecting.
|
||||
helpers::store_keys(&key_manager, config)?;
|
||||
Ok(gateway.into())
|
||||
}
|
||||
|
||||
/// Read and reuse the existing gateway configuration from a file that was generate earlier.
|
||||
pub fn reuse_existing_gateway_config<B, T>(
|
||||
id: &str,
|
||||
) -> Result<GatewayEndpointConfig, ClientCoreError<B>>
|
||||
pub fn load_existing_gateway_config<T>(id: &str) -> Result<GatewayEndpointConfig, ClientCoreError>
|
||||
where
|
||||
B: ReplyStorageBackend,
|
||||
T: NymConfig + ClientCoreConfigTrait,
|
||||
{
|
||||
println!("Not registering gateway, will reuse existing config and keys");
|
||||
T::load_from_file(Some(id))
|
||||
.map(|existing_config| existing_config.get_gateway_endpoint().clone())
|
||||
.map_err(|err| {
|
||||
@@ -149,20 +163,30 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the full client address from the client keys and the gateway identity
|
||||
pub fn get_client_address(
|
||||
key_manager: &KeyManager,
|
||||
gateway_config: &GatewayEndpointConfig,
|
||||
) -> Recipient {
|
||||
Recipient::new(
|
||||
*key_manager.identity_keypair().public_key(),
|
||||
*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(&gateway_config.gateway_id).unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Get the client address by loading the keys from stored files.
|
||||
pub fn get_client_address_from_stored_keys<B, T>(
|
||||
pub fn get_client_address_from_stored_keys<T>(
|
||||
config: &Config<T>,
|
||||
) -> Result<Recipient, ClientCoreError<B>>
|
||||
) -> Result<Recipient, ClientCoreError>
|
||||
where
|
||||
T: config::NymConfig,
|
||||
B: ReplyStorageBackend,
|
||||
{
|
||||
fn load_identity_keys<B>(
|
||||
fn load_identity_keys(
|
||||
pathfinder: &ClientKeyPathfinder,
|
||||
) -> Result<identity::KeyPair, ClientCoreError<B>>
|
||||
where
|
||||
B: ReplyStorageBackend,
|
||||
{
|
||||
) -> Result<identity::KeyPair, ClientCoreError> {
|
||||
let identity_keypair: identity::KeyPair =
|
||||
pemstore::load_keypair(&pemstore::KeyPairPath::new(
|
||||
pathfinder.private_identity_key().to_owned(),
|
||||
@@ -172,12 +196,9 @@ where
|
||||
Ok(identity_keypair)
|
||||
}
|
||||
|
||||
fn load_sphinx_keys<B>(
|
||||
fn load_sphinx_keys(
|
||||
pathfinder: &ClientKeyPathfinder,
|
||||
) -> Result<encryption::KeyPair, ClientCoreError<B>>
|
||||
where
|
||||
B: ReplyStorageBackend,
|
||||
{
|
||||
) -> Result<encryption::KeyPair, ClientCoreError> {
|
||||
let sphinx_keypair: encryption::KeyPair =
|
||||
pemstore::load_keypair(&pemstore::KeyPairPath::new(
|
||||
pathfinder.private_encryption_key().to_owned(),
|
||||
@@ -205,9 +226,9 @@ where
|
||||
pub fn output_to_json<T: Serialize>(init_results: &T, output_file: &str) {
|
||||
match std::fs::File::create(output_file) {
|
||||
Ok(file) => match serde_json::to_writer_pretty(file, init_results) {
|
||||
Ok(_) => println!("Saved: {}", output_file),
|
||||
Err(err) => eprintln!("Could not save {}: {err}", output_file),
|
||||
Ok(_) => println!("Saved: {output_file}"),
|
||||
Err(err) => eprintln!("Could not save {output_file}: {err}"),
|
||||
},
|
||||
Err(err) => eprintln!("Could not save {}: {err}", output_file),
|
||||
Err(err) => eprintln!("Could not save {output_file}: {err}"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,12 +8,12 @@ edition = "2021"
|
||||
[dependencies]
|
||||
bip39 = "1.0.1"
|
||||
cfg-if = "0.1"
|
||||
clap = { version = "3.2", features = ["cargo", "derive"] }
|
||||
clap = { version = "4.0", features = ["cargo", "derive"] }
|
||||
rand = "0.7.3"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
thiserror = "1.0"
|
||||
url = "2.2"
|
||||
tokio = { version = "1.21.2", features = ["rt-multi-thread", "net", "signal", "macros"] } # async runtime
|
||||
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal", "macros"] } # async runtime
|
||||
|
||||
coconut-interface = { path = "../../common/coconut-interface" }
|
||||
config = { path = "../../common/config" }
|
||||
@@ -23,7 +23,7 @@ credential-storage = { path = "../../common/credential-storage" }
|
||||
crypto = { path = "../../common/crypto", features = ["rand", "asymmetric", "symmetric", "aes", "hashing"] }
|
||||
network-defaults = { path = "../../common/network-defaults" }
|
||||
pemstore = { path = "../../common/pemstore" }
|
||||
validator-client = { path = "../../common/client-libs/validator-client", features = ["nymd-client"] }
|
||||
validator-client = { path = "../../common/client-libs/validator-client", features = ["nyxd-client"] }
|
||||
|
||||
[features]
|
||||
coconut = ["credentials/coconut"]
|
||||
|
||||
@@ -6,27 +6,27 @@ use bip39::Mnemonic;
|
||||
use network_defaults::{NymNetworkDetails, VOUCHER_INFO};
|
||||
use std::str::FromStr;
|
||||
use url::Url;
|
||||
use validator_client::nymd;
|
||||
use validator_client::nymd::traits::CoconutBandwidthSigningClient;
|
||||
use validator_client::nymd::{Coin, Fee, NymdClient, SigningNymdClient};
|
||||
use validator_client::nyxd;
|
||||
use validator_client::nyxd::traits::CoconutBandwidthSigningClient;
|
||||
use validator_client::nyxd::{Coin, Fee, NyxdClient, SigningNyxdClient};
|
||||
|
||||
pub(crate) struct Client {
|
||||
nymd_client: NymdClient<SigningNymdClient>,
|
||||
nyxd_client: NyxdClient<SigningNyxdClient>,
|
||||
mix_denom_base: String,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
pub fn new(nymd_url: &str, mnemonic: &str) -> Self {
|
||||
let nymd_url = Url::from_str(nymd_url).unwrap();
|
||||
pub fn new(nyxd_url: &str, mnemonic: &str) -> Self {
|
||||
let nyxd_url = Url::from_str(nyxd_url).unwrap();
|
||||
let mnemonic = Mnemonic::from_str(mnemonic).unwrap();
|
||||
let network_details = NymNetworkDetails::new_from_env();
|
||||
let config = nymd::Config::try_from_nym_network_details(&network_details)
|
||||
let config = nyxd::Config::try_from_nym_network_details(&network_details)
|
||||
.expect("failed to construct valid validator client config with the provided network");
|
||||
let nymd_client =
|
||||
NymdClient::connect_with_mnemonic(config, nymd_url.as_ref(), mnemonic, None).unwrap();
|
||||
let nyxd_client =
|
||||
NyxdClient::connect_with_mnemonic(config, nyxd_url.as_ref(), mnemonic, None).unwrap();
|
||||
|
||||
Client {
|
||||
nymd_client,
|
||||
nyxd_client,
|
||||
mix_denom_base: network_details.chain_details.mix_denom.base,
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ impl Client {
|
||||
) -> Result<String> {
|
||||
let amount = Coin::new(amount as u128, self.mix_denom_base.clone());
|
||||
Ok(self
|
||||
.nymd_client
|
||||
.nyxd_client
|
||||
.deposit(
|
||||
amount,
|
||||
String::from(VOUCHER_INFO),
|
||||
|
||||
@@ -13,7 +13,8 @@ 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 validator_client::nymd::tx::Hash;
|
||||
use validator_client::nyxd::traits::DkgQueryClient;
|
||||
use validator_client::nyxd::tx::Hash;
|
||||
use validator_client::{CoconutApiClient, Config};
|
||||
|
||||
use crate::client::Client;
|
||||
@@ -38,9 +39,9 @@ pub(crate) struct Run {
|
||||
#[clap(long)]
|
||||
pub(crate) client_home_directory: std::path::PathBuf,
|
||||
|
||||
/// The nymd URL that should be used
|
||||
/// The nyxd URL that should be used
|
||||
#[clap(long)]
|
||||
pub(crate) nymd_url: String,
|
||||
pub(crate) nyxd_url: String,
|
||||
|
||||
/// A mnemonic for the account that buys the credential
|
||||
#[clap(long)]
|
||||
@@ -51,12 +52,12 @@ pub(crate) struct Run {
|
||||
pub(crate) amount: u64,
|
||||
}
|
||||
|
||||
pub(crate) async fn deposit(nymd_url: &str, mnemonic: &str, amount: u64) -> Result<State> {
|
||||
pub(crate) async fn deposit(nyxd_url: &str, mnemonic: &str, amount: u64) -> Result<State> {
|
||||
let mut rng = OsRng;
|
||||
let signing_keypair = KeyPair::from(identity::KeyPair::new(&mut rng));
|
||||
let encryption_keypair = KeyPair::from(encryption::KeyPair::new(&mut rng));
|
||||
|
||||
let client = Client::new(nymd_url, mnemonic);
|
||||
let client = Client::new(nyxd_url, mnemonic);
|
||||
let tx_hash = client
|
||||
.deposit(
|
||||
amount,
|
||||
@@ -80,7 +81,8 @@ pub(crate) async fn get_credential(state: &State, shared_storage: PersistentStor
|
||||
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 epoch_id = client.nyxd.get_current_epoch().await?.epoch_id;
|
||||
let coconut_api_clients = CoconutApiClient::all_coconut_api_clients(&client, epoch_id).await?;
|
||||
|
||||
let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap();
|
||||
let bandwidth_credential_attributes = BandwidthVoucher::new(
|
||||
@@ -106,6 +108,7 @@ pub(crate) async fn get_credential(state: &State, shared_storage: PersistentStor
|
||||
bandwidth_credential_attributes.get_private_attributes()[0].to_bs58(),
|
||||
bandwidth_credential_attributes.get_private_attributes()[1].to_bs58(),
|
||||
signature.to_bs58(),
|
||||
epoch_id.to_string(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -7,15 +7,15 @@ use credential_storage::error::StorageError;
|
||||
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::nyxd::error::NyxdError;
|
||||
use validator_client::ValidatorClientError;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, CredentialClientError>;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum CredentialClientError {
|
||||
#[error("Nymd error: {0}")]
|
||||
Nymd(#[from] NymdError),
|
||||
#[error("Nyxd error: {0}")]
|
||||
Nyxd(#[from] NyxdError),
|
||||
|
||||
#[error("Validator client error: {0}")]
|
||||
ValidatorClientError(#[from] ValidatorClientError),
|
||||
|
||||
@@ -31,7 +31,7 @@ cfg_if::cfg_if! {
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let args = Cli::parse();
|
||||
setup_env(args.config_env_file.clone());
|
||||
setup_env(args.config_env_file.as_ref());
|
||||
let bin_name = "nym-credential-client";
|
||||
|
||||
match args.command {
|
||||
@@ -39,11 +39,11 @@ cfg_if::cfg_if! {
|
||||
let db_path = r.client_home_directory.join(DATA_DIR).join(DB_FILE_NAME);
|
||||
let shared_storage = credential_storage::initialise_storage(db_path).await;
|
||||
|
||||
let state = deposit(&r.nymd_url, &r.mnemonic, r.amount).await?;
|
||||
let state = deposit(&r.nyxd_url, &r.mnemonic, r.amount).await?;
|
||||
get_credential(&state, shared_storage).await?;
|
||||
}
|
||||
Command::Completions(c) => c.generate(&mut crate::Cli::into_app(), bin_name),
|
||||
Command::GenerateFigSpec => fig_generate(&mut crate::Cli::into_app(), bin_name)
|
||||
Command::Completions(c) => c.generate(&mut crate::Cli::command(), bin_name),
|
||||
Command::GenerateFigSpec => fig_generate(&mut crate::Cli::command(), bin_name)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.3"
|
||||
version = "1.1.6"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
@@ -20,19 +20,21 @@ futures = "0.3" # bunch of futures stuff, however, now that I think about it, it
|
||||
# and the single instance of abortable we have should really be refactored anyway
|
||||
url = "2.2"
|
||||
|
||||
clap = { version = "3.2", features = ["cargo", "derive"] }
|
||||
clap = { version = "4.0", features = ["cargo", "derive"] }
|
||||
dirs = "4.0"
|
||||
log = "0.4" # self explanatory
|
||||
lazy_static = "1.4.0"
|
||||
log = { workspace = true } # self explanatory
|
||||
pretty_env_logger = "0.4" # for formatting log messages
|
||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] } # rng-related traits + some rng implementation to use
|
||||
serde = { version = "1.0.104", features = ["derive"] } # for config serialization/deserialization
|
||||
serde_json = "1.0"
|
||||
thiserror = "1.0.34"
|
||||
tap = "1.0.1"
|
||||
tokio = { version = "1.21.2", features = ["rt-multi-thread", "net", "signal"] } # async runtime
|
||||
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] } # async runtime
|
||||
tokio-tungstenite = "0.14" # websocket
|
||||
|
||||
## internal
|
||||
build-information = { path = "../../common/build-information" }
|
||||
client-core = { path = "../client-core", features = ["fs-surb-storage"] }
|
||||
client-connections = { path = "../../common/client-connections" }
|
||||
coconut-interface = { path = "../../common/coconut-interface", optional = true }
|
||||
@@ -49,7 +51,7 @@ nymsphinx = { path = "../../common/nymsphinx" }
|
||||
pemstore = { path = "../../common/pemstore" }
|
||||
task = { path = "../../common/task" }
|
||||
topology = { path = "../../common/topology" }
|
||||
validator-client = { path = "../../common/client-libs/validator-client", features = ["nymd-client"] }
|
||||
validator-client = { path = "../../common/client-libs/validator-client", features = ["nyxd-client"] }
|
||||
version-checker = { path = "../../common/version-checker" }
|
||||
websocket-requests = { path = "websocket-requests" }
|
||||
|
||||
@@ -58,6 +60,3 @@ coconut = ["coconut-interface", "credentials", "credentials/coconut", "gateway-r
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = "1.0" # for the "textsend" example
|
||||
|
||||
[build-dependencies]
|
||||
vergen = { version = "5", default-features = false, features = ["build", "git", "rustc", "cargo"] }
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use vergen::{vergen, Config};
|
||||
|
||||
fn main() {
|
||||
vergen(Config::default()).expect("failed to extract build metadata")
|
||||
}
|
||||
+400
-256
File diff suppressed because it is too large
Load Diff
@@ -85,7 +85,7 @@ async fn send_file_without_reply() {
|
||||
let (mut ws_stream, _) = connect_async(uri).await.unwrap();
|
||||
|
||||
let recipient = get_self_address(&mut ws_stream).await;
|
||||
println!("our full address is: {}", recipient);
|
||||
println!("our full address is: {recipient}");
|
||||
|
||||
let read_data = std::fs::read("examples/dummy_file").unwrap();
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ async fn send_text_with_reply() {
|
||||
let (mut ws_stream, _) = connect_async(uri).await.unwrap();
|
||||
|
||||
let recipient = get_self_address(&mut ws_stream).await;
|
||||
println!("our full address is: {}", recipient);
|
||||
println!("our full address is: {recipient}");
|
||||
|
||||
let send_request = json!({
|
||||
"type" : "send",
|
||||
@@ -45,10 +45,7 @@ async fn send_text_with_reply() {
|
||||
"withReplySurb": true,
|
||||
});
|
||||
|
||||
println!(
|
||||
"sending {:?} (*with* reply SURB) over the mix network...",
|
||||
message
|
||||
);
|
||||
println!("sending {message:?} (*with* reply SURB) over the mix network...");
|
||||
let response =
|
||||
send_message_and_get_json_response(&mut ws_stream, send_request.to_string()).await;
|
||||
|
||||
@@ -59,10 +56,7 @@ async fn send_text_with_reply() {
|
||||
"replySurb": response["replySurb"]
|
||||
});
|
||||
|
||||
println!(
|
||||
"sending {:?} (using reply SURB!) over the mix network...",
|
||||
reply_message
|
||||
);
|
||||
println!("sending {reply_message:?} (using reply SURB!) over the mix network...");
|
||||
|
||||
let response =
|
||||
send_message_and_get_json_response(&mut ws_stream, reply_request.to_string()).await;
|
||||
@@ -76,7 +70,7 @@ async fn send_text_without_reply() {
|
||||
let (mut ws_stream, _) = connect_async(uri).await.unwrap();
|
||||
|
||||
let recipient = get_self_address(&mut ws_stream).await;
|
||||
println!("our full address is: {}", recipient);
|
||||
println!("our full address is: {recipient}");
|
||||
|
||||
let send_request = json!({
|
||||
"type" : "send",
|
||||
@@ -85,10 +79,7 @@ async fn send_text_without_reply() {
|
||||
"withReplySurb": false,
|
||||
});
|
||||
|
||||
println!(
|
||||
"sending {:?} (*without* reply SURB) over the mix network...",
|
||||
message
|
||||
);
|
||||
println!("sending {message:?} (*without* reply SURB) over the mix network...");
|
||||
let response =
|
||||
send_message_and_get_json_response(&mut ws_stream, send_request.to_string()).await;
|
||||
|
||||
|
||||
@@ -2,12 +2,15 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::config::template::config_template;
|
||||
pub use client_core::config::Config as BaseConfig;
|
||||
pub use client_core::config::MISSING_VALUE;
|
||||
use client_core::config::{ClientCoreConfigTrait, Config as BaseConfig, DebugConfig};
|
||||
use client_core::config::{ClientCoreConfigTrait, DebugConfig};
|
||||
use config::defaults::DEFAULT_WEBSOCKET_LISTENING_PORT;
|
||||
use config::NymConfig;
|
||||
use config::{NymConfig, OptionalSet};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
mod template;
|
||||
|
||||
@@ -92,6 +95,15 @@ impl Config {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_disabled_socket(mut self, disabled: bool) -> Self {
|
||||
if disabled {
|
||||
self.socket.socket_type = SocketType::None;
|
||||
} else {
|
||||
self.socket.socket_type = SocketType::WebSocket;
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_port(mut self, port: u16) -> Self {
|
||||
self.socket.listening_port = port;
|
||||
self
|
||||
@@ -121,6 +133,50 @@ impl Config {
|
||||
pub fn get_listening_port(&self) -> u16 {
|
||||
self.socket.listening_port
|
||||
}
|
||||
|
||||
// poor man's 'builder' method
|
||||
pub fn with_base<F, T>(mut self, f: F, val: T) -> Self
|
||||
where
|
||||
F: Fn(BaseConfig<Self>, T) -> BaseConfig<Self>,
|
||||
{
|
||||
self.base = f(self.base, val);
|
||||
self
|
||||
}
|
||||
|
||||
// helper methods to use `OptionalSet` trait. Those are defined due to very... ehm. 'specific' structure of this config
|
||||
// (plz, lets refactor it)
|
||||
pub fn with_optional_ext<F, T>(mut self, f: F, val: Option<T>) -> Self
|
||||
where
|
||||
F: Fn(BaseConfig<Self>, T) -> BaseConfig<Self>,
|
||||
{
|
||||
self.base = self.base.with_optional(f, val);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_optional_env_ext<F, T>(mut self, f: F, val: Option<T>, env_var: &str) -> Self
|
||||
where
|
||||
F: Fn(BaseConfig<Self>, T) -> BaseConfig<Self>,
|
||||
T: FromStr,
|
||||
<T as FromStr>::Err: Debug,
|
||||
{
|
||||
self.base = self.base.with_optional_env(f, val, env_var);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_optional_custom_env_ext<F, T, G>(
|
||||
mut self,
|
||||
f: F,
|
||||
val: Option<T>,
|
||||
env_var: &str,
|
||||
parser: G,
|
||||
) -> Self
|
||||
where
|
||||
F: Fn(BaseConfig<Self>, T) -> BaseConfig<Self>,
|
||||
G: Fn(&str) -> T,
|
||||
{
|
||||
self.base = self.base.with_optional_custom_env(f, val, env_var, parser);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||
|
||||
@@ -23,9 +23,9 @@ 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 }}
|
||||
# Addresses to nyxd validators via which the client can communicate with the chain.
|
||||
nyxd_urls = [
|
||||
{{#each client.nyxd_urls }}
|
||||
'{{this}}',
|
||||
{{/each}}
|
||||
]
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::error::ClientError;
|
||||
use crate::websocket;
|
||||
use client_connections::TransmissionLane;
|
||||
use client_core::client::base_client::{
|
||||
non_wasm_helpers, BaseClientBuilder, ClientInput, ClientOutput,
|
||||
non_wasm_helpers, BaseClientBuilder, ClientInput, ClientOutput, ClientState,
|
||||
};
|
||||
use client_core::client::inbound_messages::InputMessage;
|
||||
use client_core::client::key_manager::KeyManager;
|
||||
@@ -21,6 +21,7 @@ use nymsphinx::addressing::clients::Recipient;
|
||||
use nymsphinx::anonymous_replies::requests::AnonymousSenderTag;
|
||||
use nymsphinx::receiver::ReconstructedMessage;
|
||||
use task::TaskManager;
|
||||
use validator_client::nyxd::QueryNyxdClient;
|
||||
|
||||
pub(crate) mod config;
|
||||
|
||||
@@ -44,48 +45,35 @@ impl SocketClient {
|
||||
}
|
||||
}
|
||||
|
||||
async fn create_bandwidth_controller(config: &Config) -> BandwidthController {
|
||||
#[cfg(feature = "coconut")]
|
||||
let bandwidth_controller = {
|
||||
let details = network_defaults::NymNetworkDetails::new_from_env();
|
||||
let mut client_config =
|
||||
validator_client::Config::try_from_nym_network_details(&details)
|
||||
.expect("failed to construct validator client config");
|
||||
let nymd_url = config
|
||||
.get_base()
|
||||
.get_validator_endpoints()
|
||||
.pop()
|
||||
.expect("No nymd validator endpoint provided");
|
||||
let api_url = config
|
||||
.get_base()
|
||||
.get_nym_api_endpoints()
|
||||
.pop()
|
||||
.expect("No validator api endpoint provided");
|
||||
// overwrite env configuration with config URLs
|
||||
client_config = client_config.with_urls(nymd_url, api_url);
|
||||
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,
|
||||
)
|
||||
};
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
let bandwidth_controller = BandwidthController::new(
|
||||
async fn create_bandwidth_controller(config: &Config) -> BandwidthController<QueryNyxdClient> {
|
||||
let details = network_defaults::NymNetworkDetails::new_from_env();
|
||||
let mut client_config = validator_client::Config::try_from_nym_network_details(&details)
|
||||
.expect("failed to construct validator client config");
|
||||
let nyxd_url = config
|
||||
.get_base()
|
||||
.get_validator_endpoints()
|
||||
.pop()
|
||||
.expect("No nyxd validator endpoint provided");
|
||||
let api_url = config
|
||||
.get_base()
|
||||
.get_nym_api_endpoints()
|
||||
.pop()
|
||||
.expect("No validator api endpoint provided");
|
||||
// overwrite env configuration with config URLs
|
||||
client_config = client_config.with_urls(nyxd_url, api_url);
|
||||
let client = validator_client::Client::new_query(client_config)
|
||||
.expect("Could not construct query client");
|
||||
BandwidthController::new(
|
||||
credential_storage::initialise_storage(config.get_base().get_database_path()).await,
|
||||
client,
|
||||
)
|
||||
.expect("Could not create bandwidth controller");
|
||||
bandwidth_controller
|
||||
}
|
||||
|
||||
fn start_websocket_listener(
|
||||
config: &Config,
|
||||
client_input: ClientInput,
|
||||
client_output: ClientOutput,
|
||||
client_state: ClientState,
|
||||
self_address: &Recipient,
|
||||
shutdown: task::TaskClient,
|
||||
) {
|
||||
@@ -97,16 +85,21 @@ impl SocketClient {
|
||||
} = client_input;
|
||||
|
||||
let ClientOutput {
|
||||
shared_lane_queue_lengths,
|
||||
received_buffer_request_sender,
|
||||
} = client_output;
|
||||
|
||||
let ClientState {
|
||||
shared_lane_queue_lengths,
|
||||
reply_controller_sender,
|
||||
} = client_state;
|
||||
|
||||
let websocket_handler = websocket::HandlerBuilder::new(
|
||||
input_sender,
|
||||
connection_command_sender,
|
||||
received_buffer_request_sender,
|
||||
self_address,
|
||||
shared_lane_queue_lengths,
|
||||
reply_controller_sender,
|
||||
);
|
||||
|
||||
websocket::Listener::new(config.get_listening_port()).start(websocket_handler, shutdown);
|
||||
@@ -114,16 +107,9 @@ impl SocketClient {
|
||||
|
||||
/// blocking version of `start_socket` method. Will run forever (or until SIGINT is sent)
|
||||
pub async fn run_socket_forever(self) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
let mut shutdown = self.start_socket().await?;
|
||||
|
||||
let res = task::wait_for_signal_and_error(&mut shutdown).await;
|
||||
|
||||
log::info!("Sending shutdown");
|
||||
shutdown.signal_shutdown().ok();
|
||||
|
||||
log::info!("Waiting for tasks to finish... (Press ctrl-c to force)");
|
||||
shutdown.wait_for_shutdown().await;
|
||||
let shutdown = self.start_socket().await?;
|
||||
|
||||
let res = shutdown.catch_interrupt().await;
|
||||
log::info!("Stopping nym-client");
|
||||
res
|
||||
}
|
||||
@@ -138,7 +124,7 @@ impl SocketClient {
|
||||
self.key_manager,
|
||||
Some(Self::create_bandwidth_controller(&self.config).await),
|
||||
non_wasm_helpers::setup_fs_reply_surb_backend(
|
||||
self.config.get_base().get_reply_surb_database_path(),
|
||||
Some(self.config.get_base().get_reply_surb_database_path()),
|
||||
self.config.get_debug_settings(),
|
||||
)
|
||||
.await?,
|
||||
@@ -148,11 +134,13 @@ impl SocketClient {
|
||||
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 client_state = started_client.client_state;
|
||||
|
||||
Self::start_websocket_listener(
|
||||
&self.config,
|
||||
client_input,
|
||||
client_output,
|
||||
client_state,
|
||||
&self_address,
|
||||
started_client.task_manager.subscribe(),
|
||||
);
|
||||
@@ -173,7 +161,7 @@ impl SocketClient {
|
||||
self.key_manager,
|
||||
Some(Self::create_bandwidth_controller(&self.config).await),
|
||||
non_wasm_helpers::setup_fs_reply_surb_backend(
|
||||
self.config.get_base().get_reply_surb_database_path(),
|
||||
Some(self.config.get_base().get_reply_surb_database_path()),
|
||||
self.config.get_debug_settings(),
|
||||
)
|
||||
.await?,
|
||||
|
||||
@@ -8,6 +8,7 @@ use crate::{
|
||||
};
|
||||
use clap::Args;
|
||||
use config::NymConfig;
|
||||
use crypto::asymmetric::identity;
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
use serde::Serialize;
|
||||
use std::fmt::Display;
|
||||
@@ -21,24 +22,26 @@ pub(crate) struct Init {
|
||||
|
||||
/// Id of the gateway we are going to connect to.
|
||||
#[clap(long)]
|
||||
gateway: Option<String>,
|
||||
gateway: Option<identity::PublicKey>,
|
||||
|
||||
/// Force register gateway. WARNING: this will overwrite any existing keys for the given id,
|
||||
/// potentially causing loss of access.
|
||||
#[clap(long)]
|
||||
force_register_gateway: bool,
|
||||
|
||||
/// Comma separated list of rest endpoints of the nymd validators
|
||||
#[clap(long)]
|
||||
nymd_validators: Option<String>,
|
||||
/// Comma separated list of rest endpoints of the nyxd validators
|
||||
#[cfg(feature = "coconut")]
|
||||
#[clap(long, alias = "nymd_validators", value_delimiter = ',')]
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the API validators
|
||||
#[clap(long)]
|
||||
api_validators: Option<String>,
|
||||
#[clap(long, alias = "api_validators", value_delimiter = ',')]
|
||||
// the alias here is included for backwards compatibility (1.1.4 and before)
|
||||
nym_apis: Option<Vec<url::Url>>,
|
||||
|
||||
/// Whether to not start the websocket
|
||||
#[clap(long)]
|
||||
disable_socket: bool,
|
||||
disable_socket: Option<bool>,
|
||||
|
||||
/// Port for the socket (if applicable) to listen on in all subsequent runs
|
||||
#[clap(short, long)]
|
||||
@@ -46,18 +49,18 @@ pub(crate) struct Init {
|
||||
|
||||
/// Mostly debug-related option to increase default traffic rate so that you would not need to
|
||||
/// modify config post init
|
||||
#[clap(long, hidden = true)]
|
||||
#[clap(long, hide = true)]
|
||||
fastmode: bool,
|
||||
|
||||
/// Disable loop cover traffic and the Poisson rate limiter (for debugging only)
|
||||
#[clap(long, hidden = true)]
|
||||
#[clap(long, hide = true)]
|
||||
no_cover: bool,
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement.
|
||||
#[cfg(feature = "coconut")]
|
||||
#[clap(long)]
|
||||
enabled_credentials_mode: bool,
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
|
||||
/// Save a summary of the initialization to a json file
|
||||
#[clap(long)]
|
||||
@@ -67,13 +70,14 @@ 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,
|
||||
nym_apis: init_config.nym_apis,
|
||||
disable_socket: init_config.disable_socket,
|
||||
port: init_config.port,
|
||||
fastmode: init_config.fastmode,
|
||||
no_cover: init_config.no_cover,
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
nyxd_urls: init_config.nyxd_urls,
|
||||
#[cfg(feature = "coconut")]
|
||||
enabled_credentials_mode: init_config.enabled_credentials_mode,
|
||||
}
|
||||
@@ -110,16 +114,15 @@ pub(crate) async fn execute(args: &Init) -> Result<(), ClientError> {
|
||||
|
||||
let already_init = Config::default_config_file_path(Some(id)).exists();
|
||||
if already_init {
|
||||
println!(
|
||||
"Client \"{}\" was already initialised before! \
|
||||
Config information will be overwritten (but keys will be kept)!",
|
||||
id
|
||||
);
|
||||
println!("Client \"{id}\" was already initialised before");
|
||||
}
|
||||
|
||||
// Usually you only register with the gateway on the first init, however you can force
|
||||
// re-registering if wanted.
|
||||
let user_wants_force_register = args.force_register_gateway;
|
||||
if user_wants_force_register {
|
||||
println!("Instructed to force registering gateway. This might overwrite keys!");
|
||||
}
|
||||
|
||||
// If the client was already initialized, don't generate new keys and don't re-register with
|
||||
// the gateway (because this would create a new shared key).
|
||||
@@ -127,14 +130,14 @@ pub(crate) async fn execute(args: &Init) -> Result<(), ClientError> {
|
||||
let register_gateway = !already_init || user_wants_force_register;
|
||||
|
||||
// Attempt to use a user-provided gateway, if possible
|
||||
let user_chosen_gateway_id = args.gateway.clone();
|
||||
let user_chosen_gateway_id = args.gateway;
|
||||
|
||||
// Load and potentially override config
|
||||
let mut config = override_config(Config::new(id), OverrideConfig::from(args.clone()));
|
||||
|
||||
// Setup gateway by either registering a new one, or creating a new config from the selected
|
||||
// one but with keys kept, or reusing the gateway configuration.
|
||||
let gateway = client_core::init::setup_gateway::<_, Config, _>(
|
||||
let gateway = client_core::init::setup_gateway_from_config::<Config, _>(
|
||||
register_gateway,
|
||||
user_chosen_gateway_id,
|
||||
config.get_base(),
|
||||
@@ -152,20 +155,20 @@ pub(crate) async fn execute(args: &Init) -> Result<(), ClientError> {
|
||||
|
||||
let address = client_core::init::get_client_address_from_stored_keys(config.get_base())?;
|
||||
let init_results = InitResults::new(&config, &address);
|
||||
println!("{}", init_results);
|
||||
println!("{init_results}");
|
||||
|
||||
// Output summary to a json file, if specified
|
||||
if args.output_json {
|
||||
client_core::init::output_to_json(&init_results, "client_init_results.json");
|
||||
}
|
||||
|
||||
println!("\nThe address of this client is: {}\n", address);
|
||||
println!("\nThe address of this client is: {address}\n");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_saved_config(config: &Config) {
|
||||
let config_save_location = config.get_config_file_save_location();
|
||||
println!("Saved configuration file to {:?}", config_save_location);
|
||||
println!("Saved configuration file to {config_save_location:?}");
|
||||
println!("Using gateway: {}", config.get_base().get_gateway_id());
|
||||
log::debug!("Gateway id: {}", config.get_base().get_gateway_id());
|
||||
log::debug!("Gateway owner: {}", config.get_base().get_gateway_owner());
|
||||
|
||||
@@ -1,54 +1,31 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::error::Error;
|
||||
|
||||
use crate::client::config::{Config, SocketType};
|
||||
use crate::client::config::{BaseConfig, Config};
|
||||
use build_information::BinaryBuildInformation;
|
||||
use clap::CommandFactory;
|
||||
use clap::{Parser, Subcommand};
|
||||
use completions::{fig_generate, ArgShell};
|
||||
use config::OptionalSet;
|
||||
use lazy_static::lazy_static;
|
||||
use std::error::Error;
|
||||
|
||||
pub(crate) mod init;
|
||||
pub(crate) mod run;
|
||||
pub(crate) mod upgrade;
|
||||
|
||||
fn long_version() -> String {
|
||||
format!(
|
||||
r#"
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
"#,
|
||||
"Build Timestamp:",
|
||||
env!("VERGEN_BUILD_TIMESTAMP"),
|
||||
"Build Version:",
|
||||
env!("VERGEN_BUILD_SEMVER"),
|
||||
"Commit SHA:",
|
||||
env!("VERGEN_GIT_SHA"),
|
||||
"Commit Date:",
|
||||
env!("VERGEN_GIT_COMMIT_TIMESTAMP"),
|
||||
"Commit Branch:",
|
||||
env!("VERGEN_GIT_BRANCH"),
|
||||
"rustc Version:",
|
||||
env!("VERGEN_RUSTC_SEMVER"),
|
||||
"rustc Channel:",
|
||||
env!("VERGEN_RUSTC_CHANNEL"),
|
||||
"cargo Profile:",
|
||||
env!("VERGEN_CARGO_PROFILE"),
|
||||
)
|
||||
lazy_static! {
|
||||
pub static ref PRETTY_BUILD_INFORMATION: String =
|
||||
BinaryBuildInformation::new(env!("CARGO_PKG_VERSION")).pretty_print();
|
||||
}
|
||||
|
||||
fn long_version_static() -> &'static str {
|
||||
Box::leak(long_version().into_boxed_str())
|
||||
// Helper for passing LONG_VERSION to clap
|
||||
fn pretty_build_info_static() -> &'static str {
|
||||
&PRETTY_BUILD_INFORMATION
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
#[clap(author = "Nymtech", version, long_version = long_version_static(), about)]
|
||||
#[clap(author = "Nymtech", version, long_version = pretty_build_info_static(), about)]
|
||||
pub(crate) struct Cli {
|
||||
/// Path pointing to an env file that configures the client.
|
||||
#[clap(short, long)]
|
||||
@@ -76,15 +53,16 @@ pub(crate) enum Commands {
|
||||
|
||||
// Configuration that can be overridden.
|
||||
pub(crate) struct OverrideConfig {
|
||||
nymd_validators: Option<String>,
|
||||
api_validators: Option<String>,
|
||||
disable_socket: bool,
|
||||
nym_apis: Option<Vec<url::Url>>,
|
||||
disable_socket: Option<bool>,
|
||||
port: Option<u16>,
|
||||
fastmode: bool,
|
||||
no_cover: bool,
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
enabled_credentials_mode: bool,
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
#[cfg(feature = "coconut")]
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
}
|
||||
|
||||
pub(crate) async fn execute(args: &Cli) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
@@ -94,57 +72,38 @@ pub(crate) async fn execute(args: &Cli) -> Result<(), Box<dyn Error + Send + Syn
|
||||
Commands::Init(m) => init::execute(m).await?,
|
||||
Commands::Run(m) => run::execute(m).await?,
|
||||
Commands::Upgrade(m) => upgrade::execute(m),
|
||||
Commands::Completions(s) => s.generate(&mut Cli::into_app(), bin_name),
|
||||
Commands::GenerateFigSpec => fig_generate(&mut Cli::into_app(), bin_name),
|
||||
Commands::Completions(s) => s.generate(&mut Cli::command(), bin_name),
|
||||
Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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 {
|
||||
config
|
||||
.get_base_mut()
|
||||
.set_custom_nym_apis(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::API_VALIDATOR)
|
||||
.expect("api validator not set");
|
||||
config
|
||||
.get_base_mut()
|
||||
.set_custom_nym_apis(config::parse_validators(&raw_validators));
|
||||
}
|
||||
|
||||
if args.disable_socket {
|
||||
config = config.with_socket(SocketType::None);
|
||||
}
|
||||
|
||||
if let Some(port) = args.port {
|
||||
config = config.with_port(port);
|
||||
}
|
||||
config = config
|
||||
.with_optional(Config::with_disabled_socket, args.disable_socket)
|
||||
.with_base(BaseConfig::with_high_default_traffic_volume, args.fastmode)
|
||||
.with_base(BaseConfig::with_disabled_cover_traffic, args.no_cover)
|
||||
.with_optional(Config::with_port, args.port)
|
||||
.with_optional_custom_env_ext(
|
||||
BaseConfig::with_custom_nym_apis,
|
||||
args.nym_apis,
|
||||
network_defaults::var_names::NYM_API,
|
||||
config::parse_urls,
|
||||
);
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
{
|
||||
if args.enabled_credentials_mode {
|
||||
config.get_base_mut().with_disabled_credentials(false)
|
||||
}
|
||||
}
|
||||
|
||||
if args.fastmode {
|
||||
config.get_base_mut().set_high_default_traffic_volume();
|
||||
}
|
||||
|
||||
if args.no_cover {
|
||||
config.get_base_mut().set_no_cover_traffic();
|
||||
config = config
|
||||
.with_optional_custom_env_ext(
|
||||
BaseConfig::with_custom_nyxd,
|
||||
args.nyxd_urls,
|
||||
network_defaults::var_names::NYXD,
|
||||
config::parse_urls,
|
||||
)
|
||||
.with_optional_ext(
|
||||
BaseConfig::with_disabled_credentials,
|
||||
args.enabled_credentials_mode.map(|b| !b),
|
||||
);
|
||||
}
|
||||
|
||||
config
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::error::Error;
|
||||
@@ -11,6 +11,7 @@ use crate::{
|
||||
|
||||
use clap::Args;
|
||||
use config::NymConfig;
|
||||
use crypto::asymmetric::identity;
|
||||
use log::*;
|
||||
use version_checker::is_minor_version_compatible;
|
||||
|
||||
@@ -20,22 +21,24 @@ pub(crate) struct Run {
|
||||
#[clap(long)]
|
||||
id: String,
|
||||
|
||||
/// Comma separated list of rest endpoints of the nymd validators
|
||||
#[clap(long)]
|
||||
nymd_validators: Option<String>,
|
||||
/// Comma separated list of rest endpoints of the nyxd validators
|
||||
#[cfg(feature = "coconut")]
|
||||
#[clap(long, alias = "nymd_validators", value_delimiter = ',')]
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the API validators
|
||||
#[clap(long)]
|
||||
api_validators: Option<String>,
|
||||
#[clap(long, alias = "api_validators", value_delimiter = ',')]
|
||||
// the alias here is included for backwards compatibility (1.1.4 and before)
|
||||
nym_apis: Option<Vec<url::Url>>,
|
||||
|
||||
/// Id of the gateway we want to connect to. If overridden, it is user's responsibility to
|
||||
/// ensure prior registration happened
|
||||
#[clap(long)]
|
||||
gateway: Option<String>,
|
||||
gateway: Option<identity::PublicKey>,
|
||||
|
||||
/// Whether to not start the websocket
|
||||
#[clap(long)]
|
||||
disable_socket: bool,
|
||||
disable_socket: Option<bool>,
|
||||
|
||||
/// Port for the socket to listen on
|
||||
#[clap(short, long)]
|
||||
@@ -43,29 +46,31 @@ pub(crate) struct Run {
|
||||
|
||||
/// Mostly debug-related option to increase default traffic rate so that you would not need to
|
||||
/// modify config post init
|
||||
#[clap(long, hidden = true)]
|
||||
#[clap(long, hide = true)]
|
||||
fastmode: bool,
|
||||
|
||||
/// Disable loop cover traffic and the Poisson rate limiter (for debugging only)
|
||||
#[clap(long, hidden = true)]
|
||||
#[clap(long, hide = true)]
|
||||
no_cover: bool,
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement.
|
||||
#[cfg(feature = "coconut")]
|
||||
#[clap(long)]
|
||||
enabled_credentials_mode: bool,
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
}
|
||||
|
||||
impl From<Run> for OverrideConfig {
|
||||
fn from(run_config: Run) -> Self {
|
||||
OverrideConfig {
|
||||
nymd_validators: run_config.nymd_validators,
|
||||
api_validators: run_config.api_validators,
|
||||
nym_apis: run_config.nym_apis,
|
||||
disable_socket: run_config.disable_socket,
|
||||
port: run_config.port,
|
||||
fastmode: run_config.fastmode,
|
||||
no_cover: run_config.no_cover,
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
nyxd_urls: run_config.nyxd_urls,
|
||||
#[cfg(feature = "coconut")]
|
||||
enabled_credentials_mode: run_config.enabled_credentials_mode,
|
||||
}
|
||||
|
||||
@@ -17,36 +17,26 @@ fn fail_upgrade<D1: Display, D2: Display>(from_version: D1, to_version: D2) -> !
|
||||
}
|
||||
|
||||
fn print_start_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
|
||||
println!(
|
||||
"\n==================\nTrying to upgrade client from {} to {} ...",
|
||||
from, to
|
||||
);
|
||||
println!("\n==================\nTrying to upgrade client from {from} to {to} ...");
|
||||
}
|
||||
|
||||
fn print_failed_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
|
||||
eprintln!(
|
||||
"Upgrade from {} to {} failed!\n==================\n",
|
||||
from, to
|
||||
);
|
||||
eprintln!("Upgrade from {from} to {to} failed!\n==================\n");
|
||||
}
|
||||
|
||||
fn print_successful_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
|
||||
println!(
|
||||
"Upgrade from {} to {} was successful!\n==================\n",
|
||||
from, to
|
||||
);
|
||||
println!("Upgrade from {from} to {to} was successful!\n==================\n");
|
||||
}
|
||||
|
||||
fn outdated_upgrade(config_version: &Version, package_version: &Version) -> ! {
|
||||
eprintln!(
|
||||
"Cannot perform upgrade from {} to {}. Your version is too old to perform the upgrade.!",
|
||||
config_version, package_version
|
||||
"Cannot perform upgrade from {config_version} to {package_version}. Your version is too old to perform the upgrade.!"
|
||||
);
|
||||
process::exit(1)
|
||||
}
|
||||
|
||||
fn unsupported_upgrade(current_version: &Version, config_version: &Version) -> ! {
|
||||
eprintln!("Cannot perform upgrade from {} to {}. Please let the developers know about this issue if you expected it to work!", config_version, current_version);
|
||||
eprintln!("Cannot perform upgrade from {config_version} to {current_version}. Please let the developers know about this issue if you expected it to work!");
|
||||
process::exit(1)
|
||||
}
|
||||
|
||||
@@ -65,8 +55,7 @@ fn parse_config_version(config: &Config) -> Version {
|
||||
|
||||
if version.is_prerelease() || !version.build.is_empty() {
|
||||
eprintln!(
|
||||
"Trying to upgrade from a non-released version {}. This is not supported!",
|
||||
version
|
||||
"Trying to upgrade from a non-released version {version}. This is not supported!"
|
||||
);
|
||||
process::exit(1)
|
||||
}
|
||||
@@ -81,10 +70,7 @@ fn parse_package_version() -> Version {
|
||||
// however, we are not using them ourselves at the moment and hence it should be fine.
|
||||
// if we change our mind, we could easily tweak this code
|
||||
if version.is_prerelease() || !version.build.is_empty() {
|
||||
eprintln!(
|
||||
"Trying to upgrade to a non-released version {}. This is not supported!",
|
||||
version
|
||||
);
|
||||
eprintln!("Trying to upgrade to a non-released version {version}. This is not supported!");
|
||||
process::exit(1)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use client_core::client::replies::reply_storage::fs_backend;
|
||||
use client_core::error::ClientCoreError;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
@@ -7,7 +6,7 @@ pub enum ClientError {
|
||||
IoError(#[from] std::io::Error),
|
||||
|
||||
#[error("client-core error: {0}")]
|
||||
ClientCoreError(#[from] ClientCoreError<fs_backend::Backend>),
|
||||
ClientCoreError(#[from] ClientCoreError),
|
||||
|
||||
#[error("Failed to load config for: {0}")]
|
||||
FailedToLoadConfig(String),
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use std::error::Error;
|
||||
|
||||
use clap::{crate_version, Parser};
|
||||
use clap::{crate_name, crate_version, Parser};
|
||||
use logging::setup_logging;
|
||||
use network_defaults::setup_env;
|
||||
|
||||
@@ -15,26 +15,9 @@ pub mod websocket;
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
setup_logging();
|
||||
println!("{}", banner());
|
||||
println!("{}", logging::banner(crate_name!(), crate_version!()));
|
||||
|
||||
let args = commands::Cli::parse();
|
||||
setup_env(args.config_env_file.clone());
|
||||
setup_env(args.config_env_file.as_ref());
|
||||
commands::execute(&args).await
|
||||
}
|
||||
|
||||
fn banner() -> String {
|
||||
format!(
|
||||
r#"
|
||||
|
||||
_ __ _ _ _ __ ___
|
||||
| '_ \| | | | '_ \ _ \
|
||||
| | | | |_| | | | | | |
|
||||
|_| |_|\__, |_| |_| |_|
|
||||
|___/
|
||||
|
||||
(client - version {:})
|
||||
|
||||
"#,
|
||||
crate_version!()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use client_connections::{
|
||||
ConnectionCommand, ConnectionCommandSender, LaneQueueLengths, TransmissionLane,
|
||||
ConnectionCommand, ConnectionCommandSender, ConnectionId, LaneQueueLengths, TransmissionLane,
|
||||
};
|
||||
use client_core::client::replies::reply_controller::requests::ReplyControllerSender;
|
||||
use client_core::client::{
|
||||
inbound_messages::{InputMessage, InputMessageSender},
|
||||
received_buffer::{
|
||||
@@ -16,7 +17,9 @@ use log::*;
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
use nymsphinx::anonymous_replies::requests::AnonymousSenderTag;
|
||||
use nymsphinx::receiver::ReconstructedMessage;
|
||||
use std::time::Duration;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::time::Instant;
|
||||
use tokio_tungstenite::{
|
||||
accept_async,
|
||||
tungstenite::{protocol::Message as WsMessage, Error as WsError},
|
||||
@@ -24,23 +27,20 @@ use tokio_tungstenite::{
|
||||
};
|
||||
use websocket_requests::{requests::ClientRequest, responses::ServerResponse};
|
||||
|
||||
#[derive(Default)]
|
||||
enum ReceivedResponseType {
|
||||
#[default]
|
||||
Binary,
|
||||
Text,
|
||||
}
|
||||
|
||||
impl Default for ReceivedResponseType {
|
||||
fn default() -> Self {
|
||||
ReceivedResponseType::Binary
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct HandlerBuilder {
|
||||
msg_input: InputMessageSender,
|
||||
client_connection_tx: ConnectionCommandSender,
|
||||
buffer_requester: ReceivedBufferRequestSender,
|
||||
self_full_address: Recipient,
|
||||
lane_queue_lengths: LaneQueueLengths,
|
||||
reply_controller_sender: ReplyControllerSender,
|
||||
}
|
||||
|
||||
impl HandlerBuilder {
|
||||
@@ -50,6 +50,7 @@ impl HandlerBuilder {
|
||||
buffer_requester: ReceivedBufferRequestSender,
|
||||
self_full_address: &Recipient,
|
||||
lane_queue_lengths: LaneQueueLengths,
|
||||
reply_controller_sender: ReplyControllerSender,
|
||||
) -> Self {
|
||||
Self {
|
||||
msg_input,
|
||||
@@ -57,6 +58,7 @@ impl HandlerBuilder {
|
||||
buffer_requester,
|
||||
self_full_address: *self_full_address,
|
||||
lane_queue_lengths,
|
||||
reply_controller_sender,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +72,7 @@ impl HandlerBuilder {
|
||||
socket: None,
|
||||
received_response_type: Default::default(),
|
||||
lane_queue_lengths: self.lane_queue_lengths.clone(),
|
||||
reply_controller_sender: self.reply_controller_sender.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,6 +85,7 @@ pub(crate) struct Handler {
|
||||
socket: Option<WebSocketStream<TcpStream>>,
|
||||
received_response_type: ReceivedResponseType,
|
||||
lane_queue_lengths: LaneQueueLengths,
|
||||
reply_controller_sender: ReplyControllerSender,
|
||||
}
|
||||
|
||||
impl Drop for Handler {
|
||||
@@ -97,6 +101,48 @@ impl Drop for Handler {
|
||||
}
|
||||
|
||||
impl Handler {
|
||||
async fn get_lane_queue_length(&self, connection_id: ConnectionId) -> Option<ServerResponse> {
|
||||
let req_start = Instant::now();
|
||||
|
||||
// get the base queue length
|
||||
// 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 conn_lane = TransmissionLane::ConnectionId(connection_id);
|
||||
let Ok(base_length) = self
|
||||
.lane_queue_lengths
|
||||
.lock()
|
||||
.map(|guard| guard.get(&conn_lane).unwrap_or_default()) else {
|
||||
// I'd argue we should panic here as this error it not recoverable
|
||||
error!("The lane queue length lock is poisoned!!");
|
||||
return None
|
||||
};
|
||||
|
||||
// get the number of pending replies waiting for reply surbs
|
||||
let reply_queue_length = self
|
||||
.reply_controller_sender
|
||||
.get_lane_queue_length(connection_id)
|
||||
.await;
|
||||
|
||||
let queue_length = base_length + reply_queue_length;
|
||||
|
||||
let time_taken = req_start.elapsed();
|
||||
let msg =
|
||||
format!("it took {time_taken:?} to get lane length for connection {connection_id}. The length is: {queue_length} = {base_length} (already queued up) + {reply_queue_length} (waiting for reply SURBs)");
|
||||
|
||||
if time_taken > Duration::from_millis(1) {
|
||||
info!("{msg}");
|
||||
} else if time_taken > Duration::from_millis(10) {
|
||||
warn!("{msg}");
|
||||
} else if time_taken > Duration::from_millis(50) {
|
||||
error!("{msg}");
|
||||
}
|
||||
|
||||
Some(ServerResponse::LaneQueueLength {
|
||||
lane: connection_id,
|
||||
queue_length,
|
||||
})
|
||||
}
|
||||
|
||||
async fn handle_send(
|
||||
&mut self,
|
||||
recipient: Recipient,
|
||||
@@ -121,27 +167,11 @@ impl Handler {
|
||||
.expect("InputMessageReceiver has stopped receiving!");
|
||||
|
||||
// Only reply back with a `LaneQueueLength` if the sender providided a connection id
|
||||
let connection_id = match lane {
|
||||
TransmissionLane::General
|
||||
| TransmissionLane::ReplySurbRequest
|
||||
| TransmissionLane::Retransmission
|
||||
| TransmissionLane::AdditionalReplySurbs => return None,
|
||||
TransmissionLane::ConnectionId(id) => id,
|
||||
let TransmissionLane::ConnectionId(connection_id) = lane else {
|
||||
return None
|
||||
};
|
||||
|
||||
// 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.
|
||||
if let Ok(lane_queue_lengths) = self.lane_queue_lengths.lock() {
|
||||
let queue_length = lane_queue_lengths.get(&lane).unwrap_or(0);
|
||||
return Some(ServerResponse::LaneQueueLength {
|
||||
lane: connection_id,
|
||||
queue_length,
|
||||
});
|
||||
}
|
||||
|
||||
log::warn!("Failed to get the lane queue length lock, not responding back with the current queue length");
|
||||
None
|
||||
self.get_lane_queue_length(connection_id).await
|
||||
}
|
||||
|
||||
async fn handle_send_anonymous(
|
||||
@@ -168,27 +198,11 @@ impl Handler {
|
||||
.expect("InputMessageReceiver has stopped receiving!");
|
||||
|
||||
// Only reply back with a `LaneQueueLength` if the sender providided a connection id
|
||||
let connection_id = match lane {
|
||||
TransmissionLane::General
|
||||
| TransmissionLane::ReplySurbRequest
|
||||
| TransmissionLane::Retransmission
|
||||
| TransmissionLane::AdditionalReplySurbs => return None,
|
||||
TransmissionLane::ConnectionId(id) => id,
|
||||
let TransmissionLane::ConnectionId(connection_id) = lane else {
|
||||
return None
|
||||
};
|
||||
|
||||
// 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.
|
||||
if let Ok(lane_queue_lengths) = self.lane_queue_lengths.lock() {
|
||||
let queue_length = lane_queue_lengths.get(&lane).unwrap_or(0);
|
||||
return Some(ServerResponse::LaneQueueLength {
|
||||
lane: connection_id,
|
||||
queue_length,
|
||||
});
|
||||
}
|
||||
|
||||
log::warn!("Failed to get the lane queue length lock, not responding back with the current queue length");
|
||||
None
|
||||
self.get_lane_queue_length(connection_id).await
|
||||
}
|
||||
|
||||
async fn handle_reply(
|
||||
@@ -211,27 +225,11 @@ impl Handler {
|
||||
.expect("InputMessageReceiver has stopped receiving!");
|
||||
|
||||
// Only reply back with a `LaneQueueLength` if the sender providided a connection id
|
||||
let connection_id = match lane {
|
||||
TransmissionLane::General
|
||||
| TransmissionLane::ReplySurbRequest
|
||||
| TransmissionLane::Retransmission
|
||||
| TransmissionLane::AdditionalReplySurbs => return None,
|
||||
TransmissionLane::ConnectionId(id) => id,
|
||||
let TransmissionLane::ConnectionId(connection_id) = lane else {
|
||||
return None
|
||||
};
|
||||
|
||||
// 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.
|
||||
if let Ok(lane_queue_lengths) = self.lane_queue_lengths.lock() {
|
||||
let queue_length = lane_queue_lengths.get(&lane).unwrap_or(0);
|
||||
return Some(ServerResponse::LaneQueueLength {
|
||||
lane: connection_id,
|
||||
queue_length,
|
||||
});
|
||||
}
|
||||
|
||||
log::warn!("Failed to get the lane queue length lock, not responding back with the current queue length");
|
||||
None
|
||||
self.get_lane_queue_length(connection_id).await
|
||||
}
|
||||
|
||||
fn handle_self_address(&self) -> ServerResponse {
|
||||
@@ -245,20 +243,8 @@ impl Handler {
|
||||
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 {
|
||||
lane: connection_id,
|
||||
queue_length,
|
||||
})
|
||||
async fn handle_get_lane_queue_length(&self, connection_id: u64) -> Option<ServerResponse> {
|
||||
self.get_lane_queue_length(connection_id).await
|
||||
}
|
||||
|
||||
async fn handle_request(&mut self, request: ClientRequest) -> Option<ServerResponse> {
|
||||
@@ -287,7 +273,7 @@ impl Handler {
|
||||
|
||||
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),
|
||||
ClientRequest::GetLaneQueueLength(id) => self.handle_get_lane_queue_length(id).await,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ impl TryFrom<u8> for ErrorKind {
|
||||
|
||||
n => Err(Error::new(
|
||||
ErrorKind::MalformedResponse,
|
||||
format!("invalid error code {}", n),
|
||||
format!("invalid error code {n}"),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.3"
|
||||
version = "1.1.5"
|
||||
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"
|
||||
@@ -11,10 +11,11 @@ name = "nym_socks5"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "3.2", features = ["cargo", "derive"] }
|
||||
clap = { version = "4.0", features = ["cargo", "derive"] }
|
||||
dirs = "4.0"
|
||||
futures = "0.3"
|
||||
log = "0.4"
|
||||
lazy_static = "1.4.0"
|
||||
log = { workspace = true }
|
||||
pin-project = "1.0"
|
||||
pretty_env_logger = "0.4"
|
||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||
@@ -22,10 +23,11 @@ serde = { version = "1.0", features = ["derive"] } # for config serialization/de
|
||||
serde_json = "1.0.89"
|
||||
tap = "1.0.1"
|
||||
thiserror = "1.0.34"
|
||||
tokio = { version = "1.21.2", features = ["rt-multi-thread", "net", "signal"] }
|
||||
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] }
|
||||
url = "2.2"
|
||||
|
||||
# internal
|
||||
build-information = { path = "../../common/build-information" }
|
||||
client-core = { path = "../client-core", features = ["fs-surb-storage"] }
|
||||
client-connections = { path = "../../common/client-connections" }
|
||||
coconut-interface = { path = "../../common/coconut-interface", optional = true }
|
||||
@@ -45,12 +47,9 @@ proxy-helpers = { path = "../../common/socks5/proxy-helpers" }
|
||||
socks5-requests = { path = "../../common/socks5/requests" }
|
||||
task = { path = "../../common/task" }
|
||||
topology = { path = "../../common/topology" }
|
||||
validator-client = { path = "../../common/client-libs/validator-client", features = ["nymd-client"] }
|
||||
validator-client = { path = "../../common/client-libs/validator-client", features = ["nyxd-client"] }
|
||||
version-checker = { path = "../../common/version-checker" }
|
||||
|
||||
[features]
|
||||
coconut = ["coconut-interface", "credentials", "gateway-requests/coconut", "gateway-client/coconut", "credentials/coconut", "client-core/coconut"]
|
||||
eth = []
|
||||
|
||||
[build-dependencies]
|
||||
vergen = { version = "5", default-features = false, features = ["build", "git", "rustc", "cargo"] }
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use vergen::{vergen, Config};
|
||||
|
||||
fn main() {
|
||||
vergen(Config::default()).expect("failed to extract build metadata")
|
||||
}
|
||||
@@ -2,13 +2,16 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::config::template::config_template;
|
||||
pub use client_core::config::Config as BaseConfig;
|
||||
pub use client_core::config::MISSING_VALUE;
|
||||
use client_core::config::{ClientCoreConfigTrait, Config as BaseConfig, DebugConfig};
|
||||
use client_core::config::{ClientCoreConfigTrait, DebugConfig};
|
||||
use config::defaults::DEFAULT_SOCKS5_LISTENING_PORT;
|
||||
use config::NymConfig;
|
||||
use config::{NymConfig, OptionalSet};
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
mod template;
|
||||
|
||||
@@ -127,6 +130,50 @@ impl Config {
|
||||
pub fn get_per_request_surbs(&self) -> u32 {
|
||||
self.socks5_debug.per_request_surbs
|
||||
}
|
||||
|
||||
// poor man's 'builder' method
|
||||
pub fn with_base<F, T>(mut self, f: F, val: T) -> Self
|
||||
where
|
||||
F: Fn(BaseConfig<Self>, T) -> BaseConfig<Self>,
|
||||
{
|
||||
self.base = f(self.base, val);
|
||||
self
|
||||
}
|
||||
|
||||
// helper methods to use `OptionalSet` trait. Those are defined due to very... ehm. 'specific' structure of this config
|
||||
// (plz, lets refactor it)
|
||||
pub fn with_optional_ext<F, T>(mut self, f: F, val: Option<T>) -> Self
|
||||
where
|
||||
F: Fn(BaseConfig<Self>, T) -> BaseConfig<Self>,
|
||||
{
|
||||
self.base = self.base.with_optional(f, val);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_optional_env_ext<F, T>(mut self, f: F, val: Option<T>, env_var: &str) -> Self
|
||||
where
|
||||
F: Fn(BaseConfig<Self>, T) -> BaseConfig<Self>,
|
||||
T: FromStr,
|
||||
<T as FromStr>::Err: Debug,
|
||||
{
|
||||
self.base = self.base.with_optional_env(f, val, env_var);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_optional_custom_env_ext<F, T, G>(
|
||||
mut self,
|
||||
f: F,
|
||||
val: Option<T>,
|
||||
env_var: &str,
|
||||
parser: G,
|
||||
) -> Self
|
||||
where
|
||||
F: Fn(BaseConfig<Self>, T) -> BaseConfig<Self>,
|
||||
G: Fn(&str) -> T,
|
||||
{
|
||||
self.base = self.base.with_optional_custom_env(f, val, env_var, parser);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||
|
||||
@@ -23,9 +23,9 @@ 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 }}
|
||||
# Addresses to nyxd validators via which the client can communicate with the chain.
|
||||
nyxd_urls = [
|
||||
{{#each client.nyxd_urls }}
|
||||
'{{this}}',
|
||||
{{/each}}
|
||||
]
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::socks::{
|
||||
server::SphinxSocksServer,
|
||||
};
|
||||
use client_core::client::base_client::{
|
||||
non_wasm_helpers, BaseClientBuilder, ClientInput, ClientOutput,
|
||||
non_wasm_helpers, BaseClientBuilder, ClientInput, ClientOutput, ClientState,
|
||||
};
|
||||
use client_core::client::key_manager::KeyManager;
|
||||
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
||||
@@ -19,7 +19,8 @@ use gateway_client::bandwidth::BandwidthController;
|
||||
use log::*;
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
use std::error::Error;
|
||||
use task::{wait_for_signal_and_error, TaskClient, TaskManager};
|
||||
use task::{TaskClient, TaskManager};
|
||||
use validator_client::nyxd::QueryNyxdClient;
|
||||
|
||||
pub mod config;
|
||||
|
||||
@@ -53,48 +54,35 @@ impl NymClient {
|
||||
}
|
||||
}
|
||||
|
||||
async fn create_bandwidth_controller(config: &Config) -> BandwidthController {
|
||||
#[cfg(feature = "coconut")]
|
||||
let bandwidth_controller = {
|
||||
let details = network_defaults::NymNetworkDetails::new_from_env();
|
||||
let mut client_config =
|
||||
validator_client::Config::try_from_nym_network_details(&details)
|
||||
.expect("failed to construct validator client config");
|
||||
let nymd_url = config
|
||||
.get_base()
|
||||
.get_validator_endpoints()
|
||||
.pop()
|
||||
.expect("No nymd validator endpoint provided");
|
||||
let api_url = config
|
||||
.get_base()
|
||||
.get_nym_api_endpoints()
|
||||
.pop()
|
||||
.expect("No validator api endpoint provided");
|
||||
// overwrite env configuration with config URLs
|
||||
client_config = client_config.with_urls(nymd_url, api_url);
|
||||
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,
|
||||
)
|
||||
};
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
let bandwidth_controller = BandwidthController::new(
|
||||
async fn create_bandwidth_controller(config: &Config) -> BandwidthController<QueryNyxdClient> {
|
||||
let details = network_defaults::NymNetworkDetails::new_from_env();
|
||||
let mut client_config = validator_client::Config::try_from_nym_network_details(&details)
|
||||
.expect("failed to construct validator client config");
|
||||
let nyxd_url = config
|
||||
.get_base()
|
||||
.get_validator_endpoints()
|
||||
.pop()
|
||||
.expect("No nyxd validator endpoint provided");
|
||||
let api_url = config
|
||||
.get_base()
|
||||
.get_nym_api_endpoints()
|
||||
.pop()
|
||||
.expect("No validator api endpoint provided");
|
||||
// overwrite env configuration with config URLs
|
||||
client_config = client_config.with_urls(nyxd_url, api_url);
|
||||
let client = validator_client::Client::new_query(client_config)
|
||||
.expect("Could not construct query client");
|
||||
BandwidthController::new(
|
||||
credential_storage::initialise_storage(config.get_base().get_database_path()).await,
|
||||
client,
|
||||
)
|
||||
.expect("Could not create bandwidth controller");
|
||||
bandwidth_controller
|
||||
}
|
||||
|
||||
fn start_socks5_listener(
|
||||
config: &Config,
|
||||
client_input: ClientInput,
|
||||
client_output: ClientOutput,
|
||||
client_status: ClientState,
|
||||
self_address: Recipient,
|
||||
shutdown: TaskClient,
|
||||
) {
|
||||
@@ -108,10 +96,14 @@ impl NymClient {
|
||||
} = client_input;
|
||||
|
||||
let ClientOutput {
|
||||
shared_lane_queue_lengths,
|
||||
received_buffer_request_sender,
|
||||
} = client_output;
|
||||
|
||||
let ClientState {
|
||||
shared_lane_queue_lengths,
|
||||
reply_controller_sender: _,
|
||||
} = client_status;
|
||||
|
||||
let authenticator = Authenticator::new(auth_methods, allowed_users);
|
||||
let mut sphinx_socks = SphinxSocksServer::new(
|
||||
config.get_listening_port(),
|
||||
@@ -142,16 +134,9 @@ impl NymClient {
|
||||
|
||||
/// blocking version of `start` method. Will run forever (or until SIGINT is sent)
|
||||
pub async fn run_forever(self) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
let mut shutdown = self.start().await?;
|
||||
|
||||
let res = wait_for_signal_and_error(&mut shutdown).await;
|
||||
|
||||
log::info!("Sending shutdown");
|
||||
shutdown.signal_shutdown().ok();
|
||||
|
||||
log::info!("Waiting for tasks to finish... (Press ctrl-c to force)");
|
||||
shutdown.wait_for_shutdown().await;
|
||||
let shutdown = self.start().await?;
|
||||
|
||||
let res = shutdown.catch_interrupt().await;
|
||||
log::info!("Stopping nym-socks5-client");
|
||||
res
|
||||
}
|
||||
@@ -208,7 +193,7 @@ impl NymClient {
|
||||
self.key_manager,
|
||||
Some(Self::create_bandwidth_controller(&self.config).await),
|
||||
non_wasm_helpers::setup_fs_reply_surb_backend(
|
||||
self.config.get_base().get_reply_surb_database_path(),
|
||||
Some(self.config.get_base().get_reply_surb_database_path()),
|
||||
self.config.get_debug_settings(),
|
||||
)
|
||||
.await?,
|
||||
@@ -218,11 +203,13 @@ impl NymClient {
|
||||
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 client_state = started_client.client_state;
|
||||
|
||||
Self::start_socks5_listener(
|
||||
&self.config,
|
||||
client_input,
|
||||
client_output,
|
||||
client_state,
|
||||
self_address,
|
||||
started_client.task_manager.subscribe(),
|
||||
);
|
||||
|
||||
@@ -8,6 +8,7 @@ use crate::{
|
||||
};
|
||||
use clap::Args;
|
||||
use config::NymConfig;
|
||||
use crypto::asymmetric::identity;
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
use serde::Serialize;
|
||||
use std::fmt::Display;
|
||||
@@ -21,32 +22,35 @@ pub(crate) struct Init {
|
||||
|
||||
/// Address of the socks5 provider to send messages to.
|
||||
#[clap(long)]
|
||||
provider: String,
|
||||
provider: Recipient,
|
||||
|
||||
/// Specifies whether this client is going to use an anonymous sender tag for communication with the service provider.
|
||||
/// While this is going to hide its actual address information, it will make the actual communication
|
||||
/// slower and consume nearly double the bandwidth as it will require sending reply SURBs.
|
||||
///
|
||||
/// Note that some service providers might not support this.
|
||||
#[clap(long)]
|
||||
use_anonymous_sender_tag: bool,
|
||||
// the alias here is included for backwards compatibility (1.1.4 and before)
|
||||
#[clap(long, alias = "use_anonymous_sender_tag")]
|
||||
use_reply_surbs: Option<bool>,
|
||||
|
||||
/// Id of the gateway we are going to connect to.
|
||||
#[clap(long)]
|
||||
gateway: Option<String>,
|
||||
gateway: Option<identity::PublicKey>,
|
||||
|
||||
/// Force register gateway. WARNING: this will overwrite any existing keys for the given id,
|
||||
/// potentially causing loss of access.
|
||||
#[clap(long)]
|
||||
force_register_gateway: bool,
|
||||
|
||||
/// Comma separated list of rest endpoints of the nymd validators
|
||||
#[clap(long)]
|
||||
nymd_validators: Option<String>,
|
||||
/// Comma separated list of rest endpoints of the nyxd validators
|
||||
#[cfg(feature = "coconut")]
|
||||
#[clap(long, alias = "nymd_validators", value_delimiter = ',')]
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the API validators
|
||||
#[clap(long)]
|
||||
api_validators: Option<String>,
|
||||
#[clap(long, alias = "api_validators", value_delimiter = ',')]
|
||||
// the alias here is included for backwards compatibility (1.1.4 and before)
|
||||
nym_apis: Option<Vec<url::Url>>,
|
||||
|
||||
/// Port for the socket to listen on in all subsequent runs
|
||||
#[clap(short, long)]
|
||||
@@ -54,18 +58,18 @@ pub(crate) struct Init {
|
||||
|
||||
/// Mostly debug-related option to increase default traffic rate so that you would not need to
|
||||
/// modify config post init
|
||||
#[clap(long, hidden = true)]
|
||||
#[clap(long, hide = true)]
|
||||
fastmode: bool,
|
||||
|
||||
/// Disable loop cover traffic and the Poisson rate limiter (for debugging only)
|
||||
#[clap(long, hidden = true)]
|
||||
#[clap(long, hide = true)]
|
||||
no_cover: bool,
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement.
|
||||
#[cfg(feature = "coconut")]
|
||||
#[clap(long)]
|
||||
enabled_credentials_mode: bool,
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
|
||||
/// Save a summary of the initialization to a json file
|
||||
#[clap(long)]
|
||||
@@ -75,12 +79,14 @@ 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,
|
||||
nym_apis: init_config.nym_apis,
|
||||
port: init_config.port,
|
||||
use_anonymous_sender_tag: init_config.use_anonymous_sender_tag,
|
||||
use_anonymous_replies: init_config.use_reply_surbs,
|
||||
fastmode: init_config.fastmode,
|
||||
no_cover: init_config.no_cover,
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
nyxd_urls: init_config.nyxd_urls,
|
||||
#[cfg(feature = "coconut")]
|
||||
enabled_credentials_mode: init_config.enabled_credentials_mode,
|
||||
}
|
||||
@@ -118,16 +124,15 @@ pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> {
|
||||
|
||||
let already_init = Config::default_config_file_path(Some(id)).exists();
|
||||
if already_init {
|
||||
println!(
|
||||
"SOCKS5 client \"{}\" was already initialised before! \
|
||||
Config information will be overwritten (but keys will be kept)!",
|
||||
id
|
||||
);
|
||||
println!("SOCKS5 client \"{id}\" was already initialised before");
|
||||
}
|
||||
|
||||
// Usually you only register with the gateway on the first init, however you can force
|
||||
// re-registering if wanted.
|
||||
let user_wants_force_register = args.force_register_gateway;
|
||||
if user_wants_force_register {
|
||||
println!("Instructed to force registering gateway. This might overwrite keys!");
|
||||
}
|
||||
|
||||
// If the client was already initialized, don't generate new keys and don't re-register with
|
||||
// the gateway (because this would create a new shared key).
|
||||
@@ -135,17 +140,17 @@ pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> {
|
||||
let register_gateway = !already_init || user_wants_force_register;
|
||||
|
||||
// Attempt to use a user-provided gateway, if possible
|
||||
let user_chosen_gateway_id = args.gateway.clone();
|
||||
let user_chosen_gateway_id = args.gateway;
|
||||
|
||||
// Load and potentially override config
|
||||
let mut config = override_config(
|
||||
Config::new(id, provider_address),
|
||||
Config::new(id, &provider_address.to_string()),
|
||||
OverrideConfig::from(args.clone()),
|
||||
);
|
||||
|
||||
// Setup gateway by either registering a new one, or creating a new config from the selected
|
||||
// one but with keys kept, or reusing the gateway configuration.
|
||||
let gateway = client_core::init::setup_gateway::<_, Config, _>(
|
||||
let gateway = client_core::init::setup_gateway_from_config::<Config, _>(
|
||||
register_gateway,
|
||||
user_chosen_gateway_id,
|
||||
config.get_base(),
|
||||
|
||||
@@ -1,55 +1,31 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::error::Error;
|
||||
|
||||
use crate::client::config::Config;
|
||||
use crate::client::config::{BaseConfig, Config};
|
||||
use build_information::BinaryBuildInformation;
|
||||
use clap::CommandFactory;
|
||||
use clap::{Parser, Subcommand};
|
||||
use completions::{fig_generate, ArgShell};
|
||||
use config::parse_validators;
|
||||
use config::OptionalSet;
|
||||
use lazy_static::lazy_static;
|
||||
use std::error::Error;
|
||||
|
||||
pub mod init;
|
||||
pub(crate) mod run;
|
||||
pub(crate) mod upgrade;
|
||||
|
||||
fn long_version() -> String {
|
||||
format!(
|
||||
r#"
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
"#,
|
||||
"Build Timestamp:",
|
||||
env!("VERGEN_BUILD_TIMESTAMP"),
|
||||
"Build Version:",
|
||||
env!("VERGEN_BUILD_SEMVER"),
|
||||
"Commit SHA:",
|
||||
env!("VERGEN_GIT_SHA"),
|
||||
"Commit Date:",
|
||||
env!("VERGEN_GIT_COMMIT_TIMESTAMP"),
|
||||
"Commit Branch:",
|
||||
env!("VERGEN_GIT_BRANCH"),
|
||||
"rustc Version:",
|
||||
env!("VERGEN_RUSTC_SEMVER"),
|
||||
"rustc Channel:",
|
||||
env!("VERGEN_RUSTC_CHANNEL"),
|
||||
"cargo Profile:",
|
||||
env!("VERGEN_CARGO_PROFILE"),
|
||||
)
|
||||
lazy_static! {
|
||||
pub static ref PRETTY_BUILD_INFORMATION: String =
|
||||
BinaryBuildInformation::new(env!("CARGO_PKG_VERSION")).pretty_print();
|
||||
}
|
||||
|
||||
fn long_version_static() -> &'static str {
|
||||
Box::leak(long_version().into_boxed_str())
|
||||
// Helper for passing LONG_VERSION to clap
|
||||
fn pretty_build_info_static() -> &'static str {
|
||||
&PRETTY_BUILD_INFORMATION
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
#[clap(author = "Nymtech", version, long_version = long_version_static(), about)]
|
||||
#[clap(author = "Nymtech", version, long_version = pretty_build_info_static(), about)]
|
||||
pub(crate) struct Cli {
|
||||
/// Path pointing to an env file that configures the client.
|
||||
#[clap(short, long)]
|
||||
@@ -79,15 +55,16 @@ pub(crate) enum Commands {
|
||||
|
||||
// Configuration that can be overridden.
|
||||
pub(crate) struct OverrideConfig {
|
||||
nymd_validators: Option<String>,
|
||||
api_validators: Option<String>,
|
||||
nym_apis: Option<Vec<url::Url>>,
|
||||
port: Option<u16>,
|
||||
use_anonymous_sender_tag: bool,
|
||||
use_anonymous_replies: Option<bool>,
|
||||
fastmode: bool,
|
||||
no_cover: bool,
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
enabled_credentials_mode: bool,
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
#[cfg(feature = "coconut")]
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
}
|
||||
|
||||
pub(crate) async fn execute(args: &Cli) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
@@ -97,53 +74,38 @@ pub(crate) async fn execute(args: &Cli) -> Result<(), Box<dyn Error + Send + Syn
|
||||
Commands::Init(m) => init::execute(m).await?,
|
||||
Commands::Run(m) => run::execute(m).await?,
|
||||
Commands::Upgrade(m) => upgrade::execute(m),
|
||||
Commands::Completions(s) => s.generate(&mut Cli::into_app(), bin_name),
|
||||
Commands::GenerateFigSpec => fig_generate(&mut Cli::into_app(), bin_name),
|
||||
Commands::Completions(s) => s.generate(&mut Cli::command(), bin_name),
|
||||
Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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 {
|
||||
config
|
||||
.get_base_mut()
|
||||
.set_custom_nym_apis(parse_validators(&raw_validators));
|
||||
} else if let Ok(raw_validators) = std::env::var(network_defaults::var_names::API_VALIDATOR) {
|
||||
config
|
||||
.get_base_mut()
|
||||
.set_custom_nym_apis(parse_validators(&raw_validators));
|
||||
}
|
||||
|
||||
if args.use_anonymous_sender_tag {
|
||||
config = config.with_anonymous_replies(true)
|
||||
}
|
||||
|
||||
if let Some(port) = args.port {
|
||||
config = config.with_port(port);
|
||||
}
|
||||
config = config
|
||||
.with_base(BaseConfig::with_high_default_traffic_volume, args.fastmode)
|
||||
.with_base(BaseConfig::with_disabled_cover_traffic, args.no_cover)
|
||||
.with_optional(Config::with_anonymous_replies, args.use_anonymous_replies)
|
||||
.with_optional(Config::with_port, args.port)
|
||||
.with_optional_custom_env_ext(
|
||||
BaseConfig::with_custom_nym_apis,
|
||||
args.nym_apis,
|
||||
network_defaults::var_names::NYM_API,
|
||||
config::parse_urls,
|
||||
);
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
{
|
||||
if args.enabled_credentials_mode {
|
||||
config.get_base_mut().with_disabled_credentials(false)
|
||||
}
|
||||
}
|
||||
|
||||
if args.fastmode {
|
||||
config.get_base_mut().set_high_default_traffic_volume();
|
||||
}
|
||||
|
||||
if args.no_cover {
|
||||
config.get_base_mut().set_no_cover_traffic();
|
||||
config = config
|
||||
.with_optional_custom_env_ext(
|
||||
BaseConfig::with_custom_nyxd,
|
||||
args.nyxd_urls,
|
||||
network_defaults::var_names::NYXD,
|
||||
config::parse_urls,
|
||||
)
|
||||
.with_optional_ext(
|
||||
BaseConfig::with_disabled_credentials,
|
||||
args.enabled_credentials_mode.map(|b| !b),
|
||||
);
|
||||
}
|
||||
|
||||
config
|
||||
|
||||
@@ -9,7 +9,9 @@ use crate::{
|
||||
|
||||
use clap::Args;
|
||||
use config::NymConfig;
|
||||
use crypto::asymmetric::identity;
|
||||
use log::*;
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
use version_checker::is_minor_version_compatible;
|
||||
|
||||
#[derive(Args, Clone)]
|
||||
@@ -27,25 +29,27 @@ pub(crate) struct Run {
|
||||
/// slower and consume nearly double the bandwidth as it will require sending reply SURBs.
|
||||
///
|
||||
/// Note that some service providers might not support this.
|
||||
#[clap(long)]
|
||||
use_anonymous_sender_tag: bool,
|
||||
// the alias here is included for backwards compatibility (1.1.4 and before)
|
||||
#[clap(long, alias = "use_anonymous_sender_tag")]
|
||||
use_anonymous_replies: Option<bool>,
|
||||
|
||||
/// Address of the socks5 provider to send messages to.
|
||||
#[clap(long)]
|
||||
provider: Option<String>,
|
||||
provider: Option<Recipient>,
|
||||
|
||||
/// Id of the gateway we want to connect to. If overridden, it is user's responsibility to
|
||||
/// ensure prior registration happened
|
||||
#[clap(long)]
|
||||
gateway: Option<String>,
|
||||
gateway: Option<identity::PublicKey>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the nymd validators
|
||||
#[clap(long)]
|
||||
nymd_validators: Option<String>,
|
||||
/// Comma separated list of rest endpoints of the nyxd validators
|
||||
#[cfg(feature = "coconut")]
|
||||
#[clap(long, alias = "nymd_validators", value_delimiter = ',')]
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the Nym APIs
|
||||
#[clap(long)]
|
||||
nym_apis: Option<String>,
|
||||
#[clap(long, value_delimiter = ',')]
|
||||
nym_apis: Option<Vec<url::Url>>,
|
||||
|
||||
/// Port for the socket to listen on
|
||||
#[clap(short, long)]
|
||||
@@ -53,29 +57,31 @@ pub(crate) struct Run {
|
||||
|
||||
/// Mostly debug-related option to increase default traffic rate so that you would not need to
|
||||
/// modify config post init
|
||||
#[clap(long, hidden = true)]
|
||||
#[clap(long, hide = true)]
|
||||
fastmode: bool,
|
||||
|
||||
/// Disable loop cover traffic and the Poisson rate limiter (for debugging only)
|
||||
#[clap(long, hidden = true)]
|
||||
#[clap(long, hide = true)]
|
||||
no_cover: bool,
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement.
|
||||
#[cfg(feature = "coconut")]
|
||||
#[clap(long)]
|
||||
enabled_credentials_mode: bool,
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
}
|
||||
|
||||
impl From<Run> for OverrideConfig {
|
||||
fn from(run_config: Run) -> Self {
|
||||
OverrideConfig {
|
||||
nymd_validators: run_config.nymd_validators,
|
||||
api_validators: run_config.nym_apis,
|
||||
nym_apis: run_config.nym_apis,
|
||||
port: run_config.port,
|
||||
use_anonymous_sender_tag: run_config.use_anonymous_sender_tag,
|
||||
use_anonymous_replies: run_config.use_anonymous_replies,
|
||||
fastmode: run_config.fastmode,
|
||||
no_cover: run_config.no_cover,
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
nyxd_urls: run_config.nyxd_urls,
|
||||
#[cfg(feature = "coconut")]
|
||||
enabled_credentials_mode: run_config.enabled_credentials_mode,
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::socks::types::SocksProxyError;
|
||||
use client_core::client::replies::reply_storage::fs_backend;
|
||||
use client_core::error::ClientCoreError;
|
||||
use socks5_requests::ConnectionId;
|
||||
|
||||
@@ -9,7 +8,7 @@ pub enum Socks5ClientError {
|
||||
IoError(#[from] std::io::Error),
|
||||
|
||||
#[error("client-core error: {0}")]
|
||||
ClientCoreError(#[from] ClientCoreError<fs_backend::Backend>),
|
||||
ClientCoreError(#[from] ClientCoreError),
|
||||
|
||||
#[error("SOCKS proxy error")]
|
||||
SocksProxyError(SocksProxyError),
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use std::error::Error;
|
||||
|
||||
use clap::{crate_version, Parser};
|
||||
use clap::{crate_name, crate_version, Parser};
|
||||
use logging::setup_logging;
|
||||
use network_defaults::setup_env;
|
||||
|
||||
@@ -15,26 +15,9 @@ pub mod socks;
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
setup_logging();
|
||||
println!("{}", banner());
|
||||
println!("{}", logging::banner(crate_name!(), crate_version!()));
|
||||
|
||||
let args = commands::Cli::parse();
|
||||
setup_env(args.config_env_file.clone());
|
||||
setup_env(args.config_env_file.as_ref());
|
||||
commands::execute(&args).await
|
||||
}
|
||||
|
||||
fn banner() -> String {
|
||||
format!(
|
||||
r#"
|
||||
|
||||
_ __ _ _ _ __ ___
|
||||
| '_ \| | | | '_ \ _ \
|
||||
| | | | |_| | | | | | |
|
||||
|_| |_|\__, |_| |_| |_|
|
||||
|___/
|
||||
|
||||
(socks5 proxy - version {:})
|
||||
|
||||
"#,
|
||||
crate_version!()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use futures::channel::mpsc;
|
||||
use futures::StreamExt;
|
||||
use log::*;
|
||||
@@ -116,10 +114,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");
|
||||
self.shutdown.recv_timeout().await;
|
||||
log::debug!("MixnetResponseListener: Exiting");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ export default class ValidatorClient implements INymClient {
|
||||
|
||||
static async connect(
|
||||
mnemonic: string,
|
||||
nymdUrl: string,
|
||||
nyxdUrl: string,
|
||||
nymApiUrl: string,
|
||||
prefix: string,
|
||||
mixnetContract: string,
|
||||
@@ -94,19 +94,19 @@ export default class ValidatorClient implements INymClient {
|
||||
): Promise<ValidatorClient> {
|
||||
const wallet = await ValidatorClient.buildWallet(mnemonic, prefix);
|
||||
|
||||
const signingClient = await SigningClient.connectWithNymSigner(wallet, nymdUrl, nymApiUrl, prefix, denom);
|
||||
const signingClient = await SigningClient.connectWithNymSigner(wallet, nyxdUrl, nymApiUrl, prefix, denom);
|
||||
return new ValidatorClient(signingClient, prefix, mixnetContract, vestingContract, denom);
|
||||
}
|
||||
|
||||
static async connectForQuery(
|
||||
nymdUrl: string,
|
||||
nyxdUrl: string,
|
||||
nymApiUrl: string,
|
||||
prefix: string,
|
||||
mixnetContract: string,
|
||||
vestingContract: string,
|
||||
denom: string,
|
||||
): Promise<ValidatorClient> {
|
||||
const queryClient = await QueryClient.connectWithNym(nymdUrl, nymApiUrl);
|
||||
const queryClient = await QueryClient.connectWithNym(nyxdUrl, nymApiUrl);
|
||||
return new ValidatorClient(queryClient, prefix, mixnetContract, vestingContract, denom);
|
||||
}
|
||||
|
||||
@@ -191,11 +191,11 @@ export default class ValidatorClient implements INymClient {
|
||||
return this.client.getIntervalRewardPercent(this.mixnetContract);
|
||||
}
|
||||
|
||||
public async getAllNymdMixnodes(): Promise<MixNodeBond[]> {
|
||||
public async getAllNyxdMixnodes(): Promise<MixNodeBond[]> {
|
||||
let mixNodes: MixNodeBond[] = [];
|
||||
const limit = 50;
|
||||
let startAfter;
|
||||
for (;;) {
|
||||
for (; ;) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const pagedResponse: PagedMixnodeResponse = await this.client.getMixNodesPaged(this.mixnetContract, limit);
|
||||
mixNodes = mixNodes.concat(pagedResponse.nodes);
|
||||
@@ -209,11 +209,11 @@ export default class ValidatorClient implements INymClient {
|
||||
return mixNodes;
|
||||
}
|
||||
|
||||
public async getAllNymdGateways(): Promise<GatewayBond[]> {
|
||||
public async getAllNyxdGateways(): Promise<GatewayBond[]> {
|
||||
let gateways: GatewayBond[] = [];
|
||||
const limit = 50;
|
||||
let startAfter;
|
||||
for (;;) {
|
||||
for (; ;) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const pagedResponse: PagedGatewayResponse = await this.client.getGatewaysPaged(this.mixnetContract, limit);
|
||||
gateways = gateways.concat(pagedResponse.nodes);
|
||||
@@ -232,11 +232,11 @@ export default class ValidatorClient implements INymClient {
|
||||
*
|
||||
* @param mixIdentity identity of the node to which the delegation was sent
|
||||
*/
|
||||
public async getAllNymdSingleMixnodeDelegations(mixIdentity: string): Promise<Delegation[]> {
|
||||
public async getAllNyxdSingleMixnodeDelegations(mixIdentity: string): Promise<Delegation[]> {
|
||||
let delegations: Delegation[] = [];
|
||||
const limit = 250;
|
||||
let startAfter;
|
||||
for (;;) {
|
||||
for (; ;) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const pagedResponse: PagedMixDelegationsResponse = await this.client.getMixNodeDelegationsPaged(
|
||||
this.mixnetContract,
|
||||
@@ -255,11 +255,11 @@ export default class ValidatorClient implements INymClient {
|
||||
return delegations;
|
||||
}
|
||||
|
||||
public async getAllNymdDelegatorDelegations(delegator: string): Promise<Delegation[]> {
|
||||
public async getAllNyxdDelegatorDelegations(delegator: string): Promise<Delegation[]> {
|
||||
let delegations: Delegation[] = [];
|
||||
const limit = 250;
|
||||
let startAfter;
|
||||
for (;;) {
|
||||
for (; ;) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const pagedResponse: PagedDelegatorDelegationsResponse = await this.client.getDelegatorDelegationsPaged(
|
||||
this.mixnetContract,
|
||||
@@ -278,11 +278,11 @@ export default class ValidatorClient implements INymClient {
|
||||
return delegations;
|
||||
}
|
||||
|
||||
public async getAllNymdNetworkDelegations(): Promise<Delegation[]> {
|
||||
public async getAllNyxdNetworkDelegations(): Promise<Delegation[]> {
|
||||
let delegations: Delegation[] = [];
|
||||
const limit = 250;
|
||||
let startAfter;
|
||||
for (;;) {
|
||||
for (; ;) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const pagedResponse: PagedAllDelegationsResponse = await this.client.getAllNetworkDelegationsPaged(
|
||||
this.mixnetContract,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { JsonObject } from '@cosmjs/cosmwasm-stargate';
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import { INymdQuery } from './query-client';
|
||||
import { INyxdQuery } from './query-client';
|
||||
import {
|
||||
ContractStateParams,
|
||||
Delegation,
|
||||
@@ -25,7 +25,7 @@ interface SmartContractQuery {
|
||||
queryContractSmart(address: string, queryMsg: Record<string, unknown>): Promise<JsonObject>;
|
||||
}
|
||||
|
||||
export default class NymdQuerier implements INymdQuery {
|
||||
export default class NyxdQuerier implements INyxdQuery {
|
||||
client: SmartContractQuery;
|
||||
|
||||
constructor(client: SmartContractQuery) {
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
} from '@cosmjs/stargate';
|
||||
import { Code, CodeDetails, Contract, ContractCodeHistoryEntry } from '@cosmjs/cosmwasm-stargate/build/cosmwasmclient';
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import NymdQuerier from './nymd-querier';
|
||||
import NyxdQuerier from './nyxd-querier';
|
||||
import {
|
||||
ContractStateParams,
|
||||
Delegation,
|
||||
@@ -52,7 +52,7 @@ export interface ICosmWasmQuery {
|
||||
queryContractSmart(address: string, queryMsg: Record<string, unknown>): Promise<JsonObject>;
|
||||
}
|
||||
|
||||
export interface INymdQuery {
|
||||
export interface INyxdQuery {
|
||||
// nym-specific implemented inside NymQuerier
|
||||
getContractVersion(mixnetContractAddress: string): Promise<MixnetContractVersion>;
|
||||
|
||||
@@ -93,46 +93,46 @@ export interface INymdQuery {
|
||||
): Promise<RewardingStatus>;
|
||||
}
|
||||
|
||||
export interface IQueryClient extends ICosmWasmQuery, INymdQuery, INymApiQuery { }
|
||||
export interface IQueryClient extends ICosmWasmQuery, INyxdQuery, INymApiQuery { }
|
||||
|
||||
export default class QueryClient extends CosmWasmClient implements IQueryClient {
|
||||
private nymdQuerier: NymdQuerier;
|
||||
private nyxdQuerier: NyxdQuerier;
|
||||
|
||||
private nymApiQuerier: NymApiQuerier;
|
||||
|
||||
private constructor(tmClient: Tendermint34Client, nymApiUrl: string) {
|
||||
super(tmClient);
|
||||
this.nymdQuerier = new NymdQuerier(this);
|
||||
this.nyxdQuerier = new NyxdQuerier(this);
|
||||
this.nymApiQuerier = new NymApiQuerier(nymApiUrl);
|
||||
}
|
||||
|
||||
public static async connectWithNym(nymdUrl: string, nymApiUrl: string): Promise<QueryClient> {
|
||||
const tmClient = await Tendermint34Client.connect(nymdUrl);
|
||||
public static async connectWithNym(nyxdUrl: string, nymApiUrl: string): Promise<QueryClient> {
|
||||
const tmClient = await Tendermint34Client.connect(nyxdUrl);
|
||||
return new QueryClient(tmClient, nymApiUrl);
|
||||
}
|
||||
|
||||
getContractVersion(mixnetContractAddress: string): Promise<MixnetContractVersion> {
|
||||
return this.nymdQuerier.getContractVersion(mixnetContractAddress);
|
||||
return this.nyxdQuerier.getContractVersion(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getMixNodesPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedMixnodeResponse> {
|
||||
return this.nymdQuerier.getMixNodesPaged(mixnetContractAddress, limit, startAfter);
|
||||
return this.nyxdQuerier.getMixNodesPaged(mixnetContractAddress, limit, startAfter);
|
||||
}
|
||||
|
||||
getGatewaysPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedGatewayResponse> {
|
||||
return this.nymdQuerier.getGatewaysPaged(mixnetContractAddress, limit, startAfter);
|
||||
return this.nyxdQuerier.getGatewaysPaged(mixnetContractAddress, limit, startAfter);
|
||||
}
|
||||
|
||||
ownsMixNode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse> {
|
||||
return this.nymdQuerier.ownsMixNode(mixnetContractAddress, address);
|
||||
return this.nyxdQuerier.ownsMixNode(mixnetContractAddress, address);
|
||||
}
|
||||
|
||||
ownsGateway(mixnetContractAddress: string, address: string): Promise<GatewayOwnershipResponse> {
|
||||
return this.nymdQuerier.ownsGateway(mixnetContractAddress, address);
|
||||
return this.nyxdQuerier.ownsGateway(mixnetContractAddress, address);
|
||||
}
|
||||
|
||||
getStateParams(mixnetContractAddress: string): Promise<ContractStateParams> {
|
||||
return this.nymdQuerier.getStateParams(mixnetContractAddress);
|
||||
return this.nyxdQuerier.getStateParams(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getAllNetworkDelegationsPaged(
|
||||
@@ -140,7 +140,7 @@ export default class QueryClient extends CosmWasmClient implements IQueryClient
|
||||
limit?: number,
|
||||
startAfter?: [string, string],
|
||||
): Promise<PagedAllDelegationsResponse> {
|
||||
return this.nymdQuerier.getAllNetworkDelegationsPaged(mixnetContractAddress, limit, startAfter);
|
||||
return this.nyxdQuerier.getAllNetworkDelegationsPaged(mixnetContractAddress, limit, startAfter);
|
||||
}
|
||||
|
||||
getMixNodeDelegationsPaged(
|
||||
@@ -149,7 +149,7 @@ export default class QueryClient extends CosmWasmClient implements IQueryClient
|
||||
limit?: number,
|
||||
startAfter?: string,
|
||||
): Promise<PagedMixDelegationsResponse> {
|
||||
return this.nymdQuerier.getMixNodeDelegationsPaged(mixnetContractAddress, mixIdentity, limit, startAfter);
|
||||
return this.nyxdQuerier.getMixNodeDelegationsPaged(mixnetContractAddress, mixIdentity, limit, startAfter);
|
||||
}
|
||||
|
||||
getDelegatorDelegationsPaged(
|
||||
@@ -158,31 +158,31 @@ export default class QueryClient extends CosmWasmClient implements IQueryClient
|
||||
limit?: number,
|
||||
startAfter?: string,
|
||||
): Promise<PagedDelegatorDelegationsResponse> {
|
||||
return this.nymdQuerier.getDelegatorDelegationsPaged(mixnetContractAddress, delegator, limit, startAfter);
|
||||
return this.nyxdQuerier.getDelegatorDelegationsPaged(mixnetContractAddress, delegator, limit, startAfter);
|
||||
}
|
||||
|
||||
getDelegationDetails(mixnetContractAddress: string, mixIdentity: string, delegator: string): Promise<Delegation> {
|
||||
return this.nymdQuerier.getDelegationDetails(mixnetContractAddress, mixIdentity, delegator);
|
||||
return this.nyxdQuerier.getDelegationDetails(mixnetContractAddress, mixIdentity, delegator);
|
||||
}
|
||||
|
||||
getLayerDistribution(mixnetContractAddress: string): Promise<LayerDistribution> {
|
||||
return this.nymdQuerier.getLayerDistribution(mixnetContractAddress);
|
||||
return this.nyxdQuerier.getLayerDistribution(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getRewardPool(mixnetContractAddress: string): Promise<string> {
|
||||
return this.nymdQuerier.getRewardPool(mixnetContractAddress);
|
||||
return this.nyxdQuerier.getRewardPool(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getCirculatingSupply(mixnetContractAddress: string): Promise<string> {
|
||||
return this.nymdQuerier.getCirculatingSupply(mixnetContractAddress);
|
||||
return this.nyxdQuerier.getCirculatingSupply(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number> {
|
||||
return this.nymdQuerier.getIntervalRewardPercent(mixnetContractAddress);
|
||||
return this.nyxdQuerier.getIntervalRewardPercent(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getSybilResistancePercent(mixnetContractAddress: string): Promise<number> {
|
||||
return this.nymdQuerier.getSybilResistancePercent(mixnetContractAddress);
|
||||
return this.nyxdQuerier.getSybilResistancePercent(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getRewardingStatus(
|
||||
@@ -190,7 +190,7 @@ export default class QueryClient extends CosmWasmClient implements IQueryClient
|
||||
mixIdentity: string,
|
||||
rewardingIntervalNonce: number,
|
||||
): Promise<RewardingStatus> {
|
||||
return this.nymdQuerier.getRewardingStatus(mixnetContractAddress, mixIdentity, rewardingIntervalNonce);
|
||||
return this.nyxdQuerier.getRewardingStatus(mixnetContractAddress, mixIdentity, rewardingIntervalNonce);
|
||||
}
|
||||
|
||||
getCachedGateways(): Promise<GatewayBond[]> {
|
||||
|
||||
@@ -14,7 +14,7 @@ import { ChangeAdminResult } from '@cosmjs/cosmwasm-stargate/build/signingcosmwa
|
||||
import { TxRaw } from 'cosmjs-types/cosmos/tx/v1beta1/tx';
|
||||
import { nymGasPrice } from './stargate-helper';
|
||||
import { IQueryClient } from './query-client';
|
||||
import NymdQuerier from './nymd-querier';
|
||||
import NyxdQuerier from './nyxd-querier';
|
||||
import {
|
||||
ContractStateParams,
|
||||
Delegation,
|
||||
@@ -197,7 +197,7 @@ export interface ISigningClient extends IQueryClient, ICosmWasmSigning, INymSign
|
||||
}
|
||||
|
||||
export default class SigningClient extends SigningCosmWasmClient implements ISigningClient {
|
||||
private nymdQuerier: NymdQuerier;
|
||||
private nyxdQuerier: NyxdQuerier;
|
||||
|
||||
private nymApiQuerier: NymApiQuerier;
|
||||
|
||||
@@ -212,13 +212,13 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
|
||||
) {
|
||||
super(tmClient, wallet, signerOptions);
|
||||
this.clientAddress = clientAddress;
|
||||
this.nymdQuerier = new NymdQuerier(this);
|
||||
this.nyxdQuerier = new NyxdQuerier(this);
|
||||
this.nymApiQuerier = new NymApiQuerier(nymApiUrl);
|
||||
}
|
||||
|
||||
public static async connectWithNymSigner(
|
||||
wallet: DirectSecp256k1HdWallet,
|
||||
nymdUrl: string,
|
||||
nyxdUrl: string,
|
||||
nymApiUrl: string,
|
||||
prefix: string,
|
||||
denom: string,
|
||||
@@ -228,34 +228,34 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
|
||||
prefix,
|
||||
gasPrice: nymGasPrice(denom),
|
||||
};
|
||||
const tmClient = await Tendermint34Client.connect(nymdUrl);
|
||||
const tmClient = await Tendermint34Client.connect(nyxdUrl);
|
||||
return new SigningClient(address, nymApiUrl, tmClient, wallet, signerOptions);
|
||||
}
|
||||
|
||||
// query related:
|
||||
|
||||
getContractVersion(mixnetContractAddress: string): Promise<MixnetContractVersion> {
|
||||
return this.nymdQuerier.getContractVersion(mixnetContractAddress);
|
||||
return this.nyxdQuerier.getContractVersion(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getMixNodesPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedMixnodeResponse> {
|
||||
return this.nymdQuerier.getMixNodesPaged(mixnetContractAddress, limit, startAfter);
|
||||
return this.nyxdQuerier.getMixNodesPaged(mixnetContractAddress, limit, startAfter);
|
||||
}
|
||||
|
||||
getGatewaysPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedGatewayResponse> {
|
||||
return this.nymdQuerier.getGatewaysPaged(mixnetContractAddress, limit, startAfter);
|
||||
return this.nyxdQuerier.getGatewaysPaged(mixnetContractAddress, limit, startAfter);
|
||||
}
|
||||
|
||||
ownsMixNode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse> {
|
||||
return this.nymdQuerier.ownsMixNode(mixnetContractAddress, address);
|
||||
return this.nyxdQuerier.ownsMixNode(mixnetContractAddress, address);
|
||||
}
|
||||
|
||||
ownsGateway(mixnetContractAddress: string, address: string): Promise<GatewayOwnershipResponse> {
|
||||
return this.nymdQuerier.ownsGateway(mixnetContractAddress, address);
|
||||
return this.nyxdQuerier.ownsGateway(mixnetContractAddress, address);
|
||||
}
|
||||
|
||||
getStateParams(mixnetContractAddress: string): Promise<ContractStateParams> {
|
||||
return this.nymdQuerier.getStateParams(mixnetContractAddress);
|
||||
return this.nyxdQuerier.getStateParams(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getAllNetworkDelegationsPaged(
|
||||
@@ -263,7 +263,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
|
||||
limit?: number,
|
||||
startAfter?: [string, string],
|
||||
): Promise<PagedAllDelegationsResponse> {
|
||||
return this.nymdQuerier.getAllNetworkDelegationsPaged(mixnetContractAddress, limit, startAfter);
|
||||
return this.nyxdQuerier.getAllNetworkDelegationsPaged(mixnetContractAddress, limit, startAfter);
|
||||
}
|
||||
|
||||
getMixNodeDelegationsPaged(
|
||||
@@ -272,7 +272,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
|
||||
limit?: number,
|
||||
startAfter?: string,
|
||||
): Promise<PagedMixDelegationsResponse> {
|
||||
return this.nymdQuerier.getMixNodeDelegationsPaged(mixnetContractAddress, mixIdentity, limit, startAfter);
|
||||
return this.nyxdQuerier.getMixNodeDelegationsPaged(mixnetContractAddress, mixIdentity, limit, startAfter);
|
||||
}
|
||||
|
||||
getDelegatorDelegationsPaged(
|
||||
@@ -281,31 +281,31 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
|
||||
limit?: number,
|
||||
startAfter?: string,
|
||||
): Promise<PagedDelegatorDelegationsResponse> {
|
||||
return this.nymdQuerier.getDelegatorDelegationsPaged(mixnetContractAddress, delegator, limit, startAfter);
|
||||
return this.nyxdQuerier.getDelegatorDelegationsPaged(mixnetContractAddress, delegator, limit, startAfter);
|
||||
}
|
||||
|
||||
getDelegationDetails(mixnetContractAddress: string, mixIdentity: string, delegator: string): Promise<Delegation> {
|
||||
return this.nymdQuerier.getDelegationDetails(mixnetContractAddress, mixIdentity, delegator);
|
||||
return this.nyxdQuerier.getDelegationDetails(mixnetContractAddress, mixIdentity, delegator);
|
||||
}
|
||||
|
||||
getLayerDistribution(mixnetContractAddress: string): Promise<LayerDistribution> {
|
||||
return this.nymdQuerier.getLayerDistribution(mixnetContractAddress);
|
||||
return this.nyxdQuerier.getLayerDistribution(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getRewardPool(mixnetContractAddress: string): Promise<string> {
|
||||
return this.nymdQuerier.getRewardPool(mixnetContractAddress);
|
||||
return this.nyxdQuerier.getRewardPool(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getCirculatingSupply(mixnetContractAddress: string): Promise<string> {
|
||||
return this.nymdQuerier.getCirculatingSupply(mixnetContractAddress);
|
||||
return this.nyxdQuerier.getCirculatingSupply(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number> {
|
||||
return this.nymdQuerier.getIntervalRewardPercent(mixnetContractAddress);
|
||||
return this.nyxdQuerier.getIntervalRewardPercent(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getSybilResistancePercent(mixnetContractAddress: string): Promise<number> {
|
||||
return this.nymdQuerier.getSybilResistancePercent(mixnetContractAddress);
|
||||
return this.nyxdQuerier.getSybilResistancePercent(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getRewardingStatus(
|
||||
@@ -313,7 +313,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
|
||||
mixIdentity: string,
|
||||
rewardingIntervalNonce: number,
|
||||
): Promise<RewardingStatus> {
|
||||
return this.nymdQuerier.getRewardingStatus(mixnetContractAddress, mixIdentity, rewardingIntervalNonce);
|
||||
return this.nyxdQuerier.getRewardingStatus(mixnetContractAddress, mixIdentity, rewardingIntervalNonce);
|
||||
}
|
||||
|
||||
getCachedGateways(): Promise<GatewayBond[]> {
|
||||
|
||||
@@ -22,8 +22,10 @@ futures = "0.3"
|
||||
js-sys = "0.3"
|
||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
anyhow = "1.0"
|
||||
serde-wasm-bindgen = "0.4"
|
||||
tokio = { version = "1.21.2", features = ["sync"] }
|
||||
tokio = { version = "1.24.1", features = ["sync"] }
|
||||
url = "2.2"
|
||||
wasm-bindgen = { version = "=0.2.83", features = ["serde-serialize"] }
|
||||
wasm-bindgen-futures = "0.4"
|
||||
|
||||
@@ -16,11 +16,7 @@ They should be implemented soon. You can build your applications, but don't rely
|
||||
|
||||
## Using it
|
||||
|
||||
See the [Nym docs](https://nymtech.net/docs).
|
||||
|
||||
### Demo
|
||||
|
||||
There's a demo web application in the `js-example` folder. To run it, first make sure you've got a recent `npm` installed, then follow the instructions in its README.
|
||||
See the [SDK directory](../../sdk/typescript/examples) for examples on how to use it and the NPM packages available.
|
||||
|
||||
## Developing
|
||||
|
||||
@@ -36,8 +32,13 @@ To be clear, this is not something that most JS developers need to worry about,
|
||||
|
||||
### Packaging
|
||||
|
||||
If you're a Nym platform developer who's made changes to the Rust (or JS) files and wants to re-publish the package to NPM, here's how you do it:
|
||||
If you're a Nym platform developer who's made changes to the Rust files and wants to re-publish the package to NPM, here's how you do it:
|
||||
|
||||
1. bump version numbers as necessary for SemVer
|
||||
2. `wasm-pack build --scope nymproject` builds the wasm binaries into the `pkg` directory (not in source control)
|
||||
3. `cd pkg && npm publish --access=public` will publish your changed package to NPM
|
||||
2. go the `sdk/typescript` directory (off the project root)
|
||||
3. run:
|
||||
```
|
||||
yarn
|
||||
yarn build
|
||||
yarn publish
|
||||
```
|
||||
@@ -1,24 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { spawn } = require("child_process");
|
||||
const fs = require("fs");
|
||||
|
||||
let folderName = '.';
|
||||
|
||||
if (process.argv.length >= 3) {
|
||||
folderName = process.argv[2];
|
||||
if (!fs.existsSync(folderName)) {
|
||||
fs.mkdirSync(folderName);
|
||||
}
|
||||
}
|
||||
|
||||
const clone = spawn("git", ["clone", "https://github.com/rustwasm/create-wasm-app.git", folderName]);
|
||||
|
||||
clone.on("close", code => {
|
||||
if (code !== 0) {
|
||||
console.error("cloning the template failed!")
|
||||
process.exit(code);
|
||||
} else {
|
||||
console.log("🦀 Rust + 🕸 Wasm = ❤");
|
||||
}
|
||||
});
|
||||
@@ -1,2 +0,0 @@
|
||||
node_modules
|
||||
dist
|
||||
@@ -1,5 +0,0 @@
|
||||
language: node_js
|
||||
node_js: "10"
|
||||
|
||||
script:
|
||||
- ./node_modules/.bin/webpack
|
||||
@@ -1,201 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -1,28 +0,0 @@
|
||||
# Nym Sphinx Wasm Demo
|
||||
|
||||
This example application demonstrates how to use WebAssembly to create Sphinx packets, in the browser, and forward them to a Nym gateway.
|
||||
|
||||
## 🚴 Usage
|
||||
|
||||
Build the WASM package for bundling:
|
||||
|
||||
```
|
||||
wasm-pack build --scope nymproject --target no-modules
|
||||
```
|
||||
|
||||
in the `clients/webassembly` directory (one up).
|
||||
|
||||
Start the webpack dev server:
|
||||
|
||||
```
|
||||
npm install # set up dependencies
|
||||
npm run start # starts a web server at http://localhost:8001
|
||||
```
|
||||
|
||||
Check your dev console for output.
|
||||
|
||||
### Rebuild after Rust source changes
|
||||
|
||||
Install `wasm-pack`. Instruction are at the [Rust WASM tutorial](https://rustwasm.github.io/docs/book/game-of-life/hello-world.html).
|
||||
|
||||
`wasm-pack build --scope nymproject --target no-modules` in the `clients/webassembly` directory (one up) will rebuild the wasm package if you make changes to the Rust source. That will be automatically picked up (and reloaded, if need be) by the npm dev server.
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
// A dependency graph that contains any wasm must all be imported
|
||||
// asynchronously. This `bootstrap.js` file does the single async import, so
|
||||
// that no one else needs to worry about it again.
|
||||
import('./index.js')
|
||||
.catch(e => console.error('Error importing `index.js`:', e));
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user