Compare commits

..

7 Commits

Author SHA1 Message Date
Jędrzej Stuczyński 2f35db45c6 fixed vesting contract tests 2024-07-15 09:16:48 +01:00
Jędrzej Stuczyński c35c9fe173 fixed wallet vesting-related tests 2024-07-12 16:39:32 +01:00
Jędrzej Stuczyński eb8a4c0efc updated contract schema 2024-07-12 16:36:55 +01:00
Jędrzej Stuczyński 8533f15213 exposed migration commands to nym-cli + clippy 2024-07-12 16:26:02 +01:00
Jędrzej Stuczyński 32dd987e12 implemented migration into non-vesting mixnodes/delegations 2024-07-12 15:26:50 +01:00
Jędrzej Stuczyński 1287aa2a6c ensure no pending proxy events when migrating 2024-07-11 16:48:59 +01:00
Jędrzej Stuczyński 70c68796d3 removed all on_behalf mixnet contract methods 2024-07-11 16:48:58 +01:00
101 changed files with 2312 additions and 4573 deletions
Generated
-17
View File
@@ -4999,23 +4999,6 @@ dependencies = [
"zeroize",
]
[[package]]
name = "nym-network-statistics"
version = "1.1.34"
dependencies = [
"dirs 4.0.0",
"log",
"nym-bin-common",
"nym-statistics-common",
"nym-task",
"pretty_env_logger",
"rocket",
"serde",
"sqlx",
"thiserror",
"tokio",
]
[[package]]
name = "nym-node"
version = "1.1.4"
+1 -1
View File
@@ -52,7 +52,7 @@ References for developers:
You can chat to us in two places:
* The #dev channel on [Matrix](https://matrix.to/#/#dev:nymtech.chat)
* The various developer channels on [Discord](https://discord.gg/nym)
* The various developer channels on [Discord](https://discord.gg/nymproject)
### Tokenomics & Rewards
@@ -683,6 +683,24 @@ pub trait MixnetSigningClient {
.await
}
async fn migrate_vested_mixnode(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError> {
self.execute_mixnet_contract(fee, MixnetExecuteMsg::MigrateVestedMixNode {}, vec![])
.await
}
async fn migrate_vested_delegation(
&self,
mix_id: MixId,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_mixnet_contract(
fee,
MixnetExecuteMsg::MigrateVestedDelegation { mix_id },
vec![],
)
.await
}
#[cfg(feature = "contract-testing")]
async fn testing_resolve_all_pending_events(
&self,
@@ -928,6 +946,12 @@ mod tests {
MixnetExecuteMsg::WithdrawDelegatorRewardOnBehalf { mix_id, owner } => client
.withdraw_delegator_reward_on_behalf(owner.parse().unwrap(), mix_id, None)
.ignore(),
MixnetExecuteMsg::MigrateVestedMixNode { .. } => {
client.migrate_vested_mixnode(None).ignore()
}
MixnetExecuteMsg::MigrateVestedDelegation { mix_id } => {
client.migrate_vested_delegation(mix_id, None).ignore()
}
#[cfg(feature = "contract-testing")]
MixnetExecuteMsg::TestingResolveAllPendingEvents { .. } => {
@@ -437,6 +437,7 @@ where
mod tests {
use super::*;
use crate::nyxd::contract_traits::tests::{mock_coin, IgnoreValue};
use nym_vesting_contract_common::ExecuteMsg;
// it's enough that this compiles and clippy is happy about it
#[allow(dead_code)]
@@ -560,6 +561,9 @@ mod tests {
VestingExecuteMsg::UpdateLockedPledgeCap { address, cap } => client
.update_locked_pledge_cap(address.parse().unwrap(), cap, None)
.ignore(),
// those will never be manually called by clients
ExecuteMsg::TrackMigratedMixnode { .. } => "explicitly_ignored".ignore(),
ExecuteMsg::TrackMigratedDelegation { .. } => "explicitly_ignored".ignore(),
};
}
}
@@ -0,0 +1,42 @@
// Copyright 2024 - 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::MixId;
use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient};
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub mix_id: Option<MixId>,
#[clap(long)]
pub identity_key: Option<String>,
}
pub async fn migrate_vested_delegation(args: Args, client: SigningClient) {
let mix_id = match args.mix_id {
Some(mix_id) => mix_id,
None => {
let identity_key = args
.identity_key
.expect("either mix_id or mix_identity has to be specified");
let node_details = client
.get_mixnode_details_by_identity(identity_key)
.await
.expect("contract query failed")
.mixnode_details
.expect("mixnode with the specified identity doesnt exist");
node_details.mix_id()
}
};
let res = client
.migrate_vested_delegation(mix_id, None)
.await
.expect("failed to migrate delegation!");
info!("migration result: {:?}", res)
}
@@ -7,6 +7,7 @@ pub mod rewards;
pub mod delegate_to_mixnode;
pub mod delegate_to_multiple_mixnodes;
pub mod migrate_vested_delegation;
pub mod query_for_delegations;
pub mod undelegate_from_mixnode;
pub mod vesting_delegate_to_mixnode;
@@ -35,4 +36,6 @@ pub enum MixnetDelegatorsCommands {
DelegateVesting(vesting_delegate_to_mixnode::Args),
/// Undelegate from a mixnode (when originally using locked tokens)
UndelegateVesting(vesting_undelegate_from_mixnode::Args),
/// Migrate the delegation to use liquid tokens
MigrateVestedDelegation(migrate_vested_delegation::Args),
}
@@ -96,6 +96,7 @@ async fn print_delegation_events(events: Vec<PendingEpochEvent>, client: &Signin
mix_id,
amount,
proxy,
..
} => {
if owner.as_str() == client.nyxd.address().as_ref() {
table.add_row(vec![
@@ -111,6 +112,7 @@ async fn print_delegation_events(events: Vec<PendingEpochEvent>, client: &Signin
owner,
mix_id,
proxy,
..
} => {
if owner.as_str() == client.nyxd.address().as_ref() {
table.add_row(vec![
@@ -8,7 +8,7 @@ use cosmwasm_std::Coin;
use nym_bin_common::output_format::OutputFormat;
use nym_mixnet_contract_common::construct_gateway_bonding_sign_payload;
use nym_network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT};
use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, NymContractsProvider};
use nym_validator_client::nyxd::contract_traits::MixnetQueryClient;
#[derive(Debug, Parser)]
pub struct Args {
@@ -39,10 +39,6 @@ pub struct Args {
)]
pub amount: u128,
/// Indicates whether the gateway is going to get bonded via a vesting account
#[arg(long)]
pub with_vesting_account: bool,
#[clap(short, long, default_value_t = OutputFormat::default())]
output: OutputFormat,
}
@@ -74,15 +70,8 @@ pub async fn create_payload(args: Args, client: SigningClient) {
};
let address = account_id_to_cw_addr(&client.address());
let proxy = if args.with_vesting_account {
Some(account_id_to_cw_addr(
client.vesting_contract_address().unwrap(),
))
} else {
None
};
let payload = construct_gateway_bonding_sign_payload(nonce, address, proxy, coin, gateway);
let payload = construct_gateway_bonding_sign_payload(nonce, address, coin, gateway);
let wrapper = DataWrapper::new(payload.to_base58_string().unwrap());
println!("{}", args.output.format(&wrapper))
}
@@ -5,33 +5,21 @@ use crate::context::SigningClient;
use clap::Parser;
use log::info;
use nym_validator_client::nyxd::contract_traits::MixnetSigningClient;
use nym_validator_client::nyxd::contract_traits::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")
};
let res = client
.create_family(args.family_label, None)
.await
.expect("failed to create family");
info!("Family creation result: {:?}", res);
}
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::context::QueryClient;
use crate::utils::{account_id_to_cw_addr, DataWrapper};
use crate::utils::DataWrapper;
use clap::Parser;
use cosmrs::AccountId;
use log::info;
@@ -10,7 +10,7 @@ use nym_bin_common::output_format::OutputFormat;
use nym_crypto::asymmetric::identity;
use nym_mixnet_contract_common::construct_family_join_permit;
use nym_mixnet_contract_common::families::FamilyHead;
use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, NymContractsProvider};
use nym_validator_client::nyxd::contract_traits::MixnetQueryClient;
#[derive(Debug, Parser)]
pub struct Args {
@@ -18,10 +18,6 @@ pub struct Args {
#[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)]
@@ -68,18 +64,9 @@ pub async fn create_family_join_permit_sign_payload(args: Args, client: QueryCli
}
};
// 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().unwrap(),
))
} else {
None
};
let head = FamilyHead::new(mixnode.bond_information.identity());
let payload = construct_family_join_permit(nonce, head, proxy, args.member.to_base58_string());
let payload = construct_family_join_permit(nonce, head, args.member.to_base58_string());
let wrapper = DataWrapper::new(payload.to_base58_string().unwrap());
println!("{}", args.output.format(&wrapper))
}
@@ -8,7 +8,6 @@ use nym_contracts_common::signing::MessageSignature;
use nym_crypto::asymmetric::identity;
use nym_mixnet_contract_common::families::FamilyHead;
use nym_validator_client::nyxd::contract_traits::MixnetSigningClient;
use nym_validator_client::nyxd::contract_traits::VestingSigningClient;
#[derive(Debug, Parser)]
pub struct Args {
@@ -16,10 +15,6 @@ pub struct Args {
#[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,
@@ -30,17 +25,10 @@ pub async fn join_family(args: Args, client: SigningClient) {
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")
};
let res = client
.join_family(args.join_permit, family_head, None)
.await
.expect("failed to join family");
info!("Family join result: {:?}", res);
}
@@ -7,17 +7,12 @@ use log::info;
use nym_crypto::asymmetric::identity;
use nym_mixnet_contract_common::families::FamilyHead;
use nym_validator_client::nyxd::contract_traits::MixnetSigningClient;
use nym_validator_client::nyxd::contract_traits::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) {
@@ -25,17 +20,10 @@ pub async fn leave_family(args: Args, client: SigningClient) {
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")
};
let res = client
.leave_family(family_head, None)
.await
.expect("failed to leave family");
info!("Family leave result: {:?}", res);
}
@@ -0,0 +1,19 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
use nym_validator_client::nyxd::contract_traits::MixnetSigningClient;
#[derive(Debug, Parser)]
pub struct Args {}
pub async fn migrate_vested_mixnode(_args: Args, client: SigningClient) {
let res = client
.migrate_vested_mixnode(None)
.await
.expect("failed to migrate mixnode!");
info!("migration result: {:?}", res)
}
@@ -11,7 +11,7 @@ use nym_mixnet_contract_common::{construct_mixnode_bonding_sign_payload, MixNode
use nym_network_defaults::{
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
};
use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, NymContractsProvider};
use nym_validator_client::nyxd::contract_traits::MixnetQueryClient;
use nym_validator_client::nyxd::CosmWasmCoin;
#[derive(Debug, Parser)]
@@ -52,10 +52,6 @@ pub struct Args {
)]
pub amount: u128,
/// Indicates whether the mixnode is going to get bonded via a vesting account
#[arg(long)]
pub with_vesting_account: bool,
#[clap(short, long, default_value_t = OutputFormat::default())]
output: OutputFormat,
}
@@ -100,16 +96,9 @@ pub async fn create_payload(args: Args, client: SigningClient) {
};
let address = account_id_to_cw_addr(&client.address());
let proxy = if args.with_vesting_account {
Some(account_id_to_cw_addr(
client.vesting_contract_address().unwrap(),
))
} else {
None
};
let payload =
construct_mixnode_bonding_sign_payload(nonce, address, proxy, coin, mixnode, cost_params);
construct_mixnode_bonding_sign_payload(nonce, address, coin, mixnode, cost_params);
let wrapper = DataWrapper::new(payload.to_base58_string().unwrap());
println!("{}", args.output.format(&wrapper))
}
@@ -7,6 +7,7 @@ pub mod bond_mixnode;
pub mod decrease_pledge;
pub mod families;
pub mod keys;
pub mod migrate_vested_mixnode;
pub mod mixnode_bonding_sign_payload;
pub mod pledge_more;
pub mod rewards;
@@ -52,4 +53,6 @@ pub enum MixnetOperatorsMixnodeCommands {
DecreasePledge(decrease_pledge::Args),
/// Decrease pledge with locked tokens
DecreasePledgeVesting(vesting_decrease_pledge::Args),
/// Migrate the mixnode to use liquid tokens
MigrateVestedNode(migrate_vested_mixnode::Args),
}
@@ -218,7 +218,6 @@ where
#[derive(Serialize)]
pub struct ContractMessageContent<T> {
pub sender: Addr,
pub proxy: Option<Addr>,
pub funds: Vec<Coin>,
pub data: T,
}
@@ -233,25 +232,17 @@ where
}
impl<T> ContractMessageContent<T> {
pub fn new(sender: Addr, proxy: Option<Addr>, funds: Vec<Coin>, data: T) -> Self {
pub fn new(sender: Addr, funds: Vec<Coin>, data: T) -> Self {
ContractMessageContent {
sender,
proxy,
funds,
data,
}
}
pub fn new_with_info(info: MessageInfo, signer: Addr, data: T) -> Self {
let proxy = if info.sender == signer {
None
} else {
Some(info.sender)
};
ContractMessageContent {
sender: signer,
proxy,
funds: info.funds,
data,
}
@@ -65,7 +65,6 @@ impl Delegation {
cumulative_reward_ratio: Decimal,
amount: Coin,
height: u64,
proxy: Option<Addr>,
) -> Self {
assert!(
amount.amount <= TOKEN_SUPPLY,
@@ -78,7 +77,7 @@ impl Delegation {
cumulative_reward_ratio,
amount,
height,
proxy,
proxy: None,
}
}
@@ -76,21 +76,11 @@ pub enum MixnetContractError {
#[error("Received multiple coin types during staking")]
MultipleDenoms,
#[error("Proxy address mismatch, expected {existing}, got {incoming}")]
ProxyMismatch { existing: String, incoming: String },
#[error("Proxy address ({received}) is not set to the vesting contract ({vesting_contract})")]
ProxyIsNotVestingContract {
received: Addr,
vesting_contract: Addr,
},
#[error(
"Sender of this message ({received}) is not the vesting contract ({vesting_contract})"
)]
SenderIsNotVestingContract {
received: Addr,
vesting_contract: Addr,
},
#[error("Failed to recover ed25519 public key from its base58 representation - {0}")]
MalformedEd25519IdentityKey(String),
@@ -239,6 +229,17 @@ pub enum MixnetContractError {
#[from]
source: ApiVerifierError,
},
#[error("this operation is no longer allowed to be performed with vesting tokens. please move them to your liquid balance and try again")]
DisabledVestingOperation,
#[error(
"this mixnode has not been bonded with the vesting tokens or has already been migrated"
)]
NotAVestingMixnode,
#[error("this delegation has not been performed with the vesting tokens or has already been migrated")]
NotAVestingDelegation,
}
impl MixnetContractError {
@@ -103,7 +103,6 @@ impl Display for MixnetEventType {
// attributes that are used in multiple places
pub const OWNER_KEY: &str = "owner";
pub const AMOUNT_KEY: &str = "amount";
pub const PROXY_KEY: &str = "proxy";
// event-specific attributes
@@ -163,7 +162,6 @@ pub const NEW_EPOCHS_IN_INTERVAL: &str = "new_epochs_in_interval";
pub fn new_delegation_event(
created_at: BlockHeight,
delegator: &Addr,
proxy: &Option<Addr>,
amount: &Coin,
mix_id: MixId,
unit_reward: Decimal,
@@ -171,58 +169,34 @@ pub fn new_delegation_event(
Event::new(MixnetEventType::Delegation)
.add_attribute(EVENT_CREATION_HEIGHT_KEY, created_at.to_string())
.add_attribute(DELEGATOR_KEY, delegator)
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
.add_attribute(AMOUNT_KEY, amount.to_string())
.add_attribute(DELEGATION_TARGET_KEY, mix_id.to_string())
.add_attribute(UNIT_REWARD_KEY, unit_reward.to_string())
}
pub fn new_delegation_on_unbonded_node_event(
delegator: &Addr,
proxy: &Option<Addr>,
mix_id: MixId,
) -> Event {
pub fn new_delegation_on_unbonded_node_event(delegator: &Addr, mix_id: MixId) -> Event {
Event::new(MixnetEventType::Delegation)
.add_attribute(DELEGATOR_KEY, delegator)
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
.add_attribute(DELEGATION_TARGET_KEY, mix_id.to_string())
}
pub fn new_pending_delegation_event(
delegator: &Addr,
proxy: &Option<Addr>,
amount: &Coin,
mix_id: MixId,
) -> Event {
pub fn new_pending_delegation_event(delegator: &Addr, amount: &Coin, mix_id: MixId) -> Event {
Event::new(MixnetEventType::PendingDelegation)
.add_attribute(DELEGATOR_KEY, delegator)
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
.add_attribute(AMOUNT_KEY, amount.to_string())
.add_attribute(DELEGATION_TARGET_KEY, mix_id.to_string())
}
pub fn new_withdraw_operator_reward_event(
owner: &Addr,
proxy: &Option<Addr>,
amount: Coin,
mix_id: MixId,
) -> Event {
pub fn new_withdraw_operator_reward_event(owner: &Addr, amount: Coin, mix_id: MixId) -> Event {
Event::new(MixnetEventType::WithdrawOperatorReward)
.add_attribute(OWNER_KEY, owner.as_str())
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
.add_attribute(AMOUNT_KEY, amount.to_string())
.add_attribute(MIX_ID_KEY, mix_id.to_string())
}
pub fn new_withdraw_delegator_reward_event(
delegator: &Addr,
proxy: &Option<Addr>,
amount: Coin,
mix_id: MixId,
) -> Event {
pub fn new_withdraw_delegator_reward_event(delegator: &Addr, amount: Coin, mix_id: MixId) -> Event {
Event::new(MixnetEventType::WithdrawDelegatorReward)
.add_attribute(DELEGATOR_KEY, delegator)
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
.add_attribute(AMOUNT_KEY, amount.to_string())
.add_attribute(DELEGATION_TARGET_KEY, mix_id.to_string())
}
@@ -278,59 +252,43 @@ pub fn new_pending_rewarding_params_update_event(
)
}
pub fn new_undelegation_event(
created_at: BlockHeight,
delegator: &Addr,
proxy: &Option<Addr>,
mix_id: MixId,
) -> Event {
pub fn new_undelegation_event(created_at: BlockHeight, delegator: &Addr, mix_id: MixId) -> Event {
Event::new(MixnetEventType::Undelegation)
.add_attribute(EVENT_CREATION_HEIGHT_KEY, created_at.to_string())
.add_attribute(DELEGATOR_KEY, delegator)
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
.add_attribute(MIX_ID_KEY, mix_id.to_string())
}
pub fn new_pending_undelegation_event(
delegator: &Addr,
proxy: &Option<Addr>,
mix_id: MixId,
) -> Event {
pub fn new_pending_undelegation_event(delegator: &Addr, mix_id: MixId) -> Event {
Event::new(MixnetEventType::PendingUndelegation)
.add_attribute(DELEGATOR_KEY, delegator)
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
.add_attribute(MIX_ID_KEY, mix_id.to_string())
}
pub fn new_gateway_bonding_event(
owner: &Addr,
proxy: &Option<Addr>,
amount: &Coin,
identity: IdentityKeyRef<'_>,
) -> Event {
Event::new(MixnetEventType::GatewayBonding)
.add_attribute(OWNER_KEY, owner)
.add_attribute(NODE_IDENTITY_KEY, identity)
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
.add_attribute(AMOUNT_KEY, amount.to_string())
}
pub fn new_gateway_unbonding_event(
owner: &Addr,
proxy: &Option<Addr>,
amount: &Coin,
identity: IdentityKeyRef<'_>,
) -> Event {
Event::new(MixnetEventType::GatewayUnbonding)
.add_attribute(OWNER_KEY, owner)
.add_attribute(NODE_IDENTITY_KEY, identity)
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
.add_attribute(AMOUNT_KEY, amount.to_string())
}
pub fn new_mixnode_bonding_event(
owner: &Addr,
proxy: &Option<Addr>,
amount: &Coin,
identity: IdentityKeyRef<'_>,
mix_id: MixId,
@@ -341,7 +299,6 @@ pub fn new_mixnode_bonding_event(
.add_attribute(MIX_ID_KEY, mix_id.to_string())
.add_attribute(NODE_IDENTITY_KEY, identity)
.add_attribute(OWNER_KEY, owner)
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
.add_attribute(ASSIGNED_LAYER_KEY, assigned_layer)
.add_attribute(AMOUNT_KEY, amount.to_string())
}
@@ -380,7 +337,6 @@ pub fn new_mixnode_unbonding_event(created_at: BlockHeight, mix_id: MixId) -> Ev
pub fn new_pending_mixnode_unbonding_event(
owner: &Addr,
proxy: &Option<Addr>,
identity: IdentityKeyRef<'_>,
mix_id: MixId,
) -> Event {
@@ -388,43 +344,33 @@ pub fn new_pending_mixnode_unbonding_event(
.add_attribute(MIX_ID_KEY, mix_id.to_string())
.add_attribute(NODE_IDENTITY_KEY, identity)
.add_attribute(OWNER_KEY, owner)
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
}
pub fn new_mixnode_config_update_event(
mix_id: MixId,
owner: &Addr,
proxy: &Option<Addr>,
update: &MixNodeConfigUpdate,
) -> Event {
Event::new(MixnetEventType::MixnodeConfigUpdate)
.add_attribute(MIX_ID_KEY, mix_id.to_string())
.add_attribute(OWNER_KEY, owner)
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
.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 {
pub fn new_gateway_config_update_event(owner: &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,
proxy: &Option<Addr>,
new_costs: &MixNodeCostParams,
) -> Event {
Event::new(MixnetEventType::PendingMixnodeCostParamsUpdate)
.add_attribute(MIX_ID_KEY, mix_id.to_string())
.add_attribute(OWNER_KEY, owner)
.add_optional_attribute(PROXY_KEY, proxy.as_ref())
.add_attribute(UPDATED_MIXNODE_COST_PARAMS_KEY, new_costs.to_inline_json())
}
@@ -3,7 +3,6 @@
use crate::{IdentityKey, IdentityKeyRef};
use cosmwasm_schema::cw_serde;
use cosmwasm_std::Addr;
use schemars::JsonSchema;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fmt::{Display, Formatter};
@@ -84,10 +83,10 @@ impl FamilyHead {
}
impl Family {
pub fn new(head: FamilyHead, proxy: Option<Addr>, label: String) -> Self {
pub fn new(head: FamilyHead, label: String) -> Self {
Family {
head,
proxy: proxy.map(|p| p.to_string()),
proxy: None,
label,
}
}
@@ -55,19 +55,13 @@ pub struct GatewayBond {
}
impl GatewayBond {
pub fn new(
pledge_amount: Coin,
owner: Addr,
block_height: u64,
gateway: Gateway,
proxy: Option<Addr>,
) -> Self {
pub fn new(pledge_amount: Coin, owner: Addr, block_height: u64, gateway: Gateway) -> Self {
GatewayBond {
pledge_amount,
owner,
block_height,
gateway,
proxy,
proxy: None,
}
}
@@ -518,7 +518,6 @@ impl MixNodeBond {
original_pledge: Coin,
layer: Layer,
mix_node: MixNode,
proxy: Option<Addr>,
bonding_height: u64,
) -> Self {
MixNodeBond {
@@ -527,7 +526,7 @@ impl MixNodeBond {
original_pledge,
layer,
mix_node,
proxy,
proxy: None,
bonding_height,
is_unbonding: false,
}
@@ -269,6 +269,12 @@ pub enum ExecuteMsg {
owner: String,
},
// vesting migration:
MigrateVestedMixNode {},
MigrateVestedDelegation {
mix_id: MixId,
},
// testing-only
#[cfg(feature = "contract-testing")]
TestingResolveAllPendingEvents {
@@ -381,6 +387,9 @@ impl ExecuteMsg {
ExecuteMsg::WithdrawDelegatorRewardOnBehalf { mix_id, .. } => {
format!("withdrawing delegator reward from mixnode {mix_id} on behalf")
}
ExecuteMsg::MigrateVestedMixNode { .. } => "migrate vested mixnode".into(),
ExecuteMsg::MigrateVestedDelegation { .. } => "migrate vested delegation".to_string(),
#[cfg(feature = "contract-testing")]
ExecuteMsg::TestingResolveAllPendingEvents { .. } => {
"resolving all pending events".into()
@@ -38,6 +38,7 @@ pub enum PendingEpochEventKind {
/// Request to create a delegation towards particular mixnode.
/// Note that if such delegation already exists, it will get updated with the provided token amount.
#[serde(alias = "Delegate")]
#[non_exhaustive]
Delegate {
/// The address of the owner of the delegation.
owner: Addr,
@@ -55,6 +56,7 @@ pub enum PendingEpochEventKind {
/// Request to remove delegation from particular mixnode.
#[serde(alias = "Undelegate")]
#[non_exhaustive]
Undelegate {
/// The address of the owner of the delegation.
owner: Addr,
@@ -109,6 +111,23 @@ impl PendingEpochEventKind {
kind: self,
}
}
pub fn new_delegate(owner: Addr, mix_id: MixId, amount: Coin) -> Self {
PendingEpochEventKind::Delegate {
owner,
mix_id,
amount,
proxy: None,
}
}
pub fn new_undelegate(owner: Addr, mix_id: MixId) -> Self {
PendingEpochEventKind::Undelegate {
owner,
mix_id,
proxy: None,
}
}
}
impl From<(EpochEventId, PendingEpochEventData)> for PendingEpochEvent {
@@ -47,7 +47,6 @@ impl SimulatedNode {
self.rewarding_details.total_unit_reward,
delegation,
42,
None,
);
self.delegations.insert(delegator, delegation);
@@ -37,13 +37,12 @@ impl SigningPurpose for MixnodeBondingPayload {
pub fn construct_mixnode_bonding_sign_payload(
nonce: Nonce,
sender: Addr,
proxy: Option<Addr>,
pledge: Coin,
mix_node: MixNode,
cost_params: MixNodeCostParams,
) -> SignableMixNodeBondingMsg {
let payload = MixnodeBondingPayload::new(mix_node, cost_params);
let content = ContractMessageContent::new(sender, proxy, vec![pledge], payload);
let content = ContractMessageContent::new(sender, vec![pledge], payload);
SignableMessage::new(nonce, content)
}
@@ -68,12 +67,11 @@ impl SigningPurpose for GatewayBondingPayload {
pub fn construct_gateway_bonding_sign_payload(
nonce: Nonce,
sender: Addr,
proxy: Option<Addr>,
pledge: Coin,
gateway: Gateway,
) -> SignableGatewayBondingMsg {
let payload = GatewayBondingPayload::new(gateway);
let content = ContractMessageContent::new(sender, proxy, vec![pledge], payload);
let content = ContractMessageContent::new(sender, vec![pledge], payload);
SignableMessage::new(nonce, content)
}
@@ -82,17 +80,14 @@ pub fn construct_gateway_bonding_sign_payload(
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,
}
impl FamilyJoinPermit {
pub fn new(family_head: FamilyHead, proxy: Option<Addr>, member_node: IdentityKey) -> Self {
pub fn new(family_head: FamilyHead, member_node: IdentityKey) -> Self {
Self {
family_head,
proxy,
member_node,
}
}
@@ -107,10 +102,9 @@ impl SigningPurpose for FamilyJoinPermit {
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);
let payload = FamilyJoinPermit::new(family_head, member_node);
// 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
@@ -167,3 +167,11 @@ pub fn new_track_undelegation_event() -> Event {
pub fn new_track_reward_event() -> Event {
Event::new(TRACK_REWARD_EVENT_TYPE)
}
pub fn new_track_migrate_mixnode_event() -> Event {
Event::new("track_migrate_vesting_mixnode")
}
pub fn new_track_migrate_delegation_event() -> Event {
Event::new("track_migrate_vesting_delegation")
}
@@ -136,6 +136,14 @@ pub enum ExecuteMsg {
address: String,
cap: PledgeCap,
},
TrackMigratedMixnode {
owner: String,
},
// no need to track migrated gateways as there are no vesting gateways on mainnet
TrackMigratedDelegation {
owner: String,
mix_id: MixId,
},
}
impl ExecuteMsg {
@@ -171,6 +179,10 @@ impl ExecuteMsg {
ExecuteMsg::TransferOwnership { .. } => "VestingExecuteMsg::TransferOwnership",
ExecuteMsg::UpdateStakingAddress { .. } => "VestingExecuteMsg::UpdateStakingAddress",
ExecuteMsg::UpdateLockedPledgeCap { .. } => "VestingExecuteMsg::UpdateLockedPledgeCap",
ExecuteMsg::TrackMigratedMixnode { .. } => "VestingExecuteMsg::TrackMigratedMixnode",
ExecuteMsg::TrackMigratedDelegation { .. } => {
"VestingExecuteMsg::TrackMigratedDelegation"
}
}
}
}
+2
View File
@@ -84,6 +84,7 @@ impl PendingEpochEventData {
mix_id,
amount,
proxy,
..
} => Ok(PendingEpochEventData::Delegate {
owner: owner.into_string(),
mix_id,
@@ -94,6 +95,7 @@ impl PendingEpochEventData {
owner,
mix_id,
proxy,
..
} => Ok(PendingEpochEventData::Undelegate {
owner: owner.into_string(),
mix_id,
@@ -1,239 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::support::helpers::{mix_coin, mix_coins, vesting_owner};
use crate::support::setup::{TestSetup, MIX_DENOM};
use cosmwasm_std::Addr;
use cw_multi_test::Executor;
use nym_contracts_common::Percent;
use nym_mixnet_contract_common::error::MixnetContractError;
use nym_mixnet_contract_common::{ContractStateParams, MixNodeCostParams};
use nym_mixnet_contract_common::{MixOwnershipResponse, QueryMsg as MixnetQueryMsg};
use nym_vesting_contract_common::{ExecuteMsg as VestingExecuteMsg, VestingContractError};
#[test]
fn decrease_mixnode_pledge_from_vesting_account_with_minimum_pledge() {
let mut test = TestSetup::new_simple();
let vesting_account = "vesting-account";
// 0. get the minimum pledge amount
let state_params: ContractStateParams = test
.app
.wrap()
.query_wasm_smart(test.mixnet_contract(), &MixnetQueryMsg::GetStateParams {})
.unwrap();
let minimum_pledge = state_params.minimum_mixnode_pledge;
// 1. create vesting account
let create_msg = VestingExecuteMsg::CreateAccount {
owner_address: vesting_account.to_string(),
staking_address: None,
vesting_spec: None,
cap: None,
};
test.app
.execute_contract(
vesting_owner(),
test.vesting_contract(),
&create_msg,
&mix_coins(1_000_000_000),
)
.unwrap();
// 2. bond mixnode with the vesting account
let pledge = minimum_pledge.clone();
let cost_params = MixNodeCostParams {
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
interval_operating_cost: mix_coin(40_000_000),
};
let (mix_node, owner_signature) = test.valid_mixnode_with_sig(
vesting_account,
Some(test.vesting_contract()),
cost_params.clone(),
pledge.clone(),
);
let bond_msg = VestingExecuteMsg::BondMixnode {
mix_node,
cost_params,
owner_signature,
amount: pledge.clone(),
};
test.app
.execute_contract(
Addr::unchecked(vesting_account),
test.vesting_contract(),
&bond_msg,
&[],
)
.unwrap();
// 3. try to decrease the pledge
// trying to decrease by a zero amount - not valid
let decrease_pledge_msg = VestingExecuteMsg::DecreasePledge {
amount: mix_coin(0),
};
let res_zero = test
.app
.execute_contract(
Addr::unchecked(vesting_account),
test.vesting_contract(),
&decrease_pledge_msg,
&[],
)
.unwrap_err();
assert_eq!(
VestingContractError::EmptyFunds,
res_zero.downcast().unwrap()
);
// trying to go below the cap - also not valid
let amount = mix_coin(50_000);
let decrease_pledge_msg = VestingExecuteMsg::DecreasePledge {
amount: amount.clone(),
};
let res_below = test
.app
.execute_contract(
Addr::unchecked(vesting_account),
test.vesting_contract(),
&decrease_pledge_msg,
&[],
)
.unwrap_err();
assert_eq!(
MixnetContractError::InvalidPledgeReduction {
current: pledge.amount,
decrease_by: amount.amount,
minimum: minimum_pledge.amount,
denom: minimum_pledge.denom
},
res_below.downcast().unwrap()
)
}
#[test]
fn decrease_mixnode_pledge_from_vesting_account_with_sufficient_pledge() {
let mut test = TestSetup::new_simple();
let vesting_account = "vesting-account";
// 1. create vesting account
let create_msg = VestingExecuteMsg::CreateAccount {
owner_address: vesting_account.to_string(),
staking_address: None,
vesting_spec: None,
cap: None,
};
test.app
.execute_contract(
vesting_owner(),
test.vesting_contract(),
&create_msg,
&mix_coins(10_000_000_000),
)
.unwrap();
// 2. bond mixnode with the vesting account
let pledge = mix_coin(150_000_000);
let cost_params = MixNodeCostParams {
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
interval_operating_cost: mix_coin(40_000_000),
};
let (mix_node, owner_signature) = test.valid_mixnode_with_sig(
vesting_account,
Some(test.vesting_contract()),
cost_params.clone(),
pledge.clone(),
);
let bond_msg = VestingExecuteMsg::BondMixnode {
mix_node,
cost_params,
owner_signature,
amount: pledge,
};
test.app
.execute_contract(
Addr::unchecked(vesting_account),
test.vesting_contract(),
&bond_msg,
&[],
)
.unwrap();
// 3. try to decrease the pledge
let before: MixOwnershipResponse = test
.app
.wrap()
.query_wasm_smart(
test.mixnet_contract(),
&MixnetQueryMsg::GetOwnedMixnode {
address: vesting_account.to_string(),
},
)
.unwrap();
let balance_before = test
.app
.wrap()
.query_balance(test.vesting_contract(), MIX_DENOM)
.unwrap();
assert_eq!(balance_before.amount.u128(), 9_850_000_000);
let decrease_pledge_msg = VestingExecuteMsg::DecreasePledge {
amount: mix_coin(50_000_000),
};
test.app
.execute_contract(
Addr::unchecked(vesting_account),
test.vesting_contract(),
&decrease_pledge_msg,
&[],
)
.unwrap();
let after_decrease: MixOwnershipResponse = test
.app
.wrap()
.query_wasm_smart(
test.mixnet_contract(),
&MixnetQueryMsg::GetOwnedMixnode {
address: vesting_account.to_string(),
},
)
.unwrap();
// note: nothing has changed with the pledge because the event hasn't been resolved yet!
assert_eq!(before.address, after_decrease.address);
let before_details = before.mixnode_details.unwrap();
let after_details = after_decrease.mixnode_details.unwrap();
assert_eq!(
before_details.rewarding_details,
after_details.rewarding_details
);
assert_eq!(
before_details.bond_information,
after_details.bond_information
);
// but we have the pending change saved now!
assert!(before_details.pending_changes.pledge_change.is_none());
assert_eq!(Some(1), after_details.pending_changes.pledge_change);
// 4. resolve events
test.advance_mixnet_epoch();
let balance_after = test
.app
.wrap()
.query_balance(test.vesting_contract(), MIX_DENOM)
.unwrap();
assert_eq!(balance_after.amount.u128(), 9_900_000_000);
}
@@ -12,27 +12,33 @@ pub fn mixnet_owner() -> Addr {
Addr::unchecked(MIXNET_OWNER)
}
#[allow(unused)]
pub fn vesting_owner() -> Addr {
Addr::unchecked(VESTING_OWNER)
}
#[allow(unused)]
pub fn rewarding_validator() -> Addr {
Addr::unchecked(REWARDING_VALIDATOR)
}
#[allow(unused)]
pub fn mix_coins(amount: u128) -> Vec<Coin> {
coins(amount, MIX_DENOM)
}
#[allow(unused)]
pub fn mix_coin(amount: u128) -> Coin {
coin(amount, MIX_DENOM)
}
#[allow(unused)]
pub fn test_rng() -> ChaCha20Rng {
let dummy_seed = [42u8; 32];
ChaCha20Rng::from_seed(dummy_seed)
}
#[allow(unused)]
pub fn mixnet_contract_wrapper() -> Box<dyn Contract<Empty>> {
Box::new(
ContractWrapper::new(
@@ -26,6 +26,7 @@ pub const VESTING_OWNER: &str = "vesting-owner";
pub const REWARDING_VALIDATOR: &str = "rewarding-validator";
pub const MIX_DENOM: &str = "unym";
#[allow(unused)]
pub struct ContractInstantiationResult {
mixnet_contract_address: Addr,
vesting_contract_address: Addr,
@@ -69,14 +70,15 @@ impl TestSetupBuilder {
}
}
#[allow(unused)]
pub struct TestSetup {
pub app: App,
pub rng: ChaCha20Rng,
pub mixnet_contract: Addr,
pub vesting_contract: Addr,
}
#[allow(unused)]
impl TestSetup {
pub fn new_simple() -> Self {
TestSetup::new(Default::default(), fixtures::default_mixnet_init_msg())
@@ -91,7 +93,6 @@ impl TestSetup {
app,
rng: test_rng(),
mixnet_contract: contracts.mixnet_contract_address,
vesting_contract: contracts.vesting_contract_address,
}
}
@@ -99,10 +100,6 @@ impl TestSetup {
self.mixnet_contract.clone()
}
pub fn vesting_contract(&self) -> Addr {
self.vesting_contract.clone()
}
pub fn skip_to_current_epoch_end(&mut self) {
let current_interval: CurrentIntervalResponse = self
.app
@@ -209,7 +206,6 @@ impl TestSetup {
pub fn valid_mixnode_with_sig(
&mut self,
owner: &str,
proxy: Option<Addr>,
cost_params: MixNodeCostParams,
stake: Coin,
) -> (MixNode, MessageSignature) {
@@ -239,8 +235,7 @@ impl TestSetup {
};
let payload = MixnodeBondingPayload::new(mixnode.clone(), cost_params);
let content =
ContractMessageContent::new(Addr::unchecked(owner), proxy, vec![stake], payload);
let content = ContractMessageContent::new(Addr::unchecked(owner), vec![stake], payload);
let sign_payload = SignableMixNodeBondingMsg::new(signing_nonce, content);
let plaintext = sign_payload.to_plaintext().unwrap();
let signature = keypair.private_key().sign(plaintext);
@@ -1,5 +1,4 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
mod decrease_mixnode_pledge;
mod support;
@@ -1146,6 +1146,42 @@
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"migrate_vested_mix_node"
],
"properties": {
"migrate_vested_mix_node": {
"type": "object",
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"migrate_vested_delegation"
],
"properties": {
"migrate_vested_delegation": {
"type": "object",
"required": [
"mix_id"
],
"properties": {
"mix_id": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
],
"definitions": {
+36
View File
@@ -1029,6 +1029,42 @@
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"migrate_vested_mix_node"
],
"properties": {
"migrate_vested_mix_node": {
"type": "object",
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"migrate_vested_delegation"
],
"properties": {
"migrate_vested_delegation": {
"type": "object",
"required": [
"mix_id"
],
"properties": {
"mix_id": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
],
"definitions": {
+31 -115
View File
@@ -118,44 +118,6 @@ pub fn execute(
ExecuteMsg::KickFamilyMember { member } => {
crate::families::transactions::try_head_kick_member(deps, info, member)
}
ExecuteMsg::CreateFamilyOnBehalf {
owner_address,
label,
} => crate::families::transactions::try_create_family_on_behalf(
deps,
info,
owner_address,
label,
),
ExecuteMsg::JoinFamilyOnBehalf {
member_address,
join_permit,
family_head,
} => crate::families::transactions::try_join_family_on_behalf(
deps,
info,
member_address,
join_permit,
family_head,
),
ExecuteMsg::LeaveFamilyOnBehalf {
member_address,
family_head,
} => crate::families::transactions::try_leave_family_on_behalf(
deps,
info,
member_address,
family_head,
),
ExecuteMsg::KickFamilyMemberOnBehalf {
head_address,
member,
} => crate::families::transactions::try_head_kick_member_on_behalf(
deps,
info,
head_address,
member,
),
// state/sys-params-related
ExecuteMsg::UpdateRewardingValidatorAddress { address } => {
crate::mixnet_contract_settings::transactions::try_update_rewarding_validator_address(
@@ -232,62 +194,23 @@ pub fn execute(
cost_params,
owner_signature,
),
ExecuteMsg::BondMixnodeOnBehalf {
mix_node,
cost_params,
owner,
owner_signature,
} => crate::mixnodes::transactions::try_add_mixnode_on_behalf(
deps,
env,
info,
mix_node,
cost_params,
owner,
owner_signature,
),
ExecuteMsg::PledgeMore {} => {
crate::mixnodes::transactions::try_increase_pledge(deps, env, info)
}
ExecuteMsg::PledgeMoreOnBehalf { owner } => {
crate::mixnodes::transactions::try_increase_pledge_on_behalf(deps, env, info, owner)
}
ExecuteMsg::DecreasePledge { decrease_by } => {
crate::mixnodes::transactions::try_decrease_pledge(deps, env, info, decrease_by)
}
ExecuteMsg::DecreasePledgeOnBehalf { owner, decrease_by } => {
crate::mixnodes::transactions::try_decrease_pledge_on_behalf(
deps,
env,
info,
decrease_by,
owner,
)
}
ExecuteMsg::UnbondMixnode {} => {
crate::mixnodes::transactions::try_remove_mixnode(deps, env, info)
}
ExecuteMsg::UnbondMixnodeOnBehalf { owner } => {
crate::mixnodes::transactions::try_remove_mixnode_on_behalf(deps, env, info, owner)
}
ExecuteMsg::UpdateMixnodeCostParams { new_costs } => {
crate::mixnodes::transactions::try_update_mixnode_cost_params(
deps, env, info, new_costs,
)
}
ExecuteMsg::UpdateMixnodeCostParamsOnBehalf { new_costs, owner } => {
crate::mixnodes::transactions::try_update_mixnode_cost_params_on_behalf(
deps, env, info, new_costs, owner,
)
}
ExecuteMsg::UpdateMixnodeConfig { new_config } => {
crate::mixnodes::transactions::try_update_mixnode_config(deps, info, new_config)
}
ExecuteMsg::UpdateMixnodeConfigOnBehalf { new_config, owner } => {
crate::mixnodes::transactions::try_update_mixnode_config_on_behalf(
deps, info, new_config, owner,
)
}
// gateway-related:
ExecuteMsg::BondGateway {
@@ -300,52 +223,22 @@ pub fn execute(
gateway,
owner_signature,
),
ExecuteMsg::BondGatewayOnBehalf {
gateway,
owner,
owner_signature,
} => crate::gateways::transactions::try_add_gateway_on_behalf(
deps,
env,
info,
gateway,
owner,
owner_signature,
),
ExecuteMsg::UnbondGateway {} => {
crate::gateways::transactions::try_remove_gateway(deps, info)
}
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 } => {
crate::delegations::transactions::try_delegate_to_mixnode(deps, env, info, mix_id)
}
ExecuteMsg::DelegateToMixnodeOnBehalf { mix_id, delegate } => {
crate::delegations::transactions::try_delegate_to_mixnode_on_behalf(
deps, env, info, mix_id, delegate,
)
}
ExecuteMsg::UndelegateFromMixnode { mix_id } => {
crate::delegations::transactions::try_remove_delegation_from_mixnode(
deps, env, info, mix_id,
)
}
ExecuteMsg::UndelegateFromMixnodeOnBehalf { mix_id, delegate } => {
crate::delegations::transactions::try_remove_delegation_from_mixnode_on_behalf(
deps, env, info, mix_id, delegate,
)
}
// reward-related
ExecuteMsg::RewardMixnode {
@@ -356,16 +249,37 @@ pub fn execute(
ExecuteMsg::WithdrawOperatorReward {} => {
crate::rewards::transactions::try_withdraw_operator_reward(deps, info)
}
ExecuteMsg::WithdrawOperatorRewardOnBehalf { owner } => {
crate::rewards::transactions::try_withdraw_operator_reward_on_behalf(deps, info, owner)
}
ExecuteMsg::WithdrawDelegatorReward { mix_id } => {
crate::rewards::transactions::try_withdraw_delegator_reward(deps, info, mix_id)
}
ExecuteMsg::WithdrawDelegatorRewardOnBehalf { mix_id, owner } => {
crate::rewards::transactions::try_withdraw_delegator_reward_on_behalf(
deps, info, mix_id, owner,
)
// vesting migration:
ExecuteMsg::MigrateVestedMixNode { .. } => {
crate::vesting_migration::try_migrate_vested_mixnode(deps, info)
}
ExecuteMsg::MigrateVestedDelegation { mix_id } => {
crate::vesting_migration::try_migrate_vested_delegation(deps, info, mix_id)
}
// legacy vesting
ExecuteMsg::CreateFamilyOnBehalf { .. }
| ExecuteMsg::JoinFamilyOnBehalf { .. }
| ExecuteMsg::LeaveFamilyOnBehalf { .. }
| ExecuteMsg::KickFamilyMemberOnBehalf { .. }
| ExecuteMsg::BondMixnodeOnBehalf { .. }
| ExecuteMsg::PledgeMoreOnBehalf { .. }
| ExecuteMsg::DecreasePledgeOnBehalf { .. }
| ExecuteMsg::UnbondMixnodeOnBehalf { .. }
| ExecuteMsg::UpdateMixnodeCostParamsOnBehalf { .. }
| ExecuteMsg::UpdateMixnodeConfigOnBehalf { .. }
| ExecuteMsg::BondGatewayOnBehalf { .. }
| ExecuteMsg::UnbondGatewayOnBehalf { .. }
| ExecuteMsg::UpdateGatewayConfigOnBehalf { .. }
| ExecuteMsg::DelegateToMixnodeOnBehalf { .. }
| ExecuteMsg::UndelegateFromMixnodeOnBehalf { .. }
| ExecuteMsg::WithdrawOperatorRewardOnBehalf { .. }
| ExecuteMsg::WithdrawDelegatorRewardOnBehalf { .. } => {
Err(MixnetContractError::DisabledVestingOperation)
}
// testing-only
@@ -607,13 +521,15 @@ pub fn query(
#[entry_point]
pub fn migrate(
deps: DepsMut<'_>,
mut deps: DepsMut<'_>,
_env: Env,
msg: MigrateMsg,
) -> Result<Response, MixnetContractError> {
set_build_information!(deps.storage)?;
cw2::ensure_from_older_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
crate::queued_migrations::vesting_purge(deps.branch())?;
// due to circular dependency on contract addresses (i.e. mixnet contract requiring vesting contract address
// and vesting contract requiring the mixnet contract address), if we ever want to deploy any new fresh
// environment, one of the contracts will HAVE TO go through a migration
+2 -73
View File
@@ -302,10 +302,7 @@ mod tests {
mod delegator_delegations {
use super::*;
use crate::delegations::transactions::try_delegate_to_mixnode_on_behalf;
use crate::support::tests::fixtures::TEST_COIN_DENOM;
use cosmwasm_std::testing::mock_info;
use cosmwasm_std::{coin, Addr};
use cosmwasm_std::Addr;
#[test]
fn obeys_limits() {
@@ -453,25 +450,10 @@ mod tests {
#[test]
fn all_retrieved_delegations_are_from_the_specified_delegator() {
let mut test = TestSetup::new();
let env = test.env();
// it means we have, for example, delegation from "delegator1" towards mix1, mix2, ...., from "delegator2" towards mix1, mix2, ...., etc
add_dummy_mixes_with_delegations(&mut test, 50, 100);
// add some proxies while we're at it to make sure they're queried for separately
let with_proxy = "delegator42";
let vesting_contract = test.vesting_contract();
for mix_id in 1..=25 {
try_delegate_to_mixnode_on_behalf(
test.deps_mut(),
env.clone(),
mock_info(vesting_contract.as_ref(), &[coin(100_000, TEST_COIN_DENOM)]),
mix_id,
with_proxy.into(),
)
.unwrap();
}
test.execute_all_pending_events();
// make few queries
let res1 =
query_delegator_delegations_paged(test.deps(), "delegator2".into(), None, None)
@@ -490,59 +472,6 @@ mod tests {
.delegations
.into_iter()
.all(|d| d.owner == Addr::unchecked("delegator35")));
let with_proxy_full =
query_delegator_delegations_paged(test.deps(), with_proxy.into(), None, None)
.unwrap();
assert_eq!(with_proxy_full.delegations.len(), 125);
// all delegations have correct owner
assert!(with_proxy_full
.delegations
.iter()
.all(|d| d.owner == Addr::unchecked(with_proxy)));
// and we have 100 delegations without proxy and 25 with
let no_proxy = with_proxy_full
.delegations
.iter()
.filter(|d| d.proxy.is_none())
.count();
assert_eq!(no_proxy, 100);
let proxy = with_proxy_full
.delegations
.iter()
.filter(|d| d.proxy.is_some())
.count();
assert_eq!(proxy, 25);
assert!(with_proxy_full
.delegations
.iter()
.filter(|d| d.proxy.is_some())
.all(|d| d.proxy.as_ref().unwrap() == vesting_contract));
// now make sure that if we do it in paged manner, we'll get exactly the same result
let per_page = Some(15);
let mut delegations = Vec::new();
let mut start_after = None;
loop {
let mut paged_response = query_delegator_delegations_paged(
test.deps(),
with_proxy.into(),
start_after,
per_page,
)
.unwrap();
delegations.append(&mut paged_response.delegations);
if let Some(start_after_res) = paged_response.start_next_after {
start_after = Some(start_after_res)
} else {
break;
}
}
assert_eq!(with_proxy_full.delegations, delegations)
}
}
+13 -154
View File
@@ -1,14 +1,12 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::storage;
use crate::interval::storage as interval_storage;
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use crate::mixnodes::storage as mixnodes_storage;
use crate::support::helpers::{
ensure_epoch_in_progress_state, ensure_sent_by_vesting_contract, validate_delegation_stake,
};
use cosmwasm_std::{Addr, Coin, DepsMut, Env, MessageInfo, Response};
use crate::support::helpers::{ensure_epoch_in_progress_state, validate_delegation_stake};
use cosmwasm_std::{DepsMut, Env, MessageInfo, Response};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::events::{
new_pending_delegation_event, new_pending_undelegation_event,
@@ -21,30 +19,6 @@ pub(crate) fn try_delegate_to_mixnode(
env: Env,
info: MessageInfo,
mix_id: MixId,
) -> Result<Response, MixnetContractError> {
_try_delegate_to_mixnode(deps, env, mix_id, info.sender, info.funds, None)
}
pub(crate) fn try_delegate_to_mixnode_on_behalf(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
mix_id: MixId,
delegate: String,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let delegate = deps.api.addr_validate(&delegate)?;
_try_delegate_to_mixnode(deps, env, mix_id, delegate, info.funds, Some(info.sender))
}
pub(crate) fn _try_delegate_to_mixnode(
deps: DepsMut<'_>,
env: Env,
mix_id: MixId,
delegate: Addr,
amount: Vec<Coin>,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
// delegation is only allowed if the epoch is currently not in the process of being advanced
ensure_epoch_in_progress_state(deps.storage)?;
@@ -52,7 +26,7 @@ pub(crate) fn _try_delegate_to_mixnode(
// check if the delegation contains any funds of the appropriate denomination
let contract_state = mixnet_params_storage::CONTRACT_STATE.load(deps.storage)?;
let delegation = validate_delegation_stake(
amount,
info.funds,
contract_state.params.minimum_mixnode_delegation,
contract_state.rewarding_denom,
)?;
@@ -67,14 +41,9 @@ pub(crate) fn _try_delegate_to_mixnode(
}
// push the event onto the queue and wait for it to be picked up at the end of the epoch
let cosmos_event = new_pending_delegation_event(&delegate, &proxy, &delegation, mix_id);
let cosmos_event = new_pending_delegation_event(&info.sender, &delegation, mix_id);
let epoch_event = PendingEpochEventKind::Delegate {
owner: delegate,
mix_id,
amount: delegation,
proxy,
};
let epoch_event = PendingEpochEventKind::new_delegate(info.sender, mix_id, delegation);
interval_storage::push_new_epoch_event(deps.storage, &env, epoch_event)?;
Ok(Response::new().add_event(cosmos_event))
@@ -85,35 +54,12 @@ pub(crate) fn try_remove_delegation_from_mixnode(
env: Env,
info: MessageInfo,
mix_id: MixId,
) -> Result<Response, MixnetContractError> {
_try_remove_delegation_from_mixnode(deps, env, mix_id, info.sender, None)
}
pub(crate) fn try_remove_delegation_from_mixnode_on_behalf(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
mix_id: MixId,
delegate: String,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let delegate = deps.api.addr_validate(&delegate)?;
_try_remove_delegation_from_mixnode(deps, env, mix_id, delegate, Some(info.sender))
}
pub(crate) fn _try_remove_delegation_from_mixnode(
deps: DepsMut<'_>,
env: Env,
mix_id: MixId,
delegate: Addr,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
// undelegation is only allowed if the epoch is currently not in the process of being advanced
ensure_epoch_in_progress_state(deps.storage)?;
// see if the delegation even exists
let storage_key = Delegation::generate_storage_key(mix_id, &delegate, proxy.as_ref());
let storage_key = Delegation::generate_storage_key(mix_id, &info.sender, None);
if storage::delegations()
.may_load(deps.storage, storage_key)?
@@ -121,19 +67,15 @@ pub(crate) fn _try_remove_delegation_from_mixnode(
{
return Err(MixnetContractError::NoMixnodeDelegationFound {
mix_id,
address: delegate.into_string(),
proxy: proxy.map(Addr::into_string),
address: info.sender.into_string(),
proxy: None,
});
}
// push the event onto the queue and wait for it to be picked up at the end of the epoch
let cosmos_event = new_pending_undelegation_event(&delegate, &proxy, mix_id);
let cosmos_event = new_pending_undelegation_event(&info.sender, mix_id);
let epoch_event = PendingEpochEventKind::Undelegate {
owner: delegate,
mix_id,
proxy,
};
let epoch_event = PendingEpochEventKind::new_undelegate(info.sender, mix_id);
interval_storage::push_new_epoch_event(deps.storage, &env, epoch_event)?;
Ok(Response::new().add_event(cosmos_event))
@@ -151,7 +93,7 @@ mod tests {
use crate::support::tests::fixtures::TEST_COIN_DENOM;
use crate::support::tests::test_helpers::TestSetup;
use cosmwasm_std::testing::mock_info;
use cosmwasm_std::{coin, Decimal};
use cosmwasm_std::{coin, Addr, Decimal};
use mixnet_contract_common::{EpochState, EpochStatus};
#[test]
@@ -368,65 +310,17 @@ mod tests {
let mix_id = test.add_dummy_mixnode("mix-owner", None);
let amount1 = coin(100_000_000, TEST_COIN_DENOM);
let amount2 = coin(50_000_000, TEST_COIN_DENOM);
let sender1 = mock_info(owner, &[amount1.clone()]);
let sender2 = mock_info(test.vesting_contract().as_str(), &[amount2.clone()]);
try_delegate_to_mixnode(test.deps_mut(), env.clone(), sender1, mix_id).unwrap();
try_delegate_to_mixnode_on_behalf(test.deps_mut(), env, sender2, mix_id, owner.into())
.unwrap();
let events = test.pending_epoch_events();
assert_eq!(
events[0].kind,
PendingEpochEventKind::Delegate {
owner: Addr::unchecked(owner),
mix_id,
amount: amount1,
proxy: None
}
PendingEpochEventKind::new_delegate(Addr::unchecked(owner), mix_id, amount1,)
);
assert_eq!(
events[1].kind,
PendingEpochEventKind::Delegate {
owner: Addr::unchecked(owner),
mix_id,
amount: amount2,
proxy: Some(test.vesting_contract())
}
);
}
#[test]
fn fails_for_illegal_proxy() {
let mut test = TestSetup::new();
let env = test.env();
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let owner = "delegator";
let mix_id = test.add_dummy_mixnode("mix-owner", None);
let res = try_delegate_to_mixnode_on_behalf(
test.deps_mut(),
env,
mock_info(illegal_proxy.as_ref(), &[coin(123, TEST_COIN_DENOM)]),
mix_id,
owner.into(),
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract
}
)
}
}
@@ -573,40 +467,5 @@ mod tests {
);
assert!(res.is_ok());
}
#[test]
fn fails_for_illegal_proxy() {
let mut test = TestSetup::new();
let env = test.env();
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let owner = "delegator";
let mix_id = test.add_dummy_mixnode("mix-owner", None);
test.add_immediate_delegation_with_illegal_proxy(
owner,
10000u32,
mix_id,
illegal_proxy.clone(),
);
let res = try_remove_delegation_from_mixnode_on_behalf(
test.deps_mut(),
env,
mock_info(illegal_proxy.as_ref(), &[coin(123, TEST_COIN_DENOM)]),
mix_id,
owner.into(),
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract
}
)
}
}
}
@@ -4,7 +4,7 @@
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 cosmwasm_std::Deps;
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::families::FamilyHead;
use mixnet_contract_common::{construct_family_join_permit, IdentityKeyRef};
@@ -13,7 +13,6 @@ 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> {
@@ -32,7 +31,7 @@ pub(crate) fn verify_family_join_permit(
});
};
let nonce = signing_storage::get_signing_nonce(deps.storage, head_mixnode.owner)?;
let msg = construct_family_join_permit(nonce, granter, proxy, member.to_owned());
let msg = construct_family_join_permit(nonce, granter, member.to_owned());
if deps.api.verify_message(msg, signature, &public_key)? {
Ok(())
+15 -285
View File
@@ -1,4 +1,4 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::storage::{
@@ -7,41 +7,20 @@ use super::storage::{
};
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 crate::support::helpers::ensure_bonded;
use cosmwasm_std::{DepsMut, MessageInfo, Response};
use mixnet_contract_common::families::{Family, FamilyHead};
use mixnet_contract_common::{error::MixnetContractError, IdentityKey};
use nym_contracts_common::signing::MessageSignature;
/// Creates a new MixNode family with senders node as head
pub fn try_create_family(
pub(crate) fn try_create_family(
deps: DepsMut,
info: MessageInfo,
label: String,
) -> Result<Response, MixnetContractError> {
_try_create_family(deps, &info.sender, label, None)
}
pub fn try_create_family_on_behalf(
deps: DepsMut,
info: MessageInfo,
owner_address: String,
label: String,
) -> 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))
}
fn _try_create_family(
deps: DepsMut,
owner: &Addr,
label: String,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
let existing_bond =
crate::mixnodes::helpers::must_get_mixnode_bond_by_owner(deps.storage, owner)?;
crate::mixnodes::helpers::must_get_mixnode_bond_by_owner(deps.storage, &info.sender)?;
ensure_bonded(&existing_bond)?;
@@ -60,43 +39,19 @@ fn _try_create_family(
return Err(MixnetContractError::FamilyWithLabelExists(label));
}
let family = Family::new(family_head, proxy, label);
let family = Family::new(family_head, label);
save_family(&family, deps.storage)?;
Ok(Response::default())
}
pub fn try_join_family(
pub(crate) fn try_join_family(
deps: DepsMut,
info: MessageInfo,
join_permit: MessageSignature,
family_head: FamilyHead,
) -> Result<Response, MixnetContractError> {
_try_join_family(deps, &info.sender, join_permit, family_head, None)
}
pub fn try_join_family_on_behalf(
deps: DepsMut,
info: MessageInfo,
member_address: String,
join_permit: MessageSignature,
family_head: FamilyHead,
) -> 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)
}
fn _try_join_family(
deps: DepsMut,
owner: &Addr,
join_permit: MessageSignature,
family_head: FamilyHead,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
let existing_bond =
crate::mixnodes::helpers::must_get_mixnode_bond_by_owner(deps.storage, owner)?;
crate::mixnodes::helpers::must_get_mixnode_bond_by_owner(deps.storage, &info.sender)?;
ensure_bonded(&existing_bond)?;
@@ -116,7 +71,6 @@ fn _try_join_family(
verify_family_join_permit(
deps.as_ref(),
family_head.clone(),
proxy,
existing_bond.identity(),
join_permit,
)?;
@@ -128,33 +82,13 @@ fn _try_join_family(
Ok(Response::default())
}
pub fn try_leave_family(
pub(crate) fn try_leave_family(
deps: DepsMut,
info: MessageInfo,
family_head: FamilyHead,
) -> Result<Response, MixnetContractError> {
_try_leave_family(deps, &info.sender, family_head)
}
pub fn try_leave_family_on_behalf(
deps: DepsMut,
info: MessageInfo,
member_address: String,
family_head: FamilyHead,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let member_address = deps.api.addr_validate(&member_address)?;
_try_leave_family(deps, &member_address, family_head)
}
fn _try_leave_family(
deps: DepsMut,
owner: &Addr,
family_head: FamilyHead,
) -> Result<Response, MixnetContractError> {
let existing_bond =
crate::mixnodes::helpers::must_get_mixnode_bond_by_owner(deps.storage, owner)?;
crate::mixnodes::helpers::must_get_mixnode_bond_by_owner(deps.storage, &info.sender)?;
ensure_bonded(&existing_bond)?;
@@ -178,32 +112,13 @@ fn _try_leave_family(
Ok(Response::default())
}
pub fn try_head_kick_member(
pub(crate) fn try_head_kick_member(
deps: DepsMut,
info: MessageInfo,
member: IdentityKey,
) -> Result<Response, MixnetContractError> {
_try_head_kick_member(deps, &info.sender, member)
}
pub fn try_head_kick_member_on_behalf(
deps: DepsMut,
info: MessageInfo,
head_address: String,
member: IdentityKey,
) -> 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)
}
fn _try_head_kick_member(
deps: DepsMut,
owner: &Addr,
member: IdentityKey,
) -> Result<Response, MixnetContractError> {
let head_bond = crate::mixnodes::helpers::must_get_mixnode_bond_by_owner(deps.storage, owner)?;
let head_bond =
crate::mixnodes::helpers::must_get_mixnode_bond_by_owner(deps.storage, &info.sender)?;
// make sure we're still in the mixnet
ensure_bonded(&head_bond)?;
@@ -321,7 +236,7 @@ mod test {
assert_eq!(family.head_identity(), family_head.identity());
let join_permit =
test.generate_family_join_permit(&head_keypair, &member_mixnode.identity_key, false);
test.generate_family_join_permit(&head_keypair, &member_mixnode.identity_key);
try_join_family(
test.deps_mut(),
@@ -345,7 +260,7 @@ mod test {
);
let new_join_permit =
test.generate_family_join_permit(&head_keypair, &member_mixnode.identity_key, false);
test.generate_family_join_permit(&head_keypair, &member_mixnode.identity_key);
try_join_family(
test.deps_mut(),
@@ -373,189 +288,4 @@ mod test {
!is_family_member(test.deps().storage, &family, &member_mixnode.identity_key).unwrap()
);
}
#[cfg(test)]
mod creating_family {
use super::*;
#[test]
fn fails_for_illegal_proxy() {
let mut test = TestSetup::new();
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let head = "alice";
test.add_dummy_mixnode(head, None);
let res = try_create_family_on_behalf(
test.deps_mut(),
mock_info(illegal_proxy.as_ref(), &[]),
head.to_string(),
"label".to_string(),
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract
}
)
}
}
#[cfg(test)]
mod joining_family {
use super::*;
#[test]
fn fails_for_illegal_proxy() {
let mut test = TestSetup::new();
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let head = "alice";
let label = "family";
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 join_permit = test.generate_family_join_permit(
&head_keys,
&member_keys.public_key().to_base58_string(),
false,
);
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,
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract
}
)
}
}
#[cfg(test)]
mod leaving_family {
use super::*;
#[test]
fn fails_for_illegal_proxy() {
let mut test = TestSetup::new();
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let head = "alice";
let label = "family";
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 join_permit = test.generate_family_join_permit(
&head_keys,
&member_keys.public_key().to_base58_string(),
true,
);
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(),
)
.unwrap();
let res = try_leave_family_on_behalf(
test.deps_mut(),
mock_info(illegal_proxy.as_ref(), &[]),
new_member.to_string(),
family_head,
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract
}
)
}
}
#[cfg(test)]
mod kicking_family_member {
use super::*;
#[test]
fn fails_for_illegal_proxy() {
let mut test = TestSetup::new();
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let head = "alice";
let label = "family";
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 join_permit = test.generate_family_join_permit(
&head_keys,
&member_keys.public_key().to_base58_string(),
true,
);
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,
)
.unwrap();
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(),
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract
}
)
}
}
}
@@ -12,7 +12,6 @@ use nym_contracts_common::signing::Verifier;
pub(crate) fn verify_gateway_bonding_signature(
deps: Deps<'_>,
sender: Addr,
proxy: Option<Addr>,
pledge: Coin,
gateway: Gateway,
signature: MessageSignature,
@@ -22,7 +21,7 @@ pub(crate) fn verify_gateway_bonding_signature(
// reconstruct the payload
let nonce = signing_storage::get_signing_nonce(deps.storage, sender.clone())?;
let msg = construct_gateway_bonding_sign_payload(nonce, sender, proxy, pledge, gateway);
let msg = construct_gateway_bonding_sign_payload(nonce, sender, pledge, gateway);
if deps.api.verify_message(msg, signature, &public_key)? {
Ok(())
+28 -253
View File
@@ -1,4 +1,4 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::helpers::must_get_gateway_bond_by_owner;
@@ -6,10 +6,8 @@ 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_proxy_match, ensure_sent_by_vesting_contract, validate_pledge,
};
use cosmwasm_std::{wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response};
use crate::support::helpers::{ensure_no_existing_bond, validate_pledge};
use cosmwasm_std::{BankMsg, DepsMut, Env, MessageInfo, Response};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::events::{
new_gateway_bonding_event, new_gateway_config_update_event, new_gateway_unbonding_event,
@@ -17,72 +15,28 @@ use mixnet_contract_common::events::{
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;
pub fn try_add_gateway(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
gateway: Gateway,
owner_signature: MessageSignature,
) -> Result<Response, MixnetContractError> {
_try_add_gateway(
deps,
env,
gateway,
info.funds,
info.sender,
owner_signature,
None,
)
}
pub fn try_add_gateway_on_behalf(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
gateway: Gateway,
owner: String,
owner_signature: MessageSignature,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let proxy = info.sender;
let owner = deps.api.addr_validate(&owner)?;
_try_add_gateway(
deps,
env,
gateway,
info.funds,
owner,
owner_signature,
Some(proxy),
)
}
// TODO: perhaps also require the user to explicitly provide what it thinks is the current nonce
// so that we could return a better error message if it doesn't match?
pub(crate) fn _try_add_gateway(
pub(crate) fn try_add_gateway(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
gateway: Gateway,
pledge: Vec<Coin>,
owner: Addr,
owner_signature: MessageSignature,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
// check if the pledge contains any funds of the appropriate denomination
let minimum_pledge = mixnet_params_storage::minimum_gateway_pledge(deps.storage)?;
let pledge = validate_pledge(pledge, minimum_pledge)?;
let pledge = validate_pledge(info.funds, minimum_pledge)?;
// if the client has an active bonded mixnode or gateway, don't allow bonding
ensure_no_existing_bond(&owner, deps.storage)?;
ensure_no_existing_bond(&info.sender, deps.storage)?;
// check if somebody else has already bonded a gateway with this identity
if let Some(existing_bond) =
storage::gateways().may_load(deps.storage, &gateway.identity_key)?
{
if existing_bond.owner != owner {
if existing_bond.owner != info.sender {
return Err(MixnetContractError::DuplicateGateway {
owner: existing_bond.owner,
});
@@ -92,105 +46,62 @@ pub(crate) fn _try_add_gateway(
// check if this sender actually owns the gateway by checking the signature
verify_gateway_bonding_signature(
deps.as_ref(),
owner.clone(),
proxy.clone(),
info.sender.clone(),
pledge.clone(),
gateway.clone(),
owner_signature,
)?;
// update the signing nonce associated with this sender so that the future signature would be made on the new value
signing_storage::increment_signing_nonce(deps.storage, owner.clone())?;
signing_storage::increment_signing_nonce(deps.storage, info.sender.clone())?;
let gateway_identity = gateway.identity_key.clone();
let bond = GatewayBond::new(
pledge.clone(),
owner.clone(),
info.sender.clone(),
env.block.height,
gateway,
proxy.clone(),
);
storage::gateways().save(deps.storage, bond.identity(), &bond)?;
Ok(Response::new().add_event(new_gateway_bonding_event(
&owner,
&proxy,
&info.sender,
&pledge,
&gateway_identity,
)))
}
pub fn try_remove_gateway_on_behalf(
pub(crate) fn try_remove_gateway(
deps: DepsMut<'_>,
info: MessageInfo,
owner: String,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let proxy = info.sender;
let owner = deps.api.addr_validate(&owner)?;
_try_remove_gateway(deps, owner, Some(proxy))
}
pub fn try_remove_gateway(
deps: DepsMut<'_>,
info: MessageInfo,
) -> Result<Response, MixnetContractError> {
_try_remove_gateway(deps, info.sender, None)
}
pub(crate) fn _try_remove_gateway(
deps: DepsMut<'_>,
owner: Addr,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
// try to find the node of the sender
let gateway_bond = match storage::gateways()
.idx
.owner
.item(deps.storage, owner.clone())?
.item(deps.storage, info.sender.clone())?
{
Some(record) => record.1,
None => return Err(MixnetContractError::NoAssociatedGatewayBond { owner }),
None => return Err(MixnetContractError::NoAssociatedGatewayBond { owner: info.sender }),
};
if proxy != gateway_bond.proxy {
return Err(MixnetContractError::ProxyMismatch {
existing: gateway_bond
.proxy
.map_or_else(|| "None".to_string(), |a| a.as_str().to_string()),
incoming: proxy.map_or_else(|| "None".to_string(), |a| a.as_str().to_string()),
});
}
// send bonded funds back to the bond owner
let return_tokens = BankMsg::Send {
to_address: proxy.as_ref().unwrap_or(&owner).to_string(),
to_address: info.sender.to_string(),
amount: vec![gateway_bond.pledge_amount()],
};
// remove the bond
storage::gateways().remove(deps.storage, gateway_bond.identity())?;
let mut response = Response::new().add_message(return_tokens);
if let Some(proxy) = &proxy {
let msg = VestingContractExecuteMsg::TrackUnbondGateway {
owner: owner.as_str().to_string(),
amount: gateway_bond.pledge_amount(),
};
let track_unbond_message = wasm_execute(proxy, &msg, vec![])?;
response = response.add_message(track_unbond_message);
}
Ok(response.add_event(new_gateway_unbonding_event(
&owner,
&proxy,
&gateway_bond.pledge_amount,
gateway_bond.identity(),
)))
Ok(Response::new()
.add_message(return_tokens)
.add_event(new_gateway_unbonding_event(
&info.sender,
&gateway_bond.pledge_amount,
gateway_bond.identity(),
)))
}
pub(crate) fn try_update_gateway_config(
@@ -198,36 +109,9 @@ pub(crate) fn try_update_gateway_config(
info: MessageInfo,
new_config: GatewayConfigUpdate,
) -> Result<Response, MixnetContractError> {
let owner = info.sender;
_try_update_gateway_config(deps, new_config, owner, None)
}
let existing_bond = must_get_gateway_bond_by_owner(deps.storage, &info.sender)?;
let cfg_update_event = new_gateway_config_update_event(&info.sender, &new_config);
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);
// clippy beta 1.70.0-beta.1 false positive
#[allow(clippy::redundant_clone)]
let mut updated_bond = existing_bond.clone();
updated_bond.gateway.host = new_config.host;
updated_bond.gateway.mix_port = new_config.mix_port;
@@ -254,10 +138,10 @@ pub mod tests {
use crate::mixnet_contract_settings::storage::minimum_gateway_pledge;
use crate::support::tests;
use crate::support::tests::fixtures;
use crate::support::tests::fixtures::{good_gateway_pledge, good_mixnode_pledge};
use crate::support::tests::fixtures::good_mixnode_pledge;
use crate::support::tests::test_helpers::TestSetup;
use cosmwasm_std::testing::mock_info;
use cosmwasm_std::Uint128;
use cosmwasm_std::{Addr, Uint128};
use mixnet_contract_common::ExecuteMsg;
#[test]
@@ -392,42 +276,12 @@ pub mod tests {
.unwrap();
assert_eq!(1, updated_nonce);
_try_remove_gateway(test.deps_mut(), Addr::unchecked(sender), None).unwrap();
try_remove_gateway(test.deps_mut(), info.clone()).unwrap();
let res = try_add_gateway(test.deps_mut(), env, info, gateway, signature);
assert_eq!(res, Err(MixnetContractError::InvalidEd25519Signature));
}
#[test]
fn gateway_add_with_illegal_proxy() {
let mut test = TestSetup::new();
let env = test.env();
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let owner = "alice";
let (gateway, sig) = test.gateway_with_signature(owner, None);
let res = try_add_gateway_on_behalf(
test.deps_mut(),
env,
mock_info(illegal_proxy.as_ref(), &good_gateway_pledge()),
gateway,
owner.to_string(),
sig,
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract
}
)
}
#[test]
fn gateway_remove() {
let mut test = TestSetup::new();
@@ -495,7 +349,6 @@ pub mod tests {
.add_message(expected_message)
.add_event(new_gateway_unbonding_event(
&Addr::unchecked("fred"),
&None,
&tests::fixtures::good_gateway_pledge()[0],
&fred_identity,
));
@@ -510,33 +363,6 @@ pub mod tests {
assert_eq!(&Addr::unchecked("bob"), nodes[0].owner());
}
#[test]
fn gateway_remove_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 res = try_remove_gateway_on_behalf(
test.deps_mut(),
mock_info(illegal_proxy.as_ref(), &good_gateway_pledge()),
owner.to_string(),
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract
}
)
}
#[test]
fn update_gateway_config() {
let mut test = TestSetup::new();
@@ -561,22 +387,6 @@ pub mod tests {
);
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());
@@ -591,39 +401,4 @@ pub mod tests {
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
}
)
}
}
+41 -441
View File
@@ -24,7 +24,7 @@ use crate::interval::storage;
use crate::mixnodes::helpers::{cleanup_post_unbond_mixnode_storage, get_mixnode_details_by_id};
use crate::mixnodes::storage as mixnodes_storage;
use crate::rewards::storage as rewards_storage;
use crate::support::helpers::{send_to_proxy_or_owner, VestingTracking};
use crate::support::helpers::AttachSendTokens;
pub(crate) trait ContractExecutableEvent {
// note: the error only means a HARD error like we failed to read from storage.
@@ -40,7 +40,6 @@ pub(crate) fn delegate(
owner: Addr,
mix_id: MixId,
amount: Coin,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
// check if the target node still exists (it might have unbonded between this event getting created
// and being executed). Do note that it's absolutely possible for a mixnode to get immediately
@@ -56,20 +55,9 @@ pub(crate) fn delegate(
_ => {
// if mixnode is no longer bonded or in the process of unbonding, return the tokens back to the
// delegator;
// (read the notes regarding possible epoch progressiong halting behaviour in `maybe_add_track_undelegation_message`)
let return_tokens = send_to_proxy_or_owner(&proxy, &owner, vec![amount.clone()]);
let response = Response::new()
.add_message(return_tokens)
.add_event(new_delegation_on_unbonded_node_event(
&owner, &proxy, mix_id,
))
.maybe_add_track_vesting_undelegation_message(
deps.storage,
proxy,
owner.to_string(),
mix_id,
amount,
)?;
.send_tokens(&owner, amount.clone())
.add_event(new_delegation_on_unbonded_node_event(&owner, mix_id));
return Ok(response);
}
@@ -84,7 +72,7 @@ pub(crate) fn delegate(
// if there's an existing delegation, then withdraw the full reward and create a new delegation
// with the sum of both
let storage_key = Delegation::generate_storage_key(mix_id, &owner, proxy.as_ref());
let storage_key = Delegation::generate_storage_key(mix_id, &owner, None);
let old_delegation = if let Some(existing_delegation) =
delegations_storage::delegations().may_load(deps.storage, storage_key.clone())?
{
@@ -106,7 +94,6 @@ pub(crate) fn delegate(
let cosmos_event = new_delegation_event(
created_at,
&owner,
&proxy,
&new_delegation_amount,
mix_id,
mix_rewarding.total_unit_reward,
@@ -118,7 +105,6 @@ pub(crate) fn delegate(
mix_rewarding.total_unit_reward,
stored_delegation_amount,
env.block.height,
proxy,
);
// save on reading since `.save()` would have attempted to read old data that we already have on hand
@@ -138,11 +124,10 @@ pub(crate) fn undelegate(
created_at: BlockHeight,
owner: Addr,
mix_id: MixId,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
// see if the delegation still exists (in case of impatient user who decided to send multiple
// undelegation requests in an epoch)
let storage_key = Delegation::generate_storage_key(mix_id, &owner, proxy.as_ref());
let storage_key = Delegation::generate_storage_key(mix_id, &owner, None);
let delegation = match delegations_storage::delegations().may_load(deps.storage, storage_key)? {
None => return Ok(Response::default()),
Some(delegation) => delegation,
@@ -155,18 +140,9 @@ pub(crate) fn undelegate(
let tokens_to_return =
delegations::helpers::undelegate(deps.storage, delegation, mix_rewarding)?;
// (read the notes regarding possible epoch progressiong halting behaviour in `maybe_add_track_undelegation_message`)
let return_tokens = send_to_proxy_or_owner(&proxy, &owner, vec![tokens_to_return.clone()]);
let response = Response::new()
.add_message(return_tokens)
.add_event(new_undelegation_event(created_at, &owner, &proxy, mix_id))
.maybe_add_track_vesting_undelegation_message(
deps.storage,
proxy,
owner.to_string(),
mix_id,
tokens_to_return,
)?;
.send_tokens(&owner, tokens_to_return.clone())
.add_event(new_undelegation_event(created_at, &owner, mix_id));
Ok(response)
}
@@ -197,25 +173,15 @@ pub(crate) fn unbond_mixnode(
.rewarding_details
.operator_pledge_with_reward(rewarding_denom);
let proxy = &node_details.bond_information.proxy;
let owner = &node_details.bond_information.owner;
// send bonded funds (alongside all earned rewards) to the bond owner
let return_tokens = send_to_proxy_or_owner(proxy, owner, vec![tokens.clone()]);
// remove the bond and if there are no delegations left, also the rewarding information
// decrement the associated layer count
cleanup_post_unbond_mixnode_storage(deps.storage, env, &node_details)?;
let response = Response::new()
.add_message(return_tokens)
.add_event(new_mixnode_unbonding_event(created_at, mix_id))
.maybe_add_track_vesting_unbond_mixnode_message(
deps.storage,
proxy.clone(),
owner.clone().into_string(),
tokens,
)?;
.send_tokens(owner, tokens.clone())
.add_event(new_mixnode_unbonding_event(created_at, mix_id));
Ok(response)
}
@@ -311,12 +277,8 @@ pub(crate) fn decrease_pledge(
updated_bond.original_pledge.amount -= decrease_by.amount;
updated_rewarding.decrease_operator_uint128(decrease_by.amount)?;
let proxy = &mix_details.bond_information.proxy;
let owner = &mix_details.bond_information.owner;
// send the removed tokens back to the operator
let return_tokens = send_to_proxy_or_owner(proxy, owner, vec![decrease_by.clone()]);
// update all: bond information, rewarding details and pending pledge changes
mixnodes_storage::mixnode_bonds().replace(
deps.storage,
@@ -328,14 +290,8 @@ pub(crate) fn decrease_pledge(
mixnodes_storage::PENDING_MIXNODE_CHANGES.save(deps.storage, mix_id, &pending_changes)?;
let response = Response::new()
.add_message(return_tokens)
.add_event(new_pledge_decrease_event(created_at, mix_id, &decrease_by))
.maybe_add_track_vesting_decrease_mixnode_pledge(
deps.storage,
proxy.clone(),
owner.clone().to_string(),
decrease_by,
)?;
.send_tokens(owner, decrease_by.clone())
.add_event(new_pledge_decrease_event(created_at, mix_id, &decrease_by));
Ok(response)
}
@@ -349,13 +305,11 @@ impl ContractExecutableEvent for PendingEpochEventData {
owner,
mix_id,
amount,
proxy,
} => delegate(deps, env, self.created_at, owner, mix_id, amount, proxy),
PendingEpochEventKind::Undelegate {
owner,
mix_id,
proxy,
} => undelegate(deps, self.created_at, owner, mix_id, proxy),
..
} => delegate(deps, env, self.created_at, owner, mix_id, amount),
PendingEpochEventKind::Undelegate { owner, mix_id, .. } => {
undelegate(deps, self.created_at, owner, mix_id)
}
PendingEpochEventKind::PledgeMore { mix_id, amount } => {
increase_pledge(deps, self.created_at, mix_id, amount)
}
@@ -472,33 +426,25 @@ impl ContractExecutableEvent for PendingIntervalEventData {
#[cfg(test)]
mod tests {
use std::time::Duration;
use cosmwasm_std::Decimal;
use mixnet_contract_common::Percent;
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
use super::*;
use crate::support::tests::test_helpers;
use crate::support::tests::test_helpers::{assert_decimals, TestSetup};
use super::*;
use cosmwasm_std::Decimal;
use mixnet_contract_common::Percent;
use std::time::Duration;
// note that authorization and basic validation has already been performed for all of those
// before being pushed onto the event queues
#[cfg(test)]
mod delegating {
use cosmwasm_std::testing::mock_info;
use cosmwasm_std::{coin, to_binary, CosmosMsg, WasmMsg};
use mixnet_contract_common::rewarding::helpers::truncate_reward_amount;
use super::*;
use crate::mixnodes::transactions::try_remove_mixnode;
use crate::support::tests::fixtures::TEST_COIN_DENOM;
use crate::support::tests::test_helpers::get_bank_send_msg;
use super::*;
use cosmwasm_std::coin;
use cosmwasm_std::testing::mock_info;
use mixnet_contract_common::rewarding::helpers::truncate_reward_amount;
#[test]
fn returns_the_tokens_if_mixnode_has_unbonded() {
@@ -523,7 +469,6 @@ mod tests {
Addr::unchecked(owner1),
mix_id,
delegation_coin.clone(),
None,
)
.unwrap();
@@ -549,7 +494,6 @@ mod tests {
Addr::unchecked(owner2),
mix_id,
delegation_coin.clone(),
None,
)
.unwrap();
let storage_key =
@@ -588,7 +532,6 @@ mod tests {
Addr::unchecked(owner1),
mix_id,
delegation_coin.clone(),
None,
)
.unwrap();
@@ -614,7 +557,6 @@ mod tests {
Addr::unchecked(owner2),
mix_id,
delegation_coin.clone(),
None,
)
.unwrap();
let storage_key =
@@ -650,7 +592,6 @@ mod tests {
Addr::unchecked(owner),
mix_id,
delegation_coin_new,
None,
)
.unwrap();
@@ -725,7 +666,6 @@ mod tests {
Addr::unchecked(owner),
mix_id,
delegation_coin_new,
None,
)
.unwrap();
@@ -797,7 +737,6 @@ mod tests {
Addr::unchecked(owner),
mix_id,
delegation_coin.clone(),
None,
)
.unwrap();
assert!(get_bank_send_msg(&res).is_none());
@@ -816,117 +755,13 @@ mod tests {
Decimal::from_atomics(delegation, 0).unwrap()
)
}
#[test]
fn attaches_vesting_contract_track_message_if_tokens_are_returned() {
let mut test = TestSetup::new();
let mix_id = test.add_dummy_mixnode("mix-owner", None);
let delegation = 120_000_000u128;
let delegation_coin = coin(delegation, TEST_COIN_DENOM);
let owner = "delegator";
let env = test.env();
unbond_mixnode(test.deps_mut(), &env, 123, mix_id).unwrap();
let vesting_contract = test.vesting_contract();
// for a fresh delegation, nothing was added to the storage either
let res_vesting = delegate(
test.deps_mut(),
&env,
123,
Addr::unchecked(owner),
mix_id,
delegation_coin.clone(),
Some(vesting_contract.clone()),
)
.unwrap();
let storage_key = Delegation::generate_storage_key(
mix_id,
&Addr::unchecked(owner),
Some(vesting_contract.clone()).as_ref(),
);
assert!(delegations_storage::delegations()
.may_load(test.deps().storage, storage_key)
.unwrap()
.is_none());
// and all tokens are returned back to the proxy
let (receiver, sent_amount) = get_bank_send_msg(&res_vesting).unwrap();
assert_eq!(receiver, vesting_contract.as_str());
assert_eq!(sent_amount[0], delegation_coin);
// and we get appropriate track message
let mut found_track = true;
for msg in &res_vesting.messages {
if let CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr,
msg,
funds,
}) = &msg.msg
{
found_track = true;
assert_eq!(contract_addr, vesting_contract.as_str());
let expected_msg = to_binary(&VestingContractExecuteMsg::TrackUndelegation {
owner: owner.to_string(),
mix_id,
amount: delegation_coin.clone(),
})
.unwrap();
assert_eq!(&expected_msg, msg);
assert!(funds.is_empty())
}
}
assert!(found_track);
}
#[test]
fn returns_error_for_illegal_proxy() {
let mut test = TestSetup::new();
let mix_id = test.add_dummy_mixnode("mix-owner", None);
let delegation = 120_000_000u128;
let delegation_coin = coin(delegation, TEST_COIN_DENOM);
let owner = "delegator";
let dummy_proxy = Addr::unchecked("not-vesting-contract");
let env = test.env();
unbond_mixnode(test.deps_mut(), &env, 123, mix_id).unwrap();
let vesting_contract = test.vesting_contract();
// try to add illegal delegation (with invalid proxy)
let res_other_proxy = delegate(
test.deps_mut(),
&env,
123,
Addr::unchecked(owner),
mix_id,
delegation_coin,
Some(dummy_proxy.clone()),
)
.unwrap_err();
assert_eq!(
res_other_proxy,
MixnetContractError::ProxyIsNotVestingContract {
received: dummy_proxy,
vesting_contract,
}
);
}
}
#[cfg(test)]
mod undelegating {
use cosmwasm_std::{coin, to_binary, CosmosMsg, WasmMsg};
use mixnet_contract_common::rewarding::helpers::truncate_reward_amount;
use crate::support::tests::fixtures::TEST_COIN_DENOM;
use crate::support::tests::test_helpers::get_bank_send_msg;
use super::*;
use crate::support::tests::test_helpers::get_bank_send_msg;
use mixnet_contract_common::rewarding::helpers::truncate_reward_amount;
#[test]
fn doesnt_return_any_tokens_if_it_doesnt_exist() {
@@ -935,7 +770,7 @@ mod tests {
let owner = Addr::unchecked("delegator");
let res = undelegate(test.deps_mut(), 123, owner, mix_id, None).unwrap();
let res = undelegate(test.deps_mut(), 123, owner, mix_id).unwrap();
assert!(get_bank_send_msg(&res).is_none());
}
@@ -950,7 +785,7 @@ mod tests {
// this should never happen in actual code, but if we manually messed something up,
// lets make sure this throws an error
rewards_storage::MIXNODE_REWARDING.remove(test.deps_mut().storage, mix_id);
let res = undelegate(test.deps_mut(), 123, owner, mix_id, None);
let res = undelegate(test.deps_mut(), 123, owner, mix_id);
assert!(matches!(
res,
Err(MixnetContractError::InconsistentState { .. })
@@ -996,8 +831,7 @@ mod tests {
let expected_return = delegation + truncated_reward.u128();
let res =
undelegate(test.deps_mut(), 123, Addr::unchecked(owner), mix_id, None).unwrap();
let res = undelegate(test.deps_mut(), 123, Addr::unchecked(owner), mix_id).unwrap();
let (receiver, sent_amount) = get_bank_send_msg(&res).unwrap();
assert_eq!(receiver, owner);
assert_eq!(sent_amount[0].amount.u128(), expected_return);
@@ -1015,117 +849,19 @@ mod tests {
assert!(rewarding.delegates.is_zero());
assert_eq!(rewarding.unique_delegations, 0);
}
#[test]
fn attaches_vesting_contract_track_message_if_tokens_are_returned() {
let mut test = TestSetup::new();
let mix_id = test.add_dummy_mixnode("mix-owner", None);
let delegation = 120_000_000u128;
let delegation_coin = coin(delegation, TEST_COIN_DENOM);
let owner = "delegator";
let vesting_contract = test.vesting_contract();
test.add_immediate_delegation_with_legal_proxy(owner, delegation, mix_id);
let res_vesting = undelegate(
test.deps_mut(),
123,
Addr::unchecked(owner),
mix_id,
Some(vesting_contract.clone()),
)
.unwrap();
let storage_key = Delegation::generate_storage_key(
mix_id,
&Addr::unchecked(owner),
Some(vesting_contract.clone()).as_ref(),
);
assert!(delegations_storage::delegations()
.may_load(test.deps().storage, storage_key)
.unwrap()
.is_none());
// and all tokens are returned back to the proxy
let (receiver, sent_amount) = get_bank_send_msg(&res_vesting).unwrap();
assert_eq!(receiver, vesting_contract.as_str());
assert_eq!(sent_amount[0], delegation_coin);
// and we get appropriate track message
let mut found_track = true;
for msg in &res_vesting.messages {
if let CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr,
msg,
funds,
}) = &msg.msg
{
found_track = true;
assert_eq!(contract_addr, vesting_contract.as_str());
let expected_msg = to_binary(&VestingContractExecuteMsg::TrackUndelegation {
owner: owner.to_string(),
mix_id,
amount: delegation_coin.clone(),
})
.unwrap();
assert_eq!(&expected_msg, msg);
assert!(funds.is_empty())
}
}
assert!(found_track);
}
#[test]
fn returns_error_for_illegal_proxy() {
let mut test = TestSetup::new();
let mix_id = test.add_dummy_mixnode("mix-owner", None);
let delegation = 120_000_000u128;
let owner = "delegator1";
let vesting_contract = test.vesting_contract();
let dummy_proxy = Addr::unchecked("not-vesting-contract");
test.add_immediate_delegation_with_illegal_proxy(
owner,
delegation,
mix_id,
dummy_proxy.clone(),
);
let res_other_proxy = undelegate(
test.deps_mut(),
123,
Addr::unchecked(owner),
mix_id,
Some(dummy_proxy.clone()),
)
.unwrap_err();
assert_eq!(
res_other_proxy,
MixnetContractError::ProxyIsNotVestingContract {
received: dummy_proxy,
vesting_contract,
}
);
}
}
#[cfg(test)]
mod mixnode_unbonding {
use cosmwasm_std::{coin, to_binary, CosmosMsg, Uint128, WasmMsg};
use super::*;
use crate::mixnodes::storage as mixnodes_storage;
use crate::mixnodes::transactions::{try_decrease_pledge, try_increase_pledge};
use crate::support::tests::test_helpers::get_bank_send_msg;
use cosmwasm_std::testing::mock_info;
use cosmwasm_std::Uint128;
use mixnet_contract_common::mixnode::{PendingMixNodeChanges, UnbondedMixnode};
use mixnet_contract_common::rewarding::helpers::truncate_reward_amount;
use crate::mixnodes::storage as mixnodes_storage;
use crate::mixnodes::transactions::{_try_decrease_pledge, _try_increase_pledge};
use crate::support::tests::fixtures::TEST_COIN_DENOM;
use crate::support::tests::test_helpers::get_bank_send_msg;
use super::*;
#[test]
fn returns_hard_error_if_mixnode_doesnt_exist() {
// this should have never happened so hard error MUST be thrown here
@@ -1150,12 +886,10 @@ mod tests {
let pledge = Uint128::new(250_000_000);
let mix_id = test.add_dummy_mixnode(owner, Some(pledge));
_try_increase_pledge(
try_increase_pledge(
test.deps_mut(),
env.clone(),
change.clone(),
Addr::unchecked(owner),
None,
mock_info(owner, &change.clone()),
)
.unwrap();
@@ -1170,12 +904,11 @@ mod tests {
let pledge = Uint128::new(250_000_000);
let mix_id = test.add_dummy_mixnode(owner, Some(pledge));
_try_decrease_pledge(
try_decrease_pledge(
test.deps_mut(),
env.clone(),
mock_info(owner, &[]),
change[0].clone(),
Addr::unchecked(owner),
None,
)
.unwrap();
@@ -1263,79 +996,6 @@ mod tests {
0
)
}
#[test]
fn attaches_vesting_contract_track_message_if_tokens_are_returned() {
let mut test = TestSetup::new();
let vesting_contract = test.vesting_contract();
let pledge = Uint128::new(250_000_000);
let pledge_coin = coin(250_000_000, TEST_COIN_DENOM);
let owner = "mix-owner1";
let mix_id_vesting = test.add_dummy_mixnode_with_legal_proxy(owner, Some(pledge));
let env = test.env();
let res = unbond_mixnode(test.deps_mut(), &env, 123, mix_id_vesting).unwrap();
assert!(mixnodes_storage::mixnode_bonds()
.may_load(test.deps().storage, mix_id_vesting)
.unwrap()
.is_none());
// and all tokens are returned back to the proxy
let (receiver, sent_amount) = get_bank_send_msg(&res).unwrap();
assert_eq!(receiver, vesting_contract.as_str());
assert_eq!(sent_amount[0], pledge_coin);
// and we get appropriate track message
let mut found_track = true;
for msg in &res.messages {
if let CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr,
msg,
funds,
}) = &msg.msg
{
found_track = true;
assert_eq!(contract_addr, vesting_contract.as_str());
let expected_msg = to_binary(&VestingContractExecuteMsg::TrackUnbondMixnode {
owner: owner.to_string(),
amount: pledge_coin.clone(),
})
.unwrap();
assert_eq!(&expected_msg, msg);
assert!(funds.is_empty())
}
}
assert!(found_track);
}
#[test]
fn returns_error_for_illegal_proxy() {
let mut test = TestSetup::new();
let dummy_proxy = Addr::unchecked("not-vesting-contract");
let env = test.env();
let vesting_contract = test.vesting_contract();
let owner = "mix-owner";
let pledge = Uint128::new(250_000_000);
let mix_id_illegal_proxy =
test.add_dummy_mixnode_with_illegal_proxy(owner, Some(pledge), dummy_proxy.clone());
// this is the halting issue that should have never occurred
let res_other_proxy =
unbond_mixnode(test.deps_mut(), &env, 123, mix_id_illegal_proxy).unwrap_err();
assert_eq!(
res_other_proxy,
MixnetContractError::ProxyIsNotVestingContract {
received: dummy_proxy,
vesting_contract,
}
);
}
}
#[cfg(test)]
@@ -1615,11 +1275,9 @@ mod tests {
#[cfg(test)]
mod decreasing_pledge {
use cosmwasm_std::{to_binary, BankMsg, CosmosMsg, Uint128, WasmMsg};
use mixnet_contract_common::rewarding::helpers::truncate_reward_amount;
use super::*;
use cosmwasm_std::{BankMsg, CosmosMsg, Uint128};
use mixnet_contract_common::rewarding::helpers::truncate_reward_amount;
#[test]
fn returns_hard_error_if_mixnode_doesnt_exist() {
@@ -1699,64 +1357,6 @@ mod tests {
)
}
#[test]
fn returns_tokens_back_to_the_proxy_if_bonded_with_vesting() {
let mut test = TestSetup::new();
let owner = "mix-owner";
let mix_id = test.add_dummy_mixnode_with_legal_proxy(owner, None);
test.set_pending_pledge_change(mix_id, None);
let vesting_contract = test.vesting_contract();
let amount = test.coin(12345);
let res = decrease_pledge(test.deps_mut(), 123, mix_id, amount.clone()).unwrap();
assert_eq!(res.messages.len(), 2);
assert_eq!(
res.messages[0].msg,
CosmosMsg::Bank(BankMsg::Send {
to_address: vesting_contract.to_string(),
amount: vec![amount],
})
)
}
#[test]
fn attaches_vesting_track_message() {
let mut test = TestSetup::new();
let mix_id_no_proxy = test.add_dummy_mixnode("mix-owner1", None);
test.set_pending_pledge_change(mix_id_no_proxy, None);
let mix_id_proxy = test.add_dummy_mixnode_with_legal_proxy("mix-owner2", None);
test.set_pending_pledge_change(mix_id_proxy, None);
let vesting_contract = test.vesting_contract();
let amount = test.coin(12345);
let res_no_proxy =
decrease_pledge(test.deps_mut(), 123, mix_id_no_proxy, amount.clone()).unwrap();
// nothing was attached (apart from bank message tested in `returns_tokens_back_to_the_owner`)
// because it wasn't done with proxy!
assert_eq!(res_no_proxy.messages.len(), 1);
let res_proxy =
decrease_pledge(test.deps_mut(), 123, mix_id_proxy, amount.clone()).unwrap();
assert_eq!(res_proxy.messages.len(), 2);
assert_eq!(
res_proxy.messages[1].msg,
CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: vesting_contract.to_string(),
msg: to_binary(&VestingContractExecuteMsg::TrackDecreasePledge {
owner: "mix-owner2".to_string(),
amount,
})
.unwrap(),
funds: vec![],
})
);
}
#[test]
fn without_any_events_in_between_is_equivalent_to_pledging_the_same_amount_immediately() {
let mut test = TestSetup::new();
+4 -10
View File
@@ -159,11 +159,8 @@ mod tests {
}
fn push_dummy_epoch_action(test: &mut TestSetup) {
let dummy_action = PendingEpochEventKind::Undelegate {
owner: Addr::unchecked("foomp"),
mix_id: test.rng.next_u32(),
proxy: None,
};
let dummy_action =
PendingEpochEventKind::new_undelegate(Addr::unchecked("foomp"), test.rng.next_u32());
let env = test.env();
storage::push_new_epoch_event(test.deps_mut().storage, &env, dummy_action).unwrap();
}
@@ -571,11 +568,8 @@ mod tests {
);
// it exists
let dummy_action = PendingEpochEventKind::Undelegate {
owner: Addr::unchecked("foomp"),
mix_id: test.rng.next_u32(),
proxy: None,
};
let dummy_action =
PendingEpochEventKind::new_undelegate(Addr::unchecked("foomp"), test.rng.next_u32());
let env = test.env();
storage::push_new_epoch_event(test.deps_mut().storage, &env, dummy_action.clone()).unwrap();
let expected = PendingEpochEventResponse {
+8 -10
View File
@@ -222,11 +222,10 @@ mod tests {
let env = test.env();
for _ in 0..500 {
let dummy_action = PendingEpochEventKind::Undelegate {
owner: Addr::unchecked("foomp"),
mix_id: test.rng.next_u32(),
proxy: None,
};
let dummy_action = PendingEpochEventKind::new_undelegate(
Addr::unchecked("foomp"),
test.rng.next_u32(),
);
let id = push_new_epoch_event(test.deps_mut().storage, &env, dummy_action).unwrap();
let expected = EPOCH_EVENT_ID_COUNTER.load(test.deps().storage).unwrap();
assert_eq!(expected, id);
@@ -235,11 +234,10 @@ mod tests {
test.execute_all_pending_events();
for _ in 0..10 {
let dummy_action = PendingEpochEventKind::Undelegate {
owner: Addr::unchecked("foomp"),
mix_id: test.rng.next_u32(),
proxy: None,
};
let dummy_action = PendingEpochEventKind::new_undelegate(
Addr::unchecked("foomp"),
test.rng.next_u32(),
);
let id = push_new_epoch_event(test.deps_mut().storage, &env, dummy_action).unwrap();
let expected = EPOCH_EVENT_ID_COUNTER.load(test.deps().storage).unwrap();
assert_eq!(expected, id);
+17 -57
View File
@@ -373,18 +373,14 @@ mod tests {
use crate::support::tests::test_helpers::TestSetup;
use cosmwasm_std::Addr;
use mixnet_contract_common::pending_events::PendingEpochEventKind;
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
fn push_n_dummy_epoch_actions(test: &mut TestSetup, n: usize) {
// if you attempt to undelegate non-existent delegation,
// it will return an empty response, but will not fail
let env = test.env();
for i in 0..n {
let dummy_action = PendingEpochEventKind::Undelegate {
owner: Addr::unchecked("foomp"),
mix_id: i as MixId,
proxy: None,
};
let dummy_action =
PendingEpochEventKind::new_undelegate(Addr::unchecked("foomp"), i as MixId);
storage::push_new_epoch_event(test.deps_mut().storage, &env, dummy_action).unwrap();
}
}
@@ -406,7 +402,7 @@ mod tests {
mod performing_pending_epoch_actions {
use super::*;
use crate::support::tests::fixtures::TEST_COIN_DENOM;
use cosmwasm_std::{coin, coins, wasm_execute, BankMsg, Empty, SubMsg};
use cosmwasm_std::{coin, coins, BankMsg, Empty, SubMsg};
use mixnet_contract_common::events::{
new_active_set_update_event, new_delegation_on_unbonded_node_event,
new_undelegation_event,
@@ -495,7 +491,6 @@ mod tests {
#[test]
fn catches_all_events_and_messages_from_executed_actions() {
let mut test = TestSetup::new();
let vesting_contract = test.vesting_contract();
let env = test.env();
let legit_mix = test.add_dummy_mixnode("mix-owner", None);
@@ -509,17 +504,15 @@ mod tests {
// delegate to node that doesn't exist,
// we expect to receive BankMsg with tokens being returned,
// and event regarding delegation
let non_existent_delegation = PendingEpochEventKind::Delegate {
owner: Addr::unchecked("foomp"),
mix_id: 123,
amount: coin(123, TEST_COIN_DENOM),
proxy: None,
};
let non_existent_delegation = PendingEpochEventKind::new_delegate(
Addr::unchecked("foomp"),
123,
coin(123, TEST_COIN_DENOM),
);
storage::push_new_epoch_event(test.deps_mut().storage, &env, non_existent_delegation)
.unwrap();
expected_events.push(new_delegation_on_unbonded_node_event(
&Addr::unchecked("foomp"),
&None,
123,
));
expected_messages.push(SubMsg::new(BankMsg::Send {
@@ -527,33 +520,6 @@ mod tests {
amount: coins(123, TEST_COIN_DENOM),
}));
// delegation to node that doesn't exist with vesting contract
// we expect the same as above PLUS TrackUndelegation message
let non_existent_delegation = PendingEpochEventKind::Delegate {
owner: Addr::unchecked("foomp2"),
mix_id: 123,
amount: coin(123, TEST_COIN_DENOM),
proxy: Some(vesting_contract.clone()),
};
storage::push_new_epoch_event(test.deps_mut().storage, &env, non_existent_delegation)
.unwrap();
expected_events.push(new_delegation_on_unbonded_node_event(
&Addr::unchecked("foomp2"),
&Some(vesting_contract.clone()),
123,
));
expected_messages.push(SubMsg::new(BankMsg::Send {
to_address: vesting_contract.clone().into_string(),
amount: coins(123, TEST_COIN_DENOM),
}));
let msg = VestingContractExecuteMsg::TrackUndelegation {
owner: "foomp2".to_string(),
mix_id: 123,
amount: coin(123, TEST_COIN_DENOM),
};
let track_undelegate_message = wasm_execute(vesting_contract, &msg, vec![]).unwrap();
expected_messages.push(SubMsg::new(track_undelegate_message));
// updating active set should only emit events and no cosmos messages
let action_with_event = PendingEpochEventKind::UpdateActiveSetSize { new_size: 50 };
storage::push_new_epoch_event(test.deps_mut().storage, &env, action_with_event)
@@ -561,16 +527,12 @@ mod tests {
expected_events.push(new_active_set_update_event(env.block.height, 50));
// undelegation just returns tokens and emits event
let legit_undelegate = PendingEpochEventKind::Undelegate {
owner: delegator.clone(),
mix_id: legit_mix,
proxy: None,
};
let legit_undelegate =
PendingEpochEventKind::new_undelegate(delegator.clone(), legit_mix);
storage::push_new_epoch_event(test.deps_mut().storage, &env, legit_undelegate).unwrap();
expected_events.push(new_undelegation_event(
env.block.height,
&delegator,
&None,
legit_mix,
));
expected_messages.push(SubMsg::new(BankMsg::Send {
@@ -583,9 +545,9 @@ mod tests {
let mut expected = Response::new().add_events(expected_events);
expected.messages = expected_messages;
assert_eq!(res, expected);
assert_eq!(executed, 4);
assert_eq!(executed, 3);
assert_eq!(
4,
3,
storage::LAST_PROCESSED_EPOCH_EVENT
.load(test.deps().storage)
.unwrap()
@@ -1330,17 +1292,15 @@ mod tests {
let mut expected_messages: Vec<SubMsg<Empty>> = Vec::new();
// epoch event
let non_existent_delegation = PendingEpochEventKind::Delegate {
owner: Addr::unchecked("foomp"),
mix_id: 123,
amount: coin(123, TEST_COIN_DENOM),
proxy: None,
};
let non_existent_delegation = PendingEpochEventKind::new_delegate(
Addr::unchecked("foomp"),
123,
coin(123, TEST_COIN_DENOM),
);
storage::push_new_epoch_event(test.deps_mut().storage, &env, non_existent_delegation)
.unwrap();
expected_events.push(new_delegation_on_unbonded_node_event(
&Addr::unchecked("foomp"),
&None,
123,
));
expected_messages.push(SubMsg::new(BankMsg::Send {
+1
View File
@@ -19,3 +19,4 @@ mod support;
#[cfg(feature = "contract-testing")]
mod testing;
mod vesting_migration;
+1 -14
View File
@@ -97,7 +97,6 @@ pub(crate) fn save_new_mixnode(
mixnode: MixNode,
cost_params: MixNodeCostParams,
owner: Addr,
proxy: Option<Addr>,
pledge: Coin,
) -> Result<(MixId, Layer), MixnetContractError> {
let layer = assign_layer(storage)?;
@@ -105,15 +104,7 @@ pub(crate) fn save_new_mixnode(
let current_epoch = interval_storage::current_interval(storage)?.current_epoch_absolute_id();
let mixnode_rewarding = MixNodeRewarding::initialise_new(cost_params, &pledge, current_epoch)?;
let mixnode_bond = MixNodeBond::new(
mix_id,
owner,
pledge,
layer,
mixnode,
proxy,
env.block.height,
);
let mixnode_bond = MixNodeBond::new(mix_id, owner, pledge, layer, mixnode, env.block.height);
// save mixnode bond data
// note that this implicitly checks for uniqueness on identity key, sphinx key and owner
@@ -411,7 +402,6 @@ pub(crate) mod tests {
mixnode,
cost_params.clone(),
owner.clone(),
None,
pledge.clone(),
)
.unwrap();
@@ -444,7 +434,6 @@ pub(crate) mod tests {
mixnode,
cost_params.clone(),
Addr::unchecked("different-owner"),
None,
pledge.clone(),
);
assert!(res.is_err());
@@ -457,7 +446,6 @@ pub(crate) mod tests {
mixnode,
cost_params.clone(),
owner,
None,
pledge.clone(),
);
assert!(res.is_err());
@@ -471,7 +459,6 @@ pub(crate) mod tests {
mixnode,
cost_params,
Addr::unchecked("different-owner"),
None,
pledge,
);
assert!(res.is_err());
@@ -12,7 +12,6 @@ use nym_contracts_common::signing::Verifier;
pub(crate) fn verify_mixnode_bonding_signature(
deps: Deps<'_>,
sender: Addr,
proxy: Option<Addr>,
pledge: Coin,
mixnode: MixNode,
cost_params: MixNodeCostParams,
@@ -23,8 +22,7 @@ pub(crate) fn verify_mixnode_bonding_signature(
// reconstruct the payload
let nonce = signing_storage::get_signing_nonce(deps.storage, sender.clone())?;
let msg =
construct_mixnode_bonding_sign_payload(nonce, sender, proxy, pledge, mixnode, cost_params);
let msg = construct_mixnode_bonding_sign_payload(nonce, sender, pledge, mixnode, cost_params);
if deps.api.verify_message(msg, signature, &public_key)? {
Ok(())
+23 -576
View File
@@ -1,8 +1,7 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{coin, Addr, Coin, DepsMut, Env, MessageInfo, Response, Storage};
use cosmwasm_std::{coin, Coin, DepsMut, Env, MessageInfo, Response, Storage};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::events::{
new_mixnode_bonding_event, new_mixnode_config_update_event,
@@ -25,8 +24,7 @@ use crate::mixnodes::signature_helpers::verify_mixnode_bonding_signature;
use crate::signing::storage as signing_storage;
use crate::support::helpers::{
ensure_bonded, ensure_epoch_in_progress_state, ensure_is_authorized, ensure_no_existing_bond,
ensure_no_pending_pledge_changes, ensure_proxy_match, ensure_sent_by_vesting_contract,
validate_pledge,
ensure_no_pending_pledge_changes, validate_pledge,
};
use super::storage;
@@ -61,74 +59,24 @@ pub fn assign_mixnode_layer(
Ok(Response::default())
}
pub fn try_add_mixnode(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner_signature: MessageSignature,
) -> Result<Response, MixnetContractError> {
_try_add_mixnode(
deps,
env,
mix_node,
cost_params,
info.funds,
info.sender,
owner_signature,
None,
)
}
pub fn try_add_mixnode_on_behalf(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
mix_node: MixNode,
cost_params: MixNodeCostParams,
owner: String,
owner_signature: MessageSignature,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let proxy = info.sender;
let owner = deps.api.addr_validate(&owner)?;
_try_add_mixnode(
deps,
env,
mix_node,
cost_params,
info.funds,
owner,
owner_signature,
Some(proxy),
)
}
// I'm not entirely sure how to deal with this warning at the current moment
//
// TODO: perhaps also require the user to explicitly provide what it thinks is the current nonce
// so that we could return a better error message if it doesn't match?
#[allow(clippy::too_many_arguments)]
fn _try_add_mixnode(
pub(crate) fn try_add_mixnode(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
mixnode: MixNode,
cost_params: MixNodeCostParams,
pledge: Vec<Coin>,
owner: Addr,
owner_signature: MessageSignature,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
// check if the pledge contains any funds of the appropriate denomination
let minimum_pledge = mixnet_params_storage::minimum_mixnode_pledge(deps.storage)?;
let pledge = validate_pledge(pledge, minimum_pledge)?;
let pledge = validate_pledge(info.funds, minimum_pledge)?;
// if the client has an active bonded mixnode or gateway, don't allow bonding
// note that this has to be done explicitly as `UniqueIndex` constraint would not protect us
// against attempting to use different node types (i.e. gateways and mixnodes)
ensure_no_existing_bond(&owner, deps.storage)?;
ensure_no_existing_bond(&info.sender, deps.storage)?;
// there's no need to explicitly check whether there already exists mixnode with the same
// identity or sphinx keys as this is going to be done implicitly when attempting to save
@@ -137,8 +85,7 @@ fn _try_add_mixnode(
// check if this sender actually owns the mixnode by checking the signature
verify_mixnode_bonding_signature(
deps.as_ref(),
owner.clone(),
proxy.clone(),
info.sender.clone(),
pledge.clone(),
mixnode.clone(),
cost_params.clone(),
@@ -146,7 +93,7 @@ fn _try_add_mixnode(
)?;
// update the signing nonce associated with this sender so that the future signature would be made on the new value
signing_storage::increment_signing_nonce(deps.storage, owner.clone())?;
signing_storage::increment_signing_nonce(deps.storage, info.sender.clone())?;
let node_identity = mixnode.identity_key.clone();
let (node_id, layer) = save_new_mixnode(
@@ -154,14 +101,12 @@ fn _try_add_mixnode(
env,
mixnode,
cost_params,
owner.clone(),
proxy.clone(),
info.sender.clone(),
pledge.clone(),
)?;
Ok(Response::new().add_event(new_mixnode_bonding_event(
&owner,
&proxy,
&info.sender,
&pledge,
&node_identity,
node_id,
@@ -174,43 +119,19 @@ pub fn try_increase_pledge(
env: Env,
info: MessageInfo,
) -> Result<Response, MixnetContractError> {
_try_increase_pledge(deps, env, info.funds, info.sender, None)
}
pub fn try_increase_pledge_on_behalf(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
owner: String,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let proxy = info.sender;
let owner = deps.api.addr_validate(&owner)?;
_try_increase_pledge(deps, env, info.funds, owner, Some(proxy))
}
pub fn _try_increase_pledge(
deps: DepsMut<'_>,
env: Env,
increase: Vec<Coin>,
owner: Addr,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
let mix_details = get_mixnode_details_by_owner(deps.storage, owner.clone())?
.ok_or(MixnetContractError::NoAssociatedMixNodeBond { owner })?;
let mix_details = get_mixnode_details_by_owner(deps.storage, info.sender.clone())?
.ok_or(MixnetContractError::NoAssociatedMixNodeBond { owner: info.sender })?;
let mut pending_changes = mix_details.pending_changes;
let mix_id = mix_details.mix_id();
// increasing pledge is only allowed if the epoch is currently not in the process of being advanced
ensure_epoch_in_progress_state(deps.storage)?;
ensure_proxy_match(&proxy, &mix_details.bond_information.proxy)?;
ensure_bonded(&mix_details.bond_information)?;
ensure_no_pending_pledge_changes(&pending_changes)?;
let rewarding_denom = rewarding_denom(deps.storage)?;
let pledge_increase = validate_pledge(increase, coin(1, rewarding_denom))?;
let pledge_increase = validate_pledge(info.funds, coin(1, rewarding_denom))?;
let cosmos_event = new_pending_pledge_increase_event(mix_id, &pledge_increase);
@@ -232,39 +153,14 @@ pub fn try_decrease_pledge(
info: MessageInfo,
decrease_by: Coin,
) -> Result<Response, MixnetContractError> {
_try_decrease_pledge(deps, env, decrease_by, info.sender, None)
}
pub fn try_decrease_pledge_on_behalf(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
decrease_by: Coin,
owner: String,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let proxy = info.sender;
let owner = deps.api.addr_validate(&owner)?;
_try_decrease_pledge(deps, env, decrease_by, owner, Some(proxy))
}
pub fn _try_decrease_pledge(
deps: DepsMut<'_>,
env: Env,
decrease_by: Coin,
owner: Addr,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
let mix_details = get_mixnode_details_by_owner(deps.storage, owner.clone())?
.ok_or(MixnetContractError::NoAssociatedMixNodeBond { owner })?;
let mix_details = get_mixnode_details_by_owner(deps.storage, info.sender.clone())?
.ok_or(MixnetContractError::NoAssociatedMixNodeBond { owner: info.sender })?;
let mut pending_changes = mix_details.pending_changes;
let mix_id = mix_details.mix_id();
// decreasing pledge is only allowed if the epoch is currently not in the process of being advanced
ensure_epoch_in_progress_state(deps.storage)?;
ensure_proxy_match(&proxy, &mix_details.bond_information.proxy)?;
ensure_bonded(&mix_details.bond_information)?;
ensure_no_pending_pledge_changes(&pending_changes)?;
@@ -312,34 +208,12 @@ pub fn _try_decrease_pledge(
Ok(Response::new().add_event(cosmos_event))
}
pub fn try_remove_mixnode_on_behalf(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
owner: String,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let proxy = info.sender;
let owner = deps.api.addr_validate(&owner)?;
_try_remove_mixnode(deps, env, owner, Some(proxy))
}
pub fn try_remove_mixnode(
pub(crate) fn try_remove_mixnode(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
) -> Result<Response, MixnetContractError> {
_try_remove_mixnode(deps, env, info.sender, None)
}
pub(crate) fn _try_remove_mixnode(
deps: DepsMut<'_>,
env: Env,
owner: Addr,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
let existing_bond = must_get_mixnode_bond_by_owner(deps.storage, &owner)?;
let existing_bond = must_get_mixnode_bond_by_owner(deps.storage, &info.sender)?;
let pending_changes = storage::PENDING_MIXNODE_CHANGES
.may_load(deps.storage, existing_bond.mix_id)?
.unwrap_or_default();
@@ -348,15 +222,12 @@ pub(crate) fn _try_remove_mixnode(
ensure_epoch_in_progress_state(deps.storage)?;
// see if the proxy matches
ensure_proxy_match(&proxy, &existing_bond.proxy)?;
ensure_bonded(&existing_bond)?;
// if there are any pending requests to change the pledge, wait for them to resolve before allowing the unbonding
ensure_no_pending_pledge_changes(&pending_changes)?;
// set `is_unbonding` field
// clippy beta 1.70.0-beta.1 false positive
#[allow(clippy::redundant_clone)]
let mut updated_bond = existing_bond.clone();
updated_bond.is_unbonding = true;
storage::mixnode_bonds().replace(
@@ -375,7 +246,6 @@ pub(crate) fn _try_remove_mixnode(
Ok(
Response::new().add_event(new_pending_mixnode_unbonding_event(
&existing_bond.owner,
&existing_bond.proxy,
existing_bond.identity(),
existing_bond.mix_id,
)),
@@ -387,39 +257,13 @@ pub(crate) fn try_update_mixnode_config(
info: MessageInfo,
new_config: MixNodeConfigUpdate,
) -> Result<Response, MixnetContractError> {
let owner = info.sender;
_try_update_mixnode_config(deps, new_config, owner, None)
}
pub(crate) fn try_update_mixnode_config_on_behalf(
deps: DepsMut<'_>,
info: MessageInfo,
new_config: MixNodeConfigUpdate,
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_mixnode_config(deps, new_config, owner, Some(proxy))
}
pub(crate) fn _try_update_mixnode_config(
deps: DepsMut<'_>,
new_config: MixNodeConfigUpdate,
owner: Addr,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
let existing_bond = must_get_mixnode_bond_by_owner(deps.storage, &owner)?;
let existing_bond = must_get_mixnode_bond_by_owner(deps.storage, &info.sender)?;
ensure_bonded(&existing_bond)?;
ensure_proxy_match(&proxy, &existing_bond.proxy)?;
let cfg_update_event =
new_mixnode_config_update_event(existing_bond.mix_id, &owner, &proxy, &new_config);
new_mixnode_config_update_event(existing_bond.mix_id, &info.sender, &new_config);
// clippy beta 1.70.0-beta.1 false positive
#[allow(clippy::redundant_clone)]
let mut updated_bond = existing_bond.clone();
updated_bond.mix_node.host = new_config.host;
updated_bond.mix_node.mix_port = new_config.mix_port;
@@ -442,45 +286,18 @@ pub(crate) fn try_update_mixnode_cost_params(
env: Env,
info: MessageInfo,
new_costs: MixNodeCostParams,
) -> Result<Response, MixnetContractError> {
let owner = info.sender;
_try_update_mixnode_cost_params(deps, env, new_costs, owner, None)
}
pub(crate) fn try_update_mixnode_cost_params_on_behalf(
deps: DepsMut,
env: Env,
info: MessageInfo,
new_costs: MixNodeCostParams,
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_mixnode_cost_params(deps, env, new_costs, owner, Some(proxy))
}
pub(crate) fn _try_update_mixnode_cost_params(
deps: DepsMut,
env: Env,
new_costs: MixNodeCostParams,
owner: Addr,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
// see if the node still exists
let existing_bond = must_get_mixnode_bond_by_owner(deps.storage, &owner)?;
let existing_bond = must_get_mixnode_bond_by_owner(deps.storage, &info.sender)?;
// changing cost params is only allowed if the epoch is currently not in the process of being advanced
ensure_epoch_in_progress_state(deps.storage)?;
ensure_proxy_match(&proxy, &existing_bond.proxy)?;
ensure_bonded(&existing_bond)?;
let cosmos_event = new_mixnode_pending_cost_params_update_event(
existing_bond.mix_id,
&owner,
&proxy,
&info.sender,
&new_costs,
);
@@ -497,7 +314,7 @@ pub(crate) fn _try_update_mixnode_cost_params(
#[cfg(test)]
pub mod tests {
use cosmwasm_std::testing::mock_info;
use cosmwasm_std::{Order, StdResult, Uint128};
use cosmwasm_std::{Addr, Order, StdResult, Uint128};
use mixnet_contract_common::mixnode::PendingMixNodeChanges;
use mixnet_contract_common::{EpochState, EpochStatus, ExecuteMsg, LayerDistribution, Percent};
@@ -680,38 +497,6 @@ pub mod tests {
assert_eq!(res, Err(MixnetContractError::InvalidEd25519Signature));
}
#[test]
fn mixnode_add_with_illegal_proxy() {
let mut test = TestSetup::new();
let env = test.env();
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let owner = "alice";
let (mixnode, sig, _) = test.mixnode_with_signature(owner, None);
let cost_params = fixtures::mix_node_cost_params_fixture();
let res = try_add_mixnode_on_behalf(
test.deps_mut(),
env,
mock_info(illegal_proxy.as_ref(), &good_mixnode_pledge()),
mixnode,
cost_params,
owner.to_string(),
sig,
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract,
}
)
}
#[test]
fn removing_mixnode_cant_be_performed_if_epoch_transition_is_in_progress() {
let bad_states = vec![
@@ -761,23 +546,6 @@ pub mod tests {
);
let mix_id = test.add_dummy_mixnode(owner, None);
let vesting_contract = test.vesting_contract();
// attempted to remove on behalf with invalid proxy (current is `None`)
let res = try_remove_mixnode_on_behalf(
test.deps_mut(),
env.clone(),
mock_info(vesting_contract.as_ref(), &[]),
owner.to_string(),
);
assert_eq!(
res,
Err(MixnetContractError::ProxyMismatch {
existing: "None".to_string(),
incoming: vesting_contract.into_string(),
})
);
// "normal" unbonding succeeds and unbonding event is pushed to the pending epoch events
let res = try_remove_mixnode(test.deps_mut(), env.clone(), info.clone());
@@ -799,35 +567,6 @@ pub mod tests {
assert_eq!(res, Err(MixnetContractError::MixnodeIsUnbonding { mix_id }))
}
#[test]
fn mixnode_remove_with_illegal_proxy() {
let mut test = TestSetup::new();
let env = test.env();
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let owner = "alice";
test.add_dummy_mixnode_with_illegal_proxy(owner, None, illegal_proxy.clone());
let res = try_remove_mixnode_on_behalf(
test.deps_mut(),
env,
mock_info(illegal_proxy.as_ref(), &[]),
owner.to_string(),
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract,
}
)
}
#[test]
fn mixnode_remove_is_not_allowed_if_there_are_pending_pledge_changes() {
let mut test = TestSetup::new();
@@ -908,22 +647,7 @@ pub mod tests {
);
let mix_id = test.add_dummy_mixnode(owner, None);
let vesting_contract = test.vesting_contract();
// attempted to remove on behalf with invalid proxy (current is `None`)
let res = try_update_mixnode_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_mixnode_config(test.deps_mut(), info.clone(), update.clone());
assert!(res.is_ok());
@@ -943,41 +667,6 @@ pub mod tests {
assert_eq!(res, Err(MixnetContractError::MixnodeIsUnbonding { mix_id }))
}
#[test]
fn updating_mixnode_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_mixnode_with_illegal_proxy(owner, None, illegal_proxy.clone());
let update = MixNodeConfigUpdate {
host: "1.1.1.1:1234".to_string(),
mix_port: 1234,
verloc_port: 1235,
http_api_port: 1236,
version: "v1.2.3".to_string(),
};
let res = try_update_mixnode_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,
}
)
}
#[test]
fn mixnode_cost_params_cant_be_updated_when_epoch_transition_is_in_progress() {
let bad_states = vec![
@@ -1042,23 +731,7 @@ pub mod tests {
);
let mix_id = test.add_dummy_mixnode(owner, None);
let vesting_contract = test.vesting_contract();
// attempted to remove on behalf with invalid proxy (current is `None`)
let res = try_update_mixnode_cost_params_on_behalf(
test.deps_mut(),
env.clone(),
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_mixnode_cost_params(
test.deps_mut(),
@@ -1099,40 +772,6 @@ pub mod tests {
assert_eq!(res, Err(MixnetContractError::MixnodeIsUnbonding { mix_id }))
}
#[test]
fn updating_mixnode_cost_params_with_illegal_proxy() {
let mut test = TestSetup::new();
let env = test.env();
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let owner = "alice";
test.add_dummy_mixnode_with_illegal_proxy(owner, None, illegal_proxy.clone());
let update = MixNodeCostParams {
profit_margin_percent: Percent::from_percentage_value(42).unwrap(),
interval_operating_cost: Coin::new(12345678, TEST_COIN_DENOM),
};
let res = try_update_mixnode_cost_params_on_behalf(
test.deps_mut(),
env,
mock_info(illegal_proxy.as_ref(), &[]),
update,
owner.to_string(),
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract,
}
)
}
#[test]
fn adding_mixnode_with_duplicate_sphinx_key_errors_out() {
let mut test = TestSetup::new();
@@ -1240,69 +879,6 @@ pub mod tests {
)
}
#[test]
fn is_not_allowed_if_theres_proxy_mismatch() {
let mut test = TestSetup::new();
let env = test.env();
let owner_without_proxy = Addr::unchecked("no-proxy");
let owner_with_proxy = Addr::unchecked("with-proxy");
let proxy = Addr::unchecked("proxy");
let wrong_proxy = Addr::unchecked("unrelated-proxy");
test.add_dummy_mixnode(owner_without_proxy.as_str(), None);
test.add_dummy_mixnode_with_illegal_proxy(
owner_with_proxy.as_str(),
None,
proxy.clone(),
);
let res = _try_increase_pledge(
test.deps_mut(),
env.clone(),
Vec::new(),
owner_without_proxy.clone(),
Some(proxy),
);
assert_eq!(
res,
Err(MixnetContractError::ProxyMismatch {
existing: "None".to_string(),
incoming: "proxy".to_string(),
})
);
let res = _try_increase_pledge(
test.deps_mut(),
env.clone(),
Vec::new(),
owner_with_proxy.clone(),
None,
);
assert_eq!(
res,
Err(MixnetContractError::ProxyMismatch {
existing: "proxy".to_string(),
incoming: "None".to_string(),
})
);
let res = _try_increase_pledge(
test.deps_mut(),
env,
Vec::new(),
owner_with_proxy.clone(),
Some(wrong_proxy),
);
assert_eq!(
res,
Err(MixnetContractError::ProxyMismatch {
existing: "proxy".to_string(),
incoming: "unrelated-proxy".to_string(),
})
)
}
#[test]
fn is_not_allowed_if_mixnode_has_unbonded_or_is_unbonding() {
let mut test = TestSetup::new();
@@ -1457,35 +1033,6 @@ pub mod tests {
}
);
}
#[test]
fn fails_for_illegal_proxy() {
let mut test = TestSetup::new();
let env = test.env();
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let owner = "alice";
test.add_dummy_mixnode_with_illegal_proxy(owner, None, illegal_proxy.clone());
let res = try_increase_pledge_on_behalf(
test.deps_mut(),
env,
mock_info(illegal_proxy.as_ref(), &[coin(123, TEST_COIN_DENOM)]),
owner.to_string(),
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract,
}
)
}
}
#[cfg(test)]
@@ -1546,73 +1093,6 @@ pub mod tests {
)
}
#[test]
fn is_not_allowed_if_theres_proxy_mismatch() {
let mut test = TestSetup::new();
let env = test.env();
let owner_without_proxy = Addr::unchecked("no-proxy");
let owner_with_proxy = Addr::unchecked("with-proxy");
let proxy = Addr::unchecked("proxy");
let wrong_proxy = Addr::unchecked("unrelated-proxy");
// just to make sure that after decrease the value would still be above the minimum
let stake = Uint128::new(100_000_000_000);
let decrease = test.coin(1000);
test.add_dummy_mixnode(owner_without_proxy.as_str(), Some(stake));
test.add_dummy_mixnode_with_illegal_proxy(
owner_with_proxy.as_str(),
Some(stake),
proxy.clone(),
);
let res = _try_decrease_pledge(
test.deps_mut(),
env.clone(),
decrease.clone(),
owner_without_proxy.clone(),
Some(proxy),
);
assert_eq!(
res,
Err(MixnetContractError::ProxyMismatch {
existing: "None".to_string(),
incoming: "proxy".to_string(),
})
);
let res = _try_decrease_pledge(
test.deps_mut(),
env.clone(),
decrease.clone(),
owner_with_proxy.clone(),
None,
);
assert_eq!(
res,
Err(MixnetContractError::ProxyMismatch {
existing: "proxy".to_string(),
incoming: "None".to_string(),
})
);
let res = _try_decrease_pledge(
test.deps_mut(),
env,
decrease,
owner_with_proxy.clone(),
Some(wrong_proxy),
);
assert_eq!(
res,
Err(MixnetContractError::ProxyMismatch {
existing: "proxy".to_string(),
incoming: "unrelated-proxy".to_string(),
})
)
}
#[test]
fn is_not_allowed_if_mixnode_has_unbonded_or_is_unbonding() {
let mut test = TestSetup::new();
@@ -1809,38 +1289,5 @@ pub mod tests {
}
);
}
#[test]
fn fails_for_illegal_proxy() {
let mut test = TestSetup::new();
let env = test.env();
let stake = Uint128::new(100_000_000_000);
let decrease = test.coin(1000);
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let owner = "alice";
test.add_dummy_mixnode_with_illegal_proxy(owner, Some(stake), illegal_proxy.clone());
let res = try_decrease_pledge_on_behalf(
test.deps_mut(),
env,
mock_info(illegal_proxy.as_ref(), &[coin(123, TEST_COIN_DENOM)]),
decrease,
owner.to_string(),
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract,
}
)
}
}
}
+49
View File
@@ -1,2 +1,51 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::interval::storage as interval_storage;
use cosmwasm_std::{DepsMut, Order, Storage};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::PendingEpochEventKind;
fn ensure_no_pending_proxy_events(storage: &dyn Storage) -> Result<(), MixnetContractError> {
let last_executed = interval_storage::LAST_PROCESSED_EPOCH_EVENT.load(storage)?;
let last_inserted = interval_storage::EPOCH_EVENT_ID_COUNTER.load(storage)?;
// no pending events
if last_executed == last_inserted {
return Ok(());
}
for maybe_event in
interval_storage::PENDING_EPOCH_EVENTS.range(storage, None, None, Order::Ascending)
{
let (id, event_data) = maybe_event?;
match event_data.kind {
PendingEpochEventKind::Delegate { proxy, .. } => {
if proxy.is_some() {
return Err(MixnetContractError::FailedMigration {
comment: format!(
"there is a pending vesting contract delegation with id {id}"
),
});
}
}
PendingEpochEventKind::Undelegate { proxy, .. } => {
if proxy.is_some() {
return Err(MixnetContractError::FailedMigration {
comment: format!(
"there is a pending vesting contract undelegation with id {id}"
),
});
}
}
_ => continue,
}
}
Ok(())
}
pub(crate) fn vesting_purge(deps: DepsMut) -> Result<(), MixnetContractError> {
ensure_no_pending_proxy_events(deps.storage)?;
Ok(())
}
+33 -194
View File
@@ -1,8 +1,19 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{wasm_execute, Addr, DepsMut, Env, MessageInfo, Response};
use super::storage;
use crate::delegations::storage as delegations_storage;
use crate::interval::storage as interval_storage;
use crate::interval::storage::{push_new_epoch_event, push_new_interval_event};
use crate::mixnodes::helpers::get_mixnode_details_by_owner;
use crate::mixnodes::storage as mixnodes_storage;
use crate::rewards::helpers;
use crate::rewards::helpers::update_and_save_last_rewarded;
use crate::support::helpers::{
ensure_bonded, ensure_can_advance_epoch, ensure_epoch_in_progress_state, ensure_is_owner,
AttachSendTokens,
};
use cosmwasm_std::{DepsMut, Env, MessageInfo, Response};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::events::{
new_active_set_update_event, new_mix_rewarding_event,
@@ -16,22 +27,6 @@ use mixnet_contract_common::reward_params::{
IntervalRewardingParamsUpdate, NodeRewardParams, Performance,
};
use mixnet_contract_common::{Delegation, EpochState, MixId};
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
use crate::delegations::storage as delegations_storage;
use crate::interval::storage as interval_storage;
use crate::interval::storage::{push_new_epoch_event, push_new_interval_event};
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use crate::mixnodes::helpers::get_mixnode_details_by_owner;
use crate::mixnodes::storage as mixnodes_storage;
use crate::rewards::helpers;
use crate::rewards::helpers::update_and_save_last_rewarded;
use crate::support::helpers::{
ensure_bonded, ensure_can_advance_epoch, ensure_epoch_in_progress_state, ensure_is_owner,
ensure_proxy_match, ensure_sent_by_vesting_contract, send_to_proxy_or_owner,
};
use super::storage;
pub(crate) fn try_reward_mixnode(
deps: DepsMut<'_>,
@@ -140,37 +135,16 @@ pub(crate) fn try_withdraw_operator_reward(
deps: DepsMut<'_>,
info: MessageInfo,
) -> Result<Response, MixnetContractError> {
_try_withdraw_operator_reward(deps, info.sender, None)
}
pub(crate) fn try_withdraw_operator_reward_on_behalf(
deps: DepsMut<'_>,
info: MessageInfo,
owner: String,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let proxy = info.sender;
let owner = deps.api.addr_validate(&owner)?;
_try_withdraw_operator_reward(deps, owner, Some(proxy))
}
pub(crate) fn _try_withdraw_operator_reward(
deps: DepsMut<'_>,
owner: Addr,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
// we need to grab all of the node's details so we'd known original pledge alongside
// we need to grab all of the node's details, so we'd known original pledge alongside
// all the earned rewards (and obviously to know if this node even exists and is still
// in the bonded state)
let mix_details = get_mixnode_details_by_owner(deps.storage, owner.clone())?.ok_or(
let mix_details = get_mixnode_details_by_owner(deps.storage, info.sender.clone())?.ok_or(
MixnetContractError::NoAssociatedMixNodeBond {
owner: owner.clone(),
owner: info.sender.clone(),
},
)?;
let mix_id = mix_details.mix_id();
ensure_proxy_match(&proxy, &mix_details.bond_information.proxy)?;
ensure_bonded(&mix_details.bond_information)?;
let reward = helpers::withdraw_operator_reward(deps.storage, mix_details)?;
@@ -178,26 +152,13 @@ pub(crate) fn _try_withdraw_operator_reward(
// if the reward is zero, don't track or send anything - there's no point
if !reward.amount.is_zero() {
let return_tokens = send_to_proxy_or_owner(&proxy, &owner, vec![reward.clone()]);
response = response.add_message(return_tokens);
if let Some(proxy) = &proxy {
// we can only attempt to send the message to the vesting contract if the proxy IS the vesting contract
// otherwise, we don't care
let vesting_contract = mixnet_params_storage::vesting_contract_address(deps.storage)?;
if proxy == vesting_contract {
let msg = VestingContractExecuteMsg::TrackReward {
amount: reward.clone(),
address: owner.clone().into_string(),
};
let track_reward_message = wasm_execute(proxy, &msg, vec![])?;
response = response.add_message(track_reward_message);
}
}
response = response.send_tokens(&info.sender, reward.clone())
}
Ok(response.add_event(new_withdraw_operator_reward_event(
&owner, &proxy, reward, mix_id,
&info.sender,
reward,
mix_id,
)))
}
@@ -205,37 +166,15 @@ pub(crate) fn try_withdraw_delegator_reward(
deps: DepsMut<'_>,
info: MessageInfo,
mix_id: MixId,
) -> Result<Response, MixnetContractError> {
_try_withdraw_delegator_reward(deps, mix_id, info.sender, None)
}
pub(crate) fn try_withdraw_delegator_reward_on_behalf(
deps: DepsMut<'_>,
info: MessageInfo,
mix_id: MixId,
owner: String,
) -> Result<Response, MixnetContractError> {
ensure_sent_by_vesting_contract(&info, deps.storage)?;
let proxy = info.sender;
let owner = deps.api.addr_validate(&owner)?;
_try_withdraw_delegator_reward(deps, mix_id, owner, Some(proxy))
}
pub(crate) fn _try_withdraw_delegator_reward(
deps: DepsMut<'_>,
mix_id: MixId,
owner: Addr,
proxy: Option<Addr>,
) -> Result<Response, MixnetContractError> {
// see if the delegation even exists
let storage_key = Delegation::generate_storage_key(mix_id, &owner, proxy.as_ref());
let storage_key = Delegation::generate_storage_key(mix_id, &info.sender, None);
let delegation = match delegations_storage::delegations().may_load(deps.storage, storage_key)? {
None => {
return Err(MixnetContractError::NoMixnodeDelegationFound {
mix_id,
address: owner.into_string(),
proxy: proxy.map(Addr::into_string),
address: info.sender.into_string(),
proxy: None,
});
}
Some(delegation) => delegation,
@@ -257,33 +196,18 @@ pub(crate) fn _try_withdraw_delegator_reward(
_ => (),
};
ensure_proxy_match(&proxy, &delegation.proxy)?;
let reward = helpers::withdraw_delegator_reward(deps.storage, delegation, mix_rewarding)?;
let mut response = Response::new();
// if the reward is zero, don't track or send anything - there's no point
if !reward.amount.is_zero() {
let return_tokens = send_to_proxy_or_owner(&proxy, &owner, vec![reward.clone()]);
response = response.add_message(return_tokens);
if let Some(proxy) = &proxy {
// we can only attempt to send the message to the vesting contract if the proxy IS the vesting contract
// otherwise, we don't care
let vesting_contract = mixnet_params_storage::vesting_contract_address(deps.storage)?;
if proxy == vesting_contract {
let msg = VestingContractExecuteMsg::TrackReward {
amount: reward.clone(),
address: owner.clone().into_string(),
};
let track_reward_message = wasm_execute(proxy, &msg, vec![])?;
response = response.add_message(track_reward_message);
}
}
response = response.send_tokens(&info.sender, reward.clone())
}
Ok(response.add_event(new_withdraw_delegator_reward_event(
&owner, &proxy, reward, mix_id,
&info.sender,
reward,
mix_id,
)))
}
@@ -1405,13 +1329,10 @@ pub mod tests {
#[cfg(test)]
mod withdrawing_delegator_reward {
use cosmwasm_std::{coin, BankMsg, CosmosMsg, Decimal, Uint128};
use mixnet_contract_common::rewarding::helpers::truncate_reward_amount;
use crate::interval::pending_events;
use crate::support::tests::fixtures::TEST_COIN_DENOM;
use crate::support::tests::test_helpers::{assert_eq_with_leeway, TestSetup};
use cosmwasm_std::{BankMsg, CosmosMsg, Decimal, Uint128};
use mixnet_contract_common::rewarding::helpers::truncate_reward_amount;
use super::*;
@@ -1742,60 +1663,14 @@ pub mod tests {
let accumulated_actual = truncate_reward_amount(accumulated_quad);
assert_eq_with_leeway(total_claimed, accumulated_actual, Uint128::new(6));
}
#[test]
fn fails_for_illegal_proxy() {
let test = TestSetup::new();
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let mut test = TestSetup::new();
let mix_id =
test.add_dummy_mixnode("mix-owner1", Some(Uint128::new(1_000_000_000_000)));
let delegator = "delegator";
test.add_immediate_delegation_with_illegal_proxy(
delegator,
100_000_000u128,
mix_id,
illegal_proxy.clone(),
);
// reward the node
test.skip_to_next_epoch_end();
test.force_change_rewarded_set(vec![mix_id]);
test.start_epoch_transition();
test.reward_with_distribution(mix_id, test_helpers::performance(100.0));
let res = try_withdraw_delegator_reward_on_behalf(
test.deps_mut(),
mock_info(illegal_proxy.as_ref(), &[coin(123, TEST_COIN_DENOM)]),
mix_id,
delegator.to_string(),
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract,
}
)
}
}
#[cfg(test)]
mod withdrawing_operator_reward {
use cosmwasm_std::{coin, BankMsg, CosmosMsg, Uint128};
use crate::interval::pending_events;
use crate::support::tests::fixtures::TEST_COIN_DENOM;
use crate::support::tests::test_helpers::TestSetup;
use super::*;
use crate::interval::pending_events;
use crate::support::tests::test_helpers::TestSetup;
use cosmwasm_std::{Addr, BankMsg, CosmosMsg, Uint128};
#[test]
fn can_only_be_done_if_bond_exists() {
@@ -1908,42 +1783,6 @@ pub mod tests {
})
);
}
#[test]
fn fails_for_illegal_proxy() {
let mut test = TestSetup::new();
let illegal_proxy = Addr::unchecked("not-vesting-contract");
let vesting_contract = test.vesting_contract();
let owner = "mix-owner1";
let mix_id = test.add_dummy_mixnode_with_illegal_proxy(
owner,
Some(Uint128::new(1_000_000_000_000)),
illegal_proxy.clone(),
);
// reward the node
test.skip_to_next_epoch_end();
test.force_change_rewarded_set(vec![mix_id]);
test.start_epoch_transition();
test.reward_with_distribution(mix_id, test_helpers::performance(100.0));
let res = try_withdraw_operator_reward_on_behalf(
test.deps_mut(),
mock_info(illegal_proxy.as_ref(), &[coin(123, TEST_COIN_DENOM)]),
owner.to_string(),
)
.unwrap_err();
assert_eq!(
res,
MixnetContractError::SenderIsNotVestingContract {
received: illegal_proxy,
vesting_contract,
}
)
}
}
#[cfg(test)]
+10 -174
View File
@@ -2,13 +2,11 @@
// SPDX-License-Identifier: Apache-2.0
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::{Addr, BankMsg, Coin, CosmosMsg, Response, Storage};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::mixnode::PendingMixNodeChanges;
use mixnet_contract_common::{EpochState, EpochStatus, IdentityKeyRef, MixId, MixNodeBond};
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
use mixnet_contract_common::{EpochState, EpochStatus, IdentityKeyRef, MixNodeBond};
// helper trait to attach `Msg` to a response if it's provided
#[allow(dead_code)]
@@ -26,131 +24,16 @@ impl<T> AttachOptionalMessage<T> for Response<T> {
}
}
// another helper trait to remove some duplicate code and consolidate comments regarding
// possible epoch progression halting behaviour
pub(crate) trait VestingTracking
where
Self: Sized,
{
fn maybe_add_track_vesting_undelegation_message(
self,
storage: &dyn Storage,
proxy: Option<Addr>,
owner: String,
mix_id: MixId,
amount: Coin,
) -> Result<Self, MixnetContractError>;
fn maybe_add_track_vesting_unbond_mixnode_message(
self,
storage: &dyn Storage,
proxy: Option<Addr>,
owner: String,
amount: Coin,
) -> Result<Self, MixnetContractError>;
fn maybe_add_track_vesting_decrease_mixnode_pledge(
self,
storage: &dyn Storage,
proxy: Option<Addr>,
owner: String,
amount: Coin,
) -> Result<Self, MixnetContractError>;
pub(crate) trait AttachSendTokens {
fn send_tokens(self, to: impl AsRef<str>, amount: Coin) -> Self;
}
impl VestingTracking for Response {
fn maybe_add_track_vesting_undelegation_message(
self,
storage: &dyn Storage,
proxy: Option<Addr>,
owner: String,
mix_id: MixId,
amount: Coin,
) -> Result<Self, MixnetContractError> {
// if there's a proxy set (i.e. the vesting contract), send the track message
if let Some(proxy) = proxy {
let vesting_contract = mixnet_params_storage::vesting_contract_address(storage)?;
// Note: this can INTENTIONALLY cause epoch progression halt if the proxy is not the vesting contract
// But this is fine, since this situation should have NEVER occurred in the first place
// (as all 'on_behalf' methods, including 'DelegateToMixnodeOnBehalf' that got us here,
// explicitly require the proxy to be the vesting contract)
// 'fixing' it would require manually inspecting the problematic event, investigating
// it's cause and manually (presumably via migration) clearing it.
if proxy != vesting_contract {
return Err(MixnetContractError::ProxyIsNotVestingContract {
received: proxy,
vesting_contract,
});
}
let msg = VestingContractExecuteMsg::TrackUndelegation {
owner,
mix_id,
amount,
};
let track_undelegate_message = wasm_execute(proxy, &msg, vec![])?;
Ok(self.add_message(track_undelegate_message))
} else {
// there's no proxy so nothing to do
Ok(self)
}
}
fn maybe_add_track_vesting_unbond_mixnode_message(
self,
storage: &dyn Storage,
proxy: Option<Addr>,
owner: String,
amount: Coin,
) -> Result<Self, MixnetContractError> {
// if there's a proxy set (i.e. the vesting contract), send the track message
if let Some(proxy) = proxy {
let vesting_contract = mixnet_params_storage::vesting_contract_address(storage)?;
// exactly the same possible halting behaviour as in `maybe_add_track_vesting_undelegation_message`.
if proxy != vesting_contract {
return Err(MixnetContractError::ProxyIsNotVestingContract {
received: proxy,
vesting_contract,
});
}
let msg = VestingContractExecuteMsg::TrackUnbondMixnode { owner, amount };
let track_unbond_message = wasm_execute(proxy, &msg, vec![])?;
Ok(self.add_message(track_unbond_message))
} else {
// there's no proxy so nothing to do
Ok(self)
}
}
fn maybe_add_track_vesting_decrease_mixnode_pledge(
self,
storage: &dyn Storage,
proxy: Option<Addr>,
owner: String,
amount: Coin,
) -> Result<Self, MixnetContractError> {
if let Some(proxy) = proxy {
let vesting_contract = mixnet_params_storage::vesting_contract_address(storage)?;
// exactly the same possible halting behaviour as in `maybe_add_track_vesting_undelegation_message`.
if proxy != vesting_contract {
return Err(MixnetContractError::ProxyIsNotVestingContract {
received: proxy,
vesting_contract,
});
}
let msg = VestingContractExecuteMsg::TrackDecreasePledge { owner, amount };
let track_decrease_pledge_message = wasm_execute(proxy, &msg, vec![])?;
Ok(self.add_message(track_decrease_pledge_message))
} else {
// there's no proxy so nothing to do
Ok(self)
}
impl<T> AttachSendTokens for Response<T> {
fn send_tokens(self, to: impl AsRef<str>, amount: Coin) -> Self {
self.add_message(BankMsg::Send {
to_address: to.as_ref().to_string(),
amount: vec![amount],
})
}
}
@@ -158,20 +41,6 @@ impl VestingTracking for Response {
// api.debug(&*format!("\n\n\n=========================================\n{}\n=========================================\n\n\n", msg.into()));
// }
/// Attempts to construct a `BankMsg` to send specified tokens to the provided
/// proxy address. If that's unavailable, the `BankMsg` will use the "owner" as the
/// "to_address".
pub(crate) fn send_to_proxy_or_owner(
proxy: &Option<Addr>,
owner: &Addr,
amount: Vec<Coin>,
) -> BankMsg {
BankMsg::Send {
to_address: proxy.as_ref().unwrap_or(owner).to_string(),
amount,
}
}
pub(crate) fn validate_pledge(
mut pledge: Vec<Coin>,
minimum_pledge: Coin,
@@ -337,39 +206,6 @@ pub(crate) fn ensure_is_owner(
Ok(())
}
pub(crate) fn ensure_proxy_match(
actual: &Option<Addr>,
expected: &Option<Addr>,
) -> Result<(), MixnetContractError> {
if actual != expected {
return Err(MixnetContractError::ProxyMismatch {
existing: expected
.as_ref()
.map_or_else(|| "None".to_string(), |a| a.as_str().to_string()),
incoming: actual
.as_ref()
.map_or_else(|| "None".to_string(), |a| a.as_str().to_string()),
});
}
Ok(())
}
pub(crate) fn ensure_sent_by_vesting_contract(
info: &MessageInfo,
storage: &dyn Storage,
) -> Result<(), MixnetContractError> {
let vesting_contract_address =
crate::mixnet_contract_settings::storage::vesting_contract_address(storage)?;
if info.sender != vesting_contract_address {
Err(MixnetContractError::SenderIsNotVestingContract {
received: info.sender.clone(),
vesting_contract: vesting_contract_address,
})
} else {
Ok(())
}
}
pub(crate) fn ensure_bonded(bond: &MixNodeBond) -> Result<(), MixnetContractError> {
if bond.is_unbonding {
return Err(MixnetContractError::MixnodeIsUnbonding {
@@ -22,7 +22,7 @@ pub(crate) fn valid_bond_gateway_msg(
..tests::fixtures::gateway_fixture()
};
let msg = gateway_bonding_sign_payload(deps, sender, None, gateway.clone(), stake);
let msg = gateway_bonding_sign_payload(deps, sender, gateway.clone(), stake);
let owner_signature = ed25519_sign_message(msg, keypair.private_key());
let identity_key = keypair.public_key().to_base58_string();
+16 -278
View File
@@ -14,8 +14,7 @@ pub mod test_helpers {
use crate::delegations::storage as delegations_storage;
use crate::delegations::transactions::try_delegate_to_mixnode;
use crate::families::transactions::{try_create_family, try_join_family};
use crate::gateways::storage as gateways_storage;
use crate::gateways::transactions::{try_add_gateway, try_add_gateway_on_behalf};
use crate::gateways::transactions::try_add_gateway;
use crate::interval::transactions::{
perform_pending_epoch_actions, perform_pending_interval_actions, try_begin_epoch_transition,
};
@@ -27,9 +26,7 @@ pub mod test_helpers {
};
use crate::mixnodes::storage as mixnodes_storage;
use crate::mixnodes::storage::mixnode_bonds;
use crate::mixnodes::transactions::{
try_add_mixnode, try_add_mixnode_on_behalf, try_remove_mixnode,
};
use crate::mixnodes::transactions::{try_add_mixnode, try_remove_mixnode};
use crate::rewards::queries::{
query_pending_delegator_reward, query_pending_mixnode_operator_reward,
};
@@ -45,7 +42,7 @@ pub mod test_helpers {
use cosmwasm_std::testing::mock_info;
use cosmwasm_std::testing::MockApi;
use cosmwasm_std::testing::MockQuerier;
use cosmwasm_std::{coin, coins, Addr, Api, BankMsg, CosmosMsg, Storage};
use cosmwasm_std::{coin, coins, Addr, BankMsg, CosmosMsg, Storage};
use cosmwasm_std::{Coin, Order};
use cosmwasm_std::{Decimal, Empty, MemoryStorage};
use cosmwasm_std::{Deps, OwnedDeps};
@@ -148,13 +145,6 @@ pub mod test_helpers {
self.owner.clone()
}
pub fn vesting_contract(&self) -> Addr {
mixnet_params_storage::CONTRACT_STATE
.load(self.deps().storage)
.unwrap()
.vesting_contract_address
}
pub fn coin(&self, amount: u128) -> Coin {
coin(amount, rewarding_denom(self.deps().storage).unwrap())
}
@@ -178,7 +168,6 @@ pub mod test_helpers {
&mut self,
family_owner_keys: &identity::KeyPair,
member_node: IdentityKeyRef,
vesting: bool,
) -> MessageSignature {
let identity = family_owner_keys.public_key().to_base58_string();
@@ -195,14 +184,7 @@ pub mod test_helpers {
let nonce = signing_storage::get_signing_nonce(self.deps().storage, owner).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 msg = construct_family_join_permit(nonce, family_head, member_node.to_owned());
let sig_bytes = family_owner_keys
.private_key()
@@ -217,13 +199,11 @@ pub mod test_helpers {
member: &str,
member_keys: &identity::KeyPair,
head_keys: &identity::KeyPair,
vesting: bool,
) {
let member_identity = member_keys.public_key().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 join_permit = self.generate_family_join_permit(head_keys, &member_identity);
let family_head = FamilyHead::new(head_identity);
try_join_family(
@@ -235,12 +215,13 @@ pub mod test_helpers {
.unwrap();
}
#[allow(dead_code)]
pub fn create_dummy_mixnode_with_new_family(
&mut self,
head: &str,
label: &str,
) -> (MixId, identity::KeyPair) {
let (mix_id, keys) = self.add_dummy_mixnode_with_proxy_and_keypair(head, None);
let (mix_id, keys) = self.add_dummy_mixnode_with_keypair(head, None);
try_create_family(self.deps_mut(), mock_info(head, &[]), label.to_string()).unwrap();
(mix_id, keys)
@@ -338,7 +319,7 @@ pub mod test_helpers {
stake: Option<Uint128>,
) -> MessageSignature {
let stake = self.make_mix_pledge(stake);
let msg = mixnode_bonding_sign_payload(self.deps(), owner, None, mixnode, stake);
let msg = mixnode_bonding_sign_payload(self.deps(), owner, mixnode, stake);
ed25519_sign_message(msg, key)
}
@@ -359,13 +340,8 @@ pub mod test_helpers {
..tests::fixtures::mix_node_fixture()
};
let msg = mixnode_bonding_sign_payload(
self.deps(),
owner,
None,
mixnode.clone(),
stake.clone(),
);
let msg =
mixnode_bonding_sign_payload(self.deps(), owner, mixnode.clone(), stake.clone());
let owner_signature = ed25519_sign_message(msg, keypair.private_key());
let info = mock_info(owner, &stake);
@@ -389,156 +365,6 @@ pub mod test_helpers {
(current_id_counter + 1, keypair)
}
pub fn add_dummy_mixnode_with_proxy_and_keypair(
&mut self,
owner: &str,
stake: Option<Uint128>,
) -> (MixId, identity::KeyPair) {
let stake = self.make_mix_pledge(stake);
let proxy = self.vesting_contract();
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,
Some(proxy.clone()),
mixnode.clone(),
stake.clone(),
);
let owner_signature = ed25519_sign_message(msg, keypair.private_key());
let info = mock_info(proxy.as_str(), &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_on_behalf(
self.deps_mut(),
env,
info,
mixnode,
tests::fixtures::mix_node_cost_params_fixture(),
owner.to_string(),
owner_signature,
)
.unwrap();
// newly added mixnode gets assigned the current counter + 1
(current_id_counter + 1, keypair)
}
pub fn add_dummy_mixnode_with_legal_proxy(
&mut self,
owner: &str,
stake: Option<Uint128>,
) -> MixId {
self.add_dummy_mixnode_with_proxy_and_keypair(owner, stake)
.0
}
pub fn set_illegal_mixnode_proxy(&mut self, mix_id: MixId, proxy: Addr) {
let mut bond_details = mixnodes_storage::mixnode_bonds()
.load(self.deps().storage, mix_id)
.unwrap();
bond_details.proxy = Some(proxy);
mixnodes_storage::mixnode_bonds()
.save(self.deps_mut().storage, mix_id, &bond_details)
.unwrap();
}
pub fn add_dummy_gateway_with_illegal_proxy(
&mut self,
owner: &str,
stake: Option<Uint128>,
proxy: Addr,
) -> IdentityKey {
let gateway_identity = self.add_dummy_gateway_with_legal_proxy(owner, stake);
self.set_illegal_gateway_proxy(&gateway_identity, proxy);
gateway_identity
}
pub fn set_illegal_gateway_proxy(&mut self, gateway_id: &str, proxy: Addr) {
let mut gateway = gateways_storage::gateways()
.load(self.deps().storage, gateway_id)
.unwrap();
gateway.proxy = Some(proxy);
gateways_storage::gateways()
.save(self.deps_mut().storage, gateway_id, &gateway)
.unwrap();
}
pub fn add_dummy_gateway_with_legal_proxy(
&mut self,
owner: &str,
stake: Option<Uint128>,
) -> IdentityKey {
let stake = match stake {
Some(amount) => {
let denom = rewarding_denom(self.deps().storage).unwrap();
Coin { denom, amount }
}
None => minimum_mixnode_pledge(self.deps.as_ref().storage).unwrap(),
};
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 proxy = self.vesting_contract();
let gateway = Gateway {
identity_key,
sphinx_key: legit_sphinx_keys.public_key().to_base58_string(),
..tests::fixtures::gateway_fixture()
};
let msg = gateway_bonding_sign_payload(
self.deps(),
owner,
Some(proxy.clone()),
gateway.clone(),
vec![stake.clone()],
);
let owner_signature = ed25519_sign_message(msg, keypair.private_key());
let env = self.env();
let info = mock_info(proxy.as_ref(), &[stake]);
try_add_gateway_on_behalf(
self.deps_mut(),
env,
info,
gateway,
owner.to_string(),
owner_signature,
)
.unwrap();
keypair.public_key().to_base58_string()
}
pub fn add_dummy_mixnode_with_illegal_proxy(
&mut self,
owner: &str,
stake: Option<Uint128>,
proxy: Addr,
) -> MixId {
let mix_id = self.add_dummy_mixnode_with_legal_proxy(owner, stake);
self.set_illegal_mixnode_proxy(mix_id, proxy);
mix_id
}
pub fn mixnode_with_signature(
&mut self,
sender: &str,
@@ -555,8 +381,7 @@ pub mod test_helpers {
sphinx_key: legit_sphinx_keys.public_key().to_base58_string(),
..tests::fixtures::mix_node_fixture()
};
let msg =
mixnode_bonding_sign_payload(self.deps(), sender, None, mixnode.clone(), stake);
let msg = mixnode_bonding_sign_payload(self.deps(), sender, mixnode.clone(), stake);
let owner_signature = ed25519_sign_message(msg, keypair.private_key());
(mixnode, owner_signature, keypair)
@@ -579,8 +404,7 @@ pub mod test_helpers {
..tests::fixtures::gateway_fixture()
};
let msg =
gateway_bonding_sign_payload(self.deps(), sender, None, gateway.clone(), stake);
let msg = gateway_bonding_sign_payload(self.deps(), sender, gateway.clone(), stake);
let owner_signature = ed25519_sign_message(msg, keypair.private_key());
(gateway, owner_signature)
@@ -625,87 +449,10 @@ pub mod test_helpers {
Addr::unchecked(delegator),
target,
amount,
None,
)
.unwrap();
}
pub fn add_immediate_delegation_with_legal_proxy(
&mut self,
delegator: &str,
amount: impl Into<Uint128>,
target: MixId,
) {
let denom = rewarding_denom(self.deps().storage).unwrap();
let amount = Coin {
denom,
amount: amount.into(),
};
let env = self.env();
let proxy = self.vesting_contract();
pending_events::delegate(
self.deps_mut(),
&env,
env.block.height,
Addr::unchecked(delegator),
target,
amount,
Some(proxy),
)
.unwrap();
}
// to set illegal proxy we have to bypass "normal" flow and put the value
// directly into the storage
pub fn add_immediate_delegation_with_illegal_proxy(
&mut self,
delegator: &str,
amount: impl Into<Uint128>,
target: MixId,
proxy: Addr,
) {
let denom = rewarding_denom(self.deps().storage).unwrap();
let amount = Coin {
denom,
amount: amount.into(),
};
let owner = self.deps.api.addr_validate(delegator).unwrap();
let storage_key = Delegation::generate_storage_key(target, &owner, Some(&proxy));
let mut mix_rewarding = self.mix_rewarding(target);
let mut stored_delegation_amount = amount;
if let Some(existing_delegation) = delegations_storage::delegations()
.may_load(&self.deps.storage, storage_key.clone())
.unwrap()
{
let og_with_reward = mix_rewarding.undelegate(&existing_delegation).unwrap();
stored_delegation_amount.amount += og_with_reward.amount;
}
mix_rewarding
.add_base_delegation(stored_delegation_amount.amount)
.unwrap();
let delegation = Delegation::new(
owner,
target,
mix_rewarding.total_unit_reward,
stored_delegation_amount,
self.env.block.height,
Some(proxy),
);
delegations_storage::delegations()
.save(&mut self.deps.storage, storage_key, &delegation)
.unwrap();
rewards_storage::MIXNODE_REWARDING
.save(&mut self.deps.storage, target, &mix_rewarding)
.unwrap();
}
#[allow(unused)]
pub fn add_delegation(
&mut self,
@@ -724,14 +471,8 @@ pub mod test_helpers {
pub fn remove_immediate_delegation(&mut self, delegator: &str, target: MixId) {
let height = self.env.block.height;
pending_events::undelegate(
self.deps_mut(),
height,
Addr::unchecked(delegator),
target,
None,
)
.unwrap();
pending_events::undelegate(self.deps_mut(), height, Addr::unchecked(delegator), target)
.unwrap();
}
pub fn start_epoch_transition(&mut self) {
@@ -1109,7 +850,6 @@ pub mod test_helpers {
Addr::unchecked(format!("owner{}", i)),
mix_id,
tests::fixtures::good_mixnode_pledge().pop().unwrap(),
None,
)
.unwrap();
}
@@ -1205,7 +945,6 @@ pub mod test_helpers {
pub fn mixnode_bonding_sign_payload(
deps: Deps<'_>,
owner: &str,
proxy: Option<Addr>,
mixnode: MixNode,
stake: Vec<Coin>,
) -> SignableMixNodeBondingMsg {
@@ -1214,14 +953,13 @@ pub mod test_helpers {
signing_storage::get_signing_nonce(deps.storage, Addr::unchecked(owner)).unwrap();
let payload = MixnodeBondingPayload::new(mixnode, cost_params);
let content = ContractMessageContent::new(Addr::unchecked(owner), proxy, stake, payload);
let content = ContractMessageContent::new(Addr::unchecked(owner), stake, payload);
SignableMixNodeBondingMsg::new(nonce, content)
}
pub fn gateway_bonding_sign_payload(
deps: Deps<'_>,
owner: &str,
proxy: Option<Addr>,
gateway: Gateway,
stake: Vec<Coin>,
) -> SignableGatewayBondingMsg {
@@ -1229,7 +967,7 @@ pub mod test_helpers {
signing_storage::get_signing_nonce(deps.storage, Addr::unchecked(owner)).unwrap();
let payload = GatewayBondingPayload::new(gateway);
let content = ContractMessageContent::new(Addr::unchecked(owner), proxy, stake, payload);
let content = ContractMessageContent::new(Addr::unchecked(owner), stake, payload);
SignableGatewayBondingMsg::new(nonce, content)
}
+95
View File
@@ -0,0 +1,95 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::delegations::storage as delegations_storage;
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use crate::mixnodes::helpers::get_mixnode_details_by_owner;
use crate::mixnodes::storage as mixnodes_storage;
use crate::support::helpers::{
ensure_bonded, ensure_epoch_in_progress_state, ensure_no_pending_pledge_changes,
};
use cosmwasm_std::{wasm_execute, DepsMut, MessageInfo, Response};
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::{Delegation, MixId};
use vesting_contract_common::messages::ExecuteMsg as VestingExecuteMsg;
pub(crate) fn try_migrate_vested_mixnode(
deps: DepsMut<'_>,
info: MessageInfo,
) -> Result<Response, MixnetContractError> {
let mix_details = get_mixnode_details_by_owner(deps.storage, info.sender.clone())?.ok_or(
MixnetContractError::NoAssociatedMixNodeBond {
owner: info.sender.clone(),
},
)?;
let mix_id = mix_details.mix_id();
ensure_epoch_in_progress_state(deps.storage)?;
ensure_no_pending_pledge_changes(&mix_details.pending_changes)?;
ensure_bonded(&mix_details.bond_information)?;
let Some(proxy) = &mix_details.bond_information.proxy else {
return Err(MixnetContractError::NotAVestingMixnode);
};
let vesting_contract = mixnet_params_storage::vesting_contract_address(deps.storage)?;
if proxy != vesting_contract {
return Err(MixnetContractError::ProxyIsNotVestingContract {
received: proxy.clone(),
vesting_contract,
});
}
let mut updated_bond = mix_details.bond_information.clone();
updated_bond.proxy = None;
mixnodes_storage::mixnode_bonds().replace(
deps.storage,
mix_id,
Some(&updated_bond),
Some(&mix_details.bond_information),
)?;
Ok(Response::new().add_message(wasm_execute(
vesting_contract,
&VestingExecuteMsg::TrackMigratedMixnode {
owner: info.sender.into_string(),
},
vec![],
)?))
}
pub(crate) fn try_migrate_vested_delegation(
deps: DepsMut<'_>,
info: MessageInfo,
mix_id: MixId,
) -> Result<Response, MixnetContractError> {
ensure_epoch_in_progress_state(deps.storage)?;
let vesting_contract = mixnet_params_storage::vesting_contract_address(deps.storage)?;
let storage_key =
Delegation::generate_storage_key(mix_id, &info.sender, Some(&vesting_contract));
let Some(mut delegation) =
delegations_storage::delegations().may_load(deps.storage, storage_key.clone())?
else {
return Err(MixnetContractError::NotAVestingDelegation);
};
// sanity check that's meant to blow up the contract
assert_eq!(delegation.proxy, Some(vesting_contract.clone()));
// update the delegation and save it under the correct storage key
delegation.proxy = None;
let updated_storage_key = Delegation::generate_storage_key(mix_id, &info.sender, None);
delegations_storage::delegations().remove(deps.storage, storage_key)?;
delegations_storage::delegations().save(deps.storage, updated_storage_key, &delegation)?;
Ok(Response::new().add_message(wasm_execute(
vesting_contract,
&VestingExecuteMsg::TrackMigratedDelegation {
owner: info.sender.into_string(),
mix_id,
},
vec![],
)?))
}
@@ -691,6 +691,54 @@
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"track_migrated_mixnode"
],
"properties": {
"track_migrated_mixnode": {
"type": "object",
"required": [
"owner"
],
"properties": {
"owner": {
"type": "string"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"track_migrated_delegation"
],
"properties": {
"track_migrated_delegation": {
"type": "object",
"required": [
"mix_id",
"owner"
],
"properties": {
"mix_id": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"owner": {
"type": "string"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
],
"definitions": {
+48
View File
@@ -669,6 +669,54 @@
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"track_migrated_mixnode"
],
"properties": {
"track_migrated_mixnode": {
"type": "object",
"required": [
"owner"
],
"properties": {
"owner": {
"type": "string"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"track_migrated_delegation"
],
"properties": {
"track_migrated_delegation": {
"type": "object",
"required": [
"mix_id",
"owner"
],
"properties": {
"mix_id": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"owner": {
"type": "string"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
],
"definitions": {
+6 -74
View File
@@ -70,55 +70,12 @@ pub fn execute(
msg: ExecuteMsg,
) -> Result<Response, VestingContractError> {
match msg {
ExecuteMsg::CreateFamily { label } => try_create_family(info, deps, label),
ExecuteMsg::JoinFamily {
join_permit,
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),
ExecuteMsg::UpdateLockedPledgeCap { address, cap } => {
try_update_locked_pledge_cap(address, cap, info, deps)
}
ExecuteMsg::TrackReward { amount, address } => {
try_track_reward(deps, info, amount, &address)
}
ExecuteMsg::ClaimOperatorReward {} => try_claim_operator_reward(deps, info),
ExecuteMsg::ClaimDelegatorReward { mix_id } => {
try_claim_delegator_reward(deps, info, mix_id)
}
ExecuteMsg::UpdateMixnodeConfig { new_config } => {
try_update_mixnode_config(new_config, info, deps)
}
ExecuteMsg::UpdateMixnodeCostParams { new_costs } => {
try_update_mixnode_cost_params(new_costs, info, deps)
}
ExecuteMsg::UpdateMixnetAddress { address } => {
try_update_mixnet_address(address, info, deps)
}
ExecuteMsg::DelegateToMixnode {
mix_id,
amount,
on_behalf_of,
} => try_delegate_to_mixnode(mix_id, amount, on_behalf_of, info, env, deps),
ExecuteMsg::UndelegateFromMixnode {
mix_id,
on_behalf_of,
} => try_undelegate_from_mixnode(mix_id, on_behalf_of, info, deps),
ExecuteMsg::CreateAccount {
owner_address,
staking_address,
vesting_spec,
cap,
} => try_create_periodic_vesting_account(
&owner_address,
staking_address,
vesting_spec,
cap,
info,
env,
deps,
),
ExecuteMsg::WithdrawVestedCoins { amount } => {
try_withdraw_vested_coins(amount, env, info, deps)
}
@@ -127,47 +84,22 @@ pub fn execute(
mix_id,
amount,
} => try_track_undelegation(&owner, mix_id, amount, info, deps),
ExecuteMsg::BondMixnode {
mix_node,
cost_params,
owner_signature,
amount,
} => try_bond_mixnode(
mix_node,
cost_params,
owner_signature,
amount,
info,
env,
deps,
),
ExecuteMsg::PledgeMore { amount } => try_pledge_more(deps, env, info, amount),
ExecuteMsg::DecreasePledge { amount } => try_decrease_pledge(deps, info, amount),
ExecuteMsg::UnbondMixnode {} => try_unbond_mixnode(info, deps),
ExecuteMsg::TrackUnbondMixnode { owner, amount } => {
try_track_unbond_mixnode(&owner, amount, info, deps)
}
ExecuteMsg::TrackDecreasePledge { owner, amount } => {
try_track_decrease_mixnode_pledge(&owner, amount, info, deps)
}
ExecuteMsg::BondGateway {
gateway,
owner_signature,
amount,
} => try_bond_gateway(gateway, owner_signature, amount, info, env, deps),
ExecuteMsg::UnbondGateway {} => try_unbond_gateway(info, deps),
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)
}
ExecuteMsg::UpdateStakingAddress { to_address } => {
try_update_staking_address(to_address, info, deps)
ExecuteMsg::TrackMigratedMixnode { owner } => try_track_migrate_mixnode(&owner, info, deps),
ExecuteMsg::TrackMigratedDelegation { owner, mix_id } => {
try_track_migrate_delegation(&owner, mix_id, info, deps)
}
_ => Err(VestingContractError::Other {
message: "the contract has been disabled".to_string(),
}),
}
}
@@ -61,6 +61,10 @@ pub trait MixnodeBondingAccount {
new_costs: MixNodeCostParams,
storage: &mut dyn Storage,
) -> Result<Response, VestingContractError>;
fn try_track_migrated_mixnode(
&self,
storage: &mut dyn Storage,
) -> Result<(), VestingContractError>;
}
pub trait GatewayBondingAccount {
@@ -44,4 +44,9 @@ pub trait DelegatingAccount {
amount: Coin,
storage: &mut dyn Storage,
) -> Result<(), VestingContractError>;
fn track_migrated_delegation(
&self,
mix_id: MixId,
storage: &mut dyn Storage,
) -> Result<(), VestingContractError>;
}
+32 -2
View File
@@ -18,8 +18,9 @@ use mixnet_contract_common::{
use vesting_contract_common::events::{
new_ownership_transfer_event, new_periodic_vesting_account_event,
new_staking_address_update_event, new_track_gateway_unbond_event,
new_track_mixnode_pledge_decrease_event, new_track_mixnode_unbond_event,
new_track_reward_event, new_track_undelegation_event, new_vested_coins_withdraw_event,
new_track_migrate_mixnode_event, new_track_mixnode_pledge_decrease_event,
new_track_mixnode_unbond_event, new_track_reward_event, new_track_undelegation_event,
new_vested_coins_withdraw_event,
};
use vesting_contract_common::{Account, PledgeCap, VestingContractError, VestingSpecification};
@@ -255,6 +256,35 @@ pub fn try_track_unbond_gateway(
Ok(Response::new().add_event(new_track_gateway_unbond_event()))
}
/// Track vesting mixnode being converted into the usage of liquid tokens. invoked by the mixnet contract after successful migration.
pub fn try_track_migrate_mixnode(
owner: &str,
info: MessageInfo,
deps: DepsMut<'_>,
) -> Result<Response, VestingContractError> {
if info.sender != MIXNET_CONTRACT_ADDRESS.load(deps.storage)? {
return Err(VestingContractError::NotMixnetContract(info.sender));
}
let account = account_from_address(owner, deps.storage, deps.api)?;
account.try_track_migrated_mixnode(deps.storage)?;
Ok(Response::new().add_event(new_track_migrate_mixnode_event()))
}
/// Track vesting delegation being converted into the usage of liquid tokens. invoked by the mixnet contract after successful migration.
pub fn try_track_migrate_delegation(
owner: &str,
mix_id: MixId,
info: MessageInfo,
deps: DepsMut<'_>,
) -> Result<Response, VestingContractError> {
if info.sender != MIXNET_CONTRACT_ADDRESS.load(deps.storage)? {
return Err(VestingContractError::NotMixnetContract(info.sender));
}
let account = account_from_address(owner, deps.storage, deps.api)?;
account.track_migrated_delegation(mix_id, deps.storage)?;
Ok(Response::new().add_event(new_track_migrate_mixnode_event()))
}
/// Bond a mixnode, sends [mixnet_contract_common::ExecuteMsg::BondMixnodeOnBehalf] to [crate::storage::MIXNET_CONTRACT_ADDRESS].
pub fn try_bond_mixnode(
mix_node: MixNode,
@@ -125,4 +125,25 @@ impl DelegatingAccount for Account {
self.save_balance(new_balance, storage)?;
Ok(())
}
fn track_migrated_delegation(
&self,
mix_id: MixId,
storage: &mut dyn Storage,
) -> Result<(), VestingContractError> {
let delegation = self.total_delegations_for_mix(mix_id, storage)?;
if delegation.is_zero() {
return Err(VestingContractError::NoSuchDelegation(
self.owner_address.clone(),
mix_id,
));
}
// treat the tokens that were used for delegation as 'withdrawn'
let current_withdrawn = self.load_withdrawn(storage)?;
self.save_withdrawn(current_withdrawn + delegation, storage)?;
// remove the delegation data since it no longer belongs to the vesting contract
self.remove_delegations_for_mix(mix_id, storage)
}
}
@@ -221,4 +221,24 @@ impl MixnodeBondingAccount for Account {
.add_message(update_mixnode_costs_msg)
.add_event(new_vesting_update_mixnode_cost_params_event()))
}
fn try_track_migrated_mixnode(
&self,
storage: &mut dyn Storage,
) -> Result<(), VestingContractError> {
let Some(pledge) = self.load_mixnode_pledge(storage)? else {
return Err(VestingContractError::NoBondFound(
self.owner_address().as_str().to_string(),
));
};
// treat the tokens that were used for bonding as 'withdrawn'
let current_withdrawn = self.load_withdrawn(storage)?;
self.save_withdrawn(current_withdrawn + pledge.amount.amount, storage)?;
// don't change the balance as the tokens are left in the mixnet contract
// remove the pledge data since it no longer belongs to the vesting account
self.remove_mixnode_pledge(storage)
}
}
+54 -296
View File
@@ -13,14 +13,14 @@ pub fn populate_vesting_periods(
#[cfg(test)]
mod tests {
use crate::contract::*;
use crate::storage::*;
use crate::support::tests::helpers::vesting_account_percent_fixture;
use crate::support::tests::helpers::{
init_contract, vesting_account_mid_fixture, vesting_account_new_fixture, TEST_COIN_DENOM,
};
use crate::traits::DelegatingAccount;
use crate::traits::GatewayBondingAccount;
use crate::traits::VestingAccount;
use crate::traits::{GatewayBondingAccount, MixnodeBondingAccount};
use crate::vesting::account::StorableVestingAccountExt;
use crate::vesting::populate_vesting_periods;
use contracts_common::signing::MessageSignature;
@@ -36,162 +36,56 @@ mod tests {
fn test_account_creation() {
let mut deps = init_contract();
let env = mock_env();
let info = mock_info("not_admin", &coins(1_000_000_000_000, TEST_COIN_DENOM));
let msg = ExecuteMsg::CreateAccount {
owner_address: "owner".to_string(),
staking_address: Some("staking".to_string()),
vesting_spec: None,
cap: Some(PledgeCap::Absolute(Uint128::from(100_000_000_000u128))),
};
// Try creating an account when not admin
let response = execute(deps.as_mut(), env.clone(), info, msg.clone());
assert!(response.is_err());
let info = mock_info("admin", &coins(1_000_000_000_000, TEST_COIN_DENOM));
let _response = execute(deps.as_mut(), env.clone(), info.clone(), msg.clone());
let created_account = load_account(Addr::unchecked("owner"), &deps.storage)
.unwrap()
.unwrap();
let response = execute(deps.as_mut(), env.clone(), info.clone(), msg.clone());
assert_eq!(
created_account.load_balance(&deps.storage).unwrap(),
// One was liquidated
Uint128::new(1_000_000_000_000)
response,
Err(VestingContractError::Other {
message: "the contract has been disabled".to_string()
})
);
// nothing is saved for "staking" account!
let created_account_test_by_staking =
load_account(Addr::unchecked("staking"), &deps.storage).unwrap();
assert!(created_account_test_by_staking.is_none());
// but we can stake on its behalf!
let stake_msg = ExecuteMsg::DelegateToMixnode {
on_behalf_of: Some("owner".to_string()),
mix_id: 42,
amount: coin(500, TEST_COIN_DENOM),
};
let response = execute(
deps.as_mut(),
env.clone(),
mock_info("staking", &[]),
stake_msg,
);
assert!(response.is_ok());
assert_eq!(
created_account.load_balance(&deps.storage).unwrap(),
// One was liquidated
Uint128::new(999_999_999_500)
);
// Try create the same account again
let response = execute(deps.as_mut(), env.clone(), info, msg);
assert!(response.is_err());
let account_again = vesting_account_new_fixture(&mut deps.storage, &env);
assert_eq!(created_account.storage_key(), 1);
assert_ne!(created_account.storage_key(), account_again.storage_key());
}
#[test]
fn test_ownership_transfer() {
let mut deps = init_contract();
let mut env = mock_env();
let env = mock_env();
let info = mock_info("owner", &[]);
let account = vesting_account_new_fixture(&mut deps.storage, &env);
let staker = account.staking_address().unwrap();
let msg = ExecuteMsg::TransferOwnership {
to_address: "new_owner".to_string(),
};
let _response = execute(deps.as_mut(), env.clone(), info.clone(), msg.clone()).unwrap();
let new_owner_account = load_account(Addr::unchecked("new_owner"), &deps.storage)
.unwrap()
.unwrap();
let response = execute(deps.as_mut(), env.clone(), info.clone(), msg.clone());
assert_eq!(
new_owner_account.load_balance(&deps.storage),
account.load_balance(&deps.storage)
response,
Err(VestingContractError::Other {
message: "the contract has been disabled".to_string()
})
);
// Check old account is gone
let old_owner_account = load_account(Addr::unchecked("owner"), &deps.storage).unwrap();
assert!(old_owner_account.is_none());
// Not the owner
let response = execute(deps.as_mut(), env.clone(), info, msg);
assert!(response.is_err());
// can't stake on behalf of the original owner anymore, but we can do it for the new one!
let stake_msg = ExecuteMsg::DelegateToMixnode {
on_behalf_of: Some("owner".to_string()),
mix_id: 42,
amount: coin(500, TEST_COIN_DENOM),
};
let response = execute(
deps.as_mut(),
env.clone(),
mock_info(staker.as_ref(), &[]),
stake_msg,
);
assert!(response.is_err());
let new_stake_msg = ExecuteMsg::DelegateToMixnode {
on_behalf_of: Some("new_owner".to_string()),
mix_id: 42,
amount: coin(500, TEST_COIN_DENOM),
};
let response = execute(
deps.as_mut(),
env.clone(),
mock_info(staker.as_ref(), &[]),
new_stake_msg,
);
assert!(response.is_ok());
let info = mock_info("new_owner", &[]);
let msg = ExecuteMsg::UpdateStakingAddress {
to_address: Some("new_staking".to_string()),
};
let _response = execute(deps.as_mut(), env.clone(), info, msg).unwrap();
let msg = ExecuteMsg::WithdrawVestedCoins {
amount: Coin {
amount: Uint128::new(1),
denom: TEST_COIN_DENOM.to_string(),
},
};
let info = mock_info("new_owner", &[]);
env.block.time = Timestamp::from_nanos(env.block.time.nanos() + 100_000_000_000_000_000);
let response = execute(deps.as_mut(), env.clone(), info, msg.clone());
assert!(response.is_ok());
let info = mock_info("owner", &[]);
let response = execute(deps.as_mut(), env.clone(), info, msg);
assert!(response.is_err());
}
#[test]
fn test_staking_account() {
let mut deps = init_contract();
let mut env = mock_env();
let env = mock_env();
let info = mock_info("staking", &[]);
let msg = ExecuteMsg::TransferOwnership {
to_address: "new_owner".to_string(),
};
let response = execute(deps.as_mut(), env.clone(), info.clone(), msg);
// Only owner can transfer
assert!(response.is_err());
let msg = ExecuteMsg::WithdrawVestedCoins {
amount: Coin {
amount: Uint128::new(1),
denom: "nym".to_string(),
},
};
env.block.time = Timestamp::from_nanos(env.block.time.nanos() + 100_000_000_000_000_000);
let response = execute(deps.as_mut(), env, info, msg);
// Only owner can withdraw
assert!(response.is_err());
assert_eq!(
response,
Err(VestingContractError::Other {
message: "the contract has been disabled".to_string()
})
);
}
#[test]
@@ -213,31 +107,12 @@ mod tests {
mock_info(original_staker.as_ref(), &[]),
stake_msg.clone(),
);
assert!(response.is_ok());
let info = mock_info("owner", &[]);
let msg = ExecuteMsg::UpdateStakingAddress {
to_address: Some("new_staking".to_string()),
};
let _response = execute(deps.as_mut(), env.clone(), info, msg).unwrap();
// the old staking account can't do any staking anymore!
let response = execute(
deps.as_mut(),
env.clone(),
mock_info(original_staker.as_ref(), &[]),
stake_msg.clone(),
assert_eq!(
response,
Err(VestingContractError::Other {
message: "the contract has been disabled".to_string()
})
);
assert!(response.is_err());
// but the new one can
let response = execute(
deps.as_mut(),
env.clone(),
mock_info("new_staking", &[]),
stake_msg,
);
assert!(response.is_ok());
}
#[test]
@@ -245,66 +120,27 @@ mod tests {
let mut deps = init_contract();
let env = mock_env();
let amount1 = coin(1000000000, "unym");
let amount2 = coin(100, "unym");
let amount = coin(1000000000, "unym");
// create the accounts
let msg1 = ExecuteMsg::CreateAccount {
let msg = ExecuteMsg::CreateAccount {
owner_address: "vesting1".to_string(),
staking_address: None,
vesting_spec: None,
cap: None,
};
let res1 = execute(
let response = execute(
deps.as_mut(),
env.clone(),
mock_info("admin", &[amount1.clone()]),
msg1,
);
assert!(res1.is_ok());
let msg2 = ExecuteMsg::CreateAccount {
owner_address: "vesting2".to_string(),
staking_address: None,
vesting_spec: None,
cap: None,
};
let res2 = execute(
deps.as_mut(),
env.clone(),
mock_info("admin", &[amount2.clone()]),
msg2,
);
assert!(res2.is_ok());
let vesting1 = try_get_vesting_coins("vesting1", None, env.clone(), deps.as_ref()).unwrap();
assert_eq!(vesting1, amount1);
let vesting2 = try_get_vesting_coins("vesting2", None, env.clone(), deps.as_ref()).unwrap();
assert_eq!(vesting2, amount2);
let staking_address_change = ExecuteMsg::UpdateStakingAddress {
to_address: Some("vesting1".to_string()),
};
let res = execute(
deps.as_mut(),
env.clone(),
mock_info("vesting2", &[]),
staking_address_change,
mock_info("admin", &[amount.clone()]),
msg,
);
assert_eq!(
Err(VestingContractError::StakingAccountExists(
"vesting1".to_string()
)),
res
response,
Err(VestingContractError::Other {
message: "the contract has been disabled".to_string()
})
);
// ensure nothing has changed!
let vesting1 = try_get_vesting_coins("vesting1", None, env.clone(), deps.as_ref()).unwrap();
assert_eq!(vesting1, amount1);
let vesting2 = try_get_vesting_coins("vesting2", None, env, deps.as_ref()).unwrap();
assert_eq!(vesting2, amount2);
}
#[test]
@@ -566,63 +402,13 @@ mod tests {
};
let info = mock_info("admin", &coins(1_000_000_000_000, TEST_COIN_DENOM));
let _response = execute(deps.as_mut(), env.clone(), info, msg);
let account = load_account(Addr::unchecked("owner"), &deps.storage)
.unwrap()
.unwrap();
// Try delegating too much
let err = account.try_delegate_to_mixnode(
1,
Coin {
amount: Uint128::new(1_000_000_000_001),
denom: TEST_COIN_DENOM.to_string(),
},
&env,
&mut deps.storage,
let response = execute(deps.as_mut(), env.clone(), info, msg);
assert_eq!(
response,
Err(VestingContractError::Other {
message: "the contract has been disabled".to_string()
})
);
assert!(err.is_err());
let ok = account.try_delegate_to_mixnode(
1,
Coin {
amount: Uint128::new(90_000_000_000),
denom: TEST_COIN_DENOM.to_string(),
},
&env,
&mut deps.storage,
);
assert!(ok.is_ok());
// Fails due to delegation locked delegation cap
let ok = account.try_delegate_to_mixnode(
1,
Coin {
amount: Uint128::new(20_000_000_000),
denom: TEST_COIN_DENOM.to_string(),
},
&env,
&mut deps.storage,
);
assert!(ok.is_err());
let balance = account.load_balance(&deps.storage).unwrap();
assert_eq!(balance, Uint128::new(910000000000));
// Try delegating too much againcalca
let err = account.try_delegate_to_mixnode(
1,
Coin {
amount: Uint128::new(500_000_000_001),
denom: TEST_COIN_DENOM.to_string(),
},
&env,
&mut deps.storage,
);
assert!(err.is_err());
let total_delegations = account.total_delegations_for_mix(1, &deps.storage).unwrap();
assert_eq!(Uint128::new(90_000_000_000), total_delegations);
}
#[test]
@@ -649,52 +435,24 @@ mod tests {
amount: Uint128::new(40),
},
};
// Try delegating too much
let err = account.try_bond_mixnode(
mix_node.clone(),
cost_params.clone(),
MessageSignature::from(vec![1, 2, 3]),
Coin {
amount: Uint128::new(1_000_000_000_001),
denom: TEST_COIN_DENOM.to_string(),
},
&env,
&mut deps.storage,
);
assert!(err.is_err());
let ok = account.try_bond_mixnode(
mix_node.clone(),
cost_params.clone(),
MessageSignature::from(vec![1, 2, 3]),
Coin {
let msg = ExecuteMsg::BondMixnode {
mix_node,
cost_params,
owner_signature: vec![1, 2, 3, 4].into(),
amount: Coin {
amount: Uint128::new(90_000_000_000),
denom: TEST_COIN_DENOM.to_string(),
},
&env,
&mut deps.storage,
};
let info = mock_info(account.owner_address.as_str(), &[]);
let response = execute(deps.as_mut(), env.clone(), info, msg);
assert_eq!(
response,
Err(VestingContractError::Other {
message: "the contract has been disabled".to_string()
})
);
assert!(ok.is_ok());
let balance = account.load_balance(&deps.storage).unwrap();
assert_eq!(balance, Uint128::new(910_000_000_000));
// Try delegating too much again
let err = account.try_bond_mixnode(
mix_node,
cost_params,
MessageSignature::from(vec![1, 2, 3]),
Coin {
amount: Uint128::new(10_000_000_001),
denom: TEST_COIN_DENOM.to_string(),
},
&env,
&mut deps.storage,
);
assert!(err.is_err());
let pledge = account.load_mixnode_pledge(&deps.storage).unwrap().unwrap();
assert_eq!(Uint128::new(90_000_000_000), pledge.amount().amount);
}
#[test]
@@ -46,4 +46,4 @@ If you have successfully completed KYC on Synaps earlier and provided your Nym w
Using the Nym network is free for now, but user fees will be introduced in the future and those will be paid in NYM tokens, so set them aside! And until then, you can contribute to running our decentralized infrastructure and earn mix mining rewards by setting up Nym mix nodes and bonding your tokens to them. You don't need to run nodes to contribute to the security and performance of the Nym network though. You can pledge your trust in someone else's node by delegating your NYM tokens to it, and receive a share of their mix mining rewards.
You can find out more about how staking works in [this](https://www.youtube.com/watch?v=PcNGcTwlm0I) video from our Chief Scientist Claudia Diaz and if you need help setting up your mix node, choosing one to delegate your tokens to, or anything else, our community is there to help on [Discord](https://discord.gg/nym) and [Telegram](https://t.me/nymchan).
You can find out more about how staking works in [this](https://www.youtube.com/watch?v=PcNGcTwlm0I) video from our Chief Scientist Claudia Diaz and if you need help setting up your mix node, choosing one to delegate your tokens to, or anything else, our community is there to help on [Discord](https://discord.gg/nymproject) and [Telegram](https://t.me/nymchan).
+1 -1
View File
@@ -11,7 +11,7 @@ Since these docs autogenerate command output and import docs from binaries in `t
Changes merged to `master` will be autodeployed to the production site.
### Contributing a new translation
To contribute tranlsations in a new language, please get in touch via [Matrix](https://matrix.to/#/#general:nymtech.chat) or [Discord](https://discord.gg/nym).
To contribute tranlsations in a new language, please get in touch via [Matrix](https://matrix.to/#/#general:nymtech.chat) or [Discord](https://discord.gg/nymproject).
### Variables
There are some variables that are shared across the entire docs site, such as the current latest software version.
@@ -36,7 +36,7 @@ Create an account on Sandbox using the nym-cli:
```./nym-cli --config-env-file <path-to>sandbox.env account create```
You will need `nymt` funds sent to this account. Get in touch via Nym [Telegram](https://t.me/nymchan) or [Discord](https://discord.gg/nym) and we can send them to you.
You will need `nymt` funds sent to this account. Get in touch via Nym [Telegram](https://t.me/nymchan) or [Discord](https://discord.gg/nymproject) and we can send them to you.
Next, you init the nym-client with the enabled credentials mode set to true:
@@ -11,7 +11,7 @@ This document presents an initiative to further support Nyms mission of allow
All the technical changes on the side of Nym nodes - ***Project Smoosh*** - are described in the [FAQ section](../archive/faq/smoosh-faq.md).
```admonish warning
Nym core team cannot provide comprehensive legal advice across all jurisdictions. Knowledge and experience with the legalities are being built up with the help of our counsel and with you, the community of Nym node operators. We encourage Nym node operators to join the operator channels ([Element](https://matrix.to/#/#operators:nymtech.chat), [Discord](https://discord.gg/nym), [Telegram](https://t.me/nymchan_help_chat)) to share best practices and experiences.
Nym core team cannot provide comprehensive legal advice across all jurisdictions. Knowledge and experience with the legalities are being built up with the help of our counsel and with you, the community of Nym node operators. We encourage Nym node operators to join the operator channels ([Element](https://matrix.to/#/#operators:nymtech.chat), [Discord](https://discord.gg/nymproject), [Telegram](https://t.me/nymchan_help_chat)) to share best practices and experiences.
```
@@ -11,6 +11,6 @@ If the `/dev/sda` partition is almost full, try pruning some of the `.gz` syslog
## Where can I get more help?
The fastest way to reach one of us or get a help from the community, visit our [Telegram Node Setup Help Chat](https://t.me/nymchan_help_chat) or head to our [Discord](https://discord.gg/nym).
The fastest way to reach one of us or get a help from the community, visit our [Telegram Node Setup Help Chat](https://t.me/nymchan_help_chat) or head to our [Discord](https://discord.gg/nymproject).
For more tech heavy question join our [Matrix core community channel](https://matrix.to/#/#general:nymtech.chat), where you can meet other builders and Nym core team members.
+1 -1
View File
@@ -10,7 +10,7 @@ import { DiscordIcon } from '../icons/socials/DiscordIcon'
export const TELEGRAM_LINK = 'https://t.me/nymchan'
export const TWITTER_LINK = 'https://twitter.com/nymproject'
export const GITHUB_LINK = 'https://github.com/nymtech'
export const DISCORD_LINK = 'https://discord.gg/nym'
export const DISCORD_LINK = 'https://discord.gg/nymproject'
export const Socials: FCWithChildren<{ isFooter?: boolean }> = ({
isFooter = false,
+1 -1
View File
@@ -10,7 +10,7 @@ import { DiscordIcon } from '../icons/socials/DiscordIcon';
export const TELEGRAM_LINK = 'https://t.me/nymchan';
export const TWITTER_LINK = 'https://twitter.com/nymproject';
export const GITHUB_LINK = 'https://github.com/nymtech';
export const DISCORD_LINK = 'https://discord.gg/nym';
export const DISCORD_LINK = 'https://discord.gg/nymproject';
export const Socials: FCWithChildren<{ isFooter?: boolean }> = ({ isFooter }) => {
const theme = useTheme();
-3
View File
@@ -148,9 +148,6 @@ pub enum GatewayError {
#[error("failed to startup local ip packet router")]
IpPacketRouterStartupFailure,
#[error("failed to startup local authenticator")]
AuthenticatorStartupFailure,
#[error("there are no nym API endpoints available")]
NoNymApisAvailable,
+17 -72
View File
@@ -47,16 +47,6 @@ struct StartedNetworkRequester {
handle: LocalEmbeddedClientHandle,
}
// TODO: should this struct live here?
#[allow(unused)]
struct StartedAuthenticator {
#[cfg(feature = "wireguard")]
wg_api: Arc<nym_wireguard::WgApiWrapper>,
/// Handle to interact with the local authenticator
handle: LocalEmbeddedClientHandle,
}
/// Wire up and create Gateway instance
pub async fn create_gateway(
config: Config,
@@ -248,63 +238,17 @@ impl<St> Gateway<St> {
#[cfg(all(feature = "wireguard", target_os = "linux"))]
async fn start_authenticator(
&mut self,
forwarding_channel: MixForwardingSender,
opts: &LocalAuthenticatorOpts,
shutdown: TaskClient,
) -> Result<StartedAuthenticator, Box<dyn std::error::Error + Send + Sync>> {
let opts = self
.authenticator_opts
.as_ref()
.ok_or(GatewayError::UnspecifiedAuthenticatorConfig)?;
let (router_tx, mut router_rx) = oneshot::channel();
let (auth_mix_sender, auth_mix_receiver) = mpsc::unbounded();
let router_shutdown = shutdown.fork("message_router");
let transceiver = LocalGateway::new(
*self.identity_keypair.public_key(),
forwarding_channel,
router_tx,
);
) -> Result<Arc<nym_wireguard::WgApiWrapper>, Box<dyn std::error::Error + Send + Sync>> {
if let Some(wireguard_data) = self.wireguard_data.take() {
let (on_start_tx, on_start_rx) = oneshot::channel();
let mut authenticator_server = nym_authenticator::Authenticator::new(
let authenticator_server = nym_authenticator::Authenticator::new(
opts.config.clone(),
wireguard_data.inner.clone(),
)
.with_custom_gateway_transceiver(Box::new(transceiver))
.with_shutdown(shutdown.fork("authenticator"))
.with_wait_for_gateway(true)
.with_minimum_gateway_performance(0)
.with_on_start(on_start_tx);
if let Some(custom_mixnet) = &opts.custom_mixnet_path {
authenticator_server = authenticator_server.with_stored_topology(custom_mixnet)?
}
tokio::spawn(async move {
if let Err(e) = authenticator_server.run_service_provider().await {
log::error!("Run authenticator server - {e}");
}
});
let start_data = on_start_rx
.await
.map_err(|_| GatewayError::AuthenticatorStartupFailure)?;
// this should be instantaneous since the data is sent on this channel before the on start is called;
// the failure should be impossible
let Ok(Some(packet_router)) = router_rx.try_recv() else {
return Err(Box::new(GatewayError::AuthenticatorStartupFailure));
};
MessageRouter::new(auth_mix_receiver, packet_router)
.start_with_shutdown(router_shutdown);
let wg_api = nym_wireguard::start_wireguard(shutdown, wireguard_data).await?;
Ok(StartedAuthenticator {
wg_api,
handle: LocalEmbeddedClientHandle::new(start_data.address, auth_mix_sender),
})
.with_shutdown(shutdown.fork("authenticator"));
tokio::spawn(async move { authenticator_server.run_service_provider().await });
nym_wireguard::start_wireguard(shutdown, wireguard_data).await
} else {
Err(Box::new(GatewayError::WireguardNotSet))
}
@@ -313,9 +257,9 @@ impl<St> Gateway<St> {
#[cfg(all(feature = "wireguard", not(target_os = "linux")))]
async fn start_authenticator(
&self,
_forwarding_channel: MixForwardingSender,
_opts: &LocalAuthenticatorOpts,
_shutdown: TaskClient,
) -> Result<StartedAuthenticator, Box<dyn std::error::Error + Send + Sync>> {
) -> Result<Arc<nym_wireguard::WgApiWrapper>, Box<dyn std::error::Error + Send + Sync>> {
todo!("Authenticator is currently only supported on Linux");
}
@@ -610,7 +554,7 @@ impl<St> Gateway<St> {
if self.config.ip_packet_router.enabled {
let embedded_ip_sp = self
.start_ip_packet_router(
mix_forwarding_channel.clone(),
mix_forwarding_channel,
shutdown.fork("ip_service_provider"),
)
.await?;
@@ -620,13 +564,14 @@ impl<St> Gateway<St> {
};
#[cfg(feature = "wireguard")]
let _wg_api = {
let embedded_auth = self
.start_authenticator(mix_forwarding_channel, shutdown.fork("authenticator"))
.await
.map_err(|source| GatewayError::AuthenticatorStartError { source })?;
active_clients_store.insert_embedded(embedded_auth.handle);
Some(embedded_auth.wg_api)
let _wg_api = if let Some(opts) = self.authenticator_opts.clone() {
Some(
self.start_authenticator(&opts, shutdown.fork("wireguard"))
.await
.map_err(|source| GatewayError::AuthenticatorStartError { source })?,
)
} else {
None
};
if self.run_http_server {
+6
View File
@@ -409,6 +409,9 @@ async fn migrate_gateway(mut args: Args) -> Result<(), NymNodeError> {
bind_address: SocketAddr::new(ip, cfg.gateway.clients_port),
announce_ws_port: None,
announce_wss_port: cfg.gateway.clients_wss_port,
authenticator: config::authenticator::Authenticator {
debug: Default::default(),
},
debug: config::entry_gateway::Debug {
message_retrieval_limit: cfg.debug.message_retrieval_limit,
},
@@ -454,6 +457,9 @@ async fn migrate_gateway(mut args: Args) -> Result<(), NymNodeError> {
.unwrap_or_default(),
},
},
authenticator: config::authenticator::Authenticator {
debug: Default::default(),
},
}),
)
.build();
+6 -43
View File
@@ -8,13 +8,11 @@ use crate::error::EntryGatewayError;
use nym_config::defaults::DEFAULT_CLIENT_LISTENING_PORT;
use nym_config::helpers::inaddr_any;
use nym_config::serde_helpers::de_maybe_port;
use nym_gateway::node::LocalAuthenticatorOpts;
use serde::{Deserialize, Serialize};
use std::net::SocketAddr;
use std::path::Path;
use super::helpers::{base_client_config, EphemeralConfig};
use super::LocalWireguardOpts;
use super::authenticator::Authenticator;
pub const DEFAULT_WS_PORT: u16 = DEFAULT_CLIENT_LISTENING_PORT;
@@ -42,6 +40,8 @@ pub struct EntryGatewayConfig {
#[serde(deserialize_with = "de_maybe_port")]
pub announce_wss_port: Option<u16>,
pub authenticator: Authenticator,
#[serde(default)]
pub debug: Debug,
}
@@ -73,6 +73,7 @@ impl EntryGatewayConfig {
bind_address: SocketAddr::new(inaddr_any(), DEFAULT_WS_PORT),
announce_ws_port: None,
announce_wss_port: None,
authenticator: Default::default(),
debug: Default::default(),
}
}
@@ -82,44 +83,6 @@ impl EntryGatewayConfig {
pub fn ephemeral_entry_gateway_config(
config: Config,
mnemonic: &bip39::Mnemonic,
) -> Result<EphemeralConfig, EntryGatewayError> {
let auth_opts = LocalAuthenticatorOpts {
config: nym_authenticator::Config {
base: nym_client_core_config_types::Config {
client: base_client_config(&config),
debug: config.authenticator.debug.client_debug,
},
authenticator: config.wireguard.clone().into(),
storage_paths: nym_authenticator::config::AuthenticatorPaths {
common_paths: config
.exit_gateway
.storage_paths
.authenticator
.to_common_client_paths(),
},
logging: config.logging,
},
custom_mixnet_path: None,
};
let wg_opts = LocalWireguardOpts {
config: super::Wireguard {
enabled: config.wireguard.enabled,
bind_address: config.wireguard.bind_address,
private_ip: config.wireguard.private_ip,
announced_port: config.wireguard.announced_port,
private_network_prefix: config.wireguard.private_network_prefix,
storage_paths: config.wireguard.storage_paths.clone(),
},
custom_mixnet_path: None,
};
let gateway = ephemeral_gateway_config(config, mnemonic)?;
Ok(EphemeralConfig {
nr_opts: None,
ipr_opts: None,
auth_opts,
wg_opts,
gateway,
})
) -> Result<nym_gateway::config::Config, EntryGatewayError> {
Ok(ephemeral_gateway_config(config, mnemonic)?)
}
+28 -7
View File
@@ -5,6 +5,7 @@ use crate::config::helpers::ephemeral_gateway_config;
use crate::config::persistence::ExitGatewayPaths;
use crate::config::Config;
use crate::error::ExitGatewayError;
use clap::crate_version;
use nym_client_core_config_types::DebugConfig as ClientDebugConfig;
use nym_config::defaults::mainnet;
use nym_gateway::node::{
@@ -14,10 +15,7 @@ use serde::{Deserialize, Serialize};
use std::path::Path;
use url::Url;
use super::{
helpers::{base_client_config, EphemeralConfig},
LocalWireguardOpts,
};
use super::{authenticator::Authenticator, LocalWireguardOpts};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
@@ -34,6 +32,8 @@ pub struct ExitGatewayConfig {
pub network_requester: NetworkRequester,
pub ip_packet_router: IpPacketRouter,
pub authenticator: Authenticator,
}
impl ExitGatewayConfig {
@@ -49,6 +49,7 @@ impl ExitGatewayConfig {
.expect("invalid default exit policy URL"),
network_requester: Default::default(),
ip_packet_router: Default::default(),
authenticator: Default::default(),
}
}
}
@@ -138,6 +139,25 @@ impl Default for IpPacketRouterDebug {
}
}
pub struct EphemeralConfig {
pub gateway: nym_gateway::config::Config,
pub nr_opts: LocalNetworkRequesterOpts,
pub ipr_opts: LocalIpPacketRouterOpts,
pub auth_opts: LocalAuthenticatorOpts,
pub wg_opts: LocalWireguardOpts,
}
fn base_client_config(config: &Config) -> nym_client_core_config_types::Client {
nym_client_core_config_types::Client {
version: format!("{}-nym-node", crate_version!()),
id: config.id.clone(),
// irrelevant field - no need for credentials in embedded mode
disabled_credentials_mode: true,
nyxd_urls: config.mixnet.nyxd_urls.clone(),
nym_api_urls: config.mixnet.nym_api_urls.clone(),
}
}
// that function is rather disgusting, but I hope it's not going to live for too long
pub fn ephemeral_exit_gateway_config(
config: Config,
@@ -224,7 +244,7 @@ pub fn ephemeral_exit_gateway_config(
config: nym_authenticator::Config {
base: nym_client_core_config_types::Config {
client: base_client_config(&config),
debug: config.authenticator.debug.client_debug,
debug: config.exit_gateway.authenticator.debug.client_debug,
},
authenticator: config.wireguard.clone().into(),
storage_paths: nym_authenticator::config::AuthenticatorPaths {
@@ -233,6 +253,7 @@ pub fn ephemeral_exit_gateway_config(
.storage_paths
.authenticator
.to_common_client_paths(),
authenticator_description: Default::default(),
},
logging: config.logging,
},
@@ -269,8 +290,8 @@ pub fn ephemeral_exit_gateway_config(
gateway.storage_paths.keys.public_identity_key_file = pub_id_path;
Ok(EphemeralConfig {
nr_opts: Some(nr_opts),
ipr_opts: Some(ipr_opts),
nr_opts,
ipr_opts,
auth_opts,
wg_opts,
gateway,
-24
View File
@@ -3,14 +3,9 @@
use crate::config::Config;
use clap::crate_version;
use nym_gateway::node::{
LocalAuthenticatorOpts, LocalIpPacketRouterOpts, LocalNetworkRequesterOpts,
};
use std::net::IpAddr;
use thiserror::Error;
use super::LocalWireguardOpts;
#[derive(Debug, Error)]
#[error("currently it's not supported to have different ip addresses for clients and mixnet ({clients_bind_ip} and {mix_bind_ip} were used)")]
pub struct UnsupportedGatewayAddresses {
@@ -85,22 +80,3 @@ pub fn ephemeral_gateway_config(
},
))
}
pub fn base_client_config(config: &Config) -> nym_client_core_config_types::Client {
nym_client_core_config_types::Client {
version: format!("{}-nym-node", crate_version!()),
id: config.id.clone(),
// irrelevant field - no need for credentials in embedded mode
disabled_credentials_mode: true,
nyxd_urls: config.mixnet.nyxd_urls.clone(),
nym_api_urls: config.mixnet.nym_api_urls.clone(),
}
}
pub struct EphemeralConfig {
pub gateway: nym_gateway::config::Config,
pub nr_opts: Option<LocalNetworkRequesterOpts>,
pub ipr_opts: Option<LocalIpPacketRouterOpts>,
pub auth_opts: LocalAuthenticatorOpts,
pub wg_opts: LocalWireguardOpts,
}
-7
View File
@@ -4,7 +4,6 @@
use crate::config::persistence::NymNodePaths;
use crate::config::template::CONFIG_TEMPLATE;
use crate::error::NymNodeError;
use authenticator::Authenticator;
use celes::Country;
use clap::ValueEnum;
use nym_bin_common::logging::LoggingSettings;
@@ -114,8 +113,6 @@ pub struct ConfigBuilder {
pub exit_gateway: Option<ExitGatewayConfig>,
pub authenticator: Option<Authenticator>,
pub logging: Option<LoggingSettings>,
}
@@ -134,7 +131,6 @@ impl ConfigBuilder {
mixnode: None,
entry_gateway: None,
exit_gateway: None,
authenticator: None,
logging: None,
}
}
@@ -211,7 +207,6 @@ impl ConfigBuilder {
.unwrap_or_else(|| ExitGatewayConfig::new_default(&self.data_dir)),
logging: self.logging.unwrap_or_default(),
save_path: Some(self.config_path),
authenticator: self.authenticator.unwrap_or_default(),
}
}
}
@@ -248,8 +243,6 @@ pub struct Config {
pub exit_gateway: ExitGatewayConfig,
pub authenticator: Authenticator,
#[serde(default)]
pub logging: LoggingSettings,
}
+4 -4
View File
@@ -1,8 +1,8 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
mod old_config_v1;
mod old_config_v2;
mod old_config_1_1_2;
mod old_config_1_1_3;
pub use old_config_v1::try_upgrade_config_v1;
pub use old_config_v2::try_upgrade_config_v2;
pub use old_config_1_1_2::try_upgrade_config_1_1_2;
pub use old_config_1_1_3::try_upgrade_config_1_1_3;
@@ -8,21 +8,21 @@ use nym_client_core_config_types::DebugConfig as ClientDebugConfig;
use nym_config::serde_helpers::de_maybe_port;
use nym_crypto::asymmetric::encryption::KeyPair;
use nym_pemstore::store_keypair;
use old_configs::old_config_v2::*;
use old_configs::old_config_1_1_3::*;
use rand::rngs::OsRng;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct WireguardPathsV1 {
pub struct WireguardPaths1_1_2 {
pub private_diffie_hellman_key_file: PathBuf,
pub public_diffie_hellman_key_file: PathBuf,
}
impl WireguardPathsV1 {
impl WireguardPaths1_1_2 {
pub fn new<P: AsRef<Path>>(data_dir: P) -> Self {
let data_dir = data_dir.as_ref();
WireguardPathsV1 {
WireguardPaths1_1_2 {
private_diffie_hellman_key_file: data_dir
.join(persistence::DEFAULT_X25519_WG_DH_KEY_FILENAME),
public_diffie_hellman_key_file: data_dir
@@ -40,7 +40,7 @@ impl WireguardPathsV1 {
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct WireguardV1 {
pub struct Wireguard1_1_2 {
/// Specifies whether the wireguard service is enabled on this node.
pub enabled: bool,
@@ -61,13 +61,13 @@ pub struct WireguardV1 {
pub private_network_prefix: u8,
/// Paths for wireguard keys, client registries, etc.
pub storage_paths: WireguardPathsV1,
pub storage_paths: WireguardPaths1_1_2,
}
// a temporary solution until all "types" are run at the same time
#[derive(Debug, Default, Serialize, Deserialize, ValueEnum, Clone, Copy)]
#[serde(rename_all = "snake_case")]
pub enum NodeModeV1 {
pub enum NodeMode1_1_2 {
#[default]
#[clap(alias = "mix")]
Mixnode,
@@ -79,12 +79,12 @@ pub enum NodeModeV1 {
ExitGateway,
}
impl From<NodeModeV1> for NodeModeV2 {
fn from(config: NodeModeV1) -> Self {
impl From<NodeMode1_1_2> for NodeMode1_1_3 {
fn from(config: NodeMode1_1_2) -> Self {
match config {
NodeModeV1::Mixnode => NodeModeV2::Mixnode,
NodeModeV1::EntryGateway => NodeModeV2::EntryGateway,
NodeModeV1::ExitGateway => NodeModeV2::ExitGateway,
NodeMode1_1_2::Mixnode => NodeMode1_1_3::Mixnode,
NodeMode1_1_2::EntryGateway => NodeMode1_1_3::EntryGateway,
NodeMode1_1_2::ExitGateway => NodeMode1_1_3::ExitGateway,
}
}
}
@@ -93,7 +93,7 @@ impl From<NodeModeV1> for NodeModeV2 {
#[derive(Debug, Clone, Default, Deserialize, PartialEq, Serialize)]
#[serde(default)]
#[serde(deny_unknown_fields)]
pub struct HostV1 {
pub struct Host1_1_2 {
/// Ip address(es) of this host, such as 1.1.1.1 that external clients will use for connections.
/// If no values are provided, when this node gets included in the network,
/// its ip addresses will be populated by whatever value is resolved by associated nym-api.
@@ -112,7 +112,7 @@ pub struct HostV1 {
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(default)]
#[serde(deny_unknown_fields)]
pub struct MixnetDebugV1 {
pub struct MixnetDebug1_1_2 {
/// Initial value of an exponential backoff to reconnect to dropped TCP connection when
/// forwarding sphinx packets.
#[serde(with = "humantime_serde")]
@@ -134,16 +134,16 @@ pub struct MixnetDebugV1 {
pub unsafe_disable_noise: bool,
}
impl MixnetDebugV1 {
impl MixnetDebug1_1_2 {
const DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF: Duration = Duration::from_millis(10_000);
const DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF: Duration = Duration::from_millis(300_000);
const DEFAULT_INITIAL_CONNECTION_TIMEOUT: Duration = Duration::from_millis(1_500);
const DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE: usize = 2000;
}
impl Default for MixnetDebugV1 {
impl Default for MixnetDebug1_1_2 {
fn default() -> Self {
MixnetDebugV1 {
MixnetDebug1_1_2 {
packet_forwarding_initial_backoff: Self::DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF,
packet_forwarding_maximum_backoff: Self::DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF,
initial_connection_timeout: Self::DEFAULT_INITIAL_CONNECTION_TIMEOUT,
@@ -154,7 +154,7 @@ impl Default for MixnetDebugV1 {
}
}
impl Default for MixnetV1 {
impl Default for Mixnet1_1_2 {
fn default() -> Self {
// SAFETY:
// our hardcoded values should always be valid
@@ -173,7 +173,7 @@ impl Default for MixnetV1 {
vec![mainnet::NYXD_URL.parse().expect("Invalid default nyxd URL")]
};
MixnetV1 {
Mixnet1_1_2 {
bind_address: SocketAddr::new(inaddr_any(), DEFAULT_MIXNET_PORT),
nym_api_urls,
nyxd_urls,
@@ -185,7 +185,7 @@ impl Default for MixnetV1 {
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(default)]
#[serde(deny_unknown_fields)]
pub struct MixnetV1 {
pub struct Mixnet1_1_2 {
/// Address this node will bind to for listening for mixnet packets
/// default: `0.0.0.0:1789`
pub bind_address: SocketAddr,
@@ -197,12 +197,12 @@ pub struct MixnetV1 {
pub nyxd_urls: Vec<Url>,
#[serde(default)]
pub debug: MixnetDebugV1,
pub debug: MixnetDebug1_1_2,
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct KeysPathsV1 {
pub struct KeysPaths1_1_2 {
/// Path to file containing ed25519 identity private key.
pub private_ed25519_identity_key_file: PathBuf,
@@ -224,8 +224,8 @@ pub struct KeysPathsV1 {
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct NymNodePathsV1 {
pub keys: KeysPathsV1,
pub struct NymNodePaths1_1_2 {
pub keys: KeysPaths1_1_2,
/// Path to a file containing basic node description: human-readable name, website, details, etc.
pub description: PathBuf,
@@ -234,7 +234,7 @@ pub struct NymNodePathsV1 {
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(default)]
#[serde(deny_unknown_fields)]
pub struct HttpV1 {
pub struct Http1_1_2 {
/// Socket address this node will use for binding its http API.
/// default: `0.0.0.0:8080`
pub bind_address: SocketAddr,
@@ -263,9 +263,9 @@ pub struct HttpV1 {
pub expose_crypto_hardware: bool,
}
impl Default for HttpV1 {
impl Default for Http1_1_2 {
fn default() -> Self {
HttpV1 {
Http1_1_2 {
bind_address: SocketAddr::new(inaddr_any(), DEFAULT_HTTP_PORT),
landing_page_assets_path: None,
access_token: None,
@@ -278,11 +278,11 @@ impl Default for HttpV1 {
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct MixnodePathsV1 {}
pub struct MixnodePaths1_1_2 {}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct DebugV1 {
pub struct Debug1_1_2 {
/// Delay between each subsequent node statistics being logged to the console
#[serde(with = "humantime_serde")]
pub node_stats_logging_delay: Duration,
@@ -294,7 +294,7 @@ pub struct DebugV1 {
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct VerlocDebugV1 {
pub struct VerlocDebug1_1_2 {
/// Specifies number of echo packets sent to each node during a measurement run.
pub packets_per_node: usize,
@@ -325,16 +325,16 @@ pub struct VerlocDebugV1 {
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct VerlocV1 {
pub struct Verloc1_1_2 {
/// Socket address this node will use for binding its verloc API.
/// default: `0.0.0.0:1790`
pub bind_address: SocketAddr,
#[serde(default)]
pub debug: VerlocDebugV1,
pub debug: VerlocDebug1_1_2,
}
impl VerlocDebugV1 {
impl VerlocDebug1_1_2 {
const DEFAULT_PACKETS_PER_NODE: usize = 100;
const DEFAULT_CONNECTION_TIMEOUT: Duration = Duration::from_millis(5000);
const DEFAULT_PACKET_TIMEOUT: Duration = Duration::from_millis(1500);
@@ -344,9 +344,9 @@ impl VerlocDebugV1 {
const DEFAULT_RETRY_TIMEOUT: Duration = Duration::from_secs(60 * 30);
}
impl Default for VerlocDebugV1 {
impl Default for VerlocDebug1_1_2 {
fn default() -> Self {
VerlocDebugV1 {
VerlocDebug1_1_2 {
packets_per_node: Self::DEFAULT_PACKETS_PER_NODE,
connection_timeout: Self::DEFAULT_CONNECTION_TIMEOUT,
packet_timeout: Self::DEFAULT_PACKET_TIMEOUT,
@@ -360,23 +360,23 @@ impl Default for VerlocDebugV1 {
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct MixnodeConfigV1 {
pub storage_paths: MixnodePathsV1,
pub struct MixnodeConfig1_1_2 {
pub storage_paths: MixnodePaths1_1_2,
pub verloc: VerlocV1,
pub verloc: Verloc1_1_2,
#[serde(default)]
pub debug: DebugV1,
pub debug: Debug1_1_2,
}
impl DebugV1 {
impl Debug1_1_2 {
const DEFAULT_NODE_STATS_LOGGING_DELAY: Duration = Duration::from_millis(60_000);
const DEFAULT_NODE_STATS_UPDATING_DELAY: Duration = Duration::from_millis(30_000);
}
impl Default for DebugV1 {
impl Default for Debug1_1_2 {
fn default() -> Self {
DebugV1 {
Debug1_1_2 {
node_stats_logging_delay: Self::DEFAULT_NODE_STATS_LOGGING_DELAY,
node_stats_updating_delay: Self::DEFAULT_NODE_STATS_UPDATING_DELAY,
}
@@ -385,7 +385,7 @@ impl Default for DebugV1 {
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct EntryGatewayPathsV1 {
pub struct EntryGatewayPaths1_1_2 {
/// Path to sqlite database containing all persistent data: messages for offline clients,
/// derived shared keys and available client bandwidths.
pub clients_storage: PathBuf,
@@ -396,18 +396,18 @@ pub struct EntryGatewayPathsV1 {
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct EntryGatewayConfigDebugV1 {
pub struct EntryGatewayConfigDebug1_1_2 {
/// Number of messages from offline client that can be pulled at once (i.e. with a single SQL query) from the storage.
pub message_retrieval_limit: i64,
}
impl EntryGatewayConfigDebugV1 {
impl EntryGatewayConfigDebug1_1_2 {
const DEFAULT_MESSAGE_RETRIEVAL_LIMIT: i64 = 100;
}
impl Default for EntryGatewayConfigDebugV1 {
impl Default for EntryGatewayConfigDebug1_1_2 {
fn default() -> Self {
EntryGatewayConfigDebugV1 {
EntryGatewayConfigDebug1_1_2 {
message_retrieval_limit: Self::DEFAULT_MESSAGE_RETRIEVAL_LIMIT,
}
}
@@ -415,8 +415,8 @@ impl Default for EntryGatewayConfigDebugV1 {
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct EntryGatewayConfigV1 {
pub storage_paths: EntryGatewayPathsV1,
pub struct EntryGatewayConfig1_1_2 {
pub storage_paths: EntryGatewayPaths1_1_2,
/// Indicates whether this gateway is accepting only coconut credentials for accessing the mixnet
/// or if it also accepts non-paying clients
@@ -438,12 +438,12 @@ pub struct EntryGatewayConfigV1 {
pub announce_wss_port: Option<u16>,
#[serde(default)]
pub debug: EntryGatewayConfigDebugV1,
pub debug: EntryGatewayConfigDebug1_1_2,
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct NetworkRequesterPathsV1 {
pub struct NetworkRequesterPaths1_1_2 {
/// Path to file containing network requester ed25519 identity private key.
pub private_ed25519_identity_key_file: PathBuf,
@@ -473,7 +473,7 @@ pub struct NetworkRequesterPathsV1 {
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct IpPacketRouterPathsV1 {
pub struct IpPacketRouterPaths1_1_2 {
/// Path to file containing ip packet router ed25519 identity private key.
pub private_ed25519_identity_key_file: PathBuf,
@@ -503,15 +503,15 @@ pub struct IpPacketRouterPathsV1 {
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ExitGatewayPathsV1 {
pub network_requester: NetworkRequesterPathsV1,
pub struct ExitGatewayPaths1_1_2 {
pub network_requester: NetworkRequesterPaths1_1_2,
pub ip_packet_router: IpPacketRouterPathsV1,
pub ip_packet_router: IpPacketRouterPaths1_1_2,
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default)]
pub struct IpPacketRouterDebugV1 {
pub struct IpPacketRouterDebug1_1_2 {
/// Specifies whether ip packet routing service is enabled in this process.
/// This is only here for debugging purposes as exit gateway should always run **both**
/// network requester and an ip packet router.
@@ -527,9 +527,9 @@ pub struct IpPacketRouterDebugV1 {
pub client_debug: ClientDebugConfig,
}
impl Default for IpPacketRouterDebugV1 {
impl Default for IpPacketRouterDebug1_1_2 {
fn default() -> Self {
IpPacketRouterDebugV1 {
IpPacketRouterDebug1_1_2 {
enabled: true,
disable_poisson_rate: true,
client_debug: Default::default(),
@@ -538,22 +538,22 @@ impl Default for IpPacketRouterDebugV1 {
}
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
pub struct IpPacketRouterV1 {
pub struct IpPacketRouter1_1_2 {
#[serde(default)]
pub debug: IpPacketRouterDebugV1,
pub debug: IpPacketRouterDebug1_1_2,
}
#[allow(clippy::derivable_impls)]
impl Default for IpPacketRouterV1 {
impl Default for IpPacketRouter1_1_2 {
fn default() -> Self {
IpPacketRouterV1 {
IpPacketRouter1_1_2 {
debug: Default::default(),
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
pub struct NetworkRequesterDebugV1 {
pub struct NetworkRequesterDebug1_1_2 {
/// Specifies whether network requester service is enabled in this process.
/// This is only here for debugging purposes as exit gateway should always run **both**
/// network requester and an ip packet router.
@@ -569,9 +569,9 @@ pub struct NetworkRequesterDebugV1 {
pub client_debug: ClientDebugConfig,
}
impl Default for NetworkRequesterDebugV1 {
impl Default for NetworkRequesterDebug1_1_2 {
fn default() -> Self {
NetworkRequesterDebugV1 {
NetworkRequesterDebug1_1_2 {
enabled: true,
disable_poisson_rate: true,
client_debug: Default::default(),
@@ -580,15 +580,15 @@ impl Default for NetworkRequesterDebugV1 {
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
pub struct NetworkRequesterV1 {
pub struct NetworkRequester1_1_2 {
#[serde(default)]
pub debug: NetworkRequesterDebugV1,
pub debug: NetworkRequesterDebug1_1_2,
}
#[allow(clippy::derivable_impls)]
impl Default for NetworkRequesterV1 {
impl Default for NetworkRequester1_1_2 {
fn default() -> Self {
NetworkRequesterV1 {
NetworkRequester1_1_2 {
debug: Default::default(),
}
}
@@ -596,8 +596,8 @@ impl Default for NetworkRequesterV1 {
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ExitGatewayConfigV1 {
pub storage_paths: ExitGatewayPathsV1,
pub struct ExitGatewayConfig1_1_2 {
pub storage_paths: ExitGatewayPaths1_1_2,
/// specifies whether this exit node should run in 'open-proxy' mode
/// and thus would attempt to resolve **ANY** request it receives.
@@ -606,20 +606,20 @@ pub struct ExitGatewayConfigV1 {
/// Specifies the url for an upstream source of the exit policy used by this node.
pub upstream_exit_policy_url: Url,
pub network_requester: NetworkRequesterV1,
pub network_requester: NetworkRequester1_1_2,
pub ip_packet_router: IpPacketRouterV1,
pub ip_packet_router: IpPacketRouter1_1_2,
}
#[derive(Debug, Default, Copy, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct LoggingSettingsV1 {
pub struct LoggingSettings1_1_2 {
// well, we need to implement something here at some point...
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1 {
pub struct Config1_1_2 {
// additional metadata holding on-disk location of this config file
#[serde(skip)]
pub(crate) save_path: Option<PathBuf>,
@@ -629,37 +629,37 @@ pub struct ConfigV1 {
/// Current mode of this nym-node.
/// Expect this field to be changed in the future to allow running the node in multiple modes (i.e. mixnode + gateway)
pub mode: NodeModeV1,
pub mode: NodeMode1_1_2,
pub host: HostV1,
pub host: Host1_1_2,
pub mixnet: MixnetV1,
pub mixnet: Mixnet1_1_2,
/// Storage paths to persistent nym-node data, such as its long term keys.
pub storage_paths: NymNodePathsV1,
pub storage_paths: NymNodePaths1_1_2,
#[serde(default)]
pub http: HttpV1,
pub http: Http1_1_2,
pub wireguard: WireguardV1,
pub wireguard: Wireguard1_1_2,
pub mixnode: MixnodeConfigV1,
pub mixnode: MixnodeConfig1_1_2,
pub entry_gateway: EntryGatewayConfigV1,
pub entry_gateway: EntryGatewayConfig1_1_2,
pub exit_gateway: ExitGatewayConfigV1,
pub exit_gateway: ExitGatewayConfig1_1_2,
#[serde(default)]
pub logging: LoggingSettingsV1,
pub logging: LoggingSettings1_1_2,
}
impl NymConfigTemplate for ConfigV1 {
impl NymConfigTemplate for Config1_1_2 {
fn template(&self) -> &'static str {
CONFIG_TEMPLATE
}
}
impl ConfigV1 {
impl Config1_1_2 {
pub fn save(&self) -> Result<(), NymNodeError> {
let save_location = self.save_location();
debug!(
@@ -727,7 +727,7 @@ impl ConfigV1 {
// simple wrapper that reads config file and assigns path location
fn read_from_path<P: AsRef<Path>>(path: P) -> Result<Self, NymNodeError> {
let path = path.as_ref();
let mut loaded: ConfigV1 =
let mut loaded: Config1_1_2 =
read_config_from_toml_file(path).map_err(|source| NymNodeError::ConfigLoadFailure {
path: path.to_path_buf(),
source,
@@ -742,7 +742,7 @@ impl ConfigV1 {
}
}
fn initialise(config: &WireguardV2) -> std::io::Result<()> {
fn initialise(config: &Wireguard1_1_3) -> std::io::Result<()> {
let mut rng = OsRng;
let x25519_keys = KeyPair::new(&mut rng);
@@ -754,43 +754,36 @@ fn initialise(config: &WireguardV2) -> std::io::Result<()> {
Ok(())
}
pub async fn try_upgrade_config_v1<P: AsRef<Path>>(
path: P,
prev_config: Option<ConfigV1>,
) -> Result<ConfigV2, NymNodeError> {
tracing::debug!("Updating from 1.1.2");
let old_cfg = if let Some(prev_config) = prev_config {
prev_config
} else {
ConfigV1::read_from_path(&path)?
};
let wireguard = WireguardV2 {
// currently there are no upgrades
pub async fn try_upgrade_config_1_1_2<P: AsRef<Path>>(path: P) -> Result<(), NymNodeError> {
let old_cfg = Config1_1_2::read_from_path(&path)?;
let wireguard = Wireguard1_1_3 {
enabled: old_cfg.wireguard.enabled,
bind_address: old_cfg.wireguard.bind_address,
private_ip: old_cfg.wireguard.private_network_ip,
announced_port: old_cfg.wireguard.announced_port,
private_network_prefix: old_cfg.wireguard.private_network_prefix,
storage_paths: WireguardPathsV2::new(ConfigV2::default_data_directory(path)?),
storage_paths: WireguardPaths1_1_3::new(Config1_1_3::default_data_directory(path)?),
};
initialise(&wireguard).map_err(|err| KeyIOFailure::KeyPairStoreFailure {
keys: "wg-x25519-dh".to_string(),
paths: wireguard.storage_paths.x25519_wireguard_storage_paths(),
err,
})?;
let cfg = ConfigV2 {
let cfg = Config1_1_3 {
save_path: old_cfg.save_path,
id: old_cfg.id,
mode: old_cfg.mode.into(),
host: HostV2 {
host: Host1_1_3 {
public_ips: old_cfg.host.public_ips,
hostname: old_cfg.host.hostname,
location: old_cfg.host.location,
},
mixnet: MixnetV2 {
mixnet: Mixnet1_1_3 {
bind_address: old_cfg.mixnet.bind_address,
nym_api_urls: old_cfg.mixnet.nym_api_urls,
nyxd_urls: old_cfg.mixnet.nyxd_urls,
debug: MixnetDebugV2 {
debug: MixnetDebug1_1_3 {
packet_forwarding_initial_backoff: old_cfg
.mixnet
.debug
@@ -804,8 +797,8 @@ pub async fn try_upgrade_config_v1<P: AsRef<Path>>(
unsafe_disable_noise: old_cfg.mixnet.debug.unsafe_disable_noise,
},
},
storage_paths: NymNodePathsV2 {
keys: KeysPathsV2 {
storage_paths: NymNodePaths1_1_3 {
keys: KeysPaths1_1_3 {
private_ed25519_identity_key_file: old_cfg
.storage_paths
.keys
@@ -833,7 +826,7 @@ pub async fn try_upgrade_config_v1<P: AsRef<Path>>(
},
description: old_cfg.storage_paths.description,
},
http: HttpV2 {
http: Http1_1_3 {
bind_address: old_cfg.http.bind_address,
landing_page_assets_path: old_cfg.http.landing_page_assets_path,
access_token: old_cfg.http.access_token,
@@ -842,11 +835,11 @@ pub async fn try_upgrade_config_v1<P: AsRef<Path>>(
expose_crypto_hardware: old_cfg.http.expose_crypto_hardware,
},
wireguard,
mixnode: MixnodeConfigV2 {
storage_paths: MixnodePathsV2 {},
verloc: VerlocV2 {
mixnode: MixnodeConfig1_1_3 {
storage_paths: MixnodePaths1_1_3 {},
verloc: Verloc1_1_3 {
bind_address: old_cfg.mixnode.verloc.bind_address,
debug: VerlocDebugV2 {
debug: VerlocDebug1_1_3 {
packets_per_node: old_cfg.mixnode.verloc.debug.packets_per_node,
connection_timeout: old_cfg.mixnode.verloc.debug.connection_timeout,
packet_timeout: old_cfg.mixnode.verloc.debug.packet_timeout,
@@ -856,13 +849,13 @@ pub async fn try_upgrade_config_v1<P: AsRef<Path>>(
retry_timeout: old_cfg.mixnode.verloc.debug.retry_timeout,
},
},
debug: DebugV2 {
debug: Debug1_1_3 {
node_stats_logging_delay: old_cfg.mixnode.debug.node_stats_logging_delay,
node_stats_updating_delay: old_cfg.mixnode.debug.node_stats_updating_delay,
},
},
entry_gateway: EntryGatewayConfigV2 {
storage_paths: EntryGatewayPathsV2 {
entry_gateway: EntryGatewayConfig1_1_3 {
storage_paths: EntryGatewayPaths1_1_3 {
clients_storage: old_cfg.entry_gateway.storage_paths.clients_storage,
cosmos_mnemonic: old_cfg.entry_gateway.storage_paths.cosmos_mnemonic,
},
@@ -870,13 +863,13 @@ pub async fn try_upgrade_config_v1<P: AsRef<Path>>(
bind_address: old_cfg.entry_gateway.bind_address,
announce_ws_port: old_cfg.entry_gateway.announce_ws_port,
announce_wss_port: old_cfg.entry_gateway.announce_wss_port,
debug: EntryGatewayConfigDebugV2 {
debug: EntryGatewayConfigDebug1_1_3 {
message_retrieval_limit: old_cfg.entry_gateway.debug.message_retrieval_limit,
},
},
exit_gateway: ExitGatewayConfigV2 {
storage_paths: ExitGatewayPathsV2 {
network_requester: NetworkRequesterPathsV2 {
exit_gateway: ExitGatewayConfig1_1_3 {
storage_paths: ExitGatewayPaths1_1_3 {
network_requester: NetworkRequesterPaths1_1_3 {
private_ed25519_identity_key_file: old_cfg
.exit_gateway
.storage_paths
@@ -913,7 +906,7 @@ pub async fn try_upgrade_config_v1<P: AsRef<Path>>(
.network_requester
.gateway_registrations,
},
ip_packet_router: IpPacketRouterPathsV2 {
ip_packet_router: IpPacketRouterPaths1_1_3 {
private_ed25519_identity_key_file: old_cfg
.exit_gateway
.storage_paths
@@ -953,8 +946,8 @@ pub async fn try_upgrade_config_v1<P: AsRef<Path>>(
},
open_proxy: old_cfg.exit_gateway.open_proxy,
upstream_exit_policy_url: old_cfg.exit_gateway.upstream_exit_policy_url,
network_requester: NetworkRequesterV2 {
debug: NetworkRequesterDebugV2 {
network_requester: NetworkRequester1_1_3 {
debug: NetworkRequesterDebug1_1_3 {
enabled: old_cfg.exit_gateway.network_requester.debug.enabled,
disable_poisson_rate: old_cfg
.exit_gateway
@@ -964,8 +957,8 @@ pub async fn try_upgrade_config_v1<P: AsRef<Path>>(
client_debug: old_cfg.exit_gateway.network_requester.debug.client_debug,
},
},
ip_packet_router: IpPacketRouterV2 {
debug: IpPacketRouterDebugV2 {
ip_packet_router: IpPacketRouter1_1_3 {
debug: IpPacketRouterDebug1_1_3 {
enabled: old_cfg.exit_gateway.ip_packet_router.debug.enabled,
disable_poisson_rate: old_cfg
.exit_gateway
@@ -976,8 +969,10 @@ pub async fn try_upgrade_config_v1<P: AsRef<Path>>(
},
},
},
logging: LoggingSettingsV2 {},
logging: LoggingSettings1_1_3 {},
};
Ok(cfg)
cfg.save()?;
Ok(())
}
@@ -2,38 +2,25 @@
// SPDX-License-Identifier: GPL-3.0-only
#![allow(dead_code)]
use crate::{config::*, error::KeyIOFailure};
use entry_gateway::Debug as EntryGatewayConfigDebug;
use exit_gateway::{IpPacketRouter, IpPacketRouterDebug, NetworkRequester, NetworkRequesterDebug};
use mixnode::{Verloc, VerlocDebug};
use crate::config::*;
use nym_client_core_config_types::DebugConfig as ClientDebugConfig;
use nym_config::serde_helpers::de_maybe_port;
use nym_crypto::asymmetric::{ed25519, x25519};
use nym_network_requester::{
set_active_gateway, setup_fs_gateways_storage, store_gateway_details, CustomGatewayDetails,
GatewayDetails,
};
use nym_pemstore::{load_key, store_key, store_keypair};
use nym_sphinx_acknowledgements::AckKey;
use persistence::{
AuthenticatorPaths, EntryGatewayPaths, ExitGatewayPaths, IpPacketRouterPaths, KeysPaths,
MixnodePaths, NetworkRequesterPaths, WireguardPaths,
};
use nym_crypto::asymmetric::encryption::KeyPair;
use nym_pemstore::store_keypair;
use rand::rngs::OsRng;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct WireguardPathsV2 {
pub struct WireguardPaths1_1_3 {
pub private_diffie_hellman_key_file: PathBuf,
pub public_diffie_hellman_key_file: PathBuf,
}
impl WireguardPathsV2 {
impl WireguardPaths1_1_3 {
pub fn new<P: AsRef<Path>>(data_dir: P) -> Self {
let data_dir = data_dir.as_ref();
WireguardPathsV2 {
WireguardPaths1_1_3 {
private_diffie_hellman_key_file: data_dir
.join(persistence::DEFAULT_X25519_WG_DH_KEY_FILENAME),
public_diffie_hellman_key_file: data_dir
@@ -51,7 +38,7 @@ impl WireguardPathsV2 {
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct WireguardV2 {
pub struct Wireguard1_1_3 {
/// Specifies whether the wireguard service is enabled on this node.
pub enabled: bool,
@@ -72,13 +59,13 @@ pub struct WireguardV2 {
pub private_network_prefix: u8,
/// Paths for wireguard keys, client registries, etc.
pub storage_paths: WireguardPathsV2,
pub storage_paths: WireguardPaths1_1_3,
}
// a temporary solution until all "types" are run at the same time
#[derive(Debug, Default, Serialize, Deserialize, ValueEnum, Clone, Copy)]
#[serde(rename_all = "snake_case")]
pub enum NodeModeV2 {
pub enum NodeMode1_1_3 {
#[default]
#[clap(alias = "mix")]
Mixnode,
@@ -90,21 +77,11 @@ pub enum NodeModeV2 {
ExitGateway,
}
impl From<NodeModeV2> for NodeMode {
fn from(config: NodeModeV2) -> Self {
match config {
NodeModeV2::Mixnode => NodeMode::Mixnode,
NodeModeV2::EntryGateway => NodeMode::EntryGateway,
NodeModeV2::ExitGateway => NodeMode::ExitGateway,
}
}
}
// TODO: this is very much a WIP. we need proper ssl certificate support here
#[derive(Debug, Clone, Default, Deserialize, PartialEq, Serialize)]
#[serde(default)]
#[serde(deny_unknown_fields)]
pub struct HostV2 {
pub struct Host1_1_3 {
/// Ip address(es) of this host, such as 1.1.1.1 that external clients will use for connections.
/// If no values are provided, when this node gets included in the network,
/// its ip addresses will be populated by whatever value is resolved by associated nym-api.
@@ -123,7 +100,7 @@ pub struct HostV2 {
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(default)]
#[serde(deny_unknown_fields)]
pub struct MixnetDebugV2 {
pub struct MixnetDebug1_1_3 {
/// Initial value of an exponential backoff to reconnect to dropped TCP connection when
/// forwarding sphinx packets.
#[serde(with = "humantime_serde")]
@@ -145,16 +122,16 @@ pub struct MixnetDebugV2 {
pub unsafe_disable_noise: bool,
}
impl MixnetDebugV2 {
impl MixnetDebug1_1_3 {
const DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF: Duration = Duration::from_millis(10_000);
const DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF: Duration = Duration::from_millis(300_000);
const DEFAULT_INITIAL_CONNECTION_TIMEOUT: Duration = Duration::from_millis(1_500);
const DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE: usize = 2000;
}
impl Default for MixnetDebugV2 {
impl Default for MixnetDebug1_1_3 {
fn default() -> Self {
MixnetDebugV2 {
MixnetDebug1_1_3 {
packet_forwarding_initial_backoff: Self::DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF,
packet_forwarding_maximum_backoff: Self::DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF,
initial_connection_timeout: Self::DEFAULT_INITIAL_CONNECTION_TIMEOUT,
@@ -165,7 +142,7 @@ impl Default for MixnetDebugV2 {
}
}
impl Default for MixnetV2 {
impl Default for Mixnet1_1_3 {
fn default() -> Self {
// SAFETY:
// our hardcoded values should always be valid
@@ -184,7 +161,7 @@ impl Default for MixnetV2 {
vec![mainnet::NYXD_URL.parse().expect("Invalid default nyxd URL")]
};
MixnetV2 {
Mixnet1_1_3 {
bind_address: SocketAddr::new(inaddr_any(), DEFAULT_MIXNET_PORT),
nym_api_urls,
nyxd_urls,
@@ -196,7 +173,7 @@ impl Default for MixnetV2 {
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(default)]
#[serde(deny_unknown_fields)]
pub struct MixnetV2 {
pub struct Mixnet1_1_3 {
/// Address this node will bind to for listening for mixnet packets
/// default: `0.0.0.0:1789`
pub bind_address: SocketAddr,
@@ -208,12 +185,12 @@ pub struct MixnetV2 {
pub nyxd_urls: Vec<Url>,
#[serde(default)]
pub debug: MixnetDebugV2,
pub debug: MixnetDebug1_1_3,
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct KeysPathsV2 {
pub struct KeysPaths1_1_3 {
/// Path to file containing ed25519 identity private key.
pub private_ed25519_identity_key_file: PathBuf,
@@ -235,8 +212,8 @@ pub struct KeysPathsV2 {
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct NymNodePathsV2 {
pub keys: KeysPathsV2,
pub struct NymNodePaths1_1_3 {
pub keys: KeysPaths1_1_3,
/// Path to a file containing basic node description: human-readable name, website, details, etc.
pub description: PathBuf,
@@ -245,7 +222,7 @@ pub struct NymNodePathsV2 {
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(default)]
#[serde(deny_unknown_fields)]
pub struct HttpV2 {
pub struct Http1_1_3 {
/// Socket address this node will use for binding its http API.
/// default: `0.0.0.0:8080`
pub bind_address: SocketAddr,
@@ -274,9 +251,9 @@ pub struct HttpV2 {
pub expose_crypto_hardware: bool,
}
impl Default for HttpV2 {
impl Default for Http1_1_3 {
fn default() -> Self {
HttpV2 {
Http1_1_3 {
bind_address: SocketAddr::new(inaddr_any(), DEFAULT_HTTP_PORT),
landing_page_assets_path: None,
access_token: None,
@@ -289,11 +266,11 @@ impl Default for HttpV2 {
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct MixnodePathsV2 {}
pub struct MixnodePaths1_1_3 {}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct DebugV2 {
pub struct Debug1_1_3 {
/// Delay between each subsequent node statistics being logged to the console
#[serde(with = "humantime_serde")]
pub node_stats_logging_delay: Duration,
@@ -305,7 +282,7 @@ pub struct DebugV2 {
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct VerlocDebugV2 {
pub struct VerlocDebug1_1_3 {
/// Specifies number of echo packets sent to each node during a measurement run.
pub packets_per_node: usize,
@@ -336,16 +313,16 @@ pub struct VerlocDebugV2 {
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct VerlocV2 {
pub struct Verloc1_1_3 {
/// Socket address this node will use for binding its verloc API.
/// default: `0.0.0.0:1790`
pub bind_address: SocketAddr,
#[serde(default)]
pub debug: VerlocDebugV2,
pub debug: VerlocDebug1_1_3,
}
impl VerlocDebugV2 {
impl VerlocDebug1_1_3 {
const DEFAULT_PACKETS_PER_NODE: usize = 100;
const DEFAULT_CONNECTION_TIMEOUT: Duration = Duration::from_millis(5000);
const DEFAULT_PACKET_TIMEOUT: Duration = Duration::from_millis(1500);
@@ -355,9 +332,9 @@ impl VerlocDebugV2 {
const DEFAULT_RETRY_TIMEOUT: Duration = Duration::from_secs(60 * 30);
}
impl Default for VerlocDebugV2 {
impl Default for VerlocDebug1_1_3 {
fn default() -> Self {
VerlocDebugV2 {
VerlocDebug1_1_3 {
packets_per_node: Self::DEFAULT_PACKETS_PER_NODE,
connection_timeout: Self::DEFAULT_CONNECTION_TIMEOUT,
packet_timeout: Self::DEFAULT_PACKET_TIMEOUT,
@@ -371,23 +348,23 @@ impl Default for VerlocDebugV2 {
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct MixnodeConfigV2 {
pub storage_paths: MixnodePathsV2,
pub struct MixnodeConfig1_1_3 {
pub storage_paths: MixnodePaths1_1_3,
pub verloc: VerlocV2,
pub verloc: Verloc1_1_3,
#[serde(default)]
pub debug: DebugV2,
pub debug: Debug1_1_3,
}
impl DebugV2 {
impl Debug1_1_3 {
const DEFAULT_NODE_STATS_LOGGING_DELAY: Duration = Duration::from_millis(60_000);
const DEFAULT_NODE_STATS_UPDATING_DELAY: Duration = Duration::from_millis(30_000);
}
impl Default for DebugV2 {
impl Default for Debug1_1_3 {
fn default() -> Self {
DebugV2 {
Debug1_1_3 {
node_stats_logging_delay: Self::DEFAULT_NODE_STATS_LOGGING_DELAY,
node_stats_updating_delay: Self::DEFAULT_NODE_STATS_UPDATING_DELAY,
}
@@ -396,7 +373,7 @@ impl Default for DebugV2 {
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct EntryGatewayPathsV2 {
pub struct EntryGatewayPaths1_1_3 {
/// Path to sqlite database containing all persistent data: messages for offline clients,
/// derived shared keys and available client bandwidths.
pub clients_storage: PathBuf,
@@ -407,18 +384,18 @@ pub struct EntryGatewayPathsV2 {
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct EntryGatewayConfigDebugV2 {
pub struct EntryGatewayConfigDebug1_1_3 {
/// Number of messages from offline client that can be pulled at once (i.e. with a single SQL query) from the storage.
pub message_retrieval_limit: i64,
}
impl EntryGatewayConfigDebugV2 {
impl EntryGatewayConfigDebug1_1_3 {
const DEFAULT_MESSAGE_RETRIEVAL_LIMIT: i64 = 100;
}
impl Default for EntryGatewayConfigDebugV2 {
impl Default for EntryGatewayConfigDebug1_1_3 {
fn default() -> Self {
EntryGatewayConfigDebugV2 {
EntryGatewayConfigDebug1_1_3 {
message_retrieval_limit: Self::DEFAULT_MESSAGE_RETRIEVAL_LIMIT,
}
}
@@ -426,8 +403,8 @@ impl Default for EntryGatewayConfigDebugV2 {
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct EntryGatewayConfigV2 {
pub storage_paths: EntryGatewayPathsV2,
pub struct EntryGatewayConfig1_1_3 {
pub storage_paths: EntryGatewayPaths1_1_3,
/// Indicates whether this gateway is accepting only coconut credentials for accessing the mixnet
/// or if it also accepts non-paying clients
@@ -449,12 +426,12 @@ pub struct EntryGatewayConfigV2 {
pub announce_wss_port: Option<u16>,
#[serde(default)]
pub debug: EntryGatewayConfigDebugV2,
pub debug: EntryGatewayConfigDebug1_1_3,
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct NetworkRequesterPathsV2 {
pub struct NetworkRequesterPaths1_1_3 {
/// Path to file containing network requester ed25519 identity private key.
pub private_ed25519_identity_key_file: PathBuf,
@@ -484,7 +461,7 @@ pub struct NetworkRequesterPathsV2 {
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct IpPacketRouterPathsV2 {
pub struct IpPacketRouterPaths1_1_3 {
/// Path to file containing ip packet router ed25519 identity private key.
pub private_ed25519_identity_key_file: PathBuf,
@@ -514,15 +491,15 @@ pub struct IpPacketRouterPathsV2 {
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ExitGatewayPathsV2 {
pub network_requester: NetworkRequesterPathsV2,
pub struct ExitGatewayPaths1_1_3 {
pub network_requester: NetworkRequesterPaths1_1_3,
pub ip_packet_router: IpPacketRouterPathsV2,
pub ip_packet_router: IpPacketRouterPaths1_1_3,
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default)]
pub struct IpPacketRouterDebugV2 {
pub struct IpPacketRouterDebug1_1_3 {
/// Specifies whether ip packet routing service is enabled in this process.
/// This is only here for debugging purposes as exit gateway should always run **both**
/// network requester and an ip packet router.
@@ -538,9 +515,9 @@ pub struct IpPacketRouterDebugV2 {
pub client_debug: ClientDebugConfig,
}
impl Default for IpPacketRouterDebugV2 {
impl Default for IpPacketRouterDebug1_1_3 {
fn default() -> Self {
IpPacketRouterDebugV2 {
IpPacketRouterDebug1_1_3 {
enabled: true,
disable_poisson_rate: true,
client_debug: Default::default(),
@@ -549,22 +526,22 @@ impl Default for IpPacketRouterDebugV2 {
}
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
pub struct IpPacketRouterV2 {
pub struct IpPacketRouter1_1_3 {
#[serde(default)]
pub debug: IpPacketRouterDebugV2,
pub debug: IpPacketRouterDebug1_1_3,
}
#[allow(clippy::derivable_impls)]
impl Default for IpPacketRouterV2 {
impl Default for IpPacketRouter1_1_3 {
fn default() -> Self {
IpPacketRouterV2 {
IpPacketRouter1_1_3 {
debug: Default::default(),
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
pub struct NetworkRequesterDebugV2 {
pub struct NetworkRequesterDebug1_1_3 {
/// Specifies whether network requester service is enabled in this process.
/// This is only here for debugging purposes as exit gateway should always run **both**
/// network requester and an ip packet router.
@@ -580,9 +557,9 @@ pub struct NetworkRequesterDebugV2 {
pub client_debug: ClientDebugConfig,
}
impl Default for NetworkRequesterDebugV2 {
impl Default for NetworkRequesterDebug1_1_3 {
fn default() -> Self {
NetworkRequesterDebugV2 {
NetworkRequesterDebug1_1_3 {
enabled: true,
disable_poisson_rate: true,
client_debug: Default::default(),
@@ -591,15 +568,15 @@ impl Default for NetworkRequesterDebugV2 {
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
pub struct NetworkRequesterV2 {
pub struct NetworkRequester1_1_3 {
#[serde(default)]
pub debug: NetworkRequesterDebugV2,
pub debug: NetworkRequesterDebug1_1_3,
}
#[allow(clippy::derivable_impls)]
impl Default for NetworkRequesterV2 {
impl Default for NetworkRequester1_1_3 {
fn default() -> Self {
NetworkRequesterV2 {
NetworkRequester1_1_3 {
debug: Default::default(),
}
}
@@ -607,8 +584,8 @@ impl Default for NetworkRequesterV2 {
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ExitGatewayConfigV2 {
pub storage_paths: ExitGatewayPathsV2,
pub struct ExitGatewayConfig1_1_3 {
pub storage_paths: ExitGatewayPaths1_1_3,
/// specifies whether this exit node should run in 'open-proxy' mode
/// and thus would attempt to resolve **ANY** request it receives.
@@ -617,20 +594,20 @@ pub struct ExitGatewayConfigV2 {
/// Specifies the url for an upstream source of the exit policy used by this node.
pub upstream_exit_policy_url: Url,
pub network_requester: NetworkRequesterV2,
pub network_requester: NetworkRequester1_1_3,
pub ip_packet_router: IpPacketRouterV2,
pub ip_packet_router: IpPacketRouter1_1_3,
}
#[derive(Debug, Default, Copy, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct LoggingSettingsV2 {
pub struct LoggingSettings1_1_3 {
// well, we need to implement something here at some point...
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV2 {
pub struct Config1_1_3 {
// additional metadata holding on-disk location of this config file
#[serde(skip)]
pub(crate) save_path: Option<PathBuf>,
@@ -640,37 +617,37 @@ pub struct ConfigV2 {
/// Current mode of this nym-node.
/// Expect this field to be changed in the future to allow running the node in multiple modes (i.e. mixnode + gateway)
pub mode: NodeModeV2,
pub mode: NodeMode1_1_3,
pub host: HostV2,
pub host: Host1_1_3,
pub mixnet: MixnetV2,
pub mixnet: Mixnet1_1_3,
/// Storage paths to persistent nym-node data, such as its long term keys.
pub storage_paths: NymNodePathsV2,
pub storage_paths: NymNodePaths1_1_3,
#[serde(default)]
pub http: HttpV2,
pub http: Http1_1_3,
pub wireguard: WireguardV2,
pub wireguard: Wireguard1_1_3,
pub mixnode: MixnodeConfigV2,
pub mixnode: MixnodeConfig1_1_3,
pub entry_gateway: EntryGatewayConfigV2,
pub entry_gateway: EntryGatewayConfig1_1_3,
pub exit_gateway: ExitGatewayConfigV2,
pub exit_gateway: ExitGatewayConfig1_1_3,
#[serde(default)]
pub logging: LoggingSettingsV2,
pub logging: LoggingSettings1_1_3,
}
impl NymConfigTemplate for ConfigV2 {
impl NymConfigTemplate for Config1_1_3 {
fn template(&self) -> &'static str {
CONFIG_TEMPLATE
}
}
impl ConfigV2 {
impl Config1_1_3 {
pub fn save(&self) -> Result<(), NymNodeError> {
let save_location = self.save_location();
debug!(
@@ -738,7 +715,7 @@ impl ConfigV2 {
// simple wrapper that reads config file and assigns path location
fn read_from_path<P: AsRef<Path>>(path: P) -> Result<Self, NymNodeError> {
let path = path.as_ref();
let mut loaded: ConfigV2 =
let mut loaded: Config1_1_3 =
read_config_from_toml_file(path).map_err(|source| NymNodeError::ConfigLoadFailure {
path: path.to_path_buf(),
source,
@@ -753,303 +730,37 @@ impl ConfigV2 {
}
}
pub async fn initialise(
paths: &AuthenticatorPaths,
public_key: nym_crypto::asymmetric::identity::PublicKey,
) -> Result<(), NymNodeError> {
fn initialise(config: &Wireguard1_1_3) -> std::io::Result<()> {
let mut rng = OsRng;
let ed25519_keys = ed25519::KeyPair::new(&mut rng);
let x25519_keys = x25519::KeyPair::new(&mut rng);
let aes128ctr_key = AckKey::new(&mut rng);
let gateway_details = GatewayDetails::Custom(CustomGatewayDetails::new(public_key)).into();
let x25519_keys = KeyPair::new(&mut rng);
store_keypair(&ed25519_keys, &paths.ed25519_identity_storage_paths()).map_err(|e| {
KeyIOFailure::KeyPairStoreFailure {
keys: "ed25519-identity".to_string(),
paths: paths.ed25519_identity_storage_paths(),
err: e,
}
})?;
store_keypair(&x25519_keys, &paths.x25519_diffie_hellman_storage_paths()).map_err(|e| {
KeyIOFailure::KeyPairStoreFailure {
keys: "x25519-dh".to_string(),
paths: paths.x25519_diffie_hellman_storage_paths(),
err: e,
}
})?;
store_key(&aes128ctr_key, &paths.ack_key_file).map_err(|e| KeyIOFailure::KeyStoreFailure {
key: "ack".to_string(),
path: paths.ack_key_file.clone(),
err: e,
})?;
// insert all required information into the gateways store
// (I hate that we have to do it, but that's currently the simplest thing to do)
let storage = setup_fs_gateways_storage(&paths.gateway_registrations).await?;
store_gateway_details(&storage, &gateway_details).await?;
set_active_gateway(&storage, &gateway_details.gateway_id().to_base58_string()).await?;
store_keypair(
&x25519_keys,
&config.storage_paths.x25519_wireguard_storage_paths(),
)?;
Ok(())
}
pub async fn try_upgrade_config_v2<P: AsRef<Path>>(
path: P,
prev_config: Option<ConfigV2>,
) -> Result<Config, NymNodeError> {
tracing::debug!("Updating from 1.1.3");
let old_cfg = if let Some(prev_config) = prev_config {
prev_config
} else {
ConfigV2::read_from_path(&path)?
};
let authenticator_paths = AuthenticatorPaths::new(
old_cfg
.exit_gateway
.storage_paths
.ip_packet_router
.private_ed25519_identity_key_file
.parent()
.ok_or(NymNodeError::DataDirDerivationFailure)?,
);
// currently there are no upgrades
pub async fn try_upgrade_config_1_1_3<P: AsRef<Path>>(path: P) -> Result<(), NymNodeError> {
let old_cfg = Config::read_from_path(&path)?;
let cfg = Config {
save_path: old_cfg.save_path,
id: old_cfg.id,
mode: old_cfg.mode.into(),
host: Host {
public_ips: old_cfg.host.public_ips,
hostname: old_cfg.host.hostname,
location: old_cfg.host.location,
},
mixnet: Mixnet {
bind_address: old_cfg.mixnet.bind_address,
nym_api_urls: old_cfg.mixnet.nym_api_urls,
nyxd_urls: old_cfg.mixnet.nyxd_urls,
debug: MixnetDebug {
packet_forwarding_initial_backoff: old_cfg
.mixnet
.debug
.packet_forwarding_initial_backoff,
packet_forwarding_maximum_backoff: old_cfg
.mixnet
.debug
.packet_forwarding_maximum_backoff,
initial_connection_timeout: old_cfg.mixnet.debug.initial_connection_timeout,
maximum_connection_buffer_size: old_cfg.mixnet.debug.maximum_connection_buffer_size,
unsafe_disable_noise: old_cfg.mixnet.debug.unsafe_disable_noise,
},
},
storage_paths: NymNodePaths {
keys: KeysPaths {
private_ed25519_identity_key_file: old_cfg
.storage_paths
.keys
.private_ed25519_identity_key_file,
public_ed25519_identity_key_file: old_cfg
.storage_paths
.keys
.public_ed25519_identity_key_file,
private_x25519_sphinx_key_file: old_cfg
.storage_paths
.keys
.private_x25519_sphinx_key_file,
public_x25519_sphinx_key_file: old_cfg
.storage_paths
.keys
.public_x25519_sphinx_key_file,
private_x25519_noise_key_file: old_cfg
.storage_paths
.keys
.private_x25519_noise_key_file,
public_x25519_noise_key_file: old_cfg
.storage_paths
.keys
.public_x25519_noise_key_file,
},
description: old_cfg.storage_paths.description,
},
http: Http {
bind_address: old_cfg.http.bind_address,
landing_page_assets_path: old_cfg.http.landing_page_assets_path,
access_token: old_cfg.http.access_token,
expose_system_info: old_cfg.http.expose_system_info,
expose_system_hardware: old_cfg.http.expose_system_hardware,
expose_crypto_hardware: old_cfg.http.expose_crypto_hardware,
},
wireguard: Wireguard {
enabled: old_cfg.wireguard.enabled,
bind_address: old_cfg.wireguard.bind_address,
private_ip: old_cfg.wireguard.private_ip,
announced_port: old_cfg.wireguard.announced_port,
private_network_prefix: old_cfg.wireguard.private_network_prefix,
storage_paths: WireguardPaths {
private_diffie_hellman_key_file: old_cfg
.wireguard
.storage_paths
.private_diffie_hellman_key_file,
public_diffie_hellman_key_file: old_cfg
.wireguard
.storage_paths
.public_diffie_hellman_key_file,
},
},
mixnode: MixnodeConfig {
storage_paths: MixnodePaths {},
verloc: Verloc {
bind_address: old_cfg.mixnode.verloc.bind_address,
debug: VerlocDebug {
packets_per_node: old_cfg.mixnode.verloc.debug.packets_per_node,
connection_timeout: old_cfg.mixnode.verloc.debug.connection_timeout,
packet_timeout: old_cfg.mixnode.verloc.debug.packet_timeout,
delay_between_packets: old_cfg.mixnode.verloc.debug.delay_between_packets,
tested_nodes_batch_size: old_cfg.mixnode.verloc.debug.tested_nodes_batch_size,
testing_interval: old_cfg.mixnode.verloc.debug.testing_interval,
retry_timeout: old_cfg.mixnode.verloc.debug.retry_timeout,
},
},
debug: mixnode::Debug {
node_stats_logging_delay: old_cfg.mixnode.debug.node_stats_logging_delay,
node_stats_updating_delay: old_cfg.mixnode.debug.node_stats_updating_delay,
},
},
entry_gateway: EntryGatewayConfig {
storage_paths: EntryGatewayPaths {
clients_storage: old_cfg.entry_gateway.storage_paths.clients_storage,
cosmos_mnemonic: old_cfg.entry_gateway.storage_paths.cosmos_mnemonic,
authenticator: authenticator_paths.clone(),
},
enforce_zk_nyms: old_cfg.entry_gateway.enforce_zk_nyms,
bind_address: old_cfg.entry_gateway.bind_address,
announce_ws_port: old_cfg.entry_gateway.announce_ws_port,
announce_wss_port: old_cfg.entry_gateway.announce_wss_port,
debug: EntryGatewayConfigDebug {
message_retrieval_limit: old_cfg.entry_gateway.debug.message_retrieval_limit,
},
},
exit_gateway: ExitGatewayConfig {
storage_paths: ExitGatewayPaths {
network_requester: NetworkRequesterPaths {
private_ed25519_identity_key_file: old_cfg
.exit_gateway
.storage_paths
.network_requester
.private_ed25519_identity_key_file,
public_ed25519_identity_key_file: old_cfg
.exit_gateway
.storage_paths
.network_requester
.public_ed25519_identity_key_file,
private_x25519_diffie_hellman_key_file: old_cfg
.exit_gateway
.storage_paths
.network_requester
.private_x25519_diffie_hellman_key_file,
public_x25519_diffie_hellman_key_file: old_cfg
.exit_gateway
.storage_paths
.network_requester
.public_x25519_diffie_hellman_key_file,
ack_key_file: old_cfg
.exit_gateway
.storage_paths
.network_requester
.ack_key_file,
reply_surb_database: old_cfg
.exit_gateway
.storage_paths
.network_requester
.reply_surb_database,
gateway_registrations: old_cfg
.exit_gateway
.storage_paths
.network_requester
.gateway_registrations,
},
ip_packet_router: IpPacketRouterPaths {
private_ed25519_identity_key_file: old_cfg
.exit_gateway
.storage_paths
.ip_packet_router
.private_ed25519_identity_key_file,
public_ed25519_identity_key_file: old_cfg
.exit_gateway
.storage_paths
.ip_packet_router
.public_ed25519_identity_key_file,
private_x25519_diffie_hellman_key_file: old_cfg
.exit_gateway
.storage_paths
.ip_packet_router
.private_x25519_diffie_hellman_key_file,
public_x25519_diffie_hellman_key_file: old_cfg
.exit_gateway
.storage_paths
.ip_packet_router
.public_x25519_diffie_hellman_key_file,
ack_key_file: old_cfg
.exit_gateway
.storage_paths
.ip_packet_router
.ack_key_file,
reply_surb_database: old_cfg
.exit_gateway
.storage_paths
.ip_packet_router
.reply_surb_database,
gateway_registrations: old_cfg
.exit_gateway
.storage_paths
.ip_packet_router
.gateway_registrations,
},
authenticator: authenticator_paths.clone(),
},
open_proxy: old_cfg.exit_gateway.open_proxy,
upstream_exit_policy_url: old_cfg.exit_gateway.upstream_exit_policy_url,
network_requester: NetworkRequester {
debug: NetworkRequesterDebug {
enabled: old_cfg.exit_gateway.network_requester.debug.enabled,
disable_poisson_rate: old_cfg
.exit_gateway
.network_requester
.debug
.disable_poisson_rate,
client_debug: old_cfg.exit_gateway.network_requester.debug.client_debug,
},
},
ip_packet_router: IpPacketRouter {
debug: IpPacketRouterDebug {
enabled: old_cfg.exit_gateway.ip_packet_router.debug.enabled,
disable_poisson_rate: old_cfg
.exit_gateway
.ip_packet_router
.debug
.disable_poisson_rate,
client_debug: old_cfg.exit_gateway.ip_packet_router.debug.client_debug,
},
},
},
authenticator: Default::default(),
logging: LoggingSettings {},
mode: old_cfg.mode,
host: old_cfg.host,
mixnet: old_cfg.mixnet,
storage_paths: old_cfg.storage_paths,
http: old_cfg.http,
wireguard: old_cfg.wireguard,
mixnode: old_cfg.mixnode,
entry_gateway: old_cfg.entry_gateway,
exit_gateway: old_cfg.exit_gateway,
logging: old_cfg.logging,
};
let public_key = load_key(
cfg.storage_paths
.keys
.ed25519_identity_storage_paths()
.public_key_path,
)
.map_err(|source| NymNodeError::DescriptionLoadFailure {
path: cfg
.storage_paths
.keys
.ed25519_identity_storage_paths()
.public_key_path,
source,
})?;
cfg.save()?;
initialise(&authenticator_paths, public_key).await?;
Ok(cfg)
Ok(())
}
-3
View File
@@ -149,8 +149,6 @@ pub struct EntryGatewayPaths {
/// Path to file containing cosmos account mnemonic used for zk-nym redemption.
pub cosmos_mnemonic: PathBuf,
pub authenticator: AuthenticatorPaths,
}
impl EntryGatewayPaths {
@@ -158,7 +156,6 @@ impl EntryGatewayPaths {
EntryGatewayPaths {
clients_storage: data_dir.as_ref().join(DEFAULT_CLIENTS_STORAGE_FILENAME),
cosmos_mnemonic: data_dir.as_ref().join(DEFAULT_MNEMONIC_FILENAME),
authenticator: AuthenticatorPaths::new(data_dir),
}
}
-57
View File
@@ -167,7 +167,6 @@ announce_ws_port = {{#if entry_gateway.announce_ws_port }} {{ entry_gateway.anno
# (default: 0 - disabled)
announce_wss_port = {{#if entry_gateway.announce_wss_port }} {{ entry_gateway.announce_wss_port }} {{else}} 0 {{/if}}
[entry_gateway.storage_paths]
# Path to sqlite database containing all persistent data: messages for offline clients,
# derived shared keys and available client bandwidths.
@@ -176,32 +175,6 @@ clients_storage = '{{ entry_gateway.storage_paths.clients_storage }}'
# Path to file containing cosmos account mnemonic used for zk-nym redemption.
cosmos_mnemonic = '{{ entry_gateway.storage_paths.cosmos_mnemonic }}'
[entry_gateway.storage_paths.authenticator]
# Path to file containing authenticator ed25519 identity private key.
private_ed25519_identity_key_file = '{{ entry_gateway.storage_paths.authenticator.private_ed25519_identity_key_file }}'
# Path to file containing authenticator ed25519 identity public key.
public_ed25519_identity_key_file = '{{ entry_gateway.storage_paths.authenticator.public_ed25519_identity_key_file }}'
# Path to file containing authenticator x25519 diffie hellman private key.
private_x25519_diffie_hellman_key_file = '{{ entry_gateway.storage_paths.authenticator.private_x25519_diffie_hellman_key_file }}'
# Path to file containing authenticator x25519 diffie hellman public key.
public_x25519_diffie_hellman_key_file = '{{ entry_gateway.storage_paths.authenticator.public_x25519_diffie_hellman_key_file }}'
# 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.
ack_key_file = '{{ entry_gateway.storage_paths.authenticator.ack_key_file }}'
# Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
reply_surb_database = '{{ entry_gateway.storage_paths.authenticator.reply_surb_database }}'
# Normally this is a path to the file containing information about gateways used by this client,
# i.e. details such as their public keys, owner addresses or the network information.
# but in this case it just has the basic information of "we're using custom gateway".
# Due to how clients are started up, this file has to exist.
gateway_registrations = '{{ entry_gateway.storage_paths.authenticator.gateway_registrations }}'
##### exit-gateway mode nym-node config options #####
[exit_gateway]
@@ -273,36 +246,6 @@ reply_surb_database = '{{ exit_gateway.storage_paths.ip_packet_router.reply_surb
# Due to how clients are started up, this file has to exist.
gateway_registrations = '{{ exit_gateway.storage_paths.ip_packet_router.gateway_registrations }}'
[exit_gateway.storage_paths.authenticator]
# Path to file containing authenticator ed25519 identity private key.
private_ed25519_identity_key_file = '{{ exit_gateway.storage_paths.authenticator.private_ed25519_identity_key_file }}'
# Path to file containing authenticator ed25519 identity public key.
public_ed25519_identity_key_file = '{{ exit_gateway.storage_paths.authenticator.public_ed25519_identity_key_file }}'
# Path to file containing authenticator x25519 diffie hellman private key.
private_x25519_diffie_hellman_key_file = '{{ exit_gateway.storage_paths.authenticator.private_x25519_diffie_hellman_key_file }}'
# Path to file containing authenticator x25519 diffie hellman public key.
public_x25519_diffie_hellman_key_file = '{{ exit_gateway.storage_paths.authenticator.public_x25519_diffie_hellman_key_file }}'
# 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.
ack_key_file = '{{ exit_gateway.storage_paths.authenticator.ack_key_file }}'
# Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
reply_surb_database = '{{ exit_gateway.storage_paths.authenticator.reply_surb_database }}'
# Normally this is a path to the file containing information about gateways used by this client,
# i.e. details such as their public keys, owner addresses or the network information.
# but in this case it just has the basic information of "we're using custom gateway".
# Due to how clients are started up, this file has to exist.
gateway_registrations = '{{ exit_gateway.storage_paths.authenticator.gateway_registrations }}'
[authenticator]
# currently empty (there are some debug options one might want to configure)
##### logging configuration options #####
[logging]
+9 -7
View File
@@ -1,6 +1,8 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use tracing::debug;
use crate::config::old_configs::*;
use crate::config::Config;
use crate::error::NymNodeError;
@@ -8,14 +10,14 @@ use std::path::Path;
// currently there are no upgrades
async fn try_upgrade_config(path: &Path) -> Result<(), NymNodeError> {
let cfg = try_upgrade_config_v1(path, None).await.ok();
match try_upgrade_config_v2(path, cfg).await {
Ok(cfg) => cfg.save(),
Err(e) => {
tracing::error!("Failed to finish upgrade - {e}");
Err(NymNodeError::FailedUpgrade)
}
if try_upgrade_config_1_1_2(path).await.is_ok() {
debug!("Updated from 1.1.2 or previous");
}
if try_upgrade_config_1_1_3(path).await.is_ok() {
debug!("Updated from 1.1.3");
}
Ok(())
}
pub async fn try_load_current_config<P: AsRef<Path>>(
-14
View File
@@ -143,13 +143,6 @@ pub enum NymNodeError {
#[error(transparent)]
ExitGatewayFailure(#[from] ExitGatewayError),
// TODO: more granular errors
#[error(transparent)]
ExternalClientCore(#[from] ClientCoreError),
#[error("failed upgrade")]
FailedUpgrade,
}
impl From<nym_mixnode::error::MixnodeError> for NymNodeError {
@@ -179,13 +172,6 @@ pub enum MixnodeError {
#[derive(Debug, Error)]
pub enum EntryGatewayError {
#[error(transparent)]
KeyFailure(#[from] KeyIOFailure),
// TODO: more granular errors
#[error(transparent)]
ExternalClientCore(#[from] ClientCoreError),
#[error("failed to load entry gateway account mnemonic from {}: {source}", path.display())]
MnemonicLoadFailure {
path: PathBuf,
+7 -70
View File
@@ -19,7 +19,6 @@ use nym_network_requester::{
use nym_node::config::entry_gateway::ephemeral_entry_gateway_config;
use nym_node::config::exit_gateway::ephemeral_exit_gateway_config;
use nym_node::config::mixnode::ephemeral_mixnode_config;
use nym_node::config::persistence::AuthenticatorPaths;
use nym_node::config::{
Config, EntryGatewayConfig, ExitGatewayConfig, MixnodeConfig, NodeMode, Wireguard,
};
@@ -203,7 +202,7 @@ impl ExitGatewayData {
config: &ExitGatewayConfig,
public_key: ed25519::PublicKey,
) -> Result<(), ExitGatewayError> {
// generate all the keys for NR, IPR and AUTH
// generate all the keys for NR and IPR
let mut rng = OsRng;
let gateway_details = GatewayDetails::Custom(CustomGatewayDetails::new(public_key)).into();
@@ -329,57 +328,6 @@ pub(crate) struct NymNode {
}
impl NymNode {
fn initialise_client_keys<R: RngCore + CryptoRng>(
rng: &mut R,
typ: &str,
ed25519_paths: nym_pemstore::KeyPairPath,
x25519_paths: nym_pemstore::KeyPairPath,
ack_key_path: &Path,
) -> Result<(), EntryGatewayError> {
let ed25519_keys = ed25519::KeyPair::new(rng);
let x25519_keys = x25519::KeyPair::new(rng);
let aes128ctr_key = AckKey::new(rng);
store_keypair(
&ed25519_keys,
ed25519_paths,
format!("{typ}-ed25519-identity"),
)?;
store_keypair(&x25519_keys, x25519_paths, format!("{typ}-x25519-dh"))?;
store_key(&aes128ctr_key, ack_key_path, format!("{typ}-ack-key"))?;
Ok(())
}
async fn initialise_client_gateway_storage(
storage_path: &Path,
registration: &GatewayRegistration,
) -> Result<(), EntryGatewayError> {
// insert all required information into the gateways store
// (I hate that we have to do it, but that's currently the simplest thing to do)
let storage = setup_fs_gateways_storage(storage_path).await?;
store_gateway_details(&storage, registration).await?;
set_active_gateway(&storage, &registration.gateway_id().to_base58_string()).await?;
Ok(())
}
pub async fn initialise_authenticator<R: RngCore + CryptoRng>(
rng: &mut R,
paths: &AuthenticatorPaths,
registration: &GatewayRegistration,
) -> Result<(), NymNodeError> {
trace!("initialising authenticator keys");
Self::initialise_client_keys(
rng,
"authenticator",
paths.ed25519_identity_storage_paths(),
paths.x25519_diffie_hellman_storage_paths(),
&paths.ack_key_file,
)?;
Self::initialise_client_gateway_storage(&paths.gateway_registrations, registration).await?;
Ok(())
}
pub(crate) async fn initialise(
config: &Config,
custom_mnemonic: Option<Zeroizing<bip39::Mnemonic>>,
@@ -426,17 +374,6 @@ impl NymNode {
ExitGatewayData::initialise(&config.exit_gateway, *ed25519_identity_keys.public_key())
.await?;
// authenticator initialization:
Self::initialise_authenticator(
&mut rng,
&config.entry_gateway.storage_paths.authenticator,
&GatewayDetails::Custom(CustomGatewayDetails::new(
*ed25519_identity_keys.public_key(),
))
.into(),
)
.await?;
// wireguard initialisation
WireguardData::initialise(&config.wireguard)?;
@@ -560,10 +497,10 @@ impl NymNode {
let config =
ephemeral_entry_gateway_config(self.config.clone(), &self.entry_gateway.mnemonic)?;
let mut entry_gateway = Gateway::new_loaded(
config.gateway,
config.nr_opts,
config.ipr_opts,
Some(config.auth_opts),
config,
None,
None,
None,
self.ed25519_identity_keys.clone(),
self.x25519_sphinx_keys.clone(),
self.entry_gateway.client_storage.clone(),
@@ -589,8 +526,8 @@ impl NymNode {
let mut exit_gateway = Gateway::new_loaded(
config.gateway,
config.nr_opts,
config.ipr_opts,
Some(config.nr_opts),
Some(config.ipr_opts),
Some(config.auth_opts),
self.ed25519_identity_keys.clone(),
self.x25519_sphinx_keys.clone(),
+1 -22
View File
@@ -656,25 +656,6 @@ dependencies = [
"strsim",
]
[[package]]
name = "clap_complete"
version = "4.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fc443334c81a804575546c5a8a79b4913b50e28d69232903604cada1de817ce"
dependencies = [
"clap",
]
[[package]]
name = "clap_complete_fig"
version = "4.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99fee1d30a51305a6c2ed3fc5709be3c8af626c9c958e04dd9ae94e27bcbce9f"
dependencies = [
"clap",
"clap_complete",
]
[[package]]
name = "clap_derive"
version = "4.4.7"
@@ -3128,9 +3109,6 @@ dependencies = [
name = "nym-bin-common"
version = "0.6.0"
dependencies = [
"clap",
"clap_complete",
"clap_complete_fig",
"const-str",
"log",
"pretty_env_logger",
@@ -3283,6 +3261,7 @@ version = "0.1.0"
dependencies = [
"async-trait",
"http 1.1.0",
"nym-bin-common",
"reqwest 0.12.4",
"serde",
"serde_json",
+4
View File
@@ -134,6 +134,10 @@ pub enum BackendError {
WalletValidatorConnectionFailed,
#[error("No defined default validator URL")]
WalletNoDefaultValidator,
#[error(
"this vesting operation has been disabled. please use the non-vesting variant instead."
)]
UnsupportedVestingOperation,
#[error(transparent)]
WalletError {
+18 -94
View File
@@ -12,7 +12,7 @@ use nym_mixnet_contract_common::{
construct_mixnode_bonding_sign_payload, Gateway, GatewayBondingPayload, MixNode,
MixNodeCostParams, SignableGatewayBondingMsg, SignableMixNodeBondingMsg,
};
use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, NymContractsProvider};
use nym_validator_client::nyxd::contract_traits::MixnetQueryClient;
use nym_validator_client::nyxd::error::NyxdError;
use nym_validator_client::nyxd::Coin;
use nym_validator_client::DirectSigningHttpRpcValidatorClient;
@@ -21,7 +21,6 @@ use nym_validator_client::DirectSigningHttpRpcValidatorClient;
#[async_trait]
pub(crate) trait AddressAndNonceProvider {
async fn get_signing_nonce(&self) -> Result<Nonce, NyxdError>;
fn vesting_contract_address(&self) -> Addr;
fn cw_address(&self) -> Addr;
}
@@ -31,30 +30,11 @@ impl AddressAndNonceProvider for DirectSigningHttpRpcValidatorClient {
self.nyxd.get_signing_nonce(&self.nyxd.address()).await
}
fn vesting_contract_address(&self) -> Addr {
// the call to unchecked is fine here as we're converting directly from `AccountId`
// which must have been a valid bech32 address
Addr::unchecked(
self.nyxd
.vesting_contract_address()
.expect("unknown vesting contract address")
.as_ref(),
)
}
fn cw_address(&self) -> Addr {
self.nyxd.cw_address()
}
}
fn proxy<P: AddressAndNonceProvider>(client: &P, vesting: bool) -> Option<Addr> {
if vesting {
Some(client.vesting_contract_address())
} else {
None
}
}
// since the message has to go back to the user due to the increasing nonce, we might as well sign the entire payload
pub(crate) async fn create_mixnode_bonding_sign_payload<P: AddressAndNonceProvider>(
client: &P,
@@ -63,14 +43,15 @@ pub(crate) async fn create_mixnode_bonding_sign_payload<P: AddressAndNonceProvid
pledge: Coin,
vesting: bool,
) -> Result<SignableMixNodeBondingMsg, BackendError> {
if vesting {
return Err(BackendError::UnsupportedVestingOperation);
}
let sender = client.cw_address();
let proxy = proxy(client, vesting);
let nonce = client.get_signing_nonce().await?;
Ok(construct_mixnode_bonding_sign_payload(
nonce,
sender,
proxy,
pledge.into(),
mix_node,
cost_params,
@@ -85,6 +66,9 @@ pub(crate) async fn verify_mixnode_bonding_sign_payload<P: AddressAndNonceProvid
vesting: bool,
msg_signature: &MessageSignature,
) -> Result<(), BackendError> {
if vesting {
return Err(BackendError::UnsupportedVestingOperation);
}
let identity_key = identity::PublicKey::from_base58_string(&mix_node.identity_key)?;
let signature = identity::Signature::from_bytes(msg_signature.as_ref())?;
@@ -118,10 +102,12 @@ pub(crate) async fn create_gateway_bonding_sign_payload<P: AddressAndNonceProvid
pledge: Coin,
vesting: bool,
) -> Result<SignableGatewayBondingMsg, BackendError> {
if vesting {
return Err(BackendError::UnsupportedVestingOperation);
}
let payload = GatewayBondingPayload::new(gateway);
let sender = client.cw_address();
let proxy = proxy(client, vesting);
let content = ContractMessageContent::new(sender, proxy, vec![pledge.into()], payload);
let content = ContractMessageContent::new(sender, vec![pledge.into()], payload);
let nonce = client.get_signing_nonce().await?;
Ok(SignableMessage::new(nonce, content))
@@ -134,6 +120,9 @@ pub(crate) async fn verify_gateway_bonding_sign_payload<P: AddressAndNonceProvid
vesting: bool,
msg_signature: &MessageSignature,
) -> Result<(), BackendError> {
if vesting {
return Err(BackendError::UnsupportedVestingOperation);
}
let identity_key = identity::PublicKey::from_base58_string(&gateway.identity_key)?;
let signature = identity::Signature::from_bytes(msg_signature.as_ref())?;
@@ -170,7 +159,6 @@ mod tests {
struct MockClient {
address: Addr,
vesting_contract: Addr,
signing_nonce: Nonce,
}
@@ -180,10 +168,6 @@ mod tests {
Ok(self.signing_nonce)
}
fn vesting_contract_address(&self) -> Addr {
self.vesting_contract.clone()
}
fn cw_address(&self) -> Addr {
self.address.clone()
}
@@ -211,7 +195,6 @@ mod tests {
let dummy_account = Addr::unchecked("n16t2umcd83zjpl5puyuuq6lgmy4p3qedjd8ynn6");
let dummy_client = MockClient {
address: dummy_account,
vesting_contract: Addr::unchecked("n17tj0a0w6v7r2dc54rnkzfza6s8hxs87rj273a5"),
signing_nonce: 42,
};
@@ -240,16 +223,8 @@ mod tests {
dummy_pledge.clone(),
true,
)
.await
.unwrap();
let plaintext_vesting = signing_msg_vesting.to_plaintext().unwrap();
let sig_vesting: MessageSignature = identity_keypair
.private_key()
.sign(&plaintext_vesting)
.to_bytes()
.as_ref()
.into();
.await;
assert!(signing_msg_vesting.is_err());
let res = verify_mixnode_bonding_sign_payload(
&dummy_client,
@@ -262,28 +237,6 @@ mod tests {
.await;
assert!(res.is_ok());
let res = verify_mixnode_bonding_sign_payload(
&dummy_client,
&dummy_mixnode,
&dummy_cost_params,
&dummy_pledge,
true,
&sig_vesting,
)
.await;
assert!(res.is_ok());
let res = verify_mixnode_bonding_sign_payload(
&dummy_client,
&dummy_mixnode,
&dummy_cost_params,
&dummy_pledge,
false,
&sig_vesting,
)
.await;
assert!(res.is_err());
let res = verify_mixnode_bonding_sign_payload(
&dummy_client,
&dummy_mixnode,
@@ -315,7 +268,6 @@ mod tests {
let dummy_account = Addr::unchecked("n16t2umcd83zjpl5puyuuq6lgmy4p3qedjd8ynn6");
let dummy_client = MockClient {
address: dummy_account,
vesting_contract: Addr::unchecked("n17tj0a0w6v7r2dc54rnkzfza6s8hxs87rj273a5"),
signing_nonce: 42,
};
@@ -342,16 +294,8 @@ mod tests {
dummy_pledge.clone(),
true,
)
.await
.unwrap();
let plaintext_vesting = signing_msg_vesting.to_plaintext().unwrap();
let sig_vesting: MessageSignature = identity_keypair
.private_key()
.sign(&plaintext_vesting)
.to_bytes()
.as_ref()
.into();
.await;
assert!(signing_msg_vesting.is_err());
let res = verify_gateway_bonding_sign_payload(
&dummy_client,
@@ -363,26 +307,6 @@ mod tests {
.await;
assert!(res.is_ok());
let res = verify_gateway_bonding_sign_payload(
&dummy_client,
&dummy_gateway,
&dummy_pledge,
true,
&sig_vesting,
)
.await;
assert!(res.is_ok());
let res = verify_gateway_bonding_sign_payload(
&dummy_client,
&dummy_gateway,
&dummy_pledge,
false,
&sig_vesting,
)
.await;
assert!(res.is_err());
let res = verify_gateway_bonding_sign_payload(
&dummy_client,
&dummy_gateway,
+8 -8
View File
@@ -615,11 +615,11 @@ brace-expansion@^2.0.1:
balanced-match "^1.0.0"
braces@^3.0.1, braces@~3.0.2:
version "3.0.3"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
dependencies:
fill-range "^7.1.1"
fill-range "^7.0.1"
browser-stdout@1.3.1:
version "1.3.1"
@@ -1107,10 +1107,10 @@ filelist@^1.0.1:
dependencies:
minimatch "^5.0.1"
fill-range@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
dependencies:
to-regex-range "^5.0.1"
+1 -1
View File
@@ -4,7 +4,7 @@ import Stack from '@mui/material/Stack';
const links = [
['Twitter', 'https://twitter.com/nymproject'],
['Telegram', 'https://t.me/nymchan'],
['Discord', 'https://discord.gg/nym'],
['Discord', 'https://discord.gg/nymproject'],
['GitHub', 'https://github.com/nymtech/nym'],
['Nym Wallet', 'https://nymtech.net/download/wallet'],
['Nym Explorer', 'https://explorer.nymtech.net/'],
+1 -1
View File
@@ -8,7 +8,7 @@ const config: DocsThemeConfig = {
link: 'https://github.com/nymtech/nym',
},
chat: {
link: 'https://discord.gg/nym',
link: 'https://discord.gg/nymproject',
},
docsRepositoryBase: 'https://github.com/nymtech/nym/tree/develop/sdk/typescript/docs',
footer: {
@@ -30,16 +30,15 @@
"@mui/styles": ">= 5",
"@mui/system": ">= 5",
"@nymproject/mui-theme": "1",
"zxcvbn": "^4.4.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"@nymproject/nym-validator-client": "^0.18.0",
"@nymproject/types": "1",
"base58": "4",
"bech32": "^1.1.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"zxcvbn": "^4.4.2"
"bech32": "^1.1.4"
},
"dependencies": {
"@mui/x-tree-view": "^7.10.0",
"flat": "^5.0.2",
"use-clipboard-copy": "^0.2.0"
},
@@ -1,7 +1,10 @@
/* eslint-disable react/no-array-index-key */
import * as React from 'react';
import type { Theme } from '@mui/material/styles';
import { SimpleTreeView, TreeItem } from '@mui/x-tree-view';
import * as React from 'react';
import TreeView from '@mui/lab/TreeView';
import TreeItem from '@mui/lab/TreeItem';
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
import ArrowRightIcon from '@mui/icons-material/ArrowRight';
import { Box } from '@mui/material';
const MUIThemeExplorerItem: FCWithChildren<{
@@ -18,7 +21,7 @@ const MUIThemeExplorerItem: FCWithChildren<{
if (typeof item === 'string') {
return (
<TreeItem
itemId={`${path}`}
nodeId={`${path}`}
label={
<Box sx={{ display: 'flex', alignItems: 'center', p: 0.5, pr: 0 }}>
<Box
@@ -45,7 +48,7 @@ const MUIThemeExplorerItem: FCWithChildren<{
if (Array.isArray(item)) {
return (
<TreeItem itemId={`${path}-array`} label={`${parentKey}[]`}>
<TreeItem nodeId={`${path}-array`} label={`${parentKey}[]`}>
{item.map((i, idx) => (
<MUIThemeExplorerItem
key={`${parentKey}-${idx}`}
@@ -62,7 +65,7 @@ const MUIThemeExplorerItem: FCWithChildren<{
if (typeof item === 'object' && Object.keys(item).length) {
return (
<TreeItem itemId={`${path}`} label={`${parentKey}`}>
<TreeItem nodeId={`${path}`} label={`${parentKey}`}>
{Object.keys(item).map((key) => (
<MUIThemeExplorerItem
key={`${parentKey}-${key}`}
@@ -82,11 +85,14 @@ const MUIThemeExplorerItem: FCWithChildren<{
export const MUIThemeExplorer: FCWithChildren<{
theme: Theme;
}> = ({ theme }) => (
<SimpleTreeView
defaultExpandedItems={['theme.palette', 'theme.palette.nym', 'theme.palette.nym.highlight']}
<TreeView
defaultExpanded={['theme.palette', 'theme.palette.nym', 'theme.palette.nym.highlight']}
defaultCollapseIcon={<ArrowDropDownIcon />}
defaultExpandIcon={<ArrowRightIcon />}
defaultEndIcon={<div style={{ width: 24 }} />}
sx={{ height: 500, flexGrow: 1, width: '100%', overflowY: 'auto' }}
>
<MUIThemeExplorerItem theme={theme} item={theme.palette} parentKey="palette" path="theme.palette" />
<TreeItem itemId="test">Test</TreeItem>
</SimpleTreeView>
<TreeItem nodeId="test">Test</TreeItem>
</TreeView>
);
@@ -6,7 +6,7 @@ use std::path::Path;
use futures::channel::oneshot;
use ipnetwork::IpNetwork;
use nym_client_core::{HardcodedTopologyProvider, TopologyProvider};
use nym_sdk::{mixnet::Recipient, GatewayTransceiver};
use nym_sdk::mixnet::Recipient;
use nym_task::{TaskClient, TaskHandle};
use nym_wireguard::WireguardGatewayData;
@@ -28,7 +28,6 @@ pub struct Authenticator {
config: Config,
wait_for_gateway: bool,
custom_topology_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
custom_gateway_transceiver: Option<Box<dyn GatewayTransceiver + Send + Sync>>,
wireguard_gateway_data: WireguardGatewayData,
shutdown: Option<TaskClient>,
on_start: Option<oneshot::Sender<OnStartData>>,
@@ -40,7 +39,6 @@ impl Authenticator {
config,
wait_for_gateway: false,
custom_topology_provider: None,
custom_gateway_transceiver: None,
wireguard_gateway_data,
shutdown: None,
on_start: None,
@@ -61,12 +59,6 @@ impl Authenticator {
self
}
#[must_use]
pub fn with_minimum_gateway_performance(mut self, minimum_gateway_performance: u8) -> Self {
self.config.base.debug.topology.minimum_gateway_performance = minimum_gateway_performance;
self
}
#[must_use]
#[allow(unused)]
pub fn with_on_start(mut self, on_start: oneshot::Sender<OnStartData>) -> Self {
@@ -74,16 +66,6 @@ impl Authenticator {
self
}
#[must_use]
#[allow(unused)]
pub fn with_custom_gateway_transceiver(
mut self,
gateway_transceiver: Box<dyn GatewayTransceiver + Send + Sync>,
) -> Self {
self.custom_gateway_transceiver = Some(gateway_transceiver);
self
}
#[must_use]
#[allow(unused)]
pub fn with_custom_topology_provider(
@@ -112,7 +94,7 @@ impl Authenticator {
let mixnet_client = crate::mixnet_client::create_mixnet_client(
&self.config.base,
task_handle.get_handle().named("nym_sdk::MixnetClient"),
self.custom_gateway_transceiver,
None,
self.custom_topology_provider,
self.wait_for_gateway,
&self.config.storage_paths.common_paths,
@@ -3,12 +3,17 @@
use nym_client_core::config::disk_persistence::CommonClientPaths;
use serde::{Deserialize, Serialize};
use std::path::Path;
use std::path::{Path, PathBuf};
pub const DEFAULT_DESCRIPTION_FILENAME: &str = "description.toml";
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone)]
pub struct AuthenticatorPaths {
#[serde(flatten)]
pub common_paths: CommonClientPaths,
/// Location of the file containing our description
pub authenticator_description: PathBuf,
}
impl AuthenticatorPaths {
@@ -17,6 +22,7 @@ impl AuthenticatorPaths {
Self {
common_paths: CommonClientPaths::new_base(base_dir),
authenticator_description: base_dir.join(DEFAULT_DESCRIPTION_FILENAME),
}
}
}
@@ -71,6 +71,9 @@ allowed_list_location = '{{ storage_paths.allowed_list_location }}'
# Location of the file containing our unknown.list
unknown_list_location = '{{ storage_paths.unknown_list_location }}'
# Path to file containing description of this authenticator.
authenticator_description = '{{ storage_paths.authenticator_description }}'
##### logging configuration options #####
@@ -34,7 +34,10 @@ pub(crate) async fn execute(
}
nym_cli_commands::validator::mixnet::delegators::MixnetDelegatorsCommands::List(args) => {
nym_cli_commands::validator::mixnet::delegators::query_for_delegations::execute(args, create_signing_client_with_nym_api(global_args, network_details)?).await
}
},
nym_cli_commands::validator::mixnet::delegators::MixnetDelegatorsCommands::MigrateVestedDelegation(args) => {
nym_cli_commands::validator::mixnet::delegators::migrate_vested_delegation::migrate_vested_delegation(args, create_signing_client(global_args, network_details)?).await
},
}
Ok(())
}
@@ -48,7 +48,10 @@ pub(crate) async fn execute(
nym_cli_commands::validator::mixnet::operators::mixnode::MixnetOperatorsMixnodeCommands::DecreasePledgeVesting(args) => {
nym_cli_commands::validator::mixnet::operators::mixnode::vesting_decrease_pledge::vesting_decrease_pledge(args, create_signing_client(global_args, network_details)?).await
}
_ => unreachable!(),
nym_cli_commands::validator::mixnet::operators::mixnode::MixnetOperatorsMixnodeCommands::MigrateVestedNode(args) => {
nym_cli_commands::validator::mixnet::operators::mixnode::migrate_vested_mixnode::migrate_vested_mixnode(args, create_signing_client(global_args, network_details)?).await
}
_ => unreachable!()
}
Ok(())
}

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