From 4e7ff53214fc500c31d7ecb7eecf4cf753d1f84a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Thu, 16 Mar 2023 13:43:56 +0100 Subject: [PATCH] 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 --- .../src/nyxd/traits/mixnet_signing_client.rs | 31 ++++ .../src/nyxd/traits/vesting_signing_client.rs | 20 +++ .../validator/mixnet/operators/gateway/mod.rs | 3 + .../mixnet/operators/gateway/settings/mod.rs | 22 +++ .../gateway/settings/update_config.rs | 60 +++++++ .../gateway/settings/vesting_update_config.rs | 61 +++++++ .../mixnet-contract/src/events.rs | 15 ++ .../mixnet-contract/src/gateway.rs | 20 +++ .../mixnet-contract/src/lib.rs | 3 +- .../mixnet-contract/src/msg.rs | 12 ++ .../vesting-contract/src/events.rs | 5 + .../vesting-contract/src/messages.rs | 5 + contracts/mixnet/src/contract.rs | 8 + contracts/mixnet/src/gateways/helpers.rs | 20 +++ contracts/mixnet/src/gateways/mod.rs | 1 + contracts/mixnet/src/gateways/transactions.rs | 150 +++++++++++++++++- contracts/mixnet/src/mixnodes/transactions.rs | 4 +- contracts/mixnet/src/support/tests/mod.rs | 2 +- contracts/vesting/src/contract.rs | 13 ++ .../vesting/src/traits/bonding_account.rs | 7 + .../account/gateway_bonding_account.rs | 23 ++- nym-wallet/src-tauri/src/main.rs | 1 + .../src-tauri/src/operations/mixnet/bond.rs | 26 +++ tools/ts-rs-cli/src/main.rs | 6 +- .../src/types/rust/GatewayConfigUpdate.ts | 9 ++ 25 files changed, 518 insertions(+), 9 deletions(-) create mode 100644 common/commands/src/validator/mixnet/operators/gateway/settings/mod.rs create mode 100644 common/commands/src/validator/mixnet/operators/gateway/settings/update_config.rs create mode 100644 common/commands/src/validator/mixnet/operators/gateway/settings/vesting_update_config.rs create mode 100644 contracts/mixnet/src/gateways/helpers.rs create mode 100644 ts-packages/types/src/types/rust/GatewayConfigUpdate.ts diff --git a/common/client-libs/validator-client/src/nyxd/traits/mixnet_signing_client.rs b/common/client-libs/validator-client/src/nyxd/traits/mixnet_signing_client.rs index 0f49dcc622..159835f5a7 100644 --- a/common/client-libs/validator-client/src/nyxd/traits/mixnet_signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/traits/mixnet_signing_client.rs @@ -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, + ) -> Result { + 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, + ) -> Result { + self.execute_mixnet_contract( + fee, + MixnetExecuteMsg::UpdateGatewayConfigOnBehalf { + new_config, + owner: owner.to_string(), + }, + vec![], + ) + .await + } + // delegation-related: async fn delegate_to_mixnode( diff --git a/common/client-libs/validator-client/src/nyxd/traits/vesting_signing_client.rs b/common/client-libs/validator-client/src/nyxd/traits/vesting_signing_client.rs index fec9c19c04..296b3219dc 100644 --- a/common/client-libs/validator-client/src/nyxd/traits/vesting_signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/traits/vesting_signing_client.rs @@ -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, ) -> Result; + async fn vesting_update_gateway_config( + &self, + new_config: GatewayConfigUpdate, + fee: Option, + ) -> Result; + async fn update_mixnet_address( &self, address: &str, @@ -185,6 +192,19 @@ impl VestingSigningClient for Ny .await } + async fn vesting_update_gateway_config( + &self, + new_config: GatewayConfigUpdate, + fee: Option, + ) -> Result { + self.execute_vesting_contract( + fee, + VestingExecuteMsg::UpdateGatewayConfig { new_config }, + vec![], + ) + .await + } + async fn update_mixnet_address( &self, address: &str, diff --git a/common/commands/src/validator/mixnet/operators/gateway/mod.rs b/common/commands/src/validator/mixnet/operators/gateway/mod.rs index 1cdd67520b..8c4b8753c3 100644 --- a/common/commands/src/validator/mixnet/operators/gateway/mod.rs +++ b/common/commands/src/validator/mixnet/operators/gateway/mod.rs @@ -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 diff --git a/common/commands/src/validator/mixnet/operators/gateway/settings/mod.rs b/common/commands/src/validator/mixnet/operators/gateway/settings/mod.rs new file mode 100644 index 0000000000..d5b96c8ed2 --- /dev/null +++ b/common/commands/src/validator/mixnet/operators/gateway/settings/mod.rs @@ -0,0 +1,22 @@ +// Copyright 2023 - Nym Technologies SA +// 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), +} diff --git a/common/commands/src/validator/mixnet/operators/gateway/settings/update_config.rs b/common/commands/src/validator/mixnet/operators/gateway/settings/update_config.rs new file mode 100644 index 0000000000..b88d0c79a6 --- /dev/null +++ b/common/commands/src/validator/mixnet/operators/gateway/settings/update_config.rs @@ -0,0 +1,60 @@ +// Copyright 2023 - Nym Technologies SA +// 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, + + #[clap(long)] + pub mix_port: Option, + + #[clap(long)] + pub clients_port: Option, + + #[clap(long)] + pub location: Option, + + #[clap(long)] + pub version: Option, +} + +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) +} diff --git a/common/commands/src/validator/mixnet/operators/gateway/settings/vesting_update_config.rs b/common/commands/src/validator/mixnet/operators/gateway/settings/vesting_update_config.rs new file mode 100644 index 0000000000..50c25b301d --- /dev/null +++ b/common/commands/src/validator/mixnet/operators/gateway/settings/vesting_update_config.rs @@ -0,0 +1,61 @@ +// Copyright 2023 - Nym Technologies SA +// 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, + + #[clap(long)] + pub mix_port: Option, + + #[clap(long)] + pub clients_port: Option, + + #[clap(long)] + pub location: Option, + + #[clap(long)] + pub version: Option, +} + +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) +} diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/events.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/events.rs index 98ecb8a1b7..e61e26adb2 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/events.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/events.rs @@ -1,6 +1,7 @@ // Copyright 2022 - Nym Technologies SA // 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 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, + 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, diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/gateway.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/gateway.rs index ebb5464064..3ba030a5b3 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/gateway.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/gateway.rs @@ -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, diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/lib.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/lib.rs index 738aeb6daa..3079d03640 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/lib.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/lib.rs @@ -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, diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs index 8376259bfc..83fa00be2f 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs @@ -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") diff --git a/common/cosmwasm-smart-contracts/vesting-contract/src/events.rs b/common/cosmwasm-smart-contracts/vesting-contract/src/events.rs index 4e5809beee..e46cd67159 100644 --- a/common/cosmwasm-smart-contracts/vesting-contract/src/events.rs +++ b/common/cosmwasm-smart-contracts/vesting-contract/src/events.rs @@ -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) } diff --git a/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs b/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs index 39b5e74187..d55e9a0fa7 100644 --- a/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs +++ b/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs @@ -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", diff --git a/contracts/mixnet/src/contract.rs b/contracts/mixnet/src/contract.rs index 623e9c0a1c..181f70a82f 100644 --- a/contracts/mixnet/src/contract.rs +++ b/contracts/mixnet/src/contract.rs @@ -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 } => { diff --git a/contracts/mixnet/src/gateways/helpers.rs b/contracts/mixnet/src/gateways/helpers.rs new file mode 100644 index 0000000000..8a450bd3cd --- /dev/null +++ b/contracts/mixnet/src/gateways/helpers.rs @@ -0,0 +1,20 @@ +// Copyright 2023 - Nym Technologies SA +// 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 { + Ok(storage::gateways() + .idx + .owner + .item(store, owner.clone())? + .ok_or(MixnetContractError::NoAssociatedGatewayBond { + owner: owner.clone(), + })? + .1) +} diff --git a/contracts/mixnet/src/gateways/mod.rs b/contracts/mixnet/src/gateways/mod.rs index d76ed62ced..ebff4a6ace 100644 --- a/contracts/mixnet/src/gateways/mod.rs +++ b/contracts/mixnet/src/gateways/mod.rs @@ -1,6 +1,7 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +pub mod helpers; pub mod queries; pub mod signature_helpers; pub mod storage; diff --git a/contracts/mixnet/src/gateways/transactions.rs b/contracts/mixnet/src/gateways/transactions.rs index dc32d6ce4d..193fe99252 100644 --- a/contracts/mixnet/src/gateways/transactions.rs +++ b/contracts/mixnet/src/gateways/transactions.rs @@ -1,16 +1,20 @@ // Copyright 2021-2022 - Nym Technologies SA // 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 { + 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 { + 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, +) -> Result { + 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 + } + ) + } } diff --git a/contracts/mixnet/src/mixnodes/transactions.rs b/contracts/mixnet/src/mixnodes/transactions.rs index 6ce2416851..f2bfa89941 100644 --- a/contracts/mixnet/src/mixnodes/transactions.rs +++ b/contracts/mixnet/src/mixnodes/transactions.rs @@ -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, diff --git a/contracts/mixnet/src/support/tests/mod.rs b/contracts/mixnet/src/support/tests/mod.rs index 71a2d96437..e693b6b1f4 100644 --- a/contracts/mixnet/src/support/tests/mod.rs +++ b/contracts/mixnet/src/support/tests/mod.rs @@ -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, diff --git a/contracts/vesting/src/contract.rs b/contracts/vesting/src/contract.rs index cc6e59aa29..9454a2c34e 100644 --- a/contracts/vesting/src/contract.rs +++ b/contracts/vesting/src/contract.rs @@ -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 { + 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, diff --git a/contracts/vesting/src/traits/bonding_account.rs b/contracts/vesting/src/traits/bonding_account.rs index 242620a3be..402b156924 100644 --- a/contracts/vesting/src/traits/bonding_account.rs +++ b/contracts/vesting/src/traits/bonding_account.rs @@ -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; } diff --git a/contracts/vesting/src/vesting/account/gateway_bonding_account.rs b/contracts/vesting/src/vesting/account/gateway_bonding_account.rs index 72f83405b3..12910acbcc 100644 --- a/contracts/vesting/src/vesting/account/gateway_bonding_account.rs +++ b/contracts/vesting/src/vesting/account/gateway_bonding_account.rs @@ -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 { + 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())) + } } diff --git a/nym-wallet/src-tauri/src/main.rs b/nym-wallet/src-tauri/src/main.rs index cda784a8b7..0493bc1493 100644 --- a/nym-wallet/src-tauri/src/main.rs +++ b/nym-wallet/src-tauri/src/main.rs @@ -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, diff --git a/nym-wallet/src-tauri/src/operations/mixnet/bond.rs b/nym-wallet/src-tauri/src/operations/mixnet/bond.rs index 9884c88080..fcfdf9868d 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/bond.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/bond.rs @@ -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, + state: tauri::State<'_, WalletState>, +) -> Result { + 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>, diff --git a/tools/ts-rs-cli/src/main.rs b/tools/ts-rs-cli/src/main.rs index 9b6ea3f2c2..8bfdf074eb 100644 --- a/tools/ts-rs-cli/src/main.rs +++ b/tools/ts-rs-cli/src/main.rs @@ -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); diff --git a/ts-packages/types/src/types/rust/GatewayConfigUpdate.ts b/ts-packages/types/src/types/rust/GatewayConfigUpdate.ts new file mode 100644 index 0000000000..acd40d4421 --- /dev/null +++ b/ts-packages/types/src/types/rust/GatewayConfigUpdate.ts @@ -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; +}