From df93356a2368fe284fe481d565987790ab279bf0 Mon Sep 17 00:00:00 2001 From: dynco-nym <173912580+dynco-nym@users.noreply.github.com> Date: Tue, 9 Dec 2025 16:09:33 +0100 Subject: [PATCH] Unit tests & minor tweaks --- .../nym-performance-contract/src/msg.rs | 7 - contracts/performance/src/contract.rs | 12 +- contracts/performance/src/storage.rs | 20 +- contracts/performance/src/transactions.rs | 222 +++++++++++++++++- 4 files changed, 244 insertions(+), 17 deletions(-) diff --git a/common/cosmwasm-smart-contracts/nym-performance-contract/src/msg.rs b/common/cosmwasm-smart-contracts/nym-performance-contract/src/msg.rs index a26c9a3739..3b773b817f 100644 --- a/common/cosmwasm-smart-contracts/nym-performance-contract/src/msg.rs +++ b/common/cosmwasm-smart-contracts/nym-performance-contract/src/msg.rs @@ -36,13 +36,6 @@ pub enum ExecuteMsg { data: Vec, }, - // TODO dz rename to remove speicifc suffix - /// Attempt to submit performance data of a batch of nodes for given epoch - BatchSubmitSpecific { - epoch: EpochId, - data: Vec, - }, - /// Measurement kind needs to be defined by the admin before measurements of /// that kind can be submitted. DefineMeasurementKind { measurement_kind: String }, diff --git a/contracts/performance/src/contract.rs b/contracts/performance/src/contract.rs index e67b7b818d..762464d8e0 100644 --- a/contracts/performance/src/contract.rs +++ b/contracts/performance/src/contract.rs @@ -12,7 +12,8 @@ use crate::storage::NYM_PERFORMANCE_CONTRACT_STORAGE; use crate::transactions::{ try_authorise_network_monitor, try_batch_submit_performance_results, try_define_measurement_kind, try_remove_epoch_measurements, try_remove_node_measurements, - try_retire_network_monitor, try_submit_performance_results, try_update_contract_admin, + try_retire_measurement_kind, try_retire_network_monitor, try_submit_performance_results, + try_update_contract_admin, }; use cosmwasm_std::{ Binary, Deps, DepsMut, Env, MessageInfo, Response, entry_point, to_json_binary, @@ -63,13 +64,12 @@ pub fn execute( ExecuteMsg::BatchSubmit { epoch, data } => { try_batch_submit_performance_results(deps, env, info, epoch, data) } - // TODO dz implement - ExecuteMsg::BatchSubmitSpecific { epoch, data } => todo!(), ExecuteMsg::DefineMeasurementKind { measurement_kind } => { - try_define_measurement_kind(deps, info, measurement_kind) + try_define_measurement_kind(deps, &info.sender, measurement_kind) + } + ExecuteMsg::RetireMeasurementKind { measurement_kind } => { + try_retire_measurement_kind(deps, &info.sender, measurement_kind) } - // TODO dz implement - ExecuteMsg::RetireMeasurementKind { measurement_kind } => todo!(), ExecuteMsg::AuthoriseNetworkMonitor { address } => { try_authorise_network_monitor(deps, env, info, address) } diff --git a/contracts/performance/src/storage.rs b/contracts/performance/src/storage.rs index 540c542ab0..a0cbb3a8bb 100644 --- a/contracts/performance/src/storage.rs +++ b/contracts/performance/src/storage.rs @@ -634,6 +634,24 @@ impl PerformanceResultsStorage { .map_err(From::from) } + /// assumes authorization had been performed + pub fn retire_measurement_kind( + &self, + storage: &mut dyn Storage, + measurement_kind: MeasurementKind, + ) -> Result<(), NymPerformanceContractError> { + if !self.is_measurement_defined(storage, measurement_kind.clone())? { + return Err(NymPerformanceContractError::InvalidInput(format!( + "Invalid input: measurement {} not defined", + &measurement_kind + ))); + } + + self.defined_measurements.remove(storage, measurement_kind); + + Ok(()) + } + fn update_submission_metadata( &self, storage: &mut dyn Storage, @@ -1309,7 +1327,7 @@ mod tests { NodePerformanceSpecific { node_id: nodes[8], performance: Default::default(), - measurement_kind: measurement_kind, + measurement_kind, }, )?; let metadata = storage diff --git a/contracts/performance/src/transactions.rs b/contracts/performance/src/transactions.rs index d07844a852..833545720f 100644 --- a/contracts/performance/src/transactions.rs +++ b/contracts/performance/src/transactions.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::storage::{MeasurementKind, NYM_PERFORMANCE_CONTRACT_STORAGE}; -use cosmwasm_std::{DepsMut, Env, Event, MessageInfo, Response, to_json_binary}; +use cosmwasm_std::{Addr, DepsMut, Env, Event, MessageInfo, Response, to_json_binary}; use nym_performance_contract_common::{ EpochId, NodeId, NodePerformanceSpecific, NymPerformanceContractError, }; @@ -23,12 +23,12 @@ pub fn try_update_contract_admin( pub fn try_define_measurement_kind( deps: DepsMut<'_>, - info: MessageInfo, + sender: &Addr, measurement_kind: MeasurementKind, ) -> Result { NYM_PERFORMANCE_CONTRACT_STORAGE .contract_admin - .assert_admin(deps.as_ref(), &info.sender)?; + .assert_admin(deps.as_ref(), sender)?; NYM_PERFORMANCE_CONTRACT_STORAGE .performance_results @@ -37,6 +37,22 @@ pub fn try_define_measurement_kind( Ok(Response::new()) } +pub fn try_retire_measurement_kind( + deps: DepsMut<'_>, + sender_addr: &Addr, + measurement_kind: MeasurementKind, +) -> Result { + NYM_PERFORMANCE_CONTRACT_STORAGE + .contract_admin + .assert_admin(deps.as_ref(), sender_addr)?; + + NYM_PERFORMANCE_CONTRACT_STORAGE + .performance_results + .retire_measurement_kind(deps.storage, measurement_kind)?; + + Ok(Response::new()) +} + pub fn try_submit_performance_results( deps: DepsMut<'_>, env: Env, @@ -77,6 +93,10 @@ pub fn try_batch_submit_performance_results( .add_attribute( "non_existent_nodes", format!("{:?}", res.non_existent_nodes), + ) + .add_attribute( + "non_existent_measurement_kinds", + format!("{:?}", res.non_existent_measurement_kind), ), ); Ok(response) @@ -333,4 +353,200 @@ mod tests { Ok(()) } + + mod measurement_kind_authorization { + use cosmwasm_std::testing::message_info; + use nym_contracts_common_testing::{AdminExt, ContractOpts}; + use nym_performance_contract_common::NymPerformanceContractError; + + use crate::{ + storage::MeasurementKind, + testing::{PerformanceContractTesterExt, init_contract_tester}, + transactions::{ + try_define_measurement_kind, try_retire_measurement_kind, + try_submit_performance_results, + }, + }; + + #[allow(clippy::panic)] + #[test] + fn add_requires_admin() { + let mut tester = init_contract_tester(); + let admin = tester.admin_msg(); + let new_measurement = MeasurementKind::from("new-measurement"); + + assert!( + try_define_measurement_kind( + tester.deps_mut(), + &admin.sender, + new_measurement.clone() + ) + .is_ok() + ); + } + + #[allow(clippy::panic)] + #[test] + fn retire_requires_admin() { + let mut tester = init_contract_tester(); + let admin = tester.admin_msg(); + let new_measurement = MeasurementKind::from("new-measurement"); + + try_define_measurement_kind(tester.deps_mut(), &admin.sender, new_measurement.clone()) + .unwrap(); + + let unauthorized_addr = tester.addr_make("unauthorized-addr"); + let unauthorized = try_retire_measurement_kind( + tester.deps_mut(), + &unauthorized_addr, + new_measurement.clone(), + ); + assert!(matches!( + unauthorized, + Err(NymPerformanceContractError::Admin { .. }) + )); + + let authorized = try_retire_measurement_kind( + tester.deps_mut(), + &admin.sender, + new_measurement.clone(), + ); + assert!(matches!(authorized, Ok(..))); + } + + #[allow(clippy::panic)] + #[test] + fn cannot_add_existing() { + let mut tester = init_contract_tester(); + let admin = tester.admin_msg(); + let new_measurement = MeasurementKind::from("new-measurement"); + + let first_attempt = try_define_measurement_kind( + tester.deps_mut(), + &admin.sender, + new_measurement.clone(), + ); + assert!(matches!(first_attempt, Ok(..))); + + let second_attempt = + try_define_measurement_kind(tester.deps_mut(), &admin.sender, new_measurement); + assert!(matches!( + second_attempt, + Err(NymPerformanceContractError::InvalidInput(_)) + )); + } + + #[allow(clippy::panic)] + #[test] + fn cannot_retire_nonexistent() { + let mut tester = init_contract_tester(); + let admin = tester.admin_msg(); + let nonexistent = MeasurementKind::from("nonexistent"); + + let err = try_retire_measurement_kind(tester.deps_mut(), &admin.sender, nonexistent); + + assert!(matches!( + err, + Err(NymPerformanceContractError::InvalidInput(_)) + )); + } + + #[allow(clippy::panic)] + #[test] + fn cannot_submit_undefined() { + let mut tester = init_contract_tester(); + let env = tester.env(); + let admin = tester.admin_msg(); + let dummy_perf = tester.dummy_node_performance(); + let nm = tester.addr_make("network-monitor"); + tester.authorise_network_monitor(&nm).unwrap(); + + let dummy_measurement = dummy_perf.measurement_kind.clone(); + + let first_attempt = try_submit_performance_results( + tester.deps_mut(), + env.clone(), + // network monitor submits + message_info(&nm, &[]), + 0, + dummy_perf.clone(), + ); + assert!(matches!( + first_attempt, + Err(NymPerformanceContractError::UnsupportedMeasurementKind { .. }) + )); + + try_define_measurement_kind( + tester.deps_mut(), + // admin defines + &admin.sender, + dummy_measurement.clone(), + ) + .unwrap(); + let second_attempt = try_submit_performance_results( + tester.deps_mut(), + env, + // network monitor submits + message_info(&nm, &[]), + 0, + dummy_perf, + ); + assert!(matches!(second_attempt, Ok(..))); + } + + #[allow(clippy::panic)] + #[test] + fn cannot_submit_retired() { + let mut tester = init_contract_tester(); + let env = tester.env(); + let admin = tester.admin_msg(); + let dummy_perf = tester.dummy_node_performance(); + let nm = tester.addr_make("network-monitor"); + tester.authorise_network_monitor(&nm).unwrap(); + + let dummy_measurement = dummy_perf.measurement_kind.clone(); + + try_define_measurement_kind( + tester.deps_mut(), + // admin defines + &admin.sender, + dummy_measurement.clone(), + ) + .unwrap(); + let defined_ok = try_submit_performance_results( + tester.deps_mut(), + env.clone(), + // network monitor submits + message_info(&nm, &[]), + 0, + dummy_perf.clone(), + ); + assert!(matches!(defined_ok, Ok(..))); + + // can't submit for the same node in the same epoch again + tester.advance_mixnet_epoch().unwrap(); + + try_retire_measurement_kind( + tester.deps_mut(), + // admin defines + &admin.sender, + dummy_measurement.clone(), + ) + .unwrap(); + + let retired_err = try_submit_performance_results( + tester.deps_mut(), + env, + // network monitor submits + message_info(&nm, &[]), + 1, + dummy_perf, + ); + println!("{:#?}", retired_err); + assert!(matches!( + retired_err, + Err(NymPerformanceContractError::UnsupportedMeasurementKind { .. }) + )); + } + } }