Compare commits

..

5 Commits

Author SHA1 Message Date
pierre 81e31c0550 build(nc-mobile): remove webpack-favicons plugin 2023-03-24 12:21:03 +01:00
Tommy Verrall 1c771df941 Merge pull request #3211 from nymtech/feature/no_creds_err_msg
Better error log for lacking credential in db
2023-03-24 11:18:41 +03:00
benedetta davico 17c2aecd99 starting to add vesting tests (#3134)
* starting to add vesting tests

* adding more vesting client tests

* starting to add vesting tests & merging develop

* adding more vesting client tests

* fix linting

* Revert "Merge conflicts"

This reverts commit 0beb025a62.

* fix linting errors

* fix linting errors

* removing only adding skip

---------

Co-authored-by: fmtabbara <fmtabbara@hotmail.co.uk>
2023-03-22 18:30:07 +01:00
Bogdan-Ștefan Neacșu 85074f3ac8 Better error log for lacking credential in db 2023-03-22 17:21:20 +02:00
Bogdan-Ștefan Neacşu c7020da81c Use nym task crate for network statistics signal handle (#3209)
* Use nym task crate for network statistics signal handle

* Update changelog
2023-03-22 16:43:00 +02:00
161 changed files with 1921 additions and 2473 deletions
@@ -80,7 +80,7 @@ jobs:
components: rustfmt, clippy
- name: Install wasm-opt
run: cargo install --version 0.112.0 wasm-opt
run: cargo install --version 0.110.0 wasm-opt
- name: Build release contracts
run: make wasm
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
components: rustfmt, clippy
- name: Install wasm-opt
run: cargo install --version 0.112.0 wasm-opt
run: cargo install --version 0.110.0 wasm-opt
- name: Build release contracts
run: make wasm
+2 -12
View File
@@ -4,19 +4,9 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [v1.1.14] (2023-04-04)
- nym-network-statistics properly handles signals ([#3209])
- Investigate cause of qwerty validator being in invalid rewarding state ([#3224])
- Fix NR config due to changes in #3199 ([#3223])
- [Issue] Mixnodes and gateway do not close connections properly ([#3187])
- disable sign-ext when using wasm-opt + update wasm-opt ([#3203])
- chore: tidy up client `Debug` config section ([#3199])
[#3224]: https://github.com/nymtech/nym/issues/3224
[#3223]: https://github.com/nymtech/nym/issues/3223
[#3187]: https://github.com/nymtech/nym/issues/3187
[#3203]: https://github.com/nymtech/nym/pull/3203
[#3199]: https://github.com/nymtech/nym/pull/3199
[#3209]: https://github.com/nymtech/nym/issues/3209
## [v1.1.13] (2023-03-15)
Generated
+12 -12
View File
@@ -653,7 +653,7 @@ dependencies = [
[[package]]
name = "client-core"
version = "1.1.14"
version = "1.1.13"
dependencies = [
"async-trait",
"dashmap 5.4.0",
@@ -1610,7 +1610,7 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]]
name = "explorer-api"
version = "1.1.14"
version = "1.1.13"
dependencies = [
"chrono",
"clap 4.1.11",
@@ -3059,7 +3059,7 @@ dependencies = [
[[package]]
name = "nym-api"
version = "1.1.15"
version = "1.1.14"
dependencies = [
"anyhow",
"async-trait",
@@ -3139,7 +3139,7 @@ dependencies = [
[[package]]
name = "nym-bin-common"
version = "0.4.0"
version = "0.3.0"
dependencies = [
"clap 4.1.11",
"clap_complete",
@@ -3168,7 +3168,7 @@ dependencies = [
[[package]]
name = "nym-cli"
version = "1.1.14"
version = "1.1.13"
dependencies = [
"anyhow",
"base64 0.13.1",
@@ -3210,7 +3210,6 @@ dependencies = [
"nym-coconut-bandwidth-contract-common",
"nym-coconut-dkg-common",
"nym-contracts-common",
"nym-crypto",
"nym-mixnet-contract-common",
"nym-multisig-contract-common",
"nym-network-defaults",
@@ -3228,7 +3227,7 @@ dependencies = [
[[package]]
name = "nym-client"
version = "1.1.14"
version = "1.1.13"
dependencies = [
"clap 4.1.11",
"client-core",
@@ -3403,7 +3402,7 @@ dependencies = [
[[package]]
name = "nym-gateway"
version = "1.1.14"
version = "1.1.13"
dependencies = [
"anyhow",
"async-trait",
@@ -3489,7 +3488,7 @@ dependencies = [
[[package]]
name = "nym-mixnode"
version = "1.1.15"
version = "1.1.14"
dependencies = [
"anyhow",
"atty",
@@ -3558,7 +3557,7 @@ dependencies = [
[[package]]
name = "nym-network-requester"
version = "1.1.14"
version = "1.1.13"
dependencies = [
"async-trait",
"clap 4.1.11",
@@ -3596,12 +3595,13 @@ dependencies = [
[[package]]
name = "nym-network-statistics"
version = "1.1.14"
version = "1.1.13"
dependencies = [
"dirs",
"log",
"nym-bin-common",
"nym-statistics-common",
"nym-task",
"pretty_env_logger",
"rocket",
"serde",
@@ -3696,7 +3696,7 @@ dependencies = [
[[package]]
name = "nym-socks5-client"
version = "1.1.14"
version = "1.1.13"
dependencies = [
"clap 4.1.11",
"client-core",
+2 -2
View File
@@ -81,8 +81,8 @@ build-nym-cli:
wasm:
RUSTFLAGS='-C link-arg=-s' cargo build --manifest-path contracts/Cargo.toml --release --target wasm32-unknown-unknown
wasm-opt --disable-sign-ext -Os contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm -o contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm
wasm-opt --disable-sign-ext -Os contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm -o contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm
wasm-opt -Os contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm -o contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm
wasm-opt -Os contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm -o contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm
# NOTE: this seems deprecated an not needed anymore?
mixnet-opt: wasm
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "client-core"
version = "1.1.14"
version = "1.1.13"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021"
rust-version = "1.66"
@@ -5,11 +5,7 @@ use crate::{client::replies::reply_storage, config::DebugConfig};
pub fn setup_empty_reply_surb_backend(debug_config: &DebugConfig) -> reply_storage::Empty {
reply_storage::Empty {
min_surb_threshold: debug_config
.reply_surbs
.minimum_reply_surb_storage_threshold,
max_surb_threshold: debug_config
.reply_surbs
.maximum_reply_surb_storage_threshold,
min_surb_threshold: debug_config.minimum_reply_surb_storage_threshold,
max_surb_threshold: debug_config.maximum_reply_surb_storage_threshold,
}
}
@@ -236,15 +236,15 @@ where
let mut stream = LoopCoverTrafficStream::new(
ack_key,
debug_config.acknowledgements.average_ack_delay,
debug_config.traffic.average_packet_delay,
debug_config.cover_traffic.loop_cover_traffic_average_delay,
debug_config.average_ack_delay,
debug_config.average_packet_delay,
debug_config.loop_cover_traffic_average_delay,
mix_tx,
self_address,
topology_accessor,
);
if let Some(size) = debug_config.traffic.use_extended_packet_size {
if let Some(size) = debug_config.use_extended_packet_size {
log::debug!("Setting extended packet size: {:?}", size);
stream.set_custom_packet_size(size.into());
}
@@ -338,9 +338,7 @@ where
shared_key,
mixnet_message_sender,
ack_sender,
self.debug_config
.gateway_connection
.gateway_response_timeout,
self.debug_config.gateway_response_timeout,
self.bandwidth_controller.take(),
shutdown,
);
@@ -502,7 +500,7 @@ where
);
Self::start_topology_refresher(
topology_provider,
self.debug_config.topology.topology_refresh_rate,
self.debug_config.topology_refresh_rate,
shared_topology_accessor.clone(),
task_manager.subscribe(),
)
@@ -538,7 +536,7 @@ where
self_address,
);
if let Some(size) = self.debug_config.traffic.use_extended_packet_size {
if let Some(size) = self.debug_config.use_extended_packet_size {
log::debug!("Setting extended packet size: {:?}", size);
controller_config.set_custom_packet_size(size.into());
}
@@ -557,11 +555,7 @@ where
task_manager.subscribe(),
);
if !self
.debug_config
.cover_traffic
.disable_loop_cover_traffic_stream
{
if !self.debug_config.disable_loop_cover_traffic_stream {
Self::start_cover_traffic_stream(
self.debug_config,
self.key_manager.ack_key(),
@@ -30,12 +30,8 @@ async fn setup_fresh_backend<P: AsRef<Path>>(
// it will only be happening on the very first run and in practice won't incur huge
// costs since the storage is going to be empty
let mem_store = CombinedReplyStorage::new(
debug_config
.reply_surbs
.minimum_reply_surb_storage_threshold,
debug_config
.reply_surbs
.maximum_reply_surb_storage_threshold,
debug_config.minimum_reply_surb_storage_threshold,
debug_config.maximum_reply_surb_storage_threshold,
);
storage_backend
.init_fresh(&mem_store)
@@ -50,12 +46,8 @@ async fn setup_fresh_backend<P: AsRef<Path>>(
fn setup_inactive_backend(debug_config: &DebugConfig) -> fs_backend::Backend {
info!("creating inactive surb database");
fs_backend::Backend::new_inactive(
debug_config
.reply_surbs
.minimum_reply_surb_storage_threshold,
debug_config
.reply_surbs
.maximum_reply_surb_storage_threshold,
debug_config.minimum_reply_surb_storage_threshold,
debug_config.maximum_reply_surb_storage_threshold,
)
}
@@ -153,35 +153,25 @@ impl Config {
ack_key,
self_recipient,
packet_size: Default::default(),
ack_wait_addition: base_client_debug_config.acknowledgements.ack_wait_addition,
ack_wait_multiplier: base_client_debug_config
.acknowledgements
.ack_wait_multiplier,
average_message_sending_delay: base_client_debug_config
.traffic
.message_sending_average_delay,
average_packet_delay_duration: base_client_debug_config.traffic.average_packet_delay,
average_ack_delay_duration: base_client_debug_config.acknowledgements.average_ack_delay,
ack_wait_addition: base_client_debug_config.ack_wait_addition,
ack_wait_multiplier: base_client_debug_config.ack_wait_multiplier,
average_message_sending_delay: base_client_debug_config.message_sending_average_delay,
average_packet_delay_duration: base_client_debug_config.average_packet_delay,
average_ack_delay_duration: base_client_debug_config.average_ack_delay,
disable_main_poisson_packet_distribution: base_client_debug_config
.traffic
.disable_main_poisson_packet_distribution,
minimum_reply_surb_request_size: base_client_debug_config
.reply_surbs
.minimum_reply_surb_request_size,
maximum_reply_surb_request_size: base_client_debug_config
.reply_surbs
.maximum_reply_surb_request_size,
maximum_allowed_reply_surb_request_size: base_client_debug_config
.reply_surbs
.maximum_allowed_reply_surb_request_size,
maximum_reply_surb_rerequest_waiting_period: base_client_debug_config
.reply_surbs
.maximum_reply_surb_rerequest_waiting_period,
maximum_reply_surb_drop_waiting_period: base_client_debug_config
.reply_surbs
.maximum_reply_surb_drop_waiting_period,
maximum_reply_surb_age: base_client_debug_config.reply_surbs.maximum_reply_surb_age,
maximum_reply_key_age: base_client_debug_config.reply_surbs.maximum_reply_key_age,
maximum_reply_surb_age: base_client_debug_config.maximum_reply_surb_age,
maximum_reply_key_age: base_client_debug_config.maximum_reply_key_age,
}
}
@@ -35,12 +35,8 @@ impl ReplyStorageBackend for Backend {
) -> Result<Self, Self::StorageError> {
Ok(Backend {
empty: Empty {
min_surb_threshold: debug_config
.reply_surbs
.minimum_reply_surb_storage_threshold,
max_surb_threshold: debug_config
.reply_surbs
.maximum_reply_surb_storage_threshold,
min_surb_threshold: debug_config.minimum_reply_surb_storage_threshold,
max_surb_threshold: debug_config.maximum_reply_surb_storage_threshold,
},
})
}
@@ -35,12 +35,8 @@ impl ReplyStorageBackend for Empty {
_db_path: Option<PathBuf>,
) -> Result<Self, Self::StorageError> {
Ok(Self {
min_surb_threshold: debug_config
.reply_surbs
.minimum_reply_surb_storage_threshold,
max_surb_threshold: debug_config
.reply_surbs
.maximum_reply_surb_storage_threshold,
min_surb_threshold: debug_config.minimum_reply_surb_storage_threshold,
max_surb_threshold: debug_config.maximum_reply_surb_storage_threshold,
})
}
+92 -193
View File
@@ -13,7 +13,6 @@ use url::Url;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
pub mod old_config_v1_1_13;
pub mod persistence;
pub const MISSING_VALUE: &str = "MISSING VALUE";
@@ -211,12 +210,11 @@ impl<T> Config<T> {
}
pub fn set_high_default_traffic_volume(&mut self) {
self.debug.traffic.average_packet_delay = Duration::from_millis(10);
self.debug.average_packet_delay = Duration::from_millis(10);
// basically don't really send cover messages
self.debug.cover_traffic.loop_cover_traffic_average_delay =
Duration::from_millis(2_000_000);
self.debug.loop_cover_traffic_average_delay = Duration::from_millis(2_000_000);
// 250 "real" messages / s
self.debug.traffic.message_sending_average_delay = Duration::from_millis(4);
self.debug.message_sending_average_delay = Duration::from_millis(4);
}
pub fn with_disabled_cover_traffic(mut self, disabled: bool) -> Self {
@@ -227,8 +225,8 @@ impl<T> Config<T> {
}
pub fn set_no_cover_traffic(&mut self) {
self.debug.cover_traffic.disable_loop_cover_traffic_stream = true;
self.debug.traffic.disable_main_poisson_packet_distribution = true;
self.debug.disable_loop_cover_traffic_stream = true;
self.debug.disable_main_poisson_packet_distribution = true;
}
pub fn set_custom_version(&mut self, version: &str) {
@@ -313,93 +311,87 @@ impl<T> Config<T> {
}
pub fn get_average_packet_delay(&self) -> Duration {
self.debug.traffic.average_packet_delay
self.debug.average_packet_delay
}
pub fn get_average_ack_delay(&self) -> Duration {
self.debug.acknowledgements.average_ack_delay
self.debug.average_ack_delay
}
pub fn get_ack_wait_multiplier(&self) -> f64 {
self.debug.acknowledgements.ack_wait_multiplier
self.debug.ack_wait_multiplier
}
pub fn get_ack_wait_addition(&self) -> Duration {
self.debug.acknowledgements.ack_wait_addition
self.debug.ack_wait_addition
}
pub fn get_loop_cover_traffic_average_delay(&self) -> Duration {
self.debug.cover_traffic.loop_cover_traffic_average_delay
self.debug.loop_cover_traffic_average_delay
}
pub fn get_message_sending_average_delay(&self) -> Duration {
self.debug.traffic.message_sending_average_delay
self.debug.message_sending_average_delay
}
pub fn get_gateway_response_timeout(&self) -> Duration {
self.debug.gateway_connection.gateway_response_timeout
self.debug.gateway_response_timeout
}
pub fn get_topology_refresh_rate(&self) -> Duration {
self.debug.topology.topology_refresh_rate
self.debug.topology_refresh_rate
}
pub fn get_topology_resolution_timeout(&self) -> Duration {
self.debug.topology.topology_resolution_timeout
self.debug.topology_resolution_timeout
}
pub fn get_disabled_loop_cover_traffic_stream(&self) -> bool {
self.debug.cover_traffic.disable_loop_cover_traffic_stream
self.debug.disable_loop_cover_traffic_stream
}
pub fn get_disabled_main_poisson_packet_distribution(&self) -> bool {
self.debug.traffic.disable_main_poisson_packet_distribution
self.debug.disable_main_poisson_packet_distribution
}
pub fn get_use_extended_packet_size(&self) -> Option<ExtendedPacketSize> {
self.debug.traffic.use_extended_packet_size
self.debug.use_extended_packet_size
}
pub fn get_minimum_reply_surb_storage_threshold(&self) -> usize {
self.debug.reply_surbs.minimum_reply_surb_storage_threshold
self.debug.minimum_reply_surb_storage_threshold
}
pub fn get_maximum_reply_surb_storage_threshold(&self) -> usize {
self.debug.reply_surbs.maximum_reply_surb_storage_threshold
self.debug.maximum_reply_surb_storage_threshold
}
pub fn get_minimum_reply_surb_request_size(&self) -> u32 {
self.debug.reply_surbs.minimum_reply_surb_request_size
self.debug.minimum_reply_surb_request_size
}
pub fn get_maximum_reply_surb_request_size(&self) -> u32 {
self.debug.reply_surbs.maximum_reply_surb_request_size
self.debug.maximum_reply_surb_request_size
}
pub fn get_maximum_allowed_reply_surb_request_size(&self) -> u32 {
self.debug
.reply_surbs
.maximum_allowed_reply_surb_request_size
self.debug.maximum_allowed_reply_surb_request_size
}
pub fn get_maximum_reply_surb_rerequest_waiting_period(&self) -> Duration {
self.debug
.reply_surbs
.maximum_reply_surb_rerequest_waiting_period
self.debug.maximum_reply_surb_rerequest_waiting_period
}
pub fn get_maximum_reply_surb_drop_waiting_period(&self) -> Duration {
self.debug
.reply_surbs
.maximum_reply_surb_drop_waiting_period
self.debug.maximum_reply_surb_drop_waiting_period
}
pub fn get_maximum_reply_surb_age(&self) -> Duration {
self.debug.reply_surbs.maximum_reply_surb_age
self.debug.maximum_reply_surb_age
}
pub fn get_maximum_reply_key_age(&self) -> Duration {
self.debug.reply_surbs.maximum_reply_key_age
self.debug.maximum_reply_key_age
}
}
@@ -458,63 +450,63 @@ impl From<nym_topology::gateway::Node> for GatewayEndpointConfig {
pub struct Client<T> {
/// Version of the client for which this configuration was created.
#[serde(default = "missing_string_value")]
pub version: String,
version: String,
/// ID specifies the human readable ID of this particular client.
pub id: String,
id: String,
/// Indicates whether this client is running in a disabled credentials mode, thus attempting
/// to claim bandwidth without presenting bandwidth credentials.
#[serde(default)]
pub disabled_credentials_mode: bool,
disabled_credentials_mode: bool,
/// Addresses to nyxd validators via which the client can communicate with the chain.
#[serde(alias = "validator_urls")]
pub nyxd_urls: Vec<Url>,
nyxd_urls: Vec<Url>,
/// Addresses to APIs running on validator from which the client gets the view of the network.
#[serde(alias = "validator_api_urls")]
pub nym_api_urls: Vec<Url>,
nym_api_urls: Vec<Url>,
/// Path to file containing private identity key.
pub private_identity_key_file: PathBuf,
private_identity_key_file: PathBuf,
/// Path to file containing public identity key.
pub public_identity_key_file: PathBuf,
public_identity_key_file: PathBuf,
/// Path to file containing private encryption key.
pub private_encryption_key_file: PathBuf,
private_encryption_key_file: PathBuf,
/// Path to file containing public encryption key.
pub public_encryption_key_file: PathBuf,
public_encryption_key_file: PathBuf,
/// Path to file containing shared key derived with the specified gateway that is used
/// for all communication with it.
pub gateway_shared_key_file: PathBuf,
gateway_shared_key_file: PathBuf,
/// Path to file containing key used for encrypting and decrypting the content of an
/// acknowledgement so that nobody besides the client knows which packet it refers to.
pub ack_key_file: PathBuf,
ack_key_file: PathBuf,
/// Information regarding how the client should send data to gateway.
pub gateway_endpoint: GatewayEndpointConfig,
gateway_endpoint: GatewayEndpointConfig,
/// Path to the database containing bandwidth credentials of this client.
pub database_path: PathBuf,
database_path: PathBuf,
/// Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
// this was set to use #[serde(default)] for the purposes of compatibility for multi-surbs introduced in 1.1.4.
// if you're reading this message and we have already introduced some breaking changes, feel free
// to remove that attribute since at this point the client configs should have gotten regenerated
#[serde(default)]
pub reply_surb_database_path: PathBuf,
reply_surb_database_path: PathBuf,
/// nym_home_directory specifies absolute path to the home nym Clients directory.
/// It is expected to use default value and hence .toml file should not redefine this field.
pub nym_root_directory: PathBuf,
nym_root_directory: PathBuf,
#[serde(skip)]
pub super_struct: PhantomData<T>,
super_struct: PhantomData<T>,
}
impl<T: NymConfig> Default for Client<T> {
@@ -595,9 +587,9 @@ impl<T: NymConfig> Client<T> {
#[serde(deny_unknown_fields)]
pub struct Logging {}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct Traffic {
pub struct DebugConfig {
/// The parameter of Poisson distribution determining how long, on average,
/// sent packet is going to be delayed at any given mix node.
/// So for a packet going through three mix nodes, on average, it will take three times this value
@@ -605,74 +597,6 @@ pub struct Traffic {
#[serde(with = "humantime_serde")]
pub average_packet_delay: Duration,
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take another 'real traffic stream' message to be sent.
/// If no real packets are available and cover traffic is enabled,
/// a loop cover message is sent instead in order to preserve the rate.
#[serde(with = "humantime_serde")]
pub message_sending_average_delay: Duration,
/// Controls whether the main packet stream constantly produces packets according to the predefined
/// poisson distribution.
pub disable_main_poisson_packet_distribution: bool,
/// Controls whether the sent sphinx packet use a NON-DEFAULT bigger size.
pub use_extended_packet_size: Option<ExtendedPacketSize>,
}
impl Default for Traffic {
fn default() -> Self {
Traffic {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
disable_main_poisson_packet_distribution: false,
use_extended_packet_size: None,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct CoverTraffic {
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take for another loop cover traffic message to be sent.
#[serde(with = "humantime_serde")]
pub loop_cover_traffic_average_delay: Duration,
/// Controls whether the dedicated loop cover traffic stream should be enabled.
/// (and sending packets, on average, every [Self::loop_cover_traffic_average_delay])
pub disable_loop_cover_traffic_stream: bool,
}
impl Default for CoverTraffic {
fn default() -> Self {
CoverTraffic {
loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
disable_loop_cover_traffic_stream: false,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct GatewayConnection {
/// How long we're willing to wait for a response to a message sent to the gateway,
/// before giving up on it.
#[serde(with = "humantime_serde")]
pub gateway_response_timeout: Duration,
}
impl Default for GatewayConnection {
fn default() -> Self {
GatewayConnection {
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct Acknowledgements {
/// The parameter of Poisson distribution determining how long, on average,
/// sent acknowledgement is going to be delayed at any given mix node.
/// So for an ack going through three mix nodes, on average, it will take three times this value
@@ -690,21 +614,24 @@ pub struct Acknowledgements {
/// In an ideal network with 0 latency, this value would have been 0.
#[serde(with = "humantime_serde")]
pub ack_wait_addition: Duration,
}
impl Default for Acknowledgements {
fn default() -> Self {
Acknowledgements {
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION,
}
}
}
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take for another loop cover traffic message to be sent.
#[serde(with = "humantime_serde")]
pub loop_cover_traffic_average_delay: Duration,
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take another 'real traffic stream' message to be sent.
/// If no real packets are available and cover traffic is enabled,
/// a loop cover message is sent instead in order to preserve the rate.
#[serde(with = "humantime_serde")]
pub message_sending_average_delay: Duration,
/// How long we're willing to wait for a response to a message sent to the gateway,
/// before giving up on it.
#[serde(with = "humantime_serde")]
pub gateway_response_timeout: Duration,
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct Topology {
/// The uniform delay every which clients are querying the directory server
/// to try to obtain a compatible network topology to send sphinx packets through.
#[serde(with = "humantime_serde")]
@@ -715,20 +642,18 @@ pub struct Topology {
/// did not reach its destination.
#[serde(with = "humantime_serde")]
pub topology_resolution_timeout: Duration,
}
impl Default for Topology {
fn default() -> Self {
Topology {
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
}
}
}
/// Controls whether the dedicated loop cover traffic stream should be enabled.
/// (and sending packets, on average, every [Self::loop_cover_traffic_average_delay])
pub disable_loop_cover_traffic_stream: bool,
/// Controls whether the main packet stream constantly produces packets according to the predefined
/// poisson distribution.
pub disable_main_poisson_packet_distribution: bool,
/// Controls whether the sent sphinx packet use a NON-DEFAULT bigger size.
pub use_extended_packet_size: Option<ExtendedPacketSize>,
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct ReplySurbs {
/// Defines the minimum number of reply surbs the client wants to keep in its storage at all times.
/// It can only allow to go below that value if its to request additional reply surbs.
pub minimum_reply_surb_storage_threshold: usize,
@@ -766,9 +691,29 @@ pub struct ReplySurbs {
pub maximum_reply_key_age: Duration,
}
impl Default for ReplySurbs {
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum ExtendedPacketSize {
Extended8,
Extended16,
Extended32,
}
impl Default for DebugConfig {
fn default() -> Self {
ReplySurbs {
DebugConfig {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION,
loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
disable_loop_cover_traffic_stream: false,
disable_main_poisson_packet_distribution: false,
use_extended_packet_size: None,
minimum_reply_surb_storage_threshold: DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
maximum_reply_surb_storage_threshold: DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD,
minimum_reply_surb_request_size: DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE,
@@ -783,52 +728,6 @@ impl Default for ReplySurbs {
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct DebugConfig {
/// Defines all configuration options related to traffic streams.
pub traffic: Traffic,
/// Defines all configuration options related to cover traffic stream(s).
pub cover_traffic: CoverTraffic,
/// Defines all configuration options related to the gateway connection.
pub gateway_connection: GatewayConnection,
/// Defines all configuration options related to acknowledgements, such as delays or wait timeouts.
pub acknowledgements: Acknowledgements,
/// Defines all configuration options related topology, such as refresh rates or timeouts.
pub topology: Topology,
/// Defines all configuration options related to reply SURBs.
pub reply_surbs: ReplySurbs,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum ExtendedPacketSize {
Extended8,
Extended16,
Extended32,
}
// it could be derived, sure, but I'd rather have an explicit implementation in case we had to change
// something manually at some point
#[allow(clippy::derivable_impls)]
impl Default for DebugConfig {
fn default() -> Self {
DebugConfig {
traffic: Default::default(),
cover_traffic: Default::default(),
gateway_connection: Default::default(),
acknowledgements: Default::default(),
topology: Default::default(),
reply_surbs: Default::default(),
}
}
}
impl From<ExtendedPacketSize> for PacketSize {
fn from(size: ExtendedPacketSize) -> PacketSize {
match size {
@@ -1,198 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::{
Acknowledgements, Client, Config, CoverTraffic, DebugConfig, ExtendedPacketSize,
GatewayConnection, Logging, ReplySurbs, Topology, Traffic, DEFAULT_ACK_WAIT_ADDITION,
DEFAULT_ACK_WAIT_MULTIPLIER, DEFAULT_AVERAGE_PACKET_DELAY, DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY, DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE,
DEFAULT_MAXIMUM_REPLY_KEY_AGE, DEFAULT_MAXIMUM_REPLY_SURB_AGE,
DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD, DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE,
DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD,
DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD, DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE, DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
DEFAULT_TOPOLOGY_REFRESH_RATE, DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
};
use nym_config::NymConfig;
use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
use std::time::Duration;
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct OldConfigV1_1_13<T> {
pub client: Client<T>,
#[serde(default)]
logging: Logging,
#[serde(default)]
debug: OldDebugConfigV1_1_13,
}
impl<T: NymConfig> Default for OldConfigV1_1_13<T> {
fn default() -> Self {
OldConfigV1_1_13 {
client: Client::<T>::default(),
logging: Default::default(),
debug: Default::default(),
}
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct OldDebugConfigV1_1_13 {
#[serde(with = "humantime_serde")]
pub average_packet_delay: Duration,
#[serde(with = "humantime_serde")]
pub average_ack_delay: Duration,
pub ack_wait_multiplier: f64,
#[serde(with = "humantime_serde")]
pub ack_wait_addition: Duration,
#[serde(with = "humantime_serde")]
pub loop_cover_traffic_average_delay: Duration,
#[serde(with = "humantime_serde")]
pub message_sending_average_delay: Duration,
#[serde(with = "humantime_serde")]
pub gateway_response_timeout: Duration,
#[serde(with = "humantime_serde")]
pub topology_refresh_rate: Duration,
#[serde(with = "humantime_serde")]
pub topology_resolution_timeout: Duration,
pub disable_loop_cover_traffic_stream: bool,
pub disable_main_poisson_packet_distribution: bool,
pub use_extended_packet_size: Option<ExtendedPacketSize>,
pub minimum_reply_surb_storage_threshold: usize,
pub maximum_reply_surb_storage_threshold: usize,
pub minimum_reply_surb_request_size: u32,
pub maximum_reply_surb_request_size: u32,
pub maximum_allowed_reply_surb_request_size: u32,
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_rerequest_waiting_period: Duration,
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_drop_waiting_period: Duration,
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_age: Duration,
#[serde(with = "humantime_serde")]
pub maximum_reply_key_age: Duration,
}
impl From<OldDebugConfigV1_1_13> for DebugConfig {
fn from(value: OldDebugConfigV1_1_13) -> Self {
DebugConfig {
traffic: Traffic {
average_packet_delay: value.average_packet_delay,
message_sending_average_delay: value.message_sending_average_delay,
disable_main_poisson_packet_distribution: value
.disable_main_poisson_packet_distribution,
use_extended_packet_size: value.use_extended_packet_size,
},
cover_traffic: CoverTraffic {
loop_cover_traffic_average_delay: value.loop_cover_traffic_average_delay,
disable_loop_cover_traffic_stream: value.disable_loop_cover_traffic_stream,
},
gateway_connection: GatewayConnection {
gateway_response_timeout: value.gateway_response_timeout,
},
acknowledgements: Acknowledgements {
average_ack_delay: value.average_ack_delay,
ack_wait_multiplier: value.ack_wait_multiplier,
ack_wait_addition: value.ack_wait_addition,
},
topology: Topology {
topology_refresh_rate: value.topology_refresh_rate,
topology_resolution_timeout: value.topology_resolution_timeout,
},
reply_surbs: ReplySurbs {
minimum_reply_surb_storage_threshold: value.minimum_reply_surb_storage_threshold,
maximum_reply_surb_storage_threshold: value.maximum_reply_surb_storage_threshold,
minimum_reply_surb_request_size: value.minimum_reply_surb_request_size,
maximum_reply_surb_request_size: value.maximum_reply_surb_request_size,
maximum_allowed_reply_surb_request_size: value
.maximum_allowed_reply_surb_request_size,
maximum_reply_surb_rerequest_waiting_period: value
.maximum_reply_surb_rerequest_waiting_period,
maximum_reply_surb_drop_waiting_period: value
.maximum_reply_surb_drop_waiting_period,
maximum_reply_surb_age: value.maximum_reply_surb_age,
maximum_reply_key_age: value.maximum_reply_key_age,
},
}
}
}
impl Default for OldDebugConfigV1_1_13 {
fn default() -> Self {
OldDebugConfigV1_1_13 {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION,
loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
disable_loop_cover_traffic_stream: false,
disable_main_poisson_packet_distribution: false,
use_extended_packet_size: None,
minimum_reply_surb_storage_threshold: DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
maximum_reply_surb_storage_threshold: DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD,
minimum_reply_surb_request_size: DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE,
maximum_reply_surb_request_size: DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE,
maximum_allowed_reply_surb_request_size: DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE,
maximum_reply_surb_rerequest_waiting_period:
DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD,
maximum_reply_surb_drop_waiting_period: DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD,
maximum_reply_surb_age: DEFAULT_MAXIMUM_REPLY_SURB_AGE,
maximum_reply_key_age: DEFAULT_MAXIMUM_REPLY_KEY_AGE,
}
}
}
impl<T, U> From<OldConfigV1_1_13<T>> for Config<U> {
fn from(value: OldConfigV1_1_13<T>) -> Self {
Config {
client: Client {
version: value.client.version,
id: value.client.id,
disabled_credentials_mode: value.client.disabled_credentials_mode,
nyxd_urls: value.client.nyxd_urls,
nym_api_urls: value.client.nym_api_urls,
private_identity_key_file: value.client.private_identity_key_file,
public_identity_key_file: value.client.public_identity_key_file,
private_encryption_key_file: value.client.private_encryption_key_file,
public_encryption_key_file: value.client.public_encryption_key_file,
gateway_shared_key_file: value.client.gateway_shared_key_file,
ack_key_file: value.client.ack_key_file,
gateway_endpoint: value.client.gateway_endpoint,
database_path: value.client.database_path,
reply_surb_database_path: value.client.reply_surb_database_path,
nym_root_directory: value.client.nym_root_directory,
super_struct: PhantomData,
},
logging: value.logging,
debug: value.debug.into(),
}
}
}
+2 -2
View File
@@ -112,14 +112,14 @@ where
{
let id = config.get_id();
// If we are not going to register gateway, and an explicitly chosen gateway is not passed in,
// If we are not going to register gateway, and an explicitly chosed gateway is not passed in,
// load the existing configuration file
if !register_gateway && user_chosen_gateway_id.is_none() {
println!("Not registering gateway, will reuse existing config and keys");
return load_existing_gateway_config::<C>(&id);
}
// Else, we proceed by querying the nym-api
// Else, we preceed by querying the nym-api
let gateway = helpers::query_gateway_details(
config.get_nym_api_endpoints(),
user_chosen_gateway_id,
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "1.1.14"
version = "1.1.13"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
-1
View File
@@ -15,7 +15,6 @@ pub use client_core::config::Config as BaseConfig;
pub use client_core::config::MISSING_VALUE;
pub use client_core::config::{DebugConfig, GatewayEndpointConfig};
pub mod old_config_v1_1_13;
mod template;
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone, Copy)]
@@ -1,60 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::config::{Config, Socket};
use client_core::config::old_config_v1_1_13::OldConfigV1_1_13 as OldBaseConfigV1_1_13;
use nym_config::NymConfig;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct OldConfigV1_1_13 {
#[serde(flatten)]
base: OldBaseConfigV1_1_13<OldConfigV1_1_13>,
socket: Socket,
}
impl NymConfig for OldConfigV1_1_13 {
fn template() -> &'static str {
// not intended to be used
unimplemented!()
}
fn default_root_directory() -> PathBuf {
dirs::home_dir()
.expect("Failed to evaluate $HOME value")
.join(".nym")
.join("clients")
}
fn try_default_root_directory() -> Option<PathBuf> {
dirs::home_dir().map(|path| path.join(".nym").join("clients"))
}
fn root_directory(&self) -> PathBuf {
self.base.client.nym_root_directory.clone()
}
fn config_directory(&self) -> PathBuf {
self.root_directory()
.join(&self.base.client.id)
.join("config")
}
fn data_directory(&self) -> PathBuf {
self.root_directory()
.join(&self.base.client.id)
.join("data")
}
}
impl From<OldConfigV1_1_13> for Config {
fn from(value: OldConfigV1_1_13) -> Self {
Config {
base: value.base.into(),
socket: value.socket,
}
}
}
+4 -9
View File
@@ -110,15 +110,10 @@ host = '{{ socket.host }}'
[debug]
[debug.traffic]
average_packet_delay = '{{ debug.traffic.average_packet_delay }}'
message_sending_average_delay = '{{ debug.traffic.message_sending_average_delay }}'
[debug.acknowledgements]
average_ack_delay = '{{ debug.acknowledgements.average_ack_delay }}'
[debug.cover_traffic]
loop_cover_traffic_average_delay = '{{ debug.cover_traffic.loop_cover_traffic_average_delay }}'
average_packet_delay = '{{ debug.average_packet_delay }}'
average_ack_delay = '{{ debug.average_ack_delay }}'
loop_cover_traffic_average_delay = '{{ debug.loop_cover_traffic_average_delay }}'
message_sending_average_delay = '{{ debug.message_sending_average_delay }}'
"#
}
-4
View File
@@ -1,7 +1,6 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::try_upgrade_v1_1_13_config;
use crate::{
client::config::Config,
commands::{override_config, OverrideConfig},
@@ -122,9 +121,6 @@ pub(crate) async fn execute(args: &Init) -> Result<(), ClientError> {
let already_init = Config::default_config_file_path(id).exists();
if already_init {
// in case we're using old config, try to upgrade it
// (if we're using the current version, it's a no-op)
try_upgrade_v1_1_13_config(id)?;
println!("Client \"{id}\" was already initialised before");
}
+1 -17
View File
@@ -1,15 +1,13 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::config::old_config_v1_1_13::OldConfigV1_1_13;
use crate::client::config::{BaseConfig, Config};
use clap::CommandFactory;
use clap::{Parser, Subcommand};
use lazy_static::lazy_static;
use log::info;
use nym_bin_common::build_information::BinaryBuildInformation;
use nym_bin_common::completions::{fig_generate, ArgShell};
use nym_config::{NymConfig, OptionalSet};
use nym_config::OptionalSet;
use std::error::Error;
use std::net::IpAddr;
@@ -104,20 +102,6 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
)
}
fn try_upgrade_v1_1_13_config(id: &str) -> std::io::Result<()> {
// explicitly load it as v1.1.13 (which is incompatible with the current, i.e. 1.1.14+)
let Ok(old_config) = OldConfigV1_1_13::load_from_file(id) else {
// if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(());
};
info!("It seems the client is using <= v1.1.13 config template.");
info!("It is going to get updated to the current specification.");
let updated: Config = old_config.into();
updated.save_to_file(None)
}
#[cfg(test)]
mod tests {
use super::*;
+1 -5
View File
@@ -4,12 +4,12 @@
use std::error::Error;
use std::net::IpAddr;
use crate::commands::try_upgrade_v1_1_13_config;
use crate::{
client::{config::Config, SocketClient},
commands::{override_config, OverrideConfig},
error::ClientError,
};
use clap::Args;
use log::*;
use nym_bin_common::version_checker::is_minor_version_compatible;
@@ -100,10 +100,6 @@ fn version_check(cfg: &Config) -> bool {
pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn Error + Send + Sync>> {
let id = &args.id;
// in case we're using old config, try to upgrade it
// (if we're using the current version, it's a no-op)
try_upgrade_v1_1_13_config(id)?;
let mut config = match Config::load_from_file(id) {
Ok(cfg) => cfg,
Err(err) => {
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.14"
version = "1.1.13"
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"
-1
View File
@@ -15,7 +15,6 @@ use std::fmt::Debug;
use std::path::PathBuf;
use std::str::FromStr;
pub mod old_config_v1_1_13;
mod template;
const DEFAULT_CONNECTION_START_SURBS: u32 = 20;
@@ -1,66 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::config::{Config, Socks5, Socks5Debug};
use client_core::config::old_config_v1_1_13::OldConfigV1_1_13 as OldBaseConfigV1_1_13;
use nym_config::NymConfig;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct OldConfigV1_1_13 {
#[serde(flatten)]
base: OldBaseConfigV1_1_13<OldConfigV1_1_13>,
socks5: Socks5,
#[serde(default)]
socks5_debug: Socks5Debug,
}
impl NymConfig for OldConfigV1_1_13 {
fn template() -> &'static str {
// not intended to be used
unimplemented!()
}
fn default_root_directory() -> PathBuf {
#[cfg(not(feature = "mobile"))]
let base_dir = dirs::home_dir().expect("Failed to evaluate $HOME value");
#[cfg(feature = "mobile")]
let base_dir = PathBuf::from("/tmp");
base_dir.join(".nym").join("socks5-clients")
}
fn try_default_root_directory() -> Option<PathBuf> {
dirs::home_dir().map(|path| path.join(".nym").join("socks5-clients"))
}
fn root_directory(&self) -> PathBuf {
self.base.client.nym_root_directory.clone()
}
fn config_directory(&self) -> PathBuf {
self.root_directory()
.join(&self.base.client.id)
.join("config")
}
fn data_directory(&self) -> PathBuf {
self.root_directory()
.join(&self.base.client.id)
.join("data")
}
}
impl From<OldConfigV1_1_13> for Config {
fn from(value: OldConfigV1_1_13) -> Self {
Config {
base: value.base.into(),
socks5: value.socks5,
socks5_debug: value.socks5_debug,
}
}
}
+4 -9
View File
@@ -114,15 +114,10 @@ send_anonymously = {{ socks5.send_anonymously }}
[debug]
[debug.traffic]
average_packet_delay = '{{ debug.traffic.average_packet_delay }}'
message_sending_average_delay = '{{ debug.traffic.message_sending_average_delay }}'
[debug.acknowledgements]
average_ack_delay = '{{ debug.acknowledgements.average_ack_delay }}'
[debug.cover_traffic]
loop_cover_traffic_average_delay = '{{ debug.cover_traffic.loop_cover_traffic_average_delay }}'
average_packet_delay = '{{ debug.average_packet_delay }}'
average_ack_delay = '{{ debug.average_ack_delay }}'
loop_cover_traffic_average_delay = '{{ debug.loop_cover_traffic_average_delay }}'
message_sending_average_delay = '{{ debug.message_sending_average_delay }}'
"#
}
-4
View File
@@ -1,7 +1,6 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::try_upgrade_v1_1_13_config;
use crate::{
client::config::Config,
commands::{override_config, OverrideConfig},
@@ -125,9 +124,6 @@ pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> {
let already_init = Config::default_config_file_path(id).exists();
if already_init {
// in case we're using old config, try to upgrade it
// (if we're using the current version, it's a no-op)
try_upgrade_v1_1_13_config(id)?;
println!("SOCKS5 client \"{id}\" was already initialised before");
}
+1 -17
View File
@@ -1,15 +1,13 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::config::old_config_v1_1_13::OldConfigV1_1_13;
use crate::client::config::{BaseConfig, Config};
use clap::CommandFactory;
use clap::{Parser, Subcommand};
use lazy_static::lazy_static;
use log::info;
use nym_bin_common::build_information::BinaryBuildInformation;
use nym_bin_common::completions::{fig_generate, ArgShell};
use nym_config::{NymConfig, OptionalSet};
use nym_config::OptionalSet;
use std::error::Error;
pub mod init;
@@ -103,20 +101,6 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
)
}
fn try_upgrade_v1_1_13_config(id: &str) -> std::io::Result<()> {
// explicitly load it as v1.1.13 (which is incompatible with the current, i.e. 1.1.14+)
let Ok(old_config) = OldConfigV1_1_13::load_from_file(id) else {
// if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(());
};
info!("It seems the client is using <= v1.1.13 config template.");
info!("It is going to get updated to the current specification.");
let updated: Config = old_config.into();
updated.save_to_file(None)
}
#[cfg(test)]
mod tests {
use super::*;
+1 -5
View File
@@ -1,12 +1,12 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::try_upgrade_v1_1_13_config;
use crate::{
client::{config::Config, NymClient},
commands::{override_config, OverrideConfig},
error::Socks5ClientError,
};
use clap::Args;
use log::*;
use nym_bin_common::version_checker::is_minor_version_compatible;
@@ -108,10 +108,6 @@ fn version_check(cfg: &Config) -> bool {
pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let id = &args.id;
// in case we're using old config, try to upgrade it
// (if we're using the current version, it's a no-op)
try_upgrade_v1_1_13_config(id)?;
let mut config = match Config::load_from_file(id) {
Ok(cfg) => cfg,
Err(err) => {
+102 -7
View File
@@ -22,6 +22,7 @@ import {
MixNodeDetails,
MixNodeRewarding,
MixOwnershipResponse,
OriginalVestingResponse,
PagedAllDelegationsResponse,
PagedDelegatorDelegationsResponse,
PagedGatewayResponse,
@@ -32,10 +33,11 @@ import {
RewardingParams,
StakeSaturationResponse,
UnbondedMixnodeResponse,
VestingAccountInfo,
ContractState, VestingAccountsCoinPaged, VestingAccountsPaged, DelegationTimes, Delegations, Period, VestingAccountNode
} from '@nymproject/types';
import QueryClient from './query-client';
import SigningClient, { ISigningClient } from './signing-client';
import { ContractState } from './types/shared';
export interface INymClient {
readonly mixnetContract: string;
@@ -204,7 +206,7 @@ export default class ValidatorClient implements INymClient {
let mixNodes: UnbondedMixnodeResponse[] = [];
const limit = 50;
let startAfter;
for (;;) {
for (; ;) {
// eslint-disable-next-line no-await-in-loop
const pagedResponse: PagedUnbondedMixnodesResponse = await this.client.getUnbondedMixNodes(
this.mixnetContract,
@@ -227,7 +229,7 @@ export default class ValidatorClient implements INymClient {
let mixNodes: MixNodeBond[] = [];
const limit = 50;
let startAfter;
for (;;) {
for (; ;) {
// eslint-disable-next-line no-await-in-loop
const pagedResponse: PagedMixNodeBondResponse = await this.client.getMixNodeBonds(
this.mixnetContract,
@@ -249,7 +251,7 @@ export default class ValidatorClient implements INymClient {
let mixNodes: MixNodeDetails[] = [];
const limit = 50;
let startAfter;
for (;;) {
for (; ;) {
// eslint-disable-next-line no-await-in-loop
const pagedResponse: PagedMixNodeDetailsResponse = await this.client.getMixNodesDetailed(
this.mixnetContract,
@@ -281,7 +283,7 @@ export default class ValidatorClient implements INymClient {
let delegations: Delegation[] = [];
const limit = 250;
let startAfter;
for (;;) {
for (; ;) {
// eslint-disable-next-line no-await-in-loop
const pagedResponse: PagedMixDelegationsResponse = await this.client.getMixNodeDelegationsPaged(
this.mixnetContract,
@@ -304,7 +306,7 @@ export default class ValidatorClient implements INymClient {
let delegations: Delegation[] = [];
const limit = 250;
let startAfter;
for (;;) {
for (; ;) {
// eslint-disable-next-line no-await-in-loop
const pagedResponse: PagedDelegatorDelegationsResponse = await this.client.getDelegatorDelegationsPaged(
this.mixnetContract,
@@ -327,7 +329,7 @@ export default class ValidatorClient implements INymClient {
let delegations: Delegation[] = [];
const limit = 250;
let startAfter;
for (;;) {
for (; ;) {
// eslint-disable-next-line no-await-in-loop
const pagedResponse: PagedAllDelegationsResponse = await this.client.getAllDelegationsPaged(
this.mixnetContract,
@@ -514,4 +516,97 @@ export default class ValidatorClient implements INymClient {
this.assertSigning();
return (this.client as ISigningClient).updateContractStateParams(this.mixnetContract, newParams, fee, memo);
}
// VESTING
// TODO - MOVE TO A DIFFERENT FILE
public async getVestingAccountsPaged(): Promise<VestingAccountsPaged> {
return this.client.getVestingAccountsPaged(this.vestingContract);
}
public async getVestingAmountsAccountsPaged(): Promise<VestingAccountsCoinPaged> {
return this.client.getVestingAmountsAccountsPaged(this.vestingContract);
}
public async getLockedTokens(vestingAccountAddress: string): Promise<Coin> {
return this.client.getLockedTokens(this.vestingContract, vestingAccountAddress);
}
public async getSpendableTokens(vestingAccountAddress: string): Promise<Coin> {
return this.client.getSpendableTokens(this.vestingContract, vestingAccountAddress);
}
public async getVestedTokens(vestingAccountAddress: string): Promise<Coin> {
return this.client.getVestedTokens(this.vestingContract, vestingAccountAddress);
}
public async getVestingTokens(vestingAccountAddress: string): Promise<Coin> {
return this.client.getVestingTokens(this.vestingContract, vestingAccountAddress);
}
public async getSpendableVestedTokens(vestingAccountAddress: string): Promise<Coin> {
return this.client.getSpendableVestedTokens(this.vestingContract, vestingAccountAddress);
}
public async getSpendableRewards(vestingAccountAddress: string): Promise<Coin> {
return this.client.getSpendableRewards(this.vestingContract, vestingAccountAddress);
}
public async getDelegatedCoins(vestingAccountAddress: string): Promise<Coin> {
return this.client.getDelegatedCoins(this.vestingContract, vestingAccountAddress);
}
public async getPledgedCoins(vestingAccountAddress: string): Promise<Coin> {
return this.client.getPledgedCoins(this.vestingContract, vestingAccountAddress);
}
public async getStakedCoins(vestingAccountAddress: string): Promise<Coin> {
return this.client.getStakedCoins(this.vestingContract, vestingAccountAddress);
}
public async getWithdrawnCoins(vestingAccountAddress: string): Promise<Coin> {
return this.client.getWithdrawnCoins(this.vestingContract, vestingAccountAddress);
}
public async getStartTime(vestingAccountAddress: string): Promise<string> {
return this.client.getStartTime(this.vestingContract, vestingAccountAddress);
}
public async getEndTime(vestingAccountAddress: string): Promise<string> {
return this.client.getEndTime(this.vestingContract, vestingAccountAddress);
}
public async getOriginalVestingDetails(vestingAccountAddress: string): Promise<OriginalVestingResponse> {
return this.client.getOriginalVestingDetails(this.vestingContract, vestingAccountAddress);
}
public async getHistoricStakingRewards(vestingAccountAddress: string): Promise<Coin> {
return this.client.getHistoricStakingRewards(this.vestingContract, vestingAccountAddress);
}
public async getAccountDetails(address: string): Promise<VestingAccountInfo> {
return this.client.getAccountDetails(this.vestingContract, address);
}
public async getMixnode(address: string): Promise<VestingAccountNode> {
return this.client.getMixnode(this.vestingContract, address);
}
public async getGateway(address: string): Promise<VestingAccountNode> {
return this.client.getGateway(this.vestingContract, address);
}
public async getDelegationTimes(mix_id: number, delegatorAddress: string): Promise<DelegationTimes> {
return this.client.getDelegationTimes(this.vestingContract, mix_id, delegatorAddress);
}
public async getAllDelegations(): Promise<Delegations> {
return this.client.getAllDelegations(this.vestingContract);
}
public async getCurrentVestingPeriod(address: string): Promise<Period> {
return this.client.getCurrentVestingPeriod(this.vestingContract, address);
}
}
+136 -2
View File
@@ -4,8 +4,8 @@
*/
// eslint-disable-next-line import/no-cycle
import { INyxdQuery } from './query-client';
import { Delegation, RewardingParams, StakeSaturationResponse } from '@nymproject/types';
import {
Delegation, OriginalVestingResponse, RewardingParams, StakeSaturationResponse, VestingAccountInfo,
UnbondedMixnodeResponse,
GatewayOwnershipResponse,
MixnetContractVersion,
@@ -18,8 +18,10 @@ import {
PagedMixNodeDetailsResponse,
PagedUnbondedMixnodesResponse,
LayerDistribution,
ContractState, VestingAccountsCoinPaged, VestingAccountsPaged, DelegationTimes, Delegations, Period, VestingAccountNode
} from '@nymproject/types';
import { ContractState, SmartContractQuery } from './types/shared';
import { SmartContractQuery } from './types/shared';
import { Coin } from 'cosmjs-types/cosmos/base/v1beta1/coin';
export default class NyxdQuerier implements INyxdQuery {
client: SmartContractQuery;
@@ -188,4 +190,136 @@ export default class NyxdQuerier implements INyxdQuery {
vesting_account_address: vestingAccountAddress,
});
}
getVestingAccountsPaged(vestingContractAddress: string): Promise<VestingAccountsPaged> {
return this.client.queryContractSmart(vestingContractAddress, {
get_accounts_paged: {}
});
}
getVestingAmountsAccountsPaged(vestingContractAddress: string): Promise<VestingAccountsCoinPaged> {
return this.client.queryContractSmart(vestingContractAddress, {
get_accounts_vesting_coins_paged: {}
});
}
getLockedTokens(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.client.queryContractSmart(vestingContractAddress, {
locked_coins: { vesting_account_address: vestingAccountAddress }
});
}
getSpendableTokens(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.client.queryContractSmart(vestingContractAddress, {
spendable_coins: { vesting_account_address: vestingAccountAddress }
});
}
getVestedTokens(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.client.queryContractSmart(vestingContractAddress, {
get_vested_coins: { vesting_account_address: vestingAccountAddress }
});
}
getVestingTokens(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.client.queryContractSmart(vestingContractAddress, {
get_vesting_coins: { vesting_account_address: vestingAccountAddress }
});
}
getSpendableVestedTokens(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.client.queryContractSmart(vestingContractAddress, {
get_spendable_vested_coins: { vesting_account_address: vestingAccountAddress }
});
}
getSpendableRewards(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.client.queryContractSmart(vestingContractAddress, {
get_spendable_reward_coins: { vesting_account_address: vestingAccountAddress }
});
}
getDelegatedCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.client.queryContractSmart(vestingContractAddress, {
get_delegated_coins: { vesting_account_address: vestingAccountAddress }
});
}
getPledgedCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.client.queryContractSmart(vestingContractAddress, {
get_pledged_coins: { vesting_account_address: vestingAccountAddress }
});
}
getStakedCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.client.queryContractSmart(vestingContractAddress, {
get_staked_coins: { vesting_account_address: vestingAccountAddress }
});
}
getWithdrawnCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.client.queryContractSmart(vestingContractAddress, {
get_withdrawn_coins: { vesting_account_address: vestingAccountAddress }
});
}
getStartTime(vestingContractAddress: string, vestingAccountAddress: string): Promise<string> {
return this.client.queryContractSmart(vestingContractAddress, {
get_start_time: { vesting_account_address: vestingAccountAddress }
});
}
getEndTime(vestingContractAddress: string, vestingAccountAddress: string): Promise<string> {
return this.client.queryContractSmart(vestingContractAddress, {
get_end_time: { vesting_account_address: vestingAccountAddress }
});
}
getOriginalVestingDetails(vestingContractAddress: string, vestingAccountAddress: string): Promise<OriginalVestingResponse> {
return this.client.queryContractSmart(vestingContractAddress, {
get_original_vesting: { vesting_account_address: vestingAccountAddress }
});
}
getHistoricStakingRewards(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.client.queryContractSmart(vestingContractAddress, {
get_historical_vesting_staking_reward: { vesting_account_address: vestingAccountAddress }
});
}
getAccountDetails(vestingContractAddress: string, address: string): Promise<VestingAccountInfo> {
return this.client.queryContractSmart(vestingContractAddress, {
get_account: { address: address }
});
}
getMixnode(vestingContractAddress: string, address: string): Promise<VestingAccountNode> {
return this.client.queryContractSmart(vestingContractAddress, {
get_mixnode: { address: address }
});
}
getGateway(vestingContractAddress: string, address: string): Promise<VestingAccountNode> {
return this.client.queryContractSmart(vestingContractAddress, {
get_gateway: { address: address }
});
}
getDelegationTimes(vestingContractAddress: string, mix_id: number, address: string): Promise<DelegationTimes> {
return this.client.queryContractSmart(vestingContractAddress, {
get_delegation_times: { mix_id: mix_id, address: address }
});
}
getAllDelegations(vestingContractAddress: string): Promise<Delegations> {
return this.client.queryContractSmart(vestingContractAddress, {
get_all_delegations: {}
});
}
getCurrentVestingPeriod(vestingContractAddress: string, address: string): Promise<Period> {
return this.client.queryContractSmart(vestingContractAddress, {
get_current_vesting_period: { address: address }
});
}
}
+94 -2
View File
@@ -20,11 +20,15 @@ import {
UnbondedMixnodeResponse,
MixNodeBond,
MixNodeRewarding,
OriginalVestingResponse,
VestingAccountInfo,
ContractState, VestingAccountsCoinPaged, VestingAccountsPaged, DelegationTimes, Delegations, Period, VestingAccountNode
} from '@nymproject/types';
import NymApiQuerier, { INymApiQuery } from './nym-api-querier';
import { ContractState, ICosmWasmQuery } from './types/shared';
import { ICosmWasmQuery } from './types/shared';
import { RewardingParams } from '@nymproject/types';
import { Tendermint34Client } from '@cosmjs/tendermint-rpc';
import { Coin } from 'cosmjs-types/cosmos/base/v1beta1/coin';
export interface INyxdQuery {
// nym-specific implemented inside NymQuerier
@@ -68,7 +72,7 @@ export interface INyxdQuery {
getMixnodeRewardingDetails(mixnetContractAddress: string, mixId: number): Promise<MixNodeRewarding>;
}
export interface IQueryClient extends ICosmWasmQuery, INyxdQuery, INymApiQuery {}
export interface IQueryClient extends ICosmWasmQuery, INyxdQuery, INymApiQuery { }
export default class QueryClient extends CosmWasmClient implements IQueryClient {
private nyxdQuerier: NyxdQuerier;
@@ -199,4 +203,92 @@ export default class QueryClient extends CosmWasmClient implements IQueryClient
getSpendableCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<any> {
return this.nyxdQuerier.getSpendableCoins(vestingContractAddress, vestingAccountAddress);
}
getVestingAccountsPaged(vestingContractAddress: string): Promise<VestingAccountsPaged> {
return this.nyxdQuerier.getVestingAccountsPaged(vestingContractAddress);
}
getVestingAmountsAccountsPaged(vestingContractAddress: string): Promise<VestingAccountsCoinPaged> {
return this.nyxdQuerier.getVestingAmountsAccountsPaged(vestingContractAddress);
}
getLockedTokens(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.nyxdQuerier.getLockedTokens(vestingContractAddress, vestingAccountAddress);
}
getSpendableTokens(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.nyxdQuerier.getSpendableTokens(vestingContractAddress, vestingAccountAddress);
}
getVestedTokens(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.nyxdQuerier.getVestedTokens(vestingContractAddress, vestingAccountAddress);
}
getVestingTokens(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.nyxdQuerier.getVestingTokens(vestingContractAddress, vestingAccountAddress);
}
getSpendableVestedTokens(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.nyxdQuerier.getSpendableVestedTokens(vestingContractAddress, vestingAccountAddress);
}
getSpendableRewards(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.nyxdQuerier.getSpendableRewards(vestingContractAddress, vestingAccountAddress);
}
getDelegatedCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.nyxdQuerier.getDelegatedCoins(vestingContractAddress, vestingAccountAddress);
}
getPledgedCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.nyxdQuerier.getPledgedCoins(vestingContractAddress, vestingAccountAddress);
}
getStakedCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.nyxdQuerier.getStakedCoins(vestingContractAddress, vestingAccountAddress);
}
getWithdrawnCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.nyxdQuerier.getWithdrawnCoins(vestingContractAddress, vestingAccountAddress);
}
getStartTime(vestingContractAddress: string, vestingAccountAddress: string): Promise<string> {
return this.nyxdQuerier.getStartTime(vestingContractAddress, vestingAccountAddress);
}
getEndTime(vestingContractAddress: string, vestingAccountAddress: string): Promise<string> {
return this.nyxdQuerier.getEndTime(vestingContractAddress, vestingAccountAddress);
}
getOriginalVestingDetails(vestingContractAddress: string, vestingAccountAddress: string): Promise<OriginalVestingResponse> {
return this.nyxdQuerier.getOriginalVestingDetails(vestingContractAddress, vestingAccountAddress);
}
getHistoricStakingRewards(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.nyxdQuerier.getHistoricStakingRewards(vestingContractAddress, vestingAccountAddress);
}
getAccountDetails(vestingContractAddress: string, address: string): Promise<VestingAccountInfo> {
return this.nyxdQuerier.getAccountDetails(vestingContractAddress, address);
}
getMixnode(vestingContractAddress: string, address: string): Promise<VestingAccountNode> {
return this.nyxdQuerier.getMixnode(vestingContractAddress, address);
}
getGateway(vestingContractAddress: string, address: string): Promise<VestingAccountNode> {
return this.nyxdQuerier.getGateway(vestingContractAddress, address);
}
getDelegationTimes(vestingContractAddress: string, mix_id: number, delegatorAddress: string): Promise<DelegationTimes> {
return this.nyxdQuerier.getDelegationTimes(vestingContractAddress, mix_id, delegatorAddress);
}
getAllDelegations(vestingContractAddress: string): Promise<Delegations> {
return this.nyxdQuerier.getAllDelegations(vestingContractAddress);
}
getCurrentVestingPeriod(vestingContractAddress: string, address: string): Promise<Period> {
return this.nyxdQuerier.getCurrentVestingPeriod(vestingContractAddress, address);
}
}
+94 -2
View File
@@ -29,6 +29,7 @@ import {
MixNodeDetails,
MixNodeRewarding,
MixOwnershipResponse,
OriginalVestingResponse,
PagedAllDelegationsResponse,
PagedDelegatorDelegationsResponse,
PagedGatewayResponse,
@@ -38,9 +39,10 @@ import {
PagedUnbondedMixnodesResponse,
RewardingParams,
UnbondedMixnodeResponse,
VestingAccountInfo,
ContractState, VestingAccountsCoinPaged, VestingAccountsPaged, DelegationTimes, Delegations, Period, VestingAccountNode
} from '@nymproject/types';
import NymApiQuerier from './nym-api-querier';
import { ContractState } from './types/shared';
// methods exposed by `SigningCosmWasmClient`
export interface ICosmWasmSigning {
@@ -243,7 +245,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
// query related:
getContractVersion(mixnetContractAddress: string): Promise<MixnetContractVersion> {
return this.getContractVersion(mixnetContractAddress);
return this.nyxdQuerier.getContractVersion(mixnetContractAddress);
}
getMixNodeBonds(
@@ -508,4 +510,94 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
memo,
);
}
// vesting related
getVestingAccountsPaged(vestingContractAddress: string): Promise<VestingAccountsPaged> {
return this.nyxdQuerier.getVestingAccountsPaged(vestingContractAddress);
};
getVestingAmountsAccountsPaged(vestingContractAddress: string): Promise<VestingAccountsCoinPaged> {
return this.nyxdQuerier.getVestingAmountsAccountsPaged(vestingContractAddress);
}
getLockedTokens(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.nyxdQuerier.getLockedTokens(vestingContractAddress, vestingAccountAddress);
}
getSpendableTokens(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.nyxdQuerier.getSpendableTokens(vestingContractAddress, vestingAccountAddress);
}
getVestedTokens(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.nyxdQuerier.getVestedTokens(vestingContractAddress, vestingAccountAddress);
}
getVestingTokens(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.nyxdQuerier.getVestingTokens(vestingContractAddress, vestingAccountAddress);
}
getSpendableVestedTokens(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.nyxdQuerier.getSpendableVestedTokens(vestingContractAddress, vestingAccountAddress);
}
getSpendableRewards(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.nyxdQuerier.getSpendableRewards(vestingContractAddress, vestingAccountAddress);
}
getDelegatedCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.nyxdQuerier.getDelegatedCoins(vestingContractAddress, vestingAccountAddress);
}
getPledgedCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.nyxdQuerier.getPledgedCoins(vestingContractAddress, vestingAccountAddress);
}
getStakedCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.nyxdQuerier.getStakedCoins(vestingContractAddress, vestingAccountAddress);
}
getWithdrawnCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.nyxdQuerier.getWithdrawnCoins(vestingContractAddress, vestingAccountAddress);
}
getStartTime(vestingContractAddress: string, vestingAccountAddress: string): Promise<string> {
return this.nyxdQuerier.getStartTime(vestingContractAddress, vestingAccountAddress);
}
getEndTime(vestingContractAddress: string, vestingAccountAddress: string): Promise<string> {
return this.nyxdQuerier.getEndTime(vestingContractAddress, vestingAccountAddress);
}
getOriginalVestingDetails(vestingContractAddress: string, vestingAccountAddress: string): Promise<OriginalVestingResponse> {
return this.nyxdQuerier.getOriginalVestingDetails(vestingContractAddress, vestingAccountAddress);
}
getHistoricStakingRewards(vestingContractAddress: string, vestingAccountAddress: string): Promise<Coin> {
return this.nyxdQuerier.getHistoricStakingRewards(vestingContractAddress, vestingAccountAddress);
}
getAccountDetails(vestingContractAddress: string, address: string): Promise<VestingAccountInfo> {
return this.nyxdQuerier.getAccountDetails(vestingContractAddress, address);
}
getMixnode(vestingContractAddress: string, address: string): Promise<VestingAccountNode> {
return this.nyxdQuerier.getMixnode(vestingContractAddress, address);
}
getGateway(vestingContractAddress: string, address: string): Promise<VestingAccountNode> {
return this.nyxdQuerier.getGateway(vestingContractAddress, address);
}
getDelegationTimes(vestingContractAddress: string, mix_id: number, delegatorAddress: string): Promise<DelegationTimes> {
return this.nyxdQuerier.getDelegationTimes(vestingContractAddress, mix_id, delegatorAddress);
}
getAllDelegations(vestingContractAddress: string): Promise<Delegations> {
return this.nyxdQuerier.getAllDelegations(vestingContractAddress);
}
getCurrentVestingPeriod(vestingContractAddress: string, address: string): Promise<Period> {
return this.nyxdQuerier.getCurrentVestingPeriod(vestingContractAddress, address);
}
}
@@ -1,8 +1,9 @@
import { Coin } from 'cosmjs-types/cosmos/base/v1beta1/coin';
import expect from 'expect';
export const amountDemon = {
amount: expect.any(String),
denom: expect.any(String)
denom: expect.any(String),
amount: expect.any(String)
}
export const delegation = {
@@ -159,7 +160,6 @@ export const layerDistribution = {
layer3: expect.any(Number)
}
export const intervalRewardParams = {
reward_pool: expect.any(Number),
staking_supply: expect.any(Number),
@@ -176,3 +176,79 @@ export const rewardingParams = {
rewarded_set_size: expect.any(Number),
active_set_size: expect.any(Number)
}
export const VestAccounts = [{
account_id: expect.any(String),
owner: expect.any(String)
}]
export const VestAccountCoin = [{
account_id: expect.any(String),
owner: expect.any(String),
still_vesting: Coin
}]
export const vestingAccountsPaged = {
accounts: VestAccounts,
start_next_after: expect.any(String)
}
export const VestingCoinAccounts = {
accounts: VestAccountCoin,
start_next_after: expect.any(String)
}
export const OriginalVestingDetails = {
amount: Coin,
number_of_periods: expect.any(Number),
period_duration: expect.any(Number)
}
export const PledgeCap = {
percent: expect.any(String) || null,
};
export const Periods = [{
period_seconds: expect.any(Number),
start_time: expect.any(Number),
}]
export const VestingAccountDetails = {
owner_address: expect.any(String),
staking_address: expect.any(String) || null,
start_time: expect.any(String),
periods: Periods,
coin: Coin,
storage_key: expect.any(Number),
pledge_cap: PledgeCap
}
export const Node = {
amount: Coin,
block_time: expect.any(String)
}
export type VestingPeriod = 'Before' | { In: number } | 'After';
export const DelegationTimestamps = [
expect.any(Number)
]
export const DelegatorTimes = {
owner: expect.any(String),
account_id: expect.any(Number),
mix_id: expect.any(Number),
delegation_timestamps: DelegationTimestamps
}
export const DelegationBlock = [{
account_id: expect.any(Number),
amount: expect.any(String),
block_timestamp: expect.any(Number),
mix_id: expect.any(Number)
}]
export const Delegations = {
delegations: DelegationBlock,
start_next_after: expect.any(String) || null
}
@@ -1,5 +1,6 @@
import expect from 'expect';
import ValidatorClient from '../../index';
import { amountDemon, Delegations, DelegatorTimes, Node, OriginalVestingDetails, VestingAccountDetails, vestingAccountsPaged, VestingCoinAccounts, VestingPeriod } from '../expectedResponses';
const dotenv = require('dotenv');
@@ -19,10 +20,149 @@ describe('Vesting queries', () => {
);
});
const vesting_account_address = 'n14juvj7llvx8eppypnqj6xlrgwss9wfrcuy0nkv';
const mixnodeowner = 'n1z93z44vf8ssvdhujjvxcj4rd5e3lz0l60wdk70';
const gatewayowner = 'n1un9cuvw9e3xqratmde4j55ucksev0dkeruq800';
const mix_id = 79;
it('can query for contract version', async () => {
const contract = await client.getVestingContractVersion();
expect(contract).toBeTruthy();
});
it('can get the balance on the account', () => {});
// TODO see if we can use AccountEntry type here instead
it('can get all accounts paged', async () => {
const accounts = await client.getVestingAccountsPaged();
expect(Object.keys(accounts)).toEqual(Object.keys(vestingAccountsPaged));
expect(accounts).toBeTruthy();
});
it('can get coins for all accounts paged', async () => {
const accounts = await client.getVestingAmountsAccountsPaged();
expect(Object.keys(accounts)).toEqual(Object.keys(VestingCoinAccounts));
expect(accounts).toBeTruthy();
});
it('can get locked tokens for an account', async () => {
const locked = await client.getLockedTokens(vesting_account_address);
expect(Object.keys(locked)).toEqual(Object.keys(amountDemon));
expect(locked).toBeTruthy();
});
it('can get spendable tokens for an account', async () => {
const spendable = await client.getSpendableTokens(vesting_account_address);
expect(Object.keys(spendable)).toEqual(Object.keys(amountDemon));
expect(spendable).toBeTruthy();
});
it('can get vested tokens for an account', async () => {
const vested = await client.getVestedTokens(vesting_account_address);
expect(Object.keys(vested)).toEqual(Object.keys(amountDemon));
expect(vested).toBeTruthy();
});
it('can get vesting tokens for an account', async () => {
const vesting = await client.getVestingTokens(vesting_account_address);
expect(Object.keys(vesting)).toEqual(Object.keys(amountDemon));
expect(vesting).toBeTruthy();
});
it('can get spendable vested tokens for an account', async () => {
const spendable = await client.getSpendableVestedTokens(vesting_account_address);
expect(Object.keys(spendable)).toEqual(Object.keys(amountDemon));
expect(spendable).toBeTruthy();
});
it('can get spendable rewards for an account', async () => {
const rewards = await client.getSpendableRewards(vesting_account_address);
expect(Object.keys(rewards)).toEqual(Object.keys(amountDemon));
expect(rewards).toBeTruthy();
});
it('can get delegated coins', async () => {
const delegated = await client.getDelegatedCoins(vesting_account_address);
expect(Object.keys(delegated)).toEqual(Object.keys(amountDemon));
expect(delegated).toBeTruthy();
});
it('can get pledged coins', async () => {
const pledged = await client.getPledgedCoins(vesting_account_address);
expect(Object.keys(pledged)).toEqual(Object.keys(amountDemon));
expect(pledged).toBeTruthy();
});
it('can get staked coins', async () => {
const staked = await client.getStakedCoins(vesting_account_address);
expect(Object.keys(staked)).toEqual(Object.keys(amountDemon));
expect(staked).toBeTruthy();
});
it('can get withdrawn coins', async () => {
const withdrawn = await client.getWithdrawnCoins(vesting_account_address);
expect(Object.keys(withdrawn)).toEqual(Object.keys(amountDemon));
expect(withdrawn).toBeTruthy();
});
it('can get start time of an account', async () => {
const time = await client.getStartTime(vesting_account_address);
expect(typeof time).toBe("string");
expect(time).toBeTruthy();
});
it('can get end time of an account', async () => {
const time = await client.getEndTime(vesting_account_address);
expect(typeof time).toBe("string");
expect(time).toBeTruthy();
});
it('can get account original vesting details', async () => {
const original = await client.getOriginalVestingDetails(vesting_account_address);
expect(Object.keys(original)).toEqual(Object.keys(OriginalVestingDetails));
expect(original).toBeTruthy();
});
it('can get historic vesting staking rewards', async () => {
const rewards = await client.getHistoricStakingRewards(vesting_account_address);
expect(Object.keys(rewards)).toEqual(Object.keys(amountDemon));
expect(rewards).toBeTruthy();
});
// TODO see if we can use "VestingAccountInfo" type here instead
it('can get account details', async () => {
const account = await client.getAccountDetails(vesting_account_address);
expect(Object.keys(account)).toEqual(Object.keys(VestingAccountDetails));
expect(account).toBeTruthy();
});
// TODO add option for if account has no mixnode and expected is null
it('can get mixnode', async () => {
const mixnode = await client.getMixnode(mixnodeowner);
expect(Object.keys(mixnode)).toEqual(Object.keys(Node));
expect(mixnode).toBeTruthy();
});
// TODO add option for if account has no gateway and expected is null
it.skip('can get gateway', async () => {
const gateway = await client.getGateway(gatewayowner);
expect(Object.keys(gateway)).toEqual(Object.keys(Node));
expect(gateway).toBeTruthy();
});
it('can get delegations times', async () => {
const delegation = await client.getDelegationTimes(mix_id, mixnodeowner);
expect(Object.keys(delegation)).toEqual(Object.keys(DelegatorTimes));
expect(delegation).toBeTruthy();
});
it('can get all delegations', async () => {
const delegation = await client.getAllDelegations();
expect(Object.keys(delegation)).toEqual(Object.keys(Delegations));
expect(delegation).toBeTruthy();
});
it('can get current vesting period', async () => {
const period = await client.getCurrentVestingPeriod(gatewayowner);
expect(period).toEqual(expect.anything() as unknown as VestingPeriod);
expect(period).toBeTruthy();
});
});
+89 -240
View File
@@ -3,15 +3,8 @@
// due to expansion of #[wasm_bindgen] macro on `Debug` Config struct
#![allow(clippy::drop_non_drop)]
// another issue due to #[wasm_bindgen] and `Copy` trait
#![allow(clippy::drop_copy)]
use client_core::config::{
Acknowledgements as ConfigAcknowledgements, CoverTraffic as ConfigCoverTraffic,
DebugConfig as ConfigDebug, ExtendedPacketSize, GatewayConnection as ConfigGatewayConnection,
GatewayEndpointConfig, ReplySurbs as ConfigReplySurbs, Topology as ConfigTopology,
Traffic as ConfigTraffic,
};
use client_core::config::{DebugConfig as ConfigDebug, ExtendedPacketSize, GatewayEndpointConfig};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use url::Url;
@@ -55,124 +48,15 @@ impl Config {
}
}
// just a helper structure to more easily pass through the JS boundary
#[wasm_bindgen]
#[derive(Debug, Copy, Clone)]
pub struct Traffic {
pub struct Debug {
/// The parameter of Poisson distribution determining how long, on average,
/// sent packet is going to be delayed at any given mix node.
/// So for a packet going through three mix nodes, on average, it will take three times this value
/// until the packet reaches its destination.
pub average_packet_delay_ms: u64,
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take another 'real traffic stream' message to be sent.
/// If no real packets are available and cover traffic is enabled,
/// a loop cover message is sent instead in order to preserve the rate.
pub message_sending_average_delay_ms: u64,
/// Controls whether the main packet stream constantly produces packets according to the predefined
/// poisson distribution.
pub disable_main_poisson_packet_distribution: bool,
/// Controls whether the sent sphinx packet use the NON-DEFAULT bigger size.
pub use_extended_packet_size: bool,
}
impl From<Traffic> for ConfigTraffic {
fn from(traffic: Traffic) -> Self {
let use_extended_packet_size = traffic
.use_extended_packet_size
.then(|| ExtendedPacketSize::Extended32);
ConfigTraffic {
average_packet_delay: Duration::from_millis(traffic.average_packet_delay_ms),
message_sending_average_delay: Duration::from_millis(
traffic.message_sending_average_delay_ms,
),
disable_main_poisson_packet_distribution: traffic
.disable_main_poisson_packet_distribution,
use_extended_packet_size,
}
}
}
impl From<ConfigTraffic> for Traffic {
fn from(traffic: ConfigTraffic) -> Self {
Traffic {
average_packet_delay_ms: traffic.average_packet_delay.as_millis() as u64,
message_sending_average_delay_ms: traffic.message_sending_average_delay.as_millis()
as u64,
disable_main_poisson_packet_distribution: traffic
.disable_main_poisson_packet_distribution,
use_extended_packet_size: traffic.use_extended_packet_size.is_some(),
}
}
}
#[wasm_bindgen]
#[derive(Debug, Copy, Clone)]
pub struct CoverTraffic {
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take for another loop cover traffic message to be sent.
pub loop_cover_traffic_average_delay_ms: u64,
/// Controls whether the dedicated loop cover traffic stream should be enabled.
/// (and sending packets, on average, every [Self::loop_cover_traffic_average_delay])
pub disable_loop_cover_traffic_stream: bool,
}
impl From<CoverTraffic> for ConfigCoverTraffic {
fn from(cover_traffic: CoverTraffic) -> Self {
ConfigCoverTraffic {
loop_cover_traffic_average_delay: Duration::from_millis(
cover_traffic.loop_cover_traffic_average_delay_ms,
),
disable_loop_cover_traffic_stream: cover_traffic.disable_loop_cover_traffic_stream,
}
}
}
impl From<ConfigCoverTraffic> for CoverTraffic {
fn from(cover_traffic: ConfigCoverTraffic) -> Self {
CoverTraffic {
loop_cover_traffic_average_delay_ms: cover_traffic
.loop_cover_traffic_average_delay
.as_millis() as u64,
disable_loop_cover_traffic_stream: cover_traffic.disable_loop_cover_traffic_stream,
}
}
}
#[wasm_bindgen]
#[derive(Debug, Copy, Clone)]
pub struct GatewayConnection {
/// How long we're willing to wait for a response to a message sent to the gateway,
/// before giving up on it.
pub gateway_response_timeout_ms: u64,
}
impl From<GatewayConnection> for ConfigGatewayConnection {
fn from(gateway_connection: GatewayConnection) -> Self {
ConfigGatewayConnection {
gateway_response_timeout: Duration::from_millis(
gateway_connection.gateway_response_timeout_ms,
),
}
}
}
impl From<ConfigGatewayConnection> for GatewayConnection {
fn from(gateway_connection: ConfigGatewayConnection) -> Self {
GatewayConnection {
gateway_response_timeout_ms: gateway_connection.gateway_response_timeout.as_millis()
as u64,
}
}
}
#[wasm_bindgen]
#[derive(Debug, Copy, Clone)]
pub struct Acknowledgements {
/// The parameter of Poisson distribution determining how long, on average,
/// sent acknowledgement is going to be delayed at any given mix node.
/// So for an ack going through three mix nodes, on average, it will take three times this value
@@ -188,31 +72,21 @@ pub struct Acknowledgements {
/// it is assumed it was lost and retransmission of the data packet happens.
/// In an ideal network with 0 latency, this value would have been 0.
pub ack_wait_addition_ms: u64,
}
impl From<Acknowledgements> for ConfigAcknowledgements {
fn from(acknowledgements: Acknowledgements) -> Self {
ConfigAcknowledgements {
average_ack_delay: Duration::from_millis(acknowledgements.average_ack_delay_ms),
ack_wait_multiplier: acknowledgements.ack_wait_multiplier,
ack_wait_addition: Duration::from_millis(acknowledgements.ack_wait_addition_ms),
}
}
}
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take for another loop cover traffic message to be sent.
pub loop_cover_traffic_average_delay_ms: u64,
impl From<ConfigAcknowledgements> for Acknowledgements {
fn from(acknowledgements: ConfigAcknowledgements) -> Self {
Acknowledgements {
average_ack_delay_ms: acknowledgements.average_ack_delay.as_millis() as u64,
ack_wait_multiplier: acknowledgements.ack_wait_multiplier,
ack_wait_addition_ms: acknowledgements.ack_wait_addition.as_millis() as u64,
}
}
}
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take another 'real traffic stream' message to be sent.
/// If no real packets are available and cover traffic is enabled,
/// a loop cover message is sent instead in order to preserve the rate.
pub message_sending_average_delay_ms: u64,
/// How long we're willing to wait for a response to a message sent to the gateway,
/// before giving up on it.
pub gateway_response_timeout_ms: u64,
#[wasm_bindgen]
#[derive(Debug, Copy, Clone)]
pub struct Topology {
/// The uniform delay every which clients are querying the directory server
/// to try to obtain a compatible network topology to send sphinx packets through.
pub topology_refresh_rate_ms: u64,
@@ -221,31 +95,18 @@ pub struct Topology {
/// path. This timeout determines waiting period until it is decided that the packet
/// did not reach its destination.
pub topology_resolution_timeout_ms: u64,
}
impl From<Topology> for ConfigTopology {
fn from(topology: Topology) -> Self {
ConfigTopology {
topology_refresh_rate: Duration::from_millis(topology.topology_refresh_rate_ms),
topology_resolution_timeout: Duration::from_millis(
topology.topology_resolution_timeout_ms,
),
}
}
}
/// Controls whether the dedicated loop cover traffic stream should be enabled.
/// (and sending packets, on average, every [Self::loop_cover_traffic_average_delay_ms])
pub disable_loop_cover_traffic_stream: bool,
impl From<ConfigTopology> for Topology {
fn from(topology: ConfigTopology) -> Self {
Topology {
topology_refresh_rate_ms: topology.topology_refresh_rate.as_millis() as u64,
topology_resolution_timeout_ms: topology.topology_resolution_timeout.as_millis() as u64,
}
}
}
/// Controls whether the main packet stream constantly produces packets according to the predefined
/// poisson distribution.
pub disable_main_poisson_packet_distribution: bool,
/// Controls whether the sent sphinx packet use the NON-DEFAULT bigger size.
pub use_extended_packet_size: bool,
#[wasm_bindgen]
#[derive(Debug, Copy, Clone)]
pub struct ReplySurbs {
/// Defines the minimum number of reply surbs the client wants to keep in its storage at all times.
/// It can only allow to go below that value if its to request additional reply surbs.
pub minimum_reply_surb_storage_threshold: usize,
@@ -279,80 +140,46 @@ pub struct ReplySurbs {
pub maximum_reply_key_age_ms: u64,
}
impl From<ReplySurbs> for ConfigReplySurbs {
fn from(reply_surbs: ReplySurbs) -> Self {
ConfigReplySurbs {
minimum_reply_surb_storage_threshold: reply_surbs.minimum_reply_surb_storage_threshold,
maximum_reply_surb_storage_threshold: reply_surbs.maximum_reply_surb_storage_threshold,
minimum_reply_surb_request_size: reply_surbs.minimum_reply_surb_request_size,
maximum_reply_surb_request_size: reply_surbs.maximum_reply_surb_request_size,
maximum_allowed_reply_surb_request_size: reply_surbs
.maximum_allowed_reply_surb_request_size,
maximum_reply_surb_rerequest_waiting_period: Duration::from_millis(
reply_surbs.maximum_reply_surb_rerequest_waiting_period_ms,
),
maximum_reply_surb_drop_waiting_period: Duration::from_millis(
reply_surbs.maximum_reply_surb_drop_waiting_period_ms,
),
maximum_reply_surb_age: Duration::from_millis(reply_surbs.maximum_reply_surb_age_ms),
maximum_reply_key_age: Duration::from_millis(reply_surbs.maximum_reply_key_age_ms),
}
}
}
impl From<ConfigReplySurbs> for ReplySurbs {
fn from(reply_surbs: ConfigReplySurbs) -> Self {
ReplySurbs {
minimum_reply_surb_storage_threshold: reply_surbs.minimum_reply_surb_storage_threshold,
maximum_reply_surb_storage_threshold: reply_surbs.maximum_reply_surb_storage_threshold,
minimum_reply_surb_request_size: reply_surbs.minimum_reply_surb_request_size,
maximum_reply_surb_request_size: reply_surbs.maximum_reply_surb_request_size,
maximum_allowed_reply_surb_request_size: reply_surbs
.maximum_allowed_reply_surb_request_size,
maximum_reply_surb_rerequest_waiting_period_ms: reply_surbs
.maximum_reply_surb_rerequest_waiting_period
.as_millis() as u64,
maximum_reply_surb_drop_waiting_period_ms: reply_surbs
.maximum_reply_surb_drop_waiting_period
.as_millis() as u64,
maximum_reply_surb_age_ms: reply_surbs.maximum_reply_surb_age.as_millis() as u64,
maximum_reply_key_age_ms: reply_surbs.maximum_reply_key_age.as_millis() as u64,
}
}
}
// just a helper structure to more easily pass through the JS boundary
#[wasm_bindgen]
#[derive(Debug, Copy, Clone)]
pub struct Debug {
/// Defines all configuration options related to traffic streams.
pub traffic: Traffic,
/// Defines all configuration options related to cover traffic stream(s).
pub cover_traffic: CoverTraffic,
/// Defines all configuration options related to the gateway connection.
pub gateway_connection: GatewayConnection,
/// Defines all configuration options related to acknowledgements, such as delays or wait timeouts.
pub acknowledgements: Acknowledgements,
/// Defines all configuration options related topology, such as refresh rates or timeouts.
pub topology: Topology,
/// Defines all configuration options related to reply SURBs.
pub reply_surbs: ReplySurbs,
}
impl From<Debug> for ConfigDebug {
fn from(debug: Debug) -> Self {
// For now we just always use the (older) 32kb extended size
let use_extended_packet_size = debug
.use_extended_packet_size
.then(|| ExtendedPacketSize::Extended32);
ConfigDebug {
traffic: debug.traffic.into(),
cover_traffic: debug.cover_traffic.into(),
gateway_connection: debug.gateway_connection.into(),
acknowledgements: debug.acknowledgements.into(),
topology: debug.topology.into(),
reply_surbs: debug.reply_surbs.into(),
average_packet_delay: Duration::from_millis(debug.average_packet_delay_ms),
average_ack_delay: Duration::from_millis(debug.average_ack_delay_ms),
ack_wait_multiplier: debug.ack_wait_multiplier,
ack_wait_addition: Duration::from_millis(debug.ack_wait_addition_ms),
loop_cover_traffic_average_delay: Duration::from_millis(
debug.loop_cover_traffic_average_delay_ms,
),
message_sending_average_delay: Duration::from_millis(
debug.message_sending_average_delay_ms,
),
gateway_response_timeout: Duration::from_millis(debug.gateway_response_timeout_ms),
topology_refresh_rate: Duration::from_millis(debug.topology_refresh_rate_ms),
topology_resolution_timeout: Duration::from_millis(
debug.topology_resolution_timeout_ms,
),
disable_loop_cover_traffic_stream: debug.disable_loop_cover_traffic_stream,
disable_main_poisson_packet_distribution: debug
.disable_main_poisson_packet_distribution,
use_extended_packet_size,
minimum_reply_surb_storage_threshold: debug.minimum_reply_surb_storage_threshold,
maximum_reply_surb_storage_threshold: debug.maximum_reply_surb_storage_threshold,
minimum_reply_surb_request_size: debug.minimum_reply_surb_request_size,
maximum_reply_surb_request_size: debug.maximum_reply_surb_request_size,
maximum_allowed_reply_surb_request_size: debug.maximum_allowed_reply_surb_request_size,
maximum_reply_surb_rerequest_waiting_period: Duration::from_millis(
debug.maximum_reply_surb_rerequest_waiting_period_ms,
),
maximum_reply_surb_drop_waiting_period: Duration::from_millis(
debug.maximum_reply_surb_drop_waiting_period_ms,
),
maximum_reply_surb_age: Duration::from_millis(debug.maximum_reply_surb_age_ms),
maximum_reply_key_age: Duration::from_millis(debug.maximum_reply_key_age_ms),
}
}
}
@@ -360,12 +187,34 @@ impl From<Debug> for ConfigDebug {
impl From<ConfigDebug> for Debug {
fn from(debug: ConfigDebug) -> Self {
Debug {
traffic: debug.traffic.into(),
cover_traffic: debug.cover_traffic.into(),
gateway_connection: debug.gateway_connection.into(),
acknowledgements: debug.acknowledgements.into(),
topology: debug.topology.into(),
reply_surbs: debug.reply_surbs.into(),
average_packet_delay_ms: debug.average_packet_delay.as_millis() as u64,
average_ack_delay_ms: debug.average_ack_delay.as_millis() as u64,
ack_wait_multiplier: debug.ack_wait_multiplier,
ack_wait_addition_ms: debug.ack_wait_addition.as_millis() as u64,
loop_cover_traffic_average_delay_ms: debug.loop_cover_traffic_average_delay.as_millis()
as u64,
message_sending_average_delay_ms: debug.message_sending_average_delay.as_millis()
as u64,
gateway_response_timeout_ms: debug.gateway_response_timeout.as_millis() as u64,
topology_refresh_rate_ms: debug.topology_refresh_rate.as_millis() as u64,
topology_resolution_timeout_ms: debug.topology_resolution_timeout.as_millis() as u64,
disable_loop_cover_traffic_stream: debug.disable_loop_cover_traffic_stream,
disable_main_poisson_packet_distribution: debug
.disable_main_poisson_packet_distribution,
use_extended_packet_size: debug.use_extended_packet_size.is_some(),
minimum_reply_surb_storage_threshold: debug.minimum_reply_surb_storage_threshold,
maximum_reply_surb_storage_threshold: debug.maximum_reply_surb_storage_threshold,
minimum_reply_surb_request_size: debug.minimum_reply_surb_request_size,
maximum_reply_surb_request_size: debug.maximum_reply_surb_request_size,
maximum_allowed_reply_surb_request_size: debug.maximum_allowed_reply_surb_request_size,
maximum_reply_surb_rerequest_waiting_period_ms: debug
.maximum_reply_surb_rerequest_waiting_period
.as_millis() as u64,
maximum_reply_surb_drop_waiting_period_ms: debug
.maximum_reply_surb_drop_waiting_period
.as_millis() as u64,
maximum_reply_surb_age_ms: debug.maximum_reply_surb_age.as_millis() as u64,
maximum_reply_key_age_ms: debug.maximum_reply_key_age.as_millis() as u64,
}
}
}
+2 -8
View File
@@ -82,14 +82,8 @@ impl NymClientBuilder {
// with no persistence
fn setup_reply_surb_storage_backend(config: &Config) -> browser_backend::Backend {
browser_backend::Backend::new(
config
.debug
.reply_surbs
.minimum_reply_surb_storage_threshold,
config
.debug
.reply_surbs
.maximum_reply_surb_storage_threshold,
config.debug.minimum_reply_surb_storage_threshold,
config.debug.maximum_reply_surb_storage_threshold,
)
}
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-bin-common"
version = "0.4.0"
version = "0.3.0"
description = "Common code for nym binaries"
edition = { workspace = true }
authors = { workspace = true }
@@ -1,4 +1,4 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::nyxd::coin::Coin;
@@ -9,7 +9,6 @@ use crate::nyxd::{Fee, NyxdClient, SigningCosmWasmClient};
use async_trait::async_trait;
use cosmrs::AccountId;
use nym_contracts_common::signing::MessageSignature;
use nym_mixnet_contract_common::families::FamilyHead;
use nym_mixnet_contract_common::gateway::GatewayConfigUpdate;
use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
use nym_mixnet_contract_common::reward_params::{IntervalRewardingParamsUpdate, Performance};
@@ -147,16 +146,25 @@ pub trait MixnetSigningClient {
// family related
async fn create_family(
&self,
owner_signature: String,
label: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_mixnet_contract(fee, MixnetExecuteMsg::CreateFamily { label }, vec![])
.await
self.execute_mixnet_contract(
fee,
MixnetExecuteMsg::CreateFamily {
owner_signature,
label,
},
vec![],
)
.await
}
async fn create_family_on_behalf(
&self,
owner_address: String,
owner_signature: String,
label: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
@@ -164,6 +172,7 @@ pub trait MixnetSigningClient {
fee,
MixnetExecuteMsg::CreateFamilyOnBehalf {
owner_address,
owner_signature,
label,
},
vec![],
@@ -173,14 +182,14 @@ pub trait MixnetSigningClient {
async fn join_family(
&self,
join_permit: MessageSignature,
family_head: FamilyHead,
signature: String,
family_head: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_mixnet_contract(
fee,
MixnetExecuteMsg::JoinFamily {
join_permit,
signature,
family_head,
},
vec![],
@@ -191,15 +200,17 @@ pub trait MixnetSigningClient {
async fn join_family_on_behalf(
&self,
member_address: String,
join_permit: MessageSignature,
family_head: FamilyHead,
node_identity_signature: String,
family_signature: String,
family_head: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_mixnet_contract(
fee,
MixnetExecuteMsg::JoinFamilyOnBehalf {
member_address,
join_permit,
node_identity_signature,
family_signature,
family_head,
},
vec![],
@@ -209,23 +220,33 @@ pub trait MixnetSigningClient {
async fn leave_family(
&self,
family_head: FamilyHead,
signature: String,
family_head: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_mixnet_contract(fee, MixnetExecuteMsg::LeaveFamily { family_head }, vec![])
.await
self.execute_mixnet_contract(
fee,
MixnetExecuteMsg::LeaveFamily {
signature,
family_head,
},
vec![],
)
.await
}
async fn leave_family_on_behalf(
&self,
member_address: String,
family_head: FamilyHead,
node_identity_signature: String,
family_head: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_mixnet_contract(
fee,
MixnetExecuteMsg::LeaveFamilyOnBehalf {
member_address,
node_identity_signature,
family_head,
},
vec![],
@@ -235,16 +256,22 @@ pub trait MixnetSigningClient {
async fn kick_family_member(
&self,
signature: String,
member: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_mixnet_contract(fee, MixnetExecuteMsg::KickFamilyMember { member }, vec![])
.await
self.execute_mixnet_contract(
fee,
MixnetExecuteMsg::KickFamilyMember { signature, member },
vec![],
)
.await
}
async fn kick_family_member_on_behalf(
&self,
head_address: String,
signature: String,
member: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
@@ -252,6 +279,7 @@ pub trait MixnetSigningClient {
fee,
MixnetExecuteMsg::KickFamilyMemberOnBehalf {
head_address,
signature,
member,
},
vec![],
@@ -7,7 +7,6 @@ use crate::nyxd::error::NyxdError;
use crate::nyxd::{Coin, Fee, NyxdClient};
use async_trait::async_trait;
use nym_contracts_common::signing::MessageSignature;
use nym_mixnet_contract_common::families::FamilyHead;
use nym_mixnet_contract_common::gateway::GatewayConfigUpdate;
use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
use nym_mixnet_contract_common::{Gateway, MixId, MixNode};
@@ -137,50 +136,6 @@ pub trait VestingSigningClient {
cap: Option<PledgeCap>,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError>;
async fn vesting_create_family(
&self,
label: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_vesting_contract(fee, VestingExecuteMsg::CreateFamily { label }, vec![])
.await
}
async fn vesting_join_family(
&self,
join_permit: MessageSignature,
family_head: FamilyHead,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_vesting_contract(
fee,
VestingExecuteMsg::JoinFamily {
join_permit,
family_head,
},
vec![],
)
.await
}
async fn vesting_leave_family(
&self,
family_head: FamilyHead,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_vesting_contract(fee, VestingExecuteMsg::LeaveFamily { family_head }, vec![])
.await
}
async fn vesting_kick_family_member(
&self,
member: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_vesting_contract(fee, VestingExecuteMsg::KickFamilyMember { member }, vec![])
.await
}
}
#[async_trait]
-1
View File
@@ -29,7 +29,6 @@ cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegr
cosmwasm-std = { workspace = true }
validator-client = { path = "../client-libs/validator-client", features = ["nyxd-client"] }
nym-crypto = { path = "../../common/crypto", features = ["asymmetric"] }
nym-network-defaults = { path = "../network-defaults" }
nym-contracts-common = { path = "../cosmwasm-smart-contracts/contracts-common" }
nym-mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" }
@@ -1,37 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
use validator_client::nyxd::traits::MixnetSigningClient;
use validator_client::nyxd::VestingSigningClient;
#[derive(Debug, Parser)]
pub struct Args {
/// Label that is going to be used for creating the family
#[arg(long)]
pub family_label: String,
/// Indicates whether the family is going to get created via a vesting account
#[arg(long)]
pub with_vesting_account: bool,
}
pub async fn create_family(args: Args, client: SigningClient) {
info!("Create family");
let res = if args.with_vesting_account {
client
.vesting_create_family(args.family_label, None)
.await
.expect("failed to create family with vesting account")
} else {
client
.create_family(args.family_label, None)
.await
.expect("failed to create family")
};
info!("Family creation result: {:?}", res);
}
@@ -1,72 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::QueryClient;
use crate::utils::account_id_to_cw_addr;
use clap::Parser;
use cosmrs::AccountId;
use log::info;
use nym_crypto::asymmetric::identity;
use nym_mixnet_contract_common::construct_family_join_permit;
use nym_mixnet_contract_common::families::FamilyHead;
use validator_client::nyxd::traits::MixnetQueryClient;
#[derive(Debug, Parser)]
pub struct Args {
/// Account address (i.e. owner of the family head) which will be used for issuing the permit
#[arg(long)]
pub address: AccountId,
/// Indicates whether the member joining the family is going to use the vesting account for joining.
#[arg(long)]
pub with_vesting_account: bool,
// might as well validate the value when parsing the arguments
/// Identity of the member for whom we're issuing the permit
#[arg(long)]
pub member: identity::PublicKey,
}
pub async fn create_family_join_permit_sign_payload(args: Args, client: QueryClient) {
info!("Create family join permit sign payload");
// get the address of our mixnode to recover the family head information
let Some(mixnode) = client.get_owned_mixnode(&args.address).await.unwrap().mixnode_details else {
eprintln!("{} does not seem to even own a mixnode!", args.address);
return
};
// make sure this mixnode is actually a family head
if client
.get_node_family_by_head(mixnode.bond_information.identity())
.await
.unwrap()
.is_none()
{
eprintln!("{} does not even seem to own a family!", args.address);
return;
}
let nonce = match client.get_signing_nonce(&args.address).await {
Ok(nonce) => nonce,
Err(err) => {
eprint!(
"failed to query for the signing nonce of {}: {err}",
args.address
);
return;
}
};
// let address = account_id_to_cw_addr(&args.address);
let proxy = if args.with_vesting_account {
Some(account_id_to_cw_addr(client.vesting_contract_address()))
} else {
None
};
let head = FamilyHead::new(mixnode.bond_information.identity());
let payload = construct_family_join_permit(nonce, head, proxy, args.member.to_base58_string());
println!("{}", payload.to_base58_string().unwrap())
}
@@ -1,46 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
use nym_contracts_common::signing::MessageSignature;
use nym_crypto::asymmetric::identity;
use nym_mixnet_contract_common::families::FamilyHead;
use validator_client::nyxd::traits::MixnetSigningClient;
use validator_client::nyxd::VestingSigningClient;
#[derive(Debug, Parser)]
pub struct Args {
/// The head of the family that we intend to join
#[arg(long)]
pub family_head: identity::PublicKey,
/// Indicates whether the member joining the family is going to do so via the vesting contract
#[arg(long)]
pub with_vesting_account: bool,
/// Permission, as provided by the family head, for joining the family
#[arg(long)]
pub join_permit: MessageSignature,
}
pub async fn join_family(args: Args, client: SigningClient) {
info!("Join family");
let family_head = FamilyHead::new(&args.family_head.to_base58_string());
let res = if args.with_vesting_account {
client
.vesting_join_family(args.join_permit, family_head, None)
.await
.expect("failed to join family with vesting account")
} else {
client
.join_family(args.join_permit, family_head, None)
.await
.expect("failed to join family")
};
info!("Family join result: {:?}", res);
}
@@ -1,40 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
use nym_crypto::asymmetric::identity;
use validator_client::nyxd::traits::MixnetSigningClient;
use validator_client::nyxd::VestingSigningClient;
#[derive(Debug, Parser)]
pub struct Args {
/// The member of the family that we intend to kick
#[arg(long)]
pub member: identity::PublicKey,
/// Indicates whether the family was created (and managed) via the vesting contract
#[arg(long)]
pub with_vesting_account: bool,
}
pub async fn kick_family_member(args: Args, client: SigningClient) {
info!("Leave family");
let member = args.member.to_base58_string();
let res = if args.with_vesting_account {
client
.vesting_kick_family_member(member, None)
.await
.expect("failed to kick family member with vesting account")
} else {
client
.kick_family_member(member, None)
.await
.expect("failed to kick family member")
};
info!("Family leave result: {:?}", res);
}
@@ -1,41 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
use nym_crypto::asymmetric::identity;
use nym_mixnet_contract_common::families::FamilyHead;
use validator_client::nyxd::traits::MixnetSigningClient;
use validator_client::nyxd::VestingSigningClient;
#[derive(Debug, Parser)]
pub struct Args {
/// The head of the family that we intend to leave
#[arg(long)]
pub family_head: identity::PublicKey,
/// Indicates whether we joined the family via the vesting contract
#[arg(long)]
pub with_vesting_account: bool,
}
pub async fn leave_family(args: Args, client: SigningClient) {
info!("Leave family");
let family_head = FamilyHead::new(&args.family_head.to_base58_string());
let res = if args.with_vesting_account {
client
.vesting_leave_family(family_head, None)
.await
.expect("failed to leave family with vesting account")
} else {
client
.leave_family(family_head, None)
.await
.expect("failed to leave family")
};
info!("Family leave result: {:?}", res);
}
@@ -1,35 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod create_family;
pub mod create_family_join_permit_sign_payload;
pub mod join_family;
pub mod kick_family_member;
pub mod leave_family;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct MixnetOperatorsMixnodeFamilies {
#[clap(subcommand)]
pub command: MixnetOperatorsMixnodeFamiliesCommands,
}
#[derive(Debug, Subcommand)]
pub enum MixnetOperatorsMixnodeFamiliesCommands {
/// Create family
CreateFamily(create_family::Args),
/// Join family
JoinFamily(join_family::Args),
/// Leave family,
LeaveFamily(leave_family::Args),
/// Kick family member
KickFamilyMember(kick_family_member::Args),
/// Create a message payload that is required to get signed in order to obtain a permit for joining family
CreateFamilyJoinPermitSignPayload(create_family_join_permit_sign_payload::Args),
}
@@ -4,7 +4,6 @@
use clap::{Args, Subcommand};
pub mod bond_mixnode;
pub mod families;
pub mod keys;
pub mod mixnode_bonding_sign_payload;
pub mod rewards;
@@ -28,8 +27,6 @@ pub enum MixnetOperatorsMixnodeCommands {
Rewards(rewards::MixnetOperatorsMixnodeRewards),
/// Manage your mixnode settings stored in the directory
Settings(settings::MixnetOperatorsMixnodeSettings),
/// Operations for mixnode families
Families(families::MixnetOperatorsMixnodeFamilies),
/// Bond to a mixnode
Bond(bond_mixnode::Args),
/// Unbond from a mixnode
@@ -1,6 +1,6 @@
[package]
name = "nym-contracts-common"
version = "0.4.0"
version = "0.3.0"
description = "Common library for Nym cosmwasm contracts"
edition = { workspace = true }
authors = { workspace = true }
@@ -1,6 +1,6 @@
[package]
name = "nym-mixnet-contract-common"
version = "0.4.0"
version = "0.3.0"
description = "Common library for the Nym mixnet contract"
rust-version = "1.62"
edition = { workspace = true }
@@ -15,7 +15,7 @@ serde = { version = "1.0", features = ["derive"] }
serde_repr = "0.1"
schemars = "0.8"
thiserror = "1.0"
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.4.0" }
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.3.0" }
# use 0.4.1 as that's the version used by cosmwasm-std 1.0.0
# (and ideally we don't want to pull the same dependency twice)
serde-json-wasm = "=0.4.1"
@@ -1,9 +1,7 @@
use crate::{IdentityKey, IdentityKeyRef};
use cosmwasm_std::Addr;
use schemars::JsonSchema;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fmt::{Display, Formatter};
use std::str::FromStr;
use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
#[cfg_attr(
@@ -22,45 +20,9 @@ pub struct Family {
feature = "generate-ts",
ts(export_to = "ts-packages/types/src/types/rust/NodeFamilyHead.ts")
)]
#[derive(Debug, Clone, Eq, PartialEq, JsonSchema)]
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq, JsonSchema)]
pub struct FamilyHead(IdentityKey);
impl Serialize for FamilyHead {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.0.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for FamilyHead {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let inner = IdentityKey::deserialize(deserializer)?;
Ok(FamilyHead(inner))
}
}
impl FromStr for FamilyHead {
type Err = <IdentityKey as FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
// theoretically we should be verifying whether it's a valid base58 value
// (or even better, whether it's a valid ed25519 public key), but definition of
// `FamilyHead` might change later
Ok(FamilyHead(IdentityKey::from_str(s)?))
}
}
impl Display for FamilyHead {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl FamilyHead {
pub fn new(identity: IdentityKeyRef<'_>) -> Self {
FamilyHead(identity.to_string())
@@ -72,11 +34,11 @@ impl FamilyHead {
}
impl Family {
pub fn new(head: FamilyHead, proxy: Option<Addr>, label: String) -> Self {
pub fn new(head: FamilyHead, proxy: Option<Addr>, label: &str) -> Self {
Family {
head,
proxy: proxy.map(|p| p.to_string()),
label,
label: label.to_string(),
}
}
@@ -98,21 +60,3 @@ impl Family {
&self.label
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn family_head_serde() {
let dummy = FamilyHead::new("foomp");
let ser_str = serde_json_wasm::to_string(&dummy).unwrap();
let de_str: FamilyHead = serde_json_wasm::from_str(&ser_str).unwrap();
assert_eq!(dummy, de_str);
let ser_bytes = serde_json_wasm::to_vec(&dummy).unwrap();
let de_bytes: FamilyHead = serde_json_wasm::from_slice(&ser_bytes).unwrap();
assert_eq!(dummy, de_bytes);
}
}
@@ -3,7 +3,6 @@
use crate::delegation::OwnerProxySubKey;
use crate::error::MixnetContractError;
use crate::families::FamilyHead;
use crate::gateway::GatewayConfigUpdate;
use crate::helpers::IntoBaseDecimal;
use crate::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
@@ -83,35 +82,42 @@ pub enum ExecuteMsg {
// Families
/// Only owner of the node can crate the family with node as head
CreateFamily {
owner_signature: String,
label: String,
},
/// Family head needs to sign the joining node IdentityKey
JoinFamily {
join_permit: MessageSignature,
family_head: FamilyHead,
signature: String,
family_head: IdentityKey,
},
LeaveFamily {
family_head: FamilyHead,
signature: String,
family_head: IdentityKey,
},
KickFamilyMember {
signature: String,
member: IdentityKey,
},
CreateFamilyOnBehalf {
owner_address: String,
owner_signature: String,
label: String,
},
/// Family head needs to sign the joining node IdentityKey, MixNode needs to provide its signature proving that it wants to join the family
JoinFamilyOnBehalf {
member_address: String,
join_permit: MessageSignature,
family_head: FamilyHead,
node_identity_signature: String,
family_signature: String,
family_head: IdentityKey,
},
LeaveFamilyOnBehalf {
member_address: String,
family_head: FamilyHead,
node_identity_signature: String,
family_head: IdentityKey,
},
KickFamilyMemberOnBehalf {
head_address: String,
signature: String,
member: IdentityKey,
},
@@ -1,8 +1,7 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::families::FamilyHead;
use crate::{Gateway, IdentityKey, MixNode, MixNodeCostParams};
use crate::{Gateway, MixNode, MixNodeCostParams};
use contracts_common::signing::{
ContractMessageContent, MessageType, Nonce, SignableMessage, SigningPurpose,
};
@@ -11,7 +10,6 @@ use serde::Serialize;
pub type SignableMixNodeBondingMsg = SignableMessage<ContractMessageContent<MixnodeBondingPayload>>;
pub type SignableGatewayBondingMsg = SignableMessage<ContractMessageContent<GatewayBondingPayload>>;
pub type SignableFamilyJoinPermitMsg = SignableMessage<FamilyJoinPermit>;
#[derive(Serialize)]
pub struct MixnodeBondingPayload {
@@ -79,43 +77,75 @@ pub fn construct_gateway_bonding_sign_payload(
}
#[derive(Serialize)]
pub struct FamilyJoinPermit {
// the granter of this permit
family_head: FamilyHead,
// whether the **member** will want to join via the proxy (i.e. vesting contract)
proxy: Option<Addr>,
// the actual member we want to permit to join
member_node: IdentityKey,
pub struct FamilyCreationSignature {
label: String,
// TODO: add any extra fields?
}
impl FamilyJoinPermit {
pub fn new(family_head: FamilyHead, proxy: Option<Addr>, member_node: IdentityKey) -> Self {
Self {
family_head,
proxy,
member_node,
}
impl FamilyCreationSignature {
pub fn new(label: String) -> Self {
Self { label }
}
}
impl SigningPurpose for FamilyJoinPermit {
impl SigningPurpose for FamilyCreationSignature {
fn message_type() -> MessageType {
MessageType::new("family-join-permit")
MessageType::new("family-creation")
}
}
pub fn construct_family_join_permit(
nonce: Nonce,
family_head: FamilyHead,
proxy: Option<Addr>,
member_node: IdentityKey,
) -> SignableFamilyJoinPermitMsg {
let payload = FamilyJoinPermit::new(family_head, proxy, member_node);
#[derive(Serialize)]
pub struct FamilyJoinSignature {
family_head: String,
// TODO: add any extra fields?
}
// note: we're NOT wrapping it in `ContractMessageContent` because the family head is not going to be the one
// sending the message to the contract
SignableMessage::new(nonce, payload)
impl FamilyJoinSignature {
pub fn new(family_head: String) -> Self {
Self { family_head }
}
}
impl SigningPurpose for FamilyJoinSignature {
fn message_type() -> MessageType {
MessageType::new("family-join")
}
}
#[derive(Serialize)]
pub struct FamilyLeaveSignature {
family_head: String,
// TODO: add any extra fields?
}
impl FamilyLeaveSignature {
pub fn new(family_head: String) -> Self {
Self { family_head }
}
}
impl SigningPurpose for FamilyLeaveSignature {
fn message_type() -> MessageType {
MessageType::new("family-leave")
}
}
#[derive(Serialize)]
pub struct FamilyKickSignature {
member: String,
// TODO: add any extra fields?
}
impl FamilyKickSignature {
pub fn new(member: String) -> Self {
Self { member }
}
}
impl SigningPurpose for FamilyKickSignature {
fn message_type() -> MessageType {
MessageType::new("family-member-removal")
}
}
// TODO: depending on our threat model, we should perhaps extend it to include all _on_behalf methods
// (update: but we trust our vesting contract since its compromise would be even more devastating so there's no need)
@@ -1,6 +1,6 @@
[package]
name = "nym-vesting-contract-common"
version = "0.4.0"
version = "0.3.0"
description = "Common library for the Nym vesting contract"
edition = { workspace = true }
authors = { workspace = true }
@@ -9,8 +9,8 @@ repository = { workspace = true }
[dependencies]
cosmwasm-std = { workspace = true }
mixnet-contract-common = { path = "../mixnet-contract", package = "nym-mixnet-contract-common", version = "0.4.0" }
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.4.0" }
mixnet-contract-common = { path = "../mixnet-contract", package = "nym-mixnet-contract-common", version = "0.3.0" }
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.3.0" }
serde = { version = "1.0", features = ["derive"] }
schemars = "0.8"
ts-rs = {version = "6.1.2", optional = true}
@@ -1,6 +1,5 @@
use contracts_common::signing::MessageSignature;
use cosmwasm_std::{Coin, Timestamp};
use mixnet_contract_common::families::FamilyHead;
use mixnet_contract_common::{
gateway::GatewayConfigUpdate,
mixnode::{MixNodeConfigUpdate, MixNodeCostParams},
@@ -61,17 +60,21 @@ pub enum ExecuteMsg {
// Families
/// Only owner of the node can crate the family with node as head
CreateFamily {
owner_signature: String,
label: String,
},
/// Family head needs to sign the joining node IdentityKey, the Node provides its signature signaling consent to join the family
JoinFamily {
join_permit: MessageSignature,
family_head: FamilyHead,
node_identity_signature: String,
family_signature: String,
family_head: IdentityKey,
},
LeaveFamily {
family_head: FamilyHead,
node_identity_signature: String,
family_head: IdentityKey,
},
KickFamilyMember {
signature: String,
member: IdentityKey,
},
TrackReward {
+2 -2
View File
@@ -48,12 +48,12 @@ impl CoconutCredentialManager {
/// Tries to retrieve one of the stored, unused credentials.
pub(crate) async fn get_next_coconut_credential(
&self,
) -> Result<CoconutCredential, sqlx::Error> {
) -> Result<Option<CoconutCredential>, sqlx::Error> {
sqlx::query_as!(
CoconutCredential,
"SELECT * FROM coconut_credentials WHERE NOT consumed"
)
.fetch_one(&self.connection_pool)
.fetch_optional(&self.connection_pool)
.await
}
+3
View File
@@ -13,4 +13,7 @@ pub enum StorageError {
#[error("Inconsistent data in database")]
InconsistentData,
#[error("No unused credential in database. You need to buy at least one")]
NoCredential,
}
+2 -1
View File
@@ -90,7 +90,8 @@ impl Storage for PersistentStorage {
let credential = self
.coconut_credential_manager
.get_next_coconut_credential()
.await?;
.await?
.ok_or(StorageError::NoCredential)?;
Ok(credential)
}
+10 -14
View File
@@ -99,24 +99,17 @@ impl ConnectionHandler {
let mut framed_conn = Framed::new(conn, EchoPacketCodec);
while !shutdown_listener.is_shutdown() {
tokio::select! {
biased;
_ = shutdown_listener.recv() => {
trace!("ConnectionHandler: Shutdown received");
}
maybe_echo_packet = framed_conn.next() => {
Some(echo_packet) = framed_conn.next() => {
// handle echo packet
let reply_packet = match maybe_echo_packet {
Some(Ok(echo_packet)) => self.handle_echo_packet(echo_packet),
Some(Err(err)) => {
error!(
"The socket connection got corrupted with error: {err}. Closing the socket",
let reply_packet = match echo_packet {
Ok(echo_packet) => self.handle_echo_packet(echo_packet),
Err(err) => {
error!(
"The socket connection got corrupted with error: {}. Closing the socket",
err
);
return;
}
None => {
error!("The socket connection got terminated by the remote!");
return;
}
};
// write back the reply (note the lack of framing)
@@ -132,6 +125,9 @@ impl ConnectionHandler {
return;
}
},
_ = shutdown_listener.recv() => {
trace!("ConnectionHandler: Shutdown received");
}
}
}
}
+5 -5
View File
@@ -100,8 +100,8 @@ impl MessageReceiver for OutfoxMessageReceiver {
Self::default()
}
fn reconstructor(&mut self) -> &mut MessageReconstructor {
&mut self.reconstructor
fn reconstructor(&self) -> MessageReconstructor {
self.reconstructor.clone()
}
fn num_mix_hops(&self) -> u8 {
@@ -123,7 +123,7 @@ impl MessageReceiver for OutfoxMessageReceiver {
pub trait MessageReceiver {
fn new() -> Self;
fn reconstructor(&mut self) -> &mut MessageReconstructor;
fn reconstructor(&self) -> MessageReconstructor;
fn num_mix_hops(&self) -> u8;
fn decrypt_raw_message<C>(
@@ -239,8 +239,8 @@ impl MessageReceiver for SphinxMessageReceiver {
Ok(())
}
fn reconstructor(&mut self) -> &mut MessageReconstructor {
&mut self.reconstructor
fn reconstructor(&self) -> MessageReconstructor {
self.reconstructor.clone()
}
fn num_mix_hops(&self) -> u8 {
-9
View File
@@ -2,15 +2,6 @@
## Unreleased
## [v1.1.5] (2023-04-04)
- change in-contract signatures to include nonces and to sign entire payloads for family-related operations ([#3125])
- change in-contract signatures to include nonces and to sign entire payloads for node bonding (will require wallet changes) ([#3067])
- removed migration code from mixnet and vesting contracts ([#3207])
[#3125]: https://github.com/nymtech/nym/issues/3125
[#3067]: https://github.com/nymtech/nym/issues/3067
[#3207]: https://github.com/nymtech/nym/pull/3207
## [v1.1.4] (2023-02-21)
- Problem 142 (rust-side) ([#3024])
+5 -5
View File
@@ -1018,7 +1018,7 @@ dependencies = [
[[package]]
name = "nym-contracts-common"
version = "0.4.0"
version = "0.3.0"
dependencies = [
"bs58",
"cosmwasm-std",
@@ -1052,7 +1052,7 @@ dependencies = [
[[package]]
name = "nym-mixnet-contract"
version = "1.3.0"
version = "1.2.0"
dependencies = [
"bs58",
"cosmwasm-derive",
@@ -1076,7 +1076,7 @@ dependencies = [
[[package]]
name = "nym-mixnet-contract-common"
version = "0.4.0"
version = "0.3.0"
dependencies = [
"bs58",
"cosmwasm-std",
@@ -1121,7 +1121,7 @@ dependencies = [
[[package]]
name = "nym-vesting-contract"
version = "1.3.0"
version = "1.2.0"
dependencies = [
"base64 0.21.0",
"cosmwasm-crypto",
@@ -1144,7 +1144,7 @@ dependencies = [
[[package]]
name = "nym-vesting-contract-common"
version = "0.4.0"
version = "0.3.0"
dependencies = [
"cosmwasm-std",
"nym-contracts-common",
+4 -4
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-mixnet-contract"
version = "1.3.0"
version = "1.2.0"
description = "Nym mixnet contract"
edition = { workspace = true }
authors = { workspace = true }
@@ -22,9 +22,9 @@ name = "mixnet_contract"
crate-type = ["cdylib", "rlib"]
[dependencies]
mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract", package = "nym-mixnet-contract-common", version = "0.4.0" }
vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract", package = "nym-vesting-contract-common", version = "0.4.0" }
nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common", version = "0.4.0" }
mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract", package = "nym-mixnet-contract-common", version = "0.3.0" }
vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract", package = "nym-vesting-contract-common", version = "0.3.0" }
nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common", version = "0.3.0" }
cosmwasm-std = { workspace = true }
cosmwasm-storage = { workspace = true }
+1 -1
View File
@@ -1,5 +1,5 @@
opt: wasm
wasm-opt --disable-sign-ext -Os ../target/wasm32-unknown-unknown/release/mixnet_contract.wasm -o ../target/wasm32-unknown-unknown/release/mixnet_contract.wasm
wasm-opt -Os ../target/wasm32-unknown-unknown/release/mixnet_contract.wasm -o ../target/wasm32-unknown-unknown/release/mixnet_contract.wasm
wasm:
RUSTFLAGS='-C link-arg=-s' cargo build --release --target wasm32-unknown-unknown
+41 -16
View File
@@ -8,6 +8,7 @@ use crate::mixnodes::storage as mixnode_storage;
use crate::rewards::storage as rewards_storage;
use cosmwasm_std::{
entry_point, to_binary, Addr, Coin, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response,
StdError,
};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::{
@@ -104,56 +105,68 @@ pub fn execute(
crate::mixnodes::transactions::assign_mixnode_layer(deps, info, mix_id, layer)
}
// families
ExecuteMsg::CreateFamily { label } => {
crate::families::transactions::try_create_family(deps, info, label)
}
ExecuteMsg::CreateFamily {
owner_signature,
label,
} => crate::families::transactions::try_create_family(deps, info, owner_signature, &label),
ExecuteMsg::JoinFamily {
join_permit,
signature,
family_head,
} => crate::families::transactions::try_join_family(deps, info, join_permit, family_head),
ExecuteMsg::LeaveFamily { family_head } => {
crate::families::transactions::try_leave_family(deps, info, family_head)
} => {
crate::families::transactions::try_join_family(deps, info, None, signature, family_head)
}
ExecuteMsg::KickFamilyMember { member } => {
crate::families::transactions::try_head_kick_member(deps, info, member)
ExecuteMsg::LeaveFamily {
signature,
family_head,
} => crate::families::transactions::try_leave_family(deps, info, signature, family_head),
ExecuteMsg::KickFamilyMember { signature, member } => {
crate::families::transactions::try_head_kick_member(deps, info, signature, &member)
}
ExecuteMsg::CreateFamilyOnBehalf {
owner_address,
owner_signature,
label,
} => crate::families::transactions::try_create_family_on_behalf(
deps,
info,
owner_address,
label,
owner_signature,
&label,
),
ExecuteMsg::JoinFamilyOnBehalf {
member_address,
join_permit,
node_identity_signature,
family_signature,
family_head,
} => crate::families::transactions::try_join_family_on_behalf(
deps,
info,
member_address,
join_permit,
Some(node_identity_signature),
family_signature,
family_head,
),
ExecuteMsg::LeaveFamilyOnBehalf {
member_address,
node_identity_signature,
family_head,
} => crate::families::transactions::try_leave_family_on_behalf(
deps,
info,
member_address,
node_identity_signature,
family_head,
),
ExecuteMsg::KickFamilyMemberOnBehalf {
head_address,
signature,
member,
} => crate::families::transactions::try_head_kick_member_on_behalf(
deps,
info,
head_address,
member,
signature,
&member,
),
// state/sys-params-related
ExecuteMsg::UpdateRewardingValidatorAddress { address } => {
@@ -380,13 +393,13 @@ pub fn query(
&crate::families::queries::get_family_by_head(&head, deps.storage)?,
),
QueryMsg::GetFamilyByLabel { label } => to_binary(
&crate::families::queries::get_family_by_label(label, deps.storage)?,
&crate::families::queries::get_family_by_label(&label, deps.storage)?,
),
QueryMsg::GetFamilyMembersByHead { head } => to_binary(
&crate::families::queries::get_family_members_by_head(&head, deps.storage)?,
),
QueryMsg::GetFamilyMembersByLabel { label } => to_binary(
&crate::families::queries::get_family_members_by_label(label, deps.storage)?,
&crate::families::queries::get_family_members_by_label(&label, deps.storage)?,
),
QueryMsg::GetContractVersion {} => {
to_binary(&crate::mixnet_contract_settings::queries::query_contract_version())
@@ -590,6 +603,17 @@ pub fn migrate(
_env: Env,
msg: MigrateMsg,
) -> Result<Response, MixnetContractError> {
// this is the first migration that uses cw2 standard and thus the value in the storage doesn't yet exist
// set it instead.
if matches!(
cw2::get_contract_version(deps.storage),
Err(StdError::NotFound { .. })
) {
cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
crate::queued_migrations::create_epoch_status(deps.storage)?;
return Ok(Response::new());
}
// note: don't remove this particular bit of code as we have to ALWAYS check whether we have to update the stored version
let version: Version = CONTRACT_VERSION.parse().map_err(|error: semver::Error| {
MixnetContractError::SemVerFailure {
@@ -611,7 +635,8 @@ pub fn migrate(
cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
// If state structure changed in any contract version in the way migration is needed, it
// should occur here, for example anything from `crate::queued_migrations::`
// should occur here
crate::queued_migrations::create_epoch_status(deps.storage)?;
}
// due to circular dependency on contract addresses (i.e. mixnet contract requiring vesting contract address
-4
View File
@@ -1,7 +1,3 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod queries;
pub mod signature_helpers;
pub mod storage;
pub mod transactions;
+19 -10
View File
@@ -1,27 +1,31 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::collections::HashSet;
use crate::constants::{FAMILIES_DEFAULT_RETRIEVAL_LIMIT, FAMILIES_MAX_RETRIEVAL_LIMIT};
use super::storage::{families, get_family, get_members, MEMBERS};
use crate::constants::{FAMILIES_DEFAULT_RETRIEVAL_LIMIT, FAMILIES_MAX_RETRIEVAL_LIMIT};
use cosmwasm_std::{Order, Storage};
use cw_storage_plus::Bound;
use mixnet_contract_common::families::{Family, FamilyHead};
use mixnet_contract_common::{error::MixnetContractError, IdentityKeyRef};
use mixnet_contract_common::{IdentityKey, PagedFamiliesResponse, PagedMembersResponse};
use std::collections::HashSet;
pub fn get_family_by_label(
label: String,
label: &str,
storage: &dyn Storage,
) -> Result<Option<Family>, MixnetContractError> {
Ok(families().idx.label.item(storage, label)?.map(|o| o.1))
Ok(families()
.idx
.label
.item(storage, label.to_string())?
.map(|o| o.1))
}
pub fn get_family_by_head(
head: IdentityKeyRef<'_>,
storage: &dyn Storage,
) -> Result<Option<Family>, MixnetContractError> {
Ok(families().may_load(storage, head.to_string())?)
) -> Result<Family, MixnetContractError> {
let family_head = FamilyHead::new(head);
get_family(&family_head, storage)
}
pub fn get_family_members_by_head(
@@ -34,10 +38,15 @@ pub fn get_family_members_by_head(
}
pub fn get_family_members_by_label(
label: String,
label: &str,
storage: &dyn Storage,
) -> Result<Option<HashSet<String>>, MixnetContractError> {
if let Some(family) = families().idx.label.item(storage, label)?.map(|o| o.1) {
if let Some(family) = families()
.idx
.label
.item(storage, label.to_string())?
.map(|o| o.1)
{
Ok(Some(get_members(&family, storage)?))
} else {
Ok(None)
@@ -1,38 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::mixnodes::storage as mixnodes_storage;
use crate::signing::storage as signing_storage;
use crate::support::helpers::decode_ed25519_identity_key;
use cosmwasm_std::{Addr, Deps};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::families::FamilyHead;
use mixnet_contract_common::{construct_family_join_permit, IdentityKeyRef};
use nym_contracts_common::signing::{MessageSignature, Verifier};
pub(crate) fn verify_family_join_permit(
deps: Deps<'_>,
granter: FamilyHead,
proxy: Option<Addr>,
member: IdentityKeyRef,
signature: MessageSignature,
) -> Result<(), MixnetContractError> {
// recover the public key
let public_key = decode_ed25519_identity_key(granter.identity())?;
// that's kinda a backwards way of getting the granter's nonce, but it works, so ¯\_(ツ)_/¯
let Some(head_mixnode) = mixnodes_storage::mixnode_bonds()
.idx
.identity_key
.item(deps.storage, granter.identity().to_owned())?.map(|record| record.1) else {
return Err(MixnetContractError::FamilyDoesNotExist { head: granter.identity().to_string() })
};
let nonce = signing_storage::get_signing_nonce(deps.storage, head_mixnode.owner)?;
let msg = construct_family_join_permit(nonce, granter, proxy, member.to_owned());
if deps.api.verify_message(msg, signature, &public_key)? {
Ok(())
} else {
Err(MixnetContractError::InvalidEd25519Signature)
}
}
+18 -6
View File
@@ -1,11 +1,9 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::collections::HashSet;
use cosmwasm_std::{Order, Storage};
use cosmwasm_std::{Order, StdError, Storage};
use cw_storage_plus::{Index, IndexList, IndexedMap, Map, UniqueIndex};
use mixnet_contract_common::families::{Family, FamilyHead};
use mixnet_contract_common::{error::MixnetContractError, IdentityKey, IdentityKeyRef};
use std::collections::HashSet;
use crate::constants::{FAMILIES_INDEX_NAMESPACE, FAMILIES_MAP_NAMESPACE, MEMBERS_MAP_NAMESPACE};
@@ -53,8 +51,22 @@ pub fn get_family(head: &FamilyHead, store: &dyn Storage) -> Result<Family, Mixn
}
}
pub fn save_family(f: &Family, store: &mut dyn Storage) -> Result<(), MixnetContractError> {
Ok(families().save(store, f.head_identity().to_string(), f)?)
pub fn create_family(f: &Family, store: &mut dyn Storage) -> Result<(), MixnetContractError> {
match families().save(store, f.head_identity().to_string(), f) {
Ok(()) => Ok(()),
Err(e) => match &e {
StdError::GenericErr { msg } => {
if msg.starts_with("Violates unique constraint") {
Err(MixnetContractError::FamilyWithLabelExists(
f.label().to_string(),
))
} else {
Err(e.into())
}
}
_ => Err(e.into()),
},
}
}
pub fn add_family_member(
+229 -136
View File
@@ -1,99 +1,136 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::storage::{
add_family_member, get_family, is_any_member, is_family_member, remove_family_member,
save_family,
use crate::support::helpers::{
ensure_bonded, ensure_sent_by_vesting_contract, validate_family_signature,
validate_node_identity_signature,
};
use crate::families::queries::get_family_by_label;
use crate::families::signature_helpers::verify_family_join_permit;
use crate::support::helpers::{ensure_bonded, ensure_sent_by_vesting_contract};
use cosmwasm_std::{Addr, DepsMut, MessageInfo, Response};
use mixnet_contract_common::families::{Family, FamilyHead};
use mixnet_contract_common::{error::MixnetContractError, IdentityKey};
use nym_contracts_common::signing::MessageSignature;
use mixnet_contract_common::{error::MixnetContractError, IdentityKey, IdentityKeyRef};
use super::storage::{
add_family_member, create_family, get_family, is_any_member, is_family_member,
remove_family_member,
};
/// Creates a new MixNode family with senders node as head
pub fn try_create_family(
deps: DepsMut,
info: MessageInfo,
label: String,
owner_signature: String,
label: &str,
) -> Result<Response, MixnetContractError> {
_try_create_family(deps, &info.sender, label, None)
_try_create_family(deps, &info.sender, owner_signature, label, None)
}
pub fn try_create_family_on_behalf(
deps: DepsMut,
info: MessageInfo,
owner_address: String,
label: String,
owner_signature: String,
label: &str,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let owner_address = deps.api.addr_validate(&owner_address)?;
_try_create_family(deps, &owner_address, label, Some(info.sender))
_try_create_family(
deps,
&owner_address,
owner_signature,
label,
Some(info.sender),
)
}
fn _try_create_family(
deps: DepsMut,
owner: &Addr,
label: String,
owner_signature: String,
label: &str,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
let existing_bond =
crate::mixnodes::helpers::must_get_mixnode_bond_by_owner(deps.storage, owner)?;
let existing_bond = crate::mixnodes::storage::mixnode_bonds()
.idx
.owner
.item(deps.storage, owner.clone())?
.ok_or(MixnetContractError::NoAssociatedMixNodeBond {
owner: owner.clone(),
})?
.1;
ensure_bonded(&existing_bond)?;
validate_node_identity_signature(
deps.as_ref(),
owner,
&owner_signature,
existing_bond.identity(),
)?;
let family_head = FamilyHead::new(existing_bond.identity());
// can't overwrite existing family
if get_family(&family_head, deps.storage).is_ok() {
if let Ok(_family) = get_family(&family_head, deps.storage) {
return Err(MixnetContractError::FamilyCanHaveOnlyOne);
}
// the label must be unique
if get_family_by_label(label.clone(), deps.storage)?.is_some() {
return Err(MixnetContractError::FamilyWithLabelExists(label));
}
let family = Family::new(family_head, proxy, label);
save_family(&family, deps.storage)?;
create_family(&family, deps.storage)?;
Ok(Response::default())
}
pub fn try_join_family(
deps: DepsMut,
info: MessageInfo,
join_permit: MessageSignature,
family_head: FamilyHead,
// Required for proxy joining
node_identity_signature: Option<String>,
family_signature: String,
family_head: IdentityKey,
) -> Result<Response, MixnetContractError> {
_try_join_family(deps, &info.sender, join_permit, family_head, None)
let family_head = FamilyHead::new(&family_head);
_try_join_family(
deps,
&info.sender,
node_identity_signature,
family_signature,
family_head,
)
}
pub fn try_join_family_on_behalf(
deps: DepsMut,
info: MessageInfo,
member_address: String,
join_permit: MessageSignature,
family_head: FamilyHead,
node_identity_signature: Option<String>,
family_signature: String,
family_head: IdentityKey,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let member_address = deps.api.addr_validate(&member_address)?;
let proxy = Some(info.sender);
_try_join_family(deps, &member_address, join_permit, family_head, proxy)
let family_head = FamilyHead::new(&family_head);
_try_join_family(
deps,
&member_address,
node_identity_signature,
family_signature,
family_head,
)
}
fn _try_join_family(
deps: DepsMut,
owner: &Addr,
join_permit: MessageSignature,
node_identity_signature: Option<String>,
family_signature: String,
family_head: FamilyHead,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
let existing_bond =
crate::mixnodes::helpers::must_get_mixnode_bond_by_owner(deps.storage, owner)?;
let existing_bond = crate::mixnodes::storage::mixnode_bonds()
.idx
.owner
.item(deps.storage, owner.clone())?
.ok_or(MixnetContractError::NoAssociatedMixNodeBond {
owner: owner.clone(),
})?
.1;
ensure_bonded(&existing_bond)?;
@@ -110,12 +147,20 @@ fn _try_join_family(
));
}
verify_family_join_permit(
if let Some(node_identity_signature) = node_identity_signature {
validate_node_identity_signature(
deps.as_ref(),
owner,
&node_identity_signature,
existing_bond.identity(),
)?;
}
validate_family_signature(
deps.as_ref(),
family_head.clone(),
proxy,
existing_bond.identity(),
join_permit,
&family_signature,
family_head.identity(),
)?;
let family = get_family(&family_head, deps.storage)?;
@@ -128,30 +173,41 @@ fn _try_join_family(
pub fn try_leave_family(
deps: DepsMut,
info: MessageInfo,
family_head: FamilyHead,
signature: String,
family_head: IdentityKey,
) -> Result<Response, MixnetContractError> {
_try_leave_family(deps, &info.sender, family_head)
let family_head = FamilyHead::new(&family_head);
_try_leave_family(deps, &info.sender, signature, family_head)
}
pub fn try_leave_family_on_behalf(
deps: DepsMut,
info: MessageInfo,
member_address: String,
family_head: FamilyHead,
node_family_signature: String,
family_head: IdentityKey,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let family_head = FamilyHead::new(&family_head);
let member_address = deps.api.addr_validate(&member_address)?;
_try_leave_family(deps, &member_address, family_head)
_try_leave_family(deps, &member_address, node_family_signature, family_head)
}
fn _try_leave_family(
deps: DepsMut,
owner: &Addr,
node_family_signature: String,
family_head: FamilyHead,
) -> Result<Response, MixnetContractError> {
let existing_bond =
crate::mixnodes::helpers::must_get_mixnode_bond_by_owner(deps.storage, owner)?;
let existing_bond = crate::mixnodes::storage::mixnode_bonds()
.idx
.owner
.item(deps.storage, owner.clone())?
.ok_or(MixnetContractError::NoAssociatedMixNodeBond {
owner: owner.clone(),
})?
.1;
ensure_bonded(&existing_bond)?;
@@ -170,6 +226,13 @@ fn _try_leave_family(
});
}
validate_node_identity_signature(
deps.as_ref(),
owner,
&node_family_signature,
existing_bond.identity(),
)?;
remove_family_member(deps.storage, existing_bond.identity());
Ok(Response::default())
@@ -178,56 +241,62 @@ fn _try_leave_family(
pub fn try_head_kick_member(
deps: DepsMut,
info: MessageInfo,
member: IdentityKey,
owner_signature: String,
member: IdentityKeyRef,
) -> Result<Response, MixnetContractError> {
_try_head_kick_member(deps, &info.sender, member)
_try_head_kick_member(deps, &info.sender, owner_signature, member)
}
pub fn try_head_kick_member_on_behalf(
deps: DepsMut,
info: MessageInfo,
head_address: String,
member: IdentityKey,
owner_signature: String,
member: IdentityKeyRef,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let head_address = deps.api.addr_validate(&head_address)?;
_try_head_kick_member(deps, &head_address, member)
_try_head_kick_member(deps, &head_address, owner_signature, member)
}
#[allow(unused_variables)]
fn _try_head_kick_member(
deps: DepsMut,
owner: &Addr,
member: IdentityKey,
owner_signature: String,
member: IdentityKeyRef<'_>,
) -> Result<Response, MixnetContractError> {
let head_bond = crate::mixnodes::helpers::must_get_mixnode_bond_by_owner(deps.storage, owner)?;
Err(MixnetContractError::NotImplemented)
// let existing_bond = crate::mixnodes::storage::mixnode_bonds()
// .idx
// .owner
// .item(deps.storage, owner.clone())?
// .ok_or(MixnetContractError::NoAssociatedMixNodeBond {
// owner: owner.clone(),
// })?
// .1;
// make sure we're still in the mixnet
ensure_bonded(&head_bond)?;
// ensure_bonded(&existing_bond)?;
// make sure we're not trying to kick ourselves...
if member == head_bond.identity() {
return Err(MixnetContractError::CantLeaveOwnFamily {
head: head_bond.identity().to_string(),
member,
});
}
// validate_node_identity_signature(
// deps.as_ref(),
// owner,
// &owner_signature,
// existing_bond.identity(),
// )?;
// get the family details
let family_head = FamilyHead::new(head_bond.identity());
let family = get_family(&family_head, deps.storage)?;
// let family_head = FamilyHead::new(existing_bond.identity());
// let family = get_family(&family_head, deps.storage)?;
// if !is_family_member(deps.storage, &family, member)? {
// return Err(MixnetContractError::NotAMember {
// head: family_head.identity().to_string(),
// member: existing_bond.identity().to_string(),
// });
// }
// make sure the member we're trying to kick is an actual member
if !is_family_member(deps.storage, &family, &member)? {
return Err(MixnetContractError::NotAMember {
head: family_head.identity().to_string(),
member,
});
}
// finally get rid of the member
remove_family_member(deps.storage, &member);
Ok(Response::default())
// remove_family_member(deps.storage, member);
// Ok(Response::default())
}
#[cfg(test)]
@@ -253,9 +322,9 @@ mod test {
let cost_params = fixtures::mix_node_cost_params_fixture();
let (head_mixnode, head_bond_sig, head_keypair) = test.mixnode_with_signature(head, None);
let (malicious_mixnode, malicious_bond_sig, _malicious_keypair) =
let (malicious_mixnode, malicious_bond_sig, malicious_keypair) =
test.mixnode_with_signature(malicious_head, None);
let (member_mixnode, member_bond_sig, _member_keypair) =
let (member_mixnode, member_bond_sig, member_keypair) =
test.mixnode_with_signature(member, None);
crate::mixnodes::transactions::try_add_mixnode(
@@ -288,14 +357,26 @@ mod test {
)
.unwrap();
try_create_family(test.deps_mut(), mock_info(head, &[]), "test".to_string()).unwrap();
let old_style_head_sig = head_keypair.private_key().sign_text(head);
let old_style_malicious_head_sig =
malicious_keypair.private_key().sign_text(malicious_head);
let old_style_member_sig = member_keypair.private_key().sign_text(member);
try_create_family(
test.deps_mut(),
mock_info(head, &[]),
old_style_head_sig,
"test",
)
.unwrap();
let family_head = FamilyHead::new(&head_mixnode.identity_key);
assert!(get_family(&family_head, test.deps().storage).is_ok());
let nope = try_create_family(
test.deps_mut(),
mock_info(malicious_head, &[]),
"test".to_string(),
old_style_malicious_head_sig,
"test",
);
match nope {
@@ -306,23 +387,24 @@ mod test {
},
}
let family = get_family_by_label("test".to_string(), test.deps().storage).unwrap();
let family = get_family_by_label("test", test.deps().storage).unwrap();
assert!(family.is_some());
assert_eq!(family.unwrap().head_identity(), family_head.identity());
let family = get_family_by_head(family_head.identity(), test.deps().storage)
.unwrap()
.unwrap();
let family = get_family_by_head(family_head.identity(), test.deps().storage).unwrap();
assert_eq!(family.head_identity(), family_head.identity());
let join_permit =
test.generate_family_join_permit(&head_keypair, &member_mixnode.identity_key, false);
let join_signature = head_keypair
.private_key()
.sign(member_mixnode.identity_key.as_bytes())
.to_base58_string();
try_join_family(
test.deps_mut(),
mock_info(member, &[]),
join_permit,
family_head.clone(),
Some(old_style_member_sig.clone()),
join_signature.clone(),
head_mixnode.identity_key.clone(),
)
.unwrap();
@@ -332,21 +414,25 @@ mod test {
is_family_member(test.deps().storage, &family, &member_mixnode.identity_key).unwrap()
);
try_leave_family(test.deps_mut(), mock_info(member, &[]), family_head.clone()).unwrap();
try_leave_family(
test.deps_mut(),
mock_info(member, &[]),
old_style_member_sig.clone(),
head_mixnode.identity_key.clone(),
)
.unwrap();
let family = get_family(&family_head, test.deps().storage).unwrap();
assert!(
!is_family_member(test.deps().storage, &family, &member_mixnode.identity_key).unwrap()
);
let new_join_permit =
test.generate_family_join_permit(&head_keypair, &member_mixnode.identity_key, false);
try_join_family(
test.deps_mut(),
mock_info(member, &[]),
new_join_permit,
family_head.clone(),
Some(old_style_member_sig),
join_signature,
head_mixnode.identity_key,
)
.unwrap();
@@ -356,17 +442,16 @@ mod test {
is_family_member(test.deps().storage, &family, &member_mixnode.identity_key).unwrap()
);
try_head_kick_member(
test.deps_mut(),
mock_info(head, &[]),
member_mixnode.identity_key.clone(),
)
.unwrap();
// try_head_kick_member(
// deps.as_mut(),
// mock_info(&head, &[]),
// head_sig.clone(),
// &member_mixnode.identity_key.clone(),
// )
// .unwrap();
let family = get_family(&family_head, test.deps().storage).unwrap();
assert!(
!is_family_member(test.deps().storage, &family, &member_mixnode.identity_key).unwrap()
);
// let family = get_family(&family_head, test.deps().storage).unwrap();
// assert!(!is_family_member(test.deps().storage, &family, &member_mixnode.identity_key).unwrap());
}
#[cfg(test)]
@@ -383,13 +468,15 @@ mod test {
let head = "alice";
test.add_dummy_mixnode(head, None);
let (_, keypair) = test.add_dummy_mixnode_with_proxy_and_keypair(head, None);
let sig = keypair.private_key().sign_text(head);
let res = try_create_family_on_behalf(
test.deps_mut(),
mock_info(illegal_proxy.as_ref(), &[]),
head.to_string(),
"label".to_string(),
sig,
"label",
)
.unwrap_err();
@@ -420,22 +507,23 @@ mod test {
let new_member = "vin-diesel";
let (_, head_keys) = test.create_dummy_mixnode_with_new_family(head, label);
let (_, member_keys) = test.add_dummy_mixnode_with_keypair(new_member, None);
let (_, member_keys) = test.add_dummy_mixnode_with_proxy_and_keypair(new_member, None);
let join_permit = test.generate_family_join_permit(
&head_keys,
&member_keys.public_key().to_base58_string(),
false,
);
// TODO: those signatures are WRONG and have to be c hanged
let join_signature = head_keys
.private_key()
.sign_text(&member_keys.public_key().to_base58_string());
let member_sig = member_keys.private_key().sign_text(new_member);
let head_identity = head_keys.public_key().to_base58_string();
let family_head = FamilyHead::new(&head_identity);
let res = try_join_family_on_behalf(
test.deps_mut(),
mock_info(illegal_proxy.as_ref(), &[]),
new_member.to_string(),
join_permit,
family_head,
Some(member_sig),
join_signature,
head_identity,
)
.unwrap_err();
@@ -466,30 +554,33 @@ mod test {
let new_member = "vin-diesel";
let (_, head_keys) = test.create_dummy_mixnode_with_new_family(head, label);
let (_, member_keys) = test.add_dummy_mixnode_with_keypair(new_member, None);
let (_, member_keys) = test.add_dummy_mixnode_with_proxy_and_keypair(new_member, None);
let join_permit = test.generate_family_join_permit(
&head_keys,
&member_keys.public_key().to_base58_string(),
true,
);
// TODO: those signatures are WRONG and have to be changed
let join_signature = head_keys
.private_key()
.sign_text(&member_keys.public_key().to_base58_string());
let member_sig = member_keys.private_key().sign_text(new_member);
let head_identity = head_keys.public_key().to_base58_string();
let family_head = FamilyHead::new(&head_identity);
try_join_family_on_behalf(
test.deps_mut(),
mock_info(vesting_contract.as_ref(), &[]),
new_member.to_string(),
join_permit,
family_head.clone(),
Some(member_sig.clone()),
join_signature,
head_identity,
)
.unwrap();
let leave_signature = member_sig;
let res = try_leave_family_on_behalf(
test.deps_mut(),
mock_info(illegal_proxy.as_ref(), &[]),
new_member.to_string(),
family_head.clone(),
leave_signature,
head_keys.public_key().to_base58_string(),
)
.unwrap_err();
@@ -520,31 +611,33 @@ mod test {
let new_member = "vin-diesel";
let (_, head_keys) = test.create_dummy_mixnode_with_new_family(head, label);
let (_, member_keys) = test.add_dummy_mixnode_with_keypair(new_member, None);
let (_, member_keys) = test.add_dummy_mixnode_with_proxy_and_keypair(new_member, None);
let join_permit = test.generate_family_join_permit(
&head_keys,
&member_keys.public_key().to_base58_string(),
true,
);
// TODO: those signatures are WRONG and have to be c hanged
let join_signature = head_keys
.private_key()
.sign_text(&member_keys.public_key().to_base58_string());
let member_sig = member_keys.private_key().sign_text(new_member);
let head_identity = head_keys.public_key().to_base58_string();
let family_head = FamilyHead::new(&head_identity);
try_join_family_on_behalf(
test.deps_mut(),
mock_info(vesting_contract.as_ref(), &[]),
new_member.to_string(),
join_permit,
family_head,
Some(member_sig),
join_signature,
head_identity,
)
.unwrap();
// TODO: a completely wrong signature is being used
let res = try_head_kick_member_on_behalf(
test.deps_mut(),
mock_info(illegal_proxy.as_ref(), &[]),
head.to_string(),
member_keys.public_key().to_base58_string(),
head_keys.private_key().sign_text(head),
&member_keys.public_key().to_base58_string(),
)
.unwrap_err();
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
use crate::signing::storage as signing_storage;
use crate::support::helpers::decode_ed25519_identity_key;
use cosmwasm_std::{Addr, Coin, Deps};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::{construct_gateway_bonding_sign_payload, Gateway};
@@ -18,7 +17,10 @@ pub(crate) fn verify_gateway_bonding_signature(
signature: MessageSignature,
) -> Result<(), MixnetContractError> {
// recover the public key
let public_key = decode_ed25519_identity_key(&gateway.identity_key)?;
let mut public_key = [0u8; 32];
bs58::decode(&gateway.identity_key)
.into(&mut public_key)
.map_err(|err| MixnetContractError::MalformedEd25519IdentityKey(err.to_string()))?;
// reconstruct the payload
let nonce = signing_storage::get_signing_nonce(deps.storage, sender.clone())?;
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
use crate::signing::storage as signing_storage;
use crate::support::helpers::decode_ed25519_identity_key;
use cosmwasm_std::{Addr, Coin, Deps};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::{construct_mixnode_bonding_sign_payload, MixNode, MixNodeCostParams};
@@ -19,7 +18,10 @@ pub(crate) fn verify_mixnode_bonding_signature(
signature: MessageSignature,
) -> Result<(), MixnetContractError> {
// recover the public key
let public_key = decode_ed25519_identity_key(&mixnode.identity_key)?;
let mut public_key = [0u8; 32];
bs58::decode(&mixnode.identity_key)
.into(&mut public_key)
.map_err(|err| MixnetContractError::MalformedEd25519IdentityKey(err.to_string()))?;
// reconstruct the payload
let nonce = signing_storage::get_signing_nonce(deps.storage, sender.clone())?;
@@ -245,7 +245,12 @@ pub(crate) fn _try_remove_mixnode(
owner: Addr,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
let existing_bond = must_get_mixnode_bond_by_owner(deps.storage, &owner)?;
let existing_bond = storage::mixnode_bonds()
.idx
.owner
.item(deps.storage, owner.clone())?
.ok_or(MixnetContractError::NoAssociatedMixNodeBond { owner })?
.1;
// unbonding is only allowed if the epoch is currently not in the process of being advanced
ensure_epoch_in_progress_state(deps.storage)?;
+17 -1
View File
@@ -1,2 +1,18 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::interval::storage as interval_storage;
use cosmwasm_std::Storage;
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::EpochStatus;
pub(crate) fn create_epoch_status(storage: &mut dyn Storage) -> Result<(), MixnetContractError> {
let current_rewarding_validator =
crate::mixnet_contract_settings::storage::rewarding_validator_address(storage)?;
interval_storage::save_current_epoch_status(
storage,
&EpochStatus::new(current_rewarding_validator),
)?;
Ok(())
}
+177 -10
View File
@@ -4,7 +4,9 @@
use crate::gateways::storage as gateways_storage;
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use crate::mixnodes::storage as mixnodes_storage;
use cosmwasm_std::{wasm_execute, Addr, BankMsg, Coin, CosmosMsg, MessageInfo, Response, Storage};
use cosmwasm_std::{
wasm_execute, Addr, BankMsg, Coin, CosmosMsg, Deps, MessageInfo, Response, Storage,
};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::{EpochState, EpochStatus, IdentityKeyRef, MixId, MixNodeBond};
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
@@ -369,19 +371,184 @@ pub(crate) fn ensure_no_existing_bond(
Ok(())
}
pub(crate) fn decode_ed25519_identity_key(
encoded: IdentityKeyRef,
) -> Result<[u8; 32], MixnetContractError> {
let mut public_key = [0u8; 32];
let used = bs58::decode(encoded)
.into(&mut public_key)
.map_err(|err| MixnetContractError::MalformedEd25519IdentityKey(err.to_string()))?;
pub fn validate_node_identity_signature(
deps: Deps<'_>,
owner: &Addr,
signature: &str,
identity: IdentityKeyRef<'_>,
) -> Result<(), MixnetContractError> {
validate_ed25519_signature(deps, owner.as_bytes(), signature, identity)
}
if used != 32 {
pub fn validate_family_signature(
deps: Deps<'_>,
family_member: IdentityKeyRef<'_>,
signature: &str,
family_head: IdentityKeyRef<'_>,
) -> Result<(), MixnetContractError> {
validate_ed25519_signature(deps, family_member.as_bytes(), signature, family_head)
}
pub(crate) fn validate_ed25519_signature(
deps: Deps<'_>,
signed_bytes: &[u8],
signature: &str,
identity: IdentityKeyRef<'_>,
) -> Result<(), MixnetContractError> {
let mut identity_bytes = [0u8; 32];
let mut signature_bytes = [0u8; 64];
let identity_used_bytes = bs58::decode(identity)
.into(&mut identity_bytes)
.map_err(|err| MixnetContractError::MalformedEd25519IdentityKey(err.to_string()))?;
let signature_used_bytes = bs58::decode(signature)
.into(&mut signature_bytes)
.map_err(|err| MixnetContractError::MalformedEd25519Signature(err.to_string()))?;
if identity_used_bytes != 32 {
return Err(MixnetContractError::MalformedEd25519IdentityKey(
"Too few bytes provided for the public key".into(),
));
}
Ok(public_key)
if signature_used_bytes != 64 {
return Err(MixnetContractError::MalformedEd25519Signature(
"Too few bytes provided for the signature".into(),
));
}
let res = deps
.api
.ed25519_verify(signed_bytes, &signature_bytes, &identity_bytes)
.map_err(cosmwasm_std::StdError::verification_err)?;
if !res {
Err(MixnetContractError::InvalidEd25519Signature)
} else {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use cosmwasm_std::testing::mock_dependencies;
use nym_crypto::asymmetric::identity;
use rand_chacha::rand_core::SeedableRng;
#[test]
fn validating_node_signature() {
let deps = mock_dependencies();
// since those tests are NOT compiled to wasm, we can use rng-related dependency
let dummy_seed = [1u8; 32];
let mut rng = rand_chacha::ChaCha20Rng::from_seed(dummy_seed);
let short_bs58 = "2SfEgZ4aQUr3HSwqE";
let long_bs58 = "g34PyULki9fc3FqKobj5wdVNCaWAt1M9oZowyyMFfWSCejxg7wt574piZVjqjFEN2UXsgZ56KTkKf3jnWD4DJ2Gsf7KXQAvptFfcYRrZHTjMVo3NXcBSNm3wDBKZWZURzp4Fixv";
let address1 = Addr::unchecked("some-dummy-address1");
let address2 = Addr::unchecked("some-dummy-address2");
let keypair1 = identity::KeyPair::new(&mut rng);
let keypair2 = identity::KeyPair::new(&mut rng);
let sig_addr1_key1 = keypair1
.private_key()
.sign(address1.as_bytes())
.to_base58_string();
let sig_addr2_key1 = keypair1
.private_key()
.sign(address2.as_bytes())
.to_base58_string();
let sig_addr1_key2 = keypair2
.private_key()
.sign(address1.as_bytes())
.to_base58_string();
assert_eq!(
Err(MixnetContractError::MalformedEd25519IdentityKey(
"buffer provided to decode base58 encoded string into was too small".into()
)),
validate_node_identity_signature(deps.as_ref(), &address1, &sig_addr1_key1, long_bs58,)
);
assert_eq!(
Err(MixnetContractError::MalformedEd25519Signature(
"buffer provided to decode base58 encoded string into was too small".into()
)),
validate_node_identity_signature(
deps.as_ref(),
&address1,
long_bs58.into(),
&keypair1.public_key().to_base58_string(),
)
);
assert_eq!(
Err(MixnetContractError::MalformedEd25519IdentityKey(
"Too few bytes provided for the public key".into()
)),
validate_node_identity_signature(deps.as_ref(), &address1, &sig_addr1_key1, short_bs58,)
);
assert_eq!(
Err(MixnetContractError::MalformedEd25519Signature(
"Too few bytes provided for the signature".into()
)),
validate_node_identity_signature(
deps.as_ref(),
&address1,
short_bs58.into(),
&keypair1.public_key().to_base58_string(),
)
);
assert_eq!(
Err(MixnetContractError::InvalidEd25519Signature),
validate_node_identity_signature(
deps.as_ref(),
&address1,
&sig_addr1_key1,
&keypair2.public_key().to_base58_string(),
)
);
assert_eq!(
Err(MixnetContractError::InvalidEd25519Signature),
validate_node_identity_signature(
deps.as_ref(),
&address1,
&sig_addr2_key1,
&keypair1.public_key().to_base58_string(),
)
);
assert_eq!(
Err(MixnetContractError::InvalidEd25519Signature),
validate_node_identity_signature(
deps.as_ref(),
&address2,
&sig_addr1_key1,
&keypair1.public_key().to_base58_string(),
)
);
assert_eq!(
Err(MixnetContractError::InvalidEd25519Signature),
validate_node_identity_signature(
deps.as_ref(),
&address1,
&sig_addr1_key2,
&keypair1.public_key().to_base58_string(),
)
);
assert!(validate_node_identity_signature(
deps.as_ref(),
&address1,
&sig_addr1_key1,
&keypair1.public_key().to_base58_string(),
)
.is_ok());
}
}
+14 -99
View File
@@ -54,7 +54,6 @@ pub mod test_helpers {
use mixnet_contract_common::events::{
may_find_attribute, MixnetEventType, DELEGATES_REWARD_KEY, OPERATOR_REWARD_KEY,
};
use mixnet_contract_common::families::FamilyHead;
use mixnet_contract_common::mixnode::{MixNodeRewarding, UnbondedMixnode};
use mixnet_contract_common::pending_events::{PendingEpochEventData, PendingIntervalEventData};
use mixnet_contract_common::reward_params::{Performance, RewardingParams};
@@ -62,10 +61,10 @@ pub mod test_helpers {
use mixnet_contract_common::rewarding::simulator::Simulator;
use mixnet_contract_common::rewarding::RewardDistribution;
use mixnet_contract_common::{
construct_family_join_permit, Delegation, EpochState, EpochStatus, Gateway,
GatewayBondingPayload, IdentityKey, IdentityKeyRef, InitialRewardingParams, InstantiateMsg,
Interval, MixId, MixNode, MixNodeBond, MixnodeBondingPayload, Percent,
RewardedSetNodeStatus, SignableGatewayBondingMsg, SignableMixNodeBondingMsg,
Delegation, EpochState, EpochStatus, Gateway, GatewayBondingPayload, IdentityKey,
InitialRewardingParams, InstantiateMsg, Interval, MixId, MixNode, MixNodeBond,
MixnodeBondingPayload, Percent, RewardedSetNodeStatus, SignableGatewayBondingMsg,
SignableMixNodeBondingMsg,
};
use nym_contracts_common::signing::{
ContractMessageContent, MessageSignature, SignableMessage, SigningAlgorithm, SigningPurpose,
@@ -170,64 +169,26 @@ pub mod test_helpers {
.collect::<Vec<_>>()
}
pub fn generate_family_join_permit(
&mut self,
family_owner_keys: &identity::KeyPair,
member_node: IdentityKeyRef,
vesting: bool,
) -> MessageSignature {
let identity = family_owner_keys.public_key().to_base58_string();
let head_mixnode = mixnodes_storage::mixnode_bonds()
.idx
.identity_key
.item(self.deps().storage, identity.clone())
.unwrap()
.map(|record| record.1)
.unwrap();
let family_head = FamilyHead::new(&identity);
let owner = head_mixnode.owner;
let nonce =
signing_storage::get_signing_nonce(self.deps().storage, owner.clone()).unwrap();
let proxy = if vesting {
Some(self.vesting_contract())
} else {
None
};
let msg =
construct_family_join_permit(nonce, family_head, proxy, member_node.to_owned());
let sig_bytes = family_owner_keys
.private_key()
.sign(&msg.to_plaintext().unwrap())
.to_bytes();
MessageSignature::from(sig_bytes.as_ref())
}
#[allow(unused)]
pub fn join_family(
&mut self,
member: &str,
member_keys: &identity::KeyPair,
head_keys: &identity::KeyPair,
vesting: bool,
) {
let member_identity = member_keys.public_key().to_base58_string();
let identity_signature = member_keys.private_key().sign_text(member);
let join_signature = head_keys
.private_key()
.sign(&member_keys.public_key().to_bytes())
.to_base58_string();
let head_identity = head_keys.public_key().to_base58_string();
let join_permit =
self.generate_family_join_permit(head_keys, &member_identity, vesting);
let family_head = FamilyHead::new(&head_identity);
try_join_family(
self.deps_mut(),
mock_info(member, &[]),
join_permit,
family_head,
Some(identity_signature),
join_signature,
head_identity,
)
.unwrap();
}
@@ -238,8 +199,9 @@ pub mod test_helpers {
label: &str,
) -> (MixId, identity::KeyPair) {
let (mix_id, keys) = self.add_dummy_mixnode_with_proxy_and_keypair(head, None);
let sig = keys.private_key().sign_text(head);
try_create_family(self.deps_mut(), mock_info(head, &[]), label.to_string()).unwrap();
try_create_family(self.deps_mut(), mock_info(head, &[]), sig, label).unwrap();
(mix_id, keys)
}
@@ -328,53 +290,6 @@ pub mod test_helpers {
ed25519_sign_message(msg, key)
}
pub fn add_dummy_mixnode_with_keypair(
&mut self,
owner: &str,
stake: Option<Uint128>,
) -> (MixId, identity::KeyPair) {
let stake = self.make_mix_pledge(stake);
let keypair = identity::KeyPair::new(&mut self.rng);
let identity_key = keypair.public_key().to_base58_string();
let legit_sphinx_keys = nym_crypto::asymmetric::encryption::KeyPair::new(&mut self.rng);
let mixnode = MixNode {
identity_key,
sphinx_key: legit_sphinx_keys.public_key().to_base58_string(),
..tests::fixtures::mix_node_fixture()
};
let msg = mixnode_bonding_sign_payload(
self.deps(),
owner,
None,
mixnode.clone(),
stake.clone(),
);
let owner_signature = ed25519_sign_message(msg, keypair.private_key());
let info = mock_info(owner, &stake);
let current_id_counter = mixnodes_storage::MIXNODE_ID_COUNTER
.may_load(self.deps().storage)
.unwrap()
.unwrap_or_default();
let env = self.env();
try_add_mixnode(
self.deps_mut(),
env,
info,
mixnode,
tests::fixtures::mix_node_cost_params_fixture(),
owner_signature,
)
.unwrap();
// newly added mixnode gets assigned the current counter + 1
(current_id_counter + 1, keypair)
}
pub fn add_dummy_mixnode_with_proxy_and_keypair(
&mut self,
owner: &str,
+4 -4
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-vesting-contract"
version = "1.3.0"
version = "1.2.0"
description = "Nym vesting contract"
edition = { workspace = true }
authors = { workspace = true }
@@ -20,9 +20,9 @@ name = "vesting_contract"
crate-type = ["cdylib", "rlib"]
[dependencies]
mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract", package = "nym-mixnet-contract-common", version = "0.4.0" }
contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common", package = "nym-contracts-common", version = "0.4.0" }
vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract", package = "nym-vesting-contract-common", version = "0.4.0" }
mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract", package = "nym-mixnet-contract-common", version = "0.3.0" }
contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common", package = "nym-contracts-common", version = "0.3.0" }
vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract", package = "nym-vesting-contract-common", version = "0.3.0" }
cosmwasm-std = { workspace = true }
cosmwasm-derive = { workspace = true }
+47 -15
View File
@@ -11,10 +11,9 @@ use contracts_common::signing::MessageSignature;
use contracts_common::ContractBuildInformation;
use cosmwasm_std::{
coin, entry_point, to_binary, Addr, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, Order,
QueryResponse, Response, StdResult, Timestamp, Uint128,
QueryResponse, Response, StdError, StdResult, Timestamp, Uint128,
};
use cw_storage_plus::Bound;
use mixnet_contract_common::families::FamilyHead;
use mixnet_contract_common::gateway::GatewayConfigUpdate;
use mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
use mixnet_contract_common::{Gateway, MixId, MixNode};
@@ -76,6 +75,15 @@ pub fn instantiate(
#[entry_point]
pub fn migrate(deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
// this is the first migration that uses cw2 standard and thus the value in the storage doesn't yet exist
// set it instead.
if matches!(
cw2::get_contract_version(deps.storage),
Err(StdError::NotFound { .. })
) {
cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
}
// note: don't remove this particular bit of code as we have to ALWAYS check whether we have to update the stored version
let version: Version =
CONTRACT_VERSION
@@ -98,7 +106,7 @@ pub fn migrate(deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Respons
cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
// If state structure changed in any contract version in the way migration is needed, it
// should occur here, for example anything from `crate::queued_migrations::`
// should occur here
}
Ok(Response::new())
@@ -112,13 +120,28 @@ pub fn execute(
msg: ExecuteMsg,
) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::CreateFamily { label } => try_create_family(info, deps, label),
ExecuteMsg::CreateFamily {
owner_signature,
label,
} => try_create_family(info, deps, owner_signature, label),
ExecuteMsg::JoinFamily {
join_permit,
node_identity_signature,
family_signature,
family_head,
} => try_join_family(info, deps, join_permit, family_head),
ExecuteMsg::LeaveFamily { family_head } => try_leave_family(info, deps, family_head),
ExecuteMsg::KickFamilyMember { member } => try_kick_family_member(info, deps, member),
} => try_join_family(
info,
deps,
node_identity_signature,
family_signature,
family_head,
),
ExecuteMsg::LeaveFamily {
node_identity_signature,
family_head,
} => try_leave_family(info, deps, node_identity_signature, family_head),
ExecuteMsg::KickFamilyMember { signature, member } => {
try_kick_family_member(info, deps, signature, member)
}
ExecuteMsg::UpdateLockedPledgeCap { address, cap } => {
try_update_locked_pledge_cap(address, cap, info, deps)
}
@@ -212,35 +235,44 @@ pub fn execute(
pub fn try_create_family(
info: MessageInfo,
deps: DepsMut,
owner_signature: String,
label: String,
) -> Result<Response, ContractError> {
let account = account_from_address(info.sender.as_ref(), deps.storage, deps.api)?;
account.try_create_family(deps.storage, label)
account.try_create_family(deps.storage, owner_signature, label)
}
pub fn try_join_family(
info: MessageInfo,
deps: DepsMut,
join_permit: MessageSignature,
family_head: FamilyHead,
node_identity_signature: String,
family_signature: String,
family_head: String,
) -> Result<Response, ContractError> {
let account = account_from_address(info.sender.as_ref(), deps.storage, deps.api)?;
account.try_join_family(deps.storage, join_permit, family_head)
account.try_join_family(
deps.storage,
node_identity_signature,
family_signature,
&family_head,
)
}
pub fn try_leave_family(
info: MessageInfo,
deps: DepsMut,
family_head: FamilyHead,
node_identity_signature: String,
family_head: String,
) -> Result<Response, ContractError> {
let account = account_from_address(info.sender.as_ref(), deps.storage, deps.api)?;
account.try_leave_family(deps.storage, family_head)
account.try_leave_family(deps.storage, node_identity_signature, &family_head)
}
pub fn try_kick_family_member(
info: MessageInfo,
deps: DepsMut,
signature: String,
member: String,
) -> Result<Response, ContractError> {
let account = account_from_address(info.sender.as_ref(), deps.storage, deps.api)?;
account.try_head_kick_member(deps.storage, &member)
account.try_head_kick_member(deps.storage, signature, &member)
}
/// Update locked_pledge_cap, the hard cap for staking/bonding with unvested tokens.
+1 -1
View File
@@ -1,2 +1,2 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
@@ -1,32 +1,34 @@
use crate::errors::ContractError;
use contracts_common::signing::MessageSignature;
use cosmwasm_std::{Response, Storage};
use mixnet_contract_common::families::FamilyHead;
use mixnet_contract_common::IdentityKeyRef;
pub trait NodeFamilies {
fn try_create_family(
&self,
storage: &dyn Storage,
owner_signature: String,
label: String,
) -> Result<Response, ContractError>;
fn try_join_family(
&self,
storage: &dyn Storage,
join_permit: MessageSignature,
family_head: FamilyHead,
node_identity_signature: String,
family_signature: String,
family_head: IdentityKeyRef,
) -> Result<Response, ContractError>;
fn try_leave_family(
&self,
storage: &dyn Storage,
family_head: FamilyHead,
signature: String,
family_head: IdentityKeyRef,
) -> Result<Response, ContractError>;
fn try_head_kick_member(
&self,
storage: &dyn Storage,
signature: String,
member: IdentityKeyRef<'_>,
) -> Result<Response, ContractError>;
}
@@ -1,18 +1,18 @@
use super::Account;
use crate::{errors::ContractError, storage::MIXNET_CONTRACT_ADDRESS, traits::NodeFamilies};
use contracts_common::signing::MessageSignature;
use cosmwasm_std::{wasm_execute, Response, Storage};
use mixnet_contract_common::families::FamilyHead;
use mixnet_contract_common::{ExecuteMsg as MixnetExecuteMsg, IdentityKeyRef};
impl NodeFamilies for Account {
fn try_create_family(
&self,
storage: &dyn Storage,
owner_signature: String,
label: String,
) -> Result<Response, ContractError> {
let msg = MixnetExecuteMsg::CreateFamilyOnBehalf {
owner_address: self.owner_address().into_string(),
owner_address: self.owner_address().to_string(),
owner_signature,
label,
};
@@ -24,13 +24,15 @@ impl NodeFamilies for Account {
fn try_join_family(
&self,
storage: &dyn Storage,
join_permit: MessageSignature,
family_head: FamilyHead,
node_identity_signature: String,
family_signature: String,
family_head: IdentityKeyRef,
) -> Result<Response, ContractError> {
let msg = MixnetExecuteMsg::JoinFamilyOnBehalf {
member_address: self.owner_address().to_string(),
join_permit,
family_head,
node_identity_signature,
family_signature,
family_head: family_head.to_string(),
};
let msg = wasm_execute(MIXNET_CONTRACT_ADDRESS.load(storage)?, &msg, vec![])?;
@@ -41,11 +43,13 @@ impl NodeFamilies for Account {
fn try_leave_family(
&self,
storage: &dyn Storage,
family_head: FamilyHead,
node_identity_signature: String,
family_head: IdentityKeyRef,
) -> Result<Response, ContractError> {
let msg = MixnetExecuteMsg::LeaveFamilyOnBehalf {
member_address: self.owner_address().to_string(),
family_head,
node_identity_signature,
family_head: family_head.to_string(),
};
let msg = wasm_execute(MIXNET_CONTRACT_ADDRESS.load(storage)?, &msg, vec![])?;
@@ -56,10 +60,12 @@ impl NodeFamilies for Account {
fn try_head_kick_member(
&self,
storage: &dyn Storage,
signature: String,
member: IdentityKeyRef<'_>,
) -> Result<Response, ContractError> {
let msg = MixnetExecuteMsg::KickFamilyMemberOnBehalf {
head_address: self.owner_address().to_string(),
signature,
member: member.to_string(),
};
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "explorer-api"
version = "1.1.14"
version = "1.1.13"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-gateway"
version = "1.1.14"
version = "1.1.13"
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jędrzej Stuczyński <andrew@nymtech.net>",
@@ -189,9 +189,9 @@ impl<St: Storage> ConnectionHandler<St> {
_ = shutdown.recv() => {
log::trace!("ConnectionHandler: received shutdown");
}
framed_sphinx_packet = framed_conn.next() => {
Some(framed_sphinx_packet) = framed_conn.next() => {
match framed_sphinx_packet {
Some(Ok(framed_sphinx_packet)) => {
Ok(framed_sphinx_packet) => {
// TODO: benchmark spawning tokio task with full processing vs just processing it
// synchronously under higher load in single and multi-threaded situation.
@@ -200,13 +200,12 @@ impl<St: Storage> ConnectionHandler<St> {
// that change would only slow things down
self.handle_received_packet(framed_sphinx_packet).await;
}
Some(Err(err)) => {
Err(err) => {
error!(
"The socket connection got corrupted with error: {err}. Closing the socket",
);
return;
}
None => break, // stream got closed by remote
}
}
}
+1 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-mixnode"
version = "1.1.15"
version = "1.1.14"
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jędrzej Stuczyński <andrew@nymtech.net>",
@@ -85,9 +85,9 @@ impl ConnectionHandler {
_ = shutdown.recv() => {
log::trace!("ConnectionHandler: received shutdown");
}
framed_sphinx_packet = framed_conn.next() => {
Some(framed_sphinx_packet) = framed_conn.next() => {
match framed_sphinx_packet {
Some(Ok(framed_sphinx_packet)) => {
Ok(framed_sphinx_packet) => {
// TODO: benchmark spawning tokio task with full processing vs just processing it
// synchronously (without delaying inside of course,
// delay is moved to a global DelayQueue)
@@ -98,13 +98,12 @@ impl ConnectionHandler {
// that change would only slow things down
self.handle_received_packet(framed_sphinx_packet);
}
Some(Err(err)) => {
Err(err) => {
error!(
"The socket connection got corrupted with error: {err}. Closing the socket",
);
return;
}
None => break, // stream got closed by remote
}
},
}
+1 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-api"
version = "1.1.15"
version = "1.1.14"
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jędrzej Stuczyński <andrew@nymtech.net>",
+1 -4
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::node_status_api::models::NymApiStorageError;
use nym_mixnet_contract_common::{EpochState, MixId};
use nym_mixnet_contract_common::EpochState;
use thiserror::Error;
use validator_client::nyxd::error::NyxdError;
use validator_client::nyxd::AccountId;
@@ -22,9 +22,6 @@ pub enum RewardingError {
operation: String,
},
#[error("it seems the current epoch is in mid-rewarding state (last rewarded is {last_rewarded}). With our current nym-api this shouldn't have been possible. Manual intervention is required.")]
MidMixRewarding { last_rewarded: MixId },
// #[error("There were no mixnodes to reward (network is dead)")]
// NoMixnodesToReward,
#[error("Failed to execute the smart contract - {0}")]
+21 -9
View File
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::epoch_operations::error::RewardingError;
use crate::support::storage::models::RewardingReport;
use crate::RewardedSetUpdater;
use nym_mixnet_contract_common::reward_params::Performance;
use nym_mixnet_contract_common::{EpochState, ExecuteMsg, Interval, MixId};
@@ -41,15 +42,8 @@ impl RewardedSetUpdater {
warn!("we seem to have crashed mid epoch operations... no need to reward mixnodes as we've already done that! (or this could be a false positive if there were no nodes to reward - to fix this warning later)");
Ok(())
}
EpochState::Rewarding { last_rewarded, .. } => {
EpochState::Rewarding { .. } => {
log::info!("Rewarding the current rewarded set...");
// with how the nym-api is currently coded, this should never happen as we're always
// rewarding ALL mixnodes at once, but who knows what we might decide to do in the future...
if last_rewarded != 0 {
return Err(RewardingError::MidMixRewarding { last_rewarded });
}
if let Err(err) = self._reward_current_rewarded_set(current_interval).await {
log::error!("FAILED to reward rewarded set - {err}");
Err(err)
@@ -68,8 +62,17 @@ impl RewardedSetUpdater {
let mut to_reward = self.nodes_to_reward(current_interval).await;
to_reward.sort_by_key(|a| a.mix_id);
if let Some(existing_report) = self
.storage
.get_rewarding_report(current_interval.current_epoch_absolute_id())
.await?
{
warn!("We have already rewarded mixnodes for this rewarding epoch ({}). {} nodes should have gotten rewards", existing_report.absolute_epoch_id, existing_report.eligible_mixnodes);
return Ok(());
}
if to_reward.is_empty() {
error!("There are no nodes to reward in this epoch - we shouldn't have been in the 'Rewarding' state!");
info!("There are no nodes to reward in this epoch");
} else if let Err(err) = self.nyxd_client.send_rewarding_messages(&to_reward).await {
error!(
"failed to perform mixnode rewarding for epoch {}! Error encountered: {err}",
@@ -80,6 +83,15 @@ impl RewardedSetUpdater {
log::info!("rewarded {} mixnodes...", to_reward.len());
let rewarding_report = RewardingReport {
absolute_epoch_id: current_interval.current_epoch_absolute_id(),
eligible_mixnodes: to_reward.len() as u32,
};
self.storage
.insert_rewarding_report(rewarding_report)
.await?;
Ok(())
}
-2
View File
@@ -867,7 +867,6 @@ impl StorageManager {
/// # Arguments
///
/// * `report`: report to insert into the database
#[allow(unused)]
pub(crate) async fn insert_rewarding_report(
&self,
report: RewardingReport,
@@ -886,7 +885,6 @@ impl StorageManager {
Ok(())
}
#[allow(unused)]
pub(crate) async fn get_rewarding_report(
&self,
absolute_epoch_id: EpochId,
+22 -2
View File
@@ -9,8 +9,8 @@ use crate::node_status_api::models::{
};
use crate::node_status_api::{ONE_DAY, ONE_HOUR};
use crate::storage::manager::StorageManager;
use crate::storage::models::{NodeStatus, TestingRoute};
use nym_mixnet_contract_common::MixId;
use crate::storage::models::{NodeStatus, RewardingReport, TestingRoute};
use nym_mixnet_contract_common::{EpochId, MixId};
use rocket::fairing::AdHoc;
use sqlx::ConnectOptions;
use std::path::PathBuf;
@@ -717,6 +717,26 @@ impl NymApiStorage {
.map_err(|err| err.into())
}
pub(crate) async fn insert_rewarding_report(
&self,
report: RewardingReport,
) -> Result<(), NymApiStorageError> {
self.manager
.insert_rewarding_report(report)
.await
.map_err(|err| err.into())
}
pub(crate) async fn get_rewarding_report(
&self,
absolute_epoch_id: EpochId,
) -> Result<Option<RewardingReport>, NymApiStorageError> {
self.manager
.get_rewarding_report(absolute_epoch_id)
.await
.map_err(|err| err.into())
}
pub(crate) async fn get_blinded_signature_response(
&self,
tx_hash: &str,
-2
View File
@@ -41,8 +41,6 @@ pub(crate) struct TestingRoute {
pub(crate) monitor_run_db_id: i64,
}
// for now let's leave it here to have a data model to use with existing database tables
#[allow(unused)]
pub(crate) struct RewardingReport {
// references particular interval_rewarding
pub(crate) absolute_epoch_id: u32,
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@nym/nym-connect",
"version": "1.1.13",
"version": "1.1.12",
"main": "index.js",
"license": "MIT",
"scripts": {
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-connect"
version = "1.1.13"
version = "1.1.12"
description = "nym-connect"
authors = ["Nym Technologies SA"]
license = ""
@@ -1,7 +1,7 @@
{
"package": {
"productName": "nym-connect",
"version": "1.1.13"
"version": "1.1.12"
},
"build": {
"distDir": "../dist",
-1
View File
@@ -115,7 +115,6 @@
"webpack": "^5.75.0",
"webpack-cli": "^4.8.0",
"webpack-dev-server": "^4.5.0",
"webpack-favicons": "^1.3.8",
"webpack-merge": "^5.8.0",
"yaml-loader": "^0.8.0"
}
+10 -1
View File
@@ -6,10 +6,19 @@ const entry = {
app: path.resolve(__dirname, 'src/index.tsx'),
};
module.exports = merge(common, {
const config = merge(common, {
mode: 'production',
node: {
__dirname: false,
},
entry,
});
// Remove WebpackFavicons plugin as it makes FDroid build more
// difficult to configure since webpack-favicons depends on sharp,
// which depends on system library libvips
// As we are building for mobile, this is useless anyway
// TODO do not base deletion on index
config.plugins.splice(2, 1);
module.exports = config;
-12
View File
@@ -2,18 +2,6 @@
## [Unreleased]
## [v1.2.0] (2023-04-04)
- Add Version and Location to gateway settings ([#3266])
- Wallet - bonding screen - short info with link ([#3179])
- Wallet - discrepancies figma vs wallet ([#2183])
- Wallet - If a gateway is bonded show gateway settings in the node settings: ([#2242])
[#3266]: https://github.com/nymtech/nym/issues/3266
[#3179]: https://github.com/nymtech/nym/issues/3179
[#2183]: https://github.com/nymtech/nym/issues/2183
[#2242]: https://github.com/nymtech/nym/issues/2242
## [v1.1.12] (2023-03-15)
- Wallet - in the vesting section separate the "Unlocked transferable tokens" into "Unlocked vesting tokens" and "Transferable rewards" for better clarity ([#3132])
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@nymproject/nym-wallet-app",
"version": "1.1.13",
"version": "1.1.12",
"main": "index.js",
"license": "MIT",
"scripts": {
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym_wallet"
version = "1.1.13"
version = "1.1.12"
description = "Nym Native Wallet"
authors = ["Nym Technologies SA"]
license = ""

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