Compare commits

..

1 Commits

Author SHA1 Message Date
Fouad eff1f383c3 Feature/node settings apy playground (#1677) (#2738)
* initial ui for test my node

use svg for node path

add stories for test my node

* add initial rewards calculation

* update validation for rewards playground

* init playground with default values

* get node uptime

* get mixnode reward estimation

* calculate saturation

calculate stake saturation

* Make ComputeRewardEstParam derive Debug

* set active set to be always true

Co-authored-by: Jon Häggblad <jon.haggblad@gmail.com>

Co-authored-by: Jon Häggblad <jon.haggblad@gmail.com>
2022-12-21 12:56:40 +00:00
573 changed files with 28318 additions and 13205 deletions
+1 -1
View File
@@ -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-City
GEOIPUPDATE_EDITION_IDS=GeoLite2-Country
# 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
-5
View File
@@ -47,11 +47,6 @@ 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
-5
View File
@@ -58,11 +58,6 @@ 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
+1 -1
View File
@@ -11,7 +11,7 @@ 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:
-5
View File
@@ -73,11 +73,6 @@ 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
-5
View File
@@ -183,11 +183,6 @@ 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
@@ -198,11 +198,6 @@ 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
@@ -198,11 +198,6 @@ 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
-5
View File
@@ -54,11 +54,6 @@ 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
@@ -50,11 +50,6 @@ 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,13 +2,6 @@ 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
+1 -3
View File
@@ -2,6 +2,4 @@ node_modules
.idea
# don't commit the lock file to avoid cross-platform issues
package-lock.json
scratch
package-lock.json
@@ -1,7 +1,6 @@
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'],
@@ -39,28 +38,6 @@ 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',
);
}
}
}
/**
@@ -170,13 +147,6 @@ 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
@@ -1,80 +0,0 @@
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,
};
+1 -9
View File
@@ -11,15 +11,7 @@
"dotenv": "^16.0.0",
"handlebars": "^4.7.7",
"keybase-bot": "^3.6.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"
"octokit": "^1.7.1"
},
"devDependencies": {
"prettier": "2.3.2"
+1 -3
View File
@@ -38,6 +38,4 @@ validator-config
validator-api-config.toml
dist
storybook-static
envs/qwerty.env
Cargo.lock
nym-connect/Cargo.lock
envs/qwerty.env
+5 -7
View File
@@ -1,8 +1,6 @@
{
"mainnet": [
{
"nyxd_url": "https://rpc.nyx.nodes.guru/",
"api_url": "https://api.nyx.nodes.guru/"
}
]
}
"mainnet":[{
"nymd_url":"https://rpc.nyx.nodes.guru/",
"api_url":"https://api.nyx.nodes.guru/"
}]
}
+40 -69
View File
@@ -4,48 +4,13 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
### Added
- nym-sdk: added initial version of a Rust client sdk
### Changed
- renamed all references to validator_api to nym_api
- renamed all references to nymd to nyxd
- all-binaries: standarised argument names (note: old names should still be accepted) ([#2762]
### Fixed
- nym-api: should now correctly use `rewarding.enabled` config flag ([#2753])
[#2753]: https://github.com/nymtech/nym/pull/2753
[#2762]: https://github.com/nymtech/nym/pull/2762
## [v1.1.5] (2022-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.
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.
The release also include some additional work for distributed key generation in the Coconut signing authority nodes.
### Changed
@@ -60,7 +25,7 @@ The release also include some additional work for distributed key generation in
### Changed
- validator-api: can recover from shutdown during DKG process ([#1872])
- clients: deduplicate gateway initialization, part of work towards a rust-sdk
- clients: deduplicate gateway inititialization, 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]))
@@ -68,7 +33,7 @@ The release also include some additional work for distributed key generation in
- 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])
@@ -88,7 +53,7 @@ The release also include some additional work for distributed key generation in
- 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)
@@ -124,6 +89,7 @@ The release also include some additional work for distributed key generation in
[#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
@@ -135,7 +101,7 @@ The release also include some additional work for distributed key generation in
- 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 `NyxdClient` ([#1558])
- validator-client: added `query_contract_smart` and `query_contract_raw` on `NymdClient` ([#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
@@ -150,7 +116,7 @@ The release also include some additional work for distributed key generation in
- 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])
@@ -178,6 +144,7 @@ The release also include some additional work for distributed key generation in
[#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
@@ -230,8 +197,9 @@ The release also include some additional work for distributed key generation in
- 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 nyxd 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 nymd 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
@@ -325,9 +293,9 @@ The release also include some additional work for distributed key generation in
- 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))
@@ -336,7 +304,7 @@ The release also include some additional work for distributed key generation in
- 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))
@@ -376,13 +344,14 @@ The release also include some additional work for distributed key generation in
- 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:**
@@ -412,7 +381,7 @@ The release also include some additional work for distributed key generation in
- 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:**
@@ -427,7 +396,7 @@ The release also include some additional work for distributed key generation in
- 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)
@@ -437,7 +406,7 @@ The release also include some additional work for distributed key generation in
- 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)
@@ -493,10 +462,10 @@ The release also include some additional work for distributed key generation in
- 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))
@@ -509,7 +478,7 @@ The release also include some additional work for distributed key generation in
- 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))
@@ -527,7 +496,7 @@ The release also include some additional work for distributed key generation in
- 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))
@@ -543,7 +512,7 @@ The release also include some additional work for distributed key generation in
- 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))
@@ -567,8 +536,8 @@ The release also include some additional work for distributed key generation in
- 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))
@@ -577,14 +546,14 @@ The release also include some additional work for distributed key generation in
- 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))
@@ -596,21 +565,21 @@ The release also include some additional work for distributed key generation in
- 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/nyxd client integration [\#736](https://github.com/nymtech/nym/pull/736) ([jstuczyn](https://github.com/jstuczyn))
- Feature/nymd 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 nyxd client api [\#732](https://github.com/nymtech/nym/pull/732) ([jstuczyn](https://github.com/jstuczyn))
- Feature/completing nymd 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/nyxd client fee handling [\#730](https://github.com/nymtech/nym/pull/730) ([jstuczyn](https://github.com/jstuczyn))
- Feature/nymd 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 nyxd/cosmwasm client [\#724](https://github.com/nymtech/nym/pull/724) ([jstuczyn](https://github.com/jstuczyn))
- Rust nymd/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))
@@ -629,8 +598,10 @@ The release also include some additional work for distributed key generation in
- 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 nyxd [\#690](https://github.com/nymtech/nym/pull/690) ([futurechimp](https://github.com/futurechimp))
- Using validator API instead of nymd [\#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
+851 -1268
View File
File diff suppressed because it is too large Load Diff
+3 -6
View File
@@ -30,7 +30,6 @@ 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",
@@ -73,12 +72,10 @@ 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",
"validator-api",
"validator-api/validator-api-requests",
"tools/nym-cli",
"tools/ts-rs-cli"
]
@@ -90,7 +87,7 @@ default-members = [
"service-providers/network-requester",
"service-providers/network-statistics",
"mixnode",
"nym-api",
"validator-api",
"explorer-api",
]
+4 -1
View File
@@ -28,7 +28,7 @@ clippy-coconut:
cargo clippy --workspace --features coconut -- -D warnings
clippy-wasm:
cargo clippy --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown --workspace -- -D warnings
cargo clippy --workspace --features wasm -- -D warnings
clippy-all-contracts:
@@ -49,6 +49,9 @@ test-main:
test-coconut:
cargo test --workspace --features coconut
test-wasm:
cargo test --workspace --features wasm
test-main-expensive:
cargo test --workspace -- --ignored
+1 -2
View File
@@ -1,9 +1,8 @@
[package]
name = "client-core"
version = "1.1.5"
version = "1.1.4"
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
+40 -103
View File
@@ -33,19 +33,17 @@ 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 task::{ShutdownListener, ShutdownNotifier};
use url::Url;
use super::received_buffer::ReceivedBufferMessage;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
pub mod non_wasm_helpers;
pub struct ClientInput {
pub shared_lane_queue_lengths: LaneQueueLengths,
pub connection_command_sender: ConnectionCommandSender,
pub input_sender: InputMessageSender,
}
@@ -54,27 +52,6 @@ pub struct ClientOutput {
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,
@@ -103,38 +80,12 @@ impl ClientOutputStatus {
}
}
#[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> {
// due to wasm limitations I had to split it like this : (
gateway_config: &'a GatewayEndpointConfig,
debug_config: &'a DebugConfig,
disabled_credentials: bool,
nym_api_endpoints: Vec<Url>,
validator_api_endpoints: Vec<Url>,
reply_storage_backend: B,
bandwidth_controller: Option<BandwidthController>,
@@ -155,7 +106,7 @@ where
gateway_config: base_config.get_gateway_endpoint_config(),
debug_config: base_config.get_debug_config(),
disabled_credentials: base_config.get_disabled_credentials_mode(),
nym_api_endpoints: base_config.get_nym_api_endpoints(),
validator_api_endpoints: base_config.get_validator_api_endpoints(),
bandwidth_controller,
reply_storage_backend,
key_manager,
@@ -168,16 +119,16 @@ where
key_manager: KeyManager,
bandwidth_controller: Option<BandwidthController>,
reply_storage_backend: B,
credentials_toggle: CredentialsToggle,
nym_api_endpoints: Vec<Url>,
disabled_credentials: bool,
validator_api_endpoints: Vec<Url>,
) -> BaseClientBuilder<'a, B> {
BaseClientBuilder {
gateway_config,
debug_config,
disabled_credentials: credentials_toggle.is_disabled(),
nym_api_endpoints,
reply_storage_backend,
disabled_credentials,
validator_api_endpoints,
bandwidth_controller,
reply_storage_backend,
key_manager,
}
}
@@ -200,7 +151,7 @@ where
self_address: Recipient,
topology_accessor: TopologyAccessor,
mix_tx: BatchMixMessageSender,
shutdown: TaskClient,
shutdown: ShutdownListener,
) {
info!("Starting loop cover traffic stream...");
@@ -234,7 +185,7 @@ where
reply_controller_receiver: ReplyControllerReceiver,
lane_queue_lengths: LaneQueueLengths,
client_connection_rx: ConnectionCommandReceiver,
shutdown: TaskClient,
shutdown: ShutdownListener,
) {
info!("Starting real traffic stream...");
@@ -261,7 +212,7 @@ where
mixnet_receiver: MixnetMessageReceiver,
reply_key_storage: SentReplyKeys,
reply_controller_sender: ReplyControllerSender,
shutdown: TaskClient,
shutdown: ShutdownListener,
) {
info!("Starting received messages buffer controller...");
ReceivedMessagesBufferController::new(
@@ -278,8 +229,8 @@ where
&mut self,
mixnet_message_sender: MixnetMessageSender,
ack_sender: AcknowledgementSender,
shutdown: TaskClient,
) -> Result<GatewayClient, ClientCoreError> {
shutdown: ShutdownListener,
) -> Result<GatewayClient, ClientCoreError<B>> {
let gateway_id = self.gateway_config.gateway_id.clone();
if gateway_id.is_empty() {
return Err(ClientCoreError::GatewayIdUnknown);
@@ -297,7 +248,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.is_gateway_key_set() {
let shared_key = if self.key_manager.gateway_key_set() {
Some(self.key_manager.gateway_shared_key())
} else {
None
@@ -330,13 +281,13 @@ where
// future responsible for periodically polling directory server and updating
// the current global view of topology
async fn start_topology_refresher(
nym_api_urls: Vec<Url>,
validator_api_urls: Vec<Url>,
refresh_rate: Duration,
topology_accessor: TopologyAccessor,
shutdown: TaskClient,
) -> Result<(), ClientCoreError> {
shutdown: ShutdownListener,
) -> Result<(), ClientCoreError<B>> {
let topology_refresher_config = TopologyRefresherConfig::new(
nym_api_urls,
validator_api_urls,
refresh_rate,
env!("CARGO_PKG_VERSION").to_string(),
);
@@ -366,7 +317,7 @@ where
// requests?
fn start_mix_traffic_controller(
gateway_client: GatewayClient,
shutdown: TaskClient,
shutdown: ShutdownListener,
) -> BatchMixMessageSender {
info!("Starting mix traffic controller...");
let (mix_traffic_controller, mix_tx) = MixTrafficController::new(gateway_client);
@@ -376,18 +327,13 @@ where
async fn setup_persistent_reply_storage(
backend: B,
shutdown: TaskClient,
) -> Result<CombinedReplyStorage, ClientCoreError>
where
<B as ReplyStorageBackend>::StorageError: Sync + Send,
{
shutdown: ShutdownListener,
) -> 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: Box::new(err),
})?;
.map_err(|err| ClientCoreError::SurbStorageError { source: err })?;
let store_clone = mem_store.clone();
spawn_future(async move {
@@ -399,10 +345,7 @@ where
Ok(mem_store)
}
pub async fn start_base(mut self) -> Result<BaseClient, ClientCoreError>
where
<B as ReplyStorageBackend>::StorageError: Sync + Send,
{
pub async fn start_base(mut self) -> Result<BaseClient, ClientCoreError<B>> {
info!("Starting nym client");
// channels for inter-component communication
// TODO: make the channels be internally created by the relevant components
@@ -424,31 +367,29 @@ where
let shared_topology_accessor = TopologyAccessor::new();
// Shutdown notifier for signalling tasks to stop
let task_manager = TaskManager::default();
let shutdown = ShutdownNotifier::default();
// channels responsible for dealing with reply-related fun
let (reply_controller_sender, reply_controller_receiver) =
reply_controller::requests::new_control_channels();
reply_controller::new_control_channels();
let self_address = self.as_mix_recipient();
// the components are started in very specific order. Unless you know what you are doing,
// do not change that.
let gateway_client = self
.start_gateway_client(mixnet_messages_sender, ack_sender, task_manager.subscribe())
.start_gateway_client(mixnet_messages_sender, ack_sender, shutdown.subscribe())
.await?;
let reply_storage = Self::setup_persistent_reply_storage(
self.reply_storage_backend,
task_manager.subscribe(),
)
.await?;
let reply_storage =
Self::setup_persistent_reply_storage(self.reply_storage_backend, shutdown.subscribe())
.await?;
Self::start_topology_refresher(
self.nym_api_endpoints.clone(),
self.validator_api_endpoints.clone(),
self.debug_config.topology_refresh_rate,
shared_topology_accessor.clone(),
task_manager.subscribe(),
shutdown.subscribe(),
)
.await?;
@@ -458,7 +399,7 @@ where
mixnet_messages_receiver,
reply_storage.key_storage(),
reply_controller_sender.clone(),
task_manager.subscribe(),
shutdown.subscribe(),
);
// The sphinx_message_sender is the transmitter for any component generating sphinx packets
@@ -466,7 +407,7 @@ where
// traffic stream.
// The MixTrafficController then sends the actual traffic
let sphinx_message_sender =
Self::start_mix_traffic_controller(gateway_client, task_manager.subscribe());
Self::start_mix_traffic_controller(gateway_client, shutdown.subscribe());
// Channels that the websocket listener can use to signal downstream to the real traffic
// controller that connections are closed.
@@ -494,11 +435,11 @@ where
input_receiver,
sphinx_message_sender.clone(),
reply_storage,
reply_controller_sender.clone(),
reply_controller_sender,
reply_controller_receiver,
shared_lane_queue_lengths.clone(),
client_connection_rx,
task_manager.subscribe(),
shutdown.subscribe(),
);
if !self.debug_config.disable_loop_cover_traffic_stream {
@@ -508,7 +449,7 @@ where
self_address,
shared_topology_accessor,
sphinx_message_sender,
task_manager.subscribe(),
shutdown.subscribe(),
);
}
@@ -518,6 +459,7 @@ where
Ok(BaseClient {
client_input: ClientInputStatus::AwaitingProducer {
client_input: ClientInput {
shared_lane_queue_lengths,
connection_command_sender: client_connection_tx,
input_sender,
},
@@ -527,11 +469,7 @@ where
received_buffer_request_sender,
},
},
client_state: ClientState {
shared_lane_queue_lengths,
reply_controller_sender,
},
task_manager,
shutdown_notifier: shutdown,
})
}
}
@@ -539,7 +477,6 @@ where
pub struct BaseClient {
pub client_input: ClientInputStatus,
pub client_output: ClientOutputStatus,
pub client_state: ClientState,
pub task_manager: TaskManager,
pub shutdown_notifier: ShutdownNotifier,
}
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::client::replies::reply_storage::{
self, fs_backend, CombinedReplyStorage, ReplyStorageBackend,
fs_backend, CombinedReplyStorage, ReplyStorageBackend,
};
use crate::config::DebugConfig;
use crate::error::ClientCoreError;
@@ -14,15 +14,13 @@ use time::OffsetDateTime;
async fn setup_fresh_backend<P: AsRef<Path>>(
db_path: P,
debug_config: &DebugConfig,
) -> Result<fs_backend::Backend, ClientCoreError> {
) -> Result<fs_backend::Backend, ClientCoreError<fs_backend::Backend>> {
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: Box::new(err),
});
return Err(ClientCoreError::SurbStorageError { source: err });
}
};
@@ -36,9 +34,7 @@ async fn setup_fresh_backend<P: AsRef<Path>>(
storage_backend
.init_fresh(&mem_store)
.await
.map_err(|err| ClientCoreError::SurbStorageError {
source: Box::new(err),
})?;
.map_err(|err| ClientCoreError::SurbStorageError { source: err })?;
Ok(storage_backend)
}
@@ -67,7 +63,7 @@ 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,
debug_config: &DebugConfig,
) -> Result<fs_backend::Backend, ClientCoreError> {
) -> 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() {
@@ -85,10 +81,3 @@ pub async fn setup_fs_reply_surb_backend<P: AsRef<Path>>(
setup_fresh_backend(db_path, debug_config).await
}
}
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,
}
}
@@ -213,7 +213,7 @@ impl LoopCoverTrafficStream<OsRng> {
tokio::task::yield_now().await;
}
pub fn start_with_shutdown(mut self, mut shutdown: task::TaskClient) {
pub fn start_with_shutdown(mut self, mut shutdown: task::ShutdownListener) {
// we should set initial delay only when we actually start the stream
let sampled =
sample_poisson_duration(&mut self.rng, self.average_cover_message_sending_delay);
@@ -1,14 +0,0 @@
// 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::*;
@@ -1,13 +0,0 @@
// 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))
}
@@ -1,16 +0,0 @@
// 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)
}
+24 -100
View File
@@ -17,7 +17,6 @@ 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>,
@@ -58,22 +57,16 @@ impl KeyManager {
}
}
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),
}
// 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)
}
/// Loads previously stored client keys from the disk.
fn load_client_keys(client_pathfinder: &ClientKeyPathfinder) -> io::Result<Self> {
/// Loads previously stored keys from the disk.
pub fn load_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(),
@@ -85,49 +78,19 @@ impl KeyManager {
client_pathfinder.public_encryption_key().to_owned(),
))?;
let ack_key: AckKey = pemstore::load_key(client_pathfinder.ack_key())?;
Ok(KeyManager {
identity_keypair: Arc::new(identity_keypair),
encryption_keypair: Arc::new(encryption_keypair),
gateway_shared_key: None,
ack_key: Arc::new(ack_key),
})
}
/// 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));
let ack_key: AckKey = pemstore::load_key(client_pathfinder.ack_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)
// 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)),
ack_key: Arc::new(ack_key),
})
}
// this is actually **NOT** dead code
@@ -156,7 +119,7 @@ impl KeyManager {
pemstore::store_key(self.ack_key.as_ref(), client_pathfinder.ack_key())?;
match self.gateway_shared_key.as_ref() {
None => debug!("No gateway shared key available to store!"),
None => warn!("No gateway shared key available to store!"),
Some(gate_key) => {
pemstore::store_key(gate_key.as_ref(), client_pathfinder.gateway_shared_key())?
}
@@ -165,60 +128,16 @@ 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)
}
// 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)
}
/// 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
@@ -230,7 +149,12 @@ impl KeyManager {
)
}
pub fn is_gateway_key_set(&self) -> bool {
pub fn gateway_key_set(&self) -> bool {
self.gateway_shared_key.is_some()
}
/// Gets an atomically reference counted pointer to [`AckKey`].
pub fn ack_key(&self) -> Arc<AckKey> {
Arc::clone(&self.ack_key)
}
}
@@ -51,8 +51,8 @@ impl MixTrafficController {
};
match result {
Err(err) => {
error!("Failed to send sphinx packet(s) to the gateway! - {err}");
Err(e) => {
error!("Failed to send sphinx packet(s) to the gateway! - {:?}", e);
self.consecutive_gateway_failure_count += 1;
if self.consecutive_gateway_failure_count == MAX_FAILURE_COUNT {
// todo: in the future this should initiate a 'graceful' shutdown or try
@@ -67,11 +67,11 @@ impl MixTrafficController {
}
}
pub fn start_with_shutdown(mut self, mut shutdown: task::TaskClient) {
pub fn start_with_shutdown(mut self, mut shutdown: task::ShutdownListener) {
spawn_future(async move {
debug!("Started MixTrafficController with graceful shutdown support");
loop {
while !shutdown.is_shutdown() {
tokio::select! {
mix_packets = self.mix_rx.recv() => match mix_packets {
Some(mix_packets) => {
@@ -82,9 +82,8 @@ impl MixTrafficController {
break;
}
},
_ = shutdown.recv_with_delay() => {
_ = shutdown.recv() => {
log::trace!("MixTrafficController: Received shutdown");
break;
}
}
}
-2
View File
@@ -3,7 +3,6 @@
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;
@@ -11,4 +10,3 @@ pub mod real_messages_control;
pub mod received_buffer;
pub mod replies;
pub mod topology_control;
pub(crate) mod transmission_buffer;
@@ -65,7 +65,7 @@ impl AcknowledgementListener {
}
}
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::TaskClient) {
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
debug!("Started AcknowledgementListener with graceful shutdown support");
while !shutdown.is_shutdown() {
@@ -77,7 +77,7 @@ impl AcknowledgementListener {
break;
}
},
_ = shutdown.recv_with_delay() => {
_ = shutdown.recv() => {
log::trace!("AcknowledgementListener: Received shutdown");
}
}
@@ -213,11 +213,7 @@ 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>,
task_client: &mut task::TaskClient,
) {
fn handle_expired_ack_timer(&mut self, expired_ack: Expired<FragmentIdentifier>) {
// 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?
@@ -235,16 +231,9 @@ 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)
if self
.retransmission_sender
self.retransmission_sender
.unbounded_send(Arc::downgrade(pending_ack_data))
.is_err()
{
assert!(
task_client.is_shutdown_poll(),
"Failed to send pending ack for retransmission"
);
}
.unwrap()
} else {
// this shouldn't cause any issues but shouldn't have happened to begin with!
error!("An already removed pending ack has expired")
@@ -260,7 +249,7 @@ impl ActionController {
}
}
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::TaskClient) {
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
debug!("Started ActionController with graceful shutdown support");
while !shutdown.is_shutdown() {
@@ -275,18 +264,21 @@ impl ActionController {
}
},
expired_ack = self.pending_acks_timers.next() => match expired_ack {
Some(expired_ack) => self.handle_expired_ack_timer(expired_ack, &mut shutdown),
Some(expired_ack) => self.handle_expired_ack_timer(expired_ack),
None => {
log::trace!("ActionController: Stopping since ack channel closed");
break;
}
},
_ = shutdown.recv_with_delay() => {
_ = shutdown.recv() => {
log::trace!("ActionController: Received shutdown");
}
}
}
shutdown.recv_timeout().await;
#[cfg(not(target_arch = "wasm32"))]
tokio::time::timeout(Duration::from_secs(5), shutdown.recv())
.await
.expect("Task stopped without shutdown called");
log::debug!("ActionController: Exiting");
}
}
@@ -109,7 +109,7 @@ where
};
}
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::TaskClient) {
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
debug!("Started InputMessageListener with graceful shutdown support");
while !shutdown.is_shutdown() {
@@ -123,7 +123,7 @@ where
break;
}
},
_ = shutdown.recv_with_delay() => {
_ = shutdown.recv() => {
log::trace!("InputMessageListener: Received shutdown");
}
}
@@ -249,7 +249,7 @@ where
}
}
pub(super) fn start_with_shutdown(self, shutdown: task::TaskClient) {
pub(super) fn start_with_shutdown(self, shutdown: task::ShutdownListener) {
let mut acknowledgement_listener = self.acknowledgement_listener;
let mut input_message_listener = self.input_message_listener;
let mut retransmission_request_listener = self.retransmission_request_listener;
@@ -137,7 +137,7 @@ where
.await
}
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::TaskClient) {
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
debug!("Started RetransmissionRequestListener with graceful shutdown support");
while !shutdown.is_shutdown() {
@@ -37,7 +37,7 @@ impl SentNotificationListener {
.unwrap();
}
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::TaskClient) {
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
debug!("Started SentNotificationListener with graceful shutdown support");
while !shutdown.is_shutdown() {
@@ -51,7 +51,7 @@ impl SentNotificationListener {
break;
}
},
_ = shutdown.recv_with_delay() => {
_ = shutdown.recv() => {
log::trace!("SentNotificationListener: Received shutdown");
}
}
@@ -20,7 +20,6 @@ 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;
@@ -279,7 +278,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);
@@ -295,11 +294,19 @@ where
)))
}
pub(crate) async fn send_retransmission_reply_chunks(
// 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(
&mut self,
prepared_fragments: Vec<PreparedFragment>,
fragments: Vec<Fragment>,
reply_surbs: Vec<ReplySurb>,
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 {
@@ -308,58 +315,33 @@ where
}
self.forward_messages(real_messages, lane).await;
Ok(())
}
pub(crate) async fn try_send_reply_chunks_on_lane(
pub(crate) async fn try_send_reply_chunks(
&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.iter().map(|(_, f)| f.clone()).collect(),
reply_surbs,
)
.prepare_reply_chunks_for_sending(fragments.clone(), reply_surbs)
.await?;
let mut pending_acks = Vec::with_capacity(fragments.len());
let mut to_forward: HashMap<_, Vec<_>> = HashMap::new();
let mut real_messages = Vec::with_capacity(fragments.len());
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(fragment, delay, target, false);
let pending_ack = PendingAcknowledgement::new_anonymous(raw, delay, target, false);
let entry = to_forward.entry(lane).or_default();
entry.push(real_message);
real_messages.push(real_message);
pending_acks.push(pending_ack);
}
for (lane, real_messages) in to_forward {
self.forward_messages(real_messages, lane).await;
}
self.forward_messages(real_messages, lane).await;
self.insert_pending_acks(pending_acks);
Ok(())
}
@@ -485,7 +467,7 @@ where
Ok(prepared_fragment)
}
pub(crate) async fn prepare_reply_chunks_for_sending(
async fn prepare_reply_chunks_for_sending(
&mut self,
fragments: Vec<Fragment>,
reply_surbs: Vec<ReplySurb>,
@@ -263,7 +263,7 @@ impl RealMessagesController<OsRng> {
}
}
pub fn start_with_shutdown(self, shutdown: task::TaskClient) {
pub fn start_with_shutdown(self, shutdown: task::ShutdownListener) {
let mut out_queue_control = self.out_queue_control;
let ack_control = self.ack_control;
let mut reply_control = self.reply_control;
@@ -1,11 +1,9 @@
// 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,
};
@@ -31,7 +29,22 @@ 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 {
@@ -122,7 +135,7 @@ where
/// Buffer containing all incoming real messages keyed by transmission lane, that we will send
/// out to the mixnet.
transmission_buffer: TransmissionBuffer<RealMessage>,
transmission_buffer: TransmissionBuffer,
/// Incoming channel for being notified of closed connections, so that we can close lanes
/// corresponding to connections. To avoid sending traffic unnecessary
@@ -149,10 +162,6 @@ 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,
@@ -198,7 +207,7 @@ where
real_receiver,
rng,
topology_access,
transmission_buffer: TransmissionBuffer::new(),
transmission_buffer: Default::default(),
client_connection_rx,
lane_queue_lengths,
}
@@ -255,7 +264,7 @@ where
};
if let Err(err) = self.mix_tx.send(vec![next_message]).await {
log::error!("Failed to send: {err}");
log::error!("Failed to send: {}", err);
}
// notify ack controller about sending our message only after we actually managed to push it
@@ -319,9 +328,7 @@ 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(&mut self.rng)?;
let (lane, real_next) = self.transmission_buffer.pop_next_message_at_random()?;
// Update the published queue length
let lane_length = self.transmission_buffer.lane_length(&lane);
@@ -462,7 +469,7 @@ where
}
#[cfg(not(target_arch = "wasm32"))]
fn log_status(&self, shutdown: &mut task::TaskClient) {
fn log_status(&self, shutdown: &mut task::ShutdownListener) {
use crate::error::ClientCoreStatusMessage;
let packets = self.transmission_buffer.total_size();
@@ -507,7 +514,7 @@ where
}
}
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::TaskClient) {
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
debug!("Started OutQueueControl with graceful shutdown support");
#[cfg(not(target_arch = "wasm32"))]
@@ -518,7 +525,7 @@ where
while !shutdown.is_shutdown() {
tokio::select! {
biased;
_ = shutdown.recv_with_delay() => {
_ = shutdown.recv() => {
log::trace!("OutQueueControl: Received shutdown");
}
_ = status_timer.tick() => {
@@ -535,7 +542,9 @@ where
}
}
}
shutdown.recv_timeout().await;
tokio::time::timeout(Duration::from_secs(5), shutdown.recv())
.await
.expect("Task stopped without shutdown called");
}
#[cfg(target_arch = "wasm32")]
@@ -1,9 +1,14 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::helpers::{get_time_now, Instant};
use super::get_time_now;
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.
@@ -34,11 +39,19 @@ 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
time_when_changed: Instant,
#[cfg(not(target_arch = "wasm32"))]
time_when_changed: time::Instant,
#[cfg(target_arch = "wasm32")]
time_when_changed: wasm_timer::Instant,
/// If we have a long enough time without any backpressure detected we try reducing the sending
/// delay multiplier
time_when_backpressure_detected: Instant,
#[cfg(not(target_arch = "wasm32"))]
time_when_backpressure_detected: time::Instant,
#[cfg(target_arch = "wasm32")]
time_when_backpressure_detected: wasm_timer::Instant,
}
impl Default for SendingDelayController {
@@ -64,12 +77,10 @@ impl SendingDelayController {
self.current_multiplier
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn min_multiplier(&self) -> u32 {
self.lower_bound
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn max_multiplier(&self) -> u32 {
self.upper_bound
}
@@ -1,57 +1,38 @@
// 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;
pub(crate) trait SizedData {
fn data_size(&self) -> usize;
}
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>>,
pub(crate) struct TransmissionBuffer {
buffer: HashMap<TransmissionLane, LaneBufferEntry>,
}
impl<T> TransmissionBuffer<T> {
pub(crate) fn new() -> Self {
TransmissionBuffer {
buffer: HashMap::new(),
}
}
impl TransmissionBuffer {
#[allow(unused)]
pub(crate) fn is_empty(&self) -> bool {
self.buffer.is_empty()
}
pub(crate) fn remove(&mut self, lane: &TransmissionLane) -> Option<LaneBufferEntry<T>> {
pub(crate) fn remove(&mut self, lane: &TransmissionLane) -> Option<LaneBufferEntry> {
self.buffer.remove(lane)
}
@@ -76,22 +57,20 @@ impl<T> TransmissionBuffer<T> {
.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
where
T: SizedData,
{
pub(crate) fn total_size_in_bytes(&self) -> usize {
self.buffer
.values()
.map(|lane_buffer_entry| {
lane_buffer_entry
.items
.real_messages
.iter()
.map(|item| item.data_size())
.map(|real_message| real_message.mix_packet.sphinx_packet().len())
.sum::<usize>()
})
.sum()
@@ -113,51 +92,42 @@ impl<T> TransmissionBuffer<T> {
.collect()
}
pub(crate) fn store<I: IntoIterator<Item = T>>(&mut self, lane: &TransmissionLane, items: I) {
pub(crate) fn store(&mut self, lane: &TransmissionLane, real_messages: Vec<RealMessage>) {
if let Some(lane_buffer_entry) = self.buffer.get_mut(lane) {
lane_buffer_entry.extend(items);
lane_buffer_entry.append(real_messages);
} else {
self.buffer
.insert(*lane, LaneBufferEntry::new(items.into_iter().collect()));
.insert(*lane, LaneBufferEntry::new(real_messages));
}
}
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_lane<R: Rng + ?Sized>(&self, rng: &mut R) -> Option<&TransmissionLane> {
fn pick_random_lane(&self) -> Option<&TransmissionLane> {
let lanes: Vec<&TransmissionLane> = self.buffer.keys().collect();
lanes.choose(rng).copied()
lanes.choose(&mut rand::thread_rng()).copied()
}
fn pick_random_small_lane<R: Rng + ?Sized>(&self, rng: &mut R) -> Option<&TransmissionLane> {
fn pick_random_small_lane(&self) -> Option<&TransmissionLane> {
let lanes: Vec<&TransmissionLane> = self
.buffer
.iter()
.filter(|(_, v)| v.is_small())
.map(|(k, _)| k)
.collect();
lanes.choose(rng).copied()
lanes.choose(&mut rand::thread_rng()).copied()
}
// 2/3 chance to pick from the old lanes
fn pick_random_old_lane<R: Rng + ?Sized>(&self, rng: &mut R) -> Option<TransmissionLane> {
fn pick_random_old_lane(&self) -> 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(rng).copied()
self.pick_random_lane().copied()
}
}
fn pop_front_from_lane(&mut self, lane: &TransmissionLane) -> Option<T> {
fn pop_front_from_lane(&mut self, lane: &TransmissionLane) -> Option<RealMessage> {
let real_msgs_queued = self.buffer.get_mut(lane)?;
let real_next = real_msgs_queued.pop_front()?;
real_msgs_queued.messages_transmitted += 1;
@@ -167,48 +137,19 @@ impl<T> TransmissionBuffer<T> {
Some(real_next)
}
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)> {
pub(crate) fn pop_next_message_at_random(&mut self) -> Option<(TransmissionLane, RealMessage)> {
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(rng) {
let lane = if let Some(small_lane) = self.pick_random_small_lane() {
*small_lane
} else if let Some(old_lane) = self.pick_random_old_lane(rng) {
} else if let Some(old_lane) = self.pick_random_old_lane() {
old_lane
} else {
*self.pick_random_lane(rng)?
*self.pick_random_lane()?
};
let msg = self.pop_front_from_lane(&lane)?;
@@ -230,46 +171,35 @@ impl<T> TransmissionBuffer<T> {
}
}
pub(crate) struct LaneBufferEntry<T> {
pub items: VecDeque<T>,
pub(crate) struct LaneBufferEntry {
pub real_messages: VecDeque<RealMessage>,
pub messages_transmitted: usize,
pub time_for_last_activity: Instant,
#[cfg(not(target_arch = "wasm32"))]
pub time_for_last_activity: time::Instant,
#[cfg(target_arch = "wasm32")]
pub time_for_last_activity: wasm_timer::Instant,
}
impl<T> LaneBufferEntry<T> {
fn new_empty() -> Self {
impl LaneBufferEntry {
fn new(real_messages: Vec<RealMessage>) -> Self {
LaneBufferEntry {
items: VecDeque::new(),
real_messages: real_messages.into(),
messages_transmitted: 0,
time_for_last_activity: get_time_now(),
}
}
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);
fn append(&mut self, real_messages: Vec<RealMessage>) {
self.real_messages.append(&mut real_messages.into());
self.time_for_last_activity = get_time_now();
}
fn pop_front(&mut self) -> Option<T> {
self.items.pop_front()
fn pop_front(&mut self) -> Option<RealMessage> {
self.real_messages.pop_front()
}
fn is_small(&self) -> bool {
self.items.len() < 100
self.real_messages.len() < 100
}
fn is_stale(&self) -> bool {
@@ -278,10 +208,10 @@ impl<T> LaneBufferEntry<T> {
}
fn len(&self) -> usize {
self.items.len()
self.real_messages.len()
}
fn is_empty(&self) -> bool {
self.items.is_empty()
self.real_messages.is_empty()
}
}
@@ -399,20 +399,21 @@ impl RequestReceiver {
}
}
async fn run_with_shutdown(&mut self, mut shutdown: task::TaskClient) {
async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
debug!("Started RequestReceiver with graceful shutdown support");
while !shutdown.is_shutdown() {
tokio::select! {
biased;
_ = shutdown.recv_with_delay() => {
_ = shutdown.recv() => {
log::trace!("RequestReceiver: Received shutdown");
}
request = self.query_receiver.next() => {
if let Some(message) = request {
self.handle_message(message).await
} else {
log::trace!("RequestReceiver: Stopping since channel closed");
break;
match request {
Some(message) => self.handle_message(message).await,
None => {
log::trace!("RequestReceiver: Stopping since channel closed");
break;
},
}
},
}
@@ -438,19 +439,20 @@ impl FragmentedMessageReceiver {
}
}
async fn run_with_shutdown(&mut self, mut shutdown: task::TaskClient) {
async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
debug!("Started FragmentedMessageReceiver with graceful shutdown support");
while !shutdown.is_shutdown() {
tokio::select! {
new_messages = self.mixnet_packet_receiver.next() => {
if let Some(new_messages) = new_messages {
new_messages = self.mixnet_packet_receiver.next() => match new_messages {
Some(new_messages) => {
self.received_buffer.handle_new_received(new_messages).await;
} else {
}
None => {
log::trace!("FragmentedMessageReceiver: Stopping since channel closed");
break;
}
},
_ = shutdown.recv_with_delay() => {
_ = shutdown.recv() => {
log::trace!("FragmentedMessageReceiver: Received shutdown");
}
}
@@ -488,7 +490,7 @@ impl ReceivedMessagesBufferController {
}
}
pub fn start_with_shutdown(self, shutdown: task::TaskClient) {
pub fn start_with_shutdown(self, shutdown: task::ShutdownListener) {
let mut fragmented_message_receiver = self.fragmented_message_receiver;
let mut request_receiver = self.request_receiver;
@@ -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::{ConnectionId, TransmissionLane};
use futures::channel::oneshot;
use client_connections::TransmissionLane;
use futures::channel::mpsc;
use futures::StreamExt;
use log::{debug, error, info, trace, warn};
use nymsphinx::addressing::clients::Recipient;
@@ -15,16 +15,116 @@ 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};
use std::collections::{BTreeMap, HashMap, VecDeque};
use std::sync::{Arc, Weak};
use std::time::Duration;
use time::OffsetDateTime;
use crate::client::helpers::new_interval_stream;
use crate::client::transmission_buffer::TransmissionBuffer;
pub(crate) use requests::{ReplyControllerMessage, ReplyControllerReceiver, ReplyControllerSender};
#[cfg(not(target_arch = "wasm32"))]
type IntervalStream = tokio_stream::wrappers::IntervalStream;
pub mod requests;
#[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 struct Config {
min_surb_request_size: u32,
@@ -72,7 +172,7 @@ pub struct ReplyController<R> {
// of surbs required to send the message through
// expected_reliability: f32,
request_receiver: ReplyControllerReceiver,
pending_replies: HashMap<AnonymousSenderTag, TransmissionBuffer<Fragment>>,
pending_replies: HashMap<AnonymousSenderTag, VecDeque<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.
@@ -104,28 +204,17 @@ where
}
}
fn insert_pending_replies<I: IntoIterator<Item = Fragment>>(
/// 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>>>(
&mut self,
recipient: &AnonymousSenderTag,
fragments: I,
lane: TransmissionLane,
fragments: V,
) {
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)
if let Some(existing) = self.pending_replies.get_mut(recipient) {
existing.append(&mut fragments.into())
} else {
self.pending_replies.insert(*recipient, fragments.into());
}
}
fn re_insert_pending_retransmission(
@@ -155,7 +244,7 @@ where
let pending_queue_size = self
.pending_replies
.get(target)
.map(|pending_queue| pending_queue.total_size())
.map(|pending_queue| pending_queue.len())
.unwrap_or_default();
let retransmission_queue = self
@@ -210,61 +299,42 @@ where
}
trace!("handling reply to {:?}", recipient_tag);
let mut fragments = self.message_handler.split_reply_message(data);
let total_size = fragments.len();
trace!("This reply requires {:?} SURBs", total_size);
let fragments = self.message_handler.split_reply_message(data);
let available_surbs = self
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
.full_reply_storage
.surbs_storage_ref()
.available_surbs(&recipient_tag);
let min_surbs_threshold = self
.full_reply_storage
.surbs_storage_ref()
.min_surb_threshold();
.get_reply_surbs(&recipient_tag, required_surbs);
let max_to_send = if available_surbs > min_surbs_threshold {
min(fragments.len(), available_surbs - min_surbs_threshold)
} else {
0
};
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);
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);
}
// TODO: should we buffer that data to try again?
}
}
} else {
// we don't have enough surbs for this reply
self.insert_pending_replies(&recipient_tag, fragments);
// 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;
if self.should_request_more_surbs(&recipient_tag) {
self.request_reply_surbs_for_queue_clearing(recipient_tag)
.await;
}
}
}
@@ -328,18 +398,35 @@ where
};
let mut to_take = Vec::new();
let mut to_remove = Vec::new();
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)
// 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;
}
} else {
// our map is empty!
};
to_take.push(upgraded);
// we have taken as many entries as we could have
if to_take.len() >= max_to_clear {
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() {
@@ -360,47 +447,46 @@ where
let to_send_vec = to_take.iter().map(|ack| ack.fragment_data()).collect();
let prepared_fragments = match self
if let Err(err) = self
.message_handler
.prepare_reply_chunks_for_sending(to_send_vec, surbs_for_reply)
.try_send_retransmission_reply_chunks(
to_send_vec,
surbs_for_reply,
TransmissionLane::Retransmission,
)
.await
{
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);
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
);
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;
warn!(
"failed to clear pending retransmission queue for {:?} - {err}",
target
);
}
}
fn pop_at_most_pending_replies(
&mut self,
from: &AnonymousSenderTag,
amount: usize,
) -> Option<Vec<(TransmissionLane, Fragment)>> {
) -> Option<VecDeque<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)?.total_size();
let total = self.pending_replies.get(from)?.len();
trace!("pending queue has {total} elements");
if total == 0 {
return None;
}
self.pending_replies
.get_mut(from)?
.pop_at_most_n_next_messages_at_random(amount)
if total < amount {
self.pending_replies.remove(from)
} else {
Some(
self.pending_replies
.get_mut(from)?
.drain(..amount)
.collect(),
)
}
}
async fn try_clear_pending_queue(&mut self, target: AnonymousSenderTag) {
@@ -424,9 +510,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_clone = to_send.clone();
let to_send_vec = to_send.iter().cloned().collect::<Vec<_>>();
if to_send_clone.is_empty() {
if to_send_vec.is_empty() {
panic!(
"please let the devs know if you ever see this message (reply_controller.rs)"
);
@@ -435,22 +521,27 @@ where
let (surbs_for_reply, _) = self
.full_reply_storage
.surbs_storage_ref()
.get_reply_surbs(&target, to_send_clone.len());
.get_reply_surbs(&target, to_send_vec.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.re_insert_pending_replies(&target, to_send);
self.insert_pending_replies(&target, to_send);
return;
};
if let Err(err) = self
.message_handler
.try_send_reply_chunks(target, to_send_clone, surbs_for_reply)
.try_send_reply_chunks(
target,
to_send_vec,
surbs_for_reply,
TransmissionLane::General,
)
.await
{
let err =
err.return_unused_surbs(self.full_reply_storage.surbs_storage_ref(), &target);
self.re_insert_pending_replies(&target, to_send);
self.insert_pending_replies(&target, to_send);
warn!("failed to clear pending queue for {:?} - {err}", target);
}
} else {
@@ -621,30 +712,6 @@ 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 {
@@ -668,26 +735,19 @@ 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.total_size())
.map(|pending_queue| pending_queue.len())
.unwrap_or_default();
let retransmission_queue = self
@@ -727,7 +787,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.total_size());
error!("we have {} pending replies for {pending_reply_target}, but we somehow never received any reply surbs from them!", vals.len());
to_remove.push(*pending_reply_target);
continue;
};
@@ -823,25 +883,28 @@ where
}
}
// #[cfg(not(target_arch = "wasm32"))]
// async fn log_status(&self) {
// todo!()
// }
fn create_interval_stream(polling_rate: Duration) -> IntervalStream {
#[cfg(not(target_arch = "wasm32"))]
return tokio_stream::wrappers::IntervalStream::new(tokio::time::interval(polling_rate));
pub(crate) async fn run_with_shutdown(&mut self, mut shutdown: task::TaskClient) {
#[cfg(target_arch = "wasm32")]
return gloo_timers::future::IntervalStream::new(polling_rate.as_millis() as u32);
}
pub(crate) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
debug!("Started ReplyController with graceful shutdown support");
let polling_rate = Duration::from_secs(5);
let mut stale_inspection = new_interval_stream(polling_rate);
let mut stale_inspection = Self::create_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 = new_interval_stream(polling_rate);
let mut invalidation_inspection = Self::create_interval_stream(polling_rate);
while !shutdown.is_shutdown() {
tokio::select! {
biased;
_ = shutdown.recv_with_delay() => {
_ = shutdown.recv() => {
log::trace!("ReplyController: Received shutdown");
},
req = self.request_receiver.next() => match req {
@@ -1,154 +0,0 @@
// 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,7 +7,6 @@ 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,
}
@@ -19,11 +19,10 @@ 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
pub min_surb_threshold: usize,
pub max_surb_threshold: usize,
min_surb_threshold: usize,
max_surb_threshold: usize,
}
#[async_trait]
@@ -37,7 +37,7 @@ where
pub async fn flush_on_shutdown(
mut self,
mem_state: CombinedReplyStorage,
mut shutdown: task::TaskClient,
mut shutdown: task::ShutdownListener,
) {
use log::{debug, error, info, warn};
@@ -136,15 +136,19 @@ impl Default for TopologyAccessor {
}
pub struct TopologyRefresherConfig {
nym_api_urls: Vec<Url>,
validator_api_urls: Vec<Url>,
refresh_rate: Duration,
client_version: String,
}
impl TopologyRefresherConfig {
pub fn new(nym_api_urls: Vec<Url>, refresh_rate: Duration, client_version: String) -> Self {
pub fn new(
validator_api_urls: Vec<Url>,
refresh_rate: Duration,
client_version: String,
) -> Self {
TopologyRefresherConfig {
nym_api_urls,
validator_api_urls,
refresh_rate,
client_version,
}
@@ -152,10 +156,10 @@ impl TopologyRefresherConfig {
}
pub struct TopologyRefresher {
validator_client: validator_client::client::NymApiClient,
validator_client: validator_client::client::ApiClient,
client_version: String,
nym_api_urls: Vec<Url>,
validator_api_urls: Vec<Url>,
topology_accessor: TopologyAccessor,
refresh_rate: Duration,
@@ -165,14 +169,14 @@ pub struct TopologyRefresher {
impl TopologyRefresher {
pub fn new(mut cfg: TopologyRefresherConfig, topology_accessor: TopologyAccessor) -> Self {
cfg.nym_api_urls.shuffle(&mut thread_rng());
cfg.validator_api_urls.shuffle(&mut thread_rng());
TopologyRefresher {
validator_client: validator_client::client::NymApiClient::new(
cfg.nym_api_urls[0].clone(),
validator_client: validator_client::client::ApiClient::new(
cfg.validator_api_urls[0].clone(),
),
client_version: cfg.client_version,
nym_api_urls: cfg.nym_api_urls,
validator_api_urls: cfg.validator_api_urls,
topology_accessor,
refresh_rate: cfg.refresh_rate,
currently_used_api: 0,
@@ -180,15 +184,15 @@ impl TopologyRefresher {
}
}
fn use_next_nym_api(&mut self) {
if self.nym_api_urls.len() == 1 {
warn!("There's only a single nym API available - it won't be possible to use a different one");
fn use_next_validator_api(&mut self) {
if self.validator_api_urls.len() == 1 {
warn!("There's only a single validator API available - it won't be possible to use a different one");
return;
}
self.currently_used_api = (self.currently_used_api + 1) % self.nym_api_urls.len();
self.currently_used_api = (self.currently_used_api + 1) % self.validator_api_urls.len();
self.validator_client
.change_nym_api(self.nym_api_urls[self.currently_used_api].clone())
.change_validator_api(self.validator_api_urls[self.currently_used_api].clone())
}
/// Verifies whether nodes a reasonably distributed among all mix layers.
@@ -284,7 +288,7 @@ impl TopologyRefresher {
let new_topology = self.get_current_compatible_topology().await;
if new_topology.is_none() {
self.use_next_nym_api();
self.use_next_validator_api();
}
if new_topology.is_none() && self.was_latest_valid {
@@ -306,7 +310,7 @@ impl TopologyRefresher {
self.topology_accessor.ensure_is_routable().await
}
pub fn start_with_shutdown(mut self, mut shutdown: task::TaskClient) {
pub fn start_with_shutdown(mut self, mut shutdown: task::ShutdownListener) {
spawn_future(async move {
debug!("Started TopologyRefresher with graceful shutdown support");
+15 -61
View File
@@ -1,8 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use config::defaults::NymNetworkDetails;
use config::{NymConfig, OptionalSet, DB_FILE_NAME};
use config::{NymConfig, DB_FILE_NAME};
use nymsphinx::params::PacketSize;
use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
@@ -71,15 +70,12 @@ 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
@@ -164,9 +160,8 @@ impl<T> Config<T> {
changes_made
}
pub fn with_disabled_credentials(mut self, disabled_credentials_mode: bool) -> Self {
pub fn with_disabled_credentials(&mut self, disabled_credentials_mode: bool) {
self.client.disabled_credentials_mode = disabled_credentials_mode;
self
}
pub fn with_gateway_endpoint(&mut self, gateway_endpoint: GatewayEndpointConfig) {
@@ -177,29 +172,12 @@ impl<T> Config<T> {
self.client.gateway_endpoint.gateway_id = id.into();
}
pub fn with_custom_nyxd(mut self, urls: Vec<Url>) -> Self {
self.client.nyxd_urls = urls;
self
pub fn set_custom_validators(&mut self, validator_urls: Vec<Url>) {
self.client.validator_urls = validator_urls;
}
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_custom_validator_apis(&mut self, validator_api_urls: Vec<Url>) {
self.client.validator_api_urls = validator_api_urls;
}
pub fn set_high_default_traffic_volume(&mut self) {
@@ -210,13 +188,6 @@ 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;
@@ -263,11 +234,11 @@ impl<T> Config<T> {
}
pub fn get_validator_endpoints(&self) -> Vec<Url> {
self.client.nyxd_urls.clone()
self.client.validator_urls.clone()
}
pub fn get_nym_api_endpoints(&self) -> Vec<Url> {
self.client.nym_api_urls.clone()
pub fn get_validator_api_endpoints(&self) -> Vec<Url> {
self.client.validator_api_urls.clone()
}
pub fn get_gateway_id(&self) -> String {
@@ -449,13 +420,12 @@ pub struct Client<T> {
#[serde(default)]
disabled_credentials_mode: bool,
/// Addresses to nyxd validators via which the client can communicate with the chain.
#[serde(alias = "validator_urls")]
nyxd_urls: Vec<Url>,
/// Addresses to nymd validators via which the client can communicate with the chain.
#[serde(default)]
validator_urls: Vec<Url>,
/// Addresses to APIs running on validator from which the client gets the view of the network.
#[serde(alias = "validator_api_urls")]
nym_api_urls: Vec<Url>,
validator_api_urls: Vec<Url>,
/// Path to file containing private identity key.
private_identity_key_file: PathBuf,
@@ -500,29 +470,13 @@ 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,
nyxd_urls,
nym_api_urls,
validator_urls: vec![],
validator_api_urls: vec![],
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 {
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,
identity_private_key: PathBuf,
identity_public_key: PathBuf,
encryption_private_key: PathBuf,
encryption_public_key: PathBuf,
gateway_shared_key: PathBuf,
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("private_encryption.pem"),
encryption_public_key: config_dir.join("public_encryption.pem"),
encryption_private_key: config_dir.join("public_encryption.pem"),
encryption_public_key: config_dir.join("private_encryption.pem"),
gateway_shared_key: config_dir.join("gateway_shared.pem"),
ack_key: config_dir.join("ack_key.pem"),
}
@@ -40,28 +40,6 @@ 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
}
@@ -86,10 +64,3 @@ 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
}
+5 -9
View File
@@ -1,13 +1,14 @@
// 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 {
pub enum ClientCoreError<B: ReplyStorageBackend> {
#[error("I/O error: {0}")]
IoError(#[from] std::io::Error),
@@ -29,8 +30,8 @@ pub enum ClientCoreError {
#[error("Failed to setup gateway")]
FailedToSetupGateway,
#[error("List of nym apis is empty")]
ListOfNymApisIsEmpty,
#[error("List of validator apis is empty")]
ListOfValidatorApisIsEmpty,
#[error("Could not load existing gateway configuration: {0}")]
CouldNotLoadExistingGatewayConfiguration(std::io::Error),
@@ -39,9 +40,7 @@ pub enum ClientCoreError {
InsufficientNetworkTopology(#[from] NymTopologyError),
#[error("experienced a failure with our reply surb persistent storage: {source}")]
SurbStorageError {
source: Box<dyn std::error::Error + Send + Sync>,
},
SurbStorageError { source: B::StorageError },
#[error("The gateway id is invalid - {0}")]
UnableToCreatePublicKeyFromGatewayId(Ed25519RecoveryError),
@@ -55,9 +54,6 @@ pub enum ClientCoreError {
#[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,
}
+27 -12
View File
@@ -1,6 +1,7 @@
// 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},
@@ -10,22 +11,25 @@ use config::NymConfig;
use crypto::asymmetric::identity;
use gateway_client::GatewayClient;
use gateway_requests::registration::handshake::SharedKeys;
use rand::{seq::SliceRandom, thread_rng};
use rand::{rngs::OsRng, seq::SliceRandom, thread_rng};
use std::{sync::Arc, time::Duration};
use tap::TapFallible;
use topology::{filter::VersionFilterable, gateway};
use url::Url;
pub(super) async fn query_gateway_details(
pub(super) async fn query_gateway_details<B>(
validator_servers: Vec<Url>,
chosen_gateway_id: Option<String>,
) -> Result<gateway::Node, ClientCoreError> {
let nym_api = validator_servers
) -> Result<gateway::Node, ClientCoreError<B>>
where
B: ReplyStorageBackend,
{
let validator_api = validator_servers
.choose(&mut thread_rng())
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
let validator_client = validator_client::client::NymApiClient::new(nym_api.clone());
.ok_or(ClientCoreError::ListOfValidatorApisIsEmpty)?;
let validator_client = validator_client::client::ApiClient::new(validator_api.clone());
log::trace!("Fetching list of gateways from: {}", nym_api);
log::trace!("Fetching list of gateways from: {}", validator_api);
let gateways = validator_client.get_cached_gateways().await?;
let valid_gateways = gateways
.into_iter()
@@ -51,10 +55,13 @@ pub(super) async fn query_gateway_details(
}
}
pub(super) async fn register_with_gateway(
async fn register_with_gateway<B>(
gateway: &gateway::Node,
our_identity: Arc<identity::KeyPair>,
) -> Result<Arc<SharedKeys>, ClientCoreError> {
) -> Result<Arc<SharedKeys>, ClientCoreError<B>>
where
B: ReplyStorageBackend,
{
let timeout = Duration::from_millis(1500);
let mut gateway_client = GatewayClient::new_init(
gateway.clients_address(),
@@ -74,13 +81,21 @@ pub(super) async fn register_with_gateway(
Ok(shared_keys)
}
pub(super) fn store_keys<T>(
key_manager: &KeyManager,
pub(super) async fn register_with_gateway_and_store_keys<T, B>(
gateway_details: gateway::Node,
config: &Config<T>,
) -> Result<(), ClientCoreError>
) -> Result<(), ClientCoreError<B>>
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)
+42 -76
View File
@@ -6,26 +6,24 @@
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::key_manager::KeyManager;
use crate::client::replies::reply_storage::ReplyStorageBackend;
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,
@@ -63,95 +61,67 @@ impl Display for InitResults {
}
}
/// Create a new set of client keys.
pub fn new_client_keys() -> KeyManager {
let mut rng = OsRng;
KeyManager::new(&mut rng)
}
/// 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<C, T>(
pub async fn setup_gateway<B, C, T>(
register_gateway: bool,
// TODO: this should get refactored to instead take Option<identity::PublicKey>
user_chosen_gateway_id: Option<String>,
config: &Config<T>,
) -> Result<GatewayEndpointConfig, ClientCoreError>
) -> Result<GatewayEndpointConfig, ClientCoreError<B>>
where
B: ReplyStorageBackend,
C: NymConfig + ClientCoreConfigTrait,
T: NymConfig,
{
let id = config.get_id();
if register_gateway {
register_with_gateway_and_store(user_chosen_gateway_id, config).await
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::<C>(&id)
reuse_existing_gateway_config::<B, 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.
pub async fn register_with_gateway(
key_manager: &mut KeyManager,
nym_api_endpoints: Vec<Url>,
chosen_gateway_id: Option<String>,
) -> Result<GatewayEndpointConfig, ClientCoreError> {
// Our identity is derived from our key
let our_identity = key_manager.identity_keypair();
// 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);
// 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())
}
/// 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_and_store<T>(
chosen_gateway_id: Option<String>,
pub async fn register_with_gateway<B, T>(
user_chosen_gateway_id: Option<String>,
config: &Config<T>,
) -> Result<GatewayEndpointConfig, ClientCoreError>
) -> Result<GatewayEndpointConfig, ClientCoreError<B>>
where
B: ReplyStorageBackend,
T: NymConfig,
{
println!("Configuring gateway");
let mut key_manager = new_client_keys();
let gateway =
query_gateway_details(config.get_validator_api_endpoints(), user_chosen_gateway_id).await?;
log::debug!("Querying gateway gives: {}", gateway);
let gateway = register_with_gateway(
&mut key_manager,
config.get_nym_api_endpoints(),
chosen_gateway_id,
)
.await?;
helpers::store_keys(&key_manager, config)?;
// 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");
Ok(gateway)
Ok(gateway.into())
}
/// 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<T>(
pub async fn config_gateway_with_existing_keys<B, T>(
user_chosen_gateway_id: String,
config: &Config<T>,
) -> Result<GatewayEndpointConfig, ClientCoreError>
) -> Result<GatewayEndpointConfig, ClientCoreError<B>>
where
B: ReplyStorageBackend,
T: NymConfig,
{
println!("Using gateway provided by user, keeping existing keys");
let gateway = helpers::query_gateway_details(
config.get_nym_api_endpoints(),
let gateway = query_gateway_details(
config.get_validator_api_endpoints(),
Some(user_chosen_gateway_id),
)
.await?;
@@ -160,8 +130,11 @@ where
}
/// Read and reuse the existing gateway configuration from a file that was generate earlier.
pub fn reuse_existing_gateway_config<T>(id: &str) -> Result<GatewayEndpointConfig, ClientCoreError>
pub fn reuse_existing_gateway_config<B, T>(
id: &str,
) -> Result<GatewayEndpointConfig, ClientCoreError<B>>
where
B: ReplyStorageBackend,
T: NymConfig + ClientCoreConfigTrait,
{
println!("Not registering gateway, will reuse existing config and keys");
@@ -179,30 +152,20 @@ 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<T>(
pub fn get_client_address_from_stored_keys<B, T>(
config: &Config<T>,
) -> Result<Recipient, ClientCoreError>
) -> Result<Recipient, ClientCoreError<B>>
where
T: config::NymConfig,
B: ReplyStorageBackend,
{
fn load_identity_keys(
fn load_identity_keys<B>(
pathfinder: &ClientKeyPathfinder,
) -> Result<identity::KeyPair, ClientCoreError> {
) -> Result<identity::KeyPair, ClientCoreError<B>>
where
B: ReplyStorageBackend,
{
let identity_keypair: identity::KeyPair =
pemstore::load_keypair(&pemstore::KeyPairPath::new(
pathfinder.private_identity_key().to_owned(),
@@ -212,9 +175,12 @@ where
Ok(identity_keypair)
}
fn load_sphinx_keys(
fn load_sphinx_keys<B>(
pathfinder: &ClientKeyPathfinder,
) -> Result<encryption::KeyPair, ClientCoreError> {
) -> Result<encryption::KeyPair, ClientCoreError<B>>
where
B: ReplyStorageBackend,
{
let sphinx_keypair: encryption::KeyPair =
pemstore::load_keypair(&pemstore::KeyPairPath::new(
pathfinder.private_encryption_key().to_owned(),
@@ -243,8 +209,8 @@ 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),
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),
}
}
+2 -2
View File
@@ -8,7 +8,7 @@ edition = "2021"
[dependencies]
bip39 = "1.0.1"
cfg-if = "0.1"
clap = { version = "4.0", features = ["cargo", "derive"] }
clap = { version = "3.2", features = ["cargo", "derive"] }
rand = "0.7.3"
serde = { version = "1.0", features = ["derive"] }
thiserror = "1.0"
@@ -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 = ["nyxd-client"] }
validator-client = { path = "../../common/client-libs/validator-client", features = ["nymd-client"] }
[features]
coconut = ["credentials/coconut"]
+11 -11
View File
@@ -6,27 +6,27 @@ use bip39::Mnemonic;
use network_defaults::{NymNetworkDetails, VOUCHER_INFO};
use std::str::FromStr;
use url::Url;
use validator_client::nyxd;
use validator_client::nyxd::traits::CoconutBandwidthSigningClient;
use validator_client::nyxd::{Coin, Fee, NyxdClient, SigningNyxdClient};
use validator_client::nymd;
use validator_client::nymd::traits::CoconutBandwidthSigningClient;
use validator_client::nymd::{Coin, Fee, NymdClient, SigningNymdClient};
pub(crate) struct Client {
nyxd_client: NyxdClient<SigningNyxdClient>,
nymd_client: NymdClient<SigningNymdClient>,
mix_denom_base: String,
}
impl Client {
pub fn new(nyxd_url: &str, mnemonic: &str) -> Self {
let nyxd_url = Url::from_str(nyxd_url).unwrap();
pub fn new(nymd_url: &str, mnemonic: &str) -> Self {
let nymd_url = Url::from_str(nymd_url).unwrap();
let mnemonic = Mnemonic::from_str(mnemonic).unwrap();
let network_details = NymNetworkDetails::new_from_env();
let config = nyxd::Config::try_from_nym_network_details(&network_details)
let config = nymd::Config::try_from_nym_network_details(&network_details)
.expect("failed to construct valid validator client config with the provided network");
let nyxd_client =
NyxdClient::connect_with_mnemonic(config, nyxd_url.as_ref(), mnemonic, None).unwrap();
let nymd_client =
NymdClient::connect_with_mnemonic(config, nymd_url.as_ref(), mnemonic, None).unwrap();
Client {
nyxd_client,
nymd_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
.nyxd_client
.nymd_client
.deposit(
amount,
String::from(VOUCHER_INFO),
+5 -5
View File
@@ -13,7 +13,7 @@ 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::nyxd::tx::Hash;
use validator_client::nymd::tx::Hash;
use validator_client::{CoconutApiClient, Config};
use crate::client::Client;
@@ -38,9 +38,9 @@ pub(crate) struct Run {
#[clap(long)]
pub(crate) client_home_directory: std::path::PathBuf,
/// The nyxd URL that should be used
/// The nymd URL that should be used
#[clap(long)]
pub(crate) nyxd_url: String,
pub(crate) nymd_url: String,
/// A mnemonic for the account that buys the credential
#[clap(long)]
@@ -51,12 +51,12 @@ pub(crate) struct Run {
pub(crate) amount: u64,
}
pub(crate) async fn deposit(nyxd_url: &str, mnemonic: &str, amount: u64) -> Result<State> {
pub(crate) async fn deposit(nymd_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(nyxd_url, mnemonic);
let client = Client::new(nymd_url, mnemonic);
let tx_hash = client
.deposit(
amount,
+3 -3
View File
@@ -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::nyxd::error::NyxdError;
use validator_client::nymd::error::NymdError;
use validator_client::ValidatorClientError;
pub type Result<T> = std::result::Result<T, CredentialClientError>;
#[derive(Error, Debug)]
pub enum CredentialClientError {
#[error("Nyxd error: {0}")]
Nyxd(#[from] NyxdError),
#[error("Nymd error: {0}")]
Nymd(#[from] NymdError),
#[error("Validator client error: {0}")]
ValidatorClientError(#[from] ValidatorClientError),
+4 -4
View File
@@ -31,7 +31,7 @@ cfg_if::cfg_if! {
#[tokio::main]
async fn main() -> Result<()> {
let args = Cli::parse();
setup_env(args.config_env_file.as_ref());
setup_env(args.config_env_file.clone());
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.nyxd_url, &r.mnemonic, r.amount).await?;
let state = deposit(&r.nymd_url, &r.mnemonic, r.amount).await?;
get_credential(&state, shared_storage).await?;
}
Command::Completions(c) => c.generate(&mut crate::Cli::command(), bin_name),
Command::GenerateFigSpec => fig_generate(&mut crate::Cli::command(), bin_name)
Command::Completions(c) => c.generate(&mut crate::Cli::into_app(), bin_name),
Command::GenerateFigSpec => fig_generate(&mut crate::Cli::into_app(), bin_name)
}
Ok(())
+6 -5
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "1.1.5"
version = "1.1.4"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
@@ -20,9 +20,8 @@ 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 = "4.0", features = ["cargo", "derive"] }
clap = { version = "3.2", features = ["cargo", "derive"] }
dirs = "4.0"
lazy_static = "1.4.0"
log = "0.4" # 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
@@ -34,7 +33,6 @@ tokio = { version = "1.21.2", features = ["rt-multi-thread", "net", "signal"] }
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 }
@@ -51,7 +49,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 = ["nyxd-client"] }
validator-client = { path = "../../common/client-libs/validator-client", features = ["nymd-client"] }
version-checker = { path = "../../common/version-checker" }
websocket-requests = { path = "websocket-requests" }
@@ -60,3 +58,6 @@ 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,4 +1,4 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use vergen::{vergen, Config};
File diff suppressed because it is too large Load Diff
+2 -58
View File
@@ -2,15 +2,12 @@
// 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, DebugConfig};
use client_core::config::{ClientCoreConfigTrait, Config as BaseConfig, DebugConfig};
use config::defaults::DEFAULT_WEBSOCKET_LISTENING_PORT;
use config::{NymConfig, OptionalSet};
use config::NymConfig;
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use std::path::PathBuf;
use std::str::FromStr;
mod template;
@@ -95,15 +92,6 @@ 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
@@ -133,50 +121,6 @@ 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)]
+5 -5
View File
@@ -23,16 +23,16 @@ id = '{{ client.id }}'
# to claim bandwidth without presenting bandwidth credentials.
disabled_credentials_mode = {{ client.disabled_credentials_mode }}
# Addresses to nyxd validators via which the client can communicate with the chain.
nyxd_urls = [
{{#each client.nyxd_urls }}
# Addresses to nymd validators via which the client can communicate with the chain.
validator_urls = [
{{#each client.validator_urls }}
'{{this}}',
{{/each}}
]
# Addresses to APIs running on validator from which the client gets the view of the network.
nym_api_urls = [
{{#each client.nym_api_urls }}
validator_api_urls = [
{{#each client.validator_api_urls }}
'{{this}}',
{{/each}}
]
+25 -38
View File
@@ -1,14 +1,12 @@
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::error::Error;
use crate::client::config::Config;
use crate::error::ClientError;
use crate::websocket;
use client_connections::TransmissionLane;
use client_core::client::base_client::{
non_wasm_helpers, BaseClientBuilder, ClientInput, ClientOutput, ClientState,
non_wasm_helpers, BaseClientBuilder, ClientInput, ClientOutput,
};
use client_core::client::inbound_messages::InputMessage;
use client_core::client::key_manager::KeyManager;
@@ -20,7 +18,7 @@ use log::*;
use nymsphinx::addressing::clients::Recipient;
use nymsphinx::anonymous_replies::requests::AnonymousSenderTag;
use nymsphinx::receiver::ReconstructedMessage;
use task::TaskManager;
use task::{wait_for_signal, ShutdownNotifier};
pub(crate) mod config;
@@ -51,18 +49,18 @@ impl SocketClient {
let mut client_config =
validator_client::Config::try_from_nym_network_details(&details)
.expect("failed to construct validator client config");
let nyxd_url = config
let nymd_url = config
.get_base()
.get_validator_endpoints()
.pop()
.expect("No nyxd validator endpoint provided");
.expect("No nymd validator endpoint provided");
let api_url = config
.get_base()
.get_nym_api_endpoints()
.get_validator_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);
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 =
@@ -86,55 +84,52 @@ impl SocketClient {
config: &Config,
client_input: ClientInput,
client_output: ClientOutput,
client_state: ClientState,
self_address: &Recipient,
shutdown: task::TaskClient,
self_address: Recipient,
) {
info!("Starting websocket listener...");
let ClientInput {
shared_lane_queue_lengths,
connection_command_sender,
input_sender,
} = client_input;
let ClientOutput {
received_buffer_request_sender,
} = client_output;
let received_buffer_request_sender = client_output.received_buffer_request_sender;
let ClientState {
shared_lane_queue_lengths,
reply_controller_sender,
} = client_state;
let websocket_handler = websocket::HandlerBuilder::new(
let websocket_handler = websocket::Handler::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);
websocket::Listener::new(config.get_listening_port()).start(websocket_handler);
}
/// blocking version of `start_socket` method. Will run forever (or until SIGINT is sent)
pub async fn run_socket_forever(self) -> Result<(), Box<dyn Error + Send + Sync>> {
pub async fn run_socket_forever(self) -> Result<(), ClientError> {
let mut shutdown = self.start_socket().await?;
wait_for_signal().await;
let res = task::wait_for_signal_and_error(&mut shutdown).await;
println!(
"Received signal - the client will terminate now (threads are not yet nicely stopped, if you see stack traces that's alright)."
);
log::info!("Sending shutdown");
shutdown.signal_shutdown().ok();
// Some of these components have shutdown signalling implemented as part of socks5 work,
// but since it's not fully implemented (yet) for all the components of the native client,
// we don't try to wait and instead just stop immediately.
log::info!("Waiting for tasks to finish... (Press ctrl-c to force)");
shutdown.wait_for_shutdown().await;
log::info!("Stopping nym-client");
res
Ok(())
}
pub async fn start_socket(self) -> Result<TaskManager, ClientError> {
pub async fn start_socket(self) -> Result<ShutdownNotifier, ClientError> {
if !self.config.get_socket_type().is_websocket() {
return Err(ClientError::InvalidSocketMode);
}
@@ -154,21 +149,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(),
);
Self::start_websocket_listener(&self.config, client_input, client_output, self_address);
info!("Client startup finished!");
info!("The address of this client is: {}", self_address);
Ok(started_client.task_manager)
Ok(started_client.shutdown_notifier)
}
pub async fn start_direct(self) -> Result<DirectClient, ClientError> {
@@ -205,7 +192,7 @@ impl SocketClient {
Ok(DirectClient {
client_input,
reconstructed_receiver,
_shutdown_notifier: started_client.task_manager,
_shutdown_notifier: started_client.shutdown_notifier,
})
}
}
@@ -215,7 +202,7 @@ pub struct DirectClient {
reconstructed_receiver: ReconstructedMessagesReceiver,
// we need to keep reference to this guy otherwise things will start dropping
_shutdown_notifier: TaskManager,
_shutdown_notifier: ShutdownNotifier,
}
impl DirectClient {
+17 -21
View File
@@ -8,7 +8,6 @@ use crate::{
};
use clap::Args;
use config::NymConfig;
use crypto::asymmetric::identity;
use nymsphinx::addressing::clients::Recipient;
use serde::Serialize;
use std::fmt::Display;
@@ -22,26 +21,24 @@ pub(crate) struct Init {
/// Id of the gateway we are going to connect to.
#[clap(long)]
gateway: Option<identity::PublicKey>,
gateway: Option<String>,
/// 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 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 nymd validators
#[clap(long)]
nymd_validators: Option<String>,
/// Comma separated list of rest endpoints of the API validators
#[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>>,
#[clap(long)]
api_validators: Option<String>,
/// Whether to not start the websocket
#[clap(long)]
disable_socket: Option<bool>,
disable_socket: bool,
/// Port for the socket (if applicable) to listen on in all subsequent runs
#[clap(short, long)]
@@ -49,18 +46,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, hide = true)]
#[clap(long, hidden = true)]
fastmode: bool,
/// Disable loop cover traffic and the Poisson rate limiter (for debugging only)
#[clap(long, hide = true)]
#[clap(long, hidden = 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: Option<bool>,
enabled_credentials_mode: bool,
/// Save a summary of the initialization to a json file
#[clap(long)]
@@ -70,14 +67,13 @@ pub(crate) struct Init {
impl From<Init> for OverrideConfig {
fn from(init_config: Init) -> Self {
OverrideConfig {
nym_apis: init_config.nym_apis,
nymd_validators: init_config.nymd_validators,
api_validators: init_config.api_validators,
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,
}
@@ -131,16 +127,16 @@ 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;
let user_chosen_gateway_id = args.gateway.clone();
// 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::<_, Config, _>(
register_gateway,
user_chosen_gateway_id.map(|id| id.to_base58_string()),
user_chosen_gateway_id,
config.get_base(),
)
.await
@@ -156,14 +152,14 @@ 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: {address}\n");
println!("\nThe address of this client is: {}\n", address);
Ok(())
}
+83 -43
View File
@@ -1,31 +1,53 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::config::{BaseConfig, Config};
use build_information::BinaryBuildInformation;
use crate::client::config::{Config, SocketType};
use crate::error::ClientError;
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;
lazy_static! {
pub static ref PRETTY_BUILD_INFORMATION: String =
BinaryBuildInformation::new(env!("CARGO_PKG_VERSION")).pretty_print();
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"),
)
}
// Helper for passing LONG_VERSION to clap
fn pretty_build_info_static() -> &'static str {
&PRETTY_BUILD_INFORMATION
fn long_version_static() -> &'static str {
Box::leak(long_version().into_boxed_str())
}
#[derive(Parser)]
#[clap(author = "Nymtech", version, long_version = pretty_build_info_static(), about)]
#[clap(author = "Nymtech", version, long_version = long_version_static(), about)]
pub(crate) struct Cli {
/// Path pointing to an env file that configures the client.
#[clap(short, long)]
@@ -53,57 +75,75 @@ pub(crate) enum Commands {
// Configuration that can be overridden.
pub(crate) struct OverrideConfig {
nym_apis: Option<Vec<url::Url>>,
disable_socket: Option<bool>,
nymd_validators: Option<String>,
api_validators: Option<String>,
disable_socket: bool,
port: Option<u16>,
fastmode: bool,
no_cover: bool,
#[cfg(feature = "coconut")]
nyxd_urls: Option<Vec<url::Url>>,
#[cfg(feature = "coconut")]
enabled_credentials_mode: Option<bool>,
enabled_credentials_mode: bool,
}
pub(crate) async fn execute(args: &Cli) -> Result<(), Box<dyn Error + Send + Sync>> {
pub(crate) async fn execute(args: &Cli) -> Result<(), ClientError> {
let bin_name = "nym-native-client";
match &args.command {
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::command(), bin_name),
Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name),
Commands::Completions(s) => s.generate(&mut Cli::into_app(), bin_name),
Commands::GenerateFigSpec => fig_generate(&mut Cli::into_app(), bin_name),
}
Ok(())
}
pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Config {
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,
);
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_validator_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_validator_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);
}
#[cfg(feature = "coconut")]
{
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),
);
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
+17 -24
View File
@@ -1,8 +1,6 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::error::Error;
use crate::{
client::{config::Config, SocketClient},
commands::{override_config, OverrideConfig},
@@ -11,7 +9,6 @@ use crate::{
use clap::Args;
use config::NymConfig;
use crypto::asymmetric::identity;
use log::*;
use version_checker::is_minor_version_compatible;
@@ -21,24 +18,22 @@ pub(crate) struct Run {
#[clap(long)]
id: 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 nymd validators
#[clap(long)]
nymd_validators: Option<String>,
/// Comma separated list of rest endpoints of the API validators
#[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>>,
#[clap(long)]
api_validators: Option<String>,
/// 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<identity::PublicKey>,
gateway: Option<String>,
/// Whether to not start the websocket
#[clap(long)]
disable_socket: Option<bool>,
disable_socket: bool,
/// Port for the socket to listen on
#[clap(short, long)]
@@ -46,31 +41,29 @@ 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, hide = true)]
#[clap(long, hidden = true)]
fastmode: bool,
/// Disable loop cover traffic and the Poisson rate limiter (for debugging only)
#[clap(long, hide = true)]
#[clap(long, hidden = 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: Option<bool>,
enabled_credentials_mode: bool,
}
impl From<Run> for OverrideConfig {
fn from(run_config: Run) -> Self {
OverrideConfig {
nym_apis: run_config.nym_apis,
nymd_validators: run_config.nymd_validators,
api_validators: run_config.api_validators,
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,
}
@@ -96,14 +89,14 @@ fn version_check(cfg: &Config) -> bool {
}
}
pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn Error + Send + Sync>> {
pub(crate) async fn execute(args: &Run) -> Result<(), ClientError> {
let id = &args.id;
let mut config = match Config::load_from_file(Some(id)) {
Ok(cfg) => cfg,
Err(err) => {
error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {err})", id);
return Err(Box::new(ClientError::FailedToLoadConfig(id.to_string())));
error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {})", id, err);
return Err(ClientError::FailedToLoadConfig(id.to_string()));
}
};
@@ -116,7 +109,7 @@ pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn Error + Send + Syn
if !version_check(&config) {
error!("failed the local version check");
return Err(Box::new(ClientError::FailedLocalVersionCheck));
return Err(ClientError::FailedLocalVersionCheck);
}
SocketClient::new(config).run_socket_forever().await
+3 -3
View File
@@ -59,7 +59,7 @@ pub(crate) struct Upgrade {
fn parse_config_version(config: &Config) -> Version {
let version = Version::parse(config.get_base().get_version()).unwrap_or_else(|err| {
eprintln!("failed to parse client version! - {err}");
eprintln!("failed to parse client version! - {:?}", err);
process::exit(1)
});
@@ -110,7 +110,7 @@ fn minor_0_12_upgrade(
.set_custom_version(to_version.to_string().as_ref());
config.save_to_file(None).unwrap_or_else(|err| {
eprintln!("failed to overwrite config file! - {err}");
eprintln!("failed to overwrite config file! - {:?}", err);
print_failed_upgrade(config_version, &to_version);
process::exit(1);
});
@@ -146,7 +146,7 @@ pub(crate) fn execute(args: &Upgrade) {
let id = &args.id;
let existing_config = Config::load_from_file(Some(id)).unwrap_or_else(|err| {
eprintln!("failed to load existing config file! - {err}");
eprintln!("failed to load existing config file! - {:?}", err);
process::exit(1)
});
+2 -1
View File
@@ -1,3 +1,4 @@
use client_core::client::replies::reply_storage::fs_backend;
use client_core::error::ClientCoreError;
#[derive(thiserror::Error, Debug)]
@@ -6,7 +7,7 @@ pub enum ClientError {
IoError(#[from] std::io::Error),
#[error("client-core error: {0}")]
ClientCoreError(#[from] ClientCoreError),
ClientCoreError(#[from] ClientCoreError<fs_backend::Backend>),
#[error("Failed to load config for: {0}")]
FailedToLoadConfig(String),
+3 -4
View File
@@ -1,9 +1,8 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::error::Error;
use clap::{crate_version, Parser};
use error::ClientError;
use logging::setup_logging;
use network_defaults::setup_env;
@@ -13,12 +12,12 @@ pub mod error;
pub mod websocket;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
async fn main() -> Result<(), ClientError> {
setup_logging();
println!("{}", banner());
let args = commands::Cli::parse();
setup_env(args.config_env_file.as_ref());
setup_env(args.config_env_file.clone());
commands::execute(&args).await
}
+111 -124
View File
@@ -2,9 +2,8 @@
// SPDX-License-Identifier: Apache-2.0
use client_connections::{
ConnectionCommand, ConnectionCommandSender, ConnectionId, LaneQueueLengths, TransmissionLane,
ConnectionCommand, ConnectionCommandSender, LaneQueueLengths, TransmissionLane,
};
use client_core::client::replies::reply_controller::requests::ReplyControllerSender;
use client_core::client::{
inbound_messages::{InputMessage, InputMessageSender},
received_buffer::{
@@ -17,9 +16,7 @@ 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},
@@ -38,49 +35,6 @@ impl Default for ReceivedResponseType {
}
}
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 {
pub(crate) fn new(
msg_input: InputMessageSender,
client_connection_tx: ConnectionCommandSender,
buffer_requester: ReceivedBufferRequestSender,
self_full_address: &Recipient,
lane_queue_lengths: LaneQueueLengths,
reply_controller_sender: ReplyControllerSender,
) -> Self {
Self {
msg_input,
client_connection_tx,
buffer_requester,
self_full_address: *self_full_address,
lane_queue_lengths,
reply_controller_sender,
}
}
// TODO: make sure we only ever have one active handler
pub fn create_active_handler(&self) -> Handler {
Handler {
msg_input: self.msg_input.clone(),
client_connection_tx: self.client_connection_tx.clone(),
buffer_requester: self.buffer_requester.clone(),
self_full_address: self.self_full_address,
socket: None,
received_response_type: Default::default(),
lane_queue_lengths: self.lane_queue_lengths.clone(),
reply_controller_sender: self.reply_controller_sender.clone(),
}
}
}
pub(crate) struct Handler {
msg_input: InputMessageSender,
client_connection_tx: ConnectionCommandSender,
@@ -89,7 +43,21 @@ pub(crate) struct Handler {
socket: Option<WebSocketStream<TcpStream>>,
received_response_type: ReceivedResponseType,
lane_queue_lengths: LaneQueueLengths,
reply_controller_sender: ReplyControllerSender,
}
// clone is used to use handler on a new connection, which initially is `None`
impl Clone for Handler {
fn clone(&self) -> Self {
Handler {
msg_input: self.msg_input.clone(),
client_connection_tx: self.client_connection_tx.clone(),
buffer_requester: self.buffer_requester.clone(),
self_full_address: self.self_full_address,
socket: None,
received_response_type: Default::default(),
lane_queue_lengths: self.lane_queue_lengths.clone(),
}
}
}
impl Drop for Handler {
@@ -105,46 +73,22 @@ 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}");
pub(crate) fn new(
msg_input: InputMessageSender,
client_connection_tx: ConnectionCommandSender,
buffer_requester: ReceivedBufferRequestSender,
self_full_address: Recipient,
lane_queue_lengths: LaneQueueLengths,
) -> Self {
Handler {
msg_input,
client_connection_tx,
buffer_requester,
self_full_address,
socket: None,
received_response_type: Default::default(),
lane_queue_lengths,
}
Some(ServerResponse::LaneQueueLength {
lane: connection_id,
queue_length,
})
}
async fn handle_send(
@@ -171,11 +115,27 @@ impl Handler {
.expect("InputMessageReceiver has stopped receiving!");
// Only reply back with a `LaneQueueLength` if the sender providided a connection id
let TransmissionLane::ConnectionId(connection_id) = lane else {
return None
let connection_id = match lane {
TransmissionLane::General
| TransmissionLane::ReplySurbRequest
| TransmissionLane::Retransmission
| TransmissionLane::AdditionalReplySurbs => return None,
TransmissionLane::ConnectionId(id) => id,
};
self.get_lane_queue_length(connection_id).await
// 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
}
async fn handle_send_anonymous(
@@ -202,11 +162,27 @@ impl Handler {
.expect("InputMessageReceiver has stopped receiving!");
// Only reply back with a `LaneQueueLength` if the sender providided a connection id
let TransmissionLane::ConnectionId(connection_id) = lane else {
return None
let connection_id = match lane {
TransmissionLane::General
| TransmissionLane::ReplySurbRequest
| TransmissionLane::Retransmission
| TransmissionLane::AdditionalReplySurbs => return None,
TransmissionLane::ConnectionId(id) => id,
};
self.get_lane_queue_length(connection_id).await
// 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
}
async fn handle_reply(
@@ -229,11 +205,27 @@ impl Handler {
.expect("InputMessageReceiver has stopped receiving!");
// Only reply back with a `LaneQueueLength` if the sender providided a connection id
let TransmissionLane::ConnectionId(connection_id) = lane else {
return None
let connection_id = match lane {
TransmissionLane::General
| TransmissionLane::ReplySurbRequest
| TransmissionLane::Retransmission
| TransmissionLane::AdditionalReplySurbs => return None,
TransmissionLane::ConnectionId(id) => id,
};
self.get_lane_queue_length(connection_id).await
// 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
}
fn handle_self_address(&self) -> ServerResponse {
@@ -247,8 +239,20 @@ impl Handler {
None
}
async fn handle_get_lane_queue_length(&self, connection_id: u64) -> Option<ServerResponse> {
self.get_lane_queue_length(connection_id).await
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_request(&mut self, request: ClientRequest) -> Option<ServerResponse> {
@@ -277,7 +281,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).await,
ClientRequest::GetLaneQueueLength(id) => self.handle_get_lane_queue_length(id),
}
}
@@ -358,12 +362,8 @@ impl Handler {
}
}
async fn listen_for_requests(
&mut self,
mut msg_receiver: ReconstructedMessagesReceiver,
mut task_client: task::TaskClient,
) {
while !task_client.is_shutdown() {
async fn listen_for_requests(&mut self, mut msg_receiver: ReconstructedMessagesReceiver) {
loop {
tokio::select! {
// we can either get a client request from the websocket
socket_msg = self.next_websocket_request() => {
@@ -373,7 +373,7 @@ impl Handler {
let socket_msg = match socket_msg.unwrap() {
Ok(socket_msg) => socket_msg,
Err(err) => {
warn!("failed to obtain message from websocket stream! stopping connection handler: {err}");
warn!("failed to obtain message from websocket stream! stopping connection handler: {}", err);
break;
}
};
@@ -397,33 +397,21 @@ impl Handler {
error!("mix messages sender was unexpectedly closed! this shouldn't have ever happened! (unless we're shutting down - TODO: implement proper graceful shutdown handler)");
return
};
if let Err(err) = self.push_websocket_received_plaintexts(mix_messages).await {
warn!("failed to send sphinx packets back to the client - {err}, assuming the connection is dead");
if let Err(e) = self.push_websocket_received_plaintexts(mix_messages).await {
warn!("failed to send sphinx packets back to the client - {:?}, assuming the connection is dead", e);
break;
}
}
_ = task_client.recv() => {
log::trace!("Websocket handler: Received shutdown");
}
}
}
log::debug!("Websocket handler: Exiting");
}
// consume self to make sure `drop` is called after this is done
pub(crate) async fn handle_connection(
mut self,
socket: TcpStream,
mut task_client: task::TaskClient,
) {
// We don't want a crash in the connection handler to trigger a shutdown of the whole
// process.
task_client.mark_as_success();
pub(crate) async fn handle_connection(mut self, socket: TcpStream) {
let ws_stream = match accept_async(socket).await {
Ok(ws_stream) => ws_stream,
Err(err) => {
warn!("error while performing the websocket handshake - {err}");
warn!("error while performing the websocket handshake - {:?}", err);
return;
}
};
@@ -438,8 +426,7 @@ impl Handler {
))
.expect("the buffer request failed!");
self.listen_for_requests(reconstructed_receiver, task_client)
.await;
self.listen_for_requests(reconstructed_receiver).await;
}
}
+9 -28
View File
@@ -1,7 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::handler::HandlerBuilder;
use super::handler::Handler;
use log::*;
use std::{net::SocketAddr, process, sync::Arc};
use tokio::io::AsyncWriteExt;
@@ -32,11 +32,11 @@ impl Listener {
}
}
pub(crate) async fn run(&mut self, handler: HandlerBuilder, mut task_client: task::TaskClient) {
pub(crate) async fn run(&mut self, handler: Handler) {
let tcp_listener = match tokio::net::TcpListener::bind(self.address).await {
Ok(listener) => listener,
Err(err) => {
error!("Failed to bind to {} - {err}. Are you sure nothing else is running on the specified port and your user has sufficient permission to bind to the requested address?", self.address);
error!("Failed to bind to {} - {}. Are you sure nothing else is running on the specified port and your user has sufficient permission to bind to the requested address?", self.address, err);
process::exit(1);
}
};
@@ -45,23 +45,10 @@ impl Listener {
loop {
tokio::select! {
// When the handler finishes we check if shutdown is signalled
_ = notify.notified() => {
if task_client.is_shutdown() {
log::trace!("Websocket listener: detected shutdown after connection closed");
break;
}
// our connection terminated - we are open to a new one now!
self.state = State::AwaitingConnection;
}
// ... but when there is no connected client at the time of shutdown being
// signalled, we handle it here.
_ = task_client.recv() => {
if !self.state.is_connected() {
log::trace!("Not connected: shutting down");
break;
}
}
new_conn = tcp_listener.accept() => {
match new_conn {
Ok((mut socket, remote_addr)) => {
@@ -76,37 +63,31 @@ impl Listener {
Ok(_) => trace!(
"closed the connection between attempting websocket handshake"
),
Err(err) => warn!("failed to cleanly close the connection - {err}"),
Err(e) => warn!("failed to cleanly close the connection - {:?}", e),
};
} else {
// even though we're spawning a new task with the handler here, we will only ever spawn a single one.
// it's done so that any new connections to this listener could be rejected rather than left
// hanging because the executor doesn't come back here
let notify_clone = Arc::clone(&notify);
let fresh_handler = handler.create_active_handler();
let task_client_handler = task_client.clone();
let fresh_handler = handler.clone();
tokio::spawn(async move {
fresh_handler.handle_connection(socket, task_client_handler).await;
fresh_handler.handle_connection(socket).await;
notify_clone.notify_one();
});
self.state = State::Connected;
}
}
Err(err) => warn!("failed to get client: {err}"),
Err(e) => warn!("failed to get client: {:?}", e),
}
}
}
}
log::debug!("Websocket listener: Exiting");
}
pub(crate) fn start(
mut self,
handler: HandlerBuilder,
shutdown: task::TaskClient,
) -> JoinHandle<()> {
pub(crate) fn start(mut self, handler: Handler) -> JoinHandle<()> {
info!("Running websocket on {:?}", self.address.to_string());
tokio::spawn(async move { self.run(handler, shutdown).await })
tokio::spawn(async move { self.run(handler).await })
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub(crate) use handler::HandlerBuilder;
pub(crate) use handler::Handler;
pub(crate) use listener::Listener;
pub(crate) mod handler;
@@ -132,7 +132,7 @@ impl ClientRequest {
Err(err) => {
return Err(error::Error::new(
ErrorKind::MalformedRequest,
format!("malformed recipient: {err}"),
format!("malformed recipient: {:?}", err),
))
}
};
@@ -210,7 +210,7 @@ impl ClientRequest {
Err(err) => {
return Err(error::Error::new(
ErrorKind::MalformedRequest,
format!("malformed recipient: {err}"),
format!("malformed recipient: {:?}", err),
))
}
};
@@ -173,7 +173,7 @@ impl ServerResponse {
Err(err) => {
return Err(error::Error::new(
ErrorKind::MalformedResponse,
format!("malformed Recipient: {err}"),
format!("malformed Recipient: {:?}", err),
))
}
};
@@ -249,7 +249,7 @@ impl ServerResponse {
Err(err) => {
return Err(error::Error::new(
ErrorKind::MalformedResponse,
format!("malformed error message: {err}"),
format!("malformed error message: {:?}", err),
))
}
};
+6 -5
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.5"
version = "1.1.4"
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,9 @@ name = "nym_socks5"
path = "src/lib.rs"
[dependencies]
clap = { version = "4.0", features = ["cargo", "derive"] }
clap = { version = "3.2", features = ["cargo", "derive"] }
dirs = "4.0"
futures = "0.3"
lazy_static = "1.4.0"
log = "0.4"
pin-project = "1.0"
pretty_env_logger = "0.4"
@@ -27,7 +26,6 @@ tokio = { version = "1.21.2", 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 }
@@ -47,9 +45,12 @@ 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 = ["nyxd-client"] }
validator-client = { path = "../../common/client-libs/validator-client", features = ["nymd-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"] }
+8
View File
@@ -0,0 +1,8 @@
// 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 -49
View File
@@ -2,16 +2,13 @@
// 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, DebugConfig};
use client_core::config::{ClientCoreConfigTrait, Config as BaseConfig, DebugConfig};
use config::defaults::DEFAULT_SOCKS5_LISTENING_PORT;
use config::{NymConfig, OptionalSet};
use config::NymConfig;
use nymsphinx::addressing::clients::Recipient;
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use std::path::PathBuf;
use std::str::FromStr;
mod template;
@@ -130,50 +127,6 @@ 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)]
+5 -5
View File
@@ -23,16 +23,16 @@ id = '{{ client.id }}'
# to claim bandwidth without presenting bandwidth credentials.
disabled_credentials_mode = {{ client.disabled_credentials_mode }}
# Addresses to nyxd validators via which the client can communicate with the chain.
nyxd_urls = [
{{#each client.nyxd_urls }}
# Addresses to nymd validators via which the client can communicate with the chain.
validator_urls = [
{{#each client.validator_urls }}
'{{this}}',
{{/each}}
]
# Addresses to APIs running on validator from which the client gets the view of the network.
nym_api_urls = [
{{#each client.nym_api_urls }}
validator_api_urls = [
{{#each client.validator_api_urls }}
'{{this}}',
{{/each}}
]
+13 -22
View File
@@ -9,7 +9,7 @@ use crate::socks::{
server::SphinxSocksServer,
};
use client_core::client::base_client::{
non_wasm_helpers, BaseClientBuilder, ClientInput, ClientOutput, ClientState,
non_wasm_helpers, BaseClientBuilder, ClientInput, ClientOutput,
};
use client_core::client::key_manager::KeyManager;
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
@@ -19,7 +19,7 @@ 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::{wait_for_signal_and_error, ShutdownListener, ShutdownNotifier};
pub mod config;
@@ -60,18 +60,18 @@ impl NymClient {
let mut client_config =
validator_client::Config::try_from_nym_network_details(&details)
.expect("failed to construct validator client config");
let nyxd_url = config
let nymd_url = config
.get_base()
.get_validator_endpoints()
.pop()
.expect("No nyxd validator endpoint provided");
.expect("No nymd validator endpoint provided");
let api_url = config
.get_base()
.get_nym_api_endpoints()
.get_validator_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);
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 =
@@ -95,27 +95,20 @@ impl NymClient {
config: &Config,
client_input: ClientInput,
client_output: ClientOutput,
client_status: ClientState,
self_address: Recipient,
shutdown: TaskClient,
shutdown: ShutdownListener,
) {
info!("Starting socks5 listener...");
let auth_methods = vec![AuthenticationMethods::NoAuth as u8];
let allowed_users: Vec<User> = Vec::new();
let ClientInput {
shared_lane_queue_lengths,
connection_command_sender,
input_sender,
} = client_input;
let ClientOutput {
received_buffer_request_sender,
} = client_output;
let ClientState {
shared_lane_queue_lengths,
reply_controller_sender: _,
} = client_status;
let received_buffer_request_sender = client_output.received_buffer_request_sender;
let authenticator = Authenticator::new(auth_methods, allowed_users);
let mut sphinx_socks = SphinxSocksServer::new(
@@ -171,7 +164,7 @@ impl NymClient {
let mut shutdown = self.start().await?;
// Listen to status messages from task, that we forward back to the caller
shutdown.start_status_listener(sender).await;
shutdown.start_status_listener(sender);
let res = tokio::select! {
biased;
@@ -207,7 +200,7 @@ impl NymClient {
res
}
pub async fn start(self) -> Result<TaskManager, Socks5ClientError> {
pub async fn start(self) -> Result<ShutdownNotifier, Socks5ClientError> {
let base_builder = BaseClientBuilder::new_from_base_config(
self.config.get_base(),
self.key_manager,
@@ -223,20 +216,18 @@ 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(),
started_client.shutdown_notifier.subscribe(),
);
info!("Client startup finished!");
info!("The address of this client is: {}", self_address);
Ok(started_client.task_manager)
Ok(started_client.shutdown_notifier)
}
}
+19 -25
View File
@@ -8,7 +8,6 @@ use crate::{
};
use clap::Args;
use config::NymConfig;
use crypto::asymmetric::identity;
use nymsphinx::addressing::clients::Recipient;
use serde::Serialize;
use std::fmt::Display;
@@ -22,35 +21,32 @@ pub(crate) struct Init {
/// Address of the socks5 provider to send messages to.
#[clap(long)]
provider: Recipient,
provider: String,
/// 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.
// 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>,
#[clap(long)]
use_anonymous_sender_tag: bool,
/// Id of the gateway we are going to connect to.
#[clap(long)]
gateway: Option<identity::PublicKey>,
gateway: Option<String>,
/// 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 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 nymd validators
#[clap(long)]
nymd_validators: Option<String>,
/// Comma separated list of rest endpoints of the API validators
#[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>>,
#[clap(long)]
api_validators: Option<String>,
/// Port for the socket to listen on in all subsequent runs
#[clap(short, long)]
@@ -58,18 +54,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, hide = true)]
#[clap(long, hidden = true)]
fastmode: bool,
/// Disable loop cover traffic and the Poisson rate limiter (for debugging only)
#[clap(long, hide = true)]
#[clap(long, hidden = 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: Option<bool>,
enabled_credentials_mode: bool,
/// Save a summary of the initialization to a json file
#[clap(long)]
@@ -79,14 +75,12 @@ pub(crate) struct Init {
impl From<Init> for OverrideConfig {
fn from(init_config: Init) -> Self {
OverrideConfig {
nym_apis: init_config.nym_apis,
nymd_validators: init_config.nymd_validators,
api_validators: init_config.api_validators,
port: init_config.port,
use_anonymous_replies: init_config.use_reply_surbs,
use_anonymous_sender_tag: init_config.use_anonymous_sender_tag,
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,
}
@@ -141,19 +135,19 @@ 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;
let user_chosen_gateway_id = args.gateway.clone();
// Load and potentially override config
let mut config = override_config(
Config::new(id, &provider_address.to_string()),
Config::new(id, provider_address),
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::<_, Config, _>(
register_gateway,
user_chosen_gateway_id.map(|id| id.to_base58_string()),
user_chosen_gateway_id,
config.get_base(),
)
.await
+80 -42
View File
@@ -1,31 +1,55 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::config::{BaseConfig, Config};
use build_information::BinaryBuildInformation;
use std::error::Error;
use crate::client::config::Config;
use clap::CommandFactory;
use clap::{Parser, Subcommand};
use completions::{fig_generate, ArgShell};
use config::OptionalSet;
use lazy_static::lazy_static;
use std::error::Error;
use config::parse_validators;
pub mod init;
pub(crate) mod run;
pub(crate) mod upgrade;
lazy_static! {
pub static ref PRETTY_BUILD_INFORMATION: String =
BinaryBuildInformation::new(env!("CARGO_PKG_VERSION")).pretty_print();
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"),
)
}
// Helper for passing LONG_VERSION to clap
fn pretty_build_info_static() -> &'static str {
&PRETTY_BUILD_INFORMATION
fn long_version_static() -> &'static str {
Box::leak(long_version().into_boxed_str())
}
#[derive(Parser)]
#[clap(author = "Nymtech", version, long_version = pretty_build_info_static(), about)]
#[clap(author = "Nymtech", version, long_version = long_version_static(), about)]
pub(crate) struct Cli {
/// Path pointing to an env file that configures the client.
#[clap(short, long)]
@@ -55,16 +79,15 @@ pub(crate) enum Commands {
// Configuration that can be overridden.
pub(crate) struct OverrideConfig {
nym_apis: Option<Vec<url::Url>>,
nymd_validators: Option<String>,
api_validators: Option<String>,
port: Option<u16>,
use_anonymous_replies: Option<bool>,
use_anonymous_sender_tag: bool,
fastmode: bool,
no_cover: bool,
#[cfg(feature = "coconut")]
nyxd_urls: Option<Vec<url::Url>>,
#[cfg(feature = "coconut")]
enabled_credentials_mode: Option<bool>,
enabled_credentials_mode: bool,
}
pub(crate) async fn execute(args: &Cli) -> Result<(), Box<dyn Error + Send + Sync>> {
@@ -74,38 +97,53 @@ 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::command(), bin_name),
Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name),
Commands::Completions(s) => s.generate(&mut Cli::into_app(), bin_name),
Commands::GenerateFigSpec => fig_generate(&mut Cli::into_app(), bin_name),
}
Ok(())
}
pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Config {
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,
);
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_validator_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_validator_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);
}
#[cfg(feature = "coconut")]
{
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),
);
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
+17 -23
View File
@@ -9,9 +9,7 @@ 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)]
@@ -29,27 +27,25 @@ 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.
// 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>,
#[clap(long)]
use_anonymous_sender_tag: bool,
/// Address of the socks5 provider to send messages to.
#[clap(long)]
provider: Option<Recipient>,
provider: Option<String>,
/// 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<identity::PublicKey>,
gateway: 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 nymd validators
#[clap(long)]
nymd_validators: Option<String>,
/// Comma separated list of rest endpoints of the Nym APIs
#[clap(long, value_delimiter = ',')]
nym_apis: Option<Vec<url::Url>>,
/// Comma separated list of rest endpoints of the API validators
#[clap(long)]
api_validators: Option<String>,
/// Port for the socket to listen on
#[clap(short, long)]
@@ -57,31 +53,29 @@ 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, hide = true)]
#[clap(long, hidden = true)]
fastmode: bool,
/// Disable loop cover traffic and the Poisson rate limiter (for debugging only)
#[clap(long, hide = true)]
#[clap(long, hidden = 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: Option<bool>,
enabled_credentials_mode: bool,
}
impl From<Run> for OverrideConfig {
fn from(run_config: Run) -> Self {
OverrideConfig {
nym_apis: run_config.nym_apis,
nymd_validators: run_config.nymd_validators,
api_validators: run_config.api_validators,
port: run_config.port,
use_anonymous_replies: run_config.use_anonymous_replies,
use_anonymous_sender_tag: run_config.use_anonymous_sender_tag,
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,
}
@@ -116,7 +110,7 @@ pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn std::error::Error
let mut config = match Config::load_from_file(Some(id)) {
Ok(cfg) => cfg,
Err(err) => {
error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {err})", id);
error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {})", id, err);
return Err(Box::new(Socks5ClientError::FailedToLoadConfig(
id.to_string(),
)));
+3 -3
View File
@@ -58,7 +58,7 @@ pub(crate) struct Upgrade {
fn parse_config_version(config: &Config) -> Version {
let version = Version::parse(config.get_base().get_version()).unwrap_or_else(|err| {
eprintln!("failed to parse client version! - {err}");
eprintln!("failed to parse client version! - {:?}", err);
process::exit(1)
});
@@ -109,7 +109,7 @@ fn minor_0_12_upgrade(
.set_custom_version(to_version.to_string().as_ref());
config.save_to_file(None).unwrap_or_else(|err| {
eprintln!("failed to overwrite config file! - {err}");
eprintln!("failed to overwrite config file! - {:?}", err);
print_failed_upgrade(config_version, &to_version);
process::exit(1);
});
@@ -145,7 +145,7 @@ pub(crate) fn execute(args: &Upgrade) {
let id = &args.id;
let existing_config = Config::load_from_file(Some(id)).unwrap_or_else(|err| {
eprintln!("failed to load existing config file! - {err}");
eprintln!("failed to load existing config file! - {:?}", err);
process::exit(1)
});
+2 -8
View File
@@ -1,6 +1,6 @@
use crate::socks::types::SocksProxyError;
use client_core::client::replies::reply_storage::fs_backend;
use client_core::error::ClientCoreError;
use socks5_requests::ConnectionId;
#[derive(thiserror::Error, Debug)]
pub enum Socks5ClientError {
@@ -8,7 +8,7 @@ pub enum Socks5ClientError {
IoError(#[from] std::io::Error),
#[error("client-core error: {0}")]
ClientCoreError(#[from] ClientCoreError),
ClientCoreError(#[from] ClientCoreError<fs_backend::Backend>),
#[error("SOCKS proxy error")]
SocksProxyError(SocksProxyError),
@@ -21,10 +21,4 @@ pub enum Socks5ClientError {
#[error("Fail to bind address")]
FailToBindAddress,
#[error("Network requester: connection id {connection_id}: {error}")]
NetworkRequesterError {
connection_id: ConnectionId,
error: String,
},
}
+1 -1
View File
@@ -18,7 +18,7 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
println!("{}", banner());
let args = commands::Cli::parse();
setup_env(args.config_env_file.as_ref());
setup_env(args.config_env_file.clone());
commands::execute(&args).await
}
+4 -4
View File
@@ -20,7 +20,7 @@ use socks5_requests::{ConnectionId, Message, RemoteAddress, Request};
use std::io;
use std::net::SocketAddr;
use std::pin::Pin;
use task::TaskClient;
use task::ShutdownListener;
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, ReadBuf};
use tokio::{self, net::TcpStream};
@@ -164,7 +164,7 @@ pub(crate) struct SocksClient {
self_address: Recipient,
started_proxy: bool,
lane_queue_lengths: LaneQueueLengths,
shutdown_listener: TaskClient,
shutdown_listener: ShutdownListener,
}
impl Drop for SocksClient {
@@ -190,7 +190,7 @@ impl SocksClient {
controller_sender: ControllerSender,
self_address: &Recipient,
lane_queue_lengths: LaneQueueLengths,
mut shutdown_listener: TaskClient,
mut shutdown_listener: ShutdownListener,
) -> Self {
// If this task fails and exits, we don't want to send shutdown signal
shutdown_listener.mark_as_success();
@@ -220,7 +220,7 @@ impl SocksClient {
}
pub async fn send_error(&mut self, err: SocksProxyError) -> Result<(), SocksProxyError> {
let error_text = format!("{err}");
let error_text = format!("{}", err);
let Some(ref version) = self.socks_version else {
log::error!("Trying to send error without knowing the version");
return Ok(());
+21 -27
View File
@@ -1,3 +1,5 @@
use std::time::Duration;
use futures::channel::mpsc;
use futures::StreamExt;
use log::*;
@@ -7,15 +9,13 @@ use client_core::client::received_buffer::{ReceivedBufferMessage, ReceivedBuffer
use nymsphinx::receiver::ReconstructedMessage;
use proxy_helpers::connection_controller::{ControllerCommand, ControllerSender};
use socks5_requests::Message;
use task::TaskClient;
use crate::error::Socks5ClientError;
use task::ShutdownListener;
pub(crate) struct MixnetResponseListener {
buffer_requester: ReceivedBufferRequestSender,
mix_response_receiver: ReconstructedMessagesReceiver,
controller_sender: ControllerSender,
shutdown: TaskClient,
shutdown: ShutdownListener,
}
impl Drop for MixnetResponseListener {
@@ -25,9 +25,9 @@ impl Drop for MixnetResponseListener {
.unbounded_send(ReceivedBufferMessage::ReceiverDisconnect)
{
if self.shutdown.is_shutdown_poll() {
log::debug!("The buffer request failed: {err}");
log::debug!("The buffer request failed: {}", err);
} else {
log::error!("The buffer request failed: {err}");
log::error!("The buffer request failed: {}", err);
}
}
}
@@ -37,7 +37,7 @@ impl MixnetResponseListener {
pub(crate) fn new(
buffer_requester: ReceivedBufferRequestSender,
controller_sender: ControllerSender,
shutdown: TaskClient,
shutdown: ShutdownListener,
) -> Self {
let (mix_response_sender, mix_response_receiver) = mpsc::unbounded();
buffer_requester
@@ -52,10 +52,7 @@ impl MixnetResponseListener {
}
}
fn on_message(
&self,
reconstructed_message: ReconstructedMessage,
) -> Result<(), Socks5ClientError> {
async fn on_message(&self, reconstructed_message: ReconstructedMessage) {
let raw_message = reconstructed_message.message;
if reconstructed_message.sender_tag.is_some() {
warn!("this message was sent anonymously - it couldn't have come from the service provider");
@@ -63,12 +60,12 @@ impl MixnetResponseListener {
let response = match Message::try_from_bytes(&raw_message) {
Err(err) => {
warn!("failed to parse received response - {err}");
return Ok(());
warn!("failed to parse received response - {:?}", err);
return;
}
Ok(Message::Request(_)) => {
warn!("unexpected request");
return Ok(());
return;
}
Ok(Message::Response(data)) => data,
Ok(Message::NetworkRequesterResponse(r)) => {
@@ -76,10 +73,7 @@ impl MixnetResponseListener {
"Network requester failed on connection id {} with error: {}",
r.connection_id, r.network_requester_error
);
return Err(Socks5ClientError::NetworkRequesterError {
connection_id: r.connection_id,
error: r.network_requester_error,
});
return;
}
};
@@ -90,21 +84,18 @@ impl MixnetResponseListener {
response.is_closed,
))
.unwrap();
Ok(())
}
pub(crate) async fn run(&mut self) {
while !self.shutdown.is_shutdown() {
tokio::select! {
received_responses = self.mix_response_receiver.next() => {
if let Some(received_responses) = received_responses {
received_responses = self.mix_response_receiver.next() => match received_responses {
Some(received_responses) => {
for reconstructed_message in received_responses {
if let Err(err) = self.on_message(reconstructed_message) {
self.shutdown.send_status_msg(Box::new(err));
}
self.on_message(reconstructed_message).await;
}
} else {
},
None => {
log::trace!("MixnetResponseListener: Stopping since channel closed");
break;
}
@@ -114,7 +105,10 @@ impl MixnetResponseListener {
}
}
}
self.shutdown.recv_timeout().await;
#[cfg(not(target_arch = "wasm32"))]
tokio::time::timeout(Duration::from_secs(5), self.shutdown.recv())
.await
.expect("Task stopped without shutdown called");
log::debug!("MixnetResponseListener: Exiting");
}
}
+4 -4
View File
@@ -13,7 +13,7 @@ use nymsphinx::addressing::clients::Recipient;
use proxy_helpers::connection_controller::{BroadcastActiveConnections, Controller};
use std::net::SocketAddr;
use tap::TapFallible;
use task::TaskClient;
use task::ShutdownListener;
use tokio::net::TcpListener;
/// A Socks5 server that listens for connections.
@@ -24,7 +24,7 @@ pub struct SphinxSocksServer {
self_address: Recipient,
client_config: client::Config,
lane_queue_lengths: LaneQueueLengths,
shutdown: TaskClient,
shutdown: ShutdownListener,
}
impl SphinxSocksServer {
@@ -36,7 +36,7 @@ impl SphinxSocksServer {
self_address: Recipient,
lane_queue_lengths: LaneQueueLengths,
client_config: client::Config,
shutdown: TaskClient,
shutdown: ShutdownListener,
) -> Self {
// hardcode ip as we (presumably) ONLY want to listen locally. If we change it, we can
// just modify the config
@@ -103,7 +103,7 @@ impl SphinxSocksServer {
tokio::spawn(async move {
if let Err(err) = client.run().await {
error!("Error! {err}");
error!("Error! {}", err);
if client.send_error(err).await.is_err() {
warn!("Failed to send error code");
};
+1 -1
View File
@@ -40,7 +40,7 @@ pub enum SocksProxyError {
impl std::fmt::Display for SocksProxyError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SocksProxyError::GenericError(err) => write!(f, "GenericError - {err}"),
SocksProxyError::GenericError(err) => write!(f, "GenericError - {}", err),
SocksProxyError::UnsupportedProxyVersion(version) => {
write!(f, "Unsupported proxy version {}", version)
}
+16 -16
View File
@@ -85,8 +85,8 @@ export default class ValidatorClient implements INymClient {
static async connect(
mnemonic: string,
nyxdUrl: string,
nymApiUrl: string,
nymdUrl: string,
validatorApiUrl: string,
prefix: string,
mixnetContract: string,
vestingContract: 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, nyxdUrl, nymApiUrl, prefix, denom);
const signingClient = await SigningClient.connectWithNymSigner(wallet, nymdUrl, validatorApiUrl, prefix, denom);
return new ValidatorClient(signingClient, prefix, mixnetContract, vestingContract, denom);
}
static async connectForQuery(
nyxdUrl: string,
nymApiUrl: string,
nymdUrl: string,
validatorApiUrl: string,
prefix: string,
mixnetContract: string,
vestingContract: string,
denom: string,
): Promise<ValidatorClient> {
const queryClient = await QueryClient.connectWithNym(nyxdUrl, nymApiUrl);
const queryClient = await QueryClient.connectWithNym(nymdUrl, validatorApiUrl);
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 getAllNyxdMixnodes(): Promise<MixNodeBond[]> {
public async getAllNymdMixnodes(): 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 getAllNyxdGateways(): Promise<GatewayBond[]> {
public async getAllNymdGateways(): 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 getAllNyxdSingleMixnodeDelegations(mixIdentity: string): Promise<Delegation[]> {
public async getAllNymdSingleMixnodeDelegations(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 getAllNyxdDelegatorDelegations(delegator: string): Promise<Delegation[]> {
public async getAllNymdDelegatorDelegations(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 getAllNyxdNetworkDelegations(): Promise<Delegation[]> {
public async getAllNymdNetworkDelegations(): 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 { INyxdQuery } from './query-client';
import { INymdQuery } from './query-client';
import {
ContractStateParams,
Delegation,
@@ -25,7 +25,7 @@ interface SmartContractQuery {
queryContractSmart(address: string, queryMsg: Record<string, unknown>): Promise<JsonObject>;
}
export default class NyxdQuerier implements INyxdQuery {
export default class NymdQuerier implements INymdQuery {
client: SmartContractQuery;
constructor(client: SmartContractQuery) {
+32 -32
View File
@@ -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 NyxdQuerier from './nyxd-querier';
import NymdQuerier from './nymd-querier';
import {
ContractStateParams,
Delegation,
@@ -29,7 +29,7 @@ import {
PagedMixnodeResponse,
RewardingStatus,
} from './types';
import NymApiQuerier, { INymApiQuery as INymApiQuery } from './nym-api-querier';
import ValidatorApiQuerier, { IValidatorApiQuery } from './validator-api-querier';
export interface ICosmWasmQuery {
// methods exposed by `CosmWasmClient`
@@ -52,7 +52,7 @@ export interface ICosmWasmQuery {
queryContractSmart(address: string, queryMsg: Record<string, unknown>): Promise<JsonObject>;
}
export interface INyxdQuery {
export interface INymdQuery {
// nym-specific implemented inside NymQuerier
getContractVersion(mixnetContractAddress: string): Promise<MixnetContractVersion>;
@@ -93,46 +93,46 @@ export interface INyxdQuery {
): Promise<RewardingStatus>;
}
export interface IQueryClient extends ICosmWasmQuery, INyxdQuery, INymApiQuery { }
export interface IQueryClient extends ICosmWasmQuery, INymdQuery, IValidatorApiQuery {}
export default class QueryClient extends CosmWasmClient implements IQueryClient {
private nyxdQuerier: NyxdQuerier;
private nymdQuerier: NymdQuerier;
private nymApiQuerier: NymApiQuerier;
private validatorApiQuerier: ValidatorApiQuerier;
private constructor(tmClient: Tendermint34Client, nymApiUrl: string) {
private constructor(tmClient: Tendermint34Client, validatorApiUrl: string) {
super(tmClient);
this.nyxdQuerier = new NyxdQuerier(this);
this.nymApiQuerier = new NymApiQuerier(nymApiUrl);
this.nymdQuerier = new NymdQuerier(this);
this.validatorApiQuerier = new ValidatorApiQuerier(validatorApiUrl);
}
public static async connectWithNym(nyxdUrl: string, nymApiUrl: string): Promise<QueryClient> {
const tmClient = await Tendermint34Client.connect(nyxdUrl);
return new QueryClient(tmClient, nymApiUrl);
public static async connectWithNym(nymdUrl: string, validatorApiUrl: string): Promise<QueryClient> {
const tmClient = await Tendermint34Client.connect(nymdUrl);
return new QueryClient(tmClient, validatorApiUrl);
}
getContractVersion(mixnetContractAddress: string): Promise<MixnetContractVersion> {
return this.nyxdQuerier.getContractVersion(mixnetContractAddress);
return this.nymdQuerier.getContractVersion(mixnetContractAddress);
}
getMixNodesPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedMixnodeResponse> {
return this.nyxdQuerier.getMixNodesPaged(mixnetContractAddress, limit, startAfter);
return this.nymdQuerier.getMixNodesPaged(mixnetContractAddress, limit, startAfter);
}
getGatewaysPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedGatewayResponse> {
return this.nyxdQuerier.getGatewaysPaged(mixnetContractAddress, limit, startAfter);
return this.nymdQuerier.getGatewaysPaged(mixnetContractAddress, limit, startAfter);
}
ownsMixNode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse> {
return this.nyxdQuerier.ownsMixNode(mixnetContractAddress, address);
return this.nymdQuerier.ownsMixNode(mixnetContractAddress, address);
}
ownsGateway(mixnetContractAddress: string, address: string): Promise<GatewayOwnershipResponse> {
return this.nyxdQuerier.ownsGateway(mixnetContractAddress, address);
return this.nymdQuerier.ownsGateway(mixnetContractAddress, address);
}
getStateParams(mixnetContractAddress: string): Promise<ContractStateParams> {
return this.nyxdQuerier.getStateParams(mixnetContractAddress);
return this.nymdQuerier.getStateParams(mixnetContractAddress);
}
getAllNetworkDelegationsPaged(
@@ -140,7 +140,7 @@ export default class QueryClient extends CosmWasmClient implements IQueryClient
limit?: number,
startAfter?: [string, string],
): Promise<PagedAllDelegationsResponse> {
return this.nyxdQuerier.getAllNetworkDelegationsPaged(mixnetContractAddress, limit, startAfter);
return this.nymdQuerier.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.nyxdQuerier.getMixNodeDelegationsPaged(mixnetContractAddress, mixIdentity, limit, startAfter);
return this.nymdQuerier.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.nyxdQuerier.getDelegatorDelegationsPaged(mixnetContractAddress, delegator, limit, startAfter);
return this.nymdQuerier.getDelegatorDelegationsPaged(mixnetContractAddress, delegator, limit, startAfter);
}
getDelegationDetails(mixnetContractAddress: string, mixIdentity: string, delegator: string): Promise<Delegation> {
return this.nyxdQuerier.getDelegationDetails(mixnetContractAddress, mixIdentity, delegator);
return this.nymdQuerier.getDelegationDetails(mixnetContractAddress, mixIdentity, delegator);
}
getLayerDistribution(mixnetContractAddress: string): Promise<LayerDistribution> {
return this.nyxdQuerier.getLayerDistribution(mixnetContractAddress);
return this.nymdQuerier.getLayerDistribution(mixnetContractAddress);
}
getRewardPool(mixnetContractAddress: string): Promise<string> {
return this.nyxdQuerier.getRewardPool(mixnetContractAddress);
return this.nymdQuerier.getRewardPool(mixnetContractAddress);
}
getCirculatingSupply(mixnetContractAddress: string): Promise<string> {
return this.nyxdQuerier.getCirculatingSupply(mixnetContractAddress);
return this.nymdQuerier.getCirculatingSupply(mixnetContractAddress);
}
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number> {
return this.nyxdQuerier.getIntervalRewardPercent(mixnetContractAddress);
return this.nymdQuerier.getIntervalRewardPercent(mixnetContractAddress);
}
getSybilResistancePercent(mixnetContractAddress: string): Promise<number> {
return this.nyxdQuerier.getSybilResistancePercent(mixnetContractAddress);
return this.nymdQuerier.getSybilResistancePercent(mixnetContractAddress);
}
getRewardingStatus(
@@ -190,22 +190,22 @@ export default class QueryClient extends CosmWasmClient implements IQueryClient
mixIdentity: string,
rewardingIntervalNonce: number,
): Promise<RewardingStatus> {
return this.nyxdQuerier.getRewardingStatus(mixnetContractAddress, mixIdentity, rewardingIntervalNonce);
return this.nymdQuerier.getRewardingStatus(mixnetContractAddress, mixIdentity, rewardingIntervalNonce);
}
getCachedGateways(): Promise<GatewayBond[]> {
return this.nymApiQuerier.getCachedGateways();
return this.validatorApiQuerier.getCachedGateways();
}
getCachedMixnodes(): Promise<MixNodeBond[]> {
return this.nymApiQuerier.getCachedMixnodes();
return this.validatorApiQuerier.getCachedMixnodes();
}
getActiveMixnodes(): Promise<MixNodeBond[]> {
return this.nymApiQuerier.getActiveMixnodes();
return this.validatorApiQuerier.getActiveMixnodes();
}
getRewardedMixnodes(): Promise<MixNodeBond[]> {
return this.nymApiQuerier.getRewardedMixnodes();
return this.validatorApiQuerier.getRewardedMixnodes();
}
}
+31 -31
View File
@@ -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 NyxdQuerier from './nyxd-querier';
import NymdQuerier from './nymd-querier';
import {
ContractStateParams,
Delegation,
@@ -33,7 +33,7 @@ import {
PagedMixnodeResponse,
RewardingStatus,
} from './types';
import NymApiQuerier from './nym-api-querier';
import ValidatorApiQuerier from './validator-api-querier';
// methods exposed by `SigningCosmWasmClient`
export interface ICosmWasmSigning {
@@ -197,29 +197,29 @@ export interface ISigningClient extends IQueryClient, ICosmWasmSigning, INymSign
}
export default class SigningClient extends SigningCosmWasmClient implements ISigningClient {
private nyxdQuerier: NyxdQuerier;
private nymdQuerier: NymdQuerier;
private nymApiQuerier: NymApiQuerier;
private validatorApiQuerier: ValidatorApiQuerier;
clientAddress: string;
private constructor(
clientAddress: string,
nymApiUrl: string,
validatorApiUrl: string,
tmClient: Tendermint34Client,
wallet: DirectSecp256k1HdWallet,
signerOptions: SigningCosmWasmClientOptions,
) {
super(tmClient, wallet, signerOptions);
this.clientAddress = clientAddress;
this.nyxdQuerier = new NyxdQuerier(this);
this.nymApiQuerier = new NymApiQuerier(nymApiUrl);
this.nymdQuerier = new NymdQuerier(this);
this.validatorApiQuerier = new ValidatorApiQuerier(validatorApiUrl);
}
public static async connectWithNymSigner(
wallet: DirectSecp256k1HdWallet,
nyxdUrl: string,
nymApiUrl: string,
nymdUrl: string,
validatorApiUrl: string,
prefix: string,
denom: string,
): Promise<SigningClient> {
@@ -228,34 +228,34 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
prefix,
gasPrice: nymGasPrice(denom),
};
const tmClient = await Tendermint34Client.connect(nyxdUrl);
return new SigningClient(address, nymApiUrl, tmClient, wallet, signerOptions);
const tmClient = await Tendermint34Client.connect(nymdUrl);
return new SigningClient(address, validatorApiUrl, tmClient, wallet, signerOptions);
}
// query related:
getContractVersion(mixnetContractAddress: string): Promise<MixnetContractVersion> {
return this.nyxdQuerier.getContractVersion(mixnetContractAddress);
return this.nymdQuerier.getContractVersion(mixnetContractAddress);
}
getMixNodesPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedMixnodeResponse> {
return this.nyxdQuerier.getMixNodesPaged(mixnetContractAddress, limit, startAfter);
return this.nymdQuerier.getMixNodesPaged(mixnetContractAddress, limit, startAfter);
}
getGatewaysPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedGatewayResponse> {
return this.nyxdQuerier.getGatewaysPaged(mixnetContractAddress, limit, startAfter);
return this.nymdQuerier.getGatewaysPaged(mixnetContractAddress, limit, startAfter);
}
ownsMixNode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse> {
return this.nyxdQuerier.ownsMixNode(mixnetContractAddress, address);
return this.nymdQuerier.ownsMixNode(mixnetContractAddress, address);
}
ownsGateway(mixnetContractAddress: string, address: string): Promise<GatewayOwnershipResponse> {
return this.nyxdQuerier.ownsGateway(mixnetContractAddress, address);
return this.nymdQuerier.ownsGateway(mixnetContractAddress, address);
}
getStateParams(mixnetContractAddress: string): Promise<ContractStateParams> {
return this.nyxdQuerier.getStateParams(mixnetContractAddress);
return this.nymdQuerier.getStateParams(mixnetContractAddress);
}
getAllNetworkDelegationsPaged(
@@ -263,7 +263,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
limit?: number,
startAfter?: [string, string],
): Promise<PagedAllDelegationsResponse> {
return this.nyxdQuerier.getAllNetworkDelegationsPaged(mixnetContractAddress, limit, startAfter);
return this.nymdQuerier.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.nyxdQuerier.getMixNodeDelegationsPaged(mixnetContractAddress, mixIdentity, limit, startAfter);
return this.nymdQuerier.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.nyxdQuerier.getDelegatorDelegationsPaged(mixnetContractAddress, delegator, limit, startAfter);
return this.nymdQuerier.getDelegatorDelegationsPaged(mixnetContractAddress, delegator, limit, startAfter);
}
getDelegationDetails(mixnetContractAddress: string, mixIdentity: string, delegator: string): Promise<Delegation> {
return this.nyxdQuerier.getDelegationDetails(mixnetContractAddress, mixIdentity, delegator);
return this.nymdQuerier.getDelegationDetails(mixnetContractAddress, mixIdentity, delegator);
}
getLayerDistribution(mixnetContractAddress: string): Promise<LayerDistribution> {
return this.nyxdQuerier.getLayerDistribution(mixnetContractAddress);
return this.nymdQuerier.getLayerDistribution(mixnetContractAddress);
}
getRewardPool(mixnetContractAddress: string): Promise<string> {
return this.nyxdQuerier.getRewardPool(mixnetContractAddress);
return this.nymdQuerier.getRewardPool(mixnetContractAddress);
}
getCirculatingSupply(mixnetContractAddress: string): Promise<string> {
return this.nyxdQuerier.getCirculatingSupply(mixnetContractAddress);
return this.nymdQuerier.getCirculatingSupply(mixnetContractAddress);
}
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number> {
return this.nyxdQuerier.getIntervalRewardPercent(mixnetContractAddress);
return this.nymdQuerier.getIntervalRewardPercent(mixnetContractAddress);
}
getSybilResistancePercent(mixnetContractAddress: string): Promise<number> {
return this.nyxdQuerier.getSybilResistancePercent(mixnetContractAddress);
return this.nymdQuerier.getSybilResistancePercent(mixnetContractAddress);
}
getRewardingStatus(
@@ -313,23 +313,23 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
mixIdentity: string,
rewardingIntervalNonce: number,
): Promise<RewardingStatus> {
return this.nyxdQuerier.getRewardingStatus(mixnetContractAddress, mixIdentity, rewardingIntervalNonce);
return this.nymdQuerier.getRewardingStatus(mixnetContractAddress, mixIdentity, rewardingIntervalNonce);
}
getCachedGateways(): Promise<GatewayBond[]> {
return this.nymApiQuerier.getCachedGateways();
return this.validatorApiQuerier.getCachedGateways();
}
getCachedMixnodes(): Promise<MixNodeBond[]> {
return this.nymApiQuerier.getCachedMixnodes();
return this.validatorApiQuerier.getCachedMixnodes();
}
getActiveMixnodes(): Promise<MixNodeBond[]> {
return this.nymApiQuerier.getActiveMixnodes();
return this.validatorApiQuerier.getActiveMixnodes();
}
getRewardedMixnodes(): Promise<MixNodeBond[]> {
return this.nymApiQuerier.getRewardedMixnodes();
return this.validatorApiQuerier.getRewardedMixnodes();
}
// signing related:
@@ -6,13 +6,13 @@
import axios from 'axios';
import { GatewayBond, MixNodeBond } from './types';
export const NYM_API_VERSION = '/v1';
export const NYM_API_GATEWAYS_PATH = `${NYM_API_VERSION}/gateways`;
export const NYM_API_MIXNODES_PATH = `${NYM_API_VERSION}/mixnodes`;
export const NYM_API_ACTIVE_MIXNODES_PATH = `${NYM_API_VERSION}/mixnodes/active`;
export const NYM_API_REWARDED_MIXNODES_PATH = `${NYM_API_VERSION}/mixnodes/rewarded`;
export const VALIDATOR_API_VERSION = '/v1';
export const VALIDATOR_API_GATEWAYS_PATH = `${VALIDATOR_API_VERSION}/gateways`;
export const VALIDATOR_API_MIXNODES_PATH = `${VALIDATOR_API_VERSION}/mixnodes`;
export const VALIDATOR_API_ACTIVE_MIXNODES_PATH = `${VALIDATOR_API_VERSION}/mixnodes/active`;
export const VALIDATOR_API_REWARDED_MIXNODES_PATH = `${VALIDATOR_API_VERSION}/mixnodes/rewarded`;
export interface INymApiQuery {
export interface IValidatorApiQuery {
getCachedMixnodes(): Promise<MixNodeBond[]>;
getCachedGateways(): Promise<GatewayBond[]>;
@@ -22,16 +22,16 @@ export interface INymApiQuery {
getRewardedMixnodes(): Promise<MixNodeBond[]>;
}
export default class NymApiQuerier implements INymApiQuery {
nymApiUrl: string;
export default class ValidatorApiQuerier implements IValidatorApiQuery {
validatorApiUrl: string;
constructor(nymApiUrl: string) {
this.nymApiUrl = nymApiUrl;
constructor(validatorApiUrl: string) {
this.validatorApiUrl = validatorApiUrl;
}
async getCachedMixnodes(): Promise<MixNodeBond[]> {
const url = new URL(this.nymApiUrl);
url.pathname += NYM_API_MIXNODES_PATH;
const url = new URL(this.validatorApiUrl);
url.pathname += VALIDATOR_API_MIXNODES_PATH;
const response = await axios.get(url.toString());
if (response.status === 200) {
@@ -41,8 +41,8 @@ export default class NymApiQuerier implements INymApiQuery {
}
async getCachedGateways(): Promise<GatewayBond[]> {
const url = new URL(this.nymApiUrl);
url.pathname += NYM_API_GATEWAYS_PATH;
const url = new URL(this.validatorApiUrl);
url.pathname += VALIDATOR_API_GATEWAYS_PATH;
const response = await axios.get(url.toString());
if (response.status === 200) {
@@ -52,8 +52,8 @@ export default class NymApiQuerier implements INymApiQuery {
}
async getActiveMixnodes(): Promise<MixNodeBond[]> {
const url = new URL(this.nymApiUrl);
url.pathname += NYM_API_ACTIVE_MIXNODES_PATH;
const url = new URL(this.validatorApiUrl);
url.pathname += VALIDATOR_API_ACTIVE_MIXNODES_PATH;
const response = await axios.get(url.toString());
if (response.status === 200) {
@@ -63,8 +63,8 @@ export default class NymApiQuerier implements INymApiQuery {
}
async getRewardedMixnodes(): Promise<MixNodeBond[]> {
const url = new URL(this.nymApiUrl);
url.pathname += NYM_API_REWARDED_MIXNODES_PATH;
const url = new URL(this.validatorApiUrl);
url.pathname += VALIDATOR_API_REWARDED_MIXNODES_PATH;
const response = await axios.get(url.toString());
if (response.status === 200) {
@@ -1,4 +1,4 @@
import ValidatorClient from '../../validator/index';
import ValidatorClient from '../../dist';
import expect from 'expect';
describe('Query: balances', () => {
@@ -0,0 +1,24 @@
#!/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 = ❤");
}
});
@@ -0,0 +1,2 @@
node_modules
dist
@@ -0,0 +1,5 @@
language: node_js
node_js: "10"
script:
- ./node_modules/.bin/webpack

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