Compare commits

..

1 Commits

Author SHA1 Message Date
Jędrzej Stuczyński cd05759405 disabled default use_legacy_framed_packet_version 2022-12-09 09:55:34 +00:00
74 changed files with 538 additions and 965 deletions
-20
View File
@@ -2,26 +2,6 @@
Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [v1.1.3] (2022-12-13)
### Changed
- validator-api: can recover from shutdown during DKG process ([#1872])
- clients: deduplicate gateway inititialization, part of work towards a rust-sdk
- clients: keep all transmission lanes going at all times by making priority probabilistic
### Fixed
- network-requester: fix bug where websocket connection disconnect resulted in success error code
- clients: fix a few panics handling the gateway-client
- mixnode, gateway, validator-api: Use mainnet values as defaults for URLs and mixnet contract ([#1884])
[#1872]: https://github.com/nymtech/nym/pull/1872
[#1884]: https://github.com/nymtech/nym/pull/1884
## [v1.1.2]
### Changed
Generated
+27 -7
View File
@@ -603,7 +603,6 @@ dependencies = [
"pemstore",
"rand 0.7.3",
"serde",
"serde_json",
"sled",
"tap",
"task",
@@ -2669,7 +2668,7 @@ checksum = "fe435806c197dfeaa5efcded5e623c4b8230fd28fdf1e91e7a86e40ef2acbf90"
dependencies = [
"arrayref",
"no-std-compat",
"snafu",
"snafu 0.7.1",
]
[[package]]
@@ -3373,7 +3372,7 @@ dependencies = [
"proxy-helpers",
"rand 0.7.3",
"serde",
"serde_json",
"snafu 0.6.10",
"socks5-requests",
"tap",
"task",
@@ -5006,9 +5005,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.89"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7"
dependencies = [
"itoa 1.0.1",
"ryu",
@@ -5196,6 +5195,16 @@ version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
[[package]]
name = "snafu"
version = "0.6.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eab12d3c261b2308b0d80c26fffb58d17eba81a4be97890101f416b478c79ca7"
dependencies = [
"doc-comment",
"snafu-derive 0.6.10",
]
[[package]]
name = "snafu"
version = "0.7.1"
@@ -5203,7 +5212,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5177903bf45656592d9eb5c0e22f408fc023aae51dbe2088889b71633ba451f2"
dependencies = [
"doc-comment",
"snafu-derive",
"snafu-derive 0.7.1",
]
[[package]]
name = "snafu-derive"
version = "0.6.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1508efa03c362e23817f96cde18abed596a25219a8b2c66e8db33c03543d315b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
@@ -6517,7 +6537,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "vesting-contract"
version = "1.1.2"
version = "1.1.0"
dependencies = [
"contracts-common",
"cosmwasm-std",
+2 -3
View File
@@ -1,6 +1,6 @@
[package]
name = "client-core"
version = "1.1.3"
version = "1.1.2"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021"
@@ -13,12 +13,11 @@ humantime-serde = "1.0"
log = "0.4"
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.89"
sled = { version = "0.34", optional = true }
tap = "1.0.1"
thiserror = "1.0.34"
tokio = { version = "1.21.2", features = ["time", "macros"]}
url = { version ="2.2", features = ["serde"] }
tokio = { version = "1.21.2", features = ["time", "macros"]}
# internal
config = { path = "../../common/config" }
@@ -23,11 +23,12 @@ use gateway_client::{
AcknowledgementReceiver, AcknowledgementSender, GatewayClient, MixnetMessageReceiver,
MixnetMessageSender,
};
use log::{debug, info};
use log::info;
use nymsphinx::addressing::clients::Recipient;
use nymsphinx::addressing::nodes::NodeIdentity;
#[cfg(feature = "reply-surb")]
use std::path::PathBuf;
#[cfg(feature = "reply-surb")]
use tap::TapFallible;
use task::{ShutdownListener, ShutdownNotifier};
use url::Url;
@@ -235,22 +236,22 @@ impl<'a> BaseClientBuilder<'a> {
mixnet_message_sender: MixnetMessageSender,
ack_sender: AcknowledgementSender,
shutdown: ShutdownListener,
) -> Result<GatewayClient, ClientCoreError> {
) -> GatewayClient {
let gateway_id = self.gateway_config.gateway_id.clone();
if gateway_id.is_empty() {
return Err(ClientCoreError::GatewayIdUnknown);
panic!("The identity of the gateway is unknown - did you run `nym-client` init?")
}
let gateway_owner = self.gateway_config.gateway_owner.clone();
if gateway_owner.is_empty() {
return Err(ClientCoreError::GatewayOwnerUnknown);
panic!("The owner of the gateway is unknown - did you run `nym-client` init?")
}
let gateway_address = self.gateway_config.gateway_listener.clone();
if gateway_address.is_empty() {
return Err(ClientCoreError::GatwayAddressUnknown);
panic!("The address of the gateway is unknown - did you run `nym-client` init?")
}
let gateway_identity = identity::PublicKey::from_base58_string(gateway_id)
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
.expect("provided gateway id is invalid!");
// disgusting wasm workaround since there's no key persistence there (nor `client init`)
let shared_key = if self.key_manager.gateway_key_set() {
@@ -277,10 +278,9 @@ impl<'a> BaseClientBuilder<'a> {
gateway_client
.authenticate_and_start()
.await
.tap_err(|err| {
log::error!("Could not authenticate and start up the gateway connection - {err}")
})?;
Ok(gateway_client)
.expect("could not authenticate and start up the gateway connection");
gateway_client
}
// future responsible for periodically polling directory server and updating
@@ -375,7 +375,7 @@ impl<'a> BaseClientBuilder<'a> {
let gateway_client = self
.start_gateway_client(mixnet_messages_sender, ack_sender, shutdown.subscribe())
.await?;
.await;
// The sphinx_message_sender is the transmitter for any component generating sphinx packets
// that are to be sent to the mixnet. They are used by cover traffic stream and real
@@ -412,8 +412,8 @@ impl<'a> BaseClientBuilder<'a> {
);
}
debug!("Core client startup finished!");
debug!("The address of this client is: {}", self.as_mix_recipient());
info!("Client startup finished!");
info!("The address of this client is: {}", self.as_mix_recipient());
Ok(BaseClient {
client_input: ClientInputStatus::AwaitingProducer {
@@ -191,7 +191,7 @@ impl LoopCoverTrafficStream<OsRng> {
// However it's still useful to alert the user that the gateway or the link to
// the gateway can't keep up. Either due to insufficient bandwidth on the
// client side, or that the gateway is overloaded.
log::warn!("Failed to send sphinx packet - gateway or connection to gatway can't keep up");
log::warn!("Failed to send: gateway appears to not keep up");
}
TrySendError::Closed(_) => {
log::warn!("Failed to send cover message - channel closed");
@@ -489,7 +489,7 @@ where
fn log_status_infrequent(&self) {
if self.sending_delay_controller.current_multiplier() > 1 {
log::warn!(
"Unable to send packets at the default rate - rate reduced by setting the delay multiplier set to: {}",
"Unable to send packets fast enough - sending delay multiplier set to: {}",
self.sending_delay_controller.current_multiplier()
);
}
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use client_connections::TransmissionLane;
use rand::{seq::SliceRandom, Rng};
use rand::seq::SliceRandom;
use std::{
collections::{HashMap, HashSet, VecDeque},
time::Duration,
@@ -116,15 +116,9 @@ impl TransmissionBuffer {
lanes.choose(&mut rand::thread_rng()).copied()
}
// 2/3 chance to pick from the old lanes
fn pick_random_old_lane(&self) -> Option<TransmissionLane> {
let rand = &mut rand::thread_rng();
if rand.gen_ratio(2, 3) {
let lanes = self.get_oldest_set();
lanes.choose(rand).copied()
} else {
self.pick_random_lane().copied()
}
let lanes = self.get_oldest_set();
lanes.choose(&mut rand::thread_rng()).copied()
}
fn pop_front_from_lane(&mut self, lane: &TransmissionLane) -> Option<RealMessage> {
+4 -10
View File
@@ -34,10 +34,6 @@ pub fn missing_string_value() -> String {
MISSING_VALUE.to_string()
}
pub trait ClientCoreConfigTrait {
fn get_gateway_endpoint(&self) -> &GatewayEndpointConfig;
}
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Config<T> {
@@ -49,12 +45,6 @@ pub struct Config<T> {
debug: DebugConfig,
}
impl<T> ClientCoreConfigTrait for Config<T> {
fn get_gateway_endpoint(&self) -> &GatewayEndpointConfig {
&self.client.gateway_endpoint
}
}
impl<T> Config<T> {
pub fn new<S: Into<String>>(id: S) -> Self
where
@@ -230,6 +220,10 @@ impl<T> Config<T> {
&self.client.gateway_endpoint
}
pub fn get_gateway_endpoint(&self) -> &GatewayEndpointConfig {
&self.client.gateway_endpoint
}
pub fn get_database_path(&self) -> PathBuf {
self.client.database_path.clone()
}
-12
View File
@@ -26,8 +26,6 @@ pub enum ClientCoreError {
NoGatewayWithId(String),
#[error("No gateways on network")]
NoGatewaysOnNetwork,
#[error("Failed to setup gateway")]
FailedToSetupGateway,
#[error("List of validator apis is empty")]
ListOfValidatorApisIsEmpty,
#[error("Could not load existing gateway configuration: {0}")]
@@ -35,16 +33,6 @@ pub enum ClientCoreError {
#[error("The current network topology seem to be insufficient to route any packets through")]
InsufficientNetworkTopology,
#[error("The gateway id is invalid - {0}")]
UnableToCreatePublicKeyFromGatewayId(Ed25519RecoveryError),
#[error("The identity of the gateway is unknwown - did you run init?")]
GatewayIdUnknown,
#[error("The owner of the gateway is unknown - did you run init?")]
GatewayOwnerUnknown,
#[error("The address of the gateway is unknown - did you run init?")]
GatwayAddressUnknown,
#[error("Unexpected exit")]
UnexpectedExit,
}
@@ -1,14 +1,22 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
//! Collection of initialization steps used by client implementations
use std::{sync::Arc, time::Duration};
use rand::{rngs::OsRng, seq::SliceRandom, thread_rng};
use tap::TapFallible;
use url::Url;
use config::NymConfig;
use crypto::asymmetric::identity;
use crypto::asymmetric::{encryption, identity};
use gateway_client::GatewayClient;
use gateway_requests::registration::handshake::SharedKeys;
use nymsphinx::addressing::clients::Recipient;
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,
@@ -16,9 +24,9 @@ use crate::{
error::ClientCoreError,
};
pub(super) async fn query_gateway_details(
pub async fn query_gateway_details(
validator_servers: Vec<Url>,
chosen_gateway_id: Option<String>,
chosen_gateway_id: Option<&str>,
) -> Result<gateway::Node, ClientCoreError> {
let validator_api = validator_servers
.choose(&mut thread_rng())
@@ -51,6 +59,26 @@ pub(super) async fn query_gateway_details(
}
}
pub async fn register_with_gateway_and_store_keys<T>(
gateway_details: gateway::Node,
config: &Config<T>,
) -> 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?;
key_manager.insert_gateway_shared_key(shared_keys);
let pathfinder = ClientKeyPathfinder::new_from_config(config);
Ok(key_manager
.store_keys(&pathfinder)
.tap_err(|err| log::error!("Failed to generate keys: {err}"))?)
}
async fn register_with_gateway(
gateway: &gateway::Node,
our_identity: Arc<identity::KeyPair>,
@@ -74,22 +102,46 @@ async fn register_with_gateway(
Ok(shared_keys)
}
pub(super) async fn register_with_gateway_and_store_keys<T>(
gateway_details: gateway::Node,
config: &Config<T>,
) -> Result<(), ClientCoreError>
pub fn show_address<T>(config: &Config<T>) -> Result<(), ClientCoreError>
where
T: NymConfig,
T: config::NymConfig,
{
let mut rng = OsRng;
let mut key_manager = KeyManager::new(&mut rng);
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(),
))
.tap_err(|_| log::error!("Failed to read stored identity key files"))?;
Ok(identity_keypair)
}
let shared_keys =
register_with_gateway(&gateway_details, key_manager.identity_keypair()).await?;
key_manager.insert_gateway_shared_key(shared_keys);
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(),
))
.tap_err(|_| log::error!("Failed to read stored sphinx key files"))?;
Ok(sphinx_keypair)
}
let pathfinder = ClientKeyPathfinder::new_from_config(config);
Ok(key_manager
.store_keys(&pathfinder)
.tap_err(|err| log::error!("Failed to generate keys: {err}"))?)
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())?,
);
println!("\nThe address of this client is: {}", client_recipient);
Ok(())
}
-191
View File
@@ -1,191 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
//! Collection of initialization steps used by client implementations
use std::fmt::Display;
use nymsphinx::addressing::{clients::Recipient, nodes::NodeIdentity};
use serde::Serialize;
use tap::TapFallible;
use config::NymConfig;
use crypto::asymmetric::{encryption, identity};
use crate::{
config::{
persistence::key_pathfinder::ClientKeyPathfinder, ClientCoreConfigTrait, Config,
GatewayEndpointConfig,
},
error::ClientCoreError,
init::helpers::{query_gateway_details, register_with_gateway_and_store_keys},
};
mod helpers;
#[derive(Debug, Serialize)]
pub struct InitResults {
version: String,
id: String,
identity_key: String,
encryption_key: String,
gateway_id: String,
gateway_listener: String,
}
impl InitResults {
pub fn new<T>(config: &Config<T>, address: &Recipient) -> Self
where
T: NymConfig,
{
Self {
version: config.get_version().to_string(),
id: config.get_id(),
identity_key: address.identity().to_base58_string(),
encryption_key: address.encryption_key().to_base58_string(),
gateway_id: config.get_gateway_id(),
gateway_listener: config.get_gateway_listener(),
}
}
}
impl Display for InitResults {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "Version: {}", self.version)?;
writeln!(f, "ID: {}", self.id)?;
writeln!(f, "Identity key: {}", self.identity_key)?;
writeln!(f, "Encryption: {}", self.encryption_key)?;
writeln!(f, "Gateway ID: {}", self.gateway_id)?;
write!(f, "Gateway: {}", self.gateway_listener)
}
}
/// Convenience function for setting up the gateway for a client. Depending on the arguments given
/// it will do the sensible thing.
pub async fn setup_gateway<C: NymConfig + ClientCoreConfigTrait, T: NymConfig>(
register_gateway: bool,
user_chosen_gateway_id: Option<String>,
config: &Config<T>,
) -> Result<GatewayEndpointConfig, ClientCoreError> {
let id = config.get_id();
if register_gateway {
register_with_gateway(user_chosen_gateway_id, config).await
} else if let Some(user_chosen_gateway_id) = user_chosen_gateway_id {
config_gateway_with_existing_keys(user_chosen_gateway_id, config).await
} else {
reuse_existing_gateway_config::<C>(&id)
}
}
/// Get the gateway details by querying the validator-api. Either pick one at random or use
/// the chosen one if it's among the available ones.
/// Saves keys to disk, specified by the paths in `config`.
pub async fn register_with_gateway<T: NymConfig>(
user_chosen_gateway_id: Option<String>,
config: &Config<T>,
) -> Result<GatewayEndpointConfig, ClientCoreError> {
println!("Configuring gateway");
let gateway =
query_gateway_details(config.get_validator_api_endpoints(), user_chosen_gateway_id).await?;
log::debug!("Querying gateway gives: {}", gateway);
// Registering with gateway by setting up and writing shared keys to disk
log::trace!("Registering gateway");
register_with_gateway_and_store_keys(gateway.clone(), config).await?;
println!("Saved all generated keys");
Ok(gateway.into())
}
/// Set the gateway using the usual procedue of querying the validator-api, but don't register or
/// create any keys.
/// This assumes that the user knows what they are doing, and that the existing keys are valid for
/// the gateway being used
pub async fn config_gateway_with_existing_keys<T: NymConfig>(
user_chosen_gateway_id: String,
config: &Config<T>,
) -> Result<GatewayEndpointConfig, ClientCoreError> {
println!("Using gateway provided by user, keeping existing keys");
let gateway = query_gateway_details(
config.get_validator_api_endpoints(),
Some(user_chosen_gateway_id),
)
.await?;
log::debug!("Querying gateway gives: {}", gateway);
Ok(gateway.into())
}
/// Read and reuse the existing gateway configuration from a file that was generate earlier.
pub fn reuse_existing_gateway_config<T: NymConfig + ClientCoreConfigTrait>(
id: &str,
) -> Result<GatewayEndpointConfig, ClientCoreError> {
println!("Not registering gateway, will reuse existing config and keys");
T::load_from_file(Some(id))
.map(|existing_config| existing_config.get_gateway_endpoint().clone())
.map_err(|err| {
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)
})
}
/// Get the client address by loading the keys from stored files.
pub fn get_client_address_from_stored_keys<T>(
config: &Config<T>,
) -> Result<Recipient, ClientCoreError>
where
T: config::NymConfig,
{
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(),
))
.tap_err(|_| log::error!("Failed to read stored identity key files"))?;
Ok(identity_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(),
))
.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 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())?,
);
Ok(client_recipient)
}
pub fn output_to_json<T: Serialize>(init_results: &T, output_file: &str) {
match std::fs::File::create(output_file) {
Ok(file) => match serde_json::to_writer_pretty(file, init_results) {
Ok(_) => println!("Saved: {}", output_file),
Err(err) => eprintln!("Could not save {}: {}", output_file, err),
},
Err(err) => eprintln!("Could not save {}: {}", output_file, err),
}
}
-1
View File
@@ -98,7 +98,6 @@ pub(crate) async fn get_credential(state: &State, shared_storage: PersistentStor
&coconut_api_clients,
)
.await?;
println!("Signature: {:?}", signature.to_bs58());
shared_storage
.insert_coconut_credential(
state.amount.to_string(),
+2 -3
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "1.1.3"
version = "1.1.2"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
@@ -26,9 +26,7 @@ log = "0.4" # self explanatory
pretty_env_logger = "0.4" # for formatting log messages
rand = { version = "0.7.3", features = ["wasm-bindgen"] } # rng-related traits + some rng implementation to use
serde = { version = "1.0.104", features = ["derive"] } # for config serialization/deserialization
serde_json = "1.0"
sled = "0.34" # for storage of replySURB decryption keys
tap = "1.0.1"
thiserror = "1.0.34"
tokio = { version = "1.21.2", features = ["rt-multi-thread", "net", "signal"] } # async runtime
tokio-tungstenite = "0.14" # websocket
@@ -53,6 +51,7 @@ topology = { path = "../../common/topology" }
validator-client = { path = "../../common/client-libs/validator-client", features = ["nymd-client"] }
version-checker = { path = "../../common/version-checker" }
websocket-requests = { path = "websocket-requests" }
tap = "1.0.1"
[features]
coconut = ["coconut-interface", "credentials", "credentials/coconut", "gateway-requests/coconut", "gateway-client/coconut", "client-core/coconut"]
+2 -8
View File
@@ -2,8 +2,8 @@
// SPDX-License-Identifier: Apache-2.0
use crate::client::config::template::config_template;
use client_core::config::Config as BaseConfig;
pub use client_core::config::MISSING_VALUE;
use client_core::config::{ClientCoreConfigTrait, Config as BaseConfig};
use config::defaults::DEFAULT_WEBSOCKET_LISTENING_PORT;
use config::NymConfig;
use serde::{Deserialize, Serialize};
@@ -28,7 +28,7 @@ impl SocketType {
}
}
pub fn is_websocket(self) -> bool {
pub fn is_websocket(&self) -> bool {
matches!(self, SocketType::WebSocket)
}
}
@@ -73,12 +73,6 @@ impl NymConfig for Config {
}
}
impl ClientCoreConfigTrait for Config {
fn get_gateway_endpoint(&self) -> &client_core::config::GatewayEndpointConfig {
self.base.get_gateway_endpoint()
}
}
impl Config {
pub fn new<S: Into<String>>(id: S) -> Self {
Config {
+75 -68
View File
@@ -1,18 +1,13 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::fmt::Display;
use clap::Args;
use client_core::{config::GatewayEndpointConfig, error::ClientCoreError};
use config::NymConfig;
use nymsphinx::addressing::clients::Recipient;
use serde::Serialize;
use tap::TapFallible;
use crate::{
client::config::Config,
commands::{override_config, OverrideConfig},
error::ClientError,
};
#[derive(Args, Clone)]
@@ -60,10 +55,6 @@ pub(crate) struct Init {
#[cfg(feature = "coconut")]
#[clap(long)]
enabled_credentials_mode: bool,
/// Save a summary of the initialization to a json file
#[clap(long)]
output_json: bool,
}
impl From<Init> for OverrideConfig {
@@ -82,30 +73,7 @@ impl From<Init> for OverrideConfig {
}
}
#[derive(Debug, Serialize)]
pub struct InitResults {
#[serde(flatten)]
client_core: client_core::init::InitResults,
client_listening_port: String,
}
impl InitResults {
fn new(config: &Config, address: &Recipient) -> Self {
Self {
client_core: client_core::init::InitResults::new(config.get_base(), address),
client_listening_port: config.get_listening_port().to_string(),
}
}
}
impl Display for InitResults {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "{}", self.client_core)?;
write!(f, "Client listening port: {}", self.client_listening_port)
}
}
pub(crate) async fn execute(args: &Init) -> Result<(), ClientError> {
pub(crate) async fn execute(args: &Init) {
println!("Initialising client...");
let id = &args.id;
@@ -129,44 +97,25 @@ pub(crate) async fn execute(args: &Init) -> Result<(), ClientError> {
let register_gateway = !already_init || user_wants_force_register;
// Attempt to use a user-provided gateway, if possible
let user_chosen_gateway_id = args.gateway.clone();
let user_chosen_gateway_id = args.gateway.as_deref();
// Load and potentially override config
let mut config = override_config(Config::new(id), OverrideConfig::from(args.clone()));
// Setup gateway by either registering a new one, or creating a new config from the selected
// one but with keys kept, or reusing the gateway configuration.
let gateway = client_core::init::setup_gateway::<Config, _>(
register_gateway,
user_chosen_gateway_id,
config.get_base(),
)
.await
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?;
let mut config = Config::new(id);
let override_config_fields = OverrideConfig::from(args.clone());
config = override_config(config, override_config_fields);
let gateway = setup_gateway(id, register_gateway, user_chosen_gateway_id, &config)
.await
.unwrap_or_else(|err| {
eprintln!("Failed to setup gateway\nError: {err}");
std::process::exit(1)
});
config.get_base_mut().with_gateway_endpoint(gateway);
config.save_to_file(None).tap_err(|_| {
log::error!("Failed to save the config file");
})?;
print_saved_config(&config);
let address = client_core::init::get_client_address_from_stored_keys(config.get_base())?;
let init_results = InitResults::new(&config, &address);
println!("{}", init_results);
// Output summary to a json file, if specified
if args.output_json {
client_core::init::output_to_json(&init_results, "client_init_results.json");
}
println!("\nThe address of this client is: {}\n", address);
Ok(())
}
fn print_saved_config(config: &Config) {
let config_save_location = config.get_config_file_save_location();
config
.save_to_file(None)
.expect("Failed to save the config file");
println!("Saved configuration file to {:?}", config_save_location);
println!("Using gateway: {}", config.get_base().get_gateway_id());
log::debug!("Gateway id: {}", config.get_base().get_gateway_id());
@@ -175,5 +124,63 @@ fn print_saved_config(config: &Config) {
"Gateway listener: {}",
config.get_base().get_gateway_listener()
);
println!("Client configuration completed.\n");
println!("Client configuration completed.");
client_core::init::show_address(config.get_base()).unwrap_or_else(|err| {
eprintln!("Failed to show address\nError: {err}");
std::process::exit(1)
});
}
async fn setup_gateway(
id: &str,
register: bool,
user_chosen_gateway_id: Option<&str>,
config: &Config,
) -> Result<GatewayEndpointConfig, 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.
println!("Configuring gateway");
let gateway = client_core::init::query_gateway_details(
config.get_base().get_validator_api_endpoints(),
user_chosen_gateway_id,
)
.await?;
log::debug!("Querying gateway gives: {}", gateway);
// Registering with gateway by setting up and writing shared keys to disk
log::trace!("Registering gateway");
client_core::init::register_with_gateway_and_store_keys(gateway.clone(), config.get_base())
.await?;
println!("Saved all generated keys");
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
// valid for the gateway being used
println!("Using gateway provided by user, keeping existing keys");
let gateway = client_core::init::query_gateway_details(
config.get_base().get_validator_api_endpoints(),
user_chosen_gateway_id,
)
.await?;
log::debug!("Querying gateway gives: {}", gateway);
Ok(gateway.into())
} else {
println!("Not registering gateway, will reuse existing config and keys");
let existing_config = Config::load_from_file(Some(id)).map_err(|err| {
log::error!(
"Unable to configure gateway: {err}. \n
Seems like the client was already initialized but it was not possible to read \
the existing configuration file. \n
CAUTION: Consider backing up your gateway keys and try force gateway registration, or \
removing the existing configuration and starting over."
);
ClientCoreError::CouldNotLoadExistingGatewayConfiguration(err)
})?;
Ok(existing_config.get_base().get_gateway_endpoint().clone())
}
}
+1 -1
View File
@@ -90,7 +90,7 @@ pub(crate) async fn execute(args: &Cli) -> Result<(), ClientError> {
let bin_name = "nym-native-client";
match &args.command {
Commands::Init(m) => init::execute(m).await?,
Commands::Init(m) => init::execute(m).await,
Commands::Run(m) => run::execute(m).await?,
Commands::Upgrade(m) => upgrade::execute(m),
Commands::Completions(s) => s.generate(&mut Cli::into_app(), bin_name),
-2
View File
@@ -2,8 +2,6 @@ use client_core::error::ClientCoreError;
#[derive(thiserror::Error, Debug)]
pub enum ClientError {
#[error("I/O error: {0}")]
IoError(#[from] std::io::Error),
#[error("client-core error: {0}")]
ClientCoreError(#[from] ClientCoreError),
+3 -3
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.3"
version = "1.1.2"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021"
@@ -19,8 +19,7 @@ pin-project = "1.0"
pretty_env_logger = "0.4"
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
serde = { version = "1.0", features = ["derive"] } # for config serialization/deserialization
serde_json = "1.0.89"
tap = "1.0.1"
snafu = "0.6"
thiserror = "1.0.34"
tokio = { version = "1.21.2", features = ["rt-multi-thread", "net", "signal"] }
url = "2.2"
@@ -47,6 +46,7 @@ task = { path = "../../common/task" }
topology = { path = "../../common/topology" }
validator-client = { path = "../../common/client-libs/validator-client", features = ["nymd-client"] }
version-checker = { path = "../../common/version-checker" }
tap = "1.0.1"
[features]
coconut = ["coconut-interface", "credentials", "gateway-requests/coconut", "gateway-client/coconut", "credentials/coconut", "client-core/coconut"]
+1 -9
View File
@@ -2,8 +2,8 @@
// SPDX-License-Identifier: Apache-2.0
use crate::client::config::template::config_template;
use client_core::config::Config as BaseConfig;
pub use client_core::config::MISSING_VALUE;
use client_core::config::{ClientCoreConfigTrait, Config as BaseConfig};
use config::defaults::DEFAULT_SOCKS5_LISTENING_PORT;
use config::NymConfig;
use nymsphinx::addressing::clients::Recipient;
@@ -52,12 +52,6 @@ impl NymConfig for Config {
}
}
impl ClientCoreConfigTrait for Config {
fn get_gateway_endpoint(&self) -> &client_core::config::GatewayEndpointConfig {
self.base.get_gateway_endpoint()
}
}
impl Config {
pub fn new<S: Into<String>>(id: S, provider_mix_address: S) -> Self {
Config {
@@ -66,13 +60,11 @@ impl Config {
}
}
#[must_use]
pub fn with_port(mut self, port: u16) -> Self {
self.socks5.listening_port = port;
self
}
#[must_use]
pub fn with_provider_mix_address(mut self, address: String) -> Self {
self.socks5.provider_mix_address = address;
self
+10 -4
View File
@@ -131,8 +131,11 @@ impl NymClient {
}
/// blocking version of `start` method. Will run forever (or until SIGINT is sent)
pub async fn run_forever(self) -> Result<(), Box<dyn Error + Send + Sync>> {
let mut shutdown = self.start().await?;
pub async fn run_forever(self) -> Result<(), Box<dyn Error + Send>> {
let mut shutdown = self
.start()
.await
.map_err(|err| Box::new(err) as Box<dyn Error + Send>)?;
let res = wait_for_signal_and_error(&mut shutdown).await;
@@ -150,9 +153,12 @@ impl NymClient {
pub async fn run_and_listen(
self,
mut receiver: Socks5ControlMessageReceiver,
) -> Result<(), Box<dyn Error + Send + Sync>> {
) -> Result<(), Box<dyn Error + Send>> {
// Start the main task
let mut shutdown = self.start().await?;
let mut shutdown = self
.start()
.await
.map_err(|err| Box::new(err) as Box<dyn Error + Send>)?;
let res = tokio::select! {
biased;
+74 -71
View File
@@ -1,18 +1,13 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::fmt::Display;
use clap::Args;
use client_core::{config::GatewayEndpointConfig, error::ClientCoreError};
use config::NymConfig;
use nymsphinx::addressing::clients::Recipient;
use serde::Serialize;
use tap::TapFallible;
use crate::{
client::config::Config,
commands::{override_config, OverrideConfig},
error::Socks5ClientError,
};
#[derive(Args, Clone)]
@@ -60,10 +55,6 @@ pub(crate) struct Init {
#[cfg(feature = "coconut")]
#[clap(long)]
enabled_credentials_mode: bool,
/// Save a summary of the initialization to a json file
#[clap(long)]
output_json: bool,
}
impl From<Init> for OverrideConfig {
@@ -80,30 +71,7 @@ impl From<Init> for OverrideConfig {
}
}
#[derive(Debug, Serialize)]
pub struct InitResults {
#[serde(flatten)]
client_core: client_core::init::InitResults,
socks5_listening_port: String,
}
impl InitResults {
fn new(config: &Config, address: &Recipient) -> Self {
Self {
client_core: client_core::init::InitResults::new(config.get_base(), address),
socks5_listening_port: config.get_listening_port().to_string(),
}
}
}
impl Display for InitResults {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "{}", self.client_core)?;
write!(f, "SOCKS5 listening port: {}", self.socks5_listening_port)
}
}
pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> {
pub(crate) async fn execute(args: &Init) {
println!("Initialising client...");
let id = &args.id;
@@ -128,47 +96,25 @@ pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> {
let register_gateway = !already_init || user_wants_force_register;
// Attempt to use a user-provided gateway, if possible
let user_chosen_gateway_id = args.gateway.clone();
let user_chosen_gateway_id = args.gateway.as_deref();
// Load and potentially override config
let mut config = override_config(
Config::new(id, provider_address),
OverrideConfig::from(args.clone()),
);
// Setup gateway by either registering a new one, or creating a new config from the selected
// one but with keys kept, or reusing the gateway configuration.
let gateway = client_core::init::setup_gateway::<Config, _>(
register_gateway,
user_chosen_gateway_id,
config.get_base(),
)
.await
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?;
let mut config = Config::new(id, provider_address);
let override_config_fields = OverrideConfig::from(args.clone());
config = override_config(config, override_config_fields);
let gateway = setup_gateway(id, register_gateway, user_chosen_gateway_id, &config)
.await
.unwrap_or_else(|err| {
eprintln!("Failed to setup gateway\nError: {err}");
std::process::exit(1)
});
config.get_base_mut().with_gateway_endpoint(gateway);
config.save_to_file(None).tap_err(|_| {
log::error!("Failed to save the config file");
})?;
print_saved_config(&config);
let address = client_core::init::get_client_address_from_stored_keys(config.get_base())?;
let init_results = InitResults::new(&config, &address);
println!("{}", init_results);
// Output summary to a json file, if specified
if args.output_json {
client_core::init::output_to_json(&init_results, "socks5_client_init_results.json");
}
println!("\nThe address of this client is: {}\n", address);
Ok(())
}
fn print_saved_config(config: &Config) {
let config_save_location = config.get_config_file_save_location();
config
.save_to_file(None)
.expect("Failed to save the config file");
println!("Saved configuration file to {:?}", config_save_location);
println!("Using gateway: {}", config.get_base().get_gateway_id());
log::debug!("Gateway id: {}", config.get_base().get_gateway_id());
@@ -177,5 +123,62 @@ fn print_saved_config(config: &Config) {
"Gateway listener: {}",
config.get_base().get_gateway_listener()
);
println!("Client configuration completed.\n");
println!("Client configuration completed.");
client_core::init::show_address(config.get_base()).unwrap_or_else(|err| {
eprintln!("Failed to show address\nError: {err}");
std::process::exit(1)
});
}
async fn setup_gateway(
id: &str,
register: bool,
user_chosen_gateway_id: Option<&str>,
config: &Config,
) -> Result<GatewayEndpointConfig, 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.
println!("Configuring gateway");
let gateway = client_core::init::query_gateway_details(
config.get_base().get_validator_api_endpoints(),
user_chosen_gateway_id,
)
.await?;
log::debug!("Querying gateway gives: {}", gateway);
// Registering with gateway by setting up and writing shared keys to disk
log::trace!("Registering gateway");
client_core::init::register_with_gateway_and_store_keys(gateway.clone(), config.get_base())
.await?;
println!("Saved all generated keys");
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
// valid for the gateway being used
println!("Using gateway provided by user, keeping existing keys");
let gateway = client_core::init::query_gateway_details(
config.get_base().get_validator_api_endpoints(),
user_chosen_gateway_id,
)
.await?;
log::debug!("Querying gateway gives: {}", gateway);
Ok(gateway.into())
} else {
println!("Not registering gateway, will reuse existing config and keys");
let existing_config = Config::load_from_file(Some(id)).map_err(|err| {
log::error!(
"Unable to configure gateway: {err}. \n
Seems like the client was already initialized but it was not possible to read \
the existing configuration file. \n
CAUTION: Consider backing up your gateway keys and try force gateway registration, or \
removing the existing configuration and starting over."
);
ClientCoreError::CouldNotLoadExistingGatewayConfiguration(err)
})?;
Ok(existing_config.get_base().get_gateway_endpoint().clone())
}
}
+2 -2
View File
@@ -89,11 +89,11 @@ pub(crate) struct OverrideConfig {
enabled_credentials_mode: bool,
}
pub(crate) async fn execute(args: &Cli) -> Result<(), Box<dyn Error + Send + Sync>> {
pub(crate) async fn execute(args: &Cli) -> Result<(), Box<dyn Error + Send>> {
let bin_name = "nym-socks5-client";
match &args.command {
Commands::Init(m) => init::execute(m).await?,
Commands::Init(m) => init::execute(m).await,
Commands::Run(m) => run::execute(m).await?,
Commands::Upgrade(m) => upgrade::execute(m),
Commands::Completions(s) => s.generate(&mut Cli::into_app(), bin_name),
+1 -1
View File
@@ -95,7 +95,7 @@ fn version_check(cfg: &Config) -> bool {
}
}
pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn std::error::Error + Send>> {
let id = &args.id;
let mut config = match Config::load_from_file(Some(id)) {
+1 -1
View File
@@ -13,7 +13,7 @@ pub mod error;
pub mod socks;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
async fn main() -> Result<(), Box<dyn Error + Send>> {
setup_logging();
println!("{}", banner());
+11 -11
View File
@@ -1,3 +1,5 @@
use snafu::Snafu;
/// SOCKS4 Response codes
#[allow(dead_code)]
pub(crate) enum ResponseCodeV4 {
@@ -8,26 +10,24 @@ pub(crate) enum ResponseCodeV4 {
}
/// Possible SOCKS5 Response Codes
#[allow(dead_code)]
#[derive(Debug, thiserror::Error)]
#[derive(Debug, Snafu)]
pub(crate) enum ResponseCodeV5 {
#[error("SOCKS5 Server Success")]
Success = 0x00,
#[error("SOCKS5 Server Failure")]
#[snafu(display("SOCKS5 Server Failure"))]
Failure = 0x01,
#[error("SOCKS5 Rule failure")]
#[snafu(display("SOCKS5 Rule failure"))]
RuleFailure = 0x02,
#[error("network unreachable")]
#[snafu(display("network unreachable"))]
NetworkUnreachable = 0x03,
#[error("host unreachable")]
#[snafu(display("host unreachable"))]
HostUnreachable = 0x04,
#[error("connection refused")]
#[snafu(display("connection refused"))]
ConnectionRefused = 0x05,
#[error("TTL expired")]
#[snafu(display("TTL expired"))]
TtlExpired = 0x06,
#[error("Command not supported")]
#[snafu(display("Command not supported"))]
CommandNotSupported = 0x07,
#[error("Addr Type not supported")]
#[snafu(display("Addr Type not supported"))]
AddrTypeNotSupported = 0x08,
}
@@ -61,7 +61,7 @@ where
#[cfg(feature = "coconut")]
pub async fn prepare_coconut_credential(
&self,
) -> Result<(coconut_interface::Credential, i64), GatewayClientError> {
) -> Result<coconut_interface::Credential, GatewayClientError> {
let verification_key = obtain_aggregate_verification_key(&self.coconut_api_clients).await?;
let bandwidth_credential = self.storage.get_next_coconut_credential().await?;
let voucher_value = u64::from_str(&bandwidth_credential.voucher_value)
@@ -75,21 +75,13 @@ where
coconut_interface::Signature::try_from_bs58(bandwidth_credential.signature)?;
// the below would only be executed once we know where we want to spend it (i.e. which gateway and stuff)
Ok((
prepare_for_spending(
voucher_value,
voucher_info,
serial_number,
binding_number,
&signature,
&verification_key,
)?,
bandwidth_credential.id,
))
}
#[cfg(feature = "coconut")]
pub async fn consume_credential(&self, id: i64) -> Result<(), GatewayClientError> {
Ok(self.storage.consume_coconut_credential(id).await?)
Ok(prepare_for_spending(
voucher_value,
voucher_info,
serial_number,
binding_number,
&signature,
&verification_key,
)?)
}
}
@@ -587,7 +587,7 @@ impl GatewayClient {
let _gateway_owner = self.gateway_owner.clone();
#[cfg(feature = "coconut")]
let (credential, credential_id) = self
let credential = self
.bandwidth_controller
.as_ref()
.unwrap()
@@ -597,15 +597,7 @@ impl GatewayClient {
return self.try_claim_testnet_bandwidth().await;
#[cfg(feature = "coconut")]
{
self.claim_coconut_bandwidth(credential).await?;
self.bandwidth_controller
.as_ref()
.unwrap()
.consume_credential(credential_id)
.await?;
Ok(())
}
return self.claim_coconut_bandwidth(credential).await;
}
fn estimate_required_bandwidth(&self, packets: &[MixPacket]) -> i64 {
@@ -39,7 +39,7 @@ pub trait Storage: Send + Sync {
async fn get_next_coconut_credential(&self) -> Result<CoconutCredential, StorageError>;
async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError>;
async fn remove_coconut_credential(&self, id: i64) -> Result<(), StorageError>;
}
#[async_trait]
@@ -59,7 +59,7 @@ impl Storage for PersistentStorage {
Err(StorageError::WasmNotSupported)
}
async fn consume_coconut_credential(&self, _id: i64) -> Result<(), StorageError> {
async fn remove_coconut_credential(&self, _id: i64) -> Result<(), StorageError> {
Err(StorageError::WasmNotSupported)
}
}
@@ -1,16 +0,0 @@
/*
* Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
* SPDX-License-Identifier: Apache-2.0
*/
DROP TABLE coconut_credentials;
CREATE TABLE coconut_credentials
(
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
voucher_value TEXT NOT NULL,
voucher_info TEXT NOT NULL,
serial_number TEXT NOT NULL,
binding_number TEXT NOT NULL,
signature TEXT NOT NULL UNIQUE,
consumed BOOLEAN NOT NULL
);
+10 -16
View File
@@ -36,8 +36,8 @@ impl CoconutCredentialManager {
signature: String,
) -> Result<(), sqlx::Error> {
sqlx::query!(
"INSERT INTO coconut_credentials(voucher_value, voucher_info, serial_number, binding_number, signature, consumed) VALUES (?, ?, ?, ?, ?, ?)",
voucher_value, voucher_info, serial_number, binding_number, signature, false
"INSERT INTO coconut_credentials(voucher_value, voucher_info, serial_number, binding_number, signature) VALUES (?, ?, ?, ?, ?)",
voucher_value, voucher_info, serial_number, binding_number, signature
)
.execute(&self.connection_pool)
.await?;
@@ -48,26 +48,20 @@ impl CoconutCredentialManager {
pub(crate) async fn get_next_coconut_credential(
&self,
) -> Result<CoconutCredential, sqlx::Error> {
sqlx::query_as!(
CoconutCredential,
"SELECT * FROM coconut_credentials WHERE NOT consumed"
)
.fetch_one(&self.connection_pool)
.await
sqlx::query_as!(CoconutCredential, "SELECT * FROM coconut_credentials")
.fetch_one(&self.connection_pool)
.await
}
/// Consumes in the database the specified credential.
/// Removes from the database the specified credential.
///
/// # Arguments
///
/// * `id`: Database id.
pub(crate) async fn consume_coconut_credential(&self, id: i64) -> Result<(), sqlx::Error> {
sqlx::query!(
"UPDATE coconut_credentials SET consumed = TRUE WHERE id = ?",
id
)
.execute(&self.connection_pool)
.await?;
pub(crate) async fn remove_coconut_credential(&self, id: i64) -> Result<(), sqlx::Error> {
sqlx::query!("DELETE FROM coconut_credentials WHERE id = ?", id)
.execute(&self.connection_pool)
.await?;
Ok(())
}
}
+2 -2
View File
@@ -93,9 +93,9 @@ impl Storage for PersistentStorage {
Ok(credential)
}
async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError> {
async fn remove_coconut_credential(&self, id: i64) -> Result<(), StorageError> {
self.coconut_credential_manager
.consume_coconut_credential(id)
.remove_coconut_credential(id)
.await?;
Ok(())
-1
View File
@@ -9,5 +9,4 @@ pub struct CoconutCredential {
pub serial_number: String,
pub binding_number: String,
pub signature: String,
pub consumed: bool,
}
+3 -3
View File
@@ -25,10 +25,10 @@ pub trait Storage: Send + Sync {
/// Tries to retrieve one of the stored, unused credentials.
async fn get_next_coconut_credential(&self) -> Result<CoconutCredential, StorageError>;
/// Marks as consumed in the database the specified credential.
/// Removes from the database the specified credential.
///
/// # Arguments
///
/// * `id`: Id of the credential to be consumed.
async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError>;
/// * `signature`: Coconut credential in the form of a signature.
async fn remove_coconut_credential(&self, id: i64) -> Result<(), StorageError>;
}
+1 -1
View File
@@ -53,7 +53,7 @@ impl PublicKey {
}
}
#[derive(Clone, Debug)]
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq, Eq))]
pub struct PublicKeyWithProof {
pub(crate) key: PublicKey,
@@ -13,7 +13,7 @@ use zeroize::Zeroize;
const DISCRETE_LOG_DOMAIN: &[u8] =
b"NYM_COCONUT_NIDKG_V01_CS01_WITH_BLS12381_XMD:SHA-256_SSWU_RO_PROOF_DISCRETE_LOG";
#[derive(Clone, Debug)]
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq, Eq))]
pub struct ProofOfDiscreteLog {
pub(crate) rand_commitment: G1Projective,
+1 -72
View File
@@ -9,79 +9,19 @@ use crate::interpolation::polynomial::{Polynomial, PublicCoefficients};
use crate::interpolation::{
perform_lagrangian_interpolation_at_origin, perform_lagrangian_interpolation_at_x,
};
use crate::utils::deserialize_g2;
use crate::{NodeIndex, Share, Threshold};
use bls12_381::{G2Projective, Scalar};
use group::GroupEncoding;
use rand_core::RngCore;
use std::collections::BTreeMap;
use zeroize::Zeroize;
#[derive(Clone, Debug)]
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq, Eq))]
pub struct RecoveredVerificationKeys {
pub recovered_master: G2Projective,
pub recovered_partials: Vec<G2Projective>,
}
impl RecoveredVerificationKeys {
pub fn to_bytes(&self) -> Vec<u8> {
let partials = self.recovered_partials.len();
let mut bytes = Vec::with_capacity(96 + 4 + 96 * partials);
bytes.extend_from_slice(self.recovered_master.to_bytes().as_ref());
bytes.extend_from_slice(&((partials as u32).to_be_bytes()));
for partial in &self.recovered_partials {
bytes.extend_from_slice(partial.to_bytes().as_ref());
}
bytes
}
pub fn try_from_bytes(b: &[u8]) -> Result<Self, DkgError> {
if b.len() < 96 + 4 {
return Err(DkgError::new_deserialization_failure(
"RecoveredVerificationKeys",
"insufficient number of bytes provided",
));
}
let recovered_master = deserialize_g2(&b[..96]).ok_or_else(|| {
DkgError::new_deserialization_failure(
"RecoveredVerificationKeys.recovered_master",
"invalid curve point",
)
})?;
let partials = u32::from_be_bytes([b[96], b[97], b[98], b[99]]) as usize;
let mut recovered_partials = Vec::with_capacity(partials);
if b.len() != 96 + 4 + 96 * partials {
return Err(DkgError::new_deserialization_failure(
"RecoveredVerificationKeys",
"insufficient number of bytes provided",
));
}
let mut i = 96 + 4;
for _ in 0..partials {
let partial = deserialize_g2(&b[i..i + 96]).ok_or_else(|| {
DkgError::new_deserialization_failure(
"RecoveredVerificationKeys.recovered_partials",
"invalid curve point",
)
})?;
recovered_partials.push(partial);
i += 96;
}
Ok(RecoveredVerificationKeys {
recovered_master,
recovered_partials,
})
}
}
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq, Eq))]
pub struct Dealing {
@@ -415,17 +355,6 @@ mod tests {
use crate::combine_shares;
use rand_core::SeedableRng;
#[test]
fn recovered_verification_keys_serde() {
let keys = RecoveredVerificationKeys {
recovered_master: Default::default(),
recovered_partials: vec![Default::default(), Default::default()],
};
let bytes = keys.to_bytes();
let recovered_keys = RecoveredVerificationKeys::try_from_bytes(&bytes).unwrap();
assert_eq!(keys, recovered_keys);
}
#[test]
#[ignore] // expensive test
fn recovering_partial_verification_keys() {
+2 -2
View File
@@ -9,7 +9,7 @@ pub(crate) const BECH32_PREFIX: &str = "n";
pub const MIX_DENOM: DenomDetails = DenomDetails::new("unym", "nym", 6);
pub const STAKE_DENOM: DenomDetails = DenomDetails::new("unyx", "nyx", 6);
pub const MIXNET_CONTRACT_ADDRESS: &str =
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str =
"n17srjznxl9dvzdkpwpw24gg668wc73val88a6m5ajg6ankwvz9wtst0cznr";
pub(crate) const VESTING_CONTRACT_ADDRESS: &str =
"n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw";
@@ -25,7 +25,7 @@ pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] =
hex_literal::hex!("0000000000000000000000000000000000000000");
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy";
pub const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "https://mainnet-stats.nymte.ch:8090/";
pub(crate) const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "https://mainnet-stats.nymte.ch:8090/";
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> {
+1 -1
View File
@@ -14,7 +14,7 @@ use tokio::{
const DEFAULT_SHUTDOWN_TIMER_SECS: u64 = 5;
pub(crate) type SentError = Box<dyn Error + Send + Sync>;
pub(crate) type SentError = Box<dyn Error + Send>;
type ErrorSender = mpsc::UnboundedSender<SentError>;
type ErrorReceiver = mpsc::UnboundedReceiver<SentError>;
+1 -1
View File
@@ -22,7 +22,7 @@ pub fn spawn_with_report_error<F, T, E>(future: F, mut shutdown: ShutdownListene
where
F: Future<Output = Result<T, E>> + Send + 'static,
T: 'static,
E: std::error::Error + Send + Sync + 'static,
E: std::error::Error + Send + 'static,
{
let future_that_sends = async move {
if let Err(err) = future.await {
+1 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-gateway"
version = "1.1.3"
version = "1.1.2"
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jędrzej Stuczyński <andrew@nymtech.net>",
+3 -4
View File
@@ -5,7 +5,6 @@ use crate::config::template::config_template;
use config::defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT};
use config::NymConfig;
use log::error;
use network_defaults::mainnet::{API_VALIDATOR, NYMD_VALIDATOR, STATISTICS_SERVICE_DOMAIN_ADDRESS};
use serde::{Deserialize, Serialize};
use std::net::IpAddr;
use std::path::PathBuf;
@@ -417,9 +416,9 @@ impl Default for Gateway {
private_sphinx_key_file: Default::default(),
public_sphinx_key_file: Default::default(),
enabled_statistics: false,
statistics_service_url: Url::from_str(STATISTICS_SERVICE_DOMAIN_ADDRESS).expect("Invalid default statistics service URL"),
validator_api_urls: vec![Url::from_str(API_VALIDATOR).expect("Invalid default API URL")],
validator_nymd_urls: vec![Url::from_str(NYMD_VALIDATOR).expect("Invalid default nymd URL")],
statistics_service_url: Url::from_str("http://127.0.0.1").unwrap(),
validator_api_urls: vec![],
validator_nymd_urls: vec![],
cosmos_mnemonic: bip39::Mnemonic::from_str("exact antique hybrid width raise anchor puzzle degree fee quit long crack net vague hip despair write put useless civil mechanic broom music day").unwrap(),
nym_root_directory: Config::default_root_directory(),
persistent_storage: Default::default(),
+1 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-mixnode"
version = "1.1.3"
version = "1.1.2"
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jędrzej Stuczyński <andrew@nymtech.net>",
+2 -7
View File
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
use crate::config::template::config_template;
use config::defaults::mainnet::API_VALIDATOR;
use config::defaults::{
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
};
@@ -412,7 +411,7 @@ impl Default for MixNode {
public_identity_key_file: Default::default(),
private_sphinx_key_file: Default::default(),
public_sphinx_key_file: Default::default(),
validator_api_urls: vec![Url::from_str(API_VALIDATOR).expect("Invalid default API URL")],
validator_api_urls: vec![],
nym_root_directory: Config::default_root_directory(),
wallet_address: "nymXXXXXXXX".to_string(),
}
@@ -492,9 +491,6 @@ struct Debug {
maximum_connection_buffer_size: usize,
/// Specifies whether the mixnode should be using the legacy framing for the sphinx packets.
// it's set to true by default. The reason for that decision is to preserve compatibility with the
// existing nodes whilst everyone else is upgrading and getting the code for handling the new field.
// It shall be disabled in the subsequent releases.
use_legacy_framed_packet_version: bool,
}
@@ -507,8 +503,7 @@ impl Default for Debug {
packet_forwarding_maximum_backoff: DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF,
initial_connection_timeout: DEFAULT_INITIAL_CONNECTION_TIMEOUT,
maximum_connection_buffer_size: DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE,
// TODO: remember to change it in one of future releases!!
use_legacy_framed_packet_version: true,
use_legacy_framed_packet_version: false,
}
}
}
-4
View File
@@ -1,9 +1,5 @@
## UNRELEASED
## [nym-connect-v1.1.3](https://github.com/nymtech/nym/tree/nym-connect-v1.1.3) (2022-12-13)
- socks5-client: added support for socks4a.
## [nym-connect-v1.1.2](https://github.com/nymtech/nym/tree/nym-connect-v1.1.2) (2022-11-29)
- socks5-client: fix error with client failing and disconnecting unnecessarily.
+31 -5
View File
@@ -647,7 +647,6 @@ dependencies = [
"pemstore",
"rand 0.7.3",
"serde",
"serde_json",
"sled",
"tap",
"task",
@@ -1416,6 +1415,12 @@ dependencies = [
"zeroize",
]
[[package]]
name = "doc-comment"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
[[package]]
name = "dotenv"
version = "0.15.0"
@@ -3371,7 +3376,7 @@ dependencies = [
"proxy-helpers",
"rand 0.7.3",
"serde",
"serde_json",
"snafu",
"socks5-requests",
"tap",
"task",
@@ -4813,9 +4818,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.89"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
dependencies = [
"itoa 1.0.3",
"ryu",
@@ -5033,6 +5038,27 @@ version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
[[package]]
name = "snafu"
version = "0.6.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eab12d3c261b2308b0d80c26fffb58d17eba81a4be97890101f416b478c79ca7"
dependencies = [
"doc-comment",
"snafu-derive",
]
[[package]]
name = "snafu-derive"
version = "0.6.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1508efa03c362e23817f96cde18abed596a25219a8b2c66e8db33c03543d315b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "socket2"
version = "0.4.7"
@@ -6274,7 +6300,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "vesting-contract"
version = "1.1.2"
version = "1.1.0"
dependencies = [
"contracts-common",
"cosmwasm-std",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@nym/nym-connect",
"version": "1.1.3",
"version": "1.1.2",
"main": "index.js",
"license": "MIT",
"scripts": {
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-connect"
version = "1.1.3"
version = "1.1.2"
description = "nym-connect"
authors = ["Nym Technologies SA"]
license = ""
@@ -32,7 +32,7 @@ reqwest = { version = "0.11", features = ["json"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tap = "1.0.1"
tauri = { version = "^1.1.1", features = ["clipboard-write-text", "macos-private-api", "shell-open", "system-tray", "updater", "window-close", "window-minimize", "window-start-dragging"] }
tauri = { version = "^1.1.1", features = ["clipboard-write-text", "macos-private-api", "shell-open", "system-tray", "updater", "window-close", "window-start-dragging"] }
tendermint-rpc = "0.23.0"
thiserror = "1.0"
tokio = { version = "1.21.2", features = ["sync", "time"] }
+64 -20
View File
@@ -1,5 +1,6 @@
use std::path::PathBuf;
use client_core::config::GatewayEndpointConfig;
use std::sync::Arc;
use tap::TapFallible;
use tokio::sync::RwLock;
@@ -115,9 +116,6 @@ pub async fn init_socks5_config(provider_address: String, chosen_gateway_id: Str
// Future proofing. This flag exists for the other clients
let user_wants_force_register = false;
// If the client was already initialized, don't generate new keys and don't re-register with
// the gateway (because this would create a new shared key).
// Unless the user really wants to.
let register_gateway = !already_init || user_wants_force_register;
log::trace!("Creating config for id: {}", id);
@@ -129,38 +127,28 @@ pub async fn init_socks5_config(provider_address: String, chosen_gateway_id: Str
.set_custom_validator_apis(config_common::parse_validators(&raw_validators));
}
// Setup gateway by either registering a new one, or reusing exiting keys
let gateway = client_core::init::setup_gateway::<Socks5Config, _>(
let gateway = setup_gateway(
&id,
register_gateway,
Some(chosen_gateway_id),
config.get_base(),
Some(&chosen_gateway_id),
config.get_socks5(),
)
.await?;
config.get_base_mut().with_gateway_endpoint(gateway);
let config_save_location = config.get_socks5().get_config_file_save_location();
config.get_socks5().save_to_file(None).tap_err(|_| {
log::error!("Failed to save the config file");
})?;
print_saved_config(&config);
let address = client_core::init::get_client_address_from_stored_keys(config.get_base())?;
log::info!("The address of this client is: {}", address);
Ok(())
}
fn print_saved_config(config: &Config) {
log::info!(
"Saved configuration file to {:?}",
config.get_socks5().get_config_file_save_location()
);
log::info!("Saved configuration file to {:?}", config_save_location);
log::info!("Gateway id: {}", config.get_base().get_gateway_id());
log::info!("Gateway owner: {}", config.get_base().get_gateway_owner());
log::info!(
"Gateway listener: {}",
config.get_base().get_gateway_listener()
);
log::info!(
"Service provider address: {}",
config.get_socks5().get_provider_mix_address()
@@ -170,4 +158,60 @@ fn print_saved_config(config: &Config) {
config.get_socks5().get_listening_port()
);
log::info!("Client configuration completed.");
client_core::init::show_address(config.get_base())?;
Ok(())
}
// TODO: deduplicate with same functions in other client
async fn setup_gateway(
id: &str,
register: bool,
user_chosen_gateway_id: Option<&str>,
config: &Socks5Config,
) -> Result<GatewayEndpointConfig> {
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.
println!("Configuring gateway");
let gateway = client_core::init::query_gateway_details(
config.get_base().get_validator_api_endpoints(),
user_chosen_gateway_id,
)
.await?;
log::debug!("Querying gateway gives: {}", gateway);
// Registering with gateway by setting up and writing shared keys to disk
log::trace!("Registering gateway");
client_core::init::register_with_gateway_and_store_keys(gateway.clone(), config.get_base())
.await?;
println!("Saved all generated keys");
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
// valid for the gateway being used
println!("Using gateway provided by user, keeping existing keys");
let gateway = client_core::init::query_gateway_details(
config.get_base().get_validator_api_endpoints(),
user_chosen_gateway_id,
)
.await?;
log::debug!("Querying gateway gives: {}", gateway);
Ok(gateway.into())
} else {
println!("Not registering gateway, will reuse existing config and keys");
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 -1
View File
@@ -1,4 +1,4 @@
use client_core::config::{ClientCoreConfigTrait, GatewayEndpointConfig};
use client_core::config::GatewayEndpointConfig;
use futures::channel::mpsc;
use std::sync::Arc;
use tap::TapFallible;
+2 -3
View File
@@ -1,7 +1,7 @@
{
"package": {
"productName": "nym-connect",
"version": "1.1.3"
"version": "1.1.2"
},
"build": {
"distDir": "../dist",
@@ -57,8 +57,7 @@
},
"window": {
"startDragging": true,
"close": true,
"minimize": true
"close": true
}
},
"windows": [
+9 -17
View File
@@ -1,5 +1,5 @@
import React from 'react';
import { ArrowBack, Close, HelpOutline, Minimize } from '@mui/icons-material';
import { ArrowBack, Close, HelpOutline } from '@mui/icons-material';
import { Box, IconButton } from '@mui/material';
import { NymWordmark } from '@nymproject/react/logo/NymWordmark';
import { appWindow } from '@tauri-apps/api/window';
@@ -19,7 +19,7 @@ const customTitleBarStyles = {
};
const CustomButton = ({ Icon, onClick }: { Icon: React.JSXElementConstructor<any>; onClick: () => void }) => (
<IconButton size="small" onClick={onClick} sx={{ padding: 0 }}>
<IconButton size="small" style={{ padding: 0 }} onClick={onClick}>
<Icon style={{ fontSize: 16 }} />
</IconButton>
);
@@ -28,22 +28,14 @@ export const CustomTitleBar = () => {
const { showHelp, handleShowHelp } = useClientContext();
return (
<Box data-tauri-drag-region style={customTitleBarStyles.titlebar}>
{/* set width to keep logo centered */}
<Box sx={{ width: '40px' }}>
<CustomButton
Icon={!showHelp ? HelpOutline : ArrowBack}
onClick={() => {
handleShowHelp();
}}
/>
</Box>
<CustomButton
Icon={!showHelp ? HelpOutline : ArrowBack}
onClick={() => {
handleShowHelp();
}}
/>
<NymWordmark width={36} />
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<CustomButton Icon={Minimize} onClick={() => appWindow.minimize()} />
<CustomButton Icon={Close} onClick={() => appWindow.close()} />
</Box>
<CustomButton Icon={Close} onClick={() => appWindow.close()} />
</Box>
);
};
-4
View File
@@ -1,9 +1,5 @@
# Changelog
## [nym-wallet-v1.1.3](https://github.com/nymtech/nym/releases/tag/nym-wallet-v1.1.3) (2022-12-13)
- wallet: improved unbond screen.
## [nym-wallet-v1.1.2](https://github.com/nymtech/nym/releases/tag/nym-wallet-v1.1.2) (2022-11-09)
- wallet: Bity buy functionality
+1 -1
View File
@@ -5497,7 +5497,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "vesting-contract"
version = "1.1.2"
version = "1.1.0"
dependencies = [
"contracts-common",
"cosmwasm-std",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@nymproject/nym-wallet-app",
"version": "1.1.3",
"version": "1.1.2",
"main": "index.js",
"license": "MIT",
"scripts": {
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym_wallet"
version = "1.1.3"
version = "1.1.2"
description = "Nym Native Wallet"
authors = ["Nym Technologies SA"]
license = ""
+1 -1
View File
@@ -1,7 +1,7 @@
{
"package": {
"productName": "nym-wallet",
"version": "1.1.3"
"version": "1.1.2"
},
"build": {
"distDir": "../dist",
+17 -5
View File
@@ -16,6 +16,7 @@ const TutorialStep = ({
icon,
borderRight,
borderBottom,
fixTitleHeight,
}: {
step: number;
title: string;
@@ -23,6 +24,7 @@ const TutorialStep = ({
icon: React.ReactNode;
borderRight?: boolean;
borderBottom?: boolean;
fixTitleHeight?: boolean;
}) => (
<Grid
item
@@ -41,7 +43,13 @@ const TutorialStep = ({
{`STEP ${step}`}
</Typography>
</Stack>
<Typography fontWeight={600} variant="h6">
<Typography
fontWeight={600}
variant="h6"
sx={{
minHeight: fixTitleHeight ? '40px' : undefined,
}}
>
{title}
</Typography>
{text}
@@ -53,6 +61,7 @@ export const Tutorial = () => {
const [showSignModal, setShowSignModal] = useState(false);
const theme = useTheme();
const showBorder = useMediaQuery(theme.breakpoints.up('md'));
const fixTitleHeight = useMediaQuery(theme.breakpoints.down('lg'));
return (
<NymCard borderless title="Buy NYM with BTC without KYC" sx={{ mt: 4 }}>
@@ -87,10 +96,11 @@ export const Tutorial = () => {
}
borderRight={showBorder}
borderBottom={!showBorder}
fixTitleHeight={fixTitleHeight}
/>
<TutorialStep
step={2}
title="Sign message"
title="Sign a message with your Nym wallet"
icon={<BorderColorIcon fontSize="small" />}
text={
<Typography sx={{ color: (t) => t.palette.nym.text.muted }}>
@@ -103,17 +113,19 @@ export const Tutorial = () => {
}
borderRight={showBorder}
borderBottom={!showBorder}
fixTitleHeight={fixTitleHeight}
/>
<TutorialStep
step={3}
title="Send tx and receive NYM"
title="Make BTC tx and receive NYM"
icon={<PaidIcon fontSize="small" />}
text={
<Typography sx={{ color: (t) => t.palette.nym.text.muted }}>
{`Send the defined BTC amount to Bity's address that's given to you. As soon as your BTC transaction has 4
confirmations, Bity will send the purchased NYM tokens to your wallet.`}
Send BTC to the given address. When the transaction is confirmed your purchased NYM tokens will be
transferred in your wallet.
</Typography>
}
fixTitleHeight={fixTitleHeight}
/>
</Grid>
<Stack direction="row" gap={2} justifyContent="flex-end" mt={5}>
@@ -1,9 +1,8 @@
import React, { useContext } from 'react';
import React from 'react';
import { Stack, Typography, SxProps } from '@mui/material';
import { Link } from '@nymproject/react/link/Link';
import { TTransactionDetails } from './types';
import { ConfirmationModal } from '../Modals/ConfirmationModal';
import { AppContext } from 'src/context';
export const SendSuccessModal = ({
txDetails,
@@ -15,35 +14,26 @@ export const SendSuccessModal = ({
onClose: () => void;
sx?: SxProps;
backdropProps?: object;
}) => {
const { userBalance } = useContext(AppContext);
const handleClose = async () => {
await userBalance.refreshBalances();
onClose();
};
return (
<ConfirmationModal
open
onConfirm={handleClose}
onClose={handleClose}
title=""
confirmButton="Done"
maxWidth="xs"
fullWidth
sx={sx}
backdropProps={backdropProps}
>
<Stack alignItems="center" spacing={2}>
<Typography>You sent</Typography>
{txDetails && (
<>
<Typography variant="h5">{txDetails.amount}</Typography>
<Link href={txDetails.txUrl} target="_blank" sx={{ ml: 1 }} text="View on blockchain" />
</>
)}
</Stack>
</ConfirmationModal>
);
};
}) => (
<ConfirmationModal
open
onConfirm={onClose}
onClose={onClose}
title=""
confirmButton="Done"
maxWidth="xs"
fullWidth
sx={sx}
backdropProps={backdropProps}
>
<Stack alignItems="center" spacing={2}>
<Typography>You sent</Typography>
{txDetails && (
<>
<Typography variant="h5">{txDetails.amount}</Typography>
<Link href={txDetails.txUrl} target="_blank" sx={{ ml: 1 }} text="View on blockchain" />
</>
)}
</Stack>
</ConfirmationModal>
);
@@ -88,11 +88,7 @@ export const ParametersSettings = ({ bondedNode }: { bondedNode: TBondedMixnode
const getPendingEvents = async () => {
const events = await getPendingIntervalEvents();
const latestEvent = events
.reverse()
.find(
(evt) => 'ChangeMixCostParams' in evt.event && evt.event.ChangeMixCostParams.mix_id === bondedNode.mixId,
) as unknown as
const latestEvent = events.reverse().find((evt) => 'ChangeMixCostParams' in evt.event) as unknown as
| {
id: number;
event: {
@@ -3,7 +3,7 @@
[package]
name = "nym-network-requester"
version = "1.1.3"
version = "1.1.2"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021"
rust-version = "1.65"
@@ -437,7 +437,7 @@ impl ServiceProvider {
.await;
});
log::info!("All systems go. Press CTRL-C to stop the server.");
println!("\nAll systems go. Press CTRL-C to stop the server.");
// for each incoming message from the websocket... (which in 99.99% cases is going to be a mix message)
loop {
let Some(received) = Self::read_websocket_message(
@@ -447,7 +447,7 @@ impl ServiceProvider {
.await
else {
log::error!("The websocket stream has finished!");
return Err(NetworkRequesterError::ConnectionClosed);
return Ok(());
};
let raw_message = received.message;
@@ -7,7 +7,4 @@ pub enum NetworkRequesterError {
#[error("Websocket error")]
WebsocketConnectionError(#[from] WebsocketConnectionError),
#[error("Websocket connection closed")]
ConnectionClosed,
}
@@ -63,7 +63,7 @@ impl Run {
.unwrap_or(&DEFAULT_WEBSOCKET_LISTENING_PORT.to_string())
);
log::info!("Starting socks5 service provider");
println!("Starting socks5 service provider:");
let mut server = core::ServiceProvider::new(
uri,
self.open_proxy,
@@ -1,6 +1,6 @@
[package]
name = "nym-network-statistics"
version = "1.1.3"
version = "1.1.2"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-cli"
version = "1.1.3"
version = "1.1.2"
authors = ["Nym Technologies SA"]
edition = "2021"
+1 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-validator-api"
version = "1.1.3"
version = "1.1.2"
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jędrzej Stuczyński <andrew@nymtech.net>",
+1 -4
View File
@@ -1,12 +1,9 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[derive(Debug, Eq, PartialEq)]
pub(crate) enum ComplaintReason {
MalformedBTEPublicKey,
InvalidBTEPublicKey,
MissingDealing,
MalformedDealing,
DealingVerificationError,
+2 -17
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::coconut::dkg::client::DkgClient;
use crate::coconut::dkg::state::{ConsistentState, PersistentState, State};
use crate::coconut::dkg::state::{ConsistentState, State};
use crate::coconut::dkg::verification_key::{
verification_key_finalization, verification_key_validation,
};
@@ -63,20 +63,12 @@ impl<R: RngCore + Clone> DkgController<R> {
)) {
coconut_keypair.set(coconut_keypair_value).await;
}
let persistent_state =
PersistentState::load_from_file(config.persistent_state_path()).unwrap_or_default();
Ok(DkgController {
dkg_client: DkgClient::new(nymd_client),
secret_key_path: config.secret_key_path(),
verification_key_path: config.verification_key_path(),
state: State::new(
config.persistent_state_path(),
persistent_state,
config.get_announce_address(),
dkg_keypair,
coconut_keypair,
),
state: State::new(config.get_announce_address(), dkg_keypair, coconut_keypair),
rng,
polling_rate: config.get_dkg_contract_polling_rate(),
})
@@ -122,13 +114,6 @@ impl<R: RngCore + Clone> DkgController<R> {
};
if let Err(e) = ret {
warn!("Could not handle this iteration for the epoch state: {}", e);
} else if epoch_state != EpochState::InProgress {
let persistent_state = PersistentState::from(&self.state);
if let Err(e) =
persistent_state.save_to_file(self.state.persistent_state_path())
{
warn!("Could not backup the state for this iteration: {}", e);
}
}
}
}
-54
View File
@@ -54,8 +54,6 @@ pub(crate) async fn dealing_exchange(
#[cfg(test)]
pub(crate) mod tests {
use super::*;
use crate::coconut::dkg::complaints::ComplaintReason;
use crate::coconut::dkg::state::PersistentState;
use crate::coconut::tests::DummyClient;
use crate::coconut::KeyPair;
use coconut_dkg_common::dealer::DealerDetails;
@@ -64,7 +62,6 @@ pub(crate) mod tests {
use dkg::bte::Params;
use rand::rngs::OsRng;
use std::collections::HashMap;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::{Arc, RwLock};
use url::Url;
@@ -112,8 +109,6 @@ pub(crate) mod tests {
);
let params = setup();
let mut state = State::new(
PathBuf::default(),
PersistentState::default(),
Url::parse("localhost:8000").unwrap(),
DkgKeyPair::new(&params, OsRng),
KeyPair::new(),
@@ -153,53 +148,4 @@ pub(crate) mod tests {
.clone();
assert_eq!(dealings, new_dealings);
}
#[tokio::test]
#[ignore] // expensive test
async fn invalid_bte_proof_dealing_posted() {
let self_index = 2;
let dealer_details_db = Arc::new(RwLock::new(HashMap::new()));
let dealings_db = Arc::new(RwLock::new(HashMap::new()));
let dkg_client = DkgClient::new(
DummyClient::new(AccountId::from_str(TEST_VALIDATORS_ADDRESS[0]).unwrap())
.with_dealer_details(&dealer_details_db)
.with_dealings(&dealings_db),
);
let params = setup();
let mut state = State::new(
PathBuf::default(),
PersistentState::default(),
Url::parse("localhost:8000").unwrap(),
DkgKeyPair::new(&params, OsRng),
KeyPair::new(),
);
state.set_node_index(Some(self_index));
insert_dealers(&params, &dealer_details_db);
dealer_details_db
.write()
.unwrap()
.entry(TEST_VALIDATORS_ADDRESS[1].to_string())
.and_modify(|details| {
let mut bytes = bs58::decode(details.bte_public_key_with_proof.clone())
.into_vec()
.unwrap();
let last_byte = bytes.last_mut().unwrap();
*last_byte += 1;
details.bte_public_key_with_proof = bs58::encode(&bytes).into_string();
});
dealing_exchange(&dkg_client, &mut state, OsRng)
.await
.unwrap();
assert_eq!(
*state
.all_dealers()
.get(&Addr::unchecked(TEST_VALIDATORS_ADDRESS[1]))
.unwrap()
.as_ref()
.unwrap_err(),
ComplaintReason::InvalidBTEPublicKey
);
}
}
@@ -34,12 +34,10 @@ pub(crate) async fn public_key_submission(
#[cfg(test)]
pub(crate) mod tests {
use super::*;
use crate::coconut::dkg::state::PersistentState;
use crate::coconut::tests::DummyClient;
use crate::coconut::KeyPair;
use dkg::bte::keys::KeyPair as DkgKeyPair;
use rand::rngs::OsRng;
use std::path::PathBuf;
use std::str::FromStr;
use url::Url;
use validator_client::nymd::AccountId;
@@ -53,8 +51,6 @@ pub(crate) mod tests {
AccountId::from_str(TEST_VALIDATOR_ADDRESS).unwrap(),
));
let mut state = State::new(
PathBuf::default(),
PersistentState::default(),
Url::parse("localhost:8000").unwrap(),
DkgKeyPair::new(&dkg::bte::setup(), OsRng),
KeyPair::new(),
+23 -116
View File
@@ -9,33 +9,13 @@ use coconut_dkg_common::types::EpochState;
use cosmwasm_std::Addr;
use dkg::bte::{keys::KeyPair as DkgKeyPair, PublicKey, PublicKeyWithProof};
use dkg::{NodeIndex, RecoveredVerificationKeys, Threshold};
use serde::de::Error;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::collections::BTreeMap;
use std::path::PathBuf;
use url::Url;
fn bte_pk_serialize<S: Serializer>(
val: &PublicKeyWithProof,
serializer: S,
) -> Result<S::Ok, S::Error> {
val.to_bytes().serialize(serializer)
}
fn bte_pk_deserialize<'de, D>(deserializer: D) -> Result<PublicKeyWithProof, D::Error>
where
D: Deserializer<'de>,
{
let vec: Vec<u8> = Deserialize::deserialize(deserializer)?;
PublicKeyWithProof::try_from_bytes(&vec).map_err(|err| Error::custom(format_args!("{:?}", err)))
}
// note: each dealer is also a receiver which simplifies some logic significantly
#[derive(Clone, Deserialize, Debug, Serialize)]
#[derive(Debug)]
pub(crate) struct DkgParticipant {
pub(crate) _address: Addr,
#[serde(serialize_with = "bte_pk_serialize")]
#[serde(deserialize_with = "bte_pk_deserialize")]
pub(crate) bte_public_key_with_proof: PublicKeyWithProof,
pub(crate) assigned_index: NodeIndex,
}
@@ -50,10 +30,6 @@ impl TryFrom<DealerDetails> for DkgParticipant {
.map_err(|_| ComplaintReason::MalformedBTEPublicKey)?
.map_err(|_| ComplaintReason::MalformedBTEPublicKey)?;
if !bte_public_key_with_proof.verify() {
return Err(ComplaintReason::InvalidBTEPublicKey);
}
Ok(DkgParticipant {
_address: dealer.address,
bte_public_key_with_proof,
@@ -91,6 +67,20 @@ pub(crate) trait ConsistentState {
}
}
pub(crate) struct State {
announce_address: Url,
dkg_keypair: DkgKeyPair,
coconut_keypair: CoconutKeyPair,
node_index: Option<NodeIndex>,
dealers: BTreeMap<Addr, Result<DkgParticipant, ComplaintReason>>,
receiver_index: Option<usize>,
threshold: Option<Threshold>,
recovered_vks: Vec<RecoveredVerificationKeys>,
proposal_id: Option<u64>,
voted_vks: bool,
executed_proposal: bool,
}
#[async_trait]
impl ConsistentState for State {
fn node_index_value(&self) -> Result<NodeIndex, CoconutError> {
@@ -137,110 +127,27 @@ impl ConsistentState for State {
}
}
fn vks_serialize<S: Serializer>(
val: &[RecoveredVerificationKeys],
serializer: S,
) -> Result<S::Ok, S::Error> {
let vec: Vec<Vec<u8>> = val.iter().map(|vk| vk.to_bytes()).collect();
vec.serialize(serializer)
}
fn vks_deserialize<'de, D>(deserializer: D) -> Result<Vec<RecoveredVerificationKeys>, D::Error>
where
D: Deserializer<'de>,
{
let vec: Vec<Vec<u8>> = Deserialize::deserialize(deserializer)?;
vec.into_iter()
.map(|b| {
RecoveredVerificationKeys::try_from_bytes(&b)
.map_err(|err| D::Error::custom(format_args!("{:?}", err)))
})
.collect()
}
#[derive(Default, Deserialize, Serialize)]
pub(crate) struct PersistentState {
node_index: Option<NodeIndex>,
dealers: BTreeMap<Addr, Result<DkgParticipant, ComplaintReason>>,
receiver_index: Option<usize>,
threshold: Option<Threshold>,
#[serde(serialize_with = "vks_serialize")]
#[serde(deserialize_with = "vks_deserialize")]
recovered_vks: Vec<RecoveredVerificationKeys>,
proposal_id: Option<u64>,
voted_vks: bool,
executed_proposal: bool,
}
impl From<&State> for PersistentState {
fn from(s: &State) -> Self {
PersistentState {
node_index: s.node_index,
dealers: s.dealers.clone(),
receiver_index: s.receiver_index,
threshold: s.threshold,
recovered_vks: s.recovered_vks.clone(),
proposal_id: s.proposal_id,
voted_vks: s.voted_vks,
executed_proposal: s.executed_proposal,
}
}
}
impl PersistentState {
pub fn save_to_file(&self, path: PathBuf) -> Result<(), CoconutError> {
std::fs::write(path, serde_json::to_string(self)?)?;
Ok(())
}
pub fn load_from_file(path: PathBuf) -> Result<Self, CoconutError> {
Ok(serde_json::from_str(&std::fs::read_to_string(path)?)?)
}
}
pub(crate) struct State {
persistent_state_path: PathBuf,
announce_address: Url,
dkg_keypair: DkgKeyPair,
coconut_keypair: CoconutKeyPair,
node_index: Option<NodeIndex>,
dealers: BTreeMap<Addr, Result<DkgParticipant, ComplaintReason>>,
receiver_index: Option<usize>,
threshold: Option<Threshold>,
recovered_vks: Vec<RecoveredVerificationKeys>,
proposal_id: Option<u64>,
voted_vks: bool,
executed_proposal: bool,
}
impl State {
pub fn new(
persistent_state_path: PathBuf,
persistent_state: PersistentState,
announce_address: Url,
dkg_keypair: DkgKeyPair,
coconut_keypair: CoconutKeyPair,
) -> Self {
State {
persistent_state_path,
announce_address,
dkg_keypair,
coconut_keypair,
node_index: persistent_state.node_index,
dealers: persistent_state.dealers,
receiver_index: persistent_state.receiver_index,
threshold: persistent_state.threshold,
recovered_vks: persistent_state.recovered_vks,
proposal_id: persistent_state.proposal_id,
voted_vks: persistent_state.voted_vks,
executed_proposal: persistent_state.executed_proposal,
node_index: None,
dealers: BTreeMap::new(),
receiver_index: None,
threshold: None,
recovered_vks: vec![],
proposal_id: None,
voted_vks: false,
executed_proposal: false,
}
}
pub fn persistent_state_path(&self) -> PathBuf {
self.persistent_state_path.clone()
}
pub fn announce_address(&self) -> &Url {
&self.announce_address
}
@@ -249,7 +249,6 @@ pub(crate) mod tests {
use super::*;
use crate::coconut::dkg::dealing::dealing_exchange;
use crate::coconut::dkg::public_key::public_key_submission;
use crate::coconut::dkg::state::PersistentState;
use crate::coconut::tests::DummyClient;
use crate::coconut::KeyPair;
use coconut_dkg_common::dealer::DealerDetails;
@@ -260,7 +259,6 @@ pub(crate) mod tests {
use rand::Rng;
use std::collections::HashMap;
use std::env::temp_dir;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::{Arc, RwLock};
use url::Url;
@@ -291,8 +289,6 @@ pub(crate) mod tests {
);
let keypair = DkgKeyPair::new(&params, OsRng);
let state = State::new(
PathBuf::default(),
PersistentState::default(),
Url::parse("localhost:8000").unwrap(),
keypair,
KeyPair::new(),
-3
View File
@@ -23,9 +23,6 @@ pub enum CoconutError {
#[error("{0}")]
IOError(#[from] std::io::Error),
#[error("{0}")]
SerdeJsonError(#[from] serde_json::Error),
#[error("Could not parse Ed25519 data")]
Ed25519ParseError(#[from] Ed25519RecoveryError),
+1 -18
View File
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
use crate::config::template::config_template;
use config::defaults::mainnet::MIXNET_CONTRACT_ADDRESS;
use config::defaults::DEFAULT_VALIDATOR_API_PORT;
use config::NymConfig;
use serde::{Deserialize, Serialize};
@@ -120,7 +119,7 @@ impl Default for Base {
id: String::default(),
local_validator: default_validator,
announce_address: default_announce_address,
mixnet_contract_address: MIXNET_CONTRACT_ADDRESS.to_string(),
mixnet_contract_address: String::default(),
mnemonic: "exact antique hybrid width raise anchor puzzle degree fee quit long crack net vague hip despair write put useless civil mechanic broom music day".to_string(),
}
}
@@ -283,9 +282,6 @@ pub struct CoconutSigner {
/// Specifies whether rewarding service is enabled in this process.
enabled: bool,
/// Path to a JSON file where state is persisted between different stages of DKG.
dkg_persistent_state_path: PathBuf,
/// Path to the coconut verification key.
verification_key_path: PathBuf,
@@ -303,7 +299,6 @@ pub struct CoconutSigner {
}
impl CoconutSigner {
pub const DKG_PERSISTENT_STATE_FILE: &'static str = "dkg_persistent_state.json";
pub const DKG_DECRYPTION_KEY_FILE: &'static str = "dkg_decryption_key.pem";
pub const DKG_PUBLIC_KEY_WITH_PROOF_FILE: &'static str = "dkg_public_key_with_proof.pem";
pub const COCONUT_VERIFICATION_KEY_FILE: &'static str = "coconut_verification_key.pem";
@@ -317,10 +312,6 @@ impl CoconutSigner {
Config::default_data_directory(None).join(Self::COCONUT_SECRET_KEY_FILE)
}
fn default_dkg_persistent_state_path() -> PathBuf {
Config::default_data_directory(None).join(Self::DKG_PERSISTENT_STATE_FILE)
}
fn default_dkg_decryption_key_path() -> PathBuf {
Config::default_data_directory(None).join(Self::DKG_DECRYPTION_KEY_FILE)
}
@@ -334,7 +325,6 @@ impl Default for CoconutSigner {
fn default() -> Self {
Self {
enabled: Default::default(),
dkg_persistent_state_path: CoconutSigner::default_dkg_persistent_state_path(),
verification_key_path: CoconutSigner::default_coconut_verification_key_path(),
secret_key_path: CoconutSigner::default_coconut_secret_key_path(),
decryption_key_path: CoconutSigner::default_dkg_decryption_key_path(),
@@ -355,8 +345,6 @@ impl Config {
Config::default_data_directory(Some(id)).join(NodeStatusAPI::DB_FILE);
self.network_monitor.credentials_database_path =
Config::default_data_directory(Some(id)).join(NetworkMonitor::DB_FILE);
self.coconut_signer.dkg_persistent_state_path =
Config::default_data_directory(Some(id)).join(CoconutSigner::DKG_PERSISTENT_STATE_FILE);
self.coconut_signer.verification_key_path = Config::default_data_directory(Some(id))
.join(CoconutSigner::COCONUT_VERIFICATION_KEY_FILE);
self.coconut_signer.secret_key_path =
@@ -521,11 +509,6 @@ impl Config {
self.node_status_api.database_path.clone()
}
#[cfg(feature = "coconut")]
pub fn persistent_state_path(&self) -> PathBuf {
self.coconut_signer.dkg_persistent_state_path.clone()
}
#[cfg(feature = "coconut")]
pub fn verification_key_path(&self) -> PathBuf {
self.coconut_signer.verification_key_path.clone()