diff --git a/Cargo.lock b/Cargo.lock index 77221ef262..2e74311fa1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -300,6 +300,16 @@ dependencies = [ "nom", ] +[[package]] +name = "assert-json-diff" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "async-channel" version = "1.9.0" @@ -396,6 +406,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "auto-future" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c1e7e457ea78e524f48639f551fd79703ac3f2237f5ecccdf4708f8a75ad373" + [[package]] name = "autocfg" version = "1.3.0" @@ -525,6 +541,35 @@ dependencies = [ "tracing", ] +[[package]] +name = "axum-test" +version = "16.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3254184de359bbae2a8ca10b050870a7a4f1c8332a2c27d53f360b9835bb3911" +dependencies = [ + "anyhow", + "assert-json-diff", + "auto-future", + "axum 0.7.7", + "bytes", + "cookie", + "http 1.1.0", + "http-body-util", + "hyper 1.4.1", + "hyper-util", + "mime", + "pretty_assertions", + "reserve-port", + "rust-multipart-rfc7578_2", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "tokio", + "tower 0.5.1", + "url", +] + [[package]] name = "backtrace" version = "0.3.73" @@ -2016,6 +2061,12 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "digest" version = "0.8.1" @@ -4256,6 +4307,7 @@ dependencies = [ "async-trait", "axum 0.7.7", "axum-extra", + "axum-test", "bincode", "bip39", "bloomfilter", @@ -7066,6 +7118,16 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "pretty_assertions" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" +dependencies = [ + "diff", + "yansi", +] + [[package]] name = "pretty_env_logger" version = "0.4.0" @@ -7572,6 +7634,16 @@ dependencies = [ "winreg 0.52.0", ] +[[package]] +name = "reserve-port" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9838134a2bfaa8e1f40738fcc972ac799de6e0e06b5157acb95fc2b05a0ea283" +dependencies = [ + "lazy_static", + "thiserror", +] + [[package]] name = "rfc6979" version = "0.4.0" @@ -7782,6 +7854,22 @@ dependencies = [ "walkdir", ] +[[package]] +name = "rust-multipart-rfc7578_2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b748410c0afdef2ebbe3685a6a862e2ee937127cdaae623336a459451c8d57" +dependencies = [ + "bytes", + "futures-core", + "futures-util", + "http 0.2.12", + "mime", + "mime_guess", + "rand", + "thiserror", +] + [[package]] name = "rustc-demangle" version = "0.1.24" diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/mixnet_query_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/mixnet_query_client.rs index ba90467f32..21529ad086 100644 --- a/common/client-libs/validator-client/src/nyxd/contract_traits/mixnet_query_client.rs +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/mixnet_query_client.rs @@ -732,7 +732,6 @@ where mod tests { use super::*; use crate::nyxd::contract_traits::tests::IgnoreValue; - use nym_mixnet_contract_common::QueryMsg; // it's enough that this compiles and clippy is happy about it #[allow(dead_code)] diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/mixnet_signing_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/mixnet_signing_client.rs index 5a7b8fae26..b253c3dc94 100644 --- a/common/client-libs/validator-client/src/nyxd/contract_traits/mixnet_signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/mixnet_signing_client.rs @@ -845,13 +845,6 @@ mod tests { client.update_nymnode_config(update, None).ignore() } - ExecuteMsg::TestingUncheckedBondLegacyMixnode { .. } => { - todo!("purposely not implemented") - } - ExecuteMsg::TestingUncheckedBondLegacyGateway { .. } => { - todo!("purposely not implemented") - } - #[cfg(feature = "contract-testing")] MixnetExecuteMsg::TestingResolveAllPendingEvents { .. } => { client.testing_resolve_all_pending_events(None).ignore() diff --git a/common/commands/src/validator/mixnet/operators/mixnode/settings/update_cost_params.rs b/common/commands/src/validator/mixnet/operators/mixnode/settings/update_cost_params.rs index bd0817ab45..cd145737ca 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/settings/update_cost_params.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/settings/update_cost_params.rs @@ -2,13 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 use crate::context::SigningClient; -use anyhow::anyhow; +use crate::validator::mixnet::operators::nymnode; use clap::Parser; -use cosmwasm_std::Uint128; -use log::info; -use nym_mixnet_contract_common::{NodeCostParams, Percent}; -use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient}; -use nym_validator_client::nyxd::CosmWasmCoin; #[derive(Debug, Parser)] pub struct Args { @@ -26,48 +21,13 @@ pub struct Args { } pub async fn update_cost_params(args: Args, client: SigningClient) -> anyhow::Result<()> { - let denom = client.current_chain_details().mix_denom.base.as_str(); - - let current_parameters = if let Some(client_mixnode) = client - .get_owned_mixnode(&client.address()) - .await? - .mixnode_details - { - client_mixnode.rewarding_details.cost_params - } else { - client - .get_owned_nymnode(&client.address()) - .await? - .details - .ok_or_else(|| anyhow!("the client does not own any nodes"))? - .rewarding_details - .cost_params - }; - - let profit_margin_percent = args - .profit_margin_percent - .map(|pm| Percent::from_percentage_value(pm as u64)) - .unwrap_or(Ok(current_parameters.profit_margin_percent))?; - - let interval_operating_cost = args - .interval_operating_cost - .map(|oc| CosmWasmCoin { - denom: denom.into(), - amount: Uint128::new(oc), - }) - .unwrap_or(current_parameters.interval_operating_cost); - - let cost_params = NodeCostParams { - profit_margin_percent, - interval_operating_cost, - }; - - info!("Starting cost params updating using {cost_params:?} !"); - let res = client - .update_cost_params(cost_params, None) - .await - .expect("failed to update cost params"); - - info!("Cost params result: {:?}", res); - Ok(()) + // the below can handle both, nymnode and legacy mixnode + nymnode::settings::update_cost_params::update_cost_params( + nymnode::settings::update_cost_params::Args { + profit_margin_percent: args.profit_margin_percent, + interval_operating_cost: args.interval_operating_cost, + }, + client, + ) + .await } diff --git a/common/commands/src/validator/mixnet/operators/nymnode/settings/update_cost_params.rs b/common/commands/src/validator/mixnet/operators/nymnode/settings/update_cost_params.rs index b7168f243a..5668c92e0d 100644 --- a/common/commands/src/validator/mixnet/operators/nymnode/settings/update_cost_params.rs +++ b/common/commands/src/validator/mixnet/operators/nymnode/settings/update_cost_params.rs @@ -6,9 +6,7 @@ use anyhow::anyhow; use clap::Parser; use cosmwasm_std::Uint128; use log::info; -use nym_mixnet_contract_common::{ - NodeCostParams, Percent, DEFAULT_INTERVAL_OPERATING_COST_AMOUNT, DEFAULT_PROFIT_MARGIN_PERCENT, -}; +use nym_mixnet_contract_common::{NodeCostParams, Percent}; use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient}; use nym_validator_client::nyxd::CosmWasmCoin; @@ -18,7 +16,7 @@ pub struct Args { long, help = "input your profit margin as follows; (so it would be 20, rather than 0.2)" )] - pub profit_margin_percent: Option, + pub profit_margin_percent: Option, #[clap( long, @@ -30,37 +28,41 @@ pub struct Args { pub async fn update_cost_params(args: Args, client: SigningClient) -> anyhow::Result<()> { let denom = client.current_chain_details().mix_denom.base.as_str(); - let default_profit_margin = - Percent::from_percentage_value(DEFAULT_PROFIT_MARGIN_PERCENT).unwrap(); - - let node_details = client - .get_owned_nymnode(&client.address()) + let current_parameters = if let Some(client_mixnode) = client + .get_owned_mixnode(&client.address()) .await? - .details - .ok_or_else(|| anyhow!("the client does not own any nodes"))?; - let current_parameters = node_details.rewarding_details.cost_params; - - let profit_margin_percent = current_parameters - .map(|rd| rd.cost_params.profit_margin_percent) - .unwrap_or(default_profit_margin); - - let profit_margin_value = args - .profit_margin_percent - .map(|pm| Percent::from_percentage_value(pm as u64)) - .unwrap_or(profit_margin_percent)?; - - let cost_params = NodeCostParams { - profit_margin_percent: profit_margin_value, - interval_operating_cost: CosmWasmCoin { - denom: denom.into(), - amount: Uint128::new( - args.interval_operating_cost - .unwrap_or(DEFAULT_INTERVAL_OPERATING_COST_AMOUNT), - ), - }, + .mixnode_details + { + client_mixnode.rewarding_details.cost_params + } else { + client + .get_owned_nymnode(&client.address()) + .await? + .details + .ok_or_else(|| anyhow!("the client does not own any nodes"))? + .rewarding_details + .cost_params }; - info!("Starting nym node params updating!"); + let profit_margin_percent = args + .profit_margin_percent + .map(|pm| Percent::from_percentage_value(pm as u64)) + .unwrap_or(Ok(current_parameters.profit_margin_percent))?; + + let interval_operating_cost = args + .interval_operating_cost + .map(|oc| CosmWasmCoin { + denom: denom.into(), + amount: Uint128::new(oc), + }) + .unwrap_or(current_parameters.interval_operating_cost); + + let cost_params = NodeCostParams { + profit_margin_percent, + interval_operating_cost, + }; + + info!("Starting cost params updating using {cost_params:?} !"); let res = client .update_cost_params(cost_params, None) .await diff --git a/common/cosmwasm-smart-contracts/contracts-common/src/types.rs b/common/cosmwasm-smart-contracts/contracts-common/src/types.rs index 30a01c17a9..96d329e753 100644 --- a/common/cosmwasm-smart-contracts/contracts-common/src/types.rs +++ b/common/cosmwasm-smart-contracts/contracts-common/src/types.rs @@ -272,7 +272,6 @@ impl ContractBuildInformation { #[cfg(test)] mod tests { use super::*; - use cosmwasm_std::Fraction; #[test] fn percent_serde() { diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/interval.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/interval.rs index 2a1c885f1c..e36935d66c 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/interval.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/interval.rs @@ -369,7 +369,7 @@ impl Interval { } pub fn ensure_current_epoch_is_over(&self, env: &Env) -> Result<(), MixnetContractError> { - if !self.is_current_epoch_over(&env) { + if !self.is_current_epoch_over(env) { return Err(MixnetContractError::EpochInProgress { current_block_time: env.block.time.seconds(), epoch_start: self.current_epoch_start_unix_timestamp(), diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/nym_node.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/nym_node.rs index 21286924c1..375fd34374 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/nym_node.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/nym_node.rs @@ -287,7 +287,6 @@ impl NymNodeDetails { } } -/// #[cw_serde] pub struct NymNodeBond { /// Unique id assigned to the bonded node. diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/rewarding/helpers.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/rewarding/helpers.rs index d9c0bb5ea9..4d9ca75459 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/rewarding/helpers.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/rewarding/helpers.rs @@ -18,11 +18,3 @@ pub fn truncate_reward(reward: Decimal, denom: impl Into) -> Coin { pub fn truncate_reward_amount(reward: Decimal) -> Uint128 { truncate_decimal(reward) } - -pub fn legacy_standby_work_factor() -> Decimal { - todo!() -} - -pub fn legacy_active_work_factor() -> Decimal { - todo!() -} diff --git a/common/nymsphinx/src/receiver.rs b/common/nymsphinx/src/receiver.rs index 9799d774fd..8def09db67 100644 --- a/common/nymsphinx/src/receiver.rs +++ b/common/nymsphinx/src/receiver.rs @@ -235,7 +235,6 @@ mod message_receiver { 1, vec![mix::LegacyNode { mix_id: 123, - owner: None, host: "10.20.30.40".parse().unwrap(), mix_host: "10.20.30.40:1789".parse().unwrap(), identity_key: identity::PublicKey::from_base58_string( @@ -255,7 +254,6 @@ mod message_receiver { 2, vec![mix::LegacyNode { mix_id: 234, - owner: None, host: "11.21.31.41".parse().unwrap(), mix_host: "11.21.31.41:1789".parse().unwrap(), identity_key: identity::PublicKey::from_base58_string( @@ -275,7 +273,6 @@ mod message_receiver { 3, vec![mix::LegacyNode { mix_id: 456, - owner: None, host: "12.22.32.42".parse().unwrap(), mix_host: "12.22.32.42:1789".parse().unwrap(), identity_key: identity::PublicKey::from_base58_string( @@ -295,7 +292,7 @@ mod message_receiver { // currently coco_nodes don't really exist so this is still to be determined mixes, vec![gateway::LegacyNode { - owner: None, + node_id: 789, host: "1.2.3.4".parse().unwrap(), mix_host: "1.2.3.4:1789".parse().unwrap(), clients_ws_port: 9000, diff --git a/common/types/src/pending_events.rs b/common/types/src/pending_events.rs index 15866f6cad..9f98af61a3 100644 --- a/common/types/src/pending_events.rs +++ b/common/types/src/pending_events.rs @@ -4,12 +4,13 @@ use crate::currency::{DecCoin, RegisteredCoins}; use crate::error::TypesError; use crate::mixnode::NodeCostParams; +use nym_mixnet_contract_common::reward_params::ActiveSetUpdate; use nym_mixnet_contract_common::{ BlockHeight, EpochEventId, IntervalEventId, IntervalRewardingParamsUpdate, NodeId, PendingEpochEvent as MixnetContractPendingEpochEvent, PendingEpochEventKind as MixnetContractPendingEpochEventKind, PendingIntervalEvent as MixnetContractPendingIntervalEvent, - PendingIntervalEventKind as MixnetContractPendingIntervalEventKind, + PendingIntervalEventKind as MixnetContractPendingIntervalEventKind, PendingIntervalEventKind, }; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -22,7 +23,7 @@ use serde::{Deserialize, Serialize}; export_to = "ts-packages/types/src/types/rust/PendingEpochEvent.ts" ) )] -#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, JsonSchema)] +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, JsonSchema)] pub struct PendingEpochEvent { pub id: EpochEventId, pub created_at: BlockHeight, @@ -50,7 +51,7 @@ impl PendingEpochEvent { export_to = "ts-packages/types/src/types/rust/PendingEpochEventData.ts" ) )] -#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, JsonSchema)] +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, JsonSchema)] pub enum PendingEpochEventData { Delegate { owner: String, @@ -77,6 +78,20 @@ pub enum PendingEpochEventData { UpdateActiveSetSize { new_size: u32, }, + NymNodePledgeMore { + node_id: NodeId, + amount: DecCoin, + }, + NymNodeDecreasePledge { + node_id: NodeId, + decrease_by: DecCoin, + }, + UnbondNymNode { + node_id: NodeId, + }, + UpdateActiveSet { + update: ActiveSetUpdate, + }, } impl PendingEpochEventData { @@ -123,7 +138,25 @@ impl PendingEpochEventData { MixnetContractPendingEpochEventKind::UnbondMixnode { mix_id } => { Ok(PendingEpochEventData::UnbondMixnode { mix_id }) } - _ => todo!(), + MixnetContractPendingEpochEventKind::NymNodePledgeMore { node_id, amount } => { + Ok(PendingEpochEventData::NymNodePledgeMore { + node_id, + amount: reg.attempt_convert_to_display_dec_coin(amount.into())?, + }) + } + MixnetContractPendingEpochEventKind::NymNodeDecreasePledge { + node_id, + decrease_by, + } => Ok(PendingEpochEventData::NymNodeDecreasePledge { + node_id, + decrease_by: reg.attempt_convert_to_display_dec_coin(decrease_by.into())?, + }), + MixnetContractPendingEpochEventKind::UnbondNymNode { node_id } => { + Ok(PendingEpochEventData::UnbondNymNode { node_id }) + } + MixnetContractPendingEpochEventKind::UpdateActiveSet { update } => { + Ok(PendingEpochEventData::UpdateActiveSet { update }) + } } } } @@ -173,7 +206,10 @@ pub enum PendingIntervalEventData { mix_id: NodeId, new_costs: NodeCostParams, }, - + ChangeNymNodeCostParams { + node_id: NodeId, + new_costs: NodeCostParams, + }, UpdateRewardingParams { update: IntervalRewardingParamsUpdate, }, @@ -207,7 +243,14 @@ impl PendingIntervalEventData { epochs_in_interval, epoch_duration_secs, }), - _ => todo!(), + PendingIntervalEventKind::ChangeNymNodeCostParams { node_id, new_costs } => { + Ok(PendingIntervalEventData::ChangeNymNodeCostParams { + node_id, + new_costs: NodeCostParams::from_mixnet_contract_mixnode_cost_params( + new_costs, reg, + )?, + }) + } } } } diff --git a/contracts/mixnet-vesting-integration-tests/src/support/fixtures.rs b/contracts/mixnet-vesting-integration-tests/src/support/fixtures.rs index 9910720bb7..ac03d341ea 100644 --- a/contracts/mixnet-vesting-integration-tests/src/support/fixtures.rs +++ b/contracts/mixnet-vesting-integration-tests/src/support/fixtures.rs @@ -4,6 +4,7 @@ use crate::support::setup::{MIX_DENOM, REWARDING_VALIDATOR}; use cosmwasm_std::Decimal; use nym_contracts_common::Percent; +use nym_mixnet_contract_common::reward_params::RewardedSetParams; use nym_mixnet_contract_common::InitialRewardingParams; use std::time::Duration; @@ -21,8 +22,12 @@ pub fn default_mixnet_init_msg() -> nym_mixnet_contract_common::InstantiateMsg { sybil_resistance: Percent::from_percentage_value(30).unwrap(), active_set_work_factor: Decimal::from_atomics(10u32, 0).unwrap(), interval_pool_emission: Percent::from_percentage_value(2).unwrap(), - rewarded_set_size: 240, - active_set_size: 100, + rewarded_set_params: RewardedSetParams { + entry_gateways: 70, + exit_gateways: 50, + mixnodes: 120, + standby: 0, + }, }, profit_margin: Default::default(), interval_operating_cost: Default::default(), diff --git a/contracts/mixnet-vesting-integration-tests/src/support/setup.rs b/contracts/mixnet-vesting-integration-tests/src/support/setup.rs index e2fc5deb38..e360777a94 100644 --- a/contracts/mixnet-vesting-integration-tests/src/support/setup.rs +++ b/contracts/mixnet-vesting-integration-tests/src/support/setup.rs @@ -5,14 +5,15 @@ use crate::support::fixtures; use crate::support::helpers::{ mixnet_contract_wrapper, rewarding_validator, test_rng, vesting_contract_wrapper, }; -use cosmwasm_std::{coins, Addr, Coin, Timestamp}; +use cosmwasm_std::{coins, Addr, Coin, Decimal, Timestamp}; use cw_multi_test::{App, AppBuilder, Executor}; use nym_contracts_common::signing::{ContractMessageContent, MessageSignature, Nonce}; use nym_crypto::asymmetric::identity; -use nym_mixnet_contract_common::reward_params::Performance; +use nym_mixnet_contract_common::nym_node::{EpochAssignmentResponse, Role, RolesMetadataResponse}; +use nym_mixnet_contract_common::reward_params::{NodeRewardingParameters, Performance}; use nym_mixnet_contract_common::{ - CurrentIntervalResponse, LayerAssignment, MixnodeBondingPayload, NodeCostParams, - PagedRewardedSetResponse, RewardingParams, SignableMixNodeBondingMsg, + CurrentIntervalResponse, MixnodeBondingPayload, NodeCostParams, RewardedSet, RewardingParams, + RoleAssignment, SignableMixNodeBondingMsg, }; use nym_mixnet_contract_common::{ ExecuteMsg as MixnetExecuteMsg, MixNode, QueryMsg as MixnetQueryMsg, @@ -118,18 +119,95 @@ impl TestSetup { }) } - pub fn full_mixnet_epoch_operations(&mut self) { - let current_rewarded_set: PagedRewardedSetResponse = self + fn get_rewarded_set(&self) -> RewardedSet { + let metadata: RolesMetadataResponse = self .app .wrap() .query_wasm_smart( self.mixnet_contract(), - &MixnetQueryMsg::GetRewardedSet { - limit: Some(9999), - start_after: None, + &MixnetQueryMsg::GetRewardedSetMetadata {}, + ) + .unwrap(); + + let entry: EpochAssignmentResponse = self + .app + .wrap() + .query_wasm_smart( + self.mixnet_contract(), + &MixnetQueryMsg::GetRoleAssignment { + role: Role::EntryGateway, }, ) .unwrap(); + assert_eq!(entry.epoch_id, metadata.metadata.epoch_id); + + let exit: EpochAssignmentResponse = self + .app + .wrap() + .query_wasm_smart( + self.mixnet_contract(), + &MixnetQueryMsg::GetRoleAssignment { + role: Role::ExitGateway, + }, + ) + .unwrap(); + assert_eq!(exit.epoch_id, metadata.metadata.epoch_id); + + let layer1: EpochAssignmentResponse = self + .app + .wrap() + .query_wasm_smart( + self.mixnet_contract(), + &MixnetQueryMsg::GetRoleAssignment { role: Role::Layer1 }, + ) + .unwrap(); + assert_eq!(layer1.epoch_id, metadata.metadata.epoch_id); + + let layer2: EpochAssignmentResponse = self + .app + .wrap() + .query_wasm_smart( + self.mixnet_contract(), + &MixnetQueryMsg::GetRoleAssignment { role: Role::Layer2 }, + ) + .unwrap(); + assert_eq!(layer2.epoch_id, metadata.metadata.epoch_id); + + let layer3: EpochAssignmentResponse = self + .app + .wrap() + .query_wasm_smart( + self.mixnet_contract(), + &MixnetQueryMsg::GetRoleAssignment { role: Role::Layer3 }, + ) + .unwrap(); + assert_eq!(layer3.epoch_id, metadata.metadata.epoch_id); + + let standby: EpochAssignmentResponse = self + .app + .wrap() + .query_wasm_smart( + self.mixnet_contract(), + &MixnetQueryMsg::GetRoleAssignment { + role: Role::Standby, + }, + ) + .unwrap(); + assert_eq!(standby.epoch_id, metadata.metadata.epoch_id); + + RewardedSet { + entry_gateways: entry.nodes, + exit_gateways: exit.nodes, + layer1: layer1.nodes, + layer2: layer2.nodes, + layer3: layer3.nodes, + standby: standby.nodes, + } + } + + pub fn full_mixnet_epoch_operations(&mut self) { + let rewarded_set = self.get_rewarded_set(); + let current_params: RewardingParams = self .app .wrap() @@ -150,16 +228,30 @@ impl TestSetup { ) .unwrap(); + let work = + Decimal::one() / Decimal::from_ratio(rewarded_set.rewarded_set_size() as u64, 1u64); + let params = NodeRewardingParameters::new(Performance::hundred(), work); + + let mut nodes = rewarded_set + .layer1 + .iter() + .chain(rewarded_set.layer2.iter()) + .chain(rewarded_set.layer3.iter()) + .chain(rewarded_set.entry_gateways.iter()) + .chain(rewarded_set.exit_gateways.iter()) + .chain(rewarded_set.standby.iter()) + .copied() + .collect::>(); + + nodes.sort(); + // reward - for (mix_id, _status) in ¤t_rewarded_set.nodes { + for (node_id) in nodes { self.app .execute_contract( rewarding_validator(), self.mixnet_contract(), - &MixnetExecuteMsg::RewardNode { - node_id: *mix_id, - performance: Performance::hundred(), - }, + &MixnetExecuteMsg::RewardNode { node_id, params }, &[], ) .unwrap(); @@ -176,22 +268,86 @@ impl TestSetup { .unwrap(); // don't bother changing the active set, use the same node for update and advance - let new_rewarded_set = current_rewarded_set - .nodes - .into_iter() - .enumerate() - .map(|(i, (node, _))| { - LayerAssignment::new(node, ((i as u8 % 3) + 1).try_into().unwrap()) - }) - .collect(); self.app .execute_contract( rewarding_validator(), self.mixnet_contract(), - &MixnetExecuteMsg::AdvanceCurrentEpoch { - new_rewarded_set, - expected_active_set_size: current_params.active_set_size, + &MixnetExecuteMsg::AssignRoles { + assignment: RoleAssignment { + role: Role::EntryGateway, + nodes: rewarded_set.entry_gateways, + }, + }, + &[], + ) + .unwrap(); + + self.app + .execute_contract( + rewarding_validator(), + self.mixnet_contract(), + &MixnetExecuteMsg::AssignRoles { + assignment: RoleAssignment { + role: Role::ExitGateway, + nodes: rewarded_set.exit_gateways, + }, + }, + &[], + ) + .unwrap(); + + self.app + .execute_contract( + rewarding_validator(), + self.mixnet_contract(), + &MixnetExecuteMsg::AssignRoles { + assignment: RoleAssignment { + role: Role::Layer1, + nodes: rewarded_set.layer1, + }, + }, + &[], + ) + .unwrap(); + + self.app + .execute_contract( + rewarding_validator(), + self.mixnet_contract(), + &MixnetExecuteMsg::AssignRoles { + assignment: RoleAssignment { + role: Role::Layer2, + nodes: rewarded_set.layer2, + }, + }, + &[], + ) + .unwrap(); + + self.app + .execute_contract( + rewarding_validator(), + self.mixnet_contract(), + &MixnetExecuteMsg::AssignRoles { + assignment: RoleAssignment { + role: Role::Layer3, + nodes: rewarded_set.layer3, + }, + }, + &[], + ) + .unwrap(); + + self.app + .execute_contract( + rewarding_validator(), + self.mixnet_contract(), + &MixnetExecuteMsg::AssignRoles { + assignment: RoleAssignment { + role: Role::Standby, + nodes: rewarded_set.standby, + }, }, &[], ) diff --git a/nym-api/Cargo.toml b/nym-api/Cargo.toml index 48521ecf46..19207fafe5 100644 --- a/nym-api/Cargo.toml +++ b/nym-api/Cargo.toml @@ -134,6 +134,7 @@ sqlx = { workspace = true, features = [ ] } [dev-dependencies] +axum-test = "16.2.0" tempfile = { workspace = true } cw3 = { workspace = true } cw-utils = { workspace = true } diff --git a/nym-api/nym-api-requests/src/ecash/models.rs b/nym-api/nym-api-requests/src/ecash/models.rs index 64c1a2dccb..35d6df7a07 100644 --- a/nym-api/nym-api-requests/src/ecash/models.rs +++ b/nym-api/nym-api-requests/src/ecash/models.rs @@ -383,7 +383,7 @@ pub struct EpochCredentialsResponse { #[derive(Clone, Serialize, Deserialize, Debug, JsonSchema, ToSchema)] #[serde(rename_all = "camelCase")] pub struct IssuedCredentialsResponse { - // note: BTreeMap returns ordered results so it's fine to use it with pagination + // note: BTreeMap returns ordered results, so it's fine to use it with pagination pub credentials: BTreeMap, } diff --git a/nym-api/src/ecash/tests/issued_credentials.rs b/nym-api/src/ecash/tests/issued_credentials.rs index 5a86d70923..877e974d05 100644 --- a/nym-api/src/ecash/tests/issued_credentials.rs +++ b/nym-api/src/ecash/tests/issued_credentials.rs @@ -2,12 +2,12 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::ecash::tests::{voucher_fixture, TestFixture}; +use axum::http::StatusCode; use nym_api_requests::ecash::models::{ EpochCredentialsResponse, IssuedCredentialResponse, IssuedCredentialsResponse, Pagination, }; use nym_api_requests::ecash::CredentialsRequestBody; use nym_validator_client::nym_api::routes::{API_VERSION, ECASH_ROUTES}; -use rocket::http::Status; use std::collections::BTreeMap; #[tokio::test] @@ -19,11 +19,10 @@ async fn epoch_credentials() { let test_fixture = TestFixture::new().await; // initially we expect 0 issued - let response = test_fixture.rocket.get(&route_epoch1).dispatch().await; + let response = test_fixture.axum.get(&route_epoch1).await; - assert_eq!(response.status(), Status::Ok); - let parsed_response: EpochCredentialsResponse = - serde_json::from_str(&response.into_string().await.unwrap()).unwrap(); + assert_eq!(response.status_code(), StatusCode::OK); + let parsed_response: EpochCredentialsResponse = response.json(); assert_eq!(parsed_response.epoch_id, 1); assert_eq!(parsed_response.total_issued, 0); @@ -33,10 +32,9 @@ async fn epoch_credentials() { test_fixture.issue_dummy_credential().await; // now there should be one - let response = test_fixture.rocket.get(&route_epoch1).dispatch().await; - assert_eq!(response.status(), Status::Ok); - let parsed_response: EpochCredentialsResponse = - serde_json::from_str(&response.into_string().await.unwrap()).unwrap(); + let response = test_fixture.axum.get(&route_epoch1).await; + assert_eq!(response.status_code(), StatusCode::OK); + let parsed_response: EpochCredentialsResponse = response.json(); assert_eq!(parsed_response.epoch_id, 1); assert_eq!(parsed_response.total_issued, 1); @@ -45,11 +43,10 @@ async fn epoch_credentials() { // and another test_fixture.issue_dummy_credential().await; - let response = test_fixture.rocket.get(&route_epoch1).dispatch().await; + let response = test_fixture.axum.get(&route_epoch1).await; - assert_eq!(response.status(), Status::Ok); - let parsed_response: EpochCredentialsResponse = - serde_json::from_str(&response.into_string().await.unwrap()).unwrap(); + assert_eq!(response.status_code(), StatusCode::OK); + let parsed_response: EpochCredentialsResponse = response.json(); // note that first epoch credential didn't change assert_eq!(parsed_response.epoch_id, 1); @@ -58,10 +55,9 @@ async fn epoch_credentials() { test_fixture.set_epoch(2); - let response = test_fixture.rocket.get(&route_epoch2).dispatch().await; - assert_eq!(response.status(), Status::Ok); - let parsed_response: EpochCredentialsResponse = - serde_json::from_str(&response.into_string().await.unwrap()).unwrap(); + let response = test_fixture.axum.get(&route_epoch2).await; + assert_eq!(response.status_code(), StatusCode::OK); + let parsed_response: EpochCredentialsResponse = response.json(); // note the epoch change assert_eq!(parsed_response.epoch_id, 2); @@ -70,10 +66,9 @@ async fn epoch_credentials() { test_fixture.issue_dummy_credential().await; - let response = test_fixture.rocket.get(&route_epoch2).dispatch().await; - assert_eq!(response.status(), Status::Ok); - let parsed_response: EpochCredentialsResponse = - serde_json::from_str(&response.into_string().await.unwrap()).unwrap(); + let response = test_fixture.axum.get(&route_epoch2).await; + assert_eq!(response.status_code(), StatusCode::OK); + let parsed_response: EpochCredentialsResponse = response.json(); // note the epoch change assert_eq!(parsed_response.epoch_id, 2); @@ -81,10 +76,9 @@ async fn epoch_credentials() { assert_eq!(parsed_response.first_epoch_credential_id, Some(3)); // random epoch in the future - let response = test_fixture.rocket.get(&route_epoch42).dispatch().await; - assert_eq!(response.status(), Status::Ok); - let parsed_response: EpochCredentialsResponse = - serde_json::from_str(&response.into_string().await.unwrap()).unwrap(); + let response = test_fixture.axum.get(&route_epoch42).await; + assert_eq!(response.status_code(), StatusCode::OK); + let parsed_response: EpochCredentialsResponse = response.json(); assert_eq!(parsed_response.epoch_id, 42); assert_eq!(parsed_response.total_issued, 0); assert_eq!(parsed_response.first_epoch_credential_id, None); @@ -114,10 +108,9 @@ async fn issued_credential() { test_fixture.add_deposit(&voucher2); // random credential that was never issued - let response = test_fixture.rocket.get(route(42)).dispatch().await; - assert_eq!(response.status(), Status::Ok); - let parsed_response: IssuedCredentialResponse = - serde_json::from_str(&response.into_string().await.unwrap()).unwrap(); + let response = test_fixture.axum.get(&route(42)).await; + assert_eq!(response.status_code(), StatusCode::OK); + let parsed_response: IssuedCredentialResponse = response.json(); assert!(parsed_response.credential.is_none()); let cred1 = test_fixture.issue_credential(request1.clone()).await; @@ -125,16 +118,14 @@ async fn issued_credential() { test_fixture.set_epoch(3); let cred2 = test_fixture.issue_credential(request2.clone()).await; - let response = test_fixture.rocket.get(route(1)).dispatch().await; - assert_eq!(response.status(), Status::Ok); - let parsed_response: IssuedCredentialResponse = - serde_json::from_str(&response.into_string().await.unwrap()).unwrap(); + let response = test_fixture.axum.get(&route(1)).await; + assert_eq!(response.status_code(), StatusCode::OK); + let parsed_response: IssuedCredentialResponse = response.json(); let issued1 = parsed_response.credential.unwrap(); - let response = test_fixture.rocket.get(route(2)).dispatch().await; - assert_eq!(response.status(), Status::Ok); - let parsed_response: IssuedCredentialResponse = - serde_json::from_str(&response.into_string().await.unwrap()).unwrap(); + let response = test_fixture.axum.get(&route(2)).await; + assert_eq!(response.status_code(), StatusCode::OK); + let parsed_response: IssuedCredentialResponse = response.json(); let issued2 = parsed_response.credential.unwrap(); // TODO: currently we have no signature checks @@ -192,37 +183,33 @@ async fn issued_credentials() { let issued13 = test_fixture.issued_unchecked(13).await; let response = test_fixture - .rocket + .axum .post(&route) .json(&CredentialsRequestBody { credential_ids: vec![5], pagination: None, }) - .dispatch() .await; - assert_eq!(response.status(), Status::Ok); - let parsed_response: IssuedCredentialsResponse = - serde_json::from_str(&response.into_string().await.unwrap()).unwrap(); + assert_eq!(response.status_code(), StatusCode::OK); + let parsed_response: IssuedCredentialsResponse = response.json(); assert_eq!(parsed_response.credentials[&5], issued5); assert!(!parsed_response.credentials.contains_key(&13)); let response = test_fixture - .rocket + .axum .post(&route) .json(&CredentialsRequestBody { credential_ids: vec![5, 13], pagination: None, }) - .dispatch() .await; - assert_eq!(response.status(), Status::Ok); - let parsed_response: IssuedCredentialsResponse = - serde_json::from_str(&response.into_string().await.unwrap()).unwrap(); + assert_eq!(response.status_code(), StatusCode::OK); + let parsed_response: IssuedCredentialsResponse = response.json(); assert_eq!(parsed_response.credentials[&5], issued5); assert_eq!(parsed_response.credentials[&13], issued13); let response_paginated = test_fixture - .rocket + .axum .post(&route) .json(&CredentialsRequestBody { credential_ids: vec![], @@ -231,11 +218,9 @@ async fn issued_credentials() { limit: Some(2), }), }) - .dispatch() .await; - assert_eq!(response_paginated.status(), Status::Ok); - let parsed_response: IssuedCredentialsResponse = - serde_json::from_str(&response_paginated.into_string().await.unwrap()).unwrap(); + assert_eq!(response_paginated.status_code(), StatusCode::OK); + let parsed_response: IssuedCredentialsResponse = response_paginated.json(); let mut expected = BTreeMap::new(); expected.insert(1, issued1); @@ -243,7 +228,7 @@ async fn issued_credentials() { assert_eq!(expected, parsed_response.credentials); let response_paginated = test_fixture - .rocket + .axum .post(&route) .json(&CredentialsRequestBody { credential_ids: vec![], @@ -252,11 +237,9 @@ async fn issued_credentials() { limit: Some(3), }), }) - .dispatch() .await; - assert_eq!(response_paginated.status(), Status::Ok); - let parsed_response: IssuedCredentialsResponse = - serde_json::from_str(&response_paginated.into_string().await.unwrap()).unwrap(); + assert_eq!(response_paginated.status_code(), StatusCode::OK); + let parsed_response: IssuedCredentialsResponse = response_paginated.json(); let mut expected = BTreeMap::new(); expected.insert(3, issued3); diff --git a/nym-api/src/ecash/tests/mod.rs b/nym-api/src/ecash/tests/mod.rs index fb037a85f0..aba83c5ec4 100644 --- a/nym-api/src/ecash/tests/mod.rs +++ b/nym-api/src/ecash/tests/mod.rs @@ -1,13 +1,24 @@ // Copyright 2022-2024 - Nym Technologies SA // SPDX-License-Identifier: GPL-3.0-only -use crate::ecash; +use crate::circulating_supply_api::cache::CirculatingSupplyCache; +use crate::ecash::api_routes::handlers::ecash_routes; use crate::ecash::error::{EcashError, Result}; use crate::ecash::keys::KeyPairWithEpoch; use crate::ecash::state::EcashState; use crate::ecash::storage::EcashStorageExt; +use crate::network::models::NetworkDetails; +use crate::node_describe_cache::DescribedNodes; +use crate::node_status_api::handlers::unstable; +use crate::node_status_api::NodeStatusCache; +use crate::nym_contract_cache::cache::NymContractCache; +use crate::support::caching::cache::SharedCache; +use crate::support::http::state::AppState; use crate::support::storage::NymApiStorage; use async_trait::async_trait; +use axum::Router; +use axum_test::http::StatusCode; +use axum_test::TestServer; use cosmwasm_std::testing::{mock_env, mock_info}; use cosmwasm_std::{ from_binary, to_binary, Addr, Binary, BlockInfo, CosmosMsg, Decimal, MessageInfo, WasmMsg, @@ -31,6 +42,7 @@ use nym_coconut_dkg_common::types::{ use nym_coconut_dkg_common::verification_key::{ContractVKShare, VerificationKeyShare}; use nym_compact_ecash::BlindedSignature; use nym_compact_ecash::{ttp_keygen, VerificationKeyAuth}; +use nym_config::defaults::NymNetworkDetails; use nym_contracts_common::IdentityKey; use nym_credentials::IssuanceTicketBook; use nym_credentials_interface::TicketType; @@ -45,8 +57,6 @@ use nym_validator_client::nyxd::{AccountId, ExecTxResult, Fee, Hash, TxResponse} use nym_validator_client::{EcashApiClient, NymApiClient}; use rand::rngs::OsRng; use rand::RngCore; -use rocket::http::Status; -use rocket::local::asynchronous::Client; use std::collections::{BTreeMap, HashMap}; use std::ops::Deref; use std::str::FromStr; @@ -863,7 +873,7 @@ impl super::client::Client for DummyClient { .votes .contains_key(&(voter.clone(), proposal_id)) { - todo!("already voted"); + panic!("unhandled case: already voted"); } chain.multisig_contract.votes.insert( (voter.clone(), proposal_id), @@ -1234,7 +1244,7 @@ fn dummy_signature() -> identity::Signature { } struct TestFixture { - rocket: Client, + axum: TestServer, storage: NymApiStorage, chain_state: SharedFakeChain, epoch: Arc, @@ -1243,6 +1253,21 @@ struct TestFixture { } impl TestFixture { + fn build_app_state(storage: NymApiStorage) -> AppState { + AppState { + nym_contract_cache: NymContractCache::new(), + node_status_cache: NodeStatusCache::new(), + circulating_supply_cache: CirculatingSupplyCache::new("unym".to_owned()), + storage, + described_nodes_cache: SharedCache::::new(), + network_details: NetworkDetails::new( + "localhost".to_string(), + NymNetworkDetails::new_empty(), + ), + node_info_cache: unstable::NodeInfoCache::default(), + } + } + async fn new() -> Self { let mut rng = crate::ecash::tests::fixtures::test_rng([69u8; 32]); let coconut_keypair = ttp_keygen(1, 1).unwrap().remove(0); @@ -1283,28 +1308,24 @@ impl TestFixture { .parse() .unwrap(); - let rocket = rocket::build() - .manage( - EcashState::new( - ecash_contract, - nyxd_client, - identity, - staged_key_pair, - comm_channel, - storage.clone(), - ) - .await - .unwrap(), - ) - .mount( - "/v1/ecash", - ecash::routes_open_api(&Default::default(), true).0, - ); + let ecash_state = EcashState::new( + ecash_contract, + nyxd_client, + identity, + staged_key_pair, + comm_channel, + storage.clone(), + ) + .await + .unwrap(); TestFixture { - rocket: Client::tracked(rocket) - .await - .expect("valid rocket instance"), + axum: TestServer::new( + Router::new() + .nest("/v1/ecash", ecash_routes(Arc::new(ecash_state))) + .with_state(Self::build_app_state(storage.clone())), + ) + .unwrap(), storage, chain_state, epoch, @@ -1351,26 +1372,24 @@ impl TestFixture { async fn issue_credential(&self, req: BlindSignRequestBody) -> BlindedSignatureResponse { let response = self - .rocket - .post(format!("/{API_VERSION}/{ECASH_ROUTES}/{ECASH_BLIND_SIGN}",)) + .axum + .post(&format!("/{API_VERSION}/{ECASH_ROUTES}/{ECASH_BLIND_SIGN}",)) .json(&req) - .dispatch() .await; - assert_eq!(response.status(), Status::Ok); - serde_json::from_str(&response.into_string().await.unwrap()).unwrap() + assert_eq!(response.status_code(), StatusCode::OK); + response.json() } async fn issued_credential(&self, id: i64) -> Option { let response = self - .rocket - .get(format!( + .axum + .get(&format!( "/{API_VERSION}/{ECASH_ROUTES}/issued-credential/{id}" )) - .dispatch() .await; - assert_eq!(response.status(), Status::Ok); - serde_json::from_str(&response.into_string().await.unwrap()).unwrap() + assert_eq!(response.status_code(), StatusCode::OK); + response.json() } async fn issued_unchecked(&self, id: i64) -> IssuedTicketbookBody { @@ -1385,6 +1404,7 @@ impl TestFixture { #[cfg(test)] mod credential_tests { use super::*; + use axum::http::StatusCode; use nym_compact_ecash::ttp_keygen; #[tokio::test] @@ -1416,23 +1436,15 @@ mod credential_tests { .unwrap(); let response = test_fixture - .rocket - .post(format!("/{API_VERSION}/{ECASH_ROUTES}/{ECASH_BLIND_SIGN}",)) + .axum + .post(&format!("/{API_VERSION}/{ECASH_ROUTES}/{ECASH_BLIND_SIGN}",)) .json(&request_body) - .dispatch() .await; - assert_eq!(response.status(), Status::Ok); - let expected_response = BlindedSignatureResponse::new(sig); - // This is a more direct way, but there's a bug which makes it hang https://github.com/SergioBenitez/Rocket/issues/1893 - // let blinded_signature_response = response - // .into_json::() - // .await - // .unwrap(); - let blinded_signature_response = serde_json::from_str::( - &response.into_string().await.unwrap(), - ) - .unwrap(); + assert_eq!(response.status_code(), StatusCode::OK); + let expected_response = BlindedSignatureResponse::new(sig); + let blinded_signature_response = response.json::(); + assert_eq!( blinded_signature_response.to_bytes(), expected_response.to_bytes() @@ -1596,19 +1608,13 @@ mod credential_tests { let request_body = voucher.create_blind_sign_request_body(&signing_data); let response = test - .rocket - .post(format!("/{API_VERSION}/{ECASH_ROUTES}/{ECASH_BLIND_SIGN}")) + .axum + .post(&format!("/{API_VERSION}/{ECASH_ROUTES}/{ECASH_BLIND_SIGN}")) .json(&request_body) - .dispatch() .await; - assert_eq!(response.status(), Status::Ok); - // This is a more direct way, but there's a bug which makes it hang https://github.com/SergioBenitez/Rocket/issues/1893 - // assert!(response.into_json::().is_some()); - let blinded_signature_response = serde_json::from_str::( - &response.into_string().await.unwrap(), - ); - assert!(blinded_signature_response.is_ok()); + assert_eq!(response.status_code(), StatusCode::OK); + let _ = response.json::(); } #[test] diff --git a/nym-api/src/epoch_operations/mod.rs b/nym-api/src/epoch_operations/mod.rs index e7ccdf7240..aeae8977fe 100644 --- a/nym-api/src/epoch_operations/mod.rs +++ b/nym-api/src/epoch_operations/mod.rs @@ -119,7 +119,10 @@ impl EpochAdvancer { let legacy_mixnodes = self.nym_contract_cache.legacy_mixnodes_filtered().await; let legacy_gateways = self.nym_contract_cache.legacy_gateways_filtered().await; - let nym_nodes = self.nym_contract_cache.nym_nodes_filtered().await; + + // TODO: for the purposes of rewarding, this might have to grab some pre-filtered nodes instead, + // such as ones that use up to date version or have correct 'peanut' score + let nym_nodes = self.nym_contract_cache.nym_nodes().await; if legacy_mixnodes.is_empty() && legacy_gateways.is_empty() && nym_nodes.is_empty() { // that's a bit weird, but ok diff --git a/nym-api/src/network_monitor/monitor/preparer.rs b/nym-api/src/network_monitor/monitor/preparer.rs index 94c8d116f5..69a0ae3e4d 100644 --- a/nym-api/src/network_monitor/monitor/preparer.rs +++ b/nym-api/src/network_monitor/monitor/preparer.rs @@ -539,14 +539,14 @@ impl PacketPreparer { if let Some(rewarded_set) = rewarded_set { let mut rng = thread_rng(); for mix in mixing_nym_nodes { - if let Some(parsed) = self.nym_node_to_legacy_mix(&mut rng, &rewarded_set, &mix) { + if let Some(parsed) = self.nym_node_to_legacy_mix(&mut rng, &rewarded_set, mix) { tested_mixnodes.push(TestableNode::from(&parsed)); } } } for gateway in gateway_capable_nym_nodes { - if let Some(parsed) = self.nym_node_to_legacy_gateway(&gateway) { + if let Some(parsed) = self.nym_node_to_legacy_gateway(gateway) { tested_gateways.push((&parsed, gateway.node_id).into()) } } diff --git a/nym-api/src/node_status_api/models.rs b/nym-api/src/node_status_api/models.rs index ce4c0eb1b1..92e788047e 100644 --- a/nym-api/src/node_status_api/models.rs +++ b/nym-api/src/node_status_api/models.rs @@ -456,11 +456,3 @@ impl NymApiStorageError { } } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn uptime_response_conversion() {} -} diff --git a/nym-api/src/nym_contract_cache/cache/mod.rs b/nym-api/src/nym_contract_cache/cache/mod.rs index 1f40768a9e..ac7f89b0c5 100644 --- a/nym-api/src/nym_contract_cache/cache/mod.rs +++ b/nym-api/src/nym_contract_cache/cache/mod.rs @@ -69,6 +69,7 @@ impl NymContractCache { } } + #[allow(clippy::too_many_arguments)] pub(crate) async fn update( &self, mixnodes: Vec, @@ -182,6 +183,7 @@ impl NymContractCache { .map(|g| g.owner.to_string()) } + #[allow(dead_code)] pub async fn legacy_mixnode_owner(&self, node_id: NodeId) -> Option { self.get(|c| &c.legacy_mixnodes) .await? @@ -257,10 +259,6 @@ impl NymContractCache { .into_inner() } - pub async fn nym_nodes_filtered(&self) -> Vec { - todo!() - } - pub async fn rewarded_set(&self) -> Option>> { self.get(|cache| &cache.rewarded_set).await } diff --git a/nym-api/src/support/http/state.rs b/nym-api/src/support/http/state.rs index c20f18436b..f0803d6677 100644 --- a/nym-api/src/support/http/state.rs +++ b/nym-api/src/support/http/state.rs @@ -111,9 +111,9 @@ impl AppState { // handler helpers to easily get data or return error response impl AppState { - pub(crate) async fn describe_nodes_cache_data<'a>( - &'a self, - ) -> Result>, AxumErrorResponse> { + pub(crate) async fn describe_nodes_cache_data( + &self, + ) -> Result>, AxumErrorResponse> { Ok(self.described_nodes_cache().get().await?) } diff --git a/nym-api/src/support/storage/manager.rs b/nym-api/src/support/storage/manager.rs index 392efa6af4..f3f79ce128 100644 --- a/nym-api/src/support/storage/manager.rs +++ b/nym-api/src/support/storage/manager.rs @@ -10,7 +10,7 @@ use crate::support::storage::models::{ use nym_mixnet_contract_common::{EpochId, IdentityKey, NodeId}; use nym_types::monitoring::NodeResult; use sqlx::FromRow; -use time::Date; +use time::{Date, OffsetDateTime}; use tracing::info; #[derive(Clone)] @@ -580,41 +580,39 @@ impl StorageManager { ) -> Result<(), sqlx::Error> { info!("Inserting {} mixnode statuses", mixnode_results.len()); - todo!("look at what broke during rebasing"); - // - // let timestamp = OffsetDateTime::now_utc().unix_timestamp(); - // // insert it all in a transaction to make sure all nodes are updated at the same time - // // (plus it's a nice guard against new nodes) - // let mut tx = self.connection_pool.begin().await?; - // for mixnode_result in mixnode_results { - // let mixnode_id = sqlx::query!( - // r#" - // INSERT OR IGNORE INTO mixnode_details_v2(node_id, identity_key) VALUES (?, ?); - // SELECT id FROM mixnode_details_v2 WHERE node_id = ?; - // "#, - // mixnode_result.node_id, - // mixnode_result.identity, - // mixnode_result.node_id, - // ) - // .fetch_one(&mut tx) - // .await? - // .id; - // - // // insert the actual status - // sqlx::query!( - // r#" - // INSERT INTO mixnode_status_v2 (mixnode_details_id, reliability, timestamp) VALUES (?, ?, ?); - // "#, - // mixnode_id, - // mixnode_result.reliability, - // timestamp - // ) - // .execute(&mut tx) - // .await?; - // } - // - // // finally commit the transaction - // tx.commit().await + let timestamp = OffsetDateTime::now_utc().unix_timestamp(); + // insert it all in a transaction to make sure all nodes are updated at the same time + // (plus it's a nice guard against new nodes) + let mut tx = self.connection_pool.begin().await?; + for mixnode_result in mixnode_results { + let mixnode_id = sqlx::query!( + r#" + INSERT OR IGNORE INTO mixnode_details_v2(node_id, identity_key) VALUES (?, ?); + SELECT id FROM mixnode_details_v2 WHERE node_id = ?; + "#, + mixnode_result.node_id, + mixnode_result.identity, + mixnode_result.node_id, + ) + .fetch_one(&mut tx) + .await? + .id; + + // insert the actual status + sqlx::query!( + r#" + INSERT INTO mixnode_status_v2 (mixnode_details_id, reliability, timestamp) VALUES (?, ?, ?); + "#, + mixnode_id, + mixnode_result.reliability, + timestamp + ) + .execute(&mut tx) + .await?; + } + + // finally commit the transaction + tx.commit().await } /// Tries to submit gateway [`NodeResult`] from the network monitor to the database. @@ -672,45 +670,43 @@ impl StorageManager { ) -> Result<(), sqlx::Error> { info!("Inserting {} gateway statuses", gateway_results.len()); - todo!("look at what broke during rebasing"); + let timestamp = OffsetDateTime::now_utc().unix_timestamp(); + // insert it all in a transaction to make sure all nodes are updated at the same time + // (plus it's a nice guard against new nodes) + let mut tx = self.connection_pool.begin().await?; - // let timestamp = OffsetDateTime::now_utc().unix_timestamp(); - // // insert it all in a transaction to make sure all nodes are updated at the same time - // // (plus it's a nice guard against new nodes) - // let mut tx = self.connection_pool.begin().await?; - // - // for gateway_result in gateway_results { - // // if gateway info doesn't exist, insert it and get its id - // - // // same ID "problem" as described for mixnode insertion - // let gateway_id = sqlx::query!( - // r#" - // INSERT OR IGNORE INTO gateway_details_v2(identity, node_id) VALUES (?, ?); - // SELECT id FROM gateway_details_v2 WHERE identity = ?; - // "#, - // gateway_result.identity, - // gateway_result.node_id, - // gateway_result.identity, - // ) - // .fetch_one(&mut tx) - // .await? - // .id; - // - // // insert the actual status - // sqlx::query!( - // r#" - // INSERT INTO gateway_status_v2 (gateway_details_id, reliability, timestamp) VALUES (?, ?, ?); - // "#, - // gateway_id, - // gateway_result.reliability, - // timestamp - // ) - // .execute(&mut tx) - // .await?; - // } - // - // // finally commit the transaction - // tx.commit().await + for gateway_result in gateway_results { + // if gateway info doesn't exist, insert it and get its id + + // same ID "problem" as described for mixnode insertion + let gateway_id = sqlx::query!( + r#" + INSERT OR IGNORE INTO gateway_details_v2(identity, node_id) VALUES (?, ?); + SELECT id FROM gateway_details_v2 WHERE identity = ?; + "#, + gateway_result.identity, + gateway_result.node_id, + gateway_result.identity, + ) + .fetch_one(&mut tx) + .await? + .id; + + // insert the actual status + sqlx::query!( + r#" + INSERT INTO gateway_status_v2 (gateway_details_id, reliability, timestamp) VALUES (?, ?, ?); + "#, + gateway_id, + gateway_result.reliability, + timestamp + ) + .execute(&mut tx) + .await?; + } + + // finally commit the transaction + tx.commit().await } /// Saves the information about which nodes were used as core nodes during this particular diff --git a/nym-network-monitor/src/accounting.rs b/nym-network-monitor/src/accounting.rs index d658e9dc63..4dea0bcc74 100644 --- a/nym-network-monitor/src/accounting.rs +++ b/nym-network-monitor/src/accounting.rs @@ -15,16 +15,16 @@ use utoipa::ToSchema; use crate::{NYM_API_URL, PRIVATE_KEY, TOPOLOGY}; struct HydratedRoute { - mix_nodes: Vec, - gateway_node: gateway::Node, + mix_nodes: Vec, + gateway_node: gateway::LegacyNode, } #[derive(Serialize, Deserialize, Debug, Default, ToSchema)] -struct GatewayStats(u32, u32, Option); +struct GatewayStats(u32, u32); impl GatewayStats { - fn new(sent: u32, recv: u32, owner: Option) -> Self { - GatewayStats(sent, recv, owner) + fn new(sent: u32, recv: u32) -> Self { + GatewayStats(sent, recv) } fn success(&self) -> u32 { @@ -60,9 +60,9 @@ pub struct NetworkAccount { topology: NymTopology, tested_nodes: HashSet, #[serde(skip)] - mix_details: HashMap, + mix_details: HashMap, #[serde(skip)] - gateway_details: HashMap, + gateway_details: HashMap, } impl NetworkAccount { @@ -82,7 +82,6 @@ impl NetworkAccount { complete_routes, incomplete_routes, node.identity_key.to_base58_string(), - node.owner.clone(), ) } @@ -186,7 +185,7 @@ impl NetworkAccount { let gateway_stats_entry = self .gateway_stats .entry(route.gateway_node.identity_key.to_base58_string()) - .or_insert(GatewayStats::new(0, 0, route.gateway_node.owner.clone())); + .or_insert(GatewayStats::new(0, 0)); self.gateway_details.insert( route.gateway_node.identity_key.to_base58_string(), route.gateway_node, @@ -265,7 +264,6 @@ pub struct NodeStats { incomplete_routes: usize, reliability: f64, identity: String, - owner: Option, } impl NodeStats { @@ -274,7 +272,6 @@ impl NodeStats { complete_routes: usize, incomplete_routes: usize, identity: String, - owner: Option, ) -> Self { NodeStats { mix_id, @@ -282,7 +279,6 @@ impl NodeStats { incomplete_routes, reliability: complete_routes as f64 / (complete_routes + incomplete_routes) as f64, identity, - owner, } } diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index 80ef99ae94..445c5f297a 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -167,9 +167,9 @@ checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" [[package]] name = "async-trait" -version = "0.1.82" +version = "0.1.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", @@ -1631,9 +1631,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.33" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" dependencies = [ "crc32fast", "miniz_oxide 0.8.0", @@ -3360,6 +3360,7 @@ version = "0.1.0" dependencies = [ "base64 0.22.1", "bs58", + "hex", "serde", "time", ] @@ -3448,6 +3449,7 @@ dependencies = [ "nym-mixnet-contract-common", "nym-multisig-contract-common", "nym-network-defaults", + "nym-serde-helpers", "nym-vesting-contract-common", "prost", "reqwest 0.12.4", @@ -5760,18 +5762,18 @@ checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", diff --git a/sdk/rust/nym-sdk/examples/manually_overwrite_topology.rs b/sdk/rust/nym-sdk/examples/manually_overwrite_topology.rs index bfd62d1a8f..67f4a773a7 100644 --- a/sdk/rust/nym-sdk/examples/manually_overwrite_topology.rs +++ b/sdk/rust/nym-sdk/examples/manually_overwrite_topology.rs @@ -21,7 +21,6 @@ async fn main() { 1, vec![mix::LegacyNode { mix_id: 63, - owner: None, host: "172.105.92.48".parse().unwrap(), mix_host: "172.105.92.48:1789".parse().unwrap(), identity_key: "GLdR2NRVZBiCoCbv4fNqt9wUJZAnNjGXHkx3TjVAUzrK" @@ -38,7 +37,6 @@ async fn main() { 2, vec![mix::LegacyNode { mix_id: 23, - owner: None, host: "178.79.143.65".parse().unwrap(), mix_host: "178.79.143.65:1789".parse().unwrap(), identity_key: "4Yr4qmEHd9sgsuQ83191FR2hD88RfsbMmB4tzhhZWriz" @@ -55,7 +53,6 @@ async fn main() { 3, vec![mix::LegacyNode { mix_id: 66, - owner: None, host: "139.162.247.97".parse().unwrap(), mix_host: "139.162.247.97:1789".parse().unwrap(), identity_key: "66UngapebhJRni3Nj52EW1qcNsWYiuonjkWJzHFsmyYY" diff --git a/tools/internal/testnet-manager/src/manager/local_client.rs b/tools/internal/testnet-manager/src/manager/local_client.rs index 1a199f410f..f4afb4961e 100644 --- a/tools/internal/testnet-manager/src/manager/local_client.rs +++ b/tools/internal/testnet-manager/src/manager/local_client.rs @@ -113,7 +113,7 @@ impl NetworkManager { if let Some(node) = gateways .nodes .iter() - .find(|gw| &gw.ed25519_identity_pubkey == identity) + .find(|gw| &gw.ed25519_identity_pubkey.to_base58_string() == identity) { return SocketAddr::new( node.ip_addresses[0], diff --git a/tools/internal/testnet-manager/src/manager/local_nodes.rs b/tools/internal/testnet-manager/src/manager/local_nodes.rs index d5ea4be530..9492de53c8 100644 --- a/tools/internal/testnet-manager/src/manager/local_nodes.rs +++ b/tools/internal/testnet-manager/src/manager/local_nodes.rs @@ -9,7 +9,7 @@ use crate::manager::NetworkManager; use console::style; use nym_mixnet_contract_common::nym_node::Role; use nym_mixnet_contract_common::RoleAssignment; -use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient}; +use nym_validator_client::nyxd::contract_traits::MixnetSigningClient; use nym_validator_client::DirectSigningHttpRpcNyxdClient; use serde::{Deserialize, Serialize}; use std::fs; @@ -372,7 +372,7 @@ impl NetworkManager { ctx: &LocalNodesCtx<'a>, ) -> Result<(), NetworkManagerError> { ctx.println(format!( - "🔌 {}Assigning mixnodes to the active set...", + "🔌 {}Assigning nodes to the active set...", style("[4/5]").bold().dim() )); @@ -386,58 +386,17 @@ impl NetworkManager { let fut = rewarder.reconcile_epoch_events(None, None); ctx.async_with_progress(fut).await?; - ctx.set_pb_message("[BROKEN] finally assigning the active set..."); - // let fut = rewarder.get_rewarding_parameters(); - // let rewarding_params = ctx.async_with_progress(fut).await?; - // let active_set_size = rewarding_params.active_set_size; - - let unused_variable = "this has to be fixed up and refactored...."; - /* - fn generate_role_assignment_messages( - &self, - rewarded_set: RewardedSet, - ) -> Vec<(ExecuteMsg, Vec)> { - // currently we just assign all of them together, - // but the contract is ready to handle them separately should we need it - // if the tx is too big - let mut msgs = Vec::new(); - for (role, nodes) in [ - (Role::ExitGateway, rewarded_set.exit_gateways), - (Role::EntryGateway, rewarded_set.entry_gateways), - (Role::Layer1, rewarded_set.layer1), - (Role::Layer2, rewarded_set.layer2), - (Role::Layer3, rewarded_set.layer3), - (Role::Standby, rewarded_set.standby), - ] { - msgs.push(( - ExecuteMsg::AssignRoles { - assignment: RoleAssignment { role, nodes }, - }, - Vec::new(), - )); - } - msgs - } - */ - + ctx.set_pb_message("finally assigning the active set... entry..."); let fut = rewarder.assign_roles( RoleAssignment { - role: Role::ExitGateway, + role: Role::EntryGateway, nodes: vec![4], }, None, ); ctx.async_with_progress(fut).await?; - let fut = rewarder.assign_roles( - RoleAssignment { - role: Role::EntryGateway, - nodes: vec![], - }, - None, - ); - ctx.async_with_progress(fut).await?; - + ctx.set_pb_message("finally assigning the active set... layer1..."); let fut = rewarder.assign_roles( RoleAssignment { role: Role::Layer1, @@ -447,6 +406,7 @@ impl NetworkManager { ); ctx.async_with_progress(fut).await?; + ctx.set_pb_message("finally assigning the active set... layer2..."); let fut = rewarder.assign_roles( RoleAssignment { role: Role::Layer2, @@ -456,6 +416,7 @@ impl NetworkManager { ); ctx.async_with_progress(fut).await?; + ctx.set_pb_message("finally assigning the active set... layer3..."); let fut = rewarder.assign_roles( RoleAssignment { role: Role::Layer3, @@ -465,6 +426,7 @@ impl NetworkManager { ); ctx.async_with_progress(fut).await?; + ctx.set_pb_message("finally assigning the active set... [empty] standby..."); let fut = rewarder.assign_roles( RoleAssignment { role: Role::Standby,