Merge branch 'develop' into feature/test-my-node
This commit is contained in:
@@ -44,6 +44,12 @@ 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:
|
||||
@@ -101,6 +107,12 @@ jobs:
|
||||
command: build
|
||||
args: --workspace --features=coconut
|
||||
|
||||
- name: Reclaim some disk space (because Windows is being annoying)
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
- name: Run all tests with coconut enabled
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
|
||||
@@ -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_NYMTECH_TEAM }}"
|
||||
KEYBASE_NYM_CHANNEL: "test"
|
||||
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
|
||||
KEYBASE_NYM_CHANNEL: "ci-nightly"
|
||||
IS_SUCCESS: "${{ env.WORKFLOW_CONCLUSION == 'success' }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
name: Notification 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_NYMTECH_TEAM }}"
|
||||
KEYBASE_NYM_CHANNEL: "test"
|
||||
WORKFLOW_CONCLUSION: "failure"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
@@ -89,7 +89,7 @@ async function sendKeybaseMessage(messageBody) {
|
||||
});
|
||||
|
||||
const channel = {
|
||||
name: 'nymtech_bot',
|
||||
name: context.env.KEYBASE_NYMBOT_TEAM || 'nymtech_bot',
|
||||
membersType: 'team',
|
||||
topicName: context.keybase.channel,
|
||||
topic_type: 'CHAT',
|
||||
|
||||
@@ -8,14 +8,17 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
- 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])
|
||||
- network-requester: added additional Blockstream Green wallet endpoint to `example.allowed.list` ([#1611](https://github.com/nymtech/nym/pull/1611))
|
||||
|
||||
### 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)
|
||||
|
||||
Generated
+10
@@ -667,7 +667,10 @@ dependencies = [
|
||||
"rand 0.7.3",
|
||||
"serde",
|
||||
"sled",
|
||||
"tap",
|
||||
"task",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"topology",
|
||||
"url",
|
||||
@@ -2052,8 +2055,10 @@ dependencies = [
|
||||
"pemstore",
|
||||
"rand 0.7.3",
|
||||
"secp256k1",
|
||||
"task",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tokio-tungstenite 0.14.0",
|
||||
"tungstenite 0.13.0",
|
||||
"url",
|
||||
@@ -3253,6 +3258,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sled",
|
||||
"task",
|
||||
"tokio",
|
||||
"tokio-tungstenite 0.14.0",
|
||||
"topology",
|
||||
@@ -3373,6 +3379,7 @@ dependencies = [
|
||||
"socks5-requests",
|
||||
"sqlx 0.6.1",
|
||||
"statistics-common",
|
||||
"task",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-tungstenite 0.17.2",
|
||||
@@ -3422,6 +3429,7 @@ dependencies = [
|
||||
"serde",
|
||||
"snafu",
|
||||
"socks5-requests",
|
||||
"task",
|
||||
"tokio",
|
||||
"topology",
|
||||
"url",
|
||||
@@ -4256,6 +4264,7 @@ dependencies = [
|
||||
"log",
|
||||
"ordered-buffer",
|
||||
"socks5-requests",
|
||||
"task",
|
||||
"tokio",
|
||||
"tokio-test",
|
||||
"tokio-util 0.7.3",
|
||||
@@ -6136,6 +6145,7 @@ dependencies = [
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tokio-util 0.7.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -14,6 +14,7 @@ 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"] }
|
||||
|
||||
@@ -25,8 +26,10 @@ 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,6 +13,7 @@ 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;
|
||||
|
||||
@@ -48,6 +49,9 @@ where
|
||||
|
||||
/// Accessor to the common instance of network topology.
|
||||
topology_access: TopologyAccessor,
|
||||
|
||||
/// Listen to shutdown signals.
|
||||
shutdown: ShutdownListener,
|
||||
}
|
||||
|
||||
impl<R> Stream for LoopCoverTrafficStream<R>
|
||||
@@ -84,6 +88,7 @@ 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,
|
||||
@@ -92,6 +97,7 @@ impl LoopCoverTrafficStream<OsRng> {
|
||||
mix_tx: BatchMixMessageSender,
|
||||
our_full_destination: Recipient,
|
||||
topology_access: TopologyAccessor,
|
||||
shutdown: ShutdownListener,
|
||||
) -> Self {
|
||||
let rng = OsRng;
|
||||
|
||||
@@ -105,6 +111,7 @@ impl LoopCoverTrafficStream<OsRng> {
|
||||
our_full_destination,
|
||||
rng,
|
||||
topology_access,
|
||||
shutdown,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,9 +166,25 @@ impl LoopCoverTrafficStream<OsRng> {
|
||||
self.average_cover_message_sending_delay,
|
||||
)));
|
||||
|
||||
while self.next().await.is_some() {
|
||||
self.on_new_message().await;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert!(self.shutdown.is_shutdown_poll());
|
||||
log::debug!("LoopCoverTrafficStream: Exiting");
|
||||
}
|
||||
|
||||
pub fn start(mut self) -> JoinHandle<()> {
|
||||
|
||||
@@ -6,6 +6,7 @@ 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>>;
|
||||
@@ -18,6 +19,7 @@ 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.
|
||||
@@ -28,10 +30,12 @@ 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,
|
||||
}
|
||||
}
|
||||
@@ -66,9 +70,24 @@ impl MixTrafficController {
|
||||
}
|
||||
|
||||
pub async fn run(&mut self) {
|
||||
while let Some(mix_packets) = self.mix_rx.next().await {
|
||||
self.on_messages(mix_packets).await;
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
assert!(self.shutdown.is_shutdown_poll());
|
||||
log::debug!("MixTrafficController: Exiting");
|
||||
}
|
||||
|
||||
pub fn start(mut self) -> JoinHandle<()> {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
||||
pub mod cover_traffic_stream;
|
||||
pub mod inbound_messages;
|
||||
pub mod key_manager;
|
||||
@@ -6,3 +8,10 @@ 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);
|
||||
|
||||
+23
-5
@@ -10,6 +10,7 @@ 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.
|
||||
@@ -17,6 +18,7 @@ pub(super) struct AcknowledgementListener {
|
||||
ack_key: Arc<AckKey>,
|
||||
ack_receiver: AcknowledgementReceiver,
|
||||
action_sender: ActionSender,
|
||||
shutdown: ShutdownListener,
|
||||
}
|
||||
|
||||
impl AcknowledgementListener {
|
||||
@@ -24,11 +26,13 @@ impl AcknowledgementListener {
|
||||
ack_key: Arc<AckKey>,
|
||||
ack_receiver: AcknowledgementReceiver,
|
||||
action_sender: ActionSender,
|
||||
shutdown: ShutdownListener,
|
||||
) -> Self {
|
||||
AcknowledgementListener {
|
||||
ack_key,
|
||||
ack_receiver,
|
||||
action_sender,
|
||||
shutdown,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,12 +69,26 @@ impl AcknowledgementListener {
|
||||
|
||||
pub(super) async fn run(&mut self) {
|
||||
debug!("Started AcknowledgementListener");
|
||||
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;
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
error!("TODO: error msg. Or maybe panic?")
|
||||
assert!(self.shutdown.is_shutdown_poll());
|
||||
log::debug!("AcknowledgementListener: Exiting");
|
||||
}
|
||||
}
|
||||
|
||||
+28
-6
@@ -12,6 +12,7 @@ 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>;
|
||||
|
||||
@@ -99,12 +100,16 @@ 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();
|
||||
(
|
||||
@@ -114,6 +119,7 @@ impl ActionController {
|
||||
pending_acks_timers: NonExhaustiveDelayQueue::new(),
|
||||
incoming_actions: receiver,
|
||||
retransmission_sender,
|
||||
shutdown,
|
||||
},
|
||||
sender,
|
||||
)
|
||||
@@ -246,14 +252,30 @@ impl ActionController {
|
||||
}
|
||||
|
||||
pub(super) async fn run(&mut self) {
|
||||
loop {
|
||||
// at some point there will be a global shutdown signal here as the third option
|
||||
while !self.shutdown.is_shutdown() {
|
||||
tokio::select! {
|
||||
// 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())
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
assert!(self.shutdown.is_shutdown_poll());
|
||||
log::debug!("ActionController: Exiting");
|
||||
}
|
||||
}
|
||||
|
||||
+21
-3
@@ -16,6 +16,7 @@ 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.
|
||||
@@ -32,6 +33,7 @@ where
|
||||
real_message_sender: BatchRealMessageSender,
|
||||
topology_access: TopologyAccessor,
|
||||
reply_key_storage: ReplyKeyStorage,
|
||||
shutdown: ShutdownListener,
|
||||
}
|
||||
|
||||
impl<R> InputMessageListener<R>
|
||||
@@ -50,6 +52,7 @@ where
|
||||
real_message_sender: BatchRealMessageSender,
|
||||
topology_access: TopologyAccessor,
|
||||
reply_key_storage: ReplyKeyStorage,
|
||||
shutdown: ShutdownListener,
|
||||
) -> Self {
|
||||
InputMessageListener {
|
||||
ack_key,
|
||||
@@ -60,6 +63,7 @@ where
|
||||
real_message_sender,
|
||||
topology_access,
|
||||
reply_key_storage,
|
||||
shutdown,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,9 +186,23 @@ where
|
||||
|
||||
pub(super) async fn run(&mut self) {
|
||||
debug!("Started InputMessageListener");
|
||||
while let Some(input_msg) = self.input_receiver.next().await {
|
||||
self.on_input_message(input_msg).await;
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
error!("TODO: error msg. Or maybe panic?")
|
||||
assert!(self.shutdown.is_shutdown_poll());
|
||||
log::debug!("InputMessageListener: Exiting");
|
||||
}
|
||||
}
|
||||
|
||||
+13
-7
@@ -25,6 +25,7 @@ use std::{
|
||||
sync::{Arc, Weak},
|
||||
time::Duration,
|
||||
};
|
||||
use task::ShutdownListener;
|
||||
use tokio::task::JoinHandle;
|
||||
|
||||
mod acknowledgement_listener;
|
||||
@@ -152,6 +153,7 @@ impl<R> AcknowledgementController<R>
|
||||
where
|
||||
R: 'static + CryptoRng + Rng + Clone + Send,
|
||||
{
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(super) fn new(
|
||||
config: Config,
|
||||
rng: R,
|
||||
@@ -160,13 +162,14 @@ 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);
|
||||
ActionController::new(action_config, retransmission_tx, shutdown.clone());
|
||||
|
||||
let message_preparer = MessagePreparer::new(
|
||||
rng,
|
||||
@@ -180,6 +183,7 @@ where
|
||||
Arc::clone(&ack_key),
|
||||
connectors.ack_receiver,
|
||||
action_sender.clone(),
|
||||
shutdown.clone(),
|
||||
);
|
||||
|
||||
// will listen for any new messages from the client
|
||||
@@ -192,6 +196,7 @@ where
|
||||
connectors.real_message_sender.clone(),
|
||||
topology_access.clone(),
|
||||
reply_key_storage,
|
||||
shutdown.clone(),
|
||||
);
|
||||
|
||||
// will listen for any ack timeouts and trigger retransmission
|
||||
@@ -203,12 +208,13 @@ 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);
|
||||
SentNotificationListener::new(connectors.sent_notifier, action_sender, shutdown);
|
||||
|
||||
AcknowledgementController {
|
||||
acknowledgement_listener: Some(acknowledgement_listener),
|
||||
@@ -232,27 +238,27 @@ where
|
||||
// graceful shutdowns.
|
||||
let ack_listener_fut = tokio::spawn(async move {
|
||||
acknowledgement_listener.run().await;
|
||||
error!("The acknowledgement listener has finished execution!");
|
||||
debug!("The acknowledgement listener has finished execution!");
|
||||
acknowledgement_listener
|
||||
});
|
||||
let input_listener_fut = tokio::spawn(async move {
|
||||
input_message_listener.run().await;
|
||||
error!("The input listener has finished execution!");
|
||||
debug!("The input listener has finished execution!");
|
||||
input_message_listener
|
||||
});
|
||||
let retransmission_req_fut = tokio::spawn(async move {
|
||||
retransmission_request_listener.run().await;
|
||||
error!("The retransmission request listener has finished execution!");
|
||||
debug!("The retransmission request listener has finished execution!");
|
||||
retransmission_request_listener
|
||||
});
|
||||
let sent_notification_fut = tokio::spawn(async move {
|
||||
sent_notification_listener.run().await;
|
||||
error!("The sent notification listener has finished execution!");
|
||||
debug!("The sent notification listener has finished execution!");
|
||||
sent_notification_listener
|
||||
});
|
||||
let action_controller_fut = tokio::spawn(async move {
|
||||
action_controller.run().await;
|
||||
error!("The controller has finished execution!");
|
||||
debug!("The controller has finished execution!");
|
||||
action_controller
|
||||
});
|
||||
|
||||
|
||||
+21
-3
@@ -14,6 +14,7 @@ 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>
|
||||
@@ -27,12 +28,14 @@ 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,
|
||||
@@ -41,6 +44,7 @@ where
|
||||
real_message_sender: BatchRealMessageSender,
|
||||
request_receiver: RetransmissionRequestReceiver,
|
||||
topology_access: TopologyAccessor,
|
||||
shutdown: ShutdownListener,
|
||||
) -> Self {
|
||||
RetransmissionRequestListener {
|
||||
ack_key,
|
||||
@@ -50,6 +54,7 @@ where
|
||||
real_message_sender,
|
||||
request_receiver,
|
||||
topology_access,
|
||||
shutdown,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,9 +126,22 @@ where
|
||||
|
||||
pub(super) async fn run(&mut self) {
|
||||
debug!("Started RetransmissionRequestListener");
|
||||
while let Some(timed_out_ack) = self.request_receiver.next().await {
|
||||
self.on_retransmission_request(timed_out_ack).await;
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
error!("TODO: error msg. Or maybe panic?")
|
||||
assert!(self.shutdown.is_shutdown_poll());
|
||||
log::debug!("RetransmissionRequestListener: Exiting");
|
||||
}
|
||||
}
|
||||
|
||||
+21
-3
@@ -6,6 +6,7 @@ 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
|
||||
@@ -14,16 +15,19 @@ use nymsphinx::chunking::fragment::{FragmentIdentifier, COVER_FRAG_ID};
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,9 +48,23 @@ impl SentNotificationListener {
|
||||
|
||||
pub(super) async fn run(&mut self) {
|
||||
debug!("Started SentNotificationListener");
|
||||
while let Some(frag_id) = self.sent_notifier.next().await {
|
||||
self.on_sent_message(frag_id).await;
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
error!("TODO: error msg. Or maybe panic?")
|
||||
assert!(self.shutdown.is_shutdown_poll());
|
||||
log::debug!("SentNotificationListener: Exiting");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ 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;
|
||||
@@ -91,6 +92,7 @@ impl RealMessagesController<OsRng> {
|
||||
mix_sender: BatchMixMessageSender,
|
||||
topology_access: TopologyAccessor,
|
||||
reply_key_storage: ReplyKeyStorage,
|
||||
shutdown: ShutdownListener,
|
||||
) -> Self {
|
||||
let rng = OsRng;
|
||||
|
||||
@@ -119,6 +121,7 @@ impl RealMessagesController<OsRng> {
|
||||
config.self_recipient,
|
||||
reply_key_storage,
|
||||
ack_controller_connectors,
|
||||
shutdown.clone(),
|
||||
);
|
||||
|
||||
let out_queue_config = real_traffic_stream::Config::new(
|
||||
@@ -136,6 +139,7 @@ impl RealMessagesController<OsRng> {
|
||||
rng,
|
||||
config.self_recipient,
|
||||
topology_access,
|
||||
shutdown,
|
||||
);
|
||||
|
||||
RealMessagesController {
|
||||
@@ -153,12 +157,12 @@ impl RealMessagesController<OsRng> {
|
||||
// graceful shutdowns.
|
||||
let out_queue_control_fut = tokio::spawn(async move {
|
||||
out_queue_control.run_out_queue_control().await;
|
||||
error!("The out queue controller has finished execution!");
|
||||
debug!("The out queue controller has finished execution!");
|
||||
out_queue_control
|
||||
});
|
||||
let ack_control_fut = tokio::spawn(async move {
|
||||
ack_control.run().await;
|
||||
error!("The acknowledgement controller has finished execution!");
|
||||
debug!("The acknowledgement controller has finished execution!");
|
||||
ack_control
|
||||
});
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ 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`
|
||||
@@ -83,6 +84,9 @@ 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 {
|
||||
@@ -174,6 +178,7 @@ where
|
||||
rng: R,
|
||||
our_full_destination: Recipient,
|
||||
topology_access: TopologyAccessor,
|
||||
shutdown: ShutdownListener,
|
||||
) -> Self {
|
||||
OutQueueControl {
|
||||
config,
|
||||
@@ -186,6 +191,7 @@ where
|
||||
rng,
|
||||
topology_access,
|
||||
received_buffer: VecDeque::with_capacity(0), // we won't be putting any data into this guy directly
|
||||
shutdown,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,7 +245,15 @@ where
|
||||
// - we run out of memory
|
||||
// - the receiver channel is closed
|
||||
// in either case there's no recovery and we can only panic
|
||||
self.mix_tx.unbounded_send(vec![next_message]).unwrap();
|
||||
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}");
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -257,9 +271,26 @@ where
|
||||
self.config.average_message_sending_delay,
|
||||
)));
|
||||
|
||||
while let Some(next_message) = self.next().await {
|
||||
self.on_message(next_message).await;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert!(shutdown.is_shutdown_poll());
|
||||
log::debug!("OutQueueControl: Exiting");
|
||||
}
|
||||
|
||||
pub(crate) async fn run_out_queue_control(&mut self) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// 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;
|
||||
@@ -14,7 +15,9 @@ 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"
|
||||
@@ -292,16 +295,27 @@ impl RequestReceiver {
|
||||
|
||||
fn start(mut self) -> JoinHandle<()> {
|
||||
tokio::spawn(async move {
|
||||
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
|
||||
}
|
||||
}
|
||||
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;
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
assert!(SHUTDOWN_HAS_BEEN_SIGNALLED.load(Ordering::Relaxed));
|
||||
log::debug!("RequestReceiver: Exiting");
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -309,23 +323,41 @@ 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 let Some(new_messages) = self.mixnet_packet_receiver.next().await {
|
||||
self.received_buffer.handle_new_received(new_messages).await;
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
assert!(self.shutdown.is_shutdown_poll());
|
||||
log::debug!("FragmentedMessageReceiver: Exiting");
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -341,6 +373,7 @@ 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);
|
||||
@@ -349,6 +382,7 @@ impl ReceivedMessagesBufferController {
|
||||
fragmented_message_receiver: FragmentedMessageReceiver::new(
|
||||
received_buffer.clone(),
|
||||
mixnet_packet_receiver,
|
||||
shutdown,
|
||||
),
|
||||
request_receiver: RequestReceiver::new(received_buffer, query_receiver),
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ 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};
|
||||
@@ -303,12 +304,20 @@ impl TopologyRefresher {
|
||||
self.topology_accessor.is_routable().await
|
||||
}
|
||||
|
||||
pub fn start(mut self) -> JoinHandle<()> {
|
||||
pub fn start(mut self, mut shutdown: ShutdownListener) -> JoinHandle<()> {
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
tokio::time::sleep(self.refresh_rate).await;
|
||||
self.refresh().await;
|
||||
while !shutdown.is_shutdown() {
|
||||
tokio::select! {
|
||||
_ = tokio::time::sleep(self.refresh_rate) => {
|
||||
self.refresh().await;
|
||||
},
|
||||
_ = shutdown.recv() => {
|
||||
log::trace!("TopologyRefresher: Received shutdown");
|
||||
},
|
||||
}
|
||||
}
|
||||
assert!(shutdown.is_shutdown_poll());
|
||||
log::debug!("TopologyRefresher: Exiting");
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// 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),
|
||||
}
|
||||
@@ -14,25 +14,27 @@ 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>,
|
||||
) -> gateway::Node {
|
||||
) -> Result<gateway::Node, ClientCoreError> {
|
||||
let validator_api = validator_servers
|
||||
.choose(&mut thread_rng())
|
||||
.expect("The list of validator apis is empty");
|
||||
.ok_or(ClientCoreError::ListOfValidatorApisIsEmpty)?;
|
||||
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.unwrap();
|
||||
let gateways = validator_client.get_cached_gateways().await?;
|
||||
let valid_gateways = gateways
|
||||
.into_iter()
|
||||
.filter_map(|gateway| gateway.try_into().ok())
|
||||
@@ -47,38 +49,40 @@ pub async fn query_gateway_details(
|
||||
filtered_gateways
|
||||
.iter()
|
||||
.find(|gateway| gateway.identity_key.to_base58_string() == gateway_id)
|
||||
.expect(&*format!("no gateway with id {} exists!", gateway_id))
|
||||
.clone()
|
||||
.ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_id.to_string()))
|
||||
.cloned()
|
||||
} else {
|
||||
filtered_gateways
|
||||
.choose(&mut rand::thread_rng())
|
||||
.expect("there are no gateways on the network!")
|
||||
.clone()
|
||||
.ok_or(ClientCoreError::NoGatewaysOnNetwork)
|
||||
.cloned()
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn register_with_gateway_and_store_keys<T>(
|
||||
gateway_details: gateway::Node,
|
||||
config: &Config<T>,
|
||||
) where
|
||||
) -> Result<(), ClientCoreError>
|
||||
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);
|
||||
key_manager
|
||||
Ok(key_manager
|
||||
.store_keys(&pathfinder)
|
||||
.expect("Failed to generated keys");
|
||||
.tap_err(|err| log::error!("Failed to generate keys: {err}"))?)
|
||||
}
|
||||
|
||||
async fn register_with_gateway(
|
||||
gateway: &gateway::Node,
|
||||
our_identity: Arc<identity::KeyPair>,
|
||||
) -> Arc<SharedKeys> {
|
||||
) -> Result<Arc<SharedKeys>, ClientCoreError> {
|
||||
let timeout = Duration::from_millis(1500);
|
||||
let mut gateway_client = GatewayClient::new_init(
|
||||
gateway.clients_address(),
|
||||
@@ -86,52 +90,59 @@ async fn register_with_gateway(
|
||||
gateway.owner.clone(),
|
||||
our_identity.clone(),
|
||||
timeout,
|
||||
None,
|
||||
);
|
||||
gateway_client
|
||||
.establish_connection()
|
||||
.await
|
||||
.expect("failed to establish connection with the gateway!");
|
||||
gateway_client
|
||||
.tap_err(|_| log::warn!("Failed to establish connection with gateway!"))?;
|
||||
let shared_keys = gateway_client
|
||||
.perform_initial_authentication()
|
||||
.await
|
||||
.expect("failed to register with the gateway!")
|
||||
.tap_err(|_| log::warn!("Failed to register with the gateway!"))?;
|
||||
Ok(shared_keys)
|
||||
}
|
||||
|
||||
pub fn show_address<T>(config: &Config<T>)
|
||||
pub fn show_address<T>(config: &Config<T>) -> Result<(), ClientCoreError>
|
||||
where
|
||||
T: config::NymConfig,
|
||||
{
|
||||
fn load_identity_keys(pathfinder: &ClientKeyPathfinder) -> identity::KeyPair {
|
||||
fn load_identity_keys(
|
||||
pathfinder: &ClientKeyPathfinder,
|
||||
) -> Result<identity::KeyPair, ClientCoreError> {
|
||||
let identity_keypair: identity::KeyPair =
|
||||
pemstore::load_keypair(&pemstore::KeyPairPath::new(
|
||||
pathfinder.private_identity_key().to_owned(),
|
||||
pathfinder.public_identity_key().to_owned(),
|
||||
))
|
||||
.expect("Failed to read stored identity key files");
|
||||
identity_keypair
|
||||
.tap_err(|_| log::error!("Failed to read stored identity key files"))?;
|
||||
Ok(identity_keypair)
|
||||
}
|
||||
|
||||
fn load_sphinx_keys(pathfinder: &ClientKeyPathfinder) -> encryption::KeyPair {
|
||||
fn load_sphinx_keys(
|
||||
pathfinder: &ClientKeyPathfinder,
|
||||
) -> Result<encryption::KeyPair, ClientCoreError> {
|
||||
let sphinx_keypair: encryption::KeyPair =
|
||||
pemstore::load_keypair(&pemstore::KeyPairPath::new(
|
||||
pathfinder.private_encryption_key().to_owned(),
|
||||
pathfinder.public_encryption_key().to_owned(),
|
||||
))
|
||||
.expect("Failed to read stored sphinx key files");
|
||||
sphinx_keypair
|
||||
.tap_err(|_| log::error!("Failed to read stored sphinx key files"))?;
|
||||
Ok(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()).unwrap(),
|
||||
NodeIdentity::from_base58_string(config.get_gateway_id())?,
|
||||
);
|
||||
|
||||
println!("\nThe address of this client is: {}", client_recipient);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod client;
|
||||
pub mod config;
|
||||
pub mod error;
|
||||
pub mod init;
|
||||
|
||||
@@ -33,19 +33,20 @@ tokio-tungstenite = "0.14" # websocket
|
||||
## internal
|
||||
client-core = { path = "../client-core" }
|
||||
coconut-interface = { path = "../../common/coconut-interface", optional = true }
|
||||
credentials = { path = "../../common/credentials", optional = true }
|
||||
credential-storage = { path = "../../common/credential-storage" }
|
||||
config = { path = "../../common/config" }
|
||||
credential-storage = { path = "../../common/credential-storage" }
|
||||
credentials = { path = "../../common/credentials", optional = true }
|
||||
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" }
|
||||
network-defaults = { path = "../../common/network-defaults" }
|
||||
websocket-requests = { path = "websocket-requests" }
|
||||
|
||||
[features]
|
||||
coconut = ["coconut-interface", "credentials", "credentials/coconut", "gateway-requests/coconut", "gateway-client/coconut", "client-core/coconut"]
|
||||
|
||||
+103
-21
@@ -27,6 +27,58 @@
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
|
||||
"integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
|
||||
"dependencies": {
|
||||
"@jridgewell/set-array": "^1.0.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/resolve-uri": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
||||
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/set-array": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
|
||||
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/source-map": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
|
||||
"integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.0",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
|
||||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.15",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz",
|
||||
"integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==",
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@@ -3529,13 +3581,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.12.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz",
|
||||
"integrity": "sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ==",
|
||||
"version": "5.15.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz",
|
||||
"integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==",
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.2",
|
||||
"acorn": "^8.5.0",
|
||||
"commander": "^2.20.0",
|
||||
"source-map": "~0.7.2",
|
||||
"source-map-support": "~0.5.20"
|
||||
},
|
||||
"bin": {
|
||||
@@ -3583,14 +3635,6 @@
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
|
||||
},
|
||||
"node_modules/terser/node_modules/source-map": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
|
||||
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/thunky": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
|
||||
@@ -4330,6 +4374,49 @@
|
||||
"integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==",
|
||||
"dev": true
|
||||
},
|
||||
"@jridgewell/gen-mapping": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
|
||||
"integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
|
||||
"requires": {
|
||||
"@jridgewell/set-array": "^1.0.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
}
|
||||
},
|
||||
"@jridgewell/resolve-uri": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
||||
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w=="
|
||||
},
|
||||
"@jridgewell/set-array": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
|
||||
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw=="
|
||||
},
|
||||
"@jridgewell/source-map": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
|
||||
"integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
|
||||
"requires": {
|
||||
"@jridgewell/gen-mapping": "^0.3.0",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
}
|
||||
},
|
||||
"@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
|
||||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
|
||||
},
|
||||
"@jridgewell/trace-mapping": {
|
||||
"version": "0.3.15",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz",
|
||||
"integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==",
|
||||
"requires": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@@ -7058,13 +7145,13 @@
|
||||
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="
|
||||
},
|
||||
"terser": {
|
||||
"version": "5.12.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz",
|
||||
"integrity": "sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ==",
|
||||
"version": "5.15.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz",
|
||||
"integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==",
|
||||
"requires": {
|
||||
"@jridgewell/source-map": "^0.3.2",
|
||||
"acorn": "^8.5.0",
|
||||
"commander": "^2.20.0",
|
||||
"source-map": "~0.7.2",
|
||||
"source-map-support": "~0.5.20"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -7072,11 +7159,6 @@
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
|
||||
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -32,6 +32,7 @@ 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;
|
||||
@@ -85,6 +86,7 @@ impl NymClient {
|
||||
&self,
|
||||
topology_accessor: TopologyAccessor,
|
||||
mix_tx: BatchMixMessageSender,
|
||||
shutdown: ShutdownListener,
|
||||
) {
|
||||
info!("Starting loop cover traffic stream...");
|
||||
|
||||
@@ -98,6 +100,7 @@ impl NymClient {
|
||||
mix_tx,
|
||||
self.as_mix_recipient(),
|
||||
topology_accessor,
|
||||
shutdown,
|
||||
)
|
||||
.start();
|
||||
}
|
||||
@@ -109,6 +112,7 @@ 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(),
|
||||
@@ -129,6 +133,7 @@ impl NymClient {
|
||||
mix_sender,
|
||||
topology_accessor,
|
||||
reply_key_storage,
|
||||
shutdown,
|
||||
)
|
||||
.start();
|
||||
}
|
||||
@@ -140,6 +145,7 @@ impl NymClient {
|
||||
query_receiver: ReceivedBufferRequestReceiver,
|
||||
mixnet_receiver: MixnetMessageReceiver,
|
||||
reply_key_storage: ReplyKeyStorage,
|
||||
shutdown: ShutdownListener,
|
||||
) {
|
||||
info!("Starting received messages buffer controller...");
|
||||
ReceivedMessagesBufferController::new(
|
||||
@@ -147,6 +153,7 @@ impl NymClient {
|
||||
query_receiver,
|
||||
mixnet_receiver,
|
||||
reply_key_storage,
|
||||
shutdown,
|
||||
)
|
||||
.start()
|
||||
}
|
||||
@@ -155,6 +162,7 @@ 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() {
|
||||
@@ -197,6 +205,7 @@ impl NymClient {
|
||||
ack_sender,
|
||||
self.config.get_base().get_gateway_response_timeout(),
|
||||
Some(bandwidth_controller),
|
||||
Some(shutdown),
|
||||
);
|
||||
|
||||
gateway_client
|
||||
@@ -212,7 +221,11 @@ 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) {
|
||||
async fn start_topology_refresher(
|
||||
&mut self,
|
||||
topology_accessor: TopologyAccessor,
|
||||
shutdown: ShutdownListener,
|
||||
) {
|
||||
let topology_refresher_config = TopologyRefresherConfig::new(
|
||||
self.config.get_base().get_validator_api_endpoints(),
|
||||
self.config.get_base().get_topology_refresh_rate(),
|
||||
@@ -234,7 +247,7 @@ impl NymClient {
|
||||
}
|
||||
|
||||
info!("Starting topology refresher...");
|
||||
topology_refresher.start();
|
||||
topology_refresher.start(shutdown);
|
||||
}
|
||||
|
||||
// controller for sending sphinx packets to mixnet (either real traffic or cover traffic)
|
||||
@@ -245,9 +258,10 @@ impl NymClient {
|
||||
&mut self,
|
||||
mix_rx: BatchMixMessageReceiver,
|
||||
gateway_client: GatewayClient,
|
||||
shutdown: ShutdownListener,
|
||||
) {
|
||||
info!("Starting mix traffic controller...");
|
||||
MixTrafficController::new(mix_rx, gateway_client).start();
|
||||
MixTrafficController::new(mix_rx, gateway_client, shutdown).start();
|
||||
}
|
||||
|
||||
fn start_websocket_listener(
|
||||
@@ -308,20 +322,26 @@ impl NymClient {
|
||||
|
||||
/// blocking version of `start` method. Will run forever (or until SIGINT is sent)
|
||||
pub async fn run_forever(&mut self) {
|
||||
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
|
||||
);
|
||||
}
|
||||
let shutdown = self.start().await;
|
||||
wait_for_signal().await;
|
||||
|
||||
println!(
|
||||
"Received SIGINT - the client will terminate now (threads are not yet nicely stopped, if you see stack traces that's alright)."
|
||||
"Received signal - the client will terminate now (threads are not yet nicely stopped, if you see stack traces that's alright)."
|
||||
);
|
||||
|
||||
log::info!("Sending shutdown");
|
||||
shutdown.signal_shutdown().ok();
|
||||
|
||||
// Some of these components have shutdown signalling implemented as part of socks5 work,
|
||||
// but since it's not fully implemented (yet) for all the components of the native client,
|
||||
// we don't try to wait and instead just stop immediately.
|
||||
//log::info!("Waiting for tasks to finish... (Press ctrl-c to force)");
|
||||
//shutdown.wait_for_shutdown().await;
|
||||
|
||||
log::info!("Stopping nym-client");
|
||||
}
|
||||
|
||||
pub async fn start(&mut self) {
|
||||
pub async fn start(&mut self) -> ShutdownNotifier {
|
||||
info!("Starting nym client");
|
||||
// channels for inter-component communication
|
||||
// TODO: make the channels be internally created by the relevant components
|
||||
@@ -351,30 +371,43 @@ 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())
|
||||
self.start_topology_refresher(shared_topology_accessor.clone(), shutdown.subscribe())
|
||||
.await;
|
||||
self.start_received_messages_buffer_controller(
|
||||
received_buffer_request_receiver,
|
||||
mixnet_messages_receiver,
|
||||
reply_key_storage.clone(),
|
||||
shutdown.subscribe(),
|
||||
);
|
||||
|
||||
let gateway_client = self
|
||||
.start_gateway_client(mixnet_messages_sender, ack_sender)
|
||||
.start_gateway_client(mixnet_messages_sender, ack_sender, shutdown.subscribe())
|
||||
.await;
|
||||
|
||||
self.start_mix_traffic_controller(sphinx_message_receiver, gateway_client);
|
||||
self.start_mix_traffic_controller(
|
||||
sphinx_message_receiver,
|
||||
gateway_client,
|
||||
shutdown.subscribe(),
|
||||
);
|
||||
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);
|
||||
self.start_cover_traffic_stream(
|
||||
shared_topology_accessor,
|
||||
sphinx_message_sender,
|
||||
shutdown.subscribe(),
|
||||
);
|
||||
|
||||
match self.config.get_socket_type() {
|
||||
SocketType::WebSocket => {
|
||||
@@ -399,5 +432,7 @@ impl NymClient {
|
||||
|
||||
info!("Client startup finished!");
|
||||
info!("The address of this client is: {}", self.as_mix_recipient());
|
||||
|
||||
shutdown
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::Args;
|
||||
use client_core::config::GatewayEndpoint;
|
||||
use client_core::{config::GatewayEndpoint, error::ClientCoreError};
|
||||
use config::NymConfig;
|
||||
|
||||
use crate::{
|
||||
@@ -120,7 +120,12 @@ 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;
|
||||
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)
|
||||
});
|
||||
config.get_base_mut().with_gateway_endpoint(gateway);
|
||||
|
||||
let config_save_location = config.get_config_file_save_location();
|
||||
@@ -138,7 +143,10 @@ pub(crate) async fn execute(args: &Init) {
|
||||
);
|
||||
println!("Client configuration completed.");
|
||||
|
||||
client_core::init::show_address(config.get_base());
|
||||
client_core::init::show_address(config.get_base()).unwrap_or_else(|err| {
|
||||
eprintln!("Failed to show address\nError: {err}");
|
||||
std::process::exit(1)
|
||||
});
|
||||
}
|
||||
|
||||
async fn setup_gateway(
|
||||
@@ -146,7 +154,7 @@ async fn setup_gateway(
|
||||
register: bool,
|
||||
user_chosen_gateway_id: Option<&str>,
|
||||
config: &Config,
|
||||
) -> GatewayEndpoint {
|
||||
) -> Result<GatewayEndpoint, ClientCoreError> {
|
||||
if register {
|
||||
// Get the gateway details by querying the validator-api. Either pick one at random or use
|
||||
// the chosen one if it's among the available ones.
|
||||
@@ -155,16 +163,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");
|
||||
|
||||
gateway.into()
|
||||
Ok(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
|
||||
@@ -174,22 +182,22 @@ async fn setup_gateway(
|
||||
config.get_base().get_validator_api_endpoints(),
|
||||
user_chosen_gateway_id,
|
||||
)
|
||||
.await;
|
||||
.await?;
|
||||
log::debug!("Querying gateway gives: {}", gateway);
|
||||
gateway.into()
|
||||
Ok(gateway.into())
|
||||
} else {
|
||||
println!("Not registering gateway, will reuse existing config and keys");
|
||||
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."
|
||||
)
|
||||
}
|
||||
}
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,21 +26,22 @@ url = "2.2"
|
||||
# internal
|
||||
client-core = { path = "../client-core" }
|
||||
coconut-interface = { path = "../../common/coconut-interface", optional = true }
|
||||
credentials = { path = "../../common/credentials", optional = true }
|
||||
credential-storage = { path = "../../common/credential-storage" }
|
||||
config = { path = "../../common/config" }
|
||||
credential-storage = { path = "../../common/credential-storage" }
|
||||
credentials = { path = "../../common/credentials", optional = true }
|
||||
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"]
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// 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,
|
||||
@@ -29,6 +31,7 @@ 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::{
|
||||
@@ -84,6 +87,7 @@ impl NymClient {
|
||||
&self,
|
||||
topology_accessor: TopologyAccessor,
|
||||
mix_tx: BatchMixMessageSender,
|
||||
shutdown: ShutdownListener,
|
||||
) {
|
||||
info!("Starting loop cover traffic stream...");
|
||||
|
||||
@@ -97,6 +101,7 @@ impl NymClient {
|
||||
mix_tx,
|
||||
self.as_mix_recipient(),
|
||||
topology_accessor,
|
||||
shutdown,
|
||||
)
|
||||
.start();
|
||||
}
|
||||
@@ -108,6 +113,7 @@ 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(),
|
||||
@@ -128,6 +134,7 @@ impl NymClient {
|
||||
mix_sender,
|
||||
topology_accessor,
|
||||
reply_key_storage,
|
||||
shutdown,
|
||||
)
|
||||
.start();
|
||||
}
|
||||
@@ -139,6 +146,7 @@ impl NymClient {
|
||||
query_receiver: ReceivedBufferRequestReceiver,
|
||||
mixnet_receiver: MixnetMessageReceiver,
|
||||
reply_key_storage: ReplyKeyStorage,
|
||||
shutdown: ShutdownListener,
|
||||
) {
|
||||
info!("Starting received messages buffer controller...");
|
||||
ReceivedMessagesBufferController::new(
|
||||
@@ -146,6 +154,7 @@ impl NymClient {
|
||||
query_receiver,
|
||||
mixnet_receiver,
|
||||
reply_key_storage,
|
||||
shutdown,
|
||||
)
|
||||
.start()
|
||||
}
|
||||
@@ -154,6 +163,7 @@ 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() {
|
||||
@@ -196,6 +206,7 @@ impl NymClient {
|
||||
ack_sender,
|
||||
self.config.get_base().get_gateway_response_timeout(),
|
||||
Some(bandwidth_controller),
|
||||
Some(shutdown),
|
||||
);
|
||||
|
||||
gateway_client
|
||||
@@ -211,7 +222,11 @@ 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) {
|
||||
async fn start_topology_refresher(
|
||||
&mut self,
|
||||
topology_accessor: TopologyAccessor,
|
||||
shutdown: ShutdownListener,
|
||||
) {
|
||||
let topology_refresher_config = TopologyRefresherConfig::new(
|
||||
self.config.get_base().get_validator_api_endpoints(),
|
||||
self.config.get_base().get_topology_refresh_rate(),
|
||||
@@ -233,7 +248,7 @@ impl NymClient {
|
||||
}
|
||||
|
||||
info!("Starting topology refresher...");
|
||||
topology_refresher.start();
|
||||
topology_refresher.start(shutdown);
|
||||
}
|
||||
|
||||
// controller for sending sphinx packets to mixnet (either real traffic or cover traffic)
|
||||
@@ -244,15 +259,17 @@ impl NymClient {
|
||||
&mut self,
|
||||
mix_rx: BatchMixMessageReceiver,
|
||||
gateway_client: GatewayClient,
|
||||
shutdown: ShutdownListener,
|
||||
) {
|
||||
info!("Starting mix traffic controller...");
|
||||
MixTrafficController::new(mix_rx, gateway_client).start();
|
||||
MixTrafficController::new(mix_rx, gateway_client, shutdown).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];
|
||||
@@ -264,43 +281,57 @@ 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) {
|
||||
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
|
||||
);
|
||||
}
|
||||
let mut shutdown = self.start().await;
|
||||
wait_for_signal().await;
|
||||
|
||||
println!(
|
||||
"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");
|
||||
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");
|
||||
}
|
||||
|
||||
// Variant of `run_forever` that listends for remote control messages
|
||||
pub async fn run_and_listen(&mut self, mut receiver: Socks5ControlMessageReceiver) {
|
||||
self.start().await;
|
||||
let mut shutdown = self.start().await;
|
||||
tokio::select! {
|
||||
message = receiver.next() => {
|
||||
log::debug!("Received message: {:?}", message);
|
||||
match message {
|
||||
Some(Socks5ControlMessage::Stop) => {
|
||||
log::info!("Shutting down");
|
||||
log::info!("Graceful shutdown of tasks not yet implemented, you might see (harmless) panics until then");
|
||||
log::info!("Received stop message");
|
||||
}
|
||||
None => {
|
||||
log::info!("Channel closed, stopping");
|
||||
}
|
||||
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) {
|
||||
pub async fn start(&mut self) -> ShutdownNotifier {
|
||||
info!("Starting nym client");
|
||||
// channels for inter-component communication
|
||||
// TODO: make the channels be internally created by the relevant components
|
||||
@@ -330,33 +361,52 @@ 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())
|
||||
self.start_topology_refresher(shared_topology_accessor.clone(), shutdown.subscribe())
|
||||
.await;
|
||||
self.start_received_messages_buffer_controller(
|
||||
received_buffer_request_receiver,
|
||||
mixnet_messages_receiver,
|
||||
reply_key_storage.clone(),
|
||||
shutdown.subscribe(),
|
||||
);
|
||||
|
||||
let gateway_client = self
|
||||
.start_gateway_client(mixnet_messages_sender, ack_sender)
|
||||
.start_gateway_client(mixnet_messages_sender, ack_sender, shutdown.subscribe())
|
||||
.await;
|
||||
|
||||
self.start_mix_traffic_controller(sphinx_message_receiver, gateway_client);
|
||||
self.start_mix_traffic_controller(
|
||||
sphinx_message_receiver,
|
||||
gateway_client,
|
||||
shutdown.subscribe(),
|
||||
);
|
||||
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);
|
||||
self.start_socks5_listener(received_buffer_request_sender, input_sender);
|
||||
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(),
|
||||
);
|
||||
|
||||
info!("Client startup finished!");
|
||||
info!("The address of this client is: {}", self.as_mix_recipient());
|
||||
|
||||
shutdown
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::Args;
|
||||
use client_core::config::GatewayEndpoint;
|
||||
use client_core::{config::GatewayEndpoint, error::ClientCoreError};
|
||||
use config::NymConfig;
|
||||
|
||||
use crate::{
|
||||
@@ -120,7 +120,12 @@ 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;
|
||||
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)
|
||||
});
|
||||
config.get_base_mut().with_gateway_endpoint(gateway);
|
||||
|
||||
let config_save_location = config.get_config_file_save_location();
|
||||
@@ -138,7 +143,10 @@ pub(crate) async fn execute(args: &Init) {
|
||||
);
|
||||
println!("Client configuration completed.");
|
||||
|
||||
client_core::init::show_address(config.get_base());
|
||||
client_core::init::show_address(config.get_base()).unwrap_or_else(|err| {
|
||||
eprintln!("Failed to show address\nError: {err}");
|
||||
std::process::exit(1)
|
||||
});
|
||||
}
|
||||
|
||||
async fn setup_gateway(
|
||||
@@ -146,7 +154,7 @@ async fn setup_gateway(
|
||||
register: bool,
|
||||
user_chosen_gateway_id: Option<&str>,
|
||||
config: &Config,
|
||||
) -> GatewayEndpoint {
|
||||
) -> Result<GatewayEndpoint, ClientCoreError> {
|
||||
if register {
|
||||
// Get the gateway details by querying the validator-api. Either pick one at random or use
|
||||
// the chosen one if it's among the available ones.
|
||||
@@ -155,16 +163,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");
|
||||
|
||||
gateway.into()
|
||||
Ok(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
|
||||
@@ -174,22 +182,21 @@ async fn setup_gateway(
|
||||
config.get_base().get_validator_api_endpoints(),
|
||||
user_chosen_gateway_id,
|
||||
)
|
||||
.await;
|
||||
.await?;
|
||||
log::debug!("Querying gateway gives: {}", gateway);
|
||||
gateway.into()
|
||||
Ok(gateway.into())
|
||||
} else {
|
||||
println!("Not registering gateway, will reuse existing config and keys");
|
||||
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."
|
||||
)
|
||||
}
|
||||
}
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ 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};
|
||||
|
||||
@@ -140,6 +141,7 @@ pub(crate) struct SocksClient {
|
||||
service_provider: Recipient,
|
||||
self_address: Recipient,
|
||||
started_proxy: bool,
|
||||
shutdown_listener: ShutdownListener,
|
||||
}
|
||||
|
||||
impl Drop for SocksClient {
|
||||
@@ -163,6 +165,7 @@ impl SocksClient {
|
||||
service_provider: Recipient,
|
||||
controller_sender: ControllerSender,
|
||||
self_address: Recipient,
|
||||
shutdown_listener: ShutdownListener,
|
||||
) -> Self {
|
||||
let connection_id = Self::generate_random();
|
||||
SocksClient {
|
||||
@@ -176,6 +179,7 @@ impl SocksClient {
|
||||
service_provider,
|
||||
self_address,
|
||||
started_proxy: false,
|
||||
shutdown_listener,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,6 +254,7 @@ 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);
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
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 {
|
||||
@@ -25,6 +28,7 @@ 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
|
||||
@@ -35,6 +39,7 @@ impl MixnetResponseListener {
|
||||
buffer_requester,
|
||||
mix_response_receiver,
|
||||
controller_sender,
|
||||
shutdown,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,11 +78,25 @@ impl MixnetResponseListener {
|
||||
}
|
||||
|
||||
pub(crate) async fn run(&mut self) {
|
||||
while let Some(received_responses) = self.mix_response_receiver.next().await {
|
||||
for reconstructed_message in received_responses {
|
||||
self.on_message(reconstructed_message).await;
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
error!("We should never see this message");
|
||||
assert!(self.shutdown.is_shutdown_poll());
|
||||
log::debug!("MixnetResponseListener: Exiting");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ 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.
|
||||
@@ -19,6 +20,7 @@ pub struct SphinxSocksServer {
|
||||
listening_address: SocketAddr,
|
||||
service_provider: Recipient,
|
||||
self_address: Recipient,
|
||||
shutdown: ShutdownListener,
|
||||
}
|
||||
|
||||
impl SphinxSocksServer {
|
||||
@@ -28,6 +30,7 @@ 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
|
||||
@@ -38,6 +41,7 @@ impl SphinxSocksServer {
|
||||
listening_address: format!("{}:{}", ip, port).parse().unwrap(),
|
||||
service_provider,
|
||||
self_address,
|
||||
shutdown,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,62 +56,73 @@ impl SphinxSocksServer {
|
||||
info!("Serving Connections...");
|
||||
|
||||
// controller for managing all active connections
|
||||
let (mut active_streams_controller, controller_sender) = Controller::new();
|
||||
let (mut active_streams_controller, controller_sender) =
|
||||
Controller::new(self.shutdown.clone());
|
||||
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());
|
||||
|
||||
let mut mixnet_response_listener = MixnetResponseListener::new(
|
||||
buffer_requester,
|
||||
controller_sender.clone(),
|
||||
self.shutdown.clone(),
|
||||
);
|
||||
tokio::spawn(async move {
|
||||
mixnet_response_listener.run().await;
|
||||
});
|
||||
|
||||
loop {
|
||||
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::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(),
|
||||
);
|
||||
|
||||
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 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 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
|
||||
}
|
||||
});
|
||||
};
|
||||
// client gets dropped here
|
||||
}
|
||||
});
|
||||
},
|
||||
_ = self.shutdown.recv() => {
|
||||
log::trace!("SphinxSocksServer: Received shutdown");
|
||||
log::debug!("SphinxSocksServer: Exiting");
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.nyx.nodes.guru/', 'https://validator.nymtech.net/api/', 'n', 'n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g', 'n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw', 'nym');
|
||||
'https://rpc.nymtech.net/', 'https://validator.nymtech.net/api/', 'n', 'n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g', 'n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw', 'nym');
|
||||
const balance = await client.getBalance('n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy');
|
||||
expect(Number.parseFloat(balance.amount)).toBeGreaterThan(0);
|
||||
}).timeout(5000);
|
||||
})
|
||||
})
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
// limitations under the License.
|
||||
|
||||
import {
|
||||
NymClient,
|
||||
set_panic_hook
|
||||
NymClient,
|
||||
set_panic_hook
|
||||
} from "@nymproject/nym-client-wasm"
|
||||
|
||||
// current limitation of rust-wasm for async stuff : (
|
||||
@@ -25,7 +25,7 @@ async function main() {
|
||||
set_panic_hook();
|
||||
|
||||
// validator server we will use to get topology from
|
||||
const validator = "https://sandbox-validator.nymtech.net/api"; //"http://localhost:8081";
|
||||
const validator = "https://validator.nymtech.net/api"; //"http://localhost:8081";
|
||||
|
||||
client = new NymClient(validator);
|
||||
|
||||
|
||||
+115
-25
@@ -36,6 +36,64 @@
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
|
||||
"integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/set-array": "^1.0.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/resolve-uri": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
||||
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/set-array": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
|
||||
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/source-map": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
|
||||
"integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.0",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
|
||||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.15",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz",
|
||||
"integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@@ -3272,14 +3330,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.12.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz",
|
||||
"integrity": "sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ==",
|
||||
"version": "5.15.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz",
|
||||
"integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.2",
|
||||
"acorn": "^8.5.0",
|
||||
"commander": "^2.20.0",
|
||||
"source-map": "~0.7.2",
|
||||
"source-map-support": "~0.5.20"
|
||||
},
|
||||
"bin": {
|
||||
@@ -3372,15 +3430,6 @@
|
||||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/terser/node_modules/source-map": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
|
||||
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/thunky": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
|
||||
@@ -3828,6 +3877,55 @@
|
||||
"integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==",
|
||||
"dev": true
|
||||
},
|
||||
"@jridgewell/gen-mapping": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
|
||||
"integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/set-array": "^1.0.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
}
|
||||
},
|
||||
"@jridgewell/resolve-uri": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
||||
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
|
||||
"dev": true
|
||||
},
|
||||
"@jridgewell/set-array": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
|
||||
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
|
||||
"dev": true
|
||||
},
|
||||
"@jridgewell/source-map": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
|
||||
"integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/gen-mapping": "^0.3.0",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
}
|
||||
},
|
||||
"@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
|
||||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
|
||||
"dev": true
|
||||
},
|
||||
"@jridgewell/trace-mapping": {
|
||||
"version": "0.3.15",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz",
|
||||
"integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@@ -6334,23 +6432,15 @@
|
||||
"dev": true
|
||||
},
|
||||
"terser": {
|
||||
"version": "5.12.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz",
|
||||
"integrity": "sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ==",
|
||||
"version": "5.15.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz",
|
||||
"integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/source-map": "^0.3.2",
|
||||
"acorn": "^8.5.0",
|
||||
"commander": "^2.20.0",
|
||||
"source-map": "~0.7.2",
|
||||
"source-map-support": "~0.5.20"
|
||||
},
|
||||
"dependencies": {
|
||||
"source-map": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
|
||||
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"terser-webpack-plugin": {
|
||||
|
||||
@@ -9,7 +9,7 @@ module.exports = {
|
||||
},
|
||||
mode: "development",
|
||||
plugins: [
|
||||
new CopyWebpackPlugin({patterns: ['index.html']})
|
||||
new CopyWebpackPlugin({ patterns: ['index.html'] })
|
||||
],
|
||||
experiments: { asyncWebAssembly: true }
|
||||
experiments: { syncWebAssembly: true }
|
||||
};
|
||||
|
||||
@@ -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,12 +38,19 @@ 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,6 +30,8 @@ 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"))]
|
||||
@@ -65,6 +67,10 @@ 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 {
|
||||
@@ -80,6 +86,7 @@ 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,
|
||||
@@ -91,12 +98,19 @@ impl GatewayClient {
|
||||
local_identity,
|
||||
shared_key,
|
||||
connection: SocketState::NotConnected,
|
||||
packet_router: PacketRouter::new(ack_sender, mixnet_message_sender),
|
||||
packet_router: PacketRouter::new(
|
||||
ack_sender,
|
||||
mixnet_message_sender,
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
shutdown.clone(),
|
||||
),
|
||||
response_timeout_duration,
|
||||
bandwidth_controller,
|
||||
should_reconnect_on_failure: true,
|
||||
reconnection_attempts: DEFAULT_RECONNECTION_ATTEMPTS,
|
||||
reconnection_backoff: DEFAULT_RECONNECTION_BACKOFF,
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
shutdown,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,6 +137,7 @@ 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;
|
||||
|
||||
@@ -130,7 +145,12 @@ impl GatewayClient {
|
||||
// perfectly fine here, because it's not meant to be used
|
||||
let (ack_tx, _) = mpsc::unbounded();
|
||||
let (mix_tx, _) = mpsc::unbounded();
|
||||
let packet_router = PacketRouter::new(ack_tx, mix_tx);
|
||||
let packet_router = PacketRouter::new(
|
||||
ack_tx,
|
||||
mix_tx,
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
shutdown.clone(),
|
||||
);
|
||||
|
||||
GatewayClient {
|
||||
authenticated: false,
|
||||
@@ -148,6 +168,8 @@ impl GatewayClient {
|
||||
should_reconnect_on_failure: false,
|
||||
reconnection_attempts: DEFAULT_RECONNECTION_ATTEMPTS,
|
||||
reconnection_backoff: DEFAULT_RECONNECTION_BACKOFF,
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
shutdown,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,8 +301,33 @@ 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
|
||||
} else {
|
||||
std::future::pending::<()>().await
|
||||
}
|
||||
}
|
||||
.fuse()
|
||||
};
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
tokio::pin!(shutdown);
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let mut shutdown = std::future::pending::<()>().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);
|
||||
}
|
||||
@@ -291,14 +338,16 @@ impl GatewayClient {
|
||||
};
|
||||
match ws_msg {
|
||||
Message::Binary(bin_msg) => {
|
||||
self.packet_router.route_received(vec![bin_msg]);
|
||||
if let Err(err) = self.packet_router.route_received(vec![bin_msg]) {
|
||||
log::warn!("Route received failed: {:?}", err);
|
||||
}
|
||||
}
|
||||
Message::Text(txt_msg) => {
|
||||
break ServerResponse::try_from(txt_msg).map_err(|_| GatewayClientError::MalformedResponse);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -723,6 +772,8 @@ impl GatewayClient {
|
||||
.as_ref()
|
||||
.expect("no shared key present even though we're authenticated!"),
|
||||
),
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
self.shutdown.clone(),
|
||||
)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
|
||||
@@ -64,6 +64,9 @@ 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,
|
||||
|
||||
@@ -93,6 +96,9 @@ pub enum GatewayClientError {
|
||||
|
||||
#[error("Timed out")]
|
||||
Timeout,
|
||||
|
||||
#[error("Failed to send mixnet message")]
|
||||
MixnetMsgSenderFailedToSend,
|
||||
}
|
||||
|
||||
impl GatewayClientError {
|
||||
|
||||
@@ -8,6 +8,10 @@ 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>>>;
|
||||
@@ -19,20 +23,28 @@ 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(&self, unwrapped_packets: Vec<Vec<u8>>) {
|
||||
pub fn route_received(
|
||||
&mut self,
|
||||
unwrapped_packets: Vec<Vec<u8>>,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
let mut received_messages = Vec::new();
|
||||
let mut received_acks = Vec::new();
|
||||
|
||||
@@ -60,24 +72,29 @@ 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'");
|
||||
self.mixnet_message_sender
|
||||
.unbounded_send(received_messages)
|
||||
.unwrap();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if !received_acks.is_empty() {
|
||||
trace!("routing acks");
|
||||
match self.ack_sender.unbounded_send(received_acks) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
error!("failed to send ack: {:?}", e);
|
||||
}
|
||||
if let Err(e) = self.ack_sender.unbounded_send(received_acks) {
|
||||
error!("failed to send ack: {:?}", e);
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ 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"))]
|
||||
@@ -44,9 +46,9 @@ pub(crate) struct PartiallyDelegated {
|
||||
impl PartiallyDelegated {
|
||||
fn route_socket_message(
|
||||
ws_msg: Message,
|
||||
packet_router: &PacketRouter,
|
||||
packet_router: &mut PacketRouter,
|
||||
shared_key: &SharedKeys,
|
||||
) {
|
||||
) -> Result<(), GatewayClientError> {
|
||||
match ws_msg {
|
||||
Message::Binary(bin_msg) => {
|
||||
// this function decrypts the request and checks the MAC
|
||||
@@ -60,7 +62,7 @@ impl PartiallyDelegated {
|
||||
"message received from the gateway was malformed! - {:?}",
|
||||
err
|
||||
);
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -74,18 +76,22 @@ 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
|
||||
),
|
||||
_ => (),
|
||||
};
|
||||
Message::Text(text) => {
|
||||
trace!(
|
||||
"received a text message - probably a response to some previous query! - {}",
|
||||
text
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
@@ -97,10 +103,34 @@ 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! {
|
||||
_ = fused_receiver => {
|
||||
_ = shutdown => {
|
||||
log::trace!("GatewayClient listener: Received shutdown");
|
||||
log::debug!("GatewayClient listener: Exiting");
|
||||
return;
|
||||
}
|
||||
_ = &mut fused_receiver => {
|
||||
break Ok(());
|
||||
}
|
||||
msg = fused_stream.next() => {
|
||||
@@ -108,7 +138,9 @@ impl PartiallyDelegated {
|
||||
Err(err) => break Err(err),
|
||||
Ok(msg) => msg
|
||||
};
|
||||
Self::route_socket_message(ws_msg, &packet_router, shared_key.as_ref());
|
||||
if let Err(err) = Self::route_socket_message(ws_msg, &mut packet_router, shared_key.as_ref()) {
|
||||
log::warn!("Route socket message failed: {:?}", err);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -338,6 +338,13 @@ 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,
|
||||
|
||||
@@ -117,11 +117,6 @@ pub enum ExecuteMsg {
|
||||
UpdateLockedPledgeCap {
|
||||
amount: Uint128,
|
||||
},
|
||||
MigrateHeightsToTimestamps {
|
||||
account_id: u32,
|
||||
mix_identity: String,
|
||||
height_timestamp_map: Vec<(u64, u64)>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
|
||||
@@ -25,7 +25,7 @@ pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] =
|
||||
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy";
|
||||
|
||||
pub(crate) const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "https://mainnet-stats.nymte.ch:8090/";
|
||||
pub const NYMD_VALIDATOR: &str = "https://rpc.nyx.nodes.guru/";
|
||||
pub const NYMD_VALIDATOR: &str = "https://rpc.nymtech.net";
|
||||
pub const API_VALIDATOR: &str = "https://validator.nymtech.net/api/";
|
||||
pub(crate) fn validators() -> Vec<ValidatorDetails> {
|
||||
vec![ValidatorDetails::new(NYMD_VALIDATOR, Some(API_VALIDATOR))]
|
||||
|
||||
@@ -16,6 +16,7 @@ futures = "0.3"
|
||||
log = "0.4"
|
||||
socks5-requests = { path = "../requests" }
|
||||
ordered-buffer = { path = "../ordered-buffer" }
|
||||
task = { path = "../../task" }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio-test = "0.4.2"
|
||||
|
||||
@@ -7,6 +7,7 @@ use log::*;
|
||||
use ordered_buffer::{OrderedMessage, OrderedMessageBuffer};
|
||||
use socks5_requests::ConnectionId;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use task::ShutdownListener;
|
||||
|
||||
/// A generic message produced after reading from a socket/connection. It includes data that was
|
||||
/// actually read alongside boolean indicating whether the connection got closed so that
|
||||
@@ -74,10 +75,12 @@ pub struct Controller {
|
||||
// buffer for messages received before connection was established due to mixnet being able to
|
||||
// un-order messages. Note we don't ever expect to have more than 1-2 messages per connection here
|
||||
pending_messages: HashMap<ConnectionId, Vec<(Vec<u8>, bool)>>,
|
||||
|
||||
shutdown: ShutdownListener,
|
||||
}
|
||||
|
||||
impl Controller {
|
||||
pub fn new() -> (Self, ControllerSender) {
|
||||
pub fn new(shutdown: ShutdownListener) -> (Self, ControllerSender) {
|
||||
let (sender, receiver) = mpsc::unbounded();
|
||||
(
|
||||
Controller {
|
||||
@@ -85,6 +88,7 @@ impl Controller {
|
||||
receiver,
|
||||
recently_closed: HashSet::new(),
|
||||
pending_messages: HashMap::new(),
|
||||
shutdown,
|
||||
},
|
||||
sender,
|
||||
)
|
||||
@@ -170,16 +174,24 @@ impl Controller {
|
||||
}
|
||||
|
||||
pub async fn run(&mut self) {
|
||||
while let Some(command) = self.receiver.next().await {
|
||||
match command {
|
||||
ControllerCommand::Send(conn_id, data, is_closed) => {
|
||||
self.send_to_connection(conn_id, data, is_closed)
|
||||
loop {
|
||||
tokio::select! {
|
||||
command = self.receiver.next() => match command {
|
||||
Some(ControllerCommand::Send(conn_id, data, is_closed)) => {
|
||||
self.send_to_connection(conn_id, data, is_closed)
|
||||
}
|
||||
Some(ControllerCommand::Insert(conn_id, sender)) => {
|
||||
self.insert_connection(conn_id, sender)
|
||||
}
|
||||
Some(ControllerCommand::Remove(conn_id)) => self.remove_connection(conn_id),
|
||||
None => {
|
||||
log::trace!("SOCKS5 Controller: Stopping since channel closed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
ControllerCommand::Insert(conn_id, sender) => {
|
||||
self.insert_connection(conn_id, sender)
|
||||
}
|
||||
ControllerCommand::Remove(conn_id) => self.remove_connection(conn_id),
|
||||
}
|
||||
}
|
||||
assert!(self.shutdown.is_shutdown_poll());
|
||||
log::debug!("SOCKS5 Controller: Exiting");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ use log::*;
|
||||
use ordered_buffer::OrderedMessageSender;
|
||||
use socks5_requests::ConnectionId;
|
||||
use std::{io, sync::Arc};
|
||||
use task::ShutdownListener;
|
||||
use tokio::select;
|
||||
use tokio::{net::tcp::OwnedReadHalf, sync::Notify, time::sleep};
|
||||
|
||||
@@ -74,6 +75,7 @@ where
|
||||
is_finished
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(super) async fn run_inbound<F, S>(
|
||||
mut reader: OwnedReadHalf,
|
||||
local_destination_address: String, // addresses are provided for better logging
|
||||
@@ -82,6 +84,7 @@ pub(super) async fn run_inbound<F, S>(
|
||||
mix_sender: MixProxySender<S>,
|
||||
adapter_fn: F,
|
||||
shutdown_notify: Arc<Notify>,
|
||||
mut shutdown_listener: ShutdownListener,
|
||||
) -> OwnedReadHalf
|
||||
where
|
||||
F: Fn(ConnectionId, Vec<u8>, bool) -> S + Send + 'static,
|
||||
@@ -106,6 +109,10 @@ where
|
||||
send_empty_close(connection_id, &mut message_sender, &mix_sender, &adapter_fn);
|
||||
break;
|
||||
}
|
||||
_ = shutdown_listener.recv() => {
|
||||
log::trace!("ProxyRunner inbound: Received shutdown");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
trace!("{} - inbound closed", connection_id);
|
||||
|
||||
@@ -5,6 +5,7 @@ use crate::connection_controller::ConnectionReceiver;
|
||||
use futures::channel::mpsc;
|
||||
use socks5_requests::ConnectionId;
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use task::ShutdownListener;
|
||||
use tokio::{net::TcpStream, sync::Notify};
|
||||
|
||||
mod inbound;
|
||||
@@ -44,6 +45,9 @@ pub struct ProxyRunner<S> {
|
||||
local_destination_address: String,
|
||||
remote_source_address: String,
|
||||
connection_id: ConnectionId,
|
||||
|
||||
// Listens to shutdown commands from higher up
|
||||
shutdown_listener: ShutdownListener,
|
||||
}
|
||||
|
||||
impl<S> ProxyRunner<S>
|
||||
@@ -57,6 +61,7 @@ where
|
||||
mix_receiver: ConnectionReceiver,
|
||||
mix_sender: MixProxySender<S>,
|
||||
connection_id: ConnectionId,
|
||||
shutdown_listener: ShutdownListener,
|
||||
) -> Self {
|
||||
ProxyRunner {
|
||||
mix_receiver: Some(mix_receiver),
|
||||
@@ -65,6 +70,7 @@ where
|
||||
local_destination_address,
|
||||
remote_source_address,
|
||||
connection_id,
|
||||
shutdown_listener,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +92,7 @@ where
|
||||
self.mix_sender.clone(),
|
||||
adapter_fn,
|
||||
Arc::clone(&shutdown_notify),
|
||||
self.shutdown_listener.clone(),
|
||||
);
|
||||
|
||||
let outbound_future = outbound::run_outbound(
|
||||
@@ -95,6 +102,7 @@ where
|
||||
self.mix_receiver.take().unwrap(),
|
||||
self.connection_id,
|
||||
shutdown_notify,
|
||||
self.shutdown_listener.clone(),
|
||||
);
|
||||
|
||||
// TODO: this shouldn't really have to spawn tasks inside "library" code, but
|
||||
|
||||
@@ -8,6 +8,7 @@ use futures::StreamExt;
|
||||
use log::*;
|
||||
use socks5_requests::ConnectionId;
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use task::ShutdownListener;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::select;
|
||||
use tokio::{net::tcp::OwnedWriteHalf, sync::Notify, time::sleep, time::Instant};
|
||||
@@ -50,6 +51,7 @@ pub(super) async fn run_outbound(
|
||||
mut mix_receiver: ConnectionReceiver,
|
||||
connection_id: ConnectionId,
|
||||
shutdown_notify: Arc<Notify>,
|
||||
mut shutdown_listener: ShutdownListener,
|
||||
) -> (OwnedWriteHalf, ConnectionReceiver) {
|
||||
let shutdown_future = shutdown_notify.notified().then(|_| sleep(SHUTDOWN_TIMEOUT));
|
||||
tokio::pin!(shutdown_future);
|
||||
@@ -78,6 +80,10 @@ pub(super) async fn run_outbound(
|
||||
debug!("closing outbound proxy after inbound was closed {:?} ago", SHUTDOWN_TIMEOUT);
|
||||
break;
|
||||
}
|
||||
_ = shutdown_listener.recv() => {
|
||||
log::trace!("ProxyRunner outbound: Received shutdown");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,5 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod shutdown;
|
||||
pub mod signal;
|
||||
|
||||
pub use shutdown::{ShutdownListener, ShutdownNotifier};
|
||||
pub use signal::wait_for_signal;
|
||||
|
||||
@@ -74,7 +74,7 @@ pub struct ShutdownListener {
|
||||
}
|
||||
|
||||
impl ShutdownListener {
|
||||
pub fn new(notify: watch::Receiver<()>) -> ShutdownListener {
|
||||
fn new(notify: watch::Receiver<()>) -> ShutdownListener {
|
||||
ShutdownListener {
|
||||
shutdown: false,
|
||||
notify,
|
||||
@@ -92,6 +92,25 @@ impl ShutdownListener {
|
||||
let _ = self.notify.changed().await;
|
||||
self.shutdown = true;
|
||||
}
|
||||
|
||||
pub fn is_shutdown_poll(&mut self) -> bool {
|
||||
if self.shutdown {
|
||||
return true;
|
||||
}
|
||||
match self.notify.has_changed() {
|
||||
Ok(has_changed) => {
|
||||
if has_changed {
|
||||
self.shutdown = true;
|
||||
}
|
||||
has_changed
|
||||
}
|
||||
Err(err) => {
|
||||
log::debug!("Polling shutdown failed: {err}");
|
||||
log::debug!("Assuming this means we should shutdown...");
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#[cfg(unix)]
|
||||
pub async fn wait_for_signal() {
|
||||
use tokio::signal::unix::{signal, SignalKind};
|
||||
let mut sigterm = signal(SignalKind::terminate()).expect("Failed to setup SIGTERM channel");
|
||||
let mut sigquit = signal(SignalKind::quit()).expect("Failed to setup SIGQUIT channel");
|
||||
|
||||
tokio::select! {
|
||||
_ = tokio::signal::ctrl_c() => {
|
||||
log::info!("Received SIGINT");
|
||||
},
|
||||
_ = sigterm.recv() => {
|
||||
log::info!("Received SIGTERM");
|
||||
}
|
||||
_ = sigquit.recv() => {
|
||||
log::info!("Received SIGQUIT");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
pub async fn wait_for_signal() {
|
||||
tokio::select! {
|
||||
_ = tokio::signal::ctrl_c() => {
|
||||
log::info!("Received SIGINT");
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
## Unreleased
|
||||
## [nym-contracts-v1.0.2](https://github.com/nymtech/nym/tree/nym-contracts-v1.0.2) (2022-09-13)
|
||||
|
||||
### Added
|
||||
|
||||
@@ -12,10 +12,12 @@
|
||||
|
||||
- vesting-contract: the contract now correctly stores delegations with their timestamp as opposed to using block height ([#1544])
|
||||
- mixnet-contract: compounding delegator rewards is now possible even if the associated mixnode had already unbonded ([#1571])
|
||||
- mixnet-contract: fixed reward accumulation after claiming rewards ([#1613])
|
||||
|
||||
[#1544]: https://github.com/nymtech/nym/pull/1544
|
||||
[#1569]: https://github.com/nymtech/nym/pull/1569
|
||||
[#1569]: https://github.com/nymtech/nym/pull/1571
|
||||
[#1613]: https://github.com/nymtech/nym/pull/1613
|
||||
|
||||
## [nym-contracts-v1.0.1](https://github.com/nymtech/nym/tree/nym-contracts-v1.0.1) (2022-06-22)
|
||||
|
||||
|
||||
+7
-7
@@ -7,7 +7,7 @@
|
||||
"dependencies": {
|
||||
"@nomiclabs/hardhat-truffle5": "^2.0.5",
|
||||
"@nomiclabs/hardhat-web3": "^2.0.0",
|
||||
"@openzeppelin/contracts": "4.5.0",
|
||||
"@openzeppelin/contracts": "4.7.3",
|
||||
"@openzeppelin/test-helpers": "^0.5.15",
|
||||
"dotenv": "^16.0.0",
|
||||
"file-system": "^2.2.2",
|
||||
@@ -1920,9 +1920,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@openzeppelin/contracts": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.5.0.tgz",
|
||||
"integrity": "sha512-fdkzKPYMjrRiPK6K4y64e6GzULR7R7RwxSigHS8DDp7aWDeoReqsQI+cxHV1UuhAqX69L1lAaWDxenfP+xiqzA=="
|
||||
"version": "4.7.3",
|
||||
"resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.7.3.tgz",
|
||||
"integrity": "sha512-dGRS0agJzu8ybo44pCIf3xBaPQN/65AIXNgK8+4gzKd5kbvlqyxryUYVLJv7fK98Seyd2hDZzVEHSWAh0Bt1Yw=="
|
||||
},
|
||||
"node_modules/@openzeppelin/test-helpers": {
|
||||
"version": "0.5.15",
|
||||
@@ -21190,9 +21190,9 @@
|
||||
}
|
||||
},
|
||||
"@openzeppelin/contracts": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.5.0.tgz",
|
||||
"integrity": "sha512-fdkzKPYMjrRiPK6K4y64e6GzULR7R7RwxSigHS8DDp7aWDeoReqsQI+cxHV1UuhAqX69L1lAaWDxenfP+xiqzA=="
|
||||
"version": "4.7.3",
|
||||
"resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.7.3.tgz",
|
||||
"integrity": "sha512-dGRS0agJzu8ybo44pCIf3xBaPQN/65AIXNgK8+4gzKd5kbvlqyxryUYVLJv7fK98Seyd2hDZzVEHSWAh0Bt1Yw=="
|
||||
},
|
||||
"@openzeppelin/test-helpers": {
|
||||
"version": "0.5.15",
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"dependencies": {
|
||||
"@nomiclabs/hardhat-truffle5": "^2.0.5",
|
||||
"@nomiclabs/hardhat-web3": "^2.0.0",
|
||||
"@openzeppelin/contracts": "4.5.0",
|
||||
"@openzeppelin/contracts": "4.7.3",
|
||||
"@openzeppelin/test-helpers": "^0.5.15",
|
||||
"dotenv": "^16.0.0",
|
||||
"file-system": "^2.2.2",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mixnet-contract"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
edition = "2021"
|
||||
|
||||
|
||||
@@ -471,6 +471,7 @@ pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result<QueryResponse, C
|
||||
Ok(query_res?)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn blacklist_malicious_node(storage: &mut dyn Storage, owner: &Addr) -> Result<(), ContractError> {
|
||||
let mixnode_bond = match crate::mixnodes::storage::mixnodes()
|
||||
.idx
|
||||
@@ -491,6 +492,7 @@ fn blacklist_malicious_node(storage: &mut dyn Storage, owner: &Addr) -> Result<(
|
||||
}
|
||||
|
||||
// Removes nodes we've deemed malicious, returns the pledge to the owners, but does not send any rewards
|
||||
#[allow(unused)]
|
||||
fn remove_malicious_node(
|
||||
storage: &mut dyn Storage,
|
||||
api: &dyn Api,
|
||||
@@ -507,17 +509,8 @@ fn remove_malicious_node(
|
||||
}
|
||||
|
||||
#[entry_point]
|
||||
pub fn migrate(deps: DepsMut<'_>, env: Env, msg: MigrateMsg) -> Result<Response, ContractError> {
|
||||
let mut response = Response::new();
|
||||
for node in msg.nodes_to_remove().iter() {
|
||||
let mut sub_response = remove_malicious_node(deps.storage, deps.api, &env, node)
|
||||
.unwrap_or_else(|_| panic!("Could not remove node: {:?}", node));
|
||||
response.messages.append(&mut sub_response.messages);
|
||||
response.attributes.append(&mut sub_response.attributes);
|
||||
response.events.append(&mut sub_response.events);
|
||||
}
|
||||
|
||||
Ok(response)
|
||||
pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
|
||||
Ok(Response::default())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -547,16 +547,9 @@ pub fn calculate_delegator_reward(
|
||||
.load(storage, (key.clone(), mix_identity.to_string()))
|
||||
.unwrap_or(0);
|
||||
|
||||
// Get delegations newer then last_claimed_height, it would be nice to also fold this into the iteration bellow but it should be ok for now, as
|
||||
// I doubt folks refresh their delegations often
|
||||
let mut delegations = delegations_storage::delegations()
|
||||
.prefix((mix_identity.to_string(), key))
|
||||
.range(
|
||||
storage,
|
||||
Some(Bound::inclusive(last_claimed_height)),
|
||||
None,
|
||||
Order::Descending,
|
||||
)
|
||||
.range(storage, None, None, Order::Ascending)
|
||||
.filter_map(|record| record.ok())
|
||||
.map(|(_, delegation)| delegation)
|
||||
.collect::<Vec<Delegation>>();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "vesting-contract"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
authors = ["Drazen Urch <durch@users.noreply.github.com>"]
|
||||
edition = "2021"
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use crate::errors::ContractError;
|
||||
use crate::storage::{
|
||||
account_from_address, locked_pledge_cap, remove_delegation, save_delegation,
|
||||
update_locked_pledge_cap, BlockTimestampSecs, ADMIN, DELEGATIONS, MIXNET_CONTRACT_ADDRESS,
|
||||
MIX_DENOM,
|
||||
account_from_address, locked_pledge_cap, update_locked_pledge_cap, BlockTimestampSecs, ADMIN,
|
||||
DELEGATIONS, MIXNET_CONTRACT_ADDRESS, MIX_DENOM,
|
||||
};
|
||||
use crate::traits::{
|
||||
DelegatingAccount, GatewayBondingAccount, MixnodeBondingAccount, VestingAccount,
|
||||
@@ -129,22 +128,11 @@ pub fn execute(
|
||||
ExecuteMsg::UpdateStakingAddress { to_address } => {
|
||||
try_update_staking_address(to_address, info, deps)
|
||||
}
|
||||
ExecuteMsg::MigrateHeightsToTimestamps {
|
||||
account_id,
|
||||
mix_identity,
|
||||
height_timestamp_map,
|
||||
} => try_migrate_heights_to_timestamps(
|
||||
account_id,
|
||||
mix_identity,
|
||||
height_timestamp_map,
|
||||
info,
|
||||
deps,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Update locked_pledge_cap, the hard cap for staking/bonding with unvested tokens.
|
||||
///
|
||||
///
|
||||
/// Callable by ADMIN only, see [instantiate].
|
||||
pub fn try_update_locked_pledge_cap(
|
||||
amount: Uint128,
|
||||
@@ -169,7 +157,7 @@ pub fn try_update_mixnode_config(
|
||||
}
|
||||
|
||||
/// Updates mixnet contract address, for cases when a new mixnet contract is deployed.
|
||||
///
|
||||
///
|
||||
/// Callable by ADMIN only, see [instantiate].
|
||||
pub fn try_update_mixnet_address(
|
||||
address: String,
|
||||
@@ -259,30 +247,6 @@ fn try_update_staking_address(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_migrate_heights_to_timestamps(
|
||||
account_id: u32,
|
||||
mix_identity: String,
|
||||
height_timestamp_map: Vec<(u64, u64)>,
|
||||
info: MessageInfo,
|
||||
deps: DepsMut<'_>,
|
||||
) -> Result<Response, ContractError> {
|
||||
if info.sender != ADMIN.load(deps.storage)? {
|
||||
return Err(ContractError::NotAdmin(info.sender.as_str().to_string()));
|
||||
}
|
||||
|
||||
for (height, timestamp) in height_timestamp_map {
|
||||
let amount = DELEGATIONS.load(deps.storage, (account_id, mix_identity.clone(), height))?;
|
||||
remove_delegation((account_id, mix_identity.clone(), height), deps.storage)?;
|
||||
save_delegation(
|
||||
(account_id, mix_identity.clone(), timestamp),
|
||||
amount,
|
||||
deps.storage,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(Response::default())
|
||||
}
|
||||
|
||||
/// Bond a gateway, sends [mixnet_contract_common::ExecuteMsg::BondGatewayOnBehalf] to [crate::storage::MIXNET_CONTRACT_ADDRESS].
|
||||
pub fn try_bond_gateway(
|
||||
gateway: Gateway,
|
||||
@@ -449,7 +413,7 @@ fn try_undelegate_from_mixnode(
|
||||
}
|
||||
|
||||
/// Creates a new periodic vesting account, and deposits funds to vest into the contract.
|
||||
///
|
||||
///
|
||||
/// Callable by ADMIN only, see [instantiate].
|
||||
fn try_create_periodic_vesting_account(
|
||||
owner_address: &str,
|
||||
|
||||
@@ -925,97 +925,4 @@ mod tests {
|
||||
// the 50M delegation wasn't a thing here for VESTING tokens either
|
||||
assert_eq!(delegated_vesting.amount, Uint128::zero());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn migrate_heights_to_timestamps() {
|
||||
let mut deps = init_contract();
|
||||
let mut env = mock_env();
|
||||
|
||||
let account = vesting_account_new_fixture(&mut deps.storage, &env);
|
||||
let mix_identity = String::from("identity");
|
||||
let mut curr_block = env.block.clone();
|
||||
let mut delegation_blocks = std::iter::from_fn(move || {
|
||||
curr_block.height += 1;
|
||||
curr_block.time = curr_block.time.plus_seconds(5);
|
||||
Some(curr_block.clone())
|
||||
})
|
||||
.take(100)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for block in delegation_blocks.iter() {
|
||||
DELEGATIONS
|
||||
.save(
|
||||
&mut deps.storage,
|
||||
(account.storage_key(), mix_identity.clone(), block.height),
|
||||
&Uint128::new(90_000_000_000),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let delegations = try_get_delegation_times(
|
||||
deps.as_ref(),
|
||||
account.owner_address().as_str(),
|
||||
mix_identity.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
delegations.delegation_timestamps.len(),
|
||||
delegation_blocks.len()
|
||||
);
|
||||
for (heights, delegation_block) in delegations
|
||||
.delegation_timestamps
|
||||
.iter()
|
||||
.zip(delegation_blocks.iter())
|
||||
{
|
||||
assert_eq!(*heights, delegation_block.height);
|
||||
}
|
||||
|
||||
let height_timestamp_map = delegation_blocks
|
||||
.iter()
|
||||
.map(|block| (block.height, block.time.seconds()))
|
||||
.collect();
|
||||
let admin = ADMIN.load(&deps.storage).unwrap();
|
||||
try_migrate_heights_to_timestamps(
|
||||
account.storage_key(),
|
||||
mix_identity.clone(),
|
||||
height_timestamp_map,
|
||||
mock_info(&admin, &[]),
|
||||
deps.as_mut(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Some new delegation appears during migration
|
||||
env.block.height += 200;
|
||||
env.block.time = env.block.time.plus_seconds(1000);
|
||||
delegation_blocks.push(env.block.clone());
|
||||
account
|
||||
.try_delegate_to_mixnode(
|
||||
String::from("identity"),
|
||||
Coin {
|
||||
amount: Uint128::new(90_000_000_000),
|
||||
denom: TEST_COIN_DENOM.to_string(),
|
||||
},
|
||||
&env,
|
||||
&mut deps.storage,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let delegations = try_get_delegation_times(
|
||||
deps.as_ref(),
|
||||
account.owner_address().as_str(),
|
||||
mix_identity.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
delegations.delegation_timestamps.len(),
|
||||
delegation_blocks.len()
|
||||
);
|
||||
for (timestamp, delegation_block) in delegations
|
||||
.delegation_timestamps
|
||||
.iter()
|
||||
.zip(delegation_blocks.iter())
|
||||
{
|
||||
assert_eq!(*timestamp, delegation_block.time.seconds());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-29
@@ -26,7 +26,7 @@ use rand::thread_rng;
|
||||
use std::net::SocketAddr;
|
||||
use std::process;
|
||||
use std::sync::Arc;
|
||||
use task::{ShutdownListener, ShutdownNotifier};
|
||||
use task::{wait_for_signal, ShutdownListener, ShutdownNotifier};
|
||||
use version_checker::parse_version;
|
||||
|
||||
mod http;
|
||||
@@ -334,31 +334,3 @@ impl MixNode {
|
||||
self.wait_for_interrupt(shutdown).await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
async fn wait_for_signal() {
|
||||
use tokio::signal::unix::{signal, SignalKind};
|
||||
let mut sigterm = signal(SignalKind::terminate()).expect("Failed to setup SIGTERM channel");
|
||||
let mut sigquit = signal(SignalKind::quit()).expect("Failed to setup SIGQUIT channel");
|
||||
|
||||
tokio::select! {
|
||||
_ = tokio::signal::ctrl_c() => {
|
||||
log::info!("Received SIGINT");
|
||||
},
|
||||
_ = sigterm.recv() => {
|
||||
log::info!("Received SIGTERM");
|
||||
}
|
||||
_ = sigquit.recv() => {
|
||||
log::info!("Received SIGQUIT");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
async fn wait_for_signal() {
|
||||
tokio::select! {
|
||||
_ = tokio::signal::ctrl_c() => {
|
||||
log::info!("Received SIGINT");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+21
-4
@@ -629,6 +629,9 @@ dependencies = [
|
||||
"rand 0.7.3",
|
||||
"serde",
|
||||
"sled",
|
||||
"tap",
|
||||
"task",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"topology",
|
||||
"url",
|
||||
@@ -1919,8 +1922,10 @@ dependencies = [
|
||||
"pemstore",
|
||||
"rand 0.7.3",
|
||||
"secp256k1",
|
||||
"task",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tokio-tungstenite",
|
||||
"tungstenite",
|
||||
"url",
|
||||
@@ -3459,6 +3464,7 @@ dependencies = [
|
||||
"serde",
|
||||
"snafu",
|
||||
"socks5-requests",
|
||||
"task",
|
||||
"tokio",
|
||||
"topology",
|
||||
"url",
|
||||
@@ -4266,6 +4272,7 @@ dependencies = [
|
||||
"log",
|
||||
"ordered-buffer",
|
||||
"socks5-requests",
|
||||
"task",
|
||||
"tokio",
|
||||
"tokio-util 0.7.3",
|
||||
]
|
||||
@@ -5624,6 +5631,15 @@ dependencies = [
|
||||
"xattr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "task"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"tokio",
|
||||
"tokio-util 0.7.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri"
|
||||
version = "1.0.3"
|
||||
@@ -5944,18 +5960,18 @@ checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c"
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.31"
|
||||
version = "1.0.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
|
||||
checksum = "8c1b05ca9d106ba7d2e31a9dab4a64e7be2cce415321966ea3132c49a656e252"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.31"
|
||||
version = "1.0.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
|
||||
checksum = "e8f2591983642de85c921015f3f070c665a197ed69e417af436115e3a1407487"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -6070,6 +6086,7 @@ dependencies = [
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tokio-util 0.7.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -172,7 +172,7 @@ pub async fn init_socks5_config(provider_address: String, chosen_gateway_id: Str
|
||||
);
|
||||
log::info!("Client configuration completed.");
|
||||
|
||||
client_core::init::show_address(config.get_base());
|
||||
client_core::init::show_address(config.get_base())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -191,13 +191,13 @@ 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())
|
||||
@@ -210,23 +210,21 @@ 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())
|
||||
} else {
|
||||
println!("Not registering gateway, will reuse existing config and keys");
|
||||
match Socks5Config::load_from_file(Some(id)) {
|
||||
Ok(existing_config) => Ok(existing_config.get_base().get_gateway_endpoint().clone()),
|
||||
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."
|
||||
);
|
||||
Err(BackendError::CouldNotLoadExistingGatewayConfiguration(err))
|
||||
}
|
||||
}
|
||||
let existing_config = Socks5Config::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."
|
||||
);
|
||||
BackendError::CouldNotLoadExistingGatewayConfiguration(err)
|
||||
})?;
|
||||
Ok(existing_config.get_base().get_gateway_endpoint().clone())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use client_core::error::ClientCoreError;
|
||||
use serde::{Serialize, Serializer};
|
||||
use thiserror::Error;
|
||||
|
||||
@@ -29,6 +30,11 @@ pub enum BackendError {
|
||||
#[from]
|
||||
source: serde_json::Error,
|
||||
},
|
||||
#[error("{source}")]
|
||||
ClientCoreError {
|
||||
#[from]
|
||||
source: ClientCoreError,
|
||||
},
|
||||
|
||||
#[error("State error")]
|
||||
StateError,
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## [nym-wallet-v1.0.9](https://github.com/nymtech/nym/releases/tag/nym-wallet-v1.0.8) (2022-09-08)
|
||||
|
||||
- wallet: change default `nymd` URL to https://rpc.nymtech.net
|
||||
|
||||
## [nym-wallet-v1.0.8](https://github.com/nymtech/nym/releases/tag/nym-wallet-v1.0.8) (2022-08-11)
|
||||
|
||||
- wallet: new bonding flow and screen for bonded node
|
||||
|
||||
Generated
+20
-18
@@ -645,16 +645,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.1.18"
|
||||
version = "3.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b"
|
||||
checksum = "23b71c3ce99b7611011217b366d923f1d0a7e07a92bb2dbf1e84508c673ca3bd"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
"clap_derive",
|
||||
"clap_lex",
|
||||
"indexmap",
|
||||
"lazy_static",
|
||||
"once_cell",
|
||||
"strsim 0.10.0",
|
||||
"termcolor",
|
||||
"textwrap",
|
||||
@@ -662,9 +662,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "3.1.18"
|
||||
version = "3.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25320346e922cffe59c0bbc5410c8d8784509efb321488971081313cb1e1a33c"
|
||||
checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65"
|
||||
dependencies = [
|
||||
"heck 0.4.0",
|
||||
"proc-macro-error",
|
||||
@@ -675,9 +675,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.2.0"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213"
|
||||
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
@@ -756,9 +756,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
version = "1.2.2"
|
||||
version = "1.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3"
|
||||
checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c"
|
||||
dependencies = [
|
||||
"cache-padded",
|
||||
]
|
||||
@@ -1456,9 +1456,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "enumflags2"
|
||||
version = "0.7.4"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b3ab37dc79652c9d85f1f7b6070d77d321d2467f5fe7b00d6b7a86c57b092ae"
|
||||
checksum = "e75d4cd21b95383444831539909fbb14b9dc3fdceb2a6f5d36577329a1f55ccb"
|
||||
dependencies = [
|
||||
"enumflags2_derive",
|
||||
"serde",
|
||||
@@ -1490,9 +1490,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "2.5.2"
|
||||
version = "2.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71"
|
||||
checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
|
||||
|
||||
[[package]]
|
||||
name = "execute"
|
||||
@@ -3055,7 +3055,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym_wallet"
|
||||
version = "1.0.8"
|
||||
version = "1.0.9"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"argon2 0.3.4",
|
||||
@@ -3073,6 +3073,7 @@ dependencies = [
|
||||
"eyre",
|
||||
"futures",
|
||||
"itertools",
|
||||
"k256",
|
||||
"log",
|
||||
"mixnet-contract-common",
|
||||
"nym-types",
|
||||
@@ -3150,9 +3151,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.9.0"
|
||||
version = "1.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
|
||||
checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
@@ -3605,10 +3606,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "polling"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259"
|
||||
checksum = "899b00b9c8ab553c743b3e11e87c5c7d423b2a2de229ba95b24a756344748011"
|
||||
dependencies = [
|
||||
"autocfg 1.1.0",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"log",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym_wallet"
|
||||
version = "1.0.8"
|
||||
version = "1.0.9"
|
||||
description = "Nym Native Wallet"
|
||||
authors = ["Nym Technologies SA"]
|
||||
license = ""
|
||||
@@ -42,6 +42,7 @@ thiserror = "1.0"
|
||||
tokio = { version = "1.10", features = ["full"] }
|
||||
toml = "0.5.8"
|
||||
url = "2.2"
|
||||
k256 = { version = "0.10", features = ["ecdsa", "sha256"] }
|
||||
|
||||
aes-gcm = "0.9.4"
|
||||
argon2 = { version = "0.3.2", features = ["std"] }
|
||||
|
||||
@@ -552,7 +552,7 @@ api_url = 'https://baz/api'
|
||||
.next()
|
||||
.map(|v| v.nymd_url)
|
||||
.unwrap();
|
||||
assert_eq!(nymd_url.as_ref(), "https://rpc.nyx.nodes.guru/");
|
||||
assert_eq!(nymd_url.as_ref(), "https://rpc.nymtech.net/");
|
||||
|
||||
let api_url = config
|
||||
.get_base_validators(WalletNetwork::MAINNET)
|
||||
|
||||
@@ -69,6 +69,11 @@ pub enum BackendError {
|
||||
#[from]
|
||||
source: reqwest::Error,
|
||||
},
|
||||
#[error("{source}")]
|
||||
K256Error {
|
||||
#[from]
|
||||
source: k256::ecdsa::Error,
|
||||
},
|
||||
#[error("failed to encrypt the given data with the provided password")]
|
||||
EncryptionError,
|
||||
#[error("failed to decrypt the given data with the provided password")]
|
||||
@@ -111,6 +116,8 @@ pub enum BackendError {
|
||||
UnknownCoinDenom(String),
|
||||
#[error("Network {network} doesn't have any associated registered coin denoms")]
|
||||
NoCoinsRegistered { network: Network },
|
||||
#[error("Signature error {0}")]
|
||||
SignatureError(String),
|
||||
}
|
||||
|
||||
impl Serialize for BackendError {
|
||||
|
||||
@@ -18,6 +18,7 @@ mod wallet_storage;
|
||||
|
||||
use crate::menu::AddDefaultSubmenus;
|
||||
use crate::operations::mixnet;
|
||||
use crate::operations::signatures;
|
||||
use crate::operations::simulate;
|
||||
use crate::operations::validator_api;
|
||||
use crate::operations::vesting;
|
||||
@@ -146,6 +147,8 @@ fn main() {
|
||||
simulate::mixnet::simulate_claim_operator_reward,
|
||||
simulate::mixnet::simulate_compound_operator_reward,
|
||||
simulate::mixnet::simulate_compound_delegator_reward,
|
||||
signatures::sign::sign,
|
||||
signatures::sign::verify,
|
||||
])
|
||||
.menu(Menu::new().add_default_app_submenu_if_macos())
|
||||
.run(tauri::generate_context!())
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod mixnet;
|
||||
pub mod signatures;
|
||||
pub mod simulate;
|
||||
pub mod validator_api;
|
||||
pub mod vesting;
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
pub mod sign;
|
||||
@@ -0,0 +1,140 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::error::BackendError;
|
||||
use crate::state::WalletState;
|
||||
use cosmrs::crypto::secp256k1::{Signature, VerifyingKey};
|
||||
use cosmrs::crypto::PublicKey;
|
||||
use cosmrs::AccountId;
|
||||
use k256::ecdsa::signature::Verifier;
|
||||
use serde::Serialize;
|
||||
use serde_json::json;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct SignatureOutputJson {
|
||||
pub account_id: String,
|
||||
pub public_key: PublicKey,
|
||||
pub signature: String,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn sign(
|
||||
message: String,
|
||||
state: tauri::State<'_, WalletState>,
|
||||
) -> Result<String, BackendError> {
|
||||
let guard = state.read().await;
|
||||
let client = guard.current_client()?;
|
||||
let wallet = client.nymd.signer();
|
||||
let derived_accounts = wallet.try_derive_accounts()?;
|
||||
let account = derived_accounts.first().ok_or_else(|| {
|
||||
log::error!(">>> Unable to derive account");
|
||||
BackendError::SignatureError("unable to derive account".to_string())
|
||||
})?;
|
||||
|
||||
log::info!("<<< Signing message");
|
||||
let signature = wallet.sign_raw_with_account(account, message.as_bytes())?;
|
||||
let signature_as_hex_string = signature.to_string();
|
||||
let output = SignatureOutputJson {
|
||||
account_id: account.address().to_string(),
|
||||
public_key: account.public_key(),
|
||||
signature: signature_as_hex_string.to_string(),
|
||||
};
|
||||
log::info!(">>> Signing data {}", json!(output),);
|
||||
Ok(signature_as_hex_string)
|
||||
}
|
||||
|
||||
async fn get_pubkey_from_account_address(
|
||||
address: &AccountId,
|
||||
state: &tauri::State<'_, WalletState>,
|
||||
) -> Result<PublicKey, BackendError> {
|
||||
log::info!("Getting public key for address {} from chain...", address);
|
||||
let guard = state.read().await;
|
||||
let client = guard.current_client()?;
|
||||
let account = client
|
||||
.nymd
|
||||
.get_account_details(address)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
log::error!("No account associated with address {}", address);
|
||||
BackendError::SignatureError(format!("No account associated with address {}", address))
|
||||
})?;
|
||||
let base_account = account.try_get_base_account()?;
|
||||
|
||||
base_account.pubkey.ok_or_else(|| {
|
||||
log::error!("No pubkey found for address {}", address);
|
||||
BackendError::SignatureError(format!("No pubkey found for address {}", address))
|
||||
})
|
||||
}
|
||||
|
||||
enum VerifyInputKind {
|
||||
PublicKey(PublicKey),
|
||||
AccountAddress(String),
|
||||
CurrentAccountAddress,
|
||||
}
|
||||
|
||||
impl TryFrom<Option<String>> for VerifyInputKind {
|
||||
type Error = BackendError;
|
||||
|
||||
fn try_from(value: Option<String>) -> Result<Self, Self::Error> {
|
||||
let key = match value {
|
||||
Some(key) => key,
|
||||
None => return Ok(VerifyInputKind::CurrentAccountAddress),
|
||||
};
|
||||
if key.trim().is_empty() {
|
||||
return Err(BackendError::SignatureError(
|
||||
"Please ensure the public key or address is not empty or whitespace".to_string(),
|
||||
));
|
||||
}
|
||||
let account_id = AccountId::from_str(&key);
|
||||
let key_from_json = PublicKey::from_json(&key);
|
||||
if account_id.is_err() && key_from_json.is_err() {
|
||||
return Err(BackendError::SignatureError(
|
||||
"Please ensure the public key or address is valid".to_string(),
|
||||
));
|
||||
}
|
||||
if let Ok(k) = key_from_json {
|
||||
Ok(VerifyInputKind::PublicKey(k))
|
||||
} else {
|
||||
Ok(VerifyInputKind::AccountAddress(key))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn verify(
|
||||
public_key_as_json_or_account_address: Option<String>,
|
||||
signature_as_hex: String,
|
||||
message: String,
|
||||
state: tauri::State<'_, WalletState>,
|
||||
) -> Result<(), BackendError> {
|
||||
let public_key = match VerifyInputKind::try_from(public_key_as_json_or_account_address)? {
|
||||
VerifyInputKind::PublicKey(key) => key,
|
||||
VerifyInputKind::AccountAddress(address) => {
|
||||
// get public key from the given account address
|
||||
get_pubkey_from_account_address(&AccountId::from_str(&address)?, &state).await?
|
||||
}
|
||||
VerifyInputKind::CurrentAccountAddress => {
|
||||
// get public key from current account address
|
||||
let guard = state.read().await;
|
||||
let client = guard.current_client()?;
|
||||
let address = client.nymd.address();
|
||||
get_pubkey_from_account_address(address, &state).await?
|
||||
}
|
||||
};
|
||||
|
||||
if public_key.type_url() != PublicKey::SECP256K1_TYPE_URL {
|
||||
return Err(BackendError::SignatureError(
|
||||
"Sorry, we only support secp256k1 public keys at the moment".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
log::info!("<<< Verifying signature [{}]", signature_as_hex);
|
||||
let verifying_key = VerifyingKey::from_sec1_bytes(&public_key.to_bytes())?;
|
||||
let signature = Signature::from_str(&signature_as_hex)?;
|
||||
let message_as_bytes = message.into_bytes();
|
||||
Ok(verifying_key
|
||||
.verify(&message_as_bytes, &signature)
|
||||
.map_err(|e| {
|
||||
log::error!(">>> Verification failed, wrong signature");
|
||||
e
|
||||
})?)
|
||||
}
|
||||
@@ -516,7 +516,7 @@ mod tests {
|
||||
"http://nymd_url.com/".parse().unwrap(),
|
||||
"http://foo.com".parse().unwrap(),
|
||||
"http://bar.com".parse().unwrap(),
|
||||
"https://rpc.nyx.nodes.guru".parse().unwrap(),
|
||||
"https://rpc.nymtech.net".parse().unwrap(),
|
||||
],
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -538,7 +538,7 @@ mod tests {
|
||||
"http://nymd_url.com/".parse().unwrap(),
|
||||
"http://foo.com".parse().unwrap(),
|
||||
"http://bar.com".parse().unwrap(),
|
||||
"https://rpc.nyx.nodes.guru".parse().unwrap(),
|
||||
"https://rpc.nymtech.net".parse().unwrap(),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"package": {
|
||||
"productName": "nym-wallet",
|
||||
"version": "1.0.8"
|
||||
"version": "1.0.9"
|
||||
},
|
||||
"build": {
|
||||
"distDir": "../dist",
|
||||
|
||||
@@ -8,3 +8,4 @@ export * from './utils';
|
||||
export * from './delegation';
|
||||
export * from './rewards';
|
||||
export * from './simulate';
|
||||
export * from './signature';
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { invokeWrapper } from './wrapper';
|
||||
|
||||
export const sign = async (message: string): Promise<string> => invokeWrapper<string>('sign', { message });
|
||||
|
||||
export const verify = async (
|
||||
signatureAsHex: string,
|
||||
message: string,
|
||||
publicKeyAsJsonOrAccountAddress?: string | null,
|
||||
): Promise<string> => invokeWrapper<string>('verify', { publicKeyAsJsonOrAccountAddress, signatureAsHex, message });
|
||||
@@ -11,7 +11,7 @@ anyhow = "1.0"
|
||||
argon2 = "0.4"
|
||||
base64 = "0.13"
|
||||
bip39 = "1.0"
|
||||
clap = { version = "3.0", features = ["derive"] }
|
||||
clap = { version = "3.2.20", features = ["derive"] }
|
||||
log = "0.4"
|
||||
pretty_env_logger = "0.4"
|
||||
serde_json = "1.0.0"
|
||||
|
||||
@@ -32,15 +32,15 @@ enum DecryptedAccount {
|
||||
struct Args {
|
||||
/// Password used to attempt to decrypt the logins found in the file. The option can be
|
||||
/// provided multiple times for files that require multiple passwords.
|
||||
#[clap(short, long, min_values(1), required = true)]
|
||||
#[clap(short, long, value_parser, required = true)]
|
||||
password: Vec<String>,
|
||||
|
||||
/// Path to the wallet file that will be decrypted.
|
||||
#[clap(short, long)]
|
||||
#[clap(short, long, value_parser)]
|
||||
file: String,
|
||||
|
||||
/// Raw mode. Skips trying to parse the decrypted content.
|
||||
#[clap(short, long)]
|
||||
#[clap(short, long, action)]
|
||||
raw: bool,
|
||||
}
|
||||
|
||||
|
||||
@@ -34,4 +34,5 @@ ordered-buffer = {path = "../../common/socks5/ordered-buffer"}
|
||||
proxy-helpers = { path = "../../common/socks5/proxy-helpers" }
|
||||
socks5-requests = { path = "../../common/socks5/requests" }
|
||||
statistics-common = { path = "../../common/statistics" }
|
||||
task = { path = "../../common/task" }
|
||||
websocket-requests = { path = "../../clients/native/websocket-requests" }
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
blockstream.info
|
||||
blockstream.com
|
||||
greenaddress.it
|
||||
electrum.org
|
||||
qtornado.com
|
||||
|
||||
@@ -7,6 +7,7 @@ use proxy_helpers::connection_controller::ConnectionReceiver;
|
||||
use proxy_helpers::proxy_runner::ProxyRunner;
|
||||
use socks5_requests::{ConnectionId, Message as Socks5Message, RemoteAddress, Response};
|
||||
use std::io;
|
||||
use task::ShutdownListener;
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
/// A TCP connection between the Socks5 service provider, which makes
|
||||
@@ -40,6 +41,7 @@ impl Connection {
|
||||
&mut self,
|
||||
mix_receiver: ConnectionReceiver,
|
||||
mix_sender: mpsc::UnboundedSender<(Socks5Message, Recipient)>,
|
||||
shutdown: ShutdownListener,
|
||||
) {
|
||||
let stream = self.conn.take().unwrap();
|
||||
let remote_source_address = "???".to_string(); // we don't know ip address of requester
|
||||
@@ -52,6 +54,7 @@ impl Connection {
|
||||
mix_receiver,
|
||||
mix_sender,
|
||||
connection_id,
|
||||
shutdown,
|
||||
)
|
||||
.run(move |conn_id, read_data, socket_closed| {
|
||||
(
|
||||
|
||||
@@ -19,6 +19,7 @@ use socks5_requests::{
|
||||
use statistics_common::collector::StatisticsSender;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use task::ShutdownListener;
|
||||
use tokio_tungstenite::tungstenite::protocol::Message;
|
||||
use websocket::WebsocketConnectionError;
|
||||
use websocket_requests::{requests::ClientRequest, responses::ServerResponse};
|
||||
@@ -134,6 +135,7 @@ impl ServiceProvider {
|
||||
return_address: Recipient,
|
||||
controller_sender: ControllerSender,
|
||||
mix_input_sender: mpsc::UnboundedSender<(Socks5Message, Recipient)>,
|
||||
shutdown: ShutdownListener,
|
||||
) {
|
||||
let mut conn = match Connection::new(conn_id, remote_addr.clone(), return_address).await {
|
||||
Ok(conn) => conn,
|
||||
@@ -170,7 +172,8 @@ impl ServiceProvider {
|
||||
);
|
||||
|
||||
// run the proxy on the connection
|
||||
conn.run_proxy(mix_receiver, mix_input_sender).await;
|
||||
conn.run_proxy(mix_receiver, mix_input_sender, shutdown)
|
||||
.await;
|
||||
|
||||
// proxy is done - remove the access channel from the controller
|
||||
controller_sender
|
||||
@@ -192,6 +195,7 @@ impl ServiceProvider {
|
||||
conn_id: ConnectionId,
|
||||
remote_addr: String,
|
||||
return_address: Recipient,
|
||||
shutdown: ShutdownListener,
|
||||
) {
|
||||
if !self.open_proxy && !self.outbound_request_filter.check(&remote_addr) {
|
||||
let log_msg = format!("Domain {:?} failed filter check", remote_addr);
|
||||
@@ -218,6 +222,7 @@ impl ServiceProvider {
|
||||
return_address,
|
||||
controller_sender_clone,
|
||||
mix_input_sender_clone,
|
||||
shutdown,
|
||||
)
|
||||
.await
|
||||
});
|
||||
@@ -241,6 +246,7 @@ impl ServiceProvider {
|
||||
controller_sender: &mut ControllerSender,
|
||||
mix_input_sender: &mpsc::UnboundedSender<(Socks5Message, Recipient)>,
|
||||
stats_collector: Option<ServiceStatisticsCollector>,
|
||||
shutdown: ShutdownListener,
|
||||
) {
|
||||
let deserialized_msg = match Socks5Message::try_from_bytes(raw_request) {
|
||||
Ok(msg) => msg,
|
||||
@@ -265,6 +271,7 @@ impl ServiceProvider {
|
||||
req.conn_id,
|
||||
req.remote_addr,
|
||||
req.return_address,
|
||||
shutdown,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -302,8 +309,12 @@ impl ServiceProvider {
|
||||
let (mix_input_sender, mix_input_receiver) =
|
||||
mpsc::unbounded::<(Socks5Message, Recipient)>();
|
||||
|
||||
// controller for managing all active connections
|
||||
let (mut active_connections_controller, mut controller_sender) = Controller::new();
|
||||
// Controller for managing all active connections.
|
||||
// We provide it with a ShutdownListener since it requires it, even though for the network
|
||||
// requester shutdown signalling is not yet fully implemented.
|
||||
let shutdown = task::ShutdownNotifier::default();
|
||||
let (mut active_connections_controller, mut controller_sender) =
|
||||
Controller::new(shutdown.subscribe());
|
||||
tokio::spawn(async move {
|
||||
active_connections_controller.run().await;
|
||||
});
|
||||
@@ -353,6 +364,7 @@ impl ServiceProvider {
|
||||
&mut controller_sender,
|
||||
&mix_input_sender,
|
||||
stats_collector.clone(),
|
||||
shutdown.subscribe(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
+49
-13
@@ -357,6 +357,46 @@
|
||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
|
||||
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
|
||||
|
||||
"@jridgewell/gen-mapping@^0.3.0":
|
||||
version "0.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9"
|
||||
integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==
|
||||
dependencies:
|
||||
"@jridgewell/set-array" "^1.0.1"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||
"@jridgewell/trace-mapping" "^0.3.9"
|
||||
|
||||
"@jridgewell/resolve-uri@^3.0.3":
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
|
||||
integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
|
||||
|
||||
"@jridgewell/set-array@^1.0.1":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
|
||||
integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
|
||||
|
||||
"@jridgewell/source-map@^0.3.2":
|
||||
version "0.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb"
|
||||
integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==
|
||||
dependencies:
|
||||
"@jridgewell/gen-mapping" "^0.3.0"
|
||||
"@jridgewell/trace-mapping" "^0.3.9"
|
||||
|
||||
"@jridgewell/sourcemap-codec@^1.4.10":
|
||||
version "1.4.14"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
|
||||
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
|
||||
|
||||
"@jridgewell/trace-mapping@^0.3.9":
|
||||
version "0.3.15"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz#aba35c48a38d3fd84b37e66c9c0423f9744f9774"
|
||||
integrity sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==
|
||||
dependencies:
|
||||
"@jridgewell/resolve-uri" "^3.0.3"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||
|
||||
"@mui/base@5.0.0-alpha.55":
|
||||
version "5.0.0-alpha.55"
|
||||
resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-alpha.55.tgz#564d6c9374b4cfe86a4493512356047636076d4f"
|
||||
@@ -1321,10 +1361,10 @@ acorn-jsx@^5.3.1:
|
||||
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
|
||||
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
|
||||
|
||||
acorn@^8.7.0:
|
||||
version "8.7.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf"
|
||||
integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==
|
||||
acorn@^8.5.0, acorn@^8.7.0:
|
||||
version "8.8.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8"
|
||||
integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==
|
||||
|
||||
ajv@^6.10.0, ajv@^6.12.4:
|
||||
version "6.12.6"
|
||||
@@ -3339,11 +3379,6 @@ source-map@^0.6.0, source-map@^0.6.1:
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||
|
||||
source-map@~0.7.2:
|
||||
version "0.7.3"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
|
||||
integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
|
||||
|
||||
stable@^0.1.8:
|
||||
version "0.1.8"
|
||||
resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
|
||||
@@ -3449,12 +3484,13 @@ term-size@^2.2.1:
|
||||
integrity sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==
|
||||
|
||||
terser@^5.2.0:
|
||||
version "5.10.0"
|
||||
resolved "https://registry.yarnpkg.com/terser/-/terser-5.10.0.tgz#b86390809c0389105eb0a0b62397563096ddafcc"
|
||||
integrity sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==
|
||||
version "5.15.0"
|
||||
resolved "https://registry.yarnpkg.com/terser/-/terser-5.15.0.tgz#e16967894eeba6e1091509ec83f0c60e179f2425"
|
||||
integrity sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==
|
||||
dependencies:
|
||||
"@jridgewell/source-map" "^0.3.2"
|
||||
acorn "^8.5.0"
|
||||
commander "^2.20.0"
|
||||
source-map "~0.7.2"
|
||||
source-map-support "~0.5.20"
|
||||
|
||||
text-table@^0.2.0:
|
||||
|
||||
@@ -209,6 +209,7 @@ impl PacketSender {
|
||||
// currently we do not care about acks at all, but we must keep the channel alive
|
||||
// so that the gateway client would not crash
|
||||
let (ack_sender, ack_receiver) = mpsc::unbounded();
|
||||
|
||||
let mut gateway_client = GatewayClient::new(
|
||||
address,
|
||||
Arc::clone(&fresh_gateway_client_data.local_identity),
|
||||
@@ -219,6 +220,7 @@ impl PacketSender {
|
||||
ack_sender,
|
||||
fresh_gateway_client_data.gateway_response_timeout,
|
||||
Some(fresh_gateway_client_data.bandwidth_controller.clone()),
|
||||
None,
|
||||
);
|
||||
|
||||
gateway_client
|
||||
|
||||
@@ -8752,7 +8752,7 @@ decimal.js@^10.2.1:
|
||||
decode-uri-component@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
|
||||
integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
|
||||
integrity sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==
|
||||
|
||||
decompress-response@^4.2.0:
|
||||
version "4.2.1"
|
||||
@@ -10218,7 +10218,7 @@ fill-range@^7.0.1:
|
||||
filter-obj@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b"
|
||||
integrity sha1-mzERErxsYSehbgFsbF1/GeCAXFs=
|
||||
integrity sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==
|
||||
|
||||
finalhandler@~1.1.2:
|
||||
version "1.1.2"
|
||||
@@ -10598,7 +10598,16 @@ get-caller-file@^2.0.5:
|
||||
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
||||
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
||||
|
||||
get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1:
|
||||
get-intrinsic@^1.0.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598"
|
||||
integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==
|
||||
dependencies:
|
||||
function-bind "^1.1.1"
|
||||
has "^1.0.3"
|
||||
has-symbols "^1.0.3"
|
||||
|
||||
get-intrinsic@^1.1.0, get-intrinsic@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6"
|
||||
integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==
|
||||
@@ -10927,7 +10936,7 @@ has-glob@^1.0.0:
|
||||
dependencies:
|
||||
is-glob "^3.0.0"
|
||||
|
||||
has-symbols@^1.0.1, has-symbols@^1.0.2:
|
||||
has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
|
||||
integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
|
||||
@@ -11925,11 +11934,11 @@ is-shared-array-buffer@^1.0.1:
|
||||
integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==
|
||||
|
||||
is-ssh@^1.3.0:
|
||||
version "1.3.3"
|
||||
resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.3.tgz#7f133285ccd7f2c2c7fc897b771b53d95a2b2c7e"
|
||||
integrity sha512-NKzJmQzJfEEma3w5cJNcUMxoXfDjz0Zj0eyCalHn2E6VOwlzjZo0yuO2fcBSf8zhFuVCL/82/r5gRcoi6aEPVQ==
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.4.0.tgz#4f8220601d2839d8fa624b3106f8e8884f01b8b2"
|
||||
integrity sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==
|
||||
dependencies:
|
||||
protocols "^1.1.0"
|
||||
protocols "^2.0.1"
|
||||
|
||||
is-stream@^1.1.0:
|
||||
version "1.1.0"
|
||||
@@ -14450,11 +14459,16 @@ object-copy@^0.1.0:
|
||||
define-property "^0.2.5"
|
||||
kind-of "^3.0.3"
|
||||
|
||||
object-inspect@^1.11.0, object-inspect@^1.9.0:
|
||||
object-inspect@^1.11.0:
|
||||
version "1.12.0"
|
||||
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0"
|
||||
integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==
|
||||
|
||||
object-inspect@^1.9.0:
|
||||
version "1.12.2"
|
||||
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea"
|
||||
integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==
|
||||
|
||||
object-is@^1.0.1:
|
||||
version "1.1.5"
|
||||
resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac"
|
||||
@@ -14933,9 +14947,9 @@ parse-json@^5.0.0, parse-json@^5.2.0:
|
||||
lines-and-columns "^1.1.6"
|
||||
|
||||
parse-path@^4.0.0:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.3.tgz#82d81ec3e071dcc4ab49aa9f2c9c0b8966bb22bf"
|
||||
integrity sha512-9Cepbp2asKnWTJ9x2kpw6Fe8y9JDbqwahGCTvklzd/cEq5C5JC59x2Xb0Kx+x0QZ8bvNquGO8/BWP0cwBHzSAA==
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.4.tgz#4bf424e6b743fb080831f03b536af9fc43f0ffea"
|
||||
integrity sha512-Z2lWUis7jlmXC1jeOG9giRO2+FsuyNipeQ43HAjqAZjwSe3SEf+q/84FGPHoso3kyntbxa4c4i77t3m6fGf8cw==
|
||||
dependencies:
|
||||
is-ssh "^1.3.0"
|
||||
protocols "^1.4.0"
|
||||
@@ -14950,9 +14964,9 @@ parse-png@^1.0.0, parse-png@^1.1.1:
|
||||
pngjs "^3.2.0"
|
||||
|
||||
parse-url@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-6.0.0.tgz#f5dd262a7de9ec00914939220410b66cff09107d"
|
||||
integrity sha512-cYyojeX7yIIwuJzledIHeLUBVJ6COVLeT4eF+2P6aKVzwvgKQPndCBv3+yQ7pcWjqToYwaligxzSYNNmGoMAvw==
|
||||
version "6.0.5"
|
||||
resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-6.0.5.tgz#4acab8982cef1846a0f8675fa686cef24b2f6f9b"
|
||||
integrity sha512-e35AeLTSIlkw/5GFq70IN7po8fmDUjpDPY1rIK+VubRfsUvBonjQ+PBZG+vWMACnQSmNlvl524IucoDmcioMxA==
|
||||
dependencies:
|
||||
is-ssh "^1.3.0"
|
||||
normalize-url "^6.1.0"
|
||||
@@ -15804,11 +15818,16 @@ protobufjs@~6.11.2, protobufjs@~6.11.3:
|
||||
"@types/node" ">=13.7.0"
|
||||
long "^4.0.0"
|
||||
|
||||
protocols@^1.1.0, protocols@^1.4.0:
|
||||
protocols@^1.4.0:
|
||||
version "1.4.8"
|
||||
resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.8.tgz#48eea2d8f58d9644a4a32caae5d5db290a075ce8"
|
||||
integrity sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==
|
||||
|
||||
protocols@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/protocols/-/protocols-2.0.1.tgz#8f155da3fc0f32644e83c5782c8e8212ccf70a86"
|
||||
integrity sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==
|
||||
|
||||
proxy-addr@~2.0.7:
|
||||
version "2.0.7"
|
||||
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
|
||||
@@ -15903,13 +15922,20 @@ qs@6.9.7:
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.7.tgz#4610846871485e1e048f44ae3b94033f0e675afe"
|
||||
integrity sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==
|
||||
|
||||
qs@^6.10.0, qs@^6.9.4:
|
||||
qs@^6.10.0:
|
||||
version "6.10.3"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e"
|
||||
integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==
|
||||
dependencies:
|
||||
side-channel "^1.0.4"
|
||||
|
||||
qs@^6.9.4:
|
||||
version "6.11.0"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a"
|
||||
integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==
|
||||
dependencies:
|
||||
side-channel "^1.0.4"
|
||||
|
||||
qs@~6.5.2:
|
||||
version "6.5.3"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad"
|
||||
@@ -17595,7 +17621,7 @@ stream-to@~0.2.0:
|
||||
strict-uri-encode@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546"
|
||||
integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY=
|
||||
integrity sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==
|
||||
|
||||
string-length@^4.0.1:
|
||||
version "4.0.2"
|
||||
@@ -18057,9 +18083,9 @@ terser-webpack-plugin@^5.0.3, terser-webpack-plugin@^5.1.3:
|
||||
terser "^5.7.2"
|
||||
|
||||
terser@^4.1.2, terser@^4.6.3:
|
||||
version "4.8.0"
|
||||
resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17"
|
||||
integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==
|
||||
version "4.8.1"
|
||||
resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.1.tgz#a00e5634562de2239fd404c649051bf6fc21144f"
|
||||
integrity sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==
|
||||
dependencies:
|
||||
commander "^2.20.0"
|
||||
source-map "~0.6.1"
|
||||
|
||||
Reference in New Issue
Block a user