Compare commits

...

5 Commits

Author SHA1 Message Date
durch 0877b6edae Add gossip to mixnodes 2023-03-17 11:49:52 +01:00
Jon Häggblad 4c2967a733 Delete stray .gitignore file 2023-03-16 15:27:45 +00:00
Jon Häggblad 2b80e5d1c9 Remove leftover file from deleted crate 2023-03-16 12:59:46 +00:00
Jon Häggblad 4e7ff53214 Contract and client support for updating gateway config (#3166)
* mixnet-contract: add update gateway config

* mixnet-contract: tests for updating gateway config

* vesting-contract: add update gateway config

* validator-client: add update gateway config

* wallet: add update_gateway_config

* common/commands: add support for setting gateway config

* Remove commented out line

* Review fixes

* Generate ts file for GatewayConfigUpdate type

* Add generated GatewayConfigUpdate.ts file
2023-03-16 13:43:56 +01:00
Jon Häggblad 9ec36e49b7 contracts: remove .gitignore with Cargo.lock in it
While developing the service-provider-directory contract I ran into
issues with the lock file being inconsistent for cosmwasm-std (1.0 vs
1.2) and was hidden due to ignoring the lock file
2023-03-16 12:28:35 +00:00
38 changed files with 694 additions and 44 deletions
Generated
+19
View File
@@ -505,6 +505,22 @@ dependencies = [
"zeroize",
]
[[package]]
name = "chitchat"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fec1bff4fabc59f0b7408478183a796ab0a9bcb0534bb413f3cf66f44ce2aca1"
dependencies = [
"anyhow",
"async-trait",
"bytes",
"rand 0.8.5",
"serde",
"tokio",
"tokio-stream",
"tracing",
]
[[package]]
name = "chrono"
version = "0.4.23"
@@ -2911,6 +2927,7 @@ name = "mixnode-common"
version = "0.1.0"
dependencies = [
"bytes",
"chitchat",
"futures",
"humantime-serde",
"log",
@@ -2926,6 +2943,7 @@ dependencies = [
"nym-task",
"rand 0.8.5",
"serde",
"serde_json",
"thiserror",
"tokio",
"tokio-util",
@@ -3491,6 +3509,7 @@ dependencies = [
"anyhow",
"atty",
"bs58",
"chitchat",
"clap 4.1.4",
"colored",
"cupid",
-3
View File
@@ -1,3 +0,0 @@
/target
**/*.rs.bk
Cargo.lock
View File
@@ -9,6 +9,7 @@ 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::gateway::GatewayConfigUpdate;
use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
use nym_mixnet_contract_common::reward_params::{IntervalRewardingParamsUpdate, Performance};
use nym_mixnet_contract_common::{
@@ -498,6 +499,36 @@ pub trait MixnetSigningClient {
.await
}
async fn update_gateway_config(
&self,
new_config: GatewayConfigUpdate,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_mixnet_contract(
fee,
MixnetExecuteMsg::UpdateGatewayConfig { new_config },
vec![],
)
.await
}
async fn update_gateway_config_on_behalf(
&self,
owner: AccountId,
new_config: GatewayConfigUpdate,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_mixnet_contract(
fee,
MixnetExecuteMsg::UpdateGatewayConfigOnBehalf {
new_config,
owner: owner.to_string(),
},
vec![],
)
.await
}
// delegation-related:
async fn delegate_to_mixnode(
@@ -7,6 +7,7 @@ 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::gateway::GatewayConfigUpdate;
use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
use nym_mixnet_contract_common::{Gateway, MixId, MixNode};
use nym_vesting_contract_common::messages::{
@@ -35,6 +36,12 @@ pub trait VestingSigningClient {
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError>;
async fn vesting_update_gateway_config(
&self,
new_config: GatewayConfigUpdate,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError>;
async fn update_mixnet_address(
&self,
address: &str,
@@ -185,6 +192,19 @@ impl<C: SigningCosmWasmClient + Sync + Send + Clone> VestingSigningClient for Ny
.await
}
async fn vesting_update_gateway_config(
&self,
new_config: GatewayConfigUpdate,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_vesting_contract(
fee,
VestingExecuteMsg::UpdateGatewayConfig { new_config },
vec![],
)
.await
}
async fn update_mixnet_address(
&self,
address: &str,
@@ -5,6 +5,7 @@ use clap::{Args, Subcommand};
pub mod bond_gateway;
pub mod gateway_bonding_sign_payload;
pub mod settings;
pub mod unbond_gateway;
pub mod vesting_bond_gateway;
pub mod vesting_unbond_gateway;
@@ -18,6 +19,8 @@ pub struct MixnetOperatorsGateway {
#[derive(Debug, Subcommand)]
pub enum MixnetOperatorsGatewayCommands {
/// Manage your gateway settings stored in the directory
Settings(settings::MixnetOperatorsGatewaySettings),
/// Bond to a gateway
Bond(bond_gateway::Args),
/// Unbond from a gateway
@@ -0,0 +1,22 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod update_config;
pub mod vesting_update_config;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct MixnetOperatorsGatewaySettings {
#[clap(subcommand)]
pub command: MixnetOperatorsGatewaySettingsCommands,
}
#[derive(Debug, Subcommand)]
pub enum MixnetOperatorsGatewaySettingsCommands {
/// Update gateway configuration
UpdateConfig(update_config::Args),
/// Update gateway configuration for a gateway bonded with locked tokens
VestingUpdateConfig(vesting_update_config::Args),
}
@@ -0,0 +1,60 @@
// 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_mixnet_contract_common::GatewayConfigUpdate;
use validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient};
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub host: Option<String>,
#[clap(long)]
pub mix_port: Option<u16>,
#[clap(long)]
pub clients_port: Option<u16>,
#[clap(long)]
pub location: Option<String>,
#[clap(long)]
pub version: Option<String>,
}
pub async fn update_config(args: Args, client: SigningClient) {
info!("Update gateway config!");
let current_details = match client
.get_owned_gateway(client.address())
.await
.expect("failed to query the chain for gateway details")
.gateway
{
Some(details) => details,
None => {
log::warn!("this operator does not own a gateway to update");
return;
}
};
let update = GatewayConfigUpdate {
host: args.host.unwrap_or(current_details.gateway.host),
mix_port: args.mix_port.unwrap_or(current_details.gateway.mix_port),
clients_port: args
.clients_port
.unwrap_or(current_details.gateway.clients_port),
location: args.location.unwrap_or(current_details.gateway.location),
version: args.version.unwrap_or(current_details.gateway.version),
};
let res = client
.update_gateway_config(update, None)
.await
.expect("updating gateway config");
info!("gateway config updated: {:?}", res)
}
@@ -0,0 +1,61 @@
// 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_mixnet_contract_common::GatewayConfigUpdate;
use validator_client::nyxd::traits::MixnetQueryClient;
use validator_client::nyxd::VestingSigningClient;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub host: Option<String>,
#[clap(long)]
pub mix_port: Option<u16>,
#[clap(long)]
pub clients_port: Option<u16>,
#[clap(long)]
pub location: Option<String>,
#[clap(long)]
pub version: Option<String>,
}
pub async fn vesting_update_config(client: SigningClient, args: Args) {
info!("Update vesting gateway config!");
let current_details = match client
.get_owned_gateway(client.address())
.await
.expect("failed to query the chain for gateway details")
.gateway
{
Some(details) => details,
None => {
log::warn!("this operator does not own a gateway to update");
return;
}
};
let update = GatewayConfigUpdate {
host: args.host.unwrap_or(current_details.gateway.host),
mix_port: args.mix_port.unwrap_or(current_details.gateway.mix_port),
clients_port: args
.clients_port
.unwrap_or(current_details.gateway.clients_port),
location: args.location.unwrap_or(current_details.gateway.location),
version: args.version.unwrap_or(current_details.gateway.version),
};
let res = client
.vesting_update_gateway_config(update, None)
.await
.expect("updating vesting gateway config");
info!("gateway config updated: {:?}", res)
}
@@ -1,6 +1,7 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::gateway::GatewayConfigUpdate;
use crate::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
use crate::reward_params::{IntervalRewardParams, IntervalRewardingParamsUpdate};
use crate::rewarding::RewardDistribution;
@@ -42,6 +43,7 @@ pub enum MixnetEventType {
ReconcilePendingEvents,
PendingIntervalConfigUpdate,
IntervalConfigUpdate,
GatewayConfigUpdate,
}
impl From<MixnetEventType> for String {
@@ -86,6 +88,7 @@ impl ToString for MixnetEventType {
MixnetEventType::PendingIntervalConfigUpdate => "pending_interval_config_update",
MixnetEventType::IntervalConfigUpdate => "interval_config_update",
MixnetEventType::DelegationOnUnbonding => "delegation_on_unbonding_node",
MixnetEventType::GatewayConfigUpdate => "gateway_config_update",
};
format!("{EVENT_VERSION_PREFIX}{event_name}")
@@ -122,6 +125,7 @@ pub const OLD_REWARDING_VALIDATOR_ADDRESS_KEY: &str = "old_rewarding_validator_a
pub const NEW_REWARDING_VALIDATOR_ADDRESS_KEY: &str = "new_rewarding_validator_address";
pub const UPDATED_MIXNODE_CONFIG_KEY: &str = "updated_mixnode_config";
pub const UPDATED_GATEWAY_CONFIG_KEY: &str = "updated_gateway_config";
pub const UPDATED_MIXNODE_COST_PARAMS_KEY: &str = "updated_mixnode_cost_params";
// rewarding
@@ -382,6 +386,17 @@ pub fn new_mixnode_config_update_event(
.add_attribute(UPDATED_MIXNODE_CONFIG_KEY, update.to_inline_json())
}
pub fn new_gateway_config_update_event(
owner: &Addr,
proxy: &Option<Addr>,
update: &GatewayConfigUpdate,
) -> Event {
Event::new(MixnetEventType::GatewayConfigUpdate)
.add_attribute(OWNER_KEY, owner)
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
.add_attribute(UPDATED_GATEWAY_CONFIG_KEY, update.to_inline_json())
}
pub fn new_mixnode_pending_cost_params_update_event(
mix_id: MixId,
owner: &Addr,
@@ -112,6 +112,26 @@ impl Display for GatewayBond {
}
}
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
#[cfg_attr(
feature = "generate-ts",
ts(export_to = "ts-packages/types/src/types/rust/GatewayConfigUpdate.ts")
)]
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
pub struct GatewayConfigUpdate {
pub host: String,
pub mix_port: u16,
pub clients_port: u16,
pub location: String,
pub version: String,
}
impl GatewayConfigUpdate {
pub fn to_inline_json(&self) -> String {
serde_json::to_string(self).unwrap_or_else(|_| "serialisation failure".into())
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct PagedGatewayResponse {
pub nodes: Vec<GatewayBond>,
@@ -27,7 +27,8 @@ pub use delegation::{
PagedMixNodeDelegationsResponse,
};
pub use gateway::{
Gateway, GatewayBond, GatewayBondResponse, GatewayOwnershipResponse, PagedGatewayResponse,
Gateway, GatewayBond, GatewayBondResponse, GatewayConfigUpdate, GatewayOwnershipResponse,
PagedGatewayResponse,
};
pub use interval::{
CurrentIntervalResponse, EpochState, EpochStatus, Interval, NumberOfPendingEventsResponse,
@@ -3,6 +3,7 @@
use crate::delegation::OwnerProxySubKey;
use crate::error::MixnetContractError;
use crate::gateway::GatewayConfigUpdate;
use crate::helpers::IntoBaseDecimal;
use crate::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
use crate::reward_params::{
@@ -199,6 +200,13 @@ pub enum ExecuteMsg {
UnbondGatewayOnBehalf {
owner: String,
},
UpdateGatewayConfig {
new_config: GatewayConfigUpdate,
},
UpdateGatewayConfigOnBehalf {
new_config: GatewayConfigUpdate,
owner: String,
},
// delegation-related:
DelegateToMixnode {
@@ -313,6 +321,10 @@ impl ExecuteMsg {
}
ExecuteMsg::UnbondGateway { .. } => "unbonding gateway".into(),
ExecuteMsg::UnbondGatewayOnBehalf { .. } => "unbonding gateway on behalf".into(),
ExecuteMsg::UpdateGatewayConfig { .. } => "updating gateway configuration".into(),
ExecuteMsg::UpdateGatewayConfigOnBehalf { .. } => {
"updating gateway configuration on behalf".into()
}
ExecuteMsg::DelegateToMixnode { mix_id } => format!("delegating to mixnode {mix_id}"),
ExecuteMsg::DelegateToMixnodeOnBehalf { mix_id, .. } => {
format!("delegating to mixnode {mix_id} on behalf")
@@ -17,6 +17,7 @@ pub const VESTING_MIXNODE_BONDING_EVENT_TYPE: &str = "vesting_mixnode_bonding";
pub const VESTING_PLEDGE_MORE_EVENT_TYPE: &str = "vesting_pledge_more";
pub const VESTING_MIXNODE_UNBONDING_EVENT_TYPE: &str = "vesting_mixnode_unbonding";
pub const VESTING_UPDATE_MIXNODE_CONFIG_EVENT_TYPE: &str = "vesting_update_mixnode_config";
pub const VESTING_UPDATE_GATEWAY_CONFIG_EVENT_TYPE: &str = "vesting_update_gateway_config";
pub const VESTING_UPDATE_MIXNODE_COST_PARAMS_EVENT_TYPE: &str =
"vesting_update_mixnode_cost_params";
@@ -121,6 +122,10 @@ pub fn new_vesting_update_mixnode_config_event() -> Event {
Event::new(VESTING_UPDATE_MIXNODE_CONFIG_EVENT_TYPE)
}
pub fn new_vesting_update_gateway_config_event() -> Event {
Event::new(VESTING_UPDATE_GATEWAY_CONFIG_EVENT_TYPE)
}
pub fn new_vesting_update_mixnode_cost_params_event() -> Event {
Event::new(VESTING_UPDATE_MIXNODE_COST_PARAMS_EVENT_TYPE)
}
@@ -1,6 +1,7 @@
use contracts_common::signing::MessageSignature;
use cosmwasm_std::{Coin, Timestamp};
use mixnet_contract_common::{
gateway::GatewayConfigUpdate,
mixnode::{MixNodeConfigUpdate, MixNodeCostParams},
Gateway, IdentityKey, MixId, MixNode,
};
@@ -140,6 +141,9 @@ pub enum ExecuteMsg {
owner: String,
amount: Coin,
},
UpdateGatewayConfig {
new_config: GatewayConfigUpdate,
},
TransferOwnership {
to_address: String,
},
@@ -179,6 +183,7 @@ impl ExecuteMsg {
ExecuteMsg::BondGateway { .. } => "VestingExecuteMsg::BondGateway",
ExecuteMsg::UnbondGateway { .. } => "VestingExecuteMsg::UnbondGateway",
ExecuteMsg::TrackUnbondGateway { .. } => "VestingExecuteMsg::TrackUnbondGateway",
ExecuteMsg::UpdateGatewayConfig { .. } => "VestingExecuteMsg::UpdateGatewayConfig",
ExecuteMsg::TransferOwnership { .. } => "VestingExecuteMsg::TransferOwnership",
ExecuteMsg::UpdateStakingAddress { .. } => "VestingExecuteMsg::UpdateStakingAddress",
ExecuteMsg::UpdateLockedPledgeCap { .. } => "VestingExecuteMsg::UpdateLockedPledgeCap",
+13 -3
View File
@@ -13,12 +13,20 @@ humantime-serde = "1.0"
log = { workspace = true }
rand = "0.8"
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.24.1", features = ["time", "macros", "rt", "net", "io-util"] }
tokio = { version = "1.24.1", features = [
"time",
"macros",
"rt",
"net",
"io-util",
] }
tokio-util = { version = "0.7.4", features = ["codec"] }
url = "2.2"
thiserror = "1.0.37"
chitchat = "0.5"
serde_json = "1"
nym-crypto = { path = "../crypto" }
nym-crypto = { path = "../crypto" }
nym-network-defaults = { path = "../network-defaults" }
nym-sphinx-acknowledgements = { path = "../nymsphinx/acknowledgements" }
nym-sphinx-addressing = { path = "../nymsphinx/addressing" }
@@ -27,5 +35,7 @@ nym-sphinx-framing = { path = "../nymsphinx/framing" }
nym-sphinx-params = { path = "../nymsphinx/params" }
nym-sphinx-types = { path = "../nymsphinx/types" }
nym-task = { path = "../task" }
validator-client = { path = "../client-libs/validator-client", features = ["nyxd-client"]}
validator-client = { path = "../client-libs/validator-client", features = [
"nyxd-client",
] }
nym-bin-common = { path = "../bin-common" }
+14 -1
View File
@@ -3,6 +3,7 @@
use crate::verloc::listener::PacketListener;
use crate::verloc::sender::{PacketSender, TestedNode};
use chitchat::Chitchat;
use futures::stream::FuturesUnordered;
use futures::StreamExt;
use log::*;
@@ -16,6 +17,7 @@ use std::net::SocketAddr;
use std::net::ToSocketAddrs;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::Mutex;
use tokio::task::JoinHandle;
use tokio::time::sleep;
use url::Url;
@@ -293,7 +295,7 @@ impl VerlocMeasurer {
MeasurementOutcome::Done
}
pub async fn run(&mut self) {
pub async fn run(&mut self, chitchat_handle: Arc<Mutex<Chitchat>>) {
self.start_listening();
while !self.shutdown_listener.is_shutdown() {
@@ -345,6 +347,17 @@ impl VerlocMeasurer {
})
.collect::<Vec<_>>();
let mut chitchat_guard = chitchat_handle.lock().await;
let cc_state = chitchat_guard.self_node_state();
let tested_nodes_cc = tested_nodes
.iter()
.map(|node| node.identity.to_string())
.collect::<Vec<String>>();
cc_state.set(
"tested_nodes",
serde_json::to_value(tested_nodes_cc).expect("Could not serialize"),
);
// on start of each run remove old results
self.results.reset_results(tested_nodes.len()).await;
+1
View File
@@ -377,6 +377,7 @@ pub const DEFAULT_CLIENT_LISTENING_PORT: u16 = 9000;
// 'MIXNODE'
pub const DEFAULT_VERLOC_LISTENING_PORT: u16 = 1790;
pub const DEFAULT_HTTP_API_LISTENING_PORT: u16 = 8000;
pub const DEFAULT_GOSSIP_PORT: u16 = 10000;
// 'CLIENT'
pub const DEFAULT_WEBSOCKET_LISTENING_PORT: u16 = 1977;
-1
View File
@@ -1 +0,0 @@
Cargo.lock
+8
View File
@@ -318,6 +318,14 @@ pub fn execute(
ExecuteMsg::UnbondGatewayOnBehalf { owner } => {
crate::gateways::transactions::try_remove_gateway_on_behalf(deps, info, owner)
}
ExecuteMsg::UpdateGatewayConfig { new_config } => {
crate::gateways::transactions::try_update_gateway_config(deps, info, new_config)
}
ExecuteMsg::UpdateGatewayConfigOnBehalf { new_config, owner } => {
crate::gateways::transactions::try_update_gateway_config_on_behalf(
deps, info, new_config, owner,
)
}
// delegation-related:
ExecuteMsg::DelegateToMixnode { mix_id } => {
+20
View File
@@ -0,0 +1,20 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::storage;
use cosmwasm_std::{Addr, Storage};
use mixnet_contract_common::{error::MixnetContractError, GatewayBond};
pub(crate) fn must_get_gateway_bond_by_owner(
store: &dyn Storage,
owner: &Addr,
) -> Result<GatewayBond, MixnetContractError> {
Ok(storage::gateways()
.idx
.owner
.item(store, owner.clone())?
.ok_or(MixnetContractError::NoAssociatedGatewayBond {
owner: owner.clone(),
})?
.1)
}
+1
View File
@@ -1,6 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod helpers;
pub mod queries;
pub mod signature_helpers;
pub mod storage;
+148 -2
View File
@@ -1,16 +1,20 @@
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::helpers::must_get_gateway_bond_by_owner;
use super::storage;
use crate::gateways::signature_helpers::verify_gateway_bonding_signature;
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use crate::signing::storage as signing_storage;
use crate::support::helpers::{
ensure_no_existing_bond, ensure_sent_by_vesting_contract, validate_pledge,
ensure_no_existing_bond, ensure_proxy_match, ensure_sent_by_vesting_contract, validate_pledge,
};
use cosmwasm_std::{wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::events::{new_gateway_bonding_event, new_gateway_unbonding_event};
use mixnet_contract_common::events::{
new_gateway_bonding_event, new_gateway_config_update_event, new_gateway_unbonding_event,
};
use mixnet_contract_common::gateway::GatewayConfigUpdate;
use mixnet_contract_common::{Gateway, GatewayBond};
use nym_contracts_common::signing::MessageSignature;
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
@@ -189,6 +193,56 @@ pub(crate) fn _try_remove_gateway(
)))
}
pub(crate) fn try_update_gateway_config(
deps: DepsMut<'_>,
info: MessageInfo,
new_config: GatewayConfigUpdate,
) -> Result<Response, MixnetContractError> {
let owner = info.sender;
_try_update_gateway_config(deps, new_config, owner, None)
}
pub(crate) fn try_update_gateway_config_on_behalf(
deps: DepsMut,
info: MessageInfo,
new_config: GatewayConfigUpdate,
owner: String,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let owner = deps.api.addr_validate(&owner)?;
let proxy = info.sender;
_try_update_gateway_config(deps, new_config, owner, Some(proxy))
}
pub(crate) fn _try_update_gateway_config(
deps: DepsMut,
new_config: GatewayConfigUpdate,
owner: Addr,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
let existing_bond = must_get_gateway_bond_by_owner(deps.storage, &owner)?;
ensure_proxy_match(&proxy, &existing_bond.proxy)?;
let cfg_update_event = new_gateway_config_update_event(&owner, &proxy, &new_config);
let mut updated_bond = existing_bond.clone();
updated_bond.gateway.host = new_config.host;
updated_bond.gateway.mix_port = new_config.mix_port;
updated_bond.gateway.clients_port = new_config.clients_port;
updated_bond.gateway.location = new_config.location;
updated_bond.gateway.version = new_config.version;
storage::gateways().replace(
deps.storage,
existing_bond.identity(),
Some(&updated_bond),
Some(&existing_bond),
)?;
Ok(Response::new().add_event(cfg_update_event))
}
#[cfg(test)]
pub mod tests {
use super::*;
@@ -196,6 +250,7 @@ pub mod tests {
use crate::gateways::queries;
use crate::gateways::transactions::{
try_add_gateway, try_add_gateway_on_behalf, try_remove_gateway_on_behalf,
try_update_gateway_config, try_update_gateway_config_on_behalf,
};
use crate::interval::pending_events;
use crate::mixnet_contract_settings::storage::minimum_gateway_pledge;
@@ -207,6 +262,7 @@ pub mod tests {
use cosmwasm_std::{Addr, BankMsg, Response, Uint128};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::events::new_gateway_unbonding_event;
use mixnet_contract_common::gateway::GatewayConfigUpdate;
use mixnet_contract_common::ExecuteMsg;
#[test]
@@ -485,4 +541,94 @@ pub mod tests {
}
)
}
#[test]
fn update_gateway_config() {
let mut test = TestSetup::new();
let owner = "alice";
let info = mock_info(owner, &[]);
let update = GatewayConfigUpdate {
host: "1.1.1.1:1234".to_string(),
mix_port: 1234,
clients_port: 1235,
location: "home".to_string(),
version: "v1.2.3".to_string(),
};
// try updating a non existing gateway bond
let res = try_update_gateway_config(test.deps_mut(), info.clone(), update.clone());
assert_eq!(
res,
Err(MixnetContractError::NoAssociatedGatewayBond {
owner: Addr::unchecked(owner)
})
);
test.add_dummy_gateway(owner, None);
let vesting_contract = test.vesting_contract();
// attempted to remove on behalf with invalid proxy (current is `None`)
let res = try_update_gateway_config_on_behalf(
test.deps_mut(),
mock_info(vesting_contract.as_ref(), &[]),
update.clone(),
owner.to_string(),
);
assert_eq!(
res,
Err(MixnetContractError::ProxyMismatch {
existing: "None".to_string(),
incoming: vesting_contract.into_string()
})
);
// "normal" update succeeds
let res = try_update_gateway_config(test.deps_mut(), info, update.clone());
assert!(res.is_ok());
// and the config has actually been updated
let bond =
must_get_gateway_bond_by_owner(test.deps().storage, &Addr::unchecked(owner)).unwrap();
assert_eq!(bond.gateway.host, update.host);
assert_eq!(bond.gateway.mix_port, update.mix_port);
assert_eq!(bond.gateway.clients_port, update.clients_port);
assert_eq!(bond.gateway.location, update.location);
assert_eq!(bond.gateway.version, update.version);
}
#[test]
fn updating_gateway_config_with_illegal_proxy() {
let mut test = TestSetup::new();
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let owner = "alice";
test.add_dummy_gateway_with_illegal_proxy(owner, None, illegal_proxy.clone());
let update = GatewayConfigUpdate {
host: "1.1.1.1:1234".to_string(),
mix_port: 1234,
clients_port: 1235,
location: "at home".to_string(),
version: "v1.2.3".to_string(),
};
let res = try_update_gateway_config_on_behalf(
test.deps_mut(),
mock_info(illegal_proxy.as_ref(), &[]),
update,
owner.to_string(),
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract
}
)
}
}
@@ -295,7 +295,7 @@ pub(crate) fn try_update_mixnode_config(
}
pub(crate) fn try_update_mixnode_config_on_behalf(
deps: DepsMut,
deps: DepsMut<'_>,
info: MessageInfo,
new_config: MixNodeConfigUpdate,
owner: String,
@@ -308,7 +308,7 @@ pub(crate) fn try_update_mixnode_config_on_behalf(
}
pub(crate) fn _try_update_mixnode_config(
deps: DepsMut,
deps: DepsMut<'_>,
new_config: MixNodeConfigUpdate,
owner: Addr,
proxy: Option<Addr>,
+1 -1
View File
@@ -20,7 +20,7 @@ pub mod test_helpers {
perform_pending_epoch_actions, perform_pending_interval_actions, try_begin_epoch_transition,
};
use crate::interval::{pending_events, storage as interval_storage};
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use crate::mixnet_contract_settings::storage::{self as mixnet_params_storage};
use crate::mixnet_contract_settings::storage::{
minimum_gateway_pledge, minimum_mixnode_pledge, rewarding_denom,
rewarding_validator_address,
+13
View File
@@ -14,6 +14,7 @@ use cosmwasm_std::{
QueryResponse, Response, StdError, StdResult, Timestamp, Uint128,
};
use cw_storage_plus::Bound;
use mixnet_contract_common::gateway::GatewayConfigUpdate;
use mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
use mixnet_contract_common::{Gateway, MixId, MixNode};
use semver::Version;
@@ -219,6 +220,9 @@ pub fn execute(
ExecuteMsg::TrackUnbondGateway { owner, amount } => {
try_track_unbond_gateway(&owner, amount, info, deps)
}
ExecuteMsg::UpdateGatewayConfig { new_config } => {
try_update_gateway_config(new_config, info, deps)
}
ExecuteMsg::TransferOwnership { to_address } => {
try_transfer_ownership(to_address, info, deps)
}
@@ -300,6 +304,15 @@ pub fn try_update_mixnode_config(
account.try_update_mixnode_config(new_config, deps.storage)
}
pub fn try_update_gateway_config(
new_config: GatewayConfigUpdate,
info: MessageInfo,
deps: DepsMut,
) -> Result<Response, ContractError> {
let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?;
account.try_update_gateway_config(new_config, deps.storage)
}
pub fn try_update_mixnode_cost_params(
new_costs: MixNodeCostParams,
info: MessageInfo,
@@ -2,6 +2,7 @@ use crate::errors::ContractError;
use contracts_common::signing::MessageSignature;
use cosmwasm_std::{Coin, Env, Response, Storage};
use mixnet_contract_common::{
gateway::GatewayConfigUpdate,
mixnode::{MixNodeConfigUpdate, MixNodeCostParams},
Gateway, MixNode,
};
@@ -64,4 +65,10 @@ pub trait GatewayBondingAccount {
amount: Coin,
storage: &mut dyn Storage,
) -> Result<(), ContractError>;
fn try_update_gateway_config(
&self,
new_config: GatewayConfigUpdate,
storage: &mut dyn Storage,
) -> Result<Response, ContractError>;
}
@@ -4,9 +4,12 @@ use crate::storage::MIXNET_CONTRACT_ADDRESS;
use crate::traits::GatewayBondingAccount;
use contracts_common::signing::MessageSignature;
use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128};
use mixnet_contract_common::{ExecuteMsg as MixnetExecuteMsg, Gateway};
use mixnet_contract_common::{
gateway::GatewayConfigUpdate, ExecuteMsg as MixnetExecuteMsg, Gateway,
};
use vesting_contract_common::events::{
new_vesting_gateway_bonding_event, new_vesting_gateway_unbonding_event,
new_vesting_update_gateway_config_event,
};
use super::Account;
@@ -78,4 +81,22 @@ impl GatewayBondingAccount for Account {
self.remove_gateway_pledge(storage)?;
Ok(())
}
fn try_update_gateway_config(
&self,
new_config: GatewayConfigUpdate,
storage: &mut dyn Storage,
) -> Result<Response, ContractError> {
let msg = MixnetExecuteMsg::UpdateGatewayConfigOnBehalf {
new_config,
owner: self.owner_address().into_string(),
};
let update_gateway_config_msg =
wasm_execute(MIXNET_CONTRACT_ADDRESS.load(storage)?, &msg, vec![])?;
Ok(Response::new()
.add_message(update_gateway_config_msg)
.add_event(new_vesting_update_gateway_config_event()))
}
}
+21 -13
View File
@@ -29,32 +29,40 @@ log = { workspace = true }
pretty_env_logger = "0.4.0"
rand = "0.7.3"
rocket = { version = "0.5.0-rc.2", features = ["json"] }
serde = { version="1.0", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
sysinfo = "0.27.7"
tokio = { version="1.21.2", features = ["rt-multi-thread", "net", "signal"] }
tokio-util = { version="0.7.3", features = ["codec"] }
tokio = { version = "1.21.2", features = ["rt-multi-thread", "net", "signal"] }
tokio-util = { version = "0.7.3", features = ["codec"] }
toml = "0.5.8"
url = { version = "2.2", features = ["serde"] }
atty = "0.2"
## internal
nym-config = { path="../common/config" }
nym-crypto = { path="../common/crypto" }
nym-config = { path = "../common/config" }
nym-crypto = { path = "../common/crypto" }
nym-contracts-common = { path = "../common/cosmwasm-smart-contracts/contracts-common" }
mixnet-client = { path="../common/client-libs/mixnet-client" }
mixnode-common = { path="../common/mixnode-common" }
nym-nonexhaustive-delayqueue = { path="../common/nonexhaustive-delayqueue" }
nym-sphinx = { path="../common/nymsphinx" }
mixnet-client = { path = "../common/client-libs/mixnet-client" }
mixnode-common = { path = "../common/mixnode-common" }
nym-nonexhaustive-delayqueue = { path = "../common/nonexhaustive-delayqueue" }
nym-sphinx = { path = "../common/nymsphinx" }
nym-pemstore = { path = "../common/pemstore", version = "0.2.0" }
nym-task = { path = "../common/task" }
nym-types = { path = "../common/types" }
nym-topology = { path="../common/topology" }
validator-client = { path="../common/client-libs/validator-client" }
nym-bin-common = { path="../common/bin-common" }
nym-topology = { path = "../common/topology" }
validator-client = { path = "../common/client-libs/validator-client" }
nym-bin-common = { path = "../common/bin-common" }
# chichat
chitchat = "0.5"
[dev-dependencies]
tokio = { version="1.21.2", features = ["rt-multi-thread", "net", "signal", "test-util"] }
tokio = { version = "1.21.2", features = [
"rt-multi-thread",
"net",
"signal",
"test-util",
] }
nym-sphinx-types = { path = "../common/nymsphinx/types" }
nym-sphinx-params = { path = "../common/nymsphinx/params" }
+1 -1
View File
@@ -109,5 +109,5 @@ pub(crate) async fn execute(args: &Run, output: OutputFormat) {
Select the correct version and install it to your machine. You will need to provide the following: \n ");
mixnode.print_node_details(output);
mixnode.run().await
mixnode.run().await.expect("Could not run mixnode")
}
+11 -1
View File
@@ -4,7 +4,8 @@
use crate::config::template::config_template;
use nym_config::defaults::mainnet::NYM_API;
use nym_config::defaults::{
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
DEFAULT_GOSSIP_PORT, DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT,
DEFAULT_VERLOC_LISTENING_PORT,
};
use nym_config::NymConfig;
use serde::{Deserialize, Deserializer, Serialize};
@@ -258,6 +259,10 @@ impl Config {
self.mixnode.http_api_port
}
pub fn get_gossip_port(&self) -> u16 {
self.mixnode.gossip_port
}
pub fn get_packet_forwarding_initial_backoff(&self) -> Duration {
self.debug.packet_forwarding_initial_backoff
}
@@ -348,6 +353,10 @@ struct MixNode {
#[serde(default = "default_http_api_port")]
http_api_port: u16,
/// Port used for the gossip protocol
#[serde(default = "default_http_api_port")]
gossip_port: u16,
/// Path to file containing private identity key.
#[serde(default = "missing_string_value")]
private_identity_key_file: PathBuf,
@@ -402,6 +411,7 @@ impl Default for MixNode {
mix_port: DEFAULT_MIX_LISTENING_PORT,
verloc_port: DEFAULT_VERLOC_LISTENING_PORT,
http_api_port: DEFAULT_HTTP_API_LISTENING_PORT,
gossip_port: DEFAULT_GOSSIP_PORT,
private_identity_key_file: Default::default(),
public_identity_key_file: Default::default(),
private_sphinx_key_file: Default::default(),
+28
View File
@@ -0,0 +1,28 @@
use std::sync::Arc;
use chitchat::{Chitchat, ClusterStateSnapshot, NodeId};
use rocket::serde::json::Json;
use rocket::State;
use serde::Serialize;
use tokio::sync::Mutex;
#[derive(Serialize)]
pub struct ClusterState {
cluster_id: String,
cluster_state: ClusterStateSnapshot,
live_nodes: Vec<NodeId>,
dead_nodes: Vec<NodeId>,
}
/// Returns a description of the node and why someone might want to delegate stake to it.
#[get("/state")]
pub(crate) async fn state(chitchat: &State<Arc<Mutex<Chitchat>>>) -> Json<ClusterState> {
let chitchat_guard = chitchat.lock().await;
let cluster_state = ClusterState {
cluster_id: chitchat_guard.cluster_id().to_string(),
cluster_state: chitchat_guard.state_snapshot(),
live_nodes: chitchat_guard.live_nodes().cloned().collect::<Vec<_>>(),
dead_nodes: chitchat_guard.dead_nodes().cloned().collect::<Vec<_>>(),
};
Json(cluster_state)
}
+1
View File
@@ -1,4 +1,5 @@
pub(crate) mod description;
pub(crate) mod gossip;
pub(crate) mod hardware;
pub(crate) mod stats;
pub(crate) mod verloc;
+67 -12
View File
@@ -5,6 +5,7 @@ use crate::config::persistence::pathfinder::MixNodePathfinder;
use crate::config::Config;
use crate::node::http::{
description::description,
gossip,
hardware::hardware,
not_found,
stats::stats,
@@ -17,6 +18,10 @@ use crate::node::node_description::NodeDescription;
use crate::node::node_statistics::SharedNodeStats;
use crate::node::packet_delayforwarder::{DelayForwarder, PacketDelayForwardSender};
use crate::OutputFormat;
use chitchat::transport::UdpTransport;
use chitchat::{
spawn_chitchat, Chitchat, ChitchatConfig, ChitchatHandle, FailureDetectorConfig, NodeId,
};
use log::{error, info, warn};
use mixnode_common::verloc::{self, AtomicVerlocResult, VerlocMeasurer};
use nym_bin_common::version_checker::parse_version;
@@ -28,6 +33,8 @@ use rand::thread_rng;
use std::net::SocketAddr;
use std::process;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::Mutex;
mod http;
mod listener;
@@ -110,6 +117,7 @@ impl MixNode {
&self,
atomic_verloc_result: AtomicVerlocResult,
node_stats_pointer: SharedNodeStats,
chitchat: Arc<Mutex<Chitchat>>,
) {
info!("Starting HTTP API on http://localhost:8000");
@@ -125,11 +133,15 @@ impl MixNode {
tokio::spawn(async move {
rocket::build()
.configure(config)
.mount("/", routes![verlocRoute, description, stats, hardware])
.mount(
"/",
routes![verlocRoute, description, stats, hardware, gossip::state],
)
.register("/", catchers![not_found])
.manage(verloc_state)
.manage(descriptor)
.manage(node_stats_pointer)
.manage(chitchat)
.launch()
.await
});
@@ -199,7 +211,11 @@ impl MixNode {
packet_sender
}
fn start_verloc_measurements(&self, shutdown: TaskClient) -> AtomicVerlocResult {
fn start_verloc_measurements(
&self,
chitchat: Arc<Mutex<Chitchat>>,
shutdown: TaskClient,
) -> AtomicVerlocResult {
info!("Starting the round-trip-time measurer...");
// this is a sanity check to make sure we didn't mess up with the minimum version at some point
@@ -235,7 +251,7 @@ impl MixNode {
let mut verloc_measurer =
VerlocMeasurer::new(config, Arc::clone(&self.identity_keypair), shutdown);
let atomic_verloc_results = verloc_measurer.get_verloc_results_pointer();
tokio::spawn(async move { verloc_measurer.run().await });
tokio::spawn(async move { verloc_measurer.run(chitchat).await });
atomic_verloc_results
}
@@ -277,18 +293,53 @@ impl MixNode {
log::info!("Stopping nym mixnode");
}
pub async fn run(&mut self) {
fn gossip_node_id(&self) -> NodeId {
NodeId::new(
self.identity_keypair.public_key().to_string(),
(
self.config.get_listening_address(),
self.config.get_gossip_port(),
)
.into(),
)
}
fn gossip_config(&self) -> ChitchatConfig {
ChitchatConfig {
node_id: self.gossip_node_id(),
cluster_id: "hive_mind".to_string(),
gossip_interval: Duration::from_millis(500),
listen_addr: (
self.config.get_listening_address(),
self.config.get_gossip_port(),
)
.into(),
// We'd probably wanna get this from the nym_api, or have a few known good nodes, the ones we run probably
seed_nodes: vec![],
failure_detector_config: FailureDetectorConfig::default(),
is_ready_predicate: None,
}
}
pub async fn init_chitchat(&self) -> Result<ChitchatHandle, String> {
let chitchat_handler = spawn_chitchat(self.gossip_config(), Vec::new(), &UdpTransport)
.await
.map_err(|_| "Could not spawn chitchat")?;
Ok(chitchat_handler)
}
pub async fn run(&mut self) -> Result<(), String> {
info!("Starting nym mixnode");
if let Some(duplicate_node_key) = self.check_if_same_ip_node_exists().await {
if duplicate_node_key == self.identity_keypair.public_key().to_base58_string() {
warn!("You seem to have bonded your mixnode before starting it - that's highly unrecommended as in the future it might result in slashing");
} else {
log::error!(
"Our announce-host is identical to an existing node's announce-host! (its key is {:?})",
duplicate_node_key
);
return;
let err = format!("Our announce-host is identical to an existing node's announce-host! (its key is {:?})",
duplicate_node_key);
log::error!("{err}");
return Err(err);
}
}
@@ -303,14 +354,18 @@ impl MixNode {
delay_forwarding_channel,
shutdown.subscribe(),
);
let atomic_verloc_results = self.start_verloc_measurements(shutdown.subscribe());
let chitchat_handler = self.init_chitchat().await?;
let chitchat = chitchat_handler.chitchat();
let atomic_verloc_results =
self.start_verloc_measurements(chitchat.clone(), shutdown.subscribe());
// Rocket handles shutdown on it's own, but its shutdown handling should be incorporated
// with that of the rest of the tasks.
// Currently it's runtime is forcefully terminated once the mixnode exits.
self.start_http_api(atomic_verloc_results, node_stats_pointer);
self.start_http_api(atomic_verloc_results, node_stats_pointer, chitchat);
info!("Finished nym mixnode startup procedure - it should now be able to receive mix traffic!");
self.wait_for_interrupt(shutdown).await
self.wait_for_interrupt(shutdown).await;
Ok(())
}
}
+1
View File
@@ -63,6 +63,7 @@ fn main() {
mixnet::bond::unbond_mixnode,
mixnet::bond::update_mixnode_cost_params,
mixnet::bond::update_mixnode_config,
mixnet::bond::update_gateway_config,
mixnet::bond::get_number_of_mixnode_delegators,
mixnet::bond::get_mix_node_description,
mixnet::bond::get_mixnode_avg_uptime,
@@ -8,6 +8,7 @@ use crate::operations::helpers::{
use crate::state::WalletState;
use crate::{nyxd_client, Gateway, MixNode};
use nym_contracts_common::signing::MessageSignature;
use nym_mixnet_contract_common::gateway::GatewayConfigUpdate;
use nym_mixnet_contract_common::{MixId, MixNodeConfigUpdate};
use nym_types::currency::DecCoin;
use nym_types::gateway::GatewayBond;
@@ -227,6 +228,31 @@ pub async fn update_mixnode_config(
)?)
}
#[tauri::command]
pub async fn update_gateway_config(
update: GatewayConfigUpdate,
fee: Option<Fee>,
state: tauri::State<'_, WalletState>,
) -> Result<TransactionExecuteResult, BackendError> {
let guard = state.read().await;
let fee_amount = guard.convert_tx_fee(fee.as_ref());
log::info!(
">>> Update gateway config: update = {}, fee {:?}",
update.to_inline_json(),
fee,
);
let res = guard
.current_client()?
.nyxd
.update_gateway_config(update, fee)
.await?;
log::info!("<<< tx hash = {}", res.transaction_hash);
log::trace!("<<< {:?}", res);
Ok(TransactionExecuteResult::from_execute_result(
res, fee_amount,
)?)
}
#[tauri::command]
pub async fn get_mixnode_avg_uptime(
state: tauri::State<'_, WalletState>,
+4 -2
View File
@@ -5,8 +5,9 @@ use nym_api_requests::models::{
};
use nym_mixnet_contract_common::rewarding::RewardEstimate;
use nym_mixnet_contract_common::{
Interval as ContractInterval, IntervalRewardParams, IntervalRewardingParamsUpdate, MixNode,
MixNodeConfigUpdate, RewardedSetNodeStatus, RewardingParams, UnbondedMixnode,
GatewayConfigUpdate, Interval as ContractInterval, IntervalRewardParams,
IntervalRewardingParamsUpdate, MixNode, MixNodeConfigUpdate, RewardedSetNodeStatus,
RewardingParams, UnbondedMixnode,
};
use nym_types::account::{Account, AccountEntry, AccountWithMnemonic, Balance};
use nym_types::currency::{CurrencyDenom, DecCoin};
@@ -90,6 +91,7 @@ fn main() {
do_export!(Gas);
do_export!(GasInfo);
do_export!(Gateway);
do_export!(GatewayConfigUpdate);
do_export!(GatewayBond);
do_export!(CurrencyDenom);
do_export!(DecCoin);
@@ -0,0 +1,9 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export interface GatewayConfigUpdate {
host: string;
mix_port: number;
clients_port: number;
location: string;
version: string;
}