Compare commits

..

6 Commits

Author SHA1 Message Date
tommy a94272ffd6 update branch to be in line with develop 2022-08-18 13:28:05 +02:00
Benedetta 02da10e222 clean up 2022-08-04 15:37:05 +02:00
Benedetta 2637924e9c Fixing some to-do bits 2022-07-27 08:51:16 +02:00
Benedetta bbaa06417d Adding more tests 2022-07-25 08:39:07 +02:00
Benedetta edcaf72714 Creating the branch and initial work 2022-07-19 14:22:31 +02:00
Tommy 80ad7c1798 implement base changes for updating wallet-tests to use typescript 2022-07-14 17:51:48 +02:00
343 changed files with 4597 additions and 17510 deletions
-6
View File
@@ -44,12 +44,6 @@ jobs:
command: build
args: --workspace
- name: Reclaim some disk space (because Windows is being annoying)
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: clean
- name: Run all tests
uses: actions-rs/cargo@v1
with:
@@ -166,8 +166,8 @@ jobs:
GIT_BRANCH: "${GITHUB_REF##*/}"
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
KEYBASE_NYM_CHANNEL: "ci-nightly"
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMTECH_TEAM }}"
KEYBASE_NYM_CHANNEL: "test"
IS_SUCCESS: "${{ env.WORKFLOW_CONCLUSION == 'success' }}"
uses: docker://keybaseio/client:stable-node
with:
@@ -1,26 +0,0 @@
name: Nightly builds on dispatch
on: workflow_dispatch
jobs:
notification:
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v2
- name: Keybase - Node Install
run: npm install
working-directory: .github/workflows/support-files
- name: Keybase - Send Notification
env:
NYM_NOTIFICATION_KIND: nightly
NYM_PROJECT_NAME: "Notification test"
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "${GITHUB_REF##*/}"
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
KEYBASE_NYM_CHANNEL: "ci-nightly"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
-50
View File
@@ -1,50 +0,0 @@
name: Publish Nym CLI binaries
on:
workflow_dispatch:
release:
types: [created]
env:
NETWORK: mainnet
jobs:
publish-nym-cli:
strategy:
fail-fast: false
matrix:
platform: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v3
- name: Check the release tag starts with `nym-cli-`
if: startsWith(github.ref, 'refs/tags/nym-cli-') == false && github.event_name != 'workflow_dispatch'
uses: actions/github-script@v3
with:
script: |
core.setFailed('Release tag did not start with nym-cli-...')
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Build binary
run: make build-nym-cli
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: nym-cli-${{ matrix.platform }}
path: |
target/release/nym-cli*
retention-days: 30
- name: Upload to release based on tag name
uses: softprops/action-gh-release@v1
if: github.event_name == 'release'
with:
files: |
target/release/nym-cli
+2 -21
View File
@@ -1,7 +1,5 @@
name: Publish Nym binaries
on:
workflow_dispatch:
release:
types: [created]
@@ -20,7 +18,7 @@ jobs:
- uses: actions/checkout@v3
- name: Check the release tag starts with `nym-binaries-`
if: startsWith(github.ref, 'refs/tags/nym-binaries-') == false && github.event_name != 'workflow_dispatch'
if: startsWith(github.ref, 'refs/tags/nym-binaries-') == false
uses: actions/github-script@v3
with:
script: |
@@ -37,24 +35,8 @@ jobs:
command: build
args: --workspace --release
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: my-artifact
path: |
target/release/nym-client
target/release/nym-gateway
target/release/nym-mixnode
target/release/nym-socks5-client
target/release/nym-validator-api
target/release/nym-network-requester
target/release/nym-network-statistics
target/release/nym-cli
retention-days: 30
- name: Upload to release based on tag name
uses: softprops/action-gh-release@v1
if: github.event_name == 'release'
with:
files: |
target/release/nym-client
@@ -63,5 +45,4 @@ jobs:
target/release/nym-socks5-client
target/release/nym-validator-api
target/release/nym-network-requester
target/release/nym-network-statistics
target/release/nym-cli
target/release/nym-network-statistics
+5 -5
View File
@@ -51,12 +51,12 @@ jobs:
- name: Install dependencies
run: yarn install
working-directory: nym-wallet/webdriver
working-directory: nym-wallet/wallet-ui-tests
- name: Remove existing user datafile
uses: JesseTG/rm@v1.0.2
with:
path: nym-wallet/webdriver/common/data/user-data.json
path: nym-wallet/wallet-ui-tests/common/user-data.json
- name: Create user data json file
id: create-json
@@ -64,7 +64,7 @@ jobs:
with:
name: "user-data.json"
json: ${{ secrets.WALLET_USERDATA }}
dir: "nym-wallet/webdriver/common/data/"
dir: "nym-wallet/wallet-ui-tests/common/"
- name: Install tauri-driver
uses: actions-rs/cargo@v1
@@ -73,5 +73,5 @@ jobs:
args: tauri-driver
- name: Launch tests
run: xvfb-run yarn test:runall
working-directory: nym-wallet/webdriver
run: xvfb-run yarn test
working-directory: nym-wallet/wallet-ui-tests
+1 -14
View File
@@ -4,20 +4,11 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## Unreleased
### Added
- nym-cli: added CLI tool for interacting with the Nyx blockchain and Nym mixnet smart contracts ([#1577])
- validator-client: added `query_contract_smart` and `query_contract_raw` on `NymdClient` ([#1558])
### Changed
- validator-client: made `fee` argument optional for `execute` and `execute_multiple` ([#1541])
- socks5 client: graceful shutdown should fix error on disconnect in nym-connect ([#1591])
[#1541]: https://github.com/nymtech/nym/pull/1541
[#1558]: https://github.com/nymtech/nym/pull/1558
[#1577]: https://github.com/nymtech/nym/pull/1577
[#1591]: https://github.com/nymtech/nym/pull/1591
## [nym-binaries-1.0.2](https://github.com/nymtech/nym/tree/nym-binaries-1.0.2)
@@ -36,7 +27,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
- validator-api: add Swagger to document the REST API ([#1249]).
- validator-api: Added new endpoints for coconut spending flow and communications with coconut & multisig contracts ([#1261])
- validator-api: add `uptime`, `estimated_operator_apy`, `estimated_delegators_apy` to `/mixnodes/detailed` endpoint ([#1393])
- validator-api: add node info cache storing simulated active set inclusion probabilities
- network-statistics: a new mixnet service that aggregates and exposes anonymized data about mixnet services ([#1328])
- mixnode: Added basic mixnode hardware reporting to the HTTP API ([#1308]).
- validator-api: endpoint, in coconut mode, for returning the validator-api cosmos address ([#1404]).
@@ -56,7 +46,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
- validator: fixed local docker-compose setup to work on Apple M1 ([#1329])
- explorer-api: listen out for SIGTERM and SIGQUIT too, making it play nicely as a system service ([#1482]).
- network-requester: fix filter for suffix-only domains ([#1487])
- validator-api: listen out for SIGTERM and SIGQUIT too, making it play nicely as a system service; cleaner shutdown, without panics ([#1496], [#1573]).
- validator-api: listen out for SIGTERM and SIGQUIT too, making it play nicely as a system service ([#1496]).
### Changed
@@ -73,7 +63,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
- gateway, network-statistics: include gateway id in the sent statistical data ([#1478])
- network explorer: tweak how active set probability is shown ([#1503])
- validator-api: rewarder set update fails without panicking on possible nymd queries ([#1520])
- network-requester, socks5 client (nym-connect): send and receive respectively a message error to be displayed about filter check failure ([#1576])
[#1249]: https://github.com/nymtech/nym/pull/1249
@@ -103,8 +92,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
[#1496]: https://github.com/nymtech/nym/pull/1496
[#1503]: https://github.com/nymtech/nym/pull/1503
[#1520]: https://github.com/nymtech/nym/pull/1520
[#1573]: https://github.com/nymtech/nym/pull/1573
[#1576]: https://github.com/nymtech/nym/pull/1576
## [v1.0.1](https://github.com/nymtech/nym/tree/v1.0.1) (2022-05-04)
Generated
+333 -715
View File
File diff suppressed because it is too large Load Diff
-2
View File
@@ -27,7 +27,6 @@ members = [
"common/client-libs/mixnet-client",
"common/client-libs/validator-client",
"common/coconut-interface",
"common/commands",
"common/config",
"common/cosmwasm-smart-contracts/coconut-bandwidth-contract",
"common/cosmwasm-smart-contracts/contracts-common",
@@ -70,7 +69,6 @@ members = [
"service-providers/network-statistics",
"validator-api",
"validator-api/validator-api-requests",
"tools/nym-cli",
"tools/ts-rs-cli"
]
-3
View File
@@ -69,9 +69,6 @@ build-wallet:
build-connect:
cargo build --manifest-path nym-connect/Cargo.toml --workspace
build-nym-cli:
cargo build --release --manifest-path tools/nym-cli/Cargo.toml
fmt-main:
cargo fmt --all
-3
View File
@@ -14,7 +14,6 @@ log = "0.4"
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
serde = { version = "1.0", features = ["derive"] }
sled = "0.34"
thiserror = "1.0.34"
tokio = { version = "1.19.1", features = ["macros"] }
url = { version ="2.2", features = ["serde"] }
@@ -26,10 +25,8 @@ gateway-requests = { path = "../../gateway/gateway-requests" }
nonexhaustive-delayqueue = { path = "../../common/nonexhaustive-delayqueue" }
nymsphinx = { path = "../../common/nymsphinx" }
pemstore = { path = "../../common/pemstore" }
task = { path = "../../common/task" }
topology = { path = "../../common/topology" }
validator-client = { path = "../../common/client-libs/validator-client" }
tap = "1.0.1"
[dev-dependencies]
tempfile = "3.1.0"
@@ -13,7 +13,6 @@ use nymsphinx::utils::sample_poisson_duration;
use rand::{rngs::OsRng, CryptoRng, Rng};
use std::pin::Pin;
use std::sync::Arc;
use task::ShutdownListener;
use tokio::task::JoinHandle;
use tokio::time;
@@ -49,9 +48,6 @@ where
/// Accessor to the common instance of network topology.
topology_access: TopologyAccessor,
/// Listen to shutdown signals.
shutdown: ShutdownListener,
}
impl<R> Stream for LoopCoverTrafficStream<R>
@@ -88,7 +84,6 @@ where
// obviously when we finally make shared rng that is on 'higher' level, this should become
// generic `R`
impl LoopCoverTrafficStream<OsRng> {
#[allow(clippy::too_many_arguments)]
pub fn new(
ack_key: Arc<AckKey>,
average_ack_delay: time::Duration,
@@ -97,7 +92,6 @@ impl LoopCoverTrafficStream<OsRng> {
mix_tx: BatchMixMessageSender,
our_full_destination: Recipient,
topology_access: TopologyAccessor,
shutdown: ShutdownListener,
) -> Self {
let rng = OsRng;
@@ -111,7 +105,6 @@ impl LoopCoverTrafficStream<OsRng> {
our_full_destination,
rng,
topology_access,
shutdown,
}
}
@@ -166,25 +159,9 @@ impl LoopCoverTrafficStream<OsRng> {
self.average_cover_message_sending_delay,
)));
let mut shutdown = self.shutdown.clone();
while !shutdown.is_shutdown() {
tokio::select! {
biased;
_ = shutdown.recv() => {
log::trace!("LoopCoverTrafficStream: Received shutdown");
}
next = self.next() => {
if next.is_some() {
self.on_new_message().await;
} else {
log::trace!("LoopCoverTrafficStream: Stopping since channel closed");
break;
}
}
}
while self.next().await.is_some() {
self.on_new_message().await;
}
assert!(self.shutdown.is_shutdown_poll());
log::debug!("LoopCoverTrafficStream: Exiting");
}
pub fn start(mut self) -> JoinHandle<()> {
+2 -21
View File
@@ -6,7 +6,6 @@ use futures::StreamExt;
use gateway_client::GatewayClient;
use log::*;
use nymsphinx::forwarding::packet::MixPacket;
use task::ShutdownListener;
use tokio::task::JoinHandle;
pub type BatchMixMessageSender = mpsc::UnboundedSender<Vec<MixPacket>>;
@@ -19,7 +18,6 @@ pub struct MixTrafficController {
// later on gateway_client will need to be accessible by other entities
gateway_client: GatewayClient,
mix_rx: BatchMixMessageReceiver,
shutdown: ShutdownListener,
// TODO: this is temporary work-around.
// in long run `gateway_client` will be moved away from `MixTrafficController` anyway.
@@ -30,12 +28,10 @@ impl MixTrafficController {
pub fn new(
mix_rx: BatchMixMessageReceiver,
gateway_client: GatewayClient,
shutdown: ShutdownListener,
) -> MixTrafficController {
MixTrafficController {
gateway_client,
mix_rx,
shutdown,
consecutive_gateway_failure_count: 0,
}
}
@@ -70,24 +66,9 @@ impl MixTrafficController {
}
pub async fn run(&mut self) {
while !self.shutdown.is_shutdown() {
tokio::select! {
mix_packets = self.mix_rx.next() => match mix_packets {
Some(mix_packets) => {
self.on_messages(mix_packets).await;
},
None => {
log::trace!("MixTrafficController: Stopping since channel closed");
break;
}
},
_ = self.shutdown.recv() => {
log::trace!("MixTrafficController: Received shutdown");
}
}
while let Some(mix_packets) = self.mix_rx.next().await {
self.on_messages(mix_packets).await;
}
assert!(self.shutdown.is_shutdown_poll());
log::debug!("MixTrafficController: Exiting");
}
pub fn start(mut self) -> JoinHandle<()> {
-9
View File
@@ -1,5 +1,3 @@
use std::sync::atomic::AtomicBool;
pub mod cover_traffic_stream;
pub mod inbound_messages;
pub mod key_manager;
@@ -8,10 +6,3 @@ pub mod real_messages_control;
pub mod received_buffer;
pub mod reply_key_storage;
pub mod topology_control;
// This is *NOT* used to signal shutdown.
// It's critical that we don't have any tasks finishing early, this is an additional safety check
// that tasks exiting are doing so because shutdown has been signalled, and no other reason.
// In particular for tasks that rely on their associated channel being closed to signal shutdown,
// and don't have access to a shutdown listener channel.
pub static SHUTDOWN_HAS_BEEN_SIGNALLED: AtomicBool = AtomicBool::new(false);
@@ -10,7 +10,6 @@ use nymsphinx::{
chunking::fragment::{FragmentIdentifier, COVER_FRAG_ID},
};
use std::sync::Arc;
use task::ShutdownListener;
/// Module responsible for listening for any data resembling acknowledgements from the network
/// and firing actions to remove them from the 'Pending' state.
@@ -18,7 +17,6 @@ pub(super) struct AcknowledgementListener {
ack_key: Arc<AckKey>,
ack_receiver: AcknowledgementReceiver,
action_sender: ActionSender,
shutdown: ShutdownListener,
}
impl AcknowledgementListener {
@@ -26,13 +24,11 @@ impl AcknowledgementListener {
ack_key: Arc<AckKey>,
ack_receiver: AcknowledgementReceiver,
action_sender: ActionSender,
shutdown: ShutdownListener,
) -> Self {
AcknowledgementListener {
ack_key,
ack_receiver,
action_sender,
shutdown,
}
}
@@ -69,26 +65,12 @@ impl AcknowledgementListener {
pub(super) async fn run(&mut self) {
debug!("Started AcknowledgementListener");
while !self.shutdown.is_shutdown() {
tokio::select! {
acks = self.ack_receiver.next() => match acks {
Some(acks) => {
// realistically we would only be getting one ack at the time
for ack in acks {
self.on_ack(ack).await;
}
},
None => {
log::trace!("AcknowledgementListener: Stopping since channel closed");
break;
}
},
_ = self.shutdown.recv() => {
log::trace!("AcknowledgementListener: Received shutdown");
}
while let Some(acks) = self.ack_receiver.next().await {
// realistically we would only be getting one ack at the time
for ack in acks {
self.on_ack(ack).await;
}
}
assert!(self.shutdown.is_shutdown_poll());
log::debug!("AcknowledgementListener: Exiting");
error!("TODO: error msg. Or maybe panic?")
}
}
@@ -12,7 +12,6 @@ use nymsphinx::Delay as SphinxDelay;
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;
use task::ShutdownListener;
pub(crate) type ActionSender = UnboundedSender<Action>;
@@ -100,16 +99,12 @@ pub(super) struct ActionController {
/// Channel for notifying `RetransmissionRequestListener` about expired acknowledgements.
retransmission_sender: RetransmissionRequestSender,
/// Listen for shutdown notifications
shutdown: ShutdownListener,
}
impl ActionController {
pub(super) fn new(
config: Config,
retransmission_sender: RetransmissionRequestSender,
shutdown: ShutdownListener,
) -> (Self, ActionSender) {
let (sender, receiver) = mpsc::unbounded();
(
@@ -119,7 +114,6 @@ impl ActionController {
pending_acks_timers: NonExhaustiveDelayQueue::new(),
incoming_actions: receiver,
retransmission_sender,
shutdown,
},
sender,
)
@@ -252,30 +246,14 @@ impl ActionController {
}
pub(super) async fn run(&mut self) {
while !self.shutdown.is_shutdown() {
loop {
// at some point there will be a global shutdown signal here as the third option
tokio::select! {
action = self.incoming_actions.next() => match action {
Some(action) => self.process_action(action),
None => {
log::trace!(
"ActionController: Stopping since incoming actions channel closed"
);
break;
}
},
expired_ack = self.pending_acks_timers.next() => match expired_ack {
Some(expired_ack) => self.handle_expired_ack_timer(expired_ack),
None => {
log::trace!("ActionController: Stopping since ack channel closed");
break;
}
},
_ = self.shutdown.recv() => {
log::trace!("ActionController: Received shutdown");
}
// we NEVER expect for ANY sender to get dropped so unwrap here is fine
action = self.incoming_actions.next() => self.process_action(action.unwrap()),
// pending ack queue Stream CANNOT return a `None` so unwrap here is fine
expired_ack = self.pending_acks_timers.next() => self.handle_expired_ack_timer(expired_ack.unwrap())
}
}
assert!(self.shutdown.is_shutdown_poll());
log::debug!("ActionController: Exiting");
}
}
@@ -16,7 +16,6 @@ use nymsphinx::preparer::MessagePreparer;
use nymsphinx::{acknowledgements::AckKey, addressing::clients::Recipient};
use rand::{CryptoRng, Rng};
use std::sync::Arc;
use task::ShutdownListener;
/// Module responsible for dealing with the received messages: splitting them, creating acknowledgements,
/// putting everything into sphinx packets, etc.
@@ -33,7 +32,6 @@ where
real_message_sender: BatchRealMessageSender,
topology_access: TopologyAccessor,
reply_key_storage: ReplyKeyStorage,
shutdown: ShutdownListener,
}
impl<R> InputMessageListener<R>
@@ -52,7 +50,6 @@ where
real_message_sender: BatchRealMessageSender,
topology_access: TopologyAccessor,
reply_key_storage: ReplyKeyStorage,
shutdown: ShutdownListener,
) -> Self {
InputMessageListener {
ack_key,
@@ -63,7 +60,6 @@ where
real_message_sender,
topology_access,
reply_key_storage,
shutdown,
}
}
@@ -137,6 +133,7 @@ where
let prepared_fragment = self
.message_preparer
.prepare_chunk_for_sending(chunk_clone, topology, &self.ack_key, &recipient)
.await
.unwrap();
real_messages.push(RealMessage::new(
@@ -186,23 +183,9 @@ where
pub(super) async fn run(&mut self) {
debug!("Started InputMessageListener");
while !self.shutdown.is_shutdown() {
tokio::select! {
input_msg = self.input_receiver.next() => match input_msg {
Some(input_msg) => {
self.on_input_message(input_msg).await;
},
None => {
log::trace!("InputMessageListener: Stopping since channel closed");
break;
}
},
_ = self.shutdown.recv() => {
log::trace!("InputMessageListener: Received shutdown");
}
}
while let Some(input_msg) = self.input_receiver.next().await {
self.on_input_message(input_msg).await;
}
assert!(self.shutdown.is_shutdown_poll());
log::debug!("InputMessageListener: Exiting");
error!("TODO: error msg. Or maybe panic?")
}
}
@@ -25,7 +25,6 @@ use std::{
sync::{Arc, Weak},
time::Duration,
};
use task::ShutdownListener;
use tokio::task::JoinHandle;
mod acknowledgement_listener;
@@ -153,7 +152,6 @@ impl<R> AcknowledgementController<R>
where
R: 'static + CryptoRng + Rng + Clone + Send,
{
#[allow(clippy::too_many_arguments)]
pub(super) fn new(
config: Config,
rng: R,
@@ -162,14 +160,13 @@ where
ack_recipient: Recipient,
reply_key_storage: ReplyKeyStorage,
connectors: AcknowledgementControllerConnectors,
shutdown: ShutdownListener,
) -> Self {
let (retransmission_tx, retransmission_rx) = mpsc::unbounded();
let action_config =
action_controller::Config::new(config.ack_wait_addition, config.ack_wait_multiplier);
let (action_controller, action_sender) =
ActionController::new(action_config, retransmission_tx, shutdown.clone());
ActionController::new(action_config, retransmission_tx);
let message_preparer = MessagePreparer::new(
rng,
@@ -183,7 +180,6 @@ where
Arc::clone(&ack_key),
connectors.ack_receiver,
action_sender.clone(),
shutdown.clone(),
);
// will listen for any new messages from the client
@@ -196,7 +192,6 @@ where
connectors.real_message_sender.clone(),
topology_access.clone(),
reply_key_storage,
shutdown.clone(),
);
// will listen for any ack timeouts and trigger retransmission
@@ -208,13 +203,12 @@ where
connectors.real_message_sender,
retransmission_rx,
topology_access,
shutdown.clone(),
);
// will listen for events indicating the packet was sent through the network so that
// the retransmission timer should be started.
let sent_notification_listener =
SentNotificationListener::new(connectors.sent_notifier, action_sender, shutdown);
SentNotificationListener::new(connectors.sent_notifier, action_sender);
AcknowledgementController {
acknowledgement_listener: Some(acknowledgement_listener),
@@ -238,27 +232,27 @@ where
// graceful shutdowns.
let ack_listener_fut = tokio::spawn(async move {
acknowledgement_listener.run().await;
debug!("The acknowledgement listener has finished execution!");
error!("The acknowledgement listener has finished execution!");
acknowledgement_listener
});
let input_listener_fut = tokio::spawn(async move {
input_message_listener.run().await;
debug!("The input listener has finished execution!");
error!("The input listener has finished execution!");
input_message_listener
});
let retransmission_req_fut = tokio::spawn(async move {
retransmission_request_listener.run().await;
debug!("The retransmission request listener has finished execution!");
error!("The retransmission request listener has finished execution!");
retransmission_request_listener
});
let sent_notification_fut = tokio::spawn(async move {
sent_notification_listener.run().await;
debug!("The sent notification listener has finished execution!");
error!("The sent notification listener has finished execution!");
sent_notification_listener
});
let action_controller_fut = tokio::spawn(async move {
action_controller.run().await;
debug!("The controller has finished execution!");
error!("The controller has finished execution!");
action_controller
});
@@ -14,7 +14,6 @@ use nymsphinx::preparer::MessagePreparer;
use nymsphinx::{acknowledgements::AckKey, addressing::clients::Recipient};
use rand::{CryptoRng, Rng};
use std::sync::{Arc, Weak};
use task::ShutdownListener;
// responsible for packet retransmission upon fired timer
pub(super) struct RetransmissionRequestListener<R>
@@ -28,14 +27,12 @@ where
real_message_sender: BatchRealMessageSender,
request_receiver: RetransmissionRequestReceiver,
topology_access: TopologyAccessor,
shutdown: ShutdownListener,
}
impl<R> RetransmissionRequestListener<R>
where
R: CryptoRng + Rng,
{
#[allow(clippy::too_many_arguments)]
pub(super) fn new(
ack_key: Arc<AckKey>,
ack_recipient: Recipient,
@@ -44,7 +41,6 @@ where
real_message_sender: BatchRealMessageSender,
request_receiver: RetransmissionRequestReceiver,
topology_access: TopologyAccessor,
shutdown: ShutdownListener,
) -> Self {
RetransmissionRequestListener {
ack_key,
@@ -54,7 +50,6 @@ where
real_message_sender,
request_receiver,
topology_access,
shutdown,
}
}
@@ -88,6 +83,7 @@ where
let prepared_fragment = self
.message_preparer
.prepare_chunk_for_sending(chunk_clone, topology_ref, &self.ack_key, packet_recipient)
.await
.unwrap();
// if we have the ONLY strong reference to the ack data, it means it was removed from the
@@ -126,22 +122,9 @@ where
pub(super) async fn run(&mut self) {
debug!("Started RetransmissionRequestListener");
while !self.shutdown.is_shutdown() {
tokio::select! {
timed_out_ack = self.request_receiver.next() => match timed_out_ack {
Some(timed_out_ack) => self.on_retransmission_request(timed_out_ack).await,
None => {
log::trace!("RetransmissionRequestListener: Stopping since channel closed");
break;
}
},
_ = self.shutdown.recv() => {
log::trace!("RetransmissionRequestListener: Received shutdown");
}
}
while let Some(timed_out_ack) = self.request_receiver.next().await {
self.on_retransmission_request(timed_out_ack).await;
}
assert!(self.shutdown.is_shutdown_poll());
log::debug!("RetransmissionRequestListener: Exiting");
error!("TODO: error msg. Or maybe panic?")
}
}
@@ -6,7 +6,6 @@ use super::SentPacketNotificationReceiver;
use futures::StreamExt;
use log::*;
use nymsphinx::chunking::fragment::{FragmentIdentifier, COVER_FRAG_ID};
use task::ShutdownListener;
/// Module responsible for starting up retransmission timers.
/// It is required because when we send our packet to the `real traffic stream` controlled
@@ -15,19 +14,16 @@ use task::ShutdownListener;
pub(super) struct SentNotificationListener {
sent_notifier: SentPacketNotificationReceiver,
action_sender: ActionSender,
shutdown: ShutdownListener,
}
impl SentNotificationListener {
pub(super) fn new(
sent_notifier: SentPacketNotificationReceiver,
action_sender: ActionSender,
shutdown: ShutdownListener,
) -> Self {
SentNotificationListener {
sent_notifier,
action_sender,
shutdown,
}
}
@@ -48,23 +44,9 @@ impl SentNotificationListener {
pub(super) async fn run(&mut self) {
debug!("Started SentNotificationListener");
while !self.shutdown.is_shutdown() {
tokio::select! {
frag_id = self.sent_notifier.next() => match frag_id {
Some(frag_id) => {
self.on_sent_message(frag_id).await;
}
None => {
log::trace!("SentNotificationListener: Stopping since channel closed");
break;
}
},
_ = self.shutdown.recv() => {
log::trace!("SentNotificationListener: Received shutdown");
}
}
while let Some(frag_id) = self.sent_notifier.next().await {
self.on_sent_message(frag_id).await;
}
assert!(self.shutdown.is_shutdown_poll());
log::debug!("SentNotificationListener: Exiting");
error!("TODO: error msg. Or maybe panic?")
}
}
@@ -22,7 +22,6 @@ use nymsphinx::addressing::clients::Recipient;
use rand::{rngs::OsRng, CryptoRng, Rng};
use std::sync::Arc;
use std::time::Duration;
use task::ShutdownListener;
use tokio::task::JoinHandle;
mod acknowledgement_control;
@@ -92,7 +91,6 @@ impl RealMessagesController<OsRng> {
mix_sender: BatchMixMessageSender,
topology_access: TopologyAccessor,
reply_key_storage: ReplyKeyStorage,
shutdown: ShutdownListener,
) -> Self {
let rng = OsRng;
@@ -121,7 +119,6 @@ impl RealMessagesController<OsRng> {
config.self_recipient,
reply_key_storage,
ack_controller_connectors,
shutdown.clone(),
);
let out_queue_config = real_traffic_stream::Config::new(
@@ -139,7 +136,6 @@ impl RealMessagesController<OsRng> {
rng,
config.self_recipient,
topology_access,
shutdown,
);
RealMessagesController {
@@ -157,12 +153,12 @@ impl RealMessagesController<OsRng> {
// graceful shutdowns.
let out_queue_control_fut = tokio::spawn(async move {
out_queue_control.run_out_queue_control().await;
debug!("The out queue controller has finished execution!");
error!("The out queue controller has finished execution!");
out_queue_control
});
let ack_control_fut = tokio::spawn(async move {
ack_control.run().await;
debug!("The acknowledgement controller has finished execution!");
error!("The acknowledgement controller has finished execution!");
ack_control
});
@@ -19,7 +19,6 @@ use std::collections::VecDeque;
use std::pin::Pin;
use std::sync::Arc;
use std::time::Duration;
use task::ShutdownListener;
use tokio::time;
/// Configurable parameters of the `OutQueueControl`
@@ -84,9 +83,6 @@ where
/// Buffer containing all real messages received. It is first exhausted before more are pulled.
received_buffer: VecDeque<RealMessage>,
/// Listens for shutdown signals
shutdown: ShutdownListener,
}
pub(crate) struct RealMessage {
@@ -178,7 +174,6 @@ where
rng: R,
our_full_destination: Recipient,
topology_access: TopologyAccessor,
shutdown: ShutdownListener,
) -> Self {
OutQueueControl {
config,
@@ -191,7 +186,6 @@ where
rng,
topology_access,
received_buffer: VecDeque::with_capacity(0), // we won't be putting any data into this guy directly
shutdown,
}
}
@@ -245,15 +239,7 @@ where
// - we run out of memory
// - the receiver channel is closed
// in either case there's no recovery and we can only panic
if let Err(err) = self.mix_tx.unbounded_send(vec![next_message]) {
if self.shutdown.is_shutdown_poll() {
log::info!("Failed to send (shutdown detected)");
} else {
// We don't try to limp along, panic to avoid continuing in a potentially
// inconsistent state
panic!("{err}");
}
}
self.mix_tx.unbounded_send(vec![next_message]).unwrap();
// JS: Not entirely sure why or how it fixes stuff, but without the yield call,
// the UnboundedReceiver [of mix_rx] will not get a chance to read anything
@@ -271,26 +257,9 @@ where
self.config.average_message_sending_delay,
)));
let mut shutdown = self.shutdown.clone();
while !shutdown.is_shutdown() {
tokio::select! {
biased;
_ = shutdown.recv() => {
log::trace!("OutQueueControl: Received shutdown");
}
next_message = self.next() => match next_message {
Some(next_message) => {
self.on_message(next_message).await;
},
None => {
log::trace!("OutQueueControl: Stopping since channel closed");
break;
}
}
}
while let Some(next_message) = self.next().await {
self.on_message(next_message).await;
}
assert!(shutdown.is_shutdown_poll());
log::debug!("OutQueueControl: Exiting");
}
pub(crate) async fn run_out_queue_control(&mut self) {
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
use crate::client::reply_key_storage::ReplyKeyStorage;
use crate::client::SHUTDOWN_HAS_BEEN_SIGNALLED;
use crypto::asymmetric::encryption;
use crypto::symmetric::stream_cipher;
use crypto::Digest;
@@ -15,9 +14,7 @@ use nymsphinx::anonymous_replies::{encryption_key::EncryptionKeyDigest, SurbEncr
use nymsphinx::params::{ReplySurbEncryptionAlgorithm, ReplySurbKeyDigestAlgorithm};
use nymsphinx::receiver::{MessageReceiver, MessageRecoveryError, ReconstructedMessage};
use std::collections::HashSet;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use task::ShutdownListener;
use tokio::task::JoinHandle;
// Buffer Requests to say "hey, send any reconstructed messages to this channel"
@@ -295,27 +292,16 @@ impl RequestReceiver {
fn start(mut self) -> JoinHandle<()> {
tokio::spawn(async move {
loop {
tokio::select! {
request = self.query_receiver.next() => {
match request {
Some(ReceivedBufferMessage::ReceiverAnnounce(sender)) => {
self.received_buffer.connect_sender(sender).await;
}
Some(ReceivedBufferMessage::ReceiverDisconnect) => {
self.received_buffer.disconnect_sender().await
}
None => {
log::trace!("RequestReceiver: Stopping since channel closed");
break;
},
}
},
};
while let Some(request) = self.query_receiver.next().await {
match request {
ReceivedBufferMessage::ReceiverAnnounce(sender) => {
self.received_buffer.connect_sender(sender).await;
}
ReceivedBufferMessage::ReceiverDisconnect => {
self.received_buffer.disconnect_sender().await
}
}
}
assert!(SHUTDOWN_HAS_BEEN_SIGNALLED.load(Ordering::Relaxed));
log::debug!("RequestReceiver: Exiting");
})
}
}
@@ -323,41 +309,23 @@ impl RequestReceiver {
struct FragmentedMessageReceiver {
received_buffer: ReceivedMessagesBuffer,
mixnet_packet_receiver: MixnetMessageReceiver,
shutdown: ShutdownListener,
}
impl FragmentedMessageReceiver {
fn new(
received_buffer: ReceivedMessagesBuffer,
mixnet_packet_receiver: MixnetMessageReceiver,
shutdown: ShutdownListener,
) -> Self {
FragmentedMessageReceiver {
received_buffer,
mixnet_packet_receiver,
shutdown,
}
}
fn start(mut self) -> JoinHandle<()> {
tokio::spawn(async move {
while !self.shutdown.is_shutdown() {
tokio::select! {
new_messages = self.mixnet_packet_receiver.next() => match new_messages {
Some(new_messages) => {
self.received_buffer.handle_new_received(new_messages).await;
}
None => {
log::trace!("FragmentedMessageReceiver: Stopping since channel closed");
break;
}
},
_ = self.shutdown.recv() => {
log::trace!("FragmentedMessageReceiver: Received shutdown");
}
}
while let Some(new_messages) = self.mixnet_packet_receiver.next().await {
self.received_buffer.handle_new_received(new_messages).await;
}
assert!(self.shutdown.is_shutdown_poll());
log::debug!("FragmentedMessageReceiver: Exiting");
})
}
}
@@ -373,7 +341,6 @@ impl ReceivedMessagesBufferController {
query_receiver: ReceivedBufferRequestReceiver,
mixnet_packet_receiver: MixnetMessageReceiver,
reply_key_storage: ReplyKeyStorage,
shutdown: ShutdownListener,
) -> Self {
let received_buffer =
ReceivedMessagesBuffer::new(local_encryption_keypair, reply_key_storage);
@@ -382,7 +349,6 @@ impl ReceivedMessagesBufferController {
fragmented_message_receiver: FragmentedMessageReceiver::new(
received_buffer.clone(),
mixnet_packet_receiver,
shutdown,
),
request_receiver: RequestReceiver::new(received_buffer, query_receiver),
}
@@ -10,7 +10,6 @@ use std::ops::Deref;
use std::sync::Arc;
use std::time;
use std::time::Duration;
use task::ShutdownListener;
use tokio::sync::{RwLock, RwLockReadGuard};
use tokio::task::JoinHandle;
use topology::{nym_topology_from_bonds, NymTopology};
@@ -304,20 +303,12 @@ impl TopologyRefresher {
self.topology_accessor.is_routable().await
}
pub fn start(mut self, mut shutdown: ShutdownListener) -> JoinHandle<()> {
pub fn start(mut self) -> JoinHandle<()> {
tokio::spawn(async move {
while !shutdown.is_shutdown() {
tokio::select! {
_ = tokio::time::sleep(self.refresh_rate) => {
self.refresh().await;
},
_ = shutdown.recv() => {
log::trace!("TopologyRefresher: Received shutdown");
},
}
loop {
tokio::time::sleep(self.refresh_rate).await;
self.refresh().await;
}
assert!(shutdown.is_shutdown_poll());
log::debug!("TopologyRefresher: Exiting");
})
}
}
-27
View File
@@ -1,27 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crypto::asymmetric::identity::Ed25519RecoveryError;
use gateway_client::error::GatewayClientError;
use validator_client::ValidatorClientError;
#[derive(thiserror::Error, Debug)]
pub enum ClientCoreError {
#[error("I/O error: {0}")]
IoError(#[from] std::io::Error),
#[error("Gateway client error: {0}")]
GatewayClientError(#[from] GatewayClientError),
#[error("Ed25519 error: {0}")]
Ed25519RecoveryError(#[from] Ed25519RecoveryError),
#[error("Validator client error: {0}")]
ValidatorClientError(#[from] ValidatorClientError),
#[error("No gateway with id: {0}")]
NoGatewayWithId(String),
#[error("No gateways on network")]
NoGatewaysOnNetwork,
#[error("List of validator apis is empty")]
ListOfValidatorApisIsEmpty,
#[error("Could not load existing gateway configuration: {0}")]
CouldNotLoadExistingGatewayConfiguration(std::io::Error),
}
+25 -36
View File
@@ -14,27 +14,25 @@ use nymsphinx::addressing::nodes::NodeIdentity;
use rand::rngs::OsRng;
use rand::seq::SliceRandom;
use rand::thread_rng;
use tap::TapFallible;
use topology::{filter::VersionFilterable, gateway};
use url::Url;
use crate::{
client::key_manager::KeyManager,
config::{persistence::key_pathfinder::ClientKeyPathfinder, Config},
error::ClientCoreError,
};
pub async fn query_gateway_details(
validator_servers: Vec<Url>,
chosen_gateway_id: Option<&str>,
) -> Result<gateway::Node, ClientCoreError> {
) -> gateway::Node {
let validator_api = validator_servers
.choose(&mut thread_rng())
.ok_or(ClientCoreError::ListOfValidatorApisIsEmpty)?;
.expect("The list of validator apis is empty");
let validator_client = validator_client::ApiClient::new(validator_api.clone());
log::trace!("Fetching list of gateways from: {}", validator_api);
let gateways = validator_client.get_cached_gateways().await?;
let gateways = validator_client.get_cached_gateways().await.unwrap();
let valid_gateways = gateways
.into_iter()
.filter_map(|gateway| gateway.try_into().ok())
@@ -49,40 +47,38 @@ pub async fn query_gateway_details(
filtered_gateways
.iter()
.find(|gateway| gateway.identity_key.to_base58_string() == gateway_id)
.ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_id.to_string()))
.cloned()
.expect(&*format!("no gateway with id {} exists!", gateway_id))
.clone()
} else {
filtered_gateways
.choose(&mut rand::thread_rng())
.ok_or(ClientCoreError::NoGatewaysOnNetwork)
.cloned()
.expect("there are no gateways on the network!")
.clone()
}
}
pub async fn register_with_gateway_and_store_keys<T>(
gateway_details: gateway::Node,
config: &Config<T>,
) -> Result<(), ClientCoreError>
where
) where
T: NymConfig,
{
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?;
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
key_manager
.store_keys(&pathfinder)
.tap_err(|err| log::error!("Failed to generate keys: {err}"))?)
.expect("Failed to generated keys");
}
async fn register_with_gateway(
gateway: &gateway::Node,
our_identity: Arc<identity::KeyPair>,
) -> Result<Arc<SharedKeys>, ClientCoreError> {
) -> Arc<SharedKeys> {
let timeout = Duration::from_millis(1500);
let mut gateway_client = GatewayClient::new_init(
gateway.clients_address(),
@@ -90,59 +86,52 @@ async fn register_with_gateway(
gateway.owner.clone(),
our_identity.clone(),
timeout,
None,
);
gateway_client
.establish_connection()
.await
.tap_err(|_| log::warn!("Failed to establish connection with gateway!"))?;
let shared_keys = gateway_client
.expect("failed to establish connection with the gateway!");
gateway_client
.perform_initial_authentication()
.await
.tap_err(|_| log::warn!("Failed to register with the gateway!"))?;
Ok(shared_keys)
.expect("failed to register with the gateway!")
}
pub fn show_address<T>(config: &Config<T>) -> Result<(), ClientCoreError>
pub fn show_address<T>(config: &Config<T>)
where
T: config::NymConfig,
{
fn load_identity_keys(
pathfinder: &ClientKeyPathfinder,
) -> Result<identity::KeyPair, ClientCoreError> {
fn load_identity_keys(pathfinder: &ClientKeyPathfinder) -> identity::KeyPair {
let identity_keypair: identity::KeyPair =
pemstore::load_keypair(&pemstore::KeyPairPath::new(
pathfinder.private_identity_key().to_owned(),
pathfinder.public_identity_key().to_owned(),
))
.tap_err(|_| log::error!("Failed to read stored identity key files"))?;
Ok(identity_keypair)
.expect("Failed to read stored identity key files");
identity_keypair
}
fn load_sphinx_keys(
pathfinder: &ClientKeyPathfinder,
) -> Result<encryption::KeyPair, ClientCoreError> {
fn load_sphinx_keys(pathfinder: &ClientKeyPathfinder) -> encryption::KeyPair {
let sphinx_keypair: encryption::KeyPair =
pemstore::load_keypair(&pemstore::KeyPairPath::new(
pathfinder.private_encryption_key().to_owned(),
pathfinder.public_encryption_key().to_owned(),
))
.tap_err(|_| log::error!("Failed to read stored sphinx key files"))?;
Ok(sphinx_keypair)
.expect("Failed to read stored sphinx key files");
sphinx_keypair
}
let pathfinder = ClientKeyPathfinder::new_from_config(config);
let identity_keypair = load_identity_keys(&pathfinder)?;
let sphinx_keypair = load_sphinx_keys(&pathfinder)?;
let identity_keypair = load_identity_keys(&pathfinder);
let sphinx_keypair = load_sphinx_keys(&pathfinder);
let client_recipient = Recipient::new(
*identity_keypair.public_key(),
*sphinx_keypair.public_key(),
// TODO: below only works under assumption that gateway address == gateway id
// (which currently is true)
NodeIdentity::from_base58_string(config.get_gateway_id())?,
NodeIdentity::from_base58_string(config.get_gateway_id()).unwrap(),
);
println!("\nThe address of this client is: {}", client_recipient);
Ok(())
}
-1
View File
@@ -1,4 +1,3 @@
pub mod client;
pub mod config;
pub mod error;
pub mod init;
-1
View File
@@ -18,7 +18,6 @@ url = "2.2"
tokio = { version = "1.19.1", features = ["rt-multi-thread", "net", "signal", "macros"] } # async runtime
coconut-interface = { path = "../../common/coconut-interface" }
config = { path = "../../common/config" }
credentials = { path = "../../common/credentials" }
credential-storage = { path = "../../common/credential-storage" }
crypto = { path = "../../common/crypto", features = ["rand", "asymmetric", "symmetric", "aes", "hashing"] }
+4 -3
View File
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::error::Result;
use crate::{MNEMONIC, NYMD_URL};
use bip39::Mnemonic;
use network_defaults::{NymNetworkDetails, VOUCHER_INFO};
use std::str::FromStr;
@@ -16,9 +17,9 @@ pub(crate) struct Client {
}
impl Client {
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();
pub fn new() -> 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 = nymd::Config::try_from_nym_network_details(&network_details)
.expect("failed to construct valid validator client config with the provided network");
+4 -13
View File
@@ -6,6 +6,7 @@ use clap::{Args, Subcommand};
use pickledb::PickleDb;
use rand::rngs::OsRng;
use std::str::FromStr;
use url::Url;
use coconut_interface::{Attribute, Base58, BlindSignRequest, Bytable, Parameters};
use credential_storage::storage::Storage;
@@ -19,6 +20,7 @@ use validator_client::nymd::tx::Hash;
use crate::client::Client;
use crate::error::{CredentialClientError, Result};
use crate::state::{KeyPair, RequestData, State};
use crate::SIGNER_AUTHORITIES;
#[derive(Subcommand)]
pub(crate) enum Commands {
@@ -37,12 +39,6 @@ pub(crate) trait Execute {
#[derive(Args, Clone)]
pub(crate) struct Deposit {
/// The nymd URL that should be used
#[clap(long)]
nymd_url: String,
/// A mnemonic for the account that does the deposit
#[clap(long)]
mnemonic: String,
/// The amount that needs to be deposited
#[clap(long)]
amount: u64,
@@ -55,7 +51,7 @@ impl Execute for Deposit {
let signing_keypair = KeyPair::from(identity::KeyPair::new(&mut rng));
let encryption_keypair = KeyPair::from(encryption::KeyPair::new(&mut rng));
let client = Client::new(&self.nymd_url, &self.mnemonic);
let client = Client::new();
let tx_hash = client
.deposit(
self.amount,
@@ -100,10 +96,6 @@ pub(crate) struct GetCredential {
/// The hash of a successful deposit transaction
#[clap(long)]
tx_hash: String,
/// The URLs to the validator-api endpoints the are run as coconut signer authorities, separated
/// by comma (,)
#[clap(long)]
signer_authorities: String,
/// If we want to get the signature without attaching a blind sign request; it is expected that
/// there is already a signature stored on the signer
#[clap(long, parse(from_flag))]
@@ -116,8 +108,7 @@ impl Execute for GetCredential {
let mut state = db
.get::<State>(&self.tx_hash)
.ok_or(CredentialClientError::NoDeposit)?;
let urls = config::parse_validators(&self.signer_authorities);
let urls = SIGNER_AUTHORITIES.map(|addr| Url::from_str(addr).unwrap());
let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap();
let bandwidth_credential_attributes = if self.__no_request {
+8 -13
View File
@@ -11,24 +11,20 @@ cfg_if::cfg_if! {
use commands::{Commands, Execute};
use error::Result;
use network_defaults::setup_env;
use clap::Parser;
use pickledb::{PickleDb, PickleDbDumpPolicy, SerializationMethod};
pub const MNEMONIC: &str = "jazz fatigue diagram account outer wrist slide cherry mother grid network pause wolf pig round answer mail junior better hair dismiss toward access end";
pub const NYMD_URL: &str = "http://127.0.0.1:26657";
pub const CONTRACT_ADDRESS: &str = "nymt1nc5tatafv6eyq7llkr2gv50ff9e22mnfp9pc5s";
pub const SIGNER_AUTHORITIES: [&str; 1] = [
"http://127.0.0.1:8080",
];
#[derive(Parser)]
#[clap(author = "Nymtech", version, about)]
struct Cli {
/// Path pointing to an env file that configures the client.
#[clap(long)]
pub(crate) config_env_file: Option<std::path::PathBuf>,
/// Path where the sqlite credental database will be located.
/// It should point to a $HOME/$CLIENT_ID/data/db.sqlite file of
/// the client that is supposed to use the credential.
#[clap(long)]
pub(crate) credential_db_path: std::path::PathBuf,
#[clap(subcommand)]
command: Commands,
}
@@ -36,9 +32,8 @@ cfg_if::cfg_if! {
#[tokio::main]
async fn main() -> Result<()> {
let args = Cli::parse();
setup_env(args.config_env_file.clone());
let shared_storage = credential_storage::initialise_storage(args.credential_db_path.clone()).await;
let shared_storage = credential_storage::initialise_storage(std::path::PathBuf::from("/tmp/credential.db")).await;
let mut db = match PickleDb::load(
"credential.db",
PickleDbDumpPolicy::AutoDump,
+4 -5
View File
@@ -33,20 +33,19 @@ tokio-tungstenite = "0.14" # websocket
## internal
client-core = { path = "../client-core" }
coconut-interface = { path = "../../common/coconut-interface", optional = true }
config = { path = "../../common/config" }
credential-storage = { path = "../../common/credential-storage" }
credentials = { path = "../../common/credentials", optional = true }
credential-storage = { path = "../../common/credential-storage" }
config = { path = "../../common/config" }
crypto = { path = "../../common/crypto" }
gateway-client = { path = "../../common/client-libs/gateway-client" }
gateway-requests = { path = "../../gateway/gateway-requests" }
network-defaults = { path = "../../common/network-defaults" }
nymsphinx = { path = "../../common/nymsphinx" }
pemstore = { path = "../../common/pemstore" }
task = { path = "../../common/task" }
topology = { path = "../../common/topology" }
websocket-requests = { path = "websocket-requests" }
validator-client = { path = "../../common/client-libs/validator-client", features = ["nymd-client"] }
version-checker = { path = "../../common/version-checker" }
websocket-requests = { path = "websocket-requests" }
network-defaults = { path = "../../common/network-defaults" }
[features]
coconut = ["coconut-interface", "credentials", "credentials/coconut", "gateway-requests/coconut", "gateway-client/coconut", "client-core/coconut"]
-4
View File
@@ -50,10 +50,6 @@ impl NymConfig for Config {
.join("clients")
}
fn try_default_root_directory() -> Option<PathBuf> {
dirs::home_dir().map(|path| path.join(".nym").join("clients"))
}
fn root_directory(&self) -> PathBuf {
self.base.get_nym_root_directory()
}
+19 -54
View File
@@ -32,7 +32,6 @@ use nymsphinx::addressing::clients::Recipient;
use nymsphinx::addressing::nodes::NodeIdentity;
use nymsphinx::anonymous_replies::ReplySurb;
use nymsphinx::receiver::ReconstructedMessage;
use task::{wait_for_signal, ShutdownListener, ShutdownNotifier};
use crate::client::config::{Config, SocketType};
use crate::websocket;
@@ -86,7 +85,6 @@ impl NymClient {
&self,
topology_accessor: TopologyAccessor,
mix_tx: BatchMixMessageSender,
shutdown: ShutdownListener,
) {
info!("Starting loop cover traffic stream...");
@@ -100,7 +98,6 @@ impl NymClient {
mix_tx,
self.as_mix_recipient(),
topology_accessor,
shutdown,
)
.start();
}
@@ -112,7 +109,6 @@ impl NymClient {
ack_receiver: AcknowledgementReceiver,
input_receiver: InputMessageReceiver,
mix_sender: BatchMixMessageSender,
shutdown: ShutdownListener,
) {
let controller_config = real_messages_control::Config::new(
self.key_manager.ack_key(),
@@ -133,7 +129,6 @@ impl NymClient {
mix_sender,
topology_accessor,
reply_key_storage,
shutdown,
)
.start();
}
@@ -145,7 +140,6 @@ impl NymClient {
query_receiver: ReceivedBufferRequestReceiver,
mixnet_receiver: MixnetMessageReceiver,
reply_key_storage: ReplyKeyStorage,
shutdown: ShutdownListener,
) {
info!("Starting received messages buffer controller...");
ReceivedMessagesBufferController::new(
@@ -153,7 +147,6 @@ impl NymClient {
query_receiver,
mixnet_receiver,
reply_key_storage,
shutdown,
)
.start()
}
@@ -162,7 +155,6 @@ impl NymClient {
&mut self,
mixnet_message_sender: MixnetMessageSender,
ack_sender: AcknowledgementSender,
shutdown: ShutdownListener,
) -> GatewayClient {
let gateway_id = self.config.get_base().get_gateway_id();
if gateway_id.is_empty() {
@@ -205,12 +197,11 @@ impl NymClient {
ack_sender,
self.config.get_base().get_gateway_response_timeout(),
Some(bandwidth_controller),
Some(shutdown),
);
gateway_client
.set_disabled_credentials_mode(self.config.get_base().get_disabled_credentials_mode());
if self.config.get_base().get_disabled_credentials_mode() {
gateway_client.set_disabled_credentials_mode(true)
}
gateway_client
.authenticate_and_start()
.await
@@ -221,11 +212,7 @@ impl NymClient {
// future responsible for periodically polling directory server and updating
// the current global view of topology
async fn start_topology_refresher(
&mut self,
topology_accessor: TopologyAccessor,
shutdown: ShutdownListener,
) {
async fn start_topology_refresher(&mut self, topology_accessor: TopologyAccessor) {
let topology_refresher_config = TopologyRefresherConfig::new(
self.config.get_base().get_validator_api_endpoints(),
self.config.get_base().get_topology_refresh_rate(),
@@ -247,7 +234,7 @@ impl NymClient {
}
info!("Starting topology refresher...");
topology_refresher.start(shutdown);
topology_refresher.start();
}
// controller for sending sphinx packets to mixnet (either real traffic or cover traffic)
@@ -258,10 +245,9 @@ impl NymClient {
&mut self,
mix_rx: BatchMixMessageReceiver,
gateway_client: GatewayClient,
shutdown: ShutdownListener,
) {
info!("Starting mix traffic controller...");
MixTrafficController::new(mix_rx, gateway_client, shutdown).start();
MixTrafficController::new(mix_rx, gateway_client).start();
}
fn start_websocket_listener(
@@ -322,26 +308,20 @@ impl NymClient {
/// blocking version of `start` method. Will run forever (or until SIGINT is sent)
pub async fn run_forever(&mut self) {
let shutdown = self.start().await;
wait_for_signal().await;
self.start().await;
if let Err(e) = tokio::signal::ctrl_c().await {
error!(
"There was an error while capturing SIGINT - {:?}. We will terminate regardless",
e
);
}
println!(
"Received signal - the client will terminate now (threads are not yet nicely stopped, if you see stack traces that's alright)."
"Received SIGINT - 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");
}
pub async fn start(&mut self) -> ShutdownNotifier {
pub async fn start(&mut self) {
info!("Starting nym client");
// channels for inter-component communication
// TODO: make the channels be internally created by the relevant components
@@ -371,43 +351,30 @@ impl NymClient {
ReplyKeyStorage::load(self.config.get_base().get_reply_encryption_key_store_path())
.expect("Failed to load reply key storage!");
// Shutdown notifier for signalling tasks to stop
let shutdown = ShutdownNotifier::default();
// the components are started in very specific order. Unless you know what you are doing,
// do not change that.
self.start_topology_refresher(shared_topology_accessor.clone(), shutdown.subscribe())
self.start_topology_refresher(shared_topology_accessor.clone())
.await;
self.start_received_messages_buffer_controller(
received_buffer_request_receiver,
mixnet_messages_receiver,
reply_key_storage.clone(),
shutdown.subscribe(),
);
let gateway_client = self
.start_gateway_client(mixnet_messages_sender, ack_sender, shutdown.subscribe())
.start_gateway_client(mixnet_messages_sender, ack_sender)
.await;
self.start_mix_traffic_controller(
sphinx_message_receiver,
gateway_client,
shutdown.subscribe(),
);
self.start_mix_traffic_controller(sphinx_message_receiver, gateway_client);
self.start_real_traffic_controller(
shared_topology_accessor.clone(),
reply_key_storage,
ack_receiver,
input_receiver,
sphinx_message_sender.clone(),
shutdown.subscribe(),
);
self.start_cover_traffic_stream(
shared_topology_accessor,
sphinx_message_sender,
shutdown.subscribe(),
);
self.start_cover_traffic_stream(shared_topology_accessor, sphinx_message_sender);
match self.config.get_socket_type() {
SocketType::WebSocket => {
@@ -432,7 +399,5 @@ impl NymClient {
info!("Client startup finished!");
info!("The address of this client is: {}", self.as_mix_recipient());
shutdown
}
}
+26 -33
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use clap::Args;
use client_core::{config::GatewayEndpoint, error::ClientCoreError};
use client_core::config::GatewayEndpoint;
use config::NymConfig;
use crate::{
@@ -46,9 +46,10 @@ pub(crate) struct Init {
fastmode: bool,
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
/// with bandwidth credential requirement.
#[cfg(any(feature = "eth", feature = "coconut"))]
#[clap(long)]
/// with bandwidth credential requirement. If this value is set, --eth-endpoint and
/// --eth-private_key don't need to be set.
#[cfg(all(feature = "eth", not(feature = "coconut")))]
#[clap(long, conflicts_with_all = &["eth-endpoint", "eth-private-key"])]
enabled_credentials_mode: bool,
/// URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20
@@ -78,7 +79,7 @@ impl From<Init> for OverrideConfig {
port: init_config.port,
fastmode: init_config.fastmode,
#[cfg(any(feature = "eth", feature = "coconut"))]
#[cfg(all(feature = "eth", not(feature = "coconut")))]
enabled_credentials_mode: init_config.enabled_credentials_mode,
#[cfg(all(feature = "eth", not(feature = "coconut")))]
@@ -120,12 +121,7 @@ pub(crate) async fn execute(args: &Init) {
let override_config_fields = OverrideConfig::from(args.clone());
config = override_config(config, override_config_fields);
let gateway = setup_gateway(id, register_gateway, user_chosen_gateway_id, &config)
.await
.unwrap_or_else(|err| {
eprintln!("Failed to setup gateway\nError: {err}");
std::process::exit(1)
});
let gateway = setup_gateway(id, register_gateway, user_chosen_gateway_id, &config).await;
config.get_base_mut().with_gateway_endpoint(gateway);
let config_save_location = config.get_config_file_save_location();
@@ -143,10 +139,7 @@ pub(crate) async fn execute(args: &Init) {
);
println!("Client configuration completed.");
client_core::init::show_address(config.get_base()).unwrap_or_else(|err| {
eprintln!("Failed to show address\nError: {err}");
std::process::exit(1)
});
client_core::init::show_address(config.get_base());
}
async fn setup_gateway(
@@ -154,7 +147,7 @@ async fn setup_gateway(
register: bool,
user_chosen_gateway_id: Option<&str>,
config: &Config,
) -> Result<GatewayEndpoint, ClientCoreError> {
) -> GatewayEndpoint {
if register {
// Get the gateway details by querying the validator-api. Either pick one at random or use
// the chosen one if it's among the available ones.
@@ -163,16 +156,16 @@ async fn setup_gateway(
config.get_base().get_validator_api_endpoints(),
user_chosen_gateway_id,
)
.await?;
.await;
log::debug!("Querying gateway gives: {}", gateway);
// Registering with gateway by setting up and writing shared keys to disk
log::trace!("Registering gateway");
client_core::init::register_with_gateway_and_store_keys(gateway.clone(), config.get_base())
.await?;
.await;
println!("Saved all generated keys");
Ok(gateway.into())
gateway.into()
} else if user_chosen_gateway_id.is_some() {
// Just set the config, don't register or create any keys
// This assumes that the user knows what they are doing, and that the existing keys are
@@ -182,22 +175,22 @@ async fn setup_gateway(
config.get_base().get_validator_api_endpoints(),
user_chosen_gateway_id,
)
.await?;
.await;
log::debug!("Querying gateway gives: {}", gateway);
Ok(gateway.into())
gateway.into()
} else {
println!("Not registering gateway, will reuse existing config and keys");
let existing_config = Config::load_from_file(Some(id)).map_err(|err| {
log::error!(
"Unable to configure gateway: {err}. \n
Seems like the client was already initialized but it was not possible to read \
the existing configuration file. \n
CAUTION: Consider backing up your gateway keys and try force gateway registration, or \
removing the existing configuration and starting over."
);
ClientCoreError::CouldNotLoadExistingGatewayConfiguration(err)
})?;
Ok(existing_config.get_base().get_gateway_endpoint().clone())
match Config::load_from_file(Some(id)) {
Ok(existing_config) => existing_config.get_base().get_gateway_endpoint().clone(),
Err(err) => {
panic!(
"Unable to configure gateway: {err}. \n
Seems like the client was already initialized but it was not possible to read \
the existing configuration file. \n
CAUTION: Consider backing up your gateway keys and try force gateway registration, or \
removing the existing configuration and starting over."
)
}
}
}
}
+3 -6
View File
@@ -78,7 +78,7 @@ pub(crate) struct OverrideConfig {
port: Option<u16>,
fastmode: bool,
#[cfg(any(feature = "eth", feature = "coconut"))]
#[cfg(all(feature = "eth", not(feature = "coconut")))]
enabled_credentials_mode: bool,
#[cfg(all(feature = "eth", not(feature = "coconut")))]
@@ -126,15 +126,12 @@ pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Confi
.get_base_mut()
.with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY.to_string());
}
#[cfg(any(feature = "eth", feature = "coconut"))]
#[cfg(all(feature = "eth", not(feature = "coconut")))]
{
if args.enabled_credentials_mode {
config.get_base_mut().with_disabled_credentials(false)
}
}
#[cfg(all(feature = "eth", not(feature = "coconut")))]
{
if let Some(eth_endpoint) = args.eth_endpoint {
config.get_base_mut().with_eth_endpoint(eth_endpoint);
}
+5 -4
View File
@@ -35,9 +35,10 @@ pub(crate) struct Run {
port: Option<u16>,
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
/// with bandwidth credential requirement.
#[cfg(any(feature = "eth", feature = "coconut"))]
#[clap(long)]
/// with bandwidth credential requirement. If this value is set, --eth-endpoint and
/// --eth-private-key don't need to be set.
#[cfg(all(feature = "eth", not(feature = "coconut")))]
#[clap(long, conflicts_with_all = &["eth-endpoint", "eth-private-key"])]
enabled_credentials_mode: bool,
/// URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20
@@ -61,7 +62,7 @@ impl From<Run> for OverrideConfig {
port: run_config.port,
fastmode: false,
#[cfg(any(feature = "eth", feature = "coconut"))]
#[cfg(all(feature = "eth", not(feature = "coconut")))]
enabled_credentials_mode: run_config.enabled_credentials_mode,
#[cfg(all(feature = "eth", not(feature = "coconut")))]
+5 -6
View File
@@ -26,22 +26,21 @@ url = "2.2"
# internal
client-core = { path = "../client-core" }
coconut-interface = { path = "../../common/coconut-interface", optional = true }
config = { path = "../../common/config" }
credential-storage = { path = "../../common/credential-storage" }
credentials = { path = "../../common/credentials", optional = true }
credential-storage = { path = "../../common/credential-storage" }
config = { path = "../../common/config" }
crypto = { path = "../../common/crypto" }
gateway-client = { path = "../../common/client-libs/gateway-client" }
gateway-requests = { path = "../../gateway/gateway-requests" }
network-defaults = { path = "../../common/network-defaults" }
nymsphinx = { path = "../../common/nymsphinx" }
ordered-buffer = { path = "../../common/socks5/ordered-buffer" }
socks5-requests = { path = "../../common/socks5/requests" }
topology = { path = "../../common/topology" }
pemstore = { path = "../../common/pemstore" }
proxy-helpers = { path = "../../common/socks5/proxy-helpers" }
socks5-requests = { path = "../../common/socks5/requests" }
task = { path = "../../common/task" }
topology = { path = "../../common/topology" }
validator-client = { path = "../../common/client-libs/validator-client", features = ["nymd-client"] }
version-checker = { path = "../../common/version-checker" }
network-defaults = { path = "../../common/network-defaults" }
[features]
coconut = ["coconut-interface", "credentials", "gateway-requests/coconut", "gateway-client/coconut", "credentials/coconut", "client-core/coconut"]
-4
View File
@@ -33,10 +33,6 @@ impl NymConfig for Config {
.join("socks5-clients")
}
fn try_default_root_directory() -> Option<PathBuf> {
dirs::home_dir().map(|path| path.join(".nym").join("socks5-clients"))
}
fn root_directory(&self) -> PathBuf {
self.base.get_nym_root_directory()
}
+26 -76
View File
@@ -1,8 +1,6 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::sync::atomic::Ordering;
use client_core::client::cover_traffic_stream::LoopCoverTrafficStream;
use client_core::client::inbound_messages::{
InputMessage, InputMessageReceiver, InputMessageSender,
@@ -31,7 +29,6 @@ use gateway_client::{
use log::*;
use nymsphinx::addressing::clients::Recipient;
use nymsphinx::addressing::nodes::NodeIdentity;
use task::{wait_for_signal, ShutdownListener, ShutdownNotifier};
use crate::client::config::Config;
use crate::socks::{
@@ -87,7 +84,6 @@ impl NymClient {
&self,
topology_accessor: TopologyAccessor,
mix_tx: BatchMixMessageSender,
shutdown: ShutdownListener,
) {
info!("Starting loop cover traffic stream...");
@@ -101,7 +97,6 @@ impl NymClient {
mix_tx,
self.as_mix_recipient(),
topology_accessor,
shutdown,
)
.start();
}
@@ -113,7 +108,6 @@ impl NymClient {
ack_receiver: AcknowledgementReceiver,
input_receiver: InputMessageReceiver,
mix_sender: BatchMixMessageSender,
shutdown: ShutdownListener,
) {
let controller_config = client_core::client::real_messages_control::Config::new(
self.key_manager.ack_key(),
@@ -134,7 +128,6 @@ impl NymClient {
mix_sender,
topology_accessor,
reply_key_storage,
shutdown,
)
.start();
}
@@ -146,7 +139,6 @@ impl NymClient {
query_receiver: ReceivedBufferRequestReceiver,
mixnet_receiver: MixnetMessageReceiver,
reply_key_storage: ReplyKeyStorage,
shutdown: ShutdownListener,
) {
info!("Starting received messages buffer controller...");
ReceivedMessagesBufferController::new(
@@ -154,7 +146,6 @@ impl NymClient {
query_receiver,
mixnet_receiver,
reply_key_storage,
shutdown,
)
.start()
}
@@ -163,7 +154,6 @@ impl NymClient {
&mut self,
mixnet_message_sender: MixnetMessageSender,
ack_sender: AcknowledgementSender,
shutdown: ShutdownListener,
) -> GatewayClient {
let gateway_id = self.config.get_base().get_gateway_id();
if gateway_id.is_empty() {
@@ -206,12 +196,11 @@ impl NymClient {
ack_sender,
self.config.get_base().get_gateway_response_timeout(),
Some(bandwidth_controller),
Some(shutdown),
);
gateway_client
.set_disabled_credentials_mode(self.config.get_base().get_disabled_credentials_mode());
if self.config.get_base().get_disabled_credentials_mode() {
gateway_client.set_disabled_credentials_mode(true)
}
gateway_client
.authenticate_and_start()
.await
@@ -222,11 +211,7 @@ impl NymClient {
// future responsible for periodically polling directory server and updating
// the current global view of topology
async fn start_topology_refresher(
&mut self,
topology_accessor: TopologyAccessor,
shutdown: ShutdownListener,
) {
async fn start_topology_refresher(&mut self, topology_accessor: TopologyAccessor) {
let topology_refresher_config = TopologyRefresherConfig::new(
self.config.get_base().get_validator_api_endpoints(),
self.config.get_base().get_topology_refresh_rate(),
@@ -248,7 +233,7 @@ impl NymClient {
}
info!("Starting topology refresher...");
topology_refresher.start(shutdown);
topology_refresher.start();
}
// controller for sending sphinx packets to mixnet (either real traffic or cover traffic)
@@ -259,17 +244,15 @@ impl NymClient {
&mut self,
mix_rx: BatchMixMessageReceiver,
gateway_client: GatewayClient,
shutdown: ShutdownListener,
) {
info!("Starting mix traffic controller...");
MixTrafficController::new(mix_rx, gateway_client, shutdown).start();
MixTrafficController::new(mix_rx, gateway_client).start();
}
fn start_socks5_listener(
&self,
buffer_requester: ReceivedBufferRequestSender,
msg_input: InputMessageSender,
shutdown: ShutdownListener,
) {
info!("Starting socks5 listener...");
let auth_methods = vec![AuthenticationMethods::NoAuth as u8];
@@ -281,57 +264,43 @@ impl NymClient {
authenticator,
self.config.get_provider_mix_address(),
self.as_mix_recipient(),
shutdown,
);
tokio::spawn(async move { sphinx_socks.serve(msg_input, buffer_requester).await });
}
/// blocking version of `start` method. Will run forever (or until SIGINT is sent)
pub async fn run_forever(&mut self) {
let mut shutdown = self.start().await;
wait_for_signal().await;
self.start().await;
if let Err(e) = tokio::signal::ctrl_c().await {
error!(
"There was an error while capturing SIGINT - {:?}. We will terminate regardless",
e
);
}
log::info!("Sending shutdown");
client_core::client::SHUTDOWN_HAS_BEEN_SIGNALLED.store(true, Ordering::Relaxed);
shutdown.signal_shutdown().ok();
log::info!("Waiting for tasks to finish... (Press ctrl-c to force)");
shutdown.wait_for_shutdown().await;
log::info!("Stopping nym-socks5-client");
println!(
"Received SIGINT - the client will terminate now (threads are not yet nicely stopped, if you see stack traces that's alright)."
);
}
// Variant of `run_forever` that listends for remote control messages
pub async fn run_and_listen(&mut self, mut receiver: Socks5ControlMessageReceiver) {
let mut shutdown = self.start().await;
self.start().await;
tokio::select! {
message = receiver.next() => {
log::debug!("Received message: {:?}", message);
match message {
Some(Socks5ControlMessage::Stop) => {
log::info!("Received stop message");
}
None => {
log::info!("Channel closed, stopping");
log::info!("Shutting down");
log::info!("Graceful shutdown of tasks not yet implemented, you might see (harmless) panics until then");
}
None => log::debug!("None"),
}
}
_ = tokio::signal::ctrl_c() => {
log::info!("Received SIGINT");
},
}
log::info!("Sending shutdown");
client_core::client::SHUTDOWN_HAS_BEEN_SIGNALLED.store(true, Ordering::Relaxed);
shutdown.signal_shutdown().ok();
log::info!("Waiting for tasks to finish... (Press ctrl-c to force)");
shutdown.wait_for_shutdown().await;
log::info!("Stopping nym-socks5-client");
}
pub async fn start(&mut self) -> ShutdownNotifier {
pub async fn start(&mut self) {
info!("Starting nym client");
// channels for inter-component communication
// TODO: make the channels be internally created by the relevant components
@@ -361,52 +330,33 @@ impl NymClient {
ReplyKeyStorage::load(self.config.get_base().get_reply_encryption_key_store_path())
.expect("Failed to load reply key storage!");
// Shutdown notifier for signalling tasks to stop
let shutdown = ShutdownNotifier::default();
// the components are started in very specific order. Unless you know what you are doing,
// do not change that.
self.start_topology_refresher(shared_topology_accessor.clone(), shutdown.subscribe())
self.start_topology_refresher(shared_topology_accessor.clone())
.await;
self.start_received_messages_buffer_controller(
received_buffer_request_receiver,
mixnet_messages_receiver,
reply_key_storage.clone(),
shutdown.subscribe(),
);
let gateway_client = self
.start_gateway_client(mixnet_messages_sender, ack_sender, shutdown.subscribe())
.start_gateway_client(mixnet_messages_sender, ack_sender)
.await;
self.start_mix_traffic_controller(
sphinx_message_receiver,
gateway_client,
shutdown.subscribe(),
);
self.start_mix_traffic_controller(sphinx_message_receiver, gateway_client);
self.start_real_traffic_controller(
shared_topology_accessor.clone(),
reply_key_storage,
ack_receiver,
input_receiver,
sphinx_message_sender.clone(),
shutdown.subscribe(),
);
self.start_cover_traffic_stream(
shared_topology_accessor,
sphinx_message_sender,
shutdown.subscribe(),
);
self.start_socks5_listener(
received_buffer_request_sender,
input_sender,
shutdown.subscribe(),
);
self.start_cover_traffic_stream(shared_topology_accessor, sphinx_message_sender);
self.start_socks5_listener(received_buffer_request_sender, input_sender);
info!("Client startup finished!");
info!("The address of this client is: {}", self.as_mix_recipient());
shutdown
}
}
+26 -32
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use clap::Args;
use client_core::{config::GatewayEndpoint, error::ClientCoreError};
use client_core::config::GatewayEndpoint;
use config::NymConfig;
use crate::{
@@ -46,9 +46,10 @@ pub(crate) struct Init {
fastmode: bool,
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
/// with bandwidth credential requirement.
#[cfg(any(feature = "eth", feature = "coconut"))]
#[clap(long)]
/// with bandwidth credential requirement. If this value is set, --eth-endpoint and
/// --eth-private_key don't need to be set.
#[cfg(all(feature = "eth", not(feature = "coconut")))]
#[clap(long, conflicts_with_all = &["eth-endpoint", "eth-private-key"])]
enabled_credentials_mode: bool,
/// URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20
@@ -77,7 +78,7 @@ impl From<Init> for OverrideConfig {
port: init_config.port,
fastmode: init_config.fastmode,
#[cfg(any(feature = "eth", feature = "coconut"))]
#[cfg(all(feature = "eth", not(feature = "coconut")))]
enabled_credentials_mode: init_config.enabled_credentials_mode,
#[cfg(all(feature = "eth", not(feature = "coconut")))]
@@ -120,12 +121,7 @@ pub(crate) async fn execute(args: &Init) {
let override_config_fields = OverrideConfig::from(args.clone());
config = override_config(config, override_config_fields);
let gateway = setup_gateway(id, register_gateway, user_chosen_gateway_id, &config)
.await
.unwrap_or_else(|err| {
eprintln!("Failed to setup gateway\nError: {err}");
std::process::exit(1)
});
let gateway = setup_gateway(id, register_gateway, user_chosen_gateway_id, &config).await;
config.get_base_mut().with_gateway_endpoint(gateway);
let config_save_location = config.get_config_file_save_location();
@@ -143,10 +139,7 @@ pub(crate) async fn execute(args: &Init) {
);
println!("Client configuration completed.");
client_core::init::show_address(config.get_base()).unwrap_or_else(|err| {
eprintln!("Failed to show address\nError: {err}");
std::process::exit(1)
});
client_core::init::show_address(config.get_base());
}
async fn setup_gateway(
@@ -154,7 +147,7 @@ async fn setup_gateway(
register: bool,
user_chosen_gateway_id: Option<&str>,
config: &Config,
) -> Result<GatewayEndpoint, ClientCoreError> {
) -> GatewayEndpoint {
if register {
// Get the gateway details by querying the validator-api. Either pick one at random or use
// the chosen one if it's among the available ones.
@@ -163,16 +156,16 @@ async fn setup_gateway(
config.get_base().get_validator_api_endpoints(),
user_chosen_gateway_id,
)
.await?;
.await;
log::debug!("Querying gateway gives: {}", gateway);
// Registering with gateway by setting up and writing shared keys to disk
log::trace!("Registering gateway");
client_core::init::register_with_gateway_and_store_keys(gateway.clone(), config.get_base())
.await?;
.await;
println!("Saved all generated keys");
Ok(gateway.into())
gateway.into()
} else if user_chosen_gateway_id.is_some() {
// Just set the config, don't register or create any keys
// This assumes that the user knows what they are doing, and that the existing keys are
@@ -182,21 +175,22 @@ async fn setup_gateway(
config.get_base().get_validator_api_endpoints(),
user_chosen_gateway_id,
)
.await?;
.await;
log::debug!("Querying gateway gives: {}", gateway);
Ok(gateway.into())
gateway.into()
} else {
println!("Not registering gateway, will reuse existing config and keys");
let existing_config = Config::load_from_file(Some(id)).map_err(|err| {
log::error!(
"Unable to configure gateway: {err}. \n
Seems like the client was already initialized but it was not possible to read \
the existing configuration file. \n
CAUTION: Consider backing up your gateway keys and try force gateway registration, or \
removing the existing configuration and starting over."
);
ClientCoreError::CouldNotLoadExistingGatewayConfiguration(err)
})?;
Ok(existing_config.get_base().get_gateway_endpoint().clone())
match Config::load_from_file(Some(id)) {
Ok(existing_config) => existing_config.get_base().get_gateway_endpoint().clone(),
Err(err) => {
panic!(
"Unable to configure gateway: {err}. \n
Seems like the client was already initialized but it was not possible to read \
the existing configuration file. \n
CAUTION: Consider backing up your gateway keys and try force gateway registration, or \
removing the existing configuration and starting over."
)
}
}
}
}
+2 -5
View File
@@ -78,7 +78,7 @@ pub(crate) struct OverrideConfig {
port: Option<u16>,
fastmode: bool,
#[cfg(any(feature = "eth", feature = "coconut"))]
#[cfg(all(feature = "eth", not(feature = "coconut")))]
enabled_credentials_mode: bool,
#[cfg(all(feature = "eth", not(feature = "coconut")))]
@@ -121,14 +121,11 @@ pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Confi
.with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY.to_string());
}
#[cfg(any(feature = "eth", feature = "coconut"))]
#[cfg(all(feature = "eth", not(feature = "coconut")))]
{
if args.enabled_credentials_mode {
config.get_base_mut().with_disabled_credentials(false)
}
}
#[cfg(all(feature = "eth", not(feature = "coconut")))]
{
if let Some(eth_endpoint) = args.eth_endpoint {
config.get_base_mut().with_eth_endpoint(eth_endpoint);
}
+5 -4
View File
@@ -39,9 +39,10 @@ pub(crate) struct Run {
port: Option<u16>,
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
/// with bandwidth credential requirement.
#[cfg(any(feature = "eth", feature = "coconut"))]
#[clap(long)]
/// with bandwidth credential requirement. If this value is set, --eth-endpoint and
/// --eth-private-key don't need to be set.
#[cfg(all(feature = "eth", not(feature = "coconut")))]
#[clap(long, conflicts_with_all = &["eth-endpoint", "eth-private-key"])]
enabled_credentials_mode: bool,
/// URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20
@@ -64,7 +65,7 @@ impl From<Run> for OverrideConfig {
port: run_config.port,
fastmode: false,
#[cfg(any(feature = "eth", feature = "coconut"))]
#[cfg(all(feature = "eth", not(feature = "coconut")))]
enabled_credentials_mode: run_config.enabled_credentials_mode,
#[cfg(all(feature = "eth", not(feature = "coconut")))]
-5
View File
@@ -20,7 +20,6 @@ use socks5_requests::{ConnectionId, Message, RemoteAddress, Request};
use std::io;
use std::net::SocketAddr;
use std::pin::Pin;
use task::ShutdownListener;
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, ReadBuf};
use tokio::{self, net::TcpStream};
@@ -141,7 +140,6 @@ pub(crate) struct SocksClient {
service_provider: Recipient,
self_address: Recipient,
started_proxy: bool,
shutdown_listener: ShutdownListener,
}
impl Drop for SocksClient {
@@ -165,7 +163,6 @@ impl SocksClient {
service_provider: Recipient,
controller_sender: ControllerSender,
self_address: Recipient,
shutdown_listener: ShutdownListener,
) -> Self {
let connection_id = Self::generate_random();
SocksClient {
@@ -179,7 +176,6 @@ impl SocksClient {
service_provider,
self_address,
started_proxy: false,
shutdown_listener,
}
}
@@ -254,7 +250,6 @@ impl SocksClient {
conn_receiver,
input_sender,
connection_id,
self.shutdown_listener.clone(),
)
.run(move |conn_id, read_data, socket_closed| {
let provider_request = Request::new_send(conn_id, read_data, socket_closed);
+6 -32
View File
@@ -1,19 +1,16 @@
use client_core::client::received_buffer::ReconstructedMessagesReceiver;
use client_core::client::received_buffer::{ReceivedBufferMessage, ReceivedBufferRequestSender};
use futures::channel::mpsc;
use futures::StreamExt;
use log::*;
use client_core::client::received_buffer::ReconstructedMessagesReceiver;
use client_core::client::received_buffer::{ReceivedBufferMessage, ReceivedBufferRequestSender};
use nymsphinx::receiver::ReconstructedMessage;
use proxy_helpers::connection_controller::{ControllerCommand, ControllerSender};
use socks5_requests::Message;
use task::ShutdownListener;
pub(crate) struct MixnetResponseListener {
buffer_requester: ReceivedBufferRequestSender,
mix_response_receiver: ReconstructedMessagesReceiver,
controller_sender: ControllerSender,
shutdown: ShutdownListener,
}
impl Drop for MixnetResponseListener {
@@ -28,7 +25,6 @@ impl MixnetResponseListener {
pub(crate) fn new(
buffer_requester: ReceivedBufferRequestSender,
controller_sender: ControllerSender,
shutdown: ShutdownListener,
) -> Self {
let (mix_response_sender, mix_response_receiver) = mpsc::unbounded();
buffer_requester
@@ -39,7 +35,6 @@ impl MixnetResponseListener {
buffer_requester,
mix_response_receiver,
controller_sender,
shutdown,
}
}
@@ -59,13 +54,6 @@ impl MixnetResponseListener {
return;
}
Ok(Message::Response(data)) => data,
Ok(Message::NetworkRequesterResponse(r)) => {
error!(
"Network requester failed on connection id {} with error: {}",
r.connection_id, r.network_requester_error
);
return;
}
};
self.controller_sender
@@ -78,25 +66,11 @@ impl MixnetResponseListener {
}
pub(crate) async fn run(&mut self) {
while !self.shutdown.is_shutdown() {
tokio::select! {
received_responses = self.mix_response_receiver.next() => match received_responses {
Some(received_responses) => {
for reconstructed_message in received_responses {
self.on_message(reconstructed_message).await;
}
},
None => {
log::trace!("MixnetResponseListener: Stopping since channel closed");
break;
}
},
_ = self.shutdown.recv() => {
log::trace!("MixnetResponseListener: Received shutdown");
}
while let Some(received_responses) = self.mix_response_receiver.next().await {
for reconstructed_message in received_responses {
self.on_message(reconstructed_message).await;
}
}
assert!(self.shutdown.is_shutdown_poll());
log::debug!("MixnetResponseListener: Exiting");
error!("We should never see this message");
}
}
+42 -57
View File
@@ -11,7 +11,6 @@ use log::*;
use nymsphinx::addressing::clients::Recipient;
use proxy_helpers::connection_controller::Controller;
use std::net::SocketAddr;
use task::ShutdownListener;
use tokio::net::TcpListener;
/// A Socks5 server that listens for connections.
@@ -20,7 +19,6 @@ pub struct SphinxSocksServer {
listening_address: SocketAddr,
service_provider: Recipient,
self_address: Recipient,
shutdown: ShutdownListener,
}
impl SphinxSocksServer {
@@ -30,7 +28,6 @@ impl SphinxSocksServer {
authenticator: Authenticator,
service_provider: Recipient,
self_address: Recipient,
shutdown: ShutdownListener,
) -> Self {
// hardcode ip as we (presumably) ONLY want to listen locally. If we change it, we can
// just modify the config
@@ -41,7 +38,6 @@ impl SphinxSocksServer {
listening_address: format!("{}:{}", ip, port).parse().unwrap(),
service_provider,
self_address,
shutdown,
}
}
@@ -56,73 +52,62 @@ impl SphinxSocksServer {
info!("Serving Connections...");
// controller for managing all active connections
let (mut active_streams_controller, controller_sender) =
Controller::new(self.shutdown.clone());
let (mut active_streams_controller, controller_sender) = Controller::new();
tokio::spawn(async move {
active_streams_controller.run().await;
});
// listener for mix messages
let mut mixnet_response_listener = MixnetResponseListener::new(
buffer_requester,
controller_sender.clone(),
self.shutdown.clone(),
);
let mut mixnet_response_listener =
MixnetResponseListener::new(buffer_requester, controller_sender.clone());
tokio::spawn(async move {
mixnet_response_listener.run().await;
});
loop {
tokio::select! {
Ok((stream, _remote)) = listener.accept() => {
// TODO Optimize this
let mut client = SocksClient::new(
stream,
self.authenticator.clone(),
input_sender.clone(),
self.service_provider,
controller_sender.clone(),
self.self_address,
self.shutdown.clone(),
);
if let Ok((stream, _remote)) = listener.accept().await {
// TODO Optimize this
let mut client = SocksClient::new(
stream,
self.authenticator.clone(),
input_sender.clone(),
self.service_provider,
controller_sender.clone(),
self.self_address,
);
tokio::spawn(async move {
{
match client.run().await {
Ok(_) => {}
Err(error) => {
error!("Error! {}", error);
let error_text = format!("{}", error);
tokio::spawn(async move {
{
match client.run().await {
Ok(_) => {}
Err(error) => {
error!("Error! {}", error);
let error_text = format!("{}", error);
let response: ResponseCode;
let response: ResponseCode;
if error_text.contains("Host") {
response = ResponseCode::HostUnreachable;
} else if error_text.contains("Network") {
response = ResponseCode::NetworkUnreachable;
} else if error_text.contains("ttl") {
response = ResponseCode::TtlExpired
} else {
response = ResponseCode::Failure
}
if client.error(response).await.is_err() {
warn!("Failed to send error code");
};
if client.shutdown().await.is_err() {
warn!("Failed to shutdown TcpStream");
};
if error_text.contains("Host") {
response = ResponseCode::HostUnreachable;
} else if error_text.contains("Network") {
response = ResponseCode::NetworkUnreachable;
} else if error_text.contains("ttl") {
response = ResponseCode::TtlExpired
} else {
response = ResponseCode::Failure
}
};
// client gets dropped here
}
});
},
_ = self.shutdown.recv() => {
log::trace!("SphinxSocksServer: Received shutdown");
log::debug!("SphinxSocksServer: Exiting");
return Ok(());
}
if client.error(response).await.is_err() {
warn!("Failed to send error code");
};
if client.shutdown().await.is_err() {
warn!("Failed to shutdown TcpStream");
};
}
};
// client gets dropped here
}
});
}
}
}
@@ -4,8 +4,8 @@ import expect from 'expect';
describe('Query: balances', () => {
it('can query for an account balance', async () => {
const client = await ValidatorClient.connectForQuery(
'https://rpc.nymtech.net/', 'https://validator.nymtech.net/api/', 'n', 'n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g', 'n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw', 'nym');
'https://rpc.nyx.nodes.guru/', 'https://validator.nymtech.net/api/', 'n', 'n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g', 'n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw', 'nym');
const balance = await client.getBalance('n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy');
expect(Number.parseFloat(balance.amount)).toBeGreaterThan(0);
}).timeout(5000);
})
})
+4 -1
View File
@@ -132,7 +132,9 @@ impl NymClient {
bandwidth_controller,
);
gateway_client.set_disabled_credentials_mode(disabled_credentials_mode);
if disabled_credentials_mode {
gateway_client.set_disabled_credentials_mode(true)
}
gateway_client
.authenticate_and_start()
@@ -197,6 +199,7 @@ impl NymClient {
// don't bother with acks etc. for time being
let prepared_fragment = message_preparer
.prepare_chunk_for_sending(message_chunk, topology, &self.ack_key, &recipient)
.await
.unwrap();
console_warn!("packet is going to have round trip time of {:?}, but we're not going to do anything for acks anyway ", prepared_fragment.total_delay);
+2 -9
View File
@@ -20,13 +20,13 @@ web3 = { version = "0.17.0", default-features = false }
async-trait = { version = "0.1.51" }
# internal
coconut-interface = { path = "../../coconut-interface", optional = true }
credentials = { path = "../../credentials" }
crypto = { path = "../../crypto" }
gateway-requests = { path = "../../../gateway/gateway-requests" }
network-defaults = { path = "../../network-defaults" }
nymsphinx = { path = "../../nymsphinx" }
pemstore = { path = "../../pemstore" }
coconut-interface = { path = "../../coconut-interface", optional = true }
network-defaults = { path = "../../network-defaults" }
validator-client = { path = "../validator-client", optional = true }
[dependencies.tungstenite]
@@ -38,19 +38,12 @@ default-features = false
version = "1.19.1"
features = ["macros", "rt", "net", "sync", "time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream]
version = "0.1.9"
features = ["net", "sync", "time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
version = "0.14"
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.credential-storage]
path = "../../credential-storage"
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.task]
path = "../../task"
# wasm-only dependencies
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen]
version = "0.2"
@@ -30,8 +30,6 @@ use rand::rngs::OsRng;
use std::convert::TryFrom;
use std::sync::Arc;
use std::time::Duration;
#[cfg(not(target_arch = "wasm32"))]
use task::ShutdownListener;
use tungstenite::protocol::Message;
#[cfg(not(target_arch = "wasm32"))]
@@ -67,10 +65,6 @@ pub struct GatewayClient {
reconnection_attempts: usize,
/// Delay between each subsequent reconnection attempt.
reconnection_backoff: Duration,
#[cfg(not(target_arch = "wasm32"))]
/// Listen to shutdown messages.
shutdown: Option<ShutdownListener>,
}
impl GatewayClient {
@@ -86,7 +80,6 @@ impl GatewayClient {
ack_sender: AcknowledgementSender,
response_timeout_duration: Duration,
bandwidth_controller: Option<BandwidthController<PersistentStorage>>,
#[cfg(not(target_arch = "wasm32"))] shutdown: Option<ShutdownListener>,
) -> Self {
GatewayClient {
authenticated: false,
@@ -98,19 +91,12 @@ impl GatewayClient {
local_identity,
shared_key,
connection: SocketState::NotConnected,
packet_router: PacketRouter::new(
ack_sender,
mixnet_message_sender,
#[cfg(not(target_arch = "wasm32"))]
shutdown.clone(),
),
packet_router: PacketRouter::new(ack_sender, mixnet_message_sender),
response_timeout_duration,
bandwidth_controller,
should_reconnect_on_failure: true,
reconnection_attempts: DEFAULT_RECONNECTION_ATTEMPTS,
reconnection_backoff: DEFAULT_RECONNECTION_BACKOFF,
#[cfg(not(target_arch = "wasm32"))]
shutdown,
}
}
@@ -137,7 +123,6 @@ impl GatewayClient {
gateway_owner: String,
local_identity: Arc<identity::KeyPair>,
response_timeout_duration: Duration,
#[cfg(not(target_arch = "wasm32"))] shutdown: Option<ShutdownListener>,
) -> Self {
use futures::channel::mpsc;
@@ -145,12 +130,7 @@ impl GatewayClient {
// perfectly fine here, because it's not meant to be used
let (ack_tx, _) = mpsc::unbounded();
let (mix_tx, _) = mpsc::unbounded();
let packet_router = PacketRouter::new(
ack_tx,
mix_tx,
#[cfg(not(target_arch = "wasm32"))]
shutdown.clone(),
);
let packet_router = PacketRouter::new(ack_tx, mix_tx);
GatewayClient {
authenticated: false,
@@ -168,8 +148,6 @@ impl GatewayClient {
should_reconnect_on_failure: false,
reconnection_attempts: DEFAULT_RECONNECTION_ATTEMPTS,
reconnection_backoff: DEFAULT_RECONNECTION_BACKOFF,
#[cfg(not(target_arch = "wasm32"))]
shutdown,
}
}
@@ -301,31 +279,8 @@ impl GatewayClient {
let mut fused_timeout = timeout.fuse();
let mut fused_stream = conn.fuse();
// Bit of an ugly workaround for selecting on an `Option` without having access to
// `tokio::select`
#[cfg(not(target_arch = "wasm32"))]
let shutdown = {
let m_shutdown = self.shutdown.clone();
async {
if let Some(mut s) = m_shutdown {
s.recv().await
}
}
.fuse()
};
#[cfg(not(target_arch = "wasm32"))]
tokio::pin!(shutdown);
#[cfg(target_arch = "wasm32")]
let mut shutdown = Box::pin(async {}.fuse());
loop {
futures::select! {
_ = shutdown => {
log::trace!("GatewayClient control response: Received shutdown");
log::debug!("GatewayClient control response: Exiting");
break Err(GatewayClientError::ConnectionClosedGatewayShutdown);
}
_ = &mut fused_timeout => {
break Err(GatewayClientError::Timeout);
}
@@ -336,16 +291,14 @@ impl GatewayClient {
};
match ws_msg {
Message::Binary(bin_msg) => {
if let Err(err) = self.packet_router.route_received(vec![bin_msg]) {
log::warn!("Route received failed: {:?}", err);
}
self.packet_router.route_received(vec![bin_msg]);
}
Message::Text(txt_msg) => {
break ServerResponse::try_from(txt_msg).map_err(|_| GatewayClientError::MalformedResponse);
}
_ => (),
}
}
}
}
}
}
@@ -770,8 +723,6 @@ impl GatewayClient {
.as_ref()
.expect("no shared key present even though we're authenticated!"),
),
#[cfg(not(target_arch = "wasm32"))]
self.shutdown.clone(),
)
}
_ => unreachable!(),
@@ -64,9 +64,6 @@ pub enum GatewayClientError {
#[error("Connection was abruptly closed")]
ConnectionAbruptlyClosed,
#[error("Connection was abruptly closed as gateway was stopped")]
ConnectionClosedGatewayShutdown,
#[error("Received response was malformed")]
MalformedResponse,
@@ -96,9 +93,6 @@ pub enum GatewayClientError {
#[error("Timed out")]
Timeout,
#[error("Failed to send mixnet message")]
MixnetMsgSenderFailedToSend,
}
impl GatewayClientError {
@@ -8,10 +8,6 @@ use futures::channel::mpsc;
use log::*;
use nymsphinx::addressing::nodes::MAX_NODE_ADDRESS_UNPADDED_LEN;
use nymsphinx::params::packet_sizes::PacketSize;
#[cfg(not(target_arch = "wasm32"))]
use task::ShutdownListener;
use crate::error::GatewayClientError;
pub type MixnetMessageSender = mpsc::UnboundedSender<Vec<Vec<u8>>>;
pub type MixnetMessageReceiver = mpsc::UnboundedReceiver<Vec<Vec<u8>>>;
@@ -23,28 +19,20 @@ pub type AcknowledgementReceiver = mpsc::UnboundedReceiver<Vec<Vec<u8>>>;
pub struct PacketRouter {
ack_sender: AcknowledgementSender,
mixnet_message_sender: MixnetMessageSender,
#[cfg(not(target_arch = "wasm32"))]
shutdown: Option<ShutdownListener>,
}
impl PacketRouter {
pub fn new(
ack_sender: AcknowledgementSender,
mixnet_message_sender: MixnetMessageSender,
#[cfg(not(target_arch = "wasm32"))] shutdown: Option<ShutdownListener>,
) -> Self {
PacketRouter {
ack_sender,
mixnet_message_sender,
#[cfg(not(target_arch = "wasm32"))]
shutdown,
}
}
pub fn route_received(
&mut self,
unwrapped_packets: Vec<Vec<u8>>,
) -> Result<(), GatewayClientError> {
pub fn route_received(&self, unwrapped_packets: Vec<Vec<u8>>) {
let mut received_messages = Vec::new();
let mut received_acks = Vec::new();
@@ -72,29 +60,24 @@ impl PacketRouter {
}
}
// due to how we are currently using it, those unwraps can't fail, but if we ever
// wanted to make `gateway-client` into some more generic library, we would probably need
// to catch that error or something.
if !received_messages.is_empty() {
trace!("routing 'real'");
if let Err(err) = self.mixnet_message_sender.unbounded_send(received_messages) {
#[cfg(not(target_arch = "wasm32"))]
if let Some(shutdown) = &mut self.shutdown {
if shutdown.is_shutdown_poll() {
// This should ideally not happen, but it's ok
log::warn!("Failed to send mixnet message due to receiver task shutdown");
return Err(GatewayClientError::MixnetMsgSenderFailedToSend);
}
}
// This should never happen during ordinary operation the way it's currently used.
// Abort to be on the safe side
panic!("Failed to send mixnet message: {:?}", err);
}
self.mixnet_message_sender
.unbounded_send(received_messages)
.unwrap();
}
if !received_acks.is_empty() {
trace!("routing acks");
if let Err(e) = self.ack_sender.unbounded_send(received_acks) {
error!("failed to send ack: {:?}", e);
match self.ack_sender.unbounded_send(received_acks) {
Ok(_) => {}
Err(e) => {
error!("failed to send ack: {:?}", e);
}
};
}
Ok(())
}
}
@@ -11,8 +11,6 @@ use gateway_requests::registration::handshake::SharedKeys;
use gateway_requests::BinaryResponse;
use log::*;
use std::sync::Arc;
#[cfg(not(target_arch = "wasm32"))]
use task::ShutdownListener;
use tungstenite::Message;
#[cfg(not(target_arch = "wasm32"))]
@@ -46,9 +44,9 @@ pub(crate) struct PartiallyDelegated {
impl PartiallyDelegated {
fn route_socket_message(
ws_msg: Message,
packet_router: &mut PacketRouter,
packet_router: &PacketRouter,
shared_key: &SharedKeys,
) -> Result<(), GatewayClientError> {
) {
match ws_msg {
Message::Binary(bin_msg) => {
// this function decrypts the request and checks the MAC
@@ -62,7 +60,7 @@ impl PartiallyDelegated {
"message received from the gateway was malformed! - {:?}",
err
);
return Ok(());
return;
}
};
@@ -76,22 +74,18 @@ impl PartiallyDelegated {
// This would also require NOT discarding any text responses here.
// TODO: those can return the "send confirmations" - perhaps it should be somehow worked around?
Message::Text(text) => {
trace!(
"received a text message - probably a response to some previous query! - {}",
text
);
Ok(())
}
_ => Ok(()),
}
Message::Text(text) => trace!(
"received a text message - probably a response to some previous query! - {}",
text
),
_ => (),
};
}
pub(crate) fn split_and_listen_for_mixnet_messages(
conn: WsConn,
packet_router: PacketRouter,
shared_key: Arc<SharedKeys>,
#[cfg(not(target_arch = "wasm32"))] shutdown: Option<ShutdownListener>,
) -> Self {
// when called for, it NEEDS TO yield back the stream so that we could merge it and
// read control request responses.
@@ -103,34 +97,10 @@ impl PartiallyDelegated {
let mixnet_receiver_future = async move {
let mut fused_receiver = notify_receiver.fuse();
let mut fused_stream = (&mut stream).fuse();
let mut packet_router = packet_router;
// Bit of an ugly workaround for selecting on an `Option` without having access to
// `tokio::select`
#[cfg(not(target_arch = "wasm32"))]
let shutdown = {
let m_shutdown = shutdown.clone();
async {
if let Some(mut s) = m_shutdown {
s.recv().await
}
}
.fuse()
};
#[cfg(not(target_arch = "wasm32"))]
tokio::pin!(shutdown);
#[cfg(target_arch = "wasm32")]
let mut shutdown = Box::pin(async {}.fuse());
let ret_err = loop {
futures::select! {
_ = shutdown => {
log::trace!("GatewayClient listener: Received shutdown");
log::debug!("GatewayClient listener: Exiting");
return;
}
_ = &mut fused_receiver => {
_ = fused_receiver => {
break Ok(());
}
msg = fused_stream.next() => {
@@ -138,9 +108,7 @@ impl PartiallyDelegated {
Err(err) => break Err(err),
Ok(msg) => msg
};
if let Err(err) = Self::route_socket_message(ws_msg, &mut packet_router, shared_key.as_ref()) {
log::warn!("Route socket message failed: {:?}", err);
}
Self::route_socket_message(ws_msg, &packet_router, shared_key.as_ref());
}
};
};
@@ -194,10 +162,12 @@ impl PartiallyDelegated {
.expect("stream sender was somehow dropped without sending anything!");
if let Some(res) = receive_res {
let _res = res?;
panic!(
"This should have NEVER happened - returned a stream before receiving notification"
)
if let Err(err) = res {
// the receiver got an error. most likely a network one.
return Err(err);
} else {
panic!("This should have NEVER happened - returned a stream before receiving notification")
}
}
// this call failing is incredibly unlikely, but not impossible.
@@ -489,7 +489,7 @@ impl TryFrom<ProtoContractCodeHistoryEntry> for ContractCodeHistoryEntry {
}
}
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Serialize)]
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
pub struct GasInfo {
/// GasWanted is the maximum units of work we allow this tx to perform.
pub gas_wanted: Gas,
@@ -645,7 +645,7 @@ impl InstantiateOptions {
}
}
#[derive(Debug, Serialize)]
#[derive(Debug)]
pub struct InstantiateResult {
/// The address of the newly instantiated contract
pub contract_address: AccountId,
@@ -658,7 +658,7 @@ pub struct InstantiateResult {
pub gas_info: GasInfo,
}
#[derive(Debug, Serialize)]
#[derive(Debug)]
pub struct ChangeAdminResult {
pub logs: Vec<Log>,
@@ -668,7 +668,7 @@ pub struct ChangeAdminResult {
pub gas_info: GasInfo,
}
#[derive(Debug, Serialize)]
#[derive(Debug)]
pub struct MigrateResult {
pub logs: Vec<Log>,
@@ -678,7 +678,7 @@ pub struct MigrateResult {
pub gas_info: GasInfo,
}
#[derive(Debug, Serialize)]
#[derive(Debug)]
pub struct ExecuteResult {
pub logs: Vec<Log>,
@@ -10,8 +10,6 @@ use crate::nymd::error::NymdError;
use crate::nymd::fee::DEFAULT_SIMULATED_GAS_MULTIPLIER;
use crate::nymd::wallet::DirectSecp256k1HdWallet;
use cosmrs::cosmwasm;
use cosmrs::rpc::endpoint::block::Response as BlockResponse;
use cosmrs::rpc::query::Query;
use cosmrs::rpc::Error as TendermintRpcError;
use cosmrs::rpc::HttpClientUrl;
use cosmrs::tx::Msg;
@@ -26,7 +24,7 @@ use mixnet_contract_common::{
PagedMixDelegationsResponse, PagedMixnodeResponse, PagedRewardedSetResponse, QueryMsg,
RewardedSetUpdateDetails,
};
use serde::{Deserialize, Serialize};
use serde::Serialize;
use std::convert::TryInto;
use std::time::SystemTime;
use vesting_contract_common::ExecuteMsg as VestingExecuteMsg;
@@ -214,10 +212,6 @@ impl<C> NymdClient<C> {
&self.config
}
pub fn current_chain_details(&self) -> &ChainDetails {
&self.config.chain_details
}
pub fn set_mixnet_contract_address(&mut self, address: AccountId) {
self.config.mixnet_contract_address = Some(address);
}
@@ -288,30 +282,6 @@ impl<C> NymdClient<C> {
self.simulated_gas_multiplier = multiplier;
}
pub async fn query_contract_smart<M, T>(
&self,
contract: &AccountId,
query_msg: &M,
) -> Result<T, NymdError>
where
C: CosmWasmClient + Sync,
M: ?Sized + Serialize + Sync,
for<'a> T: Deserialize<'a>,
{
self.client.query_contract_smart(contract, query_msg).await
}
pub async fn query_contract_raw(
&self,
contract: &AccountId,
query_data: Vec<u8>,
) -> Result<Vec<u8>, NymdError>
where
C: CosmWasmClient + Sync,
{
self.client.query_contract_raw(contract, query_data).await
}
pub fn wrap_contract_execute_message<M>(
&self,
contract_address: &AccountId,
@@ -338,13 +308,6 @@ impl<C> NymdClient<C> {
&self.client_address.as_ref().unwrap()[0]
}
pub fn signer(&self) -> &DirectSecp256k1HdWallet
where
C: SigningCosmWasmClient,
{
self.client.signer()
}
pub fn gas_price(&self) -> &GasPrice
where
C: SigningCosmWasmClient,
@@ -368,26 +331,11 @@ impl<C> NymdClient<C> {
address: &AccountId,
) -> Result<Option<Account>, NymdError>
where
C: CosmWasmClient + Sync,
C: SigningCosmWasmClient + Sync,
{
self.client.get_account(address).await
}
pub async fn get_account_public_key(
&self,
address: &AccountId,
) -> Result<Option<cosmrs::crypto::PublicKey>, NymdError>
where
C: CosmWasmClient + Sync,
{
if let Some(account) = self.client.get_account(address).await? {
let base_account = account.try_get_base_account()?;
return Ok(base_account.pubkey);
}
Ok(None)
}
pub async fn get_current_block_timestamp(&self) -> Result<TendermintTime, NymdError>
where
C: CosmWasmClient + Sync,
@@ -405,13 +353,6 @@ impl<C> NymdClient<C> {
Ok(self.client.get_block(height).await?.block.header.time)
}
pub async fn get_block(&self, height: Option<u32>) -> Result<BlockResponse, NymdError>
where
C: CosmWasmClient + Sync,
{
self.client.get_block(height).await
}
pub async fn get_current_block_height(&self) -> Result<Height, NymdError>
where
C: CosmWasmClient + Sync,
@@ -456,13 +397,6 @@ impl<C> NymdClient<C> {
self.client.get_balance(address, denom).await
}
pub async fn get_all_balances(&self, address: &AccountId) -> Result<Vec<Coin>, NymdError>
where
C: CosmWasmClient + Sync,
{
self.client.get_all_balances(address).await
}
pub async fn get_tx(&self, id: tx::Hash) -> Result<TxResponse, NymdError>
where
C: CosmWasmClient + Sync,
@@ -470,13 +404,6 @@ impl<C> NymdClient<C> {
self.client.get_tx(id).await
}
pub async fn search_tx(&self, query: Query) -> Result<Vec<TxResponse>, NymdError>
where
C: CosmWasmClient + Sync,
{
self.client.search_tx(query).await
}
pub async fn get_total_supply(&self) -> Result<Vec<Coin>, NymdError>
where
C: CosmWasmClient + Sync,
@@ -7,11 +7,9 @@ use crate::nymd::error::NymdError;
use crate::nymd::NymdClient;
use async_trait::async_trait;
use cosmwasm_std::{Coin as CosmWasmCoin, Timestamp};
use mixnet_contract_common::IdentityKey;
use vesting_contract::vesting::Account;
use vesting_contract_common::{
messages::QueryMsg as VestingQueryMsg, AllDelegationsResponse, DelegationTimesResponse,
OriginalVestingResponse, Period, PledgeData, VestingDelegation,
messages::QueryMsg as VestingQueryMsg, OriginalVestingResponse, Period, PledgeData,
};
#[async_trait]
@@ -72,37 +70,6 @@ pub trait VestingQueryClient {
&self,
vesting_account_address: &str,
) -> Result<Period, NymdError>;
async fn get_delegation_timestamps(
&self,
address: &str,
mix_identity: String,
) -> Result<DelegationTimesResponse, NymdError>;
async fn get_all_vesting_delegations_paged(
&self,
start_after: Option<(u32, IdentityKey, u64)>,
limit: Option<u32>,
) -> Result<AllDelegationsResponse, NymdError>;
async fn get_all_vesting_delegations(&self) -> Result<Vec<VestingDelegation>, NymdError> {
let mut delegations = Vec::new();
let mut start_after = None;
loop {
let mut paged_response = self
.get_all_vesting_delegations_paged(start_after.take(), None)
.await?;
delegations.append(&mut paged_response.delegations);
if let Some(start_after_res) = paged_response.start_next_after {
start_after = Some(start_after_res)
} else {
break;
}
}
Ok(delegations)
}
}
#[async_trait]
@@ -265,29 +232,4 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
.query_contract_smart(self.vesting_contract_address(), &request)
.await
}
async fn get_delegation_timestamps(
&self,
address: &str,
mix_identity: String,
) -> Result<DelegationTimesResponse, NymdError> {
let request = VestingQueryMsg::GetDelegationTimes {
address: address.to_string(),
mix_identity,
};
self.client
.query_contract_smart(self.vesting_contract_address(), &request)
.await
}
async fn get_all_vesting_delegations_paged(
&self,
start_after: Option<(u32, IdentityKey, u64)>,
limit: Option<u32>,
) -> Result<AllDelegationsResponse, NymdError> {
let request = VestingQueryMsg::GetAllDelegations { start_after, limit };
self.client
.query_contract_smart(self.vesting_contract_address(), &request)
.await
}
}
@@ -4,7 +4,7 @@
use crate::nymd::error::NymdError;
use config::defaults;
use cosmrs::bip32::{DerivationPath, XPrv};
use cosmrs::crypto::secp256k1::{Signature, SigningKey};
use cosmrs::crypto::secp256k1::SigningKey;
use cosmrs::crypto::PublicKey;
use cosmrs::tx::SignDoc;
use cosmrs::{tx, AccountId};
@@ -105,17 +105,6 @@ impl DirectSecp256k1HdWallet {
self.secret.to_string()
}
pub fn sign_raw_with_account(
&self,
signer: &AccountData,
message: &[u8],
) -> Result<Signature, NymdError> {
signer
.private_key
.sign(message)
.map_err(|_| NymdError::SigningFailure)
}
pub fn sign_direct_with_account(
&self,
signer: &AccountData,
-32
View File
@@ -1,32 +0,0 @@
[package]
name = "nym-cli-commands"
version = "1.0.0"
authors = ["Nym Technologies SA"]
edition = "2021"
[dependencies]
base64 = "0.13.0"
bip39 = "1.0.1"
bs58 = "0.4"
comfy-table = "6.0.0"
cfg-if = "1.0.0"
clap = { version = "3.2", features = ["derive"] }
handlebars = "3.0.1"
humantime-serde = "1.0"
k256 = { version = "0.10", features = ["ecdsa", "sha256"] }
log = "0.4"
rand = {version = "0.6", features = ["std"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1"
thiserror = "1"
time = { version = "0.3.6", features = ["parsing", "formatting"] }
toml = "0.5.6"
url = "2.2"
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" }
cosmwasm-std = { version = "1.0.0" }
validator-client = { path = "../client-libs/validator-client", features = ["nymd-client"] }
network-defaults = { path = "../network-defaults" }
mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" }
vesting-contract-common = { path = "../cosmwasm-smart-contracts/vesting-contract" }
-13
View File
@@ -1,13 +0,0 @@
# Common `clap` Command Crate
This crate contains `clap` commands for common operations:
- account creation and queries
- block queries
- cosmwasm uploads, instantiate, execution, query, etc
- mixnet actions and queries
- sign and verify messages
- query for transactions
- create vesting schedules and query for them
For how to use this crate, please see the [Nym CLI](../../tools/nym-cli).
-4
View File
@@ -1,4 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
// TODO: add coconut commands here
-18
View File
@@ -1,18 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ContextError {
#[error("mnemonic was not provided, pass as an argument or an env var called MNEMONIC")]
MnemonicNotProvided,
#[error("failed to parse mnemonic - {0}")]
Bip39Error(#[from] bip39::Error),
// there are lots of error that can occur in the nymd client, so just pass through their display details
// TODO: improve this to return known errors
#[error("failed to create client - {0}")]
NymdError(String),
}
-138
View File
@@ -1,138 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use network_defaults::{
setup_env,
var_names::{API_VALIDATOR, MIXNET_CONTRACT_ADDRESS, NYMD_VALIDATOR, VESTING_CONTRACT_ADDRESS},
NymNetworkDetails,
};
use validator_client::nymd::{self, AccountId, NymdClient, QueryNymdClient, SigningNymdClient};
pub use validator_client::validator_api::Client as ValidatorApiClient;
use crate::context::errors::ContextError;
pub mod errors;
pub type SigningClient = validator_client::nymd::NymdClient<SigningNymdClient>;
pub type QueryClient = validator_client::nymd::NymdClient<QueryNymdClient>;
pub type SigningClientWithValidatorAPI = validator_client::Client<SigningNymdClient>;
pub type QueryClientWithValidatorAPI = validator_client::Client<QueryNymdClient>;
#[derive(Debug)]
pub struct ClientArgs {
pub config_env_file: Option<std::path::PathBuf>,
pub nymd_url: Option<String>,
pub validator_api_url: Option<String>,
pub mnemonic: Option<bip39::Mnemonic>,
pub mixnet_contract_address: Option<AccountId>,
pub vesting_contract_address: Option<AccountId>,
}
pub fn get_network_details(args: &ClientArgs) -> Result<NymNetworkDetails, ContextError> {
// let the network defaults crate handle setting up the env vars if the file arg is set, otherwise
// it will default to what is already in env vars, falling back to mainnet
setup_env(args.config_env_file.clone());
// override the env vars with user supplied arguments, if set
if let Some(nymd_url) = args.nymd_url.as_ref() {
std::env::set_var(NYMD_VALIDATOR, nymd_url);
}
if let Some(validator_api_url) = args.validator_api_url.as_ref() {
std::env::set_var(API_VALIDATOR, validator_api_url);
}
if let Some(mixnet_contract_address) = args.mixnet_contract_address.as_ref() {
std::env::set_var(MIXNET_CONTRACT_ADDRESS, mixnet_contract_address.to_string());
}
if let Some(vesting_contract_address) = args.vesting_contract_address.as_ref() {
std::env::set_var(
VESTING_CONTRACT_ADDRESS,
vesting_contract_address.to_string(),
);
}
Ok(NymNetworkDetails::new_from_env())
}
pub fn create_signing_client(
args: ClientArgs,
network_details: &NymNetworkDetails,
) -> Result<SigningClient, ContextError> {
let client_config = nymd::Config::try_from_nym_network_details(network_details)
.expect("failed to construct valid validator client config with the provided network");
// get mnemonic
let mnemonic = match std::env::var("MNEMONIC") {
Ok(value) => bip39::Mnemonic::parse(value)?,
// env var MNEMONIC is not present, so try to fall back to arg --mnemonic ...
Err(_) => match args.mnemonic {
Some(value) => value,
None => return Err(ContextError::MnemonicNotProvided), // no env var or arg provided
},
};
let nymd_url = network_details
.endpoints
.first()
.expect("network details are not defined")
.nymd_url
.as_str();
match NymdClient::connect_with_mnemonic(client_config, nymd_url, mnemonic, None) {
Ok(client) => Ok(client),
Err(e) => Err(ContextError::NymdError(format!("{:?}", e))),
}
}
pub fn create_query_client(
network_details: &NymNetworkDetails,
) -> Result<QueryClient, ContextError> {
let client_config = nymd::Config::try_from_nym_network_details(network_details)
.expect("failed to construct valid validator client config with the provided network");
let nymd_url = network_details
.endpoints
.first()
.expect("network details are not defined")
.nymd_url
.as_str();
match NymdClient::connect(client_config, nymd_url) {
Ok(client) => Ok(client),
Err(e) => Err(ContextError::NymdError(format!("{:?}", e))),
}
}
pub fn create_signing_client_with_validator_api(
args: ClientArgs,
network_details: &NymNetworkDetails,
) -> Result<SigningClientWithValidatorAPI, ContextError> {
let client_config = validator_client::Config::try_from_nym_network_details(network_details)
.expect("failed to construct valid validator client config with the provided network");
// get mnemonic
let mnemonic = match std::env::var("MNEMONIC") {
Ok(value) => bip39::Mnemonic::parse(value)?,
// env var MNEMONIC is not present, so try to fall back to arg --mnemonic ...
Err(_) => match args.mnemonic {
Some(value) => value,
None => return Err(ContextError::MnemonicNotProvided), // no env var or arg provided
},
};
match validator_client::client::Client::new_signing(client_config, mnemonic) {
Ok(client) => Ok(client),
Err(e) => Err(ContextError::NymdError(format!("{:?}", e))),
}
}
pub fn create_query_client_with_validator_api(
network_details: &NymNetworkDetails,
) -> Result<QueryClientWithValidatorAPI, ContextError> {
let client_config = validator_client::Config::try_from_nym_network_details(network_details)
.expect("failed to construct valid validator client config with the provided network");
match validator_client::client::Client::new_query(client_config) {
Ok(client) => Ok(client),
Err(e) => Err(ContextError::NymdError(format!("{:?}", e))),
}
}
-7
View File
@@ -1,7 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod coconut;
pub mod context;
pub mod utils;
pub mod validator;
-44
View File
@@ -1,44 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::error::Error;
use std::fmt::Display;
use cosmwasm_std::{Coin as CosmWasmCoin, Decimal};
use log::error;
use validator_client::nymd::Coin;
pub fn pretty_coin(coin: &Coin) -> String {
let amount = Decimal::from_ratio(coin.amount, 1_000_000u128);
let denom = if coin.denom.starts_with('u') {
&coin.denom[1..]
} else {
&coin.denom
};
format!("{} {}", amount, denom)
}
pub fn pretty_cosmwasm_coin(coin: &CosmWasmCoin) -> String {
let amount = Decimal::from_ratio(coin.amount, 1_000_000u128);
let denom = if coin.denom.starts_with('u') {
&coin.denom[1..]
} else {
&coin.denom
};
format!("{} {}", amount, denom)
}
pub fn show_error<E>(e: E)
where
E: Display,
{
error!("{}", e);
}
pub fn show_error_passthrough<E>(e: E) -> E
where
E: Error + Display,
{
error!("{}", e);
e
}
@@ -1,72 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use log::{error, info};
use validator_client::nymd::AccountId;
use crate::context::QueryClient;
use crate::utils::{pretty_coin, show_error};
#[derive(Debug, Parser)]
pub struct Args {
#[clap(value_parser)]
#[clap(help = "The account address to get the balance for")]
pub address: Option<AccountId>,
#[clap(long)]
#[clap(help = "Optional currency to show balance for")]
pub denom: Option<String>,
#[clap(long, requires = "denom")]
#[clap(help = "Optionally hide the denom")]
pub hide_denom: bool,
#[clap(long)]
#[clap(help = "Show as a raw value")]
pub raw: bool,
}
pub async fn query_balance(
args: Args,
client: &QueryClient,
address_from_mnemonic: Option<AccountId>,
) {
if args.address.is_none() && address_from_mnemonic.is_none() {
error!("Please specify an account address or a mnemonic to get the balance for");
return;
}
let address = args
.address
.unwrap_or_else(|| address_from_mnemonic.expect("please provide a mnemonic"));
info!("Getting balance for {}...", address);
match client.get_all_balances(&address).await {
Ok(coins) => {
if coins.is_empty() {
println!("No balance");
return;
}
let denom = args.denom.unwrap_or_else(|| "".to_string());
for coin in coins {
if denom.is_empty() || denom.eq_ignore_ascii_case(&coin.denom) {
if args.raw {
if !args.hide_denom {
println!("{}", coin);
} else {
println!("{}", coin.amount);
}
} else {
println!("{}", pretty_coin(&coin));
}
}
}
}
Err(e) => show_error(e),
}
}
@@ -1,24 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use validator_client::nymd::wallet::DirectSecp256k1HdWallet;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
// allowed values are 12, 18 or 24
pub word_count: Option<usize>,
}
pub fn create_account(args: Args, prefix: &str) {
let word_count = args.word_count.unwrap_or(24);
let mnemonic = bip39::Mnemonic::generate(word_count).expect("failed to generate mnemonic!");
let wallet =
DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic).expect("failed to build wallet!");
// Output address and mnemonics into separate lines for easier parsing
println!("{}", wallet.mnemonic());
println!("{}", wallet.try_derive_accounts().unwrap()[0].address());
}
@@ -1,28 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod balance;
pub mod create;
pub mod pubkey;
pub mod send;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct Account {
#[clap(subcommand)]
pub command: Option<AccountCommands>,
}
#[derive(Debug, Subcommand)]
pub enum AccountCommands {
/// Create a new mnemonic - note, this account does not appear on the chain until the account id is used in a transaction
Create(crate::validator::account::create::Args),
/// Gets the balance of an account
Balance(crate::validator::account::balance::Args),
/// Gets the public key of an account
PubKey(crate::validator::account::pubkey::Args),
/// Sends tokens to another account
Send(crate::validator::account::send::Args),
}
@@ -1,89 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use log::{error, info};
use validator_client::nymd::wallet::DirectSecp256k1HdWallet;
use validator_client::nymd::AccountId;
use crate::context::QueryClient;
use crate::utils::show_error;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(value_parser)]
#[clap(
help = "Optionally, show the public key for this account address, otherwise generate the account address from the mnemonic"
)]
pub address: Option<AccountId>,
#[clap(long)]
#[clap(help = "If set, get the public key from the mnemonic, rather than querying for it")]
pub from_mnemonic: bool,
}
pub async fn get_pubkey(
args: Args,
client: &QueryClient,
mnemonic: Option<bip39::Mnemonic>,
address_from_mnemonic: Option<AccountId>,
) {
if args.address.is_none() && address_from_mnemonic.is_none() {
error!("Please specify an account address or a mnemonic to get the balance for");
return;
}
let address = args
.address
.unwrap_or_else(|| address_from_mnemonic.expect("please provide a mnemonic"));
if args.from_mnemonic {
let prefix = client
.current_chain_details()
.bech32_account_prefix
.as_str();
get_pubkey_from_mnemonic(address, prefix, mnemonic.expect("mnemonic not set"));
return;
}
get_pubkey_from_chain(address, client).await;
}
pub fn get_pubkey_from_mnemonic(address: AccountId, prefix: &str, mnemonic: bip39::Mnemonic) {
match DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic) {
Ok(wallet) => match wallet.try_derive_accounts() {
Ok(accounts) => match accounts.iter().find(|a| *a.address() == address) {
Some(account) => {
println!("{}", account.public_key().to_string());
}
None => {
error!("Could not derive key that matches {}", address)
}
},
Err(e) => {
error!("Failed to derive accounts. {}", e);
}
},
Err(e) => show_error(e),
}
}
pub async fn get_pubkey_from_chain(address: AccountId, client: &QueryClient) {
info!("Getting public key for address {} from chain...", address);
match client.get_account_details(&address).await {
Ok(Some(account)) => {
if let Ok(base_account) = account.try_get_base_account() {
if let Some(pubkey) = base_account.pubkey {
println!("{}", pubkey.to_string());
} else {
println!("No account associated with address {}", address);
}
}
}
Ok(None) => {
println!("No account associated with address {}", address);
}
Err(e) => show_error(e),
}
}
@@ -1,66 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use log::info;
use serde_json::json;
use validator_client::nymd::{AccountId, Coin};
use crate::context::SigningClient;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(value_parser, help = "The recipient account address")]
pub recipient: AccountId,
#[clap(
value_parser,
help = "Amount to transfer in micro denomination (e.g. unym or unyx)"
)]
pub amount: u128,
#[clap(long, help = "Override the denomination")]
pub denom: Option<String>,
#[clap(long)]
pub memo: Option<String>,
}
pub async fn send(args: Args, client: &SigningClient) {
let memo = args
.memo
.unwrap_or_else(|| "Sending tokens with nym-cli".to_owned());
let denom = args
.denom
.unwrap_or_else(|| client.current_chain_details().mix_denom.base.clone());
let coin = Coin {
denom,
amount: args.amount,
};
info!(
"Sending {} {} from {} to {}...",
coin.amount,
coin.denom,
client.address(),
args.recipient
);
let res = client
.send(&args.recipient, vec![coin], memo, None)
.await
.expect("failed to send tokens!");
info!("Sending result: {}", json!(res));
println!();
println!(
"Nodesguru: https://nym.explorers.guru/transaction/{}",
&res.hash
);
println!("Mintscan: https://www.mintscan.io/nyx/txs/{}", &res.hash);
println!("Transaction result code: {}", &res.tx_result.code.value());
println!("Transaction hash: {}", &res.hash);
}
@@ -1,23 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use crate::context::QueryClient;
use crate::utils::show_error;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(value_parser)]
#[clap(help = "The block height")]
pub height: u32,
}
pub async fn query_for_block_time(args: Args, client: &QueryClient) {
match client.get_block_timestamp(Some(args.height)).await {
Ok(res) => {
println!("{}", res.to_rfc3339())
}
Err(e) => show_error(e),
}
}
@@ -1,19 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use crate::context::QueryClient;
use crate::utils::show_error;
#[derive(Debug, Parser)]
pub struct Args {}
pub async fn query_current_block_height(client: &QueryClient) {
match client.get_current_block_height().await {
Ok(res) => {
println!("Current block height:\n{}", res.value())
}
Err(e) => show_error(e),
}
}
@@ -1,24 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use crate::context::QueryClient;
use crate::utils::show_error;
use serde_json::json;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(value_parser)]
#[clap(help = "The block height")]
pub height: u32,
}
pub async fn query_for_block(args: Args, client: &QueryClient) {
match client.get_block(Some(args.height)).await {
Ok(res) => {
println!("{}", json!(res))
}
Err(e) => show_error(e),
}
}
@@ -1,25 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod block_time;
pub mod current_height;
pub mod get;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct Block {
#[clap(subcommand)]
pub command: Option<BlockCommands>,
}
#[derive(Debug, Subcommand)]
pub enum BlockCommands {
/// Gets a block's details and prints as JSON
Get(crate::validator::block::get::Args),
/// Gets the block time at a height
Time(crate::validator::block::block_time::Args),
/// Gets the current block height
CurrentHeight(crate::validator::block::current_height::Args),
}
@@ -1,60 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use cosmrs::AccountId;
use log::{error, info};
use serde_json::{json, Value};
use validator_client::nymd::Coin;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(value_parser)]
#[clap(help = "The address of contract to execute")]
pub contract_address: AccountId,
#[clap(value_parser)]
#[clap(help = "JSON encoded method arguments")]
pub json_args: String,
#[clap(long)]
pub memo: Option<String>,
#[clap(
value_parser,
requires = "fundsDenom",
help = "Amount to supply as funds in micro denomination (e.g. unym or unyx)"
)]
pub funds: Option<u128>,
#[clap(long, requires = "funds", help = "Set the denomination for the funds")]
pub funds_denom: Option<String>,
}
pub async fn execute(args: Args, client: SigningClient) {
info!("Starting contract method execution!");
let json_args: Value =
serde_json::from_str(&args.json_args).expect("Unable to parse JSON args");
let memo = args
.memo
.unwrap_or_else(|| "nym-cli execute contract method".to_owned());
let funds = match args.funds {
Some(funds) => vec![Coin::new(
funds,
args.funds_denom.expect("denom for funds not set"),
)],
None => vec![],
};
match client
.execute(&args.contract_address, &json_args, None, memo, funds)
.await
{
Ok(res) => info!("SUCCESS ✅\n{}", json!(res)),
Err(e) => error!("FAILURE ❌\n{}", e),
}
}
@@ -1,78 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use cosmrs::{AccountId, Coin as CosmosCoin};
use log::info;
use network_defaults::NymNetworkDetails;
use validator_client::nymd::cosmwasm_client::types::{ContractCodeId, InstantiateOptions};
use validator_client::nymd::Coin;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(value_parser)]
pub code_id: ContractCodeId,
#[clap(long)]
pub memo: Option<String>,
#[clap(long)]
pub label: Option<String>,
#[clap(long)]
pub init_message: String,
#[clap(long)]
pub admin: Option<AccountId>,
#[clap(
long,
requires = "fundsDenom",
help = "Amount to supply as funds in micro denomination (e.g. unym or unyx)"
)]
pub funds: Option<u128>,
#[clap(long, requires = "funds", help = "Set the denomination for the funds")]
pub funds_denom: Option<String>,
}
pub async fn init(args: Args, client: SigningClient, network_details: &NymNetworkDetails) {
info!("Starting contract instantiation!");
let memo = args
.memo
.unwrap_or_else(|| "contract instantiation".to_owned());
let label = args
.label
.unwrap_or_else(|| "Nym mixnet smart contract".to_owned());
let funds: Vec<CosmosCoin> = match args.funds {
Some(funds) => vec![Coin::new(
funds,
args.funds_denom
.unwrap_or_else(|| network_details.chain_details.mix_denom.base.to_string()),
)
.into()],
None => vec![],
};
// by default we make ourselves an admin, let me know if you don't like that behaviour
let opts = Some(InstantiateOptions {
funds,
admin: Some(args.admin.unwrap_or_else(|| client.address().clone())),
});
let msg: serde_json::Value =
serde_json::from_str(&args.init_message).expect("failed to parse init message");
// the EmptyMsg{} argument is equivalent to `--init-message='{}'`
let res = client
.instantiate(args.code_id, &msg, label, memo, opts, None)
.await
.expect("failed to instantiate the contract!");
info!("Init result: {:?}", res);
println!("{}", res.contract_address)
}
@@ -1,51 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use crate::utils::show_error_passthrough;
use clap::Parser;
use cosmrs::AccountId;
use log::info;
use validator_client::nymd::cosmwasm_client::types::{ContractCodeId, EmptyMsg};
#[derive(Debug, Parser)]
pub struct Args {
#[clap(value_parser)]
pub contract_address: AccountId,
#[clap(long)]
pub code_id: ContractCodeId,
#[clap(long)]
pub memo: Option<String>,
#[clap(long)]
pub init_message: Option<String>,
}
pub async fn migrate(args: Args, client: SigningClient) {
println!("Starting contract migration!");
let memo = args.memo.unwrap_or_else(|| "contract migration".to_owned());
let contract_address = args.contract_address;
// the EmptyMsg{} argument is equivalent to `--init-message='{}'`
let res = if let Some(raw_msg) = args.init_message {
let msg: serde_json::Value =
serde_json::from_str(&raw_msg).expect("failed to parse init message");
client
.migrate(&contract_address, args.code_id, &msg, memo, None)
.await
.map_err(show_error_passthrough)
.expect("failed to migrate the contract!")
} else {
client
.migrate(&contract_address, args.code_id, &EmptyMsg {}, memo, None)
.await
.map_err(show_error_passthrough)
.expect("failed to migrate the contract!")
};
info!("Migrate result: {:?}", res);
}
@@ -1,28 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod execute_contract;
pub mod init_contract;
pub mod migrate_contract;
pub mod upload_contract;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct Cosmwasm {
#[clap(subcommand)]
pub command: Option<CosmwasmCommands>,
}
#[derive(Debug, Subcommand)]
pub enum CosmwasmCommands {
/// Upload a smart contract WASM blob
Upload(crate::validator::cosmwasm::upload_contract::Args),
/// Init a WASM smart contract
Init(crate::validator::cosmwasm::init_contract::Args),
/// Migrate a WASM smart contract
Migrate(crate::validator::cosmwasm::migrate_contract::Args),
/// Execute a WASM smart contract method
Execute(crate::validator::cosmwasm::execute_contract::Args),
}
@@ -1,37 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
use std::io::Read;
use std::path::PathBuf;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub wasm_path: PathBuf,
#[clap(long)]
pub memo: Option<String>,
}
pub async fn upload(args: Args, client: SigningClient) {
info!("Starting contract upload!");
let mut file = std::fs::File::open(args.wasm_path).expect("failed to open the wasm blob");
let mut data = Vec::new();
file.read_to_end(&mut data).unwrap();
let memo = args.memo.unwrap_or_else(|| "contract upload".to_owned());
let res = client
.upload(data, memo, None)
.await
.expect("failed to upload the contract!");
info!("Upload result: {:?}", res);
println!("{}", res.code_id)
}
@@ -1,31 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
use mixnet_contract_common::Coin;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub identity_key: String,
#[clap(long)]
pub amount: u128,
}
pub async fn delegate_to_mixnode(args: Args, client: SigningClient) {
let denom = client.current_chain_details().mix_denom.base.as_str();
info!("Starting delegation to mixnode");
let coin = Coin::new(args.amount, denom);
let res = client
.delegate_to_mixnode(&*args.identity_key, coin.into(), None)
.await
.expect("failed to delegate to mixnode!");
info!("delegating to mixnode: {:?}", res);
}
@@ -1,35 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod rewards;
pub mod delegate_to_mixnode;
pub mod query_for_delegations;
pub mod undelegate_from_mixnode;
pub mod vesting_delegate_to_mixnode;
pub mod vesting_undelegate_from_mixnode;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct MixnetDelegators {
#[clap(subcommand)]
pub command: MixnetDelegatorsCommands,
}
#[derive(Debug, Subcommand)]
pub enum MixnetDelegatorsCommands {
/// Lists current delegations
List(query_for_delegations::Args),
/// Manage rewards from delegations
Rewards(rewards::MixnetDelegatorsReward),
/// Delegate to a mixnode
Delegate(delegate_to_mixnode::Args),
/// Undelegate from a mixnode
Undelegate(undelegate_from_mixnode::Args),
/// Delegate to a mixnode with locked tokens
DelegateVesting(vesting_delegate_to_mixnode::Args),
/// Undelegate from a mixnode (when originally using locked tokens)
UndelegateVesting(vesting_undelegate_from_mixnode::Args),
}
@@ -1,129 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use log::info;
use crate::context::SigningClientWithValidatorAPI;
use crate::utils::{pretty_cosmwasm_coin, show_error_passthrough};
use comfy_table::Table;
use mixnet_contract_common::mixnode::DelegationEvent;
use mixnet_contract_common::Delegation;
#[derive(Debug, Parser)]
pub struct Args {}
pub async fn execute(_args: Args, client: SigningClientWithValidatorAPI) {
info!(
"Getting delegations for account {}...",
client.nymd.address()
);
let delegations = client
.get_all_delegator_delegations(client.nymd.address())
.await
.map_err(show_error_passthrough);
let mixnet_contract_events = client
.nymd
.get_pending_delegation_events(client.nymd.address().to_string(), None)
.await
.map_err(show_error_passthrough);
let vesting_contract = client.nymd.vesting_contract_address();
let vesting_contract_events = client
.nymd
.get_pending_delegation_events(
client.nymd.address().to_string(),
Some(vesting_contract.to_string()),
)
.await
.map_err(show_error_passthrough);
if let Ok(res) = delegations {
println!();
if res.is_empty() {
println!("This account has not delegated any tokens to mixnodes");
} else {
println!("Delegations:");
print_delegations(res, &client).await;
}
}
if let Ok(res) = mixnet_contract_events {
if !res.is_empty() {
println!();
println!("Pending delegations (liquid tokens):");
print_delegation_events(res, &client).await;
}
}
if let Ok(res) = vesting_contract_events {
if !res.is_empty() {
println!();
println!("Pending delegations (locked tokens):");
print_delegation_events(res, &client).await;
}
}
}
async fn to_iso_timestamp(block_height: u32, client: &SigningClientWithValidatorAPI) -> String {
match client.nymd.get_block_timestamp(Some(block_height)).await {
Ok(res) => res.to_rfc3339(),
Err(_e) => "-".to_string(),
}
}
async fn print_delegations(delegations: Vec<Delegation>, client: &SigningClientWithValidatorAPI) {
let mut table = Table::new();
table.set_header(vec!["Timestamp", "Identity Key", "Delegation", "Proxy"]);
for delegation in delegations {
table.add_row(vec![
to_iso_timestamp(delegation.block_height as u32, client).await,
delegation.node_identity.to_string(),
pretty_cosmwasm_coin(&delegation.amount),
format!("{:?}", delegation.proxy),
]);
}
println!("{table}");
}
async fn print_delegation_events(
events: Vec<DelegationEvent>,
client: &SigningClientWithValidatorAPI,
) {
let mut table = Table::new();
table.set_header(vec![
"Timestamp",
"Identity Key",
"Delegation",
"Event Type",
]);
for event in events {
match event {
DelegationEvent::Delegate(delegation) => {
table.add_row(vec![
to_iso_timestamp(delegation.block_height as u32, client).await,
delegation.node_identity.to_string(),
pretty_cosmwasm_coin(&delegation.amount),
"Delegate".to_string(),
]);
}
DelegationEvent::Undelegate(undelegate) => {
table.add_row(vec![
to_iso_timestamp(undelegate.block_height() as u32, client).await,
undelegate.mix_identity().to_string(),
"-".to_string(),
"Undelegate".to_string(),
]);
}
}
}
println!("{table}");
}
@@ -1,23 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub identity_key: String,
}
pub async fn claim_delegator_reward(args: Args, client: SigningClient) {
info!("Claim delegator reward");
let res = client
.execute_claim_delegator_reward(args.identity_key, None)
.await
.expect("failed to claim delegator-reward");
info!("Claiming delegator reward: {:?}", res)
}
@@ -1,22 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod claim_delegator_reward;
pub mod vesting_claim_delegator_reward;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct MixnetDelegatorsReward {
#[clap(subcommand)]
pub command: MixnetDelegatorsRewardCommands,
}
#[derive(Debug, Subcommand)]
pub enum MixnetDelegatorsRewardCommands {
/// Claim rewards accumulated during the delegation of unlocked tokens
Claim(claim_delegator_reward::Args),
/// Claim rewards accumulated during the delegation of locked tokens
VestingClaim(vesting_claim_delegator_reward::Args),
}
@@ -1,23 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub identity: String,
}
pub async fn vesting_claim_delegator_reward(args: Args, client: SigningClient) {
info!("Claim vesting delegator reward");
let res = client
.execute_vesting_claim_delegator_reward(args.identity, None)
.await
.expect("failed to claim vesting delegator-reward");
info!("Claiming vesting delegator reward: {:?}", res)
}
@@ -1,23 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub identity_key: String,
}
pub async fn undelegate_from_mixnode(args: Args, client: SigningClient) {
info!("removing stake from mix-node");
let res = client
.remove_mixnode_delegation(&*args.identity_key, None)
.await
.expect("failed to remove stake from mixnode!");
info!("removing stake from mixnode: {:?}", res)
}
@@ -1,34 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use log::info;
use mixnet_contract_common::Coin;
use validator_client::nymd::VestingSigningClient;
use crate::context::SigningClient;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub identity_key: String,
#[clap(long)]
pub amount: u128,
}
pub async fn vesting_delegate_to_mixnode(args: Args, client: SigningClient) {
let denom = client.current_chain_details().mix_denom.base.as_str();
info!("Starting vesting delegation to mixnode");
let coin = Coin::new(args.amount, denom);
let res = client
.vesting_delegate_to_mixnode(&*args.identity_key, coin.into(), None)
.await
.expect("failed to delegate to mixnode!");
info!("vesting delegating to mixnode: {:?}", res);
}
@@ -1,26 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use log::info;
use validator_client::nymd::VestingSigningClient;
use crate::context::SigningClient;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub identity_key: String,
}
pub async fn vesting_undelegate_from_mixnode(args: Args, client: SigningClient) {
info!("removing stake from vesting mix-node");
let res = client
.vesting_undelegate_from_mixnode(&*args.identity_key, None)
.await
.expect("failed to remove stake from vesting account on mixnode!");
info!("removing stake from vesting mixnode: {:?}", res)
}
@@ -1,25 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod delegators;
pub mod operators;
pub mod query;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct Mixnet {
#[clap(subcommand)]
pub command: MixnetCommands,
}
#[derive(Debug, Subcommand)]
pub enum MixnetCommands {
/// Query the mixnet directory
Query(query::MixnetQuery),
/// Manage your delegations
Delegators(delegators::MixnetDelegators),
/// Manage a mixnode or gateway you operate
Operators(operators::MixnetOperators),
}
@@ -1,77 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::{info, warn};
use mixnet_contract_common::Coin;
use network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT};
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub host: String,
#[clap(long)]
pub signature: String,
#[clap(long)]
pub mix_port: Option<u16>,
#[clap(long)]
pub clients_port: Option<u16>,
#[clap(long)]
pub location: Option<String>,
#[clap(long)]
pub sphinx_key: String,
#[clap(long)]
pub identity_key: String,
#[clap(long)]
pub version: String,
#[clap(
long,
help = "bonding amount in current DENOMINATION (so it would be 'unym', rather than 'nym')"
)]
pub amount: u128,
#[clap(short, long)]
pub force: bool,
}
pub async fn bond_gateway(args: Args, client: SigningClient) {
let denom = client.current_chain_details().mix_denom.base.as_str();
info!("Starting gateway bonding!");
// if we're trying to bond less than 1 token
if args.amount < 1_000_000 && !args.force {
warn!("You're trying to bond only {}{} which is less than 1 full token. Are you sure that's what you want? If so, run with `--force` or `-f` flag", args.amount, denom);
return;
}
let gateway = mixnet_contract_common::Gateway {
host: args.host,
mix_port: args.mix_port.unwrap_or(DEFAULT_MIX_LISTENING_PORT),
clients_port: args.clients_port.unwrap_or(DEFAULT_CLIENT_LISTENING_PORT),
location: args
.location
.unwrap_or_else(|| "secret gateway location".to_owned()),
sphinx_key: args.sphinx_key,
identity_key: args.identity_key,
version: args.version,
};
let coin = Coin::new(args.amount, denom);
let res = client
.bond_gateway(gateway, args.signature, coin.into(), None)
.await
.expect("failed to bond gateway!");
info!("Bonding result: {:?}", res)
}
@@ -1,28 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod bond_gateway;
pub mod unbond_gateway;
pub mod vesting_bond_gateway;
pub mod vesting_unbond_gateway;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct MixnetOperatorsGateway {
#[clap(subcommand)]
pub command: MixnetOperatorsGatewayCommands,
}
#[derive(Debug, Subcommand)]
pub enum MixnetOperatorsGatewayCommands {
/// Bond to a gateway
Bond(bond_gateway::Args),
/// Unbound from a gateway
Unbound(unbond_gateway::Args),
/// Bond to a gateway with locked tokens
VestingBond(vesting_bond_gateway::Args),
/// Unbound from a gateway (when originally using locked tokens)
VestingUnbound(vesting_unbond_gateway::Args),
}
@@ -1,20 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
#[derive(Debug, Parser)]
pub struct Args {}
pub async fn unbond_gateway(client: SigningClient) {
info!("Starting gateway unbonding!");
let res = client
.unbond_gateway(None)
.await
.expect("failed to unbond gateway!");
info!("Unbonding result: {:?}", res)
}
@@ -1,76 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::{info, warn};
use mixnet_contract_common::{Coin, Gateway};
use network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT};
use validator_client::nymd::VestingSigningClient;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub host: String,
#[clap(long)]
pub signature: String,
#[clap(long)]
pub mix_port: Option<u16>,
#[clap(long)]
pub clients_port: Option<u16>,
#[clap(long)]
pub location: Option<String>,
#[clap(long)]
pub sphinx_key: String,
#[clap(long)]
pub identity_key: String,
#[clap(long)]
pub version: String,
#[clap(
long,
help = "bonding amount in current DENOMINATION (so it would be 'unym', rather than 'nym')"
)]
pub amount: u128,
#[clap(short, long)]
pub force: bool,
}
pub async fn vesting_bond_gateway(client: SigningClient, args: Args, denom: &str) {
info!("Starting vesting gateway bonding!");
// if we're trying to bond less than 1 token
if args.amount < 1_000_000 && !args.force {
warn!("You're trying to bond only {}{} which is less than 1 full token. Are you sure that's what you want? If so, run with `--force` or `-f` flag", args.amount, denom);
return;
}
let gateway = Gateway {
host: args.host,
mix_port: args.mix_port.unwrap_or(DEFAULT_MIX_LISTENING_PORT),
clients_port: args.clients_port.unwrap_or(DEFAULT_CLIENT_LISTENING_PORT),
location: args
.location
.unwrap_or_else(|| "secret gateway location".to_owned()),
sphinx_key: args.sphinx_key,
identity_key: args.identity_key,
version: args.version,
};
let coin = Coin::new(args.amount, denom);
let res = client
.vesting_bond_gateway(gateway, &*args.signature, coin.into(), None)
.await
.expect("failed to bond gateway!");
info!("Vesting bonding gateway result: {:?}", res)
}
@@ -1,20 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
#[derive(Debug, Parser)]
pub struct Args {}
pub async fn vesting_unbond_gateway(client: SigningClient) {
info!("Starting vesting gateway unbonding!");
let res = client
.unbond_gateway(None)
.await
.expect("failed to unbond vesting gateway!");
info!("Unbonding vesting result: {:?}", res)
}
@@ -1,85 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use log::{info, warn};
use mixnet_contract_common::Coin;
use network_defaults::{
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
};
use crate::context::SigningClient;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub host: String,
#[clap(long)]
pub signature: String,
#[clap(long)]
pub mix_port: Option<u16>,
#[clap(long)]
pub verloc_port: Option<u16>,
#[clap(long)]
pub http_api_port: Option<u16>,
#[clap(long)]
pub sphinx_key: String,
#[clap(long)]
pub identity_key: String,
#[clap(long)]
pub version: String,
#[clap(long)]
pub profit_margin_percent: Option<u8>,
#[clap(
long,
help = "bonding amount in current DENOMINATION (so it would be 'unym', rather than 'nym')"
)]
pub amount: u128,
#[clap(short, long)]
pub force: bool,
}
pub async fn bond_mixnode(args: Args, client: SigningClient) {
let denom = client.current_chain_details().mix_denom.base.as_str();
info!("Starting mixnode bonding!");
// if we're trying to bond less than 1 token
if args.amount < 1_000_000 && !args.force {
warn!("You're trying to bond only {}{} which is less than 1 full token. Are you sure that's what you want? If so, run with `--force` or `-f` flag", args.amount, denom);
return;
}
let mixnode = mixnet_contract_common::MixNode {
host: args.host,
mix_port: args.mix_port.unwrap_or(DEFAULT_MIX_LISTENING_PORT),
verloc_port: args.verloc_port.unwrap_or(DEFAULT_VERLOC_LISTENING_PORT),
http_api_port: args
.http_api_port
.unwrap_or(DEFAULT_HTTP_API_LISTENING_PORT),
sphinx_key: args.sphinx_key,
identity_key: args.identity_key,
version: args.version,
profit_margin_percent: args.profit_margin_percent.unwrap_or(10),
};
let coin = Coin::new(args.amount, denom);
let res = client
.bond_mixnode(mixnode, args.signature, coin.into(), None)
.await
.expect("failed to bond mixnode!");
info!("Bonding result: {:?}", res)
}
@@ -1,17 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(short, long)]
pub key: String,
}
pub fn decode_mixnode_key(args: Args) {
let b64_decoded = base64::decode(args.key).expect("failed to decode base64 string");
let b58_encoded = bs58::encode(&b64_decoded).into_string();
println!("{}", b58_encoded)
}
@@ -1,19 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod decode_mixnode_key;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct MixnetOperatorsMixnodeKeys {
#[clap(subcommand)]
pub command: MixnetOperatorsMixnodeKeysCommands,
}
#[derive(Debug, Subcommand)]
pub enum MixnetOperatorsMixnodeKeysCommands {
/// Decode a mixnode key
DecodeMixnodeKey(decode_mixnode_key::Args),
}
@@ -1,37 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod bond_mixnode;
pub mod keys;
pub mod rewards;
pub mod settings;
pub mod unbond_mixnode;
pub mod vesting_bond_mixnode;
pub mod vesting_unbond_mixnode;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct MixnetOperatorsMixnode {
#[clap(subcommand)]
pub command: MixnetOperatorsMixnodeCommands,
}
#[derive(Debug, Subcommand)]
pub enum MixnetOperatorsMixnodeCommands {
/// Operations for mixnode keys
Keys(keys::MixnetOperatorsMixnodeKeys),
/// Manage your mixnode operator rewards
Rewards(rewards::MixnetOperatorsMixnodeRewards),
/// Manage your mixnode settings stored in the directory
Settings(settings::MixnetOperatorsMixnodeSettings),
/// Bond to a mixnode
Bond(bond_mixnode::Args),
/// Unbound from a mixnode
Unbound(unbond_mixnode::Args),
/// Bond to a mixnode with locked tokens
BondVesting(vesting_bond_mixnode::Args),
/// Unbound from a mixnode (when originally using locked tokens)
UnboundVesting(vesting_unbond_mixnode::Args),
}
@@ -1,20 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
#[derive(Debug, Parser)]
pub struct Args {}
pub async fn claim_operator_reward(_args: Args, client: SigningClient) {
info!("Claim operator reward");
let res = client
.execute_claim_operator_reward(None)
.await
.expect("failed to claim operator reward");
info!("Claiming operator reward: {:?}", res)
}
@@ -1,22 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod claim_operator_reward;
pub mod vesting_claim_operator_reward;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct MixnetOperatorsMixnodeRewards {
#[clap(subcommand)]
pub command: MixnetOperatorsMixnodeRewardsCommands,
}
#[derive(Debug, Subcommand)]
pub enum MixnetOperatorsMixnodeRewardsCommands {
/// Claim rewards
Claim(claim_operator_reward::Args),
/// Claim rewards for a mixnode bonded with locked tokens
VestingClaim(vesting_claim_operator_reward::Args),
}
@@ -1,23 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub gas: Option<u64>,
}
pub async fn vesting_claim_operator_reward(client: SigningClient) {
info!("Claim vesting operator reward");
let res = client
.execute_vesting_claim_operator_reward(None)
.await
.expect("failed to claim vesting operator reward");
info!("Claiming vesting operator reward: {:?}", res)
}

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